From: Michael Beck Date: Sun, 14 Sep 2008 00:51:19 +0000 (+0000) Subject: - implemented computed goto X-Git-Url: http://nsz.repo.hu/git/?a=commitdiff_plain;h=75054c64100620c5b7ecac17b8fcf6dfe42e8f94;p=cparser - implemented computed goto [r21932] --- diff --git a/ast.c b/ast.c index d08f6a0..70dc234 100644 --- 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 --- 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; diff --git a/ast2firm.c b/ast2firm.c index 390c53b..f3f8f20 100644 --- a/ast2firm.c +++ b/ast2firm.c @@ -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 --- 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 { diff --git a/parser.c b/parser.c index c11906c..f3674c1 100644 --- 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 == ¤t_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 == ¤t_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) {