/* * This file is part of libFirm. * Copyright (C) 2012 University of Karlsruhe. */ /** * @file * @author Sebastian Hack, Matthias Braun * * Handling of the stack frame. It is composed of three types: * 1) The type of the arguments which are pushed on the stack. * 2) The "between type" which consists of stuff the call of the * function pushes on the stack (like the return address and * the old base pointer for ia32). * 3) The Firm frame type which consists of all local variables * and the spills. */ #include "bestack.h" #include "beirg.h" #include "benode.h" #include "besched.h" #include "bessaconstr.h" #include "ircons_t.h" #include "iredges_t.h" #include "irgmod.h" #include "irgwalk.h" #include "irnode_t.h" #include "util.h" static unsigned round_up2_misaligned(unsigned const offset, unsigned const alignment, unsigned const misalign) { return round_up2(offset + misalign, alignment) - misalign; } static void sim_be_IncSP(ir_node *node, stack_pointer_state_t *state) { int const ofs = be_get_IncSP_offset(node); unsigned const p2align = be_get_IncSP_no_align(node) ? 0 : state->p2align; unsigned const alignment = 1u << p2align; int const prev_offset = state->offset; int const new_offset = prev_offset - state->align_padding + ofs; int const aligned = round_up2_misaligned(new_offset, alignment, state->misalign); be_set_IncSP_offset(node, aligned - prev_offset); state->align_padding = aligned - new_offset; state->offset = aligned; } /** * Simulate stack pointer offset relative to offset at function begin. */ static void process_stack_bias(sp_sim_func const sim, ir_node *const block, unsigned const p2align, unsigned const misalign, int offset, unsigned align_padding) { /* TODO: We really should check that offset corresponds to the one we got * last time when the block was already visited. Add a map in debug mode? */ if (Block_block_visited(block)) return; mark_Block_block_visited(block); stack_pointer_state_t state = { .misalign = misalign, .p2align = p2align, .offset = offset, .align_padding = align_padding, }; sched_foreach(block, node) { if (be_is_IncSP(node)) { sim_be_IncSP(node, &state); } else { sim(node, &state); } } /* Continue at our control flow successors. */ foreach_block_succ(block, edge) { ir_node *succ = get_edge_src_irn(edge); process_stack_bias(sim, succ, p2align, misalign, state.offset, state.align_padding); } } void be_sim_stack_pointer(ir_graph *const irg, unsigned const misalign, unsigned const p2align, sp_sim_func const sim) { ir_node *const start_block = get_irg_start_block(irg); ir_reserve_resources(irg, IR_RESOURCE_BLOCK_VISITED); inc_irg_block_visited(irg); process_stack_bias(sim, start_block, p2align, misalign, 0, 0); ir_free_resources(irg, IR_RESOURCE_BLOCK_VISITED); } typedef struct fix_stack_walker_env_t { const arch_register_t *sp; ir_node **sp_nodes; } fix_stack_walker_env_t; /** * Walker. Collect all stack modifying nodes. */ static void collect_stack_nodes_walker(ir_node *node, void *data) { fix_stack_walker_env_t *const env = (fix_stack_walker_env_t*)data; if (get_irn_mode(node) != mode_T && arch_get_irn_register(node) == env->sp) ARR_APP1(ir_node*, env->sp_nodes, node); } void be_fix_stack_nodes(ir_graph *const irg, arch_register_t const *const sp) { be_irg_t *const birg = be_birg_from_irg(irg); bool const ignore = !rbitset_is_set(birg->allocatable_regs, sp->global_index); arch_register_req_t const *const sp_req = be_create_reg_req(irg, sp, ignore); fix_stack_walker_env_t walker_env; walker_env.sp = sp; walker_env.sp_nodes = NEW_ARR_F(ir_node*, 0); irg_walk_graph(irg, collect_stack_nodes_walker, NULL, &walker_env); /* nothing to be done if we didn't find any node, in fact we mustn't * continue, as for endless loops incsp might have had no users and is bad * now. */ size_t n_sp_nodes = ARR_LEN(walker_env.sp_nodes); if (n_sp_nodes == 0) { DEL_ARR_F(walker_env.sp_nodes); return; } be_ssa_construction_env_t senv; be_ssa_construction_init(&senv, irg); be_ssa_construction_add_copies(&senv, walker_env.sp_nodes, n_sp_nodes); be_ssa_construction_fix_users_array(&senv, walker_env.sp_nodes, n_sp_nodes); be_lv_t *const lv = be_get_irg_liveness(irg); if (lv->sets_valid) { for (size_t i = 0; i < n_sp_nodes; ++i) { be_liveness_update(lv, walker_env.sp_nodes[i]); } be_ssa_construction_update_liveness_phis(&senv, lv); } ir_node **phis = be_ssa_construction_get_new_phis(&senv); /* set register requirements for stack phis */ for (size_t i = 0, n_phis = ARR_LEN(phis); i < n_phis; ++i) { ir_node *phi = phis[i]; be_set_phi_reg_req(phi, sp_req); arch_set_irn_register(phi, sp); } be_ssa_construction_destroy(&senv); DEL_ARR_F(walker_env.sp_nodes); /* when doing code with frame-pointers then often the last sp producers are * not used anymore because we copy the framepointer to the stack pointer * when leaving the function. Though the last sp producer is often kept * (because you often don't know which sp producer is the last one and * fixstack should find them all). Remove unnecessary keep edges and sp * producers. */ ir_node *end = get_irg_end(irg); foreach_irn_in_r(end, i, in) { if (get_irn_mode(in) != mode_T && arch_get_irn_register(in) == sp) { remove_End_n(end, i); if (get_irn_n_edges(in) == 0) { if (!is_Proj(in)) sched_remove(in); kill_node(in); } } } } static int cmp_slots_last(void const *const p0, void const *const p1) { ir_entity const *const e0 = *(ir_entity const**)p0; ir_entity const *const e1 = *(ir_entity const**)p1; if (e0->kind == IR_ENTITY_SPILLSLOT) { if (e1->kind != IR_ENTITY_SPILLSLOT) return -1; } else if (e1->kind == IR_ENTITY_SPILLSLOT) return 1; return QSORT_CMP(e1->nr, e0->nr); } static int cmp_slots_first(void const *const p0, void const *const p1) { ir_entity const *const e0 = *(ir_entity const**)p0; ir_entity const *const e1 = *(ir_entity const**)p1; if (e0->kind == IR_ENTITY_SPILLSLOT) { if (e1->kind != IR_ENTITY_SPILLSLOT) return 1; } else if (e1->kind == IR_ENTITY_SPILLSLOT) return -1; return QSORT_CMP(e0->nr, e1->nr); } void be_sort_frame_entities(ir_type *const frame, bool spillslots_first) { unsigned const n_members = get_compound_n_members(frame); assert(is_compound_type(frame)); QSORT(frame->attr.compound.members, n_members, spillslots_first ? cmp_slots_first : cmp_slots_last); } void be_layout_frame_type(ir_type *const frame, int const begin, unsigned const misalign) { assert(get_type_state(frame) == layout_undefined); /* Layout entities into negative direction. */ int offset = begin; for (unsigned i = 0, n_members = get_compound_n_members(frame); i < n_members; ++i) { ir_entity *const member = get_compound_member(frame, i); int const member_offset = get_entity_offset(member); if (member_offset != INVALID_OFFSET) { assert(member_offset >= begin); continue; } assert(get_entity_bitfield_size(member) == 0); unsigned size; unsigned alignment = get_entity_alignment(member); if (member->kind == IR_ENTITY_SPILLSLOT) { size = member->attr.spillslot.size; } else { ir_type const *const type = get_entity_type(member); unsigned const type_alignment = get_type_alignment(type); size = get_type_size(type); alignment = MAX(alignment, type_alignment); } offset -= size; offset = -round_up2_misaligned(-offset, alignment, misalign); set_entity_offset(member, offset); } set_type_size(frame, -(offset-begin)); set_type_state(frame, layout_fixed); }