[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,
{
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);
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.
*
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;
bool is_constant_expression(const expression_t *expression)
{
- switch(expression->kind) {
+ switch (expression->kind) {
case EXPR_CONST:
case EXPR_CHARACTER_CONSTANT:
case EXPR_OFFSETOF:
case EXPR_ALIGNOF:
case EXPR_BUILTIN_CONSTANT_P:
+ case EXPR_LABEL_ADDRESS:
return true;
case EXPR_SIZEOF: {
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;
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;
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);
/* 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;
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
}
#endif
- switch(expression->kind) {
+ switch (expression->kind) {
case EXPR_CHARACTER_CONSTANT:
return character_constant_to_firm(&expression->conste);
case EXPR_WIDE_CHARACTER_CONSTANT:
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:
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();
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);
}
}
}
-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);
}
set_cur_block(block);
- keep_alive(get_store());
keep_alive(block);
+ keep_all_memory(block);
if (statement->statement != NULL) {
statement_to_firm(statement->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);
}
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.
*/
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));
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);
set_type_alignment_bytes(frame_type, align_all);
irg_vrfy(irg);
+ current_function = old_current_function;
}
static void scope_to_firm(scope_t *scope)
EXPR_VA_START,
EXPR_VA_ARG,
EXPR_STATEMENT,
+ EXPR_LABEL_ADDRESS, /**< GCC extension &&label operator */
EXPR_UNARY_FIRST,
EXPR_UNARY_NEGATE = EXPR_UNARY_FIRST,
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;
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 {
[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];
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;
}
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:
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.
*/
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();
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;
}
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.
*/
*/
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) {