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