redo reverted changes to new set_transformer interface in sparc_transform.c
[libfirm] / ir / be / sparc / sparc_emitter.c
index 53585b1..5c553b4 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 1995-2008 University of Karlsruhe.  All right reserved.
+ * Copyright (C) 1995-2010 University of Karlsruhe.  All right reserved.
  *
  * This file is part of libFirm.
  *
@@ -20,7 +20,7 @@
 /**
  * @file
  * @brief   emit assembler for a backend graph
- * @version $Id: sparc_emitter.c 26542 2009-09-18 09:18:32Z matze $
+ * @version $Id$
  */
 #include "config.h"
 
 #include "gen_sparc_emitter.h"
 #include "sparc_nodes_attr.h"
 #include "sparc_new_nodes.h"
+#include "gen_sparc_regalloc_if.h"
+
 
 #define SNPRINTF_BUF_LEN 128
 DEBUG_ONLY(static firm_dbg_module_t *dbg = NULL;)
 
+/**
+ * attribute of SAVE node which follows immediatelly after the START node
+ * we need this to correct all offsets since SPARC expects
+ * some reserved stack space after the stackpointer
+ */
+const sparc_save_attr_t *save_attr;
+
 /**
  * Returns the register at in position pos.
  */
@@ -122,8 +131,8 @@ static const arch_register_t *get_out_reg(const ir_node *node, int pos)
 
 void sparc_emit_immediate(const ir_node *node)
 {
-       // TODO: make sure it's a valid simm13 ?
        const sparc_attr_t *attr = get_sparc_attr_const(node);
+       assert(!(attr->immediate_value < -4096 || attr->immediate_value >= 4096));
        be_emit_irprintf("%d", attr->immediate_value);
 }
 
@@ -164,10 +173,12 @@ void sparc_emit_offset(const ir_node *node)
 {
        const sparc_load_store_attr_t *attr = get_sparc_load_store_attr_const(node);
        assert(attr->base.is_load_store);
+
        if (attr->offset > 0)
-               be_emit_irprintf("+0x%X", attr->offset);
+               be_emit_irprintf("+%ld", attr->offset);
 }
 
+
 /**
  *  Emit load mode char
  */
@@ -209,6 +220,75 @@ void sparc_emit_store_mode(const ir_node *node)
     }
 }
 
+/**
+ * emit integer signed/unsigned prefix char
+ */
+void sparc_emit_mode_sign_prefix(const ir_node *node)
+{
+    ir_mode *mode      = get_irn_mode(node);
+    bool     is_signed = mode_is_signed(mode);
+    be_emit_string(is_signed ? "s" : "u");
+}
+
+/**
+ * emit FP load mode char
+ */
+void sparc_emit_fp_load_mode(const ir_node *node)
+{
+       const sparc_load_store_attr_t *attr = get_sparc_load_store_attr_const(node);
+    ir_mode *mode      = attr->load_store_mode;
+    int      bits      = get_mode_size_bits(mode);
+
+    assert(mode_is_float(mode));
+
+    if (bits == 32) {
+        be_emit_string("f");
+    } else if (bits == 64) {
+        be_emit_string("df");
+    } else {
+               panic("FP load mode > 64bits not implemented yet");
+    }
+}
+
+/**
+ * emit FP store mode char
+ */
+void sparc_emit_fp_store_mode(const ir_node *node)
+{
+       const sparc_load_store_attr_t *attr = get_sparc_load_store_attr_const(node);
+    ir_mode *mode      = attr->load_store_mode;
+    int      bits      = get_mode_size_bits(mode);
+
+    assert(mode_is_float(mode));
+
+    if (bits == 32) {
+        be_emit_string("f");
+    } else if (bits == 64) {
+        be_emit_string("df");
+    } else {
+               panic("FP store mode > 64bits not implemented yet");
+    }
+}
+
+/**
+ * emits the FP mode suffix char
+ */
+void sparc_emit_fp_mode_suffix(const ir_node *node)
+{
+    ir_mode *mode      = get_irn_mode(node);
+    int      bits      = get_mode_size_bits(mode);
+
+    assert(mode_is_float(mode));
+
+    if (bits == 32) {
+               be_emit_string("s");
+    } else if (bits == 64) {
+               be_emit_string("d");
+    } else {
+               panic("FP mode > 64bits not implemented yet");
+    }
+}
+
 /**
  * Returns the target label for a control flow node.
  */
