- fix gcc warning
[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_values_interfere(const ir_node *a, const ir_node *b);
27
28 typedef struct be_verify_register_pressure_env_t_ {
29         ir_graph                    *irg;                 /**< the irg to verify */
30          be_lv_t                    *lv;                  /**< Liveness information. */
31         const arch_env_t            *arch_env;            /**< an architecture environment */
32         const arch_register_class_t *cls;                 /**< the register class to check for */
33         int                         registers_available;  /**< number of available registers */
34         int                         problem_found;        /**< flag indicating if a problem was found */
35 } be_verify_register_pressure_env_t;
36
37 /**
38  * Print all nodes of a pset into a file.
39  */
40 static void print_living_values(FILE *F, pset *live_nodes)
41 {
42         ir_node *node;
43
44         ir_fprintf(F, "\t");
45         foreach_pset(live_nodes, node) {
46                 ir_fprintf(F, "%+F ", node);
47         }
48         ir_fprintf(F, "\n");
49 }
50
51 /**
52  * Check if number of live nodes never exceeds the number of available registers.
53  */
54 static void verify_liveness_walker(ir_node *block, void *data)
55 {
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 {
95         be_verify_register_pressure_env_t env;
96
97         env.lv                  = be_liveness(irg);
98         env.irg                 = irg;
99         env.arch_env            = arch_env;
100         env.cls                 = cls;
101         env.registers_available = arch_count_non_ignore_regs(arch_env, cls);
102         env.problem_found       = 0;
103
104         irg_block_walk_graph(irg, verify_liveness_walker, NULL, &env);
105         be_liveness_free(env.lv);
106
107         return ! env.problem_found;
108 }
109
110 typedef struct be_verify_schedule_env_t_ {
111         int      problem_found;    /**< flags indicating if there was a problem */
112         ir_graph *irg;             /**< the irg to check */
113 } be_verify_schedule_env_t;
114
115 /**
116  * Simple schedule checker.
117  */
118 static void verify_schedule_walker(ir_node *block, void *data)
119 {
120         be_verify_schedule_env_t *env = (be_verify_schedule_env_t*) data;
121         ir_node *node;
122         int non_phi_found  = 0;
123         int cfchange_found = 0;
124         // TODO ask arch about delay branches
125         int delay_branches = 0;
126         pset *uses = pset_new_ptr_default();
127
128         /*
129          * Tests for the following things:
130          *   1. Make sure that all phi nodes are scheduled at the beginning of the block
131          *   2. There is 1 or no control flow changing node scheduled and exactly delay_branches operations after it.
132          *   3. No value is defined after it has been used
133          */
134         sched_foreach(block, node) {
135                 int i, arity;
136
137                 // 1. Check for phis
138                 if (is_Phi(node)) {
139                         if (non_phi_found) {
140                                 ir_fprintf(stderr, "Verify Warning: Phi node %+F scheduled after non-Phi nodes in block %+F (%s)\n",
141                                         node, block, get_irg_dump_name(env->irg));
142                                 env->problem_found = 1;
143                         }
144                 } else {
145                         non_phi_found = 1;
146                 }
147
148                 // 2. Check for control flow changing nodes
149                 if (is_cfop(node) && get_irn_opcode(node) != iro_Start) {
150                         /* check, that only one CF operation is scheduled */
151                         if (cfchange_found == 1) {
152                                 ir_fprintf(stderr, "Verify Warning: More than 1 control flow changing node (%+F) scheduled in block %+F (%s)\n",
153                                         node, block, get_irg_dump_name(env->irg));
154                                 env->problem_found = 1;
155                         }
156                         cfchange_found = 1;
157                 } else if (cfchange_found) {
158                         // proj and keepany aren't real instructions...
159                         if(!is_Proj(node) && !be_is_Keep(node)) {
160                                 /* check for delay branches */
161                                 if (delay_branches == 0) {
162                                         ir_fprintf(stderr, "Verify Warning: Node %+F scheduled after control flow changing node (+delay branches) in block %+F (%s)\n",
163                                                 node, block, get_irg_dump_name(env->irg));
164                                         env->problem_found = 1;
165                                 } else {
166                                         delay_branches--;
167                                 }
168                         }
169                 }
170
171                 // 3. Check for uses
172                 if(pset_find_ptr(uses, node)) {
173                         ir_fprintf(stderr, "Verify Warning: Value %+F used before it was defined in block %+F (%s)\n",
174                                 node, block, get_irg_dump_name(env->irg));
175                         env->problem_found = 1;
176                 }
177                 if(!is_Phi(node)) {
178                         for(i = 0, arity = get_irn_arity(node); i < arity; ++i) {
179                                 pset_insert_ptr(uses, get_irn_n(node, i));
180                         }
181                 }
182         }
183         del_pset(uses);
184
185         /* check that all delay branches are filled (at least with NOPs) */
186         if (cfchange_found && delay_branches != 0) {
187                 ir_fprintf(stderr, "Not all delay slots filled after jump (%d/%d) in block %+F (%s)\n",
188                         block, get_irg_dump_name(env->irg));
189                 env->problem_found = 1;
190         }
191 }
192
193 /**
194  * Start a walk over the irg and check schedule.
195  */
196 int be_verify_schedule(ir_graph *irg)
197 {
198         be_verify_schedule_env_t env;
199
200         env.problem_found = 0;
201         env.irg           = irg;
202
203         irg_block_walk_graph(irg, verify_schedule_walker, NULL, &env);
204
205         return ! env.problem_found;
206 }
207
208
209
210 //---------------------------------------------------------------------------
211
212
213
214 typedef struct _spill_t {
215         ir_node *spill;
216         entity *ent;
217 } spill_t;
218
219 typedef struct {
220         be_lv_t *lv;
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                                 my_values_interfere(sp1->spill, sp2->spill);
377                                 printf("Intf: %d\n", values_interfere(env->lv, sp1->spill, sp2->spill));
378                         }
379                 }
380         }
381 }
382
383 int be_verify_spillslots(ir_graph *irg)
384 {
385         be_verify_spillslots_env_t env;
386
387         env.irg = irg;
388         env.spills = new_set(cmp_spill, 10);
389         env.reloads = NEW_ARR_F(ir_node*, 0);
390         env.problem_found = 0;
391         env.lv = be_liveness(irg);
392
393         irg_walk_graph(irg, collect_spills_walker, NULL, &env);
394
395         check_spillslot_interference(&env);
396
397         be_liveness_free(env.lv);
398         DEL_ARR_F(env.reloads);
399         del_set(env.spills);
400
401         return ! env.problem_found;
402 }
403
404
405
406 //---------------------------------------------------------------------------
407
408
409
410 /**
411  * Check, if two values interfere.
412  * @param a The first value.
413  * @param b The second value.
414  * @return 1, if a and b interfere, 0 if not.
415  */
416 static int my_values_interfere(const ir_node *a, const ir_node *b)
417 {
418         const ir_edge_t *edge;
419         ir_node *bb;
420         int a2b = value_dominates(a, b);
421         int b2a = value_dominates(b, a);
422
423         /* If there is no dominance relation, they do not interfere. */
424         if(!a2b && !b2a)
425                 return 0;
426
427         /*
428          * Adjust a and b so, that a dominates b if
429          * a dominates b or vice versa.
430          */
431         if(b2a) {
432                 const ir_node *t = a;
433                 a = b;
434                 b = t;
435         }
436
437         bb = get_nodes_block(b);
438
439         /*
440          * Look at all usages of a.
441          * If there's one usage of a in the block of b, then
442          * we check, if this use is dominated by b, if that's true
443          * a and b interfere. Note that b must strictly dominate the user,
444          * since if b is the last user of in the block, b and a do not
445          * interfere.
446          * Uses of a not in b's block can be disobeyed, because the
447          * check for a being live at the end of b's block is already
448          * performed.
449          */
450         foreach_out_edge(a, edge) {
451                 const ir_node *user = get_edge_src_irn(edge);
452                 if(b == user)
453                         continue;
454
455                 if(get_irn_opcode(user) == iro_End)
456                         continue;
457
458                 // in case of phi arguments we compare with the block the value comes from
459                 if(is_Phi(user)) {
460                         ir_node *phiblock = get_nodes_block(user);
461                         if(phiblock == bb)
462                                 continue;
463                         user = get_irn_n(phiblock, get_edge_src_pos(edge));
464                 }
465
466                 if(value_dominates(b, user))
467                         return 1;
468         }
469
470         return 0;
471 }