• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright © 2011 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 /**
25  * \file ir_function_detect_recursion.cpp
26  * Determine whether a shader contains static recursion.
27  *
28  * Consider the (possibly disjoint) graph of function calls in a shader.  If a
29  * program contains recursion, this graph will contain a cycle.  If a function
30  * is part of a cycle, it will have a caller and it will have a callee (it
31  * calls another function).
32  *
33  * To detect recursion, the function call graph is constructed.  The graph is
34  * repeatedly reduced by removing any function that either has no callees
35  * (leaf functions) or has no caller.  Eventually the only functions that
36  * remain will be the functions in the cycles.
37  *
38  * The GLSL spec is a bit wishy-washy about recursion.
39  *
40  * From page 39 (page 45 of the PDF) of the GLSL 1.10 spec:
41  *
42  *     "Behavior is undefined if recursion is used. Recursion means having any
43  *     function appearing more than once at any one time in the run-time stack
44  *     of function calls. That is, a function may not call itself either
45  *     directly or indirectly. Compilers may give diagnostic messages when
46  *     this is detectable at compile time, but not all such cases can be
47  *     detected at compile time."
48  *
49  * From page 79 (page 85 of the PDF):
50  *
51  *     "22) Should recursion be supported?
52  *
53  *      DISCUSSION: Probably not necessary, but another example of limiting
54  *      the language based on how it would directly map to hardware. One
55  *      thought is that recursion would benefit ray tracing shaders. On the
56  *      other hand, many recursion operations can also be implemented with the
57  *      user managing the recursion through arrays. RenderMan doesn't support
58  *      recursion. This could be added at a later date, if it proved to be
59  *      necessary.
60  *
61  *      RESOLVED on September 10, 2002: Implementations are not required to
62  *      support recursion.
63  *
64  *      CLOSED on September 10, 2002."
65  *
66  * From page 79 (page 85 of the PDF):
67  *
68  *     "56) Is it an error for an implementation to support recursion if the
69  *     specification says recursion is not supported?
70  *
71  *     ADDED on September 10, 2002.
72  *
73  *     DISCUSSION: This issues is related to Issue (22). If we say that
74  *     recursion (or some other piece of functionality) is not supported, is
75  *     it an error for an implementation to support it? Perhaps the
76  *     specification should remain silent on these kind of things so that they
77  *     could be gracefully added later as an extension or as part of the
78  *     standard.
79  *
80  *     RESOLUTION: Languages, in general, have programs that are not
81  *     well-formed in ways a compiler cannot detect. Portability is only
82  *     ensured for well-formed programs. Detecting recursion is an example of
83  *     this. The language will say a well-formed program may not recurse, but
84  *     compilers are not forced to detect that recursion may happen.
85  *
86  *     CLOSED: November 29, 2002."
87  *
88  * In GLSL 1.10 the behavior of recursion is undefined.  Compilers don't have
89  * to reject shaders (at compile-time or link-time) that contain recursion.
90  * Instead they could work, or crash, or kill a kitten.
91  *
92  * From page 44 (page 50 of the PDF) of the GLSL 1.20 spec:
93  *
94  *     "Recursion is not allowed, not even statically. Static recursion is
95  *     present if the static function call graph of the program contains
96  *     cycles."
97  *
98  * This langauge clears things up a bit, but it still leaves a lot of
99  * questions unanswered.
100  *
101  *     - Is the error generated at compile-time or link-time?
102  *
103  *     - Is it an error to have a recursive function that is never statically
104  *       called by main or any function called directly or indirectly by main?
105  *       Technically speaking, such a function is not in the "static function
106  *       call graph of the program" at all.
107  *
108  * \bug
109  * If a shader has multiple cycles, this algorithm may erroneously complain
110  * about functions that aren't in any cycle, but are in the part of the call
111  * tree that connects them.  For example, if the call graph consists of a
112  * cycle between A and B, and a cycle between D and E, and B also calls C
113  * which calls D, then this algorithm will report C as a function which "has
114  * static recursion" even though it is not part of any cycle.
115  *
116  * A better algorithm for cycle detection that doesn't have this drawback can
117  * be found here:
118  *
119  * http://en.wikipedia.org/wiki/Tarjan%E2%80%99s_strongly_connected_components_algorithm
120  *
121  * \author Ian Romanick <ian.d.romanick@intel.com>
122  */
123 #include "main/core.h"
124 #include "ir.h"
125 #include "glsl_parser_extras.h"
126 #include "linker.h"
127 #include "program/hash_table.h"
128 #include "program.h"
129 
130 struct call_node : public exec_node {
131    class function *func;
132 };
133 
134 class function {
135 public:
function(ir_function_signature * sig)136    function(ir_function_signature *sig)
137       : sig(sig)
138    {
139       /* empty */
140    }
141 
142 
143    /* Callers of this ralloc-based new need not call delete. It's
144     * easier to just ralloc_free 'ctx' (or any of its ancestors). */
operator new(size_t size,void * ctx)145    static void* operator new(size_t size, void *ctx)
146    {
147       void *node;
148 
149       node = ralloc_size(ctx, size);
150       assert(node != NULL);
151 
152       return node;
153    }
154 
155    /* If the user *does* call delete, that's OK, we will just
156     * ralloc_free in that case. */
operator delete(void * node)157    static void operator delete(void *node)
158    {
159       ralloc_free(node);
160    }
161 
162    ir_function_signature *sig;
163 
164    /** List of functions called by this function. */
165    exec_list callees;
166 
167    /** List of functions that call this function. */
168    exec_list callers;
169 };
170 
171 class has_recursion_visitor : public ir_hierarchical_visitor {
172 public:
has_recursion_visitor()173    has_recursion_visitor()
174       : current(NULL)
175    {
176       progress = false;
177       this->mem_ctx = ralloc_context(NULL);
178       this->function_hash = hash_table_ctor(0, hash_table_pointer_hash,
179 					    hash_table_pointer_compare);
180    }
181 
~has_recursion_visitor()182    ~has_recursion_visitor()
183    {
184       hash_table_dtor(this->function_hash);
185       ralloc_free(this->mem_ctx);
186    }
187 
get_function(ir_function_signature * sig)188    function *get_function(ir_function_signature *sig)
189    {
190       function *f = (function *) hash_table_find(this->function_hash, sig);
191       if (f == NULL) {
192 	 f = new(mem_ctx) function(sig);
193 	 hash_table_insert(this->function_hash, f, sig);
194       }
195 
196       return f;
197    }
198 
visit_enter(ir_function_signature * sig)199    virtual ir_visitor_status visit_enter(ir_function_signature *sig)
200    {
201       this->current = this->get_function(sig);
202       return visit_continue;
203    }
204 
visit_leave(ir_function_signature * sig)205    virtual ir_visitor_status visit_leave(ir_function_signature *sig)
206    {
207       (void) sig;
208       this->current = NULL;
209       return visit_continue;
210    }
211 
visit_enter(ir_call * call)212    virtual ir_visitor_status visit_enter(ir_call *call)
213    {
214       /* At global scope this->current will be NULL.  Since there is no way to
215        * call global scope, it can never be part of a cycle.  Don't bother
216        * adding calls from global scope to the graph.
217        */
218       if (this->current == NULL)
219 	 return visit_continue;
220 
221       function *const target = this->get_function(call->callee);
222 
223       /* Create a link from the caller to the callee.
224        */
225       call_node *node = new(mem_ctx) call_node;
226       node->func = target;
227       this->current->callees.push_tail(node);
228 
229       /* Create a link from the callee to the caller.
230        */
231       node = new(mem_ctx) call_node;
232       node->func = this->current;
233       target->callers.push_tail(node);
234       return visit_continue;
235    }
236 
237    function *current;
238    struct hash_table *function_hash;
239    void *mem_ctx;
240    bool progress;
241 };
242 
243 static void
destroy_links(exec_list * list,function * f)244 destroy_links(exec_list *list, function *f)
245 {
246    foreach_list_safe(node, list) {
247       struct call_node *n = (struct call_node *) node;
248 
249       /* If this is the right function, remove it.  Note that the loop cannot
250        * terminate now.  There can be multiple links to a function if it is
251        * either called multiple times or calls multiple times.
252        */
253       if (n->func == f)
254 	 n->remove();
255    }
256 }
257 
258 
259 /**
260  * Remove a function if it has either no in or no out links
261  */
262 static void
remove_unlinked_functions(const void * key,void * data,void * closure)263 remove_unlinked_functions(const void *key, void *data, void *closure)
264 {
265    has_recursion_visitor *visitor = (has_recursion_visitor *) closure;
266    function *f = (function *) data;
267 
268    if (f->callers.is_empty() || f->callees.is_empty()) {
269       while (!f->callers.is_empty()) {
270 	 struct call_node *n = (struct call_node *) f->callers.pop_head();
271 	 destroy_links(& n->func->callees, f);
272       }
273 
274       while (!f->callees.is_empty()) {
275 	 struct call_node *n = (struct call_node *) f->callees.pop_head();
276 	 destroy_links(& n->func->callers, f);
277       }
278 
279       hash_table_remove(visitor->function_hash, key);
280       visitor->progress = true;
281    }
282 }
283 
284 
285 static void
emit_errors_unlinked(const void * key,void * data,void * closure)286 emit_errors_unlinked(const void *key, void *data, void *closure)
287 {
288    struct _mesa_glsl_parse_state *state =
289       (struct _mesa_glsl_parse_state *) closure;
290    function *f = (function *) data;
291    YYLTYPE loc;
292 
293    (void) key;
294 
295    char *proto = prototype_string(f->sig->return_type,
296 				  f->sig->function_name(),
297 				  &f->sig->parameters);
298 
299    memset(&loc, 0, sizeof(loc));
300    _mesa_glsl_error(&loc, state,
301 		    "function `%s' has static recursion.",
302 		    proto);
303    ralloc_free(proto);
304 }
305 
306 
307 static void
emit_errors_linked(const void * key,void * data,void * closure)308 emit_errors_linked(const void *key, void *data, void *closure)
309 {
310    struct gl_shader_program *prog =
311       (struct gl_shader_program *) closure;
312    function *f = (function *) data;
313 
314    (void) key;
315 
316    char *proto = prototype_string(f->sig->return_type,
317 				  f->sig->function_name(),
318 				  &f->sig->parameters);
319 
320    linker_error(prog, "function `%s' has static recursion.\n", proto);
321    ralloc_free(proto);
322    prog->LinkStatus = false;
323 }
324 
325 
326 void
detect_recursion_unlinked(struct _mesa_glsl_parse_state * state,exec_list * instructions)327 detect_recursion_unlinked(struct _mesa_glsl_parse_state *state,
328 			  exec_list *instructions)
329 {
330    has_recursion_visitor v;
331 
332    /* Collect all of the information about which functions call which other
333     * functions.
334     */
335    v.run(instructions);
336 
337    /* Remove from the set all of the functions that either have no caller or
338     * call no other functions.  Repeat until no functions are removed.
339     */
340    do {
341       v.progress = false;
342       hash_table_call_foreach(v.function_hash, remove_unlinked_functions, & v);
343    } while (v.progress);
344 
345 
346    /* At this point any functions still in the hash must be part of a cycle.
347     */
348    hash_table_call_foreach(v.function_hash, emit_errors_unlinked, state);
349 }
350 
351 
352 void
detect_recursion_linked(struct gl_shader_program * prog,exec_list * instructions)353 detect_recursion_linked(struct gl_shader_program *prog,
354 			exec_list *instructions)
355 {
356    has_recursion_visitor v;
357 
358    /* Collect all of the information about which functions call which other
359     * functions.
360     */
361    v.run(instructions);
362 
363    /* Remove from the set all of the functions that either have no caller or
364     * call no other functions.  Repeat until no functions are removed.
365     */
366    do {
367       v.progress = false;
368       hash_table_call_foreach(v.function_hash, remove_unlinked_functions, & v);
369    } while (v.progress);
370 
371 
372    /* At this point any functions still in the hash must be part of a cycle.
373     */
374    hash_table_call_foreach(v.function_hash, emit_errors_linked, prog);
375 }
376