@@ -223,7 +303,7 @@ static void sparc_emit_cfop_target(const ir_node *node)
  */
 static void sparc_emit_entity(ir_entity *entity)
 {
-       be_emit_ident(get_entity_ld_ident(entity));
+       be_gas_emit_entity(entity);
 }
 
 /***********************************************************************************
@@ -244,36 +324,120 @@ static void emit_be_IncSP(const ir_node *irn)
 {
        int offs = -be_get_IncSP_offset(irn);
 
-       if (offs != 0) {
-               /* SPARC stack grows downwards */
-               if (offs < 0) {
-                       be_emit_cstring("\tsub ");
-                       offs = -offs;
-               } else {
-                       be_emit_cstring("\tadd ");
-               }
+       if (offs == 0)
+                       return;
 
-               sparc_emit_source_register(irn, 0);
-               be_emit_irprintf(", %d", offs);
-               be_emit_cstring(", ");
-               sparc_emit_dest_register(irn, 0);
-               be_emit_finish_line_gas(irn);
+       /* SPARC stack grows downwards */
+       if (offs < 0) {
+               be_emit_cstring("\tsub ");
+               offs = -offs;
        } else {
-               // ignore IncSP(0)
-               //be_emit_cstring("\t/* IncSP(0) skipped */");
-
-//             be_emit_cstring("\t/* ");
-//             be_emit_cstring("sub ");
-//             offs = -offs;
-//             sparc_emit_source_register(irn, 0);
-//             be_emit_irprintf(", %d", offs);
-//             be_emit_cstring(", ");
-//             sparc_emit_dest_register(irn, 0);
-//             be_emit_cstring(" ignored */ ");
-//             be_emit_finish_line_gas(irn);
+               be_emit_cstring("\tadd ");
        }
 
+       sparc_emit_source_register(irn, 0);
+       be_emit_irprintf(", %d", offs);
+       be_emit_cstring(", ");
+       sparc_emit_dest_register(irn, 0);
+       be_emit_finish_line_gas(irn);
+}
 
