Initial revision
[libfirm] / ir / ir / irgmod.c
1 /* Copyright (C) 1998 - 2000 by Universitaet Karlsruhe
2 ** All rights reserved.
3 **
4 ** Authors: Martin Trapp, Christian Schaefer
5 **
6 ** irgmod: ir graph modification
7 */
8
9 # include "irgmod.h"
10 # include "iropt.h"
11
12 /*  ir_node * */
13 /*  arg_access (ir_mode *mode, long proj) */
14 /*  { */
15 /*    return new_r_Proj (current_ir_graph, current_ir_graph->start,  */
16 /*                   current_ir_graph->args, mode, proj); */
17 /*  } */
18
19
20 /* these two functions are used in phi_merge, but defined in ircons.c */
21 inline ir_node * get_r_value_internal (ir_node *block,
22                                        int pos, ir_mode *mode);
23 inline ir_node * new_r_Phi_in (ir_graph *irg, ir_node *block, ir_mode *mode,
24                                ir_node **in, int ins);
25
26 /** This function computes the predecessors for a real Phi node, and then
27     allocates and returns this node.  The routine called to allocate the
28     node might optimize it away and return a real value, or even a pointer
29     to a deallocated Phi node on top of the obstack!
30     This function is called with an in-array of proper size. **/
31 static inline ir_node *
32 phi_merge (ir_node *block, int pos, ir_mode *mode, ir_node **nin, int ins)
33 {
34   ir_node *prevBlock, *res;
35   int i;
36
37   /* This loop goes to all predecessor blocks of the block the Phi node is in
38      and there finds the operands of the Phi node by calling get_r_value_internal. */
39   for (i = 1;  i <= ins;  ++i) {
40     assert (block->in[i]);
41     prevBlock = block->in[i]->in[0]; /* go past control flow op to prev block */
42     assert (prevBlock);
43     nin[i-1] = get_r_value_internal (prevBlock, pos, mode);
44   }
45
46   /* After collecting all predecessors into the array nin a new Phi node
47      with these predecessors is created.  This constructor contains an
48      optimization: If all predecessors of the Phi node are identical it
49      returns the only operand instead of a new Phi node.  If the value
50      passes two different control flow edges without being defined, and
51      this is the second path treated, a pointer to the node that will be
52      allocated for the first path (recurstion) is returned.  We already
53      know the address of this node, as it is the next node to be allocated
54      and will be placed on top of the obstack. (The obstack is a _stack_!) */
55   res = new_r_Phi_in (current_ir_graph, block, mode, nin, ins);
56
57   /* Now we now the value "pos" and can enter it in the array with
58      all known local variables.  Attention: this might be a pointer to
59      a node, that later will be allocated!!! See new_r_Phi_in.
60      If this is called in mature, after some set_value in the same block,
61      the proper value must not be overwritten:
62      The call order
63        get_value    (makes Phi0, put's it into graph_arr)
64        set_value    (overwrites Phi0 in graph_arr)
65        mature_block (upgrades Phi0, puts it again into graph_arr, overwriting
66                      the proper value.)
67      fails. */
68   if (!block->attr.block.graph_arr[pos]) {
69     block->attr.block.graph_arr[pos] = res;
70   } else {
71     //  printf(" value already computed by %s\n",
72     //  id_to_str(block->attr.block.graph_arr[pos]->op->name));
73   }
74
75   return res;
76 }
77
78
79 /* this function is used in get_r_value_internal, but defined in ircons.c */
80 inline ir_node * new_r_Phi0 (ir_graph *irg, ir_node *block, ir_mode *mode);
81
82
83 /* This function returns the last definition of a variable.  In case
84    this variable was last defined in a previous block, Phi nodes are
85    inserted.  If the part of the firm graph containing the definition
86    is not yet constructed, a dummy Phi node is returned. */
87 inline ir_node *
88 get_r_value_internal (ir_node *block, int pos, ir_mode *mode)
89 {
90   ir_node *res;
91   /* There are 4 cases to treat.
92
93      1. The block is not mature and we visit it the first time.  We can not
94         create a proper Phi node, therefore a Phi0, i.e., a Phi without
95         predecessors is returned.  This node is added to the linked list (field
96         "link") of the containing block to be completed when this block is
97         matured. (Comlpletion will add a new Phi and turn the Phi0 into a Id
98         node.)
99
100      2. The value is already known in this block, graph_arr[pos] is set and we
101         visit the block the first time.  We can return the value without
102         creating any new nodes.
103
104      3. The block is mature and we visit it the first time.  A Phi node needs
105         to be created (phi_merge).  If the Phi is not needed, as all it's
106         operands are the same value reaching the block through different
107         paths, it's optimizes away and the value itself is returned.
108
109      4. The block is mature, and we visit it the second time.  Now two
110         subcases are possible:
111         * The value was computed completely the last time we were here.
112           This is the case if there is no loop.  We can return the proper value.
113         * The recursion that visited this node and set the flag did not
114           return yet.  We are computing a value in a loop and need to
115           break the recursion without knowing the result yet.
116         There is no simple check for the second subcase.  Therefore we check
117         for a second visit and treat all such cases as the second subcase.
118         Anyways, the basic situation is the same:  we reached a block
119         on two paths without finding a definition of the value:  No Phi
120         nodes are needed on both paths.
121         We return this information "Two paths, no Phi needed" by a very tricky
122         implementation that relies on the fact that an obstack is a stack and
123         will return a node with the same address on different allocations.
124         Look also at phi_merge and get_r_phi_in to understand this.
125   */
126
127   /* case 4 -- already visited. */
128   if (block->visit == ir_visited) return NULL;
129
130   /* visited the first time */
131   block->visit = ir_visited;
132
133   /* Get the local valid value */
134   res = block->attr.block.graph_arr[pos];
135
136   /* case 2 -- If the value is actually computed, return it. */
137   if (res) { return res;};
138
139   if (block->attr.block.matured) { /* case 3 */
140
141     /* The Phi has the same amount of ins as the corresponding block. */
142     int ins = get_irn_arity(block); // ARR_LEN (block->in)-1;
143     ir_node **nin;
144     NEW_ARR_A (ir_node *, nin, ins);
145
146     /* Phi merge collects the predecessors and then creates a node. */
147     res = phi_merge (block, pos, mode, nin, ins);
148
149   } else {  /* case 1 */
150     /* The block is not mature, we don't know how many in's are needed.  A Phi
151        with zero predecessors is created.  Such a Phi node is called Phi0
152        node.  (There is also an obsolete Phi0 opcode.) The Phi0 is then added
153        to the list of Phi0 nodes in this block to be matured by mature_block
154        later.
155        The Phi0 has to remember the pos of it's internal value.  If the real Phi
156        is computed, pos is used to update the array with the local values. */
157
158     res = new_r_Phi0 (current_ir_graph, block, mode);
159     res->attr.phi0_pos = pos;
160     res->link = block->link;
161     block->link = res;
162   }
163
164   /* If we get here, the frontend missed a use-before-definition error */
165   if (!res) {
166     /* Error Message */
167     printf("Error: no value set\n");
168     assert (mode->code >= irm_f && mode->code <= irm_p);
169     res = new_r_Const (current_ir_graph, block, mode,
170                        tarval_mode_null[mode->code]);
171   }
172
173   /* The local valid value is available now. */
174   block->attr.block.graph_arr[pos] = res;
175
176   return res;
177 }
178
179
180 /* add an adge to a jmp node */
181 void
182 add_in_edge (ir_node *block, ir_node *jmp)
183 {
184   if (block->attr.block.matured) {
185     printf("Error: Block already matured!\n");
186   }
187   else {
188     assert (jmp != NULL);
189     ARR_APP1 (ir_node *, block->in, jmp);
190   }
191 }
192
193
194 /****************************/
195 /* parameter administration */
196
197 /* get a value from the parameter array from the current block by its index */
198 ir_node *
199 get_value (int pos, ir_mode *mode)
200 {
201   ++ir_visited;
202   return get_r_value_internal (current_ir_graph->current_block, pos + 1, mode);
203 }
204
205
206 /* set a value at position pos in the parameter array from the current block */
207 inline void
208 set_value (int pos, ir_node *value)
209 {
210   current_ir_graph->current_block->attr.block.graph_arr[pos + 1] = value;
211 }
212
213
214 /* get the current store */
215 inline ir_node *
216 get_store (void)
217 {
218   /* GL: one could call get_value instead */
219   ++ir_visited;
220   return get_r_value_internal (current_ir_graph->current_block, 0, mode_M);
221 }
222
223
224 /* set the current store */
225 inline void
226 set_store (ir_node *store)
227 {
228   /* GL: one could call set_value instead */
229   current_ir_graph->current_block->attr.block.graph_arr[0] = store;
230 }
231
232 /** Finalize a Block node, when all control flows are known.  */
233 /** Acceptable parameters are only Block nodes.               */
234 void
235 mature_block (ir_node *block)
236 {
237
238   int ins;
239   ir_node *n, **nin;
240   ir_node *next;
241
242   assert (get_irn_opcode(block) == iro_Block);
243
244   if (!get_Block_matured(block)) {
245
246     /* turn the dynamic in-array into a static one. */
247     ins = ARR_LEN (block->in)-1;
248     NEW_ARR_A (ir_node *, nin, ins);
249
250     /* GL, 7.2.2000.  seems to be not complete. added this: but does'nt work.*
251     memcpy (nin, block->in, sizeof (ir_node *) * ins);
252     block->in = nin;
253     */
254
255     /* Traverse a chain of Phi nodes attached to this block and mature these, too. **/
256     for (n = block->link;  n;  n=next) {
257       ir_visited++;
258       next = n->link;
259       exchange (n, phi_merge (block, n->attr.phi0_pos, n->mode, nin, ins));
260     }
261
262     block->attr.block.matured = 1;
263
264     block = optimize_in_place(block);
265     ir_vrfy(block);
266   }
267
268 }
269
270 /* changing the current block */
271 void
272 switch_block (ir_node *target)
273 {
274   current_ir_graph->current_block = target;
275 }
276
277
278 void
279 turn_into_tuple (ir_node *node, int arity)
280 {
281   assert(node);
282   set_irn_op(node, op_Tuple);
283   if (get_irn_arity(node) == arity) {
284     /* keep old array */
285   } else {
286     /* allocate new array, remove old one. */
287     /* !!!??? free old in_array */
288     node->in = NEW_ARR_D (ir_node *, current_ir_graph->obst, arity+1);
289   }
290 }
291
292
293
294 /* Insert irnode `new' in place of irnode `old'
295    Since `new' may be bigger than `old' replace `old'
296    by an op_Id which is smaller than everything */
297 inline void
298 exchange (ir_node *old, ir_node *new)
299 {
300   ir_node *block = old->in[0];
301
302   old->op = op_Id;
303   old->in = NEW_ARR_D (ir_node *, current_ir_graph->obst, 2);
304   old->in[0] = block;
305   old->in[1] = new;
306 }