Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1901 serge 1
/*
2
 * Copyright © 2010 Intel Corporation
3
 *
4
 * Permission is hereby granted, free of charge, to any person obtaining a
5
 * constant 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, constant, 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 constantright 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 CONSTANTRIGHT 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
/**
25
 * \file opt_constant_propagation.cpp
26
 *
27
 * Tracks assignments of constants to channels of variables, and
28
 * usage of those constant channels with direct usage of the constants.
29
 *
30
 * This can lead to constant folding and algebraic optimizations in
31
 * those later expressions, while causing no increase in instruction
32
 * count (due to constants being generally free to load from a
33
 * constant push buffer or as instruction immediate values) and
34
 * possibly reducing register pressure.
35
 */
36
 
37
#include "ir.h"
38
#include "ir_visitor.h"
39
#include "ir_rvalue_visitor.h"
40
#include "ir_basic_block.h"
41
#include "ir_optimization.h"
42
#include "glsl_types.h"
43
 
44
class acp_entry : public exec_node
45
{
46
public:
47
   acp_entry(ir_variable *var, unsigned write_mask, ir_constant *constant)
48
   {
49
      assert(var);
50
      assert(constant);
51
      this->var = var;
52
      this->write_mask = write_mask;
53
      this->constant = constant;
54
   }
55
 
56
   ir_variable *var;
57
   ir_constant *constant;
58
   unsigned write_mask;
59
};
60
 
61
 
62
class kill_entry : public exec_node
63
{
64
public:
65
   kill_entry(ir_variable *var, unsigned write_mask)
66
   {
67
      assert(var);
68
      this->var = var;
69
      this->write_mask = write_mask;
70
   }
71
 
72
   ir_variable *var;
73
   unsigned write_mask;
74
};
75
 
76
class ir_constant_propagation_visitor : public ir_rvalue_visitor {
77
public:
78
   ir_constant_propagation_visitor()
79
   {
80
      progress = false;
81
      mem_ctx = ralloc_context(0);
82
      this->acp = new(mem_ctx) exec_list;
83
      this->kills = new(mem_ctx) exec_list;
84
   }
85
   ~ir_constant_propagation_visitor()
86
   {
87
      ralloc_free(mem_ctx);
88
   }
89
 
90
   virtual ir_visitor_status visit_enter(class ir_loop *);
91
   virtual ir_visitor_status visit_enter(class ir_function_signature *);
92
   virtual ir_visitor_status visit_enter(class ir_function *);
93
   virtual ir_visitor_status visit_leave(class ir_assignment *);
94
   virtual ir_visitor_status visit_enter(class ir_call *);
95
   virtual ir_visitor_status visit_enter(class ir_if *);
96
 
97
   void add_constant(ir_assignment *ir);
98
   void kill(ir_variable *ir, unsigned write_mask);
99
   void handle_if_block(exec_list *instructions);
100
   void handle_rvalue(ir_rvalue **rvalue);
101
 
102
   /** List of acp_entry: The available constants to propagate */
103
   exec_list *acp;
104
 
105
   /**
106
    * List of kill_entry: The masks of variables whose values were
107
    * killed in this block.
108
    */
109
   exec_list *kills;
110
 
111
   bool progress;
112
 
113
   bool killed_all;
114
 
115
   void *mem_ctx;
116
};
117
 
118
 
119
void
120
ir_constant_propagation_visitor::handle_rvalue(ir_rvalue **rvalue)
121
{
122
   if (this->in_assignee || !*rvalue)
123
      return;
124
 
125
   const glsl_type *type = (*rvalue)->type;
126
   if (!type->is_scalar() && !type->is_vector())
127
      return;
128
 
129
   ir_swizzle *swiz = NULL;
130
   ir_dereference_variable *deref = (*rvalue)->as_dereference_variable();
131
   if (!deref) {
132
      swiz = (*rvalue)->as_swizzle();
133
      if (!swiz)
134
	 return;
135
 
136
      deref = swiz->val->as_dereference_variable();
137
      if (!deref)
138
	 return;
139
   }
140
 
141
   ir_constant_data data;
142
   memset(&data, 0, sizeof(data));
143
 
144
   for (unsigned int i = 0; i < type->components(); i++) {
145
      int channel;
146
      acp_entry *found = NULL;
147
 
148
      if (swiz) {
149
	 switch (i) {
150
	 case 0: channel = swiz->mask.x; break;
151
	 case 1: channel = swiz->mask.y; break;
152
	 case 2: channel = swiz->mask.z; break;
153
	 case 3: channel = swiz->mask.w; break;
154
	 default: assert(!"shouldn't be reached"); channel = 0; break;
155
	 }
156
      } else {
157
	 channel = i;
158
      }
159
 
160
      foreach_iter(exec_list_iterator, iter, *this->acp) {
161
	 acp_entry *entry = (acp_entry *)iter.get();
162
	 if (entry->var == deref->var && entry->write_mask & (1 << channel)) {
163
	    found = entry;
164
	    break;
165
	 }
166
      }
167
 
168
      if (!found)
169
	 return;
170
 
171
      int rhs_channel = 0;
172
      for (int j = 0; j < 4; j++) {
173
	 if (j == channel)
174
	    break;
175
	 if (found->write_mask & (1 << j))
176
	    rhs_channel++;
177
      }
178
 
179
      switch (type->base_type) {
180
      case GLSL_TYPE_FLOAT:
181
	 data.f[i] = found->constant->value.f[rhs_channel];
182
	 break;
183
      case GLSL_TYPE_INT:
184
	 data.i[i] = found->constant->value.i[rhs_channel];
185
	 break;
186
      case GLSL_TYPE_UINT:
187
	 data.u[i] = found->constant->value.u[rhs_channel];
188
	 break;
189
      case GLSL_TYPE_BOOL:
190
	 data.b[i] = found->constant->value.b[rhs_channel];
191
	 break;
192
      default:
193
	 assert(!"not reached");
194
	 break;
195
      }
196
   }
197
 
198
   *rvalue = new(ralloc_parent(deref)) ir_constant(type, &data);
199
   this->progress = true;
200
}
201
 
