Commit 6befda80 authored by Matthias Braun's avatar Matthias Braun
Browse files

sparc: improved IncSP/Save/Restore handling

parent 0929d789
......@@ -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);
......
......@@ -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)
......
......@@ -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,
};
......
......@@ -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
......@@ -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(", ");
......
/*
* 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);
}
......@@ -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 => {
......
......@@ -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);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment