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;
typedef void irg_walk_func(ir_node *, 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. */
typedef const ir_node *ir_node_cnst_ptr;
......
......@@ -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 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"
......
......@@ -41,6 +41,7 @@
#include "irtools.h"
#include "array_t.h"
#include "debug.h"
#include "error.h"
#include "irflag.h"
/**
......@@ -75,32 +76,53 @@ static ir_node *get_effective_use_block(ir_node *node, int pos)
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.
*
* @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
* 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;
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);
int pos = get_edge_src_pos(edge);
ir_node *blk = get_effective_use_block(succ, pos);
next = get_irn_out_edge_next(irn, edge);
if (block_dominates(block, blk)) {
/*
* 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)
* We can replace the input with the Constant
* branch label.
*/
if (! c) {
ir_mode *mode = get_irn_mode(irn);
ir_tarval *tv = new_tarval_from_long(nr, mode);
c = new_r_Const(current_ir_graph, tv);
}
if (c == NULL)
c = get_case_value(switchn, pn);
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));
env->num_consts += 1;
......@@ -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)
{
ir_node *cond, *proj, *selector;
ir_mode *mode;
env_t *env = (env_t*) data;
ir_node *cond;
ir_node *proj;
/*
* 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)
return;
cond = get_Proj_pred(proj);
if (! is_Cond(cond))
return;
selector = get_Cond_selector(cond);
mode = get_irn_mode(selector);
if (mode == mode_b) {
if (is_Switch(cond)) {
long proj_nr = get_Proj_proj(proj);
handle_case(block, cond, proj_nr, env);
} else if (is_Const(cond)) {
ir_node *selector = get_Cond_selector(cond);
ir_relation rel;
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)
if (get_Proj_proj(proj) != pn_Cond_true) {
/* it's the false branch */
mode = get_irn_mode(get_Cmp_left(selector));
rel = get_negated_relation(rel);
}
DB((dbg, LEVEL_2, "At %+F using %+F Confirm %=\n", block, selector, rel));
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)
static void emit_arm_SwitchJmp(const ir_node *irn)
{
const ir_edge_t *edge;
ir_node *proj;
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 ");
const arm_SwitchJmp_attr_t *attr = get_arm_SwitchJmp_attr_const(irn);
be_emit_cstring("\tldrls pc, [pc, ");
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_cstring("\tbhi ");
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);
be_emit_jump_table(irn, attr->table, NULL, get_cfop_target_block);
}
/** Emit an IncSP node */
......
......@@ -324,30 +324,6 @@ void set_arm_CondJmp_relation(ir_node *node, ir_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. */
static void init_arm_attributes(ir_node *node, arch_irn_flags_t flags,
const arch_register_req_t ** in_reqs,
......@@ -420,6 +396,20 @@ static void init_arm_CopyB_attributes(ir_node *res, unsigned 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)
{
(void) a;
......
......@@ -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,
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 "gen_arm_new_nodes.h"
......
......@@ -112,9 +112,8 @@ typedef struct arm_CondJmp_attr_t {
/** Attributes for a SwitchJmp */
typedef struct arm_SwitchJmp_attr_t {
arm_attr_t base;
int n_projs;
long default_proj_num;
arm_attr_t base;
const ir_switch_table *table;
} arm_SwitchJmp_attr_t;
/** CopyB attributes */
......
......@@ -415,11 +415,10 @@ SwitchJmp => {
op_flags => [ "labeled", "cfopcode", "forking" ],
state => "pinned",
mode => "mode_T",
attr => "int n_projs, long def_proj_num",
init_attr => "\tset_arm_SwitchJmp_n_projs(res, n_projs);\n".
"\tset_arm_SwitchJmp_default_proj_num(res, def_proj_num);\n".
"\tinfo->out_infos = NULL;",
attr => "const ir_switch_table *table",
init_attr => "init_arm_SwitchJmp_attributes(res, table);",
reg_req => { in => [ "gp" ], out => [ "none" ] },
out_arity => "variable",
attr_type => "arm_SwitchJmp_attr_t",
},
......
......@@ -1013,46 +1013,23 @@ static ir_node *gen_Jmp(ir_node *node)
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_node *selector = get_Cond_selector(node);
dbg_info *dbgi = get_irn_dbg_info(node);
ir_node *new_op = be_transform_node(selector);
ir_node *const_graph;
ir_node *sub;
ir_node *proj;
const ir_edge_t *edge;
int min = INT_MAX;
int max = INT_MIN;
int translation;
int pn;
int n_projs;
ir_graph *irg = get_irn_irg(node);
ir_node *block = be_transform_node(get_nodes_block(node));
ir_node *selector = get_Switch_selector(node);
dbg_info *dbgi = get_irn_dbg_info(node);
ir_node *new_op = be_transform_node(selector);
ir_mode *mode = get_irn_mode(selector);
const ir_switch_table *table = get_Switch_table(node);
unsigned n_outs = get_Switch_n_outs(node);
foreach_out_edge(node, edge) {
proj = get_edge_src_irn(edge);
assert(is_Proj(proj) && "Only proj allowed at SwitchJmp");
table = ir_switch_table_duplicate(irg, table);
pn = get_Proj_proj(proj);
min = pn<min ? pn : min;
max = pn>max ? pn : max;
}
translation = min;
n_projs = max - translation + 1;
/* switch with smaller modes not implemented yet */
assert(get_mode_size_bits(mode) == 32);
foreach_out_edge(node, edge) {
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);
return new_bd_arm_SwitchJmp(dbgi, block, new_op, n_outs, table);
}
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)
{
ir_node *selector = get_Cond_selector(node);
ir_mode *mode = get_irn_mode(selector);
ir_relation relation;
ir_node *block;
ir_node *flag_node;
dbg_info *dbgi;
if (mode != mode_b) {
return gen_SwitchJmp(node);
}
assert(is_Cmp(selector));
block = be_transform_node(get_nodes_block(node));
......@@ -1612,6 +1585,7 @@ static ir_node *gen_Proj(ir_node *node)
case iro_Start:
return gen_Proj_Start(node);
case iro_Cond:
case iro_Switch:
/* nothing to do */
return be_duplicate_node(node);
case iro_Proj: {
......@@ -2102,6 +2076,7 @@ static void arm_register_transformers(void)
be_set_transform_function(op_Start, gen_Start);
be_set_transform_function(op_Store, gen_Store);
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_Unknown, gen_Unknown);
be_set_transform_function(op_Builtin, gen_Builtin);
......
......@@ -541,7 +541,7 @@ static void arm_lower_for_target(void)
for (i = 0; i < n_irgs; ++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) {
......
......@@ -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,
get_cfop_target_func get_cfop_target)
void be_emit_jump_table(const ir_node *node, const ir_switch_table *table,
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 */
unsigned n_outs = arch_get_irn_n_outs(node);
const ir_node **targets = XMALLOCNZ(const ir_node*, n_outs);
size_t n_entries = ir_switch_table_get_n_entries(table);
unsigned long length = 0;
size_t e;
const ir_edge_t *edge;
unsigned i;
const ir_node **labels;
/* go over all proj's and collect their jump targets */
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;
ir_node *proj = get_edge_src_irn(edge);
long pn = get_Proj_proj(proj);
ir_node *target = get_cfop_target(proj);
assert(targets[pn] == NULL);
targets[pn] = target;
}
/* go over table to determine max value (note that we normalized the
* ranges so that the minimum is 0) */
for (e = 0; e < n_entries; ++e) {
const ir_switch_table_entry *entry
= ir_switch_table_get_entry_const(table, e);
ir_tarval *max = entry->max;
unsigned long val;
if (entry->pn == 0)
continue;
if (!tarval_is_long(max))
panic("switch case overflow (%+F)", node);
val = (unsigned long) get_tarval_long(max);
if (val > length) {
length = val;
}
}
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");
panic("too large switch encountered (%+F)", node);
}
++length;
labels = XMALLOCNZ(const ir_node*, length);
for (e = 0; e < n_entries; ++e) {
const ir_switch_table_entry *entry
= ir_switch_table_get_entry_const(table, e);
ir_tarval *min = entry->min;
ir_tarval *max = entry->max;
const ir_node *target = targets[entry->pn];
assert(entry->pn < (long)n_outs);
if (min == max) {
unsigned long val = (unsigned long)get_tarval_long(max);
labels[val] = target;
} else {
unsigned long min_val;
unsigned long max_val;
unsigned long i;
if (!tarval_is_long(min))
panic("switch case overflow (%+F)", node);
min_val = (unsigned long)get_tarval_long(min);
max_val = (unsigned long)get_tarval_long(max);
assert(min_val <= max_val);
for (i = min_val; i <= max_val; ++i) {
labels[i] = target;
}
}
}
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 */
if (entity != NULL) {
be_gas_emit_switch_section(GAS_SECTION_RODATA);
be_emit_cstring("\t.align 4\n");
be_gas_emit_entity(entity);
be_emit_cstring(":\n");
}
/* 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];
const ir_node *block = labels[i];
if (block == NULL)
block = default_block;
block = targets[0];
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);
if (entity != NULL)