From: Matthias Braun Date: Thu, 9 Jun 2011 16:55:48 +0000 (+0200) Subject: unify sparc/ia32 SwitchJmp handling X-Git-Url: http://nsz.repo.hu/git/?a=commitdiff_plain;h=b46a6da6b265982882b0705ed5104a24d0deb154;p=libfirm unify sparc/ia32 SwitchJmp handling --- diff --git a/ir/be/begnuas.c b/ir/be/begnuas.c index 23151694b..5d826161c 100644 --- a/ir/be/begnuas.c +++ b/ir/be/begnuas.c @@ -1659,3 +1659,65 @@ void be_gas_emit_decls(const be_main_env_t *main_env) be_emit_write_line(); } } + +void emit_jump_table(const ir_node *node, long default_pn, ir_entity *entity, + get_cfop_target_func get_cfop_target) +{ + long switch_max = LONG_MIN; + ir_node *default_block = NULL; + unsigned long length; + const ir_edge_t *edge; + unsigned i; + ir_node **table; + + /* go over all proj's and collect them */ + foreach_out_edge(node, edge) { + ir_node *proj = get_edge_src_irn(edge); + long pn = get_Proj_proj(proj); + + /* check for default proj */ + if (pn == default_pn) { + assert(default_block == NULL); /* more than 1 default_pn? */ + default_block = get_cfop_target(proj); + } else { + switch_max = pn > switch_max ? pn : switch_max; + } + } + assert(switch_max > LONG_MIN); + + length = (unsigned long) switch_max + 1; + /* the 16000 isn't a real limit of the architecture. But should protect us + * from seamingly endless compiler runs */ + if (length > 16000) { + /* switch lowerer should have broken this monster to pieces... */ + panic("too large switch encountered"); + } + + table = XMALLOCNZ(ir_node*, length); + foreach_out_edge(node, edge) { + ir_node *proj = get_edge_src_irn(edge); + long pn = get_Proj_proj(proj); + if (pn == default_pn) + continue; + + table[pn] = get_cfop_target(proj); + } + + /* emit table */ + be_gas_emit_switch_section(GAS_SECTION_RODATA); + be_emit_cstring("\t.align 4\n"); + be_gas_emit_entity(entity); + be_emit_cstring(":\n"); + for (i = 0; i < length; ++i) { + ir_node *block = table[i]; + if (block == NULL) + block = default_block; + be_emit_cstring("\t.long "); + be_gas_emit_block_name(block); + be_emit_char('\n'); + be_emit_write_line(); + } + be_gas_emit_switch_section(GAS_SECTION_TEXT); + + xfree(table); +} diff --git a/ir/be/begnuas.h b/ir/be/begnuas.h index ba171852f..b2192d67f 100644 --- a/ir/be/begnuas.h +++ b/ir/be/begnuas.h @@ -107,4 +107,12 @@ void be_gas_emit_block_name(const ir_node *block); */ const char *be_gas_insn_label_prefix(void); +typedef ir_node* (*get_cfop_target_func)(const ir_node *cfop); + +/** + * Emits a jump table for switch operations + */ +void emit_jump_table(const ir_node *node, long default_pn, ir_entity *table, + get_cfop_target_func get_cfop_target); + #endif diff --git a/ir/be/ia32/bearch_ia32.c b/ir/be/ia32/bearch_ia32.c index 036f775fa..9e90b505f 100644 --- a/ir/be/ia32/bearch_ia32.c +++ b/ir/be/ia32/bearch_ia32.c @@ -2046,7 +2046,7 @@ static void ia32_lower_for_target(void) /* lower for mode_b stuff */ ir_lower_mode_b(irg, &lower_mode_b_config); /* break up switches with wide ranges */ - lower_switch(irg, 4, 256, true); + lower_switch(irg, 4, 256, false); } } diff --git a/ir/be/ia32/ia32_emitter.c b/ir/be/ia32/ia32_emitter.c index 68407b6f0..efa87db67 100644 --- a/ir/be/ia32/ia32_emitter.c +++ b/ir/be/ia32/ia32_emitter.c @@ -1134,127 +1134,17 @@ static void emit_ia32_CMovcc(const ir_node *node) ia32_emitf(node, "\tcmov%P %#AR, %#R\n", cc, in_true, out); } - -/* jump table entry (target and corresponding number) */ -typedef struct branch_t { - ir_node *target; - int value; -} branch_t; - -/* jump table for switch generation */ -typedef struct jmp_tbl_t { - ir_node *defProj; /**< default target */ - long min_value; /**< smallest switch case */ - long max_value; /**< largest switch case */ - long num_branches; /**< number of jumps */ - char label[SNPRINTF_BUF_LEN]; /**< label of the jump table */ - branch_t *branches; /**< jump array */ -} jmp_tbl_t; - /** - * Compare two variables of type branch_t. Used to sort all switch cases - */ -static int ia32_cmp_branch_t(const void *a, const void *b) -{ - branch_t *b1 = (branch_t *)a; - branch_t *b2 = (branch_t *)b; - - if (b1->value <= b2->value) - return -1; - else - return 1; -} - -static void generate_jump_table(jmp_tbl_t *tbl, const ir_node *node) -{ - int i; - long default_pn = get_ia32_default_pn(node); - ir_node *proj; - const ir_edge_t *edge; - - /* fill the table structure */ - get_unique_label(tbl->label, SNPRINTF_BUF_LEN, "TBL_"); - tbl->defProj = NULL; - tbl->num_branches = get_irn_n_edges(node) - 1; - tbl->branches = XMALLOCNZ(branch_t, tbl->num_branches); - tbl->min_value = LONG_MAX; - tbl->max_value = LONG_MIN; - - i = 0; - /* go over all proj's and collect them */ - foreach_out_edge(node, edge) { - long pn; - proj = get_edge_src_irn(edge); - assert(is_Proj(proj) && "Only proj allowed at SwitchJmp"); - - pn = get_Proj_proj(proj); - - /* check for default proj */ - if (pn == default_pn) { - assert(tbl->defProj == NULL && "found two default Projs at SwitchJmp"); - tbl->defProj = proj; - } else { - tbl->min_value = pn < tbl->min_value ? pn : tbl->min_value; - tbl->max_value = pn > tbl->max_value ? pn : tbl->max_value; - - /* create branch entry */ - tbl->branches[i].target = proj; - tbl->branches[i].value = pn; - ++i; - } - - } - assert(i == tbl->num_branches); - - /* sort the branches by their number */ - qsort(tbl->branches, tbl->num_branches, sizeof(tbl->branches[0]), ia32_cmp_branch_t); -} - -/** - * Emits code for a SwitchJmp (creates a jump table if - * possible otherwise a cmp-jmp cascade). Port from - * cggg ia32 backend + * Emits code for a SwitchJmp */ static void emit_ia32_SwitchJmp(const ir_node *node) { - unsigned long interval; - int last_value, i; - jmp_tbl_t tbl; - - /* fill the table structure */ - generate_jump_table(&tbl, node); - - /* two-complement's magic make this work without overflow */ - interval = tbl.max_value - tbl.min_value; - - /* emit the table */ - ia32_emitf(node, "\tcmpl $%u, %S0\n", interval); - ia32_emitf(tbl.defProj, "\tja %L\n"); - - if (tbl.num_branches > 1) { - /* create table */ - ia32_emitf(node, "\tjmp *%s(,%S0,4)\n", tbl.label); + ir_entity *jump_table = get_ia32_am_sc(node); + long default_pn = get_ia32_default_pn(node); - be_gas_emit_switch_section(GAS_SECTION_RODATA); - ia32_emitf(NULL, "\t.align 4\n"); - ia32_emitf(NULL, "%s:\n", tbl.label); - - last_value = tbl.branches[0].value; - for (i = 0; i != tbl.num_branches; ++i) { - while (last_value != tbl.branches[i].value) { - ia32_emitf(tbl.defProj, ".long %L\n"); - ++last_value; - } - ia32_emitf(tbl.branches[i].target, ".long %L\n"); - ++last_value; - } - be_gas_emit_switch_section(GAS_SECTION_TEXT); - } else { - /* one jump is enough */ - ia32_emitf(tbl.branches[0].target, "\tjmp %L\n"); - } + ia32_emitf(node, "\tjmp *%AM\n", node); - free(tbl.branches); + emit_jump_table(node, default_pn, jump_table, get_cfop_target_block); } /** @@ -3415,63 +3305,13 @@ static void bemit_ia32_jcc(const ir_node *node) static void bemit_switchjmp(const ir_node *node) { - unsigned long interval; - int last_value; - int i; - jmp_tbl_t tbl; - const arch_register_t *in; + ir_entity *jump_table = get_ia32_am_sc(node); + long default_pn = get_ia32_default_pn(node); - /* fill the table structure */ - generate_jump_table(&tbl, node); - - /* two-complement's magic make this work without overflow */ - interval = tbl.max_value - tbl.min_value; - - in = get_in_reg(node, 0); - /* emit the table */ - if (get_signed_imm_size(interval) == 1) { - bemit8(0x83); // cmpl $imm8, %in - bemit_modru(in, 7); - bemit8(interval); - } else { - bemit8(0x81); // cmpl $imm32, %in - bemit_modru(in, 7); - bemit32(interval); - } - bemit8(0x0F); // ja tbl.defProj - bemit8(0x87); - ia32_emitf(tbl.defProj, ".long %L - . - 4\n"); - - if (tbl.num_branches > 1) { - /* create table */ - bemit8(0xFF); // jmp *tbl.label(,%in,4) - bemit8(MOD_IND | ENC_REG(4) | ENC_RM(0x04)); - bemit8(ENC_SIB(2, reg_gp_map[in->index], 0x05)); - be_emit_irprintf("\t.long %s\n", tbl.label); - - be_gas_emit_switch_section(GAS_SECTION_RODATA); - be_emit_cstring(".align 4\n"); - be_emit_irprintf("%s:\n", tbl.label); - - last_value = tbl.branches[0].value; - for (i = 0; i != tbl.num_branches; ++i) { - while (last_value != tbl.branches[i].value) { - ia32_emitf(tbl.defProj, ".long %L\n"); - ++last_value; - } - ia32_emitf(tbl.branches[i].target, ".long %L\n"); - ++last_value; - } - be_gas_emit_switch_section(GAS_SECTION_TEXT); - } else { - /* one jump is enough */ - panic("switch only has one case"); - //ia32_emitf(tbl.branches[0].target, "\tjmp %L\n"); - } - - be_emit_write_line(); + bemit8(0xFF); // jmp *tbl.label(,%in,4) + bemit_mod_am(0x05, node); - free(tbl.branches); + emit_jump_table(node, default_pn, jump_table, get_cfop_target_block); } /** diff --git a/ir/be/ia32/ia32_nodes_attr.h b/ir/be/ia32/ia32_nodes_attr.h index 5e232c5b5..4b4eb6614 100644 --- a/ir/be/ia32/ia32_nodes_attr.h +++ b/ir/be/ia32/ia32_nodes_attr.h @@ -237,6 +237,7 @@ typedef struct ia32_switch_attr_t ia32_switch_attr_t; struct ia32_switch_attr_t { ia32_attr_t attr; /**< generic attribute */ long default_pn; + ir_entity *jump_table; }; /** diff --git a/ir/be/ia32/ia32_spec.pl b/ir/be/ia32/ia32_spec.pl index ae6b2b98f..d67f80f0f 100644 --- a/ir/be/ia32/ia32_spec.pl +++ b/ir/be/ia32/ia32_spec.pl @@ -1042,13 +1042,13 @@ Jcc => { SwitchJmp => { state => "pinned", op_flags => [ "labeled", "cfopcode", "forking" ], - reg_req => { in => [ "gp" ] }, + reg_req => { in => [ "gp", "gp" ] }, + ins => [ "base", "index" ], mode => "mode_T", attr_type => "ia32_switch_attr_t", attr => "long default_pn", - latency => 3, + latency => 2, units => [ "BRANCH" ], - modified_flags => $status_flags, init_attr => "info->out_infos = NULL;", # XXX ugly hack for out requirements }, diff --git a/ir/be/ia32/ia32_transform.c b/ir/be/ia32/ia32_transform.c index ca8b7ea07..803a4d3e9 100644 --- a/ir/be/ia32/ia32_transform.c +++ b/ir/be/ia32/ia32_transform.c @@ -2815,45 +2815,28 @@ static ir_node *gen_Store(ir_node *node) */ static ir_node *create_Switch(ir_node *node) { - dbg_info *dbgi = get_irn_dbg_info(node); - ir_node *block = be_transform_node(get_nodes_block(node)); - ir_node *sel = get_Cond_selector(node); - ir_node *new_sel = be_transform_node(sel); - long switch_min = LONG_MAX; - long switch_max = LONG_MIN; - long default_pn = get_Cond_default_proj(node); - ir_node *new_node; - const ir_edge_t *edge; + dbg_info *dbgi = get_irn_dbg_info(node); + ir_node *block = be_transform_node(get_nodes_block(node)); + ir_node *sel = get_Cond_selector(node); + ir_node *new_sel = be_transform_node(sel); + long default_pn = get_Cond_default_proj(node); + ir_node *new_node; + ir_entity *entity; assert(get_mode_size_bits(get_irn_mode(sel)) == 32); - /* determine the smallest switch case value */ - foreach_out_edge(node, edge) { - ir_node *proj = get_edge_src_irn(edge); - long pn = get_Proj_proj(proj); - if (pn == default_pn) - continue; + entity = new_entity(NULL, id_unique("TBL%u"), get_unknown_type()); + set_entity_visibility(entity, ir_visibility_private); + add_entity_linkage(entity, IR_LINKAGE_CONSTANT); - if (pn < switch_min) - switch_min = pn; - if (pn > switch_max) - switch_max = pn; - } - - if ((unsigned long) (switch_max - switch_min) > 128000) { - panic("Size of switch %+F bigger than 128000", node); - } - - if (switch_min != 0) { - /* if smallest switch case is not 0 we need an additional sub */ - new_sel = new_bd_ia32_Lea(dbgi, block, new_sel, noreg_GP); - add_ia32_am_offs_int(new_sel, -switch_min); - set_ia32_op_type(new_sel, ia32_AddrModeS); - - SET_IA32_ORIG_NODE(new_sel, node); - } - - new_node = new_bd_ia32_SwitchJmp(dbgi, block, new_sel, default_pn); + /* TODO: we could perform some more matching here to also use the base + * register of the address mode */ + new_node + = new_bd_ia32_SwitchJmp(dbgi, block, noreg_GP, new_sel, default_pn); + set_ia32_am_scale(new_node, 2); + set_ia32_am_sc(new_node, entity); + set_ia32_op_type(new_node, ia32_AddrModeS); + set_ia32_ls_mode(new_node, mode_Iu); SET_IA32_ORIG_NODE(new_node, node); return new_node; diff --git a/ir/be/sparc/sparc_emitter.c b/ir/be/sparc/sparc_emitter.c index d9734a69f..499ea5aff 100644 --- a/ir/be/sparc/sparc_emitter.c +++ b/ir/be/sparc/sparc_emitter.c @@ -845,78 +845,17 @@ static void emit_sparc_Ba(const ir_node *node) be_emit_finish_line_gas(node); } -static void emit_jump_table(const ir_node *node) +static void emit_sparc_SwitchJmp(const ir_node *node) { const sparc_switch_jmp_attr_t *attr = get_sparc_switch_jmp_attr_const(node); - long switch_max = LONG_MIN; - long default_pn = attr->default_proj_num; - ir_entity *entity = attr->jump_table; - ir_node *default_block = NULL; - unsigned long length; - const ir_edge_t *edge; - unsigned i; - ir_node **table; - - /* go over all proj's and collect them */ - foreach_out_edge(node, edge) { - ir_node *proj = get_edge_src_irn(edge); - long pn = get_Proj_proj(proj); - - /* check for default proj */ - if (pn == default_pn) { - assert(default_block == NULL); /* more than 1 default_pn? */ - default_block = get_jump_target(proj); - } else { - switch_max = pn > switch_max ? pn : switch_max; - } - } - assert(switch_max > LONG_MIN); - - length = (unsigned long) switch_max + 1; - /* the 16000 isn't a real limit of the architecture. But should protect us - * from seamingly endless compiler runs */ - if (length > 16000) { - /* switch lowerer should have broken this monster to pieces... */ - panic("too large switch encountered"); - } - - table = XMALLOCNZ(ir_node*, length); - foreach_out_edge(node, edge) { - ir_node *proj = get_edge_src_irn(edge); - long pn = get_Proj_proj(proj); - if (pn == default_pn) - continue; - - table[pn] = get_jump_target(proj); - } - - /* emit table */ - be_gas_emit_switch_section(GAS_SECTION_RODATA); - be_emit_cstring("\t.align 4\n"); - be_gas_emit_entity(entity); - be_emit_cstring(":\n"); - for (i = 0; i < length; ++i) { - ir_node *block = table[i]; - if (block == NULL) - block = default_block; - be_emit_cstring("\t.long "); - be_gas_emit_block_name(block); - be_emit_char('\n'); - be_emit_write_line(); - } - be_gas_emit_switch_section(GAS_SECTION_TEXT); - - xfree(table); -} -static void emit_sparc_SwitchJmp(const ir_node *node) -{ be_emit_cstring("\tjmp "); sparc_emit_source_register(node, 0); be_emit_finish_line_gas(node); fill_delay_slot(); - emit_jump_table(node); + emit_jump_table(node, attr->default_proj_num, attr->jump_table, + get_jump_target); } static void emit_fmov(const ir_node *node, const arch_register_t *src_reg, diff --git a/ir/be/sparc/sparc_transform.c b/ir/be/sparc/sparc_transform.c index 16fa11e8e..8fa46689f 100644 --- a/ir/be/sparc/sparc_transform.c +++ b/ir/be/sparc/sparc_transform.c @@ -1005,11 +1005,6 @@ static ir_node *gen_SwitchJmp(ir_node *node) set_entity_visibility(entity, ir_visibility_private); add_entity_linkage(entity, IR_LINKAGE_CONSTANT); - /* TODO: this code does not construct code to check for access - * out-of bounds of the jumptable yet. I think we should put this stuff - * into the switch_lowering phase to get some additional optimisations - * done. */ - /* construct base address */ table_address = make_address(dbgi, block, entity, 0); /* scale index */