sparc: new patterns to match comparisons of bitoperations with zero
[libfirm] / ir / be / sparc / sparc_emitter.c
index bffd3e3..07eef61 100644 (file)
@@ -109,17 +109,46 @@ static const arch_register_t *get_out_reg(const ir_node *node, int pos)
        return reg;
 }
 
+static bool is_valid_immediate(int32_t value)
+{
+       return -4096 <= value && value < 4096;
+}
+
 void sparc_emit_immediate(const ir_node *node)
 {
-       int32_t value = get_sparc_attr_const(node)->immediate_value;
-       assert(-4096 <= value && value < 4096);
-       be_emit_irprintf("%d", value);
+       const sparc_attr_t *attr   = get_sparc_attr_const(node);
+       ir_entity          *entity = attr->immediate_value_entity;
+
+       if (entity == NULL) {
+               int32_t value = attr->immediate_value;
+               assert(is_valid_immediate(value));
+               be_emit_irprintf("%d", value);
+       } else {
+               be_emit_cstring("%lo(");
+               be_gas_emit_entity(entity);
+               if (attr->immediate_value != 0) {
+                       be_emit_irprintf("%+d", attr->immediate_value);
+               }
+               be_emit_char(')');
+       }
 }
 
 void sparc_emit_high_immediate(const ir_node *node)
 {
-       uint32_t value = (uint32_t) get_sparc_attr_const(node)->immediate_value;
-       be_emit_irprintf("%%hi(0x%X)", value);
+       const sparc_attr_t *attr   = get_sparc_attr_const(node);
+       ir_entity          *entity = attr->immediate_value_entity;
+
+       be_emit_cstring("%hi(");
+       if (entity == NULL) {
+               uint32_t value = (uint32_t) attr->immediate_value;
+               be_emit_irprintf("0x%X", value);
+       } else {
+               be_gas_emit_entity(entity);
+               if (attr->immediate_value != 0) {
+                       be_emit_irprintf("%+d", attr->immediate_value);
+               }
+       }
+       be_emit_char(')');
 }
 
 void sparc_emit_source_register(const ir_node *node, int pos)
@@ -162,17 +191,30 @@ static bool is_stack_pointer_relative(const ir_node *node)
 /**
  * emit SP offset
  */
-void sparc_emit_offset(const ir_node *node)
+void sparc_emit_offset(const ir_node *node, int offset_node_pos)
 {
        const sparc_load_store_attr_t *attr = get_sparc_load_store_attr_const(node);
-       long                           offset = attr->offset;
-
-       /* bad hack: the real stack stuff is behind the always-there spill
-        * space for the register window and stack */
-       if (is_stack_pointer_relative(node))
-               offset += SPARC_MIN_STACKSIZE;
-       if (offset != 0) {
-               be_emit_irprintf("%+ld", offset);
+
+       if (attr->is_reg_reg) {
+               assert(!attr->is_frame_entity);
+               assert(attr->base.immediate_value == 0);
+               assert(attr->base.immediate_value_entity == NULL);
+               be_emit_char('+');
+               sparc_emit_source_register(node, offset_node_pos);
+       } else if (attr->is_frame_entity) {
+               int32_t offset = attr->base.immediate_value;
+               /* bad hack: the real stack stuff is behind the always-there spill
+                * space for the register window and stack */
+               if (is_stack_pointer_relative(node))
+                       offset += SPARC_MIN_STACKSIZE;
+               if (offset != 0) {
+                       assert(is_valid_immediate(offset));
+                       be_emit_irprintf("%+ld", offset);
+               }
+       } else if (attr->base.immediate_value != 0
+                       || attr->base.immediate_value_entity != NULL) {
+               be_emit_char('+');
+               sparc_emit_immediate(node);
        }
 }
 
@@ -280,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);
 }
 
@@ -358,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? */
@@ -370,14 +423,13 @@ 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");
        sparc_emit_source_register(node, 1);
        be_emit_cstring(", ");
