added function to retrieve irn ops
[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 "bitset.h"
13 #include "set.h"
14 #include "array.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
23 #include "beverify.h"
24 #include "belive.h"
25 #include "besched_t.h"
26 #include "benode_t.h"
27
28 static int my_values_interfere(const ir_node *a, const ir_node *b);
29
30 typedef struct be_verify_register_pressure_env_t_ {
31         ir_graph                    *irg;                 /**< the irg to verify */
32          be_lv_t                    *lv;                  /**< Liveness information. */
33         const arch_env_t            *arch_env;            /**< an architecture environment */
34         const arch_register_class_t *cls;                 /**< the register class to check for */
35         int                         registers_available;  /**< number of available registers */
36         int                         problem_found;        /**< flag indicating if a problem was found */
37 } be_verify_register_pressure_env_t;
38
39 /**
40  * Print all nodes of a pset into a file.
41  */
42 static void print_living_values(FILE *F, pset *live_nodes) {
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         be_verify_register_pressure_env_t *env = (be_verify_register_pressure_env_t *)data;
57         pset    *live_nodes = pset_new_ptr_default();
58         ir_node *irn;
59         int pressure;
60
61         /* collect register pressure info, start with end of a block */
62         be_liveness_end_of_block(env->lv, env->arch_env, env->cls, block, live_nodes);
63
64         pressure = pset_count(live_nodes);
65         if(pressure > env->registers_available) {
66                 ir_fprintf(stderr, "Verify Warning: Register pressure too high at end of block %+F(%s) (%d/%d):\n",
67                         block, get_irg_dump_name(env->irg), pressure, env->registers_available);
68                 print_living_values(stderr, live_nodes);
69                 env->problem_found = 1;
70         }
71
72         sched_foreach_reverse(block, irn) {
73                 if (is_Phi(irn))
74                         break;
75
76                 be_liveness_transfer(env->arch_env, env->cls, irn, live_nodes);
77
78                 pressure = pset_count(live_nodes);
79
80                 if(pressure > env->registers_available) {
81                         ir_fprintf(stderr, "Verify Warning: Register pressure too high before node %+F in %+F(%s) (%d/%d):\n",
82                                 irn, block, get_irg_dump_name(env->irg), pressure, env->registers_available);
83                         print_living_values(stderr, live_nodes);
84                         env->problem_found = 1;
85                 }
86         }
87         del_pset(live_nodes);
88 }
89
90 /**
91  * Start a walk over the irg and check the register pressure.
92  */
93 int be_verify_register_pressure(const arch_env_t *arch_env, const arch_register_class_t *cls, ir_graph *irg) {
94         be_verify_register_pressure_env_t env;
95
96         env.lv                  = be_liveness(irg);
97         env.irg                 = irg;
98         env.arch_env            = arch_env;
99         env.cls                 = cls;
100         env.registers_available = arch_count_non_ignore_regs(arch_env, cls);
101         env.problem_found       = 0;
102
103         irg_block_walk_graph(irg, verify_liveness_walker, NULL, &env);
104         be_liveness_free(env.lv);
105
106         return ! env.problem_found;
107 }
108
109 typedef struct be_verify_schedule_env_t_ {
110         int      problem_found;    /**< flags indicating if there was a problem */
111         ir_graph *irg;             /**< the irg to check */
112 } be_verify_schedule_env_t;
113
114 /**
115  * Simple schedule checker.
116  */
117 static void verify_schedule_walker(ir_node *block, void *data) {
118         be_verify_schedule_env_t *env = (be_verify_schedule_env_t*) data;
119         ir_node *node;
120         int non_phi_found  = 0;
121         int cfchange_found = 0;
122         // TODO ask arch about delay branches
123         int delay_branches = 0;
124         pset *uses = pset_new_ptr_default();
125
126         /*
127          * Tests for the following things:
128          *   1. Make sure that all phi nodes are scheduled at the beginning of the block
129          *   2. There is 1 or no control flow changing node scheduled and exactly delay_branches operations after it.
130          *   3. No value is defined after it has been used
131          */
132         sched_foreach(block, node) {
133                 int i, arity;
134
135                 // 1. Check for phis
136                 if (is_Phi(node)) {
137                         if (non_phi_found) {
138                                 ir_fprintf(stderr, "Verify Warning: Phi node %+F scheduled after non-Phi nodes in block %+F (%s)\n",
139                                         node, block, get_irg_dump_name(env->irg));
140                                 env->problem_found = 1;
141                         }
142                 } else {
143                         non_phi_found = 1;
144                 }
145
146                 // 2. Check for control flow changing nodes
147                 if (is_cfop(node) && get_irn_opcode(node) != iro_Start) {
148                         /* check, that only one CF operation is scheduled */
149                         if (cfchange_found == 1) {
150                                 ir_fprintf(stderr, "Verify Warning: More than 1 control flow changing node (%+F) scheduled in block %+F (%s)\n",
151                                         node, block, get_irg_dump_name(env->irg));
152                                 env->problem_found = 1;
153                         }
154                         cfchange_found = 1;
155                 } else if (cfchange_found) {
156                         // proj and keepany aren't real instructions...
157                         if(!is_Proj(node) && !be_is_Keep(node)) {
158                                 /* check for delay branches */
159                                 if (delay_branches == 0) {
160                                         ir_fprintf(stderr, "Verify Warning: Node %+F scheduled after control flow changing node (+delay branches) in block %+F (%s)\n",
161                                                 node, block, get_irg_dump_name(env->irg));
162                                         env->problem_found = 1;
163                                 } else {
164                                         delay_branches--;
165                                 }
166                         }
167                 }
168
169                 // 3. Check for uses
170                 if(pset_find_ptr(uses, node)) {
171                         ir_fprintf(stderr, "Verify Warning: Value %+F used before it was defined in block %+F (%s)\n",
172                                 node, block, get_irg_dump_name(env->irg));
173                         env->problem_found = 1;
174                 }
175                 if(!is_Phi(node)) {
176                         for(i = 0, arity = get_irn_arity(node); i < arity; ++i) {
177                                 pset_insert_ptr(uses, get_irn_n(node, i));
178                         }
179                 }
180         }
181         del_pset(uses);
182
183         /* check that all delay branches are filled (at least with NOPs) */
184         if (cfchange_found && delay_branches != 0) {
185                 ir_fprintf(stderr, "Verify warning: Not all delay slots filled after jump (%d/%d) in block %+F (%s)\n",
186                         block, get_irg_dump_name(env->irg));
187                 env->problem_found = 1;
188         }
189 }
190
191 static int should_be_scheduled(ir_node *node) {
192         if(is_Block(node))
193                 return -1;
194
195         if(get_irn_mode(node) == mode_M) {
196                 if(is_Proj(node))
197                         return -1;
198                 if(is_Phi(node) || is_Sync(node) || get_irn_opcode(node) == iro_Pin)
199                         return 0;
200         }
201         if(is_Proj(node) && get_irn_mode(node) == mode_X)
202                 return 0;
203         if(be_is_Keep(node) && get_irn_opcode(get_nodes_block(node)) == iro_Bad)
204                 return 0;
205
206         switch(get_irn_opcode(node)) {
207         case iro_End:
208         case iro_NoMem:
209         case iro_Bad:
210                 return 0;
211         default:
212                 break;
213         }
214
215         return 1;
216 }
217
218 static void check_schedule(ir_node *node, void *data) {
219         be_verify_schedule_env_t *env = data;
220         int should_be;
221
222         should_be = should_be_scheduled(node);
223         if(should_be == -1)
224                 return;
225
226         if(should_be ? !sched_is_scheduled(node) : sched_is_scheduled(node)) {
227                 ir_fprintf(stderr, "Verify warning: Node %+F in block %+F(%s) should%s be scheduled\n",
228                         node, get_nodes_block(node), get_irg_dump_name(env->irg), should_be ? "" : " not");
229                 env->problem_found = 1;
230         }
231 }
232
233 /**
234  * Start a walk over the irg and check schedule.
235  */
236 int be_verify_schedule(ir_graph *irg)
237 {
238         be_verify_schedule_env_t env;
239
240         env.problem_found = 0;
241         env.irg           = irg;
242
243         irg_block_walk_graph(irg, verify_schedule_walker, NULL, &env);
244         // check if all nodes are scheduled
245         irg_walk_graph(irg, check_schedule, NULL, &env);
246
247         return ! env.problem_found;
248 }
249
250
251
252 //---------------------------------------------------------------------------
253
254
255
256 typedef struct _spill_t {
257         ir_node *spill;
258         entity *ent;
259 } spill_t;
260
261 typedef struct {
262         const arch_env_t *arch_env;
263         ir_graph *irg;
264         set *spills;
265         ir_node **reloads;
266         int problem_found;
267 } be_verify_spillslots_env_t;
268
269 static int cmp_spill(const void* d1, const void* d2, size_t size) {
270         const spill_t* s1 = d1;
271         const spill_t* s2 = d2;
272         return s1->spill != s2->spill;
273 }
274
275 static spill_t *find_spill(be_verify_spillslots_env_t *env, ir_node *node) {
276         spill_t spill;
277
278         spill.spill = node;
279         return set_find(env->spills, &spill, sizeof(spill), HASH_PTR(node));
280 }
281
282 static spill_t *get_spill(be_verify_spillslots_env_t *env, ir_node *node, entity *ent) {
283         spill_t spill, *res;
284         int hash = HASH_PTR(node);
285
286         spill.spill = node;
287         res = set_find(env->spills, &spill, sizeof(spill), hash);
288
289         if(res == NULL) {
290                 spill.ent = ent;
291                 res = set_insert(env->spills, &spill, sizeof(spill), hash);
292         }
293
294         return res;
295 }
296
297 static ir_node *get_memory_edge(const ir_node *node) {
298         int i, arity;
299         ir_node *result = NULL;
300
301         arity = get_irn_arity(node);
302         for(i = arity - 1; i >= 0; --i) {
303                 ir_node *arg = get_irn_n(node, i);
304                 if(get_irn_mode(arg) == mode_M) {
305                         assert(result == NULL);
306                         result = arg;
307                 }
308         }
309
310         return result;
311 }
312
313 static void collect(be_verify_spillslots_env_t *env, ir_node *node, ir_node *reload, entity* ent);
314
315 static void check_entity(be_verify_spillslots_env_t *env, ir_node *node, entity *ent) {
316         if(ent == NULL) {
317                 ir_fprintf(stderr, "Verify warning: Node %+F in block %+F(%s) should have an entity assigned\n",
318                            node, get_nodes_block(node), get_irg_dump_name(env->irg));
319         }
320 }
321
322 static void collect_spill(be_verify_spillslots_env_t *env, ir_node *node, ir_node *reload, entity* ent) {
323         entity *spillent = arch_get_frame_entity(env->arch_env, node);
324         check_entity(env, node, spillent);
325         get_spill(env, node, ent);
326
327         if(spillent != ent) {
328                 ir_fprintf(stderr, "Verify warning: Spill %+F has different entity than reload %+F in block %+F(%s)\n",
329                         node, reload, get_nodes_block(node), get_irg_dump_name(env->irg));
330                 env->problem_found = 1;
331         }
332 }
333
334 static void collect_memperm(be_verify_spillslots_env_t *env, ir_node *node, ir_node *reload, entity* ent) {
335         int i, arity;
336         spill_t spill, *res;
337         int hash = HASH_PTR(node);
338         int out;
339         ir_node* memperm;
340         entity *spillent;
341
342         assert(is_Proj(node));
343
344         memperm = get_Proj_pred(node);
345         out = get_Proj_proj(node);
346
347         spillent = be_get_MemPerm_out_entity(memperm, out);
348         check_entity(env, memperm, spillent);
349         if(spillent != ent) {
350                 ir_fprintf(stderr, "Verify warning: MemPerm %+F has different entity than reload %+F in block %+F(%s)\n",
351                         node, reload, get_nodes_block(node), get_irg_dump_name(env->irg));
352                 env->problem_found = 1;
353         }
354
355         spill.spill = node;
356         res = set_find(env->spills, &spill, sizeof(spill), hash);
357         if(res != NULL) {
358                 return;
359         }
360
361         spill.ent = spillent;
362         res = set_insert(env->spills, &spill, sizeof(spill), hash);
363
364         for(i = 0, arity = be_get_MemPerm_entity_arity(memperm); i < arity; ++i) {
365                 ir_node* arg = get_irn_n(memperm, i + 1);
366                 entity* argent = be_get_MemPerm_in_entity(memperm, i);
367
368                 collect(env, arg, memperm, argent);
369         }
370 }
371
372 static void collect_memphi(be_verify_spillslots_env_t *env, ir_node *node, ir_node *reload, entity *ent) {
373         int i, arity;
374         spill_t spill, *res;
375         int hash = HASH_PTR(node);
376
377         assert(is_Phi(node));
378
379         spill.spill = node;
380         res = set_find(env->spills, &spill, sizeof(spill), hash);
381         if(res != NULL) {
382                 return;
383         }
384
385         spill.ent = ent;
386         res = set_insert(env->spills, &spill, sizeof(spill), hash);
387
388         // is 1 of the arguments a spill?
389         for(i = 0, arity = get_irn_arity(node); i < arity; ++i) {
390                 ir_node* arg = get_irn_n(node, i);
391                 collect(env, arg, reload, ent);
392         }
393 }
394
395 static void collect(be_verify_spillslots_env_t *env, ir_node *node, ir_node *reload, entity* ent) {
396         if(be_is_Spill(node)) {
397                 collect_spill(env, node, reload, ent);
398         } else if(is_Proj(node)) {
399                 collect_memperm(env, node, reload, ent);
400         } else if(is_Phi(node) && get_irn_mode(node) == mode_M) {
401                 collect_memphi(env, node, reload, ent);
402         } else {
403                 ir_fprintf(stderr, "Verify warning: No spill, memperm or memphi attached to node %+F found from node %+F in block %+F(%s)\n",
404                         node, reload, get_nodes_block(node), get_irg_dump_name(env->irg));
405                 env->problem_found = 1;
406         }
407 }
408
409 /**
410  * This walker function searches for reloads and collects all the spills
411  * and memphis attached to them.
412  */
413 static void collect_spills_walker(ir_node *node, void *data) {
414         be_verify_spillslots_env_t *env = data;
415         const arch_env_t *arch_env = env->arch_env;
416
417         // @@@ ia32_classify returns classification of Proj_pred :-/
418         if(is_Proj(node))
419                 return;
420
421         if(arch_irn_class_is(arch_env, node, reload)) {
422                 ir_node *spill = get_memory_edge(node);
423                 entity *ent;
424
425                 if(spill == NULL) {
426                         ir_fprintf(stderr, "Verify warning: No spill attached to reload %+F in block %+F(%s)\n",
427                                    node, get_nodes_block(node), get_irg_dump_name(env->irg));
428                         env->problem_found = 1;
429                         return;
430                 }
431                 ent = arch_get_frame_entity(env->arch_env, node);
432                 check_entity(env, node, ent);
433
434                 collect(env, spill, node, ent);
435                 ARR_APP1(ir_node*, env->reloads, node);
436         }
437 }
438
439 static void check_spillslot_interference(be_verify_spillslots_env_t *env) {
440         int spillcount = set_count(env->spills);
441         spill_t **spills = alloca(spillcount * sizeof(spills[0]));
442         spill_t *spill;
443         int i;
444
445         for(spill = set_first(env->spills), i = 0; spill != NULL; spill = set_next(env->spills), ++i) {
446                 spills[i] = spill;
447         }
448
449         for(i = 0; i < spillcount; ++i) {
450                 spill_t *sp1 = spills[i];
451                 int i2;
452
453                 for(i2 = i+1; i2 < spillcount; ++i2) {
454                         spill_t *sp2 = spills[i2];
455
456                         if(sp1->ent != sp2->ent)
457                                 continue;
458
459                         if(my_values_interfere(sp1->spill, sp2->spill)) {
460                                 ir_fprintf(stderr, "Verify warning: Spillslots for %+F in block %+F(%s) and %+F in block %+F(%s) interfere\n",
461                                         sp1->spill, get_nodes_block(sp1->spill), get_irg_dump_name(env->irg),
462                                         sp2->spill, get_nodes_block(sp2->spill), get_irg_dump_name(env->irg));
463                                 env->problem_found = 1;
464                                 my_values_interfere(sp1->spill, sp2->spill);
465                         }
466                 }
467         }
468 }
469
470 static void check_lonely_spills(ir_node *node, void *data) {
471         be_verify_spillslots_env_t *env = data;
472
473         if(be_is_Spill(node) || (is_Proj(node) && be_is_MemPerm(get_Proj_pred(node)))) {
474                 spill_t *spill = find_spill(env, node);
475                 if(be_is_Spill(node)) {
476                         entity *ent = arch_get_frame_entity(env->arch_env, node);
477                         check_entity(env, node, ent);
478                 }
479
480                 if(spill == NULL) {
481                         ir_fprintf(stderr, "Verify warning: Node %+F in block %+F(%s) not connected to a reaload\n",
482                                    node, get_nodes_block(node), get_irg_dump_name(env->irg));
483                 }
484         }
485 }
486
487 int be_verify_spillslots(const arch_env_t *arch_env, ir_graph *irg)
488 {
489         be_verify_spillslots_env_t env;
490
491         env.arch_env = arch_env;
492         env.irg = irg;
493         env.spills = new_set(cmp_spill, 10);
494         env.reloads = NEW_ARR_F(ir_node*, 0);
495         env.problem_found = 0;
496
497         irg_walk_graph(irg, collect_spills_walker, NULL, &env);
498         irg_walk_graph(irg, check_lonely_spills, NULL, &env);
499
500         check_spillslot_interference(&env);
501
502         DEL_ARR_F(env.reloads);
503         del_set(env.spills);
504
505         return ! env.problem_found;
506 }
507
508
509
510 //---------------------------------------------------------------------------
511
512
513
514 /**
515  * Check, if two values interfere.
516  * @param a The first value.
517  * @param b The second value.
518  * @return 1, if a and b interfere, 0 if not.
519  */
520 static int my_values_interfere(const ir_node *a, const ir_node *b) {
521         const ir_edge_t *edge;
522         ir_node *bb;
523         int a2b = value_dominates(a, b);
524         int b2a = value_dominates(b, a);
525
526         /* If there is no dominance relation, they do not interfere. */
527         if(!a2b && !b2a)
528                 return 0;
529
530         /*
531          * Adjust a and b so, that a dominates b if
532          * a dominates b or vice versa.
533          */
534         if(b2a) {
535                 const ir_node *t = a;
536                 a = b;
537                 b = t;
538         }
539
540         bb = get_nodes_block(b);
541
542         /*
543          * Look at all usages of a.
544          * If there's one usage of a in the block of b, then
545          * we check, if this use is dominated by b, if that's true
546          * a and b interfere. Note that b must strictly dominate the user,
547          * since if b is the last user of in the block, b and a do not
548          * interfere.
549          * Uses of a not in b's block can be disobeyed, because the
550          * check for a being live at the end of b's block is already
551          * performed.
552          */
553         foreach_out_edge(a, edge) {
554                 const ir_node *user = get_edge_src_irn(edge);
555                 if(b == user)
556                         continue;
557
558                 if(get_irn_opcode(user) == iro_End)
559                         continue;
560
561                 // in case of phi arguments we compare with the block the value comes from
562                 if(is_Phi(user)) {
563                         ir_node *phiblock = get_nodes_block(user);
564                         if(phiblock == bb)
565                                 continue;
566                         user = get_irn_n(phiblock, get_edge_src_pos(edge));
567                 }
568
569                 if(value_dominates(b, user))
570                         return 1;
571         }
572
573         return 0;
574 }
575
576
577
578 //---------------------------------------------------------------------------
579
580
581
582 typedef struct _be_verify_register_allocation_env_t {
583         const arch_env_t *arch_env;
584         ir_graph *irg;
585         be_lv_t *lv;
586         int problem_found;
587 } be_verify_register_allocation_env_t;
588
589 static void check_register_allocation(be_verify_register_allocation_env_t *env,
590                                       const arch_register_class_t *regclass, pset *nodes) {
591         const arch_env_t *arch_env = env->arch_env;
592         ir_node *node;
593
594         bitset_t *registers = bitset_alloca(arch_register_class_n_regs(regclass));
595
596         foreach_pset(nodes, node) {
597                 const arch_register_t *reg;
598                 if(arch_get_irn_reg_class(arch_env, node, -1) != regclass)
599                         continue;
600
601                 reg = arch_get_irn_register(arch_env, node);
602                 if(reg == NULL) {
603                         ir_fprintf(stderr, "Verify warning: Node %+F in block %+F(%s) should have a register assigned\n",
604                                    node, get_nodes_block(node), get_irg_dump_name(env->irg));
605                         env->problem_found = 1;
606                         continue;
607                 }
608                 if(bitset_is_set(registers, reg->index)) {
609                         ir_fprintf(stderr, "Verify warning: Register %s assigned more than once at node %+F in block %+F(%s)\n",
610                                    reg->name, node, get_nodes_block(node), get_irg_dump_name(env->irg));
611                         env->problem_found = 1;
612                         continue;
613                 }
614                 bitset_set(registers, reg->index);
615         }
616 }
617
618 static void verify_block_register_allocation(ir_node *block, void *data) {
619         be_verify_register_allocation_env_t *env = data;
620         const arch_env_t *arch_env = env->arch_env;
621         const arch_isa_t *isa = arch_env->isa;
622         int i, nregclasses;
623
624         nregclasses = arch_isa_get_n_reg_class(isa);
625         for (i = 0; i < nregclasses; ++i) {
626                 const arch_register_class_t *regclass = arch_isa_get_reg_class(isa, i);
627                 ir_node *node;
628                 pset *live_nodes = pset_new_ptr_default();
629
630                 be_liveness_end_of_block(env->lv, env->arch_env, regclass, block, live_nodes);
631                 check_register_allocation(env, regclass, live_nodes);
632
633                 sched_foreach_reverse(block, node) {
634                         if (is_Phi(node))
635                                 break;
636
637                         be_liveness_transfer(env->arch_env, regclass, node, live_nodes);
638                         check_register_allocation(env, regclass, live_nodes);
639                 }
640
641                 del_pset(live_nodes);
642         }
643 }
644
645 int be_verify_register_allocation(const arch_env_t *arch_env, ir_graph *irg) {
646         be_verify_register_allocation_env_t env;
647
648         env.arch_env = arch_env;
649         env.irg = irg;
650         env.lv = be_liveness(irg);
651         env.problem_found = 0;
652
653         irg_block_walk_graph(irg, verify_block_register_allocation, NULL, &env);
654
655         be_liveness_free(env.lv);
656
657         return !env.problem_found;
658 }