202
ir_visitor_status
203
ir_constant_propagation_visitor::visit_enter(ir_function_signature *ir)
204
{
205
   /* Treat entry into a function signature as a completely separate
206
    * block.  Any instructions at global scope will be shuffled into
207
    * main() at link time, so they're irrelevant to us.
208
    */
209
   exec_list *orig_acp = this->acp;
210
   exec_list *orig_kills = this->kills;
211
   bool orig_killed_all = this->killed_all;
212
 
213
   this->acp = new(mem_ctx) exec_list;
214
   this->kills = new(mem_ctx) exec_list;
215
   this->killed_all = false;
216
 
217
   visit_list_elements(this, &ir->body);
218
 
219
   this->kills = orig_kills;
220
   this->acp = orig_acp;
221
   this->killed_all = orig_killed_all;
222
 
223
   return visit_continue_with_parent;
224
}
225
 
226
ir_visitor_status
227
ir_constant_propagation_visitor::visit_leave(ir_assignment *ir)
228
{
229
   if (this->in_assignee)
230
      return visit_continue;
231
 
232
   kill(ir->lhs->variable_referenced(), ir->write_mask);
233
 
234
   add_constant(ir);
235
 
236
   return visit_continue;
237
}
238
 
239
ir_visitor_status
240
ir_constant_propagation_visitor::visit_enter(ir_function *ir)
241
{
242
   (void) ir;
243
   return visit_continue;
244
}
245
 
246
ir_visitor_status
247
ir_constant_propagation_visitor::visit_enter(ir_call *ir)
248
{
249
   /* Do constant propagation on call parameters, but skip any out params */
250
   exec_list_iterator sig_param_iter = ir->get_callee()->parameters.iterator();
251
   foreach_iter(exec_list_iterator, iter, ir->actual_parameters) {
252
      ir_variable *sig_param = (ir_variable *)sig_param_iter.get();
253
      ir_rvalue *param = (ir_rvalue *)iter.get();
254
      if (sig_param->mode != ir_var_out && sig_param->mode != ir_var_inout) {
255
	 ir_rvalue *new_param = param;
256
	 handle_rvalue(&new_param);
257
         if (new_param != param)
258
	    param->replace_with(new_param);
259
	 else
260
	    param->accept(this);
261
      }
262
      sig_param_iter.next();
263
   }
264
 
265
   /* Since we're unlinked, we don't (necssarily) know the side effects of
266
    * this call.  So kill all copies.
267
    */
268
   acp->make_empty();
269
   this->killed_all = true;
270
 
271
   return visit_continue_with_parent;
272
}
273
 
274
void
275
ir_constant_propagation_visitor::handle_if_block(exec_list *instructions)
276
{
277
   exec_list *orig_acp = this->acp;
278
   exec_list *orig_kills = this->kills;
279
   bool orig_killed_all = this->killed_all;
280
 
281
   this->acp = new(mem_ctx) exec_list;
282
   this->kills = new(mem_ctx) exec_list;
283
   this->killed_all = false;
284
 
285
   /* Populate the initial acp with a constant of the original */
286
   foreach_iter(exec_list_iterator, iter, *orig_acp) {
287
      acp_entry *a = (acp_entry *)iter.get();
288
      this->acp->push_tail(new(this->mem_ctx) acp_entry(a->var, a->write_mask,
289
							a->constant));
290
   }
291
 
292
   visit_list_elements(this, instructions);
293
 
294
   if (this->killed_all) {
295
      orig_acp->make_empty();
296
   }
297
 
298
   exec_list *new_kills = this->kills;
299
   this->kills = orig_kills;
300
   this->acp = orig_acp;
301
   this->killed_all = this->killed_all || orig_killed_all;
302
 
303
   foreach_iter(exec_list_iterator, iter, *new_kills) {
304
      kill_entry *k = (kill_entry *)iter.get();
305
      kill(k->var, k->write_mask);
306
   }
307
}
308
 