-       sparc_emit_source_register(node, 2);
+       sparc_emit_reg_or_imm(node, 2);
        be_emit_cstring(", ");
        sparc_emit_dest_register(node, 0);
        be_emit_finish_line_gas(node);
@@ -385,11 +437,7 @@ static void emit_sparc_Div(const ir_node *node, bool is_signed)
 
 static void emit_sparc_SDiv(const ir_node *node)
 {
-       (void) node;
-       /* aehm we would need an aditional register for an sra instruction to
-        * compute the upper bits... Just panic for now */
-       //emit_sparc_Div(node, true);
-       panic("signed div is wrong");
+       emit_sparc_Div(node, true);
 }
 
 static void emit_sparc_UDiv(const ir_node *node)
@@ -420,6 +468,9 @@ static void emit_sparc_Call(const ir_node *node)
        be_emit_cstring("\tcall ");
        if (entity != NULL) {
            sparc_emit_entity(entity);
+           if (attr->immediate_value != 0) {
+                       be_emit_irprintf("%+d", attr->immediate_value);
+               }
                be_emit_cstring(", 0");
        } else {
                int last = get_irn_arity(node);
@@ -427,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();
 }
 
 /**
@@ -521,55 +570,29 @@ static void emit_be_MemPerm(const ir_node *node)
        assert(sp_change == 0);
 }
 
-/**
- * Emit a SymConst.
- */
-static void emit_sparc_SymConst(const ir_node *irn)
-{
-       const sparc_symconst_attr_t *attr = get_sparc_symconst_attr_const(irn);
-
-       //sethi %hi(const32),%reg
-       //or    %reg,%lo(const32),%reg
-
-       be_emit_cstring("\tsethi %hi(");
-       be_gas_emit_entity(attr->entity);
-       be_emit_cstring("), ");
-       sparc_emit_dest_register(irn, 0);
-       be_emit_cstring("\n ");
-
-       // TODO: could be combined with the following load/store instruction
-       be_emit_cstring("\tor ");
-       sparc_emit_dest_register(irn, 0);
-       be_emit_cstring(", %lo(");
-       be_gas_emit_entity(attr->entity);
-       be_emit_cstring("), ");
-       sparc_emit_dest_register(irn, 0);
-       be_emit_finish_line_gas(irn);
-}
-
 /**
  * Emits code for FrameAddr fix
  */
-static void emit_sparc_FrameAddr(const ir_node *irn)
+static void emit_sparc_FrameAddr(const ir_node *node)
 {
-       const sparc_symconst_attr_t *attr = get_irn_generic_attr_const(irn);
+       const sparc_attr_t *attr = get_sparc_attr_const(node);
 
        // no need to fix offset as we are adressing via the framepointer
-       if (attr->fp_offset >= 0) {
+       if (attr->immediate_value >= 0) {
                be_emit_cstring("\tadd ");
-               sparc_emit_source_register(irn, 0);
+               sparc_emit_source_register(node, 0);
                be_emit_cstring(", ");
-               be_emit_irprintf("%ld", attr->fp_offset);
+               be_emit_irprintf("%ld", attr->immediate_value);
        } else {
                be_emit_cstring("\tsub ");
-               sparc_emit_source_register(irn, 0);
+               sparc_emit_source_register(node, 0);
                be_emit_cstring(", ");
-               be_emit_irprintf("%ld", -attr->fp_offset);
+               be_emit_irprintf("%ld", -attr->immediate_value);
        }
 
        be_emit_cstring(", ");
-       sparc_emit_dest_register(irn, 0);
-       be_emit_finish_line_gas(irn);
+       sparc_emit_dest_register(node, 0);
+       be_emit_finish_line_gas(node);
 }
 
 static const char *get_icc_unsigned(pn_Cmp pnc)
@@ -678,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 ");
@@ -691,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();
        }
 }
 
@@ -724,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);
@@ -733,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)
 {
@@ -833,7 +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_SymConst,  emit_sparc_SymConst);
+       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 */