- improve set transformation
[libfirm] / ir / be / ia32 / ia32_optimize.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       Implements several optimizations for IA32.
23  * @author      Matthias Braun, Christian Wuerdig
24  * @version     $Id$
25  */
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
29
30 #include "irnode.h"
31 #include "irprog_t.h"
32 #include "ircons.h"
33 #include "irtools.h"
34 #include "firm_types.h"
35 #include "iredges.h"
36 #include "tv.h"
37 #include "irgmod.h"
38 #include "irgwalk.h"
39 #include "height.h"
40 #include "irbitset.h"
41 #include "irprintf.h"
42 #include "error.h"
43
44 #include "../be_t.h"
45 #include "../beabi.h"
46 #include "../benode_t.h"
47 #include "../besched_t.h"
48 #include "../bepeephole.h"
49
50 #include "ia32_new_nodes.h"
51 #include "ia32_optimize.h"
52 #include "bearch_ia32_t.h"
53 #include "gen_ia32_regalloc_if.h"
54 #include "ia32_transform.h"
55 #include "ia32_dbg_stat.h"
56 #include "ia32_util.h"
57
58 DEBUG_ONLY(static firm_dbg_module_t *dbg = NULL;)
59
60 static const arch_env_t *arch_env;
61 static ia32_code_gen_t  *cg;
62
63 static void peephole_IncSP_IncSP(ir_node *node);
64
65 #if 0
66 static void peephole_ia32_Store_IncSP_to_push(ir_node *node)
67 {
68         ir_node  *base  = get_irn_n(node, n_ia32_Store_base);
69         ir_node  *index = get_irn_n(node, n_ia32_Store_index);
70         ir_node  *mem   = get_irn_n(node, n_ia32_Store_mem);
71         ir_node  *incsp = base;
72         ir_node  *val;
73         ir_node  *noreg;
74         ir_graph *irg;
75         ir_node  *block;
76         dbg_info *dbgi;
77         ir_mode  *mode;
78         ir_node  *push;
79         ir_node  *proj;
80         int       offset;
81         int       node_offset;
82
83         /* nomem inidicates the store doesn't alias with anything else */
84         if(!is_NoMem(mem))
85                 return;
86
87         /* find an IncSP in front of us, we might have to skip barriers for this */
88         while(is_Proj(incsp)) {
89                 ir_node *proj_pred = get_Proj_pred(incsp);
90                 if(!be_is_Barrier(proj_pred))
91                         return;
92                 incsp = get_irn_n(proj_pred, get_Proj_proj(incsp));
93         }
94         if(!be_is_IncSP(incsp))
95                 return;
96
97         peephole_IncSP_IncSP(incsp);
98
99         /* must be in the same block */
100         if(get_nodes_block(incsp) != get_nodes_block(node))
101                 return;
102
103         if(!is_ia32_NoReg_GP(index) || get_ia32_am_sc(node) != NULL) {
104                 panic("Invalid storeAM found (%+F)", node);
105         }
106
107         /* we should be the store to the end of the stackspace */
108         offset      = be_get_IncSP_offset(incsp);
109         mode        = get_ia32_ls_mode(node);
110         node_offset = get_ia32_am_offs_int(node);
111         if(node_offset != offset - get_mode_size_bytes(mode))
112                 return;
113
114         /* we can use a push instead of the store */
115         irg   = current_ir_graph;
116         block = get_nodes_block(node);
117         dbgi  = get_irn_dbg_info(node);
118         noreg = ia32_new_NoReg_gp(cg);
119         base  = be_get_IncSP_pred(incsp);
120         val   = get_irn_n(node, n_ia32_Store_val);
121         push  = new_rd_ia32_Push(dbgi, irg, block, noreg, noreg, mem, base, val);
122
123         proj  = new_r_Proj(irg, block, push, mode_M, pn_ia32_Push_M);
124
125         be_set_IncSP_offset(incsp, offset - get_mode_size_bytes(mode));
126
127         sched_add_before(node, push);
128         sched_remove(node);
129
130         be_peephole_node_replaced(node, proj);
131         exchange(node, proj);
132 }
133
134 static void peephole_ia32_Store(ir_node *node)
135 {
136         peephole_ia32_Store_IncSP_to_push(node);
137 }
138 #endif
139
140 // only optimize up to 48 stores behind IncSPs
141 #define MAXPUSH_OPTIMIZE        48
142
143 /**
144  * Tries to create pushs from IncSP,Store combinations
145  */
146 static void peephole_IncSP_Store_to_push(ir_node *irn)
147 {
148         int i;
149         int offset;
150         ir_node *node;
151         ir_node *stores[MAXPUSH_OPTIMIZE];
152         ir_node *block = get_nodes_block(irn);
153         ir_graph *irg = cg->irg;
154         ir_node *curr_sp;
155         ir_mode *spmode = get_irn_mode(irn);
156
157         memset(stores, 0, sizeof(stores));
158
159         assert(be_is_IncSP(irn));
160
161         offset = be_get_IncSP_offset(irn);
162         if(offset < 4)
163                 return;
164
165         /*
166          * We first walk the schedule after the IncSP node as long as we find
167          * suitable stores that could be transformed to a push.
168          * We save them into the stores array which is sorted by the frame offset/4
169          * attached to the node
170          */
171         for(node = sched_next(irn); !sched_is_end(node); node = sched_next(node)) {
172                 ir_node *mem;
173                 int offset;
174                 int storeslot;
175
176                 // it has to be a store
177                 if(!is_ia32_Store(node))
178                         break;
179
180                 // it has to use our sp value
181                 if(get_irn_n(node, n_ia32_base) != irn)
182                         continue;
183                 // store has to be attached to NoMem
184                 mem = get_irn_n(node, n_ia32_mem);
185                 if(!is_NoMem(mem)) {
186                         continue;
187                 }
188
189                 /* unfortunately we can't support the full AMs possible for push at the
190                  * moment. TODO: fix this */
191                 if(get_ia32_am_scale(node) > 0 || !is_ia32_NoReg_GP(get_irn_n(node, n_ia32_index)))
192                         break;
193
194                 offset = get_ia32_am_offs_int(node);
195
196                 storeslot = offset / 4;
197                 if(storeslot >= MAXPUSH_OPTIMIZE)
198                         continue;
199
200                 // storing into the same slot twice is bad (and shouldn't happen...)
201                 if(stores[storeslot] != NULL)
202                         break;
203
204                 // storing at half-slots is bad
205                 if(offset % 4 != 0)
206                         break;
207
208                 stores[storeslot] = node;
209         }
210
211         curr_sp = be_get_IncSP_pred(irn);
212
213         // walk the stores in inverse order and create pushs for them
214         i = (offset / 4) - 1;
215         if(i >= MAXPUSH_OPTIMIZE) {
216                 i = MAXPUSH_OPTIMIZE - 1;
217         }
218
219         for( ; i >= 0; --i) {
220                 const arch_register_t *spreg;
221                 ir_node *push;
222                 ir_node *val, *mem, *mem_proj;
223                 ir_node *store = stores[i];
224                 ir_node *noreg = ia32_new_NoReg_gp(cg);
225
226                 if(store == NULL || is_Bad(store))
227                         break;
228
229                 val = get_irn_n(store, n_ia32_unary_op);
230                 mem = get_irn_n(store, n_ia32_mem);
231                 spreg = arch_get_irn_register(cg->arch_env, curr_sp);
232
233                 push = new_rd_ia32_Push(get_irn_dbg_info(store), irg, block, noreg, noreg, mem, curr_sp, val);
234
235                 sched_add_before(irn, push);
236
237                 // create stackpointer proj
238                 curr_sp = new_r_Proj(irg, block, push, spmode, pn_ia32_Push_stack);
239                 arch_set_irn_register(cg->arch_env, curr_sp, spreg);
240
241                 // create memory proj
242                 mem_proj = new_r_Proj(irg, block, push, mode_M, pn_ia32_Push_M);
243
244                 // use the memproj now
245                 exchange(store, mem_proj);
246
247                 // we can remove the store now
248                 sched_remove(store);
249
250                 offset -= 4;
251         }
252
253         be_set_IncSP_offset(irn, offset);
254         be_set_IncSP_pred(irn, curr_sp);
255         be_peephole_node_replaced(irn, irn);
256 }
257
258 /**
259  * Tries to optimize two following IncSP.
260  */
261 static void peephole_IncSP_IncSP(ir_node *node)
262 {
263         int      pred_offs;
264         int      curr_offs;
265         int      offs;
266         ir_node *pred = be_get_IncSP_pred(node);
267         ir_node *predpred;
268
269         if(!be_is_IncSP(pred))
270                 return;
271
272         if(get_irn_n_edges(pred) > 1)
273                 return;
274
275         pred_offs = be_get_IncSP_offset(pred);
276         curr_offs = be_get_IncSP_offset(node);
277
278         if(pred_offs == BE_STACK_FRAME_SIZE_EXPAND) {
279                 if(curr_offs != BE_STACK_FRAME_SIZE_SHRINK) {
280                         return;
281                 }
282                 offs = 0;
283         } else if(pred_offs == BE_STACK_FRAME_SIZE_SHRINK) {
284                 if(curr_offs != BE_STACK_FRAME_SIZE_EXPAND) {
285                         return;
286                 }
287                 offs = 0;
288         } else if(curr_offs == BE_STACK_FRAME_SIZE_EXPAND
289                         || curr_offs == BE_STACK_FRAME_SIZE_SHRINK) {
290                 return;
291         } else {
292                 offs = curr_offs + pred_offs;
293         }
294
295         /* add pred offset to ours and remove pred IncSP */
296         be_set_IncSP_offset(node, offs);
297
298         predpred = be_get_IncSP_pred(pred);
299         be_peephole_node_replaced(pred, predpred);
300
301         /* rewire dependency edges */
302         edges_reroute_kind(pred, predpred, EDGE_KIND_DEP, current_ir_graph);
303         be_set_IncSP_pred(node, predpred);
304         sched_remove(pred);
305
306         be_kill_node(pred);
307 }
308
309 static const arch_register_t *get_free_gp_reg(void)
310 {
311         int i;
312
313         for(i = 0; i < N_ia32_gp_REGS; ++i) {
314                 const arch_register_t *reg = &ia32_gp_regs[i];
315                 if(arch_register_type_is(reg, ignore))
316                         continue;
317
318                 if(be_peephole_get_value(CLASS_ia32_gp, i) == NULL)
319                         return &ia32_gp_regs[i];
320         }
321
322         return NULL;
323 }
324
325 static void peephole_be_IncSP(ir_node *node)
326 {
327         const arch_register_t *esp = &ia32_gp_regs[REG_ESP];
328         const arch_register_t *reg;
329         ir_graph              *irg;
330         dbg_info              *dbgi;
331         ir_node               *block;
332         ir_node               *keep;
333         ir_node               *val;
334         ir_node               *pop;
335         ir_node               *noreg;
336         ir_node               *stack;
337         int                    offset;
338
339         /* first optimize incsp->incsp combinations */
340         peephole_IncSP_IncSP(node);
341
342         /* transform IncSP->Store combinations to Push where possible */
343         peephole_IncSP_Store_to_push(node);
344
345         /* replace IncSP -4 by Pop freereg when possible */
346         offset = be_get_IncSP_offset(node);
347         if(offset != -4)
348                 return;
349
350         if(arch_get_irn_register(arch_env, node) != esp)
351                 return;
352
353         reg = get_free_gp_reg();
354         if(reg == NULL)
355                 return;
356
357         irg   = current_ir_graph;
358         dbgi  = get_irn_dbg_info(node);
359         block = get_nodes_block(node);
360         noreg = ia32_new_NoReg_gp(cg);
361         stack = be_get_IncSP_pred(node);
362         pop   = new_rd_ia32_Pop(dbgi, irg, block, noreg, noreg, new_NoMem(), stack);
363
364         stack = new_r_Proj(irg, block, pop, mode_Iu, pn_ia32_Pop_stack);
365         arch_set_irn_register(arch_env, stack, esp);
366         val   = new_r_Proj(irg, block, pop, mode_Iu, pn_ia32_Pop_res);
367         arch_set_irn_register(arch_env, val, reg);
368
369         sched_add_before(node, pop);
370
371         keep  = sched_next(node);
372         if(!be_is_Keep(keep)) {
373                 ir_node *in[1];
374                 in[0] = val;
375                 keep = be_new_Keep(&ia32_reg_classes[CLASS_ia32_gp], irg, block, 1, in);
376                 sched_add_before(node, keep);
377         } else {
378                 be_Keep_add_node(keep, &ia32_reg_classes[CLASS_ia32_gp], val);
379         }
380
381         be_peephole_node_replaced(node, stack);
382
383         exchange(node, stack);
384         sched_remove(node);
385 }
386
387 /**
388  * Peephole optimisation for ia32_Const's
389  */
390 static void peephole_ia32_Const(ir_node *node)
391 {
392         const ia32_immediate_attr_t *attr = get_ia32_immediate_attr_const(node);
393         const arch_register_t       *reg;
394         ir_graph                    *irg = current_ir_graph;
395         ir_node                     *block;
396         dbg_info                    *dbgi;
397         ir_node                     *produceval;
398         ir_node                     *xor;
399         ir_node                     *noreg;
400
401         /* try to transform a mov 0, reg to xor reg reg */
402         if(attr->offset != 0 || attr->symconst != NULL)
403                 return;
404         /* xor destroys the flags, so no-one must be using them */
405         if(be_peephole_get_value(CLASS_ia32_flags, REG_EFLAGS) != NULL)
406                 return;
407
408         reg = arch_get_irn_register(arch_env, node);
409         assert(be_peephole_get_reg_value(reg) == NULL);
410
411         /* create xor(produceval, produceval) */
412         block      = get_nodes_block(node);
413         dbgi       = get_irn_dbg_info(node);
414         produceval = new_rd_ia32_ProduceVal(dbgi, irg, block);
415         arch_set_irn_register(arch_env, produceval, reg);
416
417         noreg = ia32_new_NoReg_gp(cg);
418         xor   = new_rd_ia32_Xor(dbgi, irg, block, noreg, noreg, new_NoMem(),
419                                 produceval, produceval);
420         arch_set_irn_register(arch_env, xor, reg);
421
422         sched_add_before(node, produceval);
423         sched_add_before(node, xor);
424
425         be_peephole_node_replaced(node, xor);
426         exchange(node, xor);
427         sched_remove(node);
428 }
429
430 static INLINE int is_noreg(ia32_code_gen_t *cg, const ir_node *node)
431 {
432         return node == cg->noreg_gp;
433 }
434
435 static ir_node *create_immediate_from_int(ia32_code_gen_t *cg, int val)
436 {
437         ir_graph *irg         = current_ir_graph;
438         ir_node  *start_block = get_irg_start_block(irg);
439         ir_node  *immediate   = new_rd_ia32_Immediate(NULL, irg, start_block, NULL,
440                                                       0, val);
441         arch_set_irn_register(cg->arch_env, immediate, &ia32_gp_regs[REG_GP_NOREG]);
442
443         return immediate;
444 }
445
446 static ir_node *create_immediate_from_am(ia32_code_gen_t *cg,
447                                          const ir_node *node)
448 {
449         ir_graph  *irg     = get_irn_irg(node);
450         ir_node   *block   = get_nodes_block(node);
451         int        offset  = get_ia32_am_offs_int(node);
452         int        sc_sign = is_ia32_am_sc_sign(node);
453         ir_entity *entity  = get_ia32_am_sc(node);
454         ir_node   *res;
455
456         res = new_rd_ia32_Immediate(NULL, irg, block, entity, sc_sign, offset);
457         arch_set_irn_register(cg->arch_env, res, &ia32_gp_regs[REG_GP_NOREG]);
458         return res;
459 }
460
461 static int is_am_one(const ir_node *node)
462 {
463         int        offset  = get_ia32_am_offs_int(node);
464         ir_entity *entity  = get_ia32_am_sc(node);
465
466         return offset == 1 && entity == NULL;
467 }
468
469 static int is_am_minus_one(const ir_node *node)
470 {
471         int        offset  = get_ia32_am_offs_int(node);
472         ir_entity *entity  = get_ia32_am_sc(node);
473
474         return offset == -1 && entity == NULL;
475 }
476
477 /**
478  * Transforms a LEA into an Add or SHL if possible.
479  */
480 static void peephole_ia32_Lea(ir_node *node)
481 {
482         const arch_env_t      *arch_env = cg->arch_env;
483         ir_graph              *irg      = current_ir_graph;
484         ir_node               *base;
485         ir_node               *index;
486         const arch_register_t *base_reg;
487         const arch_register_t *index_reg;
488         const arch_register_t *out_reg;
489         int                    scale;
490         int                    has_immediates;
491         ir_node               *op1;
492         ir_node               *op2;
493         dbg_info              *dbgi;
494         ir_node               *block;
495         ir_node               *res;
496         ir_node               *noreg;
497         ir_node               *nomem;
498
499         assert(is_ia32_Lea(node));
500
501         /* we can only do this if are allowed to globber the flags */
502         if(be_peephole_get_value(CLASS_ia32_flags, REG_EFLAGS) != NULL)
503                 return;
504
505         base  = get_irn_n(node, n_ia32_Lea_base);
506         index = get_irn_n(node, n_ia32_Lea_index);
507
508         if(is_noreg(cg, base)) {
509                 base     = NULL;
510                 base_reg = NULL;
511         } else {
512                 base_reg = arch_get_irn_register(arch_env, base);
513         }
514         if(is_noreg(cg, index)) {
515                 index     = NULL;
516                 index_reg = NULL;
517         } else {
518                 index_reg = arch_get_irn_register(arch_env, index);
519         }
520
521         if(base == NULL && index == NULL) {
522                 /* we shouldn't construct these in the first place... */
523 #ifdef DEBUG_libfirm
524                 ir_fprintf(stderr, "Optimisation warning: found immediate only lea\n");
525 #endif
526                 return;
527         }
528
529         out_reg = arch_get_irn_register(arch_env, node);
530         scale   = get_ia32_am_scale(node);
531         assert(!is_ia32_need_stackent(node) || get_ia32_frame_ent(node) != NULL);
532         /* check if we have immediates values (frame entities should already be
533          * expressed in the offsets) */
534         if(get_ia32_am_offs_int(node) != 0 || get_ia32_am_sc(node) != NULL) {
535                 has_immediates = 1;
536         } else {
537                 has_immediates = 0;
538         }
539
540         /* we can transform leas where the out register is the same as either the
541          * base or index register back to an Add or Shl */
542         if(out_reg == base_reg) {
543                 if(index == NULL) {
544 #ifdef DEBUG_libfirm
545                         if(!has_immediates) {
546                                 ir_fprintf(stderr, "Optimisation warning: found lea which is "
547                                            "just a copy\n");
548                         }
549 #endif
550                         op1 = base;
551                         goto make_add_immediate;
552                 }
553                 if(scale == 0 && !has_immediates) {
554                         op1 = base;
555                         op2 = index;
556                         goto make_add;
557                 }
558                 /* can't create an add */
559                 return;
560         } else if(out_reg == index_reg) {
561                 if(base == NULL) {
562                         if(has_immediates && scale == 0) {
563                                 op1 = index;
564                                 goto make_add_immediate;
565                         } else if(!has_immediates && scale > 0) {
566                                 op1 = index;
567                                 op2 = create_immediate_from_int(cg, scale);
568                                 goto make_shl;
569                         } else if(!has_immediates) {
570 #ifdef DEBUG_libfirm
571                                 ir_fprintf(stderr, "Optimisation warning: found lea which is "
572                                            "just a copy\n");
573 #endif
574                         }
575                 } else if(scale == 0 && !has_immediates) {
576                         op1 = index;
577                         op2 = base;
578                         goto make_add;
579                 }
580                 /* can't create an add */
581                 return;
582         } else {
583                 /* can't create an add */
584                 return;
585         }
586
587 make_add_immediate:
588         if(cg->isa->opt & IA32_OPT_INCDEC) {
589                 if(is_am_one(node)) {
590                         dbgi  = get_irn_dbg_info(node);
591                         block = get_nodes_block(node);
592                         res   = new_rd_ia32_Inc(dbgi, irg, block, op1);
593                         arch_set_irn_register(arch_env, res, out_reg);
594                         goto exchange;
595                 }
596                 if(is_am_minus_one(node)) {
597                         dbgi  = get_irn_dbg_info(node);
598                         block = get_nodes_block(node);
599                         res   = new_rd_ia32_Dec(dbgi, irg, block, op1);
600                         arch_set_irn_register(arch_env, res, out_reg);
601                         goto exchange;
602                 }
603         }
604         op2 = create_immediate_from_am(cg, node);
605
606 make_add:
607         dbgi  = get_irn_dbg_info(node);
608         block = get_nodes_block(node);
609         noreg = ia32_new_NoReg_gp(cg);
610         nomem = new_NoMem();
611         res   = new_rd_ia32_Add(dbgi, irg, block, noreg, noreg, nomem, op1, op2);
612         arch_set_irn_register(arch_env, res, out_reg);
613         set_ia32_commutative(res);
614         goto exchange;
615
616 make_shl:
617         dbgi  = get_irn_dbg_info(node);
618         block = get_nodes_block(node);
619         noreg = ia32_new_NoReg_gp(cg);
620         nomem = new_NoMem();
621         res   = new_rd_ia32_Shl(dbgi, irg, block, op1, op2);
622         arch_set_irn_register(arch_env, res, out_reg);
623         goto exchange;
624
625 exchange:
626         SET_IA32_ORIG_NODE(res, ia32_get_old_node_name(cg, node));
627
628         /* add new ADD/SHL to schedule */
629         sched_add_before(node, res);
630
631         DBG_OPT_LEA2ADD(node, res);
632
633         /* remove the old LEA */
634         sched_remove(node);
635
636         /* exchange the Add and the LEA */
637         be_peephole_node_replaced(node, res);
638         exchange(node, res);
639 }
640
641 /**
642  * Register a peephole optimisation function.
643  */
644 static void register_peephole_optimisation(ir_op *op, peephole_opt_func func) {
645         assert(op->ops.generic == NULL);
646         op->ops.generic = (void*) func;
647 }
648
649 /* Perform peephole-optimizations. */
650 void ia32_peephole_optimization(ia32_code_gen_t *new_cg)
651 {
652         cg       = new_cg;
653         arch_env = cg->arch_env;
654
655         /* register peephole optimisations */
656         clear_irp_opcodes_generic_func();
657         register_peephole_optimisation(op_ia32_Const, peephole_ia32_Const);
658         //register_peephole_optimisation(op_ia32_Store, peephole_ia32_Store);
659         register_peephole_optimisation(op_be_IncSP, peephole_be_IncSP);
660         register_peephole_optimisation(op_ia32_Lea, peephole_ia32_Lea);
661
662         be_peephole_opt(cg->birg);
663 }
664
665 /**
666  * Removes node from schedule if it is not used anymore. If irn is a mode_T node
667  * all it's Projs are removed as well.
668  * @param irn  The irn to be removed from schedule
669  */
670 static INLINE void try_kill(ir_node *node)
671 {
672         if(get_irn_mode(node) == mode_T) {
673                 const ir_edge_t *edge, *next;
674                 foreach_out_edge_safe(node, edge, next) {
675                         ir_node *proj = get_edge_src_irn(edge);
676                         try_kill(proj);
677                 }
678         }
679
680         if(get_irn_n_edges(node) != 0)
681                 return;
682
683         if (sched_is_scheduled(node)) {
684                 sched_remove(node);
685         }
686
687         be_kill_node(node);
688 }
689
690 static void optimize_conv_store(ir_node *node)
691 {
692         ir_node *pred;
693         ir_node *pred_proj;
694         ir_mode *conv_mode;
695         ir_mode *store_mode;
696
697         if(!is_ia32_Store(node) && !is_ia32_Store8Bit(node))
698                 return;
699
700         assert(n_ia32_Store_val == n_ia32_Store8Bit_val);
701         pred_proj = get_irn_n(node, n_ia32_Store_val);
702         if(is_Proj(pred_proj)) {
703                 pred = get_Proj_pred(pred_proj);
704         } else {
705                 pred = pred_proj;
706         }
707         if(!is_ia32_Conv_I2I(pred) && !is_ia32_Conv_I2I8Bit(pred))
708                 return;
709         if(get_ia32_op_type(pred) != ia32_Normal)
710                 return;
711
712         /* the store only stores the lower bits, so we only need the conv
713          * it it shrinks the mode */
714         conv_mode  = get_ia32_ls_mode(pred);
715         store_mode = get_ia32_ls_mode(node);
716         if(get_mode_size_bits(conv_mode) < get_mode_size_bits(store_mode))
717                 return;
718
719         set_irn_n(node, n_ia32_Store_val, get_irn_n(pred, n_ia32_Conv_I2I_val));
720         if(get_irn_n_edges(pred_proj) == 0) {
721                 be_kill_node(pred_proj);
722                 if(pred != pred_proj)
723                         be_kill_node(pred);
724         }
725 }
726
727 static void optimize_load_conv(ir_node *node)
728 {
729         ir_node *pred, *predpred;
730         ir_mode *load_mode;
731         ir_mode *conv_mode;
732
733         if (!is_ia32_Conv_I2I(node) && !is_ia32_Conv_I2I8Bit(node))
734                 return;
735
736         assert(n_ia32_Conv_I2I_val == n_ia32_Conv_I2I8Bit_val);
737         pred = get_irn_n(node, n_ia32_Conv_I2I_val);
738         if(!is_Proj(pred))
739                 return;
740
741         predpred = get_Proj_pred(pred);
742         if(!is_ia32_Load(predpred))
743                 return;
744
745         /* the load is sign extending the upper bits, so we only need the conv
746          * if it shrinks the mode */
747         load_mode = get_ia32_ls_mode(predpred);
748         conv_mode = get_ia32_ls_mode(node);
749         if(get_mode_size_bits(conv_mode) < get_mode_size_bits(load_mode))
750                 return;
751
752         if(get_mode_sign(conv_mode) != get_mode_sign(load_mode)) {
753                 /* change the load if it has only 1 user */
754                 if(get_irn_n_edges(pred) == 1) {
755                         ir_mode *newmode;
756                         if(get_mode_sign(conv_mode)) {
757                                 newmode = find_signed_mode(load_mode);
758                         } else {
759                                 newmode = find_unsigned_mode(load_mode);
760                         }
761                         assert(newmode != NULL);
762                         set_ia32_ls_mode(predpred, newmode);
763                 } else {
764                         /* otherwise we have to keep the conv */
765                         return;
766                 }
767         }
768
769         /* kill the conv */
770         exchange(node, pred);
771 }
772
773 static void optimize_conv_conv(ir_node *node)
774 {
775         ir_node *pred_proj, *pred, *result_conv;
776         ir_mode *pred_mode, *conv_mode;
777         int      conv_mode_bits;
778         int      pred_mode_bits;
779
780         if (!is_ia32_Conv_I2I(node) && !is_ia32_Conv_I2I8Bit(node))
781                 return;
782
783         assert(n_ia32_Conv_I2I_val == n_ia32_Conv_I2I8Bit_val);
784         pred_proj = get_irn_n(node, n_ia32_Conv_I2I_val);
785         if(is_Proj(pred_proj))
786                 pred = get_Proj_pred(pred_proj);
787         else
788                 pred = pred_proj;
789
790         if(!is_ia32_Conv_I2I(pred) && !is_ia32_Conv_I2I8Bit(pred))
791                 return;
792
793         /* we know that after a conv, the upper bits are sign extended
794          * so we only need the 2nd conv if it shrinks the mode */
795         conv_mode      = get_ia32_ls_mode(node);
796         conv_mode_bits = get_mode_size_bits(conv_mode);
797         pred_mode      = get_ia32_ls_mode(pred);
798         pred_mode_bits = get_mode_size_bits(pred_mode);
799
800         if(conv_mode_bits == pred_mode_bits
801                         && get_mode_sign(conv_mode) == get_mode_sign(pred_mode)) {
802                 result_conv = pred_proj;
803         } else if(conv_mode_bits <= pred_mode_bits) {
804                 /* if 2nd conv is smaller then first conv, then we can always take the
805                  * 2nd conv */
806                 if(get_irn_n_edges(pred_proj) == 1) {
807                         result_conv = pred_proj;
808                         set_ia32_ls_mode(pred, conv_mode);
809
810                         /* Argh:We must change the opcode to 8bit AND copy the register constraints */
811                         if (get_mode_size_bits(conv_mode) == 8) {
812                                 set_irn_op(pred, op_ia32_Conv_I2I8Bit);
813                                 set_ia32_in_req_all(pred, get_ia32_in_req_all(node));
814                         }
815                 } else {
816                         /* we don't want to end up with 2 loads, so we better do nothing */
817                         if(get_irn_mode(pred) == mode_T) {
818                                 return;
819                         }
820
821                         result_conv = exact_copy(pred);
822                         set_ia32_ls_mode(result_conv, conv_mode);
823
824                         /* Argh:We must change the opcode to 8bit AND copy the register constraints */
825                         if (get_mode_size_bits(conv_mode) == 8) {
826                                 set_irn_op(result_conv, op_ia32_Conv_I2I8Bit);
827                                 set_ia32_in_req_all(result_conv, get_ia32_in_req_all(node));
828                         }
829                 }
830         } else {
831                 /* if both convs have the same sign, then we can take the smaller one */
832                 if(get_mode_sign(conv_mode) == get_mode_sign(pred_mode)) {
833                         result_conv = pred_proj;
834                 } else {
835                         /* no optimisation possible if smaller conv is sign-extend */
836                         if(mode_is_signed(pred_mode)) {
837                                 return;
838                         }
839                         /* we can take the smaller conv if it is unsigned */
840                         result_conv = pred_proj;
841                 }
842         }
843
844         /* kill the conv */
845         exchange(node, result_conv);
846
847         if(get_irn_n_edges(pred_proj) == 0) {
848                 be_kill_node(pred_proj);
849                 if(pred != pred_proj)
850                         be_kill_node(pred);
851         }
852         optimize_conv_conv(result_conv);
853 }
854
855 static void optimize_node(ir_node *node, void *env)
856 {
857         (void) env;
858
859         optimize_load_conv(node);
860         optimize_conv_store(node);
861         optimize_conv_conv(node);
862 }
863
864 /**
865  * Performs conv and address mode optimization.
866  */
867 void ia32_optimize_graph(ia32_code_gen_t *cg)
868 {
869         irg_walk_blkwise_graph(cg->irg, NULL, optimize_node, cg);
870
871         if (cg->dump)
872                 be_dump(cg->irg, "-opt", dump_ir_block_graph_sched);
873 }
874
875 void ia32_init_optimize(void)
876 {
877         FIRM_DBG_REGISTER(dbg, "firm.be.ia32.optimize");
878 }