+ return attribute;
+}
+
+/**
+ * parse one constant expression argument.
+ */
+static void parse_gnu_attribute_const_arg(gnu_attribute_t *attribute)
+{
+ expression_t *expression;
+ add_anchor_token(')');
+ expression = parse_constant_expression();
+ rem_anchor_token(')');
+ expect(')');
+ attribute->u.argument = fold_constant(expression);
+ return;
+end_error:
+ attribute->invalid = true;
+}
+
+/**
+ * parse a list of constant expressions arguments.
+ */
+static void parse_gnu_attribute_const_arg_list(gnu_attribute_t *attribute)
+{
+ argument_list_t **list = &attribute->u.arguments;
+ argument_list_t *entry;
+ expression_t *expression;
+ add_anchor_token(')');
+ add_anchor_token(',');
+ while (true) {
+ expression = parse_constant_expression();
+ entry = obstack_alloc(&temp_obst, sizeof(entry));
+ entry->argument = fold_constant(expression);
+ entry->next = NULL;
+ *list = entry;
+ list = &entry->next;
+ if (token.type != ',')
+ break;
+ next_token();
+ }
+ rem_anchor_token(',');
+ rem_anchor_token(')');
+ expect(')');
+ return;
+end_error:
+ attribute->invalid = true;
+}
+
+/**
+ * parse one string literal argument.
+ */
+static void parse_gnu_attribute_string_arg(gnu_attribute_t *attribute,
+ string_t *string)
+{
+ add_anchor_token('(');
+ if (token.type != T_STRING_LITERAL) {
+ parse_error_expected("while parsing attribute directive",
+ T_STRING_LITERAL, NULL);
+ goto end_error;
+ }
+ *string = parse_string_literals();
+ rem_anchor_token('(');
+ expect(')');
+ return;
+end_error:
+ attribute->invalid = true;
+}
+
+/**
+ * parse one tls model.
+ */
+static void parse_gnu_attribute_tls_model_arg(gnu_attribute_t *attribute)
+{
+ static const char *const tls_models[] = {
+ "global-dynamic",
+ "local-dynamic",
+ "initial-exec",
+ "local-exec"
+ };
+ string_t string = { NULL, 0 };
+ parse_gnu_attribute_string_arg(attribute, &string);
+ if (string.begin != NULL) {
+ for(size_t i = 0; i < 4; ++i) {
+ if (strcmp(tls_models[i], string.begin) == 0) {
+ attribute->u.value = i;
+ return;
+ }
+ }
+ errorf(HERE, "'%s' is an unrecognized tls model", string.begin);
+ }
+ attribute->invalid = true;
+}
+
+/**
+ * parse one tls model.
+ */
+static void parse_gnu_attribute_visibility_arg(gnu_attribute_t *attribute)
+{
+ static const char *const visibilities[] = {
+ "default",
+ "protected",
+ "hidden",
+ "internal"
+ };
+ string_t string = { NULL, 0 };
+ parse_gnu_attribute_string_arg(attribute, &string);
+ if (string.begin != NULL) {
+ for(size_t i = 0; i < 4; ++i) {
+ if (strcmp(visibilities[i], string.begin) == 0) {
+ attribute->u.value = i;
+ return;
+ }
+ }
+ errorf(HERE, "'%s' is an unrecognized visibility", string.begin);
+ }
+ attribute->invalid = true;
+}
+
+/**
+ * parse one (code) model.
+ */
+static void parse_gnu_attribute_model_arg(gnu_attribute_t *attribute)
+{
+ static const char *const visibilities[] = {
+ "small",
+ "medium",
+ "large"
+ };
+ string_t string = { NULL, 0 };
+ parse_gnu_attribute_string_arg(attribute, &string);
+ if (string.begin != NULL) {
+ for(int i = 0; i < 3; ++i) {
+ if (strcmp(visibilities[i], string.begin) == 0) {
+ attribute->u.value = i;
+ return;
+ }
+ }
+ errorf(HERE, "'%s' is an unrecognized model", string.begin);
+ }
+ attribute->invalid = true;
+}
+
+static void parse_gnu_attribute_mode_arg(gnu_attribute_t *attribute)
+{
+ /* TODO: find out what is allowed here... */
+
+ /* at least: byte, word, pointer, list of machine modes
+ * __XXX___ is interpreted as XXX */
+ add_anchor_token(')');
+
+ if (token.type != T_IDENTIFIER) {
+ expect(T_IDENTIFIER);
+ }
+
+ /* This isn't really correct, the backend should provide a list of machine
+ * specific modes (according to gcc philosophy that is...) */
+ const char *symbol_str = token.v.symbol->string;
+ if (strcmp_underscore("QI", symbol_str) == 0 ||
+ strcmp_underscore("byte", symbol_str) == 0) {
+ attribute->u.akind = ATOMIC_TYPE_CHAR;
+ } else if (strcmp_underscore("HI", symbol_str) == 0) {
+ attribute->u.akind = ATOMIC_TYPE_SHORT;
+ } else if (strcmp_underscore("SI", symbol_str) == 0
+ || strcmp_underscore("word", symbol_str) == 0
+ || strcmp_underscore("pointer", symbol_str) == 0) {
+ attribute->u.akind = ATOMIC_TYPE_INT;
+ } else if (strcmp_underscore("DI", symbol_str) == 0) {
+ attribute->u.akind = ATOMIC_TYPE_LONGLONG;
+ } else {
+ warningf(HERE, "ignoring unknown mode '%s'", symbol_str);
+ attribute->invalid = true;
+ }
+ next_token();
+
+ rem_anchor_token(')');
+ expect(')');
+ return;
+end_error:
+ attribute->invalid = true;
+}
+
+/**
+ * parse one interrupt argument.
+ */
+static void parse_gnu_attribute_interrupt_arg(gnu_attribute_t *attribute)
+{
+ static const char *const interrupts[] = {
+ "IRQ",
+ "FIQ",
+ "SWI",
+ "ABORT",
+ "UNDEF"
+ };
+ string_t string = { NULL, 0 };
+ parse_gnu_attribute_string_arg(attribute, &string);
+ if (string.begin != NULL) {
+ for(size_t i = 0; i < 5; ++i) {
+ if (strcmp(interrupts[i], string.begin) == 0) {
+ attribute->u.value = i;
+ return;
+ }
+ }
+ errorf(HERE, "'%s' is not an interrupt", string.begin);
+ }
+ attribute->invalid = true;
+}
+
+/**
+ * parse ( identifier, const expression, const expression )
+ */
+static void parse_gnu_attribute_format_args(gnu_attribute_t *attribute)
+{
+ static const char *const format_names[] = {
+ "printf",
+ "scanf",
+ "strftime",
+ "strfmon"
+ };
+ int i;
+
+ if (token.type != T_IDENTIFIER) {
+ parse_error_expected("while parsing format attribute directive", T_IDENTIFIER, NULL);
+ goto end_error;
+ }
+ const char *name = token.v.symbol->string;
+ for(i = 0; i < 4; ++i) {
+ if (strcmp_underscore(format_names[i], name) == 0)
+ break;
+ }
+ if (i >= 4) {
+ if (warning.attribute)
+ warningf(HERE, "'%s' is an unrecognized format function type", name);
+ }
+ next_token();
+
+ expect(',');
+ add_anchor_token(')');
+ add_anchor_token(',');
+ parse_constant_expression();
+ rem_anchor_token(',');
+ rem_anchor_token(')');
+
+ expect(',');
+ add_anchor_token(')');
+ parse_constant_expression();
+ rem_anchor_token(')');
+ expect(')');
+ return;
+end_error:
+ attribute->u.value = true;
+}
+
+static void check_no_argument(gnu_attribute_t *attribute, const char *name)
+{
+ if (!attribute->have_arguments)
+ return;
+
+ /* should have no arguments */
+ errorf(HERE, "wrong number of arguments specified for '%s' attribute", name);
+ eat_until_matching_token('(');
+ /* we have already consumed '(', so we stop before ')', eat it */
+ eat(')');
+ attribute->invalid = true;
+}
+
+/**
+ * Parse one GNU attribute.
+ *
+ * Note that attribute names can be specified WITH or WITHOUT
+ * double underscores, ie const or __const__.
+ *
+ * The following attributes are parsed without arguments
+ * const
+ * volatile
+ * cdecl
+ * stdcall
+ * fastcall
+ * deprecated
+ * noinline
+ * noreturn
+ * naked
+ * pure
+ * always_inline
+ * malloc
+ * weak
+ * constructor
+ * destructor
+ * nothrow
+ * transparent_union
+ * common
+ * nocommon
+ * packed
+ * shared
+ * notshared
+ * used
+ * unused
+ * no_instrument_function
+ * warn_unused_result
+ * longcall
+ * shortcall
+ * long_call
+ * short_call
+ * function_vector
+ * interrupt_handler
+ * nmi_handler
+ * nesting
+ * near
+ * far
+ * signal
+ * eightbit_data
+ * tiny_data
+ * saveall
+ * flatten
+ * sseregparm
+ * externally_visible
+ * return_twice
+ * may_alias
+ * ms_struct
+ * gcc_struct
+ * dllimport
+ * dllexport
+ *
+ * The following attributes are parsed with arguments
+ * aligned( const expression )
+ * alias( string literal )
+ * section( string literal )
+ * format( identifier, const expression, const expression )
+ * format_arg( const expression )
+ * tls_model( string literal )
+ * visibility( string literal )
+ * regparm( const expression )
+ * model( string leteral )
+ * trap_exit( const expression )
+ * sp_switch( string literal )
+ *
+ * The following attributes might have arguments
+ * weak_ref( string literal )
+ * non_null( const expression // ',' )
+ * interrupt( string literal )
+ * sentinel( constant expression )
+ */
+static decl_modifiers_t parse_gnu_attribute(gnu_attribute_t **attributes)
+{
+ gnu_attribute_t *head = *attributes;
+ gnu_attribute_t *last = *attributes;
+ decl_modifiers_t modifiers = 0;
+ gnu_attribute_t *attribute;
+
+ eat(T___attribute__);
+ expect('(');
+ expect('(');
+
+ if (token.type != ')') {
+ /* find the end of the list */
+ if (last != NULL) {
+ while (last->next != NULL)
+ last = last->next;
+ }
+
+ /* non-empty attribute list */
+ while (true) {
+ const char *name;
+ if (token.type == T_const) {
+ name = "const";
+ } else if (token.type == T_volatile) {
+ name = "volatile";
+ } else if (token.type == T_cdecl) {
+ /* __attribute__((cdecl)), WITH ms mode */
+ name = "cdecl";
+ } else if (token.type == T_IDENTIFIER) {
+ const symbol_t *sym = token.v.symbol;
+ name = sym->string;
+ } else {
+ parse_error_expected("while parsing GNU attribute", T_IDENTIFIER, NULL);
+ break;
+ }
+
+ next_token();
+
+ int i;
+ for(i = 0; i < GNU_AK_LAST; ++i) {
+ if (strcmp_underscore(gnu_attribute_names[i], name) == 0)
+ break;
+ }
+ gnu_attribute_kind_t kind = (gnu_attribute_kind_t)i;
+
+ attribute = NULL;
+ if (kind == GNU_AK_LAST) {
+ if (warning.attribute)
+ warningf(HERE, "'%s' attribute directive ignored", name);
+
+ /* skip possible arguments */
+ if (token.type == '(') {
+ eat_until_matching_token(')');
+ }
+ } else {
+ /* check for arguments */
+ attribute = allocate_gnu_attribute(kind);
+ if (token.type == '(') {
+ next_token();
+ if (token.type == ')') {
+ /* empty args are allowed */
+ next_token();
+ } else
+ attribute->have_arguments = true;
+ }
+
+ switch(kind) {
+ case GNU_AK_CONST:
+ case GNU_AK_VOLATILE:
+ case GNU_AK_NAKED:
+ case GNU_AK_MALLOC:
+ case GNU_AK_WEAK:
+ case GNU_AK_COMMON:
+ case GNU_AK_NOCOMMON:
+ case GNU_AK_SHARED:
+ case GNU_AK_NOTSHARED:
+ case GNU_AK_NO_INSTRUMENT_FUNCTION:
+ case GNU_AK_WARN_UNUSED_RESULT:
+ case GNU_AK_LONGCALL:
+ case GNU_AK_SHORTCALL:
+ case GNU_AK_LONG_CALL:
+ case GNU_AK_SHORT_CALL:
+ case GNU_AK_FUNCTION_VECTOR:
+ case GNU_AK_INTERRUPT_HANDLER:
+ case GNU_AK_NMI_HANDLER:
+ case GNU_AK_NESTING:
+ case GNU_AK_NEAR:
+ case GNU_AK_FAR:
+ case GNU_AK_SIGNAL:
+ case GNU_AK_EIGTHBIT_DATA:
+ case GNU_AK_TINY_DATA:
+ case GNU_AK_SAVEALL:
+ case GNU_AK_FLATTEN:
+ case GNU_AK_SSEREGPARM:
+ case GNU_AK_EXTERNALLY_VISIBLE:
+ case GNU_AK_RETURN_TWICE:
+ case GNU_AK_MAY_ALIAS:
+ case GNU_AK_MS_STRUCT:
+ case GNU_AK_GCC_STRUCT:
+ goto no_arg;
+
+ case GNU_AK_CDECL: modifiers |= DM_CDECL; goto no_arg;
+ case GNU_AK_FASTCALL: modifiers |= DM_FASTCALL; goto no_arg;
+ case GNU_AK_STDCALL: modifiers |= DM_STDCALL; goto no_arg;
+ case GNU_AK_UNUSED: modifiers |= DM_UNUSED; goto no_arg;
+ case GNU_AK_USED: modifiers |= DM_USED; goto no_arg;
+ case GNU_AK_PURE: modifiers |= DM_PURE; goto no_arg;
+ case GNU_AK_ALWAYS_INLINE: modifiers |= DM_FORCEINLINE; goto no_arg;
+ case GNU_AK_DLLIMPORT: modifiers |= DM_DLLIMPORT; goto no_arg;
+ case GNU_AK_DLLEXPORT: modifiers |= DM_DLLEXPORT; goto no_arg;
+ case GNU_AK_PACKED: modifiers |= DM_PACKED; goto no_arg;
+ case GNU_AK_NOINLINE: modifiers |= DM_NOINLINE; goto no_arg;
+ case GNU_AK_NORETURN: modifiers |= DM_NORETURN; goto no_arg;
+ case GNU_AK_NOTHROW: modifiers |= DM_NOTHROW; goto no_arg;
+ case GNU_AK_TRANSPARENT_UNION: modifiers |= DM_TRANSPARENT_UNION; goto no_arg;
+ case GNU_AK_CONSTRUCTOR: modifiers |= DM_CONSTRUCTOR; goto no_arg;
+ case GNU_AK_DESTRUCTOR: modifiers |= DM_DESTRUCTOR; goto no_arg;
+ case GNU_AK_DEPRECATED: modifiers |= DM_DEPRECATED; goto no_arg;
+
+ case GNU_AK_ALIGNED:
+ /* __align__ may be used without an argument */
+ if (attribute->have_arguments) {
+ parse_gnu_attribute_const_arg(attribute);
+ }
+ break;
+
+ case GNU_AK_FORMAT_ARG:
+ case GNU_AK_REGPARM:
+ case GNU_AK_TRAP_EXIT:
+ if (!attribute->have_arguments) {
+ /* should have arguments */
+ errorf(HERE, "wrong number of arguments specified for '%s' attribute", name);
+ attribute->invalid = true;
+ } else
+ parse_gnu_attribute_const_arg(attribute);
+ break;
+ case GNU_AK_ALIAS:
+ case GNU_AK_SECTION:
+ case GNU_AK_SP_SWITCH:
+ if (!attribute->have_arguments) {
+ /* should have arguments */
+ errorf(HERE, "wrong number of arguments specified for '%s' attribute", name);
+ attribute->invalid = true;
+ } else
+ parse_gnu_attribute_string_arg(attribute, &attribute->u.string);
+ break;
+ case GNU_AK_FORMAT:
+ if (!attribute->have_arguments) {
+ /* should have arguments */
+ errorf(HERE, "wrong number of arguments specified for '%s' attribute", name);
+ attribute->invalid = true;
+ } else
+ parse_gnu_attribute_format_args(attribute);
+ break;
+ case GNU_AK_WEAKREF:
+ /* may have one string argument */
+ if (attribute->have_arguments)
+ parse_gnu_attribute_string_arg(attribute, &attribute->u.string);
+ break;
+ case GNU_AK_NONNULL:
+ if (attribute->have_arguments)
+ parse_gnu_attribute_const_arg_list(attribute);
+ break;
+ case GNU_AK_TLS_MODEL:
+ if (!attribute->have_arguments) {
+ /* should have arguments */
+ errorf(HERE, "wrong number of arguments specified for '%s' attribute", name);
+ } else
+ parse_gnu_attribute_tls_model_arg(attribute);
+ break;
+ case GNU_AK_VISIBILITY:
+ if (!attribute->have_arguments) {
+ /* should have arguments */
+ errorf(HERE, "wrong number of arguments specified for '%s' attribute", name);
+ } else
+ parse_gnu_attribute_visibility_arg(attribute);
+ break;
+ case GNU_AK_MODEL:
+ if (!attribute->have_arguments) {
+ /* should have arguments */
+ errorf(HERE, "wrong number of arguments specified for '%s' attribute", name);
+ } else {
+ parse_gnu_attribute_model_arg(attribute);
+ }
+ break;
+ case GNU_AK_MODE:
+ if (!attribute->have_arguments) {
+ /* should have arguments */
+ errorf(HERE, "wrong number of arguments specified for '%s' attribute", name);
+ } else {
+ parse_gnu_attribute_mode_arg(attribute);
+ }
+ break;
+ case GNU_AK_INTERRUPT:
+ /* may have one string argument */
+ if (attribute->have_arguments)
+ parse_gnu_attribute_interrupt_arg(attribute);
+ break;
+ case GNU_AK_SENTINEL:
+ /* may have one string argument */
+ if (attribute->have_arguments)
+ parse_gnu_attribute_const_arg(attribute);
+ break;
+ case GNU_AK_LAST:
+ /* already handled */
+ break;
+
+no_arg:
+ check_no_argument(attribute, name);
+ }
+ }
+ if (attribute != NULL) {
+ if (last != NULL) {
+ last->next = attribute;
+ last = attribute;
+ } else {
+ head = last = attribute;
+ }
+ }
+
+ if (token.type != ',')
+ break;
+ next_token();
+ }
+ }
+ expect(')');
+ expect(')');
+end_error:
+ *attributes = head;
+
+ return modifiers;
+}
+
+/**
+ * Parse GNU attributes.
+ */
+static decl_modifiers_t parse_attributes(gnu_attribute_t **attributes)
+{
+ decl_modifiers_t modifiers = 0;
+
+ while (true) {
+ switch(token.type) {
+ case T___attribute__:
+ modifiers |= parse_gnu_attribute(attributes);
+ continue;
+
+ case T_asm:
+ next_token();
+ expect('(');
+ if (token.type != T_STRING_LITERAL) {
+ parse_error_expected("while parsing assembler attribute",
+ T_STRING_LITERAL, NULL);
+ eat_until_matching_token('(');
+ break;
+ } else {
+ parse_string_literals();
+ }
+ expect(')');
+ continue;
+
+ case T_cdecl: modifiers |= DM_CDECL; break;
+ case T__fastcall: modifiers |= DM_FASTCALL; break;
+ case T__stdcall: modifiers |= DM_STDCALL; break;
+
+ case T___thiscall:
+ /* TODO record modifier */
+ warningf(HERE, "Ignoring declaration modifier %K", &token);
+ break;
+
+end_error:
+ default: return modifiers;
+ }
+
+ next_token();
+ }
+}
+
+static designator_t *parse_designation(void)
+{
+ designator_t *result = NULL;
+ designator_t *last = NULL;
+
+ while (true) {
+ designator_t *designator;