verify: Clarify assertion message.
[libfirm] / ir / opt / tailrec.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   Tail-recursion call optimization.
23  * @date    08.06.2004
24  * @author  Michael Beck
25  */
26 #include "config.h"
27
28 #include <string.h>
29 #include <assert.h>
30
31 #include "debug.h"
32 #include "iroptimize.h"
33 #include "scalar_replace.h"
34 #include "array_t.h"
35 #include "irprog_t.h"
36 #include "irgwalk.h"
37 #include "irgmod.h"
38 #include "irop.h"
39 #include "irnode_t.h"
40 #include "irgraph_t.h"
41 #include "ircons.h"
42 #include "irflag.h"
43 #include "trouts.h"
44 #include "irouts.h"
45 #include "irhooks.h"
46 #include "ircons_t.h"
47 #include "irpass.h"
48 #include "util.h"
49
50 DEBUG_ONLY(static firm_dbg_module_t *dbg;)
51
52 /**
53  * the environment for collecting data
54  */
55 typedef struct collect_t {
56         ir_node *proj_X;      /**< initial exec proj */
57         ir_node *block;       /**< old first block */
58         int     blk_idx;      /**< cfgpred index of the initial exec in block */
59         ir_node *proj_m;      /**< memory from start proj's */
60         ir_node *proj_data;   /**< linked list of all parameter access proj's */
61 } collect_t;
62
63 /**
64  * walker for collecting data, fills a collect_t environment
65  */
66 static void collect_data(ir_node *node, void *env)
67 {
68         collect_t *data = (collect_t*)env;
69         ir_node *pred;
70         ir_opcode opcode;
71
72         switch (get_irn_opcode(node)) {
73         case iro_Proj:
74                 pred = get_Proj_pred(node);
75
76                 opcode = (ir_opcode)get_irn_opcode(pred);
77                 if (opcode == iro_Proj) {
78                         ir_node *start = get_Proj_pred(pred);
79
80                         if (is_Start(start)) {
81                                 if (get_Proj_proj(pred) == pn_Start_T_args) {
82                                         /* found Proj(ProjT(Start)) */
83                                         set_irn_link(node, data->proj_data);
84                                         data->proj_data = node;
85                                 }
86                         }
87                 } else if (opcode == iro_Start) {
88                         if (get_Proj_proj(node) == pn_Start_X_initial_exec) {
89                                 /* found ProjX(Start) */
90                                 data->proj_X = node;
91                         }
92                 }
93                 break;
94         case iro_Block: {
95                 int i, n_pred = get_Block_n_cfgpreds(node);
96                 for (i = 0; i < n_pred; ++i) {
97                         if (get_Block_cfgpred(node, i) == data->proj_X) {
98                                 data->block   = node;
99                                 data->blk_idx = i;
100                                 break;
101                         }
102                 }
103                 break;
104         }
105         default:
106                 break;
107         }
108 }
109
110 typedef enum tail_rec_variants {
111         TR_DIRECT,  /**< direct return value, i.e. return func(). */
112         TR_ADD,     /**< additive return value, i.e. return x +/- func() */
113         TR_MUL,     /**< multiplicative return value, i.e. return x * func() or return -func() */
114         TR_BAD,     /**< any other transformation */
115         TR_UNKNOWN  /**< during construction */
116 } tail_rec_variants;
117
118 typedef struct tr_env {
119         int               n_tail_calls;  /**< number of tail calls found */
120         int               n_ress;        /**< number of return values */
121         tail_rec_variants *variants;     /**< return value variants */
122         ir_node           *rets;         /**< list of returns that can be transformed */
123 } tr_env;
124
125
126 /**
127  * do the graph reconstruction for tail-recursion elimination
128  *
129  * @param irg  the graph that will reconstructed
130  * @param env  tail recursion environment
131  */
132 static void do_opt_tail_rec(ir_graph *irg, tr_env *env)
133 {
134         ir_node *end_block = get_irg_end_block(irg);
135         ir_node *block, *jmp, *call, *calls;
136         ir_node **in;
137         ir_node **phis;
138         ir_node ***call_params;
139         ir_node *p, *n;
140         int i, j, n_params, n_locs;
141         collect_t data;
142         int rem            = get_optimize();
143         ir_entity *ent     = get_irg_entity(irg);
144         ir_type *method_tp = get_entity_type(ent);
145
146         assert(env->n_tail_calls > 0);
147
148         /* we add new blocks and change the control flow */
149         clear_irg_properties(irg, IR_GRAPH_PROPERTY_CONSISTENT_DOMINANCE);
150
151         /* we must build some new nodes WITHOUT CSE */
152         set_optimize(0);
153
154         /* collect needed data */
155         data.proj_X    = NULL;
156         data.block     = NULL;
157         data.blk_idx   = -1;
158         data.proj_m    = get_irg_initial_mem(irg);
159         data.proj_data = NULL;
160         irg_walk_graph(irg, NULL, collect_data, &data);
161
162         /* check number of arguments */
163         call     = (ir_node*)get_irn_link(end_block);
164         n_params = get_Call_n_params(call);
165
166         assert(data.proj_X && "Could not find initial exec from Start");
167         assert(data.block  && "Could not find first block");
168         assert(data.proj_m && "Could not find initial memory");
169         assert((data.proj_data || n_params == 0) && "Could not find Proj(ProjT(Start)) of non-void function");
170
171         /* allocate in's for phi and block construction */
172         NEW_ARR_A(ir_node *, in, env->n_tail_calls + 1);
173
174         /* build a new header block for the loop we create */
175         i = 0;
176         in[i++] = data.proj_X;
177
178         /* turn Return's into Jmp's */
179         for (p = env->rets; p; p = n) {
180                 ir_node *block = get_nodes_block(p);
181
182                 n = (ir_node*)get_irn_link(p);
183                 in[i++] = new_r_Jmp(block);
184
185                 // exchange(p, new_r_Bad(irg));
186
187                 /* we might generate an endless loop, so add
188                  * the block to the keep-alive list */
189                 add_End_keepalive(get_irg_end(irg), block);
190         }
191         assert(i == env->n_tail_calls + 1);
192
193         /* now create it */
194         block = new_r_Block(irg, i, in);
195         jmp   = new_r_Jmp(block);
196
197         /* the old first block is now the second one */
198         set_Block_cfgpred(data.block, data.blk_idx, jmp);
199
200         /* allocate phi's, position 0 contains the memory phi */
201         NEW_ARR_A(ir_node *, phis, n_params + 1);
202
203         /* build the memory phi */
204         i = 0;
205         in[i] = new_r_Proj(get_irg_start(irg), mode_M, pn_Start_M);
206         set_irg_initial_mem(irg, in[i]);
207         ++i;
208
209         for (calls = call; calls != NULL; calls = (ir_node*)get_irn_link(calls)) {
210                 in[i] = get_Call_mem(calls);
211                 ++i;
212         }
213         assert(i == env->n_tail_calls + 1);
214
215         phis[0] = new_r_Phi(block, env->n_tail_calls + 1, in, mode_M);
216
217         /* build the data Phi's */
218         if (n_params > 0) {
219                 ir_node *calls;
220                 ir_node *args;
221
222                 NEW_ARR_A(ir_node **, call_params, env->n_tail_calls);
223
224                 /* collect all parameters */
225                 for (i = 0, calls = call; calls != NULL;
226                      calls = (ir_node*)get_irn_link(calls)) {
227                         call_params[i] = get_Call_param_arr(calls);
228                         ++i;
229                 }
230
231                 /* build new Proj's and Phi's */
232                 args    = get_irg_args(irg);
233                 for (i = 0; i < n_params; ++i) {
234                         ir_mode *mode = get_type_mode(get_method_param_type(method_tp, i));
235
236                         in[0] = new_r_Proj(args, mode, i);
237                         for (j = 0; j < env->n_tail_calls; ++j)
238                                 in[j + 1] = call_params[j][i];
239
240                         phis[i + 1] = new_r_Phi(block, env->n_tail_calls + 1, in, mode);
241                 }
242         }
243
244         /*
245          * ok, we are here, so we have build and collected all needed Phi's
246          * now exchange all Projs into links to Phi
247          */
248         exchange(data.proj_m, phis[0]);
249         for (p = data.proj_data; p; p = n) {
250                 long proj = get_Proj_proj(p);
251
252                 assert(0 <= proj && proj < n_params);
253                 n = (ir_node*)get_irn_link(p);
254                 exchange(p, phis[proj + 1]);
255         }
256
257         /* tail recursion was done, all info is invalid */
258         clear_irg_properties(irg, IR_GRAPH_PROPERTY_CONSISTENT_DOMINANCE
259                            | IR_GRAPH_PROPERTY_CONSISTENT_LOOPINFO);
260         set_irg_callee_info_state(irg, irg_callee_info_inconsistent);
261
262         set_optimize(rem);
263
264         /* check if we need new values */
265         n_locs = 0;
266         for (i = 0; i < env->n_ress; ++i) {
267                 if (env->variants[i] != TR_DIRECT) {
268                         ++n_locs;
269                         break;
270                 }
271         }
272
273         if (n_locs > 0) {
274                 ir_node *start_block;
275                 ir_node **in;
276                 ir_mode **modes;
277
278                 NEW_ARR_A(ir_node *, in, env->n_ress);
279                 NEW_ARR_A(ir_mode *, modes, env->n_ress);
280                 ssa_cons_start(irg, env->n_ress);
281
282                 start_block = get_irg_start_block(irg);
283                 set_r_cur_block(irg, start_block);
284
285                 /* set the neutral elements for the iteration start */
286                 for (i = 0; i < env->n_ress; ++i) {
287                         ir_type *tp = get_method_res_type(method_tp, i);
288                         ir_mode *mode = get_type_mode(tp);
289
290                         modes[i] = mode;
291                         if (env->variants[i] == TR_ADD) {
292                                 set_r_value(irg, i, new_r_Const(irg, get_mode_null(mode)));
293                         } else if (env->variants[i] == TR_MUL) {
294                                 set_r_value(irg, i, new_r_Const(irg, get_mode_one(mode)));
295                         }
296                 }
297                 mature_immBlock(start_block);
298
299                 /* no: we can kill all returns */
300                 for (p = env->rets; p; p = n) {
301                         ir_node *block = get_nodes_block(p);
302                         ir_node *jmp, *tuple;
303
304                         set_r_cur_block(irg, block);
305                         n = (ir_node*)get_irn_link(p);
306
307                         ir_node *const call = skip_Proj(get_Return_mem(p));
308                         ir_node *const mem  = get_Call_mem(call);
309
310                         /* create a new jump, free of CSE */
311                         set_optimize(0);
312                         jmp = new_r_Jmp(block);
313                         set_optimize(rem);
314
315                         for (i = 0; i < env->n_ress; ++i) {
316                                 ir_mode *mode = modes[i];
317                                 if (env->variants[i] != TR_DIRECT) {
318                                         in[i] = get_r_value(irg, i, mode);
319                                 } else {
320                                         in[i] = new_r_Bad(irg, mode);
321                                 }
322                         }
323                         /* create a new tuple for the return values */
324                         tuple = new_r_Tuple(block, env->n_ress, in);
325
326                         ir_node *const in[] = {
327                                 [pn_Call_M]         = mem,
328                                 [pn_Call_T_result]  = tuple,
329                                 [pn_Call_X_regular] = jmp,
330                                 [pn_Call_X_except]  = new_r_Bad(irg, mode_X),
331                         };
332                         turn_into_tuple(call, ARRAY_SIZE(in), in);
333
334                         for (i = 0; i < env->n_ress; ++i) {
335                                 ir_node *res = get_Return_res(p, i);
336                                 if (env->variants[i] != TR_DIRECT) {
337                                         set_r_value(irg, i, res);
338                                 }
339                         }
340
341                         exchange(p, new_r_Bad(irg, mode_X));
342                 }
343
344                 /* finally fix all other returns */
345                 end_block = get_irg_end_block(irg);
346                 for (i = get_Block_n_cfgpreds(end_block) - 1; i >= 0; --i) {
347                         ir_node *ret = get_Block_cfgpred(end_block, i);
348                         ir_node *block;
349
350                         /* search all Returns of a block */
351                         if (! is_Return(ret))
352                                 continue;
353
354                         block = get_nodes_block(ret);
355                         set_r_cur_block(irg, block);
356                         for (j = 0; j < env->n_ress; ++j) {
357                                 ir_node *pred = get_Return_res(ret, j);
358                                 ir_node *n;
359
360                                 switch (env->variants[j]) {
361                                 case TR_DIRECT:
362                                         continue;
363
364                                 case TR_ADD:
365                                         n = get_r_value(irg, j, modes[j]);
366                                         n = new_r_Add(block, n, pred, modes[j]);
367                                         set_Return_res(ret, j, n);
368                                         break;
369
370                                 case TR_MUL:
371                                         n = get_r_value(irg, j, modes[j]);
372                                         n = new_r_Mul(block, n, pred, modes[j]);
373                                         set_Return_res(ret, j, n);
374                                         break;
375
376                                 default:
377                                         assert(!"unexpected tail recursion variant");
378                                 }
379                         }
380                 }
381                 ssa_cons_finish(irg);
382         } else {
383                 ir_node *bad = new_r_Bad(irg, mode_X);
384
385                 /* no: we can kill all returns */
386                 for (p = env->rets; p; p = n) {
387                         n = (ir_node*)get_irn_link(p);
388                         exchange(p, bad);
389                 }
390         }
391 }
392
393 /**
394  * Check the lifetime of locals in the given graph.
395  * Tail recursion can only be done, if we can prove that
396  * the lifetime of locals end with the recursive call.
397  * We do this by checking that no address of a local variable is
398  * stored or transmitted as an argument to a call.
399  *
400  * @return non-zero if it's ok to do tail recursion
401  */
402 static int check_lifetime_of_locals(ir_graph *irg)
403 {
404         ir_node *irg_frame;
405         int i;
406         ir_type *frame_tp = get_irg_frame_type(irg);
407
408         irg_frame = get_irg_frame(irg);
409         for (i = get_irn_n_outs(irg_frame) - 1; i >= 0; --i) {
410                 ir_node *succ = get_irn_out(irg_frame, i);
411
412                 if (is_Sel(succ)) {
413                         /* Check if we have compound arguments.
414                            For now, we cannot handle them, */
415                         if (get_entity_owner(get_Sel_entity(succ)) != frame_tp)
416                                 return 0;
417
418                         if (is_address_taken(succ))
419                                 return 0;
420                 }
421         }
422         return 1;
423 }
424
425 /**
426  * Examine irn and detect the recursion variant.
427  */
428 static tail_rec_variants find_variant(ir_node *irn, ir_node *call)
429 {
430         ir_node           *a, *b;
431         tail_rec_variants va, vb, res;
432
433         if (skip_Proj(skip_Proj(irn)) == call) {
434                 /* found it */
435                 return TR_DIRECT;
436         }
437         switch (get_irn_opcode(irn)) {
438         case iro_Add:
439                 /* try additive */
440                 a = get_Add_left(irn);
441                 if (get_nodes_block(a) != get_nodes_block(call)) {
442                         /* we are outside, ignore */
443                         va = TR_UNKNOWN;
444                 } else {
445                         va = find_variant(a, call);
446                         if (va == TR_BAD)
447                                 return TR_BAD;
448                 }
449                 b = get_Add_right(irn);
450                 if (get_nodes_block(b) != get_nodes_block(call)) {
451                         /* we are outside, ignore */
452                         vb = TR_UNKNOWN;
453                 } else {
454                         vb = find_variant(b, call);
455                         if (vb == TR_BAD)
456                                 return TR_BAD;
457                 }
458                 if (va == vb) {
459                         res = va;
460                 }
461                 else if (va == TR_UNKNOWN)
462                         res = vb;
463                 else if (vb == TR_UNKNOWN)
464                         res = va;
465                 else {
466                         /* they are different but none is TR_UNKNOWN -> incompatible */
467                         return TR_BAD;
468                 }
469                 if (res == TR_DIRECT || res == TR_ADD)
470                         return TR_ADD;
471                 /* not compatible */
472                 return TR_BAD;
473
474         case iro_Sub:
475                 /* try additive, but return value must be left */
476                 a = get_Sub_left(irn);
477                 if (get_nodes_block(a) != get_nodes_block(call)) {
478                         /* we are outside, ignore */
479                         va = TR_UNKNOWN;
480                 } else {
481                         va = find_variant(a, call);
482                         if (va == TR_BAD)
483                                 return TR_BAD;
484                 }
485                 b = get_Sub_right(irn);
486                 if (get_nodes_block(b) != get_nodes_block(call)) {
487                         /* we are outside, ignore */
488                         vb = TR_UNKNOWN;
489                 } else {
490                         vb = find_variant(b, call);
491                         if (vb != TR_UNKNOWN)
492                                 return TR_BAD;
493                 }
494                 res = va;
495                 if (res == TR_DIRECT || res == TR_ADD)
496                         return res;
497                 /* not compatible */
498                 return TR_BAD;
499
500         case iro_Mul:
501                 /* try multiplicative */
502                 a = get_Mul_left(irn);
503                 if (get_nodes_block(a) != get_nodes_block(call)) {
504                         /* we are outside, ignore */
505                         va = TR_UNKNOWN;
506                 } else {
507                         va = find_variant(a, call);
508                         if (va == TR_BAD)
509                                 return TR_BAD;
510                 }
511                 b = get_Mul_right(irn);
512                 if (get_nodes_block(b) != get_nodes_block(call)) {
513                         /* we are outside, ignore */
514                         vb = TR_UNKNOWN;
515                 } else {
516                         vb = find_variant(b, call);
517                         if (vb == TR_BAD)
518                                 return TR_BAD;
519                 }
520                 if (va == vb) {
521                         res = va;
522                 }
523                 else if (va == TR_UNKNOWN)
524                         res = vb;
525                 else if (vb == TR_UNKNOWN)
526                         res = va;
527                 else {
528                         /* they are different but none is TR_UNKNOWN -> incompatible */
529                         return TR_BAD;
530                 }
531                 if (res == TR_DIRECT || res == TR_MUL)
532                         return TR_MUL;
533                 /* not compatible */
534                 return TR_BAD;
535
536         case iro_Minus:
537                 /* try multiplicative */
538                 a = get_Minus_op(irn);
539                 res =  find_variant(a, call);
540                 if (res == TR_DIRECT)
541                         return TR_MUL;
542                 if (res == TR_MUL || res == TR_UNKNOWN)
543                         return res;
544                 /* not compatible */
545                 return TR_BAD;
546
547         default:
548                 return TR_UNKNOWN;
549         }
550 }
551
552
553 /*
554  * convert simple tail-calls into loops
555  */
556 void opt_tail_rec_irg(ir_graph *irg)
557 {
558         tr_env    env;
559         ir_node   *end_block;
560         int       i, n_ress, n_tail_calls = 0;
561         ir_node   *rets = NULL;
562         ir_type   *mtd_type, *call_type;
563         ir_entity *ent;
564         ir_graph  *rem;
565
566         assure_irg_properties(irg,
567                 IR_GRAPH_PROPERTY_MANY_RETURNS
568                 | IR_GRAPH_PROPERTY_NO_BADS
569                 | IR_GRAPH_PROPERTY_CONSISTENT_OUTS);
570
571         FIRM_DBG_REGISTER(dbg, "firm.opt.tailrec");
572
573         if (! check_lifetime_of_locals(irg)) {
574                 confirm_irg_properties(irg, IR_GRAPH_PROPERTIES_ALL);
575                 return;
576         }
577
578         rem = current_ir_graph;
579         current_ir_graph = irg;
580
581         ent      = get_irg_entity(irg);
582         mtd_type = get_entity_type(ent);
583         n_ress   = get_method_n_ress(mtd_type);
584
585         env.variants = NULL;
586         env.n_ress   = n_ress;
587
588         if (n_ress > 0) {
589                 NEW_ARR_A(tail_rec_variants, env.variants, n_ress);
590
591                 for (i = 0; i < n_ress; ++i)
592                         env.variants[i] = TR_DIRECT;
593         }
594
595         ir_reserve_resources(irg, IR_RESOURCE_IRN_LINK);
596
597         end_block = get_irg_end_block(irg);
598         set_irn_link(end_block, NULL);
599
600         for (i = get_Block_n_cfgpreds(end_block) - 1; i >= 0; --i) {
601                 ir_node *ret = get_Block_cfgpred(end_block, i);
602                 ir_node *call, *call_ptr;
603                 int j;
604                 ir_node **ress;
605
606                 /* search all Returns of a block */
607                 if (! is_Return(ret))
608                         continue;
609
610                 /* check, if it's a Return self() */
611                 call = skip_Proj(get_Return_mem(ret));
612                 if (! is_Call(call))
613                         continue;
614
615                 /* the call must be in the same block as the return */
616                 if (get_nodes_block(call) != get_nodes_block(ret))
617                         continue;
618
619                 /* check if it's a recursive call */
620                 call_ptr = get_Call_ptr(call);
621
622                 if (! is_SymConst_addr_ent(call_ptr))
623                         continue;
624
625                 ent = get_SymConst_entity(call_ptr);
626                 if (!ent || get_entity_irg(ent) != irg)
627                         continue;
628
629                 /*
630                  * Check, that the types match. At least in C
631                  * this might fail.
632                  */
633                 mtd_type  = get_entity_type(ent);
634                 call_type = get_Call_type(call);
635
636                 if (mtd_type != call_type) {
637                         /*
638                          * Hmm, the types did not match, bad.
639                          * This can happen in C when no prototype is given
640                          * or K&R style is used.
641                          */
642                         DB((dbg, LEVEL_3, "  tail recursion fails because of call type mismatch: %+F != %+F\n", mtd_type, call_type));
643                         continue;
644                 }
645
646                 /* ok, mem is routed to a recursive call, check return args */
647                 ress = get_Return_res_arr(ret);
648                 for (j = get_Return_n_ress(ret) - 1; j >= 0; --j) {
649                         tail_rec_variants var = find_variant(ress[j], call);
650
651                         if (var >= TR_BAD) {
652                                 /* cannot be transformed */
653                                 break;
654                         }
655                         if (var == TR_DIRECT) {
656                                 var = env.variants[j];
657                         } else if (env.variants[j] == TR_DIRECT) {
658                                 env.variants[j] = var;
659                         }
660                         if (env.variants[j] != var) {
661                                 /* not compatible */
662                                 DB((dbg, LEVEL_3, "  tail recursion fails for %d return value of %+F\n", j, ret));
663                                 break;
664                         }
665                 }
666                 if (j >= 0)
667                         continue;
668
669                 /* here, we have found a call */
670                 set_irn_link(call, get_irn_link(end_block));
671                 set_irn_link(end_block, call);
672                 ++n_tail_calls;
673
674                 /* link all returns, we will need this */
675                 set_irn_link(ret, rets);
676                 rets = ret;
677         }
678
679         /* now, end_block->link contains the list of all tail calls */
680         if (n_tail_calls > 0) {
681                 DB((dbg, LEVEL_2, "  Performing tail recursion for graph %s and %d Calls\n",
682                     get_entity_ld_name(get_irg_entity(irg)), n_tail_calls));
683
684                 hook_tail_rec(irg, n_tail_calls);
685
686                 env.n_tail_calls = n_tail_calls;
687                 env.rets         = rets;
688                 do_opt_tail_rec(irg, &env);
689                 confirm_irg_properties(irg, IR_GRAPH_PROPERTIES_NONE);
690         } else {
691                 confirm_irg_properties(irg, IR_GRAPH_PROPERTIES_ALL);
692         }
693         ir_free_resources(irg, IR_RESOURCE_IRN_LINK);
694         current_ir_graph = rem;
695 }
696
697 ir_graph_pass_t *opt_tail_rec_irg_pass(const char *name)
698 {
699         return def_graph_pass(name ? name : "tailrec", opt_tail_rec_irg);
700 }
701
702 /*
703  * optimize tail recursion away
704  */
705 void opt_tail_recursion(void)
706 {
707         size_t i, n;
708
709         FIRM_DBG_REGISTER(dbg, "firm.opt.tailrec");
710
711         DB((dbg, LEVEL_1, "Performing tail recursion ...\n"));
712         for (i = 0, n = get_irp_n_irgs(); i < n; ++i) {
713                 ir_graph *irg = get_irp_irg(i);
714                 opt_tail_rec_irg(irg);
715         }
716 }
717
718 ir_prog_pass_t *opt_tail_recursion_pass(const char *name)
719 {
720         return def_prog_pass(name ? name : "tailrec", opt_tail_recursion);
721 }