1/* 2 * Copyright (C) 2021 Collabora, Ltd. 3 * Copyright (C) 2019 Ryan Houdek <Sonicadvance1@gmail.com> 4 * Copyright (C) 2014 Rob Clark <robclark@freedesktop.org> 5 * Copyright © 2015 Red Hat 6 * 7 * Permission is hereby granted, free of charge, to any person obtaining a 8 * copy of this software and associated documentation files (the "Software"), 9 * to deal in the Software without restriction, including without limitation 10 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 11 * and/or sell copies of the Software, and to permit persons to whom the 12 * Software is furnished to do so, subject to the following conditions: 13 * 14 * The above copyright notice and this permission notice (including the next 15 * paragraph) shall be included in all copies or substantial portions of the 16 * Software. 17 * 18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 21 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 * SOFTWARE. 25 */ 26 27#include <getopt.h> 28#include <string.h> 29#include "disassemble.h" 30#include "valhall/disassemble.h" 31#include "compiler.h" 32 33#include "main/mtypes.h" 34#include "compiler/glsl/standalone.h" 35#include "compiler/glsl/glsl_to_nir.h" 36#include "compiler/glsl/gl_nir.h" 37#include "compiler/nir_types.h" 38#include "util/u_dynarray.h" 39#include "bifrost_compile.h" 40 41unsigned gpu_id = 0x7212; 42int verbose = 0; 43 44static gl_shader_stage 45filename_to_stage(const char *stage) 46{ 47 const char *ext = strrchr(stage, '.'); 48 49 if (ext == NULL) { 50 fprintf(stderr, "No extension found in %s\n", stage); 51 exit(1); 52 } 53 54 if (!strcmp(ext, ".cs") || !strcmp(ext, ".comp")) 55 return MESA_SHADER_COMPUTE; 56 else if (!strcmp(ext, ".vs") || !strcmp(ext, ".vert")) 57 return MESA_SHADER_VERTEX; 58 else if (!strcmp(ext, ".fs") || !strcmp(ext, ".frag")) 59 return MESA_SHADER_FRAGMENT; 60 else { 61 fprintf(stderr, "Invalid extension %s\n", ext); 62 exit(1); 63 } 64 65 unreachable("Should've returned or bailed"); 66} 67 68static int 69st_packed_uniforms_type_size(const struct glsl_type *type, bool bindless) 70{ 71 return glsl_count_dword_slots(type, bindless); 72} 73 74static int 75glsl_type_size(const struct glsl_type *type, bool bindless) 76{ 77 return glsl_count_attribute_slots(type, false); 78} 79 80static void 81insert_sorted(struct exec_list *var_list, nir_variable *new_var) 82{ 83 nir_foreach_variable_in_list (var, var_list) { 84 if (var->data.location > new_var->data.location) { 85 exec_node_insert_node_before(&var->node, &new_var->node); 86 return; 87 } 88 } 89 exec_list_push_tail(var_list, &new_var->node); 90} 91 92static void 93sort_varyings(nir_shader *nir, nir_variable_mode mode) 94{ 95 struct exec_list new_list; 96 exec_list_make_empty(&new_list); 97 nir_foreach_variable_with_modes_safe (var, nir, mode) { 98 exec_node_remove(&var->node); 99 insert_sorted(&new_list, var); 100 } 101 exec_list_append(&nir->variables, &new_list); 102} 103 104static void 105fixup_varying_slots(nir_shader *nir, nir_variable_mode mode) 106{ 107 nir_foreach_variable_with_modes (var, nir, mode) { 108 if (var->data.location >= VARYING_SLOT_VAR0) { 109 var->data.location += 9; 110 } else if ((var->data.location >= VARYING_SLOT_TEX0) && 111 (var->data.location <= VARYING_SLOT_TEX7)) { 112 var->data.location += VARYING_SLOT_VAR0 - VARYING_SLOT_TEX0; 113 } 114 } 115} 116 117static void 118compile_shader(int stages, char **files) 119{ 120 struct gl_shader_program *prog; 121 nir_shader *nir[MESA_SHADER_COMPUTE + 1]; 122 unsigned shader_types[MESA_SHADER_COMPUTE + 1]; 123 124 if (stages > MESA_SHADER_COMPUTE) { 125 fprintf(stderr, "Too many stages"); 126 exit(1); 127 } 128 129 for (unsigned i = 0; i < stages; ++i) 130 shader_types[i] = filename_to_stage(files[i]); 131 132 struct standalone_options options = { 133 .glsl_version = 300, /* ES - needed for precision */ 134 .do_link = true, 135 .lower_precision = true 136 }; 137 138 static struct gl_context local_ctx; 139 140 prog = standalone_compile_shader(&options, stages, files, &local_ctx); 141 142 for (unsigned i = 0; i < stages; ++i) { 143 gl_shader_stage stage = shader_types[i]; 144 prog->_LinkedShaders[stage]->Program->info.stage = stage; 145 } 146 147 struct util_dynarray binary; 148 149 util_dynarray_init(&binary, NULL); 150 151 for (unsigned i = 0; i < stages; ++i) { 152 nir[i] = glsl_to_nir(&local_ctx.Const, prog, shader_types[i], &bifrost_nir_options); 153 154 if (shader_types[i] == MESA_SHADER_VERTEX) { 155 nir_assign_var_locations(nir[i], nir_var_shader_in, &nir[i]->num_inputs, 156 glsl_type_size); 157 sort_varyings(nir[i], nir_var_shader_out); 158 nir_assign_var_locations(nir[i], nir_var_shader_out, &nir[i]->num_outputs, 159 glsl_type_size); 160 fixup_varying_slots(nir[i], nir_var_shader_out); 161 } else if (shader_types[i] == MESA_SHADER_FRAGMENT) { 162 sort_varyings(nir[i], nir_var_shader_in); 163 nir_assign_var_locations(nir[i], nir_var_shader_in, &nir[i]->num_inputs, 164 glsl_type_size); 165 fixup_varying_slots(nir[i], nir_var_shader_in); 166 nir_assign_var_locations(nir[i], nir_var_shader_out, &nir[i]->num_outputs, 167 glsl_type_size); 168 } 169 170 nir_assign_var_locations(nir[i], nir_var_uniform, &nir[i]->num_uniforms, 171 glsl_type_size); 172 173 NIR_PASS_V(nir[i], nir_lower_global_vars_to_local); 174 NIR_PASS_V(nir[i], nir_lower_io_to_temporaries, nir_shader_get_entrypoint(nir[i]), true, i == 0); 175 NIR_PASS_V(nir[i], nir_opt_copy_prop_vars); 176 NIR_PASS_V(nir[i], nir_opt_combine_stores, nir_var_all); 177 178 NIR_PASS_V(nir[i], nir_lower_system_values); 179 NIR_PASS_V(nir[i], gl_nir_lower_samplers, prog); 180 NIR_PASS_V(nir[i], nir_split_var_copies); 181 NIR_PASS_V(nir[i], nir_lower_var_copies); 182 183 NIR_PASS_V(nir[i], nir_lower_io, nir_var_uniform, 184 st_packed_uniforms_type_size, 185 (nir_lower_io_options)0); 186 NIR_PASS_V(nir[i], nir_lower_uniforms_to_ubo, true, false); 187 188 /* before buffers and vars_to_ssa */ 189 NIR_PASS_V(nir[i], gl_nir_lower_images, true); 190 191 NIR_PASS_V(nir[i], gl_nir_lower_buffers, prog); 192 NIR_PASS_V(nir[i], nir_opt_constant_folding); 193 194 struct panfrost_compile_inputs inputs = { 195 .gpu_id = gpu_id, 196 .fixed_sysval_ubo = -1, 197 }; 198 struct pan_shader_info info = { 0 }; 199 200 util_dynarray_clear(&binary); 201 bifrost_compile_shader_nir(nir[i], &inputs, &binary, &info); 202 203 char *fn = NULL; 204 asprintf(&fn, "shader_%u.bin", i); 205 assert(fn != NULL); 206 FILE *fp = fopen(fn, "wb"); 207 fwrite(binary.data, 1, binary.size, fp); 208 fclose(fp); 209 free(fn); 210 } 211 212 util_dynarray_fini(&binary); 213} 214 215#define BI_FOURCC(ch0, ch1, ch2, ch3) ( \ 216 (uint32_t)(ch0) | (uint32_t)(ch1) << 8 | \ 217 (uint32_t)(ch2) << 16 | (uint32_t)(ch3) << 24) 218 219static void 220disassemble(const char *filename) 221{ 222 FILE *fp = fopen(filename, "rb"); 223 assert(fp); 224 225 fseek(fp, 0, SEEK_END); 226 unsigned filesize = ftell(fp); 227 rewind(fp); 228 229 uint32_t *code = malloc(filesize); 230 unsigned res = fread(code, 1, filesize, fp); 231 if (res != filesize) { 232 printf("Couldn't read full file\n"); 233 } 234 235 fclose(fp); 236 237 void *entrypoint = code; 238 239 if (filesize && code[0] == BI_FOURCC('M', 'B', 'S', '2')) { 240 for (int i = 0; i < filesize / 4; ++i) { 241 if (code[i] != BI_FOURCC('O', 'B', 'J', 'C')) 242 continue; 243 244 unsigned size = code[i + 1]; 245 unsigned offset = i + 2; 246 247 entrypoint = code + offset; 248 filesize = size; 249 } 250 } 251 252 if ((gpu_id >> 12) >= 9) 253 disassemble_valhall(stdout, entrypoint, filesize, verbose); 254 else 255 disassemble_bifrost(stdout, entrypoint, filesize, verbose); 256 257 free(code); 258} 259 260int 261main(int argc, char **argv) 262{ 263 int c; 264 265 if (argc < 2) { 266 printf("Pass a command\n"); 267 exit(1); 268 } 269 270 static struct option longopts[] = { 271 { "id", optional_argument, NULL, 'i' }, 272 { "gpu", optional_argument, NULL, 'g' }, 273 { "verbose", no_argument, &verbose, 'v' }, 274 { NULL, 0, NULL, 0 } 275 }; 276 277 static struct { 278 const char *name; 279 unsigned major, minor; 280 } gpus[] = { 281 { "G71", 6, 0 }, 282 { "G72", 6, 2 }, 283 { "G51", 7, 0 }, 284 { "G76", 7, 1 }, 285 { "G52", 7, 2 }, 286 { "G31", 7, 3 }, 287 { "G77", 9, 0 }, 288 { "G57", 9, 1 }, 289 { "G78", 9, 2 }, 290 { "G57", 9, 3 }, 291 { "G68", 9, 4 }, 292 { "G78AE", 9, 5 }, 293 }; 294 295 while ((c = getopt_long(argc, argv, "v:", longopts, NULL)) != -1) { 296 297 switch (c) { 298 case 'i': 299 gpu_id = atoi(optarg); 300 301 if (!gpu_id) { 302 fprintf(stderr, "Expected GPU ID, got %s\n", optarg); 303 return 1; 304 } 305 306 break; 307 case 'g': 308 gpu_id = 0; 309 310 /* Compatibility with the Arm compiler */ 311 if (strncmp(optarg, "Mali-", 5) == 0) optarg += 5; 312 313 for (unsigned i = 0; i < ARRAY_SIZE(gpus); ++i) { 314 if (strcmp(gpus[i].name, optarg)) continue; 315 316 unsigned major = gpus[i].major; 317 unsigned minor = gpus[i].minor; 318 319 gpu_id = (major << 12) | (minor << 8); 320 break; 321 } 322 323 if (!gpu_id) { 324 fprintf(stderr, "Unknown GPU %s\n", optarg); 325 return 1; 326 } 327 328 break; 329 default: 330 break; 331 } 332 } 333 334 if (strcmp(argv[optind], "compile") == 0) 335 compile_shader(argc - optind - 1, &argv[optind + 1]); 336 else if (strcmp(argv[optind], "disasm") == 0) 337 disassemble(argv[optind + 1]); 338 else { 339 fprintf(stderr, "Unknown command. Valid: compile/disasm\n"); 340 return 1; 341 } 342 343 return 0; 344} 345