309
ir_visitor_status
310
ir_constant_propagation_visitor::visit_enter(ir_if *ir)
311
{
312
   ir->condition->accept(this);
313
   handle_rvalue(&ir->condition);
314
 
315
   handle_if_block(&ir->then_instructions);
316
   handle_if_block(&ir->else_instructions);
317
 
318
   /* handle_if_block() already descended into the children. */
319
   return visit_continue_with_parent;
320
}
321
 
322
ir_visitor_status
323
ir_constant_propagation_visitor::visit_enter(ir_loop *ir)
324
{
325
   exec_list *orig_acp = this->acp;
326
   exec_list *orig_kills = this->kills;
327
   bool orig_killed_all = this->killed_all;
328
 
329
   /* FINISHME: For now, the initial acp for loops is totally empty.
330
    * We could go through once, then go through again with the acp
331
    * cloned minus the killed entries after the first run through.
332
    */
333
   this->acp = new(mem_ctx) exec_list;
334
   this->kills = new(mem_ctx) exec_list;
335
   this->killed_all = false;
336
 
337
   visit_list_elements(this, &ir->body_instructions);
338
 
339
   if (this->killed_all) {
340
      orig_acp->make_empty();
341
   }
342
 
343
   exec_list *new_kills = this->kills;
344
   this->kills = orig_kills;
345
   this->acp = orig_acp;
346
   this->killed_all = this->killed_all || orig_killed_all;
347
 
348
   foreach_iter(exec_list_iterator, iter, *new_kills) {
349
      kill_entry *k = (kill_entry *)iter.get();
350
      kill(k->var, k->write_mask);
351
   }
352
 
353
   /* already descended into the children. */
354
   return visit_continue_with_parent;
355
}
356
 
357
void
358
ir_constant_propagation_visitor::kill(ir_variable *var, unsigned write_mask)
359
{
360
   assert(var != NULL);
361
 
362
   /* We don't track non-vectors. */
363
   if (!var->type->is_vector() && !var->type->is_scalar())
364
      return;
365
 
366
   /* Remove any entries currently in the ACP for this kill. */
367
   foreach_iter(exec_list_iterator, iter, *this->acp) {
368
      acp_entry *entry = (acp_entry *)iter.get();
369
 
370
      if (entry->var == var) {
371
	 entry->write_mask &= ~write_mask;
372
	 if (entry->write_mask == 0)
373
	    entry->remove();
374
      }
375
   }
376
 
377
   /* Add this writemask of the variable to the list of killed
378
    * variables in this block.
379
    */
380
   foreach_iter(exec_list_iterator, iter, *this->kills) {
381
      kill_entry *entry = (kill_entry *)iter.get();
382
 
383
      if (entry->var == var) {
384
	 entry->write_mask |= write_mask;
385
	 return;
386
      }
387
   }
388
   /* Not already in the list.  Make new entry. */
389
   this->kills->push_tail(new(this->mem_ctx) kill_entry(var, write_mask));
390
}
391
 
392
/**
393
 * Adds an entry to the available constant list if it's a plain assignment
394
 * of a variable to a variable.
395
 */
396
void
397
ir_constant_propagation_visitor::add_constant(ir_assignment *ir)
398
{
399
   acp_entry *entry;
400
 
401
   if (ir->condition) {
402
      ir_constant *condition = ir->condition->as_constant();
403
      if (!condition || !condition->value.b[0])
404
	 return;
405
   }
406
 
407
   if (!ir->write_mask)
408
      return;
409
 
410
   ir_dereference_variable *deref = ir->lhs->as_dereference_variable();
411
   ir_constant *constant = ir->rhs->as_constant();
412
 
413
   if (!deref || !constant)
414
      return;
415
 
416
   /* Only do constant propagation on vectors.  Constant matrices,
417
    * arrays, or structures would require more work elsewhere.
418
    */
419
   if (!deref->var->type->is_vector() && !deref->var->type->is_scalar())
420
      return;
421
 
422
   entry = new(this->mem_ctx) acp_entry(deref->var, ir->write_mask, constant);
423
   this->acp->push_tail(entry);
424
}
425
 
426
/**
427
 * Does a constant propagation pass on the code present in the instruction stream.
428
 */
429
bool
430
do_constant_propagation(exec_list *instructions)
431
{
432
   ir_constant_propagation_visitor v;
433
 
434
   visit_list_elements(&v, instructions);
435
 
436
   return v.progress;
437
}