61d805f6d2a9aa4e1fcae0a1c3746fcb9b12fbc8
[libfirm] / ir / be / arm / arm_optimize.c
1 /*
2  * Copyright (C) 1995-2008 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       Implements several optimizations for ARM.
23  * @author      Michael Beck
24  * @version     $Id: $
25  */
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
29
30 #include "irgmod.h"
31 #include "ircons.h"
32 #include "error.h"
33
34 #include "benode_t.h"
35 #include "bepeephole.h"
36 #include "besched.h"
37
38 #include "arm_optimize.h"
39 #include "gen_arm_regalloc_if.h"
40 #include "gen_arm_new_nodes.h"
41
42 static arm_code_gen_t  *cg;
43
44 /** Execute ARM ROL. */
45 static unsigned arm_rol(unsigned v, unsigned rol) {
46         return (v << rol) | (v >> (32 - rol));
47 }
48
49 /*
50  * construct 8bit values and rot amounts for a value.
51  */
52 void arm_gen_vals_from_word(unsigned int value, arm_vals *result)
53 {
54         int initial = 0;
55
56         memset(result, 0, sizeof(*result));
57
58         /* special case: we prefer shift amount 0 */
59         if (value < 0x100) {
60                 result->values[0] = value;
61                 result->ops       = 1;
62                 return;
63         }
64
65         while (value != 0) {
66                 if (value & 0xFF) {
67                         unsigned v = arm_rol(value, 8) & 0xFFFFFF;
68                         int shf = 0;
69                         for (;;) {
70                                 if ((v & 3) != 0)
71                                         break;
72                                 shf += 2;
73                                 v >>= 2;
74                         }
75                         v  &= 0xFF;
76                         shf = (initial + shf - 8) & 0x1F;
77                         result->values[result->ops] = v;
78                         result->shifts[result->ops] = shf;
79                         ++result->ops;
80
81                         value ^= arm_rol(v, shf) >> initial;
82                 }
83                 else {
84                         value >>= 8;
85                         initial += 8;
86                 }
87         }
88 }
89
90 /**
91  * Encodes an immediate with shifter operand
92  */
93 unsigned int arm_encode_imm_w_shift(unsigned int shift, unsigned int immediate) {
94         return immediate | ((shift>>1)<<8);
95 }
96
97 /**
98  * Decode an immediate with shifter operand
99  */
100 unsigned int arm_decode_imm_w_shift(long imm_value) {
101         unsigned l = (unsigned)imm_value;
102         unsigned rol = (l & ~0xFF) >> 7;
103
104         return arm_rol(l & 0xFF, rol);
105 }
106
107 /**
108  * Returns non.zero if the given offset can be directly encoded into an ARM instruction.
109  */
110 static int allowed_arm_immediate(int offset, arm_vals *result) {
111         arm_gen_vals_from_word(offset, result);
112         return result->ops <= 1;
113 }
114
115 /**
116  * Fix an IncSP node if the offset gets too big
117  */
118 static void peephole_be_IncSP(ir_node *node) {
119         ir_graph *irg;
120         ir_node  *block;
121         int      offset, cnt, align, sign = 1;
122         arm_vals v;
123
124         /* first optimize incsp->incsp combinations */
125         node = be_peephole_IncSP_IncSP(node);
126
127         offset = be_get_IncSP_offset(node);
128         /* can be transformed into Add OR Sub */
129         if (offset < 0) {
130                 sign = -1;
131                 offset = -offset;
132         }
133         if (allowed_arm_immediate(offset, &v))
134                 return;
135
136         be_set_IncSP_offset(node, (int)arm_rol(v.values[0], v.shifts[0]) * sign);
137
138         irg   = current_ir_graph;
139         block = get_nodes_block(node);
140         align = be_get_IncSP_align(node);
141         for (cnt = 1; cnt < v.ops; ++cnt) {
142                 int value = (int)arm_rol(v.values[cnt], v.shifts[cnt]);
143                 ir_node *next = be_new_IncSP(&arm_gp_regs[REG_SP], irg, block, node, value * sign, align);
144                 sched_add_after(node, next);
145                 node = next;
146         }
147 }
148
149 /**
150  * creates the address by Adds
151  */
152 static ir_node *gen_ptr_add(ir_node *node, ir_node *frame, arm_vals *v)
153 {
154         ir_graph *irg   = current_ir_graph;
155         dbg_info *dbg   = get_irn_dbg_info(node);
156         ir_node  *block = get_nodes_block(node);
157         int     cnt;
158         ir_node *ptr;
159
160         ptr = new_rd_arm_Add_i(dbg, irg, block, frame, mode_Iu, arm_encode_imm_w_shift(v->shifts[0], v->values[0]));
161         arch_set_irn_register(ptr, &arm_gp_regs[REG_R12]);
162         sched_add_before(node, ptr);
163
164         for (cnt = 1; cnt < v->ops; ++cnt) {
165                 long value = arm_encode_imm_w_shift(v->shifts[cnt], v->values[cnt]);
166                 ir_node *next = new_rd_arm_Add_i(dbg, irg, block, ptr, mode_Iu, value);
167                 arch_set_irn_register(next, &arm_gp_regs[REG_R12]);
168                 sched_add_before(node, next);
169                 ptr = next;
170         }
171         return ptr;
172 }
173
174 /**
175 * creates the address by Subs
176 */
177 static ir_node *gen_ptr_sub(ir_node *node, ir_node *frame, arm_vals *v)
178 {
179         ir_graph *irg   = current_ir_graph;
180         dbg_info *dbg   = get_irn_dbg_info(node);
181         ir_node  *block = get_nodes_block(node);
182         int     cnt;
183         ir_node *ptr;
184
185         ptr = new_rd_arm_Sub_i(dbg, irg, block, frame, mode_Iu, arm_encode_imm_w_shift(v->shifts[0], v->values[0]));
186         arch_set_irn_register(ptr, &arm_gp_regs[REG_R12]);
187         sched_add_before(node, ptr);
188
189         for (cnt = 1; cnt < v->ops; ++cnt) {
190                 long value = arm_encode_imm_w_shift(v->shifts[cnt], v->values[cnt]);
191                 ir_node *next = new_rd_arm_Sub_i(dbg, irg, block, ptr, mode_Iu, value);
192                 arch_set_irn_register(next, &arm_gp_regs[REG_R12]);
193                 sched_add_before(node, next);
194                 ptr = next;
195         }
196         return ptr;
197 }
198
199 /**
200  * Fix an be_Spill node if the offset gets too big
201  */
202 static void peephole_be_Spill(ir_node *node) {
203         ir_entity *ent   = be_get_frame_entity(node);
204         int       use_add = 1, offset = get_entity_offset(ent);
205         ir_node   *block, *ptr, *frame, *value, *store;
206         ir_mode   *mode;
207         dbg_info  *dbg;
208         ir_graph  *irg;
209         arm_vals  v;
210
211         if (allowed_arm_immediate(offset, &v))
212                 return;
213         if (offset < 0) {
214                 use_add = 0;
215                 offset = -offset;
216         }
217
218         frame = be_get_Spill_frame(node);
219         if (use_add) {
220                 ptr = gen_ptr_add(node, frame, &v);
221         } else {
222                 ptr = gen_ptr_sub(node, frame, &v);
223         }
224
225         value = be_get_Spill_val(node);
226         mode  = get_irn_mode(value);
227         irg   = current_ir_graph;
228         dbg   = get_irn_dbg_info(node);
229         block = get_nodes_block(node);
230
231         if (mode_is_float(mode)) {
232                 if (USE_FPA(cg->isa)) {
233                         /* transform into fpaStf */
234                         store = new_rd_arm_fpaStf(dbg, irg, block, ptr, value, get_irg_no_mem(irg), mode);
235                         sched_add_before(node, store);
236                 } else {
237                         panic("peephole_be_Spill: spill not supported for this mode");
238                 }
239         } else if (mode_is_dataM(mode)) {
240                  /* transform into Store */;
241                  store = new_rd_arm_Store(dbg, irg, block, ptr, value, get_irg_no_mem(irg));
242                  sched_add_before(node, store);
243         } else {
244                 panic("peephole_be_Spill: spill not supported for this mode");
245         }
246
247         be_peephole_exchange(node, store);
248 }
249
250 /**
251  * Fix an be_Reload node if the offset gets too big
252  */
253 static void peephole_be_Reload(ir_node *node) {
254         ir_entity *ent   = be_get_frame_entity(node);
255         int       use_add = 1, offset = get_entity_offset(ent);
256         ir_node   *block, *ptr, *frame, *load, *mem, *proj;
257         ir_mode   *mode;
258         dbg_info  *dbg;
259         ir_graph  *irg;
260         arm_vals  v;
261         const arch_register_t *reg;
262
263         if (allowed_arm_immediate(offset, &v))
264                 return;
265         if (offset < 0) {
266                 use_add = 0;
267                 offset = -offset;
268         }
269
270         frame = be_get_Reload_frame(node);
271         if (use_add) {
272                 ptr = gen_ptr_add(node, frame, &v);
273         } else {
274                 ptr = gen_ptr_sub(node, frame, &v);
275         }
276
277         reg   = arch_get_irn_register(node);
278         mem   = be_get_Reload_mem(node);
279         mode  = get_irn_mode(node);
280         irg   = current_ir_graph;
281         dbg   = get_irn_dbg_info(node);
282         block = get_nodes_block(node);
283
284         if (mode_is_float(mode)) {
285                 if (USE_FPA(cg->isa)) {
286                         /* transform into fpaLdf */
287                         load = new_rd_arm_fpaLdf(dbg, irg, block, ptr, mem, mode);
288                         sched_add_before(node, load);
289                         proj = new_rd_Proj(dbg, irg, block, load, mode, pn_arm_fpaLdf_res);
290                         arch_set_irn_register(proj, reg);
291                 } else {
292                         panic("peephole_be_Spill: spill not supported for this mode");
293                 }
294         } else if (mode_is_dataM(mode)) {
295                 /* transform into Store */;
296                 load = new_rd_arm_Load(dbg, irg, block, ptr, mem);
297                 sched_add_before(node, load);
298                 proj = new_rd_Proj(dbg, irg, block, load, mode_Iu, pn_arm_Load_res);
299                 arch_set_irn_register(proj, reg);
300         } else {
301                 panic("peephole_be_Spill: spill not supported for this mode");
302         }
303
304         be_peephole_exchange(node, proj);
305 }
306
307 /**
308  * Register a peephole optimization function.
309  */
310 static void register_peephole_optimisation(ir_op *op, peephole_opt_func func) {
311         assert(op->ops.generic == NULL);
312         op->ops.generic = (op_func)func;
313 }
314
315 /* Perform peephole-optimizations. */
316 void arm_peephole_optimization(arm_code_gen_t *new_cg)
317 {
318         cg = new_cg;
319
320         /* register peephole optimizations */
321         clear_irp_opcodes_generic_func();
322         register_peephole_optimisation(op_be_IncSP, peephole_be_IncSP);
323         register_peephole_optimisation(op_be_Spill, peephole_be_Spill);
324         register_peephole_optimisation(op_be_Reload, peephole_be_Reload);
325
326         be_peephole_opt(cg->birg);
327 }