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);
+}
*/
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
/* 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);
}
}
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);
}
/**
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);
}
/**
struct ia32_switch_attr_t {
ia32_attr_t attr; /**< generic attribute */
long default_pn;
+ ir_entity *jump_table;
};
/**
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
},
*/
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;
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,
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 */