1/*
2 * Copyright © 2010 Intel Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 */
23
24#include "ir.h"
25
26/**
27 * \file ir_hv_accept.cpp
28 * Implementations of all hierarchical visitor accept methods for IR
29 * instructions.
30 */
31
32/**
33 * Process a list of nodes using a hierarchical vistor.
34 *
35 * If statement_list is true (the default), this is a list of statements, so
36 * v->base_ir will be set to point to each statement just before iterating
37 * over it, and restored after iteration is complete.  If statement_list is
38 * false, this is a list that appears inside a statement (e.g. a parameter
39 * list), so v->base_ir will be left alone.
40 *
41 * \warning
42 * This function will operate correctly if a node being processed is removed
43 * from the list.  However, if nodes are added to the list after the node being
44 * processed, some of the added nodes may not be processed.
45 */
46ir_visitor_status
47visit_list_elements(ir_hierarchical_visitor *v, exec_list *l,
48                    bool statement_list)
49{
50   ir_instruction *prev_base_ir = v->base_ir;
51
52   foreach_list_safe(n, l) {
53      ir_instruction *const ir = (ir_instruction *) n;
54      if (statement_list)
55         v->base_ir = ir;
56      ir_visitor_status s = ir->accept(v);
57
58      if (s != visit_continue)
59	 return s;
60   }
61   if (statement_list)
62      v->base_ir = prev_base_ir;
63
64   return visit_continue;
65}
66
67
68ir_visitor_status
69ir_rvalue::accept(ir_hierarchical_visitor *v)
70{
71   return v->visit(this);
72}
73
74
75ir_visitor_status
76ir_variable::accept(ir_hierarchical_visitor *v)
77{
78   return v->visit(this);
79}
80
81
82ir_visitor_status
83ir_loop::accept(ir_hierarchical_visitor *v)
84{
85   ir_visitor_status s = v->visit_enter(this);
86
87   if (s != visit_continue)
88      return (s == visit_continue_with_parent) ? visit_continue : s;
89
90   s = visit_list_elements(v, &this->body_instructions);
91   if (s == visit_stop)
92      return s;
93
94   if (s != visit_continue_with_parent) {
95      if (this->from) {
96	 s = this->from->accept(v);
97	 if (s != visit_continue)
98	    return (s == visit_continue_with_parent) ? visit_continue : s;
99      }
100
101      if (this->to) {
102	 s = this->to->accept(v);
103	 if (s != visit_continue)
104	    return (s == visit_continue_with_parent) ? visit_continue : s;
105      }
106
107      if (this->increment) {
108	 s = this->increment->accept(v);
109	 if (s != visit_continue)
110	    return (s == visit_continue_with_parent) ? visit_continue : s;
111      }
112   }
113
114   return v->visit_leave(this);
115}
116
117
118ir_visitor_status
119ir_loop_jump::accept(ir_hierarchical_visitor *v)
120{
121   return v->visit(this);
122}
123
124
125ir_visitor_status
126ir_function_signature::accept(ir_hierarchical_visitor *v)
127{
128   ir_visitor_status s = v->visit_enter(this);
129   if (s != visit_continue)
130      return (s == visit_continue_with_parent) ? visit_continue : s;
131
132   s = visit_list_elements(v, &this->parameters);
133   if (s == visit_stop)
134      return s;
135
136   s = visit_list_elements(v, &this->body);
137   return (s == visit_stop) ? s : v->visit_leave(this);
138}
139
140
141ir_visitor_status
142ir_function::accept(ir_hierarchical_visitor *v)
143{
144   ir_visitor_status s = v->visit_enter(this);
145   if (s != visit_continue)
146      return (s == visit_continue_with_parent) ? visit_continue : s;
147
148   s = visit_list_elements(v, &this->signatures, false);
149   return (s == visit_stop) ? s : v->visit_leave(this);
150}
151
152
153ir_visitor_status
154ir_expression::accept(ir_hierarchical_visitor *v)
155{
156   ir_visitor_status s = v->visit_enter(this);
157
158   if (s != visit_continue)
159      return (s == visit_continue_with_parent) ? visit_continue : s;
160
161   for (unsigned i = 0; i < this->get_num_operands(); i++) {
162      switch (this->operands[i]->accept(v)) {
163      case visit_continue:
164	 break;
165
166      case visit_continue_with_parent:
167	 // I wish for Java's labeled break-statement here.
168	 goto done;
169
170      case visit_stop:
171	 return s;
172      }
173   }
174
175done:
176   return v->visit_leave(this);
177}
178
179ir_visitor_status
180ir_texture::accept(ir_hierarchical_visitor *v)
181{
182   ir_visitor_status s = v->visit_enter(this);
183   if (s != visit_continue)
184      return (s == visit_continue_with_parent) ? visit_continue : s;
185
186   s = this->sampler->accept(v);
187   if (s != visit_continue)
188      return (s == visit_continue_with_parent) ? visit_continue : s;
189
190   if (this->coordinate) {
191      s = this->coordinate->accept(v);
192      if (s != visit_continue)
193	 return (s == visit_continue_with_parent) ? visit_continue : s;
194   }
195
196   if (this->projector) {
197      s = this->projector->accept(v);
198      if (s != visit_continue)
199	 return (s == visit_continue_with_parent) ? visit_continue : s;
200   }
201
202   if (this->shadow_comparitor) {
203      s = this->shadow_comparitor->accept(v);
204      if (s != visit_continue)
205	 return (s == visit_continue_with_parent) ? visit_continue : s;
206   }
207
208   if (this->offset) {
209      s = this->offset->accept(v);
210      if (s != visit_continue)
211	 return (s == visit_continue_with_parent) ? visit_continue : s;
212   }
213
214   switch (this->op) {
215   case ir_tex:
216      break;
217   case ir_txb:
218      s = this->lod_info.bias->accept(v);
219      if (s != visit_continue)
220	 return (s == visit_continue_with_parent) ? visit_continue : s;
221      break;
222   case ir_txl:
223   case ir_txf:
224   case ir_txs:
225      s = this->lod_info.lod->accept(v);
226      if (s != visit_continue)
227	 return (s == visit_continue_with_parent) ? visit_continue : s;
228      break;
229   case ir_txd:
230      s = this->lod_info.grad.dPdx->accept(v);
231      if (s != visit_continue)
232	 return (s == visit_continue_with_parent) ? visit_continue : s;
233
234      s = this->lod_info.grad.dPdy->accept(v);
235      if (s != visit_continue)
236	 return (s == visit_continue_with_parent) ? visit_continue : s;
237      break;
238   }
239
240   return (s == visit_stop) ? s : v->visit_leave(this);
241}
242
243
244ir_visitor_status
245ir_swizzle::accept(ir_hierarchical_visitor *v)
246{
247   ir_visitor_status s = v->visit_enter(this);
248   if (s != visit_continue)
249      return (s == visit_continue_with_parent) ? visit_continue : s;
250
251   s = this->val->accept(v);
252   return (s == visit_stop) ? s : v->visit_leave(this);
253}
254
255
256ir_visitor_status
257ir_dereference_variable::accept(ir_hierarchical_visitor *v)
258{
259   return v->visit(this);
260}
261
262
263ir_visitor_status
264ir_dereference_array::accept(ir_hierarchical_visitor *v)
265{
266   ir_visitor_status s = v->visit_enter(this);
267   if (s != visit_continue)
268      return (s == visit_continue_with_parent) ? visit_continue : s;
269
270   /* The array index is not the target of the assignment, so clear the
271    * 'in_assignee' flag.  Restore it after returning from the array index.
272    */
273   const bool was_in_assignee = v->in_assignee;
274   v->in_assignee = false;
275   s = this->array_index->accept(v);
276   v->in_assignee = was_in_assignee;
277
278   if (s != visit_continue)
279      return (s == visit_continue_with_parent) ? visit_continue : s;
280
281   s = this->array->accept(v);
282   return (s == visit_stop) ? s : v->visit_leave(this);
283}
284
285
286ir_visitor_status
287ir_dereference_record::accept(ir_hierarchical_visitor *v)
288{
289   ir_visitor_status s = v->visit_enter(this);
290   if (s != visit_continue)
291      return (s == visit_continue_with_parent) ? visit_continue : s;
292
293   s = this->record->accept(v);
294   return (s == visit_stop) ? s : v->visit_leave(this);
295}
296
297
298ir_visitor_status
299ir_assignment::accept(ir_hierarchical_visitor *v)
300{
301   ir_visitor_status s = v->visit_enter(this);
302   if (s != visit_continue)
303      return (s == visit_continue_with_parent) ? visit_continue : s;
304
305   v->in_assignee = true;
306   s = this->lhs->accept(v);
307   v->in_assignee = false;
308   if (s != visit_continue)
309      return (s == visit_continue_with_parent) ? visit_continue : s;
310
311   s = this->rhs->accept(v);
312   if (s != visit_continue)
313      return (s == visit_continue_with_parent) ? visit_continue : s;
314
315   if (this->condition)
316      s = this->condition->accept(v);
317
318   return (s == visit_stop) ? s : v->visit_leave(this);
319}
320
321
322ir_visitor_status
323ir_constant::accept(ir_hierarchical_visitor *v)
324{
325   return v->visit(this);
326}
327
328
329ir_visitor_status
330ir_call::accept(ir_hierarchical_visitor *v)
331{
332   ir_visitor_status s = v->visit_enter(this);
333   if (s != visit_continue)
334      return (s == visit_continue_with_parent) ? visit_continue : s;
335
336   if (this->return_deref != NULL) {
337      v->in_assignee = true;
338      s = this->return_deref->accept(v);
339      v->in_assignee = false;
340      if (s != visit_continue)
341	 return (s == visit_continue_with_parent) ? visit_continue : s;
342   }
343
344   s = visit_list_elements(v, &this->actual_parameters, false);
345   if (s == visit_stop)
346      return s;
347
348   return v->visit_leave(this);
349}
350
351
352ir_visitor_status
353ir_return::accept(ir_hierarchical_visitor *v)
354{
355   ir_visitor_status s = v->visit_enter(this);
356   if (s != visit_continue)
357      return (s == visit_continue_with_parent) ? visit_continue : s;
358
359   ir_rvalue *val = this->get_value();
360   if (val) {
361      s = val->accept(v);
362      if (s != visit_continue)
363	 return (s == visit_continue_with_parent) ? visit_continue : s;
364   }
365
366   return v->visit_leave(this);
367}
368
369
370ir_visitor_status
371ir_discard::accept(ir_hierarchical_visitor *v)
372{
373   ir_visitor_status s = v->visit_enter(this);
374   if (s != visit_continue)
375      return (s == visit_continue_with_parent) ? visit_continue : s;
376
377   if (this->condition != NULL) {
378      s = this->condition->accept(v);
379      if (s != visit_continue)
380	 return (s == visit_continue_with_parent) ? visit_continue : s;
381   }
382
383   return v->visit_leave(this);
384}
385
386
387ir_visitor_status
388ir_if::accept(ir_hierarchical_visitor *v)
389{
390   ir_visitor_status s = v->visit_enter(this);
391   if (s != visit_continue)
392      return (s == visit_continue_with_parent) ? visit_continue : s;
393
394   s = this->condition->accept(v);
395   if (s != visit_continue)
396      return (s == visit_continue_with_parent) ? visit_continue : s;
397
398   if (s != visit_continue_with_parent) {
399      s = visit_list_elements(v, &this->then_instructions);
400      if (s == visit_stop)
401	 return s;
402   }
403
404   if (s != visit_continue_with_parent) {
405      s = visit_list_elements(v, &this->else_instructions);
406      if (s == visit_stop)
407	 return s;
408   }
409
410   return v->visit_leave(this);
411}
412