split graph state into properties and constraints
[libfirm] / ir / opt / convopt.c
1 /*
2  * Copyright (C) 1995-2011 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   conv node optimisation
23  * @author  Matthias Braun, Christoph Mallon
24  *
25  * Try to minimize the number of conv nodes by changing modes of operations.
26  * The typical example is the following structure:
27  *    (some node mode_Hs)
28  *            |                                       (some node_Hs)
29  *         Conv Is                                          |
30  *            |                                          Add Hs
31  *          Add Is            gets transformed to           |
32  *            |
33  *         Conv Hs
34  *
35  * TODO: * try to optimize cmp modes
36  *       * decide when it is useful to move the convs through phis
37  */
38 #include "config.h"
39
40 #include "iroptimize.h"
41
42 #include <assert.h>
43 #include <stdbool.h>
44 #include "debug.h"
45 #include "ircons.h"
46 #include "irgmod.h"
47 #include "irgopt.h"
48 #include "irnode_t.h"
49 #include "iropt_t.h"
50 #include "iredges_t.h"
51 #include "irgwalk.h"
52 #include "irprintf.h"
53 #include "irpass_t.h"
54 #include "tv.h"
55 #include "vrp.h"
56 #include "opt_manage.h"
57
58 DEBUG_ONLY(static firm_dbg_module_t *dbg;)
59
60 static inline int imin(int a, int b) { return a < b ? a : b; }
61
62 static bool is_optimizable_node(const ir_node *node, ir_mode *dest_mode)
63 {
64         switch (get_irn_opcode(node)) {
65         case iro_Add:
66         case iro_And:
67         case iro_Eor:
68         case iro_Minus:
69         case iro_Mul:
70         case iro_Not:
71         case iro_Or:
72         case iro_Phi:
73         case iro_Sub:
74                 return true;
75         case iro_Shl: {
76                 int modulo_shift = get_mode_modulo_shift(dest_mode);
77                 int old_shift    = get_mode_modulo_shift(get_irn_mode(node));
78                 /* bail out if modulo shift changes */
79                 if (modulo_shift != old_shift)
80                         return false;
81                 return true;
82         }
83
84         default:
85                 return false;
86         }
87 }
88
89 static ir_tarval* conv_const_tv(const ir_node* cnst, ir_mode* dest_mode)
90 {
91         return tarval_convert_to(get_Const_tarval(cnst), dest_mode);
92 }
93
94 static int is_downconv(ir_mode *src_mode, ir_mode *dest_mode)
95 {
96         return
97                 mode_is_int(src_mode) &&
98                 mode_is_int(dest_mode) &&
99                 get_mode_size_bits(dest_mode) <= get_mode_size_bits(src_mode);
100 }
101
102 static int get_conv_costs(const ir_node *node, ir_mode *dest_mode)
103 {
104         ir_mode *mode = get_irn_mode(node);
105         int arity;
106         int i;
107         int costs;
108
109         if (mode == dest_mode)
110                 return 0;
111
112         if (is_Const(node)) {
113                 /* TODO tarval module is incomplete and can't convert floats to ints */
114                 return conv_const_tv(node, dest_mode) == tarval_bad ? 1 : 0;
115         }
116
117         if (is_Conv(node) &&
118                         is_downconv(mode, dest_mode) &&
119                         get_irn_mode(get_Conv_op(node)) == dest_mode) {
120                 return -1;
121         }
122
123         if (get_irn_n_edges(node) > 1) {
124                 DB((dbg, LEVEL_3, "multi outs at %+F\n", node));
125                 return 1;
126         }
127
128         if (ir_zero_when_converted(node, dest_mode)) {
129                 return -1;
130         }
131
132 #if 0 // TODO
133         /* Take the minimum of the conversion costs for Phi predecessors as only one
134          * branch is actually executed at a time */
135         if (is_Phi(node)) {
136                 int i;
137                 int arity = get_Phi_n_preds(node);
138                 int costs;
139
140                 costs = get_conv_costs(get_Phi_pred(node, 0), dest_mode);
141                 for (i = 1; i < arity; ++i) {
142                         ir_node *pred = get_Phi_pred(node, i);
143                         int c = get_conv_costs(pred, dest_mode);
144                         if (c < costs) costs = c;
145                 }
146
147                 return costs;
148         }
149 #endif
150
151         if (!is_downconv(mode, dest_mode)) {
152                 return 1;
153         }
154
155         if (is_Conv(node)) {
156                 ir_node *pred      = get_Conv_op(node);
157                 ir_mode *pred_mode = get_irn_mode(pred);
158
159                 if (!values_in_mode(dest_mode, pred_mode)) {
160                         return 1;
161                 }
162                 return get_conv_costs(get_Conv_op(node), dest_mode) - 1;
163         }
164
165         if (!is_optimizable_node(node, dest_mode)) {
166                 return 1;
167         }
168
169         costs = 0;
170         // The shift count does not participate in the conv optimisation
171         arity = is_Shl(node) ? 1 : get_irn_arity(node);
172         for (i = 0; i < arity; ++i) {
173                 ir_node *pred = get_irn_n(node, i);
174                 costs += imin(get_conv_costs(pred, dest_mode), 1);
175         }
176
177         return costs;
178 }
179
180 static ir_node *place_conv(ir_node *node, ir_mode *dest_mode)
181 {
182         ir_node *block = get_nodes_block(node);
183         ir_node *conv = new_r_Conv(block, node, dest_mode);
184         return conv;
185 }
186
187 static ir_node *conv_transform(ir_node *node, ir_mode *dest_mode)
188 {
189         ir_mode  *mode = get_irn_mode(node);
190         ir_graph *irg  = get_irn_irg(node);
191         int       arity;
192         int       conv_arity;
193         int       i;
194         ir_node  *new_node;
195         ir_node **ins;
196
197         if (mode == dest_mode)
198                 return node;
199
200         if (is_Const(node)) {
201                 /* TODO tarval module is incomplete and can't convert floats to ints */
202                 ir_tarval *tv = conv_const_tv(node, dest_mode);
203                 if (tv == tarval_bad) {
204                         return place_conv(node, dest_mode);
205                 } else {
206                         return new_r_Const(irg, tv);
207                 }
208         }
209
210         if (is_Conv(node) &&
211                         is_downconv(mode, dest_mode) &&
212                         get_irn_mode(get_Conv_op(node)) == dest_mode) {
213                 return get_Conv_op(node);
214         }
215
216         if (get_irn_n_edges(node) > 1) {
217                 return place_conv(node, dest_mode);
218         }
219
220         if (!is_downconv(mode, dest_mode)) {
221                 return place_conv(node, dest_mode);
222         }
223
224         if (is_Conv(node)) {
225                 ir_node *pred      = get_Conv_op(node);
226                 ir_mode *pred_mode = get_irn_mode(pred);
227
228                 if (!values_in_mode(dest_mode, pred_mode)) {
229                         return place_conv(node, dest_mode);
230                 }
231                 return conv_transform(get_Conv_op(node), dest_mode);
232         }
233
234         if (!is_optimizable_node(node, dest_mode)) {
235                 return place_conv(node, dest_mode);
236         }
237
238         // We want to create a new node with the right mode
239         arity = get_irn_arity(node);
240         ins = ALLOCAN(ir_node *, arity);
241
242         // The shift count does not participate in the conv optimisation
243         conv_arity = is_Shl(node) ? 1 : arity;
244         for (i = 0; i < conv_arity; i++) {
245                 ir_node *pred = get_irn_n(node, i);
246                 ir_node *transformed;
247                 if (get_conv_costs(pred, dest_mode) > 0) {
248                         transformed = place_conv(pred, dest_mode);
249                 } else {
250                         transformed = conv_transform(pred, dest_mode);
251                 }
252                 ins[i] = transformed;
253         }
254
255         for (i = conv_arity; i < arity; i++) {
256                 ins[i] = get_irn_n(node, i);
257         }
258
259         new_node = new_ir_node(get_irn_dbg_info(node),
260                                 irg,
261                                 get_nodes_block(node),
262                                 get_irn_op(node),
263                                 dest_mode,
264                                 arity,
265                                 ins);
266         copy_node_attr(irg, node, new_node);
267
268         return new_node;
269 }
270
271 static void conv_opt_walker(ir_node *node, void *data)
272 {
273         ir_node *transformed;
274         ir_node *pred;
275         ir_mode *pred_mode;
276         ir_mode *mode;
277         int costs;
278         bool *changed = (bool*)data;
279
280         if (!is_Conv(node))
281                 return;
282
283         pred      = get_Conv_op(node);
284         mode      = get_irn_mode(node);
285         pred_mode = get_irn_mode(pred);
286
287         if (mode_is_reference(mode) || mode_is_reference(pred_mode))
288                 return;
289
290         if (!is_Phi(pred) && !is_downconv(pred_mode, mode))
291                 return;
292
293         /* - 1 for the initial conv */
294         costs = get_conv_costs(pred, mode) - 1;
295         DB((dbg, LEVEL_2, "Costs for %+F -> %+F: %d\n", node, pred, costs));
296         if (costs > 0)
297                 return;
298
299         transformed = conv_transform(pred, mode);
300         if (node != transformed) {
301                 exchange(node, transformed);
302                 *changed = true;
303         }
304 }
305
306 static ir_graph_properties_t do_deconv(ir_graph *irg)
307 {
308         bool changed;
309         FIRM_DBG_REGISTER(dbg, "firm.opt.conv");
310
311         DB((dbg, LEVEL_1, "===> Performing conversion optimization on %+F\n", irg));
312
313         do {
314                 changed = false;
315                 irg_walk_graph(irg, NULL, conv_opt_walker, &changed);
316                 local_optimize_graph(irg);
317         } while (changed);
318
319         return 0;
320 }
321
322 static optdesc_t opt_deconv = {
323         "deconv",
324         IR_GRAPH_PROPERTY_CONSISTENT_OUT_EDGES,
325         do_deconv,
326 };
327
328 int conv_opt(ir_graph *irg)
329 {
330         perform_irg_optimization(irg, &opt_deconv);
331         return 1;
332 }
333
334 /* Creates an ir_graph pass for conv_opt. */
335 ir_graph_pass_t *conv_opt_pass(const char *name)
336 {
337         ir_graph_pass_t *path = def_graph_pass_ret(name ? name : "conv_opt", conv_opt);
338
339         /* safe to run parallel on all irgs */
340         ir_graph_pass_set_parallel(path, 1);
341
342         return path;
343 }