6d45d745b055e4a39c1cc41ccd4be1e1553c2944
[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         be_lv_t *lv;
222         ir_graph *irg;
223         set *spills;
224         ir_node **reloads;
225         int problem_found;
226 } be_verify_spillslots_env_t;
227
228 static int cmp_spill(const void* d1, const void* d2, size_t size) {
229         const spill_t* s1 = d1;
230         const spill_t* s2 = d2;
231         return s1->spill != s2->spill;
232 }
233
234 static spill_t *get_spill(be_verify_spillslots_env_t *env, ir_node *node, entity *ent) {
235         spill_t spill, *res;
236         int hash = HASH_PTR(node);
237
238         spill.spill = node;
239         res = set_find(env->spills, &spill, sizeof(spill), hash);
240
241         if(res == NULL) {
242                 spill.ent = ent;
243                 res = set_insert(env->spills, &spill, sizeof(spill), hash);
244         }
245
246         return res;
247 }
248
249 static void collect(be_verify_spillslots_env_t *env, ir_node *node, ir_node *reload, entity* ent);
250
251 static void collect_spill(be_verify_spillslots_env_t *env, ir_node *node, ir_node *reload, entity* ent) {
252         entity *spillent = be_get_frame_entity(node);
253         get_spill(env, node, ent);
254
255         if(spillent != ent) {
256                 ir_fprintf(stderr, "Verify warning: Spill %+F has different entity than reload %+F in block %+F(%s)\n",
257                         node, reload, get_nodes_block(node), get_irg_dump_name(env->irg));
258                 env->problem_found = 1;
259         }
260 }
261
262 static void collect_memperm(be_verify_spillslots_env_t *env, ir_node *node, ir_node *reload, entity* ent) {
263         int i, arity;
264         spill_t spill, *res;
265         int hash = HASH_PTR(node);
266         int out;
267         ir_node* memperm;
268         entity *spillent;
269
270         assert(is_Proj(node));
271
272         memperm = get_Proj_pred(node);
273         out = get_Proj_proj(node);
274
275         spillent = be_get_MemPerm_out_entity(memperm, out);
276         if(spillent != ent) {
277                 ir_fprintf(stderr, "Verify warning: MemPerm %+F has different entity than reload %+F in block %+F(%s)\n",
278                         node, reload, get_nodes_block(node), get_irg_dump_name(env->irg));
279                 env->problem_found = 1;
280         }
281
282         spill.spill = node;
283         res = set_find(env->spills, &spill, sizeof(spill), hash);
284         if(res != NULL) {
285                 return;
286         }
287
288         spill.ent = spillent;
289         res = set_insert(env->spills, &spill, sizeof(spill), hash);
290
291         for(i = 0, arity = get_irn_arity(memperm); i < arity; ++i) {
292                 ir_node* arg = get_irn_n(memperm, i);
293                 entity* argent = be_get_MemPerm_in_entity(memperm, i);
294
295                 collect(env, arg, memperm, argent);
296         }
297 }
298
299 static void collect_memphi(be_verify_spillslots_env_t *env, ir_node *node, ir_node *reload, entity *ent) {
300         int i, arity;
301         spill_t spill, *res;
302         int hash = HASH_PTR(node);
303
304         assert(is_Phi(node));
305
306         spill.spill = node;
307         res = set_find(env->spills, &spill, sizeof(spill), hash);
308         if(res != NULL) {
309                 return;
310         }
311
312         spill.ent = ent;
313         res = set_insert(env->spills, &spill, sizeof(spill), hash);
314
315         // is 1 of the arguments a spill?
316         for(i = 0, arity = get_irn_arity(node); i < arity; ++i) {
317                 ir_node* arg = get_irn_n(node, i);
318                 collect(env, arg, reload, ent);
319         }
320 }
321
322 static void collect(be_verify_spillslots_env_t *env, ir_node *node, ir_node *reload, entity* ent) {
323         if(be_is_Spill(node)) {
324                 collect_spill(env, node, reload, ent);
325         } else if(is_Proj(node)) {
326                 collect_memperm(env, node, reload, ent);
327         } else if(is_Phi(node) && get_irn_mode(node) == mode_M) {
328                 collect_memphi(env, node, reload, ent);
329         } else {
330                 ir_fprintf(stderr, "Verify warning: No spill, memperm or memphi attached to node %+F found from node %+F in block %+F(%s)\n",
331                         node, reload, get_nodes_block(node), get_irg_dump_name(env->irg));
332                 env->problem_found = 1;
333         }
334 }
335
336 /**
337  * This walker function searches for reloads and collects all the spills
338  * and memphis attached to them.
339  */
340 static void collect_spills_walker(ir_node *node, void *data) {
341         be_verify_spillslots_env_t *env = data;
342
343         if(be_is_Reload(node)) {
344                 ir_node *spill = get_irn_n(node, be_pos_Reload_mem);
345                 entity* ent = be_get_frame_entity(node);
346
347                 collect(env, spill, node, ent);
348                 ARR_APP1(ir_node*, env->reloads, node);
349         }
350 }
351
352 static void check_spillslot_interference(be_verify_spillslots_env_t *env) {
353         int spillcount = set_count(env->spills);
354         spill_t **spills = alloca(spillcount * sizeof(spills[0]));
355         spill_t *spill;
356         int i;
357
358         for(spill = set_first(env->spills), i = 0; spill != NULL; spill = set_next(env->spills), ++i) {
359                 spills[i] = spill;
360         }
361
362         for(i = 0; i < spillcount; ++i) {
363                 spill_t *sp1 = spills[i];
364                 int i2;
365
366                 for(i2 = i+1; i2 < spillcount; ++i2) {
367                         spill_t *sp2 = spills[i2];
368
369                         if(sp1->ent != sp2->ent)
370                                 continue;
371
372                         if(my_values_interfere(sp1->spill, sp2->spill)) {
373                                 ir_fprintf(stderr, "Verify warning: Spillslots for %+F in block %+F(%s) and %+F in block %+F(%s) interfere\n",
374                                         sp1->spill, get_nodes_block(sp1->spill), get_irg_dump_name(env->irg),
375                                         sp2->spill, get_nodes_block(sp2->spill), get_irg_dump_name(env->irg));
376                                 env->problem_found = 1;
377                                 my_values_interfere(sp1->spill, sp2->spill);
378                                 printf("Intf: %d\n", values_interfere(env->lv, sp1->spill, sp2->spill));
379                         }
380                 }
381         }
382 }
383
384 int be_verify_spillslots(ir_graph *irg)
385 {
386         be_verify_spillslots_env_t env;
387
388         env.irg = irg;
389         env.spills = new_set(cmp_spill, 10);
390         env.reloads = NEW_ARR_F(ir_node*, 0);
391         env.problem_found = 0;
392         env.lv = be_liveness(irg);
393
394         irg_walk_graph(irg, collect_spills_walker, NULL, &env);
395
396         check_spillslot_interference(&env);
397
398         be_liveness_free(env.lv);
399         DEL_ARR_F(env.reloads);
400         del_set(env.spills);
401
402         return ! env.problem_found;
403 }
404
405
406
407 //---------------------------------------------------------------------------
408
409
410
411 /**
412  * Check, if two values interfere.
413  * @param a The first value.
414  * @param b The second value.
415  * @return 1, if a and b interfere, 0 if not.
416  */
417 static int my_values_interfere(const ir_node *a, const ir_node *b)
418 {
419         const ir_edge_t *edge;
420         ir_node *bb;
421         int a2b = value_dominates(a, b);
422         int b2a = value_dominates(b, a);
423
424         /* If there is no dominance relation, they do not interfere. */
425         if(!a2b && !b2a)
426                 return 0;
427
428         /*
429          * Adjust a and b so, that a dominates b if
430          * a dominates b or vice versa.
431          */
432         if(b2a) {
433                 const ir_node *t = a;
434                 a = b;
435                 b = t;
436         }
437
438         bb = get_nodes_block(b);
439
440         /*
441          * Look at all usages of a.
442          * If there's one usage of a in the block of b, then
443          * we check, if this use is dominated by b, if that's true
444          * a and b interfere. Note that b must strictly dominate the user,
445          * since if b is the last user of in the block, b and a do not
446          * interfere.
447          * Uses of a not in b's block can be disobeyed, because the
448          * check for a being live at the end of b's block is already
449          * performed.
450          */
451         foreach_out_edge(a, edge) {
452                 const ir_node *user = get_edge_src_irn(edge);
453                 if(b == user)
454                         continue;
455
456                 if(get_irn_opcode(user) == iro_End)
457                         continue;
458
459                 // in case of phi arguments we compare with the block the value comes from
460                 if(is_Phi(user)) {
461                         ir_node *phiblock = get_nodes_block(user);
462                         if(phiblock == bb)
463                                 continue;
464                         user = get_irn_n(phiblock, get_edge_src_pos(edge));
465                 }
466
467                 if(value_dominates(b, user))
468                         return 1;
469         }
470
471         return 0;
472 }