1bf215546Sopenharmony_ci/*
2bf215546Sopenharmony_ci * Copyright © 2010 Intel Corporation
3bf215546Sopenharmony_ci *
4bf215546Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
5bf215546Sopenharmony_ci * copy of this software and associated documentation files (the "Software"),
6bf215546Sopenharmony_ci * to deal in the Software without restriction, including without limitation
7bf215546Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8bf215546Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the
9bf215546Sopenharmony_ci * Software is furnished to do so, subject to the following conditions:
10bf215546Sopenharmony_ci *
11bf215546Sopenharmony_ci * The above copyright notice and this permission notice (including the next
12bf215546Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the
13bf215546Sopenharmony_ci * Software.
14bf215546Sopenharmony_ci *
15bf215546Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16bf215546Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17bf215546Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18bf215546Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19bf215546Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20bf215546Sopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21bf215546Sopenharmony_ci * DEALINGS IN THE SOFTWARE.
22bf215546Sopenharmony_ci */
23bf215546Sopenharmony_ci
24bf215546Sopenharmony_ci/**
25bf215546Sopenharmony_ci * \file opt_constant_variable.cpp
26bf215546Sopenharmony_ci *
27bf215546Sopenharmony_ci * Marks variables assigned a single constant value over the course
28bf215546Sopenharmony_ci * of the program as constant.
29bf215546Sopenharmony_ci *
30bf215546Sopenharmony_ci * The goal here is to trigger further constant folding and then dead
31bf215546Sopenharmony_ci * code elimination.  This is common with vector/matrix constructors
32bf215546Sopenharmony_ci * and calls to builtin functions.
33bf215546Sopenharmony_ci */
34bf215546Sopenharmony_ci
35bf215546Sopenharmony_ci#include "ir.h"
36bf215546Sopenharmony_ci#include "ir_visitor.h"
37bf215546Sopenharmony_ci#include "ir_optimization.h"
38bf215546Sopenharmony_ci#include "compiler/glsl_types.h"
39bf215546Sopenharmony_ci#include "util/hash_table.h"
40bf215546Sopenharmony_ci
41bf215546Sopenharmony_cinamespace {
42bf215546Sopenharmony_ci
43bf215546Sopenharmony_cistruct assignment_entry {
44bf215546Sopenharmony_ci   int assignment_count;
45bf215546Sopenharmony_ci   ir_variable *var;
46bf215546Sopenharmony_ci   ir_constant *constval;
47bf215546Sopenharmony_ci   bool our_scope;
48bf215546Sopenharmony_ci};
49bf215546Sopenharmony_ci
50bf215546Sopenharmony_ciclass ir_constant_variable_visitor : public ir_hierarchical_visitor {
51bf215546Sopenharmony_cipublic:
52bf215546Sopenharmony_ci   using ir_hierarchical_visitor::visit;
53bf215546Sopenharmony_ci   using ir_hierarchical_visitor::visit_enter;
54bf215546Sopenharmony_ci
55bf215546Sopenharmony_ci   virtual ir_visitor_status visit_enter(ir_dereference_variable *);
56bf215546Sopenharmony_ci   virtual ir_visitor_status visit(ir_variable *);
57bf215546Sopenharmony_ci   virtual ir_visitor_status visit_enter(ir_assignment *);
58bf215546Sopenharmony_ci   virtual ir_visitor_status visit_enter(ir_call *);
59bf215546Sopenharmony_ci
60bf215546Sopenharmony_ci   struct hash_table *ht;
61bf215546Sopenharmony_ci};
62bf215546Sopenharmony_ci
63bf215546Sopenharmony_ci} /* unnamed namespace */
64bf215546Sopenharmony_ci
65bf215546Sopenharmony_cistatic struct assignment_entry *
66bf215546Sopenharmony_ciget_assignment_entry(ir_variable *var, struct hash_table *ht)
67bf215546Sopenharmony_ci{
68bf215546Sopenharmony_ci   struct hash_entry *hte = _mesa_hash_table_search(ht, var);
69bf215546Sopenharmony_ci   struct assignment_entry *entry;
70bf215546Sopenharmony_ci
71bf215546Sopenharmony_ci   if (hte) {
72bf215546Sopenharmony_ci      entry = (struct assignment_entry *) hte->data;
73bf215546Sopenharmony_ci   } else {
74bf215546Sopenharmony_ci      entry = (struct assignment_entry *) calloc(1, sizeof(*entry));
75bf215546Sopenharmony_ci      entry->var = var;
76bf215546Sopenharmony_ci      _mesa_hash_table_insert(ht, var, entry);
77bf215546Sopenharmony_ci   }
78bf215546Sopenharmony_ci
79bf215546Sopenharmony_ci   return entry;
80bf215546Sopenharmony_ci}
81bf215546Sopenharmony_ci
82bf215546Sopenharmony_ciir_visitor_status
83bf215546Sopenharmony_ciir_constant_variable_visitor::visit(ir_variable *ir)
84bf215546Sopenharmony_ci{
85bf215546Sopenharmony_ci   struct assignment_entry *entry = get_assignment_entry(ir, this->ht);
86bf215546Sopenharmony_ci   entry->our_scope = true;
87bf215546Sopenharmony_ci   return visit_continue;
88bf215546Sopenharmony_ci}
89bf215546Sopenharmony_ci
90bf215546Sopenharmony_ci/* Skip derefs of variables so that we can detect declarations. */
91bf215546Sopenharmony_ciir_visitor_status
92bf215546Sopenharmony_ciir_constant_variable_visitor::visit_enter(ir_dereference_variable *ir)
93bf215546Sopenharmony_ci{
94bf215546Sopenharmony_ci   (void)ir;
95bf215546Sopenharmony_ci   return visit_continue_with_parent;
96bf215546Sopenharmony_ci}
97bf215546Sopenharmony_ci
98bf215546Sopenharmony_ciir_visitor_status
99bf215546Sopenharmony_ciir_constant_variable_visitor::visit_enter(ir_assignment *ir)
100bf215546Sopenharmony_ci{
101bf215546Sopenharmony_ci   ir_constant *constval;
102bf215546Sopenharmony_ci   struct assignment_entry *entry;
103bf215546Sopenharmony_ci
104bf215546Sopenharmony_ci   entry = get_assignment_entry(ir->lhs->variable_referenced(), this->ht);
105bf215546Sopenharmony_ci   assert(entry);
106bf215546Sopenharmony_ci   entry->assignment_count++;
107bf215546Sopenharmony_ci
108bf215546Sopenharmony_ci   /* If there's more than one assignment, don't bother - we won't do anything
109bf215546Sopenharmony_ci    * with this variable anyway, and continuing just wastes memory cloning
110bf215546Sopenharmony_ci    * constant expressions.
111bf215546Sopenharmony_ci    */
112bf215546Sopenharmony_ci   if (entry->assignment_count > 1)
113bf215546Sopenharmony_ci      return visit_continue;
114bf215546Sopenharmony_ci
115bf215546Sopenharmony_ci   /* If it's already constant, don't do the work. */
116bf215546Sopenharmony_ci   if (entry->var->constant_value)
117bf215546Sopenharmony_ci      return visit_continue;
118bf215546Sopenharmony_ci
119bf215546Sopenharmony_ci   ir_variable *var = ir->whole_variable_written();
120bf215546Sopenharmony_ci   if (!var)
121bf215546Sopenharmony_ci      return visit_continue;
122bf215546Sopenharmony_ci
123bf215546Sopenharmony_ci   /* Ignore buffer variables, since the underlying storage is shared
124bf215546Sopenharmony_ci    * and we can't be sure that this variable won't be written by another
125bf215546Sopenharmony_ci    * thread.
126bf215546Sopenharmony_ci    */
127bf215546Sopenharmony_ci   if (var->data.mode == ir_var_shader_storage ||
128bf215546Sopenharmony_ci       var->data.mode == ir_var_shader_shared)
129bf215546Sopenharmony_ci      return visit_continue;
130bf215546Sopenharmony_ci
131bf215546Sopenharmony_ci   constval = ir->rhs->constant_expression_value(ralloc_parent(ir));
132bf215546Sopenharmony_ci   if (!constval)
133bf215546Sopenharmony_ci      return visit_continue;
134bf215546Sopenharmony_ci
135bf215546Sopenharmony_ci   /* Mark this entry as having a constant assignment (if the
136bf215546Sopenharmony_ci    * assignment count doesn't go >1).  do_constant_variable will fix
137bf215546Sopenharmony_ci    * up the variable with the constant value later.
138bf215546Sopenharmony_ci    */
139bf215546Sopenharmony_ci   entry->constval = constval;
140bf215546Sopenharmony_ci
141bf215546Sopenharmony_ci   return visit_continue;
142bf215546Sopenharmony_ci}
143bf215546Sopenharmony_ci
144bf215546Sopenharmony_ciir_visitor_status
145bf215546Sopenharmony_ciir_constant_variable_visitor::visit_enter(ir_call *ir)
146bf215546Sopenharmony_ci{
147bf215546Sopenharmony_ci   /* Mark any out parameters as assigned to */
148bf215546Sopenharmony_ci   foreach_two_lists(formal_node, &ir->callee->parameters,
149bf215546Sopenharmony_ci                     actual_node, &ir->actual_parameters) {
150bf215546Sopenharmony_ci      ir_rvalue *param_rval = (ir_rvalue *) actual_node;
151bf215546Sopenharmony_ci      ir_variable *param = (ir_variable *) formal_node;
152bf215546Sopenharmony_ci
153bf215546Sopenharmony_ci      if (param->data.mode == ir_var_function_out ||
154bf215546Sopenharmony_ci	  param->data.mode == ir_var_function_inout) {
155bf215546Sopenharmony_ci	 ir_variable *var = param_rval->variable_referenced();
156bf215546Sopenharmony_ci	 struct assignment_entry *entry;
157bf215546Sopenharmony_ci
158bf215546Sopenharmony_ci	 assert(var);
159bf215546Sopenharmony_ci	 entry = get_assignment_entry(var, this->ht);
160bf215546Sopenharmony_ci	 entry->assignment_count++;
161bf215546Sopenharmony_ci      }
162bf215546Sopenharmony_ci
163bf215546Sopenharmony_ci      /* We don't know if the variable passed to this function has been
164bf215546Sopenharmony_ci       * assigned a value or if it is undefined, so for now we always assume
165bf215546Sopenharmony_ci       * it has been assigned a value. Once functions have been inlined any
166bf215546Sopenharmony_ci       * further potential optimisations will be taken care of.
167bf215546Sopenharmony_ci       */
168bf215546Sopenharmony_ci      struct assignment_entry *entry;
169bf215546Sopenharmony_ci      entry = get_assignment_entry(param, this->ht);
170bf215546Sopenharmony_ci      entry->assignment_count++;
171bf215546Sopenharmony_ci   }
172bf215546Sopenharmony_ci
173bf215546Sopenharmony_ci   /* Mark the return storage as having been assigned to */
174bf215546Sopenharmony_ci   if (ir->return_deref != NULL) {
175bf215546Sopenharmony_ci      ir_variable *var = ir->return_deref->variable_referenced();
176bf215546Sopenharmony_ci      struct assignment_entry *entry;
177bf215546Sopenharmony_ci
178bf215546Sopenharmony_ci      assert(var);
179bf215546Sopenharmony_ci      entry = get_assignment_entry(var, this->ht);
180bf215546Sopenharmony_ci      entry->assignment_count++;
181bf215546Sopenharmony_ci   }
182bf215546Sopenharmony_ci
183bf215546Sopenharmony_ci   return visit_continue;
184bf215546Sopenharmony_ci}
185bf215546Sopenharmony_ci
186bf215546Sopenharmony_ci/**
187bf215546Sopenharmony_ci * Does a copy propagation pass on the code present in the instruction stream.
188bf215546Sopenharmony_ci */
189bf215546Sopenharmony_cibool
190bf215546Sopenharmony_cido_constant_variable(exec_list *instructions)
191bf215546Sopenharmony_ci{
192bf215546Sopenharmony_ci   bool progress = false;
193bf215546Sopenharmony_ci   ir_constant_variable_visitor v;
194bf215546Sopenharmony_ci
195bf215546Sopenharmony_ci   v.ht = _mesa_pointer_hash_table_create(NULL);
196bf215546Sopenharmony_ci   v.run(instructions);
197bf215546Sopenharmony_ci
198bf215546Sopenharmony_ci   hash_table_foreach(v.ht, hte) {
199bf215546Sopenharmony_ci      struct assignment_entry *entry = (struct assignment_entry *) hte->data;
200bf215546Sopenharmony_ci
201bf215546Sopenharmony_ci      if (entry->assignment_count == 1 && entry->constval && entry->our_scope) {
202bf215546Sopenharmony_ci	 entry->var->constant_value = entry->constval;
203bf215546Sopenharmony_ci	 progress = true;
204bf215546Sopenharmony_ci      }
205bf215546Sopenharmony_ci      hte->data = NULL;
206bf215546Sopenharmony_ci      free(entry);
207bf215546Sopenharmony_ci   }
208bf215546Sopenharmony_ci   _mesa_hash_table_destroy(v.ht, NULL);
209bf215546Sopenharmony_ci
210bf215546Sopenharmony_ci   return progress;
211bf215546Sopenharmony_ci}
212bf215546Sopenharmony_ci
213bf215546Sopenharmony_cibool
214bf215546Sopenharmony_cido_constant_variable_unlinked(exec_list *instructions)
215bf215546Sopenharmony_ci{
216bf215546Sopenharmony_ci   bool progress = false;
217bf215546Sopenharmony_ci
218bf215546Sopenharmony_ci   foreach_in_list(ir_instruction, ir, instructions) {
219bf215546Sopenharmony_ci      ir_function *f = ir->as_function();
220bf215546Sopenharmony_ci      if (f) {
221bf215546Sopenharmony_ci	 foreach_in_list(ir_function_signature, sig, &f->signatures) {
222bf215546Sopenharmony_ci	    if (do_constant_variable(&sig->body))
223bf215546Sopenharmony_ci	       progress = true;
224bf215546Sopenharmony_ci	 }
225bf215546Sopenharmony_ci      }
226bf215546Sopenharmony_ci   }
227bf215546Sopenharmony_ci
228bf215546Sopenharmony_ci   return progress;
229bf215546Sopenharmony_ci}
230