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 glsl-recursion.c
26  * Verify that shaders containing static recursion are rejected.
27  *
28  * From page 44 (page 50 of the PDF) of the GLSL 1.20 spec:
29  *
30  *     "Recursion is not allowed, not even statically. Static recursion is
31  *     present if the static function call graph of the program contains
32  *     cycles."
33  *
34  * This langauge leaves a lot of questions unanswered.
35  *
36  *     - Is the error generated at compile-time or link-time?
37  *
38  *     - Is it an error to have a recursive function that is never statically
39  *       called by main or any function called directly or indirectly by main?
40  *       Technically speaking, such a function is not in the "static function
41  *       call graph of the program" at all.
42  *
43  * This set of tests checks for a variety of forms of recursion in shaders.
44  * Logs are dumped at both compile-time and link-time.  Errors are only
45  * checked at link time.  However, a compile error will also generate a link
46  * error (linking an uncompiled shader).
47  *
48  * \author Ian Romanick <ian.d.romanick@intel.com>
49  */
50 #include "piglit-util-gl.h"
51 
52 PIGLIT_GL_TEST_CONFIG_BEGIN
53 
54 	config.supports_gl_compat_version = 10;
55 
56 	config.window_visual = PIGLIT_GL_VISUAL_RGB | PIGLIT_GL_VISUAL_DOUBLE;
57 
58 PIGLIT_GL_TEST_CONFIG_END
59 
60 static const char simple_text[] =
61 	"#version 120\n"
62 	"int A(void) { return A(); }\n"
63 	"\n"
64 	"void main() {\n"
65 	"  A();\n"
66 	"  gl_Position = gl_Vertex;\n"
67 	"}"
68 	;
69 
70 static const char unreachable_text[] =
71 	"#version 120\n"
72 	"int A(void) { return A(); }\n"
73 	"\n"
74 	"void main() {\n"
75 	"  gl_Position = gl_Vertex;\n"
76 	"}"
77 	;
78 
79 static const char unreachable_opt_text[] =
80 	"#version 120\n"
81 	"int A(void) { return A(); }\n"
82 	"\n"
83 	"void main() {\n"
84 	"  if (false) A();\n"
85 	"  gl_Position = gl_Vertex;\n"
86 	"}"
87 	;
88 
89 static const char indirect_text[] =
90 	"#version 120\n"
91 	"int A(void);\n"
92 	"int B(void) { return A(); }\n"
93 	"int A(void) { return B(); }\n"
94 	"\n"
95 	"void main() {\n"
96 	"  A();\n"
97 	"  gl_Position = gl_Vertex;\n"
98 	"}"
99 	;
100 
101 static const char indirect_sep1_text[] =
102 	"#version 120\n"
103 	"int B(void);\n"
104 	"int A(void) { return B(); }\n"
105 	"\n"
106 	"void main() {\n"
107 	"  A();\n"
108 	"  gl_Position = gl_Vertex;\n"
109 	"}"
110 	;
111 
112 static const char indirect_sep2_text[] =
113 	"#version 120\n"
114 	"int A(void);\n"
115 	"int B(void) { return A(); }\n"
116 	;
117 
118 static const char indirect_complex_text[] =
119 	"#version 120\n"
120 	"int A(bool);\n"
121 	"int B(bool from_a) { if (!from_a) return A(true); return 0; }\n"
122 	"int A(bool from_b) { if (!from_b) return B(true); return 0; }\n"
123 	"\n"
124 	"void main() {\n"
125 	"  A(false);\n"
126 	"  B(false);\n"
127 	"  gl_Position = gl_Vertex;\n"
128 	"}"
129 	;
130 
131 static const char indirect_complex1_text[] =
132 	"#version 120\n"
133 	"int B(bool);\n"
134 	"int A(bool from_b) { if (!from_b) return B(true); return 0; }\n"
135 	"\n"
136 	"void main() {\n"
137 	"  A(false);\n"
138 	"  B(false);\n"
139 	"  gl_Position = gl_Vertex;\n"
140 	"}"
141 	;
142 
143 static const char indirect_complex2_text[] =
144 	"#version 120\n"
145 	"int A(bool);\n"
146 	"int B(bool from_a) { if (!from_a) return A(true); return 0; }\n"
147 	;
148 
149 struct test_vector {
150 	const char *name;
151 	const char *description;
152 	const char *shader_source[4];
153 };
154 
155 static const struct test_vector all_tests[] = {
156 	{
157 		"simple",
158 		"Trivial test of recursion.  main calls A, and A calls A.\n",
159 		{ simple_text, NULL }
160 	},
161 	{
162 		"unreachable",
163 		"Shader contains a function A that calls itself, but A is\n"
164 		"trivially unreachable from main.\n",
165 		{ unreachable_text, NULL }
166 	},
167 	{
168 		"unreachable-constant-folding",
169 		"Shader contains a function A that calls itself, but A is\n"
170 		"unreachable from main if a constant folding is performed\n"
171 		"before the check for recursion.\n",
172 		{ unreachable_opt_text, NULL }
173 	},
174 	{
175 		"indirect",
176 		"Trivial test of indirect recursion.  main calls A, A calls\n"
177 		"B, and B calls A.\n",
178 		{ indirect_text, NULL }
179 	},
180 	{
181 		"indirect-separate",
182 		"Trivial test of indirect recursion.  main calls A, A calls\n"
183 		"B, and B calls A.  A and B are in separate compilation\n"
184 		"units.\n",
185 		{ indirect_sep1_text, indirect_sep2_text, NULL }
186 	},
187 	{
188 		"indirect-complex",
189 		"Two functions A and B are statically mutually recursive,\n"
190 		"but the parameters passed to the functions ensure that no\n"
191 		"recursion actually occurs.  This is still an error.\n",
192 		{ indirect_complex_text, NULL }
193 	},
194 	{
195 		"indirect-complex-separate",
196 		"Two functions A and B are statically mutually recursive,\n"
197 		"but the parameters passed to the functions ensure that no\n"
198 		"recursion actually occurs.  This is still an error.  A and\n"
199 		"B are in separate compilation units.\n",
200 		{ indirect_complex1_text, indirect_complex2_text, NULL }
201 	},
202 };
203 
204 enum piglit_result
piglit_display(void)205 piglit_display(void)
206 {
207 	return PIGLIT_FAIL;
208 }
209 
210 bool
do_named_test(const char * name)211 do_named_test(const char *name)
212 {
213 	bool pass = true;
214 	unsigned i;
215 	unsigned j;
216 
217 	for (i = 0; i < ARRAY_SIZE(all_tests); i++) {
218 		GLint ok;
219 		GLuint prog;
220 		GLint size;
221 
222 		if (name != NULL && strcmp(name, all_tests[i].name) != 0)
223 			continue;
224 
225 		printf("Starting test \"%s\":\n", all_tests[i].name);
226 
227 		prog = glCreateProgram();
228 
229 		for (j = 0; all_tests[i].shader_source[j] != NULL; j++) {
230 			GLuint vs;
231 
232 			vs = glCreateShader(GL_VERTEX_SHADER);
233 			glShaderSource(vs, 1,
234 					    (const GLchar **)
235 					    & all_tests[i].shader_source[j],
236 					    NULL);
237 			glCompileShader(vs);
238 
239 			/* Some drivers return a size of 1 for an empty log.
240 			 * This is the size of a log that contains only a
241 			 * terminating NUL character.
242 			 */
243 			printf("Compilation info log for shader %u:\n", j);
244 			glGetShaderiv(vs, GL_INFO_LOG_LENGTH, &size);
245 			if (size > 1) {
246 				GLchar *info = malloc(size);
247 
248 				glGetShaderInfoLog(vs, size, NULL, info);
249 				printf("%s\n", info);
250 				free(info);
251 			} else {
252 				printf("<empty log>\n\n");
253 			}
254 
255 			glAttachShader(prog, vs);
256 			glDeleteShader(vs);
257 		}
258 
259 		glLinkProgram(prog);
260 
261 		/* Some drivers return a size of 1 for an empty log.  This is
262 		 * the size of a log that contains only a terminating NUL
263 		 * character.
264 		 */
265 		printf("Link info log:\n");
266 		glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &size);
267 		if (size > 1) {
268 			GLchar *info = malloc(size);
269 			glGetProgramInfoLog(prog, size, NULL, info);
270 			printf("%s\n", info);
271 			free(info);
272 		} else {
273 			printf("<empty log>\n\n");
274 		}
275 
276 		glGetProgramiv(prog, GL_LINK_STATUS, &ok);
277 		if (ok) {
278 			fprintf(stderr,
279 				"Shader with recursion compiled and linked, "
280 				"but it should have failed.\n");
281 			pass = false;
282 		}
283 		printf("Done with test \"%s\".\n\n", all_tests[i].name);
284 
285 		glDeleteProgram(prog);
286 
287 		if (name != NULL)
288 			break;
289 	}
290 
291 	return pass;
292 }
293 
294 void
piglit_init(int argc,char ** argv)295 piglit_init(int argc, char **argv)
296 {
297 	bool pass = true;
298 	const char *glsl_version_string;
299 
300 	piglit_require_vertex_shader();
301 
302 	glsl_version_string = (const char *)
303 		glGetString(GL_SHADING_LANGUAGE_VERSION);
304 	if (strtod(glsl_version_string, NULL) < 1.2) {
305 		printf("Requires GLSL 1.20 (have version `%s')\n",
306 		       glsl_version_string);
307 		piglit_report_result(PIGLIT_SKIP);
308 	}
309 
310 	if (argc == 1) {
311 		pass = do_named_test(NULL);
312 	} else {
313 		int i;
314 		for (i = 1; i < argc; i++) {
315 			pass = do_named_test(argv[i]) && pass;
316 		}
317 	}
318 
319 	piglit_report_result(pass ? PIGLIT_PASS : PIGLIT_FAIL);
320 }
321