improve peephole API, do IncSP stuff as peephole opts, add IncSP -4 -> Pop opt
[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
42 #include "../be_t.h"
43 #include "../beabi.h"
44 #include "../benode_t.h"
45 #include "../besched_t.h"
46 #include "../bepeephole.h"
47
48 #include "ia32_new_nodes.h"
49 #include "bearch_ia32_t.h"
50 #include "gen_ia32_regalloc_if.h"
51 #include "ia32_transform.h"
52 #include "ia32_dbg_stat.h"
53 #include "ia32_util.h"
54
55 DEBUG_ONLY(static firm_dbg_module_t *dbg = NULL;)
56
57 static const arch_env_t *arch_env;
58 static ia32_code_gen_t  *cg;
59
60 typedef int is_op_func_t(const ir_node *n);
61 typedef ir_node *load_func_t(dbg_info *db, ir_graph *irg, ir_node *block, ir_node *base, ir_node *index, ir_node *mem);
62
63 /**
64  * checks if a node represents the NOREG value
65  */
66 static INLINE int be_is_NoReg(ia32_code_gen_t *cg, const ir_node *irn) {
67         return irn == cg->noreg_gp || irn == cg->noreg_xmm || irn == cg->noreg_vfp;
68 }
69
70 /********************************************************************************************************
71  *  _____                _           _         ____        _   _           _          _   _
72  * |  __ \              | |         | |       / __ \      | | (_)         (_)        | | (_)
73  * | |__) |__  ___ _ __ | |__   ___ | | ___  | |  | |_ __ | |_ _ _ __ ___  _ ______ _| |_ _  ___  _ __
74  * |  ___/ _ \/ _ \ '_ \| '_ \ / _ \| |/ _ \ | |  | | '_ \| __| | '_ ` _ \| |_  / _` | __| |/ _ \| '_ \
75  * | |  |  __/  __/ |_) | | | | (_) | |  __/ | |__| | |_) | |_| | | | | | | |/ / (_| | |_| | (_) | | | |
76  * |_|   \___|\___| .__/|_| |_|\___/|_|\___|  \____/| .__/ \__|_|_| |_| |_|_/___\__,_|\__|_|\___/|_| |_|
77  *                | |                               | |
78  *                |_|                               |_|
79  ********************************************************************************************************/
80
81 /**
82  * NOTE: THESE PEEPHOLE OPTIMIZATIONS MUST BE CALLED AFTER SCHEDULING AND REGISTER ALLOCATION.
83  */
84
85 // only optimize up to 48 stores behind IncSPs
86 #define MAXPUSH_OPTIMIZE        48
87
88 /**
89  * Tries to create pushs from IncSP,Store combinations
90  */
91 static void ia32_create_Pushs(ir_node *irn)
92 {
93         int i;
94         int offset;
95         ir_node *node;
96         ir_node *stores[MAXPUSH_OPTIMIZE];
97         ir_node *block = get_nodes_block(irn);
98         ir_graph *irg = cg->irg;
99         ir_node *curr_sp;
100         ir_mode *spmode = get_irn_mode(irn);
101
102         memset(stores, 0, sizeof(stores));
103
104         assert(be_is_IncSP(irn));
105
106         offset = be_get_IncSP_offset(irn);
107         if(offset < 4)
108                 return;
109
110         /*
111          * We first walk the schedule after the IncSP node as long as we find
112          * suitable stores that could be transformed to a push.
113          * We save them into the stores array which is sorted by the frame offset/4
114          * attached to the node
115          */
116         for(node = sched_next(irn); !sched_is_end(node); node = sched_next(node)) {
117                 ir_node *mem;
118                 int offset;
119                 int storeslot;
120
121                 // it has to be a store
122                 if(!is_ia32_Store(node))
123                         break;
124
125                 // it has to use our sp value
126                 if(get_irn_n(node, n_ia32_base) != irn)
127                         continue;
128                 // store has to be attached to NoMem
129                 mem = get_irn_n(node, n_ia32_mem);
130                 if(!is_NoMem(mem)) {
131                         continue;
132                 }
133
134                 /* unfortunately we can't support the full AMs possible for push at the
135                  * moment. TODO: fix this */
136                 if(get_ia32_am_scale(node) > 0 || !is_ia32_NoReg_GP(get_irn_n(node, n_ia32_index)))
137                         break;
138
139                 offset = get_ia32_am_offs_int(node);
140
141                 storeslot = offset / 4;
142                 if(storeslot >= MAXPUSH_OPTIMIZE)
143                         continue;
144
145                 // storing into the same slot twice is bad (and shouldn't happen...)
146                 if(stores[storeslot] != NULL)
147                         break;
148
149                 // storing at half-slots is bad
150                 if(offset % 4 != 0)
151                         break;
152
153                 stores[storeslot] = node;
154         }
155
156         curr_sp = be_get_IncSP_pred(irn);
157
158         // walk the stores in inverse order and create pushs for them
159         i = (offset / 4) - 1;
160         if(i >= MAXPUSH_OPTIMIZE) {
161                 i = MAXPUSH_OPTIMIZE - 1;
162         }
163
164         for( ; i >= 0; --i) {
165                 const arch_register_t *spreg;
166                 ir_node *push;
167                 ir_node *val, *mem, *mem_proj;
168                 ir_node *store = stores[i];
169                 ir_node *noreg = ia32_new_NoReg_gp(cg);
170
171                 if(store == NULL || is_Bad(store))
172                         break;
173
174                 val = get_irn_n(store, n_ia32_unary_op);
175                 mem = get_irn_n(store, n_ia32_mem);
176                 spreg = arch_get_irn_register(cg->arch_env, curr_sp);
177
178                 push = new_rd_ia32_Push(get_irn_dbg_info(store), irg, block, noreg, noreg, mem, curr_sp, val);
179
180                 set_ia32_am_support(push, ia32_am_Source, ia32_am_unary);
181
182                 sched_add_before(irn, push);
183
184                 // create stackpointer proj
185                 curr_sp = new_r_Proj(irg, block, push, spmode, pn_ia32_Push_stack);
186                 arch_set_irn_register(cg->arch_env, curr_sp, spreg);
187
188                 // create memory proj
189                 mem_proj = new_r_Proj(irg, block, push, mode_M, pn_ia32_Push_M);
190
191                 // use the memproj now
192                 exchange(store, mem_proj);
193
194                 // we can remove the store now
195                 sched_remove(store);
196
197                 offset -= 4;
198         }
199
200         be_set_IncSP_offset(irn, offset);
201
202         // can we remove the IncSP now?
203         if(offset == 0) {
204                 const ir_edge_t *edge, *next;
205
206                 foreach_out_edge_safe(irn, edge, next) {
207                         ir_node *arg = get_edge_src_irn(edge);
208                         int pos = get_edge_src_pos(edge);
209
210                         set_irn_n(arg, pos, curr_sp);
211                 }
212
213                 set_irn_n(irn, 0, new_Bad());
214                 sched_remove(irn);
215         } else {
216                 set_irn_n(irn, 0, curr_sp);
217         }
218 }
219
220 /**
221  * Performs Peephole Optimizations for IncSP nodes.
222  */
223 static void ia32_peephole_optimize_node(ir_node *node, void *env)
224 {
225         (void) env;
226         if (be_is_IncSP(node)) {
227                 ia32_create_Pushs(node);
228         }
229 }
230
231 /**
232  * Tries to optimize two following IncSP.
233  */
234 static void peephole_IncSP_IncSP(ir_node *node)
235 {
236         int      pred_offs;
237         int      curr_offs;
238         int      offs;
239         ir_node *pred = be_get_IncSP_pred(node);
240         ir_node *predpred;
241
242         if(!be_is_IncSP(pred))
243                 return;
244
245         if(get_irn_n_edges(pred) > 1)
246                 return;
247
248         pred_offs = be_get_IncSP_offset(pred);
249         curr_offs = be_get_IncSP_offset(node);
250
251         if(pred_offs == BE_STACK_FRAME_SIZE_EXPAND) {
252                 if(curr_offs != BE_STACK_FRAME_SIZE_SHRINK) {
253                         return;
254                 }
255                 offs = 0;
256         } else if(pred_offs == BE_STACK_FRAME_SIZE_SHRINK) {
257                 if(curr_offs != BE_STACK_FRAME_SIZE_EXPAND) {
258                         return;
259                 }
260                 offs = 0;
261         } else if(curr_offs == BE_STACK_FRAME_SIZE_EXPAND
262                         || curr_offs == BE_STACK_FRAME_SIZE_SHRINK) {
263                 return;
264         } else {
265                 offs = curr_offs + pred_offs;
266         }
267
268         /* add pred offset to ours and remove pred IncSP */
269         be_set_IncSP_offset(node, offs);
270
271         predpred = be_get_IncSP_pred(pred);
272         be_peephole_node_replaced(pred, predpred);
273
274         /* rewire dependency edges */
275         edges_reroute_kind(pred, predpred, EDGE_KIND_DEP, current_ir_graph);
276         be_set_IncSP_pred(node, predpred);
277         sched_remove(pred);
278
279         be_kill_node(pred);
280 }
281
282 static const arch_register_t *get_free_gp_reg(void)
283 {
284         int i;
285
286         for(i = 0; i < N_ia32_gp_REGS; ++i) {
287                 const arch_register_t *reg = &ia32_gp_regs[i];
288                 if(arch_register_type_is(reg, ignore))
289                         continue;
290
291                 if(be_peephole_get_value(CLASS_ia32_gp, i) == NULL)
292                         return &ia32_gp_regs[i];
293         }
294
295         return NULL;
296 }
297
298 static void peephole_be_IncSP(ir_node *node)
299 {
300         const arch_register_t *esp = &ia32_gp_regs[REG_ESP];
301         const arch_register_t *reg;
302         ir_graph              *irg;
303         dbg_info              *dbgi;
304         ir_node               *block;
305         ir_node               *keep;
306         ir_node               *val;
307         ir_node               *pop;
308         ir_node               *noreg;
309         ir_node               *stack;
310         int                    offset;
311
312         /* first optimize incsp->incsp combinations */
313         peephole_IncSP_IncSP(node);
314
315         /* replace IncSP -4 by Pop freereg when possible */
316         offset = be_get_IncSP_offset(node);
317         if(offset != -4)
318                 return;
319
320         if(arch_get_irn_register(arch_env, node) != esp)
321                 return;
322
323         reg = get_free_gp_reg();
324         if(reg == NULL)
325                 return;
326
327         irg   = current_ir_graph;
328         dbgi  = get_irn_dbg_info(node);
329         block = get_nodes_block(node);
330         noreg = ia32_new_NoReg_gp(cg);
331         stack = be_get_IncSP_pred(node);
332         pop   = new_rd_ia32_Pop(dbgi, irg, block, noreg, noreg, new_NoMem(), stack);
333
334         stack = new_r_Proj(irg, block, pop, mode_Iu, pn_ia32_Pop_stack);
335         arch_set_irn_register(arch_env, stack, esp);
336         val   = new_r_Proj(irg, block, pop, mode_Iu, pn_ia32_Pop_res);
337         arch_set_irn_register(arch_env, val, reg);
338
339         sched_add_before(node, pop);
340
341         keep  = sched_next(node);
342         if(!be_is_Keep(keep)) {
343                 ir_node *in[1];
344                 in[0] = val;
345                 keep = be_new_Keep(&ia32_reg_classes[CLASS_ia32_gp], irg, block, 1, in);
346                 sched_add_before(node, keep);
347         } else {
348                 be_Keep_add_node(keep, &ia32_reg_classes[CLASS_ia32_gp], val);
349         }
350
351         be_peephole_node_replaced(node, stack);
352
353         exchange(node, stack);
354         sched_remove(node);
355 }
356
357 /**
358  * Peephole optimisation for ia32_Const's
359  */
360 static void peephole_ia32_Const(ir_node *node)
361 {
362         const ia32_immediate_attr_t *attr = get_ia32_immediate_attr_const(node);
363         const arch_register_t       *reg;
364         ir_graph                    *irg = current_ir_graph;
365         ir_node                     *block;
366         dbg_info                    *dbgi;
367         ir_node                     *produceval;
368         ir_node                     *xor;
369         ir_node                     *noreg;
370
371         /* try to transform a mov 0, reg to xor reg reg */
372         if(attr->offset != 0 || attr->symconst != NULL)
373                 return;
374         /* xor destroys the flags, so no-one must be using them */
375         if(be_peephole_get_value(CLASS_ia32_flags, REG_EFLAGS) != NULL)
376                 return;
377
378         reg = arch_get_irn_register(arch_env, node);
379         assert(be_peephole_get_reg_value(reg) == NULL);
380
381         /* create xor(produceval, produceval) */
382         block      = get_nodes_block(node);
383         dbgi       = get_irn_dbg_info(node);
384         produceval = new_rd_ia32_ProduceVal(dbgi, irg, block);
385         arch_set_irn_register(arch_env, produceval, reg);
386
387         noreg = ia32_new_NoReg_gp(cg);
388         xor   = new_rd_ia32_Xor(dbgi, irg, block, noreg, noreg, new_NoMem(),
389                                 produceval, produceval);
390         arch_set_irn_register(arch_env, xor, reg);
391
392         sched_add_before(node, produceval);
393         sched_add_before(node, xor);
394
395         be_peephole_node_replaced(node, xor);
396         exchange(node, xor);
397         sched_remove(node);
398 }
399
400 /**
401  * Register a peephole optimisation function.
402  */
403 static void register_peephole_optimisation(ir_op *op, peephole_opt_func func) {
404         assert(op->ops.generic == NULL);
405         op->ops.generic = (void*) func;
406 }
407
408 /* Perform peephole-optimizations. */
409 void ia32_peephole_optimization(ir_graph *irg, ia32_code_gen_t *new_cg)
410 {
411         cg       = new_cg;
412         arch_env = cg->arch_env;
413
414         /* register peephole optimisations */
415         clear_irp_opcodes_generic_func();
416         register_peephole_optimisation(op_ia32_Const, peephole_ia32_Const);
417         register_peephole_optimisation(op_be_IncSP, peephole_be_IncSP);
418
419         be_peephole_opt(cg->birg);
420         irg_walk_graph(irg, ia32_peephole_optimize_node, NULL, NULL);
421 }
422
423 /**
424  * Removes node from schedule if it is not used anymore. If irn is a mode_T node
425  * all it's Projs are removed as well.
426  * @param irn  The irn to be removed from schedule
427  */
428 static INLINE void try_kill(ir_node *node)
429 {
430         if(get_irn_mode(node) == mode_T) {
431                 const ir_edge_t *edge, *next;
432                 foreach_out_edge_safe(node, edge, next) {
433                         ir_node *proj = get_edge_src_irn(edge);
434                         try_kill(proj);
435                 }
436         }
437
438         if(get_irn_n_edges(node) != 0)
439                 return;
440
441         if (sched_is_scheduled(node)) {
442                 sched_remove(node);
443         }
444
445         be_kill_node(node);
446 }
447
448 static void optimize_conv_store(ir_node *node)
449 {
450         ir_node *pred;
451         ir_mode *conv_mode;
452         ir_mode *store_mode;
453
454         if(!is_ia32_Store(node) && !is_ia32_Store8Bit(node))
455                 return;
456
457         pred = get_irn_n(node, 2);
458         if(!is_ia32_Conv_I2I(pred) && !is_ia32_Conv_I2I8Bit(pred))
459                 return;
460
461         /* the store only stores the lower bits, so we only need the conv
462          * it it shrinks the mode */
463         conv_mode  = get_ia32_ls_mode(pred);
464         store_mode = get_ia32_ls_mode(node);
465         if(get_mode_size_bits(conv_mode) < get_mode_size_bits(store_mode))
466                 return;
467
468         set_irn_n(node, 2, get_irn_n(pred, 2));
469         if(get_irn_n_edges(pred) == 0) {
470                 be_kill_node(pred);
471         }
472 }
473
474 static void optimize_load_conv(ir_node *node)
475 {
476         ir_node *pred, *predpred;
477         ir_mode *load_mode;
478         ir_mode *conv_mode;
479
480         if (!is_ia32_Conv_I2I(node) && !is_ia32_Conv_I2I8Bit(node))
481                 return;
482
483         pred = get_irn_n(node, 2);
484         if(!is_Proj(pred))
485                 return;
486
487         predpred = get_Proj_pred(pred);
488         if(!is_ia32_Load(predpred))
489                 return;
490
491         /* the load is sign extending the upper bits, so we only need the conv
492          * if it shrinks the mode */
493         load_mode = get_ia32_ls_mode(predpred);
494         conv_mode = get_ia32_ls_mode(node);
495         if(get_mode_size_bits(conv_mode) < get_mode_size_bits(load_mode))
496                 return;
497
498         if(get_mode_sign(conv_mode) != get_mode_sign(load_mode)) {
499                 /* change the load if it has only 1 user */
500                 if(get_irn_n_edges(pred) == 1) {
501                         ir_mode *newmode;
502                         if(get_mode_sign(conv_mode)) {
503                                 newmode = find_signed_mode(load_mode);
504                         } else {
505                                 newmode = find_unsigned_mode(load_mode);
506                         }
507                         assert(newmode != NULL);
508                         set_ia32_ls_mode(predpred, newmode);
509                 } else {
510                         /* otherwise we have to keep the conv */
511                         return;
512                 }
513         }
514
515         /* kill the conv */
516         exchange(node, pred);
517 }
518
519 static void optimize_conv_conv(ir_node *node)
520 {
521         ir_node *pred_proj, *pred, *result_conv;
522         ir_mode *pred_mode, *conv_mode;
523
524         if (!is_ia32_Conv_I2I(node) && !is_ia32_Conv_I2I8Bit(node))
525                 return;
526
527         assert(n_ia32_Conv_I2I_val == n_ia32_Conv_I2I8Bit_val);
528         pred_proj = get_irn_n(node, n_ia32_Conv_I2I_val);
529         if(is_Proj(pred_proj))
530                 pred = get_Proj_pred(pred_proj);
531         else
532                 pred = pred_proj;
533
534         if(!is_ia32_Conv_I2I(pred) && !is_ia32_Conv_I2I8Bit(pred))
535                 return;
536
537         /* we know that after a conv, the upper bits are sign extended
538          * so we only need the 2nd conv if it shrinks the mode */
539         conv_mode = get_ia32_ls_mode(node);
540         pred_mode = get_ia32_ls_mode(pred);
541         /* if 2nd conv is smaller then first conv, then we can always take the 2nd
542          * conv */
543         if(get_mode_size_bits(conv_mode) <= get_mode_size_bits(pred_mode)) {
544                 if(get_irn_n_edges(pred_proj) == 1) {
545                         result_conv = pred_proj;
546                         set_ia32_ls_mode(pred, conv_mode);
547
548                         /* Argh:We must change the opcode to 8bit AND copy the register constraints */
549                         if (get_mode_size_bits(conv_mode) == 8) {
550                                 set_irn_op(pred, op_ia32_Conv_I2I8Bit);
551                                 set_ia32_in_req_all(pred, get_ia32_in_req_all(node));
552                         }
553                 } else {
554                         /* we don't want to end up with 2 loads, so we better do nothing */
555                         if(get_irn_mode(pred) == mode_T) {
556                                 return;
557                         }
558
559                         result_conv = exact_copy(pred);
560                         set_ia32_ls_mode(result_conv, conv_mode);
561
562                         /* Argh:We must change the opcode to 8bit AND copy the register constraints */
563                         if (get_mode_size_bits(conv_mode) == 8) {
564                                 set_irn_op(result_conv, op_ia32_Conv_I2I8Bit);
565                                 set_ia32_in_req_all(result_conv, get_ia32_in_req_all(node));
566                         }
567                 }
568         } else {
569                 /* if both convs have the same sign, then we can take the smaller one */
570                 if(get_mode_sign(conv_mode) == get_mode_sign(pred_mode)) {
571                         result_conv = pred_proj;
572                 } else {
573                         /* no optimisation possible if smaller conv is sign-extend */
574                         if(mode_is_signed(pred_mode)) {
575                                 return;
576                         }
577                         /* we can take the smaller conv if it is unsigned */
578                         result_conv = pred_proj;
579                 }
580         }
581
582         /* kill the conv */
583         exchange(node, result_conv);
584
585         if(get_irn_n_edges(pred) == 0) {
586                 be_kill_node(pred);
587         }
588         optimize_conv_conv(result_conv);
589 }
590
591 static void optimize_node(ir_node *node, void *env)
592 {
593         (void) env;
594
595         optimize_load_conv(node);
596         optimize_conv_store(node);
597         optimize_conv_conv(node);
598 }
599
600 /**
601  * Performs conv and address mode optimization.
602  */
603 void ia32_optimize_graph(ia32_code_gen_t *cg)
604 {
605         irg_walk_blkwise_graph(cg->irg, NULL, optimize_node, cg);
606
607         if (cg->dump)
608                 be_dump(cg->irg, "-opt", dump_ir_block_graph_sched);
609 }
610
611 void ia32_init_optimize(void)
612 {
613         FIRM_DBG_REGISTER(dbg, "firm.be.ia32.optimize");
614 }