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