Fix typos in comments: s/wether/whether/ and related corrections.
[libfirm] / ir / be / sparc / sparc_finish.c
1 /*
2  * Copyright (C) 1995-2010 University of Karlsruhe.  All right reserved.
3  *
4  * This file is part of libFirm.
5  *
6  * This file may be distributed and/or modified under the terms of the
7  * GNU General Public License version 2 as published by the Free Software
8  * Foundation and appearing in the file LICENSE.GPL included in the
9  * packaging of this file.
10  *
11  * Licensees holding valid libFirm Professional Edition licenses may use
12  * this file in accordance with the libFirm Commercial License.
13  * Agreement provided with the Software.
14  *
15  * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
16  * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE.
18  */
19
20 /**
21  * @file
22  * @brief    Peephole optimization and legalization of a sparc function
23  * @author   Matthias Braun
24  * @version  $Id$
25  *
26  * A note on sparc stackpointer (sp) behaviour:
27  * The ABI expects SPARC_MIN_STACKSIZE bytes to be available at the
28  * stackpointer. This space will be used to spill register windows,
29  * and for spilling va_arg arguments (maybe we can optimize this away for
30  * statically known not-va-arg-functions...)
31  * This in effect means that we allocate that extra space at the function begin
32  * which is easy. But this space isn't really fixed at the beginning of the
33  * stackframe. Instead you should rather imagine the space as always being the
34  * last-thing on the stack.
35  * So when addressing anything stack-specific we have to account for this
36  * area, while our compiler thinks the space is occupied at the beginning
37  * of the stack frame. The code here among other things adjusts these offsets
38  * accordingly.
39  */
40 #include "config.h"
41
42 #include "bearch_sparc_t.h"
43 #include "gen_sparc_regalloc_if.h"
44 #include "sparc_new_nodes.h"
45 #include "irprog.h"
46 #include "irgmod.h"
47
48 #include "../bepeephole.h"
49 #include "../benode.h"
50 #include "../besched.h"
51
52 static void finish_sparc_Save(ir_node *node)
53 {
54         sparc_attr_t *attr = get_sparc_attr(node);
55         int offset = attr->immediate_value;
56         ir_node  *schedpoint = node;
57         dbg_info *dbgi;
58         ir_node  *block;
59         ir_node  *new_save;
60         ir_node  *stack;
61         ir_entity *entity;
62
63         if (sparc_is_value_imm_encodeable(offset))
64                 return;
65
66         /* uhh only works for the imm variant yet */
67         assert(get_irn_arity(node) == 1);
68
69         block = get_nodes_block(node);
70         dbgi = get_irn_dbg_info(node);
71         stack = get_irn_n(node, n_sparc_Save_stack);
72         entity = attr->immediate_value_entity;
73         new_save = new_bd_sparc_Save_imm(dbgi, block, stack, entity, 0);
74         arch_set_irn_register(new_save, &sparc_registers[REG_SP]);
75         stack = new_save;
76
77         sched_add_after(node, new_save);
78         schedpoint = new_save;
79         while (offset > SPARC_IMMEDIATE_MAX || offset < SPARC_IMMEDIATE_MIN) {
80                 if (offset > 0) {
81                         stack = be_new_IncSP(&sparc_registers[REG_SP], block, stack,
82                                              SPARC_IMMEDIATE_MIN, 0);
83                         offset -= -SPARC_IMMEDIATE_MIN;
84                 } else {
85                         stack = be_new_IncSP(&sparc_registers[REG_SP], block, stack,
86                                              -SPARC_IMMEDIATE_MIN, 0);
87                         offset -= SPARC_IMMEDIATE_MIN;
88                 }
89                 sched_add_after(schedpoint, stack);
90                 schedpoint = stack;
91         }
92         attr = get_sparc_attr(new_save);
93         attr->immediate_value = offset;
94         be_peephole_exchange(node, stack);
95 }
96
97 /**
98  * sparc immediates are limited. Split IncSP with bigger immediates if
99  * necessary.
100  */
101 static void finish_be_IncSP(ir_node *node)
102 {
103         int      sign   = 1;
104         int      offset = be_get_IncSP_offset(node);
105         ir_node *sp     = be_get_IncSP_pred(node);
106         ir_node *block;
107
108         /* we might have to break the IncSP apart if the constant has become too
109          * big */
110         if (offset < 0) {
111                 offset = -offset;
112                 sign   = -1;
113         }
114
115         if (sparc_is_value_imm_encodeable(-offset))
116                 return;
117
118         /* split incsp into multiple instructions */
119         block = get_nodes_block(node);
120         while (offset > -SPARC_IMMEDIATE_MIN) {
121                 sp = be_new_IncSP(&sparc_registers[REG_SP], block, sp,
122                                   sign * -SPARC_IMMEDIATE_MIN, 0);
123                 sched_add_before(node, sp);
124                 offset -= -SPARC_IMMEDIATE_MIN;
125         }
126
127         be_set_IncSP_pred(node, sp);
128         be_set_IncSP_offset(node, sign*offset);
129 }
130
131 /**
132  * adjust sp-relative offsets. Split into multiple instructions if offset
133  * exceeds sparc immediate range.
134  */
135 static void finish_sparc_FrameAddr(ir_node *node)
136 {
137         /* adapt to sparc stack magic */
138         sparc_attr_t *attr   = get_sparc_attr(node);
139         int           offset = attr->immediate_value;
140         ir_node      *base   = get_irn_n(node, n_sparc_FrameAddr_base);
141         dbg_info     *dbgi   = get_irn_dbg_info(node);
142         ir_node      *block  = get_nodes_block(node);
143         int           sign   = 1;
144         bool          sp_relative
145                 = arch_get_irn_register(base) == &sparc_registers[REG_SP];
146         if (sp_relative) {
147                 offset += SPARC_MIN_STACKSIZE;
148         }
149
150         if (offset < 0) {
151                 sign   = -1;
152                 offset = -offset;
153         }
154
155         if (offset > -SPARC_IMMEDIATE_MIN) {
156                 ir_entity *entity = attr->immediate_value_entity;
157                 ir_node   *new_frameaddr
158                         = new_bd_sparc_FrameAddr(dbgi, block, base, entity, 0);
159                 ir_node   *schedpoint = node;
160                 const arch_register_t *reg = arch_get_irn_register(node);
161
162                 sched_add_after(schedpoint, new_frameaddr);
163                 schedpoint = new_frameaddr;
164                 arch_set_irn_register(new_frameaddr, reg);
165                 base = new_frameaddr;
166
167                 while (offset > -SPARC_IMMEDIATE_MIN) {
168                         if (sign > 0) {
169                                 base = new_bd_sparc_Sub_imm(dbgi, block, base, NULL,
170                                                                                         SPARC_IMMEDIATE_MIN);
171                         } else {
172                                 base = new_bd_sparc_Add_imm(dbgi, block, base, NULL,
173                                                                                         SPARC_IMMEDIATE_MIN);
174                         }
175                         arch_set_irn_register(base, reg);
176                         sched_add_after(schedpoint, base);
177                         schedpoint = base;
178
179                         offset -= -SPARC_IMMEDIATE_MIN;
180                 }
181
182                 be_peephole_exchange(node, base);
183                 attr = get_sparc_attr(new_frameaddr);
184         }
185         attr->immediate_value = sign*offset;
186 }
187
188 static void finish_sparc_LdSt(ir_node *node)
189 {
190         sparc_load_store_attr_t *attr = get_sparc_load_store_attr(node);
191         if (attr->is_frame_entity) {
192                 ir_node *base;
193                 bool     sp_relative;
194                 if (is_sparc_Ld(node) || is_sparc_Ldf(node)) {
195                         base = get_irn_n(node, n_sparc_Ld_ptr);
196                 } else {
197                         assert(is_sparc_St(node) || is_sparc_Stf(node));
198                         base = get_irn_n(node, n_sparc_St_ptr);
199                 }
200                 sp_relative = arch_get_irn_register(base) == &sparc_registers[REG_SP];
201                 if (sp_relative)
202                         attr->base.immediate_value += SPARC_MIN_STACKSIZE;
203         }
204 }
205
206 static void peephole_be_IncSP(ir_node *node)
207 {
208         ir_node *pred;
209         node = be_peephole_IncSP_IncSP(node);
210         if (!be_is_IncSP(node))
211                 return;
212
213         pred = be_get_IncSP_pred(node);
214         if (is_sparc_Save(pred) && be_has_only_one_user(pred)) {
215                 int offset = -be_get_IncSP_offset(node);
216                 sparc_attr_t *attr = get_sparc_attr(pred);
217                 attr->immediate_value += offset;
218                 be_peephole_exchange(node, pred);
219         }
220 }
221
222 static void peephole_sparc_FrameAddr(ir_node *node)
223 {
224         /* the peephole code currently doesn't allow this since it changes
225          * the register. Find out why and how to workaround this... */
226 #if 0
227         const sparc_attr_t *attr = get_sparc_attr_const(node);
228         if (attr->immediate_value == 0) {
229                 ir_node *base = get_irn_n(node, n_sparc_FrameAddr_base);
230                 be_peephole_exchange(node, base);
231         }
232 #endif
233         (void) node;
234 }
235
236 static void finish_be_Return(ir_node *node)
237 {
238         ir_node *schedpoint = node;
239         ir_node *restore;
240         /* see that there is no code between Return and restore, if there is move
241          * it in front of the restore */
242         while (true) {
243                 if (!sched_has_prev(schedpoint))
244                         return;
245                 schedpoint = sched_prev(schedpoint);
246                 if (is_sparc_Restore(schedpoint) || is_sparc_RestoreZero(schedpoint))
247                         break;
248         }
249         restore = schedpoint;
250         schedpoint = sched_prev(node);
251         /* move all code between return and restore up */
252         while (schedpoint != restore) {
253                 ir_node *next_schedpoint = sched_prev(schedpoint);
254                 sched_remove(schedpoint);
255                 sched_add_before(restore, schedpoint);
256                 schedpoint = next_schedpoint;
257         }
258 }
259
260 static void register_peephole_optimisation(ir_op *op, peephole_opt_func func)
261 {
262         assert(op->ops.generic == NULL);
263         op->ops.generic = (op_func) func;
264 }
265
266 void sparc_finish(ir_graph *irg)
267 {
268         clear_irp_opcodes_generic_func();
269         register_peephole_optimisation(op_be_IncSP,        peephole_be_IncSP);
270         register_peephole_optimisation(op_sparc_FrameAddr, peephole_sparc_FrameAddr);
271         be_peephole_opt(irg);
272
273         clear_irp_opcodes_generic_func();
274         register_peephole_optimisation(op_be_IncSP,        finish_be_IncSP);
275         register_peephole_optimisation(op_be_Return,       finish_be_Return);
276         register_peephole_optimisation(op_sparc_FrameAddr, finish_sparc_FrameAddr);
277         register_peephole_optimisation(op_sparc_Ld,        finish_sparc_LdSt);
278         register_peephole_optimisation(op_sparc_Ldf,       finish_sparc_LdSt);
279         register_peephole_optimisation(op_sparc_Save,      finish_sparc_Save);
280         register_peephole_optimisation(op_sparc_St,        finish_sparc_LdSt);
281         register_peephole_optimisation(op_sparc_Stf,       finish_sparc_LdSt);
282         be_peephole_opt(irg);
283 }