NULL, /* perform_memory_operand */
};
-
-
/**
* Transforms the standard firm graph into
* a SPARC firm graph
emit_fp_suffix(attr->fp_mode);
}
+static ir_node *get_jump_target(const ir_node *jump)
+{
+ return get_irn_link(jump);
+}
+
/**
* Returns the target label for a control flow node.
*/
static void sparc_emit_cfop_target(const ir_node *node)
{
- ir_node *block = get_irn_link(node);
+ ir_node *block = get_jump_target(node);
be_gas_emit_block_name(block);
}
be_emit_finish_line_gas(irn);
}
+static void fill_delay_slot(void)
+{
+ be_emit_cstring("\tnop\n");
+ be_emit_write_line();
+}
+
static void emit_sparc_Div(const ir_node *node, bool is_signed)
{
/* can we get the delay count of the wr instruction somewhere? */
be_emit_finish_line_gas(node);
for (i = 0; i < wry_delay_count; ++i) {
- be_emit_cstring("\tnop");
- be_emit_finish_line_gas(node);
+ fill_delay_slot();
}
be_emit_irprintf("\t%s ", is_signed ? "sdiv" : "udiv");
}
be_emit_finish_line_gas(node);
- /* fill delay slot */
- be_emit_cstring("\tnop");
- be_emit_finish_line_gas(node);
+ fill_delay_slot();
}
/**
sparc_emit_cfop_target(proj_true);
be_emit_finish_line_gas(proj_true);
- be_emit_cstring("\tnop");
- be_emit_pad_comment();
- be_emit_cstring("/* TODO: use delay slot */\n");
+ fill_delay_slot();
if (get_irn_link(proj_false) == next_block) {
be_emit_cstring("\t/* fallthrough to ");
be_emit_cstring("\tba ");
sparc_emit_cfop_target(proj_false);
be_emit_finish_line_gas(proj_false);
- be_emit_cstring("\tnop\t\t/* TODO: use delay slot */\n");
- be_emit_finish_line_gas(proj_false);
+ fill_delay_slot();
}
}
be_emit_cstring("\tba ");
sparc_emit_cfop_target(node);
be_emit_finish_line_gas(node);
- be_emit_cstring("\tnop\t\t/* TODO: use delay slot */\n");
+ fill_delay_slot();
} else {
be_emit_cstring("\t/* fallthrough to ");
sparc_emit_cfop_target(node);
be_emit_finish_line_gas(node);
}
+static void emit_jump_table(const ir_node *node)
+{
+ const sparc_switch_jmp_attr_t *attr = get_sparc_switch_jmp_attr_const(node);
+ long switch_min = LONG_MAX;
+ long switch_max = LONG_MIN;
+ long default_pn = attr->default_proj_num;
+ ir_entity *entity = attr->jump_table;
+ ir_node *default_block = NULL;
+ unsigned length;
+ const ir_edge_t *edge;
+ unsigned i;
+ ir_node **table;
+
+ /* go over all proj's and collect them */
+ foreach_out_edge(node, edge) {
+ ir_node *proj = get_edge_src_irn(edge);
+ long pn = get_Proj_proj(proj);
+
+ /* check for default proj */
+ if (pn == default_pn) {
+ assert(default_block == NULL); /* more than 1 default_pn? */
+ default_block = get_jump_target(proj);
+ } else {
+ switch_min = pn < switch_min ? pn : switch_min;
+ switch_max = pn > switch_max ? pn : switch_max;
+ }
+ }
+ length = (unsigned long) (switch_max - switch_min) + 1;
+ assert(switch_min < LONG_MAX || switch_max > LONG_MIN);
+
+ 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 - switch_min] = get_jump_target(proj);
+ }
+
+ /* emit table */
+ be_gas_emit_switch_section(GAS_SECTION_RODATA);
+ be_emit_cstring("\t.align 4\n");
+ be_gas_emit_entity(entity);
+ be_emit_cstring(":\n");
+ for (i = 0; i < length; ++i) {
+ ir_node *block = table[i];
+ if (block == NULL)
+ block = default_block;
+ be_emit_cstring("\t.long ");
+ be_gas_emit_block_name(block);
+ be_emit_char('\n');
+ be_emit_write_line();
+ }
+ be_gas_emit_switch_section(GAS_SECTION_TEXT);
+
+ xfree(table);
+}
+
+static void emit_sparc_SwitchJmp(const ir_node *node)
+{
+ be_emit_cstring("\tjmp ");
+ sparc_emit_source_register(node, 0);
+ be_emit_finish_line_gas(node);
+ fill_delay_slot();
+
+ emit_jump_table(node);
+}
+
static void emit_fmov(const ir_node *node, const arch_register_t *src_reg,
const arch_register_t *dst_reg)
{
set_emitter(op_sparc_Mulh, emit_sparc_Mulh);
set_emitter(op_sparc_Save, emit_sparc_Save);
set_emitter(op_sparc_SDiv, emit_sparc_SDiv);
+ set_emitter(op_sparc_SwitchJmp, emit_sparc_SwitchJmp);
set_emitter(op_sparc_UDiv, emit_sparc_UDiv);
/* no need to emit anything for the following nodes */
return is_sparc_Bicc(node) || is_sparc_fbfcc(node);
}
-static bool has_jmp_switch_attr(const ir_node *node)
+static bool has_switch_jmp_attr(const ir_node *node)
{
return is_sparc_SwitchJmp(node);
}
fprintf(F, "pnc: %d (%s)\n", attr->pnc, get_pnc_string(attr->pnc));
fprintf(F, "unsigned: %s\n", attr->is_unsigned ? "true" : "false");
}
- if (has_jmp_switch_attr(n)) {
- const sparc_jmp_switch_attr_t *attr
- = get_sparc_jmp_switch_attr_const(n);
- fprintf(F, "n projs: %d\n", attr->n_projs);
+ 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)) {
attr->is_unsigned = is_unsigned;
}
-void set_sparc_jmp_switch_n_projs(ir_node *node, int n_projs)
-{
- sparc_jmp_switch_attr_t *attr = get_sparc_jmp_switch_attr(node);
- attr->n_projs = n_projs;
-}
-
-void set_sparc_jmp_switch_default_proj_num(ir_node *node, long def_proj_num)
-{
- sparc_jmp_switch_attr_t *attr = get_sparc_jmp_switch_attr(node);
- attr->default_proj_num = def_proj_num;
-}
-
-
-
-int get_sparc_jmp_switch_n_projs(const ir_node *node)
-{
- const sparc_jmp_switch_attr_t *attr = get_sparc_jmp_switch_attr_const(node);
- return attr->n_projs;
-}
-
-long get_sparc_jmp_switch_default_proj_num(const ir_node *node)
-{
- const sparc_jmp_switch_attr_t *attr = get_sparc_jmp_switch_attr_const(node);
- return attr->default_proj_num;
-}
-
sparc_attr_t *get_sparc_attr(ir_node *node)
{
assert(is_sparc_irn(node));
return (const sparc_jmp_cond_attr_t*) get_irn_generic_attr_const(node);
}
-sparc_jmp_switch_attr_t *get_sparc_jmp_switch_attr(ir_node *node)
+sparc_switch_jmp_attr_t *get_sparc_switch_jmp_attr(ir_node *node)
{
- assert(has_jmp_switch_attr(node));
- return (sparc_jmp_switch_attr_t*) get_irn_generic_attr_const(node);
+ assert(has_switch_jmp_attr(node));
+ return (sparc_switch_jmp_attr_t*) get_irn_generic_attr_const(node);
}
-const sparc_jmp_switch_attr_t *get_sparc_jmp_switch_attr_const(const ir_node *node)
+const sparc_switch_jmp_attr_t *get_sparc_switch_jmp_attr_const(const ir_node *node)
{
- assert(has_jmp_switch_attr(node));
- return (const sparc_jmp_switch_attr_t*) get_irn_generic_attr_const(node);
+ assert(has_switch_jmp_attr(node));
+ return (const sparc_switch_jmp_attr_t*) get_irn_generic_attr_const(node);
}
sparc_save_attr_t *get_sparc_save_attr(ir_node *node)
attr->dest_mode = dest_mode;
}
+static void init_sparc_switch_jmp_attributes(ir_node *res, long default_pn,
+ ir_entity *jump_table)
+{
+ sparc_switch_jmp_attr_t *attr = get_sparc_switch_jmp_attr(res);
+ attr->default_proj_num = default_pn;
+ attr->jump_table = jump_table;
+}
+
/**
* copies sparc attributes of node
*/
|| attr_a->is_unsigned != attr_b->is_unsigned;
}
-static int cmp_attr_sparc_jmp_switch(ir_node *a, ir_node *b)
+static int cmp_attr_sparc_switch_jmp(ir_node *a, ir_node *b)
{
- const sparc_jmp_switch_attr_t *attr_a = get_sparc_jmp_switch_attr_const(a);
- const sparc_jmp_switch_attr_t *attr_b = get_sparc_jmp_switch_attr_const(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
- || attr_a->n_projs != attr_b->n_projs;
+ return attr_a->default_proj_num != attr_b->default_proj_num;
}
static int cmp_attr_sparc_save(ir_node *a, ir_node *b)
sparc_jmp_cond_attr_t *get_sparc_jmp_cond_attr(ir_node *node);
const sparc_jmp_cond_attr_t *get_sparc_jmp_cond_attr_const(const ir_node *node);
-sparc_jmp_switch_attr_t *get_sparc_jmp_switch_attr(ir_node *node);
-const sparc_jmp_switch_attr_t *get_sparc_jmp_switch_attr_const(const ir_node *node);
+sparc_switch_jmp_attr_t *get_sparc_switch_jmp_attr(ir_node *node);
+const sparc_switch_jmp_attr_t *get_sparc_switch_jmp_attr_const(const ir_node *node);
sparc_save_attr_t *get_sparc_save_attr(ir_node *node);
const sparc_save_attr_t *get_sparc_save_attr_const(const ir_node *node);
*/
void set_sparc_req_in(ir_node *node, const arch_register_req_t *req, int pos);
-/**
- * Returns the number of projs of a SwitchJmp.
- */
-int get_sparc_jmp_switch_n_projs(const ir_node *node);
-
-/**
- * Sets the number of projs of a SwitchJmp.
- */
-void set_sparc_jmp_switch_n_projs(ir_node *node, int n_projs);
-
-/**
- * Returns the default_proj_num.
- */
-long get_sparc_jmp_switch_default_proj_num(const ir_node *node);
-
-/**
- * Sets the default_proj_num.
- */
-void set_sparc_jmp_switch_default_proj_num(ir_node *node, long default_proj_num);
-
/* Include the generated headers */
#include "gen_sparc_new_nodes.h"
/**
* attributes for switch jumps
*/
-typedef struct sparc_jmp_switch_attr_t sparc_jmp_switch_attr_t;
-struct sparc_jmp_switch_attr_t {
+typedef struct sparc_switch_jmp_attr_t sparc_switch_jmp_attr_t;
+struct sparc_switch_jmp_attr_t {
sparc_attr_t base; /**< generic attribute */
- int n_projs;
long default_proj_num;
+ ir_entity *jump_table;
};
#endif
sparc_attr_t => "\tinit_sparc_attributes(res, flags, in_reqs, exec_units, n_res);",
sparc_load_store_attr_t => "\tinit_sparc_attributes(res, flags, in_reqs, exec_units, n_res);",
sparc_jmp_cond_attr_t => "\tinit_sparc_attributes(res, flags, in_reqs, exec_units, n_res);",
- sparc_jmp_switch_attr_t => "\tinit_sparc_attributes(res, flags, in_reqs, exec_units, n_res);",
+ sparc_switch_jmp_attr_t => "\tinit_sparc_attributes(res, flags, in_reqs, exec_units, n_res);\n".
+ "\tinit_sparc_switch_jmp_attributes(res, default_pn, jump_table);\n",
sparc_save_attr_t => "\tinit_sparc_attributes(res, flags, in_reqs, exec_units, n_res);",
sparc_fp_attr_t => "\tinit_sparc_attributes(res, flags, in_reqs, exec_units, n_res);\n".
"\tinit_sparc_fp_attributes(res, fp_mode);\n",
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_jmp_switch_attr_t => "cmp_attr_sparc_jmp_switch",
+ sparc_switch_jmp_attr_t => "cmp_attr_sparc_switch_jmp",
sparc_save_attr_t => "cmp_attr_sparc_save",
sparc_fp_attr_t => "cmp_attr_sparc_fp",
sparc_fp_conv_attr_t => "cmp_attr_sparc_fp_conv",
},
);
-my %unop_operand_constructors = (
- imm => {
- attr => "ir_entity *immediate_entity, int32_t immediate_value",
- custominit => "sparc_set_attr_imm(res, immediate_entity, immediate_value);",
- reg_req => { in => [], out => [ "gp" ] },
- },
- reg => {
- reg_req => { in => [ "gp" ], out => [ "gp" ] },
- },
-);
-
my %binop_operand_constructors = (
imm => {
attr => "ir_entity *immediate_entity, int32_t immediate_value",
},
SwitchJmp => {
- op_flags => [ "labeled", "cfopcode", "forking" ],
- irn_flags => [ "modifies_flags" ],
- state => "pinned",
- mode => "mode_T",
- attr => "int n_projs, long def_proj_num",
- init_attr => "\tset_sparc_jmp_switch_n_projs(res, n_projs);\n".
- "\tset_sparc_jmp_switch_default_proj_num(res, def_proj_num);",
- reg_req => { in => [ "gp" ], out => [ "none" ] },
- attr_type => "sparc_jmp_switch_attr_t",
+ op_flags => [ "labeled", "cfopcode", "forking" ],
+ state => "pinned",
+ mode => "mode_T",
+ reg_req => { in => [ "gp" ], out => [ ] },
+ 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
},
Sll => {
return get_irn_mode(op);
}
-/**
- * Transform Cond nodes
- */
+static ir_node *make_address(dbg_info *dbgi, ir_node *block, ir_entity *entity,
+ int32_t offset)
+{
+ ir_node *hi = new_bd_sparc_SetHi(dbgi, block, entity, offset);
+ ir_node *low = new_bd_sparc_Or_imm(dbgi, block, hi, entity, offset);
+ be_dep_on_frame(hi);
+ return low;
+}
+
+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 switch_min = LONG_MAX;
+ long switch_max = LONG_MIN;
+ long default_pn = get_Cond_default_proj(node);
+ ir_entity *entity;
+ ir_node *table_address;
+ ir_node *index;
+ ir_node *load;
+ ir_node *address;
+ unsigned length;
+ const ir_edge_t *edge;
+
+ /* switch with smaller mode not implemented yet */
+ assert(get_mode_size_bits(get_irn_mode(selector)) == 32);
+
+ foreach_out_edge(node, edge) {
+ ir_node *proj = get_edge_src_irn(edge);
+ long pn = get_Proj_proj(proj);
+ if (pn == default_pn)
+ continue;
+
+ switch_min = pn<switch_min ? pn : switch_min;
+ switch_max = pn>switch_max ? pn : switch_max;
+ }
+ length = (unsigned long) (switch_max - switch_min);
+ if (length > 16000) {
+ panic("Size of switch %+F bigger than 16000", node);
+ }
+
+ 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: this code does not construct code to check for access
+ * out-of bounds of the jumptable yet. I think we should put this stuff
+ * into the switch_lowering phase to get some additional optimisations
+ * done. */
+
+ /* construct base address */
+ table_address = make_address(dbgi, block, entity,
+ -switch_min * get_mode_size_bytes(mode_gp));
+ /* scale index */
+ index = new_bd_sparc_Sll_imm(dbgi, block, new_selector, NULL, 2);
+ /* load from jumptable */
+ load = new_bd_sparc_Ld_reg(dbgi, block, table_address, index, new_NoMem(),
+ mode_gp);
+ address = new_r_Proj(load, mode_gp, pn_sparc_Ld_res);
+
+ return new_bd_sparc_SwitchJmp(dbgi, block, address, default_pn, entity);
+}
+
static ir_node *gen_Cond(ir_node *node)
{
ir_node *selector = get_Cond_selector(node);
// switch/case jumps
if (mode != mode_b) {
- panic("SwitchJump not implemented yet");
+ return gen_SwitchJmp(node);
}
// regular if/else jumps
dbg_info *dbgi = get_irn_dbg_info(node);
ir_node *block = get_nodes_block(node);
ir_node *new_block = be_transform_node(block);
- ir_node *hi = new_bd_sparc_SetHi(dbgi, new_block, entity, 0);
- ir_node *low = new_bd_sparc_Or_imm(dbgi, new_block, hi, entity, 0);
- be_dep_on_frame(hi);
-
- return low;
+ return make_address(dbgi, new_block, entity, 0);
}
static ir_node *create_fftof(dbg_info *dbgi, ir_node *block, ir_node *op,