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
 * 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 
25
#include 
26
#include 
27
 
28
#include "main/core.h"
29
#include "glsl_symbol_table.h"
30
#include "glsl_parser_extras.h"
31
#include "ir.h"
32
#include "program.h"
33
#include "program/hash_table.h"
34
#include "linker.h"
35
 
36
static ir_function_signature *
37
find_matching_signature(const char *name, const exec_list *actual_parameters,
38
			gl_shader **shader_list, unsigned num_shaders);
39
 
40
class call_link_visitor : public ir_hierarchical_visitor {
41
public:
42
   call_link_visitor(gl_shader_program *prog, gl_shader *linked,
43
		     gl_shader **shader_list, unsigned num_shaders)
44
   {
45
      this->prog = prog;
46
      this->shader_list = shader_list;
47
      this->num_shaders = num_shaders;
48
      this->success = true;
49
      this->linked = linked;
50
 
51
      this->locals = hash_table_ctor(0, hash_table_pointer_hash,
52
				     hash_table_pointer_compare);
53
   }
54
 
55
   ~call_link_visitor()
56
   {
57
      hash_table_dtor(this->locals);
58
   }
59
 
60
   virtual ir_visitor_status visit(ir_variable *ir)
61
   {
62
      hash_table_insert(locals, ir, ir);
63
      return visit_continue;
64
   }
65
 
66
   virtual ir_visitor_status visit_enter(ir_call *ir)
67
   {
68
      /* If ir is an ir_call from a function that was imported from another
69
       * shader callee will point to an ir_function_signature in the original
70
       * shader.  In this case the function signature MUST NOT BE MODIFIED.
71
       * Doing so will modify the original shader.  This may prevent that
72
       * shader from being linkable in other programs.
73
       */
74
      const ir_function_signature *const callee = ir->get_callee();
75
      assert(callee != NULL);
76
      const char *const name = callee->function_name();
77
 
78
      /* Determine if the requested function signature already exists in the
79
       * final linked shader.  If it does, use it as the target of the call.
80
       */
81
      ir_function_signature *sig =
82
	 find_matching_signature(name, &callee->parameters, &linked, 1);
83
      if (sig != NULL) {
84
	 ir->set_callee(sig);
85
	 return visit_continue;
86
      }
87
 
88
      /* Try to find the signature in one of the other shaders that is being
89
       * linked.  If it's not found there, return an error.
90
       */
91
      sig = find_matching_signature(name, &ir->actual_parameters, shader_list,
92
				    num_shaders);
93
      if (sig == NULL) {
94
	 /* FINISHME: Log the full signature of unresolved function.
95
	  */
96
	 linker_error_printf(this->prog, "unresolved reference to function "
97
			     "`%s'\n", name);
98
	 this->success = false;
99
	 return visit_stop;
100
      }
101
 
102
      /* Find the prototype information in the linked shader.  Generate any
103
       * details that may be missing.
104
       */
105
      ir_function *f = linked->symbols->get_function(name);
106
      if (f == NULL)
107
	 f = new(linked) ir_function(name);
108
 
109
      ir_function_signature *linked_sig =
110
	 f->exact_matching_signature(&callee->parameters);
111
      if (linked_sig == NULL) {
112
	 linked_sig = new(linked) ir_function_signature(callee->return_type);
113
	 f->add_signature(linked_sig);
114
      }
115
 
116
      /* At this point linked_sig and called may be the same.  If ir is an
117
       * ir_call from linked then linked_sig and callee will be
118
       * ir_function_signatures that have no definitions (is_defined is false).
119
       */
120
      assert(!linked_sig->is_defined);
121
      assert(linked_sig->body.is_empty());
122
 
123
      /* Create an in-place clone of the function definition.  This multistep
124
       * process introduces some complexity here, but it has some advantages.
125
       * The parameter list and the and function body are cloned separately.
126
       * The clone of the parameter list is used to prime the hashtable used
127
       * to replace variable references in the cloned body.
128
       *
129
       * The big advantage is that the ir_function_signature does not change.
130
       * This means that we don't have to process the rest of the IR tree to
131
       * patch ir_call nodes.  In addition, there is no way to remove or
132
       * replace signature stored in a function.  One could easily be added,
133
       * but this avoids the need.
134
       */
135
      struct hash_table *ht = hash_table_ctor(0, hash_table_pointer_hash,
136
					      hash_table_pointer_compare);
137
      exec_list formal_parameters;
138
      foreach_list_const(node, &sig->parameters) {
139
	 const ir_instruction *const original = (ir_instruction *) node;
140
	 assert(const_cast(original)->as_variable());
141
 
142
	 ir_instruction *copy = original->clone(linked, ht);
143
	 formal_parameters.push_tail(copy);
144
      }
145
 
146
      linked_sig->replace_parameters(&formal_parameters);
147
 
148
      foreach_list_const(node, &sig->body) {
149
	 const ir_instruction *const original = (ir_instruction *) node;
150
 
151
	 ir_instruction *copy = original->clone(linked, ht);
152
	 linked_sig->body.push_tail(copy);
153
      }
154
 
155
      linked_sig->is_defined = true;
156
      hash_table_dtor(ht);
157
 
158
      /* Patch references inside the function to things outside the function
159
       * (i.e., function calls and global variables).
160
       */
161
      linked_sig->accept(this);
162
 
163
      ir->set_callee(linked_sig);
164
 
165
      return visit_continue;
166
   }
167
 
168
   virtual ir_visitor_status visit(ir_dereference_variable *ir)
169
   {
170
      if (hash_table_find(locals, ir->var) == NULL) {
171
	 /* The non-function variable must be a global, so try to find the
172
	  * variable in the shader's symbol table.  If the variable is not
173
	  * found, then it's a global that *MUST* be defined in the original
174
	  * shader.
175
	  */
176
	 ir_variable *var = linked->symbols->get_variable(ir->var->name);
177
	 if (var == NULL) {
178
	    /* Clone the ir_variable that the dereference already has and add
179
	     * it to the linked shader.
180
	     */
181
	    var = ir->var->clone(linked, NULL);
182
	    linked->symbols->add_variable(var);
183
	    linked->ir->push_head(var);
184
	 } else if (var->type->is_array()) {
185
	    /* It is possible to have a global array declared in multiple
186
	     * shaders without a size.  The array is implicitly sized by the
187
	     * maximal access to it in *any* shader.  Because of this, we
188
	     * need to track the maximal access to the array as linking pulls
189
	     * more functions in that access the array.
190
	     */
191
	    var->max_array_access =
192
	       MAX2(var->max_array_access, ir->var->max_array_access);
193
 
194
	    if (var->type->length == 0 && ir->var->type->length != 0)
195
	       var->type = ir->var->type;
196
	 }
197
 
198
	 ir->var = var;
199
      }
200
 
201
      return visit_continue;
202
   }
203
 
204
   /** Was function linking successful? */
205
   bool success;
206
 
207
private:
208
   /**
209
    * Shader program being linked
210
    *
211
    * This is only used for logging error messages.
212
    */
213
   gl_shader_program *prog;
214
 
215
   /** List of shaders available for linking. */
216
   gl_shader **shader_list;
217
 
218
   /** Number of shaders available for linking. */
219
   unsigned num_shaders;
220
 
221
   /**
222
    * Final linked shader
223
    *
224
    * This is used two ways.  It is used to find global variables in the
225
    * linked shader that are accessed by the function.  It is also used to add
226
    * global variables from the shader where the function originated.
227
    */
228
   gl_shader *linked;
229
 
230
   /**
231
    * Table of variables local to the function.
232
    */
233
   hash_table *locals;
234
};
235
 
236
 
237
/**
238
 * Searches a list of shaders for a particular function definition
239
 */
240
ir_function_signature *
241
find_matching_signature(const char *name, const exec_list *actual_parameters,
242
			gl_shader **shader_list, unsigned num_shaders)
243
{
244
   for (unsigned i = 0; i < num_shaders; i++) {
245
      ir_function *const f = shader_list[i]->symbols->get_function(name);
246
 
247
      if (f == NULL)
248
	 continue;
249
 
250
      ir_function_signature *sig = f->matching_signature(actual_parameters);
251
 
252
      if ((sig == NULL) || !sig->is_defined)
253
	 continue;
254
 
255
      return sig;
256
   }
257
 
258
   return NULL;
259
}
260
 
261
 
262
bool
263
link_function_calls(gl_shader_program *prog, gl_shader *main,
264
		    gl_shader **shader_list, unsigned num_shaders)
265
{
266
   call_link_visitor v(prog, main, shader_list, num_shaders);
267
 
268
   v.run(main->ir);
269
   return v.success;
270
}