sparc: improved IncSP/Save/Restore handling
authorMatthias Braun <matze@braunis.de>
Mon, 28 Feb 2011 18:43:07 +0000 (19:43 +0100)
committerMatthias Braun <matze@braunis.de>
Fri, 4 Mar 2011 19:14:03 +0000 (20:14 +0100)
ir/be/bepeephole.c
ir/be/bepeephole.h
ir/be/sparc/bearch_sparc.c
ir/be/sparc/bearch_sparc_t.h
ir/be/sparc/sparc_emitter.c
ir/be/sparc/sparc_finish.c [new file with mode: 0644]
ir/be/sparc/sparc_spec.pl
ir/be/sparc/sparc_transform.c

index fedab04..07e3ab1 100644 (file)
@@ -237,24 +237,25 @@ static void process_block(ir_node *block, void *data)
 /**
  * Check whether the node has only one user.  Explicitly ignore the anchor.
  */
-static int has_only_one_user(ir_node *node)
+bool be_has_only_one_user(ir_node *node)
 {
        int              n = get_irn_n_edges(node);
+       int              n_users;
        const ir_edge_t *edge;
 
        if (n <= 1)
                return 1;
 
-       if (n > 2)
-               return 0;
-
+       n_users = 0;
        foreach_out_edge(node, edge) {
                ir_node *src = get_edge_src_irn(edge);
-               if (is_Anchor(src))
-                       return 1;
+               /* ignore anchor and keep-alive edges */
+               if (is_Anchor(src) || is_End(src))
+                       continue;
+               n_users++;
        }
 
-       return 0;
+       return n_users == 1;
 }
 
 /*
@@ -271,7 +272,7 @@ ir_node *be_peephole_IncSP_IncSP(ir_node *node)
        if (!be_is_IncSP(pred))
                return node;
 
-       if (!has_only_one_user(pred))
+       if (!be_has_only_one_user(pred))
                return node;
 
        pred_offs = be_get_IncSP_offset(pred);
index 1972843..924d8a9 100644 (file)
@@ -71,6 +71,8 @@ void be_peephole_exchange(ir_node *old, ir_node *nw);
  */
 ir_node *be_peephole_IncSP_IncSP(ir_node *node);
 
+bool be_has_only_one_user(ir_node *node);
+
 /**
  * Do peephole optimisations. It traverses the schedule of all blocks in
  * backward direction. The register_values variable indicates which (live)
index ae59975..3adab36 100644 (file)
@@ -647,7 +647,7 @@ const arch_isa_if_t sparc_isa_if = {
        sparc_prepare_graph,
        sparc_before_ra,
        sparc_after_ra,
-       NULL, /* finish */
+       sparc_finish,
        sparc_emit_routine,
 };
 
index 04d7d17..ea9491f 100644 (file)
@@ -72,4 +72,6 @@ static inline bool sparc_is_value_imm_encodeable(int32_t value)
        return SPARC_IMMEDIATE_MIN <= value && value <= SPARC_IMMEDIATE_MAX;
 }
 
+void sparc_finish(ir_graph *irg);
+
 #endif
index 9340fc7..d55c6da 100644 (file)
@@ -118,11 +118,6 @@ 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)
 {
        const sparc_attr_t *attr   = get_sparc_attr_const(node);
@@ -130,7 +125,7 @@ void sparc_emit_immediate(const ir_node *node)
 
        if (entity == NULL) {
                int32_t value = attr->immediate_value;
-               assert(is_valid_immediate(value));
+               assert(sparc_is_value_imm_encodeable(value));
                be_emit_irprintf("%d", value);
        } else {
                be_emit_cstring("%lo(");
@@ -190,13 +185,6 @@ void sparc_emit_reg_or_imm(const ir_node *node, int pos)
        }
 }
 
-static bool is_stack_pointer_relative(const ir_node *node)
-{
-       const arch_register_t *sp = &sparc_registers[REG_SP];
-       return (is_sparc_St(node) && get_in_reg(node, n_sparc_St_ptr) == sp)
-           || (is_sparc_Ld(node) && get_in_reg(node, n_sparc_Ld_ptr) == sp);
-}
-
 /**
  * emit SP offset
  */
