build.c (167857B)
1 /* See LICENSE for license details. */ 2 /* NOTE: inspired by nob: https://github.com/tsoding/nob.h */ 3 4 /* TODO(rnp): 5 * [ ]: refactor: unify struct type paths 6 * - struct printing can do a stack traversal for sub types 7 * but it should not have other branching 8 * - basically we should flatten structs into a base type 9 * similar to ornot where we know the size of everything 10 * and all names are fully resolved 11 * [ ]: refactor: allow @Expand to come before the table definition 12 * [ ]: cross compile/override baked compiler 13 * [ ]: msvc build doesn't detect out of date files correctly 14 * [ ]: seperate dwarf debug info 15 */ 16 17 #include "util.h" 18 19 #include <stdarg.h> 20 #include <setjmp.h> 21 #include <stdio.h> 22 23 #define BeamformerMaxComputeShaderStages 1 24 #include "beamformer_parameters.h" 25 26 global char *g_argv0; 27 28 #define META_NAMESPACE_UPPER "Beamformer" 29 #define META_NAMESPACE_LOWER "beamformer" 30 31 #define OUTDIR "out" 32 #define OUTPUT(s) OUTDIR OS_PATH_SEPARATOR s 33 34 #if COMPILER_MSVC 35 #define COMMON_CFLAGS "-std:c11" 36 #define COMMON_FLAGS "-nologo", "-Fo:" OUTDIR "\\", "-Z7", "-Zo" 37 #define DEBUG_FLAGS "-Od", "-D_DEBUG" 38 #define OPTIMIZED_FLAGS "-O2" 39 #define EXTRA_FLAGS "" 40 #else 41 #define COMMON_CFLAGS "-std=c11" 42 #define COMMON_FLAGS "-pipe", "-Wall" 43 #define DEBUG_FLAGS "-O0", "-D_DEBUG", "-Wno-unused-function" 44 #define OPTIMIZED_FLAGS "-O3" 45 #define EXTRA_FLAGS_BASE "-Werror", "-Wextra", "-Wno-unused-parameter", \ 46 "-Wno-error=unused-function", "-fno-builtin" 47 #if COMPILER_GCC 48 #define EXTRA_FLAGS EXTRA_FLAGS_BASE, "-Wno-unused-variable" 49 #else 50 #define EXTRA_FLAGS EXTRA_FLAGS_BASE 51 #endif 52 #endif 53 54 #define is_aarch64 ARCH_ARM64 55 #define is_amd64 ARCH_X64 56 #define is_unix OS_LINUX 57 #define is_w32 OS_WINDOWS 58 #define is_clang COMPILER_CLANG 59 #define is_gcc COMPILER_GCC 60 #define is_msvc COMPILER_MSVC 61 62 #define BEAMFORMER_IMPORT function 63 64 #if OS_LINUX 65 66 #include <dirent.h> 67 #include <errno.h> 68 #include <string.h> 69 #include <sys/select.h> 70 #include <sys/wait.h> 71 72 #include "os_linux.c" 73 74 #define W32_DECL(x) 75 76 #define OS_SHARED_LINK_LIB(s) "lib" s ".so" 77 #define OS_SHARED_LIB(s) s ".so" 78 #define OS_STATIC_LIB(s) s ".a" 79 #define OS_MAIN "main_linux.c" 80 81 #elif OS_WINDOWS 82 83 #include <string.h> 84 85 #include "os_win32.c" 86 87 #define W32_DECL(x) x 88 89 #define OS_SHARED_LINK_LIB(s) s ".dll" 90 #define OS_SHARED_LIB(s) s ".dll" 91 #define OS_STATIC_LIB(s) s ".lib" 92 #define OS_MAIN "main_w32.c" 93 94 #else 95 #error Unsupported Platform 96 #endif 97 98 #if COMPILER_CLANG 99 #define COMPILER "clang" 100 #define CPP_COMPILER "clang++" 101 #define PREPROCESSOR "clang", "-E", "-P" 102 #elif COMPILER_MSVC 103 #define COMPILER "cl" 104 #define CPP_COMPILER "cl" 105 #define PREPROCESSOR "cl", "/EP" 106 #else 107 #define COMPILER "cc" 108 #define CPP_COMPILER "c++" 109 #define PREPROCESSOR "cc", "-E", "-P" 110 #endif 111 112 #if COMPILER_MSVC 113 #define LINK_LIB(name) name ".lib" 114 #define OBJECT(name) name ".obj" 115 #define OUTPUT_DLL(name) "/LD", "/Fe:", name 116 #define OUTPUT_LIB(name) "/out:" OUTPUT(name) 117 #define OUTPUT_EXE(name) "/Fe:", name 118 #define COMPILER_OUTPUT "/Fo:" 119 #define STATIC_LIBRARY_BEGIN(name) "lib", "/nologo", name 120 #else 121 #define LINK_LIB(name) "-l" name 122 #define OBJECT(name) name ".o" 123 #define OUTPUT_DLL(name) "-fPIC", "-shared", "-o", name 124 #define OUTPUT_LIB(name) OUTPUT(name) 125 #define OUTPUT_EXE(name) "-o", name 126 #define COMPILER_OUTPUT "-o" 127 #define STATIC_LIBRARY_BEGIN(name) "ar", "rc", name 128 #endif 129 130 #define shift(list, count) ((count)--, *(list)++) 131 132 #define cmd_append_count da_append_count 133 #define cmd_append(a, s, ...) da_append_count(a, s, ((char *[]){__VA_ARGS__}), \ 134 (iz)(sizeof((char *[]){__VA_ARGS__}) / sizeof(char *))) 135 136 DA_STRUCT(char *, Command); 137 138 typedef struct { 139 b32 bake_shaders; 140 b32 debug; 141 b32 generic; 142 b32 sanitize; 143 b32 tests; 144 b32 time; 145 } Config; 146 global Config config; 147 148 read_only global s8 c_file_header = s8_comp("" 149 "/* See LICENSE for license details. */\n\n" 150 "// GENERATED CODE\n\n" 151 ); 152 153 #define BUILD_LOG_KINDS \ 154 X(Error, "\x1B[31m[ERROR]\x1B[0m ") \ 155 X(Warning, "\x1B[33m[WARNING]\x1B[0m ") \ 156 X(Generate, "\x1B[32m[GENERATE]\x1B[0m ") \ 157 X(Info, "\x1B[33m[INFO]\x1B[0m ") \ 158 X(Command, "\x1B[36m[COMMAND]\x1B[0m ") 159 #define X(t, ...) BuildLogKind_##t, 160 typedef enum {BUILD_LOG_KINDS BuildLogKind_Count} BuildLogKind; 161 #undef X 162 163 function void 164 build_log_base(BuildLogKind kind, char *format, va_list args) 165 { 166 #define X(t, pre) pre, 167 read_only local_persist char *prefixes[BuildLogKind_Count + 1] = {BUILD_LOG_KINDS "[INVALID] "}; 168 #undef X 169 FILE *out = kind == BuildLogKind_Error? stderr : stdout; 170 fputs(prefixes[MIN(kind, BuildLogKind_Count)], out); 171 vfprintf(out, format, args); 172 fputc('\n', out); 173 } 174 175 #define build_log_failure(format, ...) build_log(BuildLogKind_Error, \ 176 "failed to build: " format, ##__VA_ARGS__) 177 #define build_log_error(...) build_log(BuildLogKind_Error, ##__VA_ARGS__) 178 #define build_log_generate(...) build_log(BuildLogKind_Generate, ##__VA_ARGS__) 179 #define build_log_info(...) build_log(BuildLogKind_Info, ##__VA_ARGS__) 180 #define build_log_command(...) build_log(BuildLogKind_Command, ##__VA_ARGS__) 181 #define build_log_warning(...) build_log(BuildLogKind_Warning, ##__VA_ARGS__) 182 183 function print_format(2, 3) void 184 build_log(BuildLogKind kind, char *format, ...) 185 { 186 va_list ap; 187 va_start(ap, format); 188 build_log_base(kind, format, ap); 189 va_end(ap); 190 } 191 192 #define build_fatal(fmt, ...) build_fatal_("%s: " fmt, __FUNCTION__, ##__VA_ARGS__) 193 function no_return print_format(1, 2) void 194 build_fatal_(char *format, ...) 195 { 196 va_list ap; 197 va_start(ap, format); 198 build_log_base(BuildLogKind_Error, format, ap); 199 va_end(ap); 200 os_exit(1); 201 } 202 203 function s8 204 read_entire_file(const char *file, Arena *arena) 205 { 206 s8 result = {0}; 207 result.len = os_read_entire_file(file, arena->beg, arena_capacity(arena, u8)); 208 if (result.len) result.data = arena_commit(arena, result.len); 209 return result; 210 } 211 212 function b32 213 s8_contains(s8 s, u8 byte) 214 { 215 b32 result = 0; 216 for (iz i = 0 ; !result && i < s.len; i++) 217 result |= s.data[i] == byte; 218 return result; 219 } 220 221 function void 222 stream_push_command(Stream *s, CommandList *c) 223 { 224 if (!s->errors) { 225 for (iz i = 0; i < c->count; i++) { 226 s8 item = c_str_to_s8(c->data[i]); 227 if (item.len) { 228 b32 escape = s8_contains(item, ' ') || s8_contains(item, '"'); 229 if (escape) stream_append_byte(s, '\''); 230 stream_append_s8(s, item); 231 if (escape) stream_append_byte(s, '\''); 232 if (i != c->count - 1) stream_append_byte(s, ' '); 233 } 234 } 235 } 236 } 237 238 function print_format(1, 2) char * 239 temp_sprintf(char *format, ...) 240 { 241 local_persist char buffer[4096]; 242 va_list ap; 243 va_start(ap, format); 244 vsnprintf(buffer, countof(buffer), format, ap); 245 va_end(ap); 246 return buffer; 247 } 248 249 #if OS_LINUX 250 251 function b32 252 os_rename_file(char *name, char *new) 253 { 254 b32 result = rename(name, new) != -1; 255 return result; 256 } 257 258 function b32 259 os_remove_file(char *name) 260 { 261 b32 result = remove(name) != -1; 262 return result; 263 } 264 265 function void 266 os_make_directory(char *name) 267 { 268 mkdir(name, 0770); 269 } 270 271 #define os_remove_directory(f) os_remove_directory_(AT_FDCWD, (f)) 272 function b32 273 os_remove_directory_(i32 base_fd, char *name) 274 { 275 /* POSix sucks */ 276 #ifndef DT_DIR 277 enum {DT_DIR = 4, DT_REG = 8, DT_LNK = 10}; 278 #endif 279 280 i32 dir_fd = openat(base_fd, name, O_DIRECTORY); 281 b32 result = dir_fd != -1 || errno == ENOTDIR || errno == ENOENT; 282 DIR *dir; 283 if (dir_fd != -1 && (dir = fdopendir(dir_fd))) { 284 struct dirent *dp; 285 while ((dp = readdir(dir))) { 286 switch (dp->d_type) { 287 case DT_LNK: 288 case DT_REG: 289 { 290 unlinkat(dir_fd, dp->d_name, 0); 291 }break; 292 case DT_DIR:{ 293 s8 dir_name = c_str_to_s8(dp->d_name); 294 if (!s8_equal(s8("."), dir_name) && !s8_equal(s8(".."), dir_name)) 295 os_remove_directory_(dir_fd, dp->d_name); 296 }break; 297 default:{ 298 build_log_warning("\"%s\": unknown directory entry kind: %d", dp->d_name, dp->d_type); 299 }break; 300 } 301 } 302 303 closedir(dir); 304 result = unlinkat(base_fd, name, AT_REMOVEDIR) == 0; 305 } 306 return result; 307 } 308 309 function u64 310 os_get_filetime(char *file) 311 { 312 struct stat sb; 313 u64 result = (u64)-1; 314 if (stat(file, &sb) != -1) 315 result = (u64)sb.st_mtim.tv_sec; 316 return result; 317 } 318 319 function iptr 320 os_spawn_process(CommandList *cmd, Stream sb) 321 { 322 pid_t result = fork(); 323 switch (result) { 324 case -1: build_fatal("failed to fork command: %s: %s", cmd->data[0], strerror(errno)); break; 325 case 0: { 326 if (execvp(cmd->data[0], cmd->data) == -1) 327 build_fatal("failed to exec command: %s: %s", cmd->data[0], strerror(errno)); 328 unreachable(); 329 } break; 330 } 331 return (iptr)result; 332 } 333 334 function b32 335 os_wait_close_process(iptr handle) 336 { 337 b32 result = 0; 338 for (;;) { 339 i32 status; 340 iptr wait_pid = (iptr)waitpid((i32)handle, &status, 0); 341 if (wait_pid == -1) 342 build_fatal("failed to wait on child process: %s", strerror(errno)); 343 if (wait_pid == handle) { 344 if (WIFEXITED(status)) { 345 status = WEXITSTATUS(status); 346 /* TODO(rnp): logging */ 347 result = status == 0; 348 break; 349 } 350 if (WIFSIGNALED(status)) { 351 /* TODO(rnp): logging */ 352 result = 0; 353 break; 354 } 355 } else { 356 /* TODO(rnp): handle multiple children */ 357 InvalidCodePath; 358 } 359 } 360 return result; 361 } 362 363 #elif OS_WINDOWS 364 365 enum { 366 MOVEFILE_REPLACE_EXISTING = 0x01, 367 368 FILE_ATTRIBUTE_DIRECTORY = 0x10, 369 370 ERROR_FILE_NOT_FOUND = 0x02, 371 ERROR_PATH_NOT_FOUND = 0x03, 372 }; 373 374 #pragma pack(push, 1) 375 typedef struct { 376 u32 file_attributes; 377 u64 creation_time; 378 u64 last_access_time; 379 u64 last_write_time; 380 u64 file_size; 381 u64 reserved; 382 c8 file_name[260]; 383 c8 alternate_file_name[14]; 384 u32 file_type; 385 u32 creator_type; 386 u16 finder_flag; 387 } w32_find_data; 388 #pragma pack(pop) 389 390 W32(b32) CreateDirectoryA(c8 *, void *); 391 W32(b32) CreateProcessA(u8 *, u8 *, iptr, iptr, b32, u32, iptr, u8 *, iptr, iptr); 392 W32(b32) FindClose(iptr); 393 W32(iptr) FindFirstFileA(c8 *, w32_find_data *); 394 W32(b32) FindNextFileA(iptr, w32_find_data *); 395 W32(b32) GetExitCodeProcess(iptr, u32 *); 396 W32(b32) GetFileTime(iptr, iptr, iptr, iptr); 397 W32(b32) MoveFileExA(c8 *, c8 *, u32); 398 W32(b32) RemoveDirectoryA(c8 *); 399 400 function void 401 os_make_directory(char *name) 402 { 403 CreateDirectoryA(name, 0); 404 } 405 406 function b32 407 os_remove_directory(char *name) 408 { 409 w32_find_data find_data[1]; 410 char *search = temp_sprintf(".\\%s\\*", name); 411 iptr handle = FindFirstFileA(search, find_data); 412 b32 result = 1; 413 if (handle != INVALID_FILE) { 414 do { 415 s8 file_name = c_str_to_s8(find_data->file_name); 416 if (!s8_equal(s8("."), file_name) && !s8_equal(s8(".."), file_name)) { 417 char *full_path = temp_sprintf("%s" OS_PATH_SEPARATOR "%s", name, find_data->file_name); 418 if (find_data->file_attributes & FILE_ATTRIBUTE_DIRECTORY) { 419 char *wow_w32_is_even_worse_than_POSix = strdup(full_path); 420 os_remove_directory(wow_w32_is_even_worse_than_POSix); 421 free(wow_w32_is_even_worse_than_POSix); 422 } else { 423 DeleteFileA(full_path); 424 } 425 } 426 } while (FindNextFileA(handle, find_data)); 427 FindClose(handle); 428 } else { 429 i32 error = GetLastError(); 430 result = error == ERROR_FILE_NOT_FOUND || error == ERROR_PATH_NOT_FOUND; 431 } 432 RemoveDirectoryA(name); 433 return result; 434 } 435 436 function b32 437 os_rename_file(char *name, char *new) 438 { 439 b32 result = MoveFileExA(name, new, MOVEFILE_REPLACE_EXISTING) != 0; 440 return result; 441 } 442 443 function b32 444 os_remove_file(char *name) 445 { 446 b32 result = DeleteFileA(name); 447 return result; 448 } 449 450 function u64 451 os_get_filetime(char *file) 452 { 453 u64 result = (u64)-1; 454 iptr h = CreateFileA(file, 0, 0, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); 455 if (h != INVALID_FILE) { 456 union { struct { u32 low, high; }; u64 U64; } w32_filetime; 457 GetFileTime(h, 0, 0, (iptr)&w32_filetime); 458 result = w32_filetime.U64; 459 CloseHandle(h); 460 } 461 return result; 462 } 463 464 function iptr 465 os_spawn_process(CommandList *cmd, Stream sb) 466 { 467 struct { 468 u32 cb; 469 u8 *reserved, *desktop, *title; 470 u32 x, y, x_size, y_size, x_count_chars, y_count_chars; 471 u32 fill_attr, flags; 472 u16 show_window, reserved_2; 473 u8 *reserved_3; 474 iptr std_input, std_output, std_error; 475 } w32_startup_info = { 476 .cb = sizeof(w32_startup_info), 477 .flags = 0x100, 478 .std_input = GetStdHandle(STD_INPUT_HANDLE), 479 .std_output = GetStdHandle(STD_OUTPUT_HANDLE), 480 .std_error = GetStdHandle(STD_ERROR_HANDLE), 481 }; 482 483 struct { 484 iptr phandle, thandle; 485 u32 pid, tid; 486 } w32_process_info = {0}; 487 488 /* TODO(rnp): warn if we need to clamp last string */ 489 sb.widx = MIN(sb.widx, (i32)(KB(32) - 1)); 490 if (sb.widx < sb.cap) sb.data[sb.widx] = 0; 491 else sb.data[sb.widx - 1] = 0; 492 493 iptr result = INVALID_FILE; 494 if (CreateProcessA(0, sb.data, 0, 0, 1, 0, 0, 0, (iptr)&w32_startup_info, 495 (iptr)&w32_process_info)) 496 { 497 CloseHandle(w32_process_info.thandle); 498 result = w32_process_info.phandle; 499 } 500 return result; 501 } 502 503 function b32 504 os_wait_close_process(iptr handle) 505 { 506 b32 result = WaitForSingleObject(handle, (u32)-1) != 0xFFFFFFFFUL; 507 if (result) { 508 u32 status; 509 GetExitCodeProcess(handle, &status); 510 result = status == 0; 511 } 512 CloseHandle(handle); 513 return result; 514 } 515 516 #endif 517 518 #define needs_rebuild(b, ...) needs_rebuild_(b, ((char *[]){__VA_ARGS__}), \ 519 (sizeof((char *[]){__VA_ARGS__}) / sizeof(char *))) 520 function b32 521 needs_rebuild_(char *binary, char *deps[], iz deps_count) 522 { 523 u64 binary_filetime = os_get_filetime(binary); 524 u64 argv0_filetime = os_get_filetime(g_argv0); 525 b32 result = (binary_filetime == (u64)-1) | (argv0_filetime > binary_filetime); 526 for (iz i = 0; i < deps_count; i++) { 527 u64 filetime = os_get_filetime(deps[i]); 528 result |= (filetime == (u64)-1) | (filetime > binary_filetime); 529 } 530 return result; 531 } 532 533 function b32 534 run_synchronous(Arena a, CommandList *command) 535 { 536 Stream sb = arena_stream(a); 537 stream_push_command(&sb, command); 538 build_log_command("%.*s", (i32)sb.widx, sb.data); 539 return os_wait_close_process(os_spawn_process(command, sb)); 540 } 541 542 function b32 543 use_sanitization(void) 544 { 545 return config.sanitize && !is_msvc && !(is_w32 && is_gcc); 546 } 547 548 function void 549 cmd_base(Arena *a, CommandList *c, b32 cpp, b32 debug) 550 { 551 Config *o = &config; 552 553 cmd_append(a, c, cpp ? CPP_COMPILER : COMPILER); 554 555 if (!is_msvc) { 556 /* TODO(rnp): support cross compiling with clang */ 557 if (!o->generic) cmd_append(a, c, "-march=native"); 558 else if (is_amd64) cmd_append(a, c, "-march=x86-64-v3", "-msse4.1"); 559 else if (is_aarch64) cmd_append(a, c, "-march=armv8"); 560 } 561 562 if (!cpp) cmd_append(a, c, COMMON_CFLAGS); 563 cmd_append(a, c, COMMON_FLAGS); 564 if (debug) cmd_append(a, c, DEBUG_FLAGS); 565 else cmd_append(a, c, OPTIMIZED_FLAGS); 566 567 /* NOTE: ancient gcc bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80454 */ 568 if (is_gcc) cmd_append(a, c, "-Wno-missing-braces"); 569 570 if (!is_msvc) cmd_append(a, c, "-fms-extensions"); 571 572 if (debug && is_unix) cmd_append(a, c, "-gdwarf-4"); 573 574 /* NOTE(rnp): need to avoid w32-gcc for ci */ 575 b32 sanitize = use_sanitization(); 576 if (sanitize) cmd_append(a, c, "-fsanitize=address,undefined"); 577 if (!sanitize && o->sanitize) build_log_warning("santizers not supported with this compiler"); 578 } 579 580 function void 581 check_rebuild_self(Arena arena, i32 argc, char *argv[]) 582 { 583 char *binary = shift(argv, argc); 584 if (needs_rebuild(binary, __FILE__, "os_win32.c", "os_linux.c", "util.c", "util.h", "beamformer_parameters.h")) { 585 Stream name_buffer = arena_stream(arena); 586 stream_append_s8s(&name_buffer, c_str_to_s8(binary), s8(".old")); 587 char *old_name = (char *)arena_stream_commit_zero(&arena, &name_buffer).data; 588 589 if (!os_rename_file(binary, old_name)) 590 build_fatal("failed to move: %s -> %s", binary, old_name); 591 592 CommandList c = {0}; 593 cmd_base(&arena, &c, 0, 0); 594 cmd_append(&arena, &c, EXTRA_FLAGS); 595 if (!is_msvc) cmd_append(&arena, &c, "-Wno-unused-function"); 596 cmd_append(&arena, &c, __FILE__, OUTPUT_EXE(binary)); 597 if (is_msvc) cmd_append(&arena, &c, "/link", "-incremental:no", "-opt:ref"); 598 cmd_append(&arena, &c, (void *)0); 599 if (!run_synchronous(arena, &c)) { 600 os_rename_file(old_name, binary); 601 build_fatal("failed to rebuild self"); 602 } 603 os_remove_file(old_name); 604 605 c.count = 0; 606 cmd_append(&arena, &c, binary); 607 cmd_append_count(&arena, &c, argv, argc); 608 cmd_append(&arena, &c, (void *)0); 609 if (!run_synchronous(arena, &c)) 610 os_exit(1); 611 612 os_exit(0); 613 } 614 } 615 616 function void 617 usage(char *argv0) 618 { 619 printf("%s [--bake-shaders] [--debug] [--sanitize] [--time]\n" 620 " --debug: dynamically link and build with debug symbols\n" 621 " --generic: compile for a generic target (x86-64-v3 or armv8 with NEON)\n" 622 " --sanitize: build with ASAN and UBSAN\n" 623 " --tests: also build programs in tests/\n" 624 " --time: print build time\n" 625 , argv0); 626 os_exit(0); 627 } 628 629 function void 630 parse_config(i32 argc, char *argv[]) 631 { 632 char *argv0 = shift(argv, argc); 633 while (argc > 0) { 634 char *arg = shift(argv, argc); 635 s8 str = c_str_to_s8(arg); 636 if (s8_equal(str, s8("--bake-shaders"))) { 637 config.bake_shaders = 1; 638 } else if (s8_equal(str, s8("--debug"))) { 639 config.debug = 1; 640 } else if (s8_equal(str, s8("--generic"))) { 641 config.generic = 1; 642 } else if (s8_equal(str, s8("--sanitize"))) { 643 config.sanitize = 1; 644 } else if (s8_equal(str, s8("--tests"))) { 645 config.tests = 1; 646 } else if (s8_equal(str, s8("--time"))) { 647 config.time = 1; 648 } else { 649 usage(argv0); 650 } 651 } 652 } 653 654 /* NOTE(rnp): produce pdbs on w32 */ 655 function void 656 cmd_pdb(Arena *a, CommandList *cmd, char *name) 657 { 658 if (is_w32 && is_clang) { 659 cmd_append(a, cmd, "-fuse-ld=lld", "-g", "-gcodeview", "-Wl,--pdb="); 660 } else if (is_msvc) { 661 Stream sb = arena_stream(*a); 662 stream_append_s8s(&sb, s8("-PDB:"), c_str_to_s8(name), s8(".pdb")); 663 char *pdb = (char *)arena_stream_commit_zero(a, &sb).data; 664 cmd_append(a, cmd, "/link", "-incremental:no", "-opt:ref", "-DEBUG", pdb); 665 } 666 } 667 668 function void 669 git_submodule_update(Arena a, char *name) 670 { 671 Stream sb = arena_stream(a); 672 stream_append_s8s(&sb, c_str_to_s8(name), s8(OS_PATH_SEPARATOR), s8(".git")); 673 arena_stream_commit_zero(&a, &sb); 674 675 CommandList git = {0}; 676 /* NOTE(rnp): cryptic bs needed to get a simple exit code if name is dirty */ 677 cmd_append(&a, &git, "git", "diff-index", "--quiet", "HEAD", "--", name, (void *)0); 678 if (!os_file_exists((c8 *)sb.data) || !run_synchronous(a, &git)) { 679 git.count = 1; 680 cmd_append(&a, &git, "submodule", "update", "--init", "--depth=1", name, (void *)0); 681 if (!run_synchronous(a, &git)) 682 build_fatal("failed to clone required module: %s", name); 683 } 684 } 685 686 function b32 687 build_shared_library(Arena a, CommandList cc, char *name, char *output, char **libs, iz libs_count, char **srcs, iz srcs_count) 688 { 689 cmd_append_count(&a, &cc, srcs, srcs_count); 690 cmd_append(&a, &cc, OUTPUT_DLL(output)); 691 cmd_pdb(&a, &cc, name); 692 cmd_append_count(&a, &cc, libs, libs_count); 693 cmd_append(&a, &cc, (void *)0); 694 b32 result = run_synchronous(a, &cc); 695 if (!result) build_log_failure("%s", output); 696 return result; 697 } 698 699 function b32 700 cc_single_file(Arena a, CommandList cc, char *exe, char *src, char *dest, char **tail, iz tail_count) 701 { 702 char *executable[] = {src, is_msvc? "/Fe:" : "-o", dest}; 703 char *object[] = {is_msvc? "/c" : "-c", src, is_msvc? "/Fo:" : "-o", dest}; 704 705 cmd_append_count(&a, &cc, exe? executable : object, 706 exe? countof(executable) : countof(object)); 707 if (exe) cmd_pdb(&a, &cc, exe); 708 cmd_append_count(&a, &cc, tail, tail_count); 709 cmd_append(&a, &cc, (void *)0); 710 b32 result = run_synchronous(a, &cc); 711 if (!result) build_log_failure("%s", dest); 712 return result; 713 } 714 715 function b32 716 build_static_library_from_objects(Arena a, char *name, char **flags, iz flags_count, char **objects, iz count) 717 { 718 CommandList ar = {0}; 719 cmd_append(&a, &ar, STATIC_LIBRARY_BEGIN(name)); 720 cmd_append_count(&a, &ar, flags, flags_count); 721 cmd_append_count(&a, &ar, objects, count); 722 cmd_append(&a, &ar, (void *)0); 723 b32 result = run_synchronous(a, &ar); 724 if (!result) build_log_failure("%s", name); 725 return result; 726 } 727 728 function b32 729 build_static_library(Arena a, CommandList cc, char *name, char **deps, char **outputs, iz count) 730 { 731 /* TODO(rnp): refactor to not need outputs */ 732 b32 result = 1; 733 for (iz i = 0; i < count; i++) 734 result &= cc_single_file(a, cc, 0, deps[i], outputs[i], 0, 0); 735 if (result) result = build_static_library_from_objects(a, name, 0, 0, outputs, count); 736 return result; 737 } 738 739 function b32 740 build_raylib(Arena a) 741 { 742 b32 result = 1, shared = config.debug; 743 char *libraylib = shared ? OS_SHARED_LINK_LIB("raylib") : OUTPUT_LIB(OS_STATIC_LIB("raylib")); 744 if (needs_rebuild(libraylib, "external/raylib")) { 745 git_submodule_update(a, "external/raylib"); 746 747 CommandList cc = {0}; 748 cmd_base(&a, &cc, 0, config.debug); 749 if (is_unix) cmd_append(&a, &cc, "-D_GLFW_X11"); 750 cmd_append(&a, &cc, "-DPLATFORM_DESKTOP_GLFW"); 751 if (!is_msvc) cmd_append(&a, &cc, "-Wno-unused-but-set-variable"); 752 cmd_append(&a, &cc, "-Iexternal/include", "-Iexternal/raylib/src", "-Iexternal/raylib/src/external/glfw/include"); 753 #define RAYLIB_SOURCES \ 754 X(rglfw) \ 755 X(rshapes) \ 756 X(rtext) \ 757 X(rtextures) \ 758 X(utils) 759 #define X(name) "external/raylib/src/" #name ".c", 760 char *srcs[] = {"external/rcore_extended.c", RAYLIB_SOURCES}; 761 #undef X 762 #define X(name) OUTPUT(OBJECT(#name)), 763 char *outs[] = {OUTPUT(OBJECT("rcore_extended")), RAYLIB_SOURCES}; 764 #undef X 765 766 if (shared) { 767 char *libs[] = {LINK_LIB("user32"), LINK_LIB("shell32"), LINK_LIB("gdi32"), LINK_LIB("winmm")}; 768 iz libs_count = is_w32 ? countof(libs) : 0; 769 cmd_append(&a, &cc, "-DBUILD_LIBTYPE_SHARED", "-D_GLFW_BUILD_DLL"); 770 result = build_shared_library(a, cc, "raylib", libraylib, libs, libs_count, srcs, countof(srcs)); 771 } else { 772 result = build_static_library(a, cc, libraylib, srcs, outs, countof(srcs)); 773 } 774 } 775 return result; 776 } 777 778 function b32 779 build_glslang(Arena a) 780 { 781 b32 result = 1; 782 char *lib = OUTPUT_LIB(OS_STATIC_LIB("glslang")); 783 if (needs_rebuild(lib, "external/glslang", "external/glslang_local/glslang.cpp")) { 784 git_submodule_update(a, "external/glslang"); 785 786 // NOTE(rnp): do not build this with debug symbols. The size explodes because c++ 787 CommandList cc = {0}; 788 cmd_base(&a, &cc, 1, 0); 789 cmd_append(&a, &cc, "-std=c++17", "-fno-rtti", "-fno-exceptions", "-Wno-unused-but-set-variable"); 790 cmd_append(&a, &cc, "-Iexternal/glslang_local", "-Iexternal/glslang"); 791 792 #if OS_WINDOWS 793 #define GLSLANG_SOURCES_OS X(ossource, "glslang/glslang/OSDependent/Windows/") 794 #else 795 #define GLSLANG_SOURCES_OS 796 #endif 797 798 #define GLSLANG_SOURCES_COMMON \ 799 X(glslang, "glslang_local/") \ 800 X(spirv_c_interface, "glslang/SPIRV/CInterface/") \ 801 802 #define GLSLANG_SOURCES \ 803 GLSLANG_SOURCES_COMMON \ 804 GLSLANG_SOURCES_OS \ 805 806 #define X(name, extra) "external/" extra #name ".cpp", 807 char *srcs[] = {GLSLANG_SOURCES}; 808 #undef X 809 #define X(name, ...) OUTPUT(OBJECT(#name)), 810 char *outs[] = {GLSLANG_SOURCES}; 811 #undef X 812 813 result = build_static_library(a, cc, lib, srcs, outs, countof(srcs)); 814 } 815 return result; 816 } 817 818 function b32 819 build_helper_library(Arena arena) 820 { 821 CommandList cc = {0}; 822 cmd_base(&arena, &cc, 0, 0); 823 cmd_append(&arena, &cc, EXTRA_FLAGS); 824 825 ///////////// 826 // library 827 char *library = OUTPUT(OS_SHARED_LIB("ogl_beamformer_lib")); 828 char *libs[] = {LINK_LIB("Synchronization")}; 829 iz libs_count = is_w32 ? countof(libs) : 0; 830 831 if (!is_msvc) cmd_append(&arena, &cc, "-Wno-unused-function"); 832 b32 result = build_shared_library(arena, cc, "ogl_beamformer_lib", library, 833 libs, libs_count, (char *[]){"lib/ogl_beamformer_lib.c"}, 1); 834 return result; 835 } 836 837 function void 838 cmd_beamformer_base(Arena *a, CommandList *c) 839 { 840 cmd_base(a, c, 0, config.debug); 841 cmd_append(a, c, "-Iexternal/include"); 842 cmd_append(a, c, EXTRA_FLAGS); 843 cmd_append(a, c, config.bake_shaders? "-DBakeShaders=1" : "-DBakeShaders=0"); 844 if (config.debug) cmd_append(a, c, "-DBEAMFORMER_DEBUG", "-DBEAMFORMER_RENDERDOC_HOOKS"); 845 846 /* NOTE(rnp): impossible to autodetect on GCC versions < 14 (ci has 13) */ 847 cmd_append(a, c, use_sanitization() ? "-DASAN_ACTIVE=1" : "-DASAN_ACTIVE=0"); 848 } 849 850 function b32 851 build_beamformer_main(Arena arena) 852 { 853 CommandList c = {0}; 854 cmd_beamformer_base(&arena, &c); 855 856 cmd_append(&arena, &c, OS_MAIN, OUTPUT_EXE("ogl")); 857 cmd_pdb(&arena, &c, "ogl"); 858 if (config.debug) { 859 if (!is_w32) cmd_append(&arena, &c, "-Wl,--export-dynamic", "-Wl,-rpath,."); 860 if (!is_msvc) cmd_append(&arena, &c, "-L."); 861 cmd_append(&arena, &c, LINK_LIB("raylib")); 862 } else { 863 if (!is_msvc) cmd_append(&arena, &c, "-flto"); 864 cmd_append(&arena, &c, OUTPUT(OS_STATIC_LIB("raylib"))); 865 } 866 // TODO(rnp): not sure how to do this with msvc. we don't want a runtime dependence on libc++ 867 cmd_append(&arena, &c, OUTPUT(OS_STATIC_LIB("glslang")), "-Wl,-Bstatic", "-lstdc++", "-Wl,-Bdynamic"); 868 869 if (!is_msvc) cmd_append(&arena, &c, "-lm"); 870 if (is_unix) cmd_append(&arena, &c, "-lGL"); 871 872 if (is_w32) { 873 cmd_append(&arena, &c, LINK_LIB("user32"), LINK_LIB("shell32"), LINK_LIB("gdi32"), 874 LINK_LIB("opengl32"), LINK_LIB("winmm"), LINK_LIB("Synchronization")); 875 if (!is_msvc) cmd_append(&arena, &c, "-Wl,--out-implib," OUTPUT(OS_STATIC_LIB("main"))); 876 } 877 878 cmd_append(&arena, &c, (void *)0); 879 880 return run_synchronous(arena, &c); 881 } 882 883 function b32 884 build_beamformer_as_library(Arena arena) 885 { 886 CommandList cc = {0}; 887 cmd_beamformer_base(&arena, &cc); 888 889 if (is_msvc) { 890 build_static_library_from_objects(arena, OUTPUT_LIB(OS_STATIC_LIB("main")), 891 arg_list(char *, "/def", "/name:ogl.exe"), 892 arg_list(char *, OUTPUT(OBJECT("main_w32")))); 893 } 894 895 char *library = OS_SHARED_LIB("beamformer"); 896 char *libs[] = {!is_msvc? "-L." : "", LINK_LIB("raylib"), LINK_LIB("gdi32"), 897 LINK_LIB("shell32"), LINK_LIB("user32"), LINK_LIB("opengl32"), 898 LINK_LIB("winmm"), LINK_LIB("Synchronization"), OUTPUT("main.lib")}; 899 iz libs_count = is_w32 ? countof(libs) : 0; 900 cmd_append(&arena, &cc, "-D_BEAMFORMER_DLL"); 901 b32 result = build_shared_library(arena, cc, "beamformer", library, 902 libs, libs_count, arg_list(char *, "beamformer_core.c")); 903 return result; 904 } 905 906 function b32 907 build_tests(Arena arena) 908 { 909 CommandList cc = {0}; 910 cmd_base(&arena, &cc, 0, config.debug); 911 cmd_append(&arena, &cc, EXTRA_FLAGS); 912 913 #define TEST_PROGRAMS \ 914 X("throughput", LINK_LIB("m"), LINK_LIB("zstd"), W32_DECL(LINK_LIB("Synchronization"))) \ 915 X("decode", LINK_LIB("m"), W32_DECL(LINK_LIB("Synchronization"))) \ 916 917 os_make_directory(OUTPUT("tests")); 918 if (!is_msvc) cmd_append(&arena, &cc, "-Wno-unused-function"); 919 cmd_append(&arena, &cc, "-I.", "-Ilib"); 920 921 b32 result = 1; 922 iz cc_count = cc.count; 923 #define X(prog, ...) \ 924 result &= cc_single_file(arena, cc, prog, "tests" OS_PATH_SEPARATOR prog ".c", \ 925 OUTPUT("tests" OS_PATH_SEPARATOR prog), \ 926 arg_list(char *, ##__VA_ARGS__)); \ 927 cc.count = cc_count; 928 TEST_PROGRAMS 929 #undef X 930 return result; 931 } 932 933 typedef struct { 934 s8 *data; 935 da_count count; 936 da_count capacity; 937 } s8_list; 938 939 function s8 940 s8_chop(s8 *in, iz count) 941 { 942 count = Clamp(count, 0, in->len); 943 s8 result = {.data = in->data, .len = count}; 944 in->data += count; 945 in->len -= count; 946 return result; 947 } 948 949 function str8 950 str8_chop(str8 *in, i64 count) 951 { 952 count = Clamp(count, 0, in->length); 953 str8 result = {.data = in->data, .length = count}; 954 in->data += count; 955 in->length -= count; 956 return result; 957 } 958 959 function void 960 str8_split(str8 str, str8 *left, str8 *right, u8 byte) 961 { 962 i64 i; 963 for (i = 0; i < str.length; i++) if (str.data[i] == byte) break; 964 965 if (left) *left = (str8){.data = str.data, .length = i}; 966 if (right) { 967 right->data = str.data + i + 1; 968 right->length = Max(0, str.length - (i + 1)); 969 } 970 } 971 972 function str8 973 str8_trim(str8 in) 974 { 975 str8 result = in; 976 for (i64 i = 0; i < in.length && *result.data == ' '; i++) result.data++; 977 result.length -= result.data - in.data; 978 for (; result.length > 0 && result.data[result.length - 1] == ' '; result.length--); 979 return result; 980 } 981 982 typedef struct { 983 Stream stream; 984 Arena scratch; 985 i32 indentation_level; 986 } MetaprogramContext; 987 988 function b32 989 meta_write_and_reset(MetaprogramContext *m, char *file) 990 { 991 b32 result = os_write_new_file(file, stream_to_str8(&m->stream)); 992 if (!result) build_log_failure("%s", file); 993 m->stream.widx = 0; 994 m->indentation_level = 0; 995 return result; 996 } 997 998 #define meta_push(m, ...) meta_push_(m, arg_list(s8, __VA_ARGS__)) 999 function void 1000 meta_push_(MetaprogramContext *m, s8 *items, iz count) 1001 { 1002 stream_append_s8s_(&m->stream, items, count); 1003 } 1004 1005 #define meta_pad(m, b, n) stream_pad(&(m)->stream, (b), (n)) 1006 #define meta_indent(m) meta_pad((m), '\t', (m)->indentation_level) 1007 #define meta_begin_line(m, ...) do { meta_indent(m); meta_push(m, __VA_ARGS__); } while(0) 1008 #define meta_end_line(m, ...) meta_push(m, ##__VA_ARGS__, s8("\n")) 1009 #define meta_push_line(m, ...) do { meta_indent(m); meta_push(m, ##__VA_ARGS__, s8("\n")); } while(0) 1010 #define meta_begin_scope(m, ...) do { meta_push_line(m, __VA_ARGS__); (m)->indentation_level++; } while(0) 1011 #define meta_end_scope(m, ...) do { (m)->indentation_level--; meta_push_line(m, __VA_ARGS__); } while(0) 1012 #define meta_push_f64(m, n) stream_append_f64(&(m)->stream, (n), 1000000) 1013 #define meta_push_u64(m, n) stream_append_u64(&(m)->stream, (n)) 1014 #define meta_push_i64(m, n) stream_append_i64(&(m)->stream, (n)) 1015 #define meta_push_u64_hex(m, n) stream_append_hex_u64(&(m)->stream, (n)) 1016 #define meta_push_u64_hex_width(m, n, w) stream_append_hex_u64_width(&(m)->stream, (n), (w)) 1017 1018 #define MATLAB_NAMESPACE "OGL" 1019 1020 #define meta_begin_matlab_class_cracker(_1, _2, FN, ...) FN 1021 #define meta_begin_matlab_class_1(m, name) meta_begin_scope(m, s8("classdef " name)) 1022 #define meta_begin_matlab_class_2(m, name, type) \ 1023 meta_begin_scope(m, s8("classdef " name " < " type)) 1024 1025 #define meta_begin_matlab_class(m, ...) \ 1026 meta_begin_matlab_class_cracker(__VA_ARGS__, \ 1027 meta_begin_matlab_class_2, \ 1028 meta_begin_matlab_class_1)(m, __VA_ARGS__) 1029 1030 function void 1031 meta_push_matlab_property(MetaprogramContext *m, s8 name, u64 length, s8 kind) 1032 { 1033 meta_begin_line(m, name, s8("(1,")); 1034 meta_push_u64(m, (u64)length); 1035 meta_end_line(m, s8(")"), kind.len > 0 ? s8(" ") : s8(""), kind); 1036 } 1037 1038 function b32 1039 meta_end_and_write_matlab(MetaprogramContext *m, char *path) 1040 { 1041 while (m->indentation_level > 0) meta_end_scope(m, s8("end")); 1042 b32 result = meta_write_and_reset(m, path); 1043 return result; 1044 } 1045 1046 #define META_ENTRY_KIND_LIST \ 1047 X(Invalid) \ 1048 X(Array) \ 1049 X(Bake) \ 1050 X(BeginScope) \ 1051 X(Constant) \ 1052 X(Embed) \ 1053 X(Emit) \ 1054 X(EndScope) \ 1055 X(Enumeration) \ 1056 X(Expand) \ 1057 X(FragmentShader) \ 1058 X(Library) \ 1059 X(MATLAB) \ 1060 X(PushConstants) \ 1061 X(RenderShader) \ 1062 X(Shader) \ 1063 X(ShaderAlias) \ 1064 X(ShaderGroup) \ 1065 X(String) \ 1066 X(Struct) \ 1067 X(Table) \ 1068 X(Union) \ 1069 X(VertexShader) \ 1070 1071 typedef enum { 1072 #define X(k, ...) MetaEntryKind_## k, 1073 META_ENTRY_KIND_LIST 1074 #undef X 1075 MetaEntryKind_Count, 1076 } MetaEntryKind; 1077 1078 #define X(k, ...) #k, 1079 read_only global char *meta_entry_kind_strings[] = {META_ENTRY_KIND_LIST}; 1080 #undef X 1081 1082 #define META_EMIT_LANG_LIST \ 1083 X(C) \ 1084 X(CLibrary) \ 1085 X(MATLAB) 1086 1087 typedef enum { 1088 #define X(k, ...) MetaEmitLang_## k, 1089 META_EMIT_LANG_LIST 1090 #undef X 1091 MetaEmitLang_Count, 1092 } MetaEmitLang; 1093 1094 #define META_KIND_LIST \ 1095 X(M4, m4, f32mat4, float, single, 64, 16) \ 1096 X(V4, v4, f32vec4, float, single, 16, 4) \ 1097 X(SV4, iv4, i32vec4, int32_t, int32, 16, 4) \ 1098 X(UV4, uv4, u32vec4, uint32_t, uint32, 16, 4) \ 1099 X(UV2, uv2, u32vec2, uint32_t, uint32, 8, 2) \ 1100 X(V3, v3, f32vec3, float, single, 12, 3) \ 1101 X(V2, v2, f32vec2, float, single, 8, 2) \ 1102 X(F32, f32, float32_t, float, single, 4, 1) \ 1103 X(S32, i32, int32_t, int32_t, int32, 4, 1) \ 1104 X(S16, i16, int16_t, int16_t, int16, 2, 1) \ 1105 X(S8, i8, int8_t, int8_t, int8, 1, 1) \ 1106 X(B64, b64, uint64_t, uint64_t, uint64, 8, 1) \ 1107 X(B32, b32, bool, uint32_t, uint32, 4, 1) \ 1108 X(B16, b16, uint16_t, uint16_t, uint16, 2, 1) \ 1109 X(B8, b8, uint8_t, uint8_t, uint8, 1, 1) \ 1110 X(U64, u64, uint64_t, uint64_t, uint64, 8, 1) \ 1111 X(U32, u32, uint32_t, uint32_t, uint32, 4, 1) \ 1112 X(U16, u16, uint16_t, uint16_t, uint16, 2, 1) \ 1113 X(U8, u8, uint8_t, uint8_t, uint8, 1, 1) \ 1114 1115 typedef enum { 1116 #define X(k, ...) MetaKind_## k, 1117 META_KIND_LIST 1118 #undef X 1119 MetaKind_Count, 1120 } MetaKind; 1121 1122 read_only global u8 meta_kind_byte_sizes[] = { 1123 #define X(_k, _c, _g, _b, _m, bytes, ...) bytes, 1124 META_KIND_LIST 1125 #undef X 1126 }; 1127 1128 read_only global u8 meta_kind_elements[] = { 1129 #define X(_k, _c, _g, _b, _m, _by, elements, ...) elements, 1130 META_KIND_LIST 1131 #undef X 1132 }; 1133 1134 read_only global str8 meta_kind_meta_types[] = { 1135 #define X(k, ...) str8_comp(#k), 1136 META_KIND_LIST 1137 #undef X 1138 }; 1139 1140 read_only global str8 meta_kind_matlab_types[] = { 1141 #define X(_k, _c, _g, _b, m, ...) str8_comp(#m), 1142 META_KIND_LIST 1143 #undef X 1144 }; 1145 1146 read_only global str8 meta_kind_base_c_types[] = { 1147 #define X(_k, _c, _g, base, ...) str8_comp(#base), 1148 META_KIND_LIST 1149 #undef X 1150 }; 1151 1152 read_only global str8 meta_kind_glsl_types[] = { 1153 #define X(_k, _c, glsl, ...) str8_comp(#glsl), 1154 META_KIND_LIST 1155 #undef X 1156 }; 1157 1158 read_only global str8 meta_kind_c_types[] = { 1159 #define X(_k, c, ...) str8_comp(#c), 1160 META_KIND_LIST 1161 #undef X 1162 }; 1163 1164 #define META_CURRENT_LOCATION (MetaLocation){__LINE__, 0} 1165 typedef struct { u32 line, column; } MetaLocation; 1166 1167 #define META_ENTRY_ARGUMENT_KIND_LIST \ 1168 X(None) \ 1169 X(String) \ 1170 X(Array) 1171 1172 #define X(k, ...) MetaEntryArgumentKind_## k, 1173 typedef enum {META_ENTRY_ARGUMENT_KIND_LIST} MetaEntryArgumentKind; 1174 #undef X 1175 1176 typedef struct { 1177 MetaEntryArgumentKind kind; 1178 MetaLocation location; 1179 union { 1180 s8 string; 1181 struct { 1182 s8 *strings; 1183 u64 count; 1184 }; 1185 }; 1186 } MetaEntryArgument; 1187 1188 typedef struct { 1189 MetaEntryKind kind; 1190 u32 argument_count; 1191 MetaEntryArgument *arguments; 1192 s8 name; 1193 MetaLocation location; 1194 } MetaEntry; 1195 1196 typedef struct { 1197 MetaEntry *data; 1198 da_count count; 1199 da_count capacity; 1200 s8 raw; 1201 } MetaEntryStack; 1202 1203 #define META_PARSE_TOKEN_LIST \ 1204 X('@', Entry) \ 1205 X('`', RawString) \ 1206 X('(', BeginArgs) \ 1207 X(')', EndArgs) \ 1208 X('[', BeginArray) \ 1209 X(']', EndArray) \ 1210 X('{', BeginScope) \ 1211 X('}', EndScope) 1212 1213 typedef enum { 1214 MetaParseToken_EOF, 1215 MetaParseToken_String, 1216 #define X(__1, kind, ...) MetaParseToken_## kind, 1217 META_PARSE_TOKEN_LIST 1218 #undef X 1219 MetaParseToken_Count, 1220 } MetaParseToken; 1221 1222 typedef union { 1223 MetaEntryKind kind; 1224 s8 string; 1225 } MetaParseUnion; 1226 1227 typedef struct { 1228 s8 s; 1229 MetaLocation location; 1230 } MetaParsePoint; 1231 1232 typedef struct { 1233 MetaParsePoint p; 1234 MetaParseUnion u; 1235 MetaParsePoint save_point; 1236 } MetaParser; 1237 1238 global char *compiler_file; 1239 global jmp_buf compiler_jmp_buf; 1240 1241 #define meta_parser_save(v) (v)->save_point = (v)->p 1242 #define meta_parser_restore(v) swap((v)->p, (v)->save_point) 1243 #define meta_parser_commit(v) meta_parser_restore(v) 1244 1245 #define meta_compiler_message(format, ...) \ 1246 fprintf(stderr, format, ##__VA_ARGS__) 1247 1248 #define meta_compiler_error_message(loc, format, ...) \ 1249 fprintf(stderr, "%s:%u:%u: error: "format, compiler_file, \ 1250 loc.line + 1, loc.column + 1, ##__VA_ARGS__) 1251 1252 #define meta_compiler_error(loc, format, ...) do { \ 1253 meta_compiler_error_message(loc, format, ##__VA_ARGS__); \ 1254 meta_error(); \ 1255 } while (0) 1256 1257 #define meta_entry_error(e, ...) meta_entry_error_column((e), (i32)(e)->location.column, __VA_ARGS__) 1258 #define meta_entry_error_column(e, column, ...) do { \ 1259 meta_compiler_error_message((e)->location, __VA_ARGS__); \ 1260 meta_entry_print((e), 2 * (column), 0); \ 1261 meta_error(); \ 1262 } while(0) 1263 1264 #define meta_entry_pair_error(e, prefix, base_kind) \ 1265 meta_entry_error(e, prefix"@%s() in @%s()\n", \ 1266 meta_entry_kind_strings[(e)->kind], \ 1267 meta_entry_kind_strings[(base_kind)]) 1268 1269 #define meta_entry_nesting_error(e, base_kind) meta_entry_pair_error(e, "invalid nesting: ", base_kind) 1270 1271 #define meta_entry_error_location(e, loc, ...) do { \ 1272 meta_compiler_error_message((loc), __VA_ARGS__); \ 1273 meta_entry_print((e), 1, (i32)(loc).column); \ 1274 meta_error(); \ 1275 } while (0) 1276 1277 function no_return void 1278 meta_error(void) 1279 { 1280 assert(0); 1281 longjmp(compiler_jmp_buf, 1); 1282 } 1283 1284 function void 1285 meta_entry_print(MetaEntry *e, i32 indent, i32 caret) 1286 { 1287 char *kind = meta_entry_kind_strings[e->kind]; 1288 if (e->kind == MetaEntryKind_BeginScope) kind = "{"; 1289 if (e->kind == MetaEntryKind_EndScope) kind = "}"; 1290 1291 fprintf(stderr, "%*s@%s", indent, "", kind); 1292 1293 if (e->argument_count) { 1294 fprintf(stderr, "("); 1295 for (u32 i = 0; i < e->argument_count; i++) { 1296 MetaEntryArgument *a = e->arguments + i; 1297 if (i != 0) fprintf(stderr, " "); 1298 if (a->kind == MetaEntryArgumentKind_Array) { 1299 fprintf(stderr, "["); 1300 for (u64 j = 0; j < a->count; j++) { 1301 if (j != 0) fprintf(stderr, " "); 1302 fprintf(stderr, "%.*s", (i32)a->strings[j].len, a->strings[j].data); 1303 } 1304 fprintf(stderr, "]"); 1305 } else { 1306 fprintf(stderr, "%.*s", (i32)a->string.len, a->string.data); 1307 } 1308 } 1309 fprintf(stderr, ")"); 1310 } 1311 if (e->name.len) fprintf(stderr, " %.*s", (i32)e->name.len, e->name.data); 1312 1313 if (caret >= 0) fprintf(stderr, "\n%*s^", indent + caret, ""); 1314 1315 fprintf(stderr, "\n"); 1316 } 1317 1318 function iz 1319 meta_lookup_string_slow(s8 *strings, iz string_count, s8 s) 1320 { 1321 // TODO(rnp): obviously this is slow 1322 iz result = -1; 1323 for (iz i = 0; i < string_count; i++) { 1324 if (s8_equal(s, strings[i])) { 1325 result = i; 1326 break; 1327 } 1328 } 1329 return result; 1330 } 1331 1332 function MetaEntryKind 1333 meta_entry_kind_from_string(s8 s) 1334 { 1335 #define X(k, ...) s8_comp(#k), 1336 read_only local_persist s8 kinds[] = {META_ENTRY_KIND_LIST}; 1337 #undef X 1338 MetaEntryKind result = MetaEntryKind_Invalid; 1339 iz id = meta_lookup_string_slow(kinds + 1, countof(kinds) - 1, s); 1340 if (id > 0) result = (MetaEntryKind)(id + 1); 1341 return result; 1342 } 1343 1344 function void 1345 meta_parser_trim(MetaParser *p) 1346 { 1347 u8 *s, *end = p->p.s.data + p->p.s.len; 1348 b32 done = 0; 1349 b32 comment = 0; 1350 for (s = p->p.s.data; !done && s != end;) { 1351 switch (*s) { 1352 case '\r': case '\t': case ' ': 1353 { 1354 p->p.location.column++; 1355 }break; 1356 case '\n':{ p->p.location.line++; p->p.location.column = 0; comment = 0; }break; 1357 case '/':{ 1358 comment |= ((s + 1) != end && s[1] == '/'); 1359 if (comment) s++; 1360 } /* FALLTHROUGH */ 1361 default:{done = !comment;}break; 1362 } 1363 if (!done) s++; 1364 } 1365 p->p.s.data = s; 1366 p->p.s.len = end - s; 1367 } 1368 1369 function s8 1370 meta_parser_extract_raw_string(MetaParser *p) 1371 { 1372 s8 result = {.data = p->p.s.data}; 1373 for (; result.len < p->p.s.len; result.len++) { 1374 u8 byte = p->p.s.data[result.len]; 1375 p->p.location.column++; 1376 if (byte == '`') { 1377 break; 1378 } else if (byte == '\n') { 1379 p->p.location.column = 0; 1380 p->p.location.line++; 1381 } 1382 } 1383 p->p.s.data += (result.len + 1); 1384 p->p.s.len -= (result.len + 1); 1385 return result; 1386 } 1387 1388 function s8 1389 meta_parser_extract_string(MetaParser *p) 1390 { 1391 s8 result = {.data = p->p.s.data}; 1392 for (; result.len < p->p.s.len; result.len++) { 1393 b32 done = 0; 1394 switch (p->p.s.data[result.len]) { 1395 #define X(t, ...) case t: 1396 META_PARSE_TOKEN_LIST 1397 #undef X 1398 case ' ': case '\n': case '\r': case '\t': 1399 {done = 1;}break; 1400 case '/':{ 1401 done = (result.len + 1 < p->p.s.len) && (p->p.s.data[result.len + 1] == '/'); 1402 }break; 1403 default:{}break; 1404 } 1405 if (done) break; 1406 } 1407 p->p.location.column += (u32)result.len; 1408 p->p.s.data += result.len; 1409 p->p.s.len -= result.len; 1410 return result; 1411 } 1412 1413 function s8 1414 meta_parser_token_name(MetaParser *p, MetaParseToken t) 1415 { 1416 s8 result = s8("\"invalid\""); 1417 read_only local_persist s8 names[MetaParseToken_Count] = { 1418 [MetaParseToken_EOF] = s8_comp("\"EOF\""), 1419 #define X(k, v, ...) [MetaParseToken_## v] = s8_comp(#k), 1420 META_PARSE_TOKEN_LIST 1421 #undef X 1422 }; 1423 if (t >= 0 && t < countof(names)) result = names[t]; 1424 if (t == MetaParseToken_String) result = p->u.string; 1425 if (t == MetaParseToken_RawString) result = (s8){.data = p->u.string.data - 1, .len = p->u.string.len + 1}; 1426 return result; 1427 } 1428 1429 function MetaParseToken 1430 meta_parser_token(MetaParser *p) 1431 { 1432 MetaParseToken result = MetaParseToken_EOF; 1433 meta_parser_save(p); 1434 if (p->p.s.len > 0) { 1435 b32 chop = 1; 1436 switch (p->p.s.data[0]) { 1437 #define X(t, kind, ...) case t:{ result = MetaParseToken_## kind; }break; 1438 META_PARSE_TOKEN_LIST 1439 #undef X 1440 default:{ result = MetaParseToken_String; chop = 0; }break; 1441 } 1442 if (chop) { s8_chop(&p->p.s, 1); p->p.location.column++; } 1443 1444 if (result != MetaParseToken_RawString) meta_parser_trim(p); 1445 switch (result) { 1446 case MetaParseToken_RawString:{ p->u.string = meta_parser_extract_raw_string(p); }break; 1447 case MetaParseToken_String:{ p->u.string = meta_parser_extract_string(p); }break; 1448 1449 /* NOTE(rnp): '{' and '}' are shorthand for @BeginScope and @EndScope */ 1450 case MetaParseToken_BeginScope:{ p->u.kind = MetaEntryKind_BeginScope; }break; 1451 case MetaParseToken_EndScope:{ p->u.kind = MetaEntryKind_EndScope; }break; 1452 1453 /* NOTE(rnp): loose '[' implies implicit @Array() */ 1454 case MetaParseToken_BeginArray:{ p->u.kind = MetaEntryKind_Array; }break; 1455 1456 case MetaParseToken_Entry:{ 1457 s8 kind = meta_parser_extract_string(p); 1458 p->u.kind = meta_entry_kind_from_string(kind); 1459 if (p->u.kind == MetaEntryKind_Invalid) { 1460 meta_compiler_error(p->p.location, "invalid keyword: @%.*s\n", (i32)kind.len, kind.data); 1461 } 1462 }break; 1463 default:{}break; 1464 } 1465 meta_parser_trim(p); 1466 } 1467 1468 return result; 1469 } 1470 1471 function MetaParseToken 1472 meta_parser_peek_token(MetaParser *p) 1473 { 1474 MetaParseToken result = meta_parser_token(p); 1475 meta_parser_restore(p); 1476 return result; 1477 } 1478 1479 function void 1480 meta_parser_unexpected_token(MetaParser *p, MetaParseToken t) 1481 { 1482 meta_parser_restore(p); 1483 s8 token_name = meta_parser_token_name(p, t); 1484 meta_compiler_error(p->p.location, "unexpected token: %.*s\n", (i32)token_name.len, token_name.data); 1485 } 1486 1487 function void 1488 meta_parser_fill_argument_array(MetaParser *p, MetaEntryArgument *array, Arena *arena) 1489 { 1490 array->kind = MetaEntryArgumentKind_Array; 1491 array->strings = arena_aligned_start(*arena, alignof(s8)); 1492 array->location = p->p.location; 1493 for (MetaParseToken token = meta_parser_token(p); 1494 token != MetaParseToken_EndArray; 1495 token = meta_parser_token(p)) 1496 { 1497 switch (token) { 1498 case MetaParseToken_RawString: 1499 case MetaParseToken_String: 1500 { 1501 assert((u8 *)(array->strings + array->count) == arena->beg); 1502 *push_struct(arena, s8) = p->u.string; 1503 array->count++; 1504 }break; 1505 default:{ meta_parser_unexpected_token(p, token); }break; 1506 } 1507 } 1508 } 1509 1510 function void 1511 meta_parser_arguments(MetaParser *p, MetaEntry *e, Arena *arena) 1512 { 1513 if (meta_parser_peek_token(p) == MetaParseToken_BeginArgs) { 1514 meta_parser_commit(p); 1515 1516 e->arguments = arena_aligned_start(*arena, alignof(MetaEntryArgument)); 1517 for (MetaParseToken token = meta_parser_token(p); 1518 token != MetaParseToken_EndArgs; 1519 token = meta_parser_token(p)) 1520 { 1521 e->argument_count++; 1522 MetaEntryArgument *arg = push_struct(arena, MetaEntryArgument); 1523 switch (token) { 1524 case MetaParseToken_RawString: 1525 case MetaParseToken_String: 1526 { 1527 arg->kind = MetaEntryArgumentKind_String; 1528 arg->string = p->u.string; 1529 arg->location = p->p.location; 1530 }break; 1531 case MetaParseToken_BeginArray:{ 1532 meta_parser_fill_argument_array(p, arg, arena); 1533 }break; 1534 default:{ meta_parser_unexpected_token(p, token); }break; 1535 } 1536 } 1537 } 1538 } 1539 1540 typedef struct { 1541 MetaEntry *start; 1542 MetaEntry *one_past_last; 1543 iz consumed; 1544 } MetaEntryScope; 1545 1546 function MetaEntryScope 1547 meta_entry_extract_scope(MetaEntry *base, iz entry_count) 1548 { 1549 assert(base->kind != MetaEntryKind_BeginScope && base->kind != MetaEntryKind_EndScope); 1550 assert(entry_count > 0); 1551 1552 MetaEntryScope result = {.start = base + 1, .consumed = 1}; 1553 iz sub_scope = 0; 1554 for (MetaEntry *e = result.start; result.consumed < entry_count; result.consumed++, e++) { 1555 switch (e->kind) { 1556 case MetaEntryKind_BeginScope:{ sub_scope++; }break; 1557 case MetaEntryKind_EndScope:{ sub_scope--; }break; 1558 default:{}break; 1559 } 1560 if (sub_scope == 0) break; 1561 } 1562 1563 if (sub_scope != 0) 1564 meta_entry_error(base, "unclosed scope for entry\n"); 1565 1566 result.one_past_last = base + result.consumed; 1567 if (result.start->kind == MetaEntryKind_BeginScope) result.start++; 1568 if (result.one_past_last == result.start) result.one_past_last++; 1569 1570 return result; 1571 } 1572 1573 function MetaEntryStack 1574 meta_entry_stack_from_file(Arena *arena, char *file) 1575 { 1576 MetaParser parser = {.p.s = read_entire_file(file, arena)}; 1577 MetaEntryStack result = {.raw = parser.p.s}; 1578 1579 compiler_file = file; 1580 1581 meta_parser_trim(&parser); 1582 1583 for (MetaParseToken token = meta_parser_token(&parser); 1584 token != MetaParseToken_EOF; 1585 token = meta_parser_token(&parser)) 1586 { 1587 MetaEntry *e = da_push(arena, &result); 1588 switch (token) { 1589 case MetaParseToken_String: 1590 case MetaParseToken_RawString: 1591 { 1592 e->kind = MetaEntryKind_String; 1593 e->location = parser.save_point.location; 1594 e->name = parser.u.string; 1595 }break; 1596 1597 case MetaParseToken_BeginScope: 1598 case MetaParseToken_EndScope: 1599 { 1600 e->kind = parser.u.kind; 1601 e->location = parser.save_point.location; 1602 }break; 1603 1604 case MetaParseToken_BeginArray: 1605 case MetaParseToken_Entry: 1606 { 1607 e->kind = parser.u.kind; 1608 e->location = parser.save_point.location; 1609 1610 if (token == MetaParseToken_Entry) 1611 meta_parser_arguments(&parser, e, arena); 1612 1613 if (token == MetaParseToken_BeginArray) { 1614 MetaEntryArgument *a = e->arguments = push_struct(arena, MetaEntryArgument); 1615 e->argument_count = 1; 1616 meta_parser_fill_argument_array(&parser, a, arena); 1617 } 1618 1619 if (meta_parser_peek_token(&parser) == MetaParseToken_String) { 1620 meta_parser_commit(&parser); 1621 e->name = parser.u.string; 1622 } 1623 }break; 1624 1625 default:{ meta_parser_unexpected_token(&parser, token); }break; 1626 } 1627 } 1628 1629 return result; 1630 } 1631 1632 #define meta_entry_argument_expected(e, ...) \ 1633 meta_entry_argument_expected_((e), arg_list(s8, __VA_ARGS__)) 1634 function void 1635 meta_entry_argument_expected_(MetaEntry *e, s8 *args, uz count) 1636 { 1637 if (e->argument_count != count) { 1638 meta_compiler_error_message(e->location, "incorrect argument count for entry %s() got: %u expected: %u\n", 1639 meta_entry_kind_strings[e->kind], e->argument_count, (u32)count); 1640 fprintf(stderr, " format: @%s(", meta_entry_kind_strings[e->kind]); 1641 for (uz i = 0; i < count; i++) { 1642 if (i != 0) fprintf(stderr, ", "); 1643 fprintf(stderr, "%.*s", (i32)args[i].len, args[i].data); 1644 } 1645 fprintf(stderr, ")\n"); 1646 meta_error(); 1647 } 1648 } 1649 1650 function MetaEntryArgument 1651 meta_entry_argument_expect(MetaEntry *e, u32 index, MetaEntryArgumentKind kind) 1652 { 1653 #define X(k, ...) #k, 1654 read_only local_persist char *kinds[] = {META_ENTRY_ARGUMENT_KIND_LIST}; 1655 #undef X 1656 1657 assert(e->argument_count > index); 1658 MetaEntryArgument result = e->arguments[index]; 1659 1660 if (result.kind != kind) { 1661 meta_entry_error_location(e, result.location, "unexpected argument kind: expected %s but got: %s\n", 1662 kinds[kind], kinds[result.kind]); 1663 } 1664 1665 if (kind == MetaEntryArgumentKind_Array && result.count == 0) 1666 meta_entry_error_location(e, result.location, "array arguments must have at least 1 element\n"); 1667 1668 return result; 1669 } 1670 1671 typedef struct { da_count value; } MetaEntityID; 1672 1673 typedef struct { 1674 da_count *data; 1675 da_count count; 1676 da_count capacity; 1677 } MetaIDList; 1678 1679 typedef enum { 1680 MetaExpansionPartKind_Alignment, 1681 MetaExpansionPartKind_Conditional, 1682 MetaExpansionPartKind_EvalKind, 1683 MetaExpansionPartKind_EvalKindCount, 1684 MetaExpansionPartKind_Reference, 1685 MetaExpansionPartKind_String, 1686 } MetaExpansionPartKind; 1687 1688 typedef enum { 1689 MetaExpansionConditionalArgumentKind_Invalid, 1690 MetaExpansionConditionalArgumentKind_Number, 1691 MetaExpansionConditionalArgumentKind_Evaluation, 1692 MetaExpansionConditionalArgumentKind_Reference, 1693 } MetaExpansionConditionalArgumentKind; 1694 1695 typedef struct { 1696 MetaExpansionConditionalArgumentKind kind; 1697 union { 1698 s8 *strings; 1699 i64 number; 1700 }; 1701 } MetaExpansionConditionalArgument; 1702 1703 typedef enum { 1704 MetaExpansionOperation_Invalid, 1705 MetaExpansionOperation_LessThan, 1706 MetaExpansionOperation_GreaterThan, 1707 } MetaExpansionOperation; 1708 1709 typedef struct { 1710 MetaExpansionConditionalArgument lhs; 1711 MetaExpansionConditionalArgument rhs; 1712 MetaExpansionOperation op; 1713 u32 instruction_skip; 1714 } MetaExpansionConditional; 1715 1716 typedef struct { 1717 MetaExpansionPartKind kind; 1718 union { 1719 s8 string; 1720 s8 *strings; 1721 MetaExpansionConditional conditional; 1722 }; 1723 } MetaExpansionPart; 1724 DA_STRUCT(MetaExpansionPart, MetaExpansionPart); 1725 1726 typedef enum { 1727 MetaEmitOperationKind_Expand, 1728 MetaEmitOperationKind_FileBytes, 1729 MetaEmitOperationKind_String, 1730 } MetaEmitOperationKind; 1731 1732 typedef struct { 1733 MetaExpansionPart *parts; 1734 u32 part_count; 1735 da_count table_entity_id; 1736 } MetaEmitOperationExpansion; 1737 1738 typedef struct { 1739 union { 1740 str8 string; 1741 MetaEmitOperationExpansion expansion_operation; 1742 }; 1743 MetaEmitOperationKind kind; 1744 MetaLocation location; 1745 } MetaEmitOperation; 1746 1747 typedef struct { 1748 MetaEmitOperation *data; 1749 da_count count; 1750 da_count capacity; 1751 1752 s8 filename; 1753 } MetaEmitOperationList; 1754 1755 typedef struct { 1756 MetaEmitOperationList *data; 1757 da_count count; 1758 da_count capacity; 1759 } MetaEmitOperationListSet; 1760 1761 typedef enum { 1762 MetaShaderKind_Alias, 1763 MetaShaderKind_Compute, 1764 MetaShaderKind_Render, 1765 MetaShaderKind_Count, 1766 } MetaShaderKind; 1767 1768 typedef enum { 1769 MetaShaderPrimitiveKind_Mesh, 1770 MetaShaderPrimitiveKind_Vertex, 1771 MetaShaderPrimitiveKind_Count, 1772 } MetaShaderPrimitiveKind; 1773 1774 typedef struct { 1775 MetaShaderPrimitiveKind kind; 1776 } MetaRenderShader; 1777 1778 typedef struct { 1779 MetaShaderKind kind; 1780 MetaIDList entity_reference_ids; 1781 s8 files[2]; 1782 union { 1783 MetaEntityID alias_parent_id; 1784 MetaRenderShader render; 1785 }; 1786 } MetaShader; 1787 1788 #define META_STRUCT_FIELDS \ 1789 X(Name, name) \ 1790 X(Type, type) \ 1791 X(Elements, elements) \ 1792 1793 #define X(id, ...) MetaStructField_##id, 1794 typedef enum {META_STRUCT_FIELDS} MetaStructFields; 1795 #undef X 1796 1797 #define META_BAKE_FIELDS \ 1798 X(NameUpper, name_upper) \ 1799 X(NameLower, name_lower) \ 1800 X(Type, type) \ 1801 1802 #define X(id, ...) MetaBakeField_##id, 1803 typedef enum {META_BAKE_FIELDS} MetaBakeFields; 1804 #undef X 1805 1806 typedef struct { 1807 s8 *fields; 1808 s8 **entries; 1809 u32 field_count; 1810 u32 entry_count; 1811 union { 1812 i32 struct_info_id; 1813 }; 1814 } MetaTable; 1815 1816 typedef enum { 1817 MetaConstantKind_Integer, 1818 MetaConstantKind_Float, 1819 MetaConstantKind_Count, 1820 } MetaConstantKind; 1821 1822 typedef struct { 1823 MetaConstantKind kind; 1824 u32 name_id; 1825 union { 1826 u64 U64; 1827 f64 F64; 1828 }; 1829 } MetaConstant; 1830 1831 typedef struct { 1832 s8 reference_name; 1833 MetaEntityID resolved_id; 1834 da_count reference_count; 1835 1836 // NOTE: only used for namespacing MATLAB unions 1837 s8 scope_name; 1838 } MetaEntityReference; 1839 1840 // X(name, is_table, is_struct, struct_reference_target) 1841 #define META_ENTITY_KIND_LIST \ 1842 X(Nil, 0, 0, 0) \ 1843 X(List, 0, 0, 0) \ 1844 X(BakeParameters, 1, 1, 0) \ 1845 X(Constant, 0, 0, 0) \ 1846 X(Enumeration, 1, 0, 1) \ 1847 X(PushConstants, 1, 1, 0) \ 1848 X(Reference, 0, 0, 0) \ 1849 X(ReferenceReference, 0, 0, 0) \ 1850 X(Shader, 0, 0, 0) \ 1851 X(ShaderGroup, 0, 0, 0) \ 1852 X(Struct, 1, 1, 1) \ 1853 X(Table, 1, 0, 0) \ 1854 X(Union, 1, 1, 1) \ 1855 1856 // X(EntityKind, TypeField, ElementsField, NameField, AllowReferences, Emit) 1857 #define META_STRUCT_MAP_LIST \ 1858 X(BakeParameters, MetaBakeField_Type, -1, MetaBakeField_NameLower, 0, 1) \ 1859 X(PushConstants, MetaStructField_Type, MetaStructField_Elements, MetaStructField_Name, 0, 1) \ 1860 X(Struct, MetaStructField_Type, MetaStructField_Elements, MetaStructField_Name, 1, 1) \ 1861 X(Union, MetaStructField_Type, MetaStructField_Elements, MetaStructField_Name, 1, 0) \ 1862 1863 1864 typedef enum { 1865 #define X(name, ...) MetaEntityKind_ ##name, 1866 META_ENTITY_KIND_LIST 1867 #undef X 1868 MetaEntityKind_Count, 1869 } MetaEntityKind; 1870 1871 typedef struct { 1872 MetaEntityKind kind; 1873 MetaEntityID parent; 1874 MetaEntityID first_child; 1875 MetaEntityID next_sibling; 1876 MetaEntityID previous_sibling; 1877 MetaLocation location; 1878 union { 1879 MetaConstant constant; 1880 MetaEntityReference reference; 1881 MetaShader shader; 1882 MetaTable table; 1883 }; 1884 } MetaEntity; 1885 DA_STRUCT(MetaEntity, MetaEntity); 1886 1887 #define X(name, ...) s8_comp(#name), 1888 read_only global s8 meta_entity_kind_names[] = {META_ENTITY_KIND_LIST}; 1889 #undef X 1890 #define X(_n, table, ...) table, 1891 read_only global b8 meta_entity_kind_is_table[] = {META_ENTITY_KIND_LIST}; 1892 #undef X 1893 #define X(_n, _t, s, ...) s, 1894 read_only global b8 meta_entity_kind_is_struct[] = {META_ENTITY_KIND_LIST}; 1895 #undef X 1896 #define X(_n, _t, _s, srt, ...) srt, 1897 read_only global b8 meta_entity_kind_struct_reference_target[] = {META_ENTITY_KIND_LIST}; 1898 #undef X 1899 1900 #define X(k, ...) MetaEntityKind_##k, 1901 read_only global MetaEntityKind meta_struct_entity_kinds[] = {META_STRUCT_MAP_LIST}; 1902 #undef X 1903 #define X(_k, t, ...) t, 1904 read_only global i32 meta_struct_type_field[] = {META_STRUCT_MAP_LIST}; 1905 #undef X 1906 #define X(_k, _t, e, ...) e, 1907 read_only global i32 meta_struct_element_field[] = {META_STRUCT_MAP_LIST}; 1908 #undef X 1909 #define X(_k, _t, _e, n, ...) n, 1910 read_only global i32 meta_struct_name_field[] = {META_STRUCT_MAP_LIST}; 1911 #undef X 1912 #define X(_k, _t, _e, _n, allow, ...) allow, 1913 read_only global b8 meta_struct_allow_references[] = {META_STRUCT_MAP_LIST}; 1914 #undef X 1915 #define X(_k, _t, _e, _n, _a, emit, ...) emit, 1916 read_only global b8 meta_struct_emit[] = {META_STRUCT_MAP_LIST}; 1917 #undef X 1918 1919 typedef enum { 1920 MetaStructFlag_Union = 1 << 0, 1921 MetaStructFlag_ContainsUnion = 1 << 1, 1922 } MetaStructFlags; 1923 1924 typedef enum { 1925 MetaStructMemberFlag_ReferenceType = 1 << 0, 1926 MetaStructMemberFlag_ReferenceElements = 1 << 1, 1927 } MetaStructMemberFlags; 1928 1929 typedef struct { 1930 str8 name; 1931 1932 str8 *members; 1933 i32 *type_ids; 1934 i32 *elements; 1935 1936 MetaStructMemberFlags *member_flags; 1937 1938 u32 member_count; 1939 u32 byte_size; 1940 1941 MetaStructFlags flags; 1942 1943 MetaEntityID entity; 1944 MetaLocation location; 1945 } MetaStruct; 1946 1947 typedef struct { 1948 Arena *arena, scratch; 1949 1950 str8 filename; 1951 str8 directory; 1952 1953 MetaEntityID library_entity; 1954 MetaEntityID matlab_entity; 1955 1956 // NOTE(rnp): arrays of entity ids sorted by kind and counted by entity_kind_counts 1957 da_count *entity_kind_ids[MetaEntityKind_Count]; 1958 1959 da_count entity_kind_counts[MetaEntityKind_Count]; 1960 s8_list entity_names; 1961 MetaEntityList entities; 1962 1963 // NOTE(rnp): list of all entities referenced by shaders. needed for header string baking 1964 MetaIDList shader_entity_references; 1965 1966 // NOTE(rnp): fully resolved structs 1967 MetaStruct *struct_infos; 1968 u32 struct_infos_count; 1969 1970 // NOTE(rnp): dumb jank to support treating CudaHilbert/CudaDecode as shaders and 1971 // allowing shader names to alias. 1972 da_count base_shader_count; 1973 da_count *base_shader_ids; 1974 // NOTE(rnp): map index in the entity_kind_ids[MetaEntityKind_Shader] to base_shader_ids index 1975 da_count *base_shader_id_map; 1976 1977 1978 MetaEmitOperationListSet emit_sets[MetaEmitLang_Count]; 1979 } MetaContext; 1980 1981 function da_count 1982 meta_lookup_id_slow(da_count *v, da_count count, da_count id) 1983 { 1984 // TODO(rnp): obviously this is slow 1985 da_count result = -1; 1986 for (da_count i = 0; i < count; i++) { 1987 if (id == v[i]) { 1988 result = i; 1989 break; 1990 } 1991 } 1992 return result; 1993 } 1994 1995 function da_count 1996 meta_intern_string(MetaContext *ctx, s8_list *sv, s8 s) 1997 { 1998 da_count result = meta_lookup_string_slow(sv->data, sv->count, s); 1999 if (result < 0) { 2000 *da_push(ctx->arena, sv) = s; 2001 result = sv->count - 1; 2002 } 2003 return result; 2004 } 2005 2006 function da_count 2007 meta_intern_id(MetaContext *ctx, MetaIDList *v, da_count id) 2008 { 2009 da_count result = meta_lookup_id_slow(v->data, v->count, id); 2010 if (result < 0) { 2011 *da_push(ctx->arena, v) = id; 2012 result = v->count - 1; 2013 } 2014 return result; 2015 } 2016 2017 function da_count 2018 meta_entity_children_count(MetaContext *ctx, MetaEntityID entity_id) 2019 { 2020 MetaEntityID child = ctx->entities.data[entity_id.value].first_child; 2021 da_count result = 0; 2022 if (child.value != 0) { 2023 do { 2024 result++; 2025 child = ctx->entities.data[child.value].next_sibling; 2026 } while (child.value != ctx->entities.data[entity_id.value].first_child.value); 2027 } 2028 return result; 2029 } 2030 2031 function da_count * 2032 meta_entity_extract_children(MetaContext *ctx, MetaEntityID entity_id, da_count *children_count, Arena *arena) 2033 { 2034 *children_count = meta_entity_children_count(ctx, entity_id); 2035 da_count *result = push_array_no_zero(arena, da_count, *children_count); 2036 2037 // NOTE(rnp): children are pushed in LIFO order 2038 MetaEntity *e = ctx->entities.data + entity_id.value; 2039 da_count index = 0; 2040 MetaEntityID child = e->first_child; 2041 do { 2042 child = ctx->entities.data[child.value].previous_sibling; 2043 result[index++] = child.value; 2044 } while (child.value != e->first_child.value); 2045 2046 return result; 2047 } 2048 2049 function MetaEntity * 2050 meta_entity(MetaContext *ctx, MetaEntityID id) 2051 { 2052 assert(id.value != 0 && id.value < ctx->entities.count); 2053 MetaEntity *result = ctx->entities.data + id.value; 2054 return result; 2055 } 2056 2057 function MetaEntityID 2058 meta_root_entity_id(MetaContext *ctx) 2059 { 2060 MetaEntityID result = {0}; 2061 return result; 2062 } 2063 2064 function MetaEntityID 2065 meta_intern_entity(MetaContext *ctx, s8 name, MetaEntityKind kind, MetaEntityID parent, 2066 MetaLocation location, b32 allow_existing) 2067 { 2068 MetaEntityID result = {0}; 2069 assert(ctx->entities.data[0].kind == MetaEntityKind_Nil); 2070 assert(Between(kind, MetaEntityKind_Nil + 1, MetaEntityKind_Count - 1)); 2071 2072 da_count name_id = meta_intern_string(ctx, &ctx->entity_names, name); 2073 if (name_id < ctx->entities.count && ctx->entities.data[name_id].kind != kind) { 2074 s8 old_kind = meta_entity_kind_names[ctx->entities.data[name_id].kind]; 2075 s8 new_kind = meta_entity_kind_names[kind]; 2076 meta_compiler_error_message(location, "attempting to redefine %.*s as kind %.*s\n", 2077 (i32)name.len, name.data, (i32)new_kind.len, new_kind.data); 2078 meta_compiler_error_message(ctx->entities.data[name_id].location, "previously defined as kind %.*s\n", 2079 (i32)old_kind.len, old_kind.data); 2080 meta_error(); 2081 } else if (name_id < ctx->entities.count && !allow_existing) { 2082 meta_compiler_error_message(location, "redefinition of %.*s\n", (i32)name.len, name.data); 2083 meta_compiler_error_message(ctx->entities.data[name_id].location, "previously defined here\n"); 2084 meta_error(); 2085 } else { 2086 if (name_id < ctx->entities.count) { 2087 result.value = name_id; 2088 } else { 2089 ctx->entity_kind_counts[kind]++; 2090 MetaEntity *new = da_push(ctx->arena, &ctx->entities); 2091 new->location = location; 2092 result.value = da_index(new, &ctx->entities); 2093 } 2094 2095 MetaEntity *e = ctx->entities.data + result.value; 2096 e->kind = kind; 2097 e->parent = parent; 2098 2099 MetaEntity *p = ctx->entities.data + parent.value; 2100 e->next_sibling = p->first_child; 2101 p->first_child = result; 2102 2103 if (e->next_sibling.value == 0) 2104 e->next_sibling = p->first_child; 2105 2106 e->previous_sibling = ctx->entities.data[e->next_sibling.value].previous_sibling; 2107 ctx->entities.data[e->next_sibling.value].previous_sibling = result; 2108 ctx->entities.data[e->previous_sibling.value].next_sibling = result; 2109 } 2110 2111 return result; 2112 } 2113 2114 function MetaEntityID 2115 meta_entity_reference(MetaContext *ctx, s8 name, MetaLocation location) 2116 { 2117 MetaEntityID result = {0}; 2118 Arena scratch; 2119 DeferLoop(scratch = ctx->scratch, ctx->scratch = scratch) { 2120 s8 ref_name = push_s8_from_parts(&ctx->scratch, s8(""), s8("R"), name); 2121 result = meta_intern_entity(ctx, ref_name, MetaEntityKind_Reference, 2122 meta_root_entity_id(ctx), location, 1); 2123 MetaEntity *r = meta_entity(ctx, result); 2124 if (r->reference.reference_count == 0) 2125 ctx->entity_names.data[result.value] = push_s8(ctx->arena, ref_name); 2126 r->reference.reference_count++; 2127 r->reference.reference_name = name; 2128 } 2129 return result; 2130 } 2131 2132 function MetaEntityID 2133 meta_entity_reference_reference(MetaContext *ctx, s8 name, s8 scope_name, MetaLocation location, MetaEntityID parent, s8 prefix) 2134 { 2135 MetaEntityID result = {0}; 2136 // NOTE(rnp): base reference 2137 MetaEntityID ref_id = meta_entity_reference(ctx, name, location); 2138 2139 Arena scratch; 2140 DeferLoop(scratch = ctx->scratch, ctx->scratch = scratch) { 2141 s8 refref_name = push_s8_from_parts(&ctx->scratch, s8(""), prefix, s8("RR"), name); 2142 result = meta_intern_entity(ctx, refref_name, MetaEntityKind_ReferenceReference, 2143 parent, location, 1); 2144 2145 MetaEntity *rr = meta_entity(ctx, result); 2146 if (rr->reference.reference_count == 0) 2147 ctx->entity_names.data[result.value] = push_s8(ctx->arena, refref_name); 2148 rr->reference.reference_count++; 2149 rr->reference.reference_name = name; 2150 rr->reference.resolved_id = ref_id; 2151 rr->reference.scope_name = scope_name; 2152 } 2153 return result; 2154 } 2155 2156 function MetaEntityID 2157 meta_entity_first_child_of_kind(MetaContext *ctx, MetaEntity *e, MetaEntityKind kind) 2158 { 2159 MetaEntityID result = {0}; 2160 MetaEntityID child = e->first_child; 2161 if (child.value) do { 2162 if (ctx->entities.data[child.value].kind == kind) { 2163 result = child; 2164 break; 2165 } 2166 child = ctx->entities.data[child.value].next_sibling; 2167 } while (child.value != e->first_child.value); 2168 return result; 2169 } 2170 2171 function void 2172 meta_pack_table_begin(MetaEntry *e, MetaTable *t) 2173 { 2174 switch (e->kind) { 2175 2176 case MetaEntryKind_Bake: 2177 { 2178 meta_entry_argument_expected_(e, 0, 0); 2179 #define X(_i, name, ...) s8_comp(#name), 2180 read_only local_persist s8 bake_fields[] = {META_BAKE_FIELDS}; 2181 #undef X 2182 t->fields = bake_fields; 2183 t->field_count = countof(bake_fields); 2184 }break; 2185 2186 case MetaEntryKind_Enumeration:{ 2187 read_only local_persist s8 enumeration_fields[] = {s8_comp("name")}; 2188 t->fields = enumeration_fields; 2189 t->field_count = countof(enumeration_fields); 2190 }break; 2191 2192 case MetaEntryKind_PushConstants: 2193 case MetaEntryKind_Struct: 2194 case MetaEntryKind_Union: 2195 { 2196 meta_entry_argument_expected_(e, 0, 0); 2197 #define X(_i, name, ...) s8_comp(#name), 2198 read_only local_persist s8 struct_fields[] = {META_STRUCT_FIELDS}; 2199 #undef X 2200 t->fields = struct_fields; 2201 t->field_count = countof(struct_fields); 2202 }break; 2203 2204 case MetaEntryKind_Table:{ 2205 meta_entry_argument_expected(e, s8("[field ...]")); 2206 MetaEntryArgument fields = meta_entry_argument_expect(e, 0, MetaEntryArgumentKind_Array); 2207 t->fields = fields.strings; 2208 t->field_count = (u32)fields.count; 2209 }break; 2210 2211 InvalidDefaultCase; 2212 } 2213 } 2214 2215 function i64 2216 meta_pack_table_entity(MetaContext *ctx, MetaEntry *e, i64 entry_count, s8 name, MetaEntityID parent) 2217 { 2218 MetaEntityKind entity_kind = MetaEntityKind_Nil; 2219 switch (e->kind) { 2220 case MetaEntryKind_Bake:{ entity_kind = MetaEntityKind_BakeParameters;}break; 2221 case MetaEntryKind_Enumeration:{ entity_kind = MetaEntityKind_Enumeration; }break; 2222 case MetaEntryKind_PushConstants:{entity_kind = MetaEntityKind_PushConstants; }break; 2223 case MetaEntryKind_Struct:{ entity_kind = MetaEntityKind_Struct; }break; 2224 case MetaEntryKind_Table:{ entity_kind = MetaEntityKind_Table; }break; 2225 case MetaEntryKind_Union:{ entity_kind = MetaEntityKind_Union; }break; 2226 InvalidDefaultCase; 2227 } 2228 2229 MetaEntityID entity_id = meta_intern_entity(ctx, name, entity_kind, parent, e->location, 0); 2230 2231 MetaTable table = {0}, *t = &table; 2232 meta_pack_table_begin(e, t); 2233 2234 b32 structure = e->kind == MetaEntryKind_Struct || 2235 e->kind == MetaEntryKind_PushConstants || 2236 e->kind == MetaEntryKind_Union; 2237 2238 MetaEntryScope scope = meta_entry_extract_scope(e, entry_count); 2239 if (scope.consumed > 1) { 2240 for (MetaEntry *row = scope.start; row != scope.one_past_last; row++) { 2241 if (row->kind != MetaEntryKind_Array && row->kind != MetaEntryKind_String) 2242 meta_entry_nesting_error(row, e->kind); 2243 2244 MetaEntryArgument entries = {.count = 1}; 2245 if (row->kind == MetaEntryKind_Array) 2246 entries.count = meta_entry_argument_expect(row, 0, MetaEntryArgumentKind_Array).count; 2247 2248 if (structure && entries.count != 2 && entries.count != 3) { 2249 meta_compiler_error(row->location, "incorrect field count for @%s entry got: %zu expected: " 2250 "[name type (elements)]\n", meta_entry_kind_strings[e->kind], 2251 (size_t)entries.count); 2252 } else if (!structure && entries.count != t->field_count) { 2253 meta_compiler_error_message(row->location, "incorrect field count for @%s entry got: %zu expected: %u\n", 2254 meta_entry_kind_strings[e->kind], (size_t)entries.count, t->field_count); 2255 fprintf(stderr, " fields: ["); 2256 for (u64 i = 0; i < t->field_count; i++) { 2257 if (i != 0) fprintf(stderr, " "); 2258 fprintf(stderr, "%.*s", (i32)t->fields[i].len, t->fields[i].data); 2259 } 2260 fprintf(stderr, "]\n"); 2261 meta_error(); 2262 } 2263 2264 t->entry_count++; 2265 } 2266 2267 t->entries = push_array(ctx->arena, s8 *, t->field_count); 2268 for (u32 field = 0; field < t->field_count; field++) 2269 t->entries[field] = push_array(ctx->arena, s8, t->entry_count); 2270 2271 u32 row_index = 0; 2272 for (MetaEntry *row = scope.start; row != scope.one_past_last; row++, row_index++) { 2273 s8 *fs = &row->name; 2274 if (row->arguments) 2275 fs = row->arguments->strings; 2276 2277 for (u32 field = 0; field < t->field_count; field++) 2278 t->entries[field][row_index] = fs[field]; 2279 2280 // NOTE(rnp): if we are filling out a struct the array element count is optional 2281 // and defaults to 1. fill this out here for uniformity elsewhere in the code 2282 if (structure && row->arguments->count == 2) 2283 t->entries[2][row_index] = s8("1"); 2284 } 2285 } 2286 2287 MetaEntity *entity = meta_entity(ctx, entity_id); 2288 entity->table = table; 2289 2290 switch (e->kind) { 2291 case MetaEntryKind_Bake: 2292 case MetaEntryKind_PushConstants: 2293 case MetaEntryKind_Struct: 2294 case MetaEntryKind_Union: 2295 case MetaEntryKind_Enumeration: 2296 case MetaEntryKind_Table: 2297 {}break; 2298 2299 InvalidDefaultCase; 2300 } 2301 2302 return scope.consumed; 2303 } 2304 2305 function i64 2306 meta_pack_shader_common(MetaContext *ctx, MetaEntityID shader_id, MetaEntry *e, i64 entry_count, MetaEntityID group_entity_id) 2307 { 2308 assert(ctx->entities.data[shader_id.value].kind == MetaEntityKind_Shader); 2309 i64 result = 0; 2310 2311 switch(e->kind) { 2312 2313 case MetaEntryKind_Bake:{ 2314 e->name = push_s8_from_parts(ctx->arena, s8(""), ctx->entity_names.data[shader_id.value], s8("BakeParameters")); 2315 result = meta_pack_table_entity(ctx, e, entry_count, e->name, shader_id); 2316 }break; 2317 2318 case MetaEntryKind_PushConstants:{ 2319 e->name = push_s8_from_parts(ctx->arena, s8(""), ctx->entity_names.data[shader_id.value], s8("PushConstants")); 2320 result = meta_pack_table_entity(ctx, e, entry_count, e->name, shader_id); 2321 goto reference; 2322 }break; 2323 2324 case MetaEntryKind_ShaderAlias:{ 2325 MetaEntityID alias_id = meta_intern_entity(ctx, e->name, MetaEntityKind_Shader, group_entity_id, 2326 e->location, 0); 2327 meta_entity(ctx, alias_id)->shader.kind = MetaShaderKind_Alias; 2328 meta_entity(ctx, alias_id)->shader.alias_parent_id = shader_id; 2329 }break; 2330 2331 case MetaEntryKind_Enumeration: 2332 case MetaEntryKind_Constant: 2333 case MetaEntryKind_Struct: 2334 reference: 2335 { 2336 meta_entry_argument_expected(e); 2337 // TODO(rnp): MetaIDList.data should be of type MetaEntityID 2338 MetaEntityID ref_id = meta_entity_reference(ctx, e->name, e->location); 2339 meta_intern_id(ctx, &meta_entity(ctx, shader_id)->shader.entity_reference_ids, ref_id.value); 2340 }break; 2341 2342 default:{ meta_entry_nesting_error(e, MetaEntryKind_Shader); }break; 2343 } 2344 2345 return result; 2346 } 2347 2348 function i64 2349 meta_pack_render_shader(MetaContext *ctx, MetaEntry *entries, i64 entry_count, MetaEntityID group_entity_id) 2350 { 2351 assert(entries[0].kind == MetaEntryKind_RenderShader); 2352 2353 MetaEntityID entity_id = meta_intern_entity(ctx, entries->name, MetaEntityKind_Shader, 2354 group_entity_id, entries->location, 0); 2355 meta_entity(ctx, entity_id)->shader.kind = MetaShaderKind_Render; 2356 2357 meta_entry_argument_expected(entries); 2358 2359 MetaEntryScope scope = meta_entry_extract_scope(entries, entry_count); 2360 if (scope.consumed > 1) { 2361 for (MetaEntry *e = scope.start; e < scope.one_past_last; e++) { 2362 switch (e->kind) { 2363 2364 case MetaEntryKind_VertexShader:{ 2365 if (meta_entity(ctx, entity_id)->shader.files[0].len) 2366 meta_entry_error(e, "primitive shader file redefined\n"); 2367 meta_entity(ctx, entity_id)->shader.files[0] = meta_entry_argument_expect(e, 0, MetaEntryArgumentKind_String).string; 2368 meta_entity(ctx, entity_id)->shader.render.kind = MetaShaderPrimitiveKind_Vertex; 2369 }break; 2370 2371 case MetaEntryKind_FragmentShader:{ 2372 if (meta_entity(ctx, entity_id)->shader.files[1].len) 2373 meta_entry_error(e, "fragment shader file redefined\n"); 2374 meta_entity(ctx, entity_id)->shader.files[1] = meta_entry_argument_expect(e, 0, MetaEntryArgumentKind_String).string; 2375 }break; 2376 2377 default:{ 2378 e += meta_pack_shader_common(ctx, entity_id, e, scope.one_past_last - e, group_entity_id); 2379 }break; 2380 } 2381 } 2382 } 2383 return scope.consumed; 2384 } 2385 2386 function i64 2387 meta_pack_compute_shader(MetaContext *ctx, MetaEntry *entries, i64 entry_count, MetaEntityID group_entity_id) 2388 { 2389 assert(entries[0].kind == MetaEntryKind_Shader); 2390 2391 MetaEntityID entity_id = meta_intern_entity(ctx, entries->name, MetaEntityKind_Shader, group_entity_id, 2392 entries->location, 0); 2393 meta_entity(ctx, entity_id)->shader.kind = MetaShaderKind_Compute; 2394 2395 if (entries->argument_count > 1) { 2396 meta_entry_argument_expected(entries, s8("[file_name]")); 2397 } else if (entries->argument_count == 1) { 2398 s8 shader_file = meta_entry_argument_expect(entries, 0, MetaEntryArgumentKind_String).string; 2399 meta_entity(ctx, entity_id)->shader.files[0] = shader_file; 2400 } 2401 2402 MetaEntryScope scope = meta_entry_extract_scope(entries, entry_count); 2403 if (scope.consumed > 1) { 2404 for (MetaEntry *e = scope.start; e < scope.one_past_last; e++) 2405 e += meta_pack_shader_common(ctx, entity_id, e, scope.one_past_last - e, group_entity_id); 2406 } else { 2407 assert(scope.consumed == 1); 2408 // TODO(rnp): some functions (@Expand) expect no scope and that the next entry 2409 // is treated as in scope; here we do not want that behaviour. 2410 scope.consumed = 0; 2411 } 2412 return scope.consumed; 2413 } 2414 2415 function i64 2416 meta_pack_shader_group(MetaContext *ctx, MetaEntry *entries, i64 entry_count) 2417 { 2418 assert(entries->kind == MetaEntryKind_ShaderGroup); 2419 2420 MetaEntityID entity_id = meta_intern_entity(ctx, entries->name, MetaEntityKind_ShaderGroup, 2421 meta_root_entity_id(ctx), entries->location, 0); 2422 2423 MetaEntryScope scope = meta_entry_extract_scope(entries, entry_count); 2424 if (scope.consumed > 1) { 2425 for (MetaEntry *e = scope.start; e < scope.one_past_last; e++) { 2426 switch (e->kind) { 2427 case MetaEntryKind_RenderShader:{ 2428 e += meta_pack_render_shader(ctx, e, scope.one_past_last - e, entity_id); 2429 }break; 2430 case MetaEntryKind_Shader:{ 2431 e += meta_pack_compute_shader(ctx, e, scope.one_past_last - e, entity_id); 2432 }break; 2433 default:{meta_entry_nesting_error(e, MetaEntryKind_ShaderGroup);}break; 2434 } 2435 } 2436 } 2437 return scope.consumed; 2438 } 2439 2440 function i64 2441 meta_pack_references(MetaContext *ctx, MetaEntry *entries, i64 entry_count, MetaEntityID parent, s8 scope_name, s8 prefix) 2442 { 2443 MetaEntryScope scope = meta_entry_extract_scope(entries, entry_count); 2444 for (MetaEntry *e = scope.start; e < scope.one_past_last; e++) { 2445 switch (e->kind) { 2446 case MetaEntryKind_Struct: 2447 case MetaEntryKind_Union: 2448 { 2449 meta_entity_reference_reference(ctx, e->name, scope_name, e->location, parent, prefix); 2450 }break; 2451 default:{meta_entry_nesting_error(e, entries->kind);}break; 2452 } 2453 } 2454 return scope.consumed; 2455 } 2456 2457 function void 2458 meta_expansion_string_split(str8 string, str8 *left, str8 *inner, str8 *remainder, MetaLocation loc) 2459 { 2460 b32 found = 0; 2461 for (u8 *s = string.data, *e = s + string.length; (s + 1) != e; s++) { 2462 u32 val = (u32)'$' << 8u | (u32)'('; 2463 u32 test = (u32)s[0] << 8u | s[1]; 2464 if (test == val) { 2465 if (left) { 2466 left->data = string.data; 2467 left->length = s - string.data; 2468 } 2469 2470 u8 *start = s + 2; 2471 while (s != e && *s != ')') s++; 2472 if (s == e) { 2473 meta_compiler_error_message(loc, "unterminated expansion in raw string:\n %.*s\n", 2474 (i32)string.length, string.data); 2475 fprintf(stderr, " %.*s^\n", (i32)(start - string.data), ""); 2476 meta_error(); 2477 } 2478 2479 if (inner) { 2480 inner->data = start; 2481 inner->length = s - start; 2482 } 2483 2484 if (remainder) { 2485 remainder->data = s + 1; 2486 remainder->length = string.length - (remainder->data - string.data); 2487 } 2488 found = 1; 2489 break; 2490 } 2491 } 2492 if (!found) { 2493 if (left) *left = string; 2494 if (inner) *inner = (str8){0}; 2495 if (remainder) *remainder = (str8){0}; 2496 } 2497 } 2498 2499 function MetaExpansionPart * 2500 meta_push_expansion_part(MetaContext *ctx, Arena *arena, MetaExpansionPartList *parts, 2501 MetaExpansionPartKind kind, str8 string, MetaEntity *table, MetaLocation loc) 2502 { 2503 MetaExpansionPart *result = da_push(arena, parts); 2504 2505 result->kind = kind; 2506 switch (kind) { 2507 case MetaExpansionPartKind_Alignment: 2508 case MetaExpansionPartKind_Conditional: 2509 {}break; 2510 2511 case MetaExpansionPartKind_EvalKind: 2512 case MetaExpansionPartKind_EvalKindCount: 2513 case MetaExpansionPartKind_Reference: 2514 { 2515 assert(meta_entity_kind_is_table[table->kind]); 2516 MetaTable *t = &table->table; 2517 2518 da_count index = meta_lookup_string_slow(t->fields, t->field_count, s8_from_str8(string)); 2519 result->strings = t->entries[index]; 2520 if (index < 0) { 2521 /* TODO(rnp): fix this location to point directly at the field in the string */ 2522 s8 table_name = ctx->entity_names.data[da_index(table, &ctx->entities)]; 2523 meta_compiler_error(loc, "table \"%.*s\" does not contain member: %.*s\n", 2524 (i32)table_name.len, table_name.data, (i32)string.length, string.data); 2525 } 2526 }break; 2527 2528 case MetaExpansionPartKind_String:{ result->string = s8_from_str8(string); }break; 2529 InvalidDefaultCase; 2530 } 2531 return result; 2532 } 2533 2534 #define META_EXPANSION_TOKEN_LIST \ 2535 X('|', Alignment) \ 2536 X('%', TypeEval) \ 2537 X('#', TypeEvalElements) \ 2538 X('"', Quote) \ 2539 X('-', Dash) \ 2540 X('>', GreaterThan) \ 2541 X('<', LessThan) \ 2542 2543 typedef enum { 2544 MetaExpansionToken_EOF, 2545 MetaExpansionToken_Identifier, 2546 MetaExpansionToken_Number, 2547 MetaExpansionToken_String, 2548 #define X(__1, kind, ...) MetaExpansionToken_## kind, 2549 META_EXPANSION_TOKEN_LIST 2550 #undef X 2551 MetaExpansionToken_Count, 2552 } MetaExpansionToken; 2553 2554 read_only global s8 meta_expansion_token_strings[] = { 2555 s8_comp("EOF"), 2556 s8_comp("Indentifier"), 2557 s8_comp("Number"), 2558 s8_comp("String"), 2559 #define X(s, kind, ...) s8_comp(#s), 2560 META_EXPANSION_TOKEN_LIST 2561 #undef X 2562 }; 2563 2564 typedef struct { 2565 str8 s; 2566 union { 2567 i64 number; 2568 str8 string; 2569 }; 2570 str8 save; 2571 MetaLocation loc; 2572 } MetaExpansionParser; 2573 2574 #define meta_expansion_save(v) (v)->save = (v)->s 2575 #define meta_expansion_restore(v) swap((v)->s, (v)->save) 2576 #define meta_expansion_commit(v) meta_expansion_restore(v) 2577 2578 #define meta_expansion_expected(loc, e, g) \ 2579 meta_compiler_error(loc, "invalid expansion string: expected %.*s after %.*s\n", \ 2580 (i32)meta_expansion_token_strings[e].len, meta_expansion_token_strings[e].data, \ 2581 (i32)meta_expansion_token_strings[g].len, meta_expansion_token_strings[g].data) 2582 2583 function str8 2584 meta_expansion_extract_string(MetaExpansionParser *p) 2585 { 2586 str8 result = {.data = p->s.data}; 2587 for (; result.length < p->s.length; result.length++) { 2588 b32 done = 0; 2589 switch (p->s.data[result.length]) { 2590 #define X(t, ...) case t: 2591 META_EXPANSION_TOKEN_LIST 2592 #undef X 2593 case ' ': 2594 {done = 1;}break; 2595 default:{}break; 2596 } 2597 if (done) break; 2598 } 2599 p->s.data += result.length; 2600 p->s.length -= result.length; 2601 return result; 2602 } 2603 2604 function MetaExpansionToken 2605 meta_expansion_token(MetaExpansionParser *p) 2606 { 2607 MetaExpansionToken result = MetaExpansionToken_EOF; 2608 meta_expansion_save(p); 2609 if (p->s.length > 0) { 2610 b32 chop = 1; 2611 switch (p->s.data[0]) { 2612 #define X(t, kind, ...) case t:{ result = MetaExpansionToken_## kind; }break; 2613 META_EXPANSION_TOKEN_LIST 2614 #undef X 2615 default:{ 2616 chop = 0; 2617 if (BETWEEN(p->s.data[0], '0', '9')) result = MetaExpansionToken_Number; 2618 else result = MetaExpansionToken_Identifier; 2619 }break; 2620 } 2621 if (chop) { 2622 str8_chop(&p->s, 1); 2623 p->s = str8_trim(p->s); 2624 } 2625 2626 switch (result) { 2627 case MetaExpansionToken_Number:{ 2628 NumberConversion integer = integer_from_str8(p->s); 2629 if (integer.result != NumberConversionResult_Success) { 2630 /* TODO(rnp): point at start */ 2631 meta_compiler_error(p->loc, "invalid integer in expansion string\n"); 2632 } 2633 p->number = integer.S64; 2634 p->s = integer.unparsed; 2635 }break; 2636 case MetaExpansionToken_Identifier:{ p->string = meta_expansion_extract_string(p); }break; 2637 default:{}break; 2638 } 2639 p->s = str8_trim(p->s); 2640 } 2641 return result; 2642 } 2643 2644 function MetaExpansionPart * 2645 meta_expansion_start_conditional(MetaContext *ctx, Arena *arena, MetaExpansionPartList *ops, 2646 MetaExpansionParser *p, MetaExpansionToken token, b32 negate) 2647 { 2648 MetaExpansionPart *result = meta_push_expansion_part(ctx, arena, ops, MetaExpansionPartKind_Conditional, 2649 str8(""), 0, p->loc); 2650 switch (token) { 2651 case MetaExpansionToken_Number:{ 2652 result->conditional.lhs.kind = MetaExpansionConditionalArgumentKind_Number; 2653 result->conditional.lhs.number = negate ? -p->number : p->number; 2654 }break; 2655 default:{}break; 2656 } 2657 return result; 2658 } 2659 2660 function void 2661 meta_expansion_end_conditional(MetaExpansionPart *ep, MetaExpansionParser *p, MetaExpansionToken token, b32 negate) 2662 { 2663 if (ep->conditional.rhs.kind != MetaExpansionConditionalArgumentKind_Invalid) { 2664 meta_compiler_error(p->loc, "invalid expansion conditional: duplicate right hand expression: '%.*s'\n", 2665 (i32)p->save.length, p->save.data); 2666 } 2667 switch (token) { 2668 case MetaExpansionToken_Number:{ 2669 ep->conditional.rhs.kind = MetaExpansionConditionalArgumentKind_Number; 2670 ep->conditional.rhs.number = negate ? -p->number : p->number; 2671 }break; 2672 default:{}break; 2673 } 2674 } 2675 2676 function MetaExpansionPartList 2677 meta_generate_expansion_set(MetaContext *ctx, Arena *arena, str8 expansion_string, MetaEntity *table, MetaLocation loc) 2678 { 2679 MetaExpansionPartList result = {0}; 2680 str8 left = {0}, inner, remainder = expansion_string; 2681 do { 2682 meta_expansion_string_split(remainder, &left, &inner, &remainder, loc); 2683 if (left.length) meta_push_expansion_part(ctx, arena, &result, MetaExpansionPartKind_String, left, table, loc); 2684 if (inner.length) { 2685 MetaExpansionParser p[1] = {{.s = inner, .loc = loc}}; 2686 2687 MetaExpansionPart *test_part = 0; 2688 b32 count_test_parts = 0; 2689 2690 for (MetaExpansionToken token = meta_expansion_token(p); 2691 token != MetaExpansionToken_EOF; 2692 token = meta_expansion_token(p)) 2693 { 2694 if (count_test_parts) test_part->conditional.instruction_skip++; 2695 switch (token) { 2696 case MetaExpansionToken_Alignment:{ 2697 meta_push_expansion_part(ctx, arena, &result, MetaExpansionPartKind_Alignment, p->s, table, loc); 2698 }break; 2699 2700 case MetaExpansionToken_Identifier:{ 2701 meta_push_expansion_part(ctx, arena, &result, MetaExpansionPartKind_Reference, p->string, table, loc); 2702 }break; 2703 2704 case MetaExpansionToken_TypeEval: 2705 case MetaExpansionToken_TypeEvalElements: 2706 { 2707 if (meta_expansion_token(p) != MetaExpansionToken_Identifier) { 2708 loc.column += (u32)(p->save.data - expansion_string.data); 2709 meta_expansion_expected(loc, MetaExpansionToken_Identifier, token); 2710 } 2711 MetaExpansionPartKind kind = token == MetaExpansionToken_TypeEval ? 2712 MetaExpansionPartKind_EvalKind : 2713 MetaExpansionPartKind_EvalKindCount; 2714 meta_push_expansion_part(ctx, arena, &result, kind, p->string, table, loc); 2715 }break; 2716 2717 case MetaExpansionToken_Quote:{ 2718 u8 *point = p->s.data; 2719 str8 string = meta_expansion_extract_string(p); 2720 token = meta_expansion_token(p); 2721 if (token != MetaExpansionToken_Quote) { 2722 loc.column += (u32)(point - expansion_string.data); 2723 /* TODO(rnp): point at start */ 2724 meta_compiler_error(loc, "unterminated string in expansion\n"); 2725 } 2726 meta_push_expansion_part(ctx, arena, &result, MetaExpansionPartKind_String, string, table, loc); 2727 }break; 2728 2729 case MetaExpansionToken_Dash:{ 2730 token = meta_expansion_token(p); 2731 switch (token) { 2732 case MetaExpansionToken_GreaterThan:{ 2733 if (!test_part) goto error; 2734 if (test_part->conditional.lhs.kind == MetaExpansionConditionalArgumentKind_Invalid || 2735 test_part->conditional.rhs.kind == MetaExpansionConditionalArgumentKind_Invalid) 2736 { 2737 b32 lhs = test_part->conditional.lhs.kind == MetaExpansionConditionalArgumentKind_Invalid; 2738 b32 rhs = test_part->conditional.rhs.kind == MetaExpansionConditionalArgumentKind_Invalid; 2739 if (lhs && rhs) 2740 meta_compiler_error(loc, "expansion string test terminated without arguments\n"); 2741 meta_compiler_error(loc, "expansion string test terminated without %s argument\n", 2742 lhs? "left" : "right"); 2743 } 2744 count_test_parts = 1; 2745 }break; 2746 case MetaExpansionToken_Number:{ 2747 if (test_part) meta_expansion_end_conditional(test_part, p, token, 1); 2748 else test_part = meta_expansion_start_conditional(ctx, arena, &result, p, token, 1); 2749 }break; 2750 default:{ goto error; }break; 2751 } 2752 }break; 2753 2754 case MetaExpansionToken_Number:{ 2755 if (test_part) meta_expansion_end_conditional(test_part, p, token, 0); 2756 else test_part = meta_expansion_start_conditional(ctx, arena, &result, p, token, 0); 2757 }break; 2758 2759 case MetaExpansionToken_GreaterThan: 2760 case MetaExpansionToken_LessThan: 2761 { 2762 if (test_part && test_part->conditional.op != MetaExpansionOperation_Invalid) goto error; 2763 if (!test_part) { 2764 if (result.count == 0) { 2765 meta_compiler_error(p->loc, "invalid expansion conditional: missing left hand side\n"); 2766 } 2767 2768 s8 *strings = result.data[result.count - 1].strings; 2769 MetaExpansionPartKind last_kind = result.data[result.count - 1].kind; 2770 if (last_kind != MetaExpansionPartKind_EvalKindCount && 2771 last_kind != MetaExpansionPartKind_Reference) 2772 { 2773 meta_compiler_error(p->loc, "invalid expansion conditional: left hand side not numeric\n"); 2774 } 2775 result.count--; 2776 test_part = meta_expansion_start_conditional(ctx, arena, &result, p, token, 0); 2777 if (last_kind == MetaExpansionPartKind_EvalKindCount) { 2778 test_part->conditional.lhs.kind = MetaExpansionConditionalArgumentKind_Evaluation; 2779 } else { 2780 test_part->conditional.lhs.kind = MetaExpansionConditionalArgumentKind_Reference; 2781 } 2782 test_part->conditional.lhs.strings = strings; 2783 } 2784 test_part->conditional.op = token == MetaExpansionToken_LessThan ? 2785 MetaExpansionOperation_LessThan : 2786 MetaExpansionOperation_GreaterThan; 2787 }break; 2788 2789 error: 2790 default: 2791 { 2792 meta_compiler_error(loc, "invalid nested %.*s in expansion string\n", 2793 (i32)meta_expansion_token_strings[token].len, 2794 meta_expansion_token_strings[token].data); 2795 }break; 2796 } 2797 } 2798 } 2799 } while (remainder.length); 2800 return result; 2801 } 2802 2803 function s8 * 2804 meta_expand_to_s8_array(MetaContext *ctx, Arena scratch, s8 expand, MetaEntity *table, MetaLocation location) 2805 { 2806 MetaExpansionPartList parts = meta_generate_expansion_set(ctx, &scratch, str8_from_s8(expand), table, location); 2807 s8 *result = push_array(ctx->arena, s8, table->table.entry_count); 2808 for EachIndex(table->table.entry_count, expansion) { 2809 Stream sb = arena_stream(*ctx->arena); 2810 for EachIndex((u64)parts.count, part) { 2811 MetaExpansionPart *p = parts.data + part; 2812 u32 index = 0; 2813 if (p->kind == MetaExpansionPartKind_Reference) index = expansion; 2814 stream_append_s8(&sb, p->strings[index]); 2815 } 2816 result[expansion] = arena_stream_commit(ctx->arena, &sb); 2817 } 2818 return result; 2819 } 2820 2821 function i64 2822 meta_expand(MetaContext *ctx, Arena scratch, MetaEntry *e, iz entry_count, MetaEmitOperationList *ops) 2823 { 2824 assert(e->kind == MetaEntryKind_Expand); 2825 2826 /* TODO(rnp): for now this requires that the @Table came first */ 2827 meta_entry_argument_expected(e, s8("table_name")); 2828 s8 table_name = meta_entry_argument_expect(e, 0, MetaEntryArgumentKind_String).string; 2829 2830 MetaEntity *table = ctx->entities.data + meta_lookup_string_slow(ctx->entity_names.data, 2831 ctx->entity_names.count, 2832 table_name); 2833 2834 if (table < ctx->entities.data) 2835 meta_entry_error(e, "undefined table %.*s\n", (i32)table_name.len, table_name.data); 2836 if (!meta_entity_kind_is_table[table->kind]) { 2837 s8 old_kind = meta_entity_kind_names[table->kind]; 2838 s8 wanted_kind = meta_entity_kind_names[MetaEntityKind_Table]; 2839 meta_entry_error(e, "%.*s previously defined as %.*s but should be %.*s\n", 2840 (i32)table_name.len, table_name.data, 2841 (i32)old_kind.len, old_kind.data, 2842 (i32)wanted_kind.len, wanted_kind.data); 2843 } 2844 2845 MetaEntryScope scope = meta_entry_extract_scope(e, entry_count); 2846 for (MetaEntry *row = scope.start; row != scope.one_past_last; row++) { 2847 switch (row->kind) { 2848 case MetaEntryKind_String:{ 2849 if (!ops) goto error; 2850 2851 MetaExpansionPartList parts = meta_generate_expansion_set(ctx, ctx->arena, str8_from_s8(row->name), table, row->location); 2852 2853 MetaEmitOperation *op = da_push(ctx->arena, ops); 2854 op->kind = MetaEmitOperationKind_Expand; 2855 op->location = row->location; 2856 op->expansion_operation.parts = parts.data; 2857 op->expansion_operation.part_count = (u32)parts.count; 2858 op->expansion_operation.table_entity_id = da_index(table, &ctx->entities); 2859 }break; 2860 2861 case MetaEntryKind_Enumeration:{ 2862 if (ops) meta_entry_nesting_error(row, MetaEntryKind_Emit); 2863 2864 meta_entry_argument_expected(row, s8("`raw_string`")); 2865 s8 expand = meta_entry_argument_expect(row, 0, MetaEntryArgumentKind_String).string; 2866 2867 MetaEntityID entity_id = meta_intern_entity(ctx, row->name, MetaEntityKind_Enumeration, 2868 meta_root_entity_id(ctx), row->location, 0); 2869 MetaEntry entry = {.kind = MetaEntryKind_Enumeration}; 2870 MetaEntity *new = ctx->entities.data + entity_id.value; 2871 meta_pack_table_begin(&entry, &new->table); 2872 new->table.entries = push_array(ctx->arena, s8 *, new->table.field_count); 2873 new->table.entry_count = table->table.entry_count; 2874 new->table.entries[0] = meta_expand_to_s8_array(ctx, scratch, expand, table, row->location); 2875 }break; 2876 2877 case MetaEntryKind_Union:{ 2878 if (ops) meta_entry_nesting_error(row, MetaEntryKind_Emit); 2879 MetaEntryArgument fields = meta_entry_argument_expect(row, 0, MetaEntryArgumentKind_Array); 2880 if (fields.count != 2 && fields.count != 3) { 2881 meta_compiler_error(row->location, "Invalid arguments in table expansion: '%.*s'\n" 2882 "Union expansion requires field names for member names, type names, " 2883 "and optionally element counts.\n", (i32)table_name.len, table_name.data); 2884 } 2885 2886 MetaEntityID entity_id = meta_intern_entity(ctx, row->name, MetaEntityKind_Union, 2887 meta_root_entity_id(ctx), row->location, 0); 2888 MetaEntry entry = {.kind = MetaEntryKind_Union}; 2889 MetaEntity *new = ctx->entities.data + entity_id.value; 2890 meta_pack_table_begin(&entry, &new->table); 2891 new->table.entries = push_array(ctx->arena, s8 *, new->table.field_count); 2892 new->table.entry_count = table->table.entry_count; 2893 new->table.entries[MetaStructField_Name] = meta_expand_to_s8_array(ctx, scratch, fields.strings[0], 2894 table, row->location); 2895 new->table.entries[MetaStructField_Type] = meta_expand_to_s8_array(ctx, scratch, fields.strings[1], 2896 table, row->location); 2897 if (fields.count == 3) { 2898 new->table.entries[MetaStructField_Elements] = meta_expand_to_s8_array(ctx, scratch, fields.strings[2], 2899 table, row->location); 2900 } else { 2901 new->table.entries[MetaStructField_Elements] = push_array(ctx->arena, s8, table->table.entry_count); 2902 for EachIndex(new->table.entry_count, entry) 2903 new->table.entries[MetaStructField_Elements][entry] = s8("1"); 2904 } 2905 }break; 2906 2907 error: 2908 default: 2909 { 2910 meta_entry_nesting_error(row, MetaEntryKind_Expand); 2911 }break; 2912 } 2913 } 2914 return scope.consumed; 2915 } 2916 2917 function void 2918 meta_embed(MetaContext *ctx, Arena scratch, MetaEntry *e, iz entry_count) 2919 { 2920 assert(e->kind == MetaEntryKind_Embed); 2921 2922 meta_entry_argument_expected(e, s8("filename")); 2923 s8 filename = meta_entry_argument_expect(e, 0, MetaEntryArgumentKind_String).string; 2924 2925 MetaEmitOperationList *ops = da_push(ctx->arena, ctx->emit_sets + MetaEmitLang_C); 2926 if (e->name.len == 0) meta_entry_error(e, "name must be provided for output array"); 2927 2928 MetaEmitOperation *op; 2929 op = da_push(ctx->arena, ops); 2930 op->kind = MetaEmitOperationKind_String; 2931 op->string = push_str8_from_parts(ctx->arena, str8(""), str8("read_only global u8 "), str8_from_s8(e->name), str8("[] = {")); 2932 2933 op = da_push(ctx->arena, ops); 2934 op->kind = MetaEmitOperationKind_FileBytes; 2935 op->string = str8_from_s8(filename); 2936 2937 op = da_push(ctx->arena, ops); 2938 op->kind = MetaEmitOperationKind_String; 2939 op->string = str8("};"); 2940 } 2941 2942 function MetaKind 2943 meta_map_kind(s8 kind, s8 table_name, MetaLocation location) 2944 { 2945 i64 id = meta_lookup_string_slow((s8 *)meta_kind_meta_types, MetaKind_Count, kind); 2946 if (id < 0) { 2947 meta_compiler_error(location, "Invalid Kind in '%.*s' table expansion: %.*s\n", 2948 (i32)table_name.len, table_name.data, (i32)kind.len, kind.data); 2949 } 2950 MetaKind result = (MetaKind)id; 2951 return result; 2952 } 2953 2954 function MetaEmitLang 2955 meta_map_emit_lang(s8 lang, MetaEntry *e) 2956 { 2957 #define X(k, ...) s8_comp(#k), 2958 read_only local_persist s8 meta_lang_strings[] = {META_EMIT_LANG_LIST}; 2959 #undef X 2960 2961 iz id = meta_lookup_string_slow(meta_lang_strings, MetaEmitLang_Count, lang); 2962 if (id < 0) { 2963 #define X(k, ...) #k ", " 2964 meta_entry_error(e, "Unknown Emit Language: '%.*s'\nPossible Values: " 2965 META_EMIT_LANG_LIST "\n", (i32)lang.len, lang.data); 2966 #undef X 2967 } 2968 MetaEmitLang result = (MetaEmitLang)id; 2969 return result; 2970 } 2971 2972 function void 2973 meta_pack_constant(MetaContext *ctx, MetaEntry *e) 2974 { 2975 assert(e->kind == MetaEntryKind_Constant); 2976 2977 MetaEntityID entity_id = meta_intern_entity(ctx, e->name, MetaEntityKind_Constant, 2978 meta_root_entity_id(ctx), e->location, 0); 2979 2980 meta_entry_argument_expected(e, s8("value")); 2981 s8 value = meta_entry_argument_expect(e, 0, MetaEntryArgumentKind_String).string; 2982 2983 NumberConversion number = number_from_str8(str8_from_s8(value)); 2984 if (number.result != NumberConversionResult_Success || number.unparsed.length != 0) { 2985 meta_compiler_error(e->location, "Invalid integer in definition of Constant '%.*s': %.*s\n", 2986 (i32)e->name.len, e->name.data, (i32)value.len, value.data); 2987 } 2988 2989 MetaEntity *entity = meta_entity(ctx, entity_id); 2990 if (number.kind == NumberConversionKind_Float) { 2991 entity->constant.kind = MetaConstantKind_Float; 2992 entity->constant.F64 = number.F64; 2993 } else { 2994 entity->constant.kind = MetaConstantKind_Integer; 2995 entity->constant.U64 = number.U64; 2996 } 2997 } 2998 2999 function i64 3000 meta_pack_emit(MetaContext *ctx, Arena scratch, MetaEntry *e, i64 entry_count) 3001 { 3002 assert(e->kind == MetaEntryKind_Emit); 3003 3004 MetaEmitLang lang = MetaEmitLang_C; 3005 if (e->argument_count) { 3006 meta_entry_argument_expected(e, s8("emit_language")); 3007 s8 name = meta_entry_argument_expect(e, 0, MetaEntryArgumentKind_String).string; 3008 lang = meta_map_emit_lang(name, e); 3009 } 3010 3011 MetaEmitOperationList *ops = da_push(ctx->arena, ctx->emit_sets + lang); 3012 /* TODO(rnp): probably we should check this is unique */ 3013 ops->filename = e->name; 3014 3015 MetaEntryScope scope = meta_entry_extract_scope(e, entry_count); 3016 for (MetaEntry *row = scope.start; row != scope.one_past_last; row++) { 3017 switch (row->kind) { 3018 case MetaEntryKind_String:{ 3019 MetaEmitOperation *op = da_push(ctx->arena, ops); 3020 op->kind = MetaEmitOperationKind_String; 3021 op->string = str8_from_s8(row->name); 3022 op->location = row->location; 3023 }break; 3024 case MetaEntryKind_Expand:{ 3025 row += meta_expand(ctx, scratch, row, entry_count - (row - e), ops); 3026 }break; 3027 default:{ meta_entry_nesting_error(row, MetaEntryKind_Emit); }break; 3028 } 3029 } 3030 return scope.consumed; 3031 } 3032 3033 function CommandList 3034 meta_extract_emit_file_dependencies(MetaContext *ctx, Arena *arena) 3035 { 3036 CommandList result = {0}; 3037 for (iz set = 0; set < ctx->emit_sets[MetaEmitLang_C].count; set++) { 3038 MetaEmitOperationList *ops = ctx->emit_sets[MetaEmitLang_C].data + set; 3039 for (iz opcode = 0; opcode < ops->count; opcode++) { 3040 MetaEmitOperation *op = ops->data + opcode; 3041 switch (op->kind) { 3042 case MetaEmitOperationKind_FileBytes:{ 3043 str8 filename = push_str8_from_parts(arena, str8(OS_PATH_SEPARATOR), ctx->directory, op->string); 3044 *da_push(arena, &result) = (c8 *)filename.data; 3045 }break; 3046 default:{}break; 3047 } 3048 } 3049 } 3050 return result; 3051 } 3052 3053 function void 3054 metagen_push_byte_array(MetaprogramContext *m, s8 bytes) 3055 { 3056 for (iz i = 0; i < bytes.len; i++) { 3057 b32 end_line = (i != 0) && (i % 16) == 0; 3058 if (i != 0) meta_push(m, end_line ? s8(",") : s8(", ")); 3059 if (end_line) meta_end_line(m); 3060 if ((i % 16) == 0) meta_indent(m); 3061 meta_push(m, s8("0x")); 3062 meta_push_u64_hex(m, bytes.data[i]); 3063 } 3064 meta_end_line(m); 3065 } 3066 3067 function void 3068 metagen_push_table(MetaprogramContext *m, Arena scratch, str8 row_start, str8 row_end, 3069 s8 **column_strings, uz rows, uz columns) 3070 { 3071 u32 *column_widths = 0; 3072 if (columns > 1) { 3073 column_widths = push_array(&scratch, u32, (iz)columns - 1); 3074 for (uz column = 0; column < columns - 1; column++) { 3075 s8 *strings = column_strings[column]; 3076 for (uz row = 0; row < rows; row++) 3077 column_widths[column] = MAX(column_widths[column], (u32)strings[row].len); 3078 } 3079 } 3080 3081 for (uz row = 0; row < rows; row++) { 3082 meta_begin_line(m, s8_from_str8(row_start)); 3083 for (uz column = 0; column < columns; column++) { 3084 s8 text = column_strings[column][row]; 3085 meta_push(m, text); 3086 i32 pad = columns > 1 ? 1 : 0; 3087 if (column_widths && column < columns - 1) 3088 pad += (i32)column_widths[column] - (i32)text.len; 3089 if (column < columns - 1) meta_pad(m, ' ', pad); 3090 } 3091 meta_end_line(m, s8_from_str8(row_end)); 3092 } 3093 } 3094 3095 function i64 3096 meta_expansion_part_conditional_argument(MetaExpansionConditionalArgument a, u32 entry, 3097 s8 table_name, MetaLocation loc) 3098 { 3099 i64 result = 0; 3100 switch (a.kind) { 3101 case MetaExpansionConditionalArgumentKind_Number:{ 3102 result = a.number; 3103 }break; 3104 3105 case MetaExpansionConditionalArgumentKind_Evaluation: 3106 { 3107 s8 string = a.strings[entry]; 3108 MetaKind kind = meta_map_kind(string, table_name, loc); 3109 result = meta_kind_elements[kind]; 3110 }break; 3111 3112 case MetaExpansionConditionalArgumentKind_Reference:{ 3113 str8 string = str8_from_s8(a.strings[entry]); 3114 NumberConversion integer = integer_from_str8(string); 3115 if (integer.result != NumberConversionResult_Success) { 3116 meta_compiler_error(loc, "Invalid integer in '%.*s' table expansion: %.*s\n", 3117 (i32)table_name.len, table_name.data, (i32)string.length, string.data); 3118 } 3119 result = integer.S64; 3120 }break; 3121 3122 InvalidDefaultCase; 3123 } 3124 3125 return result; 3126 } 3127 3128 function b32 3129 meta_expansion_part_conditional(MetaExpansionPart *p, u32 entry, s8 table_name, MetaLocation loc) 3130 { 3131 assert(p->kind == MetaExpansionPartKind_Conditional); 3132 b32 result = 0; 3133 i64 lhs = meta_expansion_part_conditional_argument(p->conditional.lhs, entry, table_name, loc); 3134 i64 rhs = meta_expansion_part_conditional_argument(p->conditional.rhs, entry, table_name, loc); 3135 switch (p->conditional.op) { 3136 case MetaExpansionOperation_LessThan:{ result = lhs < rhs; }break; 3137 case MetaExpansionOperation_GreaterThan:{ result = lhs > rhs; }break; 3138 InvalidDefaultCase; 3139 } 3140 return result; 3141 } 3142 3143 function void 3144 metagen_run_emit(MetaprogramContext *m, MetaContext *ctx, MetaEmitOperationList *ops, s8 *evaluation_table) 3145 { 3146 for (iz opcode = 0; opcode < ops->count; opcode++) { 3147 MetaEmitOperation *op = ops->data + opcode; 3148 switch (op->kind) { 3149 case MetaEmitOperationKind_String:{ meta_push_line(m, s8_from_str8(op->string)); }break; 3150 case MetaEmitOperationKind_FileBytes:{ 3151 Arena scratch = m->scratch; 3152 str8 filename = push_str8_from_parts(&scratch, str8(OS_PATH_SEPARATOR), ctx->directory, op->string); 3153 s8 file = read_entire_file((c8 *)filename.data, &scratch); 3154 m->indentation_level++; 3155 metagen_push_byte_array(m, file); 3156 m->indentation_level--; 3157 }break; 3158 case MetaEmitOperationKind_Expand:{ 3159 Arena scratch = m->scratch; 3160 3161 MetaEmitOperationExpansion *eop = &op->expansion_operation; 3162 MetaTable *t = &ctx->entities.data[eop->table_entity_id].table; 3163 s8 table_name = ctx->entity_names.data[eop->table_entity_id]; 3164 3165 u32 alignment_count = 1; 3166 u32 evaluation_count = 0; 3167 for (u32 part = 0; part < eop->part_count; part++) { 3168 if (eop->parts[part].kind == MetaExpansionPartKind_Alignment) 3169 alignment_count++; 3170 if (eop->parts[part].kind == MetaExpansionPartKind_EvalKind || 3171 eop->parts[part].kind == MetaExpansionPartKind_EvalKindCount) 3172 evaluation_count++; 3173 } 3174 3175 MetaKind **evaluation_columns = push_array(&scratch, MetaKind *, evaluation_count); 3176 for (u32 column = 0; column < evaluation_count; column++) 3177 evaluation_columns[column] = push_array(&scratch, MetaKind, t->entry_count); 3178 3179 for (u32 part = 0; part < eop->part_count; part++) { 3180 u32 eval_column = 0; 3181 MetaExpansionPart *p = eop->parts + part; 3182 if (p->kind == MetaExpansionPartKind_EvalKind) { 3183 for (u32 entry = 0; entry < t->entry_count; entry++) { 3184 evaluation_columns[eval_column][entry] = meta_map_kind(p->strings[entry], 3185 table_name, op->location); 3186 } 3187 eval_column++; 3188 } 3189 } 3190 3191 s8 **columns = push_array(&scratch, s8 *, alignment_count); 3192 for (u32 column = 0; column < alignment_count; column++) 3193 columns[column] = push_array(&scratch, s8, t->entry_count); 3194 3195 Stream sb = arena_stream(scratch); 3196 for (u32 entry = 0; entry < t->entry_count; entry++) { 3197 u32 column = 0; 3198 u32 eval_column = 0; 3199 for (u32 part = 0; part < eop->part_count; part++) { 3200 MetaExpansionPart *p = eop->parts + part; 3201 switch (p->kind) { 3202 case MetaExpansionPartKind_Alignment:{ 3203 columns[column][entry] = arena_stream_commit_and_reset(&scratch, &sb); 3204 column++; 3205 }break; 3206 3207 case MetaExpansionPartKind_Conditional:{ 3208 if (!meta_expansion_part_conditional(p, entry, table_name, op->location)) 3209 part += p->conditional.instruction_skip; 3210 }break; 3211 3212 case MetaExpansionPartKind_EvalKind:{ 3213 s8 kind = evaluation_table[evaluation_columns[eval_column][entry]]; 3214 stream_append_s8(&sb, kind); 3215 }break; 3216 3217 case MetaExpansionPartKind_EvalKindCount:{ 3218 stream_append_u64(&sb, meta_kind_elements[evaluation_columns[eval_column][entry]]); 3219 }break; 3220 3221 case MetaExpansionPartKind_Reference: 3222 case MetaExpansionPartKind_String: 3223 { 3224 s8 string = p->kind == MetaExpansionPartKind_Reference ? p->strings[entry] : p->string; 3225 stream_append_s8(&sb, string); 3226 }break; 3227 } 3228 } 3229 3230 columns[column][entry] = arena_stream_commit_and_reset(&scratch, &sb); 3231 } 3232 metagen_push_table(m, scratch, str8(""), str8(""), columns, t->entry_count, alignment_count); 3233 }break; 3234 InvalidDefaultCase; 3235 } 3236 } 3237 meta_end_line(m); 3238 } 3239 3240 function void 3241 metagen_run_emit_set(MetaprogramContext *m, MetaContext *ctx, MetaEmitOperationListSet *emit_set, 3242 s8 *evaluation_table) 3243 { 3244 for (iz set = 0; set < emit_set->count; set++) { 3245 MetaEmitOperationList *ops = emit_set->data + set; 3246 metagen_run_emit(m, ctx, ops, evaluation_table); 3247 } 3248 } 3249 3250 function i32 3251 meta_struct_member_elements(MetaContext *ctx, MetaStruct *s, u32 member) 3252 { 3253 assert(member < s->member_count); 3254 i32 result = s->elements[member]; 3255 if (s->member_flags[member] & MetaStructMemberFlag_ReferenceElements) 3256 result = (i32)meta_entity(ctx, (MetaEntityID){result})->constant.U64; 3257 return result; 3258 } 3259 3260 function void 3261 metagen_push_counted_enum_body(MetaprogramContext *m, s8 kind, s8 prefix, s8 mid, s8 suffix, s8 *ids, iz ids_count) 3262 { 3263 iz max_id_length = 0; 3264 for (iz id = 0; id < ids_count; id++) 3265 max_id_length = MAX(max_id_length, ids[id].len); 3266 3267 for (iz id = 0; id < ids_count; id++) { 3268 meta_begin_line(m, prefix, kind, ids[id]); 3269 meta_pad(m, ' ', 1 + (i32)(max_id_length - ids[id].len)); 3270 meta_push(m, mid); 3271 meta_push_u64(m, (u64)id); 3272 meta_end_line(m, suffix); 3273 } 3274 } 3275 3276 function void 3277 metagen_push_counted_enum_body_from_ids(MetaprogramContext *m, s8 kind, s8 prefix, s8 mid, s8 suffix, 3278 da_count *ids, s8 *id_names, da_count ids_count) 3279 { 3280 i64 max_id_length = 0; 3281 for (i64 id = 0; id < ids_count; id++) 3282 max_id_length = Max(max_id_length, id_names[ids[id]].len); 3283 3284 for (i64 id = 0; id < ids_count; id++) { 3285 meta_begin_line(m, prefix, kind, id_names[ids[id]]); 3286 meta_pad(m, ' ', 1 + (i32)(max_id_length - id_names[ids[id]].len)); 3287 meta_push(m, mid); 3288 meta_push_i64(m, id); 3289 meta_end_line(m, suffix); 3290 } 3291 } 3292 3293 function void 3294 metagen_push_c_enum(MetaprogramContext *m, Arena scratch, s8 kind, s8 *ids, iz ids_count) 3295 { 3296 s8 kind_full = push_s8_from_parts(&scratch, s8(""), kind, s8("_")); 3297 meta_begin_scope(m, s8("typedef enum {")); 3298 metagen_push_counted_enum_body(m, kind_full, s8(""), s8("= "), s8(","), ids, ids_count); 3299 meta_push_line(m, kind_full, s8("Count,")); 3300 meta_end_scope(m, s8("} "), kind, s8(";\n")); 3301 } 3302 3303 function u32 3304 meta_struct_flattened_member_count(Arena scratch, MetaContext *ctx, MetaStruct *meta_struct) 3305 { 3306 struct stack_item {MetaStruct *s; u32 member_offset;} init[16]; 3307 struct { 3308 struct stack_item *data; 3309 da_count count; 3310 da_count capacity; 3311 } stack = {init, 0, countof(init)}; 3312 3313 u32 result = 0; 3314 *da_push(&scratch, &stack) = (struct stack_item){meta_struct, 0}; 3315 while (stack.count > 0) { 3316 stack.count--; 3317 MetaStruct *s = stack.data[stack.count].s; 3318 u32 member = stack.data[stack.count].member_offset; 3319 while (member < s->member_count) { 3320 if (s->members[member].length == 0) { 3321 assert(s->member_flags[member] & MetaStructMemberFlag_ReferenceType); 3322 MetaStruct *ss = ctx->struct_infos + ctx->entities.data[s->type_ids[member]].table.struct_info_id; 3323 if (ss->flags & MetaStructFlag_Union) { 3324 member++; 3325 result++; 3326 } else { 3327 *da_push(&scratch, &stack) = (struct stack_item){s, member + 1}; 3328 *da_push(&scratch, &stack) = (struct stack_item){ss, 0}; 3329 break; 3330 } 3331 } else { 3332 member++; 3333 result++; 3334 } 3335 } 3336 } 3337 return result; 3338 } 3339 3340 typedef enum { 3341 MetaPushStructStyle_C, 3342 MetaPushStructStyle_MATLAB, 3343 MetaPushStructStyle_Count, 3344 } MetaPushStructStyle; 3345 3346 typedef struct { 3347 MetaPushStructStyle layout_style; 3348 MetaPushStructStyle union_style; 3349 MetaPushStructStyle element_count_style; 3350 str8 *base_types; 3351 u8 *base_type_element_count_scales; 3352 str8 prefix; 3353 str8 suffix; 3354 str8 str_element_prefix; 3355 } MetaPushStructParameters; 3356 3357 function void 3358 meta_push_struct_body(MetaContext *ctx, MetaprogramContext *m, MetaEntity *struct_entity, 3359 MetaPushStructParameters p) 3360 { 3361 MetaStruct *meta_struct = ctx->struct_infos + struct_entity->table.struct_info_id; 3362 struct stack_item {MetaEntity *se; u32 member_offset;} init[16]; 3363 struct { 3364 struct stack_item *data; 3365 da_count count; 3366 da_count capacity; 3367 } stack = {init, 0, countof(init)}; 3368 3369 u32 flattened_member_count = meta_struct_flattened_member_count(m->scratch, ctx, meta_struct); 3370 3371 s8 *columns[2]; 3372 columns[0] = push_array(&m->scratch, s8, flattened_member_count); 3373 columns[1] = push_array(&m->scratch, s8, flattened_member_count); 3374 3375 u32 row = 0, scope = 0; 3376 *da_push(&m->scratch, &stack) = (struct stack_item){struct_entity, 0}; 3377 while (stack.count > 0) { 3378 stack.count--; 3379 MetaEntity *se = stack.data[stack.count].se; 3380 MetaStruct *s = ctx->struct_infos + se->table.struct_info_id; 3381 u32 member = stack.data[stack.count].member_offset; 3382 3383 while (member < s->member_count) { 3384 b32 type_reference = (s->member_flags[member] & MetaStructMemberFlag_ReferenceType) != 0; 3385 i32 type_id = s->type_ids[member]; 3386 s8 member_name = s8_from_str8(s->members[member]); 3387 3388 assert(member_name.len != 0 || type_reference); 3389 3390 if (s->members[member].length == 0 && 3391 (p.union_style != MetaPushStructStyle_MATLAB || ctx->entities.data[type_id].kind != MetaEntityKind_Union)) 3392 { 3393 *da_push(&m->scratch, &stack) = (struct stack_item){se, member + 1}; 3394 *da_push(&m->scratch, &stack) = (struct stack_item){ctx->entities.data + type_id, 0}; 3395 3396 MetaStruct *ss = ctx->struct_infos + ctx->entities.data[type_id].table.struct_info_id; 3397 if (p.layout_style == MetaPushStructStyle_C && ss->flags & MetaStructFlag_Union) { 3398 metagen_push_table(m, m->scratch, p.prefix, p.suffix, columns, row, 2); 3399 meta_begin_scope(m, s8("union {")); 3400 row = 0; 3401 scope++; 3402 } 3403 3404 break; 3405 } else { 3406 Stream sb = arena_stream(m->scratch); 3407 3408 b32 elements_reference = (s->member_flags[member] & MetaStructMemberFlag_ReferenceElements) != 0; 3409 3410 // NOTE(rnp): member name column 3411 { 3412 read_only local_persist str8 elements_count_open[MetaPushStructStyle_Count] = { 3413 [MetaPushStructStyle_C] = str8_comp("["), 3414 [MetaPushStructStyle_MATLAB] = str8_comp("("), 3415 }; 3416 read_only local_persist str8 elements_count_close[MetaPushStructStyle_Count] = { 3417 [MetaPushStructStyle_C] = str8_comp("]"), 3418 [MetaPushStructStyle_MATLAB] = str8_comp(")"), 3419 }; 3420 read_only local_persist i32 name_column[MetaPushStructStyle_Count] = { 3421 [MetaPushStructStyle_C] = 1, 3422 [MetaPushStructStyle_MATLAB] = 0, 3423 }; 3424 3425 u32 resolved_element_count = meta_struct_member_elements(ctx, s, member); 3426 3427 if (type_reference && p.union_style == MetaPushStructStyle_MATLAB) { 3428 MetaEntity *re = ctx->entities.data + type_id; 3429 MetaStruct *rs = ctx->struct_infos + re->table.struct_info_id; 3430 if (member_name.len == 0) { 3431 assert(rs->flags & MetaStructFlag_Union); 3432 member_name = s8("data"); 3433 } 3434 if (rs->flags & MetaStructFlag_Union) 3435 resolved_element_count *= rs->byte_size; 3436 } else if (!type_reference && p.base_type_element_count_scales) { 3437 resolved_element_count *= p.base_type_element_count_scales[type_id]; 3438 } 3439 3440 if (resolved_element_count > 1 || p.element_count_style == MetaPushStructStyle_MATLAB) { 3441 stream_append_s8s(&sb, member_name, s8_from_str8(elements_count_open[p.layout_style])); 3442 if (elements_reference && p.element_count_style != MetaPushStructStyle_MATLAB) { 3443 stream_append_s8s(&sb, s8_from_str8(p.str_element_prefix), ctx->entity_names.data[s->elements[member]]); 3444 } else { 3445 if (p.element_count_style == MetaPushStructStyle_MATLAB) 3446 stream_append_s8(&sb, s8("1, ")); 3447 stream_append_u64(&sb, resolved_element_count); 3448 } 3449 stream_append_s8(&sb, s8_from_str8(elements_count_close[p.layout_style])); 3450 columns[name_column[p.layout_style]][row] = arena_stream_commit_and_reset(&m->scratch, &sb); 3451 } else { 3452 columns[name_column[p.layout_style]][row] = member_name; 3453 } 3454 } 3455 3456 // NOTE(rnp): type column 3457 { 3458 read_only local_persist i32 type_column[MetaPushStructStyle_Count] = { 3459 [MetaPushStructStyle_C] = 0, 3460 [MetaPushStructStyle_MATLAB] = 1, 3461 }; 3462 3463 if (type_reference) { 3464 MetaEntity *re = ctx->entities.data + type_id; 3465 MetaStruct *rs = 0; 3466 3467 if (meta_entity_kind_is_struct[re->kind]) 3468 rs = ctx->struct_infos + re->table.struct_info_id; 3469 3470 if (rs && rs->flags & MetaStructFlag_Union && p.union_style == MetaPushStructStyle_MATLAB) { 3471 stream_append_s8(&sb, s8_from_str8(p.base_types[MetaKind_U8])); 3472 if (p.layout_style == MetaPushStructStyle_MATLAB) 3473 stream_append_s8(&sb, s8(" % +")); 3474 } else if (re->kind == MetaEntityKind_Enumeration && p.layout_style == MetaPushStructStyle_MATLAB) { 3475 // NOTE(rnp): matlab enumerations are int32 if we make this uint32 3476 // MATLAB won't fuck up the type when the field is assigned 3477 stream_append_s8(&sb, s8_from_str8(p.base_types[MetaKind_U32])); 3478 if (p.layout_style == MetaPushStructStyle_MATLAB) 3479 stream_append_s8(&sb, s8(" % ")); 3480 } else { 3481 if (p.layout_style == MetaPushStructStyle_MATLAB) { 3482 // NOTE(rnp): matlab has really broken requirements around sub structures 3483 // we can only use an opaque struct here 3484 stream_append_s8(&sb, s8("struct % ")); 3485 } else { 3486 s8 name = rs ? s8_from_str8(rs->name) : ctx->entity_names.data[type_id]; 3487 stream_append_s8s(&sb, s8_from_str8(p.str_element_prefix), name); 3488 } 3489 } 3490 3491 if (p.layout_style == MetaPushStructStyle_MATLAB) { 3492 stream_append_s8s(&sb, s8_from_str8(p.str_element_prefix), 3493 rs ? s8_from_str8(rs->name) : ctx->entity_names.data[type_id]); 3494 } 3495 3496 columns[type_column[p.layout_style]][row] = arena_stream_commit_and_reset(&m->scratch, &sb); 3497 } else { 3498 columns[type_column[p.layout_style]][row] = s8_from_str8(p.base_types[type_id]); 3499 } 3500 } 3501 3502 row++; 3503 member++; 3504 } 3505 } 3506 3507 if (member == s->member_count && s->flags & MetaStructFlag_Union && p.layout_style == MetaPushStructStyle_C) { 3508 metagen_push_table(m, m->scratch, p.prefix, p.suffix, columns, row, 2); 3509 while (scope > 0) { 3510 meta_end_scope(m, s8("};")); 3511 scope--; 3512 } 3513 row = 0; 3514 } 3515 } 3516 metagen_push_table(m, m->scratch, p.prefix, p.suffix, columns, row, 2); 3517 } 3518 3519 function void 3520 meta_push_matlab_properties(MetaprogramContext *m, MetaContext *ctx, MetaStruct *meta_struct) 3521 { 3522 meta_begin_scope(m, s8("properties")); 3523 { 3524 meta_push_struct_body(ctx, m, meta_entity(ctx, meta_struct->entity), (MetaPushStructParameters){ 3525 .layout_style = MetaPushStructStyle_MATLAB, 3526 .union_style = MetaPushStructStyle_MATLAB, 3527 .element_count_style = MetaPushStructStyle_MATLAB, 3528 .base_types = meta_kind_matlab_types, 3529 .suffix = str8(""), 3530 .str_element_prefix = str8(MATLAB_NAMESPACE META_NAMESPACE_UPPER), 3531 .base_type_element_count_scales = meta_kind_elements, 3532 }); 3533 } meta_end_scope(m, s8("end")); 3534 } 3535 3536 3537 function void 3538 meta_push_shader_reload_info(MetaprogramContext *m, MetaContext *ctx) 3539 { 3540 /////////////////////////////// 3541 // NOTE(rnp): reloadable infos 3542 meta_begin_scope(m, s8("read_only global " META_NAMESPACE_UPPER "ShaderKind " META_NAMESPACE_LOWER "_reloadable_shader_kinds[] = {")); 3543 { 3544 for (da_count shader = 0; shader < ctx->base_shader_count; shader++) { 3545 da_count id = ctx->base_shader_ids[shader]; 3546 meta_push_line(m, s8(META_NAMESPACE_UPPER "ShaderKind_"), ctx->entity_names.data[id], s8(",")); 3547 } 3548 } meta_end_scope(m, s8("};\n")); 3549 3550 meta_begin_scope(m, s8("read_only global s8 *" META_NAMESPACE_LOWER "_reloadable_shader_files[] = {")); 3551 { 3552 for (da_count shader = 0; shader < ctx->base_shader_count; shader++) { 3553 da_count id = ctx->base_shader_ids[shader]; 3554 MetaShader *s = &ctx->entities.data[id].shader; 3555 meta_begin_line(m, s8("(s8 []){s8_comp(\""), s->files[0], s8("\")")); 3556 if (s->files[1].len) 3557 meta_push(m, s8(", s8_comp(\""), s->files[1], s8("\")")); 3558 meta_end_line(m, s8("},")); 3559 } 3560 } meta_end_scope(m, s8("};\n")); 3561 3562 meta_begin_scope(m, s8("read_only global i32 " META_NAMESPACE_LOWER "_shader_reloadable_index_by_shader[] = {")); 3563 { 3564 for (da_count shader = 0; shader < ctx->entity_kind_counts[MetaEntityKind_Shader]; shader++) { 3565 meta_indent(m); 3566 meta_push_i64(m, ctx->base_shader_id_map[shader]); 3567 meta_end_line(m, s8(",")); 3568 } 3569 } meta_end_scope(m, s8("};\n")); 3570 3571 { 3572 u32 info_index = 0; 3573 for (da_count group = 0; group < ctx->entity_kind_counts[MetaEntityKind_ShaderGroup]; group++) { 3574 da_count id = ctx->entity_kind_ids[MetaEntityKind_ShaderGroup][group]; 3575 s8 name = ctx->entity_names.data[id]; 3576 meta_begin_line(m, s8("read_only global i32 " META_NAMESPACE_LOWER "_reloadable")); 3577 for (i64 i = 0; i < name.len; i++) { 3578 if IsUpper(name.data[i]) 3579 stream_append_byte(&m->stream, '_'); 3580 stream_append_byte(&m->stream, ToLower(name.data[i])); 3581 } 3582 3583 meta_begin_scope(m, s8("_shader_info_indices[] = {")); { 3584 MetaEntityID child = ctx->entities.data[id].first_child; 3585 do { 3586 /* TODO(rnp): store base shader list in a better format */ 3587 for (da_count bs = 0; bs < ctx->base_shader_count; bs++) { 3588 if (ctx->base_shader_ids[bs] == child.value) { 3589 meta_indent(m); 3590 meta_push_u64(m, info_index++); 3591 meta_end_line(m, s8(",")); 3592 break; 3593 } 3594 } 3595 child = ctx->entities.data[child.value].next_sibling; 3596 } while (child.value != ctx->entities.data[id].first_child.value); 3597 } meta_end_scope(m, s8("};\n")); 3598 } 3599 } 3600 3601 //////////////////////////////////// 3602 // NOTE(rnp): shader header strings 3603 meta_begin_scope(m, s8("read_only global s8 " META_NAMESPACE_LOWER "_shader_global_header_strings[] = {")); 3604 { 3605 for (da_count ref = 0; ref < ctx->shader_entity_references.count; ref++) { 3606 da_count entity_id = ctx->shader_entity_references.data[ref]; 3607 s8 entity_name = ctx->entity_names.data[entity_id]; 3608 MetaEntity *e = ctx->entities.data + entity_id; 3609 3610 switch (e->kind) { 3611 3612 case MetaEntityKind_Constant:{ 3613 meta_begin_line(m, s8("s8_comp(\"#define "), entity_name, s8(" (")); 3614 switch(e->constant.kind) { 3615 case MetaConstantKind_Integer:{ meta_push_u64(m, e->constant.U64); }break; 3616 case MetaConstantKind_Float:{ meta_push_f64(m, e->constant.F64); }break; 3617 InvalidDefaultCase; 3618 } 3619 meta_end_line(m, s8(")\\n\\n\"),")); 3620 }break; 3621 3622 case MetaEntityKind_Struct:{ 3623 meta_push_line(m, s8("s8_comp(\"\"")); 3624 meta_push_line(m, s8("\"struct "), entity_name, s8(" {\\n\"")); 3625 meta_push_struct_body(ctx, m, e, (MetaPushStructParameters){ 3626 .layout_style = MetaPushStructStyle_C, 3627 .union_style = MetaPushStructStyle_C, 3628 .element_count_style = MetaPushStructStyle_C, 3629 .base_types = meta_kind_glsl_types, 3630 .prefix = str8("\" "), 3631 .suffix = str8(";\\n\""), 3632 }); 3633 meta_push_line(m, s8("\"};\\n\"")); 3634 meta_push_line(m, s8("\"\\n\"),")); 3635 }break; 3636 3637 case MetaEntityKind_PushConstants:{ 3638 meta_push_line(m, s8("s8_comp(\"\"")); 3639 meta_push_line(m, s8("\"layout(push_constant, std430) uniform PushConstants {\\n\"")); 3640 meta_push_struct_body(ctx, m, e, (MetaPushStructParameters){ 3641 .layout_style = MetaPushStructStyle_C, 3642 .union_style = MetaPushStructStyle_C, 3643 .element_count_style = MetaPushStructStyle_C, 3644 .base_types = meta_kind_glsl_types, 3645 .prefix = str8("\" "), 3646 .suffix = str8(";\\n\""), 3647 }); 3648 meta_push_line(m, s8("\"};\\n\"")); 3649 meta_push_line(m, s8("\"\\n\"),")); 3650 }break; 3651 3652 case MetaEntityKind_Enumeration:{ 3653 s8 kind_name = push_s8_from_parts(&m->scratch, s8(""), entity_name, s8("_")); 3654 meta_push_line(m, s8("s8_comp(\"\"")); 3655 metagen_push_counted_enum_body(m, kind_name, s8("\"#define "), s8(""), s8("\\n\""), 3656 e->table.entries[0], e->table.entry_count); 3657 meta_push_line(m, s8("\"\\n\"),")); 3658 }break; 3659 3660 InvalidDefaultCase; 3661 } 3662 3663 m->scratch = ctx->scratch; 3664 } 3665 } meta_end_scope(m, s8("};\n")); 3666 3667 meta_begin_scope(m, s8("read_only global b8 " META_NAMESPACE_LOWER "_shader_has_primitive[] = {")); 3668 for (da_count bs = 0; bs < ctx->base_shader_count; bs++) { 3669 MetaShader *s = &ctx->entities.data[ctx->base_shader_ids[bs]].shader; 3670 meta_push_line(m, s->kind == MetaShaderKind_Render ? s8("1,") : s8("0,")); 3671 } 3672 meta_end_scope(m, s8("};\n")); 3673 3674 meta_begin_scope(m, s8("read_only global b8 " META_NAMESPACE_LOWER "_shader_primitive_is_vertex[] = {")); 3675 for (da_count bs = 0; bs < ctx->base_shader_count; bs++) { 3676 MetaShader *s = &ctx->entities.data[ctx->base_shader_ids[bs]].shader; 3677 b8 vertex = s->kind == MetaShaderKind_Render && s->render.kind == MetaShaderPrimitiveKind_Vertex; 3678 meta_push_line(m, vertex ? s8("1,") : s8("0,")); 3679 } 3680 meta_end_scope(m, s8("};\n")); 3681 } 3682 3683 function void 3684 meta_push_shader_bake(MetaprogramContext *m, MetaContext *ctx) 3685 { 3686 for (da_count bs = 0; bs < ctx->base_shader_count; bs++) { 3687 MetaShader *s = &ctx->entities.data[ctx->base_shader_ids[bs]].shader; 3688 3689 s8 shader_name = ctx->entity_names.data[ctx->base_shader_ids[bs]]; 3690 3691 for EachElement(s->files, it) { 3692 if (s->files[it].len > 0) { 3693 meta_begin_line(m, s8("read_only global u8 " META_NAMESPACE_LOWER "_shader_")); 3694 for (i64 i = 0; i < shader_name.len; i++) 3695 stream_append_byte(&m->stream, ToLower(shader_name.data[i])); 3696 3697 if (s->kind == MetaShaderKind_Render) 3698 meta_push(m, it == 0 ? s8("_primitive") : s8("_fragment")); 3699 3700 meta_begin_scope(m, s8("_bytes[] = {")); { 3701 Arena scratch = m->scratch; 3702 s8 filename = push_s8_from_parts(&scratch, s8(OS_PATH_SEPARATOR), s8("shaders"), s->files[it]); 3703 s8 file = read_entire_file((c8 *)filename.data, &scratch); 3704 metagen_push_byte_array(m, file); 3705 } meta_end_scope(m, s8("};\n")); 3706 } 3707 } 3708 } 3709 3710 meta_begin_scope(m, s8("read_only global s8 *" META_NAMESPACE_LOWER "_shader_data[] = {")); { 3711 for (da_count bs = 0; bs < ctx->base_shader_count; bs++) { 3712 MetaShader *s = &ctx->entities.data[ctx->base_shader_ids[bs]].shader; 3713 3714 s8 shader_name = ctx->entity_names.data[ctx->base_shader_ids[bs]]; 3715 3716 if (s->kind == MetaShaderKind_Render) { 3717 meta_begin_scope(m, s8("(s8 []){")); 3718 meta_indent(m); 3719 } else { 3720 meta_begin_line(m, s8("(s8 []){")); 3721 } 3722 3723 meta_push(m, s8("{.data = " META_NAMESPACE_LOWER "_shader_")); 3724 for (i64 i = 0; i < shader_name.len; i++) 3725 stream_append_byte(&m->stream, ToLower(shader_name.data[i])); 3726 3727 if (s->kind == MetaShaderKind_Render) 3728 meta_push(m, s8("_primitive")); 3729 3730 meta_push(m, s8("_bytes, .len = countof(" META_NAMESPACE_LOWER "_shader_")); 3731 for (i64 i = 0; i < shader_name.len; i++) 3732 stream_append_byte(&m->stream, ToLower(shader_name.data[i])); 3733 3734 if (s->kind == MetaShaderKind_Render) 3735 meta_push(m, s8("_primitive")); 3736 meta_push(m, s8("_bytes)}")); 3737 3738 if (s->kind == MetaShaderKind_Render) { 3739 meta_end_line(m, s8(",")); 3740 meta_begin_line(m, s8("{.data = " META_NAMESPACE_LOWER "_shader_")); 3741 for (i64 i = 0; i < shader_name.len; i++) 3742 stream_append_byte(&m->stream, ToLower(shader_name.data[i])); 3743 3744 meta_push(m, s8("_fragment_bytes, .len = countof(" META_NAMESPACE_LOWER "_shader_")); 3745 for (i64 i = 0; i < shader_name.len; i++) 3746 stream_append_byte(&m->stream, ToLower(shader_name.data[i])); 3747 meta_end_line(m, s8("_fragment_bytes)}")); 3748 } 3749 3750 if (s->kind == MetaShaderKind_Render) meta_end_scope(m, s8("},")); 3751 else meta_end_line(m, s8("},")); 3752 } 3753 } meta_end_scope(m, s8("};\n")); 3754 } 3755 3756 function void 3757 metagen_emit_c_s8_list(MetaprogramContext *m, s8 *strs, u32 count) 3758 { 3759 meta_begin_scope(m, s8("(s8 []){")); 3760 for (u32 index = 0; index < count; index++) 3761 meta_push_line(m, s8("s8_comp(\""), strs[index], s8("\"),")); 3762 meta_end_scope(m, s8("},")); 3763 } 3764 3765 function b32 3766 metagen_emit_c_code(MetaContext *ctx, Arena arena) 3767 { 3768 os_make_directory("generated"); 3769 char *out_meta = "generated" OS_PATH_SEPARATOR "beamformer.meta.c"; 3770 3771 MetaprogramContext m[1] = {{.stream = arena_stream(arena), .scratch = ctx->scratch}}; 3772 3773 if (setjmp(compiler_jmp_buf)) { 3774 build_fatal("Failed to generate C Code"); 3775 } 3776 3777 b32 result = 1; 3778 3779 //////////////////////////// 3780 // NOTE(rnp): shader baking 3781 { 3782 char *out_shaders = "generated" OS_PATH_SEPARATOR "beamformer_shaders.c"; 3783 char **deps = push_array(&m->scratch, char *, 2 * ctx->base_shader_count); 3784 u32 dep_count = 0; 3785 for (da_count bs = 0; bs < ctx->base_shader_count; bs++) { 3786 MetaShader *s = &ctx->entities.data[ctx->base_shader_ids[bs]].shader; 3787 deps[dep_count++] = (c8 *)push_s8_from_parts(&m->scratch, s8(OS_PATH_SEPARATOR), s8("shaders"), s->files[0]).data; 3788 if (s->files[1].len > 0) 3789 deps[dep_count++] = (c8 *)push_s8_from_parts(&m->scratch, s8(OS_PATH_SEPARATOR), s8("shaders"), s->files[1]).data; 3790 } 3791 if (needs_rebuild_(out_shaders, deps, dep_count)) { 3792 build_log_generate("Bake Shaders"); 3793 meta_push(m, c_file_header); 3794 meta_push_shader_bake(m, ctx); 3795 result &= meta_write_and_reset(m, out_shaders); 3796 } 3797 m->scratch = ctx->scratch; 3798 } 3799 3800 if (!needs_rebuild(out_meta, "beamformer.meta")) 3801 return result; 3802 3803 build_log_generate("Core C Code"); 3804 3805 meta_push(m, c_file_header); 3806 3807 ///////////////////////// 3808 // NOTE(rnp): constants 3809 { 3810 u32 integers = 0; 3811 u32 floats = 0; 3812 3813 for (da_count constant = 0; constant < ctx->entity_kind_counts[MetaEntityKind_Constant]; constant++) { 3814 da_count id = ctx->entity_kind_ids[MetaEntityKind_Constant][constant]; 3815 MetaEntity *e = ctx->entities.data + id; 3816 if (e->constant.kind == MetaConstantKind_Integer) integers++; 3817 if (e->constant.kind == MetaConstantKind_Float) floats++; 3818 } 3819 3820 u32 row_alloc_count = Max(integers, floats); 3821 s8 *columns[2]; 3822 columns[0] = push_array(&m->scratch, s8, row_alloc_count); 3823 columns[1] = push_array(&m->scratch, s8, row_alloc_count); 3824 3825 u32 row_count; 3826 3827 row_count = 0; 3828 meta_push_line(m, s8("// NOTE: Constants (Integer)")); 3829 for (da_count constant = 0; constant < ctx->entity_kind_counts[MetaEntityKind_Constant]; constant++) { 3830 da_count id = ctx->entity_kind_ids[MetaEntityKind_Constant][constant]; 3831 MetaEntity *e = ctx->entities.data + id; 3832 if (e->constant.kind == MetaConstantKind_Integer) { 3833 Stream sb = arena_stream(m->scratch); 3834 stream_append_s8(&sb, s8("(")); 3835 stream_append_u64(&sb, e->constant.U64); 3836 columns[0][row_count] = ctx->entity_names.data[id]; 3837 columns[1][row_count] = arena_stream_commit(&m->scratch, &sb); 3838 row_count++; 3839 } 3840 } 3841 metagen_push_table(m, m->scratch, str8("#define " META_NAMESPACE_UPPER), str8(")"), columns, row_count, 2); 3842 3843 row_count = 0; 3844 meta_push_line(m, s8("\n// NOTE: Constants (Float)")); 3845 for (da_count constant = 0; constant < ctx->entity_kind_counts[MetaEntityKind_Constant]; constant++) { 3846 da_count id = ctx->entity_kind_ids[MetaEntityKind_Constant][constant]; 3847 MetaEntity *e = ctx->entities.data + id; 3848 if (e->constant.kind == MetaConstantKind_Float) { 3849 Stream sb = arena_stream(m->scratch); 3850 stream_append_s8(&sb, s8("(")); 3851 stream_append_f64(&sb, e->constant.F64, 1000000); 3852 columns[0][row_count] = ctx->entity_names.data[id]; 3853 columns[1][row_count] = arena_stream_commit(&m->scratch, &sb); 3854 row_count++; 3855 } 3856 } 3857 metagen_push_table(m, m->scratch, str8("#define " META_NAMESPACE_UPPER), str8(")"), columns, row_count, 2); 3858 3859 m->scratch = ctx->scratch; 3860 } 3861 meta_push(m, s8("\n")); 3862 3863 ///////////////////////// 3864 // NOTE(rnp): enumerants 3865 for (da_count kind = 0; kind < ctx->entity_kind_counts[MetaEntityKind_Enumeration]; kind++) { 3866 da_count id = ctx->entity_kind_ids[MetaEntityKind_Enumeration][kind]; 3867 MetaEntity *e = ctx->entities.data + id; 3868 3869 s8 enum_name = push_s8_from_parts(&m->scratch, s8(""), s8(META_NAMESPACE_UPPER), 3870 ctx->entity_names.data[id]); 3871 metagen_push_c_enum(m, m->scratch, enum_name, e->table.entries[0], e->table.entry_count); 3872 m->scratch = ctx->scratch; 3873 } 3874 3875 { 3876 s8 kind = s8(META_NAMESPACE_UPPER "ShaderKind"); 3877 s8 kind_full = s8(META_NAMESPACE_UPPER "ShaderKind_"); 3878 meta_begin_scope(m, s8("typedef enum {")); 3879 metagen_push_counted_enum_body_from_ids(m, kind_full, s8(""), s8("= "), s8(","), 3880 ctx->entity_kind_ids[MetaEntityKind_Shader], ctx->entity_names.data, 3881 ctx->entity_kind_counts[MetaEntityKind_Shader]); 3882 meta_push_line(m, kind_full, s8("Count,\n")); 3883 3884 s8 *columns[2]; 3885 columns[0] = push_array(&m->scratch, s8, ctx->entity_kind_counts[MetaEntityKind_ShaderGroup] * 3); 3886 columns[1] = push_array(&m->scratch, s8, ctx->entity_kind_counts[MetaEntityKind_ShaderGroup] * 3); 3887 3888 u32 rows = 0; 3889 for (da_count group = 0; group < ctx->entity_kind_counts[MetaEntityKind_ShaderGroup]; group++) { 3890 da_count id = ctx->entity_kind_ids[MetaEntityKind_ShaderGroup][group]; 3891 MetaEntityID child = ctx->entities.data[id].first_child; 3892 s8 name = ctx->entity_names.data[id]; 3893 3894 da_count shader_count = meta_entity_children_count(ctx, (MetaEntityID){.value = id}); 3895 3896 if (child.value != 0) { 3897 // NOTE(rnp): childen pushed in LIFO order 3898 s8 first_name = ctx->entity_names.data[ctx->entities.data[child.value].previous_sibling.value]; 3899 s8 last_name = ctx->entity_names.data[child.value]; 3900 3901 columns[0][3 * group + 0] = push_s8_from_parts(&m->scratch, s8(""), kind, s8("_"), name, s8("First")); 3902 columns[1][3 * group + 0] = push_s8_from_parts(&m->scratch, s8(""), s8("= "), kind, s8("_"), first_name); 3903 3904 columns[0][3 * group + 1] = push_s8_from_parts(&m->scratch, s8(""), kind, s8("_"), name, s8("Last")); 3905 columns[1][3 * group + 1] = push_s8_from_parts(&m->scratch, s8(""),s8("= "), kind, s8("_"), last_name); 3906 3907 columns[0][3 * group + 2] = push_s8_from_parts(&m->scratch, s8(""), kind, s8("_"), name, s8("Count")); 3908 Stream sb = arena_stream(m->scratch); 3909 stream_append_s8(&sb, s8("= ")); 3910 stream_append_i64(&sb, shader_count); 3911 columns[1][3 * group + 2] = arena_stream_commit(&m->scratch, &sb); 3912 3913 rows += 3; 3914 } 3915 } 3916 metagen_push_table(m, m->scratch, str8(""), str8(","), columns, rows, 2); 3917 3918 meta_end_scope(m, s8("} "), kind, s8(";\n")); 3919 m->scratch = ctx->scratch; 3920 } 3921 3922 ////////////////////// 3923 // NOTE(rnp): structs 3924 { 3925 for EachElement(meta_struct_entity_kinds, kind_it) { 3926 if (meta_struct_emit[kind_it]) { 3927 for (da_count it = 0; it < ctx->entity_kind_counts[meta_struct_entity_kinds[kind_it]]; it++) { 3928 da_count entity = ctx->entity_kind_ids[meta_struct_entity_kinds[kind_it]][it]; 3929 3930 meta_begin_scope(m, s8("typedef struct {")); { 3931 meta_push_struct_body(ctx, m, ctx->entities.data + entity, (MetaPushStructParameters){ 3932 .layout_style = MetaPushStructStyle_C, 3933 .union_style = MetaPushStructStyle_C, 3934 .element_count_style = MetaPushStructStyle_C, 3935 .base_types = meta_kind_c_types, 3936 .suffix = str8(";"), 3937 .str_element_prefix = str8(META_NAMESPACE_UPPER), 3938 }); 3939 } meta_end_scope(m, s8("} " META_NAMESPACE_UPPER), ctx->entity_names.data[entity], s8(";")); 3940 meta_push(m, s8("\n")); 3941 } 3942 } 3943 } 3944 } 3945 3946 // NOTE: shader bake parameter union 3947 meta_begin_scope(m, s8("typedef union {")); 3948 { 3949 Arena scratch; 3950 DeferLoop(scratch = m->scratch, m->scratch = scratch) 3951 { 3952 s8 *columns[2]; 3953 columns[0] = push_array(&m->scratch, s8, ctx->entity_kind_counts[MetaEntityKind_BakeParameters]); 3954 columns[1] = push_array(&m->scratch, s8, ctx->entity_kind_counts[MetaEntityKind_BakeParameters]); 3955 3956 for (da_count bake = 0; bake < ctx->entity_kind_counts[MetaEntityKind_BakeParameters]; bake++) { 3957 da_count id = ctx->entity_kind_ids[MetaEntityKind_BakeParameters][bake]; 3958 3959 s8 bake_name = ctx->entity_names.data[id]; 3960 s8 shader_name = {.data = bake_name.data, .len = bake_name.len - s8("BakeParameters").len}; 3961 3962 columns[0][bake] = push_s8_from_parts(&m->scratch, s8(""), s8(META_NAMESPACE_UPPER), bake_name); 3963 columns[1][bake] = shader_name; 3964 } 3965 metagen_push_table(m, m->scratch, str8(""), str8(";"), columns, 3966 ctx->entity_kind_counts[MetaEntityKind_BakeParameters], 2); 3967 } 3968 } meta_end_scope(m, s8("} " META_NAMESPACE_UPPER "ShaderBakeParameters;\n")); 3969 3970 metagen_run_emit_set(m, ctx, ctx->emit_sets + MetaEmitLang_C, (s8 *)meta_kind_c_types); 3971 3972 ///////////////////////////////// 3973 // NOTE(rnp): shader info tables 3974 meta_begin_scope(m, s8("read_only global s8 " META_NAMESPACE_LOWER "_shader_names[] = {")); 3975 for (da_count shader = 0; shader < ctx->entity_kind_counts[MetaEntityKind_Shader]; shader++) { 3976 da_count id = ctx->entity_kind_ids[MetaEntityKind_Shader][shader]; 3977 meta_push_line(m, s8("s8_comp(\""), ctx->entity_names.data[id], s8("\"),")); 3978 } meta_end_scope(m, s8("};\n")); 3979 3980 meta_push_shader_reload_info(m, ctx); 3981 3982 meta_begin_scope(m, s8("read_only global i32 *" META_NAMESPACE_LOWER "_shader_header_vectors[] = {")); 3983 { 3984 for (da_count bs = 0; bs < ctx->base_shader_count; bs++) { 3985 da_count id = ctx->base_shader_ids[bs]; 3986 MetaShader *s = &ctx->entities.data[id].shader; 3987 if (s->entity_reference_ids.count) { 3988 meta_begin_line(m, s8("(i32 []){")); 3989 for (da_count ref_id = 0; ref_id < s->entity_reference_ids.count; ref_id++) { 3990 if (ref_id != 0) meta_push(m, s8(", ")); 3991 MetaEntityReference *r = &ctx->entities.data[s->entity_reference_ids.data[ref_id]].reference; 3992 meta_push_i64(m, meta_lookup_id_slow(ctx->shader_entity_references.data, 3993 ctx->shader_entity_references.count, 3994 r->resolved_id.value)); 3995 } 3996 meta_end_line(m, s8("},")); 3997 } else { 3998 meta_push_line(m, s8("0,")); 3999 } 4000 } 4001 } meta_end_scope(m, s8("};\n")); 4002 4003 meta_begin_scope(m, s8("read_only global i32 " META_NAMESPACE_LOWER "_shader_header_vector_lengths[] = {")); 4004 { 4005 for (da_count bs= 0; bs < ctx->base_shader_count; bs++) { 4006 da_count id = ctx->base_shader_ids[bs]; 4007 MetaShader *s = &ctx->entities.data[id].shader; 4008 meta_indent(m); 4009 meta_push_i64(m, s->entity_reference_ids.count); 4010 meta_end_line(m, s8(",")); 4011 } 4012 } meta_end_scope(m, s8("};\n")); 4013 4014 meta_begin_scope(m, s8("read_only global s8 *" META_NAMESPACE_LOWER "_shader_bake_parameter_names[] = {")); 4015 { 4016 for (da_count bs = 0; bs < ctx->base_shader_count; bs++) { 4017 da_count id = ctx->base_shader_ids[bs]; 4018 MetaEntity *e = ctx->entities.data + id; 4019 MetaEntityID bp_id = meta_entity_first_child_of_kind(ctx, e, MetaEntityKind_BakeParameters); 4020 if (bp_id.value != 0) { 4021 MetaEntity *bp = meta_entity(ctx, bp_id); 4022 metagen_emit_c_s8_list(m, bp->table.entries[MetaBakeField_NameUpper], bp->table.entry_count); 4023 } else { 4024 meta_push_line(m, s8("0,")); 4025 } 4026 } 4027 } meta_end_scope(m, s8("};\n")); 4028 4029 meta_begin_scope(m, s8("read_only global u32 " META_NAMESPACE_LOWER "_shader_bake_parameter_float_bits[] = {")); 4030 { 4031 for (da_count bs = 0; bs < ctx->base_shader_count; bs++) { 4032 da_count id = ctx->base_shader_ids[bs]; 4033 MetaEntity *e = ctx->entities.data + id; 4034 MetaEntityID bp_id = meta_entity_first_child_of_kind(ctx, e, MetaEntityKind_BakeParameters); 4035 u32 hex = 0; 4036 if (bp_id.value != 0) { 4037 MetaTable *t = &ctx->entities.data[bp_id.value].table; 4038 MetaStruct *s = ctx->struct_infos + t->struct_info_id; 4039 for EachIndex(s->member_count, member) { 4040 b32 type_reference = (s->member_flags[member] & MetaStructMemberFlag_ReferenceType) != 0; 4041 if (!type_reference && s->type_ids[member] == MetaKind_F32) 4042 hex |= 1 << member; 4043 } 4044 } 4045 meta_begin_line(m, s8("0x")); 4046 meta_push_u64_hex_width(m, hex, 8); 4047 meta_end_line(m, s8("UL,")); 4048 } 4049 } meta_end_scope(m, s8("};\n")); 4050 4051 meta_begin_scope(m, s8("read_only global u8 " META_NAMESPACE_LOWER "_shader_bake_parameter_counts[] = {")); 4052 { 4053 for (da_count bs = 0; bs < ctx->base_shader_count; bs++) { 4054 da_count id = ctx->base_shader_ids[bs]; 4055 MetaEntity *e = ctx->entities.data + id; 4056 MetaEntityID bp_id = meta_entity_first_child_of_kind(ctx, e, MetaEntityKind_BakeParameters); 4057 u32 count = 0; 4058 if (bp_id.value != 0) 4059 count = ctx->entities.data[bp_id.value].table.entry_count; 4060 meta_indent(m); 4061 meta_push_u64(m, count); 4062 meta_end_line(m, s8(",")); 4063 } 4064 } meta_end_scope(m, s8("};\n")); 4065 4066 meta_begin_scope(m, s8("read_only global u8 " META_NAMESPACE_LOWER "_shader_push_constant_sizes[] = {")); 4067 for (da_count bs = 0; bs < ctx->base_shader_count; bs++) { 4068 da_count id = ctx->base_shader_ids[bs]; 4069 MetaEntity *e = ctx->entities.data + id; 4070 MetaEntityID pc_id = meta_entity_first_child_of_kind(ctx, e, MetaEntityKind_PushConstants); 4071 if (pc_id.value != 0) { 4072 meta_push_line(m, s8("sizeof(" META_NAMESPACE_UPPER), ctx->entity_names.data[id], s8("PushConstants),")); 4073 } else { 4074 meta_push_line(m, s8("0,")); 4075 } 4076 } 4077 meta_end_scope(m, s8("};\n")); 4078 4079 //fprintf(stderr, "%.*s\n", (i32)m.stream.widx, m.stream.data); 4080 4081 result = meta_write_and_reset(m, out_meta); 4082 4083 return result; 4084 } 4085 4086 function b32 4087 metagen_matlab_union(MetaprogramContext *m, MetaContext *ctx, MetaStruct *u, s8 outdir, s8 namespace) 4088 { 4089 b32 result = 1; 4090 4091 Arena scratch; 4092 DeferLoop(scratch = m->scratch, m->scratch = scratch) 4093 { 4094 s8 outfile = push_s8_from_parts(&m->scratch, s8(OS_PATH_SEPARATOR), outdir, s8("Base.m")); 4095 meta_begin_scope(m, s8("classdef Base")); 4096 { 4097 meta_begin_scope(m, s8("properties (Constant)")); 4098 { 4099 meta_begin_line(m, s8("byteSize(1,1) uint32 = ")); 4100 meta_push_u64(m, u->byte_size); 4101 meta_end_line(m); 4102 } meta_end_scope(m, s8("end")); 4103 } meta_end_scope(m, s8("end")); 4104 result &= meta_end_and_write_matlab(m, (c8 *)outfile.data); 4105 } 4106 4107 for EachIndex(u->member_count, union_member) { 4108 if ((u->member_flags[union_member] & MetaStructMemberFlag_ReferenceType) == 0) { 4109 str8 type_name = meta_kind_c_types[u->type_ids[union_member]]; 4110 str8 name = u->members[union_member]; 4111 build_log_failure("%.*s:%u:%u: error: base type in MATLAB union:\n" 4112 "%.*s %.*s\n" 4113 "MATLAB unions only support Struct and Union members\n", 4114 (i32)ctx->filename.length, ctx->filename.data, u->location.line, u->location.column, 4115 (i32)name.length, name.data, (i32)type_name.length, type_name.data); 4116 result = 0; 4117 break; 4118 } 4119 4120 DeferLoop(scratch = m->scratch, m->scratch = scratch) 4121 { 4122 MetaStruct *s = ctx->struct_infos + ctx->entities.data[u->type_ids[union_member]].table.struct_info_id; 4123 s8 sub_name = s8_from_str8(u->members[union_member]); 4124 s8 outfile = push_s8_from_parts(&m->scratch, s8(""), outdir, s8(OS_PATH_SEPARATOR), sub_name, s8(".m")); 4125 meta_begin_scope(m, s8("classdef "), sub_name, s8(" < " MATLAB_NAMESPACE META_NAMESPACE_UPPER), 4126 namespace, s8(".Base")); 4127 { 4128 meta_push_matlab_properties(m, ctx, s); 4129 4130 meta_push(m, s8("\n")); 4131 4132 meta_begin_scope(m, s8("methods")); 4133 { 4134 meta_begin_scope(m, s8("function bytes = toBytes(obj)")); 4135 { 4136 meta_begin_scope(m, s8("arguments (Output)")); 4137 { 4138 meta_push_line(m, s8("bytes uint8")); 4139 } meta_end_scope(m, s8("end")); 4140 meta_push_line(m, s8("bytes = zeros(1, obj.byteSize, 'uint8');")); 4141 4142 s8 *columns[3]; 4143 columns[0] = push_array(&m->scratch, s8, s->member_count); 4144 columns[1] = push_array(&m->scratch, s8, s->member_count); 4145 columns[2] = push_array(&m->scratch, s8, s->member_count); 4146 4147 u32 offset = 1; 4148 for EachIndex(s->member_count, member) { 4149 Stream sb = arena_stream(m->scratch); 4150 4151 i32 type_id = s->type_ids[member]; 4152 4153 u32 member_size = 0; 4154 if (s->member_flags[member] & MetaStructMemberFlag_ReferenceType) { 4155 MetaStruct *ref = ctx->struct_infos + ctx->entities.data[type_id].table.struct_info_id; 4156 member_size = ref->byte_size; 4157 // TODO(rnp): arrays of structs 4158 // - calculate member count with element count multiplied in for struct members 4159 // - do a sub loop for struct arrays calling toBytes method on each struct array element 4160 if (meta_struct_member_elements(ctx, s, member) != 1) { 4161 str8 name = s->members[member]; 4162 build_log_failure("%.*s:%u:%u: error: array of structs present in struct referenced by MATLAB union:\n" 4163 "%.*s %.*s\n" 4164 "MATLAB unions do not currently support array of structs\n", 4165 (i32)ctx->filename.length, ctx->filename.data, u->location.line, u->location.column, 4166 (i32)name.length, name.data, (i32)ref->name.length, ref->name.data); 4167 } 4168 } else { 4169 member_size = meta_kind_byte_sizes[type_id]; 4170 } 4171 4172 stream_append_u64(&sb, offset); 4173 stream_append_byte(&sb, ':'); 4174 stream_append_u64(&sb, offset - 1 + member_size); 4175 stream_append_byte(&sb, ')'); 4176 offset += member_size; 4177 4178 columns[0][member] = arena_stream_commit_and_reset(&m->scratch, &sb); 4179 4180 stream_append_s8s(&sb, s8("= typecast(obj."), s8_from_str8(s->members[member])); 4181 if (s->member_flags[member] & MetaStructMemberFlag_ReferenceType) { 4182 // TODO(rnp): arrays of structs 4183 // - calculate member count with element count multiplied in for struct members 4184 // - do a sub loop for struct arrays calling toBytes method on each struct array element 4185 // lookup subtype, if union replace with byte array, else reference sub type 4186 MetaStruct *ref = ctx->struct_infos + ctx->entities.data[type_id].table.struct_info_id; 4187 if ((ref->flags & MetaStructFlag_Union) == 0) { 4188 stream_append_s8(&sb, s8(".toBytes()")); 4189 } 4190 } else { 4191 stream_append_s8(&sb, s8("(:)")); 4192 } 4193 stream_append_byte(&sb, ','); 4194 4195 columns[1][member] = arena_stream_commit_and_reset(&m->scratch, &sb); 4196 columns[2][member] = s8("'uint8');"); 4197 } 4198 4199 metagen_push_table(m, m->scratch, str8("bytes("), str8(""), columns, s->member_count, 3); 4200 } meta_end_scope(m, s8("end")); 4201 } meta_end_scope(m, s8("end")); 4202 } meta_end_scope(m, s8("end")); 4203 result &= meta_end_and_write_matlab(m, (c8 *)outfile.data); 4204 } 4205 } 4206 4207 return result; 4208 } 4209 4210 function b32 4211 metagen_emit_matlab_code(MetaContext *ctx, Arena arena) 4212 { 4213 b32 result = 1; 4214 if (!needs_rebuild(OUTPUT("matlab/OGLBeamformerLiveImagingParameters.m"), "beamformer_parameters.h", "beamformer.meta")) 4215 return result; 4216 4217 build_log_generate("MATLAB Bindings"); 4218 char *base_directory = OUTPUT("matlab"); 4219 if (!os_remove_directory(base_directory)) 4220 build_fatal("failed to remove directory: %s", base_directory); 4221 4222 if (setjmp(compiler_jmp_buf)) { 4223 os_remove_directory(base_directory); 4224 build_log_error("Failed to generate MATLAB Bindings"); 4225 return 0; 4226 } 4227 4228 os_make_directory(base_directory); 4229 4230 MetaprogramContext m[1] = {{.stream = arena_stream(arena), .scratch = ctx->scratch}}; 4231 4232 #define X(name, flag, ...) meta_push_line(m, s8(#name " (" str(flag) ")")); 4233 meta_begin_matlab_class(m, "OGLBeamformerLiveFeedbackFlags", "int32"); 4234 meta_begin_scope(m, s8("enumeration")); 4235 BEAMFORMER_LIVE_IMAGING_DIRTY_FLAG_LIST 4236 result &= meta_end_and_write_matlab(m, OUTPUT("matlab/OGLBeamformerLiveFeedbackFlags.m")); 4237 #undef X 4238 4239 #define X(name, __t, __s, elements, ...) meta_push_matlab_property(m, s8(#name), elements, s8("")); 4240 meta_begin_matlab_class(m, "OGLBeamformerLiveImagingParameters"); 4241 meta_begin_scope(m, s8("properties")); 4242 BEAMFORMER_LIVE_IMAGING_PARAMETERS_LIST 4243 result &= meta_end_and_write_matlab(m, OUTPUT("matlab/OGLBeamformerLiveImagingParameters.m")); 4244 #undef X 4245 4246 meta_begin_matlab_class(m, "OGLBeamformerShaderStage", "int32"); 4247 meta_begin_scope(m, s8("enumeration")); 4248 { 4249 da_count group_id = -1; 4250 for (da_count group = 0; group < ctx->entity_kind_counts[MetaEntityKind_ShaderGroup]; group++) { 4251 da_count id = ctx->entity_kind_ids[MetaEntityKind_ShaderGroup][group]; 4252 s8 group_name = ctx->entity_names.data[id]; 4253 if (s8_equal(group_name, s8("Compute"))) { 4254 group_id = id; 4255 break; 4256 } 4257 } 4258 if (group_id != -1) { 4259 da_count children; 4260 da_count *ids = meta_entity_extract_children(ctx, (MetaEntityID){.value = group_id}, 4261 &children, &m->scratch); 4262 if (children > 0) { 4263 metagen_push_counted_enum_body_from_ids(m, s8(""), s8(""), s8("("), s8(")"), ids, 4264 ctx->entity_names.data, children); 4265 } 4266 m->scratch = ctx->scratch; 4267 } else { 4268 build_log_failure("failed to find Compute shader group in meta info\n"); 4269 } 4270 result &= group_id != -1; 4271 } 4272 result &= meta_end_and_write_matlab(m, OUTPUT("matlab/OGLBeamformerShaderStage.m")); 4273 4274 for (da_count kind = 0; kind < ctx->entity_kind_counts[MetaEntityKind_Enumeration]; kind++) { 4275 Arena scratch = ctx->scratch; 4276 da_count id = ctx->entity_kind_ids[MetaEntityKind_Enumeration][kind]; 4277 s8 name = ctx->entity_names.data[id]; 4278 s8 output = push_s8_from_parts(&scratch, s8(""), s8(OUTPUT("matlab/OGLBeamformer")), name, s8(".m")); 4279 4280 MetaTable *etable = &ctx->entities.data[id].table; 4281 s8 *kinds = etable->entries[0]; 4282 meta_begin_scope(m, s8("classdef OGLBeamformer"), name, s8(" < int32")); 4283 meta_begin_scope(m, s8("enumeration")); 4284 s8 prefix = s8(""); 4285 if (etable->entry_count > 0 && IsDigit(kinds[0].data[0])) prefix = s8("m"); 4286 metagen_push_counted_enum_body(m, s8(""), prefix, s8("("), s8(")"), kinds, etable->entry_count); 4287 result &= meta_end_and_write_matlab(m, (c8 *)output.data); 4288 } 4289 4290 //////////////////// 4291 // NOTE: emit files 4292 { 4293 MetaEmitOperationListSet *emit_set = ctx->emit_sets + MetaEmitLang_MATLAB; 4294 for (da_count list = 0; list < emit_set->count; list++) { 4295 MetaEmitOperationList *ops = emit_set->data + list; 4296 Arena scratch = m->scratch; 4297 s8 output = push_s8_from_parts(&m->scratch, s8(""), 4298 s8(OUTPUT("matlab") OS_PATH_SEPARATOR "OGLBeamformer"), 4299 ops->filename, s8(".m")); 4300 meta_push_line(m, s8("% GENERATED CODE")); 4301 metagen_run_emit(m, ctx, ops, (s8 *)meta_kind_matlab_types); 4302 result &= meta_write_and_reset(m, (c8 *)output.data); 4303 m->scratch = scratch; 4304 } 4305 } 4306 4307 ///////////////////////// 4308 // NOTE(rnp): entities marked @MATLAB 4309 { 4310 da_count children; 4311 da_count *ids = meta_entity_extract_children(ctx, ctx->matlab_entity, &children, &m->scratch); 4312 4313 for EachIndex((u64)children, it) { 4314 MetaEntity *rr = ctx->entities.data + ids[it]; 4315 da_count ref_id = ctx->entities.data[rr->reference.resolved_id.value].reference.resolved_id.value; 4316 MetaEntity *re = ctx->entities.data + ref_id; 4317 4318 switch (re->kind) { 4319 InvalidDefaultCase; 4320 case MetaEntityKind_Union:{ 4321 Arena scratch; 4322 DeferLoop(scratch = m->scratch, m->scratch = scratch) { 4323 MetaStruct *s = ctx->struct_infos + re->table.struct_info_id; 4324 s8 name = (rr->reference.scope_name.len > 0) ? rr->reference.scope_name : s8_from_str8(s->name); 4325 s8 outdir = push_s8_from_parts(&m->scratch, s8(""), s8(OUTPUT("matlab") OS_PATH_SEPARATOR), 4326 s8("+" MATLAB_NAMESPACE META_NAMESPACE_UPPER), name); 4327 os_make_directory((c8 *)outdir.data); 4328 result &= metagen_matlab_union(m, ctx, s, outdir, name); 4329 } 4330 }break; 4331 4332 case MetaEntityKind_Struct:{ 4333 MetaStruct *s = ctx->struct_infos + re->table.struct_info_id; 4334 s8 name = (rr->reference.scope_name.len > 0) ? rr->reference.scope_name : s8_from_str8(s->name); 4335 s8 outfile = push_s8_from_parts(&m->scratch, s8(""), s8(OUTPUT("matlab") OS_PATH_SEPARATOR), 4336 s8(MATLAB_NAMESPACE META_NAMESPACE_UPPER), name, 4337 s8(".m")); 4338 meta_begin_scope(m, s8("classdef " MATLAB_NAMESPACE META_NAMESPACE_UPPER), name); 4339 { 4340 meta_push_matlab_properties(m, ctx, s); 4341 } meta_end_scope(m, s8("end")); 4342 result &= meta_end_and_write_matlab(m, (c8 *)outfile.data); 4343 }break; 4344 4345 } 4346 } 4347 m->scratch = ctx->scratch; 4348 } 4349 4350 return result; 4351 } 4352 4353 function void 4354 meta_push_helper_library_header_base(MetaprogramContext *m, MetaContext *ctx) 4355 { 4356 meta_push(m, c_file_header); 4357 meta_push_line(m, s8("#include <stdint.h>\n")); 4358 4359 ///////////////////////// 4360 // NOTE(rnp): Constants 4361 { 4362 u32 integers = 0; 4363 for (da_count constant = 0; constant < ctx->entity_kind_counts[MetaEntityKind_Constant]; constant++) { 4364 da_count id = ctx->entity_kind_ids[MetaEntityKind_Constant][constant]; 4365 MetaEntity *e = ctx->entities.data + id; 4366 if (e->constant.kind == MetaConstantKind_Integer) integers++; 4367 } 4368 4369 s8 *columns[2]; 4370 columns[0] = push_array(&m->scratch, s8, integers); 4371 columns[1] = push_array(&m->scratch, s8, integers); 4372 4373 u32 row_count = 0; 4374 meta_push_line(m, s8("// NOTE: Constants (Integer)")); 4375 for (da_count constant = 0; constant < ctx->entity_kind_counts[MetaEntityKind_Constant]; constant++) { 4376 da_count id = ctx->entity_kind_ids[MetaEntityKind_Constant][constant]; 4377 MetaEntity *e = ctx->entities.data + id; 4378 if (e->constant.kind == MetaConstantKind_Integer) { 4379 Stream sb = arena_stream(m->scratch); 4380 stream_append_s8(&sb, s8("(")); 4381 stream_append_u64(&sb, e->constant.U64); 4382 columns[0][row_count] = ctx->entity_names.data[id]; 4383 columns[1][row_count] = arena_stream_commit(&m->scratch, &sb); 4384 row_count++; 4385 } 4386 } 4387 metagen_push_table(m, m->scratch, str8("#define " META_NAMESPACE_UPPER), str8(")"), columns, row_count, 2); 4388 meta_push(m, s8("\n")); 4389 } 4390 4391 ///////////////////////// 4392 // NOTE(rnp): enumerants 4393 for (da_count kind = 0; kind < ctx->entity_kind_counts[MetaEntityKind_Enumeration]; kind++) { 4394 da_count id = ctx->entity_kind_ids[MetaEntityKind_Enumeration][kind]; 4395 MetaEntity *e = ctx->entities.data + id; 4396 4397 s8 enum_name = push_s8_from_parts(&m->scratch, s8(""), s8(META_NAMESPACE_UPPER), 4398 ctx->entity_names.data[id]); 4399 metagen_push_c_enum(m, m->scratch, enum_name, e->table.entries[0], e->table.entry_count); 4400 m->scratch = ctx->scratch; 4401 } 4402 4403 { 4404 da_count group_id = -1; 4405 for (da_count group = 0; group < ctx->entity_kind_counts[MetaEntityKind_ShaderGroup]; group++) { 4406 da_count id = ctx->entity_kind_ids[MetaEntityKind_ShaderGroup][group]; 4407 s8 group_name = ctx->entity_names.data[id]; 4408 if (s8_equal(group_name, s8("Compute"))) { 4409 group_id = id; 4410 break; 4411 } 4412 } 4413 4414 if (group_id != -1) { 4415 da_count children; 4416 da_count *ids = meta_entity_extract_children(ctx, (MetaEntityID){.value = group_id}, 4417 &children, &m->scratch); 4418 if (children > 0) { 4419 s8 kind = s8(META_NAMESPACE_UPPER "ShaderKind"); 4420 s8 kind_full = s8(META_NAMESPACE_UPPER "ShaderKind_"); 4421 meta_begin_scope(m, s8("typedef enum {")); 4422 { 4423 metagen_push_counted_enum_body_from_ids(m, kind_full, s8(""), s8("= "), s8(","), ids, 4424 ctx->entity_names.data, children); 4425 meta_push_line(m, kind_full, s8("Count,")); 4426 } meta_end_scope(m, s8("} "), kind, s8(";\n")); 4427 4428 m->scratch = ctx->scratch; 4429 4430 meta_begin_line(m, s8("#define "), kind_full, s8("ComputeCount (")); 4431 meta_push_i64(m, children); 4432 meta_end_line(m, s8(")\n")); 4433 } 4434 m->scratch = ctx->scratch; 4435 } else { 4436 build_log_failure("failed to find Compute shader group in meta info\n"); 4437 } 4438 } 4439 } 4440 4441 function void 4442 meta_entity_resolve_references(MetaContext *ctx, da_count *ids, u64 id_count) 4443 { 4444 for EachIndex(id_count, it) { 4445 MetaEntity *e = meta_entity(ctx, (MetaEntityID){ids[it]}); 4446 switch (e->kind) { 4447 InvalidDefaultCase; 4448 case MetaEntityKind_ReferenceReference:{ 4449 MetaEntity *r = meta_entity(ctx, e->reference.resolved_id); 4450 ids[it] = r->reference.resolved_id.value; 4451 }break; 4452 } 4453 } 4454 } 4455 4456 function b32 4457 metagen_emit_helper_library_header(MetaContext *ctx, Arena arena) 4458 { 4459 b32 result = 1; 4460 char *out = OUTPUT("ogl_beamformer_lib.h"); 4461 if (!needs_rebuild(out, "lib/ogl_beamformer_lib_base.h", "beamformer.meta")) 4462 return result; 4463 4464 build_log_generate("Library Header"); 4465 4466 s8 parameters_header = read_entire_file("beamformer_parameters.h", &arena); 4467 s8 base_header = read_entire_file("lib/ogl_beamformer_lib_base.h", &arena); 4468 4469 MetaprogramContext m[1] = {{.stream = arena_stream(arena), .scratch = ctx->scratch}}; 4470 4471 meta_push_helper_library_header_base(m, ctx); 4472 ///////////////////////// 4473 // NOTE(rnp): entities marked @Library 4474 { 4475 da_count children; 4476 da_count *ids = meta_entity_extract_children(ctx, ctx->library_entity, &children, &m->scratch); 4477 4478 meta_entity_resolve_references(ctx, ids, children); 4479 4480 for EachIndex((u64)children, it) { 4481 MetaEntity *e = ctx->entities.data + ids[it]; 4482 switch (e->kind) { 4483 InvalidDefaultCase; 4484 4485 case MetaEntityKind_Struct:{ 4486 meta_begin_scope(m, s8("typedef struct {")); { 4487 meta_push_struct_body(ctx, m, e, (MetaPushStructParameters){ 4488 .layout_style = MetaPushStructStyle_C, 4489 .union_style = MetaPushStructStyle_C, 4490 .element_count_style = MetaPushStructStyle_C, 4491 .base_types = meta_kind_base_c_types, 4492 .suffix = str8(";"), 4493 .str_element_prefix = str8(META_NAMESPACE_UPPER), 4494 .base_type_element_count_scales = meta_kind_elements, 4495 }); 4496 } meta_end_scope(m, s8("} " META_NAMESPACE_UPPER), ctx->entity_names.data[ids[it]], s8(";\n")); 4497 }break; 4498 4499 case MetaEntityKind_Union:{ 4500 }break; 4501 4502 } 4503 } 4504 m->scratch = ctx->scratch; 4505 } 4506 4507 metagen_run_emit_set(m, ctx, ctx->emit_sets + MetaEmitLang_CLibrary, (s8 *)meta_kind_base_c_types); 4508 4509 meta_push_line(m, s8("// END GENERATED CODE\n")); 4510 4511 meta_push(m, parameters_header, base_header); 4512 result &= meta_write_and_reset(m, out); 4513 4514 // NOTE(rnp): matlab compatible header 4515 { 4516 meta_push_helper_library_header_base(m, ctx); 4517 ///////////////////////// 4518 // NOTE(rnp): entities marked @Library 4519 { 4520 da_count children; 4521 da_count *ids = meta_entity_extract_children(ctx, ctx->library_entity, &children, &m->scratch); 4522 4523 meta_entity_resolve_references(ctx, ids, children); 4524 4525 for EachIndex((u64)children, it) { 4526 MetaEntity *e = ctx->entities.data + ids[it]; 4527 switch (e->kind) { 4528 InvalidDefaultCase; 4529 case MetaEntityKind_Struct:{ 4530 meta_begin_scope(m, s8("typedef struct {")); 4531 { 4532 meta_push_struct_body(ctx, m, e, (MetaPushStructParameters){ 4533 .layout_style = MetaPushStructStyle_C, 4534 .union_style = MetaPushStructStyle_MATLAB, 4535 .element_count_style = MetaPushStructStyle_C, 4536 .base_types = meta_kind_base_c_types, 4537 .suffix = str8(";"), 4538 .str_element_prefix = str8(META_NAMESPACE_UPPER), 4539 .base_type_element_count_scales = meta_kind_elements, 4540 }); 4541 } meta_end_scope(m, s8("} " META_NAMESPACE_UPPER), ctx->entity_names.data[ids[it]], s8(";\n")); 4542 }break; 4543 case MetaEntityKind_Union:{}break; 4544 } 4545 } 4546 m->scratch = ctx->scratch; 4547 } 4548 4549 metagen_run_emit_set(m, ctx, ctx->emit_sets + MetaEmitLang_CLibrary, (s8 *)meta_kind_base_c_types); 4550 4551 meta_push_line(m, s8("// END GENERATED CODE\n")); 4552 4553 meta_push(m, parameters_header, base_header); 4554 result &= meta_write_and_reset(m, OUTPUT("ogl_beamformer_lib_matlab.h")); 4555 } 4556 4557 { 4558 CommandList cpp = {0}; 4559 cmd_append(&arena, &cpp, PREPROCESSOR, out, COMPILER_OUTPUT, OUTPUT("ogl_beamformer_lib_python_ffi.h")); 4560 result &= run_synchronous(arena, &cpp); 4561 } 4562 4563 return result; 4564 } 4565 4566 function MetaContext * 4567 metagen_load_context(Arena *arena, char *filename) 4568 { 4569 if (setjmp(compiler_jmp_buf)) { 4570 /* NOTE(rnp): compiler error */ 4571 return 0; 4572 } 4573 4574 MetaContext *ctx = push_struct(arena, MetaContext); 4575 ctx->scratch = sub_arena(arena, MB(1), 16); 4576 ctx->arena = arena; 4577 4578 // NOTE(rnp): nil entity 4579 *da_push(ctx->arena, &ctx->entity_names) = s8("Nil"); 4580 da_push(ctx->arena, &ctx->entities); 4581 4582 MetaContext *result = ctx; 4583 4584 ctx->filename = str8_from_c_str(filename); 4585 ctx->directory = str8_chop(&ctx->filename, str8_scan_backwards(ctx->filename, OS_PATH_SEPARATOR_CHAR)); 4586 str8_chop(&ctx->filename, 1); 4587 if (ctx->directory.length <= 0) ctx->directory = str8("."); 4588 4589 Arena scratch = ctx->scratch; 4590 MetaEntryStack entries = meta_entry_stack_from_file(ctx->arena, filename); 4591 4592 for (iz i = 0; i < entries.count; i++) { 4593 MetaEntry *e = entries.data + i; 4594 4595 switch (e->kind) { 4596 case MetaEntryKind_Constant:{ 4597 meta_pack_constant(ctx, e); 4598 }break; 4599 4600 case MetaEntryKind_Emit:{ 4601 i += meta_pack_emit(ctx, scratch, e, entries.count - i); 4602 }break; 4603 4604 case MetaEntryKind_Embed:{ 4605 meta_embed(ctx, scratch, e, entries.count - i); 4606 }break; 4607 4608 case MetaEntryKind_Expand:{ 4609 i += meta_expand(ctx, scratch, e, entries.count - i, 0); 4610 }break; 4611 4612 case MetaEntryKind_Library: 4613 case MetaEntryKind_MATLAB: 4614 { 4615 if (e->kind == MetaEntryKind_Library && ctx->library_entity.value == 0) { 4616 ctx->library_entity = meta_intern_entity(ctx, s8("LibraryEntity"), MetaEntityKind_List, 4617 meta_root_entity_id(ctx), (MetaLocation){0}, 0); 4618 } 4619 4620 if (e->kind == MetaEntryKind_MATLAB && ctx->matlab_entity.value == 0) { 4621 ctx->matlab_entity = meta_intern_entity(ctx, s8("MATLABEntity"), MetaEntityKind_List, 4622 meta_root_entity_id(ctx), (MetaLocation){0}, 0); 4623 } 4624 4625 MetaEntityID parent = e->kind == MetaEntryKind_Library ? ctx->library_entity : ctx->matlab_entity; 4626 s8 prefix = e->kind == MetaEntryKind_Library ? s8("Library") : s8("MATLAB"); 4627 s8 scope_name = s8(""); 4628 if (e->argument_count > 0) 4629 scope_name = meta_entry_argument_expect(e, 0, MetaEntryArgumentKind_String).string; 4630 i += meta_pack_references(ctx, e, entries.count - i, parent, scope_name, prefix); 4631 }break; 4632 4633 case MetaEntryKind_ShaderGroup:{ 4634 i += meta_pack_shader_group(ctx, e, entries.count - i); 4635 }break; 4636 4637 case MetaEntryKind_Enumeration: 4638 case MetaEntryKind_Struct: 4639 case MetaEntryKind_Table: 4640 case MetaEntryKind_Union: 4641 { 4642 i += meta_pack_table_entity(ctx, e, entries.count - i, e->name, meta_root_entity_id(ctx)); 4643 }break; 4644 4645 default: 4646 { 4647 meta_entry_error(e, "invalid @%s() in global scope\n", meta_entry_kind_strings[e->kind]); 4648 }break; 4649 } 4650 } 4651 4652 // NOTE(rnp): sort enitity ids into sub arrays 4653 { 4654 assert(ctx->entity_kind_counts[MetaEntityKind_Nil] == 0); 4655 4656 for EachNonZeroEnumValue(MetaEntityKind, it) { 4657 if (ctx->entity_kind_counts[it]) { 4658 ctx->entity_kind_ids[it] = push_array(ctx->arena, typeof(*ctx->entity_kind_ids[it]), ctx->entity_kind_counts[it]); 4659 } 4660 } 4661 4662 da_count entity_counts[MetaEntityKind_Count] = {0}; 4663 for (da_count entity = 1; entity < ctx->entities.count; entity++) { 4664 MetaEntity *e = ctx->entities.data + entity; 4665 da_count index = entity_counts[e->kind]++; 4666 ctx->entity_kind_ids[e->kind][index] = da_index(e, &ctx->entities); 4667 } 4668 } 4669 4670 // NOTE(rnp): resolve reference entities 4671 { 4672 for (da_count entity = 0; entity < ctx->entity_kind_counts[MetaEntityKind_Reference]; entity++) { 4673 MetaEntity *e = ctx->entities.data + ctx->entity_kind_ids[MetaEntityKind_Reference][entity]; 4674 da_count reference_id = meta_lookup_string_slow(ctx->entity_names.data, ctx->entity_names.count, e->reference.reference_name); 4675 if (reference_id >= 0) 4676 e->reference.resolved_id.value = reference_id; 4677 } 4678 4679 b32 error = 0; 4680 for (da_count entity = 0; entity < ctx->entity_kind_counts[MetaEntityKind_Reference]; entity++) { 4681 MetaEntity *e = ctx->entities.data + ctx->entity_kind_ids[MetaEntityKind_Reference][entity]; 4682 MetaEntityReference *r = &e->reference; 4683 if (e->reference.resolved_id.value == 0) { 4684 meta_compiler_error_message(e->location, "undefined reference%s to '%.*s'\n", 4685 r->reference_count > 1? "s" : "", 4686 (i32)r->reference_name.len, r->reference_name.data); 4687 if (r->reference_count > 1) 4688 meta_compiler_message(" referenced in %d other places\n", r->reference_count - 1); 4689 error = 1; 4690 } 4691 } 4692 if (error) meta_error(); 4693 } 4694 4695 // NOTE(rnp): extract entities referenced by shaders 4696 { 4697 for (da_count shader = 0; shader < ctx->entity_kind_counts[MetaEntityKind_Shader]; shader++) { 4698 da_count id = ctx->entity_kind_ids[MetaEntityKind_Shader][shader]; 4699 MetaShader *s = &ctx->entities.data[id].shader; 4700 for (da_count ref = 0; ref < s->entity_reference_ids.count; ref++) { 4701 MetaEntityReference *r = &ctx->entities.data[s->entity_reference_ids.data[ref]].reference; 4702 meta_intern_id(ctx, &ctx->shader_entity_references, r->resolved_id.value); 4703 } 4704 } 4705 } 4706 4707 // NOTE(rnp): finalize struct info 4708 { 4709 da_count struct_infos_count = 0; 4710 for EachElement(meta_struct_entity_kinds, kind_it) 4711 struct_infos_count += ctx->entity_kind_counts[meta_struct_entity_kinds[kind_it]]; 4712 4713 ctx->struct_infos_count = struct_infos_count; 4714 ctx->struct_infos = push_array(ctx->arena, MetaStruct, struct_infos_count); 4715 4716 da_count struct_info_index = 0; 4717 for EachElement(meta_struct_entity_kinds, kind_it) { 4718 for (da_count it = 0; it < ctx->entity_kind_counts[meta_struct_entity_kinds[kind_it]]; it++) { 4719 da_count entity = ctx->entity_kind_ids[meta_struct_entity_kinds[kind_it]][it]; 4720 MetaEntity *e = ctx->entities.data + entity; 4721 e->table.struct_info_id = struct_info_index++; 4722 4723 MetaStruct *s = ctx->struct_infos + e->table.struct_info_id; 4724 s->name = str8_from_s8(ctx->entity_names.data[entity]); 4725 s->members = (str8 *)e->table.entries[meta_struct_name_field[kind_it]]; 4726 s->member_count = e->table.entry_count; 4727 s->location = e->location; 4728 s->byte_size = (u32)-1; 4729 s->entity = (MetaEntityID){entity}; 4730 if (meta_struct_entity_kinds[kind_it] == MetaEntityKind_Union) 4731 s->flags = MetaStructFlag_Union; 4732 4733 s->member_flags = push_array(ctx->arena, MetaStructMemberFlags, s->member_count); 4734 s->elements = push_array_no_zero(ctx->arena, i32, s->member_count); 4735 s->type_ids = push_array_no_zero(ctx->arena, i32, s->member_count); 4736 memory_clear(s->type_ids, -1, sizeof(*s->type_ids) * s->member_count); 4737 memory_clear(s->elements, -1, sizeof(*s->elements) * s->member_count); 4738 } 4739 } 4740 4741 // NOTE(rnp): resolve types 4742 for EachElement(meta_struct_entity_kinds, kind_it) { 4743 for (da_count it = 0; it < ctx->entity_kind_counts[meta_struct_entity_kinds[kind_it]]; it++) { 4744 da_count entity = ctx->entity_kind_ids[meta_struct_entity_kinds[kind_it]][it]; 4745 MetaEntity *e = ctx->entities.data + entity; 4746 MetaStruct *s = ctx->struct_infos + e->table.struct_info_id; 4747 4748 s8 *types = e->table.entries[meta_struct_type_field[kind_it]]; 4749 for EachIndex(s->member_count, member) { 4750 s->type_ids[member] = meta_lookup_string_slow((s8 *)meta_kind_meta_types, MetaKind_Count, types[member]); 4751 4752 if (s->type_ids[member] == -1 && meta_struct_allow_references[kind_it]) { 4753 s->member_flags[member] = MetaStructMemberFlag_ReferenceType; 4754 i64 id = meta_lookup_string_slow(ctx->entity_names.data, ctx->entity_names.count, types[member]); 4755 if (id >= 0) { 4756 MetaEntityKind kind = ctx->entities.data[id].kind; 4757 if (!meta_entity_kind_struct_reference_target[kind]) { 4758 meta_compiler_error(e->location, "struct '%.*s' references entity '%.*s' which is not a valid struct member\n", 4759 (i32)s->name.length, s->name.data, (i32)types[member].len, types[member].data); 4760 } 4761 if (ctx->entities.data[id].kind == MetaEntityKind_Union) 4762 s->flags |= MetaStructFlag_ContainsUnion; 4763 s->type_ids[member] = id; 4764 } 4765 } 4766 4767 if (s->type_ids[member] == -1) { 4768 meta_compiler_error(e->location, "struct '%.*s' references undefined type '%.*s'\n", 4769 (i32)s->name.length, s->name.data, (i32)types[member].len, types[member].data); 4770 } 4771 } 4772 } 4773 } 4774 4775 // NOTE(rnp): resolve element counts 4776 for EachElement(meta_struct_entity_kinds, kind_it) { 4777 for (da_count it = 0; it < ctx->entity_kind_counts[meta_struct_entity_kinds[kind_it]]; it++) { 4778 da_count entity = ctx->entity_kind_ids[meta_struct_entity_kinds[kind_it]][it]; 4779 MetaEntity *e = ctx->entities.data + entity; 4780 MetaStruct *s = ctx->struct_infos + e->table.struct_info_id; 4781 4782 i32 field = meta_struct_element_field[kind_it]; 4783 s8 *elements = field >= 0 ? e->table.entries[field] : 0; 4784 for EachIndex(s->member_count, member) { 4785 if (elements) { 4786 NumberConversion integer = integer_from_str8(str8_from_s8(elements[member])); 4787 if (integer.result == NumberConversionResult_Success) { 4788 s->elements[member] = integer.U64; 4789 } else { 4790 s->member_flags[member] |= MetaStructMemberFlag_ReferenceElements; 4791 i64 id = meta_lookup_string_slow(ctx->entity_names.data, ctx->entity_names.count, elements[member]); 4792 if (id >= 0) { 4793 MetaEntity *ee = ctx->entities.data + id; 4794 if (ee->kind != MetaEntityKind_Constant || ee->constant.kind != MetaConstantKind_Integer) { 4795 // TODO(rnp): point at correct member 4796 meta_compiler_error(e->location, "struct '%.*s': element count for field '%.*s'" 4797 "references '%.*s' which is not an integer constant\n", 4798 (i32)s->name.length, s->name.data, 4799 (i32)s->members[member].length, s->members[member].data, 4800 (i32)elements[member].len, elements[member].data); 4801 } 4802 s->elements[member] = id; 4803 } 4804 } 4805 } else { 4806 s->elements[member] = 1; 4807 } 4808 4809 if (s->elements[member] == -1) { 4810 meta_compiler_error(e->location, "struct '%.*s': element count for field '%.*s' could not be determined\n", 4811 (i32)s->name.length, s->name.data, 4812 (i32)s->members[member].length, s->members[member].data); 4813 } 4814 } 4815 } 4816 } 4817 4818 // NOTE(rnp): resolve size 4819 // TODO(rnp): depth could be predetermined 4820 b32 all_done = 0; 4821 for (u32 iterations = 0; !all_done && iterations < 16; iterations++) { 4822 for EachIndex(ctx->struct_infos_count, structure) { 4823 MetaStruct *s = ctx->struct_infos + structure; 4824 u32 size = 0; 4825 b32 is_union = (s->flags & MetaStructFlag_Union) != 0; 4826 for EachIndex(s->member_count, member) { 4827 b32 type_reference = (s->member_flags[member] & MetaStructMemberFlag_ReferenceType) != 0; 4828 u32 elements = meta_struct_member_elements(ctx, s, member); 4829 4830 u32 member_size = 0; 4831 if (type_reference) { 4832 MetaEntity *ref = ctx->entities.data + s->type_ids[member]; 4833 if (ref->kind == MetaEntityKind_Enumeration) { 4834 if (ref->table.entry_count < U32_MAX) member_size = sizeof(u32); 4835 else member_size = sizeof(u64); 4836 } else { 4837 MetaStruct *sub_struct = ctx->struct_infos + ref->table.struct_info_id; 4838 if (sub_struct->byte_size != (u32)-1) { 4839 member_size = sub_struct->byte_size * elements; 4840 } else { 4841 size = (u32)-1; 4842 break; 4843 } 4844 } 4845 } else { 4846 member_size = meta_kind_byte_sizes[s->type_ids[member]] * elements; 4847 } 4848 size = is_union ? Max(size, member_size) : size + member_size; 4849 } 4850 if (size != (u32)-1) 4851 s->byte_size = size; 4852 } 4853 4854 all_done = 1; 4855 for EachIndex(ctx->struct_infos_count, structure) 4856 all_done &= ctx->struct_infos[structure].byte_size != (u32)-1; 4857 } 4858 4859 if (!all_done) { 4860 for EachIndex(ctx->struct_infos_count, structure) { 4861 MetaStruct *s = ctx->struct_infos + structure; 4862 if (s->byte_size == (u32)-1) { 4863 meta_compiler_error(s->location, "storage size for struct '%.*s' could not be determined\n", 4864 (i32)s->name.length, s->name.data); 4865 } 4866 } 4867 } 4868 } 4869 4870 // NOTE(rnp): finalize base shader nonsense 4871 { 4872 for (da_count shader = 0; shader < ctx->entity_kind_counts[MetaEntityKind_Shader]; shader++) { 4873 MetaEntity *e = ctx->entities.data + ctx->entity_kind_ids[MetaEntityKind_Shader][shader]; 4874 if (e->shader.files[0].len > 0) 4875 ctx->base_shader_count++; 4876 } 4877 4878 ctx->base_shader_ids = push_array(ctx->arena, da_count, ctx->base_shader_count); 4879 ctx->base_shader_id_map = push_array(ctx->arena, da_count, ctx->entity_kind_counts[MetaEntityKind_Shader]); 4880 4881 da_count base_shader_ids_index = 0; 4882 for (da_count shader = 0; shader < ctx->entity_kind_counts[MetaEntityKind_Shader]; shader++) { 4883 da_count id = ctx->entity_kind_ids[MetaEntityKind_Shader][shader]; 4884 if (ctx->entities.data[id].shader.files[0].len > 0) 4885 ctx->base_shader_ids[base_shader_ids_index++] = id; 4886 } 4887 4888 // NOTE(rnp): first pass to resolve real shaders 4889 for (da_count shader = 0; shader < ctx->entity_kind_counts[MetaEntityKind_Shader]; shader++) { 4890 da_count id = ctx->entity_kind_ids[MetaEntityKind_Shader][shader]; 4891 if (ctx->entities.data[id].shader.files[0].len > 0) { 4892 ctx->base_shader_id_map[shader] = meta_lookup_id_slow(ctx->base_shader_ids, 4893 ctx->base_shader_count, 4894 id); 4895 } else { 4896 ctx->base_shader_id_map[shader] = -1; 4897 } 4898 } 4899 4900 // NOTE(rnp): second pass to resolve aliases 4901 for (da_count shader = 0; shader < ctx->entity_kind_counts[MetaEntityKind_Shader]; shader++) { 4902 da_count id = ctx->entity_kind_ids[MetaEntityKind_Shader][shader]; 4903 if (ctx->base_shader_id_map[shader] == -1) { 4904 if (ctx->entities.data[id].shader.kind == MetaShaderKind_Alias) { 4905 ctx->base_shader_id_map[shader] = meta_lookup_id_slow(ctx->base_shader_ids, 4906 ctx->base_shader_count, 4907 ctx->entities.data[id].shader.alias_parent_id.value); 4908 assert(ctx->base_shader_id_map[shader] != -1); 4909 } 4910 } 4911 } 4912 } 4913 4914 result->arena = 0; 4915 return result; 4916 } 4917 4918 function b32 4919 metagen_file_direct(Arena arena, char *filename) 4920 { 4921 MetaContext *ctx = metagen_load_context(&arena, filename); 4922 if (!ctx) return 0; 4923 4924 b32 result = 1; 4925 char *out; 4926 { 4927 str8 basename; 4928 str8_split(ctx->filename, &basename, 0, '.'); 4929 4930 Stream sb = arena_stream(arena); 4931 stream_append_s8s(&sb, s8_from_str8(ctx->directory), s8(OS_PATH_SEPARATOR), s8("generated")); 4932 stream_append_byte(&sb, 0); 4933 os_make_directory((c8 *)sb.data); 4934 stream_reset(&sb, sb.widx - 1); 4935 4936 stream_append_s8s(&sb, s8(OS_PATH_SEPARATOR), s8_from_str8(basename), s8(".c")); 4937 stream_append_byte(&sb, 0); 4938 4939 out = (c8 *)arena_stream_commit(&arena, &sb).data; 4940 } 4941 4942 CommandList deps = meta_extract_emit_file_dependencies(ctx, &arena); 4943 MetaprogramContext m[1] = {{.stream = arena_stream(arena), .scratch = ctx->scratch}}; 4944 if (needs_rebuild_(out, deps.data, deps.count)) { 4945 build_log_generate("%s", out); 4946 meta_push(m, c_file_header); 4947 metagen_run_emit_set(m, ctx, ctx->emit_sets + MetaEmitLang_C, (s8 *)meta_kind_c_types); 4948 result &= meta_write_and_reset(m, out); 4949 } 4950 4951 return result; 4952 } 4953 4954 i32 4955 main(i32 argc, char *argv[]) 4956 { 4957 u64 start_time = os_timer_count(); 4958 g_argv0 = argv[0]; 4959 4960 b32 result = 1; 4961 Arena arena = os_alloc_arena(MB(8)); 4962 check_rebuild_self(arena, argc, argv); 4963 4964 os_make_directory(OUTDIR); 4965 4966 result &= metagen_file_direct(arena, "assets" OS_PATH_SEPARATOR "assets.meta"); 4967 4968 MetaContext *meta = metagen_load_context(&arena, "beamformer.meta"); 4969 if (!meta) return 1; 4970 4971 result &= metagen_emit_c_code(meta, arena); 4972 result &= metagen_emit_helper_library_header(meta, arena); 4973 result &= metagen_emit_matlab_code(meta, arena); 4974 4975 parse_config(argc, argv); 4976 4977 if (!build_raylib(arena)) return 1; 4978 if (!build_glslang(arena)) return 1; 4979 4980 ///////////////// 4981 // lib/tests 4982 result &= build_helper_library(arena); 4983 if (config.tests) result &= build_tests(arena); 4984 4985 ////////////////// 4986 // static portion 4987 result &= build_beamformer_main(arena); 4988 4989 ///////////////////////// 4990 // hot reloadable portion 4991 // 4992 // NOTE: this is built after main because on w32 we need to export 4993 // gl function pointers for the reloadable portion to import 4994 if (config.debug) result &= build_beamformer_as_library(arena); 4995 4996 if (config.time) { 4997 f64 seconds = (f64)(os_timer_count() - start_time) / (f64)os_timer_frequency(); 4998 build_log_info("took %0.03f [s]", seconds); 4999 } 5000 5001 return result != 1; 5002 }