• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2023 Alyssa Rosenzweig
3  * Copyright 2020 Intel Corporation
4  * SPDX-License-Identifier: MIT
5  */
6 
7 #include "compiler/clc/clc.h"
8 #include "util/hash_table.h"
9 #include "util/set.h"
10 #include "util/u_dynarray.h"
11 
12 #include <getopt.h>
13 #include <inttypes.h>
14 #include <stdio.h>
15 
16 /* Shader functions */
17 #define SPIR_V_MAGIC_NUMBER 0x07230203
18 
19 static void
msg_callback(void * priv,const char * msg)20 msg_callback(void *priv, const char *msg)
21 {
22    (void)priv;
23    fprintf(stderr, "%s", msg);
24 }
25 
26 static void
print_usage(char * exec_name,FILE * f)27 print_usage(char *exec_name, FILE *f)
28 {
29    fprintf(
30       f,
31       "Usage: %s [options] [input files] -- [clang args]\n"
32       "Options:\n"
33       "  -h  --help              Print this help.\n"
34       "  -o, --out <filename>    Specify the output filename.\n"
35       "  -v, --verbose           Print more information during compilation.\n",
36       exec_name);
37 }
38 
39 static uint32_t
get_module_spirv_version(const uint32_t * spirv,size_t size)40 get_module_spirv_version(const uint32_t *spirv, size_t size)
41 {
42    assert(size >= 8);
43    assert(spirv[0] == SPIR_V_MAGIC_NUMBER);
44    return spirv[1];
45 }
46 
47 static void
set_module_spirv_version(uint32_t * spirv,size_t size,uint32_t version)48 set_module_spirv_version(uint32_t *spirv, size_t size, uint32_t version)
49 {
50    assert(size >= 8);
51    assert(spirv[0] == SPIR_V_MAGIC_NUMBER);
52    spirv[1] = version;
53 }
54 
55 int
main(int argc,char ** argv)56 main(int argc, char **argv)
57 {
58    static struct option long_options[] = {
59       {"help", no_argument, 0, 'h'},
60       {"in", required_argument, 0, 'i'},
61       {"out", required_argument, 0, 'o'},
62       {"depfile", required_argument, 0, 'd'},
63       {"verbose", no_argument, 0, 'v'},
64       {0, 0, 0, 0},
65    };
66 
67    char *outfile = NULL, *depfile = NULL;
68    struct util_dynarray clang_args;
69    struct util_dynarray input_files;
70    struct util_dynarray spirv_objs;
71    struct util_dynarray spirv_ptr_objs;
72 
73    void *mem_ctx = ralloc_context(NULL);
74 
75    util_dynarray_init(&clang_args, mem_ctx);
76    util_dynarray_init(&input_files, mem_ctx);
77    util_dynarray_init(&spirv_objs, mem_ctx);
78    util_dynarray_init(&spirv_ptr_objs, mem_ctx);
79 
80    struct set *deps =
81       _mesa_set_create(mem_ctx, _mesa_hash_string, _mesa_key_string_equal);
82 
83    int ch;
84    while ((ch = getopt_long(argc, argv, "he:i:o:d:v", long_options, NULL)) !=
85           -1) {
86       switch (ch) {
87       case 'h':
88          print_usage(argv[0], stdout);
89          return 0;
90       case 'o':
91          outfile = optarg;
92          break;
93       case 'd':
94          depfile = optarg;
95          break;
96       default:
97          fprintf(stderr, "Unrecognized option \"%s\".\n", optarg);
98          print_usage(argv[0], stderr);
99          return 1;
100       }
101    }
102 
103    for (int i = optind; i < argc; i++) {
104       char *arg = argv[i];
105       bool option = arg[0] == '-';
106 
107       util_dynarray_append(option ? &clang_args : &input_files, char *, arg);
108    }
109 
110    /* Set the OpenCL standard to CL 2.0, this enables everything at a frontend
111     * level. See comment below about driver support.
112     */
113    util_dynarray_append(&clang_args, char *, "-cl-std=cl2.0");
114    util_dynarray_append(&clang_args, char *, "-D__OPENCL_VERSION__=200");
115 
116    if (util_dynarray_num_elements(&input_files, char *) == 0) {
117       fprintf(stderr, "No input file(s).\n");
118       print_usage(argv[0], stderr);
119       return -1;
120    }
121 
122    if (outfile == NULL) {
123       fprintf(stderr, "No output specified.\n");
124       print_usage(argv[0], stderr);
125       return -1;
126    }
127 
128    struct clc_logger logger = {
129       .error = msg_callback,
130       .warning = msg_callback,
131    };
132 
133    util_dynarray_foreach(&input_files, char *, infile) {
134       FILE *fp = fopen(*infile, "rb");
135       if (!fp) {
136          fprintf(stderr, "Failed to open %s\n", *infile);
137          ralloc_free(mem_ctx);
138          return 1;
139       }
140 
141       fseek(fp, 0L, SEEK_END);
142       size_t len = ftell(fp);
143       rewind(fp);
144 
145       char *map = ralloc_array_size(mem_ctx, 1, len + 1);
146       if (!map) {
147          fprintf(stderr, "Failed to allocate");
148          ralloc_free(mem_ctx);
149          return 1;
150       }
151 
152       fread(map, 1, len, fp);
153       map[len] = 0;
154       fclose(fp);
155 
156       struct clc_compile_args clc_args = {
157          .source.name = *infile,
158          .source.value = map,
159          .args = util_dynarray_begin(&clang_args),
160          .num_args = util_dynarray_num_elements(&clang_args, char *),
161       };
162 
163       /* Enable all features, we don't know the target here and it is the
164        * responsibility of the driver to only use features they will actually
165        * support. Not our job to blow up here.
166        */
167       memset(&clc_args.features, true, sizeof(clc_args.features));
168 
169       struct clc_binary *spirv_out =
170          util_dynarray_grow(&spirv_objs, struct clc_binary, 1);
171 
172       if (!clc_compile_c_to_spirv(&clc_args, &logger, spirv_out, deps)) {
173          ralloc_free(mem_ctx);
174          return 1;
175       }
176    }
177 
178 
179    util_dynarray_foreach(&spirv_objs, struct clc_binary, p) {
180       util_dynarray_append(&spirv_ptr_objs, struct clc_binary *, p);
181    }
182 
183    /* The SPIRV-Tools linker started checking that all modules have the same
184     * version. But SPIRV-LLVM-Translator picks the lower required version for
185     * each module it compiles. So we have to iterate over all of them and set
186     * the max found to make SPIRV-Tools link our modules.
187     *
188     * TODO: This is not the correct thing to do. We need SPIRV-LLVM-Translator
189     *       to pick a given SPIRV version given to it and have all the modules
190     *       at that version. We should remove this hack when this issue is
191     *       fixed :
192     *       https://github.com/KhronosGroup/SPIRV-LLVM-Translator/issues/1445
193     */
194    uint32_t max_spirv_version = 0;
195    util_dynarray_foreach(&spirv_ptr_objs, struct clc_binary *, module) {
196       max_spirv_version =
197          MAX2(max_spirv_version,
198               get_module_spirv_version((*module)->data, (*module)->size));
199    }
200 
201    assert(max_spirv_version > 0);
202    util_dynarray_foreach(&spirv_ptr_objs, struct clc_binary *, module) {
203       set_module_spirv_version((*module)->data, (*module)->size,
204                                max_spirv_version);
205    }
206 
207    struct clc_linker_args link_args = {
208       .in_objs = util_dynarray_begin(&spirv_ptr_objs),
209       .num_in_objs =
210          util_dynarray_num_elements(&spirv_ptr_objs, struct clc_binary *),
211       .create_library = true,
212    };
213    struct clc_binary final_spirv;
214    if (!clc_link_spirv(&link_args, &logger, &final_spirv)) {
215       ralloc_free(mem_ctx);
216       return 1;
217    }
218 
219    FILE *fp = fopen(outfile, "w");
220    fwrite(final_spirv.data, final_spirv.size, 1, fp);
221    fclose(fp);
222 
223    if (depfile) {
224       FILE *fp = fopen(depfile, "w");
225       fprintf(fp, "%s:", outfile);
226       set_foreach(deps, ent) {
227          fprintf(fp, " %s", (const char *)ent->key);
228       }
229       fprintf(fp, "\n");
230       fclose(fp);
231    }
232 
233    util_dynarray_foreach(&spirv_objs, struct clc_binary, p) {
234       clc_free_spirv(p);
235    }
236 
237    clc_free_spirv(&final_spirv);
238    ralloc_free(mem_ctx);
239 
240    return 0;
241 }
242