summaryrefslogtreecommitdiffhomepage
path: root/ir/be/sparc/sparc_stackframe.c
blob: 8bb805c986f466d5a5b4ecee8389cdad32946d59 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
/*
 * This file is part of libFirm.
 * Copyright (C) 2012 University of Karlsruhe.
 */

/**
 * @file
 * @brief   Manage addressing into the stackframe
 * @author  Matthias Braun
 *
 * Logical sparc stacklayout (omitfp==false), situation after a call:
 *
 *        high address |-----------------------------|
 *                     |            ...              |
 *                     |         stackarg 1          |
 *                     |         stackarg 0          |
 * SPARC_MIN_STACKSIZE |-----------------------------|  entity offset 0
 *                     | space for storing regarg0-5 |
 *                     | pointer to aggregate return |
 *                     |      16 words save area     |
 *    stack pointer -> |-----------------------------|
 *                     |    high end of stackframe   |
 *                     |            ...              |
 *                     |    low end of stackframe    |
 *        low address  |-----------------------------|
 */
#include "beirg.h"
#include "benode.h"
#include "besched.h"
#include "bestack.h"
#include "betranshlp.h"
#include "bevarargs.h"
#include "bitfiddle.h"
#include "firm_types.h"
#include "iredges_t.h"
#include "irnode_t.h"
#include "panic.h"
#include "sparc_bearch_t.h"
#include "sparc_cconv.h"
#include "sparc_new_nodes.h"

ir_entity *sparc_get_frame_entity(const ir_node *node)
{
	if (be_is_MemPerm(node))
		return be_get_MemPerm_in_entity(node, 0);
	if (is_sparc_FrameAddr(node)) {
		const sparc_attr_t *attr = get_sparc_attr_const(node);
		return attr->immediate_value_entity;
	}

	if (sparc_has_load_store_attr(node)) {
		const sparc_load_store_attr_t *load_store_attr
			= get_sparc_load_store_attr_const(node);
		if (load_store_attr->is_frame_entity) {
			return load_store_attr->base.immediate_value_entity;
		}
	}

	return NULL;
}

static bool node_has_sp_base(ir_node const *const node)
{
	int input;
	if (is_sparc_FrameAddr(node)) {
		input = n_sparc_FrameAddr_base;
	} else if (is_sparc_Ld(node)) {
		input = n_sparc_Ld_ptr;
	} else if (is_sparc_St(node)) {
		input = n_sparc_St_ptr;
	} else if (is_sparc_Ldf(node)) {
		input = n_sparc_Ldf_ptr;
	} else if (is_sparc_Stf(node)) {
		input = n_sparc_Stf_ptr;
	} else {
		panic("Unexpected node %+F", node);
	}
	arch_register_t const *const reg = arch_get_irn_register_in(node, input);
	return reg == &sparc_registers[REG_SP];
}

static void sparc_determine_frameoffset(ir_node *const node,
                                        int const sp_offset)
{
	if (is_sparc_FrameAddr(node)) {
		sparc_attr_t    *const attr   = get_sparc_attr(node);
		ir_entity const *const entity = attr->immediate_value_entity;
		if (entity != NULL) {
			attr->immediate_value += get_entity_offset(entity);
			if (node_has_sp_base(node))
				attr->immediate_value += sp_offset + SPARC_MIN_STACKSIZE;
		}
	} else if (sparc_has_load_store_attr(node)) {
		sparc_load_store_attr_t *const attr = get_sparc_load_store_attr(node);
		if (!attr->is_frame_entity)
			return;
		ir_entity const *const entity
			= attr->base.immediate_value_entity;
		if (entity != NULL) {
			attr->base.immediate_value += get_entity_offset(entity);
			if (node_has_sp_base(node))
				attr->base.immediate_value += sp_offset + SPARC_MIN_STACKSIZE;
		}
	} else if (be_is_MemPerm(node)) {
		ir_graph *irg = get_irn_irg(node);
		if (sparc_get_irg_data(irg)->omit_fp)
			be_set_MemPerm_offset(node, sp_offset + SPARC_MIN_STACKSIZE);
	}
}

static void sparc_sp_sim(ir_node *const node, stack_pointer_state_t *state)
{
	sparc_determine_frameoffset(node, state->offset);

	if (is_sparc_Save(node)) {
		sparc_attr_t *const attr = get_sparc_attr(node);
		if (get_irn_arity(node) == 3)
			panic("no support for _reg variant yet");

		/* Adjust for alignment */
		assert(state->misalign == 0);
		int const prev_offset = state->offset;
		int const new_offset  = prev_offset - state->align_padding
		                        - attr->immediate_value;
		int const aligned     = round_up2(new_offset, 1u << state->p2align);
		attr->immediate_value = -(aligned - prev_offset);
		state->align_padding  = aligned - new_offset;
		state->offset         = aligned;
	} else if (is_sparc_SubSP(node) || is_sparc_AddSP(node)) {
		state->align_padding = 0;
	} else if (is_sparc_RestoreZero(node)) {
		state->offset        = 0;
		state->align_padding = 0;
	}
}

