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

be: Rewrite stack frame handling

This is a bigger rewrite of stack frame handling in the backend:

- Do the stack frame type layout late, after all the spill slots have
  been created. This way we can avoid (and remove) the brittle
  frame_alloc_area().
- Standardize the meaning of stack entity offsets. After stack frame type
  layout they are relative to the stack pointer position at the beginning
  of the function.  It is all in one type now, no splitting into "arg",
  "between" and "stack_frame" type anymore.
- Generalize the stack pointer simulation code to work with a single
  callback. Represent stack state as current offset and align_padding
  number. Now that callbacks can access and modify them both we do not
  need custom code in the sparc backend anymore.
- Remove alignment specification on IncSP, only keep a ignore_align flag
  used for the last IncSP that has to remove all existing sp adjustments
  and may end up on a misaligned SP on ia32/amd64.
- Align stack by default on ia32+amd64 backends.
parent f2b6fca4
......@@ -1592,21 +1592,6 @@ FIRM_API int is_frame_type(const ir_type *tp);
*/
FIRM_API ir_type *clone_frame_type(ir_type *type);
/**
* Allocate an area of size bytes aligned at alignment
* at the start or the end of a frame type.
* The frame type must already have a fixed layout.
*
* @param frame_type a frame type
* @param size the size of the entity
* @param alignment the alignment of the entity
* @param at_start if true, put the area at the frame type's start, else at end
*
* @return the entity representing the area
*/
FIRM_API ir_entity *frame_alloc_area(ir_type *frame_type, int size,
unsigned alignment, int at_start);
/** @} */
/** @defgroup segment_type Segment
......
......@@ -45,18 +45,6 @@ pmap *amd64_constants;
ir_mode *amd64_mode_xmm;
static ir_entity *amd64_get_frame_entity(const ir_node *node)
{
if (!is_amd64_irn(node))
return NULL;
if (!amd64_has_addr_attr(get_amd64_attr_const(node)->op_mode))
return NULL;
const amd64_addr_attr_t *attr = get_amd64_addr_attr_const(node);
if (attr->addr.immediate.kind != X86_IMM_FRAMEENT)
return NULL;
return attr->addr.immediate.entity;
}
static int get_insn_size_bytes(amd64_insn_size_t size)
{
switch (size) {
......@@ -70,44 +58,6 @@ static int get_insn_size_bytes(amd64_insn_size_t size)
panic("bad insn mode");
}
/**
* This function is called by the generic backend to correct offsets for
* nodes accessing the stack.
*/
static void amd64_set_frame_offset(ir_node *node, int offset)
{
if (!is_amd64_irn(node))
return;
amd64_addr_attr_t *attr = get_amd64_addr_attr(node);
attr->addr.immediate.offset += offset;
if (is_amd64_pop_am(node)) {
ir_graph *irg = get_irn_irg(node);
if (amd64_get_irg_data(irg)->omit_fp)
attr->addr.immediate.offset -= get_insn_size_bytes(attr->size);
}
assert(attr->addr.immediate.kind == X86_IMM_FRAMEENT);
attr->addr.immediate.kind = X86_IMM_VALUE;
attr->addr.immediate.entity = NULL;
}
static int amd64_get_sp_bias(const ir_node *node)
{
if (is_amd64_push_am(node)) {
const amd64_addr_attr_t *attr = get_amd64_addr_attr_const(node);
return get_insn_size_bytes(attr->size);
} else if (is_amd64_push_reg(node)) {
/* 64-bit register size */
return AMD64_REGISTER_SIZE;
} else if (is_amd64_pop_am(node)) {
const amd64_addr_attr_t *attr = get_amd64_addr_attr_const(node);
return -get_insn_size_bytes(attr->size);
} else if (is_amd64_leave(node)) {
return SP_BIAS_RESET;
}
return 0;
}
static ir_node *create_push(ir_node *node, ir_node *schedpoint, ir_node *sp,
ir_node *mem, ir_entity *ent,
amd64_insn_size_t size)
......@@ -171,15 +121,15 @@ static void transform_MemPerm(ir_node *node)
ir_node *sp = be_get_Start_proj(irg, &amd64_registers[REG_RSP]);
int arity = be_get_MemPerm_entity_arity(node);
ir_node **pops = ALLOCAN(ir_node*, arity);
int i;
/* create Pushs */
for (i = 0; i < arity; ++i) {
for (int i = 0; i < arity; ++i) {
ir_entity *inent = be_get_MemPerm_in_entity(node, i);
ir_entity *outent = be_get_MemPerm_out_entity(node, i);
ir_type *enttype = get_entity_type(inent);
unsigned entsize = get_type_size(enttype);
unsigned entsize2 = get_type_size(get_entity_type(outent));
assert(inent->kind == IR_ENTITY_SPILLSLOT);
assert(outent->kind == IR_ENTITY_SPILLSLOT);
unsigned entsize = inent->attr.spillslot.size;
unsigned entsize2 = outent->attr.spillslot.size;
ir_node *mem = get_irn_n(node, i);
/* work around cases where entities have different sizes */
......@@ -212,12 +162,13 @@ static void transform_MemPerm(ir_node *node)
}
/* create pops */
for (i = arity; i-- > 0; ) {
for (int i = arity; i-- > 0; ) {
ir_entity *inent = be_get_MemPerm_in_entity(node, i);
ir_entity *outent = be_get_MemPerm_out_entity(node, i);
ir_type *enttype = get_entity_type(outent);
unsigned entsize = get_type_size(enttype);
unsigned entsize2 = get_type_size(get_entity_type(inent));
assert(inent->kind == IR_ENTITY_SPILLSLOT);
assert(outent->kind == IR_ENTITY_SPILLSLOT);
unsigned entsize = outent->attr.spillslot.size;
unsigned entsize2 = inent->attr.spillslot.size;
/* work around cases where entities have different sizes */
if (entsize2 < entsize)
......@@ -521,12 +472,10 @@ static void amd64_select_instructions(ir_graph *irg)
static void introduce_epilogue(ir_node *ret, bool omit_fp)
{
ir_graph *irg = get_irn_irg(ret);
ir_node *block = get_nodes_block(ret);
ir_type *frame_type = get_irg_frame_type(irg);
unsigned frame_size = get_type_size(frame_type);
ir_node *first_sp = get_irn_n(ret, n_amd64_ret_stack);
ir_node *curr_sp = first_sp;
ir_graph *irg = get_irn_irg(ret);
ir_node *block = get_nodes_block(ret);
ir_node *first_sp = get_irn_n(ret, n_amd64_ret_stack);
ir_node *curr_sp = first_sp;
if (!omit_fp) {
int const n_rbp = determine_rbp_input(ret);
......@@ -541,12 +490,12 @@ static void introduce_epilogue(ir_node *ret, bool omit_fp)
set_irn_n(ret, n_amd64_ret_mem, curr_mem);
set_irn_n(ret, n_rbp, curr_bp);
} else {
if (frame_size > 0) {
ir_node *incsp = amd64_new_IncSP(block, curr_sp,
-(int)frame_size, 0);
sched_add_before(ret, incsp);
curr_sp = incsp;
}
ir_type *frame_type = get_irg_frame_type(irg);
unsigned frame_size = get_type_size(frame_type);
ir_node *incsp = amd64_new_IncSP(block, curr_sp, -(int)frame_size,
true);
sched_add_before(ret, incsp);
curr_sp = incsp;
}
set_irn_n(ret, n_amd64_ret_stack, curr_sp);
......@@ -582,22 +531,17 @@ static void introduce_prologue(ir_graph *const irg, bool omit_fp)
arch_copy_irn_out_info(curr_bp, 0, initial_bp);
edges_reroute_except(initial_bp, curr_bp, push);
ir_node *incsp = amd64_new_IncSP(block, curr_sp, frame_size, 0);
ir_node *incsp = amd64_new_IncSP(block, curr_sp, frame_size, false);
sched_add_after(curr_bp, incsp);
edges_reroute_except(initial_sp, incsp, push);
/* make sure the initial IncSP is really used by someone */
be_keep_if_unused(incsp);
be_stack_layout_t *const layout = be_get_irg_stack_layout(irg);
layout->initial_bias = -8;
} else {
if (frame_size > 0) {
ir_node *const incsp = amd64_new_IncSP(block, initial_sp,
frame_size, 0);
sched_add_after(start, incsp);
edges_reroute_except(initial_sp, incsp, incsp);
}
ir_node *const incsp = amd64_new_IncSP(block, initial_sp,
frame_size, false);
sched_add_after(start, incsp);
edges_reroute_except(initial_sp, incsp, incsp);
}
}
......@@ -612,19 +556,85 @@ static void introduce_prologue_epilogue(ir_graph *irg, bool omit_fp)
introduce_prologue(irg, omit_fp);
}
static bool node_has_sp_base(ir_node const *const node,
amd64_addr_t const *const addr)
{
if (!x86_addr_variant_has_base(addr->variant))
return false;
arch_register_t const *const base_reg
= arch_get_irn_register_in(node, addr->base_input);
return base_reg == &amd64_registers[REG_RSP];
}
static void amd64_determine_frameoffset(ir_node *node, int sp_offset)
{
if (!is_amd64_irn(node)
|| !amd64_has_addr_attr(get_amd64_attr_const(node)->op_mode))
return;
amd64_addr_t *const addr = &get_amd64_addr_attr(node)->addr;
if (addr->immediate.kind == X86_IMM_FRAMEENT) {
addr->immediate.offset += get_entity_offset(addr->immediate.entity);
addr->immediate.entity = NULL;
addr->immediate.kind = X86_IMM_FRAMEOFFSET;
}
if (addr->immediate.kind == X86_IMM_FRAMEOFFSET) {
if (node_has_sp_base(node, addr))
addr->immediate.offset += sp_offset;
else {
/* we calculate offsets relative to the SP value at function begin,
* but RBP points after the saved old frame pointer */
addr->immediate.offset += AMD64_REGISTER_SIZE;
}
addr->immediate.kind = X86_IMM_VALUE;
}
}
static void amd64_sp_sim(ir_node *const node, stack_pointer_state_t *state)
{
/* Pop nodes modify the stack pointer before calculating destination
* address, so do this first */
if (is_amd64_pop_am(node)) {
const amd64_addr_attr_t *attr = get_amd64_addr_attr_const(node);
state->offset -= get_insn_size_bytes(attr->size);
}
amd64_determine_frameoffset(node, state->offset);
if (is_amd64_push_am(node)) {
const amd64_addr_attr_t *attr = get_amd64_addr_attr_const(node);
state->offset += get_insn_size_bytes(attr->size);
} else if (is_amd64_push_reg(node)) {
/* 64-bit register size */
state->offset += AMD64_REGISTER_SIZE;
} else if (is_amd64_leave(node)) {
state->offset = 0;
state->align_padding = 0;
} else if (is_amd64_sub_sp(node)) {
state->align_padding = 0;
}
}
/**
* Called immediatly before emit phase.
*/
static void amd64_finish_and_emit(ir_graph *irg)
{
bool omit_fp = amd64_get_irg_data(irg)->omit_fp;
be_fec_env_t *fec_env = be_new_frame_entity_coalescer(irg);
bool omit_fp = amd64_get_irg_data(irg)->omit_fp;
/* create and coalesce frame entities */
be_fec_env_t *fec_env = be_new_frame_entity_coalescer(irg);
irg_walk_graph(irg, NULL, amd64_collect_frame_entity_nodes, fec_env);
be_assign_entities(fec_env, amd64_set_frame_entity, omit_fp);
be_free_frame_entity_coalescer(fec_env);
ir_type *const frame = get_irg_frame_type(irg);
be_sort_frame_entities(frame, omit_fp);
unsigned const misalign = AMD64_REGISTER_SIZE; /* return address on stack */
int const begin = omit_fp ? 0 : -AMD64_REGISTER_SIZE;
be_layout_frame_type(frame, begin, misalign);
irg_block_walk_graph(irg, NULL, amd64_after_ra_walker, NULL);
introduce_prologue_epilogue(irg, omit_fp);
......@@ -632,8 +642,8 @@ static void amd64_finish_and_emit(ir_graph *irg)
/* fix stack entity offsets */
be_fix_stack_nodes(irg, &amd64_registers[REG_RSP]);
be_birg_from_irg(irg)->non_ssa_regs = NULL;
be_abi_fix_stack_bias(irg, amd64_get_sp_bias, amd64_set_frame_offset,
amd64_get_frame_entity);
unsigned const p2align = AMD64_PO2_STACK_ALIGNMENT;
be_sim_stack_pointer(irg, misalign, p2align, amd64_sp_sim);
/* Fix 2-address code constraints. */
amd64_finish_irg(irg);
......
......@@ -28,8 +28,6 @@
#include "irgwalk.h"
#include "panic.h"
static be_stack_layout_t *layout;
static char get_gp_size_suffix(amd64_insn_size_t const size)
{
switch (size) {
......@@ -229,6 +227,7 @@ static void emit_relocation_no_offset(x86_immediate_kind_t const kind,
case X86_IMM_TLS_LE:
case X86_IMM_PICBASE_REL:
case X86_IMM_FRAMEENT:
case X86_IMM_FRAMEOFFSET:
case X86_IMM_GOT:
case X86_IMM_GOTOFF:
break;
......@@ -263,14 +262,6 @@ static void amd64_emit_immediate32(bool const prefix,
be_emit_irprintf("%+" PRId32, imm->offset);
}
#ifndef NDEBUG
static bool is_fp_relative(const ir_entity *entity)
{
ir_type *owner = get_entity_owner(entity);
return is_frame_type(owner) || owner == layout->arg_type;
}
#endif
static void amd64_emit_addr(const ir_node *const node,
const amd64_addr_t *const addr)
{
......@@ -281,7 +272,7 @@ static void amd64_emit_addr(const ir_node *const node,
if (entity != NULL) {
assert(addr->immediate.kind != X86_IMM_VALUE);
assert(!is_fp_relative(entity));
assert(!is_frame_type(get_entity_owner(entity)));
emit_relocation_no_offset(addr->immediate.kind, entity);
if (offset != 0)
be_emit_irprintf("%+" PRId32, offset);
......@@ -961,8 +952,6 @@ void amd64_emit_function(ir_graph *irg)
{
ir_entity *entity = get_irg_entity(irg);
layout = be_get_irg_stack_layout(irg);
/* register all emitter functions */
amd64_register_emitters();
......
......@@ -524,10 +524,10 @@ static ir_node *gen_be_Relocation(ir_node *const node)
}
ir_node *amd64_new_IncSP(ir_node *block, ir_node *old_sp, int offset,
unsigned align)
bool no_align)
{
ir_node *incsp = be_new_IncSP(&amd64_registers[REG_RSP], block, old_sp,
offset, align);
offset, no_align);
arch_add_irn_flags(incsp, arch_irn_flag_modify_flags);
return incsp;
}
......@@ -1776,10 +1776,10 @@ static ir_node *gen_Call(ir_node *const node)
/* stack pointer input */
/* construct an IncSP -> we have to always be sure that the stack is
* aligned even if we don't push arguments on it */
ir_node *const stack = get_initial_sp(irg);
ir_node *const callframe
= amd64_new_IncSP(new_block, stack, cconv->param_stacksize,
AMD64_PO2_STACK_ALIGNMENT);
ir_node *const stack = get_initial_sp(irg);
unsigned param_stacksize = cconv->param_stacksize;
ir_node *callframe = param_stacksize == 0 ? stack
: amd64_new_IncSP(new_block, stack, param_stacksize, false);
/* match callee */
amd64_addr_t addr;
......@@ -1849,11 +1849,11 @@ static ir_node *gen_Call(ir_node *const node)
}
}
sync_ins[sync_arity++] = be_transform_node(mem);
no_call_mem:
no_call_mem:;
in_req[in_arity] = amd64_registers[REG_RSP].single_req;
in[in_arity] = callframe;
++in_arity;
int const call_sp_pos = in_arity++;
in_req[call_sp_pos] = amd64_registers[REG_RSP].single_req;
in[call_sp_pos] = callframe;
/* vararg calls need the number of SSE registers used */
if (is_method_variadic(type)) {
......@@ -1974,9 +1974,19 @@ no_call_mem:
/* IncSP to destroy the call stackframe */
ir_node *const call_stack = be_new_Proj(call, pn_amd64_call_stack);
ir_node *const incsp = amd64_new_IncSP(new_block, call_stack,
-cconv->param_stacksize, 0);
be_stack_record_chain(&stack_env, callframe, n_be_IncSP_pred, incsp);
ir_node *sp;
ir_node *first_sp_node;
unsigned first_sp_pos;
if (param_stacksize > 0) {
sp = amd64_new_IncSP(new_block, call_stack, -param_stacksize, false);
first_sp_node = callframe;
first_sp_pos = n_be_IncSP_pred;
} else {
sp = call_stack;
first_sp_node = call;
first_sp_pos = call_sp_pos;
}
be_stack_record_chain(&stack_env, first_sp_node, first_sp_pos, sp);
x86_free_calling_convention(cconv);
return call;
......@@ -3250,64 +3260,6 @@ static void amd64_register_transformers(void)
be_set_upper_bits_clean_function(op_Shrs, NULL);
}
static ir_type *amd64_get_between_type(bool omit_fp)
{
static ir_type *between_type = NULL;
static ir_type *omit_fp_between_type = NULL;
if (between_type == NULL) {
between_type = new_type_class(new_id_from_str("amd64_between_type"));
/* between type contains return address + saved base pointer */
set_type_size(between_type, 2*get_mode_size_bytes(mode_gp));
omit_fp_between_type
= new_type_class(new_id_from_str("amd64_between_type"));
/* between type contains return address */
set_type_size(omit_fp_between_type, get_mode_size_bytes(mode_gp));
}
return omit_fp ? omit_fp_between_type : between_type;
}
static void amd64_create_stacklayout(ir_graph *irg, const x86_cconv_t *cconv)
{
/* construct argument type */
ir_entity *const entity = get_irg_entity(irg);
ident *const arg_id = new_id_fmt("%s_arg_type", get_entity_ident(entity));
ir_type *const arg_type = new_type_struct(arg_id);
for (size_t p = 0, n_params = cconv->n_parameters; p < n_params; ++p) {
reg_or_stackslot_t *param = &cconv->parameters[p];
if (param->type == NULL)
continue;
ident *const id = new_id_fmt("param_%u", (unsigned)p);
param->entity = new_entity(arg_type, id, param->type);
set_entity_offset(param->entity, param->offset);
}
ir_type *const function_type = get_entity_type(entity);
if (is_method_variadic(function_type)) {
ir_entity *stack_args_param = new_parameter_entity(
arg_type, IR_VA_START_PARAMETER_NUMBER, get_unknown_type());
set_entity_offset(stack_args_param, cconv->param_stacksize);
amd64_set_va_stack_args_param(stack_args_param);
}
be_stack_layout_t *const layout = be_get_irg_stack_layout(irg);
memset(layout, 0, sizeof(*layout));
layout->frame_type = get_irg_frame_type(irg);
layout->between_type = amd64_get_between_type(cconv->omit_fp);
layout->arg_type = arg_type;
layout->initial_offset = 0;
layout->initial_bias = 0;
layout->sp_relative = cconv->omit_fp;
assert(N_FRAME_TYPES == 3);
layout->order[0] = layout->frame_type;
layout->order[1] = layout->between_type;
layout->order[2] = layout->arg_type;
}
void amd64_transform_graph(ir_graph *irg)
{
assure_irg_properties(irg, IR_GRAPH_PROPERTY_NO_TUPLES
......@@ -3324,7 +3276,8 @@ void amd64_transform_graph(ir_graph *irg)
bool const is_variadic = is_method_variadic(mtp);
if (is_variadic)
amd64_insert_reg_save_area(irg, current_cconv);
amd64_create_stacklayout(irg, current_cconv);
x86_layout_param_entities(irg, current_cconv, AMD64_REGISTER_SIZE);
amd64_set_va_stack_args_param(current_cconv->va_start_addr);
be_add_parameter_entity_stores(irg);
x86_create_parameter_loads(irg, current_cconv);
......@@ -3337,10 +3290,6 @@ void amd64_transform_graph(ir_graph *irg)
be_stack_finish(&stack_env);
ir_type *frame_type = get_irg_frame_type(irg);
if (get_type_state(frame_type) == layout_undefined)
default_layout_compound_type(frame_type);
if (is_variadic) {
ir_node *const fp = get_frame_base(irg);
amd64_save_vararg_registers(irg, current_cconv, fp);
......
......@@ -35,7 +35,7 @@ ir_node *amd64_new_reload(ir_node *value, ir_node *spill, ir_node *before);
void amd64_transform_graph(ir_graph *irg);
ir_node *amd64_new_IncSP(ir_node *block, ir_node *old_sp, int offset,
unsigned align);
bool no_align);
/**
* Creates an entity for a constant floating point value.
......
......@@ -540,7 +540,9 @@ static void emit_be_MemPerm(const ir_node *node)
if (memperm_arity > 12)
panic("memperm with more than 12 inputs not supported yet");
int sp_change = 0;
int const memperm_offset = be_get_MemPerm_offset(node);
int sp_change = memperm_offset;
for (int i = 0; i < memperm_arity; ++i) {
/* spill register */
arm_emitf(node, "str r%d, [sp, #-4]!", i);
......@@ -560,7 +562,7 @@ static void emit_be_MemPerm(const ir_node *node)
arm_emitf(node, "ldr r%d, [sp], #4", i);
sp_change -= 4;
}
assert(sp_change == 0);
assert(sp_change == memperm_offset);
}
static void emit_arm_Jmp(const ir_node *node)
......
......@@ -66,7 +66,8 @@ static void introduce_epilog(ir_node *ret)
ir_graph *const irg = get_irn_irg(ret);
ir_type *const frame_type = get_irg_frame_type(irg);
unsigned const frame_size = get_type_size(frame_type);
ir_node *const incsp = be_new_IncSP(sp_reg, block, sp, -frame_size, 0);
ir_node *const incsp = be_new_IncSP(sp_reg, block, sp, -frame_size,
true);
set_irn_n(ret, n_arm_Return_sp, incsp);
sched_add_before(ret, incsp);
}
......@@ -86,72 +87,61 @@ static void introduce_prolog_epilog(ir_graph *irg)
ir_type *frame_type = get_irg_frame_type(irg);
unsigned frame_size = get_type_size(frame_type);
ir_node *const incsp = be_new_IncSP(sp_reg, block, initial_sp, frame_size, 0);
ir_node *const incsp = be_new_IncSP(sp_reg, block, initial_sp, frame_size,
false);
edges_reroute_except(initial_sp, incsp, incsp);
sched_add_after(start, incsp);
}
/**
* This function is called by the generic backend to correct offsets for
* nodes accessing the stack.
*/
static void arm_set_frame_offset(ir_node *irn, int bias)
{
if (be_is_MemPerm(irn)) {
be_set_MemPerm_offset(irn, bias);
} else if (is_arm_FrameAddr(irn)) {
arm_Address_attr_t *attr = get_arm_Address_attr(irn);
attr->fp_offset += bias;
} else {
arm_load_store_attr_t *attr = get_arm_load_store_attr(irn);
assert(attr->base.is_load_store);
attr->offset += bias;
}
}
static int arm_get_sp_bias(const ir_node *node)
{
(void)node;
return 0;
}
static ir_entity *arm_get_frame_entity(const ir_node *irn)
static void arm_determine_frameoffset(ir_node *node, int sp_offset)
{
if (be_is_MemPerm(irn))
return be_get_MemPerm_in_entity(irn, 0);
if (!is_arm_irn(irn))
return NULL;
const arm_attr_t *attr = get_arm_attr_const(irn);
if (is_arm_FrameAddr(irn)) {
const arm_Address_attr_t *frame_attr = get_arm_Address_attr_const(irn);
return frame_attr->entity;
}
if (attr->is_load_store) {
const arm_load_store_attr_t *load_store_attr
= get_arm_load_store_attr_const(irn);
if (!is_arm_irn(node))
return;
const arm_attr_t *attr = get_arm_attr_const(node);
if (is_arm_FrameAddr(node)) {
arm_Address_attr_t *const attr = get_arm_Address_attr(node);
ir_entity const *const entity = attr->entity;
if (entity != NULL)
attr->fp_offset += get_entity_offset(entity);
attr->fp_offset += sp_offset;
} else if (attr->is_load_store) {
arm_load_store_attr_t *const load_store_attr
= get_arm_load_store_attr(node);
if (load_store_attr->is_frame_entity) {
return load_store_attr->entity;
ir_entity const *const entity = load_store_attr->entity;
if (entity != NULL)
load_store_attr->offset += get_entity_offset(entity);
load_store_attr->offset += sp_offset;
}
}
return NULL;
}
static void arm_sp_sim(ir_node *const node, stack_pointer_state_t *state)
{