Fix r15888.
[libfirm] / ir / be / ia32 / ia32_address_mode.c
1 /*
2  * Copyright (C) 1995-2007 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       This file contains functions for matching firm graphs for
23  *              nodes that can be used as addressmode for x86 commands
24  * @author      Matthias Braun
25  * @version     $Id$
26  */
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30
31 #include "ia32_address_mode.h"
32
33 #include "irtypes.h"
34 #include "irnode_t.h"
35 #include "irprintf.h"
36 #include "error.h"
37 #include "iredges_t.h"
38
39 #include "../benode_t.h"
40
41 #undef AGGRESSIVE_AM
42
43 /* gas/ld don't support negative symconsts :-( */
44 #undef SUPPORT_NEGATIVE_SYMCONSTS
45
46 static int is_immediate_2(const ir_node *node, int *symconsts, int negate)
47 {
48         ir_node *left;
49         ir_node *right;
50
51         switch(get_irn_opcode(node)) {
52         case iro_Const:
53                 if(!tarval_is_long(get_Const_tarval(node))) {
54 #ifdef DEBUG_libfirm
55                         ir_fprintf(stderr, "Optimisation warning tarval of %+F is not a "
56                                    "long.");
57 #endif
58                         return 0;
59                 }
60                 return 1;
61         case iro_SymConst:
62 #ifndef SUPPORT_NEGATIVE_SYMCONSTS
63                 /* unfortunately the assembler/linker doesn't support -symconst */
64                 if(negate)
65                         return 0;
66 #endif
67
68                 if(get_SymConst_kind(node) != symconst_addr_ent)
69                         return 0;
70                 (*symconsts)++;
71                 if(*symconsts > 1)
72                         return 0;
73
74                 return 1;
75         case iro_Add:
76         case iro_Sub:
77                 left  = get_binop_left(node);
78                 right = get_binop_right(node);
79                 if(!is_immediate_2(left, symconsts, negate))
80                         return 0;
81                 if(!is_immediate_2(right, symconsts, is_Sub(node) ? !negate : negate))
82                         return 0;
83
84                 return 1;
85         default:
86                 break;
87         }
88
89         return 0;
90 }
91
92 static int is_immediate(ia32_address_t *addr, const ir_node *node, int negate)
93 {
94         int symconsts = 0;
95         if(addr->symconst_ent != NULL)
96                 symconsts = 1;
97
98         return is_immediate_2(node, &symconsts, negate);
99 }
100
101 static void eat_immediate(ia32_address_t *addr, ir_node *node, int negate)
102 {
103         tarval  *tv;
104         ir_node *left;
105         ir_node *right;
106
107         switch(get_irn_opcode(node)) {
108         case iro_Const:
109                 tv = get_Const_tarval(node);
110                 if(negate) {
111                         addr->offset -= get_tarval_long(tv);
112                 } else {
113                         addr->offset += get_tarval_long(tv);
114                 }
115                 break;
116         case iro_SymConst:
117                 if(addr->symconst_ent != NULL) {
118                         panic("Internal error: more than 1 symconst in address "
119                               "calculation");
120                 }
121                 addr->symconst_ent  = get_SymConst_entity(node);
122 #ifndef SUPPORT_NEGATIVE_SYMCONSTS
123                 assert(!negate);
124 #endif
125                 addr->symconst_sign = negate;
126                 break;
127         case iro_Add:
128                 left  = get_Add_left(node);
129                 right = get_Add_right(node);
130                 eat_immediate(addr, left, negate);
131                 eat_immediate(addr, right, negate);
132                 break;
133         case iro_Sub:
134                 left  = get_Sub_left(node);
135                 right = get_Sub_right(node);
136                 eat_immediate(addr, left, negate);
137                 eat_immediate(addr, right, !negate);
138                 break;
139         default:
140                 panic("Internal error in immediate address calculation");
141         }
142 }
143
144 static ir_node *eat_immediates(ia32_address_t *addr, ir_node *node)
145 {
146         if(is_Add(node)) {
147                 ir_node *left  = get_Add_left(node);
148                 ir_node *right = get_Add_right(node);
149
150                 if(is_immediate(addr, left, 0)) {
151                         eat_immediate(addr, left, 0);
152                         return eat_immediates(addr, right);
153                 }
154                 if(is_immediate(addr, right, 0)) {
155                         eat_immediate(addr, right, 0);
156                         return eat_immediates(addr, left);
157                 }
158         } else if(is_Sub(node)) {
159                 ir_node *left  = get_Sub_left(node);
160                 ir_node *right = get_Sub_right(node);
161
162                 if(is_immediate(addr, right, 1)) {
163                         eat_immediate(addr, right, 1);
164                         return eat_immediates(addr, left);
165                 }
166         }
167
168         return node;
169 }
170
171 static int eat_shl(ia32_address_t *addr, ir_node *node)
172 {
173         ir_node *right = get_Shl_right(node);
174         tarval  *tv;
175         long     val;
176
177         /* we can only eat a shl if we don't have a scale or index set */
178         if(addr->scale != 0 || addr->index != NULL)
179                 return 0;
180
181         /* we can use shl with 1, 2 or 3 shift */
182         if(!is_Const(right))
183                 return 0;
184         tv = get_Const_tarval(right);
185         if(!tarval_is_long(tv))
186                 return 0;
187         val = get_tarval_long(tv);
188         if(val < 0 || val > 3)
189                 return 0;
190         if(val == 0) {
191                 ir_fprintf(stderr, "Optimisation warning: unoptimized Shl(,0) found\n");
192         }
193
194 #ifndef AGGRESSIVE_AM
195         if(get_irn_n_edges(node) > 1)
196                 return 0;
197 #endif
198
199         addr->scale = val;
200         addr->index = eat_immediates(addr, get_Shl_left(node));
201         return 1;
202 }
203
204 void ia32_create_address_mode(ia32_address_t *addr, ir_node *node, int force)
205 {
206         int      res = 0;
207         ir_node *eat_imms;
208
209         if(is_immediate(addr, node, 0)) {
210                 eat_immediate(addr, node, 0);
211                 return;
212         }
213
214 #ifndef AGGRESSIVE_AM
215         if(!force && get_irn_n_edges(node) > 1) {
216                 addr->base = node;
217                 return;
218         }
219 #endif
220
221         eat_imms = eat_immediates(addr, node);
222         if(eat_imms != node) {
223                 res  = 1;
224                 node = eat_imms;
225 #ifndef AGGRESSIVE_AM
226                 if(get_irn_n_edges(node) > 1) {
227                         addr->base = node;
228                         return;
229                 }
230 #endif
231         }
232
233         /* starting point Add, Sub or Shl, FrameAddr */
234         if(is_Shl(node)) {
235                 if(eat_shl(addr, node))
236                         return;
237         } else if(is_immediate(addr, node, 0)) {
238                 eat_immediate(addr, node, 0);
239                 return;
240         } else if(be_is_FrameAddr(node)) {
241                 assert(addr->base == NULL);
242                 assert(addr->frame_entity == NULL);
243                 addr->base         = be_get_FrameAddr_frame(node);
244                 addr->use_frame    = 1;
245                 addr->frame_entity = be_get_FrameAddr_entity(node);
246                 return;
247         } else if(is_Add(node)) {
248                 ir_node *left  = get_Add_left(node);
249                 ir_node *right = get_Add_right(node);
250                 assert(force || !is_immediate(addr, left, 0));
251                 assert(force || !is_immediate(addr, right, 0));
252
253                 if(is_Shl(left) && eat_shl(addr, left)) {
254                         left = NULL;
255                 } else if(is_Shl(right) && eat_shl(addr, right)) {
256                         right = NULL;
257                 }
258                 if(left != NULL && be_is_FrameAddr(left)) {
259                         assert(addr->base == NULL);
260                         assert(addr->frame_entity == NULL);
261                         addr->base         = be_get_FrameAddr_frame(left);
262                         addr->use_frame    = 1;
263                         addr->frame_entity = be_get_FrameAddr_entity(left);
264                         left               = NULL;
265                 } else if(right != NULL && be_is_FrameAddr(right)) {
266                         assert(addr->base == NULL);
267                         assert(addr->frame_entity == NULL);
268                         addr->base         = be_get_FrameAddr_frame(right);
269                         addr->use_frame    = 1;
270                         addr->frame_entity = be_get_FrameAddr_entity(right);
271                         right              = NULL;
272                 }
273
274                 if(left != NULL) {
275                         if(addr->base != NULL) {
276                                 assert(addr->index == NULL && addr->scale == 0);
277                                 assert(right == NULL);
278                                 addr->index = left;
279                         } else {
280                                 addr->base = left;
281                         }
282                 }
283                 if(right != NULL) {
284                         if(addr->base == NULL) {
285                                 addr->base = right;
286                         } else {
287                                 assert(addr->index == NULL && addr->scale == 0);
288                                 addr->index = right;
289                         }
290                 }
291                 return;
292         }
293
294         addr->base = node;
295 }
296
297
298 typedef struct mark_evn_t mark_evn_t;
299 struct mark_evn_t {
300         bitset_t *non_address_nodes;
301 };
302
303
304 static void mark_non_address_nodes(mark_evn_t *env, ir_node *node)
305 {
306         bitset_set(env->non_address_nodes, get_irn_idx(node));
307
308         if(is_Load(node)) {
309                 ir_node *ptr = get_Load_ptr(node);
310                 ir_node *mem = get_Load_mem(node);
311
312                 mark_non_address_nodes(env, mem);
313                 (void) ptr;
314         } else if(is_Store(node)) {
315                 ir_node *val = get_Store_value(node);
316                 ir_node *ptr = get_Store_ptr(node);
317                 ir_node *mem = get_Store_mem(node);
318
319                 mark_non_address_nodes(env, val);
320                 mark_non_address_nodes(env, mem);
321                 (void) ptr;
322         } else {
323                 int i;
324                 int arity = get_irn_arity(node);
325
326                 for(i = 0; i < arity; ++i) {
327                         ir_node *in = get_irn_n(node, i);
328                         mark_non_address_nodes(env, in);
329                 }
330         }
331 }