1bf215546Sopenharmony_ci/*
2bf215546Sopenharmony_ci * Copyright © 2022 Imagination Technologies Ltd.
3bf215546Sopenharmony_ci *
4bf215546Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a copy
5bf215546Sopenharmony_ci * of this software and associated documentation files (the "Software"), to deal
6bf215546Sopenharmony_ci * in the Software without restriction, including without limitation the rights
7bf215546Sopenharmony_ci * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8bf215546Sopenharmony_ci * copies of the Software, and to permit persons to whom the Software is
9bf215546Sopenharmony_ci * furnished to do so, subject to the following conditions:
10bf215546Sopenharmony_ci *
11bf215546Sopenharmony_ci * The above copyright notice and this permission notice (including the next
12bf215546Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the
13bf215546Sopenharmony_ci * Software.
14bf215546Sopenharmony_ci *
15bf215546Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16bf215546Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17bf215546Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18bf215546Sopenharmony_ci * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19bf215546Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20bf215546Sopenharmony_ci * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21bf215546Sopenharmony_ci * SOFTWARE.
22bf215546Sopenharmony_ci */
23bf215546Sopenharmony_ci
24bf215546Sopenharmony_ci#include "compiler/shader_enums.h"
25bf215546Sopenharmony_ci#include "nir/nir.h"
26bf215546Sopenharmony_ci#include "rogue.h"
27bf215546Sopenharmony_ci#include "rogue_build_data.h"
28bf215546Sopenharmony_ci#include "rogue_compiler.h"
29bf215546Sopenharmony_ci#include "rogue_dump.h"
30bf215546Sopenharmony_ci#include "util/os_file.h"
31bf215546Sopenharmony_ci#include "util/ralloc.h"
32bf215546Sopenharmony_ci
33bf215546Sopenharmony_ci#include <getopt.h>
34bf215546Sopenharmony_ci#include <limits.h>
35bf215546Sopenharmony_ci#include <stdbool.h>
36bf215546Sopenharmony_ci#include <stdio.h>
37bf215546Sopenharmony_ci#include <stdlib.h>
38bf215546Sopenharmony_ci#include <string.h>
39bf215546Sopenharmony_ci
40bf215546Sopenharmony_ci/* Number of hex columns to dump before starting a new line. */
41bf215546Sopenharmony_ci#define ARRAY_DUMP_COLS 16
42bf215546Sopenharmony_ci
43bf215546Sopenharmony_ci/**
44bf215546Sopenharmony_ci * \file compiler.c
45bf215546Sopenharmony_ci *
46bf215546Sopenharmony_ci * \brief Rogue offline compiler.
47bf215546Sopenharmony_ci */
48bf215546Sopenharmony_ci
49bf215546Sopenharmony_cistatic const struct option cmdline_opts[] = {
50bf215546Sopenharmony_ci   /* Arguments. */
51bf215546Sopenharmony_ci   { "stage", required_argument, NULL, 's' },
52bf215546Sopenharmony_ci   { "file", required_argument, NULL, 'f' },
53bf215546Sopenharmony_ci   { "entry", required_argument, NULL, 'e' },
54bf215546Sopenharmony_ci
55bf215546Sopenharmony_ci   /* Options. */
56bf215546Sopenharmony_ci   { "help", no_argument, NULL, 'h' },
57bf215546Sopenharmony_ci   { "out", required_argument, NULL, 'o' },
58bf215546Sopenharmony_ci
59bf215546Sopenharmony_ci   { "dump-c-array", no_argument, NULL, 'c' },
60bf215546Sopenharmony_ci   { "dump-rogue", no_argument, NULL, 'r' },
61bf215546Sopenharmony_ci   { "dump-nir", no_argument, NULL, 'n' },
62bf215546Sopenharmony_ci
63bf215546Sopenharmony_ci   { NULL, 0, NULL, 0 },
64bf215546Sopenharmony_ci};
65bf215546Sopenharmony_ci
66bf215546Sopenharmony_cistruct compiler_opts {
67bf215546Sopenharmony_ci   gl_shader_stage stage;
68bf215546Sopenharmony_ci   char *file;
69bf215546Sopenharmony_ci   char *entry;
70bf215546Sopenharmony_ci   char *out_file;
71bf215546Sopenharmony_ci   bool dump_c_array;
72bf215546Sopenharmony_ci   bool dump_rogue;
73bf215546Sopenharmony_ci   bool dump_nir;
74bf215546Sopenharmony_ci};
75bf215546Sopenharmony_ci
76bf215546Sopenharmony_cistatic void usage(const char *argv0)
77bf215546Sopenharmony_ci{
78bf215546Sopenharmony_ci   /* clang-format off */
79bf215546Sopenharmony_ci   printf("Rogue offline compiler.\n");
80bf215546Sopenharmony_ci   printf("Usage: %s -s <stage> -f <file> [-e <entry>] [-o <file>] [-c] [-r] [-n] [-h]\n", argv0);
81bf215546Sopenharmony_ci   printf("\n");
82bf215546Sopenharmony_ci
83bf215546Sopenharmony_ci   printf("Required arguments:\n");
84bf215546Sopenharmony_ci   printf("\t-s, --stage <stage> Shader stage (supported options: frag, vert).\n");
85bf215546Sopenharmony_ci   printf("\t-f, --file <file>   Shader SPIR-V filename.\n");
86bf215546Sopenharmony_ci   printf("\n");
87bf215546Sopenharmony_ci
88bf215546Sopenharmony_ci   printf("Options:\n");
89bf215546Sopenharmony_ci   printf("\t-h, --help          Prints this help message.\n");
90bf215546Sopenharmony_ci   printf("\t-e, --entry <entry> Overrides the shader entry-point name (default: 'main').\n");
91bf215546Sopenharmony_ci   printf("\t-o, --out <file>    Overrides the output filename (default: 'out.bin').\n");
92bf215546Sopenharmony_ci   printf("\n");
93bf215546Sopenharmony_ci
94bf215546Sopenharmony_ci   printf("\t-c, --dump-c-array  Print the shader binary as a C byte array.\n");
95bf215546Sopenharmony_ci   printf("\t-r, --dump-rogue    Prints the shader Rogue assembly.\n");
96bf215546Sopenharmony_ci   printf("\t-n, --dump-nir      Prints the shader NIR.\n");
97bf215546Sopenharmony_ci   printf("\n");
98bf215546Sopenharmony_ci   /* clang-format on */
99bf215546Sopenharmony_ci}
100bf215546Sopenharmony_ci
101bf215546Sopenharmony_cistatic bool parse_cmdline(int argc, char *argv[], struct compiler_opts *opts)
102bf215546Sopenharmony_ci{
103bf215546Sopenharmony_ci   int opt;
104bf215546Sopenharmony_ci   int longindex;
105bf215546Sopenharmony_ci
106bf215546Sopenharmony_ci   while (
107bf215546Sopenharmony_ci      (opt =
108bf215546Sopenharmony_ci          getopt_long(argc, argv, "crnhs:f:e:o:", cmdline_opts, &longindex)) !=
109bf215546Sopenharmony_ci      -1) {
110bf215546Sopenharmony_ci      switch (opt) {
111bf215546Sopenharmony_ci      case 'c':
112bf215546Sopenharmony_ci         opts->dump_c_array = true;
113bf215546Sopenharmony_ci         break;
114bf215546Sopenharmony_ci
115bf215546Sopenharmony_ci      case 'e':
116bf215546Sopenharmony_ci         if (opts->entry)
117bf215546Sopenharmony_ci            continue;
118bf215546Sopenharmony_ci
119bf215546Sopenharmony_ci         opts->entry = optarg;
120bf215546Sopenharmony_ci         break;
121bf215546Sopenharmony_ci
122bf215546Sopenharmony_ci      case 'f':
123bf215546Sopenharmony_ci         if (opts->file)
124bf215546Sopenharmony_ci            continue;
125bf215546Sopenharmony_ci
126bf215546Sopenharmony_ci         opts->file = optarg;
127bf215546Sopenharmony_ci         break;
128bf215546Sopenharmony_ci
129bf215546Sopenharmony_ci      case 'n':
130bf215546Sopenharmony_ci         opts->dump_nir = true;
131bf215546Sopenharmony_ci         break;
132bf215546Sopenharmony_ci
133bf215546Sopenharmony_ci      case 'o':
134bf215546Sopenharmony_ci         if (opts->out_file)
135bf215546Sopenharmony_ci            continue;
136bf215546Sopenharmony_ci
137bf215546Sopenharmony_ci         opts->out_file = optarg;
138bf215546Sopenharmony_ci         break;
139bf215546Sopenharmony_ci
140bf215546Sopenharmony_ci      case 'r':
141bf215546Sopenharmony_ci         opts->dump_rogue = true;
142bf215546Sopenharmony_ci         break;
143bf215546Sopenharmony_ci
144bf215546Sopenharmony_ci      case 's':
145bf215546Sopenharmony_ci         if (opts->stage != MESA_SHADER_NONE)
146bf215546Sopenharmony_ci            continue;
147bf215546Sopenharmony_ci
148bf215546Sopenharmony_ci         if (!strcmp(optarg, "frag"))
149bf215546Sopenharmony_ci            opts->stage = MESA_SHADER_FRAGMENT;
150bf215546Sopenharmony_ci         else if (!strcmp(optarg, "vert"))
151bf215546Sopenharmony_ci            opts->stage = MESA_SHADER_VERTEX;
152bf215546Sopenharmony_ci         else {
153bf215546Sopenharmony_ci            fprintf(stderr, "Invalid stage \"%s\".\n", optarg);
154bf215546Sopenharmony_ci            usage(argv[0]);
155bf215546Sopenharmony_ci            return false;
156bf215546Sopenharmony_ci         }
157bf215546Sopenharmony_ci
158bf215546Sopenharmony_ci         break;
159bf215546Sopenharmony_ci
160bf215546Sopenharmony_ci      case 'h':
161bf215546Sopenharmony_ci      default:
162bf215546Sopenharmony_ci         usage(argv[0]);
163bf215546Sopenharmony_ci         return false;
164bf215546Sopenharmony_ci      }
165bf215546Sopenharmony_ci   }
166bf215546Sopenharmony_ci
167bf215546Sopenharmony_ci   if (opts->stage == MESA_SHADER_NONE || !opts->file) {
168bf215546Sopenharmony_ci      fprintf(stderr,
169bf215546Sopenharmony_ci              "%s: --stage and --file are required arguments.\n",
170bf215546Sopenharmony_ci              argv[0]);
171bf215546Sopenharmony_ci      usage(argv[0]);
172bf215546Sopenharmony_ci      return false;
173bf215546Sopenharmony_ci   }
174bf215546Sopenharmony_ci
175bf215546Sopenharmony_ci   if (!opts->out_file)
176bf215546Sopenharmony_ci      opts->out_file = "out.bin";
177bf215546Sopenharmony_ci
178bf215546Sopenharmony_ci   if (!opts->entry)
179bf215546Sopenharmony_ci      opts->entry = "main";
180bf215546Sopenharmony_ci
181bf215546Sopenharmony_ci   return true;
182bf215546Sopenharmony_ci}
183bf215546Sopenharmony_ci
184bf215546Sopenharmony_ciint main(int argc, char *argv[])
185bf215546Sopenharmony_ci{
186bf215546Sopenharmony_ci   /* Command-line options. */
187bf215546Sopenharmony_ci   /* N.B. MESA_SHADER_NONE != 0 */
188bf215546Sopenharmony_ci   struct compiler_opts opts = { .stage = MESA_SHADER_NONE, 0 };
189bf215546Sopenharmony_ci
190bf215546Sopenharmony_ci   /* Input file data. */
191bf215546Sopenharmony_ci   char *input_data;
192bf215546Sopenharmony_ci   size_t input_size;
193bf215546Sopenharmony_ci
194bf215546Sopenharmony_ci   /* Compiler context. */
195bf215546Sopenharmony_ci   struct rogue_compiler *compiler;
196bf215546Sopenharmony_ci
197bf215546Sopenharmony_ci   /* Multi-stage build context. */
198bf215546Sopenharmony_ci   struct rogue_build_ctx *ctx;
199bf215546Sopenharmony_ci
200bf215546Sopenharmony_ci   /* Output file. */
201bf215546Sopenharmony_ci   FILE *fp;
202bf215546Sopenharmony_ci   size_t bytes_written;
203bf215546Sopenharmony_ci
204bf215546Sopenharmony_ci   /* Parse command-line options. */
205bf215546Sopenharmony_ci   if (!parse_cmdline(argc, argv, &opts))
206bf215546Sopenharmony_ci      return 1;
207bf215546Sopenharmony_ci
208bf215546Sopenharmony_ci   /* Load SPIR-V input file. */
209bf215546Sopenharmony_ci   input_data = os_read_file(opts.file, &input_size);
210bf215546Sopenharmony_ci   if (!input_data) {
211bf215546Sopenharmony_ci      fprintf(stderr, "Failed to read file \"%s\".\n", opts.file);
212bf215546Sopenharmony_ci      return 1;
213bf215546Sopenharmony_ci   }
214bf215546Sopenharmony_ci
215bf215546Sopenharmony_ci   /* Create compiler context. */
216bf215546Sopenharmony_ci   compiler = rogue_compiler_create(NULL);
217bf215546Sopenharmony_ci   if (!compiler) {
218bf215546Sopenharmony_ci      fprintf(stderr, "Failed to set up compiler context.\n");
219bf215546Sopenharmony_ci      goto err_free_input;
220bf215546Sopenharmony_ci   }
221bf215546Sopenharmony_ci
222bf215546Sopenharmony_ci   ctx = rogue_create_build_context(compiler);
223bf215546Sopenharmony_ci   if (!ctx) {
224bf215546Sopenharmony_ci      fprintf(stderr, "Failed to set up build context.\n");
225bf215546Sopenharmony_ci      goto err_destroy_compiler;
226bf215546Sopenharmony_ci   }
227bf215546Sopenharmony_ci
228bf215546Sopenharmony_ci   /* SPIR-V -> NIR. */
229bf215546Sopenharmony_ci   ctx->nir[opts.stage] = rogue_spirv_to_nir(ctx,
230bf215546Sopenharmony_ci                                             opts.stage,
231bf215546Sopenharmony_ci                                             opts.entry,
232bf215546Sopenharmony_ci                                             input_size / sizeof(uint32_t),
233bf215546Sopenharmony_ci                                             (uint32_t *)input_data,
234bf215546Sopenharmony_ci                                             0,
235bf215546Sopenharmony_ci                                             NULL);
236bf215546Sopenharmony_ci   if (!ctx->nir[opts.stage]) {
237bf215546Sopenharmony_ci      fprintf(stderr, "Failed to translate SPIR-V input to NIR.\n");
238bf215546Sopenharmony_ci      goto err_free_build_context;
239bf215546Sopenharmony_ci   }
240bf215546Sopenharmony_ci
241bf215546Sopenharmony_ci   /* Dump NIR shader. */
242bf215546Sopenharmony_ci   if (opts.dump_nir)
243bf215546Sopenharmony_ci      nir_print_shader(ctx->nir[opts.stage], stdout);
244bf215546Sopenharmony_ci
245bf215546Sopenharmony_ci   /* NIR -> Rogue. */
246bf215546Sopenharmony_ci   ctx->rogue[opts.stage] = rogue_nir_to_rogue(ctx, ctx->nir[opts.stage]);
247bf215546Sopenharmony_ci   if (!ctx->rogue[opts.stage]) {
248bf215546Sopenharmony_ci      fprintf(stderr, "Failed to translate NIR input to Rogue.\n");
249bf215546Sopenharmony_ci      goto err_free_build_context;
250bf215546Sopenharmony_ci   }
251bf215546Sopenharmony_ci
252bf215546Sopenharmony_ci   /* Dump Rogue shader. */
253bf215546Sopenharmony_ci   if (opts.dump_rogue)
254bf215546Sopenharmony_ci      rogue_dump_shader(ctx->rogue[opts.stage], stdout);
255bf215546Sopenharmony_ci
256bf215546Sopenharmony_ci   /* Rogue -> Binary. */
257bf215546Sopenharmony_ci   ctx->binary[opts.stage] = rogue_to_binary(ctx, ctx->rogue[opts.stage]);
258bf215546Sopenharmony_ci   if (!ctx->binary[opts.stage]) {
259bf215546Sopenharmony_ci      fprintf(stderr, "Failed to translate Rogue to binary.\n");
260bf215546Sopenharmony_ci      goto err_free_build_context;
261bf215546Sopenharmony_ci   }
262bf215546Sopenharmony_ci
263bf215546Sopenharmony_ci   /* Dump binary as a C array. */
264bf215546Sopenharmony_ci   if (opts.dump_c_array) {
265bf215546Sopenharmony_ci      printf("uint8_t shader_bytes[%zu] = {", ctx->binary[opts.stage]->size);
266bf215546Sopenharmony_ci      for (size_t u = 0U; u < ctx->binary[opts.stage]->size; ++u) {
267bf215546Sopenharmony_ci         if (!(u % ARRAY_DUMP_COLS))
268bf215546Sopenharmony_ci            printf("\n\t");
269bf215546Sopenharmony_ci
270bf215546Sopenharmony_ci         printf("0x%02x, ", ctx->binary[opts.stage]->data[u]);
271bf215546Sopenharmony_ci      }
272bf215546Sopenharmony_ci      printf("\n};\n");
273bf215546Sopenharmony_ci   }
274bf215546Sopenharmony_ci
275bf215546Sopenharmony_ci   /* Write shader binary to disk. */
276bf215546Sopenharmony_ci   fp = fopen(opts.out_file, "wb");
277bf215546Sopenharmony_ci   if (!fp) {
278bf215546Sopenharmony_ci      fprintf(stderr, "Failed to open output file \"%s\".\n", opts.out_file);
279bf215546Sopenharmony_ci      goto err_free_build_context;
280bf215546Sopenharmony_ci   }
281bf215546Sopenharmony_ci
282bf215546Sopenharmony_ci   bytes_written = fwrite(ctx->binary[opts.stage]->data,
283bf215546Sopenharmony_ci                          1,
284bf215546Sopenharmony_ci                          ctx->binary[opts.stage]->size,
285bf215546Sopenharmony_ci                          fp);
286bf215546Sopenharmony_ci   if (bytes_written != ctx->binary[opts.stage]->size) {
287bf215546Sopenharmony_ci      fprintf(
288bf215546Sopenharmony_ci         stderr,
289bf215546Sopenharmony_ci         "Failed to write to output file \"%s\" (%zu bytes of %zu written).\n",
290bf215546Sopenharmony_ci         opts.out_file,
291bf215546Sopenharmony_ci         bytes_written,
292bf215546Sopenharmony_ci         ctx->binary[opts.stage]->size);
293bf215546Sopenharmony_ci      goto err_close_outfile;
294bf215546Sopenharmony_ci   }
295bf215546Sopenharmony_ci
296bf215546Sopenharmony_ci   /* Clean up. */
297bf215546Sopenharmony_ci   fclose(fp);
298bf215546Sopenharmony_ci   ralloc_free(ctx);
299bf215546Sopenharmony_ci   rogue_compiler_destroy(compiler);
300bf215546Sopenharmony_ci   free(input_data);
301bf215546Sopenharmony_ci
302bf215546Sopenharmony_ci   return 0;
303bf215546Sopenharmony_ci
304bf215546Sopenharmony_cierr_close_outfile:
305bf215546Sopenharmony_ci   fclose(fp);
306bf215546Sopenharmony_cierr_free_build_context:
307bf215546Sopenharmony_ci   ralloc_free(ctx);
308bf215546Sopenharmony_cierr_destroy_compiler:
309bf215546Sopenharmony_ci   rogue_compiler_destroy(compiler);
310bf215546Sopenharmony_cierr_free_input:
311bf215546Sopenharmony_ci   free(input_data);
312bf215546Sopenharmony_ci
313bf215546Sopenharmony_ci   return 1;
314bf215546Sopenharmony_ci}
315