change backends to produce 1 big array with all registers
[libfirm] / ir / be / arm / arm_optimize.c
index aebe76f..aa57547 100644 (file)
  * @file
  * @brief       Implements several optimizations for ARM.
  * @author      Michael Beck
- * @version     $Id$
+ * @version     $Id$
  */
 #include "config.h"
 
 #include "irgmod.h"
 #include "ircons.h"
+#include "iredges.h"
 #include "error.h"
 
-#include "benode_t.h"
+#include "benode.h"
 #include "bepeephole.h"
 #include "besched.h"
 
 #include "arm_optimize.h"
 #include "gen_arm_regalloc_if.h"
 #include "gen_arm_new_nodes.h"
+#include "arm_nodes_attr.h"
+#include "arm_new_nodes.h"
 
-static arm_code_gen_t  *cg;
-
-/** Execute ARM ROL. */
-static unsigned arm_rol(unsigned v, unsigned rol) {
-       return (v << rol) | (v >> (32 - rol));
+static unsigned arm_ror(unsigned v, unsigned ror)
+{
+       return (v << (32 - ror)) | (v >> ror);
 }
 
 /*
@@ -51,61 +52,38 @@ void arm_gen_vals_from_word(unsigned int value, arm_vals *result)
 {
        int initial = 0;
 
-       memset(result, 0, sizeof(*result));
+       /* TODO: not optimal yet, as we only "shift" the value and don't take advantage of rotations */
 
        /* special case: we prefer shift amount 0 */
-       if (value < 0x100) {
+       if (value <= 0xFF) {
                result->values[0] = value;
+               result->rors[0]   = 0;
                result->ops       = 1;
                return;
        }
 
-       while (value != 0) {
-               if (value & 0xFF) {
-                       unsigned v = arm_rol(value, 8) & 0xFFFFFF;
-                       int shf = 0;
-                       for (;;) {
-                               if ((v & 3) != 0)
-                                       break;
-                               shf += 2;
-                               v >>= 2;
-                       }
-                       v  &= 0xFF;
-                       shf = (initial + shf - 8) & 0x1F;
-                       result->values[result->ops] = v;
-                       result->shifts[result->ops] = shf;
-                       ++result->ops;
-
-                       value ^= arm_rol(v, shf) >> initial;
-               }
-               else {
-                       value >>= 8;
-                       initial += 8;
+       result->ops = 0;
+       do {
+               while ( (value & 0x3) == 0) {
+                       value  >>= 2;
+                       initial += 2;
                }
-       }
-}
-
-/**
- * Encodes an immediate with shifter operand
- */
-unsigned int arm_encode_imm_w_shift(unsigned int shift, unsigned int immediate) {
-       return immediate | ((shift>>1)<<8);
-}
 
-/**
- * Decode an immediate with shifter operand
- */
-unsigned int arm_decode_imm_w_shift(long imm_value) {
-       unsigned l = (unsigned)imm_value;
-       unsigned rol = (l & ~0xFF) >> 7;
+               result->values[result->ops] = value & 0xFF;
+               result->rors[result->ops]   = (32-initial) % 32;
+               ++result->ops;
 
-       return arm_rol(l & 0xFF, rol);
+               value  >>= 8;
+               initial += 8;
+       } while (value != 0);
 }
 
 /**
- * Returns non.zero if the given offset can be directly encoded into an ARM instruction.
+ * Returns non.zero if the given offset can be directly encoded into an ARM
+ * instruction.
  */
-static int allowed_arm_immediate(int offset, arm_vals *result) {
+static int allowed_arm_immediate(int offset, arm_vals *result)
+{
        arm_gen_vals_from_word(offset, result);
        return result->ops <= 1;
 }
@@ -113,11 +91,17 @@ static int allowed_arm_immediate(int offset, arm_vals *result) {
 /**
  * Fix an IncSP node if the offset gets too big
  */
-static void peephole_be_IncSP(ir_node *node) {
-       ir_graph *irg;
+static void peephole_be_IncSP(ir_node *node)
+{
+       ir_node  *first;
+       ir_node  *last;
        ir_node  *block;
-       int      offset, cnt, align, sign = 1;
-       arm_vals v;
+       int       offset;
+       int       cnt;
+       int       sign = 1;
+       arm_vals  v;
+       const ir_edge_t *edge;
+       const ir_edge_t *next;
 
        /* first optimize incsp->incsp combinations */
        node = be_peephole_IncSP_IncSP(node);
@@ -131,17 +115,28 @@ static void peephole_be_IncSP(ir_node *node) {
        if (allowed_arm_immediate(offset, &v))
                return;
 
-       be_set_IncSP_offset(node, (int)arm_rol(v.values[0], v.shifts[0]) * sign);
+       be_set_IncSP_offset(node, sign * arm_ror(v.values[0], v.rors[0]));
 
-       irg   = current_ir_graph;
+       first = node;
        block = get_nodes_block(node);
-       align = be_get_IncSP_align(node);
        for (cnt = 1; cnt < v.ops; ++cnt) {
-               int value = (int)arm_rol(v.values[cnt], v.shifts[cnt]);
-               ir_node *next = be_new_IncSP(&arm_gp_regs[REG_SP], irg, block, node, value * sign, align);
+               int value = sign * arm_ror(v.values[cnt], v.rors[cnt]);
+               ir_node *next = be_new_IncSP(&arm_registers[REG_SP], block, node,
+                                            value, 1);
                sched_add_after(node, next);
                node = next;
        }
+
+       /* reattach IncSP users */
+       last = node;
+       node = sched_next(first);
+       foreach_out_edge_safe(first, edge, next) {
+               ir_node *user = get_edge_src_irn(edge);
+               int      pos  = get_edge_src_pos(edge);
+               if (user == node)
+                       continue;
+               set_irn_n(user, pos, last);
+       }
 }
 
 /**
@@ -149,20 +144,19 @@ static void peephole_be_IncSP(ir_node *node) {
  */
 static ir_node *gen_ptr_add(ir_node *node, ir_node *frame, arm_vals *v)
 {
-       ir_graph *irg   = current_ir_graph;
-       dbg_info *dbg   = get_irn_dbg_info(node);
+       dbg_info *dbgi  = get_irn_dbg_info(node);
        ir_node  *block = get_nodes_block(node);
        int     cnt;
        ir_node *ptr;
 
-       ptr = new_rd_arm_Add_i(dbg, irg, block, frame, mode_Iu, arm_encode_imm_w_shift(v->shifts[0], v->values[0]));
-       arch_set_irn_register(ptr, &arm_gp_regs[REG_R12]);
+       ptr = new_bd_arm_Add_imm(dbgi, block, frame, v->values[0], v->rors[0]);
+       arch_set_irn_register(ptr, &arm_registers[REG_R12]);
        sched_add_before(node, ptr);
 
        for (cnt = 1; cnt < v->ops; ++cnt) {
-               long value = arm_encode_imm_w_shift(v->shifts[cnt], v->values[cnt]);
-               ir_node *next = new_rd_arm_Add_i(dbg, irg, block, ptr, mode_Iu, value);
-               arch_set_irn_register(next, &arm_gp_regs[REG_R12]);
+               ir_node *next = new_bd_arm_Add_imm(dbgi, block, ptr, v->values[cnt],
+                                                  v->rors[cnt]);
+               arch_set_irn_register(next, &arm_registers[REG_R12]);
                sched_add_before(node, next);
                ptr = next;
        }
@@ -174,152 +168,111 @@ static ir_node *gen_ptr_add(ir_node *node, ir_node *frame, arm_vals *v)
 */
 static ir_node *gen_ptr_sub(ir_node *node, ir_node *frame, arm_vals *v)
 {
-       ir_graph *irg   = current_ir_graph;
-       dbg_info *dbg   = get_irn_dbg_info(node);
+       dbg_info *dbgi  = get_irn_dbg_info(node);
        ir_node  *block = get_nodes_block(node);
        int     cnt;
        ir_node *ptr;
 
-       ptr = new_rd_arm_Sub_i(dbg, irg, block, frame, mode_Iu, arm_encode_imm_w_shift(v->shifts[0], v->values[0]));
-       arch_set_irn_register(ptr, &arm_gp_regs[REG_R12]);
+       ptr = new_bd_arm_Sub_imm(dbgi, block, frame, v->values[0], v->rors[0]);
+       arch_set_irn_register(ptr, &arm_registers[REG_R12]);
        sched_add_before(node, ptr);
 
        for (cnt = 1; cnt < v->ops; ++cnt) {
-               long value = arm_encode_imm_w_shift(v->shifts[cnt], v->values[cnt]);
-               ir_node *next = new_rd_arm_Sub_i(dbg, irg, block, ptr, mode_Iu, value);
-               arch_set_irn_register(next, &arm_gp_regs[REG_R12]);
+               ir_node *next = new_bd_arm_Sub_imm(dbgi, block, ptr, v->values[cnt],
+                                                  v->rors[cnt]);
+               arch_set_irn_register(next, &arm_registers[REG_R12]);
                sched_add_before(node, next);
                ptr = next;
        }
        return ptr;
 }
 
-/**
- * Fix an be_Spill node if the offset gets too big
- */
-static void peephole_be_Spill(ir_node *node) {
-       ir_entity *ent   = be_get_frame_entity(node);
-       int       use_add = 1, offset = get_entity_offset(ent);
-       ir_node   *block, *ptr, *frame, *value, *store;
-       ir_mode   *mode;
-       dbg_info  *dbg;
-       ir_graph  *irg;
-       arm_vals  v;
+/** fix frame addresses which are too big */
+static void peephole_arm_FrameAddr(ir_node *node)
+{
+       arm_SymConst_attr_t *attr   = get_arm_SymConst_attr(node);
+       int                  offset = attr->fp_offset;
+       arm_vals             v;
+       ir_node             *base;
+       ir_node             *ptr;
 
        if (allowed_arm_immediate(offset, &v))
                return;
-       if (offset < 0) {
-               use_add = 0;
-               offset = -offset;
-       }
-
-       frame = be_get_Spill_frame(node);
-       if (use_add) {
-               ptr = gen_ptr_add(node, frame, &v);
-       } else {
-               ptr = gen_ptr_sub(node, frame, &v);
-       }
-
-       value = be_get_Spill_val(node);
-       mode  = get_irn_mode(value);
-       irg   = current_ir_graph;
-       dbg   = get_irn_dbg_info(node);
-       block = get_nodes_block(node);
 
-       if (mode_is_float(mode)) {
-               if (USE_FPA(cg->isa)) {
-                       /* transform into fpaStf */
-                       store = new_rd_arm_fpaStf(dbg, irg, block, ptr, value, get_irg_no_mem(irg), mode);
-                       sched_add_before(node, store);
-               } else {
-                       panic("peephole_be_Spill: spill not supported for this mode");
-               }
-       } else if (mode_is_dataM(mode)) {
-                /* transform into Store */;
-                store = new_rd_arm_Store(dbg, irg, block, ptr, value, get_irg_no_mem(irg));
-                sched_add_before(node, store);
-       } else {
-               panic("peephole_be_Spill: spill not supported for this mode");
-       }
+       base = get_irn_n(node, n_arm_FrameAddr_base);
+       /* TODO: suboptimal */
+       ptr = gen_ptr_add(node, base, &v);
 
-       be_peephole_exchange(node, store);
+       attr->fp_offset = 0;
+       set_irn_n(node, n_arm_FrameAddr_base, ptr);
 }
 
 /**
- * Fix an be_Reload node if the offset gets too big
+ * Fix stackpointer relative stores if the offset gets too big
  */
-static void peephole_be_Reload(ir_node *node) {
-       ir_entity *ent   = be_get_frame_entity(node);
-       int       use_add = 1, offset = get_entity_offset(ent);
-       ir_node   *block, *ptr, *frame, *load, *mem, *proj;
-       ir_mode   *mode;
-       dbg_info  *dbg;
-       ir_graph  *irg;
-       arm_vals  v;
-       const arch_register_t *reg;
+static void peephole_arm_Str_Ldr(ir_node *node)
+{
+       arm_load_store_attr_t *attr    = get_arm_load_store_attr(node);
+       int                    offset  = attr->offset;
+       int                    use_add = 1;
+       ir_node               *ptr;
+       arm_vals              v;
 
        if (allowed_arm_immediate(offset, &v))
                return;
+
+       /* we should only have too big offsets for frame entities */
+       if (!attr->is_frame_entity) {
+               fprintf(stderr,
+                       "POSSIBLE ARM BACKEND PROBLEM: offset in Store too big\n");
+       }
        if (offset < 0) {
                use_add = 0;
                offset = -offset;
        }
 
-       frame = be_get_Reload_frame(node);
-       if (use_add) {
-               ptr = gen_ptr_add(node, frame, &v);
+       if (is_arm_Str(node)) {
+               ptr = get_irn_n(node, n_arm_Str_ptr);
        } else {
-               ptr = gen_ptr_sub(node, frame, &v);
+               assert(is_arm_Ldr(node));
+               ptr = get_irn_n(node, n_arm_Ldr_ptr);
        }
 
-       reg   = arch_get_irn_register(node);
-       mem   = be_get_Reload_mem(node);
-       mode  = get_irn_mode(node);
-       irg   = current_ir_graph;
-       dbg   = get_irn_dbg_info(node);
-       block = get_nodes_block(node);
-
-       if (mode_is_float(mode)) {
-               if (USE_FPA(cg->isa)) {
-                       /* transform into fpaLdf */
-                       load = new_rd_arm_fpaLdf(dbg, irg, block, ptr, mem, mode);
-                       sched_add_before(node, load);
-                       proj = new_rd_Proj(dbg, irg, block, load, mode, pn_arm_fpaLdf_res);
-                       arch_set_irn_register(proj, reg);
-               } else {
-                       panic("peephole_be_Spill: spill not supported for this mode");
-               }
-       } else if (mode_is_dataM(mode)) {
-               /* transform into Store */;
-               load = new_rd_arm_Load(dbg, irg, block, ptr, mem);
-               sched_add_before(node, load);
-               proj = new_rd_Proj(dbg, irg, block, load, mode_Iu, pn_arm_Load_res);
-               arch_set_irn_register(proj, reg);
+       if (use_add) {
+               ptr = gen_ptr_add(node, ptr, &v);
        } else {
-               panic("peephole_be_Spill: spill not supported for this mode");
+               ptr = gen_ptr_sub(node, ptr, &v);
        }
 
-       be_peephole_exchange(node, proj);
+       /* TODO: sub-optimal, the last offset could probably be left inside the
+          store */
+       if (is_arm_Str(node)) {
+               set_irn_n(node, n_arm_Str_ptr, ptr);
+       } else {
+               assert(is_arm_Ldr(node));
+               set_irn_n(node, n_arm_Ldr_ptr, ptr);
+       }
+       attr->offset = 0;
 }
 
 /**
  * Register a peephole optimization function.
  */
-static void register_peephole_optimisation(ir_op *op, peephole_opt_func func) {
+static void register_peephole_optimisation(ir_op *op, peephole_opt_func func)
+{
        assert(op->ops.generic == NULL);
        op->ops.generic = (op_func)func;
 }
 
 /* Perform peephole-optimizations. */
-void arm_peephole_optimization(arm_code_gen_t *new_cg)
+void arm_peephole_optimization(ir_graph *irg)
 {
-       cg = new_cg;
-
        /* register peephole optimizations */
        clear_irp_opcodes_generic_func();
-       register_peephole_optimisation(op_be_IncSP, peephole_be_IncSP);
-       register_peephole_optimisation(op_be_Spill, peephole_be_Spill);
-       register_peephole_optimisation(op_be_Reload, peephole_be_Reload);
+       register_peephole_optimisation(op_be_IncSP,      peephole_be_IncSP);
+       register_peephole_optimisation(op_arm_Str,       peephole_arm_Str_Ldr);
+       register_peephole_optimisation(op_arm_Ldr,       peephole_arm_Str_Ldr);
+       register_peephole_optimisation(op_arm_FrameAddr, peephole_arm_FrameAddr);
 
-       be_peephole_opt(cg->birg);
+       be_peephole_opt(irg);
 }