Commit b46a6da6 authored by Matthias Braun's avatar Matthias Braun
Browse files

unify sparc/ia32 SwitchJmp handling

parent 978df701
......@@ -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);
}
......@@ -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
......@@ -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);
}
}
......
......@@ -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);
}
/**
......
......@@ -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;
};
/**
......
......@@ -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
},
......
......@@ -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;
......
......@@ -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,
......
......@@ -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 */
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment