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 |
||
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 | }> |