Commit f62bf2f3 authored by Matthias Braun's avatar Matthias Braun
Browse files

Directly create Start/Return node in sparc backend

Since we have no complicated barrier/prolog stuff in the transform phase
anymore we can move away from beabihelper now and create the nodes
directly which will be necessary when modeling special constraints like
wide aligned registers.
parent 9a78c10b
......@@ -62,6 +62,7 @@ static const arch_register_t *sp_reg = &arm_registers[REG_SP];
static ir_mode *mode_gp;
static ir_mode *mode_fp;
static beabi_helper_env_t *abihelper;
static be_stackorder_t *stackorder;
static calling_convention_t *cconv = NULL;
static arm_isa_t *isa;
......@@ -1776,7 +1777,7 @@ static ir_node *gen_Start(ir_node *node)
static ir_node *get_stack_pointer_for(ir_node *node)
{
/* get predecessor in stack_order list */
ir_node *stack_pred = be_get_stack_pred(abihelper, node);
ir_node *stack_pred = be_get_stack_pred(stackorder, node);
ir_node *stack;
if (stack_pred == NULL) {
......@@ -2173,7 +2174,7 @@ void arm_transform_graph(ir_graph *irg)
assert(abihelper == NULL);
abihelper = be_abihelper_prepare(irg);
be_collect_stacknodes(abihelper);
stackorder = be_collect_stacknodes(irg);
assert(cconv == NULL);
cconv = arm_decide_calling_convention(irg, get_entity_type(entity));
create_stacklayout(irg);
......@@ -2182,6 +2183,8 @@ void arm_transform_graph(ir_graph *irg)
be_abihelper_finish(abihelper);
abihelper = NULL;
be_free_stackorder(stackorder);
stackorder = NULL;
arm_free_calling_convention(cconv);
cconv = NULL;
......
......@@ -65,7 +65,6 @@ struct beabi_helper_env_t {
ir_graph *irg; /**< the graph we operate on */
register_state_mapping_t prolog; /**< the register state map for the prolog */
register_state_mapping_t epilog; /**< the register state map for the epilog */
ir_phase *stack_order; /**< a phase to handle stack dependencies. */
};
/**
......@@ -620,9 +619,17 @@ static void process_ops_in_block(ir_node *block, void *data)
xfree(nodes);
}
void be_collect_stacknodes(beabi_helper_env_t *env)
struct be_stackorder_t {
ir_phase *stack_order; /**< a phase to handle stack dependencies. */
};
be_stackorder_t *be_collect_stacknodes(ir_graph *irg)
{
ir_graph *irg = env->irg;
be_stackorder_t *env = XMALLOCZ(be_stackorder_t);
ir_reserve_resources(irg, IR_RESOURCE_IRN_LINK);
/* collect all potential^stack accessing nodes */
irg_walk_graph(irg, firm_clear_link, link_ops_in_block_walker, NULL);
......@@ -635,13 +642,23 @@ void be_collect_stacknodes(beabi_helper_env_t *env)
heights = heights_new(irg);
irg_block_walk_graph(irg, NULL, process_ops_in_block, env->stack_order);
heights_free(heights);
ir_free_resources(irg, IR_RESOURCE_IRN_LINK);
return env;
}
ir_node *be_get_stack_pred(const beabi_helper_env_t *env, const ir_node *node)
ir_node *be_get_stack_pred(const be_stackorder_t *env, const ir_node *node)
{
return (ir_node*)phase_get_irn_data(env->stack_order, node);
}
void be_free_stackorder(be_stackorder_t *env)
{
phase_free(env->stack_order);
free(env);
}
void be_add_parameter_entity_stores(ir_graph *irg)
{
ir_type *frame_type = get_irg_frame_type(irg);
......
......@@ -32,6 +32,7 @@
#include "bearch.h"
typedef struct beabi_helper_env_t beabi_helper_env_t;
typedef struct be_stackorder_t be_stackorder_t;
/**
* Creates a helper object for the ABI constraint handling.
......@@ -113,16 +114,25 @@ ir_node *be_epilog_create_return(beabi_helper_env_t *env, dbg_info *dbgi,
void be_add_missing_keeps(ir_graph *irg);
/**
* Collect firm nodes that will probably modify the stack.
* Put them into an order that respects all their dependencies.
* In the normal firm representation some nodes like pure calls, builtins
* have no memory inputs+outputs. However in the backend these sometimes have to
* access the stack to work and therefore suddenly need to be enqueued into the
* memory edge again.
* This API creates a possible order to enqueue them so we can be sure to create
* a legal dependency graph when transforming them.
*/
void be_collect_stacknodes(beabi_helper_env_t *env);
be_stackorder_t *be_collect_stacknodes(ir_graph *irg);
/**
* return node that should produce the predecessor stack node in a block.
* returns NULL if there's no predecessor in the current block.
*/
ir_node *be_get_stack_pred(const beabi_helper_env_t *env, const ir_node *node);
ir_node *be_get_stack_pred(const be_stackorder_t *env, const ir_node *node);
/**
* free memory associated with a stackorder structure
*/
void be_free_stackorder(be_stackorder_t *env);
/**
* In case where a parameter is transmitted via register but someone takes its
......
......@@ -130,10 +130,12 @@ calling_convention_t *sparc_decide_calling_convention(ir_type *function_type,
reg_or_stackslot_t *param = &params[i];
if (regnum < n_param_regs) {
const arch_register_t *reg = param_regs[regnum++];
const arch_register_t *reg = param_regs[regnum];
if (irg == NULL || omit_fp)
reg = map_i_to_o_reg(reg);
param->reg0 = reg;
param->reg0 = reg;
param->reg_offset = regnum;
++regnum;
} else {
param->type = param_type;
param->offset = stack_offset;
......@@ -148,10 +150,12 @@ calling_convention_t *sparc_decide_calling_convention(ir_type *function_type,
panic("only 32 and 64bit modes supported in sparc backend");
if (regnum < n_param_regs) {
const arch_register_t *reg = param_regs[regnum++];
const arch_register_t *reg = param_regs[regnum];
if (irg == NULL || omit_fp)
reg = map_i_to_o_reg(reg);
param->reg1 = reg;
param->reg1 = reg;
param->reg_offset = regnum;
++regnum;
} else {
ir_mode *regmode = param_regs[0]->reg_class->mode;
ir_type *type = get_type_for_mode(regmode);
......
......@@ -36,6 +36,7 @@ typedef struct reg_or_stackslot_t
const arch_register_t *reg0; /**< if != NULL, the first register used for
this parameter. */
const arch_register_t *reg1; /**< if != NULL, the second register used. */
size_t reg_offset;
ir_type *type; /**< indicates that an entity of the specific
type is needed */
unsigned offset; /**< if transmitted via stack, the offset for
......
......@@ -368,7 +368,7 @@ static bool has_delay_slot(const ir_node *node)
return is_sparc_Bicc(node) || is_sparc_fbfcc(node) || is_sparc_Ba(node)
|| is_sparc_SwitchJmp(node) || is_sparc_Call(node)
|| is_sparc_SDiv(node) || is_sparc_UDiv(node)
|| be_is_Return(node);
|| is_sparc_Return(node);
}
/** returns true if the emitter for this sparc node can produce more than one
......@@ -414,7 +414,7 @@ static const ir_node *pick_delay_slot_for(const ir_node *node)
/* the Call also destroys the value of %o7, but since this is currently
* marked as ignore register in the backend, it should never be used by
* the instruction in the delay slot. */
} else if (be_is_Return(node)) {
} else if (is_sparc_Return(node)) {
/* we only have to check the jump destination value */
int arity = get_irn_arity(node);
int i;
......@@ -664,7 +664,7 @@ static void emit_be_MemPerm(const ir_node *node)
assert(sp_change == 0);
}
static void emit_be_Return(const ir_node *node)
static void emit_sparc_Return(const ir_node *node)
{
const char *destreg = "%o7";
......@@ -936,21 +936,21 @@ static void sparc_register_emitters(void)
set_emitter(op_be_IncSP, emit_be_IncSP);
set_emitter(op_be_MemPerm, emit_be_MemPerm);
set_emitter(op_be_Perm, emit_be_Perm);
set_emitter(op_be_Return, emit_be_Return);
set_emitter(op_sparc_Ba, emit_sparc_Ba);
set_emitter(op_sparc_Bicc, emit_sparc_Bicc);
set_emitter(op_sparc_Call, emit_sparc_Call);
set_emitter(op_sparc_fbfcc, emit_sparc_fbfcc);
set_emitter(op_sparc_FrameAddr, emit_sparc_FrameAddr);
set_emitter(op_sparc_Mulh, emit_sparc_Mulh);
set_emitter(op_sparc_Return, emit_sparc_Return);
set_emitter(op_sparc_SDiv, emit_sparc_SDiv);
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 */
set_emitter(op_be_Keep, emit_nothing);
set_emitter(op_be_Start, emit_nothing);
set_emitter(op_Phi, emit_nothing);
set_emitter(op_be_Keep, emit_nothing);
set_emitter(op_sparc_Start, emit_nothing);
set_emitter(op_Phi, emit_nothing);
}
/**
......
......@@ -118,7 +118,7 @@ void sparc_introduce_prolog_epilog(ir_graph *irg)
for (i = 0; i < arity; ++i) {
ir_node *ret = get_irn_n(end_block, i);
assert(be_is_Return(ret));
assert(is_sparc_Return(ret));
introduce_epilog(ret);
}
}
......@@ -342,7 +342,7 @@ static void peephole_sparc_FrameAddr(ir_node *node)
(void) node;
}
static void finish_be_Return(ir_node *node)
static void finish_sparc_Return(ir_node *node)
{
ir_node *schedpoint = node;
ir_node *restore;
......@@ -383,10 +383,10 @@ void sparc_finish(ir_graph *irg)
/* perform legalizations (mostly fix nodes with too big immediates) */
clear_irp_opcodes_generic_func();
register_peephole_optimisation(op_be_IncSP, finish_be_IncSP);
register_peephole_optimisation(op_be_Return, finish_be_Return);
register_peephole_optimisation(op_sparc_FrameAddr, finish_sparc_FrameAddr);
register_peephole_optimisation(op_sparc_Ld, finish_sparc_LdSt);
register_peephole_optimisation(op_sparc_Ldf, finish_sparc_LdSt);
register_peephole_optimisation(op_sparc_Return, finish_sparc_Return);
register_peephole_optimisation(op_sparc_Save, finish_sparc_Save);
register_peephole_optimisation(op_sparc_St, finish_sparc_LdSt);
register_peephole_optimisation(op_sparc_Stf, finish_sparc_LdSt);
......
......@@ -480,21 +480,29 @@ Ba => {
mode => "mode_X",
},
Start => {
state => "pinned",
out_arity => "variable",
ins => [],
},
# This is a JumpLink instruction, but with the addition that you can add custom
# register constraints to model your calling conventions
Return => {
state => "pinned",
op_flags => [ "cfopcode" ],
arity => "variable",
out_arity => "variable",
mode => "mode_X",
constructors => {
imm => {
attr => "ir_entity *entity, int32_t offset",
custominit => "\tsparc_set_attr_imm(res, entity, offset);",
arity => "variable",
out_arity => "variable",
reg_req => { out => [ "none" ] },
},
reg => {
arity => "variable",
out_arity => "variable",
reg_req => { out => [ "none" ] },
}
},
},
......
......@@ -61,16 +61,27 @@
DEBUG_ONLY(static firm_dbg_module_t *dbg = NULL;)
static beabi_helper_env_t *abihelper;
static const arch_register_t *sp_reg = &sparc_registers[REG_SP];
static const arch_register_t *fp_reg = &sparc_registers[REG_FRAME_POINTER];
static calling_convention_t *current_cconv = NULL;
static be_stackorder_t *stackorder;
static ir_mode *mode_gp;
static ir_mode *mode_flags;
static ir_mode *mode_fp;
static ir_mode *mode_fp2;
//static ir_mode *mode_fp4;
static pmap *node_to_stack;
static size_t start_mem_offset;
static ir_node *start_mem;
static size_t start_g0_offset;
static ir_node *start_g0;
static size_t start_sp_offset;
static ir_node *start_sp;
static size_t start_fp_offset;
static ir_node *start_fp;
static ir_node *frame_base;
static size_t start_params_offset;
static size_t start_callee_saves_offset;
static const arch_register_t *const caller_saves[] = {
&sparc_registers[REG_G1],
......@@ -439,9 +450,15 @@ static ir_node *gen_helper_binopx(ir_node *node, match_flags_t match_flags,
}
static ir_node *get_g0(void)
static ir_node *get_g0(ir_graph *irg)
{
return be_prolog_get_reg_value(abihelper, &sparc_registers[REG_G0]);
if (start_g0 == NULL) {
/* this is already the transformed start node */
ir_node *start = get_irg_start(irg);
assert(is_sparc_Start(start));
start_g0 = new_r_Proj(start, mode_gp, start_g0_offset);
}
return start_g0;
}
typedef struct address_t {
......@@ -832,7 +849,8 @@ static ir_node *gen_Div(ir_node *node)
new_right);
}
} else {
ir_node *left_high = get_g0();
ir_graph *irg = get_irn_irg(node);
ir_node *left_high = get_g0(irg);
if (is_imm_encodeable(right)) {
int32_t immediate = get_tarval_long(get_Const_tarval(right));
res = new_bd_sparc_UDiv_imm(dbgi, new_block, left_high, left_low,
......@@ -855,7 +873,8 @@ static ir_node *gen_Div(ir_node *node)
static ir_node *gen_Not(ir_node *node)
{
ir_node *op = get_Not_op(node);
ir_node *zero = get_g0();
ir_graph *irg = get_irn_irg(node);
ir_node *zero = get_g0(irg);
dbg_info *dbgi = get_irn_dbg_info(node);
ir_node *block = be_transform_node(get_nodes_block(node));
ir_node *new_op = be_transform_node(op);
......@@ -952,7 +971,7 @@ static ir_node *gen_Minus(ir_node *node)
dbgi = get_irn_dbg_info(node);
op = get_Minus_op(node);
new_op = be_transform_node(op);
zero = get_g0();
zero = get_g0(get_irn_irg(node));
return new_bd_sparc_Sub_reg(dbgi, block, zero, new_op);
}
......@@ -1014,9 +1033,10 @@ static ir_node *gen_Const(ir_node *node)
value = get_tarval_long(tv);
if (value == 0) {
return get_g0();
return get_g0(get_irn_irg(node));
} else if (sparc_is_value_imm_encodeable(value)) {
return new_bd_sparc_Or_imm(dbgi, block, get_g0(), NULL, value);
ir_graph *irg = get_irn_irg(node);
return new_bd_sparc_Or_imm(dbgi, block, get_g0(irg), NULL, value);
} else {
ir_node *hi = new_bd_sparc_SetHi(dbgi, block, NULL, value);
if ((value & 0x3ff) != 0) {
......@@ -1348,7 +1368,8 @@ static ir_node *gen_Unknown(ir_node *node)
ir_node *block = be_transform_node(get_nodes_block(node));
return gen_float_const(NULL, block, get_mode_null(mode));
} else if (mode_needs_gp_reg(mode)) {
return get_g0();
ir_graph *irg = get_irn_irg(node);
return get_g0(irg);
}
panic("Unexpected Unknown mode");
......@@ -1465,54 +1486,131 @@ static ir_node *gen_Start(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);
struct obstack *obst = be_get_be_obst(irg);
const arch_register_req_t *req;
size_t n_outs;
ir_node *start;
size_t i;
size_t o;
/* start building list of start constraints */
assert(obstack_object_size(obst) == 0);
/* calculate number of outputs */
n_outs = 3; /* memory, zero, sp */
if (!current_cconv->omit_fp)
++n_outs; /* framepointer */
/* function parameters */
n_outs += current_cconv->n_param_regs;
/* callee saves */
if (current_cconv->omit_fp) {
n_outs += ARRAY_SIZE(omit_fp_callee_saves);
}
start = new_bd_sparc_Start(dbgi, new_block, n_outs);
o = 0;
/* stackpointer is important at function prolog */
be_prolog_add_reg(abihelper, sp_reg,
/* first output is memory */
start_mem_offset = o;
arch_set_out_register_req(start, o++, arch_no_register_req);
/* the zero register */
start_g0_offset = o;
req = be_create_reg_req(obst, &sparc_registers[REG_G0],
arch_register_req_type_ignore);
arch_set_out_register_req(start, o, req);
arch_irn_set_register(start, o, &sparc_registers[REG_G0]);
++o;
/* we need an output for the stackpointer */
start_sp_offset = o;
req = be_create_reg_req(obst, sp_reg,
arch_register_req_type_produces_sp | arch_register_req_type_ignore);
be_prolog_add_reg(abihelper, &sparc_registers[REG_G0],
arch_register_req_type_ignore);
arch_set_out_register_req(start, o, req);
arch_irn_set_register(start, o, sp_reg);
++o;
if (!current_cconv->omit_fp) {
start_fp_offset = o;
req = be_create_reg_req(obst, fp_reg, arch_register_req_type_ignore);
arch_set_out_register_req(start, o, req);
arch_irn_set_register(start, o, fp_reg);
++o;
}
/* function parameters in registers */
start_params_offset = o;
for (i = 0; i < get_method_n_params(function_type); ++i) {
const reg_or_stackslot_t *param = &current_cconv->parameters[i];
if (param->reg0 != NULL) {
be_prolog_add_reg(abihelper, param->reg0,
arch_register_req_type_none);
const arch_register_t *reg0 = param->reg0;
const arch_register_t *reg1 = param->reg1;
if (reg0 != NULL) {
arch_set_out_register_req(start, o, reg0->single_req);
arch_irn_set_register(start, o, reg0);
++o;
}
if (param->reg1 != NULL) {
be_prolog_add_reg(abihelper, param->reg1,
arch_register_req_type_none);
if (reg1 != NULL) {
arch_set_out_register_req(start, o, reg1->single_req);
arch_irn_set_register(start, o, reg1);
++o;
}
}
/* we need the values of the callee saves (Note: non omit-fp mode has no
* callee saves) */
start_callee_saves_offset = o;
if (current_cconv->omit_fp) {
size_t n_callee_saves = ARRAY_SIZE(omit_fp_callee_saves);
size_t c;
for (c = 0; c < n_callee_saves; ++c) {
be_prolog_add_reg(abihelper, omit_fp_callee_saves[c],
arch_register_req_type_none);
const arch_register_t *reg = omit_fp_callee_saves[c];
arch_set_out_register_req(start, o, reg->single_req);
arch_irn_set_register(start, o, reg);
++o;
}
} else {
be_prolog_add_reg(abihelper, fp_reg, arch_register_req_type_ignore);
}
assert(n_outs == o);
start = be_prolog_create_start(abihelper, dbgi, new_block);
return start;
}
static ir_node *get_initial_sp(ir_graph *irg)
{
if (start_sp == NULL) {
ir_node *start = get_irg_start(irg);
start_sp = new_r_Proj(start, mode_gp, start_sp_offset);
}
return start_sp;
}
static ir_node *get_initial_fp(ir_graph *irg)
{
if (start_fp == NULL) {
ir_node *start = get_irg_start(irg);
start_fp = new_r_Proj(start, mode_gp, start_fp_offset);
}
return start_fp;
}
static ir_node *get_initial_mem(ir_graph *irg)
{
if (start_mem == NULL) {
ir_node *start = get_irg_start(irg);
start_mem = new_r_Proj(start, mode_M, start_mem_offset);
}
return start_mem;
}
static ir_node *get_stack_pointer_for(ir_node *node)
{
/* get predecessor in stack_order list */
ir_node *stack_pred = be_get_stack_pred(abihelper, node);
ir_node *stack_pred = be_get_stack_pred(stackorder, node);
ir_node *stack;
if (stack_pred == NULL) {
/* first stack user in the current block. We can simply use the
* initial sp_proj for it */
ir_node *sp_proj = be_prolog_get_reg_value(abihelper, sp_reg);
return sp_proj;
ir_graph *irg = get_irn_irg(node);
return get_initial_sp(irg);
}
be_transform_node(stack_pred);
......@@ -1530,22 +1628,37 @@ 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_graph *irg = get_irn_irg(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);
struct obstack *be_obst = be_get_be_obst(irg);
ir_node *bereturn;
ir_node **in;
const arch_register_req_t **reqs;
size_t i;
size_t p;
size_t n_ins;
/* estimate number of return values */
n_ins = 2 + n_res; /* memory + stackpointer, return values */
if (current_cconv->omit_fp)
n_ins += ARRAY_SIZE(omit_fp_callee_saves);
be_epilog_begin(abihelper);
be_epilog_set_memory(abihelper, new_mem);
/* connect stack pointer with initial stack pointer. fix_stack phase
will later serialize all stack pointer adjusting nodes */
be_epilog_add_reg(abihelper, sp_reg,
arch_register_req_type_produces_sp | arch_register_req_type_ignore,
sp);
in = ALLOCAN(ir_node*, n_ins);
reqs = OALLOCN(be_obst, const arch_register_req_t*, n_ins);
p = 0;
in[p] = new_mem;
reqs[p] = arch_no_register_req;
++p;
in[p] = sp;
reqs[p] = sp_reg->single_req;
++p;
/* result values */
for (i = 0; i < n_res; ++i) {
......@@ -1554,22 +1667,29 @@ static ir_node *gen_Return(ir_node *node)
const reg_or_stackslot_t *slot = &current_cconv->results[i];
const arch_register_t *reg = slot->reg0;
assert(slot->reg1 == NULL);
be_epilog_add_reg(abihelper, reg, arch_register_req_type_none,
new_res_value);
in[p] = new_res_value;
reqs[p] = reg->single_req;
++p;
}
/* callee saves */
if (current_cconv->omit_fp) {
size_t n_callee_saves = ARRAY_SIZE(omit_fp_callee_saves);
ir_node *start = get_irg_start(irg);
size_t n_callee_saves = ARRAY_SIZE(omit_fp_callee_saves);
for (i = 0; i < n_callee_saves; ++i) {
const arch_register_t *reg = omit_fp_callee_saves[i];
ir_mode *mode = reg->reg_class->mode;
ir_node *value
= be_prolog_get_reg_value(abihelper, reg);
be_epilog_add_reg(abihelper, reg, arch_register_req_type_none,
value);
= new_r_Proj(start, mode, i + start_callee_saves_offset);
in[p] = value;
reqs[p] = reg->single_req;
++p;
}
}
assert(p == n_ins);
bereturn = new_bd_sparc_Return_reg(dbgi, new_block, n_ins, in);
arch_set_in_register_reqs(bereturn, reqs);
bereturn = be_epilog_create_return(abihelper, dbgi, new_block);
return bereturn;
}
......@@ -1986,10 +2106,16 @@ static ir_node *gen_Proj_Div(ir_node *node)
panic("Unsupported Proj from Div");
}