@@ -212,12 +200,8 @@ void sparc_emit_offset(const ir_node *node, int offset_node_pos)
                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));
+                       assert(sparc_is_value_imm_encodeable(offset));
                        be_emit_irprintf("%+ld", offset);
                }
        } else if (attr->base.immediate_value != 0
@@ -482,21 +466,21 @@ static const ir_node *pick_delay_slot_for(const ir_node *node)
  */
 static void emit_be_IncSP(const ir_node *irn)
 {
-       int offs = -be_get_IncSP_offset(irn);
+       int offset = be_get_IncSP_offset(irn);
 
-       if (offs == 0)
+       if (offset == 0)
                return;
 
        /* SPARC stack grows downwards */
-       if (offs < 0) {
+       if (offset < 0) {
                be_emit_cstring("\tsub ");
-               offs = -offs;
+               offset = -offset;
        } else {
                be_emit_cstring("\tadd ");
        }
 
        sparc_emit_source_register(irn, 0);
-       be_emit_irprintf(", %d", offs);
+       be_emit_irprintf(", %d", -offset);
        be_emit_cstring(", ");
        sparc_emit_dest_register(irn, 0);
        be_emit_finish_line_gas(irn);
@@ -700,19 +684,21 @@ static void emit_be_Return(const ir_node *node)
 
 static void emit_sparc_FrameAddr(const ir_node *node)
 {
-       const sparc_attr_t *attr = get_sparc_attr_const(node);
+       const sparc_attr_t *attr   = get_sparc_attr_const(node);
+       int32_t             offset = attr->immediate_value;
 
-       // no need to fix offset as we are adressing via the framepointer
-       if (attr->immediate_value >= 0) {
+       if (offset < 0) {
                be_emit_cstring("\tadd ");
                sparc_emit_source_register(node, 0);
                be_emit_cstring(", ");
-               be_emit_irprintf("%ld", attr->immediate_value);
+               assert(sparc_is_value_imm_encodeable(offset));
+               be_emit_irprintf("%ld", offset);
        } else {
                be_emit_cstring("\tsub ");
                sparc_emit_source_register(node, 0);
                be_emit_cstring(", ");
-               be_emit_irprintf("%ld", -attr->immediate_value);
+               assert(sparc_is_value_imm_encodeable(-offset));
+               be_emit_irprintf("%ld", -offset);
        }
 
        be_emit_cstring(", ");
diff --git a/ir/be/sparc/sparc_finish.c b/ir/be/sparc/sparc_finish.c
new file mode 100644 (file)
index 0000000..2409410
--- /dev/null
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 1995-2010 University of Karlsruhe.  All right reserved.
+ *
+ * This file is part of libFirm.
+ *
+ * This file may be distributed and/or modified under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation and appearing in the file LICENSE.GPL included in the
+ * packaging of this file.
+ *
+ * Licensees holding valid libFirm Professional Edition licenses may use
+ * this file in accordance with the libFirm Commercial License.
+ * Agreement provided with the Software.
+ *
+ * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+ * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE.
+ */
+
+/**
+ * @file
+ * @brief    Peephole optimization and legalization of a sparc function
+ * @author   Matthias Braun
+ * @version  $Id$
+ *
+ * A note on sparc stackpointer (sp) behaviour:
+ * The ABI expects SPARC_MIN_STACKSIZE bytes to be available at the
+ * stackpointer. This space will be used to spill register windows,
+ * and for spilling va_arg arguments (maybe we can optimize this away for
+ * statically known not-va-arg-functions...)
+ * This in effect means that we allocate that extra space at the function begin
+ * which is easy. But this space isn't really fixed at the beginning of the
+ * stackframe. Instead you should rather imagine the space as always being the
+ * last-thing on the stack.
+ * So when addressing anything stack-specific we have to account for this
+ * area, while our compiler thinks the space is occupied at the beginning
+ * of the stack frame. The code here among other things adjusts these offsets
+ * accordingly.
+ */
+#include "config.h"
+
+#include "bearch_sparc_t.h"
+#include "gen_sparc_regalloc_if.h"
+#include "sparc_new_nodes.h"
+#include "irprog.h"
+#include "irgmod.h"
+
+#include "../bepeephole.h"
+#include "../benode.h"
+#include "../besched.h"
+
+static void finish_sparc_Save(ir_node *node)
+{
+       sparc_attr_t *attr = get_sparc_attr(node);
+       int offset = attr->immediate_value;
+       ir_node  *schedpoint = node;
+       dbg_info *dbgi;
+       ir_node  *block;
+       ir_node  *new_save;
+       ir_node  *stack;
+       ir_entity *entity;
+
+       if (sparc_is_value_imm_encodeable(offset))
+               return;
+
+       /* uhh only works for the imm variant yet */
+       assert(get_irn_arity(node) == 1);
+
+       block = get_nodes_block(node);
+       dbgi = get_irn_dbg_info(node);
+       stack = get_irn_n(node, n_sparc_Save_stack);
+       entity = attr->immediate_value_entity;
+       new_save = new_bd_sparc_Save_imm(dbgi, block, stack, entity, 0);
+       arch_set_irn_register(new_save, &sparc_registers[REG_SP]);
+       stack = new_save;
+
+       sched_add_after(node, new_save);
+       schedpoint = new_save;
+       while (offset > SPARC_IMMEDIATE_MAX || offset < SPARC_IMMEDIATE_MIN) {
+               if (offset > 0) {
+                       stack = be_new_IncSP(&sparc_registers[REG_SP], block, stack,
+                                            SPARC_IMMEDIATE_MIN, 0);
+                       offset -= -SPARC_IMMEDIATE_MIN;
+               } else {
+                       stack = be_new_IncSP(&sparc_registers[REG_SP], block, stack,
+                                            -SPARC_IMMEDIATE_MIN, 0);
+                       offset -= SPARC_IMMEDIATE_MIN;
+               }
+               sched_add_after(schedpoint, stack);
+               schedpoint = stack;
+       }
+       attr = get_sparc_attr(new_save);
+       attr->immediate_value = offset;
+       be_peephole_exchange(node, stack);
+}
+
+/**
+ * sparc immediates are limited. Split IncSP with bigger immediates if
+ * necessary.
+ */
+static void finish_be_IncSP(ir_node *node)
+{
+       int      sign   = 1;
+       int      offset = be_get_IncSP_offset(node);
+       ir_node *sp     = be_get_IncSP_pred(node);
+       ir_node *block;
+
+       /* we might have to break the IncSP apart if the constant has become too
+        * big */
+       if (offset < 0) {
+               offset = -offset;
+               sign   = -1;
+       }
+
+       if (sparc_is_value_imm_encodeable(-offset))
+               return;
+
+       /* split incsp into multiple instructions */
+       block = get_nodes_block(node);
+       while (offset > -SPARC_IMMEDIATE_MIN) {
+               sp = be_new_IncSP(&sparc_registers[REG_SP], block, sp,
+                                 sign * -SPARC_IMMEDIATE_MIN, 0);
+               sched_add_before(node, sp);
+               offset -= -SPARC_IMMEDIATE_MIN;
+       }
+
+       be_set_IncSP_pred(node, sp);
+       be_set_IncSP_offset(node, sign*offset);
+}
+
+/**
+ * adjust sp-relative offsets. Split into multiple instructions if offset
+ * exceeds sparc immediate range.
+ */
+static void finish_sparc_FrameAddr(ir_node *node)
+{
+       /* adapt to sparc stack magic */
+       sparc_attr_t *attr   = get_sparc_attr(node);
+       int           offset = attr->immediate_value;
+       ir_node      *base   = get_irn_n(node, n_sparc_FrameAddr_base);
+       dbg_info     *dbgi   = get_irn_dbg_info(node);
+       ir_node      *block  = get_nodes_block(node);
+       int           sign   = 1;
+       bool          sp_relative
+               = arch_get_irn_register(base) == &sparc_registers[REG_SP];
+       if (sp_relative) {
+               offset += SPARC_MIN_STACKSIZE;
+       }
+
+       if (offset < 0) {
+               sign   = -1;
+               offset = -offset;
+       }
+
+       if (offset > -SPARC_IMMEDIATE_MIN) {
+               ir_entity *entity = attr->immediate_value_entity;
+               ir_node   *new_frameaddr
+                       = new_bd_sparc_FrameAddr(dbgi, block, base, entity, 0);
+               ir_node   *schedpoint = node;
+               const arch_register_t *reg = arch_get_irn_register(node);
+
+               sched_add_after(schedpoint, new_frameaddr);
+               schedpoint = new_frameaddr;
+               arch_set_irn_register(new_frameaddr, reg);
+               base = new_frameaddr;
+
+               while (offset > -SPARC_IMMEDIATE_MIN) {
+                       if (sign > 0) {
+                               base = new_bd_sparc_Sub_imm(dbgi, block, base, NULL,
+                                                                                       SPARC_IMMEDIATE_MIN);
+                       } else {
+                               base = new_bd_sparc_Add_imm(dbgi, block, base, NULL,
+                                                                                       SPARC_IMMEDIATE_MIN);
+                       }
+                       arch_set_irn_register(base, reg);
+                       sched_add_after(schedpoint, base);
+                       schedpoint = base;
+
+                       offset -= -SPARC_IMMEDIATE_MIN;
+               }
+
+               be_peephole_exchange(node, base);
+               attr = get_sparc_attr(new_frameaddr);
+       }
+       attr->immediate_value = sign*offset;
+}
+
+static void finish_sparc_LdSt(ir_node *node)
+{
+       sparc_load_store_attr_t *attr = get_sparc_load_store_attr(node);
+       if (attr->is_frame_entity) {
+               ir_node *base;
+               bool     sp_relative;
+               if (is_sparc_Ld(node)) {
+                       base = get_irn_n(node, n_sparc_Ld_ptr);
+               } else {
+                       assert(is_sparc_St(node));
+                       base = get_irn_n(node, n_sparc_St_ptr);
+               }
+               sp_relative = arch_get_irn_register(base) == &sparc_registers[REG_SP];
+               if (sp_relative)
+                       attr->base.immediate_value += SPARC_MIN_STACKSIZE;
+       }
+}
+
+static void peephole_be_IncSP(ir_node *node)
+{
+       ir_node *pred;
+       node = be_peephole_IncSP_IncSP(node);
+       if (!be_is_IncSP(node))
+               return;
+
+       pred = be_get_IncSP_pred(node);
+       if (is_sparc_Save(pred) && be_has_only_one_user(pred)) {
+               int offset = -be_get_IncSP_offset(node);
+               sparc_attr_t *attr = get_sparc_attr(pred);
+               attr->immediate_value += offset;
+               be_peephole_exchange(node, pred);
+       }
+}
+
+static void peephole_sparc_FrameAddr(ir_node *node)
+{
+       /* the peephole code currently doesn't allow this since it changes
+        * the register. Find out why and how to workaround this... */
+#if 0
+       const sparc_attr_t *attr = get_sparc_attr_const(node);
+       if (attr->immediate_value == 0) {
+               ir_node *base = get_irn_n(node, n_sparc_FrameAddr_base);
+               be_peephole_exchange(node, base);
+       }
+#endif
+       (void) node;
+}
+
+static void register_peephole_optimisation(ir_op *op, peephole_opt_func func)
+{
+       assert(op->ops.generic == NULL);
+       op->ops.generic = (op_func) func;
+}
+
+void sparc_finish(ir_graph *irg)
+{
+       clear_irp_opcodes_generic_func();
+       register_peephole_optimisation(op_be_IncSP,        peephole_be_IncSP);
+       register_peephole_optimisation(op_sparc_FrameAddr, peephole_sparc_FrameAddr);
+       be_peephole_opt(irg);
+
+       clear_irp_opcodes_generic_func();
+       register_peephole_optimisation(op_be_IncSP,        finish_be_IncSP);
+       register_peephole_optimisation(op_sparc_Save,      finish_sparc_Save);
+       register_peephole_optimisation(op_sparc_FrameAddr, finish_sparc_FrameAddr);
+       register_peephole_optimisation(op_sparc_Ld,        finish_sparc_LdSt);
+       register_peephole_optimisation(op_sparc_St,        finish_sparc_LdSt);
+       be_peephole_opt(irg);
+}
index 8301db7..c1bd3f0 100644 (file)
@@ -305,6 +305,7 @@ St => {
 Save => {
        emit      => '. save %S0, %R1I, %D0',
        outs      => [ "stack" ],
+       ins       => [ "stack" ],
        constructors => {
                imm => {
                        attr       => "ir_entity *immediate_entity, int32_t immediate_value",
@@ -317,11 +318,13 @@ Save => {
                        ins        => [ "stack", "increment" ],
                }
        },
+       mode => $mode_gp,
 },
 
 Restore => {
        emit => '. restore %S0, %R1I, %D0',
        outs => [ "stack" ],
+       ins  => [ "stack" ],
        constructors => {
                imm => {
                        attr       => "ir_entity *immediate_entity, int32_t immediate_value",
@@ -334,13 +337,15 @@ Restore => {
                        ins        => [ "stack", "increment" ],
                }
        },
+       mode => $mode_gp,
 },
 
 RestoreZero => {
        emit => '. restore',
-       outs => [ ],
-       ins  => [ ],
-       mode => "mode_T",
+       reg_req => { out => [ "sp:I|S" ] },
+       outs    => [ "stack" ],
+       ins     => [ ],
+       mode    => $mode_gp,
 },
 
 SubSP => {
index 1d39dd4..338fae2 100644 (file)
@@ -1324,8 +1324,8 @@ static ir_node *gen_Start(ir_node *node)
                ir_node *save = new_bd_sparc_Save_imm(NULL, block, sp, NULL,
                                                      -SPARC_MIN_STACKSIZE);
                arch_irn_add_flags(save, arch_irn_flags_prolog);
-               sp = new_r_Proj(save, mode_gp, pn_sparc_Save_stack);
-               arch_set_irn_register(sp, sp_reg);
+               arch_set_irn_register(save, sp_reg);
+               sp = save;
        }
 
        sp = be_new_IncSP(sp_reg, new_block, sp, BE_STACK_FRAME_SIZE_EXPAND, 0);
@@ -1364,13 +1364,14 @@ static ir_node *get_stack_pointer_for(ir_node *node)
  */
 static ir_node *gen_Return(ir_node *node)
 {
-       ir_node  *block          = get_nodes_block(node);
-       ir_node  *new_block      = be_transform_node(block);
-       dbg_info *dbgi           = get_irn_dbg_info(node);
-       ir_node  *mem            = get_Return_mem(node);
-       ir_node  *new_mem        = be_transform_node(mem);
-       ir_node  *sp             = get_stack_pointer_for(node);
-       size_t    n_res          = get_Return_n_ress(node);
+       ir_node  *block     = get_nodes_block(node);
+       ir_node  *new_block = be_transform_node(block);
+       dbg_info *dbgi      = get_irn_dbg_info(node);
+       ir_node  *mem       = get_Return_mem(node);
+       ir_node  *new_mem   = be_transform_node(mem);
+       ir_node  *sp        = get_stack_pointer_for(node);
+       size_t    n_res     = get_Return_n_ress(node);
+       ir_node  *barrier;
        ir_node  *bereturn;
        size_t    i;
 
@@ -1405,17 +1406,19 @@ static ir_node *gen_Return(ir_node *node)
                }
        }
 
-       /* epilog code: an incsp */
-       sp = be_epilog_get_reg_value(abihelper, sp_reg);
-       sp = be_new_IncSP(sp_reg, new_block, sp,
-                         BE_STACK_FRAME_SIZE_SHRINK, 0);
-       be_epilog_set_reg_value(abihelper, sp_reg, sp);
-
        /* we need a restore instruction */
        if (!cconv->omit_fp) {
                ir_node *restore = new_bd_sparc_RestoreZero(NULL, block);
                arch_irn_add_flags(restore, arch_irn_flags_epilog);
-               keep_alive(restore);
+               add_irn_dep(restore, barrier);
+               arch_set_irn_register(restore, sp_reg);
+               be_epilog_set_reg_value(abihelper, sp_reg, restore);
+       } else {
+               /* epilog code: an incsp */
+               sp = be_epilog_get_reg_value(abihelper, sp_reg);
+               sp = be_new_IncSP(sp_reg, new_block, sp,
+                                                 BE_STACK_FRAME_SIZE_SHRINK, 0);
+               be_epilog_set_reg_value(abihelper, sp_reg, sp);
        }
 
        bereturn = be_epilog_create_return(abihelper, dbgi, new_block);