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