sparc: preliminary SwitchJmp implementation
authorMatthias Braun <matze@braunis.de>
Thu, 19 Aug 2010 15:56:41 +0000 (15:56 +0000)
committerMatthias Braun <matze@braunis.de>
Thu, 19 Aug 2010 15:56:41 +0000 (15:56 +0000)
[r27957]

ir/be/sparc/bearch_sparc.c
ir/be/sparc/sparc_emitter.c
ir/be/sparc/sparc_new_nodes.c
ir/be/sparc/sparc_new_nodes.h
ir/be/sparc/sparc_nodes_attr.h
ir/be/sparc/sparc_spec.pl
ir/be/sparc/sparc_transform.c

index 56e4a7d..fb14bae 100644 (file)
@@ -131,8 +131,6 @@ static const arch_irn_ops_t sparc_irn_ops = {
        NULL,    /* perform_memory_operand  */
 };
 
-
-
 /**
  * Transforms the standard firm graph into
  * a SPARC firm graph
index a2fffbe..07eef61 100644 (file)
@@ -322,12 +322,17 @@ void sparc_emit_fp_mode_suffix(const ir_node *node)
        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);
 }
 
@@ -400,6 +405,12 @@ static void emit_sparc_Mulh(const ir_node *irn)
        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? */
@@ -412,8 +423,7 @@ static void emit_sparc_Div(const ir_node *node, bool is_signed)
        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");
@@ -468,9 +478,7 @@ static void emit_sparc_Call(const ir_node *node)
        }
        be_emit_finish_line_gas(node);
 
-       /* fill delay slot */
-       be_emit_cstring("\tnop");
-       be_emit_finish_line_gas(node);
+       fill_delay_slot();
 }
 
 /**
@@ -693,9 +701,7 @@ static void emit_sparc_branch(const ir_node *node, get_cc_func get_cc)
        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 ");
@@ -706,8 +712,7 @@ static void emit_sparc_branch(const ir_node *node, get_cc_func get_cc)
                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();
        }
 }
 
@@ -739,7 +744,7 @@ static void emit_sparc_Ba(const ir_node *node)
                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);
@@ -748,6 +753,75 @@ static void emit_sparc_Ba(const ir_node *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)
 {
@@ -848,6 +922,7 @@ static void sparc_register_emitters(void)
        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 */
index f42b777..b48240f 100644 (file)
@@ -55,7 +55,7 @@ static bool has_jmp_cond_attr(const ir_node *node)
        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);
 }
@@ -121,10 +121,9 @@ static void sparc_dump_node(FILE *F, ir_node *n, dump_reason_t reason)
                        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)) {
@@ -158,32 +157,6 @@ static void init_sparc_jmp_cond_attr(ir_node *node, int pnc, bool is_unsigned)
        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));
@@ -220,16 +193,16 @@ const sparc_jmp_cond_attr_t *get_sparc_jmp_cond_attr_const(const ir_node *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)
@@ -356,6 +329,14 @@ static void init_sparc_fp_conv_attributes(ir_node *res, ir_mode *src_mode,
        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
  */
@@ -411,16 +392,15 @@ static int cmp_attr_sparc_jmp_cond(ir_node *a, ir_node *b)
            || 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)
index 0647fc8..fa5e66c 100644 (file)
@@ -41,8 +41,8 @@ const sparc_load_store_attr_t *get_sparc_load_store_attr_const(const ir_node *no
 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);
@@ -70,26 +70,6 @@ const arch_register_req_t *get_sparc_in_req(const ir_node *node, int pos);
  */
 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"
 
index b08d070..8a82bf4 100644 (file)
@@ -96,11 +96,11 @@ struct sparc_jmp_cond_attr_t {
 /**
  * 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
index 4df0919..fae0740 100644 (file)
@@ -149,7 +149,8 @@ $default_copy_attr = "sparc_copy_attr";
        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",
@@ -161,7 +162,7 @@ $default_copy_attr = "sparc_copy_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_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",
@@ -188,17 +189,6 @@ my %cmp_operand_constructors = (
        },
 );
 
-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",
@@ -429,15 +419,13 @@ Tst => {
 },
 
 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 => {
index 3725174..6e16f99 100644 (file)
@@ -850,9 +850,71 @@ static ir_mode *get_cmp_mode(ir_node *b_value)
        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);
@@ -866,7 +928,7 @@ static ir_node *gen_Cond(ir_node *node)
 
        // switch/case jumps
        if (mode != mode_b) {
-               panic("SwitchJump not implemented yet");
+               return gen_SwitchJmp(node);
        }
 
        // regular if/else jumps
@@ -929,11 +991,7 @@ static ir_node *gen_SymConst(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_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,