+/**
+ * emits code for save instruction with min. required stack space
+ */
+static void emit_sparc_Save(const ir_node *irn)
+{
+       save_attr = get_sparc_save_attr_const(irn);
+       be_emit_cstring("\tsave ");
+       sparc_emit_source_register(irn, 0);
+       be_emit_irprintf(", %d, ", -save_attr->initial_stacksize);
+       sparc_emit_dest_register(irn, 0);
+       be_emit_finish_line_gas(irn);
+}
+
+/**
+ * emits code to load hi 22 bit of a constant
+ */
+static void emit_sparc_HiImm(const ir_node *irn)
+{
+       const sparc_attr_t *attr = get_sparc_attr_const(irn);
+       be_emit_cstring("\tsethi ");
+       be_emit_irprintf("%%hi(%d), ", attr->immediate_value);
+       sparc_emit_dest_register(irn, 0);
+       be_emit_finish_line_gas(irn);
+}
+
+/**
+ * emits code to load lo 10bits of a constant
+ */
+static void emit_sparc_LoImm(const ir_node *irn)
+{
+       const sparc_attr_t *attr = get_sparc_attr_const(irn);
+       be_emit_cstring("\tor ");
+       sparc_emit_source_register(irn, 0);
+       be_emit_irprintf(", %%lo(%d), ", attr->immediate_value);
+       sparc_emit_dest_register(irn, 0);
+       be_emit_finish_line_gas(irn);
+}
+
+/**
+ * emit code for div with the correct sign prefix
+ */
+static void emit_sparc_Div(const ir_node *irn)
+{
+       be_emit_cstring("\t");
+       sparc_emit_mode_sign_prefix(irn);
+       be_emit_cstring("div ");
+
+       sparc_emit_source_register(irn, 0);
+       be_emit_cstring(", ");
+       sparc_emit_reg_or_imm(irn, 1);
+       be_emit_cstring(", ");
+       sparc_emit_dest_register(irn, 0);
+       be_emit_finish_line_gas(irn);
+}
+
+/**
+ * emit code for mul with the correct sign prefix
+ */
+static void emit_sparc_Mul(const ir_node *irn)
+{
+       be_emit_cstring("\t");
+       sparc_emit_mode_sign_prefix(irn);
+       be_emit_cstring("mul ");
+
+       sparc_emit_source_register(irn, 0);
+       be_emit_cstring(", ");
+       sparc_emit_reg_or_imm(irn, 1);
+       be_emit_cstring(", ");
+       sparc_emit_dest_register(irn, 0);
+       be_emit_finish_line_gas(irn);
+}
+
+/**
+ * emits code for mulh
+ */
+static void emit_sparc_Mulh(const ir_node *irn)
+{
+       be_emit_cstring("\t");
+       sparc_emit_mode_sign_prefix(irn);
+       be_emit_cstring("mul ");
+
+       sparc_emit_source_register(irn, 0);
+       be_emit_cstring(", ");
+       sparc_emit_reg_or_imm(irn, 1);
+       be_emit_cstring(", ");
+       sparc_emit_dest_register(irn, 0);
+       be_emit_finish_line_gas(irn);
+
+       // our result is in the y register now
+       // we just copy it to the assigned target reg
+       be_emit_cstring("\tmov ");
+       be_emit_char('%');
+       be_emit_string(arch_register_get_name(&sparc_flags_regs[REG_Y]));
+       be_emit_cstring(", ");
+       sparc_emit_dest_register(irn, 0);
+       be_emit_finish_line_gas(irn);
 }
 
 /**
@@ -282,6 +446,9 @@ static void emit_be_IncSP(const ir_node *irn)
 static void emit_be_Return(const ir_node *irn)
 {
        be_emit_cstring("\tret");
+       //be_emit_cstring("\tjmp %i7+8");
+       be_emit_finish_line_gas(irn);
+       be_emit_cstring("\trestore");
        be_emit_finish_line_gas(irn);
 }
 
@@ -295,6 +462,7 @@ static void emit_be_Call(const ir_node *irn)
        if (entity != NULL) {
                be_emit_cstring("\tcall ");
            sparc_emit_entity(entity);
+               be_emit_cstring(", 0");
                be_emit_finish_line_gas(irn);
                be_emit_cstring("\tnop");
                be_emit_pad_comment();
@@ -307,40 +475,130 @@ static void emit_be_Call(const ir_node *irn)
        }
 }
 
+/**
+ * Emit code for Perm node
+ */
+static void emit_be_Perm(const ir_node *irn)
+{
+       be_emit_cstring("\txor ");
+       sparc_emit_source_register(irn, 1);
+       be_emit_cstring(", ");
+       sparc_emit_source_register(irn, 0);
+       be_emit_cstring(", ");
+       sparc_emit_source_register(irn, 0);
+       be_emit_finish_line_gas(NULL);
+
+       be_emit_cstring("\txor ");
+       sparc_emit_source_register(irn, 1);
+       be_emit_cstring(", ");
+       sparc_emit_source_register(irn, 0);
+       be_emit_cstring(", ");
+       sparc_emit_source_register(irn, 1);
+       be_emit_finish_line_gas(NULL);
+
+       be_emit_cstring("\txor ");
+       sparc_emit_source_register(irn, 1);
+       be_emit_cstring(", ");
+       sparc_emit_source_register(irn, 0);
+       be_emit_cstring(", ");
+       sparc_emit_source_register(irn, 0);
+       be_emit_finish_line_gas(irn);
+}
+
+/**
+ * TODO: not really tested but seems to work with memperm_arity == 1
+ */
+static void emit_be_MemPerm(const ir_node *node)
+{
+       int i;
+       int memperm_arity;
+       int sp_change = 0;
+
+       /* TODO: this implementation is slower than necessary.
+          The longterm goal is however to avoid the memperm node completely */
+
+       memperm_arity = be_get_MemPerm_entity_arity(node);
+       // we use our local registers - so this is limited to 8 inputs !
+       if (memperm_arity > 8)
+               panic("memperm with more than 8 inputs not supported yet");
+
+       for (i = 0; i < memperm_arity; ++i) {
+               int offset;
+               ir_entity *entity = be_get_MemPerm_in_entity(node, i);
+
+               /* spill register */
+               sp_change += 4;
+               be_emit_irprintf("\tst %%l%d, [%%sp-%d]", i, sp_change);
+               be_emit_finish_line_gas(node);
+
+               /* load from entity */
+               offset = get_entity_offset(entity) + sp_change;
+               be_emit_irprintf("\tld [%%sp+%d], %%l%d", offset, i);
+               be_emit_finish_line_gas(node);
+       }
+
+       for (i = memperm_arity-1; i >= 0; --i) {
+               int        offset;
+               ir_entity *entity = be_get_MemPerm_out_entity(node, i);
+
+               /* store to new entity */
+               offset = get_entity_offset(entity) + sp_change;
+               be_emit_irprintf("\tst %%l%d, [%%sp+%d]", i, offset);
+               be_emit_finish_line_gas(node);
+               /* restore register */
+               be_emit_irprintf("\tld [%%sp-%d], %%l%d", sp_change, i);
+               sp_change -= 4;
+               be_emit_finish_line_gas(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);
-       //const char *entity_name = get_entity_ld_name(attr->entity);
-       ident *id_symconst = get_entity_ident(attr->entity);
-       const char *label = get_id_str(id_symconst);
 
        //sethi %hi(const32),%reg
        //or    %reg,%lo(const32),%reg
 
-       be_emit_irprintf("\tsethi %%hi(%s), ", label);
+       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_irprintf(", %%lo(%s), ", label);
+       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)
 {
        const sparc_symconst_attr_t *attr = get_irn_generic_attr_const(irn);
-       be_emit_cstring("\tadd ");
-       sparc_emit_source_register(irn, 0);
-       be_emit_cstring(", ");
-       be_emit_irprintf("0x%X", attr->fp_offset);
+
+       // no need to fix offset as we are adressing via the framepointer
+       if (attr->fp_offset >= 0) {
+               be_emit_cstring("\tadd ");
+               sparc_emit_source_register(irn, 0);
+               be_emit_cstring(", ");
+               be_emit_irprintf("%ld", attr->fp_offset + save_attr->initial_stacksize);
+       } else {
+               be_emit_cstring("\tsub ");
+               sparc_emit_source_register(irn, 0);
+               be_emit_cstring(", ");
+               be_emit_irprintf("%ld", -attr->fp_offset);
+       }
+
        be_emit_cstring(", ");
        sparc_emit_dest_register(irn, 0);
        be_emit_finish_line_gas(irn);
@@ -397,6 +655,7 @@ static void emit_sparc_Branch(const ir_node *irn)
                proj_num   = get_negated_pnc(proj_num, mode_Iu);
        }
 
+
        switch (proj_num) {
                case pn_Cmp_Eq:  suffix = "e"; break;
                case pn_Cmp_Lt:  suffix = "l"; break;
@@ -426,13 +685,13 @@ static void emit_sparc_Branch(const ir_node *irn)
                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 */");
-               be_emit_write_line();
+               be_emit_cstring("\tnop\t\t/* TODO: use delay slot */\n");
+               be_emit_finish_line_gas(proj_false);
        }
 }
 
 /**
- * emit Jmp (which actually is a branch always)
+ * emit Jmp (which actually is a branch always (ba) instruction)
  */
 static void emit_sparc_Jmp(const ir_node *node)
 {
@@ -446,6 +705,8 @@ static void emit_sparc_Jmp(const ir_node *node)
        if (get_irn_link(node) != next_block) {
                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");
        } else {
                be_emit_cstring("\t/* fallthrough to ");
                sparc_emit_cfop_target(node);
@@ -454,6 +715,9 @@ static void emit_sparc_Jmp(const ir_node *node)
        be_emit_finish_line_gas(node);
 }
 
+/**
+ * emit copy node
+ */
 static void emit_be_Copy(const ir_node *irn)
 {
        ir_mode *mode = get_irn_mode(irn);
@@ -514,16 +778,26 @@ static void sparc_register_emitters(void)
        sparc_register_spec_emitters();
 
        /* custom emitter */
-    set_emitter(op_be_IncSP,       emit_be_IncSP);
-    set_emitter(op_be_Return,      emit_be_Return);
-    set_emitter(op_be_Call,        emit_be_Call);
-    set_emitter(op_sparc_FrameAddr,  emit_sparc_FrameAddr);
-    set_emitter(op_sparc_Branch,   emit_sparc_Branch);
-    set_emitter(op_sparc_SymConst,   emit_sparc_SymConst);
-    set_emitter(op_sparc_Jmp,        emit_sparc_Jmp);
-
-    set_emitter(op_be_Copy,        emit_be_Copy);
-    set_emitter(op_be_CopyKeep,    emit_be_Copy);
+    set_emitter(op_be_IncSP,                   emit_be_IncSP);
+    set_emitter(op_be_Return,                  emit_be_Return);
+    set_emitter(op_be_Call,                            emit_be_Call);
+    set_emitter(op_sparc_FrameAddr,            emit_sparc_FrameAddr);
+    set_emitter(op_sparc_Branch,               emit_sparc_Branch);
+    set_emitter(op_sparc_SymConst,             emit_sparc_SymConst);
+    set_emitter(op_sparc_Jmp,                  emit_sparc_Jmp);
+    set_emitter(op_sparc_Save,                 emit_sparc_Save);
+
+    set_emitter(op_sparc_HiImm,                        emit_sparc_HiImm);
+    set_emitter(op_sparc_LoImm,                        emit_sparc_LoImm);
+    set_emitter(op_sparc_Div,                  emit_sparc_Div);
+    set_emitter(op_sparc_Mul,                  emit_sparc_Mul);
+    set_emitter(op_sparc_Mulh,                 emit_sparc_Mulh);
+
+    set_emitter(op_be_Copy,                            emit_be_Copy);
+    set_emitter(op_be_CopyKeep,                        emit_be_Copy);
+
+    set_emitter(op_be_Perm,                            emit_be_Perm);
+    set_emitter(op_be_MemPerm,                 emit_be_MemPerm);
 
 /*
     set_emitter(op_arm_B,          emit_arm_B);
@@ -532,8 +806,7 @@ static void sparc_register_emitters(void)
     set_emitter(op_arm_fpaDbl2GP,  emit_arm_fpaDbl2GP);
     set_emitter(op_arm_LdTls,      emit_arm_LdTls);
     set_emitter(op_arm_SwitchJmp,  emit_arm_SwitchJmp);
-    set_emitter(op_be_MemPerm,     emit_be_MemPerm);
-    set_emitter(op_be_Perm,        emit_be_Perm);
+
 */
     /* no need to emit anything for the following nodes */
        set_emitter(op_Phi,            emit_nothing);
@@ -588,12 +861,7 @@ static void sparc_gen_block(ir_node *block, void *data)
 static void sparc_emit_func_prolog(ir_graph *irg)
 {
        ir_entity *ent = get_irg_entity(irg);
-
        be_gas_emit_function_prolog(ent, 4);
-       // TODO: fetch reg names via API func
-       // TODO: move value to SPARC_MIN_STACKSIZE const
-       be_emit_cstring("\tsave %sp, -64, %sp");
-       be_emit_cstring("\t/* incr CWP and alloc min. required stack space */\n");
        be_emit_write_line();
 }
 
@@ -604,9 +872,7 @@ static void sparc_emit_func_epilog(ir_graph *irg)
 {
        ir_entity *ent = get_irg_entity(irg);
        const char *irg_name = get_entity_ld_name(ent);
-
-       be_emit_cstring("\trestore");
-       be_emit_cstring("\t/* decr CWP */\n");
+       be_emit_write_line();
        be_emit_irprintf("\t.size  %s, .-%s\n", irg_name, irg_name);
        be_emit_cstring("# -- End ");
        be_emit_string(irg_name);
@@ -641,15 +907,17 @@ void sparc_gen_routine(const sparc_code_gen_t *cg, ir_graph *irg)
        ir_node *last_block = NULL;
        ir_entity *entity     = get_irg_entity(irg);
        int i, n;
+       (void) cg;
 
        be_gas_elf_type_char = '#';
+       be_gas_object_file_format = OBJECT_FILE_FORMAT_ELF_SPARC;
 
        /* register all emitter functions */
        sparc_register_emitters();
-       be_dbg_method_begin(entity, be_abi_get_stack_layout(cg->birg->abi));
+       be_dbg_method_begin(entity);
 
        /* create the block schedule. For now, we don't need it earlier. */
-       blk_sched = be_create_block_schedule(cg->irg, cg->birg->exec_freq);
+       blk_sched = be_create_block_schedule(irg);
 
        // emit function prolog
        sparc_emit_func_prolog(irg);