8f0fb8e733ccb083769a303d7b1cf15b79edc8ae
[libfirm] / ir / be / benode.c
1 /**
2  * @file   benode.c
3  * @date   17.05.2005
4  * @author Sebastian Hack
5  *
6  * Backend node support.
7  *
8  * This file provdies Perm, Copy, Spill and Reload nodes.
9  *
10  * Copyright (C) 2005 Universitaet Karlsruhe
11  * Released under the GPL
12  */
13
14 #ifdef HAVE_CONFIG_H
15 #include "config.h"
16 #endif
17
18 #include <stdlib.h>
19
20 #include "obst.h"
21 #include "set.h"
22 #include "pmap.h"
23 #include "util.h"
24 #include "debug.h"
25 #include "fourcc.h"
26
27 #include "irop_t.h"
28 #include "irmode_t.h"
29 #include "irnode_t.h"
30 #include "ircons_t.h"
31 #include "irprintf.h"
32
33 #include "be_t.h"
34 #include "belive_t.h"
35 #include "besched_t.h"
36 #include "benode_t.h"
37
38 #include "beirgmod.h"
39
40 #define DBG_LEVEL 0
41
42 #define BENODE_MAGIC FOURCC('B', 'E', 'N', 'O')
43
44 typedef enum _node_kind_t {
45         node_kind_spill,
46         node_kind_reload,
47         node_kind_perm,
48         node_kind_copy,
49         node_kind_last
50 } node_kind_t;
51
52 typedef struct {
53         node_kind_t kind;
54         const arch_register_class_t *cls;
55         ir_op *op;
56         int n_pos;
57         int *pos;
58 } be_op_t;
59
60 typedef struct {
61         const arch_register_t *reg;
62         arch_register_req_t   req;
63 } be_reg_data_t;
64
65 typedef struct {
66         unsigned      magic;
67         const be_op_t *op;
68         int           n_regs;
69         be_reg_data_t reg_data[1];
70 } be_node_attr_t;
71
72 typedef struct {
73         be_node_attr_t attr;
74         ir_node *spill_ctx;  /**< The node in whose context this spill was introduced. */
75         unsigned offset;     /**< The offset of the memory location the spill writes to
76                                                    in the spill area. */
77 } be_spill_attr_t;
78
79 static int templ_pos_Spill[] = {
80         0
81 };
82
83 static int templ_pos_Reload[] = {
84         -1
85 };
86
87 static int templ_pos_Copy[] = {
88         0, -1
89 };
90
91 static int dump_node(ir_node *irn, FILE *f, dump_reason_t reason);
92
93 static const ir_op_ops be_node_ops = {
94         NULL,
95         NULL,
96         NULL,
97         NULL,
98         NULL,
99         NULL,
100         NULL,
101         NULL,
102         NULL,
103         NULL,
104         NULL,
105         dump_node,
106         NULL
107 };
108
109 static INLINE int is_be_node(const ir_node *irn)
110 {
111         const be_node_attr_t *attr = (const be_node_attr_t *) &irn->attr;
112         return attr->magic == BENODE_MAGIC;
113 }
114
115 static INLINE int is_be_kind(const ir_node *irn, node_kind_t kind)
116 {
117         const be_node_attr_t *a = (const be_node_attr_t *) &irn->attr;
118         return a->magic == BENODE_MAGIC && a->op && a->op->kind == kind;
119 }
120
121 static INLINE void *get_attr_and_check(ir_node *irn, node_kind_t kind)
122 {
123         is_be_kind(irn, kind);
124         return &irn->attr;
125 }
126
127 static be_node_attr_t *init_node_attr(ir_node *irn,
128                                                                           const be_op_t *op,
129                                                                           const arch_register_class_t *cls,
130                                                                           int n_regs)
131
132 {
133         be_node_attr_t *attr = (be_node_attr_t *) &irn->attr;
134         int i;
135
136         attr->magic   = BENODE_MAGIC;
137         attr->n_regs  = n_regs;
138         attr->op      = op;
139
140         for(i = 0; i < n_regs; ++i) {
141                 be_reg_data_t *rd = attr->reg_data + i;
142
143                 rd->reg      = NULL;
144                 rd->req.cls  = cls;
145                 rd->req.type = arch_register_req_type_normal;
146         }
147
148         return attr;
149 }
150
151 #define ARRSIZE(x) (sizeof(x) / sizeof(x[0]))
152
153 static int cmp_op_map(const void *a, const void *b, size_t size)
154 {
155         const be_op_t *x = a;
156         const be_op_t *y = b;
157
158         return !(x->kind == y->kind && x->cls == y->cls);
159 }
160
161 static be_op_t *get_op(const be_node_factory_t *fact,
162                 const arch_register_class_t *cls, node_kind_t kind)
163 {
164         be_op_t templ;
165
166         templ.kind = kind;
167         templ.cls = cls;
168
169         return set_insert(fact->ops, &templ, sizeof(templ),
170                 HASH_PTR(cls) + 7 * kind);
171 }
172
173 ir_node *new_Spill(const be_node_factory_t *factory,
174                 const arch_register_class_t *cls,
175                 ir_graph *irg, ir_node *bl, ir_node *node_to_spill, ir_node *ctx)
176 {
177         be_spill_attr_t *a;
178         ir_node *irn;
179         ir_node *in[1];
180         be_op_t *bop = get_op(factory, cls, node_kind_spill);
181         ir_op *op    = bop->op;
182
183         assert(op && "Spill opcode must be present for this register class");
184         in[0]        = node_to_spill;
185         irn          = new_ir_node(NULL, irg, bl, op, mode_M, 1, in);
186         a            = (be_spill_attr_t *) init_node_attr(irn, bop, cls, 0);
187         a->spill_ctx = ctx;
188         a->offset    = 0;
189
190         return irn;
191 }
192
193 void set_Spill_offset(ir_node *irn, unsigned offset)
194 {
195         be_spill_attr_t *a = (be_spill_attr_t *) &irn->attr;
196         assert(is_be_kind(irn, node_kind_spill));
197         a->offset = offset;
198 }
199
200 static ir_node *find_a_spill_walker(ir_node *irn, unsigned visited_nr)
201 {
202         if(get_irn_visited(irn) < visited_nr) {
203                 set_irn_visited(irn, visited_nr);
204
205                 if(is_Phi(irn)) {
206                         int i, n;
207                         for(i = 0, n = get_irn_arity(irn); i < n; ++i) {
208                                 ir_node *n = find_a_spill_walker(get_irn_n(irn, i), visited_nr);
209                                 if(n != NULL)
210                                         return n;
211                         }
212                 }
213
214                 else if(is_be_kind(irn, node_kind_spill))
215                         return irn;
216         }
217
218         return NULL;
219 }
220
221 /**
222  * Finds a spill for a reload.
223  * If the reload is directly using the spill, this is simple,
224  * else we perform DFS from the reload (over all PhiMs) and return
225  * the first spill node we find.
226  */
227 static INLINE ir_node *find_a_spill(ir_node *irn)
228 {
229         ir_graph *irg       = get_irn_irg(irn);
230         unsigned visited_nr = get_irg_visited(irg) + 1;
231
232         assert(is_be_kind(irn, node_kind_reload));
233         set_irg_visited(irg, visited_nr);
234         return find_a_spill_walker(irn, visited_nr);
235 }
236
237 unsigned get_irn_spill_offset(ir_node *irn)
238 {
239         be_node_attr_t *a = (be_node_attr_t *) &irn->attr;
240         assert(is_be_node(irn));
241
242         switch(a->op->kind) {
243         case node_kind_reload:
244                 assert(0 && "not yet implemented");
245                 return get_irn_spill_offset(find_a_spill(irn));
246         case node_kind_spill:
247                 return ((be_spill_attr_t *) a)->offset;
248         default:
249                 assert(0 && "Illegal node kind (spill/reload required)");
250         }
251
252         return 0;
253 }
254
255 ir_node *new_Reload(const be_node_factory_t *factory,
256                 const arch_register_class_t *cls, ir_graph *irg,
257                 ir_node *bl, ir_mode *mode, ir_node *spill_node)
258 {
259         ir_node *irn, *in[1];
260         be_op_t *bop = get_op(factory, cls, node_kind_reload);
261         ir_op *op    = bop->op;
262
263         assert(op && "Reload opcode must be present for this register class");
264         in[0] = spill_node;
265
266         irn  = new_ir_node(NULL, irg, bl, op, mode, 1, in);
267         init_node_attr(irn, bop, cls, 1);
268
269         return irn;
270 }
271
272 ir_node *new_Perm(const be_node_factory_t *factory,
273                 const arch_register_class_t *cls,
274                 ir_graph *irg, ir_node *bl, int arity, ir_node **in)
275 {
276         ir_node *irn;
277         be_op_t *bop = get_op(factory, cls, node_kind_perm);
278         ir_op *op    = bop->op;
279
280         irn  = new_ir_node(NULL, irg, bl, op, mode_T, arity, in);
281         init_node_attr(irn, bop, cls, arity);
282
283         return irn;
284 }
285
286 ir_node *new_Copy(const be_node_factory_t *factory,
287                 const arch_register_class_t *cls,
288                 ir_graph *irg, ir_node *bl, ir_node *in)
289 {
290         ir_node *irn, *ins[1];
291         be_op_t *bop = get_op(factory, cls, node_kind_copy);
292         ir_op *op    = bop->op;
293
294         ins[0] = in;
295
296         irn  = new_ir_node(NULL, irg, bl, op, get_irn_mode(in), 1, ins);
297         init_node_attr(irn, bop, cls, 1);
298
299         return irn;
300 }
301
302 ir_node *be_spill(
303                 const be_node_factory_t *factory,
304                 const arch_env_t *arch_env,
305                 ir_node *irn, ir_node *ctx)
306 {
307         const arch_register_class_t *cls = arch_get_irn_reg_class(arch_env, irn, -1);
308
309         ir_node *bl    = get_nodes_block(irn);
310         ir_graph *irg  = get_irn_irg(bl);
311         ir_node *spill = new_Spill(factory, cls, irg, bl, irn, ctx);
312         ir_node *insert;
313
314         /*
315          * search the right insertion point. a spill of a phi cannot be put
316          * directly after the phi, if there are some phis behind the one which
317          * is spilled.
318          */
319         insert = sched_next(irn);
320         while(is_Phi(insert) && !sched_is_end(insert))
321                 insert = sched_next(insert);
322
323         sched_add_before(insert, spill);
324         return spill;
325 }
326
327 ir_node *be_reload(const be_node_factory_t *factory,
328                                          const arch_env_t *arch_env,
329                                          const arch_register_class_t *cls,
330                                          ir_node *irn, int pos, ir_mode *mode, ir_node *spill)
331 {
332         ir_node *reload;
333
334         ir_node *bl   = get_nodes_block(irn);
335         ir_graph *irg = get_irn_irg(bl);
336
337         assert(is_Spill(spill)
338                         || (is_Phi(spill) && get_irn_mode(spill) == mode_M));
339
340         reload = new_Reload(factory, cls, irg, bl, mode, spill);
341
342         set_irn_n(irn, pos, reload);
343         sched_add_before(irn, reload);
344         return reload;
345 }
346
347 static INLINE arch_register_req_t *get_Perm_reqs(ir_node *perm)
348 {
349         be_node_attr_t *attr = (be_node_attr_t *) &perm->attr;
350         char *ptr            = (char *) &perm->attr;
351
352         ptr += sizeof(be_node_attr_t);
353         ptr += sizeof(arch_register_t *) * attr->n_regs;
354
355         return (arch_register_req_t *) ptr;
356 }
357
358 /**
359  * If the node is a proj, reset the node to the proj's target and return
360  * the proj number.
361  * @param node The address of a node pointer.
362  * @param def  A default value.
363  * @return     If *node is a Proj, *node is set to the Proj's target and
364  *             the Proj number is returned. Else *node remains the same and @p def
365  *             is returned.
366  */
367 static int redir_proj(const ir_node **node, int def)
368 {
369         const ir_node *n = *node;
370
371         if(is_Proj(n)) {
372                 *node = get_Proj_pred(n);
373                 def = -(get_Proj_proj(n) + 1);
374         }
375
376         return def;
377 }
378
379 static const arch_register_req_t *
380 be_node_get_irn_reg_req(const arch_irn_ops_t *_self,
381                 arch_register_req_t *req, const ir_node *irn, int pos)
382 {
383         be_op_t *bo;
384         const be_node_factory_t *factory =
385                 container_of(_self, const be_node_factory_t, irn_ops);
386
387         /* We cannot get output requirements for tuple nodes. */
388         if(get_irn_mode(irn) == mode_T && pos < 0)
389                 return NULL;
390
391         /*
392          * if we're interested in an output operand (pos < 0), so let's resolve projs.
393          */
394         if(pos < 0)
395                 pos = redir_proj((const ir_node **) &irn, pos);
396
397         /* look if the node is one of ours. */
398         bo = pmap_get(factory->irn_op_map, get_irn_op(irn));
399
400         if(bo) {
401                 int i;
402
403                 for(i = 0; i < bo->n_pos; ++i) {
404                         if(pos == bo->pos[i]) {
405
406                                 /* be nodes have no input constraints.
407                                    so return normal register requirements. */
408                                 if(pos >= 0) {
409                                         req->cls = bo->cls;
410                                         req->type = arch_register_req_type_normal;
411                                 }
412
413                                 /*
414                                  * if an output requirement is requested,
415                                  * return the one stored in the node.
416                                  */
417                                 else {
418                                         be_node_attr_t *attr = (be_node_attr_t *) &irn->attr;
419                                         *req = attr->reg_data[pos].req;
420                                 }
421
422                                 return req;
423                         }
424                 }
425         }
426
427         return NULL;
428 }
429
430 void be_set_Perm_out_req(ir_node *irn, int pos, const arch_register_req_t *req)
431 {
432         be_op_t *bo;
433         be_node_attr_t *a = get_attr_and_check(irn, node_kind_perm);
434
435         assert(pos >= 0 && pos < get_irn_arity(irn) && "position out of range");
436         assert(a->op->kind == node_kind_perm && "node must be a perm node");
437
438         a->reg_data[pos].req = *req;
439 }
440
441 void
442 be_node_set_irn_reg(const arch_irn_ops_t *_self, ir_node *irn,
443                 const arch_register_t *reg)
444 {
445         int pos;
446         be_op_t *bo;
447         be_node_attr_t *attr;
448         const be_node_factory_t *factory =
449                 container_of(_self, const be_node_factory_t, irn_ops);
450
451         if(get_irn_mode(irn) == mode_T)
452                 return;
453
454         pos = redir_proj((const ir_node **) &irn, -1);
455         bo = pmap_get(factory->irn_op_map, get_irn_op(irn));
456
457         if(!bo)
458                 return;
459
460         attr = (be_node_attr_t *) &irn->attr;
461         attr->reg_data[-pos - 1].reg = reg;
462 }
463
464 const arch_register_t *
465 be_node_get_irn_reg(const arch_irn_ops_t *_self, const ir_node *irn)
466 {
467         int i, pos;
468         be_op_t *bo;
469         const be_node_factory_t *factory =
470                 container_of(_self, const be_node_factory_t, irn_ops);
471
472         if(get_irn_mode(irn) == mode_T)
473                 return NULL;
474
475         pos = redir_proj((const ir_node **) &irn, -1);
476         bo = pmap_get(factory->irn_op_map, get_irn_op(irn));
477
478         if(!bo)
479                 return NULL;
480
481         for(i = 0; i < bo->n_pos; ++i) {
482                 if(bo->pos[i] == pos) {
483                         be_node_attr_t *attr = (be_node_attr_t *) &irn->attr;
484                         return attr->reg_data[-pos - 1].reg;
485                 }
486         }
487
488         return NULL;
489 }
490
491 arch_irn_class_t be_node_classify(const arch_irn_ops_t *_self, const ir_node *irn)
492 {
493         const be_node_factory_t *factory = container_of(_self, const be_node_factory_t, irn_ops);
494
495         be_op_t *bo;
496         int idx;
497
498         idx = redir_proj(&irn, 0);
499         bo = pmap_get(factory->irn_op_map, get_irn_op(irn));
500
501         switch(bo->kind) {
502 #define XXX(a) case node_kind_ ## a: return arch_irn_class_ ## a;
503                 XXX(spill)
504                 XXX(reload)
505                 XXX(perm)
506                 XXX(copy)
507 #undef XXX
508                 default:
509                 return 0;
510         }
511
512         return 0;
513 }
514
515 arch_irn_class_t be_node_get_flags(const arch_irn_ops_t *_self, const ir_node *irn)
516 {
517         return 0;
518 }
519
520 static const arch_irn_ops_t *
521 be_node_get_irn_ops(const arch_irn_handler_t *_self, const ir_node *irn)
522 {
523         be_op_t *bo;
524         const be_node_factory_t *factory =
525                 container_of(_self, const be_node_factory_t, handler);
526
527         redir_proj(&irn, 0);
528         bo = pmap_get(factory->irn_op_map, get_irn_op(irn));
529
530         return bo ? &factory->irn_ops : NULL;
531 }
532
533 const arch_irn_handler_t *be_node_get_irn_handler(const be_node_factory_t *f)
534 {
535         return &f->handler;
536 }
537
538 int is_Spill(const ir_node *irn)
539 {
540         return is_be_kind(irn, node_kind_spill);
541 }
542
543 int is_Perm(const ir_node *irn)
544 {
545         return is_be_kind(irn, node_kind_perm);
546 }
547
548 be_node_factory_t *be_node_factory_init(be_node_factory_t *factory, const arch_isa_t *isa)
549 {
550         int i, j, n;
551
552         factory->ops = new_set(cmp_op_map, 64);
553         factory->irn_op_map = pmap_create();
554         obstack_init(&factory->obst);
555
556         factory->handler.get_irn_ops = be_node_get_irn_ops;
557
558         factory->irn_ops.get_irn_reg_req = be_node_get_irn_reg_req;
559         factory->irn_ops.set_irn_reg     = be_node_set_irn_reg;
560         factory->irn_ops.get_irn_reg     = be_node_get_irn_reg;
561         factory->irn_ops.classify        = be_node_classify;
562         factory->irn_ops.get_flags       = be_node_get_flags;
563
564         for(i = 0, n = arch_isa_get_n_reg_class(isa); i < n; ++i) {
565                 const arch_register_class_t *cls = arch_isa_get_reg_class(isa, i);
566                 be_op_t *ent;
567
568                 ent = get_op(factory, cls, node_kind_spill);
569                 ent->op = new_ir_op(get_next_ir_opcode(), "Spill", op_pin_state_pinned,
570                                 0, oparity_unary, 0, sizeof(be_spill_attr_t), &be_node_ops);
571                 ent->n_pos = ARRSIZE(templ_pos_Spill);
572                 ent->pos = templ_pos_Spill;
573                 pmap_insert(factory->irn_op_map, ent->op, ent);
574
575                 ent = get_op(factory, cls, node_kind_reload);
576                 ent->op = new_ir_op(get_next_ir_opcode(), "Reload", op_pin_state_pinned, 0,
577                                 oparity_unary, 0, sizeof(be_node_attr_t), &be_node_ops);
578                 ent->n_pos = ARRSIZE(templ_pos_Reload);
579                 ent->pos = templ_pos_Reload;
580                 pmap_insert(factory->irn_op_map, ent->op, ent);
581
582                 ent = get_op(factory, cls, node_kind_copy);
583                 ent->op = new_ir_op(get_next_ir_opcode(), "Copy", op_pin_state_pinned, 0,
584                                 oparity_unary, 0, sizeof(be_node_attr_t), &be_node_ops);
585                 ent->n_pos = ARRSIZE(templ_pos_Copy);
586                 ent->pos = templ_pos_Copy;
587                 pmap_insert(factory->irn_op_map, ent->op, ent);
588
589                 ent = get_op(factory, cls, node_kind_perm);
590                 ent->op = new_ir_op(get_next_ir_opcode(), "Perm", op_pin_state_pinned, 0,
591                                 oparity_variable, 0,
592                                 sizeof(be_node_attr_t)
593                                 + sizeof(be_reg_data_t) * cls->n_regs, &be_node_ops);
594                 ent->n_pos = 2 * cls->n_regs;
595                 ent->pos = obstack_alloc(&factory->obst, sizeof(ent->pos[0]) * ent->n_pos);
596                 for(j = 0; j < ent->n_pos; j += 2) {
597                         int k = j / 2;
598                         ent->pos[j] = k;
599                         ent->pos[j + 1] = -(k + 1);
600                 }
601                 pmap_insert(factory->irn_op_map, ent->op, ent);
602
603         }
604
605         return factory;
606 }
607
608 static int dump_node(ir_node *irn, FILE *f, dump_reason_t reason)
609 {
610         be_node_attr_t *at = (be_node_attr_t *) &irn->attr;
611         const be_op_t *bo;
612         int i;
613
614         assert(is_be_node(irn));
615         bo = at->op;
616
617         switch(reason) {
618                 case dump_node_opcode_txt:
619                         fprintf(f, get_op_name(bo->op));
620                         break;
621                 case dump_node_mode_txt:
622                         fprintf(f, get_mode_name(get_irn_mode(irn)));
623                         break;
624                 case dump_node_nodeattr_txt:
625                         fprintf(f, "%s ", bo->cls->name);
626                         break;
627                 case dump_node_info_txt:
628                         for(i = 0; i < at->n_regs; ++i) {
629                                 const arch_register_t *reg = at->reg_data[i].reg;
630                                 fprintf(f, "reg #%d: %s\n", i, reg ? reg->name : "n/a");
631                         }
632
633                         if(bo->kind == node_kind_spill) {
634                                 be_spill_attr_t *a = (be_spill_attr_t *) at;
635                                 ir_fprintf(f, "spill context: %+F\n", a->spill_ctx);
636                         }
637                         break;
638         }
639
640         return 0;
641 }
642
643 ir_node *insert_Perm_after(const be_main_env_t *env,
644                                                          const arch_register_class_t *cls,
645                                                          dom_front_info_t *dom_front,
646                                                          ir_node *pos)
647 {
648         const arch_env_t *arch_env  = env->arch_env;
649         ir_node *bl                 = is_Block(pos) ? pos : get_nodes_block(pos);
650         ir_graph *irg               = get_irn_irg(bl);
651         pset *live                  = pset_new_ptr_default();
652         firm_dbg_module_t *dbg      = firm_dbg_register("firm.be.node");
653
654         irn_live_t *li;
655         ir_node *curr, *irn, *perm, **nodes;
656         int i, n;
657
658         firm_dbg_set_mask(dbg, DBG_LEVEL);
659         DBG((dbg, LEVEL_1, "Insert Perm after: %+F\n", pos));
660
661
662         live_foreach(bl, li) {
663                 ir_node *irn = (ir_node *) li->irn;
664                 if(live_is_end(li) && arch_irn_has_reg_class(arch_env, irn, -1, cls))
665                         pset_insert_ptr(live, irn);
666         }
667
668         sched_foreach_reverse(bl, irn) {
669                 ir_node *x;
670
671         /*
672          * If we encounter the node we want to insert the Perm after,
673          * exit immediately, so that this node is still live
674          */
675                 if(irn == pos)
676                         break;
677
678                 DBG((dbg, LEVEL_1, "%+F\n", irn));
679                 for(x = pset_first(live); x; x = pset_next(live))
680                         DBG((dbg, LEVEL_1, "\tlive: %+F\n", x));
681
682                 if(arch_irn_has_reg_class(arch_env, irn, -1, cls))
683                         pset_remove_ptr(live, irn);
684
685                 for(i = 0, n = get_irn_arity(irn); i < n; ++i) {
686                         ir_node *op = get_irn_n(irn, i);
687
688                         if(arch_irn_has_reg_class(arch_env, op, -1, cls))
689                                 pset_insert_ptr(live, op);
690                 }
691         }
692
693         n = pset_count(live);
694         nodes = malloc(n * sizeof(nodes[0]));
695
696         DBG((dbg, LEVEL_1, "live:\n"));
697         for(irn = pset_first(live), i = 0; irn; irn = pset_next(live), i++) {
698                 DBG((dbg, LEVEL_1, "\t%+F\n", irn));
699                 nodes[i] = irn;
700         }
701
702         perm = new_Perm(env->node_factory, cls, irg, bl, n, nodes);
703         sched_add_after(pos, perm);
704         free(nodes);
705
706         curr = perm;
707         for(i = 0; i < n; ++i) {
708                 ir_node *copies[1];
709                 ir_node *perm_op = get_irn_n(perm, i);
710                 const arch_register_t *reg = arch_get_irn_register(arch_env, perm_op);
711
712                 ir_mode *mode = get_irn_mode(perm_op);
713                 ir_node *proj = new_r_Proj(irg, bl, perm, mode, i);
714                 arch_set_irn_register(arch_env, proj, reg);
715
716                 sched_add_after(curr, proj);
717                 curr = proj;
718
719                 copies[0] = proj;
720                 be_introduce_copies(dom_front, perm_op, array_size(copies), copies);
721         }
722         return perm;
723 }