1/*
2 * Copyright © 2008, 2009 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#include <getopt.h>
24
25/** @file main.cpp
26 *
27 * This file is the main() routine and scaffolding for producing
28 * builtin_compiler (which doesn't include builtins itself and is used
29 * to generate the profile information for builtin_function.cpp), and
30 * for glsl_compiler (which does include builtins and can be used to
31 * offline compile GLSL code and examine the resulting GLSL IR.
32 */
33
34#include "ast.h"
35#include "glsl_parser_extras.h"
36#include "ir_optimization.h"
37#include "ir_print_visitor.h"
38#include "program.h"
39#include "loop_analysis.h"
40#include "standalone_scaffolding.h"
41
42static void
43initialize_context(struct gl_context *ctx, gl_api api)
44{
45   initialize_context_to_defaults(ctx, api);
46
47   /* The standalone compiler needs to claim support for almost
48    * everything in order to compile the built-in functions.
49    */
50   ctx->Const.GLSLVersion = 140;
51
52   ctx->Const.MaxClipPlanes = 8;
53   ctx->Const.MaxDrawBuffers = 2;
54
55   /* More than the 1.10 minimum to appease parser tests taken from
56    * apps that (hopefully) already checked the number of coords.
57    */
58   ctx->Const.MaxTextureCoordUnits = 4;
59
60   ctx->Driver.NewShader = _mesa_new_shader;
61}
62
63/* Returned string will have 'ctx' as its ralloc owner. */
64static char *
65load_text_file(void *ctx, const char *file_name)
66{
67	char *text = NULL;
68	size_t size;
69	size_t total_read = 0;
70	FILE *fp = fopen(file_name, "rb");
71
72	if (!fp) {
73		return NULL;
74	}
75
76	fseek(fp, 0L, SEEK_END);
77	size = ftell(fp);
78	fseek(fp, 0L, SEEK_SET);
79
80	text = (char *) ralloc_size(ctx, size + 1);
81	if (text != NULL) {
82		do {
83			size_t bytes = fread(text + total_read,
84					     1, size - total_read, fp);
85			if (bytes < size - total_read) {
86				free(text);
87				text = NULL;
88				break;
89			}
90
91			if (bytes == 0) {
92				break;
93			}
94
95			total_read += bytes;
96		} while (total_read < size);
97
98		text[total_read] = '\0';
99	}
100
101	fclose(fp);
102
103	return text;
104}
105
106int glsl_es = 0;
107int dump_ast = 0;
108int dump_hir = 0;
109int dump_lir = 0;
110int do_link = 0;
111
112const struct option compiler_opts[] = {
113   { "glsl-es",  0, &glsl_es,  1 },
114   { "dump-ast", 0, &dump_ast, 1 },
115   { "dump-hir", 0, &dump_hir, 1 },
116   { "dump-lir", 0, &dump_lir, 1 },
117   { "link",     0, &do_link,  1 },
118   { NULL, 0, NULL, 0 }
119};
120
121/**
122 * \brief Print proper usage and exit with failure.
123 */
124void
125usage_fail(const char *name)
126{
127
128   const char *header =
129      "usage: %s [options] <file.vert | file.geom | file.frag>\n"
130      "\n"
131      "Possible options are:\n";
132   printf(header, name, name);
133   for (const struct option *o = compiler_opts; o->name != 0; ++o) {
134      printf("    --%s\n", o->name);
135   }
136   exit(EXIT_FAILURE);
137}
138
139
140void
141compile_shader(struct gl_context *ctx, struct gl_shader *shader)
142{
143   struct _mesa_glsl_parse_state *state =
144      new(shader) _mesa_glsl_parse_state(ctx, shader->Type, shader);
145
146   const char *source = shader->Source;
147   state->error = glcpp_preprocess(state, &source, &state->info_log,
148			     state->extensions, ctx->API) != 0;
149
150   if (!state->error) {
151      _mesa_glsl_lexer_ctor(state, source);
152      _mesa_glsl_parse(state);
153      _mesa_glsl_lexer_dtor(state);
154   }
155
156   if (dump_ast) {
157      foreach_list_const(n, &state->translation_unit) {
158	 ast_node *ast = exec_node_data(ast_node, n, link);
159	 ast->print();
160      }
161      printf("\n\n");
162   }
163
164   shader->ir = new(shader) exec_list;
165   if (!state->error && !state->translation_unit.is_empty())
166      _mesa_ast_to_hir(shader->ir, state);
167
168   /* Print out the unoptimized IR. */
169   if (!state->error && dump_hir) {
170      validate_ir_tree(shader->ir);
171      _mesa_print_ir(shader->ir, state);
172   }
173
174   /* Optimization passes */
175   if (!state->error && !shader->ir->is_empty()) {
176      bool progress;
177      do {
178	 progress = do_common_optimization(shader->ir, false, false, 32);
179      } while (progress);
180
181      validate_ir_tree(shader->ir);
182   }
183
184
185   /* Print out the resulting IR */
186   if (!state->error && dump_lir) {
187      _mesa_print_ir(shader->ir, state);
188   }
189
190   shader->symbols = state->symbols;
191   shader->CompileStatus = !state->error;
192   shader->Version = state->language_version;
193   memcpy(shader->builtins_to_link, state->builtins_to_link,
194	  sizeof(shader->builtins_to_link[0]) * state->num_builtins_to_link);
195   shader->num_builtins_to_link = state->num_builtins_to_link;
196
197   if (shader->InfoLog)
198      ralloc_free(shader->InfoLog);
199
200   shader->InfoLog = state->info_log;
201
202   /* Retain any live IR, but trash the rest. */
203   reparent_ir(shader->ir, shader);
204
205   ralloc_free(state);
206
207   return;
208}
209
210int
211main(int argc, char **argv)
212{
213   int status = EXIT_SUCCESS;
214   struct gl_context local_ctx;
215   struct gl_context *ctx = &local_ctx;
216
217   int c;
218   int idx = 0;
219   while ((c = getopt_long(argc, argv, "", compiler_opts, &idx)) != -1)
220      /* empty */ ;
221
222
223   if (argc <= optind)
224      usage_fail(argv[0]);
225
226   initialize_context(ctx, (glsl_es) ? API_OPENGLES2 : API_OPENGL);
227
228   struct gl_shader_program *whole_program;
229
230   whole_program = rzalloc (NULL, struct gl_shader_program);
231   assert(whole_program != NULL);
232   whole_program->InfoLog = ralloc_strdup(whole_program, "");
233
234   for (/* empty */; argc > optind; optind++) {
235      whole_program->Shaders =
236	 reralloc(whole_program, whole_program->Shaders,
237		  struct gl_shader *, whole_program->NumShaders + 1);
238      assert(whole_program->Shaders != NULL);
239
240      struct gl_shader *shader = rzalloc(whole_program, gl_shader);
241
242      whole_program->Shaders[whole_program->NumShaders] = shader;
243      whole_program->NumShaders++;
244
245      const unsigned len = strlen(argv[optind]);
246      if (len < 6)
247	 usage_fail(argv[0]);
248
249      const char *const ext = & argv[optind][len - 5];
250      if (strncmp(".vert", ext, 5) == 0 || strncmp(".glsl", ext, 5) == 0)
251	 shader->Type = GL_VERTEX_SHADER;
252      else if (strncmp(".geom", ext, 5) == 0)
253	 shader->Type = GL_GEOMETRY_SHADER;
254      else if (strncmp(".frag", ext, 5) == 0)
255	 shader->Type = GL_FRAGMENT_SHADER;
256      else
257	 usage_fail(argv[0]);
258
259      shader->Source = load_text_file(whole_program, argv[optind]);
260      if (shader->Source == NULL) {
261	 printf("File \"%s\" does not exist.\n", argv[optind]);
262	 exit(EXIT_FAILURE);
263      }
264
265      compile_shader(ctx, shader);
266
267      if (!shader->CompileStatus) {
268	 printf("Info log for %s:\n%s\n", argv[optind], shader->InfoLog);
269	 status = EXIT_FAILURE;
270	 break;
271      }
272   }
273
274   if ((status == EXIT_SUCCESS) && do_link)  {
275      link_shaders(ctx, whole_program);
276      status = (whole_program->LinkStatus) ? EXIT_SUCCESS : EXIT_FAILURE;
277
278      if (strlen(whole_program->InfoLog) > 0)
279	 printf("Info log for linking:\n%s\n", whole_program->InfoLog);
280   }
281
282   for (unsigned i = 0; i < MESA_SHADER_TYPES; i++)
283      ralloc_free(whole_program->_LinkedShaders[i]);
284
285   ralloc_free(whole_program);
286   _mesa_glsl_release_types();
287   _mesa_glsl_release_functions();
288
289   return status;
290}
291