Commit 1c89dc2a authored by Matthias Braun's avatar Matthias Braun
Browse files

introduce Switch node

This is the new way of handling switch-jumps. The node contains a table
which maps (ranges of) input values to proj numbers. Compared to a
Cond-node this results in a clean consecutive sequence of Proj numbers
(no searching for a free number for the default_pn anymore) and allows
factoring multiple cases jumping to the same block in a single Proj
(though we still need the optimisation in cfopt for that).
parent 2198e371
...@@ -64,6 +64,13 @@ typedef union ir_initializer_t ir_initializer_t, *ir_initializer_ptr; ...@@ -64,6 +64,13 @@ typedef union ir_initializer_t ir_initializer_t, *ir_initializer_ptr;
typedef void irg_walk_func(ir_node *, void *); typedef void irg_walk_func(ir_node *, void *);
typedef void irg_reg_walk_func(ir_region *, void *); typedef void irg_reg_walk_func(ir_region *, void *);
/**
* A switch table mapping integer numbers to proj-numbers of a Switch-node.
* Entries map a continuous range of integer numbers to a proj-number.
* There must never be two different entries matching the same integer number.
*/
typedef struct ir_switch_table ir_switch_table;
/* Needed for MSVC to suppress warnings because it doest NOT handle const right. */ /* Needed for MSVC to suppress warnings because it doest NOT handle const right. */
typedef const ir_node *ir_node_cnst_ptr; typedef const ir_node *ir_node_cnst_ptr;
......
...@@ -727,6 +727,23 @@ FIRM_API unsigned firm_default_hash(const ir_node *node); ...@@ -727,6 +727,23 @@ FIRM_API unsigned firm_default_hash(const ir_node *node);
*/ */
FIRM_API const char *gdb_node_helper(void *firm_object); FIRM_API const char *gdb_node_helper(void *firm_object);
FIRM_API ir_switch_table *ir_new_switch_table(ir_graph *irg, size_t n_entries);
FIRM_API size_t ir_switch_table_get_n_entries(const ir_switch_table *table);
FIRM_API void ir_switch_table_set(ir_switch_table *table, size_t entry,
ir_tarval *min, ir_tarval *max, long pn);
FIRM_API ir_tarval *ir_switch_table_get_max(const ir_switch_table *table,
size_t entry);
FIRM_API ir_tarval *ir_switch_table_get_min(const ir_switch_table *table,
size_t entry);
FIRM_API long ir_switch_table_get_pn(const ir_switch_table *table, size_t entry);
FIRM_API ir_switch_table *ir_switch_table_duplicate(ir_graph *irg, const ir_switch_table *table);
/*@}*/ /*@}*/
#include "end.h" #include "end.h"
......
...@@ -41,6 +41,7 @@ ...@@ -41,6 +41,7 @@
#include "irtools.h" #include "irtools.h"
#include "array_t.h" #include "array_t.h"
#include "debug.h" #include "debug.h"
#include "error.h"
#include "irflag.h" #include "irflag.h"
/** /**
...@@ -75,32 +76,53 @@ static ir_node *get_effective_use_block(ir_node *node, int pos) ...@@ -75,32 +76,53 @@ static ir_node *get_effective_use_block(ir_node *node, int pos)
return get_nodes_block(node); return get_nodes_block(node);
} }
static ir_node *get_case_value(ir_node *switchn, long pn)
{
ir_graph *irg = get_irn_irg(switchn);
const ir_switch_table *table = get_Switch_table(switchn);
size_t n_entries = ir_switch_table_get_n_entries(table);
ir_tarval *val = NULL;
size_t e;
for (e = 0; e < n_entries; ++e) {
const ir_switch_table_entry *entry
= ir_switch_table_get_entry_const(table, e);
if (entry->pn != pn)
continue;
/* multiple matching entries gets too complicated for a single
* Confirm */
if (val != NULL)
return NULL;
/* case ranges are too complicated too */
if (entry->min != entry->max)
return NULL;
val = entry->min;
}
assert(val != NULL);
return new_r_Const(irg, val);
}
/** /**
* Handle a CASE-branch. * Handle a CASE-branch.
* *
* @param block the block which is entered by the branch
* @param irn the node expressing the switch value
* @param nr the branch label
* @param env statistical environment
*
* Branch labels are a simple case. We can replace the value * Branch labels are a simple case. We can replace the value
* by a Const with the branch label. * by a Const with the branch label.
*/ */
static void handle_case(ir_node *block, ir_node *irn, long nr, env_t *env) static void handle_case(ir_node *block, ir_node *switchn, long pn, env_t *env)
{ {
const ir_edge_t *edge, *next; ir_node *c = NULL;
ir_node *c = NULL; ir_node *selector = get_Switch_selector(switchn);
const ir_edge_t *edge;
const ir_edge_t *next;
if (is_Bad(irn)) /* we can't do usefull things with the default label */
if (pn == pn_Switch_default)
return; return;
for (edge = get_irn_out_edge_first(irn); edge; edge = next) { foreach_out_edge_safe(selector, edge, next) {
ir_node *succ = get_edge_src_irn(edge); ir_node *succ = get_edge_src_irn(edge);
int pos = get_edge_src_pos(edge); int pos = get_edge_src_pos(edge);
ir_node *blk = get_effective_use_block(succ, pos); ir_node *blk = get_effective_use_block(succ, pos);
next = get_irn_out_edge_next(irn, edge);
if (block_dominates(block, blk)) { if (block_dominates(block, blk)) {
/* /*
* Ok, we found a user of irn that is placed * Ok, we found a user of irn that is placed
...@@ -108,15 +130,11 @@ static void handle_case(ir_node *block, ir_node *irn, long nr, env_t *env) ...@@ -108,15 +130,11 @@ static void handle_case(ir_node *block, ir_node *irn, long nr, env_t *env)
* We can replace the input with the Constant * We can replace the input with the Constant
* branch label. * branch label.
*/ */
if (c == NULL)
if (! c) { c = get_case_value(switchn, pn);
ir_mode *mode = get_irn_mode(irn);
ir_tarval *tv = new_tarval_from_long(nr, mode);
c = new_r_Const(current_ir_graph, tv);
}
set_irn_n(succ, pos, c); set_irn_n(succ, pos, c);
DBG_OPT_CONFIRM_C(irn, c); DBG_OPT_CONFIRM_C(selector, c);
DB((dbg, LEVEL_2, "Replacing input %d of node %+F with %+F\n", pos, succ, c)); DB((dbg, LEVEL_2, "Replacing input %d of node %+F with %+F\n", pos, succ, c));
env->num_consts += 1; env->num_consts += 1;
...@@ -405,9 +423,9 @@ static void handle_if(ir_node *block, ir_node *cmp, ir_relation rel, env_t *env) ...@@ -405,9 +423,9 @@ static void handle_if(ir_node *block, ir_node *cmp, ir_relation rel, env_t *env)
*/ */
static void insert_Confirm_in_block(ir_node *block, void *data) static void insert_Confirm_in_block(ir_node *block, void *data)
{ {
ir_node *cond, *proj, *selector;
ir_mode *mode;
env_t *env = (env_t*) data; env_t *env = (env_t*) data;
ir_node *cond;
ir_node *proj;
/* /*
* we can only handle blocks with only ONE control flow * we can only handle blocks with only ONE control flow
...@@ -421,13 +439,11 @@ static void insert_Confirm_in_block(ir_node *block, void *data) ...@@ -421,13 +439,11 @@ static void insert_Confirm_in_block(ir_node *block, void *data)
return; return;
cond = get_Proj_pred(proj); cond = get_Proj_pred(proj);
if (! is_Cond(cond)) if (is_Switch(cond)) {
return; long proj_nr = get_Proj_proj(proj);
handle_case(block, cond, proj_nr, env);
selector = get_Cond_selector(cond); } else if (is_Const(cond)) {
mode = get_irn_mode(selector); ir_node *selector = get_Cond_selector(cond);
if (mode == mode_b) {
ir_relation rel; ir_relation rel;
handle_modeb(block, selector, (pn_Cond) get_Proj_proj(proj), env); handle_modeb(block, selector, (pn_Cond) get_Proj_proj(proj), env);
...@@ -439,20 +455,11 @@ static void insert_Confirm_in_block(ir_node *block, void *data) ...@@ -439,20 +455,11 @@ static void insert_Confirm_in_block(ir_node *block, void *data)
if (get_Proj_proj(proj) != pn_Cond_true) { if (get_Proj_proj(proj) != pn_Cond_true) {
/* it's the false branch */ /* it's the false branch */
mode = get_irn_mode(get_Cmp_left(selector));
rel = get_negated_relation(rel); rel = get_negated_relation(rel);
} }
DB((dbg, LEVEL_2, "At %+F using %+F Confirm %=\n", block, selector, rel)); DB((dbg, LEVEL_2, "At %+F using %+F Confirm %=\n", block, selector, rel));
handle_if(block, selector, rel, env); handle_if(block, selector, rel, env);
} else if (mode_is_int(mode)) {
long proj_nr = get_Proj_proj(proj);
/* this is a CASE, but we cannot handle the default case */
if (proj_nr == get_Cond_default_proj(cond))
return;
handle_case(block, get_Cond_selector(cond), proj_nr, env);
} }
} }
......
...@@ -579,80 +579,13 @@ static void emit_arm_CopyB(const ir_node *irn) ...@@ -579,80 +579,13 @@ static void emit_arm_CopyB(const ir_node *irn)
static void emit_arm_SwitchJmp(const ir_node *irn) static void emit_arm_SwitchJmp(const ir_node *irn)
{ {
const ir_edge_t *edge; const arm_SwitchJmp_attr_t *attr = get_arm_SwitchJmp_attr_const(irn);
ir_node *proj; be_emit_cstring("\tldrls pc, [pc, ");
int i;
ir_node **projs;
int n_projs;
int block_nr;
ir_node *default_proj = NULL;
block_nr = get_irn_node_nr(irn);
n_projs = get_arm_SwitchJmp_n_projs(irn);
projs = XMALLOCNZ(ir_node*, n_projs);
foreach_out_edge(irn, edge) {
proj = get_edge_src_irn(edge);
assert(is_Proj(proj) && "Only proj allowed at SwitchJmp");
if (get_Proj_proj(proj) == get_arm_SwitchJmp_default_proj_num(irn))
default_proj = proj;
projs[get_Proj_proj(proj)] = proj;
}
assert(default_proj != NULL && "SwitchJmp should have a Default Proj");
/*
CMP %1S, n_projs - 1
BHI default
*/
be_emit_cstring("\tcmp ");
arm_emit_source_register(irn, 0); arm_emit_source_register(irn, 0);
be_emit_irprintf(", #%u", n_projs - 1); be_emit_cstring(", asl #2]");
be_emit_finish_line_gas(irn); be_emit_finish_line_gas(irn);
be_emit_cstring("\tbhi "); be_emit_jump_table(irn, attr->table, NULL, get_cfop_target_block);
arm_emit_cfop_target(default_proj);
be_emit_finish_line_gas(default_proj);
/*
LDR %r12, .TABLE_X_START
ADD %r12, %r12, [%1S, LSL #2]
LDR %r15, %r12
*/
be_emit_irprintf("\tldr %%r12, TABLE_%d_START", block_nr);
be_emit_finish_line_gas(NULL);
be_emit_irprintf("\tadd %%r12, %%r12, ");
arm_emit_source_register(irn, 0);
be_emit_cstring(", LSL #2");
be_emit_finish_line_gas(NULL);
be_emit_cstring("\tldr %r15, [%r12, #0]");
be_emit_finish_line_gas(NULL);
be_emit_irprintf("TABLE_%d_START:\n\t.word\tTABLE_%d", block_nr, block_nr);
be_emit_finish_line_gas(NULL);
be_emit_irprintf("\t.align 2");
be_emit_finish_line_gas(NULL);
be_emit_irprintf("TABLE_%d:", block_nr);
be_emit_finish_line_gas(NULL);
for (i = 0; i < n_projs; ++i) {
proj = projs[i];
if (proj == NULL) {
proj = projs[get_arm_SwitchJmp_default_proj_num(irn)];
}
be_emit_cstring("\t.word\t");
arm_emit_cfop_target(proj);
be_emit_finish_line_gas(proj);
}
be_emit_irprintf("\t.align 2\n");
be_emit_finish_line_gas(NULL);
xfree(projs);
} }
/** Emit an IncSP node */ /** Emit an IncSP node */
......
...@@ -324,30 +324,6 @@ void set_arm_CondJmp_relation(ir_node *node, ir_relation relation) ...@@ -324,30 +324,6 @@ void set_arm_CondJmp_relation(ir_node *node, ir_relation relation)
attr->relation = relation; attr->relation = relation;
} }
int get_arm_SwitchJmp_n_projs(const ir_node *node)
{
const arm_SwitchJmp_attr_t *attr = get_arm_SwitchJmp_attr_const(node);
return attr->n_projs;
}
void set_arm_SwitchJmp_n_projs(ir_node *node, int n_projs)
{
arm_SwitchJmp_attr_t *attr = get_arm_SwitchJmp_attr(node);
attr->n_projs = n_projs;
}
long get_arm_SwitchJmp_default_proj_num(const ir_node *node)
{
const arm_SwitchJmp_attr_t *attr = get_arm_SwitchJmp_attr_const(node);
return attr->default_proj_num;
}
void set_arm_SwitchJmp_default_proj_num(ir_node *node, long default_proj_num)
{
arm_SwitchJmp_attr_t *attr = get_arm_SwitchJmp_attr(node);
attr->default_proj_num = default_proj_num;
}
/* Set the ARM machine node attributes to default values. */ /* Set the ARM machine node attributes to default values. */
static void init_arm_attributes(ir_node *node, arch_irn_flags_t flags, static void init_arm_attributes(ir_node *node, arch_irn_flags_t flags,
const arch_register_req_t ** in_reqs, const arch_register_req_t ** in_reqs,
...@@ -420,6 +396,20 @@ static void init_arm_CopyB_attributes(ir_node *res, unsigned size) ...@@ -420,6 +396,20 @@ static void init_arm_CopyB_attributes(ir_node *res, unsigned size)
attr->size = size; attr->size = size;
} }
static void init_arm_SwitchJmp_attributes(ir_node *res,
const ir_switch_table *table)
{
unsigned n_outs = arch_get_irn_n_outs(res);
unsigned o;
arm_SwitchJmp_attr_t *attr = get_arm_SwitchJmp_attr(res);
attr->table = table;
for (o = 0; o < n_outs; ++o) {
arch_set_irn_register_req_out(res, o, arch_no_register_req);
}
}
static int cmp_attr_arm(const ir_node *a, const ir_node *b) static int cmp_attr_arm(const ir_node *a, const ir_node *b)
{ {
(void) a; (void) a;
......
...@@ -91,26 +91,6 @@ void set_arm_CondJmp_relation(ir_node *node, ir_relation relation); ...@@ -91,26 +91,6 @@ void set_arm_CondJmp_relation(ir_node *node, ir_relation relation);
ir_node *new_r_arm_StoreStackMInc(ir_graph *irg, ir_node *block, ir_node *mem, ir_node *sp, ir_node *new_r_arm_StoreStackMInc(ir_graph *irg, ir_node *block, ir_node *mem, ir_node *sp,
int n_regs, ir_node **regs, ir_mode *mode); int n_regs, ir_node **regs, ir_mode *mode);
/**
* Returns the number of projs of a SwitchJmp.
*/
int get_arm_SwitchJmp_n_projs(const ir_node *node);
/**
* Sets the number of projs of a SwitchJmp.
*/
void set_arm_SwitchJmp_n_projs(ir_node *node, int n_projs);
/**
* Returns the default_proj_num.
*/
long get_arm_SwitchJmp_default_proj_num(const ir_node *node);
/**
* Sets the default_proj_num.
*/
void set_arm_SwitchJmp_default_proj_num(ir_node *node, long default_proj_num);
/* Include the generated headers */ /* Include the generated headers */
#include "gen_arm_new_nodes.h" #include "gen_arm_new_nodes.h"
......
...@@ -112,9 +112,8 @@ typedef struct arm_CondJmp_attr_t { ...@@ -112,9 +112,8 @@ typedef struct arm_CondJmp_attr_t {
/** Attributes for a SwitchJmp */ /** Attributes for a SwitchJmp */
typedef struct arm_SwitchJmp_attr_t { typedef struct arm_SwitchJmp_attr_t {
arm_attr_t base; arm_attr_t base;
int n_projs; const ir_switch_table *table;
long default_proj_num;
} arm_SwitchJmp_attr_t; } arm_SwitchJmp_attr_t;
/** CopyB attributes */ /** CopyB attributes */
......
...@@ -415,11 +415,10 @@ SwitchJmp => { ...@@ -415,11 +415,10 @@ SwitchJmp => {
op_flags => [ "labeled", "cfopcode", "forking" ], op_flags => [ "labeled", "cfopcode", "forking" ],
state => "pinned", state => "pinned",
mode => "mode_T", mode => "mode_T",
attr => "int n_projs, long def_proj_num", attr => "const ir_switch_table *table",
init_attr => "\tset_arm_SwitchJmp_n_projs(res, n_projs);\n". init_attr => "init_arm_SwitchJmp_attributes(res, table);",
"\tset_arm_SwitchJmp_default_proj_num(res, def_proj_num);\n".
"\tinfo->out_infos = NULL;",
reg_req => { in => [ "gp" ], out => [ "none" ] }, reg_req => { in => [ "gp" ], out => [ "none" ] },
out_arity => "variable",
attr_type => "arm_SwitchJmp_attr_t", attr_type => "arm_SwitchJmp_attr_t",
}, },
......
...@@ -1013,46 +1013,23 @@ static ir_node *gen_Jmp(ir_node *node) ...@@ -1013,46 +1013,23 @@ static ir_node *gen_Jmp(ir_node *node)
return new_bd_arm_Jmp(dbgi, new_block); return new_bd_arm_Jmp(dbgi, new_block);
} }
static ir_node *gen_SwitchJmp(ir_node *node) static ir_node *gen_Switch(ir_node *node)
{ {
ir_node *block = be_transform_node(get_nodes_block(node)); ir_graph *irg = get_irn_irg(node);
ir_node *selector = get_Cond_selector(node); ir_node *block = be_transform_node(get_nodes_block(node));
dbg_info *dbgi = get_irn_dbg_info(node); ir_node *selector = get_Switch_selector(node);
ir_node *new_op = be_transform_node(selector); dbg_info *dbgi = get_irn_dbg_info(node);
ir_node *const_graph; ir_node *new_op = be_transform_node(selector);
ir_node *sub; ir_mode *mode = get_irn_mode(selector);
const ir_switch_table *table = get_Switch_table(node);
ir_node *proj; unsigned n_outs = get_Switch_n_outs(node);
const ir_edge_t *edge;
int min = INT_MAX;
int max = INT_MIN;
int translation;
int pn;
int n_projs;
foreach_out_edge(node, edge) { table = ir_switch_table_duplicate(irg, table);
proj = get_edge_src_irn(edge);
assert(is_Proj(proj) && "Only proj allowed at SwitchJmp");
pn = get_Proj_proj(proj); /* switch with smaller modes not implemented yet */
assert(get_mode_size_bits(mode) == 32);
min = pn<min ? pn : min;
max = pn>max ? pn : max;
}
translation = min;
n_projs = max - translation + 1;
foreach_out_edge(node, edge) { return new_bd_arm_SwitchJmp(dbgi, block, new_op, n_outs, table);
proj = get_edge_src_irn(edge);
assert(is_Proj(proj) && "Only proj allowed at SwitchJmp");
pn = get_Proj_proj(proj) - translation;
set_Proj_proj(proj, pn);
}
const_graph = create_const_graph_value(dbgi, block, translation);
sub = new_bd_arm_Sub_reg(dbgi, block, new_op, const_graph);
return new_bd_arm_SwitchJmp(dbgi, block, sub, n_projs, get_Cond_default_proj(node) - translation);
} }
static ir_node *gen_Cmp(ir_node *node) static ir_node *gen_Cmp(ir_node *node)
...@@ -1089,15 +1066,11 @@ static ir_node *gen_Cmp(ir_node *node) ...@@ -1089,15 +1066,11 @@ static ir_node *gen_Cmp(ir_node *node)
static ir_node *gen_Cond(ir_node *node) static ir_node *gen_Cond(ir_node *node)
{ {
ir_node *selector = get_Cond_selector(node); ir_node *selector = get_Cond_selector(node);
ir_mode *mode = get_irn_mode(selector);
ir_relation relation; ir_relation relation;
ir_node *block; ir_node *block;
ir_node *flag_node; ir_node *flag_node;
dbg_info *dbgi; dbg_info *dbgi;
if (mode != mode_b) {
return gen_SwitchJmp(node);
}
assert(is_Cmp(selector)); assert(is_Cmp(selector));
block = be_transform_node(get_nodes_block(node)); block = be_transform_node(get_nodes_block(node));
...@@ -1612,6 +1585,7 @@ static ir_node *gen_Proj(ir_node *node) ...@@ -1612,6 +1585,7 @@ static ir_node *gen_Proj(ir_node *node)
case iro_Start: case iro_Start:
return gen_Proj_Start(node); return gen_Proj_Start(node);
case iro_Cond: case iro_Cond:
case iro_Switch:
/* nothing to do */ /* nothing to do */
return be_duplicate_node(node); return be_duplicate_node(node);
case iro_Proj: { case iro_Proj: {
...@@ -2102,6 +2076,7 @@ static void arm_register_transformers(void) ...@@ -2102,6 +2076,7 @@ static void arm_register_transformers(void)
be_set_transform_function(op_Start, gen_Start); be_set_transform_function(op_Start, gen_Start);
be_set_transform_function(op_Store, gen_Store); be_set_transform_function(op_Store, gen_Store);
be_set_transform_function(op_Sub, gen_Sub); be_set_transform_function(op_Sub, gen_Sub);
be_set_transform_function(op_Switch, gen_Switch);
be_set_transform_function(op_SymConst, gen_SymConst); be_set_transform_function(op_SymConst, gen_SymConst);
be_set_transform_function(op_Unknown, gen_Unknown); be_set_transform_function(op_Unknown, gen_Unknown);
be_set_transform_function(op_Builtin, gen_Builtin); be_set_transform_function(op_Builtin, gen_Builtin);
......
...@@ -541,7 +541,7 @@ static void arm_lower_for_target(void) ...@@ -541,7 +541,7 @@ static void arm_lower_for_target(void)
for (i = 0; i < n_irgs; ++i) { for (i = 0; i < n_irgs; ++i) {
ir_graph *irg = get_irp_irg(i); ir_graph *irg = get_irp_irg(i);
lower_switch(irg, 4, 256, true); lower_switch(irg, 4, 256, false);
} }
for (i = 0; i < n_irgs; ++i) { for (i = 0; i < n_irgs; ++i) {
......
...@@ -1720,66 +1720,101 @@ static void emit_global_decls(const be_main_env_t *main_env) ...@@ -1720,66 +1720,101 @@ static void emit_global_decls(const be_main_env_t *main_env)
} }
} }
void emit_jump_table(const ir_node *node, long default_pn, ir_entity *entity, void be_emit_jump_table(const ir_node *node, const ir_switch_table *table,
get_cfop_target_func get_cfop_target) ir_entity *entity, get_cfop_target_func get_cfop_target)
{ {
long switch_max = LONG_MIN; unsigned n_outs = arch_get_irn_n_outs(node);
ir_node *default_block = NULL; const ir_node **targets = XMALLOCNZ(const ir_node*, n_outs);
unsigned long length; size_t n_entries = ir_switch_table_get_n_entries(table);
const ir_edge_t *edge; unsigned long length = 0;
unsigned i;