+ gnu_attribute_t *attribute = obstack_alloc(&temp_obst, sizeof(*attribute));
+ attribute->kind = kind;
+ attribute->next = NULL;
+ attribute->invalid = false;
+ attribute->have_arguments = false;
+
+ 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('(');