use custom value_interfere function in verifiers (which is slower but doesn't rely...
[libfirm] / ir / be / beverify.c
1 /*
2  * Author:    Matthias Braun
3  * Date:      05.05.2006
4  * Copyright: (c) Universitaet Karlsruhe
5  * License:   This file protected by GPL -  GNU GENERAL PUBLIC LICENSE.
6  * CVS-Id:    $Id$
7  */
8 #ifdef HAVE_CONFIG_H
9 #include "config.h"
10 #endif
11
12 #include "beverify.h"
13 #include "belive.h"
14 #include "besched_t.h"
15
16 #include "irnode.h"
17 #include "irgraph.h"
18 #include "irgwalk.h"
19 #include "irprintf.h"
20 #include "irdump_t.h"
21 #include "iredges.h"
22 #include "set.h"
23 #include "array.h"
24 #include "benode_t.h"
25
26 static int my_value_dominates(const ir_node *a, const ir_node *b);
27 static int my_values_interfere(const ir_node *a, const ir_node *b);
28
29 typedef struct be_verify_register_pressure_env_t_ {
30         ir_graph                    *irg;                 /**< the irg to verify */
31          be_lv_t                    *lv;                  /**< Liveness information. */
32         const arch_env_t            *arch_env;            /**< an architecture environment */
33         const arch_register_class_t *cls;                 /**< the register class to check for */
34         int                         registers_available;  /**< number of available registers */
35         int                         problem_found;        /**< flag indicating if a problem was found */
36 } be_verify_register_pressure_env_t;
37
38 /**
39  * Print all nodes of a pset into a file.
40  */
41 static void print_living_values(FILE *F, pset *live_nodes)
42 {
43         ir_node *node;
44
45         ir_fprintf(F, "\t");
46         foreach_pset(live_nodes, node) {
47                 ir_fprintf(F, "%+F ", node);
48         }
49         ir_fprintf(F, "\n");
50 }
51
52 /**
53  * Check if number of live nodes never exceeds the number of available registers.
54  */
55 static void verify_liveness_walker(ir_node *block, void *data)
56 {
57         be_verify_register_pressure_env_t *env = (be_verify_register_pressure_env_t *)data;
58         pset    *live_nodes = pset_new_ptr_default();
59         ir_node *irn;
60         int pressure;
61
62         /* collect register pressure info, start with end of a block */
63         be_liveness_end_of_block(env->lv, env->arch_env, env->cls, block, live_nodes);
64
65         pressure = pset_count(live_nodes);
66         if(pressure > env->registers_available) {
67                 ir_fprintf(stderr, "Verify Warning: Register pressure too high at end of block %+F(%s) (%d/%d):\n",
68                         block, get_irg_dump_name(env->irg), pressure, env->registers_available);
69                 print_living_values(stderr, live_nodes);
70                 env->problem_found = 1;
71         }
72
73         sched_foreach_reverse(block, irn) {
74                 if (is_Phi(irn))
75                         break;
76
77                 be_liveness_transfer(env->arch_env, env->cls, irn, live_nodes);
78
79                 pressure = pset_count(live_nodes);
80
81                 if(pressure > env->registers_available) {
82                         ir_fprintf(stderr, "Verify Warning: Register pressure too high before node %+F in %+F(%s) (%d/%d):\n",
83                                 irn, block, get_irg_dump_name(env->irg), pressure, env->registers_available);
84                         print_living_values(stderr, live_nodes);
85                         env->problem_found = 1;
86                 }
87         }
88         del_pset(live_nodes);
89 }
90
91 /**
92  * Start a walk over the irg and check the register pressure.
93  */
94 int be_verify_register_pressure(const arch_env_t *arch_env, const arch_register_class_t *cls, ir_graph *irg)
95 {
96         be_verify_register_pressure_env_t env;
97
98         env.lv                  = be_liveness(irg);
99         env.irg                 = irg;
100         env.arch_env            = arch_env;
101         env.cls                 = cls;
102         env.registers_available = arch_count_non_ignore_regs(arch_env, cls);
103         env.problem_found       = 0;
104
105         irg_block_walk_graph(irg, verify_liveness_walker, NULL, &env);
106         be_liveness_free(env.lv);
107
108         return ! env.problem_found;
109 }
110
111 typedef struct be_verify_schedule_env_t_ {
112         int      problem_found;    /**< flags indicating if there was a problem */
113         ir_graph *irg;             /**< the irg to check */
114 } be_verify_schedule_env_t;
115
116 /**
117  * Simple schedule checker.
118  */
119 static void verify_schedule_walker(ir_node *block, void *data)
120 {
121         be_verify_schedule_env_t *env = (be_verify_schedule_env_t*) data;
122         ir_node *node;
123         int non_phi_found  = 0;
124         int cfchange_found = 0;
125         // TODO ask arch about delay branches
126         int delay_branches = 0;
127         pset *uses = pset_new_ptr_default();
128
129         /*
130          * Tests for the following things:
131          *   1. Make sure that all phi nodes are scheduled at the beginning of the block
132          *   2. There is 1 or no control flow changing node scheduled and exactly delay_branches operations after it.
133          *   3. No value is defined after it has been used
134          */
135         sched_foreach(block, node) {
136                 int i, arity;
137
138                 // 1. Check for phis
139                 if (is_Phi(node)) {
140                         if (non_phi_found) {
141                                 ir_fprintf(stderr, "Verify Warning: Phi node %+F scheduled after non-Phi nodes in block %+F (%s)\n",
142                                         node, block, get_irg_dump_name(env->irg));
143                                 env->problem_found = 1;
144                         }
145                 } else {
146                         non_phi_found = 1;
147                 }
148
149                 // 2. Check for control flow changing nodes
150                 if (is_cfop(node) && get_irn_opcode(node) != iro_Start) {
151                         /* check, that only one CF operation is scheduled */
152                         if (cfchange_found == 1) {
153                                 ir_fprintf(stderr, "Verify Warning: More than 1 control flow changing node (%+F) scheduled in block %+F (%s)\n",
154                                         node, block, get_irg_dump_name(env->irg));
155                                 env->problem_found = 1;
156                         }
157                         cfchange_found = 1;
158                 } else if (cfchange_found) {
159                         // proj and keepany aren't real instructions...
160                         if(!is_Proj(node) && !be_is_Keep(node)) {
161                                 /* check for delay branches */
162                                 if (delay_branches == 0) {
163                                         ir_fprintf(stderr, "Verify Warning: Node %+F scheduled after control flow changing node (+delay branches) in block %+F (%s)\n",
164                                                 node, block, get_irg_dump_name(env->irg));
165                                         env->problem_found = 1;
166                                 } else {
167                                         delay_branches--;
168                                 }
169                         }
170                 }
171
172                 // 3. Check for uses
173                 if(pset_find_ptr(uses, node)) {
174                         ir_fprintf(stderr, "Verify Warning: Value %+F used before it was defined in block %+F (%s)\n",
175                                 node, block, get_irg_dump_name(env->irg));
176                         env->problem_found = 1;
177                 }
178                 if(!is_Phi(node)) {
179                         for(i = 0, arity = get_irn_arity(node); i < arity; ++i) {
180                                 pset_insert_ptr(uses, get_irn_n(node, i));
181                         }
182                 }
183         }
184         del_pset(uses);
185
186         /* check that all delay branches are filled (at least with NOPs) */
187         if (cfchange_found && delay_branches != 0) {
188                 ir_fprintf(stderr, "Not all delay slots filled after jump (%d/%d) in block %+F (%s)\n",
189                         block, get_irg_dump_name(env->irg));
190                 env->problem_found = 1;
191         }
192 }
193
194 /**
195  * Start a walk over the irg and check schedule.
196  */
197 int be_verify_schedule(ir_graph *irg)
198 {
199         be_verify_schedule_env_t env;
200
201         env.problem_found = 0;
202         env.irg           = irg;
203
204         irg_block_walk_graph(irg, verify_schedule_walker, NULL, &env);
205
206         return ! env.problem_found;
207 }
208
209
210
211 //---------------------------------------------------------------------------
212
213
214
215 typedef struct _spill_t {
216         ir_node *spill;
217         entity *ent;
218 } spill_t;
219
220 typedef struct {
221         ir_graph *irg;
222         set *spills;
223         ir_node **reloads;
224         int problem_found;
225 } be_verify_spillslots_env_t;
226
227 static int cmp_spill(const void* d1, const void* d2, size_t size) {
228         const spill_t* s1 = d1;
229         const spill_t* s2 = d2;
230         return s1->spill != s2->spill;
231 }
232
233 static spill_t *get_spill(be_verify_spillslots_env_t *env, ir_node *node, entity *ent) {
234         spill_t spill, *res;
235         int hash = HASH_PTR(node);
236
237         spill.spill = node;
238         res = set_find(env->spills, &spill, sizeof(spill), hash);
239
240         if(res == NULL) {
241                 spill.ent = ent;
242                 res = set_insert(env->spills, &spill, sizeof(spill), hash);
243         }
244
245         return res;
246 }
247
248 static void collect(be_verify_spillslots_env_t *env, ir_node *node, ir_node *reload, entity* ent);
249
250 static void collect_spill(be_verify_spillslots_env_t *env, ir_node *node, ir_node *reload, entity* ent) {
251         entity *spillent = be_get_frame_entity(node);
252         get_spill(env, node, ent);
253
254         if(spillent != ent) {
255                 ir_fprintf(stderr, "Verify warning: Spill %+F has different entity than reload %+F in block %+F(%s)\n",
256                         node, reload, get_nodes_block(node), get_irg_dump_name(env->irg));
257                 env->problem_found = 1;
258         }
259 }
260
261 static void collect_memperm(be_verify_spillslots_env_t *env, ir_node *node, ir_node *reload, entity* ent) {
262         int i, arity;
263         spill_t spill, *res;
264         int hash = HASH_PTR(node);
265         int out;
266         ir_node* memperm;
267         entity *spillent;
268
269         assert(is_Proj(node));
270
271         memperm = get_Proj_pred(node);
272         out = get_Proj_proj(node);
273
274         spillent = be_get_MemPerm_out_entity(memperm, out);
275         if(spillent != ent) {
276                 ir_fprintf(stderr, "Verify warning: MemPerm %+F has different entity than reload %+F in block %+F(%s)\n",
277                         node, reload, get_nodes_block(node), get_irg_dump_name(env->irg));
278                 env->problem_found = 1;
279         }
280
281         spill.spill = node;
282         res = set_find(env->spills, &spill, sizeof(spill), hash);
283         if(res != NULL) {
284                 return;
285         }
286
287         spill.ent = spillent;
288         res = set_insert(env->spills, &spill, sizeof(spill), hash);
289
290         for(i = 0, arity = get_irn_arity(memperm); i < arity; ++i) {
291                 ir_node* arg = get_irn_n(memperm, i);
292                 entity* argent = be_get_MemPerm_in_entity(memperm, i);
293
294                 collect(env, arg, memperm, argent);
295         }
296 }
297
298 static void collect_memphi(be_verify_spillslots_env_t *env, ir_node *node, ir_node *reload, entity *ent) {
299         int i, arity;
300         spill_t spill, *res;
301         int hash = HASH_PTR(node);
302
303         assert(is_Phi(node));
304
305         spill.spill = node;
306         res = set_find(env->spills, &spill, sizeof(spill), hash);
307         if(res != NULL) {
308                 return;
309         }
310
311         spill.ent = ent;
312         res = set_insert(env->spills, &spill, sizeof(spill), hash);
313
314         // is 1 of the arguments a spill?
315         for(i = 0, arity = get_irn_arity(node); i < arity; ++i) {
316                 ir_node* arg = get_irn_n(node, i);
317                 collect(env, arg, reload, ent);
318         }
319 }
320
321 static void collect(be_verify_spillslots_env_t *env, ir_node *node, ir_node *reload, entity* ent) {
322         if(be_is_Spill(node)) {
323                 collect_spill(env, node, reload, ent);
324         } else if(is_Proj(node)) {
325                 collect_memperm(env, node, reload, ent);
326         } else if(is_Phi(node) && get_irn_mode(node) == mode_M) {
327                 collect_memphi(env, node, reload, ent);
328         } else {
329                 ir_fprintf(stderr, "Verify warning: No spill, memperm or memphi attached to node %+F found from node %+F in block %+F(%s)\n",
330                         node, reload, get_nodes_block(node), get_irg_dump_name(env->irg));
331                 env->problem_found = 1;
332         }
333 }
334
335 /**
336  * This walker function searches for reloads and collects all the spills
337  * and memphis attached to them.
338  */
339 static void collect_spills_walker(ir_node *node, void *data) {
340         be_verify_spillslots_env_t *env = data;
341
342         if(be_is_Reload(node)) {
343                 ir_node *spill = get_irn_n(node, be_pos_Reload_mem);
344                 entity* ent = be_get_frame_entity(node);
345
346                 collect(env, spill, node, ent);
347                 ARR_APP1(ir_node*, env->reloads, node);
348         }
349 }
350
351 static void check_spillslot_interference(be_verify_spillslots_env_t *env) {
352         int spillcount = set_count(env->spills);
353         spill_t **spills = alloca(spillcount * sizeof(spills[0]));
354         spill_t *spill;
355         int i;
356
357         for(spill = set_first(env->spills), i = 0; spill != NULL; spill = set_next(env->spills), ++i) {
358                 spills[i] = spill;
359         }
360
361         for(i = 0; i < spillcount; ++i) {
362                 spill_t *sp1 = spills[i];
363                 int i2;
364
365                 for(i2 = i+1; i2 < spillcount; ++i2) {
366                         spill_t *sp2 = spills[i2];
367
368                         if(sp1->ent != sp2->ent)
369                                 continue;
370
371                         if(my_values_interfere(sp1->spill, sp2->spill)) {
372                                 ir_fprintf(stderr, "Verify warning: Spillslots for %+F in block %+F(%s) and %+F in block %+F(%s) interfere\n",
373                                         sp1->spill, get_nodes_block(sp1->spill), get_irg_dump_name(env->irg),
374                                         sp2->spill, get_nodes_block(sp2->spill), get_irg_dump_name(env->irg));
375                                 env->problem_found = 1;
376                         }
377                 }
378         }
379 }
380
381 int be_verify_spillslots(ir_graph *irg)
382 {
383         be_verify_spillslots_env_t env;
384
385         env.irg = irg;
386         env.spills = new_set(cmp_spill, 10);
387         env.reloads = NEW_ARR_F(ir_node*, 0);
388         env.problem_found = 0;
389
390         irg_walk_graph(irg, collect_spills_walker, NULL, &env);
391
392         check_spillslot_interference(&env);
393
394         DEL_ARR_F(env.reloads);
395         del_set(env.spills);
396
397         return ! env.problem_found;
398 }
399
400
401
402 //---------------------------------------------------------------------------
403
404
405
406 static int my_value_dominates(const ir_node *a, const ir_node *b)
407 {
408         int res = 0;
409         const ir_node *ba = get_block(a);
410         const ir_node *bb = get_block(b);
411
412         /*
413          * a and b are not in the same block,
414          * so dominance is determined by the dominance of the blocks.
415          */
416         if(ba != bb) {
417                 res = block_dominates(ba, bb);
418
419         /*
420          * Dominance is determined by the time steps of the schedule.
421          */
422         } else {
423                 sched_timestep_t as = sched_get_time_step(a);
424                 sched_timestep_t bs = sched_get_time_step(b);
425                 res = as <= bs;
426         }
427
428         return res;
429 }
430
431 /**
432  * Check, if two values interfere.
433  * @param a The first value.
434  * @param b The second value.
435  * @return 1, if a and b interfere, 0 if not.
436  */
437 static int my_values_interfere(const ir_node *a, const ir_node *b)
438 {
439         const ir_edge_t *edge;
440         ir_node *bb;
441         int a2b = my_value_dominates(a, b);
442         int b2a = my_value_dominates(b, a);
443
444         /* If there is no dominance relation, they do not interfere. */
445         if(!a2b && !b2a)
446                 return 0;
447
448         /*
449          * Adjust a and b so, that a dominates b if
450          * a dominates b or vice versa.
451          */
452         if(b2a) {
453                 const ir_node *t = a;
454                 a = b;
455                 b = t;
456         }
457
458         bb = get_nodes_block(b);
459
460         /*
461          * Look at all usages of a.
462          * If there's one usage of a in the block of b, then
463          * we check, if this use is dominated by b, if that's true
464          * a and b interfere. Note that b must strictly dominate the user,
465          * since if b is the last user of in the block, b and a do not
466          * interfere.
467          * Uses of a not in b's block can be disobeyed, because the
468          * check for a being live at the end of b's block is already
469          * performed.
470          */
471         foreach_out_edge(a, edge) {
472                 const ir_node *user = get_edge_src_irn(edge);
473                 if(b == user)
474                         continue;
475
476                 // in case of phi arguments we compare with the block the value comes from
477                 if(is_Phi(user)) {
478                         ir_node *phiblock = get_nodes_block(user);
479                         user = get_irn_n(phiblock, get_edge_src_pos(edge));
480                 }
481
482                 if(my_value_dominates(b, user))
483                         return 1;
484         }
485
486         return 0;
487 }