- implemented computed goto
authorMichael Beck <beck@ipd.info.uni-karlsruhe.de>
Sun, 14 Sep 2008 00:51:19 +0000 (00:51 +0000)
committerMichael Beck <beck@ipd.info.uni-karlsruhe.de>
Sun, 14 Sep 2008 00:51:19 +0000 (00:51 +0000)
[r21932]

ast.c
ast.h
ast2firm.c
ast_t.h
parser.c

diff --git a/ast.c b/ast.c
index d08f6a0..70dc234 100644 (file)
--- a/ast.c
+++ b/ast.c
@@ -125,6 +125,7 @@ static unsigned get_expression_precedence(expression_kind_t kind)
                [EXPR_VA_START]                  = PREC_PRIM,
                [EXPR_VA_ARG]                    = PREC_PRIM,
                [EXPR_STATEMENT]                 = PREC_ACCESS,
+               [EXPR_LABEL_ADDRESS]             = PREC_PRIM,
 
                [EXPR_UNARY_NEGATE]              = PREC_UNARY,
                [EXPR_UNARY_PLUS]                = PREC_UNARY,
@@ -478,14 +479,14 @@ static void print_unary_expression(const unary_expression_t *unexpr)
 {
        unsigned prec = get_expression_precedence(unexpr->base.kind);
        switch(unexpr->base.kind) {
-       case EXPR_UNARY_NEGATE:           fputs("-", out);  break;
-       case EXPR_UNARY_PLUS:             fputs("+", out);  break;
-       case EXPR_UNARY_NOT:              fputs("!", out);  break;
-       case EXPR_UNARY_BITWISE_NEGATE:   fputs("~", out);  break;
+       case EXPR_UNARY_NEGATE:           fputc('-', out);  break;
+       case EXPR_UNARY_PLUS:             fputc('+', out);  break;
+       case EXPR_UNARY_NOT:              fputc('!', out);  break;
+       case EXPR_UNARY_BITWISE_NEGATE:   fputc('~', out);  break;
        case EXPR_UNARY_PREFIX_INCREMENT: fputs("++", out); break;
        case EXPR_UNARY_PREFIX_DECREMENT: fputs("--", out); break;
-       case EXPR_UNARY_DEREFERENCE:      fputs("*", out);  break;
-       case EXPR_UNARY_TAKE_ADDRESS:     fputs("&", out);  break;
+       case EXPR_UNARY_DEREFERENCE:      fputc('*', out);  break;
+       case EXPR_UNARY_TAKE_ADDRESS:     fputc('&', out);  break;
 
        case EXPR_UNARY_POSTFIX_INCREMENT:
                print_expression_prec(unexpr->value, prec);
@@ -522,6 +523,16 @@ static void print_reference_expression(const reference_expression_t *ref)
        fputs(ref->declaration->symbol->string, out);
 }
 
+/**
+ * Prints a label address expression.
+ *
+ * @param ref   the reference expression
+ */
+static void print_label_address_expression(const label_address_expression_t *le)
+{
+       fprintf(out, "&&%s", le->declaration->symbol->string);
+}
+
 /**
  * Prints an array expression.
  *
@@ -786,6 +797,9 @@ static void print_expression_prec(const expression_t *expression, unsigned top_p
        case EXPR_ARRAY_ACCESS:
                print_array_expression(&expression->array_access);
                break;
+       case EXPR_LABEL_ADDRESS:
+               print_label_address_expression(&expression->label_address);
+               break;
        EXPR_UNARY_CASES
                print_unary_expression(&expression->unary);
                break;
@@ -1659,7 +1673,7 @@ static bool is_object_with_constant_address(const expression_t *expression)
 
 bool is_constant_expression(const expression_t *expression)
 {
-       switch(expression->kind) {
+       switch (expression->kind) {
 
        case EXPR_CONST:
        case EXPR_CHARACTER_CONSTANT:
@@ -1671,6 +1685,7 @@ bool is_constant_expression(const expression_t *expression)
        case EXPR_OFFSETOF:
        case EXPR_ALIGNOF:
        case EXPR_BUILTIN_CONSTANT_P:
+       case EXPR_LABEL_ADDRESS:
                return true;
 
        case EXPR_SIZEOF: {
diff --git a/ast.h b/ast.h
index c6ddb82..1de96dc 100644 (file)
--- a/ast.h
+++ b/ast.h
@@ -54,6 +54,7 @@ typedef struct builtin_constant_expression_t    builtin_constant_expression_t;
 typedef struct builtin_prefetch_expression_t    builtin_prefetch_expression_t;
 typedef struct classify_type_expression_t       classify_type_expression_t;
 typedef struct bitfield_extract_expression_t    bitfield_extract_expression_t;
+typedef struct label_address_expression_t       label_address_expression_t;
 typedef union  expression_t                     expression_t;
 
 typedef struct initializer_base_t           initializer_base_t;
index 390c53b..f3f8f20 100644 (file)
@@ -53,18 +53,20 @@ static ir_type *ir_type_wchar_t;
 static ir_type *ir_type_void;
 static ir_type *ir_type_int;
 
-static int       next_value_number_function;
-static ir_node  *continue_label;
-static ir_node  *break_label;
-static ir_node  *current_switch_cond;
-static bool      saw_default_label;
-static ir_node **immature_blocks;
-static bool constant_folding;
+static int             next_value_number_function;
+static ir_node        *continue_label;
+static ir_node        *break_label;
+static ir_node        *current_switch_cond;
+static bool            saw_default_label;
+static declaration_t **all_labels;
+static ir_node        *ijmp_list;
+static bool            constant_folding;
 
 static const declaration_t *current_function_decl;
 static ir_node             *current_function_name;
 static ir_node             *current_funcsig;
 static switch_statement_t  *current_switch;
+static ir_graph            *current_function;
 
 static entitymap_t  entitymap;
 
@@ -1388,6 +1390,18 @@ static ir_node *create_conv(dbg_info *dbgi, ir_node *value, ir_mode *dest_mode)
        return new_d_Conv(dbgi, value, dest_mode);
 }
 
+/**
+ * Keep all memory edges of the given block.
+ */
+static void keep_all_memory(ir_node *block) {
+       ir_node *old = get_cur_block();
+
+       set_cur_block(block);
+       keep_alive(get_store());
+       /* TODO: keep all memory edges from restricted pointers */
+       set_cur_block(old);
+}
+
 static ir_node *reference_expression_to_firm(const reference_expression_t *ref)
 {
        dbg_info      *dbgi        = get_dbg_info(&ref->base.source_position);
@@ -1397,7 +1411,7 @@ static ir_node *reference_expression_to_firm(const reference_expression_t *ref)
        /* make sure the type is constructed */
        (void) get_ir_type(type);
 
-       switch((declaration_kind_t) declaration->declaration_kind) {
+       switch ((declaration_kind_t)declaration->declaration_kind) {
        case DECLARATION_KIND_TYPE:
        case DECLARATION_KIND_UNKNOWN:
                break;
@@ -2940,6 +2954,52 @@ static ir_node *builtin_prefetch_to_firm(
        return NULL;
 }
 
+static ir_node *get_label_block(declaration_t *label)
+{
+       assert(label->namespc == NAMESPACE_LABEL);
+
+       if (label->declaration_kind == DECLARATION_KIND_LABEL_BLOCK) {
+               return label->v.block;
+       }
+       assert(label->declaration_kind == DECLARATION_KIND_UNKNOWN);
+
+       /* beware: might be called from create initializer with current_ir_graph
+        * set to const_code_irg. */
+       ir_graph *rem    = current_ir_graph;
+       current_ir_graph = current_function;
+
+       ir_node *old_cur_block = get_cur_block();
+       ir_node *block         = new_immBlock();
+       set_cur_block(old_cur_block);
+
+       label->declaration_kind = DECLARATION_KIND_LABEL_BLOCK;
+       label->v.block          = block;
+
+       ARR_APP1(declaration_t *, all_labels, label);
+
+       current_ir_graph = rem;
+       return block;
+}
+
+/**
+ * Pointer to a label.  This is used for the
+ * GNU address-of-label extension.
+ */
+static ir_node *label_address_to_firm(
+               const label_address_expression_t *label)
+{
+       ir_node    *block = get_label_block(label->declaration);
+       ir_label_t  nr    = get_Block_label(block);
+
+       if (nr == 0) {
+               nr = get_irp_next_label_nr();
+               set_Block_label(block, nr);
+       }
+       symconst_symbol value;
+       value.label = nr;
+       return new_SymConst(mode_P_code, value, symconst_label);
+}
+
 /**
  * creates firm nodes for an expression. The difference between this function
  * and expression_to_firm is, that this version might produce mode_b nodes
@@ -2954,7 +3014,7 @@ static ir_node *_expression_to_firm(const expression_t *expression)
        }
 #endif
 
-       switch(expression->kind) {
+       switch (expression->kind) {
        case EXPR_CHARACTER_CONSTANT:
                return character_constant_to_firm(&expression->conste);
        case EXPR_WIDE_CHARACTER_CONSTANT:
@@ -3003,6 +3063,8 @@ static ir_node *_expression_to_firm(const expression_t *expression)
                return offsetof_to_firm(&expression->offsetofe);
        case EXPR_COMPOUND_LITERAL:
                return compound_literal_to_firm(&expression->compound_literal);
+       case EXPR_LABEL_ADDRESS:
+               return label_address_to_firm(&expression->label_address);
 
        case EXPR_UNKNOWN:
        case EXPR_INVALID:
@@ -4231,8 +4293,8 @@ static void while_statement_to_firm(while_statement_t *statement)
                add_immBlock_pred(body_block, header_jmp);
 
                keep_alive(body_block);
+               keep_all_memory(body_block);
                set_cur_block(body_block);
-               keep_alive(get_store());
        } else {
                if (false_block == NULL) {
                        false_block = new_immBlock();
@@ -4376,7 +4438,7 @@ static void for_statement_to_firm(for_statement_t *statement)
                                            false_block);
        } else {
                keep_alive(header_block);
-               keep_alive(get_store());
+               keep_all_memory(header_block);
                jmp = new_Jmp();
                add_immBlock_pred(body_block, jmp);
        }
@@ -4550,27 +4612,6 @@ static void case_label_to_firm(const case_label_statement_t *statement)
        }
 }
 
-static ir_node *get_label_block(declaration_t *label)
-{
-       assert(label->namespc == NAMESPACE_LABEL);
-
-       if (label->declaration_kind == DECLARATION_KIND_LABEL_BLOCK) {
-               return label->v.block;
-       }
-       assert(label->declaration_kind == DECLARATION_KIND_UNKNOWN);
-
-       ir_node *old_cur_block = get_cur_block();
-       ir_node *block         = new_immBlock();
-       set_cur_block(old_cur_block);
-
-       label->declaration_kind = DECLARATION_KIND_LABEL_BLOCK;
-       label->v.block          = block;
-
-       ARR_APP1(ir_node *, immature_blocks, block);
-
-       return block;
-}
-
 static void label_to_firm(const label_statement_t *statement)
 {
        ir_node *block = get_label_block(statement->label);
@@ -4581,8 +4622,8 @@ static void label_to_firm(const label_statement_t *statement)
        }
 
        set_cur_block(block);
-       keep_alive(get_store());
        keep_alive(block);
+       keep_all_memory(block);
 
        if (statement->statement != NULL) {
                statement_to_firm(statement->statement);
@@ -4594,10 +4635,18 @@ static void goto_to_firm(const goto_statement_t *statement)
        if (get_cur_block() == NULL)
                return;
 
-       ir_node *block = get_label_block(statement->label);
-       ir_node *jmp   = new_Jmp();
-       add_immBlock_pred(block, jmp);
+       if (statement->expression) {
+               ir_node  *irn  = expression_to_firm(statement->expression);
+               dbg_info *dbgi = get_dbg_info(&statement->base.source_position);
+               ir_node  *ijmp = new_d_IJmp(dbgi, irn);
 
+               set_irn_link(ijmp, ijmp_list);
+               ijmp_list = ijmp;
+       } else {
+               ir_node *block = get_label_block(statement->label);
+               ir_node *jmp   = new_Jmp();
+               add_immBlock_pred(block, jmp);
+       }
        set_cur_block(NULL);
 }
 
@@ -5267,6 +5316,16 @@ static void add_function_pointer(ir_type *segment, ir_entity *method,
        set_atomic_ent_value(ptr, val);
 }
 
+/**
+ * Generate possible IJmp branches to a given label block.
+ */
+static void gen_ijmp_branches(ir_node *block) {
+       ir_node *ijmp;
+       for (ijmp = ijmp_list; ijmp != NULL; ijmp = get_irn_link(ijmp)) {
+               add_immBlock_pred(block, ijmp);
+       }
+}
+
 /**
  * Create code for a function.
  */
@@ -5290,12 +5349,16 @@ static void create_function(declaration_t *declaration)
        current_function_name = NULL;
        current_funcsig       = NULL;
 
-       assert(immature_blocks == NULL);
-       immature_blocks = NEW_ARR_F(ir_node*, 0);
+       assert(all_labels == NULL);
+       all_labels = NEW_ARR_F(declaration_t *, 0);
+       ijmp_list  = NULL;
 
        int       n_local_vars = get_function_n_local_vars(declaration);
        ir_graph *irg          = new_ir_graph(function_entity, n_local_vars);
 
+       ir_graph *old_current_function = current_function;
+       current_function = irg;
+
        set_irg_fp_model(irg, firm_opt.fp_model);
        tarval_enable_fp_ops((firm_opt.fp_model & fp_strict_algebraic) == 0);
        set_irn_dbg_info(get_irg_start_block(irg), get_entity_dbg_info(function_entity));
@@ -5345,11 +5408,27 @@ static void create_function(declaration_t *declaration)
                add_immBlock_pred(end_block, ret);
        }
 
-       for(int i = 0; i < ARR_LEN(immature_blocks); ++i) {
-               mature_immBlock(immature_blocks[i]);
+       bool has_computed_gotos = false;
+       for (int i = ARR_LEN(all_labels) - 1; i >= 0; --i) {
+               declaration_t *label = all_labels[i];
+               if (label->address_taken) {
+                       gen_ijmp_branches(label->v.block);
+                       has_computed_gotos = true;
+               }
+               mature_immBlock(label->v.block);
+       }
+       if (has_computed_gotos) {
+               /* if we have computed goto's in the function, we cannot inline it */
+               if (get_irg_inline_property(irg) >= irg_inline_recomended) {
+                       warningf(&declaration->source_position,
+                               "function '%Y' can never be inlined because it contains a computed goto",
+                               declaration->symbol);
+               }
+               set_irg_inline_property(irg, irg_inline_forbidden);
        }
-       DEL_ARR_F(immature_blocks);
-       immature_blocks = NULL;
+
+       DEL_ARR_F(all_labels);
+       all_labels = NULL;
 
        mature_immBlock(first_block);
        mature_immBlock(end_block);
@@ -5383,6 +5462,7 @@ static void create_function(declaration_t *declaration)
        set_type_alignment_bytes(frame_type, align_all);
 
        irg_vrfy(irg);
+       current_function = old_current_function;
 }
 
 static void scope_to_firm(scope_t *scope)
diff --git a/ast_t.h b/ast_t.h
index 508f19a..3848373 100644 (file)
--- a/ast_t.h
+++ b/ast_t.h
@@ -61,6 +61,7 @@ typedef enum expression_kind_t {
        EXPR_VA_START,
        EXPR_VA_ARG,
        EXPR_STATEMENT,
+       EXPR_LABEL_ADDRESS, /**< GCC extension &&label operator */
 
        EXPR_UNARY_FIRST,
        EXPR_UNARY_NEGATE = EXPR_UNARY_FIRST,
@@ -337,6 +338,11 @@ struct classify_type_expression_t {
        expression_t      *type_expression;
 };
 
+struct label_address_expression_t {
+       expression_base_t  base;
+       declaration_t     *declaration;
+};
+
 union expression_t {
        expression_kind_t                kind;
        expression_base_t                base;
@@ -361,6 +367,7 @@ union expression_t {
        conditional_expression_t         conditional;
        statement_expression_t           statement;
        classify_type_expression_t       classify_type;
+       label_address_expression_t       label_address;
 };
 
 typedef enum storage_class_tag_t {
index c11906c..f3674c1 100644 (file)
--- a/parser.c
+++ b/parser.c
@@ -315,6 +315,7 @@ static size_t get_expression_struct_size(expression_kind_t kind)
                [EXPR_VA_START]                = sizeof(va_start_expression_t),
                [EXPR_VA_ARG]                  = sizeof(va_arg_expression_t),
                [EXPR_STATEMENT]               = sizeof(statement_expression_t),
+               [EXPR_LABEL_ADDRESS]           = sizeof(label_address_expression_t),
        };
        if (kind >= EXPR_UNARY_FIRST && kind <= EXPR_UNARY_LAST) {
                return sizes[EXPR_UNARY_FIRST];
@@ -4794,6 +4795,10 @@ static void check_labels(void)
        for (const goto_statement_t *goto_statement = goto_first;
            goto_statement != NULL;
            goto_statement = goto_statement->next) {
+               /* skip computed gotos */
+               if (goto_statement->expression != NULL)
+                       continue;
+
                declaration_t *label = goto_statement->label;
 
                label->used = true;
@@ -4999,9 +5004,16 @@ found_break_parent:
                }
 
                case STATEMENT_GOTO:
-                       next = stmt->gotos.label->init.statement;
-                       if (next == NULL) /* missing label */
-                               return;
+                       if (stmt->gotos.expression) {
+                               statement_t *parent = stmt->base.parent;
+                               if (parent == NULL) /* top level goto */
+                                       return;
+                               next = parent;
+                       } else {
+                               next = stmt->gotos.label->init.statement;
+                               if (next == NULL) /* missing label */
+                                       return;
+                       }
                        break;
 
                case STATEMENT_LABEL:
@@ -6594,6 +6606,62 @@ end_error:
        return create_invalid_expression();
 }
 
+/**
+ * Return the declaration for a given label symbol or create a new one.
+ *
+ * @param symbol  the symbol of the label
+ */
+static declaration_t *get_label(symbol_t *symbol)
+{
+       declaration_t *candidate = get_declaration(symbol, NAMESPACE_LABEL);
+       assert(current_function != NULL);
+       /* if we found a label in the same function, then we already created the
+        * declaration */
+       if (candidate != NULL
+                       && candidate->parent_scope == &current_function->scope) {
+               return candidate;
+       }
+
+       /* otherwise we need to create a new one */
+       declaration_t *const declaration = allocate_declaration_zero();
+       declaration->namespc       = NAMESPACE_LABEL;
+       declaration->symbol        = symbol;
+
+       label_push(declaration);
+
+       return declaration;
+}
+
+/**
+ * Parses a GNU && label address expression.
+ */
+static expression_t *parse_label_address(void)
+{
+       source_position_t source_position = token.source_position;
+       eat(T_ANDAND);
+       if (token.type != T_IDENTIFIER) {
+               parse_error_expected("while parsing label address", T_IDENTIFIER, NULL);
+               goto end_error;
+       }
+       symbol_t *symbol = token.v.symbol;
+       next_token();
+
+       declaration_t *label = get_label(symbol);
+
+       label->used          = true;
+       label->address_taken = true;
+
+       expression_t *expression = allocate_expression_zero(EXPR_LABEL_ADDRESS);
+       expression->base.source_position = source_position;
+
+       /* label address is threaten as a void pointer */
+       expression->base.type                 = type_void_ptr;
+       expression->label_address.declaration = label;
+       return expression;
+end_error:
+       return create_invalid_expression();
+}
+
 /**
  * Parse a microsoft __noop expression.
  */
@@ -6671,6 +6739,10 @@ static expression_t *parse_primary_expression(void)
                case T___builtin_constant_p:     return parse_builtin_constant();
                case T___builtin_prefetch:       return parse_builtin_prefetch();
                case T__assume:                  return parse_assume();
+               case T_ANDAND:
+                       if (c_mode & _GNUC)
+                               return parse_label_address();
+                       break;
 
                case '(':                        return parse_parenthesized_expression();
                case T___noop:                   return parse_noop_expression();
@@ -7376,7 +7448,7 @@ static void semantic_dereference(unary_expression_t *expression)
        if (!is_type_pointer(type)) {
                if (is_type_valid(type)) {
                        errorf(&expression->base.source_position,
-                              "Unary '*' needs pointer or arrray type, but type '%T' given", orig_type);
+                              "Unary '*' needs pointer or array type, but type '%T' given", orig_type);
                }
                return;
        }
@@ -8622,32 +8694,6 @@ end_error:
        return create_invalid_statement();
 }
 
-/**
- * Return the declaration for a given label symbol or create a new one.
- *
- * @param symbol  the symbol of the label
- */
-static declaration_t *get_label(symbol_t *symbol)
-{
-       declaration_t *candidate = get_declaration(symbol, NAMESPACE_LABEL);
-       assert(current_function != NULL);
-       /* if we found a label in the same function, then we already created the
-        * declaration */
-       if (candidate != NULL
-                       && candidate->parent_scope == &current_function->scope) {
-               return candidate;
-       }
-
-       /* otherwise we need to create a new one */
-       declaration_t *const declaration = allocate_declaration_zero();
-       declaration->namespc       = NAMESPACE_LABEL;
-       declaration->symbol        = symbol;
-
-       label_push(declaration);
-
-       return declaration;
-}
-
 /**
  * Parse a label statement.
  */
@@ -8985,22 +9031,47 @@ end_error:
  */
 static statement_t *parse_goto(void)
 {
+       source_position_t source_position = token.source_position;
        eat(T_goto);
 
-       if (token.type != T_IDENTIFIER) {
-               parse_error_expected("while parsing goto", T_IDENTIFIER, NULL);
-               eat_statement();
-               goto end_error;
-       }
-       symbol_t *symbol = token.v.symbol;
-       next_token();
+       statement_t *statement;
+       if (c_mode & _GNUC && token.type == '*') {
+               next_token();
+               expression_t *expression = parse_expression();
 
-       declaration_t *label = get_label(symbol);
+               /* Argh: although documentation say the expression must be of type void *,
+                * gcc excepts anything that can be casted into void * without error */
+               type_t *type = expression->base.type;
 
-       statement_t *statement          = allocate_statement_zero(STATEMENT_GOTO);
-       statement->base.source_position = token.source_position;
+               if (type != type_error_type) {
+                       if (!is_type_pointer(type) && !is_type_integer(type)) {
+                               errorf(&source_position, "cannot convert to a pointer type");
+                       } else if (type != type_void_ptr) {
+                               warningf(&source_position,
+                                       "type of computed goto expression should be 'void*' not '%T'", type);
+                       }
+                       expression = create_implicit_cast(expression, type_void_ptr);
+               }
+
+               statement                       = allocate_statement_zero(STATEMENT_GOTO);
+               statement->base.source_position = source_position;
+               statement->gotos.expression     = expression;
+       } else {
+               if (token.type != T_IDENTIFIER) {
+                       if (c_mode & _GNUC)
+                               parse_error_expected("while parsing goto", T_IDENTIFIER, '*', NULL);
+                       else
+                               parse_error_expected("while parsing goto", T_IDENTIFIER, NULL);
+                       eat_statement();
+                       goto end_error;
+               }
+               symbol_t *symbol = token.v.symbol;
+               next_token();
 
-       statement->gotos.label = label;
+               statement                       = allocate_statement_zero(STATEMENT_GOTO);
+               statement->base.source_position = source_position;
+               statement->gotos.label          = get_label(symbol);
+       }
 
        /* remember the goto's in a list for later checking */
        if (goto_last == NULL) {