void sparc_fix_stack_bias(ir_graph *irg)
{
	unsigned const misalign = 0;
	be_sim_stack_pointer(irg, misalign, SPARC_PO2_STACK_ALIGNMENT,
	                     sparc_sp_sim);
}

/**
 * Perform some fixups for variadic functions.
 * To make the rest of the frontend code easier to understand we add
 * "dummy" parameters until the number of parameters transmitted in registers.
 * (because otherwise the backend wouldn't store the value of the register
 *  parameters into memory for the VLA magic)
 */
static bool sparc_variadic_fixups(ir_graph *const irg, calling_convention_t *const cconv)
{
	ir_entity *entity = get_irg_entity(irg);
	ir_type   *mtp    = get_entity_type(entity);
	if (!is_method_variadic(mtp))
		return false;

	if (cconv->n_param_regs >= SPARC_N_PARAM_REGS)
		return false;

	size_t                    const n_params     = get_method_n_params(mtp);
	size_t                    const n_ress       = get_method_n_ress(mtp);
	size_t                    const new_n_params = n_params + (SPARC_N_PARAM_REGS - cconv->n_param_regs);
	unsigned                  const cc_mask      = get_method_calling_convention(mtp);
	mtp_additional_properties const props        = get_method_additional_properties(mtp);
	ir_type                  *const new_mtp      = new_type_method(new_n_params, n_ress, true, cc_mask, props);

	type_dbg_info *const dbgi = get_type_dbg_info(mtp);
	set_type_dbg_info(new_mtp, dbgi);

	for (size_t i = 0; i < n_ress; ++i) {
		ir_type *type = get_method_res_type(mtp, i);
		set_method_res_type(new_mtp, i, type);
	}
	for (size_t i = 0; i < n_params; ++i) {
		ir_type *type = get_method_param_type(mtp, i);
		set_method_param_type(new_mtp, i, type);
	}
	ir_type *const frame_type  = get_irg_frame_type(irg);
	ir_mode *const gp_reg_mode = sparc_reg_classes[CLASS_sparc_gp].mode;
	ir_type *const gp_reg_type = get_type_for_mode(gp_reg_mode);
	for (size_t i = n_params; i < new_n_params; ++i) {
		set_method_param_type(new_mtp, i, gp_reg_type);
		new_parameter_entity(frame_type, i, gp_reg_type);
	}

	set_entity_type(entity, new_mtp);
	return true;
}

static void sparc_layout_param_entities(ir_graph *const irg, calling_convention_t *const cconv, ir_type *const non_lowered)
{
	ir_entity **const param_map  = be_collect_parameter_entities(irg);
	ir_type    *const frame_type = get_irg_frame_type(irg);
	size_t      const n_params   = cconv->n_parameters;
	/* calculate offsets/create missing entities */
	for (size_t i = 0; i < n_params; ++i) {
		reg_or_stackslot_t *const param  = &cconv->parameters[i];
		ir_entity          *      entity = param_map[i];
		if (entity == NULL) {
			if (!param->already_stored)
				continue;
			entity = new_parameter_entity(frame_type, i, param->type);
		}
		param->entity = entity;
		set_entity_offset(entity, param->offset);
	}

	ir_entity *const function      = get_irg_entity(irg);
	ir_type   *const function_type = get_entity_type(function);
	if (is_method_variadic(function_type)) {
		/* sparc_variadic_fixups() fiddled with our type, find out the
		 * original number of parameters */
		size_t const orig_n_params = get_method_n_params(non_lowered);
		long offset;
		if (orig_n_params < n_params) {
			assert(param_map[orig_n_params] != NULL);
			offset = get_entity_offset(param_map[orig_n_params]);
		} else {
			offset = cconv->param_stack_size + SPARC_MIN_STACKSIZE;
		}

		cconv->va_start_addr = be_make_va_start_entity(frame_type, offset);
	}

	free(param_map);
}

calling_convention_t *sparc_prepare_calling_convention(ir_graph *const irg)
{
	ir_entity *const entity      = get_irg_entity(irg);
	ir_type   *const non_lowered = get_entity_type(entity);
	calling_convention_t *cconv = sparc_decide_calling_convention(get_entity_type(entity), irg);
	if (sparc_variadic_fixups(irg, cconv)) {
		sparc_free_calling_convention(cconv);
		cconv = sparc_decide_calling_convention(get_entity_type(entity), irg);
	}
	sparc_layout_param_entities(irg, cconv, non_lowered);
	return cconv;
}