change backends to produce 1 big array with all registers
[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 #include "config.h"
27
28 #include "irgmod.h"
29 #include "ircons.h"
30 #include "iredges.h"
31 #include "error.h"
32
33 #include "benode.h"
34 #include "bepeephole.h"
35 #include "besched.h"
36
37 #include "arm_optimize.h"
38 #include "gen_arm_regalloc_if.h"
39 #include "gen_arm_new_nodes.h"
40 #include "arm_nodes_attr.h"
41 #include "arm_new_nodes.h"
42
43 static unsigned arm_ror(unsigned v, unsigned ror)
44 {
45         return (v << (32 - ror)) | (v >> ror);
46 }
47
48 /*
49  * construct 8bit values and rot amounts for a value.
50  */
51 void arm_gen_vals_from_word(unsigned int value, arm_vals *result)
52 {
53         int initial = 0;
54
55         /* TODO: not optimal yet, as we only "shift" the value and don't take advantage of rotations */
56
57         /* special case: we prefer shift amount 0 */
58         if (value <= 0xFF) {
59                 result->values[0] = value;
60                 result->rors[0]   = 0;
61                 result->ops       = 1;
62                 return;
63         }
64
65         result->ops = 0;
66         do {
67                 while ( (value & 0x3) == 0) {
68                         value  >>= 2;
69                         initial += 2;
70                 }
71
72                 result->values[result->ops] = value & 0xFF;
73                 result->rors[result->ops]   = (32-initial) % 32;
74                 ++result->ops;
75
76                 value  >>= 8;
77                 initial += 8;
78         } while (value != 0);
79 }
80
81 /**
82  * Returns non.zero if the given offset can be directly encoded into an ARM
83  * instruction.
84  */
85 static int allowed_arm_immediate(int offset, arm_vals *result)
86 {
87         arm_gen_vals_from_word(offset, result);
88         return result->ops <= 1;
89 }
90
91 /**
92  * Fix an IncSP node if the offset gets too big
93  */
94 static void peephole_be_IncSP(ir_node *node)
95 {
96         ir_node  *first;
97         ir_node  *last;
98         ir_node  *block;
99         int       offset;
100         int       cnt;
101         int       sign = 1;
102         arm_vals  v;
103         const ir_edge_t *edge;
104         const ir_edge_t *next;
105
106         /* first optimize incsp->incsp combinations */
107         node = be_peephole_IncSP_IncSP(node);
108
109         offset = be_get_IncSP_offset(node);
110         /* can be transformed into Add OR Sub */
111         if (offset < 0) {
112                 sign = -1;
113                 offset = -offset;
114         }
115         if (allowed_arm_immediate(offset, &v))
116                 return;
117
118         be_set_IncSP_offset(node, sign * arm_ror(v.values[0], v.rors[0]));
119
120         first = node;
121         block = get_nodes_block(node);
122         for (cnt = 1; cnt < v.ops; ++cnt) {
123                 int value = sign * arm_ror(v.values[cnt], v.rors[cnt]);
124                 ir_node *next = be_new_IncSP(&arm_registers[REG_SP], block, node,
125                                              value, 1);
126                 sched_add_after(node, next);
127                 node = next;
128         }
129
130         /* reattach IncSP users */
131         last = node;
132         node = sched_next(first);
133         foreach_out_edge_safe(first, edge, next) {
134                 ir_node *user = get_edge_src_irn(edge);
135                 int      pos  = get_edge_src_pos(edge);
136                 if (user == node)
137                         continue;
138                 set_irn_n(user, pos, last);
139         }
140 }
141
142 /**
143  * creates the address by Adds
144  */
145 static ir_node *gen_ptr_add(ir_node *node, ir_node *frame, arm_vals *v)
146 {
147         dbg_info *dbgi  = get_irn_dbg_info(node);
148         ir_node  *block = get_nodes_block(node);
149         int     cnt;
150         ir_node *ptr;
151
152         ptr = new_bd_arm_Add_imm(dbgi, block, frame, v->values[0], v->rors[0]);
153         arch_set_irn_register(ptr, &arm_registers[REG_R12]);
154         sched_add_before(node, ptr);
155
156         for (cnt = 1; cnt < v->ops; ++cnt) {
157                 ir_node *next = new_bd_arm_Add_imm(dbgi, block, ptr, v->values[cnt],
158                                                    v->rors[cnt]);
159                 arch_set_irn_register(next, &arm_registers[REG_R12]);
160                 sched_add_before(node, next);
161                 ptr = next;
162         }
163         return ptr;
164 }
165
166 /**
167 * creates the address by Subs
168 */
169 static ir_node *gen_ptr_sub(ir_node *node, ir_node *frame, arm_vals *v)
170 {
171         dbg_info *dbgi  = get_irn_dbg_info(node);
172         ir_node  *block = get_nodes_block(node);
173         int     cnt;
174         ir_node *ptr;
175
176         ptr = new_bd_arm_Sub_imm(dbgi, block, frame, v->values[0], v->rors[0]);
177         arch_set_irn_register(ptr, &arm_registers[REG_R12]);
178         sched_add_before(node, ptr);
179
180         for (cnt = 1; cnt < v->ops; ++cnt) {
181                 ir_node *next = new_bd_arm_Sub_imm(dbgi, block, ptr, v->values[cnt],
182                                                    v->rors[cnt]);
183                 arch_set_irn_register(next, &arm_registers[REG_R12]);
184                 sched_add_before(node, next);
185                 ptr = next;
186         }
187         return ptr;
188 }
189
190 /** fix frame addresses which are too big */
191 static void peephole_arm_FrameAddr(ir_node *node)
192 {
193         arm_SymConst_attr_t *attr   = get_arm_SymConst_attr(node);
194         int                  offset = attr->fp_offset;
195         arm_vals             v;
196         ir_node             *base;
197         ir_node             *ptr;
198
199         if (allowed_arm_immediate(offset, &v))
200                 return;
201
202         base = get_irn_n(node, n_arm_FrameAddr_base);
203         /* TODO: suboptimal */
204         ptr = gen_ptr_add(node, base, &v);
205
206         attr->fp_offset = 0;
207         set_irn_n(node, n_arm_FrameAddr_base, ptr);
208 }
209
210 /**
211  * Fix stackpointer relative stores if the offset gets too big
212  */
213 static void peephole_arm_Str_Ldr(ir_node *node)
214 {
215         arm_load_store_attr_t *attr    = get_arm_load_store_attr(node);
216         int                    offset  = attr->offset;
217         int                    use_add = 1;
218         ir_node               *ptr;
219         arm_vals              v;
220
221         if (allowed_arm_immediate(offset, &v))
222                 return;
223
224         /* we should only have too big offsets for frame entities */
225         if (!attr->is_frame_entity) {
226                 fprintf(stderr,
227                         "POSSIBLE ARM BACKEND PROBLEM: offset in Store too big\n");
228         }
229         if (offset < 0) {
230                 use_add = 0;
231                 offset = -offset;
232         }
233
234         if (is_arm_Str(node)) {
235                 ptr = get_irn_n(node, n_arm_Str_ptr);
236         } else {
237                 assert(is_arm_Ldr(node));
238                 ptr = get_irn_n(node, n_arm_Ldr_ptr);
239         }
240
241         if (use_add) {
242                 ptr = gen_ptr_add(node, ptr, &v);
243         } else {
244                 ptr = gen_ptr_sub(node, ptr, &v);
245         }
246
247         /* TODO: sub-optimal, the last offset could probably be left inside the
248            store */
249         if (is_arm_Str(node)) {
250                 set_irn_n(node, n_arm_Str_ptr, ptr);
251         } else {
252                 assert(is_arm_Ldr(node));
253                 set_irn_n(node, n_arm_Ldr_ptr, ptr);
254         }
255         attr->offset = 0;
256 }
257
258 /**
259  * Register a peephole optimization function.
260  */
261 static void register_peephole_optimisation(ir_op *op, peephole_opt_func func)
262 {
263         assert(op->ops.generic == NULL);
264         op->ops.generic = (op_func)func;
265 }
266
267 /* Perform peephole-optimizations. */
268 void arm_peephole_optimization(ir_graph *irg)
269 {
270         /* register peephole optimizations */
271         clear_irp_opcodes_generic_func();
272         register_peephole_optimisation(op_be_IncSP,      peephole_be_IncSP);
273         register_peephole_optimisation(op_arm_Str,       peephole_arm_Str_Ldr);
274         register_peephole_optimisation(op_arm_Ldr,       peephole_arm_Str_Ldr);
275         register_peephole_optimisation(op_arm_FrameAddr, peephole_arm_FrameAddr);
276
277         be_peephole_opt(irg);
278 }