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