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;
*/
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 "irtools.h"
#include "array_t.h"
#include "debug.h"
+#include "error.h"
#include "irflag.h"
/**
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
* 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;
*/
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
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);
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);
}
}
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 */
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,
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;
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"
/** 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 */
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",
},
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)
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));
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: {
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);
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) {
}
}
-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)
+ be_gas_emit_switch_section(GAS_SECTION_TEXT);
+
+ xfree(labels);
+ xfree(targets);
}
static void emit_global_asms(void)
/**
* 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);
+void be_emit_jump_table(const ir_node *node, const ir_switch_table *table,
+ ir_entity *entity,
+ get_cfop_target_func get_cfop_target);
#endif
*/
static void emit_ia32_SwitchJmp(const ir_node *node)
{
- ir_entity *jump_table = get_ia32_am_sc(node);
- long default_pn = get_ia32_default_pn(node);
+ ir_entity *jump_table = get_ia32_am_sc(node);
+ const ir_switch_table *table = get_ia32_switch_table(node);
ia32_emitf(node, "\tjmp %*AM\n");
- emit_jump_table(node, default_pn, jump_table, get_cfop_target_block);
+ be_emit_jump_table(node, table, jump_table, get_cfop_target_block);
}
/**
static void bemit_switchjmp(const ir_node *node)
{
- ir_entity *jump_table = get_ia32_am_sc(node);
- long default_pn = get_ia32_default_pn(node);
+ ir_entity *jump_table = get_ia32_am_sc(node);
+ const ir_switch_table *table = get_ia32_switch_table(node);
bemit8(0xFF); // jmp *tbl.label(,%in,4)
bemit_mod_am(0x05, node);
- emit_jump_table(node, default_pn, jump_table, get_cfop_target_block);
+ be_emit_jump_table(node, table, jump_table, get_cfop_target_block);
}
/**
fprintf(F, "AM scale = %u\n", get_ia32_am_scale(n));
/* dump pn code */
- if (is_ia32_SwitchJmp(n)) {
- fprintf(F, "default_pn = %ld\n", get_ia32_default_pn(n));
- } else if (is_ia32_CMovcc(n) || is_ia32_Setcc(n) || is_ia32_Jcc(n)) {
+ if (is_ia32_CMovcc(n) || is_ia32_Setcc(n) || is_ia32_Jcc(n)) {
ia32_attr_t *attr = get_ia32_attr(n);
fprintf(F, "condition_code = 0x%X\n", (unsigned)get_ia32_condcode(n));
fprintf(F, "ins_permuted = %u\n", (unsigned)attr->data.ins_permuted);
return op_attr->latency;
}
-/**
- * Returns the condition code of a node.
- */
+const ir_switch_table *get_ia32_switch_table(const ir_node *node)
+{
+ const ia32_switch_attr_t *attr = get_ia32_switch_attr_const(node);
+ return attr->table;
+}
+
ia32_condition_code_t get_ia32_condcode(const ir_node *node)
{
const ia32_condcode_attr_t *attr = get_ia32_condcode_attr_const(node);
attr->condition_code = code;
}
-long get_ia32_default_pn(const ir_node *node)
-{
- const ia32_switch_attr_t *attr = get_ia32_switch_attr_const(node);
- return attr->default_pn;
-}
-
/**
* Returns the condition code of a node.
*/
attr->count = count;
}
-static void init_ia32_switch_attributes(ir_node *res, long default_pn)
+static void init_ia32_switch_attributes(ir_node *node,
+ const ir_switch_table *table)
{
- ia32_switch_attr_t *attr = (ia32_switch_attr_t*) get_irn_generic_attr(res);
+ unsigned n_outs = arch_get_irn_n_outs(node);
+ unsigned o;
+
+ ia32_switch_attr_t *attr = (ia32_switch_attr_t*) get_irn_generic_attr(node);
#ifndef NDEBUG
attr->attr.attr_type |= IA32_ATTR_ia32_switch_attr_t;
#endif
- attr->default_pn = default_pn;
-}
+ attr->table = table;
+ for (o = 0; o < n_outs; ++o) {
+ arch_set_irn_register_req_out(node, o, arch_no_register_req);
+ }
+}
/* default compare operation to compare attributes */
static int ia32_compare_attr(const ia32_attr_t *a, const ia32_attr_t *b)
return 0;
}
-/** Compare node attributes for nodes with condition code. */
-static int ia32_compare_switch_attr(const ir_node *a, const ir_node *b)
-{
- const ia32_switch_attr_t *attr_a;
- const ia32_switch_attr_t *attr_b;
-
- if (ia32_compare_nodes_attr(a, b))
- return 1;
-
- attr_a = get_ia32_switch_attr_const(a);
- attr_b = get_ia32_switch_attr_const(b);
-
- if (attr_a->default_pn != attr_b->default_pn)
- return 1;
-
- return 0;
-}
-
/** Compare node attributes for call nodes. */
static int ia32_compare_call_attr(const ir_node *a, const ir_node *b)
{
*/
void set_ia32_condcode(ir_node *node, ia32_condition_code_t code);
-long get_ia32_default_pn(const ir_node *node);
+const ir_switch_table *get_ia32_switch_table(const ir_node *node);
unsigned get_ia32_copyb_size(const ir_node *node);
*/
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;
+ ia32_attr_t attr; /**< generic attribute */
+ const ir_switch_table *table;
+ ir_entity *jump_table;
};
/**
"\tinit_ia32_condcode_attributes(res, condition_code);",
ia32_switch_attr_t =>
"\tinit_ia32_attributes(res, irn_flags_, in_reqs, exec_units, n_res);\n".
- "\tinit_ia32_switch_attributes(res, default_pn);",
+ "\tinit_ia32_switch_attributes(res, switch_table);",
ia32_copyb_attr_t =>
"\tinit_ia32_attributes(res, irn_flags_, in_reqs, exec_units, n_res);\n".
"\tinit_ia32_copyb_attributes(res, size);",
ia32_attr_t => "ia32_compare_nodes_attr",
ia32_call_attr_t => "ia32_compare_call_attr",
ia32_condcode_attr_t => "ia32_compare_condcode_attr",
- ia32_switch_attr_t => "ia32_compare_switch_attr",
ia32_copyb_attr_t => "ia32_compare_copyb_attr",
+ ia32_switch_attr_t => "ia32_compare_nodes_attr",
ia32_immediate_attr_t => "ia32_compare_immediate_attr",
ia32_x87_attr_t => "ia32_compare_x87_attr",
ia32_climbframe_attr_t => "ia32_compare_climbframe_attr",
op_flags => [ "labeled", "cfopcode", "forking" ],
reg_req => { in => [ "gp", "gp" ] },
ins => [ "base", "index" ],
- mode => "mode_T",
+ out_arity => "variable",
attr_type => "ia32_switch_attr_t",
- attr => "long default_pn",
+ attr => "const ir_switch_table *switch_table",
latency => 2,
units => [ "BRANCH" ],
- init_attr => "info->out_infos = NULL;", # XXX ugly hack for out requirements
},
Jmp => {
*
* @return the created ia32 SwitchJmp 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 default_pn = get_Cond_default_proj(node);
- ir_node *new_node;
- ir_entity *entity;
-
- assert(get_mode_size_bits(get_irn_mode(sel)) == 32);
+static ir_node *gen_Switch(ir_node *node)
+{
+ dbg_info *dbgi = get_irn_dbg_info(node);
+ ir_graph *irg = get_irn_irg(node);
+ ir_node *block = be_transform_node(get_nodes_block(node));
+ ir_node *sel = get_Switch_selector(node);
+ ir_node *new_sel = be_transform_node(sel);
+ ir_mode *sel_mode = get_irn_mode(sel);
+ const ir_switch_table *table = get_Switch_table(node);
+ unsigned n_outs = get_Switch_n_outs(node);
+ ir_node *new_node;
+ ir_entity *entity;
+
+ assert(get_mode_size_bits(get_irn_mode(sel)) <= 32);
+ if (get_mode_size_bits(sel_mode) != 32)
+ new_sel = create_upconv(new_sel, sel);
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);
- /* 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);
+ table = ir_switch_table_duplicate(irg, table);
+
+ new_node = new_bd_ia32_SwitchJmp(dbgi, block, noreg_GP, new_sel, n_outs, table);
set_ia32_am_scale(new_node, 2);
set_ia32_am_sc(new_node, entity);
set_ia32_op_type(new_node, ia32_AddrModeS);
ir_node *new_block = be_transform_node(block);
dbg_info *dbgi = get_irn_dbg_info(node);
ir_node *sel = get_Cond_selector(node);
- ir_mode *sel_mode = get_irn_mode(sel);
ir_node *flags = NULL;
ir_node *new_node;
ia32_condition_code_t cc;
- if (sel_mode != mode_b) {
- return create_Switch(node);
- }
-
/* we get flags from a Cmp */
flags = get_flags_node(sel, &cc);
be_set_transform_function(op_Shrs, gen_Shrs);
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, ia32_gen_Unknown);
}
be_emit_finish_line_gas(node);
fill_delay_slot();
- emit_jump_table(node, attr->default_proj_num, attr->jump_table,
- get_jump_target);
+ be_emit_jump_table(node, attr->table, attr->table_entity, get_jump_target);
}
static void emit_fmov(const ir_node *node, const arch_register_t *src_reg,
get_relation_string(attr->relation));
fprintf(F, "unsigned: %s\n", attr->is_unsigned ? "true" : "false");
}
- if (has_switch_jmp_attr(n)) {
- const sparc_switch_jmp_attr_t *attr
- = get_sparc_switch_jmp_attr_const(n);
- fprintf(F, "default proj: %ld\n", attr->default_proj_num);
- }
if (has_fp_attr(n)) {
const sparc_fp_attr_t *attr = get_sparc_fp_attr_const(n);
ir_fprintf(F, "fp_mode: %+F\n", attr->fp_mode);
attr->dest_mode = dest_mode;
}
-static void init_sparc_switch_jmp_attributes(ir_node *res, long default_pn,
- ir_entity *jump_table)
+static void init_sparc_switch_jmp_attributes(ir_node *node,
+ const ir_switch_table *table,
+ ir_entity *table_entity)
{
- sparc_switch_jmp_attr_t *attr = get_sparc_switch_jmp_attr(res);
- attr->default_proj_num = default_pn;
- attr->jump_table = jump_table;
+ unsigned n_outs = arch_get_irn_n_outs(node);
+ unsigned o;
+
+ sparc_switch_jmp_attr_t *attr = get_sparc_switch_jmp_attr(node);
+ attr->table = table;
+ attr->table_entity = table_entity;
+
+ for (o = 0; o < n_outs; ++o) {
+ arch_set_irn_register_req_out(node, o, arch_no_register_req);
+ }
}
/**
|| attr_a->is_unsigned != attr_b->is_unsigned;
}
-static int cmp_attr_sparc_switch_jmp(const ir_node *a, const ir_node *b)
-{
- const sparc_switch_jmp_attr_t *attr_a = get_sparc_switch_jmp_attr_const(a);
- const sparc_switch_jmp_attr_t *attr_b = get_sparc_switch_jmp_attr_const(b);
-
- if (cmp_attr_sparc(a, b))
- return 1;
-
- return attr_a->default_proj_num != attr_b->default_proj_num;
-}
-
static int cmp_attr_sparc_fp(const ir_node *a, const ir_node *b)
{
const sparc_fp_attr_t *attr_a = get_sparc_fp_attr_const(a);
*/
typedef struct sparc_switch_jmp_attr_t sparc_switch_jmp_attr_t;
struct sparc_switch_jmp_attr_t {
- sparc_attr_t base; /**< generic attribute */
- long default_proj_num;
- ir_entity *jump_table;
+ sparc_attr_t base;
+ const ir_switch_table *table;
+ ir_entity *table_entity;
};
#endif
sparc_load_store_attr_t => "\tinit_sparc_attributes(res, irn_flags_, in_reqs, exec_units, n_res);",
sparc_jmp_cond_attr_t => "\tinit_sparc_attributes(res, irn_flags_, in_reqs, exec_units, n_res);",
sparc_switch_jmp_attr_t => "\tinit_sparc_attributes(res, irn_flags_, in_reqs, exec_units, n_res);\n".
- "\tinit_sparc_switch_jmp_attributes(res, default_pn, jump_table);\n",
+ "\tinit_sparc_switch_jmp_attributes(res, table, jump_table);\n",
sparc_fp_attr_t => "\tinit_sparc_attributes(res, irn_flags_, in_reqs, exec_units, n_res);\n".
"\tinit_sparc_fp_attributes(res, fp_mode);\n",
sparc_fp_conv_attr_t => "\tinit_sparc_attributes(res, irn_flags_, in_reqs, exec_units, n_res);".
%compare_attr = (
sparc_attr_t => "cmp_attr_sparc",
- sparc_load_store_attr_t => "cmp_attr_sparc_load_store",
- sparc_jmp_cond_attr_t => "cmp_attr_sparc_jmp_cond",
- sparc_switch_jmp_attr_t => "cmp_attr_sparc_switch_jmp",
sparc_fp_attr_t => "cmp_attr_sparc_fp",
sparc_fp_conv_attr_t => "cmp_attr_sparc_fp_conv",
+ sparc_jmp_cond_attr_t => "cmp_attr_sparc_jmp_cond",
+ sparc_load_store_attr_t => "cmp_attr_sparc_load_store",
+ sparc_switch_jmp_attr_t => "cmp_attr_sparc",
);
%custom_irn_flags = (
state => "pinned",
mode => "mode_T",
reg_req => { in => [ "gp" ], out => [ ] },
+ out_arity => "variable",
attr_type => "sparc_switch_jmp_attr_t",
- attr => "long default_pn, ir_entity *jump_table",
- init_attr => "info->out_infos = NULL;", # XXX ugly hack for out requirements
+ attr => "const ir_switch_table *table, ir_entity *jump_table",
},
Sll => {
}
}
-static ir_node *gen_SwitchJmp(ir_node *node)
-{
- dbg_info *dbgi = get_irn_dbg_info(node);
- ir_node *block = be_transform_node(get_nodes_block(node));
- ir_node *selector = get_Cond_selector(node);
- ir_node *new_selector = be_transform_node(selector);
- long default_pn = get_Cond_default_proj(node);
- ir_entity *entity;
- ir_node *table_address;
- ir_node *idx;
- ir_node *load;
- ir_node *address;
+static ir_node *gen_Switch(ir_node *node)
+{
+ dbg_info *dbgi = get_irn_dbg_info(node);
+ ir_node *block = get_nodes_block(node);
+ ir_node *new_block = be_transform_node(block);
+ ir_graph *irg = get_irn_irg(block);
+ ir_node *selector = get_Switch_selector(node);
+ ir_node *new_selector = be_transform_node(selector);
+ const ir_switch_table *table = get_Switch_table(node);
+ unsigned n_outs = get_Switch_n_outs(node);
+ ir_entity *entity;
+ ir_node *table_address;
+ ir_node *idx;
+ ir_node *load;
+ ir_node *address;
+
+ table = ir_switch_table_duplicate(irg, table);
/* switch with smaller mode not implemented yet */
assert(get_mode_size_bits(get_irn_mode(selector)) == 32);
add_entity_linkage(entity, IR_LINKAGE_CONSTANT);
/* construct base address */
- table_address = make_address(dbgi, block, entity, 0);
+ table_address = make_address(dbgi, new_block, entity, 0);
/* scale index */
- idx = new_bd_sparc_Sll_imm(dbgi, block, new_selector, NULL, 2);
+ idx = new_bd_sparc_Sll_imm(dbgi, new_block, new_selector, NULL, 2);
/* load from jumptable */
- load = new_bd_sparc_Ld_reg(dbgi, block, table_address, idx,
+ load = new_bd_sparc_Ld_reg(dbgi, new_block, table_address, idx,
get_irg_no_mem(current_ir_graph),
mode_gp);
address = new_r_Proj(load, mode_gp, pn_sparc_Ld_res);
- return new_bd_sparc_SwitchJmp(dbgi, block, address, default_pn, entity);
+ return new_bd_sparc_SwitchJmp(dbgi, new_block, address, n_outs, table, entity);
}
static ir_node *gen_Cond(ir_node *node)
{
ir_node *selector = get_Cond_selector(node);
- ir_mode *mode = get_irn_mode(selector);
ir_node *cmp_left;
ir_mode *cmp_mode;
ir_node *block;
ir_relation relation;
dbg_info *dbgi;
- /* switch/case jumps */
- if (mode != mode_b) {
- return gen_SwitchJmp(node);
- }
-
/* note: after lower_mode_b we are guaranteed to have a Cmp input */
block = be_transform_node(get_nodes_block(node));
dbgi = get_irn_dbg_info(node);
return gen_Proj_Call(node);
case iro_Cmp:
return gen_Proj_Cmp(node);
+ case iro_Switch:
case iro_Cond:
return be_duplicate_node(node);
case iro_Div:
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);
}
break;
- case iro_Proj: {
- ir_node *pred = get_Proj_pred(n);
-
- if (get_irn_opcode(pred) == iro_Cond
- && get_Proj_proj(n) == get_Cond_default_proj(pred)
- && get_irn_mode(get_Cond_selector(pred)) != mode_b)
- fprintf(F, "defProj");
- else
- goto default_case;
- } break;
-
case iro_Load:
if (get_Load_unaligned(n) == align_non_aligned)
fprintf(F, "ua");
break;
default:
-default_case:
fprintf(F, "%s", get_irn_opname(n));
}
}
#include "irdump_t.h"
#include "irgraph_t.h"
+#include "irnode_t.h"
#include "irprog_t.h"
#include "entity_t.h"
fprintf(F, " Private Attributes:\n");
- if (is_Proj(n))
- fprintf(F, " proj nr: %ld\n", get_Proj_proj(n));
+ if (is_Proj(n)) {
+ ir_node *pred = get_Proj_pred(n);
+ long pn = get_Proj_proj(n);
+ fprintf(F, " proj nr: %ld\n", pn);
+ if (is_Switch(pred)) {
+ const ir_switch_table *table = get_Switch_table(pred);
+ size_t n_entries = ir_switch_table_get_n_entries(table);
+ size_t i;
+ for (i = 0; i < n_entries; ++i) {
+ const ir_switch_table_entry *entry
+ = ir_switch_table_get_entry_const(table, i);
+ if (entry->pn == pn && entry->min != NULL && entry->max != NULL) {
+ ir_tarval *min = entry->min;
+ ir_tarval *max = entry->max;
+ if (min != max) {
+ ir_fprintf(F, " switch case %+F .. %+F\n", min, max);
+ } else {
+ ir_fprintf(F, " switch case %+F\n", min);
+ }
+ }
+ }
+ }
+ }
if (is_fragile_op(n)) {
fprintf(F, " pinned state: %s\n", get_op_pin_state_name(get_irn_pinned(n)));
ir_fprintf(F, " param %d type: %+F\n", i, get_method_param_type(tp, i));
} break;
case iro_Cond: {
- fprintf(F, " default ProjNr: %ld\n", get_Cond_default_proj(n));
if (get_Cond_jmp_pred(n) != COND_JMP_PRED_NONE) {
fprintf(F, " jump prediction: %s\n",
get_cond_jmp_predicate_name(get_Cond_jmp_pred(n)));
return get_irn_dbg_info_(n);
}
+ir_switch_table *ir_new_switch_table(ir_graph *irg, size_t n_entries)
+{
+ struct obstack *obst = get_irg_obstack(irg);
+ ir_switch_table *res = OALLOCFZ(obst, ir_switch_table, entries, n_entries);
+ res->n_entries = n_entries;
+ return res;
+}
+
+void ir_switch_table_set(ir_switch_table *table, size_t n,
+ ir_tarval *min, ir_tarval *max, long pn)
+{
+ ir_switch_table_entry *entry = ir_switch_table_get_entry(table, n);
+ entry->min = min;
+ entry->max = max;
+ entry->pn = pn;
+}
+
+size_t (ir_switch_table_get_n_entries)(const ir_switch_table *table)
+{
+ return ir_switch_table_get_n_entries_(table);
+}
+
+ir_tarval *ir_switch_table_get_max(const ir_switch_table *table, size_t e)
+{
+ return ir_switch_table_get_entry_const(table, e)->max;
+}
+
+ir_tarval *ir_switch_table_get_min(const ir_switch_table *table, size_t e)
+{
+ return ir_switch_table_get_entry_const(table, e)->min;
+}
+
+long ir_switch_table_get_pn(const ir_switch_table *table, size_t e)
+{
+ return ir_switch_table_get_entry_const(table, e)->pn;
+}
+
+ir_switch_table *ir_switch_table_duplicate(ir_graph *irg,
+ const ir_switch_table *table)
+{
+ size_t n_entries = ir_switch_table_get_n_entries(table);
+ size_t e;
+ ir_switch_table *res = ir_new_switch_table(irg, n_entries);
+ for (e = 0; e < n_entries; ++e) {
+ const ir_switch_table_entry *entry
+ = ir_switch_table_get_entry_const(table, e);
+ ir_switch_table_entry *new_entry = ir_switch_table_get_entry(res, e);
+ *new_entry = *entry;
+ }
+ return res;
+}
+
/*
* Calculate a hash value of a node.
*/
return pn_Start_T_args == get_Proj_proj(node) && is_Start(get_Proj_pred(node));
}
+static inline size_t ir_switch_table_get_n_entries_(const ir_switch_table *table)
+{
+ return table->n_entries;
+}
+
+static inline ir_switch_table_entry *ir_switch_table_get_entry(
+ ir_switch_table *table, size_t entry)
+{
+ assert(entry < table->n_entries);
+ return &table->entries[entry];
+}
+
+static inline const ir_switch_table_entry *ir_switch_table_get_entry_const(
+ const ir_switch_table *table, size_t entry)
+{
+ assert(entry < table->n_entries);
+ return &table->entries[entry];
+}
+
/** initialize ir_node module */
void init_irnode(void);
#define get_Phi_next(node) get_Phi_next_(node)
#define is_arg_Proj(node) is_arg_Proj_(node)
+#define ir_switch_table_get_n_entries(table) ir_switch_table_get_n_entries_(table)
#endif
new_node->attr.assem.clobbers = DUP_ARR_D(ident*, irg->obst, old_node->attr.assem.clobbers);
}
+static void switch_copy_attr(ir_graph *irg, const ir_node *old_node,
+ ir_node *new_node)
+{
+ const ir_switch_table *table = get_Switch_table(old_node);
+ new_node->attr.switcha.table = ir_switch_table_duplicate(irg, table);
+ new_node->attr.switcha.n_outs = old_node->attr.switcha.n_outs;
+}
+
/**
* Sets the default copy_attr operation for an ir_ops
*
static ir_op_ops *firm_set_default_copy_attr(unsigned code, ir_op_ops *ops)
{
switch (code) {
- case iro_Call:
- ops->copy_attr = call_copy_attr;
- break;
- case iro_Block:
- ops->copy_attr = block_copy_attr;
- break;
- case iro_Phi:
- ops->copy_attr = phi_copy_attr;
- break;
- case iro_ASM:
- ops->copy_attr = ASM_copy_attr;
- break;
+ case iro_Call: ops->copy_attr = call_copy_attr; break;
+ case iro_Block: ops->copy_attr = block_copy_attr; break;
+ case iro_Phi: ops->copy_attr = phi_copy_attr; break;
+ case iro_ASM: ops->copy_attr = ASM_copy_attr; break;
+ case iro_Switch: ops->copy_attr = switch_copy_attr; break;
default:
- /* not allowed to be NULL */
- if (! ops->copy_attr)
+ if (ops->copy_attr == NULL)
ops->copy_attr = default_copy_attr;
}
return ops;
*/
static ir_node *transform_node_Cond(ir_node *n)
{
-
ir_node *a = get_Cond_selector(n);
ir_graph *irg = get_irn_irg(n);
ir_tarval *ta;
if (get_irg_pinned(irg) == op_pin_state_floats)
return n;
- /* we do not handle switches here */
- if (get_irn_mode(a) != mode_b)
- return n;
-
ta = value_of(a);
if (ta == tarval_bad && is_Cmp(a)) {
/* try again with a direct call to compute_cmp, as we don't care
return n;
}
+static ir_node *transform_node_Switch(ir_node *n)
+{
+ ir_node *op = get_Switch_selector(n);
+ ir_tarval *val = value_of(op);
+ if (val != tarval_bad) {
+ dbg_info *dbgi = get_irn_dbg_info(n);
+ ir_graph *irg = get_irn_irg(n);
+ unsigned n_outs = get_Switch_n_outs(n);
+ ir_node *block = get_nodes_block(n);
+ ir_node *bad = new_r_Bad(irg, mode_X);
+ ir_node **in = XMALLOCN(ir_node*, n_outs);
+ const ir_switch_table *table = get_Switch_table(n);
+ size_t n_entries = ir_switch_table_get_n_entries(table);
+ long jmp_pn = 0;
+ size_t i;
+ unsigned o;
+ for (i = 0; i < n_entries; ++i) {
+ const ir_switch_table_entry *entry
+ = ir_switch_table_get_entry_const(table, i);
+ ir_tarval *min = entry->min;
+ ir_tarval *max = entry->max;
+ if (entry->pn == 0)
+ continue;
+ if ((min == max && min == val)
+ || (tarval_cmp(val, min) != ir_relation_less
+ && tarval_cmp(val, max) != ir_relation_greater)) {
+ jmp_pn = entry->pn;
+ break;
+ }
+ }
+ for (o = 0; o < n_outs; ++o) {
+ if (o == (unsigned)jmp_pn) {
+ in[o] = new_rd_Jmp(dbgi, block);
+ } else {
+ in[o] = bad;
+ }
+ }
+ return new_r_Tuple(block, (int)n_outs, in);
+ }
+ return n;
+}
+
/**
* normalisation: (x & c1) >> c2 to (x >> c2) & (c1 >> c2)
* (we can use:
return proj;
}
-/**
- * Optimizes jump tables (CondIs or CondIu) by removing all impossible cases.
- */
-static ir_node *transform_node_Proj_Cond(ir_node *proj)
-{
- ir_node *n = get_Proj_pred(proj);
- ir_node *b = get_Cond_selector(n);
-
- if (mode_is_int(get_irn_mode(b))) {
- ir_tarval *tb = value_of(b);
-
- if (tb != tarval_bad) {
- /* we have a constant switch */
- long num = get_Proj_proj(proj);
-
- if (num != get_Cond_default_proj(n)) { /* we cannot optimize default Proj's yet */
- if (get_tarval_long(tb) == num) {
- /* Do NOT create a jump here, or we will have 2 control flow ops
- * in a block. This case is optimized away in optimize_cf(). */
- return proj;
- } else {
- ir_graph *irg = get_irn_irg(proj);
- /* this case will NEVER be taken, kill it */
- clear_irg_state(irg, IR_GRAPH_STATE_NO_UNREACHABLE_CODE);
- return new_r_Bad(irg, mode_X);
- }
- }
- } else {
- long num = get_Proj_proj(proj);
- vrp_attr *b_vrp = vrp_get_info(b);
- if (num != get_Cond_default_proj(n) && b_vrp) {
- /* Try handling with vrp data. We only remove dead parts. */
- ir_tarval *tp = new_tarval_from_long(num, get_irn_mode(b));
-
- if (b_vrp->range_type == VRP_RANGE) {
- ir_relation cmp_result = tarval_cmp(b_vrp->range_bottom, tp);
- ir_relation cmp_result2 = tarval_cmp(b_vrp->range_top, tp);
-
- if ((cmp_result & ir_relation_greater) == cmp_result
- && (cmp_result2 & ir_relation_less) == cmp_result2) {
- ir_graph *irg = get_irn_irg(proj);
- clear_irg_state(irg, IR_GRAPH_STATE_NO_UNREACHABLE_CODE);
- return new_r_Bad(irg, mode_X);
- }
- } else if (b_vrp->range_type == VRP_ANTIRANGE) {
- ir_relation cmp_result = tarval_cmp(b_vrp->range_bottom, tp);
- ir_relation cmp_result2 = tarval_cmp(b_vrp->range_top, tp);
-
- if ((cmp_result & ir_relation_less_equal) == cmp_result
- && (cmp_result2 & ir_relation_greater_equal) == cmp_result2) {
- ir_graph *irg = get_irn_irg(proj);
- clear_irg_state(irg, IR_GRAPH_STATE_NO_UNREACHABLE_CODE);
- return new_r_Bad(irg, mode_X);
- }
- }
-
- if (!(tarval_cmp(
- tarval_and( b_vrp->bits_set, tp),
- b_vrp->bits_set
- ) == ir_relation_equal)) {
- ir_graph *irg = get_irn_irg(proj);
- clear_irg_state(irg, IR_GRAPH_STATE_NO_UNREACHABLE_CODE);
- return new_r_Bad(irg, mode_X);
- }
-
- if (!(tarval_cmp(
- tarval_and(
- tarval_not(tp),
- tarval_not(b_vrp->bits_not_set)),
- tarval_not(b_vrp->bits_not_set))
- == ir_relation_equal)) {
- ir_graph *irg = get_irn_irg(proj);
- clear_irg_state(irg, IR_GRAPH_STATE_NO_UNREACHABLE_CODE);
- return new_r_Bad(irg, mode_X);
- }
- }
- }
- }
- return proj;
-}
-
/**
* return true if the operation returns a value with exactly 1 bit set
*/
CASE(Block);
CASE(Call);
CASE(Cmp);
+ CASE(Cond);
CASE(Conv);
CASE(End);
CASE(Eor);
CASE(Shr);
CASE(Shrs);
CASE(Sub);
+ CASE(Switch);
CASE(Sync);
CASE_PROJ(Bound);
CASE_PROJ(CopyB);
CASE_PROJ(Store);
- CASE_PROJ_EX(Cond);
CASE_PROJ_EX(Div);
CASE_PROJ_EX(Load);
CASE_PROJ_EX(Mod);
const void *tv_priv; /**< tarval module will save private data here */
};
+/* note: we use "long" here because that is the type used for Proj-Numbers */
+typedef struct ir_switch_table_entry {
+ ir_tarval *min;
+ ir_tarval *max;
+ long pn;
+} ir_switch_table_entry;
+
+struct ir_switch_table {
+ size_t n_entries;
+ ir_switch_table_entry entries[];
+};
+
/* ir node attributes */
/** first attribute of Bad, Block, Anchor nodes */
long proj; /**< position of tuple sub-value which is projected */
} proj_attr;
+typedef struct switch_attr {
+ unsigned n_outs;
+ ir_switch_table *table;
+} switch_attr;
+
/** Some IR-nodes just have one attribute, these are stored here,
some have more. Their name is 'irnodename_attr' */
typedef union ir_attr {
div_attr div; /**< For Div operation */
mod_attr mod; /**< For Mod operation */
asm_attr assem; /**< For ASM operation. */
+ switch_attr switcha; /**< For Switch operation. */
} ir_attr;
/**
static int verify_node_Proj_Cond(const ir_node *p)
{
ir_mode *mode = get_irn_mode(p);
- ir_node *pred = get_Proj_pred(p);
- long proj = get_Proj_proj(p);
+ long proj = get_Proj_proj(p);
ASSERT_AND_RET_DBG(
- (
- (proj >= 0 && mode == mode_X && get_irn_mode(get_Cond_selector(pred)) == mode_b) || /* compare */
- (mode == mode_X && mode_is_int(get_irn_mode(get_Cond_selector(pred)))) /* switch */
- ),
+ mode == mode_X && (proj == pn_Cond_false || proj == pn_Cond_true),
"wrong Proj from Cond", 0,
show_proj_failure(p);
);
return 1;
}
+static int verify_node_Proj_Switch(const ir_node *p)
+{
+ ir_mode *mode = get_irn_mode(p);
+ long pn = get_Proj_proj(p);
+ ir_node *pred = get_Proj_pred(p);
+ ASSERT_AND_RET_DBG(
+ mode == mode_X && (pn >= 0 && pn < (long)get_Switch_n_outs(pred)),
+ "wrong Proj from Switch", 0,
+ show_proj_failure(p);
+ );
+ return 1;
+}
+
/**
* verify a Proj(Raise) node
*/
ir_mode *mymode = get_irn_mode(n);
ir_mode *op1mode = get_irn_mode(get_Cond_selector(n));
- ASSERT_AND_RET(
- /* Cond: BB x b --> X x X */
- (op1mode == mode_b ||
- /* Cond: BB x int --> X^n */
- mode_is_int(op1mode) ), "Cond node", 0
- );
+ ASSERT_AND_RET(op1mode == mode_b, "Cond operand not mode_b", 0);
ASSERT_AND_RET(mymode == mode_T, "Cond mode is not a tuple", 0);
+ return 1;
+}
+
+static int verify_switch_table(const ir_node *n)
+{
+ const ir_switch_table *table = get_Switch_table(n);
+ size_t n_entries = ir_switch_table_get_n_entries(table);
+ unsigned n_outs = get_Switch_n_outs(n);
+ 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 == 0)
+ continue;
+ ASSERT_AND_RET(entry->min != NULL && entry->max != NULL,
+ "switch table entry without min+max value", 0);
+ ASSERT_AND_RET(tarval_cmp(entry->min, entry->max) != ir_relation_greater,
+ "switch table entry without min+max value", 0);
+ ASSERT_AND_RET(entry->pn >= 0 && entry->pn < (long)n_outs,
+ "switch table entry with invalid proj number", 0);
+ }
+ return 1;
+}
+static int verify_node_Switch(const ir_node *n)
+{
+ ir_mode *mymode = get_irn_mode(n);
+ ir_mode *op1mode = get_irn_mode(get_Switch_selector(n));
+ if (!verify_switch_table(n))
+ return 0;
+
+ ASSERT_AND_RET(mode_is_int(op1mode), "Switch operand not integer", 0);
+ ASSERT_AND_RET(mymode == mode_T, "Switch mode is not a tuple", 0);
return 1;
}
if (is_Cond(branch)) {
long pn = get_Proj_proj(branch_proj);
- if (get_irn_mode(get_Cond_selector(branch)) == mode_b) {
- if (pn == pn_Cond_true)
- ir_nodeset_insert(&env->true_projs, branch);
- if (pn == pn_Cond_false)
- ir_nodeset_insert(&env->false_projs, branch);
- } else {
- long default_pn = get_Cond_default_proj(branch);
- if (pn == default_pn)
- ir_nodeset_insert(&env->true_projs, branch);
- }
+ if (pn == pn_Cond_true)
+ ir_nodeset_insert(&env->true_projs, branch);
+ if (pn == pn_Cond_false)
+ ir_nodeset_insert(&env->false_projs, branch);
+ } else if (is_Switch(branch)) {
+ long pn = get_Proj_proj(branch_proj);
+ if (pn == pn_Switch_default)
+ ir_nodeset_insert(&env->true_projs, branch);
}
}
static int verify_cond_projs(const ir_node *cond, check_cfg_env_t *env)
{
- if (get_irn_mode(get_Cond_selector(cond)) == mode_b) {
- ASSERT_AND_RET_DBG(ir_nodeset_contains(&env->true_projs, cond),
- "Cond node lacks true proj", 0,
- ir_printf("Cond %+F\n", cond);
- );
- ASSERT_AND_RET_DBG(ir_nodeset_contains(&env->false_projs, cond),
- "Cond node lacks false proj", 0,
- ir_printf("Cond %+F\n", cond);
- );
- } else {
- ASSERT_AND_RET_DBG(ir_nodeset_contains(&env->true_projs, cond),
- "Cond node lacks default Proj", 0,
- ir_printf("Cond %+F\n", cond);
- );
- }
+ ASSERT_AND_RET_DBG(ir_nodeset_contains(&env->true_projs, cond),
+ "Cond node lacks true proj", 0,
+ ir_printf("Cond %+F\n", cond);
+ );
+ ASSERT_AND_RET_DBG(ir_nodeset_contains(&env->false_projs, cond),
+ "Cond node lacks false proj", 0,
+ ir_printf("Cond %+F\n", cond);
+ );
+ return 1;
+}
+
+static int verify_switch_projs(const ir_node *sw, check_cfg_env_t *env)
+{
+ ASSERT_AND_RET_DBG(ir_nodeset_contains(&env->true_projs, sw),
+ "Switch node lacks default Proj", 0,
+ ir_printf("Switch %+F\n", sw);
+ );
return 1;
}
env->res &= verify_block_branch(node, env);
} else if (is_Cond(node)) {
env->res &= verify_cond_projs(node, env);
+ } else if (is_Switch(node)) {
+ env->res &= verify_switch_projs(node, env);
}
}
break
switch (code) {
- CASE(Proj);
+ CASE(Add);
+ CASE(Alloc);
+ CASE(And);
CASE(Block);
- CASE(Start);
- CASE(Jmp);
- CASE(IJmp);
+ CASE(Bound);
+ CASE(Call);
+ CASE(Cast);
+ CASE(Cmp);
CASE(Cond);
- CASE(Return);
- CASE(Raise);
+ CASE(Confirm);
CASE(Const);
- CASE(SymConst);
- CASE(Sel);
+ CASE(Conv);
+ CASE(CopyB);
+ CASE(Div);
+ CASE(Eor);
+ CASE(Free);
+ CASE(IJmp);
CASE(InstOf);
- CASE(Call);
- CASE(Add);
- CASE(Sub);
+ CASE(Jmp);
+ CASE(Load);
CASE(Minus);
+ CASE(Mod);
CASE(Mul);
CASE(Mulh);
- CASE(Div);
- CASE(Mod);
- CASE(And);
- CASE(Or);
- CASE(Eor);
+ CASE(Mux);
CASE(Not);
- CASE(Cmp);
+ CASE(Or);
+ CASE(Phi);
+ CASE(Proj);
+ CASE(Raise);
+ CASE(Return);
+ CASE(Rotl);
+ CASE(Sel);
CASE(Shl);
CASE(Shr);
CASE(Shrs);
- CASE(Rotl);
- CASE(Conv);
- CASE(Cast);
- CASE(Phi);
- CASE(Load);
+ CASE(Start);
CASE(Store);
- CASE(Alloc);
- CASE(Free);
+ CASE(Sub);
+ CASE(Switch);
+ CASE(SymConst);
CASE(Sync);
- CASE(Confirm);
- CASE(Mux);
- CASE(CopyB);
- CASE(Bound);
default:
break;
}
break
switch (code) {
- CASE(Start);
- CASE(Cond);
- CASE(Raise);
- CASE(InstOf);
+ CASE(Alloc);
+ CASE(Bound);
CASE(Call);
+ CASE(Cond);
+ CASE(CopyB);
CASE(Div);
- CASE(Mod);
+ CASE(InstOf);
CASE(Load);
- CASE(Store);
- CASE(Alloc);
+ CASE(Mod);
CASE(Proj);
+ CASE(Raise);
+ CASE(Start);
+ CASE(Store);
+ CASE(Switch);
CASE(Tuple);
- CASE(CopyB);
- CASE(Bound);
default:
break;
}
return get_edge_src_irn(first);
}
+static void lower_Switch(ir_node *node, ir_mode *high_mode)
+{
+ ir_node *selector = get_Switch_selector(node);
+ ir_mode *mode = get_irn_mode(selector);
+ (void)high_mode;
+ if (mode == env->high_signed || mode == env->high_unsigned) {
+ /* we can't really handle Switch with 64bit offsets */
+ panic("Switch with 64bit jumptable not supported");
+ }
+ lower_node(selector);
+}
+
/**
* Translate a Cond.
*/
{
ir_node *left, *right, *block;
ir_node *sel = get_Cond_selector(node);
- ir_mode *m = get_irn_mode(sel);
ir_mode *cmp_mode;
const lower64_entry_t *lentry, *rentry;
ir_node *projT = NULL, *projF = NULL;
(void) high_mode;
- if (m != mode_b) {
- if (m == env->high_signed || m == env->high_unsigned) {
- /* bad we can't really handle Switch with 64bit offsets */
- panic("Cond with 64bit jumptable not supported");
- }
- lower_node(sel);
- return;
- }
-
if (!is_Cmp(sel)) {
lower_node(sel);
return;
ir_register_dw_lower_function(op_Start, lower_Start);
ir_register_dw_lower_function(op_Store, lower_Store);
ir_register_dw_lower_function(op_Sub, lower_binop);
+ ir_register_dw_lower_function(op_Switch, lower_Switch);
ir_register_dw_lower_function(op_Unknown, lower_Unknown);
}
} walk_env_t;
typedef struct case_data_t {
- long value;
- ir_node *target;
+ const ir_switch_table_entry *entry;
+ ir_node *target;
} case_data_t;
-typedef struct cond_env_t {
- ir_node *sel;
- long switch_min;
- long switch_max;
- ir_node *default_block;
- unsigned num_cases;
- ir_node **defusers; /**< the Projs pointing to the default case */
-} cond_env_t;
+typedef struct switch_info_t {
+ ir_node *switchn;
+ long switch_min;
+ long switch_max;
+ ir_node *default_block;
+ unsigned num_cases;
+ case_data_t *cases;
+ ir_node **defusers; /**< the Projs pointing to the default case */
+} switch_info_t;
-static void analyse_switch(cond_env_t *env, ir_node *cond)
+/**
+ * analyze enough to decide if we should lower the switch
+ */
+static bool analyse_switch0(switch_info_t *info, ir_node *switchn)
{
- long default_pn = get_Cond_default_proj(cond);
- long switch_min = LONG_MAX;
- long switch_max = LONG_MIN;
- ir_node *default_block = NULL;
- unsigned num_cases = 0;
- int i;
- ir_node *proj;
-
- foreach_out_irn(cond, i, proj) {
- long pn = get_Proj_proj(proj);
- if (pn == default_pn) {
- ir_node *target = get_irn_out(proj, 0);
- assert(default_block == NULL || default_block == target);
- default_block = target;
+ const ir_switch_table *table = get_Switch_table(switchn);
+ size_t n_entries = ir_switch_table_get_n_entries(table);
+ long switch_min = LONG_MAX;
+ long switch_max = LONG_MIN;
+ unsigned num_cases = 0;
+ size_t e;
+
+ for (e = 0; e < n_entries; ++e) {
+ const ir_switch_table_entry *entry
+ = ir_switch_table_get_entry_const(table, e);
+ long minval;
+ long maxval;
+ if (entry->pn == 0)
continue;
- }
- if (pn < switch_min)
- switch_min = pn;
- if (pn > switch_max)
- switch_max = pn;
+ if (!tarval_is_long(entry->min) || !tarval_is_long(entry->max))
+ return false;
+ minval = get_tarval_long(entry->min);
+ maxval = get_tarval_long(entry->max);
+ if (minval < switch_min)
+ switch_min = minval;
+ if (maxval > switch_max)
+ switch_max = maxval;
++num_cases;
}
- assert(default_block != NULL);
- env->switch_min = switch_min;
- env->switch_max = switch_max;
- env->num_cases = num_cases;
- env->default_block = default_block;
+ info->switchn = switchn;
+ info->switch_min = switch_min;
+ info->switch_max = switch_max;
+ info->num_cases = num_cases;
+ return true;
}
static int casecmp(const void *a, const void *b)
{
- const case_data_t *cda = (const case_data_t*)a;
- const case_data_t *cdb = (const case_data_t*)b;
+ const case_data_t *cda = (const case_data_t*)a;
+ const case_data_t *cdb = (const case_data_t*)b;
+ const ir_switch_table_entry *ea = cda->entry;
+ const ir_switch_table_entry *eb = cdb->entry;
+
+ if (ea == eb)
+ return 0;
+
+ if (tarval_cmp(ea->max, eb->min) == ir_relation_less)
+ return -1;
+ /* cases must be non overlapping, so the only remaining case is greater */
+ assert(tarval_cmp(ea->min, eb->max) == ir_relation_greater);
+ return 1;
+}
+
+/**
+ * Analyse the stuff that anayse_switch0() left out
+ */
+static void analyse_switch1(switch_info_t *info)
+{
+ const ir_node *switchn = info->switchn;
+ const ir_switch_table *table = get_Switch_table(switchn);
+ size_t n_entries = ir_switch_table_get_n_entries(table);
+ unsigned n_outs = get_Switch_n_outs(switchn);
+ ir_node **targets = XMALLOCNZ(ir_node*, n_outs);
+ unsigned num_cases = info->num_cases;
+ case_data_t *cases = XMALLOCN(case_data_t, num_cases);
+ unsigned c = 0;
+ size_t e;
+ int i;
+ ir_node *proj;
+
+ foreach_out_irn(switchn, i, proj) {
+ long pn = get_Proj_proj(proj);
+ ir_node *target = get_irn_out(proj, 0);
+
+ assert((unsigned)pn < n_outs);
+ assert(targets[(unsigned)pn] == NULL);
+ targets[(unsigned)pn] = target;
+ }
+
+ for (e = 0; e < n_entries; ++e) {
+ const ir_switch_table_entry *entry
+ = ir_switch_table_get_entry_const(table, e);
+ if (entry->pn == 0)
+ continue;
+
+ cases[c].entry = entry;
+ cases[c].target = targets[entry->pn];
+ ++c;
+ }
+ assert(c == num_cases);
/*
- * Enforce unsigned sorting. Signed comparison will behave differently for
- * 32-bit values, depending on sizeof(long). This will make the resulting
- * array deterministic.
+ * Switch should be transformed into an if cascade.
+ * So first order the cases, so we can do a binary search on them.
*/
- return ((unsigned long)cda->value > (unsigned long)cdb->value) -
- ((unsigned long)cda->value < (unsigned long)cdb->value);
+ qsort(cases, num_cases, sizeof(cases[0]), casecmp);
+
+ info->default_block = targets[pn_Switch_default];
+ info->cases = cases;
+}
+
+static void normalize_table(ir_node *switchn, ir_mode *new_mode,
+ ir_tarval *delta)
+{
+ ir_switch_table *table = get_Switch_table(switchn);
+ size_t n_entries = ir_switch_table_get_n_entries(table);
+ size_t e;
+ /* adapt switch_table */
+ for (e = 0; e < n_entries; ++e) {
+ ir_switch_table_entry *entry = ir_switch_table_get_entry(table, e);
+ ir_tarval *min = entry->min;
+
+ if (entry->pn == 0)
+ continue;
+
+ min = tarval_convert_to(min, new_mode);
+ if (delta != NULL)
+ min = tarval_sub(min, delta, NULL);
+
+ if (entry->min == entry->max) {
+ entry->min = min;
+ entry->max = min;
+ } else {
+ ir_tarval *max = entry->max;
+ max = tarval_convert_to(max, new_mode);
+ if (delta != NULL)
+ max = tarval_sub(max, delta, NULL);
+ entry->min = min;
+ entry->max = max;
+ }
+ }
+}
+
+/**
+ * normalize switch to work on an unsigned input with the first case at 0
+ */
+static void normalize_switch(switch_info_t *info)
+{
+ ir_node *switchn = info->switchn;
+ ir_graph *irg = get_irn_irg(switchn);
+ ir_node *block = get_nodes_block(switchn);
+ ir_node *selector = get_Switch_selector(switchn);
+ ir_mode *mode = get_irn_mode(selector);
+ ir_tarval *delta = NULL;
+ bool change_mode = false;
+
+ if (mode_is_signed(mode)) {
+ mode = find_unsigned_mode(mode);
+ selector = new_r_Conv(block, selector, mode);
+ change_mode = true;
+ }
+
+ /* normalize so switch_min is at 0 */
+ if (info->switch_min != 0) {
+ dbg_info *dbgi = get_irn_dbg_info(switchn);
+ ir_node *min_const;
+
+ delta = new_tarval_from_long(info->switch_min, mode);
+
+ min_const = new_r_Const(irg, delta);
+ selector = new_rd_Sub(dbgi, block, selector, min_const, mode);
+
+ info->switch_min = 0;
+ info->switch_max -= info->switch_min;
+ }
+
+ if (delta != NULL || change_mode) {
+ set_Switch_selector(switchn, selector);
+ normalize_table(switchn, mode, delta);
+ }
+}
+
+/**
+ * Create an if (selector == caseval) Cond node (and handle the special case
+ * of ranged cases)
+ */
+static ir_node *create_case_cond(const ir_switch_table_entry *entry,
+ dbg_info *dbgi, ir_node *block,
+ ir_node *selector)
+{
+ ir_graph *irg = get_irn_irg(block);
+ ir_node *minconst = new_r_Const(irg, entry->min);
+ ir_node *cmp;
+
+ if (entry->min == entry->max) {
+ cmp = new_rd_Cmp(dbgi, block, selector, minconst, ir_relation_equal);
+ } else {
+ ir_tarval *adjusted_max = tarval_sub(entry->max, entry->min, NULL);
+ ir_node *sub = new_rd_Sub(dbgi, block, selector, minconst,
+ get_tarval_mode(adjusted_max));
+ ir_node *maxconst = new_r_Const(irg, adjusted_max);
+ cmp = new_rd_Cmp(dbgi, block, sub, maxconst, ir_relation_less_equal);
+ }
+
+ return new_rd_Cond(dbgi, block, cmp);
}
/**
* Creates an if cascade realizing binary search.
*/
-static void create_if_cascade(cond_env_t *env, dbg_info *dbgi, ir_node *block,
+static void create_if_cascade(switch_info_t *info, ir_node *block,
case_data_t *curcases, unsigned numcases)
{
- ir_graph *irg = get_irn_irg(block);
- ir_mode *cmp_mode;
- ir_node *cmp_sel;
- ir_node *sel_block;
-
- /* Get the mode and sel node for the comparison. */
- cmp_mode = get_irn_mode(env->sel);
- cmp_sel = env->sel;
- sel_block = get_nodes_block(cmp_sel);
-
- /*
- * Make sure that an unsigned comparison is used, by converting the sel
- * node to an unsigned mode and using that mode for the constants, too.
- * This is important, because the qsort applied to the case labels uses
- * an unsigned comparison and both comparison methods have to match.
- */
- if (mode_is_signed(cmp_mode)) {
- cmp_mode = find_unsigned_mode(cmp_mode);
- cmp_sel = new_r_Conv(sel_block, cmp_sel, cmp_mode);
- }
+ ir_graph *irg = get_irn_irg(block);
+ const ir_node *switchn = info->switchn;
+ dbg_info *dbgi = get_irn_dbg_info(switchn);
+ ir_node *selector = get_Switch_selector(switchn);
if (numcases == 0) {
/* zero cases: "goto default;" */
- ARR_APP1(ir_node*, env->defusers, new_r_Jmp(block));
+ ARR_APP1(ir_node*, info->defusers, new_r_Jmp(block));
} else if (numcases == 1) {
- /* only one case: "if (sel == val) goto target else goto default;" */
- ir_node *val = new_r_Const_long(irg, cmp_mode, curcases[0].value);
- ir_node *cmp = new_rd_Cmp(dbgi, block, cmp_sel, val,
- ir_relation_equal);
- ir_node *cond = new_rd_Cond(dbgi, block, cmp);
+ /*only one case: "if (sel == val) goto target else goto default;"*/
+ const ir_switch_table_entry *entry = curcases[0].entry;
+ ir_node *cond = create_case_cond(entry, dbgi, block, selector);
ir_node *trueproj = new_r_Proj(cond, mode_X, pn_Cond_true);
ir_node *falseproj = new_r_Proj(cond, mode_X, pn_Cond_false);
set_Block_cfgpred(curcases[0].target, 0, trueproj);
- ARR_APP1(ir_node*, env->defusers, falseproj);
+ ARR_APP1(ir_node*, info->defusers, falseproj);
} else if (numcases == 2) {
/* only two cases: "if (sel == val[0]) goto target[0];" */
- ir_node *val = new_r_Const_long(irg, cmp_mode, curcases[0].value);
- ir_node *cmp = new_rd_Cmp(dbgi, block, cmp_sel, val,
- ir_relation_equal);
- ir_node *cond = new_rd_Cond(dbgi, block, cmp);
+ const ir_switch_table_entry *entry0 = curcases[0].entry;
+ const ir_switch_table_entry *entry1 = curcases[1].entry;
+ ir_node *cond = create_case_cond(entry0, dbgi, block, selector);
ir_node *trueproj = new_r_Proj(cond, mode_X, pn_Cond_true);
ir_node *falseproj = new_r_Proj(cond, mode_X, pn_Cond_false);
ir_node *in[1];
neblock = new_r_Block(irg, 1, in);
/* second part: "else if (sel == val[1]) goto target[1] else goto default;" */
- val = new_r_Const_long(irg, cmp_mode, curcases[1].value);
- cmp = new_rd_Cmp(dbgi, neblock, cmp_sel, val, ir_relation_equal);
- cond = new_rd_Cond(dbgi, neblock, cmp);
+ cond = create_case_cond(entry1, dbgi, neblock, selector);
trueproj = new_r_Proj(cond, mode_X, pn_Cond_true);
falseproj = new_r_Proj(cond, mode_X, pn_Cond_false);
set_Block_cfgpred(curcases[1].target, 0, trueproj);
- ARR_APP1(ir_node*, env->defusers, falseproj);
+ ARR_APP1(ir_node*, info->defusers, falseproj);
} else {
/* recursive case: split cases in the middle */
- int midcase = numcases / 2;
- ir_node *val = new_r_Const_long(irg, cmp_mode,
- curcases[midcase].value);
- ir_node *cmp = new_rd_Cmp(dbgi, block, cmp_sel, val, ir_relation_less);
+ unsigned midcase = numcases / 2;
+ const ir_switch_table_entry *entry = curcases[midcase].entry;
+ ir_node *val = new_r_Const(irg, entry->min);
+ ir_node *cmp = new_rd_Cmp(dbgi, block, selector, val, ir_relation_less);
ir_node *cond = new_rd_Cond(dbgi, block, cmp);
ir_node *in[1];
ir_node *ltblock;
in[0] = new_r_Proj(cond, mode_X, pn_Cond_false);
geblock = new_r_Block(irg, 1, in);
- create_if_cascade(env, dbgi, ltblock, curcases, midcase);
- create_if_cascade(env, dbgi, geblock, curcases + midcase,
- numcases - midcase);
+ create_if_cascade(info, ltblock, curcases, midcase);
+ create_if_cascade(info, geblock, curcases + midcase, numcases - midcase);
}
}
-static void create_out_of_bounds_check(cond_env_t *env, ir_node *cond)
+static void create_out_of_bounds_check(switch_info_t *info)
{
- ir_graph *irg = get_irn_irg(cond);
- dbg_info *dbgi = get_irn_dbg_info(cond);
- ir_node *sel = get_Cond_selector(cond);
- ir_node *block = get_nodes_block(cond);
- ir_mode *cmp_mode = get_irn_mode(sel);
- ir_node **default_preds = NEW_ARR_F(ir_node*, 0);
- long default_pn = get_Cond_default_proj(cond);
- long delta = 0;
- ir_node *max_const;
- ir_node *proj_true;
- ir_node *proj_false;
- ir_node *cmp;
- ir_node *oob_cond;
- ir_node *in[1];
- ir_node *new_block;
- size_t n_default_preds;
- int i;
- ir_node *proj;
-
- if (mode_is_signed(cmp_mode)) {
- cmp_mode = find_unsigned_mode(cmp_mode);
- sel = new_r_Conv(block, sel, cmp_mode);
- }
-
- /* normalize so switch_min is at 0 */
- if (env->switch_min != 0) {
- ir_node *min_const = new_r_Const_long(irg, cmp_mode, env->switch_min);
- sel = new_rd_Sub(dbgi, block, sel, min_const, cmp_mode);
-
- delta = env->switch_min;
- env->switch_min = 0;
- env->switch_max -= delta;
- set_Cond_selector(cond, sel);
- }
+ ir_node *switchn = info->switchn;
+ ir_graph *irg = get_irn_irg(switchn);
+ dbg_info *dbgi = get_irn_dbg_info(switchn);
+ ir_node *selector = get_Switch_selector(switchn);
+ ir_node *block = get_nodes_block(switchn);
+ ir_mode *cmp_mode = get_irn_mode(selector);
+ ir_node **default_preds = NEW_ARR_F(ir_node*, 0);
+ ir_node *default_block = NULL;
+ ir_node *max_const;
+ ir_node *proj_true;
+ ir_node *proj_false;
+ ir_node *cmp;
+ ir_node *oob_cond;
+ ir_node *in[1];
+ ir_node *new_block;
+ int i;
+ ir_node *proj;
+ size_t n_default_preds;
+
+ assert(info->switch_min == 0);
/* check for out-of-bounds */
- max_const = new_r_Const_long(irg, cmp_mode, env->switch_max);
- cmp = new_rd_Cmp(dbgi, block, sel, max_const, ir_relation_less_equal);
+ max_const = new_r_Const_long(irg, cmp_mode, info->switch_max);
+ cmp = new_rd_Cmp(dbgi, block, selector, max_const, ir_relation_less_equal);
oob_cond = new_rd_Cond(dbgi, block, cmp);
proj_true = new_r_Proj(oob_cond, mode_X, pn_Cond_true);
proj_false = new_r_Proj(oob_cond, mode_X, pn_Cond_false);
ARR_APP1(ir_node*, default_preds, proj_false);
- /* create new block containing the cond */
+ /* create new block containing the switch */
in[0] = proj_true;
new_block = new_r_Block(irg, 1, in);
- set_nodes_block(cond, new_block);
-
- /* adapt projs */
- foreach_out_irn(cond, i, proj) {
- long pn = get_Proj_proj(proj);
- long new_pn = pn - delta;
- if (pn == default_pn) {
- set_Cond_default_proj(cond, new_pn);
+ set_nodes_block(switchn, new_block);
+
+ /* adjust projs */
+ foreach_out_irn(switchn, i, proj) {
+ long pn = get_Proj_proj(proj);
+ if (pn == pn_Switch_default) {
+ assert(default_block == NULL);
+ default_block = get_irn_out(proj, 0);
ARR_APP1(ir_node*, default_preds, proj);
}
-
- set_Proj_proj(proj, new_pn);
set_nodes_block(proj, new_block);
}
/* adapt default block */
n_default_preds = ARR_LEN(default_preds);
if (n_default_preds > 1) {
- size_t p;
-
/* create new intermediate blocks so we don't have critical edges */
+ size_t p;
for (p = 0; p < n_default_preds; ++p) {
ir_node *pred = default_preds[p];
ir_node *split_block;
default_preds[p] = new_r_Jmp(split_block);
}
}
- set_irn_in(env->default_block, n_default_preds, default_preds);
+ set_irn_in(default_block, n_default_preds, default_preds);
DEL_ARR_F(default_preds);
+
+ clear_irg_state(irg, IR_GRAPH_STATE_CONSISTENT_DOMINANCE
+ | IR_GRAPH_STATE_VALID_EXTENDED_BLOCKS);
}
/**
- * Block-Walker: searches for Cond nodes with a non-boolean mode
+ * Block-Walker: searches for Switch nodes
*/
-static void find_cond_nodes(ir_node *block, void *ctx)
+static void find_switch_nodes(ir_node *block, void *ctx)
{
- walk_env_t *env = (walk_env_t *)ctx;
- ir_node *projx;
- ir_node *cond;
- ir_node *sel;
- ir_mode *sel_mode;
- long default_pn;
- int i;
- unsigned j = 0;
- unsigned numcases;
- ir_node *proj;
- case_data_t *cases;
- ir_node *condblock;
- ir_node *defblock = NULL;
- dbg_info *dbgi;
- cond_env_t cond_env;
+ walk_env_t *env = (walk_env_t *)ctx;
+ ir_node *projx;
+ ir_node *switchn;
+ switch_info_t info;
unsigned long spare;
- bool lower_switch = false;
+ bool lower_switch = false;
+ bool could_analyze;
/* because we split critical blocks only blocks with 1 predecessors may
* contain Proj->Cond nodes */
return;
assert(get_irn_mode(projx) == mode_X);
- cond = get_Proj_pred(projx);
- if (!is_Cond(cond))
+ switchn = get_Proj_pred(projx);
+ if (!is_Switch(switchn))
return;
- sel = get_Cond_selector(cond);
- sel_mode = get_irn_mode(sel);
-
- if (sel_mode == mode_b)
+ if (ir_nodeset_contains(&env->processed, switchn))
return;
+ ir_nodeset_insert(&env->processed, switchn);
- if (ir_nodeset_contains(&env->processed, cond))
+ could_analyze = analyse_switch0(&info, switchn);
+ /* the code can't handle values which are not representable in the host */
+ if (!could_analyze) {
+ ir_fprintf(stderr, "libfirm warning: Couldn't analyse %+F (this could go wrong in the backend)\n", switchn);
return;
- ir_nodeset_insert(&env->processed, cond);
-
- /* the algorithms here don't work reliable for modes bigger than 32
- * since we operate with long numbers */
- assert(get_mode_size_bits(sel_mode) <= 32);
-
- analyse_switch(&cond_env, cond);
+ }
/*
* Here we have: num_cases and [switch_min, switch_max] interval.
* We do an if-cascade if there are too many spare numbers.
*/
- spare = (unsigned long) cond_env.switch_max
- - (unsigned long) cond_env.switch_min
- - (unsigned long) cond_env.num_cases + 1;
+ spare = (unsigned long) info.switch_max
+ - (unsigned long) info.switch_min
+ - (unsigned long) info.num_cases + 1;
lower_switch |= spare >= env->spare_size;
- lower_switch |= cond_env.num_cases <= env->small_switch;
+ lower_switch |= info.num_cases <= env->small_switch;
if (!lower_switch) {
/* we won't decompose the switch. But we might have to add
* out-of-bounds checking */
if (!env->allow_out_of_bounds) {
- create_out_of_bounds_check(&cond_env, cond);
- env->changed = true;
+ normalize_switch(&info);
+ create_out_of_bounds_check(&info);
}
return;
}
- /*
- * Switch should be transformed into an if cascade.
- * So first order the cases, so we can do a binary search on them.
- */
- env->changed = true;
-
- numcases = get_irn_n_outs(cond) - 1; /* does not contain default case */
- cases = XMALLOCN(case_data_t, numcases);
-
- default_pn = get_Cond_default_proj(cond);
- cond_env.sel = sel;
- cond_env.defusers = NEW_ARR_F(ir_node*, 0);
-
- foreach_out_irn(cond, i, proj) {
- long pn = get_Proj_proj(proj);
- ir_node *target = get_irn_out(proj, 0);
- assert(get_Block_n_cfgpreds(target) == 1);
-
- if (pn == default_pn) {
- defblock = target;
- continue;
- }
-
- cases[j].value = pn;
- cases[j].target = target;
- j++;
- }
- assert(j == numcases);
-
- if (defblock == NULL)
- panic("Switch %+F has no default proj", cond);
-
- qsort(cases, numcases, sizeof(cases[0]), casecmp);
+ normalize_switch(&info);
+ analyse_switch1(&info);
/* Now create the if cascade */
- condblock = get_nodes_block(cond);
- dbgi = get_irn_dbg_info(cond);
- create_if_cascade(&cond_env, dbgi, condblock, cases, numcases);
+ env->changed = true;
+ info.defusers = NEW_ARR_F(ir_node*, 0);
+ block = get_nodes_block(switchn);
+ create_if_cascade(&info, block, info.cases, info.num_cases);
/* Connect new default case users */
- set_irn_in(defblock, ARR_LEN(cond_env.defusers), cond_env.defusers);
+ set_irn_in(info.default_block, ARR_LEN(info.defusers), info.defusers);
- xfree(cases);
- DEL_ARR_F(cond_env.defusers);
+ DEL_ARR_F(info.defusers);
+ xfree(info.cases);
+ clear_irg_state(get_irn_irg(block), IR_GRAPH_STATE_NO_CRITICAL_EDGES
+ | IR_GRAPH_STATE_CONSISTENT_DOMINANCE
+ | IR_GRAPH_STATE_VALID_EXTENDED_BLOCKS);
}
void lower_switch(ir_graph *irg, unsigned small_switch, unsigned spare_size,
remove_critical_cf_edges(irg);
assure_irg_outs(irg);
- irg_block_walk_graph(irg, find_cond_nodes, NULL, &env);
+ irg_block_walk_graph(irg, find_switch_nodes, NULL, &env);
ir_nodeset_destroy(&env.processed);
-
- if (env.changed) {
- /* control flow changed */
- clear_irg_state(irg, IR_GRAPH_STATE_CONSISTENT_DOMINANCE
- | IR_GRAPH_STATE_VALID_EXTENDED_BLOCKS);
- }
}
*/
static void collect_nodes(ir_node *n, void *ctx)
{
- ir_node ***switch_conds = (ir_node***)ctx;
-
+ (void) ctx;
if (is_Phi(n)) {
/* Collect Phi nodes to compact ins along with block's ins. */
ir_node *block = get_nodes_block(n);
ir_node *pred = get_Proj_pred(n);
set_irn_link(n, get_irn_link(pred));
set_irn_link(pred, n);
- } else if (is_Cond(n) && is_switch_Cond(n)) {
- /* found a switch-Cond, collect */
- ARR_APP1(ir_node*, *switch_conds, n);
}
}
}
xfree(in);
}
-/**
- * Optimize table-switch Conds.
- *
- * @param cond the switch-Cond
- * @return true if the switch-Cond was optimized
- */
-static bool handle_switch_cond(ir_node *cond)
-{
- ir_node *sel = get_Cond_selector(cond);
- ir_node *proj1 = (ir_node*)get_irn_link(cond);
- ir_node *proj2 = (ir_node*)get_irn_link(proj1);
- ir_node *blk = get_nodes_block(cond);
-
- /* exactly 1 Proj on the Cond node: must be the defaultProj */
- if (proj2 == NULL) {
- ir_node *jmp = new_r_Jmp(blk);
- assert(get_Cond_default_proj(cond) == get_Proj_proj(proj1));
- /* convert it into a Jmp */
- exchange(proj1, jmp);
- return true;
- }
-
- /* handle Cond nodes with constant argument. In this case the localopt rules
- * should have killed all obviously impossible cases.
- * So the only case left to handle here is 1 defaultProj + 1 case
- * (this one case should be the one taken) */
- if (get_irn_link(proj2) == NULL) {
- ir_tarval *tv = value_of(sel);
-
- if (tv != tarval_bad) {
- /* we have a constant switch */
- long num = get_tarval_long(tv);
- long def_num = get_Cond_default_proj(cond);
- ir_graph *irg = get_irn_irg(cond);
- ir_node *bad = new_r_Bad(irg, mode_X);
-
- if (def_num == get_Proj_proj(proj1)) {
- /* first one is the defProj */
- if (num == get_Proj_proj(proj2)) {
- ir_node *jmp = new_r_Jmp(blk);
- exchange(proj2, jmp);
- exchange(proj1, bad);
- return true;
- }
- } else if (def_num == get_Proj_proj(proj2)) {
- /* second one is the defProj */
- if (num == get_Proj_proj(proj1)) {
- ir_node *jmp = new_r_Jmp(blk);
- exchange(proj1, jmp);
- exchange(proj2, bad);
- return true;
- }
- } else {
- /* neither: strange, Cond was not optimized so far */
- if (num == get_Proj_proj(proj1)) {
- ir_node *jmp = new_r_Jmp(blk);
- exchange(proj1, jmp);
- exchange(proj2, bad);
- return true;
- } else if (num == get_Proj_proj(proj2)) {
- ir_node *jmp = new_r_Jmp(blk);
- exchange(proj2, jmp);
- exchange(proj1, bad);
- return true;
- }
- }
- }
- }
- return false;
-}
-
/**
* Optimize boolean Conds, where true and false jump to the same block into a Jmp
* Block must contain no Phi nodes.
/* The switch Cond optimization might expose unreachable code, so we loop */
for (;;) {
- int length;
- ir_node **switch_conds = NULL;
bool changed = false;
assure_doms(irg);
* Finally it marks all blocks that do not contain useful
* computations, i.e., these blocks might be removed.
*/
- switch_conds = NEW_ARR_F(ir_node*, 0);
- irg_walk(end, clear_link_and_mark_blocks_removable, collect_nodes, &switch_conds);
-
- /* handle all collected switch-Conds */
- length = ARR_LEN(switch_conds);
- for (i = 0; i < length; ++i) {
- ir_node *cond = switch_conds[i];
- changed |= handle_switch_cond(cond);
- }
- DEL_ARR_F(switch_conds);
+ irg_walk(end, clear_link_and_mark_blocks_removable, collect_nodes, NULL);
if (!changed)
break;
if (y->on_cprop == 0) {
partition_t *Y = y->part;
ir_node *irn = y->node;
+ ir_node *skipped = skip_Proj(irn);
/* place Conds and all its Projs on the cprop_X list */
- if (is_Cond(skip_Proj(irn)))
+ if (is_Cond(skipped) || is_Switch(skipped))
list_add_tail(&y->cprop_list, &Y->cprop_X);
else
list_add_tail(&y->cprop_list, &Y->cprop);
if (node->type.tv == tarval_reachable)
return;
- if (get_irn_mode(sel) == mode_b) {
- /* an IF */
- if (pnc == pn_Cond_true) {
- if (selector->type.tv == tarval_b_false) {
- node->type.tv = tarval_unreachable;
- } else if (selector->type.tv == tarval_b_true) {
- node->type.tv = tarval_reachable;
- } else if (selector->type.tv == tarval_bottom) {
- node->type.tv = tarval_reachable;
- } else {
- assert(selector->type.tv == tarval_top);
- if (tarval_UNKNOWN == tarval_top) {
- /* any condition based on Top is "!=" */
- node->type.tv = tarval_unreachable;
- } else {
- node->type.tv = tarval_unreachable;
- }
- }
+ if (pnc == pn_Cond_true) {
+ if (selector->type.tv == tarval_b_false) {
+ node->type.tv = tarval_unreachable;
+ } else if (selector->type.tv == tarval_b_true) {
+ node->type.tv = tarval_reachable;
+ } else if (selector->type.tv == tarval_bottom) {
+ node->type.tv = tarval_reachable;
} else {
- assert(pnc == pn_Cond_false);
-
- if (selector->type.tv == tarval_b_false) {
- node->type.tv = tarval_reachable;
- } else if (selector->type.tv == tarval_b_true) {
+ assert(selector->type.tv == tarval_top);
+ if (tarval_UNKNOWN == tarval_top) {
+ /* any condition based on Top is "!=" */
node->type.tv = tarval_unreachable;
- } else if (selector->type.tv == tarval_bottom) {
- node->type.tv = tarval_reachable;
} else {
- assert(selector->type.tv == tarval_top);
- if (tarval_UNKNOWN == tarval_top) {
- /* any condition based on Top is "!=" */
- node->type.tv = tarval_reachable;
- } else {
- node->type.tv = tarval_unreachable;
- }
+ node->type.tv = tarval_unreachable;
}
}
} else {
- /* an SWITCH */
- if (selector->type.tv == tarval_bottom) {
+ assert(pnc == pn_Cond_false);
+
+ if (selector->type.tv == tarval_b_false) {
+ node->type.tv = tarval_reachable;
+ } else if (selector->type.tv == tarval_b_true) {
+ node->type.tv = tarval_unreachable;
+ } else if (selector->type.tv == tarval_bottom) {
node->type.tv = tarval_reachable;
- } else if (selector->type.tv == tarval_top) {
- if (tarval_UNKNOWN == tarval_top &&
- pnc == get_Cond_default_proj(cond)) {
- /* a switch based of Top is always "default" */
+ } else {
+ assert(selector->type.tv == tarval_top);
+ if (tarval_UNKNOWN == tarval_top) {
+ /* any condition based on Top is "!=" */
node->type.tv = tarval_reachable;
} else {
node->type.tv = tarval_unreachable;
}
+ }
+ }
+} /* compute_Proj_Cond */
+
+static void compute_Proj_Switch(node_t *node, ir_node *switchn)
+{
+ ir_node *proj = node->node;
+ long pnc = get_Proj_proj(proj);
+ ir_node *sel = get_Switch_selector(switchn);
+ node_t *selector = get_irn_node(sel);
+
+ /* see long comment in compute_Proj_Cond */
+ if (node->type.tv == tarval_reachable)
+ return;
+
+ if (selector->type.tv == tarval_bottom) {
+ node->type.tv = tarval_reachable;
+ } else if (selector->type.tv == tarval_top) {
+ if (tarval_UNKNOWN == tarval_top && pnc == pn_Switch_default) {
+ /* a switch based of Top is always "default" */
+ node->type.tv = tarval_reachable;
} else {
- long value = get_tarval_long(selector->type.tv);
- if (pnc == get_Cond_default_proj(cond)) {
- /* default switch, have to check ALL other cases */
- int i;
-
- for (i = get_irn_n_outs(cond) - 1; i >= 0; --i) {
- ir_node *succ = get_irn_out(cond, i);
-
- if (succ == proj)
- continue;
- if (value == get_Proj_proj(succ)) {
- /* we found a match, will NOT take the default case */
- node->type.tv = tarval_unreachable;
- return;
- }
+ node->type.tv = tarval_unreachable;
+ }
+ } else {
+ long value = get_tarval_long(selector->type.tv);
+ const ir_switch_table *table = get_Switch_table(switchn);
+ size_t n_entries = ir_switch_table_get_n_entries(table);
+ size_t e;
+
+ 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;
+ if (min == max) {
+ if (selector->type.tv == min) {
+ node->type.tv = entry->pn == pnc
+ ? tarval_reachable : tarval_unreachable;
+ return;
}
- /* all cases checked, no match, will take default case */
- node->type.tv = tarval_reachable;
} else {
- /* normal case */
- node->type.tv = value == pnc ? tarval_reachable : tarval_unreachable;
+ long minval = get_tarval_long(min);
+ long maxval = get_tarval_long(max);
+ if (minval <= value && value <= maxval) {
+ node->type.tv = entry->pn == pnc
+ ? tarval_reachable : tarval_unreachable;
+ return;
+ }
}
}
+
+ /* no entry matched: default */
+ node->type.tv
+ = pnc == pn_Switch_default ? tarval_reachable : tarval_unreachable;
}
-} /* compute_Proj_Cond */
+}
/**
- * (Re-)compute the type for a Proj-Node.
- *
- * @param node the node
- */
+* (Re-)compute the type for a Proj-Node.
+*
+* @param node the node
+*/
static void compute_Proj(node_t *node)
{
- ir_node *proj = node->node;
+ir_node *proj = node->node;
ir_mode *mode = get_irn_mode(proj);
node_t *block = get_irn_node(get_nodes_block(skip_Proj(proj)));
ir_node *pred = get_Proj_pred(proj);
node->type.tv = tarval_top;
return;
}
- if (get_irn_node(pred)->type.tv == tarval_top && !is_Cond(pred)) {
+ if (get_irn_node(pred)->type.tv == tarval_top && !is_Cond(pred) && !is_Switch(pred)) {
/* if the predecessor is Top, its Proj follow */
node->type.tv = tarval_top;
return;
case iro_Cond:
compute_Proj_Cond(node, pred);
return;
+ case iro_Switch:
+ compute_Proj_Switch(node, pred);
+ return;
default:
break;
}
/* leave or Jmp */
ir_node *cond = get_Proj_pred(irn);
- if (is_Cond(cond)) {
+ if (is_Cond(cond) || is_Switch(cond)) {
if (only_one_reachable_proj(cond)) {
ir_node *jmp = new_r_Jmp(block->node);
set_irn_node(jmp, node);
exchange(irn, jmp);
env->modified = 1;
} else {
- node_t *sel = get_irn_node(get_Cond_selector(cond));
- ir_tarval *tv = sel->type.tv;
-
- if (is_tarval(tv) && tarval_is_constant(tv)) {
- /* The selector is a constant, but more
- * than one output is active: An unoptimized
- * case found. */
- env->unopt_cf = 1;
+ if (is_Switch(cond)) {
+ node_t *sel = get_irn_node(get_Switch_selector(cond));
+ ir_tarval *tv = sel->type.tv;
+
+ if (is_tarval(tv) && tarval_is_constant(tv)) {
+ /* The selector is a constant, but more
+ * than one output is active: An unoptimized
+ * case found. */
+ env->unopt_cf = 1;
+ }
}
}
}
} else if (is_Cond(pred)) {
ir_node* const selector = get_Cond_selector(pred);
bitinfo* const b = get_bitinfo(selector);
- if (is_undefined(b)) {
+ if (is_undefined(b))
goto unreachable_X;
- } else if (get_irn_mode(selector) == mode_b) {
- if (b->z == b->o) {
- if ((b->z == t) == get_Proj_proj(irn)) {
- z = o = t;
- } else {
- z = o = f;
- }
+ if (b->z == b->o) {
+ if ((b->z == t) == get_Proj_proj(irn)) {
+ z = o = t;
} else {
- goto result_unknown_X;
+ z = o = f;
}
} else {
- long const val = get_Proj_proj(irn);
- if (val != get_Cond_default_proj(pred)) {
- ir_tarval* const tv = new_tarval_from_long(val, get_irn_mode(selector));
- if (!tarval_is_null(tarval_andnot(tv, b->z)) ||
- !tarval_is_null(tarval_andnot(b->o, tv))) {
- // At least one bit differs.
- z = o = f;
-#if 0 // TODO must handle default Proj
- } else if (b->z == b->o && b->z == tv) {
- z = o = t;
-#endif
- } else {
- goto result_unknown_X;
- }
- } else {
- goto cannot_analyse_X;
- }
+ goto result_unknown_X;
}
+ } else if (is_Switch(pred)) {
+ ir_node* const selector = get_Switch_selector(pred);
+ bitinfo* const b = get_bitinfo(selector);
+ if (is_undefined(b))
+ goto unreachable_X;
+ /* TODO */
+ goto cannot_analyse_X;
} else {
goto cannot_analyse_X;
}
}
/* ignore control flow */
mode = get_irn_mode(node);
- if (mode == mode_X || is_Cond(node))
+ if (mode == mode_X || is_Cond(node) || is_Switch(node))
continue;
#ifdef AVOID_PHIB
/* we may not copy mode_b nodes, because this could produce Phi with
ir_mode *mode;
mode = get_irn_mode(node);
- if (mode == mode_X || is_Cond(node))
+ if (mode == mode_X || is_Cond(node) || is_Switch(node))
continue;
#ifdef AVOID_PHIB
if (mode == mode_b)
assert(get_irn_mode(projx) == mode_X);
cond = get_Proj_pred(projx);
- if (!is_Cond(cond))
- return;
-
- selector = get_Cond_selector(cond);
/* TODO handle switch Conds */
- if (get_irn_mode(selector) != mode_b)
+ if (!is_Cond(cond))
return;
/* handle cases that can be immediately evaluated */
+ selector = get_Cond_selector(cond);
selector_evaluated = -1;
if (is_Cmp(selector)) {
ir_node *left = get_Cmp_left(selector);
pinned_init = "op_pin_state_pinned"
throws_init = "false"
attr_struct = "bound_attr"
- attrs_name = "bound"
class Builtin(Op):
"""performs a backend-specific builtin."""
attr_struct = "cmp_attr"
class Cond(Op):
- """Conditionally change control flow. There are two versions of this node:
-
- Boolean Cond:
+ """Conditionally change control flow.
Input: A value of mode_b
Output: A tuple of two control flows. The first is taken if the input is
false, the second if it is true.
-
- Switch Cond:
- Input: A value of mode_Iu
- Output: A tuple of n control flows. If the Cond's input is i, control flow
- will proceed along output i. If the input is >= n control flow proceeds
- along output def_proj.
"""
ins = [
("selector", "condition parameter"),
flags = [ "cfopcode", "forking" ]
pinned = "yes"
attrs = [
- dict(
- name = "default_proj",
- type = "long",
- init = "0",
- comment = "Proj-number of default case for switch-Cond",
- ),
dict(
name = "jmp_pred",
type = "cond_jmp_predicate",
init = "COND_JMP_PRED_NONE",
comment = "can indicate the most likely jump",
- )
+ ),
]
attr_struct = "cond_attr"
+class Switch(Op):
+ """Change control flow. The destination is choosen based on an integer input value which is looked up in a table.
+
+ Backends can implement this efficiently using a jump table."""
+ ins = [
+ ("selector", "input selector"),
+ ]
+ outs = [
+ ("default", "control flow if no other case matches"),
+ ]
+ flags = [ "cfopcode", "forking" ]
+ pinned = "yes"
+ attrs = [
+ dict(
+ name = "n_outs",
+ type = "unsigned",
+ comment = "number of outputs (including pn_Switch_default)",
+ ),
+ dict(
+ name = "table",
+ type = "ir_switch_table*",
+ comment = "table describing mapping from input values to Proj numbers",
+ ),
+ ]
+ attr_struct = "switch_attr"
+ attrs_name = "switcha"
+
class Confirm(Op):
"""Specifies constraints for a value. This allows explicit representation
of path-sensitive properties. (Example: This value is always >= 0 on 1
),
]
attr_struct = "confirm_attr"
- attrs_name = "confirm"
class Const(Op):
"""Returns a constant value."""
)
]
attr_struct = "conv_attr"
- attrs_name = "conv"
class CopyB(Op):
"""Copies a block of memory"""
)
]
attr_struct = "copyb_attr"
- attrs_name = "copyb"
pinned = "memory"
pinned_init = "op_pin_state_pinned"
throws_init = "false"
("X_except", "control flow when exception occured"),
]
flags = [ "fragile", "uses_memory" ]
- attrs_name = "div"
attrs = [
dict(
type = "ir_mode*",
("X_except", "control flow when exception occured"),
]
flags = [ "fragile", "uses_memory" ]
- attrs_name = "mod"
attrs = [
dict(
type = "ir_mode*",