| author | |
| committer | |
| log | fab9b7110ed1fa7bb082aad5e095047441db2b24 |
| tree | 81fef60aa45e7980dab8f3e23e5b5e92b40ee0a9 |
| parent | d20d69b59e6b65a99f45cb6a45c14e887034dd18 |
| parent | 60935decd318498529a016eeb1379d943a7e830d |
231 files changed, 17563 insertions(+), 15987 deletions(-)
.github/workflows/ci.yaml+1| ... | ... | @@ -19,6 +19,7 @@ jobs: |
| 19 | 19 | - name: Build and Test |
| 20 | 20 | run: sh ci/x86_64-linux-debug.sh |
| 21 | 21 | x86_64-linux-release: |
| 22 | timeout-minutes: 420 | |
| 22 | 23 | runs-on: [self-hosted, Linux, x86_64] |
| 23 | 24 | steps: |
| 24 | 25 | - name: Checkout |
CMakeLists.txt+10-37| ... | ... | @@ -513,7 +513,7 @@ set(ZIG_STAGE2_SOURCES |
| 513 | 513 | "${CMAKE_SOURCE_DIR}/lib/std/zig/Ast.zig" |
| 514 | 514 | "${CMAKE_SOURCE_DIR}/lib/std/zig/CrossTarget.zig" |
| 515 | 515 | "${CMAKE_SOURCE_DIR}/lib/std/zig/c_builtins.zig" |
| 516 | "${CMAKE_SOURCE_DIR}/lib/std/zig/parse.zig" | |
| 516 | "${CMAKE_SOURCE_DIR}/lib/std/zig/Parse.zig" | |
| 517 | 517 | "${CMAKE_SOURCE_DIR}/lib/std/zig/render.zig" |
| 518 | 518 | "${CMAKE_SOURCE_DIR}/lib/std/zig/string_literal.zig" |
| 519 | 519 | "${CMAKE_SOURCE_DIR}/lib/std/zig/system.zig" |
| ... | ... | @@ -654,46 +654,19 @@ include_directories( |
| 654 | 654 | "${CMAKE_SOURCE_DIR}/src" |
| 655 | 655 | ) |
| 656 | 656 | |
| 657 | # These have to go before the -Wno- flags | |
| 658 | 657 | if(MSVC) |
| 659 | 658 | set(EXE_CXX_FLAGS "/std:c++17") |
| 660 | else(MSVC) | |
| 661 | set(EXE_CXX_FLAGS "-std=c++17") | |
| 662 | endif(MSVC) | |
| 663 | ||
| 664 | if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") | |
| 665 | if(MSVC) | |
| 666 | set(EXE_CXX_FLAGS "${EXE_CXX_FLAGS} /w") | |
| 667 | else() | |
| 668 | set(EXE_CXX_FLAGS "${EXE_CXX_FLAGS} -Werror -Wall") | |
| 669 | # fallthrough support was added in GCC 7.0 | |
| 670 | if(NOT CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 7.0) | |
| 671 | set(EXE_CXX_FLAGS "${EXE_CXX_FLAGS} -Werror=implicit-fallthrough") | |
| 672 | endif() | |
| 673 | # GCC 9.2 and older are unable to detect valid variable initialization in some cases | |
| 674 | if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS_EQUAL 9.2) | |
| 675 | set(EXE_CXX_FLAGS "${EXE_CXX_FLAGS} -Wno-maybe-uninitialized") | |
| 676 | endif() | |
| 677 | endif() | |
| 678 | endif() | |
| 679 | ||
| 680 | if(MSVC) | |
| 681 | set(EXE_CXX_FLAGS "${EXE_CXX_FLAGS}") | |
| 659 | set(EXE_LDFLAGS "/STACK:16777216") | |
| 660 | if(NOT "${CMAKE_BUILD_TYPE}" STREQUAL "Release" AND NOT "${CMAKE_BUILD_TYPE}" STREQUAL "MinSizeRel") | |
| 661 | set(EXE_LDFLAGS "${EXE_LDFLAGS} /debug:fastlink") | |
| 662 | endif() | |
| 682 | 663 | else() |
| 683 | set(EXE_CXX_FLAGS "${EXE_CXX_FLAGS} -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -D_GNU_SOURCE -fvisibility-inlines-hidden -fno-exceptions -fno-rtti -Werror=type-limits -Wno-missing-braces -Wno-comment") | |
| 684 | if(MINGW) | |
| 685 | set(EXE_CXX_FLAGS "${EXE_CXX_FLAGS} -Wno-format") | |
| 686 | endif() | |
| 687 | endif() | |
| 688 | ||
| 689 | set(EXE_LDFLAGS " ") | |
| 690 | if(MSVC) | |
| 691 | set(EXE_LDFLAGS "${EXE_LDFLAGS} /STACK:16777216") | |
| 692 | if(NOT "${CMAKE_BUILD_TYPE}" STREQUAL "Release" AND NOT "${CMAKE_BUILD_TYPE}" STREQUAL "MinSizeRel") | |
| 693 | set(EXE_LDFLAGS "${EXE_LDFLAGS} /debug:fastlink") | |
| 694 | endif() | |
| 695 | elseif(MINGW) | |
| 664 | set(EXE_CXX_FLAGS "-std=c++17 -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -D_GNU_SOURCE -fvisibility-inlines-hidden -fno-exceptions -fno-rtti -Werror=type-limits -Wno-missing-braces -Wno-comment") | |
| 665 | set(EXE_LDFLAGS " ") | |
| 666 | if(MINGW) | |
| 667 | set(EXE_CXX_FLAGS "${EXE_CXX_FLAGS} -Wno-format") | |
| 696 | 668 | set(EXE_LDFLAGS "${EXE_LDFLAGS} -Wl,--stack,16777216") |
| 669 | endif() | |
| 697 | 670 | endif() |
| 698 | 671 | |
| 699 | 672 | if(ZIG_STATIC) |
build.zig+55-204| ... | ... | @@ -1,19 +1,18 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | const builtin = std.builtin; |
| 3 | const Builder = std.build.Builder; | |
| 4 | 3 | const tests = @import("test/tests.zig"); |
| 5 | 4 | const BufMap = std.BufMap; |
| 6 | 5 | const mem = std.mem; |
| 7 | 6 | const ArrayList = std.ArrayList; |
| 8 | 7 | const io = std.io; |
| 9 | 8 | const fs = std.fs; |
| 10 | const InstallDirectoryOptions = std.build.InstallDirectoryOptions; | |
| 9 | const InstallDirectoryOptions = std.Build.InstallDirectoryOptions; | |
| 11 | 10 | const assert = std.debug.assert; |
| 12 | 11 | |
| 13 | 12 | const zig_version = std.builtin.Version{ .major = 0, .minor = 11, .patch = 0 }; |
| 14 | 13 | const stack_size = 32 * 1024 * 1024; |
| 15 | 14 | |
| 16 | pub fn build(b: *Builder) !void { | |
| 15 | pub fn build(b: *std.Build) !void { | |
| 17 | 16 | const release = b.option(bool, "release", "Build in release mode") orelse false; |
| 18 | 17 | const only_c = b.option(bool, "only-c", "Translate the Zig compiler to C code, with only the C backend enabled") orelse false; |
| 19 | 18 | const target = t: { |
| ... | ... | @@ -23,7 +22,7 @@ pub fn build(b: *Builder) !void { |
| 23 | 22 | } |
| 24 | 23 | break :t b.standardTargetOptions(.{ .default_target = default_target }); |
| 25 | 24 | }; |
| 26 | const mode: std.builtin.Mode = if (release) switch (target.getCpuArch()) { | |
| 25 | const optimize: std.builtin.OptimizeMode = if (release) switch (target.getCpuArch()) { | |
| 27 | 26 | .wasm32 => .ReleaseSmall, |
| 28 | 27 | else => .ReleaseFast, |
| 29 | 28 | } else .Debug; |
| ... | ... | @@ -33,7 +32,12 @@ pub fn build(b: *Builder) !void { |
| 33 | 32 | |
| 34 | 33 | const test_step = b.step("test", "Run all the tests"); |
| 35 | 34 | |
| 36 | const docgen_exe = b.addExecutable("docgen", "doc/docgen.zig"); | |
| 35 | const docgen_exe = b.addExecutable(.{ | |
| 36 | .name = "docgen", | |
| 37 | .root_source_file = .{ .path = "doc/docgen.zig" }, | |
| 38 | .target = .{}, | |
| 39 | .optimize = .Debug, | |
| 40 | }); | |
| 37 | 41 | docgen_exe.single_threaded = single_threaded; |
| 38 | 42 | |
| 39 | 43 | const rel_zig_exe = try fs.path.relative(b.allocator, b.build_root, b.zig_exe); |
| ... | ... | @@ -53,10 +57,12 @@ pub fn build(b: *Builder) !void { |
| 53 | 57 | const docs_step = b.step("docs", "Build documentation"); |
| 54 | 58 | docs_step.dependOn(&docgen_cmd.step); |
| 55 | 59 | |
| 56 | const test_cases = b.addTest("src/test.zig"); | |
| 60 | const test_cases = b.addTest(.{ | |
| 61 | .root_source_file = .{ .path = "src/test.zig" }, | |
| 62 | .optimize = optimize, | |
| 63 | }); | |
| 57 | 64 | test_cases.main_pkg_path = "."; |
| 58 | 65 | test_cases.stack_size = stack_size; |
| 59 | test_cases.setBuildMode(mode); | |
| 60 | 66 | test_cases.single_threaded = single_threaded; |
| 61 | 67 | |
| 62 | 68 | const fmt_build_zig = b.addFmt(&[_][]const u8{"build.zig"}); |
| ... | ... | @@ -154,17 +160,15 @@ pub fn build(b: *Builder) !void { |
| 154 | 160 | |
| 155 | 161 | const mem_leak_frames: u32 = b.option(u32, "mem-leak-frames", "How many stack frames to print when a memory leak occurs. Tests get 2x this amount.") orelse blk: { |
| 156 | 162 | if (strip == true) break :blk @as(u32, 0); |
| 157 | if (mode != .Debug) break :blk 0; | |
| 163 | if (optimize != .Debug) break :blk 0; | |
| 158 | 164 | break :blk 4; |
| 159 | 165 | }; |
| 160 | 166 | |
| 161 | const exe = addCompilerStep(b); | |
| 167 | const exe = addCompilerStep(b, optimize, target); | |
| 162 | 168 | exe.strip = strip; |
| 163 | 169 | exe.sanitize_thread = sanitize_thread; |
| 164 | 170 | exe.build_id = b.option(bool, "build-id", "Include a build id note") orelse false; |
| 165 | 171 | exe.install(); |
| 166 | exe.setBuildMode(mode); | |
| 167 | exe.setTarget(target); | |
| 168 | 172 | |
| 169 | 173 | const compile_step = b.step("compile", "Build the self-hosted compiler"); |
| 170 | 174 | compile_step.dependOn(&exe.step); |
| ... | ... | @@ -201,7 +205,7 @@ pub fn build(b: *Builder) !void { |
| 201 | 205 | test_cases.linkLibC(); |
| 202 | 206 | } |
| 203 | 207 | |
| 204 | const is_debug = mode == .Debug; | |
| 208 | const is_debug = optimize == .Debug; | |
| 205 | 209 | const enable_logging = b.option(bool, "log", "Enable debug logging with --debug-log") orelse is_debug; |
| 206 | 210 | const enable_link_snapshots = b.option(bool, "link-snapshot", "Whether to enable linker state snapshots") orelse false; |
| 207 | 211 | |
| ... | ... | @@ -367,25 +371,25 @@ pub fn build(b: *Builder) !void { |
| 367 | 371 | test_step.dependOn(test_cases_step); |
| 368 | 372 | } |
| 369 | 373 | |
| 370 | var chosen_modes: [4]builtin.Mode = undefined; | |
| 374 | var chosen_opt_modes_buf: [4]builtin.Mode = undefined; | |
| 371 | 375 | var chosen_mode_index: usize = 0; |
| 372 | 376 | if (!skip_debug) { |
| 373 | chosen_modes[chosen_mode_index] = builtin.Mode.Debug; | |
| 377 | chosen_opt_modes_buf[chosen_mode_index] = builtin.Mode.Debug; | |
| 374 | 378 | chosen_mode_index += 1; |
| 375 | 379 | } |
| 376 | 380 | if (!skip_release_safe) { |
| 377 | chosen_modes[chosen_mode_index] = builtin.Mode.ReleaseSafe; | |
| 381 | chosen_opt_modes_buf[chosen_mode_index] = builtin.Mode.ReleaseSafe; | |
| 378 | 382 | chosen_mode_index += 1; |
| 379 | 383 | } |
| 380 | 384 | if (!skip_release_fast) { |
| 381 | chosen_modes[chosen_mode_index] = builtin.Mode.ReleaseFast; | |
| 385 | chosen_opt_modes_buf[chosen_mode_index] = builtin.Mode.ReleaseFast; | |
| 382 | 386 | chosen_mode_index += 1; |
| 383 | 387 | } |
| 384 | 388 | if (!skip_release_small) { |
| 385 | chosen_modes[chosen_mode_index] = builtin.Mode.ReleaseSmall; | |
| 389 | chosen_opt_modes_buf[chosen_mode_index] = builtin.Mode.ReleaseSmall; | |
| 386 | 390 | chosen_mode_index += 1; |
| 387 | 391 | } |
| 388 | const modes = chosen_modes[0..chosen_mode_index]; | |
| 392 | const optimization_modes = chosen_opt_modes_buf[0..chosen_mode_index]; | |
| 389 | 393 | |
| 390 | 394 | // run stage1 `zig fmt` on this build.zig file just to make sure it works |
| 391 | 395 | test_step.dependOn(&fmt_build_zig.step); |
| ... | ... | @@ -398,7 +402,7 @@ pub fn build(b: *Builder) !void { |
| 398 | 402 | "test/behavior.zig", |
| 399 | 403 | "behavior", |
| 400 | 404 | "Run the behavior tests", |
| 401 | modes, | |
| 405 | optimization_modes, | |
| 402 | 406 | skip_single_threaded, |
| 403 | 407 | skip_non_native, |
| 404 | 408 | skip_libc, |
| ... | ... | @@ -412,7 +416,7 @@ pub fn build(b: *Builder) !void { |
| 412 | 416 | "lib/compiler_rt.zig", |
| 413 | 417 | "compiler-rt", |
| 414 | 418 | "Run the compiler_rt tests", |
| 415 | modes, | |
| 419 | optimization_modes, | |
| 416 | 420 | true, // skip_single_threaded |
| 417 | 421 | skip_non_native, |
| 418 | 422 | true, // skip_libc |
| ... | ... | @@ -426,7 +430,7 @@ pub fn build(b: *Builder) !void { |
| 426 | 430 | "lib/c.zig", |
| 427 | 431 | "universal-libc", |
| 428 | 432 | "Run the universal libc tests", |
| 429 | modes, | |
| 433 | optimization_modes, | |
| 430 | 434 | true, // skip_single_threaded |
| 431 | 435 | skip_non_native, |
| 432 | 436 | true, // skip_libc |
| ... | ... | @@ -434,11 +438,11 @@ pub fn build(b: *Builder) !void { |
| 434 | 438 | skip_stage2_tests or true, // TODO get these all passing |
| 435 | 439 | )); |
| 436 | 440 | |
| 437 | test_step.dependOn(tests.addCompareOutputTests(b, test_filter, modes)); | |
| 441 | test_step.dependOn(tests.addCompareOutputTests(b, test_filter, optimization_modes)); | |
| 438 | 442 | test_step.dependOn(tests.addStandaloneTests( |
| 439 | 443 | b, |
| 440 | 444 | test_filter, |
| 441 | modes, | |
| 445 | optimization_modes, | |
| 442 | 446 | skip_non_native, |
| 443 | 447 | enable_macos_sdk, |
| 444 | 448 | target, |
| ... | ... | @@ -451,10 +455,10 @@ pub fn build(b: *Builder) !void { |
| 451 | 455 | enable_symlinks_windows, |
| 452 | 456 | )); |
| 453 | 457 | test_step.dependOn(tests.addCAbiTests(b, skip_non_native, skip_release)); |
| 454 | test_step.dependOn(tests.addLinkTests(b, test_filter, modes, enable_macos_sdk, skip_stage2_tests, enable_symlinks_windows)); | |
| 455 | test_step.dependOn(tests.addStackTraceTests(b, test_filter, modes)); | |
| 456 | test_step.dependOn(tests.addCliTests(b, test_filter, modes)); | |
| 457 | test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, modes)); | |
| 458 | test_step.dependOn(tests.addLinkTests(b, test_filter, optimization_modes, enable_macos_sdk, skip_stage2_tests, enable_symlinks_windows)); | |
| 459 | test_step.dependOn(tests.addStackTraceTests(b, test_filter, optimization_modes)); | |
| 460 | test_step.dependOn(tests.addCliTests(b, test_filter, optimization_modes)); | |
| 461 | test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, optimization_modes)); | |
| 458 | 462 | test_step.dependOn(tests.addTranslateCTests(b, test_filter)); |
| 459 | 463 | if (!skip_run_translated_c) { |
| 460 | 464 | test_step.dependOn(tests.addRunTranslatedCTests(b, test_filter, target)); |
| ... | ... | @@ -468,7 +472,7 @@ pub fn build(b: *Builder) !void { |
| 468 | 472 | "lib/std/std.zig", |
| 469 | 473 | "std", |
| 470 | 474 | "Run the standard library tests", |
| 471 | modes, | |
| 475 | optimization_modes, | |
| 472 | 476 | skip_single_threaded, |
| 473 | 477 | skip_non_native, |
| 474 | 478 | skip_libc, |
| ... | ... | @@ -479,7 +483,7 @@ pub fn build(b: *Builder) !void { |
| 479 | 483 | try addWasiUpdateStep(b, version); |
| 480 | 484 | } |
| 481 | 485 | |
| 482 | fn addWasiUpdateStep(b: *Builder, version: [:0]const u8) !void { | |
| 486 | fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void { | |
| 483 | 487 | const semver = try std.SemanticVersion.parse(version); |
| 484 | 488 | |
| 485 | 489 | var target: std.zig.CrossTarget = .{ |
| ... | ... | @@ -488,9 +492,7 @@ fn addWasiUpdateStep(b: *Builder, version: [:0]const u8) !void { |
| 488 | 492 | }; |
| 489 | 493 | target.cpu_features_add.addFeature(@enumToInt(std.Target.wasm.Feature.bulk_memory)); |
| 490 | 494 | |
| 491 | const exe = addCompilerStep(b); | |
| 492 | exe.setBuildMode(.ReleaseSmall); | |
| 493 | exe.setTarget(target); | |
| 495 | const exe = addCompilerStep(b, .ReleaseSmall, target); | |
| 494 | 496 | |
| 495 | 497 | const exe_options = b.addOptions(); |
| 496 | 498 | exe.addOptions("build_options", exe_options); |
| ... | ... | @@ -517,8 +519,17 @@ fn addWasiUpdateStep(b: *Builder, version: [:0]const u8) !void { |
| 517 | 519 | update_zig1_step.dependOn(&run_opt.step); |
| 518 | 520 | } |
| 519 | 521 | |
| 520 | fn addCompilerStep(b: *Builder) *std.build.LibExeObjStep { | |
| 521 | const exe = b.addExecutable("zig", "src/main.zig"); | |
| 522 | fn addCompilerStep( | |
| 523 | b: *std.Build, | |
| 524 | optimize: std.builtin.OptimizeMode, | |
| 525 | target: std.zig.CrossTarget, | |
| 526 | ) *std.Build.CompileStep { | |
| 527 | const exe = b.addExecutable(.{ | |
| 528 | .name = "zig", | |
| 529 | .root_source_file = .{ .path = "src/main.zig" }, | |
| 530 | .target = target, | |
| 531 | .optimize = optimize, | |
| 532 | }); | |
| 522 | 533 | exe.stack_size = stack_size; |
| 523 | 534 | return exe; |
| 524 | 535 | } |
| ... | ... | @@ -538,9 +549,9 @@ const exe_cflags = [_][]const u8{ |
| 538 | 549 | }; |
| 539 | 550 | |
| 540 | 551 | fn addCmakeCfgOptionsToExe( |
| 541 | b: *Builder, | |
| 552 | b: *std.Build, | |
| 542 | 553 | cfg: CMakeConfig, |
| 543 | exe: *std.build.LibExeObjStep, | |
| 554 | exe: *std.Build.CompileStep, | |
| 544 | 555 | use_zig_libcxx: bool, |
| 545 | 556 | ) !void { |
| 546 | 557 | if (exe.target.isDarwin()) { |
| ... | ... | @@ -619,7 +630,7 @@ fn addCmakeCfgOptionsToExe( |
| 619 | 630 | } |
| 620 | 631 | } |
| 621 | 632 | |
| 622 | fn addStaticLlvmOptionsToExe(exe: *std.build.LibExeObjStep) !void { | |
| 633 | fn addStaticLlvmOptionsToExe(exe: *std.Build.CompileStep) !void { | |
| 623 | 634 | // Adds the Zig C++ sources which both stage1 and stage2 need. |
| 624 | 635 | // |
| 625 | 636 | // We need this because otherwise zig_clang_cc1_main.cpp ends up pulling |
| ... | ... | @@ -656,9 +667,9 @@ fn addStaticLlvmOptionsToExe(exe: *std.build.LibExeObjStep) !void { |
| 656 | 667 | } |
| 657 | 668 | |
| 658 | 669 | fn addCxxKnownPath( |
| 659 | b: *Builder, | |
| 670 | b: *std.Build, | |
| 660 | 671 | ctx: CMakeConfig, |
| 661 | exe: *std.build.LibExeObjStep, | |
| 672 | exe: *std.Build.CompileStep, | |
| 662 | 673 | objname: []const u8, |
| 663 | 674 | errtxt: ?[]const u8, |
| 664 | 675 | need_cpp_includes: bool, |
| ... | ... | @@ -691,7 +702,7 @@ fn addCxxKnownPath( |
| 691 | 702 | } |
| 692 | 703 | } |
| 693 | 704 | |
| 694 | fn addCMakeLibraryList(exe: *std.build.LibExeObjStep, list: []const u8) void { | |
| 705 | fn addCMakeLibraryList(exe: *std.Build.CompileStep, list: []const u8) void { | |
| 695 | 706 | var it = mem.tokenize(u8, list, ";"); |
| 696 | 707 | while (it.next()) |lib| { |
| 697 | 708 | if (mem.startsWith(u8, lib, "-l")) { |
| ... | ... | @@ -705,7 +716,7 @@ fn addCMakeLibraryList(exe: *std.build.LibExeObjStep, list: []const u8) void { |
| 705 | 716 | } |
| 706 | 717 | |
| 707 | 718 | const CMakeConfig = struct { |
| 708 | llvm_linkage: std.build.LibExeObjStep.Linkage, | |
| 719 | llvm_linkage: std.Build.CompileStep.Linkage, | |
| 709 | 720 | cmake_binary_dir: []const u8, |
| 710 | 721 | cmake_prefix_path: []const u8, |
| 711 | 722 | cmake_static_library_prefix: []const u8, |
| ... | ... | @@ -722,7 +733,7 @@ const CMakeConfig = struct { |
| 722 | 733 | |
| 723 | 734 | const max_config_h_bytes = 1 * 1024 * 1024; |
| 724 | 735 | |
| 725 | fn findConfigH(b: *Builder, config_h_path_option: ?[]const u8) ?[]const u8 { | |
| 736 | fn findConfigH(b: *std.Build, config_h_path_option: ?[]const u8) ?[]const u8 { | |
| 726 | 737 | if (config_h_path_option) |path| { |
| 727 | 738 | var config_h_or_err = fs.cwd().openFile(path, .{}); |
| 728 | 739 | if (config_h_or_err) |*file| { |
| ... | ... | @@ -768,7 +779,7 @@ fn findConfigH(b: *Builder, config_h_path_option: ?[]const u8) ?[]const u8 { |
| 768 | 779 | } else unreachable; // TODO should not need `else unreachable`. |
| 769 | 780 | } |
| 770 | 781 | |
| 771 | fn parseConfigH(b: *Builder, config_h_text: []const u8) ?CMakeConfig { | |
| 782 | fn parseConfigH(b: *std.Build, config_h_text: []const u8) ?CMakeConfig { | |
| 772 | 783 | var ctx: CMakeConfig = .{ |
| 773 | 784 | .llvm_linkage = undefined, |
| 774 | 785 | .cmake_binary_dir = undefined, |
| ... | ... | @@ -857,7 +868,7 @@ fn parseConfigH(b: *Builder, config_h_text: []const u8) ?CMakeConfig { |
| 857 | 868 | return ctx; |
| 858 | 869 | } |
| 859 | 870 | |
| 860 | fn toNativePathSep(b: *Builder, s: []const u8) []u8 { | |
| 871 | fn toNativePathSep(b: *std.Build, s: []const u8) []u8 { | |
| 861 | 872 | const duplicated = b.allocator.dupe(u8, s) catch unreachable; |
| 862 | 873 | for (duplicated) |*byte| switch (byte.*) { |
| 863 | 874 | '/' => byte.* = fs.path.sep, |
| ... | ... | @@ -866,166 +877,6 @@ fn toNativePathSep(b: *Builder, s: []const u8) []u8 { |
| 866 | 877 | return duplicated; |
| 867 | 878 | } |
| 868 | 879 | |
| 869 | const softfloat_sources = [_][]const u8{ | |
| 870 | "deps/SoftFloat-3e/source/8086/f128M_isSignalingNaN.c", | |
| 871 | "deps/SoftFloat-3e/source/8086/extF80M_isSignalingNaN.c", | |
| 872 | "deps/SoftFloat-3e/source/8086/s_commonNaNToF128M.c", | |
| 873 | "deps/SoftFloat-3e/source/8086/s_commonNaNToExtF80M.c", | |
| 874 | "deps/SoftFloat-3e/source/8086/s_commonNaNToF16UI.c", | |
| 875 | "deps/SoftFloat-3e/source/8086/s_commonNaNToF32UI.c", | |
| 876 | "deps/SoftFloat-3e/source/8086/s_commonNaNToF64UI.c", | |
| 877 | "deps/SoftFloat-3e/source/8086/s_f128MToCommonNaN.c", | |
| 878 | "deps/SoftFloat-3e/source/8086/s_extF80MToCommonNaN.c", | |
| 879 | "deps/SoftFloat-3e/source/8086/s_f16UIToCommonNaN.c", | |
| 880 | "deps/SoftFloat-3e/source/8086/s_f32UIToCommonNaN.c", | |
| 881 | "deps/SoftFloat-3e/source/8086/s_f64UIToCommonNaN.c", | |
| 882 | "deps/SoftFloat-3e/source/8086/s_propagateNaNF128M.c", | |
| 883 | "deps/SoftFloat-3e/source/8086/s_propagateNaNExtF80M.c", | |
| 884 | "deps/SoftFloat-3e/source/8086/s_propagateNaNF16UI.c", | |
| 885 | "deps/SoftFloat-3e/source/8086/softfloat_raiseFlags.c", | |
| 886 | "deps/SoftFloat-3e/source/f128M_add.c", | |
| 887 | "deps/SoftFloat-3e/source/f128M_div.c", | |
| 888 | "deps/SoftFloat-3e/source/f128M_eq.c", | |
| 889 | "deps/SoftFloat-3e/source/f128M_eq_signaling.c", | |
| 890 | "deps/SoftFloat-3e/source/f128M_le.c", | |
| 891 | "deps/SoftFloat-3e/source/f128M_le_quiet.c", | |
| 892 | "deps/SoftFloat-3e/source/f128M_lt.c", | |
| 893 | "deps/SoftFloat-3e/source/f128M_lt_quiet.c", | |
| 894 | "deps/SoftFloat-3e/source/f128M_mul.c", | |
| 895 | "deps/SoftFloat-3e/source/f128M_mulAdd.c", | |
| 896 | "deps/SoftFloat-3e/source/f128M_rem.c", | |
| 897 | "deps/SoftFloat-3e/source/f128M_roundToInt.c", | |
| 898 | "deps/SoftFloat-3e/source/f128M_sqrt.c", | |
| 899 | "deps/SoftFloat-3e/source/f128M_sub.c", | |
| 900 | "deps/SoftFloat-3e/source/f128M_to_f16.c", | |
| 901 | "deps/SoftFloat-3e/source/f128M_to_f32.c", | |
| 902 | "deps/SoftFloat-3e/source/f128M_to_f64.c", | |
| 903 | "deps/SoftFloat-3e/source/f128M_to_extF80M.c", | |
| 904 | "deps/SoftFloat-3e/source/f128M_to_i32.c", | |
| 905 | "deps/SoftFloat-3e/source/f128M_to_i32_r_minMag.c", | |
| 906 | "deps/SoftFloat-3e/source/f128M_to_i64.c", | |
| 907 | "deps/SoftFloat-3e/source/f128M_to_i64_r_minMag.c", | |
| 908 | "deps/SoftFloat-3e/source/f128M_to_ui32.c", | |
| 909 | "deps/SoftFloat-3e/source/f128M_to_ui32_r_minMag.c", | |
| 910 | "deps/SoftFloat-3e/source/f128M_to_ui64.c", | |
| 911 | "deps/SoftFloat-3e/source/f128M_to_ui64_r_minMag.c", | |
| 912 | "deps/SoftFloat-3e/source/extF80M_add.c", | |
| 913 | "deps/SoftFloat-3e/source/extF80M_div.c", | |
| 914 | "deps/SoftFloat-3e/source/extF80M_eq.c", | |
| 915 | "deps/SoftFloat-3e/source/extF80M_le.c", | |
| 916 | "deps/SoftFloat-3e/source/extF80M_lt.c", | |
| 917 | "deps/SoftFloat-3e/source/extF80M_mul.c", | |
| 918 | "deps/SoftFloat-3e/source/extF80M_rem.c", | |
| 919 | "deps/SoftFloat-3e/source/extF80M_roundToInt.c", | |
| 920 | "deps/SoftFloat-3e/source/extF80M_sqrt.c", | |
| 921 | "deps/SoftFloat-3e/source/extF80M_sub.c", | |
| 922 | "deps/SoftFloat-3e/source/extF80M_to_f16.c", | |
| 923 | "deps/SoftFloat-3e/source/extF80M_to_f32.c", | |
| 924 | "deps/SoftFloat-3e/source/extF80M_to_f64.c", | |
| 925 | "deps/SoftFloat-3e/source/extF80M_to_f128M.c", | |
| 926 | "deps/SoftFloat-3e/source/f16_add.c", | |
| 927 | "deps/SoftFloat-3e/source/f16_div.c", | |
| 928 | "deps/SoftFloat-3e/source/f16_eq.c", | |
| 929 | "deps/SoftFloat-3e/source/f16_isSignalingNaN.c", | |
| 930 | "deps/SoftFloat-3e/source/f16_lt.c", | |
| 931 | "deps/SoftFloat-3e/source/f16_mul.c", | |
| 932 | "deps/SoftFloat-3e/source/f16_mulAdd.c", | |
| 933 | "deps/SoftFloat-3e/source/f16_rem.c", | |
| 934 | "deps/SoftFloat-3e/source/f16_roundToInt.c", | |
| 935 | "deps/SoftFloat-3e/source/f16_sqrt.c", | |
| 936 | "deps/SoftFloat-3e/source/f16_sub.c", | |
| 937 | "deps/SoftFloat-3e/source/f16_to_extF80M.c", | |
| 938 | "deps/SoftFloat-3e/source/f16_to_f128M.c", | |
| 939 | "deps/SoftFloat-3e/source/f16_to_f64.c", | |
| 940 | "deps/SoftFloat-3e/source/f32_to_extF80M.c", | |
| 941 | "deps/SoftFloat-3e/source/f32_to_f128M.c", | |
| 942 | "deps/SoftFloat-3e/source/f64_to_extF80M.c", | |
| 943 | "deps/SoftFloat-3e/source/f64_to_f128M.c", | |
| 944 | "deps/SoftFloat-3e/source/f64_to_f16.c", | |
| 945 | "deps/SoftFloat-3e/source/i32_to_f128M.c", | |
| 946 | "deps/SoftFloat-3e/source/s_add256M.c", | |
| 947 | "deps/SoftFloat-3e/source/s_addCarryM.c", | |
| 948 | "deps/SoftFloat-3e/source/s_addComplCarryM.c", | |
| 949 | "deps/SoftFloat-3e/source/s_addF128M.c", | |
| 950 | "deps/SoftFloat-3e/source/s_addExtF80M.c", | |
| 951 | "deps/SoftFloat-3e/source/s_addM.c", | |
| 952 | "deps/SoftFloat-3e/source/s_addMagsF16.c", | |
| 953 | "deps/SoftFloat-3e/source/s_addMagsF32.c", | |
| 954 | "deps/SoftFloat-3e/source/s_addMagsF64.c", | |
| 955 | "deps/SoftFloat-3e/source/s_approxRecip32_1.c", | |
| 956 | "deps/SoftFloat-3e/source/s_approxRecipSqrt32_1.c", | |
| 957 | "deps/SoftFloat-3e/source/s_approxRecipSqrt_1Ks.c", | |
| 958 | "deps/SoftFloat-3e/source/s_approxRecip_1Ks.c", | |
| 959 | "deps/SoftFloat-3e/source/s_compare128M.c", | |
| 960 | "deps/SoftFloat-3e/source/s_compare96M.c", | |
| 961 | "deps/SoftFloat-3e/source/s_compareNonnormExtF80M.c", | |
| 962 | "deps/SoftFloat-3e/source/s_countLeadingZeros16.c", | |
| 963 | "deps/SoftFloat-3e/source/s_countLeadingZeros32.c", | |
| 964 | "deps/SoftFloat-3e/source/s_countLeadingZeros64.c", | |
| 965 | "deps/SoftFloat-3e/source/s_countLeadingZeros8.c", | |
| 966 | "deps/SoftFloat-3e/source/s_eq128.c", | |
| 967 | "deps/SoftFloat-3e/source/s_invalidF128M.c", | |
| 968 | "deps/SoftFloat-3e/source/s_invalidExtF80M.c", | |
| 969 | "deps/SoftFloat-3e/source/s_isNaNF128M.c", | |
| 970 | "deps/SoftFloat-3e/source/s_le128.c", | |
| 971 | "deps/SoftFloat-3e/source/s_lt128.c", | |
| 972 | "deps/SoftFloat-3e/source/s_mul128MTo256M.c", | |
| 973 | "deps/SoftFloat-3e/source/s_mul64To128M.c", | |
| 974 | "deps/SoftFloat-3e/source/s_mulAddF128M.c", | |
| 975 | "deps/SoftFloat-3e/source/s_mulAddF16.c", | |
| 976 | "deps/SoftFloat-3e/source/s_mulAddF32.c", | |
| 977 | "deps/SoftFloat-3e/source/s_mulAddF64.c", | |
| 978 | "deps/SoftFloat-3e/source/s_negXM.c", | |
| 979 | "deps/SoftFloat-3e/source/s_normExtF80SigM.c", | |
| 980 | "deps/SoftFloat-3e/source/s_normRoundPackMToF128M.c", | |
| 981 | "deps/SoftFloat-3e/source/s_normRoundPackMToExtF80M.c", | |
| 982 | "deps/SoftFloat-3e/source/s_normRoundPackToF16.c", | |
| 983 | "deps/SoftFloat-3e/source/s_normRoundPackToF32.c", | |
| 984 | "deps/SoftFloat-3e/source/s_normRoundPackToF64.c", | |
| 985 | "deps/SoftFloat-3e/source/s_normSubnormalF128SigM.c", | |
| 986 | "deps/SoftFloat-3e/source/s_normSubnormalF16Sig.c", | |
| 987 | "deps/SoftFloat-3e/source/s_normSubnormalF32Sig.c", | |
| 988 | "deps/SoftFloat-3e/source/s_normSubnormalF64Sig.c", | |
| 989 | "deps/SoftFloat-3e/source/s_remStepMBy32.c", | |
| 990 | "deps/SoftFloat-3e/source/s_roundMToI64.c", | |
| 991 | "deps/SoftFloat-3e/source/s_roundMToUI64.c", | |
| 992 | "deps/SoftFloat-3e/source/s_roundPackMToExtF80M.c", | |
| 993 | "deps/SoftFloat-3e/source/s_roundPackMToF128M.c", | |
| 994 | "deps/SoftFloat-3e/source/s_roundPackToF16.c", | |
| 995 | "deps/SoftFloat-3e/source/s_roundPackToF32.c", | |
| 996 | "deps/SoftFloat-3e/source/s_roundPackToF64.c", | |
| 997 | "deps/SoftFloat-3e/source/s_roundToI32.c", | |
| 998 | "deps/SoftFloat-3e/source/s_roundToI64.c", | |
| 999 | "deps/SoftFloat-3e/source/s_roundToUI32.c", | |
| 1000 | "deps/SoftFloat-3e/source/s_roundToUI64.c", | |
| 1001 | "deps/SoftFloat-3e/source/s_shiftLeftM.c", | |
| 1002 | "deps/SoftFloat-3e/source/s_shiftNormSigF128M.c", | |
| 1003 | "deps/SoftFloat-3e/source/s_shiftRightJam256M.c", | |
| 1004 | "deps/SoftFloat-3e/source/s_shiftRightJam32.c", | |
| 1005 | "deps/SoftFloat-3e/source/s_shiftRightJam64.c", | |
| 1006 | "deps/SoftFloat-3e/source/s_shiftRightJamM.c", | |
| 1007 | "deps/SoftFloat-3e/source/s_shiftRightM.c", | |
| 1008 | "deps/SoftFloat-3e/source/s_shortShiftLeft64To96M.c", | |
| 1009 | "deps/SoftFloat-3e/source/s_shortShiftLeftM.c", | |
| 1010 | "deps/SoftFloat-3e/source/s_shortShiftRightExtendM.c", | |
| 1011 | "deps/SoftFloat-3e/source/s_shortShiftRightJam64.c", | |
| 1012 | "deps/SoftFloat-3e/source/s_shortShiftRightJamM.c", | |
| 1013 | "deps/SoftFloat-3e/source/s_shortShiftRightM.c", | |
| 1014 | "deps/SoftFloat-3e/source/s_sub1XM.c", | |
| 1015 | "deps/SoftFloat-3e/source/s_sub256M.c", | |
| 1016 | "deps/SoftFloat-3e/source/s_subM.c", | |
| 1017 | "deps/SoftFloat-3e/source/s_subMagsF16.c", | |
| 1018 | "deps/SoftFloat-3e/source/s_subMagsF32.c", | |
| 1019 | "deps/SoftFloat-3e/source/s_subMagsF64.c", | |
| 1020 | "deps/SoftFloat-3e/source/s_tryPropagateNaNF128M.c", | |
| 1021 | "deps/SoftFloat-3e/source/s_tryPropagateNaNExtF80M.c", | |
| 1022 | "deps/SoftFloat-3e/source/softfloat_state.c", | |
| 1023 | "deps/SoftFloat-3e/source/ui32_to_f128M.c", | |
| 1024 | "deps/SoftFloat-3e/source/ui64_to_f128M.c", | |
| 1025 | "deps/SoftFloat-3e/source/ui32_to_extF80M.c", | |
| 1026 | "deps/SoftFloat-3e/source/ui64_to_extF80M.c", | |
| 1027 | }; | |
| 1028 | ||
| 1029 | 880 | const zig_cpp_sources = [_][]const u8{ |
| 1030 | 881 | // These are planned to stay even when we are self-hosted. |
| 1031 | 882 | "src/zig_llvm.cpp", |
ci/x86_64-windows-debug.ps1+3-2| ... | ... | @@ -76,7 +76,8 @@ Write-Output "Build x86_64-windows-msvc behavior tests using the C backend..." |
| 76 | 76 | -ofmt=c ` |
| 77 | 77 | -femit-bin="test-x86_64-windows-msvc.c" ` |
| 78 | 78 | --test-no-exec ` |
| 79 | -target x86_64-windows-msvc | |
| 79 | -target x86_64-windows-msvc ` | |
| 80 | -lc | |
| 80 | 81 | CheckLastExitCode |
| 81 | 82 | |
| 82 | 83 | & "stage3-debug\bin\zig.exe" build-obj ` |
| ... | ... | @@ -99,7 +100,7 @@ Enter-VsDevShell -VsInstallPath "C:\Program Files\Microsoft Visual Studio\2022\E |
| 99 | 100 | CheckLastExitCode |
| 100 | 101 | |
| 101 | 102 | Write-Output "Build and run behavior tests with msvc..." |
| 102 | & cl.exe -I..\lib test-x86_64-windows-msvc.c compiler_rt-x86_64-windows-msvc.c /W3 /Z7 -link -nologo -debug -subsystem:console -entry:wWinMainCRTStartup kernel32.lib ntdll.lib vcruntime.lib libucrt.lib | |
| 103 | & cl.exe -I..\lib test-x86_64-windows-msvc.c compiler_rt-x86_64-windows-msvc.c /W3 /Z7 -link -nologo -debug -subsystem:console kernel32.lib ntdll.lib libcmt.lib | |
| 103 | 104 | CheckLastExitCode |
| 104 | 105 | |
| 105 | 106 | & .\test-x86_64-windows-msvc.exe |
ci/x86_64-windows-release.ps1+3-2| ... | ... | @@ -76,7 +76,8 @@ Write-Output "Build x86_64-windows-msvc behavior tests using the C backend..." |
| 76 | 76 | -ofmt=c ` |
| 77 | 77 | -femit-bin="test-x86_64-windows-msvc.c" ` |
| 78 | 78 | --test-no-exec ` |
| 79 | -target x86_64-windows-msvc | |
| 79 | -target x86_64-windows-msvc ` | |
| 80 | -lc | |
| 80 | 81 | CheckLastExitCode |
| 81 | 82 | |
| 82 | 83 | & "stage3-release\bin\zig.exe" build-obj ` |
| ... | ... | @@ -99,7 +100,7 @@ Enter-VsDevShell -VsInstallPath "C:\Program Files\Microsoft Visual Studio\2022\E |
| 99 | 100 | CheckLastExitCode |
| 100 | 101 | |
| 101 | 102 | Write-Output "Build and run behavior tests with msvc..." |
| 102 | & cl.exe -I..\lib test-x86_64-windows-msvc.c compiler_rt-x86_64-windows-msvc.c /W3 /Z7 -link -nologo -debug -subsystem:console -entry:wWinMainCRTStartup kernel32.lib ntdll.lib vcruntime.lib libucrt.lib | |
| 103 | & cl.exe -I..\lib test-x86_64-windows-msvc.c compiler_rt-x86_64-windows-msvc.c /W3 /Z7 -link -nologo -debug -subsystem:console kernel32.lib ntdll.lib libcmt.lib | |
| 103 | 104 | CheckLastExitCode |
| 104 | 105 | |
| 105 | 106 | & .\test-x86_64-windows-msvc.exe |
doc/langref.html.in+93-58| ... | ... | @@ -871,6 +871,13 @@ pub fn main() void { |
| 871 | 871 | However, it is possible to embed non-UTF-8 bytes into a string literal using <code>\xNN</code> notation. |
| 872 | 872 | </p> |
| 873 | 873 | <p> |
| 874 | Indexing into a string containing non-ASCII bytes will return individual bytes, whether valid | |
| 875 | UTF-8 or not. | |
| 876 | The {#link|Zig Standard Library#} provides routines for checking the validity of UTF-8 encoded | |
| 877 | strings, accessing their code points and other encoding/decoding related tasks in | |
| 878 | {#syntax#}std.unicode{#endsyntax#}. | |
| 879 | </p> | |
| 880 | <p> | |
| 874 | 881 | Unicode code point literals have type {#syntax#}comptime_int{#endsyntax#}, the same as |
| 875 | 882 | {#link|Integer Literals#}. All {#link|Escape Sequences#} are valid in both string literals |
| 876 | 883 | and Unicode code point literals. |
| ... | ... | @@ -894,9 +901,12 @@ pub fn main() void { |
| 894 | 901 | print("{}\n", .{'e' == '\x65'}); // true |
| 895 | 902 | print("{d}\n", .{'\u{1f4a9}'}); // 128169 |
| 896 | 903 | print("{d}\n", .{'💯'}); // 128175 |
| 897 | print("{}\n", .{mem.eql(u8, "hello", "h\x65llo")}); // true | |
| 898 | print("0x{x}\n", .{"\xff"[0]}); // non-UTF-8 strings are possible with \xNN notation. | |
| 899 | 904 | print("{u}\n", .{'âš¡'}); |
| 905 | print("{}\n", .{mem.eql(u8, "hello", "h\x65llo")}); // true | |
| 906 | print("{}\n", .{mem.eql(u8, "💯", "\xf0\x9f\x92\xaf")}); // also true | |
| 907 | const invalid_utf8 = "\xff\xfe"; // non-UTF-8 strings are possible with \xNN notation. | |
| 908 | print("0x{x}\n", .{invalid_utf8[1]}); // indexing them returns individual bytes... | |
| 909 | print("0x{x}\n", .{"💯"[1]}); // ...as does indexing part-way through non-ASCII characters | |
| 900 | 910 | } |
| 901 | 911 | {#code_end#} |
| 902 | 912 | {#see_also|Arrays|Source Encoding#} |
| ... | ... | @@ -8799,6 +8809,15 @@ pub const PrefetchOptions = struct { |
| 8799 | 8809 | {#link|Optional Pointers#} are allowed. Casting an optional pointer which is {#link|null#} |
| 8800 | 8810 | to a non-optional pointer invokes safety-checked {#link|Undefined Behavior#}. |
| 8801 | 8811 | </p> |
| 8812 | <p> | |
| 8813 | {#syntax#}@ptrCast{#endsyntax#} cannot be used for: | |
| 8814 | </p> | |
| 8815 | <ul> | |
| 8816 | <li>Removing {#syntax#}const{#endsyntax#} or {#syntax#}volatile{#endsyntax#} qualifier, use {#link|@qualCast#}.</li> | |
| 8817 | <li>Changing pointer address space, use {#link|@addrSpaceCast#}.</li> | |
| 8818 | <li>Increasing pointer alignment, use {#link|@alignCast#}.</li> | |
| 8819 | <li>Casting a non-slice pointer to a slice, use slicing syntax {#syntax#}ptr[start..end]{#endsyntax#}.</li> | |
| 8820 | </ul> | |
| 8802 | 8821 | {#header_close#} |
| 8803 | 8822 | |
| 8804 | 8823 | {#header_open|@ptrToInt#} |
| ... | ... | @@ -8811,6 +8830,13 @@ pub const PrefetchOptions = struct { |
| 8811 | 8830 | |
| 8812 | 8831 | {#header_close#} |
| 8813 | 8832 | |
| 8833 | {#header_open|@qualCast#} | |
| 8834 | <pre>{#syntax#}@qualCast(comptime DestType: type, value: anytype) DestType{#endsyntax#}</pre> | |
| 8835 | <p> | |
| 8836 | Remove {#syntax#}const{#endsyntax#} or {#syntax#}volatile{#endsyntax#} qualifier from a pointer. | |
| 8837 | </p> | |
| 8838 | {#header_close#} | |
| 8839 | ||
| 8814 | 8840 | {#header_open|@rem#} |
| 8815 | 8841 | <pre>{#syntax#}@rem(numerator: T, denominator: T) T{#endsyntax#}</pre> |
| 8816 | 8842 | <p> |
| ... | ... | @@ -9180,8 +9206,7 @@ fn doTheTest() !void { |
| 9180 | 9206 | when available. |
| 9181 | 9207 | </p> |
| 9182 | 9208 | <p> |
| 9183 | Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that | |
| 9184 | <a href="https://github.com/ziglang/zig/issues/4026">some float operations are not yet implemented for all float types</a>. | |
| 9209 | Supports {#link|Floats#} and {#link|Vectors#} of floats. | |
| 9185 | 9210 | </p> |
| 9186 | 9211 | {#header_close#} |
| 9187 | 9212 | {#header_open|@sin#} |
| ... | ... | @@ -9191,8 +9216,7 @@ fn doTheTest() !void { |
| 9191 | 9216 | when available. |
| 9192 | 9217 | </p> |
| 9193 | 9218 | <p> |
| 9194 | Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that | |
| 9195 | <a href="https://github.com/ziglang/zig/issues/4026">some float operations are not yet implemented for all float types</a>. | |
| 9219 | Supports {#link|Floats#} and {#link|Vectors#} of floats. | |
| 9196 | 9220 | </p> |
| 9197 | 9221 | {#header_close#} |
| 9198 | 9222 | |
| ... | ... | @@ -9203,8 +9227,7 @@ fn doTheTest() !void { |
| 9203 | 9227 | when available. |
| 9204 | 9228 | </p> |
| 9205 | 9229 | <p> |
| 9206 | Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that | |
| 9207 | <a href="https://github.com/ziglang/zig/issues/4026">some float operations are not yet implemented for all float types</a>. | |
| 9230 | Supports {#link|Floats#} and {#link|Vectors#} of floats. | |
| 9208 | 9231 | </p> |
| 9209 | 9232 | {#header_close#} |
| 9210 | 9233 | |
| ... | ... | @@ -9215,8 +9238,7 @@ fn doTheTest() !void { |
| 9215 | 9238 | Uses a dedicated hardware instruction when available. |
| 9216 | 9239 | </p> |
| 9217 | 9240 | <p> |
| 9218 | Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that | |
| 9219 | <a href="https://github.com/ziglang/zig/issues/4026">some float operations are not yet implemented for all float types</a>. | |
| 9241 | Supports {#link|Floats#} and {#link|Vectors#} of floats. | |
| 9220 | 9242 | </p> |
| 9221 | 9243 | {#header_close#} |
| 9222 | 9244 | |
| ... | ... | @@ -9227,8 +9249,7 @@ fn doTheTest() !void { |
| 9227 | 9249 | when available. |
| 9228 | 9250 | </p> |
| 9229 | 9251 | <p> |
| 9230 | Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that | |
| 9231 | <a href="https://github.com/ziglang/zig/issues/4026">some float operations are not yet implemented for all float types</a>. | |
| 9252 | Supports {#link|Floats#} and {#link|Vectors#} of floats. | |
| 9232 | 9253 | </p> |
| 9233 | 9254 | {#header_close#} |
| 9234 | 9255 | {#header_open|@exp2#} |
| ... | ... | @@ -9238,8 +9259,7 @@ fn doTheTest() !void { |
| 9238 | 9259 | when available. |
| 9239 | 9260 | </p> |
| 9240 | 9261 | <p> |
| 9241 | Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that | |
| 9242 | <a href="https://github.com/ziglang/zig/issues/4026">some float operations are not yet implemented for all float types</a>. | |
| 9262 | Supports {#link|Floats#} and {#link|Vectors#} of floats. | |
| 9243 | 9263 | </p> |
| 9244 | 9264 | {#header_close#} |
| 9245 | 9265 | {#header_open|@log#} |
| ... | ... | @@ -9249,8 +9269,7 @@ fn doTheTest() !void { |
| 9249 | 9269 | when available. |
| 9250 | 9270 | </p> |
| 9251 | 9271 | <p> |
| 9252 | Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that | |
| 9253 | <a href="https://github.com/ziglang/zig/issues/4026">some float operations are not yet implemented for all float types</a>. | |
| 9272 | Supports {#link|Floats#} and {#link|Vectors#} of floats. | |
| 9254 | 9273 | </p> |
| 9255 | 9274 | {#header_close#} |
| 9256 | 9275 | {#header_open|@log2#} |
| ... | ... | @@ -9260,8 +9279,7 @@ fn doTheTest() !void { |
| 9260 | 9279 | when available. |
| 9261 | 9280 | </p> |
| 9262 | 9281 | <p> |
| 9263 | Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that | |
| 9264 | <a href="https://github.com/ziglang/zig/issues/4026">some float operations are not yet implemented for all float types</a>. | |
| 9282 | Supports {#link|Floats#} and {#link|Vectors#} of floats. | |
| 9265 | 9283 | </p> |
| 9266 | 9284 | {#header_close#} |
| 9267 | 9285 | {#header_open|@log10#} |
| ... | ... | @@ -9271,8 +9289,7 @@ fn doTheTest() !void { |
| 9271 | 9289 | when available. |
| 9272 | 9290 | </p> |
| 9273 | 9291 | <p> |
| 9274 | Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that | |
| 9275 | <a href="https://github.com/ziglang/zig/issues/4026">some float operations are not yet implemented for all float types</a>. | |
| 9292 | Supports {#link|Floats#} and {#link|Vectors#} of floats. | |
| 9276 | 9293 | </p> |
| 9277 | 9294 | {#header_close#} |
| 9278 | 9295 | {#header_open|@fabs#} |
| ... | ... | @@ -9282,8 +9299,7 @@ fn doTheTest() !void { |
| 9282 | 9299 | when available. |
| 9283 | 9300 | </p> |
| 9284 | 9301 | <p> |
| 9285 | Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that | |
| 9286 | <a href="https://github.com/ziglang/zig/issues/4026">some float operations are not yet implemented for all float types</a>. | |
| 9302 | Supports {#link|Floats#} and {#link|Vectors#} of floats. | |
| 9287 | 9303 | </p> |
| 9288 | 9304 | {#header_close#} |
| 9289 | 9305 | {#header_open|@floor#} |
| ... | ... | @@ -9293,8 +9309,7 @@ fn doTheTest() !void { |
| 9293 | 9309 | Uses a dedicated hardware instruction when available. |
| 9294 | 9310 | </p> |
| 9295 | 9311 | <p> |
| 9296 | Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that | |
| 9297 | <a href="https://github.com/ziglang/zig/issues/4026">some float operations are not yet implemented for all float types</a>. | |
| 9312 | Supports {#link|Floats#} and {#link|Vectors#} of floats. | |
| 9298 | 9313 | </p> |
| 9299 | 9314 | {#header_close#} |
| 9300 | 9315 | {#header_open|@ceil#} |
| ... | ... | @@ -9304,8 +9319,7 @@ fn doTheTest() !void { |
| 9304 | 9319 | Uses a dedicated hardware instruction when available. |
| 9305 | 9320 | </p> |
| 9306 | 9321 | <p> |
| 9307 | Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that | |
| 9308 | <a href="https://github.com/ziglang/zig/issues/4026">some float operations are not yet implemented for all float types</a>. | |
| 9322 | Supports {#link|Floats#} and {#link|Vectors#} of floats. | |
| 9309 | 9323 | </p> |
| 9310 | 9324 | {#header_close#} |
| 9311 | 9325 | {#header_open|@trunc#} |
| ... | ... | @@ -9315,8 +9329,7 @@ fn doTheTest() !void { |
| 9315 | 9329 | Uses a dedicated hardware instruction when available. |
| 9316 | 9330 | </p> |
| 9317 | 9331 | <p> |
| 9318 | Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that | |
| 9319 | <a href="https://github.com/ziglang/zig/issues/4026">some float operations are not yet implemented for all float types</a>. | |
| 9332 | Supports {#link|Floats#} and {#link|Vectors#} of floats. | |
| 9320 | 9333 | </p> |
| 9321 | 9334 | {#header_close#} |
| 9322 | 9335 | {#header_open|@round#} |
| ... | ... | @@ -9326,8 +9339,7 @@ fn doTheTest() !void { |
| 9326 | 9339 | when available. |
| 9327 | 9340 | </p> |
| 9328 | 9341 | <p> |
| 9329 | Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that | |
| 9330 | <a href="https://github.com/ziglang/zig/issues/4026">some float operations are not yet implemented for all float types</a>. | |
| 9342 | Supports {#link|Floats#} and {#link|Vectors#} of floats. | |
| 9331 | 9343 | </p> |
| 9332 | 9344 | {#header_close#} |
| 9333 | 9345 | |
| ... | ... | @@ -9528,11 +9540,15 @@ fn foo(comptime T: type, ptr: *T) T { |
| 9528 | 9540 | To add standard build options to a <code class="file">build.zig</code> file: |
| 9529 | 9541 | </p> |
| 9530 | 9542 | {#code_begin|syntax|build#} |
| 9531 | const Builder = @import("std").build.Builder; | |
| 9543 | const std = @import("std"); | |
| 9532 | 9544 | |
| 9533 | pub fn build(b: *Builder) void { | |
| 9534 | const exe = b.addExecutable("example", "example.zig"); | |
| 9535 | exe.setBuildMode(b.standardReleaseOptions()); | |
| 9545 | pub fn build(b: *std.Build) void { | |
| 9546 | const optimize = b.standardOptimizeOption(.{}); | |
| 9547 | const exe = b.addExecutable(.{ | |
| 9548 | .name = "example", | |
| 9549 | .root_source_file = .{ .path = "example.zig" }, | |
| 9550 | .optimize = optimize, | |
| 9551 | }); | |
| 9536 | 9552 | b.default_step.dependOn(&exe.step); |
| 9537 | 9553 | } |
| 9538 | 9554 | {#code_end#} |
| ... | ... | @@ -10547,22 +10563,26 @@ const separator = if (builtin.os.tag == .windows) '\\' else '/'; |
| 10547 | 10563 | <p>This <code class="file">build.zig</code> file is automatically generated |
| 10548 | 10564 | by <kbd>zig init-exe</kbd>.</p> |
| 10549 | 10565 | {#code_begin|syntax|build_executable#} |
| 10550 | const Builder = @import("std").build.Builder; | |
| 10566 | const std = @import("std"); | |
| 10551 | 10567 | |
| 10552 | pub fn build(b: *Builder) void { | |
| 10568 | pub fn build(b: *std.Build) void { | |
| 10553 | 10569 | // Standard target options allows the person running `zig build` to choose |
| 10554 | 10570 | // what target to build for. Here we do not override the defaults, which |
| 10555 | 10571 | // means any target is allowed, and the default is native. Other options |
| 10556 | 10572 | // for restricting supported target set are available. |
| 10557 | 10573 | const target = b.standardTargetOptions(.{}); |
| 10558 | 10574 | |
| 10559 | // Standard release options allow the person running `zig build` to select | |
| 10560 | // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. | |
| 10561 | const mode = b.standardReleaseOptions(); | |
| 10575 | // Standard optimization options allow the person running `zig build` to select | |
| 10576 | // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not | |
| 10577 | // set a preferred release mode, allowing the user to decide how to optimize. | |
| 10578 | const optimize = b.standardOptimizeOption(.{}); | |
| 10562 | 10579 | |
| 10563 | const exe = b.addExecutable("example", "src/main.zig"); | |
| 10564 | exe.setTarget(target); | |
| 10565 | exe.setBuildMode(mode); | |
| 10580 | const exe = b.addExecutable(.{ | |
| 10581 | .name = "example", | |
| 10582 | .root_source_file = .{ .path = "src/main.zig" }, | |
| 10583 | .target = target, | |
| 10584 | .optimize = optimize, | |
| 10585 | }); | |
| 10566 | 10586 | exe.install(); |
| 10567 | 10587 | |
| 10568 | 10588 | const run_cmd = exe.run(); |
| ... | ... | @@ -10581,16 +10601,21 @@ pub fn build(b: *Builder) void { |
| 10581 | 10601 | <p>This <code class="file">build.zig</code> file is automatically generated |
| 10582 | 10602 | by <kbd>zig init-lib</kbd>.</p> |
| 10583 | 10603 | {#code_begin|syntax|build_library#} |
| 10584 | const Builder = @import("std").build.Builder; | |
| 10604 | const std = @import("std"); | |
| 10585 | 10605 | |
| 10586 | pub fn build(b: *Builder) void { | |
| 10587 | const mode = b.standardReleaseOptions(); | |
| 10588 | const lib = b.addStaticLibrary("example", "src/main.zig"); | |
| 10589 | lib.setBuildMode(mode); | |
| 10606 | pub fn build(b: *std.Build) void { | |
| 10607 | const optimize = b.standardOptimizeOption(.{}); | |
| 10608 | const lib = b.addStaticLibrary(.{ | |
| 10609 | .name = "example", | |
| 10610 | .root_source_file = .{ .path = "src/main.zig" }, | |
| 10611 | .optimize = optimize, | |
| 10612 | }); | |
| 10590 | 10613 | lib.install(); |
| 10591 | 10614 | |
| 10592 | var main_tests = b.addTest("src/main.zig"); | |
| 10593 | main_tests.setBuildMode(mode); | |
| 10615 | const main_tests = b.addTest(.{ | |
| 10616 | .root_source_file = .{ .path = "src/main.zig" }, | |
| 10617 | .optimize = optimize, | |
| 10618 | }); | |
| 10594 | 10619 | |
| 10595 | 10620 | const test_step = b.step("test", "Run library tests"); |
| 10596 | 10621 | test_step.dependOn(&main_tests.step); |
| ... | ... | @@ -10949,12 +10974,17 @@ int main(int argc, char **argv) { |
| 10949 | 10974 | } |
| 10950 | 10975 | {#end_syntax_block#} |
| 10951 | 10976 | {#code_begin|syntax|build_c#} |
| 10952 | const Builder = @import("std").build.Builder; | |
| 10953 | ||
| 10954 | pub fn build(b: *Builder) void { | |
| 10955 | const lib = b.addSharedLibrary("mathtest", "mathtest.zig", b.version(1, 0, 0)); | |
| 10977 | const std = @import("std"); | |
| 10956 | 10978 | |
| 10957 | const exe = b.addExecutable("test", null); | |
| 10979 | pub fn build(b: *std.Build) void { | |
| 10980 | const lib = b.addSharedLibrary(.{ | |
| 10981 | .name = "mathtest", | |
| 10982 | .root_source_file = .{ .path = "mathtest.zig" }, | |
| 10983 | .version = .{ .major = 1, .minor = 0, .patch = 0 }, | |
| 10984 | }); | |
| 10985 | const exe = b.addExecutable(.{ | |
| 10986 | .name = "test", | |
| 10987 | }); | |
| 10958 | 10988 | exe.addCSourceFile("test.c", &[_][]const u8{"-std=c99"}); |
| 10959 | 10989 | exe.linkLibrary(lib); |
| 10960 | 10990 | exe.linkSystemLibrary("c"); |
| ... | ... | @@ -11011,12 +11041,17 @@ int main(int argc, char **argv) { |
| 11011 | 11041 | } |
| 11012 | 11042 | {#end_syntax_block#} |
| 11013 | 11043 | {#code_begin|syntax|build_object#} |
| 11014 | const Builder = @import("std").build.Builder; | |
| 11044 | const std = @import("std"); | |
| 11015 | 11045 | |
| 11016 | pub fn build(b: *Builder) void { | |
| 11017 | const obj = b.addObject("base64", "base64.zig"); | |
| 11046 | pub fn build(b: *std.Build) void { | |
| 11047 | const obj = b.addObject(.{ | |
| 11048 | .name = "base64", | |
| 11049 | .root_source_file = .{ .path = "base64.zig" }, | |
| 11050 | }); | |
| 11018 | 11051 | |
| 11019 | const exe = b.addExecutable("test", null); | |
| 11052 | const exe = b.addExecutable(.{ | |
| 11053 | .name = "test", | |
| 11054 | }); | |
| 11020 | 11055 | exe.addCSourceFile("test.c", &[_][]const u8{"-std=c99"}); |
| 11021 | 11056 | exe.addObject(obj); |
| 11022 | 11057 | exe.linkSystemLibrary("c"); |
lib/build_runner.zig+7-5| ... | ... | @@ -3,7 +3,6 @@ const std = @import("std"); |
| 3 | 3 | const builtin = @import("builtin"); |
| 4 | 4 | const io = std.io; |
| 5 | 5 | const fmt = std.fmt; |
| 6 | const Builder = std.build.Builder; | |
| 7 | 6 | const mem = std.mem; |
| 8 | 7 | const process = std.process; |
| 9 | 8 | const ArrayList = std.ArrayList; |
| ... | ... | @@ -42,12 +41,15 @@ pub fn main() !void { |
| 42 | 41 | return error.InvalidArgs; |
| 43 | 42 | }; |
| 44 | 43 | |
| 45 | const builder = try Builder.create( | |
| 44 | const host = try std.zig.system.NativeTargetInfo.detect(.{}); | |
| 45 | ||
| 46 | const builder = try std.Build.create( | |
| 46 | 47 | allocator, |
| 47 | 48 | zig_exe, |
| 48 | 49 | build_root, |
| 49 | 50 | cache_root, |
| 50 | 51 | global_cache_root, |
| 52 | host, | |
| 51 | 53 | ); |
| 52 | 54 | defer builder.destroy(); |
| 53 | 55 | |
| ... | ... | @@ -58,7 +60,7 @@ pub fn main() !void { |
| 58 | 60 | const stdout_stream = io.getStdOut().writer(); |
| 59 | 61 | |
| 60 | 62 | var install_prefix: ?[]const u8 = null; |
| 61 | var dir_list = Builder.DirList{}; | |
| 63 | var dir_list = std.Build.DirList{}; | |
| 62 | 64 | |
| 63 | 65 | // before arg parsing, check for the NO_COLOR environment variable |
| 64 | 66 | // if it exists, default the color setting to .off |
| ... | ... | @@ -230,7 +232,7 @@ pub fn main() !void { |
| 230 | 232 | }; |
| 231 | 233 | } |
| 232 | 234 | |
| 233 | fn usage(builder: *Builder, already_ran_build: bool, out_stream: anytype) !void { | |
| 235 | fn usage(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !void { | |
| 234 | 236 | // run the build script to collect the options |
| 235 | 237 | if (!already_ran_build) { |
| 236 | 238 | builder.resolveInstallPrefix(null, .{}); |
| ... | ... | @@ -330,7 +332,7 @@ fn usage(builder: *Builder, already_ran_build: bool, out_stream: anytype) !void |
| 330 | 332 | ); |
| 331 | 333 | } |
| 332 | 334 | |
| 333 | fn usageAndErr(builder: *Builder, already_ran_build: bool, out_stream: anytype) void { | |
| 335 | fn usageAndErr(builder: *std.Build, already_ran_build: bool, out_stream: anytype) void { | |
| 334 | 336 | usage(builder, already_ran_build, out_stream) catch {}; |
| 335 | 337 | process.exit(1); |
| 336 | 338 | } |
lib/compiler_rt/README.md+534-471| ... | ... | @@ -27,482 +27,545 @@ then statically linked and therefore is a transparent dependency for the |
| 27 | 27 | programmer. |
| 28 | 28 | For details see `../compiler_rt.zig`. |
| 29 | 29 | |
| 30 | The routines in this folder are listed below. | |
| 31 | Routines are annotated as `type source routine // description`, with `routine` | |
| 32 | being the name used in aforementioned `compiler_rt.zig`. | |
| 33 | `dev` means deviating from compiler_rt, `port` ported, `source` is the | |
| 34 | information source for the implementation, `none` means unimplemented. | |
| 35 | Some examples for the naming convention are: | |
| 36 | - dev source name_routine, name_routine2 various implementations for performance, simplicity etc | |
| 37 | - port llvm compiler-rt library routines from [LLVM](http://compiler-rt.llvm.org/) | |
| 38 | * LLVM emits library calls to compiler-rt, if the hardware lacks functionality | |
| 39 | - port musl libc routines from [musl](https://musl.libc.org/) | |
| 40 | If the library or information source is uncommon, use the entry `other` for `source`. | |
| 41 | Please do not break the search by inserting entries in another format than `impl space source`. | |
| 42 | ||
| 43 | 30 | Bugs should be solved by trying to duplicate the bug upstream, if possible. |
| 44 | 31 | * If the bug exists upstream, get it fixed upstream and port the fix downstream to Zig. |
| 45 | 32 | * If the bug only exists in Zig, use the corresponding C code and debug |
| 46 | 33 | both implementations side by side to figure out what is wrong. |
| 47 | 34 | |
| 48 | ## Integer library routines | |
| 49 | ||
| 50 | #### Integer Bit operations | |
| 51 | ||
| 52 | - dev HackersDelight __clzsi2 // count leading zeros | |
| 53 | - dev HackersDelight __clzdi2 // count leading zeros | |
| 54 | - dev HackersDelight __clzti2 // count leading zeros | |
| 55 | - dev HackersDelight __ctzsi2 // count trailing zeros | |
| 56 | - dev HackersDelight __ctzdi2 // count trailing zeros | |
| 57 | - dev HackersDelight __ctzti2 // count trailing zeros | |
| 58 | - dev __ctzsi2 __ffssi2 // find least significant 1 bit | |
| 59 | - dev __ctzsi2 __ffsdi2 // find least significant 1 bit | |
| 60 | - dev __ctzsi2 __ffsti2 // find least significant 1 bit | |
| 61 | - dev BitTwiddlingHacks __paritysi2 // bit parity | |
| 62 | - dev BitTwiddlingHacks __paritydi2 // bit parity | |
| 63 | - dev BitTwiddlingHacks __parityti2 // bit parity | |
| 64 | - dev TAOCP __popcountsi2 // bit population | |
| 65 | - dev TAOCP __popcountdi2 // bit population | |
| 66 | - dev TAOCP __popcountti2 // bit population | |
| 67 | - dev other __bswapsi2 // a byteswapped | |
| 68 | - dev other __bswapdi2 // a byteswapped | |
| 69 | - dev other __bswapti2 // a byteswapped | |
| 70 | ||
| 71 | #### Integer Comparison | |
| 72 | ||
| 73 | - port llvm __cmpsi2 // a,b: i32, (a<b)-> 0, (a==b) -> 1, (a>b) -> 2 | |
| 74 | - port llvm __cmpdi2 // a,b: i64 | |
| 75 | - port llvm __cmpti2 // a,b: i128 | |
| 76 | - port llvm __ucmpsi2 // a,b: u32, (a<b)-> 0, (a==b) -> 1, (a>b) -> 2 | |
| 77 | - port llvm __ucmpdi2 // a,b: u64 | |
| 78 | - port llvm __ucmpti2 // a,b: u128 | |
| 79 | ||
| 80 | #### Integer Arithmetic | |
| 81 | ||
| 82 | - none none __ashlsi3 // a,b: i32, a << b unused in llvm, TODO (e.g. used by rl78) | |
| 83 | - port llvm __ashldi3 // a,b: u64 | |
| 84 | - port llvm __ashlti3 // a,b: u128 | |
| 85 | - none none __ashrsi3 // a,b: i32, a >> b arithmetic (sign fill) TODO (e.g. used by rl78) | |
| 86 | - port llvm __ashrdi3 // .. | |
| 87 | - port llvm __ashrti3 // | |
| 88 | - none none __lshrsi3 // a,b: i32, a >> b logical (zero fill) TODO (e.g. used by rl78) | |
| 89 | - port llvm __lshrdi3 // | |
| 90 | - port llvm __lshrti3 // | |
| 91 | - port llvm __negdi2 // a: i32, -a, symbol-level compatibility with libgcc | |
| 92 | - port llvm __negti2 // unnecessary: unused in backends | |
| 93 | - port llvm __mulsi3 // a,b: i32, a * b | |
| 94 | - port llvm __muldi3 // | |
| 95 | - port llvm __multi3 // | |
| 96 | - port llvm __divsi3 // a,b: i32, a / b | |
| 97 | - port llvm __divdi3 // | |
| 98 | - port llvm __divti3 // | |
| 99 | - port llvm __udivsi3 // a,b: u32, a / b | |
| 100 | - port llvm __udivdi3 // | |
| 101 | - port llvm __udivti3 // | |
| 102 | - port llvm __modsi3 // a,b: i32, a % b | |
| 103 | - port llvm __moddi3 // | |
| 104 | - port llvm __modti3 // | |
| 105 | - port llvm __umodsi3 // a,b: u32, a % b | |
| 106 | - port llvm __umoddi3 // | |
| 107 | - port llvm __umodti3 // | |
| 108 | - port llvm __udivmoddi4 // a,b: u32, a / b, rem.* = a % b unsigned | |
| 109 | - port llvm __udivmodti4 // | |
| 110 | - port llvm __udivmodsi4 // | |
| 111 | - port llvm __divmodsi4 // a,b: i32, a / b, rem.* = a % b signed, ARM | |
| 112 | - port llvm __divmoddi4 // | |
| 113 | ||
| 114 | #### Integer Arithmetic with trapping overflow | |
| 115 | ||
| 116 | - dev BitTwiddlingHacks __absvsi2 // abs(a) | |
| 117 | - dev BitTwiddlingHacks __absvdi2 // abs(a) | |
| 118 | - dev BitTwiddlingHacks __absvti2 // abs(a) | |
| 119 | - port llvm __negvsi2 // -a symbol-level compatibility: libgcc | |
| 120 | - port llvm __negvdi2 // -a unnecessary: unused in backends | |
| 121 | - port llvm __negvti2 // -a | |
| 122 | - TODO upstreaming __addvsi3..__mulvti3 after testing panics works | |
| 123 | - dev HackersDelight __addvsi3 // a + b | |
| 124 | - dev HackersDelight __addvdi3 // | |
| 125 | - dev HackersDelight __addvti3 // | |
| 126 | - dev HackersDelight __subvsi3 // a - b | |
| 127 | - dev HackersDelight __subvdi3 // | |
| 128 | - dev HackersDelight __subvti3 // | |
| 129 | - dev HackersDelight __mulvsi3 // a * b | |
| 130 | - dev HackersDelight __mulvdi3 // | |
| 131 | - dev HackersDelight __mulvti3 // | |
| 132 | ||
| 133 | #### Integer Arithmetic which returns if overflow (would be faster without pointer) | |
| 134 | ||
| 135 | - dev HackersDelight __addosi4 // a + b, overflow->ov.*=1 else 0 | |
| 136 | - dev HackersDelight __addodi4 // (completeness + performance, llvm does not use them) | |
| 137 | - dev HackersDelight __addoti4 // | |
| 138 | - dev HackersDelight __subosi4 // a - b, overflow->ov.*=1 else 0 | |
| 139 | - dev HackersDelight __subodi4 // (completeness + performance, llvm does not use them) | |
| 140 | - dev HackersDelight __suboti4 // | |
| 141 | - dev HackersDelight __mulosi4 // a * b, overflow->ov.*=1 else 0 | |
| 142 | - dev HackersDelight __mulodi4 // (required by llvm) | |
| 143 | - dev HackersDelight __muloti4 // | |
| 144 | ||
| 145 | ## Float library routines | |
| 146 | ||
| 147 | TODO: review source of implementation | |
| 148 | ||
| 149 | #### Float Conversion | |
| 150 | ||
| 151 | - dev other __extendsfdf2 // a: f32 -> f64, TODO: missing tests | |
| 152 | - dev other __extendsftf2 // a: f32 -> f128 | |
| 153 | - dev llvm __extendsfxf2 // a: f32 -> f80, TODO: missing tests | |
| 154 | - dev other __extenddftf2 // a: f64 -> f128 | |
| 155 | - dev llvm __extenddfxf2 // a: f64 -> f80 | |
| 156 | - dev other __truncdfsf2 // a: f64 -> f32, rounding towards zero | |
| 157 | - dev other __trunctfdf2 // a: f128-> f64 | |
| 158 | - dev other __trunctfsf2 // a: f128-> f32 | |
| 159 | - dev llvm __truncxfsf2 // a: f80 -> f32, TODO: missing tests | |
| 160 | - dev llvm __truncxfdf2 // a: f80 -> f64, TODO: missing tests | |
| 161 | ||
| 162 | - dev unclear __fixsfsi // a: f32 -> i32, rounding towards zero | |
| 163 | - dev unclear __fixdfsi // a: f64 -> i32 | |
| 164 | - dev unclear __fixtfsi // a: f128-> i32 | |
| 165 | - dev unclear __fixxfsi // a: f80 -> i32, TODO: missing tests | |
| 166 | - dev unclear __fixsfdi // a: f32 -> i64, rounding towards zero | |
| 167 | - dev unclear __fixdfdi // .. | |
| 168 | - dev unclear __fixtfdi // | |
| 169 | - dev unclear __fixxfdi // TODO: missing tests | |
| 170 | - dev unclear __fixsfti // a: f32 -> i128, rounding towards zero | |
| 171 | - dev unclear __fixdfti // .. | |
| 172 | - dev unclear __fixtfdi // | |
| 173 | - dev unclear __fixxfti // TODO: missing tests | |
| 174 | ||
| 175 | - dev unclear __fixunssfsi // a: f32 -> u32, rounding towards zero. negative values become 0. | |
| 176 | - dev unclear __fixunsdfsi // .. | |
| 177 | - dev unclear __fixunstfsi // | |
| 178 | - dev unclear __fixunsxfsi // TODO: missing tests | |
| 179 | - dev unclear __fixunssfdi // a: f32 -> u64, rounding towards zero. negative values become 0. | |
| 180 | - dev unclear __fixunsdfdi // | |
| 181 | - dev unclear __fixunstfdi // | |
| 182 | - dev unclear __fixunsxfdi // TODO: missing tests | |
| 183 | - dev unclear __fixunssfti // a: f32 -> u128, rounding towards zero. negative values become 0. | |
| 184 | - dev unclear __fixunsdfti // | |
| 185 | - dev unclear __fixunstfdi // | |
| 186 | - dev unclear __fixunsxfti // TODO: some more tests needed for base coverage | |
| 187 | ||
| 188 | - dev unclear __floatsisf // a: i32 -> f32 | |
| 189 | - dev unclear __floatsidf // a: i32 -> f64, TODO: missing tests | |
| 190 | - dev unclear __floatsitf // .. | |
| 191 | - dev unclear __floatsixf // TODO: missing tests | |
| 192 | - dev unclear __floatdisf // a: i64 -> f32 | |
| 193 | - dev unclear __floatdidf // | |
| 194 | - dev unclear __floatditf // | |
| 195 | - dev unclear __floatdixf // TODO: missing tests | |
| 196 | - dev unclear __floattisf // a: i128-> f32 | |
| 197 | - dev unclear __floattidf // | |
| 198 | - dev unclear __floattitf // | |
| 199 | - dev unclear __floattixf // TODO: missing tests | |
| 200 | ||
| 201 | - dev unclear __floatunsisf // a: u32 -> f32 | |
| 202 | - dev unclear __floatunsidf // TODO: missing tests | |
| 203 | - dev unclear __floatunsitf // | |
| 204 | - dev unclear __floatunsixf // TODO: missing tests | |
| 205 | - dev unclear __floatundisf // a: u64 -> f32 | |
| 206 | - dev unclear __floatundidf // | |
| 207 | - dev unclear __floatunditf // | |
| 208 | - dev unclear __floatundixf // TODO: missing tests | |
| 209 | - dev unclear __floatuntisf // a: u128-> f32 | |
| 210 | - dev unclear __floatuntidf // | |
| 211 | - dev unclear __floatuntitf // | |
| 212 | - dev unclear __floatuntixf // TODO: missing tests | |
| 213 | ||
| 214 | #### Float Comparison | |
| 215 | ||
| 216 | - dev other __cmpsf2 // a,b:f32, (a<b)->-1,(a==b)->0,(a>b)->1,Nan->1 | |
| 217 | - dev other __cmpdf2 // exported from __lesf2, __ledf2, __letf2 (below) | |
| 218 | - dev other __cmptf2 // But: if NaN is a possibility, use another routine. | |
| 219 | - dev other __unordsf2 // a,b:f32, (a==+-NaN or b==+-NaN) -> !=0, else -> 0 | |
| 220 | - dev other __unorddf2 // __only reliable for (input!=NaN)__ | |
| 221 | - dev other __unordtf2 // TODO: missing tests | |
| 222 | - dev other __eqsf2 // (a!=NaN) and (b!=Nan) and (a==b) -> output=0 | |
| 223 | - dev other __eqdf2 // | |
| 224 | - dev other __eqtf2 // | |
| 225 | - dev other __nesf2 // (a==NaN) or (b==Nan) or (a!=b) -> output!=0 | |
| 226 | - dev other __nedf2 // | |
| 227 | - dev other __netf2 // __eqtf2 and __netf2 have same return value -> tested with __eqsf2 | |
| 228 | - dev other __gesf2 // (a!=Nan) and (b!=Nan) and (a>=b) -> output>=0 | |
| 229 | - dev other __gedf2 // | |
| 230 | - dev other __getf2 // TODO: missing tests | |
| 231 | - dev other __ltsf2 // (a!=Nan) and (b!=Nan) and (a<b) -> output<0 | |
| 232 | - dev other __ltdf2 // | |
| 233 | - dev other __lttf2 // TODO: missing tests | |
| 234 | - dev other __lesf2 // (a!=Nan) and (b!=Nan) and (a<=b) -> output<=0 | |
| 235 | - dev other __ledf2 // | |
| 236 | - dev other __letf2 // TODO: missing tests | |
| 237 | - dev other __gtsf2 // (a!=Nan) and (b!=Nan) and (a>b) -> output>0 | |
| 238 | - dev other __gtdf2 // | |
| 239 | - dev other __gttf2 // TODO: missing tests | |
| 240 | ||
| 241 | #### Float Arithmetic | |
| 242 | ||
| 243 | - dev unclear __addsf3 // a + b f32, TODO: missing tests | |
| 244 | - dev unclear __adddf3 // a + b f64, TODO: missing tests | |
| 245 | - dev unclear __addtf3 // a + b f128 | |
| 246 | - dev unclear __addxf3 // a + b f80 | |
| 247 | - dev unclear __aeabi_fadd // a + b f64 ARM: AAPCS | |
| 248 | - dev unclear __aeabi_dadd // a + b f64 ARM: AAPCS | |
| 249 | - dev unclear __subsf3 // a - b, TODO: missing tests | |
| 250 | - dev unclear __subdf3 // a - b, TODO: missing tests | |
| 251 | - dev unclear __subtf3 // a - b | |
| 252 | - dev unclear __subxf3 // a - b f80, TODO: missing tests | |
| 253 | - dev unclear __aeabi_fsub // a - b f64 ARM: AAPCS | |
| 254 | - dev unclear __aeabi_dsub // a - b f64 ARM: AAPCS | |
| 255 | - dev unclear __mulsf3 // a * b, TODO: missing tests | |
| 256 | - dev unclear __muldf3 // a * b, TODO: missing tests | |
| 257 | - dev unclear __multf3 // a * b | |
| 258 | - dev unclear __mulxf3 // a * b | |
| 259 | - dev unclear __divsf3 // a / b, TODO: review tests | |
| 260 | - dev unclear __divdf3 // a / b, TODO: review tests | |
| 261 | - dev unclear __divtf3 // a / b | |
| 262 | - dev unclear __divxf3 // a / b | |
| 263 | - dev unclear __negsf2 // -a symbol-level compatibility: libgcc uses this for the rl78 | |
| 264 | - dev unclear __negdf2 // -a unnecessary: can be lowered directly to a xor | |
| 265 | - dev unclear __negtf2 // -a, TODO: missing tests | |
| 266 | - dev unclear __negxf2 // -a, TODO: missing tests | |
| 267 | ||
| 268 | #### Floating point raised to integer power | |
| 269 | - dev unclear __powisf2 // a ^ b, TODO | |
| 270 | - dev unclear __powidf2 // | |
| 271 | - dev unclear __powitf2 // | |
| 272 | - dev unclear __powixf2 // | |
| 273 | - dev unclear __mulsc3 // (a+ib) * (c+id) | |
| 274 | - dev unclear __muldc3 // | |
| 275 | - dev unclear __multc3 // | |
| 276 | - dev unclear __mulxc3 // | |
| 277 | - dev unclear __divsc3 // (a+ib) * / (c+id) | |
| 278 | - dev unclear __divdc3 // | |
| 279 | - dev unclear __divtc3 // | |
| 280 | - dev unclear __divxc3 // | |
| 281 | ||
| 282 | ## Decimal float library routines | |
| 35 | Routines with status are given below. Sources were besides | |
| 36 | "The Art of Computer Programming" by Donald E. Knuth, "HackersDelight" by Henry S. Warren, | |
| 37 | "Bit Twiddling Hacks" collected by Sean Eron Anderson, "Berkeley SoftFloat" by John R. Hauser, | |
| 38 | LLVM "compiler-rt" as it was MIT-licensed, "musl libc" and thoughts + work of contributors. | |
| 39 | ||
| 40 | The compiler-rt routines have not yet been audited. | |
| 41 | See https://github.com/ziglang/zig/issues/1504. | |
| 42 | ||
| 43 | From left to right the columns mean 1. if the routine is implemented (✗ or ✓), | |
| 44 | 2. the name, 3. input (`a`), 4. input (`b`), 5. return value, | |
| 45 | 6. an explanation of the functionality, .. to repeat the comment from the | |
| 46 | column a row above and/or additional return values. | |
| 47 | Some routines have more extensive comments supplemented with a reference text. | |
| 48 | ||
| 49 | Integer and Float Operations | |
| 50 | ||
| 51 | | Done | Name | a | b | Out | Comment | | |
| 52 | | ------ | ------------- | ---- | ---- | ---- | ------------------------------ | | |
| 53 | | | | | | | **Integer Bit Operations** | | |
| 54 | | ✓ | __clzsi2 | u32 | ∅ | i32 | count leading zeroes | | |
| 55 | | ✓ | __clzdi2 | u64 | ∅ | i32 | count leading zeroes | | |
| 56 | | ✓ | __clzti2 | u128 | ∅ | i32 | count trailing zeros | | |
| 57 | | ✓ | __ctzsi2 | u32 | ∅ | i32 | count trailing zeros | | |
| 58 | | ✓ | __ctzdi2 | u64 | ∅ | i32 | count trailing zeros | | |
| 59 | | ✓ | __ctzti2 | u128 | ∅ | i32 | count leading zeroes | | |
| 60 | | ✓ | __ffssi2 | u32 | ∅ | i32 | count leading zeroes | | |
| 61 | | ✓ | __ffsdi2 | u64 | ∅ | i32 | count leading zeroes | | |
| 62 | | ✓ | __ffsti2 | u128 | ∅ | i32 | count leading zeroes | | |
| 63 | | ✓ | __paritysi2 | u32 | ∅ | i32 | find least significant 1 bit | | |
| 64 | | ✓ | __paritydi2 | u64 | ∅ | i32 | find least significant 1 bit | | |
| 65 | | ✓ | __parityti2 | u128 | ∅ | i32 | find least significant 1 bit | | |
| 66 | | ✓ | __popcountsi2 | u32 | ∅ | i32 | bit population | | |
| 67 | | ✓ | __popcountdi2 | u64 | ∅ | i32 | bit population | | |
| 68 | | ✓ | __popcountti2 | u128 | ∅ | i32 | bit population | | |
| 69 | | ✓ | __bswapsi2 | u32 | ∅ | i32 | bit parity | | |
| 70 | | ✓ | __bswapdi2 | u64 | ∅ | i32 | bit parity | | |
| 71 | | ✓ | __bswapti2 | u128 | ∅ | i32 | bit parity | | |
| 72 | | | | | | | **Integer Comparison** | | |
| 73 | | ✓ | __cmpsi2 | i32 | i32 | i32 | `(a<b) -> 0, (a==b) -> 1, (a>b) -> 2` | | |
| 74 | | ✓ | __cmpdi2 | i64 | i64 | i32 | .. | | |
| 75 | | ✓ | __cmpti2 | i128 | i128 | i32 | .. | | |
| 76 | | ✓ | __ucmpsi2 | i32 | i32 | i32 | `(a<b) -> 0, (a==b) -> 1, (a>b) -> 2` | | |
| 77 | | ✓ | __ucmpdi2 | i64 | i64 | i32 | .. | | |
| 78 | | ✓ | __ucmpti2 | i128 | i128 | i32 | .. | | |
| 79 | | | | | | | **Integer Arithmetic** | | |
| 80 | | ✗ | __ashlsi3 | i32 | i32 | i32 | `a << b` [^unused_rl78] | | |
| 81 | | ✓ | __ashldi3 | i64 | i32 | i64 | .. | | |
| 82 | | ✓ | __ashlti3 | i128 | i32 | i128 | .. | | |
| 83 | | ✓ | __aeabi_llsl | i32 | i32 | i32 | .. ARM | | |
| 84 | | ✗ | __ashrsi3 | i32 | i32 | i32 | `a >> b` arithmetic (sign fill) [^unused_rl78] | | |
| 85 | | ✓ | __ashrdi3 | i64 | i32 | i64 | .. | | |
| 86 | | ✓ | __ashrti3 | i128 | i32 | i128 | .. | | |
| 87 | | ✓ | __aeabi_lasr | i64 | i32 | i64 | .. ARM | | |
| 88 | | ✗ | __lshrsi3 | i32 | i32 | i32 | `a >> b` logical (zero fill) [^unused_rl78] | | |
| 89 | | ✓ | __lshrdi3 | i64 | i32 | i64 | .. | | |
| 90 | | ✓ | __lshrti3 | i128 | i32 | i128 | .. | | |
| 91 | | ✓ | __aeabi_llsr | i64 | i32 | i64 | .. ARM | | |
| 92 | | ✓ | __negsi2 | i32 | i32 | i32 | `-a` [^libgcc_compat] | | |
| 93 | | ✓ | __negdi2 | i64 | i64 | i64 | .. | | |
| 94 | | ✓ | __negti2 | i128 | i128 | i128 | .. | | |
| 95 | | ✓ | __mulsi3 | i32 | i32 | i32 | `a * b` | | |
| 96 | | ✓ | __muldi3 | i64 | i64 | i64 | .. | | |
| 97 | | ✓ | __multi3 | i128 | i128 | i128 | .. | | |
| 98 | | ✓ | __divsi3 | i32 | i32 | i32 | `a / b` | | |
| 99 | | ✓ | __divdi3 | i64 | i64 | i64 | .. | | |
| 100 | | ✓ | __divti3 | i128 | i128 | i128 | .. | | |
| 101 | | ✓ | __aeabi_idiv | i32 | i32 | i32 | .. ARM | | |
| 102 | | ✓ | __udivsi3 | u32 | u32 | u32 | `a / b` | | |
| 103 | | ✓ | __udivdi3 | u64 | u64 | u64 | .. | | |
| 104 | | ✓ | __udivti3 | u128 | u128 | u128 | .. | | |
| 105 | | ✓ | __aeabi_uidiv | i32 | i32 | i32 | .. ARM | | |
| 106 | | ✓ | __modsi3 | i32 | i32 | i32 | `a % b` | | |
| 107 | | ✓ | __moddi3 | i64 | i64 | i64 | .. | | |
| 108 | | ✓ | __modti3 | i128 | i128 | i128 | .. | | |
| 109 | | ✓ | __umodsi3 | u32 | u32 | u32 | `a % b` | | |
| 110 | | ✓ | __umoddi3 | u64 | u64 | u64 | .. | | |
| 111 | | ✓ | __umodti3 | u128 | u128 | u128 | .. | | |
| 112 | | ✓ | __udivmodsi4 | u32 | u32 | u32 | `a / b, rem.* = a % b` | | |
| 113 | | ✓ | __udivmoddi4 | u64 | u64 | u64 | .. | | |
| 114 | | ✓ | __udivmodti4 | u128 | u128 | u128 | .. | | |
| 115 | | ✓ | __divmodsi4 | i32 | i32 | i32 | `a / b, rem.* = a % b` | | |
| 116 | | ✓ | __divmoddi4 | i64 | i64 | i64 | .. | | |
| 117 | | ✗ | __divmodti4 | i128 | i128 | i128 | .. [^libgcc_compat] | | |
| 118 | | | | | | | **Integer Arithmetic with Trapping Overflow**| | |
| 119 | | ✓ | __absvsi2 | i32 | i32 | i32 | abs(a) | | |
| 120 | | ✓ | __absvdi2 | i64 | i64 | i64 | .. | | |
| 121 | | ✓ | __absvti2 | i128 | i128 | i128 | .. | | |
| 122 | | ✓ | __negvsi2 | i32 | i32 | i32 | `-a` [^libgcc_compat] | | |
| 123 | | ✓ | __negvdi2 | i64 | i64 | i64 | .. | | |
| 124 | | ✓ | __negvti2 | i128 | i128 | i128 | .. | | |
| 125 | | ✗ | __addvsi3 | i32 | i32 | i32 | `a + b` | | |
| 126 | | ✗ | __addvdi3 | i64 | i64 | i64 | .. | | |
| 127 | | ✗ | __addvti3 | i128 | i128 | i128 | .. | | |
| 128 | | ✗ | __subvsi3 | i32 | i32 | i32 | `a - b` | | |
| 129 | | ✗ | __subvdi3 | i64 | i64 | i64 | .. | | |
| 130 | | ✗ | __subvti3 | i128 | i128 | i128 | .. | | |
| 131 | | ✗ | __mulvsi3 | i32 | i32 | i32 | `a * b` | | |
| 132 | | ✗ | __mulvdi3 | i64 | i64 | i64 | .. | | |
| 133 | | ✗ | __mulvti3 | i128 | i128 | i128 | .. | | |
| 134 | | | | | | | **Integer Arithmetic which Return on Overflow** [^noptr_faster] | | |
| 135 | | ✓ | __addosi4 | i32 | i32 | i32 | `a + b`, overflow->ov.*=1 else 0 [^perf_addition] | | |
| 136 | | ✓ | __addodi4 | i64 | i64 | i64 | .. | | |
| 137 | | ✓ | __addoti4 | i128 | i128 | i128 | .. | | |
| 138 | | ✓ | __subosi4 | i32 | i32 | i32 | `a - b`, overflow->ov.*=1 else 0 [^perf_addition] | | |
| 139 | | ✓ | __subodi4 | i64 | i64 | i64 | .. | | |
| 140 | | ✓ | __suboti4 | i128 | i128 | i128 | .. | | |
| 141 | | ✓ | __mulosi4 | i32 | i32 | i32 | `a * b`, overflow->ov.*=1 else 0 | | |
| 142 | | ✓ | __mulodi4 | i64 | i64 | i64 | .. | | |
| 143 | | ✓ | __muloti4 | i128 | i128 | i128 | .. | | |
| 144 | | | | | | | **Float Conversion** | | |
| 145 | | ✓ | __extendsfdf2 | f32 | ∅ | f64 | .. | | |
| 146 | | ✓ | __extendsftf2 | f32 | ∅ | f128 | .. | | |
| 147 | | ✓ | __extendsfxf2 | f32 | ∅ | f80 | .. | | |
| 148 | | ✓ | __extenddftf2 | f64 | ∅ | f128 | .. | | |
| 149 | | ✓ | __extenddfxf2 | f64 | ∅ | f80 | .. | | |
| 150 | | ✓ | __truncsfhf2 | f32 | ∅ | f16 | rounding towards zero | | |
| 151 | | ✓ | __truncdfhf2 | f64 | ∅ | f16 | .. | | |
| 152 | | ✓ | __truncdfsf2 | f64 | ∅ | f32 | .. | | |
| 153 | | ✓ | __trunctfhf2 | f128 | ∅ | f16 | .. | | |
| 154 | | ✓ | __trunctfsf2 | f128 | ∅ | f32 | .. | | |
| 155 | | ✓ | __trunctfdf2 | f128 | ∅ | f64 | .. | | |
| 156 | | ✓ | __trunctfxf2 | f128 | ∅ | f80 | .. | | |
| 157 | | ✓ | __truncxfhf2 | f80 | ∅ | f16 | .. | | |
| 158 | | ✓ | __truncxfsf2 | f80 | ∅ | f32 | .. | | |
| 159 | | ✓ | __truncxfdf2 | f80 | ∅ | f64 | .. | | |
| 160 | | ✓ | __aeabi_f2h | f32 | ∅ | f16 | .. ARM | | |
| 161 | | ✓ | __gnu_f2h_ieee | f32 | ∅ | f16 | ..GNU naming convention | | |
| 162 | | ✓ | __aeabi_d2h | f64 | ∅ | f16 | .. ARM | | |
| 163 | | ✓ | __aeabi_d2f | f64 | ∅ | f32 | .. ARM | | |
| 164 | | ✓ | __trunckfsf2 | f128 | ∅ | f32 | .. PPC | | |
| 165 | | ✓ | _Qp_qtos |*f128 | ∅ | f32 | .. SPARC | | |
| 166 | | ✓ | __trunckfdf2 | f128 | ∅ | f64 | .. PPC | | |
| 167 | | ✓ | _Qp_qtod |*f128 | ∅ | f64 | .. SPARC | | |
| 168 | | ✓ | __fixhfsi | f16 | ∅ | i32 | rounding towards zero | | |
| 169 | | ✓ | __fixsfsi | f32 | ∅ | i32 | .. | | |
| 170 | | ✓ | __fixdfsi | f64 | ∅ | i32 | .. | | |
| 171 | | ✓ | __fixtfsi | f128 | ∅ | i32 | .. | | |
| 172 | | ✓ | __fixxfsi | f80 | ∅ | i32 | .. | | |
| 173 | | ✓ | __fixhfdi | f16 | ∅ | i64 | .. | | |
| 174 | | ✓ | __fixsfdi | f32 | ∅ | i64 | .. | | |
| 175 | | ✓ | __fixdfdi | f64 | ∅ | i64 | .. | | |
| 176 | | ✓ | __fixtfdi | f128 | ∅ | i64 | .. | | |
| 177 | | ✓ | __fixxfdi | f80 | ∅ | i64 | .. | | |
| 178 | | ✓ | __fixhfti | f16 | ∅ | i128 | .. | | |
| 179 | | ✓ | __fixsfti | f32 | ∅ | i128 | .. | | |
| 180 | | ✓ | __fixdfti | f64 | ∅ | i128 | .. | | |
| 181 | | ✓ | __fixtfti | f128 | ∅ | i128 | .. | | |
| 182 | | ✓ | __fixxfti | f80 | ∅ | i128 | .. | | |
| 183 | | ✓ | __fixunshfsi | f16 | ∅ | u32 | rounding towards zero. negative values become 0. | | |
| 184 | | ✓ | __fixunssfsi | f32 | ∅ | u32 | .. | | |
| 185 | | ✓ | __fixunsdfsi | f64 | ∅ | u32 | .. | | |
| 186 | | ✓ | __fixunstfsi | f128 | ∅ | u32 | .. | | |
| 187 | | ✓ | __fixunsxfsi | f80 | ∅ | u32 | .. | | |
| 188 | | ✓ | __fixunshfdi | f16 | ∅ | u64 | .. | | |
| 189 | | ✓ | __fixunssfdi | f32 | ∅ | u64 | .. | | |
| 190 | | ✓ | __fixunsdfdi | f64 | ∅ | u64 | .. | | |
| 191 | | ✓ | __fixunstfdi | f128 | ∅ | u64 | .. | | |
| 192 | | ✓ | __fixunsxfdi | f80 | ∅ | u64 | .. | | |
| 193 | | ✓ | __fixunshfti | f16 | ∅ | u128 | .. | | |
| 194 | | ✓ | __fixunssfti | f32 | ∅ | u128 | .. | | |
| 195 | | ✓ | __fixunsdfti | f64 | ∅ | u128 | .. | | |
| 196 | | ✓ | __fixunstfti | f128 | ∅ | u128 | .. | | |
| 197 | | ✓ | __fixunsxfti | f80 | ∅ | u128 | .. | | |
| 198 | | ✓ | __floatsihf | i32 | ∅ | f16 | int_to_float conversions | | |
| 199 | | ✓ | __floatsisf | i32 | ∅ | f32 | .. | | |
| 200 | | ✓ | __floatsidf | i32 | ∅ | f64 | .. | | |
| 201 | | ✓ | __floatsitf | i32 | ∅ | f128 | .. | | |
| 202 | | ✓ | __floatsixf | i32 | ∅ | f80 | .. | | |
| 203 | | ✓ | __floatdisf | i64 | ∅ | f32 | .. | | |
| 204 | | ✓ | __floatdidf | i64 | ∅ | f64 | .. | | |
| 205 | | ✓ | __floatditf | i64 | ∅ | f128 | .. | | |
| 206 | | ✓ | __floatdixf | i64 | ∅ | f80 | .. | | |
| 207 | | ✓ | __floattihf | i128 | ∅ | f16 | .. | | |
| 208 | | ✓ | __floattisf | i128 | ∅ | f32 | .. | | |
| 209 | | ✓ | __floattidf | i128 | ∅ | f64 | .. | | |
| 210 | | ✓ | __floattitf | i128 | ∅ | f128 | .. | | |
| 211 | | ✓ | __floattixf | i128 | ∅ | f80 | .. | | |
| 212 | | ✓ | __floatunsihf | u32 | ∅ | f16 | uint_to_float conversions | | |
| 213 | | ✓ | __floatunsisf | u32 | ∅ | f32 | .. | | |
| 214 | | ✓ | __floatunsidf | u32 | ∅ | f64 | .. | | |
| 215 | | ✓ | __floatunsitf | u32 | ∅ | f128 | .. | | |
| 216 | | ✓ | __floatunsixf | u32 | ∅ | f80 | .. | | |
| 217 | | ✓ | __floatundihf | u64 | ∅ | f16 | .. | | |
| 218 | | ✓ | __floatundisf | u64 | ∅ | f32 | .. | | |
| 219 | | ✓ | __floatundidf | u64 | ∅ | f64 | .. | | |
| 220 | | ✓ | __floatunditf | u64 | ∅ | f128 | .. | | |
| 221 | | ✓ | __floatundixf | u64 | ∅ | f80 | .. | | |
| 222 | | ✓ | __floatuntihf | u128 | ∅ | f16 | .. | | |
| 223 | | ✓ | __floatuntisf | u128 | ∅ | f32 | .. | | |
| 224 | | ✓ | __floatuntidf | u128 | ∅ | f64 | .. | | |
| 225 | | ✓ | __floatuntitf | u128 | ∅ | f128 | .. | | |
| 226 | | ✓ | __floatuntixf | u128 | ∅ | f80 | .. | | |
| 227 | | | | | | | **Float Comparison** | | |
| 228 | | ✓ | __cmphf2 | f16 | f16 | i32 | `(a<b)->-1, (a==b)->0, (a>b)->1, Nan->1` | | |
| 229 | | ✓ | __cmpsf2 | f32 | f32 | i32 | exported from __lesf2, __ledf2, __letf2 (below) | | |
| 230 | | ✓ | __cmpdf2 | f64 | f64 | i32 | But: if NaN is a possibility, use another routine. | | |
| 231 | | ✓ | __cmptf2 | f128 | f128 | i32 | .. | | |
| 232 | | ✓ | __cmpxf2 | f80 | f80 | i32 | .. | | |
| 233 | | ✓ | _Qp_cmp |*f128 |*f128 | i32 | .. SPARC | | |
| 234 | | ✓ | __unordhf2 | f16 | f16 | i32 | `(a==+-NaN or b==+-NaN) -> !=0, else -> 0` | | |
| 235 | | ✓ | __unordsf2 | f32 | f32 | i32 | .. | | |
| 236 | | ✓ | __unorddf2 | f64 | f64 | i32 | Note: only reliable for (input!=NaN) | | |
| 237 | | ✓ | __unordtf2 | f128 | f128 | i32 | .. | | |
| 238 | | ✓ | __unordxf2 | f80 | f80 | i32 | .. | | |
| 239 | | ✓ | __aeabi_fcmpun | f32 | f32 | i32 | .. ARM | | |
| 240 | | ✓ | __aeabi_dcmpun | f32 | f32 | i32 | .. ARM | | |
| 241 | | ✓ | __unordkf2 | f128 | f128 | i32 | .. PPC | | |
| 242 | | ✓ | __eqhf2 | f16 | f16 | i32 | `(a!=NaN) and (b!=Nan) and (a==b) -> output=0` | | |
| 243 | | ✓ | __eqsf2 | f32 | f32 | i32 | .. | | |
| 244 | | ✓ | __eqdf2 | f64 | f64 | i32 | .. | | |
| 245 | | ✓ | __eqtf2 | f128 | f128 | i32 | .. | | |
| 246 | | ✓ | __eqxf2 | f80 | f80 | i32 | .. | | |
| 247 | | ✓ | __aeabi_fcmpeq | f32 | f32 | i32 | .. ARM | | |
| 248 | | ✓ | __aeabi_dcmpeq | f32 | f32 | i32 | .. ARM | | |
| 249 | | ✓ | __eqkf2 | f128 | f128 | i32 | .. PPC | | |
| 250 | | ✓ | _Qp_feq |*f128 |*f128 | bool | .. SPARC | | |
| 251 | | ✓ | __nehf2 | f16 | f16 | i32 | `(a==NaN) or (b==Nan) or (a!=b) -> output!=0` | | |
| 252 | | ✓ | __nesf2 | f32 | f32 | i32 | Note: __eqXf2 and __neXf2 have same return value | | |
| 253 | | ✓ | __nedf2 | f64 | f64 | i32 | .. | | |
| 254 | | ✓ | __netf2 | f128 | f128 | i32 | .. | | |
| 255 | | ✓ | __nexf2 | f80 | f80 | i32 | .. | | |
| 256 | | ✓ | __nekf2 | f128 | f128 | i32 | .. PPC | | |
| 257 | | ✓ | _Qp_fne |*f128 |*f128 | bool | .. SPARC | | |
| 258 | | ✓ | __gehf2 | f16 | f16 | i32 | `(a!=Nan) and (b!=Nan) and (a>=b) -> output>=0` | | |
| 259 | | ✓ | __gesf2 | f32 | f32 | i32 | .. | | |
| 260 | | ✓ | __gedf2 | f64 | f64 | i32 | .. | | |
| 261 | | ✓ | __getf2 | f128 | f128 | i32 | .. | | |
| 262 | | ✓ | __gexf2 | f80 | f80 | i32 | .. | | |
| 263 | | ✓ | __gekf2 | f128 | f128 | i32 | .. PPC | | |
| 264 | | ✓ | _Qp_fge |*f128 |*f128 | bool | .. SPARC | | |
| 265 | | ✓ | __lthf2 | f16 | f16 | i32 | `(a!=Nan) and (b!=Nan) and (a<b) -> output<0` | | |
| 266 | | ✓ | __ltsf2 | f32 | f32 | i32 | .. | | |
| 267 | | ✓ | __ltdf2 | f64 | f64 | i32 | .. | | |
| 268 | | ✓ | __lttf2 | f128 | f128 | i32 | .. | | |
| 269 | | ✓ | __ltxf2 | f80 | f80 | i32 | .. | | |
| 270 | | ✓ | __ltkf2 | f128 | f128 | i32 | .. PPC | | |
| 271 | | ✓ | __aeabi_fcmplt | f32 | f32 | i32 | .. ARM | | |
| 272 | | ✓ | __aeabi_dcmplt | f32 | f32 | i32 | .. ARM | | |
| 273 | | ✓ | _Qp_flt |*f128 |*f128 | bool | .. SPARC | | |
| 274 | | ✓ | __lehf2 | f16 | f16 | i32 | `(a!=Nan) and (b!=Nan) and (a<=b) -> output<=0` | | |
| 275 | | ✓ | __lesf2 | f32 | f32 | i32 | .. | | |
| 276 | | ✓ | __ledf2 | f64 | f64 | i32 | .. | | |
| 277 | | ✓ | __letf2 | f128 | f128 | i32 | .. | | |
| 278 | | ✓ | __lexf2 | f80 | f80 | i32 | .. | | |
| 279 | | ✓ | __aeabi_fcmple | f32 | f32 | i32 | .. ARM | | |
| 280 | | ✓ | __aeabi_dcmple | f32 | f32 | i32 | .. ARM | | |
| 281 | | ✓ | __lekf2 | f128 | f128 | i32 | .. PPC | | |
| 282 | | ✓ | _Qp_fle |*f128 |*f128 | bool | .. SPARC | | |
| 283 | | ✓ | __gthf2 | f16 | f16 | i32 | `(a!=Nan) and (b!=Nan) and (a>b) -> output>0` | | |
| 284 | | ✓ | __gtsf2 | f32 | f32 | i32 | .. | | |
| 285 | | ✓ | __gtdf2 | f64 | f64 | i32 | .. | | |
| 286 | | ✓ | __gttf2 | f128 | f128 | i32 | .. | | |
| 287 | | ✓ | __gtxf2 | f80 | f80 | i32 | .. | | |
| 288 | | ✓ | __gtkf2 | f128 | f128 | i32 | .. PPC | | |
| 289 | | ✓ | _Qp_fgt |*f128 |*f128 | bool | .. SPARC | | |
| 290 | | | | | | | **Float Arithmetic** | | |
| 291 | | ✓ | __addhf3 | f32 | f32 | f32 | `a + b` | | |
| 292 | | ✓ | __addsf3 | f32 | f32 | f32 | .. | | |
| 293 | | ✓ | __adddf3 | f64 | f64 | f64 | .. | | |
| 294 | | ✓ | __addtf3 | f128 | f128 | f128 | .. | | |
| 295 | | ✓ | __addxf3 | f80 | f80 | f80 | .. | | |
| 296 | | ✓ | __aeabi_fadd | f32 | f32 | f32 | .. ARM | | |
| 297 | | ✓ | __aeabi_dadd | f64 | f64 | f64 | .. ARM | | |
| 298 | | ✓ | __addkf3 | f128 | f128 | f128 | .. PPC | | |
| 299 | | ✓ | _Qp_add |*f128 |*f128 | void | .. SPARC args *c,*a,*b c=a+b | | |
| 300 | | ✓ | __subhf3 | f32 | f32 | f32 | `a - b` | | |
| 301 | | ✓ | __subsf3 | f32 | f32 | f32 | .. | | |
| 302 | | ✓ | __subdf3 | f64 | f64 | f64 | .. | | |
| 303 | | ✓ | __subtf3 | f128 | f128 | f128 | .. | | |
| 304 | | ✓ | __subxf3 | f80 | f80 | f80 | .. | | |
| 305 | | ✓ | __aeabi_fsub | f32 | f32 | f32 | .. ARM | | |
| 306 | | ✓ | __aeabi_dsub | f64 | f64 | f64 | .. ARM | | |
| 307 | | ✓ | __subkf3 | f128 | f128 | f128 | .. PPC | | |
| 308 | | ✓ | _Qp_sub |*f128 |*f128 | void | .. SPARC args *c,*a,*b c=a-b | | |
| 309 | | ✓ | __mulhf3 | f32 | f32 | f32 | `a * b` | | |
| 310 | | ✓ | __mulsf3 | f32 | f32 | f32 | .. | | |
| 311 | | ✓ | __muldf3 | f64 | f64 | f64 | .. | | |
| 312 | | ✓ | __multf3 | f128 | f128 | f128 | .. | | |
| 313 | | ✓ | __mulxf3 | f80 | f80 | f80 | .. | | |
| 314 | | ✓ | __aeabi_fmul | f32 | f32 | f32 | .. ARM | | |
| 315 | | ✓ | __aeabi_dmul | f64 | f64 | f64 | .. ARM | | |
| 316 | | ✓ | __mulkf3 | f128 | f128 | f128 | .. PPC | | |
| 317 | | ✓ | _Qp_mul |*f128 |*f128 | void | .. SPARC args *c,*a,*b c=a*b | | |
| 318 | | ✓ | __divsf3 | f32 | f32 | f32 | `a / b` | | |
| 319 | | ✓ | __divdf3 | f64 | f64 | f64 | .. | | |
| 320 | | ✓ | __divtf3 | f128 | f128 | f128 | .. | | |
| 321 | | ✓ | __divxf3 | f80 | f80 | f80 | .. | | |
| 322 | | ✓ | __aeabi_fdiv | f32 | f32 | f32 | .. ARM | | |
| 323 | | ✓ | __aeabi_ddiv | f64 | f64 | f64 | .. ARM | | |
| 324 | | ✓ | __divkf3 | f128 | f128 | f128 | .. PPC | | |
| 325 | | ✓ | _Qp_div |*f128 |*f128 | void | .. SPARC args *c,*a,*b c=a*b | | |
| 326 | | ✓ | __negsf2 | f32 | ∅ | f32[^unused_rl78] | -a (can be lowered directly to a xor) | | |
| 327 | | ✓ | __negdf2 | f64 | ∅ | f64 | .. | | |
| 328 | | ✓ | __negtf2 | f128 | ∅ | f128 | .. | | |
| 329 | | ✓ | __negxf2 | f80 | ∅ | f80 | .. | | |
| 330 | | | | | | | **Floating point raised to integer power** | | |
| 331 | | ✗ | __powihf2 | f16 | f16 | f16 | `a ^ b` | | |
| 332 | | ✗ | __powisf2 | f32 | f32 | f32 | .. | | |
| 333 | | ✗ | __powidf2 | f64 | f64 | f64 | .. | | |
| 334 | | ✗ | __powitf2 | f128 | f128 | f128 | .. | | |
| 335 | | ✗ | __powixf2 | f80 | f80 | f80 | .. | | |
| 336 | | ✓ | __mulhc3 | all4 | f16 | f16 | `(a+ib) * (c+id)` | | |
| 337 | | ✓ | __mulsc3 | all4 | f32 | f32 | .. | | |
| 338 | | ✓ | __muldc3 | all4 | f64 | f64 | .. | | |
| 339 | | ✓ | __multc3 | all4 | f128 | f128 | .. | | |
| 340 | | ✓ | __mulxc3 | all4 | f80 | f80 | .. | | |
| 341 | | ✓ | __divhc3 | all4 | f16 | f16 | `(a+ib) / (c+id)` | | |
| 342 | | ✓ | __divsc3 | all4 | f32 | f32 | .. | | |
| 343 | | ✓ | __divdc3 | all4 | f64 | f64 | .. | | |
| 344 | | ✓ | __divtc3 | all4 | f128 | f128 | .. | | |
| 345 | | ✓ | __divxc3 | all4 | f80 | f80 | .. | | |
| 346 | ||
| 347 | [^unused_rl78]: Unused in LLVM, but used for example by rl78. | |
| 348 | [^libgcc_compat]: Unused in backends and for symbol-level compatibility with libgcc. | |
| 349 | [^noptr_faster]: Operations without pointer and without C struct semantics lead to better optimizations. | |
| 350 | [^perf_addition]: Has better performance than standard method due to 2s complement semantics. | |
| 351 | Not provided by LLVM and libgcc. | |
| 352 | ||
| 353 | Decimal float library routines | |
| 283 | 354 | |
| 284 | 355 | BID means Binary Integer Decimal encoding, DPD means Densely Packed Decimal encoding. |
| 285 | 356 | BID should be only chosen for binary data, DPD for decimal data (ASCII, Unicode etc). |
| 286 | If possible, use BCD instead of DPD to represent numbers not accurately representable | |
| 287 | in binary like the number 0.2. | |
| 288 | ||
| 289 | All routines are TODO. | |
| 290 | ||
| 291 | #### Decimal float Conversion | |
| 292 | ||
| 293 | - __dpd_extendsddd2 // dec32->dec64 | |
| 294 | - __bid_extendsddd2 // dec32->dec64 | |
| 295 | - __dpd_extendsdtd2 // dec32->dec128 | |
| 296 | - __bid_extendsdtd2 // dec32->dec128 | |
| 297 | - __dpd_extendddtd2 // dec64->dec128 | |
| 298 | - __bid_extendddtd2 // dec64->dec128 | |
| 299 | - __dpd_truncddsd2 // dec64->dec32 | |
| 300 | - __bid_truncddsd2 // dec64->dec32 | |
| 301 | - __dpd_trunctdsd2 // dec128->dec32 | |
| 302 | - __bid_trunctdsd2 // dec128->dec32 | |
| 303 | - __dpd_trunctddd2 // dec128->dec64 | |
| 304 | - __bid_trunctddd2 // dec128->dec64 | |
| 305 | ||
| 306 | - __dpd_extendsfdd // float->dec64 | |
| 307 | - __bid_extendsfdd // float->dec64 | |
| 308 | - __dpd_extendsftd // float->dec128 | |
| 309 | - __bid_extendsftd // float->dec128 | |
| 310 | - __dpd_extenddftd // double->dec128 | |
| 311 | - __bid_extenddftd // double->dec128 | |
| 312 | - __dpd_extendxftd // long double->dec128 | |
| 313 | - __bid_extendxftd // long double->dec128 | |
| 314 | - __dpd_truncdfsd // double->dec32 | |
| 315 | - __bid_truncdfsd // double->dec32 | |
| 316 | - __dpd_truncxfsd // long double->dec32 | |
| 317 | - __bid_truncxfsd // long double->dec32 | |
| 318 | - __dpd_trunctfsd // long double->dec32 | |
| 319 | - __bid_trunctfsd // long double->dec32 | |
| 320 | - __dpd_truncxfdd // long double->dec64 | |
| 321 | - __bid_truncxfdd // long double->dec64 | |
| 322 | - __dpd_trunctfdd // long double->dec64 | |
| 323 | - __bid_trunctfdd // long double->dec64 | |
| 324 | ||
| 325 | - __dpd_truncddsf // dec64->float | |
| 326 | - __bid_truncddsf // dec64->float | |
| 327 | - __dpd_trunctdsf // dec128->float | |
| 328 | - __bid_trunctdsf // dec128->float | |
| 329 | - __dpd_extendsddf // dec32->double | |
| 330 | - __bid_extendsddf // dec32->double | |
| 331 | - __dpd_trunctddf // dec128->double | |
| 332 | - __bid_trunctddf // dec128->double | |
| 333 | - __dpd_extendsdxf // dec32->long double | |
| 334 | - __bid_extendsdxf // dec32->long double | |
| 335 | - __dpd_extendddxf // dec64->long double | |
| 336 | - __bid_extendddxf // dec64->long double | |
| 337 | - __dpd_trunctdxf // dec128->long double | |
| 338 | - __bid_trunctdxf // dec128->long double | |
| 339 | - __dpd_extendsdtf // dec32->long double | |
| 340 | - __bid_extendsdtf // dec32->long double | |
| 341 | - __dpd_extendddtf // dec64->long double | |
| 342 | - __bid_extendddtf // dec64->long double | |
| 343 | ||
| 344 | Same size conversion: | |
| 345 | - __dpd_extendsfsd // float->dec32 | |
| 346 | - __bid_extendsfsd // float->dec32 | |
| 347 | - __dpd_extenddfdd // double->dec64 | |
| 348 | - __bid_extenddfdd // double->dec64 | |
| 349 | - __dpd_extendtftd //long double->dec128 | |
| 350 | - __bid_extendtftd //long double->dec128 | |
| 351 | - __dpd_truncsdsf // dec32->float | |
| 352 | - __bid_truncsdsf // dec32->float | |
| 353 | - __dpd_truncdddf // dec64->float | |
| 354 | - __bid_truncdddf // dec64->float | |
| 355 | - __dpd_trunctdtf // dec128->long double | |
| 356 | - __bid_trunctdtf // dec128->long double | |
| 357 | ||
| 358 | - __dpd_fixsdsi // dec32->int | |
| 359 | - __bid_fixsdsi // dec32->int | |
| 360 | - __dpd_fixddsi // dec64->int | |
| 361 | - __bid_fixddsi // dec64->int | |
| 362 | - __dpd_fixtdsi // dec128->int | |
| 363 | - __bid_fixtdsi // dec128->int | |
| 364 | ||
| 365 | - __dpd_fixsddi // dec32->long | |
| 366 | - __bid_fixsddi // dec32->long | |
| 367 | - __dpd_fixdddi // dec64->long | |
| 368 | - __bid_fixdddi // dec64->long | |
| 369 | - __dpd_fixtddi // dec128->long | |
| 370 | - __bid_fixtddi // dec128->long | |
| 371 | ||
| 372 | - __dpd_fixunssdsi // dec32->unsigned int, All negative values become zero. | |
| 373 | - __bid_fixunssdsi // dec32->unsigned int | |
| 374 | - __dpd_fixunsddsi // dec64->unsigned int | |
| 375 | - __bid_fixunsddsi // dec64->unsigned int | |
| 376 | - __dpd_fixunstdsi // dec128->unsigned int | |
| 377 | - __bid_fixunstdsi // dec128->unsigned int | |
| 378 | ||
| 379 | - __dpd_fixunssddi // dec32->unsigned long, All negative values become zero. | |
| 380 | - __bid_fixunssddi // dec32->unsigned long | |
| 381 | - __dpd_fixunsdddi // dec64->unsigned long | |
| 382 | - __bid_fixunsdddi // dec64->unsigned long | |
| 383 | - __dpd_fixunstddi // dec128->unsigned long | |
| 384 | - __bid_fixunstddi // dec128->unsigned long | |
| 385 | ||
| 386 | - __dpd_floatsisd // int->dec32 | |
| 387 | - __bid_floatsisd // int->dec32 | |
| 388 | - __dpd_floatsidd // int->dec64 | |
| 389 | - __bid_floatsidd // int->dec64 | |
| 390 | - __dpd_floatsitd // int->dec128 | |
| 391 | - __bid_floatsitd // int->dec128 | |
| 392 | ||
| 393 | - __dpd_floatdisd // long->dec32 | |
| 394 | - __bid_floatdisd // long->dec32 | |
| 395 | - __dpd_floatdidd // long->dec64 | |
| 396 | - __bid_floatdidd // long->dec64 | |
| 397 | - __dpd_floatditd // long->dec128 | |
| 398 | - __bid_floatditd // long->dec128 | |
| 399 | ||
| 400 | - __dpd_floatunssisd // unsigned int->dec32 | |
| 401 | - __bid_floatunssisd // unsigned int->dec32 | |
| 402 | - __dpd_floatunssidd // unsigned int->dec64 | |
| 403 | - __bid_floatunssidd // unsigned int->dec64 | |
| 404 | - __dpd_floatunssitd // unsigned int->dec128 | |
| 405 | - __bid_floatunssitd // unsigned int->dec128 | |
| 406 | ||
| 407 | - __dpd_floatunsdisd // unsigned long->dec32 | |
| 408 | - __bid_floatunsdisd // unsigned long->dec32 | |
| 409 | - __dpd_floatunsdidd // unsigned long->dec64 | |
| 410 | - __bid_floatunsdidd // unsigned long->dec64 | |
| 411 | - __dpd_floatunsditd // unsigned long->dec128 | |
| 412 | - __bid_floatunsditd // unsigned long->dec128 | |
| 413 | ||
| 414 | #### Decimal float Comparison | |
| 415 | ||
| 416 | All decimal float comparison routines return c_int. | |
| 417 | ||
| 418 | - __dpd_unordsd2 // a,b: dec32, a +-NaN or a +-NaN -> 1(nonzero), else -> 0 | |
| 419 | - __bid_unordsd2 // a,b: dec32 | |
| 420 | - __dpd_unorddd2 // a,b: dec64 | |
| 421 | - __bid_unorddd2 // a,b: dec64 | |
| 422 | - __dpd_unordtd2 // a,b: dec128 | |
| 423 | - __bid_unordtd2 // a,b: dec128 | |
| 424 | ||
| 425 | - __dpd_eqsd2 // a,b: dec32, a!=+-NaN and b!=+-Nan and a==b -> 0, else -> 1(nonzero) | |
| 426 | - __bid_eqsd2 // a,b: dec32 | |
| 427 | - __dpd_eqdd2 // a,b: dec64 | |
| 428 | - __bid_eqdd2 // a,b: dec64 | |
| 429 | - __dpd_eqtd2 // a,b: dec128 | |
| 430 | - __bid_eqtd2 // a,b: dec128 | |
| 431 | ||
| 432 | - __dpd_nesd2 // a,b: dec32, a==+-NaN or b==+-NaN or a!=b -> 1(nonzero), else -> 0 | |
| 433 | - __bid_nesd2 // a,b: dec32 | |
| 434 | - __dpd_nedd2 // a,b: dec64 | |
| 435 | - __bid_nedd2 // a,b: dec64 | |
| 436 | - __dpd_netd2 // a,b: dec128 | |
| 437 | - __bid_netd2 // a,b: dec128 | |
| 438 | ||
| 439 | - __dpd_gesd2 // a,b: dec32, a!=+-NaN and b!=+-NaN and a>=b -> >=0, else -> <0 | |
| 440 | - __bid_gesd2 // a,b: dec32 | |
| 441 | - __dpd_gedd2 // a,b: dec64 | |
| 442 | - __bid_gedd2 // a,b: dec64 | |
| 443 | - __dpd_getd2 // a,b: dec128 | |
| 444 | - __bid_getd2 // a,b: dec128 | |
| 445 | ||
| 446 | - __dpd_ltsd2 // a,b: dec32, a!=+-NaN and b!=+-NaN and a<b -> <0, else -> >=0 | |
| 447 | - __bid_ltsd2 // a,b: dec32 | |
| 448 | - __dpd_ltdd2 // a,b: dec64 | |
| 449 | - __bid_ltdd2 // a,b: dec64 | |
| 450 | - __dpd_lttd2 // a,b: dec128 | |
| 451 | - __bid_lttd2 // a,b: dec128 | |
| 452 | ||
| 453 | - __dpd_lesd2 // a,b: dec32, a!=+-NaN and b!=+-NaN and a<=b -> <=0, else -> >=0 | |
| 454 | - __bid_lesd2 // a,b: dec32 | |
| 455 | - __dpd_ledd2 // a,b: dec64 | |
| 456 | - __bid_ledd2 // a,b: dec64 | |
| 457 | - __dpd_letd2 // a,b: dec128 | |
| 458 | - __bid_letd2 // a,b: dec128 | |
| 459 | ||
| 460 | - __dpd_gtsd2 // a,b: dec32, a!=+-NaN and b!=+-NaN and a>b -> >0, else -> <=0 | |
| 461 | - __bid_gtsd2 // a,b: dec32 | |
| 462 | - __dpd_gtdd2 // a,b: dec64 | |
| 463 | - __bid_gtdd2 // a,b: dec64 | |
| 464 | - __dpd_gttd2 // a,b: dec128 | |
| 465 | - __bid_gttd2 // a,b: dec128 | |
| 466 | ||
| 467 | #### Decimal float Arithmetic | |
| 468 | ||
| 469 | These numbers include options with routines for +-0 and +-Nan. | |
| 470 | ||
| 471 | - __dpd_addsd3 // a,b: dec32 -> dec32, a + b | |
| 472 | - __bid_addsd3 // a,b: dec32 -> dec32 | |
| 473 | - __dpd_adddd3 // a,b: dec64 -> dec64 | |
| 474 | - __bid_adddd3 // a,b: dec64 -> dec64 | |
| 475 | - __dpd_addtd3 // a,b: dec128-> dec128 | |
| 476 | - __bid_addtd3 // a,b: dec128-> dec128 | |
| 477 | - __dpd_subsd3 // a,b: dec32, a - b | |
| 478 | - __bid_subsd3 // a,b: dec32 -> dec32 | |
| 479 | - __dpd_subdd3 // a,b: dec64 .. | |
| 480 | - __bid_subdd3 // a,b: dec64 | |
| 481 | - __dpd_subtd3 // a,b: dec128 | |
| 482 | - __bid_subtd3 // a,b: dec128 | |
| 483 | - __dpd_mulsd3 // a,b: dec32, a * b | |
| 484 | - __bid_mulsd3 // a,b: dec32 -> dec32 | |
| 485 | - __dpd_muldd3 // a,b: dec64 .. | |
| 486 | - __bid_muldd3 // a,b: dec64 | |
| 487 | - __dpd_multd3 // a,b: dec128 | |
| 488 | - __bid_multd3 // a,b: dec128 | |
| 489 | - __dpd_divsd3 // a,b: dec32, a / b | |
| 490 | - __bid_divsd3 // a,b: dec32 -> dec32 | |
| 491 | - __dpd_divdd3 // a,b: dec64 .. | |
| 492 | - __bid_divdd3 // a,b: dec64 | |
| 493 | - __dpd_divtd3 // a,b: dec128 | |
| 494 | - __bid_divtd3 // a,b: dec128 | |
| 495 | - __dpd_negsd2 // a,b: dec32, -a | |
| 496 | - __bid_negsd2 // a,b: dec32 -> dec32 | |
| 497 | - __dpd_negdd2 // a,b: dec64 .. | |
| 498 | - __bid_negdd2 // a,b: dec64 | |
| 499 | - __dpd_negtd2 // a,b: dec128 | |
| 500 | - __bid_negtd2 // a,b: dec128 | |
| 501 | ||
| 502 | ## Fixed-point fractional library routines | |
| 503 | ||
| 504 | TODO | |
| 505 | ||
| 506 | Too unclear for work items: | |
| 507 | - Miscellaneous routines => unclear, if supported (cache control and stack functions) | |
| 508 | - Zig-specific language runtime features, for example "Arbitrary length integer library routines" | |
| 357 | For example the number 0.2 is not accurately representable in binary data. | |
| 358 | ||
| 359 | | Done | Name | a | b | Out | Comment | | |
| 360 | | ------ | ------------- | --------- | --------- | --------- | ---------------------------- | | |
| 361 | | | | | | | **Decimal Float Conversion** | | |
| 362 | | ✗ | __dpd_extendsddd2 | dec32 | ∅ | dec64 | conversion | | |
| 363 | | ✗ | __bid_extendsddd2 | dec32 | ∅ | dec64 | .. | | |
| 364 | | ✗ | __dpd_extendsdtd2 | dec32 | ∅ | dec128| .. | | |
| 365 | | ✗ | __bid_extendsdtd2 | dec32 | ∅ | dec128| .. | | |
| 366 | | ✗ | __dpd_extendddtd2 | dec64 | ∅ | dec128| .. | | |
| 367 | | ✗ | __bid_extendddtd2 | dec64 | ∅ | dec128| .. | | |
| 368 | | ✗ | __dpd_truncddsd2 | dec64 | ∅ | dec32 | .. | | |
| 369 | | ✗ | __bid_truncddsd2 | dec64 | ∅ | dec32 | .. | | |
| 370 | | ✗ | __dpd_trunctdsd2 | dec128 | ∅ | dec32 | .. | | |
| 371 | | ✗ | __bid_trunctdsd2 | dec128 | ∅ | dec32 | .. | | |
| 372 | | ✗ | __dpd_trunctddd2 | dec128 | ∅ | dec64 | .. | | |
| 373 | | ✗ | __bid_trunctddd2 | dec128 | ∅ | dec64 | .. | | |
| 374 | | ✗ | __dpd_extendsfdd | float | ∅ | dec64 | .. | | |
| 375 | | ✗ | __bid_extendsfdd | float | ∅ | dec64 | .. | | |
| 376 | | ✗ | __dpd_extendsftd | float | ∅ | dec128| .. | | |
| 377 | | ✗ | __bid_extendsftd | float | ∅ | dec128| .. | | |
| 378 | | ✗ | __dpd_extenddftd | double | ∅ | dec128| .. | | |
| 379 | | ✗ | __bid_extenddftd | double | ∅ | dec128| .. | | |
| 380 | | ✗ | __dpd_extendxftd |long double | ∅ | dec128| .. | | |
| 381 | | ✗ | __bid_extendxftd |long double | ∅ | dec128| .. | | |
| 382 | | ✗ | __dpd_truncdfsd | double | ∅ | dec32 | .. | | |
| 383 | | ✗ | __bid_truncdfsd | double | ∅ | dec32 | .. | | |
| 384 | | ✗ | __dpd_truncxfsd |long double | ∅ | dec32 | .. | | |
| 385 | | ✗ | __bid_truncxfsd |long double | ∅ | dec32 | .. | | |
| 386 | | ✗ | __dpd_trunctfsd |long double | ∅ | dec32 | .. | | |
| 387 | | ✗ | __bid_trunctfsd |long double | ∅ | dec32 | .. | | |
| 388 | | ✗ | __dpd_truncxfdd |long double | ∅ | dec64 | .. | | |
| 389 | | ✗ | __bid_truncxfdd |long double | ∅ | dec64 | .. | | |
| 390 | | ✗ | __dpd_trunctfdd |long double | ∅ | dec64 | .. | | |
| 391 | | ✗ | __bid_trunctfdd |long double | ∅ | dec64 | .. | | |
| 392 | | ✗ | __dpd_truncddsf | dec64 | ∅ | float | .. | | |
| 393 | | ✗ | __bid_truncddsf | dec64 | ∅ | float | .. | | |
| 394 | | ✗ | __dpd_trunctdsf | dec128 | ∅ | float | .. | | |
| 395 | | ✗ | __bid_trunctdsf | dec128 | ∅ | float | .. | | |
| 396 | | ✗ | __dpd_extendsddf | dec32 | ∅ | double| .. | | |
| 397 | | ✗ | __bid_extendsddf | dec32 | ∅ | double| .. | | |
| 398 | | ✗ | __dpd_trunctddf | dec128 | ∅ | double| .. | | |
| 399 | | ✗ | __bid_trunctddf | dec128 | ∅ | double| .. | | |
| 400 | | ✗ | __dpd_extendsdxf | dec32 | ∅ |long double| .. | | |
| 401 | | ✗ | __bid_extendsdxf | dec32 | ∅ |long double| .. | | |
| 402 | | ✗ | __dpd_extendddxf | dec64 | ∅ |long double| .. | | |
| 403 | | ✗ | __bid_extendddxf | dec64 | ∅ |long double| .. | | |
| 404 | | ✗ | __dpd_trunctdxf | dec128 | ∅ |long double| .. | | |
| 405 | | ✗ | __bid_trunctdxf | dec128 | ∅ |long double| .. | | |
| 406 | | ✗ | __dpd_extendsdtf | dec32 | ∅ |long double| .. | | |
| 407 | | ✗ | __bid_extendsdtf | dec32 | ∅ |long double| .. | | |
| 408 | | ✗ | __dpd_extendddtf | dec64 | ∅ |long double| .. | | |
| 409 | | ✗ | __bid_extendddtf | dec64 | ∅ |long double| .. | | |
| 410 | | ✗ | __dpd_extendsfsd | float | ∅ | dec32 | same size conversions | | |
| 411 | | ✗ | __bid_extendsfsd | float | ∅ | dec32 | .. | | |
| 412 | | ✗ | __dpd_extenddfdd | double | ∅ | dec64 | .. | | |
| 413 | | ✗ | __bid_extenddfdd | double | ∅ | dec64 | .. | | |
| 414 | | ✗ | __dpd_extendtftd |long double | ∅ | dec128| .. | | |
| 415 | | ✗ | __bid_extendtftd |long double | ∅ | dec128| .. | | |
| 416 | | ✗ | __dpd_truncsdsf | dec32 | ∅ | float | .. | | |
| 417 | | ✗ | __bid_truncsdsf | dec32 | ∅ | float | .. | | |
| 418 | | ✗ | __dpd_truncdddf | dec64 | ∅ | float | conversion | | |
| 419 | | ✗ | __bid_truncdddf | dec64 | ∅ | float | .. | | |
| 420 | | ✗ | __dpd_trunctdtf | dec128 | ∅ |long double| .. | | |
| 421 | | ✗ | __bid_trunctdtf | dec128 | ∅ |long double| .. | | |
| 422 | | ✗ | __dpd_fixsdsi | dec32 | ∅ | int | .. | | |
| 423 | | ✗ | __bid_fixsdsi | dec32 | ∅ | int | .. | | |
| 424 | | ✗ | __dpd_fixddsi | dec64 | ∅ | int | .. | | |
| 425 | | ✗ | __bid_fixddsi | dec64 | ∅ | int | .. | | |
| 426 | | ✗ | __dpd_fixtdsi | dec128 | ∅ | int | .. | | |
| 427 | | ✗ | __bid_fixtdsi | dec128 | ∅ | int | .. | | |
| 428 | | ✗ | __dpd_fixsddi | dec32 | ∅ | long | .. | | |
| 429 | | ✗ | __bid_fixsddi | dec32 | ∅ | long | .. | | |
| 430 | | ✗ | __dpd_fixdddi | dec64 | ∅ | long | .. | | |
| 431 | | ✗ | __bid_fixdddi | dec64 | ∅ | long | .. | | |
| 432 | | ✗ | __dpd_fixtddi | dec128 | ∅ | long | .. | | |
| 433 | | ✗ | __bid_fixtddi | dec128 | ∅ | long | .. | | |
| 434 | | ✗ | __dpd_fixunssdsi | dec32 | ∅ |unsigned int | .. All negative values become zero. | | |
| 435 | | ✗ | __bid_fixunssdsi | dec32 | ∅ |unsigned int | .. | | |
| 436 | | ✗ | __dpd_fixunsddsi | dec64 | ∅ |unsigned int | .. | | |
| 437 | | ✗ | __bid_fixunsddsi | dec64 | ∅ |unsigned int | .. | | |
| 438 | | ✗ | __dpd_fixunstdsi | dec128 | ∅ |unsigned int | .. | | |
| 439 | | ✗ | __bid_fixunstdsi | dec128 | ∅ |unsigned int | .. | | |
| 440 | | ✗ | __dpd_fixunssddi | dec32 | ∅ |unsigned long| .. | | |
| 441 | | ✗ | __bid_fixunssddi | dec32 | ∅ |unsigned long| .. | | |
| 442 | | ✗ | __dpd_fixunsdddi | dec64 | ∅ |unsigned long| .. | | |
| 443 | | ✗ | __bid_fixunsdddi | dec64 | ∅ |unsigned long| .. | | |
| 444 | | ✗ | __dpd_fixunstddi | dec128 | ∅ |unsigned long| .. | | |
| 445 | | ✗ | __bid_fixunstddi | dec128 | ∅ |unsigned long| .. | | |
| 446 | | ✗ | __dpd_floatsisd | int | ∅ | dec32 | .. | | |
| 447 | | ✗ | __bid_floatsisd | int | ∅ | dec32 | .. | | |
| 448 | | ✗ | __dpd_floatsidd | int | ∅ | dec64 | .. | | |
| 449 | | ✗ | __bid_floatsidd | int | ∅ | dec64 | .. | | |
| 450 | | ✗ | __dpd_floatsitd | int | ∅ | dec128 | .. | | |
| 451 | | ✗ | __bid_floatsitd | int | ∅ | dec128 | .. | | |
| 452 | | ✗ | __dpd_floatdisd | long | ∅ | dec32 | .. | | |
| 453 | | ✗ | __bid_floatdisd | long | ∅ | dec32 | .. | | |
| 454 | | ✗ | __dpd_floatdidd | long | ∅ | dec64 | .. | | |
| 455 | | ✗ | __bid_floatdidd | long | ∅ | dec64 | .. | | |
| 456 | | ✗ | __dpd_floatditd | long | ∅ | dec128 | .. | | |
| 457 | | ✗ | __bid_floatditd | long | ∅ | dec128 | .. | | |
| 458 | | ✗ | __dpd_floatunssisd | unsigned int| ∅ | dec32 | .. | | |
| 459 | | ✗ | __bid_floatunssisd | unsigned int| ∅ | dec32 | .. | | |
| 460 | | ✗ | __dpd_floatunssidd | unsigned int| ∅ | dec64 | .. | | |
| 461 | | ✗ | __bid_floatunssidd | unsigned int| ∅ | dec64 | .. | | |
| 462 | | ✗ | __dpd_floatunssitd | unsigned int| ∅ | dec128 | .. | | |
| 463 | | ✗ | __bid_floatunssitd | unsigned int| ∅ | dec128 | .. | | |
| 464 | | ✗ | __dpd_floatunsdisd |unsigned long| ∅ | dec32 | .. | | |
| 465 | | ✗ | __bid_floatunsdisd |unsigned long| ∅ | dec32 | .. | | |
| 466 | | ✗ | __dpd_floatunsdidd |unsigned long| ∅ | dec64 | .. | | |
| 467 | | ✗ | __bid_floatunsdidd |unsigned long| ∅ | dec64 | .. | | |
| 468 | | ✗ | __dpd_floatunsditd |unsigned long| ∅ | dec128 | .. | | |
| 469 | | ✗ | __bid_floatunsditd |unsigned long| ∅ | dec128 | .. | | |
| 470 | | | | | | | **Decimal Float Comparison** | | |
| 471 | | ✗ | __dpd_unordsd2 | dec32 | dec32 | c_int | `a +-NaN or a +-NaN -> 1(nonzero), else -> 0` | | |
| 472 | | ✗ | __bid_unordsd2 | dec32 | dec32 | c_int | .. | | |
| 473 | | ✗ | __dpd_unorddd2 | dec64 | dec64 | c_int | .. | | |
| 474 | | ✗ | __bid_unorddd2 | dec64 | dec64 | c_int | .. | | |
| 475 | | ✗ | __dpd_unordtd2 | dec128 | dec128 | c_int | .. | | |
| 476 | | ✗ | __bid_unordtd2 | dec128 | dec128 | c_int | .. | | |
| 477 | | ✗ | __dpd_eqsd2 | dec32 | dec32 | c_int |`a!=+-NaN and b!=+-Nan and a==b -> 0, else -> 1(nonzero)`| | |
| 478 | | ✗ | __bid_eqsd2 | dec32 | dec32 | c_int | .. | | |
| 479 | | ✗ | __dpd_eqdd2 | dec64 | dec64 | c_int | .. | | |
| 480 | | ✗ | __bid_eqdd2 | dec64 | dec64 | c_int | .. | | |
| 481 | | ✗ | __dpd_eqtd2 | dec128 | dec128 | c_int | .. | | |
| 482 | | ✗ | __bid_eqtd2 | dec128 | dec128 | c_int | .. | | |
| 483 | | ✗ | __dpd_nesd2 | dec32 | dec32 | c_int | `a==+-NaN or b==+-NaN or a!=b -> 1(nonzero), else -> 0` | | |
| 484 | | ✗ | __bid_nesd2 | dec32 | dec32 | c_int | .. | | |
| 485 | | ✗ | __dpd_nedd2 | dec64 | dec64 | c_int | .. | | |
| 486 | | ✗ | __bid_nedd2 | dec64 | dec64 | c_int | .. | | |
| 487 | | ✗ | __dpd_netd2 | dec128 | dec128 | c_int | .. | | |
| 488 | | ✗ | __bid_netd2 | dec128 | dec128 | c_int | .. | | |
| 489 | | ✗ | __dpd_gesd2 | dec32 | dec32 | c_int | `a!=+-NaN and b!=+-NaN and a>=b -> >=0, else -> <0` | | |
| 490 | | ✗ | __bid_gesd2 | dec32 | dec32 | c_int | .. | | |
| 491 | | ✗ | __dpd_gedd2 | dec64 | dec64 | c_int | .. | | |
| 492 | | ✗ | __bid_gedd2 | dec64 | dec64 | c_int | .. | | |
| 493 | | ✗ | __dpd_getd2 | dec128 | dec128 | c_int | .. | | |
| 494 | | ✗ | __bid_getd2 | dec128 | dec128 | c_int | .. | | |
| 495 | | ✗ | __dpd_ltsd2 | dec32 | dec32 | c_int | `a!=+-NaN and b!=+-NaN and a<b -> <0, else -> >=0` | | |
| 496 | | ✗ | __bid_ltsd2 | dec32 | dec32 | c_int | .. | | |
| 497 | | ✗ | __dpd_ltdd2 | dec64 | dec64 | c_int | .. | | |
| 498 | | ✗ | __bid_ltdd2 | dec64 | dec64 | c_int | .. | | |
| 499 | | ✗ | __dpd_lttd2 | dec128 | dec128 | c_int | .. | | |
| 500 | | ✗ | __bid_lttd2 | dec128 | dec128 | c_int | .. | | |
| 501 | | ✗ | __dpd_lesd2 | dec32 | dec32 | c_int | `a!=+-NaN and b!=+-NaN and a<=b -> <=0, else -> >=0` | | |
| 502 | | ✗ | __bid_lesd2 | dec32 | dec32 | c_int | .. | | |
| 503 | | ✗ | __dpd_ledd2 | dec64 | dec64 | c_int | .. | | |
| 504 | | ✗ | __bid_ledd2 | dec64 | dec64 | c_int | .. | | |
| 505 | | ✗ | __dpd_letd2 | dec128 | dec128 | c_int | .. | | |
| 506 | | ✗ | __bid_letd2 | dec128 | dec128 | c_int | .. | | |
| 507 | | ✗ | __dpd_gtsd2 | dec32 | dec32 | c_int | `a!=+-NaN and b!=+-NaN and a>b -> >0, else -> <=0` | | |
| 508 | | ✗ | __bid_gtsd2 | dec32 | dec32 | c_int | .. | | |
| 509 | | ✗ | __dpd_gtdd2 | dec64 | dec64 | c_int | .. | | |
| 510 | | ✗ | __bid_gtdd2 | dec64 | dec64 | c_int | .. | | |
| 511 | | ✗ | __dpd_gttd2 | dec128 | dec128 | c_int | .. | | |
| 512 | | ✗ | __bid_gttd2 | dec128 | dec128 | c_int | .. | | |
| 513 | | | | | | | **Decimal Float Arithmetic**[^options] | | |
| 514 | | ✗ | __dpd_addsd3 | dec32 | dec32 | dec32 |`a + b`| | |
| 515 | | ✗ | __bid_addsd3 | dec32 | dec32 | dec32 | .. | | |
| 516 | | ✗ | __dpd_adddd3 | dec64 | dec64 | dec64 | .. | | |
| 517 | | ✗ | __bid_adddd3 | dec64 | dec64 | dec64 | .. | | |
| 518 | | ✗ | __dpd_addtd3 | dec128 | dec128 | dec128 | .. | | |
| 519 | | ✗ | __bid_addtd3 | dec128 | dec128 | dec128 | .. | | |
| 520 | | ✗ | __dpd_subsd3 | dec32 | dec32 | dec32 |`a - b`| | |
| 521 | | ✗ | __bid_subsd3 | dec32 | dec32 | dec32 | .. | | |
| 522 | | ✗ | __dpd_subdd3 | dec64 | dec64 | dec64 | .. | | |
| 523 | | ✗ | __bid_subdd3 | dec64 | dec64 | dec64 | .. | | |
| 524 | | ✗ | __dpd_subtd3 | dec128 | dec128 | dec128 | .. | | |
| 525 | | ✗ | __bid_subtd3 | dec128 | dec128 | dec128 | .. | | |
| 526 | | ✗ | __dpd_mulsd3 | dec32 | dec32 | dec32 |`a * b`| | |
| 527 | | ✗ | __bid_mulsd3 | dec32 | dec32 | dec32 | .. | | |
| 528 | | ✗ | __dpd_muldd3 | dec64 | dec64 | dec64 | .. | | |
| 529 | | ✗ | __bid_muldd3 | dec64 | dec64 | dec64 | .. | | |
| 530 | | ✗ | __dpd_multd3 | dec128 | dec128 | dec128 | .. | | |
| 531 | | ✗ | __bid_multd3 | dec128 | dec128 | dec128 | .. | | |
| 532 | | ✗ | __dpd_divsd3 | dec32 | dec32 | dec32 |`a / b`| | |
| 533 | | ✗ | __bid_divsd3 | dec32 | dec32 | dec32 | .. | | |
| 534 | | ✗ | __dpd_divdd3 | dec64 | dec64 | dec64 | .. | | |
| 535 | | ✗ | __bid_divdd3 | dec64 | dec64 | dec64 | .. | | |
| 536 | | ✗ | __dpd_divtd3 | dec128 | dec128 | dec128 | .. | | |
| 537 | | ✗ | __bid_divtd3 | dec128 | dec128 | dec128 | .. | | |
| 538 | | ✗ | __dpd_negsd2 | dec32 | dec32 | dec32 | `-a` | | |
| 539 | | ✗ | __bid_negsd2 | dec32 | dec32 | dec32 | .. | | |
| 540 | | ✗ | __dpd_negdd2 | dec64 | dec64 | dec64 | .. | | |
| 541 | | ✗ | __bid_negdd2 | dec64 | dec64 | dec64 | .. | | |
| 542 | | ✗ | __dpd_negtd2 | dec128 | dec128 | dec128 | .. | | |
| 543 | | ✗ | __bid_negtd2 | dec128 | dec128 | dec128 | .. | | |
| 544 | ||
| 545 | [^options]: These numbers include options with routines for +-0 and +-Nan. | |
| 546 | ||
| 547 | Fixed-point fractional library routines | |
| 548 | ||
| 549 | TODO brief explanation + implementation | |
| 550 | ||
| 551 | | Done | Name | a | b | Out | Comment | | |
| 552 | | ------ | ------------- | --------- | --------- | --------- | -------------------------- | | |
| 553 | | | | | | | **Fixed-Point Fractional** | | |
| 554 | ||
| 555 | Further content: | |
| 556 | - aarch64 outline atomics | |
| 557 | - atomics | |
| 558 | - msvc things like _alldiv, _aulldiv, _allrem | |
| 559 | - clear cache | |
| 560 | - tls emulation | |
| 561 | - math routines (cos, sin, tan, ceil, floor, exp, exp2, fabs, log, log10, log2, sincos, sqrt) | |
| 562 | - bcmp | |
| 563 | - ieee float routines (fma, fmax, fmin, fmod, fabs, float rounding, ) | |
| 564 | - arm routines (memory routines + memclr [setting to 0], divmod routines and stubs for unwind_cpp) | |
| 565 | - memory routines (memcmp, memcpy, memset, memmove) | |
| 566 | - objective-c __isPlatformVersionAtLeast check | |
| 567 | - stack probe routines | |
| 568 | ||
| 569 | Future work | |
| 570 | ||
| 571 | Arbitrary length integer library routines |
lib/compiler_rt/atomics.zig+104| ... | ... | @@ -192,6 +192,10 @@ fn __atomic_load_8(src: *u64, model: i32) callconv(.C) u64 { |
| 192 | 192 | return atomic_load_N(u64, src, model); |
| 193 | 193 | } |
| 194 | 194 | |
| 195 | fn __atomic_load_16(src: *u128, model: i32) callconv(.C) u128 { | |
| 196 | return atomic_load_N(u128, src, model); | |
| 197 | } | |
| 198 | ||
| 195 | 199 | inline fn atomic_store_N(comptime T: type, dst: *T, value: T, model: i32) void { |
| 196 | 200 | _ = model; |
| 197 | 201 | if (@sizeOf(T) > largest_atomic_size) { |
| ... | ... | @@ -219,6 +223,10 @@ fn __atomic_store_8(dst: *u64, value: u64, model: i32) callconv(.C) void { |
| 219 | 223 | return atomic_store_N(u64, dst, value, model); |
| 220 | 224 | } |
| 221 | 225 | |
| 226 | fn __atomic_store_16(dst: *u128, value: u128, model: i32) callconv(.C) void { | |
| 227 | return atomic_store_N(u128, dst, value, model); | |
| 228 | } | |
| 229 | ||
| 222 | 230 | fn wideUpdate(comptime T: type, ptr: *T, val: T, update: anytype) T { |
| 223 | 231 | const WideAtomic = std.meta.Int(.unsigned, smallest_atomic_fetch_exch_size * 8); |
| 224 | 232 | |
| ... | ... | @@ -282,6 +290,10 @@ fn __atomic_exchange_8(ptr: *u64, val: u64, model: i32) callconv(.C) u64 { |
| 282 | 290 | return atomic_exchange_N(u64, ptr, val, model); |
| 283 | 291 | } |
| 284 | 292 | |
| 293 | fn __atomic_exchange_16(ptr: *u128, val: u128, model: i32) callconv(.C) u128 { | |
| 294 | return atomic_exchange_N(u128, ptr, val, model); | |
| 295 | } | |
| 296 | ||
| 285 | 297 | inline fn atomic_compare_exchange_N( |
| 286 | 298 | comptime T: type, |
| 287 | 299 | ptr: *T, |
| ... | ... | @@ -327,6 +339,10 @@ fn __atomic_compare_exchange_8(ptr: *u64, expected: *u64, desired: u64, success: |
| 327 | 339 | return atomic_compare_exchange_N(u64, ptr, expected, desired, success, failure); |
| 328 | 340 | } |
| 329 | 341 | |
| 342 | fn __atomic_compare_exchange_16(ptr: *u128, expected: *u128, desired: u128, success: i32, failure: i32) callconv(.C) i32 { | |
| 343 | return atomic_compare_exchange_N(u128, ptr, expected, desired, success, failure); | |
| 344 | } | |
| 345 | ||
| 330 | 346 | inline fn fetch_op_N(comptime T: type, comptime op: std.builtin.AtomicRmwOp, ptr: *T, val: T, model: i32) T { |
| 331 | 347 | _ = model; |
| 332 | 348 | const Updater = struct { |
| ... | ... | @@ -338,6 +354,8 @@ inline fn fetch_op_N(comptime T: type, comptime op: std.builtin.AtomicRmwOp, ptr |
| 338 | 354 | .Nand => ~(old & new), |
| 339 | 355 | .Or => old | new, |
| 340 | 356 | .Xor => old ^ new, |
| 357 | .Max => @max(old, new), | |
| 358 | .Min => @min(old, new), | |
| 341 | 359 | else => @compileError("unsupported atomic op"), |
| 342 | 360 | }; |
| 343 | 361 | } |
| ... | ... | @@ -374,6 +392,10 @@ fn __atomic_fetch_add_8(ptr: *u64, val: u64, model: i32) callconv(.C) u64 { |
| 374 | 392 | return fetch_op_N(u64, .Add, ptr, val, model); |
| 375 | 393 | } |
| 376 | 394 | |
| 395 | fn __atomic_fetch_add_16(ptr: *u128, val: u128, model: i32) callconv(.C) u128 { | |
| 396 | return fetch_op_N(u128, .Add, ptr, val, model); | |
| 397 | } | |
| 398 | ||
| 377 | 399 | fn __atomic_fetch_sub_1(ptr: *u8, val: u8, model: i32) callconv(.C) u8 { |
| 378 | 400 | return fetch_op_N(u8, .Sub, ptr, val, model); |
| 379 | 401 | } |
| ... | ... | @@ -390,6 +412,10 @@ fn __atomic_fetch_sub_8(ptr: *u64, val: u64, model: i32) callconv(.C) u64 { |
| 390 | 412 | return fetch_op_N(u64, .Sub, ptr, val, model); |
| 391 | 413 | } |
| 392 | 414 | |
| 415 | fn __atomic_fetch_sub_16(ptr: *u128, val: u128, model: i32) callconv(.C) u128 { | |
| 416 | return fetch_op_N(u128, .Sub, ptr, val, model); | |
| 417 | } | |
| 418 | ||
| 393 | 419 | fn __atomic_fetch_and_1(ptr: *u8, val: u8, model: i32) callconv(.C) u8 { |
| 394 | 420 | return fetch_op_N(u8, .And, ptr, val, model); |
| 395 | 421 | } |
| ... | ... | @@ -406,6 +432,10 @@ fn __atomic_fetch_and_8(ptr: *u64, val: u64, model: i32) callconv(.C) u64 { |
| 406 | 432 | return fetch_op_N(u64, .And, ptr, val, model); |
| 407 | 433 | } |
| 408 | 434 | |
| 435 | fn __atomic_fetch_and_16(ptr: *u128, val: u128, model: i32) callconv(.C) u128 { | |
| 436 | return fetch_op_N(u128, .And, ptr, val, model); | |
| 437 | } | |
| 438 | ||
| 409 | 439 | fn __atomic_fetch_or_1(ptr: *u8, val: u8, model: i32) callconv(.C) u8 { |
| 410 | 440 | return fetch_op_N(u8, .Or, ptr, val, model); |
| 411 | 441 | } |
| ... | ... | @@ -422,6 +452,10 @@ fn __atomic_fetch_or_8(ptr: *u64, val: u64, model: i32) callconv(.C) u64 { |
| 422 | 452 | return fetch_op_N(u64, .Or, ptr, val, model); |
| 423 | 453 | } |
| 424 | 454 | |
| 455 | fn __atomic_fetch_or_16(ptr: *u128, val: u128, model: i32) callconv(.C) u128 { | |
| 456 | return fetch_op_N(u128, .Or, ptr, val, model); | |
| 457 | } | |
| 458 | ||
| 425 | 459 | fn __atomic_fetch_xor_1(ptr: *u8, val: u8, model: i32) callconv(.C) u8 { |
| 426 | 460 | return fetch_op_N(u8, .Xor, ptr, val, model); |
| 427 | 461 | } |
| ... | ... | @@ -438,6 +472,10 @@ fn __atomic_fetch_xor_8(ptr: *u64, val: u64, model: i32) callconv(.C) u64 { |
| 438 | 472 | return fetch_op_N(u64, .Xor, ptr, val, model); |
| 439 | 473 | } |
| 440 | 474 | |
| 475 | fn __atomic_fetch_xor_16(ptr: *u128, val: u128, model: i32) callconv(.C) u128 { | |
| 476 | return fetch_op_N(u128, .Xor, ptr, val, model); | |
| 477 | } | |
| 478 | ||
| 441 | 479 | fn __atomic_fetch_nand_1(ptr: *u8, val: u8, model: i32) callconv(.C) u8 { |
| 442 | 480 | return fetch_op_N(u8, .Nand, ptr, val, model); |
| 443 | 481 | } |
| ... | ... | @@ -454,6 +492,50 @@ fn __atomic_fetch_nand_8(ptr: *u64, val: u64, model: i32) callconv(.C) u64 { |
| 454 | 492 | return fetch_op_N(u64, .Nand, ptr, val, model); |
| 455 | 493 | } |
| 456 | 494 | |
| 495 | fn __atomic_fetch_nand_16(ptr: *u128, val: u128, model: i32) callconv(.C) u128 { | |
| 496 | return fetch_op_N(u128, .Nand, ptr, val, model); | |
| 497 | } | |
| 498 | ||
| 499 | fn __atomic_fetch_umax_1(ptr: *u8, val: u8, model: i32) callconv(.C) u8 { | |
| 500 | return fetch_op_N(u8, .Max, ptr, val, model); | |
| 501 | } | |
| 502 | ||
| 503 | fn __atomic_fetch_umax_2(ptr: *u16, val: u16, model: i32) callconv(.C) u16 { | |
| 504 | return fetch_op_N(u16, .Max, ptr, val, model); | |
| 505 | } | |
| 506 | ||
| 507 | fn __atomic_fetch_umax_4(ptr: *u32, val: u32, model: i32) callconv(.C) u32 { | |
| 508 | return fetch_op_N(u32, .Max, ptr, val, model); | |
| 509 | } | |
| 510 | ||
| 511 | fn __atomic_fetch_umax_8(ptr: *u64, val: u64, model: i32) callconv(.C) u64 { | |
| 512 | return fetch_op_N(u64, .Max, ptr, val, model); | |
| 513 | } | |
| 514 | ||
| 515 | fn __atomic_fetch_umax_16(ptr: *u128, val: u128, model: i32) callconv(.C) u128 { | |
| 516 | return fetch_op_N(u128, .Max, ptr, val, model); | |
| 517 | } | |
| 518 | ||
| 519 | fn __atomic_fetch_umin_1(ptr: *u8, val: u8, model: i32) callconv(.C) u8 { | |
| 520 | return fetch_op_N(u8, .Min, ptr, val, model); | |
| 521 | } | |
| 522 | ||
| 523 | fn __atomic_fetch_umin_2(ptr: *u16, val: u16, model: i32) callconv(.C) u16 { | |
| 524 | return fetch_op_N(u16, .Min, ptr, val, model); | |
| 525 | } | |
| 526 | ||
| 527 | fn __atomic_fetch_umin_4(ptr: *u32, val: u32, model: i32) callconv(.C) u32 { | |
| 528 | return fetch_op_N(u32, .Min, ptr, val, model); | |
| 529 | } | |
| 530 | ||
| 531 | fn __atomic_fetch_umin_8(ptr: *u64, val: u64, model: i32) callconv(.C) u64 { | |
| 532 | return fetch_op_N(u64, .Min, ptr, val, model); | |
| 533 | } | |
| 534 | ||
| 535 | fn __atomic_fetch_umin_16(ptr: *u128, val: u128, model: i32) callconv(.C) u128 { | |
| 536 | return fetch_op_N(u128, .Min, ptr, val, model); | |
| 537 | } | |
| 538 | ||
| 457 | 539 | comptime { |
| 458 | 540 | if (supports_atomic_ops and builtin.object_format != .c) { |
| 459 | 541 | @export(__atomic_load, .{ .name = "__atomic_load", .linkage = linkage, .visibility = visibility }); |
| ... | ... | @@ -465,50 +547,72 @@ comptime { |
| 465 | 547 | @export(__atomic_fetch_add_2, .{ .name = "__atomic_fetch_add_2", .linkage = linkage, .visibility = visibility }); |
| 466 | 548 | @export(__atomic_fetch_add_4, .{ .name = "__atomic_fetch_add_4", .linkage = linkage, .visibility = visibility }); |
| 467 | 549 | @export(__atomic_fetch_add_8, .{ .name = "__atomic_fetch_add_8", .linkage = linkage, .visibility = visibility }); |
| 550 | @export(__atomic_fetch_add_16, .{ .name = "__atomic_fetch_add_16", .linkage = linkage, .visibility = visibility }); | |
| 468 | 551 | |
| 469 | 552 | @export(__atomic_fetch_sub_1, .{ .name = "__atomic_fetch_sub_1", .linkage = linkage, .visibility = visibility }); |
| 470 | 553 | @export(__atomic_fetch_sub_2, .{ .name = "__atomic_fetch_sub_2", .linkage = linkage, .visibility = visibility }); |
| 471 | 554 | @export(__atomic_fetch_sub_4, .{ .name = "__atomic_fetch_sub_4", .linkage = linkage, .visibility = visibility }); |
| 472 | 555 | @export(__atomic_fetch_sub_8, .{ .name = "__atomic_fetch_sub_8", .linkage = linkage, .visibility = visibility }); |
| 556 | @export(__atomic_fetch_sub_16, .{ .name = "__atomic_fetch_sub_16", .linkage = linkage, .visibility = visibility }); | |
| 473 | 557 | |
| 474 | 558 | @export(__atomic_fetch_and_1, .{ .name = "__atomic_fetch_and_1", .linkage = linkage, .visibility = visibility }); |
| 475 | 559 | @export(__atomic_fetch_and_2, .{ .name = "__atomic_fetch_and_2", .linkage = linkage, .visibility = visibility }); |
| 476 | 560 | @export(__atomic_fetch_and_4, .{ .name = "__atomic_fetch_and_4", .linkage = linkage, .visibility = visibility }); |
| 477 | 561 | @export(__atomic_fetch_and_8, .{ .name = "__atomic_fetch_and_8", .linkage = linkage, .visibility = visibility }); |
| 562 | @export(__atomic_fetch_and_16, .{ .name = "__atomic_fetch_and_16", .linkage = linkage, .visibility = visibility }); | |
| 478 | 563 | |
| 479 | 564 | @export(__atomic_fetch_or_1, .{ .name = "__atomic_fetch_or_1", .linkage = linkage, .visibility = visibility }); |
| 480 | 565 | @export(__atomic_fetch_or_2, .{ .name = "__atomic_fetch_or_2", .linkage = linkage, .visibility = visibility }); |
| 481 | 566 | @export(__atomic_fetch_or_4, .{ .name = "__atomic_fetch_or_4", .linkage = linkage, .visibility = visibility }); |
| 482 | 567 | @export(__atomic_fetch_or_8, .{ .name = "__atomic_fetch_or_8", .linkage = linkage, .visibility = visibility }); |
| 568 | @export(__atomic_fetch_or_16, .{ .name = "__atomic_fetch_or_16", .linkage = linkage, .visibility = visibility }); | |
| 483 | 569 | |
| 484 | 570 | @export(__atomic_fetch_xor_1, .{ .name = "__atomic_fetch_xor_1", .linkage = linkage, .visibility = visibility }); |
| 485 | 571 | @export(__atomic_fetch_xor_2, .{ .name = "__atomic_fetch_xor_2", .linkage = linkage, .visibility = visibility }); |
| 486 | 572 | @export(__atomic_fetch_xor_4, .{ .name = "__atomic_fetch_xor_4", .linkage = linkage, .visibility = visibility }); |
| 487 | 573 | @export(__atomic_fetch_xor_8, .{ .name = "__atomic_fetch_xor_8", .linkage = linkage, .visibility = visibility }); |
| 574 | @export(__atomic_fetch_xor_16, .{ .name = "__atomic_fetch_xor_16", .linkage = linkage, .visibility = visibility }); | |
| 488 | 575 | |
| 489 | 576 | @export(__atomic_fetch_nand_1, .{ .name = "__atomic_fetch_nand_1", .linkage = linkage, .visibility = visibility }); |
| 490 | 577 | @export(__atomic_fetch_nand_2, .{ .name = "__atomic_fetch_nand_2", .linkage = linkage, .visibility = visibility }); |
| 491 | 578 | @export(__atomic_fetch_nand_4, .{ .name = "__atomic_fetch_nand_4", .linkage = linkage, .visibility = visibility }); |
| 492 | 579 | @export(__atomic_fetch_nand_8, .{ .name = "__atomic_fetch_nand_8", .linkage = linkage, .visibility = visibility }); |
| 580 | @export(__atomic_fetch_nand_16, .{ .name = "__atomic_fetch_nand_16", .linkage = linkage, .visibility = visibility }); | |
| 581 | ||
| 582 | @export(__atomic_fetch_umax_1, .{ .name = "__atomic_fetch_umax_1", .linkage = linkage, .visibility = visibility }); | |
| 583 | @export(__atomic_fetch_umax_2, .{ .name = "__atomic_fetch_umax_2", .linkage = linkage, .visibility = visibility }); | |
| 584 | @export(__atomic_fetch_umax_4, .{ .name = "__atomic_fetch_umax_4", .linkage = linkage, .visibility = visibility }); | |
| 585 | @export(__atomic_fetch_umax_8, .{ .name = "__atomic_fetch_umax_8", .linkage = linkage, .visibility = visibility }); | |
| 586 | @export(__atomic_fetch_umax_16, .{ .name = "__atomic_fetch_umax_16", .linkage = linkage, .visibility = visibility }); | |
| 587 | ||
| 588 | @export(__atomic_fetch_umin_1, .{ .name = "__atomic_fetch_umin_1", .linkage = linkage, .visibility = visibility }); | |
| 589 | @export(__atomic_fetch_umin_2, .{ .name = "__atomic_fetch_umin_2", .linkage = linkage, .visibility = visibility }); | |
| 590 | @export(__atomic_fetch_umin_4, .{ .name = "__atomic_fetch_umin_4", .linkage = linkage, .visibility = visibility }); | |
| 591 | @export(__atomic_fetch_umin_8, .{ .name = "__atomic_fetch_umin_8", .linkage = linkage, .visibility = visibility }); | |
| 592 | @export(__atomic_fetch_umin_16, .{ .name = "__atomic_fetch_umin_16", .linkage = linkage, .visibility = visibility }); | |
| 493 | 593 | |
| 494 | 594 | @export(__atomic_load_1, .{ .name = "__atomic_load_1", .linkage = linkage, .visibility = visibility }); |
| 495 | 595 | @export(__atomic_load_2, .{ .name = "__atomic_load_2", .linkage = linkage, .visibility = visibility }); |
| 496 | 596 | @export(__atomic_load_4, .{ .name = "__atomic_load_4", .linkage = linkage, .visibility = visibility }); |
| 497 | 597 | @export(__atomic_load_8, .{ .name = "__atomic_load_8", .linkage = linkage, .visibility = visibility }); |
| 598 | @export(__atomic_load_16, .{ .name = "__atomic_load_16", .linkage = linkage, .visibility = visibility }); | |
| 498 | 599 | |
| 499 | 600 | @export(__atomic_store_1, .{ .name = "__atomic_store_1", .linkage = linkage, .visibility = visibility }); |
| 500 | 601 | @export(__atomic_store_2, .{ .name = "__atomic_store_2", .linkage = linkage, .visibility = visibility }); |
| 501 | 602 | @export(__atomic_store_4, .{ .name = "__atomic_store_4", .linkage = linkage, .visibility = visibility }); |
| 502 | 603 | @export(__atomic_store_8, .{ .name = "__atomic_store_8", .linkage = linkage, .visibility = visibility }); |
| 604 | @export(__atomic_store_16, .{ .name = "__atomic_store_16", .linkage = linkage, .visibility = visibility }); | |
| 503 | 605 | |
| 504 | 606 | @export(__atomic_exchange_1, .{ .name = "__atomic_exchange_1", .linkage = linkage, .visibility = visibility }); |
| 505 | 607 | @export(__atomic_exchange_2, .{ .name = "__atomic_exchange_2", .linkage = linkage, .visibility = visibility }); |
| 506 | 608 | @export(__atomic_exchange_4, .{ .name = "__atomic_exchange_4", .linkage = linkage, .visibility = visibility }); |
| 507 | 609 | @export(__atomic_exchange_8, .{ .name = "__atomic_exchange_8", .linkage = linkage, .visibility = visibility }); |
| 610 | @export(__atomic_exchange_16, .{ .name = "__atomic_exchange_16", .linkage = linkage, .visibility = visibility }); | |
| 508 | 611 | |
| 509 | 612 | @export(__atomic_compare_exchange_1, .{ .name = "__atomic_compare_exchange_1", .linkage = linkage, .visibility = visibility }); |
| 510 | 613 | @export(__atomic_compare_exchange_2, .{ .name = "__atomic_compare_exchange_2", .linkage = linkage, .visibility = visibility }); |
| 511 | 614 | @export(__atomic_compare_exchange_4, .{ .name = "__atomic_compare_exchange_4", .linkage = linkage, .visibility = visibility }); |
| 512 | 615 | @export(__atomic_compare_exchange_8, .{ .name = "__atomic_compare_exchange_8", .linkage = linkage, .visibility = visibility }); |
| 616 | @export(__atomic_compare_exchange_16, .{ .name = "__atomic_compare_exchange_16", .linkage = linkage, .visibility = visibility }); | |
| 513 | 617 | } |
| 514 | 618 | } |
lib/docs/main.js+4-1| ... | ... | @@ -1354,6 +1354,10 @@ const NAV_MODES = { |
| 1354 | 1354 | payloadHtml += "ptrCast"; |
| 1355 | 1355 | break; |
| 1356 | 1356 | } |
| 1357 | case "qual_cast": { | |
| 1358 | payloadHtml += "qualCast"; | |
| 1359 | break; | |
| 1360 | } | |
| 1357 | 1361 | case "truncate": { |
| 1358 | 1362 | payloadHtml += "truncate"; |
| 1359 | 1363 | break; |
| ... | ... | @@ -3158,7 +3162,6 @@ const NAV_MODES = { |
| 3158 | 3162 | canonTypeDecls = new Array(zigAnalysis.types.length); |
| 3159 | 3163 | |
| 3160 | 3164 | for (let pkgI = 0; pkgI < zigAnalysis.packages.length; pkgI += 1) { |
| 3161 | if (pkgI === zigAnalysis.rootPkg && rootIsStd) continue; | |
| 3162 | 3165 | let pkg = zigAnalysis.packages[pkgI]; |
| 3163 | 3166 | let pkgNames = canonPkgPaths[pkgI]; |
| 3164 | 3167 | if (pkgNames === undefined) continue; |
lib/init-exe/build.zig+43-10| ... | ... | @@ -1,34 +1,67 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | |
| 3 | pub fn build(b: *std.build.Builder) void { | |
| 3 | // Although this function looks imperative, note that its job is to | |
| 4 | // declaratively construct a build graph that will be executed by an external | |
| 5 | // runner. | |
| 6 | pub fn build(b: *std.Build) void { | |
| 4 | 7 | // Standard target options allows the person running `zig build` to choose |
| 5 | 8 | // what target to build for. Here we do not override the defaults, which |
| 6 | 9 | // means any target is allowed, and the default is native. Other options |
| 7 | 10 | // for restricting supported target set are available. |
| 8 | 11 | const target = b.standardTargetOptions(.{}); |
| 9 | 12 | |
| 10 | // Standard release options allow the person running `zig build` to select | |
| 11 | // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. | |
| 12 | const mode = b.standardReleaseOptions(); | |
| 13 | // Standard optimization options allow the person running `zig build` to select | |
| 14 | // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not | |
| 15 | // set a preferred release mode, allowing the user to decide how to optimize. | |
| 16 | const optimize = b.standardOptimizeOption(.{}); | |
| 13 | 17 | |
| 14 | const exe = b.addExecutable("$", "src/main.zig"); | |
| 15 | exe.setTarget(target); | |
| 16 | exe.setBuildMode(mode); | |
| 18 | const exe = b.addExecutable(.{ | |
| 19 | .name = "$", | |
| 20 | // In this case the main source file is merely a path, however, in more | |
| 21 | // complicated build scripts, this could be a generated file. | |
| 22 | .root_source_file = .{ .path = "src/main.zig" }, | |
| 23 | .target = target, | |
| 24 | .optimize = optimize, | |
| 25 | }); | |
| 26 | ||
| 27 | // This declares intent for the executable to be installed into the | |
| 28 | // standard location when the user invokes the "install" step (the default | |
| 29 | // step when running `zig build`). | |
| 17 | 30 | exe.install(); |
| 18 | 31 | |
| 32 | // This *creates* a RunStep in the build graph, to be executed when another | |
| 33 | // step is evaluated that depends on it. The next line below will establish | |
| 34 | // such a dependency. | |
| 19 | 35 | const run_cmd = exe.run(); |
| 36 | ||
| 37 | // By making the run step depend on the install step, it will be run from the | |
| 38 | // installation directory rather than directly from within the cache directory. | |
| 39 | // This is not necessary, however, if the application depends on other installed | |
| 40 | // files, this ensures they will be present and in the expected location. | |
| 20 | 41 | run_cmd.step.dependOn(b.getInstallStep()); |
| 42 | ||
| 43 | // This allows the user to pass arguments to the application in the build | |
| 44 | // command itself, like this: `zig build run -- arg1 arg2 etc` | |
| 21 | 45 | if (b.args) |args| { |
| 22 | 46 | run_cmd.addArgs(args); |
| 23 | 47 | } |
| 24 | 48 | |
| 49 | // This creates a build step. It will be visible in the `zig build --help` menu, | |
| 50 | // and can be selected like this: `zig build run` | |
| 51 | // This will evaluate the `run` step rather than the default, which is "install". | |
| 25 | 52 | const run_step = b.step("run", "Run the app"); |
| 26 | 53 | run_step.dependOn(&run_cmd.step); |
| 27 | 54 | |
| 28 | const exe_tests = b.addTest("src/main.zig"); | |
| 29 | exe_tests.setTarget(target); | |
| 30 | exe_tests.setBuildMode(mode); | |
| 55 | // Creates a step for unit testing. | |
| 56 | const exe_tests = b.addTest(.{ | |
| 57 | .root_source_file = .{ .path = "src/main.zig" }, | |
| 58 | .target = target, | |
| 59 | .optimize = optimize, | |
| 60 | }); | |
| 31 | 61 | |
| 62 | // Similar to creating the run step earlier, this exposes a `test` step to | |
| 63 | // the `zig build --help` menu, providing a way for the user to request | |
| 64 | // running the unit tests. | |
| 32 | 65 | const test_step = b.step("test", "Run unit tests"); |
| 33 | 66 | test_step.dependOn(&exe_tests.step); |
| 34 | 67 | } |
lib/init-lib/build.zig+35-8| ... | ... | @@ -1,17 +1,44 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | |
| 3 | pub fn build(b: *std.build.Builder) void { | |
| 4 | // Standard release options allow the person running `zig build` to select | |
| 5 | // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. | |
| 6 | const mode = b.standardReleaseOptions(); | |
| 3 | // Although this function looks imperative, note that its job is to | |
| 4 | // declaratively construct a build graph that will be executed by an external | |
| 5 | // runner. | |
| 6 | pub fn build(b: *std.Build) void { | |
| 7 | // Standard target options allows the person running `zig build` to choose | |
| 8 | // what target to build for. Here we do not override the defaults, which | |
| 9 | // means any target is allowed, and the default is native. Other options | |
| 10 | // for restricting supported target set are available. | |
| 11 | const target = b.standardTargetOptions(.{}); | |
| 7 | 12 | |
| 8 | const lib = b.addStaticLibrary("$", "src/main.zig"); | |
| 9 | lib.setBuildMode(mode); | |
| 13 | // Standard optimization options allow the person running `zig build` to select | |
| 14 | // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not | |
| 15 | // set a preferred release mode, allowing the user to decide how to optimize. | |
| 16 | const optimize = b.standardOptimizeOption(.{}); | |
| 17 | ||
| 18 | const lib = b.addStaticLibrary(.{ | |
| 19 | .name = "$", | |
| 20 | // In this case the main source file is merely a path, however, in more | |
| 21 | // complicated build scripts, this could be a generated file. | |
| 22 | .root_source_file = .{ .path = "src/main.zig" }, | |
| 23 | .target = target, | |
| 24 | .optimize = optimize, | |
| 25 | }); | |
| 26 | ||
| 27 | // This declares intent for the library to be installed into the standard | |
| 28 | // location when the user invokes the "install" step (the default step when | |
| 29 | // running `zig build`). | |
| 10 | 30 | lib.install(); |
| 11 | 31 | |
| 12 | const main_tests = b.addTest("src/main.zig"); | |
| 13 | main_tests.setBuildMode(mode); | |
| 32 | // Creates a step for unit testing. | |
| 33 | const main_tests = b.addTest(.{ | |
| 34 | .root_source_file = .{ .path = "src/main.zig" }, | |
| 35 | .target = target, | |
| 36 | .optimize = optimize, | |
| 37 | }); | |
| 14 | 38 | |
| 39 | // This creates a build step. It will be visible in the `zig build --help` menu, | |
| 40 | // and can be selected like this: `zig build test` | |
| 41 | // This will evaluate the `test` step rather than the default, which is "install". | |
| 15 | 42 | const test_step = b.step("test", "Run library tests"); |
| 16 | 43 | test_step.dependOn(&main_tests.step); |
| 17 | 44 | } |
lib/libc/mingw/misc/strtoimax.c+1-4| ... | ... | @@ -31,10 +31,7 @@ |
| 31 | 31 | #define valid(n, b)	((n) >= 0 && (n) < (b)) |
| 32 | 32 | |
| 33 | 33 | intmax_t |
| 34 | strtoimax(nptr, endptr, base) | |
| 35 | 	register const char * __restrict__	nptr; | |
| 36 | 	char ** __restrict__			endptr; | |
| 37 | 	register int				base; | |
| 34 | strtoimax(const char * __restrict__ nptr, char ** __restrict__ endptr, int base) | |
| 38 | 35 | 	{ |
| 39 | 36 | 	register uintmax_t	accum;	/* accumulates converted value */ |
| 40 | 37 | 	register int		n;	/* numeral from digit character */ |
lib/libc/mingw/misc/strtoumax.c+1-4| ... | ... | @@ -31,10 +31,7 @@ |
| 31 | 31 | #define valid(n, b)	((n) >= 0 && (n) < (b)) |
| 32 | 32 | |
| 33 | 33 | uintmax_t |
| 34 | strtoumax(nptr, endptr, base) | |
| 35 | 	register const char * __restrict__	nptr; | |
| 36 | 	char ** __restrict__			endptr; | |
| 37 | 	register int				base; | |
| 34 | strtoumax(const char * __restrict__ nptr, char ** __restrict__ endptr, int base) | |
| 38 | 35 | 	{ |
| 39 | 36 | 	register uintmax_t	accum;	/* accumulates converted value */ |
| 40 | 37 | 	register uintmax_t	next;	/* for computing next value of accum */ |
lib/libc/mingw/misc/wcstoimax.c+1-4| ... | ... | @@ -33,10 +33,7 @@ |
| 33 | 33 | #define valid(n, b)	((n) >= 0 && (n) < (b)) |
| 34 | 34 | |
| 35 | 35 | intmax_t |
| 36 | wcstoimax(nptr, endptr, base) | |
| 37 | 	register const wchar_t * __restrict__	nptr; | |
| 38 | 	wchar_t ** __restrict__				endptr; | |
| 39 | 	register int					base; | |
| 36 | wcstoimax(const wchar_t * __restrict__ nptr, wchar_t ** __restrict__ endptr, int base) | |
| 40 | 37 | 	{ |
| 41 | 38 | 	register uintmax_t	accum;	/* accumulates converted value */ |
| 42 | 39 | 	register int		n;	/* numeral from digit character */ |
lib/libc/mingw/misc/wcstoumax.c+1-4| ... | ... | @@ -33,10 +33,7 @@ |
| 33 | 33 | #define valid(n, b)	((n) >= 0 && (n) < (b)) |
| 34 | 34 | |
| 35 | 35 | uintmax_t |
| 36 | wcstoumax(nptr, endptr, base) | |
| 37 | 	register const wchar_t * __restrict__	nptr; | |
| 38 | 	wchar_t ** __restrict__				endptr; | |
| 39 | 	register int					base; | |
| 36 | wcstoumax(const wchar_t * __restrict__ nptr, wchar_t ** __restrict__ endptr, int base) | |
| 40 | 37 | 	{ |
| 41 | 38 | 	register uintmax_t	accum;	/* accumulates converted value */ |
| 42 | 39 | 	register uintmax_t	next;	/* for computing next value of accum */ |
lib/std/Build.zig created+1780| ... | ... | @@ -0,0 +1,1780 @@ |
| 1 | const std = @import("std.zig"); | |
| 2 | const builtin = @import("builtin"); | |
| 3 | const io = std.io; | |
| 4 | const fs = std.fs; | |
| 5 | const mem = std.mem; | |
| 6 | const debug = std.debug; | |
| 7 | const panic = std.debug.panic; | |
| 8 | const assert = debug.assert; | |
| 9 | const log = std.log; | |
| 10 | const ArrayList = std.ArrayList; | |
| 11 | const StringHashMap = std.StringHashMap; | |
| 12 | const Allocator = mem.Allocator; | |
| 13 | const process = std.process; | |
| 14 | const EnvMap = std.process.EnvMap; | |
| 15 | const fmt_lib = std.fmt; | |
| 16 | const File = std.fs.File; | |
| 17 | const CrossTarget = std.zig.CrossTarget; | |
| 18 | const NativeTargetInfo = std.zig.system.NativeTargetInfo; | |
| 19 | const Sha256 = std.crypto.hash.sha2.Sha256; | |
| 20 | const Build = @This(); | |
| 21 | ||
| 22 | /// deprecated: use `CompileStep`. | |
| 23 | pub const LibExeObjStep = CompileStep; | |
| 24 | /// deprecated: use `Build`. | |
| 25 | pub const Builder = Build; | |
| 26 | /// deprecated: use `InstallDirStep.Options` | |
| 27 | pub const InstallDirectoryOptions = InstallDirStep.Options; | |
| 28 | ||
| 29 | pub const Step = @import("Build/Step.zig"); | |
| 30 | pub const CheckFileStep = @import("Build/CheckFileStep.zig"); | |
| 31 | pub const CheckObjectStep = @import("Build/CheckObjectStep.zig"); | |
| 32 | pub const ConfigHeaderStep = @import("Build/ConfigHeaderStep.zig"); | |
| 33 | pub const EmulatableRunStep = @import("Build/EmulatableRunStep.zig"); | |
| 34 | pub const FmtStep = @import("Build/FmtStep.zig"); | |
| 35 | pub const InstallArtifactStep = @import("Build/InstallArtifactStep.zig"); | |
| 36 | pub const InstallDirStep = @import("Build/InstallDirStep.zig"); | |
| 37 | pub const InstallFileStep = @import("Build/InstallFileStep.zig"); | |
| 38 | pub const InstallRawStep = @import("Build/InstallRawStep.zig"); | |
| 39 | pub const CompileStep = @import("Build/CompileStep.zig"); | |
| 40 | pub const LogStep = @import("Build/LogStep.zig"); | |
| 41 | pub const OptionsStep = @import("Build/OptionsStep.zig"); | |
| 42 | pub const RemoveDirStep = @import("Build/RemoveDirStep.zig"); | |
| 43 | pub const RunStep = @import("Build/RunStep.zig"); | |
| 44 | pub const TranslateCStep = @import("Build/TranslateCStep.zig"); | |
| 45 | pub const WriteFileStep = @import("Build/WriteFileStep.zig"); | |
| 46 | ||
| 47 | install_tls: TopLevelStep, | |
| 48 | uninstall_tls: TopLevelStep, | |
| 49 | allocator: Allocator, | |
| 50 | user_input_options: UserInputOptionsMap, | |
| 51 | available_options_map: AvailableOptionsMap, | |
| 52 | available_options_list: ArrayList(AvailableOption), | |
| 53 | verbose: bool, | |
| 54 | verbose_link: bool, | |
| 55 | verbose_cc: bool, | |
| 56 | verbose_air: bool, | |
| 57 | verbose_llvm_ir: bool, | |
| 58 | verbose_cimport: bool, | |
| 59 | verbose_llvm_cpu_features: bool, | |
| 60 | /// The purpose of executing the command is for a human to read compile errors from the terminal | |
| 61 | prominent_compile_errors: bool, | |
| 62 | color: enum { auto, on, off } = .auto, | |
| 63 | reference_trace: ?u32 = null, | |
| 64 | invalid_user_input: bool, | |
| 65 | zig_exe: []const u8, | |
| 66 | default_step: *Step, | |
| 67 | env_map: *EnvMap, | |
| 68 | top_level_steps: ArrayList(*TopLevelStep), | |
| 69 | install_prefix: []const u8, | |
| 70 | dest_dir: ?[]const u8, | |
| 71 | lib_dir: []const u8, | |
| 72 | exe_dir: []const u8, | |
| 73 | h_dir: []const u8, | |
| 74 | install_path: []const u8, | |
| 75 | sysroot: ?[]const u8 = null, | |
| 76 | search_prefixes: ArrayList([]const u8), | |
| 77 | libc_file: ?[]const u8 = null, | |
| 78 | installed_files: ArrayList(InstalledFile), | |
| 79 | /// Path to the directory containing build.zig. | |
| 80 | build_root: []const u8, | |
| 81 | cache_root: []const u8, | |
| 82 | global_cache_root: []const u8, | |
| 83 | /// zig lib dir | |
| 84 | override_lib_dir: ?[]const u8, | |
| 85 | vcpkg_root: VcpkgRoot = .unattempted, | |
| 86 | pkg_config_pkg_list: ?(PkgConfigError![]const PkgConfigPkg) = null, | |
| 87 | args: ?[][]const u8 = null, | |
| 88 | debug_log_scopes: []const []const u8 = &.{}, | |
| 89 | debug_compile_errors: bool = false, | |
| 90 | ||
| 91 | /// Experimental. Use system Darling installation to run cross compiled macOS build artifacts. | |
| 92 | enable_darling: bool = false, | |
| 93 | /// Use system QEMU installation to run cross compiled foreign architecture build artifacts. | |
| 94 | enable_qemu: bool = false, | |
| 95 | /// Darwin. Use Rosetta to run x86_64 macOS build artifacts on arm64 macOS. | |
| 96 | enable_rosetta: bool = false, | |
| 97 | /// Use system Wasmtime installation to run cross compiled wasm/wasi build artifacts. | |
| 98 | enable_wasmtime: bool = false, | |
| 99 | /// Use system Wine installation to run cross compiled Windows build artifacts. | |
| 100 | enable_wine: bool = false, | |
| 101 | /// After following the steps in https://github.com/ziglang/zig/wiki/Updating-libc#glibc, | |
| 102 | /// this will be the directory $glibc-build-dir/install/glibcs | |
| 103 | /// Given the example of the aarch64 target, this is the directory | |
| 104 | /// that contains the path `aarch64-linux-gnu/lib/ld-linux-aarch64.so.1`. | |
| 105 | glibc_runtimes_dir: ?[]const u8 = null, | |
| 106 | ||
| 107 | /// Information about the native target. Computed before build() is invoked. | |
| 108 | host: NativeTargetInfo, | |
| 109 | ||
| 110 | dep_prefix: []const u8 = "", | |
| 111 | ||
| 112 | pub const ExecError = error{ | |
| 113 | ReadFailure, | |
| 114 | ExitCodeFailure, | |
| 115 | ProcessTerminated, | |
| 116 | ExecNotSupported, | |
| 117 | } || std.ChildProcess.SpawnError; | |
| 118 | ||
| 119 | pub const PkgConfigError = error{ | |
| 120 | PkgConfigCrashed, | |
| 121 | PkgConfigFailed, | |
| 122 | PkgConfigNotInstalled, | |
| 123 | PkgConfigInvalidOutput, | |
| 124 | }; | |
| 125 | ||
| 126 | pub const PkgConfigPkg = struct { | |
| 127 | name: []const u8, | |
| 128 | desc: []const u8, | |
| 129 | }; | |
| 130 | ||
| 131 | pub const CStd = enum { | |
| 132 | C89, | |
| 133 | C99, | |
| 134 | C11, | |
| 135 | }; | |
| 136 | ||
| 137 | const UserInputOptionsMap = StringHashMap(UserInputOption); | |
| 138 | const AvailableOptionsMap = StringHashMap(AvailableOption); | |
| 139 | ||
| 140 | const AvailableOption = struct { | |
| 141 | name: []const u8, | |
| 142 | type_id: TypeId, | |
| 143 | description: []const u8, | |
| 144 | /// If the `type_id` is `enum` this provides the list of enum options | |
| 145 | enum_options: ?[]const []const u8, | |
| 146 | }; | |
| 147 | ||
| 148 | const UserInputOption = struct { | |
| 149 | name: []const u8, | |
| 150 | value: UserValue, | |
| 151 | used: bool, | |
| 152 | }; | |
| 153 | ||
| 154 | const UserValue = union(enum) { | |
| 155 | flag: void, | |
| 156 | scalar: []const u8, | |
| 157 | list: ArrayList([]const u8), | |
| 158 | map: StringHashMap(*const UserValue), | |
| 159 | }; | |
| 160 | ||
| 161 | const TypeId = enum { | |
| 162 | bool, | |
| 163 | int, | |
| 164 | float, | |
| 165 | @"enum", | |
| 166 | string, | |
| 167 | list, | |
| 168 | }; | |
| 169 | ||
| 170 | const TopLevelStep = struct { | |
| 171 | pub const base_id = .top_level; | |
| 172 | ||
| 173 | step: Step, | |
| 174 | description: []const u8, | |
| 175 | }; | |
| 176 | ||
| 177 | pub const DirList = struct { | |
| 178 | lib_dir: ?[]const u8 = null, | |
| 179 | exe_dir: ?[]const u8 = null, | |
| 180 | include_dir: ?[]const u8 = null, | |
| 181 | }; | |
| 182 | ||
| 183 | pub fn create( | |
| 184 | allocator: Allocator, | |
| 185 | zig_exe: []const u8, | |
| 186 | build_root: []const u8, | |
| 187 | cache_root: []const u8, | |
| 188 | global_cache_root: []const u8, | |
| 189 | host: NativeTargetInfo, | |
| 190 | ) !*Build { | |
| 191 | const env_map = try allocator.create(EnvMap); | |
| 192 | env_map.* = try process.getEnvMap(allocator); | |
| 193 | ||
| 194 | const self = try allocator.create(Build); | |
| 195 | self.* = Build{ | |
| 196 | .zig_exe = zig_exe, | |
| 197 | .build_root = build_root, | |
| 198 | .cache_root = try fs.path.relative(allocator, build_root, cache_root), | |
| 199 | .global_cache_root = global_cache_root, | |
| 200 | .verbose = false, | |
| 201 | .verbose_link = false, | |
| 202 | .verbose_cc = false, | |
| 203 | .verbose_air = false, | |
| 204 | .verbose_llvm_ir = false, | |
| 205 | .verbose_cimport = false, | |
| 206 | .verbose_llvm_cpu_features = false, | |
| 207 | .prominent_compile_errors = false, | |
| 208 | .invalid_user_input = false, | |
| 209 | .allocator = allocator, | |
| 210 | .user_input_options = UserInputOptionsMap.init(allocator), | |
| 211 | .available_options_map = AvailableOptionsMap.init(allocator), | |
| 212 | .available_options_list = ArrayList(AvailableOption).init(allocator), | |
| 213 | .top_level_steps = ArrayList(*TopLevelStep).init(allocator), | |
| 214 | .default_step = undefined, | |
| 215 | .env_map = env_map, | |
| 216 | .search_prefixes = ArrayList([]const u8).init(allocator), | |
| 217 | .install_prefix = undefined, | |
| 218 | .lib_dir = undefined, | |
| 219 | .exe_dir = undefined, | |
| 220 | .h_dir = undefined, | |
| 221 | .dest_dir = env_map.get("DESTDIR"), | |
| 222 | .installed_files = ArrayList(InstalledFile).init(allocator), | |
| 223 | .install_tls = TopLevelStep{ | |
| 224 | .step = Step.initNoOp(.top_level, "install", allocator), | |
| 225 | .description = "Copy build artifacts to prefix path", | |
| 226 | }, | |
| 227 | .uninstall_tls = TopLevelStep{ | |
| 228 | .step = Step.init(.top_level, "uninstall", allocator, makeUninstall), | |
| 229 | .description = "Remove build artifacts from prefix path", | |
| 230 | }, | |
| 231 | .override_lib_dir = null, | |
| 232 | .install_path = undefined, | |
| 233 | .args = null, | |
| 234 | .host = host, | |
| 235 | }; | |
| 236 | try self.top_level_steps.append(&self.install_tls); | |
| 237 | try self.top_level_steps.append(&self.uninstall_tls); | |
| 238 | self.default_step = &self.install_tls.step; | |
| 239 | return self; | |
| 240 | } | |
| 241 | ||
| 242 | fn createChild( | |
| 243 | parent: *Build, | |
| 244 | dep_name: []const u8, | |
| 245 | build_root: []const u8, | |
| 246 | args: anytype, | |
| 247 | ) !*Build { | |
| 248 | const child = try createChildOnly(parent, dep_name, build_root); | |
| 249 | try applyArgs(child, args); | |
| 250 | return child; | |
| 251 | } | |
| 252 | ||
| 253 | fn createChildOnly(parent: *Build, dep_name: []const u8, build_root: []const u8) !*Build { | |
| 254 | const allocator = parent.allocator; | |
| 255 | const child = try allocator.create(Build); | |
| 256 | child.* = .{ | |
| 257 | .allocator = allocator, | |
| 258 | .install_tls = .{ | |
| 259 | .step = Step.initNoOp(.top_level, "install", allocator), | |
| 260 | .description = "Copy build artifacts to prefix path", | |
| 261 | }, | |
| 262 | .uninstall_tls = .{ | |
| 263 | .step = Step.init(.top_level, "uninstall", allocator, makeUninstall), | |
| 264 | .description = "Remove build artifacts from prefix path", | |
| 265 | }, | |
| 266 | .user_input_options = UserInputOptionsMap.init(allocator), | |
| 267 | .available_options_map = AvailableOptionsMap.init(allocator), | |
| 268 | .available_options_list = ArrayList(AvailableOption).init(allocator), | |
| 269 | .verbose = parent.verbose, | |
| 270 | .verbose_link = parent.verbose_link, | |
| 271 | .verbose_cc = parent.verbose_cc, | |
| 272 | .verbose_air = parent.verbose_air, | |
| 273 | .verbose_llvm_ir = parent.verbose_llvm_ir, | |
| 274 | .verbose_cimport = parent.verbose_cimport, | |
| 275 | .verbose_llvm_cpu_features = parent.verbose_llvm_cpu_features, | |
| 276 | .prominent_compile_errors = parent.prominent_compile_errors, | |
| 277 | .color = parent.color, | |
| 278 | .reference_trace = parent.reference_trace, | |
| 279 | .invalid_user_input = false, | |
| 280 | .zig_exe = parent.zig_exe, | |
| 281 | .default_step = undefined, | |
| 282 | .env_map = parent.env_map, | |
| 283 | .top_level_steps = ArrayList(*TopLevelStep).init(allocator), | |
| 284 | .install_prefix = undefined, | |
| 285 | .dest_dir = parent.dest_dir, | |
| 286 | .lib_dir = parent.lib_dir, | |
| 287 | .exe_dir = parent.exe_dir, | |
| 288 | .h_dir = parent.h_dir, | |
| 289 | .install_path = parent.install_path, | |
| 290 | .sysroot = parent.sysroot, | |
| 291 | .search_prefixes = ArrayList([]const u8).init(allocator), | |
| 292 | .libc_file = parent.libc_file, | |
| 293 | .installed_files = ArrayList(InstalledFile).init(allocator), | |
| 294 | .build_root = build_root, | |
| 295 | .cache_root = parent.cache_root, | |
| 296 | .global_cache_root = parent.global_cache_root, | |
| 297 | .override_lib_dir = parent.override_lib_dir, | |
| 298 | .debug_log_scopes = parent.debug_log_scopes, | |
| 299 | .debug_compile_errors = parent.debug_compile_errors, | |
| 300 | .enable_darling = parent.enable_darling, | |
| 301 | .enable_qemu = parent.enable_qemu, | |
| 302 | .enable_rosetta = parent.enable_rosetta, | |
| 303 | .enable_wasmtime = parent.enable_wasmtime, | |
| 304 | .enable_wine = parent.enable_wine, | |
| 305 | .glibc_runtimes_dir = parent.glibc_runtimes_dir, | |
| 306 | .host = parent.host, | |
| 307 | .dep_prefix = parent.fmt("{s}{s}.", .{ parent.dep_prefix, dep_name }), | |
| 308 | }; | |
| 309 | try child.top_level_steps.append(&child.install_tls); | |
| 310 | try child.top_level_steps.append(&child.uninstall_tls); | |
| 311 | child.default_step = &child.install_tls.step; | |
| 312 | return child; | |
| 313 | } | |
| 314 | ||
| 315 | fn applyArgs(b: *Build, args: anytype) !void { | |
| 316 | inline for (@typeInfo(@TypeOf(args)).Struct.fields) |field| { | |
| 317 | const v = @field(args, field.name); | |
| 318 | const T = @TypeOf(v); | |
| 319 | switch (T) { | |
| 320 | CrossTarget => { | |
| 321 | try b.user_input_options.put(field.name, .{ | |
| 322 | .name = field.name, | |
| 323 | .value = .{ .scalar = try v.zigTriple(b.allocator) }, | |
| 324 | .used = false, | |
| 325 | }); | |
| 326 | try b.user_input_options.put("cpu", .{ | |
| 327 | .name = "cpu", | |
| 328 | .value = .{ .scalar = try serializeCpu(b.allocator, v.getCpu()) }, | |
| 329 | .used = false, | |
| 330 | }); | |
| 331 | }, | |
| 332 | []const u8 => { | |
| 333 | try b.user_input_options.put(field.name, .{ | |
| 334 | .name = field.name, | |
| 335 | .value = .{ .scalar = v }, | |
| 336 | .used = false, | |
| 337 | }); | |
| 338 | }, | |
| 339 | else => switch (@typeInfo(T)) { | |
| 340 | .Bool => { | |
| 341 | try b.user_input_options.put(field.name, .{ | |
| 342 | .name = field.name, | |
| 343 | .value = .{ .scalar = if (v) "true" else "false" }, | |
| 344 | .used = false, | |
| 345 | }); | |
| 346 | }, | |
| 347 | .Enum => { | |
| 348 | try b.user_input_options.put(field.name, .{ | |
| 349 | .name = field.name, | |
| 350 | .value = .{ .scalar = @tagName(v) }, | |
| 351 | .used = false, | |
| 352 | }); | |
| 353 | }, | |
| 354 | .Int => { | |
| 355 | try b.user_input_options.put(field.name, .{ | |
| 356 | .name = field.name, | |
| 357 | .value = .{ .scalar = try std.fmt.allocPrint(b.allocator, "{d}", .{v}) }, | |
| 358 | .used = false, | |
| 359 | }); | |
| 360 | }, | |
| 361 | else => @compileError("option '" ++ field.name ++ "' has unsupported type: " ++ @typeName(T)), | |
| 362 | }, | |
| 363 | } | |
| 364 | } | |
| 365 | const Hasher = std.crypto.auth.siphash.SipHash128(1, 3); | |
| 366 | // Random bytes to make unique. Refresh this with new random bytes when | |
| 367 | // implementation is modified in a non-backwards-compatible way. | |
| 368 | var hash = Hasher.init("ZaEsvQ5ClaA2IdH9"); | |
| 369 | hash.update(b.dep_prefix); | |
| 370 | // TODO additionally update the hash with `args`. | |
| 371 | ||
| 372 | var digest: [16]u8 = undefined; | |
| 373 | hash.final(&digest); | |
| 374 | var hash_basename: [digest.len * 2]u8 = undefined; | |
| 375 | _ = std.fmt.bufPrint(&hash_basename, "{s}", .{std.fmt.fmtSliceHexLower(&digest)}) catch | |
| 376 | unreachable; | |
| 377 | ||
| 378 | const install_prefix = b.pathJoin(&.{ b.cache_root, "i", &hash_basename }); | |
| 379 | b.resolveInstallPrefix(install_prefix, .{}); | |
| 380 | } | |
| 381 | ||
| 382 | pub fn destroy(self: *Build) void { | |
| 383 | self.env_map.deinit(); | |
| 384 | self.top_level_steps.deinit(); | |
| 385 | self.allocator.destroy(self); | |
| 386 | } | |
| 387 | ||
| 388 | /// This function is intended to be called by lib/build_runner.zig, not a build.zig file. | |
| 389 | pub fn resolveInstallPrefix(self: *Build, install_prefix: ?[]const u8, dir_list: DirList) void { | |
| 390 | if (self.dest_dir) |dest_dir| { | |
| 391 | self.install_prefix = install_prefix orelse "/usr"; | |
| 392 | self.install_path = self.pathJoin(&.{ dest_dir, self.install_prefix }); | |
| 393 | } else { | |
| 394 | self.install_prefix = install_prefix orelse | |
| 395 | (self.pathJoin(&.{ self.build_root, "zig-out" })); | |
| 396 | self.install_path = self.install_prefix; | |
| 397 | } | |
| 398 | ||
| 399 | var lib_list = [_][]const u8{ self.install_path, "lib" }; | |
| 400 | var exe_list = [_][]const u8{ self.install_path, "bin" }; | |
| 401 | var h_list = [_][]const u8{ self.install_path, "include" }; | |
| 402 | ||
| 403 | if (dir_list.lib_dir) |dir| { | |
| 404 | if (std.fs.path.isAbsolute(dir)) lib_list[0] = self.dest_dir orelse ""; | |
| 405 | lib_list[1] = dir; | |
| 406 | } | |
| 407 | ||
| 408 | if (dir_list.exe_dir) |dir| { | |
| 409 | if (std.fs.path.isAbsolute(dir)) exe_list[0] = self.dest_dir orelse ""; | |
| 410 | exe_list[1] = dir; | |
| 411 | } | |
| 412 | ||
| 413 | if (dir_list.include_dir) |dir| { | |
| 414 | if (std.fs.path.isAbsolute(dir)) h_list[0] = self.dest_dir orelse ""; | |
| 415 | h_list[1] = dir; | |
| 416 | } | |
| 417 | ||
| 418 | self.lib_dir = self.pathJoin(&lib_list); | |
| 419 | self.exe_dir = self.pathJoin(&exe_list); | |
| 420 | self.h_dir = self.pathJoin(&h_list); | |
| 421 | } | |
| 422 | ||
| 423 | pub fn addOptions(self: *Build) *OptionsStep { | |
| 424 | return OptionsStep.create(self); | |
| 425 | } | |
| 426 | ||
| 427 | pub const ExecutableOptions = struct { | |
| 428 | name: []const u8, | |
| 429 | root_source_file: ?FileSource = null, | |
| 430 | version: ?std.builtin.Version = null, | |
| 431 | target: CrossTarget = .{}, | |
| 432 | optimize: std.builtin.Mode = .Debug, | |
| 433 | linkage: ?CompileStep.Linkage = null, | |
| 434 | }; | |
| 435 | ||
| 436 | pub fn addExecutable(b: *Build, options: ExecutableOptions) *CompileStep { | |
| 437 | return CompileStep.create(b, .{ | |
| 438 | .name = options.name, | |
| 439 | .root_source_file = options.root_source_file, | |
| 440 | .version = options.version, | |
| 441 | .target = options.target, | |
| 442 | .optimize = options.optimize, | |
| 443 | .kind = .exe, | |
| 444 | .linkage = options.linkage, | |
| 445 | }); | |
| 446 | } | |
| 447 | ||
| 448 | pub const ObjectOptions = struct { | |
| 449 | name: []const u8, | |
| 450 | root_source_file: ?FileSource = null, | |
| 451 | target: CrossTarget, | |
| 452 | optimize: std.builtin.Mode, | |
| 453 | }; | |
| 454 | ||
| 455 | pub fn addObject(b: *Build, options: ObjectOptions) *CompileStep { | |
| 456 | return CompileStep.create(b, .{ | |
| 457 | .name = options.name, | |
| 458 | .root_source_file = options.root_source_file, | |
| 459 | .target = options.target, | |
| 460 | .optimize = options.optimize, | |
| 461 | .kind = .obj, | |
| 462 | }); | |
| 463 | } | |
| 464 | ||
| 465 | pub const SharedLibraryOptions = struct { | |
| 466 | name: []const u8, | |
| 467 | root_source_file: ?FileSource = null, | |
| 468 | version: ?std.builtin.Version = null, | |
| 469 | target: CrossTarget, | |
| 470 | optimize: std.builtin.Mode, | |
| 471 | }; | |
| 472 | ||
| 473 | pub fn addSharedLibrary(b: *Build, options: SharedLibraryOptions) *CompileStep { | |
| 474 | return CompileStep.create(b, .{ | |
| 475 | .name = options.name, | |
| 476 | .root_source_file = options.root_source_file, | |
| 477 | .kind = .lib, | |
| 478 | .linkage = .dynamic, | |
| 479 | .version = options.version, | |
| 480 | .target = options.target, | |
| 481 | .optimize = options.optimize, | |
| 482 | }); | |
| 483 | } | |
| 484 | ||
| 485 | pub const StaticLibraryOptions = struct { | |
| 486 | name: []const u8, | |
| 487 | root_source_file: ?FileSource = null, | |
| 488 | target: CrossTarget, | |
| 489 | optimize: std.builtin.Mode, | |
| 490 | version: ?std.builtin.Version = null, | |
| 491 | }; | |
| 492 | ||
| 493 | pub fn addStaticLibrary(b: *Build, options: StaticLibraryOptions) *CompileStep { | |
| 494 | return CompileStep.create(b, .{ | |
| 495 | .name = options.name, | |
| 496 | .root_source_file = options.root_source_file, | |
| 497 | .kind = .lib, | |
| 498 | .linkage = .static, | |
| 499 | .version = options.version, | |
| 500 | .target = options.target, | |
| 501 | .optimize = options.optimize, | |
| 502 | }); | |
| 503 | } | |
| 504 | ||
| 505 | pub const TestOptions = struct { | |
| 506 | name: []const u8 = "test", | |
| 507 | kind: CompileStep.Kind = .@"test", | |
| 508 | root_source_file: FileSource, | |
| 509 | target: CrossTarget = .{}, | |
| 510 | optimize: std.builtin.Mode = .Debug, | |
| 511 | version: ?std.builtin.Version = null, | |
| 512 | }; | |
| 513 | ||
| 514 | pub fn addTest(b: *Build, options: TestOptions) *CompileStep { | |
| 515 | return CompileStep.create(b, .{ | |
| 516 | .name = options.name, | |
| 517 | .kind = options.kind, | |
| 518 | .root_source_file = options.root_source_file, | |
| 519 | .target = options.target, | |
| 520 | .optimize = options.optimize, | |
| 521 | }); | |
| 522 | } | |
| 523 | ||
| 524 | pub const AssemblyOptions = struct { | |
| 525 | name: []const u8, | |
| 526 | source_file: FileSource, | |
| 527 | target: CrossTarget, | |
| 528 | optimize: std.builtin.Mode, | |
| 529 | }; | |
| 530 | ||
| 531 | pub fn addAssembly(b: *Build, options: AssemblyOptions) *CompileStep { | |
| 532 | const obj_step = CompileStep.create(b, .{ | |
| 533 | .name = options.name, | |
| 534 | .root_source_file = null, | |
| 535 | .target = options.target, | |
| 536 | .optimize = options.optimize, | |
| 537 | }); | |
| 538 | obj_step.addAssemblyFileSource(options.source_file.dupe(b)); | |
| 539 | return obj_step; | |
| 540 | } | |
| 541 | ||
| 542 | /// Initializes a RunStep with argv, which must at least have the path to the | |
| 543 | /// executable. More command line arguments can be added with `addArg`, | |
| 544 | /// `addArgs`, and `addArtifactArg`. | |
| 545 | /// Be careful using this function, as it introduces a system dependency. | |
| 546 | /// To run an executable built with zig build, see `CompileStep.run`. | |
| 547 | pub fn addSystemCommand(self: *Build, argv: []const []const u8) *RunStep { | |
| 548 | assert(argv.len >= 1); | |
| 549 | const run_step = RunStep.create(self, self.fmt("run {s}", .{argv[0]})); | |
| 550 | run_step.addArgs(argv); | |
| 551 | return run_step; | |
| 552 | } | |
| 553 | ||
| 554 | pub fn addConfigHeader( | |
| 555 | b: *Build, | |
| 556 | source: FileSource, | |
| 557 | style: ConfigHeaderStep.Style, | |
| 558 | values: anytype, | |
| 559 | ) *ConfigHeaderStep { | |
| 560 | const config_header_step = ConfigHeaderStep.create(b, source, style); | |
| 561 | config_header_step.addValues(values); | |
| 562 | return config_header_step; | |
| 563 | } | |
| 564 | ||
| 565 | /// Allocator.dupe without the need to handle out of memory. | |
| 566 | pub fn dupe(self: *Build, bytes: []const u8) []u8 { | |
| 567 | return self.allocator.dupe(u8, bytes) catch @panic("OOM"); | |
| 568 | } | |
| 569 | ||
| 570 | /// Duplicates an array of strings without the need to handle out of memory. | |
| 571 | pub fn dupeStrings(self: *Build, strings: []const []const u8) [][]u8 { | |
| 572 | const array = self.allocator.alloc([]u8, strings.len) catch @panic("OOM"); | |
| 573 | for (strings) |s, i| { | |
| 574 | array[i] = self.dupe(s); | |
| 575 | } | |
| 576 | return array; | |
| 577 | } | |
| 578 | ||
| 579 | /// Duplicates a path and converts all slashes to the OS's canonical path separator. | |
| 580 | pub fn dupePath(self: *Build, bytes: []const u8) []u8 { | |
| 581 | const the_copy = self.dupe(bytes); | |
| 582 | for (the_copy) |*byte| { | |
| 583 | switch (byte.*) { | |
| 584 | '/', '\\' => byte.* = fs.path.sep, | |
| 585 | else => {}, | |
| 586 | } | |
| 587 | } | |
| 588 | return the_copy; | |
| 589 | } | |
| 590 | ||
| 591 | /// Duplicates a package recursively. | |
| 592 | pub fn dupePkg(self: *Build, package: Pkg) Pkg { | |
| 593 | var the_copy = Pkg{ | |
| 594 | .name = self.dupe(package.name), | |
| 595 | .source = package.source.dupe(self), | |
| 596 | }; | |
| 597 | ||
| 598 | if (package.dependencies) |dependencies| { | |
| 599 | const new_dependencies = self.allocator.alloc(Pkg, dependencies.len) catch @panic("OOM"); | |
| 600 | the_copy.dependencies = new_dependencies; | |
| 601 | ||
| 602 | for (dependencies) |dep_package, i| { | |
| 603 | new_dependencies[i] = self.dupePkg(dep_package); | |
| 604 | } | |
| 605 | } | |
| 606 | return the_copy; | |
| 607 | } | |
| 608 | ||
| 609 | pub fn addWriteFile(self: *Build, file_path: []const u8, data: []const u8) *WriteFileStep { | |
| 610 | const write_file_step = self.addWriteFiles(); | |
| 611 | write_file_step.add(file_path, data); | |
| 612 | return write_file_step; | |
| 613 | } | |
| 614 | ||
| 615 | pub fn addWriteFiles(self: *Build) *WriteFileStep { | |
| 616 | const write_file_step = self.allocator.create(WriteFileStep) catch @panic("OOM"); | |
| 617 | write_file_step.* = WriteFileStep.init(self); | |
| 618 | return write_file_step; | |
| 619 | } | |
| 620 | ||
| 621 | pub fn addLog(self: *Build, comptime format: []const u8, args: anytype) *LogStep { | |
| 622 | const data = self.fmt(format, args); | |
| 623 | const log_step = self.allocator.create(LogStep) catch @panic("OOM"); | |
| 624 | log_step.* = LogStep.init(self, data); | |
| 625 | return log_step; | |
| 626 | } | |
| 627 | ||
| 628 | pub fn addRemoveDirTree(self: *Build, dir_path: []const u8) *RemoveDirStep { | |
| 629 | const remove_dir_step = self.allocator.create(RemoveDirStep) catch @panic("OOM"); | |
| 630 | remove_dir_step.* = RemoveDirStep.init(self, dir_path); | |
| 631 | return remove_dir_step; | |
| 632 | } | |
| 633 | ||
| 634 | pub fn addFmt(self: *Build, paths: []const []const u8) *FmtStep { | |
| 635 | return FmtStep.create(self, paths); | |
| 636 | } | |
| 637 | ||
| 638 | pub fn addTranslateC(self: *Build, options: TranslateCStep.Options) *TranslateCStep { | |
| 639 | return TranslateCStep.create(self, options); | |
| 640 | } | |
| 641 | ||
| 642 | pub fn make(self: *Build, step_names: []const []const u8) !void { | |
| 643 | try self.makePath(self.cache_root); | |
| 644 | ||
| 645 | var wanted_steps = ArrayList(*Step).init(self.allocator); | |
| 646 | defer wanted_steps.deinit(); | |
| 647 | ||
| 648 | if (step_names.len == 0) { | |
| 649 | try wanted_steps.append(self.default_step); | |
| 650 | } else { | |
| 651 | for (step_names) |step_name| { | |
| 652 | const s = try self.getTopLevelStepByName(step_name); | |
| 653 | try wanted_steps.append(s); | |
| 654 | } | |
| 655 | } | |
| 656 | ||
| 657 | for (wanted_steps.items) |s| { | |
| 658 | try self.makeOneStep(s); | |
| 659 | } | |
| 660 | } | |
| 661 | ||
| 662 | pub fn getInstallStep(self: *Build) *Step { | |
| 663 | return &self.install_tls.step; | |
| 664 | } | |
| 665 | ||
| 666 | pub fn getUninstallStep(self: *Build) *Step { | |
| 667 | return &self.uninstall_tls.step; | |
| 668 | } | |
| 669 | ||
| 670 | fn makeUninstall(uninstall_step: *Step) anyerror!void { | |
| 671 | const uninstall_tls = @fieldParentPtr(TopLevelStep, "step", uninstall_step); | |
| 672 | const self = @fieldParentPtr(Build, "uninstall_tls", uninstall_tls); | |
| 673 | ||
| 674 | for (self.installed_files.items) |installed_file| { | |
| 675 | const full_path = self.getInstallPath(installed_file.dir, installed_file.path); | |
| 676 | if (self.verbose) { | |
| 677 | log.info("rm {s}", .{full_path}); | |
| 678 | } | |
| 679 | fs.cwd().deleteTree(full_path) catch {}; | |
| 680 | } | |
| 681 | ||
| 682 | // TODO remove empty directories | |
| 683 | } | |
| 684 | ||
| 685 | fn makeOneStep(self: *Build, s: *Step) anyerror!void { | |
| 686 | if (s.loop_flag) { | |
| 687 | log.err("Dependency loop detected:\n {s}", .{s.name}); | |
| 688 | return error.DependencyLoopDetected; | |
| 689 | } | |
| 690 | s.loop_flag = true; | |
| 691 | ||
| 692 | for (s.dependencies.items) |dep| { | |
| 693 | self.makeOneStep(dep) catch |err| { | |
| 694 | if (err == error.DependencyLoopDetected) { | |
| 695 | log.err(" {s}", .{s.name}); | |
| 696 | } | |
| 697 | return err; | |
| 698 | }; | |
| 699 | } | |
| 700 | ||
| 701 | s.loop_flag = false; | |
| 702 | ||
| 703 | try s.make(); | |
| 704 | } | |
| 705 | ||
| 706 | fn getTopLevelStepByName(self: *Build, name: []const u8) !*Step { | |
| 707 | for (self.top_level_steps.items) |top_level_step| { | |
| 708 | if (mem.eql(u8, top_level_step.step.name, name)) { | |
| 709 | return &top_level_step.step; | |
| 710 | } | |
| 711 | } | |
| 712 | log.err("Cannot run step '{s}' because it does not exist", .{name}); | |
| 713 | return error.InvalidStepName; | |
| 714 | } | |
| 715 | ||
| 716 | pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_raw: []const u8) ?T { | |
| 717 | const name = self.dupe(name_raw); | |
| 718 | const description = self.dupe(description_raw); | |
| 719 | const type_id = comptime typeToEnum(T); | |
| 720 | const enum_options = if (type_id == .@"enum") blk: { | |
| 721 | const fields = comptime std.meta.fields(T); | |
| 722 | var options = ArrayList([]const u8).initCapacity(self.allocator, fields.len) catch @panic("OOM"); | |
| 723 | ||
| 724 | inline for (fields) |field| { | |
| 725 | options.appendAssumeCapacity(field.name); | |
| 726 | } | |
| 727 | ||
| 728 | break :blk options.toOwnedSlice() catch @panic("OOM"); | |
| 729 | } else null; | |
| 730 | const available_option = AvailableOption{ | |
| 731 | .name = name, | |
| 732 | .type_id = type_id, | |
| 733 | .description = description, | |
| 734 | .enum_options = enum_options, | |
| 735 | }; | |
| 736 | if ((self.available_options_map.fetchPut(name, available_option) catch @panic("OOM")) != null) { | |
| 737 | panic("Option '{s}' declared twice", .{name}); | |
| 738 | } | |
| 739 | self.available_options_list.append(available_option) catch @panic("OOM"); | |
| 740 | ||
| 741 | const option_ptr = self.user_input_options.getPtr(name) orelse return null; | |
| 742 | option_ptr.used = true; | |
| 743 | switch (type_id) { | |
| 744 | .bool => switch (option_ptr.value) { | |
| 745 | .flag => return true, | |
| 746 | .scalar => |s| { | |
| 747 | if (mem.eql(u8, s, "true")) { | |
| 748 | return true; | |
| 749 | } else if (mem.eql(u8, s, "false")) { | |
| 750 | return false; | |
| 751 | } else { | |
| 752 | log.err("Expected -D{s} to be a boolean, but received '{s}'\n", .{ name, s }); | |
| 753 | self.markInvalidUserInput(); | |
| 754 | return null; | |
| 755 | } | |
| 756 | }, | |
| 757 | .list, .map => { | |
| 758 | log.err("Expected -D{s} to be a boolean, but received a {s}.\n", .{ | |
| 759 | name, @tagName(option_ptr.value), | |
| 760 | }); | |
| 761 | self.markInvalidUserInput(); | |
| 762 | return null; | |
| 763 | }, | |
| 764 | }, | |
| 765 | .int => switch (option_ptr.value) { | |
| 766 | .flag, .list, .map => { | |
| 767 | log.err("Expected -D{s} to be an integer, but received a {s}.\n", .{ | |
| 768 | name, @tagName(option_ptr.value), | |
| 769 | }); | |
| 770 | self.markInvalidUserInput(); | |
| 771 | return null; | |
| 772 | }, | |
| 773 | .scalar => |s| { | |
| 774 | const n = std.fmt.parseInt(T, s, 10) catch |err| switch (err) { | |
| 775 | error.Overflow => { | |
| 776 | log.err("-D{s} value {s} cannot fit into type {s}.\n", .{ name, s, @typeName(T) }); | |
| 777 | self.markInvalidUserInput(); | |
| 778 | return null; | |
| 779 | }, | |
| 780 | else => { | |
| 781 | log.err("Expected -D{s} to be an integer of type {s}.\n", .{ name, @typeName(T) }); | |
| 782 | self.markInvalidUserInput(); | |
| 783 | return null; | |
| 784 | }, | |
| 785 | }; | |
| 786 | return n; | |
| 787 | }, | |
| 788 | }, | |
| 789 | .float => switch (option_ptr.value) { | |
| 790 | .flag, .map, .list => { | |
| 791 | log.err("Expected -D{s} to be a float, but received a {s}.\n", .{ | |
| 792 | name, @tagName(option_ptr.value), | |
| 793 | }); | |
| 794 | self.markInvalidUserInput(); | |
| 795 | return null; | |
| 796 | }, | |
| 797 | .scalar => |s| { | |
| 798 | const n = std.fmt.parseFloat(T, s) catch { | |
| 799 | log.err("Expected -D{s} to be a float of type {s}.\n", .{ name, @typeName(T) }); | |
| 800 | self.markInvalidUserInput(); | |
| 801 | return null; | |
| 802 | }; | |
| 803 | return n; | |
| 804 | }, | |
| 805 | }, | |
| 806 | .@"enum" => switch (option_ptr.value) { | |
| 807 | .flag, .map, .list => { | |
| 808 | log.err("Expected -D{s} to be an enum, but received a {s}.\n", .{ | |
| 809 | name, @tagName(option_ptr.value), | |
| 810 | }); | |
| 811 | self.markInvalidUserInput(); | |
| 812 | return null; | |
| 813 | }, | |
| 814 | .scalar => |s| { | |
| 815 | if (std.meta.stringToEnum(T, s)) |enum_lit| { | |
| 816 | return enum_lit; | |
| 817 | } else { | |
| 818 | log.err("Expected -D{s} to be of type {s}.\n", .{ name, @typeName(T) }); | |
| 819 | self.markInvalidUserInput(); | |
| 820 | return null; | |
| 821 | } | |
| 822 | }, | |
| 823 | }, | |
| 824 | .string => switch (option_ptr.value) { | |
| 825 | .flag, .list, .map => { | |
| 826 | log.err("Expected -D{s} to be a string, but received a {s}.\n", .{ | |
| 827 | name, @tagName(option_ptr.value), | |
| 828 | }); | |
| 829 | self.markInvalidUserInput(); | |
| 830 | return null; | |
| 831 | }, | |
| 832 | .scalar => |s| return s, | |
| 833 | }, | |
| 834 | .list => switch (option_ptr.value) { | |
| 835 | .flag, .map => { | |
| 836 | log.err("Expected -D{s} to be a list, but received a {s}.\n", .{ | |
| 837 | name, @tagName(option_ptr.value), | |
| 838 | }); | |
| 839 | self.markInvalidUserInput(); | |
| 840 | return null; | |
| 841 | }, | |
| 842 | .scalar => |s| { | |
| 843 | return self.allocator.dupe([]const u8, &[_][]const u8{s}) catch @panic("OOM"); | |
| 844 | }, | |
| 845 | .list => |lst| return lst.items, | |
| 846 | }, | |
| 847 | } | |
| 848 | } | |
| 849 | ||
| 850 | pub fn step(self: *Build, name: []const u8, description: []const u8) *Step { | |
| 851 | const step_info = self.allocator.create(TopLevelStep) catch @panic("OOM"); | |
| 852 | step_info.* = TopLevelStep{ | |
| 853 | .step = Step.initNoOp(.top_level, name, self.allocator), | |
| 854 | .description = self.dupe(description), | |
| 855 | }; | |
| 856 | self.top_level_steps.append(step_info) catch @panic("OOM"); | |
| 857 | return &step_info.step; | |
| 858 | } | |
| 859 | ||
| 860 | pub const StandardOptimizeOptionOptions = struct { | |
| 861 | preferred_optimize_mode: ?std.builtin.Mode = null, | |
| 862 | }; | |
| 863 | ||
| 864 | pub fn standardOptimizeOption(self: *Build, options: StandardOptimizeOptionOptions) std.builtin.Mode { | |
| 865 | if (options.preferred_optimize_mode) |mode| { | |
| 866 | if (self.option(bool, "release", "optimize for end users") orelse false) { | |
| 867 | return mode; | |
| 868 | } else { | |
| 869 | return .Debug; | |
| 870 | } | |
| 871 | } else { | |
| 872 | return self.option( | |
| 873 | std.builtin.Mode, | |
| 874 | "optimize", | |
| 875 | "prioritize performance, safety, or binary size (-O flag)", | |
| 876 | ) orelse .Debug; | |
| 877 | } | |
| 878 | } | |
| 879 | ||
| 880 | pub const StandardTargetOptionsArgs = struct { | |
| 881 | whitelist: ?[]const CrossTarget = null, | |
| 882 | ||
| 883 | default_target: CrossTarget = CrossTarget{}, | |
| 884 | }; | |
| 885 | ||
| 886 | /// Exposes standard `zig build` options for choosing a target. | |
| 887 | pub fn standardTargetOptions(self: *Build, args: StandardTargetOptionsArgs) CrossTarget { | |
| 888 | const maybe_triple = self.option( | |
| 889 | []const u8, | |
| 890 | "target", | |
| 891 | "The CPU architecture, OS, and ABI to build for", | |
| 892 | ); | |
| 893 | const mcpu = self.option([]const u8, "cpu", "Target CPU features to add or subtract"); | |
| 894 | ||
| 895 | if (maybe_triple == null and mcpu == null) { | |
| 896 | return args.default_target; | |
| 897 | } | |
| 898 | ||
| 899 | const triple = maybe_triple orelse "native"; | |
| 900 | ||
| 901 | var diags: CrossTarget.ParseOptions.Diagnostics = .{}; | |
| 902 | const selected_target = CrossTarget.parse(.{ | |
| 903 | .arch_os_abi = triple, | |
| 904 | .cpu_features = mcpu, | |
| 905 | .diagnostics = &diags, | |
| 906 | }) catch |err| switch (err) { | |
| 907 | error.UnknownCpuModel => { | |
| 908 | log.err("Unknown CPU: '{s}'\nAvailable CPUs for architecture '{s}':", .{ | |
| 909 | diags.cpu_name.?, | |
| 910 | @tagName(diags.arch.?), | |
| 911 | }); | |
| 912 | for (diags.arch.?.allCpuModels()) |cpu| { | |
| 913 | log.err(" {s}", .{cpu.name}); | |
| 914 | } | |
| 915 | self.markInvalidUserInput(); | |
| 916 | return args.default_target; | |
| 917 | }, | |
| 918 | error.UnknownCpuFeature => { | |
| 919 | log.err( | |
| 920 | \\Unknown CPU feature: '{s}' | |
| 921 | \\Available CPU features for architecture '{s}': | |
| 922 | \\ | |
| 923 | , .{ | |
| 924 | diags.unknown_feature_name.?, | |
| 925 | @tagName(diags.arch.?), | |
| 926 | }); | |
| 927 | for (diags.arch.?.allFeaturesList()) |feature| { | |
| 928 | log.err(" {s}: {s}", .{ feature.name, feature.description }); | |
| 929 | } | |
| 930 | self.markInvalidUserInput(); | |
| 931 | return args.default_target; | |
| 932 | }, | |
| 933 | error.UnknownOperatingSystem => { | |
| 934 | log.err( | |
| 935 | \\Unknown OS: '{s}' | |
| 936 | \\Available operating systems: | |
| 937 | \\ | |
| 938 | , .{diags.os_name.?}); | |
| 939 | inline for (std.meta.fields(std.Target.Os.Tag)) |field| { | |
| 940 | log.err(" {s}", .{field.name}); | |
| 941 | } | |
| 942 | self.markInvalidUserInput(); | |
| 943 | return args.default_target; | |
| 944 | }, | |
| 945 | else => |e| { | |
| 946 | log.err("Unable to parse target '{s}': {s}\n", .{ triple, @errorName(e) }); | |
| 947 | self.markInvalidUserInput(); | |
| 948 | return args.default_target; | |
| 949 | }, | |
| 950 | }; | |
| 951 | ||
| 952 | const selected_canonicalized_triple = selected_target.zigTriple(self.allocator) catch @panic("OOM"); | |
| 953 | ||
| 954 | if (args.whitelist) |list| whitelist_check: { | |
| 955 | // Make sure it's a match of one of the list. | |
| 956 | var mismatch_triple = true; | |
| 957 | var mismatch_cpu_features = true; | |
| 958 | var whitelist_item = CrossTarget{}; | |
| 959 | for (list) |t| { | |
| 960 | mismatch_cpu_features = true; | |
| 961 | mismatch_triple = true; | |
| 962 | ||
| 963 | const t_triple = t.zigTriple(self.allocator) catch @panic("OOM"); | |
| 964 | if (mem.eql(u8, t_triple, selected_canonicalized_triple)) { | |
| 965 | mismatch_triple = false; | |
| 966 | whitelist_item = t; | |
| 967 | if (t.getCpuFeatures().isSuperSetOf(selected_target.getCpuFeatures())) { | |
| 968 | mismatch_cpu_features = false; | |
| 969 | break :whitelist_check; | |
| 970 | } else { | |
| 971 | break; | |
| 972 | } | |
| 973 | } | |
| 974 | } | |
| 975 | if (mismatch_triple) { | |
| 976 | log.err("Chosen target '{s}' does not match one of the supported targets:", .{ | |
| 977 | selected_canonicalized_triple, | |
| 978 | }); | |
| 979 | for (list) |t| { | |
| 980 | const t_triple = t.zigTriple(self.allocator) catch @panic("OOM"); | |
| 981 | log.err(" {s}", .{t_triple}); | |
| 982 | } | |
| 983 | } else { | |
| 984 | assert(mismatch_cpu_features); | |
| 985 | const whitelist_cpu = whitelist_item.getCpu(); | |
| 986 | const selected_cpu = selected_target.getCpu(); | |
| 987 | log.err("Chosen CPU model '{s}' does not match one of the supported targets:", .{ | |
| 988 | selected_cpu.model.name, | |
| 989 | }); | |
| 990 | log.err(" Supported feature Set: ", .{}); | |
| 991 | const all_features = whitelist_cpu.arch.allFeaturesList(); | |
| 992 | var populated_cpu_features = whitelist_cpu.model.features; | |
| 993 | populated_cpu_features.populateDependencies(all_features); | |
| 994 | for (all_features) |feature, i_usize| { | |
| 995 | const i = @intCast(std.Target.Cpu.Feature.Set.Index, i_usize); | |
| 996 | const in_cpu_set = populated_cpu_features.isEnabled(i); | |
| 997 | if (in_cpu_set) { | |
| 998 | log.err("{s} ", .{feature.name}); | |
| 999 | } | |
| 1000 | } | |
| 1001 | log.err(" Remove: ", .{}); | |
| 1002 | for (all_features) |feature, i_usize| { | |
| 1003 | const i = @intCast(std.Target.Cpu.Feature.Set.Index, i_usize); | |
| 1004 | const in_cpu_set = populated_cpu_features.isEnabled(i); | |
| 1005 | const in_actual_set = selected_cpu.features.isEnabled(i); | |
| 1006 | if (in_actual_set and !in_cpu_set) { | |
| 1007 | log.err("{s} ", .{feature.name}); | |
| 1008 | } | |
| 1009 | } | |
| 1010 | } | |
| 1011 | self.markInvalidUserInput(); | |
| 1012 | return args.default_target; | |
| 1013 | } | |
| 1014 | ||
| 1015 | return selected_target; | |
| 1016 | } | |
| 1017 | ||
| 1018 | pub fn addUserInputOption(self: *Build, name_raw: []const u8, value_raw: []const u8) !bool { | |
| 1019 | const name = self.dupe(name_raw); | |
| 1020 | const value = self.dupe(value_raw); | |
| 1021 | const gop = try self.user_input_options.getOrPut(name); | |
| 1022 | if (!gop.found_existing) { | |
| 1023 | gop.value_ptr.* = UserInputOption{ | |
| 1024 | .name = name, | |
| 1025 | .value = .{ .scalar = value }, | |
| 1026 | .used = false, | |
| 1027 | }; | |
| 1028 | return false; | |
| 1029 | } | |
| 1030 | ||
| 1031 | // option already exists | |
| 1032 | switch (gop.value_ptr.value) { | |
| 1033 | .scalar => |s| { | |
| 1034 | // turn it into a list | |
| 1035 | var list = ArrayList([]const u8).init(self.allocator); | |
| 1036 | try list.append(s); | |
| 1037 | try list.append(value); | |
| 1038 | try self.user_input_options.put(name, .{ | |
| 1039 | .name = name, | |
| 1040 | .value = .{ .list = list }, | |
| 1041 | .used = false, | |
| 1042 | }); | |
| 1043 | }, | |
| 1044 | .list => |*list| { | |
| 1045 | // append to the list | |
| 1046 | try list.append(value); | |
| 1047 | try self.user_input_options.put(name, .{ | |
| 1048 | .name = name, | |
| 1049 | .value = .{ .list = list.* }, | |
| 1050 | .used = false, | |
| 1051 | }); | |
| 1052 | }, | |
| 1053 | .flag => { | |
| 1054 | log.warn("Option '-D{s}={s}' conflicts with flag '-D{s}'.", .{ name, value, name }); | |
| 1055 | return true; | |
| 1056 | }, | |
| 1057 | .map => |*map| { | |
| 1058 | _ = map; | |
| 1059 | log.warn("TODO maps as command line arguments is not implemented yet.", .{}); | |
| 1060 | return true; | |
| 1061 | }, | |
| 1062 | } | |
| 1063 | return false; | |
| 1064 | } | |
| 1065 | ||
| 1066 | pub fn addUserInputFlag(self: *Build, name_raw: []const u8) !bool { | |
| 1067 | const name = self.dupe(name_raw); | |
| 1068 | const gop = try self.user_input_options.getOrPut(name); | |
| 1069 | if (!gop.found_existing) { | |
| 1070 | gop.value_ptr.* = .{ | |
| 1071 | .name = name, | |
| 1072 | .value = .{ .flag = {} }, | |
| 1073 | .used = false, | |
| 1074 | }; | |
| 1075 | return false; | |
| 1076 | } | |
| 1077 | ||
| 1078 | // option already exists | |
| 1079 | switch (gop.value_ptr.value) { | |
| 1080 | .scalar => |s| { | |
| 1081 | log.err("Flag '-D{s}' conflicts with option '-D{s}={s}'.", .{ name, name, s }); | |
| 1082 | return true; | |
| 1083 | }, | |
| 1084 | .list, .map => { | |
| 1085 | log.err("Flag '-D{s}' conflicts with multiple options of the same name.", .{name}); | |
| 1086 | return true; | |
| 1087 | }, | |
| 1088 | .flag => {}, | |
| 1089 | } | |
| 1090 | return false; | |
| 1091 | } | |
| 1092 | ||
| 1093 | fn typeToEnum(comptime T: type) TypeId { | |
| 1094 | return switch (@typeInfo(T)) { | |
| 1095 | .Int => .int, | |
| 1096 | .Float => .float, | |
| 1097 | .Bool => .bool, | |
| 1098 | .Enum => .@"enum", | |
| 1099 | else => switch (T) { | |
| 1100 | []const u8 => .string, | |
| 1101 | []const []const u8 => .list, | |
| 1102 | else => @compileError("Unsupported type: " ++ @typeName(T)), | |
| 1103 | }, | |
| 1104 | }; | |
| 1105 | } | |
| 1106 | ||
| 1107 | fn markInvalidUserInput(self: *Build) void { | |
| 1108 | self.invalid_user_input = true; | |
| 1109 | } | |
| 1110 | ||
| 1111 | pub fn validateUserInputDidItFail(self: *Build) bool { | |
| 1112 | // make sure all args are used | |
| 1113 | var it = self.user_input_options.iterator(); | |
| 1114 | while (it.next()) |entry| { | |
| 1115 | if (!entry.value_ptr.used) { | |
| 1116 | log.err("Invalid option: -D{s}", .{entry.key_ptr.*}); | |
| 1117 | self.markInvalidUserInput(); | |
| 1118 | } | |
| 1119 | } | |
| 1120 | ||
| 1121 | return self.invalid_user_input; | |
| 1122 | } | |
| 1123 | ||
| 1124 | pub fn spawnChild(self: *Build, argv: []const []const u8) !void { | |
| 1125 | return self.spawnChildEnvMap(null, self.env_map, argv); | |
| 1126 | } | |
| 1127 | ||
| 1128 | fn printCmd(cwd: ?[]const u8, argv: []const []const u8) void { | |
| 1129 | if (cwd) |yes_cwd| std.debug.print("cd {s} && ", .{yes_cwd}); | |
| 1130 | for (argv) |arg| { | |
| 1131 | std.debug.print("{s} ", .{arg}); | |
| 1132 | } | |
| 1133 | std.debug.print("\n", .{}); | |
| 1134 | } | |
| 1135 | ||
| 1136 | pub fn spawnChildEnvMap(self: *Build, cwd: ?[]const u8, env_map: *const EnvMap, argv: []const []const u8) !void { | |
| 1137 | if (self.verbose) { | |
| 1138 | printCmd(cwd, argv); | |
| 1139 | } | |
| 1140 | ||
| 1141 | if (!std.process.can_spawn) | |
| 1142 | return error.ExecNotSupported; | |
| 1143 | ||
| 1144 | var child = std.ChildProcess.init(argv, self.allocator); | |
| 1145 | child.cwd = cwd; | |
| 1146 | child.env_map = env_map; | |
| 1147 | ||
| 1148 | const term = child.spawnAndWait() catch |err| { | |
| 1149 | log.err("Unable to spawn {s}: {s}", .{ argv[0], @errorName(err) }); | |
| 1150 | return err; | |
| 1151 | }; | |
| 1152 | ||
| 1153 | switch (term) { | |
| 1154 | .Exited => |code| { | |
| 1155 | if (code != 0) { | |
| 1156 | log.err("The following command exited with error code {}:", .{code}); | |
| 1157 | printCmd(cwd, argv); | |
| 1158 | return error.UncleanExit; | |
| 1159 | } | |
| 1160 | }, | |
| 1161 | else => { | |
| 1162 | log.err("The following command terminated unexpectedly:", .{}); | |
| 1163 | printCmd(cwd, argv); | |
| 1164 | ||
| 1165 | return error.UncleanExit; | |
| 1166 | }, | |
| 1167 | } | |
| 1168 | } | |
| 1169 | ||
| 1170 | pub fn makePath(self: *Build, path: []const u8) !void { | |
| 1171 | fs.cwd().makePath(self.pathFromRoot(path)) catch |err| { | |
| 1172 | log.err("Unable to create path {s}: {s}", .{ path, @errorName(err) }); | |
| 1173 | return err; | |
| 1174 | }; | |
| 1175 | } | |
| 1176 | ||
| 1177 | pub fn installArtifact(self: *Build, artifact: *CompileStep) void { | |
| 1178 | self.getInstallStep().dependOn(&self.addInstallArtifact(artifact).step); | |
| 1179 | } | |
| 1180 | ||
| 1181 | pub fn addInstallArtifact(self: *Build, artifact: *CompileStep) *InstallArtifactStep { | |
| 1182 | return InstallArtifactStep.create(self, artifact); | |
| 1183 | } | |
| 1184 | ||
| 1185 | ///`dest_rel_path` is relative to prefix path | |
| 1186 | pub fn installFile(self: *Build, src_path: []const u8, dest_rel_path: []const u8) void { | |
| 1187 | self.getInstallStep().dependOn(&self.addInstallFileWithDir(.{ .path = src_path }, .prefix, dest_rel_path).step); | |
| 1188 | } | |
| 1189 | ||
| 1190 | pub fn installDirectory(self: *Build, options: InstallDirectoryOptions) void { | |
| 1191 | self.getInstallStep().dependOn(&self.addInstallDirectory(options).step); | |
| 1192 | } | |
| 1193 | ||
| 1194 | ///`dest_rel_path` is relative to bin path | |
| 1195 | pub fn installBinFile(self: *Build, src_path: []const u8, dest_rel_path: []const u8) void { | |
| 1196 | self.getInstallStep().dependOn(&self.addInstallFileWithDir(.{ .path = src_path }, .bin, dest_rel_path).step); | |
| 1197 | } | |
| 1198 | ||
| 1199 | ///`dest_rel_path` is relative to lib path | |
| 1200 | pub fn installLibFile(self: *Build, src_path: []const u8, dest_rel_path: []const u8) void { | |
| 1201 | self.getInstallStep().dependOn(&self.addInstallFileWithDir(.{ .path = src_path }, .lib, dest_rel_path).step); | |
| 1202 | } | |
| 1203 | ||
| 1204 | /// Output format (BIN vs Intel HEX) determined by filename | |
| 1205 | pub fn installRaw(self: *Build, artifact: *CompileStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep { | |
| 1206 | const raw = self.addInstallRaw(artifact, dest_filename, options); | |
| 1207 | self.getInstallStep().dependOn(&raw.step); | |
| 1208 | return raw; | |
| 1209 | } | |
| 1210 | ||
| 1211 | ///`dest_rel_path` is relative to install prefix path | |
| 1212 | pub fn addInstallFile(self: *Build, source: FileSource, dest_rel_path: []const u8) *InstallFileStep { | |
| 1213 | return self.addInstallFileWithDir(source.dupe(self), .prefix, dest_rel_path); | |
| 1214 | } | |
| 1215 | ||
| 1216 | ///`dest_rel_path` is relative to bin path | |
| 1217 | pub fn addInstallBinFile(self: *Build, source: FileSource, dest_rel_path: []const u8) *InstallFileStep { | |
| 1218 | return self.addInstallFileWithDir(source.dupe(self), .bin, dest_rel_path); | |
| 1219 | } | |
| 1220 | ||
| 1221 | ///`dest_rel_path` is relative to lib path | |
| 1222 | pub fn addInstallLibFile(self: *Build, source: FileSource, dest_rel_path: []const u8) *InstallFileStep { | |
| 1223 | return self.addInstallFileWithDir(source.dupe(self), .lib, dest_rel_path); | |
| 1224 | } | |
| 1225 | ||
| 1226 | pub fn addInstallHeaderFile(b: *Build, src_path: []const u8, dest_rel_path: []const u8) *InstallFileStep { | |
| 1227 | return b.addInstallFileWithDir(.{ .path = src_path }, .header, dest_rel_path); | |
| 1228 | } | |
| 1229 | ||
| 1230 | pub fn addInstallRaw(self: *Build, artifact: *CompileStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep { | |
| 1231 | return InstallRawStep.create(self, artifact, dest_filename, options); | |
| 1232 | } | |
| 1233 | ||
| 1234 | pub fn addInstallFileWithDir( | |
| 1235 | self: *Build, | |
| 1236 | source: FileSource, | |
| 1237 | install_dir: InstallDir, | |
| 1238 | dest_rel_path: []const u8, | |
| 1239 | ) *InstallFileStep { | |
| 1240 | if (dest_rel_path.len == 0) { | |
| 1241 | panic("dest_rel_path must be non-empty", .{}); | |
| 1242 | } | |
| 1243 | const install_step = self.allocator.create(InstallFileStep) catch @panic("OOM"); | |
| 1244 | install_step.* = InstallFileStep.init(self, source.dupe(self), install_dir, dest_rel_path); | |
| 1245 | return install_step; | |
| 1246 | } | |
| 1247 | ||
| 1248 | pub fn addInstallDirectory(self: *Build, options: InstallDirectoryOptions) *InstallDirStep { | |
| 1249 | const install_step = self.allocator.create(InstallDirStep) catch @panic("OOM"); | |
| 1250 | install_step.* = InstallDirStep.init(self, options); | |
| 1251 | return install_step; | |
| 1252 | } | |
| 1253 | ||
| 1254 | pub fn pushInstalledFile(self: *Build, dir: InstallDir, dest_rel_path: []const u8) void { | |
| 1255 | const file = InstalledFile{ | |
| 1256 | .dir = dir, | |
| 1257 | .path = dest_rel_path, | |
| 1258 | }; | |
| 1259 | self.installed_files.append(file.dupe(self)) catch @panic("OOM"); | |
| 1260 | } | |
| 1261 | ||
| 1262 | pub fn updateFile(self: *Build, source_path: []const u8, dest_path: []const u8) !void { | |
| 1263 | if (self.verbose) { | |
| 1264 | log.info("cp {s} {s} ", .{ source_path, dest_path }); | |
| 1265 | } | |
| 1266 | const cwd = fs.cwd(); | |
| 1267 | const prev_status = try fs.Dir.updateFile(cwd, source_path, cwd, dest_path, .{}); | |
| 1268 | if (self.verbose) switch (prev_status) { | |
| 1269 | .stale => log.info("# installed", .{}), | |
| 1270 | .fresh => log.info("# up-to-date", .{}), | |
| 1271 | }; | |
| 1272 | } | |
| 1273 | ||
| 1274 | pub fn truncateFile(self: *Build, dest_path: []const u8) !void { | |
| 1275 | if (self.verbose) { | |
| 1276 | log.info("truncate {s}", .{dest_path}); | |
| 1277 | } | |
| 1278 | const cwd = fs.cwd(); | |
| 1279 | var src_file = cwd.createFile(dest_path, .{}) catch |err| switch (err) { | |
| 1280 | error.FileNotFound => blk: { | |
| 1281 | if (fs.path.dirname(dest_path)) |dirname| { | |
| 1282 | try cwd.makePath(dirname); | |
| 1283 | } | |
| 1284 | break :blk try cwd.createFile(dest_path, .{}); | |
| 1285 | }, | |
| 1286 | else => |e| return e, | |
| 1287 | }; | |
| 1288 | src_file.close(); | |
| 1289 | } | |
| 1290 | ||
| 1291 | pub fn pathFromRoot(self: *Build, rel_path: []const u8) []u8 { | |
| 1292 | return fs.path.resolve(self.allocator, &[_][]const u8{ self.build_root, rel_path }) catch @panic("OOM"); | |
| 1293 | } | |
| 1294 | ||
| 1295 | pub fn pathJoin(self: *Build, paths: []const []const u8) []u8 { | |
| 1296 | return fs.path.join(self.allocator, paths) catch @panic("OOM"); | |
| 1297 | } | |
| 1298 | ||
| 1299 | pub fn fmt(self: *Build, comptime format: []const u8, args: anytype) []u8 { | |
| 1300 | return fmt_lib.allocPrint(self.allocator, format, args) catch @panic("OOM"); | |
| 1301 | } | |
| 1302 | ||
| 1303 | pub fn findProgram(self: *Build, names: []const []const u8, paths: []const []const u8) ![]const u8 { | |
| 1304 | // TODO report error for ambiguous situations | |
| 1305 | const exe_extension = @as(CrossTarget, .{}).exeFileExt(); | |
| 1306 | for (self.search_prefixes.items) |search_prefix| { | |
| 1307 | for (names) |name| { | |
| 1308 | if (fs.path.isAbsolute(name)) { | |
| 1309 | return name; | |
| 1310 | } | |
| 1311 | const full_path = self.pathJoin(&.{ | |
| 1312 | search_prefix, | |
| 1313 | "bin", | |
| 1314 | self.fmt("{s}{s}", .{ name, exe_extension }), | |
| 1315 | }); | |
| 1316 | return fs.realpathAlloc(self.allocator, full_path) catch continue; | |
| 1317 | } | |
| 1318 | } | |
| 1319 | if (self.env_map.get("PATH")) |PATH| { | |
| 1320 | for (names) |name| { | |
| 1321 | if (fs.path.isAbsolute(name)) { | |
| 1322 | return name; | |
| 1323 | } | |
| 1324 | var it = mem.tokenize(u8, PATH, &[_]u8{fs.path.delimiter}); | |
| 1325 | while (it.next()) |path| { | |
| 1326 | const full_path = self.pathJoin(&.{ | |
| 1327 | path, | |
| 1328 | self.fmt("{s}{s}", .{ name, exe_extension }), | |
| 1329 | }); | |
| 1330 | return fs.realpathAlloc(self.allocator, full_path) catch continue; | |
| 1331 | } | |
| 1332 | } | |
| 1333 | } | |
| 1334 | for (names) |name| { | |
| 1335 | if (fs.path.isAbsolute(name)) { | |
| 1336 | return name; | |
| 1337 | } | |
| 1338 | for (paths) |path| { | |
| 1339 | const full_path = self.pathJoin(&.{ | |
| 1340 | path, | |
| 1341 | self.fmt("{s}{s}", .{ name, exe_extension }), | |
| 1342 | }); | |
| 1343 | return fs.realpathAlloc(self.allocator, full_path) catch continue; | |
| 1344 | } | |
| 1345 | } | |
| 1346 | return error.FileNotFound; | |
| 1347 | } | |
| 1348 | ||
| 1349 | pub fn execAllowFail( | |
| 1350 | self: *Build, | |
| 1351 | argv: []const []const u8, | |
| 1352 | out_code: *u8, | |
| 1353 | stderr_behavior: std.ChildProcess.StdIo, | |
| 1354 | ) ExecError![]u8 { | |
| 1355 | assert(argv.len != 0); | |
| 1356 | ||
| 1357 | if (!std.process.can_spawn) | |
| 1358 | return error.ExecNotSupported; | |
| 1359 | ||
| 1360 | const max_output_size = 400 * 1024; | |
| 1361 | var child = std.ChildProcess.init(argv, self.allocator); | |
| 1362 | child.stdin_behavior = .Ignore; | |
| 1363 | child.stdout_behavior = .Pipe; | |
| 1364 | child.stderr_behavior = stderr_behavior; | |
| 1365 | child.env_map = self.env_map; | |
| 1366 | ||
| 1367 | try child.spawn(); | |
| 1368 | ||
| 1369 | const stdout = child.stdout.?.reader().readAllAlloc(self.allocator, max_output_size) catch { | |
| 1370 | return error.ReadFailure; | |
| 1371 | }; | |
| 1372 | errdefer self.allocator.free(stdout); | |
| 1373 | ||
| 1374 | const term = try child.wait(); | |
| 1375 | switch (term) { | |
| 1376 | .Exited => |code| { | |
| 1377 | if (code != 0) { | |
| 1378 | out_code.* = @truncate(u8, code); | |
| 1379 | return error.ExitCodeFailure; | |
| 1380 | } | |
| 1381 | return stdout; | |
| 1382 | }, | |
| 1383 | .Signal, .Stopped, .Unknown => |code| { | |
| 1384 | out_code.* = @truncate(u8, code); | |
| 1385 | return error.ProcessTerminated; | |
| 1386 | }, | |
| 1387 | } | |
| 1388 | } | |
| 1389 | ||
| 1390 | pub fn execFromStep(self: *Build, argv: []const []const u8, src_step: ?*Step) ![]u8 { | |
| 1391 | assert(argv.len != 0); | |
| 1392 | ||
| 1393 | if (self.verbose) { | |
| 1394 | printCmd(null, argv); | |
| 1395 | } | |
| 1396 | ||
| 1397 | if (!std.process.can_spawn) { | |
| 1398 | if (src_step) |s| log.err("{s}...", .{s.name}); | |
| 1399 | log.err("Unable to spawn the following command: cannot spawn child process", .{}); | |
| 1400 | printCmd(null, argv); | |
| 1401 | std.os.abort(); | |
| 1402 | } | |
| 1403 | ||
| 1404 | var code: u8 = undefined; | |
| 1405 | return self.execAllowFail(argv, &code, .Inherit) catch |err| switch (err) { | |
| 1406 | error.ExecNotSupported => { | |
| 1407 | if (src_step) |s| log.err("{s}...", .{s.name}); | |
| 1408 | log.err("Unable to spawn the following command: cannot spawn child process", .{}); | |
| 1409 | printCmd(null, argv); | |
| 1410 | std.os.abort(); | |
| 1411 | }, | |
| 1412 | error.FileNotFound => { | |
| 1413 | if (src_step) |s| log.err("{s}...", .{s.name}); | |
| 1414 | log.err("Unable to spawn the following command: file not found", .{}); | |
| 1415 | printCmd(null, argv); | |
| 1416 | std.os.exit(@truncate(u8, code)); | |
| 1417 | }, | |
| 1418 | error.ExitCodeFailure => { | |
| 1419 | if (src_step) |s| log.err("{s}...", .{s.name}); | |
| 1420 | if (self.prominent_compile_errors) { | |
| 1421 | log.err("The step exited with error code {d}", .{code}); | |
| 1422 | } else { | |
| 1423 | log.err("The following command exited with error code {d}:", .{code}); | |
| 1424 | printCmd(null, argv); | |
| 1425 | } | |
| 1426 | ||
| 1427 | std.os.exit(@truncate(u8, code)); | |
| 1428 | }, | |
| 1429 | error.ProcessTerminated => { | |
| 1430 | if (src_step) |s| log.err("{s}...", .{s.name}); | |
| 1431 | log.err("The following command terminated unexpectedly:", .{}); | |
| 1432 | printCmd(null, argv); | |
| 1433 | std.os.exit(@truncate(u8, code)); | |
| 1434 | }, | |
| 1435 | else => |e| return e, | |
| 1436 | }; | |
| 1437 | } | |
| 1438 | ||
| 1439 | pub fn exec(self: *Build, argv: []const []const u8) ![]u8 { | |
| 1440 | return self.execFromStep(argv, null); | |
| 1441 | } | |
| 1442 | ||
| 1443 | pub fn addSearchPrefix(self: *Build, search_prefix: []const u8) void { | |
| 1444 | self.search_prefixes.append(self.dupePath(search_prefix)) catch @panic("OOM"); | |
| 1445 | } | |
| 1446 | ||
| 1447 | pub fn getInstallPath(self: *Build, dir: InstallDir, dest_rel_path: []const u8) []const u8 { | |
| 1448 | assert(!fs.path.isAbsolute(dest_rel_path)); // Install paths must be relative to the prefix | |
| 1449 | const base_dir = switch (dir) { | |
| 1450 | .prefix => self.install_path, | |
| 1451 | .bin => self.exe_dir, | |
| 1452 | .lib => self.lib_dir, | |
| 1453 | .header => self.h_dir, | |
| 1454 | .custom => |path| self.pathJoin(&.{ self.install_path, path }), | |
| 1455 | }; | |
| 1456 | return fs.path.resolve( | |
| 1457 | self.allocator, | |
| 1458 | &[_][]const u8{ base_dir, dest_rel_path }, | |
| 1459 | ) catch @panic("OOM"); | |
| 1460 | } | |
| 1461 | ||
| 1462 | pub const Dependency = struct { | |
| 1463 | builder: *Build, | |
| 1464 | ||
| 1465 | pub fn artifact(d: *Dependency, name: []const u8) *CompileStep { | |
| 1466 | var found: ?*CompileStep = null; | |
| 1467 | for (d.builder.install_tls.step.dependencies.items) |dep_step| { | |
| 1468 | const inst = dep_step.cast(InstallArtifactStep) orelse continue; | |
| 1469 | if (mem.eql(u8, inst.artifact.name, name)) { | |
| 1470 | if (found != null) panic("artifact name '{s}' is ambiguous", .{name}); | |
| 1471 | found = inst.artifact; | |
| 1472 | } | |
| 1473 | } | |
| 1474 | return found orelse { | |
| 1475 | for (d.builder.install_tls.step.dependencies.items) |dep_step| { | |
| 1476 | const inst = dep_step.cast(InstallArtifactStep) orelse continue; | |
| 1477 | log.info("available artifact: '{s}'", .{inst.artifact.name}); | |
| 1478 | } | |
| 1479 | panic("unable to find artifact '{s}'", .{name}); | |
| 1480 | }; | |
| 1481 | } | |
| 1482 | }; | |
| 1483 | ||
| 1484 | pub fn dependency(b: *Build, name: []const u8, args: anytype) *Dependency { | |
| 1485 | const build_runner = @import("root"); | |
| 1486 | const deps = build_runner.dependencies; | |
| 1487 | ||
| 1488 | inline for (@typeInfo(deps.imports).Struct.decls) |decl| { | |
| 1489 | if (mem.startsWith(u8, decl.name, b.dep_prefix) and | |
| 1490 | mem.endsWith(u8, decl.name, name) and | |
| 1491 | decl.name.len == b.dep_prefix.len + name.len) | |
| 1492 | { | |
| 1493 | const build_zig = @field(deps.imports, decl.name); | |
| 1494 | const build_root = @field(deps.build_root, decl.name); | |
| 1495 | return dependencyInner(b, name, build_root, build_zig, args); | |
| 1496 | } | |
| 1497 | } | |
| 1498 | ||
| 1499 | const full_path = b.pathFromRoot("build.zig.zon"); | |
| 1500 | std.debug.print("no dependency named '{s}' in '{s}'. All packages used in build.zig must be declared in this file.\n", .{ name, full_path }); | |
| 1501 | std.process.exit(1); | |
| 1502 | } | |
| 1503 | ||
| 1504 | fn dependencyInner( | |
| 1505 | b: *Build, | |
| 1506 | name: []const u8, | |
| 1507 | build_root: []const u8, | |
| 1508 | comptime build_zig: type, | |
| 1509 | args: anytype, | |
| 1510 | ) *Dependency { | |
| 1511 | const sub_builder = b.createChild(name, build_root, args) catch @panic("unhandled error"); | |
| 1512 | sub_builder.runBuild(build_zig) catch @panic("unhandled error"); | |
| 1513 | ||
| 1514 | if (sub_builder.validateUserInputDidItFail()) { | |
| 1515 | std.debug.dumpCurrentStackTrace(@returnAddress()); | |
| 1516 | } | |
| 1517 | ||
| 1518 | const dep = b.allocator.create(Dependency) catch @panic("OOM"); | |
| 1519 | dep.* = .{ .builder = sub_builder }; | |
| 1520 | return dep; | |
| 1521 | } | |
| 1522 | ||
| 1523 | pub fn runBuild(b: *Build, build_zig: anytype) anyerror!void { | |
| 1524 | switch (@typeInfo(@typeInfo(@TypeOf(build_zig.build)).Fn.return_type.?)) { | |
| 1525 | .Void => build_zig.build(b), | |
| 1526 | .ErrorUnion => try build_zig.build(b), | |
| 1527 | else => @compileError("expected return type of build to be 'void' or '!void'"), | |
| 1528 | } | |
| 1529 | } | |
| 1530 | ||
| 1531 | test "builder.findProgram compiles" { | |
| 1532 | if (builtin.os.tag == .wasi) return error.SkipZigTest; | |
| 1533 | ||
| 1534 | var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); | |
| 1535 | defer arena.deinit(); | |
| 1536 | ||
| 1537 | const host = try NativeTargetInfo.detect(.{}); | |
| 1538 | ||
| 1539 | const builder = try Build.create( | |
| 1540 | arena.allocator(), | |
| 1541 | "zig", | |
| 1542 | "zig-cache", | |
| 1543 | "zig-cache", | |
| 1544 | "zig-cache", | |
| 1545 | host, | |
| 1546 | ); | |
| 1547 | defer builder.destroy(); | |
| 1548 | _ = builder.findProgram(&[_][]const u8{}, &[_][]const u8{}) catch null; | |
| 1549 | } | |
| 1550 | ||
| 1551 | pub const Pkg = struct { | |
| 1552 | name: []const u8, | |
| 1553 | source: FileSource, | |
| 1554 | dependencies: ?[]const Pkg = null, | |
| 1555 | }; | |
| 1556 | ||
| 1557 | /// A file that is generated by a build step. | |
| 1558 | /// This struct is an interface that is meant to be used with `@fieldParentPtr` to implement the actual path logic. | |
| 1559 | pub const GeneratedFile = struct { | |
| 1560 | /// The step that generates the file | |
| 1561 | step: *Step, | |
| 1562 | ||
| 1563 | /// The path to the generated file. Must be either absolute or relative to the build root. | |
| 1564 | /// This value must be set in the `fn make()` of the `step` and must not be `null` afterwards. | |
| 1565 | path: ?[]const u8 = null, | |
| 1566 | ||
| 1567 | pub fn getPath(self: GeneratedFile) []const u8 { | |
| 1568 | return self.path orelse std.debug.panic( | |
| 1569 | "getPath() was called on a GeneratedFile that wasn't build yet. Is there a missing Step dependency on step '{s}'?", | |
| 1570 | .{self.step.name}, | |
| 1571 | ); | |
| 1572 | } | |
| 1573 | }; | |
| 1574 | ||
| 1575 | /// A file source is a reference to an existing or future file. | |
| 1576 | /// | |
| 1577 | pub const FileSource = union(enum) { | |
| 1578 | /// A plain file path, relative to build root or absolute. | |
| 1579 | path: []const u8, | |
| 1580 | ||
| 1581 | /// A file that is generated by an interface. Those files usually are | |
| 1582 | /// not available until built by a build step. | |
| 1583 | generated: *const GeneratedFile, | |
| 1584 | ||
| 1585 | /// Returns a new file source that will have a relative path to the build root guaranteed. | |
| 1586 | /// This should be preferred over setting `.path` directly as it documents that the files are in the project directory. | |
| 1587 | pub fn relative(path: []const u8) FileSource { | |
| 1588 | std.debug.assert(!std.fs.path.isAbsolute(path)); | |
| 1589 | return FileSource{ .path = path }; | |
| 1590 | } | |
| 1591 | ||
| 1592 | /// Returns a string that can be shown to represent the file source. | |
| 1593 | /// Either returns the path or `"generated"`. | |
| 1594 | pub fn getDisplayName(self: FileSource) []const u8 { | |
| 1595 | return switch (self) { | |
| 1596 | .path => self.path, | |
| 1597 | .generated => "generated", | |
| 1598 | }; | |
| 1599 | } | |
| 1600 | ||
| 1601 | /// Adds dependencies this file source implies to the given step. | |
| 1602 | pub fn addStepDependencies(self: FileSource, other_step: *Step) void { | |
| 1603 | switch (self) { | |
| 1604 | .path => {}, | |
| 1605 | .generated => |gen| other_step.dependOn(gen.step), | |
| 1606 | } | |
| 1607 | } | |
| 1608 | ||
| 1609 | /// Should only be called during make(), returns a path relative to the build root or absolute. | |
| 1610 | pub fn getPath(self: FileSource, builder: *Build) []const u8 { | |
| 1611 | const path = switch (self) { | |
| 1612 | .path => |p| builder.pathFromRoot(p), | |
| 1613 | .generated => |gen| gen.getPath(), | |
| 1614 | }; | |
| 1615 | return path; | |
| 1616 | } | |
| 1617 | ||
| 1618 | /// Duplicates the file source for a given builder. | |
| 1619 | pub fn dupe(self: FileSource, b: *Build) FileSource { | |
| 1620 | return switch (self) { | |
| 1621 | .path => |p| .{ .path = b.dupePath(p) }, | |
| 1622 | .generated => |gen| .{ .generated = gen }, | |
| 1623 | }; | |
| 1624 | } | |
| 1625 | }; | |
| 1626 | ||
| 1627 | /// Allocates a new string for assigning a value to a named macro. | |
| 1628 | /// If the value is omitted, it is set to 1. | |
| 1629 | /// `name` and `value` need not live longer than the function call. | |
| 1630 | pub fn constructCMacro(allocator: Allocator, name: []const u8, value: ?[]const u8) []const u8 { | |
| 1631 | var macro = allocator.alloc( | |
| 1632 | u8, | |
| 1633 | name.len + if (value) |value_slice| value_slice.len + 1 else 0, | |
| 1634 | ) catch |err| if (err == error.OutOfMemory) @panic("Out of memory") else unreachable; | |
| 1635 | mem.copy(u8, macro, name); | |
| 1636 | if (value) |value_slice| { | |
| 1637 | macro[name.len] = '='; | |
| 1638 | mem.copy(u8, macro[name.len + 1 ..], value_slice); | |
| 1639 | } | |
| 1640 | return macro; | |
| 1641 | } | |
| 1642 | ||
| 1643 | pub const VcpkgRoot = union(VcpkgRootStatus) { | |
| 1644 | unattempted: void, | |
| 1645 | not_found: void, | |
| 1646 | found: []const u8, | |
| 1647 | }; | |
| 1648 | ||
| 1649 | pub const VcpkgRootStatus = enum { | |
| 1650 | unattempted, | |
| 1651 | not_found, | |
| 1652 | found, | |
| 1653 | }; | |
| 1654 | ||
| 1655 | pub const InstallDir = union(enum) { | |
| 1656 | prefix: void, | |
| 1657 | lib: void, | |
| 1658 | bin: void, | |
| 1659 | header: void, | |
| 1660 | /// A path relative to the prefix | |
| 1661 | custom: []const u8, | |
| 1662 | ||
| 1663 | /// Duplicates the install directory including the path if set to custom. | |
| 1664 | pub fn dupe(self: InstallDir, builder: *Build) InstallDir { | |
| 1665 | if (self == .custom) { | |
| 1666 | // Written with this temporary to avoid RLS problems | |
| 1667 | const duped_path = builder.dupe(self.custom); | |
| 1668 | return .{ .custom = duped_path }; | |
| 1669 | } else { | |
| 1670 | return self; | |
| 1671 | } | |
| 1672 | } | |
| 1673 | }; | |
| 1674 | ||
| 1675 | pub const InstalledFile = struct { | |
| 1676 | dir: InstallDir, | |
| 1677 | path: []const u8, | |
| 1678 | ||
| 1679 | /// Duplicates the installed file path and directory. | |
| 1680 | pub fn dupe(self: InstalledFile, builder: *Build) InstalledFile { | |
| 1681 | return .{ | |
| 1682 | .dir = self.dir.dupe(builder), | |
| 1683 | .path = builder.dupe(self.path), | |
| 1684 | }; | |
| 1685 | } | |
| 1686 | }; | |
| 1687 | ||
| 1688 | pub fn serializeCpu(allocator: Allocator, cpu: std.Target.Cpu) ![]const u8 { | |
| 1689 | // TODO this logic can disappear if cpu model + features becomes part of the target triple | |
| 1690 | const all_features = cpu.arch.allFeaturesList(); | |
| 1691 | var populated_cpu_features = cpu.model.features; | |
| 1692 | populated_cpu_features.populateDependencies(all_features); | |
| 1693 | ||
| 1694 | if (populated_cpu_features.eql(cpu.features)) { | |
| 1695 | // The CPU name alone is sufficient. | |
| 1696 | return cpu.model.name; | |
| 1697 | } else { | |
| 1698 | var mcpu_buffer = ArrayList(u8).init(allocator); | |
| 1699 | try mcpu_buffer.appendSlice(cpu.model.name); | |
| 1700 | ||
| 1701 | for (all_features) |feature, i_usize| { | |
| 1702 | const i = @intCast(std.Target.Cpu.Feature.Set.Index, i_usize); | |
| 1703 | const in_cpu_set = populated_cpu_features.isEnabled(i); | |
| 1704 | const in_actual_set = cpu.features.isEnabled(i); | |
| 1705 | if (in_cpu_set and !in_actual_set) { | |
| 1706 | try mcpu_buffer.writer().print("-{s}", .{feature.name}); | |
| 1707 | } else if (!in_cpu_set and in_actual_set) { | |
| 1708 | try mcpu_buffer.writer().print("+{s}", .{feature.name}); | |
| 1709 | } | |
| 1710 | } | |
| 1711 | ||
| 1712 | return try mcpu_buffer.toOwnedSlice(); | |
| 1713 | } | |
| 1714 | } | |
| 1715 | ||
| 1716 | test "dupePkg()" { | |
| 1717 | if (builtin.os.tag == .wasi) return error.SkipZigTest; | |
| 1718 | ||
| 1719 | var arena = std.heap.ArenaAllocator.init(std.testing.allocator); | |
| 1720 | defer arena.deinit(); | |
| 1721 | ||
| 1722 | const host = try NativeTargetInfo.detect(.{}); | |
| 1723 | ||
| 1724 | var builder = try Build.create( | |
| 1725 | arena.allocator(), | |
| 1726 | "test", | |
| 1727 | "test", | |
| 1728 | "test", | |
| 1729 | "test", | |
| 1730 | host, | |
| 1731 | ); | |
| 1732 | defer builder.destroy(); | |
| 1733 | ||
| 1734 | var pkg_dep = Pkg{ | |
| 1735 | .name = "pkg_dep", | |
| 1736 | .source = .{ .path = "/not/a/pkg_dep.zig" }, | |
| 1737 | }; | |
| 1738 | var pkg_top = Pkg{ | |
| 1739 | .name = "pkg_top", | |
| 1740 | .source = .{ .path = "/not/a/pkg_top.zig" }, | |
| 1741 | .dependencies = &[_]Pkg{pkg_dep}, | |
| 1742 | }; | |
| 1743 | const duped = builder.dupePkg(pkg_top); | |
| 1744 | ||
| 1745 | const original_deps = pkg_top.dependencies.?; | |
| 1746 | const dupe_deps = duped.dependencies.?; | |
| 1747 | ||
| 1748 | // probably the same top level package details | |
| 1749 | try std.testing.expectEqualStrings(pkg_top.name, duped.name); | |
| 1750 | ||
| 1751 | // probably the same dependencies | |
| 1752 | try std.testing.expectEqual(original_deps.len, dupe_deps.len); | |
| 1753 | try std.testing.expectEqual(original_deps[0].name, pkg_dep.name); | |
| 1754 | ||
| 1755 | // could segfault otherwise if pointers in duplicated package's fields are | |
| 1756 | // the same as those in stack allocated package's fields | |
| 1757 | try std.testing.expect(dupe_deps.ptr != original_deps.ptr); | |
| 1758 | try std.testing.expect(duped.name.ptr != pkg_top.name.ptr); | |
| 1759 | try std.testing.expect(duped.source.path.ptr != pkg_top.source.path.ptr); | |
| 1760 | try std.testing.expect(dupe_deps[0].name.ptr != pkg_dep.name.ptr); | |
| 1761 | try std.testing.expect(dupe_deps[0].source.path.ptr != pkg_dep.source.path.ptr); | |
| 1762 | } | |
| 1763 | ||
| 1764 | test { | |
| 1765 | _ = CheckFileStep; | |
| 1766 | _ = CheckObjectStep; | |
| 1767 | _ = EmulatableRunStep; | |
| 1768 | _ = FmtStep; | |
| 1769 | _ = InstallArtifactStep; | |
| 1770 | _ = InstallDirStep; | |
| 1771 | _ = InstallFileStep; | |
| 1772 | _ = InstallRawStep; | |
| 1773 | _ = CompileStep; | |
| 1774 | _ = LogStep; | |
| 1775 | _ = OptionsStep; | |
| 1776 | _ = RemoveDirStep; | |
| 1777 | _ = RunStep; | |
| 1778 | _ = TranslateCStep; | |
| 1779 | _ = WriteFileStep; | |
| 1780 | } |
lib/std/Build/CheckFileStep.zig created+51| ... | ... | @@ -0,0 +1,51 @@ |
| 1 | const std = @import("../std.zig"); | |
| 2 | const Step = std.Build.Step; | |
| 3 | const fs = std.fs; | |
| 4 | const mem = std.mem; | |
| 5 | ||
| 6 | const CheckFileStep = @This(); | |
| 7 | ||
| 8 | pub const base_id = .check_file; | |
| 9 | ||
| 10 | step: Step, | |
| 11 | builder: *std.Build, | |
| 12 | expected_matches: []const []const u8, | |
| 13 | source: std.Build.FileSource, | |
| 14 | max_bytes: usize = 20 * 1024 * 1024, | |
| 15 | ||
| 16 | pub fn create( | |
| 17 | builder: *std.Build, | |
| 18 | source: std.Build.FileSource, | |
| 19 | expected_matches: []const []const u8, | |
| 20 | ) *CheckFileStep { | |
| 21 | const self = builder.allocator.create(CheckFileStep) catch @panic("OOM"); | |
| 22 | self.* = CheckFileStep{ | |
| 23 | .builder = builder, | |
| 24 | .step = Step.init(.check_file, "CheckFile", builder.allocator, make), | |
| 25 | .source = source.dupe(builder), | |
| 26 | .expected_matches = builder.dupeStrings(expected_matches), | |
| 27 | }; | |
| 28 | self.source.addStepDependencies(&self.step); | |
| 29 | return self; | |
| 30 | } | |
| 31 | ||
| 32 | fn make(step: *Step) !void { | |
| 33 | const self = @fieldParentPtr(CheckFileStep, "step", step); | |
| 34 | ||
| 35 | const src_path = self.source.getPath(self.builder); | |
| 36 | const contents = try fs.cwd().readFileAlloc(self.builder.allocator, src_path, self.max_bytes); | |
| 37 | ||
| 38 | for (self.expected_matches) |expected_match| { | |
| 39 | if (mem.indexOf(u8, contents, expected_match) == null) { | |
| 40 | std.debug.print( | |
| 41 | \\ | |
| 42 | \\========= Expected to find: =================== | |
| 43 | \\{s} | |
| 44 | \\========= But file does not contain it: ======= | |
| 45 | \\{s} | |
| 46 | \\ | |
| 47 | , .{ expected_match, contents }); | |
| 48 | return error.TestFailed; | |
| 49 | } | |
| 50 | } | |
| 51 | } |
lib/std/Build/CheckObjectStep.zig created+1024| ... | ... | @@ -0,0 +1,1024 @@ |
| 1 | const std = @import("../std.zig"); | |
| 2 | const assert = std.debug.assert; | |
| 3 | const fs = std.fs; | |
| 4 | const macho = std.macho; | |
| 5 | const math = std.math; | |
| 6 | const mem = std.mem; | |
| 7 | const testing = std.testing; | |
| 8 | ||
| 9 | const CheckObjectStep = @This(); | |
| 10 | ||
| 11 | const Allocator = mem.Allocator; | |
| 12 | const Step = std.Build.Step; | |
| 13 | const EmulatableRunStep = std.Build.EmulatableRunStep; | |
| 14 | ||
| 15 | pub const base_id = .check_object; | |
| 16 | ||
| 17 | step: Step, | |
| 18 | builder: *std.Build, | |
| 19 | source: std.Build.FileSource, | |
| 20 | max_bytes: usize = 20 * 1024 * 1024, | |
| 21 | checks: std.ArrayList(Check), | |
| 22 | dump_symtab: bool = false, | |
| 23 | obj_format: std.Target.ObjectFormat, | |
| 24 | ||
| 25 | pub fn create(builder: *std.Build, source: std.Build.FileSource, obj_format: std.Target.ObjectFormat) *CheckObjectStep { | |
| 26 | const gpa = builder.allocator; | |
| 27 | const self = gpa.create(CheckObjectStep) catch @panic("OOM"); | |
| 28 | self.* = .{ | |
| 29 | .builder = builder, | |
| 30 | .step = Step.init(.check_file, "CheckObject", gpa, make), | |
| 31 | .source = source.dupe(builder), | |
| 32 | .checks = std.ArrayList(Check).init(gpa), | |
| 33 | .obj_format = obj_format, | |
| 34 | }; | |
| 35 | self.source.addStepDependencies(&self.step); | |
| 36 | return self; | |
| 37 | } | |
| 38 | ||
| 39 | /// Runs and (optionally) compares the output of a binary. | |
| 40 | /// Asserts `self` was generated from an executable step. | |
| 41 | pub fn runAndCompare(self: *CheckObjectStep) *EmulatableRunStep { | |
| 42 | const dependencies_len = self.step.dependencies.items.len; | |
| 43 | assert(dependencies_len > 0); | |
| 44 | const exe_step = self.step.dependencies.items[dependencies_len - 1]; | |
| 45 | const exe = exe_step.cast(std.Build.CompileStep).?; | |
| 46 | const emulatable_step = EmulatableRunStep.create(self.builder, "EmulatableRun", exe); | |
| 47 | emulatable_step.step.dependOn(&self.step); | |
| 48 | return emulatable_step; | |
| 49 | } | |
| 50 | ||
| 51 | /// There two types of actions currently suported: | |
| 52 | /// * `.match` - is the main building block of standard matchers with optional eat-all token `{*}` | |
| 53 | /// and extractors by name such as `{n_value}`. Please note this action is very simplistic in nature | |
| 54 | /// i.e., it won't really handle edge cases/nontrivial examples. But given that we do want to use | |
| 55 | /// it mainly to test the output of our object format parser-dumpers when testing the linkers, etc. | |
| 56 | /// it should be plenty useful in its current form. | |
| 57 | /// * `.compute_cmp` - can be used to perform an operation on the extracted global variables | |
| 58 | /// using the MatchAction. It currently only supports an addition. The operation is required | |
| 59 | /// to be specified in Reverse Polish Notation to ease in operator-precedence parsing (well, | |
| 60 | /// to avoid any parsing really). | |
| 61 | /// For example, if the two extracted values were saved as `vmaddr` and `entryoff` respectively | |
| 62 | /// they could then be added with this simple program `vmaddr entryoff +`. | |
| 63 | const Action = struct { | |
| 64 | tag: enum { match, not_present, compute_cmp }, | |
| 65 | phrase: []const u8, | |
| 66 | expected: ?ComputeCompareExpected = null, | |
| 67 | ||
| 68 | /// Will return true if the `phrase` was found in the `haystack`. | |
| 69 | /// Some examples include: | |
| 70 | /// | |
| 71 | /// LC 0 => will match in its entirety | |
| 72 | /// vmaddr {vmaddr} => will match `vmaddr` and then extract the following value as u64 | |
| 73 | /// and save under `vmaddr` global name (see `global_vars` param) | |
| 74 | /// name {*}libobjc{*}.dylib => will match `name` followed by a token which contains `libobjc` and `.dylib` | |
| 75 | /// in that order with other letters in between | |
| 76 | fn match(act: Action, haystack: []const u8, global_vars: anytype) !bool { | |
| 77 | assert(act.tag == .match or act.tag == .not_present); | |
| 78 | ||
| 79 | var candidate_var: ?struct { name: []const u8, value: u64 } = null; | |
| 80 | var hay_it = mem.tokenize(u8, mem.trim(u8, haystack, " "), " "); | |
| 81 | var needle_it = mem.tokenize(u8, mem.trim(u8, act.phrase, " "), " "); | |
| 82 | ||
| 83 | while (needle_it.next()) |needle_tok| { | |
| 84 | const hay_tok = hay_it.next() orelse return false; | |
| 85 | ||
| 86 | if (mem.indexOf(u8, needle_tok, "{*}")) |index| { | |
| 87 | // We have fuzzy matchers within the search pattern, so we match substrings. | |
| 88 | var start = index; | |
| 89 | var n_tok = needle_tok; | |
| 90 | var h_tok = hay_tok; | |
| 91 | while (true) { | |
| 92 | n_tok = n_tok[start + 3 ..]; | |
| 93 | const inner = if (mem.indexOf(u8, n_tok, "{*}")) |sub_end| | |
| 94 | n_tok[0..sub_end] | |
| 95 | else | |
| 96 | n_tok; | |
| 97 | if (mem.indexOf(u8, h_tok, inner) == null) return false; | |
| 98 | start = mem.indexOf(u8, n_tok, "{*}") orelse break; | |
| 99 | } | |
| 100 | } else if (mem.startsWith(u8, needle_tok, "{")) { | |
| 101 | const closing_brace = mem.indexOf(u8, needle_tok, "}") orelse return error.MissingClosingBrace; | |
| 102 | if (closing_brace != needle_tok.len - 1) return error.ClosingBraceNotLast; | |
| 103 | ||
| 104 | const name = needle_tok[1..closing_brace]; | |
| 105 | if (name.len == 0) return error.MissingBraceValue; | |
| 106 | const value = try std.fmt.parseInt(u64, hay_tok, 16); | |
| 107 | candidate_var = .{ | |
| 108 | .name = name, | |
| 109 | .value = value, | |
| 110 | }; | |
| 111 | } else { | |
| 112 | if (!mem.eql(u8, hay_tok, needle_tok)) return false; | |
| 113 | } | |
| 114 | } | |
| 115 | ||
| 116 | if (candidate_var) |v| { | |
| 117 | try global_vars.putNoClobber(v.name, v.value); | |
| 118 | } | |
| 119 | ||
| 120 | return true; | |
| 121 | } | |
| 122 | ||
| 123 | /// Will return true if the `phrase` is correctly parsed into an RPN program and | |
| 124 | /// its reduced, computed value compares using `op` with the expected value, either | |
| 125 | /// a literal or another extracted variable. | |
| 126 | fn computeCmp(act: Action, gpa: Allocator, global_vars: anytype) !bool { | |
| 127 | var op_stack = std.ArrayList(enum { add, sub, mod, mul }).init(gpa); | |
| 128 | var values = std.ArrayList(u64).init(gpa); | |
| 129 | ||
| 130 | var it = mem.tokenize(u8, act.phrase, " "); | |
| 131 | while (it.next()) |next| { | |
| 132 | if (mem.eql(u8, next, "+")) { | |
| 133 | try op_stack.append(.add); | |
| 134 | } else if (mem.eql(u8, next, "-")) { | |
| 135 | try op_stack.append(.sub); | |
| 136 | } else if (mem.eql(u8, next, "%")) { | |
| 137 | try op_stack.append(.mod); | |
| 138 | } else if (mem.eql(u8, next, "*")) { | |
| 139 | try op_stack.append(.mul); | |
| 140 | } else { | |
| 141 | const val = std.fmt.parseInt(u64, next, 0) catch blk: { | |
| 142 | break :blk global_vars.get(next) orelse { | |
| 143 | std.debug.print( | |
| 144 | \\ | |
| 145 | \\========= Variable was not extracted: =========== | |
| 146 | \\{s} | |
| 147 | \\ | |
| 148 | , .{next}); | |
| 149 | return error.UnknownVariable; | |
| 150 | }; | |
| 151 | }; | |
| 152 | try values.append(val); | |
| 153 | } | |
| 154 | } | |
| 155 | ||
| 156 | var op_i: usize = 1; | |
| 157 | var reduced: u64 = values.items[0]; | |
| 158 | for (op_stack.items) |op| { | |
| 159 | const other = values.items[op_i]; | |
| 160 | switch (op) { | |
| 161 | .add => { | |
| 162 | reduced += other; | |
| 163 | }, | |
| 164 | .sub => { | |
| 165 | reduced -= other; | |
| 166 | }, | |
| 167 | .mod => { | |
| 168 | reduced %= other; | |
| 169 | }, | |
| 170 | .mul => { | |
| 171 | reduced *= other; | |
| 172 | }, | |
| 173 | } | |
| 174 | op_i += 1; | |
| 175 | } | |
| 176 | ||
| 177 | const exp_value = switch (act.expected.?.value) { | |
| 178 | .variable => |name| global_vars.get(name) orelse { | |
| 179 | std.debug.print( | |
| 180 | \\ | |
| 181 | \\========= Variable was not extracted: =========== | |
| 182 | \\{s} | |
| 183 | \\ | |
| 184 | , .{name}); | |
| 185 | return error.UnknownVariable; | |
| 186 | }, | |
| 187 | .literal => |x| x, | |
| 188 | }; | |
| 189 | return math.compare(reduced, act.expected.?.op, exp_value); | |
| 190 | } | |
| 191 | }; | |
| 192 | ||
| 193 | const ComputeCompareExpected = struct { | |
| 194 | op: math.CompareOperator, | |
| 195 | value: union(enum) { | |
| 196 | variable: []const u8, | |
| 197 | literal: u64, | |
| 198 | }, | |
| 199 | ||
| 200 | pub fn format( | |
| 201 | value: @This(), | |
| 202 | comptime fmt: []const u8, | |
| 203 | options: std.fmt.FormatOptions, | |
| 204 | writer: anytype, | |
| 205 | ) !void { | |
| 206 | if (fmt.len != 0) std.fmt.invalidFmtError(fmt, value); | |
| 207 | _ = options; | |
| 208 | try writer.print("{s} ", .{@tagName(value.op)}); | |
| 209 | switch (value.value) { | |
| 210 | .variable => |name| try writer.writeAll(name), | |
| 211 | .literal => |x| try writer.print("{x}", .{x}), | |
| 212 | } | |
| 213 | } | |
| 214 | }; | |
| 215 | ||
| 216 | const Check = struct { | |
| 217 | builder: *std.Build, | |
| 218 | actions: std.ArrayList(Action), | |
| 219 | ||
| 220 | fn create(b: *std.Build) Check { | |
| 221 | return .{ | |
| 222 | .builder = b, | |
| 223 | .actions = std.ArrayList(Action).init(b.allocator), | |
| 224 | }; | |
| 225 | } | |
| 226 | ||
| 227 | fn match(self: *Check, phrase: []const u8) void { | |
| 228 | self.actions.append(.{ | |
| 229 | .tag = .match, | |
| 230 | .phrase = self.builder.dupe(phrase), | |
| 231 | }) catch @panic("OOM"); | |
| 232 | } | |
| 233 | ||
| 234 | fn notPresent(self: *Check, phrase: []const u8) void { | |
| 235 | self.actions.append(.{ | |
| 236 | .tag = .not_present, | |
| 237 | .phrase = self.builder.dupe(phrase), | |
| 238 | }) catch @panic("OOM"); | |
| 239 | } | |
| 240 | ||
| 241 | fn computeCmp(self: *Check, phrase: []const u8, expected: ComputeCompareExpected) void { | |
| 242 | self.actions.append(.{ | |
| 243 | .tag = .compute_cmp, | |
| 244 | .phrase = self.builder.dupe(phrase), | |
| 245 | .expected = expected, | |
| 246 | }) catch @panic("OOM"); | |
| 247 | } | |
| 248 | }; | |
| 249 | ||
| 250 | /// Creates a new sequence of actions with `phrase` as the first anchor searched phrase. | |
| 251 | pub fn checkStart(self: *CheckObjectStep, phrase: []const u8) void { | |
| 252 | var new_check = Check.create(self.builder); | |
| 253 | new_check.match(phrase); | |
| 254 | self.checks.append(new_check) catch @panic("OOM"); | |
| 255 | } | |
| 256 | ||
| 257 | /// Adds another searched phrase to the latest created Check with `CheckObjectStep.checkStart(...)`. | |
| 258 | /// Asserts at least one check already exists. | |
| 259 | pub fn checkNext(self: *CheckObjectStep, phrase: []const u8) void { | |
| 260 | assert(self.checks.items.len > 0); | |
| 261 | const last = &self.checks.items[self.checks.items.len - 1]; | |
| 262 | last.match(phrase); | |
| 263 | } | |
| 264 | ||
| 265 | /// Adds another searched phrase to the latest created Check with `CheckObjectStep.checkStart(...)` | |
| 266 | /// however ensures there is no matching phrase in the output. | |
| 267 | /// Asserts at least one check already exists. | |
| 268 | pub fn checkNotPresent(self: *CheckObjectStep, phrase: []const u8) void { | |
| 269 | assert(self.checks.items.len > 0); | |
| 270 | const last = &self.checks.items[self.checks.items.len - 1]; | |
| 271 | last.notPresent(phrase); | |
| 272 | } | |
| 273 | ||
| 274 | /// Creates a new check checking specifically symbol table parsed and dumped from the object | |
| 275 | /// file. | |
| 276 | /// Issuing this check will force parsing and dumping of the symbol table. | |
| 277 | pub fn checkInSymtab(self: *CheckObjectStep) void { | |
| 278 | self.dump_symtab = true; | |
| 279 | const symtab_label = switch (self.obj_format) { | |
| 280 | .macho => MachODumper.symtab_label, | |
| 281 | else => @panic("TODO other parsers"), | |
| 282 | }; | |
| 283 | self.checkStart(symtab_label); | |
| 284 | } | |
| 285 | ||
| 286 | /// Creates a new standalone, singular check which allows running simple binary operations | |
| 287 | /// on the extracted variables. It will then compare the reduced program with the value of | |
| 288 | /// the expected variable. | |
| 289 | pub fn checkComputeCompare( | |
| 290 | self: *CheckObjectStep, | |
| 291 | program: []const u8, | |
| 292 | expected: ComputeCompareExpected, | |
| 293 | ) void { | |
| 294 | var new_check = Check.create(self.builder); | |
| 295 | new_check.computeCmp(program, expected); | |
| 296 | self.checks.append(new_check) catch @panic("OOM"); | |
| 297 | } | |
| 298 | ||
| 299 | fn make(step: *Step) !void { | |
| 300 | const self = @fieldParentPtr(CheckObjectStep, "step", step); | |
| 301 | ||
| 302 | const gpa = self.builder.allocator; | |
| 303 | const src_path = self.source.getPath(self.builder); | |
| 304 | const contents = try fs.cwd().readFileAllocOptions( | |
| 305 | gpa, | |
| 306 | src_path, | |
| 307 | self.max_bytes, | |
| 308 | null, | |
| 309 | @alignOf(u64), | |
| 310 | null, | |
| 311 | ); | |
| 312 | ||
| 313 | const output = switch (self.obj_format) { | |
| 314 | .macho => try MachODumper.parseAndDump(contents, .{ | |
| 315 | .gpa = gpa, | |
| 316 | .dump_symtab = self.dump_symtab, | |
| 317 | }), | |
| 318 | .elf => @panic("TODO elf parser"), | |
| 319 | .coff => @panic("TODO coff parser"), | |
| 320 | .wasm => try WasmDumper.parseAndDump(contents, .{ | |
| 321 | .gpa = gpa, | |
| 322 | .dump_symtab = self.dump_symtab, | |
| 323 | }), | |
| 324 | else => unreachable, | |
| 325 | }; | |
| 326 | ||
| 327 | var vars = std.StringHashMap(u64).init(gpa); | |
| 328 | ||
| 329 | for (self.checks.items) |chk| { | |
| 330 | var it = mem.tokenize(u8, output, "\r\n"); | |
| 331 | for (chk.actions.items) |act| { | |
| 332 | switch (act.tag) { | |
| 333 | .match => { | |
| 334 | while (it.next()) |line| { | |
| 335 | if (try act.match(line, &vars)) break; | |
| 336 | } else { | |
| 337 | std.debug.print( | |
| 338 | \\ | |
| 339 | \\========= Expected to find: ========================== | |
| 340 | \\{s} | |
| 341 | \\========= But parsed file does not contain it: ======= | |
| 342 | \\{s} | |
| 343 | \\ | |
| 344 | , .{ act.phrase, output }); | |
| 345 | return error.TestFailed; | |
| 346 | } | |
| 347 | }, | |
| 348 | .not_present => { | |
| 349 | while (it.next()) |line| { | |
| 350 | if (try act.match(line, &vars)) { | |
| 351 | std.debug.print( | |
| 352 | \\ | |
| 353 | \\========= Expected not to find: =================== | |
| 354 | \\{s} | |
| 355 | \\========= But parsed file does contain it: ======== | |
| 356 | \\{s} | |
| 357 | \\ | |
| 358 | , .{ act.phrase, output }); | |
| 359 | return error.TestFailed; | |
| 360 | } | |
| 361 | } | |
| 362 | }, | |
| 363 | .compute_cmp => { | |
| 364 | const res = act.computeCmp(gpa, vars) catch |err| switch (err) { | |
| 365 | error.UnknownVariable => { | |
| 366 | std.debug.print( | |
| 367 | \\========= From parsed file: ===================== | |
| 368 | \\{s} | |
| 369 | \\ | |
| 370 | , .{output}); | |
| 371 | return error.TestFailed; | |
| 372 | }, | |
| 373 | else => |e| return e, | |
| 374 | }; | |
| 375 | if (!res) { | |
| 376 | std.debug.print( | |
| 377 | \\ | |
| 378 | \\========= Comparison failed for action: =========== | |
| 379 | \\{s} {} | |
| 380 | \\========= From parsed file: ======================= | |
| 381 | \\{s} | |
| 382 | \\ | |
| 383 | , .{ act.phrase, act.expected.?, output }); | |
| 384 | return error.TestFailed; | |
| 385 | } | |
| 386 | }, | |
| 387 | } | |
| 388 | } | |
| 389 | } | |
| 390 | } | |
| 391 | ||
| 392 | const Opts = struct { | |
| 393 | gpa: ?Allocator = null, | |
| 394 | dump_symtab: bool = false, | |
| 395 | }; | |
| 396 | ||
| 397 | const MachODumper = struct { | |
| 398 | const LoadCommandIterator = macho.LoadCommandIterator; | |
| 399 | const symtab_label = "symtab"; | |
| 400 | ||
| 401 | fn parseAndDump(bytes: []align(@alignOf(u64)) const u8, opts: Opts) ![]const u8 { | |
| 402 | const gpa = opts.gpa orelse unreachable; // MachO dumper requires an allocator | |
| 403 | var stream = std.io.fixedBufferStream(bytes); | |
| 404 | const reader = stream.reader(); | |
| 405 | ||
| 406 | const hdr = try reader.readStruct(macho.mach_header_64); | |
| 407 | if (hdr.magic != macho.MH_MAGIC_64) { | |
| 408 | return error.InvalidMagicNumber; | |
| 409 | } | |
| 410 | ||
| 411 | var output = std.ArrayList(u8).init(gpa); | |
| 412 | const writer = output.writer(); | |
| 413 | ||
| 414 | var symtab: []const macho.nlist_64 = undefined; | |
| 415 | var strtab: []const u8 = undefined; | |
| 416 | var sections = std.ArrayList(macho.section_64).init(gpa); | |
| 417 | var imports = std.ArrayList([]const u8).init(gpa); | |
| 418 | ||
| 419 | var it = LoadCommandIterator{ | |
| 420 | .ncmds = hdr.ncmds, | |
| 421 | .buffer = bytes[@sizeOf(macho.mach_header_64)..][0..hdr.sizeofcmds], | |
| 422 | }; | |
| 423 | var i: usize = 0; | |
| 424 | while (it.next()) |cmd| { | |
| 425 | switch (cmd.cmd()) { | |
| 426 | .SEGMENT_64 => { | |
| 427 | const seg = cmd.cast(macho.segment_command_64).?; | |
| 428 | try sections.ensureUnusedCapacity(seg.nsects); | |
| 429 | for (cmd.getSections()) |sect| { | |
| 430 | sections.appendAssumeCapacity(sect); | |
| 431 | } | |
| 432 | }, | |
| 433 | .SYMTAB => if (opts.dump_symtab) { | |
| 434 | const lc = cmd.cast(macho.symtab_command).?; | |
| 435 | symtab = @ptrCast( | |
| 436 | [*]const macho.nlist_64, | |
| 437 | @alignCast(@alignOf(macho.nlist_64), &bytes[lc.symoff]), | |
| 438 | )[0..lc.nsyms]; | |
| 439 | strtab = bytes[lc.stroff..][0..lc.strsize]; | |
| 440 | }, | |
| 441 | .LOAD_DYLIB, | |
| 442 | .LOAD_WEAK_DYLIB, | |
| 443 | .REEXPORT_DYLIB, | |
| 444 | => { | |
| 445 | try imports.append(cmd.getDylibPathName()); | |
| 446 | }, | |
| 447 | else => {}, | |
| 448 | } | |
| 449 | ||
| 450 | try dumpLoadCommand(cmd, i, writer); | |
| 451 | try writer.writeByte('\n'); | |
| 452 | ||
| 453 | i += 1; | |
| 454 | } | |
| 455 | ||
| 456 | if (opts.dump_symtab) { | |
| 457 | try writer.print("{s}\n", .{symtab_label}); | |
| 458 | for (symtab) |sym| { | |
| 459 | if (sym.stab()) continue; | |
| 460 | const sym_name = mem.sliceTo(@ptrCast([*:0]const u8, strtab.ptr + sym.n_strx), 0); | |
| 461 | if (sym.sect()) { | |
| 462 | const sect = sections.items[sym.n_sect - 1]; | |
| 463 | try writer.print("{x} ({s},{s})", .{ | |
| 464 | sym.n_value, | |
| 465 | sect.segName(), | |
| 466 | sect.sectName(), | |
| 467 | }); | |
| 468 | if (sym.ext()) { | |
| 469 | try writer.writeAll(" external"); | |
| 470 | } | |
| 471 | try writer.print(" {s}\n", .{sym_name}); | |
| 472 | } else if (sym.undf()) { | |
| 473 | const ordinal = @divTrunc(@bitCast(i16, sym.n_desc), macho.N_SYMBOL_RESOLVER); | |
| 474 | const import_name = blk: { | |
| 475 | if (ordinal <= 0) { | |
| 476 | if (ordinal == macho.BIND_SPECIAL_DYLIB_SELF) | |
| 477 | break :blk "self import"; | |
| 478 | if (ordinal == macho.BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE) | |
| 479 | break :blk "main executable"; | |
| 480 | if (ordinal == macho.BIND_SPECIAL_DYLIB_FLAT_LOOKUP) | |
| 481 | break :blk "flat lookup"; | |
| 482 | unreachable; | |
| 483 | } | |
| 484 | const full_path = imports.items[@bitCast(u16, ordinal) - 1]; | |
| 485 | const basename = fs.path.basename(full_path); | |
| 486 | assert(basename.len > 0); | |
| 487 | const ext = mem.lastIndexOfScalar(u8, basename, '.') orelse basename.len; | |
| 488 | break :blk basename[0..ext]; | |
| 489 | }; | |
| 490 | try writer.writeAll("(undefined)"); | |
| 491 | if (sym.weakRef()) { | |
| 492 | try writer.writeAll(" weak"); | |
| 493 | } | |
| 494 | if (sym.ext()) { | |
| 495 | try writer.writeAll(" external"); | |
| 496 | } | |
| 497 | try writer.print(" {s} (from {s})\n", .{ | |
| 498 | sym_name, | |
| 499 | import_name, | |
| 500 | }); | |
| 501 | } else unreachable; | |
| 502 | } | |
| 503 | } | |
| 504 | ||
| 505 | return output.toOwnedSlice(); | |
| 506 | } | |
| 507 | ||
| 508 | fn dumpLoadCommand(lc: macho.LoadCommandIterator.LoadCommand, index: usize, writer: anytype) !void { | |
| 509 | // print header first | |
| 510 | try writer.print( | |
| 511 | \\LC {d} | |
| 512 | \\cmd {s} | |
| 513 | \\cmdsize {d} | |
| 514 | , .{ index, @tagName(lc.cmd()), lc.cmdsize() }); | |
| 515 | ||
| 516 | switch (lc.cmd()) { | |
| 517 | .SEGMENT_64 => { | |
| 518 | const seg = lc.cast(macho.segment_command_64).?; | |
| 519 | try writer.writeByte('\n'); | |
| 520 | try writer.print( | |
| 521 | \\segname {s} | |
| 522 | \\vmaddr {x} | |
| 523 | \\vmsize {x} | |
| 524 | \\fileoff {x} | |
| 525 | \\filesz {x} | |
| 526 | , .{ | |
| 527 | seg.segName(), | |
| 528 | seg.vmaddr, | |
| 529 | seg.vmsize, | |
| 530 | seg.fileoff, | |
| 531 | seg.filesize, | |
| 532 | }); | |
| 533 | ||
| 534 | for (lc.getSections()) |sect| { | |
| 535 | try writer.writeByte('\n'); | |
| 536 | try writer.print( | |
| 537 | \\sectname {s} | |
| 538 | \\addr {x} | |
| 539 | \\size {x} | |
| 540 | \\offset {x} | |
| 541 | \\align {x} | |
| 542 | , .{ | |
| 543 | sect.sectName(), | |
| 544 | sect.addr, | |
| 545 | sect.size, | |
| 546 | sect.offset, | |
| 547 | sect.@"align", | |
| 548 | }); | |
| 549 | } | |
| 550 | }, | |
| 551 | ||
| 552 | .ID_DYLIB, | |
| 553 | .LOAD_DYLIB, | |
| 554 | .LOAD_WEAK_DYLIB, | |
| 555 | .REEXPORT_DYLIB, | |
| 556 | => { | |
| 557 | const dylib = lc.cast(macho.dylib_command).?; | |
| 558 | try writer.writeByte('\n'); | |
| 559 | try writer.print( | |
| 560 | \\name {s} | |
| 561 | \\timestamp {d} | |
| 562 | \\current version {x} | |
| 563 | \\compatibility version {x} | |
| 564 | , .{ | |
| 565 | lc.getDylibPathName(), | |
| 566 | dylib.dylib.timestamp, | |
| 567 | dylib.dylib.current_version, | |
| 568 | dylib.dylib.compatibility_version, | |
| 569 | }); | |
| 570 | }, | |
| 571 | ||
| 572 | .MAIN => { | |
| 573 | const main = lc.cast(macho.entry_point_command).?; | |
| 574 | try writer.writeByte('\n'); | |
| 575 | try writer.print( | |
| 576 | \\entryoff {x} | |
| 577 | \\stacksize {x} | |
| 578 | , .{ main.entryoff, main.stacksize }); | |
| 579 | }, | |
| 580 | ||
| 581 | .RPATH => { | |
| 582 | try writer.writeByte('\n'); | |
| 583 | try writer.print( | |
| 584 | \\path {s} | |
| 585 | , .{ | |
| 586 | lc.getRpathPathName(), | |
| 587 | }); | |
| 588 | }, | |
| 589 | ||
| 590 | .UUID => { | |
| 591 | const uuid = lc.cast(macho.uuid_command).?; | |
| 592 | try writer.writeByte('\n'); | |
| 593 | try writer.print("uuid {x}", .{std.fmt.fmtSliceHexLower(&uuid.uuid)}); | |
| 594 | }, | |
| 595 | ||
| 596 | .DATA_IN_CODE, | |
| 597 | .FUNCTION_STARTS, | |
| 598 | .CODE_SIGNATURE, | |
| 599 | => { | |
| 600 | const llc = lc.cast(macho.linkedit_data_command).?; | |
| 601 | try writer.writeByte('\n'); | |
| 602 | try writer.print( | |
| 603 | \\dataoff {x} | |
| 604 | \\datasize {x} | |
| 605 | , .{ llc.dataoff, llc.datasize }); | |
| 606 | }, | |
| 607 | ||
| 608 | .DYLD_INFO_ONLY => { | |
| 609 | const dlc = lc.cast(macho.dyld_info_command).?; | |
| 610 | try writer.writeByte('\n'); | |
| 611 | try writer.print( | |
| 612 | \\rebaseoff {x} | |
| 613 | \\rebasesize {x} | |
| 614 | \\bindoff {x} | |
| 615 | \\bindsize {x} | |
| 616 | \\weakbindoff {x} | |
| 617 | \\weakbindsize {x} | |
| 618 | \\lazybindoff {x} | |
| 619 | \\lazybindsize {x} | |
| 620 | \\exportoff {x} | |
| 621 | \\exportsize {x} | |
| 622 | , .{ | |
| 623 | dlc.rebase_off, | |
| 624 | dlc.rebase_size, | |
| 625 | dlc.bind_off, | |
| 626 | dlc.bind_size, | |
| 627 | dlc.weak_bind_off, | |
| 628 | dlc.weak_bind_size, | |
| 629 | dlc.lazy_bind_off, | |
| 630 | dlc.lazy_bind_size, | |
| 631 | dlc.export_off, | |
| 632 | dlc.export_size, | |
| 633 | }); | |
| 634 | }, | |
| 635 | ||
| 636 | .SYMTAB => { | |
| 637 | const slc = lc.cast(macho.symtab_command).?; | |
| 638 | try writer.writeByte('\n'); | |
| 639 | try writer.print( | |
| 640 | \\symoff {x} | |
| 641 | \\nsyms {x} | |
| 642 | \\stroff {x} | |
| 643 | \\strsize {x} | |
| 644 | , .{ | |
| 645 | slc.symoff, | |
| 646 | slc.nsyms, | |
| 647 | slc.stroff, | |
| 648 | slc.strsize, | |
| 649 | }); | |
| 650 | }, | |
| 651 | ||
| 652 | .DYSYMTAB => { | |
| 653 | const dlc = lc.cast(macho.dysymtab_command).?; | |
| 654 | try writer.writeByte('\n'); | |
| 655 | try writer.print( | |
| 656 | \\ilocalsym {x} | |
| 657 | \\nlocalsym {x} | |
| 658 | \\iextdefsym {x} | |
| 659 | \\nextdefsym {x} | |
| 660 | \\iundefsym {x} | |
| 661 | \\nundefsym {x} | |
| 662 | \\indirectsymoff {x} | |
| 663 | \\nindirectsyms {x} | |
| 664 | , .{ | |
| 665 | dlc.ilocalsym, | |
| 666 | dlc.nlocalsym, | |
| 667 | dlc.iextdefsym, | |
| 668 | dlc.nextdefsym, | |
| 669 | dlc.iundefsym, | |
| 670 | dlc.nundefsym, | |
| 671 | dlc.indirectsymoff, | |
| 672 | dlc.nindirectsyms, | |
| 673 | }); | |
| 674 | }, | |
| 675 | ||
| 676 | else => {}, | |
| 677 | } | |
| 678 | } | |
| 679 | }; | |
| 680 | ||
| 681 | const WasmDumper = struct { | |
| 682 | const symtab_label = "symbols"; | |
| 683 | ||
| 684 | fn parseAndDump(bytes: []const u8, opts: Opts) ![]const u8 { | |
| 685 | const gpa = opts.gpa orelse unreachable; // Wasm dumper requires an allocator | |
| 686 | if (opts.dump_symtab) { | |
| 687 | @panic("TODO: Implement symbol table parsing and dumping"); | |
| 688 | } | |
| 689 | ||
| 690 | var fbs = std.io.fixedBufferStream(bytes); | |
| 691 | const reader = fbs.reader(); | |
| 692 | ||
| 693 | const buf = try reader.readBytesNoEof(8); | |
| 694 | if (!mem.eql(u8, buf[0..4], &std.wasm.magic)) { | |
| 695 | return error.InvalidMagicByte; | |
| 696 | } | |
| 697 | if (!mem.eql(u8, buf[4..], &std.wasm.version)) { | |
| 698 | return error.UnsupportedWasmVersion; | |
| 699 | } | |
| 700 | ||
| 701 | var output = std.ArrayList(u8).init(gpa); | |
| 702 | errdefer output.deinit(); | |
| 703 | const writer = output.writer(); | |
| 704 | ||
| 705 | while (reader.readByte()) |current_byte| { | |
| 706 | const section = std.meta.intToEnum(std.wasm.Section, current_byte) catch |err| { | |
| 707 | std.debug.print("Found invalid section id '{d}'\n", .{current_byte}); | |
| 708 | return err; | |
| 709 | }; | |
| 710 | ||
| 711 | const section_length = try std.leb.readULEB128(u32, reader); | |
| 712 | try parseAndDumpSection(section, bytes[fbs.pos..][0..section_length], writer); | |
| 713 | fbs.pos += section_length; | |
| 714 | } else |_| {} // reached end of stream | |
| 715 | ||
| 716 | return output.toOwnedSlice(); | |
| 717 | } | |
| 718 | ||
| 719 | fn parseAndDumpSection(section: std.wasm.Section, data: []const u8, writer: anytype) !void { | |
| 720 | var fbs = std.io.fixedBufferStream(data); | |
| 721 | const reader = fbs.reader(); | |
| 722 | ||
| 723 | try writer.print( | |
| 724 | \\Section {s} | |
| 725 | \\size {d} | |
| 726 | , .{ @tagName(section), data.len }); | |
| 727 | ||
| 728 | switch (section) { | |
| 729 | .type, | |
| 730 | .import, | |
| 731 | .function, | |
| 732 | .table, | |
| 733 | .memory, | |
| 734 | .global, | |
| 735 | .@"export", | |
| 736 | .element, | |
| 737 | .code, | |
| 738 | .data, | |
| 739 | => { | |
| 740 | const entries = try std.leb.readULEB128(u32, reader); | |
| 741 | try writer.print("\nentries {d}\n", .{entries}); | |
| 742 | try dumpSection(section, data[fbs.pos..], entries, writer); | |
| 743 | }, | |
| 744 | .custom => { | |
| 745 | const name_length = try std.leb.readULEB128(u32, reader); | |
| 746 | const name = data[fbs.pos..][0..name_length]; | |
| 747 | fbs.pos += name_length; | |
| 748 | try writer.print("\nname {s}\n", .{name}); | |
| 749 | ||
| 750 | if (mem.eql(u8, name, "name")) { | |
| 751 | try parseDumpNames(reader, writer, data); | |
| 752 | } else if (mem.eql(u8, name, "producers")) { | |
| 753 | try parseDumpProducers(reader, writer, data); | |
| 754 | } else if (mem.eql(u8, name, "target_features")) { | |
| 755 | try parseDumpFeatures(reader, writer, data); | |
| 756 | } | |
| 757 | // TODO: Implement parsing and dumping other custom sections (such as relocations) | |
| 758 | }, | |
| 759 | .start => { | |
| 760 | const start = try std.leb.readULEB128(u32, reader); | |
| 761 | try writer.print("\nstart {d}\n", .{start}); | |
| 762 | }, | |
| 763 | else => {}, // skip unknown sections | |
| 764 | } | |
| 765 | } | |
| 766 | ||
| 767 | fn dumpSection(section: std.wasm.Section, data: []const u8, entries: u32, writer: anytype) !void { | |
| 768 | var fbs = std.io.fixedBufferStream(data); | |
| 769 | const reader = fbs.reader(); | |
| 770 | ||
| 771 | switch (section) { | |
| 772 | .type => { | |
| 773 | var i: u32 = 0; | |
| 774 | while (i < entries) : (i += 1) { | |
| 775 | const func_type = try reader.readByte(); | |
| 776 | if (func_type != std.wasm.function_type) { | |
| 777 | std.debug.print("Expected function type, found byte '{d}'\n", .{func_type}); | |
| 778 | return error.UnexpectedByte; | |
| 779 | } | |
| 780 | const params = try std.leb.readULEB128(u32, reader); | |
| 781 | try writer.print("params {d}\n", .{params}); | |
| 782 | var index: u32 = 0; | |
| 783 | while (index < params) : (index += 1) { | |
| 784 | try parseDumpType(std.wasm.Valtype, reader, writer); | |
| 785 | } else index = 0; | |
| 786 | const returns = try std.leb.readULEB128(u32, reader); | |
| 787 | try writer.print("returns {d}\n", .{returns}); | |
| 788 | while (index < returns) : (index += 1) { | |
| 789 | try parseDumpType(std.wasm.Valtype, reader, writer); | |
| 790 | } | |
| 791 | } | |
| 792 | }, | |
| 793 | .import => { | |
| 794 | var i: u32 = 0; | |
| 795 | while (i < entries) : (i += 1) { | |
| 796 | const module_name_len = try std.leb.readULEB128(u32, reader); | |
| 797 | const module_name = data[fbs.pos..][0..module_name_len]; | |
| 798 | fbs.pos += module_name_len; | |
| 799 | const name_len = try std.leb.readULEB128(u32, reader); | |
| 800 | const name = data[fbs.pos..][0..name_len]; | |
| 801 | fbs.pos += name_len; | |
| 802 | ||
| 803 | const kind = std.meta.intToEnum(std.wasm.ExternalKind, try reader.readByte()) catch |err| { | |
| 804 | std.debug.print("Invalid import kind\n", .{}); | |
| 805 | return err; | |
| 806 | }; | |
| 807 | ||
| 808 | try writer.print( | |
| 809 | \\module {s} | |
| 810 | \\name {s} | |
| 811 | \\kind {s} | |
| 812 | , .{ module_name, name, @tagName(kind) }); | |
| 813 | try writer.writeByte('\n'); | |
| 814 | switch (kind) { | |
| 815 | .function => { | |
| 816 | try writer.print("index {d}\n", .{try std.leb.readULEB128(u32, reader)}); | |
| 817 | }, | |
| 818 | .memory => { | |
| 819 | try parseDumpLimits(reader, writer); | |
| 820 | }, | |
| 821 | .global => { | |
| 822 | try parseDumpType(std.wasm.Valtype, reader, writer); | |
| 823 | try writer.print("mutable {}\n", .{0x01 == try std.leb.readULEB128(u32, reader)}); | |
| 824 | }, | |
| 825 | .table => { | |
| 826 | try parseDumpType(std.wasm.RefType, reader, writer); | |
| 827 | try parseDumpLimits(reader, writer); | |
| 828 | }, | |
| 829 | } | |
| 830 | } | |
| 831 | }, | |
| 832 | .function => { | |
| 833 | var i: u32 = 0; | |
| 834 | while (i < entries) : (i += 1) { | |
| 835 | try writer.print("index {d}\n", .{try std.leb.readULEB128(u32, reader)}); | |
| 836 | } | |
| 837 | }, | |
| 838 | .table => { | |
| 839 | var i: u32 = 0; | |
| 840 | while (i < entries) : (i += 1) { | |
| 841 | try parseDumpType(std.wasm.RefType, reader, writer); | |
| 842 | try parseDumpLimits(reader, writer); | |
| 843 | } | |
| 844 | }, | |
| 845 | .memory => { | |
| 846 | var i: u32 = 0; | |
| 847 | while (i < entries) : (i += 1) { | |
| 848 | try parseDumpLimits(reader, writer); | |
| 849 | } | |
| 850 | }, | |
| 851 | .global => { | |
| 852 | var i: u32 = 0; | |
| 853 | while (i < entries) : (i += 1) { | |
| 854 | try parseDumpType(std.wasm.Valtype, reader, writer); | |
| 855 | try writer.print("mutable {}\n", .{0x01 == try std.leb.readULEB128(u1, reader)}); | |
| 856 | try parseDumpInit(reader, writer); | |
| 857 | } | |
| 858 | }, | |
| 859 | .@"export" => { | |
| 860 | var i: u32 = 0; | |
| 861 | while (i < entries) : (i += 1) { | |
| 862 | const name_len = try std.leb.readULEB128(u32, reader); | |
| 863 | const name = data[fbs.pos..][0..name_len]; | |
| 864 | fbs.pos += name_len; | |
| 865 | const kind_byte = try std.leb.readULEB128(u8, reader); | |
| 866 | const kind = std.meta.intToEnum(std.wasm.ExternalKind, kind_byte) catch |err| { | |
| 867 | std.debug.print("invalid export kind value '{d}'\n", .{kind_byte}); | |
| 868 | return err; | |
| 869 | }; | |
| 870 | const index = try std.leb.readULEB128(u32, reader); | |
| 871 | try writer.print( | |
| 872 | \\name {s} | |
| 873 | \\kind {s} | |
| 874 | \\index {d} | |
| 875 | , .{ name, @tagName(kind), index }); | |
| 876 | try writer.writeByte('\n'); | |
| 877 | } | |
| 878 | }, | |
| 879 | .element => { | |
| 880 | var i: u32 = 0; | |
| 881 | while (i < entries) : (i += 1) { | |
| 882 | try writer.print("table index {d}\n", .{try std.leb.readULEB128(u32, reader)}); | |
| 883 | try parseDumpInit(reader, writer); | |
| 884 | ||
| 885 | const function_indexes = try std.leb.readULEB128(u32, reader); | |
| 886 | var function_index: u32 = 0; | |
| 887 | try writer.print("indexes {d}\n", .{function_indexes}); | |
| 888 | while (function_index < function_indexes) : (function_index += 1) { | |
| 889 | try writer.print("index {d}\n", .{try std.leb.readULEB128(u32, reader)}); | |
| 890 | } | |
| 891 | } | |
| 892 | }, | |
| 893 | .code => {}, // code section is considered opaque to linker | |
| 894 | .data => { | |
| 895 | var i: u32 = 0; | |
| 896 | while (i < entries) : (i += 1) { | |
| 897 | const index = try std.leb.readULEB128(u32, reader); | |
| 898 | try writer.print("memory index 0x{x}\n", .{index}); | |
| 899 | try parseDumpInit(reader, writer); | |
| 900 | const size = try std.leb.readULEB128(u32, reader); | |
| 901 | try writer.print("size {d}\n", .{size}); | |
| 902 | try reader.skipBytes(size, .{}); // we do not care about the content of the segments | |
| 903 | } | |
| 904 | }, | |
| 905 | else => unreachable, | |
| 906 | } | |
| 907 | } | |
| 908 | ||
| 909 | fn parseDumpType(comptime WasmType: type, reader: anytype, writer: anytype) !void { | |
| 910 | const type_byte = try reader.readByte(); | |
| 911 | const valtype = std.meta.intToEnum(WasmType, type_byte) catch |err| { | |
| 912 | std.debug.print("Invalid wasm type value '{d}'\n", .{type_byte}); | |
| 913 | return err; | |
| 914 | }; | |
| 915 | try writer.print("type {s}\n", .{@tagName(valtype)}); | |
| 916 | } | |
| 917 | ||
| 918 | fn parseDumpLimits(reader: anytype, writer: anytype) !void { | |
| 919 | const flags = try std.leb.readULEB128(u8, reader); | |
| 920 | const min = try std.leb.readULEB128(u32, reader); | |
| 921 | ||
| 922 | try writer.print("min {x}\n", .{min}); | |
| 923 | if (flags != 0) { | |
| 924 | try writer.print("max {x}\n", .{try std.leb.readULEB128(u32, reader)}); | |
| 925 | } | |
| 926 | } | |
| 927 | ||
| 928 | fn parseDumpInit(reader: anytype, writer: anytype) !void { | |
| 929 | const byte = try std.leb.readULEB128(u8, reader); | |
| 930 | const opcode = std.meta.intToEnum(std.wasm.Opcode, byte) catch |err| { | |
| 931 | std.debug.print("invalid wasm opcode '{d}'\n", .{byte}); | |
| 932 | return err; | |
| 933 | }; | |
| 934 | switch (opcode) { | |
| 935 | .i32_const => try writer.print("i32.const {x}\n", .{try std.leb.readILEB128(i32, reader)}), | |
| 936 | .i64_const => try writer.print("i64.const {x}\n", .{try std.leb.readILEB128(i64, reader)}), | |
| 937 | .f32_const => try writer.print("f32.const {x}\n", .{@bitCast(f32, try reader.readIntLittle(u32))}), | |
| 938 | .f64_const => try writer.print("f64.const {x}\n", .{@bitCast(f64, try reader.readIntLittle(u64))}), | |
| 939 | .global_get => try writer.print("global.get {x}\n", .{try std.leb.readULEB128(u32, reader)}), | |
| 940 | else => unreachable, | |
| 941 | } | |
| 942 | const end_opcode = try std.leb.readULEB128(u8, reader); | |
| 943 | if (end_opcode != std.wasm.opcode(.end)) { | |
| 944 | std.debug.print("expected 'end' opcode in init expression\n", .{}); | |
| 945 | return error.MissingEndOpcode; | |
| 946 | } | |
| 947 | } | |
| 948 | ||
| 949 | fn parseDumpNames(reader: anytype, writer: anytype, data: []const u8) !void { | |
| 950 | while (reader.context.pos < data.len) { | |
| 951 | try parseDumpType(std.wasm.NameSubsection, reader, writer); | |
| 952 | const size = try std.leb.readULEB128(u32, reader); | |
| 953 | const entries = try std.leb.readULEB128(u32, reader); | |
| 954 | try writer.print( | |
| 955 | \\size {d} | |
| 956 | \\names {d} | |
| 957 | , .{ size, entries }); | |
| 958 | try writer.writeByte('\n'); | |
| 959 | var i: u32 = 0; | |
| 960 | while (i < entries) : (i += 1) { | |
| 961 | const index = try std.leb.readULEB128(u32, reader); | |
| 962 | const name_len = try std.leb.readULEB128(u32, reader); | |
| 963 | const pos = reader.context.pos; | |
| 964 | const name = data[pos..][0..name_len]; | |
| 965 | reader.context.pos += name_len; | |
| 966 | ||
| 967 | try writer.print( | |
| 968 | \\index {d} | |
| 969 | \\name {s} | |
| 970 | , .{ index, name }); | |
| 971 | try writer.writeByte('\n'); | |
| 972 | } | |
| 973 | } | |
| 974 | } | |
| 975 | ||
| 976 | fn parseDumpProducers(reader: anytype, writer: anytype, data: []const u8) !void { | |
| 977 | const field_count = try std.leb.readULEB128(u32, reader); | |
| 978 | try writer.print("fields {d}\n", .{field_count}); | |
| 979 | var current_field: u32 = 0; | |
| 980 | while (current_field < field_count) : (current_field += 1) { | |
| 981 | const field_name_length = try std.leb.readULEB128(u32, reader); | |
| 982 | const field_name = data[reader.context.pos..][0..field_name_length]; | |
| 983 | reader.context.pos += field_name_length; | |
| 984 | ||
| 985 | const value_count = try std.leb.readULEB128(u32, reader); | |
| 986 | try writer.print( | |
| 987 | \\field_name {s} | |
| 988 | \\values {d} | |
| 989 | , .{ field_name, value_count }); | |
| 990 | try writer.writeByte('\n'); | |
| 991 | var current_value: u32 = 0; | |
| 992 | while (current_value < value_count) : (current_value += 1) { | |
| 993 | const value_length = try std.leb.readULEB128(u32, reader); | |
| 994 | const value = data[reader.context.pos..][0..value_length]; | |
| 995 | reader.context.pos += value_length; | |
| 996 | ||
| 997 | const version_length = try std.leb.readULEB128(u32, reader); | |
| 998 | const version = data[reader.context.pos..][0..version_length]; | |
| 999 | reader.context.pos += version_length; | |
| 1000 | ||
| 1001 | try writer.print( | |
| 1002 | \\value_name {s} | |
| 1003 | \\version {s} | |
| 1004 | , .{ value, version }); | |
| 1005 | try writer.writeByte('\n'); | |
| 1006 | } | |
| 1007 | } | |
| 1008 | } | |
| 1009 | ||
| 1010 | fn parseDumpFeatures(reader: anytype, writer: anytype, data: []const u8) !void { | |
| 1011 | const feature_count = try std.leb.readULEB128(u32, reader); | |
| 1012 | try writer.print("features {d}\n", .{feature_count}); | |
| 1013 | ||
| 1014 | var index: u32 = 0; | |
| 1015 | while (index < feature_count) : (index += 1) { | |
| 1016 | const prefix_byte = try std.leb.readULEB128(u8, reader); | |
| 1017 | const name_length = try std.leb.readULEB128(u32, reader); | |
| 1018 | const feature_name = data[reader.context.pos..][0..name_length]; | |
| 1019 | reader.context.pos += name_length; | |
| 1020 | ||
| 1021 | try writer.print("{c} {s}\n", .{ prefix_byte, feature_name }); | |
| 1022 | } | |
| 1023 | } | |
| 1024 | }; |
lib/std/Build/CompileStep.zig created+2053| ... | ... | @@ -0,0 +1,2053 @@ |
| 1 | const builtin = @import("builtin"); | |
| 2 | const std = @import("../std.zig"); | |
| 3 | const mem = std.mem; | |
| 4 | const log = std.log; | |
| 5 | const fs = std.fs; | |
| 6 | const assert = std.debug.assert; | |
| 7 | const panic = std.debug.panic; | |
| 8 | const ArrayList = std.ArrayList; | |
| 9 | const StringHashMap = std.StringHashMap; | |
| 10 | const Sha256 = std.crypto.hash.sha2.Sha256; | |
| 11 | const Allocator = mem.Allocator; | |
| 12 | const Step = std.Build.Step; | |
| 13 | const CrossTarget = std.zig.CrossTarget; | |
| 14 | const NativeTargetInfo = std.zig.system.NativeTargetInfo; | |
| 15 | const FileSource = std.Build.FileSource; | |
| 16 | const PkgConfigPkg = std.Build.PkgConfigPkg; | |
| 17 | const PkgConfigError = std.Build.PkgConfigError; | |
| 18 | const ExecError = std.Build.ExecError; | |
| 19 | const Pkg = std.Build.Pkg; | |
| 20 | const VcpkgRoot = std.Build.VcpkgRoot; | |
| 21 | const InstallDir = std.Build.InstallDir; | |
| 22 | const InstallArtifactStep = std.Build.InstallArtifactStep; | |
| 23 | const GeneratedFile = std.Build.GeneratedFile; | |
| 24 | const InstallRawStep = std.Build.InstallRawStep; | |
| 25 | const EmulatableRunStep = std.Build.EmulatableRunStep; | |
| 26 | const CheckObjectStep = std.Build.CheckObjectStep; | |
| 27 | const RunStep = std.Build.RunStep; | |
| 28 | const OptionsStep = std.Build.OptionsStep; | |
| 29 | const ConfigHeaderStep = std.Build.ConfigHeaderStep; | |
| 30 | const CompileStep = @This(); | |
| 31 | ||
| 32 | pub const base_id: Step.Id = .compile; | |
| 33 | ||
| 34 | step: Step, | |
| 35 | builder: *std.Build, | |
| 36 | name: []const u8, | |
| 37 | target: CrossTarget, | |
| 38 | target_info: NativeTargetInfo, | |
| 39 | optimize: std.builtin.Mode, | |
| 40 | linker_script: ?FileSource = null, | |
| 41 | version_script: ?[]const u8 = null, | |
| 42 | out_filename: []const u8, | |
| 43 | linkage: ?Linkage = null, | |
| 44 | version: ?std.builtin.Version, | |
| 45 | kind: Kind, | |
| 46 | major_only_filename: ?[]const u8, | |
| 47 | name_only_filename: ?[]const u8, | |
| 48 | strip: ?bool, | |
| 49 | unwind_tables: ?bool, | |
| 50 | // keep in sync with src/link.zig:CompressDebugSections | |
| 51 | compress_debug_sections: enum { none, zlib } = .none, | |
| 52 | lib_paths: ArrayList([]const u8), | |
| 53 | rpaths: ArrayList([]const u8), | |
| 54 | framework_dirs: ArrayList([]const u8), | |
| 55 | frameworks: StringHashMap(FrameworkLinkInfo), | |
| 56 | verbose_link: bool, | |
| 57 | verbose_cc: bool, | |
| 58 | emit_analysis: EmitOption = .default, | |
| 59 | emit_asm: EmitOption = .default, | |
| 60 | emit_bin: EmitOption = .default, | |
| 61 | emit_docs: EmitOption = .default, | |
| 62 | emit_implib: EmitOption = .default, | |
| 63 | emit_llvm_bc: EmitOption = .default, | |
| 64 | emit_llvm_ir: EmitOption = .default, | |
| 65 | // Lots of things depend on emit_h having a consistent path, | |
| 66 | // so it is not an EmitOption for now. | |
| 67 | emit_h: bool = false, | |
| 68 | bundle_compiler_rt: ?bool = null, | |
| 69 | single_threaded: ?bool = null, | |
| 70 | stack_protector: ?bool = null, | |
| 71 | disable_stack_probing: bool, | |
| 72 | disable_sanitize_c: bool, | |
| 73 | sanitize_thread: bool, | |
| 74 | rdynamic: bool, | |
| 75 | import_memory: bool = false, | |
| 76 | /// For WebAssembly targets, this will allow for undefined symbols to | |
| 77 | /// be imported from the host environment. | |
| 78 | import_symbols: bool = false, | |
| 79 | import_table: bool = false, | |
| 80 | export_table: bool = false, | |
| 81 | initial_memory: ?u64 = null, | |
| 82 | max_memory: ?u64 = null, | |
| 83 | shared_memory: bool = false, | |
| 84 | global_base: ?u64 = null, | |
| 85 | c_std: std.Build.CStd, | |
| 86 | override_lib_dir: ?[]const u8, | |
| 87 | main_pkg_path: ?[]const u8, | |
| 88 | exec_cmd_args: ?[]const ?[]const u8, | |
| 89 | name_prefix: []const u8, | |
| 90 | filter: ?[]const u8, | |
| 91 | test_evented_io: bool = false, | |
| 92 | test_runner: ?[]const u8, | |
| 93 | code_model: std.builtin.CodeModel = .default, | |
| 94 | wasi_exec_model: ?std.builtin.WasiExecModel = null, | |
| 95 | /// Symbols to be exported when compiling to wasm | |
| 96 | export_symbol_names: []const []const u8 = &.{}, | |
| 97 | ||
| 98 | root_src: ?FileSource, | |
| 99 | out_h_filename: []const u8, | |
| 100 | out_lib_filename: []const u8, | |
| 101 | out_pdb_filename: []const u8, | |
| 102 | packages: ArrayList(Pkg), | |
| 103 | ||
| 104 | object_src: []const u8, | |
| 105 | ||
| 106 | link_objects: ArrayList(LinkObject), | |
| 107 | include_dirs: ArrayList(IncludeDir), | |
| 108 | c_macros: ArrayList([]const u8), | |
| 109 | installed_headers: ArrayList(*Step), | |
| 110 | output_dir: ?[]const u8, | |
| 111 | is_linking_libc: bool = false, | |
| 112 | is_linking_libcpp: bool = false, | |
| 113 | vcpkg_bin_path: ?[]const u8 = null, | |
| 114 | ||
| 115 | /// This may be set in order to override the default install directory | |
| 116 | override_dest_dir: ?InstallDir, | |
| 117 | installed_path: ?[]const u8, | |
| 118 | install_step: ?*InstallArtifactStep, | |
| 119 | ||
| 120 | /// Base address for an executable image. | |
| 121 | image_base: ?u64 = null, | |
| 122 | ||
| 123 | libc_file: ?FileSource = null, | |
| 124 | ||
| 125 | valgrind_support: ?bool = null, | |
| 126 | each_lib_rpath: ?bool = null, | |
| 127 | /// On ELF targets, this will emit a link section called ".note.gnu.build-id" | |
| 128 | /// which can be used to coordinate a stripped binary with its debug symbols. | |
| 129 | /// As an example, the bloaty project refuses to work unless its inputs have | |
| 130 | /// build ids, in order to prevent accidental mismatches. | |
| 131 | /// The default is to not include this section because it slows down linking. | |
| 132 | build_id: ?bool = null, | |
| 133 | ||
| 134 | /// Create a .eh_frame_hdr section and a PT_GNU_EH_FRAME segment in the ELF | |
| 135 | /// file. | |
| 136 | link_eh_frame_hdr: bool = false, | |
| 137 | link_emit_relocs: bool = false, | |
| 138 | ||
| 139 | /// Place every function in its own section so that unused ones may be | |
| 140 | /// safely garbage-collected during the linking phase. | |
| 141 | link_function_sections: bool = false, | |
| 142 | ||
| 143 | /// Remove functions and data that are unreachable by the entry point or | |
| 144 | /// exported symbols. | |
| 145 | link_gc_sections: ?bool = null, | |
| 146 | ||
| 147 | linker_allow_shlib_undefined: ?bool = null, | |
| 148 | ||
| 149 | /// Permit read-only relocations in read-only segments. Disallowed by default. | |
| 150 | link_z_notext: bool = false, | |
| 151 | ||
| 152 | /// Force all relocations to be read-only after processing. | |
| 153 | link_z_relro: bool = true, | |
| 154 | ||
| 155 | /// Allow relocations to be lazily processed after load. | |
| 156 | link_z_lazy: bool = false, | |
| 157 | ||
| 158 | /// Common page size | |
| 159 | link_z_common_page_size: ?u64 = null, | |
| 160 | ||
| 161 | /// Maximum page size | |
| 162 | link_z_max_page_size: ?u64 = null, | |
| 163 | ||
| 164 | /// (Darwin) Install name for the dylib | |
| 165 | install_name: ?[]const u8 = null, | |
| 166 | ||
| 167 | /// (Darwin) Path to entitlements file | |
| 168 | entitlements: ?[]const u8 = null, | |
| 169 | ||
| 170 | /// (Darwin) Size of the pagezero segment. | |
| 171 | pagezero_size: ?u64 = null, | |
| 172 | ||
| 173 | /// (Darwin) Search strategy for searching system libraries. Either `paths_first` or `dylibs_first`. | |
| 174 | /// The former lowers to `-search_paths_first` linker option, while the latter to `-search_dylibs_first` | |
| 175 | /// option. | |
| 176 | /// By default, if no option is specified, the linker assumes `paths_first` as the default | |
| 177 | /// search strategy. | |
| 178 | search_strategy: ?enum { paths_first, dylibs_first } = null, | |
| 179 | ||
| 180 | /// (Darwin) Set size of the padding between the end of load commands | |
| 181 | /// and start of `__TEXT,__text` section. | |
| 182 | headerpad_size: ?u32 = null, | |
| 183 | ||
| 184 | /// (Darwin) Automatically Set size of the padding between the end of load commands | |
| 185 | /// and start of `__TEXT,__text` section to a value fitting all paths expanded to MAXPATHLEN. | |
| 186 | headerpad_max_install_names: bool = false, | |
| 187 | ||
| 188 | /// (Darwin) Remove dylibs that are unreachable by the entry point or exported symbols. | |
| 189 | dead_strip_dylibs: bool = false, | |
| 190 | ||
| 191 | /// Position Independent Code | |
| 192 | force_pic: ?bool = null, | |
| 193 | ||
| 194 | /// Position Independent Executable | |
| 195 | pie: ?bool = null, | |
| 196 | ||
| 197 | red_zone: ?bool = null, | |
| 198 | ||
| 199 | omit_frame_pointer: ?bool = null, | |
| 200 | dll_export_fns: ?bool = null, | |
| 201 | ||
| 202 | subsystem: ?std.Target.SubSystem = null, | |
| 203 | ||
| 204 | entry_symbol_name: ?[]const u8 = null, | |
| 205 | ||
| 206 | /// Overrides the default stack size | |
| 207 | stack_size: ?u64 = null, | |
| 208 | ||
| 209 | want_lto: ?bool = null, | |
| 210 | use_llvm: ?bool = null, | |
| 211 | use_lld: ?bool = null, | |
| 212 | ||
| 213 | output_path_source: GeneratedFile, | |
| 214 | output_lib_path_source: GeneratedFile, | |
| 215 | output_h_path_source: GeneratedFile, | |
| 216 | output_pdb_path_source: GeneratedFile, | |
| 217 | ||
| 218 | pub const CSourceFiles = struct { | |
| 219 | files: []const []const u8, | |
| 220 | flags: []const []const u8, | |
| 221 | }; | |
| 222 | ||
| 223 | pub const CSourceFile = struct { | |
| 224 | source: FileSource, | |
| 225 | args: []const []const u8, | |
| 226 | ||
| 227 | pub fn dupe(self: CSourceFile, b: *std.Build) CSourceFile { | |
| 228 | return .{ | |
| 229 | .source = self.source.dupe(b), | |
| 230 | .args = b.dupeStrings(self.args), | |
| 231 | }; | |
| 232 | } | |
| 233 | }; | |
| 234 | ||
| 235 | pub const LinkObject = union(enum) { | |
| 236 | static_path: FileSource, | |
| 237 | other_step: *CompileStep, | |
| 238 | system_lib: SystemLib, | |
| 239 | assembly_file: FileSource, | |
| 240 | c_source_file: *CSourceFile, | |
| 241 | c_source_files: *CSourceFiles, | |
| 242 | }; | |
| 243 | ||
| 244 | pub const SystemLib = struct { | |
| 245 | name: []const u8, | |
| 246 | needed: bool, | |
| 247 | weak: bool, | |
| 248 | use_pkg_config: enum { | |
| 249 | /// Don't use pkg-config, just pass -lfoo where foo is name. | |
| 250 | no, | |
| 251 | /// Try to get information on how to link the library from pkg-config. | |
| 252 | /// If that fails, fall back to passing -lfoo where foo is name. | |
| 253 | yes, | |
| 254 | /// Try to get information on how to link the library from pkg-config. | |
| 255 | /// If that fails, error out. | |
| 256 | force, | |
| 257 | }, | |
| 258 | }; | |
| 259 | ||
| 260 | const FrameworkLinkInfo = struct { | |
| 261 | needed: bool = false, | |
| 262 | weak: bool = false, | |
| 263 | }; | |
| 264 | ||
| 265 | pub const IncludeDir = union(enum) { | |
| 266 | raw_path: []const u8, | |
| 267 | raw_path_system: []const u8, | |
| 268 | other_step: *CompileStep, | |
| 269 | config_header_step: *ConfigHeaderStep, | |
| 270 | }; | |
| 271 | ||
| 272 | pub const Options = struct { | |
| 273 | name: []const u8, | |
| 274 | root_source_file: ?FileSource = null, | |
| 275 | target: CrossTarget, | |
| 276 | optimize: std.builtin.Mode, | |
| 277 | kind: Kind, | |
| 278 | linkage: ?Linkage = null, | |
| 279 | version: ?std.builtin.Version = null, | |
| 280 | }; | |
| 281 | ||
| 282 | pub const Kind = enum { | |
| 283 | exe, | |
| 284 | lib, | |
| 285 | obj, | |
| 286 | @"test", | |
| 287 | test_exe, | |
| 288 | }; | |
| 289 | ||
| 290 | pub const Linkage = enum { dynamic, static }; | |
| 291 | ||
| 292 | pub const EmitOption = union(enum) { | |
| 293 | default: void, | |
| 294 | no_emit: void, | |
| 295 | emit: void, | |
| 296 | emit_to: []const u8, | |
| 297 | ||
| 298 | fn getArg(self: @This(), b: *std.Build, arg_name: []const u8) ?[]const u8 { | |
| 299 | return switch (self) { | |
| 300 | .no_emit => b.fmt("-fno-{s}", .{arg_name}), | |
| 301 | .default => null, | |
| 302 | .emit => b.fmt("-f{s}", .{arg_name}), | |
| 303 | .emit_to => |path| b.fmt("-f{s}={s}", .{ arg_name, path }), | |
| 304 | }; | |
| 305 | } | |
| 306 | }; | |
| 307 | ||
| 308 | pub fn create(builder: *std.Build, options: Options) *CompileStep { | |
| 309 | const name = builder.dupe(options.name); | |
| 310 | const root_src: ?FileSource = if (options.root_source_file) |rsrc| rsrc.dupe(builder) else null; | |
| 311 | if (mem.indexOf(u8, name, "/") != null or mem.indexOf(u8, name, "\\") != null) { | |
| 312 | panic("invalid name: '{s}'. It looks like a file path, but it is supposed to be the library or application name.", .{name}); | |
| 313 | } | |
| 314 | ||
| 315 | const self = builder.allocator.create(CompileStep) catch @panic("OOM"); | |
| 316 | self.* = CompileStep{ | |
| 317 | .strip = null, | |
| 318 | .unwind_tables = null, | |
| 319 | .builder = builder, | |
| 320 | .verbose_link = false, | |
| 321 | .verbose_cc = false, | |
| 322 | .optimize = options.optimize, | |
| 323 | .target = options.target, | |
| 324 | .linkage = options.linkage, | |
| 325 | .kind = options.kind, | |
| 326 | .root_src = root_src, | |
| 327 | .name = name, | |
| 328 | .frameworks = StringHashMap(FrameworkLinkInfo).init(builder.allocator), | |
| 329 | .step = Step.init(base_id, name, builder.allocator, make), | |
| 330 | .version = options.version, | |
| 331 | .out_filename = undefined, | |
| 332 | .out_h_filename = builder.fmt("{s}.h", .{name}), | |
| 333 | .out_lib_filename = undefined, | |
| 334 | .out_pdb_filename = builder.fmt("{s}.pdb", .{name}), | |
| 335 | .major_only_filename = null, | |
| 336 | .name_only_filename = null, | |
| 337 | .packages = ArrayList(Pkg).init(builder.allocator), | |
| 338 | .include_dirs = ArrayList(IncludeDir).init(builder.allocator), | |
| 339 | .link_objects = ArrayList(LinkObject).init(builder.allocator), | |
| 340 | .c_macros = ArrayList([]const u8).init(builder.allocator), | |
| 341 | .lib_paths = ArrayList([]const u8).init(builder.allocator), | |
| 342 | .rpaths = ArrayList([]const u8).init(builder.allocator), | |
| 343 | .framework_dirs = ArrayList([]const u8).init(builder.allocator), | |
| 344 | .installed_headers = ArrayList(*Step).init(builder.allocator), | |
| 345 | .object_src = undefined, | |
| 346 | .c_std = std.Build.CStd.C99, | |
| 347 | .override_lib_dir = null, | |
| 348 | .main_pkg_path = null, | |
| 349 | .exec_cmd_args = null, | |
| 350 | .name_prefix = "", | |
| 351 | .filter = null, | |
| 352 | .test_runner = null, | |
| 353 | .disable_stack_probing = false, | |
| 354 | .disable_sanitize_c = false, | |
| 355 | .sanitize_thread = false, | |
| 356 | .rdynamic = false, | |
| 357 | .output_dir = null, | |
| 358 | .override_dest_dir = null, | |
| 359 | .installed_path = null, | |
| 360 | .install_step = null, | |
| 361 | ||
| 362 | .output_path_source = GeneratedFile{ .step = &self.step }, | |
| 363 | .output_lib_path_source = GeneratedFile{ .step = &self.step }, | |
| 364 | .output_h_path_source = GeneratedFile{ .step = &self.step }, | |
| 365 | .output_pdb_path_source = GeneratedFile{ .step = &self.step }, | |
| 366 | ||
| 367 | .target_info = NativeTargetInfo.detect(self.target) catch @panic("unhandled error"), | |
| 368 | }; | |
| 369 | self.computeOutFileNames(); | |
| 370 | if (root_src) |rs| rs.addStepDependencies(&self.step); | |
| 371 | return self; | |
| 372 | } | |
| 373 | ||
| 374 | fn computeOutFileNames(self: *CompileStep) void { | |
| 375 | const target = self.target_info.target; | |
| 376 | ||
| 377 | self.out_filename = std.zig.binNameAlloc(self.builder.allocator, .{ | |
| 378 | .root_name = self.name, | |
| 379 | .target = target, | |
| 380 | .output_mode = switch (self.kind) { | |
| 381 | .lib => .Lib, | |
| 382 | .obj => .Obj, | |
| 383 | .exe, .@"test", .test_exe => .Exe, | |
| 384 | }, | |
| 385 | .link_mode = if (self.linkage) |some| @as(std.builtin.LinkMode, switch (some) { | |
| 386 | .dynamic => .Dynamic, | |
| 387 | .static => .Static, | |
| 388 | }) else null, | |
| 389 | .version = self.version, | |
| 390 | }) catch @panic("OOM"); | |
| 391 | ||
| 392 | if (self.kind == .lib) { | |
| 393 | if (self.linkage != null and self.linkage.? == .static) { | |
| 394 | self.out_lib_filename = self.out_filename; | |
| 395 | } else if (self.version) |version| { | |
| 396 | if (target.isDarwin()) { | |
| 397 | self.major_only_filename = self.builder.fmt("lib{s}.{d}.dylib", .{ | |
| 398 | self.name, | |
| 399 | version.major, | |
| 400 | }); | |
| 401 | self.name_only_filename = self.builder.fmt("lib{s}.dylib", .{self.name}); | |
| 402 | self.out_lib_filename = self.out_filename; | |
| 403 | } else if (target.os.tag == .windows) { | |
| 404 | self.out_lib_filename = self.builder.fmt("{s}.lib", .{self.name}); | |
| 405 | } else { | |
| 406 | self.major_only_filename = self.builder.fmt("lib{s}.so.{d}", .{ self.name, version.major }); | |
| 407 | self.name_only_filename = self.builder.fmt("lib{s}.so", .{self.name}); | |
| 408 | self.out_lib_filename = self.out_filename; | |
| 409 | } | |
| 410 | } else { | |
| 411 | if (target.isDarwin()) { | |
| 412 | self.out_lib_filename = self.out_filename; | |
| 413 | } else if (target.os.tag == .windows) { | |
| 414 | self.out_lib_filename = self.builder.fmt("{s}.lib", .{self.name}); | |
| 415 | } else { | |
| 416 | self.out_lib_filename = self.out_filename; | |
| 417 | } | |
| 418 | } | |
| 419 | if (self.output_dir != null) { | |
| 420 | self.output_lib_path_source.path = self.builder.pathJoin( | |
| 421 | &.{ self.output_dir.?, self.out_lib_filename }, | |
| 422 | ); | |
| 423 | } | |
| 424 | } | |
| 425 | } | |
| 426 | ||
| 427 | pub fn setOutputDir(self: *CompileStep, dir: []const u8) void { | |
| 428 | self.output_dir = self.builder.dupePath(dir); | |
| 429 | } | |
| 430 | ||
| 431 | pub fn install(self: *CompileStep) void { | |
| 432 | self.builder.installArtifact(self); | |
| 433 | } | |
| 434 | ||
| 435 | pub fn installRaw(self: *CompileStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep { | |
| 436 | return self.builder.installRaw(self, dest_filename, options); | |
| 437 | } | |
| 438 | ||
| 439 | pub fn installHeader(a: *CompileStep, src_path: []const u8, dest_rel_path: []const u8) void { | |
| 440 | const install_file = a.builder.addInstallHeaderFile(src_path, dest_rel_path); | |
| 441 | a.builder.getInstallStep().dependOn(&install_file.step); | |
| 442 | a.installed_headers.append(&install_file.step) catch @panic("OOM"); | |
| 443 | } | |
| 444 | ||
| 445 | pub fn installHeadersDirectory( | |
| 446 | a: *CompileStep, | |
| 447 | src_dir_path: []const u8, | |
| 448 | dest_rel_path: []const u8, | |
| 449 | ) void { | |
| 450 | return installHeadersDirectoryOptions(a, .{ | |
| 451 | .source_dir = src_dir_path, | |
| 452 | .install_dir = .header, | |
| 453 | .install_subdir = dest_rel_path, | |
| 454 | }); | |
| 455 | } | |
| 456 | ||
| 457 | pub fn installHeadersDirectoryOptions( | |
| 458 | a: *CompileStep, | |
| 459 | options: std.Build.InstallDirStep.Options, | |
| 460 | ) void { | |
| 461 | const install_dir = a.builder.addInstallDirectory(options); | |
| 462 | a.builder.getInstallStep().dependOn(&install_dir.step); | |
| 463 | a.installed_headers.append(&install_dir.step) catch @panic("OOM"); | |
| 464 | } | |
| 465 | ||
| 466 | pub fn installLibraryHeaders(a: *CompileStep, l: *CompileStep) void { | |
| 467 | assert(l.kind == .lib); | |
| 468 | const install_step = a.builder.getInstallStep(); | |
| 469 | // Copy each element from installed_headers, modifying the builder | |
| 470 | // to be the new parent's builder. | |
| 471 | for (l.installed_headers.items) |step| { | |
| 472 | const step_copy = switch (step.id) { | |
| 473 | inline .install_file, .install_dir => |id| blk: { | |
| 474 | const T = id.Type(); | |
| 475 | const ptr = a.builder.allocator.create(T) catch @panic("OOM"); | |
| 476 | ptr.* = step.cast(T).?.*; | |
| 477 | ptr.override_source_builder = ptr.builder; | |
| 478 | ptr.builder = a.builder; | |
| 479 | break :blk &ptr.step; | |
| 480 | }, | |
| 481 | else => unreachable, | |
| 482 | }; | |
| 483 | a.installed_headers.append(step_copy) catch @panic("OOM"); | |
| 484 | install_step.dependOn(step_copy); | |
| 485 | } | |
| 486 | a.installed_headers.appendSlice(l.installed_headers.items) catch @panic("OOM"); | |
| 487 | } | |
| 488 | ||
| 489 | /// Creates a `RunStep` with an executable built with `addExecutable`. | |
| 490 | /// Add command line arguments with `addArg`. | |
| 491 | pub fn run(exe: *CompileStep) *RunStep { | |
| 492 | assert(exe.kind == .exe or exe.kind == .test_exe); | |
| 493 | ||
| 494 | // It doesn't have to be native. We catch that if you actually try to run it. | |
| 495 | // Consider that this is declarative; the run step may not be run unless a user | |
| 496 | // option is supplied. | |
| 497 | const run_step = RunStep.create(exe.builder, exe.builder.fmt("run {s}", .{exe.step.name})); | |
| 498 | run_step.addArtifactArg(exe); | |
| 499 | ||
| 500 | if (exe.kind == .test_exe) { | |
| 501 | run_step.addArg(exe.builder.zig_exe); | |
| 502 | } | |
| 503 | ||
| 504 | if (exe.vcpkg_bin_path) |path| { | |
| 505 | run_step.addPathDir(path); | |
| 506 | } | |
| 507 | ||
| 508 | return run_step; | |
| 509 | } | |
| 510 | ||
| 511 | /// Creates an `EmulatableRunStep` with an executable built with `addExecutable`. | |
| 512 | /// Allows running foreign binaries through emulation platforms such as Qemu or Rosetta. | |
| 513 | /// When a binary cannot be ran through emulation or the option is disabled, a warning | |
| 514 | /// will be printed and the binary will *NOT* be ran. | |
| 515 | pub fn runEmulatable(exe: *CompileStep) *EmulatableRunStep { | |
| 516 | assert(exe.kind == .exe or exe.kind == .test_exe); | |
| 517 | ||
| 518 | const run_step = EmulatableRunStep.create(exe.builder, exe.builder.fmt("run {s}", .{exe.step.name}), exe); | |
| 519 | if (exe.vcpkg_bin_path) |path| { | |
| 520 | RunStep.addPathDirInternal(&run_step.step, exe.builder, path); | |
| 521 | } | |
| 522 | return run_step; | |
| 523 | } | |
| 524 | ||
| 525 | pub fn checkObject(self: *CompileStep, obj_format: std.Target.ObjectFormat) *CheckObjectStep { | |
| 526 | return CheckObjectStep.create(self.builder, self.getOutputSource(), obj_format); | |
| 527 | } | |
| 528 | ||
| 529 | pub fn setLinkerScriptPath(self: *CompileStep, source: FileSource) void { | |
| 530 | self.linker_script = source.dupe(self.builder); | |
| 531 | source.addStepDependencies(&self.step); | |
| 532 | } | |
| 533 | ||
| 534 | pub fn linkFramework(self: *CompileStep, framework_name: []const u8) void { | |
| 535 | self.frameworks.put(self.builder.dupe(framework_name), .{}) catch @panic("OOM"); | |
| 536 | } | |
| 537 | ||
| 538 | pub fn linkFrameworkNeeded(self: *CompileStep, framework_name: []const u8) void { | |
| 539 | self.frameworks.put(self.builder.dupe(framework_name), .{ | |
| 540 | .needed = true, | |
| 541 | }) catch @panic("OOM"); | |
| 542 | } | |
| 543 | ||
| 544 | pub fn linkFrameworkWeak(self: *CompileStep, framework_name: []const u8) void { | |
| 545 | self.frameworks.put(self.builder.dupe(framework_name), .{ | |
| 546 | .weak = true, | |
| 547 | }) catch @panic("OOM"); | |
| 548 | } | |
| 549 | ||
| 550 | /// Returns whether the library, executable, or object depends on a particular system library. | |
| 551 | pub fn dependsOnSystemLibrary(self: CompileStep, name: []const u8) bool { | |
| 552 | if (isLibCLibrary(name)) { | |
| 553 | return self.is_linking_libc; | |
| 554 | } | |
| 555 | if (isLibCppLibrary(name)) { | |
| 556 | return self.is_linking_libcpp; | |
| 557 | } | |
| 558 | for (self.link_objects.items) |link_object| { | |
| 559 | switch (link_object) { | |
| 560 | .system_lib => |lib| if (mem.eql(u8, lib.name, name)) return true, | |
| 561 | else => continue, | |
| 562 | } | |
| 563 | } | |
| 564 | return false; | |
| 565 | } | |
| 566 | ||
| 567 | pub fn linkLibrary(self: *CompileStep, lib: *CompileStep) void { | |
| 568 | assert(lib.kind == .lib); | |
| 569 | self.linkLibraryOrObject(lib); | |
| 570 | } | |
| 571 | ||
| 572 | pub fn isDynamicLibrary(self: *CompileStep) bool { | |
| 573 | return self.kind == .lib and self.linkage == Linkage.dynamic; | |
| 574 | } | |
| 575 | ||
| 576 | pub fn isStaticLibrary(self: *CompileStep) bool { | |
| 577 | return self.kind == .lib and self.linkage != Linkage.dynamic; | |
| 578 | } | |
| 579 | ||
| 580 | pub fn producesPdbFile(self: *CompileStep) bool { | |
| 581 | if (!self.target.isWindows() and !self.target.isUefi()) return false; | |
| 582 | if (self.target.getObjectFormat() == .c) return false; | |
| 583 | if (self.strip == true) return false; | |
| 584 | return self.isDynamicLibrary() or self.kind == .exe or self.kind == .test_exe; | |
| 585 | } | |
| 586 | ||
| 587 | pub fn linkLibC(self: *CompileStep) void { | |
| 588 | self.is_linking_libc = true; | |
| 589 | } | |
| 590 | ||
| 591 | pub fn linkLibCpp(self: *CompileStep) void { | |
| 592 | self.is_linking_libcpp = true; | |
| 593 | } | |
| 594 | ||
| 595 | /// If the value is omitted, it is set to 1. | |
| 596 | /// `name` and `value` need not live longer than the function call. | |
| 597 | pub fn defineCMacro(self: *CompileStep, name: []const u8, value: ?[]const u8) void { | |
| 598 | const macro = std.Build.constructCMacro(self.builder.allocator, name, value); | |
| 599 | self.c_macros.append(macro) catch @panic("OOM"); | |
| 600 | } | |
| 601 | ||
| 602 | /// name_and_value looks like [name]=[value]. If the value is omitted, it is set to 1. | |
| 603 | pub fn defineCMacroRaw(self: *CompileStep, name_and_value: []const u8) void { | |
| 604 | self.c_macros.append(self.builder.dupe(name_and_value)) catch @panic("OOM"); | |
| 605 | } | |
| 606 | ||
| 607 | /// This one has no integration with anything, it just puts -lname on the command line. | |
| 608 | /// Prefer to use `linkSystemLibrary` instead. | |
| 609 | pub fn linkSystemLibraryName(self: *CompileStep, name: []const u8) void { | |
| 610 | self.link_objects.append(.{ | |
| 611 | .system_lib = .{ | |
| 612 | .name = self.builder.dupe(name), | |
| 613 | .needed = false, | |
| 614 | .weak = false, | |
| 615 | .use_pkg_config = .no, | |
| 616 | }, | |
| 617 | }) catch @panic("OOM"); | |
| 618 | } | |
| 619 | ||
| 620 | /// This one has no integration with anything, it just puts -needed-lname on the command line. | |
| 621 | /// Prefer to use `linkSystemLibraryNeeded` instead. | |
| 622 | pub fn linkSystemLibraryNeededName(self: *CompileStep, name: []const u8) void { | |
| 623 | self.link_objects.append(.{ | |
| 624 | .system_lib = .{ | |
| 625 | .name = self.builder.dupe(name), | |
| 626 | .needed = true, | |
| 627 | .weak = false, | |
| 628 | .use_pkg_config = .no, | |
| 629 | }, | |
| 630 | }) catch @panic("OOM"); | |
| 631 | } | |
| 632 | ||
| 633 | /// Darwin-only. This one has no integration with anything, it just puts -weak-lname on the | |
| 634 | /// command line. Prefer to use `linkSystemLibraryWeak` instead. | |
| 635 | pub fn linkSystemLibraryWeakName(self: *CompileStep, name: []const u8) void { | |
| 636 | self.link_objects.append(.{ | |
| 637 | .system_lib = .{ | |
| 638 | .name = self.builder.dupe(name), | |
| 639 | .needed = false, | |
| 640 | .weak = true, | |
| 641 | .use_pkg_config = .no, | |
| 642 | }, | |
| 643 | }) catch @panic("OOM"); | |
| 644 | } | |
| 645 | ||
| 646 | /// This links against a system library, exclusively using pkg-config to find the library. | |
| 647 | /// Prefer to use `linkSystemLibrary` instead. | |
| 648 | pub fn linkSystemLibraryPkgConfigOnly(self: *CompileStep, lib_name: []const u8) void { | |
| 649 | self.link_objects.append(.{ | |
| 650 | .system_lib = .{ | |
| 651 | .name = self.builder.dupe(lib_name), | |
| 652 | .needed = false, | |
| 653 | .weak = false, | |
| 654 | .use_pkg_config = .force, | |
| 655 | }, | |
| 656 | }) catch @panic("OOM"); | |
| 657 | } | |
| 658 | ||
| 659 | /// This links against a system library, exclusively using pkg-config to find the library. | |
| 660 | /// Prefer to use `linkSystemLibraryNeeded` instead. | |
| 661 | pub fn linkSystemLibraryNeededPkgConfigOnly(self: *CompileStep, lib_name: []const u8) void { | |
| 662 | self.link_objects.append(.{ | |
| 663 | .system_lib = .{ | |
| 664 | .name = self.builder.dupe(lib_name), | |
| 665 | .needed = true, | |
| 666 | .weak = false, | |
| 667 | .use_pkg_config = .force, | |
| 668 | }, | |
| 669 | }) catch @panic("OOM"); | |
| 670 | } | |
| 671 | ||
| 672 | /// Run pkg-config for the given library name and parse the output, returning the arguments | |
| 673 | /// that should be passed to zig to link the given library. | |
| 674 | pub fn runPkgConfig(self: *CompileStep, lib_name: []const u8) ![]const []const u8 { | |
| 675 | const pkg_name = match: { | |
| 676 | // First we have to map the library name to pkg config name. Unfortunately, | |
| 677 | // there are several examples where this is not straightforward: | |
| 678 | // -lSDL2 -> pkg-config sdl2 | |
| 679 | // -lgdk-3 -> pkg-config gdk-3.0 | |
| 680 | // -latk-1.0 -> pkg-config atk | |
| 681 | const pkgs = try getPkgConfigList(self.builder); | |
| 682 | ||
| 683 | // Exact match means instant winner. | |
| 684 | for (pkgs) |pkg| { | |
| 685 | if (mem.eql(u8, pkg.name, lib_name)) { | |
| 686 | break :match pkg.name; | |
| 687 | } | |
| 688 | } | |
| 689 | ||
| 690 | // Next we'll try ignoring case. | |
| 691 | for (pkgs) |pkg| { | |
| 692 | if (std.ascii.eqlIgnoreCase(pkg.name, lib_name)) { | |
| 693 | break :match pkg.name; | |
| 694 | } | |
| 695 | } | |
| 696 | ||
| 697 | // Now try appending ".0". | |
| 698 | for (pkgs) |pkg| { | |
| 699 | if (std.ascii.indexOfIgnoreCase(pkg.name, lib_name)) |pos| { | |
| 700 | if (pos != 0) continue; | |
| 701 | if (mem.eql(u8, pkg.name[lib_name.len..], ".0")) { | |
| 702 | break :match pkg.name; | |
| 703 | } | |
| 704 | } | |
| 705 | } | |
| 706 | ||
| 707 | // Trimming "-1.0". | |
| 708 | if (mem.endsWith(u8, lib_name, "-1.0")) { | |
| 709 | const trimmed_lib_name = lib_name[0 .. lib_name.len - "-1.0".len]; | |
| 710 | for (pkgs) |pkg| { | |
| 711 | if (std.ascii.eqlIgnoreCase(pkg.name, trimmed_lib_name)) { | |
| 712 | break :match pkg.name; | |
| 713 | } | |
| 714 | } | |
| 715 | } | |
| 716 | ||
| 717 | return error.PackageNotFound; | |
| 718 | }; | |
| 719 | ||
| 720 | var code: u8 = undefined; | |
| 721 | const stdout = if (self.builder.execAllowFail(&[_][]const u8{ | |
| 722 | "pkg-config", | |
| 723 | pkg_name, | |
| 724 | "--cflags", | |
| 725 | "--libs", | |
| 726 | }, &code, .Ignore)) |stdout| stdout else |err| switch (err) { | |
| 727 | error.ProcessTerminated => return error.PkgConfigCrashed, | |
| 728 | error.ExecNotSupported => return error.PkgConfigFailed, | |
| 729 | error.ExitCodeFailure => return error.PkgConfigFailed, | |
| 730 | error.FileNotFound => return error.PkgConfigNotInstalled, | |
| 731 | error.ChildExecFailed => return error.PkgConfigFailed, | |
| 732 | else => return err, | |
| 733 | }; | |
| 734 | ||
| 735 | var zig_args = ArrayList([]const u8).init(self.builder.allocator); | |
| 736 | defer zig_args.deinit(); | |
| 737 | ||
| 738 | var it = mem.tokenize(u8, stdout, " \r\n\t"); | |
| 739 | while (it.next()) |tok| { | |
| 740 | if (mem.eql(u8, tok, "-I")) { | |
| 741 | const dir = it.next() orelse return error.PkgConfigInvalidOutput; | |
| 742 | try zig_args.appendSlice(&[_][]const u8{ "-I", dir }); | |
| 743 | } else if (mem.startsWith(u8, tok, "-I")) { | |
| 744 | try zig_args.append(tok); | |
| 745 | } else if (mem.eql(u8, tok, "-L")) { | |
| 746 | const dir = it.next() orelse return error.PkgConfigInvalidOutput; | |
| 747 | try zig_args.appendSlice(&[_][]const u8{ "-L", dir }); | |
| 748 | } else if (mem.startsWith(u8, tok, "-L")) { | |
| 749 | try zig_args.append(tok); | |
| 750 | } else if (mem.eql(u8, tok, "-l")) { | |
| 751 | const lib = it.next() orelse return error.PkgConfigInvalidOutput; | |
| 752 | try zig_args.appendSlice(&[_][]const u8{ "-l", lib }); | |
| 753 | } else if (mem.startsWith(u8, tok, "-l")) { | |
| 754 | try zig_args.append(tok); | |
| 755 | } else if (mem.eql(u8, tok, "-D")) { | |
| 756 | const macro = it.next() orelse return error.PkgConfigInvalidOutput; | |
| 757 | try zig_args.appendSlice(&[_][]const u8{ "-D", macro }); | |
| 758 | } else if (mem.startsWith(u8, tok, "-D")) { | |
| 759 | try zig_args.append(tok); | |
| 760 | } else if (self.builder.verbose) { | |
| 761 | log.warn("Ignoring pkg-config flag '{s}'", .{tok}); | |
| 762 | } | |
| 763 | } | |
| 764 | ||
| 765 | return zig_args.toOwnedSlice(); | |
| 766 | } | |
| 767 | ||
| 768 | pub fn linkSystemLibrary(self: *CompileStep, name: []const u8) void { | |
| 769 | self.linkSystemLibraryInner(name, .{}); | |
| 770 | } | |
| 771 | ||
| 772 | pub fn linkSystemLibraryNeeded(self: *CompileStep, name: []const u8) void { | |
| 773 | self.linkSystemLibraryInner(name, .{ .needed = true }); | |
| 774 | } | |
| 775 | ||
| 776 | pub fn linkSystemLibraryWeak(self: *CompileStep, name: []const u8) void { | |
| 777 | self.linkSystemLibraryInner(name, .{ .weak = true }); | |
| 778 | } | |
| 779 | ||
| 780 | fn linkSystemLibraryInner(self: *CompileStep, name: []const u8, opts: struct { | |
| 781 | needed: bool = false, | |
| 782 | weak: bool = false, | |
| 783 | }) void { | |
| 784 | if (isLibCLibrary(name)) { | |
| 785 | self.linkLibC(); | |
| 786 | return; | |
| 787 | } | |
| 788 | if (isLibCppLibrary(name)) { | |
| 789 | self.linkLibCpp(); | |
| 790 | return; | |
| 791 | } | |
| 792 | ||
| 793 | self.link_objects.append(.{ | |
| 794 | .system_lib = .{ | |
| 795 | .name = self.builder.dupe(name), | |
| 796 | .needed = opts.needed, | |
| 797 | .weak = opts.weak, | |
| 798 | .use_pkg_config = .yes, | |
| 799 | }, | |
| 800 | }) catch @panic("OOM"); | |
| 801 | } | |
| 802 | ||
| 803 | pub fn setNamePrefix(self: *CompileStep, text: []const u8) void { | |
| 804 | assert(self.kind == .@"test" or self.kind == .test_exe); | |
| 805 | self.name_prefix = self.builder.dupe(text); | |
| 806 | } | |
| 807 | ||
| 808 | pub fn setFilter(self: *CompileStep, text: ?[]const u8) void { | |
| 809 | assert(self.kind == .@"test" or self.kind == .test_exe); | |
| 810 | self.filter = if (text) |t| self.builder.dupe(t) else null; | |
| 811 | } | |
| 812 | ||
| 813 | pub fn setTestRunner(self: *CompileStep, path: ?[]const u8) void { | |
| 814 | assert(self.kind == .@"test" or self.kind == .test_exe); | |
| 815 | self.test_runner = if (path) |p| self.builder.dupePath(p) else null; | |
| 816 | } | |
| 817 | ||
| 818 | /// Handy when you have many C/C++ source files and want them all to have the same flags. | |
| 819 | pub fn addCSourceFiles(self: *CompileStep, files: []const []const u8, flags: []const []const u8) void { | |
| 820 | const c_source_files = self.builder.allocator.create(CSourceFiles) catch @panic("OOM"); | |
| 821 | ||
| 822 | const files_copy = self.builder.dupeStrings(files); | |
| 823 | const flags_copy = self.builder.dupeStrings(flags); | |
| 824 | ||
| 825 | c_source_files.* = .{ | |
| 826 | .files = files_copy, | |
| 827 | .flags = flags_copy, | |
| 828 | }; | |
| 829 | self.link_objects.append(.{ .c_source_files = c_source_files }) catch @panic("OOM"); | |
| 830 | } | |
| 831 | ||
| 832 | pub fn addCSourceFile(self: *CompileStep, file: []const u8, flags: []const []const u8) void { | |
| 833 | self.addCSourceFileSource(.{ | |
| 834 | .args = flags, | |
| 835 | .source = .{ .path = file }, | |
| 836 | }); | |
| 837 | } | |
| 838 | ||
| 839 | pub fn addCSourceFileSource(self: *CompileStep, source: CSourceFile) void { | |
| 840 | const c_source_file = self.builder.allocator.create(CSourceFile) catch @panic("OOM"); | |
| 841 | c_source_file.* = source.dupe(self.builder); | |
| 842 | self.link_objects.append(.{ .c_source_file = c_source_file }) catch @panic("OOM"); | |
| 843 | source.source.addStepDependencies(&self.step); | |
| 844 | } | |
| 845 | ||
| 846 | pub fn setVerboseLink(self: *CompileStep, value: bool) void { | |
| 847 | self.verbose_link = value; | |
| 848 | } | |
| 849 | ||
| 850 | pub fn setVerboseCC(self: *CompileStep, value: bool) void { | |
| 851 | self.verbose_cc = value; | |
| 852 | } | |
| 853 | ||
| 854 | pub fn overrideZigLibDir(self: *CompileStep, dir_path: []const u8) void { | |
| 855 | self.override_lib_dir = self.builder.dupePath(dir_path); | |
| 856 | } | |
| 857 | ||
| 858 | pub fn setMainPkgPath(self: *CompileStep, dir_path: []const u8) void { | |
| 859 | self.main_pkg_path = self.builder.dupePath(dir_path); | |
| 860 | } | |
| 861 | ||
| 862 | pub fn setLibCFile(self: *CompileStep, libc_file: ?FileSource) void { | |
| 863 | self.libc_file = if (libc_file) |f| f.dupe(self.builder) else null; | |
| 864 | } | |
| 865 | ||
| 866 | /// Returns the generated executable, library or object file. | |
| 867 | /// To run an executable built with zig build, use `run`, or create an install step and invoke it. | |
| 868 | pub fn getOutputSource(self: *CompileStep) FileSource { | |
| 869 | return FileSource{ .generated = &self.output_path_source }; | |
| 870 | } | |
| 871 | ||
| 872 | /// Returns the generated import library. This function can only be called for libraries. | |
| 873 | pub fn getOutputLibSource(self: *CompileStep) FileSource { | |
| 874 | assert(self.kind == .lib); | |
| 875 | return FileSource{ .generated = &self.output_lib_path_source }; | |
| 876 | } | |
| 877 | ||
| 878 | /// Returns the generated header file. | |
| 879 | /// This function can only be called for libraries or object files which have `emit_h` set. | |
| 880 | pub fn getOutputHSource(self: *CompileStep) FileSource { | |
| 881 | assert(self.kind != .exe and self.kind != .test_exe and self.kind != .@"test"); | |
| 882 | assert(self.emit_h); | |
| 883 | return FileSource{ .generated = &self.output_h_path_source }; | |
| 884 | } | |
| 885 | ||
| 886 | /// Returns the generated PDB file. This function can only be called for Windows and UEFI. | |
| 887 | pub fn getOutputPdbSource(self: *CompileStep) FileSource { | |
| 888 | // TODO: Is this right? Isn't PDB for *any* PE/COFF file? | |
| 889 | assert(self.target.isWindows() or self.target.isUefi()); | |
| 890 | return FileSource{ .generated = &self.output_pdb_path_source }; | |
| 891 | } | |
| 892 | ||
| 893 | pub fn addAssemblyFile(self: *CompileStep, path: []const u8) void { | |
| 894 | self.link_objects.append(.{ | |
| 895 | .assembly_file = .{ .path = self.builder.dupe(path) }, | |
| 896 | }) catch @panic("OOM"); | |
| 897 | } | |
| 898 | ||
| 899 | pub fn addAssemblyFileSource(self: *CompileStep, source: FileSource) void { | |
| 900 | const source_duped = source.dupe(self.builder); | |
| 901 | self.link_objects.append(.{ .assembly_file = source_duped }) catch @panic("OOM"); | |
| 902 | source_duped.addStepDependencies(&self.step); | |
| 903 | } | |
| 904 | ||
| 905 | pub fn addObjectFile(self: *CompileStep, source_file: []const u8) void { | |
| 906 | self.addObjectFileSource(.{ .path = source_file }); | |
| 907 | } | |
| 908 | ||
| 909 | pub fn addObjectFileSource(self: *CompileStep, source: FileSource) void { | |
| 910 | self.link_objects.append(.{ .static_path = source.dupe(self.builder) }) catch @panic("OOM"); | |
| 911 | source.addStepDependencies(&self.step); | |
| 912 | } | |
| 913 | ||
| 914 | pub fn addObject(self: *CompileStep, obj: *CompileStep) void { | |
| 915 | assert(obj.kind == .obj); | |
| 916 | self.linkLibraryOrObject(obj); | |
| 917 | } | |
| 918 | ||
| 919 | pub const addSystemIncludeDir = @compileError("deprecated; use addSystemIncludePath"); | |
| 920 | pub const addIncludeDir = @compileError("deprecated; use addIncludePath"); | |
| 921 | pub const addLibPath = @compileError("deprecated, use addLibraryPath"); | |
| 922 | pub const addFrameworkDir = @compileError("deprecated, use addFrameworkPath"); | |
| 923 | ||
| 924 | pub fn addSystemIncludePath(self: *CompileStep, path: []const u8) void { | |
| 925 | self.include_dirs.append(IncludeDir{ .raw_path_system = self.builder.dupe(path) }) catch @panic("OOM"); | |
| 926 | } | |
| 927 | ||
| 928 | pub fn addIncludePath(self: *CompileStep, path: []const u8) void { | |
| 929 | self.include_dirs.append(IncludeDir{ .raw_path = self.builder.dupe(path) }) catch @panic("OOM"); | |
| 930 | } | |
| 931 | ||
| 932 | pub fn addConfigHeader(self: *CompileStep, config_header: *ConfigHeaderStep) void { | |
| 933 | self.step.dependOn(&config_header.step); | |
| 934 | self.include_dirs.append(.{ .config_header_step = config_header }) catch @panic("OOM"); | |
| 935 | } | |
| 936 | ||
| 937 | pub fn addLibraryPath(self: *CompileStep, path: []const u8) void { | |
| 938 | self.lib_paths.append(self.builder.dupe(path)) catch @panic("OOM"); | |
| 939 | } | |
| 940 | ||
| 941 | pub fn addRPath(self: *CompileStep, path: []const u8) void { | |
| 942 | self.rpaths.append(self.builder.dupe(path)) catch @panic("OOM"); | |
| 943 | } | |
| 944 | ||
| 945 | pub fn addFrameworkPath(self: *CompileStep, dir_path: []const u8) void { | |
| 946 | self.framework_dirs.append(self.builder.dupe(dir_path)) catch @panic("OOM"); | |
| 947 | } | |
| 948 | ||
| 949 | pub fn addPackage(self: *CompileStep, package: Pkg) void { | |
| 950 | self.packages.append(self.builder.dupePkg(package)) catch @panic("OOM"); | |
| 951 | self.addRecursiveBuildDeps(package); | |
| 952 | } | |
| 953 | ||
| 954 | pub fn addOptions(self: *CompileStep, package_name: []const u8, options: *OptionsStep) void { | |
| 955 | self.addPackage(options.getPackage(package_name)); | |
| 956 | } | |
| 957 | ||
| 958 | fn addRecursiveBuildDeps(self: *CompileStep, package: Pkg) void { | |
| 959 | package.source.addStepDependencies(&self.step); | |
| 960 | if (package.dependencies) |deps| { | |
| 961 | for (deps) |dep| { | |
| 962 | self.addRecursiveBuildDeps(dep); | |
| 963 | } | |
| 964 | } | |
| 965 | } | |
| 966 | ||
| 967 | pub fn addPackagePath(self: *CompileStep, name: []const u8, pkg_index_path: []const u8) void { | |
| 968 | self.addPackage(Pkg{ | |
| 969 | .name = self.builder.dupe(name), | |
| 970 | .source = .{ .path = self.builder.dupe(pkg_index_path) }, | |
| 971 | }); | |
| 972 | } | |
| 973 | ||
| 974 | /// If Vcpkg was found on the system, it will be added to include and lib | |
| 975 | /// paths for the specified target. | |
| 976 | pub fn addVcpkgPaths(self: *CompileStep, linkage: CompileStep.Linkage) !void { | |
| 977 | // Ideally in the Unattempted case we would call the function recursively | |
| 978 | // after findVcpkgRoot and have only one switch statement, but the compiler | |
| 979 | // cannot resolve the error set. | |
| 980 | switch (self.builder.vcpkg_root) { | |
| 981 | .unattempted => { | |
| 982 | self.builder.vcpkg_root = if (try findVcpkgRoot(self.builder.allocator)) |root| | |
| 983 | VcpkgRoot{ .found = root } | |
| 984 | else | |
| 985 | .not_found; | |
| 986 | }, | |
| 987 | .not_found => return error.VcpkgNotFound, | |
| 988 | .found => {}, | |
| 989 | } | |
| 990 | ||
| 991 | switch (self.builder.vcpkg_root) { | |
| 992 | .unattempted => unreachable, | |
| 993 | .not_found => return error.VcpkgNotFound, | |
| 994 | .found => |root| { | |
| 995 | const allocator = self.builder.allocator; | |
| 996 | const triplet = try self.target.vcpkgTriplet(allocator, if (linkage == .static) .Static else .Dynamic); | |
| 997 | defer self.builder.allocator.free(triplet); | |
| 998 | ||
| 999 | const include_path = self.builder.pathJoin(&.{ root, "installed", triplet, "include" }); | |
| 1000 | errdefer allocator.free(include_path); | |
| 1001 | try self.include_dirs.append(IncludeDir{ .raw_path = include_path }); | |
| 1002 | ||
| 1003 | const lib_path = self.builder.pathJoin(&.{ root, "installed", triplet, "lib" }); | |
| 1004 | try self.lib_paths.append(lib_path); | |
| 1005 | ||
| 1006 | self.vcpkg_bin_path = self.builder.pathJoin(&.{ root, "installed", triplet, "bin" }); | |
| 1007 | }, | |
| 1008 | } | |
| 1009 | } | |
| 1010 | ||
| 1011 | pub fn setExecCmd(self: *CompileStep, args: []const ?[]const u8) void { | |
| 1012 | assert(self.kind == .@"test"); | |
| 1013 | const duped_args = self.builder.allocator.alloc(?[]u8, args.len) catch @panic("OOM"); | |
| 1014 | for (args) |arg, i| { | |
| 1015 | duped_args[i] = if (arg) |a| self.builder.dupe(a) else null; | |
| 1016 | } | |
| 1017 | self.exec_cmd_args = duped_args; | |
| 1018 | } | |
| 1019 | ||
| 1020 | fn linkLibraryOrObject(self: *CompileStep, other: *CompileStep) void { | |
| 1021 | self.step.dependOn(&other.step); | |
| 1022 | self.link_objects.append(.{ .other_step = other }) catch @panic("OOM"); | |
| 1023 | self.include_dirs.append(.{ .other_step = other }) catch @panic("OOM"); | |
| 1024 | } | |
| 1025 | ||
| 1026 | fn makePackageCmd(self: *CompileStep, pkg: Pkg, zig_args: *ArrayList([]const u8)) error{OutOfMemory}!void { | |
| 1027 | const builder = self.builder; | |
| 1028 | ||
| 1029 | try zig_args.append("--pkg-begin"); | |
| 1030 | try zig_args.append(pkg.name); | |
| 1031 | try zig_args.append(builder.pathFromRoot(pkg.source.getPath(self.builder))); | |
| 1032 | ||
| 1033 | if (pkg.dependencies) |dependencies| { | |
| 1034 | for (dependencies) |sub_pkg| { | |
| 1035 | try self.makePackageCmd(sub_pkg, zig_args); | |
| 1036 | } | |
| 1037 | } | |
| 1038 | ||
| 1039 | try zig_args.append("--pkg-end"); | |
| 1040 | } | |
| 1041 | ||
| 1042 | fn make(step: *Step) !void { | |
| 1043 | const self = @fieldParentPtr(CompileStep, "step", step); | |
| 1044 | const builder = self.builder; | |
| 1045 | ||
| 1046 | if (self.root_src == null and self.link_objects.items.len == 0) { | |
| 1047 | log.err("{s}: linker needs 1 or more objects to link", .{self.step.name}); | |
| 1048 | return error.NeedAnObject; | |
| 1049 | } | |
| 1050 | ||
| 1051 | var zig_args = ArrayList([]const u8).init(builder.allocator); | |
| 1052 | defer zig_args.deinit(); | |
| 1053 | ||
| 1054 | try zig_args.append(builder.zig_exe); | |
| 1055 | ||
| 1056 | const cmd = switch (self.kind) { | |
| 1057 | .lib => "build-lib", | |
| 1058 | .exe => "build-exe", | |
| 1059 | .obj => "build-obj", | |
| 1060 | .@"test" => "test", | |
| 1061 | .test_exe => "test", | |
| 1062 | }; | |
| 1063 | try zig_args.append(cmd); | |
| 1064 | ||
| 1065 | if (builder.color != .auto) { | |
| 1066 | try zig_args.append("--color"); | |
| 1067 | try zig_args.append(@tagName(builder.color)); | |
| 1068 | } | |
| 1069 | ||
| 1070 | if (builder.reference_trace) |some| { | |
| 1071 | try zig_args.append(try std.fmt.allocPrint(builder.allocator, "-freference-trace={d}", .{some})); | |
| 1072 | } | |
| 1073 | ||
| 1074 | try addFlag(&zig_args, "LLVM", self.use_llvm); | |
| 1075 | try addFlag(&zig_args, "LLD", self.use_lld); | |
| 1076 | ||
| 1077 | if (self.target.ofmt) |ofmt| { | |
| 1078 | try zig_args.append(try std.fmt.allocPrint(builder.allocator, "-ofmt={s}", .{@tagName(ofmt)})); | |
| 1079 | } | |
| 1080 | ||
| 1081 | if (self.entry_symbol_name) |entry| { | |
| 1082 | try zig_args.append("--entry"); | |
| 1083 | try zig_args.append(entry); | |
| 1084 | } | |
| 1085 | ||
| 1086 | if (self.stack_size) |stack_size| { | |
| 1087 | try zig_args.append("--stack"); | |
| 1088 | try zig_args.append(try std.fmt.allocPrint(builder.allocator, "{}", .{stack_size})); | |
| 1089 | } | |
| 1090 | ||
| 1091 | if (self.root_src) |root_src| try zig_args.append(root_src.getPath(builder)); | |
| 1092 | ||
| 1093 | // We will add link objects from transitive dependencies, but we want to keep | |
| 1094 | // all link objects in the same order provided. | |
| 1095 | // This array is used to keep self.link_objects immutable. | |
| 1096 | var transitive_deps: TransitiveDeps = .{ | |
| 1097 | .link_objects = ArrayList(LinkObject).init(builder.allocator), | |
| 1098 | .seen_system_libs = StringHashMap(void).init(builder.allocator), | |
| 1099 | .seen_steps = std.AutoHashMap(*const Step, void).init(builder.allocator), | |
| 1100 | .is_linking_libcpp = self.is_linking_libcpp, | |
| 1101 | .is_linking_libc = self.is_linking_libc, | |
| 1102 | .frameworks = &self.frameworks, | |
| 1103 | }; | |
| 1104 | ||
| 1105 | try transitive_deps.seen_steps.put(&self.step, {}); | |
| 1106 | try transitive_deps.add(self.link_objects.items); | |
| 1107 | ||
| 1108 | var prev_has_extra_flags = false; | |
| 1109 | ||
| 1110 | for (transitive_deps.link_objects.items) |link_object| { | |
| 1111 | switch (link_object) { | |
| 1112 | .static_path => |static_path| try zig_args.append(static_path.getPath(builder)), | |
| 1113 | ||
| 1114 | .other_step => |other| switch (other.kind) { | |
| 1115 | .exe => @panic("Cannot link with an executable build artifact"), | |
| 1116 | .test_exe => @panic("Cannot link with an executable build artifact"), | |
| 1117 | .@"test" => @panic("Cannot link with a test"), | |
| 1118 | .obj => { | |
| 1119 | try zig_args.append(other.getOutputSource().getPath(builder)); | |
| 1120 | }, | |
| 1121 | .lib => l: { | |
| 1122 | if (self.isStaticLibrary() and other.isStaticLibrary()) { | |
| 1123 | // Avoid putting a static library inside a static library. | |
| 1124 | break :l; | |
| 1125 | } | |
| 1126 | ||
| 1127 | const full_path_lib = other.getOutputLibSource().getPath(builder); | |
| 1128 | try zig_args.append(full_path_lib); | |
| 1129 | ||
| 1130 | if (other.linkage == Linkage.dynamic and !self.target.isWindows()) { | |
| 1131 | if (fs.path.dirname(full_path_lib)) |dirname| { | |
| 1132 | try zig_args.append("-rpath"); | |
| 1133 | try zig_args.append(dirname); | |
| 1134 | } | |
| 1135 | } | |
| 1136 | }, | |
| 1137 | }, | |
| 1138 | ||
| 1139 | .system_lib => |system_lib| { | |
| 1140 | const prefix: []const u8 = prefix: { | |
| 1141 | if (system_lib.needed) break :prefix "-needed-l"; | |
| 1142 | if (system_lib.weak) { | |
| 1143 | if (self.target.isDarwin()) break :prefix "-weak-l"; | |
| 1144 | log.warn("Weak library import used for a non-darwin target, this will be converted to normally library import `-lname`", .{}); | |
| 1145 | } | |
| 1146 | break :prefix "-l"; | |
| 1147 | }; | |
| 1148 | switch (system_lib.use_pkg_config) { | |
| 1149 | .no => try zig_args.append(builder.fmt("{s}{s}", .{ prefix, system_lib.name })), | |
| 1150 | .yes, .force => { | |
| 1151 | if (self.runPkgConfig(system_lib.name)) |args| { | |
| 1152 | try zig_args.appendSlice(args); | |
| 1153 | } else |err| switch (err) { | |
| 1154 | error.PkgConfigInvalidOutput, | |
| 1155 | error.PkgConfigCrashed, | |
| 1156 | error.PkgConfigFailed, | |
| 1157 | error.PkgConfigNotInstalled, | |
| 1158 | error.PackageNotFound, | |
| 1159 | => switch (system_lib.use_pkg_config) { | |
| 1160 | .yes => { | |
| 1161 | // pkg-config failed, so fall back to linking the library | |
| 1162 | // by name directly. | |
| 1163 | try zig_args.append(builder.fmt("{s}{s}", .{ | |
| 1164 | prefix, | |
| 1165 | system_lib.name, | |
| 1166 | })); | |
| 1167 | }, | |
| 1168 | .force => { | |
| 1169 | panic("pkg-config failed for library {s}", .{system_lib.name}); | |
| 1170 | }, | |
| 1171 | .no => unreachable, | |
| 1172 | }, | |
| 1173 | ||
| 1174 | else => |e| return e, | |
| 1175 | } | |
| 1176 | }, | |
| 1177 | } | |
| 1178 | }, | |
| 1179 | ||
| 1180 | .assembly_file => |asm_file| { | |
| 1181 | if (prev_has_extra_flags) { | |
| 1182 | try zig_args.append("-extra-cflags"); | |
| 1183 | try zig_args.append("--"); | |
| 1184 | prev_has_extra_flags = false; | |
| 1185 | } | |
| 1186 | try zig_args.append(asm_file.getPath(builder)); | |
| 1187 | }, | |
| 1188 | ||
| 1189 | .c_source_file => |c_source_file| { | |
| 1190 | if (c_source_file.args.len == 0) { | |
| 1191 | if (prev_has_extra_flags) { | |
| 1192 | try zig_args.append("-cflags"); | |
| 1193 | try zig_args.append("--"); | |
| 1194 | prev_has_extra_flags = false; | |
| 1195 | } | |
| 1196 | } else { | |
| 1197 | try zig_args.append("-cflags"); | |
| 1198 | for (c_source_file.args) |arg| { | |
| 1199 | try zig_args.append(arg); | |
| 1200 | } | |
| 1201 | try zig_args.append("--"); | |
| 1202 | } | |
| 1203 | try zig_args.append(c_source_file.source.getPath(builder)); | |
| 1204 | }, | |
| 1205 | ||
| 1206 | .c_source_files => |c_source_files| { | |
| 1207 | if (c_source_files.flags.len == 0) { | |
| 1208 | if (prev_has_extra_flags) { | |
| 1209 | try zig_args.append("-cflags"); | |
| 1210 | try zig_args.append("--"); | |
| 1211 | prev_has_extra_flags = false; | |
| 1212 | } | |
| 1213 | } else { | |
| 1214 | try zig_args.append("-cflags"); | |
| 1215 | for (c_source_files.flags) |flag| { | |
| 1216 | try zig_args.append(flag); | |
| 1217 | } | |
| 1218 | try zig_args.append("--"); | |
| 1219 | } | |
| 1220 | for (c_source_files.files) |file| { | |
| 1221 | try zig_args.append(builder.pathFromRoot(file)); | |
| 1222 | } | |
| 1223 | }, | |
| 1224 | } | |
| 1225 | } | |
| 1226 | ||
| 1227 | if (transitive_deps.is_linking_libcpp) { | |
| 1228 | try zig_args.append("-lc++"); | |
| 1229 | } | |
| 1230 | ||
| 1231 | if (transitive_deps.is_linking_libc) { | |
| 1232 | try zig_args.append("-lc"); | |
| 1233 | } | |
| 1234 | ||
| 1235 | if (self.image_base) |image_base| { | |
| 1236 | try zig_args.append("--image-base"); | |
| 1237 | try zig_args.append(builder.fmt("0x{x}", .{image_base})); | |
| 1238 | } | |
| 1239 | ||
| 1240 | if (self.filter) |filter| { | |
| 1241 | try zig_args.append("--test-filter"); | |
| 1242 | try zig_args.append(filter); | |
| 1243 | } | |
| 1244 | ||
| 1245 | if (self.test_evented_io) { | |
| 1246 | try zig_args.append("--test-evented-io"); | |
| 1247 | } | |
| 1248 | ||
| 1249 | if (self.name_prefix.len != 0) { | |
| 1250 | try zig_args.append("--test-name-prefix"); | |
| 1251 | try zig_args.append(self.name_prefix); | |
| 1252 | } | |
| 1253 | ||
| 1254 | if (self.test_runner) |test_runner| { | |
| 1255 | try zig_args.append("--test-runner"); | |
| 1256 | try zig_args.append(builder.pathFromRoot(test_runner)); | |
| 1257 | } | |
| 1258 | ||
| 1259 | for (builder.debug_log_scopes) |log_scope| { | |
| 1260 | try zig_args.append("--debug-log"); | |
| 1261 | try zig_args.append(log_scope); | |
| 1262 | } | |
| 1263 | ||
| 1264 | if (builder.debug_compile_errors) { | |
| 1265 | try zig_args.append("--debug-compile-errors"); | |
| 1266 | } | |
| 1267 | ||
| 1268 | if (builder.verbose_cimport) try zig_args.append("--verbose-cimport"); | |
| 1269 | if (builder.verbose_air) try zig_args.append("--verbose-air"); | |
| 1270 | if (builder.verbose_llvm_ir) try zig_args.append("--verbose-llvm-ir"); | |
| 1271 | if (builder.verbose_link or self.verbose_link) try zig_args.append("--verbose-link"); | |
| 1272 | if (builder.verbose_cc or self.verbose_cc) try zig_args.append("--verbose-cc"); | |
| 1273 | if (builder.verbose_llvm_cpu_features) try zig_args.append("--verbose-llvm-cpu-features"); | |
| 1274 | ||
| 1275 | if (self.emit_analysis.getArg(builder, "emit-analysis")) |arg| try zig_args.append(arg); | |
| 1276 | if (self.emit_asm.getArg(builder, "emit-asm")) |arg| try zig_args.append(arg); | |
| 1277 | if (self.emit_bin.getArg(builder, "emit-bin")) |arg| try zig_args.append(arg); | |
| 1278 | if (self.emit_docs.getArg(builder, "emit-docs")) |arg| try zig_args.append(arg); | |
| 1279 | if (self.emit_implib.getArg(builder, "emit-implib")) |arg| try zig_args.append(arg); | |
| 1280 | if (self.emit_llvm_bc.getArg(builder, "emit-llvm-bc")) |arg| try zig_args.append(arg); | |
| 1281 | if (self.emit_llvm_ir.getArg(builder, "emit-llvm-ir")) |arg| try zig_args.append(arg); | |
| 1282 | ||
| 1283 | if (self.emit_h) try zig_args.append("-femit-h"); | |
| 1284 | ||
| 1285 | try addFlag(&zig_args, "strip", self.strip); | |
| 1286 | try addFlag(&zig_args, "unwind-tables", self.unwind_tables); | |
| 1287 | ||
| 1288 | switch (self.compress_debug_sections) { | |
| 1289 | .none => {}, | |
| 1290 | .zlib => try zig_args.append("--compress-debug-sections=zlib"), | |
| 1291 | } | |
| 1292 | ||
| 1293 | if (self.link_eh_frame_hdr) { | |
| 1294 | try zig_args.append("--eh-frame-hdr"); | |
| 1295 | } | |
| 1296 | if (self.link_emit_relocs) { | |
| 1297 | try zig_args.append("--emit-relocs"); | |
| 1298 | } | |
| 1299 | if (self.link_function_sections) { | |
| 1300 | try zig_args.append("-ffunction-sections"); | |
| 1301 | } | |
| 1302 | if (self.link_gc_sections) |x| { | |
| 1303 | try zig_args.append(if (x) "--gc-sections" else "--no-gc-sections"); | |
| 1304 | } | |
| 1305 | if (self.linker_allow_shlib_undefined) |x| { | |
| 1306 | try zig_args.append(if (x) "-fallow-shlib-undefined" else "-fno-allow-shlib-undefined"); | |
| 1307 | } | |
| 1308 | if (self.link_z_notext) { | |
| 1309 | try zig_args.append("-z"); | |
| 1310 | try zig_args.append("notext"); | |
| 1311 | } | |
| 1312 | if (!self.link_z_relro) { | |
| 1313 | try zig_args.append("-z"); | |
| 1314 | try zig_args.append("norelro"); | |
| 1315 | } | |
| 1316 | if (self.link_z_lazy) { | |
| 1317 | try zig_args.append("-z"); | |
| 1318 | try zig_args.append("lazy"); | |
| 1319 | } | |
| 1320 | if (self.link_z_common_page_size) |size| { | |
| 1321 | try zig_args.append("-z"); | |
| 1322 | try zig_args.append(builder.fmt("common-page-size={d}", .{size})); | |
| 1323 | } | |
| 1324 | if (self.link_z_max_page_size) |size| { | |
| 1325 | try zig_args.append("-z"); | |
| 1326 | try zig_args.append(builder.fmt("max-page-size={d}", .{size})); | |
| 1327 | } | |
| 1328 | ||
| 1329 | if (self.libc_file) |libc_file| { | |
| 1330 | try zig_args.append("--libc"); | |
| 1331 | try zig_args.append(libc_file.getPath(builder)); | |
| 1332 | } else if (builder.libc_file) |libc_file| { | |
| 1333 | try zig_args.append("--libc"); | |
| 1334 | try zig_args.append(libc_file); | |
| 1335 | } | |
| 1336 | ||
| 1337 | switch (self.optimize) { | |
| 1338 | .Debug => {}, // Skip since it's the default. | |
| 1339 | else => try zig_args.append(builder.fmt("-O{s}", .{@tagName(self.optimize)})), | |
| 1340 | } | |
| 1341 | ||
| 1342 | try zig_args.append("--cache-dir"); | |
| 1343 | try zig_args.append(builder.pathFromRoot(builder.cache_root)); | |
| 1344 | ||
| 1345 | try zig_args.append("--global-cache-dir"); | |
| 1346 | try zig_args.append(builder.pathFromRoot(builder.global_cache_root)); | |
| 1347 | ||
| 1348 | try zig_args.append("--name"); | |
| 1349 | try zig_args.append(self.name); | |
| 1350 | ||
| 1351 | if (self.linkage) |some| switch (some) { | |
| 1352 | .dynamic => try zig_args.append("-dynamic"), | |
| 1353 | .static => try zig_args.append("-static"), | |
| 1354 | }; | |
| 1355 | if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic) { | |
| 1356 | if (self.version) |version| { | |
| 1357 | try zig_args.append("--version"); | |
| 1358 | try zig_args.append(builder.fmt("{}", .{version})); | |
| 1359 | } | |
| 1360 | ||
| 1361 | if (self.target.isDarwin()) { | |
| 1362 | const install_name = self.install_name orelse builder.fmt("@rpath/{s}{s}{s}", .{ | |
| 1363 | self.target.libPrefix(), | |
| 1364 | self.name, | |
| 1365 | self.target.dynamicLibSuffix(), | |
| 1366 | }); | |
| 1367 | try zig_args.append("-install_name"); | |
| 1368 | try zig_args.append(install_name); | |
| 1369 | } | |
| 1370 | } | |
| 1371 | ||
| 1372 | if (self.entitlements) |entitlements| { | |
| 1373 | try zig_args.appendSlice(&[_][]const u8{ "--entitlements", entitlements }); | |
| 1374 | } | |
| 1375 | if (self.pagezero_size) |pagezero_size| { | |
| 1376 | const size = try std.fmt.allocPrint(builder.allocator, "{x}", .{pagezero_size}); | |
| 1377 | try zig_args.appendSlice(&[_][]const u8{ "-pagezero_size", size }); | |
| 1378 | } | |
| 1379 | if (self.search_strategy) |strat| switch (strat) { | |
| 1380 | .paths_first => try zig_args.append("-search_paths_first"), | |
| 1381 | .dylibs_first => try zig_args.append("-search_dylibs_first"), | |
| 1382 | }; | |
| 1383 | if (self.headerpad_size) |headerpad_size| { | |
| 1384 | const size = try std.fmt.allocPrint(builder.allocator, "{x}", .{headerpad_size}); | |
| 1385 | try zig_args.appendSlice(&[_][]const u8{ "-headerpad", size }); | |
| 1386 | } | |
| 1387 | if (self.headerpad_max_install_names) { | |
| 1388 | try zig_args.append("-headerpad_max_install_names"); | |
| 1389 | } | |
| 1390 | if (self.dead_strip_dylibs) { | |
| 1391 | try zig_args.append("-dead_strip_dylibs"); | |
| 1392 | } | |
| 1393 | ||
| 1394 | try addFlag(&zig_args, "compiler-rt", self.bundle_compiler_rt); | |
| 1395 | try addFlag(&zig_args, "single-threaded", self.single_threaded); | |
| 1396 | if (self.disable_stack_probing) { | |
| 1397 | try zig_args.append("-fno-stack-check"); | |
| 1398 | } | |
| 1399 | try addFlag(&zig_args, "stack-protector", self.stack_protector); | |
| 1400 | if (self.red_zone) |red_zone| { | |
| 1401 | if (red_zone) { | |
| 1402 | try zig_args.append("-mred-zone"); | |
| 1403 | } else { | |
| 1404 | try zig_args.append("-mno-red-zone"); | |
| 1405 | } | |
| 1406 | } | |
| 1407 | try addFlag(&zig_args, "omit-frame-pointer", self.omit_frame_pointer); | |
| 1408 | try addFlag(&zig_args, "dll-export-fns", self.dll_export_fns); | |
| 1409 | ||
| 1410 | if (self.disable_sanitize_c) { | |
| 1411 | try zig_args.append("-fno-sanitize-c"); | |
| 1412 | } | |
| 1413 | if (self.sanitize_thread) { | |
| 1414 | try zig_args.append("-fsanitize-thread"); | |
| 1415 | } | |
| 1416 | if (self.rdynamic) { | |
| 1417 | try zig_args.append("-rdynamic"); | |
| 1418 | } | |
| 1419 | if (self.import_memory) { | |
| 1420 | try zig_args.append("--import-memory"); | |
| 1421 | } | |
| 1422 | if (self.import_symbols) { | |
| 1423 | try zig_args.append("--import-symbols"); | |
| 1424 | } | |
| 1425 | if (self.import_table) { | |
| 1426 | try zig_args.append("--import-table"); | |
| 1427 | } | |
| 1428 | if (self.export_table) { | |
| 1429 | try zig_args.append("--export-table"); | |
| 1430 | } | |
| 1431 | if (self.initial_memory) |initial_memory| { | |
| 1432 | try zig_args.append(builder.fmt("--initial-memory={d}", .{initial_memory})); | |
| 1433 | } | |
| 1434 | if (self.max_memory) |max_memory| { | |
| 1435 | try zig_args.append(builder.fmt("--max-memory={d}", .{max_memory})); | |
| 1436 | } | |
| 1437 | if (self.shared_memory) { | |
| 1438 | try zig_args.append("--shared-memory"); | |
| 1439 | } | |
| 1440 | if (self.global_base) |global_base| { | |
| 1441 | try zig_args.append(builder.fmt("--global-base={d}", .{global_base})); | |
| 1442 | } | |
| 1443 | ||
| 1444 | if (self.code_model != .default) { | |
| 1445 | try zig_args.append("-mcmodel"); | |
| 1446 | try zig_args.append(@tagName(self.code_model)); | |
| 1447 | } | |
| 1448 | if (self.wasi_exec_model) |model| { | |
| 1449 | try zig_args.append(builder.fmt("-mexec-model={s}", .{@tagName(model)})); | |
| 1450 | } | |
| 1451 | for (self.export_symbol_names) |symbol_name| { | |
| 1452 | try zig_args.append(builder.fmt("--export={s}", .{symbol_name})); | |
| 1453 | } | |
| 1454 | ||
| 1455 | if (!self.target.isNative()) { | |
| 1456 | try zig_args.appendSlice(&.{ | |
| 1457 | "-target", try self.target.zigTriple(builder.allocator), | |
| 1458 | "-mcpu", try std.Build.serializeCpu(builder.allocator, self.target.getCpu()), | |
| 1459 | }); | |
| 1460 | ||
| 1461 | if (self.target.dynamic_linker.get()) |dynamic_linker| { | |
| 1462 | try zig_args.append("--dynamic-linker"); | |
| 1463 | try zig_args.append(dynamic_linker); | |
| 1464 | } | |
| 1465 | } | |
| 1466 | ||
| 1467 | if (self.linker_script) |linker_script| { | |
| 1468 | try zig_args.append("--script"); | |
| 1469 | try zig_args.append(linker_script.getPath(builder)); | |
| 1470 | } | |
| 1471 | ||
| 1472 | if (self.version_script) |version_script| { | |
| 1473 | try zig_args.append("--version-script"); | |
| 1474 | try zig_args.append(builder.pathFromRoot(version_script)); | |
| 1475 | } | |
| 1476 | ||
| 1477 | if (self.kind == .@"test") { | |
| 1478 | if (self.exec_cmd_args) |exec_cmd_args| { | |
| 1479 | for (exec_cmd_args) |cmd_arg| { | |
| 1480 | if (cmd_arg) |arg| { | |
| 1481 | try zig_args.append("--test-cmd"); | |
| 1482 | try zig_args.append(arg); | |
| 1483 | } else { | |
| 1484 | try zig_args.append("--test-cmd-bin"); | |
| 1485 | } | |
| 1486 | } | |
| 1487 | } else { | |
| 1488 | const need_cross_glibc = self.target.isGnuLibC() and transitive_deps.is_linking_libc; | |
| 1489 | ||
| 1490 | switch (builder.host.getExternalExecutor(self.target_info, .{ | |
| 1491 | .qemu_fixes_dl = need_cross_glibc and builder.glibc_runtimes_dir != null, | |
| 1492 | .link_libc = transitive_deps.is_linking_libc, | |
| 1493 | })) { | |
| 1494 | .native => {}, | |
| 1495 | .bad_dl, .bad_os_or_cpu => { | |
| 1496 | try zig_args.append("--test-no-exec"); | |
| 1497 | }, | |
| 1498 | .rosetta => if (builder.enable_rosetta) { | |
| 1499 | try zig_args.append("--test-cmd-bin"); | |
| 1500 | } else { | |
| 1501 | try zig_args.append("--test-no-exec"); | |
| 1502 | }, | |
| 1503 | .qemu => |bin_name| ok: { | |
| 1504 | if (builder.enable_qemu) qemu: { | |
| 1505 | const glibc_dir_arg = if (need_cross_glibc) | |
| 1506 | builder.glibc_runtimes_dir orelse break :qemu | |
| 1507 | else | |
| 1508 | null; | |
| 1509 | try zig_args.append("--test-cmd"); | |
| 1510 | try zig_args.append(bin_name); | |
| 1511 | if (glibc_dir_arg) |dir| { | |
| 1512 | // TODO look into making this a call to `linuxTriple`. This | |
| 1513 | // needs the directory to be called "i686" rather than | |
| 1514 | // "x86" which is why we do it manually here. | |
| 1515 | const fmt_str = "{s}" ++ fs.path.sep_str ++ "{s}-{s}-{s}"; | |
| 1516 | const cpu_arch = self.target.getCpuArch(); | |
| 1517 | const os_tag = self.target.getOsTag(); | |
| 1518 | const abi = self.target.getAbi(); | |
| 1519 | const cpu_arch_name: []const u8 = if (cpu_arch == .x86) | |
| 1520 | "i686" | |
| 1521 | else | |
| 1522 | @tagName(cpu_arch); | |
| 1523 | const full_dir = try std.fmt.allocPrint(builder.allocator, fmt_str, .{ | |
| 1524 | dir, cpu_arch_name, @tagName(os_tag), @tagName(abi), | |
| 1525 | }); | |
| 1526 | ||
| 1527 | try zig_args.append("--test-cmd"); | |
| 1528 | try zig_args.append("-L"); | |
| 1529 | try zig_args.append("--test-cmd"); | |
| 1530 | try zig_args.append(full_dir); | |
| 1531 | } | |
| 1532 | try zig_args.append("--test-cmd-bin"); | |
| 1533 | break :ok; | |
| 1534 | } | |
| 1535 | try zig_args.append("--test-no-exec"); | |
| 1536 | }, | |
| 1537 | .wine => |bin_name| if (builder.enable_wine) { | |
| 1538 | try zig_args.append("--test-cmd"); | |
| 1539 | try zig_args.append(bin_name); | |
| 1540 | try zig_args.append("--test-cmd-bin"); | |
| 1541 | } else { | |
| 1542 | try zig_args.append("--test-no-exec"); | |
| 1543 | }, | |
| 1544 | .wasmtime => |bin_name| if (builder.enable_wasmtime) { | |
| 1545 | try zig_args.append("--test-cmd"); | |
| 1546 | try zig_args.append(bin_name); | |
| 1547 | try zig_args.append("--test-cmd"); | |
| 1548 | try zig_args.append("--dir=."); | |
| 1549 | try zig_args.append("--test-cmd-bin"); | |
| 1550 | } else { | |
| 1551 | try zig_args.append("--test-no-exec"); | |
| 1552 | }, | |
| 1553 | .darling => |bin_name| if (builder.enable_darling) { | |
| 1554 | try zig_args.append("--test-cmd"); | |
| 1555 | try zig_args.append(bin_name); | |
| 1556 | try zig_args.append("--test-cmd-bin"); | |
| 1557 | } else { | |
| 1558 | try zig_args.append("--test-no-exec"); | |
| 1559 | }, | |
| 1560 | } | |
| 1561 | } | |
| 1562 | } else if (self.kind == .test_exe) { | |
| 1563 | try zig_args.append("--test-no-exec"); | |
| 1564 | } | |
| 1565 | ||
| 1566 | for (self.packages.items) |pkg| { | |
| 1567 | try self.makePackageCmd(pkg, &zig_args); | |
| 1568 | } | |
| 1569 | ||
| 1570 | for (self.include_dirs.items) |include_dir| { | |
| 1571 | switch (include_dir) { | |
| 1572 | .raw_path => |include_path| { | |
| 1573 | try zig_args.append("-I"); | |
| 1574 | try zig_args.append(builder.pathFromRoot(include_path)); | |
| 1575 | }, | |
| 1576 | .raw_path_system => |include_path| { | |
| 1577 | if (builder.sysroot != null) { | |
| 1578 | try zig_args.append("-iwithsysroot"); | |
| 1579 | } else { | |
| 1580 | try zig_args.append("-isystem"); | |
| 1581 | } | |
| 1582 | ||
| 1583 | const resolved_include_path = builder.pathFromRoot(include_path); | |
| 1584 | ||
| 1585 | const common_include_path = if (builtin.os.tag == .windows and builder.sysroot != null and fs.path.isAbsolute(resolved_include_path)) blk: { | |
| 1586 | // We need to check for disk designator and strip it out from dir path so | |
| 1587 | // that zig/clang can concat resolved_include_path with sysroot. | |
| 1588 | const disk_designator = fs.path.diskDesignatorWindows(resolved_include_path); | |
| 1589 | ||
| 1590 | if (mem.indexOf(u8, resolved_include_path, disk_designator)) |where| { | |
| 1591 | break :blk resolved_include_path[where + disk_designator.len ..]; | |
| 1592 | } | |
| 1593 | ||
| 1594 | break :blk resolved_include_path; | |
| 1595 | } else resolved_include_path; | |
| 1596 | ||
| 1597 | try zig_args.append(common_include_path); | |
| 1598 | }, | |
| 1599 | .other_step => |other| { | |
| 1600 | if (other.emit_h) { | |
| 1601 | const h_path = other.getOutputHSource().getPath(builder); | |
| 1602 | try zig_args.append("-isystem"); | |
| 1603 | try zig_args.append(fs.path.dirname(h_path).?); | |
| 1604 | } | |
| 1605 | if (other.installed_headers.items.len > 0) { | |
| 1606 | for (other.installed_headers.items) |install_step| { | |
| 1607 | try install_step.make(); | |
| 1608 | } | |
| 1609 | try zig_args.append("-I"); | |
| 1610 | try zig_args.append(builder.pathJoin(&.{ | |
| 1611 | other.builder.install_prefix, "include", | |
| 1612 | })); | |
| 1613 | } | |
| 1614 | }, | |
| 1615 | .config_header_step => |config_header| { | |
| 1616 | try zig_args.append("-I"); | |
| 1617 | try zig_args.append(config_header.output_dir); | |
| 1618 | }, | |
| 1619 | } | |
| 1620 | } | |
| 1621 | ||
| 1622 | for (self.lib_paths.items) |lib_path| { | |
| 1623 | try zig_args.append("-L"); | |
| 1624 | try zig_args.append(lib_path); | |
| 1625 | } | |
| 1626 | ||
| 1627 | for (self.rpaths.items) |rpath| { | |
| 1628 | try zig_args.append("-rpath"); | |
| 1629 | try zig_args.append(rpath); | |
| 1630 | } | |
| 1631 | ||
| 1632 | for (self.c_macros.items) |c_macro| { | |
| 1633 | try zig_args.append("-D"); | |
| 1634 | try zig_args.append(c_macro); | |
| 1635 | } | |
| 1636 | ||
| 1637 | if (self.target.isDarwin()) { | |
| 1638 | for (self.framework_dirs.items) |dir| { | |
| 1639 | if (builder.sysroot != null) { | |
| 1640 | try zig_args.append("-iframeworkwithsysroot"); | |
| 1641 | } else { | |
| 1642 | try zig_args.append("-iframework"); | |
| 1643 | } | |
| 1644 | try zig_args.append(dir); | |
| 1645 | try zig_args.append("-F"); | |
| 1646 | try zig_args.append(dir); | |
| 1647 | } | |
| 1648 | ||
| 1649 | var it = self.frameworks.iterator(); | |
| 1650 | while (it.next()) |entry| { | |
| 1651 | const name = entry.key_ptr.*; | |
| 1652 | const info = entry.value_ptr.*; | |
| 1653 | if (info.needed) { | |
| 1654 | try zig_args.append("-needed_framework"); | |
| 1655 | } else if (info.weak) { | |
| 1656 | try zig_args.append("-weak_framework"); | |
| 1657 | } else { | |
| 1658 | try zig_args.append("-framework"); | |
| 1659 | } | |
| 1660 | try zig_args.append(name); | |
| 1661 | } | |
| 1662 | } else { | |
| 1663 | if (self.framework_dirs.items.len > 0) { | |
| 1664 | log.info("Framework directories have been added for a non-darwin target, this will have no affect on the build", .{}); | |
| 1665 | } | |
| 1666 | ||
| 1667 | if (self.frameworks.count() > 0) { | |
| 1668 | log.info("Frameworks have been added for a non-darwin target, this will have no affect on the build", .{}); | |
| 1669 | } | |
| 1670 | } | |
| 1671 | ||
| 1672 | if (builder.sysroot) |sysroot| { | |
| 1673 | try zig_args.appendSlice(&[_][]const u8{ "--sysroot", sysroot }); | |
| 1674 | } | |
| 1675 | ||
| 1676 | for (builder.search_prefixes.items) |search_prefix| { | |
| 1677 | try zig_args.append("-L"); | |
| 1678 | try zig_args.append(builder.pathJoin(&.{ | |
| 1679 | search_prefix, "lib", | |
| 1680 | })); | |
| 1681 | try zig_args.append("-I"); | |
| 1682 | try zig_args.append(builder.pathJoin(&.{ | |
| 1683 | search_prefix, "include", | |
| 1684 | })); | |
| 1685 | } | |
| 1686 | ||
| 1687 | try addFlag(&zig_args, "valgrind", self.valgrind_support); | |
| 1688 | try addFlag(&zig_args, "each-lib-rpath", self.each_lib_rpath); | |
| 1689 | try addFlag(&zig_args, "build-id", self.build_id); | |
| 1690 | ||
| 1691 | if (self.override_lib_dir) |dir| { | |
| 1692 | try zig_args.append("--zig-lib-dir"); | |
| 1693 | try zig_args.append(builder.pathFromRoot(dir)); | |
| 1694 | } else if (builder.override_lib_dir) |dir| { | |
| 1695 | try zig_args.append("--zig-lib-dir"); | |
| 1696 | try zig_args.append(builder.pathFromRoot(dir)); | |
| 1697 | } | |
| 1698 | ||
| 1699 | if (self.main_pkg_path) |dir| { | |
| 1700 | try zig_args.append("--main-pkg-path"); | |
| 1701 | try zig_args.append(builder.pathFromRoot(dir)); | |
| 1702 | } | |
| 1703 | ||
| 1704 | try addFlag(&zig_args, "PIC", self.force_pic); | |
| 1705 | try addFlag(&zig_args, "PIE", self.pie); | |
| 1706 | try addFlag(&zig_args, "lto", self.want_lto); | |
| 1707 | ||
| 1708 | if (self.subsystem) |subsystem| { | |
| 1709 | try zig_args.append("--subsystem"); | |
| 1710 | try zig_args.append(switch (subsystem) { | |
| 1711 | .Console => "console", | |
| 1712 | .Windows => "windows", | |
| 1713 | .Posix => "posix", | |
| 1714 | .Native => "native", | |
| 1715 | .EfiApplication => "efi_application", | |
| 1716 | .EfiBootServiceDriver => "efi_boot_service_driver", | |
| 1717 | .EfiRom => "efi_rom", | |
| 1718 | .EfiRuntimeDriver => "efi_runtime_driver", | |
| 1719 | }); | |
| 1720 | } | |
| 1721 | ||
| 1722 | try zig_args.append("--enable-cache"); | |
| 1723 | ||
| 1724 | // Windows has an argument length limit of 32,766 characters, macOS 262,144 and Linux | |
| 1725 | // 2,097,152. If our args exceed 30 KiB, we instead write them to a "response file" and | |
| 1726 | // pass that to zig, e.g. via 'zig build-lib @args.rsp' | |
| 1727 | // See @file syntax here: https://gcc.gnu.org/onlinedocs/gcc/Overall-Options.html | |
| 1728 | var args_length: usize = 0; | |
| 1729 | for (zig_args.items) |arg| { | |
| 1730 | args_length += arg.len + 1; // +1 to account for null terminator | |
| 1731 | } | |
| 1732 | if (args_length >= 30 * 1024) { | |
| 1733 | const args_dir = try fs.path.join( | |
| 1734 | builder.allocator, | |
| 1735 | &[_][]const u8{ builder.pathFromRoot("zig-cache"), "args" }, | |
| 1736 | ); | |
| 1737 | try std.fs.cwd().makePath(args_dir); | |
| 1738 | ||
| 1739 | var args_arena = std.heap.ArenaAllocator.init(builder.allocator); | |
| 1740 | defer args_arena.deinit(); | |
| 1741 | ||
| 1742 | const args_to_escape = zig_args.items[2..]; | |
| 1743 | var escaped_args = try ArrayList([]const u8).initCapacity(args_arena.allocator(), args_to_escape.len); | |
| 1744 | ||
| 1745 | arg_blk: for (args_to_escape) |arg| { | |
| 1746 | for (arg) |c, arg_idx| { | |
| 1747 | if (c == '\\' or c == '"') { | |
| 1748 | // Slow path for arguments that need to be escaped. We'll need to allocate and copy | |
| 1749 | var escaped = try ArrayList(u8).initCapacity(args_arena.allocator(), arg.len + 1); | |
| 1750 | const writer = escaped.writer(); | |
| 1751 | try writer.writeAll(arg[0..arg_idx]); | |
| 1752 | for (arg[arg_idx..]) |to_escape| { | |
| 1753 | if (to_escape == '\\' or to_escape == '"') try writer.writeByte('\\'); | |
| 1754 | try writer.writeByte(to_escape); | |
| 1755 | } | |
| 1756 | escaped_args.appendAssumeCapacity(escaped.items); | |
| 1757 | continue :arg_blk; | |
| 1758 | } | |
| 1759 | } | |
| 1760 | escaped_args.appendAssumeCapacity(arg); // no escaping needed so just use original argument | |
| 1761 | } | |
| 1762 | ||
| 1763 | // Write the args to zig-cache/args/<SHA256 hash of args> to avoid conflicts with | |
| 1764 | // other zig build commands running in parallel. | |
| 1765 | const partially_quoted = try std.mem.join(builder.allocator, "\" \"", escaped_args.items); | |
| 1766 | const args = try std.mem.concat(builder.allocator, u8, &[_][]const u8{ "\"", partially_quoted, "\"" }); | |
| 1767 | ||
| 1768 | var args_hash: [Sha256.digest_length]u8 = undefined; | |
| 1769 | Sha256.hash(args, &args_hash, .{}); | |
| 1770 | var args_hex_hash: [Sha256.digest_length * 2]u8 = undefined; | |
| 1771 | _ = try std.fmt.bufPrint( | |
| 1772 | &args_hex_hash, | |
| 1773 | "{s}", | |
| 1774 | .{std.fmt.fmtSliceHexLower(&args_hash)}, | |
| 1775 | ); | |
| 1776 | ||
| 1777 | const args_file = try fs.path.join(builder.allocator, &[_][]const u8{ args_dir, args_hex_hash[0..] }); | |
| 1778 | try std.fs.cwd().writeFile(args_file, args); | |
| 1779 | ||
| 1780 | zig_args.shrinkRetainingCapacity(2); | |
| 1781 | try zig_args.append(try std.mem.concat(builder.allocator, u8, &[_][]const u8{ "@", args_file })); | |
| 1782 | } | |
| 1783 | ||
| 1784 | const output_dir_nl = try builder.execFromStep(zig_args.items, &self.step); | |
| 1785 | const build_output_dir = mem.trimRight(u8, output_dir_nl, "\r\n"); | |
| 1786 | ||
| 1787 | if (self.output_dir) |output_dir| { | |
| 1788 | var src_dir = try std.fs.cwd().openIterableDir(build_output_dir, .{}); | |
| 1789 | defer src_dir.close(); | |
| 1790 | ||
| 1791 | // Create the output directory if it doesn't exist. | |
| 1792 | try std.fs.cwd().makePath(output_dir); | |
| 1793 | ||
| 1794 | var dest_dir = try std.fs.cwd().openDir(output_dir, .{}); | |
| 1795 | defer dest_dir.close(); | |
| 1796 | ||
| 1797 | var it = src_dir.iterate(); | |
| 1798 | while (try it.next()) |entry| { | |
| 1799 | // The compiler can put these files into the same directory, but we don't | |
| 1800 | // want to copy them over. | |
| 1801 | if (mem.eql(u8, entry.name, "llvm-ar.id") or | |
| 1802 | mem.eql(u8, entry.name, "libs.txt") or | |
| 1803 | mem.eql(u8, entry.name, "builtin.zig") or | |
| 1804 | mem.eql(u8, entry.name, "zld.id") or | |
| 1805 | mem.eql(u8, entry.name, "lld.id")) continue; | |
| 1806 | ||
| 1807 | _ = try src_dir.dir.updateFile(entry.name, dest_dir, entry.name, .{}); | |
| 1808 | } | |
| 1809 | } else { | |
| 1810 | self.output_dir = build_output_dir; | |
| 1811 | } | |
| 1812 | ||
| 1813 | // This will ensure all output filenames will now have the output_dir available! | |
| 1814 | self.computeOutFileNames(); | |
| 1815 | ||
| 1816 | // Update generated files | |
| 1817 | if (self.output_dir != null) { | |
| 1818 | self.output_path_source.path = builder.pathJoin( | |
| 1819 | &.{ self.output_dir.?, self.out_filename }, | |
| 1820 | ); | |
| 1821 | ||
| 1822 | if (self.emit_h) { | |
| 1823 | self.output_h_path_source.path = builder.pathJoin( | |
| 1824 | &.{ self.output_dir.?, self.out_h_filename }, | |
| 1825 | ); | |
| 1826 | } | |
| 1827 | ||
| 1828 | if (self.target.isWindows() or self.target.isUefi()) { | |
| 1829 | self.output_pdb_path_source.path = builder.pathJoin( | |
| 1830 | &.{ self.output_dir.?, self.out_pdb_filename }, | |
| 1831 | ); | |
| 1832 | } | |
| 1833 | } | |
| 1834 | ||
| 1835 | if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic and self.version != null and self.target.wantSharedLibSymLinks()) { | |
| 1836 | try doAtomicSymLinks(builder.allocator, self.getOutputSource().getPath(builder), self.major_only_filename.?, self.name_only_filename.?); | |
| 1837 | } | |
| 1838 | } | |
| 1839 | ||
| 1840 | fn isLibCLibrary(name: []const u8) bool { | |
| 1841 | const libc_libraries = [_][]const u8{ "c", "m", "dl", "rt", "pthread" }; | |
| 1842 | for (libc_libraries) |libc_lib_name| { | |
| 1843 | if (mem.eql(u8, name, libc_lib_name)) | |
| 1844 | return true; | |
| 1845 | } | |
| 1846 | return false; | |
| 1847 | } | |
| 1848 | ||
| 1849 | fn isLibCppLibrary(name: []const u8) bool { | |
| 1850 | const libcpp_libraries = [_][]const u8{ "c++", "stdc++" }; | |
| 1851 | for (libcpp_libraries) |libcpp_lib_name| { | |
| 1852 | if (mem.eql(u8, name, libcpp_lib_name)) | |
| 1853 | return true; | |
| 1854 | } | |
| 1855 | return false; | |
| 1856 | } | |
| 1857 | ||
| 1858 | /// Returned slice must be freed by the caller. | |
| 1859 | fn findVcpkgRoot(allocator: Allocator) !?[]const u8 { | |
| 1860 | const appdata_path = try fs.getAppDataDir(allocator, "vcpkg"); | |
| 1861 | defer allocator.free(appdata_path); | |
| 1862 | ||
| 1863 | const path_file = try fs.path.join(allocator, &[_][]const u8{ appdata_path, "vcpkg.path.txt" }); | |
| 1864 | defer allocator.free(path_file); | |
| 1865 | ||
| 1866 | const file = fs.cwd().openFile(path_file, .{}) catch return null; | |
| 1867 | defer file.close(); | |
| 1868 | ||
| 1869 | const size = @intCast(usize, try file.getEndPos()); | |
| 1870 | const vcpkg_path = try allocator.alloc(u8, size); | |
| 1871 | const size_read = try file.read(vcpkg_path); | |
| 1872 | std.debug.assert(size == size_read); | |
| 1873 | ||
| 1874 | return vcpkg_path; | |
| 1875 | } | |
| 1876 | ||
| 1877 | pub fn doAtomicSymLinks( | |
| 1878 | allocator: Allocator, | |
| 1879 | output_path: []const u8, | |
| 1880 | filename_major_only: []const u8, | |
| 1881 | filename_name_only: []const u8, | |
| 1882 | ) !void { | |
| 1883 | const out_dir = fs.path.dirname(output_path) orelse "."; | |
| 1884 | const out_basename = fs.path.basename(output_path); | |
| 1885 | // sym link for libfoo.so.1 to libfoo.so.1.2.3 | |
| 1886 | const major_only_path = try fs.path.join( | |
| 1887 | allocator, | |
| 1888 | &[_][]const u8{ out_dir, filename_major_only }, | |
| 1889 | ); | |
| 1890 | fs.atomicSymLink(allocator, out_basename, major_only_path) catch |err| { | |
| 1891 | log.err("Unable to symlink {s} -> {s}", .{ major_only_path, out_basename }); | |
| 1892 | return err; | |
| 1893 | }; | |
| 1894 | // sym link for libfoo.so to libfoo.so.1 | |
| 1895 | const name_only_path = try fs.path.join( | |
| 1896 | allocator, | |
| 1897 | &[_][]const u8{ out_dir, filename_name_only }, | |
| 1898 | ); | |
| 1899 | fs.atomicSymLink(allocator, filename_major_only, name_only_path) catch |err| { | |
| 1900 | log.err("Unable to symlink {s} -> {s}", .{ name_only_path, filename_major_only }); | |
| 1901 | return err; | |
| 1902 | }; | |
| 1903 | } | |
| 1904 | ||
| 1905 | fn execPkgConfigList(self: *std.Build, out_code: *u8) (PkgConfigError || ExecError)![]const PkgConfigPkg { | |
| 1906 | const stdout = try self.execAllowFail(&[_][]const u8{ "pkg-config", "--list-all" }, out_code, .Ignore); | |
| 1907 | var list = ArrayList(PkgConfigPkg).init(self.allocator); | |
| 1908 | errdefer list.deinit(); | |
| 1909 | var line_it = mem.tokenize(u8, stdout, "\r\n"); | |
| 1910 | while (line_it.next()) |line| { | |
| 1911 | if (mem.trim(u8, line, " \t").len == 0) continue; | |
| 1912 | var tok_it = mem.tokenize(u8, line, " \t"); | |
| 1913 | try list.append(PkgConfigPkg{ | |
| 1914 | .name = tok_it.next() orelse return error.PkgConfigInvalidOutput, | |
| 1915 | .desc = tok_it.rest(), | |
| 1916 | }); | |
| 1917 | } | |
| 1918 | return list.toOwnedSlice(); | |
| 1919 | } | |
| 1920 | ||
| 1921 | fn getPkgConfigList(self: *std.Build) ![]const PkgConfigPkg { | |
| 1922 | if (self.pkg_config_pkg_list) |res| { | |
| 1923 | return res; | |
| 1924 | } | |
| 1925 | var code: u8 = undefined; | |
| 1926 | if (execPkgConfigList(self, &code)) |list| { | |
| 1927 | self.pkg_config_pkg_list = list; | |
| 1928 | return list; | |
| 1929 | } else |err| { | |
| 1930 | const result = switch (err) { | |
| 1931 | error.ProcessTerminated => error.PkgConfigCrashed, | |
| 1932 | error.ExecNotSupported => error.PkgConfigFailed, | |
| 1933 | error.ExitCodeFailure => error.PkgConfigFailed, | |
| 1934 | error.FileNotFound => error.PkgConfigNotInstalled, | |
| 1935 | error.InvalidName => error.PkgConfigNotInstalled, | |
| 1936 | error.PkgConfigInvalidOutput => error.PkgConfigInvalidOutput, | |
| 1937 | error.ChildExecFailed => error.PkgConfigFailed, | |
| 1938 | else => return err, | |
| 1939 | }; | |
| 1940 | self.pkg_config_pkg_list = result; | |
| 1941 | return result; | |
| 1942 | } | |
| 1943 | } | |
| 1944 | ||
| 1945 | test "addPackage" { | |
| 1946 | if (builtin.os.tag == .wasi) return error.SkipZigTest; | |
| 1947 | ||
| 1948 | var arena = std.heap.ArenaAllocator.init(std.testing.allocator); | |
| 1949 | defer arena.deinit(); | |
| 1950 | ||
| 1951 | const host = try NativeTargetInfo.detect(.{}); | |
| 1952 | ||
| 1953 | var builder = try std.Build.create( | |
| 1954 | arena.allocator(), | |
| 1955 | "test", | |
| 1956 | "test", | |
| 1957 | "test", | |
| 1958 | "test", | |
| 1959 | host, | |
| 1960 | ); | |
| 1961 | defer builder.destroy(); | |
| 1962 | ||
| 1963 | const pkg_dep = Pkg{ | |
| 1964 | .name = "pkg_dep", | |
| 1965 | .source = .{ .path = "/not/a/pkg_dep.zig" }, | |
| 1966 | }; | |
| 1967 | const pkg_top = Pkg{ | |
| 1968 | .name = "pkg_dep", | |
| 1969 | .source = .{ .path = "/not/a/pkg_top.zig" }, | |
| 1970 | .dependencies = &[_]Pkg{pkg_dep}, | |
| 1971 | }; | |
| 1972 | ||
| 1973 | var exe = builder.addExecutable(.{ | |
| 1974 | .name = "not_an_executable", | |
| 1975 | .root_source_file = .{ .path = "/not/an/executable.zig" }, | |
| 1976 | }); | |
| 1977 | exe.addPackage(pkg_top); | |
| 1978 | ||
| 1979 | try std.testing.expectEqual(@as(usize, 1), exe.packages.items.len); | |
| 1980 | ||
| 1981 | const dupe = exe.packages.items[0]; | |
| 1982 | try std.testing.expectEqualStrings(pkg_top.name, dupe.name); | |
| 1983 | } | |
| 1984 | ||
| 1985 | fn addFlag(args: *ArrayList([]const u8), comptime name: []const u8, opt: ?bool) !void { | |
| 1986 | const cond = opt orelse return; | |
| 1987 | try args.ensureUnusedCapacity(1); | |
| 1988 | if (cond) { | |
| 1989 | args.appendAssumeCapacity("-f" ++ name); | |
| 1990 | } else { | |
| 1991 | args.appendAssumeCapacity("-fno-" ++ name); | |
| 1992 | } | |
| 1993 | } | |
| 1994 | ||
| 1995 | const TransitiveDeps = struct { | |
| 1996 | link_objects: ArrayList(LinkObject), | |
| 1997 | seen_system_libs: StringHashMap(void), | |
| 1998 | seen_steps: std.AutoHashMap(*const Step, void), | |
| 1999 | is_linking_libcpp: bool, | |
| 2000 | is_linking_libc: bool, | |
| 2001 | frameworks: *StringHashMap(FrameworkLinkInfo), | |
| 2002 | ||
| 2003 | fn add(td: *TransitiveDeps, link_objects: []const LinkObject) !void { | |
| 2004 | try td.link_objects.ensureUnusedCapacity(link_objects.len); | |
| 2005 | ||
| 2006 | for (link_objects) |link_object| { | |
| 2007 | try td.link_objects.append(link_object); | |
| 2008 | switch (link_object) { | |
| 2009 | .other_step => |other| try addInner(td, other, other.isDynamicLibrary()), | |
| 2010 | else => {}, | |
| 2011 | } | |
| 2012 | } | |
| 2013 | } | |
| 2014 | ||
| 2015 | fn addInner(td: *TransitiveDeps, other: *CompileStep, dyn: bool) !void { | |
| 2016 | // Inherit dependency on libc and libc++ | |
| 2017 | td.is_linking_libcpp = td.is_linking_libcpp or other.is_linking_libcpp; | |
| 2018 | td.is_linking_libc = td.is_linking_libc or other.is_linking_libc; | |
| 2019 | ||
| 2020 | // Inherit dependencies on darwin frameworks | |
| 2021 | if (!dyn) { | |
| 2022 | var it = other.frameworks.iterator(); | |
| 2023 | while (it.next()) |framework| { | |
| 2024 | try td.frameworks.put(framework.key_ptr.*, framework.value_ptr.*); | |
| 2025 | } | |
| 2026 | } | |
| 2027 | ||
| 2028 | // Inherit dependencies on system libraries and static libraries. | |
| 2029 | for (other.link_objects.items) |other_link_object| { | |
| 2030 | switch (other_link_object) { | |
| 2031 | .system_lib => |system_lib| { | |
| 2032 | if ((try td.seen_system_libs.fetchPut(system_lib.name, {})) != null) | |
| 2033 | continue; | |
| 2034 | ||
| 2035 | if (dyn) | |
| 2036 | continue; | |
| 2037 | ||
| 2038 | try td.link_objects.append(other_link_object); | |
| 2039 | }, | |
| 2040 | .other_step => |inner_other| { | |
| 2041 | if ((try td.seen_steps.fetchPut(&inner_other.step, {})) != null) | |
| 2042 | continue; | |
| 2043 | ||
| 2044 | if (!dyn) | |
| 2045 | try td.link_objects.append(other_link_object); | |
| 2046 | ||
| 2047 | try addInner(td, inner_other, dyn or inner_other.isDynamicLibrary()); | |
| 2048 | }, | |
| 2049 | else => continue, | |
| 2050 | } | |
| 2051 | } | |
| 2052 | } | |
| 2053 | }; |
lib/std/Build/ConfigHeaderStep.zig created+299| ... | ... | @@ -0,0 +1,299 @@ |
| 1 | const std = @import("../std.zig"); | |
| 2 | const ConfigHeaderStep = @This(); | |
| 3 | const Step = std.Build.Step; | |
| 4 | ||
| 5 | pub const base_id: Step.Id = .config_header; | |
| 6 | ||
| 7 | pub const Style = enum { | |
| 8 | /// The configure format supported by autotools. It uses `#undef foo` to | |
| 9 | /// mark lines that can be substituted with different values. | |
| 10 | autoconf, | |
| 11 | /// The configure format supported by CMake. It uses `@@FOO@@` and | |
| 12 | /// `#cmakedefine` for template substitution. | |
| 13 | cmake, | |
| 14 | }; | |
| 15 | ||
| 16 | pub const Value = union(enum) { | |
| 17 | undef, | |
| 18 | defined, | |
| 19 | boolean: bool, | |
| 20 | int: i64, | |
| 21 | ident: []const u8, | |
| 22 | string: []const u8, | |
| 23 | }; | |
| 24 | ||
| 25 | step: Step, | |
| 26 | builder: *std.Build, | |
| 27 | source: std.Build.FileSource, | |
| 28 | style: Style, | |
| 29 | values: std.StringHashMap(Value), | |
| 30 | max_bytes: usize = 2 * 1024 * 1024, | |
| 31 | output_dir: []const u8, | |
| 32 | output_basename: []const u8, | |
| 33 | ||
| 34 | pub fn create(builder: *std.Build, source: std.Build.FileSource, style: Style) *ConfigHeaderStep { | |
| 35 | const self = builder.allocator.create(ConfigHeaderStep) catch @panic("OOM"); | |
| 36 | const name = builder.fmt("configure header {s}", .{source.getDisplayName()}); | |
| 37 | self.* = .{ | |
| 38 | .builder = builder, | |
| 39 | .step = Step.init(base_id, name, builder.allocator, make), | |
| 40 | .source = source, | |
| 41 | .style = style, | |
| 42 | .values = std.StringHashMap(Value).init(builder.allocator), | |
| 43 | .output_dir = undefined, | |
| 44 | .output_basename = "config.h", | |
| 45 | }; | |
| 46 | switch (source) { | |
| 47 | .path => |p| { | |
| 48 | const basename = std.fs.path.basename(p); | |
| 49 | if (std.mem.endsWith(u8, basename, ".h.in")) { | |
| 50 | self.output_basename = basename[0 .. basename.len - 3]; | |
| 51 | } | |
| 52 | }, | |
| 53 | else => {}, | |
| 54 | } | |
| 55 | return self; | |
| 56 | } | |
| 57 | ||
| 58 | pub fn addValues(self: *ConfigHeaderStep, values: anytype) void { | |
| 59 | return addValuesInner(self, values) catch @panic("OOM"); | |
| 60 | } | |
| 61 | ||
| 62 | fn addValuesInner(self: *ConfigHeaderStep, values: anytype) !void { | |
| 63 | inline for (@typeInfo(@TypeOf(values)).Struct.fields) |field| { | |
| 64 | try putValue(self, field.name, field.type, @field(values, field.name)); | |
| 65 | } | |
| 66 | } | |
| 67 | ||
| 68 | fn putValue(self: *ConfigHeaderStep, field_name: []const u8, comptime T: type, v: T) !void { | |
| 69 | switch (@typeInfo(T)) { | |
| 70 | .Null => { | |
| 71 | try self.values.put(field_name, .undef); | |
| 72 | }, | |
| 73 | .Void => { | |
| 74 | try self.values.put(field_name, .defined); | |
| 75 | }, | |
| 76 | .Bool => { | |
| 77 | try self.values.put(field_name, .{ .boolean = v }); | |
| 78 | }, | |
| 79 | .Int => { | |
| 80 | try self.values.put(field_name, .{ .int = v }); | |
| 81 | }, | |
| 82 | .ComptimeInt => { | |
| 83 | try self.values.put(field_name, .{ .int = v }); | |
| 84 | }, | |
| 85 | .EnumLiteral => { | |
| 86 | try self.values.put(field_name, .{ .ident = @tagName(v) }); | |
| 87 | }, | |
| 88 | .Optional => { | |
| 89 | if (v) |x| { | |
| 90 | return putValue(self, field_name, @TypeOf(x), x); | |
| 91 | } else { | |
| 92 | try self.values.put(field_name, .undef); | |
| 93 | } | |
| 94 | }, | |
| 95 | .Pointer => |ptr| { | |
| 96 | switch (@typeInfo(ptr.child)) { | |
| 97 | .Array => |array| { | |
| 98 | if (ptr.size == .One and array.child == u8) { | |
| 99 | try self.values.put(field_name, .{ .string = v }); | |
| 100 | return; | |
| 101 | } | |
| 102 | }, | |
| 103 | else => {}, | |
| 104 | } | |
| 105 | ||
| 106 | @compileError("unsupported ConfigHeaderStep value type: " ++ @typeName(T)); | |
| 107 | }, | |
| 108 | else => @compileError("unsupported ConfigHeaderStep value type: " ++ @typeName(T)), | |
| 109 | } | |
| 110 | } | |
| 111 | ||
| 112 | fn make(step: *Step) !void { | |
| 113 | const self = @fieldParentPtr(ConfigHeaderStep, "step", step); | |
| 114 | const gpa = self.builder.allocator; | |
| 115 | const src_path = self.source.getPath(self.builder); | |
| 116 | const contents = try std.fs.cwd().readFileAlloc(gpa, src_path, self.max_bytes); | |
| 117 | ||
| 118 | // The cache is used here not really as a way to speed things up - because writing | |
| 119 | // the data to a file would probably be very fast - but as a way to find a canonical | |
| 120 | // location to put build artifacts. | |
| 121 | ||
| 122 | // If, for example, a hard-coded path was used as the location to put ConfigHeaderStep | |
| 123 | // files, then two ConfigHeaderStep executing in parallel might clobber each other. | |
| 124 | ||
| 125 | // TODO port the cache system from the compiler to zig std lib. Until then | |
| 126 | // we construct the path directly, and no "cache hit" detection happens; | |
| 127 | // the files are always written. | |
| 128 | // Note there is very similar code over in WriteFileStep | |
| 129 | const Hasher = std.crypto.auth.siphash.SipHash128(1, 3); | |
| 130 | // Random bytes to make ConfigHeaderStep unique. Refresh this with new | |
| 131 | // random bytes when ConfigHeaderStep implementation is modified in a | |
| 132 | // non-backwards-compatible way. | |
| 133 | var hash = Hasher.init("X1pQzdDt91Zlh7Eh"); | |
| 134 | hash.update(self.source.getDisplayName()); | |
| 135 | hash.update(contents); | |
| 136 | ||
| 137 | var digest: [16]u8 = undefined; | |
| 138 | hash.final(&digest); | |
| 139 | var hash_basename: [digest.len * 2]u8 = undefined; | |
| 140 | _ = std.fmt.bufPrint( | |
| 141 | &hash_basename, | |
| 142 | "{s}", | |
| 143 | .{std.fmt.fmtSliceHexLower(&digest)}, | |
| 144 | ) catch unreachable; | |
| 145 | ||
| 146 | self.output_dir = try std.fs.path.join(gpa, &[_][]const u8{ | |
| 147 | self.builder.cache_root, "o", &hash_basename, | |
| 148 | }); | |
| 149 | var dir = std.fs.cwd().makeOpenPath(self.output_dir, .{}) catch |err| { | |
| 150 | std.debug.print("unable to make path {s}: {s}\n", .{ self.output_dir, @errorName(err) }); | |
| 151 | return err; | |
| 152 | }; | |
| 153 | defer dir.close(); | |
| 154 | ||
| 155 | var values_copy = try self.values.clone(); | |
| 156 | defer values_copy.deinit(); | |
| 157 | ||
| 158 | var output = std.ArrayList(u8).init(gpa); | |
| 159 | defer output.deinit(); | |
| 160 | try output.ensureTotalCapacity(contents.len); | |
| 161 | ||
| 162 | try output.appendSlice("/* This file was generated by ConfigHeaderStep using the Zig Build System. */\n"); | |
| 163 | ||
| 164 | switch (self.style) { | |
| 165 | .autoconf => try render_autoconf(contents, &output, &values_copy, src_path), | |
| 166 | .cmake => try render_cmake(contents, &output, &values_copy, src_path), | |
| 167 | } | |
| 168 | ||
| 169 | try dir.writeFile(self.output_basename, output.items); | |
| 170 | } | |
| 171 | ||
| 172 | fn render_autoconf( | |
| 173 | contents: []const u8, | |
| 174 | output: *std.ArrayList(u8), | |
| 175 | values_copy: *std.StringHashMap(Value), | |
| 176 | src_path: []const u8, | |
| 177 | ) !void { | |
| 178 | var any_errors = false; | |
| 179 | var line_index: u32 = 0; | |
| 180 | var line_it = std.mem.split(u8, contents, "\n"); | |
| 181 | while (line_it.next()) |line| : (line_index += 1) { | |
| 182 | if (!std.mem.startsWith(u8, line, "#")) { | |
| 183 | try output.appendSlice(line); | |
| 184 | try output.appendSlice("\n"); | |
| 185 | continue; | |
| 186 | } | |
| 187 | var it = std.mem.tokenize(u8, line[1..], " \t\r"); | |
| 188 | const undef = it.next().?; | |
| 189 | if (!std.mem.eql(u8, undef, "undef")) { | |
| 190 | try output.appendSlice(line); | |
| 191 | try output.appendSlice("\n"); | |
| 192 | continue; | |
| 193 | } | |
| 194 | const name = it.rest(); | |
| 195 | const kv = values_copy.fetchRemove(name) orelse { | |
| 196 | std.debug.print("{s}:{d}: error: unspecified config header value: '{s}'\n", .{ | |
| 197 | src_path, line_index + 1, name, | |
| 198 | }); | |
| 199 | any_errors = true; | |
| 200 | continue; | |
| 201 | }; | |
| 202 | try renderValue(output, name, kv.value); | |
| 203 | } | |
| 204 | ||
| 205 | { | |
| 206 | var it = values_copy.iterator(); | |
| 207 | while (it.next()) |entry| { | |
| 208 | const name = entry.key_ptr.*; | |
| 209 | std.debug.print("{s}: error: config header value unused: '{s}'\n", .{ src_path, name }); | |
| 210 | } | |
| 211 | } | |
| 212 | ||
| 213 | if (any_errors) { | |
| 214 | return error.HeaderConfigFailed; | |
| 215 | } | |
| 216 | } | |
| 217 | ||
| 218 | fn render_cmake( | |
| 219 | contents: []const u8, | |
| 220 | output: *std.ArrayList(u8), | |
| 221 | values_copy: *std.StringHashMap(Value), | |
| 222 | src_path: []const u8, | |
| 223 | ) !void { | |
| 224 | var any_errors = false; | |
| 225 | var line_index: u32 = 0; | |
| 226 | var line_it = std.mem.split(u8, contents, "\n"); | |
| 227 | while (line_it.next()) |line| : (line_index += 1) { | |
| 228 | if (!std.mem.startsWith(u8, line, "#")) { | |
| 229 | try output.appendSlice(line); | |
| 230 | try output.appendSlice("\n"); | |
| 231 | continue; | |
| 232 | } | |
| 233 | var it = std.mem.tokenize(u8, line[1..], " \t\r"); | |
| 234 | const cmakedefine = it.next().?; | |
| 235 | if (!std.mem.eql(u8, cmakedefine, "cmakedefine")) { | |
| 236 | try output.appendSlice(line); | |
| 237 | try output.appendSlice("\n"); | |
| 238 | continue; | |
| 239 | } | |
| 240 | const name = it.next() orelse { | |
| 241 | std.debug.print("{s}:{d}: error: missing define name\n", .{ | |
| 242 | src_path, line_index + 1, | |
| 243 | }); | |
| 244 | any_errors = true; | |
| 245 | continue; | |
| 246 | }; | |
| 247 | const kv = values_copy.fetchRemove(name) orelse { | |
| 248 | std.debug.print("{s}:{d}: error: unspecified config header value: '{s}'\n", .{ | |
| 249 | src_path, line_index + 1, name, | |
| 250 | }); | |
| 251 | any_errors = true; | |
| 252 | continue; | |
| 253 | }; | |
| 254 | try renderValue(output, name, kv.value); | |
| 255 | } | |
| 256 | ||
| 257 | { | |
| 258 | var it = values_copy.iterator(); | |
| 259 | while (it.next()) |entry| { | |
| 260 | const name = entry.key_ptr.*; | |
| 261 | std.debug.print("{s}: error: config header value unused: '{s}'\n", .{ src_path, name }); | |
| 262 | } | |
| 263 | } | |
| 264 | ||
| 265 | if (any_errors) { | |
| 266 | return error.HeaderConfigFailed; | |
| 267 | } | |
| 268 | } | |
| 269 | ||
| 270 | fn renderValue(output: *std.ArrayList(u8), name: []const u8, value: Value) !void { | |
| 271 | switch (value) { | |
| 272 | .undef => { | |
| 273 | try output.appendSlice("/* #undef "); | |
| 274 | try output.appendSlice(name); | |
| 275 | try output.appendSlice(" */\n"); | |
| 276 | }, | |
| 277 | .defined => { | |
| 278 | try output.appendSlice("#define "); | |
| 279 | try output.appendSlice(name); | |
| 280 | try output.appendSlice("\n"); | |
| 281 | }, | |
| 282 | .boolean => |b| { | |
| 283 | try output.appendSlice("#define "); | |
| 284 | try output.appendSlice(name); | |
| 285 | try output.appendSlice(" "); | |
| 286 | try output.appendSlice(if (b) "true\n" else "false\n"); | |
| 287 | }, | |
| 288 | .int => |i| { | |
| 289 | try output.writer().print("#define {s} {d}\n", .{ name, i }); | |
| 290 | }, | |
| 291 | .ident => |ident| { | |
| 292 | try output.writer().print("#define {s} {s}\n", .{ name, ident }); | |
| 293 | }, | |
| 294 | .string => |string| { | |
| 295 | // TODO: use C-specific escaping instead of zig string literals | |
| 296 | try output.writer().print("#define {s} \"{}\"\n", .{ name, std.zig.fmtEscapes(string) }); | |
| 297 | }, | |
| 298 | } | |
| 299 | } |
lib/std/Build/EmulatableRunStep.zig created+213| ... | ... | @@ -0,0 +1,213 @@ |
| 1 | //! Unlike `RunStep` this step will provide emulation, when enabled, to run foreign binaries. | |
| 2 | //! When a binary is foreign, but emulation for the target is disabled, the specified binary | |
| 3 | //! will not be run and therefore also not validated against its output. | |
| 4 | //! This step can be useful when wishing to run a built binary on multiple platforms, | |
| 5 | //! without having to verify if it's possible to be ran against. | |
| 6 | ||
| 7 | const std = @import("../std.zig"); | |
| 8 | const Step = std.Build.Step; | |
| 9 | const CompileStep = std.Build.CompileStep; | |
| 10 | const RunStep = std.Build.RunStep; | |
| 11 | ||
| 12 | const fs = std.fs; | |
| 13 | const process = std.process; | |
| 14 | const EnvMap = process.EnvMap; | |
| 15 | ||
| 16 | const EmulatableRunStep = @This(); | |
| 17 | ||
| 18 | pub const base_id = .emulatable_run; | |
| 19 | ||
| 20 | const max_stdout_size = 1 * 1024 * 1024; // 1 MiB | |
| 21 | ||
| 22 | step: Step, | |
| 23 | builder: *std.Build, | |
| 24 | ||
| 25 | /// The artifact (executable) to be run by this step | |
| 26 | exe: *CompileStep, | |
| 27 | ||
| 28 | /// Set this to `null` to ignore the exit code for the purpose of determining a successful execution | |
| 29 | expected_exit_code: ?u8 = 0, | |
| 30 | ||
| 31 | /// Override this field to modify the environment | |
| 32 | env_map: ?*EnvMap, | |
| 33 | ||
| 34 | /// Set this to modify the current working directory | |
| 35 | cwd: ?[]const u8, | |
| 36 | ||
| 37 | stdout_action: RunStep.StdIoAction = .inherit, | |
| 38 | stderr_action: RunStep.StdIoAction = .inherit, | |
| 39 | ||
| 40 | /// When set to true, hides the warning of skipping a foreign binary which cannot be run on the host | |
| 41 | /// or through emulation. | |
| 42 | hide_foreign_binaries_warning: bool, | |
| 43 | ||
| 44 | /// Creates a step that will execute the given artifact. This step will allow running the | |
| 45 | /// binary through emulation when any of the emulation options such as `enable_rosetta` are set to true. | |
| 46 | /// When set to false, and the binary is foreign, running the executable is skipped. | |
| 47 | /// Asserts given artifact is an executable. | |
| 48 | pub fn create(builder: *std.Build, name: []const u8, artifact: *CompileStep) *EmulatableRunStep { | |
| 49 | std.debug.assert(artifact.kind == .exe or artifact.kind == .test_exe); | |
| 50 | const self = builder.allocator.create(EmulatableRunStep) catch @panic("OOM"); | |
| 51 | ||
| 52 | const option_name = "hide-foreign-warnings"; | |
| 53 | const hide_warnings = if (builder.available_options_map.get(option_name) == null) warn: { | |
| 54 | break :warn builder.option(bool, option_name, "Hide the warning when a foreign binary which is incompatible is skipped") orelse false; | |
| 55 | } else false; | |
| 56 | ||
| 57 | self.* = .{ | |
| 58 | .builder = builder, | |
| 59 | .step = Step.init(.emulatable_run, name, builder.allocator, make), | |
| 60 | .exe = artifact, | |
| 61 | .env_map = null, | |
| 62 | .cwd = null, | |
| 63 | .hide_foreign_binaries_warning = hide_warnings, | |
| 64 | }; | |
| 65 | self.step.dependOn(&artifact.step); | |
| 66 | ||
| 67 | return self; | |
| 68 | } | |
| 69 | ||
| 70 | fn make(step: *Step) !void { | |
| 71 | const self = @fieldParentPtr(EmulatableRunStep, "step", step); | |
| 72 | const host_info = self.builder.host; | |
| 73 | ||
| 74 | var argv_list = std.ArrayList([]const u8).init(self.builder.allocator); | |
| 75 | defer argv_list.deinit(); | |
| 76 | ||
| 77 | const need_cross_glibc = self.exe.target.isGnuLibC() and self.exe.is_linking_libc; | |
| 78 | switch (host_info.getExternalExecutor(self.exe.target_info, .{ | |
| 79 | .qemu_fixes_dl = need_cross_glibc and self.builder.glibc_runtimes_dir != null, | |
| 80 | .link_libc = self.exe.is_linking_libc, | |
| 81 | })) { | |
| 82 | .native => {}, | |
| 83 | .rosetta => if (!self.builder.enable_rosetta) return warnAboutForeignBinaries(self), | |
| 84 | .wine => |bin_name| if (self.builder.enable_wine) { | |
| 85 | try argv_list.append(bin_name); | |
| 86 | } else return, | |
| 87 | .qemu => |bin_name| if (self.builder.enable_qemu) { | |
| 88 | const glibc_dir_arg = if (need_cross_glibc) | |
| 89 | self.builder.glibc_runtimes_dir orelse return | |
| 90 | else | |
| 91 | null; | |
| 92 | try argv_list.append(bin_name); | |
| 93 | if (glibc_dir_arg) |dir| { | |
| 94 | // TODO look into making this a call to `linuxTriple`. This | |
| 95 | // needs the directory to be called "i686" rather than | |
| 96 | // "x86" which is why we do it manually here. | |
| 97 | const fmt_str = "{s}" ++ fs.path.sep_str ++ "{s}-{s}-{s}"; | |
| 98 | const cpu_arch = self.exe.target.getCpuArch(); | |
| 99 | const os_tag = self.exe.target.getOsTag(); | |
| 100 | const abi = self.exe.target.getAbi(); | |
| 101 | const cpu_arch_name: []const u8 = if (cpu_arch == .x86) | |
| 102 | "i686" | |
| 103 | else | |
| 104 | @tagName(cpu_arch); | |
| 105 | const full_dir = try std.fmt.allocPrint(self.builder.allocator, fmt_str, .{ | |
| 106 | dir, cpu_arch_name, @tagName(os_tag), @tagName(abi), | |
| 107 | }); | |
| 108 | ||
| 109 | try argv_list.append("-L"); | |
| 110 | try argv_list.append(full_dir); | |
| 111 | } | |
| 112 | } else return warnAboutForeignBinaries(self), | |
| 113 | .darling => |bin_name| if (self.builder.enable_darling) { | |
| 114 | try argv_list.append(bin_name); | |
| 115 | } else return warnAboutForeignBinaries(self), | |
| 116 | .wasmtime => |bin_name| if (self.builder.enable_wasmtime) { | |
| 117 | try argv_list.append(bin_name); | |
| 118 | try argv_list.append("--dir=."); | |
| 119 | } else return warnAboutForeignBinaries(self), | |
| 120 | else => return warnAboutForeignBinaries(self), | |
| 121 | } | |
| 122 | ||
| 123 | if (self.exe.target.isWindows()) { | |
| 124 | // On Windows we don't have rpaths so we have to add .dll search paths to PATH | |
| 125 | RunStep.addPathForDynLibsInternal(&self.step, self.builder, self.exe); | |
| 126 | } | |
| 127 | ||
| 128 | const executable_path = self.exe.installed_path orelse self.exe.getOutputSource().getPath(self.builder); | |
| 129 | try argv_list.append(executable_path); | |
| 130 | ||
| 131 | try RunStep.runCommand( | |
| 132 | argv_list.items, | |
| 133 | self.builder, | |
| 134 | self.expected_exit_code, | |
| 135 | self.stdout_action, | |
| 136 | self.stderr_action, | |
| 137 | .Inherit, | |
| 138 | self.env_map, | |
| 139 | self.cwd, | |
| 140 | false, | |
| 141 | ); | |
| 142 | } | |
| 143 | ||
| 144 | pub fn expectStdErrEqual(self: *EmulatableRunStep, bytes: []const u8) void { | |
| 145 | self.stderr_action = .{ .expect_exact = self.builder.dupe(bytes) }; | |
| 146 | } | |
| 147 | ||
| 148 | pub fn expectStdOutEqual(self: *EmulatableRunStep, bytes: []const u8) void { | |
| 149 | self.stdout_action = .{ .expect_exact = self.builder.dupe(bytes) }; | |
| 150 | } | |
| 151 | ||
| 152 | fn warnAboutForeignBinaries(step: *EmulatableRunStep) void { | |
| 153 | if (step.hide_foreign_binaries_warning) return; | |
| 154 | const builder = step.builder; | |
| 155 | const artifact = step.exe; | |
| 156 | ||
| 157 | const host_name = builder.host.target.zigTriple(builder.allocator) catch @panic("unhandled error"); | |
| 158 | const foreign_name = artifact.target.zigTriple(builder.allocator) catch @panic("unhandled error"); | |
| 159 | const target_info = std.zig.system.NativeTargetInfo.detect(artifact.target) catch @panic("unhandled error"); | |
| 160 | const need_cross_glibc = artifact.target.isGnuLibC() and artifact.is_linking_libc; | |
| 161 | switch (builder.host.getExternalExecutor(target_info, .{ | |
| 162 | .qemu_fixes_dl = need_cross_glibc and builder.glibc_runtimes_dir != null, | |
| 163 | .link_libc = artifact.is_linking_libc, | |
| 164 | })) { | |
| 165 | .native => unreachable, | |
| 166 | .bad_dl => |foreign_dl| { | |
| 167 | const host_dl = builder.host.dynamic_linker.get() orelse "(none)"; | |
| 168 | std.debug.print("the host system does not appear to be capable of executing binaries from the target because the host dynamic linker is '{s}', while the target dynamic linker is '{s}'. Consider setting the dynamic linker as '{s}'.\n", .{ | |
| 169 | host_dl, foreign_dl, host_dl, | |
| 170 | }); | |
| 171 | }, | |
| 172 | .bad_os_or_cpu => { | |
| 173 | std.debug.print("the host system ({s}) does not appear to be capable of executing binaries from the target ({s}).\n", .{ | |
| 174 | host_name, foreign_name, | |
| 175 | }); | |
| 176 | }, | |
| 177 | .darling => if (!builder.enable_darling) { | |
| 178 | std.debug.print( | |
| 179 | "the host system ({s}) does not appear to be capable of executing binaries " ++ | |
| 180 | "from the target ({s}). Consider enabling darling.\n", | |
| 181 | .{ host_name, foreign_name }, | |
| 182 | ); | |
| 183 | }, | |
| 184 | .rosetta => if (!builder.enable_rosetta) { | |
| 185 | std.debug.print( | |
| 186 | "the host system ({s}) does not appear to be capable of executing binaries " ++ | |
| 187 | "from the target ({s}). Consider enabling rosetta.\n", | |
| 188 | .{ host_name, foreign_name }, | |
| 189 | ); | |
| 190 | }, | |
| 191 | .wine => if (!builder.enable_wine) { | |
| 192 | std.debug.print( | |
| 193 | "the host system ({s}) does not appear to be capable of executing binaries " ++ | |
| 194 | "from the target ({s}). Consider enabling wine.\n", | |
| 195 | .{ host_name, foreign_name }, | |
| 196 | ); | |
| 197 | }, | |
| 198 | .qemu => if (!builder.enable_qemu) { | |
| 199 | std.debug.print( | |
| 200 | "the host system ({s}) does not appear to be capable of executing binaries " ++ | |
| 201 | "from the target ({s}). Consider enabling qemu.\n", | |
| 202 | .{ host_name, foreign_name }, | |
| 203 | ); | |
| 204 | }, | |
| 205 | .wasmtime => { | |
| 206 | std.debug.print( | |
| 207 | "the host system ({s}) does not appear to be capable of executing binaries " ++ | |
| 208 | "from the target ({s}). Consider enabling wasmtime.\n", | |
| 209 | .{ host_name, foreign_name }, | |
| 210 | ); | |
| 211 | }, | |
| 212 | } | |
| 213 | } |
lib/std/Build/FmtStep.zig created+32| ... | ... | @@ -0,0 +1,32 @@ |
| 1 | const std = @import("../std.zig"); | |
| 2 | const Step = std.Build.Step; | |
| 3 | const FmtStep = @This(); | |
| 4 | ||
| 5 | pub const base_id = .fmt; | |
| 6 | ||
| 7 | step: Step, | |
| 8 | builder: *std.Build, | |
| 9 | argv: [][]const u8, | |
| 10 | ||
| 11 | pub fn create(builder: *std.Build, paths: []const []const u8) *FmtStep { | |
| 12 | const self = builder.allocator.create(FmtStep) catch @panic("OOM"); | |
| 13 | const name = "zig fmt"; | |
| 14 | self.* = FmtStep{ | |
| 15 | .step = Step.init(.fmt, name, builder.allocator, make), | |
| 16 | .builder = builder, | |
| 17 | .argv = builder.allocator.alloc([]u8, paths.len + 2) catch @panic("OOM"), | |
| 18 | }; | |
| 19 | ||
| 20 | self.argv[0] = builder.zig_exe; | |
| 21 | self.argv[1] = "fmt"; | |
| 22 | for (paths) |path, i| { | |
| 23 | self.argv[2 + i] = builder.pathFromRoot(path); | |
| 24 | } | |
| 25 | return self; | |
| 26 | } | |
| 27 | ||
| 28 | fn make(step: *Step) !void { | |
| 29 | const self = @fieldParentPtr(FmtStep, "step", step); | |
| 30 | ||
| 31 | return self.builder.spawnChild(self.argv); | |
| 32 | } |
lib/std/Build/InstallArtifactStep.zig created+85| ... | ... | @@ -0,0 +1,85 @@ |
| 1 | const std = @import("../std.zig"); | |
| 2 | const Step = std.Build.Step; | |
| 3 | const CompileStep = std.Build.CompileStep; | |
| 4 | const InstallDir = std.Build.InstallDir; | |
| 5 | const InstallArtifactStep = @This(); | |
| 6 | ||
| 7 | pub const base_id = .install_artifact; | |
| 8 | ||
| 9 | step: Step, | |
| 10 | builder: *std.Build, | |
| 11 | artifact: *CompileStep, | |
| 12 | dest_dir: InstallDir, | |
| 13 | pdb_dir: ?InstallDir, | |
| 14 | h_dir: ?InstallDir, | |
| 15 | ||
| 16 | pub fn create(builder: *std.Build, artifact: *CompileStep) *InstallArtifactStep { | |
| 17 | if (artifact.install_step) |s| return s; | |
| 18 | ||
| 19 | const self = builder.allocator.create(InstallArtifactStep) catch @panic("OOM"); | |
| 20 | self.* = InstallArtifactStep{ | |
| 21 | .builder = builder, | |
| 22 | .step = Step.init(.install_artifact, builder.fmt("install {s}", .{artifact.step.name}), builder.allocator, make), | |
| 23 | .artifact = artifact, | |
| 24 | .dest_dir = artifact.override_dest_dir orelse switch (artifact.kind) { | |
| 25 | .obj => @panic("Cannot install a .obj build artifact."), | |
| 26 | .@"test" => @panic("Cannot install a .test build artifact, use .test_exe instead."), | |
| 27 | .exe, .test_exe => InstallDir{ .bin = {} }, | |
| 28 | .lib => InstallDir{ .lib = {} }, | |
| 29 | }, | |
| 30 | .pdb_dir = if (artifact.producesPdbFile()) blk: { | |
| 31 | if (artifact.kind == .exe or artifact.kind == .test_exe) { | |
| 32 | break :blk InstallDir{ .bin = {} }; | |
| 33 | } else { | |
| 34 | break :blk InstallDir{ .lib = {} }; | |
| 35 | } | |
| 36 | } else null, | |
| 37 | .h_dir = if (artifact.kind == .lib and artifact.emit_h) .header else null, | |
| 38 | }; | |
| 39 | self.step.dependOn(&artifact.step); | |
| 40 | artifact.install_step = self; | |
| 41 | ||
| 42 | builder.pushInstalledFile(self.dest_dir, artifact.out_filename); | |
| 43 | if (self.artifact.isDynamicLibrary()) { | |
| 44 | if (artifact.major_only_filename) |name| { | |
| 45 | builder.pushInstalledFile(.lib, name); | |
| 46 | } | |
| 47 | if (artifact.name_only_filename) |name| { | |
| 48 | builder.pushInstalledFile(.lib, name); | |
| 49 | } | |
| 50 | if (self.artifact.target.isWindows()) { | |
| 51 | builder.pushInstalledFile(.lib, artifact.out_lib_filename); | |
| 52 | } | |
| 53 | } | |
| 54 | if (self.pdb_dir) |pdb_dir| { | |
| 55 | builder.pushInstalledFile(pdb_dir, artifact.out_pdb_filename); | |
| 56 | } | |
| 57 | if (self.h_dir) |h_dir| { | |
| 58 | builder.pushInstalledFile(h_dir, artifact.out_h_filename); | |
| 59 | } | |
| 60 | return self; | |
| 61 | } | |
| 62 | ||
| 63 | fn make(step: *Step) !void { | |
| 64 | const self = @fieldParentPtr(InstallArtifactStep, "step", step); | |
| 65 | const builder = self.builder; | |
| 66 | ||
| 67 | const full_dest_path = builder.getInstallPath(self.dest_dir, self.artifact.out_filename); | |
| 68 | try builder.updateFile(self.artifact.getOutputSource().getPath(builder), full_dest_path); | |
| 69 | if (self.artifact.isDynamicLibrary() and self.artifact.version != null and self.artifact.target.wantSharedLibSymLinks()) { | |
| 70 | try CompileStep.doAtomicSymLinks(builder.allocator, full_dest_path, self.artifact.major_only_filename.?, self.artifact.name_only_filename.?); | |
| 71 | } | |
| 72 | if (self.artifact.isDynamicLibrary() and self.artifact.target.isWindows() and self.artifact.emit_implib != .no_emit) { | |
| 73 | const full_implib_path = builder.getInstallPath(self.dest_dir, self.artifact.out_lib_filename); | |
| 74 | try builder.updateFile(self.artifact.getOutputLibSource().getPath(builder), full_implib_path); | |
| 75 | } | |
| 76 | if (self.pdb_dir) |pdb_dir| { | |
| 77 | const full_pdb_path = builder.getInstallPath(pdb_dir, self.artifact.out_pdb_filename); | |
| 78 | try builder.updateFile(self.artifact.getOutputPdbSource().getPath(builder), full_pdb_path); | |
| 79 | } | |
| 80 | if (self.h_dir) |h_dir| { | |
| 81 | const full_h_path = builder.getInstallPath(h_dir, self.artifact.out_h_filename); | |
| 82 | try builder.updateFile(self.artifact.getOutputHSource().getPath(builder), full_h_path); | |
| 83 | } | |
| 84 | self.artifact.installed_path = full_dest_path; | |
| 85 | } |
lib/std/Build/InstallDirStep.zig created+93| ... | ... | @@ -0,0 +1,93 @@ |
| 1 | const std = @import("../std.zig"); | |
| 2 | const mem = std.mem; | |
| 3 | const fs = std.fs; | |
| 4 | const Step = std.Build.Step; | |
| 5 | const InstallDir = std.Build.InstallDir; | |
| 6 | const InstallDirStep = @This(); | |
| 7 | const log = std.log; | |
| 8 | ||
| 9 | step: Step, | |
| 10 | builder: *std.Build, | |
| 11 | options: Options, | |
| 12 | /// This is used by the build system when a file being installed comes from one | |
| 13 | /// package but is being installed by another. | |
| 14 | override_source_builder: ?*std.Build = null, | |
| 15 | ||
| 16 | pub const base_id = .install_dir; | |
| 17 | ||
| 18 | pub const Options = struct { | |
| 19 | source_dir: []const u8, | |
| 20 | install_dir: InstallDir, | |
| 21 | install_subdir: []const u8, | |
| 22 | /// File paths which end in any of these suffixes will be excluded | |
| 23 | /// from being installed. | |
| 24 | exclude_extensions: []const []const u8 = &.{}, | |
| 25 | /// File paths which end in any of these suffixes will result in | |
| 26 | /// empty files being installed. This is mainly intended for large | |
| 27 | /// test.zig files in order to prevent needless installation bloat. | |
| 28 | /// However if the files were not present at all, then | |
| 29 | /// `@import("test.zig")` would be a compile error. | |
| 30 | blank_extensions: []const []const u8 = &.{}, | |
| 31 | ||
| 32 | fn dupe(self: Options, b: *std.Build) Options { | |
| 33 | return .{ | |
| 34 | .source_dir = b.dupe(self.source_dir), | |
| 35 | .install_dir = self.install_dir.dupe(b), | |
| 36 | .install_subdir = b.dupe(self.install_subdir), | |
| 37 | .exclude_extensions = b.dupeStrings(self.exclude_extensions), | |
| 38 | .blank_extensions = b.dupeStrings(self.blank_extensions), | |
| 39 | }; | |
| 40 | } | |
| 41 | }; | |
| 42 | ||
| 43 | pub fn init( | |
| 44 | builder: *std.Build, | |
| 45 | options: Options, | |
| 46 | ) InstallDirStep { | |
| 47 | builder.pushInstalledFile(options.install_dir, options.install_subdir); | |
| 48 | return InstallDirStep{ | |
| 49 | .builder = builder, | |
| 50 | .step = Step.init(.install_dir, builder.fmt("install {s}/", .{options.source_dir}), builder.allocator, make), | |
| 51 | .options = options.dupe(builder), | |
| 52 | }; | |
| 53 | } | |
| 54 | ||
| 55 | fn make(step: *Step) !void { | |
| 56 | const self = @fieldParentPtr(InstallDirStep, "step", step); | |
| 57 | const dest_prefix = self.builder.getInstallPath(self.options.install_dir, self.options.install_subdir); | |
| 58 | const src_builder = self.override_source_builder orelse self.builder; | |
| 59 | const full_src_dir = src_builder.pathFromRoot(self.options.source_dir); | |
| 60 | var src_dir = std.fs.cwd().openIterableDir(full_src_dir, .{}) catch |err| { | |
| 61 | log.err("InstallDirStep: unable to open source directory '{s}': {s}", .{ | |
| 62 | full_src_dir, @errorName(err), | |
| 63 | }); | |
| 64 | return error.StepFailed; | |
| 65 | }; | |
| 66 | defer src_dir.close(); | |
| 67 | var it = try src_dir.walk(self.builder.allocator); | |
| 68 | next_entry: while (try it.next()) |entry| { | |
| 69 | for (self.options.exclude_extensions) |ext| { | |
| 70 | if (mem.endsWith(u8, entry.path, ext)) { | |
| 71 | continue :next_entry; | |
| 72 | } | |
| 73 | } | |
| 74 | ||
| 75 | const full_path = self.builder.pathJoin(&.{ full_src_dir, entry.path }); | |
| 76 | const dest_path = self.builder.pathJoin(&.{ dest_prefix, entry.path }); | |
| 77 | ||
| 78 | switch (entry.kind) { | |
| 79 | .Directory => try fs.cwd().makePath(dest_path), | |
| 80 | .File => { | |
| 81 | for (self.options.blank_extensions) |ext| { | |
| 82 | if (mem.endsWith(u8, entry.path, ext)) { | |
| 83 | try self.builder.truncateFile(dest_path); | |
| 84 | continue :next_entry; | |
| 85 | } | |
| 86 | } | |
| 87 | ||
| 88 | try self.builder.updateFile(full_path, dest_path); | |
| 89 | }, | |
| 90 | else => continue, | |
| 91 | } | |
| 92 | } | |
| 93 | } |
lib/std/Build/InstallFileStep.zig created+40| ... | ... | @@ -0,0 +1,40 @@ |
| 1 | const std = @import("../std.zig"); | |
| 2 | const Step = std.Build.Step; | |
| 3 | const FileSource = std.Build.FileSource; | |
| 4 | const InstallDir = std.Build.InstallDir; | |
| 5 | const InstallFileStep = @This(); | |
| 6 | ||
| 7 | pub const base_id = .install_file; | |
| 8 | ||
| 9 | step: Step, | |
| 10 | builder: *std.Build, | |
| 11 | source: FileSource, | |
| 12 | dir: InstallDir, | |
| 13 | dest_rel_path: []const u8, | |
| 14 | /// This is used by the build system when a file being installed comes from one | |
| 15 | /// package but is being installed by another. | |
| 16 | override_source_builder: ?*std.Build = null, | |
| 17 | ||
| 18 | pub fn init( | |
| 19 | builder: *std.Build, | |
| 20 | source: FileSource, | |
| 21 | dir: InstallDir, | |
| 22 | dest_rel_path: []const u8, | |
| 23 | ) InstallFileStep { | |
| 24 | builder.pushInstalledFile(dir, dest_rel_path); | |
| 25 | return InstallFileStep{ | |
| 26 | .builder = builder, | |
| 27 | .step = Step.init(.install_file, builder.fmt("install {s} to {s}", .{ source.getDisplayName(), dest_rel_path }), builder.allocator, make), | |
| 28 | .source = source.dupe(builder), | |
| 29 | .dir = dir.dupe(builder), | |
| 30 | .dest_rel_path = builder.dupePath(dest_rel_path), | |
| 31 | }; | |
| 32 | } | |
| 33 | ||
| 34 | fn make(step: *Step) !void { | |
| 35 | const self = @fieldParentPtr(InstallFileStep, "step", step); | |
| 36 | const src_builder = self.override_source_builder orelse self.builder; | |
| 37 | const full_src_path = self.source.getPath(src_builder); | |
| 38 | const full_dest_path = self.builder.getInstallPath(self.dir, self.dest_rel_path); | |
| 39 | try self.builder.updateFile(full_src_path, full_dest_path); | |
| 40 | } |
lib/std/Build/InstallRawStep.zig created+110| ... | ... | @@ -0,0 +1,110 @@ |
| 1 | //! TODO: Rename this to ObjCopyStep now that it invokes the `zig objcopy` | |
| 2 | //! subcommand rather than containing an implementation directly. | |
| 3 | ||
| 4 | const std = @import("std"); | |
| 5 | const InstallRawStep = @This(); | |
| 6 | ||
| 7 | const Allocator = std.mem.Allocator; | |
| 8 | const ArenaAllocator = std.heap.ArenaAllocator; | |
| 9 | const ArrayListUnmanaged = std.ArrayListUnmanaged; | |
| 10 | const File = std.fs.File; | |
| 11 | const InstallDir = std.Build.InstallDir; | |
| 12 | const CompileStep = std.Build.CompileStep; | |
| 13 | const Step = std.Build.Step; | |
| 14 | const elf = std.elf; | |
| 15 | const fs = std.fs; | |
| 16 | const io = std.io; | |
| 17 | const sort = std.sort; | |
| 18 | ||
| 19 | pub const base_id = .install_raw; | |
| 20 | ||
| 21 | pub const RawFormat = enum { | |
| 22 | bin, | |
| 23 | hex, | |
| 24 | }; | |
| 25 | ||
| 26 | step: Step, | |
| 27 | builder: *std.Build, | |
| 28 | artifact: *CompileStep, | |
| 29 | dest_dir: InstallDir, | |
| 30 | dest_filename: []const u8, | |
| 31 | options: CreateOptions, | |
| 32 | output_file: std.Build.GeneratedFile, | |
| 33 | ||
| 34 | pub const CreateOptions = struct { | |
| 35 | format: ?RawFormat = null, | |
| 36 | dest_dir: ?InstallDir = null, | |
| 37 | only_section: ?[]const u8 = null, | |
| 38 | pad_to: ?u64 = null, | |
| 39 | }; | |
| 40 | ||
| 41 | pub fn create( | |
| 42 | builder: *std.Build, | |
| 43 | artifact: *CompileStep, | |
| 44 | dest_filename: []const u8, | |
| 45 | options: CreateOptions, | |
| 46 | ) *InstallRawStep { | |
| 47 | const self = builder.allocator.create(InstallRawStep) catch @panic("OOM"); | |
| 48 | self.* = InstallRawStep{ | |
| 49 | .step = Step.init(.install_raw, builder.fmt("install raw binary {s}", .{artifact.step.name}), builder.allocator, make), | |
| 50 | .builder = builder, | |
| 51 | .artifact = artifact, | |
| 52 | .dest_dir = if (options.dest_dir) |d| d else switch (artifact.kind) { | |
| 53 | .obj => unreachable, | |
| 54 | .@"test" => unreachable, | |
| 55 | .exe, .test_exe => .bin, | |
| 56 | .lib => unreachable, | |
| 57 | }, | |
| 58 | .dest_filename = dest_filename, | |
| 59 | .options = options, | |
| 60 | .output_file = std.Build.GeneratedFile{ .step = &self.step }, | |
| 61 | }; | |
| 62 | self.step.dependOn(&artifact.step); | |
| 63 | ||
| 64 | builder.pushInstalledFile(self.dest_dir, dest_filename); | |
| 65 | return self; | |
| 66 | } | |
| 67 | ||
| 68 | pub fn getOutputSource(self: *const InstallRawStep) std.Build.FileSource { | |
| 69 | return std.Build.FileSource{ .generated = &self.output_file }; | |
| 70 | } | |
| 71 | ||
| 72 | fn make(step: *Step) !void { | |
| 73 | const self = @fieldParentPtr(InstallRawStep, "step", step); | |
| 74 | const b = self.builder; | |
| 75 | ||
| 76 | if (self.artifact.target.getObjectFormat() != .elf) { | |
| 77 | std.debug.print("InstallRawStep only works with ELF format.\n", .{}); | |
| 78 | return error.InvalidObjectFormat; | |
| 79 | } | |
| 80 | ||
| 81 | const full_src_path = self.artifact.getOutputSource().getPath(b); | |
| 82 | const full_dest_path = b.getInstallPath(self.dest_dir, self.dest_filename); | |
| 83 | self.output_file.path = full_dest_path; | |
| 84 | ||
| 85 | try fs.cwd().makePath(b.getInstallPath(self.dest_dir, "")); | |
| 86 | ||
| 87 | var argv_list = std.ArrayList([]const u8).init(b.allocator); | |
| 88 | try argv_list.appendSlice(&.{ b.zig_exe, "objcopy" }); | |
| 89 | ||
| 90 | if (self.options.only_section) |only_section| { | |
| 91 | try argv_list.appendSlice(&.{ "-j", only_section }); | |
| 92 | } | |
| 93 | if (self.options.pad_to) |pad_to| { | |
| 94 | try argv_list.appendSlice(&.{ | |
| 95 | "--pad-to", | |
| 96 | b.fmt("{d}", .{pad_to}), | |
| 97 | }); | |
| 98 | } | |
| 99 | if (self.options.format) |format| switch (format) { | |
| 100 | .bin => try argv_list.appendSlice(&.{ "-O", "binary" }), | |
| 101 | .hex => try argv_list.appendSlice(&.{ "-O", "hex" }), | |
| 102 | }; | |
| 103 | ||
| 104 | try argv_list.appendSlice(&.{ full_src_path, full_dest_path }); | |
| 105 | _ = try self.builder.execFromStep(argv_list.items, &self.step); | |
| 106 | } | |
| 107 | ||
| 108 | test { | |
| 109 | std.testing.refAllDecls(InstallRawStep); | |
| 110 | } |
lib/std/Build/LogStep.zig created+23| ... | ... | @@ -0,0 +1,23 @@ |
| 1 | const std = @import("../std.zig"); | |
| 2 | const log = std.log; | |
| 3 | const Step = std.Build.Step; | |
| 4 | const LogStep = @This(); | |
| 5 | ||
| 6 | pub const base_id = .log; | |
| 7 | ||
| 8 | step: Step, | |
| 9 | builder: *std.Build, | |
| 10 | data: []const u8, | |
| 11 | ||
| 12 | pub fn init(builder: *std.Build, data: []const u8) LogStep { | |
| 13 | return LogStep{ | |
| 14 | .builder = builder, | |
| 15 | .step = Step.init(.log, builder.fmt("log {s}", .{data}), builder.allocator, make), | |
| 16 | .data = builder.dupe(data), | |
| 17 | }; | |
| 18 | } | |
| 19 | ||
| 20 | fn make(step: *Step) anyerror!void { | |
| 21 | const self = @fieldParentPtr(LogStep, "step", step); | |
| 22 | log.info("{s}", .{self.data}); | |
| 23 | } |
lib/std/Build/OptionsStep.zig created+371| ... | ... | @@ -0,0 +1,371 @@ |
| 1 | const std = @import("../std.zig"); | |
| 2 | const builtin = @import("builtin"); | |
| 3 | const fs = std.fs; | |
| 4 | const Step = std.Build.Step; | |
| 5 | const GeneratedFile = std.Build.GeneratedFile; | |
| 6 | const CompileStep = std.Build.CompileStep; | |
| 7 | const FileSource = std.Build.FileSource; | |
| 8 | ||
| 9 | const OptionsStep = @This(); | |
| 10 | ||
| 11 | pub const base_id = .options; | |
| 12 | ||
| 13 | step: Step, | |
| 14 | generated_file: GeneratedFile, | |
| 15 | builder: *std.Build, | |
| 16 | ||
| 17 | contents: std.ArrayList(u8), | |
| 18 | artifact_args: std.ArrayList(OptionArtifactArg), | |
| 19 | file_source_args: std.ArrayList(OptionFileSourceArg), | |
| 20 | ||
| 21 | pub fn create(builder: *std.Build) *OptionsStep { | |
| 22 | const self = builder.allocator.create(OptionsStep) catch @panic("OOM"); | |
| 23 | self.* = .{ | |
| 24 | .builder = builder, | |
| 25 | .step = Step.init(.options, "options", builder.allocator, make), | |
| 26 | .generated_file = undefined, | |
| 27 | .contents = std.ArrayList(u8).init(builder.allocator), | |
| 28 | .artifact_args = std.ArrayList(OptionArtifactArg).init(builder.allocator), | |
| 29 | .file_source_args = std.ArrayList(OptionFileSourceArg).init(builder.allocator), | |
| 30 | }; | |
| 31 | self.generated_file = .{ .step = &self.step }; | |
| 32 | ||
| 33 | return self; | |
| 34 | } | |
| 35 | ||
| 36 | pub fn addOption(self: *OptionsStep, comptime T: type, name: []const u8, value: T) void { | |
| 37 | return addOptionFallible(self, T, name, value) catch @panic("unhandled error"); | |
| 38 | } | |
| 39 | ||
| 40 | fn addOptionFallible(self: *OptionsStep, comptime T: type, name: []const u8, value: T) !void { | |
| 41 | const out = self.contents.writer(); | |
| 42 | switch (T) { | |
| 43 | []const []const u8 => { | |
| 44 | try out.print("pub const {}: []const []const u8 = &[_][]const u8{{\n", .{std.zig.fmtId(name)}); | |
| 45 | for (value) |slice| { | |
| 46 | try out.print(" \"{}\",\n", .{std.zig.fmtEscapes(slice)}); | |
| 47 | } | |
| 48 | try out.writeAll("};\n"); | |
| 49 | return; | |
| 50 | }, | |
| 51 | [:0]const u8 => { | |
| 52 | try out.print("pub const {}: [:0]const u8 = \"{}\";\n", .{ std.zig.fmtId(name), std.zig.fmtEscapes(value) }); | |
| 53 | return; | |
| 54 | }, | |
| 55 | []const u8 => { | |
| 56 | try out.print("pub const {}: []const u8 = \"{}\";\n", .{ std.zig.fmtId(name), std.zig.fmtEscapes(value) }); | |
| 57 | return; | |
| 58 | }, | |
| 59 | ?[:0]const u8 => { | |
| 60 | try out.print("pub const {}: ?[:0]const u8 = ", .{std.zig.fmtId(name)}); | |
| 61 | if (value) |payload| { | |
| 62 | try out.print("\"{}\";\n", .{std.zig.fmtEscapes(payload)}); | |
| 63 | } else { | |
| 64 | try out.writeAll("null;\n"); | |
| 65 | } | |
| 66 | return; | |
| 67 | }, | |
| 68 | ?[]const u8 => { | |
| 69 | try out.print("pub const {}: ?[]const u8 = ", .{std.zig.fmtId(name)}); | |
| 70 | if (value) |payload| { | |
| 71 | try out.print("\"{}\";\n", .{std.zig.fmtEscapes(payload)}); | |
| 72 | } else { | |
| 73 | try out.writeAll("null;\n"); | |
| 74 | } | |
| 75 | return; | |
| 76 | }, | |
| 77 | std.builtin.Version => { | |
| 78 | try out.print( | |
| 79 | \\pub const {}: @import("std").builtin.Version = .{{ | |
| 80 | \\ .major = {d}, | |
| 81 | \\ .minor = {d}, | |
| 82 | \\ .patch = {d}, | |
| 83 | \\}}; | |
| 84 | \\ | |
| 85 | , .{ | |
| 86 | std.zig.fmtId(name), | |
| 87 | ||
| 88 | value.major, | |
| 89 | value.minor, | |
| 90 | value.patch, | |
| 91 | }); | |
| 92 | return; | |
| 93 | }, | |
| 94 | std.SemanticVersion => { | |
| 95 | try out.print( | |
| 96 | \\pub const {}: @import("std").SemanticVersion = .{{ | |
| 97 | \\ .major = {d}, | |
| 98 | \\ .minor = {d}, | |
| 99 | \\ .patch = {d}, | |
| 100 | \\ | |
| 101 | , .{ | |
| 102 | std.zig.fmtId(name), | |
| 103 | ||
| 104 | value.major, | |
| 105 | value.minor, | |
| 106 | value.patch, | |
| 107 | }); | |
| 108 | if (value.pre) |some| { | |
| 109 | try out.print(" .pre = \"{}\",\n", .{std.zig.fmtEscapes(some)}); | |
| 110 | } | |
| 111 | if (value.build) |some| { | |
| 112 | try out.print(" .build = \"{}\",\n", .{std.zig.fmtEscapes(some)}); | |
| 113 | } | |
| 114 | try out.writeAll("};\n"); | |
| 115 | return; | |
| 116 | }, | |
| 117 | else => {}, | |
| 118 | } | |
| 119 | switch (@typeInfo(T)) { | |
| 120 | .Enum => |enum_info| { | |
| 121 | try out.print("pub const {} = enum {{\n", .{std.zig.fmtId(@typeName(T))}); | |
| 122 | inline for (enum_info.fields) |field| { | |
| 123 | try out.print(" {},\n", .{std.zig.fmtId(field.name)}); | |
| 124 | } | |
| 125 | try out.writeAll("};\n"); | |
| 126 | try out.print("pub const {}: {s} = {s}.{s};\n", .{ | |
| 127 | std.zig.fmtId(name), | |
| 128 | std.zig.fmtId(@typeName(T)), | |
| 129 | std.zig.fmtId(@typeName(T)), | |
| 130 | std.zig.fmtId(@tagName(value)), | |
| 131 | }); | |
| 132 | return; | |
| 133 | }, | |
| 134 | else => {}, | |
| 135 | } | |
| 136 | try out.print("pub const {}: {s} = ", .{ std.zig.fmtId(name), @typeName(T) }); | |
| 137 | try printLiteral(out, value, 0); | |
| 138 | try out.writeAll(";\n"); | |
| 139 | } | |
| 140 | ||
| 141 | // TODO: non-recursive? | |
| 142 | fn printLiteral(out: anytype, val: anytype, indent: u8) !void { | |
| 143 | const T = @TypeOf(val); | |
| 144 | switch (@typeInfo(T)) { | |
| 145 | .Array => { | |
| 146 | try out.print("{s} {{\n", .{@typeName(T)}); | |
| 147 | for (val) |item| { | |
| 148 | try out.writeByteNTimes(' ', indent + 4); | |
| 149 | try printLiteral(out, item, indent + 4); | |
| 150 | try out.writeAll(",\n"); | |
| 151 | } | |
| 152 | try out.writeByteNTimes(' ', indent); | |
| 153 | try out.writeAll("}"); | |
| 154 | }, | |
| 155 | .Pointer => |p| { | |
| 156 | if (p.size != .Slice) { | |
| 157 | @compileError("Non-slice pointers are not yet supported in build options"); | |
| 158 | } | |
| 159 | try out.print("&[_]{s} {{\n", .{@typeName(p.child)}); | |
| 160 | for (val) |item| { | |
| 161 | try out.writeByteNTimes(' ', indent + 4); | |
| 162 | try printLiteral(out, item, indent + 4); | |
| 163 | try out.writeAll(",\n"); | |
| 164 | } | |
| 165 | try out.writeByteNTimes(' ', indent); | |
| 166 | try out.writeAll("}"); | |
| 167 | }, | |
| 168 | .Optional => { | |
| 169 | if (val) |inner| { | |
| 170 | return printLiteral(out, inner, indent); | |
| 171 | } else { | |
| 172 | return out.writeAll("null"); | |
| 173 | } | |
| 174 | }, | |
| 175 | .Void, | |
| 176 | .Bool, | |
| 177 | .Int, | |
| 178 | .ComptimeInt, | |
| 179 | .Float, | |
| 180 | .Null, | |
| 181 | => try out.print("{any}", .{val}), | |
| 182 | else => @compileError(std.fmt.comptimePrint("`{s}` are not yet supported as build options", .{@tagName(@typeInfo(T))})), | |
| 183 | } | |
| 184 | } | |
| 185 | ||
| 186 | /// The value is the path in the cache dir. | |
| 187 | /// Adds a dependency automatically. | |
| 188 | pub fn addOptionFileSource( | |
| 189 | self: *OptionsStep, | |
| 190 | name: []const u8, | |
| 191 | source: FileSource, | |
| 192 | ) void { | |
| 193 | self.file_source_args.append(.{ | |
| 194 | .name = name, | |
| 195 | .source = source.dupe(self.builder), | |
| 196 | }) catch @panic("OOM"); | |
| 197 | source.addStepDependencies(&self.step); | |
| 198 | } | |
| 199 | ||
| 200 | /// The value is the path in the cache dir. | |
| 201 | /// Adds a dependency automatically. | |
| 202 | pub fn addOptionArtifact(self: *OptionsStep, name: []const u8, artifact: *CompileStep) void { | |
| 203 | self.artifact_args.append(.{ .name = self.builder.dupe(name), .artifact = artifact }) catch @panic("OOM"); | |
| 204 | self.step.dependOn(&artifact.step); | |
| 205 | } | |
| 206 | ||
| 207 | pub fn getPackage(self: *OptionsStep, package_name: []const u8) std.Build.Pkg { | |
| 208 | return .{ .name = package_name, .source = self.getSource() }; | |
| 209 | } | |
| 210 | ||
| 211 | pub fn getSource(self: *OptionsStep) FileSource { | |
| 212 | return .{ .generated = &self.generated_file }; | |
| 213 | } | |
| 214 | ||
| 215 | fn make(step: *Step) !void { | |
| 216 | const self = @fieldParentPtr(OptionsStep, "step", step); | |
| 217 | ||
| 218 | for (self.artifact_args.items) |item| { | |
| 219 | self.addOption( | |
| 220 | []const u8, | |
| 221 | item.name, | |
| 222 | self.builder.pathFromRoot(item.artifact.getOutputSource().getPath(self.builder)), | |
| 223 | ); | |
| 224 | } | |
| 225 | ||
| 226 | for (self.file_source_args.items) |item| { | |
| 227 | self.addOption( | |
| 228 | []const u8, | |
| 229 | item.name, | |
| 230 | item.source.getPath(self.builder), | |
| 231 | ); | |
| 232 | } | |
| 233 | ||
| 234 | const options_directory = self.builder.pathFromRoot( | |
| 235 | try fs.path.join( | |
| 236 | self.builder.allocator, | |
| 237 | &[_][]const u8{ self.builder.cache_root, "options" }, | |
| 238 | ), | |
| 239 | ); | |
| 240 | ||
| 241 | try fs.cwd().makePath(options_directory); | |
| 242 | ||
| 243 | const options_file = try fs.path.join( | |
| 244 | self.builder.allocator, | |
| 245 | &[_][]const u8{ options_directory, &self.hashContentsToFileName() }, | |
| 246 | ); | |
| 247 | ||
| 248 | try fs.cwd().writeFile(options_file, self.contents.items); | |
| 249 | ||
| 250 | self.generated_file.path = options_file; | |
| 251 | } | |
| 252 | ||
| 253 | fn hashContentsToFileName(self: *OptionsStep) [64]u8 { | |
| 254 | // This implementation is copied from `WriteFileStep.make` | |
| 255 | ||
| 256 | var hash = std.crypto.hash.blake2.Blake2b384.init(.{}); | |
| 257 | ||
| 258 | // Random bytes to make OptionsStep unique. Refresh this with | |
| 259 | // new random bytes when OptionsStep implementation is modified | |
| 260 | // in a non-backwards-compatible way. | |
| 261 | hash.update("yL0Ya4KkmcCjBlP8"); | |
| 262 | hash.update(self.contents.items); | |
| 263 | ||
| 264 | var digest: [48]u8 = undefined; | |
| 265 | hash.final(&digest); | |
| 266 | var hash_basename: [64]u8 = undefined; | |
| 267 | _ = fs.base64_encoder.encode(&hash_basename, &digest); | |
| 268 | return hash_basename; | |
| 269 | } | |
| 270 | ||
| 271 | const OptionArtifactArg = struct { | |
| 272 | name: []const u8, | |
| 273 | artifact: *CompileStep, | |
| 274 | }; | |
| 275 | ||
| 276 | const OptionFileSourceArg = struct { | |
| 277 | name: []const u8, | |
| 278 | source: FileSource, | |
| 279 | }; | |
| 280 | ||
| 281 | test "OptionsStep" { | |
| 282 | if (builtin.os.tag == .wasi) return error.SkipZigTest; | |
| 283 | ||
| 284 | var arena = std.heap.ArenaAllocator.init(std.testing.allocator); | |
| 285 | defer arena.deinit(); | |
| 286 | ||
| 287 | const host = try std.zig.system.NativeTargetInfo.detect(.{}); | |
| 288 | ||
| 289 | var builder = try std.Build.create( | |
| 290 | arena.allocator(), | |
| 291 | "test", | |
| 292 | "test", | |
| 293 | "test", | |
| 294 | "test", | |
| 295 | host, | |
| 296 | ); | |
| 297 | defer builder.destroy(); | |
| 298 | ||
| 299 | const options = builder.addOptions(); | |
| 300 | ||
| 301 | // TODO this regressed at some point | |
| 302 | //const KeywordEnum = enum { | |
| 303 | // @"0.8.1", | |
| 304 | //}; | |
| 305 | ||
| 306 | const nested_array = [2][2]u16{ | |
| 307 | [2]u16{ 300, 200 }, | |
| 308 | [2]u16{ 300, 200 }, | |
| 309 | }; | |
| 310 | const nested_slice: []const []const u16 = &[_][]const u16{ &nested_array[0], &nested_array[1] }; | |
| 311 | ||
| 312 | options.addOption(usize, "option1", 1); | |
| 313 | options.addOption(?usize, "option2", null); | |
| 314 | options.addOption(?usize, "option3", 3); | |
| 315 | options.addOption(comptime_int, "option4", 4); | |
| 316 | options.addOption([]const u8, "string", "zigisthebest"); | |
| 317 | options.addOption(?[]const u8, "optional_string", null); | |
| 318 | options.addOption([2][2]u16, "nested_array", nested_array); | |
| 319 | options.addOption([]const []const u16, "nested_slice", nested_slice); | |
| 320 | //options.addOption(KeywordEnum, "keyword_enum", .@"0.8.1"); | |
| 321 | options.addOption(std.builtin.Version, "version", try std.builtin.Version.parse("0.1.2")); | |
| 322 | options.addOption(std.SemanticVersion, "semantic_version", try std.SemanticVersion.parse("0.1.2-foo+bar")); | |
| 323 | ||
| 324 | try std.testing.expectEqualStrings( | |
| 325 | \\pub const option1: usize = 1; | |
| 326 | \\pub const option2: ?usize = null; | |
| 327 | \\pub const option3: ?usize = 3; | |
| 328 | \\pub const option4: comptime_int = 4; | |
| 329 | \\pub const string: []const u8 = "zigisthebest"; | |
| 330 | \\pub const optional_string: ?[]const u8 = null; | |
| 331 | \\pub const nested_array: [2][2]u16 = [2][2]u16 { | |
| 332 | \\ [2]u16 { | |
| 333 | \\ 300, | |
| 334 | \\ 200, | |
| 335 | \\ }, | |
| 336 | \\ [2]u16 { | |
| 337 | \\ 300, | |
| 338 | \\ 200, | |
| 339 | \\ }, | |
| 340 | \\}; | |
| 341 | \\pub const nested_slice: []const []const u16 = &[_][]const u16 { | |
| 342 | \\ &[_]u16 { | |
| 343 | \\ 300, | |
| 344 | \\ 200, | |
| 345 | \\ }, | |
| 346 | \\ &[_]u16 { | |
| 347 | \\ 300, | |
| 348 | \\ 200, | |
| 349 | \\ }, | |
| 350 | \\}; | |
| 351 | //\\pub const KeywordEnum = enum { | |
| 352 | //\\ @"0.8.1", | |
| 353 | //\\}; | |
| 354 | //\\pub const keyword_enum: KeywordEnum = KeywordEnum.@"0.8.1"; | |
| 355 | \\pub const version: @import("std").builtin.Version = .{ | |
| 356 | \\ .major = 0, | |
| 357 | \\ .minor = 1, | |
| 358 | \\ .patch = 2, | |
| 359 | \\}; | |
| 360 | \\pub const semantic_version: @import("std").SemanticVersion = .{ | |
| 361 | \\ .major = 0, | |
| 362 | \\ .minor = 1, | |
| 363 | \\ .patch = 2, | |
| 364 | \\ .pre = "foo", | |
| 365 | \\ .build = "bar", | |
| 366 | \\}; | |
| 367 | \\ | |
| 368 | , options.contents.items); | |
| 369 | ||
| 370 | _ = try std.zig.Ast.parse(arena.allocator(), try options.contents.toOwnedSliceSentinel(0), .zig); | |
| 371 | } |
lib/std/Build/RemoveDirStep.zig created+29| ... | ... | @@ -0,0 +1,29 @@ |
| 1 | const std = @import("../std.zig"); | |
| 2 | const log = std.log; | |
| 3 | const fs = std.fs; | |
| 4 | const Step = std.Build.Step; | |
| 5 | const RemoveDirStep = @This(); | |
| 6 | ||
| 7 | pub const base_id = .remove_dir; | |
| 8 | ||
| 9 | step: Step, | |
| 10 | builder: *std.Build, | |
| 11 | dir_path: []const u8, | |
| 12 | ||
| 13 | pub fn init(builder: *std.Build, dir_path: []const u8) RemoveDirStep { | |
| 14 | return RemoveDirStep{ | |
| 15 | .builder = builder, | |
| 16 | .step = Step.init(.remove_dir, builder.fmt("RemoveDir {s}", .{dir_path}), builder.allocator, make), | |
| 17 | .dir_path = builder.dupePath(dir_path), | |
| 18 | }; | |
| 19 | } | |
| 20 | ||
| 21 | fn make(step: *Step) !void { | |
| 22 | const self = @fieldParentPtr(RemoveDirStep, "step", step); | |
| 23 | ||
| 24 | const full_path = self.builder.pathFromRoot(self.dir_path); | |
| 25 | fs.cwd().deleteTree(full_path) catch |err| { | |
| 26 | log.err("Unable to remove {s}: {s}", .{ full_path, @errorName(err) }); | |
| 27 | return err; | |
| 28 | }; | |
| 29 | } |
lib/std/Build/RunStep.zig created+376| ... | ... | @@ -0,0 +1,376 @@ |
| 1 | const std = @import("../std.zig"); | |
| 2 | const builtin = @import("builtin"); | |
| 3 | const Step = std.Build.Step; | |
| 4 | const CompileStep = std.Build.CompileStep; | |
| 5 | const WriteFileStep = std.Build.WriteFileStep; | |
| 6 | const fs = std.fs; | |
| 7 | const mem = std.mem; | |
| 8 | const process = std.process; | |
| 9 | const ArrayList = std.ArrayList; | |
| 10 | const EnvMap = process.EnvMap; | |
| 11 | const Allocator = mem.Allocator; | |
| 12 | const ExecError = std.Build.ExecError; | |
| 13 | ||
| 14 | const max_stdout_size = 1 * 1024 * 1024; // 1 MiB | |
| 15 | ||
| 16 | const RunStep = @This(); | |
| 17 | ||
| 18 | pub const base_id: Step.Id = .run; | |
| 19 | ||
| 20 | step: Step, | |
| 21 | builder: *std.Build, | |
| 22 | ||
| 23 | /// See also addArg and addArgs to modifying this directly | |
| 24 | argv: ArrayList(Arg), | |
| 25 | ||
| 26 | /// Set this to modify the current working directory | |
| 27 | cwd: ?[]const u8, | |
| 28 | ||
| 29 | /// Override this field to modify the environment, or use setEnvironmentVariable | |
| 30 | env_map: ?*EnvMap, | |
| 31 | ||
| 32 | stdout_action: StdIoAction = .inherit, | |
| 33 | stderr_action: StdIoAction = .inherit, | |
| 34 | ||
| 35 | stdin_behavior: std.ChildProcess.StdIo = .Inherit, | |
| 36 | ||
| 37 | /// Set this to `null` to ignore the exit code for the purpose of determining a successful execution | |
| 38 | expected_exit_code: ?u8 = 0, | |
| 39 | ||
| 40 | /// Print the command before running it | |
| 41 | print: bool, | |
| 42 | ||
| 43 | pub const StdIoAction = union(enum) { | |
| 44 | inherit, | |
| 45 | ignore, | |
| 46 | expect_exact: []const u8, | |
| 47 | expect_matches: []const []const u8, | |
| 48 | }; | |
| 49 | ||
| 50 | pub const Arg = union(enum) { | |
| 51 | artifact: *CompileStep, | |
| 52 | file_source: std.Build.FileSource, | |
| 53 | bytes: []u8, | |
| 54 | }; | |
| 55 | ||
| 56 | pub fn create(builder: *std.Build, name: []const u8) *RunStep { | |
| 57 | const self = builder.allocator.create(RunStep) catch @panic("OOM"); | |
| 58 | self.* = RunStep{ | |
| 59 | .builder = builder, | |
| 60 | .step = Step.init(base_id, name, builder.allocator, make), | |
| 61 | .argv = ArrayList(Arg).init(builder.allocator), | |
| 62 | .cwd = null, | |
| 63 | .env_map = null, | |
| 64 | .print = builder.verbose, | |
| 65 | }; | |
| 66 | return self; | |
| 67 | } | |
| 68 | ||
| 69 | pub fn addArtifactArg(self: *RunStep, artifact: *CompileStep) void { | |
| 70 | self.argv.append(Arg{ .artifact = artifact }) catch @panic("OOM"); | |
| 71 | self.step.dependOn(&artifact.step); | |
| 72 | } | |
| 73 | ||
| 74 | pub fn addFileSourceArg(self: *RunStep, file_source: std.Build.FileSource) void { | |
| 75 | self.argv.append(Arg{ | |
| 76 | .file_source = file_source.dupe(self.builder), | |
| 77 | }) catch @panic("OOM"); | |
| 78 | file_source.addStepDependencies(&self.step); | |
| 79 | } | |
| 80 | ||
| 81 | pub fn addArg(self: *RunStep, arg: []const u8) void { | |
| 82 | self.argv.append(Arg{ .bytes = self.builder.dupe(arg) }) catch @panic("OOM"); | |
| 83 | } | |
| 84 | ||
| 85 | pub fn addArgs(self: *RunStep, args: []const []const u8) void { | |
| 86 | for (args) |arg| { | |
| 87 | self.addArg(arg); | |
| 88 | } | |
| 89 | } | |
| 90 | ||
| 91 | pub fn clearEnvironment(self: *RunStep) void { | |
| 92 | const new_env_map = self.builder.allocator.create(EnvMap) catch @panic("OOM"); | |
| 93 | new_env_map.* = EnvMap.init(self.builder.allocator); | |
| 94 | self.env_map = new_env_map; | |
| 95 | } | |
| 96 | ||
| 97 | pub fn addPathDir(self: *RunStep, search_path: []const u8) void { | |
| 98 | addPathDirInternal(&self.step, self.builder, search_path); | |
| 99 | } | |
| 100 | ||
| 101 | /// For internal use only, users of `RunStep` should use `addPathDir` directly. | |
| 102 | pub fn addPathDirInternal(step: *Step, builder: *std.Build, search_path: []const u8) void { | |
| 103 | const env_map = getEnvMapInternal(step, builder.allocator); | |
| 104 | ||
| 105 | const key = "PATH"; | |
| 106 | var prev_path = env_map.get(key); | |
| 107 | ||
| 108 | if (prev_path) |pp| { | |
| 109 | const new_path = builder.fmt("{s}" ++ [1]u8{fs.path.delimiter} ++ "{s}", .{ pp, search_path }); | |
| 110 | env_map.put(key, new_path) catch @panic("OOM"); | |
| 111 | } else { | |
| 112 | env_map.put(key, builder.dupePath(search_path)) catch @panic("OOM"); | |
| 113 | } | |
| 114 | } | |
| 115 | ||
| 116 | pub fn getEnvMap(self: *RunStep) *EnvMap { | |
| 117 | return getEnvMapInternal(&self.step, self.builder.allocator); | |
| 118 | } | |
| 119 | ||
| 120 | fn getEnvMapInternal(step: *Step, allocator: Allocator) *EnvMap { | |
| 121 | const maybe_env_map = switch (step.id) { | |
| 122 | .run => step.cast(RunStep).?.env_map, | |
| 123 | .emulatable_run => step.cast(std.Build.EmulatableRunStep).?.env_map, | |
| 124 | else => unreachable, | |
| 125 | }; | |
| 126 | return maybe_env_map orelse { | |
| 127 | const env_map = allocator.create(EnvMap) catch @panic("OOM"); | |
| 128 | env_map.* = process.getEnvMap(allocator) catch @panic("unhandled error"); | |
| 129 | switch (step.id) { | |
| 130 | .run => step.cast(RunStep).?.env_map = env_map, | |
| 131 | .emulatable_run => step.cast(RunStep).?.env_map = env_map, | |
| 132 | else => unreachable, | |
| 133 | } | |
| 134 | return env_map; | |
| 135 | }; | |
| 136 | } | |
| 137 | ||
| 138 | pub fn setEnvironmentVariable(self: *RunStep, key: []const u8, value: []const u8) void { | |
| 139 | const env_map = self.getEnvMap(); | |
| 140 | env_map.put( | |
| 141 | self.builder.dupe(key), | |
| 142 | self.builder.dupe(value), | |
| 143 | ) catch @panic("unhandled error"); | |
| 144 | } | |
| 145 | ||
| 146 | pub fn expectStdErrEqual(self: *RunStep, bytes: []const u8) void { | |
| 147 | self.stderr_action = .{ .expect_exact = self.builder.dupe(bytes) }; | |
| 148 | } | |
| 149 | ||
| 150 | pub fn expectStdOutEqual(self: *RunStep, bytes: []const u8) void { | |
| 151 | self.stdout_action = .{ .expect_exact = self.builder.dupe(bytes) }; | |
| 152 | } | |
| 153 | ||
| 154 | fn stdIoActionToBehavior(action: StdIoAction) std.ChildProcess.StdIo { | |
| 155 | return switch (action) { | |
| 156 | .ignore => .Ignore, | |
| 157 | .inherit => .Inherit, | |
| 158 | .expect_exact, .expect_matches => .Pipe, | |
| 159 | }; | |
| 160 | } | |
| 161 | ||
| 162 | fn make(step: *Step) !void { | |
| 163 | const self = @fieldParentPtr(RunStep, "step", step); | |
| 164 | ||
| 165 | var argv_list = ArrayList([]const u8).init(self.builder.allocator); | |
| 166 | for (self.argv.items) |arg| { | |
| 167 | switch (arg) { | |
| 168 | .bytes => |bytes| try argv_list.append(bytes), | |
| 169 | .file_source => |file| try argv_list.append(file.getPath(self.builder)), | |
| 170 | .artifact => |artifact| { | |
| 171 | if (artifact.target.isWindows()) { | |
| 172 | // On Windows we don't have rpaths so we have to add .dll search paths to PATH | |
| 173 | self.addPathForDynLibs(artifact); | |
| 174 | } | |
| 175 | const executable_path = artifact.installed_path orelse artifact.getOutputSource().getPath(self.builder); | |
| 176 | try argv_list.append(executable_path); | |
| 177 | }, | |
| 178 | } | |
| 179 | } | |
| 180 | ||
| 181 | try runCommand( | |
| 182 | argv_list.items, | |
| 183 | self.builder, | |
| 184 | self.expected_exit_code, | |
| 185 | self.stdout_action, | |
| 186 | self.stderr_action, | |
| 187 | self.stdin_behavior, | |
| 188 | self.env_map, | |
| 189 | self.cwd, | |
| 190 | self.print, | |
| 191 | ); | |
| 192 | } | |
| 193 | ||
| 194 | pub fn runCommand( | |
| 195 | argv: []const []const u8, | |
| 196 | builder: *std.Build, | |
| 197 | expected_exit_code: ?u8, | |
| 198 | stdout_action: StdIoAction, | |
| 199 | stderr_action: StdIoAction, | |
| 200 | stdin_behavior: std.ChildProcess.StdIo, | |
| 201 | env_map: ?*EnvMap, | |
| 202 | maybe_cwd: ?[]const u8, | |
| 203 | print: bool, | |
| 204 | ) !void { | |
| 205 | const cwd = if (maybe_cwd) |cwd| builder.pathFromRoot(cwd) else builder.build_root; | |
| 206 | ||
| 207 | if (!std.process.can_spawn) { | |
| 208 | const cmd = try std.mem.join(builder.allocator, " ", argv); | |
| 209 | std.debug.print("the following command cannot be executed ({s} does not support spawning a child process):\n{s}", .{ @tagName(builtin.os.tag), cmd }); | |
| 210 | builder.allocator.free(cmd); | |
| 211 | return ExecError.ExecNotSupported; | |
| 212 | } | |
| 213 | ||
| 214 | var child = std.ChildProcess.init(argv, builder.allocator); | |
| 215 | child.cwd = cwd; | |
| 216 | child.env_map = env_map orelse builder.env_map; | |
| 217 | ||
| 218 | child.stdin_behavior = stdin_behavior; | |
| 219 | child.stdout_behavior = stdIoActionToBehavior(stdout_action); | |
| 220 | child.stderr_behavior = stdIoActionToBehavior(stderr_action); | |
| 221 | ||
| 222 | if (print) | |
| 223 | printCmd(cwd, argv); | |
| 224 | ||
| 225 | child.spawn() catch |err| { | |
| 226 | std.debug.print("Unable to spawn {s}: {s}\n", .{ argv[0], @errorName(err) }); | |
| 227 | return err; | |
| 228 | }; | |
| 229 | ||
| 230 | // TODO need to poll to read these streams to prevent a deadlock (or rely on evented I/O). | |
| 231 | ||
| 232 | var stdout: ?[]const u8 = null; | |
| 233 | defer if (stdout) |s| builder.allocator.free(s); | |
| 234 | ||
| 235 | switch (stdout_action) { | |
| 236 | .expect_exact, .expect_matches => { | |
| 237 | stdout = try child.stdout.?.reader().readAllAlloc(builder.allocator, max_stdout_size); | |
| 238 | }, | |
| 239 | .inherit, .ignore => {}, | |
| 240 | } | |
| 241 | ||
| 242 | var stderr: ?[]const u8 = null; | |
| 243 | defer if (stderr) |s| builder.allocator.free(s); | |
| 244 | ||
| 245 | switch (stderr_action) { | |
| 246 | .expect_exact, .expect_matches => { | |
| 247 | stderr = try child.stderr.?.reader().readAllAlloc(builder.allocator, max_stdout_size); | |
| 248 | }, | |
| 249 | .inherit, .ignore => {}, | |
| 250 | } | |
| 251 | ||
| 252 | const term = child.wait() catch |err| { | |
| 253 | std.debug.print("Unable to spawn {s}: {s}\n", .{ argv[0], @errorName(err) }); | |
| 254 | return err; | |
| 255 | }; | |
| 256 | ||
| 257 | switch (term) { | |
| 258 | .Exited => |code| blk: { | |
| 259 | const expected_code = expected_exit_code orelse break :blk; | |
| 260 | ||
| 261 | if (code != expected_code) { | |
| 262 | if (builder.prominent_compile_errors) { | |
| 263 | std.debug.print("Run step exited with error code {} (expected {})\n", .{ | |
| 264 | code, | |
| 265 | expected_code, | |
| 266 | }); | |
| 267 | } else { | |
| 268 | std.debug.print("The following command exited with error code {} (expected {}):\n", .{ | |
| 269 | code, | |
| 270 | expected_code, | |
| 271 | }); | |
| 272 | printCmd(cwd, argv); | |
| 273 | } | |
| 274 | ||
| 275 | return error.UnexpectedExitCode; | |
| 276 | } | |
| 277 | }, | |
| 278 | else => { | |
| 279 | std.debug.print("The following command terminated unexpectedly:\n", .{}); | |
| 280 | printCmd(cwd, argv); | |
| 281 | return error.UncleanExit; | |
| 282 | }, | |
| 283 | } | |
| 284 | ||
| 285 | switch (stderr_action) { | |
| 286 | .inherit, .ignore => {}, | |
| 287 | .expect_exact => |expected_bytes| { | |
| 288 | if (!mem.eql(u8, expected_bytes, stderr.?)) { | |
| 289 | std.debug.print( | |
| 290 | \\ | |
| 291 | \\========= Expected this stderr: ========= | |
| 292 | \\{s} | |
| 293 | \\========= But found: ==================== | |
| 294 | \\{s} | |
| 295 | \\ | |
| 296 | , .{ expected_bytes, stderr.? }); | |
| 297 | printCmd(cwd, argv); | |
| 298 | return error.TestFailed; | |
| 299 | } | |
| 300 | }, | |
| 301 | .expect_matches => |matches| for (matches) |match| { | |
| 302 | if (mem.indexOf(u8, stderr.?, match) == null) { | |
| 303 | std.debug.print( | |
| 304 | \\ | |
| 305 | \\========= Expected to find in stderr: ========= | |
| 306 | \\{s} | |
| 307 | \\========= But stderr does not contain it: ===== | |
| 308 | \\{s} | |
| 309 | \\ | |
| 310 | , .{ match, stderr.? }); | |
| 311 | printCmd(cwd, argv); | |
| 312 | return error.TestFailed; | |
| 313 | } | |
| 314 | }, | |
| 315 | } | |
| 316 | ||
| 317 | switch (stdout_action) { | |
| 318 | .inherit, .ignore => {}, | |
| 319 | .expect_exact => |expected_bytes| { | |
| 320 | if (!mem.eql(u8, expected_bytes, stdout.?)) { | |
| 321 | std.debug.print( | |
| 322 | \\ | |
| 323 | \\========= Expected this stdout: ========= | |
| 324 | \\{s} | |
| 325 | \\========= But found: ==================== | |
| 326 | \\{s} | |
| 327 | \\ | |
| 328 | , .{ expected_bytes, stdout.? }); | |
| 329 | printCmd(cwd, argv); | |
| 330 | return error.TestFailed; | |
| 331 | } | |
| 332 | }, | |
| 333 | .expect_matches => |matches| for (matches) |match| { | |
| 334 | if (mem.indexOf(u8, stdout.?, match) == null) { | |
| 335 | std.debug.print( | |
| 336 | \\ | |
| 337 | \\========= Expected to find in stdout: ========= | |
| 338 | \\{s} | |
| 339 | \\========= But stdout does not contain it: ===== | |
| 340 | \\{s} | |
| 341 | \\ | |
| 342 | , .{ match, stdout.? }); | |
| 343 | printCmd(cwd, argv); | |
| 344 | return error.TestFailed; | |
| 345 | } | |
| 346 | }, | |
| 347 | } | |
| 348 | } | |
| 349 | ||
| 350 | fn printCmd(cwd: ?[]const u8, argv: []const []const u8) void { | |
| 351 | if (cwd) |yes_cwd| std.debug.print("cd {s} && ", .{yes_cwd}); | |
| 352 | for (argv) |arg| { | |
| 353 | std.debug.print("{s} ", .{arg}); | |
| 354 | } | |
| 355 | std.debug.print("\n", .{}); | |
| 356 | } | |
| 357 | ||
| 358 | fn addPathForDynLibs(self: *RunStep, artifact: *CompileStep) void { | |
| 359 | addPathForDynLibsInternal(&self.step, self.builder, artifact); | |
| 360 | } | |
| 361 | ||
| 362 | /// This should only be used for internal usage, this is called automatically | |
| 363 | /// for the user. | |
| 364 | pub fn addPathForDynLibsInternal(step: *Step, builder: *std.Build, artifact: *CompileStep) void { | |
| 365 | for (artifact.link_objects.items) |link_object| { | |
| 366 | switch (link_object) { | |
| 367 | .other_step => |other| { | |
| 368 | if (other.target.isWindows() and other.isDynamicLibrary()) { | |
| 369 | addPathDirInternal(step, builder, fs.path.dirname(other.getOutputSource().getPath(builder)).?); | |
| 370 | addPathForDynLibsInternal(step, builder, other); | |
| 371 | } | |
| 372 | }, | |
| 373 | else => {}, | |
| 374 | } | |
| 375 | } | |
| 376 | } |
lib/std/Build/Step.zig created+97| ... | ... | @@ -0,0 +1,97 @@ |
| 1 | id: Id, | |
| 2 | name: []const u8, | |
| 3 | makeFn: *const fn (self: *Step) anyerror!void, | |
| 4 | dependencies: std.ArrayList(*Step), | |
| 5 | loop_flag: bool, | |
| 6 | done_flag: bool, | |
| 7 | ||
| 8 | pub const Id = enum { | |
| 9 | top_level, | |
| 10 | compile, | |
| 11 | install_artifact, | |
| 12 | install_file, | |
| 13 | install_dir, | |
| 14 | log, | |
| 15 | remove_dir, | |
| 16 | fmt, | |
| 17 | translate_c, | |
| 18 | write_file, | |
| 19 | run, | |
| 20 | emulatable_run, | |
| 21 | check_file, | |
| 22 | check_object, | |
| 23 | config_header, | |
| 24 | install_raw, | |
| 25 | options, | |
| 26 | custom, | |
| 27 | ||
| 28 | pub fn Type(comptime id: Id) type { | |
| 29 | return switch (id) { | |
| 30 | .top_level => Build.TopLevelStep, | |
| 31 | .compile => Build.CompileStep, | |
| 32 | .install_artifact => Build.InstallArtifactStep, | |
| 33 | .install_file => Build.InstallFileStep, | |
| 34 | .install_dir => Build.InstallDirStep, | |
| 35 | .log => Build.LogStep, | |
| 36 | .remove_dir => Build.RemoveDirStep, | |
| 37 | .fmt => Build.FmtStep, | |
| 38 | .translate_c => Build.TranslateCStep, | |
| 39 | .write_file => Build.WriteFileStep, | |
| 40 | .run => Build.RunStep, | |
| 41 | .emulatable_run => Build.EmulatableRunStep, | |
| 42 | .check_file => Build.CheckFileStep, | |
| 43 | .check_object => Build.CheckObjectStep, | |
| 44 | .config_header => Build.ConfigHeaderStep, | |
| 45 | .install_raw => Build.InstallRawStep, | |
| 46 | .options => Build.OptionsStep, | |
| 47 | .custom => @compileError("no type available for custom step"), | |
| 48 | }; | |
| 49 | } | |
| 50 | }; | |
| 51 | ||
| 52 | pub fn init( | |
| 53 | id: Id, | |
| 54 | name: []const u8, | |
| 55 | allocator: Allocator, | |
| 56 | makeFn: *const fn (self: *Step) anyerror!void, | |
| 57 | ) Step { | |
| 58 | return Step{ | |
| 59 | .id = id, | |
| 60 | .name = allocator.dupe(u8, name) catch @panic("OOM"), | |
| 61 | .makeFn = makeFn, | |
| 62 | .dependencies = std.ArrayList(*Step).init(allocator), | |
| 63 | .loop_flag = false, | |
| 64 | .done_flag = false, | |
| 65 | }; | |
| 66 | } | |
| 67 | ||
| 68 | pub fn initNoOp(id: Id, name: []const u8, allocator: Allocator) Step { | |
| 69 | return init(id, name, allocator, makeNoOp); | |
| 70 | } | |
| 71 | ||
| 72 | pub fn make(self: *Step) !void { | |
| 73 | if (self.done_flag) return; | |
| 74 | ||
| 75 | try self.makeFn(self); | |
| 76 | self.done_flag = true; | |
| 77 | } | |
| 78 | ||
| 79 | pub fn dependOn(self: *Step, other: *Step) void { | |
| 80 | self.dependencies.append(other) catch @panic("OOM"); | |
| 81 | } | |
| 82 | ||
| 83 | fn makeNoOp(self: *Step) anyerror!void { | |
| 84 | _ = self; | |
| 85 | } | |
| 86 | ||
| 87 | pub fn cast(step: *Step, comptime T: type) ?*T { | |
| 88 | if (step.id == T.base_id) { | |
| 89 | return @fieldParentPtr(T, "step", step); | |
| 90 | } | |
| 91 | return null; | |
| 92 | } | |
| 93 | ||
| 94 | const Step = @This(); | |
| 95 | const std = @import("../std.zig"); | |
| 96 | const Build = std.Build; | |
| 97 | const Allocator = std.mem.Allocator; |
lib/std/Build/TranslateCStep.zig created+136| ... | ... | @@ -0,0 +1,136 @@ |
| 1 | const std = @import("../std.zig"); | |
| 2 | const Step = std.Build.Step; | |
| 3 | const CompileStep = std.Build.CompileStep; | |
| 4 | const CheckFileStep = std.Build.CheckFileStep; | |
| 5 | const fs = std.fs; | |
| 6 | const mem = std.mem; | |
| 7 | const CrossTarget = std.zig.CrossTarget; | |
| 8 | ||
| 9 | const TranslateCStep = @This(); | |
| 10 | ||
| 11 | pub const base_id = .translate_c; | |
| 12 | ||
| 13 | step: Step, | |
| 14 | builder: *std.Build, | |
| 15 | source: std.Build.FileSource, | |
| 16 | include_dirs: std.ArrayList([]const u8), | |
| 17 | c_macros: std.ArrayList([]const u8), | |
| 18 | output_dir: ?[]const u8, | |
| 19 | out_basename: []const u8, | |
| 20 | target: CrossTarget, | |
| 21 | optimize: std.builtin.OptimizeMode, | |
| 22 | output_file: std.Build.GeneratedFile, | |
| 23 | ||
| 24 | pub const Options = struct { | |
| 25 | source_file: std.Build.FileSource, | |
| 26 | target: CrossTarget, | |
| 27 | optimize: std.builtin.OptimizeMode, | |
| 28 | }; | |
| 29 | ||
| 30 | pub fn create(builder: *std.Build, options: Options) *TranslateCStep { | |
| 31 | const self = builder.allocator.create(TranslateCStep) catch @panic("OOM"); | |
| 32 | const source = options.source_file.dupe(builder); | |
| 33 | self.* = TranslateCStep{ | |
| 34 | .step = Step.init(.translate_c, "translate-c", builder.allocator, make), | |
| 35 | .builder = builder, | |
| 36 | .source = source, | |
| 37 | .include_dirs = std.ArrayList([]const u8).init(builder.allocator), | |
| 38 | .c_macros = std.ArrayList([]const u8).init(builder.allocator), | |
| 39 | .output_dir = null, | |
| 40 | .out_basename = undefined, | |
| 41 | .target = options.target, | |
| 42 | .optimize = options.optimize, | |
| 43 | .output_file = std.Build.GeneratedFile{ .step = &self.step }, | |
| 44 | }; | |
| 45 | source.addStepDependencies(&self.step); | |
| 46 | return self; | |
| 47 | } | |
| 48 | ||
| 49 | pub const AddExecutableOptions = struct { | |
| 50 | name: ?[]const u8 = null, | |
| 51 | version: ?std.builtin.Version = null, | |
| 52 | target: ?CrossTarget = null, | |
| 53 | optimize: ?std.builtin.Mode = null, | |
| 54 | linkage: ?CompileStep.Linkage = null, | |
| 55 | }; | |
| 56 | ||
| 57 | /// Creates a step to build an executable from the translated source. | |
| 58 | pub fn addExecutable(self: *TranslateCStep, options: AddExecutableOptions) *CompileStep { | |
| 59 | return self.builder.addExecutable(.{ | |
| 60 | .root_source_file = .{ .generated = &self.output_file }, | |
| 61 | .name = options.name orelse "translated_c", | |
| 62 | .version = options.version, | |
| 63 | .target = options.target orelse self.target, | |
| 64 | .optimize = options.optimize orelse self.optimize, | |
| 65 | .linkage = options.linkage, | |
| 66 | }); | |
| 67 | } | |
| 68 | ||
| 69 | pub fn addIncludeDir(self: *TranslateCStep, include_dir: []const u8) void { | |
| 70 | self.include_dirs.append(self.builder.dupePath(include_dir)) catch @panic("OOM"); | |
| 71 | } | |
| 72 | ||
| 73 | pub fn addCheckFile(self: *TranslateCStep, expected_matches: []const []const u8) *CheckFileStep { | |
| 74 | return CheckFileStep.create(self.builder, .{ .generated = &self.output_file }, self.builder.dupeStrings(expected_matches)); | |
| 75 | } | |
| 76 | ||
| 77 | /// If the value is omitted, it is set to 1. | |
| 78 | /// `name` and `value` need not live longer than the function call. | |
| 79 | pub fn defineCMacro(self: *TranslateCStep, name: []const u8, value: ?[]const u8) void { | |
| 80 | const macro = std.Build.constructCMacro(self.builder.allocator, name, value); | |
| 81 | self.c_macros.append(macro) catch @panic("OOM"); | |
| 82 | } | |
| 83 | ||
| 84 | /// name_and_value looks like [name]=[value]. If the value is omitted, it is set to 1. | |
| 85 | pub fn defineCMacroRaw(self: *TranslateCStep, name_and_value: []const u8) void { | |
| 86 | self.c_macros.append(self.builder.dupe(name_and_value)) catch @panic("OOM"); | |
| 87 | } | |
| 88 | ||
| 89 | fn make(step: *Step) !void { | |
| 90 | const self = @fieldParentPtr(TranslateCStep, "step", step); | |
| 91 | ||
| 92 | var argv_list = std.ArrayList([]const u8).init(self.builder.allocator); | |
| 93 | try argv_list.append(self.builder.zig_exe); | |
| 94 | try argv_list.append("translate-c"); | |
| 95 | try argv_list.append("-lc"); | |
| 96 | ||
| 97 | try argv_list.append("--enable-cache"); | |
| 98 | ||
| 99 | if (!self.target.isNative()) { | |
| 100 | try argv_list.append("-target"); | |
| 101 | try argv_list.append(try self.target.zigTriple(self.builder.allocator)); | |
| 102 | } | |
| 103 | ||
| 104 | switch (self.optimize) { | |
| 105 | .Debug => {}, // Skip since it's the default. | |
| 106 | else => try argv_list.append(self.builder.fmt("-O{s}", .{@tagName(self.optimize)})), | |
| 107 | } | |
| 108 | ||
| 109 | for (self.include_dirs.items) |include_dir| { | |
| 110 | try argv_list.append("-I"); | |
| 111 | try argv_list.append(include_dir); | |
| 112 | } | |
| 113 | ||
| 114 | for (self.c_macros.items) |c_macro| { | |
| 115 | try argv_list.append("-D"); | |
| 116 | try argv_list.append(c_macro); | |
| 117 | } | |
| 118 | ||
| 119 | try argv_list.append(self.source.getPath(self.builder)); | |
| 120 | ||
| 121 | const output_path_nl = try self.builder.execFromStep(argv_list.items, &self.step); | |
| 122 | const output_path = mem.trimRight(u8, output_path_nl, "\r\n"); | |
| 123 | ||
| 124 | self.out_basename = fs.path.basename(output_path); | |
| 125 | if (self.output_dir) |output_dir| { | |
| 126 | const full_dest = try fs.path.join(self.builder.allocator, &[_][]const u8{ output_dir, self.out_basename }); | |
| 127 | try self.builder.updateFile(output_path, full_dest); | |
| 128 | } else { | |
| 129 | self.output_dir = fs.path.dirname(output_path).?; | |
| 130 | } | |
| 131 | ||
| 132 | self.output_file.path = try fs.path.join( | |
| 133 | self.builder.allocator, | |
| 134 | &[_][]const u8{ self.output_dir.?, self.out_basename }, | |
| 135 | ); | |
| 136 | } |
lib/std/Build/WriteFileStep.zig created+115| ... | ... | @@ -0,0 +1,115 @@ |
| 1 | const std = @import("../std.zig"); | |
| 2 | const Step = std.Build.Step; | |
| 3 | const fs = std.fs; | |
| 4 | const ArrayList = std.ArrayList; | |
| 5 | ||
| 6 | const WriteFileStep = @This(); | |
| 7 | ||
| 8 | pub const base_id = .write_file; | |
| 9 | ||
| 10 | step: Step, | |
| 11 | builder: *std.Build, | |
| 12 | output_dir: []const u8, | |
| 13 | files: std.TailQueue(File), | |
| 14 | ||
| 15 | pub const File = struct { | |
| 16 | source: std.Build.GeneratedFile, | |
| 17 | basename: []const u8, | |
| 18 | bytes: []const u8, | |
| 19 | }; | |
| 20 | ||
| 21 | pub fn init(builder: *std.Build) WriteFileStep { | |
| 22 | return WriteFileStep{ | |
| 23 | .builder = builder, | |
| 24 | .step = Step.init(.write_file, "writefile", builder.allocator, make), | |
| 25 | .files = .{}, | |
| 26 | .output_dir = undefined, | |
| 27 | }; | |
| 28 | } | |
| 29 | ||
| 30 | pub fn add(self: *WriteFileStep, basename: []const u8, bytes: []const u8) void { | |
| 31 | const node = self.builder.allocator.create(std.TailQueue(File).Node) catch @panic("unhandled error"); | |
| 32 | node.* = .{ | |
| 33 | .data = .{ | |
| 34 | .source = std.Build.GeneratedFile{ .step = &self.step }, | |
| 35 | .basename = self.builder.dupePath(basename), | |
| 36 | .bytes = self.builder.dupe(bytes), | |
| 37 | }, | |
| 38 | }; | |
| 39 | ||
| 40 | self.files.append(node); | |
| 41 | } | |
| 42 | ||
| 43 | /// Gets a file source for the given basename. If the file does not exist, returns `null`. | |
| 44 | pub fn getFileSource(step: *WriteFileStep, basename: []const u8) ?std.Build.FileSource { | |
| 45 | var it = step.files.first; | |
| 46 | while (it) |node| : (it = node.next) { | |
| 47 | if (std.mem.eql(u8, node.data.basename, basename)) | |
| 48 | return std.Build.FileSource{ .generated = &node.data.source }; | |
| 49 | } | |
| 50 | return null; | |
| 51 | } | |
| 52 | ||
| 53 | fn make(step: *Step) !void { | |
| 54 | const self = @fieldParentPtr(WriteFileStep, "step", step); | |
| 55 | ||
| 56 | // The cache is used here not really as a way to speed things up - because writing | |
| 57 | // the data to a file would probably be very fast - but as a way to find a canonical | |
| 58 | // location to put build artifacts. | |
| 59 | ||
| 60 | // If, for example, a hard-coded path was used as the location to put WriteFileStep | |
| 61 | // files, then two WriteFileSteps executing in parallel might clobber each other. | |
| 62 | ||
| 63 | // TODO port the cache system from the compiler to zig std lib. Until then | |
| 64 | // we directly construct the path, and no "cache hit" detection happens; | |
| 65 | // the files are always written. | |
| 66 | // Note there is similar code over in ConfigHeaderStep. | |
| 67 | const Hasher = std.crypto.auth.siphash.SipHash128(1, 3); | |
| 68 | // Random bytes to make WriteFileStep unique. Refresh this with | |
| 69 | // new random bytes when WriteFileStep implementation is modified | |
| 70 | // in a non-backwards-compatible way. | |
| 71 | var hash = Hasher.init("eagVR1dYXoE7ARDP"); | |
| 72 | ||
| 73 | { | |
| 74 | var it = self.files.first; | |
| 75 | while (it) |node| : (it = node.next) { | |
| 76 | hash.update(node.data.basename); | |
| 77 | hash.update(node.data.bytes); | |
| 78 | hash.update("|"); | |
| 79 | } | |
| 80 | } | |
| 81 | var digest: [16]u8 = undefined; | |
| 82 | hash.final(&digest); | |
| 83 | var hash_basename: [digest.len * 2]u8 = undefined; | |
| 84 | _ = std.fmt.bufPrint( | |
| 85 | &hash_basename, | |
| 86 | "{s}", | |
| 87 | .{std.fmt.fmtSliceHexLower(&digest)}, | |
| 88 | ) catch unreachable; | |
| 89 | ||
| 90 | self.output_dir = try fs.path.join(self.builder.allocator, &[_][]const u8{ | |
| 91 | self.builder.cache_root, "o", &hash_basename, | |
| 92 | }); | |
| 93 | var dir = fs.cwd().makeOpenPath(self.output_dir, .{}) catch |err| { | |
| 94 | std.debug.print("unable to make path {s}: {s}\n", .{ self.output_dir, @errorName(err) }); | |
| 95 | return err; | |
| 96 | }; | |
| 97 | defer dir.close(); | |
| 98 | { | |
| 99 | var it = self.files.first; | |
| 100 | while (it) |node| : (it = node.next) { | |
| 101 | dir.writeFile(node.data.basename, node.data.bytes) catch |err| { | |
| 102 | std.debug.print("unable to write {s} into {s}: {s}\n", .{ | |
| 103 | node.data.basename, | |
| 104 | self.output_dir, | |
| 105 | @errorName(err), | |
| 106 | }); | |
| 107 | return err; | |
| 108 | }; | |
| 109 | node.data.source.path = try fs.path.join( | |
| 110 | self.builder.allocator, | |
| 111 | &[_][]const u8{ self.output_dir, node.data.basename }, | |
| 112 | ); | |
| 113 | } | |
| 114 | } | |
| 115 | } |
lib/std/Thread.zig+1-1| ... | ... | @@ -166,7 +166,7 @@ pub const GetNameError = error{ |
| 166 | 166 | |
| 167 | 167 | pub fn getName(self: Thread, buffer_ptr: *[max_name_len:0]u8) GetNameError!?[]const u8 { |
| 168 | 168 | buffer_ptr[max_name_len] = 0; |
| 169 | var buffer = std.mem.span(buffer_ptr); | |
| 169 | var buffer: [:0]u8 = buffer_ptr; | |
| 170 | 170 | |
| 171 | 171 | switch (target.os.tag) { |
| 172 | 172 | .linux => if (use_pthreads and is_gnu) { |
lib/std/array_hash_map.zig+2-1| ... | ... | @@ -1145,7 +1145,8 @@ pub fn ArrayHashMapUnmanaged( |
| 1145 | 1145 | } |
| 1146 | 1146 | |
| 1147 | 1147 | /// Create a copy of the hash map which can be modified separately. |
| 1148 | /// The copy uses the same context and allocator as this instance. | |
| 1148 | /// The copy uses the same context as this instance, but is allocated | |
| 1149 | /// with the provided allocator. | |
| 1149 | 1150 | pub fn clone(self: Self, allocator: Allocator) !Self { |
| 1150 | 1151 | if (@sizeOf(ByIndexContext) != 0) |
| 1151 | 1152 | @compileError("Cannot infer context " ++ @typeName(Context) ++ ", call cloneContext instead."); |
lib/std/bounded_array.zig+5-1| ... | ... | @@ -29,7 +29,11 @@ pub fn BoundedArray(comptime T: type, comptime buffer_capacity: usize) type { |
| 29 | 29 | } |
| 30 | 30 | |
| 31 | 31 | /// View the internal array as a slice whose size was previously set. |
| 32 | pub fn slice(self: anytype) mem.Span(@TypeOf(&self.buffer)) { | |
| 32 | pub fn slice(self: anytype) switch (@TypeOf(&self.buffer)) { | |
| 33 | *[buffer_capacity]T => []T, | |
| 34 | *const [buffer_capacity]T => []const T, | |
| 35 | else => unreachable, | |
| 36 | } { | |
| 33 | 37 | return self.buffer[0..self.len]; |
| 34 | 38 | } |
| 35 | 39 |
lib/std/build.zig deleted-1781| ... | ... | @@ -1,1781 +0,0 @@ |
| 1 | const std = @import("std.zig"); | |
| 2 | const builtin = @import("builtin"); | |
| 3 | const io = std.io; | |
| 4 | const fs = std.fs; | |
| 5 | const mem = std.mem; | |
| 6 | const debug = std.debug; | |
| 7 | const panic = std.debug.panic; | |
| 8 | const assert = debug.assert; | |
| 9 | const log = std.log; | |
| 10 | const ArrayList = std.ArrayList; | |
| 11 | const StringHashMap = std.StringHashMap; | |
| 12 | const Allocator = mem.Allocator; | |
| 13 | const process = std.process; | |
| 14 | const EnvMap = std.process.EnvMap; | |
| 15 | const fmt_lib = std.fmt; | |
| 16 | const File = std.fs.File; | |
| 17 | const CrossTarget = std.zig.CrossTarget; | |
| 18 | const NativeTargetInfo = std.zig.system.NativeTargetInfo; | |
| 19 | const Sha256 = std.crypto.hash.sha2.Sha256; | |
| 20 | const ThisModule = @This(); | |
| 21 | ||
| 22 | pub const CheckFileStep = @import("build/CheckFileStep.zig"); | |
| 23 | pub const CheckObjectStep = @import("build/CheckObjectStep.zig"); | |
| 24 | pub const ConfigHeaderStep = @import("build/ConfigHeaderStep.zig"); | |
| 25 | pub const EmulatableRunStep = @import("build/EmulatableRunStep.zig"); | |
| 26 | pub const FmtStep = @import("build/FmtStep.zig"); | |
| 27 | pub const InstallArtifactStep = @import("build/InstallArtifactStep.zig"); | |
| 28 | pub const InstallDirStep = @import("build/InstallDirStep.zig"); | |
| 29 | pub const InstallFileStep = @import("build/InstallFileStep.zig"); | |
| 30 | pub const InstallRawStep = @import("build/InstallRawStep.zig"); | |
| 31 | pub const LibExeObjStep = @import("build/LibExeObjStep.zig"); | |
| 32 | pub const LogStep = @import("build/LogStep.zig"); | |
| 33 | pub const OptionsStep = @import("build/OptionsStep.zig"); | |
| 34 | pub const RemoveDirStep = @import("build/RemoveDirStep.zig"); | |
| 35 | pub const RunStep = @import("build/RunStep.zig"); | |
| 36 | pub const TranslateCStep = @import("build/TranslateCStep.zig"); | |
| 37 | pub const WriteFileStep = @import("build/WriteFileStep.zig"); | |
| 38 | ||
| 39 | pub const Builder = struct { | |
| 40 | install_tls: TopLevelStep, | |
| 41 | uninstall_tls: TopLevelStep, | |
| 42 | allocator: Allocator, | |
| 43 | user_input_options: UserInputOptionsMap, | |
| 44 | available_options_map: AvailableOptionsMap, | |
| 45 | available_options_list: ArrayList(AvailableOption), | |
| 46 | verbose: bool, | |
| 47 | verbose_link: bool, | |
| 48 | verbose_cc: bool, | |
| 49 | verbose_air: bool, | |
| 50 | verbose_llvm_ir: bool, | |
| 51 | verbose_cimport: bool, | |
| 52 | verbose_llvm_cpu_features: bool, | |
| 53 | /// The purpose of executing the command is for a human to read compile errors from the terminal | |
| 54 | prominent_compile_errors: bool, | |
| 55 | color: enum { auto, on, off } = .auto, | |
| 56 | reference_trace: ?u32 = null, | |
| 57 | invalid_user_input: bool, | |
| 58 | zig_exe: []const u8, | |
| 59 | default_step: *Step, | |
| 60 | env_map: *EnvMap, | |
| 61 | top_level_steps: ArrayList(*TopLevelStep), | |
| 62 | install_prefix: []const u8, | |
| 63 | dest_dir: ?[]const u8, | |
| 64 | lib_dir: []const u8, | |
| 65 | exe_dir: []const u8, | |
| 66 | h_dir: []const u8, | |
| 67 | install_path: []const u8, | |
| 68 | sysroot: ?[]const u8 = null, | |
| 69 | search_prefixes: ArrayList([]const u8), | |
| 70 | libc_file: ?[]const u8 = null, | |
| 71 | installed_files: ArrayList(InstalledFile), | |
| 72 | /// Path to the directory containing build.zig. | |
| 73 | build_root: []const u8, | |
| 74 | cache_root: []const u8, | |
| 75 | global_cache_root: []const u8, | |
| 76 | release_mode: ?std.builtin.Mode, | |
| 77 | is_release: bool, | |
| 78 | /// zig lib dir | |
| 79 | override_lib_dir: ?[]const u8, | |
| 80 | vcpkg_root: VcpkgRoot = .unattempted, | |
| 81 | pkg_config_pkg_list: ?(PkgConfigError![]const PkgConfigPkg) = null, | |
| 82 | args: ?[][]const u8 = null, | |
| 83 | debug_log_scopes: []const []const u8 = &.{}, | |
| 84 | debug_compile_errors: bool = false, | |
| 85 | ||
| 86 | /// Experimental. Use system Darling installation to run cross compiled macOS build artifacts. | |
| 87 | enable_darling: bool = false, | |
| 88 | /// Use system QEMU installation to run cross compiled foreign architecture build artifacts. | |
| 89 | enable_qemu: bool = false, | |
| 90 | /// Darwin. Use Rosetta to run x86_64 macOS build artifacts on arm64 macOS. | |
| 91 | enable_rosetta: bool = false, | |
| 92 | /// Use system Wasmtime installation to run cross compiled wasm/wasi build artifacts. | |
| 93 | enable_wasmtime: bool = false, | |
| 94 | /// Use system Wine installation to run cross compiled Windows build artifacts. | |
| 95 | enable_wine: bool = false, | |
| 96 | /// After following the steps in https://github.com/ziglang/zig/wiki/Updating-libc#glibc, | |
| 97 | /// this will be the directory $glibc-build-dir/install/glibcs | |
| 98 | /// Given the example of the aarch64 target, this is the directory | |
| 99 | /// that contains the path `aarch64-linux-gnu/lib/ld-linux-aarch64.so.1`. | |
| 100 | glibc_runtimes_dir: ?[]const u8 = null, | |
| 101 | ||
| 102 | /// Information about the native target. Computed before build() is invoked. | |
| 103 | host: NativeTargetInfo, | |
| 104 | ||
| 105 | dep_prefix: []const u8 = "", | |
| 106 | ||
| 107 | pub const ExecError = error{ | |
| 108 | ReadFailure, | |
| 109 | ExitCodeFailure, | |
| 110 | ProcessTerminated, | |
| 111 | ExecNotSupported, | |
| 112 | } || std.ChildProcess.SpawnError; | |
| 113 | ||
| 114 | pub const PkgConfigError = error{ | |
| 115 | PkgConfigCrashed, | |
| 116 | PkgConfigFailed, | |
| 117 | PkgConfigNotInstalled, | |
| 118 | PkgConfigInvalidOutput, | |
| 119 | }; | |
| 120 | ||
| 121 | pub const PkgConfigPkg = struct { | |
| 122 | name: []const u8, | |
| 123 | desc: []const u8, | |
| 124 | }; | |
| 125 | ||
| 126 | pub const CStd = enum { | |
| 127 | C89, | |
| 128 | C99, | |
| 129 | C11, | |
| 130 | }; | |
| 131 | ||
| 132 | const UserInputOptionsMap = StringHashMap(UserInputOption); | |
| 133 | const AvailableOptionsMap = StringHashMap(AvailableOption); | |
| 134 | ||
| 135 | const AvailableOption = struct { | |
| 136 | name: []const u8, | |
| 137 | type_id: TypeId, | |
| 138 | description: []const u8, | |
| 139 | /// If the `type_id` is `enum` this provides the list of enum options | |
| 140 | enum_options: ?[]const []const u8, | |
| 141 | }; | |
| 142 | ||
| 143 | const UserInputOption = struct { | |
| 144 | name: []const u8, | |
| 145 | value: UserValue, | |
| 146 | used: bool, | |
| 147 | }; | |
| 148 | ||
| 149 | const UserValue = union(enum) { | |
| 150 | flag: void, | |
| 151 | scalar: []const u8, | |
| 152 | list: ArrayList([]const u8), | |
| 153 | }; | |
| 154 | ||
| 155 | const TypeId = enum { | |
| 156 | bool, | |
| 157 | int, | |
| 158 | float, | |
| 159 | @"enum", | |
| 160 | string, | |
| 161 | list, | |
| 162 | }; | |
| 163 | ||
| 164 | const TopLevelStep = struct { | |
| 165 | pub const base_id = .top_level; | |
| 166 | ||
| 167 | step: Step, | |
| 168 | description: []const u8, | |
| 169 | }; | |
| 170 | ||
| 171 | pub const DirList = struct { | |
| 172 | lib_dir: ?[]const u8 = null, | |
| 173 | exe_dir: ?[]const u8 = null, | |
| 174 | include_dir: ?[]const u8 = null, | |
| 175 | }; | |
| 176 | ||
| 177 | pub fn create( | |
| 178 | allocator: Allocator, | |
| 179 | zig_exe: []const u8, | |
| 180 | build_root: []const u8, | |
| 181 | cache_root: []const u8, | |
| 182 | global_cache_root: []const u8, | |
| 183 | ) !*Builder { | |
| 184 | const env_map = try allocator.create(EnvMap); | |
| 185 | env_map.* = try process.getEnvMap(allocator); | |
| 186 | ||
| 187 | const host = try NativeTargetInfo.detect(.{}); | |
| 188 | ||
| 189 | const self = try allocator.create(Builder); | |
| 190 | self.* = Builder{ | |
| 191 | .zig_exe = zig_exe, | |
| 192 | .build_root = build_root, | |
| 193 | .cache_root = try fs.path.relative(allocator, build_root, cache_root), | |
| 194 | .global_cache_root = global_cache_root, | |
| 195 | .verbose = false, | |
| 196 | .verbose_link = false, | |
| 197 | .verbose_cc = false, | |
| 198 | .verbose_air = false, | |
| 199 | .verbose_llvm_ir = false, | |
| 200 | .verbose_cimport = false, | |
| 201 | .verbose_llvm_cpu_features = false, | |
| 202 | .prominent_compile_errors = false, | |
| 203 | .invalid_user_input = false, | |
| 204 | .allocator = allocator, | |
| 205 | .user_input_options = UserInputOptionsMap.init(allocator), | |
| 206 | .available_options_map = AvailableOptionsMap.init(allocator), | |
| 207 | .available_options_list = ArrayList(AvailableOption).init(allocator), | |
| 208 | .top_level_steps = ArrayList(*TopLevelStep).init(allocator), | |
| 209 | .default_step = undefined, | |
| 210 | .env_map = env_map, | |
| 211 | .search_prefixes = ArrayList([]const u8).init(allocator), | |
| 212 | .install_prefix = undefined, | |
| 213 | .lib_dir = undefined, | |
| 214 | .exe_dir = undefined, | |
| 215 | .h_dir = undefined, | |
| 216 | .dest_dir = env_map.get("DESTDIR"), | |
| 217 | .installed_files = ArrayList(InstalledFile).init(allocator), | |
| 218 | .install_tls = TopLevelStep{ | |
| 219 | .step = Step.initNoOp(.top_level, "install", allocator), | |
| 220 | .description = "Copy build artifacts to prefix path", | |
| 221 | }, | |
| 222 | .uninstall_tls = TopLevelStep{ | |
| 223 | .step = Step.init(.top_level, "uninstall", allocator, makeUninstall), | |
| 224 | .description = "Remove build artifacts from prefix path", | |
| 225 | }, | |
| 226 | .release_mode = null, | |
| 227 | .is_release = false, | |
| 228 | .override_lib_dir = null, | |
| 229 | .install_path = undefined, | |
| 230 | .args = null, | |
| 231 | .host = host, | |
| 232 | }; | |
| 233 | try self.top_level_steps.append(&self.install_tls); | |
| 234 | try self.top_level_steps.append(&self.uninstall_tls); | |
| 235 | self.default_step = &self.install_tls.step; | |
| 236 | return self; | |
| 237 | } | |
| 238 | ||
| 239 | fn createChild( | |
| 240 | parent: *Builder, | |
| 241 | dep_name: []const u8, | |
| 242 | build_root: []const u8, | |
| 243 | args: anytype, | |
| 244 | ) !*Builder { | |
| 245 | const child = try createChildOnly(parent, dep_name, build_root); | |
| 246 | try applyArgs(child, args); | |
| 247 | return child; | |
| 248 | } | |
| 249 | ||
| 250 | fn createChildOnly(parent: *Builder, dep_name: []const u8, build_root: []const u8) !*Builder { | |
| 251 | const allocator = parent.allocator; | |
| 252 | const child = try allocator.create(Builder); | |
| 253 | child.* = .{ | |
| 254 | .allocator = allocator, | |
| 255 | .install_tls = .{ | |
| 256 | .step = Step.initNoOp(.top_level, "install", allocator), | |
| 257 | .description = "Copy build artifacts to prefix path", | |
| 258 | }, | |
| 259 | .uninstall_tls = .{ | |
| 260 | .step = Step.init(.top_level, "uninstall", allocator, makeUninstall), | |
| 261 | .description = "Remove build artifacts from prefix path", | |
| 262 | }, | |
| 263 | .user_input_options = UserInputOptionsMap.init(allocator), | |
| 264 | .available_options_map = AvailableOptionsMap.init(allocator), | |
| 265 | .available_options_list = ArrayList(AvailableOption).init(allocator), | |
| 266 | .verbose = parent.verbose, | |
| 267 | .verbose_link = parent.verbose_link, | |
| 268 | .verbose_cc = parent.verbose_cc, | |
| 269 | .verbose_air = parent.verbose_air, | |
| 270 | .verbose_llvm_ir = parent.verbose_llvm_ir, | |
| 271 | .verbose_cimport = parent.verbose_cimport, | |
| 272 | .verbose_llvm_cpu_features = parent.verbose_llvm_cpu_features, | |
| 273 | .prominent_compile_errors = parent.prominent_compile_errors, | |
| 274 | .color = parent.color, | |
| 275 | .reference_trace = parent.reference_trace, | |
| 276 | .invalid_user_input = false, | |
| 277 | .zig_exe = parent.zig_exe, | |
| 278 | .default_step = undefined, | |
| 279 | .env_map = parent.env_map, | |
| 280 | .top_level_steps = ArrayList(*TopLevelStep).init(allocator), | |
| 281 | .install_prefix = undefined, | |
| 282 | .dest_dir = parent.dest_dir, | |
| 283 | .lib_dir = parent.lib_dir, | |
| 284 | .exe_dir = parent.exe_dir, | |
| 285 | .h_dir = parent.h_dir, | |
| 286 | .install_path = parent.install_path, | |
| 287 | .sysroot = parent.sysroot, | |
| 288 | .search_prefixes = ArrayList([]const u8).init(allocator), | |
| 289 | .libc_file = parent.libc_file, | |
| 290 | .installed_files = ArrayList(InstalledFile).init(allocator), | |
| 291 | .build_root = build_root, | |
| 292 | .cache_root = parent.cache_root, | |
| 293 | .global_cache_root = parent.global_cache_root, | |
| 294 | .release_mode = parent.release_mode, | |
| 295 | .is_release = parent.is_release, | |
| 296 | .override_lib_dir = parent.override_lib_dir, | |
| 297 | .debug_log_scopes = parent.debug_log_scopes, | |
| 298 | .debug_compile_errors = parent.debug_compile_errors, | |
| 299 | .enable_darling = parent.enable_darling, | |
| 300 | .enable_qemu = parent.enable_qemu, | |
| 301 | .enable_rosetta = parent.enable_rosetta, | |
| 302 | .enable_wasmtime = parent.enable_wasmtime, | |
| 303 | .enable_wine = parent.enable_wine, | |
| 304 | .glibc_runtimes_dir = parent.glibc_runtimes_dir, | |
| 305 | .host = parent.host, | |
| 306 | .dep_prefix = parent.fmt("{s}{s}.", .{ parent.dep_prefix, dep_name }), | |
| 307 | }; | |
| 308 | try child.top_level_steps.append(&child.install_tls); | |
| 309 | try child.top_level_steps.append(&child.uninstall_tls); | |
| 310 | child.default_step = &child.install_tls.step; | |
| 311 | return child; | |
| 312 | } | |
| 313 | ||
| 314 | fn applyArgs(b: *Builder, args: anytype) !void { | |
| 315 | // TODO this function is the way that a build.zig file communicates | |
| 316 | // options to its dependencies. It is the programmatic way to give | |
| 317 | // command line arguments to a build.zig script. | |
| 318 | _ = args; | |
| 319 | const Hasher = std.crypto.auth.siphash.SipHash128(1, 3); | |
| 320 | // Random bytes to make unique. Refresh this with new random bytes when | |
| 321 | // implementation is modified in a non-backwards-compatible way. | |
| 322 | var hash = Hasher.init("ZaEsvQ5ClaA2IdH9"); | |
| 323 | hash.update(b.dep_prefix); | |
| 324 | // TODO additionally update the hash with `args`. | |
| 325 | ||
| 326 | var digest: [16]u8 = undefined; | |
| 327 | hash.final(&digest); | |
| 328 | var hash_basename: [digest.len * 2]u8 = undefined; | |
| 329 | _ = std.fmt.bufPrint(&hash_basename, "{s}", .{std.fmt.fmtSliceHexLower(&digest)}) catch | |
| 330 | unreachable; | |
| 331 | ||
| 332 | const install_prefix = b.pathJoin(&.{ b.cache_root, "i", &hash_basename }); | |
| 333 | b.resolveInstallPrefix(install_prefix, .{}); | |
| 334 | } | |
| 335 | ||
| 336 | pub fn destroy(self: *Builder) void { | |
| 337 | self.env_map.deinit(); | |
| 338 | self.top_level_steps.deinit(); | |
| 339 | self.allocator.destroy(self); | |
| 340 | } | |
| 341 | ||
| 342 | /// This function is intended to be called by lib/build_runner.zig, not a build.zig file. | |
| 343 | pub fn resolveInstallPrefix(self: *Builder, install_prefix: ?[]const u8, dir_list: DirList) void { | |
| 344 | if (self.dest_dir) |dest_dir| { | |
| 345 | self.install_prefix = install_prefix orelse "/usr"; | |
| 346 | self.install_path = self.pathJoin(&.{ dest_dir, self.install_prefix }); | |
| 347 | } else { | |
| 348 | self.install_prefix = install_prefix orelse | |
| 349 | (self.pathJoin(&.{ self.build_root, "zig-out" })); | |
| 350 | self.install_path = self.install_prefix; | |
| 351 | } | |
| 352 | ||
| 353 | var lib_list = [_][]const u8{ self.install_path, "lib" }; | |
| 354 | var exe_list = [_][]const u8{ self.install_path, "bin" }; | |
| 355 | var h_list = [_][]const u8{ self.install_path, "include" }; | |
| 356 | ||
| 357 | if (dir_list.lib_dir) |dir| { | |
| 358 | if (std.fs.path.isAbsolute(dir)) lib_list[0] = self.dest_dir orelse ""; | |
| 359 | lib_list[1] = dir; | |
| 360 | } | |
| 361 | ||
| 362 | if (dir_list.exe_dir) |dir| { | |
| 363 | if (std.fs.path.isAbsolute(dir)) exe_list[0] = self.dest_dir orelse ""; | |
| 364 | exe_list[1] = dir; | |
| 365 | } | |
| 366 | ||
| 367 | if (dir_list.include_dir) |dir| { | |
| 368 | if (std.fs.path.isAbsolute(dir)) h_list[0] = self.dest_dir orelse ""; | |
| 369 | h_list[1] = dir; | |
| 370 | } | |
| 371 | ||
| 372 | self.lib_dir = self.pathJoin(&lib_list); | |
| 373 | self.exe_dir = self.pathJoin(&exe_list); | |
| 374 | self.h_dir = self.pathJoin(&h_list); | |
| 375 | } | |
| 376 | ||
| 377 | fn convertOptionalPathToFileSource(path: ?[]const u8) ?FileSource { | |
| 378 | return if (path) |p| | |
| 379 | FileSource{ .path = p } | |
| 380 | else | |
| 381 | null; | |
| 382 | } | |
| 383 | ||
| 384 | pub fn addExecutable(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep { | |
| 385 | return addExecutableSource(self, name, convertOptionalPathToFileSource(root_src)); | |
| 386 | } | |
| 387 | ||
| 388 | pub fn addExecutableSource(builder: *Builder, name: []const u8, root_src: ?FileSource) *LibExeObjStep { | |
| 389 | return LibExeObjStep.createExecutable(builder, name, root_src); | |
| 390 | } | |
| 391 | ||
| 392 | pub fn addOptions(self: *Builder) *OptionsStep { | |
| 393 | return OptionsStep.create(self); | |
| 394 | } | |
| 395 | ||
| 396 | pub fn addObject(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep { | |
| 397 | return addObjectSource(self, name, convertOptionalPathToFileSource(root_src)); | |
| 398 | } | |
| 399 | ||
| 400 | pub fn addObjectSource(builder: *Builder, name: []const u8, root_src: ?FileSource) *LibExeObjStep { | |
| 401 | return LibExeObjStep.createObject(builder, name, root_src); | |
| 402 | } | |
| 403 | ||
| 404 | pub fn addSharedLibrary( | |
| 405 | self: *Builder, | |
| 406 | name: []const u8, | |
| 407 | root_src: ?[]const u8, | |
| 408 | kind: LibExeObjStep.SharedLibKind, | |
| 409 | ) *LibExeObjStep { | |
| 410 | return addSharedLibrarySource(self, name, convertOptionalPathToFileSource(root_src), kind); | |
| 411 | } | |
| 412 | ||
| 413 | pub fn addSharedLibrarySource( | |
| 414 | self: *Builder, | |
| 415 | name: []const u8, | |
| 416 | root_src: ?FileSource, | |
| 417 | kind: LibExeObjStep.SharedLibKind, | |
| 418 | ) *LibExeObjStep { | |
| 419 | return LibExeObjStep.createSharedLibrary(self, name, root_src, kind); | |
| 420 | } | |
| 421 | ||
| 422 | pub fn addStaticLibrary(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep { | |
| 423 | return addStaticLibrarySource(self, name, convertOptionalPathToFileSource(root_src)); | |
| 424 | } | |
| 425 | ||
| 426 | pub fn addStaticLibrarySource(self: *Builder, name: []const u8, root_src: ?FileSource) *LibExeObjStep { | |
| 427 | return LibExeObjStep.createStaticLibrary(self, name, root_src); | |
| 428 | } | |
| 429 | ||
| 430 | pub fn addTest(self: *Builder, root_src: []const u8) *LibExeObjStep { | |
| 431 | return LibExeObjStep.createTest(self, "test", .{ .path = root_src }); | |
| 432 | } | |
| 433 | ||
| 434 | pub fn addTestSource(self: *Builder, root_src: FileSource) *LibExeObjStep { | |
| 435 | return LibExeObjStep.createTest(self, "test", root_src.dupe(self)); | |
| 436 | } | |
| 437 | ||
| 438 | pub fn addTestExe(self: *Builder, name: []const u8, root_src: []const u8) *LibExeObjStep { | |
| 439 | return LibExeObjStep.createTestExe(self, name, .{ .path = root_src }); | |
| 440 | } | |
| 441 | ||
| 442 | pub fn addTestExeSource(self: *Builder, name: []const u8, root_src: FileSource) *LibExeObjStep { | |
| 443 | return LibExeObjStep.createTestExe(self, name, root_src.dupe(self)); | |
| 444 | } | |
| 445 | ||
| 446 | pub fn addAssemble(self: *Builder, name: []const u8, src: []const u8) *LibExeObjStep { | |
| 447 | return addAssembleSource(self, name, .{ .path = src }); | |
| 448 | } | |
| 449 | ||
| 450 | pub fn addAssembleSource(self: *Builder, name: []const u8, src: FileSource) *LibExeObjStep { | |
| 451 | const obj_step = LibExeObjStep.createObject(self, name, null); | |
| 452 | obj_step.addAssemblyFileSource(src.dupe(self)); | |
| 453 | return obj_step; | |
| 454 | } | |
| 455 | ||
| 456 | /// Initializes a RunStep with argv, which must at least have the path to the | |
| 457 | /// executable. More command line arguments can be added with `addArg`, | |
| 458 | /// `addArgs`, and `addArtifactArg`. | |
| 459 | /// Be careful using this function, as it introduces a system dependency. | |
| 460 | /// To run an executable built with zig build, see `LibExeObjStep.run`. | |
| 461 | pub fn addSystemCommand(self: *Builder, argv: []const []const u8) *RunStep { | |
| 462 | assert(argv.len >= 1); | |
| 463 | const run_step = RunStep.create(self, self.fmt("run {s}", .{argv[0]})); | |
| 464 | run_step.addArgs(argv); | |
| 465 | return run_step; | |
| 466 | } | |
| 467 | ||
| 468 | pub fn addConfigHeader( | |
| 469 | b: *Builder, | |
| 470 | source: FileSource, | |
| 471 | style: ConfigHeaderStep.Style, | |
| 472 | values: anytype, | |
| 473 | ) *ConfigHeaderStep { | |
| 474 | const config_header_step = ConfigHeaderStep.create(b, source, style); | |
| 475 | config_header_step.addValues(values); | |
| 476 | return config_header_step; | |
| 477 | } | |
| 478 | ||
| 479 | /// Allocator.dupe without the need to handle out of memory. | |
| 480 | pub fn dupe(self: *Builder, bytes: []const u8) []u8 { | |
| 481 | return self.allocator.dupe(u8, bytes) catch unreachable; | |
| 482 | } | |
| 483 | ||
| 484 | /// Duplicates an array of strings without the need to handle out of memory. | |
| 485 | pub fn dupeStrings(self: *Builder, strings: []const []const u8) [][]u8 { | |
| 486 | const array = self.allocator.alloc([]u8, strings.len) catch unreachable; | |
| 487 | for (strings) |s, i| { | |
| 488 | array[i] = self.dupe(s); | |
| 489 | } | |
| 490 | return array; | |
| 491 | } | |
| 492 | ||
| 493 | /// Duplicates a path and converts all slashes to the OS's canonical path separator. | |
| 494 | pub fn dupePath(self: *Builder, bytes: []const u8) []u8 { | |
| 495 | const the_copy = self.dupe(bytes); | |
| 496 | for (the_copy) |*byte| { | |
| 497 | switch (byte.*) { | |
| 498 | '/', '\\' => byte.* = fs.path.sep, | |
| 499 | else => {}, | |
| 500 | } | |
| 501 | } | |
| 502 | return the_copy; | |
| 503 | } | |
| 504 | ||
| 505 | /// Duplicates a package recursively. | |
| 506 | pub fn dupePkg(self: *Builder, package: Pkg) Pkg { | |
| 507 | var the_copy = Pkg{ | |
| 508 | .name = self.dupe(package.name), | |
| 509 | .source = package.source.dupe(self), | |
| 510 | }; | |
| 511 | ||
| 512 | if (package.dependencies) |dependencies| { | |
| 513 | const new_dependencies = self.allocator.alloc(Pkg, dependencies.len) catch unreachable; | |
| 514 | the_copy.dependencies = new_dependencies; | |
| 515 | ||
| 516 | for (dependencies) |dep_package, i| { | |
| 517 | new_dependencies[i] = self.dupePkg(dep_package); | |
| 518 | } | |
| 519 | } | |
| 520 | return the_copy; | |
| 521 | } | |
| 522 | ||
| 523 | pub fn addWriteFile(self: *Builder, file_path: []const u8, data: []const u8) *WriteFileStep { | |
| 524 | const write_file_step = self.addWriteFiles(); | |
| 525 | write_file_step.add(file_path, data); | |
| 526 | return write_file_step; | |
| 527 | } | |
| 528 | ||
| 529 | pub fn addWriteFiles(self: *Builder) *WriteFileStep { | |
| 530 | const write_file_step = self.allocator.create(WriteFileStep) catch unreachable; | |
| 531 | write_file_step.* = WriteFileStep.init(self); | |
| 532 | return write_file_step; | |
| 533 | } | |
| 534 | ||
| 535 | pub fn addLog(self: *Builder, comptime format: []const u8, args: anytype) *LogStep { | |
| 536 | const data = self.fmt(format, args); | |
| 537 | const log_step = self.allocator.create(LogStep) catch unreachable; | |
| 538 | log_step.* = LogStep.init(self, data); | |
| 539 | return log_step; | |
| 540 | } | |
| 541 | ||
| 542 | pub fn addRemoveDirTree(self: *Builder, dir_path: []const u8) *RemoveDirStep { | |
| 543 | const remove_dir_step = self.allocator.create(RemoveDirStep) catch unreachable; | |
| 544 | remove_dir_step.* = RemoveDirStep.init(self, dir_path); | |
| 545 | return remove_dir_step; | |
| 546 | } | |
| 547 | ||
| 548 | pub fn addFmt(self: *Builder, paths: []const []const u8) *FmtStep { | |
| 549 | return FmtStep.create(self, paths); | |
| 550 | } | |
| 551 | ||
| 552 | pub fn addTranslateC(self: *Builder, source: FileSource) *TranslateCStep { | |
| 553 | return TranslateCStep.create(self, source.dupe(self)); | |
| 554 | } | |
| 555 | ||
| 556 | pub fn version(self: *const Builder, major: u32, minor: u32, patch: u32) LibExeObjStep.SharedLibKind { | |
| 557 | _ = self; | |
| 558 | return .{ | |
| 559 | .versioned = .{ | |
| 560 | .major = major, | |
| 561 | .minor = minor, | |
| 562 | .patch = patch, | |
| 563 | }, | |
| 564 | }; | |
| 565 | } | |
| 566 | ||
| 567 | pub fn make(self: *Builder, step_names: []const []const u8) !void { | |
| 568 | try self.makePath(self.cache_root); | |
| 569 | ||
| 570 | var wanted_steps = ArrayList(*Step).init(self.allocator); | |
| 571 | defer wanted_steps.deinit(); | |
| 572 | ||
| 573 | if (step_names.len == 0) { | |
| 574 | try wanted_steps.append(self.default_step); | |
| 575 | } else { | |
| 576 | for (step_names) |step_name| { | |
| 577 | const s = try self.getTopLevelStepByName(step_name); | |
| 578 | try wanted_steps.append(s); | |
| 579 | } | |
| 580 | } | |
| 581 | ||
| 582 | for (wanted_steps.items) |s| { | |
| 583 | try self.makeOneStep(s); | |
| 584 | } | |
| 585 | } | |
| 586 | ||
| 587 | pub fn getInstallStep(self: *Builder) *Step { | |
| 588 | return &self.install_tls.step; | |
| 589 | } | |
| 590 | ||
| 591 | pub fn getUninstallStep(self: *Builder) *Step { | |
| 592 | return &self.uninstall_tls.step; | |
| 593 | } | |
| 594 | ||
| 595 | fn makeUninstall(uninstall_step: *Step) anyerror!void { | |
| 596 | const uninstall_tls = @fieldParentPtr(TopLevelStep, "step", uninstall_step); | |
| 597 | const self = @fieldParentPtr(Builder, "uninstall_tls", uninstall_tls); | |
| 598 | ||
| 599 | for (self.installed_files.items) |installed_file| { | |
| 600 | const full_path = self.getInstallPath(installed_file.dir, installed_file.path); | |
| 601 | if (self.verbose) { | |
| 602 | log.info("rm {s}", .{full_path}); | |
| 603 | } | |
| 604 | fs.cwd().deleteTree(full_path) catch {}; | |
| 605 | } | |
| 606 | ||
| 607 | // TODO remove empty directories | |
| 608 | } | |
| 609 | ||
| 610 | fn makeOneStep(self: *Builder, s: *Step) anyerror!void { | |
| 611 | if (s.loop_flag) { | |
| 612 | log.err("Dependency loop detected:\n {s}", .{s.name}); | |
| 613 | return error.DependencyLoopDetected; | |
| 614 | } | |
| 615 | s.loop_flag = true; | |
| 616 | ||
| 617 | for (s.dependencies.items) |dep| { | |
| 618 | self.makeOneStep(dep) catch |err| { | |
| 619 | if (err == error.DependencyLoopDetected) { | |
| 620 | log.err(" {s}", .{s.name}); | |
| 621 | } | |
| 622 | return err; | |
| 623 | }; | |
| 624 | } | |
| 625 | ||
| 626 | s.loop_flag = false; | |
| 627 | ||
| 628 | try s.make(); | |
| 629 | } | |
| 630 | ||
| 631 | fn getTopLevelStepByName(self: *Builder, name: []const u8) !*Step { | |
| 632 | for (self.top_level_steps.items) |top_level_step| { | |
| 633 | if (mem.eql(u8, top_level_step.step.name, name)) { | |
| 634 | return &top_level_step.step; | |
| 635 | } | |
| 636 | } | |
| 637 | log.err("Cannot run step '{s}' because it does not exist", .{name}); | |
| 638 | return error.InvalidStepName; | |
| 639 | } | |
| 640 | ||
| 641 | pub fn option(self: *Builder, comptime T: type, name_raw: []const u8, description_raw: []const u8) ?T { | |
| 642 | const name = self.dupe(name_raw); | |
| 643 | const description = self.dupe(description_raw); | |
| 644 | const type_id = comptime typeToEnum(T); | |
| 645 | const enum_options = if (type_id == .@"enum") blk: { | |
| 646 | const fields = comptime std.meta.fields(T); | |
| 647 | var options = ArrayList([]const u8).initCapacity(self.allocator, fields.len) catch unreachable; | |
| 648 | ||
| 649 | inline for (fields) |field| { | |
| 650 | options.appendAssumeCapacity(field.name); | |
| 651 | } | |
| 652 | ||
| 653 | break :blk options.toOwnedSlice() catch unreachable; | |
| 654 | } else null; | |
| 655 | const available_option = AvailableOption{ | |
| 656 | .name = name, | |
| 657 | .type_id = type_id, | |
| 658 | .description = description, | |
| 659 | .enum_options = enum_options, | |
| 660 | }; | |
| 661 | if ((self.available_options_map.fetchPut(name, available_option) catch unreachable) != null) { | |
| 662 | panic("Option '{s}' declared twice", .{name}); | |
| 663 | } | |
| 664 | self.available_options_list.append(available_option) catch unreachable; | |
| 665 | ||
| 666 | const option_ptr = self.user_input_options.getPtr(name) orelse return null; | |
| 667 | option_ptr.used = true; | |
| 668 | switch (type_id) { | |
| 669 | .bool => switch (option_ptr.value) { | |
| 670 | .flag => return true, | |
| 671 | .scalar => |s| { | |
| 672 | if (mem.eql(u8, s, "true")) { | |
| 673 | return true; | |
| 674 | } else if (mem.eql(u8, s, "false")) { | |
| 675 | return false; | |
| 676 | } else { | |
| 677 | log.err("Expected -D{s} to be a boolean, but received '{s}'\n", .{ name, s }); | |
| 678 | self.markInvalidUserInput(); | |
| 679 | return null; | |
| 680 | } | |
| 681 | }, | |
| 682 | .list => { | |
| 683 | log.err("Expected -D{s} to be a boolean, but received a list.\n", .{name}); | |
| 684 | self.markInvalidUserInput(); | |
| 685 | return null; | |
| 686 | }, | |
| 687 | }, | |
| 688 | .int => switch (option_ptr.value) { | |
| 689 | .flag => { | |
| 690 | log.err("Expected -D{s} to be an integer, but received a boolean.\n", .{name}); | |
| 691 | self.markInvalidUserInput(); | |
| 692 | return null; | |
| 693 | }, | |
| 694 | .scalar => |s| { | |
| 695 | const n = std.fmt.parseInt(T, s, 10) catch |err| switch (err) { | |
| 696 | error.Overflow => { | |
| 697 | log.err("-D{s} value {s} cannot fit into type {s}.\n", .{ name, s, @typeName(T) }); | |
| 698 | self.markInvalidUserInput(); | |
| 699 | return null; | |
| 700 | }, | |
| 701 | else => { | |
| 702 | log.err("Expected -D{s} to be an integer of type {s}.\n", .{ name, @typeName(T) }); | |
| 703 | self.markInvalidUserInput(); | |
| 704 | return null; | |
| 705 | }, | |
| 706 | }; | |
| 707 | return n; | |
| 708 | }, | |
| 709 | .list => { | |
| 710 | log.err("Expected -D{s} to be an integer, but received a list.\n", .{name}); | |
| 711 | self.markInvalidUserInput(); | |
| 712 | return null; | |
| 713 | }, | |
| 714 | }, | |
| 715 | .float => switch (option_ptr.value) { | |
| 716 | .flag => { | |
| 717 | log.err("Expected -D{s} to be a float, but received a boolean.\n", .{name}); | |
| 718 | self.markInvalidUserInput(); | |
| 719 | return null; | |
| 720 | }, | |
| 721 | .scalar => |s| { | |
| 722 | const n = std.fmt.parseFloat(T, s) catch { | |
| 723 | log.err("Expected -D{s} to be a float of type {s}.\n", .{ name, @typeName(T) }); | |
| 724 | self.markInvalidUserInput(); | |
| 725 | return null; | |
| 726 | }; | |
| 727 | return n; | |
| 728 | }, | |
| 729 | .list => { | |
| 730 | log.err("Expected -D{s} to be a float, but received a list.\n", .{name}); | |
| 731 | self.markInvalidUserInput(); | |
| 732 | return null; | |
| 733 | }, | |
| 734 | }, | |
| 735 | .@"enum" => switch (option_ptr.value) { | |
| 736 | .flag => { | |
| 737 | log.err("Expected -D{s} to be a string, but received a boolean.\n", .{name}); | |
| 738 | self.markInvalidUserInput(); | |
| 739 | return null; | |
| 740 | }, | |
| 741 | .scalar => |s| { | |
| 742 | if (std.meta.stringToEnum(T, s)) |enum_lit| { | |
| 743 | return enum_lit; | |
| 744 | } else { | |
| 745 | log.err("Expected -D{s} to be of type {s}.\n", .{ name, @typeName(T) }); | |
| 746 | self.markInvalidUserInput(); | |
| 747 | return null; | |
| 748 | } | |
| 749 | }, | |
| 750 | .list => { | |
| 751 | log.err("Expected -D{s} to be a string, but received a list.\n", .{name}); | |
| 752 | self.markInvalidUserInput(); | |
| 753 | return null; | |
| 754 | }, | |
| 755 | }, | |
| 756 | .string => switch (option_ptr.value) { | |
| 757 | .flag => { | |
| 758 | log.err("Expected -D{s} to be a string, but received a boolean.\n", .{name}); | |
| 759 | self.markInvalidUserInput(); | |
| 760 | return null; | |
| 761 | }, | |
| 762 | .list => { | |
| 763 | log.err("Expected -D{s} to be a string, but received a list.\n", .{name}); | |
| 764 | self.markInvalidUserInput(); | |
| 765 | return null; | |
| 766 | }, | |
| 767 | .scalar => |s| return s, | |
| 768 | }, | |
| 769 | .list => switch (option_ptr.value) { | |
| 770 | .flag => { | |
| 771 | log.err("Expected -D{s} to be a list, but received a boolean.\n", .{name}); | |
| 772 | self.markInvalidUserInput(); | |
| 773 | return null; | |
| 774 | }, | |
| 775 | .scalar => |s| { | |
| 776 | return self.allocator.dupe([]const u8, &[_][]const u8{s}) catch unreachable; | |
| 777 | }, | |
| 778 | .list => |lst| return lst.items, | |
| 779 | }, | |
| 780 | } | |
| 781 | } | |
| 782 | ||
| 783 | pub fn step(self: *Builder, name: []const u8, description: []const u8) *Step { | |
| 784 | const step_info = self.allocator.create(TopLevelStep) catch unreachable; | |
| 785 | step_info.* = TopLevelStep{ | |
| 786 | .step = Step.initNoOp(.top_level, name, self.allocator), | |
| 787 | .description = self.dupe(description), | |
| 788 | }; | |
| 789 | self.top_level_steps.append(step_info) catch unreachable; | |
| 790 | return &step_info.step; | |
| 791 | } | |
| 792 | ||
| 793 | /// This provides the -Drelease option to the build user and does not give them the choice. | |
| 794 | pub fn setPreferredReleaseMode(self: *Builder, mode: std.builtin.Mode) void { | |
| 795 | if (self.release_mode != null) { | |
| 796 | @panic("setPreferredReleaseMode must be called before standardReleaseOptions and may not be called twice"); | |
| 797 | } | |
| 798 | const description = self.fmt("Create a release build ({s})", .{@tagName(mode)}); | |
| 799 | self.is_release = self.option(bool, "release", description) orelse false; | |
| 800 | self.release_mode = if (self.is_release) mode else std.builtin.Mode.Debug; | |
| 801 | } | |
| 802 | ||
| 803 | /// If you call this without first calling `setPreferredReleaseMode` then it gives the build user | |
| 804 | /// the choice of what kind of release. | |
| 805 | pub fn standardReleaseOptions(self: *Builder) std.builtin.Mode { | |
| 806 | if (self.release_mode) |mode| return mode; | |
| 807 | ||
| 808 | const release_safe = self.option(bool, "release-safe", "Optimizations on and safety on") orelse false; | |
| 809 | const release_fast = self.option(bool, "release-fast", "Optimizations on and safety off") orelse false; | |
| 810 | const release_small = self.option(bool, "release-small", "Size optimizations on and safety off") orelse false; | |
| 811 | ||
| 812 | const mode = if (release_safe and !release_fast and !release_small) | |
| 813 | std.builtin.Mode.ReleaseSafe | |
| 814 | else if (release_fast and !release_safe and !release_small) | |
| 815 | std.builtin.Mode.ReleaseFast | |
| 816 | else if (release_small and !release_fast and !release_safe) | |
| 817 | std.builtin.Mode.ReleaseSmall | |
| 818 | else if (!release_fast and !release_safe and !release_small) | |
| 819 | std.builtin.Mode.Debug | |
| 820 | else x: { | |
| 821 | log.err("Multiple release modes (of -Drelease-safe, -Drelease-fast and -Drelease-small)\n", .{}); | |
| 822 | self.markInvalidUserInput(); | |
| 823 | break :x std.builtin.Mode.Debug; | |
| 824 | }; | |
| 825 | self.is_release = mode != .Debug; | |
| 826 | self.release_mode = mode; | |
| 827 | return mode; | |
| 828 | } | |
| 829 | ||
| 830 | pub const StandardTargetOptionsArgs = struct { | |
| 831 | whitelist: ?[]const CrossTarget = null, | |
| 832 | ||
| 833 | default_target: CrossTarget = CrossTarget{}, | |
| 834 | }; | |
| 835 | ||
| 836 | /// Exposes standard `zig build` options for choosing a target. | |
| 837 | pub fn standardTargetOptions(self: *Builder, args: StandardTargetOptionsArgs) CrossTarget { | |
| 838 | const maybe_triple = self.option( | |
| 839 | []const u8, | |
| 840 | "target", | |
| 841 | "The CPU architecture, OS, and ABI to build for", | |
| 842 | ); | |
| 843 | const mcpu = self.option([]const u8, "cpu", "Target CPU features to add or subtract"); | |
| 844 | ||
| 845 | if (maybe_triple == null and mcpu == null) { | |
| 846 | return args.default_target; | |
| 847 | } | |
| 848 | ||
| 849 | const triple = maybe_triple orelse "native"; | |
| 850 | ||
| 851 | var diags: CrossTarget.ParseOptions.Diagnostics = .{}; | |
| 852 | const selected_target = CrossTarget.parse(.{ | |
| 853 | .arch_os_abi = triple, | |
| 854 | .cpu_features = mcpu, | |
| 855 | .diagnostics = &diags, | |
| 856 | }) catch |err| switch (err) { | |
| 857 | error.UnknownCpuModel => { | |
| 858 | log.err("Unknown CPU: '{s}'\nAvailable CPUs for architecture '{s}':", .{ | |
| 859 | diags.cpu_name.?, | |
| 860 | @tagName(diags.arch.?), | |
| 861 | }); | |
| 862 | for (diags.arch.?.allCpuModels()) |cpu| { | |
| 863 | log.err(" {s}", .{cpu.name}); | |
| 864 | } | |
| 865 | self.markInvalidUserInput(); | |
| 866 | return args.default_target; | |
| 867 | }, | |
| 868 | error.UnknownCpuFeature => { | |
| 869 | log.err( | |
| 870 | \\Unknown CPU feature: '{s}' | |
| 871 | \\Available CPU features for architecture '{s}': | |
| 872 | \\ | |
| 873 | , .{ | |
| 874 | diags.unknown_feature_name.?, | |
| 875 | @tagName(diags.arch.?), | |
| 876 | }); | |
| 877 | for (diags.arch.?.allFeaturesList()) |feature| { | |
| 878 | log.err(" {s}: {s}", .{ feature.name, feature.description }); | |
| 879 | } | |
| 880 | self.markInvalidUserInput(); | |
| 881 | return args.default_target; | |
| 882 | }, | |
| 883 | error.UnknownOperatingSystem => { | |
| 884 | log.err( | |
| 885 | \\Unknown OS: '{s}' | |
| 886 | \\Available operating systems: | |
| 887 | \\ | |
| 888 | , .{diags.os_name.?}); | |
| 889 | inline for (std.meta.fields(std.Target.Os.Tag)) |field| { | |
| 890 | log.err(" {s}", .{field.name}); | |
| 891 | } | |
| 892 | self.markInvalidUserInput(); | |
| 893 | return args.default_target; | |
| 894 | }, | |
| 895 | else => |e| { | |
| 896 | log.err("Unable to parse target '{s}': {s}\n", .{ triple, @errorName(e) }); | |
| 897 | self.markInvalidUserInput(); | |
| 898 | return args.default_target; | |
| 899 | }, | |
| 900 | }; | |
| 901 | ||
| 902 | const selected_canonicalized_triple = selected_target.zigTriple(self.allocator) catch unreachable; | |
| 903 | ||
| 904 | if (args.whitelist) |list| whitelist_check: { | |
| 905 | // Make sure it's a match of one of the list. | |
| 906 | var mismatch_triple = true; | |
| 907 | var mismatch_cpu_features = true; | |
| 908 | var whitelist_item = CrossTarget{}; | |
| 909 | for (list) |t| { | |
| 910 | mismatch_cpu_features = true; | |
| 911 | mismatch_triple = true; | |
| 912 | ||
| 913 | const t_triple = t.zigTriple(self.allocator) catch unreachable; | |
| 914 | if (mem.eql(u8, t_triple, selected_canonicalized_triple)) { | |
| 915 | mismatch_triple = false; | |
| 916 | whitelist_item = t; | |
| 917 | if (t.getCpuFeatures().isSuperSetOf(selected_target.getCpuFeatures())) { | |
| 918 | mismatch_cpu_features = false; | |
| 919 | break :whitelist_check; | |
| 920 | } else { | |
| 921 | break; | |
| 922 | } | |
| 923 | } | |
| 924 | } | |
| 925 | if (mismatch_triple) { | |
| 926 | log.err("Chosen target '{s}' does not match one of the supported targets:", .{ | |
| 927 | selected_canonicalized_triple, | |
| 928 | }); | |
| 929 | for (list) |t| { | |
| 930 | const t_triple = t.zigTriple(self.allocator) catch unreachable; | |
| 931 | log.err(" {s}", .{t_triple}); | |
| 932 | } | |
| 933 | } else { | |
| 934 | assert(mismatch_cpu_features); | |
| 935 | const whitelist_cpu = whitelist_item.getCpu(); | |
| 936 | const selected_cpu = selected_target.getCpu(); | |
| 937 | log.err("Chosen CPU model '{s}' does not match one of the supported targets:", .{ | |
| 938 | selected_cpu.model.name, | |
| 939 | }); | |
| 940 | log.err(" Supported feature Set: ", .{}); | |
| 941 | const all_features = whitelist_cpu.arch.allFeaturesList(); | |
| 942 | var populated_cpu_features = whitelist_cpu.model.features; | |
| 943 | populated_cpu_features.populateDependencies(all_features); | |
| 944 | for (all_features) |feature, i_usize| { | |
| 945 | const i = @intCast(std.Target.Cpu.Feature.Set.Index, i_usize); | |
| 946 | const in_cpu_set = populated_cpu_features.isEnabled(i); | |
| 947 | if (in_cpu_set) { | |
| 948 | log.err("{s} ", .{feature.name}); | |
| 949 | } | |
| 950 | } | |
| 951 | log.err(" Remove: ", .{}); | |
| 952 | for (all_features) |feature, i_usize| { | |
| 953 | const i = @intCast(std.Target.Cpu.Feature.Set.Index, i_usize); | |
| 954 | const in_cpu_set = populated_cpu_features.isEnabled(i); | |
| 955 | const in_actual_set = selected_cpu.features.isEnabled(i); | |
| 956 | if (in_actual_set and !in_cpu_set) { | |
| 957 | log.err("{s} ", .{feature.name}); | |
| 958 | } | |
| 959 | } | |
| 960 | } | |
| 961 | self.markInvalidUserInput(); | |
| 962 | return args.default_target; | |
| 963 | } | |
| 964 | ||
| 965 | return selected_target; | |
| 966 | } | |
| 967 | ||
| 968 | pub fn addUserInputOption(self: *Builder, name_raw: []const u8, value_raw: []const u8) !bool { | |
| 969 | const name = self.dupe(name_raw); | |
| 970 | const value = self.dupe(value_raw); | |
| 971 | const gop = try self.user_input_options.getOrPut(name); | |
| 972 | if (!gop.found_existing) { | |
| 973 | gop.value_ptr.* = UserInputOption{ | |
| 974 | .name = name, | |
| 975 | .value = .{ .scalar = value }, | |
| 976 | .used = false, | |
| 977 | }; | |
| 978 | return false; | |
| 979 | } | |
| 980 | ||
| 981 | // option already exists | |
| 982 | switch (gop.value_ptr.value) { | |
| 983 | .scalar => |s| { | |
| 984 | // turn it into a list | |
| 985 | var list = ArrayList([]const u8).init(self.allocator); | |
| 986 | list.append(s) catch unreachable; | |
| 987 | list.append(value) catch unreachable; | |
| 988 | self.user_input_options.put(name, .{ | |
| 989 | .name = name, | |
| 990 | .value = .{ .list = list }, | |
| 991 | .used = false, | |
| 992 | }) catch unreachable; | |
| 993 | }, | |
| 994 | .list => |*list| { | |
| 995 | // append to the list | |
| 996 | list.append(value) catch unreachable; | |
| 997 | self.user_input_options.put(name, .{ | |
| 998 | .name = name, | |
| 999 | .value = .{ .list = list.* }, | |
| 1000 | .used = false, | |
| 1001 | }) catch unreachable; | |
| 1002 | }, | |
| 1003 | .flag => { | |
| 1004 | log.warn("Option '-D{s}={s}' conflicts with flag '-D{s}'.", .{ name, value, name }); | |
| 1005 | return true; | |
| 1006 | }, | |
| 1007 | } | |
| 1008 | return false; | |
| 1009 | } | |
| 1010 | ||
| 1011 | pub fn addUserInputFlag(self: *Builder, name_raw: []const u8) !bool { | |
| 1012 | const name = self.dupe(name_raw); | |
| 1013 | const gop = try self.user_input_options.getOrPut(name); | |
| 1014 | if (!gop.found_existing) { | |
| 1015 | gop.value_ptr.* = .{ | |
| 1016 | .name = name, | |
| 1017 | .value = .{ .flag = {} }, | |
| 1018 | .used = false, | |
| 1019 | }; | |
| 1020 | return false; | |
| 1021 | } | |
| 1022 | ||
| 1023 | // option already exists | |
| 1024 | switch (gop.value_ptr.value) { | |
| 1025 | .scalar => |s| { | |
| 1026 | log.err("Flag '-D{s}' conflicts with option '-D{s}={s}'.", .{ name, name, s }); | |
| 1027 | return true; | |
| 1028 | }, | |
| 1029 | .list => { | |
| 1030 | log.err("Flag '-D{s}' conflicts with multiple options of the same name.", .{name}); | |
| 1031 | return true; | |
| 1032 | }, | |
| 1033 | .flag => {}, | |
| 1034 | } | |
| 1035 | return false; | |
| 1036 | } | |
| 1037 | ||
| 1038 | fn typeToEnum(comptime T: type) TypeId { | |
| 1039 | return switch (@typeInfo(T)) { | |
| 1040 | .Int => .int, | |
| 1041 | .Float => .float, | |
| 1042 | .Bool => .bool, | |
| 1043 | .Enum => .@"enum", | |
| 1044 | else => switch (T) { | |
| 1045 | []const u8 => .string, | |
| 1046 | []const []const u8 => .list, | |
| 1047 | else => @compileError("Unsupported type: " ++ @typeName(T)), | |
| 1048 | }, | |
| 1049 | }; | |
| 1050 | } | |
| 1051 | ||
| 1052 | fn markInvalidUserInput(self: *Builder) void { | |
| 1053 | self.invalid_user_input = true; | |
| 1054 | } | |
| 1055 | ||
| 1056 | pub fn validateUserInputDidItFail(self: *Builder) bool { | |
| 1057 | // make sure all args are used | |
| 1058 | var it = self.user_input_options.iterator(); | |
| 1059 | while (it.next()) |entry| { | |
| 1060 | if (!entry.value_ptr.used) { | |
| 1061 | log.err("Invalid option: -D{s}\n", .{entry.key_ptr.*}); | |
| 1062 | self.markInvalidUserInput(); | |
| 1063 | } | |
| 1064 | } | |
| 1065 | ||
| 1066 | return self.invalid_user_input; | |
| 1067 | } | |
| 1068 | ||
| 1069 | pub fn spawnChild(self: *Builder, argv: []const []const u8) !void { | |
| 1070 | return self.spawnChildEnvMap(null, self.env_map, argv); | |
| 1071 | } | |
| 1072 | ||
| 1073 | fn printCmd(cwd: ?[]const u8, argv: []const []const u8) void { | |
| 1074 | if (cwd) |yes_cwd| std.debug.print("cd {s} && ", .{yes_cwd}); | |
| 1075 | for (argv) |arg| { | |
| 1076 | std.debug.print("{s} ", .{arg}); | |
| 1077 | } | |
| 1078 | std.debug.print("\n", .{}); | |
| 1079 | } | |
| 1080 | ||
| 1081 | pub fn spawnChildEnvMap(self: *Builder, cwd: ?[]const u8, env_map: *const EnvMap, argv: []const []const u8) !void { | |
| 1082 | if (self.verbose) { | |
| 1083 | printCmd(cwd, argv); | |
| 1084 | } | |
| 1085 | ||
| 1086 | if (!std.process.can_spawn) | |
| 1087 | return error.ExecNotSupported; | |
| 1088 | ||
| 1089 | var child = std.ChildProcess.init(argv, self.allocator); | |
| 1090 | child.cwd = cwd; | |
| 1091 | child.env_map = env_map; | |
| 1092 | ||
| 1093 | const term = child.spawnAndWait() catch |err| { | |
| 1094 | log.err("Unable to spawn {s}: {s}", .{ argv[0], @errorName(err) }); | |
| 1095 | return err; | |
| 1096 | }; | |
| 1097 | ||
| 1098 | switch (term) { | |
| 1099 | .Exited => |code| { | |
| 1100 | if (code != 0) { | |
| 1101 | log.err("The following command exited with error code {}:", .{code}); | |
| 1102 | printCmd(cwd, argv); | |
| 1103 | return error.UncleanExit; | |
| 1104 | } | |
| 1105 | }, | |
| 1106 | else => { | |
| 1107 | log.err("The following command terminated unexpectedly:", .{}); | |
| 1108 | printCmd(cwd, argv); | |
| 1109 | ||
| 1110 | return error.UncleanExit; | |
| 1111 | }, | |
| 1112 | } | |
| 1113 | } | |
| 1114 | ||
| 1115 | pub fn makePath(self: *Builder, path: []const u8) !void { | |
| 1116 | fs.cwd().makePath(self.pathFromRoot(path)) catch |err| { | |
| 1117 | log.err("Unable to create path {s}: {s}", .{ path, @errorName(err) }); | |
| 1118 | return err; | |
| 1119 | }; | |
| 1120 | } | |
| 1121 | ||
| 1122 | pub fn installArtifact(self: *Builder, artifact: *LibExeObjStep) void { | |
| 1123 | self.getInstallStep().dependOn(&self.addInstallArtifact(artifact).step); | |
| 1124 | } | |
| 1125 | ||
| 1126 | pub fn addInstallArtifact(self: *Builder, artifact: *LibExeObjStep) *InstallArtifactStep { | |
| 1127 | return InstallArtifactStep.create(self, artifact); | |
| 1128 | } | |
| 1129 | ||
| 1130 | ///`dest_rel_path` is relative to prefix path | |
| 1131 | pub fn installFile(self: *Builder, src_path: []const u8, dest_rel_path: []const u8) void { | |
| 1132 | self.getInstallStep().dependOn(&self.addInstallFileWithDir(.{ .path = src_path }, .prefix, dest_rel_path).step); | |
| 1133 | } | |
| 1134 | ||
| 1135 | pub fn installDirectory(self: *Builder, options: InstallDirectoryOptions) void { | |
| 1136 | self.getInstallStep().dependOn(&self.addInstallDirectory(options).step); | |
| 1137 | } | |
| 1138 | ||
| 1139 | ///`dest_rel_path` is relative to bin path | |
| 1140 | pub fn installBinFile(self: *Builder, src_path: []const u8, dest_rel_path: []const u8) void { | |
| 1141 | self.getInstallStep().dependOn(&self.addInstallFileWithDir(.{ .path = src_path }, .bin, dest_rel_path).step); | |
| 1142 | } | |
| 1143 | ||
| 1144 | ///`dest_rel_path` is relative to lib path | |
| 1145 | pub fn installLibFile(self: *Builder, src_path: []const u8, dest_rel_path: []const u8) void { | |
| 1146 | self.getInstallStep().dependOn(&self.addInstallFileWithDir(.{ .path = src_path }, .lib, dest_rel_path).step); | |
| 1147 | } | |
| 1148 | ||
| 1149 | /// Output format (BIN vs Intel HEX) determined by filename | |
| 1150 | pub fn installRaw(self: *Builder, artifact: *LibExeObjStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep { | |
| 1151 | const raw = self.addInstallRaw(artifact, dest_filename, options); | |
| 1152 | self.getInstallStep().dependOn(&raw.step); | |
| 1153 | return raw; | |
| 1154 | } | |
| 1155 | ||
| 1156 | ///`dest_rel_path` is relative to install prefix path | |
| 1157 | pub fn addInstallFile(self: *Builder, source: FileSource, dest_rel_path: []const u8) *InstallFileStep { | |
| 1158 | return self.addInstallFileWithDir(source.dupe(self), .prefix, dest_rel_path); | |
| 1159 | } | |
| 1160 | ||
| 1161 | ///`dest_rel_path` is relative to bin path | |
| 1162 | pub fn addInstallBinFile(self: *Builder, source: FileSource, dest_rel_path: []const u8) *InstallFileStep { | |
| 1163 | return self.addInstallFileWithDir(source.dupe(self), .bin, dest_rel_path); | |
| 1164 | } | |
| 1165 | ||
| 1166 | ///`dest_rel_path` is relative to lib path | |
| 1167 | pub fn addInstallLibFile(self: *Builder, source: FileSource, dest_rel_path: []const u8) *InstallFileStep { | |
| 1168 | return self.addInstallFileWithDir(source.dupe(self), .lib, dest_rel_path); | |
| 1169 | } | |
| 1170 | ||
| 1171 | pub fn addInstallHeaderFile(b: *Builder, src_path: []const u8, dest_rel_path: []const u8) *InstallFileStep { | |
| 1172 | return b.addInstallFileWithDir(.{ .path = src_path }, .header, dest_rel_path); | |
| 1173 | } | |
| 1174 | ||
| 1175 | pub fn addInstallRaw(self: *Builder, artifact: *LibExeObjStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep { | |
| 1176 | return InstallRawStep.create(self, artifact, dest_filename, options); | |
| 1177 | } | |
| 1178 | ||
| 1179 | pub fn addInstallFileWithDir( | |
| 1180 | self: *Builder, | |
| 1181 | source: FileSource, | |
| 1182 | install_dir: InstallDir, | |
| 1183 | dest_rel_path: []const u8, | |
| 1184 | ) *InstallFileStep { | |
| 1185 | if (dest_rel_path.len == 0) { | |
| 1186 | panic("dest_rel_path must be non-empty", .{}); | |
| 1187 | } | |
| 1188 | const install_step = self.allocator.create(InstallFileStep) catch unreachable; | |
| 1189 | install_step.* = InstallFileStep.init(self, source.dupe(self), install_dir, dest_rel_path); | |
| 1190 | return install_step; | |
| 1191 | } | |
| 1192 | ||
| 1193 | pub fn addInstallDirectory(self: *Builder, options: InstallDirectoryOptions) *InstallDirStep { | |
| 1194 | const install_step = self.allocator.create(InstallDirStep) catch unreachable; | |
| 1195 | install_step.* = InstallDirStep.init(self, options); | |
| 1196 | return install_step; | |
| 1197 | } | |
| 1198 | ||
| 1199 | pub fn pushInstalledFile(self: *Builder, dir: InstallDir, dest_rel_path: []const u8) void { | |
| 1200 | const file = InstalledFile{ | |
| 1201 | .dir = dir, | |
| 1202 | .path = dest_rel_path, | |
| 1203 | }; | |
| 1204 | self.installed_files.append(file.dupe(self)) catch unreachable; | |
| 1205 | } | |
| 1206 | ||
| 1207 | pub fn updateFile(self: *Builder, source_path: []const u8, dest_path: []const u8) !void { | |
| 1208 | if (self.verbose) { | |
| 1209 | log.info("cp {s} {s} ", .{ source_path, dest_path }); | |
| 1210 | } | |
| 1211 | const cwd = fs.cwd(); | |
| 1212 | const prev_status = try fs.Dir.updateFile(cwd, source_path, cwd, dest_path, .{}); | |
| 1213 | if (self.verbose) switch (prev_status) { | |
| 1214 | .stale => log.info("# installed", .{}), | |
| 1215 | .fresh => log.info("# up-to-date", .{}), | |
| 1216 | }; | |
| 1217 | } | |
| 1218 | ||
| 1219 | pub fn truncateFile(self: *Builder, dest_path: []const u8) !void { | |
| 1220 | if (self.verbose) { | |
| 1221 | log.info("truncate {s}", .{dest_path}); | |
| 1222 | } | |
| 1223 | const cwd = fs.cwd(); | |
| 1224 | var src_file = cwd.createFile(dest_path, .{}) catch |err| switch (err) { | |
| 1225 | error.FileNotFound => blk: { | |
| 1226 | if (fs.path.dirname(dest_path)) |dirname| { | |
| 1227 | try cwd.makePath(dirname); | |
| 1228 | } | |
| 1229 | break :blk try cwd.createFile(dest_path, .{}); | |
| 1230 | }, | |
| 1231 | else => |e| return e, | |
| 1232 | }; | |
| 1233 | src_file.close(); | |
| 1234 | } | |
| 1235 | ||
| 1236 | pub fn pathFromRoot(self: *Builder, rel_path: []const u8) []u8 { | |
| 1237 | return fs.path.resolve(self.allocator, &[_][]const u8{ self.build_root, rel_path }) catch unreachable; | |
| 1238 | } | |
| 1239 | ||
| 1240 | /// Shorthand for `std.fs.path.join(builder.allocator, paths) catch unreachable` | |
| 1241 | pub fn pathJoin(self: *Builder, paths: []const []const u8) []u8 { | |
| 1242 | return fs.path.join(self.allocator, paths) catch unreachable; | |
| 1243 | } | |
| 1244 | ||
| 1245 | pub fn fmt(self: *Builder, comptime format: []const u8, args: anytype) []u8 { | |
| 1246 | return fmt_lib.allocPrint(self.allocator, format, args) catch unreachable; | |
| 1247 | } | |
| 1248 | ||
| 1249 | pub fn findProgram(self: *Builder, names: []const []const u8, paths: []const []const u8) ![]const u8 { | |
| 1250 | // TODO report error for ambiguous situations | |
| 1251 | const exe_extension = @as(CrossTarget, .{}).exeFileExt(); | |
| 1252 | for (self.search_prefixes.items) |search_prefix| { | |
| 1253 | for (names) |name| { | |
| 1254 | if (fs.path.isAbsolute(name)) { | |
| 1255 | return name; | |
| 1256 | } | |
| 1257 | const full_path = self.pathJoin(&.{ | |
| 1258 | search_prefix, | |
| 1259 | "bin", | |
| 1260 | self.fmt("{s}{s}", .{ name, exe_extension }), | |
| 1261 | }); | |
| 1262 | return fs.realpathAlloc(self.allocator, full_path) catch continue; | |
| 1263 | } | |
| 1264 | } | |
| 1265 | if (self.env_map.get("PATH")) |PATH| { | |
| 1266 | for (names) |name| { | |
| 1267 | if (fs.path.isAbsolute(name)) { | |
| 1268 | return name; | |
| 1269 | } | |
| 1270 | var it = mem.tokenize(u8, PATH, &[_]u8{fs.path.delimiter}); | |
| 1271 | while (it.next()) |path| { | |
| 1272 | const full_path = self.pathJoin(&.{ | |
| 1273 | path, | |
| 1274 | self.fmt("{s}{s}", .{ name, exe_extension }), | |
| 1275 | }); | |
| 1276 | return fs.realpathAlloc(self.allocator, full_path) catch continue; | |
| 1277 | } | |
| 1278 | } | |
| 1279 | } | |
| 1280 | for (names) |name| { | |
| 1281 | if (fs.path.isAbsolute(name)) { | |
| 1282 | return name; | |
| 1283 | } | |
| 1284 | for (paths) |path| { | |
| 1285 | const full_path = self.pathJoin(&.{ | |
| 1286 | path, | |
| 1287 | self.fmt("{s}{s}", .{ name, exe_extension }), | |
| 1288 | }); | |
| 1289 | return fs.realpathAlloc(self.allocator, full_path) catch continue; | |
| 1290 | } | |
| 1291 | } | |
| 1292 | return error.FileNotFound; | |
| 1293 | } | |
| 1294 | ||
| 1295 | pub fn execAllowFail( | |
| 1296 | self: *Builder, | |
| 1297 | argv: []const []const u8, | |
| 1298 | out_code: *u8, | |
| 1299 | stderr_behavior: std.ChildProcess.StdIo, | |
| 1300 | ) ExecError![]u8 { | |
| 1301 | assert(argv.len != 0); | |
| 1302 | ||
| 1303 | if (!std.process.can_spawn) | |
| 1304 | return error.ExecNotSupported; | |
| 1305 | ||
| 1306 | const max_output_size = 400 * 1024; | |
| 1307 | var child = std.ChildProcess.init(argv, self.allocator); | |
| 1308 | child.stdin_behavior = .Ignore; | |
| 1309 | child.stdout_behavior = .Pipe; | |
| 1310 | child.stderr_behavior = stderr_behavior; | |
| 1311 | child.env_map = self.env_map; | |
| 1312 | ||
| 1313 | try child.spawn(); | |
| 1314 | ||
| 1315 | const stdout = child.stdout.?.reader().readAllAlloc(self.allocator, max_output_size) catch { | |
| 1316 | return error.ReadFailure; | |
| 1317 | }; | |
| 1318 | errdefer self.allocator.free(stdout); | |
| 1319 | ||
| 1320 | const term = try child.wait(); | |
| 1321 | switch (term) { | |
| 1322 | .Exited => |code| { | |
| 1323 | if (code != 0) { | |
| 1324 | out_code.* = @truncate(u8, code); | |
| 1325 | return error.ExitCodeFailure; | |
| 1326 | } | |
| 1327 | return stdout; | |
| 1328 | }, | |
| 1329 | .Signal, .Stopped, .Unknown => |code| { | |
| 1330 | out_code.* = @truncate(u8, code); | |
| 1331 | return error.ProcessTerminated; | |
| 1332 | }, | |
| 1333 | } | |
| 1334 | } | |
| 1335 | ||
| 1336 | pub fn execFromStep(self: *Builder, argv: []const []const u8, src_step: ?*Step) ![]u8 { | |
| 1337 | assert(argv.len != 0); | |
| 1338 | ||
| 1339 | if (self.verbose) { | |
| 1340 | printCmd(null, argv); | |
| 1341 | } | |
| 1342 | ||
| 1343 | if (!std.process.can_spawn) { | |
| 1344 | if (src_step) |s| log.err("{s}...", .{s.name}); | |
| 1345 | log.err("Unable to spawn the following command: cannot spawn child process", .{}); | |
| 1346 | printCmd(null, argv); | |
| 1347 | std.os.abort(); | |
| 1348 | } | |
| 1349 | ||
| 1350 | var code: u8 = undefined; | |
| 1351 | return self.execAllowFail(argv, &code, .Inherit) catch |err| switch (err) { | |
| 1352 | error.ExecNotSupported => { | |
| 1353 | if (src_step) |s| log.err("{s}...", .{s.name}); | |
| 1354 | log.err("Unable to spawn the following command: cannot spawn child process", .{}); | |
| 1355 | printCmd(null, argv); | |
| 1356 | std.os.abort(); | |
| 1357 | }, | |
| 1358 | error.FileNotFound => { | |
| 1359 | if (src_step) |s| log.err("{s}...", .{s.name}); | |
| 1360 | log.err("Unable to spawn the following command: file not found", .{}); | |
| 1361 | printCmd(null, argv); | |
| 1362 | std.os.exit(@truncate(u8, code)); | |
| 1363 | }, | |
| 1364 | error.ExitCodeFailure => { | |
| 1365 | if (src_step) |s| log.err("{s}...", .{s.name}); | |
| 1366 | if (self.prominent_compile_errors) { | |
| 1367 | log.err("The step exited with error code {d}", .{code}); | |
| 1368 | } else { | |
| 1369 | log.err("The following command exited with error code {d}:", .{code}); | |
| 1370 | printCmd(null, argv); | |
| 1371 | } | |
| 1372 | ||
| 1373 | std.os.exit(@truncate(u8, code)); | |
| 1374 | }, | |
| 1375 | error.ProcessTerminated => { | |
| 1376 | if (src_step) |s| log.err("{s}...", .{s.name}); | |
| 1377 | log.err("The following command terminated unexpectedly:", .{}); | |
| 1378 | printCmd(null, argv); | |
| 1379 | std.os.exit(@truncate(u8, code)); | |
| 1380 | }, | |
| 1381 | else => |e| return e, | |
| 1382 | }; | |
| 1383 | } | |
| 1384 | ||
| 1385 | pub fn exec(self: *Builder, argv: []const []const u8) ![]u8 { | |
| 1386 | return self.execFromStep(argv, null); | |
| 1387 | } | |
| 1388 | ||
| 1389 | pub fn addSearchPrefix(self: *Builder, search_prefix: []const u8) void { | |
| 1390 | self.search_prefixes.append(self.dupePath(search_prefix)) catch unreachable; | |
| 1391 | } | |
| 1392 | ||
| 1393 | pub fn getInstallPath(self: *Builder, dir: InstallDir, dest_rel_path: []const u8) []const u8 { | |
| 1394 | assert(!fs.path.isAbsolute(dest_rel_path)); // Install paths must be relative to the prefix | |
| 1395 | const base_dir = switch (dir) { | |
| 1396 | .prefix => self.install_path, | |
| 1397 | .bin => self.exe_dir, | |
| 1398 | .lib => self.lib_dir, | |
| 1399 | .header => self.h_dir, | |
| 1400 | .custom => |path| self.pathJoin(&.{ self.install_path, path }), | |
| 1401 | }; | |
| 1402 | return fs.path.resolve( | |
| 1403 | self.allocator, | |
| 1404 | &[_][]const u8{ base_dir, dest_rel_path }, | |
| 1405 | ) catch unreachable; | |
| 1406 | } | |
| 1407 | ||
| 1408 | pub const Dependency = struct { | |
| 1409 | builder: *Builder, | |
| 1410 | ||
| 1411 | pub fn artifact(d: *Dependency, name: []const u8) *LibExeObjStep { | |
| 1412 | var found: ?*LibExeObjStep = null; | |
| 1413 | for (d.builder.install_tls.step.dependencies.items) |dep_step| { | |
| 1414 | const inst = dep_step.cast(InstallArtifactStep) orelse continue; | |
| 1415 | if (mem.eql(u8, inst.artifact.name, name)) { | |
| 1416 | if (found != null) panic("artifact name '{s}' is ambiguous", .{name}); | |
| 1417 | found = inst.artifact; | |
| 1418 | } | |
| 1419 | } | |
| 1420 | return found orelse { | |
| 1421 | for (d.builder.install_tls.step.dependencies.items) |dep_step| { | |
| 1422 | const inst = dep_step.cast(InstallArtifactStep) orelse continue; | |
| 1423 | log.info("available artifact: '{s}'", .{inst.artifact.name}); | |
| 1424 | } | |
| 1425 | panic("unable to find artifact '{s}'", .{name}); | |
| 1426 | }; | |
| 1427 | } | |
| 1428 | }; | |
| 1429 | ||
| 1430 | pub fn dependency(b: *Builder, name: []const u8, args: anytype) *Dependency { | |
| 1431 | const build_runner = @import("root"); | |
| 1432 | const deps = build_runner.dependencies; | |
| 1433 | ||
| 1434 | inline for (@typeInfo(deps.imports).Struct.decls) |decl| { | |
| 1435 | if (mem.startsWith(u8, decl.name, b.dep_prefix) and | |
| 1436 | mem.endsWith(u8, decl.name, name) and | |
| 1437 | decl.name.len == b.dep_prefix.len + name.len) | |
| 1438 | { | |
| 1439 | const build_zig = @field(deps.imports, decl.name); | |
| 1440 | const build_root = @field(deps.build_root, decl.name); | |
| 1441 | return dependencyInner(b, name, build_root, build_zig, args); | |
| 1442 | } | |
| 1443 | } | |
| 1444 | ||
| 1445 | const full_path = b.pathFromRoot("build.zig.ini"); | |
| 1446 | std.debug.print("no dependency named '{s}' in '{s}'\n", .{ name, full_path }); | |
| 1447 | std.process.exit(1); | |
| 1448 | } | |
| 1449 | ||
| 1450 | fn dependencyInner( | |
| 1451 | b: *Builder, | |
| 1452 | name: []const u8, | |
| 1453 | build_root: []const u8, | |
| 1454 | comptime build_zig: type, | |
| 1455 | args: anytype, | |
| 1456 | ) *Dependency { | |
| 1457 | const sub_builder = b.createChild(name, build_root, args) catch unreachable; | |
| 1458 | sub_builder.runBuild(build_zig) catch unreachable; | |
| 1459 | const dep = b.allocator.create(Dependency) catch unreachable; | |
| 1460 | dep.* = .{ .builder = sub_builder }; | |
| 1461 | return dep; | |
| 1462 | } | |
| 1463 | ||
| 1464 | pub fn runBuild(b: *Builder, build_zig: anytype) anyerror!void { | |
| 1465 | switch (@typeInfo(@typeInfo(@TypeOf(build_zig.build)).Fn.return_type.?)) { | |
| 1466 | .Void => build_zig.build(b), | |
| 1467 | .ErrorUnion => try build_zig.build(b), | |
| 1468 | else => @compileError("expected return type of build to be 'void' or '!void'"), | |
| 1469 | } | |
| 1470 | } | |
| 1471 | }; | |
| 1472 | ||
| 1473 | test "builder.findProgram compiles" { | |
| 1474 | if (builtin.os.tag == .wasi) return error.SkipZigTest; | |
| 1475 | ||
| 1476 | var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); | |
| 1477 | defer arena.deinit(); | |
| 1478 | ||
| 1479 | const builder = try Builder.create( | |
| 1480 | arena.allocator(), | |
| 1481 | "zig", | |
| 1482 | "zig-cache", | |
| 1483 | "zig-cache", | |
| 1484 | "zig-cache", | |
| 1485 | ); | |
| 1486 | defer builder.destroy(); | |
| 1487 | _ = builder.findProgram(&[_][]const u8{}, &[_][]const u8{}) catch null; | |
| 1488 | } | |
| 1489 | ||
| 1490 | pub const Pkg = struct { | |
| 1491 | name: []const u8, | |
| 1492 | source: FileSource, | |
| 1493 | dependencies: ?[]const Pkg = null, | |
| 1494 | }; | |
| 1495 | ||
| 1496 | /// A file that is generated by a build step. | |
| 1497 | /// This struct is an interface that is meant to be used with `@fieldParentPtr` to implement the actual path logic. | |
| 1498 | pub const GeneratedFile = struct { | |
| 1499 | /// The step that generates the file | |
| 1500 | step: *Step, | |
| 1501 | ||
| 1502 | /// The path to the generated file. Must be either absolute or relative to the build root. | |
| 1503 | /// This value must be set in the `fn make()` of the `step` and must not be `null` afterwards. | |
| 1504 | path: ?[]const u8 = null, | |
| 1505 | ||
| 1506 | pub fn getPath(self: GeneratedFile) []const u8 { | |
| 1507 | return self.path orelse std.debug.panic( | |
| 1508 | "getPath() was called on a GeneratedFile that wasn't build yet. Is there a missing Step dependency on step '{s}'?", | |
| 1509 | .{self.step.name}, | |
| 1510 | ); | |
| 1511 | } | |
| 1512 | }; | |
| 1513 | ||
| 1514 | /// A file source is a reference to an existing or future file. | |
| 1515 | /// | |
| 1516 | pub const FileSource = union(enum) { | |
| 1517 | /// A plain file path, relative to build root or absolute. | |
| 1518 | path: []const u8, | |
| 1519 | ||
| 1520 | /// A file that is generated by an interface. Those files usually are | |
| 1521 | /// not available until built by a build step. | |
| 1522 | generated: *const GeneratedFile, | |
| 1523 | ||
| 1524 | /// Returns a new file source that will have a relative path to the build root guaranteed. | |
| 1525 | /// This should be preferred over setting `.path` directly as it documents that the files are in the project directory. | |
| 1526 | pub fn relative(path: []const u8) FileSource { | |
| 1527 | std.debug.assert(!std.fs.path.isAbsolute(path)); | |
| 1528 | return FileSource{ .path = path }; | |
| 1529 | } | |
| 1530 | ||
| 1531 | /// Returns a string that can be shown to represent the file source. | |
| 1532 | /// Either returns the path or `"generated"`. | |
| 1533 | pub fn getDisplayName(self: FileSource) []const u8 { | |
| 1534 | return switch (self) { | |
| 1535 | .path => self.path, | |
| 1536 | .generated => "generated", | |
| 1537 | }; | |
| 1538 | } | |
| 1539 | ||
| 1540 | /// Adds dependencies this file source implies to the given step. | |
| 1541 | pub fn addStepDependencies(self: FileSource, step: *Step) void { | |
| 1542 | switch (self) { | |
| 1543 | .path => {}, | |
| 1544 | .generated => |gen| step.dependOn(gen.step), | |
| 1545 | } | |
| 1546 | } | |
| 1547 | ||
| 1548 | /// Should only be called during make(), returns a path relative to the build root or absolute. | |
| 1549 | pub fn getPath(self: FileSource, builder: *Builder) []const u8 { | |
| 1550 | const path = switch (self) { | |
| 1551 | .path => |p| builder.pathFromRoot(p), | |
| 1552 | .generated => |gen| gen.getPath(), | |
| 1553 | }; | |
| 1554 | return path; | |
| 1555 | } | |
| 1556 | ||
| 1557 | /// Duplicates the file source for a given builder. | |
| 1558 | pub fn dupe(self: FileSource, b: *Builder) FileSource { | |
| 1559 | return switch (self) { | |
| 1560 | .path => |p| .{ .path = b.dupePath(p) }, | |
| 1561 | .generated => |gen| .{ .generated = gen }, | |
| 1562 | }; | |
| 1563 | } | |
| 1564 | }; | |
| 1565 | ||
| 1566 | /// Allocates a new string for assigning a value to a named macro. | |
| 1567 | /// If the value is omitted, it is set to 1. | |
| 1568 | /// `name` and `value` need not live longer than the function call. | |
| 1569 | pub fn constructCMacro(allocator: Allocator, name: []const u8, value: ?[]const u8) []const u8 { | |
| 1570 | var macro = allocator.alloc( | |
| 1571 | u8, | |
| 1572 | name.len + if (value) |value_slice| value_slice.len + 1 else 0, | |
| 1573 | ) catch |err| if (err == error.OutOfMemory) @panic("Out of memory") else unreachable; | |
| 1574 | mem.copy(u8, macro, name); | |
| 1575 | if (value) |value_slice| { | |
| 1576 | macro[name.len] = '='; | |
| 1577 | mem.copy(u8, macro[name.len + 1 ..], value_slice); | |
| 1578 | } | |
| 1579 | return macro; | |
| 1580 | } | |
| 1581 | ||
| 1582 | /// deprecated: use `InstallDirStep.Options` | |
| 1583 | pub const InstallDirectoryOptions = InstallDirStep.Options; | |
| 1584 | ||
| 1585 | pub const Step = struct { | |
| 1586 | id: Id, | |
| 1587 | name: []const u8, | |
| 1588 | makeFn: MakeFn, | |
| 1589 | dependencies: ArrayList(*Step), | |
| 1590 | loop_flag: bool, | |
| 1591 | done_flag: bool, | |
| 1592 | ||
| 1593 | const MakeFn = *const fn (self: *Step) anyerror!void; | |
| 1594 | ||
| 1595 | pub const Id = enum { | |
| 1596 | top_level, | |
| 1597 | lib_exe_obj, | |
| 1598 | install_artifact, | |
| 1599 | install_file, | |
| 1600 | install_dir, | |
| 1601 | log, | |
| 1602 | remove_dir, | |
| 1603 | fmt, | |
| 1604 | translate_c, | |
| 1605 | write_file, | |
| 1606 | run, | |
| 1607 | emulatable_run, | |
| 1608 | check_file, | |
| 1609 | check_object, | |
| 1610 | config_header, | |
| 1611 | install_raw, | |
| 1612 | options, | |
| 1613 | custom, | |
| 1614 | ||
| 1615 | pub fn Type(comptime id: Id) type { | |
| 1616 | return switch (id) { | |
| 1617 | .top_level => Builder.TopLevelStep, | |
| 1618 | .lib_exe_obj => LibExeObjStep, | |
| 1619 | .install_artifact => InstallArtifactStep, | |
| 1620 | .install_file => InstallFileStep, | |
| 1621 | .install_dir => InstallDirStep, | |
| 1622 | .log => LogStep, | |
| 1623 | .remove_dir => RemoveDirStep, | |
| 1624 | .fmt => FmtStep, | |
| 1625 | .translate_c => TranslateCStep, | |
| 1626 | .write_file => WriteFileStep, | |
| 1627 | .run => RunStep, | |
| 1628 | .emulatable_run => EmulatableRunStep, | |
| 1629 | .check_file => CheckFileStep, | |
| 1630 | .check_object => CheckObjectStep, | |
| 1631 | .config_header => ConfigHeaderStep, | |
| 1632 | .install_raw => InstallRawStep, | |
| 1633 | .options => OptionsStep, | |
| 1634 | .custom => @compileError("no type available for custom step"), | |
| 1635 | }; | |
| 1636 | } | |
| 1637 | }; | |
| 1638 | ||
| 1639 | pub fn init(id: Id, name: []const u8, allocator: Allocator, makeFn: MakeFn) Step { | |
| 1640 | return Step{ | |
| 1641 | .id = id, | |
| 1642 | .name = allocator.dupe(u8, name) catch unreachable, | |
| 1643 | .makeFn = makeFn, | |
| 1644 | .dependencies = ArrayList(*Step).init(allocator), | |
| 1645 | .loop_flag = false, | |
| 1646 | .done_flag = false, | |
| 1647 | }; | |
| 1648 | } | |
| 1649 | pub fn initNoOp(id: Id, name: []const u8, allocator: Allocator) Step { | |
| 1650 | return init(id, name, allocator, makeNoOp); | |
| 1651 | } | |
| 1652 | ||
| 1653 | pub fn make(self: *Step) !void { | |
| 1654 | if (self.done_flag) return; | |
| 1655 | ||
| 1656 | try self.makeFn(self); | |
| 1657 | self.done_flag = true; | |
| 1658 | } | |
| 1659 | ||
| 1660 | pub fn dependOn(self: *Step, other: *Step) void { | |
| 1661 | self.dependencies.append(other) catch unreachable; | |
| 1662 | } | |
| 1663 | ||
| 1664 | fn makeNoOp(self: *Step) anyerror!void { | |
| 1665 | _ = self; | |
| 1666 | } | |
| 1667 | ||
| 1668 | pub fn cast(step: *Step, comptime T: type) ?*T { | |
| 1669 | if (step.id == T.base_id) { | |
| 1670 | return @fieldParentPtr(T, "step", step); | |
| 1671 | } | |
| 1672 | return null; | |
| 1673 | } | |
| 1674 | }; | |
| 1675 | ||
| 1676 | pub const VcpkgRoot = union(VcpkgRootStatus) { | |
| 1677 | unattempted: void, | |
| 1678 | not_found: void, | |
| 1679 | found: []const u8, | |
| 1680 | }; | |
| 1681 | ||
| 1682 | pub const VcpkgRootStatus = enum { | |
| 1683 | unattempted, | |
| 1684 | not_found, | |
| 1685 | found, | |
| 1686 | }; | |
| 1687 | ||
| 1688 | pub const InstallDir = union(enum) { | |
| 1689 | prefix: void, | |
| 1690 | lib: void, | |
| 1691 | bin: void, | |
| 1692 | header: void, | |
| 1693 | /// A path relative to the prefix | |
| 1694 | custom: []const u8, | |
| 1695 | ||
| 1696 | /// Duplicates the install directory including the path if set to custom. | |
| 1697 | pub fn dupe(self: InstallDir, builder: *Builder) InstallDir { | |
| 1698 | if (self == .custom) { | |
| 1699 | // Written with this temporary to avoid RLS problems | |
| 1700 | const duped_path = builder.dupe(self.custom); | |
| 1701 | return .{ .custom = duped_path }; | |
| 1702 | } else { | |
| 1703 | return self; | |
| 1704 | } | |
| 1705 | } | |
| 1706 | }; | |
| 1707 | ||
| 1708 | pub const InstalledFile = struct { | |
| 1709 | dir: InstallDir, | |
| 1710 | path: []const u8, | |
| 1711 | ||
| 1712 | /// Duplicates the installed file path and directory. | |
| 1713 | pub fn dupe(self: InstalledFile, builder: *Builder) InstalledFile { | |
| 1714 | return .{ | |
| 1715 | .dir = self.dir.dupe(builder), | |
| 1716 | .path = builder.dupe(self.path), | |
| 1717 | }; | |
| 1718 | } | |
| 1719 | }; | |
| 1720 | ||
| 1721 | test "dupePkg()" { | |
| 1722 | if (builtin.os.tag == .wasi) return error.SkipZigTest; | |
| 1723 | ||
| 1724 | var arena = std.heap.ArenaAllocator.init(std.testing.allocator); | |
| 1725 | defer arena.deinit(); | |
| 1726 | var builder = try Builder.create( | |
| 1727 | arena.allocator(), | |
| 1728 | "test", | |
| 1729 | "test", | |
| 1730 | "test", | |
| 1731 | "test", | |
| 1732 | ); | |
| 1733 | defer builder.destroy(); | |
| 1734 | ||
| 1735 | var pkg_dep = Pkg{ | |
| 1736 | .name = "pkg_dep", | |
| 1737 | .source = .{ .path = "/not/a/pkg_dep.zig" }, | |
| 1738 | }; | |
| 1739 | var pkg_top = Pkg{ | |
| 1740 | .name = "pkg_top", | |
| 1741 | .source = .{ .path = "/not/a/pkg_top.zig" }, | |
| 1742 | .dependencies = &[_]Pkg{pkg_dep}, | |
| 1743 | }; | |
| 1744 | const dupe = builder.dupePkg(pkg_top); | |
| 1745 | ||
| 1746 | const original_deps = pkg_top.dependencies.?; | |
| 1747 | const dupe_deps = dupe.dependencies.?; | |
| 1748 | ||
| 1749 | // probably the same top level package details | |
| 1750 | try std.testing.expectEqualStrings(pkg_top.name, dupe.name); | |
| 1751 | ||
| 1752 | // probably the same dependencies | |
| 1753 | try std.testing.expectEqual(original_deps.len, dupe_deps.len); | |
| 1754 | try std.testing.expectEqual(original_deps[0].name, pkg_dep.name); | |
| 1755 | ||
| 1756 | // could segfault otherwise if pointers in duplicated package's fields are | |
| 1757 | // the same as those in stack allocated package's fields | |
| 1758 | try std.testing.expect(dupe_deps.ptr != original_deps.ptr); | |
| 1759 | try std.testing.expect(dupe.name.ptr != pkg_top.name.ptr); | |
| 1760 | try std.testing.expect(dupe.source.path.ptr != pkg_top.source.path.ptr); | |
| 1761 | try std.testing.expect(dupe_deps[0].name.ptr != pkg_dep.name.ptr); | |
| 1762 | try std.testing.expect(dupe_deps[0].source.path.ptr != pkg_dep.source.path.ptr); | |
| 1763 | } | |
| 1764 | ||
| 1765 | test { | |
| 1766 | _ = CheckFileStep; | |
| 1767 | _ = CheckObjectStep; | |
| 1768 | _ = EmulatableRunStep; | |
| 1769 | _ = FmtStep; | |
| 1770 | _ = InstallArtifactStep; | |
| 1771 | _ = InstallDirStep; | |
| 1772 | _ = InstallFileStep; | |
| 1773 | _ = InstallRawStep; | |
| 1774 | _ = LibExeObjStep; | |
| 1775 | _ = LogStep; | |
| 1776 | _ = OptionsStep; | |
| 1777 | _ = RemoveDirStep; | |
| 1778 | _ = RunStep; | |
| 1779 | _ = TranslateCStep; | |
| 1780 | _ = WriteFileStep; | |
| 1781 | } |
lib/std/build/CheckFileStep.zig deleted-53| ... | ... | @@ -1,53 +0,0 @@ |
| 1 | const std = @import("../std.zig"); | |
| 2 | const build = std.build; | |
| 3 | const Step = build.Step; | |
| 4 | const Builder = build.Builder; | |
| 5 | const fs = std.fs; | |
| 6 | const mem = std.mem; | |
| 7 | ||
| 8 | const CheckFileStep = @This(); | |
| 9 | ||
| 10 | pub const base_id = .check_file; | |
| 11 | ||
| 12 | step: Step, | |
| 13 | builder: *Builder, | |
| 14 | expected_matches: []const []const u8, | |
| 15 | source: build.FileSource, | |
| 16 | max_bytes: usize = 20 * 1024 * 1024, | |
| 17 | ||
| 18 | pub fn create( | |
| 19 | builder: *Builder, | |
| 20 | source: build.FileSource, | |
| 21 | expected_matches: []const []const u8, | |
| 22 | ) *CheckFileStep { | |
| 23 | const self = builder.allocator.create(CheckFileStep) catch unreachable; | |
| 24 | self.* = CheckFileStep{ | |
| 25 | .builder = builder, | |
| 26 | .step = Step.init(.check_file, "CheckFile", builder.allocator, make), | |
| 27 | .source = source.dupe(builder), | |
| 28 | .expected_matches = builder.dupeStrings(expected_matches), | |
| 29 | }; | |
| 30 | self.source.addStepDependencies(&self.step); | |
| 31 | return self; | |
| 32 | } | |
| 33 | ||
| 34 | fn make(step: *Step) !void { | |
| 35 | const self = @fieldParentPtr(CheckFileStep, "step", step); | |
| 36 | ||
| 37 | const src_path = self.source.getPath(self.builder); | |
| 38 | const contents = try fs.cwd().readFileAlloc(self.builder.allocator, src_path, self.max_bytes); | |
| 39 | ||
| 40 | for (self.expected_matches) |expected_match| { | |
| 41 | if (mem.indexOf(u8, contents, expected_match) == null) { | |
| 42 | std.debug.print( | |
| 43 | \\ | |
| 44 | \\========= Expected to find: =================== | |
| 45 | \\{s} | |
| 46 | \\========= But file does not contain it: ======= | |
| 47 | \\{s} | |
| 48 | \\ | |
| 49 | , .{ expected_match, contents }); | |
| 50 | return error.TestFailed; | |
| 51 | } | |
| 52 | } | |
| 53 | } |
lib/std/build/CheckObjectStep.zig deleted-1026| ... | ... | @@ -1,1026 +0,0 @@ |
| 1 | const std = @import("../std.zig"); | |
| 2 | const assert = std.debug.assert; | |
| 3 | const build = std.build; | |
| 4 | const fs = std.fs; | |
| 5 | const macho = std.macho; | |
| 6 | const math = std.math; | |
| 7 | const mem = std.mem; | |
| 8 | const testing = std.testing; | |
| 9 | ||
| 10 | const CheckObjectStep = @This(); | |
| 11 | ||
| 12 | const Allocator = mem.Allocator; | |
| 13 | const Builder = build.Builder; | |
| 14 | const Step = build.Step; | |
| 15 | const EmulatableRunStep = build.EmulatableRunStep; | |
| 16 | ||
| 17 | pub const base_id = .check_object; | |
| 18 | ||
| 19 | step: Step, | |
| 20 | builder: *Builder, | |
| 21 | source: build.FileSource, | |
| 22 | max_bytes: usize = 20 * 1024 * 1024, | |
| 23 | checks: std.ArrayList(Check), | |
| 24 | dump_symtab: bool = false, | |
| 25 | obj_format: std.Target.ObjectFormat, | |
| 26 | ||
| 27 | pub fn create(builder: *Builder, source: build.FileSource, obj_format: std.Target.ObjectFormat) *CheckObjectStep { | |
| 28 | const gpa = builder.allocator; | |
| 29 | const self = gpa.create(CheckObjectStep) catch unreachable; | |
| 30 | self.* = .{ | |
| 31 | .builder = builder, | |
| 32 | .step = Step.init(.check_file, "CheckObject", gpa, make), | |
| 33 | .source = source.dupe(builder), | |
| 34 | .checks = std.ArrayList(Check).init(gpa), | |
| 35 | .obj_format = obj_format, | |
| 36 | }; | |
| 37 | self.source.addStepDependencies(&self.step); | |
| 38 | return self; | |
| 39 | } | |
| 40 | ||
| 41 | /// Runs and (optionally) compares the output of a binary. | |
| 42 | /// Asserts `self` was generated from an executable step. | |
| 43 | pub fn runAndCompare(self: *CheckObjectStep) *EmulatableRunStep { | |
| 44 | const dependencies_len = self.step.dependencies.items.len; | |
| 45 | assert(dependencies_len > 0); | |
| 46 | const exe_step = self.step.dependencies.items[dependencies_len - 1]; | |
| 47 | const exe = exe_step.cast(std.build.LibExeObjStep).?; | |
| 48 | const emulatable_step = EmulatableRunStep.create(self.builder, "EmulatableRun", exe); | |
| 49 | emulatable_step.step.dependOn(&self.step); | |
| 50 | return emulatable_step; | |
| 51 | } | |
| 52 | ||
| 53 | /// There two types of actions currently suported: | |
| 54 | /// * `.match` - is the main building block of standard matchers with optional eat-all token `{*}` | |
| 55 | /// and extractors by name such as `{n_value}`. Please note this action is very simplistic in nature | |
| 56 | /// i.e., it won't really handle edge cases/nontrivial examples. But given that we do want to use | |
| 57 | /// it mainly to test the output of our object format parser-dumpers when testing the linkers, etc. | |
| 58 | /// it should be plenty useful in its current form. | |
| 59 | /// * `.compute_cmp` - can be used to perform an operation on the extracted global variables | |
| 60 | /// using the MatchAction. It currently only supports an addition. The operation is required | |
| 61 | /// to be specified in Reverse Polish Notation to ease in operator-precedence parsing (well, | |
| 62 | /// to avoid any parsing really). | |
| 63 | /// For example, if the two extracted values were saved as `vmaddr` and `entryoff` respectively | |
| 64 | /// they could then be added with this simple program `vmaddr entryoff +`. | |
| 65 | const Action = struct { | |
| 66 | tag: enum { match, not_present, compute_cmp }, | |
| 67 | phrase: []const u8, | |
| 68 | expected: ?ComputeCompareExpected = null, | |
| 69 | ||
| 70 | /// Will return true if the `phrase` was found in the `haystack`. | |
| 71 | /// Some examples include: | |
| 72 | /// | |
| 73 | /// LC 0 => will match in its entirety | |
| 74 | /// vmaddr {vmaddr} => will match `vmaddr` and then extract the following value as u64 | |
| 75 | /// and save under `vmaddr` global name (see `global_vars` param) | |
| 76 | /// name {*}libobjc{*}.dylib => will match `name` followed by a token which contains `libobjc` and `.dylib` | |
| 77 | /// in that order with other letters in between | |
| 78 | fn match(act: Action, haystack: []const u8, global_vars: anytype) !bool { | |
| 79 | assert(act.tag == .match or act.tag == .not_present); | |
| 80 | ||
| 81 | var candidate_var: ?struct { name: []const u8, value: u64 } = null; | |
| 82 | var hay_it = mem.tokenize(u8, mem.trim(u8, haystack, " "), " "); | |
| 83 | var needle_it = mem.tokenize(u8, mem.trim(u8, act.phrase, " "), " "); | |
| 84 | ||
| 85 | while (needle_it.next()) |needle_tok| { | |
| 86 | const hay_tok = hay_it.next() orelse return false; | |
| 87 | ||
| 88 | if (mem.indexOf(u8, needle_tok, "{*}")) |index| { | |
| 89 | // We have fuzzy matchers within the search pattern, so we match substrings. | |
| 90 | var start = index; | |
| 91 | var n_tok = needle_tok; | |
| 92 | var h_tok = hay_tok; | |
| 93 | while (true) { | |
| 94 | n_tok = n_tok[start + 3 ..]; | |
| 95 | const inner = if (mem.indexOf(u8, n_tok, "{*}")) |sub_end| | |
| 96 | n_tok[0..sub_end] | |
| 97 | else | |
| 98 | n_tok; | |
| 99 | if (mem.indexOf(u8, h_tok, inner) == null) return false; | |
| 100 | start = mem.indexOf(u8, n_tok, "{*}") orelse break; | |
| 101 | } | |
| 102 | } else if (mem.startsWith(u8, needle_tok, "{")) { | |
| 103 | const closing_brace = mem.indexOf(u8, needle_tok, "}") orelse return error.MissingClosingBrace; | |
| 104 | if (closing_brace != needle_tok.len - 1) return error.ClosingBraceNotLast; | |
| 105 | ||
| 106 | const name = needle_tok[1..closing_brace]; | |
| 107 | if (name.len == 0) return error.MissingBraceValue; | |
| 108 | const value = try std.fmt.parseInt(u64, hay_tok, 16); | |
| 109 | candidate_var = .{ | |
| 110 | .name = name, | |
| 111 | .value = value, | |
| 112 | }; | |
| 113 | } else { | |
| 114 | if (!mem.eql(u8, hay_tok, needle_tok)) return false; | |
| 115 | } | |
| 116 | } | |
| 117 | ||
| 118 | if (candidate_var) |v| { | |
| 119 | try global_vars.putNoClobber(v.name, v.value); | |
| 120 | } | |
| 121 | ||
| 122 | return true; | |
| 123 | } | |
| 124 | ||
| 125 | /// Will return true if the `phrase` is correctly parsed into an RPN program and | |
| 126 | /// its reduced, computed value compares using `op` with the expected value, either | |
| 127 | /// a literal or another extracted variable. | |
| 128 | fn computeCmp(act: Action, gpa: Allocator, global_vars: anytype) !bool { | |
| 129 | var op_stack = std.ArrayList(enum { add, sub, mod, mul }).init(gpa); | |
| 130 | var values = std.ArrayList(u64).init(gpa); | |
| 131 | ||
| 132 | var it = mem.tokenize(u8, act.phrase, " "); | |
| 133 | while (it.next()) |next| { | |
| 134 | if (mem.eql(u8, next, "+")) { | |
| 135 | try op_stack.append(.add); | |
| 136 | } else if (mem.eql(u8, next, "-")) { | |
| 137 | try op_stack.append(.sub); | |
| 138 | } else if (mem.eql(u8, next, "%")) { | |
| 139 | try op_stack.append(.mod); | |
| 140 | } else if (mem.eql(u8, next, "*")) { | |
| 141 | try op_stack.append(.mul); | |
| 142 | } else { | |
| 143 | const val = std.fmt.parseInt(u64, next, 0) catch blk: { | |
| 144 | break :blk global_vars.get(next) orelse { | |
| 145 | std.debug.print( | |
| 146 | \\ | |
| 147 | \\========= Variable was not extracted: =========== | |
| 148 | \\{s} | |
| 149 | \\ | |
| 150 | , .{next}); | |
| 151 | return error.UnknownVariable; | |
| 152 | }; | |
| 153 | }; | |
| 154 | try values.append(val); | |
| 155 | } | |
| 156 | } | |
| 157 | ||
| 158 | var op_i: usize = 1; | |
| 159 | var reduced: u64 = values.items[0]; | |
| 160 | for (op_stack.items) |op| { | |
| 161 | const other = values.items[op_i]; | |
| 162 | switch (op) { | |
| 163 | .add => { | |
| 164 | reduced += other; | |
| 165 | }, | |
| 166 | .sub => { | |
| 167 | reduced -= other; | |
| 168 | }, | |
| 169 | .mod => { | |
| 170 | reduced %= other; | |
| 171 | }, | |
| 172 | .mul => { | |
| 173 | reduced *= other; | |
| 174 | }, | |
| 175 | } | |
| 176 | op_i += 1; | |
| 177 | } | |
| 178 | ||
| 179 | const exp_value = switch (act.expected.?.value) { | |
| 180 | .variable => |name| global_vars.get(name) orelse { | |
| 181 | std.debug.print( | |
| 182 | \\ | |
| 183 | \\========= Variable was not extracted: =========== | |
| 184 | \\{s} | |
| 185 | \\ | |
| 186 | , .{name}); | |
| 187 | return error.UnknownVariable; | |
| 188 | }, | |
| 189 | .literal => |x| x, | |
| 190 | }; | |
| 191 | return math.compare(reduced, act.expected.?.op, exp_value); | |
| 192 | } | |
| 193 | }; | |
| 194 | ||
| 195 | const ComputeCompareExpected = struct { | |
| 196 | op: math.CompareOperator, | |
| 197 | value: union(enum) { | |
| 198 | variable: []const u8, | |
| 199 | literal: u64, | |
| 200 | }, | |
| 201 | ||
| 202 | pub fn format( | |
| 203 | value: @This(), | |
| 204 | comptime fmt: []const u8, | |
| 205 | options: std.fmt.FormatOptions, | |
| 206 | writer: anytype, | |
| 207 | ) !void { | |
| 208 | if (fmt.len != 0) std.fmt.invalidFmtError(fmt, value); | |
| 209 | _ = options; | |
| 210 | try writer.print("{s} ", .{@tagName(value.op)}); | |
| 211 | switch (value.value) { | |
| 212 | .variable => |name| try writer.writeAll(name), | |
| 213 | .literal => |x| try writer.print("{x}", .{x}), | |
| 214 | } | |
| 215 | } | |
| 216 | }; | |
| 217 | ||
| 218 | const Check = struct { | |
| 219 | builder: *Builder, | |
| 220 | actions: std.ArrayList(Action), | |
| 221 | ||
| 222 | fn create(b: *Builder) Check { | |
| 223 | return .{ | |
| 224 | .builder = b, | |
| 225 | .actions = std.ArrayList(Action).init(b.allocator), | |
| 226 | }; | |
| 227 | } | |
| 228 | ||
| 229 | fn match(self: *Check, phrase: []const u8) void { | |
| 230 | self.actions.append(.{ | |
| 231 | .tag = .match, | |
| 232 | .phrase = self.builder.dupe(phrase), | |
| 233 | }) catch unreachable; | |
| 234 | } | |
| 235 | ||
| 236 | fn notPresent(self: *Check, phrase: []const u8) void { | |
| 237 | self.actions.append(.{ | |
| 238 | .tag = .not_present, | |
| 239 | .phrase = self.builder.dupe(phrase), | |
| 240 | }) catch unreachable; | |
| 241 | } | |
| 242 | ||
| 243 | fn computeCmp(self: *Check, phrase: []const u8, expected: ComputeCompareExpected) void { | |
| 244 | self.actions.append(.{ | |
| 245 | .tag = .compute_cmp, | |
| 246 | .phrase = self.builder.dupe(phrase), | |
| 247 | .expected = expected, | |
| 248 | }) catch unreachable; | |
| 249 | } | |
| 250 | }; | |
| 251 | ||
| 252 | /// Creates a new sequence of actions with `phrase` as the first anchor searched phrase. | |
| 253 | pub fn checkStart(self: *CheckObjectStep, phrase: []const u8) void { | |
| 254 | var new_check = Check.create(self.builder); | |
| 255 | new_check.match(phrase); | |
| 256 | self.checks.append(new_check) catch unreachable; | |
| 257 | } | |
| 258 | ||
| 259 | /// Adds another searched phrase to the latest created Check with `CheckObjectStep.checkStart(...)`. | |
| 260 | /// Asserts at least one check already exists. | |
| 261 | pub fn checkNext(self: *CheckObjectStep, phrase: []const u8) void { | |
| 262 | assert(self.checks.items.len > 0); | |
| 263 | const last = &self.checks.items[self.checks.items.len - 1]; | |
| 264 | last.match(phrase); | |
| 265 | } | |
| 266 | ||
| 267 | /// Adds another searched phrase to the latest created Check with `CheckObjectStep.checkStart(...)` | |
| 268 | /// however ensures there is no matching phrase in the output. | |
| 269 | /// Asserts at least one check already exists. | |
| 270 | pub fn checkNotPresent(self: *CheckObjectStep, phrase: []const u8) void { | |
| 271 | assert(self.checks.items.len > 0); | |
| 272 | const last = &self.checks.items[self.checks.items.len - 1]; | |
| 273 | last.notPresent(phrase); | |
| 274 | } | |
| 275 | ||
| 276 | /// Creates a new check checking specifically symbol table parsed and dumped from the object | |
| 277 | /// file. | |
| 278 | /// Issuing this check will force parsing and dumping of the symbol table. | |
| 279 | pub fn checkInSymtab(self: *CheckObjectStep) void { | |
| 280 | self.dump_symtab = true; | |
| 281 | const symtab_label = switch (self.obj_format) { | |
| 282 | .macho => MachODumper.symtab_label, | |
| 283 | else => @panic("TODO other parsers"), | |
| 284 | }; | |
| 285 | self.checkStart(symtab_label); | |
| 286 | } | |
| 287 | ||
| 288 | /// Creates a new standalone, singular check which allows running simple binary operations | |
| 289 | /// on the extracted variables. It will then compare the reduced program with the value of | |
| 290 | /// the expected variable. | |
| 291 | pub fn checkComputeCompare( | |
| 292 | self: *CheckObjectStep, | |
| 293 | program: []const u8, | |
| 294 | expected: ComputeCompareExpected, | |
| 295 | ) void { | |
| 296 | var new_check = Check.create(self.builder); | |
| 297 | new_check.computeCmp(program, expected); | |
| 298 | self.checks.append(new_check) catch unreachable; | |
| 299 | } | |
| 300 | ||
| 301 | fn make(step: *Step) !void { | |
| 302 | const self = @fieldParentPtr(CheckObjectStep, "step", step); | |
| 303 | ||
| 304 | const gpa = self.builder.allocator; | |
| 305 | const src_path = self.source.getPath(self.builder); | |
| 306 | const contents = try fs.cwd().readFileAllocOptions( | |
| 307 | gpa, | |
| 308 | src_path, | |
| 309 | self.max_bytes, | |
| 310 | null, | |
| 311 | @alignOf(u64), | |
| 312 | null, | |
| 313 | ); | |
| 314 | ||
| 315 | const output = switch (self.obj_format) { | |
| 316 | .macho => try MachODumper.parseAndDump(contents, .{ | |
| 317 | .gpa = gpa, | |
| 318 | .dump_symtab = self.dump_symtab, | |
| 319 | }), | |
| 320 | .elf => @panic("TODO elf parser"), | |
| 321 | .coff => @panic("TODO coff parser"), | |
| 322 | .wasm => try WasmDumper.parseAndDump(contents, .{ | |
| 323 | .gpa = gpa, | |
| 324 | .dump_symtab = self.dump_symtab, | |
| 325 | }), | |
| 326 | else => unreachable, | |
| 327 | }; | |
| 328 | ||
| 329 | var vars = std.StringHashMap(u64).init(gpa); | |
| 330 | ||
| 331 | for (self.checks.items) |chk| { | |
| 332 | var it = mem.tokenize(u8, output, "\r\n"); | |
| 333 | for (chk.actions.items) |act| { | |
| 334 | switch (act.tag) { | |
| 335 | .match => { | |
| 336 | while (it.next()) |line| { | |
| 337 | if (try act.match(line, &vars)) break; | |
| 338 | } else { | |
| 339 | std.debug.print( | |
| 340 | \\ | |
| 341 | \\========= Expected to find: ========================== | |
| 342 | \\{s} | |
| 343 | \\========= But parsed file does not contain it: ======= | |
| 344 | \\{s} | |
| 345 | \\ | |
| 346 | , .{ act.phrase, output }); | |
| 347 | return error.TestFailed; | |
| 348 | } | |
| 349 | }, | |
| 350 | .not_present => { | |
| 351 | while (it.next()) |line| { | |
| 352 | if (try act.match(line, &vars)) { | |
| 353 | std.debug.print( | |
| 354 | \\ | |
| 355 | \\========= Expected not to find: =================== | |
| 356 | \\{s} | |
| 357 | \\========= But parsed file does contain it: ======== | |
| 358 | \\{s} | |
| 359 | \\ | |
| 360 | , .{ act.phrase, output }); | |
| 361 | return error.TestFailed; | |
| 362 | } | |
| 363 | } | |
| 364 | }, | |
| 365 | .compute_cmp => { | |
| 366 | const res = act.computeCmp(gpa, vars) catch |err| switch (err) { | |
| 367 | error.UnknownVariable => { | |
| 368 | std.debug.print( | |
| 369 | \\========= From parsed file: ===================== | |
| 370 | \\{s} | |
| 371 | \\ | |
| 372 | , .{output}); | |
| 373 | return error.TestFailed; | |
| 374 | }, | |
| 375 | else => |e| return e, | |
| 376 | }; | |
| 377 | if (!res) { | |
| 378 | std.debug.print( | |
| 379 | \\ | |
| 380 | \\========= Comparison failed for action: =========== | |
| 381 | \\{s} {} | |
| 382 | \\========= From parsed file: ======================= | |
| 383 | \\{s} | |
| 384 | \\ | |
| 385 | , .{ act.phrase, act.expected.?, output }); | |
| 386 | return error.TestFailed; | |
| 387 | } | |
| 388 | }, | |
| 389 | } | |
| 390 | } | |
| 391 | } | |
| 392 | } | |
| 393 | ||
| 394 | const Opts = struct { | |
| 395 | gpa: ?Allocator = null, | |
| 396 | dump_symtab: bool = false, | |
| 397 | }; | |
| 398 | ||
| 399 | const MachODumper = struct { | |
| 400 | const LoadCommandIterator = macho.LoadCommandIterator; | |
| 401 | const symtab_label = "symtab"; | |
| 402 | ||
| 403 | fn parseAndDump(bytes: []align(@alignOf(u64)) const u8, opts: Opts) ![]const u8 { | |
| 404 | const gpa = opts.gpa orelse unreachable; // MachO dumper requires an allocator | |
| 405 | var stream = std.io.fixedBufferStream(bytes); | |
| 406 | const reader = stream.reader(); | |
| 407 | ||
| 408 | const hdr = try reader.readStruct(macho.mach_header_64); | |
| 409 | if (hdr.magic != macho.MH_MAGIC_64) { | |
| 410 | return error.InvalidMagicNumber; | |
| 411 | } | |
| 412 | ||
| 413 | var output = std.ArrayList(u8).init(gpa); | |
| 414 | const writer = output.writer(); | |
| 415 | ||
| 416 | var symtab: []const macho.nlist_64 = undefined; | |
| 417 | var strtab: []const u8 = undefined; | |
| 418 | var sections = std.ArrayList(macho.section_64).init(gpa); | |
| 419 | var imports = std.ArrayList([]const u8).init(gpa); | |
| 420 | ||
| 421 | var it = LoadCommandIterator{ | |
| 422 | .ncmds = hdr.ncmds, | |
| 423 | .buffer = bytes[@sizeOf(macho.mach_header_64)..][0..hdr.sizeofcmds], | |
| 424 | }; | |
| 425 | var i: usize = 0; | |
| 426 | while (it.next()) |cmd| { | |
| 427 | switch (cmd.cmd()) { | |
| 428 | .SEGMENT_64 => { | |
| 429 | const seg = cmd.cast(macho.segment_command_64).?; | |
| 430 | try sections.ensureUnusedCapacity(seg.nsects); | |
| 431 | for (cmd.getSections()) |sect| { | |
| 432 | sections.appendAssumeCapacity(sect); | |
| 433 | } | |
| 434 | }, | |
| 435 | .SYMTAB => if (opts.dump_symtab) { | |
| 436 | const lc = cmd.cast(macho.symtab_command).?; | |
| 437 | symtab = @ptrCast( | |
| 438 | [*]const macho.nlist_64, | |
| 439 | @alignCast(@alignOf(macho.nlist_64), &bytes[lc.symoff]), | |
| 440 | )[0..lc.nsyms]; | |
| 441 | strtab = bytes[lc.stroff..][0..lc.strsize]; | |
| 442 | }, | |
| 443 | .LOAD_DYLIB, | |
| 444 | .LOAD_WEAK_DYLIB, | |
| 445 | .REEXPORT_DYLIB, | |
| 446 | => { | |
| 447 | try imports.append(cmd.getDylibPathName()); | |
| 448 | }, | |
| 449 | else => {}, | |
| 450 | } | |
| 451 | ||
| 452 | try dumpLoadCommand(cmd, i, writer); | |
| 453 | try writer.writeByte('\n'); | |
| 454 | ||
| 455 | i += 1; | |
| 456 | } | |
| 457 | ||
| 458 | if (opts.dump_symtab) { | |
| 459 | try writer.print("{s}\n", .{symtab_label}); | |
| 460 | for (symtab) |sym| { | |
| 461 | if (sym.stab()) continue; | |
| 462 | const sym_name = mem.sliceTo(@ptrCast([*:0]const u8, strtab.ptr + sym.n_strx), 0); | |
| 463 | if (sym.sect()) { | |
| 464 | const sect = sections.items[sym.n_sect - 1]; | |
| 465 | try writer.print("{x} ({s},{s})", .{ | |
| 466 | sym.n_value, | |
| 467 | sect.segName(), | |
| 468 | sect.sectName(), | |
| 469 | }); | |
| 470 | if (sym.ext()) { | |
| 471 | try writer.writeAll(" external"); | |
| 472 | } | |
| 473 | try writer.print(" {s}\n", .{sym_name}); | |
| 474 | } else if (sym.undf()) { | |
| 475 | const ordinal = @divTrunc(@bitCast(i16, sym.n_desc), macho.N_SYMBOL_RESOLVER); | |
| 476 | const import_name = blk: { | |
| 477 | if (ordinal <= 0) { | |
| 478 | if (ordinal == macho.BIND_SPECIAL_DYLIB_SELF) | |
| 479 | break :blk "self import"; | |
| 480 | if (ordinal == macho.BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE) | |
| 481 | break :blk "main executable"; | |
| 482 | if (ordinal == macho.BIND_SPECIAL_DYLIB_FLAT_LOOKUP) | |
| 483 | break :blk "flat lookup"; | |
| 484 | unreachable; | |
| 485 | } | |
| 486 | const full_path = imports.items[@bitCast(u16, ordinal) - 1]; | |
| 487 | const basename = fs.path.basename(full_path); | |
| 488 | assert(basename.len > 0); | |
| 489 | const ext = mem.lastIndexOfScalar(u8, basename, '.') orelse basename.len; | |
| 490 | break :blk basename[0..ext]; | |
| 491 | }; | |
| 492 | try writer.writeAll("(undefined)"); | |
| 493 | if (sym.weakRef()) { | |
| 494 | try writer.writeAll(" weak"); | |
| 495 | } | |
| 496 | if (sym.ext()) { | |
| 497 | try writer.writeAll(" external"); | |
| 498 | } | |
| 499 | try writer.print(" {s} (from {s})\n", .{ | |
| 500 | sym_name, | |
| 501 | import_name, | |
| 502 | }); | |
| 503 | } else unreachable; | |
| 504 | } | |
| 505 | } | |
| 506 | ||
| 507 | return output.toOwnedSlice(); | |
| 508 | } | |
| 509 | ||
| 510 | fn dumpLoadCommand(lc: macho.LoadCommandIterator.LoadCommand, index: usize, writer: anytype) !void { | |
| 511 | // print header first | |
| 512 | try writer.print( | |
| 513 | \\LC {d} | |
| 514 | \\cmd {s} | |
| 515 | \\cmdsize {d} | |
| 516 | , .{ index, @tagName(lc.cmd()), lc.cmdsize() }); | |
| 517 | ||
| 518 | switch (lc.cmd()) { | |
| 519 | .SEGMENT_64 => { | |
| 520 | const seg = lc.cast(macho.segment_command_64).?; | |
| 521 | try writer.writeByte('\n'); | |
| 522 | try writer.print( | |
| 523 | \\segname {s} | |
| 524 | \\vmaddr {x} | |
| 525 | \\vmsize {x} | |
| 526 | \\fileoff {x} | |
| 527 | \\filesz {x} | |
| 528 | , .{ | |
| 529 | seg.segName(), | |
| 530 | seg.vmaddr, | |
| 531 | seg.vmsize, | |
| 532 | seg.fileoff, | |
| 533 | seg.filesize, | |
| 534 | }); | |
| 535 | ||
| 536 | for (lc.getSections()) |sect| { | |
| 537 | try writer.writeByte('\n'); | |
| 538 | try writer.print( | |
| 539 | \\sectname {s} | |
| 540 | \\addr {x} | |
| 541 | \\size {x} | |
| 542 | \\offset {x} | |
| 543 | \\align {x} | |
| 544 | , .{ | |
| 545 | sect.sectName(), | |
| 546 | sect.addr, | |
| 547 | sect.size, | |
| 548 | sect.offset, | |
| 549 | sect.@"align", | |
| 550 | }); | |
| 551 | } | |
| 552 | }, | |
| 553 | ||
| 554 | .ID_DYLIB, | |
| 555 | .LOAD_DYLIB, | |
| 556 | .LOAD_WEAK_DYLIB, | |
| 557 | .REEXPORT_DYLIB, | |
| 558 | => { | |
| 559 | const dylib = lc.cast(macho.dylib_command).?; | |
| 560 | try writer.writeByte('\n'); | |
| 561 | try writer.print( | |
| 562 | \\name {s} | |
| 563 | \\timestamp {d} | |
| 564 | \\current version {x} | |
| 565 | \\compatibility version {x} | |
| 566 | , .{ | |
| 567 | lc.getDylibPathName(), | |
| 568 | dylib.dylib.timestamp, | |
| 569 | dylib.dylib.current_version, | |
| 570 | dylib.dylib.compatibility_version, | |
| 571 | }); | |
| 572 | }, | |
| 573 | ||
| 574 | .MAIN => { | |
| 575 | const main = lc.cast(macho.entry_point_command).?; | |
| 576 | try writer.writeByte('\n'); | |
| 577 | try writer.print( | |
| 578 | \\entryoff {x} | |
| 579 | \\stacksize {x} | |
| 580 | , .{ main.entryoff, main.stacksize }); | |
| 581 | }, | |
| 582 | ||
| 583 | .RPATH => { | |
| 584 | try writer.writeByte('\n'); | |
| 585 | try writer.print( | |
| 586 | \\path {s} | |
| 587 | , .{ | |
| 588 | lc.getRpathPathName(), | |
| 589 | }); | |
| 590 | }, | |
| 591 | ||
| 592 | .UUID => { | |
| 593 | const uuid = lc.cast(macho.uuid_command).?; | |
| 594 | try writer.writeByte('\n'); | |
| 595 | try writer.print("uuid {x}", .{std.fmt.fmtSliceHexLower(&uuid.uuid)}); | |
| 596 | }, | |
| 597 | ||
| 598 | .DATA_IN_CODE, | |
| 599 | .FUNCTION_STARTS, | |
| 600 | .CODE_SIGNATURE, | |
| 601 | => { | |
| 602 | const llc = lc.cast(macho.linkedit_data_command).?; | |
| 603 | try writer.writeByte('\n'); | |
| 604 | try writer.print( | |
| 605 | \\dataoff {x} | |
| 606 | \\datasize {x} | |
| 607 | , .{ llc.dataoff, llc.datasize }); | |
| 608 | }, | |
| 609 | ||
| 610 | .DYLD_INFO_ONLY => { | |
| 611 | const dlc = lc.cast(macho.dyld_info_command).?; | |
| 612 | try writer.writeByte('\n'); | |
| 613 | try writer.print( | |
| 614 | \\rebaseoff {x} | |
| 615 | \\rebasesize {x} | |
| 616 | \\bindoff {x} | |
| 617 | \\bindsize {x} | |
| 618 | \\weakbindoff {x} | |
| 619 | \\weakbindsize {x} | |
| 620 | \\lazybindoff {x} | |
| 621 | \\lazybindsize {x} | |
| 622 | \\exportoff {x} | |
| 623 | \\exportsize {x} | |
| 624 | , .{ | |
| 625 | dlc.rebase_off, | |
| 626 | dlc.rebase_size, | |
| 627 | dlc.bind_off, | |
| 628 | dlc.bind_size, | |
| 629 | dlc.weak_bind_off, | |
| 630 | dlc.weak_bind_size, | |
| 631 | dlc.lazy_bind_off, | |
| 632 | dlc.lazy_bind_size, | |
| 633 | dlc.export_off, | |
| 634 | dlc.export_size, | |
| 635 | }); | |
| 636 | }, | |
| 637 | ||
| 638 | .SYMTAB => { | |
| 639 | const slc = lc.cast(macho.symtab_command).?; | |
| 640 | try writer.writeByte('\n'); | |
| 641 | try writer.print( | |
| 642 | \\symoff {x} | |
| 643 | \\nsyms {x} | |
| 644 | \\stroff {x} | |
| 645 | \\strsize {x} | |
| 646 | , .{ | |
| 647 | slc.symoff, | |
| 648 | slc.nsyms, | |
| 649 | slc.stroff, | |
| 650 | slc.strsize, | |
| 651 | }); | |
| 652 | }, | |
| 653 | ||
| 654 | .DYSYMTAB => { | |
| 655 | const dlc = lc.cast(macho.dysymtab_command).?; | |
| 656 | try writer.writeByte('\n'); | |
| 657 | try writer.print( | |
| 658 | \\ilocalsym {x} | |
| 659 | \\nlocalsym {x} | |
| 660 | \\iextdefsym {x} | |
| 661 | \\nextdefsym {x} | |
| 662 | \\iundefsym {x} | |
| 663 | \\nundefsym {x} | |
| 664 | \\indirectsymoff {x} | |
| 665 | \\nindirectsyms {x} | |
| 666 | , .{ | |
| 667 | dlc.ilocalsym, | |
| 668 | dlc.nlocalsym, | |
| 669 | dlc.iextdefsym, | |
| 670 | dlc.nextdefsym, | |
| 671 | dlc.iundefsym, | |
| 672 | dlc.nundefsym, | |
| 673 | dlc.indirectsymoff, | |
| 674 | dlc.nindirectsyms, | |
| 675 | }); | |
| 676 | }, | |
| 677 | ||
| 678 | else => {}, | |
| 679 | } | |
| 680 | } | |
| 681 | }; | |
| 682 | ||
| 683 | const WasmDumper = struct { | |
| 684 | const symtab_label = "symbols"; | |
| 685 | ||
| 686 | fn parseAndDump(bytes: []const u8, opts: Opts) ![]const u8 { | |
| 687 | const gpa = opts.gpa orelse unreachable; // Wasm dumper requires an allocator | |
| 688 | if (opts.dump_symtab) { | |
| 689 | @panic("TODO: Implement symbol table parsing and dumping"); | |
| 690 | } | |
| 691 | ||
| 692 | var fbs = std.io.fixedBufferStream(bytes); | |
| 693 | const reader = fbs.reader(); | |
| 694 | ||
| 695 | const buf = try reader.readBytesNoEof(8); | |
| 696 | if (!mem.eql(u8, buf[0..4], &std.wasm.magic)) { | |
| 697 | return error.InvalidMagicByte; | |
| 698 | } | |
| 699 | if (!mem.eql(u8, buf[4..], &std.wasm.version)) { | |
| 700 | return error.UnsupportedWasmVersion; | |
| 701 | } | |
| 702 | ||
| 703 | var output = std.ArrayList(u8).init(gpa); | |
| 704 | errdefer output.deinit(); | |
| 705 | const writer = output.writer(); | |
| 706 | ||
| 707 | while (reader.readByte()) |current_byte| { | |
| 708 | const section = std.meta.intToEnum(std.wasm.Section, current_byte) catch |err| { | |
| 709 | std.debug.print("Found invalid section id '{d}'\n", .{current_byte}); | |
| 710 | return err; | |
| 711 | }; | |
| 712 | ||
| 713 | const section_length = try std.leb.readULEB128(u32, reader); | |
| 714 | try parseAndDumpSection(section, bytes[fbs.pos..][0..section_length], writer); | |
| 715 | fbs.pos += section_length; | |
| 716 | } else |_| {} // reached end of stream | |
| 717 | ||
| 718 | return output.toOwnedSlice(); | |
| 719 | } | |
| 720 | ||
| 721 | fn parseAndDumpSection(section: std.wasm.Section, data: []const u8, writer: anytype) !void { | |
| 722 | var fbs = std.io.fixedBufferStream(data); | |
| 723 | const reader = fbs.reader(); | |
| 724 | ||
| 725 | try writer.print( | |
| 726 | \\Section {s} | |
| 727 | \\size {d} | |
| 728 | , .{ @tagName(section), data.len }); | |
| 729 | ||
| 730 | switch (section) { | |
| 731 | .type, | |
| 732 | .import, | |
| 733 | .function, | |
| 734 | .table, | |
| 735 | .memory, | |
| 736 | .global, | |
| 737 | .@"export", | |
| 738 | .element, | |
| 739 | .code, | |
| 740 | .data, | |
| 741 | => { | |
| 742 | const entries = try std.leb.readULEB128(u32, reader); | |
| 743 | try writer.print("\nentries {d}\n", .{entries}); | |
| 744 | try dumpSection(section, data[fbs.pos..], entries, writer); | |
| 745 | }, | |
| 746 | .custom => { | |
| 747 | const name_length = try std.leb.readULEB128(u32, reader); | |
| 748 | const name = data[fbs.pos..][0..name_length]; | |
| 749 | fbs.pos += name_length; | |
| 750 | try writer.print("\nname {s}\n", .{name}); | |
| 751 | ||
| 752 | if (mem.eql(u8, name, "name")) { | |
| 753 | try parseDumpNames(reader, writer, data); | |
| 754 | } else if (mem.eql(u8, name, "producers")) { | |
| 755 | try parseDumpProducers(reader, writer, data); | |
| 756 | } else if (mem.eql(u8, name, "target_features")) { | |
| 757 | try parseDumpFeatures(reader, writer, data); | |
| 758 | } | |
| 759 | // TODO: Implement parsing and dumping other custom sections (such as relocations) | |
| 760 | }, | |
| 761 | .start => { | |
| 762 | const start = try std.leb.readULEB128(u32, reader); | |
| 763 | try writer.print("\nstart {d}\n", .{start}); | |
| 764 | }, | |
| 765 | else => {}, // skip unknown sections | |
| 766 | } | |
| 767 | } | |
| 768 | ||
| 769 | fn dumpSection(section: std.wasm.Section, data: []const u8, entries: u32, writer: anytype) !void { | |
| 770 | var fbs = std.io.fixedBufferStream(data); | |
| 771 | const reader = fbs.reader(); | |
| 772 | ||
| 773 | switch (section) { | |
| 774 | .type => { | |
| 775 | var i: u32 = 0; | |
| 776 | while (i < entries) : (i += 1) { | |
| 777 | const func_type = try reader.readByte(); | |
| 778 | if (func_type != std.wasm.function_type) { | |
| 779 | std.debug.print("Expected function type, found byte '{d}'\n", .{func_type}); | |
| 780 | return error.UnexpectedByte; | |
| 781 | } | |
| 782 | const params = try std.leb.readULEB128(u32, reader); | |
| 783 | try writer.print("params {d}\n", .{params}); | |
| 784 | var index: u32 = 0; | |
| 785 | while (index < params) : (index += 1) { | |
| 786 | try parseDumpType(std.wasm.Valtype, reader, writer); | |
| 787 | } else index = 0; | |
| 788 | const returns = try std.leb.readULEB128(u32, reader); | |
| 789 | try writer.print("returns {d}\n", .{returns}); | |
| 790 | while (index < returns) : (index += 1) { | |
| 791 | try parseDumpType(std.wasm.Valtype, reader, writer); | |
| 792 | } | |
| 793 | } | |
| 794 | }, | |
| 795 | .import => { | |
| 796 | var i: u32 = 0; | |
| 797 | while (i < entries) : (i += 1) { | |
| 798 | const module_name_len = try std.leb.readULEB128(u32, reader); | |
| 799 | const module_name = data[fbs.pos..][0..module_name_len]; | |
| 800 | fbs.pos += module_name_len; | |
| 801 | const name_len = try std.leb.readULEB128(u32, reader); | |
| 802 | const name = data[fbs.pos..][0..name_len]; | |
| 803 | fbs.pos += name_len; | |
| 804 | ||
| 805 | const kind = std.meta.intToEnum(std.wasm.ExternalKind, try reader.readByte()) catch |err| { | |
| 806 | std.debug.print("Invalid import kind\n", .{}); | |
| 807 | return err; | |
| 808 | }; | |
| 809 | ||
| 810 | try writer.print( | |
| 811 | \\module {s} | |
| 812 | \\name {s} | |
| 813 | \\kind {s} | |
| 814 | , .{ module_name, name, @tagName(kind) }); | |
| 815 | try writer.writeByte('\n'); | |
| 816 | switch (kind) { | |
| 817 | .function => { | |
| 818 | try writer.print("index {d}\n", .{try std.leb.readULEB128(u32, reader)}); | |
| 819 | }, | |
| 820 | .memory => { | |
| 821 | try parseDumpLimits(reader, writer); | |
| 822 | }, | |
| 823 | .global => { | |
| 824 | try parseDumpType(std.wasm.Valtype, reader, writer); | |
| 825 | try writer.print("mutable {}\n", .{0x01 == try std.leb.readULEB128(u32, reader)}); | |
| 826 | }, | |
| 827 | .table => { | |
| 828 | try parseDumpType(std.wasm.RefType, reader, writer); | |
| 829 | try parseDumpLimits(reader, writer); | |
| 830 | }, | |
| 831 | } | |
| 832 | } | |
| 833 | }, | |
| 834 | .function => { | |
| 835 | var i: u32 = 0; | |
| 836 | while (i < entries) : (i += 1) { | |
| 837 | try writer.print("index {d}\n", .{try std.leb.readULEB128(u32, reader)}); | |
| 838 | } | |
| 839 | }, | |
| 840 | .table => { | |
| 841 | var i: u32 = 0; | |
| 842 | while (i < entries) : (i += 1) { | |
| 843 | try parseDumpType(std.wasm.RefType, reader, writer); | |
| 844 | try parseDumpLimits(reader, writer); | |
| 845 | } | |
| 846 | }, | |
| 847 | .memory => { | |
| 848 | var i: u32 = 0; | |
| 849 | while (i < entries) : (i += 1) { | |
| 850 | try parseDumpLimits(reader, writer); | |
| 851 | } | |
| 852 | }, | |
| 853 | .global => { | |
| 854 | var i: u32 = 0; | |
| 855 | while (i < entries) : (i += 1) { | |
| 856 | try parseDumpType(std.wasm.Valtype, reader, writer); | |
| 857 | try writer.print("mutable {}\n", .{0x01 == try std.leb.readULEB128(u1, reader)}); | |
| 858 | try parseDumpInit(reader, writer); | |
| 859 | } | |
| 860 | }, | |
| 861 | .@"export" => { | |
| 862 | var i: u32 = 0; | |
| 863 | while (i < entries) : (i += 1) { | |
| 864 | const name_len = try std.leb.readULEB128(u32, reader); | |
| 865 | const name = data[fbs.pos..][0..name_len]; | |
| 866 | fbs.pos += name_len; | |
| 867 | const kind_byte = try std.leb.readULEB128(u8, reader); | |
| 868 | const kind = std.meta.intToEnum(std.wasm.ExternalKind, kind_byte) catch |err| { | |
| 869 | std.debug.print("invalid export kind value '{d}'\n", .{kind_byte}); | |
| 870 | return err; | |
| 871 | }; | |
| 872 | const index = try std.leb.readULEB128(u32, reader); | |
| 873 | try writer.print( | |
| 874 | \\name {s} | |
| 875 | \\kind {s} | |
| 876 | \\index {d} | |
| 877 | , .{ name, @tagName(kind), index }); | |
| 878 | try writer.writeByte('\n'); | |
| 879 | } | |
| 880 | }, | |
| 881 | .element => { | |
| 882 | var i: u32 = 0; | |
| 883 | while (i < entries) : (i += 1) { | |
| 884 | try writer.print("table index {d}\n", .{try std.leb.readULEB128(u32, reader)}); | |
| 885 | try parseDumpInit(reader, writer); | |
| 886 | ||
| 887 | const function_indexes = try std.leb.readULEB128(u32, reader); | |
| 888 | var function_index: u32 = 0; | |
| 889 | try writer.print("indexes {d}\n", .{function_indexes}); | |
| 890 | while (function_index < function_indexes) : (function_index += 1) { | |
| 891 | try writer.print("index {d}\n", .{try std.leb.readULEB128(u32, reader)}); | |
| 892 | } | |
| 893 | } | |
| 894 | }, | |
| 895 | .code => {}, // code section is considered opaque to linker | |
| 896 | .data => { | |
| 897 | var i: u32 = 0; | |
| 898 | while (i < entries) : (i += 1) { | |
| 899 | const index = try std.leb.readULEB128(u32, reader); | |
| 900 | try writer.print("memory index 0x{x}\n", .{index}); | |
| 901 | try parseDumpInit(reader, writer); | |
| 902 | const size = try std.leb.readULEB128(u32, reader); | |
| 903 | try writer.print("size {d}\n", .{size}); | |
| 904 | try reader.skipBytes(size, .{}); // we do not care about the content of the segments | |
| 905 | } | |
| 906 | }, | |
| 907 | else => unreachable, | |
| 908 | } | |
| 909 | } | |
| 910 | ||
| 911 | fn parseDumpType(comptime WasmType: type, reader: anytype, writer: anytype) !void { | |
| 912 | const type_byte = try reader.readByte(); | |
| 913 | const valtype = std.meta.intToEnum(WasmType, type_byte) catch |err| { | |
| 914 | std.debug.print("Invalid wasm type value '{d}'\n", .{type_byte}); | |
| 915 | return err; | |
| 916 | }; | |
| 917 | try writer.print("type {s}\n", .{@tagName(valtype)}); | |
| 918 | } | |
| 919 | ||
| 920 | fn parseDumpLimits(reader: anytype, writer: anytype) !void { | |
| 921 | const flags = try std.leb.readULEB128(u8, reader); | |
| 922 | const min = try std.leb.readULEB128(u32, reader); | |
| 923 | ||
| 924 | try writer.print("min {x}\n", .{min}); | |
| 925 | if (flags != 0) { | |
| 926 | try writer.print("max {x}\n", .{try std.leb.readULEB128(u32, reader)}); | |
| 927 | } | |
| 928 | } | |
| 929 | ||
| 930 | fn parseDumpInit(reader: anytype, writer: anytype) !void { | |
| 931 | const byte = try std.leb.readULEB128(u8, reader); | |
| 932 | const opcode = std.meta.intToEnum(std.wasm.Opcode, byte) catch |err| { | |
| 933 | std.debug.print("invalid wasm opcode '{d}'\n", .{byte}); | |
| 934 | return err; | |
| 935 | }; | |
| 936 | switch (opcode) { | |
| 937 | .i32_const => try writer.print("i32.const {x}\n", .{try std.leb.readILEB128(i32, reader)}), | |
| 938 | .i64_const => try writer.print("i64.const {x}\n", .{try std.leb.readILEB128(i64, reader)}), | |
| 939 | .f32_const => try writer.print("f32.const {x}\n", .{@bitCast(f32, try reader.readIntLittle(u32))}), | |
| 940 | .f64_const => try writer.print("f64.const {x}\n", .{@bitCast(f64, try reader.readIntLittle(u64))}), | |
| 941 | .global_get => try writer.print("global.get {x}\n", .{try std.leb.readULEB128(u32, reader)}), | |
| 942 | else => unreachable, | |
| 943 | } | |
| 944 | const end_opcode = try std.leb.readULEB128(u8, reader); | |
| 945 | if (end_opcode != std.wasm.opcode(.end)) { | |
| 946 | std.debug.print("expected 'end' opcode in init expression\n", .{}); | |
| 947 | return error.MissingEndOpcode; | |
| 948 | } | |
| 949 | } | |
| 950 | ||
| 951 | fn parseDumpNames(reader: anytype, writer: anytype, data: []const u8) !void { | |
| 952 | while (reader.context.pos < data.len) { | |
| 953 | try parseDumpType(std.wasm.NameSubsection, reader, writer); | |
| 954 | const size = try std.leb.readULEB128(u32, reader); | |
| 955 | const entries = try std.leb.readULEB128(u32, reader); | |
| 956 | try writer.print( | |
| 957 | \\size {d} | |
| 958 | \\names {d} | |
| 959 | , .{ size, entries }); | |
| 960 | try writer.writeByte('\n'); | |
| 961 | var i: u32 = 0; | |
| 962 | while (i < entries) : (i += 1) { | |
| 963 | const index = try std.leb.readULEB128(u32, reader); | |
| 964 | const name_len = try std.leb.readULEB128(u32, reader); | |
| 965 | const pos = reader.context.pos; | |
| 966 | const name = data[pos..][0..name_len]; | |
| 967 | reader.context.pos += name_len; | |
| 968 | ||
| 969 | try writer.print( | |
| 970 | \\index {d} | |
| 971 | \\name {s} | |
| 972 | , .{ index, name }); | |
| 973 | try writer.writeByte('\n'); | |
| 974 | } | |
| 975 | } | |
| 976 | } | |
| 977 | ||
| 978 | fn parseDumpProducers(reader: anytype, writer: anytype, data: []const u8) !void { | |
| 979 | const field_count = try std.leb.readULEB128(u32, reader); | |
| 980 | try writer.print("fields {d}\n", .{field_count}); | |
| 981 | var current_field: u32 = 0; | |
| 982 | while (current_field < field_count) : (current_field += 1) { | |
| 983 | const field_name_length = try std.leb.readULEB128(u32, reader); | |
| 984 | const field_name = data[reader.context.pos..][0..field_name_length]; | |
| 985 | reader.context.pos += field_name_length; | |
| 986 | ||
| 987 | const value_count = try std.leb.readULEB128(u32, reader); | |
| 988 | try writer.print( | |
| 989 | \\field_name {s} | |
| 990 | \\values {d} | |
| 991 | , .{ field_name, value_count }); | |
| 992 | try writer.writeByte('\n'); | |
| 993 | var current_value: u32 = 0; | |
| 994 | while (current_value < value_count) : (current_value += 1) { | |
| 995 | const value_length = try std.leb.readULEB128(u32, reader); | |
| 996 | const value = data[reader.context.pos..][0..value_length]; | |
| 997 | reader.context.pos += value_length; | |
| 998 | ||
| 999 | const version_length = try std.leb.readULEB128(u32, reader); | |
| 1000 | const version = data[reader.context.pos..][0..version_length]; | |
| 1001 | reader.context.pos += version_length; | |
| 1002 | ||
| 1003 | try writer.print( | |
| 1004 | \\value_name {s} | |
| 1005 | \\version {s} | |
| 1006 | , .{ value, version }); | |
| 1007 | try writer.writeByte('\n'); | |
| 1008 | } | |
| 1009 | } | |
| 1010 | } | |
| 1011 | ||
| 1012 | fn parseDumpFeatures(reader: anytype, writer: anytype, data: []const u8) !void { | |
| 1013 | const feature_count = try std.leb.readULEB128(u32, reader); | |
| 1014 | try writer.print("features {d}\n", .{feature_count}); | |
| 1015 | ||
| 1016 | var index: u32 = 0; | |
| 1017 | while (index < feature_count) : (index += 1) { | |
| 1018 | const prefix_byte = try std.leb.readULEB128(u8, reader); | |
| 1019 | const name_length = try std.leb.readULEB128(u32, reader); | |
| 1020 | const feature_name = data[reader.context.pos..][0..name_length]; | |
| 1021 | reader.context.pos += name_length; | |
| 1022 | ||
| 1023 | try writer.print("{c} {s}\n", .{ prefix_byte, feature_name }); | |
| 1024 | } | |
| 1025 | } | |
| 1026 | }; |
lib/std/build/ConfigHeaderStep.zig deleted-288| ... | ... | @@ -1,288 +0,0 @@ |
| 1 | const std = @import("../std.zig"); | |
| 2 | const ConfigHeaderStep = @This(); | |
| 3 | const Step = std.build.Step; | |
| 4 | const Builder = std.build.Builder; | |
| 5 | ||
| 6 | pub const base_id: Step.Id = .config_header; | |
| 7 | ||
| 8 | pub const Style = enum { | |
| 9 | /// The configure format supported by autotools. It uses `#undef foo` to | |
| 10 | /// mark lines that can be substituted with different values. | |
| 11 | autoconf, | |
| 12 | /// The configure format supported by CMake. It uses `@@FOO@@` and | |
| 13 | /// `#cmakedefine` for template substitution. | |
| 14 | cmake, | |
| 15 | }; | |
| 16 | ||
| 17 | pub const Value = union(enum) { | |
| 18 | undef, | |
| 19 | defined, | |
| 20 | boolean: bool, | |
| 21 | int: i64, | |
| 22 | ident: []const u8, | |
| 23 | string: []const u8, | |
| 24 | }; | |
| 25 | ||
| 26 | step: Step, | |
| 27 | builder: *Builder, | |
| 28 | source: std.build.FileSource, | |
| 29 | style: Style, | |
| 30 | values: std.StringHashMap(Value), | |
| 31 | max_bytes: usize = 2 * 1024 * 1024, | |
| 32 | output_dir: []const u8, | |
| 33 | output_basename: []const u8, | |
| 34 | ||
| 35 | pub fn create(builder: *Builder, source: std.build.FileSource, style: Style) *ConfigHeaderStep { | |
| 36 | const self = builder.allocator.create(ConfigHeaderStep) catch @panic("OOM"); | |
| 37 | const name = builder.fmt("configure header {s}", .{source.getDisplayName()}); | |
| 38 | self.* = .{ | |
| 39 | .builder = builder, | |
| 40 | .step = Step.init(base_id, name, builder.allocator, make), | |
| 41 | .source = source, | |
| 42 | .style = style, | |
| 43 | .values = std.StringHashMap(Value).init(builder.allocator), | |
| 44 | .output_dir = undefined, | |
| 45 | .output_basename = "config.h", | |
| 46 | }; | |
| 47 | switch (source) { | |
| 48 | .path => |p| { | |
| 49 | const basename = std.fs.path.basename(p); | |
| 50 | if (std.mem.endsWith(u8, basename, ".h.in")) { | |
| 51 | self.output_basename = basename[0 .. basename.len - 3]; | |
| 52 | } | |
| 53 | }, | |
| 54 | else => {}, | |
| 55 | } | |
| 56 | return self; | |
| 57 | } | |
| 58 | ||
| 59 | pub fn addValues(self: *ConfigHeaderStep, values: anytype) void { | |
| 60 | return addValuesInner(self, values) catch @panic("OOM"); | |
| 61 | } | |
| 62 | ||
| 63 | fn addValuesInner(self: *ConfigHeaderStep, values: anytype) !void { | |
| 64 | inline for (@typeInfo(@TypeOf(values)).Struct.fields) |field| { | |
| 65 | switch (@typeInfo(field.type)) { | |
| 66 | .Null => { | |
| 67 | try self.values.put(field.name, .undef); | |
| 68 | }, | |
| 69 | .Void => { | |
| 70 | try self.values.put(field.name, .defined); | |
| 71 | }, | |
| 72 | .Bool => { | |
| 73 | try self.values.put(field.name, .{ .boolean = @field(values, field.name) }); | |
| 74 | }, | |
| 75 | .ComptimeInt => { | |
| 76 | try self.values.put(field.name, .{ .int = @field(values, field.name) }); | |
| 77 | }, | |
| 78 | .EnumLiteral => { | |
| 79 | try self.values.put(field.name, .{ .ident = @tagName(@field(values, field.name)) }); | |
| 80 | }, | |
| 81 | .Pointer => |ptr| { | |
| 82 | switch (@typeInfo(ptr.child)) { | |
| 83 | .Array => |array| { | |
| 84 | if (ptr.size == .One and array.child == u8) { | |
| 85 | try self.values.put(field.name, .{ .string = @field(values, field.name) }); | |
| 86 | continue; | |
| 87 | } | |
| 88 | }, | |
| 89 | else => {}, | |
| 90 | } | |
| 91 | ||
| 92 | @compileError("unsupported ConfigHeaderStep value type: " ++ | |
| 93 | @typeName(field.type)); | |
| 94 | }, | |
| 95 | else => @compileError("unsupported ConfigHeaderStep value type: " ++ | |
| 96 | @typeName(field.type)), | |
| 97 | } | |
| 98 | } | |
| 99 | } | |
| 100 | ||
| 101 | fn make(step: *Step) !void { | |
| 102 | const self = @fieldParentPtr(ConfigHeaderStep, "step", step); | |
| 103 | const gpa = self.builder.allocator; | |
| 104 | const src_path = self.source.getPath(self.builder); | |
| 105 | const contents = try std.fs.cwd().readFileAlloc(gpa, src_path, self.max_bytes); | |
| 106 | ||
| 107 | // The cache is used here not really as a way to speed things up - because writing | |
| 108 | // the data to a file would probably be very fast - but as a way to find a canonical | |
| 109 | // location to put build artifacts. | |
| 110 | ||
| 111 | // If, for example, a hard-coded path was used as the location to put ConfigHeaderStep | |
| 112 | // files, then two ConfigHeaderStep executing in parallel might clobber each other. | |
| 113 | ||
| 114 | // TODO port the cache system from the compiler to zig std lib. Until then | |
| 115 | // we construct the path directly, and no "cache hit" detection happens; | |
| 116 | // the files are always written. | |
| 117 | // Note there is very similar code over in WriteFileStep | |
| 118 | const Hasher = std.crypto.auth.siphash.SipHash128(1, 3); | |
| 119 | // Random bytes to make ConfigHeaderStep unique. Refresh this with new | |
| 120 | // random bytes when ConfigHeaderStep implementation is modified in a | |
| 121 | // non-backwards-compatible way. | |
| 122 | var hash = Hasher.init("X1pQzdDt91Zlh7Eh"); | |
| 123 | hash.update(self.source.getDisplayName()); | |
| 124 | hash.update(contents); | |
| 125 | ||
| 126 | var digest: [16]u8 = undefined; | |
| 127 | hash.final(&digest); | |
| 128 | var hash_basename: [digest.len * 2]u8 = undefined; | |
| 129 | _ = std.fmt.bufPrint( | |
| 130 | &hash_basename, | |
| 131 | "{s}", | |
| 132 | .{std.fmt.fmtSliceHexLower(&digest)}, | |
| 133 | ) catch unreachable; | |
| 134 | ||
| 135 | self.output_dir = try std.fs.path.join(gpa, &[_][]const u8{ | |
| 136 | self.builder.cache_root, "o", &hash_basename, | |
| 137 | }); | |
| 138 | var dir = std.fs.cwd().makeOpenPath(self.output_dir, .{}) catch |err| { | |
| 139 | std.debug.print("unable to make path {s}: {s}\n", .{ self.output_dir, @errorName(err) }); | |
| 140 | return err; | |
| 141 | }; | |
| 142 | defer dir.close(); | |
| 143 | ||
| 144 | var values_copy = try self.values.clone(); | |
| 145 | defer values_copy.deinit(); | |
| 146 | ||
| 147 | var output = std.ArrayList(u8).init(gpa); | |
| 148 | defer output.deinit(); | |
| 149 | try output.ensureTotalCapacity(contents.len); | |
| 150 | ||
| 151 | try output.appendSlice("/* This file was generated by ConfigHeaderStep using the Zig Build System. */\n"); | |
| 152 | ||
| 153 | switch (self.style) { | |
| 154 | .autoconf => try render_autoconf(contents, &output, &values_copy, src_path), | |
| 155 | .cmake => try render_cmake(contents, &output, &values_copy, src_path), | |
| 156 | } | |
| 157 | ||
| 158 | try dir.writeFile(self.output_basename, output.items); | |
| 159 | } | |
| 160 | ||
| 161 | fn render_autoconf( | |
| 162 | contents: []const u8, | |
| 163 | output: *std.ArrayList(u8), | |
| 164 | values_copy: *std.StringHashMap(Value), | |
| 165 | src_path: []const u8, | |
| 166 | ) !void { | |
| 167 | var any_errors = false; | |
| 168 | var line_index: u32 = 0; | |
| 169 | var line_it = std.mem.split(u8, contents, "\n"); | |
| 170 | while (line_it.next()) |line| : (line_index += 1) { | |
| 171 | if (!std.mem.startsWith(u8, line, "#")) { | |
| 172 | try output.appendSlice(line); | |
| 173 | try output.appendSlice("\n"); | |
| 174 | continue; | |
| 175 | } | |
| 176 | var it = std.mem.tokenize(u8, line[1..], " \t\r"); | |
| 177 | const undef = it.next().?; | |
| 178 | if (!std.mem.eql(u8, undef, "undef")) { | |
| 179 | try output.appendSlice(line); | |
| 180 | try output.appendSlice("\n"); | |
| 181 | continue; | |
| 182 | } | |
| 183 | const name = it.rest(); | |
| 184 | const kv = values_copy.fetchRemove(name) orelse { | |
| 185 | std.debug.print("{s}:{d}: error: unspecified config header value: '{s}'\n", .{ | |
| 186 | src_path, line_index + 1, name, | |
| 187 | }); | |
| 188 | any_errors = true; | |
| 189 | continue; | |
| 190 | }; | |
| 191 | try renderValue(output, name, kv.value); | |
| 192 | } | |
| 193 | ||
| 194 | { | |
| 195 | var it = values_copy.iterator(); | |
| 196 | while (it.next()) |entry| { | |
| 197 | const name = entry.key_ptr.*; | |
| 198 | std.debug.print("{s}: error: config header value unused: '{s}'\n", .{ src_path, name }); | |
| 199 | } | |
| 200 | } | |
| 201 | ||
| 202 | if (any_errors) { | |
| 203 | return error.HeaderConfigFailed; | |
| 204 | } | |
| 205 | } | |
| 206 | ||
| 207 | fn render_cmake( | |
| 208 | contents: []const u8, | |
| 209 | output: *std.ArrayList(u8), | |
| 210 | values_copy: *std.StringHashMap(Value), | |
| 211 | src_path: []const u8, | |
| 212 | ) !void { | |
| 213 | var any_errors = false; | |
| 214 | var line_index: u32 = 0; | |
| 215 | var line_it = std.mem.split(u8, contents, "\n"); | |
| 216 | while (line_it.next()) |line| : (line_index += 1) { | |
| 217 | if (!std.mem.startsWith(u8, line, "#")) { | |
| 218 | try output.appendSlice(line); | |
| 219 | try output.appendSlice("\n"); | |
| 220 | continue; | |
| 221 | } | |
| 222 | var it = std.mem.tokenize(u8, line[1..], " \t\r"); | |
| 223 | const cmakedefine = it.next().?; | |
| 224 | if (!std.mem.eql(u8, cmakedefine, "cmakedefine")) { | |
| 225 | try output.appendSlice(line); | |
| 226 | try output.appendSlice("\n"); | |
| 227 | continue; | |
| 228 | } | |
| 229 | const name = it.next() orelse { | |
| 230 | std.debug.print("{s}:{d}: error: missing define name\n", .{ | |
| 231 | src_path, line_index + 1, | |
| 232 | }); | |
| 233 | any_errors = true; | |
| 234 | continue; | |
| 235 | }; | |
| 236 | const kv = values_copy.fetchRemove(name) orelse { | |
| 237 | std.debug.print("{s}:{d}: error: unspecified config header value: '{s}'\n", .{ | |
| 238 | src_path, line_index + 1, name, | |
| 239 | }); | |
| 240 | any_errors = true; | |
| 241 | continue; | |
| 242 | }; | |
| 243 | try renderValue(output, name, kv.value); | |
| 244 | } | |
| 245 | ||
| 246 | { | |
| 247 | var it = values_copy.iterator(); | |
| 248 | while (it.next()) |entry| { | |
| 249 | const name = entry.key_ptr.*; | |
| 250 | std.debug.print("{s}: error: config header value unused: '{s}'\n", .{ src_path, name }); | |
| 251 | } | |
| 252 | } | |
| 253 | ||
| 254 | if (any_errors) { | |
| 255 | return error.HeaderConfigFailed; | |
| 256 | } | |
| 257 | } | |
| 258 | ||
| 259 | fn renderValue(output: *std.ArrayList(u8), name: []const u8, value: Value) !void { | |
| 260 | switch (value) { | |
| 261 | .undef => { | |
| 262 | try output.appendSlice("/* #undef "); | |
| 263 | try output.appendSlice(name); | |
| 264 | try output.appendSlice(" */\n"); | |
| 265 | }, | |
| 266 | .defined => { | |
| 267 | try output.appendSlice("#define "); | |
| 268 | try output.appendSlice(name); | |
| 269 | try output.appendSlice("\n"); | |
| 270 | }, | |
| 271 | .boolean => |b| { | |
| 272 | try output.appendSlice("#define "); | |
| 273 | try output.appendSlice(name); | |
| 274 | try output.appendSlice(" "); | |
| 275 | try output.appendSlice(if (b) "true\n" else "false\n"); | |
| 276 | }, | |
| 277 | .int => |i| { | |
| 278 | try output.writer().print("#define {s} {d}\n", .{ name, i }); | |
| 279 | }, | |
| 280 | .ident => |ident| { | |
| 281 | try output.writer().print("#define {s} {s}\n", .{ name, ident }); | |
| 282 | }, | |
| 283 | .string => |string| { | |
| 284 | // TODO: use C-specific escaping instead of zig string literals | |
| 285 | try output.writer().print("#define {s} \"{}\"\n", .{ name, std.zig.fmtEscapes(string) }); | |
| 286 | }, | |
| 287 | } | |
| 288 | } |
lib/std/build/EmulatableRunStep.zig deleted-215| ... | ... | @@ -1,215 +0,0 @@ |
| 1 | //! Unlike `RunStep` this step will provide emulation, when enabled, to run foreign binaries. | |
| 2 | //! When a binary is foreign, but emulation for the target is disabled, the specified binary | |
| 3 | //! will not be run and therefore also not validated against its output. | |
| 4 | //! This step can be useful when wishing to run a built binary on multiple platforms, | |
| 5 | //! without having to verify if it's possible to be ran against. | |
| 6 | ||
| 7 | const std = @import("../std.zig"); | |
| 8 | const build = std.build; | |
| 9 | const Step = std.build.Step; | |
| 10 | const Builder = std.build.Builder; | |
| 11 | const LibExeObjStep = std.build.LibExeObjStep; | |
| 12 | const RunStep = std.build.RunStep; | |
| 13 | ||
| 14 | const fs = std.fs; | |
| 15 | const process = std.process; | |
| 16 | const EnvMap = process.EnvMap; | |
| 17 | ||
| 18 | const EmulatableRunStep = @This(); | |
| 19 | ||
| 20 | pub const base_id = .emulatable_run; | |
| 21 | ||
| 22 | const max_stdout_size = 1 * 1024 * 1024; // 1 MiB | |
| 23 | ||
| 24 | step: Step, | |
| 25 | builder: *Builder, | |
| 26 | ||
| 27 | /// The artifact (executable) to be run by this step | |
| 28 | exe: *LibExeObjStep, | |
| 29 | ||
| 30 | /// Set this to `null` to ignore the exit code for the purpose of determining a successful execution | |
| 31 | expected_exit_code: ?u8 = 0, | |
| 32 | ||
| 33 | /// Override this field to modify the environment | |
| 34 | env_map: ?*EnvMap, | |
| 35 | ||
| 36 | /// Set this to modify the current working directory | |
| 37 | cwd: ?[]const u8, | |
| 38 | ||
| 39 | stdout_action: RunStep.StdIoAction = .inherit, | |
| 40 | stderr_action: RunStep.StdIoAction = .inherit, | |
| 41 | ||
| 42 | /// When set to true, hides the warning of skipping a foreign binary which cannot be run on the host | |
| 43 | /// or through emulation. | |
| 44 | hide_foreign_binaries_warning: bool, | |
| 45 | ||
| 46 | /// Creates a step that will execute the given artifact. This step will allow running the | |
| 47 | /// binary through emulation when any of the emulation options such as `enable_rosetta` are set to true. | |
| 48 | /// When set to false, and the binary is foreign, running the executable is skipped. | |
| 49 | /// Asserts given artifact is an executable. | |
| 50 | pub fn create(builder: *Builder, name: []const u8, artifact: *LibExeObjStep) *EmulatableRunStep { | |
| 51 | std.debug.assert(artifact.kind == .exe or artifact.kind == .test_exe); | |
| 52 | const self = builder.allocator.create(EmulatableRunStep) catch unreachable; | |
| 53 | ||
| 54 | const option_name = "hide-foreign-warnings"; | |
| 55 | const hide_warnings = if (builder.available_options_map.get(option_name) == null) warn: { | |
| 56 | break :warn builder.option(bool, option_name, "Hide the warning when a foreign binary which is incompatible is skipped") orelse false; | |
| 57 | } else false; | |
| 58 | ||
| 59 | self.* = .{ | |
| 60 | .builder = builder, | |
| 61 | .step = Step.init(.emulatable_run, name, builder.allocator, make), | |
| 62 | .exe = artifact, | |
| 63 | .env_map = null, | |
| 64 | .cwd = null, | |
| 65 | .hide_foreign_binaries_warning = hide_warnings, | |
| 66 | }; | |
| 67 | self.step.dependOn(&artifact.step); | |
| 68 | ||
| 69 | return self; | |
| 70 | } | |
| 71 | ||
| 72 | fn make(step: *Step) !void { | |
| 73 | const self = @fieldParentPtr(EmulatableRunStep, "step", step); | |
| 74 | const host_info = self.builder.host; | |
| 75 | ||
| 76 | var argv_list = std.ArrayList([]const u8).init(self.builder.allocator); | |
| 77 | defer argv_list.deinit(); | |
| 78 | ||
| 79 | const need_cross_glibc = self.exe.target.isGnuLibC() and self.exe.is_linking_libc; | |
| 80 | switch (host_info.getExternalExecutor(self.exe.target_info, .{ | |
| 81 | .qemu_fixes_dl = need_cross_glibc and self.builder.glibc_runtimes_dir != null, | |
| 82 | .link_libc = self.exe.is_linking_libc, | |
| 83 | })) { | |
| 84 | .native => {}, | |
| 85 | .rosetta => if (!self.builder.enable_rosetta) return warnAboutForeignBinaries(self), | |
| 86 | .wine => |bin_name| if (self.builder.enable_wine) { | |
| 87 | try argv_list.append(bin_name); | |
| 88 | } else return, | |
| 89 | .qemu => |bin_name| if (self.builder.enable_qemu) { | |
| 90 | const glibc_dir_arg = if (need_cross_glibc) | |
| 91 | self.builder.glibc_runtimes_dir orelse return | |
| 92 | else | |
| 93 | null; | |
| 94 | try argv_list.append(bin_name); | |
| 95 | if (glibc_dir_arg) |dir| { | |
| 96 | // TODO look into making this a call to `linuxTriple`. This | |
| 97 | // needs the directory to be called "i686" rather than | |
| 98 | // "x86" which is why we do it manually here. | |
| 99 | const fmt_str = "{s}" ++ fs.path.sep_str ++ "{s}-{s}-{s}"; | |
| 100 | const cpu_arch = self.exe.target.getCpuArch(); | |
| 101 | const os_tag = self.exe.target.getOsTag(); | |
| 102 | const abi = self.exe.target.getAbi(); | |
| 103 | const cpu_arch_name: []const u8 = if (cpu_arch == .x86) | |
| 104 | "i686" | |
| 105 | else | |
| 106 | @tagName(cpu_arch); | |
| 107 | const full_dir = try std.fmt.allocPrint(self.builder.allocator, fmt_str, .{ | |
| 108 | dir, cpu_arch_name, @tagName(os_tag), @tagName(abi), | |
| 109 | }); | |
| 110 | ||
| 111 | try argv_list.append("-L"); | |
| 112 | try argv_list.append(full_dir); | |
| 113 | } | |
| 114 | } else return warnAboutForeignBinaries(self), | |
| 115 | .darling => |bin_name| if (self.builder.enable_darling) { | |
| 116 | try argv_list.append(bin_name); | |
| 117 | } else return warnAboutForeignBinaries(self), | |
| 118 | .wasmtime => |bin_name| if (self.builder.enable_wasmtime) { | |
| 119 | try argv_list.append(bin_name); | |
| 120 | try argv_list.append("--dir=."); | |
| 121 | } else return warnAboutForeignBinaries(self), | |
| 122 | else => return warnAboutForeignBinaries(self), | |
| 123 | } | |
| 124 | ||
| 125 | if (self.exe.target.isWindows()) { | |
| 126 | // On Windows we don't have rpaths so we have to add .dll search paths to PATH | |
| 127 | RunStep.addPathForDynLibsInternal(&self.step, self.builder, self.exe); | |
| 128 | } | |
| 129 | ||
| 130 | const executable_path = self.exe.installed_path orelse self.exe.getOutputSource().getPath(self.builder); | |
| 131 | try argv_list.append(executable_path); | |
| 132 | ||
| 133 | try RunStep.runCommand( | |
| 134 | argv_list.items, | |
| 135 | self.builder, | |
| 136 | self.expected_exit_code, | |
| 137 | self.stdout_action, | |
| 138 | self.stderr_action, | |
| 139 | .Inherit, | |
| 140 | self.env_map, | |
| 141 | self.cwd, | |
| 142 | false, | |
| 143 | ); | |
| 144 | } | |
| 145 | ||
| 146 | pub fn expectStdErrEqual(self: *EmulatableRunStep, bytes: []const u8) void { | |
| 147 | self.stderr_action = .{ .expect_exact = self.builder.dupe(bytes) }; | |
| 148 | } | |
| 149 | ||
| 150 | pub fn expectStdOutEqual(self: *EmulatableRunStep, bytes: []const u8) void { | |
| 151 | self.stdout_action = .{ .expect_exact = self.builder.dupe(bytes) }; | |
| 152 | } | |
| 153 | ||
| 154 | fn warnAboutForeignBinaries(step: *EmulatableRunStep) void { | |
| 155 | if (step.hide_foreign_binaries_warning) return; | |
| 156 | const builder = step.builder; | |
| 157 | const artifact = step.exe; | |
| 158 | ||
| 159 | const host_name = builder.host.target.zigTriple(builder.allocator) catch unreachable; | |
| 160 | const foreign_name = artifact.target.zigTriple(builder.allocator) catch unreachable; | |
| 161 | const target_info = std.zig.system.NativeTargetInfo.detect(artifact.target) catch unreachable; | |
| 162 | const need_cross_glibc = artifact.target.isGnuLibC() and artifact.is_linking_libc; | |
| 163 | switch (builder.host.getExternalExecutor(target_info, .{ | |
| 164 | .qemu_fixes_dl = need_cross_glibc and builder.glibc_runtimes_dir != null, | |
| 165 | .link_libc = artifact.is_linking_libc, | |
| 166 | })) { | |
| 167 | .native => unreachable, | |
| 168 | .bad_dl => |foreign_dl| { | |
| 169 | const host_dl = builder.host.dynamic_linker.get() orelse "(none)"; | |
| 170 | std.debug.print("the host system does not appear to be capable of executing binaries from the target because the host dynamic linker is '{s}', while the target dynamic linker is '{s}'. Consider setting the dynamic linker as '{s}'.\n", .{ | |
| 171 | host_dl, foreign_dl, host_dl, | |
| 172 | }); | |
| 173 | }, | |
| 174 | .bad_os_or_cpu => { | |
| 175 | std.debug.print("the host system ({s}) does not appear to be capable of executing binaries from the target ({s}).\n", .{ | |
| 176 | host_name, foreign_name, | |
| 177 | }); | |
| 178 | }, | |
| 179 | .darling => if (!builder.enable_darling) { | |
| 180 | std.debug.print( | |
| 181 | "the host system ({s}) does not appear to be capable of executing binaries " ++ | |
| 182 | "from the target ({s}). Consider enabling darling.\n", | |
| 183 | .{ host_name, foreign_name }, | |
| 184 | ); | |
| 185 | }, | |
| 186 | .rosetta => if (!builder.enable_rosetta) { | |
| 187 | std.debug.print( | |
| 188 | "the host system ({s}) does not appear to be capable of executing binaries " ++ | |
| 189 | "from the target ({s}). Consider enabling rosetta.\n", | |
| 190 | .{ host_name, foreign_name }, | |
| 191 | ); | |
| 192 | }, | |
| 193 | .wine => if (!builder.enable_wine) { | |
| 194 | std.debug.print( | |
| 195 | "the host system ({s}) does not appear to be capable of executing binaries " ++ | |
| 196 | "from the target ({s}). Consider enabling wine.\n", | |
| 197 | .{ host_name, foreign_name }, | |
| 198 | ); | |
| 199 | }, | |
| 200 | .qemu => if (!builder.enable_qemu) { | |
| 201 | std.debug.print( | |
| 202 | "the host system ({s}) does not appear to be capable of executing binaries " ++ | |
| 203 | "from the target ({s}). Consider enabling qemu.\n", | |
| 204 | .{ host_name, foreign_name }, | |
| 205 | ); | |
| 206 | }, | |
| 207 | .wasmtime => { | |
| 208 | std.debug.print( | |
| 209 | "the host system ({s}) does not appear to be capable of executing binaries " ++ | |
| 210 | "from the target ({s}). Consider enabling wasmtime.\n", | |
| 211 | .{ host_name, foreign_name }, | |
| 212 | ); | |
| 213 | }, | |
| 214 | } | |
| 215 | } |
lib/std/build/FmtStep.zig deleted-37| ... | ... | @@ -1,37 +0,0 @@ |
| 1 | const std = @import("../std.zig"); | |
| 2 | const build = @import("../build.zig"); | |
| 3 | const Step = build.Step; | |
| 4 | const Builder = build.Builder; | |
| 5 | const BufMap = std.BufMap; | |
| 6 | const mem = std.mem; | |
| 7 | ||
| 8 | const FmtStep = @This(); | |
| 9 | ||
| 10 | pub const base_id = .fmt; | |
| 11 | ||
| 12 | step: Step, | |
| 13 | builder: *Builder, | |
| 14 | argv: [][]const u8, | |
| 15 | ||
| 16 | pub fn create(builder: *Builder, paths: []const []const u8) *FmtStep { | |
| 17 | const self = builder.allocator.create(FmtStep) catch unreachable; | |
| 18 | const name = "zig fmt"; | |
| 19 | self.* = FmtStep{ | |
| 20 | .step = Step.init(.fmt, name, builder.allocator, make), | |
| 21 | .builder = builder, | |
| 22 | .argv = builder.allocator.alloc([]u8, paths.len + 2) catch unreachable, | |
| 23 | }; | |
| 24 | ||
| 25 | self.argv[0] = builder.zig_exe; | |
| 26 | self.argv[1] = "fmt"; | |
| 27 | for (paths) |path, i| { | |
| 28 | self.argv[2 + i] = builder.pathFromRoot(path); | |
| 29 | } | |
| 30 | return self; | |
| 31 | } | |
| 32 | ||
| 33 | fn make(step: *Step) !void { | |
| 34 | const self = @fieldParentPtr(FmtStep, "step", step); | |
| 35 | ||
| 36 | return self.builder.spawnChild(self.argv); | |
| 37 | } |
lib/std/build/InstallArtifactStep.zig deleted-88| ... | ... | @@ -1,88 +0,0 @@ |
| 1 | const std = @import("../std.zig"); | |
| 2 | const build = @import("../build.zig"); | |
| 3 | const Step = build.Step; | |
| 4 | const Builder = build.Builder; | |
| 5 | const LibExeObjStep = std.build.LibExeObjStep; | |
| 6 | const InstallDir = std.build.InstallDir; | |
| 7 | ||
| 8 | pub const base_id = .install_artifact; | |
| 9 | ||
| 10 | step: Step, | |
| 11 | builder: *Builder, | |
| 12 | artifact: *LibExeObjStep, | |
| 13 | dest_dir: InstallDir, | |
| 14 | pdb_dir: ?InstallDir, | |
| 15 | h_dir: ?InstallDir, | |
| 16 | ||
| 17 | const Self = @This(); | |
| 18 | ||
| 19 | pub fn create(builder: *Builder, artifact: *LibExeObjStep) *Self { | |
| 20 | if (artifact.install_step) |s| return s; | |
| 21 | ||
| 22 | const self = builder.allocator.create(Self) catch unreachable; | |
| 23 | self.* = Self{ | |
| 24 | .builder = builder, | |
| 25 | .step = Step.init(.install_artifact, builder.fmt("install {s}", .{artifact.step.name}), builder.allocator, make), | |
| 26 | .artifact = artifact, | |
| 27 | .dest_dir = artifact.override_dest_dir orelse switch (artifact.kind) { | |
| 28 | .obj => @panic("Cannot install a .obj build artifact."), | |
| 29 | .@"test" => @panic("Cannot install a test build artifact, use addTestExe instead."), | |
| 30 | .exe, .test_exe => InstallDir{ .bin = {} }, | |
| 31 | .lib => InstallDir{ .lib = {} }, | |
| 32 | }, | |
| 33 | .pdb_dir = if (artifact.producesPdbFile()) blk: { | |
| 34 | if (artifact.kind == .exe or artifact.kind == .test_exe) { | |
| 35 | break :blk InstallDir{ .bin = {} }; | |
| 36 | } else { | |
| 37 | break :blk InstallDir{ .lib = {} }; | |
| 38 | } | |
| 39 | } else null, | |
| 40 | .h_dir = if (artifact.kind == .lib and artifact.emit_h) .header else null, | |
| 41 | }; | |
| 42 | self.step.dependOn(&artifact.step); | |
| 43 | artifact.install_step = self; | |
| 44 | ||
| 45 | builder.pushInstalledFile(self.dest_dir, artifact.out_filename); | |
| 46 | if (self.artifact.isDynamicLibrary()) { | |
| 47 | if (artifact.major_only_filename) |name| { | |
| 48 | builder.pushInstalledFile(.lib, name); | |
| 49 | } | |
| 50 | if (artifact.name_only_filename) |name| { | |
| 51 | builder.pushInstalledFile(.lib, name); | |
| 52 | } | |
| 53 | if (self.artifact.target.isWindows()) { | |
| 54 | builder.pushInstalledFile(.lib, artifact.out_lib_filename); | |
| 55 | } | |
| 56 | } | |
| 57 | if (self.pdb_dir) |pdb_dir| { | |
| 58 | builder.pushInstalledFile(pdb_dir, artifact.out_pdb_filename); | |
| 59 | } | |
| 60 | if (self.h_dir) |h_dir| { | |
| 61 | builder.pushInstalledFile(h_dir, artifact.out_h_filename); | |
| 62 | } | |
| 63 | return self; | |
| 64 | } | |
| 65 | ||
| 66 | fn make(step: *Step) !void { | |
| 67 | const self = @fieldParentPtr(Self, "step", step); | |
| 68 | const builder = self.builder; | |
| 69 | ||
| 70 | const full_dest_path = builder.getInstallPath(self.dest_dir, self.artifact.out_filename); | |
| 71 | try builder.updateFile(self.artifact.getOutputSource().getPath(builder), full_dest_path); | |
| 72 | if (self.artifact.isDynamicLibrary() and self.artifact.version != null and self.artifact.target.wantSharedLibSymLinks()) { | |
| 73 | try LibExeObjStep.doAtomicSymLinks(builder.allocator, full_dest_path, self.artifact.major_only_filename.?, self.artifact.name_only_filename.?); | |
| 74 | } | |
| 75 | if (self.artifact.isDynamicLibrary() and self.artifact.target.isWindows() and self.artifact.emit_implib != .no_emit) { | |
| 76 | const full_implib_path = builder.getInstallPath(self.dest_dir, self.artifact.out_lib_filename); | |
| 77 | try builder.updateFile(self.artifact.getOutputLibSource().getPath(builder), full_implib_path); | |
| 78 | } | |
| 79 | if (self.pdb_dir) |pdb_dir| { | |
| 80 | const full_pdb_path = builder.getInstallPath(pdb_dir, self.artifact.out_pdb_filename); | |
| 81 | try builder.updateFile(self.artifact.getOutputPdbSource().getPath(builder), full_pdb_path); | |
| 82 | } | |
| 83 | if (self.h_dir) |h_dir| { | |
| 84 | const full_h_path = builder.getInstallPath(h_dir, self.artifact.out_h_filename); | |
| 85 | try builder.updateFile(self.artifact.getOutputHSource().getPath(builder), full_h_path); | |
| 86 | } | |
| 87 | self.artifact.installed_path = full_dest_path; | |
| 88 | } |
lib/std/build/InstallDirStep.zig deleted-95| ... | ... | @@ -1,95 +0,0 @@ |
| 1 | const std = @import("../std.zig"); | |
| 2 | const mem = std.mem; | |
| 3 | const fs = std.fs; | |
| 4 | const build = @import("../build.zig"); | |
| 5 | const Step = build.Step; | |
| 6 | const Builder = build.Builder; | |
| 7 | const InstallDir = std.build.InstallDir; | |
| 8 | const InstallDirStep = @This(); | |
| 9 | const log = std.log; | |
| 10 | ||
| 11 | step: Step, | |
| 12 | builder: *Builder, | |
| 13 | options: Options, | |
| 14 | /// This is used by the build system when a file being installed comes from one | |
| 15 | /// package but is being installed by another. | |
| 16 | override_source_builder: ?*Builder = null, | |
| 17 | ||
| 18 | pub const base_id = .install_dir; | |
| 19 | ||
| 20 | pub const Options = struct { | |
| 21 | source_dir: []const u8, | |
| 22 | install_dir: InstallDir, | |
| 23 | install_subdir: []const u8, | |
| 24 | /// File paths which end in any of these suffixes will be excluded | |
| 25 | /// from being installed. | |
| 26 | exclude_extensions: []const []const u8 = &.{}, | |
| 27 | /// File paths which end in any of these suffixes will result in | |
| 28 | /// empty files being installed. This is mainly intended for large | |
| 29 | /// test.zig files in order to prevent needless installation bloat. | |
| 30 | /// However if the files were not present at all, then | |
| 31 | /// `@import("test.zig")` would be a compile error. | |
| 32 | blank_extensions: []const []const u8 = &.{}, | |
| 33 | ||
| 34 | fn dupe(self: Options, b: *Builder) Options { | |
| 35 | return .{ | |
| 36 | .source_dir = b.dupe(self.source_dir), | |
| 37 | .install_dir = self.install_dir.dupe(b), | |
| 38 | .install_subdir = b.dupe(self.install_subdir), | |
| 39 | .exclude_extensions = b.dupeStrings(self.exclude_extensions), | |
| 40 | .blank_extensions = b.dupeStrings(self.blank_extensions), | |
| 41 | }; | |
| 42 | } | |
| 43 | }; | |
| 44 | ||
| 45 | pub fn init( | |
| 46 | builder: *Builder, | |
| 47 | options: Options, | |
| 48 | ) InstallDirStep { | |
| 49 | builder.pushInstalledFile(options.install_dir, options.install_subdir); | |
| 50 | return InstallDirStep{ | |
| 51 | .builder = builder, | |
| 52 | .step = Step.init(.install_dir, builder.fmt("install {s}/", .{options.source_dir}), builder.allocator, make), | |
| 53 | .options = options.dupe(builder), | |
| 54 | }; | |
| 55 | } | |
| 56 | ||
| 57 | fn make(step: *Step) !void { | |
| 58 | const self = @fieldParentPtr(InstallDirStep, "step", step); | |
| 59 | const dest_prefix = self.builder.getInstallPath(self.options.install_dir, self.options.install_subdir); | |
| 60 | const src_builder = self.override_source_builder orelse self.builder; | |
| 61 | const full_src_dir = src_builder.pathFromRoot(self.options.source_dir); | |
| 62 | var src_dir = std.fs.cwd().openIterableDir(full_src_dir, .{}) catch |err| { | |
| 63 | log.err("InstallDirStep: unable to open source directory '{s}': {s}", .{ | |
| 64 | full_src_dir, @errorName(err), | |
| 65 | }); | |
| 66 | return error.StepFailed; | |
| 67 | }; | |
| 68 | defer src_dir.close(); | |
| 69 | var it = try src_dir.walk(self.builder.allocator); | |
| 70 | next_entry: while (try it.next()) |entry| { | |
| 71 | for (self.options.exclude_extensions) |ext| { | |
| 72 | if (mem.endsWith(u8, entry.path, ext)) { | |
| 73 | continue :next_entry; | |
| 74 | } | |
| 75 | } | |
| 76 | ||
| 77 | const full_path = self.builder.pathJoin(&.{ full_src_dir, entry.path }); | |
| 78 | const dest_path = self.builder.pathJoin(&.{ dest_prefix, entry.path }); | |
| 79 | ||
| 80 | switch (entry.kind) { | |
| 81 | .Directory => try fs.cwd().makePath(dest_path), | |
| 82 | .File => { | |
| 83 | for (self.options.blank_extensions) |ext| { | |
| 84 | if (mem.endsWith(u8, entry.path, ext)) { | |
| 85 | try self.builder.truncateFile(dest_path); | |
| 86 | continue :next_entry; | |
| 87 | } | |
| 88 | } | |
| 89 | ||
| 90 | try self.builder.updateFile(full_path, dest_path); | |
| 91 | }, | |
| 92 | else => continue, | |
| 93 | } | |
| 94 | } | |
| 95 | } |
lib/std/build/InstallFileStep.zig deleted-42| ... | ... | @@ -1,42 +0,0 @@ |
| 1 | const std = @import("../std.zig"); | |
| 2 | const build = @import("../build.zig"); | |
| 3 | const Step = build.Step; | |
| 4 | const Builder = build.Builder; | |
| 5 | const FileSource = std.build.FileSource; | |
| 6 | const InstallDir = std.build.InstallDir; | |
| 7 | const InstallFileStep = @This(); | |
| 8 | ||
| 9 | pub const base_id = .install_file; | |
| 10 | ||
| 11 | step: Step, | |
| 12 | builder: *Builder, | |
| 13 | source: FileSource, | |
| 14 | dir: InstallDir, | |
| 15 | dest_rel_path: []const u8, | |
| 16 | /// This is used by the build system when a file being installed comes from one | |
| 17 | /// package but is being installed by another. | |
| 18 | override_source_builder: ?*Builder = null, | |
| 19 | ||
| 20 | pub fn init( | |
| 21 | builder: *Builder, | |
| 22 | source: FileSource, | |
| 23 | dir: InstallDir, | |
| 24 | dest_rel_path: []const u8, | |
| 25 | ) InstallFileStep { | |
| 26 | builder.pushInstalledFile(dir, dest_rel_path); | |
| 27 | return InstallFileStep{ | |
| 28 | .builder = builder, | |
| 29 | .step = Step.init(.install_file, builder.fmt("install {s} to {s}", .{ source.getDisplayName(), dest_rel_path }), builder.allocator, make), | |
| 30 | .source = source.dupe(builder), | |
| 31 | .dir = dir.dupe(builder), | |
| 32 | .dest_rel_path = builder.dupePath(dest_rel_path), | |
| 33 | }; | |
| 34 | } | |
| 35 | ||
| 36 | fn make(step: *Step) !void { | |
| 37 | const self = @fieldParentPtr(InstallFileStep, "step", step); | |
| 38 | const src_builder = self.override_source_builder orelse self.builder; | |
| 39 | const full_src_path = self.source.getPath(src_builder); | |
| 40 | const full_dest_path = self.builder.getInstallPath(self.dir, self.dest_rel_path); | |
| 41 | try self.builder.updateFile(full_src_path, full_dest_path); | |
| 42 | } |
lib/std/build/InstallRawStep.zig deleted-106| ... | ... | @@ -1,106 +0,0 @@ |
| 1 | //! TODO: Rename this to ObjCopyStep now that it invokes the `zig objcopy` | |
| 2 | //! subcommand rather than containing an implementation directly. | |
| 3 | ||
| 4 | const std = @import("std"); | |
| 5 | const InstallRawStep = @This(); | |
| 6 | ||
| 7 | const Allocator = std.mem.Allocator; | |
| 8 | const ArenaAllocator = std.heap.ArenaAllocator; | |
| 9 | const ArrayListUnmanaged = std.ArrayListUnmanaged; | |
| 10 | const Builder = std.build.Builder; | |
| 11 | const File = std.fs.File; | |
| 12 | const InstallDir = std.build.InstallDir; | |
| 13 | const LibExeObjStep = std.build.LibExeObjStep; | |
| 14 | const Step = std.build.Step; | |
| 15 | const elf = std.elf; | |
| 16 | const fs = std.fs; | |
| 17 | const io = std.io; | |
| 18 | const sort = std.sort; | |
| 19 | ||
| 20 | pub const base_id = .install_raw; | |
| 21 | ||
| 22 | pub const RawFormat = enum { | |
| 23 | bin, | |
| 24 | hex, | |
| 25 | }; | |
| 26 | ||
| 27 | step: Step, | |
| 28 | builder: *Builder, | |
| 29 | artifact: *LibExeObjStep, | |
| 30 | dest_dir: InstallDir, | |
| 31 | dest_filename: []const u8, | |
| 32 | options: CreateOptions, | |
| 33 | output_file: std.build.GeneratedFile, | |
| 34 | ||
| 35 | pub const CreateOptions = struct { | |
| 36 | format: ?RawFormat = null, | |
| 37 | dest_dir: ?InstallDir = null, | |
| 38 | only_section: ?[]const u8 = null, | |
| 39 | pad_to: ?u64 = null, | |
| 40 | }; | |
| 41 | ||
| 42 | pub fn create(builder: *Builder, artifact: *LibExeObjStep, dest_filename: []const u8, options: CreateOptions) *InstallRawStep { | |
| 43 | const self = builder.allocator.create(InstallRawStep) catch unreachable; | |
| 44 | self.* = InstallRawStep{ | |
| 45 | .step = Step.init(.install_raw, builder.fmt("install raw binary {s}", .{artifact.step.name}), builder.allocator, make), | |
| 46 | .builder = builder, | |
| 47 | .artifact = artifact, | |
| 48 | .dest_dir = if (options.dest_dir) |d| d else switch (artifact.kind) { | |
| 49 | .obj => unreachable, | |
| 50 | .@"test" => unreachable, | |
| 51 | .exe, .test_exe => .bin, | |
| 52 | .lib => unreachable, | |
| 53 | }, | |
| 54 | .dest_filename = dest_filename, | |
| 55 | .options = options, | |
| 56 | .output_file = std.build.GeneratedFile{ .step = &self.step }, | |
| 57 | }; | |
| 58 | self.step.dependOn(&artifact.step); | |
| 59 | ||
| 60 | builder.pushInstalledFile(self.dest_dir, dest_filename); | |
| 61 | return self; | |
| 62 | } | |
| 63 | ||
| 64 | pub fn getOutputSource(self: *const InstallRawStep) std.build.FileSource { | |
| 65 | return std.build.FileSource{ .generated = &self.output_file }; | |
| 66 | } | |
| 67 | ||
| 68 | fn make(step: *Step) !void { | |
| 69 | const self = @fieldParentPtr(InstallRawStep, "step", step); | |
| 70 | const b = self.builder; | |
| 71 | ||
| 72 | if (self.artifact.target.getObjectFormat() != .elf) { | |
| 73 | std.debug.print("InstallRawStep only works with ELF format.\n", .{}); | |
| 74 | return error.InvalidObjectFormat; | |
| 75 | } | |
| 76 | ||
| 77 | const full_src_path = self.artifact.getOutputSource().getPath(b); | |
| 78 | const full_dest_path = b.getInstallPath(self.dest_dir, self.dest_filename); | |
| 79 | self.output_file.path = full_dest_path; | |
| 80 | ||
| 81 | fs.cwd().makePath(b.getInstallPath(self.dest_dir, "")) catch unreachable; | |
| 82 | ||
| 83 | var argv_list = std.ArrayList([]const u8).init(b.allocator); | |
| 84 | try argv_list.appendSlice(&.{ b.zig_exe, "objcopy" }); | |
| 85 | ||
| 86 | if (self.options.only_section) |only_section| { | |
| 87 | try argv_list.appendSlice(&.{ "-j", only_section }); | |
| 88 | } | |
| 89 | if (self.options.pad_to) |pad_to| { | |
| 90 | try argv_list.appendSlice(&.{ | |
| 91 | "--pad-to", | |
| 92 | b.fmt("{d}", .{pad_to}), | |
| 93 | }); | |
| 94 | } | |
| 95 | if (self.options.format) |format| switch (format) { | |
| 96 | .bin => try argv_list.appendSlice(&.{ "-O", "binary" }), | |
| 97 | .hex => try argv_list.appendSlice(&.{ "-O", "hex" }), | |
| 98 | }; | |
| 99 | ||
| 100 | try argv_list.appendSlice(&.{ full_src_path, full_dest_path }); | |
| 101 | _ = try self.builder.execFromStep(argv_list.items, &self.step); | |
| 102 | } | |
| 103 | ||
| 104 | test { | |
| 105 | std.testing.refAllDecls(InstallRawStep); | |
| 106 | } |
lib/std/build/LibExeObjStep.zig deleted-2110| ... | ... | @@ -1,2110 +0,0 @@ |
| 1 | const builtin = @import("builtin"); | |
| 2 | const std = @import("../std.zig"); | |
| 3 | const mem = std.mem; | |
| 4 | const log = std.log; | |
| 5 | const fs = std.fs; | |
| 6 | const assert = std.debug.assert; | |
| 7 | const panic = std.debug.panic; | |
| 8 | const ArrayList = std.ArrayList; | |
| 9 | const StringHashMap = std.StringHashMap; | |
| 10 | const Sha256 = std.crypto.hash.sha2.Sha256; | |
| 11 | const Allocator = mem.Allocator; | |
| 12 | const build = @import("../build.zig"); | |
| 13 | const Step = build.Step; | |
| 14 | const Builder = build.Builder; | |
| 15 | const CrossTarget = std.zig.CrossTarget; | |
| 16 | const NativeTargetInfo = std.zig.system.NativeTargetInfo; | |
| 17 | const FileSource = std.build.FileSource; | |
| 18 | const PkgConfigPkg = Builder.PkgConfigPkg; | |
| 19 | const PkgConfigError = Builder.PkgConfigError; | |
| 20 | const ExecError = Builder.ExecError; | |
| 21 | const Pkg = std.build.Pkg; | |
| 22 | const VcpkgRoot = std.build.VcpkgRoot; | |
| 23 | const InstallDir = std.build.InstallDir; | |
| 24 | const InstallArtifactStep = std.build.InstallArtifactStep; | |
| 25 | const GeneratedFile = std.build.GeneratedFile; | |
| 26 | const InstallRawStep = std.build.InstallRawStep; | |
| 27 | const EmulatableRunStep = std.build.EmulatableRunStep; | |
| 28 | const CheckObjectStep = std.build.CheckObjectStep; | |
| 29 | const RunStep = std.build.RunStep; | |
| 30 | const OptionsStep = std.build.OptionsStep; | |
| 31 | const ConfigHeaderStep = std.build.ConfigHeaderStep; | |
| 32 | const LibExeObjStep = @This(); | |
| 33 | ||
| 34 | pub const base_id = .lib_exe_obj; | |
| 35 | ||
| 36 | step: Step, | |
| 37 | builder: *Builder, | |
| 38 | name: []const u8, | |
| 39 | target: CrossTarget = CrossTarget{}, | |
| 40 | target_info: NativeTargetInfo, | |
| 41 | linker_script: ?FileSource = null, | |
| 42 | version_script: ?[]const u8 = null, | |
| 43 | out_filename: []const u8, | |
| 44 | linkage: ?Linkage = null, | |
| 45 | version: ?std.builtin.Version, | |
| 46 | build_mode: std.builtin.Mode, | |
| 47 | kind: Kind, | |
| 48 | major_only_filename: ?[]const u8, | |
| 49 | name_only_filename: ?[]const u8, | |
| 50 | strip: ?bool, | |
| 51 | unwind_tables: ?bool, | |
| 52 | // keep in sync with src/link.zig:CompressDebugSections | |
| 53 | compress_debug_sections: enum { none, zlib } = .none, | |
| 54 | lib_paths: ArrayList([]const u8), | |
| 55 | rpaths: ArrayList([]const u8), | |
| 56 | framework_dirs: ArrayList([]const u8), | |
| 57 | frameworks: StringHashMap(FrameworkLinkInfo), | |
| 58 | verbose_link: bool, | |
| 59 | verbose_cc: bool, | |
| 60 | emit_analysis: EmitOption = .default, | |
| 61 | emit_asm: EmitOption = .default, | |
| 62 | emit_bin: EmitOption = .default, | |
| 63 | emit_docs: EmitOption = .default, | |
| 64 | emit_implib: EmitOption = .default, | |
| 65 | emit_llvm_bc: EmitOption = .default, | |
| 66 | emit_llvm_ir: EmitOption = .default, | |
| 67 | // Lots of things depend on emit_h having a consistent path, | |
| 68 | // so it is not an EmitOption for now. | |
| 69 | emit_h: bool = false, | |
| 70 | bundle_compiler_rt: ?bool = null, | |
| 71 | single_threaded: ?bool = null, | |
| 72 | stack_protector: ?bool = null, | |
| 73 | disable_stack_probing: bool, | |
| 74 | disable_sanitize_c: bool, | |
| 75 | sanitize_thread: bool, | |
| 76 | rdynamic: bool, | |
| 77 | import_memory: bool = false, | |
| 78 | /// For WebAssembly targets, this will allow for undefined symbols to | |
| 79 | /// be imported from the host environment. | |
| 80 | import_symbols: bool = false, | |
| 81 | import_table: bool = false, | |
| 82 | export_table: bool = false, | |
| 83 | initial_memory: ?u64 = null, | |
| 84 | max_memory: ?u64 = null, | |
| 85 | shared_memory: bool = false, | |
| 86 | global_base: ?u64 = null, | |
| 87 | c_std: Builder.CStd, | |
| 88 | override_lib_dir: ?[]const u8, | |
| 89 | main_pkg_path: ?[]const u8, | |
| 90 | exec_cmd_args: ?[]const ?[]const u8, | |
| 91 | name_prefix: []const u8, | |
| 92 | filter: ?[]const u8, | |
| 93 | test_evented_io: bool = false, | |
| 94 | test_runner: ?[]const u8, | |
| 95 | code_model: std.builtin.CodeModel = .default, | |
| 96 | wasi_exec_model: ?std.builtin.WasiExecModel = null, | |
| 97 | /// Symbols to be exported when compiling to wasm | |
| 98 | export_symbol_names: []const []const u8 = &.{}, | |
| 99 | ||
| 100 | root_src: ?FileSource, | |
| 101 | out_h_filename: []const u8, | |
| 102 | out_lib_filename: []const u8, | |
| 103 | out_pdb_filename: []const u8, | |
| 104 | packages: ArrayList(Pkg), | |
| 105 | ||
| 106 | object_src: []const u8, | |
| 107 | ||
| 108 | link_objects: ArrayList(LinkObject), | |
| 109 | include_dirs: ArrayList(IncludeDir), | |
| 110 | c_macros: ArrayList([]const u8), | |
| 111 | installed_headers: ArrayList(*std.build.Step), | |
| 112 | output_dir: ?[]const u8, | |
| 113 | is_linking_libc: bool = false, | |
| 114 | is_linking_libcpp: bool = false, | |
| 115 | vcpkg_bin_path: ?[]const u8 = null, | |
| 116 | ||
| 117 | /// This may be set in order to override the default install directory | |
| 118 | override_dest_dir: ?InstallDir, | |
| 119 | installed_path: ?[]const u8, | |
| 120 | install_step: ?*InstallArtifactStep, | |
| 121 | ||
| 122 | /// Base address for an executable image. | |
| 123 | image_base: ?u64 = null, | |
| 124 | ||
| 125 | libc_file: ?FileSource = null, | |
| 126 | ||
| 127 | valgrind_support: ?bool = null, | |
| 128 | each_lib_rpath: ?bool = null, | |
| 129 | /// On ELF targets, this will emit a link section called ".note.gnu.build-id" | |
| 130 | /// which can be used to coordinate a stripped binary with its debug symbols. | |
| 131 | /// As an example, the bloaty project refuses to work unless its inputs have | |
| 132 | /// build ids, in order to prevent accidental mismatches. | |
| 133 | /// The default is to not include this section because it slows down linking. | |
| 134 | build_id: ?bool = null, | |
| 135 | ||
| 136 | /// Create a .eh_frame_hdr section and a PT_GNU_EH_FRAME segment in the ELF | |
| 137 | /// file. | |
| 138 | link_eh_frame_hdr: bool = false, | |
| 139 | link_emit_relocs: bool = false, | |
| 140 | ||
| 141 | /// Place every function in its own section so that unused ones may be | |
| 142 | /// safely garbage-collected during the linking phase. | |
| 143 | link_function_sections: bool = false, | |
| 144 | ||
| 145 | /// Remove functions and data that are unreachable by the entry point or | |
| 146 | /// exported symbols. | |
| 147 | link_gc_sections: ?bool = null, | |
| 148 | ||
| 149 | linker_allow_shlib_undefined: ?bool = null, | |
| 150 | ||
| 151 | /// Permit read-only relocations in read-only segments. Disallowed by default. | |
| 152 | link_z_notext: bool = false, | |
| 153 | ||
| 154 | /// Force all relocations to be read-only after processing. | |
| 155 | link_z_relro: bool = true, | |
| 156 | ||
| 157 | /// Allow relocations to be lazily processed after load. | |
| 158 | link_z_lazy: bool = false, | |
| 159 | ||
| 160 | /// Common page size | |
| 161 | link_z_common_page_size: ?u64 = null, | |
| 162 | ||
| 163 | /// Maximum page size | |
| 164 | link_z_max_page_size: ?u64 = null, | |
| 165 | ||
| 166 | /// (Darwin) Install name for the dylib | |
| 167 | install_name: ?[]const u8 = null, | |
| 168 | ||
| 169 | /// (Darwin) Path to entitlements file | |
| 170 | entitlements: ?[]const u8 = null, | |
| 171 | ||
| 172 | /// (Darwin) Size of the pagezero segment. | |
| 173 | pagezero_size: ?u64 = null, | |
| 174 | ||
| 175 | /// (Darwin) Search strategy for searching system libraries. Either `paths_first` or `dylibs_first`. | |
| 176 | /// The former lowers to `-search_paths_first` linker option, while the latter to `-search_dylibs_first` | |
| 177 | /// option. | |
| 178 | /// By default, if no option is specified, the linker assumes `paths_first` as the default | |
| 179 | /// search strategy. | |
| 180 | search_strategy: ?enum { paths_first, dylibs_first } = null, | |
| 181 | ||
| 182 | /// (Darwin) Set size of the padding between the end of load commands | |
| 183 | /// and start of `__TEXT,__text` section. | |
| 184 | headerpad_size: ?u32 = null, | |
| 185 | ||
| 186 | /// (Darwin) Automatically Set size of the padding between the end of load commands | |
| 187 | /// and start of `__TEXT,__text` section to a value fitting all paths expanded to MAXPATHLEN. | |
| 188 | headerpad_max_install_names: bool = false, | |
| 189 | ||
| 190 | /// (Darwin) Remove dylibs that are unreachable by the entry point or exported symbols. | |
| 191 | dead_strip_dylibs: bool = false, | |
| 192 | ||
| 193 | /// Position Independent Code | |
| 194 | force_pic: ?bool = null, | |
| 195 | ||
| 196 | /// Position Independent Executable | |
| 197 | pie: ?bool = null, | |
| 198 | ||
| 199 | red_zone: ?bool = null, | |
| 200 | ||
| 201 | omit_frame_pointer: ?bool = null, | |
| 202 | dll_export_fns: ?bool = null, | |
| 203 | ||
| 204 | subsystem: ?std.Target.SubSystem = null, | |
| 205 | ||
| 206 | entry_symbol_name: ?[]const u8 = null, | |
| 207 | ||
| 208 | /// Overrides the default stack size | |
| 209 | stack_size: ?u64 = null, | |
| 210 | ||
| 211 | want_lto: ?bool = null, | |
| 212 | use_llvm: ?bool = null, | |
| 213 | use_lld: ?bool = null, | |
| 214 | ||
| 215 | output_path_source: GeneratedFile, | |
| 216 | output_lib_path_source: GeneratedFile, | |
| 217 | output_h_path_source: GeneratedFile, | |
| 218 | output_pdb_path_source: GeneratedFile, | |
| 219 | ||
| 220 | pub const CSourceFiles = struct { | |
| 221 | files: []const []const u8, | |
| 222 | flags: []const []const u8, | |
| 223 | }; | |
| 224 | ||
| 225 | pub const CSourceFile = struct { | |
| 226 | source: FileSource, | |
| 227 | args: []const []const u8, | |
| 228 | ||
| 229 | pub fn dupe(self: CSourceFile, b: *Builder) CSourceFile { | |
| 230 | return .{ | |
| 231 | .source = self.source.dupe(b), | |
| 232 | .args = b.dupeStrings(self.args), | |
| 233 | }; | |
| 234 | } | |
| 235 | }; | |
| 236 | ||
| 237 | pub const LinkObject = union(enum) { | |
| 238 | static_path: FileSource, | |
| 239 | other_step: *LibExeObjStep, | |
| 240 | system_lib: SystemLib, | |
| 241 | assembly_file: FileSource, | |
| 242 | c_source_file: *CSourceFile, | |
| 243 | c_source_files: *CSourceFiles, | |
| 244 | }; | |
| 245 | ||
| 246 | pub const SystemLib = struct { | |
| 247 | name: []const u8, | |
| 248 | needed: bool, | |
| 249 | weak: bool, | |
| 250 | use_pkg_config: enum { | |
| 251 | /// Don't use pkg-config, just pass -lfoo where foo is name. | |
| 252 | no, | |
| 253 | /// Try to get information on how to link the library from pkg-config. | |
| 254 | /// If that fails, fall back to passing -lfoo where foo is name. | |
| 255 | yes, | |
| 256 | /// Try to get information on how to link the library from pkg-config. | |
| 257 | /// If that fails, error out. | |
| 258 | force, | |
| 259 | }, | |
| 260 | }; | |
| 261 | ||
| 262 | const FrameworkLinkInfo = struct { | |
| 263 | needed: bool = false, | |
| 264 | weak: bool = false, | |
| 265 | }; | |
| 266 | ||
| 267 | pub const IncludeDir = union(enum) { | |
| 268 | raw_path: []const u8, | |
| 269 | raw_path_system: []const u8, | |
| 270 | other_step: *LibExeObjStep, | |
| 271 | config_header_step: *ConfigHeaderStep, | |
| 272 | }; | |
| 273 | ||
| 274 | pub const Kind = enum { | |
| 275 | exe, | |
| 276 | lib, | |
| 277 | obj, | |
| 278 | @"test", | |
| 279 | test_exe, | |
| 280 | }; | |
| 281 | ||
| 282 | pub const SharedLibKind = union(enum) { | |
| 283 | versioned: std.builtin.Version, | |
| 284 | unversioned: void, | |
| 285 | }; | |
| 286 | ||
| 287 | pub const Linkage = enum { dynamic, static }; | |
| 288 | ||
| 289 | pub const EmitOption = union(enum) { | |
| 290 | default: void, | |
| 291 | no_emit: void, | |
| 292 | emit: void, | |
| 293 | emit_to: []const u8, | |
| 294 | ||
| 295 | fn getArg(self: @This(), b: *Builder, arg_name: []const u8) ?[]const u8 { | |
| 296 | return switch (self) { | |
| 297 | .no_emit => b.fmt("-fno-{s}", .{arg_name}), | |
| 298 | .default => null, | |
| 299 | .emit => b.fmt("-f{s}", .{arg_name}), | |
| 300 | .emit_to => |path| b.fmt("-f{s}={s}", .{ arg_name, path }), | |
| 301 | }; | |
| 302 | } | |
| 303 | }; | |
| 304 | ||
| 305 | pub fn createSharedLibrary(builder: *Builder, name: []const u8, root_src: ?FileSource, kind: SharedLibKind) *LibExeObjStep { | |
| 306 | return initExtraArgs(builder, name, root_src, .lib, .dynamic, switch (kind) { | |
| 307 | .versioned => |ver| ver, | |
| 308 | .unversioned => null, | |
| 309 | }); | |
| 310 | } | |
| 311 | ||
| 312 | pub fn createStaticLibrary(builder: *Builder, name: []const u8, root_src: ?FileSource) *LibExeObjStep { | |
| 313 | return initExtraArgs(builder, name, root_src, .lib, .static, null); | |
| 314 | } | |
| 315 | ||
| 316 | pub fn createObject(builder: *Builder, name: []const u8, root_src: ?FileSource) *LibExeObjStep { | |
| 317 | return initExtraArgs(builder, name, root_src, .obj, null, null); | |
| 318 | } | |
| 319 | ||
| 320 | pub fn createExecutable(builder: *Builder, name: []const u8, root_src: ?FileSource) *LibExeObjStep { | |
| 321 | return initExtraArgs(builder, name, root_src, .exe, null, null); | |
| 322 | } | |
| 323 | ||
| 324 | pub fn createTest(builder: *Builder, name: []const u8, root_src: FileSource) *LibExeObjStep { | |
| 325 | return initExtraArgs(builder, name, root_src, .@"test", null, null); | |
| 326 | } | |
| 327 | ||
| 328 | pub fn createTestExe(builder: *Builder, name: []const u8, root_src: FileSource) *LibExeObjStep { | |
| 329 | return initExtraArgs(builder, name, root_src, .test_exe, null, null); | |
| 330 | } | |
| 331 | ||
| 332 | fn initExtraArgs( | |
| 333 | builder: *Builder, | |
| 334 | name_raw: []const u8, | |
| 335 | root_src_raw: ?FileSource, | |
| 336 | kind: Kind, | |
| 337 | linkage: ?Linkage, | |
| 338 | ver: ?std.builtin.Version, | |
| 339 | ) *LibExeObjStep { | |
| 340 | const name = builder.dupe(name_raw); | |
| 341 | const root_src: ?FileSource = if (root_src_raw) |rsrc| rsrc.dupe(builder) else null; | |
| 342 | if (mem.indexOf(u8, name, "/") != null or mem.indexOf(u8, name, "\\") != null) { | |
| 343 | panic("invalid name: '{s}'. It looks like a file path, but it is supposed to be the library or application name.", .{name}); | |
| 344 | } | |
| 345 | ||
| 346 | const self = builder.allocator.create(LibExeObjStep) catch unreachable; | |
| 347 | self.* = LibExeObjStep{ | |
| 348 | .strip = null, | |
| 349 | .unwind_tables = null, | |
| 350 | .builder = builder, | |
| 351 | .verbose_link = false, | |
| 352 | .verbose_cc = false, | |
| 353 | .build_mode = std.builtin.Mode.Debug, | |
| 354 | .linkage = linkage, | |
| 355 | .kind = kind, | |
| 356 | .root_src = root_src, | |
| 357 | .name = name, | |
| 358 | .frameworks = StringHashMap(FrameworkLinkInfo).init(builder.allocator), | |
| 359 | .step = Step.init(base_id, name, builder.allocator, make), | |
| 360 | .version = ver, | |
| 361 | .out_filename = undefined, | |
| 362 | .out_h_filename = builder.fmt("{s}.h", .{name}), | |
| 363 | .out_lib_filename = undefined, | |
| 364 | .out_pdb_filename = builder.fmt("{s}.pdb", .{name}), | |
| 365 | .major_only_filename = null, | |
| 366 | .name_only_filename = null, | |
| 367 | .packages = ArrayList(Pkg).init(builder.allocator), | |
| 368 | .include_dirs = ArrayList(IncludeDir).init(builder.allocator), | |
| 369 | .link_objects = ArrayList(LinkObject).init(builder.allocator), | |
| 370 | .c_macros = ArrayList([]const u8).init(builder.allocator), | |
| 371 | .lib_paths = ArrayList([]const u8).init(builder.allocator), | |
| 372 | .rpaths = ArrayList([]const u8).init(builder.allocator), | |
| 373 | .framework_dirs = ArrayList([]const u8).init(builder.allocator), | |
| 374 | .installed_headers = ArrayList(*std.build.Step).init(builder.allocator), | |
| 375 | .object_src = undefined, | |
| 376 | .c_std = Builder.CStd.C99, | |
| 377 | .override_lib_dir = null, | |
| 378 | .main_pkg_path = null, | |
| 379 | .exec_cmd_args = null, | |
| 380 | .name_prefix = "", | |
| 381 | .filter = null, | |
| 382 | .test_runner = null, | |
| 383 | .disable_stack_probing = false, | |
| 384 | .disable_sanitize_c = false, | |
| 385 | .sanitize_thread = false, | |
| 386 | .rdynamic = false, | |
| 387 | .output_dir = null, | |
| 388 | .override_dest_dir = null, | |
| 389 | .installed_path = null, | |
| 390 | .install_step = null, | |
| 391 | ||
| 392 | .output_path_source = GeneratedFile{ .step = &self.step }, | |
| 393 | .output_lib_path_source = GeneratedFile{ .step = &self.step }, | |
| 394 | .output_h_path_source = GeneratedFile{ .step = &self.step }, | |
| 395 | .output_pdb_path_source = GeneratedFile{ .step = &self.step }, | |
| 396 | ||
| 397 | .target_info = undefined, // populated in computeOutFileNames | |
| 398 | }; | |
| 399 | self.computeOutFileNames(); | |
| 400 | if (root_src) |rs| rs.addStepDependencies(&self.step); | |
| 401 | return self; | |
| 402 | } | |
| 403 | ||
| 404 | fn computeOutFileNames(self: *LibExeObjStep) void { | |
| 405 | self.target_info = NativeTargetInfo.detect(self.target) catch | |
| 406 | unreachable; | |
| 407 | ||
| 408 | const target = self.target_info.target; | |
| 409 | ||
| 410 | self.out_filename = std.zig.binNameAlloc(self.builder.allocator, .{ | |
| 411 | .root_name = self.name, | |
| 412 | .target = target, | |
| 413 | .output_mode = switch (self.kind) { | |
| 414 | .lib => .Lib, | |
| 415 | .obj => .Obj, | |
| 416 | .exe, .@"test", .test_exe => .Exe, | |
| 417 | }, | |
| 418 | .link_mode = if (self.linkage) |some| @as(std.builtin.LinkMode, switch (some) { | |
| 419 | .dynamic => .Dynamic, | |
| 420 | .static => .Static, | |
| 421 | }) else null, | |
| 422 | .version = self.version, | |
| 423 | }) catch unreachable; | |
| 424 | ||
| 425 | if (self.kind == .lib) { | |
| 426 | if (self.linkage != null and self.linkage.? == .static) { | |
| 427 | self.out_lib_filename = self.out_filename; | |
| 428 | } else if (self.version) |version| { | |
| 429 | if (target.isDarwin()) { | |
| 430 | self.major_only_filename = self.builder.fmt("lib{s}.{d}.dylib", .{ | |
| 431 | self.name, | |
| 432 | version.major, | |
| 433 | }); | |
| 434 | self.name_only_filename = self.builder.fmt("lib{s}.dylib", .{self.name}); | |
| 435 | self.out_lib_filename = self.out_filename; | |
| 436 | } else if (target.os.tag == .windows) { | |
| 437 | self.out_lib_filename = self.builder.fmt("{s}.lib", .{self.name}); | |
| 438 | } else { | |
| 439 | self.major_only_filename = self.builder.fmt("lib{s}.so.{d}", .{ self.name, version.major }); | |
| 440 | self.name_only_filename = self.builder.fmt("lib{s}.so", .{self.name}); | |
| 441 | self.out_lib_filename = self.out_filename; | |
| 442 | } | |
| 443 | } else { | |
| 444 | if (target.isDarwin()) { | |
| 445 | self.out_lib_filename = self.out_filename; | |
| 446 | } else if (target.os.tag == .windows) { | |
| 447 | self.out_lib_filename = self.builder.fmt("{s}.lib", .{self.name}); | |
| 448 | } else { | |
| 449 | self.out_lib_filename = self.out_filename; | |
| 450 | } | |
| 451 | } | |
| 452 | if (self.output_dir != null) { | |
| 453 | self.output_lib_path_source.path = self.builder.pathJoin( | |
| 454 | &.{ self.output_dir.?, self.out_lib_filename }, | |
| 455 | ); | |
| 456 | } | |
| 457 | } | |
| 458 | } | |
| 459 | ||
| 460 | pub fn setTarget(self: *LibExeObjStep, target: CrossTarget) void { | |
| 461 | self.target = target; | |
| 462 | self.computeOutFileNames(); | |
| 463 | } | |
| 464 | ||
| 465 | pub fn setOutputDir(self: *LibExeObjStep, dir: []const u8) void { | |
| 466 | self.output_dir = self.builder.dupePath(dir); | |
| 467 | } | |
| 468 | ||
| 469 | pub fn install(self: *LibExeObjStep) void { | |
| 470 | self.builder.installArtifact(self); | |
| 471 | } | |
| 472 | ||
| 473 | pub fn installRaw(self: *LibExeObjStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep { | |
| 474 | return self.builder.installRaw(self, dest_filename, options); | |
| 475 | } | |
| 476 | ||
| 477 | pub fn installHeader(a: *LibExeObjStep, src_path: []const u8, dest_rel_path: []const u8) void { | |
| 478 | const install_file = a.builder.addInstallHeaderFile(src_path, dest_rel_path); | |
| 479 | a.builder.getInstallStep().dependOn(&install_file.step); | |
| 480 | a.installed_headers.append(&install_file.step) catch unreachable; | |
| 481 | } | |
| 482 | ||
| 483 | pub fn installHeadersDirectory( | |
| 484 | a: *LibExeObjStep, | |
| 485 | src_dir_path: []const u8, | |
| 486 | dest_rel_path: []const u8, | |
| 487 | ) void { | |
| 488 | return installHeadersDirectoryOptions(a, .{ | |
| 489 | .source_dir = src_dir_path, | |
| 490 | .install_dir = .header, | |
| 491 | .install_subdir = dest_rel_path, | |
| 492 | }); | |
| 493 | } | |
| 494 | ||
| 495 | pub fn installHeadersDirectoryOptions( | |
| 496 | a: *LibExeObjStep, | |
| 497 | options: std.build.InstallDirStep.Options, | |
| 498 | ) void { | |
| 499 | const install_dir = a.builder.addInstallDirectory(options); | |
| 500 | a.builder.getInstallStep().dependOn(&install_dir.step); | |
| 501 | a.installed_headers.append(&install_dir.step) catch unreachable; | |
| 502 | } | |
| 503 | ||
| 504 | pub fn installLibraryHeaders(a: *LibExeObjStep, l: *LibExeObjStep) void { | |
| 505 | assert(l.kind == .lib); | |
| 506 | const install_step = a.builder.getInstallStep(); | |
| 507 | // Copy each element from installed_headers, modifying the builder | |
| 508 | // to be the new parent's builder. | |
| 509 | for (l.installed_headers.items) |step| { | |
| 510 | const step_copy = switch (step.id) { | |
| 511 | inline .install_file, .install_dir => |id| blk: { | |
| 512 | const T = id.Type(); | |
| 513 | const ptr = a.builder.allocator.create(T) catch unreachable; | |
| 514 | ptr.* = step.cast(T).?.*; | |
| 515 | ptr.override_source_builder = ptr.builder; | |
| 516 | ptr.builder = a.builder; | |
| 517 | break :blk &ptr.step; | |
| 518 | }, | |
| 519 | else => unreachable, | |
| 520 | }; | |
| 521 | a.installed_headers.append(step_copy) catch unreachable; | |
| 522 | install_step.dependOn(step_copy); | |
| 523 | } | |
| 524 | a.installed_headers.appendSlice(l.installed_headers.items) catch unreachable; | |
| 525 | } | |
| 526 | ||
| 527 | /// Creates a `RunStep` with an executable built with `addExecutable`. | |
| 528 | /// Add command line arguments with `addArg`. | |
| 529 | pub fn run(exe: *LibExeObjStep) *RunStep { | |
| 530 | assert(exe.kind == .exe or exe.kind == .test_exe); | |
| 531 | ||
| 532 | // It doesn't have to be native. We catch that if you actually try to run it. | |
| 533 | // Consider that this is declarative; the run step may not be run unless a user | |
| 534 | // option is supplied. | |
| 535 | const run_step = RunStep.create(exe.builder, exe.builder.fmt("run {s}", .{exe.step.name})); | |
| 536 | run_step.addArtifactArg(exe); | |
| 537 | ||
| 538 | if (exe.kind == .test_exe) { | |
| 539 | run_step.addArg(exe.builder.zig_exe); | |
| 540 | } | |
| 541 | ||
| 542 | if (exe.vcpkg_bin_path) |path| { | |
| 543 | run_step.addPathDir(path); | |
| 544 | } | |
| 545 | ||
| 546 | return run_step; | |
| 547 | } | |
| 548 | ||
| 549 | /// Creates an `EmulatableRunStep` with an executable built with `addExecutable`. | |
| 550 | /// Allows running foreign binaries through emulation platforms such as Qemu or Rosetta. | |
| 551 | /// When a binary cannot be ran through emulation or the option is disabled, a warning | |
| 552 | /// will be printed and the binary will *NOT* be ran. | |
| 553 | pub fn runEmulatable(exe: *LibExeObjStep) *EmulatableRunStep { | |
| 554 | assert(exe.kind == .exe or exe.kind == .test_exe); | |
| 555 | ||
| 556 | const run_step = EmulatableRunStep.create(exe.builder, exe.builder.fmt("run {s}", .{exe.step.name}), exe); | |
| 557 | if (exe.vcpkg_bin_path) |path| { | |
| 558 | RunStep.addPathDirInternal(&run_step.step, exe.builder, path); | |
| 559 | } | |
| 560 | return run_step; | |
| 561 | } | |
| 562 | ||
| 563 | pub fn checkObject(self: *LibExeObjStep, obj_format: std.Target.ObjectFormat) *CheckObjectStep { | |
| 564 | return CheckObjectStep.create(self.builder, self.getOutputSource(), obj_format); | |
| 565 | } | |
| 566 | ||
| 567 | pub fn setLinkerScriptPath(self: *LibExeObjStep, source: FileSource) void { | |
| 568 | self.linker_script = source.dupe(self.builder); | |
| 569 | source.addStepDependencies(&self.step); | |
| 570 | } | |
| 571 | ||
| 572 | pub fn linkFramework(self: *LibExeObjStep, framework_name: []const u8) void { | |
| 573 | self.frameworks.put(self.builder.dupe(framework_name), .{}) catch unreachable; | |
| 574 | } | |
| 575 | ||
| 576 | pub fn linkFrameworkNeeded(self: *LibExeObjStep, framework_name: []const u8) void { | |
| 577 | self.frameworks.put(self.builder.dupe(framework_name), .{ | |
| 578 | .needed = true, | |
| 579 | }) catch unreachable; | |
| 580 | } | |
| 581 | ||
| 582 | pub fn linkFrameworkWeak(self: *LibExeObjStep, framework_name: []const u8) void { | |
| 583 | self.frameworks.put(self.builder.dupe(framework_name), .{ | |
| 584 | .weak = true, | |
| 585 | }) catch unreachable; | |
| 586 | } | |
| 587 | ||
| 588 | /// Returns whether the library, executable, or object depends on a particular system library. | |
| 589 | pub fn dependsOnSystemLibrary(self: LibExeObjStep, name: []const u8) bool { | |
| 590 | if (isLibCLibrary(name)) { | |
| 591 | return self.is_linking_libc; | |
| 592 | } | |
| 593 | if (isLibCppLibrary(name)) { | |
| 594 | return self.is_linking_libcpp; | |
| 595 | } | |
| 596 | for (self.link_objects.items) |link_object| { | |
| 597 | switch (link_object) { | |
| 598 | .system_lib => |lib| if (mem.eql(u8, lib.name, name)) return true, | |
| 599 | else => continue, | |
| 600 | } | |
| 601 | } | |
| 602 | return false; | |
| 603 | } | |
| 604 | ||
| 605 | pub fn linkLibrary(self: *LibExeObjStep, lib: *LibExeObjStep) void { | |
| 606 | assert(lib.kind == .lib); | |
| 607 | self.linkLibraryOrObject(lib); | |
| 608 | } | |
| 609 | ||
| 610 | pub fn isDynamicLibrary(self: *LibExeObjStep) bool { | |
| 611 | return self.kind == .lib and self.linkage == Linkage.dynamic; | |
| 612 | } | |
| 613 | ||
| 614 | pub fn isStaticLibrary(self: *LibExeObjStep) bool { | |
| 615 | return self.kind == .lib and self.linkage != Linkage.dynamic; | |
| 616 | } | |
| 617 | ||
| 618 | pub fn producesPdbFile(self: *LibExeObjStep) bool { | |
| 619 | if (!self.target.isWindows() and !self.target.isUefi()) return false; | |
| 620 | if (self.strip == true) return false; | |
| 621 | return self.isDynamicLibrary() or self.kind == .exe or self.kind == .test_exe; | |
| 622 | } | |
| 623 | ||
| 624 | pub fn linkLibC(self: *LibExeObjStep) void { | |
| 625 | self.is_linking_libc = true; | |
| 626 | } | |
| 627 | ||
| 628 | pub fn linkLibCpp(self: *LibExeObjStep) void { | |
| 629 | self.is_linking_libcpp = true; | |
| 630 | } | |
| 631 | ||
| 632 | /// If the value is omitted, it is set to 1. | |
| 633 | /// `name` and `value` need not live longer than the function call. | |
| 634 | pub fn defineCMacro(self: *LibExeObjStep, name: []const u8, value: ?[]const u8) void { | |
| 635 | const macro = std.build.constructCMacro(self.builder.allocator, name, value); | |
| 636 | self.c_macros.append(macro) catch unreachable; | |
| 637 | } | |
| 638 | ||
| 639 | /// name_and_value looks like [name]=[value]. If the value is omitted, it is set to 1. | |
| 640 | pub fn defineCMacroRaw(self: *LibExeObjStep, name_and_value: []const u8) void { | |
| 641 | self.c_macros.append(self.builder.dupe(name_and_value)) catch unreachable; | |
| 642 | } | |
| 643 | ||
| 644 | /// This one has no integration with anything, it just puts -lname on the command line. | |
| 645 | /// Prefer to use `linkSystemLibrary` instead. | |
| 646 | pub fn linkSystemLibraryName(self: *LibExeObjStep, name: []const u8) void { | |
| 647 | self.link_objects.append(.{ | |
| 648 | .system_lib = .{ | |
| 649 | .name = self.builder.dupe(name), | |
| 650 | .needed = false, | |
| 651 | .weak = false, | |
| 652 | .use_pkg_config = .no, | |
| 653 | }, | |
| 654 | }) catch unreachable; | |
| 655 | } | |
| 656 | ||
| 657 | /// This one has no integration with anything, it just puts -needed-lname on the command line. | |
| 658 | /// Prefer to use `linkSystemLibraryNeeded` instead. | |
| 659 | pub fn linkSystemLibraryNeededName(self: *LibExeObjStep, name: []const u8) void { | |
| 660 | self.link_objects.append(.{ | |
| 661 | .system_lib = .{ | |
| 662 | .name = self.builder.dupe(name), | |
| 663 | .needed = true, | |
| 664 | .weak = false, | |
| 665 | .use_pkg_config = .no, | |
| 666 | }, | |
| 667 | }) catch unreachable; | |
| 668 | } | |
| 669 | ||
| 670 | /// Darwin-only. This one has no integration with anything, it just puts -weak-lname on the | |
| 671 | /// command line. Prefer to use `linkSystemLibraryWeak` instead. | |
| 672 | pub fn linkSystemLibraryWeakName(self: *LibExeObjStep, name: []const u8) void { | |
| 673 | self.link_objects.append(.{ | |
| 674 | .system_lib = .{ | |
| 675 | .name = self.builder.dupe(name), | |
| 676 | .needed = false, | |
| 677 | .weak = true, | |
| 678 | .use_pkg_config = .no, | |
| 679 | }, | |
| 680 | }) catch unreachable; | |
| 681 | } | |
| 682 | ||
| 683 | /// This links against a system library, exclusively using pkg-config to find the library. | |
| 684 | /// Prefer to use `linkSystemLibrary` instead. | |
| 685 | pub fn linkSystemLibraryPkgConfigOnly(self: *LibExeObjStep, lib_name: []const u8) void { | |
| 686 | self.link_objects.append(.{ | |
| 687 | .system_lib = .{ | |
| 688 | .name = self.builder.dupe(lib_name), | |
| 689 | .needed = false, | |
| 690 | .weak = false, | |
| 691 | .use_pkg_config = .force, | |
| 692 | }, | |
| 693 | }) catch unreachable; | |
| 694 | } | |
| 695 | ||
| 696 | /// This links against a system library, exclusively using pkg-config to find the library. | |
| 697 | /// Prefer to use `linkSystemLibraryNeeded` instead. | |
| 698 | pub fn linkSystemLibraryNeededPkgConfigOnly(self: *LibExeObjStep, lib_name: []const u8) void { | |
| 699 | self.link_objects.append(.{ | |
| 700 | .system_lib = .{ | |
| 701 | .name = self.builder.dupe(lib_name), | |
| 702 | .needed = true, | |
| 703 | .weak = false, | |
| 704 | .use_pkg_config = .force, | |
| 705 | }, | |
| 706 | }) catch unreachable; | |
| 707 | } | |
| 708 | ||
| 709 | /// Run pkg-config for the given library name and parse the output, returning the arguments | |
| 710 | /// that should be passed to zig to link the given library. | |
| 711 | pub fn runPkgConfig(self: *LibExeObjStep, lib_name: []const u8) ![]const []const u8 { | |
| 712 | const pkg_name = match: { | |
| 713 | // First we have to map the library name to pkg config name. Unfortunately, | |
| 714 | // there are several examples where this is not straightforward: | |
| 715 | // -lSDL2 -> pkg-config sdl2 | |
| 716 | // -lgdk-3 -> pkg-config gdk-3.0 | |
| 717 | // -latk-1.0 -> pkg-config atk | |
| 718 | const pkgs = try getPkgConfigList(self.builder); | |
| 719 | ||
| 720 | // Exact match means instant winner. | |
| 721 | for (pkgs) |pkg| { | |
| 722 | if (mem.eql(u8, pkg.name, lib_name)) { | |
| 723 | break :match pkg.name; | |
| 724 | } | |
| 725 | } | |
| 726 | ||
| 727 | // Next we'll try ignoring case. | |
| 728 | for (pkgs) |pkg| { | |
| 729 | if (std.ascii.eqlIgnoreCase(pkg.name, lib_name)) { | |
| 730 | break :match pkg.name; | |
| 731 | } | |
| 732 | } | |
| 733 | ||
| 734 | // Now try appending ".0". | |
| 735 | for (pkgs) |pkg| { | |
| 736 | if (std.ascii.indexOfIgnoreCase(pkg.name, lib_name)) |pos| { | |
| 737 | if (pos != 0) continue; | |
| 738 | if (mem.eql(u8, pkg.name[lib_name.len..], ".0")) { | |
| 739 | break :match pkg.name; | |
| 740 | } | |
| 741 | } | |
| 742 | } | |
| 743 | ||
| 744 | // Trimming "-1.0". | |
| 745 | if (mem.endsWith(u8, lib_name, "-1.0")) { | |
| 746 | const trimmed_lib_name = lib_name[0 .. lib_name.len - "-1.0".len]; | |
| 747 | for (pkgs) |pkg| { | |
| 748 | if (std.ascii.eqlIgnoreCase(pkg.name, trimmed_lib_name)) { | |
| 749 | break :match pkg.name; | |
| 750 | } | |
| 751 | } | |
| 752 | } | |
| 753 | ||
| 754 | return error.PackageNotFound; | |
| 755 | }; | |
| 756 | ||
| 757 | var code: u8 = undefined; | |
| 758 | const stdout = if (self.builder.execAllowFail(&[_][]const u8{ | |
| 759 | "pkg-config", | |
| 760 | pkg_name, | |
| 761 | "--cflags", | |
| 762 | "--libs", | |
| 763 | }, &code, .Ignore)) |stdout| stdout else |err| switch (err) { | |
| 764 | error.ProcessTerminated => return error.PkgConfigCrashed, | |
| 765 | error.ExecNotSupported => return error.PkgConfigFailed, | |
| 766 | error.ExitCodeFailure => return error.PkgConfigFailed, | |
| 767 | error.FileNotFound => return error.PkgConfigNotInstalled, | |
| 768 | error.ChildExecFailed => return error.PkgConfigFailed, | |
| 769 | else => return err, | |
| 770 | }; | |
| 771 | ||
| 772 | var zig_args = ArrayList([]const u8).init(self.builder.allocator); | |
| 773 | defer zig_args.deinit(); | |
| 774 | ||
| 775 | var it = mem.tokenize(u8, stdout, " \r\n\t"); | |
| 776 | while (it.next()) |tok| { | |
| 777 | if (mem.eql(u8, tok, "-I")) { | |
| 778 | const dir = it.next() orelse return error.PkgConfigInvalidOutput; | |
| 779 | try zig_args.appendSlice(&[_][]const u8{ "-I", dir }); | |
| 780 | } else if (mem.startsWith(u8, tok, "-I")) { | |
| 781 | try zig_args.append(tok); | |
| 782 | } else if (mem.eql(u8, tok, "-L")) { | |
| 783 | const dir = it.next() orelse return error.PkgConfigInvalidOutput; | |
| 784 | try zig_args.appendSlice(&[_][]const u8{ "-L", dir }); | |
| 785 | } else if (mem.startsWith(u8, tok, "-L")) { | |
| 786 | try zig_args.append(tok); | |
| 787 | } else if (mem.eql(u8, tok, "-l")) { | |
| 788 | const lib = it.next() orelse return error.PkgConfigInvalidOutput; | |
| 789 | try zig_args.appendSlice(&[_][]const u8{ "-l", lib }); | |
| 790 | } else if (mem.startsWith(u8, tok, "-l")) { | |
| 791 | try zig_args.append(tok); | |
| 792 | } else if (mem.eql(u8, tok, "-D")) { | |
| 793 | const macro = it.next() orelse return error.PkgConfigInvalidOutput; | |
| 794 | try zig_args.appendSlice(&[_][]const u8{ "-D", macro }); | |
| 795 | } else if (mem.startsWith(u8, tok, "-D")) { | |
| 796 | try zig_args.append(tok); | |
| 797 | } else if (self.builder.verbose) { | |
| 798 | log.warn("Ignoring pkg-config flag '{s}'", .{tok}); | |
| 799 | } | |
| 800 | } | |
| 801 | ||
| 802 | return zig_args.toOwnedSlice(); | |
| 803 | } | |
| 804 | ||
| 805 | pub fn linkSystemLibrary(self: *LibExeObjStep, name: []const u8) void { | |
| 806 | self.linkSystemLibraryInner(name, .{}); | |
| 807 | } | |
| 808 | ||
| 809 | pub fn linkSystemLibraryNeeded(self: *LibExeObjStep, name: []const u8) void { | |
| 810 | self.linkSystemLibraryInner(name, .{ .needed = true }); | |
| 811 | } | |
| 812 | ||
| 813 | pub fn linkSystemLibraryWeak(self: *LibExeObjStep, name: []const u8) void { | |
| 814 | self.linkSystemLibraryInner(name, .{ .weak = true }); | |
| 815 | } | |
| 816 | ||
| 817 | fn linkSystemLibraryInner(self: *LibExeObjStep, name: []const u8, opts: struct { | |
| 818 | needed: bool = false, | |
| 819 | weak: bool = false, | |
| 820 | }) void { | |
| 821 | if (isLibCLibrary(name)) { | |
| 822 | self.linkLibC(); | |
| 823 | return; | |
| 824 | } | |
| 825 | if (isLibCppLibrary(name)) { | |
| 826 | self.linkLibCpp(); | |
| 827 | return; | |
| 828 | } | |
| 829 | ||
| 830 | self.link_objects.append(.{ | |
| 831 | .system_lib = .{ | |
| 832 | .name = self.builder.dupe(name), | |
| 833 | .needed = opts.needed, | |
| 834 | .weak = opts.weak, | |
| 835 | .use_pkg_config = .yes, | |
| 836 | }, | |
| 837 | }) catch unreachable; | |
| 838 | } | |
| 839 | ||
| 840 | pub fn setNamePrefix(self: *LibExeObjStep, text: []const u8) void { | |
| 841 | assert(self.kind == .@"test" or self.kind == .test_exe); | |
| 842 | self.name_prefix = self.builder.dupe(text); | |
| 843 | } | |
| 844 | ||
| 845 | pub fn setFilter(self: *LibExeObjStep, text: ?[]const u8) void { | |
| 846 | assert(self.kind == .@"test" or self.kind == .test_exe); | |
| 847 | self.filter = if (text) |t| self.builder.dupe(t) else null; | |
| 848 | } | |
| 849 | ||
| 850 | pub fn setTestRunner(self: *LibExeObjStep, path: ?[]const u8) void { | |
| 851 | assert(self.kind == .@"test" or self.kind == .test_exe); | |
| 852 | self.test_runner = if (path) |p| self.builder.dupePath(p) else null; | |
| 853 | } | |
| 854 | ||
| 855 | /// Handy when you have many C/C++ source files and want them all to have the same flags. | |
| 856 | pub fn addCSourceFiles(self: *LibExeObjStep, files: []const []const u8, flags: []const []const u8) void { | |
| 857 | const c_source_files = self.builder.allocator.create(CSourceFiles) catch unreachable; | |
| 858 | ||
| 859 | const files_copy = self.builder.dupeStrings(files); | |
| 860 | const flags_copy = self.builder.dupeStrings(flags); | |
| 861 | ||
| 862 | c_source_files.* = .{ | |
| 863 | .files = files_copy, | |
| 864 | .flags = flags_copy, | |
| 865 | }; | |
| 866 | self.link_objects.append(.{ .c_source_files = c_source_files }) catch unreachable; | |
| 867 | } | |
| 868 | ||
| 869 | pub fn addCSourceFile(self: *LibExeObjStep, file: []const u8, flags: []const []const u8) void { | |
| 870 | self.addCSourceFileSource(.{ | |
| 871 | .args = flags, | |
| 872 | .source = .{ .path = file }, | |
| 873 | }); | |
| 874 | } | |
| 875 | ||
| 876 | pub fn addCSourceFileSource(self: *LibExeObjStep, source: CSourceFile) void { | |
| 877 | const c_source_file = self.builder.allocator.create(CSourceFile) catch unreachable; | |
| 878 | c_source_file.* = source.dupe(self.builder); | |
| 879 | self.link_objects.append(.{ .c_source_file = c_source_file }) catch unreachable; | |
| 880 | source.source.addStepDependencies(&self.step); | |
| 881 | } | |
| 882 | ||
| 883 | pub fn setVerboseLink(self: *LibExeObjStep, value: bool) void { | |
| 884 | self.verbose_link = value; | |
| 885 | } | |
| 886 | ||
| 887 | pub fn setVerboseCC(self: *LibExeObjStep, value: bool) void { | |
| 888 | self.verbose_cc = value; | |
| 889 | } | |
| 890 | ||
| 891 | pub fn setBuildMode(self: *LibExeObjStep, mode: std.builtin.Mode) void { | |
| 892 | self.build_mode = mode; | |
| 893 | } | |
| 894 | ||
| 895 | pub fn overrideZigLibDir(self: *LibExeObjStep, dir_path: []const u8) void { | |
| 896 | self.override_lib_dir = self.builder.dupePath(dir_path); | |
| 897 | } | |
| 898 | ||
| 899 | pub fn setMainPkgPath(self: *LibExeObjStep, dir_path: []const u8) void { | |
| 900 | self.main_pkg_path = self.builder.dupePath(dir_path); | |
| 901 | } | |
| 902 | ||
| 903 | pub fn setLibCFile(self: *LibExeObjStep, libc_file: ?FileSource) void { | |
| 904 | self.libc_file = if (libc_file) |f| f.dupe(self.builder) else null; | |
| 905 | } | |
| 906 | ||
| 907 | /// Returns the generated executable, library or object file. | |
| 908 | /// To run an executable built with zig build, use `run`, or create an install step and invoke it. | |
| 909 | pub fn getOutputSource(self: *LibExeObjStep) FileSource { | |
| 910 | return FileSource{ .generated = &self.output_path_source }; | |
| 911 | } | |
| 912 | ||
| 913 | /// Returns the generated import library. This function can only be called for libraries. | |
| 914 | pub fn getOutputLibSource(self: *LibExeObjStep) FileSource { | |
| 915 | assert(self.kind == .lib); | |
| 916 | return FileSource{ .generated = &self.output_lib_path_source }; | |
| 917 | } | |
| 918 | ||
| 919 | /// Returns the generated header file. | |
| 920 | /// This function can only be called for libraries or object files which have `emit_h` set. | |
| 921 | pub fn getOutputHSource(self: *LibExeObjStep) FileSource { | |
| 922 | assert(self.kind != .exe and self.kind != .test_exe and self.kind != .@"test"); | |
| 923 | assert(self.emit_h); | |
| 924 | return FileSource{ .generated = &self.output_h_path_source }; | |
| 925 | } | |
| 926 | ||
| 927 | /// Returns the generated PDB file. This function can only be called for Windows and UEFI. | |
| 928 | pub fn getOutputPdbSource(self: *LibExeObjStep) FileSource { | |
| 929 | // TODO: Is this right? Isn't PDB for *any* PE/COFF file? | |
| 930 | assert(self.target.isWindows() or self.target.isUefi()); | |
| 931 | return FileSource{ .generated = &self.output_pdb_path_source }; | |
| 932 | } | |
| 933 | ||
| 934 | pub fn addAssemblyFile(self: *LibExeObjStep, path: []const u8) void { | |
| 935 | self.link_objects.append(.{ | |
| 936 | .assembly_file = .{ .path = self.builder.dupe(path) }, | |
| 937 | }) catch unreachable; | |
| 938 | } | |
| 939 | ||
| 940 | pub fn addAssemblyFileSource(self: *LibExeObjStep, source: FileSource) void { | |
| 941 | const source_duped = source.dupe(self.builder); | |
| 942 | self.link_objects.append(.{ .assembly_file = source_duped }) catch unreachable; | |
| 943 | source_duped.addStepDependencies(&self.step); | |
| 944 | } | |
| 945 | ||
| 946 | pub fn addObjectFile(self: *LibExeObjStep, source_file: []const u8) void { | |
| 947 | self.addObjectFileSource(.{ .path = source_file }); | |
| 948 | } | |
| 949 | ||
| 950 | pub fn addObjectFileSource(self: *LibExeObjStep, source: FileSource) void { | |
| 951 | self.link_objects.append(.{ .static_path = source.dupe(self.builder) }) catch unreachable; | |
| 952 | source.addStepDependencies(&self.step); | |
| 953 | } | |
| 954 | ||
| 955 | pub fn addObject(self: *LibExeObjStep, obj: *LibExeObjStep) void { | |
| 956 | assert(obj.kind == .obj); | |
| 957 | self.linkLibraryOrObject(obj); | |
| 958 | } | |
| 959 | ||
| 960 | pub const addSystemIncludeDir = @compileError("deprecated; use addSystemIncludePath"); | |
| 961 | pub const addIncludeDir = @compileError("deprecated; use addIncludePath"); | |
| 962 | pub const addLibPath = @compileError("deprecated, use addLibraryPath"); | |
| 963 | pub const addFrameworkDir = @compileError("deprecated, use addFrameworkPath"); | |
| 964 | ||
| 965 | pub fn addSystemIncludePath(self: *LibExeObjStep, path: []const u8) void { | |
| 966 | self.include_dirs.append(IncludeDir{ .raw_path_system = self.builder.dupe(path) }) catch unreachable; | |
| 967 | } | |
| 968 | ||
| 969 | pub fn addIncludePath(self: *LibExeObjStep, path: []const u8) void { | |
| 970 | self.include_dirs.append(IncludeDir{ .raw_path = self.builder.dupe(path) }) catch unreachable; | |
| 971 | } | |
| 972 | ||
| 973 | pub fn addConfigHeader(self: *LibExeObjStep, config_header: *ConfigHeaderStep) void { | |
| 974 | self.step.dependOn(&config_header.step); | |
| 975 | self.include_dirs.append(.{ .config_header_step = config_header }) catch @panic("OOM"); | |
| 976 | } | |
| 977 | ||
| 978 | pub fn addLibraryPath(self: *LibExeObjStep, path: []const u8) void { | |
| 979 | self.lib_paths.append(self.builder.dupe(path)) catch unreachable; | |
| 980 | } | |
| 981 | ||
| 982 | pub fn addRPath(self: *LibExeObjStep, path: []const u8) void { | |
| 983 | self.rpaths.append(self.builder.dupe(path)) catch unreachable; | |
| 984 | } | |
| 985 | ||
| 986 | pub fn addFrameworkPath(self: *LibExeObjStep, dir_path: []const u8) void { | |
| 987 | self.framework_dirs.append(self.builder.dupe(dir_path)) catch unreachable; | |
| 988 | } | |
| 989 | ||
| 990 | pub fn addPackage(self: *LibExeObjStep, package: Pkg) void { | |
| 991 | self.packages.append(self.builder.dupePkg(package)) catch unreachable; | |
| 992 | self.addRecursiveBuildDeps(package); | |
| 993 | } | |
| 994 | ||
| 995 | pub fn addOptions(self: *LibExeObjStep, package_name: []const u8, options: *OptionsStep) void { | |
| 996 | self.addPackage(options.getPackage(package_name)); | |
| 997 | } | |
| 998 | ||
| 999 | fn addRecursiveBuildDeps(self: *LibExeObjStep, package: Pkg) void { | |
| 1000 | package.source.addStepDependencies(&self.step); | |
| 1001 | if (package.dependencies) |deps| { | |
| 1002 | for (deps) |dep| { | |
| 1003 | self.addRecursiveBuildDeps(dep); | |
| 1004 | } | |
| 1005 | } | |
| 1006 | } | |
| 1007 | ||
| 1008 | pub fn addPackagePath(self: *LibExeObjStep, name: []const u8, pkg_index_path: []const u8) void { | |
| 1009 | self.addPackage(Pkg{ | |
| 1010 | .name = self.builder.dupe(name), | |
| 1011 | .source = .{ .path = self.builder.dupe(pkg_index_path) }, | |
| 1012 | }); | |
| 1013 | } | |
| 1014 | ||
| 1015 | /// If Vcpkg was found on the system, it will be added to include and lib | |
| 1016 | /// paths for the specified target. | |
| 1017 | pub fn addVcpkgPaths(self: *LibExeObjStep, linkage: LibExeObjStep.Linkage) !void { | |
| 1018 | // Ideally in the Unattempted case we would call the function recursively | |
| 1019 | // after findVcpkgRoot and have only one switch statement, but the compiler | |
| 1020 | // cannot resolve the error set. | |
| 1021 | switch (self.builder.vcpkg_root) { | |
| 1022 | .unattempted => { | |
| 1023 | self.builder.vcpkg_root = if (try findVcpkgRoot(self.builder.allocator)) |root| | |
| 1024 | VcpkgRoot{ .found = root } | |
| 1025 | else | |
| 1026 | .not_found; | |
| 1027 | }, | |
| 1028 | .not_found => return error.VcpkgNotFound, | |
| 1029 | .found => {}, | |
| 1030 | } | |
| 1031 | ||
| 1032 | switch (self.builder.vcpkg_root) { | |
| 1033 | .unattempted => unreachable, | |
| 1034 | .not_found => return error.VcpkgNotFound, | |
| 1035 | .found => |root| { | |
| 1036 | const allocator = self.builder.allocator; | |
| 1037 | const triplet = try self.target.vcpkgTriplet(allocator, if (linkage == .static) .Static else .Dynamic); | |
| 1038 | defer self.builder.allocator.free(triplet); | |
| 1039 | ||
| 1040 | const include_path = self.builder.pathJoin(&.{ root, "installed", triplet, "include" }); | |
| 1041 | errdefer allocator.free(include_path); | |
| 1042 | try self.include_dirs.append(IncludeDir{ .raw_path = include_path }); | |
| 1043 | ||
| 1044 | const lib_path = self.builder.pathJoin(&.{ root, "installed", triplet, "lib" }); | |
| 1045 | try self.lib_paths.append(lib_path); | |
| 1046 | ||
| 1047 | self.vcpkg_bin_path = self.builder.pathJoin(&.{ root, "installed", triplet, "bin" }); | |
| 1048 | }, | |
| 1049 | } | |
| 1050 | } | |
| 1051 | ||
| 1052 | pub fn setExecCmd(self: *LibExeObjStep, args: []const ?[]const u8) void { | |
| 1053 | assert(self.kind == .@"test"); | |
| 1054 | const duped_args = self.builder.allocator.alloc(?[]u8, args.len) catch unreachable; | |
| 1055 | for (args) |arg, i| { | |
| 1056 | duped_args[i] = if (arg) |a| self.builder.dupe(a) else null; | |
| 1057 | } | |
| 1058 | self.exec_cmd_args = duped_args; | |
| 1059 | } | |
| 1060 | ||
| 1061 | fn linkLibraryOrObject(self: *LibExeObjStep, other: *LibExeObjStep) void { | |
| 1062 | self.step.dependOn(&other.step); | |
| 1063 | self.link_objects.append(.{ .other_step = other }) catch unreachable; | |
| 1064 | self.include_dirs.append(.{ .other_step = other }) catch unreachable; | |
| 1065 | } | |
| 1066 | ||
| 1067 | fn makePackageCmd(self: *LibExeObjStep, pkg: Pkg, zig_args: *ArrayList([]const u8)) error{OutOfMemory}!void { | |
| 1068 | const builder = self.builder; | |
| 1069 | ||
| 1070 | try zig_args.append("--pkg-begin"); | |
| 1071 | try zig_args.append(pkg.name); | |
| 1072 | try zig_args.append(builder.pathFromRoot(pkg.source.getPath(self.builder))); | |
| 1073 | ||
| 1074 | if (pkg.dependencies) |dependencies| { | |
| 1075 | for (dependencies) |sub_pkg| { | |
| 1076 | try self.makePackageCmd(sub_pkg, zig_args); | |
| 1077 | } | |
| 1078 | } | |
| 1079 | ||
| 1080 | try zig_args.append("--pkg-end"); | |
| 1081 | } | |
| 1082 | ||
| 1083 | fn make(step: *Step) !void { | |
| 1084 | const self = @fieldParentPtr(LibExeObjStep, "step", step); | |
| 1085 | const builder = self.builder; | |
| 1086 | ||
| 1087 | if (self.root_src == null and self.link_objects.items.len == 0) { | |
| 1088 | log.err("{s}: linker needs 1 or more objects to link", .{self.step.name}); | |
| 1089 | return error.NeedAnObject; | |
| 1090 | } | |
| 1091 | ||
| 1092 | var zig_args = ArrayList([]const u8).init(builder.allocator); | |
| 1093 | defer zig_args.deinit(); | |
| 1094 | ||
| 1095 | zig_args.append(builder.zig_exe) catch unreachable; | |
| 1096 | ||
| 1097 | const cmd = switch (self.kind) { | |
| 1098 | .lib => "build-lib", | |
| 1099 | .exe => "build-exe", | |
| 1100 | .obj => "build-obj", | |
| 1101 | .@"test" => "test", | |
| 1102 | .test_exe => "test", | |
| 1103 | }; | |
| 1104 | zig_args.append(cmd) catch unreachable; | |
| 1105 | ||
| 1106 | if (builder.color != .auto) { | |
| 1107 | try zig_args.append("--color"); | |
| 1108 | try zig_args.append(@tagName(builder.color)); | |
| 1109 | } | |
| 1110 | ||
| 1111 | if (builder.reference_trace) |some| { | |
| 1112 | try zig_args.append(try std.fmt.allocPrint(builder.allocator, "-freference-trace={d}", .{some})); | |
| 1113 | } | |
| 1114 | ||
| 1115 | try addFlag(&zig_args, "LLVM", self.use_llvm); | |
| 1116 | try addFlag(&zig_args, "LLD", self.use_lld); | |
| 1117 | ||
| 1118 | if (self.target.ofmt) |ofmt| { | |
| 1119 | try zig_args.append(try std.fmt.allocPrint(builder.allocator, "-ofmt={s}", .{@tagName(ofmt)})); | |
| 1120 | } | |
| 1121 | ||
| 1122 | if (self.entry_symbol_name) |entry| { | |
| 1123 | try zig_args.append("--entry"); | |
| 1124 | try zig_args.append(entry); | |
| 1125 | } | |
| 1126 | ||
| 1127 | if (self.stack_size) |stack_size| { | |
| 1128 | try zig_args.append("--stack"); | |
| 1129 | try zig_args.append(try std.fmt.allocPrint(builder.allocator, "{}", .{stack_size})); | |
| 1130 | } | |
| 1131 | ||
| 1132 | if (self.root_src) |root_src| try zig_args.append(root_src.getPath(builder)); | |
| 1133 | ||
| 1134 | // We will add link objects from transitive dependencies, but we want to keep | |
| 1135 | // all link objects in the same order provided. | |
| 1136 | // This array is used to keep self.link_objects immutable. | |
| 1137 | var transitive_deps: TransitiveDeps = .{ | |
| 1138 | .link_objects = ArrayList(LinkObject).init(builder.allocator), | |
| 1139 | .seen_system_libs = StringHashMap(void).init(builder.allocator), | |
| 1140 | .seen_steps = std.AutoHashMap(*const Step, void).init(builder.allocator), | |
| 1141 | .is_linking_libcpp = self.is_linking_libcpp, | |
| 1142 | .is_linking_libc = self.is_linking_libc, | |
| 1143 | .frameworks = &self.frameworks, | |
| 1144 | }; | |
| 1145 | ||
| 1146 | try transitive_deps.seen_steps.put(&self.step, {}); | |
| 1147 | try transitive_deps.add(self.link_objects.items); | |
| 1148 | ||
| 1149 | var prev_has_extra_flags = false; | |
| 1150 | ||
| 1151 | for (transitive_deps.link_objects.items) |link_object| { | |
| 1152 | switch (link_object) { | |
| 1153 | .static_path => |static_path| try zig_args.append(static_path.getPath(builder)), | |
| 1154 | ||
| 1155 | .other_step => |other| switch (other.kind) { | |
| 1156 | .exe => @panic("Cannot link with an executable build artifact"), | |
| 1157 | .test_exe => @panic("Cannot link with an executable build artifact"), | |
| 1158 | .@"test" => @panic("Cannot link with a test"), | |
| 1159 | .obj => { | |
| 1160 | try zig_args.append(other.getOutputSource().getPath(builder)); | |
| 1161 | }, | |
| 1162 | .lib => l: { | |
| 1163 | if (self.isStaticLibrary() and other.isStaticLibrary()) { | |
| 1164 | // Avoid putting a static library inside a static library. | |
| 1165 | break :l; | |
| 1166 | } | |
| 1167 | ||
| 1168 | const full_path_lib = other.getOutputLibSource().getPath(builder); | |
| 1169 | try zig_args.append(full_path_lib); | |
| 1170 | ||
| 1171 | if (other.linkage == Linkage.dynamic and !self.target.isWindows()) { | |
| 1172 | if (fs.path.dirname(full_path_lib)) |dirname| { | |
| 1173 | try zig_args.append("-rpath"); | |
| 1174 | try zig_args.append(dirname); | |
| 1175 | } | |
| 1176 | } | |
| 1177 | }, | |
| 1178 | }, | |
| 1179 | ||
| 1180 | .system_lib => |system_lib| { | |
| 1181 | const prefix: []const u8 = prefix: { | |
| 1182 | if (system_lib.needed) break :prefix "-needed-l"; | |
| 1183 | if (system_lib.weak) { | |
| 1184 | if (self.target.isDarwin()) break :prefix "-weak-l"; | |
| 1185 | log.warn("Weak library import used for a non-darwin target, this will be converted to normally library import `-lname`", .{}); | |
| 1186 | } | |
| 1187 | break :prefix "-l"; | |
| 1188 | }; | |
| 1189 | switch (system_lib.use_pkg_config) { | |
| 1190 | .no => try zig_args.append(builder.fmt("{s}{s}", .{ prefix, system_lib.name })), | |
| 1191 | .yes, .force => { | |
| 1192 | if (self.runPkgConfig(system_lib.name)) |args| { | |
| 1193 | try zig_args.appendSlice(args); | |
| 1194 | } else |err| switch (err) { | |
| 1195 | error.PkgConfigInvalidOutput, | |
| 1196 | error.PkgConfigCrashed, | |
| 1197 | error.PkgConfigFailed, | |
| 1198 | error.PkgConfigNotInstalled, | |
| 1199 | error.PackageNotFound, | |
| 1200 | => switch (system_lib.use_pkg_config) { | |
| 1201 | .yes => { | |
| 1202 | // pkg-config failed, so fall back to linking the library | |
| 1203 | // by name directly. | |
| 1204 | try zig_args.append(builder.fmt("{s}{s}", .{ | |
| 1205 | prefix, | |
| 1206 | system_lib.name, | |
| 1207 | })); | |
| 1208 | }, | |
| 1209 | .force => { | |
| 1210 | panic("pkg-config failed for library {s}", .{system_lib.name}); | |
| 1211 | }, | |
| 1212 | .no => unreachable, | |
| 1213 | }, | |
| 1214 | ||
| 1215 | else => |e| return e, | |
| 1216 | } | |
| 1217 | }, | |
| 1218 | } | |
| 1219 | }, | |
| 1220 | ||
| 1221 | .assembly_file => |asm_file| { | |
| 1222 | if (prev_has_extra_flags) { | |
| 1223 | try zig_args.append("-extra-cflags"); | |
| 1224 | try zig_args.append("--"); | |
| 1225 | prev_has_extra_flags = false; | |
| 1226 | } | |
| 1227 | try zig_args.append(asm_file.getPath(builder)); | |
| 1228 | }, | |
| 1229 | ||
| 1230 | .c_source_file => |c_source_file| { | |
| 1231 | if (c_source_file.args.len == 0) { | |
| 1232 | if (prev_has_extra_flags) { | |
| 1233 | try zig_args.append("-cflags"); | |
| 1234 | try zig_args.append("--"); | |
| 1235 | prev_has_extra_flags = false; | |
| 1236 | } | |
| 1237 | } else { | |
| 1238 | try zig_args.append("-cflags"); | |
| 1239 | for (c_source_file.args) |arg| { | |
| 1240 | try zig_args.append(arg); | |
| 1241 | } | |
| 1242 | try zig_args.append("--"); | |
| 1243 | } | |
| 1244 | try zig_args.append(c_source_file.source.getPath(builder)); | |
| 1245 | }, | |
| 1246 | ||
| 1247 | .c_source_files => |c_source_files| { | |
| 1248 | if (c_source_files.flags.len == 0) { | |
| 1249 | if (prev_has_extra_flags) { | |
| 1250 | try zig_args.append("-cflags"); | |
| 1251 | try zig_args.append("--"); | |
| 1252 | prev_has_extra_flags = false; | |
| 1253 | } | |
| 1254 | } else { | |
| 1255 | try zig_args.append("-cflags"); | |
| 1256 | for (c_source_files.flags) |flag| { | |
| 1257 | try zig_args.append(flag); | |
| 1258 | } | |
| 1259 | try zig_args.append("--"); | |
| 1260 | } | |
| 1261 | for (c_source_files.files) |file| { | |
| 1262 | try zig_args.append(builder.pathFromRoot(file)); | |
| 1263 | } | |
| 1264 | }, | |
| 1265 | } | |
| 1266 | } | |
| 1267 | ||
| 1268 | if (transitive_deps.is_linking_libcpp) { | |
| 1269 | try zig_args.append("-lc++"); | |
| 1270 | } | |
| 1271 | ||
| 1272 | if (transitive_deps.is_linking_libc) { | |
| 1273 | try zig_args.append("-lc"); | |
| 1274 | } | |
| 1275 | ||
| 1276 | if (self.image_base) |image_base| { | |
| 1277 | try zig_args.append("--image-base"); | |
| 1278 | try zig_args.append(builder.fmt("0x{x}", .{image_base})); | |
| 1279 | } | |
| 1280 | ||
| 1281 | if (self.filter) |filter| { | |
| 1282 | try zig_args.append("--test-filter"); | |
| 1283 | try zig_args.append(filter); | |
| 1284 | } | |
| 1285 | ||
| 1286 | if (self.test_evented_io) { | |
| 1287 | try zig_args.append("--test-evented-io"); | |
| 1288 | } | |
| 1289 | ||
| 1290 | if (self.name_prefix.len != 0) { | |
| 1291 | try zig_args.append("--test-name-prefix"); | |
| 1292 | try zig_args.append(self.name_prefix); | |
| 1293 | } | |
| 1294 | ||
| 1295 | if (self.test_runner) |test_runner| { | |
| 1296 | try zig_args.append("--test-runner"); | |
| 1297 | try zig_args.append(builder.pathFromRoot(test_runner)); | |
| 1298 | } | |
| 1299 | ||
| 1300 | for (builder.debug_log_scopes) |log_scope| { | |
| 1301 | try zig_args.append("--debug-log"); | |
| 1302 | try zig_args.append(log_scope); | |
| 1303 | } | |
| 1304 | ||
| 1305 | if (builder.debug_compile_errors) { | |
| 1306 | try zig_args.append("--debug-compile-errors"); | |
| 1307 | } | |
| 1308 | ||
| 1309 | if (builder.verbose_cimport) zig_args.append("--verbose-cimport") catch unreachable; | |
| 1310 | if (builder.verbose_air) zig_args.append("--verbose-air") catch unreachable; | |
| 1311 | if (builder.verbose_llvm_ir) zig_args.append("--verbose-llvm-ir") catch unreachable; | |
| 1312 | if (builder.verbose_link or self.verbose_link) zig_args.append("--verbose-link") catch unreachable; | |
| 1313 | if (builder.verbose_cc or self.verbose_cc) zig_args.append("--verbose-cc") catch unreachable; | |
| 1314 | if (builder.verbose_llvm_cpu_features) zig_args.append("--verbose-llvm-cpu-features") catch unreachable; | |
| 1315 | ||
| 1316 | if (self.emit_analysis.getArg(builder, "emit-analysis")) |arg| try zig_args.append(arg); | |
| 1317 | if (self.emit_asm.getArg(builder, "emit-asm")) |arg| try zig_args.append(arg); | |
| 1318 | if (self.emit_bin.getArg(builder, "emit-bin")) |arg| try zig_args.append(arg); | |
| 1319 | if (self.emit_docs.getArg(builder, "emit-docs")) |arg| try zig_args.append(arg); | |
| 1320 | if (self.emit_implib.getArg(builder, "emit-implib")) |arg| try zig_args.append(arg); | |
| 1321 | if (self.emit_llvm_bc.getArg(builder, "emit-llvm-bc")) |arg| try zig_args.append(arg); | |
| 1322 | if (self.emit_llvm_ir.getArg(builder, "emit-llvm-ir")) |arg| try zig_args.append(arg); | |
| 1323 | ||
| 1324 | if (self.emit_h) try zig_args.append("-femit-h"); | |
| 1325 | ||
| 1326 | try addFlag(&zig_args, "strip", self.strip); | |
| 1327 | try addFlag(&zig_args, "unwind-tables", self.unwind_tables); | |
| 1328 | ||
| 1329 | switch (self.compress_debug_sections) { | |
| 1330 | .none => {}, | |
| 1331 | .zlib => try zig_args.append("--compress-debug-sections=zlib"), | |
| 1332 | } | |
| 1333 | ||
| 1334 | if (self.link_eh_frame_hdr) { | |
| 1335 | try zig_args.append("--eh-frame-hdr"); | |
| 1336 | } | |
| 1337 | if (self.link_emit_relocs) { | |
| 1338 | try zig_args.append("--emit-relocs"); | |
| 1339 | } | |
| 1340 | if (self.link_function_sections) { | |
| 1341 | try zig_args.append("-ffunction-sections"); | |
| 1342 | } | |
| 1343 | if (self.link_gc_sections) |x| { | |
| 1344 | try zig_args.append(if (x) "--gc-sections" else "--no-gc-sections"); | |
| 1345 | } | |
| 1346 | if (self.linker_allow_shlib_undefined) |x| { | |
| 1347 | try zig_args.append(if (x) "-fallow-shlib-undefined" else "-fno-allow-shlib-undefined"); | |
| 1348 | } | |
| 1349 | if (self.link_z_notext) { | |
| 1350 | try zig_args.append("-z"); | |
| 1351 | try zig_args.append("notext"); | |
| 1352 | } | |
| 1353 | if (!self.link_z_relro) { | |
| 1354 | try zig_args.append("-z"); | |
| 1355 | try zig_args.append("norelro"); | |
| 1356 | } | |
| 1357 | if (self.link_z_lazy) { | |
| 1358 | try zig_args.append("-z"); | |
| 1359 | try zig_args.append("lazy"); | |
| 1360 | } | |
| 1361 | if (self.link_z_common_page_size) |size| { | |
| 1362 | try zig_args.append("-z"); | |
| 1363 | try zig_args.append(builder.fmt("common-page-size={d}", .{size})); | |
| 1364 | } | |
| 1365 | if (self.link_z_max_page_size) |size| { | |
| 1366 | try zig_args.append("-z"); | |
| 1367 | try zig_args.append(builder.fmt("max-page-size={d}", .{size})); | |
| 1368 | } | |
| 1369 | ||
| 1370 | if (self.libc_file) |libc_file| { | |
| 1371 | try zig_args.append("--libc"); | |
| 1372 | try zig_args.append(libc_file.getPath(builder)); | |
| 1373 | } else if (builder.libc_file) |libc_file| { | |
| 1374 | try zig_args.append("--libc"); | |
| 1375 | try zig_args.append(libc_file); | |
| 1376 | } | |
| 1377 | ||
| 1378 | switch (self.build_mode) { | |
| 1379 | .Debug => {}, // Skip since it's the default. | |
| 1380 | else => zig_args.append(builder.fmt("-O{s}", .{@tagName(self.build_mode)})) catch unreachable, | |
| 1381 | } | |
| 1382 | ||
| 1383 | try zig_args.append("--cache-dir"); | |
| 1384 | try zig_args.append(builder.pathFromRoot(builder.cache_root)); | |
| 1385 | ||
| 1386 | try zig_args.append("--global-cache-dir"); | |
| 1387 | try zig_args.append(builder.pathFromRoot(builder.global_cache_root)); | |
| 1388 | ||
| 1389 | zig_args.append("--name") catch unreachable; | |
| 1390 | zig_args.append(self.name) catch unreachable; | |
| 1391 | ||
| 1392 | if (self.linkage) |some| switch (some) { | |
| 1393 | .dynamic => try zig_args.append("-dynamic"), | |
| 1394 | .static => try zig_args.append("-static"), | |
| 1395 | }; | |
| 1396 | if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic) { | |
| 1397 | if (self.version) |version| { | |
| 1398 | zig_args.append("--version") catch unreachable; | |
| 1399 | zig_args.append(builder.fmt("{}", .{version})) catch unreachable; | |
| 1400 | } | |
| 1401 | ||
| 1402 | if (self.target.isDarwin()) { | |
| 1403 | const install_name = self.install_name orelse builder.fmt("@rpath/{s}{s}{s}", .{ | |
| 1404 | self.target.libPrefix(), | |
| 1405 | self.name, | |
| 1406 | self.target.dynamicLibSuffix(), | |
| 1407 | }); | |
| 1408 | try zig_args.append("-install_name"); | |
| 1409 | try zig_args.append(install_name); | |
| 1410 | } | |
| 1411 | } | |
| 1412 | ||
| 1413 | if (self.entitlements) |entitlements| { | |
| 1414 | try zig_args.appendSlice(&[_][]const u8{ "--entitlements", entitlements }); | |
| 1415 | } | |
| 1416 | if (self.pagezero_size) |pagezero_size| { | |
| 1417 | const size = try std.fmt.allocPrint(builder.allocator, "{x}", .{pagezero_size}); | |
| 1418 | try zig_args.appendSlice(&[_][]const u8{ "-pagezero_size", size }); | |
| 1419 | } | |
| 1420 | if (self.search_strategy) |strat| switch (strat) { | |
| 1421 | .paths_first => try zig_args.append("-search_paths_first"), | |
| 1422 | .dylibs_first => try zig_args.append("-search_dylibs_first"), | |
| 1423 | }; | |
| 1424 | if (self.headerpad_size) |headerpad_size| { | |
| 1425 | const size = try std.fmt.allocPrint(builder.allocator, "{x}", .{headerpad_size}); | |
| 1426 | try zig_args.appendSlice(&[_][]const u8{ "-headerpad", size }); | |
| 1427 | } | |
| 1428 | if (self.headerpad_max_install_names) { | |
| 1429 | try zig_args.append("-headerpad_max_install_names"); | |
| 1430 | } | |
| 1431 | if (self.dead_strip_dylibs) { | |
| 1432 | try zig_args.append("-dead_strip_dylibs"); | |
| 1433 | } | |
| 1434 | ||
| 1435 | try addFlag(&zig_args, "compiler-rt", self.bundle_compiler_rt); | |
| 1436 | try addFlag(&zig_args, "single-threaded", self.single_threaded); | |
| 1437 | if (self.disable_stack_probing) { | |
| 1438 | try zig_args.append("-fno-stack-check"); | |
| 1439 | } | |
| 1440 | try addFlag(&zig_args, "stack-protector", self.stack_protector); | |
| 1441 | if (self.red_zone) |red_zone| { | |
| 1442 | if (red_zone) { | |
| 1443 | try zig_args.append("-mred-zone"); | |
| 1444 | } else { | |
| 1445 | try zig_args.append("-mno-red-zone"); | |
| 1446 | } | |
| 1447 | } | |
| 1448 | try addFlag(&zig_args, "omit-frame-pointer", self.omit_frame_pointer); | |
| 1449 | try addFlag(&zig_args, "dll-export-fns", self.dll_export_fns); | |
| 1450 | ||
| 1451 | if (self.disable_sanitize_c) { | |
| 1452 | try zig_args.append("-fno-sanitize-c"); | |
| 1453 | } | |
| 1454 | if (self.sanitize_thread) { | |
| 1455 | try zig_args.append("-fsanitize-thread"); | |
| 1456 | } | |
| 1457 | if (self.rdynamic) { | |
| 1458 | try zig_args.append("-rdynamic"); | |
| 1459 | } | |
| 1460 | if (self.import_memory) { | |
| 1461 | try zig_args.append("--import-memory"); | |
| 1462 | } | |
| 1463 | if (self.import_symbols) { | |
| 1464 | try zig_args.append("--import-symbols"); | |
| 1465 | } | |
| 1466 | if (self.import_table) { | |
| 1467 | try zig_args.append("--import-table"); | |
| 1468 | } | |
| 1469 | if (self.export_table) { | |
| 1470 | try zig_args.append("--export-table"); | |
| 1471 | } | |
| 1472 | if (self.initial_memory) |initial_memory| { | |
| 1473 | try zig_args.append(builder.fmt("--initial-memory={d}", .{initial_memory})); | |
| 1474 | } | |
| 1475 | if (self.max_memory) |max_memory| { | |
| 1476 | try zig_args.append(builder.fmt("--max-memory={d}", .{max_memory})); | |
| 1477 | } | |
| 1478 | if (self.shared_memory) { | |
| 1479 | try zig_args.append("--shared-memory"); | |
| 1480 | } | |
| 1481 | if (self.global_base) |global_base| { | |
| 1482 | try zig_args.append(builder.fmt("--global-base={d}", .{global_base})); | |
| 1483 | } | |
| 1484 | ||
| 1485 | if (self.code_model != .default) { | |
| 1486 | try zig_args.append("-mcmodel"); | |
| 1487 | try zig_args.append(@tagName(self.code_model)); | |
| 1488 | } | |
| 1489 | if (self.wasi_exec_model) |model| { | |
| 1490 | try zig_args.append(builder.fmt("-mexec-model={s}", .{@tagName(model)})); | |
| 1491 | } | |
| 1492 | for (self.export_symbol_names) |symbol_name| { | |
| 1493 | try zig_args.append(builder.fmt("--export={s}", .{symbol_name})); | |
| 1494 | } | |
| 1495 | ||
| 1496 | if (!self.target.isNative()) { | |
| 1497 | try zig_args.append("-target"); | |
| 1498 | try zig_args.append(try self.target.zigTriple(builder.allocator)); | |
| 1499 | ||
| 1500 | // TODO this logic can disappear if cpu model + features becomes part of the target triple | |
| 1501 | const cross = self.target.toTarget(); | |
| 1502 | const all_features = cross.cpu.arch.allFeaturesList(); | |
| 1503 | var populated_cpu_features = cross.cpu.model.features; | |
| 1504 | populated_cpu_features.populateDependencies(all_features); | |
| 1505 | ||
| 1506 | if (populated_cpu_features.eql(cross.cpu.features)) { | |
| 1507 | // The CPU name alone is sufficient. | |
| 1508 | try zig_args.append("-mcpu"); | |
| 1509 | try zig_args.append(cross.cpu.model.name); | |
| 1510 | } else { | |
| 1511 | var mcpu_buffer = ArrayList(u8).init(builder.allocator); | |
| 1512 | ||
| 1513 | try mcpu_buffer.writer().print("-mcpu={s}", .{cross.cpu.model.name}); | |
| 1514 | ||
| 1515 | for (all_features) |feature, i_usize| { | |
| 1516 | const i = @intCast(std.Target.Cpu.Feature.Set.Index, i_usize); | |
| 1517 | const in_cpu_set = populated_cpu_features.isEnabled(i); | |
| 1518 | const in_actual_set = cross.cpu.features.isEnabled(i); | |
| 1519 | if (in_cpu_set and !in_actual_set) { | |
| 1520 | try mcpu_buffer.writer().print("-{s}", .{feature.name}); | |
| 1521 | } else if (!in_cpu_set and in_actual_set) { | |
| 1522 | try mcpu_buffer.writer().print("+{s}", .{feature.name}); | |
| 1523 | } | |
| 1524 | } | |
| 1525 | ||
| 1526 | try zig_args.append(try mcpu_buffer.toOwnedSlice()); | |
| 1527 | } | |
| 1528 | ||
| 1529 | if (self.target.dynamic_linker.get()) |dynamic_linker| { | |
| 1530 | try zig_args.append("--dynamic-linker"); | |
| 1531 | try zig_args.append(dynamic_linker); | |
| 1532 | } | |
| 1533 | } | |
| 1534 | ||
| 1535 | if (self.linker_script) |linker_script| { | |
| 1536 | try zig_args.append("--script"); | |
| 1537 | try zig_args.append(linker_script.getPath(builder)); | |
| 1538 | } | |
| 1539 | ||
| 1540 | if (self.version_script) |version_script| { | |
| 1541 | try zig_args.append("--version-script"); | |
| 1542 | try zig_args.append(builder.pathFromRoot(version_script)); | |
| 1543 | } | |
| 1544 | ||
| 1545 | if (self.kind == .@"test") { | |
| 1546 | if (self.exec_cmd_args) |exec_cmd_args| { | |
| 1547 | for (exec_cmd_args) |cmd_arg| { | |
| 1548 | if (cmd_arg) |arg| { | |
| 1549 | try zig_args.append("--test-cmd"); | |
| 1550 | try zig_args.append(arg); | |
| 1551 | } else { | |
| 1552 | try zig_args.append("--test-cmd-bin"); | |
| 1553 | } | |
| 1554 | } | |
| 1555 | } else { | |
| 1556 | const need_cross_glibc = self.target.isGnuLibC() and transitive_deps.is_linking_libc; | |
| 1557 | ||
| 1558 | switch (builder.host.getExternalExecutor(self.target_info, .{ | |
| 1559 | .qemu_fixes_dl = need_cross_glibc and builder.glibc_runtimes_dir != null, | |
| 1560 | .link_libc = transitive_deps.is_linking_libc, | |
| 1561 | })) { | |
| 1562 | .native => {}, | |
| 1563 | .bad_dl, .bad_os_or_cpu => { | |
| 1564 | try zig_args.append("--test-no-exec"); | |
| 1565 | }, | |
| 1566 | .rosetta => if (builder.enable_rosetta) { | |
| 1567 | try zig_args.append("--test-cmd-bin"); | |
| 1568 | } else { | |
| 1569 | try zig_args.append("--test-no-exec"); | |
| 1570 | }, | |
| 1571 | .qemu => |bin_name| ok: { | |
| 1572 | if (builder.enable_qemu) qemu: { | |
| 1573 | const glibc_dir_arg = if (need_cross_glibc) | |
| 1574 | builder.glibc_runtimes_dir orelse break :qemu | |
| 1575 | else | |
| 1576 | null; | |
| 1577 | try zig_args.append("--test-cmd"); | |
| 1578 | try zig_args.append(bin_name); | |
| 1579 | if (glibc_dir_arg) |dir| { | |
| 1580 | // TODO look into making this a call to `linuxTriple`. This | |
| 1581 | // needs the directory to be called "i686" rather than | |
| 1582 | // "x86" which is why we do it manually here. | |
| 1583 | const fmt_str = "{s}" ++ fs.path.sep_str ++ "{s}-{s}-{s}"; | |
| 1584 | const cpu_arch = self.target.getCpuArch(); | |
| 1585 | const os_tag = self.target.getOsTag(); | |
| 1586 | const abi = self.target.getAbi(); | |
| 1587 | const cpu_arch_name: []const u8 = if (cpu_arch == .x86) | |
| 1588 | "i686" | |
| 1589 | else | |
| 1590 | @tagName(cpu_arch); | |
| 1591 | const full_dir = try std.fmt.allocPrint(builder.allocator, fmt_str, .{ | |
| 1592 | dir, cpu_arch_name, @tagName(os_tag), @tagName(abi), | |
| 1593 | }); | |
| 1594 | ||
| 1595 | try zig_args.append("--test-cmd"); | |
| 1596 | try zig_args.append("-L"); | |
| 1597 | try zig_args.append("--test-cmd"); | |
| 1598 | try zig_args.append(full_dir); | |
| 1599 | } | |
| 1600 | try zig_args.append("--test-cmd-bin"); | |
| 1601 | break :ok; | |
| 1602 | } | |
| 1603 | try zig_args.append("--test-no-exec"); | |
| 1604 | }, | |
| 1605 | .wine => |bin_name| if (builder.enable_wine) { | |
| 1606 | try zig_args.append("--test-cmd"); | |
| 1607 | try zig_args.append(bin_name); | |
| 1608 | try zig_args.append("--test-cmd-bin"); | |
| 1609 | } else { | |
| 1610 | try zig_args.append("--test-no-exec"); | |
| 1611 | }, | |
| 1612 | .wasmtime => |bin_name| if (builder.enable_wasmtime) { | |
| 1613 | try zig_args.append("--test-cmd"); | |
| 1614 | try zig_args.append(bin_name); | |
| 1615 | try zig_args.append("--test-cmd"); | |
| 1616 | try zig_args.append("--dir=."); | |
| 1617 | try zig_args.append("--test-cmd-bin"); | |
| 1618 | } else { | |
| 1619 | try zig_args.append("--test-no-exec"); | |
| 1620 | }, | |
| 1621 | .darling => |bin_name| if (builder.enable_darling) { | |
| 1622 | try zig_args.append("--test-cmd"); | |
| 1623 | try zig_args.append(bin_name); | |
| 1624 | try zig_args.append("--test-cmd-bin"); | |
| 1625 | } else { | |
| 1626 | try zig_args.append("--test-no-exec"); | |
| 1627 | }, | |
| 1628 | } | |
| 1629 | } | |
| 1630 | } else if (self.kind == .test_exe) { | |
| 1631 | try zig_args.append("--test-no-exec"); | |
| 1632 | } | |
| 1633 | ||
| 1634 | for (self.packages.items) |pkg| { | |
| 1635 | try self.makePackageCmd(pkg, &zig_args); | |
| 1636 | } | |
| 1637 | ||
| 1638 | for (self.include_dirs.items) |include_dir| { | |
| 1639 | switch (include_dir) { | |
| 1640 | .raw_path => |include_path| { | |
| 1641 | try zig_args.append("-I"); | |
| 1642 | try zig_args.append(builder.pathFromRoot(include_path)); | |
| 1643 | }, | |
| 1644 | .raw_path_system => |include_path| { | |
| 1645 | if (builder.sysroot != null) { | |
| 1646 | try zig_args.append("-iwithsysroot"); | |
| 1647 | } else { | |
| 1648 | try zig_args.append("-isystem"); | |
| 1649 | } | |
| 1650 | ||
| 1651 | const resolved_include_path = builder.pathFromRoot(include_path); | |
| 1652 | ||
| 1653 | const common_include_path = if (builtin.os.tag == .windows and builder.sysroot != null and fs.path.isAbsolute(resolved_include_path)) blk: { | |
| 1654 | // We need to check for disk designator and strip it out from dir path so | |
| 1655 | // that zig/clang can concat resolved_include_path with sysroot. | |
| 1656 | const disk_designator = fs.path.diskDesignatorWindows(resolved_include_path); | |
| 1657 | ||
| 1658 | if (mem.indexOf(u8, resolved_include_path, disk_designator)) |where| { | |
| 1659 | break :blk resolved_include_path[where + disk_designator.len ..]; | |
| 1660 | } | |
| 1661 | ||
| 1662 | break :blk resolved_include_path; | |
| 1663 | } else resolved_include_path; | |
| 1664 | ||
| 1665 | try zig_args.append(common_include_path); | |
| 1666 | }, | |
| 1667 | .other_step => |other| { | |
| 1668 | if (other.emit_h) { | |
| 1669 | const h_path = other.getOutputHSource().getPath(builder); | |
| 1670 | try zig_args.append("-isystem"); | |
| 1671 | try zig_args.append(fs.path.dirname(h_path).?); | |
| 1672 | } | |
| 1673 | if (other.installed_headers.items.len > 0) { | |
| 1674 | for (other.installed_headers.items) |install_step| { | |
| 1675 | try install_step.make(); | |
| 1676 | } | |
| 1677 | try zig_args.append("-I"); | |
| 1678 | try zig_args.append(builder.pathJoin(&.{ | |
| 1679 | other.builder.install_prefix, "include", | |
| 1680 | })); | |
| 1681 | } | |
| 1682 | }, | |
| 1683 | .config_header_step => |config_header| { | |
| 1684 | try zig_args.append("-I"); | |
| 1685 | try zig_args.append(config_header.output_dir); | |
| 1686 | }, | |
| 1687 | } | |
| 1688 | } | |
| 1689 | ||
| 1690 | for (self.lib_paths.items) |lib_path| { | |
| 1691 | try zig_args.append("-L"); | |
| 1692 | try zig_args.append(lib_path); | |
| 1693 | } | |
| 1694 | ||
| 1695 | for (self.rpaths.items) |rpath| { | |
| 1696 | try zig_args.append("-rpath"); | |
| 1697 | try zig_args.append(rpath); | |
| 1698 | } | |
| 1699 | ||
| 1700 | for (self.c_macros.items) |c_macro| { | |
| 1701 | try zig_args.append("-D"); | |
| 1702 | try zig_args.append(c_macro); | |
| 1703 | } | |
| 1704 | ||
| 1705 | if (self.target.isDarwin()) { | |
| 1706 | for (self.framework_dirs.items) |dir| { | |
| 1707 | if (builder.sysroot != null) { | |
| 1708 | try zig_args.append("-iframeworkwithsysroot"); | |
| 1709 | } else { | |
| 1710 | try zig_args.append("-iframework"); | |
| 1711 | } | |
| 1712 | try zig_args.append(dir); | |
| 1713 | try zig_args.append("-F"); | |
| 1714 | try zig_args.append(dir); | |
| 1715 | } | |
| 1716 | ||
| 1717 | var it = self.frameworks.iterator(); | |
| 1718 | while (it.next()) |entry| { | |
| 1719 | const name = entry.key_ptr.*; | |
| 1720 | const info = entry.value_ptr.*; | |
| 1721 | if (info.needed) { | |
| 1722 | zig_args.append("-needed_framework") catch unreachable; | |
| 1723 | } else if (info.weak) { | |
| 1724 | zig_args.append("-weak_framework") catch unreachable; | |
| 1725 | } else { | |
| 1726 | zig_args.append("-framework") catch unreachable; | |
| 1727 | } | |
| 1728 | zig_args.append(name) catch unreachable; | |
| 1729 | } | |
| 1730 | } else { | |
| 1731 | if (self.framework_dirs.items.len > 0) { | |
| 1732 | log.info("Framework directories have been added for a non-darwin target, this will have no affect on the build", .{}); | |
| 1733 | } | |
| 1734 | ||
| 1735 | if (self.frameworks.count() > 0) { | |
| 1736 | log.info("Frameworks have been added for a non-darwin target, this will have no affect on the build", .{}); | |
| 1737 | } | |
| 1738 | } | |
| 1739 | ||
| 1740 | if (builder.sysroot) |sysroot| { | |
| 1741 | try zig_args.appendSlice(&[_][]const u8{ "--sysroot", sysroot }); | |
| 1742 | } | |
| 1743 | ||
| 1744 | for (builder.search_prefixes.items) |search_prefix| { | |
| 1745 | try zig_args.append("-L"); | |
| 1746 | try zig_args.append(builder.pathJoin(&.{ | |
| 1747 | search_prefix, "lib", | |
| 1748 | })); | |
| 1749 | try zig_args.append("-I"); | |
| 1750 | try zig_args.append(builder.pathJoin(&.{ | |
| 1751 | search_prefix, "include", | |
| 1752 | })); | |
| 1753 | } | |
| 1754 | ||
| 1755 | try addFlag(&zig_args, "valgrind", self.valgrind_support); | |
| 1756 | try addFlag(&zig_args, "each-lib-rpath", self.each_lib_rpath); | |
| 1757 | try addFlag(&zig_args, "build-id", self.build_id); | |
| 1758 | ||
| 1759 | if (self.override_lib_dir) |dir| { | |
| 1760 | try zig_args.append("--zig-lib-dir"); | |
| 1761 | try zig_args.append(builder.pathFromRoot(dir)); | |
| 1762 | } else if (builder.override_lib_dir) |dir| { | |
| 1763 | try zig_args.append("--zig-lib-dir"); | |
| 1764 | try zig_args.append(builder.pathFromRoot(dir)); | |
| 1765 | } | |
| 1766 | ||
| 1767 | if (self.main_pkg_path) |dir| { | |
| 1768 | try zig_args.append("--main-pkg-path"); | |
| 1769 | try zig_args.append(builder.pathFromRoot(dir)); | |
| 1770 | } | |
| 1771 | ||
| 1772 | try addFlag(&zig_args, "PIC", self.force_pic); | |
| 1773 | try addFlag(&zig_args, "PIE", self.pie); | |
| 1774 | try addFlag(&zig_args, "lto", self.want_lto); | |
| 1775 | ||
| 1776 | if (self.subsystem) |subsystem| { | |
| 1777 | try zig_args.append("--subsystem"); | |
| 1778 | try zig_args.append(switch (subsystem) { | |
| 1779 | .Console => "console", | |
| 1780 | .Windows => "windows", | |
| 1781 | .Posix => "posix", | |
| 1782 | .Native => "native", | |
| 1783 | .EfiApplication => "efi_application", | |
| 1784 | .EfiBootServiceDriver => "efi_boot_service_driver", | |
| 1785 | .EfiRom => "efi_rom", | |
| 1786 | .EfiRuntimeDriver => "efi_runtime_driver", | |
| 1787 | }); | |
| 1788 | } | |
| 1789 | ||
| 1790 | try zig_args.append("--enable-cache"); | |
| 1791 | ||
| 1792 | // Windows has an argument length limit of 32,766 characters, macOS 262,144 and Linux | |
| 1793 | // 2,097,152. If our args exceed 30 KiB, we instead write them to a "response file" and | |
| 1794 | // pass that to zig, e.g. via 'zig build-lib @args.rsp' | |
| 1795 | // See @file syntax here: https://gcc.gnu.org/onlinedocs/gcc/Overall-Options.html | |
| 1796 | var args_length: usize = 0; | |
| 1797 | for (zig_args.items) |arg| { | |
| 1798 | args_length += arg.len + 1; // +1 to account for null terminator | |
| 1799 | } | |
| 1800 | if (args_length >= 30 * 1024) { | |
| 1801 | const args_dir = try fs.path.join( | |
| 1802 | builder.allocator, | |
| 1803 | &[_][]const u8{ builder.pathFromRoot("zig-cache"), "args" }, | |
| 1804 | ); | |
| 1805 | try std.fs.cwd().makePath(args_dir); | |
| 1806 | ||
| 1807 | var args_arena = std.heap.ArenaAllocator.init(builder.allocator); | |
| 1808 | defer args_arena.deinit(); | |
| 1809 | ||
| 1810 | const args_to_escape = zig_args.items[2..]; | |
| 1811 | var escaped_args = try ArrayList([]const u8).initCapacity(args_arena.allocator(), args_to_escape.len); | |
| 1812 | ||
| 1813 | arg_blk: for (args_to_escape) |arg| { | |
| 1814 | for (arg) |c, arg_idx| { | |
| 1815 | if (c == '\\' or c == '"') { | |
| 1816 | // Slow path for arguments that need to be escaped. We'll need to allocate and copy | |
| 1817 | var escaped = try ArrayList(u8).initCapacity(args_arena.allocator(), arg.len + 1); | |
| 1818 | const writer = escaped.writer(); | |
| 1819 | writer.writeAll(arg[0..arg_idx]) catch unreachable; | |
| 1820 | for (arg[arg_idx..]) |to_escape| { | |
| 1821 | if (to_escape == '\\' or to_escape == '"') try writer.writeByte('\\'); | |
| 1822 | try writer.writeByte(to_escape); | |
| 1823 | } | |
| 1824 | escaped_args.appendAssumeCapacity(escaped.items); | |
| 1825 | continue :arg_blk; | |
| 1826 | } | |
| 1827 | } | |
| 1828 | escaped_args.appendAssumeCapacity(arg); // no escaping needed so just use original argument | |
| 1829 | } | |
| 1830 | ||
| 1831 | // Write the args to zig-cache/args/<SHA256 hash of args> to avoid conflicts with | |
| 1832 | // other zig build commands running in parallel. | |
| 1833 | const partially_quoted = try std.mem.join(builder.allocator, "\" \"", escaped_args.items); | |
| 1834 | const args = try std.mem.concat(builder.allocator, u8, &[_][]const u8{ "\"", partially_quoted, "\"" }); | |
| 1835 | ||
| 1836 | var args_hash: [Sha256.digest_length]u8 = undefined; | |
| 1837 | Sha256.hash(args, &args_hash, .{}); | |
| 1838 | var args_hex_hash: [Sha256.digest_length * 2]u8 = undefined; | |
| 1839 | _ = try std.fmt.bufPrint( | |
| 1840 | &args_hex_hash, | |
| 1841 | "{s}", | |
| 1842 | .{std.fmt.fmtSliceHexLower(&args_hash)}, | |
| 1843 | ); | |
| 1844 | ||
| 1845 | const args_file = try fs.path.join(builder.allocator, &[_][]const u8{ args_dir, args_hex_hash[0..] }); | |
| 1846 | try std.fs.cwd().writeFile(args_file, args); | |
| 1847 | ||
| 1848 | zig_args.shrinkRetainingCapacity(2); | |
| 1849 | try zig_args.append(try std.mem.concat(builder.allocator, u8, &[_][]const u8{ "@", args_file })); | |
| 1850 | } | |
| 1851 | ||
| 1852 | const output_dir_nl = try builder.execFromStep(zig_args.items, &self.step); | |
| 1853 | const build_output_dir = mem.trimRight(u8, output_dir_nl, "\r\n"); | |
| 1854 | ||
| 1855 | if (self.output_dir) |output_dir| { | |
| 1856 | var src_dir = try std.fs.cwd().openIterableDir(build_output_dir, .{}); | |
| 1857 | defer src_dir.close(); | |
| 1858 | ||
| 1859 | // Create the output directory if it doesn't exist. | |
| 1860 | try std.fs.cwd().makePath(output_dir); | |
| 1861 | ||
| 1862 | var dest_dir = try std.fs.cwd().openDir(output_dir, .{}); | |
| 1863 | defer dest_dir.close(); | |
| 1864 | ||
| 1865 | var it = src_dir.iterate(); | |
| 1866 | while (try it.next()) |entry| { | |
| 1867 | // The compiler can put these files into the same directory, but we don't | |
| 1868 | // want to copy them over. | |
| 1869 | if (mem.eql(u8, entry.name, "llvm-ar.id") or | |
| 1870 | mem.eql(u8, entry.name, "libs.txt") or | |
| 1871 | mem.eql(u8, entry.name, "builtin.zig") or | |
| 1872 | mem.eql(u8, entry.name, "zld.id") or | |
| 1873 | mem.eql(u8, entry.name, "lld.id")) continue; | |
| 1874 | ||
| 1875 | _ = try src_dir.dir.updateFile(entry.name, dest_dir, entry.name, .{}); | |
| 1876 | } | |
| 1877 | } else { | |
| 1878 | self.output_dir = build_output_dir; | |
| 1879 | } | |
| 1880 | ||
| 1881 | // This will ensure all output filenames will now have the output_dir available! | |
| 1882 | self.computeOutFileNames(); | |
| 1883 | ||
| 1884 | // Update generated files | |
| 1885 | if (self.output_dir != null) { | |
| 1886 | self.output_path_source.path = builder.pathJoin( | |
| 1887 | &.{ self.output_dir.?, self.out_filename }, | |
| 1888 | ); | |
| 1889 | ||
| 1890 | if (self.emit_h) { | |
| 1891 | self.output_h_path_source.path = builder.pathJoin( | |
| 1892 | &.{ self.output_dir.?, self.out_h_filename }, | |
| 1893 | ); | |
| 1894 | } | |
| 1895 | ||
| 1896 | if (self.target.isWindows() or self.target.isUefi()) { | |
| 1897 | self.output_pdb_path_source.path = builder.pathJoin( | |
| 1898 | &.{ self.output_dir.?, self.out_pdb_filename }, | |
| 1899 | ); | |
| 1900 | } | |
| 1901 | } | |
| 1902 | ||
| 1903 | if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic and self.version != null and self.target.wantSharedLibSymLinks()) { | |
| 1904 | try doAtomicSymLinks(builder.allocator, self.getOutputSource().getPath(builder), self.major_only_filename.?, self.name_only_filename.?); | |
| 1905 | } | |
| 1906 | } | |
| 1907 | ||
| 1908 | fn isLibCLibrary(name: []const u8) bool { | |
| 1909 | const libc_libraries = [_][]const u8{ "c", "m", "dl", "rt", "pthread" }; | |
| 1910 | for (libc_libraries) |libc_lib_name| { | |
| 1911 | if (mem.eql(u8, name, libc_lib_name)) | |
| 1912 | return true; | |
| 1913 | } | |
| 1914 | return false; | |
| 1915 | } | |
| 1916 | ||
| 1917 | fn isLibCppLibrary(name: []const u8) bool { | |
| 1918 | const libcpp_libraries = [_][]const u8{ "c++", "stdc++" }; | |
| 1919 | for (libcpp_libraries) |libcpp_lib_name| { | |
| 1920 | if (mem.eql(u8, name, libcpp_lib_name)) | |
| 1921 | return true; | |
| 1922 | } | |
| 1923 | return false; | |
| 1924 | } | |
| 1925 | ||
| 1926 | /// Returned slice must be freed by the caller. | |
| 1927 | fn findVcpkgRoot(allocator: Allocator) !?[]const u8 { | |
| 1928 | const appdata_path = try fs.getAppDataDir(allocator, "vcpkg"); | |
| 1929 | defer allocator.free(appdata_path); | |
| 1930 | ||
| 1931 | const path_file = try fs.path.join(allocator, &[_][]const u8{ appdata_path, "vcpkg.path.txt" }); | |
| 1932 | defer allocator.free(path_file); | |
| 1933 | ||
| 1934 | const file = fs.cwd().openFile(path_file, .{}) catch return null; | |
| 1935 | defer file.close(); | |
| 1936 | ||
| 1937 | const size = @intCast(usize, try file.getEndPos()); | |
| 1938 | const vcpkg_path = try allocator.alloc(u8, size); | |
| 1939 | const size_read = try file.read(vcpkg_path); | |
| 1940 | std.debug.assert(size == size_read); | |
| 1941 | ||
| 1942 | return vcpkg_path; | |
| 1943 | } | |
| 1944 | ||
| 1945 | pub fn doAtomicSymLinks(allocator: Allocator, output_path: []const u8, filename_major_only: []const u8, filename_name_only: []const u8) !void { | |
| 1946 | const out_dir = fs.path.dirname(output_path) orelse "."; | |
| 1947 | const out_basename = fs.path.basename(output_path); | |
| 1948 | // sym link for libfoo.so.1 to libfoo.so.1.2.3 | |
| 1949 | const major_only_path = fs.path.join( | |
| 1950 | allocator, | |
| 1951 | &[_][]const u8{ out_dir, filename_major_only }, | |
| 1952 | ) catch unreachable; | |
| 1953 | fs.atomicSymLink(allocator, out_basename, major_only_path) catch |err| { | |
| 1954 | log.err("Unable to symlink {s} -> {s}", .{ major_only_path, out_basename }); | |
| 1955 | return err; | |
| 1956 | }; | |
| 1957 | // sym link for libfoo.so to libfoo.so.1 | |
| 1958 | const name_only_path = fs.path.join( | |
| 1959 | allocator, | |
| 1960 | &[_][]const u8{ out_dir, filename_name_only }, | |
| 1961 | ) catch unreachable; | |
| 1962 | fs.atomicSymLink(allocator, filename_major_only, name_only_path) catch |err| { | |
| 1963 | log.err("Unable to symlink {s} -> {s}", .{ name_only_path, filename_major_only }); | |
| 1964 | return err; | |
| 1965 | }; | |
| 1966 | } | |
| 1967 | ||
| 1968 | fn execPkgConfigList(self: *Builder, out_code: *u8) (PkgConfigError || ExecError)![]const PkgConfigPkg { | |
| 1969 | const stdout = try self.execAllowFail(&[_][]const u8{ "pkg-config", "--list-all" }, out_code, .Ignore); | |
| 1970 | var list = ArrayList(PkgConfigPkg).init(self.allocator); | |
| 1971 | errdefer list.deinit(); | |
| 1972 | var line_it = mem.tokenize(u8, stdout, "\r\n"); | |
| 1973 | while (line_it.next()) |line| { | |
| 1974 | if (mem.trim(u8, line, " \t").len == 0) continue; | |
| 1975 | var tok_it = mem.tokenize(u8, line, " \t"); | |
| 1976 | try list.append(PkgConfigPkg{ | |
| 1977 | .name = tok_it.next() orelse return error.PkgConfigInvalidOutput, | |
| 1978 | .desc = tok_it.rest(), | |
| 1979 | }); | |
| 1980 | } | |
| 1981 | return list.toOwnedSlice(); | |
| 1982 | } | |
| 1983 | ||
| 1984 | fn getPkgConfigList(self: *Builder) ![]const PkgConfigPkg { | |
| 1985 | if (self.pkg_config_pkg_list) |res| { | |
| 1986 | return res; | |
| 1987 | } | |
| 1988 | var code: u8 = undefined; | |
| 1989 | if (execPkgConfigList(self, &code)) |list| { | |
| 1990 | self.pkg_config_pkg_list = list; | |
| 1991 | return list; | |
| 1992 | } else |err| { | |
| 1993 | const result = switch (err) { | |
| 1994 | error.ProcessTerminated => error.PkgConfigCrashed, | |
| 1995 | error.ExecNotSupported => error.PkgConfigFailed, | |
| 1996 | error.ExitCodeFailure => error.PkgConfigFailed, | |
| 1997 | error.FileNotFound => error.PkgConfigNotInstalled, | |
| 1998 | error.InvalidName => error.PkgConfigNotInstalled, | |
| 1999 | error.PkgConfigInvalidOutput => error.PkgConfigInvalidOutput, | |
| 2000 | error.ChildExecFailed => error.PkgConfigFailed, | |
| 2001 | else => return err, | |
| 2002 | }; | |
| 2003 | self.pkg_config_pkg_list = result; | |
| 2004 | return result; | |
| 2005 | } | |
| 2006 | } | |
| 2007 | ||
| 2008 | test "addPackage" { | |
| 2009 | if (builtin.os.tag == .wasi) return error.SkipZigTest; | |
| 2010 | ||
| 2011 | var arena = std.heap.ArenaAllocator.init(std.testing.allocator); | |
| 2012 | defer arena.deinit(); | |
| 2013 | ||
| 2014 | var builder = try Builder.create( | |
| 2015 | arena.allocator(), | |
| 2016 | "test", | |
| 2017 | "test", | |
| 2018 | "test", | |
| 2019 | "test", | |
| 2020 | ); | |
| 2021 | defer builder.destroy(); | |
| 2022 | ||
| 2023 | const pkg_dep = Pkg{ | |
| 2024 | .name = "pkg_dep", | |
| 2025 | .source = .{ .path = "/not/a/pkg_dep.zig" }, | |
| 2026 | }; | |
| 2027 | const pkg_top = Pkg{ | |
| 2028 | .name = "pkg_dep", | |
| 2029 | .source = .{ .path = "/not/a/pkg_top.zig" }, | |
| 2030 | .dependencies = &[_]Pkg{pkg_dep}, | |
| 2031 | }; | |
| 2032 | ||
| 2033 | var exe = builder.addExecutable("not_an_executable", "/not/an/executable.zig"); | |
| 2034 | exe.addPackage(pkg_top); | |
| 2035 | ||
| 2036 | try std.testing.expectEqual(@as(usize, 1), exe.packages.items.len); | |
| 2037 | ||
| 2038 | const dupe = exe.packages.items[0]; | |
| 2039 | try std.testing.expectEqualStrings(pkg_top.name, dupe.name); | |
| 2040 | } | |
| 2041 | ||
| 2042 | fn addFlag(args: *ArrayList([]const u8), comptime name: []const u8, opt: ?bool) !void { | |
| 2043 | const cond = opt orelse return; | |
| 2044 | try args.ensureUnusedCapacity(1); | |
| 2045 | if (cond) { | |
| 2046 | args.appendAssumeCapacity("-f" ++ name); | |
| 2047 | } else { | |
| 2048 | args.appendAssumeCapacity("-fno-" ++ name); | |
| 2049 | } | |
| 2050 | } | |
| 2051 | ||
| 2052 | const TransitiveDeps = struct { | |
| 2053 | link_objects: ArrayList(LinkObject), | |
| 2054 | seen_system_libs: StringHashMap(void), | |
| 2055 | seen_steps: std.AutoHashMap(*const Step, void), | |
| 2056 | is_linking_libcpp: bool, | |
| 2057 | is_linking_libc: bool, | |
| 2058 | frameworks: *StringHashMap(FrameworkLinkInfo), | |
| 2059 | ||
| 2060 | fn add(td: *TransitiveDeps, link_objects: []const LinkObject) !void { | |
| 2061 | try td.link_objects.ensureUnusedCapacity(link_objects.len); | |
| 2062 | ||
| 2063 | for (link_objects) |link_object| { | |
| 2064 | try td.link_objects.append(link_object); | |
| 2065 | switch (link_object) { | |
| 2066 | .other_step => |other| try addInner(td, other, other.isDynamicLibrary()), | |
| 2067 | else => {}, | |
| 2068 | } | |
| 2069 | } | |
| 2070 | } | |
| 2071 | ||
| 2072 | fn addInner(td: *TransitiveDeps, other: *LibExeObjStep, dyn: bool) !void { | |
| 2073 | // Inherit dependency on libc and libc++ | |
| 2074 | td.is_linking_libcpp = td.is_linking_libcpp or other.is_linking_libcpp; | |
| 2075 | td.is_linking_libc = td.is_linking_libc or other.is_linking_libc; | |
| 2076 | ||
| 2077 | // Inherit dependencies on darwin frameworks | |
| 2078 | if (!dyn) { | |
| 2079 | var it = other.frameworks.iterator(); | |
| 2080 | while (it.next()) |framework| { | |
| 2081 | try td.frameworks.put(framework.key_ptr.*, framework.value_ptr.*); | |
| 2082 | } | |
| 2083 | } | |
| 2084 | ||
| 2085 | // Inherit dependencies on system libraries and static libraries. | |
| 2086 | for (other.link_objects.items) |other_link_object| { | |
| 2087 | switch (other_link_object) { | |
| 2088 | .system_lib => |system_lib| { | |
| 2089 | if ((try td.seen_system_libs.fetchPut(system_lib.name, {})) != null) | |
| 2090 | continue; | |
| 2091 | ||
| 2092 | if (dyn) | |
| 2093 | continue; | |
| 2094 | ||
| 2095 | try td.link_objects.append(other_link_object); | |
| 2096 | }, | |
| 2097 | .other_step => |inner_other| { | |
| 2098 | if ((try td.seen_steps.fetchPut(&inner_other.step, {})) != null) | |
| 2099 | continue; | |
| 2100 | ||
| 2101 | if (!dyn) | |
| 2102 | try td.link_objects.append(other_link_object); | |
| 2103 | ||
| 2104 | try addInner(td, inner_other, dyn or inner_other.isDynamicLibrary()); | |
| 2105 | }, | |
| 2106 | else => continue, | |
| 2107 | } | |
| 2108 | } | |
| 2109 | } | |
| 2110 | }; |
lib/std/build/LogStep.zig deleted-25| ... | ... | @@ -1,25 +0,0 @@ |
| 1 | const std = @import("../std.zig"); | |
| 2 | const log = std.log; | |
| 3 | const build = @import("../build.zig"); | |
| 4 | const Step = build.Step; | |
| 5 | const Builder = build.Builder; | |
| 6 | const LogStep = @This(); | |
| 7 | ||
| 8 | pub const base_id = .log; | |
| 9 | ||
| 10 | step: Step, | |
| 11 | builder: *Builder, | |
| 12 | data: []const u8, | |
| 13 | ||
| 14 | pub fn init(builder: *Builder, data: []const u8) LogStep { | |
| 15 | return LogStep{ | |
| 16 | .builder = builder, | |
| 17 | .step = Step.init(.log, builder.fmt("log {s}", .{data}), builder.allocator, make), | |
| 18 | .data = builder.dupe(data), | |
| 19 | }; | |
| 20 | } | |
| 21 | ||
| 22 | fn make(step: *Step) anyerror!void { | |
| 23 | const self = @fieldParentPtr(LogStep, "step", step); | |
| 24 | log.info("{s}", .{self.data}); | |
| 25 | } |
lib/std/build/OptionsStep.zig deleted-365| ... | ... | @@ -1,365 +0,0 @@ |
| 1 | const std = @import("../std.zig"); | |
| 2 | const builtin = @import("builtin"); | |
| 3 | const build = std.build; | |
| 4 | const fs = std.fs; | |
| 5 | const Step = build.Step; | |
| 6 | const Builder = build.Builder; | |
| 7 | const GeneratedFile = build.GeneratedFile; | |
| 8 | const LibExeObjStep = build.LibExeObjStep; | |
| 9 | const FileSource = build.FileSource; | |
| 10 | ||
| 11 | const OptionsStep = @This(); | |
| 12 | ||
| 13 | pub const base_id = .options; | |
| 14 | ||
| 15 | step: Step, | |
| 16 | generated_file: GeneratedFile, | |
| 17 | builder: *Builder, | |
| 18 | ||
| 19 | contents: std.ArrayList(u8), | |
| 20 | artifact_args: std.ArrayList(OptionArtifactArg), | |
| 21 | file_source_args: std.ArrayList(OptionFileSourceArg), | |
| 22 | ||
| 23 | pub fn create(builder: *Builder) *OptionsStep { | |
| 24 | const self = builder.allocator.create(OptionsStep) catch unreachable; | |
| 25 | self.* = .{ | |
| 26 | .builder = builder, | |
| 27 | .step = Step.init(.options, "options", builder.allocator, make), | |
| 28 | .generated_file = undefined, | |
| 29 | .contents = std.ArrayList(u8).init(builder.allocator), | |
| 30 | .artifact_args = std.ArrayList(OptionArtifactArg).init(builder.allocator), | |
| 31 | .file_source_args = std.ArrayList(OptionFileSourceArg).init(builder.allocator), | |
| 32 | }; | |
| 33 | self.generated_file = .{ .step = &self.step }; | |
| 34 | ||
| 35 | return self; | |
| 36 | } | |
| 37 | ||
| 38 | pub fn addOption(self: *OptionsStep, comptime T: type, name: []const u8, value: T) void { | |
| 39 | const out = self.contents.writer(); | |
| 40 | switch (T) { | |
| 41 | []const []const u8 => { | |
| 42 | out.print("pub const {}: []const []const u8 = &[_][]const u8{{\n", .{std.zig.fmtId(name)}) catch unreachable; | |
| 43 | for (value) |slice| { | |
| 44 | out.print(" \"{}\",\n", .{std.zig.fmtEscapes(slice)}) catch unreachable; | |
| 45 | } | |
| 46 | out.writeAll("};\n") catch unreachable; | |
| 47 | return; | |
| 48 | }, | |
| 49 | [:0]const u8 => { | |
| 50 | out.print("pub const {}: [:0]const u8 = \"{}\";\n", .{ std.zig.fmtId(name), std.zig.fmtEscapes(value) }) catch unreachable; | |
| 51 | return; | |
| 52 | }, | |
| 53 | []const u8 => { | |
| 54 | out.print("pub const {}: []const u8 = \"{}\";\n", .{ std.zig.fmtId(name), std.zig.fmtEscapes(value) }) catch unreachable; | |
| 55 | return; | |
| 56 | }, | |
| 57 | ?[:0]const u8 => { | |
| 58 | out.print("pub const {}: ?[:0]const u8 = ", .{std.zig.fmtId(name)}) catch unreachable; | |
| 59 | if (value) |payload| { | |
| 60 | out.print("\"{}\";\n", .{std.zig.fmtEscapes(payload)}) catch unreachable; | |
| 61 | } else { | |
| 62 | out.writeAll("null;\n") catch unreachable; | |
| 63 | } | |
| 64 | return; | |
| 65 | }, | |
| 66 | ?[]const u8 => { | |
| 67 | out.print("pub const {}: ?[]const u8 = ", .{std.zig.fmtId(name)}) catch unreachable; | |
| 68 | if (value) |payload| { | |
| 69 | out.print("\"{}\";\n", .{std.zig.fmtEscapes(payload)}) catch unreachable; | |
| 70 | } else { | |
| 71 | out.writeAll("null;\n") catch unreachable; | |
| 72 | } | |
| 73 | return; | |
| 74 | }, | |
| 75 | std.builtin.Version => { | |
| 76 | out.print( | |
| 77 | \\pub const {}: @import("std").builtin.Version = .{{ | |
| 78 | \\ .major = {d}, | |
| 79 | \\ .minor = {d}, | |
| 80 | \\ .patch = {d}, | |
| 81 | \\}}; | |
| 82 | \\ | |
| 83 | , .{ | |
| 84 | std.zig.fmtId(name), | |
| 85 | ||
| 86 | value.major, | |
| 87 | value.minor, | |
| 88 | value.patch, | |
| 89 | }) catch unreachable; | |
| 90 | return; | |
| 91 | }, | |
| 92 | std.SemanticVersion => { | |
| 93 | out.print( | |
| 94 | \\pub const {}: @import("std").SemanticVersion = .{{ | |
| 95 | \\ .major = {d}, | |
| 96 | \\ .minor = {d}, | |
| 97 | \\ .patch = {d}, | |
| 98 | \\ | |
| 99 | , .{ | |
| 100 | std.zig.fmtId(name), | |
| 101 | ||
| 102 | value.major, | |
| 103 | value.minor, | |
| 104 | value.patch, | |
| 105 | }) catch unreachable; | |
| 106 | if (value.pre) |some| { | |
| 107 | out.print(" .pre = \"{}\",\n", .{std.zig.fmtEscapes(some)}) catch unreachable; | |
| 108 | } | |
| 109 | if (value.build) |some| { | |
| 110 | out.print(" .build = \"{}\",\n", .{std.zig.fmtEscapes(some)}) catch unreachable; | |
| 111 | } | |
| 112 | out.writeAll("};\n") catch unreachable; | |
| 113 | return; | |
| 114 | }, | |
| 115 | else => {}, | |
| 116 | } | |
| 117 | switch (@typeInfo(T)) { | |
| 118 | .Enum => |enum_info| { | |
| 119 | out.print("pub const {} = enum {{\n", .{std.zig.fmtId(@typeName(T))}) catch unreachable; | |
| 120 | inline for (enum_info.fields) |field| { | |
| 121 | out.print(" {},\n", .{std.zig.fmtId(field.name)}) catch unreachable; | |
| 122 | } | |
| 123 | out.writeAll("};\n") catch unreachable; | |
| 124 | out.print("pub const {}: {s} = {s}.{s};\n", .{ | |
| 125 | std.zig.fmtId(name), | |
| 126 | std.zig.fmtId(@typeName(T)), | |
| 127 | std.zig.fmtId(@typeName(T)), | |
| 128 | std.zig.fmtId(@tagName(value)), | |
| 129 | }) catch unreachable; | |
| 130 | return; | |
| 131 | }, | |
| 132 | else => {}, | |
| 133 | } | |
| 134 | out.print("pub const {}: {s} = ", .{ std.zig.fmtId(name), @typeName(T) }) catch unreachable; | |
| 135 | printLiteral(out, value, 0) catch unreachable; | |
| 136 | out.writeAll(";\n") catch unreachable; | |
| 137 | } | |
| 138 | ||
| 139 | // TODO: non-recursive? | |
| 140 | fn printLiteral(out: anytype, val: anytype, indent: u8) !void { | |
| 141 | const T = @TypeOf(val); | |
| 142 | switch (@typeInfo(T)) { | |
| 143 | .Array => { | |
| 144 | try out.print("{s} {{\n", .{@typeName(T)}); | |
| 145 | for (val) |item| { | |
| 146 | try out.writeByteNTimes(' ', indent + 4); | |
| 147 | try printLiteral(out, item, indent + 4); | |
| 148 | try out.writeAll(",\n"); | |
| 149 | } | |
| 150 | try out.writeByteNTimes(' ', indent); | |
| 151 | try out.writeAll("}"); | |
| 152 | }, | |
| 153 | .Pointer => |p| { | |
| 154 | if (p.size != .Slice) { | |
| 155 | @compileError("Non-slice pointers are not yet supported in build options"); | |
| 156 | } | |
| 157 | try out.print("&[_]{s} {{\n", .{@typeName(p.child)}); | |
| 158 | for (val) |item| { | |
| 159 | try out.writeByteNTimes(' ', indent + 4); | |
| 160 | try printLiteral(out, item, indent + 4); | |
| 161 | try out.writeAll(",\n"); | |
| 162 | } | |
| 163 | try out.writeByteNTimes(' ', indent); | |
| 164 | try out.writeAll("}"); | |
| 165 | }, | |
| 166 | .Optional => { | |
| 167 | if (val) |inner| { | |
| 168 | return printLiteral(out, inner, indent); | |
| 169 | } else { | |
| 170 | return out.writeAll("null"); | |
| 171 | } | |
| 172 | }, | |
| 173 | .Void, | |
| 174 | .Bool, | |
| 175 | .Int, | |
| 176 | .ComptimeInt, | |
| 177 | .Float, | |
| 178 | .Null, | |
| 179 | => try out.print("{any}", .{val}), | |
| 180 | else => @compileError(std.fmt.comptimePrint("`{s}` are not yet supported as build options", .{@tagName(@typeInfo(T))})), | |
| 181 | } | |
| 182 | } | |
| 183 | ||
| 184 | /// The value is the path in the cache dir. | |
| 185 | /// Adds a dependency automatically. | |
| 186 | pub fn addOptionFileSource( | |
| 187 | self: *OptionsStep, | |
| 188 | name: []const u8, | |
| 189 | source: FileSource, | |
| 190 | ) void { | |
| 191 | self.file_source_args.append(.{ | |
| 192 | .name = name, | |
| 193 | .source = source.dupe(self.builder), | |
| 194 | }) catch unreachable; | |
| 195 | source.addStepDependencies(&self.step); | |
| 196 | } | |
| 197 | ||
| 198 | /// The value is the path in the cache dir. | |
| 199 | /// Adds a dependency automatically. | |
| 200 | pub fn addOptionArtifact(self: *OptionsStep, name: []const u8, artifact: *LibExeObjStep) void { | |
| 201 | self.artifact_args.append(.{ .name = self.builder.dupe(name), .artifact = artifact }) catch unreachable; | |
| 202 | self.step.dependOn(&artifact.step); | |
| 203 | } | |
| 204 | ||
| 205 | pub fn getPackage(self: *OptionsStep, package_name: []const u8) build.Pkg { | |
| 206 | return .{ .name = package_name, .source = self.getSource() }; | |
| 207 | } | |
| 208 | ||
| 209 | pub fn getSource(self: *OptionsStep) FileSource { | |
| 210 | return .{ .generated = &self.generated_file }; | |
| 211 | } | |
| 212 | ||
| 213 | fn make(step: *Step) !void { | |
| 214 | const self = @fieldParentPtr(OptionsStep, "step", step); | |
| 215 | ||
| 216 | for (self.artifact_args.items) |item| { | |
| 217 | self.addOption( | |
| 218 | []const u8, | |
| 219 | item.name, | |
| 220 | self.builder.pathFromRoot(item.artifact.getOutputSource().getPath(self.builder)), | |
| 221 | ); | |
| 222 | } | |
| 223 | ||
| 224 | for (self.file_source_args.items) |item| { | |
| 225 | self.addOption( | |
| 226 | []const u8, | |
| 227 | item.name, | |
| 228 | item.source.getPath(self.builder), | |
| 229 | ); | |
| 230 | } | |
| 231 | ||
| 232 | const options_directory = self.builder.pathFromRoot( | |
| 233 | try fs.path.join( | |
| 234 | self.builder.allocator, | |
| 235 | &[_][]const u8{ self.builder.cache_root, "options" }, | |
| 236 | ), | |
| 237 | ); | |
| 238 | ||
| 239 | try fs.cwd().makePath(options_directory); | |
| 240 | ||
| 241 | const options_file = try fs.path.join( | |
| 242 | self.builder.allocator, | |
| 243 | &[_][]const u8{ options_directory, &self.hashContentsToFileName() }, | |
| 244 | ); | |
| 245 | ||
| 246 | try fs.cwd().writeFile(options_file, self.contents.items); | |
| 247 | ||
| 248 | self.generated_file.path = options_file; | |
| 249 | } | |
| 250 | ||
| 251 | fn hashContentsToFileName(self: *OptionsStep) [64]u8 { | |
| 252 | // This implementation is copied from `WriteFileStep.make` | |
| 253 | ||
| 254 | var hash = std.crypto.hash.blake2.Blake2b384.init(.{}); | |
| 255 | ||
| 256 | // Random bytes to make OptionsStep unique. Refresh this with | |
| 257 | // new random bytes when OptionsStep implementation is modified | |
| 258 | // in a non-backwards-compatible way. | |
| 259 | hash.update("yL0Ya4KkmcCjBlP8"); | |
| 260 | hash.update(self.contents.items); | |
| 261 | ||
| 262 | var digest: [48]u8 = undefined; | |
| 263 | hash.final(&digest); | |
| 264 | var hash_basename: [64]u8 = undefined; | |
| 265 | _ = fs.base64_encoder.encode(&hash_basename, &digest); | |
| 266 | return hash_basename; | |
| 267 | } | |
| 268 | ||
| 269 | const OptionArtifactArg = struct { | |
| 270 | name: []const u8, | |
| 271 | artifact: *LibExeObjStep, | |
| 272 | }; | |
| 273 | ||
| 274 | const OptionFileSourceArg = struct { | |
| 275 | name: []const u8, | |
| 276 | source: FileSource, | |
| 277 | }; | |
| 278 | ||
| 279 | test "OptionsStep" { | |
| 280 | if (builtin.os.tag == .wasi) return error.SkipZigTest; | |
| 281 | ||
| 282 | var arena = std.heap.ArenaAllocator.init(std.testing.allocator); | |
| 283 | defer arena.deinit(); | |
| 284 | var builder = try Builder.create( | |
| 285 | arena.allocator(), | |
| 286 | "test", | |
| 287 | "test", | |
| 288 | "test", | |
| 289 | "test", | |
| 290 | ); | |
| 291 | defer builder.destroy(); | |
| 292 | ||
| 293 | const options = builder.addOptions(); | |
| 294 | ||
| 295 | // TODO this regressed at some point | |
| 296 | //const KeywordEnum = enum { | |
| 297 | // @"0.8.1", | |
| 298 | //}; | |
| 299 | ||
| 300 | const nested_array = [2][2]u16{ | |
| 301 | [2]u16{ 300, 200 }, | |
| 302 | [2]u16{ 300, 200 }, | |
| 303 | }; | |
| 304 | const nested_slice: []const []const u16 = &[_][]const u16{ &nested_array[0], &nested_array[1] }; | |
| 305 | ||
| 306 | options.addOption(usize, "option1", 1); | |
| 307 | options.addOption(?usize, "option2", null); | |
| 308 | options.addOption(?usize, "option3", 3); | |
| 309 | options.addOption(comptime_int, "option4", 4); | |
| 310 | options.addOption([]const u8, "string", "zigisthebest"); | |
| 311 | options.addOption(?[]const u8, "optional_string", null); | |
| 312 | options.addOption([2][2]u16, "nested_array", nested_array); | |
| 313 | options.addOption([]const []const u16, "nested_slice", nested_slice); | |
| 314 | //options.addOption(KeywordEnum, "keyword_enum", .@"0.8.1"); | |
| 315 | options.addOption(std.builtin.Version, "version", try std.builtin.Version.parse("0.1.2")); | |
| 316 | options.addOption(std.SemanticVersion, "semantic_version", try std.SemanticVersion.parse("0.1.2-foo+bar")); | |
| 317 | ||
| 318 | try std.testing.expectEqualStrings( | |
| 319 | \\pub const option1: usize = 1; | |
| 320 | \\pub const option2: ?usize = null; | |
| 321 | \\pub const option3: ?usize = 3; | |
| 322 | \\pub const option4: comptime_int = 4; | |
| 323 | \\pub const string: []const u8 = "zigisthebest"; | |
| 324 | \\pub const optional_string: ?[]const u8 = null; | |
| 325 | \\pub const nested_array: [2][2]u16 = [2][2]u16 { | |
| 326 | \\ [2]u16 { | |
| 327 | \\ 300, | |
| 328 | \\ 200, | |
| 329 | \\ }, | |
| 330 | \\ [2]u16 { | |
| 331 | \\ 300, | |
| 332 | \\ 200, | |
| 333 | \\ }, | |
| 334 | \\}; | |
| 335 | \\pub const nested_slice: []const []const u16 = &[_][]const u16 { | |
| 336 | \\ &[_]u16 { | |
| 337 | \\ 300, | |
| 338 | \\ 200, | |
| 339 | \\ }, | |
| 340 | \\ &[_]u16 { | |
| 341 | \\ 300, | |
| 342 | \\ 200, | |
| 343 | \\ }, | |
| 344 | \\}; | |
| 345 | //\\pub const KeywordEnum = enum { | |
| 346 | //\\ @"0.8.1", | |
| 347 | //\\}; | |
| 348 | //\\pub const keyword_enum: KeywordEnum = KeywordEnum.@"0.8.1"; | |
| 349 | \\pub const version: @import("std").builtin.Version = .{ | |
| 350 | \\ .major = 0, | |
| 351 | \\ .minor = 1, | |
| 352 | \\ .patch = 2, | |
| 353 | \\}; | |
| 354 | \\pub const semantic_version: @import("std").SemanticVersion = .{ | |
| 355 | \\ .major = 0, | |
| 356 | \\ .minor = 1, | |
| 357 | \\ .patch = 2, | |
| 358 | \\ .pre = "foo", | |
| 359 | \\ .build = "bar", | |
| 360 | \\}; | |
| 361 | \\ | |
| 362 | , options.contents.items); | |
| 363 | ||
| 364 | _ = try std.zig.parse(arena.allocator(), try options.contents.toOwnedSliceSentinel(0)); | |
| 365 | } |
lib/std/build/RemoveDirStep.zig deleted-31| ... | ... | @@ -1,31 +0,0 @@ |
| 1 | const std = @import("../std.zig"); | |
| 2 | const log = std.log; | |
| 3 | const fs = std.fs; | |
| 4 | const build = @import("../build.zig"); | |
| 5 | const Step = build.Step; | |
| 6 | const Builder = build.Builder; | |
| 7 | const RemoveDirStep = @This(); | |
| 8 | ||
| 9 | pub const base_id = .remove_dir; | |
| 10 | ||
| 11 | step: Step, | |
| 12 | builder: *Builder, | |
| 13 | dir_path: []const u8, | |
| 14 | ||
| 15 | pub fn init(builder: *Builder, dir_path: []const u8) RemoveDirStep { | |
| 16 | return RemoveDirStep{ | |
| 17 | .builder = builder, | |
| 18 | .step = Step.init(.remove_dir, builder.fmt("RemoveDir {s}", .{dir_path}), builder.allocator, make), | |
| 19 | .dir_path = builder.dupePath(dir_path), | |
| 20 | }; | |
| 21 | } | |
| 22 | ||
| 23 | fn make(step: *Step) !void { | |
| 24 | const self = @fieldParentPtr(RemoveDirStep, "step", step); | |
| 25 | ||
| 26 | const full_path = self.builder.pathFromRoot(self.dir_path); | |
| 27 | fs.cwd().deleteTree(full_path) catch |err| { | |
| 28 | log.err("Unable to remove {s}: {s}", .{ full_path, @errorName(err) }); | |
| 29 | return err; | |
| 30 | }; | |
| 31 | } |
lib/std/build/RunStep.zig deleted-378| ... | ... | @@ -1,378 +0,0 @@ |
| 1 | const std = @import("../std.zig"); | |
| 2 | const builtin = @import("builtin"); | |
| 3 | const build = std.build; | |
| 4 | const Step = build.Step; | |
| 5 | const Builder = build.Builder; | |
| 6 | const LibExeObjStep = build.LibExeObjStep; | |
| 7 | const WriteFileStep = build.WriteFileStep; | |
| 8 | const fs = std.fs; | |
| 9 | const mem = std.mem; | |
| 10 | const process = std.process; | |
| 11 | const ArrayList = std.ArrayList; | |
| 12 | const EnvMap = process.EnvMap; | |
| 13 | const Allocator = mem.Allocator; | |
| 14 | const ExecError = build.Builder.ExecError; | |
| 15 | ||
| 16 | const max_stdout_size = 1 * 1024 * 1024; // 1 MiB | |
| 17 | ||
| 18 | const RunStep = @This(); | |
| 19 | ||
| 20 | pub const base_id: Step.Id = .run; | |
| 21 | ||
| 22 | step: Step, | |
| 23 | builder: *Builder, | |
| 24 | ||
| 25 | /// See also addArg and addArgs to modifying this directly | |
| 26 | argv: ArrayList(Arg), | |
| 27 | ||
| 28 | /// Set this to modify the current working directory | |
| 29 | cwd: ?[]const u8, | |
| 30 | ||
| 31 | /// Override this field to modify the environment, or use setEnvironmentVariable | |
| 32 | env_map: ?*EnvMap, | |
| 33 | ||
| 34 | stdout_action: StdIoAction = .inherit, | |
| 35 | stderr_action: StdIoAction = .inherit, | |
| 36 | ||
| 37 | stdin_behavior: std.ChildProcess.StdIo = .Inherit, | |
| 38 | ||
| 39 | /// Set this to `null` to ignore the exit code for the purpose of determining a successful execution | |
| 40 | expected_exit_code: ?u8 = 0, | |
| 41 | ||
| 42 | /// Print the command before running it | |
| 43 | print: bool, | |
| 44 | ||
| 45 | pub const StdIoAction = union(enum) { | |
| 46 | inherit, | |
| 47 | ignore, | |
| 48 | expect_exact: []const u8, | |
| 49 | expect_matches: []const []const u8, | |
| 50 | }; | |
| 51 | ||
| 52 | pub const Arg = union(enum) { | |
| 53 | artifact: *LibExeObjStep, | |
| 54 | file_source: build.FileSource, | |
| 55 | bytes: []u8, | |
| 56 | }; | |
| 57 | ||
| 58 | pub fn create(builder: *Builder, name: []const u8) *RunStep { | |
| 59 | const self = builder.allocator.create(RunStep) catch unreachable; | |
| 60 | self.* = RunStep{ | |
| 61 | .builder = builder, | |
| 62 | .step = Step.init(base_id, name, builder.allocator, make), | |
| 63 | .argv = ArrayList(Arg).init(builder.allocator), | |
| 64 | .cwd = null, | |
| 65 | .env_map = null, | |
| 66 | .print = builder.verbose, | |
| 67 | }; | |
| 68 | return self; | |
| 69 | } | |
| 70 | ||
| 71 | pub fn addArtifactArg(self: *RunStep, artifact: *LibExeObjStep) void { | |
| 72 | self.argv.append(Arg{ .artifact = artifact }) catch unreachable; | |
| 73 | self.step.dependOn(&artifact.step); | |
| 74 | } | |
| 75 | ||
| 76 | pub fn addFileSourceArg(self: *RunStep, file_source: build.FileSource) void { | |
| 77 | self.argv.append(Arg{ | |
| 78 | .file_source = file_source.dupe(self.builder), | |
| 79 | }) catch unreachable; | |
| 80 | file_source.addStepDependencies(&self.step); | |
| 81 | } | |
| 82 | ||
| 83 | pub fn addArg(self: *RunStep, arg: []const u8) void { | |
| 84 | self.argv.append(Arg{ .bytes = self.builder.dupe(arg) }) catch unreachable; | |
| 85 | } | |
| 86 | ||
| 87 | pub fn addArgs(self: *RunStep, args: []const []const u8) void { | |
| 88 | for (args) |arg| { | |
| 89 | self.addArg(arg); | |
| 90 | } | |
| 91 | } | |
| 92 | ||
| 93 | pub fn clearEnvironment(self: *RunStep) void { | |
| 94 | const new_env_map = self.builder.allocator.create(EnvMap) catch unreachable; | |
| 95 | new_env_map.* = EnvMap.init(self.builder.allocator); | |
| 96 | self.env_map = new_env_map; | |
| 97 | } | |
| 98 | ||
| 99 | pub fn addPathDir(self: *RunStep, search_path: []const u8) void { | |
| 100 | addPathDirInternal(&self.step, self.builder, search_path); | |
| 101 | } | |
| 102 | ||
| 103 | /// For internal use only, users of `RunStep` should use `addPathDir` directly. | |
| 104 | pub fn addPathDirInternal(step: *Step, builder: *Builder, search_path: []const u8) void { | |
| 105 | const env_map = getEnvMapInternal(step, builder.allocator); | |
| 106 | ||
| 107 | const key = "PATH"; | |
| 108 | var prev_path = env_map.get(key); | |
| 109 | ||
| 110 | if (prev_path) |pp| { | |
| 111 | const new_path = builder.fmt("{s}" ++ [1]u8{fs.path.delimiter} ++ "{s}", .{ pp, search_path }); | |
| 112 | env_map.put(key, new_path) catch unreachable; | |
| 113 | } else { | |
| 114 | env_map.put(key, builder.dupePath(search_path)) catch unreachable; | |
| 115 | } | |
| 116 | } | |
| 117 | ||
| 118 | pub fn getEnvMap(self: *RunStep) *EnvMap { | |
| 119 | return getEnvMapInternal(&self.step, self.builder.allocator); | |
| 120 | } | |
| 121 | ||
| 122 | fn getEnvMapInternal(step: *Step, allocator: Allocator) *EnvMap { | |
| 123 | const maybe_env_map = switch (step.id) { | |
| 124 | .run => step.cast(RunStep).?.env_map, | |
| 125 | .emulatable_run => step.cast(build.EmulatableRunStep).?.env_map, | |
| 126 | else => unreachable, | |
| 127 | }; | |
| 128 | return maybe_env_map orelse { | |
| 129 | const env_map = allocator.create(EnvMap) catch unreachable; | |
| 130 | env_map.* = process.getEnvMap(allocator) catch unreachable; | |
| 131 | switch (step.id) { | |
| 132 | .run => step.cast(RunStep).?.env_map = env_map, | |
| 133 | .emulatable_run => step.cast(RunStep).?.env_map = env_map, | |
| 134 | else => unreachable, | |
| 135 | } | |
| 136 | return env_map; | |
| 137 | }; | |
| 138 | } | |
| 139 | ||
| 140 | pub fn setEnvironmentVariable(self: *RunStep, key: []const u8, value: []const u8) void { | |
| 141 | const env_map = self.getEnvMap(); | |
| 142 | env_map.put( | |
| 143 | self.builder.dupe(key), | |
| 144 | self.builder.dupe(value), | |
| 145 | ) catch unreachable; | |
| 146 | } | |
| 147 | ||
| 148 | pub fn expectStdErrEqual(self: *RunStep, bytes: []const u8) void { | |
| 149 | self.stderr_action = .{ .expect_exact = self.builder.dupe(bytes) }; | |
| 150 | } | |
| 151 | ||
| 152 | pub fn expectStdOutEqual(self: *RunStep, bytes: []const u8) void { | |
| 153 | self.stdout_action = .{ .expect_exact = self.builder.dupe(bytes) }; | |
| 154 | } | |
| 155 | ||
| 156 | fn stdIoActionToBehavior(action: StdIoAction) std.ChildProcess.StdIo { | |
| 157 | return switch (action) { | |
| 158 | .ignore => .Ignore, | |
| 159 | .inherit => .Inherit, | |
| 160 | .expect_exact, .expect_matches => .Pipe, | |
| 161 | }; | |
| 162 | } | |
| 163 | ||
| 164 | fn make(step: *Step) !void { | |
| 165 | const self = @fieldParentPtr(RunStep, "step", step); | |
| 166 | ||
| 167 | var argv_list = ArrayList([]const u8).init(self.builder.allocator); | |
| 168 | for (self.argv.items) |arg| { | |
| 169 | switch (arg) { | |
| 170 | .bytes => |bytes| try argv_list.append(bytes), | |
| 171 | .file_source => |file| try argv_list.append(file.getPath(self.builder)), | |
| 172 | .artifact => |artifact| { | |
| 173 | if (artifact.target.isWindows()) { | |
| 174 | // On Windows we don't have rpaths so we have to add .dll search paths to PATH | |
| 175 | self.addPathForDynLibs(artifact); | |
| 176 | } | |
| 177 | const executable_path = artifact.installed_path orelse artifact.getOutputSource().getPath(self.builder); | |
| 178 | try argv_list.append(executable_path); | |
| 179 | }, | |
| 180 | } | |
| 181 | } | |
| 182 | ||
| 183 | try runCommand( | |
| 184 | argv_list.items, | |
| 185 | self.builder, | |
| 186 | self.expected_exit_code, | |
| 187 | self.stdout_action, | |
| 188 | self.stderr_action, | |
| 189 | self.stdin_behavior, | |
| 190 | self.env_map, | |
| 191 | self.cwd, | |
| 192 | self.print, | |
| 193 | ); | |
| 194 | } | |
| 195 | ||
| 196 | pub fn runCommand( | |
| 197 | argv: []const []const u8, | |
| 198 | builder: *Builder, | |
| 199 | expected_exit_code: ?u8, | |
| 200 | stdout_action: StdIoAction, | |
| 201 | stderr_action: StdIoAction, | |
| 202 | stdin_behavior: std.ChildProcess.StdIo, | |
| 203 | env_map: ?*EnvMap, | |
| 204 | maybe_cwd: ?[]const u8, | |
| 205 | print: bool, | |
| 206 | ) !void { | |
| 207 | const cwd = if (maybe_cwd) |cwd| builder.pathFromRoot(cwd) else builder.build_root; | |
| 208 | ||
| 209 | if (!std.process.can_spawn) { | |
| 210 | const cmd = try std.mem.join(builder.allocator, " ", argv); | |
| 211 | std.debug.print("the following command cannot be executed ({s} does not support spawning a child process):\n{s}", .{ @tagName(builtin.os.tag), cmd }); | |
| 212 | builder.allocator.free(cmd); | |
| 213 | return ExecError.ExecNotSupported; | |
| 214 | } | |
| 215 | ||
| 216 | var child = std.ChildProcess.init(argv, builder.allocator); | |
| 217 | child.cwd = cwd; | |
| 218 | child.env_map = env_map orelse builder.env_map; | |
| 219 | ||
| 220 | child.stdin_behavior = stdin_behavior; | |
| 221 | child.stdout_behavior = stdIoActionToBehavior(stdout_action); | |
| 222 | child.stderr_behavior = stdIoActionToBehavior(stderr_action); | |
| 223 | ||
| 224 | if (print) | |
| 225 | printCmd(cwd, argv); | |
| 226 | ||
| 227 | child.spawn() catch |err| { | |
| 228 | std.debug.print("Unable to spawn {s}: {s}\n", .{ argv[0], @errorName(err) }); | |
| 229 | return err; | |
| 230 | }; | |
| 231 | ||
| 232 | // TODO need to poll to read these streams to prevent a deadlock (or rely on evented I/O). | |
| 233 | ||
| 234 | var stdout: ?[]const u8 = null; | |
| 235 | defer if (stdout) |s| builder.allocator.free(s); | |
| 236 | ||
| 237 | switch (stdout_action) { | |
| 238 | .expect_exact, .expect_matches => { | |
| 239 | stdout = child.stdout.?.reader().readAllAlloc(builder.allocator, max_stdout_size) catch unreachable; | |
| 240 | }, | |
| 241 | .inherit, .ignore => {}, | |
| 242 | } | |
| 243 | ||
| 244 | var stderr: ?[]const u8 = null; | |
| 245 | defer if (stderr) |s| builder.allocator.free(s); | |
| 246 | ||
| 247 | switch (stderr_action) { | |
| 248 | .expect_exact, .expect_matches => { | |
| 249 | stderr = child.stderr.?.reader().readAllAlloc(builder.allocator, max_stdout_size) catch unreachable; | |
| 250 | }, | |
| 251 | .inherit, .ignore => {}, | |
| 252 | } | |
| 253 | ||
| 254 | const term = child.wait() catch |err| { | |
| 255 | std.debug.print("Unable to spawn {s}: {s}\n", .{ argv[0], @errorName(err) }); | |
| 256 | return err; | |
| 257 | }; | |
| 258 | ||
| 259 | switch (term) { | |
| 260 | .Exited => |code| blk: { | |
| 261 | const expected_code = expected_exit_code orelse break :blk; | |
| 262 | ||
| 263 | if (code != expected_code) { | |
| 264 | if (builder.prominent_compile_errors) { | |
| 265 | std.debug.print("Run step exited with error code {} (expected {})\n", .{ | |
| 266 | code, | |
| 267 | expected_code, | |
| 268 | }); | |
| 269 | } else { | |
| 270 | std.debug.print("The following command exited with error code {} (expected {}):\n", .{ | |
| 271 | code, | |
| 272 | expected_code, | |
| 273 | }); | |
| 274 | printCmd(cwd, argv); | |
| 275 | } | |
| 276 | ||
| 277 | return error.UnexpectedExitCode; | |
| 278 | } | |
| 279 | }, | |
| 280 | else => { | |
| 281 | std.debug.print("The following command terminated unexpectedly:\n", .{}); | |
| 282 | printCmd(cwd, argv); | |
| 283 | return error.UncleanExit; | |
| 284 | }, | |
| 285 | } | |
| 286 | ||
| 287 | switch (stderr_action) { | |
| 288 | .inherit, .ignore => {}, | |
| 289 | .expect_exact => |expected_bytes| { | |
| 290 | if (!mem.eql(u8, expected_bytes, stderr.?)) { | |
| 291 | std.debug.print( | |
| 292 | \\ | |
| 293 | \\========= Expected this stderr: ========= | |
| 294 | \\{s} | |
| 295 | \\========= But found: ==================== | |
| 296 | \\{s} | |
| 297 | \\ | |
| 298 | , .{ expected_bytes, stderr.? }); | |
| 299 | printCmd(cwd, argv); | |
| 300 | return error.TestFailed; | |
| 301 | } | |
| 302 | }, | |
| 303 | .expect_matches => |matches| for (matches) |match| { | |
| 304 | if (mem.indexOf(u8, stderr.?, match) == null) { | |
| 305 | std.debug.print( | |
| 306 | \\ | |
| 307 | \\========= Expected to find in stderr: ========= | |
| 308 | \\{s} | |
| 309 | \\========= But stderr does not contain it: ===== | |
| 310 | \\{s} | |
| 311 | \\ | |
| 312 | , .{ match, stderr.? }); | |
| 313 | printCmd(cwd, argv); | |
| 314 | return error.TestFailed; | |
| 315 | } | |
| 316 | }, | |
| 317 | } | |
| 318 | ||
| 319 | switch (stdout_action) { | |
| 320 | .inherit, .ignore => {}, | |
| 321 | .expect_exact => |expected_bytes| { | |
| 322 | if (!mem.eql(u8, expected_bytes, stdout.?)) { | |
| 323 | std.debug.print( | |
| 324 | \\ | |
| 325 | \\========= Expected this stdout: ========= | |
| 326 | \\{s} | |
| 327 | \\========= But found: ==================== | |
| 328 | \\{s} | |
| 329 | \\ | |
| 330 | , .{ expected_bytes, stdout.? }); | |
| 331 | printCmd(cwd, argv); | |
| 332 | return error.TestFailed; | |
| 333 | } | |
| 334 | }, | |
| 335 | .expect_matches => |matches| for (matches) |match| { | |
| 336 | if (mem.indexOf(u8, stdout.?, match) == null) { | |
| 337 | std.debug.print( | |
| 338 | \\ | |
| 339 | \\========= Expected to find in stdout: ========= | |
| 340 | \\{s} | |
| 341 | \\========= But stdout does not contain it: ===== | |
| 342 | \\{s} | |
| 343 | \\ | |
| 344 | , .{ match, stdout.? }); | |
| 345 | printCmd(cwd, argv); | |
| 346 | return error.TestFailed; | |
| 347 | } | |
| 348 | }, | |
| 349 | } | |
| 350 | } | |
| 351 | ||
| 352 | fn printCmd(cwd: ?[]const u8, argv: []const []const u8) void { | |
| 353 | if (cwd) |yes_cwd| std.debug.print("cd {s} && ", .{yes_cwd}); | |
| 354 | for (argv) |arg| { | |
| 355 | std.debug.print("{s} ", .{arg}); | |
| 356 | } | |
| 357 | std.debug.print("\n", .{}); | |
| 358 | } | |
| 359 | ||
| 360 | fn addPathForDynLibs(self: *RunStep, artifact: *LibExeObjStep) void { | |
| 361 | addPathForDynLibsInternal(&self.step, self.builder, artifact); | |
| 362 | } | |
| 363 | ||
| 364 | /// This should only be used for internal usage, this is called automatically | |
| 365 | /// for the user. | |
| 366 | pub fn addPathForDynLibsInternal(step: *Step, builder: *Builder, artifact: *LibExeObjStep) void { | |
| 367 | for (artifact.link_objects.items) |link_object| { | |
| 368 | switch (link_object) { | |
| 369 | .other_step => |other| { | |
| 370 | if (other.target.isWindows() and other.isDynamicLibrary()) { | |
| 371 | addPathDirInternal(step, builder, fs.path.dirname(other.getOutputSource().getPath(builder)).?); | |
| 372 | addPathForDynLibsInternal(step, builder, other); | |
| 373 | } | |
| 374 | }, | |
| 375 | else => {}, | |
| 376 | } | |
| 377 | } | |
| 378 | } |
lib/std/build/TranslateCStep.zig deleted-112| ... | ... | @@ -1,112 +0,0 @@ |
| 1 | const std = @import("../std.zig"); | |
| 2 | const build = std.build; | |
| 3 | const Step = build.Step; | |
| 4 | const Builder = build.Builder; | |
| 5 | const LibExeObjStep = build.LibExeObjStep; | |
| 6 | const CheckFileStep = build.CheckFileStep; | |
| 7 | const fs = std.fs; | |
| 8 | const mem = std.mem; | |
| 9 | const CrossTarget = std.zig.CrossTarget; | |
| 10 | ||
| 11 | const TranslateCStep = @This(); | |
| 12 | ||
| 13 | pub const base_id = .translate_c; | |
| 14 | ||
| 15 | step: Step, | |
| 16 | builder: *Builder, | |
| 17 | source: build.FileSource, | |
| 18 | include_dirs: std.ArrayList([]const u8), | |
| 19 | c_macros: std.ArrayList([]const u8), | |
| 20 | output_dir: ?[]const u8, | |
| 21 | out_basename: []const u8, | |
| 22 | target: CrossTarget = CrossTarget{}, | |
| 23 | output_file: build.GeneratedFile, | |
| 24 | ||
| 25 | pub fn create(builder: *Builder, source: build.FileSource) *TranslateCStep { | |
| 26 | const self = builder.allocator.create(TranslateCStep) catch unreachable; | |
| 27 | self.* = TranslateCStep{ | |
| 28 | .step = Step.init(.translate_c, "translate-c", builder.allocator, make), | |
| 29 | .builder = builder, | |
| 30 | .source = source, | |
| 31 | .include_dirs = std.ArrayList([]const u8).init(builder.allocator), | |
| 32 | .c_macros = std.ArrayList([]const u8).init(builder.allocator), | |
| 33 | .output_dir = null, | |
| 34 | .out_basename = undefined, | |
| 35 | .output_file = build.GeneratedFile{ .step = &self.step }, | |
| 36 | }; | |
| 37 | source.addStepDependencies(&self.step); | |
| 38 | return self; | |
| 39 | } | |
| 40 | ||
| 41 | pub fn setTarget(self: *TranslateCStep, target: CrossTarget) void { | |
| 42 | self.target = target; | |
| 43 | } | |
| 44 | ||
| 45 | /// Creates a step to build an executable from the translated source. | |
| 46 | pub fn addExecutable(self: *TranslateCStep) *LibExeObjStep { | |
| 47 | return self.builder.addExecutableSource("translated_c", build.FileSource{ .generated = &self.output_file }); | |
| 48 | } | |
| 49 | ||
| 50 | pub fn addIncludeDir(self: *TranslateCStep, include_dir: []const u8) void { | |
| 51 | self.include_dirs.append(self.builder.dupePath(include_dir)) catch unreachable; | |
| 52 | } | |
| 53 | ||
| 54 | pub fn addCheckFile(self: *TranslateCStep, expected_matches: []const []const u8) *CheckFileStep { | |
| 55 | return CheckFileStep.create(self.builder, .{ .generated = &self.output_file }, self.builder.dupeStrings(expected_matches)); | |
| 56 | } | |
| 57 | ||
| 58 | /// If the value is omitted, it is set to 1. | |
| 59 | /// `name` and `value` need not live longer than the function call. | |
| 60 | pub fn defineCMacro(self: *TranslateCStep, name: []const u8, value: ?[]const u8) void { | |
| 61 | const macro = build.constructCMacro(self.builder.allocator, name, value); | |
| 62 | self.c_macros.append(macro) catch unreachable; | |
| 63 | } | |
| 64 | ||
| 65 | /// name_and_value looks like [name]=[value]. If the value is omitted, it is set to 1. | |
| 66 | pub fn defineCMacroRaw(self: *TranslateCStep, name_and_value: []const u8) void { | |
| 67 | self.c_macros.append(self.builder.dupe(name_and_value)) catch unreachable; | |
| 68 | } | |
| 69 | ||
| 70 | fn make(step: *Step) !void { | |
| 71 | const self = @fieldParentPtr(TranslateCStep, "step", step); | |
| 72 | ||
| 73 | var argv_list = std.ArrayList([]const u8).init(self.builder.allocator); | |
| 74 | try argv_list.append(self.builder.zig_exe); | |
| 75 | try argv_list.append("translate-c"); | |
| 76 | try argv_list.append("-lc"); | |
| 77 | ||
| 78 | try argv_list.append("--enable-cache"); | |
| 79 | ||
| 80 | if (!self.target.isNative()) { | |
| 81 | try argv_list.append("-target"); | |
| 82 | try argv_list.append(try self.target.zigTriple(self.builder.allocator)); | |
| 83 | } | |
| 84 | ||
| 85 | for (self.include_dirs.items) |include_dir| { | |
| 86 | try argv_list.append("-I"); | |
| 87 | try argv_list.append(include_dir); | |
| 88 | } | |
| 89 | ||
| 90 | for (self.c_macros.items) |c_macro| { | |
| 91 | try argv_list.append("-D"); | |
| 92 | try argv_list.append(c_macro); | |
| 93 | } | |
| 94 | ||
| 95 | try argv_list.append(self.source.getPath(self.builder)); | |
| 96 | ||
| 97 | const output_path_nl = try self.builder.execFromStep(argv_list.items, &self.step); | |
| 98 | const output_path = mem.trimRight(u8, output_path_nl, "\r\n"); | |
| 99 | ||
| 100 | self.out_basename = fs.path.basename(output_path); | |
| 101 | if (self.output_dir) |output_dir| { | |
| 102 | const full_dest = try fs.path.join(self.builder.allocator, &[_][]const u8{ output_dir, self.out_basename }); | |
| 103 | try self.builder.updateFile(output_path, full_dest); | |
| 104 | } else { | |
| 105 | self.output_dir = fs.path.dirname(output_path).?; | |
| 106 | } | |
| 107 | ||
| 108 | self.output_file.path = fs.path.join( | |
| 109 | self.builder.allocator, | |
| 110 | &[_][]const u8{ self.output_dir.?, self.out_basename }, | |
| 111 | ) catch unreachable; | |
| 112 | } |
lib/std/build/WriteFileStep.zig deleted-117| ... | ... | @@ -1,117 +0,0 @@ |
| 1 | const std = @import("../std.zig"); | |
| 2 | const build = @import("../build.zig"); | |
| 3 | const Step = build.Step; | |
| 4 | const Builder = build.Builder; | |
| 5 | const fs = std.fs; | |
| 6 | const ArrayList = std.ArrayList; | |
| 7 | ||
| 8 | const WriteFileStep = @This(); | |
| 9 | ||
| 10 | pub const base_id = .write_file; | |
| 11 | ||
| 12 | step: Step, | |
| 13 | builder: *Builder, | |
| 14 | output_dir: []const u8, | |
| 15 | files: std.TailQueue(File), | |
| 16 | ||
| 17 | pub const File = struct { | |
| 18 | source: build.GeneratedFile, | |
| 19 | basename: []const u8, | |
| 20 | bytes: []const u8, | |
| 21 | }; | |
| 22 | ||
| 23 | pub fn init(builder: *Builder) WriteFileStep { | |
| 24 | return WriteFileStep{ | |
| 25 | .builder = builder, | |
| 26 | .step = Step.init(.write_file, "writefile", builder.allocator, make), | |
| 27 | .files = .{}, | |
| 28 | .output_dir = undefined, | |
| 29 | }; | |
| 30 | } | |
| 31 | ||
| 32 | pub fn add(self: *WriteFileStep, basename: []const u8, bytes: []const u8) void { | |
| 33 | const node = self.builder.allocator.create(std.TailQueue(File).Node) catch unreachable; | |
| 34 | node.* = .{ | |
| 35 | .data = .{ | |
| 36 | .source = build.GeneratedFile{ .step = &self.step }, | |
| 37 | .basename = self.builder.dupePath(basename), | |
| 38 | .bytes = self.builder.dupe(bytes), | |
| 39 | }, | |
| 40 | }; | |
| 41 | ||
| 42 | self.files.append(node); | |
| 43 | } | |
| 44 | ||
| 45 | /// Gets a file source for the given basename. If the file does not exist, returns `null`. | |
| 46 | pub fn getFileSource(step: *WriteFileStep, basename: []const u8) ?build.FileSource { | |
| 47 | var it = step.files.first; | |
| 48 | while (it) |node| : (it = node.next) { | |
| 49 | if (std.mem.eql(u8, node.data.basename, basename)) | |
| 50 | return build.FileSource{ .generated = &node.data.source }; | |
| 51 | } | |
| 52 | return null; | |
| 53 | } | |
| 54 | ||
| 55 | fn make(step: *Step) !void { | |
| 56 | const self = @fieldParentPtr(WriteFileStep, "step", step); | |
| 57 | ||
| 58 | // The cache is used here not really as a way to speed things up - because writing | |
| 59 | // the data to a file would probably be very fast - but as a way to find a canonical | |
| 60 | // location to put build artifacts. | |
| 61 | ||
| 62 | // If, for example, a hard-coded path was used as the location to put WriteFileStep | |
| 63 | // files, then two WriteFileSteps executing in parallel might clobber each other. | |
| 64 | ||
| 65 | // TODO port the cache system from the compiler to zig std lib. Until then | |
| 66 | // we directly construct the path, and no "cache hit" detection happens; | |
| 67 | // the files are always written. | |
| 68 | // Note there is similar code over in ConfigHeaderStep. | |
| 69 | const Hasher = std.crypto.auth.siphash.SipHash128(1, 3); | |
| 70 | // Random bytes to make WriteFileStep unique. Refresh this with | |
| 71 | // new random bytes when WriteFileStep implementation is modified | |
| 72 | // in a non-backwards-compatible way. | |
| 73 | var hash = Hasher.init("eagVR1dYXoE7ARDP"); | |
| 74 | ||
| 75 | { | |
| 76 | var it = self.files.first; | |
| 77 | while (it) |node| : (it = node.next) { | |
| 78 | hash.update(node.data.basename); | |
| 79 | hash.update(node.data.bytes); | |
| 80 | hash.update("|"); | |
| 81 | } | |
| 82 | } | |
| 83 | var digest: [16]u8 = undefined; | |
| 84 | hash.final(&digest); | |
| 85 | var hash_basename: [digest.len * 2]u8 = undefined; | |
| 86 | _ = std.fmt.bufPrint( | |
| 87 | &hash_basename, | |
| 88 | "{s}", | |
| 89 | .{std.fmt.fmtSliceHexLower(&digest)}, | |
| 90 | ) catch unreachable; | |
| 91 | ||
| 92 | self.output_dir = try fs.path.join(self.builder.allocator, &[_][]const u8{ | |
| 93 | self.builder.cache_root, "o", &hash_basename, | |
| 94 | }); | |
| 95 | var dir = fs.cwd().makeOpenPath(self.output_dir, .{}) catch |err| { | |
| 96 | std.debug.print("unable to make path {s}: {s}\n", .{ self.output_dir, @errorName(err) }); | |
| 97 | return err; | |
| 98 | }; | |
| 99 | defer dir.close(); | |
| 100 | { | |
| 101 | var it = self.files.first; | |
| 102 | while (it) |node| : (it = node.next) { | |
| 103 | dir.writeFile(node.data.basename, node.data.bytes) catch |err| { | |
| 104 | std.debug.print("unable to write {s} into {s}: {s}\n", .{ | |
| 105 | node.data.basename, | |
| 106 | self.output_dir, | |
| 107 | @errorName(err), | |
| 108 | }); | |
| 109 | return err; | |
| 110 | }; | |
| 111 | node.data.source.path = fs.path.join( | |
| 112 | self.builder.allocator, | |
| 113 | &[_][]const u8{ self.output_dir, node.data.basename }, | |
| 114 | ) catch unreachable; | |
| 115 | } | |
| 116 | } | |
| 117 | } |
lib/std/builtin.zig+4-1| ... | ... | @@ -131,13 +131,16 @@ pub const CodeModel = enum { |
| 131 | 131 | |
| 132 | 132 | /// This data structure is used by the Zig language code generation and |
| 133 | 133 | /// therefore must be kept in sync with the compiler implementation. |
| 134 | pub const Mode = enum { | |
| 134 | pub const OptimizeMode = enum { | |
| 135 | 135 | Debug, |
| 136 | 136 | ReleaseSafe, |
| 137 | 137 | ReleaseFast, |
| 138 | 138 | ReleaseSmall, |
| 139 | 139 | }; |
| 140 | 140 | |
| 141 | /// Deprecated; use OptimizeMode. | |
| 142 | pub const Mode = OptimizeMode; | |
| 143 | ||
| 141 | 144 | /// This data structure is used by the Zig language code generation and |
| 142 | 145 | /// therefore must be kept in sync with the compiler implementation. |
| 143 | 146 | pub const CallingConvention = enum { |
lib/std/c.zig+2-1| ... | ... | @@ -90,6 +90,8 @@ pub usingnamespace switch (builtin.os.tag) { |
| 90 | 90 | pub extern "c" fn stat(noalias path: [*:0]const u8, noalias buf: *c.Stat) c_int; |
| 91 | 91 | |
| 92 | 92 | pub extern "c" fn alarm(seconds: c_uint) c_uint; |
| 93 | ||
| 94 | pub extern "c" fn msync(addr: *align(page_size) const anyopaque, len: usize, flags: c_int) c_int; | |
| 93 | 95 | }, |
| 94 | 96 | }; |
| 95 | 97 | |
| ... | ... | @@ -145,7 +147,6 @@ pub extern "c" fn write(fd: c.fd_t, buf: [*]const u8, nbyte: usize) isize; |
| 145 | 147 | pub extern "c" fn pwrite(fd: c.fd_t, buf: [*]const u8, nbyte: usize, offset: c.off_t) isize; |
| 146 | 148 | pub extern "c" fn mmap(addr: ?*align(page_size) anyopaque, len: usize, prot: c_uint, flags: c_uint, fd: c.fd_t, offset: c.off_t) *anyopaque; |
| 147 | 149 | pub extern "c" fn munmap(addr: *align(page_size) const anyopaque, len: usize) c_int; |
| 148 | pub extern "c" fn msync(addr: *align(page_size) const anyopaque, len: usize, flags: c_int) c_int; | |
| 149 | 150 | pub extern "c" fn mprotect(addr: *align(page_size) anyopaque, len: usize, prot: c_uint) c_int; |
| 150 | 151 | pub extern "c" fn link(oldpath: [*:0]const u8, newpath: [*:0]const u8, flags: c_int) c_int; |
| 151 | 152 | pub extern "c" fn linkat(oldfd: c.fd_t, oldpath: [*:0]const u8, newfd: c.fd_t, newpath: [*:0]const u8, flags: c_int) c_int; |
lib/std/c/netbsd.zig+3| ... | ... | @@ -59,6 +59,9 @@ pub const sched_yield = __libc_thr_yield; |
| 59 | 59 | |
| 60 | 60 | pub extern "c" fn posix_memalign(memptr: *?*anyopaque, alignment: usize, size: usize) c_int; |
| 61 | 61 | |
| 62 | pub extern "c" fn __msync13(addr: *align(std.mem.page_size) const anyopaque, len: usize, flags: c_int) c_int; | |
| 63 | pub const msync = __msync13; | |
| 64 | ||
| 62 | 65 | pub const pthread_mutex_t = extern struct { |
| 63 | 66 | magic: u32 = 0x33330003, |
| 64 | 67 | errorcheck: padded_pthread_spin_t = 0, |
lib/std/child_process.zig+2-2| ... | ... | @@ -1164,7 +1164,7 @@ fn windowsCreateProcessPathExt( |
| 1164 | 1164 | var app_name_unicode_string = windows.UNICODE_STRING{ |
| 1165 | 1165 | .Length = app_name_len_bytes, |
| 1166 | 1166 | .MaximumLength = app_name_len_bytes, |
| 1167 | .Buffer = @intToPtr([*]u16, @ptrToInt(app_name_wildcard.ptr)), | |
| 1167 | .Buffer = @qualCast([*:0]u16, app_name_wildcard.ptr), | |
| 1168 | 1168 | }; |
| 1169 | 1169 | const rc = windows.ntdll.NtQueryDirectoryFile( |
| 1170 | 1170 | dir.fd, |
| ... | ... | @@ -1261,7 +1261,7 @@ fn windowsCreateProcessPathExt( |
| 1261 | 1261 | var app_name_unicode_string = windows.UNICODE_STRING{ |
| 1262 | 1262 | .Length = app_name_len_bytes, |
| 1263 | 1263 | .MaximumLength = app_name_len_bytes, |
| 1264 | .Buffer = @intToPtr([*]u16, @ptrToInt(app_name_appended.ptr)), | |
| 1264 | .Buffer = @qualCast([*:0]u16, app_name_appended.ptr), | |
| 1265 | 1265 | }; |
| 1266 | 1266 | |
| 1267 | 1267 | // Re-use the directory handle but this time we call with the appended app name |
lib/std/cstr.zig-1| ... | ... | @@ -28,7 +28,6 @@ test "cstr fns" { |
| 28 | 28 | |
| 29 | 29 | fn testCStrFnsImpl() !void { |
| 30 | 30 | try testing.expect(cmp("aoeu", "aoez") == -1); |
| 31 | try testing.expect(mem.len("123456789") == 9); | |
| 32 | 31 | } |
| 33 | 32 | |
| 34 | 33 | /// Returns a mutable, null-terminated slice with the same length as `slice`. |
lib/std/debug.zig+5| ... | ... | @@ -2060,6 +2060,11 @@ pub fn dumpStackPointerAddr(prefix: []const u8) void { |
| 2060 | 2060 | test "manage resources correctly" { |
| 2061 | 2061 | if (builtin.os.tag == .wasi) return error.SkipZigTest; |
| 2062 | 2062 | |
| 2063 | if (builtin.os.tag == .windows and builtin.cpu.arch == .x86_64) { | |
| 2064 | // https://github.com/ziglang/zig/issues/13963 | |
| 2065 | return error.SkipZigTest; | |
| 2066 | } | |
| 2067 | ||
| 2063 | 2068 | const writer = std.io.null_writer; |
| 2064 | 2069 | var di = try openSelfDebugInfo(testing.allocator); |
| 2065 | 2070 | defer di.deinit(); |
lib/std/fmt.zig+6-5| ... | ... | @@ -1,11 +1,12 @@ |
| 1 | 1 | const std = @import("std.zig"); |
| 2 | const builtin = @import("builtin"); | |
| 3 | ||
| 2 | 4 | const io = std.io; |
| 3 | 5 | const math = std.math; |
| 4 | 6 | const assert = std.debug.assert; |
| 5 | 7 | const mem = std.mem; |
| 6 | 8 | const unicode = std.unicode; |
| 7 | 9 | const meta = std.meta; |
| 8 | const builtin = @import("builtin"); | |
| 9 | 10 | const errol = @import("fmt/errol.zig"); |
| 10 | 11 | const lossyCast = std.math.lossyCast; |
| 11 | 12 | const expectFmt = std.testing.expectFmt; |
| ... | ... | @@ -190,7 +191,7 @@ pub fn format( |
| 190 | 191 | .precision = precision, |
| 191 | 192 | }, |
| 192 | 193 | writer, |
| 193 | default_max_depth, | |
| 194 | std.options.fmt_max_depth, | |
| 194 | 195 | ); |
| 195 | 196 | } |
| 196 | 197 | |
| ... | ... | @@ -2140,15 +2141,15 @@ test "buffer" { |
| 2140 | 2141 | { |
| 2141 | 2142 | var buf1: [32]u8 = undefined; |
| 2142 | 2143 | var fbs = std.io.fixedBufferStream(&buf1); |
| 2143 | try formatType(1234, "", FormatOptions{}, fbs.writer(), default_max_depth); | |
| 2144 | try formatType(1234, "", FormatOptions{}, fbs.writer(), std.options.fmt_max_depth); | |
| 2144 | 2145 | try std.testing.expect(mem.eql(u8, fbs.getWritten(), "1234")); |
| 2145 | 2146 | |
| 2146 | 2147 | fbs.reset(); |
| 2147 | try formatType('a', "c", FormatOptions{}, fbs.writer(), default_max_depth); | |
| 2148 | try formatType('a', "c", FormatOptions{}, fbs.writer(), std.options.fmt_max_depth); | |
| 2148 | 2149 | try std.testing.expect(mem.eql(u8, fbs.getWritten(), "a")); |
| 2149 | 2150 | |
| 2150 | 2151 | fbs.reset(); |
| 2151 | try formatType(0b1100, "b", FormatOptions{}, fbs.writer(), default_max_depth); | |
| 2152 | try formatType(0b1100, "b", FormatOptions{}, fbs.writer(), std.options.fmt_max_depth); | |
| 2152 | 2153 | try std.testing.expect(mem.eql(u8, fbs.getWritten(), "1100")); |
| 2153 | 2154 | } |
| 2154 | 2155 | } |
lib/std/fs.zig+2-2| ... | ... | @@ -834,7 +834,7 @@ pub const IterableDir = struct { |
| 834 | 834 | self.end_index = self.index; // Force fd_readdir in the next loop. |
| 835 | 835 | continue :start_over; |
| 836 | 836 | } |
| 837 | const name = mem.span(self.buf[name_index .. name_index + entry.d_namlen]); | |
| 837 | const name = self.buf[name_index .. name_index + entry.d_namlen]; | |
| 838 | 838 | |
| 839 | 839 | const next_index = name_index + entry.d_namlen; |
| 840 | 840 | self.index = next_index; |
| ... | ... | @@ -1763,7 +1763,7 @@ pub const Dir = struct { |
| 1763 | 1763 | var nt_name = w.UNICODE_STRING{ |
| 1764 | 1764 | .Length = path_len_bytes, |
| 1765 | 1765 | .MaximumLength = path_len_bytes, |
| 1766 | .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w)), | |
| 1766 | .Buffer = @qualCast([*:0]u16, sub_path_w), | |
| 1767 | 1767 | }; |
| 1768 | 1768 | var attr = w.OBJECT_ATTRIBUTES{ |
| 1769 | 1769 | .Length = @sizeOf(w.OBJECT_ATTRIBUTES), |
lib/std/fs/file.zig+2-1| ... | ... | @@ -179,7 +179,7 @@ pub const File = struct { |
| 179 | 179 | lock_nonblocking: bool = false, |
| 180 | 180 | |
| 181 | 181 | /// For POSIX systems this is the file system mode the file will |
| 182 | /// be created with. | |
| 182 | /// be created with. On other systems this is always 0. | |
| 183 | 183 | mode: Mode = default_mode, |
| 184 | 184 | |
| 185 | 185 | /// Setting this to `.blocking` prevents `O.NONBLOCK` from being passed even |
| ... | ... | @@ -307,6 +307,7 @@ pub const File = struct { |
| 307 | 307 | /// is unique to each filesystem. |
| 308 | 308 | inode: INode, |
| 309 | 309 | size: u64, |
| 310 | /// This is available on POSIX systems and is always 0 otherwise. | |
| 310 | 311 | mode: Mode, |
| 311 | 312 | kind: Kind, |
| 312 | 313 |
lib/std/io/fixed_buffer_stream.zig+19-6| ... | ... | @@ -113,14 +113,27 @@ pub fn FixedBufferStream(comptime Buffer: type) type { |
| 113 | 113 | }; |
| 114 | 114 | } |
| 115 | 115 | |
| 116 | pub fn fixedBufferStream(buffer: anytype) FixedBufferStream(NonSentinelSpan(@TypeOf(buffer))) { | |
| 117 | return .{ .buffer = mem.span(buffer), .pos = 0 }; | |
| 116 | pub fn fixedBufferStream(buffer: anytype) FixedBufferStream(Slice(@TypeOf(buffer))) { | |
| 117 | return .{ .buffer = buffer, .pos = 0 }; | |
| 118 | 118 | } |
| 119 | 119 | |
| 120 | fn NonSentinelSpan(comptime T: type) type { | |
| 121 | var ptr_info = @typeInfo(mem.Span(T)).Pointer; | |
| 122 | ptr_info.sentinel = null; | |
| 123 | return @Type(.{ .Pointer = ptr_info }); | |
| 120 | fn Slice(comptime T: type) type { | |
| 121 | switch (@typeInfo(T)) { | |
| 122 | .Pointer => |ptr_info| { | |
| 123 | var new_ptr_info = ptr_info; | |
| 124 | switch (ptr_info.size) { | |
| 125 | .Slice => {}, | |
| 126 | .One => switch (@typeInfo(ptr_info.child)) { | |
| 127 | .Array => |info| new_ptr_info.child = info.child, | |
| 128 | else => @compileError("invalid type given to fixedBufferStream"), | |
| 129 | }, | |
| 130 | else => @compileError("invalid type given to fixedBufferStream"), | |
| 131 | } | |
| 132 | new_ptr_info.size = .Slice; | |
| 133 | return @Type(.{ .Pointer = new_ptr_info }); | |
| 134 | }, | |
| 135 | else => @compileError("invalid type given to fixedBufferStream"), | |
| 136 | } | |
| 124 | 137 | } |
| 125 | 138 | |
| 126 | 139 | test "FixedBufferStream output" { |
lib/std/json.zig+2-1| ... | ... | @@ -1384,7 +1384,7 @@ fn ParseInternalErrorImpl(comptime T: type, comptime inferred_types: []const typ |
| 1384 | 1384 | return errors; |
| 1385 | 1385 | }, |
| 1386 | 1386 | .Array => |arrayInfo| { |
| 1387 | return error{ UnexpectedEndOfJson, UnexpectedToken } || TokenStream.Error || | |
| 1387 | return error{ UnexpectedEndOfJson, UnexpectedToken, LengthMismatch } || TokenStream.Error || | |
| 1388 | 1388 | UnescapeValidStringError || |
| 1389 | 1389 | ParseInternalErrorImpl(arrayInfo.child, inferred_types ++ [_]type{T}); |
| 1390 | 1390 | }, |
| ... | ... | @@ -1625,6 +1625,7 @@ fn parseInternal( |
| 1625 | 1625 | if (arrayInfo.child != u8) return error.UnexpectedToken; |
| 1626 | 1626 | var r: T = undefined; |
| 1627 | 1627 | const source_slice = stringToken.slice(tokens.slice, tokens.i - 1); |
| 1628 | if (r.len != stringToken.decodedLength()) return error.LengthMismatch; | |
| 1628 | 1629 | switch (stringToken.escapes) { |
| 1629 | 1630 | .None => mem.copy(u8, &r, source_slice), |
| 1630 | 1631 | .Some => try unescapeValidString(&r, source_slice), |
lib/std/json/test.zig+6| ... | ... | @@ -2238,6 +2238,12 @@ test "parse into struct with no fields" { |
| 2238 | 2238 | try testing.expectEqual(T{}, try parse(T, &ts, ParseOptions{})); |
| 2239 | 2239 | } |
| 2240 | 2240 | |
| 2241 | test "parse into struct where destination and source lengths mismatch" { | |
| 2242 | const T = struct { a: [2]u8 }; | |
| 2243 | var ts = TokenStream.init("{\"a\": \"bbb\"}"); | |
| 2244 | try testing.expectError(error.LengthMismatch, parse(T, &ts, ParseOptions{})); | |
| 2245 | } | |
| 2246 | ||
| 2241 | 2247 | test "parse into struct with misc fields" { |
| 2242 | 2248 | @setEvalBranchQuota(10000); |
| 2243 | 2249 | const options = ParseOptions{ .allocator = testing.allocator }; |
lib/std/mem.zig+24-78| ... | ... | @@ -636,12 +636,9 @@ test "indexOfDiff" { |
| 636 | 636 | try testing.expectEqual(indexOfDiff(u8, "xne", "one"), 0); |
| 637 | 637 | } |
| 638 | 638 | |
| 639 | /// Takes a pointer to an array, a sentinel-terminated pointer, or a slice, and | |
| 640 | /// returns a slice. If there is a sentinel on the input type, there will be a | |
| 641 | /// sentinel on the output type. The constness of the output type matches | |
| 642 | /// the constness of the input type. `[*c]` pointers are assumed to be 0-terminated, | |
| 643 | /// and assumed to not allow null. | |
| 644 | pub fn Span(comptime T: type) type { | |
| 639 | /// Takes a sentinel-terminated pointer and returns a slice preserving pointer attributes. | |
| 640 | /// `[*c]` pointers are assumed to be 0-terminated and assumed to not be allowzero. | |
| 641 | fn Span(comptime T: type) type { | |
| 645 | 642 | switch (@typeInfo(T)) { |
| 646 | 643 | .Optional => |optional_info| { |
| 647 | 644 | return ?Span(optional_info.child); |
| ... | ... | @@ -649,39 +646,22 @@ pub fn Span(comptime T: type) type { |
| 649 | 646 | .Pointer => |ptr_info| { |
| 650 | 647 | var new_ptr_info = ptr_info; |
| 651 | 648 | switch (ptr_info.size) { |
| 652 | .One => switch (@typeInfo(ptr_info.child)) { | |
| 653 | .Array => |info| { | |
| 654 | new_ptr_info.child = info.child; | |
| 655 | new_ptr_info.sentinel = info.sentinel; | |
| 656 | }, | |
| 657 | else => @compileError("invalid type given to std.mem.Span"), | |
| 658 | }, | |
| 659 | 649 | .C => { |
| 660 | 650 | new_ptr_info.sentinel = &@as(ptr_info.child, 0); |
| 661 | 651 | new_ptr_info.is_allowzero = false; |
| 662 | 652 | }, |
| 663 | .Many, .Slice => {}, | |
| 653 | .Many => if (ptr_info.sentinel == null) @compileError("invalid type given to std.mem.span: " ++ @typeName(T)), | |
| 654 | .One, .Slice => @compileError("invalid type given to std.mem.span: " ++ @typeName(T)), | |
| 664 | 655 | } |
| 665 | 656 | new_ptr_info.size = .Slice; |
| 666 | 657 | return @Type(.{ .Pointer = new_ptr_info }); |
| 667 | 658 | }, |
| 668 | else => @compileError("invalid type given to std.mem.Span"), | |
| 659 | else => {}, | |
| 669 | 660 | } |
| 661 | @compileError("invalid type given to std.mem.span: " ++ @typeName(T)); | |
| 670 | 662 | } |
| 671 | 663 | |
| 672 | 664 | test "Span" { |
| 673 | try testing.expect(Span(*[5]u16) == []u16); | |
| 674 | try testing.expect(Span(?*[5]u16) == ?[]u16); | |
| 675 | try testing.expect(Span(*const [5]u16) == []const u16); | |
| 676 | try testing.expect(Span(?*const [5]u16) == ?[]const u16); | |
| 677 | try testing.expect(Span([]u16) == []u16); | |
| 678 | try testing.expect(Span(?[]u16) == ?[]u16); | |
| 679 | try testing.expect(Span([]const u8) == []const u8); | |
| 680 | try testing.expect(Span(?[]const u8) == ?[]const u8); | |
| 681 | try testing.expect(Span([:1]u16) == [:1]u16); | |
| 682 | try testing.expect(Span(?[:1]u16) == ?[:1]u16); | |
| 683 | try testing.expect(Span([:1]const u8) == [:1]const u8); | |
| 684 | try testing.expect(Span(?[:1]const u8) == ?[:1]const u8); | |
| 685 | 665 | try testing.expect(Span([*:1]u16) == [:1]u16); |
| 686 | 666 | try testing.expect(Span(?[*:1]u16) == ?[:1]u16); |
| 687 | 667 | try testing.expect(Span([*:1]const u8) == [:1]const u8); |
| ... | ... | @@ -692,13 +672,10 @@ test "Span" { |
| 692 | 672 | try testing.expect(Span(?[*c]const u8) == ?[:0]const u8); |
| 693 | 673 | } |
| 694 | 674 | |
| 695 | /// Takes a pointer to an array, a sentinel-terminated pointer, or a slice, and | |
| 696 | /// returns a slice. If there is a sentinel on the input type, there will be a | |
| 697 | /// sentinel on the output type. The constness of the output type matches | |
| 698 | /// the constness of the input type. | |
| 699 | /// | |
| 700 | /// When there is both a sentinel and an array length or slice length, the | |
| 701 | /// length value is used instead of the sentinel. | |
| 675 | /// Takes a sentinel-terminated pointer and returns a slice, iterating over the | |
| 676 | /// memory to find the sentinel and determine the length. | |
| 677 | /// Ponter attributes such as const are preserved. | |
| 678 | /// `[*c]` pointers are assumed to be non-null and 0-terminated. | |
| 702 | 679 | pub fn span(ptr: anytype) Span(@TypeOf(ptr)) { |
| 703 | 680 | if (@typeInfo(@TypeOf(ptr)) == .Optional) { |
| 704 | 681 | if (ptr) |non_null| { |
| ... | ... | @@ -722,7 +699,6 @@ test "span" { |
| 722 | 699 | var array: [5]u16 = [_]u16{ 1, 2, 3, 4, 5 }; |
| 723 | 700 | const ptr = @as([*:3]u16, array[0..2 :3]); |
| 724 | 701 | try testing.expect(eql(u16, span(ptr), &[_]u16{ 1, 2 })); |
| 725 | try testing.expect(eql(u16, span(&array), &[_]u16{ 1, 2, 3, 4, 5 })); | |
| 726 | 702 | try testing.expectEqual(@as(?[:0]u16, null), span(@as(?[*:0]u16, null))); |
| 727 | 703 | } |
| 728 | 704 | |
| ... | ... | @@ -919,22 +895,15 @@ test "lenSliceTo" { |
| 919 | 895 | } |
| 920 | 896 | } |
| 921 | 897 | |
| 922 | /// Takes a pointer to an array, an array, a vector, a sentinel-terminated pointer, | |
| 923 | /// a slice or a tuple, and returns the length. | |
| 924 | /// In the case of a sentinel-terminated array, it uses the array length. | |
| 925 | /// For C pointers it assumes it is a pointer-to-many with a 0 sentinel. | |
| 898 | /// Takes a sentinel-terminated pointer and iterates over the memory to find the | |
| 899 | /// sentinel and determine the length. | |
| 900 | /// `[*c]` pointers are assumed to be non-null and 0-terminated. | |
| 926 | 901 | pub fn len(value: anytype) usize { |
| 927 | return switch (@typeInfo(@TypeOf(value))) { | |
| 928 | .Array => |info| info.len, | |
| 929 | .Vector => |info| info.len, | |
| 902 | switch (@typeInfo(@TypeOf(value))) { | |
| 930 | 903 | .Pointer => |info| switch (info.size) { |
| 931 | .One => switch (@typeInfo(info.child)) { | |
| 932 | .Array => value.len, | |
| 933 | else => @compileError("invalid type given to std.mem.len"), | |
| 934 | }, | |
| 935 | 904 | .Many => { |
| 936 | 905 | const sentinel_ptr = info.sentinel orelse |
| 937 | @compileError("length of pointer with no sentinel"); | |
| 906 | @compileError("invalid type given to std.mem.len: " ++ @typeName(@TypeOf(value))); | |
| 938 | 907 | const sentinel = @ptrCast(*align(1) const info.child, sentinel_ptr).*; |
| 939 | 908 | return indexOfSentinel(info.child, sentinel, value); |
| 940 | 909 | }, |
| ... | ... | @@ -942,41 +911,18 @@ pub fn len(value: anytype) usize { |
| 942 | 911 | assert(value != null); |
| 943 | 912 | return indexOfSentinel(info.child, 0, value); |
| 944 | 913 | }, |
| 945 | .Slice => value.len, | |
| 914 | else => @compileError("invalid type given to std.mem.len: " ++ @typeName(@TypeOf(value))), | |
| 946 | 915 | }, |
| 947 | .Struct => |info| if (info.is_tuple) { | |
| 948 | return info.fields.len; | |
| 949 | } else @compileError("invalid type given to std.mem.len"), | |
| 950 | else => @compileError("invalid type given to std.mem.len"), | |
| 951 | }; | |
| 916 | else => @compileError("invalid type given to std.mem.len: " ++ @typeName(@TypeOf(value))), | |
| 917 | } | |
| 952 | 918 | } |
| 953 | 919 | |
| 954 | 920 | test "len" { |
| 955 | try testing.expect(len("aoeu") == 4); | |
| 956 | ||
| 957 | { | |
| 958 | var array: [5]u16 = [_]u16{ 1, 2, 3, 4, 5 }; | |
| 959 | try testing.expect(len(&array) == 5); | |
| 960 | try testing.expect(len(array[0..3]) == 3); | |
| 961 | array[2] = 0; | |
| 962 | const ptr = @as([*:0]u16, array[0..2 :0]); | |
| 963 | try testing.expect(len(ptr) == 2); | |
| 964 | } | |
| 965 | { | |
| 966 | var array: [5:0]u16 = [_:0]u16{ 1, 2, 3, 4, 5 }; | |
| 967 | try testing.expect(len(&array) == 5); | |
| 968 | array[2] = 0; | |
| 969 | try testing.expect(len(&array) == 5); | |
| 970 | } | |
| 971 | { | |
| 972 | const vector: meta.Vector(2, u32) = [2]u32{ 1, 2 }; | |
| 973 | try testing.expect(len(vector) == 2); | |
| 974 | } | |
| 975 | { | |
| 976 | const tuple = .{ 1, 2 }; | |
| 977 | try testing.expect(len(tuple) == 2); | |
| 978 | try testing.expect(tuple[0] == 1); | |
| 979 | } | |
| 921 | var array: [5]u16 = [_]u16{ 1, 2, 0, 4, 5 }; | |
| 922 | const ptr = @as([*:4]u16, array[0..3 :4]); | |
| 923 | try testing.expect(len(ptr) == 3); | |
| 924 | const c_ptr = @as([*c]u16, ptr); | |
| 925 | try testing.expect(len(c_ptr) == 2); | |
| 980 | 926 | } |
| 981 | 927 | |
| 982 | 928 | pub fn indexOfSentinel(comptime Elem: type, comptime sentinel: Elem, ptr: [*:sentinel]const Elem) usize { |
lib/std/os.zig+5-6| ... | ... | @@ -550,7 +550,6 @@ pub fn abort() noreturn { |
| 550 | 550 | exit(0); // TODO choose appropriate exit code |
| 551 | 551 | } |
| 552 | 552 | if (builtin.os.tag == .wasi) { |
| 553 | @breakpoint(); | |
| 554 | 553 | exit(1); |
| 555 | 554 | } |
| 556 | 555 | if (builtin.os.tag == .cuda) { |
| ... | ... | @@ -4514,7 +4513,7 @@ pub fn faccessatW(dirfd: fd_t, sub_path_w: [*:0]const u16, mode: u32, flags: u32 |
| 4514 | 4513 | var nt_name = windows.UNICODE_STRING{ |
| 4515 | 4514 | .Length = path_len_bytes, |
| 4516 | 4515 | .MaximumLength = path_len_bytes, |
| 4517 | .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w)), | |
| 4516 | .Buffer = @qualCast([*:0]u16, sub_path_w), | |
| 4518 | 4517 | }; |
| 4519 | 4518 | var attr = windows.OBJECT_ATTRIBUTES{ |
| 4520 | 4519 | .Length = @sizeOf(windows.OBJECT_ATTRIBUTES), |
| ... | ... | @@ -6029,7 +6028,7 @@ pub fn sendfile( |
| 6029 | 6028 | .BADF => unreachable, // Always a race condition. |
| 6030 | 6029 | .FAULT => unreachable, // Segmentation fault. |
| 6031 | 6030 | .OVERFLOW => unreachable, // We avoid passing too large of a `count`. |
| 6032 | .NOTCONN => unreachable, // `out_fd` is an unconnected socket. | |
| 6031 | .NOTCONN => return error.BrokenPipe, // `out_fd` is an unconnected socket | |
| 6033 | 6032 | |
| 6034 | 6033 | .INVAL, .NOSYS => { |
| 6035 | 6034 | // EINVAL could be any of the following situations: |
| ... | ... | @@ -6097,7 +6096,7 @@ pub fn sendfile( |
| 6097 | 6096 | |
| 6098 | 6097 | .BADF => unreachable, // Always a race condition. |
| 6099 | 6098 | .FAULT => unreachable, // Segmentation fault. |
| 6100 | .NOTCONN => unreachable, // `out_fd` is an unconnected socket. | |
| 6099 | .NOTCONN => return error.BrokenPipe, // `out_fd` is an unconnected socket | |
| 6101 | 6100 | |
| 6102 | 6101 | .INVAL, .OPNOTSUPP, .NOTSOCK, .NOSYS => { |
| 6103 | 6102 | // EINVAL could be any of the following situations: |
| ... | ... | @@ -6179,7 +6178,7 @@ pub fn sendfile( |
| 6179 | 6178 | .BADF => unreachable, // Always a race condition. |
| 6180 | 6179 | .FAULT => unreachable, // Segmentation fault. |
| 6181 | 6180 | .INVAL => unreachable, |
| 6182 | .NOTCONN => unreachable, // `out_fd` is an unconnected socket. | |
| 6181 | .NOTCONN => return error.BrokenPipe, // `out_fd` is an unconnected socket | |
| 6183 | 6182 | |
| 6184 | 6183 | .OPNOTSUPP, .NOTSOCK, .NOSYS => break :sf, |
| 6185 | 6184 | |
| ... | ... | @@ -6473,7 +6472,7 @@ pub fn recvfrom( |
| 6473 | 6472 | .BADF => unreachable, // always a race condition |
| 6474 | 6473 | .FAULT => unreachable, |
| 6475 | 6474 | .INVAL => unreachable, |
| 6476 | .NOTCONN => unreachable, | |
| 6475 | .NOTCONN => return error.SocketNotConnected, | |
| 6477 | 6476 | .NOTSOCK => unreachable, |
| 6478 | 6477 | .INTR => continue, |
| 6479 | 6478 | .AGAIN => return error.WouldBlock, |
lib/std/os/uefi/pool_allocator.zig+1-1| ... | ... | @@ -22,7 +22,7 @@ const UefiPoolAllocator = struct { |
| 22 | 22 | |
| 23 | 23 | assert(len > 0); |
| 24 | 24 | |
| 25 | const ptr_align = 1 << log2_ptr_align; | |
| 25 | const ptr_align = @as(usize, 1) << @intCast(Allocator.Log2Align, log2_ptr_align); | |
| 26 | 26 | |
| 27 | 27 | const metadata_len = mem.alignForward(@sizeOf(usize), ptr_align); |
| 28 | 28 |
lib/std/os/windows.zig+7-7| ... | ... | @@ -85,7 +85,7 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN |
| 85 | 85 | var nt_name = UNICODE_STRING{ |
| 86 | 86 | .Length = path_len_bytes, |
| 87 | 87 | .MaximumLength = path_len_bytes, |
| 88 | .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w.ptr)), | |
| 88 | .Buffer = @qualCast([*]u16, sub_path_w.ptr), | |
| 89 | 89 | }; |
| 90 | 90 | var attr = OBJECT_ATTRIBUTES{ |
| 91 | 91 | .Length = @sizeOf(OBJECT_ATTRIBUTES), |
| ... | ... | @@ -634,7 +634,7 @@ pub fn SetCurrentDirectory(path_name: []const u16) SetCurrentDirectoryError!void |
| 634 | 634 | var nt_name = UNICODE_STRING{ |
| 635 | 635 | .Length = path_len_bytes, |
| 636 | 636 | .MaximumLength = path_len_bytes, |
| 637 | .Buffer = @intToPtr([*]u16, @ptrToInt(path_name.ptr)), | |
| 637 | .Buffer = @qualCast([*]u16, path_name.ptr), | |
| 638 | 638 | }; |
| 639 | 639 | |
| 640 | 640 | const rc = ntdll.RtlSetCurrentDirectory_U(&nt_name); |
| ... | ... | @@ -766,7 +766,7 @@ pub fn ReadLink(dir: ?HANDLE, sub_path_w: []const u16, out_buffer: []u8) ReadLin |
| 766 | 766 | var nt_name = UNICODE_STRING{ |
| 767 | 767 | .Length = path_len_bytes, |
| 768 | 768 | .MaximumLength = path_len_bytes, |
| 769 | .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w.ptr)), | |
| 769 | .Buffer = @qualCast([*]u16, sub_path_w.ptr), | |
| 770 | 770 | }; |
| 771 | 771 | var attr = OBJECT_ATTRIBUTES{ |
| 772 | 772 | .Length = @sizeOf(OBJECT_ATTRIBUTES), |
| ... | ... | @@ -876,7 +876,7 @@ pub fn DeleteFile(sub_path_w: []const u16, options: DeleteFileOptions) DeleteFil |
| 876 | 876 | .Length = path_len_bytes, |
| 877 | 877 | .MaximumLength = path_len_bytes, |
| 878 | 878 | // The Windows API makes this mutable, but it will not mutate here. |
| 879 | .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w.ptr)), | |
| 879 | .Buffer = @qualCast([*]u16, sub_path_w.ptr), | |
| 880 | 880 | }; |
| 881 | 881 | |
| 882 | 882 | if (sub_path_w[0] == '.' and sub_path_w[1] == 0) { |
| ... | ... | @@ -1414,7 +1414,7 @@ pub fn sendmsg( |
| 1414 | 1414 | } |
| 1415 | 1415 | |
| 1416 | 1416 | pub fn sendto(s: ws2_32.SOCKET, buf: [*]const u8, len: usize, flags: u32, to: ?*const ws2_32.sockaddr, to_len: ws2_32.socklen_t) i32 { |
| 1417 | var buffer = ws2_32.WSABUF{ .len = @truncate(u31, len), .buf = @intToPtr([*]u8, @ptrToInt(buf)) }; | |
| 1417 | var buffer = ws2_32.WSABUF{ .len = @truncate(u31, len), .buf = @qualCast([*]u8, buf) }; | |
| 1418 | 1418 | var bytes_send: DWORD = undefined; |
| 1419 | 1419 | if (ws2_32.WSASendTo(s, @ptrCast([*]ws2_32.WSABUF, &buffer), 1, &bytes_send, flags, to, @intCast(i32, to_len), null, null) == ws2_32.SOCKET_ERROR) { |
| 1420 | 1420 | return ws2_32.SOCKET_ERROR; |
| ... | ... | @@ -1876,13 +1876,13 @@ pub fn eqlIgnoreCaseWTF16(a: []const u16, b: []const u16) bool { |
| 1876 | 1876 | const a_string = UNICODE_STRING{ |
| 1877 | 1877 | .Length = a_bytes, |
| 1878 | 1878 | .MaximumLength = a_bytes, |
| 1879 | .Buffer = @intToPtr([*]u16, @ptrToInt(a.ptr)), | |
| 1879 | .Buffer = @qualCast([*]u16, a.ptr), | |
| 1880 | 1880 | }; |
| 1881 | 1881 | const b_bytes = @intCast(u16, b.len * 2); |
| 1882 | 1882 | const b_string = UNICODE_STRING{ |
| 1883 | 1883 | .Length = b_bytes, |
| 1884 | 1884 | .MaximumLength = b_bytes, |
| 1885 | .Buffer = @intToPtr([*]u16, @ptrToInt(b.ptr)), | |
| 1885 | .Buffer = @qualCast([*]u16, b.ptr), | |
| 1886 | 1886 | }; |
| 1887 | 1887 | return ntdll.RtlEqualUnicodeString(&a_string, &b_string, TRUE) == TRUE; |
| 1888 | 1888 | } |
lib/std/start_windows_tls.zig+5-3| ... | ... | @@ -7,12 +7,14 @@ export var _tls_end: u8 linksection(".tls$ZZZ") = 0; |
| 7 | 7 | export var __xl_a: std.os.windows.PIMAGE_TLS_CALLBACK linksection(".CRT$XLA") = null; |
| 8 | 8 | export var __xl_z: std.os.windows.PIMAGE_TLS_CALLBACK linksection(".CRT$XLZ") = null; |
| 9 | 9 | |
| 10 | const tls_array: u32 = 0x2c; | |
| 11 | 10 | comptime { |
| 12 | if (builtin.target.cpu.arch == .x86) { | |
| 11 | if (builtin.target.cpu.arch == .x86 and builtin.zig_backend != .stage2_c) { | |
| 13 | 12 | // The __tls_array is the offset of the ThreadLocalStoragePointer field |
| 14 | 13 | // in the TEB block whose base address held in the %fs segment. |
| 15 | @export(tls_array, .{ .name = "_tls_array" }); | |
| 14 | asm ( | |
| 15 | \\ .global __tls_array | |
| 16 | \\ __tls_array = 0x2C | |
| 17 | ); | |
| 16 | 18 | } |
| 17 | 19 | } |
| 18 | 20 |
lib/std/std.zig+9-1| ... | ... | @@ -9,6 +9,7 @@ pub const AutoArrayHashMapUnmanaged = array_hash_map.AutoArrayHashMapUnmanaged; |
| 9 | 9 | pub const AutoHashMap = hash_map.AutoHashMap; |
| 10 | 10 | pub const AutoHashMapUnmanaged = hash_map.AutoHashMapUnmanaged; |
| 11 | 11 | pub const BoundedArray = @import("bounded_array.zig").BoundedArray; |
| 12 | pub const Build = @import("Build.zig"); | |
| 12 | 13 | pub const BufMap = @import("buf_map.zig").BufMap; |
| 13 | 14 | pub const BufSet = @import("buf_set.zig").BufSet; |
| 14 | 15 | pub const ChildProcess = @import("child_process.zig").ChildProcess; |
| ... | ... | @@ -49,7 +50,6 @@ pub const array_hash_map = @import("array_hash_map.zig"); |
| 49 | 50 | pub const atomic = @import("atomic.zig"); |
| 50 | 51 | pub const base64 = @import("base64.zig"); |
| 51 | 52 | pub const bit_set = @import("bit_set.zig"); |
| 52 | pub const build = @import("build.zig"); | |
| 53 | 53 | pub const builtin = @import("builtin.zig"); |
| 54 | 54 | pub const c = @import("c.zig"); |
| 55 | 55 | pub const coff = @import("coff.zig"); |
| ... | ... | @@ -96,6 +96,9 @@ pub const wasm = @import("wasm.zig"); |
| 96 | 96 | pub const zig = @import("zig.zig"); |
| 97 | 97 | pub const start = @import("start.zig"); |
| 98 | 98 | |
| 99 | /// deprecated: use `Build`. | |
| 100 | pub const build = Build; | |
| 101 | ||
| 99 | 102 | const root = @import("root"); |
| 100 | 103 | const options_override = if (@hasDecl(root, "std_options")) root.std_options else struct {}; |
| 101 | 104 | |
| ... | ... | @@ -150,6 +153,11 @@ pub const options = struct { |
| 150 | 153 | else |
| 151 | 154 | log.defaultLog; |
| 152 | 155 | |
| 156 | pub const fmt_max_depth = if (@hasDecl(options_override, "fmt_max_depth")) | |
| 157 | options_override.fmt_max_depth | |
| 158 | else | |
| 159 | fmt.default_max_depth; | |
| 160 | ||
| 153 | 161 | pub const cryptoRandomSeed: fn (buffer: []u8) void = if (@hasDecl(options_override, "cryptoRandomSeed")) |
| 154 | 162 | options_override.cryptoRandomSeed |
| 155 | 163 | else |
lib/std/tar.zig+23| ... | ... | @@ -1,6 +1,18 @@ |
| 1 | 1 | pub const Options = struct { |
| 2 | 2 | /// Number of directory levels to skip when extracting files. |
| 3 | 3 | strip_components: u32 = 0, |
| 4 | /// How to handle the "mode" property of files from within the tar file. | |
| 5 | mode_mode: ModeMode = .executable_bit_only, | |
| 6 | ||
| 7 | const ModeMode = enum { | |
| 8 | /// The mode from the tar file is completely ignored. Files are created | |
| 9 | /// with the default mode when creating files. | |
| 10 | ignore, | |
| 11 | /// The mode from the tar file is inspected for the owner executable bit | |
| 12 | /// only. This bit is copied to the group and other executable bits. | |
| 13 | /// Other bits of the mode are left as the default when creating files. | |
| 14 | executable_bit_only, | |
| 15 | }; | |
| 4 | 16 | }; |
| 5 | 17 | |
| 6 | 18 | pub const Header = struct { |
| ... | ... | @@ -72,6 +84,17 @@ pub const Header = struct { |
| 72 | 84 | }; |
| 73 | 85 | |
| 74 | 86 | pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: Options) !void { |
| 87 | switch (options.mode_mode) { | |
| 88 | .ignore => {}, | |
| 89 | .executable_bit_only => { | |
| 90 | // This code does not look at the mode bits yet. To implement this feature, | |
| 91 | // the implementation must be adjusted to look at the mode, and check the | |
| 92 | // user executable bit, then call fchmod on newly created files when | |
| 93 | // the executable bit is supposed to be set. | |
| 94 | // It also needs to properly deal with ACLs on Windows. | |
| 95 | @panic("TODO: unimplemented: tar ModeMode.executable_bit_only"); | |
| 96 | }, | |
| 97 | } | |
| 75 | 98 | var file_name_buffer: [255]u8 = undefined; |
| 76 | 99 | var buffer: [512 * 8]u8 = undefined; |
| 77 | 100 | var start: usize = 0; |
lib/std/target.zig+556-4| ... | ... | @@ -702,9 +702,6 @@ pub const Target = struct { |
| 702 | 702 | pub const ShiftInt = std.math.Log2Int(usize); |
| 703 | 703 | |
| 704 | 704 | pub const empty = Set{ .ints = [1]usize{0} ** usize_count }; |
| 705 | pub fn empty_workaround() Set { | |
| 706 | return Set{ .ints = [1]usize{0} ** usize_count }; | |
| 707 | } | |
| 708 | 705 | |
| 709 | 706 | pub fn isEmpty(set: Set) bool { |
| 710 | 707 | return for (set.ints) |x| { |
| ... | ... | @@ -787,7 +784,7 @@ pub const Target = struct { |
| 787 | 784 | return struct { |
| 788 | 785 | /// Populates only the feature bits specified. |
| 789 | 786 | pub fn featureSet(features: []const F) Set { |
| 790 | var x = Set.empty_workaround(); // TODO remove empty_workaround | |
| 787 | var x = Set.empty; | |
| 791 | 788 | for (features) |feature| { |
| 792 | 789 | x.addFeature(@enumToInt(feature)); |
| 793 | 790 | } |
| ... | ... | @@ -1907,6 +1904,561 @@ pub const Target = struct { |
| 1907 | 1904 | => 16, |
| 1908 | 1905 | }; |
| 1909 | 1906 | } |
| 1907 | ||
| 1908 | pub const CType = enum { | |
| 1909 | short, | |
| 1910 | ushort, | |
| 1911 | int, | |
| 1912 | uint, | |
| 1913 | long, | |
| 1914 | ulong, | |
| 1915 | longlong, | |
| 1916 | ulonglong, | |
| 1917 | float, | |
| 1918 | double, | |
| 1919 | longdouble, | |
| 1920 | }; | |
| 1921 | ||
| 1922 | pub fn c_type_byte_size(t: Target, c_type: CType) u16 { | |
| 1923 | return switch (c_type) { | |
| 1924 | .short, | |
| 1925 | .ushort, | |
| 1926 | .int, | |
| 1927 | .uint, | |
| 1928 | .long, | |
| 1929 | .ulong, | |
| 1930 | .longlong, | |
| 1931 | .ulonglong, | |
| 1932 | => @divExact(c_type_bit_size(t, c_type), 8), | |
| 1933 | ||
| 1934 | .float => 4, | |
| 1935 | .double => 8, | |
| 1936 | ||
| 1937 | .longdouble => switch (c_type_bit_size(t, c_type)) { | |
| 1938 | 16 => 2, | |
| 1939 | 32 => 4, | |
| 1940 | 64 => 8, | |
| 1941 | 80 => @intCast(u16, mem.alignForward(10, c_type_alignment(t, .longdouble))), | |
| 1942 | 128 => 16, | |
| 1943 | else => unreachable, | |
| 1944 | }, | |
| 1945 | }; | |
| 1946 | } | |
| 1947 | ||
| 1948 | pub fn c_type_bit_size(target: Target, c_type: CType) u16 { | |
| 1949 | switch (target.os.tag) { | |
| 1950 | .freestanding, .other => switch (target.cpu.arch) { | |
| 1951 | .msp430 => switch (c_type) { | |
| 1952 | .short, .ushort, .int, .uint => return 16, | |
| 1953 | .float, .long, .ulong => return 32, | |
| 1954 | .longlong, .ulonglong, .double, .longdouble => return 64, | |
| 1955 | }, | |
| 1956 | .avr => switch (c_type) { | |
| 1957 | .short, .ushort, .int, .uint => return 16, | |
| 1958 | .long, .ulong, .float, .double, .longdouble => return 32, | |
| 1959 | .longlong, .ulonglong => return 64, | |
| 1960 | }, | |
| 1961 | .tce, .tcele => switch (c_type) { | |
| 1962 | .short, .ushort => return 16, | |
| 1963 | .int, .uint, .long, .ulong, .longlong, .ulonglong => return 32, | |
| 1964 | .float, .double, .longdouble => return 32, | |
| 1965 | }, | |
| 1966 | .mips64, .mips64el => switch (c_type) { | |
| 1967 | .short, .ushort => return 16, | |
| 1968 | .int, .uint, .float => return 32, | |
| 1969 | .long, .ulong => return if (target.abi != .gnuabin32) 64 else 32, | |
| 1970 | .longlong, .ulonglong, .double => return 64, | |
| 1971 | .longdouble => return 128, | |
| 1972 | }, | |
| 1973 | .x86_64 => switch (c_type) { | |
| 1974 | .short, .ushort => return 16, | |
| 1975 | .int, .uint, .float => return 32, | |
| 1976 | .long, .ulong => switch (target.abi) { | |
| 1977 | .gnux32, .muslx32 => return 32, | |
| 1978 | else => return 64, | |
| 1979 | }, | |
| 1980 | .longlong, .ulonglong, .double => return 64, | |
| 1981 | .longdouble => return 80, | |
| 1982 | }, | |
| 1983 | else => switch (c_type) { | |
| 1984 | .short, .ushort => return 16, | |
| 1985 | .int, .uint, .float => return 32, | |
| 1986 | .long, .ulong => return target.cpu.arch.ptrBitWidth(), | |
| 1987 | .longlong, .ulonglong, .double => return 64, | |
| 1988 | .longdouble => switch (target.cpu.arch) { | |
| 1989 | .x86 => switch (target.abi) { | |
| 1990 | .android => return 64, | |
| 1991 | else => return 80, | |
| 1992 | }, | |
| 1993 | ||
| 1994 | .powerpc, | |
| 1995 | .powerpcle, | |
| 1996 | .powerpc64, | |
| 1997 | .powerpc64le, | |
| 1998 | => switch (target.abi) { | |
| 1999 | .musl, | |
| 2000 | .musleabi, | |
| 2001 | .musleabihf, | |
| 2002 | .muslx32, | |
| 2003 | => return 64, | |
| 2004 | else => return 128, | |
| 2005 | }, | |
| 2006 | ||
| 2007 | .riscv32, | |
| 2008 | .riscv64, | |
| 2009 | .aarch64, | |
| 2010 | .aarch64_be, | |
| 2011 | .aarch64_32, | |
| 2012 | .s390x, | |
| 2013 | .sparc, | |
| 2014 | .sparc64, | |
| 2015 | .sparcel, | |
| 2016 | .wasm32, | |
| 2017 | .wasm64, | |
| 2018 | => return 128, | |
| 2019 | ||
| 2020 | else => return 64, | |
| 2021 | }, | |
| 2022 | }, | |
| 2023 | }, | |
| 2024 | ||
| 2025 | .linux, | |
| 2026 | .freebsd, | |
| 2027 | .netbsd, | |
| 2028 | .dragonfly, | |
| 2029 | .openbsd, | |
| 2030 | .wasi, | |
| 2031 | .emscripten, | |
| 2032 | .plan9, | |
| 2033 | .solaris, | |
| 2034 | .haiku, | |
| 2035 | .ananas, | |
| 2036 | .fuchsia, | |
| 2037 | .minix, | |
| 2038 | => switch (target.cpu.arch) { | |
| 2039 | .msp430 => switch (c_type) { | |
| 2040 | .short, .ushort, .int, .uint => return 16, | |
| 2041 | .long, .ulong, .float => return 32, | |
| 2042 | .longlong, .ulonglong, .double, .longdouble => return 64, | |
| 2043 | }, | |
| 2044 | .avr => switch (c_type) { | |
| 2045 | .short, .ushort, .int, .uint => return 16, | |
| 2046 | .long, .ulong, .float, .double, .longdouble => return 32, | |
| 2047 | .longlong, .ulonglong => return 64, | |
| 2048 | }, | |
| 2049 | .tce, .tcele => switch (c_type) { | |
| 2050 | .short, .ushort => return 16, | |
| 2051 | .int, .uint, .long, .ulong, .longlong, .ulonglong => return 32, | |
| 2052 | .float, .double, .longdouble => return 32, | |
| 2053 | }, | |
| 2054 | .mips64, .mips64el => switch (c_type) { | |
| 2055 | .short, .ushort => return 16, | |
| 2056 | .int, .uint, .float => return 32, | |
| 2057 | .long, .ulong => return if (target.abi != .gnuabin32) 64 else 32, | |
| 2058 | .longlong, .ulonglong, .double => return 64, | |
| 2059 | .longdouble => if (target.os.tag == .freebsd) return 64 else return 128, | |
| 2060 | }, | |
| 2061 | .x86_64 => switch (c_type) { | |
| 2062 | .short, .ushort => return 16, | |
| 2063 | .int, .uint, .float => return 32, | |
| 2064 | .long, .ulong => switch (target.abi) { | |
| 2065 | .gnux32, .muslx32 => return 32, | |
| 2066 | else => return 64, | |
| 2067 | }, | |
| 2068 | .longlong, .ulonglong, .double => return 64, | |
| 2069 | .longdouble => return 80, | |
| 2070 | }, | |
| 2071 | else => switch (c_type) { | |
| 2072 | .short, .ushort => return 16, | |
| 2073 | .int, .uint, .float => return 32, | |
| 2074 | .long, .ulong => return target.cpu.arch.ptrBitWidth(), | |
| 2075 | .longlong, .ulonglong, .double => return 64, | |
| 2076 | .longdouble => switch (target.cpu.arch) { | |
| 2077 | .x86 => switch (target.abi) { | |
| 2078 | .android => return 64, | |
| 2079 | else => return 80, | |
| 2080 | }, | |
| 2081 | ||
| 2082 | .powerpc, | |
| 2083 | .powerpcle, | |
| 2084 | => switch (target.abi) { | |
| 2085 | .musl, | |
| 2086 | .musleabi, | |
| 2087 | .musleabihf, | |
| 2088 | .muslx32, | |
| 2089 | => return 64, | |
| 2090 | else => switch (target.os.tag) { | |
| 2091 | .freebsd, .netbsd, .openbsd => return 64, | |
| 2092 | else => return 128, | |
| 2093 | }, | |
| 2094 | }, | |
| 2095 | ||
| 2096 | .powerpc64, | |
| 2097 | .powerpc64le, | |
| 2098 | => switch (target.abi) { | |
| 2099 | .musl, | |
| 2100 | .musleabi, | |
| 2101 | .musleabihf, | |
| 2102 | .muslx32, | |
| 2103 | => return 64, | |
| 2104 | else => switch (target.os.tag) { | |
| 2105 | .freebsd, .openbsd => return 64, | |
| 2106 | else => return 128, | |
| 2107 | }, | |
| 2108 | }, | |
| 2109 | ||
| 2110 | .riscv32, | |
| 2111 | .riscv64, | |
| 2112 | .aarch64, | |
| 2113 | .aarch64_be, | |
| 2114 | .aarch64_32, | |
| 2115 | .s390x, | |
| 2116 | .mips64, | |
| 2117 | .mips64el, | |
| 2118 | .sparc, | |
| 2119 | .sparc64, | |
| 2120 | .sparcel, | |
| 2121 | .wasm32, | |
| 2122 | .wasm64, | |
| 2123 | => return 128, | |
| 2124 | ||
| 2125 | else => return 64, | |
| 2126 | }, | |
| 2127 | }, | |
| 2128 | }, | |
| 2129 | ||
| 2130 | .windows, .uefi => switch (target.cpu.arch) { | |
| 2131 | .x86 => switch (c_type) { | |
| 2132 | .short, .ushort => return 16, | |
| 2133 | .int, .uint, .float => return 32, | |
| 2134 | .long, .ulong => return 32, | |
| 2135 | .longlong, .ulonglong, .double => return 64, | |
| 2136 | .longdouble => switch (target.abi) { | |
| 2137 | .gnu, .gnuilp32, .cygnus => return 80, | |
| 2138 | else => return 64, | |
| 2139 | }, | |
| 2140 | }, | |
| 2141 | .x86_64 => switch (c_type) { | |
| 2142 | .short, .ushort => return 16, | |
| 2143 | .int, .uint, .float => return 32, | |
| 2144 | .long, .ulong => switch (target.abi) { | |
| 2145 | .cygnus => return 64, | |
| 2146 | else => return 32, | |
| 2147 | }, | |
| 2148 | .longlong, .ulonglong, .double => return 64, | |
| 2149 | .longdouble => switch (target.abi) { | |
| 2150 | .gnu, .gnuilp32, .cygnus => return 80, | |
| 2151 | else => return 64, | |
| 2152 | }, | |
| 2153 | }, | |
| 2154 | else => switch (c_type) { | |
| 2155 | .short, .ushort => return 16, | |
| 2156 | .int, .uint, .float => return 32, | |
| 2157 | .long, .ulong => return 32, | |
| 2158 | .longlong, .ulonglong, .double => return 64, | |
| 2159 | .longdouble => return 64, | |
| 2160 | }, | |
| 2161 | }, | |
| 2162 | ||
| 2163 | .macos, .ios, .tvos, .watchos => switch (c_type) { | |
| 2164 | .short, .ushort => return 16, | |
| 2165 | .int, .uint, .float => return 32, | |
| 2166 | .long, .ulong => switch (target.cpu.arch) { | |
| 2167 | .x86, .arm, .aarch64_32 => return 32, | |
| 2168 | .x86_64 => switch (target.abi) { | |
| 2169 | .gnux32, .muslx32 => return 32, | |
| 2170 | else => return 64, | |
| 2171 | }, | |
| 2172 | else => return 64, | |
| 2173 | }, | |
| 2174 | .longlong, .ulonglong, .double => return 64, | |
| 2175 | .longdouble => switch (target.cpu.arch) { | |
| 2176 | .x86 => switch (target.abi) { | |
| 2177 | .android => return 64, | |
| 2178 | else => return 80, | |
| 2179 | }, | |
| 2180 | .x86_64 => return 80, | |
| 2181 | else => return 64, | |
| 2182 | }, | |
| 2183 | }, | |
| 2184 | ||
| 2185 | .nvcl, .cuda => switch (c_type) { | |
| 2186 | .short, .ushort => return 16, | |
| 2187 | .int, .uint, .float => return 32, | |
| 2188 | .long, .ulong => switch (target.cpu.arch) { | |
| 2189 | .nvptx => return 32, | |
| 2190 | .nvptx64 => return 64, | |
| 2191 | else => return 64, | |
| 2192 | }, | |
| 2193 | .longlong, .ulonglong, .double => return 64, | |
| 2194 | .longdouble => return 64, | |
| 2195 | }, | |
| 2196 | ||
| 2197 | .amdhsa, .amdpal => switch (c_type) { | |
| 2198 | .short, .ushort => return 16, | |
| 2199 | .int, .uint, .float => return 32, | |
| 2200 | .long, .ulong, .longlong, .ulonglong, .double => return 64, | |
| 2201 | .longdouble => return 128, | |
| 2202 | }, | |
| 2203 | ||
| 2204 | .cloudabi, | |
| 2205 | .kfreebsd, | |
| 2206 | .lv2, | |
| 2207 | .zos, | |
| 2208 | .rtems, | |
| 2209 | .nacl, | |
| 2210 | .aix, | |
| 2211 | .ps4, | |
| 2212 | .ps5, | |
| 2213 | .elfiamcu, | |
| 2214 | .mesa3d, | |
| 2215 | .contiki, | |
| 2216 | .hermit, | |
| 2217 | .hurd, | |
| 2218 | .opencl, | |
| 2219 | .glsl450, | |
| 2220 | .vulkan, | |
| 2221 | .driverkit, | |
| 2222 | .shadermodel, | |
| 2223 | => @panic("TODO specify the C integer and float type sizes for this OS"), | |
| 2224 | } | |
| 2225 | } | |
| 2226 | ||
| 2227 | pub fn c_type_alignment(target: Target, c_type: CType) u16 { | |
| 2228 | // Overrides for unusual alignments | |
| 2229 | switch (target.cpu.arch) { | |
| 2230 | .avr => switch (c_type) { | |
| 2231 | .short, .ushort => return 2, | |
| 2232 | else => return 1, | |
| 2233 | }, | |
| 2234 | .x86 => switch (target.os.tag) { | |
| 2235 | .windows, .uefi => switch (c_type) { | |
| 2236 | .longlong, .ulonglong, .double => return 8, | |
| 2237 | .longdouble => switch (target.abi) { | |
| 2238 | .gnu, .gnuilp32, .cygnus => return 4, | |
| 2239 | else => return 8, | |
| 2240 | }, | |
| 2241 | else => {}, | |
| 2242 | }, | |
| 2243 | else => {}, | |
| 2244 | }, | |
| 2245 | else => {}, | |
| 2246 | } | |
| 2247 | ||
| 2248 | // Next-power-of-two-aligned, up to a maximum. | |
| 2249 | return @min( | |
| 2250 | std.math.ceilPowerOfTwoAssert(u16, (c_type_bit_size(target, c_type) + 7) / 8), | |
| 2251 | switch (target.cpu.arch) { | |
| 2252 | .arm, .armeb, .thumb, .thumbeb => switch (target.os.tag) { | |
| 2253 | .netbsd => switch (target.abi) { | |
| 2254 | .gnueabi, | |
| 2255 | .gnueabihf, | |
| 2256 | .eabi, | |
| 2257 | .eabihf, | |
| 2258 | .android, | |
| 2259 | .musleabi, | |
| 2260 | .musleabihf, | |
| 2261 | => 8, | |
| 2262 | ||
| 2263 | else => @as(u16, 4), | |
| 2264 | }, | |
| 2265 | .ios, .tvos, .watchos => 4, | |
| 2266 | else => 8, | |
| 2267 | }, | |
| 2268 | ||
| 2269 | .msp430, | |
| 2270 | .avr, | |
| 2271 | => 2, | |
| 2272 | ||
| 2273 | .arc, | |
| 2274 | .csky, | |
| 2275 | .x86, | |
| 2276 | .xcore, | |
| 2277 | .dxil, | |
| 2278 | .loongarch32, | |
| 2279 | .tce, | |
| 2280 | .tcele, | |
| 2281 | .le32, | |
| 2282 | .amdil, | |
| 2283 | .hsail, | |
| 2284 | .spir, | |
| 2285 | .spirv32, | |
| 2286 | .kalimba, | |
| 2287 | .shave, | |
| 2288 | .renderscript32, | |
| 2289 | .ve, | |
| 2290 | .spu_2, | |
| 2291 | .xtensa, | |
| 2292 | => 4, | |
| 2293 | ||
| 2294 | .aarch64_32, | |
| 2295 | .amdgcn, | |
| 2296 | .amdil64, | |
| 2297 | .bpfel, | |
| 2298 | .bpfeb, | |
| 2299 | .hexagon, | |
| 2300 | .hsail64, | |
| 2301 | .loongarch64, | |
| 2302 | .m68k, | |
| 2303 | .mips, | |
| 2304 | .mipsel, | |
| 2305 | .sparc, | |
| 2306 | .sparcel, | |
| 2307 | .sparc64, | |
| 2308 | .lanai, | |
| 2309 | .le64, | |
| 2310 | .nvptx, | |
| 2311 | .nvptx64, | |
| 2312 | .r600, | |
| 2313 | .s390x, | |
| 2314 | .spir64, | |
| 2315 | .spirv64, | |
| 2316 | .renderscript64, | |
| 2317 | => 8, | |
| 2318 | ||
| 2319 | .aarch64, | |
| 2320 | .aarch64_be, | |
| 2321 | .mips64, | |
| 2322 | .mips64el, | |
| 2323 | .powerpc, | |
| 2324 | .powerpcle, | |
| 2325 | .powerpc64, | |
| 2326 | .powerpc64le, | |
| 2327 | .riscv32, | |
| 2328 | .riscv64, | |
| 2329 | .x86_64, | |
| 2330 | .wasm32, | |
| 2331 | .wasm64, | |
| 2332 | => 16, | |
| 2333 | }, | |
| 2334 | ); | |
| 2335 | } | |
| 2336 | ||
| 2337 | pub fn c_type_preferred_alignment(target: Target, c_type: CType) u16 { | |
| 2338 | // Overrides for unusual alignments | |
| 2339 | switch (target.cpu.arch) { | |
| 2340 | .arm, .armeb, .thumb, .thumbeb => switch (target.os.tag) { | |
| 2341 | .netbsd => switch (target.abi) { | |
| 2342 | .gnueabi, | |
| 2343 | .gnueabihf, | |
| 2344 | .eabi, | |
| 2345 | .eabihf, | |
| 2346 | .android, | |
| 2347 | .musleabi, | |
| 2348 | .musleabihf, | |
| 2349 | => {}, | |
| 2350 | ||
| 2351 | else => switch (c_type) { | |
| 2352 | .longdouble => return 4, | |
| 2353 | else => {}, | |
| 2354 | }, | |
| 2355 | }, | |
| 2356 | .ios, .tvos, .watchos => switch (c_type) { | |
| 2357 | .longdouble => return 4, | |
| 2358 | else => {}, | |
| 2359 | }, | |
| 2360 | else => {}, | |
| 2361 | }, | |
| 2362 | .arc => switch (c_type) { | |
| 2363 | .longdouble => return 4, | |
| 2364 | else => {}, | |
| 2365 | }, | |
| 2366 | .avr => switch (c_type) { | |
| 2367 | .int, .uint, .long, .ulong, .float, .longdouble => return 1, | |
| 2368 | .short, .ushort => return 2, | |
| 2369 | .double => return 4, | |
| 2370 | .longlong, .ulonglong => return 8, | |
| 2371 | }, | |
| 2372 | .x86 => switch (target.os.tag) { | |
| 2373 | .windows, .uefi => switch (c_type) { | |
| 2374 | .longdouble => switch (target.abi) { | |
| 2375 | .gnu, .gnuilp32, .cygnus => return 4, | |
| 2376 | else => return 8, | |
| 2377 | }, | |
| 2378 | else => {}, | |
| 2379 | }, | |
| 2380 | else => switch (c_type) { | |
| 2381 | .longdouble => return 4, | |
| 2382 | else => {}, | |
| 2383 | }, | |
| 2384 | }, | |
| 2385 | else => {}, | |
| 2386 | } | |
| 2387 | ||
| 2388 | // Next-power-of-two-aligned, up to a maximum. | |
| 2389 | return @min( | |
| 2390 | std.math.ceilPowerOfTwoAssert(u16, (c_type_bit_size(target, c_type) + 7) / 8), | |
| 2391 | switch (target.cpu.arch) { | |
| 2392 | .msp430 => @as(u16, 2), | |
| 2393 | ||
| 2394 | .csky, | |
| 2395 | .xcore, | |
| 2396 | .dxil, | |
| 2397 | .loongarch32, | |
| 2398 | .tce, | |
| 2399 | .tcele, | |
| 2400 | .le32, | |
| 2401 | .amdil, | |
| 2402 | .hsail, | |
| 2403 | .spir, | |
| 2404 | .spirv32, | |
| 2405 | .kalimba, | |
| 2406 | .shave, | |
| 2407 | .renderscript32, | |
| 2408 | .ve, | |
| 2409 | .spu_2, | |
| 2410 | .xtensa, | |
| 2411 | => 4, | |
| 2412 | ||
| 2413 | .arc, | |
| 2414 | .arm, | |
| 2415 | .armeb, | |
| 2416 | .avr, | |
| 2417 | .thumb, | |
| 2418 | .thumbeb, | |
| 2419 | .aarch64_32, | |
| 2420 | .amdgcn, | |
| 2421 | .amdil64, | |
| 2422 | .bpfel, | |
| 2423 | .bpfeb, | |
| 2424 | .hexagon, | |
| 2425 | .hsail64, | |
| 2426 | .x86, | |
| 2427 | .loongarch64, | |
| 2428 | .m68k, | |
| 2429 | .mips, | |
| 2430 | .mipsel, | |
| 2431 | .sparc, | |
| 2432 | .sparcel, | |
| 2433 | .sparc64, | |
| 2434 | .lanai, | |
| 2435 | .le64, | |
| 2436 | .nvptx, | |
| 2437 | .nvptx64, | |
| 2438 | .r600, | |
| 2439 | .s390x, | |
| 2440 | .spir64, | |
| 2441 | .spirv64, | |
| 2442 | .renderscript64, | |
| 2443 | => 8, | |
| 2444 | ||
| 2445 | .aarch64, | |
| 2446 | .aarch64_be, | |
| 2447 | .mips64, | |
| 2448 | .mips64el, | |
| 2449 | .powerpc, | |
| 2450 | .powerpcle, | |
| 2451 | .powerpc64, | |
| 2452 | .powerpc64le, | |
| 2453 | .riscv32, | |
| 2454 | .riscv64, | |
| 2455 | .x86_64, | |
| 2456 | .wasm32, | |
| 2457 | .wasm64, | |
| 2458 | => 16, | |
| 2459 | }, | |
| 2460 | ); | |
| 2461 | } | |
| 1910 | 2462 | }; |
| 1911 | 2463 | |
| 1912 | 2464 | test { |
lib/std/testing.zig+246| ... | ... | @@ -670,6 +670,252 @@ pub fn expectStringEndsWith(actual: []const u8, expected_ends_with: []const u8) |
| 670 | 670 | return error.TestExpectedEndsWith; |
| 671 | 671 | } |
| 672 | 672 | |
| 673 | /// This function is intended to be used only in tests. When the two values are not | |
| 674 | /// deeply equal, prints diagnostics to stderr to show exactly how they are not equal, | |
| 675 | /// then returns a test failure error. | |
| 676 | /// `actual` is casted to the type of `expected`. | |
| 677 | /// | |
| 678 | /// Deeply equal is defined as follows: | |
| 679 | /// Primitive types are deeply equal if they are equal using `==` operator. | |
| 680 | /// Struct values are deeply equal if their corresponding fields are deeply equal. | |
| 681 | /// Container types(like Array/Slice/Vector) deeply equal when their corresponding elements are deeply equal. | |
| 682 | /// Pointer values are deeply equal if values they point to are deeply equal. | |
| 683 | /// | |
| 684 | /// Note: Self-referential structs are not supported (e.g. things like std.SinglyLinkedList) | |
| 685 | pub fn expectEqualDeep(expected: anytype, actual: @TypeOf(expected)) !void { | |
| 686 | switch (@typeInfo(@TypeOf(actual))) { | |
| 687 | .NoReturn, | |
| 688 | .Opaque, | |
| 689 | .Frame, | |
| 690 | .AnyFrame, | |
| 691 | => @compileError("value of type " ++ @typeName(@TypeOf(actual)) ++ " encountered"), | |
| 692 | ||
| 693 | .Undefined, | |
| 694 | .Null, | |
| 695 | .Void, | |
| 696 | => return, | |
| 697 | ||
| 698 | .Type => { | |
| 699 | if (actual != expected) { | |
| 700 | std.debug.print("expected type {s}, found type {s}\n", .{ @typeName(expected), @typeName(actual) }); | |
| 701 | return error.TestExpectedEqual; | |
| 702 | } | |
| 703 | }, | |
| 704 | ||
| 705 | .Bool, | |
| 706 | .Int, | |
| 707 | .Float, | |
| 708 | .ComptimeFloat, | |
| 709 | .ComptimeInt, | |
| 710 | .EnumLiteral, | |
| 711 | .Enum, | |
| 712 | .Fn, | |
| 713 | .ErrorSet, | |
| 714 | => { | |
| 715 | if (actual != expected) { | |
| 716 | std.debug.print("expected {}, found {}\n", .{ expected, actual }); | |
| 717 | return error.TestExpectedEqual; | |
| 718 | } | |
| 719 | }, | |
| 720 | ||
| 721 | .Pointer => |pointer| { | |
| 722 | switch (pointer.size) { | |
| 723 | // We have no idea what is behind those pointers, so the best we can do is `==` check. | |
| 724 | .C, .Many => { | |
| 725 | if (actual != expected) { | |
| 726 | std.debug.print("expected {*}, found {*}\n", .{ expected, actual }); | |
| 727 | return error.TestExpectedEqual; | |
| 728 | } | |
| 729 | }, | |
| 730 | .One => { | |
| 731 | // Length of those pointers are runtime value, so the best we can do is `==` check. | |
| 732 | switch (@typeInfo(pointer.child)) { | |
| 733 | .Fn, .Opaque => { | |
| 734 | if (actual != expected) { | |
| 735 | std.debug.print("expected {*}, found {*}\n", .{ expected, actual }); | |
| 736 | return error.TestExpectedEqual; | |
| 737 | } | |
| 738 | }, | |
| 739 | else => try expectEqualDeep(expected.*, actual.*), | |
| 740 | } | |
| 741 | }, | |
| 742 | .Slice => { | |
| 743 | if (expected.len != actual.len) { | |
| 744 | std.debug.print("Slice len not the same, expected {d}, found {d}\n", .{ expected.len, actual.len }); | |
| 745 | return error.TestExpectedEqual; | |
| 746 | } | |
| 747 | var i: usize = 0; | |
| 748 | while (i < expected.len) : (i += 1) { | |
| 749 | expectEqualDeep(expected[i], actual[i]) catch |e| { | |
| 750 | std.debug.print("index {d} incorrect. expected {any}, found {any}\n", .{ | |
| 751 | i, expected[i], actual[i], | |
| 752 | }); | |
| 753 | return e; | |
| 754 | }; | |
| 755 | } | |
| 756 | }, | |
| 757 | } | |
| 758 | }, | |
| 759 | ||
| 760 | .Array => |_| { | |
| 761 | if (expected.len != actual.len) { | |
| 762 | std.debug.print("Array len not the same, expected {d}, found {d}\n", .{ expected.len, actual.len }); | |
| 763 | return error.TestExpectedEqual; | |
| 764 | } | |
| 765 | var i: usize = 0; | |
| 766 | while (i < expected.len) : (i += 1) { | |
| 767 | expectEqualDeep(expected[i], actual[i]) catch |e| { | |
| 768 | std.debug.print("index {d} incorrect. expected {any}, found {any}\n", .{ | |
| 769 | i, expected[i], actual[i], | |
| 770 | }); | |
| 771 | return e; | |
| 772 | }; | |
| 773 | } | |
| 774 | }, | |
| 775 | ||
| 776 | .Vector => |info| { | |
| 777 | if (info.len != @typeInfo(@TypeOf(actual)).Vector.len) { | |
| 778 | std.debug.print("Vector len not the same, expected {d}, found {d}\n", .{ info.len, @typeInfo(@TypeOf(actual)).Vector.len }); | |
| 779 | return error.TestExpectedEqual; | |
| 780 | } | |
| 781 | var i: usize = 0; | |
| 782 | while (i < info.len) : (i += 1) { | |
| 783 | expectEqualDeep(expected[i], actual[i]) catch |e| { | |
| 784 | std.debug.print("index {d} incorrect. expected {any}, found {any}\n", .{ | |
| 785 | i, expected[i], actual[i], | |
| 786 | }); | |
| 787 | return e; | |
| 788 | }; | |
| 789 | } | |
| 790 | }, | |
| 791 | ||
| 792 | .Struct => |structType| { | |
| 793 | inline for (structType.fields) |field| { | |
| 794 | expectEqualDeep(@field(expected, field.name), @field(actual, field.name)) catch |e| { | |
| 795 | std.debug.print("Field {s} incorrect. expected {any}, found {any}\n", .{ field.name, @field(expected, field.name), @field(actual, field.name) }); | |
| 796 | return e; | |
| 797 | }; | |
| 798 | } | |
| 799 | }, | |
| 800 | ||
| 801 | .Union => |union_info| { | |
| 802 | if (union_info.tag_type == null) { | |
| 803 | @compileError("Unable to compare untagged union values"); | |
| 804 | } | |
| 805 | ||
| 806 | const Tag = std.meta.Tag(@TypeOf(expected)); | |
| 807 | ||
| 808 | const expectedTag = @as(Tag, expected); | |
| 809 | const actualTag = @as(Tag, actual); | |
| 810 | ||
| 811 | try expectEqual(expectedTag, actualTag); | |
| 812 | ||
| 813 | // we only reach this loop if the tags are equal | |
| 814 | switch (expected) { | |
| 815 | inline else => |val, tag| { | |
| 816 | try expectEqualDeep(val, @field(actual, @tagName(tag))); | |
| 817 | }, | |
| 818 | } | |
| 819 | }, | |
| 820 | ||
| 821 | .Optional => { | |
| 822 | if (expected) |expected_payload| { | |
| 823 | if (actual) |actual_payload| { | |
| 824 | try expectEqualDeep(expected_payload, actual_payload); | |
| 825 | } else { | |
| 826 | std.debug.print("expected {any}, found null\n", .{expected_payload}); | |
| 827 | return error.TestExpectedEqual; | |
| 828 | } | |
| 829 | } else { | |
| 830 | if (actual) |actual_payload| { | |
| 831 | std.debug.print("expected null, found {any}\n", .{actual_payload}); | |
| 832 | return error.TestExpectedEqual; | |
| 833 | } | |
| 834 | } | |
| 835 | }, | |
| 836 | ||
| 837 | .ErrorUnion => { | |
| 838 | if (expected) |expected_payload| { | |
| 839 | if (actual) |actual_payload| { | |
| 840 | try expectEqualDeep(expected_payload, actual_payload); | |
| 841 | } else |actual_err| { | |
| 842 | std.debug.print("expected {any}, found {any}\n", .{ expected_payload, actual_err }); | |
| 843 | return error.TestExpectedEqual; | |
| 844 | } | |
| 845 | } else |expected_err| { | |
| 846 | if (actual) |actual_payload| { | |
| 847 | std.debug.print("expected {any}, found {any}\n", .{ expected_err, actual_payload }); | |
| 848 | return error.TestExpectedEqual; | |
| 849 | } else |actual_err| { | |
| 850 | try expectEqualDeep(expected_err, actual_err); | |
| 851 | } | |
| 852 | } | |
| 853 | }, | |
| 854 | } | |
| 855 | } | |
| 856 | ||
| 857 | test "expectEqualDeep primitive type" { | |
| 858 | try expectEqualDeep(1, 1); | |
| 859 | try expectEqualDeep(true, true); | |
| 860 | try expectEqualDeep(1.5, 1.5); | |
| 861 | try expectEqualDeep(u8, u8); | |
| 862 | try expectEqualDeep(error.Bad, error.Bad); | |
| 863 | ||
| 864 | // optional | |
| 865 | { | |
| 866 | const foo: ?u32 = 1; | |
| 867 | const bar: ?u32 = 1; | |
| 868 | try expectEqualDeep(foo, bar); | |
| 869 | try expectEqualDeep(?u32, ?u32); | |
| 870 | } | |
| 871 | // function type | |
| 872 | { | |
| 873 | const fnType = struct { | |
| 874 | fn foo() void { | |
| 875 | unreachable; | |
| 876 | } | |
| 877 | }.foo; | |
| 878 | try expectEqualDeep(fnType, fnType); | |
| 879 | } | |
| 880 | } | |
| 881 | ||
| 882 | test "expectEqualDeep pointer" { | |
| 883 | const a = 1; | |
| 884 | const b = 1; | |
| 885 | try expectEqualDeep(&a, &b); | |
| 886 | } | |
| 887 | ||
| 888 | test "expectEqualDeep composite type" { | |
| 889 | try expectEqualDeep("abc", "abc"); | |
| 890 | const s1: []const u8 = "abc"; | |
| 891 | const s2 = "abcd"; | |
| 892 | const s3: []const u8 = s2[0..3]; | |
| 893 | try expectEqualDeep(s1, s3); | |
| 894 | ||
| 895 | const TestStruct = struct { s: []const u8 }; | |
| 896 | try expectEqualDeep(TestStruct{ .s = "abc" }, TestStruct{ .s = "abc" }); | |
| 897 | try expectEqualDeep([_][]const u8{ "a", "b", "c" }, [_][]const u8{ "a", "b", "c" }); | |
| 898 | ||
| 899 | // vector | |
| 900 | try expectEqualDeep(@splat(4, @as(u32, 4)), @splat(4, @as(u32, 4))); | |
| 901 | ||
| 902 | // nested array | |
| 903 | { | |
| 904 | const a = [2][2]f32{ | |
| 905 | [_]f32{ 1.0, 0.0 }, | |
| 906 | [_]f32{ 0.0, 1.0 }, | |
| 907 | }; | |
| 908 | ||
| 909 | const b = [2][2]f32{ | |
| 910 | [_]f32{ 1.0, 0.0 }, | |
| 911 | [_]f32{ 0.0, 1.0 }, | |
| 912 | }; | |
| 913 | ||
| 914 | try expectEqualDeep(a, b); | |
| 915 | try expectEqualDeep(&a, &b); | |
| 916 | } | |
| 917 | } | |
| 918 | ||
| 673 | 919 | fn printIndicatorLine(source: []const u8, indicator_index: usize) void { |
| 674 | 920 | const line_begin_index = if (std.mem.lastIndexOfScalar(u8, source[0..indicator_index], '\n')) |line_begin| |
| 675 | 921 | line_begin + 1 |
lib/std/zig.zig-1| ... | ... | @@ -8,7 +8,6 @@ pub const Tokenizer = tokenizer.Tokenizer; |
| 8 | 8 | pub const fmtId = fmt.fmtId; |
| 9 | 9 | pub const fmtEscapes = fmt.fmtEscapes; |
| 10 | 10 | pub const isValidId = fmt.isValidId; |
| 11 | pub const parse = @import("zig/parse.zig").parse; | |
| 12 | 11 | pub const string_literal = @import("zig/string_literal.zig"); |
| 13 | 12 | pub const number_literal = @import("zig/number_literal.zig"); |
| 14 | 13 | pub const primitives = @import("zig/primitives.zig"); |
lib/std/zig/Ast.zig+73-9| ... | ... | @@ -1,4 +1,8 @@ |
| 1 | 1 | //! Abstract Syntax Tree for Zig source code. |
| 2 | //! For Zig syntax, the root node is at nodes[0] and contains the list of | |
| 3 | //! sub-nodes. | |
| 4 | //! For Zon syntax, the root node is at nodes[0] and contains lhs as the node | |
| 5 | //! index of the main expression. | |
| 2 | 6 | |
| 3 | 7 | /// Reference to externally-owned data. |
| 4 | 8 | source: [:0]const u8, |
| ... | ... | @@ -11,13 +15,6 @@ extra_data: []Node.Index, |
| 11 | 15 | |
| 12 | 16 | errors: []const Error, |
| 13 | 17 | |
| 14 | const std = @import("../std.zig"); | |
| 15 | const assert = std.debug.assert; | |
| 16 | const testing = std.testing; | |
| 17 | const mem = std.mem; | |
| 18 | const Token = std.zig.Token; | |
| 19 | const Ast = @This(); | |
| 20 | ||
| 21 | 18 | pub const TokenIndex = u32; |
| 22 | 19 | pub const ByteOffset = u32; |
| 23 | 20 | |
| ... | ... | @@ -34,7 +31,7 @@ pub const Location = struct { |
| 34 | 31 | line_end: usize, |
| 35 | 32 | }; |
| 36 | 33 | |
| 37 | pub fn deinit(tree: *Ast, gpa: mem.Allocator) void { | |
| 34 | pub fn deinit(tree: *Ast, gpa: Allocator) void { | |
| 38 | 35 | tree.tokens.deinit(gpa); |
| 39 | 36 | tree.nodes.deinit(gpa); |
| 40 | 37 | gpa.free(tree.extra_data); |
| ... | ... | @@ -48,11 +45,69 @@ pub const RenderError = error{ |
| 48 | 45 | OutOfMemory, |
| 49 | 46 | }; |
| 50 | 47 | |
| 48 | pub const Mode = enum { zig, zon }; | |
| 49 | ||
| 50 | /// Result should be freed with tree.deinit() when there are | |
| 51 | /// no more references to any of the tokens or nodes. | |
| 52 | pub fn parse(gpa: Allocator, source: [:0]const u8, mode: Mode) Allocator.Error!Ast { | |
| 53 | var tokens = Ast.TokenList{}; | |
| 54 | defer tokens.deinit(gpa); | |
| 55 | ||
| 56 | // Empirically, the zig std lib has an 8:1 ratio of source bytes to token count. | |
| 57 | const estimated_token_count = source.len / 8; | |
| 58 | try tokens.ensureTotalCapacity(gpa, estimated_token_count); | |
| 59 | ||
| 60 | var tokenizer = std.zig.Tokenizer.init(source); | |
| 61 | while (true) { | |
| 62 | const token = tokenizer.next(); | |
| 63 | try tokens.append(gpa, .{ | |
| 64 | .tag = token.tag, | |
| 65 | .start = @intCast(u32, token.loc.start), | |
| 66 | }); | |
| 67 | if (token.tag == .eof) break; | |
| 68 | } | |
| 69 | ||
| 70 | var parser: Parse = .{ | |
| 71 | .source = source, | |
| 72 | .gpa = gpa, | |
| 73 | .token_tags = tokens.items(.tag), | |
| 74 | .token_starts = tokens.items(.start), | |
| 75 | .errors = .{}, | |
| 76 | .nodes = .{}, | |
| 77 | .extra_data = .{}, | |
| 78 | .scratch = .{}, | |
| 79 | .tok_i = 0, | |
| 80 | }; | |
| 81 | defer parser.errors.deinit(gpa); | |
| 82 | defer parser.nodes.deinit(gpa); | |
| 83 | defer parser.extra_data.deinit(gpa); | |
| 84 | defer parser.scratch.deinit(gpa); | |
| 85 | ||
| 86 | // Empirically, Zig source code has a 2:1 ratio of tokens to AST nodes. | |
| 87 | // Make sure at least 1 so we can use appendAssumeCapacity on the root node below. | |
| 88 | const estimated_node_count = (tokens.len + 2) / 2; | |
| 89 | try parser.nodes.ensureTotalCapacity(gpa, estimated_node_count); | |
| 90 | ||
| 91 | switch (mode) { | |
| 92 | .zig => try parser.parseRoot(), | |
| 93 | .zon => try parser.parseZon(), | |
| 94 | } | |
| 95 | ||
| 96 | // TODO experiment with compacting the MultiArrayList slices here | |
| 97 | return Ast{ | |
| 98 | .source = source, | |
| 99 | .tokens = tokens.toOwnedSlice(), | |
| 100 | .nodes = parser.nodes.toOwnedSlice(), | |
| 101 | .extra_data = try parser.extra_data.toOwnedSlice(gpa), | |
| 102 | .errors = try parser.errors.toOwnedSlice(gpa), | |
| 103 | }; | |
| 104 | } | |
| 105 | ||
| 51 | 106 | /// `gpa` is used for allocating the resulting formatted source code, as well as |
| 52 | 107 | /// for allocating extra stack memory if needed, because this function utilizes recursion. |
| 53 | 108 | /// Note: that's not actually true yet, see https://github.com/ziglang/zig/issues/1006. |
| 54 | 109 | /// Caller owns the returned slice of bytes, allocated with `gpa`. |
| 55 | pub fn render(tree: Ast, gpa: mem.Allocator) RenderError![]u8 { | |
| 110 | pub fn render(tree: Ast, gpa: Allocator) RenderError![]u8 { | |
| 56 | 111 | var buffer = std.ArrayList(u8).init(gpa); |
| 57 | 112 | defer buffer.deinit(); |
| 58 | 113 | |
| ... | ... | @@ -3347,3 +3402,12 @@ pub const Node = struct { |
| 3347 | 3402 | rparen: TokenIndex, |
| 3348 | 3403 | }; |
| 3349 | 3404 | }; |
| 3405 | ||
| 3406 | const std = @import("../std.zig"); | |
| 3407 | const assert = std.debug.assert; | |
| 3408 | const testing = std.testing; | |
| 3409 | const mem = std.mem; | |
| 3410 | const Token = std.zig.Token; | |
| 3411 | const Ast = @This(); | |
| 3412 | const Allocator = std.mem.Allocator; | |
| 3413 | const Parse = @import("Parse.zig"); |
lib/std/zig/Parse.zig created+3825| ... | ... | @@ -0,0 +1,3825 @@ |
| 1 | //! Represents in-progress parsing, will be converted to an Ast after completion. | |
| 2 | ||
| 3 | pub const Error = error{ParseError} || Allocator.Error; | |
| 4 | ||
| 5 | gpa: Allocator, | |
| 6 | source: []const u8, | |
| 7 | token_tags: []const Token.Tag, | |
| 8 | token_starts: []const Ast.ByteOffset, | |
| 9 | tok_i: TokenIndex, | |
| 10 | errors: std.ArrayListUnmanaged(AstError), | |
| 11 | nodes: Ast.NodeList, | |
| 12 | extra_data: std.ArrayListUnmanaged(Node.Index), | |
| 13 | scratch: std.ArrayListUnmanaged(Node.Index), | |
| 14 | ||
| 15 | const SmallSpan = union(enum) { | |
| 16 | zero_or_one: Node.Index, | |
| 17 | multi: Node.SubRange, | |
| 18 | }; | |
| 19 | ||
| 20 | const Members = struct { | |
| 21 | len: usize, | |
| 22 | lhs: Node.Index, | |
| 23 | rhs: Node.Index, | |
| 24 | trailing: bool, | |
| 25 | ||
| 26 | fn toSpan(self: Members, p: *Parse) !Node.SubRange { | |
| 27 | if (self.len <= 2) { | |
| 28 | const nodes = [2]Node.Index{ self.lhs, self.rhs }; | |
| 29 | return p.listToSpan(nodes[0..self.len]); | |
| 30 | } else { | |
| 31 | return Node.SubRange{ .start = self.lhs, .end = self.rhs }; | |
| 32 | } | |
| 33 | } | |
| 34 | }; | |
| 35 | ||
| 36 | fn listToSpan(p: *Parse, list: []const Node.Index) !Node.SubRange { | |
| 37 | try p.extra_data.appendSlice(p.gpa, list); | |
| 38 | return Node.SubRange{ | |
| 39 | .start = @intCast(Node.Index, p.extra_data.items.len - list.len), | |
| 40 | .end = @intCast(Node.Index, p.extra_data.items.len), | |
| 41 | }; | |
| 42 | } | |
| 43 | ||
| 44 | fn addNode(p: *Parse, elem: Ast.NodeList.Elem) Allocator.Error!Node.Index { | |
| 45 | const result = @intCast(Node.Index, p.nodes.len); | |
| 46 | try p.nodes.append(p.gpa, elem); | |
| 47 | return result; | |
| 48 | } | |
| 49 | ||
| 50 | fn setNode(p: *Parse, i: usize, elem: Ast.NodeList.Elem) Node.Index { | |
| 51 | p.nodes.set(i, elem); | |
| 52 | return @intCast(Node.Index, i); | |
| 53 | } | |
| 54 | ||
| 55 | fn reserveNode(p: *Parse, tag: Ast.Node.Tag) !usize { | |
| 56 | try p.nodes.resize(p.gpa, p.nodes.len + 1); | |
| 57 | p.nodes.items(.tag)[p.nodes.len - 1] = tag; | |
| 58 | return p.nodes.len - 1; | |
| 59 | } | |
| 60 | ||
| 61 | fn unreserveNode(p: *Parse, node_index: usize) void { | |
| 62 | if (p.nodes.len == node_index) { | |
| 63 | p.nodes.resize(p.gpa, p.nodes.len - 1) catch unreachable; | |
| 64 | } else { | |
| 65 | // There is zombie node left in the tree, let's make it as inoffensive as possible | |
| 66 | // (sadly there's no no-op node) | |
| 67 | p.nodes.items(.tag)[node_index] = .unreachable_literal; | |
| 68 | p.nodes.items(.main_token)[node_index] = p.tok_i; | |
| 69 | } | |
| 70 | } | |
| 71 | ||
| 72 | fn addExtra(p: *Parse, extra: anytype) Allocator.Error!Node.Index { | |
| 73 | const fields = std.meta.fields(@TypeOf(extra)); | |
| 74 | try p.extra_data.ensureUnusedCapacity(p.gpa, fields.len); | |
| 75 | const result = @intCast(u32, p.extra_data.items.len); | |
| 76 | inline for (fields) |field| { | |
| 77 | comptime assert(field.type == Node.Index); | |
| 78 | p.extra_data.appendAssumeCapacity(@field(extra, field.name)); | |
| 79 | } | |
| 80 | return result; | |
| 81 | } | |
| 82 | ||
| 83 | fn warnExpected(p: *Parse, expected_token: Token.Tag) error{OutOfMemory}!void { | |
| 84 | @setCold(true); | |
| 85 | try p.warnMsg(.{ | |
| 86 | .tag = .expected_token, | |
| 87 | .token = p.tok_i, | |
| 88 | .extra = .{ .expected_tag = expected_token }, | |
| 89 | }); | |
| 90 | } | |
| 91 | ||
| 92 | fn warn(p: *Parse, error_tag: AstError.Tag) error{OutOfMemory}!void { | |
| 93 | @setCold(true); | |
| 94 | try p.warnMsg(.{ .tag = error_tag, .token = p.tok_i }); | |
| 95 | } | |
| 96 | ||
| 97 | fn warnMsg(p: *Parse, msg: Ast.Error) error{OutOfMemory}!void { | |
| 98 | @setCold(true); | |
| 99 | switch (msg.tag) { | |
| 100 | .expected_semi_after_decl, | |
| 101 | .expected_semi_after_stmt, | |
| 102 | .expected_comma_after_field, | |
| 103 | .expected_comma_after_arg, | |
| 104 | .expected_comma_after_param, | |
| 105 | .expected_comma_after_initializer, | |
| 106 | .expected_comma_after_switch_prong, | |
| 107 | .expected_semi_or_else, | |
| 108 | .expected_semi_or_lbrace, | |
| 109 | .expected_token, | |
| 110 | .expected_block, | |
| 111 | .expected_block_or_assignment, | |
| 112 | .expected_block_or_expr, | |
| 113 | .expected_block_or_field, | |
| 114 | .expected_expr, | |
| 115 | .expected_expr_or_assignment, | |
| 116 | .expected_fn, | |
| 117 | .expected_inlinable, | |
| 118 | .expected_labelable, | |
| 119 | .expected_param_list, | |
| 120 | .expected_prefix_expr, | |
| 121 | .expected_primary_type_expr, | |
| 122 | .expected_pub_item, | |
| 123 | .expected_return_type, | |
| 124 | .expected_suffix_op, | |
| 125 | .expected_type_expr, | |
| 126 | .expected_var_decl, | |
| 127 | .expected_var_decl_or_fn, | |
| 128 | .expected_loop_payload, | |
| 129 | .expected_container, | |
| 130 | => if (msg.token != 0 and !p.tokensOnSameLine(msg.token - 1, msg.token)) { | |
| 131 | var copy = msg; | |
| 132 | copy.token_is_prev = true; | |
| 133 | copy.token -= 1; | |
| 134 | return p.errors.append(p.gpa, copy); | |
| 135 | }, | |
| 136 | else => {}, | |
| 137 | } | |
| 138 | try p.errors.append(p.gpa, msg); | |
| 139 | } | |
| 140 | ||
| 141 | fn fail(p: *Parse, tag: Ast.Error.Tag) error{ ParseError, OutOfMemory } { | |
| 142 | @setCold(true); | |
| 143 | return p.failMsg(.{ .tag = tag, .token = p.tok_i }); | |
| 144 | } | |
| 145 | ||
| 146 | fn failExpected(p: *Parse, expected_token: Token.Tag) error{ ParseError, OutOfMemory } { | |
| 147 | @setCold(true); | |
| 148 | return p.failMsg(.{ | |
| 149 | .tag = .expected_token, | |
| 150 | .token = p.tok_i, | |
| 151 | .extra = .{ .expected_tag = expected_token }, | |
| 152 | }); | |
| 153 | } | |
| 154 | ||
| 155 | fn failMsg(p: *Parse, msg: Ast.Error) error{ ParseError, OutOfMemory } { | |
| 156 | @setCold(true); | |
| 157 | try p.warnMsg(msg); | |
| 158 | return error.ParseError; | |
| 159 | } | |
| 160 | ||
| 161 | /// Root <- skip container_doc_comment? ContainerMembers eof | |
| 162 | pub fn parseRoot(p: *Parse) !void { | |
| 163 | // Root node must be index 0. | |
| 164 | p.nodes.appendAssumeCapacity(.{ | |
| 165 | .tag = .root, | |
| 166 | .main_token = 0, | |
| 167 | .data = undefined, | |
| 168 | }); | |
| 169 | const root_members = try p.parseContainerMembers(); | |
| 170 | const root_decls = try root_members.toSpan(p); | |
| 171 | if (p.token_tags[p.tok_i] != .eof) { | |
| 172 | try p.warnExpected(.eof); | |
| 173 | } | |
| 174 | p.nodes.items(.data)[0] = .{ | |
| 175 | .lhs = root_decls.start, | |
| 176 | .rhs = root_decls.end, | |
| 177 | }; | |
| 178 | } | |
| 179 | ||
| 180 | /// Parse in ZON mode. Subset of the language. | |
| 181 | /// TODO: set a flag in Parse struct, and honor that flag | |
| 182 | /// by emitting compilation errors when non-zon nodes are encountered. | |
| 183 | pub fn parseZon(p: *Parse) !void { | |
| 184 | // We must use index 0 so that 0 can be used as null elsewhere. | |
| 185 | p.nodes.appendAssumeCapacity(.{ | |
| 186 | .tag = .root, | |
| 187 | .main_token = 0, | |
| 188 | .data = undefined, | |
| 189 | }); | |
| 190 | const node_index = p.expectExpr() catch |err| switch (err) { | |
| 191 | error.ParseError => { | |
| 192 | assert(p.errors.items.len > 0); | |
| 193 | return; | |
| 194 | }, | |
| 195 | else => |e| return e, | |
| 196 | }; | |
| 197 | if (p.token_tags[p.tok_i] != .eof) { | |
| 198 | try p.warnExpected(.eof); | |
| 199 | } | |
| 200 | p.nodes.items(.data)[0] = .{ | |
| 201 | .lhs = node_index, | |
| 202 | .rhs = undefined, | |
| 203 | }; | |
| 204 | } | |
| 205 | ||
| 206 | /// ContainerMembers <- ContainerDeclarations (ContainerField COMMA)* (ContainerField / ContainerDeclarations) | |
| 207 | /// | |
| 208 | /// ContainerDeclarations | |
| 209 | /// <- TestDecl ContainerDeclarations | |
| 210 | /// / ComptimeDecl ContainerDeclarations | |
| 211 | /// / doc_comment? KEYWORD_pub? Decl ContainerDeclarations | |
| 212 | /// / | |
| 213 | /// | |
| 214 | /// ComptimeDecl <- KEYWORD_comptime Block | |
| 215 | fn parseContainerMembers(p: *Parse) !Members { | |
| 216 | const scratch_top = p.scratch.items.len; | |
| 217 | defer p.scratch.shrinkRetainingCapacity(scratch_top); | |
| 218 | ||
| 219 | var field_state: union(enum) { | |
| 220 | /// No fields have been seen. | |
| 221 | none, | |
| 222 | /// Currently parsing fields. | |
| 223 | seen, | |
| 224 | /// Saw fields and then a declaration after them. | |
| 225 | /// Payload is first token of previous declaration. | |
| 226 | end: Node.Index, | |
| 227 | /// There was a declaration between fields, don't report more errors. | |
| 228 | err, | |
| 229 | } = .none; | |
| 230 | ||
| 231 | var last_field: TokenIndex = undefined; | |
| 232 | ||
| 233 | // Skip container doc comments. | |
| 234 | while (p.eatToken(.container_doc_comment)) |_| {} | |
| 235 | ||
| 236 | var trailing = false; | |
| 237 | while (true) { | |
| 238 | const doc_comment = try p.eatDocComments(); | |
| 239 | ||
| 240 | switch (p.token_tags[p.tok_i]) { | |
| 241 | .keyword_test => { | |
| 242 | if (doc_comment) |some| { | |
| 243 | try p.warnMsg(.{ .tag = .test_doc_comment, .token = some }); | |
| 244 | } | |
| 245 | const test_decl_node = try p.expectTestDeclRecoverable(); | |
| 246 | if (test_decl_node != 0) { | |
| 247 | if (field_state == .seen) { | |
| 248 | field_state = .{ .end = test_decl_node }; | |
| 249 | } | |
| 250 | try p.scratch.append(p.gpa, test_decl_node); | |
| 251 | } | |
| 252 | trailing = false; | |
| 253 | }, | |
| 254 | .keyword_comptime => switch (p.token_tags[p.tok_i + 1]) { | |
| 255 | .l_brace => { | |
| 256 | if (doc_comment) |some| { | |
| 257 | try p.warnMsg(.{ .tag = .comptime_doc_comment, .token = some }); | |
| 258 | } | |
| 259 | const comptime_token = p.nextToken(); | |
| 260 | const block = p.parseBlock() catch |err| switch (err) { | |
| 261 | error.OutOfMemory => return error.OutOfMemory, | |
| 262 | error.ParseError => blk: { | |
| 263 | p.findNextContainerMember(); | |
| 264 | break :blk null_node; | |
| 265 | }, | |
| 266 | }; | |
| 267 | if (block != 0) { | |
| 268 | const comptime_node = try p.addNode(.{ | |
| 269 | .tag = .@"comptime", | |
| 270 | .main_token = comptime_token, | |
| 271 | .data = .{ | |
| 272 | .lhs = block, | |
| 273 | .rhs = undefined, | |
| 274 | }, | |
| 275 | }); | |
| 276 | if (field_state == .seen) { | |
| 277 | field_state = .{ .end = comptime_node }; | |
| 278 | } | |
| 279 | try p.scratch.append(p.gpa, comptime_node); | |
| 280 | } | |
| 281 | trailing = false; | |
| 282 | }, | |
| 283 | else => { | |
| 284 | const identifier = p.tok_i; | |
| 285 | defer last_field = identifier; | |
| 286 | const container_field = p.expectContainerField() catch |err| switch (err) { | |
| 287 | error.OutOfMemory => return error.OutOfMemory, | |
| 288 | error.ParseError => { | |
| 289 | p.findNextContainerMember(); | |
| 290 | continue; | |
| 291 | }, | |
| 292 | }; | |
| 293 | switch (field_state) { | |
| 294 | .none => field_state = .seen, | |
| 295 | .err, .seen => {}, | |
| 296 | .end => |node| { | |
| 297 | try p.warnMsg(.{ | |
| 298 | .tag = .decl_between_fields, | |
| 299 | .token = p.nodes.items(.main_token)[node], | |
| 300 | }); | |
| 301 | try p.warnMsg(.{ | |
| 302 | .tag = .previous_field, | |
| 303 | .is_note = true, | |
| 304 | .token = last_field, | |
| 305 | }); | |
| 306 | try p.warnMsg(.{ | |
| 307 | .tag = .next_field, | |
| 308 | .is_note = true, | |
| 309 | .token = identifier, | |
| 310 | }); | |
| 311 | // Continue parsing; error will be reported later. | |
| 312 | field_state = .err; | |
| 313 | }, | |
| 314 | } | |
| 315 | try p.scratch.append(p.gpa, container_field); | |
| 316 | switch (p.token_tags[p.tok_i]) { | |
| 317 | .comma => { | |
| 318 | p.tok_i += 1; | |
| 319 | trailing = true; | |
| 320 | continue; | |
| 321 | }, | |
| 322 | .r_brace, .eof => { | |
| 323 | trailing = false; | |
| 324 | break; | |
| 325 | }, | |
| 326 | else => {}, | |
| 327 | } | |
| 328 | // There is not allowed to be a decl after a field with no comma. | |
| 329 | // Report error but recover parser. | |
| 330 | try p.warn(.expected_comma_after_field); | |
| 331 | p.findNextContainerMember(); | |
| 332 | }, | |
| 333 | }, | |
| 334 | .keyword_pub => { | |
| 335 | p.tok_i += 1; | |
| 336 | const top_level_decl = try p.expectTopLevelDeclRecoverable(); | |
| 337 | if (top_level_decl != 0) { | |
| 338 | if (field_state == .seen) { | |
| 339 | field_state = .{ .end = top_level_decl }; | |
| 340 | } | |
| 341 | try p.scratch.append(p.gpa, top_level_decl); | |
| 342 | } | |
| 343 | trailing = p.token_tags[p.tok_i - 1] == .semicolon; | |
| 344 | }, | |
| 345 | .keyword_usingnamespace => { | |
| 346 | const node = try p.expectUsingNamespaceRecoverable(); | |
| 347 | if (node != 0) { | |
| 348 | if (field_state == .seen) { | |
| 349 | field_state = .{ .end = node }; | |
| 350 | } | |
| 351 | try p.scratch.append(p.gpa, node); | |
| 352 | } | |
| 353 | trailing = p.token_tags[p.tok_i - 1] == .semicolon; | |
| 354 | }, | |
| 355 | .keyword_const, | |
| 356 | .keyword_var, | |
| 357 | .keyword_threadlocal, | |
| 358 | .keyword_export, | |
| 359 | .keyword_extern, | |
| 360 | .keyword_inline, | |
| 361 | .keyword_noinline, | |
| 362 | .keyword_fn, | |
| 363 | => { | |
| 364 | const top_level_decl = try p.expectTopLevelDeclRecoverable(); | |
| 365 | if (top_level_decl != 0) { | |
| 366 | if (field_state == .seen) { | |
| 367 | field_state = .{ .end = top_level_decl }; | |
| 368 | } | |
| 369 | try p.scratch.append(p.gpa, top_level_decl); | |
| 370 | } | |
| 371 | trailing = p.token_tags[p.tok_i - 1] == .semicolon; | |
| 372 | }, | |
| 373 | .eof, .r_brace => { | |
| 374 | if (doc_comment) |tok| { | |
| 375 | try p.warnMsg(.{ | |
| 376 | .tag = .unattached_doc_comment, | |
| 377 | .token = tok, | |
| 378 | }); | |
| 379 | } | |
| 380 | break; | |
| 381 | }, | |
| 382 | else => { | |
| 383 | const c_container = p.parseCStyleContainer() catch |err| switch (err) { | |
| 384 | error.OutOfMemory => return error.OutOfMemory, | |
| 385 | error.ParseError => false, | |
| 386 | }; | |
| 387 | if (c_container) continue; | |
| 388 | ||
| 389 | const identifier = p.tok_i; | |
| 390 | defer last_field = identifier; | |
| 391 | const container_field = p.expectContainerField() catch |err| switch (err) { | |
| 392 | error.OutOfMemory => return error.OutOfMemory, | |
| 393 | error.ParseError => { | |
| 394 | p.findNextContainerMember(); | |
| 395 | continue; | |
| 396 | }, | |
| 397 | }; | |
| 398 | switch (field_state) { | |
| 399 | .none => field_state = .seen, | |
| 400 | .err, .seen => {}, | |
| 401 | .end => |node| { | |
| 402 | try p.warnMsg(.{ | |
| 403 | .tag = .decl_between_fields, | |
| 404 | .token = p.nodes.items(.main_token)[node], | |
| 405 | }); | |
| 406 | try p.warnMsg(.{ | |
| 407 | .tag = .previous_field, | |
| 408 | .is_note = true, | |
| 409 | .token = last_field, | |
| 410 | }); | |
| 411 | try p.warnMsg(.{ | |
| 412 | .tag = .next_field, | |
| 413 | .is_note = true, | |
| 414 | .token = identifier, | |
| 415 | }); | |
| 416 | // Continue parsing; error will be reported later. | |
| 417 | field_state = .err; | |
| 418 | }, | |
| 419 | } | |
| 420 | try p.scratch.append(p.gpa, container_field); | |
| 421 | switch (p.token_tags[p.tok_i]) { | |
| 422 | .comma => { | |
| 423 | p.tok_i += 1; | |
| 424 | trailing = true; | |
| 425 | continue; | |
| 426 | }, | |
| 427 | .r_brace, .eof => { | |
| 428 | trailing = false; | |
| 429 | break; | |
| 430 | }, | |
| 431 | else => {}, | |
| 432 | } | |
| 433 | // There is not allowed to be a decl after a field with no comma. | |
| 434 | // Report error but recover parser. | |
| 435 | try p.warn(.expected_comma_after_field); | |
| 436 | if (p.token_tags[p.tok_i] == .semicolon and p.token_tags[identifier] == .identifier) { | |
| 437 | try p.warnMsg(.{ | |
| 438 | .tag = .var_const_decl, | |
| 439 | .is_note = true, | |
| 440 | .token = identifier, | |
| 441 | }); | |
| 442 | } | |
| 443 | p.findNextContainerMember(); | |
| 444 | continue; | |
| 445 | }, | |
| 446 | } | |
| 447 | } | |
| 448 | ||
| 449 | const items = p.scratch.items[scratch_top..]; | |
| 450 | switch (items.len) { | |
| 451 | 0 => return Members{ | |
| 452 | .len = 0, | |
| 453 | .lhs = 0, | |
| 454 | .rhs = 0, | |
| 455 | .trailing = trailing, | |
| 456 | }, | |
| 457 | 1 => return Members{ | |
| 458 | .len = 1, | |
| 459 | .lhs = items[0], | |
| 460 | .rhs = 0, | |
| 461 | .trailing = trailing, | |
| 462 | }, | |
| 463 | 2 => return Members{ | |
| 464 | .len = 2, | |
| 465 | .lhs = items[0], | |
| 466 | .rhs = items[1], | |
| 467 | .trailing = trailing, | |
| 468 | }, | |
| 469 | else => { | |
| 470 | const span = try p.listToSpan(items); | |
| 471 | return Members{ | |
| 472 | .len = items.len, | |
| 473 | .lhs = span.start, | |
| 474 | .rhs = span.end, | |
| 475 | .trailing = trailing, | |
| 476 | }; | |
| 477 | }, | |
| 478 | } | |
| 479 | } | |
| 480 | ||
| 481 | /// Attempts to find next container member by searching for certain tokens | |
| 482 | fn findNextContainerMember(p: *Parse) void { | |
| 483 | var level: u32 = 0; | |
| 484 | while (true) { | |
| 485 | const tok = p.nextToken(); | |
| 486 | switch (p.token_tags[tok]) { | |
| 487 | // Any of these can start a new top level declaration. | |
| 488 | .keyword_test, | |
| 489 | .keyword_comptime, | |
| 490 | .keyword_pub, | |
| 491 | .keyword_export, | |
| 492 | .keyword_extern, | |
| 493 | .keyword_inline, | |
| 494 | .keyword_noinline, | |
| 495 | .keyword_usingnamespace, | |
| 496 | .keyword_threadlocal, | |
| 497 | .keyword_const, | |
| 498 | .keyword_var, | |
| 499 | .keyword_fn, | |
| 500 | => { | |
| 501 | if (level == 0) { | |
| 502 | p.tok_i -= 1; | |
| 503 | return; | |
| 504 | } | |
| 505 | }, | |
| 506 | .identifier => { | |
| 507 | if (p.token_tags[tok + 1] == .comma and level == 0) { | |
| 508 | p.tok_i -= 1; | |
| 509 | return; | |
| 510 | } | |
| 511 | }, | |
| 512 | .comma, .semicolon => { | |
| 513 | // this decl was likely meant to end here | |
| 514 | if (level == 0) { | |
| 515 | return; | |
| 516 | } | |
| 517 | }, | |
| 518 | .l_paren, .l_bracket, .l_brace => level += 1, | |
| 519 | .r_paren, .r_bracket => { | |
| 520 | if (level != 0) level -= 1; | |
| 521 | }, | |
| 522 | .r_brace => { | |
| 523 | if (level == 0) { | |
| 524 | // end of container, exit | |
| 525 | p.tok_i -= 1; | |
| 526 | return; | |
| 527 | } | |
| 528 | level -= 1; | |
| 529 | }, | |
| 530 | .eof => { | |
| 531 | p.tok_i -= 1; | |
| 532 | return; | |
| 533 | }, | |
| 534 | else => {}, | |
| 535 | } | |
| 536 | } | |
| 537 | } | |
| 538 | ||
| 539 | /// Attempts to find the next statement by searching for a semicolon | |
| 540 | fn findNextStmt(p: *Parse) void { | |
| 541 | var level: u32 = 0; | |
| 542 | while (true) { | |
| 543 | const tok = p.nextToken(); | |
| 544 | switch (p.token_tags[tok]) { | |
| 545 | .l_brace => level += 1, | |
| 546 | .r_brace => { | |
| 547 | if (level == 0) { | |
| 548 | p.tok_i -= 1; | |
| 549 | return; | |
| 550 | } | |
| 551 | level -= 1; | |
| 552 | }, | |
| 553 | .semicolon => { | |
| 554 | if (level == 0) { | |
| 555 | return; | |
| 556 | } | |
| 557 | }, | |
| 558 | .eof => { | |
| 559 | p.tok_i -= 1; | |
| 560 | return; | |
| 561 | }, | |
| 562 | else => {}, | |
| 563 | } | |
| 564 | } | |
| 565 | } | |
| 566 | ||
| 567 | /// TestDecl <- KEYWORD_test (STRINGLITERALSINGLE / IDENTIFIER)? Block | |
| 568 | fn expectTestDecl(p: *Parse) !Node.Index { | |
| 569 | const test_token = p.assertToken(.keyword_test); | |
| 570 | const name_token = switch (p.token_tags[p.nextToken()]) { | |
| 571 | .string_literal, .identifier => p.tok_i - 1, | |
| 572 | else => blk: { | |
| 573 | p.tok_i -= 1; | |
| 574 | break :blk null; | |
| 575 | }, | |
| 576 | }; | |
| 577 | const block_node = try p.parseBlock(); | |
| 578 | if (block_node == 0) return p.fail(.expected_block); | |
| 579 | return p.addNode(.{ | |
| 580 | .tag = .test_decl, | |
| 581 | .main_token = test_token, | |
| 582 | .data = .{ | |
| 583 | .lhs = name_token orelse 0, | |
| 584 | .rhs = block_node, | |
| 585 | }, | |
| 586 | }); | |
| 587 | } | |
| 588 | ||
| 589 | fn expectTestDeclRecoverable(p: *Parse) error{OutOfMemory}!Node.Index { | |
| 590 | return p.expectTestDecl() catch |err| switch (err) { | |
| 591 | error.OutOfMemory => return error.OutOfMemory, | |
| 592 | error.ParseError => { | |
| 593 | p.findNextContainerMember(); | |
| 594 | return null_node; | |
| 595 | }, | |
| 596 | }; | |
| 597 | } | |
| 598 | ||
| 599 | /// Decl | |
| 600 | /// <- (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE? / (KEYWORD_inline / KEYWORD_noinline))? FnProto (SEMICOLON / Block) | |
| 601 | /// / (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE?)? KEYWORD_threadlocal? VarDecl | |
| 602 | /// / KEYWORD_usingnamespace Expr SEMICOLON | |
| 603 | fn expectTopLevelDecl(p: *Parse) !Node.Index { | |
| 604 | const extern_export_inline_token = p.nextToken(); | |
| 605 | var is_extern: bool = false; | |
| 606 | var expect_fn: bool = false; | |
| 607 | var expect_var_or_fn: bool = false; | |
| 608 | switch (p.token_tags[extern_export_inline_token]) { | |
| 609 | .keyword_extern => { | |
| 610 | _ = p.eatToken(.string_literal); | |
| 611 | is_extern = true; | |
| 612 | expect_var_or_fn = true; | |
| 613 | }, | |
| 614 | .keyword_export => expect_var_or_fn = true, | |
| 615 | .keyword_inline, .keyword_noinline => expect_fn = true, | |
| 616 | else => p.tok_i -= 1, | |
| 617 | } | |
| 618 | const fn_proto = try p.parseFnProto(); | |
| 619 | if (fn_proto != 0) { | |
| 620 | switch (p.token_tags[p.tok_i]) { | |
| 621 | .semicolon => { | |
| 622 | p.tok_i += 1; | |
| 623 | return fn_proto; | |
| 624 | }, | |
| 625 | .l_brace => { | |
| 626 | if (is_extern) { | |
| 627 | try p.warnMsg(.{ .tag = .extern_fn_body, .token = extern_export_inline_token }); | |
| 628 | return null_node; | |
| 629 | } | |
| 630 | const fn_decl_index = try p.reserveNode(.fn_decl); | |
| 631 | errdefer p.unreserveNode(fn_decl_index); | |
| 632 | ||
| 633 | const body_block = try p.parseBlock(); | |
| 634 | assert(body_block != 0); | |
| 635 | return p.setNode(fn_decl_index, .{ | |
| 636 | .tag = .fn_decl, | |
| 637 | .main_token = p.nodes.items(.main_token)[fn_proto], | |
| 638 | .data = .{ | |
| 639 | .lhs = fn_proto, | |
| 640 | .rhs = body_block, | |
| 641 | }, | |
| 642 | }); | |
| 643 | }, | |
| 644 | else => { | |
| 645 | // Since parseBlock only return error.ParseError on | |
| 646 | // a missing '}' we can assume this function was | |
| 647 | // supposed to end here. | |
| 648 | try p.warn(.expected_semi_or_lbrace); | |
| 649 | return null_node; | |
| 650 | }, | |
| 651 | } | |
| 652 | } | |
| 653 | if (expect_fn) { | |
| 654 | try p.warn(.expected_fn); | |
| 655 | return error.ParseError; | |
| 656 | } | |
| 657 | ||
| 658 | const thread_local_token = p.eatToken(.keyword_threadlocal); | |
| 659 | const var_decl = try p.parseVarDecl(); | |
| 660 | if (var_decl != 0) { | |
| 661 | try p.expectSemicolon(.expected_semi_after_decl, false); | |
| 662 | return var_decl; | |
| 663 | } | |
| 664 | if (thread_local_token != null) { | |
| 665 | return p.fail(.expected_var_decl); | |
| 666 | } | |
| 667 | if (expect_var_or_fn) { | |
| 668 | return p.fail(.expected_var_decl_or_fn); | |
| 669 | } | |
| 670 | if (p.token_tags[p.tok_i] != .keyword_usingnamespace) { | |
| 671 | return p.fail(.expected_pub_item); | |
| 672 | } | |
| 673 | return p.expectUsingNamespace(); | |
| 674 | } | |
| 675 | ||
| 676 | fn expectTopLevelDeclRecoverable(p: *Parse) error{OutOfMemory}!Node.Index { | |
| 677 | return p.expectTopLevelDecl() catch |err| switch (err) { | |
| 678 | error.OutOfMemory => return error.OutOfMemory, | |
| 679 | error.ParseError => { | |
| 680 | p.findNextContainerMember(); | |
| 681 | return null_node; | |
| 682 | }, | |
| 683 | }; | |
| 684 | } | |
| 685 | ||
| 686 | fn expectUsingNamespace(p: *Parse) !Node.Index { | |
| 687 | const usingnamespace_token = p.assertToken(.keyword_usingnamespace); | |
| 688 | const expr = try p.expectExpr(); | |
| 689 | try p.expectSemicolon(.expected_semi_after_decl, false); | |
| 690 | return p.addNode(.{ | |
| 691 | .tag = .@"usingnamespace", | |
| 692 | .main_token = usingnamespace_token, | |
| 693 | .data = .{ | |
| 694 | .lhs = expr, | |
| 695 | .rhs = undefined, | |
| 696 | }, | |
| 697 | }); | |
| 698 | } | |
| 699 | ||
| 700 | fn expectUsingNamespaceRecoverable(p: *Parse) error{OutOfMemory}!Node.Index { | |
| 701 | return p.expectUsingNamespace() catch |err| switch (err) { | |
| 702 | error.OutOfMemory => return error.OutOfMemory, | |
| 703 | error.ParseError => { | |
| 704 | p.findNextContainerMember(); | |
| 705 | return null_node; | |
| 706 | }, | |
| 707 | }; | |
| 708 | } | |
| 709 | ||
| 710 | /// FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? AddrSpace? LinkSection? CallConv? EXCLAMATIONMARK? TypeExpr | |
| 711 | fn parseFnProto(p: *Parse) !Node.Index { | |
| 712 | const fn_token = p.eatToken(.keyword_fn) orelse return null_node; | |
| 713 | ||
| 714 | // We want the fn proto node to be before its children in the array. | |
| 715 | const fn_proto_index = try p.reserveNode(.fn_proto); | |
| 716 | errdefer p.unreserveNode(fn_proto_index); | |
| 717 | ||
| 718 | _ = p.eatToken(.identifier); | |
| 719 | const params = try p.parseParamDeclList(); | |
| 720 | const align_expr = try p.parseByteAlign(); | |
| 721 | const addrspace_expr = try p.parseAddrSpace(); | |
| 722 | const section_expr = try p.parseLinkSection(); | |
| 723 | const callconv_expr = try p.parseCallconv(); | |
| 724 | _ = p.eatToken(.bang); | |
| 725 | ||
| 726 | const return_type_expr = try p.parseTypeExpr(); | |
| 727 | if (return_type_expr == 0) { | |
| 728 | // most likely the user forgot to specify the return type. | |
| 729 | // Mark return type as invalid and try to continue. | |
| 730 | try p.warn(.expected_return_type); | |
| 731 | } | |
| 732 | ||
| 733 | if (align_expr == 0 and section_expr == 0 and callconv_expr == 0 and addrspace_expr == 0) { | |
| 734 | switch (params) { | |
| 735 | .zero_or_one => |param| return p.setNode(fn_proto_index, .{ | |
| 736 | .tag = .fn_proto_simple, | |
| 737 | .main_token = fn_token, | |
| 738 | .data = .{ | |
| 739 | .lhs = param, | |
| 740 | .rhs = return_type_expr, | |
| 741 | }, | |
| 742 | }), | |
| 743 | .multi => |span| { | |
| 744 | return p.setNode(fn_proto_index, .{ | |
| 745 | .tag = .fn_proto_multi, | |
| 746 | .main_token = fn_token, | |
| 747 | .data = .{ | |
| 748 | .lhs = try p.addExtra(Node.SubRange{ | |
| 749 | .start = span.start, | |
| 750 | .end = span.end, | |
| 751 | }), | |
| 752 | .rhs = return_type_expr, | |
| 753 | }, | |
| 754 | }); | |
| 755 | }, | |
| 756 | } | |
| 757 | } | |
| 758 | switch (params) { | |
| 759 | .zero_or_one => |param| return p.setNode(fn_proto_index, .{ | |
| 760 | .tag = .fn_proto_one, | |
| 761 | .main_token = fn_token, | |
| 762 | .data = .{ | |
| 763 | .lhs = try p.addExtra(Node.FnProtoOne{ | |
| 764 | .param = param, | |
| 765 | .align_expr = align_expr, | |
| 766 | .addrspace_expr = addrspace_expr, | |
| 767 | .section_expr = section_expr, | |
| 768 | .callconv_expr = callconv_expr, | |
| 769 | }), | |
| 770 | .rhs = return_type_expr, | |
| 771 | }, | |
| 772 | }), | |
| 773 | .multi => |span| { | |
| 774 | return p.setNode(fn_proto_index, .{ | |
| 775 | .tag = .fn_proto, | |
| 776 | .main_token = fn_token, | |
| 777 | .data = .{ | |
| 778 | .lhs = try p.addExtra(Node.FnProto{ | |
| 779 | .params_start = span.start, | |
| 780 | .params_end = span.end, | |
| 781 | .align_expr = align_expr, | |
| 782 | .addrspace_expr = addrspace_expr, | |
| 783 | .section_expr = section_expr, | |
| 784 | .callconv_expr = callconv_expr, | |
| 785 | }), | |
| 786 | .rhs = return_type_expr, | |
| 787 | }, | |
| 788 | }); | |
| 789 | }, | |
| 790 | } | |
| 791 | } | |
| 792 | ||
| 793 | /// VarDecl <- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? AddrSpace? LinkSection? (EQUAL Expr)? SEMICOLON | |
| 794 | fn parseVarDecl(p: *Parse) !Node.Index { | |
| 795 | const mut_token = p.eatToken(.keyword_const) orelse | |
| 796 | p.eatToken(.keyword_var) orelse | |
| 797 | return null_node; | |
| 798 | ||
| 799 | _ = try p.expectToken(.identifier); | |
| 800 | const type_node: Node.Index = if (p.eatToken(.colon) == null) 0 else try p.expectTypeExpr(); | |
| 801 | const align_node = try p.parseByteAlign(); | |
| 802 | const addrspace_node = try p.parseAddrSpace(); | |
| 803 | const section_node = try p.parseLinkSection(); | |
| 804 | const init_node: Node.Index = switch (p.token_tags[p.tok_i]) { | |
| 805 | .equal_equal => blk: { | |
| 806 | try p.warn(.wrong_equal_var_decl); | |
| 807 | p.tok_i += 1; | |
| 808 | break :blk try p.expectExpr(); | |
| 809 | }, | |
| 810 | .equal => blk: { | |
| 811 | p.tok_i += 1; | |
| 812 | break :blk try p.expectExpr(); | |
| 813 | }, | |
| 814 | else => 0, | |
| 815 | }; | |
| 816 | if (section_node == 0 and addrspace_node == 0) { | |
| 817 | if (align_node == 0) { | |
| 818 | return p.addNode(.{ | |
| 819 | .tag = .simple_var_decl, | |
| 820 | .main_token = mut_token, | |
| 821 | .data = .{ | |
| 822 | .lhs = type_node, | |
| 823 | .rhs = init_node, | |
| 824 | }, | |
| 825 | }); | |
| 826 | } else if (type_node == 0) { | |
| 827 | return p.addNode(.{ | |
| 828 | .tag = .aligned_var_decl, | |
| 829 | .main_token = mut_token, | |
| 830 | .data = .{ | |
| 831 | .lhs = align_node, | |
| 832 | .rhs = init_node, | |
| 833 | }, | |
| 834 | }); | |
| 835 | } else { | |
| 836 | return p.addNode(.{ | |
| 837 | .tag = .local_var_decl, | |
| 838 | .main_token = mut_token, | |
| 839 | .data = .{ | |
| 840 | .lhs = try p.addExtra(Node.LocalVarDecl{ | |
| 841 | .type_node = type_node, | |
| 842 | .align_node = align_node, | |
| 843 | }), | |
| 844 | .rhs = init_node, | |
| 845 | }, | |
| 846 | }); | |
| 847 | } | |
| 848 | } else { | |
| 849 | return p.addNode(.{ | |
| 850 | .tag = .global_var_decl, | |
| 851 | .main_token = mut_token, | |
| 852 | .data = .{ | |
| 853 | .lhs = try p.addExtra(Node.GlobalVarDecl{ | |
| 854 | .type_node = type_node, | |
| 855 | .align_node = align_node, | |
| 856 | .addrspace_node = addrspace_node, | |
| 857 | .section_node = section_node, | |
| 858 | }), | |
| 859 | .rhs = init_node, | |
| 860 | }, | |
| 861 | }); | |
| 862 | } | |
| 863 | } | |
| 864 | ||
| 865 | /// ContainerField | |
| 866 | /// <- doc_comment? KEYWORD_comptime? IDENTIFIER (COLON TypeExpr)? ByteAlign? (EQUAL Expr)? | |
| 867 | /// / doc_comment? KEYWORD_comptime? (IDENTIFIER COLON)? !KEYWORD_fn TypeExpr ByteAlign? (EQUAL Expr)? | |
| 868 | fn expectContainerField(p: *Parse) !Node.Index { | |
| 869 | var main_token = p.tok_i; | |
| 870 | _ = p.eatToken(.keyword_comptime); | |
| 871 | const tuple_like = p.token_tags[p.tok_i] != .identifier or p.token_tags[p.tok_i + 1] != .colon; | |
| 872 | if (!tuple_like) { | |
| 873 | main_token = p.assertToken(.identifier); | |
| 874 | } | |
| 875 | ||
| 876 | var align_expr: Node.Index = 0; | |
| 877 | var type_expr: Node.Index = 0; | |
| 878 | if (p.eatToken(.colon) != null or tuple_like) { | |
| 879 | type_expr = try p.expectTypeExpr(); | |
| 880 | align_expr = try p.parseByteAlign(); | |
| 881 | } | |
| 882 | ||
| 883 | const value_expr: Node.Index = if (p.eatToken(.equal) == null) 0 else try p.expectExpr(); | |
| 884 | ||
| 885 | if (align_expr == 0) { | |
| 886 | return p.addNode(.{ | |
| 887 | .tag = .container_field_init, | |
| 888 | .main_token = main_token, | |
| 889 | .data = .{ | |
| 890 | .lhs = type_expr, | |
| 891 | .rhs = value_expr, | |
| 892 | }, | |
| 893 | }); | |
| 894 | } else if (value_expr == 0) { | |
| 895 | return p.addNode(.{ | |
| 896 | .tag = .container_field_align, | |
| 897 | .main_token = main_token, | |
| 898 | .data = .{ | |
| 899 | .lhs = type_expr, | |
| 900 | .rhs = align_expr, | |
| 901 | }, | |
| 902 | }); | |
| 903 | } else { | |
| 904 | return p.addNode(.{ | |
| 905 | .tag = .container_field, | |
| 906 | .main_token = main_token, | |
| 907 | .data = .{ | |
| 908 | .lhs = type_expr, | |
| 909 | .rhs = try p.addExtra(Node.ContainerField{ | |
| 910 | .value_expr = value_expr, | |
| 911 | .align_expr = align_expr, | |
| 912 | }), | |
| 913 | }, | |
| 914 | }); | |
| 915 | } | |
| 916 | } | |
| 917 | ||
| 918 | /// Statement | |
| 919 | /// <- KEYWORD_comptime? VarDecl | |
| 920 | /// / KEYWORD_comptime BlockExprStatement | |
| 921 | /// / KEYWORD_nosuspend BlockExprStatement | |
| 922 | /// / KEYWORD_suspend BlockExprStatement | |
| 923 | /// / KEYWORD_defer BlockExprStatement | |
| 924 | /// / KEYWORD_errdefer Payload? BlockExprStatement | |
| 925 | /// / IfStatement | |
| 926 | /// / LabeledStatement | |
| 927 | /// / SwitchExpr | |
| 928 | /// / AssignExpr SEMICOLON | |
| 929 | fn parseStatement(p: *Parse, allow_defer_var: bool) Error!Node.Index { | |
| 930 | const comptime_token = p.eatToken(.keyword_comptime); | |
| 931 | ||
| 932 | if (allow_defer_var) { | |
| 933 | const var_decl = try p.parseVarDecl(); | |
| 934 | if (var_decl != 0) { | |
| 935 | try p.expectSemicolon(.expected_semi_after_decl, true); | |
| 936 | return var_decl; | |
| 937 | } | |
| 938 | } | |
| 939 | ||
| 940 | if (comptime_token) |token| { | |
| 941 | return p.addNode(.{ | |
| 942 | .tag = .@"comptime", | |
| 943 | .main_token = token, | |
| 944 | .data = .{ | |
| 945 | .lhs = try p.expectBlockExprStatement(), | |
| 946 | .rhs = undefined, | |
| 947 | }, | |
| 948 | }); | |
| 949 | } | |
| 950 | ||
| 951 | switch (p.token_tags[p.tok_i]) { | |
| 952 | .keyword_nosuspend => { | |
| 953 | return p.addNode(.{ | |
| 954 | .tag = .@"nosuspend", | |
| 955 | .main_token = p.nextToken(), | |
| 956 | .data = .{ | |
| 957 | .lhs = try p.expectBlockExprStatement(), | |
| 958 | .rhs = undefined, | |
| 959 | }, | |
| 960 | }); | |
| 961 | }, | |
| 962 | .keyword_suspend => { | |
| 963 | const token = p.nextToken(); | |
| 964 | const block_expr = try p.expectBlockExprStatement(); | |
| 965 | return p.addNode(.{ | |
| 966 | .tag = .@"suspend", | |
| 967 | .main_token = token, | |
| 968 | .data = .{ | |
| 969 | .lhs = block_expr, | |
| 970 | .rhs = undefined, | |
| 971 | }, | |
| 972 | }); | |
| 973 | }, | |
| 974 | .keyword_defer => if (allow_defer_var) return p.addNode(.{ | |
| 975 | .tag = .@"defer", | |
| 976 | .main_token = p.nextToken(), | |
| 977 | .data = .{ | |
| 978 | .lhs = undefined, | |
| 979 | .rhs = try p.expectBlockExprStatement(), | |
| 980 | }, | |
| 981 | }), | |
| 982 | .keyword_errdefer => if (allow_defer_var) return p.addNode(.{ | |
| 983 | .tag = .@"errdefer", | |
| 984 | .main_token = p.nextToken(), | |
| 985 | .data = .{ | |
| 986 | .lhs = try p.parsePayload(), | |
| 987 | .rhs = try p.expectBlockExprStatement(), | |
| 988 | }, | |
| 989 | }), | |
| 990 | .keyword_switch => return p.expectSwitchExpr(), | |
| 991 | .keyword_if => return p.expectIfStatement(), | |
| 992 | .keyword_enum, .keyword_struct, .keyword_union => { | |
| 993 | const identifier = p.tok_i + 1; | |
| 994 | if (try p.parseCStyleContainer()) { | |
| 995 | // Return something so that `expectStatement` is happy. | |
| 996 | return p.addNode(.{ | |
| 997 | .tag = .identifier, | |
| 998 | .main_token = identifier, | |
| 999 | .data = .{ | |
| 1000 | .lhs = undefined, | |
| 1001 | .rhs = undefined, | |
| 1002 | }, | |
| 1003 | }); | |
| 1004 | } | |
| 1005 | }, | |
| 1006 | else => {}, | |
| 1007 | } | |
| 1008 | ||
| 1009 | const labeled_statement = try p.parseLabeledStatement(); | |
| 1010 | if (labeled_statement != 0) return labeled_statement; | |
| 1011 | ||
| 1012 | const assign_expr = try p.parseAssignExpr(); | |
| 1013 | if (assign_expr != 0) { | |
| 1014 | try p.expectSemicolon(.expected_semi_after_stmt, true); | |
| 1015 | return assign_expr; | |
| 1016 | } | |
| 1017 | ||
| 1018 | return null_node; | |
| 1019 | } | |
| 1020 | ||
| 1021 | fn expectStatement(p: *Parse, allow_defer_var: bool) !Node.Index { | |
| 1022 | const statement = try p.parseStatement(allow_defer_var); | |
| 1023 | if (statement == 0) { | |
| 1024 | return p.fail(.expected_statement); | |
| 1025 | } | |
| 1026 | return statement; | |
| 1027 | } | |
| 1028 | ||
| 1029 | /// If a parse error occurs, reports an error, but then finds the next statement | |
| 1030 | /// and returns that one instead. If a parse error occurs but there is no following | |
| 1031 | /// statement, returns 0. | |
| 1032 | fn expectStatementRecoverable(p: *Parse) Error!Node.Index { | |
| 1033 | while (true) { | |
| 1034 | return p.expectStatement(true) catch |err| switch (err) { | |
| 1035 | error.OutOfMemory => return error.OutOfMemory, | |
| 1036 | error.ParseError => { | |
| 1037 | p.findNextStmt(); // Try to skip to the next statement. | |
| 1038 | switch (p.token_tags[p.tok_i]) { | |
| 1039 | .r_brace => return null_node, | |
| 1040 | .eof => return error.ParseError, | |
| 1041 | else => continue, | |
| 1042 | } | |
| 1043 | }, | |
| 1044 | }; | |
| 1045 | } | |
| 1046 | } | |
| 1047 | ||
| 1048 | /// IfStatement | |
| 1049 | /// <- IfPrefix BlockExpr ( KEYWORD_else Payload? Statement )? | |
| 1050 | /// / IfPrefix AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement ) | |
| 1051 | fn expectIfStatement(p: *Parse) !Node.Index { | |
| 1052 | const if_token = p.assertToken(.keyword_if); | |
| 1053 | _ = try p.expectToken(.l_paren); | |
| 1054 | const condition = try p.expectExpr(); | |
| 1055 | _ = try p.expectToken(.r_paren); | |
| 1056 | _ = try p.parsePtrPayload(); | |
| 1057 | ||
| 1058 | // TODO propose to change the syntax so that semicolons are always required | |
| 1059 | // inside if statements, even if there is an `else`. | |
| 1060 | var else_required = false; | |
| 1061 | const then_expr = blk: { | |
| 1062 | const block_expr = try p.parseBlockExpr(); | |
| 1063 | if (block_expr != 0) break :blk block_expr; | |
| 1064 | const assign_expr = try p.parseAssignExpr(); | |
| 1065 | if (assign_expr == 0) { | |
| 1066 | return p.fail(.expected_block_or_assignment); | |
| 1067 | } | |
| 1068 | if (p.eatToken(.semicolon)) |_| { | |
| 1069 | return p.addNode(.{ | |
| 1070 | .tag = .if_simple, | |
| 1071 | .main_token = if_token, | |
| 1072 | .data = .{ | |
| 1073 | .lhs = condition, | |
| 1074 | .rhs = assign_expr, | |
| 1075 | }, | |
| 1076 | }); | |
| 1077 | } | |
| 1078 | else_required = true; | |
| 1079 | break :blk assign_expr; | |
| 1080 | }; | |
| 1081 | _ = p.eatToken(.keyword_else) orelse { | |
| 1082 | if (else_required) { | |
| 1083 | try p.warn(.expected_semi_or_else); | |
| 1084 | } | |
| 1085 | return p.addNode(.{ | |
| 1086 | .tag = .if_simple, | |
| 1087 | .main_token = if_token, | |
| 1088 | .data = .{ | |
| 1089 | .lhs = condition, | |
| 1090 | .rhs = then_expr, | |
| 1091 | }, | |
| 1092 | }); | |
| 1093 | }; | |
| 1094 | _ = try p.parsePayload(); | |
| 1095 | const else_expr = try p.expectStatement(false); | |
| 1096 | return p.addNode(.{ | |
| 1097 | .tag = .@"if", | |
| 1098 | .main_token = if_token, | |
| 1099 | .data = .{ | |
| 1100 | .lhs = condition, | |
| 1101 | .rhs = try p.addExtra(Node.If{ | |
| 1102 | .then_expr = then_expr, | |
| 1103 | .else_expr = else_expr, | |
| 1104 | }), | |
| 1105 | }, | |
| 1106 | }); | |
| 1107 | } | |
| 1108 | ||
| 1109 | /// LabeledStatement <- BlockLabel? (Block / LoopStatement) | |
| 1110 | fn parseLabeledStatement(p: *Parse) !Node.Index { | |
| 1111 | const label_token = p.parseBlockLabel(); | |
| 1112 | const block = try p.parseBlock(); | |
| 1113 | if (block != 0) return block; | |
| 1114 | ||
| 1115 | const loop_stmt = try p.parseLoopStatement(); | |
| 1116 | if (loop_stmt != 0) return loop_stmt; | |
| 1117 | ||
| 1118 | if (label_token != 0) { | |
| 1119 | const after_colon = p.tok_i; | |
| 1120 | const node = try p.parseTypeExpr(); | |
| 1121 | if (node != 0) { | |
| 1122 | const a = try p.parseByteAlign(); | |
| 1123 | const b = try p.parseAddrSpace(); | |
| 1124 | const c = try p.parseLinkSection(); | |
| 1125 | const d = if (p.eatToken(.equal) == null) 0 else try p.expectExpr(); | |
| 1126 | if (a != 0 or b != 0 or c != 0 or d != 0) { | |
| 1127 | return p.failMsg(.{ .tag = .expected_var_const, .token = label_token }); | |
| 1128 | } | |
| 1129 | } | |
| 1130 | return p.failMsg(.{ .tag = .expected_labelable, .token = after_colon }); | |
| 1131 | } | |
| 1132 | ||
| 1133 | return null_node; | |
| 1134 | } | |
| 1135 | ||
| 1136 | /// LoopStatement <- KEYWORD_inline? (ForStatement / WhileStatement) | |
| 1137 | fn parseLoopStatement(p: *Parse) !Node.Index { | |
| 1138 | const inline_token = p.eatToken(.keyword_inline); | |
| 1139 | ||
| 1140 | const for_statement = try p.parseForStatement(); | |
| 1141 | if (for_statement != 0) return for_statement; | |
| 1142 | ||
| 1143 | const while_statement = try p.parseWhileStatement(); | |
| 1144 | if (while_statement != 0) return while_statement; | |
| 1145 | ||
| 1146 | if (inline_token == null) return null_node; | |
| 1147 | ||
| 1148 | // If we've seen "inline", there should have been a "for" or "while" | |
| 1149 | return p.fail(.expected_inlinable); | |
| 1150 | } | |
| 1151 | ||
| 1152 | /// ForPrefix <- KEYWORD_for LPAREN Expr RPAREN PtrIndexPayload | |
| 1153 | /// | |
| 1154 | /// ForStatement | |
| 1155 | /// <- ForPrefix BlockExpr ( KEYWORD_else Statement )? | |
| 1156 | /// / ForPrefix AssignExpr ( SEMICOLON / KEYWORD_else Statement ) | |
| 1157 | fn parseForStatement(p: *Parse) !Node.Index { | |
| 1158 | const for_token = p.eatToken(.keyword_for) orelse return null_node; | |
| 1159 | _ = try p.expectToken(.l_paren); | |
| 1160 | const array_expr = try p.expectExpr(); | |
| 1161 | _ = try p.expectToken(.r_paren); | |
| 1162 | const found_payload = try p.parsePtrIndexPayload(); | |
| 1163 | if (found_payload == 0) try p.warn(.expected_loop_payload); | |
| 1164 | ||
| 1165 | // TODO propose to change the syntax so that semicolons are always required | |
| 1166 | // inside while statements, even if there is an `else`. | |
| 1167 | var else_required = false; | |
| 1168 | const then_expr = blk: { | |
| 1169 | const block_expr = try p.parseBlockExpr(); | |
| 1170 | if (block_expr != 0) break :blk block_expr; | |
| 1171 | const assign_expr = try p.parseAssignExpr(); | |
| 1172 | if (assign_expr == 0) { | |
| 1173 | return p.fail(.expected_block_or_assignment); | |
| 1174 | } | |
| 1175 | if (p.eatToken(.semicolon)) |_| { | |
| 1176 | return p.addNode(.{ | |
| 1177 | .tag = .for_simple, | |
| 1178 | .main_token = for_token, | |
| 1179 | .data = .{ | |
| 1180 | .lhs = array_expr, | |
| 1181 | .rhs = assign_expr, | |
| 1182 | }, | |
| 1183 | }); | |
| 1184 | } | |
| 1185 | else_required = true; | |
| 1186 | break :blk assign_expr; | |
| 1187 | }; | |
| 1188 | _ = p.eatToken(.keyword_else) orelse { | |
| 1189 | if (else_required) { | |
| 1190 | try p.warn(.expected_semi_or_else); | |
| 1191 | } | |
| 1192 | return p.addNode(.{ | |
| 1193 | .tag = .for_simple, | |
| 1194 | .main_token = for_token, | |
| 1195 | .data = .{ | |
| 1196 | .lhs = array_expr, | |
| 1197 | .rhs = then_expr, | |
| 1198 | }, | |
| 1199 | }); | |
| 1200 | }; | |
| 1201 | return p.addNode(.{ | |
| 1202 | .tag = .@"for", | |
| 1203 | .main_token = for_token, | |
| 1204 | .data = .{ | |
| 1205 | .lhs = array_expr, | |
| 1206 | .rhs = try p.addExtra(Node.If{ | |
| 1207 | .then_expr = then_expr, | |
| 1208 | .else_expr = try p.expectStatement(false), | |
| 1209 | }), | |
| 1210 | }, | |
| 1211 | }); | |
| 1212 | } | |
| 1213 | ||
| 1214 | /// WhilePrefix <- KEYWORD_while LPAREN Expr RPAREN PtrPayload? WhileContinueExpr? | |
| 1215 | /// | |
| 1216 | /// WhileStatement | |
| 1217 | /// <- WhilePrefix BlockExpr ( KEYWORD_else Payload? Statement )? | |
| 1218 | /// / WhilePrefix AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement ) | |
| 1219 | fn parseWhileStatement(p: *Parse) !Node.Index { | |
| 1220 | const while_token = p.eatToken(.keyword_while) orelse return null_node; | |
| 1221 | _ = try p.expectToken(.l_paren); | |
| 1222 | const condition = try p.expectExpr(); | |
| 1223 | _ = try p.expectToken(.r_paren); | |
| 1224 | _ = try p.parsePtrPayload(); | |
| 1225 | const cont_expr = try p.parseWhileContinueExpr(); | |
| 1226 | ||
| 1227 | // TODO propose to change the syntax so that semicolons are always required | |
| 1228 | // inside while statements, even if there is an `else`. | |
| 1229 | var else_required = false; | |
| 1230 | const then_expr = blk: { | |
| 1231 | const block_expr = try p.parseBlockExpr(); | |
| 1232 | if (block_expr != 0) break :blk block_expr; | |
| 1233 | const assign_expr = try p.parseAssignExpr(); | |
| 1234 | if (assign_expr == 0) { | |
| 1235 | return p.fail(.expected_block_or_assignment); | |
| 1236 | } | |
| 1237 | if (p.eatToken(.semicolon)) |_| { | |
| 1238 | if (cont_expr == 0) { | |
| 1239 | return p.addNode(.{ | |
| 1240 | .tag = .while_simple, | |
| 1241 | .main_token = while_token, | |
| 1242 | .data = .{ | |
| 1243 | .lhs = condition, | |
| 1244 | .rhs = assign_expr, | |
| 1245 | }, | |
| 1246 | }); | |
| 1247 | } else { | |
| 1248 | return p.addNode(.{ | |
| 1249 | .tag = .while_cont, | |
| 1250 | .main_token = while_token, | |
| 1251 | .data = .{ | |
| 1252 | .lhs = condition, | |
| 1253 | .rhs = try p.addExtra(Node.WhileCont{ | |
| 1254 | .cont_expr = cont_expr, | |
| 1255 | .then_expr = assign_expr, | |
| 1256 | }), | |
| 1257 | }, | |
| 1258 | }); | |
| 1259 | } | |
| 1260 | } | |
| 1261 | else_required = true; | |
| 1262 | break :blk assign_expr; | |
| 1263 | }; | |
| 1264 | _ = p.eatToken(.keyword_else) orelse { | |
| 1265 | if (else_required) { | |
| 1266 | try p.warn(.expected_semi_or_else); | |
| 1267 | } | |
| 1268 | if (cont_expr == 0) { | |
| 1269 | return p.addNode(.{ | |
| 1270 | .tag = .while_simple, | |
| 1271 | .main_token = while_token, | |
| 1272 | .data = .{ | |
| 1273 | .lhs = condition, | |
| 1274 | .rhs = then_expr, | |
| 1275 | }, | |
| 1276 | }); | |
| 1277 | } else { | |
| 1278 | return p.addNode(.{ | |
| 1279 | .tag = .while_cont, | |
| 1280 | .main_token = while_token, | |
| 1281 | .data = .{ | |
| 1282 | .lhs = condition, | |
| 1283 | .rhs = try p.addExtra(Node.WhileCont{ | |
| 1284 | .cont_expr = cont_expr, | |
| 1285 | .then_expr = then_expr, | |
| 1286 | }), | |
| 1287 | }, | |
| 1288 | }); | |
| 1289 | } | |
| 1290 | }; | |
| 1291 | _ = try p.parsePayload(); | |
| 1292 | const else_expr = try p.expectStatement(false); | |
| 1293 | return p.addNode(.{ | |
| 1294 | .tag = .@"while", | |
| 1295 | .main_token = while_token, | |
| 1296 | .data = .{ | |
| 1297 | .lhs = condition, | |
| 1298 | .rhs = try p.addExtra(Node.While{ | |
| 1299 | .cont_expr = cont_expr, | |
| 1300 | .then_expr = then_expr, | |
| 1301 | .else_expr = else_expr, | |
| 1302 | }), | |
| 1303 | }, | |
| 1304 | }); | |
| 1305 | } | |
| 1306 | ||
| 1307 | /// BlockExprStatement | |
| 1308 | /// <- BlockExpr | |
| 1309 | /// / AssignExpr SEMICOLON | |
| 1310 | fn parseBlockExprStatement(p: *Parse) !Node.Index { | |
| 1311 | const block_expr = try p.parseBlockExpr(); | |
| 1312 | if (block_expr != 0) { | |
| 1313 | return block_expr; | |
| 1314 | } | |
| 1315 | const assign_expr = try p.parseAssignExpr(); | |
| 1316 | if (assign_expr != 0) { | |
| 1317 | try p.expectSemicolon(.expected_semi_after_stmt, true); | |
| 1318 | return assign_expr; | |
| 1319 | } | |
| 1320 | return null_node; | |
| 1321 | } | |
| 1322 | ||
| 1323 | fn expectBlockExprStatement(p: *Parse) !Node.Index { | |
| 1324 | const node = try p.parseBlockExprStatement(); | |
| 1325 | if (node == 0) { | |
| 1326 | return p.fail(.expected_block_or_expr); | |
| 1327 | } | |
| 1328 | return node; | |
| 1329 | } | |
| 1330 | ||
| 1331 | /// BlockExpr <- BlockLabel? Block | |
| 1332 | fn parseBlockExpr(p: *Parse) Error!Node.Index { | |
| 1333 | switch (p.token_tags[p.tok_i]) { | |
| 1334 | .identifier => { | |
| 1335 | if (p.token_tags[p.tok_i + 1] == .colon and | |
| 1336 | p.token_tags[p.tok_i + 2] == .l_brace) | |
| 1337 | { | |
| 1338 | p.tok_i += 2; | |
| 1339 | return p.parseBlock(); | |
| 1340 | } else { | |
| 1341 | return null_node; | |
| 1342 | } | |
| 1343 | }, | |
| 1344 | .l_brace => return p.parseBlock(), | |
| 1345 | else => return null_node, | |
| 1346 | } | |
| 1347 | } | |
| 1348 | ||
| 1349 | /// AssignExpr <- Expr (AssignOp Expr)? | |
| 1350 | /// | |
| 1351 | /// AssignOp | |
| 1352 | /// <- ASTERISKEQUAL | |
| 1353 | /// / ASTERISKPIPEEQUAL | |
| 1354 | /// / SLASHEQUAL | |
| 1355 | /// / PERCENTEQUAL | |
| 1356 | /// / PLUSEQUAL | |
| 1357 | /// / PLUSPIPEEQUAL | |
| 1358 | /// / MINUSEQUAL | |
| 1359 | /// / MINUSPIPEEQUAL | |
| 1360 | /// / LARROW2EQUAL | |
| 1361 | /// / LARROW2PIPEEQUAL | |
| 1362 | /// / RARROW2EQUAL | |
| 1363 | /// / AMPERSANDEQUAL | |
| 1364 | /// / CARETEQUAL | |
| 1365 | /// / PIPEEQUAL | |
| 1366 | /// / ASTERISKPERCENTEQUAL | |
| 1367 | /// / PLUSPERCENTEQUAL | |
| 1368 | /// / MINUSPERCENTEQUAL | |
| 1369 | /// / EQUAL | |
| 1370 | fn parseAssignExpr(p: *Parse) !Node.Index { | |
| 1371 | const expr = try p.parseExpr(); | |
| 1372 | if (expr == 0) return null_node; | |
| 1373 | ||
| 1374 | const tag: Node.Tag = switch (p.token_tags[p.tok_i]) { | |
| 1375 | .asterisk_equal => .assign_mul, | |
| 1376 | .slash_equal => .assign_div, | |
| 1377 | .percent_equal => .assign_mod, | |
| 1378 | .plus_equal => .assign_add, | |
| 1379 | .minus_equal => .assign_sub, | |
| 1380 | .angle_bracket_angle_bracket_left_equal => .assign_shl, | |
| 1381 | .angle_bracket_angle_bracket_left_pipe_equal => .assign_shl_sat, | |
| 1382 | .angle_bracket_angle_bracket_right_equal => .assign_shr, | |
| 1383 | .ampersand_equal => .assign_bit_and, | |
| 1384 | .caret_equal => .assign_bit_xor, | |
| 1385 | .pipe_equal => .assign_bit_or, | |
| 1386 | .asterisk_percent_equal => .assign_mul_wrap, | |
| 1387 | .plus_percent_equal => .assign_add_wrap, | |
| 1388 | .minus_percent_equal => .assign_sub_wrap, | |
| 1389 | .asterisk_pipe_equal => .assign_mul_sat, | |
| 1390 | .plus_pipe_equal => .assign_add_sat, | |
| 1391 | .minus_pipe_equal => .assign_sub_sat, | |
| 1392 | .equal => .assign, | |
| 1393 | else => return expr, | |
| 1394 | }; | |
| 1395 | return p.addNode(.{ | |
| 1396 | .tag = tag, | |
| 1397 | .main_token = p.nextToken(), | |
| 1398 | .data = .{ | |
| 1399 | .lhs = expr, | |
| 1400 | .rhs = try p.expectExpr(), | |
| 1401 | }, | |
| 1402 | }); | |
| 1403 | } | |
| 1404 | ||
| 1405 | fn expectAssignExpr(p: *Parse) !Node.Index { | |
| 1406 | const expr = try p.parseAssignExpr(); | |
| 1407 | if (expr == 0) { | |
| 1408 | return p.fail(.expected_expr_or_assignment); | |
| 1409 | } | |
| 1410 | return expr; | |
| 1411 | } | |
| 1412 | ||
| 1413 | fn parseExpr(p: *Parse) Error!Node.Index { | |
| 1414 | return p.parseExprPrecedence(0); | |
| 1415 | } | |
| 1416 | ||
| 1417 | fn expectExpr(p: *Parse) Error!Node.Index { | |
| 1418 | const node = try p.parseExpr(); | |
| 1419 | if (node == 0) { | |
| 1420 | return p.fail(.expected_expr); | |
| 1421 | } else { | |
| 1422 | return node; | |
| 1423 | } | |
| 1424 | } | |
| 1425 | ||
| 1426 | const Assoc = enum { | |
| 1427 | left, | |
| 1428 | none, | |
| 1429 | }; | |
| 1430 | ||
| 1431 | const OperInfo = struct { | |
| 1432 | prec: i8, | |
| 1433 | tag: Node.Tag, | |
| 1434 | assoc: Assoc = Assoc.left, | |
| 1435 | }; | |
| 1436 | ||
| 1437 | // A table of binary operator information. Higher precedence numbers are | |
| 1438 | // stickier. All operators at the same precedence level should have the same | |
| 1439 | // associativity. | |
| 1440 | const operTable = std.enums.directEnumArrayDefault(Token.Tag, OperInfo, .{ .prec = -1, .tag = Node.Tag.root }, 0, .{ | |
| 1441 | .keyword_or = .{ .prec = 10, .tag = .bool_or }, | |
| 1442 | ||
| 1443 | .keyword_and = .{ .prec = 20, .tag = .bool_and }, | |
| 1444 | ||
| 1445 | .equal_equal = .{ .prec = 30, .tag = .equal_equal, .assoc = Assoc.none }, | |
| 1446 | .bang_equal = .{ .prec = 30, .tag = .bang_equal, .assoc = Assoc.none }, | |
| 1447 | .angle_bracket_left = .{ .prec = 30, .tag = .less_than, .assoc = Assoc.none }, | |
| 1448 | .angle_bracket_right = .{ .prec = 30, .tag = .greater_than, .assoc = Assoc.none }, | |
| 1449 | .angle_bracket_left_equal = .{ .prec = 30, .tag = .less_or_equal, .assoc = Assoc.none }, | |
| 1450 | .angle_bracket_right_equal = .{ .prec = 30, .tag = .greater_or_equal, .assoc = Assoc.none }, | |
| 1451 | ||
| 1452 | .ampersand = .{ .prec = 40, .tag = .bit_and }, | |
| 1453 | .caret = .{ .prec = 40, .tag = .bit_xor }, | |
| 1454 | .pipe = .{ .prec = 40, .tag = .bit_or }, | |
| 1455 | .keyword_orelse = .{ .prec = 40, .tag = .@"orelse" }, | |
| 1456 | .keyword_catch = .{ .prec = 40, .tag = .@"catch" }, | |
| 1457 | ||
| 1458 | .angle_bracket_angle_bracket_left = .{ .prec = 50, .tag = .shl }, | |
| 1459 | .angle_bracket_angle_bracket_left_pipe = .{ .prec = 50, .tag = .shl_sat }, | |
| 1460 | .angle_bracket_angle_bracket_right = .{ .prec = 50, .tag = .shr }, | |
| 1461 | ||
| 1462 | .plus = .{ .prec = 60, .tag = .add }, | |
| 1463 | .minus = .{ .prec = 60, .tag = .sub }, | |
| 1464 | .plus_plus = .{ .prec = 60, .tag = .array_cat }, | |
| 1465 | .plus_percent = .{ .prec = 60, .tag = .add_wrap }, | |
| 1466 | .minus_percent = .{ .prec = 60, .tag = .sub_wrap }, | |
| 1467 | .plus_pipe = .{ .prec = 60, .tag = .add_sat }, | |
| 1468 | .minus_pipe = .{ .prec = 60, .tag = .sub_sat }, | |
| 1469 | ||
| 1470 | .pipe_pipe = .{ .prec = 70, .tag = .merge_error_sets }, | |
| 1471 | .asterisk = .{ .prec = 70, .tag = .mul }, | |
| 1472 | .slash = .{ .prec = 70, .tag = .div }, | |
| 1473 | .percent = .{ .prec = 70, .tag = .mod }, | |
| 1474 | .asterisk_asterisk = .{ .prec = 70, .tag = .array_mult }, | |
| 1475 | .asterisk_percent = .{ .prec = 70, .tag = .mul_wrap }, | |
| 1476 | .asterisk_pipe = .{ .prec = 70, .tag = .mul_sat }, | |
| 1477 | }); | |
| 1478 | ||
| 1479 | fn parseExprPrecedence(p: *Parse, min_prec: i32) Error!Node.Index { | |
| 1480 | assert(min_prec >= 0); | |
| 1481 | var node = try p.parsePrefixExpr(); | |
| 1482 | if (node == 0) { | |
| 1483 | return null_node; | |
| 1484 | } | |
| 1485 | ||
| 1486 | var banned_prec: i8 = -1; | |
| 1487 | ||
| 1488 | while (true) { | |
| 1489 | const tok_tag = p.token_tags[p.tok_i]; | |
| 1490 | const info = operTable[@intCast(usize, @enumToInt(tok_tag))]; | |
| 1491 | if (info.prec < min_prec) { | |
| 1492 | break; | |
| 1493 | } | |
| 1494 | if (info.prec == banned_prec) { | |
| 1495 | return p.fail(.chained_comparison_operators); | |
| 1496 | } | |
| 1497 | ||
| 1498 | const oper_token = p.nextToken(); | |
| 1499 | // Special-case handling for "catch" | |
| 1500 | if (tok_tag == .keyword_catch) { | |
| 1501 | _ = try p.parsePayload(); | |
| 1502 | } | |
| 1503 | const rhs = try p.parseExprPrecedence(info.prec + 1); | |
| 1504 | if (rhs == 0) { | |
| 1505 | try p.warn(.expected_expr); | |
| 1506 | return node; | |
| 1507 | } | |
| 1508 | ||
| 1509 | { | |
| 1510 | const tok_len = tok_tag.lexeme().?.len; | |
| 1511 | const char_before = p.source[p.token_starts[oper_token] - 1]; | |
| 1512 | const char_after = p.source[p.token_starts[oper_token] + tok_len]; | |
| 1513 | if (tok_tag == .ampersand and char_after == '&') { | |
| 1514 | // without types we don't know if '&&' was intended as 'bitwise_and address_of', or a c-style logical_and | |
| 1515 | // The best the parser can do is recommend changing it to 'and' or ' & &' | |
| 1516 | try p.warnMsg(.{ .tag = .invalid_ampersand_ampersand, .token = oper_token }); | |
| 1517 | } else if (std.ascii.isWhitespace(char_before) != std.ascii.isWhitespace(char_after)) { | |
| 1518 | try p.warnMsg(.{ .tag = .mismatched_binary_op_whitespace, .token = oper_token }); | |
| 1519 | } | |
| 1520 | } | |
| 1521 | ||
| 1522 | node = try p.addNode(.{ | |
| 1523 | .tag = info.tag, | |
| 1524 | .main_token = oper_token, | |
| 1525 | .data = .{ | |
| 1526 | .lhs = node, | |
| 1527 | .rhs = rhs, | |
| 1528 | }, | |
| 1529 | }); | |
| 1530 | ||
| 1531 | if (info.assoc == Assoc.none) { | |
| 1532 | banned_prec = info.prec; | |
| 1533 | } | |
| 1534 | } | |
| 1535 | ||
| 1536 | return node; | |
| 1537 | } | |
| 1538 | ||
| 1539 | /// PrefixExpr <- PrefixOp* PrimaryExpr | |
| 1540 | /// | |
| 1541 | /// PrefixOp | |
| 1542 | /// <- EXCLAMATIONMARK | |
| 1543 | /// / MINUS | |
| 1544 | /// / TILDE | |
| 1545 | /// / MINUSPERCENT | |
| 1546 | /// / AMPERSAND | |
| 1547 | /// / KEYWORD_try | |
| 1548 | /// / KEYWORD_await | |
| 1549 | fn parsePrefixExpr(p: *Parse) Error!Node.Index { | |
| 1550 | const tag: Node.Tag = switch (p.token_tags[p.tok_i]) { | |
| 1551 | .bang => .bool_not, | |
| 1552 | .minus => .negation, | |
| 1553 | .tilde => .bit_not, | |
| 1554 | .minus_percent => .negation_wrap, | |
| 1555 | .ampersand => .address_of, | |
| 1556 | .keyword_try => .@"try", | |
| 1557 | .keyword_await => .@"await", | |
| 1558 | else => return p.parsePrimaryExpr(), | |
| 1559 | }; | |
| 1560 | return p.addNode(.{ | |
| 1561 | .tag = tag, | |
| 1562 | .main_token = p.nextToken(), | |
| 1563 | .data = .{ | |
| 1564 | .lhs = try p.expectPrefixExpr(), | |
| 1565 | .rhs = undefined, | |
| 1566 | }, | |
| 1567 | }); | |
| 1568 | } | |
| 1569 | ||
| 1570 | fn expectPrefixExpr(p: *Parse) Error!Node.Index { | |
| 1571 | const node = try p.parsePrefixExpr(); | |
| 1572 | if (node == 0) { | |
| 1573 | return p.fail(.expected_prefix_expr); | |
| 1574 | } | |
| 1575 | return node; | |
| 1576 | } | |
| 1577 | ||
| 1578 | /// TypeExpr <- PrefixTypeOp* ErrorUnionExpr | |
| 1579 | /// | |
| 1580 | /// PrefixTypeOp | |
| 1581 | /// <- QUESTIONMARK | |
| 1582 | /// / KEYWORD_anyframe MINUSRARROW | |
| 1583 | /// / SliceTypeStart (ByteAlign / AddrSpace / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)* | |
| 1584 | /// / PtrTypeStart (AddrSpace / KEYWORD_align LPAREN Expr (COLON Expr COLON Expr)? RPAREN / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)* | |
| 1585 | /// / ArrayTypeStart | |
| 1586 | /// | |
| 1587 | /// SliceTypeStart <- LBRACKET (COLON Expr)? RBRACKET | |
| 1588 | /// | |
| 1589 | /// PtrTypeStart | |
| 1590 | /// <- ASTERISK | |
| 1591 | /// / ASTERISK2 | |
| 1592 | /// / LBRACKET ASTERISK (LETTERC / COLON Expr)? RBRACKET | |
| 1593 | /// | |
| 1594 | /// ArrayTypeStart <- LBRACKET Expr (COLON Expr)? RBRACKET | |
| 1595 | fn parseTypeExpr(p: *Parse) Error!Node.Index { | |
| 1596 | switch (p.token_tags[p.tok_i]) { | |
| 1597 | .question_mark => return p.addNode(.{ | |
| 1598 | .tag = .optional_type, | |
| 1599 | .main_token = p.nextToken(), | |
| 1600 | .data = .{ | |
| 1601 | .lhs = try p.expectTypeExpr(), | |
| 1602 | .rhs = undefined, | |
| 1603 | }, | |
| 1604 | }), | |
| 1605 | .keyword_anyframe => switch (p.token_tags[p.tok_i + 1]) { | |
| 1606 | .arrow => return p.addNode(.{ | |
| 1607 | .tag = .anyframe_type, | |
| 1608 | .main_token = p.nextToken(), | |
| 1609 | .data = .{ | |
| 1610 | .lhs = p.nextToken(), | |
| 1611 | .rhs = try p.expectTypeExpr(), | |
| 1612 | }, | |
| 1613 | }), | |
| 1614 | else => return p.parseErrorUnionExpr(), | |
| 1615 | }, | |
| 1616 | .asterisk => { | |
| 1617 | const asterisk = p.nextToken(); | |
| 1618 | const mods = try p.parsePtrModifiers(); | |
| 1619 | const elem_type = try p.expectTypeExpr(); | |
| 1620 | if (mods.bit_range_start != 0) { | |
| 1621 | return p.addNode(.{ | |
| 1622 | .tag = .ptr_type_bit_range, | |
| 1623 | .main_token = asterisk, | |
| 1624 | .data = .{ | |
| 1625 | .lhs = try p.addExtra(Node.PtrTypeBitRange{ | |
| 1626 | .sentinel = 0, | |
| 1627 | .align_node = mods.align_node, | |
| 1628 | .addrspace_node = mods.addrspace_node, | |
| 1629 | .bit_range_start = mods.bit_range_start, | |
| 1630 | .bit_range_end = mods.bit_range_end, | |
| 1631 | }), | |
| 1632 | .rhs = elem_type, | |
| 1633 | }, | |
| 1634 | }); | |
| 1635 | } else if (mods.addrspace_node != 0) { | |
| 1636 | return p.addNode(.{ | |
| 1637 | .tag = .ptr_type, | |
| 1638 | .main_token = asterisk, | |
| 1639 | .data = .{ | |
| 1640 | .lhs = try p.addExtra(Node.PtrType{ | |
| 1641 | .sentinel = 0, | |
| 1642 | .align_node = mods.align_node, | |
| 1643 | .addrspace_node = mods.addrspace_node, | |
| 1644 | }), | |
| 1645 | .rhs = elem_type, | |
| 1646 | }, | |
| 1647 | }); | |
| 1648 | } else { | |
| 1649 | return p.addNode(.{ | |
| 1650 | .tag = .ptr_type_aligned, | |
| 1651 | .main_token = asterisk, | |
| 1652 | .data = .{ | |
| 1653 | .lhs = mods.align_node, | |
| 1654 | .rhs = elem_type, | |
| 1655 | }, | |
| 1656 | }); | |
| 1657 | } | |
| 1658 | }, | |
| 1659 | .asterisk_asterisk => { | |
| 1660 | const asterisk = p.nextToken(); | |
| 1661 | const mods = try p.parsePtrModifiers(); | |
| 1662 | const elem_type = try p.expectTypeExpr(); | |
| 1663 | const inner: Node.Index = inner: { | |
| 1664 | if (mods.bit_range_start != 0) { | |
| 1665 | break :inner try p.addNode(.{ | |
| 1666 | .tag = .ptr_type_bit_range, | |
| 1667 | .main_token = asterisk, | |
| 1668 | .data = .{ | |
| 1669 | .lhs = try p.addExtra(Node.PtrTypeBitRange{ | |
| 1670 | .sentinel = 0, | |
| 1671 | .align_node = mods.align_node, | |
| 1672 | .addrspace_node = mods.addrspace_node, | |
| 1673 | .bit_range_start = mods.bit_range_start, | |
| 1674 | .bit_range_end = mods.bit_range_end, | |
| 1675 | }), | |
| 1676 | .rhs = elem_type, | |
| 1677 | }, | |
| 1678 | }); | |
| 1679 | } else if (mods.addrspace_node != 0) { | |
| 1680 | break :inner try p.addNode(.{ | |
| 1681 | .tag = .ptr_type, | |
| 1682 | .main_token = asterisk, | |
| 1683 | .data = .{ | |
| 1684 | .lhs = try p.addExtra(Node.PtrType{ | |
| 1685 | .sentinel = 0, | |
| 1686 | .align_node = mods.align_node, | |
| 1687 | .addrspace_node = mods.addrspace_node, | |
| 1688 | }), | |
| 1689 | .rhs = elem_type, | |
| 1690 | }, | |
| 1691 | }); | |
| 1692 | } else { | |
| 1693 | break :inner try p.addNode(.{ | |
| 1694 | .tag = .ptr_type_aligned, | |
| 1695 | .main_token = asterisk, | |
| 1696 | .data = .{ | |
| 1697 | .lhs = mods.align_node, | |
| 1698 | .rhs = elem_type, | |
| 1699 | }, | |
| 1700 | }); | |
| 1701 | } | |
| 1702 | }; | |
| 1703 | return p.addNode(.{ | |
| 1704 | .tag = .ptr_type_aligned, | |
| 1705 | .main_token = asterisk, | |
| 1706 | .data = .{ | |
| 1707 | .lhs = 0, | |
| 1708 | .rhs = inner, | |
| 1709 | }, | |
| 1710 | }); | |
| 1711 | }, | |
| 1712 | .l_bracket => switch (p.token_tags[p.tok_i + 1]) { | |
| 1713 | .asterisk => { | |
| 1714 | _ = p.nextToken(); | |
| 1715 | const asterisk = p.nextToken(); | |
| 1716 | var sentinel: Node.Index = 0; | |
| 1717 | if (p.eatToken(.identifier)) |ident| { | |
| 1718 | const ident_slice = p.source[p.token_starts[ident]..p.token_starts[ident + 1]]; | |
| 1719 | if (!std.mem.eql(u8, std.mem.trimRight(u8, ident_slice, &std.ascii.whitespace), "c")) { | |
| 1720 | p.tok_i -= 1; | |
| 1721 | } | |
| 1722 | } else if (p.eatToken(.colon)) |_| { | |
| 1723 | sentinel = try p.expectExpr(); | |
| 1724 | } | |
| 1725 | _ = try p.expectToken(.r_bracket); | |
| 1726 | const mods = try p.parsePtrModifiers(); | |
| 1727 | const elem_type = try p.expectTypeExpr(); | |
| 1728 | if (mods.bit_range_start == 0) { | |
| 1729 | if (sentinel == 0 and mods.addrspace_node == 0) { | |
| 1730 | return p.addNode(.{ | |
| 1731 | .tag = .ptr_type_aligned, | |
| 1732 | .main_token = asterisk, | |
| 1733 | .data = .{ | |
| 1734 | .lhs = mods.align_node, | |
| 1735 | .rhs = elem_type, | |
| 1736 | }, | |
| 1737 | }); | |
| 1738 | } else if (mods.align_node == 0 and mods.addrspace_node == 0) { | |
| 1739 | return p.addNode(.{ | |
| 1740 | .tag = .ptr_type_sentinel, | |
| 1741 | .main_token = asterisk, | |
| 1742 | .data = .{ | |
| 1743 | .lhs = sentinel, | |
| 1744 | .rhs = elem_type, | |
| 1745 | }, | |
| 1746 | }); | |
| 1747 | } else { | |
| 1748 | return p.addNode(.{ | |
| 1749 | .tag = .ptr_type, | |
| 1750 | .main_token = asterisk, | |
| 1751 | .data = .{ | |
| 1752 | .lhs = try p.addExtra(Node.PtrType{ | |
| 1753 | .sentinel = sentinel, | |
| 1754 | .align_node = mods.align_node, | |
| 1755 | .addrspace_node = mods.addrspace_node, | |
| 1756 | }), | |
| 1757 | .rhs = elem_type, | |
| 1758 | }, | |
| 1759 | }); | |
| 1760 | } | |
| 1761 | } else { | |
| 1762 | return p.addNode(.{ | |
| 1763 | .tag = .ptr_type_bit_range, | |
| 1764 | .main_token = asterisk, | |
| 1765 | .data = .{ | |
| 1766 | .lhs = try p.addExtra(Node.PtrTypeBitRange{ | |
| 1767 | .sentinel = sentinel, | |
| 1768 | .align_node = mods.align_node, | |
| 1769 | .addrspace_node = mods.addrspace_node, | |
| 1770 | .bit_range_start = mods.bit_range_start, | |
| 1771 | .bit_range_end = mods.bit_range_end, | |
| 1772 | }), | |
| 1773 | .rhs = elem_type, | |
| 1774 | }, | |
| 1775 | }); | |
| 1776 | } | |
| 1777 | }, | |
| 1778 | else => { | |
| 1779 | const lbracket = p.nextToken(); | |
| 1780 | const len_expr = try p.parseExpr(); | |
| 1781 | const sentinel: Node.Index = if (p.eatToken(.colon)) |_| | |
| 1782 | try p.expectExpr() | |
| 1783 | else | |
| 1784 | 0; | |
| 1785 | _ = try p.expectToken(.r_bracket); | |
| 1786 | if (len_expr == 0) { | |
| 1787 | const mods = try p.parsePtrModifiers(); | |
| 1788 | const elem_type = try p.expectTypeExpr(); | |
| 1789 | if (mods.bit_range_start != 0) { | |
| 1790 | try p.warnMsg(.{ | |
| 1791 | .tag = .invalid_bit_range, | |
| 1792 | .token = p.nodes.items(.main_token)[mods.bit_range_start], | |
| 1793 | }); | |
| 1794 | } | |
| 1795 | if (sentinel == 0 and mods.addrspace_node == 0) { | |
| 1796 | return p.addNode(.{ | |
| 1797 | .tag = .ptr_type_aligned, | |
| 1798 | .main_token = lbracket, | |
| 1799 | .data = .{ | |
| 1800 | .lhs = mods.align_node, | |
| 1801 | .rhs = elem_type, | |
| 1802 | }, | |
| 1803 | }); | |
| 1804 | } else if (mods.align_node == 0 and mods.addrspace_node == 0) { | |
| 1805 | return p.addNode(.{ | |
| 1806 | .tag = .ptr_type_sentinel, | |
| 1807 | .main_token = lbracket, | |
| 1808 | .data = .{ | |
| 1809 | .lhs = sentinel, | |
| 1810 | .rhs = elem_type, | |
| 1811 | }, | |
| 1812 | }); | |
| 1813 | } else { | |
| 1814 | return p.addNode(.{ | |
| 1815 | .tag = .ptr_type, | |
| 1816 | .main_token = lbracket, | |
| 1817 | .data = .{ | |
| 1818 | .lhs = try p.addExtra(Node.PtrType{ | |
| 1819 | .sentinel = sentinel, | |
| 1820 | .align_node = mods.align_node, | |
| 1821 | .addrspace_node = mods.addrspace_node, | |
| 1822 | }), | |
| 1823 | .rhs = elem_type, | |
| 1824 | }, | |
| 1825 | }); | |
| 1826 | } | |
| 1827 | } else { | |
| 1828 | switch (p.token_tags[p.tok_i]) { | |
| 1829 | .keyword_align, | |
| 1830 | .keyword_const, | |
| 1831 | .keyword_volatile, | |
| 1832 | .keyword_allowzero, | |
| 1833 | .keyword_addrspace, | |
| 1834 | => return p.fail(.ptr_mod_on_array_child_type), | |
| 1835 | else => {}, | |
| 1836 | } | |
| 1837 | const elem_type = try p.expectTypeExpr(); | |
| 1838 | if (sentinel == 0) { | |
| 1839 | return p.addNode(.{ | |
| 1840 | .tag = .array_type, | |
| 1841 | .main_token = lbracket, | |
| 1842 | .data = .{ | |
| 1843 | .lhs = len_expr, | |
| 1844 | .rhs = elem_type, | |
| 1845 | }, | |
| 1846 | }); | |
| 1847 | } else { | |
| 1848 | return p.addNode(.{ | |
| 1849 | .tag = .array_type_sentinel, | |
| 1850 | .main_token = lbracket, | |
| 1851 | .data = .{ | |
| 1852 | .lhs = len_expr, | |
| 1853 | .rhs = try p.addExtra(.{ | |
| 1854 | .elem_type = elem_type, | |
| 1855 | .sentinel = sentinel, | |
| 1856 | }), | |
| 1857 | }, | |
| 1858 | }); | |
| 1859 | } | |
| 1860 | } | |
| 1861 | }, | |
| 1862 | }, | |
| 1863 | else => return p.parseErrorUnionExpr(), | |
| 1864 | } | |
| 1865 | } | |
| 1866 | ||
| 1867 | fn expectTypeExpr(p: *Parse) Error!Node.Index { | |
| 1868 | const node = try p.parseTypeExpr(); | |
| 1869 | if (node == 0) { | |
| 1870 | return p.fail(.expected_type_expr); | |
| 1871 | } | |
| 1872 | return node; | |
| 1873 | } | |
| 1874 | ||
| 1875 | /// PrimaryExpr | |
| 1876 | /// <- AsmExpr | |
| 1877 | /// / IfExpr | |
| 1878 | /// / KEYWORD_break BreakLabel? Expr? | |
| 1879 | /// / KEYWORD_comptime Expr | |
| 1880 | /// / KEYWORD_nosuspend Expr | |
| 1881 | /// / KEYWORD_continue BreakLabel? | |
| 1882 | /// / KEYWORD_resume Expr | |
| 1883 | /// / KEYWORD_return Expr? | |
| 1884 | /// / BlockLabel? LoopExpr | |
| 1885 | /// / Block | |
| 1886 | /// / CurlySuffixExpr | |
| 1887 | fn parsePrimaryExpr(p: *Parse) !Node.Index { | |
| 1888 | switch (p.token_tags[p.tok_i]) { | |
| 1889 | .keyword_asm => return p.expectAsmExpr(), | |
| 1890 | .keyword_if => return p.parseIfExpr(), | |
| 1891 | .keyword_break => { | |
| 1892 | p.tok_i += 1; | |
| 1893 | return p.addNode(.{ | |
| 1894 | .tag = .@"break", | |
| 1895 | .main_token = p.tok_i - 1, | |
| 1896 | .data = .{ | |
| 1897 | .lhs = try p.parseBreakLabel(), | |
| 1898 | .rhs = try p.parseExpr(), | |
| 1899 | }, | |
| 1900 | }); | |
| 1901 | }, | |
| 1902 | .keyword_continue => { | |
| 1903 | p.tok_i += 1; | |
| 1904 | return p.addNode(.{ | |
| 1905 | .tag = .@"continue", | |
| 1906 | .main_token = p.tok_i - 1, | |
| 1907 | .data = .{ | |
| 1908 | .lhs = try p.parseBreakLabel(), | |
| 1909 | .rhs = undefined, | |
| 1910 | }, | |
| 1911 | }); | |
| 1912 | }, | |
| 1913 | .keyword_comptime => { | |
| 1914 | p.tok_i += 1; | |
| 1915 | return p.addNode(.{ | |
| 1916 | .tag = .@"comptime", | |
| 1917 | .main_token = p.tok_i - 1, | |
| 1918 | .data = .{ | |
| 1919 | .lhs = try p.expectExpr(), | |
| 1920 | .rhs = undefined, | |
| 1921 | }, | |
| 1922 | }); | |
| 1923 | }, | |
| 1924 | .keyword_nosuspend => { | |
| 1925 | p.tok_i += 1; | |
| 1926 | return p.addNode(.{ | |
| 1927 | .tag = .@"nosuspend", | |
| 1928 | .main_token = p.tok_i - 1, | |
| 1929 | .data = .{ | |
| 1930 | .lhs = try p.expectExpr(), | |
| 1931 | .rhs = undefined, | |
| 1932 | }, | |
| 1933 | }); | |
| 1934 | }, | |
| 1935 | .keyword_resume => { | |
| 1936 | p.tok_i += 1; | |
| 1937 | return p.addNode(.{ | |
| 1938 | .tag = .@"resume", | |
| 1939 | .main_token = p.tok_i - 1, | |
| 1940 | .data = .{ | |
| 1941 | .lhs = try p.expectExpr(), | |
| 1942 | .rhs = undefined, | |
| 1943 | }, | |
| 1944 | }); | |
| 1945 | }, | |
| 1946 | .keyword_return => { | |
| 1947 | p.tok_i += 1; | |
| 1948 | return p.addNode(.{ | |
| 1949 | .tag = .@"return", | |
| 1950 | .main_token = p.tok_i - 1, | |
| 1951 | .data = .{ | |
| 1952 | .lhs = try p.parseExpr(), | |
| 1953 | .rhs = undefined, | |
| 1954 | }, | |
| 1955 | }); | |
| 1956 | }, | |
| 1957 | .identifier => { | |
| 1958 | if (p.token_tags[p.tok_i + 1] == .colon) { | |
| 1959 | switch (p.token_tags[p.tok_i + 2]) { | |
| 1960 | .keyword_inline => { | |
| 1961 | p.tok_i += 3; | |
| 1962 | switch (p.token_tags[p.tok_i]) { | |
| 1963 | .keyword_for => return p.parseForExpr(), | |
| 1964 | .keyword_while => return p.parseWhileExpr(), | |
| 1965 | else => return p.fail(.expected_inlinable), | |
| 1966 | } | |
| 1967 | }, | |
| 1968 | .keyword_for => { | |
| 1969 | p.tok_i += 2; | |
| 1970 | return p.parseForExpr(); | |
| 1971 | }, | |
| 1972 | .keyword_while => { | |
| 1973 | p.tok_i += 2; | |
| 1974 | return p.parseWhileExpr(); | |
| 1975 | }, | |
| 1976 | .l_brace => { | |
| 1977 | p.tok_i += 2; | |
| 1978 | return p.parseBlock(); | |
| 1979 | }, | |
| 1980 | else => return p.parseCurlySuffixExpr(), | |
| 1981 | } | |
| 1982 | } else { | |
| 1983 | return p.parseCurlySuffixExpr(); | |
| 1984 | } | |
| 1985 | }, | |
| 1986 | .keyword_inline => { | |
| 1987 | p.tok_i += 1; | |
| 1988 | switch (p.token_tags[p.tok_i]) { | |
| 1989 | .keyword_for => return p.parseForExpr(), | |
| 1990 | .keyword_while => return p.parseWhileExpr(), | |
| 1991 | else => return p.fail(.expected_inlinable), | |
| 1992 | } | |
| 1993 | }, | |
| 1994 | .keyword_for => return p.parseForExpr(), | |
| 1995 | .keyword_while => return p.parseWhileExpr(), | |
| 1996 | .l_brace => return p.parseBlock(), | |
| 1997 | else => return p.parseCurlySuffixExpr(), | |
| 1998 | } | |
| 1999 | } | |
| 2000 | ||
| 2001 | /// IfExpr <- IfPrefix Expr (KEYWORD_else Payload? Expr)? | |
| 2002 | fn parseIfExpr(p: *Parse) !Node.Index { | |
| 2003 | return p.parseIf(expectExpr); | |
| 2004 | } | |
| 2005 | ||
| 2006 | /// Block <- LBRACE Statement* RBRACE | |
| 2007 | fn parseBlock(p: *Parse) !Node.Index { | |
| 2008 | const lbrace = p.eatToken(.l_brace) orelse return null_node; | |
| 2009 | const scratch_top = p.scratch.items.len; | |
| 2010 | defer p.scratch.shrinkRetainingCapacity(scratch_top); | |
| 2011 | while (true) { | |
| 2012 | if (p.token_tags[p.tok_i] == .r_brace) break; | |
| 2013 | const statement = try p.expectStatementRecoverable(); | |
| 2014 | if (statement == 0) break; | |
| 2015 | try p.scratch.append(p.gpa, statement); | |
| 2016 | } | |
| 2017 | _ = try p.expectToken(.r_brace); | |
| 2018 | const semicolon = (p.token_tags[p.tok_i - 2] == .semicolon); | |
| 2019 | const statements = p.scratch.items[scratch_top..]; | |
| 2020 | switch (statements.len) { | |
| 2021 | 0 => return p.addNode(.{ | |
| 2022 | .tag = .block_two, | |
| 2023 | .main_token = lbrace, | |
| 2024 | .data = .{ | |
| 2025 | .lhs = 0, | |
| 2026 | .rhs = 0, | |
| 2027 | }, | |
| 2028 | }), | |
| 2029 | 1 => return p.addNode(.{ | |
| 2030 | .tag = if (semicolon) .block_two_semicolon else .block_two, | |
| 2031 | .main_token = lbrace, | |
| 2032 | .data = .{ | |
| 2033 | .lhs = statements[0], | |
| 2034 | .rhs = 0, | |
| 2035 | }, | |
| 2036 | }), | |
| 2037 | 2 => return p.addNode(.{ | |
| 2038 | .tag = if (semicolon) .block_two_semicolon else .block_two, | |
| 2039 | .main_token = lbrace, | |
| 2040 | .data = .{ | |
| 2041 | .lhs = statements[0], | |
| 2042 | .rhs = statements[1], | |
| 2043 | }, | |
| 2044 | }), | |
| 2045 | else => { | |
| 2046 | const span = try p.listToSpan(statements); | |
| 2047 | return p.addNode(.{ | |
| 2048 | .tag = if (semicolon) .block_semicolon else .block, | |
| 2049 | .main_token = lbrace, | |
| 2050 | .data = .{ | |
| 2051 | .lhs = span.start, | |
| 2052 | .rhs = span.end, | |
| 2053 | }, | |
| 2054 | }); | |
| 2055 | }, | |
| 2056 | } | |
| 2057 | } | |
| 2058 | ||
| 2059 | /// ForPrefix <- KEYWORD_for LPAREN Expr RPAREN PtrIndexPayload | |
| 2060 | /// | |
| 2061 | /// ForExpr <- ForPrefix Expr (KEYWORD_else Expr)? | |
| 2062 | fn parseForExpr(p: *Parse) !Node.Index { | |
| 2063 | const for_token = p.eatToken(.keyword_for) orelse return null_node; | |
| 2064 | _ = try p.expectToken(.l_paren); | |
| 2065 | const array_expr = try p.expectExpr(); | |
| 2066 | _ = try p.expectToken(.r_paren); | |
| 2067 | const found_payload = try p.parsePtrIndexPayload(); | |
| 2068 | if (found_payload == 0) try p.warn(.expected_loop_payload); | |
| 2069 | ||
| 2070 | const then_expr = try p.expectExpr(); | |
| 2071 | _ = p.eatToken(.keyword_else) orelse { | |
| 2072 | return p.addNode(.{ | |
| 2073 | .tag = .for_simple, | |
| 2074 | .main_token = for_token, | |
| 2075 | .data = .{ | |
| 2076 | .lhs = array_expr, | |
| 2077 | .rhs = then_expr, | |
| 2078 | }, | |
| 2079 | }); | |
| 2080 | }; | |
| 2081 | const else_expr = try p.expectExpr(); | |
| 2082 | return p.addNode(.{ | |
| 2083 | .tag = .@"for", | |
| 2084 | .main_token = for_token, | |
| 2085 | .data = .{ | |
| 2086 | .lhs = array_expr, | |
| 2087 | .rhs = try p.addExtra(Node.If{ | |
| 2088 | .then_expr = then_expr, | |
| 2089 | .else_expr = else_expr, | |
| 2090 | }), | |
| 2091 | }, | |
| 2092 | }); | |
| 2093 | } | |
| 2094 | ||
| 2095 | /// WhilePrefix <- KEYWORD_while LPAREN Expr RPAREN PtrPayload? WhileContinueExpr? | |
| 2096 | /// | |
| 2097 | /// WhileExpr <- WhilePrefix Expr (KEYWORD_else Payload? Expr)? | |
| 2098 | fn parseWhileExpr(p: *Parse) !Node.Index { | |
| 2099 | const while_token = p.eatToken(.keyword_while) orelse return null_node; | |
| 2100 | _ = try p.expectToken(.l_paren); | |
| 2101 | const condition = try p.expectExpr(); | |
| 2102 | _ = try p.expectToken(.r_paren); | |
| 2103 | _ = try p.parsePtrPayload(); | |
| 2104 | const cont_expr = try p.parseWhileContinueExpr(); | |
| 2105 | ||
| 2106 | const then_expr = try p.expectExpr(); | |
| 2107 | _ = p.eatToken(.keyword_else) orelse { | |
| 2108 | if (cont_expr == 0) { | |
| 2109 | return p.addNode(.{ | |
| 2110 | .tag = .while_simple, | |
| 2111 | .main_token = while_token, | |
| 2112 | .data = .{ | |
| 2113 | .lhs = condition, | |
| 2114 | .rhs = then_expr, | |
| 2115 | }, | |
| 2116 | }); | |
| 2117 | } else { | |
| 2118 | return p.addNode(.{ | |
| 2119 | .tag = .while_cont, | |
| 2120 | .main_token = while_token, | |
| 2121 | .data = .{ | |
| 2122 | .lhs = condition, | |
| 2123 | .rhs = try p.addExtra(Node.WhileCont{ | |
| 2124 | .cont_expr = cont_expr, | |
| 2125 | .then_expr = then_expr, | |
| 2126 | }), | |
| 2127 | }, | |
| 2128 | }); | |
| 2129 | } | |
| 2130 | }; | |
| 2131 | _ = try p.parsePayload(); | |
| 2132 | const else_expr = try p.expectExpr(); | |
| 2133 | return p.addNode(.{ | |
| 2134 | .tag = .@"while", | |
| 2135 | .main_token = while_token, | |
| 2136 | .data = .{ | |
| 2137 | .lhs = condition, | |
| 2138 | .rhs = try p.addExtra(Node.While{ | |
| 2139 | .cont_expr = cont_expr, | |
| 2140 | .then_expr = then_expr, | |
| 2141 | .else_expr = else_expr, | |
| 2142 | }), | |
| 2143 | }, | |
| 2144 | }); | |
| 2145 | } | |
| 2146 | ||
| 2147 | /// CurlySuffixExpr <- TypeExpr InitList? | |
| 2148 | /// | |
| 2149 | /// InitList | |
| 2150 | /// <- LBRACE FieldInit (COMMA FieldInit)* COMMA? RBRACE | |
| 2151 | /// / LBRACE Expr (COMMA Expr)* COMMA? RBRACE | |
| 2152 | /// / LBRACE RBRACE | |
| 2153 | fn parseCurlySuffixExpr(p: *Parse) !Node.Index { | |
| 2154 | const lhs = try p.parseTypeExpr(); | |
| 2155 | if (lhs == 0) return null_node; | |
| 2156 | const lbrace = p.eatToken(.l_brace) orelse return lhs; | |
| 2157 | ||
| 2158 | // If there are 0 or 1 items, we can use ArrayInitOne/StructInitOne; | |
| 2159 | // otherwise we use the full ArrayInit/StructInit. | |
| 2160 | ||
| 2161 | const scratch_top = p.scratch.items.len; | |
| 2162 | defer p.scratch.shrinkRetainingCapacity(scratch_top); | |
| 2163 | const field_init = try p.parseFieldInit(); | |
| 2164 | if (field_init != 0) { | |
| 2165 | try p.scratch.append(p.gpa, field_init); | |
| 2166 | while (true) { | |
| 2167 | switch (p.token_tags[p.tok_i]) { | |
| 2168 | .comma => p.tok_i += 1, | |
| 2169 | .r_brace => { | |
| 2170 | p.tok_i += 1; | |
| 2171 | break; | |
| 2172 | }, | |
| 2173 | .colon, .r_paren, .r_bracket => return p.failExpected(.r_brace), | |
| 2174 | // Likely just a missing comma; give error but continue parsing. | |
| 2175 | else => try p.warn(.expected_comma_after_initializer), | |
| 2176 | } | |
| 2177 | if (p.eatToken(.r_brace)) |_| break; | |
| 2178 | const next = try p.expectFieldInit(); | |
| 2179 | try p.scratch.append(p.gpa, next); | |
| 2180 | } | |
| 2181 | const comma = (p.token_tags[p.tok_i - 2] == .comma); | |
| 2182 | const inits = p.scratch.items[scratch_top..]; | |
| 2183 | switch (inits.len) { | |
| 2184 | 0 => unreachable, | |
| 2185 | 1 => return p.addNode(.{ | |
| 2186 | .tag = if (comma) .struct_init_one_comma else .struct_init_one, | |
| 2187 | .main_token = lbrace, | |
| 2188 | .data = .{ | |
| 2189 | .lhs = lhs, | |
| 2190 | .rhs = inits[0], | |
| 2191 | }, | |
| 2192 | }), | |
| 2193 | else => return p.addNode(.{ | |
| 2194 | .tag = if (comma) .struct_init_comma else .struct_init, | |
| 2195 | .main_token = lbrace, | |
| 2196 | .data = .{ | |
| 2197 | .lhs = lhs, | |
| 2198 | .rhs = try p.addExtra(try p.listToSpan(inits)), | |
| 2199 | }, | |
| 2200 | }), | |
| 2201 | } | |
| 2202 | } | |
| 2203 | ||
| 2204 | while (true) { | |
| 2205 | if (p.eatToken(.r_brace)) |_| break; | |
| 2206 | const elem_init = try p.expectExpr(); | |
| 2207 | try p.scratch.append(p.gpa, elem_init); | |
| 2208 | switch (p.token_tags[p.tok_i]) { | |
| 2209 | .comma => p.tok_i += 1, | |
| 2210 | .r_brace => { | |
| 2211 | p.tok_i += 1; | |
| 2212 | break; | |
| 2213 | }, | |
| 2214 | .colon, .r_paren, .r_bracket => return p.failExpected(.r_brace), | |
| 2215 | // Likely just a missing comma; give error but continue parsing. | |
| 2216 | else => try p.warn(.expected_comma_after_initializer), | |
| 2217 | } | |
| 2218 | } | |
| 2219 | const comma = (p.token_tags[p.tok_i - 2] == .comma); | |
| 2220 | const inits = p.scratch.items[scratch_top..]; | |
| 2221 | switch (inits.len) { | |
| 2222 | 0 => return p.addNode(.{ | |
| 2223 | .tag = .struct_init_one, | |
| 2224 | .main_token = lbrace, | |
| 2225 | .data = .{ | |
| 2226 | .lhs = lhs, | |
| 2227 | .rhs = 0, | |
| 2228 | }, | |
| 2229 | }), | |
| 2230 | 1 => return p.addNode(.{ | |
| 2231 | .tag = if (comma) .array_init_one_comma else .array_init_one, | |
| 2232 | .main_token = lbrace, | |
| 2233 | .data = .{ | |
| 2234 | .lhs = lhs, | |
| 2235 | .rhs = inits[0], | |
| 2236 | }, | |
| 2237 | }), | |
| 2238 | else => return p.addNode(.{ | |
| 2239 | .tag = if (comma) .array_init_comma else .array_init, | |
| 2240 | .main_token = lbrace, | |
| 2241 | .data = .{ | |
| 2242 | .lhs = lhs, | |
| 2243 | .rhs = try p.addExtra(try p.listToSpan(inits)), | |
| 2244 | }, | |
| 2245 | }), | |
| 2246 | } | |
| 2247 | } | |
| 2248 | ||
| 2249 | /// ErrorUnionExpr <- SuffixExpr (EXCLAMATIONMARK TypeExpr)? | |
| 2250 | fn parseErrorUnionExpr(p: *Parse) !Node.Index { | |
| 2251 | const suffix_expr = try p.parseSuffixExpr(); | |
| 2252 | if (suffix_expr == 0) return null_node; | |
| 2253 | const bang = p.eatToken(.bang) orelse return suffix_expr; | |
| 2254 | return p.addNode(.{ | |
| 2255 | .tag = .error_union, | |
| 2256 | .main_token = bang, | |
| 2257 | .data = .{ | |
| 2258 | .lhs = suffix_expr, | |
| 2259 | .rhs = try p.expectTypeExpr(), | |
| 2260 | }, | |
| 2261 | }); | |
| 2262 | } | |
| 2263 | ||
| 2264 | /// SuffixExpr | |
| 2265 | /// <- KEYWORD_async PrimaryTypeExpr SuffixOp* FnCallArguments | |
| 2266 | /// / PrimaryTypeExpr (SuffixOp / FnCallArguments)* | |
| 2267 | /// | |
| 2268 | /// FnCallArguments <- LPAREN ExprList RPAREN | |
| 2269 | /// | |
| 2270 | /// ExprList <- (Expr COMMA)* Expr? | |
| 2271 | fn parseSuffixExpr(p: *Parse) !Node.Index { | |
| 2272 | if (p.eatToken(.keyword_async)) |_| { | |
| 2273 | var res = try p.expectPrimaryTypeExpr(); | |
| 2274 | while (true) { | |
| 2275 | const node = try p.parseSuffixOp(res); | |
| 2276 | if (node == 0) break; | |
| 2277 | res = node; | |
| 2278 | } | |
| 2279 | const lparen = p.eatToken(.l_paren) orelse { | |
| 2280 | try p.warn(.expected_param_list); | |
| 2281 | return res; | |
| 2282 | }; | |
| 2283 | const scratch_top = p.scratch.items.len; | |
| 2284 | defer p.scratch.shrinkRetainingCapacity(scratch_top); | |
| 2285 | while (true) { | |
| 2286 | if (p.eatToken(.r_paren)) |_| break; | |
| 2287 | const param = try p.expectExpr(); | |
| 2288 | try p.scratch.append(p.gpa, param); | |
| 2289 | switch (p.token_tags[p.tok_i]) { | |
| 2290 | .comma => p.tok_i += 1, | |
| 2291 | .r_paren => { | |
| 2292 | p.tok_i += 1; | |
| 2293 | break; | |
| 2294 | }, | |
| 2295 | .colon, .r_brace, .r_bracket => return p.failExpected(.r_paren), | |
| 2296 | // Likely just a missing comma; give error but continue parsing. | |
| 2297 | else => try p.warn(.expected_comma_after_arg), | |
| 2298 | } | |
| 2299 | } | |
| 2300 | const comma = (p.token_tags[p.tok_i - 2] == .comma); | |
| 2301 | const params = p.scratch.items[scratch_top..]; | |
| 2302 | switch (params.len) { | |
| 2303 | 0 => return p.addNode(.{ | |
| 2304 | .tag = if (comma) .async_call_one_comma else .async_call_one, | |
| 2305 | .main_token = lparen, | |
| 2306 | .data = .{ | |
| 2307 | .lhs = res, | |
| 2308 | .rhs = 0, | |
| 2309 | }, | |
| 2310 | }), | |
| 2311 | 1 => return p.addNode(.{ | |
| 2312 | .tag = if (comma) .async_call_one_comma else .async_call_one, | |
| 2313 | .main_token = lparen, | |
| 2314 | .data = .{ | |
| 2315 | .lhs = res, | |
| 2316 | .rhs = params[0], | |
| 2317 | }, | |
| 2318 | }), | |
| 2319 | else => return p.addNode(.{ | |
| 2320 | .tag = if (comma) .async_call_comma else .async_call, | |
| 2321 | .main_token = lparen, | |
| 2322 | .data = .{ | |
| 2323 | .lhs = res, | |
| 2324 | .rhs = try p.addExtra(try p.listToSpan(params)), | |
| 2325 | }, | |
| 2326 | }), | |
| 2327 | } | |
| 2328 | } | |
| 2329 | ||
| 2330 | var res = try p.parsePrimaryTypeExpr(); | |
| 2331 | if (res == 0) return res; | |
| 2332 | while (true) { | |
| 2333 | const suffix_op = try p.parseSuffixOp(res); | |
| 2334 | if (suffix_op != 0) { | |
| 2335 | res = suffix_op; | |
| 2336 | continue; | |
| 2337 | } | |
| 2338 | const lparen = p.eatToken(.l_paren) orelse return res; | |
| 2339 | const scratch_top = p.scratch.items.len; | |
| 2340 | defer p.scratch.shrinkRetainingCapacity(scratch_top); | |
| 2341 | while (true) { | |
| 2342 | if (p.eatToken(.r_paren)) |_| break; | |
| 2343 | const param = try p.expectExpr(); | |
| 2344 | try p.scratch.append(p.gpa, param); | |
| 2345 | switch (p.token_tags[p.tok_i]) { | |
| 2346 | .comma => p.tok_i += 1, | |
| 2347 | .r_paren => { | |
| 2348 | p.tok_i += 1; | |
| 2349 | break; | |
| 2350 | }, | |
| 2351 | .colon, .r_brace, .r_bracket => return p.failExpected(.r_paren), | |
| 2352 | // Likely just a missing comma; give error but continue parsing. | |
| 2353 | else => try p.warn(.expected_comma_after_arg), | |
| 2354 | } | |
| 2355 | } | |
| 2356 | const comma = (p.token_tags[p.tok_i - 2] == .comma); | |
| 2357 | const params = p.scratch.items[scratch_top..]; | |
| 2358 | res = switch (params.len) { | |
| 2359 | 0 => try p.addNode(.{ | |
| 2360 | .tag = if (comma) .call_one_comma else .call_one, | |
| 2361 | .main_token = lparen, | |
| 2362 | .data = .{ | |
| 2363 | .lhs = res, | |
| 2364 | .rhs = 0, | |
| 2365 | }, | |
| 2366 | }), | |
| 2367 | 1 => try p.addNode(.{ | |
| 2368 | .tag = if (comma) .call_one_comma else .call_one, | |
| 2369 | .main_token = lparen, | |
| 2370 | .data = .{ | |
| 2371 | .lhs = res, | |
| 2372 | .rhs = params[0], | |
| 2373 | }, | |
| 2374 | }), | |
| 2375 | else => try p.addNode(.{ | |
| 2376 | .tag = if (comma) .call_comma else .call, | |
| 2377 | .main_token = lparen, | |
| 2378 | .data = .{ | |
| 2379 | .lhs = res, | |
| 2380 | .rhs = try p.addExtra(try p.listToSpan(params)), | |
| 2381 | }, | |
| 2382 | }), | |
| 2383 | }; | |
| 2384 | } | |
| 2385 | } | |
| 2386 | ||
| 2387 | /// PrimaryTypeExpr | |
| 2388 | /// <- BUILTINIDENTIFIER FnCallArguments | |
| 2389 | /// / CHAR_LITERAL | |
| 2390 | /// / ContainerDecl | |
| 2391 | /// / DOT IDENTIFIER | |
| 2392 | /// / DOT InitList | |
| 2393 | /// / ErrorSetDecl | |
| 2394 | /// / FLOAT | |
| 2395 | /// / FnProto | |
| 2396 | /// / GroupedExpr | |
| 2397 | /// / LabeledTypeExpr | |
| 2398 | /// / IDENTIFIER | |
| 2399 | /// / IfTypeExpr | |
| 2400 | /// / INTEGER | |
| 2401 | /// / KEYWORD_comptime TypeExpr | |
| 2402 | /// / KEYWORD_error DOT IDENTIFIER | |
| 2403 | /// / KEYWORD_anyframe | |
| 2404 | /// / KEYWORD_unreachable | |
| 2405 | /// / STRINGLITERAL | |
| 2406 | /// / SwitchExpr | |
| 2407 | /// | |
| 2408 | /// ContainerDecl <- (KEYWORD_extern / KEYWORD_packed)? ContainerDeclAuto | |
| 2409 | /// | |
| 2410 | /// ContainerDeclAuto <- ContainerDeclType LBRACE container_doc_comment? ContainerMembers RBRACE | |
| 2411 | /// | |
| 2412 | /// InitList | |
| 2413 | /// <- LBRACE FieldInit (COMMA FieldInit)* COMMA? RBRACE | |
| 2414 | /// / LBRACE Expr (COMMA Expr)* COMMA? RBRACE | |
| 2415 | /// / LBRACE RBRACE | |
| 2416 | /// | |
| 2417 | /// ErrorSetDecl <- KEYWORD_error LBRACE IdentifierList RBRACE | |
| 2418 | /// | |
| 2419 | /// GroupedExpr <- LPAREN Expr RPAREN | |
| 2420 | /// | |
| 2421 | /// IfTypeExpr <- IfPrefix TypeExpr (KEYWORD_else Payload? TypeExpr)? | |
| 2422 | /// | |
| 2423 | /// LabeledTypeExpr | |
| 2424 | /// <- BlockLabel Block | |
| 2425 | /// / BlockLabel? LoopTypeExpr | |
| 2426 | /// | |
| 2427 | /// LoopTypeExpr <- KEYWORD_inline? (ForTypeExpr / WhileTypeExpr) | |
| 2428 | fn parsePrimaryTypeExpr(p: *Parse) !Node.Index { | |
| 2429 | switch (p.token_tags[p.tok_i]) { | |
| 2430 | .char_literal => return p.addNode(.{ | |
| 2431 | .tag = .char_literal, | |
| 2432 | .main_token = p.nextToken(), | |
| 2433 | .data = .{ | |
| 2434 | .lhs = undefined, | |
| 2435 | .rhs = undefined, | |
| 2436 | }, | |
| 2437 | }), | |
| 2438 | .number_literal => return p.addNode(.{ | |
| 2439 | .tag = .number_literal, | |
| 2440 | .main_token = p.nextToken(), | |
| 2441 | .data = .{ | |
| 2442 | .lhs = undefined, | |
| 2443 | .rhs = undefined, | |
| 2444 | }, | |
| 2445 | }), | |
| 2446 | .keyword_unreachable => return p.addNode(.{ | |
| 2447 | .tag = .unreachable_literal, | |
| 2448 | .main_token = p.nextToken(), | |
| 2449 | .data = .{ | |
| 2450 | .lhs = undefined, | |
| 2451 | .rhs = undefined, | |
| 2452 | }, | |
| 2453 | }), | |
| 2454 | .keyword_anyframe => return p.addNode(.{ | |
| 2455 | .tag = .anyframe_literal, | |
| 2456 | .main_token = p.nextToken(), | |
| 2457 | .data = .{ | |
| 2458 | .lhs = undefined, | |
| 2459 | .rhs = undefined, | |
| 2460 | }, | |
| 2461 | }), | |
| 2462 | .string_literal => { | |
| 2463 | const main_token = p.nextToken(); | |
| 2464 | return p.addNode(.{ | |
| 2465 | .tag = .string_literal, | |
| 2466 | .main_token = main_token, | |
| 2467 | .data = .{ | |
| 2468 | .lhs = undefined, | |
| 2469 | .rhs = undefined, | |
| 2470 | }, | |
| 2471 | }); | |
| 2472 | }, | |
| 2473 | ||
| 2474 | .builtin => return p.parseBuiltinCall(), | |
| 2475 | .keyword_fn => return p.parseFnProto(), | |
| 2476 | .keyword_if => return p.parseIf(expectTypeExpr), | |
| 2477 | .keyword_switch => return p.expectSwitchExpr(), | |
| 2478 | ||
| 2479 | .keyword_extern, | |
| 2480 | .keyword_packed, | |
| 2481 | => { | |
| 2482 | p.tok_i += 1; | |
| 2483 | return p.parseContainerDeclAuto(); | |
| 2484 | }, | |
| 2485 | ||
| 2486 | .keyword_struct, | |
| 2487 | .keyword_opaque, | |
| 2488 | .keyword_enum, | |
| 2489 | .keyword_union, | |
| 2490 | => return p.parseContainerDeclAuto(), | |
| 2491 | ||
| 2492 | .keyword_comptime => return p.addNode(.{ | |
| 2493 | .tag = .@"comptime", | |
| 2494 | .main_token = p.nextToken(), | |
| 2495 | .data = .{ | |
| 2496 | .lhs = try p.expectTypeExpr(), | |
| 2497 | .rhs = undefined, | |
| 2498 | }, | |
| 2499 | }), | |
| 2500 | .multiline_string_literal_line => { | |
| 2501 | const first_line = p.nextToken(); | |
| 2502 | while (p.token_tags[p.tok_i] == .multiline_string_literal_line) { | |
| 2503 | p.tok_i += 1; | |
| 2504 | } | |
| 2505 | return p.addNode(.{ | |
| 2506 | .tag = .multiline_string_literal, | |
| 2507 | .main_token = first_line, | |
| 2508 | .data = .{ | |
| 2509 | .lhs = first_line, | |
| 2510 | .rhs = p.tok_i - 1, | |
| 2511 | }, | |
| 2512 | }); | |
| 2513 | }, | |
| 2514 | .identifier => switch (p.token_tags[p.tok_i + 1]) { | |
| 2515 | .colon => switch (p.token_tags[p.tok_i + 2]) { | |
| 2516 | .keyword_inline => { | |
| 2517 | p.tok_i += 3; | |
| 2518 | switch (p.token_tags[p.tok_i]) { | |
| 2519 | .keyword_for => return p.parseForTypeExpr(), | |
| 2520 | .keyword_while => return p.parseWhileTypeExpr(), | |
| 2521 | else => return p.fail(.expected_inlinable), | |
| 2522 | } | |
| 2523 | }, | |
| 2524 | .keyword_for => { | |
| 2525 | p.tok_i += 2; | |
| 2526 | return p.parseForTypeExpr(); | |
| 2527 | }, | |
| 2528 | .keyword_while => { | |
| 2529 | p.tok_i += 2; | |
| 2530 | return p.parseWhileTypeExpr(); | |
| 2531 | }, | |
| 2532 | .l_brace => { | |
| 2533 | p.tok_i += 2; | |
| 2534 | return p.parseBlock(); | |
| 2535 | }, | |
| 2536 | else => return p.addNode(.{ | |
| 2537 | .tag = .identifier, | |
| 2538 | .main_token = p.nextToken(), | |
| 2539 | .data = .{ | |
| 2540 | .lhs = undefined, | |
| 2541 | .rhs = undefined, | |
| 2542 | }, | |
| 2543 | }), | |
| 2544 | }, | |
| 2545 | else => return p.addNode(.{ | |
| 2546 | .tag = .identifier, | |
| 2547 | .main_token = p.nextToken(), | |
| 2548 | .data = .{ | |
| 2549 | .lhs = undefined, | |
| 2550 | .rhs = undefined, | |
| 2551 | }, | |
| 2552 | }), | |
| 2553 | }, | |
| 2554 | .keyword_inline => { | |
| 2555 | p.tok_i += 1; | |
| 2556 | switch (p.token_tags[p.tok_i]) { | |
| 2557 | .keyword_for => return p.parseForTypeExpr(), | |
| 2558 | .keyword_while => return p.parseWhileTypeExpr(), | |
| 2559 | else => return p.fail(.expected_inlinable), | |
| 2560 | } | |
| 2561 | }, | |
| 2562 | .keyword_for => return p.parseForTypeExpr(), | |
| 2563 | .keyword_while => return p.parseWhileTypeExpr(), | |
| 2564 | .period => switch (p.token_tags[p.tok_i + 1]) { | |
| 2565 | .identifier => return p.addNode(.{ | |
| 2566 | .tag = .enum_literal, | |
| 2567 | .data = .{ | |
| 2568 | .lhs = p.nextToken(), // dot | |
| 2569 | .rhs = undefined, | |
| 2570 | }, | |
| 2571 | .main_token = p.nextToken(), // identifier | |
| 2572 | }), | |
| 2573 | .l_brace => { | |
| 2574 | const lbrace = p.tok_i + 1; | |
| 2575 | p.tok_i = lbrace + 1; | |
| 2576 | ||
| 2577 | // If there are 0, 1, or 2 items, we can use ArrayInitDotTwo/StructInitDotTwo; | |
| 2578 | // otherwise we use the full ArrayInitDot/StructInitDot. | |
| 2579 | ||
| 2580 | const scratch_top = p.scratch.items.len; | |
| 2581 | defer p.scratch.shrinkRetainingCapacity(scratch_top); | |
| 2582 | const field_init = try p.parseFieldInit(); | |
| 2583 | if (field_init != 0) { | |
| 2584 | try p.scratch.append(p.gpa, field_init); | |
| 2585 | while (true) { | |
| 2586 | switch (p.token_tags[p.tok_i]) { | |
| 2587 | .comma => p.tok_i += 1, | |
| 2588 | .r_brace => { | |
| 2589 | p.tok_i += 1; | |
| 2590 | break; | |
| 2591 | }, | |
| 2592 | .colon, .r_paren, .r_bracket => return p.failExpected(.r_brace), | |
| 2593 | // Likely just a missing comma; give error but continue parsing. | |
| 2594 | else => try p.warn(.expected_comma_after_initializer), | |
| 2595 | } | |
| 2596 | if (p.eatToken(.r_brace)) |_| break; | |
| 2597 | const next = try p.expectFieldInit(); | |
| 2598 | try p.scratch.append(p.gpa, next); | |
| 2599 | } | |
| 2600 | const comma = (p.token_tags[p.tok_i - 2] == .comma); | |
| 2601 | const inits = p.scratch.items[scratch_top..]; | |
| 2602 | switch (inits.len) { | |
| 2603 | 0 => unreachable, | |
| 2604 | 1 => return p.addNode(.{ | |
| 2605 | .tag = if (comma) .struct_init_dot_two_comma else .struct_init_dot_two, | |
| 2606 | .main_token = lbrace, | |
| 2607 | .data = .{ | |
| 2608 | .lhs = inits[0], | |
| 2609 | .rhs = 0, | |
| 2610 | }, | |
| 2611 | }), | |
| 2612 | 2 => return p.addNode(.{ | |
| 2613 | .tag = if (comma) .struct_init_dot_two_comma else .struct_init_dot_two, | |
| 2614 | .main_token = lbrace, | |
| 2615 | .data = .{ | |
| 2616 | .lhs = inits[0], | |
| 2617 | .rhs = inits[1], | |
| 2618 | }, | |
| 2619 | }), | |
| 2620 | else => { | |
| 2621 | const span = try p.listToSpan(inits); | |
| 2622 | return p.addNode(.{ | |
| 2623 | .tag = if (comma) .struct_init_dot_comma else .struct_init_dot, | |
| 2624 | .main_token = lbrace, | |
| 2625 | .data = .{ | |
| 2626 | .lhs = span.start, | |
| 2627 | .rhs = span.end, | |
| 2628 | }, | |
| 2629 | }); | |
| 2630 | }, | |
| 2631 | } | |
| 2632 | } | |
| 2633 | ||
| 2634 | while (true) { | |
| 2635 | if (p.eatToken(.r_brace)) |_| break; | |
| 2636 | const elem_init = try p.expectExpr(); | |
| 2637 | try p.scratch.append(p.gpa, elem_init); | |
| 2638 | switch (p.token_tags[p.tok_i]) { | |
| 2639 | .comma => p.tok_i += 1, | |
| 2640 | .r_brace => { | |
| 2641 | p.tok_i += 1; | |
| 2642 | break; | |
| 2643 | }, | |
| 2644 | .colon, .r_paren, .r_bracket => return p.failExpected(.r_brace), | |
| 2645 | // Likely just a missing comma; give error but continue parsing. | |
| 2646 | else => try p.warn(.expected_comma_after_initializer), | |
| 2647 | } | |
| 2648 | } | |
| 2649 | const comma = (p.token_tags[p.tok_i - 2] == .comma); | |
| 2650 | const inits = p.scratch.items[scratch_top..]; | |
| 2651 | switch (inits.len) { | |
| 2652 | 0 => return p.addNode(.{ | |
| 2653 | .tag = .struct_init_dot_two, | |
| 2654 | .main_token = lbrace, | |
| 2655 | .data = .{ | |
| 2656 | .lhs = 0, | |
| 2657 | .rhs = 0, | |
| 2658 | }, | |
| 2659 | }), | |
| 2660 | 1 => return p.addNode(.{ | |
| 2661 | .tag = if (comma) .array_init_dot_two_comma else .array_init_dot_two, | |
| 2662 | .main_token = lbrace, | |
| 2663 | .data = .{ | |
| 2664 | .lhs = inits[0], | |
| 2665 | .rhs = 0, | |
| 2666 | }, | |
| 2667 | }), | |
| 2668 | 2 => return p.addNode(.{ | |
| 2669 | .tag = if (comma) .array_init_dot_two_comma else .array_init_dot_two, | |
| 2670 | .main_token = lbrace, | |
| 2671 | .data = .{ | |
| 2672 | .lhs = inits[0], | |
| 2673 | .rhs = inits[1], | |
| 2674 | }, | |
| 2675 | }), | |
| 2676 | else => { | |
| 2677 | const span = try p.listToSpan(inits); | |
| 2678 | return p.addNode(.{ | |
| 2679 | .tag = if (comma) .array_init_dot_comma else .array_init_dot, | |
| 2680 | .main_token = lbrace, | |
| 2681 | .data = .{ | |
| 2682 | .lhs = span.start, | |
| 2683 | .rhs = span.end, | |
| 2684 | }, | |
| 2685 | }); | |
| 2686 | }, | |
| 2687 | } | |
| 2688 | }, | |
| 2689 | else => return null_node, | |
| 2690 | }, | |
| 2691 | .keyword_error => switch (p.token_tags[p.tok_i + 1]) { | |
| 2692 | .l_brace => { | |
| 2693 | const error_token = p.tok_i; | |
| 2694 | p.tok_i += 2; | |
| 2695 | while (true) { | |
| 2696 | if (p.eatToken(.r_brace)) |_| break; | |
| 2697 | _ = try p.eatDocComments(); | |
| 2698 | _ = try p.expectToken(.identifier); | |
| 2699 | switch (p.token_tags[p.tok_i]) { | |
| 2700 | .comma => p.tok_i += 1, | |
| 2701 | .r_brace => { | |
| 2702 | p.tok_i += 1; | |
| 2703 | break; | |
| 2704 | }, | |
| 2705 | .colon, .r_paren, .r_bracket => return p.failExpected(.r_brace), | |
| 2706 | // Likely just a missing comma; give error but continue parsing. | |
| 2707 | else => try p.warn(.expected_comma_after_field), | |
| 2708 | } | |
| 2709 | } | |
| 2710 | return p.addNode(.{ | |
| 2711 | .tag = .error_set_decl, | |
| 2712 | .main_token = error_token, | |
| 2713 | .data = .{ | |
| 2714 | .lhs = undefined, | |
| 2715 | .rhs = p.tok_i - 1, // rbrace | |
| 2716 | }, | |
| 2717 | }); | |
| 2718 | }, | |
| 2719 | else => { | |
| 2720 | const main_token = p.nextToken(); | |
| 2721 | const period = p.eatToken(.period); | |
| 2722 | if (period == null) try p.warnExpected(.period); | |
| 2723 | const identifier = p.eatToken(.identifier); | |
| 2724 | if (identifier == null) try p.warnExpected(.identifier); | |
| 2725 | return p.addNode(.{ | |
| 2726 | .tag = .error_value, | |
| 2727 | .main_token = main_token, | |
| 2728 | .data = .{ | |
| 2729 | .lhs = period orelse 0, | |
| 2730 | .rhs = identifier orelse 0, | |
| 2731 | }, | |
| 2732 | }); | |
| 2733 | }, | |
| 2734 | }, | |
| 2735 | .l_paren => return p.addNode(.{ | |
| 2736 | .tag = .grouped_expression, | |
| 2737 | .main_token = p.nextToken(), | |
| 2738 | .data = .{ | |
| 2739 | .lhs = try p.expectExpr(), | |
| 2740 | .rhs = try p.expectToken(.r_paren), | |
| 2741 | }, | |
| 2742 | }), | |
| 2743 | else => return null_node, | |
| 2744 | } | |
| 2745 | } | |
| 2746 | ||
| 2747 | fn expectPrimaryTypeExpr(p: *Parse) !Node.Index { | |
| 2748 | const node = try p.parsePrimaryTypeExpr(); | |
| 2749 | if (node == 0) { | |
| 2750 | return p.fail(.expected_primary_type_expr); | |
| 2751 | } | |
| 2752 | return node; | |
| 2753 | } | |
| 2754 | ||
| 2755 | /// ForPrefix <- KEYWORD_for LPAREN Expr RPAREN PtrIndexPayload | |
| 2756 | /// | |
| 2757 | /// ForTypeExpr <- ForPrefix TypeExpr (KEYWORD_else TypeExpr)? | |
| 2758 | fn parseForTypeExpr(p: *Parse) !Node.Index { | |
| 2759 | const for_token = p.eatToken(.keyword_for) orelse return null_node; | |
| 2760 | _ = try p.expectToken(.l_paren); | |
| 2761 | const array_expr = try p.expectExpr(); | |
| 2762 | _ = try p.expectToken(.r_paren); | |
| 2763 | const found_payload = try p.parsePtrIndexPayload(); | |
| 2764 | if (found_payload == 0) try p.warn(.expected_loop_payload); | |
| 2765 | ||
| 2766 | const then_expr = try p.expectTypeExpr(); | |
| 2767 | _ = p.eatToken(.keyword_else) orelse { | |
| 2768 | return p.addNode(.{ | |
| 2769 | .tag = .for_simple, | |
| 2770 | .main_token = for_token, | |
| 2771 | .data = .{ | |
| 2772 | .lhs = array_expr, | |
| 2773 | .rhs = then_expr, | |
| 2774 | }, | |
| 2775 | }); | |
| 2776 | }; | |
| 2777 | const else_expr = try p.expectTypeExpr(); | |
| 2778 | return p.addNode(.{ | |
| 2779 | .tag = .@"for", | |
| 2780 | .main_token = for_token, | |
| 2781 | .data = .{ | |
| 2782 | .lhs = array_expr, | |
| 2783 | .rhs = try p.addExtra(Node.If{ | |
| 2784 | .then_expr = then_expr, | |
| 2785 | .else_expr = else_expr, | |
| 2786 | }), | |
| 2787 | }, | |
| 2788 | }); | |
| 2789 | } | |
| 2790 | ||
| 2791 | /// WhilePrefix <- KEYWORD_while LPAREN Expr RPAREN PtrPayload? WhileContinueExpr? | |
| 2792 | /// | |
| 2793 | /// WhileTypeExpr <- WhilePrefix TypeExpr (KEYWORD_else Payload? TypeExpr)? | |
| 2794 | fn parseWhileTypeExpr(p: *Parse) !Node.Index { | |
| 2795 | const while_token = p.eatToken(.keyword_while) orelse return null_node; | |
| 2796 | _ = try p.expectToken(.l_paren); | |
| 2797 | const condition = try p.expectExpr(); | |
| 2798 | _ = try p.expectToken(.r_paren); | |
| 2799 | _ = try p.parsePtrPayload(); | |
| 2800 | const cont_expr = try p.parseWhileContinueExpr(); | |
| 2801 | ||
| 2802 | const then_expr = try p.expectTypeExpr(); | |
| 2803 | _ = p.eatToken(.keyword_else) orelse { | |
| 2804 | if (cont_expr == 0) { | |
| 2805 | return p.addNode(.{ | |
| 2806 | .tag = .while_simple, | |
| 2807 | .main_token = while_token, | |
| 2808 | .data = .{ | |
| 2809 | .lhs = condition, | |
| 2810 | .rhs = then_expr, | |
| 2811 | }, | |
| 2812 | }); | |
| 2813 | } else { | |
| 2814 | return p.addNode(.{ | |
| 2815 | .tag = .while_cont, | |
| 2816 | .main_token = while_token, | |
| 2817 | .data = .{ | |
| 2818 | .lhs = condition, | |
| 2819 | .rhs = try p.addExtra(Node.WhileCont{ | |
| 2820 | .cont_expr = cont_expr, | |
| 2821 | .then_expr = then_expr, | |
| 2822 | }), | |
| 2823 | }, | |
| 2824 | }); | |
| 2825 | } | |
| 2826 | }; | |
| 2827 | _ = try p.parsePayload(); | |
| 2828 | const else_expr = try p.expectTypeExpr(); | |
| 2829 | return p.addNode(.{ | |
| 2830 | .tag = .@"while", | |
| 2831 | .main_token = while_token, | |
| 2832 | .data = .{ | |
| 2833 | .lhs = condition, | |
| 2834 | .rhs = try p.addExtra(Node.While{ | |
| 2835 | .cont_expr = cont_expr, | |
| 2836 | .then_expr = then_expr, | |
| 2837 | .else_expr = else_expr, | |
| 2838 | }), | |
| 2839 | }, | |
| 2840 | }); | |
| 2841 | } | |
| 2842 | ||
| 2843 | /// SwitchExpr <- KEYWORD_switch LPAREN Expr RPAREN LBRACE SwitchProngList RBRACE | |
| 2844 | fn expectSwitchExpr(p: *Parse) !Node.Index { | |
| 2845 | const switch_token = p.assertToken(.keyword_switch); | |
| 2846 | _ = try p.expectToken(.l_paren); | |
| 2847 | const expr_node = try p.expectExpr(); | |
| 2848 | _ = try p.expectToken(.r_paren); | |
| 2849 | _ = try p.expectToken(.l_brace); | |
| 2850 | const cases = try p.parseSwitchProngList(); | |
| 2851 | const trailing_comma = p.token_tags[p.tok_i - 1] == .comma; | |
| 2852 | _ = try p.expectToken(.r_brace); | |
| 2853 | ||
| 2854 | return p.addNode(.{ | |
| 2855 | .tag = if (trailing_comma) .switch_comma else .@"switch", | |
| 2856 | .main_token = switch_token, | |
| 2857 | .data = .{ | |
| 2858 | .lhs = expr_node, | |
| 2859 | .rhs = try p.addExtra(Node.SubRange{ | |
| 2860 | .start = cases.start, | |
| 2861 | .end = cases.end, | |
| 2862 | }), | |
| 2863 | }, | |
| 2864 | }); | |
| 2865 | } | |
| 2866 | ||
| 2867 | /// AsmExpr <- KEYWORD_asm KEYWORD_volatile? LPAREN Expr AsmOutput? RPAREN | |
| 2868 | /// | |
| 2869 | /// AsmOutput <- COLON AsmOutputList AsmInput? | |
| 2870 | /// | |
| 2871 | /// AsmInput <- COLON AsmInputList AsmClobbers? | |
| 2872 | /// | |
| 2873 | /// AsmClobbers <- COLON StringList | |
| 2874 | /// | |
| 2875 | /// StringList <- (STRINGLITERAL COMMA)* STRINGLITERAL? | |
| 2876 | /// | |
| 2877 | /// AsmOutputList <- (AsmOutputItem COMMA)* AsmOutputItem? | |
| 2878 | /// | |
| 2879 | /// AsmInputList <- (AsmInputItem COMMA)* AsmInputItem? | |
| 2880 | fn expectAsmExpr(p: *Parse) !Node.Index { | |
| 2881 | const asm_token = p.assertToken(.keyword_asm); | |
| 2882 | _ = p.eatToken(.keyword_volatile); | |
| 2883 | _ = try p.expectToken(.l_paren); | |
| 2884 | const template = try p.expectExpr(); | |
| 2885 | ||
| 2886 | if (p.eatToken(.r_paren)) |rparen| { | |
| 2887 | return p.addNode(.{ | |
| 2888 | .tag = .asm_simple, | |
| 2889 | .main_token = asm_token, | |
| 2890 | .data = .{ | |
| 2891 | .lhs = template, | |
| 2892 | .rhs = rparen, | |
| 2893 | }, | |
| 2894 | }); | |
| 2895 | } | |
| 2896 | ||
| 2897 | _ = try p.expectToken(.colon); | |
| 2898 | ||
| 2899 | const scratch_top = p.scratch.items.len; | |
| 2900 | defer p.scratch.shrinkRetainingCapacity(scratch_top); | |
| 2901 | ||
| 2902 | while (true) { | |
| 2903 | const output_item = try p.parseAsmOutputItem(); | |
| 2904 | if (output_item == 0) break; | |
| 2905 | try p.scratch.append(p.gpa, output_item); | |
| 2906 | switch (p.token_tags[p.tok_i]) { | |
| 2907 | .comma => p.tok_i += 1, | |
| 2908 | // All possible delimiters. | |
| 2909 | .colon, .r_paren, .r_brace, .r_bracket => break, | |
| 2910 | // Likely just a missing comma; give error but continue parsing. | |
| 2911 | else => try p.warnExpected(.comma), | |
| 2912 | } | |
| 2913 | } | |
| 2914 | if (p.eatToken(.colon)) |_| { | |
| 2915 | while (true) { | |
| 2916 | const input_item = try p.parseAsmInputItem(); | |
| 2917 | if (input_item == 0) break; | |
| 2918 | try p.scratch.append(p.gpa, input_item); | |
| 2919 | switch (p.token_tags[p.tok_i]) { | |
| 2920 | .comma => p.tok_i += 1, | |
| 2921 | // All possible delimiters. | |
| 2922 | .colon, .r_paren, .r_brace, .r_bracket => break, | |
| 2923 | // Likely just a missing comma; give error but continue parsing. | |
| 2924 | else => try p.warnExpected(.comma), | |
| 2925 | } | |
| 2926 | } | |
| 2927 | if (p.eatToken(.colon)) |_| { | |
| 2928 | while (p.eatToken(.string_literal)) |_| { | |
| 2929 | switch (p.token_tags[p.tok_i]) { | |
| 2930 | .comma => p.tok_i += 1, | |
| 2931 | .colon, .r_paren, .r_brace, .r_bracket => break, | |
| 2932 | // Likely just a missing comma; give error but continue parsing. | |
| 2933 | else => try p.warnExpected(.comma), | |
| 2934 | } | |
| 2935 | } | |
| 2936 | } | |
| 2937 | } | |
| 2938 | const rparen = try p.expectToken(.r_paren); | |
| 2939 | const span = try p.listToSpan(p.scratch.items[scratch_top..]); | |
| 2940 | return p.addNode(.{ | |
| 2941 | .tag = .@"asm", | |
| 2942 | .main_token = asm_token, | |
| 2943 | .data = .{ | |
| 2944 | .lhs = template, | |
| 2945 | .rhs = try p.addExtra(Node.Asm{ | |
| 2946 | .items_start = span.start, | |
| 2947 | .items_end = span.end, | |
| 2948 | .rparen = rparen, | |
| 2949 | }), | |
| 2950 | }, | |
| 2951 | }); | |
| 2952 | } | |
| 2953 | ||
| 2954 | /// AsmOutputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERAL LPAREN (MINUSRARROW TypeExpr / IDENTIFIER) RPAREN | |
| 2955 | fn parseAsmOutputItem(p: *Parse) !Node.Index { | |
| 2956 | _ = p.eatToken(.l_bracket) orelse return null_node; | |
| 2957 | const identifier = try p.expectToken(.identifier); | |
| 2958 | _ = try p.expectToken(.r_bracket); | |
| 2959 | _ = try p.expectToken(.string_literal); | |
| 2960 | _ = try p.expectToken(.l_paren); | |
| 2961 | const type_expr: Node.Index = blk: { | |
| 2962 | if (p.eatToken(.arrow)) |_| { | |
| 2963 | break :blk try p.expectTypeExpr(); | |
| 2964 | } else { | |
| 2965 | _ = try p.expectToken(.identifier); | |
| 2966 | break :blk null_node; | |
| 2967 | } | |
| 2968 | }; | |
| 2969 | const rparen = try p.expectToken(.r_paren); | |
| 2970 | return p.addNode(.{ | |
| 2971 | .tag = .asm_output, | |
| 2972 | .main_token = identifier, | |
| 2973 | .data = .{ | |
| 2974 | .lhs = type_expr, | |
| 2975 | .rhs = rparen, | |
| 2976 | }, | |
| 2977 | }); | |
| 2978 | } | |
| 2979 | ||
| 2980 | /// AsmInputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERAL LPAREN Expr RPAREN | |
| 2981 | fn parseAsmInputItem(p: *Parse) !Node.Index { | |
| 2982 | _ = p.eatToken(.l_bracket) orelse return null_node; | |
| 2983 | const identifier = try p.expectToken(.identifier); | |
| 2984 | _ = try p.expectToken(.r_bracket); | |
| 2985 | _ = try p.expectToken(.string_literal); | |
| 2986 | _ = try p.expectToken(.l_paren); | |
| 2987 | const expr = try p.expectExpr(); | |
| 2988 | const rparen = try p.expectToken(.r_paren); | |
| 2989 | return p.addNode(.{ | |
| 2990 | .tag = .asm_input, | |
| 2991 | .main_token = identifier, | |
| 2992 | .data = .{ | |
| 2993 | .lhs = expr, | |
| 2994 | .rhs = rparen, | |
| 2995 | }, | |
| 2996 | }); | |
| 2997 | } | |
| 2998 | ||
| 2999 | /// BreakLabel <- COLON IDENTIFIER | |
| 3000 | fn parseBreakLabel(p: *Parse) !TokenIndex { | |
| 3001 | _ = p.eatToken(.colon) orelse return @as(TokenIndex, 0); | |
| 3002 | return p.expectToken(.identifier); | |
| 3003 | } | |
| 3004 | ||
| 3005 | /// BlockLabel <- IDENTIFIER COLON | |
| 3006 | fn parseBlockLabel(p: *Parse) TokenIndex { | |
| 3007 | if (p.token_tags[p.tok_i] == .identifier and | |
| 3008 | p.token_tags[p.tok_i + 1] == .colon) | |
| 3009 | { | |
| 3010 | const identifier = p.tok_i; | |
| 3011 | p.tok_i += 2; | |
| 3012 | return identifier; | |
| 3013 | } | |
| 3014 | return null_node; | |
| 3015 | } | |
| 3016 | ||
| 3017 | /// FieldInit <- DOT IDENTIFIER EQUAL Expr | |
| 3018 | fn parseFieldInit(p: *Parse) !Node.Index { | |
| 3019 | if (p.token_tags[p.tok_i + 0] == .period and | |
| 3020 | p.token_tags[p.tok_i + 1] == .identifier and | |
| 3021 | p.token_tags[p.tok_i + 2] == .equal) | |
| 3022 | { | |
| 3023 | p.tok_i += 3; | |
| 3024 | return p.expectExpr(); | |
| 3025 | } else { | |
| 3026 | return null_node; | |
| 3027 | } | |
| 3028 | } | |
| 3029 | ||
| 3030 | fn expectFieldInit(p: *Parse) !Node.Index { | |
| 3031 | if (p.token_tags[p.tok_i] != .period or | |
| 3032 | p.token_tags[p.tok_i + 1] != .identifier or | |
| 3033 | p.token_tags[p.tok_i + 2] != .equal) | |
| 3034 | return p.fail(.expected_initializer); | |
| 3035 | ||
| 3036 | p.tok_i += 3; | |
| 3037 | return p.expectExpr(); | |
| 3038 | } | |
| 3039 | ||
| 3040 | /// WhileContinueExpr <- COLON LPAREN AssignExpr RPAREN | |
| 3041 | fn parseWhileContinueExpr(p: *Parse) !Node.Index { | |
| 3042 | _ = p.eatToken(.colon) orelse { | |
| 3043 | if (p.token_tags[p.tok_i] == .l_paren and | |
| 3044 | p.tokensOnSameLine(p.tok_i - 1, p.tok_i)) | |
| 3045 | return p.fail(.expected_continue_expr); | |
| 3046 | return null_node; | |
| 3047 | }; | |
| 3048 | _ = try p.expectToken(.l_paren); | |
| 3049 | const node = try p.parseAssignExpr(); | |
| 3050 | if (node == 0) return p.fail(.expected_expr_or_assignment); | |
| 3051 | _ = try p.expectToken(.r_paren); | |
| 3052 | return node; | |
| 3053 | } | |
| 3054 | ||
| 3055 | /// LinkSection <- KEYWORD_linksection LPAREN Expr RPAREN | |
| 3056 | fn parseLinkSection(p: *Parse) !Node.Index { | |
| 3057 | _ = p.eatToken(.keyword_linksection) orelse return null_node; | |
| 3058 | _ = try p.expectToken(.l_paren); | |
| 3059 | const expr_node = try p.expectExpr(); | |
| 3060 | _ = try p.expectToken(.r_paren); | |
| 3061 | return expr_node; | |
| 3062 | } | |
| 3063 | ||
| 3064 | /// CallConv <- KEYWORD_callconv LPAREN Expr RPAREN | |
| 3065 | fn parseCallconv(p: *Parse) !Node.Index { | |
| 3066 | _ = p.eatToken(.keyword_callconv) orelse return null_node; | |
| 3067 | _ = try p.expectToken(.l_paren); | |
| 3068 | const expr_node = try p.expectExpr(); | |
| 3069 | _ = try p.expectToken(.r_paren); | |
| 3070 | return expr_node; | |
| 3071 | } | |
| 3072 | ||
| 3073 | /// AddrSpace <- KEYWORD_addrspace LPAREN Expr RPAREN | |
| 3074 | fn parseAddrSpace(p: *Parse) !Node.Index { | |
| 3075 | _ = p.eatToken(.keyword_addrspace) orelse return null_node; | |
| 3076 | _ = try p.expectToken(.l_paren); | |
| 3077 | const expr_node = try p.expectExpr(); | |
| 3078 | _ = try p.expectToken(.r_paren); | |
| 3079 | return expr_node; | |
| 3080 | } | |
| 3081 | ||
| 3082 | /// This function can return null nodes and then still return nodes afterwards, | |
| 3083 | /// such as in the case of anytype and `...`. Caller must look for rparen to find | |
| 3084 | /// out when there are no more param decls left. | |
| 3085 | /// | |
| 3086 | /// ParamDecl | |
| 3087 | /// <- doc_comment? (KEYWORD_noalias / KEYWORD_comptime)? (IDENTIFIER COLON)? ParamType | |
| 3088 | /// / DOT3 | |
| 3089 | /// | |
| 3090 | /// ParamType | |
| 3091 | /// <- KEYWORD_anytype | |
| 3092 | /// / TypeExpr | |
| 3093 | fn expectParamDecl(p: *Parse) !Node.Index { | |
| 3094 | _ = try p.eatDocComments(); | |
| 3095 | switch (p.token_tags[p.tok_i]) { | |
| 3096 | .keyword_noalias, .keyword_comptime => p.tok_i += 1, | |
| 3097 | .ellipsis3 => { | |
| 3098 | p.tok_i += 1; | |
| 3099 | return null_node; | |
| 3100 | }, | |
| 3101 | else => {}, | |
| 3102 | } | |
| 3103 | if (p.token_tags[p.tok_i] == .identifier and | |
| 3104 | p.token_tags[p.tok_i + 1] == .colon) | |
| 3105 | { | |
| 3106 | p.tok_i += 2; | |
| 3107 | } | |
| 3108 | switch (p.token_tags[p.tok_i]) { | |
| 3109 | .keyword_anytype => { | |
| 3110 | p.tok_i += 1; | |
| 3111 | return null_node; | |
| 3112 | }, | |
| 3113 | else => return p.expectTypeExpr(), | |
| 3114 | } | |
| 3115 | } | |
| 3116 | ||
| 3117 | /// Payload <- PIPE IDENTIFIER PIPE | |
| 3118 | fn parsePayload(p: *Parse) !TokenIndex { | |
| 3119 | _ = p.eatToken(.pipe) orelse return @as(TokenIndex, 0); | |
| 3120 | const identifier = try p.expectToken(.identifier); | |
| 3121 | _ = try p.expectToken(.pipe); | |
| 3122 | return identifier; | |
| 3123 | } | |
| 3124 | ||
| 3125 | /// PtrPayload <- PIPE ASTERISK? IDENTIFIER PIPE | |
| 3126 | fn parsePtrPayload(p: *Parse) !TokenIndex { | |
| 3127 | _ = p.eatToken(.pipe) orelse return @as(TokenIndex, 0); | |
| 3128 | _ = p.eatToken(.asterisk); | |
| 3129 | const identifier = try p.expectToken(.identifier); | |
| 3130 | _ = try p.expectToken(.pipe); | |
| 3131 | return identifier; | |
| 3132 | } | |
| 3133 | ||
| 3134 | /// Returns the first identifier token, if any. | |
| 3135 | /// | |
| 3136 | /// PtrIndexPayload <- PIPE ASTERISK? IDENTIFIER (COMMA IDENTIFIER)? PIPE | |
| 3137 | fn parsePtrIndexPayload(p: *Parse) !TokenIndex { | |
| 3138 | _ = p.eatToken(.pipe) orelse return @as(TokenIndex, 0); | |
| 3139 | _ = p.eatToken(.asterisk); | |
| 3140 | const identifier = try p.expectToken(.identifier); | |
| 3141 | if (p.eatToken(.comma) != null) { | |
| 3142 | _ = try p.expectToken(.identifier); | |
| 3143 | } | |
| 3144 | _ = try p.expectToken(.pipe); | |
| 3145 | return identifier; | |
| 3146 | } | |
| 3147 | ||
| 3148 | /// SwitchProng <- KEYWORD_inline? SwitchCase EQUALRARROW PtrIndexPayload? AssignExpr | |
| 3149 | /// | |
| 3150 | /// SwitchCase | |
| 3151 | /// <- SwitchItem (COMMA SwitchItem)* COMMA? | |
| 3152 | /// / KEYWORD_else | |
| 3153 | fn parseSwitchProng(p: *Parse) !Node.Index { | |
| 3154 | const scratch_top = p.scratch.items.len; | |
| 3155 | defer p.scratch.shrinkRetainingCapacity(scratch_top); | |
| 3156 | ||
| 3157 | const is_inline = p.eatToken(.keyword_inline) != null; | |
| 3158 | ||
| 3159 | if (p.eatToken(.keyword_else) == null) { | |
| 3160 | while (true) { | |
| 3161 | const item = try p.parseSwitchItem(); | |
| 3162 | if (item == 0) break; | |
| 3163 | try p.scratch.append(p.gpa, item); | |
| 3164 | if (p.eatToken(.comma) == null) break; | |
| 3165 | } | |
| 3166 | if (scratch_top == p.scratch.items.len) { | |
| 3167 | if (is_inline) p.tok_i -= 1; | |
| 3168 | return null_node; | |
| 3169 | } | |
| 3170 | } | |
| 3171 | const arrow_token = try p.expectToken(.equal_angle_bracket_right); | |
| 3172 | _ = try p.parsePtrIndexPayload(); | |
| 3173 | ||
| 3174 | const items = p.scratch.items[scratch_top..]; | |
| 3175 | switch (items.len) { | |
| 3176 | 0 => return p.addNode(.{ | |
| 3177 | .tag = if (is_inline) .switch_case_inline_one else .switch_case_one, | |
| 3178 | .main_token = arrow_token, | |
| 3179 | .data = .{ | |
| 3180 | .lhs = 0, | |
| 3181 | .rhs = try p.expectAssignExpr(), | |
| 3182 | }, | |
| 3183 | }), | |
| 3184 | 1 => return p.addNode(.{ | |
| 3185 | .tag = if (is_inline) .switch_case_inline_one else .switch_case_one, | |
| 3186 | .main_token = arrow_token, | |
| 3187 | .data = .{ | |
| 3188 | .lhs = items[0], | |
| 3189 | .rhs = try p.expectAssignExpr(), | |
| 3190 | }, | |
| 3191 | }), | |
| 3192 | else => return p.addNode(.{ | |
| 3193 | .tag = if (is_inline) .switch_case_inline else .switch_case, | |
| 3194 | .main_token = arrow_token, | |
| 3195 | .data = .{ | |
| 3196 | .lhs = try p.addExtra(try p.listToSpan(items)), | |
| 3197 | .rhs = try p.expectAssignExpr(), | |
| 3198 | }, | |
| 3199 | }), | |
| 3200 | } | |
| 3201 | } | |
| 3202 | ||
| 3203 | /// SwitchItem <- Expr (DOT3 Expr)? | |
| 3204 | fn parseSwitchItem(p: *Parse) !Node.Index { | |
| 3205 | const expr = try p.parseExpr(); | |
| 3206 | if (expr == 0) return null_node; | |
| 3207 | ||
| 3208 | if (p.eatToken(.ellipsis3)) |token| { | |
| 3209 | return p.addNode(.{ | |
| 3210 | .tag = .switch_range, | |
| 3211 | .main_token = token, | |
| 3212 | .data = .{ | |
| 3213 | .lhs = expr, | |
| 3214 | .rhs = try p.expectExpr(), | |
| 3215 | }, | |
| 3216 | }); | |
| 3217 | } | |
| 3218 | return expr; | |
| 3219 | } | |
| 3220 | ||
| 3221 | const PtrModifiers = struct { | |
| 3222 | align_node: Node.Index, | |
| 3223 | addrspace_node: Node.Index, | |
| 3224 | bit_range_start: Node.Index, | |
| 3225 | bit_range_end: Node.Index, | |
| 3226 | }; | |
| 3227 | ||
| 3228 | fn parsePtrModifiers(p: *Parse) !PtrModifiers { | |
| 3229 | var result: PtrModifiers = .{ | |
| 3230 | .align_node = 0, | |
| 3231 | .addrspace_node = 0, | |
| 3232 | .bit_range_start = 0, | |
| 3233 | .bit_range_end = 0, | |
| 3234 | }; | |
| 3235 | var saw_const = false; | |
| 3236 | var saw_volatile = false; | |
| 3237 | var saw_allowzero = false; | |
| 3238 | var saw_addrspace = false; | |
| 3239 | while (true) { | |
| 3240 | switch (p.token_tags[p.tok_i]) { | |
| 3241 | .keyword_align => { | |
| 3242 | if (result.align_node != 0) { | |
| 3243 | try p.warn(.extra_align_qualifier); | |
| 3244 | } | |
| 3245 | p.tok_i += 1; | |
| 3246 | _ = try p.expectToken(.l_paren); | |
| 3247 | result.align_node = try p.expectExpr(); | |
| 3248 | ||
| 3249 | if (p.eatToken(.colon)) |_| { | |
| 3250 | result.bit_range_start = try p.expectExpr(); | |
| 3251 | _ = try p.expectToken(.colon); | |
| 3252 | result.bit_range_end = try p.expectExpr(); | |
| 3253 | } | |
| 3254 | ||
| 3255 | _ = try p.expectToken(.r_paren); | |
| 3256 | }, | |
| 3257 | .keyword_const => { | |
| 3258 | if (saw_const) { | |
| 3259 | try p.warn(.extra_const_qualifier); | |
| 3260 | } | |
| 3261 | p.tok_i += 1; | |
| 3262 | saw_const = true; | |
| 3263 | }, | |
| 3264 | .keyword_volatile => { | |
| 3265 | if (saw_volatile) { | |
| 3266 | try p.warn(.extra_volatile_qualifier); | |
| 3267 | } | |
| 3268 | p.tok_i += 1; | |
| 3269 | saw_volatile = true; | |
| 3270 | }, | |
| 3271 | .keyword_allowzero => { | |
| 3272 | if (saw_allowzero) { | |
| 3273 | try p.warn(.extra_allowzero_qualifier); | |
| 3274 | } | |
| 3275 | p.tok_i += 1; | |
| 3276 | saw_allowzero = true; | |
| 3277 | }, | |
| 3278 | .keyword_addrspace => { | |
| 3279 | if (saw_addrspace) { | |
| 3280 | try p.warn(.extra_addrspace_qualifier); | |
| 3281 | } | |
| 3282 | result.addrspace_node = try p.parseAddrSpace(); | |
| 3283 | }, | |
| 3284 | else => return result, | |
| 3285 | } | |
| 3286 | } | |
| 3287 | } | |
| 3288 | ||
| 3289 | /// SuffixOp | |
| 3290 | /// <- LBRACKET Expr (DOT2 (Expr? (COLON Expr)?)?)? RBRACKET | |
| 3291 | /// / DOT IDENTIFIER | |
| 3292 | /// / DOTASTERISK | |
| 3293 | /// / DOTQUESTIONMARK | |
| 3294 | fn parseSuffixOp(p: *Parse, lhs: Node.Index) !Node.Index { | |
| 3295 | switch (p.token_tags[p.tok_i]) { | |
| 3296 | .l_bracket => { | |
| 3297 | const lbracket = p.nextToken(); | |
| 3298 | const index_expr = try p.expectExpr(); | |
| 3299 | ||
| 3300 | if (p.eatToken(.ellipsis2)) |_| { | |
| 3301 | const end_expr = try p.parseExpr(); | |
| 3302 | if (p.eatToken(.colon)) |_| { | |
| 3303 | const sentinel = try p.expectExpr(); | |
| 3304 | _ = try p.expectToken(.r_bracket); | |
| 3305 | return p.addNode(.{ | |
| 3306 | .tag = .slice_sentinel, | |
| 3307 | .main_token = lbracket, | |
| 3308 | .data = .{ | |
| 3309 | .lhs = lhs, | |
| 3310 | .rhs = try p.addExtra(Node.SliceSentinel{ | |
| 3311 | .start = index_expr, | |
| 3312 | .end = end_expr, | |
| 3313 | .sentinel = sentinel, | |
| 3314 | }), | |
| 3315 | }, | |
| 3316 | }); | |
| 3317 | } | |
| 3318 | _ = try p.expectToken(.r_bracket); | |
| 3319 | if (end_expr == 0) { | |
| 3320 | return p.addNode(.{ | |
| 3321 | .tag = .slice_open, | |
| 3322 | .main_token = lbracket, | |
| 3323 | .data = .{ | |
| 3324 | .lhs = lhs, | |
| 3325 | .rhs = index_expr, | |
| 3326 | }, | |
| 3327 | }); | |
| 3328 | } | |
| 3329 | return p.addNode(.{ | |
| 3330 | .tag = .slice, | |
| 3331 | .main_token = lbracket, | |
| 3332 | .data = .{ | |
| 3333 | .lhs = lhs, | |
| 3334 | .rhs = try p.addExtra(Node.Slice{ | |
| 3335 | .start = index_expr, | |
| 3336 | .end = end_expr, | |
| 3337 | }), | |
| 3338 | }, | |
| 3339 | }); | |
| 3340 | } | |
| 3341 | _ = try p.expectToken(.r_bracket); | |
| 3342 | return p.addNode(.{ | |
| 3343 | .tag = .array_access, | |
| 3344 | .main_token = lbracket, | |
| 3345 | .data = .{ | |
| 3346 | .lhs = lhs, | |
| 3347 | .rhs = index_expr, | |
| 3348 | }, | |
| 3349 | }); | |
| 3350 | }, | |
| 3351 | .period_asterisk => return p.addNode(.{ | |
| 3352 | .tag = .deref, | |
| 3353 | .main_token = p.nextToken(), | |
| 3354 | .data = .{ | |
| 3355 | .lhs = lhs, | |
| 3356 | .rhs = undefined, | |
| 3357 | }, | |
| 3358 | }), | |
| 3359 | .invalid_periodasterisks => { | |
| 3360 | try p.warn(.asterisk_after_ptr_deref); | |
| 3361 | return p.addNode(.{ | |
| 3362 | .tag = .deref, | |
| 3363 | .main_token = p.nextToken(), | |
| 3364 | .data = .{ | |
| 3365 | .lhs = lhs, | |
| 3366 | .rhs = undefined, | |
| 3367 | }, | |
| 3368 | }); | |
| 3369 | }, | |
| 3370 | .period => switch (p.token_tags[p.tok_i + 1]) { | |
| 3371 | .identifier => return p.addNode(.{ | |
| 3372 | .tag = .field_access, | |
| 3373 | .main_token = p.nextToken(), | |
| 3374 | .data = .{ | |
| 3375 | .lhs = lhs, | |
| 3376 | .rhs = p.nextToken(), | |
| 3377 | }, | |
| 3378 | }), | |
| 3379 | .question_mark => return p.addNode(.{ | |
| 3380 | .tag = .unwrap_optional, | |
| 3381 | .main_token = p.nextToken(), | |
| 3382 | .data = .{ | |
| 3383 | .lhs = lhs, | |
| 3384 | .rhs = p.nextToken(), | |
| 3385 | }, | |
| 3386 | }), | |
| 3387 | .l_brace => { | |
| 3388 | // this a misplaced `.{`, handle the error somewhere else | |
| 3389 | return null_node; | |
| 3390 | }, | |
| 3391 | else => { | |
| 3392 | p.tok_i += 1; | |
| 3393 | try p.warn(.expected_suffix_op); | |
| 3394 | return null_node; | |
| 3395 | }, | |
| 3396 | }, | |
| 3397 | else => return null_node, | |
| 3398 | } | |
| 3399 | } | |
| 3400 | ||
| 3401 | /// Caller must have already verified the first token. | |
| 3402 | /// | |
| 3403 | /// ContainerDeclAuto <- ContainerDeclType LBRACE container_doc_comment? ContainerMembers RBRACE | |
| 3404 | /// | |
| 3405 | /// ContainerDeclType | |
| 3406 | /// <- KEYWORD_struct (LPAREN Expr RPAREN)? | |
| 3407 | /// / KEYWORD_opaque | |
| 3408 | /// / KEYWORD_enum (LPAREN Expr RPAREN)? | |
| 3409 | /// / KEYWORD_union (LPAREN (KEYWORD_enum (LPAREN Expr RPAREN)? / Expr) RPAREN)? | |
| 3410 | fn parseContainerDeclAuto(p: *Parse) !Node.Index { | |
| 3411 | const main_token = p.nextToken(); | |
| 3412 | const arg_expr = switch (p.token_tags[main_token]) { | |
| 3413 | .keyword_opaque => null_node, | |
| 3414 | .keyword_struct, .keyword_enum => blk: { | |
| 3415 | if (p.eatToken(.l_paren)) |_| { | |
| 3416 | const expr = try p.expectExpr(); | |
| 3417 | _ = try p.expectToken(.r_paren); | |
| 3418 | break :blk expr; | |
| 3419 | } else { | |
| 3420 | break :blk null_node; | |
| 3421 | } | |
| 3422 | }, | |
| 3423 | .keyword_union => blk: { | |
| 3424 | if (p.eatToken(.l_paren)) |_| { | |
| 3425 | if (p.eatToken(.keyword_enum)) |_| { | |
| 3426 | if (p.eatToken(.l_paren)) |_| { | |
| 3427 | const enum_tag_expr = try p.expectExpr(); | |
| 3428 | _ = try p.expectToken(.r_paren); | |
| 3429 | _ = try p.expectToken(.r_paren); | |
| 3430 | ||
| 3431 | _ = try p.expectToken(.l_brace); | |
| 3432 | const members = try p.parseContainerMembers(); | |
| 3433 | const members_span = try members.toSpan(p); | |
| 3434 | _ = try p.expectToken(.r_brace); | |
| 3435 | return p.addNode(.{ | |
| 3436 | .tag = switch (members.trailing) { | |
| 3437 | true => .tagged_union_enum_tag_trailing, | |
| 3438 | false => .tagged_union_enum_tag, | |
| 3439 | }, | |
| 3440 | .main_token = main_token, | |
| 3441 | .data = .{ | |
| 3442 | .lhs = enum_tag_expr, | |
| 3443 | .rhs = try p.addExtra(members_span), | |
| 3444 | }, | |
| 3445 | }); | |
| 3446 | } else { | |
| 3447 | _ = try p.expectToken(.r_paren); | |
| 3448 | ||
| 3449 | _ = try p.expectToken(.l_brace); | |
| 3450 | const members = try p.parseContainerMembers(); | |
| 3451 | _ = try p.expectToken(.r_brace); | |
| 3452 | if (members.len <= 2) { | |
| 3453 | return p.addNode(.{ | |
| 3454 | .tag = switch (members.trailing) { | |
| 3455 | true => .tagged_union_two_trailing, | |
| 3456 | false => .tagged_union_two, | |
| 3457 | }, | |
| 3458 | .main_token = main_token, | |
| 3459 | .data = .{ | |
| 3460 | .lhs = members.lhs, | |
| 3461 | .rhs = members.rhs, | |
| 3462 | }, | |
| 3463 | }); | |
| 3464 | } else { | |
| 3465 | const span = try members.toSpan(p); | |
| 3466 | return p.addNode(.{ | |
| 3467 | .tag = switch (members.trailing) { | |
| 3468 | true => .tagged_union_trailing, | |
| 3469 | false => .tagged_union, | |
| 3470 | }, | |
| 3471 | .main_token = main_token, | |
| 3472 | .data = .{ | |
| 3473 | .lhs = span.start, | |
| 3474 | .rhs = span.end, | |
| 3475 | }, | |
| 3476 | }); | |
| 3477 | } | |
| 3478 | } | |
| 3479 | } else { | |
| 3480 | const expr = try p.expectExpr(); | |
| 3481 | _ = try p.expectToken(.r_paren); | |
| 3482 | break :blk expr; | |
| 3483 | } | |
| 3484 | } else { | |
| 3485 | break :blk null_node; | |
| 3486 | } | |
| 3487 | }, | |
| 3488 | else => { | |
| 3489 | p.tok_i -= 1; | |
| 3490 | return p.fail(.expected_container); | |
| 3491 | }, | |
| 3492 | }; | |
| 3493 | _ = try p.expectToken(.l_brace); | |
| 3494 | const members = try p.parseContainerMembers(); | |
| 3495 | _ = try p.expectToken(.r_brace); | |
| 3496 | if (arg_expr == 0) { | |
| 3497 | if (members.len <= 2) { | |
| 3498 | return p.addNode(.{ | |
| 3499 | .tag = switch (members.trailing) { | |
| 3500 | true => .container_decl_two_trailing, | |
| 3501 | false => .container_decl_two, | |
| 3502 | }, | |
| 3503 | .main_token = main_token, | |
| 3504 | .data = .{ | |
| 3505 | .lhs = members.lhs, | |
| 3506 | .rhs = members.rhs, | |
| 3507 | }, | |
| 3508 | }); | |
| 3509 | } else { | |
| 3510 | const span = try members.toSpan(p); | |
| 3511 | return p.addNode(.{ | |
| 3512 | .tag = switch (members.trailing) { | |
| 3513 | true => .container_decl_trailing, | |
| 3514 | false => .container_decl, | |
| 3515 | }, | |
| 3516 | .main_token = main_token, | |
| 3517 | .data = .{ | |
| 3518 | .lhs = span.start, | |
| 3519 | .rhs = span.end, | |
| 3520 | }, | |
| 3521 | }); | |
| 3522 | } | |
| 3523 | } else { | |
| 3524 | const span = try members.toSpan(p); | |
| 3525 | return p.addNode(.{ | |
| 3526 | .tag = switch (members.trailing) { | |
| 3527 | true => .container_decl_arg_trailing, | |
| 3528 | false => .container_decl_arg, | |
| 3529 | }, | |
| 3530 | .main_token = main_token, | |
| 3531 | .data = .{ | |
| 3532 | .lhs = arg_expr, | |
| 3533 | .rhs = try p.addExtra(Node.SubRange{ | |
| 3534 | .start = span.start, | |
| 3535 | .end = span.end, | |
| 3536 | }), | |
| 3537 | }, | |
| 3538 | }); | |
| 3539 | } | |
| 3540 | } | |
| 3541 | ||
| 3542 | /// Give a helpful error message for those transitioning from | |
| 3543 | /// C's 'struct Foo {};' to Zig's 'const Foo = struct {};'. | |
| 3544 | fn parseCStyleContainer(p: *Parse) Error!bool { | |
| 3545 | const main_token = p.tok_i; | |
| 3546 | switch (p.token_tags[p.tok_i]) { | |
| 3547 | .keyword_enum, .keyword_union, .keyword_struct => {}, | |
| 3548 | else => return false, | |
| 3549 | } | |
| 3550 | const identifier = p.tok_i + 1; | |
| 3551 | if (p.token_tags[identifier] != .identifier) return false; | |
| 3552 | p.tok_i += 2; | |
| 3553 | ||
| 3554 | try p.warnMsg(.{ | |
| 3555 | .tag = .c_style_container, | |
| 3556 | .token = identifier, | |
| 3557 | .extra = .{ .expected_tag = p.token_tags[main_token] }, | |
| 3558 | }); | |
| 3559 | try p.warnMsg(.{ | |
| 3560 | .tag = .zig_style_container, | |
| 3561 | .is_note = true, | |
| 3562 | .token = identifier, | |
| 3563 | .extra = .{ .expected_tag = p.token_tags[main_token] }, | |
| 3564 | }); | |
| 3565 | ||
| 3566 | _ = try p.expectToken(.l_brace); | |
| 3567 | _ = try p.parseContainerMembers(); | |
| 3568 | _ = try p.expectToken(.r_brace); | |
| 3569 | try p.expectSemicolon(.expected_semi_after_decl, true); | |
| 3570 | return true; | |
| 3571 | } | |
| 3572 | ||
| 3573 | /// Holds temporary data until we are ready to construct the full ContainerDecl AST node. | |
| 3574 | /// | |
| 3575 | /// ByteAlign <- KEYWORD_align LPAREN Expr RPAREN | |
| 3576 | fn parseByteAlign(p: *Parse) !Node.Index { | |
| 3577 | _ = p.eatToken(.keyword_align) orelse return null_node; | |
| 3578 | _ = try p.expectToken(.l_paren); | |
| 3579 | const expr = try p.expectExpr(); | |
| 3580 | _ = try p.expectToken(.r_paren); | |
| 3581 | return expr; | |
| 3582 | } | |
| 3583 | ||
| 3584 | /// SwitchProngList <- (SwitchProng COMMA)* SwitchProng? | |
| 3585 | fn parseSwitchProngList(p: *Parse) !Node.SubRange { | |
| 3586 | const scratch_top = p.scratch.items.len; | |
| 3587 | defer p.scratch.shrinkRetainingCapacity(scratch_top); | |
| 3588 | ||
| 3589 | while (true) { | |
| 3590 | const item = try parseSwitchProng(p); | |
| 3591 | if (item == 0) break; | |
| 3592 | ||
| 3593 | try p.scratch.append(p.gpa, item); | |
| 3594 | ||
| 3595 | switch (p.token_tags[p.tok_i]) { | |
| 3596 | .comma => p.tok_i += 1, | |
| 3597 | // All possible delimiters. | |
| 3598 | .colon, .r_paren, .r_brace, .r_bracket => break, | |
| 3599 | // Likely just a missing comma; give error but continue parsing. | |
| 3600 | else => try p.warn(.expected_comma_after_switch_prong), | |
| 3601 | } | |
| 3602 | } | |
| 3603 | return p.listToSpan(p.scratch.items[scratch_top..]); | |
| 3604 | } | |
| 3605 | ||
| 3606 | /// ParamDeclList <- (ParamDecl COMMA)* ParamDecl? | |
| 3607 | fn parseParamDeclList(p: *Parse) !SmallSpan { | |
| 3608 | _ = try p.expectToken(.l_paren); | |
| 3609 | const scratch_top = p.scratch.items.len; | |
| 3610 | defer p.scratch.shrinkRetainingCapacity(scratch_top); | |
| 3611 | var varargs: union(enum) { none, seen, nonfinal: TokenIndex } = .none; | |
| 3612 | while (true) { | |
| 3613 | if (p.eatToken(.r_paren)) |_| break; | |
| 3614 | if (varargs == .seen) varargs = .{ .nonfinal = p.tok_i }; | |
| 3615 | const param = try p.expectParamDecl(); | |
| 3616 | if (param != 0) { | |
| 3617 | try p.scratch.append(p.gpa, param); | |
| 3618 | } else if (p.token_tags[p.tok_i - 1] == .ellipsis3) { | |
| 3619 | if (varargs == .none) varargs = .seen; | |
| 3620 | } | |
| 3621 | switch (p.token_tags[p.tok_i]) { | |
| 3622 | .comma => p.tok_i += 1, | |
| 3623 | .r_paren => { | |
| 3624 | p.tok_i += 1; | |
| 3625 | break; | |
| 3626 | }, | |
| 3627 | .colon, .r_brace, .r_bracket => return p.failExpected(.r_paren), | |
| 3628 | // Likely just a missing comma; give error but continue parsing. | |
| 3629 | else => try p.warn(.expected_comma_after_param), | |
| 3630 | } | |
| 3631 | } | |
| 3632 | if (varargs == .nonfinal) { | |
| 3633 | try p.warnMsg(.{ .tag = .varargs_nonfinal, .token = varargs.nonfinal }); | |
| 3634 | } | |
| 3635 | const params = p.scratch.items[scratch_top..]; | |
| 3636 | return switch (params.len) { | |
| 3637 | 0 => SmallSpan{ .zero_or_one = 0 }, | |
| 3638 | 1 => SmallSpan{ .zero_or_one = params[0] }, | |
| 3639 | else => SmallSpan{ .multi = try p.listToSpan(params) }, | |
| 3640 | }; | |
| 3641 | } | |
| 3642 | ||
| 3643 | /// FnCallArguments <- LPAREN ExprList RPAREN | |
| 3644 | /// | |
| 3645 | /// ExprList <- (Expr COMMA)* Expr? | |
| 3646 | fn parseBuiltinCall(p: *Parse) !Node.Index { | |
| 3647 | const builtin_token = p.assertToken(.builtin); | |
| 3648 | if (p.token_tags[p.nextToken()] != .l_paren) { | |
| 3649 | p.tok_i -= 1; | |
| 3650 | try p.warn(.expected_param_list); | |
| 3651 | // Pretend this was an identifier so we can continue parsing. | |
| 3652 | return p.addNode(.{ | |
| 3653 | .tag = .identifier, | |
| 3654 | .main_token = builtin_token, | |
| 3655 | .data = .{ | |
| 3656 | .lhs = undefined, | |
| 3657 | .rhs = undefined, | |
| 3658 | }, | |
| 3659 | }); | |
| 3660 | } | |
| 3661 | const scratch_top = p.scratch.items.len; | |
| 3662 | defer p.scratch.shrinkRetainingCapacity(scratch_top); | |
| 3663 | while (true) { | |
| 3664 | if (p.eatToken(.r_paren)) |_| break; | |
| 3665 | const param = try p.expectExpr(); | |
| 3666 | try p.scratch.append(p.gpa, param); | |
| 3667 | switch (p.token_tags[p.tok_i]) { | |
| 3668 | .comma => p.tok_i += 1, | |
| 3669 | .r_paren => { | |
| 3670 | p.tok_i += 1; | |
| 3671 | break; | |
| 3672 | }, | |
| 3673 | // Likely just a missing comma; give error but continue parsing. | |
| 3674 | else => try p.warn(.expected_comma_after_arg), | |
| 3675 | } | |
| 3676 | } | |
| 3677 | const comma = (p.token_tags[p.tok_i - 2] == .comma); | |
| 3678 | const params = p.scratch.items[scratch_top..]; | |
| 3679 | switch (params.len) { | |
| 3680 | 0 => return p.addNode(.{ | |
| 3681 | .tag = .builtin_call_two, | |
| 3682 | .main_token = builtin_token, | |
| 3683 | .data = .{ | |
| 3684 | .lhs = 0, | |
| 3685 | .rhs = 0, | |
| 3686 | }, | |
| 3687 | }), | |
| 3688 | 1 => return p.addNode(.{ | |
| 3689 | .tag = if (comma) .builtin_call_two_comma else .builtin_call_two, | |
| 3690 | .main_token = builtin_token, | |
| 3691 | .data = .{ | |
| 3692 | .lhs = params[0], | |
| 3693 | .rhs = 0, | |
| 3694 | }, | |
| 3695 | }), | |
| 3696 | 2 => return p.addNode(.{ | |
| 3697 | .tag = if (comma) .builtin_call_two_comma else .builtin_call_two, | |
| 3698 | .main_token = builtin_token, | |
| 3699 | .data = .{ | |
| 3700 | .lhs = params[0], | |
| 3701 | .rhs = params[1], | |
| 3702 | }, | |
| 3703 | }), | |
| 3704 | else => { | |
| 3705 | const span = try p.listToSpan(params); | |
| 3706 | return p.addNode(.{ | |
| 3707 | .tag = if (comma) .builtin_call_comma else .builtin_call, | |
| 3708 | .main_token = builtin_token, | |
| 3709 | .data = .{ | |
| 3710 | .lhs = span.start, | |
| 3711 | .rhs = span.end, | |
| 3712 | }, | |
| 3713 | }); | |
| 3714 | }, | |
| 3715 | } | |
| 3716 | } | |
| 3717 | ||
| 3718 | /// IfPrefix <- KEYWORD_if LPAREN Expr RPAREN PtrPayload? | |
| 3719 | fn parseIf(p: *Parse, comptime bodyParseFn: fn (p: *Parse) Error!Node.Index) !Node.Index { | |
| 3720 | const if_token = p.eatToken(.keyword_if) orelse return null_node; | |
| 3721 | _ = try p.expectToken(.l_paren); | |
| 3722 | const condition = try p.expectExpr(); | |
| 3723 | _ = try p.expectToken(.r_paren); | |
| 3724 | _ = try p.parsePtrPayload(); | |
| 3725 | ||
| 3726 | const then_expr = try bodyParseFn(p); | |
| 3727 | assert(then_expr != 0); | |
| 3728 | ||
| 3729 | _ = p.eatToken(.keyword_else) orelse return p.addNode(.{ | |
| 3730 | .tag = .if_simple, | |
| 3731 | .main_token = if_token, | |
| 3732 | .data = .{ | |
| 3733 | .lhs = condition, | |
| 3734 | .rhs = then_expr, | |
| 3735 | }, | |
| 3736 | }); | |
| 3737 | _ = try p.parsePayload(); | |
| 3738 | const else_expr = try bodyParseFn(p); | |
| 3739 | assert(then_expr != 0); | |
| 3740 | ||
| 3741 | return p.addNode(.{ | |
| 3742 | .tag = .@"if", | |
| 3743 | .main_token = if_token, | |
| 3744 | .data = .{ | |
| 3745 | .lhs = condition, | |
| 3746 | .rhs = try p.addExtra(Node.If{ | |
| 3747 | .then_expr = then_expr, | |
| 3748 | .else_expr = else_expr, | |
| 3749 | }), | |
| 3750 | }, | |
| 3751 | }); | |
| 3752 | } | |
| 3753 | ||
| 3754 | /// Skips over doc comment tokens. Returns the first one, if any. | |
| 3755 | fn eatDocComments(p: *Parse) !?TokenIndex { | |
| 3756 | if (p.eatToken(.doc_comment)) |tok| { | |
| 3757 | var first_line = tok; | |
| 3758 | if (tok > 0 and tokensOnSameLine(p, tok - 1, tok)) { | |
| 3759 | try p.warnMsg(.{ | |
| 3760 | .tag = .same_line_doc_comment, | |
| 3761 | .token = tok, | |
| 3762 | }); | |
| 3763 | first_line = p.eatToken(.doc_comment) orelse return null; | |
| 3764 | } | |
| 3765 | while (p.eatToken(.doc_comment)) |_| {} | |
| 3766 | return first_line; | |
| 3767 | } | |
| 3768 | return null; | |
| 3769 | } | |
| 3770 | ||
| 3771 | fn tokensOnSameLine(p: *Parse, token1: TokenIndex, token2: TokenIndex) bool { | |
| 3772 | return std.mem.indexOfScalar(u8, p.source[p.token_starts[token1]..p.token_starts[token2]], '\n') == null; | |
| 3773 | } | |
| 3774 | ||
| 3775 | fn eatToken(p: *Parse, tag: Token.Tag) ?TokenIndex { | |
| 3776 | return if (p.token_tags[p.tok_i] == tag) p.nextToken() else null; | |
| 3777 | } | |
| 3778 | ||
| 3779 | fn assertToken(p: *Parse, tag: Token.Tag) TokenIndex { | |
| 3780 | const token = p.nextToken(); | |
| 3781 | assert(p.token_tags[token] == tag); | |
| 3782 | return token; | |
| 3783 | } | |
| 3784 | ||
| 3785 | fn expectToken(p: *Parse, tag: Token.Tag) Error!TokenIndex { | |
| 3786 | if (p.token_tags[p.tok_i] != tag) { | |
| 3787 | return p.failMsg(.{ | |
| 3788 | .tag = .expected_token, | |
| 3789 | .token = p.tok_i, | |
| 3790 | .extra = .{ .expected_tag = tag }, | |
| 3791 | }); | |
| 3792 | } | |
| 3793 | return p.nextToken(); | |
| 3794 | } | |
| 3795 | ||
| 3796 | fn expectSemicolon(p: *Parse, error_tag: AstError.Tag, recoverable: bool) Error!void { | |
| 3797 | if (p.token_tags[p.tok_i] == .semicolon) { | |
| 3798 | _ = p.nextToken(); | |
| 3799 | return; | |
| 3800 | } | |
| 3801 | try p.warn(error_tag); | |
| 3802 | if (!recoverable) return error.ParseError; | |
| 3803 | } | |
| 3804 | ||
| 3805 | fn nextToken(p: *Parse) TokenIndex { | |
| 3806 | const result = p.tok_i; | |
| 3807 | p.tok_i += 1; | |
| 3808 | return result; | |
| 3809 | } | |
| 3810 | ||
| 3811 | const null_node: Node.Index = 0; | |
| 3812 | ||
| 3813 | const Parse = @This(); | |
| 3814 | const std = @import("../std.zig"); | |
| 3815 | const assert = std.debug.assert; | |
| 3816 | const Allocator = std.mem.Allocator; | |
| 3817 | const Ast = std.zig.Ast; | |
| 3818 | const Node = Ast.Node; | |
| 3819 | const AstError = Ast.Error; | |
| 3820 | const TokenIndex = Ast.TokenIndex; | |
| 3821 | const Token = std.zig.Token; | |
| 3822 | ||
| 3823 | test { | |
| 3824 | _ = @import("parser_test.zig"); | |
| 3825 | } |
lib/std/zig/c_translation.zig+1-1| ... | ... | @@ -75,7 +75,7 @@ fn castPtr(comptime DestType: type, target: anytype) DestType { |
| 75 | 75 | const source = ptrInfo(@TypeOf(target)); |
| 76 | 76 | |
| 77 | 77 | if (source.is_const and !dest.is_const or source.is_volatile and !dest.is_volatile) |
| 78 | return @intToPtr(DestType, @ptrToInt(target)) | |
| 78 | return @qualCast(DestType, target) | |
| 79 | 79 | else if (@typeInfo(dest.child) == .Opaque) |
| 80 | 80 | // dest.alignment would error out |
| 81 | 81 | return @ptrCast(DestType, target) |
lib/std/zig/parse.zig deleted-3852| ... | ... | @@ -1,3852 +0,0 @@ |
| 1 | const std = @import("../std.zig"); | |
| 2 | const assert = std.debug.assert; | |
| 3 | const Allocator = std.mem.Allocator; | |
| 4 | const Ast = std.zig.Ast; | |
| 5 | const Node = Ast.Node; | |
| 6 | const AstError = Ast.Error; | |
| 7 | const TokenIndex = Ast.TokenIndex; | |
| 8 | const Token = std.zig.Token; | |
| 9 | ||
| 10 | pub const Error = error{ParseError} || Allocator.Error; | |
| 11 | ||
| 12 | /// Result should be freed with tree.deinit() when there are | |
| 13 | /// no more references to any of the tokens or nodes. | |
| 14 | pub fn parse(gpa: Allocator, source: [:0]const u8) Allocator.Error!Ast { | |
| 15 | var tokens = Ast.TokenList{}; | |
| 16 | defer tokens.deinit(gpa); | |
| 17 | ||
| 18 | // Empirically, the zig std lib has an 8:1 ratio of source bytes to token count. | |
| 19 | const estimated_token_count = source.len / 8; | |
| 20 | try tokens.ensureTotalCapacity(gpa, estimated_token_count); | |
| 21 | ||
| 22 | var tokenizer = std.zig.Tokenizer.init(source); | |
| 23 | while (true) { | |
| 24 | const token = tokenizer.next(); | |
| 25 | try tokens.append(gpa, .{ | |
| 26 | .tag = token.tag, | |
| 27 | .start = @intCast(u32, token.loc.start), | |
| 28 | }); | |
| 29 | if (token.tag == .eof) break; | |
| 30 | } | |
| 31 | ||
| 32 | var parser: Parser = .{ | |
| 33 | .source = source, | |
| 34 | .gpa = gpa, | |
| 35 | .token_tags = tokens.items(.tag), | |
| 36 | .token_starts = tokens.items(.start), | |
| 37 | .errors = .{}, | |
| 38 | .nodes = .{}, | |
| 39 | .extra_data = .{}, | |
| 40 | .scratch = .{}, | |
| 41 | .tok_i = 0, | |
| 42 | }; | |
| 43 | defer parser.errors.deinit(gpa); | |
| 44 | defer parser.nodes.deinit(gpa); | |
| 45 | defer parser.extra_data.deinit(gpa); | |
| 46 | defer parser.scratch.deinit(gpa); | |
| 47 | ||
| 48 | // Empirically, Zig source code has a 2:1 ratio of tokens to AST nodes. | |
| 49 | // Make sure at least 1 so we can use appendAssumeCapacity on the root node below. | |
| 50 | const estimated_node_count = (tokens.len + 2) / 2; | |
| 51 | try parser.nodes.ensureTotalCapacity(gpa, estimated_node_count); | |
| 52 | ||
| 53 | try parser.parseRoot(); | |
| 54 | ||
| 55 | // TODO experiment with compacting the MultiArrayList slices here | |
| 56 | return Ast{ | |
| 57 | .source = source, | |
| 58 | .tokens = tokens.toOwnedSlice(), | |
| 59 | .nodes = parser.nodes.toOwnedSlice(), | |
| 60 | .extra_data = try parser.extra_data.toOwnedSlice(gpa), | |
| 61 | .errors = try parser.errors.toOwnedSlice(gpa), | |
| 62 | }; | |
| 63 | } | |
| 64 | ||
| 65 | const null_node: Node.Index = 0; | |
| 66 | ||
| 67 | /// Represents in-progress parsing, will be converted to an Ast after completion. | |
| 68 | const Parser = struct { | |
| 69 | gpa: Allocator, | |
| 70 | source: []const u8, | |
| 71 | token_tags: []const Token.Tag, | |
| 72 | token_starts: []const Ast.ByteOffset, | |
| 73 | tok_i: TokenIndex, | |
| 74 | errors: std.ArrayListUnmanaged(AstError), | |
| 75 | nodes: Ast.NodeList, | |
| 76 | extra_data: std.ArrayListUnmanaged(Node.Index), | |
| 77 | scratch: std.ArrayListUnmanaged(Node.Index), | |
| 78 | ||
| 79 | const SmallSpan = union(enum) { | |
| 80 | zero_or_one: Node.Index, | |
| 81 | multi: Node.SubRange, | |
| 82 | }; | |
| 83 | ||
| 84 | const Members = struct { | |
| 85 | len: usize, | |
| 86 | lhs: Node.Index, | |
| 87 | rhs: Node.Index, | |
| 88 | trailing: bool, | |
| 89 | ||
| 90 | fn toSpan(self: Members, p: *Parser) !Node.SubRange { | |
| 91 | if (self.len <= 2) { | |
| 92 | const nodes = [2]Node.Index{ self.lhs, self.rhs }; | |
| 93 | return p.listToSpan(nodes[0..self.len]); | |
| 94 | } else { | |
| 95 | return Node.SubRange{ .start = self.lhs, .end = self.rhs }; | |
| 96 | } | |
| 97 | } | |
| 98 | }; | |
| 99 | ||
| 100 | fn listToSpan(p: *Parser, list: []const Node.Index) !Node.SubRange { | |
| 101 | try p.extra_data.appendSlice(p.gpa, list); | |
| 102 | return Node.SubRange{ | |
| 103 | .start = @intCast(Node.Index, p.extra_data.items.len - list.len), | |
| 104 | .end = @intCast(Node.Index, p.extra_data.items.len), | |
| 105 | }; | |
| 106 | } | |
| 107 | ||
| 108 | fn addNode(p: *Parser, elem: Ast.NodeList.Elem) Allocator.Error!Node.Index { | |
| 109 | const result = @intCast(Node.Index, p.nodes.len); | |
| 110 | try p.nodes.append(p.gpa, elem); | |
| 111 | return result; | |
| 112 | } | |
| 113 | ||
| 114 | fn setNode(p: *Parser, i: usize, elem: Ast.NodeList.Elem) Node.Index { | |
| 115 | p.nodes.set(i, elem); | |
| 116 | return @intCast(Node.Index, i); | |
| 117 | } | |
| 118 | ||
| 119 | fn reserveNode(p: *Parser, tag: Ast.Node.Tag) !usize { | |
| 120 | try p.nodes.resize(p.gpa, p.nodes.len + 1); | |
| 121 | p.nodes.items(.tag)[p.nodes.len - 1] = tag; | |
| 122 | return p.nodes.len - 1; | |
| 123 | } | |
| 124 | ||
| 125 | fn unreserveNode(p: *Parser, node_index: usize) void { | |
| 126 | if (p.nodes.len == node_index) { | |
| 127 | p.nodes.resize(p.gpa, p.nodes.len - 1) catch unreachable; | |
| 128 | } else { | |
| 129 | // There is zombie node left in the tree, let's make it as inoffensive as possible | |
| 130 | // (sadly there's no no-op node) | |
| 131 | p.nodes.items(.tag)[node_index] = .unreachable_literal; | |
| 132 | p.nodes.items(.main_token)[node_index] = p.tok_i; | |
| 133 | } | |
| 134 | } | |
| 135 | ||
| 136 | fn addExtra(p: *Parser, extra: anytype) Allocator.Error!Node.Index { | |
| 137 | const fields = std.meta.fields(@TypeOf(extra)); | |
| 138 | try p.extra_data.ensureUnusedCapacity(p.gpa, fields.len); | |
| 139 | const result = @intCast(u32, p.extra_data.items.len); | |
| 140 | inline for (fields) |field| { | |
| 141 | comptime assert(field.type == Node.Index); | |
| 142 | p.extra_data.appendAssumeCapacity(@field(extra, field.name)); | |
| 143 | } | |
| 144 | return result; | |
| 145 | } | |
| 146 | ||
| 147 | fn warnExpected(p: *Parser, expected_token: Token.Tag) error{OutOfMemory}!void { | |
| 148 | @setCold(true); | |
| 149 | try p.warnMsg(.{ | |
| 150 | .tag = .expected_token, | |
| 151 | .token = p.tok_i, | |
| 152 | .extra = .{ .expected_tag = expected_token }, | |
| 153 | }); | |
| 154 | } | |
| 155 | ||
| 156 | fn warn(p: *Parser, error_tag: AstError.Tag) error{OutOfMemory}!void { | |
| 157 | @setCold(true); | |
| 158 | try p.warnMsg(.{ .tag = error_tag, .token = p.tok_i }); | |
| 159 | } | |
| 160 | ||
| 161 | fn warnMsg(p: *Parser, msg: Ast.Error) error{OutOfMemory}!void { | |
| 162 | @setCold(true); | |
| 163 | switch (msg.tag) { | |
| 164 | .expected_semi_after_decl, | |
| 165 | .expected_semi_after_stmt, | |
| 166 | .expected_comma_after_field, | |
| 167 | .expected_comma_after_arg, | |
| 168 | .expected_comma_after_param, | |
| 169 | .expected_comma_after_initializer, | |
| 170 | .expected_comma_after_switch_prong, | |
| 171 | .expected_semi_or_else, | |
| 172 | .expected_semi_or_lbrace, | |
| 173 | .expected_token, | |
| 174 | .expected_block, | |
| 175 | .expected_block_or_assignment, | |
| 176 | .expected_block_or_expr, | |
| 177 | .expected_block_or_field, | |
| 178 | .expected_expr, | |
| 179 | .expected_expr_or_assignment, | |
| 180 | .expected_fn, | |
| 181 | .expected_inlinable, | |
| 182 | .expected_labelable, | |
| 183 | .expected_param_list, | |
| 184 | .expected_prefix_expr, | |
| 185 | .expected_primary_type_expr, | |
| 186 | .expected_pub_item, | |
| 187 | .expected_return_type, | |
| 188 | .expected_suffix_op, | |
| 189 | .expected_type_expr, | |
| 190 | .expected_var_decl, | |
| 191 | .expected_var_decl_or_fn, | |
| 192 | .expected_loop_payload, | |
| 193 | .expected_container, | |
| 194 | => if (msg.token != 0 and !p.tokensOnSameLine(msg.token - 1, msg.token)) { | |
| 195 | var copy = msg; | |
| 196 | copy.token_is_prev = true; | |
| 197 | copy.token -= 1; | |
| 198 | return p.errors.append(p.gpa, copy); | |
| 199 | }, | |
| 200 | else => {}, | |
| 201 | } | |
| 202 | try p.errors.append(p.gpa, msg); | |
| 203 | } | |
| 204 | ||
| 205 | fn fail(p: *Parser, tag: Ast.Error.Tag) error{ ParseError, OutOfMemory } { | |
| 206 | @setCold(true); | |
| 207 | return p.failMsg(.{ .tag = tag, .token = p.tok_i }); | |
| 208 | } | |
| 209 | ||
| 210 | fn failExpected(p: *Parser, expected_token: Token.Tag) error{ ParseError, OutOfMemory } { | |
| 211 | @setCold(true); | |
| 212 | return p.failMsg(.{ | |
| 213 | .tag = .expected_token, | |
| 214 | .token = p.tok_i, | |
| 215 | .extra = .{ .expected_tag = expected_token }, | |
| 216 | }); | |
| 217 | } | |
| 218 | ||
| 219 | fn failMsg(p: *Parser, msg: Ast.Error) error{ ParseError, OutOfMemory } { | |
| 220 | @setCold(true); | |
| 221 | try p.warnMsg(msg); | |
| 222 | return error.ParseError; | |
| 223 | } | |
| 224 | ||
| 225 | /// Root <- skip container_doc_comment? ContainerMembers eof | |
| 226 | fn parseRoot(p: *Parser) !void { | |
| 227 | // Root node must be index 0. | |
| 228 | p.nodes.appendAssumeCapacity(.{ | |
| 229 | .tag = .root, | |
| 230 | .main_token = 0, | |
| 231 | .data = undefined, | |
| 232 | }); | |
| 233 | const root_members = try p.parseContainerMembers(); | |
| 234 | const root_decls = try root_members.toSpan(p); | |
| 235 | if (p.token_tags[p.tok_i] != .eof) { | |
| 236 | try p.warnExpected(.eof); | |
| 237 | } | |
| 238 | p.nodes.items(.data)[0] = .{ | |
| 239 | .lhs = root_decls.start, | |
| 240 | .rhs = root_decls.end, | |
| 241 | }; | |
| 242 | } | |
| 243 | ||
| 244 | /// ContainerMembers <- ContainerDeclarations (ContainerField COMMA)* (ContainerField / ContainerDeclarations) | |
| 245 | /// | |
| 246 | /// ContainerDeclarations | |
| 247 | /// <- TestDecl ContainerDeclarations | |
| 248 | /// / ComptimeDecl ContainerDeclarations | |
| 249 | /// / doc_comment? KEYWORD_pub? Decl ContainerDeclarations | |
| 250 | /// / | |
| 251 | /// | |
| 252 | /// ComptimeDecl <- KEYWORD_comptime Block | |
| 253 | fn parseContainerMembers(p: *Parser) !Members { | |
| 254 | const scratch_top = p.scratch.items.len; | |
| 255 | defer p.scratch.shrinkRetainingCapacity(scratch_top); | |
| 256 | ||
| 257 | var field_state: union(enum) { | |
| 258 | /// No fields have been seen. | |
| 259 | none, | |
| 260 | /// Currently parsing fields. | |
| 261 | seen, | |
| 262 | /// Saw fields and then a declaration after them. | |
| 263 | /// Payload is first token of previous declaration. | |
| 264 | end: Node.Index, | |
| 265 | /// There was a declaration between fields, don't report more errors. | |
| 266 | err, | |
| 267 | } = .none; | |
| 268 | ||
| 269 | var last_field: TokenIndex = undefined; | |
| 270 | ||
| 271 | // Skip container doc comments. | |
| 272 | while (p.eatToken(.container_doc_comment)) |_| {} | |
| 273 | ||
| 274 | var trailing = false; | |
| 275 | while (true) { | |
| 276 | const doc_comment = try p.eatDocComments(); | |
| 277 | ||
| 278 | switch (p.token_tags[p.tok_i]) { | |
| 279 | .keyword_test => { | |
| 280 | if (doc_comment) |some| { | |
| 281 | try p.warnMsg(.{ .tag = .test_doc_comment, .token = some }); | |
| 282 | } | |
| 283 | const test_decl_node = try p.expectTestDeclRecoverable(); | |
| 284 | if (test_decl_node != 0) { | |
| 285 | if (field_state == .seen) { | |
| 286 | field_state = .{ .end = test_decl_node }; | |
| 287 | } | |
| 288 | try p.scratch.append(p.gpa, test_decl_node); | |
| 289 | } | |
| 290 | trailing = false; | |
| 291 | }, | |
| 292 | .keyword_comptime => switch (p.token_tags[p.tok_i + 1]) { | |
| 293 | .l_brace => { | |
| 294 | if (doc_comment) |some| { | |
| 295 | try p.warnMsg(.{ .tag = .comptime_doc_comment, .token = some }); | |
| 296 | } | |
| 297 | const comptime_token = p.nextToken(); | |
| 298 | const block = p.parseBlock() catch |err| switch (err) { | |
| 299 | error.OutOfMemory => return error.OutOfMemory, | |
| 300 | error.ParseError => blk: { | |
| 301 | p.findNextContainerMember(); | |
| 302 | break :blk null_node; | |
| 303 | }, | |
| 304 | }; | |
| 305 | if (block != 0) { | |
| 306 | const comptime_node = try p.addNode(.{ | |
| 307 | .tag = .@"comptime", | |
| 308 | .main_token = comptime_token, | |
| 309 | .data = .{ | |
| 310 | .lhs = block, | |
| 311 | .rhs = undefined, | |
| 312 | }, | |
| 313 | }); | |
| 314 | if (field_state == .seen) { | |
| 315 | field_state = .{ .end = comptime_node }; | |
| 316 | } | |
| 317 | try p.scratch.append(p.gpa, comptime_node); | |
| 318 | } | |
| 319 | trailing = false; | |
| 320 | }, | |
| 321 | else => { | |
| 322 | const identifier = p.tok_i; | |
| 323 | defer last_field = identifier; | |
| 324 | const container_field = p.expectContainerField() catch |err| switch (err) { | |
| 325 | error.OutOfMemory => return error.OutOfMemory, | |
| 326 | error.ParseError => { | |
| 327 | p.findNextContainerMember(); | |
| 328 | continue; | |
| 329 | }, | |
| 330 | }; | |
| 331 | switch (field_state) { | |
| 332 | .none => field_state = .seen, | |
| 333 | .err, .seen => {}, | |
| 334 | .end => |node| { | |
| 335 | try p.warnMsg(.{ | |
| 336 | .tag = .decl_between_fields, | |
| 337 | .token = p.nodes.items(.main_token)[node], | |
| 338 | }); | |
| 339 | try p.warnMsg(.{ | |
| 340 | .tag = .previous_field, | |
| 341 | .is_note = true, | |
| 342 | .token = last_field, | |
| 343 | }); | |
| 344 | try p.warnMsg(.{ | |
| 345 | .tag = .next_field, | |
| 346 | .is_note = true, | |
| 347 | .token = identifier, | |
| 348 | }); | |
| 349 | // Continue parsing; error will be reported later. | |
| 350 | field_state = .err; | |
| 351 | }, | |
| 352 | } | |
| 353 | try p.scratch.append(p.gpa, container_field); | |
| 354 | switch (p.token_tags[p.tok_i]) { | |
| 355 | .comma => { | |
| 356 | p.tok_i += 1; | |
| 357 | trailing = true; | |
| 358 | continue; | |
| 359 | }, | |
| 360 | .r_brace, .eof => { | |
| 361 | trailing = false; | |
| 362 | break; | |
| 363 | }, | |
| 364 | else => {}, | |
| 365 | } | |
| 366 | // There is not allowed to be a decl after a field with no comma. | |
| 367 | // Report error but recover parser. | |
| 368 | try p.warn(.expected_comma_after_field); | |
| 369 | p.findNextContainerMember(); | |
| 370 | }, | |
| 371 | }, | |
| 372 | .keyword_pub => { | |
| 373 | p.tok_i += 1; | |
| 374 | const top_level_decl = try p.expectTopLevelDeclRecoverable(); | |
| 375 | if (top_level_decl != 0) { | |
| 376 | if (field_state == .seen) { | |
| 377 | field_state = .{ .end = top_level_decl }; | |
| 378 | } | |
| 379 | try p.scratch.append(p.gpa, top_level_decl); | |
| 380 | } | |
| 381 | trailing = p.token_tags[p.tok_i - 1] == .semicolon; | |
| 382 | }, | |
| 383 | .keyword_usingnamespace => { | |
| 384 | const node = try p.expectUsingNamespaceRecoverable(); | |
| 385 | if (node != 0) { | |
| 386 | if (field_state == .seen) { | |
| 387 | field_state = .{ .end = node }; | |
| 388 | } | |
| 389 | try p.scratch.append(p.gpa, node); | |
| 390 | } | |
| 391 | trailing = p.token_tags[p.tok_i - 1] == .semicolon; | |
| 392 | }, | |
| 393 | .keyword_const, | |
| 394 | .keyword_var, | |
| 395 | .keyword_threadlocal, | |
| 396 | .keyword_export, | |
| 397 | .keyword_extern, | |
| 398 | .keyword_inline, | |
| 399 | .keyword_noinline, | |
| 400 | .keyword_fn, | |
| 401 | => { | |
| 402 | const top_level_decl = try p.expectTopLevelDeclRecoverable(); | |
| 403 | if (top_level_decl != 0) { | |
| 404 | if (field_state == .seen) { | |
| 405 | field_state = .{ .end = top_level_decl }; | |
| 406 | } | |
| 407 | try p.scratch.append(p.gpa, top_level_decl); | |
| 408 | } | |
| 409 | trailing = p.token_tags[p.tok_i - 1] == .semicolon; | |
| 410 | }, | |
| 411 | .eof, .r_brace => { | |
| 412 | if (doc_comment) |tok| { | |
| 413 | try p.warnMsg(.{ | |
| 414 | .tag = .unattached_doc_comment, | |
| 415 | .token = tok, | |
| 416 | }); | |
| 417 | } | |
| 418 | break; | |
| 419 | }, | |
| 420 | else => { | |
| 421 | const c_container = p.parseCStyleContainer() catch |err| switch (err) { | |
| 422 | error.OutOfMemory => return error.OutOfMemory, | |
| 423 | error.ParseError => false, | |
| 424 | }; | |
| 425 | if (c_container) continue; | |
| 426 | ||
| 427 | const identifier = p.tok_i; | |
| 428 | defer last_field = identifier; | |
| 429 | const container_field = p.expectContainerField() catch |err| switch (err) { | |
| 430 | error.OutOfMemory => return error.OutOfMemory, | |
| 431 | error.ParseError => { | |
| 432 | p.findNextContainerMember(); | |
| 433 | continue; | |
| 434 | }, | |
| 435 | }; | |
| 436 | switch (field_state) { | |
| 437 | .none => field_state = .seen, | |
| 438 | .err, .seen => {}, | |
| 439 | .end => |node| { | |
| 440 | try p.warnMsg(.{ | |
| 441 | .tag = .decl_between_fields, | |
| 442 | .token = p.nodes.items(.main_token)[node], | |
| 443 | }); | |
| 444 | try p.warnMsg(.{ | |
| 445 | .tag = .previous_field, | |
| 446 | .is_note = true, | |
| 447 | .token = last_field, | |
| 448 | }); | |
| 449 | try p.warnMsg(.{ | |
| 450 | .tag = .next_field, | |
| 451 | .is_note = true, | |
| 452 | .token = identifier, | |
| 453 | }); | |
| 454 | // Continue parsing; error will be reported later. | |
| 455 | field_state = .err; | |
| 456 | }, | |
| 457 | } | |
| 458 | try p.scratch.append(p.gpa, container_field); | |
| 459 | switch (p.token_tags[p.tok_i]) { | |
| 460 | .comma => { | |
| 461 | p.tok_i += 1; | |
| 462 | trailing = true; | |
| 463 | continue; | |
| 464 | }, | |
| 465 | .r_brace, .eof => { | |
| 466 | trailing = false; | |
| 467 | break; | |
| 468 | }, | |
| 469 | else => {}, | |
| 470 | } | |
| 471 | // There is not allowed to be a decl after a field with no comma. | |
| 472 | // Report error but recover parser. | |
| 473 | try p.warn(.expected_comma_after_field); | |
| 474 | if (p.token_tags[p.tok_i] == .semicolon and p.token_tags[identifier] == .identifier) { | |
| 475 | try p.warnMsg(.{ | |
| 476 | .tag = .var_const_decl, | |
| 477 | .is_note = true, | |
| 478 | .token = identifier, | |
| 479 | }); | |
| 480 | } | |
| 481 | p.findNextContainerMember(); | |
| 482 | continue; | |
| 483 | }, | |
| 484 | } | |
| 485 | } | |
| 486 | ||
| 487 | const items = p.scratch.items[scratch_top..]; | |
| 488 | switch (items.len) { | |
| 489 | 0 => return Members{ | |
| 490 | .len = 0, | |
| 491 | .lhs = 0, | |
| 492 | .rhs = 0, | |
| 493 | .trailing = trailing, | |
| 494 | }, | |
| 495 | 1 => return Members{ | |
| 496 | .len = 1, | |
| 497 | .lhs = items[0], | |
| 498 | .rhs = 0, | |
| 499 | .trailing = trailing, | |
| 500 | }, | |
| 501 | 2 => return Members{ | |
| 502 | .len = 2, | |
| 503 | .lhs = items[0], | |
| 504 | .rhs = items[1], | |
| 505 | .trailing = trailing, | |
| 506 | }, | |
| 507 | else => { | |
| 508 | const span = try p.listToSpan(items); | |
| 509 | return Members{ | |
| 510 | .len = items.len, | |
| 511 | .lhs = span.start, | |
| 512 | .rhs = span.end, | |
| 513 | .trailing = trailing, | |
| 514 | }; | |
| 515 | }, | |
| 516 | } | |
| 517 | } | |
| 518 | ||
| 519 | /// Attempts to find next container member by searching for certain tokens | |
| 520 | fn findNextContainerMember(p: *Parser) void { | |
| 521 | var level: u32 = 0; | |
| 522 | while (true) { | |
| 523 | const tok = p.nextToken(); | |
| 524 | switch (p.token_tags[tok]) { | |
| 525 | // Any of these can start a new top level declaration. | |
| 526 | .keyword_test, | |
| 527 | .keyword_comptime, | |
| 528 | .keyword_pub, | |
| 529 | .keyword_export, | |
| 530 | .keyword_extern, | |
| 531 | .keyword_inline, | |
| 532 | .keyword_noinline, | |
| 533 | .keyword_usingnamespace, | |
| 534 | .keyword_threadlocal, | |
| 535 | .keyword_const, | |
| 536 | .keyword_var, | |
| 537 | .keyword_fn, | |
| 538 | => { | |
| 539 | if (level == 0) { | |
| 540 | p.tok_i -= 1; | |
| 541 | return; | |
| 542 | } | |
| 543 | }, | |
| 544 | .identifier => { | |
| 545 | if (p.token_tags[tok + 1] == .comma and level == 0) { | |
| 546 | p.tok_i -= 1; | |
| 547 | return; | |
| 548 | } | |
| 549 | }, | |
| 550 | .comma, .semicolon => { | |
| 551 | // this decl was likely meant to end here | |
| 552 | if (level == 0) { | |
| 553 | return; | |
| 554 | } | |
| 555 | }, | |
| 556 | .l_paren, .l_bracket, .l_brace => level += 1, | |
| 557 | .r_paren, .r_bracket => { | |
| 558 | if (level != 0) level -= 1; | |
| 559 | }, | |
| 560 | .r_brace => { | |
| 561 | if (level == 0) { | |
| 562 | // end of container, exit | |
| 563 | p.tok_i -= 1; | |
| 564 | return; | |
| 565 | } | |
| 566 | level -= 1; | |
| 567 | }, | |
| 568 | .eof => { | |
| 569 | p.tok_i -= 1; | |
| 570 | return; | |
| 571 | }, | |
| 572 | else => {}, | |
| 573 | } | |
| 574 | } | |
| 575 | } | |
| 576 | ||
| 577 | /// Attempts to find the next statement by searching for a semicolon | |
| 578 | fn findNextStmt(p: *Parser) void { | |
| 579 | var level: u32 = 0; | |
| 580 | while (true) { | |
| 581 | const tok = p.nextToken(); | |
| 582 | switch (p.token_tags[tok]) { | |
| 583 | .l_brace => level += 1, | |
| 584 | .r_brace => { | |
| 585 | if (level == 0) { | |
| 586 | p.tok_i -= 1; | |
| 587 | return; | |
| 588 | } | |
| 589 | level -= 1; | |
| 590 | }, | |
| 591 | .semicolon => { | |
| 592 | if (level == 0) { | |
| 593 | return; | |
| 594 | } | |
| 595 | }, | |
| 596 | .eof => { | |
| 597 | p.tok_i -= 1; | |
| 598 | return; | |
| 599 | }, | |
| 600 | else => {}, | |
| 601 | } | |
| 602 | } | |
| 603 | } | |
| 604 | ||
| 605 | /// TestDecl <- KEYWORD_test (STRINGLITERALSINGLE / IDENTIFIER)? Block | |
| 606 | fn expectTestDecl(p: *Parser) !Node.Index { | |
| 607 | const test_token = p.assertToken(.keyword_test); | |
| 608 | const name_token = switch (p.token_tags[p.nextToken()]) { | |
| 609 | .string_literal, .identifier => p.tok_i - 1, | |
| 610 | else => blk: { | |
| 611 | p.tok_i -= 1; | |
| 612 | break :blk null; | |
| 613 | }, | |
| 614 | }; | |
| 615 | const block_node = try p.parseBlock(); | |
| 616 | if (block_node == 0) return p.fail(.expected_block); | |
| 617 | return p.addNode(.{ | |
| 618 | .tag = .test_decl, | |
| 619 | .main_token = test_token, | |
| 620 | .data = .{ | |
| 621 | .lhs = name_token orelse 0, | |
| 622 | .rhs = block_node, | |
| 623 | }, | |
| 624 | }); | |
| 625 | } | |
| 626 | ||
| 627 | fn expectTestDeclRecoverable(p: *Parser) error{OutOfMemory}!Node.Index { | |
| 628 | return p.expectTestDecl() catch |err| switch (err) { | |
| 629 | error.OutOfMemory => return error.OutOfMemory, | |
| 630 | error.ParseError => { | |
| 631 | p.findNextContainerMember(); | |
| 632 | return null_node; | |
| 633 | }, | |
| 634 | }; | |
| 635 | } | |
| 636 | ||
| 637 | /// Decl | |
| 638 | /// <- (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE? / (KEYWORD_inline / KEYWORD_noinline))? FnProto (SEMICOLON / Block) | |
| 639 | /// / (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE?)? KEYWORD_threadlocal? VarDecl | |
| 640 | /// / KEYWORD_usingnamespace Expr SEMICOLON | |
| 641 | fn expectTopLevelDecl(p: *Parser) !Node.Index { | |
| 642 | const extern_export_inline_token = p.nextToken(); | |
| 643 | var is_extern: bool = false; | |
| 644 | var expect_fn: bool = false; | |
| 645 | var expect_var_or_fn: bool = false; | |
| 646 | switch (p.token_tags[extern_export_inline_token]) { | |
| 647 | .keyword_extern => { | |
| 648 | _ = p.eatToken(.string_literal); | |
| 649 | is_extern = true; | |
| 650 | expect_var_or_fn = true; | |
| 651 | }, | |
| 652 | .keyword_export => expect_var_or_fn = true, | |
| 653 | .keyword_inline, .keyword_noinline => expect_fn = true, | |
| 654 | else => p.tok_i -= 1, | |
| 655 | } | |
| 656 | const fn_proto = try p.parseFnProto(); | |
| 657 | if (fn_proto != 0) { | |
| 658 | switch (p.token_tags[p.tok_i]) { | |
| 659 | .semicolon => { | |
| 660 | p.tok_i += 1; | |
| 661 | return fn_proto; | |
| 662 | }, | |
| 663 | .l_brace => { | |
| 664 | if (is_extern) { | |
| 665 | try p.warnMsg(.{ .tag = .extern_fn_body, .token = extern_export_inline_token }); | |
| 666 | return null_node; | |
| 667 | } | |
| 668 | const fn_decl_index = try p.reserveNode(.fn_decl); | |
| 669 | errdefer p.unreserveNode(fn_decl_index); | |
| 670 | ||
| 671 | const body_block = try p.parseBlock(); | |
| 672 | assert(body_block != 0); | |
| 673 | return p.setNode(fn_decl_index, .{ | |
| 674 | .tag = .fn_decl, | |
| 675 | .main_token = p.nodes.items(.main_token)[fn_proto], | |
| 676 | .data = .{ | |
| 677 | .lhs = fn_proto, | |
| 678 | .rhs = body_block, | |
| 679 | }, | |
| 680 | }); | |
| 681 | }, | |
| 682 | else => { | |
| 683 | // Since parseBlock only return error.ParseError on | |
| 684 | // a missing '}' we can assume this function was | |
| 685 | // supposed to end here. | |
| 686 | try p.warn(.expected_semi_or_lbrace); | |
| 687 | return null_node; | |
| 688 | }, | |
| 689 | } | |
| 690 | } | |
| 691 | if (expect_fn) { | |
| 692 | try p.warn(.expected_fn); | |
| 693 | return error.ParseError; | |
| 694 | } | |
| 695 | ||
| 696 | const thread_local_token = p.eatToken(.keyword_threadlocal); | |
| 697 | const var_decl = try p.parseVarDecl(); | |
| 698 | if (var_decl != 0) { | |
| 699 | try p.expectSemicolon(.expected_semi_after_decl, false); | |
| 700 | return var_decl; | |
| 701 | } | |
| 702 | if (thread_local_token != null) { | |
| 703 | return p.fail(.expected_var_decl); | |
| 704 | } | |
| 705 | if (expect_var_or_fn) { | |
| 706 | return p.fail(.expected_var_decl_or_fn); | |
| 707 | } | |
| 708 | if (p.token_tags[p.tok_i] != .keyword_usingnamespace) { | |
| 709 | return p.fail(.expected_pub_item); | |
| 710 | } | |
| 711 | return p.expectUsingNamespace(); | |
| 712 | } | |
| 713 | ||
| 714 | fn expectTopLevelDeclRecoverable(p: *Parser) error{OutOfMemory}!Node.Index { | |
| 715 | return p.expectTopLevelDecl() catch |err| switch (err) { | |
| 716 | error.OutOfMemory => return error.OutOfMemory, | |
| 717 | error.ParseError => { | |
| 718 | p.findNextContainerMember(); | |
| 719 | return null_node; | |
| 720 | }, | |
| 721 | }; | |
| 722 | } | |
| 723 | ||
| 724 | fn expectUsingNamespace(p: *Parser) !Node.Index { | |
| 725 | const usingnamespace_token = p.assertToken(.keyword_usingnamespace); | |
| 726 | const expr = try p.expectExpr(); | |
| 727 | try p.expectSemicolon(.expected_semi_after_decl, false); | |
| 728 | return p.addNode(.{ | |
| 729 | .tag = .@"usingnamespace", | |
| 730 | .main_token = usingnamespace_token, | |
| 731 | .data = .{ | |
| 732 | .lhs = expr, | |
| 733 | .rhs = undefined, | |
| 734 | }, | |
| 735 | }); | |
| 736 | } | |
| 737 | ||
| 738 | fn expectUsingNamespaceRecoverable(p: *Parser) error{OutOfMemory}!Node.Index { | |
| 739 | return p.expectUsingNamespace() catch |err| switch (err) { | |
| 740 | error.OutOfMemory => return error.OutOfMemory, | |
| 741 | error.ParseError => { | |
| 742 | p.findNextContainerMember(); | |
| 743 | return null_node; | |
| 744 | }, | |
| 745 | }; | |
| 746 | } | |
| 747 | ||
| 748 | /// FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? AddrSpace? LinkSection? CallConv? EXCLAMATIONMARK? TypeExpr | |
| 749 | fn parseFnProto(p: *Parser) !Node.Index { | |
| 750 | const fn_token = p.eatToken(.keyword_fn) orelse return null_node; | |
| 751 | ||
| 752 | // We want the fn proto node to be before its children in the array. | |
| 753 | const fn_proto_index = try p.reserveNode(.fn_proto); | |
| 754 | errdefer p.unreserveNode(fn_proto_index); | |
| 755 | ||
| 756 | _ = p.eatToken(.identifier); | |
| 757 | const params = try p.parseParamDeclList(); | |
| 758 | const align_expr = try p.parseByteAlign(); | |
| 759 | const addrspace_expr = try p.parseAddrSpace(); | |
| 760 | const section_expr = try p.parseLinkSection(); | |
| 761 | const callconv_expr = try p.parseCallconv(); | |
| 762 | _ = p.eatToken(.bang); | |
| 763 | ||
| 764 | const return_type_expr = try p.parseTypeExpr(); | |
| 765 | if (return_type_expr == 0) { | |
| 766 | // most likely the user forgot to specify the return type. | |
| 767 | // Mark return type as invalid and try to continue. | |
| 768 | try p.warn(.expected_return_type); | |
| 769 | } | |
| 770 | ||
| 771 | if (align_expr == 0 and section_expr == 0 and callconv_expr == 0 and addrspace_expr == 0) { | |
| 772 | switch (params) { | |
| 773 | .zero_or_one => |param| return p.setNode(fn_proto_index, .{ | |
| 774 | .tag = .fn_proto_simple, | |
| 775 | .main_token = fn_token, | |
| 776 | .data = .{ | |
| 777 | .lhs = param, | |
| 778 | .rhs = return_type_expr, | |
| 779 | }, | |
| 780 | }), | |
| 781 | .multi => |span| { | |
| 782 | return p.setNode(fn_proto_index, .{ | |
| 783 | .tag = .fn_proto_multi, | |
| 784 | .main_token = fn_token, | |
| 785 | .data = .{ | |
| 786 | .lhs = try p.addExtra(Node.SubRange{ | |
| 787 | .start = span.start, | |
| 788 | .end = span.end, | |
| 789 | }), | |
| 790 | .rhs = return_type_expr, | |
| 791 | }, | |
| 792 | }); | |
| 793 | }, | |
| 794 | } | |
| 795 | } | |
| 796 | switch (params) { | |
| 797 | .zero_or_one => |param| return p.setNode(fn_proto_index, .{ | |
| 798 | .tag = .fn_proto_one, | |
| 799 | .main_token = fn_token, | |
| 800 | .data = .{ | |
| 801 | .lhs = try p.addExtra(Node.FnProtoOne{ | |
| 802 | .param = param, | |
| 803 | .align_expr = align_expr, | |
| 804 | .addrspace_expr = addrspace_expr, | |
| 805 | .section_expr = section_expr, | |
| 806 | .callconv_expr = callconv_expr, | |
| 807 | }), | |
| 808 | .rhs = return_type_expr, | |
| 809 | }, | |
| 810 | }), | |
| 811 | .multi => |span| { | |
| 812 | return p.setNode(fn_proto_index, .{ | |
| 813 | .tag = .fn_proto, | |
| 814 | .main_token = fn_token, | |
| 815 | .data = .{ | |
| 816 | .lhs = try p.addExtra(Node.FnProto{ | |
| 817 | .params_start = span.start, | |
| 818 | .params_end = span.end, | |
| 819 | .align_expr = align_expr, | |
| 820 | .addrspace_expr = addrspace_expr, | |
| 821 | .section_expr = section_expr, | |
| 822 | .callconv_expr = callconv_expr, | |
| 823 | }), | |
| 824 | .rhs = return_type_expr, | |
| 825 | }, | |
| 826 | }); | |
| 827 | }, | |
| 828 | } | |
| 829 | } | |
| 830 | ||
| 831 | /// VarDecl <- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? AddrSpace? LinkSection? (EQUAL Expr)? SEMICOLON | |
| 832 | fn parseVarDecl(p: *Parser) !Node.Index { | |
| 833 | const mut_token = p.eatToken(.keyword_const) orelse | |
| 834 | p.eatToken(.keyword_var) orelse | |
| 835 | return null_node; | |
| 836 | ||
| 837 | _ = try p.expectToken(.identifier); | |
| 838 | const type_node: Node.Index = if (p.eatToken(.colon) == null) 0 else try p.expectTypeExpr(); | |
| 839 | const align_node = try p.parseByteAlign(); | |
| 840 | const addrspace_node = try p.parseAddrSpace(); | |
| 841 | const section_node = try p.parseLinkSection(); | |
| 842 | const init_node: Node.Index = switch (p.token_tags[p.tok_i]) { | |
| 843 | .equal_equal => blk: { | |
| 844 | try p.warn(.wrong_equal_var_decl); | |
| 845 | p.tok_i += 1; | |
| 846 | break :blk try p.expectExpr(); | |
| 847 | }, | |
| 848 | .equal => blk: { | |
| 849 | p.tok_i += 1; | |
| 850 | break :blk try p.expectExpr(); | |
| 851 | }, | |
| 852 | else => 0, | |
| 853 | }; | |
| 854 | if (section_node == 0 and addrspace_node == 0) { | |
| 855 | if (align_node == 0) { | |
| 856 | return p.addNode(.{ | |
| 857 | .tag = .simple_var_decl, | |
| 858 | .main_token = mut_token, | |
| 859 | .data = .{ | |
| 860 | .lhs = type_node, | |
| 861 | .rhs = init_node, | |
| 862 | }, | |
| 863 | }); | |
| 864 | } else if (type_node == 0) { | |
| 865 | return p.addNode(.{ | |
| 866 | .tag = .aligned_var_decl, | |
| 867 | .main_token = mut_token, | |
| 868 | .data = .{ | |
| 869 | .lhs = align_node, | |
| 870 | .rhs = init_node, | |
| 871 | }, | |
| 872 | }); | |
| 873 | } else { | |
| 874 | return p.addNode(.{ | |
| 875 | .tag = .local_var_decl, | |
| 876 | .main_token = mut_token, | |
| 877 | .data = .{ | |
| 878 | .lhs = try p.addExtra(Node.LocalVarDecl{ | |
| 879 | .type_node = type_node, | |
| 880 | .align_node = align_node, | |
| 881 | }), | |
| 882 | .rhs = init_node, | |
| 883 | }, | |
| 884 | }); | |
| 885 | } | |
| 886 | } else { | |
| 887 | return p.addNode(.{ | |
| 888 | .tag = .global_var_decl, | |
| 889 | .main_token = mut_token, | |
| 890 | .data = .{ | |
| 891 | .lhs = try p.addExtra(Node.GlobalVarDecl{ | |
| 892 | .type_node = type_node, | |
| 893 | .align_node = align_node, | |
| 894 | .addrspace_node = addrspace_node, | |
| 895 | .section_node = section_node, | |
| 896 | }), | |
| 897 | .rhs = init_node, | |
| 898 | }, | |
| 899 | }); | |
| 900 | } | |
| 901 | } | |
| 902 | ||
| 903 | /// ContainerField | |
| 904 | /// <- doc_comment? KEYWORD_comptime? IDENTIFIER (COLON TypeExpr)? ByteAlign? (EQUAL Expr)? | |
| 905 | /// / doc_comment? KEYWORD_comptime? (IDENTIFIER COLON)? !KEYWORD_fn TypeExpr ByteAlign? (EQUAL Expr)? | |
| 906 | fn expectContainerField(p: *Parser) !Node.Index { | |
| 907 | var main_token = p.tok_i; | |
| 908 | _ = p.eatToken(.keyword_comptime); | |
| 909 | const tuple_like = p.token_tags[p.tok_i] != .identifier or p.token_tags[p.tok_i + 1] != .colon; | |
| 910 | if (!tuple_like) { | |
| 911 | main_token = p.assertToken(.identifier); | |
| 912 | } | |
| 913 | ||
| 914 | var align_expr: Node.Index = 0; | |
| 915 | var type_expr: Node.Index = 0; | |
| 916 | if (p.eatToken(.colon) != null or tuple_like) { | |
| 917 | type_expr = try p.expectTypeExpr(); | |
| 918 | align_expr = try p.parseByteAlign(); | |
| 919 | } | |
| 920 | ||
| 921 | const value_expr: Node.Index = if (p.eatToken(.equal) == null) 0 else try p.expectExpr(); | |
| 922 | ||
| 923 | if (align_expr == 0) { | |
| 924 | return p.addNode(.{ | |
| 925 | .tag = .container_field_init, | |
| 926 | .main_token = main_token, | |
| 927 | .data = .{ | |
| 928 | .lhs = type_expr, | |
| 929 | .rhs = value_expr, | |
| 930 | }, | |
| 931 | }); | |
| 932 | } else if (value_expr == 0) { | |
| 933 | return p.addNode(.{ | |
| 934 | .tag = .container_field_align, | |
| 935 | .main_token = main_token, | |
| 936 | .data = .{ | |
| 937 | .lhs = type_expr, | |
| 938 | .rhs = align_expr, | |
| 939 | }, | |
| 940 | }); | |
| 941 | } else { | |
| 942 | return p.addNode(.{ | |
| 943 | .tag = .container_field, | |
| 944 | .main_token = main_token, | |
| 945 | .data = .{ | |
| 946 | .lhs = type_expr, | |
| 947 | .rhs = try p.addExtra(Node.ContainerField{ | |
| 948 | .value_expr = value_expr, | |
| 949 | .align_expr = align_expr, | |
| 950 | }), | |
| 951 | }, | |
| 952 | }); | |
| 953 | } | |
| 954 | } | |
| 955 | ||
| 956 | /// Statement | |
| 957 | /// <- KEYWORD_comptime? VarDecl | |
| 958 | /// / KEYWORD_comptime BlockExprStatement | |
| 959 | /// / KEYWORD_nosuspend BlockExprStatement | |
| 960 | /// / KEYWORD_suspend BlockExprStatement | |
| 961 | /// / KEYWORD_defer BlockExprStatement | |
| 962 | /// / KEYWORD_errdefer Payload? BlockExprStatement | |
| 963 | /// / IfStatement | |
| 964 | /// / LabeledStatement | |
| 965 | /// / SwitchExpr | |
| 966 | /// / AssignExpr SEMICOLON | |
| 967 | fn parseStatement(p: *Parser, allow_defer_var: bool) Error!Node.Index { | |
| 968 | const comptime_token = p.eatToken(.keyword_comptime); | |
| 969 | ||
| 970 | if (allow_defer_var) { | |
| 971 | const var_decl = try p.parseVarDecl(); | |
| 972 | if (var_decl != 0) { | |
| 973 | try p.expectSemicolon(.expected_semi_after_decl, true); | |
| 974 | return var_decl; | |
| 975 | } | |
| 976 | } | |
| 977 | ||
| 978 | if (comptime_token) |token| { | |
| 979 | return p.addNode(.{ | |
| 980 | .tag = .@"comptime", | |
| 981 | .main_token = token, | |
| 982 | .data = .{ | |
| 983 | .lhs = try p.expectBlockExprStatement(), | |
| 984 | .rhs = undefined, | |
| 985 | }, | |
| 986 | }); | |
| 987 | } | |
| 988 | ||
| 989 | switch (p.token_tags[p.tok_i]) { | |
| 990 | .keyword_nosuspend => { | |
| 991 | return p.addNode(.{ | |
| 992 | .tag = .@"nosuspend", | |
| 993 | .main_token = p.nextToken(), | |
| 994 | .data = .{ | |
| 995 | .lhs = try p.expectBlockExprStatement(), | |
| 996 | .rhs = undefined, | |
| 997 | }, | |
| 998 | }); | |
| 999 | }, | |
| 1000 | .keyword_suspend => { | |
| 1001 | const token = p.nextToken(); | |
| 1002 | const block_expr = try p.expectBlockExprStatement(); | |
| 1003 | return p.addNode(.{ | |
| 1004 | .tag = .@"suspend", | |
| 1005 | .main_token = token, | |
| 1006 | .data = .{ | |
| 1007 | .lhs = block_expr, | |
| 1008 | .rhs = undefined, | |
| 1009 | }, | |
| 1010 | }); | |
| 1011 | }, | |
| 1012 | .keyword_defer => if (allow_defer_var) return p.addNode(.{ | |
| 1013 | .tag = .@"defer", | |
| 1014 | .main_token = p.nextToken(), | |
| 1015 | .data = .{ | |
| 1016 | .lhs = undefined, | |
| 1017 | .rhs = try p.expectBlockExprStatement(), | |
| 1018 | }, | |
| 1019 | }), | |
| 1020 | .keyword_errdefer => if (allow_defer_var) return p.addNode(.{ | |
| 1021 | .tag = .@"errdefer", | |
| 1022 | .main_token = p.nextToken(), | |
| 1023 | .data = .{ | |
| 1024 | .lhs = try p.parsePayload(), | |
| 1025 | .rhs = try p.expectBlockExprStatement(), | |
| 1026 | }, | |
| 1027 | }), | |
| 1028 | .keyword_switch => return p.expectSwitchExpr(), | |
| 1029 | .keyword_if => return p.expectIfStatement(), | |
| 1030 | .keyword_enum, .keyword_struct, .keyword_union => { | |
| 1031 | const identifier = p.tok_i + 1; | |
| 1032 | if (try p.parseCStyleContainer()) { | |
| 1033 | // Return something so that `expectStatement` is happy. | |
| 1034 | return p.addNode(.{ | |
| 1035 | .tag = .identifier, | |
| 1036 | .main_token = identifier, | |
| 1037 | .data = .{ | |
| 1038 | .lhs = undefined, | |
| 1039 | .rhs = undefined, | |
| 1040 | }, | |
| 1041 | }); | |
| 1042 | } | |
| 1043 | }, | |
| 1044 | else => {}, | |
| 1045 | } | |
| 1046 | ||
| 1047 | const labeled_statement = try p.parseLabeledStatement(); | |
| 1048 | if (labeled_statement != 0) return labeled_statement; | |
| 1049 | ||
| 1050 | const assign_expr = try p.parseAssignExpr(); | |
| 1051 | if (assign_expr != 0) { | |
| 1052 | try p.expectSemicolon(.expected_semi_after_stmt, true); | |
| 1053 | return assign_expr; | |
| 1054 | } | |
| 1055 | ||
| 1056 | return null_node; | |
| 1057 | } | |
| 1058 | ||
| 1059 | fn expectStatement(p: *Parser, allow_defer_var: bool) !Node.Index { | |
| 1060 | const statement = try p.parseStatement(allow_defer_var); | |
| 1061 | if (statement == 0) { | |
| 1062 | return p.fail(.expected_statement); | |
| 1063 | } | |
| 1064 | return statement; | |
| 1065 | } | |
| 1066 | ||
| 1067 | /// If a parse error occurs, reports an error, but then finds the next statement | |
| 1068 | /// and returns that one instead. If a parse error occurs but there is no following | |
| 1069 | /// statement, returns 0. | |
| 1070 | fn expectStatementRecoverable(p: *Parser) Error!Node.Index { | |
| 1071 | while (true) { | |
| 1072 | return p.expectStatement(true) catch |err| switch (err) { | |
| 1073 | error.OutOfMemory => return error.OutOfMemory, | |
| 1074 | error.ParseError => { | |
| 1075 | p.findNextStmt(); // Try to skip to the next statement. | |
| 1076 | switch (p.token_tags[p.tok_i]) { | |
| 1077 | .r_brace => return null_node, | |
| 1078 | .eof => return error.ParseError, | |
| 1079 | else => continue, | |
| 1080 | } | |
| 1081 | }, | |
| 1082 | }; | |
| 1083 | } | |
| 1084 | } | |
| 1085 | ||
| 1086 | /// IfStatement | |
| 1087 | /// <- IfPrefix BlockExpr ( KEYWORD_else Payload? Statement )? | |
| 1088 | /// / IfPrefix AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement ) | |
| 1089 | fn expectIfStatement(p: *Parser) !Node.Index { | |
| 1090 | const if_token = p.assertToken(.keyword_if); | |
| 1091 | _ = try p.expectToken(.l_paren); | |
| 1092 | const condition = try p.expectExpr(); | |
| 1093 | _ = try p.expectToken(.r_paren); | |
| 1094 | _ = try p.parsePtrPayload(); | |
| 1095 | ||
| 1096 | // TODO propose to change the syntax so that semicolons are always required | |
| 1097 | // inside if statements, even if there is an `else`. | |
| 1098 | var else_required = false; | |
| 1099 | const then_expr = blk: { | |
| 1100 | const block_expr = try p.parseBlockExpr(); | |
| 1101 | if (block_expr != 0) break :blk block_expr; | |
| 1102 | const assign_expr = try p.parseAssignExpr(); | |
| 1103 | if (assign_expr == 0) { | |
| 1104 | return p.fail(.expected_block_or_assignment); | |
| 1105 | } | |
| 1106 | if (p.eatToken(.semicolon)) |_| { | |
| 1107 | return p.addNode(.{ | |
| 1108 | .tag = .if_simple, | |
| 1109 | .main_token = if_token, | |
| 1110 | .data = .{ | |
| 1111 | .lhs = condition, | |
| 1112 | .rhs = assign_expr, | |
| 1113 | }, | |
| 1114 | }); | |
| 1115 | } | |
| 1116 | else_required = true; | |
| 1117 | break :blk assign_expr; | |
| 1118 | }; | |
| 1119 | _ = p.eatToken(.keyword_else) orelse { | |
| 1120 | if (else_required) { | |
| 1121 | try p.warn(.expected_semi_or_else); | |
| 1122 | } | |
| 1123 | return p.addNode(.{ | |
| 1124 | .tag = .if_simple, | |
| 1125 | .main_token = if_token, | |
| 1126 | .data = .{ | |
| 1127 | .lhs = condition, | |
| 1128 | .rhs = then_expr, | |
| 1129 | }, | |
| 1130 | }); | |
| 1131 | }; | |
| 1132 | _ = try p.parsePayload(); | |
| 1133 | const else_expr = try p.expectStatement(false); | |
| 1134 | return p.addNode(.{ | |
| 1135 | .tag = .@"if", | |
| 1136 | .main_token = if_token, | |
| 1137 | .data = .{ | |
| 1138 | .lhs = condition, | |
| 1139 | .rhs = try p.addExtra(Node.If{ | |
| 1140 | .then_expr = then_expr, | |
| 1141 | .else_expr = else_expr, | |
| 1142 | }), | |
| 1143 | }, | |
| 1144 | }); | |
| 1145 | } | |
| 1146 | ||
| 1147 | /// LabeledStatement <- BlockLabel? (Block / LoopStatement) | |
| 1148 | fn parseLabeledStatement(p: *Parser) !Node.Index { | |
| 1149 | const label_token = p.parseBlockLabel(); | |
| 1150 | const block = try p.parseBlock(); | |
| 1151 | if (block != 0) return block; | |
| 1152 | ||
| 1153 | const loop_stmt = try p.parseLoopStatement(); | |
| 1154 | if (loop_stmt != 0) return loop_stmt; | |
| 1155 | ||
| 1156 | if (label_token != 0) { | |
| 1157 | const after_colon = p.tok_i; | |
| 1158 | const node = try p.parseTypeExpr(); | |
| 1159 | if (node != 0) { | |
| 1160 | const a = try p.parseByteAlign(); | |
| 1161 | const b = try p.parseAddrSpace(); | |
| 1162 | const c = try p.parseLinkSection(); | |
| 1163 | const d = if (p.eatToken(.equal) == null) 0 else try p.expectExpr(); | |
| 1164 | if (a != 0 or b != 0 or c != 0 or d != 0) { | |
| 1165 | return p.failMsg(.{ .tag = .expected_var_const, .token = label_token }); | |
| 1166 | } | |
| 1167 | } | |
| 1168 | return p.failMsg(.{ .tag = .expected_labelable, .token = after_colon }); | |
| 1169 | } | |
| 1170 | ||
| 1171 | return null_node; | |
| 1172 | } | |
| 1173 | ||
| 1174 | /// LoopStatement <- KEYWORD_inline? (ForStatement / WhileStatement) | |
| 1175 | fn parseLoopStatement(p: *Parser) !Node.Index { | |
| 1176 | const inline_token = p.eatToken(.keyword_inline); | |
| 1177 | ||
| 1178 | const for_statement = try p.parseForStatement(); | |
| 1179 | if (for_statement != 0) return for_statement; | |
| 1180 | ||
| 1181 | const while_statement = try p.parseWhileStatement(); | |
| 1182 | if (while_statement != 0) return while_statement; | |
| 1183 | ||
| 1184 | if (inline_token == null) return null_node; | |
| 1185 | ||
| 1186 | // If we've seen "inline", there should have been a "for" or "while" | |
| 1187 | return p.fail(.expected_inlinable); | |
| 1188 | } | |
| 1189 | ||
| 1190 | /// ForPrefix <- KEYWORD_for LPAREN Expr RPAREN PtrIndexPayload | |
| 1191 | /// | |
| 1192 | /// ForStatement | |
| 1193 | /// <- ForPrefix BlockExpr ( KEYWORD_else Statement )? | |
| 1194 | /// / ForPrefix AssignExpr ( SEMICOLON / KEYWORD_else Statement ) | |
| 1195 | fn parseForStatement(p: *Parser) !Node.Index { | |
| 1196 | const for_token = p.eatToken(.keyword_for) orelse return null_node; | |
| 1197 | _ = try p.expectToken(.l_paren); | |
| 1198 | const array_expr = try p.expectExpr(); | |
| 1199 | _ = try p.expectToken(.r_paren); | |
| 1200 | const found_payload = try p.parsePtrIndexPayload(); | |
| 1201 | if (found_payload == 0) try p.warn(.expected_loop_payload); | |
| 1202 | ||
| 1203 | // TODO propose to change the syntax so that semicolons are always required | |
| 1204 | // inside while statements, even if there is an `else`. | |
| 1205 | var else_required = false; | |
| 1206 | const then_expr = blk: { | |
| 1207 | const block_expr = try p.parseBlockExpr(); | |
| 1208 | if (block_expr != 0) break :blk block_expr; | |
| 1209 | const assign_expr = try p.parseAssignExpr(); | |
| 1210 | if (assign_expr == 0) { | |
| 1211 | return p.fail(.expected_block_or_assignment); | |
| 1212 | } | |
| 1213 | if (p.eatToken(.semicolon)) |_| { | |
| 1214 | return p.addNode(.{ | |
| 1215 | .tag = .for_simple, | |
| 1216 | .main_token = for_token, | |
| 1217 | .data = .{ | |
| 1218 | .lhs = array_expr, | |
| 1219 | .rhs = assign_expr, | |
| 1220 | }, | |
| 1221 | }); | |
| 1222 | } | |
| 1223 | else_required = true; | |
| 1224 | break :blk assign_expr; | |
| 1225 | }; | |
| 1226 | _ = p.eatToken(.keyword_else) orelse { | |
| 1227 | if (else_required) { | |
| 1228 | try p.warn(.expected_semi_or_else); | |
| 1229 | } | |
| 1230 | return p.addNode(.{ | |
| 1231 | .tag = .for_simple, | |
| 1232 | .main_token = for_token, | |
| 1233 | .data = .{ | |
| 1234 | .lhs = array_expr, | |
| 1235 | .rhs = then_expr, | |
| 1236 | }, | |
| 1237 | }); | |
| 1238 | }; | |
| 1239 | return p.addNode(.{ | |
| 1240 | .tag = .@"for", | |
| 1241 | .main_token = for_token, | |
| 1242 | .data = .{ | |
| 1243 | .lhs = array_expr, | |
| 1244 | .rhs = try p.addExtra(Node.If{ | |
| 1245 | .then_expr = then_expr, | |
| 1246 | .else_expr = try p.expectStatement(false), | |
| 1247 | }), | |
| 1248 | }, | |
| 1249 | }); | |
| 1250 | } | |
| 1251 | ||
| 1252 | /// WhilePrefix <- KEYWORD_while LPAREN Expr RPAREN PtrPayload? WhileContinueExpr? | |
| 1253 | /// | |
| 1254 | /// WhileStatement | |
| 1255 | /// <- WhilePrefix BlockExpr ( KEYWORD_else Payload? Statement )? | |
| 1256 | /// / WhilePrefix AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement ) | |
| 1257 | fn parseWhileStatement(p: *Parser) !Node.Index { | |
| 1258 | const while_token = p.eatToken(.keyword_while) orelse return null_node; | |
| 1259 | _ = try p.expectToken(.l_paren); | |
| 1260 | const condition = try p.expectExpr(); | |
| 1261 | _ = try p.expectToken(.r_paren); | |
| 1262 | _ = try p.parsePtrPayload(); | |
| 1263 | const cont_expr = try p.parseWhileContinueExpr(); | |
| 1264 | ||
| 1265 | // TODO propose to change the syntax so that semicolons are always required | |
| 1266 | // inside while statements, even if there is an `else`. | |
| 1267 | var else_required = false; | |
| 1268 | const then_expr = blk: { | |
| 1269 | const block_expr = try p.parseBlockExpr(); | |
| 1270 | if (block_expr != 0) break :blk block_expr; | |
| 1271 | const assign_expr = try p.parseAssignExpr(); | |
| 1272 | if (assign_expr == 0) { | |
| 1273 | return p.fail(.expected_block_or_assignment); | |
| 1274 | } | |
| 1275 | if (p.eatToken(.semicolon)) |_| { | |
| 1276 | if (cont_expr == 0) { | |
| 1277 | return p.addNode(.{ | |
| 1278 | .tag = .while_simple, | |
| 1279 | .main_token = while_token, | |
| 1280 | .data = .{ | |
| 1281 | .lhs = condition, | |
| 1282 | .rhs = assign_expr, | |
| 1283 | }, | |
| 1284 | }); | |
| 1285 | } else { | |
| 1286 | return p.addNode(.{ | |
| 1287 | .tag = .while_cont, | |
| 1288 | .main_token = while_token, | |
| 1289 | .data = .{ | |
| 1290 | .lhs = condition, | |
| 1291 | .rhs = try p.addExtra(Node.WhileCont{ | |
| 1292 | .cont_expr = cont_expr, | |
| 1293 | .then_expr = assign_expr, | |
| 1294 | }), | |
| 1295 | }, | |
| 1296 | }); | |
| 1297 | } | |
| 1298 | } | |
| 1299 | else_required = true; | |
| 1300 | break :blk assign_expr; | |
| 1301 | }; | |
| 1302 | _ = p.eatToken(.keyword_else) orelse { | |
| 1303 | if (else_required) { | |
| 1304 | try p.warn(.expected_semi_or_else); | |
| 1305 | } | |
| 1306 | if (cont_expr == 0) { | |
| 1307 | return p.addNode(.{ | |
| 1308 | .tag = .while_simple, | |
| 1309 | .main_token = while_token, | |
| 1310 | .data = .{ | |
| 1311 | .lhs = condition, | |
| 1312 | .rhs = then_expr, | |
| 1313 | }, | |
| 1314 | }); | |
| 1315 | } else { | |
| 1316 | return p.addNode(.{ | |
| 1317 | .tag = .while_cont, | |
| 1318 | .main_token = while_token, | |
| 1319 | .data = .{ | |
| 1320 | .lhs = condition, | |
| 1321 | .rhs = try p.addExtra(Node.WhileCont{ | |
| 1322 | .cont_expr = cont_expr, | |
| 1323 | .then_expr = then_expr, | |
| 1324 | }), | |
| 1325 | }, | |
| 1326 | }); | |
| 1327 | } | |
| 1328 | }; | |
| 1329 | _ = try p.parsePayload(); | |
| 1330 | const else_expr = try p.expectStatement(false); | |
| 1331 | return p.addNode(.{ | |
| 1332 | .tag = .@"while", | |
| 1333 | .main_token = while_token, | |
| 1334 | .data = .{ | |
| 1335 | .lhs = condition, | |
| 1336 | .rhs = try p.addExtra(Node.While{ | |
| 1337 | .cont_expr = cont_expr, | |
| 1338 | .then_expr = then_expr, | |
| 1339 | .else_expr = else_expr, | |
| 1340 | }), | |
| 1341 | }, | |
| 1342 | }); | |
| 1343 | } | |
| 1344 | ||
| 1345 | /// BlockExprStatement | |
| 1346 | /// <- BlockExpr | |
| 1347 | /// / AssignExpr SEMICOLON | |
| 1348 | fn parseBlockExprStatement(p: *Parser) !Node.Index { | |
| 1349 | const block_expr = try p.parseBlockExpr(); | |
| 1350 | if (block_expr != 0) { | |
| 1351 | return block_expr; | |
| 1352 | } | |
| 1353 | const assign_expr = try p.parseAssignExpr(); | |
| 1354 | if (assign_expr != 0) { | |
| 1355 | try p.expectSemicolon(.expected_semi_after_stmt, true); | |
| 1356 | return assign_expr; | |
| 1357 | } | |
| 1358 | return null_node; | |
| 1359 | } | |
| 1360 | ||
| 1361 | fn expectBlockExprStatement(p: *Parser) !Node.Index { | |
| 1362 | const node = try p.parseBlockExprStatement(); | |
| 1363 | if (node == 0) { | |
| 1364 | return p.fail(.expected_block_or_expr); | |
| 1365 | } | |
| 1366 | return node; | |
| 1367 | } | |
| 1368 | ||
| 1369 | /// BlockExpr <- BlockLabel? Block | |
| 1370 | fn parseBlockExpr(p: *Parser) Error!Node.Index { | |
| 1371 | switch (p.token_tags[p.tok_i]) { | |
| 1372 | .identifier => { | |
| 1373 | if (p.token_tags[p.tok_i + 1] == .colon and | |
| 1374 | p.token_tags[p.tok_i + 2] == .l_brace) | |
| 1375 | { | |
| 1376 | p.tok_i += 2; | |
| 1377 | return p.parseBlock(); | |
| 1378 | } else { | |
| 1379 | return null_node; | |
| 1380 | } | |
| 1381 | }, | |
| 1382 | .l_brace => return p.parseBlock(), | |
| 1383 | else => return null_node, | |
| 1384 | } | |
| 1385 | } | |
| 1386 | ||
| 1387 | /// AssignExpr <- Expr (AssignOp Expr)? | |
| 1388 | /// | |
| 1389 | /// AssignOp | |
| 1390 | /// <- ASTERISKEQUAL | |
| 1391 | /// / ASTERISKPIPEEQUAL | |
| 1392 | /// / SLASHEQUAL | |
| 1393 | /// / PERCENTEQUAL | |
| 1394 | /// / PLUSEQUAL | |
| 1395 | /// / PLUSPIPEEQUAL | |
| 1396 | /// / MINUSEQUAL | |
| 1397 | /// / MINUSPIPEEQUAL | |
| 1398 | /// / LARROW2EQUAL | |
| 1399 | /// / LARROW2PIPEEQUAL | |
| 1400 | /// / RARROW2EQUAL | |
| 1401 | /// / AMPERSANDEQUAL | |
| 1402 | /// / CARETEQUAL | |
| 1403 | /// / PIPEEQUAL | |
| 1404 | /// / ASTERISKPERCENTEQUAL | |
| 1405 | /// / PLUSPERCENTEQUAL | |
| 1406 | /// / MINUSPERCENTEQUAL | |
| 1407 | /// / EQUAL | |
| 1408 | fn parseAssignExpr(p: *Parser) !Node.Index { | |
| 1409 | const expr = try p.parseExpr(); | |
| 1410 | if (expr == 0) return null_node; | |
| 1411 | ||
| 1412 | const tag: Node.Tag = switch (p.token_tags[p.tok_i]) { | |
| 1413 | .asterisk_equal => .assign_mul, | |
| 1414 | .slash_equal => .assign_div, | |
| 1415 | .percent_equal => .assign_mod, | |
| 1416 | .plus_equal => .assign_add, | |
| 1417 | .minus_equal => .assign_sub, | |
| 1418 | .angle_bracket_angle_bracket_left_equal => .assign_shl, | |
| 1419 | .angle_bracket_angle_bracket_left_pipe_equal => .assign_shl_sat, | |
| 1420 | .angle_bracket_angle_bracket_right_equal => .assign_shr, | |
| 1421 | .ampersand_equal => .assign_bit_and, | |
| 1422 | .caret_equal => .assign_bit_xor, | |
| 1423 | .pipe_equal => .assign_bit_or, | |
| 1424 | .asterisk_percent_equal => .assign_mul_wrap, | |
| 1425 | .plus_percent_equal => .assign_add_wrap, | |
| 1426 | .minus_percent_equal => .assign_sub_wrap, | |
| 1427 | .asterisk_pipe_equal => .assign_mul_sat, | |
| 1428 | .plus_pipe_equal => .assign_add_sat, | |
| 1429 | .minus_pipe_equal => .assign_sub_sat, | |
| 1430 | .equal => .assign, | |
| 1431 | else => return expr, | |
| 1432 | }; | |
| 1433 | return p.addNode(.{ | |
| 1434 | .tag = tag, | |
| 1435 | .main_token = p.nextToken(), | |
| 1436 | .data = .{ | |
| 1437 | .lhs = expr, | |
| 1438 | .rhs = try p.expectExpr(), | |
| 1439 | }, | |
| 1440 | }); | |
| 1441 | } | |
| 1442 | ||
| 1443 | fn expectAssignExpr(p: *Parser) !Node.Index { | |
| 1444 | const expr = try p.parseAssignExpr(); | |
| 1445 | if (expr == 0) { | |
| 1446 | return p.fail(.expected_expr_or_assignment); | |
| 1447 | } | |
| 1448 | return expr; | |
| 1449 | } | |
| 1450 | ||
| 1451 | fn parseExpr(p: *Parser) Error!Node.Index { | |
| 1452 | return p.parseExprPrecedence(0); | |
| 1453 | } | |
| 1454 | ||
| 1455 | fn expectExpr(p: *Parser) Error!Node.Index { | |
| 1456 | const node = try p.parseExpr(); | |
| 1457 | if (node == 0) { | |
| 1458 | return p.fail(.expected_expr); | |
| 1459 | } else { | |
| 1460 | return node; | |
| 1461 | } | |
| 1462 | } | |
| 1463 | ||
| 1464 | const Assoc = enum { | |
| 1465 | left, | |
| 1466 | none, | |
| 1467 | }; | |
| 1468 | ||
| 1469 | const OperInfo = struct { | |
| 1470 | prec: i8, | |
| 1471 | tag: Node.Tag, | |
| 1472 | assoc: Assoc = Assoc.left, | |
| 1473 | }; | |
| 1474 | ||
| 1475 | // A table of binary operator information. Higher precedence numbers are | |
| 1476 | // stickier. All operators at the same precedence level should have the same | |
| 1477 | // associativity. | |
| 1478 | const operTable = std.enums.directEnumArrayDefault(Token.Tag, OperInfo, .{ .prec = -1, .tag = Node.Tag.root }, 0, .{ | |
| 1479 | .keyword_or = .{ .prec = 10, .tag = .bool_or }, | |
| 1480 | ||
| 1481 | .keyword_and = .{ .prec = 20, .tag = .bool_and }, | |
| 1482 | ||
| 1483 | .equal_equal = .{ .prec = 30, .tag = .equal_equal, .assoc = Assoc.none }, | |
| 1484 | .bang_equal = .{ .prec = 30, .tag = .bang_equal, .assoc = Assoc.none }, | |
| 1485 | .angle_bracket_left = .{ .prec = 30, .tag = .less_than, .assoc = Assoc.none }, | |
| 1486 | .angle_bracket_right = .{ .prec = 30, .tag = .greater_than, .assoc = Assoc.none }, | |
| 1487 | .angle_bracket_left_equal = .{ .prec = 30, .tag = .less_or_equal, .assoc = Assoc.none }, | |
| 1488 | .angle_bracket_right_equal = .{ .prec = 30, .tag = .greater_or_equal, .assoc = Assoc.none }, | |
| 1489 | ||
| 1490 | .ampersand = .{ .prec = 40, .tag = .bit_and }, | |
| 1491 | .caret = .{ .prec = 40, .tag = .bit_xor }, | |
| 1492 | .pipe = .{ .prec = 40, .tag = .bit_or }, | |
| 1493 | .keyword_orelse = .{ .prec = 40, .tag = .@"orelse" }, | |
| 1494 | .keyword_catch = .{ .prec = 40, .tag = .@"catch" }, | |
| 1495 | ||
| 1496 | .angle_bracket_angle_bracket_left = .{ .prec = 50, .tag = .shl }, | |
| 1497 | .angle_bracket_angle_bracket_left_pipe = .{ .prec = 50, .tag = .shl_sat }, | |
| 1498 | .angle_bracket_angle_bracket_right = .{ .prec = 50, .tag = .shr }, | |
| 1499 | ||
| 1500 | .plus = .{ .prec = 60, .tag = .add }, | |
| 1501 | .minus = .{ .prec = 60, .tag = .sub }, | |
| 1502 | .plus_plus = .{ .prec = 60, .tag = .array_cat }, | |
| 1503 | .plus_percent = .{ .prec = 60, .tag = .add_wrap }, | |
| 1504 | .minus_percent = .{ .prec = 60, .tag = .sub_wrap }, | |
| 1505 | .plus_pipe = .{ .prec = 60, .tag = .add_sat }, | |
| 1506 | .minus_pipe = .{ .prec = 60, .tag = .sub_sat }, | |
| 1507 | ||
| 1508 | .pipe_pipe = .{ .prec = 70, .tag = .merge_error_sets }, | |
| 1509 | .asterisk = .{ .prec = 70, .tag = .mul }, | |
| 1510 | .slash = .{ .prec = 70, .tag = .div }, | |
| 1511 | .percent = .{ .prec = 70, .tag = .mod }, | |
| 1512 | .asterisk_asterisk = .{ .prec = 70, .tag = .array_mult }, | |
| 1513 | .asterisk_percent = .{ .prec = 70, .tag = .mul_wrap }, | |
| 1514 | .asterisk_pipe = .{ .prec = 70, .tag = .mul_sat }, | |
| 1515 | }); | |
| 1516 | ||
| 1517 | fn parseExprPrecedence(p: *Parser, min_prec: i32) Error!Node.Index { | |
| 1518 | assert(min_prec >= 0); | |
| 1519 | var node = try p.parsePrefixExpr(); | |
| 1520 | if (node == 0) { | |
| 1521 | return null_node; | |
| 1522 | } | |
| 1523 | ||
| 1524 | var banned_prec: i8 = -1; | |
| 1525 | ||
| 1526 | while (true) { | |
| 1527 | const tok_tag = p.token_tags[p.tok_i]; | |
| 1528 | const info = operTable[@intCast(usize, @enumToInt(tok_tag))]; | |
| 1529 | if (info.prec < min_prec) { | |
| 1530 | break; | |
| 1531 | } | |
| 1532 | if (info.prec == banned_prec) { | |
| 1533 | return p.fail(.chained_comparison_operators); | |
| 1534 | } | |
| 1535 | ||
| 1536 | const oper_token = p.nextToken(); | |
| 1537 | // Special-case handling for "catch" | |
| 1538 | if (tok_tag == .keyword_catch) { | |
| 1539 | _ = try p.parsePayload(); | |
| 1540 | } | |
| 1541 | const rhs = try p.parseExprPrecedence(info.prec + 1); | |
| 1542 | if (rhs == 0) { | |
| 1543 | try p.warn(.expected_expr); | |
| 1544 | return node; | |
| 1545 | } | |
| 1546 | ||
| 1547 | { | |
| 1548 | const tok_len = tok_tag.lexeme().?.len; | |
| 1549 | const char_before = p.source[p.token_starts[oper_token] - 1]; | |
| 1550 | const char_after = p.source[p.token_starts[oper_token] + tok_len]; | |
| 1551 | if (tok_tag == .ampersand and char_after == '&') { | |
| 1552 | // without types we don't know if '&&' was intended as 'bitwise_and address_of', or a c-style logical_and | |
| 1553 | // The best the parser can do is recommend changing it to 'and' or ' & &' | |
| 1554 | try p.warnMsg(.{ .tag = .invalid_ampersand_ampersand, .token = oper_token }); | |
| 1555 | } else if (std.ascii.isWhitespace(char_before) != std.ascii.isWhitespace(char_after)) { | |
| 1556 | try p.warnMsg(.{ .tag = .mismatched_binary_op_whitespace, .token = oper_token }); | |
| 1557 | } | |
| 1558 | } | |
| 1559 | ||
| 1560 | node = try p.addNode(.{ | |
| 1561 | .tag = info.tag, | |
| 1562 | .main_token = oper_token, | |
| 1563 | .data = .{ | |
| 1564 | .lhs = node, | |
| 1565 | .rhs = rhs, | |
| 1566 | }, | |
| 1567 | }); | |
| 1568 | ||
| 1569 | if (info.assoc == Assoc.none) { | |
| 1570 | banned_prec = info.prec; | |
| 1571 | } | |
| 1572 | } | |
| 1573 | ||
| 1574 | return node; | |
| 1575 | } | |
| 1576 | ||
| 1577 | /// PrefixExpr <- PrefixOp* PrimaryExpr | |
| 1578 | /// | |
| 1579 | /// PrefixOp | |
| 1580 | /// <- EXCLAMATIONMARK | |
| 1581 | /// / MINUS | |
| 1582 | /// / TILDE | |
| 1583 | /// / MINUSPERCENT | |
| 1584 | /// / AMPERSAND | |
| 1585 | /// / KEYWORD_try | |
| 1586 | /// / KEYWORD_await | |
| 1587 | fn parsePrefixExpr(p: *Parser) Error!Node.Index { | |
| 1588 | const tag: Node.Tag = switch (p.token_tags[p.tok_i]) { | |
| 1589 | .bang => .bool_not, | |
| 1590 | .minus => .negation, | |
| 1591 | .tilde => .bit_not, | |
| 1592 | .minus_percent => .negation_wrap, | |
| 1593 | .ampersand => .address_of, | |
| 1594 | .keyword_try => .@"try", | |
| 1595 | .keyword_await => .@"await", | |
| 1596 | else => return p.parsePrimaryExpr(), | |
| 1597 | }; | |
| 1598 | return p.addNode(.{ | |
| 1599 | .tag = tag, | |
| 1600 | .main_token = p.nextToken(), | |
| 1601 | .data = .{ | |
| 1602 | .lhs = try p.expectPrefixExpr(), | |
| 1603 | .rhs = undefined, | |
| 1604 | }, | |
| 1605 | }); | |
| 1606 | } | |
| 1607 | ||
| 1608 | fn expectPrefixExpr(p: *Parser) Error!Node.Index { | |
| 1609 | const node = try p.parsePrefixExpr(); | |
| 1610 | if (node == 0) { | |
| 1611 | return p.fail(.expected_prefix_expr); | |
| 1612 | } | |
| 1613 | return node; | |
| 1614 | } | |
| 1615 | ||
| 1616 | /// TypeExpr <- PrefixTypeOp* ErrorUnionExpr | |
| 1617 | /// | |
| 1618 | /// PrefixTypeOp | |
| 1619 | /// <- QUESTIONMARK | |
| 1620 | /// / KEYWORD_anyframe MINUSRARROW | |
| 1621 | /// / SliceTypeStart (ByteAlign / AddrSpace / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)* | |
| 1622 | /// / PtrTypeStart (AddrSpace / KEYWORD_align LPAREN Expr (COLON Expr COLON Expr)? RPAREN / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)* | |
| 1623 | /// / ArrayTypeStart | |
| 1624 | /// | |
| 1625 | /// SliceTypeStart <- LBRACKET (COLON Expr)? RBRACKET | |
| 1626 | /// | |
| 1627 | /// PtrTypeStart | |
| 1628 | /// <- ASTERISK | |
| 1629 | /// / ASTERISK2 | |
| 1630 | /// / LBRACKET ASTERISK (LETTERC / COLON Expr)? RBRACKET | |
| 1631 | /// | |
| 1632 | /// ArrayTypeStart <- LBRACKET Expr (COLON Expr)? RBRACKET | |
| 1633 | fn parseTypeExpr(p: *Parser) Error!Node.Index { | |
| 1634 | switch (p.token_tags[p.tok_i]) { | |
| 1635 | .question_mark => return p.addNode(.{ | |
| 1636 | .tag = .optional_type, | |
| 1637 | .main_token = p.nextToken(), | |
| 1638 | .data = .{ | |
| 1639 | .lhs = try p.expectTypeExpr(), | |
| 1640 | .rhs = undefined, | |
| 1641 | }, | |
| 1642 | }), | |
| 1643 | .keyword_anyframe => switch (p.token_tags[p.tok_i + 1]) { | |
| 1644 | .arrow => return p.addNode(.{ | |
| 1645 | .tag = .anyframe_type, | |
| 1646 | .main_token = p.nextToken(), | |
| 1647 | .data = .{ | |
| 1648 | .lhs = p.nextToken(), | |
| 1649 | .rhs = try p.expectTypeExpr(), | |
| 1650 | }, | |
| 1651 | }), | |
| 1652 | else => return p.parseErrorUnionExpr(), | |
| 1653 | }, | |
| 1654 | .asterisk => { | |
| 1655 | const asterisk = p.nextToken(); | |
| 1656 | const mods = try p.parsePtrModifiers(); | |
| 1657 | const elem_type = try p.expectTypeExpr(); | |
| 1658 | if (mods.bit_range_start != 0) { | |
| 1659 | return p.addNode(.{ | |
| 1660 | .tag = .ptr_type_bit_range, | |
| 1661 | .main_token = asterisk, | |
| 1662 | .data = .{ | |
| 1663 | .lhs = try p.addExtra(Node.PtrTypeBitRange{ | |
| 1664 | .sentinel = 0, | |
| 1665 | .align_node = mods.align_node, | |
| 1666 | .addrspace_node = mods.addrspace_node, | |
| 1667 | .bit_range_start = mods.bit_range_start, | |
| 1668 | .bit_range_end = mods.bit_range_end, | |
| 1669 | }), | |
| 1670 | .rhs = elem_type, | |
| 1671 | }, | |
| 1672 | }); | |
| 1673 | } else if (mods.addrspace_node != 0) { | |
| 1674 | return p.addNode(.{ | |
| 1675 | .tag = .ptr_type, | |
| 1676 | .main_token = asterisk, | |
| 1677 | .data = .{ | |
| 1678 | .lhs = try p.addExtra(Node.PtrType{ | |
| 1679 | .sentinel = 0, | |
| 1680 | .align_node = mods.align_node, | |
| 1681 | .addrspace_node = mods.addrspace_node, | |
| 1682 | }), | |
| 1683 | .rhs = elem_type, | |
| 1684 | }, | |
| 1685 | }); | |
| 1686 | } else { | |
| 1687 | return p.addNode(.{ | |
| 1688 | .tag = .ptr_type_aligned, | |
| 1689 | .main_token = asterisk, | |
| 1690 | .data = .{ | |
| 1691 | .lhs = mods.align_node, | |
| 1692 | .rhs = elem_type, | |
| 1693 | }, | |
| 1694 | }); | |
| 1695 | } | |
| 1696 | }, | |
| 1697 | .asterisk_asterisk => { | |
| 1698 | const asterisk = p.nextToken(); | |
| 1699 | const mods = try p.parsePtrModifiers(); | |
| 1700 | const elem_type = try p.expectTypeExpr(); | |
| 1701 | const inner: Node.Index = inner: { | |
| 1702 | if (mods.bit_range_start != 0) { | |
| 1703 | break :inner try p.addNode(.{ | |
| 1704 | .tag = .ptr_type_bit_range, | |
| 1705 | .main_token = asterisk, | |
| 1706 | .data = .{ | |
| 1707 | .lhs = try p.addExtra(Node.PtrTypeBitRange{ | |
| 1708 | .sentinel = 0, | |
| 1709 | .align_node = mods.align_node, | |
| 1710 | .addrspace_node = mods.addrspace_node, | |
| 1711 | .bit_range_start = mods.bit_range_start, | |
| 1712 | .bit_range_end = mods.bit_range_end, | |
| 1713 | }), | |
| 1714 | .rhs = elem_type, | |
| 1715 | }, | |
| 1716 | }); | |
| 1717 | } else if (mods.addrspace_node != 0) { | |
| 1718 | break :inner try p.addNode(.{ | |
| 1719 | .tag = .ptr_type, | |
| 1720 | .main_token = asterisk, | |
| 1721 | .data = .{ | |
| 1722 | .lhs = try p.addExtra(Node.PtrType{ | |
| 1723 | .sentinel = 0, | |
| 1724 | .align_node = mods.align_node, | |
| 1725 | .addrspace_node = mods.addrspace_node, | |
| 1726 | }), | |
| 1727 | .rhs = elem_type, | |
| 1728 | }, | |
| 1729 | }); | |
| 1730 | } else { | |
| 1731 | break :inner try p.addNode(.{ | |
| 1732 | .tag = .ptr_type_aligned, | |
| 1733 | .main_token = asterisk, | |
| 1734 | .data = .{ | |
| 1735 | .lhs = mods.align_node, | |
| 1736 | .rhs = elem_type, | |
| 1737 | }, | |
| 1738 | }); | |
| 1739 | } | |
| 1740 | }; | |
| 1741 | return p.addNode(.{ | |
| 1742 | .tag = .ptr_type_aligned, | |
| 1743 | .main_token = asterisk, | |
| 1744 | .data = .{ | |
| 1745 | .lhs = 0, | |
| 1746 | .rhs = inner, | |
| 1747 | }, | |
| 1748 | }); | |
| 1749 | }, | |
| 1750 | .l_bracket => switch (p.token_tags[p.tok_i + 1]) { | |
| 1751 | .asterisk => { | |
| 1752 | _ = p.nextToken(); | |
| 1753 | const asterisk = p.nextToken(); | |
| 1754 | var sentinel: Node.Index = 0; | |
| 1755 | if (p.eatToken(.identifier)) |ident| { | |
| 1756 | const ident_slice = p.source[p.token_starts[ident]..p.token_starts[ident + 1]]; | |
| 1757 | if (!std.mem.eql(u8, std.mem.trimRight(u8, ident_slice, &std.ascii.whitespace), "c")) { | |
| 1758 | p.tok_i -= 1; | |
| 1759 | } | |
| 1760 | } else if (p.eatToken(.colon)) |_| { | |
| 1761 | sentinel = try p.expectExpr(); | |
| 1762 | } | |
| 1763 | _ = try p.expectToken(.r_bracket); | |
| 1764 | const mods = try p.parsePtrModifiers(); | |
| 1765 | const elem_type = try p.expectTypeExpr(); | |
| 1766 | if (mods.bit_range_start == 0) { | |
| 1767 | if (sentinel == 0 and mods.addrspace_node == 0) { | |
| 1768 | return p.addNode(.{ | |
| 1769 | .tag = .ptr_type_aligned, | |
| 1770 | .main_token = asterisk, | |
| 1771 | .data = .{ | |
| 1772 | .lhs = mods.align_node, | |
| 1773 | .rhs = elem_type, | |
| 1774 | }, | |
| 1775 | }); | |
| 1776 | } else if (mods.align_node == 0 and mods.addrspace_node == 0) { | |
| 1777 | return p.addNode(.{ | |
| 1778 | .tag = .ptr_type_sentinel, | |
| 1779 | .main_token = asterisk, | |
| 1780 | .data = .{ | |
| 1781 | .lhs = sentinel, | |
| 1782 | .rhs = elem_type, | |
| 1783 | }, | |
| 1784 | }); | |
| 1785 | } else { | |
| 1786 | return p.addNode(.{ | |
| 1787 | .tag = .ptr_type, | |
| 1788 | .main_token = asterisk, | |
| 1789 | .data = .{ | |
| 1790 | .lhs = try p.addExtra(Node.PtrType{ | |
| 1791 | .sentinel = sentinel, | |
| 1792 | .align_node = mods.align_node, | |
| 1793 | .addrspace_node = mods.addrspace_node, | |
| 1794 | }), | |
| 1795 | .rhs = elem_type, | |
| 1796 | }, | |
| 1797 | }); | |
| 1798 | } | |
| 1799 | } else { | |
| 1800 | return p.addNode(.{ | |
| 1801 | .tag = .ptr_type_bit_range, | |
| 1802 | .main_token = asterisk, | |
| 1803 | .data = .{ | |
| 1804 | .lhs = try p.addExtra(Node.PtrTypeBitRange{ | |
| 1805 | .sentinel = sentinel, | |
| 1806 | .align_node = mods.align_node, | |
| 1807 | .addrspace_node = mods.addrspace_node, | |
| 1808 | .bit_range_start = mods.bit_range_start, | |
| 1809 | .bit_range_end = mods.bit_range_end, | |
| 1810 | }), | |
| 1811 | .rhs = elem_type, | |
| 1812 | }, | |
| 1813 | }); | |
| 1814 | } | |
| 1815 | }, | |
| 1816 | else => { | |
| 1817 | const lbracket = p.nextToken(); | |
| 1818 | const len_expr = try p.parseExpr(); | |
| 1819 | const sentinel: Node.Index = if (p.eatToken(.colon)) |_| | |
| 1820 | try p.expectExpr() | |
| 1821 | else | |
| 1822 | 0; | |
| 1823 | _ = try p.expectToken(.r_bracket); | |
| 1824 | if (len_expr == 0) { | |
| 1825 | const mods = try p.parsePtrModifiers(); | |
| 1826 | const elem_type = try p.expectTypeExpr(); | |
| 1827 | if (mods.bit_range_start != 0) { | |
| 1828 | try p.warnMsg(.{ | |
| 1829 | .tag = .invalid_bit_range, | |
| 1830 | .token = p.nodes.items(.main_token)[mods.bit_range_start], | |
| 1831 | }); | |
| 1832 | } | |
| 1833 | if (sentinel == 0 and mods.addrspace_node == 0) { | |
| 1834 | return p.addNode(.{ | |
| 1835 | .tag = .ptr_type_aligned, | |
| 1836 | .main_token = lbracket, | |
| 1837 | .data = .{ | |
| 1838 | .lhs = mods.align_node, | |
| 1839 | .rhs = elem_type, | |
| 1840 | }, | |
| 1841 | }); | |
| 1842 | } else if (mods.align_node == 0 and mods.addrspace_node == 0) { | |
| 1843 | return p.addNode(.{ | |
| 1844 | .tag = .ptr_type_sentinel, | |
| 1845 | .main_token = lbracket, | |
| 1846 | .data = .{ | |
| 1847 | .lhs = sentinel, | |
| 1848 | .rhs = elem_type, | |
| 1849 | }, | |
| 1850 | }); | |
| 1851 | } else { | |
| 1852 | return p.addNode(.{ | |
| 1853 | .tag = .ptr_type, | |
| 1854 | .main_token = lbracket, | |
| 1855 | .data = .{ | |
| 1856 | .lhs = try p.addExtra(Node.PtrType{ | |
| 1857 | .sentinel = sentinel, | |
| 1858 | .align_node = mods.align_node, | |
| 1859 | .addrspace_node = mods.addrspace_node, | |
| 1860 | }), | |
| 1861 | .rhs = elem_type, | |
| 1862 | }, | |
| 1863 | }); | |
| 1864 | } | |
| 1865 | } else { | |
| 1866 | switch (p.token_tags[p.tok_i]) { | |
| 1867 | .keyword_align, | |
| 1868 | .keyword_const, | |
| 1869 | .keyword_volatile, | |
| 1870 | .keyword_allowzero, | |
| 1871 | .keyword_addrspace, | |
| 1872 | => return p.fail(.ptr_mod_on_array_child_type), | |
| 1873 | else => {}, | |
| 1874 | } | |
| 1875 | const elem_type = try p.expectTypeExpr(); | |
| 1876 | if (sentinel == 0) { | |
| 1877 | return p.addNode(.{ | |
| 1878 | .tag = .array_type, | |
| 1879 | .main_token = lbracket, | |
| 1880 | .data = .{ | |
| 1881 | .lhs = len_expr, | |
| 1882 | .rhs = elem_type, | |
| 1883 | }, | |
| 1884 | }); | |
| 1885 | } else { | |
| 1886 | return p.addNode(.{ | |
| 1887 | .tag = .array_type_sentinel, | |
| 1888 | .main_token = lbracket, | |
| 1889 | .data = .{ | |
| 1890 | .lhs = len_expr, | |
| 1891 | .rhs = try p.addExtra(.{ | |
| 1892 | .elem_type = elem_type, | |
| 1893 | .sentinel = sentinel, | |
| 1894 | }), | |
| 1895 | }, | |
| 1896 | }); | |
| 1897 | } | |
| 1898 | } | |
| 1899 | }, | |
| 1900 | }, | |
| 1901 | else => return p.parseErrorUnionExpr(), | |
| 1902 | } | |
| 1903 | } | |
| 1904 | ||
| 1905 | fn expectTypeExpr(p: *Parser) Error!Node.Index { | |
| 1906 | const node = try p.parseTypeExpr(); | |
| 1907 | if (node == 0) { | |
| 1908 | return p.fail(.expected_type_expr); | |
| 1909 | } | |
| 1910 | return node; | |
| 1911 | } | |
| 1912 | ||
| 1913 | /// PrimaryExpr | |
| 1914 | /// <- AsmExpr | |
| 1915 | /// / IfExpr | |
| 1916 | /// / KEYWORD_break BreakLabel? Expr? | |
| 1917 | /// / KEYWORD_comptime Expr | |
| 1918 | /// / KEYWORD_nosuspend Expr | |
| 1919 | /// / KEYWORD_continue BreakLabel? | |
| 1920 | /// / KEYWORD_resume Expr | |
| 1921 | /// / KEYWORD_return Expr? | |
| 1922 | /// / BlockLabel? LoopExpr | |
| 1923 | /// / Block | |
| 1924 | /// / CurlySuffixExpr | |
| 1925 | fn parsePrimaryExpr(p: *Parser) !Node.Index { | |
| 1926 | switch (p.token_tags[p.tok_i]) { | |
| 1927 | .keyword_asm => return p.expectAsmExpr(), | |
| 1928 | .keyword_if => return p.parseIfExpr(), | |
| 1929 | .keyword_break => { | |
| 1930 | p.tok_i += 1; | |
| 1931 | return p.addNode(.{ | |
| 1932 | .tag = .@"break", | |
| 1933 | .main_token = p.tok_i - 1, | |
| 1934 | .data = .{ | |
| 1935 | .lhs = try p.parseBreakLabel(), | |
| 1936 | .rhs = try p.parseExpr(), | |
| 1937 | }, | |
| 1938 | }); | |
| 1939 | }, | |
| 1940 | .keyword_continue => { | |
| 1941 | p.tok_i += 1; | |
| 1942 | return p.addNode(.{ | |
| 1943 | .tag = .@"continue", | |
| 1944 | .main_token = p.tok_i - 1, | |
| 1945 | .data = .{ | |
| 1946 | .lhs = try p.parseBreakLabel(), | |
| 1947 | .rhs = undefined, | |
| 1948 | }, | |
| 1949 | }); | |
| 1950 | }, | |
| 1951 | .keyword_comptime => { | |
| 1952 | p.tok_i += 1; | |
| 1953 | return p.addNode(.{ | |
| 1954 | .tag = .@"comptime", | |
| 1955 | .main_token = p.tok_i - 1, | |
| 1956 | .data = .{ | |
| 1957 | .lhs = try p.expectExpr(), | |
| 1958 | .rhs = undefined, | |
| 1959 | }, | |
| 1960 | }); | |
| 1961 | }, | |
| 1962 | .keyword_nosuspend => { | |
| 1963 | p.tok_i += 1; | |
| 1964 | return p.addNode(.{ | |
| 1965 | .tag = .@"nosuspend", | |
| 1966 | .main_token = p.tok_i - 1, | |
| 1967 | .data = .{ | |
| 1968 | .lhs = try p.expectExpr(), | |
| 1969 | .rhs = undefined, | |
| 1970 | }, | |
| 1971 | }); | |
| 1972 | }, | |
| 1973 | .keyword_resume => { | |
| 1974 | p.tok_i += 1; | |
| 1975 | return p.addNode(.{ | |
| 1976 | .tag = .@"resume", | |
| 1977 | .main_token = p.tok_i - 1, | |
| 1978 | .data = .{ | |
| 1979 | .lhs = try p.expectExpr(), | |
| 1980 | .rhs = undefined, | |
| 1981 | }, | |
| 1982 | }); | |
| 1983 | }, | |
| 1984 | .keyword_return => { | |
| 1985 | p.tok_i += 1; | |
| 1986 | return p.addNode(.{ | |
| 1987 | .tag = .@"return", | |
| 1988 | .main_token = p.tok_i - 1, | |
| 1989 | .data = .{ | |
| 1990 | .lhs = try p.parseExpr(), | |
| 1991 | .rhs = undefined, | |
| 1992 | }, | |
| 1993 | }); | |
| 1994 | }, | |
| 1995 | .identifier => { | |
| 1996 | if (p.token_tags[p.tok_i + 1] == .colon) { | |
| 1997 | switch (p.token_tags[p.tok_i + 2]) { | |
| 1998 | .keyword_inline => { | |
| 1999 | p.tok_i += 3; | |
| 2000 | switch (p.token_tags[p.tok_i]) { | |
| 2001 | .keyword_for => return p.parseForExpr(), | |
| 2002 | .keyword_while => return p.parseWhileExpr(), | |
| 2003 | else => return p.fail(.expected_inlinable), | |
| 2004 | } | |
| 2005 | }, | |
| 2006 | .keyword_for => { | |
| 2007 | p.tok_i += 2; | |
| 2008 | return p.parseForExpr(); | |
| 2009 | }, | |
| 2010 | .keyword_while => { | |
| 2011 | p.tok_i += 2; | |
| 2012 | return p.parseWhileExpr(); | |
| 2013 | }, | |
| 2014 | .l_brace => { | |
| 2015 | p.tok_i += 2; | |
| 2016 | return p.parseBlock(); | |
| 2017 | }, | |
| 2018 | else => return p.parseCurlySuffixExpr(), | |
| 2019 | } | |
| 2020 | } else { | |
| 2021 | return p.parseCurlySuffixExpr(); | |
| 2022 | } | |
| 2023 | }, | |
| 2024 | .keyword_inline => { | |
| 2025 | p.tok_i += 1; | |
| 2026 | switch (p.token_tags[p.tok_i]) { | |
| 2027 | .keyword_for => return p.parseForExpr(), | |
| 2028 | .keyword_while => return p.parseWhileExpr(), | |
| 2029 | else => return p.fail(.expected_inlinable), | |
| 2030 | } | |
| 2031 | }, | |
| 2032 | .keyword_for => return p.parseForExpr(), | |
| 2033 | .keyword_while => return p.parseWhileExpr(), | |
| 2034 | .l_brace => return p.parseBlock(), | |
| 2035 | else => return p.parseCurlySuffixExpr(), | |
| 2036 | } | |
| 2037 | } | |
| 2038 | ||
| 2039 | /// IfExpr <- IfPrefix Expr (KEYWORD_else Payload? Expr)? | |
| 2040 | fn parseIfExpr(p: *Parser) !Node.Index { | |
| 2041 | return p.parseIf(expectExpr); | |
| 2042 | } | |
| 2043 | ||
| 2044 | /// Block <- LBRACE Statement* RBRACE | |
| 2045 | fn parseBlock(p: *Parser) !Node.Index { | |
| 2046 | const lbrace = p.eatToken(.l_brace) orelse return null_node; | |
| 2047 | const scratch_top = p.scratch.items.len; | |
| 2048 | defer p.scratch.shrinkRetainingCapacity(scratch_top); | |
| 2049 | while (true) { | |
| 2050 | if (p.token_tags[p.tok_i] == .r_brace) break; | |
| 2051 | const statement = try p.expectStatementRecoverable(); | |
| 2052 | if (statement == 0) break; | |
| 2053 | try p.scratch.append(p.gpa, statement); | |
| 2054 | } | |
| 2055 | _ = try p.expectToken(.r_brace); | |
| 2056 | const semicolon = (p.token_tags[p.tok_i - 2] == .semicolon); | |
| 2057 | const statements = p.scratch.items[scratch_top..]; | |
| 2058 | switch (statements.len) { | |
| 2059 | 0 => return p.addNode(.{ | |
| 2060 | .tag = .block_two, | |
| 2061 | .main_token = lbrace, | |
| 2062 | .data = .{ | |
| 2063 | .lhs = 0, | |
| 2064 | .rhs = 0, | |
| 2065 | }, | |
| 2066 | }), | |
| 2067 | 1 => return p.addNode(.{ | |
| 2068 | .tag = if (semicolon) .block_two_semicolon else .block_two, | |
| 2069 | .main_token = lbrace, | |
| 2070 | .data = .{ | |
| 2071 | .lhs = statements[0], | |
| 2072 | .rhs = 0, | |
| 2073 | }, | |
| 2074 | }), | |
| 2075 | 2 => return p.addNode(.{ | |
| 2076 | .tag = if (semicolon) .block_two_semicolon else .block_two, | |
| 2077 | .main_token = lbrace, | |
| 2078 | .data = .{ | |
| 2079 | .lhs = statements[0], | |
| 2080 | .rhs = statements[1], | |
| 2081 | }, | |
| 2082 | }), | |
| 2083 | else => { | |
| 2084 | const span = try p.listToSpan(statements); | |
| 2085 | return p.addNode(.{ | |
| 2086 | .tag = if (semicolon) .block_semicolon else .block, | |
| 2087 | .main_token = lbrace, | |
| 2088 | .data = .{ | |
| 2089 | .lhs = span.start, | |
| 2090 | .rhs = span.end, | |
| 2091 | }, | |
| 2092 | }); | |
| 2093 | }, | |
| 2094 | } | |
| 2095 | } | |
| 2096 | ||
| 2097 | /// ForPrefix <- KEYWORD_for LPAREN Expr RPAREN PtrIndexPayload | |
| 2098 | /// | |
| 2099 | /// ForExpr <- ForPrefix Expr (KEYWORD_else Expr)? | |
| 2100 | fn parseForExpr(p: *Parser) !Node.Index { | |
| 2101 | const for_token = p.eatToken(.keyword_for) orelse return null_node; | |
| 2102 | _ = try p.expectToken(.l_paren); | |
| 2103 | const array_expr = try p.expectExpr(); | |
| 2104 | _ = try p.expectToken(.r_paren); | |
| 2105 | const found_payload = try p.parsePtrIndexPayload(); | |
| 2106 | if (found_payload == 0) try p.warn(.expected_loop_payload); | |
| 2107 | ||
| 2108 | const then_expr = try p.expectExpr(); | |
| 2109 | _ = p.eatToken(.keyword_else) orelse { | |
| 2110 | return p.addNode(.{ | |
| 2111 | .tag = .for_simple, | |
| 2112 | .main_token = for_token, | |
| 2113 | .data = .{ | |
| 2114 | .lhs = array_expr, | |
| 2115 | .rhs = then_expr, | |
| 2116 | }, | |
| 2117 | }); | |
| 2118 | }; | |
| 2119 | const else_expr = try p.expectExpr(); | |
| 2120 | return p.addNode(.{ | |
| 2121 | .tag = .@"for", | |
| 2122 | .main_token = for_token, | |
| 2123 | .data = .{ | |
| 2124 | .lhs = array_expr, | |
| 2125 | .rhs = try p.addExtra(Node.If{ | |
| 2126 | .then_expr = then_expr, | |
| 2127 | .else_expr = else_expr, | |
| 2128 | }), | |
| 2129 | }, | |
| 2130 | }); | |
| 2131 | } | |
| 2132 | ||
| 2133 | /// WhilePrefix <- KEYWORD_while LPAREN Expr RPAREN PtrPayload? WhileContinueExpr? | |
| 2134 | /// | |
| 2135 | /// WhileExpr <- WhilePrefix Expr (KEYWORD_else Payload? Expr)? | |
| 2136 | fn parseWhileExpr(p: *Parser) !Node.Index { | |
| 2137 | const while_token = p.eatToken(.keyword_while) orelse return null_node; | |
| 2138 | _ = try p.expectToken(.l_paren); | |
| 2139 | const condition = try p.expectExpr(); | |
| 2140 | _ = try p.expectToken(.r_paren); | |
| 2141 | _ = try p.parsePtrPayload(); | |
| 2142 | const cont_expr = try p.parseWhileContinueExpr(); | |
| 2143 | ||
| 2144 | const then_expr = try p.expectExpr(); | |
| 2145 | _ = p.eatToken(.keyword_else) orelse { | |
| 2146 | if (cont_expr == 0) { | |
| 2147 | return p.addNode(.{ | |
| 2148 | .tag = .while_simple, | |
| 2149 | .main_token = while_token, | |
| 2150 | .data = .{ | |
| 2151 | .lhs = condition, | |
| 2152 | .rhs = then_expr, | |
| 2153 | }, | |
| 2154 | }); | |
| 2155 | } else { | |
| 2156 | return p.addNode(.{ | |
| 2157 | .tag = .while_cont, | |
| 2158 | .main_token = while_token, | |
| 2159 | .data = .{ | |
| 2160 | .lhs = condition, | |
| 2161 | .rhs = try p.addExtra(Node.WhileCont{ | |
| 2162 | .cont_expr = cont_expr, | |
| 2163 | .then_expr = then_expr, | |
| 2164 | }), | |
| 2165 | }, | |
| 2166 | }); | |
| 2167 | } | |
| 2168 | }; | |
| 2169 | _ = try p.parsePayload(); | |
| 2170 | const else_expr = try p.expectExpr(); | |
| 2171 | return p.addNode(.{ | |
| 2172 | .tag = .@"while", | |
| 2173 | .main_token = while_token, | |
| 2174 | .data = .{ | |
| 2175 | .lhs = condition, | |
| 2176 | .rhs = try p.addExtra(Node.While{ | |
| 2177 | .cont_expr = cont_expr, | |
| 2178 | .then_expr = then_expr, | |
| 2179 | .else_expr = else_expr, | |
| 2180 | }), | |
| 2181 | }, | |
| 2182 | }); | |
| 2183 | } | |
| 2184 | ||
| 2185 | /// CurlySuffixExpr <- TypeExpr InitList? | |
| 2186 | /// | |
| 2187 | /// InitList | |
| 2188 | /// <- LBRACE FieldInit (COMMA FieldInit)* COMMA? RBRACE | |
| 2189 | /// / LBRACE Expr (COMMA Expr)* COMMA? RBRACE | |
| 2190 | /// / LBRACE RBRACE | |
| 2191 | fn parseCurlySuffixExpr(p: *Parser) !Node.Index { | |
| 2192 | const lhs = try p.parseTypeExpr(); | |
| 2193 | if (lhs == 0) return null_node; | |
| 2194 | const lbrace = p.eatToken(.l_brace) orelse return lhs; | |
| 2195 | ||
| 2196 | // If there are 0 or 1 items, we can use ArrayInitOne/StructInitOne; | |
| 2197 | // otherwise we use the full ArrayInit/StructInit. | |
| 2198 | ||
| 2199 | const scratch_top = p.scratch.items.len; | |
| 2200 | defer p.scratch.shrinkRetainingCapacity(scratch_top); | |
| 2201 | const field_init = try p.parseFieldInit(); | |
| 2202 | if (field_init != 0) { | |
| 2203 | try p.scratch.append(p.gpa, field_init); | |
| 2204 | while (true) { | |
| 2205 | switch (p.token_tags[p.tok_i]) { | |
| 2206 | .comma => p.tok_i += 1, | |
| 2207 | .r_brace => { | |
| 2208 | p.tok_i += 1; | |
| 2209 | break; | |
| 2210 | }, | |
| 2211 | .colon, .r_paren, .r_bracket => return p.failExpected(.r_brace), | |
| 2212 | // Likely just a missing comma; give error but continue parsing. | |
| 2213 | else => try p.warn(.expected_comma_after_initializer), | |
| 2214 | } | |
| 2215 | if (p.eatToken(.r_brace)) |_| break; | |
| 2216 | const next = try p.expectFieldInit(); | |
| 2217 | try p.scratch.append(p.gpa, next); | |
| 2218 | } | |
| 2219 | const comma = (p.token_tags[p.tok_i - 2] == .comma); | |
| 2220 | const inits = p.scratch.items[scratch_top..]; | |
| 2221 | switch (inits.len) { | |
| 2222 | 0 => unreachable, | |
| 2223 | 1 => return p.addNode(.{ | |
| 2224 | .tag = if (comma) .struct_init_one_comma else .struct_init_one, | |
| 2225 | .main_token = lbrace, | |
| 2226 | .data = .{ | |
| 2227 | .lhs = lhs, | |
| 2228 | .rhs = inits[0], | |
| 2229 | }, | |
| 2230 | }), | |
| 2231 | else => return p.addNode(.{ | |
| 2232 | .tag = if (comma) .struct_init_comma else .struct_init, | |
| 2233 | .main_token = lbrace, | |
| 2234 | .data = .{ | |
| 2235 | .lhs = lhs, | |
| 2236 | .rhs = try p.addExtra(try p.listToSpan(inits)), | |
| 2237 | }, | |
| 2238 | }), | |
| 2239 | } | |
| 2240 | } | |
| 2241 | ||
| 2242 | while (true) { | |
| 2243 | if (p.eatToken(.r_brace)) |_| break; | |
| 2244 | const elem_init = try p.expectExpr(); | |
| 2245 | try p.scratch.append(p.gpa, elem_init); | |
| 2246 | switch (p.token_tags[p.tok_i]) { | |
| 2247 | .comma => p.tok_i += 1, | |
| 2248 | .r_brace => { | |
| 2249 | p.tok_i += 1; | |
| 2250 | break; | |
| 2251 | }, | |
| 2252 | .colon, .r_paren, .r_bracket => return p.failExpected(.r_brace), | |
| 2253 | // Likely just a missing comma; give error but continue parsing. | |
| 2254 | else => try p.warn(.expected_comma_after_initializer), | |
| 2255 | } | |
| 2256 | } | |
| 2257 | const comma = (p.token_tags[p.tok_i - 2] == .comma); | |
| 2258 | const inits = p.scratch.items[scratch_top..]; | |
| 2259 | switch (inits.len) { | |
| 2260 | 0 => return p.addNode(.{ | |
| 2261 | .tag = .struct_init_one, | |
| 2262 | .main_token = lbrace, | |
| 2263 | .data = .{ | |
| 2264 | .lhs = lhs, | |
| 2265 | .rhs = 0, | |
| 2266 | }, | |
| 2267 | }), | |
| 2268 | 1 => return p.addNode(.{ | |
| 2269 | .tag = if (comma) .array_init_one_comma else .array_init_one, | |
| 2270 | .main_token = lbrace, | |
| 2271 | .data = .{ | |
| 2272 | .lhs = lhs, | |
| 2273 | .rhs = inits[0], | |
| 2274 | }, | |
| 2275 | }), | |
| 2276 | else => return p.addNode(.{ | |
| 2277 | .tag = if (comma) .array_init_comma else .array_init, | |
| 2278 | .main_token = lbrace, | |
| 2279 | .data = .{ | |
| 2280 | .lhs = lhs, | |
| 2281 | .rhs = try p.addExtra(try p.listToSpan(inits)), | |
| 2282 | }, | |
| 2283 | }), | |
| 2284 | } | |
| 2285 | } | |
| 2286 | ||
| 2287 | /// ErrorUnionExpr <- SuffixExpr (EXCLAMATIONMARK TypeExpr)? | |
| 2288 | fn parseErrorUnionExpr(p: *Parser) !Node.Index { | |
| 2289 | const suffix_expr = try p.parseSuffixExpr(); | |
| 2290 | if (suffix_expr == 0) return null_node; | |
| 2291 | const bang = p.eatToken(.bang) orelse return suffix_expr; | |
| 2292 | return p.addNode(.{ | |
| 2293 | .tag = .error_union, | |
| 2294 | .main_token = bang, | |
| 2295 | .data = .{ | |
| 2296 | .lhs = suffix_expr, | |
| 2297 | .rhs = try p.expectTypeExpr(), | |
| 2298 | }, | |
| 2299 | }); | |
| 2300 | } | |
| 2301 | ||
| 2302 | /// SuffixExpr | |
| 2303 | /// <- KEYWORD_async PrimaryTypeExpr SuffixOp* FnCallArguments | |
| 2304 | /// / PrimaryTypeExpr (SuffixOp / FnCallArguments)* | |
| 2305 | /// | |
| 2306 | /// FnCallArguments <- LPAREN ExprList RPAREN | |
| 2307 | /// | |
| 2308 | /// ExprList <- (Expr COMMA)* Expr? | |
| 2309 | fn parseSuffixExpr(p: *Parser) !Node.Index { | |
| 2310 | if (p.eatToken(.keyword_async)) |_| { | |
| 2311 | var res = try p.expectPrimaryTypeExpr(); | |
| 2312 | while (true) { | |
| 2313 | const node = try p.parseSuffixOp(res); | |
| 2314 | if (node == 0) break; | |
| 2315 | res = node; | |
| 2316 | } | |
| 2317 | const lparen = p.eatToken(.l_paren) orelse { | |
| 2318 | try p.warn(.expected_param_list); | |
| 2319 | return res; | |
| 2320 | }; | |
| 2321 | const scratch_top = p.scratch.items.len; | |
| 2322 | defer p.scratch.shrinkRetainingCapacity(scratch_top); | |
| 2323 | while (true) { | |
| 2324 | if (p.eatToken(.r_paren)) |_| break; | |
| 2325 | const param = try p.expectExpr(); | |
| 2326 | try p.scratch.append(p.gpa, param); | |
| 2327 | switch (p.token_tags[p.tok_i]) { | |
| 2328 | .comma => p.tok_i += 1, | |
| 2329 | .r_paren => { | |
| 2330 | p.tok_i += 1; | |
| 2331 | break; | |
| 2332 | }, | |
| 2333 | .colon, .r_brace, .r_bracket => return p.failExpected(.r_paren), | |
| 2334 | // Likely just a missing comma; give error but continue parsing. | |
| 2335 | else => try p.warn(.expected_comma_after_arg), | |
| 2336 | } | |
| 2337 | } | |
| 2338 | const comma = (p.token_tags[p.tok_i - 2] == .comma); | |
| 2339 | const params = p.scratch.items[scratch_top..]; | |
| 2340 | switch (params.len) { | |
| 2341 | 0 => return p.addNode(.{ | |
| 2342 | .tag = if (comma) .async_call_one_comma else .async_call_one, | |
| 2343 | .main_token = lparen, | |
| 2344 | .data = .{ | |
| 2345 | .lhs = res, | |
| 2346 | .rhs = 0, | |
| 2347 | }, | |
| 2348 | }), | |
| 2349 | 1 => return p.addNode(.{ | |
| 2350 | .tag = if (comma) .async_call_one_comma else .async_call_one, | |
| 2351 | .main_token = lparen, | |
| 2352 | .data = .{ | |
| 2353 | .lhs = res, | |
| 2354 | .rhs = params[0], | |
| 2355 | }, | |
| 2356 | }), | |
| 2357 | else => return p.addNode(.{ | |
| 2358 | .tag = if (comma) .async_call_comma else .async_call, | |
| 2359 | .main_token = lparen, | |
| 2360 | .data = .{ | |
| 2361 | .lhs = res, | |
| 2362 | .rhs = try p.addExtra(try p.listToSpan(params)), | |
| 2363 | }, | |
| 2364 | }), | |
| 2365 | } | |
| 2366 | } | |
| 2367 | ||
| 2368 | var res = try p.parsePrimaryTypeExpr(); | |
| 2369 | if (res == 0) return res; | |
| 2370 | while (true) { | |
| 2371 | const suffix_op = try p.parseSuffixOp(res); | |
| 2372 | if (suffix_op != 0) { | |
| 2373 | res = suffix_op; | |
| 2374 | continue; | |
| 2375 | } | |
| 2376 | const lparen = p.eatToken(.l_paren) orelse return res; | |
| 2377 | const scratch_top = p.scratch.items.len; | |
| 2378 | defer p.scratch.shrinkRetainingCapacity(scratch_top); | |
| 2379 | while (true) { | |
| 2380 | if (p.eatToken(.r_paren)) |_| break; | |
| 2381 | const param = try p.expectExpr(); | |
| 2382 | try p.scratch.append(p.gpa, param); | |
| 2383 | switch (p.token_tags[p.tok_i]) { | |
| 2384 | .comma => p.tok_i += 1, | |
| 2385 | .r_paren => { | |
| 2386 | p.tok_i += 1; | |
| 2387 | break; | |
| 2388 | }, | |
| 2389 | .colon, .r_brace, .r_bracket => return p.failExpected(.r_paren), | |
| 2390 | // Likely just a missing comma; give error but continue parsing. | |
| 2391 | else => try p.warn(.expected_comma_after_arg), | |
| 2392 | } | |
| 2393 | } | |
| 2394 | const comma = (p.token_tags[p.tok_i - 2] == .comma); | |
| 2395 | const params = p.scratch.items[scratch_top..]; | |
| 2396 | res = switch (params.len) { | |
| 2397 | 0 => try p.addNode(.{ | |
| 2398 | .tag = if (comma) .call_one_comma else .call_one, | |
| 2399 | .main_token = lparen, | |
| 2400 | .data = .{ | |
| 2401 | .lhs = res, | |
| 2402 | .rhs = 0, | |
| 2403 | }, | |
| 2404 | }), | |
| 2405 | 1 => try p.addNode(.{ | |
| 2406 | .tag = if (comma) .call_one_comma else .call_one, | |
| 2407 | .main_token = lparen, | |
| 2408 | .data = .{ | |
| 2409 | .lhs = res, | |
| 2410 | .rhs = params[0], | |
| 2411 | }, | |
| 2412 | }), | |
| 2413 | else => try p.addNode(.{ | |
| 2414 | .tag = if (comma) .call_comma else .call, | |
| 2415 | .main_token = lparen, | |
| 2416 | .data = .{ | |
| 2417 | .lhs = res, | |
| 2418 | .rhs = try p.addExtra(try p.listToSpan(params)), | |
| 2419 | }, | |
| 2420 | }), | |
| 2421 | }; | |
| 2422 | } | |
| 2423 | } | |
| 2424 | ||
| 2425 | /// PrimaryTypeExpr | |
| 2426 | /// <- BUILTINIDENTIFIER FnCallArguments | |
| 2427 | /// / CHAR_LITERAL | |
| 2428 | /// / ContainerDecl | |
| 2429 | /// / DOT IDENTIFIER | |
| 2430 | /// / DOT InitList | |
| 2431 | /// / ErrorSetDecl | |
| 2432 | /// / FLOAT | |
| 2433 | /// / FnProto | |
| 2434 | /// / GroupedExpr | |
| 2435 | /// / LabeledTypeExpr | |
| 2436 | /// / IDENTIFIER | |
| 2437 | /// / IfTypeExpr | |
| 2438 | /// / INTEGER | |
| 2439 | /// / KEYWORD_comptime TypeExpr | |
| 2440 | /// / KEYWORD_error DOT IDENTIFIER | |
| 2441 | /// / KEYWORD_anyframe | |
| 2442 | /// / KEYWORD_unreachable | |
| 2443 | /// / STRINGLITERAL | |
| 2444 | /// / SwitchExpr | |
| 2445 | /// | |
| 2446 | /// ContainerDecl <- (KEYWORD_extern / KEYWORD_packed)? ContainerDeclAuto | |
| 2447 | /// | |
| 2448 | /// ContainerDeclAuto <- ContainerDeclType LBRACE container_doc_comment? ContainerMembers RBRACE | |
| 2449 | /// | |
| 2450 | /// InitList | |
| 2451 | /// <- LBRACE FieldInit (COMMA FieldInit)* COMMA? RBRACE | |
| 2452 | /// / LBRACE Expr (COMMA Expr)* COMMA? RBRACE | |
| 2453 | /// / LBRACE RBRACE | |
| 2454 | /// | |
| 2455 | /// ErrorSetDecl <- KEYWORD_error LBRACE IdentifierList RBRACE | |
| 2456 | /// | |
| 2457 | /// GroupedExpr <- LPAREN Expr RPAREN | |
| 2458 | /// | |
| 2459 | /// IfTypeExpr <- IfPrefix TypeExpr (KEYWORD_else Payload? TypeExpr)? | |
| 2460 | /// | |
| 2461 | /// LabeledTypeExpr | |
| 2462 | /// <- BlockLabel Block | |
| 2463 | /// / BlockLabel? LoopTypeExpr | |
| 2464 | /// | |
| 2465 | /// LoopTypeExpr <- KEYWORD_inline? (ForTypeExpr / WhileTypeExpr) | |
| 2466 | fn parsePrimaryTypeExpr(p: *Parser) !Node.Index { | |
| 2467 | switch (p.token_tags[p.tok_i]) { | |
| 2468 | .char_literal => return p.addNode(.{ | |
| 2469 | .tag = .char_literal, | |
| 2470 | .main_token = p.nextToken(), | |
| 2471 | .data = .{ | |
| 2472 | .lhs = undefined, | |
| 2473 | .rhs = undefined, | |
| 2474 | }, | |
| 2475 | }), | |
| 2476 | .number_literal => return p.addNode(.{ | |
| 2477 | .tag = .number_literal, | |
| 2478 | .main_token = p.nextToken(), | |
| 2479 | .data = .{ | |
| 2480 | .lhs = undefined, | |
| 2481 | .rhs = undefined, | |
| 2482 | }, | |
| 2483 | }), | |
| 2484 | .keyword_unreachable => return p.addNode(.{ | |
| 2485 | .tag = .unreachable_literal, | |
| 2486 | .main_token = p.nextToken(), | |
| 2487 | .data = .{ | |
| 2488 | .lhs = undefined, | |
| 2489 | .rhs = undefined, | |
| 2490 | }, | |
| 2491 | }), | |
| 2492 | .keyword_anyframe => return p.addNode(.{ | |
| 2493 | .tag = .anyframe_literal, | |
| 2494 | .main_token = p.nextToken(), | |
| 2495 | .data = .{ | |
| 2496 | .lhs = undefined, | |
| 2497 | .rhs = undefined, | |
| 2498 | }, | |
| 2499 | }), | |
| 2500 | .string_literal => { | |
| 2501 | const main_token = p.nextToken(); | |
| 2502 | return p.addNode(.{ | |
| 2503 | .tag = .string_literal, | |
| 2504 | .main_token = main_token, | |
| 2505 | .data = .{ | |
| 2506 | .lhs = undefined, | |
| 2507 | .rhs = undefined, | |
| 2508 | }, | |
| 2509 | }); | |
| 2510 | }, | |
| 2511 | ||
| 2512 | .builtin => return p.parseBuiltinCall(), | |
| 2513 | .keyword_fn => return p.parseFnProto(), | |
| 2514 | .keyword_if => return p.parseIf(expectTypeExpr), | |
| 2515 | .keyword_switch => return p.expectSwitchExpr(), | |
| 2516 | ||
| 2517 | .keyword_extern, | |
| 2518 | .keyword_packed, | |
| 2519 | => { | |
| 2520 | p.tok_i += 1; | |
| 2521 | return p.parseContainerDeclAuto(); | |
| 2522 | }, | |
| 2523 | ||
| 2524 | .keyword_struct, | |
| 2525 | .keyword_opaque, | |
| 2526 | .keyword_enum, | |
| 2527 | .keyword_union, | |
| 2528 | => return p.parseContainerDeclAuto(), | |
| 2529 | ||
| 2530 | .keyword_comptime => return p.addNode(.{ | |
| 2531 | .tag = .@"comptime", | |
| 2532 | .main_token = p.nextToken(), | |
| 2533 | .data = .{ | |
| 2534 | .lhs = try p.expectTypeExpr(), | |
| 2535 | .rhs = undefined, | |
| 2536 | }, | |
| 2537 | }), | |
| 2538 | .multiline_string_literal_line => { | |
| 2539 | const first_line = p.nextToken(); | |
| 2540 | while (p.token_tags[p.tok_i] == .multiline_string_literal_line) { | |
| 2541 | p.tok_i += 1; | |
| 2542 | } | |
| 2543 | return p.addNode(.{ | |
| 2544 | .tag = .multiline_string_literal, | |
| 2545 | .main_token = first_line, | |
| 2546 | .data = .{ | |
| 2547 | .lhs = first_line, | |
| 2548 | .rhs = p.tok_i - 1, | |
| 2549 | }, | |
| 2550 | }); | |
| 2551 | }, | |
| 2552 | .identifier => switch (p.token_tags[p.tok_i + 1]) { | |
| 2553 | .colon => switch (p.token_tags[p.tok_i + 2]) { | |
| 2554 | .keyword_inline => { | |
| 2555 | p.tok_i += 3; | |
| 2556 | switch (p.token_tags[p.tok_i]) { | |
| 2557 | .keyword_for => return p.parseForTypeExpr(), | |
| 2558 | .keyword_while => return p.parseWhileTypeExpr(), | |
| 2559 | else => return p.fail(.expected_inlinable), | |
| 2560 | } | |
| 2561 | }, | |
| 2562 | .keyword_for => { | |
| 2563 | p.tok_i += 2; | |
| 2564 | return p.parseForTypeExpr(); | |
| 2565 | }, | |
| 2566 | .keyword_while => { | |
| 2567 | p.tok_i += 2; | |
| 2568 | return p.parseWhileTypeExpr(); | |
| 2569 | }, | |
| 2570 | .l_brace => { | |
| 2571 | p.tok_i += 2; | |
| 2572 | return p.parseBlock(); | |
| 2573 | }, | |
| 2574 | else => return p.addNode(.{ | |
| 2575 | .tag = .identifier, | |
| 2576 | .main_token = p.nextToken(), | |
| 2577 | .data = .{ | |
| 2578 | .lhs = undefined, | |
| 2579 | .rhs = undefined, | |
| 2580 | }, | |
| 2581 | }), | |
| 2582 | }, | |
| 2583 | else => return p.addNode(.{ | |
| 2584 | .tag = .identifier, | |
| 2585 | .main_token = p.nextToken(), | |
| 2586 | .data = .{ | |
| 2587 | .lhs = undefined, | |
| 2588 | .rhs = undefined, | |
| 2589 | }, | |
| 2590 | }), | |
| 2591 | }, | |
| 2592 | .keyword_inline => { | |
| 2593 | p.tok_i += 1; | |
| 2594 | switch (p.token_tags[p.tok_i]) { | |
| 2595 | .keyword_for => return p.parseForTypeExpr(), | |
| 2596 | .keyword_while => return p.parseWhileTypeExpr(), | |
| 2597 | else => return p.fail(.expected_inlinable), | |
| 2598 | } | |
| 2599 | }, | |
| 2600 | .keyword_for => return p.parseForTypeExpr(), | |
| 2601 | .keyword_while => return p.parseWhileTypeExpr(), | |
| 2602 | .period => switch (p.token_tags[p.tok_i + 1]) { | |
| 2603 | .identifier => return p.addNode(.{ | |
| 2604 | .tag = .enum_literal, | |
| 2605 | .data = .{ | |
| 2606 | .lhs = p.nextToken(), // dot | |
| 2607 | .rhs = undefined, | |
| 2608 | }, | |
| 2609 | .main_token = p.nextToken(), // identifier | |
| 2610 | }), | |
| 2611 | .l_brace => { | |
| 2612 | const lbrace = p.tok_i + 1; | |
| 2613 | p.tok_i = lbrace + 1; | |
| 2614 | ||
| 2615 | // If there are 0, 1, or 2 items, we can use ArrayInitDotTwo/StructInitDotTwo; | |
| 2616 | // otherwise we use the full ArrayInitDot/StructInitDot. | |
| 2617 | ||
| 2618 | const scratch_top = p.scratch.items.len; | |
| 2619 | defer p.scratch.shrinkRetainingCapacity(scratch_top); | |
| 2620 | const field_init = try p.parseFieldInit(); | |
| 2621 | if (field_init != 0) { | |
| 2622 | try p.scratch.append(p.gpa, field_init); | |
| 2623 | while (true) { | |
| 2624 | switch (p.token_tags[p.tok_i]) { | |
| 2625 | .comma => p.tok_i += 1, | |
| 2626 | .r_brace => { | |
| 2627 | p.tok_i += 1; | |
| 2628 | break; | |
| 2629 | }, | |
| 2630 | .colon, .r_paren, .r_bracket => return p.failExpected(.r_brace), | |
| 2631 | // Likely just a missing comma; give error but continue parsing. | |
| 2632 | else => try p.warn(.expected_comma_after_initializer), | |
| 2633 | } | |
| 2634 | if (p.eatToken(.r_brace)) |_| break; | |
| 2635 | const next = try p.expectFieldInit(); | |
| 2636 | try p.scratch.append(p.gpa, next); | |
| 2637 | } | |
| 2638 | const comma = (p.token_tags[p.tok_i - 2] == .comma); | |
| 2639 | const inits = p.scratch.items[scratch_top..]; | |
| 2640 | switch (inits.len) { | |
| 2641 | 0 => unreachable, | |
| 2642 | 1 => return p.addNode(.{ | |
| 2643 | .tag = if (comma) .struct_init_dot_two_comma else .struct_init_dot_two, | |
| 2644 | .main_token = lbrace, | |
| 2645 | .data = .{ | |
| 2646 | .lhs = inits[0], | |
| 2647 | .rhs = 0, | |
| 2648 | }, | |
| 2649 | }), | |
| 2650 | 2 => return p.addNode(.{ | |
| 2651 | .tag = if (comma) .struct_init_dot_two_comma else .struct_init_dot_two, | |
| 2652 | .main_token = lbrace, | |
| 2653 | .data = .{ | |
| 2654 | .lhs = inits[0], | |
| 2655 | .rhs = inits[1], | |
| 2656 | }, | |
| 2657 | }), | |
| 2658 | else => { | |
| 2659 | const span = try p.listToSpan(inits); | |
| 2660 | return p.addNode(.{ | |
| 2661 | .tag = if (comma) .struct_init_dot_comma else .struct_init_dot, | |
| 2662 | .main_token = lbrace, | |
| 2663 | .data = .{ | |
| 2664 | .lhs = span.start, | |
| 2665 | .rhs = span.end, | |
| 2666 | }, | |
| 2667 | }); | |
| 2668 | }, | |
| 2669 | } | |
| 2670 | } | |
| 2671 | ||
| 2672 | while (true) { | |
| 2673 | if (p.eatToken(.r_brace)) |_| break; | |
| 2674 | const elem_init = try p.expectExpr(); | |
| 2675 | try p.scratch.append(p.gpa, elem_init); | |
| 2676 | switch (p.token_tags[p.tok_i]) { | |
| 2677 | .comma => p.tok_i += 1, | |
| 2678 | .r_brace => { | |
| 2679 | p.tok_i += 1; | |
| 2680 | break; | |
| 2681 | }, | |
| 2682 | .colon, .r_paren, .r_bracket => return p.failExpected(.r_brace), | |
| 2683 | // Likely just a missing comma; give error but continue parsing. | |
| 2684 | else => try p.warn(.expected_comma_after_initializer), | |
| 2685 | } | |
| 2686 | } | |
| 2687 | const comma = (p.token_tags[p.tok_i - 2] == .comma); | |
| 2688 | const inits = p.scratch.items[scratch_top..]; | |
| 2689 | switch (inits.len) { | |
| 2690 | 0 => return p.addNode(.{ | |
| 2691 | .tag = .struct_init_dot_two, | |
| 2692 | .main_token = lbrace, | |
| 2693 | .data = .{ | |
| 2694 | .lhs = 0, | |
| 2695 | .rhs = 0, | |
| 2696 | }, | |
| 2697 | }), | |
| 2698 | 1 => return p.addNode(.{ | |
| 2699 | .tag = if (comma) .array_init_dot_two_comma else .array_init_dot_two, | |
| 2700 | .main_token = lbrace, | |
| 2701 | .data = .{ | |
| 2702 | .lhs = inits[0], | |
| 2703 | .rhs = 0, | |
| 2704 | }, | |
| 2705 | }), | |
| 2706 | 2 => return p.addNode(.{ | |
| 2707 | .tag = if (comma) .array_init_dot_two_comma else .array_init_dot_two, | |
| 2708 | .main_token = lbrace, | |
| 2709 | .data = .{ | |
| 2710 | .lhs = inits[0], | |
| 2711 | .rhs = inits[1], | |
| 2712 | }, | |
| 2713 | }), | |
| 2714 | else => { | |
| 2715 | const span = try p.listToSpan(inits); | |
| 2716 | return p.addNode(.{ | |
| 2717 | .tag = if (comma) .array_init_dot_comma else .array_init_dot, | |
| 2718 | .main_token = lbrace, | |
| 2719 | .data = .{ | |
| 2720 | .lhs = span.start, | |
| 2721 | .rhs = span.end, | |
| 2722 | }, | |
| 2723 | }); | |
| 2724 | }, | |
| 2725 | } | |
| 2726 | }, | |
| 2727 | else => return null_node, | |
| 2728 | }, | |
| 2729 | .keyword_error => switch (p.token_tags[p.tok_i + 1]) { | |
| 2730 | .l_brace => { | |
| 2731 | const error_token = p.tok_i; | |
| 2732 | p.tok_i += 2; | |
| 2733 | while (true) { | |
| 2734 | if (p.eatToken(.r_brace)) |_| break; | |
| 2735 | _ = try p.eatDocComments(); | |
| 2736 | _ = try p.expectToken(.identifier); | |
| 2737 | switch (p.token_tags[p.tok_i]) { | |
| 2738 | .comma => p.tok_i += 1, | |
| 2739 | .r_brace => { | |
| 2740 | p.tok_i += 1; | |
| 2741 | break; | |
| 2742 | }, | |
| 2743 | .colon, .r_paren, .r_bracket => return p.failExpected(.r_brace), | |
| 2744 | // Likely just a missing comma; give error but continue parsing. | |
| 2745 | else => try p.warn(.expected_comma_after_field), | |
| 2746 | } | |
| 2747 | } | |
| 2748 | return p.addNode(.{ | |
| 2749 | .tag = .error_set_decl, | |
| 2750 | .main_token = error_token, | |
| 2751 | .data = .{ | |
| 2752 | .lhs = undefined, | |
| 2753 | .rhs = p.tok_i - 1, // rbrace | |
| 2754 | }, | |
| 2755 | }); | |
| 2756 | }, | |
| 2757 | else => { | |
| 2758 | const main_token = p.nextToken(); | |
| 2759 | const period = p.eatToken(.period); | |
| 2760 | if (period == null) try p.warnExpected(.period); | |
| 2761 | const identifier = p.eatToken(.identifier); | |
| 2762 | if (identifier == null) try p.warnExpected(.identifier); | |
| 2763 | return p.addNode(.{ | |
| 2764 | .tag = .error_value, | |
| 2765 | .main_token = main_token, | |
| 2766 | .data = .{ | |
| 2767 | .lhs = period orelse 0, | |
| 2768 | .rhs = identifier orelse 0, | |
| 2769 | }, | |
| 2770 | }); | |
| 2771 | }, | |
| 2772 | }, | |
| 2773 | .l_paren => return p.addNode(.{ | |
| 2774 | .tag = .grouped_expression, | |
| 2775 | .main_token = p.nextToken(), | |
| 2776 | .data = .{ | |
| 2777 | .lhs = try p.expectExpr(), | |
| 2778 | .rhs = try p.expectToken(.r_paren), | |
| 2779 | }, | |
| 2780 | }), | |
| 2781 | else => return null_node, | |
| 2782 | } | |
| 2783 | } | |
| 2784 | ||
| 2785 | fn expectPrimaryTypeExpr(p: *Parser) !Node.Index { | |
| 2786 | const node = try p.parsePrimaryTypeExpr(); | |
| 2787 | if (node == 0) { | |
| 2788 | return p.fail(.expected_primary_type_expr); | |
| 2789 | } | |
| 2790 | return node; | |
| 2791 | } | |
| 2792 | ||
| 2793 | /// ForPrefix <- KEYWORD_for LPAREN Expr RPAREN PtrIndexPayload | |
| 2794 | /// | |
| 2795 | /// ForTypeExpr <- ForPrefix TypeExpr (KEYWORD_else TypeExpr)? | |
| 2796 | fn parseForTypeExpr(p: *Parser) !Node.Index { | |
| 2797 | const for_token = p.eatToken(.keyword_for) orelse return null_node; | |
| 2798 | _ = try p.expectToken(.l_paren); | |
| 2799 | const array_expr = try p.expectExpr(); | |
| 2800 | _ = try p.expectToken(.r_paren); | |
| 2801 | const found_payload = try p.parsePtrIndexPayload(); | |
| 2802 | if (found_payload == 0) try p.warn(.expected_loop_payload); | |
| 2803 | ||
| 2804 | const then_expr = try p.expectTypeExpr(); | |
| 2805 | _ = p.eatToken(.keyword_else) orelse { | |
| 2806 | return p.addNode(.{ | |
| 2807 | .tag = .for_simple, | |
| 2808 | .main_token = for_token, | |
| 2809 | .data = .{ | |
| 2810 | .lhs = array_expr, | |
| 2811 | .rhs = then_expr, | |
| 2812 | }, | |
| 2813 | }); | |
| 2814 | }; | |
| 2815 | const else_expr = try p.expectTypeExpr(); | |
| 2816 | return p.addNode(.{ | |
| 2817 | .tag = .@"for", | |
| 2818 | .main_token = for_token, | |
| 2819 | .data = .{ | |
| 2820 | .lhs = array_expr, | |
| 2821 | .rhs = try p.addExtra(Node.If{ | |
| 2822 | .then_expr = then_expr, | |
| 2823 | .else_expr = else_expr, | |
| 2824 | }), | |
| 2825 | }, | |
| 2826 | }); | |
| 2827 | } | |
| 2828 | ||
| 2829 | /// WhilePrefix <- KEYWORD_while LPAREN Expr RPAREN PtrPayload? WhileContinueExpr? | |
| 2830 | /// | |
| 2831 | /// WhileTypeExpr <- WhilePrefix TypeExpr (KEYWORD_else Payload? TypeExpr)? | |
| 2832 | fn parseWhileTypeExpr(p: *Parser) !Node.Index { | |
| 2833 | const while_token = p.eatToken(.keyword_while) orelse return null_node; | |
| 2834 | _ = try p.expectToken(.l_paren); | |
| 2835 | const condition = try p.expectExpr(); | |
| 2836 | _ = try p.expectToken(.r_paren); | |
| 2837 | _ = try p.parsePtrPayload(); | |
| 2838 | const cont_expr = try p.parseWhileContinueExpr(); | |
| 2839 | ||
| 2840 | const then_expr = try p.expectTypeExpr(); | |
| 2841 | _ = p.eatToken(.keyword_else) orelse { | |
| 2842 | if (cont_expr == 0) { | |
| 2843 | return p.addNode(.{ | |
| 2844 | .tag = .while_simple, | |
| 2845 | .main_token = while_token, | |
| 2846 | .data = .{ | |
| 2847 | .lhs = condition, | |
| 2848 | .rhs = then_expr, | |
| 2849 | }, | |
| 2850 | }); | |
| 2851 | } else { | |
| 2852 | return p.addNode(.{ | |
| 2853 | .tag = .while_cont, | |
| 2854 | .main_token = while_token, | |
| 2855 | .data = .{ | |
| 2856 | .lhs = condition, | |
| 2857 | .rhs = try p.addExtra(Node.WhileCont{ | |
| 2858 | .cont_expr = cont_expr, | |
| 2859 | .then_expr = then_expr, | |
| 2860 | }), | |
| 2861 | }, | |
| 2862 | }); | |
| 2863 | } | |
| 2864 | }; | |
| 2865 | _ = try p.parsePayload(); | |
| 2866 | const else_expr = try p.expectTypeExpr(); | |
| 2867 | return p.addNode(.{ | |
| 2868 | .tag = .@"while", | |
| 2869 | .main_token = while_token, | |
| 2870 | .data = .{ | |
| 2871 | .lhs = condition, | |
| 2872 | .rhs = try p.addExtra(Node.While{ | |
| 2873 | .cont_expr = cont_expr, | |
| 2874 | .then_expr = then_expr, | |
| 2875 | .else_expr = else_expr, | |
| 2876 | }), | |
| 2877 | }, | |
| 2878 | }); | |
| 2879 | } | |
| 2880 | ||
| 2881 | /// SwitchExpr <- KEYWORD_switch LPAREN Expr RPAREN LBRACE SwitchProngList RBRACE | |
| 2882 | fn expectSwitchExpr(p: *Parser) !Node.Index { | |
| 2883 | const switch_token = p.assertToken(.keyword_switch); | |
| 2884 | _ = try p.expectToken(.l_paren); | |
| 2885 | const expr_node = try p.expectExpr(); | |
| 2886 | _ = try p.expectToken(.r_paren); | |
| 2887 | _ = try p.expectToken(.l_brace); | |
| 2888 | const cases = try p.parseSwitchProngList(); | |
| 2889 | const trailing_comma = p.token_tags[p.tok_i - 1] == .comma; | |
| 2890 | _ = try p.expectToken(.r_brace); | |
| 2891 | ||
| 2892 | return p.addNode(.{ | |
| 2893 | .tag = if (trailing_comma) .switch_comma else .@"switch", | |
| 2894 | .main_token = switch_token, | |
| 2895 | .data = .{ | |
| 2896 | .lhs = expr_node, | |
| 2897 | .rhs = try p.addExtra(Node.SubRange{ | |
| 2898 | .start = cases.start, | |
| 2899 | .end = cases.end, | |
| 2900 | }), | |
| 2901 | }, | |
| 2902 | }); | |
| 2903 | } | |
| 2904 | ||
| 2905 | /// AsmExpr <- KEYWORD_asm KEYWORD_volatile? LPAREN Expr AsmOutput? RPAREN | |
| 2906 | /// | |
| 2907 | /// AsmOutput <- COLON AsmOutputList AsmInput? | |
| 2908 | /// | |
| 2909 | /// AsmInput <- COLON AsmInputList AsmClobbers? | |
| 2910 | /// | |
| 2911 | /// AsmClobbers <- COLON StringList | |
| 2912 | /// | |
| 2913 | /// StringList <- (STRINGLITERAL COMMA)* STRINGLITERAL? | |
| 2914 | /// | |
| 2915 | /// AsmOutputList <- (AsmOutputItem COMMA)* AsmOutputItem? | |
| 2916 | /// | |
| 2917 | /// AsmInputList <- (AsmInputItem COMMA)* AsmInputItem? | |
| 2918 | fn expectAsmExpr(p: *Parser) !Node.Index { | |
| 2919 | const asm_token = p.assertToken(.keyword_asm); | |
| 2920 | _ = p.eatToken(.keyword_volatile); | |
| 2921 | _ = try p.expectToken(.l_paren); | |
| 2922 | const template = try p.expectExpr(); | |
| 2923 | ||
| 2924 | if (p.eatToken(.r_paren)) |rparen| { | |
| 2925 | return p.addNode(.{ | |
| 2926 | .tag = .asm_simple, | |
| 2927 | .main_token = asm_token, | |
| 2928 | .data = .{ | |
| 2929 | .lhs = template, | |
| 2930 | .rhs = rparen, | |
| 2931 | }, | |
| 2932 | }); | |
| 2933 | } | |
| 2934 | ||
| 2935 | _ = try p.expectToken(.colon); | |
| 2936 | ||
| 2937 | const scratch_top = p.scratch.items.len; | |
| 2938 | defer p.scratch.shrinkRetainingCapacity(scratch_top); | |
| 2939 | ||
| 2940 | while (true) { | |
| 2941 | const output_item = try p.parseAsmOutputItem(); | |
| 2942 | if (output_item == 0) break; | |
| 2943 | try p.scratch.append(p.gpa, output_item); | |
| 2944 | switch (p.token_tags[p.tok_i]) { | |
| 2945 | .comma => p.tok_i += 1, | |
| 2946 | // All possible delimiters. | |
| 2947 | .colon, .r_paren, .r_brace, .r_bracket => break, | |
| 2948 | // Likely just a missing comma; give error but continue parsing. | |
| 2949 | else => try p.warnExpected(.comma), | |
| 2950 | } | |
| 2951 | } | |
| 2952 | if (p.eatToken(.colon)) |_| { | |
| 2953 | while (true) { | |
| 2954 | const input_item = try p.parseAsmInputItem(); | |
| 2955 | if (input_item == 0) break; | |
| 2956 | try p.scratch.append(p.gpa, input_item); | |
| 2957 | switch (p.token_tags[p.tok_i]) { | |
| 2958 | .comma => p.tok_i += 1, | |
| 2959 | // All possible delimiters. | |
| 2960 | .colon, .r_paren, .r_brace, .r_bracket => break, | |
| 2961 | // Likely just a missing comma; give error but continue parsing. | |
| 2962 | else => try p.warnExpected(.comma), | |
| 2963 | } | |
| 2964 | } | |
| 2965 | if (p.eatToken(.colon)) |_| { | |
| 2966 | while (p.eatToken(.string_literal)) |_| { | |
| 2967 | switch (p.token_tags[p.tok_i]) { | |
| 2968 | .comma => p.tok_i += 1, | |
| 2969 | .colon, .r_paren, .r_brace, .r_bracket => break, | |
| 2970 | // Likely just a missing comma; give error but continue parsing. | |
| 2971 | else => try p.warnExpected(.comma), | |
| 2972 | } | |
| 2973 | } | |
| 2974 | } | |
| 2975 | } | |
| 2976 | const rparen = try p.expectToken(.r_paren); | |
| 2977 | const span = try p.listToSpan(p.scratch.items[scratch_top..]); | |
| 2978 | return p.addNode(.{ | |
| 2979 | .tag = .@"asm", | |
| 2980 | .main_token = asm_token, | |
| 2981 | .data = .{ | |
| 2982 | .lhs = template, | |
| 2983 | .rhs = try p.addExtra(Node.Asm{ | |
| 2984 | .items_start = span.start, | |
| 2985 | .items_end = span.end, | |
| 2986 | .rparen = rparen, | |
| 2987 | }), | |
| 2988 | }, | |
| 2989 | }); | |
| 2990 | } | |
| 2991 | ||
| 2992 | /// AsmOutputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERAL LPAREN (MINUSRARROW TypeExpr / IDENTIFIER) RPAREN | |
| 2993 | fn parseAsmOutputItem(p: *Parser) !Node.Index { | |
| 2994 | _ = p.eatToken(.l_bracket) orelse return null_node; | |
| 2995 | const identifier = try p.expectToken(.identifier); | |
| 2996 | _ = try p.expectToken(.r_bracket); | |
| 2997 | _ = try p.expectToken(.string_literal); | |
| 2998 | _ = try p.expectToken(.l_paren); | |
| 2999 | const type_expr: Node.Index = blk: { | |
| 3000 | if (p.eatToken(.arrow)) |_| { | |
| 3001 | break :blk try p.expectTypeExpr(); | |
| 3002 | } else { | |
| 3003 | _ = try p.expectToken(.identifier); | |
| 3004 | break :blk null_node; | |
| 3005 | } | |
| 3006 | }; | |
| 3007 | const rparen = try p.expectToken(.r_paren); | |
| 3008 | return p.addNode(.{ | |
| 3009 | .tag = .asm_output, | |
| 3010 | .main_token = identifier, | |
| 3011 | .data = .{ | |
| 3012 | .lhs = type_expr, | |
| 3013 | .rhs = rparen, | |
| 3014 | }, | |
| 3015 | }); | |
| 3016 | } | |
| 3017 | ||
| 3018 | /// AsmInputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERAL LPAREN Expr RPAREN | |
| 3019 | fn parseAsmInputItem(p: *Parser) !Node.Index { | |
| 3020 | _ = p.eatToken(.l_bracket) orelse return null_node; | |
| 3021 | const identifier = try p.expectToken(.identifier); | |
| 3022 | _ = try p.expectToken(.r_bracket); | |
| 3023 | _ = try p.expectToken(.string_literal); | |
| 3024 | _ = try p.expectToken(.l_paren); | |
| 3025 | const expr = try p.expectExpr(); | |
| 3026 | const rparen = try p.expectToken(.r_paren); | |
| 3027 | return p.addNode(.{ | |
| 3028 | .tag = .asm_input, | |
| 3029 | .main_token = identifier, | |
| 3030 | .data = .{ | |
| 3031 | .lhs = expr, | |
| 3032 | .rhs = rparen, | |
| 3033 | }, | |
| 3034 | }); | |
| 3035 | } | |
| 3036 | ||
| 3037 | /// BreakLabel <- COLON IDENTIFIER | |
| 3038 | fn parseBreakLabel(p: *Parser) !TokenIndex { | |
| 3039 | _ = p.eatToken(.colon) orelse return @as(TokenIndex, 0); | |
| 3040 | return p.expectToken(.identifier); | |
| 3041 | } | |
| 3042 | ||
| 3043 | /// BlockLabel <- IDENTIFIER COLON | |
| 3044 | fn parseBlockLabel(p: *Parser) TokenIndex { | |
| 3045 | if (p.token_tags[p.tok_i] == .identifier and | |
| 3046 | p.token_tags[p.tok_i + 1] == .colon) | |
| 3047 | { | |
| 3048 | const identifier = p.tok_i; | |
| 3049 | p.tok_i += 2; | |
| 3050 | return identifier; | |
| 3051 | } | |
| 3052 | return null_node; | |
| 3053 | } | |
| 3054 | ||
| 3055 | /// FieldInit <- DOT IDENTIFIER EQUAL Expr | |
| 3056 | fn parseFieldInit(p: *Parser) !Node.Index { | |
| 3057 | if (p.token_tags[p.tok_i + 0] == .period and | |
| 3058 | p.token_tags[p.tok_i + 1] == .identifier and | |
| 3059 | p.token_tags[p.tok_i + 2] == .equal) | |
| 3060 | { | |
| 3061 | p.tok_i += 3; | |
| 3062 | return p.expectExpr(); | |
| 3063 | } else { | |
| 3064 | return null_node; | |
| 3065 | } | |
| 3066 | } | |
| 3067 | ||
| 3068 | fn expectFieldInit(p: *Parser) !Node.Index { | |
| 3069 | if (p.token_tags[p.tok_i] != .period or | |
| 3070 | p.token_tags[p.tok_i + 1] != .identifier or | |
| 3071 | p.token_tags[p.tok_i + 2] != .equal) | |
| 3072 | return p.fail(.expected_initializer); | |
| 3073 | ||
| 3074 | p.tok_i += 3; | |
| 3075 | return p.expectExpr(); | |
| 3076 | } | |
| 3077 | ||
| 3078 | /// WhileContinueExpr <- COLON LPAREN AssignExpr RPAREN | |
| 3079 | fn parseWhileContinueExpr(p: *Parser) !Node.Index { | |
| 3080 | _ = p.eatToken(.colon) orelse { | |
| 3081 | if (p.token_tags[p.tok_i] == .l_paren and | |
| 3082 | p.tokensOnSameLine(p.tok_i - 1, p.tok_i)) | |
| 3083 | return p.fail(.expected_continue_expr); | |
| 3084 | return null_node; | |
| 3085 | }; | |
| 3086 | _ = try p.expectToken(.l_paren); | |
| 3087 | const node = try p.parseAssignExpr(); | |
| 3088 | if (node == 0) return p.fail(.expected_expr_or_assignment); | |
| 3089 | _ = try p.expectToken(.r_paren); | |
| 3090 | return node; | |
| 3091 | } | |
| 3092 | ||
| 3093 | /// LinkSection <- KEYWORD_linksection LPAREN Expr RPAREN | |
| 3094 | fn parseLinkSection(p: *Parser) !Node.Index { | |
| 3095 | _ = p.eatToken(.keyword_linksection) orelse return null_node; | |
| 3096 | _ = try p.expectToken(.l_paren); | |
| 3097 | const expr_node = try p.expectExpr(); | |
| 3098 | _ = try p.expectToken(.r_paren); | |
| 3099 | return expr_node; | |
| 3100 | } | |
| 3101 | ||
| 3102 | /// CallConv <- KEYWORD_callconv LPAREN Expr RPAREN | |
| 3103 | fn parseCallconv(p: *Parser) !Node.Index { | |
| 3104 | _ = p.eatToken(.keyword_callconv) orelse return null_node; | |
| 3105 | _ = try p.expectToken(.l_paren); | |
| 3106 | const expr_node = try p.expectExpr(); | |
| 3107 | _ = try p.expectToken(.r_paren); | |
| 3108 | return expr_node; | |
| 3109 | } | |
| 3110 | ||
| 3111 | /// AddrSpace <- KEYWORD_addrspace LPAREN Expr RPAREN | |
| 3112 | fn parseAddrSpace(p: *Parser) !Node.Index { | |
| 3113 | _ = p.eatToken(.keyword_addrspace) orelse return null_node; | |
| 3114 | _ = try p.expectToken(.l_paren); | |
| 3115 | const expr_node = try p.expectExpr(); | |
| 3116 | _ = try p.expectToken(.r_paren); | |
| 3117 | return expr_node; | |
| 3118 | } | |
| 3119 | ||
| 3120 | /// This function can return null nodes and then still return nodes afterwards, | |
| 3121 | /// such as in the case of anytype and `...`. Caller must look for rparen to find | |
| 3122 | /// out when there are no more param decls left. | |
| 3123 | /// | |
| 3124 | /// ParamDecl | |
| 3125 | /// <- doc_comment? (KEYWORD_noalias / KEYWORD_comptime)? (IDENTIFIER COLON)? ParamType | |
| 3126 | /// / DOT3 | |
| 3127 | /// | |
| 3128 | /// ParamType | |
| 3129 | /// <- KEYWORD_anytype | |
| 3130 | /// / TypeExpr | |
| 3131 | fn expectParamDecl(p: *Parser) !Node.Index { | |
| 3132 | _ = try p.eatDocComments(); | |
| 3133 | switch (p.token_tags[p.tok_i]) { | |
| 3134 | .keyword_noalias, .keyword_comptime => p.tok_i += 1, | |
| 3135 | .ellipsis3 => { | |
| 3136 | p.tok_i += 1; | |
| 3137 | return null_node; | |
| 3138 | }, | |
| 3139 | else => {}, | |
| 3140 | } | |
| 3141 | if (p.token_tags[p.tok_i] == .identifier and | |
| 3142 | p.token_tags[p.tok_i + 1] == .colon) | |
| 3143 | { | |
| 3144 | p.tok_i += 2; | |
| 3145 | } | |
| 3146 | switch (p.token_tags[p.tok_i]) { | |
| 3147 | .keyword_anytype => { | |
| 3148 | p.tok_i += 1; | |
| 3149 | return null_node; | |
| 3150 | }, | |
| 3151 | else => return p.expectTypeExpr(), | |
| 3152 | } | |
| 3153 | } | |
| 3154 | ||
| 3155 | /// Payload <- PIPE IDENTIFIER PIPE | |
| 3156 | fn parsePayload(p: *Parser) !TokenIndex { | |
| 3157 | _ = p.eatToken(.pipe) orelse return @as(TokenIndex, 0); | |
| 3158 | const identifier = try p.expectToken(.identifier); | |
| 3159 | _ = try p.expectToken(.pipe); | |
| 3160 | return identifier; | |
| 3161 | } | |
| 3162 | ||
| 3163 | /// PtrPayload <- PIPE ASTERISK? IDENTIFIER PIPE | |
| 3164 | fn parsePtrPayload(p: *Parser) !TokenIndex { | |
| 3165 | _ = p.eatToken(.pipe) orelse return @as(TokenIndex, 0); | |
| 3166 | _ = p.eatToken(.asterisk); | |
| 3167 | const identifier = try p.expectToken(.identifier); | |
| 3168 | _ = try p.expectToken(.pipe); | |
| 3169 | return identifier; | |
| 3170 | } | |
| 3171 | ||
| 3172 | /// Returns the first identifier token, if any. | |
| 3173 | /// | |
| 3174 | /// PtrIndexPayload <- PIPE ASTERISK? IDENTIFIER (COMMA IDENTIFIER)? PIPE | |
| 3175 | fn parsePtrIndexPayload(p: *Parser) !TokenIndex { | |
| 3176 | _ = p.eatToken(.pipe) orelse return @as(TokenIndex, 0); | |
| 3177 | _ = p.eatToken(.asterisk); | |
| 3178 | const identifier = try p.expectToken(.identifier); | |
| 3179 | if (p.eatToken(.comma) != null) { | |
| 3180 | _ = try p.expectToken(.identifier); | |
| 3181 | } | |
| 3182 | _ = try p.expectToken(.pipe); | |
| 3183 | return identifier; | |
| 3184 | } | |
| 3185 | ||
| 3186 | /// SwitchProng <- KEYWORD_inline? SwitchCase EQUALRARROW PtrIndexPayload? AssignExpr | |
| 3187 | /// | |
| 3188 | /// SwitchCase | |
| 3189 | /// <- SwitchItem (COMMA SwitchItem)* COMMA? | |
| 3190 | /// / KEYWORD_else | |
| 3191 | fn parseSwitchProng(p: *Parser) !Node.Index { | |
| 3192 | const scratch_top = p.scratch.items.len; | |
| 3193 | defer p.scratch.shrinkRetainingCapacity(scratch_top); | |
| 3194 | ||
| 3195 | const is_inline = p.eatToken(.keyword_inline) != null; | |
| 3196 | ||
| 3197 | if (p.eatToken(.keyword_else) == null) { | |
| 3198 | while (true) { | |
| 3199 | const item = try p.parseSwitchItem(); | |
| 3200 | if (item == 0) break; | |
| 3201 | try p.scratch.append(p.gpa, item); | |
| 3202 | if (p.eatToken(.comma) == null) break; | |
| 3203 | } | |
| 3204 | if (scratch_top == p.scratch.items.len) { | |
| 3205 | if (is_inline) p.tok_i -= 1; | |
| 3206 | return null_node; | |
| 3207 | } | |
| 3208 | } | |
| 3209 | const arrow_token = try p.expectToken(.equal_angle_bracket_right); | |
| 3210 | _ = try p.parsePtrIndexPayload(); | |
| 3211 | ||
| 3212 | const items = p.scratch.items[scratch_top..]; | |
| 3213 | switch (items.len) { | |
| 3214 | 0 => return p.addNode(.{ | |
| 3215 | .tag = if (is_inline) .switch_case_inline_one else .switch_case_one, | |
| 3216 | .main_token = arrow_token, | |
| 3217 | .data = .{ | |
| 3218 | .lhs = 0, | |
| 3219 | .rhs = try p.expectAssignExpr(), | |
| 3220 | }, | |
| 3221 | }), | |
| 3222 | 1 => return p.addNode(.{ | |
| 3223 | .tag = if (is_inline) .switch_case_inline_one else .switch_case_one, | |
| 3224 | .main_token = arrow_token, | |
| 3225 | .data = .{ | |
| 3226 | .lhs = items[0], | |
| 3227 | .rhs = try p.expectAssignExpr(), | |
| 3228 | }, | |
| 3229 | }), | |
| 3230 | else => return p.addNode(.{ | |
| 3231 | .tag = if (is_inline) .switch_case_inline else .switch_case, | |
| 3232 | .main_token = arrow_token, | |
| 3233 | .data = .{ | |
| 3234 | .lhs = try p.addExtra(try p.listToSpan(items)), | |
| 3235 | .rhs = try p.expectAssignExpr(), | |
| 3236 | }, | |
| 3237 | }), | |
| 3238 | } | |
| 3239 | } | |
| 3240 | ||
| 3241 | /// SwitchItem <- Expr (DOT3 Expr)? | |
| 3242 | fn parseSwitchItem(p: *Parser) !Node.Index { | |
| 3243 | const expr = try p.parseExpr(); | |
| 3244 | if (expr == 0) return null_node; | |
| 3245 | ||
| 3246 | if (p.eatToken(.ellipsis3)) |token| { | |
| 3247 | return p.addNode(.{ | |
| 3248 | .tag = .switch_range, | |
| 3249 | .main_token = token, | |
| 3250 | .data = .{ | |
| 3251 | .lhs = expr, | |
| 3252 | .rhs = try p.expectExpr(), | |
| 3253 | }, | |
| 3254 | }); | |
| 3255 | } | |
| 3256 | return expr; | |
| 3257 | } | |
| 3258 | ||
| 3259 | const PtrModifiers = struct { | |
| 3260 | align_node: Node.Index, | |
| 3261 | addrspace_node: Node.Index, | |
| 3262 | bit_range_start: Node.Index, | |
| 3263 | bit_range_end: Node.Index, | |
| 3264 | }; | |
| 3265 | ||
| 3266 | fn parsePtrModifiers(p: *Parser) !PtrModifiers { | |
| 3267 | var result: PtrModifiers = .{ | |
| 3268 | .align_node = 0, | |
| 3269 | .addrspace_node = 0, | |
| 3270 | .bit_range_start = 0, | |
| 3271 | .bit_range_end = 0, | |
| 3272 | }; | |
| 3273 | var saw_const = false; | |
| 3274 | var saw_volatile = false; | |
| 3275 | var saw_allowzero = false; | |
| 3276 | var saw_addrspace = false; | |
| 3277 | while (true) { | |
| 3278 | switch (p.token_tags[p.tok_i]) { | |
| 3279 | .keyword_align => { | |
| 3280 | if (result.align_node != 0) { | |
| 3281 | try p.warn(.extra_align_qualifier); | |
| 3282 | } | |
| 3283 | p.tok_i += 1; | |
| 3284 | _ = try p.expectToken(.l_paren); | |
| 3285 | result.align_node = try p.expectExpr(); | |
| 3286 | ||
| 3287 | if (p.eatToken(.colon)) |_| { | |
| 3288 | result.bit_range_start = try p.expectExpr(); | |
| 3289 | _ = try p.expectToken(.colon); | |
| 3290 | result.bit_range_end = try p.expectExpr(); | |
| 3291 | } | |
| 3292 | ||
| 3293 | _ = try p.expectToken(.r_paren); | |
| 3294 | }, | |
| 3295 | .keyword_const => { | |
| 3296 | if (saw_const) { | |
| 3297 | try p.warn(.extra_const_qualifier); | |
| 3298 | } | |
| 3299 | p.tok_i += 1; | |
| 3300 | saw_const = true; | |
| 3301 | }, | |
| 3302 | .keyword_volatile => { | |
| 3303 | if (saw_volatile) { | |
| 3304 | try p.warn(.extra_volatile_qualifier); | |
| 3305 | } | |
| 3306 | p.tok_i += 1; | |
| 3307 | saw_volatile = true; | |
| 3308 | }, | |
| 3309 | .keyword_allowzero => { | |
| 3310 | if (saw_allowzero) { | |
| 3311 | try p.warn(.extra_allowzero_qualifier); | |
| 3312 | } | |
| 3313 | p.tok_i += 1; | |
| 3314 | saw_allowzero = true; | |
| 3315 | }, | |
| 3316 | .keyword_addrspace => { | |
| 3317 | if (saw_addrspace) { | |
| 3318 | try p.warn(.extra_addrspace_qualifier); | |
| 3319 | } | |
| 3320 | result.addrspace_node = try p.parseAddrSpace(); | |
| 3321 | }, | |
| 3322 | else => return result, | |
| 3323 | } | |
| 3324 | } | |
| 3325 | } | |
| 3326 | ||
| 3327 | /// SuffixOp | |
| 3328 | /// <- LBRACKET Expr (DOT2 (Expr? (COLON Expr)?)?)? RBRACKET | |
| 3329 | /// / DOT IDENTIFIER | |
| 3330 | /// / DOTASTERISK | |
| 3331 | /// / DOTQUESTIONMARK | |
| 3332 | fn parseSuffixOp(p: *Parser, lhs: Node.Index) !Node.Index { | |
| 3333 | switch (p.token_tags[p.tok_i]) { | |
| 3334 | .l_bracket => { | |
| 3335 | const lbracket = p.nextToken(); | |
| 3336 | const index_expr = try p.expectExpr(); | |
| 3337 | ||
| 3338 | if (p.eatToken(.ellipsis2)) |_| { | |
| 3339 | const end_expr = try p.parseExpr(); | |
| 3340 | if (p.eatToken(.colon)) |_| { | |
| 3341 | const sentinel = try p.expectExpr(); | |
| 3342 | _ = try p.expectToken(.r_bracket); | |
| 3343 | return p.addNode(.{ | |
| 3344 | .tag = .slice_sentinel, | |
| 3345 | .main_token = lbracket, | |
| 3346 | .data = .{ | |
| 3347 | .lhs = lhs, | |
| 3348 | .rhs = try p.addExtra(Node.SliceSentinel{ | |
| 3349 | .start = index_expr, | |
| 3350 | .end = end_expr, | |
| 3351 | .sentinel = sentinel, | |
| 3352 | }), | |
| 3353 | }, | |
| 3354 | }); | |
| 3355 | } | |
| 3356 | _ = try p.expectToken(.r_bracket); | |
| 3357 | if (end_expr == 0) { | |
| 3358 | return p.addNode(.{ | |
| 3359 | .tag = .slice_open, | |
| 3360 | .main_token = lbracket, | |
| 3361 | .data = .{ | |
| 3362 | .lhs = lhs, | |
| 3363 | .rhs = index_expr, | |
| 3364 | }, | |
| 3365 | }); | |
| 3366 | } | |
| 3367 | return p.addNode(.{ | |
| 3368 | .tag = .slice, | |
| 3369 | .main_token = lbracket, | |
| 3370 | .data = .{ | |
| 3371 | .lhs = lhs, | |
| 3372 | .rhs = try p.addExtra(Node.Slice{ | |
| 3373 | .start = index_expr, | |
| 3374 | .end = end_expr, | |
| 3375 | }), | |
| 3376 | }, | |
| 3377 | }); | |
| 3378 | } | |
| 3379 | _ = try p.expectToken(.r_bracket); | |
| 3380 | return p.addNode(.{ | |
| 3381 | .tag = .array_access, | |
| 3382 | .main_token = lbracket, | |
| 3383 | .data = .{ | |
| 3384 | .lhs = lhs, | |
| 3385 | .rhs = index_expr, | |
| 3386 | }, | |
| 3387 | }); | |
| 3388 | }, | |
| 3389 | .period_asterisk => return p.addNode(.{ | |
| 3390 | .tag = .deref, | |
| 3391 | .main_token = p.nextToken(), | |
| 3392 | .data = .{ | |
| 3393 | .lhs = lhs, | |
| 3394 | .rhs = undefined, | |
| 3395 | }, | |
| 3396 | }), | |
| 3397 | .invalid_periodasterisks => { | |
| 3398 | try p.warn(.asterisk_after_ptr_deref); | |
| 3399 | return p.addNode(.{ | |
| 3400 | .tag = .deref, | |
| 3401 | .main_token = p.nextToken(), | |
| 3402 | .data = .{ | |
| 3403 | .lhs = lhs, | |
| 3404 | .rhs = undefined, | |
| 3405 | }, | |
| 3406 | }); | |
| 3407 | }, | |
| 3408 | .period => switch (p.token_tags[p.tok_i + 1]) { | |
| 3409 | .identifier => return p.addNode(.{ | |
| 3410 | .tag = .field_access, | |
| 3411 | .main_token = p.nextToken(), | |
| 3412 | .data = .{ | |
| 3413 | .lhs = lhs, | |
| 3414 | .rhs = p.nextToken(), | |
| 3415 | }, | |
| 3416 | }), | |
| 3417 | .question_mark => return p.addNode(.{ | |
| 3418 | .tag = .unwrap_optional, | |
| 3419 | .main_token = p.nextToken(), | |
| 3420 | .data = .{ | |
| 3421 | .lhs = lhs, | |
| 3422 | .rhs = p.nextToken(), | |
| 3423 | }, | |
| 3424 | }), | |
| 3425 | .l_brace => { | |
| 3426 | // this a misplaced `.{`, handle the error somewhere else | |
| 3427 | return null_node; | |
| 3428 | }, | |
| 3429 | else => { | |
| 3430 | p.tok_i += 1; | |
| 3431 | try p.warn(.expected_suffix_op); | |
| 3432 | return null_node; | |
| 3433 | }, | |
| 3434 | }, | |
| 3435 | else => return null_node, | |
| 3436 | } | |
| 3437 | } | |
| 3438 | ||
| 3439 | /// Caller must have already verified the first token. | |
| 3440 | /// | |
| 3441 | /// ContainerDeclAuto <- ContainerDeclType LBRACE container_doc_comment? ContainerMembers RBRACE | |
| 3442 | /// | |
| 3443 | /// ContainerDeclType | |
| 3444 | /// <- KEYWORD_struct (LPAREN Expr RPAREN)? | |
| 3445 | /// / KEYWORD_opaque | |
| 3446 | /// / KEYWORD_enum (LPAREN Expr RPAREN)? | |
| 3447 | /// / KEYWORD_union (LPAREN (KEYWORD_enum (LPAREN Expr RPAREN)? / Expr) RPAREN)? | |
| 3448 | fn parseContainerDeclAuto(p: *Parser) !Node.Index { | |
| 3449 | const main_token = p.nextToken(); | |
| 3450 | const arg_expr = switch (p.token_tags[main_token]) { | |
| 3451 | .keyword_opaque => null_node, | |
| 3452 | .keyword_struct, .keyword_enum => blk: { | |
| 3453 | if (p.eatToken(.l_paren)) |_| { | |
| 3454 | const expr = try p.expectExpr(); | |
| 3455 | _ = try p.expectToken(.r_paren); | |
| 3456 | break :blk expr; | |
| 3457 | } else { | |
| 3458 | break :blk null_node; | |
| 3459 | } | |
| 3460 | }, | |
| 3461 | .keyword_union => blk: { | |
| 3462 | if (p.eatToken(.l_paren)) |_| { | |
| 3463 | if (p.eatToken(.keyword_enum)) |_| { | |
| 3464 | if (p.eatToken(.l_paren)) |_| { | |
| 3465 | const enum_tag_expr = try p.expectExpr(); | |
| 3466 | _ = try p.expectToken(.r_paren); | |
| 3467 | _ = try p.expectToken(.r_paren); | |
| 3468 | ||
| 3469 | _ = try p.expectToken(.l_brace); | |
| 3470 | const members = try p.parseContainerMembers(); | |
| 3471 | const members_span = try members.toSpan(p); | |
| 3472 | _ = try p.expectToken(.r_brace); | |
| 3473 | return p.addNode(.{ | |
| 3474 | .tag = switch (members.trailing) { | |
| 3475 | true => .tagged_union_enum_tag_trailing, | |
| 3476 | false => .tagged_union_enum_tag, | |
| 3477 | }, | |
| 3478 | .main_token = main_token, | |
| 3479 | .data = .{ | |
| 3480 | .lhs = enum_tag_expr, | |
| 3481 | .rhs = try p.addExtra(members_span), | |
| 3482 | }, | |
| 3483 | }); | |
| 3484 | } else { | |
| 3485 | _ = try p.expectToken(.r_paren); | |
| 3486 | ||
| 3487 | _ = try p.expectToken(.l_brace); | |
| 3488 | const members = try p.parseContainerMembers(); | |
| 3489 | _ = try p.expectToken(.r_brace); | |
| 3490 | if (members.len <= 2) { | |
| 3491 | return p.addNode(.{ | |
| 3492 | .tag = switch (members.trailing) { | |
| 3493 | true => .tagged_union_two_trailing, | |
| 3494 | false => .tagged_union_two, | |
| 3495 | }, | |
| 3496 | .main_token = main_token, | |
| 3497 | .data = .{ | |
| 3498 | .lhs = members.lhs, | |
| 3499 | .rhs = members.rhs, | |
| 3500 | }, | |
| 3501 | }); | |
| 3502 | } else { | |
| 3503 | const span = try members.toSpan(p); | |
| 3504 | return p.addNode(.{ | |
| 3505 | .tag = switch (members.trailing) { | |
| 3506 | true => .tagged_union_trailing, | |
| 3507 | false => .tagged_union, | |
| 3508 | }, | |
| 3509 | .main_token = main_token, | |
| 3510 | .data = .{ | |
| 3511 | .lhs = span.start, | |
| 3512 | .rhs = span.end, | |
| 3513 | }, | |
| 3514 | }); | |
| 3515 | } | |
| 3516 | } | |
| 3517 | } else { | |
| 3518 | const expr = try p.expectExpr(); | |
| 3519 | _ = try p.expectToken(.r_paren); | |
| 3520 | break :blk expr; | |
| 3521 | } | |
| 3522 | } else { | |
| 3523 | break :blk null_node; | |
| 3524 | } | |
| 3525 | }, | |
| 3526 | else => { | |
| 3527 | p.tok_i -= 1; | |
| 3528 | return p.fail(.expected_container); | |
| 3529 | }, | |
| 3530 | }; | |
| 3531 | _ = try p.expectToken(.l_brace); | |
| 3532 | const members = try p.parseContainerMembers(); | |
| 3533 | _ = try p.expectToken(.r_brace); | |
| 3534 | if (arg_expr == 0) { | |
| 3535 | if (members.len <= 2) { | |
| 3536 | return p.addNode(.{ | |
| 3537 | .tag = switch (members.trailing) { | |
| 3538 | true => .container_decl_two_trailing, | |
| 3539 | false => .container_decl_two, | |
| 3540 | }, | |
| 3541 | .main_token = main_token, | |
| 3542 | .data = .{ | |
| 3543 | .lhs = members.lhs, | |
| 3544 | .rhs = members.rhs, | |
| 3545 | }, | |
| 3546 | }); | |
| 3547 | } else { | |
| 3548 | const span = try members.toSpan(p); | |
| 3549 | return p.addNode(.{ | |
| 3550 | .tag = switch (members.trailing) { | |
| 3551 | true => .container_decl_trailing, | |
| 3552 | false => .container_decl, | |
| 3553 | }, | |
| 3554 | .main_token = main_token, | |
| 3555 | .data = .{ | |
| 3556 | .lhs = span.start, | |
| 3557 | .rhs = span.end, | |
| 3558 | }, | |
| 3559 | }); | |
| 3560 | } | |
| 3561 | } else { | |
| 3562 | const span = try members.toSpan(p); | |
| 3563 | return p.addNode(.{ | |
| 3564 | .tag = switch (members.trailing) { | |
| 3565 | true => .container_decl_arg_trailing, | |
| 3566 | false => .container_decl_arg, | |
| 3567 | }, | |
| 3568 | .main_token = main_token, | |
| 3569 | .data = .{ | |
| 3570 | .lhs = arg_expr, | |
| 3571 | .rhs = try p.addExtra(Node.SubRange{ | |
| 3572 | .start = span.start, | |
| 3573 | .end = span.end, | |
| 3574 | }), | |
| 3575 | }, | |
| 3576 | }); | |
| 3577 | } | |
| 3578 | } | |
| 3579 | ||
| 3580 | /// Give a helpful error message for those transitioning from | |
| 3581 | /// C's 'struct Foo {};' to Zig's 'const Foo = struct {};'. | |
| 3582 | fn parseCStyleContainer(p: *Parser) Error!bool { | |
| 3583 | const main_token = p.tok_i; | |
| 3584 | switch (p.token_tags[p.tok_i]) { | |
| 3585 | .keyword_enum, .keyword_union, .keyword_struct => {}, | |
| 3586 | else => return false, | |
| 3587 | } | |
| 3588 | const identifier = p.tok_i + 1; | |
| 3589 | if (p.token_tags[identifier] != .identifier) return false; | |
| 3590 | p.tok_i += 2; | |
| 3591 | ||
| 3592 | try p.warnMsg(.{ | |
| 3593 | .tag = .c_style_container, | |
| 3594 | .token = identifier, | |
| 3595 | .extra = .{ .expected_tag = p.token_tags[main_token] }, | |
| 3596 | }); | |
| 3597 | try p.warnMsg(.{ | |
| 3598 | .tag = .zig_style_container, | |
| 3599 | .is_note = true, | |
| 3600 | .token = identifier, | |
| 3601 | .extra = .{ .expected_tag = p.token_tags[main_token] }, | |
| 3602 | }); | |
| 3603 | ||
| 3604 | _ = try p.expectToken(.l_brace); | |
| 3605 | _ = try p.parseContainerMembers(); | |
| 3606 | _ = try p.expectToken(.r_brace); | |
| 3607 | try p.expectSemicolon(.expected_semi_after_decl, true); | |
| 3608 | return true; | |
| 3609 | } | |
| 3610 | ||
| 3611 | /// Holds temporary data until we are ready to construct the full ContainerDecl AST node. | |
| 3612 | /// | |
| 3613 | /// ByteAlign <- KEYWORD_align LPAREN Expr RPAREN | |
| 3614 | fn parseByteAlign(p: *Parser) !Node.Index { | |
| 3615 | _ = p.eatToken(.keyword_align) orelse return null_node; | |
| 3616 | _ = try p.expectToken(.l_paren); | |
| 3617 | const expr = try p.expectExpr(); | |
| 3618 | _ = try p.expectToken(.r_paren); | |
| 3619 | return expr; | |
| 3620 | } | |
| 3621 | ||
| 3622 | /// SwitchProngList <- (SwitchProng COMMA)* SwitchProng? | |
| 3623 | fn parseSwitchProngList(p: *Parser) !Node.SubRange { | |
| 3624 | const scratch_top = p.scratch.items.len; | |
| 3625 | defer p.scratch.shrinkRetainingCapacity(scratch_top); | |
| 3626 | ||
| 3627 | while (true) { | |
| 3628 | const item = try parseSwitchProng(p); | |
| 3629 | if (item == 0) break; | |
| 3630 | ||
| 3631 | try p.scratch.append(p.gpa, item); | |
| 3632 | ||
| 3633 | switch (p.token_tags[p.tok_i]) { | |
| 3634 | .comma => p.tok_i += 1, | |
| 3635 | // All possible delimiters. | |
| 3636 | .colon, .r_paren, .r_brace, .r_bracket => break, | |
| 3637 | // Likely just a missing comma; give error but continue parsing. | |
| 3638 | else => try p.warn(.expected_comma_after_switch_prong), | |
| 3639 | } | |
| 3640 | } | |
| 3641 | return p.listToSpan(p.scratch.items[scratch_top..]); | |
| 3642 | } | |
| 3643 | ||
| 3644 | /// ParamDeclList <- (ParamDecl COMMA)* ParamDecl? | |
| 3645 | fn parseParamDeclList(p: *Parser) !SmallSpan { | |
| 3646 | _ = try p.expectToken(.l_paren); | |
| 3647 | const scratch_top = p.scratch.items.len; | |
| 3648 | defer p.scratch.shrinkRetainingCapacity(scratch_top); | |
| 3649 | var varargs: union(enum) { none, seen, nonfinal: TokenIndex } = .none; | |
| 3650 | while (true) { | |
| 3651 | if (p.eatToken(.r_paren)) |_| break; | |
| 3652 | if (varargs == .seen) varargs = .{ .nonfinal = p.tok_i }; | |
| 3653 | const param = try p.expectParamDecl(); | |
| 3654 | if (param != 0) { | |
| 3655 | try p.scratch.append(p.gpa, param); | |
| 3656 | } else if (p.token_tags[p.tok_i - 1] == .ellipsis3) { | |
| 3657 | if (varargs == .none) varargs = .seen; | |
| 3658 | } | |
| 3659 | switch (p.token_tags[p.tok_i]) { | |
| 3660 | .comma => p.tok_i += 1, | |
| 3661 | .r_paren => { | |
| 3662 | p.tok_i += 1; | |
| 3663 | break; | |
| 3664 | }, | |
| 3665 | .colon, .r_brace, .r_bracket => return p.failExpected(.r_paren), | |
| 3666 | // Likely just a missing comma; give error but continue parsing. | |
| 3667 | else => try p.warn(.expected_comma_after_param), | |
| 3668 | } | |
| 3669 | } | |
| 3670 | if (varargs == .nonfinal) { | |
| 3671 | try p.warnMsg(.{ .tag = .varargs_nonfinal, .token = varargs.nonfinal }); | |
| 3672 | } | |
| 3673 | const params = p.scratch.items[scratch_top..]; | |
| 3674 | return switch (params.len) { | |
| 3675 | 0 => SmallSpan{ .zero_or_one = 0 }, | |
| 3676 | 1 => SmallSpan{ .zero_or_one = params[0] }, | |
| 3677 | else => SmallSpan{ .multi = try p.listToSpan(params) }, | |
| 3678 | }; | |
| 3679 | } | |
| 3680 | ||
| 3681 | /// FnCallArguments <- LPAREN ExprList RPAREN | |
| 3682 | /// | |
| 3683 | /// ExprList <- (Expr COMMA)* Expr? | |
| 3684 | fn parseBuiltinCall(p: *Parser) !Node.Index { | |
| 3685 | const builtin_token = p.assertToken(.builtin); | |
| 3686 | if (p.token_tags[p.nextToken()] != .l_paren) { | |
| 3687 | p.tok_i -= 1; | |
| 3688 | try p.warn(.expected_param_list); | |
| 3689 | // Pretend this was an identifier so we can continue parsing. | |
| 3690 | return p.addNode(.{ | |
| 3691 | .tag = .identifier, | |
| 3692 | .main_token = builtin_token, | |
| 3693 | .data = .{ | |
| 3694 | .lhs = undefined, | |
| 3695 | .rhs = undefined, | |
| 3696 | }, | |
| 3697 | }); | |
| 3698 | } | |
| 3699 | const scratch_top = p.scratch.items.len; | |
| 3700 | defer p.scratch.shrinkRetainingCapacity(scratch_top); | |
| 3701 | while (true) { | |
| 3702 | if (p.eatToken(.r_paren)) |_| break; | |
| 3703 | const param = try p.expectExpr(); | |
| 3704 | try p.scratch.append(p.gpa, param); | |
| 3705 | switch (p.token_tags[p.tok_i]) { | |
| 3706 | .comma => p.tok_i += 1, | |
| 3707 | .r_paren => { | |
| 3708 | p.tok_i += 1; | |
| 3709 | break; | |
| 3710 | }, | |
| 3711 | // Likely just a missing comma; give error but continue parsing. | |
| 3712 | else => try p.warn(.expected_comma_after_arg), | |
| 3713 | } | |
| 3714 | } | |
| 3715 | const comma = (p.token_tags[p.tok_i - 2] == .comma); | |
| 3716 | const params = p.scratch.items[scratch_top..]; | |
| 3717 | switch (params.len) { | |
| 3718 | 0 => return p.addNode(.{ | |
| 3719 | .tag = .builtin_call_two, | |
| 3720 | .main_token = builtin_token, | |
| 3721 | .data = .{ | |
| 3722 | .lhs = 0, | |
| 3723 | .rhs = 0, | |
| 3724 | }, | |
| 3725 | }), | |
| 3726 | 1 => return p.addNode(.{ | |
| 3727 | .tag = if (comma) .builtin_call_two_comma else .builtin_call_two, | |
| 3728 | .main_token = builtin_token, | |
| 3729 | .data = .{ | |
| 3730 | .lhs = params[0], | |
| 3731 | .rhs = 0, | |
| 3732 | }, | |
| 3733 | }), | |
| 3734 | 2 => return p.addNode(.{ | |
| 3735 | .tag = if (comma) .builtin_call_two_comma else .builtin_call_two, | |
| 3736 | .main_token = builtin_token, | |
| 3737 | .data = .{ | |
| 3738 | .lhs = params[0], | |
| 3739 | .rhs = params[1], | |
| 3740 | }, | |
| 3741 | }), | |
| 3742 | else => { | |
| 3743 | const span = try p.listToSpan(params); | |
| 3744 | return p.addNode(.{ | |
| 3745 | .tag = if (comma) .builtin_call_comma else .builtin_call, | |
| 3746 | .main_token = builtin_token, | |
| 3747 | .data = .{ | |
| 3748 | .lhs = span.start, | |
| 3749 | .rhs = span.end, | |
| 3750 | }, | |
| 3751 | }); | |
| 3752 | }, | |
| 3753 | } | |
| 3754 | } | |
| 3755 | ||
| 3756 | /// IfPrefix <- KEYWORD_if LPAREN Expr RPAREN PtrPayload? | |
| 3757 | fn parseIf(p: *Parser, comptime bodyParseFn: fn (p: *Parser) Error!Node.Index) !Node.Index { | |
| 3758 | const if_token = p.eatToken(.keyword_if) orelse return null_node; | |
| 3759 | _ = try p.expectToken(.l_paren); | |
| 3760 | const condition = try p.expectExpr(); | |
| 3761 | _ = try p.expectToken(.r_paren); | |
| 3762 | _ = try p.parsePtrPayload(); | |
| 3763 | ||
| 3764 | const then_expr = try bodyParseFn(p); | |
| 3765 | assert(then_expr != 0); | |
| 3766 | ||
| 3767 | _ = p.eatToken(.keyword_else) orelse return p.addNode(.{ | |
| 3768 | .tag = .if_simple, | |
| 3769 | .main_token = if_token, | |
| 3770 | .data = .{ | |
| 3771 | .lhs = condition, | |
| 3772 | .rhs = then_expr, | |
| 3773 | }, | |
| 3774 | }); | |
| 3775 | _ = try p.parsePayload(); | |
| 3776 | const else_expr = try bodyParseFn(p); | |
| 3777 | assert(then_expr != 0); | |
| 3778 | ||
| 3779 | return p.addNode(.{ | |
| 3780 | .tag = .@"if", | |
| 3781 | .main_token = if_token, | |
| 3782 | .data = .{ | |
| 3783 | .lhs = condition, | |
| 3784 | .rhs = try p.addExtra(Node.If{ | |
| 3785 | .then_expr = then_expr, | |
| 3786 | .else_expr = else_expr, | |
| 3787 | }), | |
| 3788 | }, | |
| 3789 | }); | |
| 3790 | } | |
| 3791 | ||
| 3792 | /// Skips over doc comment tokens. Returns the first one, if any. | |
| 3793 | fn eatDocComments(p: *Parser) !?TokenIndex { | |
| 3794 | if (p.eatToken(.doc_comment)) |tok| { | |
| 3795 | var first_line = tok; | |
| 3796 | if (tok > 0 and tokensOnSameLine(p, tok - 1, tok)) { | |
| 3797 | try p.warnMsg(.{ | |
| 3798 | .tag = .same_line_doc_comment, | |
| 3799 | .token = tok, | |
| 3800 | }); | |
| 3801 | first_line = p.eatToken(.doc_comment) orelse return null; | |
| 3802 | } | |
| 3803 | while (p.eatToken(.doc_comment)) |_| {} | |
| 3804 | return first_line; | |
| 3805 | } | |
| 3806 | return null; | |
| 3807 | } | |
| 3808 | ||
| 3809 | fn tokensOnSameLine(p: *Parser, token1: TokenIndex, token2: TokenIndex) bool { | |
| 3810 | return std.mem.indexOfScalar(u8, p.source[p.token_starts[token1]..p.token_starts[token2]], '\n') == null; | |
| 3811 | } | |
| 3812 | ||
| 3813 | fn eatToken(p: *Parser, tag: Token.Tag) ?TokenIndex { | |
| 3814 | return if (p.token_tags[p.tok_i] == tag) p.nextToken() else null; | |
| 3815 | } | |
| 3816 | ||
| 3817 | fn assertToken(p: *Parser, tag: Token.Tag) TokenIndex { | |
| 3818 | const token = p.nextToken(); | |
| 3819 | assert(p.token_tags[token] == tag); | |
| 3820 | return token; | |
| 3821 | } | |
| 3822 | ||
| 3823 | fn expectToken(p: *Parser, tag: Token.Tag) Error!TokenIndex { | |
| 3824 | if (p.token_tags[p.tok_i] != tag) { | |
| 3825 | return p.failMsg(.{ | |
| 3826 | .tag = .expected_token, | |
| 3827 | .token = p.tok_i, | |
| 3828 | .extra = .{ .expected_tag = tag }, | |
| 3829 | }); | |
| 3830 | } | |
| 3831 | return p.nextToken(); | |
| 3832 | } | |
| 3833 | ||
| 3834 | fn expectSemicolon(p: *Parser, error_tag: AstError.Tag, recoverable: bool) Error!void { | |
| 3835 | if (p.token_tags[p.tok_i] == .semicolon) { | |
| 3836 | _ = p.nextToken(); | |
| 3837 | return; | |
| 3838 | } | |
| 3839 | try p.warn(error_tag); | |
| 3840 | if (!recoverable) return error.ParseError; | |
| 3841 | } | |
| 3842 | ||
| 3843 | fn nextToken(p: *Parser) TokenIndex { | |
| 3844 | const result = p.tok_i; | |
| 3845 | p.tok_i += 1; | |
| 3846 | return result; | |
| 3847 | } | |
| 3848 | }; | |
| 3849 | ||
| 3850 | test { | |
| 3851 | _ = @import("parser_test.zig"); | |
| 3852 | } |
lib/std/zig/parser_test.zig+11-2| ... | ... | @@ -186,6 +186,15 @@ test "zig fmt: file ends in comment" { |
| 186 | 186 | ); |
| 187 | 187 | } |
| 188 | 188 | |
| 189 | test "zig fmt: file ends in multi line comment" { | |
| 190 | try testTransform( | |
| 191 | \\ \\foobar | |
| 192 | , | |
| 193 | \\\\foobar | |
| 194 | \\ | |
| 195 | ); | |
| 196 | } | |
| 197 | ||
| 189 | 198 | test "zig fmt: file ends in comment after var decl" { |
| 190 | 199 | try testTransform( |
| 191 | 200 | \\const x = 42; |
| ... | ... | @@ -6064,7 +6073,7 @@ var fixed_buffer_mem: [100 * 1024]u8 = undefined; |
| 6064 | 6073 | fn testParse(source: [:0]const u8, allocator: mem.Allocator, anything_changed: *bool) ![]u8 { |
| 6065 | 6074 | const stderr = io.getStdErr().writer(); |
| 6066 | 6075 | |
| 6067 | var tree = try std.zig.parse(allocator, source); | |
| 6076 | var tree = try std.zig.Ast.parse(allocator, source, .zig); | |
| 6068 | 6077 | defer tree.deinit(allocator); |
| 6069 | 6078 | |
| 6070 | 6079 | for (tree.errors) |parse_error| { |
| ... | ... | @@ -6115,7 +6124,7 @@ fn testCanonical(source: [:0]const u8) !void { |
| 6115 | 6124 | const Error = std.zig.Ast.Error.Tag; |
| 6116 | 6125 | |
| 6117 | 6126 | fn testError(source: [:0]const u8, expected_errors: []const Error) !void { |
| 6118 | var tree = try std.zig.parse(std.testing.allocator, source); | |
| 6127 | var tree = try std.zig.Ast.parse(std.testing.allocator, source, .zig); | |
| 6119 | 6128 | defer tree.deinit(std.testing.allocator); |
| 6120 | 6129 | |
| 6121 | 6130 | std.testing.expectEqual(expected_errors.len, tree.errors.len) catch |err| { |
lib/std/zig/perf_test.zig+1-2| ... | ... | @@ -1,7 +1,6 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | const mem = std.mem; |
| 3 | 3 | const Tokenizer = std.zig.Tokenizer; |
| 4 | const Parser = std.zig.Parser; | |
| 5 | 4 | const io = std.io; |
| 6 | 5 | const fmtIntSizeBin = std.fmt.fmtIntSizeBin; |
| 7 | 6 | |
| ... | ... | @@ -34,6 +33,6 @@ pub fn main() !void { |
| 34 | 33 | fn testOnce() usize { |
| 35 | 34 | var fixed_buf_alloc = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]); |
| 36 | 35 | var allocator = fixed_buf_alloc.allocator(); |
| 37 | _ = std.zig.parse(allocator, source) catch @panic("parse failure"); | |
| 36 | _ = std.zig.Ast.parse(allocator, source, .zig) catch @panic("parse failure"); | |
| 38 | 37 | return fixed_buf_alloc.end_index; |
| 39 | 38 | } |
lib/std/zig/render.zig+1-2| ... | ... | @@ -2759,8 +2759,7 @@ fn tokenSliceForRender(tree: Ast, token_index: Ast.TokenIndex) []const u8 { |
| 2759 | 2759 | var ret = tree.tokenSlice(token_index); |
| 2760 | 2760 | switch (tree.tokens.items(.tag)[token_index]) { |
| 2761 | 2761 | .multiline_string_literal_line => { |
| 2762 | assert(ret[ret.len - 1] == '\n'); | |
| 2763 | ret.len -= 1; | |
| 2762 | if (ret[ret.len - 1] == '\n') ret.len -= 1; | |
| 2764 | 2763 | }, |
| 2765 | 2764 | .container_doc_comment, .doc_comment => { |
| 2766 | 2765 | ret = mem.trimRight(u8, ret, &std.ascii.whitespace); |
lib/test_runner.zig+1-1| ... | ... | @@ -11,7 +11,7 @@ var log_err_count: usize = 0; |
| 11 | 11 | |
| 12 | 12 | pub fn main() void { |
| 13 | 13 | if (builtin.zig_backend != .stage1 and |
| 14 | (builtin.zig_backend != .stage2_llvm or builtin.cpu.arch == .wasm32) and | |
| 14 | builtin.zig_backend != .stage2_llvm and | |
| 15 | 15 | builtin.zig_backend != .stage2_c) |
| 16 | 16 | { |
| 17 | 17 | return main2() catch @panic("test failure"); |
lib/zig.h+24| ... | ... | @@ -93,6 +93,14 @@ typedef char bool; |
| 93 | 93 | #define zig_align zig_align_unavailable |
| 94 | 94 | #endif |
| 95 | 95 | |
| 96 | #if zig_has_attribute(aligned) | |
| 97 | #define zig_under_align(alignment) __attribute__((aligned(alignment))) | |
| 98 | #elif _MSC_VER | |
| 99 | #define zig_under_align(alignment) zig_align(alignment) | |
| 100 | #else | |
| 101 | #define zig_align zig_align_unavailable | |
| 102 | #endif | |
| 103 | ||
| 96 | 104 | #if zig_has_attribute(aligned) |
| 97 | 105 | #define zig_align_fn(alignment) __attribute__((aligned(alignment))) |
| 98 | 106 | #elif _MSC_VER |
| ... | ... | @@ -101,6 +109,22 @@ typedef char bool; |
| 101 | 109 | #define zig_align_fn zig_align_fn_unavailable |
| 102 | 110 | #endif |
| 103 | 111 | |
| 112 | #if zig_has_attribute(packed) | |
| 113 | #define zig_packed(definition) __attribute__((packed)) definition | |
| 114 | #elif _MSC_VER | |
| 115 | #define zig_packed(definition) __pragma(pack(1)) definition __pragma(pack()) | |
| 116 | #else | |
| 117 | #define zig_packed(definition) zig_packed_unavailable | |
| 118 | #endif | |
| 119 | ||
| 120 | #if zig_has_attribute(section) | |
| 121 | #define zig_linksection(name, def, ...) def __attribute__((section(name))) | |
| 122 | #elif _MSC_VER | |
| 123 | #define zig_linksection(name, def, ...) __pragma(section(name, __VA_ARGS__)) __declspec(allocate(name)) def | |
| 124 | #else | |
| 125 | #define zig_linksection(name, def, ...) zig_linksection_unavailable | |
| 126 | #endif | |
| 127 | ||
| 104 | 128 | #if zig_has_builtin(unreachable) || defined(zig_gnuc) |
| 105 | 129 | #define zig_unreachable() __builtin_unreachable() |
| 106 | 130 | #else |
src/AstGen.zig+37-2| ... | ... | @@ -2530,6 +2530,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As |
| 2530 | 2530 | .bit_size_of, |
| 2531 | 2531 | .typeof_log2_int_type, |
| 2532 | 2532 | .ptr_to_int, |
| 2533 | .qual_cast, | |
| 2533 | 2534 | .align_of, |
| 2534 | 2535 | .bool_to_int, |
| 2535 | 2536 | .embed_file, |
| ... | ... | @@ -4278,7 +4279,34 @@ fn testDecl( |
| 4278 | 4279 | var num_namespaces_out: u32 = 0; |
| 4279 | 4280 | var capturing_namespace: ?*Scope.Namespace = null; |
| 4280 | 4281 | while (true) switch (s.tag) { |
| 4281 | .local_val, .local_ptr => unreachable, // a test cannot be in a local scope | |
| 4282 | .local_val => { | |
| 4283 | const local_val = s.cast(Scope.LocalVal).?; | |
| 4284 | if (local_val.name == name_str_index) { | |
| 4285 | local_val.used = test_name_token; | |
| 4286 | return astgen.failTokNotes(test_name_token, "cannot test a {s}", .{ | |
| 4287 | @tagName(local_val.id_cat), | |
| 4288 | }, &[_]u32{ | |
| 4289 | try astgen.errNoteTok(local_val.token_src, "{s} declared here", .{ | |
| 4290 | @tagName(local_val.id_cat), | |
| 4291 | }), | |
| 4292 | }); | |
| 4293 | } | |
| 4294 | s = local_val.parent; | |
| 4295 | }, | |
| 4296 | .local_ptr => { | |
| 4297 | const local_ptr = s.cast(Scope.LocalPtr).?; | |
| 4298 | if (local_ptr.name == name_str_index) { | |
| 4299 | local_ptr.used = test_name_token; | |
| 4300 | return astgen.failTokNotes(test_name_token, "cannot test a {s}", .{ | |
| 4301 | @tagName(local_ptr.id_cat), | |
| 4302 | }, &[_]u32{ | |
| 4303 | try astgen.errNoteTok(local_ptr.token_src, "{s} declared here", .{ | |
| 4304 | @tagName(local_ptr.id_cat), | |
| 4305 | }), | |
| 4306 | }); | |
| 4307 | } | |
| 4308 | s = local_ptr.parent; | |
| 4309 | }, | |
| 4282 | 4310 | .gen_zir => s = s.cast(GenZir).?.parent, |
| 4283 | 4311 | .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent, |
| 4284 | 4312 | .namespace, .enum_namespace => { |
| ... | ... | @@ -8010,6 +8038,7 @@ fn builtinCall( |
| 8010 | 8038 | .float_cast => return typeCast(gz, scope, ri, node, params[0], params[1], .float_cast), |
| 8011 | 8039 | .int_cast => return typeCast(gz, scope, ri, node, params[0], params[1], .int_cast), |
| 8012 | 8040 | .ptr_cast => return typeCast(gz, scope, ri, node, params[0], params[1], .ptr_cast), |
| 8041 | .qual_cast => return typeCast(gz, scope, ri, node, params[0], params[1], .qual_cast), | |
| 8013 | 8042 | .truncate => return typeCast(gz, scope, ri, node, params[0], params[1], .truncate), |
| 8014 | 8043 | // zig fmt: on |
| 8015 | 8044 | |
| ... | ... | @@ -8692,6 +8721,7 @@ fn callExpr( |
| 8692 | 8721 | defer arg_block.unstack(); |
| 8693 | 8722 | |
| 8694 | 8723 | // `call_inst` is reused to provide the param type. |
| 8724 | arg_block.rl_ty_inst = call_inst; | |
| 8695 | 8725 | const arg_ref = try expr(&arg_block, &arg_block.base, .{ .rl = .{ .coerced_ty = call_inst }, .ctx = .fn_arg }, param_node); |
| 8696 | 8726 | _ = try arg_block.addBreak(.break_inline, call_index, arg_ref); |
| 8697 | 8727 | |
| ... | ... | @@ -10840,7 +10870,12 @@ const GenZir = struct { |
| 10840 | 10870 | // we emit ZIR for the block break instructions to have the result values, |
| 10841 | 10871 | // and then rvalue() on that to pass the value to the result location. |
| 10842 | 10872 | switch (parent_ri.rl) { |
| 10843 | .ty, .coerced_ty => |ty_inst| { | |
| 10873 | .coerced_ty => |ty_inst| { | |
| 10874 | // Type coercion needs to happend before breaks. | |
| 10875 | gz.rl_ty_inst = ty_inst; | |
| 10876 | gz.break_result_info = .{ .rl = .{ .ty = ty_inst } }; | |
| 10877 | }, | |
| 10878 | .ty => |ty_inst| { | |
| 10844 | 10879 | gz.rl_ty_inst = ty_inst; |
| 10845 | 10880 | gz.break_result_info = parent_ri; |
| 10846 | 10881 | }, |
src/Autodoc.zig+6-11| ... | ... | @@ -1400,6 +1400,7 @@ fn walkInstruction( |
| 1400 | 1400 | .float_cast, |
| 1401 | 1401 | .int_cast, |
| 1402 | 1402 | .ptr_cast, |
| 1403 | .qual_cast, | |
| 1403 | 1404 | .truncate, |
| 1404 | 1405 | .align_cast, |
| 1405 | 1406 | .has_decl, |
| ... | ... | @@ -2200,17 +2201,10 @@ fn walkInstruction( |
| 2200 | 2201 | false, |
| 2201 | 2202 | ); |
| 2202 | 2203 | |
| 2203 | _ = operand; | |
| 2204 | ||
| 2205 | // WIP | |
| 2206 | ||
| 2207 | printWithContext( | |
| 2208 | file, | |
| 2209 | inst_index, | |
| 2210 | "TODO: implement `{s}` for walkInstruction\n\n", | |
| 2211 | .{@tagName(tags[inst_index])}, | |
| 2212 | ); | |
| 2213 | return self.cteTodo(@tagName(tags[inst_index])); | |
| 2204 | return DocData.WalkResult{ | |
| 2205 | .typeRef = operand.expr, | |
| 2206 | .expr = .{ .@"struct" = &.{} }, | |
| 2207 | }; | |
| 2214 | 2208 | }, |
| 2215 | 2209 | .struct_init_anon => { |
| 2216 | 2210 | const pl_node = data[inst_index].pl_node; |
| ... | ... | @@ -2537,6 +2531,7 @@ fn walkInstruction( |
| 2537 | 2531 | const var_init_ref = @intToEnum(Ref, file.zir.extra[extra_index]); |
| 2538 | 2532 | const var_init = try self.walkRef(file, parent_scope, parent_src, var_init_ref, need_type); |
| 2539 | 2533 | value.expr = var_init.expr; |
| 2534 | value.typeRef = var_init.typeRef; | |
| 2540 | 2535 | } |
| 2541 | 2536 | |
| 2542 | 2537 | return value; |
src/BuiltinFn.zig+8| ... | ... | @@ -75,6 +75,7 @@ pub const Tag = enum { |
| 75 | 75 | prefetch, |
| 76 | 76 | ptr_cast, |
| 77 | 77 | ptr_to_int, |
| 78 | qual_cast, | |
| 78 | 79 | rem, |
| 79 | 80 | return_address, |
| 80 | 81 | select, |
| ... | ... | @@ -674,6 +675,13 @@ pub const list = list: { |
| 674 | 675 | .param_count = 1, |
| 675 | 676 | }, |
| 676 | 677 | }, |
| 678 | .{ | |
| 679 | "@qualCast", | |
| 680 | .{ | |
| 681 | .tag = .qual_cast, | |
| 682 | .param_count = 2, | |
| 683 | }, | |
| 684 | }, | |
| 677 | 685 | .{ |
| 678 | 686 | "@rem", |
| 679 | 687 | .{ |
src/Compilation.zig+2-2| ... | ... | @@ -385,7 +385,7 @@ pub const AllErrors = struct { |
| 385 | 385 | count: u32 = 1, |
| 386 | 386 | /// Does not include the trailing newline. |
| 387 | 387 | source_line: ?[]const u8, |
| 388 | notes: []Message = &.{}, | |
| 388 | notes: []const Message = &.{}, | |
| 389 | 389 | reference_trace: []Message = &.{}, |
| 390 | 390 | |
| 391 | 391 | /// Splits the error message up into lines to properly indent them |
| ... | ... | @@ -3299,7 +3299,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { |
| 3299 | 3299 | const gpa = comp.gpa; |
| 3300 | 3300 | const module = comp.bin_file.options.module.?; |
| 3301 | 3301 | const decl = module.declPtr(decl_index); |
| 3302 | comp.bin_file.updateDeclLineNumber(module, decl) catch |err| { | |
| 3302 | comp.bin_file.updateDeclLineNumber(module, decl_index) catch |err| { | |
| 3303 | 3303 | try module.failed_decls.ensureUnusedCapacity(gpa, 1); |
| 3304 | 3304 | module.failed_decls.putAssumeCapacityNoClobber(decl_index, try Module.ErrorMsg.create( |
| 3305 | 3305 | gpa, |
src/Manifest.zig created+499| ... | ... | @@ -0,0 +1,499 @@ |
| 1 | pub const basename = "build.zig.zon"; | |
| 2 | pub const Hash = std.crypto.hash.sha2.Sha256; | |
| 3 | ||
| 4 | pub const Dependency = struct { | |
| 5 | url: []const u8, | |
| 6 | url_tok: Ast.TokenIndex, | |
| 7 | hash: ?[]const u8, | |
| 8 | hash_tok: Ast.TokenIndex, | |
| 9 | }; | |
| 10 | ||
| 11 | pub const ErrorMessage = struct { | |
| 12 | msg: []const u8, | |
| 13 | tok: Ast.TokenIndex, | |
| 14 | off: u32, | |
| 15 | }; | |
| 16 | ||
| 17 | pub const MultihashFunction = enum(u16) { | |
| 18 | identity = 0x00, | |
| 19 | sha1 = 0x11, | |
| 20 | @"sha2-256" = 0x12, | |
| 21 | @"sha2-512" = 0x13, | |
| 22 | @"sha3-512" = 0x14, | |
| 23 | @"sha3-384" = 0x15, | |
| 24 | @"sha3-256" = 0x16, | |
| 25 | @"sha3-224" = 0x17, | |
| 26 | @"sha2-384" = 0x20, | |
| 27 | @"sha2-256-trunc254-padded" = 0x1012, | |
| 28 | @"sha2-224" = 0x1013, | |
| 29 | @"sha2-512-224" = 0x1014, | |
| 30 | @"sha2-512-256" = 0x1015, | |
| 31 | @"blake2b-256" = 0xb220, | |
| 32 | _, | |
| 33 | }; | |
| 34 | ||
| 35 | pub const multihash_function: MultihashFunction = switch (Hash) { | |
| 36 | std.crypto.hash.sha2.Sha256 => .@"sha2-256", | |
| 37 | else => @compileError("unreachable"), | |
| 38 | }; | |
| 39 | comptime { | |
| 40 | // We avoid unnecessary uleb128 code in hexDigest by asserting here the | |
| 41 | // values are small enough to be contained in the one-byte encoding. | |
| 42 | assert(@enumToInt(multihash_function) < 127); | |
| 43 | assert(Hash.digest_length < 127); | |
| 44 | } | |
| 45 | pub const multihash_len = 1 + 1 + Hash.digest_length; | |
| 46 | ||
| 47 | name: []const u8, | |
| 48 | version: std.SemanticVersion, | |
| 49 | dependencies: std.StringArrayHashMapUnmanaged(Dependency), | |
| 50 | ||
| 51 | errors: []ErrorMessage, | |
| 52 | arena_state: std.heap.ArenaAllocator.State, | |
| 53 | ||
| 54 | pub const Error = Allocator.Error; | |
| 55 | ||
| 56 | pub fn parse(gpa: Allocator, ast: std.zig.Ast) Error!Manifest { | |
| 57 | const node_tags = ast.nodes.items(.tag); | |
| 58 | const node_datas = ast.nodes.items(.data); | |
| 59 | assert(node_tags[0] == .root); | |
| 60 | const main_node_index = node_datas[0].lhs; | |
| 61 | ||
| 62 | var arena_instance = std.heap.ArenaAllocator.init(gpa); | |
| 63 | errdefer arena_instance.deinit(); | |
| 64 | ||
| 65 | var p: Parse = .{ | |
| 66 | .gpa = gpa, | |
| 67 | .ast = ast, | |
| 68 | .arena = arena_instance.allocator(), | |
| 69 | .errors = .{}, | |
| 70 | ||
| 71 | .name = undefined, | |
| 72 | .version = undefined, | |
| 73 | .dependencies = .{}, | |
| 74 | .buf = .{}, | |
| 75 | }; | |
| 76 | defer p.buf.deinit(gpa); | |
| 77 | defer p.errors.deinit(gpa); | |
| 78 | defer p.dependencies.deinit(gpa); | |
| 79 | ||
| 80 | p.parseRoot(main_node_index) catch |err| switch (err) { | |
| 81 | error.ParseFailure => assert(p.errors.items.len > 0), | |
| 82 | else => |e| return e, | |
| 83 | }; | |
| 84 | ||
| 85 | return .{ | |
| 86 | .name = p.name, | |
| 87 | .version = p.version, | |
| 88 | .dependencies = try p.dependencies.clone(p.arena), | |
| 89 | .errors = try p.arena.dupe(ErrorMessage, p.errors.items), | |
| 90 | .arena_state = arena_instance.state, | |
| 91 | }; | |
| 92 | } | |
| 93 | ||
| 94 | pub fn deinit(man: *Manifest, gpa: Allocator) void { | |
| 95 | man.arena_state.promote(gpa).deinit(); | |
| 96 | man.* = undefined; | |
| 97 | } | |
| 98 | ||
| 99 | const hex_charset = "0123456789abcdef"; | |
| 100 | ||
| 101 | pub fn hex64(x: u64) [16]u8 { | |
| 102 | var result: [16]u8 = undefined; | |
| 103 | var i: usize = 0; | |
| 104 | while (i < 8) : (i += 1) { | |
| 105 | const byte = @truncate(u8, x >> @intCast(u6, 8 * i)); | |
| 106 | result[i * 2 + 0] = hex_charset[byte >> 4]; | |
| 107 | result[i * 2 + 1] = hex_charset[byte & 15]; | |
| 108 | } | |
| 109 | return result; | |
| 110 | } | |
| 111 | ||
| 112 | test hex64 { | |
| 113 | const s = "[" ++ hex64(0x12345678_abcdef00) ++ "]"; | |
| 114 | try std.testing.expectEqualStrings("[00efcdab78563412]", s); | |
| 115 | } | |
| 116 | ||
| 117 | pub fn hexDigest(digest: [Hash.digest_length]u8) [multihash_len * 2]u8 { | |
| 118 | var result: [multihash_len * 2]u8 = undefined; | |
| 119 | ||
| 120 | result[0] = hex_charset[@enumToInt(multihash_function) >> 4]; | |
| 121 | result[1] = hex_charset[@enumToInt(multihash_function) & 15]; | |
| 122 | ||
| 123 | result[2] = hex_charset[Hash.digest_length >> 4]; | |
| 124 | result[3] = hex_charset[Hash.digest_length & 15]; | |
| 125 | ||
| 126 | for (digest) |byte, i| { | |
| 127 | result[4 + i * 2] = hex_charset[byte >> 4]; | |
| 128 | result[5 + i * 2] = hex_charset[byte & 15]; | |
| 129 | } | |
| 130 | return result; | |
| 131 | } | |
| 132 | ||
| 133 | const Parse = struct { | |
| 134 | gpa: Allocator, | |
| 135 | ast: std.zig.Ast, | |
| 136 | arena: Allocator, | |
| 137 | buf: std.ArrayListUnmanaged(u8), | |
| 138 | errors: std.ArrayListUnmanaged(ErrorMessage), | |
| 139 | ||
| 140 | name: []const u8, | |
| 141 | version: std.SemanticVersion, | |
| 142 | dependencies: std.StringArrayHashMapUnmanaged(Dependency), | |
| 143 | ||
| 144 | const InnerError = error{ ParseFailure, OutOfMemory }; | |
| 145 | ||
| 146 | fn parseRoot(p: *Parse, node: Ast.Node.Index) !void { | |
| 147 | const ast = p.ast; | |
| 148 | const main_tokens = ast.nodes.items(.main_token); | |
| 149 | const main_token = main_tokens[node]; | |
| 150 | ||
| 151 | var buf: [2]Ast.Node.Index = undefined; | |
| 152 | const struct_init = ast.fullStructInit(&buf, node) orelse { | |
| 153 | return fail(p, main_token, "expected top level expression to be a struct", .{}); | |
| 154 | }; | |
| 155 | ||
| 156 | var have_name = false; | |
| 157 | var have_version = false; | |
| 158 | ||
| 159 | for (struct_init.ast.fields) |field_init| { | |
| 160 | const name_token = ast.firstToken(field_init) - 2; | |
| 161 | const field_name = try identifierTokenString(p, name_token); | |
| 162 | // We could get fancy with reflection and comptime logic here but doing | |
| 163 | // things manually provides an opportunity to do any additional verification | |
| 164 | // that is desirable on a per-field basis. | |
| 165 | if (mem.eql(u8, field_name, "dependencies")) { | |
| 166 | try parseDependencies(p, field_init); | |
| 167 | } else if (mem.eql(u8, field_name, "name")) { | |
| 168 | p.name = try parseString(p, field_init); | |
| 169 | have_name = true; | |
| 170 | } else if (mem.eql(u8, field_name, "version")) { | |
| 171 | const version_text = try parseString(p, field_init); | |
| 172 | p.version = std.SemanticVersion.parse(version_text) catch |err| v: { | |
| 173 | try appendError(p, main_tokens[field_init], "unable to parse semantic version: {s}", .{@errorName(err)}); | |
| 174 | break :v undefined; | |
| 175 | }; | |
| 176 | have_version = true; | |
| 177 | } else { | |
| 178 | // Ignore unknown fields so that we can add fields in future zig | |
| 179 | // versions without breaking older zig versions. | |
| 180 | } | |
| 181 | } | |
| 182 | ||
| 183 | if (!have_name) { | |
| 184 | try appendError(p, main_token, "missing top-level 'name' field", .{}); | |
| 185 | } | |
| 186 | ||
| 187 | if (!have_version) { | |
| 188 | try appendError(p, main_token, "missing top-level 'version' field", .{}); | |
| 189 | } | |
| 190 | } | |
| 191 | ||
| 192 | fn parseDependencies(p: *Parse, node: Ast.Node.Index) !void { | |
| 193 | const ast = p.ast; | |
| 194 | const main_tokens = ast.nodes.items(.main_token); | |
| 195 | ||
| 196 | var buf: [2]Ast.Node.Index = undefined; | |
| 197 | const struct_init = ast.fullStructInit(&buf, node) orelse { | |
| 198 | const tok = main_tokens[node]; | |
| 199 | return fail(p, tok, "expected dependencies expression to be a struct", .{}); | |
| 200 | }; | |
| 201 | ||
| 202 | for (struct_init.ast.fields) |field_init| { | |
| 203 | const name_token = ast.firstToken(field_init) - 2; | |
| 204 | const dep_name = try identifierTokenString(p, name_token); | |
| 205 | const dep = try parseDependency(p, field_init); | |
| 206 | try p.dependencies.put(p.gpa, dep_name, dep); | |
| 207 | } | |
| 208 | } | |
| 209 | ||
| 210 | fn parseDependency(p: *Parse, node: Ast.Node.Index) !Dependency { | |
| 211 | const ast = p.ast; | |
| 212 | const main_tokens = ast.nodes.items(.main_token); | |
| 213 | ||
| 214 | var buf: [2]Ast.Node.Index = undefined; | |
| 215 | const struct_init = ast.fullStructInit(&buf, node) orelse { | |
| 216 | const tok = main_tokens[node]; | |
| 217 | return fail(p, tok, "expected dependency expression to be a struct", .{}); | |
| 218 | }; | |
| 219 | ||
| 220 | var dep: Dependency = .{ | |
| 221 | .url = undefined, | |
| 222 | .url_tok = undefined, | |
| 223 | .hash = null, | |
| 224 | .hash_tok = undefined, | |
| 225 | }; | |
| 226 | var have_url = false; | |
| 227 | ||
| 228 | for (struct_init.ast.fields) |field_init| { | |
| 229 | const name_token = ast.firstToken(field_init) - 2; | |
| 230 | const field_name = try identifierTokenString(p, name_token); | |
| 231 | // We could get fancy with reflection and comptime logic here but doing | |
| 232 | // things manually provides an opportunity to do any additional verification | |
| 233 | // that is desirable on a per-field basis. | |
| 234 | if (mem.eql(u8, field_name, "url")) { | |
| 235 | dep.url = parseString(p, field_init) catch |err| switch (err) { | |
| 236 | error.ParseFailure => continue, | |
| 237 | else => |e| return e, | |
| 238 | }; | |
| 239 | dep.url_tok = main_tokens[field_init]; | |
| 240 | have_url = true; | |
| 241 | } else if (mem.eql(u8, field_name, "hash")) { | |
| 242 | dep.hash = parseHash(p, field_init) catch |err| switch (err) { | |
| 243 | error.ParseFailure => continue, | |
| 244 | else => |e| return e, | |
| 245 | }; | |
| 246 | dep.hash_tok = main_tokens[field_init]; | |
| 247 | } else { | |
| 248 | // Ignore unknown fields so that we can add fields in future zig | |
| 249 | // versions without breaking older zig versions. | |
| 250 | } | |
| 251 | } | |
| 252 | ||
| 253 | if (!have_url) { | |
| 254 | try appendError(p, main_tokens[node], "dependency is missing 'url' field", .{}); | |
| 255 | } | |
| 256 | ||
| 257 | return dep; | |
| 258 | } | |
| 259 | ||
| 260 | fn parseString(p: *Parse, node: Ast.Node.Index) ![]const u8 { | |
| 261 | const ast = p.ast; | |
| 262 | const node_tags = ast.nodes.items(.tag); | |
| 263 | const main_tokens = ast.nodes.items(.main_token); | |
| 264 | if (node_tags[node] != .string_literal) { | |
| 265 | return fail(p, main_tokens[node], "expected string literal", .{}); | |
| 266 | } | |
| 267 | const str_lit_token = main_tokens[node]; | |
| 268 | const token_bytes = ast.tokenSlice(str_lit_token); | |
| 269 | p.buf.clearRetainingCapacity(); | |
| 270 | try parseStrLit(p, str_lit_token, &p.buf, token_bytes, 0); | |
| 271 | const duped = try p.arena.dupe(u8, p.buf.items); | |
| 272 | return duped; | |
| 273 | } | |
| 274 | ||
| 275 | fn parseHash(p: *Parse, node: Ast.Node.Index) ![]const u8 { | |
| 276 | const ast = p.ast; | |
| 277 | const main_tokens = ast.nodes.items(.main_token); | |
| 278 | const tok = main_tokens[node]; | |
| 279 | const h = try parseString(p, node); | |
| 280 | ||
| 281 | if (h.len >= 2) { | |
| 282 | const their_multihash_func = std.fmt.parseInt(u8, h[0..2], 16) catch |err| { | |
| 283 | return fail(p, tok, "invalid multihash value: unable to parse hash function: {s}", .{ | |
| 284 | @errorName(err), | |
| 285 | }); | |
| 286 | }; | |
| 287 | if (@intToEnum(MultihashFunction, their_multihash_func) != multihash_function) { | |
| 288 | return fail(p, tok, "unsupported hash function: only sha2-256 is supported", .{}); | |
| 289 | } | |
| 290 | } | |
| 291 | ||
| 292 | const hex_multihash_len = 2 * Manifest.multihash_len; | |
| 293 | if (h.len != hex_multihash_len) { | |
| 294 | return fail(p, tok, "wrong hash size. expected: {d}, found: {d}", .{ | |
| 295 | hex_multihash_len, h.len, | |
| 296 | }); | |
| 297 | } | |
| 298 | ||
| 299 | return h; | |
| 300 | } | |
| 301 | ||
| 302 | /// TODO: try to DRY this with AstGen.identifierTokenString | |
| 303 | fn identifierTokenString(p: *Parse, token: Ast.TokenIndex) InnerError![]const u8 { | |
| 304 | const ast = p.ast; | |
| 305 | const token_tags = ast.tokens.items(.tag); | |
| 306 | assert(token_tags[token] == .identifier); | |
| 307 | const ident_name = ast.tokenSlice(token); | |
| 308 | if (!mem.startsWith(u8, ident_name, "@")) { | |
| 309 | return ident_name; | |
| 310 | } | |
| 311 | p.buf.clearRetainingCapacity(); | |
| 312 | try parseStrLit(p, token, &p.buf, ident_name, 1); | |
| 313 | const duped = try p.arena.dupe(u8, p.buf.items); | |
| 314 | return duped; | |
| 315 | } | |
| 316 | ||
| 317 | /// TODO: try to DRY this with AstGen.parseStrLit | |
| 318 | fn parseStrLit( | |
| 319 | p: *Parse, | |
| 320 | token: Ast.TokenIndex, | |
| 321 | buf: *std.ArrayListUnmanaged(u8), | |
| 322 | bytes: []const u8, | |
| 323 | offset: u32, | |
| 324 | ) InnerError!void { | |
| 325 | const raw_string = bytes[offset..]; | |
| 326 | var buf_managed = buf.toManaged(p.gpa); | |
| 327 | const result = std.zig.string_literal.parseWrite(buf_managed.writer(), raw_string); | |
| 328 | buf.* = buf_managed.moveToUnmanaged(); | |
| 329 | switch (try result) { | |
| 330 | .success => {}, | |
| 331 | .failure => |err| try p.appendStrLitError(err, token, bytes, offset), | |
| 332 | } | |
| 333 | } | |
| 334 | ||
| 335 | /// TODO: try to DRY this with AstGen.failWithStrLitError | |
| 336 | fn appendStrLitError( | |
| 337 | p: *Parse, | |
| 338 | err: std.zig.string_literal.Error, | |
| 339 | token: Ast.TokenIndex, | |
| 340 | bytes: []const u8, | |
| 341 | offset: u32, | |
| 342 | ) Allocator.Error!void { | |
| 343 | const raw_string = bytes[offset..]; | |
| 344 | switch (err) { | |
| 345 | .invalid_escape_character => |bad_index| { | |
| 346 | try p.appendErrorOff( | |
| 347 | token, | |
| 348 | offset + @intCast(u32, bad_index), | |
| 349 | "invalid escape character: '{c}'", | |
| 350 | .{raw_string[bad_index]}, | |
| 351 | ); | |
| 352 | }, | |
| 353 | .expected_hex_digit => |bad_index| { | |
| 354 | try p.appendErrorOff( | |
| 355 | token, | |
| 356 | offset + @intCast(u32, bad_index), | |
| 357 | "expected hex digit, found '{c}'", | |
| 358 | .{raw_string[bad_index]}, | |
| 359 | ); | |
| 360 | }, | |
| 361 | .empty_unicode_escape_sequence => |bad_index| { | |
| 362 | try p.appendErrorOff( | |
| 363 | token, | |
| 364 | offset + @intCast(u32, bad_index), | |
| 365 | "empty unicode escape sequence", | |
| 366 | .{}, | |
| 367 | ); | |
| 368 | }, | |
| 369 | .expected_hex_digit_or_rbrace => |bad_index| { | |
| 370 | try p.appendErrorOff( | |
| 371 | token, | |
| 372 | offset + @intCast(u32, bad_index), | |
| 373 | "expected hex digit or '}}', found '{c}'", | |
| 374 | .{raw_string[bad_index]}, | |
| 375 | ); | |
| 376 | }, | |
| 377 | .invalid_unicode_codepoint => |bad_index| { | |
| 378 | try p.appendErrorOff( | |
| 379 | token, | |
| 380 | offset + @intCast(u32, bad_index), | |
| 381 | "unicode escape does not correspond to a valid codepoint", | |
| 382 | .{}, | |
| 383 | ); | |
| 384 | }, | |
| 385 | .expected_lbrace => |bad_index| { | |
| 386 | try p.appendErrorOff( | |
| 387 | token, | |
| 388 | offset + @intCast(u32, bad_index), | |
| 389 | "expected '{{', found '{c}", | |
| 390 | .{raw_string[bad_index]}, | |
| 391 | ); | |
| 392 | }, | |
| 393 | .expected_rbrace => |bad_index| { | |
| 394 | try p.appendErrorOff( | |
| 395 | token, | |
| 396 | offset + @intCast(u32, bad_index), | |
| 397 | "expected '}}', found '{c}", | |
| 398 | .{raw_string[bad_index]}, | |
| 399 | ); | |
| 400 | }, | |
| 401 | .expected_single_quote => |bad_index| { | |
| 402 | try p.appendErrorOff( | |
| 403 | token, | |
| 404 | offset + @intCast(u32, bad_index), | |
| 405 | "expected single quote ('), found '{c}", | |
| 406 | .{raw_string[bad_index]}, | |
| 407 | ); | |
| 408 | }, | |
| 409 | .invalid_character => |bad_index| { | |
| 410 | try p.appendErrorOff( | |
| 411 | token, | |
| 412 | offset + @intCast(u32, bad_index), | |
| 413 | "invalid byte in string or character literal: '{c}'", | |
| 414 | .{raw_string[bad_index]}, | |
| 415 | ); | |
| 416 | }, | |
| 417 | } | |
| 418 | } | |
| 419 | ||
| 420 | fn fail( | |
| 421 | p: *Parse, | |
| 422 | tok: Ast.TokenIndex, | |
| 423 | comptime fmt: []const u8, | |
| 424 | args: anytype, | |
| 425 | ) InnerError { | |
| 426 | try appendError(p, tok, fmt, args); | |
| 427 | return error.ParseFailure; | |
| 428 | } | |
| 429 | ||
| 430 | fn appendError(p: *Parse, tok: Ast.TokenIndex, comptime fmt: []const u8, args: anytype) !void { | |
| 431 | return appendErrorOff(p, tok, 0, fmt, args); | |
| 432 | } | |
| 433 | ||
| 434 | fn appendErrorOff( | |
| 435 | p: *Parse, | |
| 436 | tok: Ast.TokenIndex, | |
| 437 | byte_offset: u32, | |
| 438 | comptime fmt: []const u8, | |
| 439 | args: anytype, | |
| 440 | ) Allocator.Error!void { | |
| 441 | try p.errors.append(p.gpa, .{ | |
| 442 | .msg = try std.fmt.allocPrint(p.arena, fmt, args), | |
| 443 | .tok = tok, | |
| 444 | .off = byte_offset, | |
| 445 | }); | |
| 446 | } | |
| 447 | }; | |
| 448 | ||
| 449 | const Manifest = @This(); | |
| 450 | const std = @import("std"); | |
| 451 | const mem = std.mem; | |
| 452 | const Allocator = std.mem.Allocator; | |
| 453 | const assert = std.debug.assert; | |
| 454 | const Ast = std.zig.Ast; | |
| 455 | const testing = std.testing; | |
| 456 | ||
| 457 | test "basic" { | |
| 458 | const gpa = testing.allocator; | |
| 459 | ||
| 460 | const example = | |
| 461 | \\.{ | |
| 462 | \\ .name = "foo", | |
| 463 | \\ .version = "3.2.1", | |
| 464 | \\ .dependencies = .{ | |
| 465 | \\ .bar = .{ | |
| 466 | \\ .url = "https://example.com/baz.tar.gz", | |
| 467 | \\ .hash = "1220f1b680b6065fcfc94fe777f22e73bcb7e2767e5f4d99d4255fe76ded69c7a35f", | |
| 468 | \\ }, | |
| 469 | \\ }, | |
| 470 | \\} | |
| 471 | ; | |
| 472 | ||
| 473 | var ast = try std.zig.Ast.parse(gpa, example, .zon); | |
| 474 | defer ast.deinit(gpa); | |
| 475 | ||
| 476 | try testing.expect(ast.errors.len == 0); | |
| 477 | ||
| 478 | var manifest = try Manifest.parse(gpa, ast); | |
| 479 | defer manifest.deinit(gpa); | |
| 480 | ||
| 481 | try testing.expectEqualStrings("foo", manifest.name); | |
| 482 | ||
| 483 | try testing.expectEqual(@as(std.SemanticVersion, .{ | |
| 484 | .major = 3, | |
| 485 | .minor = 2, | |
| 486 | .patch = 1, | |
| 487 | }), manifest.version); | |
| 488 | ||
| 489 | try testing.expect(manifest.dependencies.count() == 1); | |
| 490 | try testing.expectEqualStrings("bar", manifest.dependencies.keys()[0]); | |
| 491 | try testing.expectEqualStrings( | |
| 492 | "https://example.com/baz.tar.gz", | |
| 493 | manifest.dependencies.values()[0].url, | |
| 494 | ); | |
| 495 | try testing.expectEqualStrings( | |
| 496 | "1220f1b680b6065fcfc94fe777f22e73bcb7e2767e5f4d99d4255fe76ded69c7a35f", | |
| 497 | manifest.dependencies.values()[0].hash orelse return error.TestFailed, | |
| 498 | ); | |
| 499 | } |
src/Module.zig+17-94| ... | ... | @@ -328,8 +328,6 @@ pub const ErrorInt = u32; |
| 328 | 328 | pub const Export = struct { |
| 329 | 329 | options: std.builtin.ExportOptions, |
| 330 | 330 | src: LazySrcLoc, |
| 331 | /// Represents the position of the export, if any, in the output file. | |
| 332 | link: link.File.Export, | |
| 333 | 331 | /// The Decl that performs the export. Note that this is *not* the Decl being exported. |
| 334 | 332 | owner_decl: Decl.Index, |
| 335 | 333 | /// The Decl containing the export statement. Inline function calls |
| ... | ... | @@ -533,16 +531,8 @@ pub const Decl = struct { |
| 533 | 531 | /// What kind of a declaration is this. |
| 534 | 532 | kind: Kind, |
| 535 | 533 | |
| 536 | /// Represents the position of the code in the output file. | |
| 537 | /// This is populated regardless of semantic analysis and code generation. | |
| 538 | link: link.File.LinkBlock, | |
| 539 | ||
| 540 | /// Represents the function in the linked output file, if the `Decl` is a function. | |
| 541 | /// This is stored here and not in `Fn` because `Decl` survives across updates but | |
| 542 | /// `Fn` does not. | |
| 543 | /// TODO Look into making `Fn` a longer lived structure and moving this field there | |
| 544 | /// to save on memory usage. | |
| 545 | fn_link: link.File.LinkFn, | |
| 534 | /// TODO remove this once Wasm backend catches up | |
| 535 | fn_link: ?link.File.Wasm.FnData = null, | |
| 546 | 536 | |
| 547 | 537 | /// The shallow set of other decls whose typed_value could possibly change if this Decl's |
| 548 | 538 | /// typed_value is modified. |
| ... | ... | @@ -2067,7 +2057,7 @@ pub const File = struct { |
| 2067 | 2057 | if (file.tree_loaded) return &file.tree; |
| 2068 | 2058 | |
| 2069 | 2059 | const source = try file.getSource(gpa); |
| 2070 | file.tree = try std.zig.parse(gpa, source.bytes); | |
| 2060 | file.tree = try Ast.parse(gpa, source.bytes, .zig); | |
| 2071 | 2061 | file.tree_loaded = true; |
| 2072 | 2062 | return &file.tree; |
| 2073 | 2063 | } |
| ... | ... | @@ -3672,7 +3662,7 @@ pub fn astGenFile(mod: *Module, file: *File) !void { |
| 3672 | 3662 | file.source = source; |
| 3673 | 3663 | file.source_loaded = true; |
| 3674 | 3664 | |
| 3675 | file.tree = try std.zig.parse(gpa, source); | |
| 3665 | file.tree = try Ast.parse(gpa, source, .zig); | |
| 3676 | 3666 | defer if (!file.tree_loaded) file.tree.deinit(gpa); |
| 3677 | 3667 | |
| 3678 | 3668 | if (file.tree.errors.len != 0) { |
| ... | ... | @@ -3987,7 +3977,7 @@ pub fn populateBuiltinFile(mod: *Module) !void { |
| 3987 | 3977 | else => |e| return e, |
| 3988 | 3978 | } |
| 3989 | 3979 | |
| 3990 | file.tree = try std.zig.parse(gpa, file.source); | |
| 3980 | file.tree = try Ast.parse(gpa, file.source, .zig); | |
| 3991 | 3981 | file.tree_loaded = true; |
| 3992 | 3982 | assert(file.tree.errors.len == 0); // builtin.zig must parse |
| 3993 | 3983 | |
| ... | ... | @@ -4098,7 +4088,7 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl_index: Decl.Index) SemaError!void { |
| 4098 | 4088 | |
| 4099 | 4089 | // The exports this Decl performs will be re-discovered, so we remove them here |
| 4100 | 4090 | // prior to re-analysis. |
| 4101 | mod.deleteDeclExports(decl_index); | |
| 4091 | try mod.deleteDeclExports(decl_index); | |
| 4102 | 4092 | |
| 4103 | 4093 | // Similarly, `@setAlignStack` invocations will be re-discovered. |
| 4104 | 4094 | if (decl.getFunction()) |func| { |
| ... | ... | @@ -4585,7 +4575,6 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool { |
| 4585 | 4575 | // We don't fully codegen the decl until later, but we do need to reserve a global |
| 4586 | 4576 | // offset table index for it. This allows us to codegen decls out of dependency |
| 4587 | 4577 | // order, increasing how many computations can be done in parallel. |
| 4588 | try mod.comp.bin_file.allocateDeclIndexes(decl_index); | |
| 4589 | 4578 | try mod.comp.work_queue.writeItem(.{ .codegen_func = func }); |
| 4590 | 4579 | if (type_changed and mod.emit_h != null) { |
| 4591 | 4580 | try mod.comp.work_queue.writeItem(.{ .emit_h_decl = decl_index }); |
| ... | ... | @@ -4697,7 +4686,6 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool { |
| 4697 | 4686 | // codegen backend wants full access to the Decl Type. |
| 4698 | 4687 | try sema.resolveTypeFully(decl.ty); |
| 4699 | 4688 | |
| 4700 | try mod.comp.bin_file.allocateDeclIndexes(decl_index); | |
| 4701 | 4689 | try mod.comp.work_queue.writeItem(.{ .codegen_decl = decl_index }); |
| 4702 | 4690 | |
| 4703 | 4691 | if (type_changed and mod.emit_h != null) { |
| ... | ... | @@ -5185,20 +5173,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) Allocator.Err |
| 5185 | 5173 | decl.zir_decl_index = @intCast(u32, decl_sub_index); |
| 5186 | 5174 | if (decl.getFunction()) |_| { |
| 5187 | 5175 | switch (comp.bin_file.tag) { |
| 5188 | .coff => { | |
| 5189 | // TODO Implement for COFF | |
| 5190 | }, | |
| 5191 | .elf => if (decl.fn_link.elf.len != 0) { | |
| 5192 | // TODO Look into detecting when this would be unnecessary by storing enough state | |
| 5193 | // in `Decl` to notice that the line number did not change. | |
| 5194 | comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl_index }); | |
| 5195 | }, | |
| 5196 | .macho => if (decl.fn_link.macho.len != 0) { | |
| 5197 | // TODO Look into detecting when this would be unnecessary by storing enough state | |
| 5198 | // in `Decl` to notice that the line number did not change. | |
| 5199 | comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl_index }); | |
| 5200 | }, | |
| 5201 | .plan9 => { | |
| 5176 | .coff, .elf, .macho, .plan9 => { | |
| 5202 | 5177 | // TODO Look into detecting when this would be unnecessary by storing enough state |
| 5203 | 5178 | // in `Decl` to notice that the line number did not change. |
| 5204 | 5179 | comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl_index }); |
| ... | ... | @@ -5267,33 +5242,15 @@ pub fn clearDecl( |
| 5267 | 5242 | assert(emit_h.decl_table.swapRemove(decl_index)); |
| 5268 | 5243 | } |
| 5269 | 5244 | _ = mod.compile_log_decls.swapRemove(decl_index); |
| 5270 | mod.deleteDeclExports(decl_index); | |
| 5245 | try mod.deleteDeclExports(decl_index); | |
| 5271 | 5246 | |
| 5272 | 5247 | if (decl.has_tv) { |
| 5273 | 5248 | if (decl.ty.isFnOrHasRuntimeBits()) { |
| 5274 | 5249 | mod.comp.bin_file.freeDecl(decl_index); |
| 5275 | 5250 | |
| 5276 | // TODO instead of a union, put this memory trailing Decl objects, | |
| 5277 | // and allow it to be variably sized. | |
| 5278 | decl.link = switch (mod.comp.bin_file.tag) { | |
| 5279 | .coff => .{ .coff = link.File.Coff.Atom.empty }, | |
| 5280 | .elf => .{ .elf = link.File.Elf.TextBlock.empty }, | |
| 5281 | .macho => .{ .macho = link.File.MachO.Atom.empty }, | |
| 5282 | .plan9 => .{ .plan9 = link.File.Plan9.DeclBlock.empty }, | |
| 5283 | .c => .{ .c = {} }, | |
| 5284 | .wasm => .{ .wasm = link.File.Wasm.DeclBlock.empty }, | |
| 5285 | .spirv => .{ .spirv = {} }, | |
| 5286 | .nvptx => .{ .nvptx = {} }, | |
| 5287 | }; | |
| 5288 | 5251 | decl.fn_link = switch (mod.comp.bin_file.tag) { |
| 5289 | .coff => .{ .coff = {} }, | |
| 5290 | .elf => .{ .elf = link.File.Dwarf.SrcFn.empty }, | |
| 5291 | .macho => .{ .macho = link.File.Dwarf.SrcFn.empty }, | |
| 5292 | .plan9 => .{ .plan9 = {} }, | |
| 5293 | .c => .{ .c = {} }, | |
| 5294 | .wasm => .{ .wasm = link.File.Wasm.FnData.empty }, | |
| 5295 | .spirv => .{ .spirv = .{} }, | |
| 5296 | .nvptx => .{ .nvptx = {} }, | |
| 5252 | .wasm => link.File.Wasm.FnData.empty, | |
| 5253 | else => null, | |
| 5297 | 5254 | }; |
| 5298 | 5255 | } |
| 5299 | 5256 | if (decl.getInnerNamespace()) |namespace| { |
| ... | ... | @@ -5315,23 +5272,6 @@ pub fn deleteUnusedDecl(mod: *Module, decl_index: Decl.Index) void { |
| 5315 | 5272 | const decl = mod.declPtr(decl_index); |
| 5316 | 5273 | log.debug("deleteUnusedDecl {d} ({s})", .{ decl_index, decl.name }); |
| 5317 | 5274 | |
| 5318 | // TODO: remove `allocateDeclIndexes` and make the API that the linker backends | |
| 5319 | // are required to notice the first time `updateDecl` happens and keep track | |
| 5320 | // of it themselves. However they can rely on getting a `freeDecl` call if any | |
| 5321 | // `updateDecl` or `updateFunc` calls happen. This will allow us to avoid any call | |
| 5322 | // into the linker backend here, since the linker backend will never have been told | |
| 5323 | // about the Decl in the first place. | |
| 5324 | // Until then, we did call `allocateDeclIndexes` on this anonymous Decl and so we | |
| 5325 | // must call `freeDecl` in the linker backend now. | |
| 5326 | switch (mod.comp.bin_file.tag) { | |
| 5327 | .c => {}, // this linker backend has already migrated to the new API | |
| 5328 | else => if (decl.has_tv) { | |
| 5329 | if (decl.ty.isFnOrHasRuntimeBits()) { | |
| 5330 | mod.comp.bin_file.freeDecl(decl_index); | |
| 5331 | } | |
| 5332 | }, | |
| 5333 | } | |
| 5334 | ||
| 5335 | 5275 | assert(!mod.declIsRoot(decl_index)); |
| 5336 | 5276 | assert(decl.src_namespace.anon_decls.swapRemove(decl_index)); |
| 5337 | 5277 | |
| ... | ... | @@ -5377,7 +5317,7 @@ pub fn abortAnonDecl(mod: *Module, decl_index: Decl.Index) void { |
| 5377 | 5317 | |
| 5378 | 5318 | /// Delete all the Export objects that are caused by this Decl. Re-analysis of |
| 5379 | 5319 | /// this Decl will cause them to be re-created (or not). |
| 5380 | fn deleteDeclExports(mod: *Module, decl_index: Decl.Index) void { | |
| 5320 | fn deleteDeclExports(mod: *Module, decl_index: Decl.Index) Allocator.Error!void { | |
| 5381 | 5321 | var export_owners = (mod.export_owners.fetchSwapRemove(decl_index) orelse return).value; |
| 5382 | 5322 | |
| 5383 | 5323 | for (export_owners.items) |exp| { |
| ... | ... | @@ -5400,16 +5340,16 @@ fn deleteDeclExports(mod: *Module, decl_index: Decl.Index) void { |
| 5400 | 5340 | } |
| 5401 | 5341 | } |
| 5402 | 5342 | if (mod.comp.bin_file.cast(link.File.Elf)) |elf| { |
| 5403 | elf.deleteExport(exp.link.elf); | |
| 5343 | elf.deleteDeclExport(decl_index, exp.options.name); | |
| 5404 | 5344 | } |
| 5405 | 5345 | if (mod.comp.bin_file.cast(link.File.MachO)) |macho| { |
| 5406 | macho.deleteExport(exp.link.macho); | |
| 5346 | try macho.deleteDeclExport(decl_index, exp.options.name); | |
| 5407 | 5347 | } |
| 5408 | 5348 | if (mod.comp.bin_file.cast(link.File.Wasm)) |wasm| { |
| 5409 | wasm.deleteExport(exp.link.wasm); | |
| 5349 | wasm.deleteDeclExport(decl_index); | |
| 5410 | 5350 | } |
| 5411 | 5351 | if (mod.comp.bin_file.cast(link.File.Coff)) |coff| { |
| 5412 | coff.deleteExport(exp.link.coff); | |
| 5352 | coff.deleteDeclExport(decl_index, exp.options.name); | |
| 5413 | 5353 | } |
| 5414 | 5354 | if (mod.failed_exports.fetchSwapRemove(exp)) |failed_kv| { |
| 5415 | 5355 | failed_kv.value.destroy(mod.gpa); |
| ... | ... | @@ -5712,25 +5652,9 @@ pub fn allocateNewDecl( |
| 5712 | 5652 | .deletion_flag = false, |
| 5713 | 5653 | .zir_decl_index = 0, |
| 5714 | 5654 | .src_scope = src_scope, |
| 5715 | .link = switch (mod.comp.bin_file.tag) { | |
| 5716 | .coff => .{ .coff = link.File.Coff.Atom.empty }, | |
| 5717 | .elf => .{ .elf = link.File.Elf.TextBlock.empty }, | |
| 5718 | .macho => .{ .macho = link.File.MachO.Atom.empty }, | |
| 5719 | .plan9 => .{ .plan9 = link.File.Plan9.DeclBlock.empty }, | |
| 5720 | .c => .{ .c = {} }, | |
| 5721 | .wasm => .{ .wasm = link.File.Wasm.DeclBlock.empty }, | |
| 5722 | .spirv => .{ .spirv = {} }, | |
| 5723 | .nvptx => .{ .nvptx = {} }, | |
| 5724 | }, | |
| 5725 | 5655 | .fn_link = switch (mod.comp.bin_file.tag) { |
| 5726 | .coff => .{ .coff = {} }, | |
| 5727 | .elf => .{ .elf = link.File.Dwarf.SrcFn.empty }, | |
| 5728 | .macho => .{ .macho = link.File.Dwarf.SrcFn.empty }, | |
| 5729 | .plan9 => .{ .plan9 = {} }, | |
| 5730 | .c => .{ .c = {} }, | |
| 5731 | .wasm => .{ .wasm = link.File.Wasm.FnData.empty }, | |
| 5732 | .spirv => .{ .spirv = .{} }, | |
| 5733 | .nvptx => .{ .nvptx = {} }, | |
| 5656 | .wasm => link.File.Wasm.FnData.empty, | |
| 5657 | else => null, | |
| 5734 | 5658 | }, |
| 5735 | 5659 | .generation = 0, |
| 5736 | 5660 | .is_pub = false, |
| ... | ... | @@ -5816,7 +5740,6 @@ pub fn initNewAnonDecl( |
| 5816 | 5740 | // the Decl will be garbage collected by the `codegen_decl` task instead of sent |
| 5817 | 5741 | // to the linker. |
| 5818 | 5742 | if (typed_value.ty.isFnOrHasRuntimeBits()) { |
| 5819 | try mod.comp.bin_file.allocateDeclIndexes(new_decl_index); | |
| 5820 | 5743 | try mod.comp.anon_work_queue.writeItem(.{ .codegen_decl = new_decl_index }); |
| 5821 | 5744 | } |
| 5822 | 5745 | } |
src/Package.zig+157-152| ... | ... | @@ -1,12 +1,13 @@ |
| 1 | 1 | const Package = @This(); |
| 2 | 2 | |
| 3 | const builtin = @import("builtin"); | |
| 3 | 4 | const std = @import("std"); |
| 4 | 5 | const fs = std.fs; |
| 5 | 6 | const mem = std.mem; |
| 6 | 7 | const Allocator = mem.Allocator; |
| 7 | 8 | const assert = std.debug.assert; |
| 8 | const Hash = std.crypto.hash.sha2.Sha256; | |
| 9 | 9 | const log = std.log.scoped(.package); |
| 10 | const main = @import("main.zig"); | |
| 10 | 11 | |
| 11 | 12 | const Compilation = @import("Compilation.zig"); |
| 12 | 13 | const Module = @import("Module.zig"); |
| ... | ... | @@ -14,6 +15,7 @@ const ThreadPool = @import("ThreadPool.zig"); |
| 14 | 15 | const WaitGroup = @import("WaitGroup.zig"); |
| 15 | 16 | const Cache = @import("Cache.zig"); |
| 16 | 17 | const build_options = @import("build_options"); |
| 18 | const Manifest = @import("Manifest.zig"); | |
| 17 | 19 | |
| 18 | 20 | pub const Table = std.StringHashMapUnmanaged(*Package); |
| 19 | 21 | |
| ... | ... | @@ -140,10 +142,10 @@ pub fn addAndAdopt(parent: *Package, gpa: Allocator, child: *Package) !void { |
| 140 | 142 | } |
| 141 | 143 | |
| 142 | 144 | pub const build_zig_basename = "build.zig"; |
| 143 | pub const ini_basename = build_zig_basename ++ ".ini"; | |
| 144 | 145 | |
| 145 | 146 | pub fn fetchAndAddDependencies( |
| 146 | 147 | pkg: *Package, |
| 148 | arena: Allocator, | |
| 147 | 149 | thread_pool: *ThreadPool, |
| 148 | 150 | http_client: *std.http.Client, |
| 149 | 151 | directory: Compilation.Directory, |
| ... | ... | @@ -152,89 +154,77 @@ pub fn fetchAndAddDependencies( |
| 152 | 154 | dependencies_source: *std.ArrayList(u8), |
| 153 | 155 | build_roots_source: *std.ArrayList(u8), |
| 154 | 156 | name_prefix: []const u8, |
| 157 | color: main.Color, | |
| 155 | 158 | ) !void { |
| 156 | 159 | const max_bytes = 10 * 1024 * 1024; |
| 157 | 160 | const gpa = thread_pool.allocator; |
| 158 | const build_zig_ini = directory.handle.readFileAlloc(gpa, ini_basename, max_bytes) catch |err| switch (err) { | |
| 161 | const build_zig_zon_bytes = directory.handle.readFileAllocOptions( | |
| 162 | arena, | |
| 163 | Manifest.basename, | |
| 164 | max_bytes, | |
| 165 | null, | |
| 166 | 1, | |
| 167 | 0, | |
| 168 | ) catch |err| switch (err) { | |
| 159 | 169 | error.FileNotFound => { |
| 160 | 170 | // Handle the same as no dependencies. |
| 161 | 171 | return; |
| 162 | 172 | }, |
| 163 | 173 | else => |e| return e, |
| 164 | 174 | }; |
| 165 | defer gpa.free(build_zig_ini); | |
| 166 | 175 | |
| 167 | const ini: std.Ini = .{ .bytes = build_zig_ini }; | |
| 168 | var any_error = false; | |
| 169 | var it = ini.iterateSection("\n[dependency]\n"); | |
| 170 | while (it.next()) |dep| { | |
| 171 | var line_it = mem.split(u8, dep, "\n"); | |
| 172 | var opt_name: ?[]const u8 = null; | |
| 173 | var opt_url: ?[]const u8 = null; | |
| 174 | var expected_hash: ?[]const u8 = null; | |
| 175 | while (line_it.next()) |kv| { | |
| 176 | const eq_pos = mem.indexOfScalar(u8, kv, '=') orelse continue; | |
| 177 | const key = kv[0..eq_pos]; | |
| 178 | const value = kv[eq_pos + 1 ..]; | |
| 179 | if (mem.eql(u8, key, "name")) { | |
| 180 | opt_name = value; | |
| 181 | } else if (mem.eql(u8, key, "url")) { | |
| 182 | opt_url = value; | |
| 183 | } else if (mem.eql(u8, key, "hash")) { | |
| 184 | expected_hash = value; | |
| 185 | } else { | |
| 186 | const loc = std.zig.findLineColumn(ini.bytes, @ptrToInt(key.ptr) - @ptrToInt(ini.bytes.ptr)); | |
| 187 | std.log.warn("{s}/{s}:{d}:{d} unrecognized key: '{s}'", .{ | |
| 188 | directory.path orelse ".", | |
| 189 | "build.zig.ini", | |
| 190 | loc.line, | |
| 191 | loc.column, | |
| 192 | key, | |
| 193 | }); | |
| 194 | } | |
| 195 | } | |
| 176 | var ast = try std.zig.Ast.parse(gpa, build_zig_zon_bytes, .zon); | |
| 177 | defer ast.deinit(gpa); | |
| 196 | 178 | |
| 197 | const name = opt_name orelse { | |
| 198 | const loc = std.zig.findLineColumn(ini.bytes, @ptrToInt(dep.ptr) - @ptrToInt(ini.bytes.ptr)); | |
| 199 | std.log.err("{s}/{s}:{d}:{d} missing key: 'name'", .{ | |
| 200 | directory.path orelse ".", | |
| 201 | "build.zig.ini", | |
| 202 | loc.line, | |
| 203 | loc.column, | |
| 204 | }); | |
| 205 | any_error = true; | |
| 206 | continue; | |
| 207 | }; | |
| 179 | if (ast.errors.len > 0) { | |
| 180 | const file_path = try directory.join(arena, &.{Manifest.basename}); | |
| 181 | try main.printErrsMsgToStdErr(gpa, arena, ast, file_path, color); | |
| 182 | return error.PackageFetchFailed; | |
| 183 | } | |
| 208 | 184 | |
| 209 | const url = opt_url orelse { | |
| 210 | const loc = std.zig.findLineColumn(ini.bytes, @ptrToInt(dep.ptr) - @ptrToInt(ini.bytes.ptr)); | |
| 211 | std.log.err("{s}/{s}:{d}:{d} missing key: 'name'", .{ | |
| 212 | directory.path orelse ".", | |
| 213 | "build.zig.ini", | |
| 214 | loc.line, | |
| 215 | loc.column, | |
| 216 | }); | |
| 217 | any_error = true; | |
| 218 | continue; | |
| 185 | var manifest = try Manifest.parse(gpa, ast); | |
| 186 | defer manifest.deinit(gpa); | |
| 187 | ||
| 188 | if (manifest.errors.len > 0) { | |
| 189 | const ttyconf: std.debug.TTY.Config = switch (color) { | |
| 190 | .auto => std.debug.detectTTYConfig(std.io.getStdErr()), | |
| 191 | .on => .escape_codes, | |
| 192 | .off => .no_color, | |
| 219 | 193 | }; |
| 194 | const file_path = try directory.join(arena, &.{Manifest.basename}); | |
| 195 | for (manifest.errors) |msg| { | |
| 196 | Report.renderErrorMessage(ast, file_path, ttyconf, msg, &.{}); | |
| 197 | } | |
| 198 | return error.PackageFetchFailed; | |
| 199 | } | |
| 220 | 200 | |
| 221 | const sub_prefix = try std.fmt.allocPrint(gpa, "{s}{s}.", .{ name_prefix, name }); | |
| 222 | defer gpa.free(sub_prefix); | |
| 201 | const report: Report = .{ | |
| 202 | .ast = &ast, | |
| 203 | .directory = directory, | |
| 204 | .color = color, | |
| 205 | .arena = arena, | |
| 206 | }; | |
| 207 | ||
| 208 | var any_error = false; | |
| 209 | const deps_list = manifest.dependencies.values(); | |
| 210 | for (manifest.dependencies.keys()) |name, i| { | |
| 211 | const dep = deps_list[i]; | |
| 212 | ||
| 213 | const sub_prefix = try std.fmt.allocPrint(arena, "{s}{s}.", .{ name_prefix, name }); | |
| 223 | 214 | const fqn = sub_prefix[0 .. sub_prefix.len - 1]; |
| 224 | 215 | |
| 225 | 216 | const sub_pkg = try fetchAndUnpack( |
| 226 | 217 | thread_pool, |
| 227 | 218 | http_client, |
| 228 | 219 | global_cache_directory, |
| 229 | url, | |
| 230 | expected_hash, | |
| 231 | ini, | |
| 232 | directory, | |
| 220 | dep, | |
| 221 | report, | |
| 233 | 222 | build_roots_source, |
| 234 | 223 | fqn, |
| 235 | 224 | ); |
| 236 | 225 | |
| 237 | 226 | try pkg.fetchAndAddDependencies( |
| 227 | arena, | |
| 238 | 228 | thread_pool, |
| 239 | 229 | http_client, |
| 240 | 230 | sub_pkg.root_src_directory, |
| ... | ... | @@ -243,6 +233,7 @@ pub fn fetchAndAddDependencies( |
| 243 | 233 | dependencies_source, |
| 244 | 234 | build_roots_source, |
| 245 | 235 | sub_prefix, |
| 236 | color, | |
| 246 | 237 | ); |
| 247 | 238 | |
| 248 | 239 | try addAndAdopt(pkg, gpa, sub_pkg); |
| ... | ... | @@ -252,7 +243,7 @@ pub fn fetchAndAddDependencies( |
| 252 | 243 | }); |
| 253 | 244 | } |
| 254 | 245 | |
| 255 | if (any_error) return error.InvalidBuildZigIniFile; | |
| 246 | if (any_error) return error.InvalidBuildManifestFile; | |
| 256 | 247 | } |
| 257 | 248 | |
| 258 | 249 | pub fn createFilePkg( |
| ... | ... | @@ -263,7 +254,7 @@ pub fn createFilePkg( |
| 263 | 254 | contents: []const u8, |
| 264 | 255 | ) !*Package { |
| 265 | 256 | const rand_int = std.crypto.random.int(u64); |
| 266 | const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ hex64(rand_int); | |
| 257 | const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ Manifest.hex64(rand_int); | |
| 267 | 258 | { |
| 268 | 259 | var tmp_dir = try cache_directory.handle.makeOpenPath(tmp_dir_sub_path, .{}); |
| 269 | 260 | defer tmp_dir.close(); |
| ... | ... | @@ -281,14 +272,73 @@ pub fn createFilePkg( |
| 281 | 272 | return createWithDir(gpa, name, cache_directory, o_dir_sub_path, basename); |
| 282 | 273 | } |
| 283 | 274 | |
| 275 | const Report = struct { | |
| 276 | ast: *const std.zig.Ast, | |
| 277 | directory: Compilation.Directory, | |
| 278 | color: main.Color, | |
| 279 | arena: Allocator, | |
| 280 | ||
| 281 | fn fail( | |
| 282 | report: Report, | |
| 283 | tok: std.zig.Ast.TokenIndex, | |
| 284 | comptime fmt_string: []const u8, | |
| 285 | fmt_args: anytype, | |
| 286 | ) error{ PackageFetchFailed, OutOfMemory } { | |
| 287 | return failWithNotes(report, &.{}, tok, fmt_string, fmt_args); | |
| 288 | } | |
| 289 | ||
| 290 | fn failWithNotes( | |
| 291 | report: Report, | |
| 292 | notes: []const Compilation.AllErrors.Message, | |
| 293 | tok: std.zig.Ast.TokenIndex, | |
| 294 | comptime fmt_string: []const u8, | |
| 295 | fmt_args: anytype, | |
| 296 | ) error{ PackageFetchFailed, OutOfMemory } { | |
| 297 | const ttyconf: std.debug.TTY.Config = switch (report.color) { | |
| 298 | .auto => std.debug.detectTTYConfig(std.io.getStdErr()), | |
| 299 | .on => .escape_codes, | |
| 300 | .off => .no_color, | |
| 301 | }; | |
| 302 | const file_path = try report.directory.join(report.arena, &.{Manifest.basename}); | |
| 303 | renderErrorMessage(report.ast.*, file_path, ttyconf, .{ | |
| 304 | .tok = tok, | |
| 305 | .off = 0, | |
| 306 | .msg = try std.fmt.allocPrint(report.arena, fmt_string, fmt_args), | |
| 307 | }, notes); | |
| 308 | return error.PackageFetchFailed; | |
| 309 | } | |
| 310 | ||
| 311 | fn renderErrorMessage( | |
| 312 | ast: std.zig.Ast, | |
| 313 | file_path: []const u8, | |
| 314 | ttyconf: std.debug.TTY.Config, | |
| 315 | msg: Manifest.ErrorMessage, | |
| 316 | notes: []const Compilation.AllErrors.Message, | |
| 317 | ) void { | |
| 318 | const token_starts = ast.tokens.items(.start); | |
| 319 | const start_loc = ast.tokenLocation(0, msg.tok); | |
| 320 | Compilation.AllErrors.Message.renderToStdErr(.{ .src = .{ | |
| 321 | .msg = msg.msg, | |
| 322 | .src_path = file_path, | |
| 323 | .line = @intCast(u32, start_loc.line), | |
| 324 | .column = @intCast(u32, start_loc.column), | |
| 325 | .span = .{ | |
| 326 | .start = token_starts[msg.tok], | |
| 327 | .end = @intCast(u32, token_starts[msg.tok] + ast.tokenSlice(msg.tok).len), | |
| 328 | .main = token_starts[msg.tok] + msg.off, | |
| 329 | }, | |
| 330 | .source_line = ast.source[start_loc.line_start..start_loc.line_end], | |
| 331 | .notes = notes, | |
| 332 | } }, ttyconf); | |
| 333 | } | |
| 334 | }; | |
| 335 | ||
| 284 | 336 | fn fetchAndUnpack( |
| 285 | 337 | thread_pool: *ThreadPool, |
| 286 | 338 | http_client: *std.http.Client, |
| 287 | 339 | global_cache_directory: Compilation.Directory, |
| 288 | url: []const u8, | |
| 289 | expected_hash: ?[]const u8, | |
| 290 | ini: std.Ini, | |
| 291 | comp_directory: Compilation.Directory, | |
| 340 | dep: Manifest.Dependency, | |
| 341 | report: Report, | |
| 292 | 342 | build_roots_source: *std.ArrayList(u8), |
| 293 | 343 | fqn: []const u8, |
| 294 | 344 | ) !*Package { |
| ... | ... | @@ -297,17 +347,9 @@ fn fetchAndUnpack( |
| 297 | 347 | |
| 298 | 348 | // Check if the expected_hash is already present in the global package |
| 299 | 349 | // cache, and thereby avoid both fetching and unpacking. |
| 300 | if (expected_hash) |h| cached: { | |
| 301 | if (h.len != 2 * Hash.digest_length) { | |
| 302 | return reportError( | |
| 303 | ini, | |
| 304 | comp_directory, | |
| 305 | h.ptr, | |
| 306 | "wrong hash size. expected: {d}, found: {d}", | |
| 307 | .{ Hash.digest_length, h.len }, | |
| 308 | ); | |
| 309 | } | |
| 310 | const hex_digest = h[0 .. 2 * Hash.digest_length]; | |
| 350 | if (dep.hash) |h| cached: { | |
| 351 | const hex_multihash_len = 2 * Manifest.multihash_len; | |
| 352 | const hex_digest = h[0..hex_multihash_len]; | |
| 311 | 353 | const pkg_dir_sub_path = "p" ++ s ++ hex_digest; |
| 312 | 354 | var pkg_dir = global_cache_directory.handle.openDir(pkg_dir_sub_path, .{}) catch |err| switch (err) { |
| 313 | 355 | error.FileNotFound => break :cached, |
| ... | ... | @@ -344,10 +386,10 @@ fn fetchAndUnpack( |
| 344 | 386 | return ptr; |
| 345 | 387 | } |
| 346 | 388 | |
| 347 | const uri = try std.Uri.parse(url); | |
| 389 | const uri = try std.Uri.parse(dep.url); | |
| 348 | 390 | |
| 349 | 391 | const rand_int = std.crypto.random.int(u64); |
| 350 | const tmp_dir_sub_path = "tmp" ++ s ++ hex64(rand_int); | |
| 392 | const tmp_dir_sub_path = "tmp" ++ s ++ Manifest.hex64(rand_int); | |
| 351 | 393 | |
| 352 | 394 | const actual_hash = a: { |
| 353 | 395 | var tmp_directory: Compilation.Directory = d: { |
| ... | ... | @@ -376,13 +418,9 @@ fn fetchAndUnpack( |
| 376 | 418 | // by default, so the same logic applies for buffering the reader as for gzip. |
| 377 | 419 | try unpackTarball(gpa, &req, tmp_directory.handle, std.compress.xz); |
| 378 | 420 | } else { |
| 379 | return reportError( | |
| 380 | ini, | |
| 381 | comp_directory, | |
| 382 | uri.path.ptr, | |
| 383 | "unknown file extension for path '{s}'", | |
| 384 | .{uri.path}, | |
| 385 | ); | |
| 421 | return report.fail(dep.url_tok, "unknown file extension for path '{s}'", .{ | |
| 422 | uri.path, | |
| 423 | }); | |
| 386 | 424 | } |
| 387 | 425 | |
| 388 | 426 | // TODO: delete files not included in the package prior to computing the package hash. |
| ... | ... | @@ -393,28 +431,21 @@ fn fetchAndUnpack( |
| 393 | 431 | break :a try computePackageHash(thread_pool, .{ .dir = tmp_directory.handle }); |
| 394 | 432 | }; |
| 395 | 433 | |
| 396 | const pkg_dir_sub_path = "p" ++ s ++ hexDigest(actual_hash); | |
| 434 | const pkg_dir_sub_path = "p" ++ s ++ Manifest.hexDigest(actual_hash); | |
| 397 | 435 | try renameTmpIntoCache(global_cache_directory.handle, tmp_dir_sub_path, pkg_dir_sub_path); |
| 398 | 436 | |
| 399 | if (expected_hash) |h| { | |
| 400 | const actual_hex = hexDigest(actual_hash); | |
| 437 | const actual_hex = Manifest.hexDigest(actual_hash); | |
| 438 | if (dep.hash) |h| { | |
| 401 | 439 | if (!mem.eql(u8, h, &actual_hex)) { |
| 402 | return reportError( | |
| 403 | ini, | |
| 404 | comp_directory, | |
| 405 | h.ptr, | |
| 406 | "hash mismatch: expected: {s}, found: {s}", | |
| 407 | .{ h, actual_hex }, | |
| 408 | ); | |
| 440 | return report.fail(dep.hash_tok, "hash mismatch: expected: {s}, found: {s}", .{ | |
| 441 | h, actual_hex, | |
| 442 | }); | |
| 409 | 443 | } |
| 410 | 444 | } else { |
| 411 | return reportError( | |
| 412 | ini, | |
| 413 | comp_directory, | |
| 414 | url.ptr, | |
| 415 | "url field is missing corresponding hash field: hash={s}", | |
| 416 | .{std.fmt.fmtSliceHexLower(&actual_hash)}, | |
| 417 | ); | |
| 445 | const notes: [1]Compilation.AllErrors.Message = .{.{ .plain = .{ | |
| 446 | .msg = try std.fmt.allocPrint(report.arena, "expected .hash = \"{s}\",", .{&actual_hex}), | |
| 447 | } }}; | |
| 448 | return report.failWithNotes(&notes, dep.url_tok, "url field is missing corresponding hash field", .{}); | |
| 418 | 449 | } |
| 419 | 450 | |
| 420 | 451 | const build_root = try global_cache_directory.join(gpa, &.{pkg_dir_sub_path}); |
| ... | ... | @@ -440,35 +471,21 @@ fn unpackTarball( |
| 440 | 471 | |
| 441 | 472 | try std.tar.pipeToFileSystem(out_dir, decompress.reader(), .{ |
| 442 | 473 | .strip_components = 1, |
| 474 | // TODO: we would like to set this to executable_bit_only, but two | |
| 475 | // things need to happen before that: | |
| 476 | // 1. the tar implementation needs to support it | |
| 477 | // 2. the hashing algorithm here needs to support detecting the is_executable | |
| 478 | // bit on Windows from the ACLs (see the isExecutable function). | |
| 479 | .mode_mode = .ignore, | |
| 443 | 480 | }); |
| 444 | 481 | } |
| 445 | 482 | |
| 446 | fn reportError( | |
| 447 | ini: std.Ini, | |
| 448 | comp_directory: Compilation.Directory, | |
| 449 | src_ptr: [*]const u8, | |
| 450 | comptime fmt_string: []const u8, | |
| 451 | fmt_args: anytype, | |
| 452 | ) error{PackageFetchFailed} { | |
| 453 | const loc = std.zig.findLineColumn(ini.bytes, @ptrToInt(src_ptr) - @ptrToInt(ini.bytes.ptr)); | |
| 454 | if (comp_directory.path) |p| { | |
| 455 | std.debug.print("{s}{c}{s}:{d}:{d}: error: " ++ fmt_string ++ "\n", .{ | |
| 456 | p, fs.path.sep, ini_basename, loc.line + 1, loc.column + 1, | |
| 457 | } ++ fmt_args); | |
| 458 | } else { | |
| 459 | std.debug.print("{s}:{d}:{d}: error: " ++ fmt_string ++ "\n", .{ | |
| 460 | ini_basename, loc.line + 1, loc.column + 1, | |
| 461 | } ++ fmt_args); | |
| 462 | } | |
| 463 | return error.PackageFetchFailed; | |
| 464 | } | |
| 465 | ||
| 466 | 483 | const HashedFile = struct { |
| 467 | 484 | path: []const u8, |
| 468 | hash: [Hash.digest_length]u8, | |
| 485 | hash: [Manifest.Hash.digest_length]u8, | |
| 469 | 486 | failure: Error!void, |
| 470 | 487 | |
| 471 | const Error = fs.File.OpenError || fs.File.ReadError; | |
| 488 | const Error = fs.File.OpenError || fs.File.ReadError || fs.File.StatError; | |
| 472 | 489 | |
| 473 | 490 | fn lessThan(context: void, lhs: *const HashedFile, rhs: *const HashedFile) bool { |
| 474 | 491 | _ = context; |
| ... | ... | @@ -479,7 +496,7 @@ const HashedFile = struct { |
| 479 | 496 | fn computePackageHash( |
| 480 | 497 | thread_pool: *ThreadPool, |
| 481 | 498 | pkg_dir: fs.IterableDir, |
| 482 | ) ![Hash.digest_length]u8 { | |
| 499 | ) ![Manifest.Hash.digest_length]u8 { | |
| 483 | 500 | const gpa = thread_pool.allocator; |
| 484 | 501 | |
| 485 | 502 | // We'll use an arena allocator for the path name strings since they all |
| ... | ... | @@ -522,7 +539,7 @@ fn computePackageHash( |
| 522 | 539 | |
| 523 | 540 | std.sort.sort(*HashedFile, all_files.items, {}, HashedFile.lessThan); |
| 524 | 541 | |
| 525 | var hasher = Hash.init(.{}); | |
| 542 | var hasher = Manifest.Hash.init(.{}); | |
| 526 | 543 | var any_failures = false; |
| 527 | 544 | for (all_files.items) |hashed_file| { |
| 528 | 545 | hashed_file.failure catch |err| { |
| ... | ... | @@ -543,7 +560,9 @@ fn workerHashFile(dir: fs.Dir, hashed_file: *HashedFile, wg: *WaitGroup) void { |
| 543 | 560 | fn hashFileFallible(dir: fs.Dir, hashed_file: *HashedFile) HashedFile.Error!void { |
| 544 | 561 | var buf: [8000]u8 = undefined; |
| 545 | 562 | var file = try dir.openFile(hashed_file.path, .{}); |
| 546 | var hasher = Hash.init(.{}); | |
| 563 | var hasher = Manifest.Hash.init(.{}); | |
| 564 | hasher.update(hashed_file.path); | |
| 565 | hasher.update(&.{ 0, @boolToInt(try isExecutable(file)) }); | |
| 547 | 566 | while (true) { |
| 548 | 567 | const bytes_read = try file.read(&buf); |
| 549 | 568 | if (bytes_read == 0) break; |
| ... | ... | @@ -552,31 +571,17 @@ fn hashFileFallible(dir: fs.Dir, hashed_file: *HashedFile) HashedFile.Error!void |
| 552 | 571 | hasher.final(&hashed_file.hash); |
| 553 | 572 | } |
| 554 | 573 | |
| 555 | const hex_charset = "0123456789abcdef"; | |
| 556 | ||
| 557 | fn hex64(x: u64) [16]u8 { | |
| 558 | var result: [16]u8 = undefined; | |
| 559 | var i: usize = 0; | |
| 560 | while (i < 8) : (i += 1) { | |
| 561 | const byte = @truncate(u8, x >> @intCast(u6, 8 * i)); | |
| 562 | result[i * 2 + 0] = hex_charset[byte >> 4]; | |
| 563 | result[i * 2 + 1] = hex_charset[byte & 15]; | |
| 564 | } | |
| 565 | return result; | |
| 566 | } | |
| 567 | ||
| 568 | test hex64 { | |
| 569 | const s = "[" ++ hex64(0x12345678_abcdef00) ++ "]"; | |
| 570 | try std.testing.expectEqualStrings("[00efcdab78563412]", s); | |
| 571 | } | |
| 572 | ||
| 573 | fn hexDigest(digest: [Hash.digest_length]u8) [Hash.digest_length * 2]u8 { | |
| 574 | var result: [Hash.digest_length * 2]u8 = undefined; | |
| 575 | for (digest) |byte, i| { | |
| 576 | result[i * 2 + 0] = hex_charset[byte >> 4]; | |
| 577 | result[i * 2 + 1] = hex_charset[byte & 15]; | |
| 574 | fn isExecutable(file: fs.File) !bool { | |
| 575 | if (builtin.os.tag == .windows) { | |
| 576 | // TODO check the ACL on Windows. | |
| 577 | // Until this is implemented, this could be a false negative on | |
| 578 | // Windows, which is why we do not yet set executable_bit_only above | |
| 579 | // when unpacking the tarball. | |
| 580 | return false; | |
| 581 | } else { | |
| 582 | const stat = try file.stat(); | |
| 583 | return (stat.mode & std.os.S.IXUSR) != 0; | |
| 578 | 584 | } |
| 579 | return result; | |
| 580 | 585 | } |
| 581 | 586 | |
| 582 | 587 | fn renameTmpIntoCache( |
src/Sema.zig+99-22| ... | ... | @@ -1015,6 +1015,7 @@ fn analyzeBodyInner( |
| 1015 | 1015 | .float_cast => try sema.zirFloatCast(block, inst), |
| 1016 | 1016 | .int_cast => try sema.zirIntCast(block, inst), |
| 1017 | 1017 | .ptr_cast => try sema.zirPtrCast(block, inst), |
| 1018 | .qual_cast => try sema.zirQualCast(block, inst), | |
| 1018 | 1019 | .truncate => try sema.zirTruncate(block, inst), |
| 1019 | 1020 | .align_cast => try sema.zirAlignCast(block, inst), |
| 1020 | 1021 | .has_decl => try sema.zirHasDecl(block, inst), |
| ... | ... | @@ -3294,7 +3295,7 @@ fn ensureResultUsed( |
| 3294 | 3295 | const msg = msg: { |
| 3295 | 3296 | const msg = try sema.errMsg(block, src, "error is ignored", .{}); |
| 3296 | 3297 | errdefer msg.destroy(sema.gpa); |
| 3297 | try sema.errNote(block, src, msg, "consider using `try`, `catch`, or `if`", .{}); | |
| 3298 | try sema.errNote(block, src, msg, "consider using 'try', 'catch', or 'if'", .{}); | |
| 3298 | 3299 | break :msg msg; |
| 3299 | 3300 | }; |
| 3300 | 3301 | return sema.failWithOwnedErrorMsg(msg); |
| ... | ... | @@ -3325,7 +3326,7 @@ fn zirEnsureResultNonError(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com |
| 3325 | 3326 | const msg = msg: { |
| 3326 | 3327 | const msg = try sema.errMsg(block, src, "error is discarded", .{}); |
| 3327 | 3328 | errdefer msg.destroy(sema.gpa); |
| 3328 | try sema.errNote(block, src, msg, "consider using `try`, `catch`, or `if`", .{}); | |
| 3329 | try sema.errNote(block, src, msg, "consider using 'try', 'catch', or 'if'", .{}); | |
| 3329 | 3330 | break :msg msg; |
| 3330 | 3331 | }; |
| 3331 | 3332 | return sema.failWithOwnedErrorMsg(msg); |
| ... | ... | @@ -5564,16 +5565,6 @@ pub fn analyzeExport( |
| 5564 | 5565 | .visibility = borrowed_options.visibility, |
| 5565 | 5566 | }, |
| 5566 | 5567 | .src = src, |
| 5567 | .link = switch (mod.comp.bin_file.tag) { | |
| 5568 | .coff => .{ .coff = .{} }, | |
| 5569 | .elf => .{ .elf = .{} }, | |
| 5570 | .macho => .{ .macho = .{} }, | |
| 5571 | .plan9 => .{ .plan9 = null }, | |
| 5572 | .c => .{ .c = {} }, | |
| 5573 | .wasm => .{ .wasm = .{} }, | |
| 5574 | .spirv => .{ .spirv = {} }, | |
| 5575 | .nvptx => .{ .nvptx = {} }, | |
| 5576 | }, | |
| 5577 | 5568 | .owner_decl = sema.owner_decl_index, |
| 5578 | 5569 | .src_decl = block.src_decl, |
| 5579 | 5570 | .exported_decl = exported_decl_index, |
| ... | ... | @@ -6446,7 +6437,12 @@ fn analyzeCall( |
| 6446 | 6437 | .extern_fn => return sema.fail(block, call_src, "{s} call of extern function", .{ |
| 6447 | 6438 | @as([]const u8, if (is_comptime_call) "comptime" else "inline"), |
| 6448 | 6439 | }), |
| 6449 | else => unreachable, | |
| 6440 | else => { | |
| 6441 | assert(callee_ty.isPtrAtRuntime()); | |
| 6442 | return sema.fail(block, call_src, "{s} call of function pointer", .{ | |
| 6443 | @as([]const u8, if (is_comptime_call) "comptime" else "inline"), | |
| 6444 | }); | |
| 6445 | }, | |
| 6450 | 6446 | }; |
| 6451 | 6447 | if (func_ty_info.is_var_args) { |
| 6452 | 6448 | return sema.fail(block, call_src, "{s} call of variadic function", .{ |
| ... | ... | @@ -6879,6 +6875,8 @@ fn analyzeInlineCallArg( |
| 6879 | 6875 | if (err == error.AnalysisFail and param_block.comptime_reason != null) try param_block.comptime_reason.?.explain(sema, sema.err); |
| 6880 | 6876 | return err; |
| 6881 | 6877 | }; |
| 6878 | } else if (!is_comptime_call and zir_tags[inst] == .param_comptime) { | |
| 6879 | _ = try sema.resolveConstMaybeUndefVal(arg_block, arg_src, uncasted_arg, "parameter is comptime"); | |
| 6882 | 6880 | } |
| 6883 | 6881 | const casted_arg = sema.coerceExtra(arg_block, param_ty, uncasted_arg, arg_src, .{ .param_src = .{ |
| 6884 | 6882 | .func_inst = func_inst, |
| ... | ... | @@ -6952,6 +6950,9 @@ fn analyzeInlineCallArg( |
| 6952 | 6950 | .val = arg_val, |
| 6953 | 6951 | }; |
| 6954 | 6952 | } else { |
| 6953 | if (zir_tags[inst] == .param_anytype_comptime) { | |
| 6954 | _ = try sema.resolveConstMaybeUndefVal(arg_block, arg_src, uncasted_arg, "parameter is comptime"); | |
| 6955 | } | |
| 6955 | 6956 | sema.inst_map.putAssumeCapacityNoClobber(inst, uncasted_arg); |
| 6956 | 6957 | } |
| 6957 | 6958 | |
| ... | ... | @@ -7510,7 +7511,6 @@ fn resolveGenericInstantiationType( |
| 7510 | 7511 | // Queue up a `codegen_func` work item for the new Fn. The `comptime_args` field |
| 7511 | 7512 | // will be populated, ensuring it will have `analyzeBody` called with the ZIR |
| 7512 | 7513 | // parameters mapped appropriately. |
| 7513 | try mod.comp.bin_file.allocateDeclIndexes(new_decl_index); | |
| 7514 | 7514 | try mod.comp.work_queue.writeItem(.{ .codegen_func = new_func }); |
| 7515 | 7515 | return new_func; |
| 7516 | 7516 | } |
| ... | ... | @@ -8473,7 +8473,7 @@ fn handleExternLibName( |
| 8473 | 8473 | return sema.fail( |
| 8474 | 8474 | block, |
| 8475 | 8475 | src_loc, |
| 8476 | "dependency on dynamic library '{s}' requires enabling Position Independent Code. Fixed by `-l{s}` or `-fPIC`.", | |
| 8476 | "dependency on dynamic library '{s}' requires enabling Position Independent Code. Fixed by '-l{s}' or '-fPIC'.", | |
| 8477 | 8477 | .{ lib_name, lib_name }, |
| 8478 | 8478 | ); |
| 8479 | 8479 | } |
| ... | ... | @@ -9010,7 +9010,18 @@ fn zirParam( |
| 9010 | 9010 | if (is_comptime and sema.preallocated_new_func != null) { |
| 9011 | 9011 | // We have a comptime value for this parameter so it should be elided from the |
| 9012 | 9012 | // function type of the function instruction in this block. |
| 9013 | const coerced_arg = try sema.coerce(block, param_ty, arg, src); | |
| 9013 | const coerced_arg = sema.coerce(block, param_ty, arg, .unneeded) catch |err| switch (err) { | |
| 9014 | error.NeededSourceLocation => { | |
| 9015 | // We are instantiating a generic function and a comptime arg | |
| 9016 | // cannot be coerced to the param type, but since we don't | |
| 9017 | // have the callee source location return `GenericPoison` | |
| 9018 | // so that the instantiation is failed and the coercion | |
| 9019 | // is handled by comptime call logic instead. | |
| 9020 | assert(sema.is_generic_instantiation); | |
| 9021 | return error.GenericPoison; | |
| 9022 | }, | |
| 9023 | else => return err, | |
| 9024 | }; | |
| 9014 | 9025 | sema.inst_map.putAssumeCapacity(inst, coerced_arg); |
| 9015 | 9026 | return; |
| 9016 | 9027 | } |
| ... | ... | @@ -19525,13 +19536,34 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air |
| 19525 | 19536 | const operand_info = operand_ty.ptrInfo().data; |
| 19526 | 19537 | const dest_info = dest_ty.ptrInfo().data; |
| 19527 | 19538 | if (!operand_info.mutable and dest_info.mutable) { |
| 19528 | return sema.fail(block, src, "cast discards const qualifier", .{}); | |
| 19539 | const msg = msg: { | |
| 19540 | const msg = try sema.errMsg(block, src, "cast discards const qualifier", .{}); | |
| 19541 | errdefer msg.destroy(sema.gpa); | |
| 19542 | ||
| 19543 | try sema.errNote(block, src, msg, "consider using '@qualCast'", .{}); | |
| 19544 | break :msg msg; | |
| 19545 | }; | |
| 19546 | return sema.failWithOwnedErrorMsg(msg); | |
| 19529 | 19547 | } |
| 19530 | 19548 | if (operand_info.@"volatile" and !dest_info.@"volatile") { |
| 19531 | return sema.fail(block, src, "cast discards volatile qualifier", .{}); | |
| 19549 | const msg = msg: { | |
| 19550 | const msg = try sema.errMsg(block, src, "cast discards volatile qualifier", .{}); | |
| 19551 | errdefer msg.destroy(sema.gpa); | |
| 19552 | ||
| 19553 | try sema.errNote(block, src, msg, "consider using '@qualCast'", .{}); | |
| 19554 | break :msg msg; | |
| 19555 | }; | |
| 19556 | return sema.failWithOwnedErrorMsg(msg); | |
| 19532 | 19557 | } |
| 19533 | 19558 | if (operand_info.@"addrspace" != dest_info.@"addrspace") { |
| 19534 | return sema.fail(block, src, "cast changes pointer address space", .{}); | |
| 19559 | const msg = msg: { | |
| 19560 | const msg = try sema.errMsg(block, src, "cast changes pointer address space", .{}); | |
| 19561 | errdefer msg.destroy(sema.gpa); | |
| 19562 | ||
| 19563 | try sema.errNote(block, src, msg, "consider using '@addrSpaceCast'", .{}); | |
| 19564 | break :msg msg; | |
| 19565 | }; | |
| 19566 | return sema.failWithOwnedErrorMsg(msg); | |
| 19535 | 19567 | } |
| 19536 | 19568 | |
| 19537 | 19569 | const dest_is_slice = dest_ty.isSlice(); |
| ... | ... | @@ -19586,6 +19618,8 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air |
| 19586 | 19618 | try sema.errNote(block, dest_ty_src, msg, "'{}' has alignment '{d}'", .{ |
| 19587 | 19619 | dest_ty.fmt(sema.mod), dest_align, |
| 19588 | 19620 | }); |
| 19621 | ||
| 19622 | try sema.errNote(block, src, msg, "consider using '@alignCast'", .{}); | |
| 19589 | 19623 | break :msg msg; |
| 19590 | 19624 | }; |
| 19591 | 19625 | return sema.failWithOwnedErrorMsg(msg); |
| ... | ... | @@ -19621,6 +19655,49 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air |
| 19621 | 19655 | return block.addBitCast(aligned_dest_ty, ptr); |
| 19622 | 19656 | } |
| 19623 | 19657 | |
| 19658 | fn zirQualCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | |
| 19659 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | |
| 19660 | const src = inst_data.src(); | |
| 19661 | const dest_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; | |
| 19662 | const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; | |
| 19663 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; | |
| 19664 | const dest_ty = try sema.resolveType(block, dest_ty_src, extra.lhs); | |
| 19665 | const operand = try sema.resolveInst(extra.rhs); | |
| 19666 | const operand_ty = sema.typeOf(operand); | |
| 19667 | ||
| 19668 | try sema.checkPtrType(block, dest_ty_src, dest_ty); | |
| 19669 | try sema.checkPtrOperand(block, operand_src, operand_ty); | |
| 19670 | ||
| 19671 | var operand_payload = operand_ty.ptrInfo(); | |
| 19672 | var dest_info = dest_ty.ptrInfo(); | |
| 19673 | ||
| 19674 | operand_payload.data.mutable = dest_info.data.mutable; | |
| 19675 | operand_payload.data.@"volatile" = dest_info.data.@"volatile"; | |
| 19676 | ||
| 19677 | const altered_operand_ty = Type.initPayload(&operand_payload.base); | |
| 19678 | if (!altered_operand_ty.eql(dest_ty, sema.mod)) { | |
| 19679 | const msg = msg: { | |
| 19680 | const msg = try sema.errMsg(block, src, "'@qualCast' can only modify 'const' and 'volatile' qualifiers", .{}); | |
| 19681 | errdefer msg.destroy(sema.gpa); | |
| 19682 | ||
| 19683 | dest_info.data.mutable = !operand_ty.isConstPtr(); | |
| 19684 | dest_info.data.@"volatile" = operand_ty.isVolatilePtr(); | |
| 19685 | const altered_dest_ty = Type.initPayload(&dest_info.base); | |
| 19686 | try sema.errNote(block, src, msg, "expected type '{}'", .{altered_dest_ty.fmt(sema.mod)}); | |
| 19687 | try sema.errNote(block, src, msg, "got type '{}'", .{operand_ty.fmt(sema.mod)}); | |
| 19688 | break :msg msg; | |
| 19689 | }; | |
| 19690 | return sema.failWithOwnedErrorMsg(msg); | |
| 19691 | } | |
| 19692 | ||
| 19693 | if (try sema.resolveMaybeUndefVal(operand)) |operand_val| { | |
| 19694 | return sema.addConstant(dest_ty, operand_val); | |
| 19695 | } | |
| 19696 | ||
| 19697 | try sema.requireRuntimeBlock(block, src, null); | |
| 19698 | return block.addBitCast(dest_ty, operand); | |
| 19699 | } | |
| 19700 | ||
| 19624 | 19701 | fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 19625 | 19702 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; |
| 19626 | 19703 | const src = inst_data.src(); |
| ... | ... | @@ -25137,7 +25214,7 @@ fn coerceExtra( |
| 25137 | 25214 | (try sema.coerceInMemoryAllowed(block, inst_ty.errorUnionPayload(), dest_ty, false, target, dest_ty_src, inst_src)) == .ok) |
| 25138 | 25215 | { |
| 25139 | 25216 | try sema.errNote(block, inst_src, msg, "cannot convert error union to payload type", .{}); |
| 25140 | try sema.errNote(block, inst_src, msg, "consider using `try`, `catch`, or `if`", .{}); | |
| 25217 | try sema.errNote(block, inst_src, msg, "consider using 'try', 'catch', or 'if'", .{}); | |
| 25141 | 25218 | } |
| 25142 | 25219 | |
| 25143 | 25220 | // ?T to T |
| ... | ... | @@ -25146,7 +25223,7 @@ fn coerceExtra( |
| 25146 | 25223 | (try sema.coerceInMemoryAllowed(block, inst_ty.optionalChild(&buf), dest_ty, false, target, dest_ty_src, inst_src)) == .ok) |
| 25147 | 25224 | { |
| 25148 | 25225 | try sema.errNote(block, inst_src, msg, "cannot convert optional to payload type", .{}); |
| 25149 | try sema.errNote(block, inst_src, msg, "consider using `.?`, `orelse`, or `if`", .{}); | |
| 25226 | try sema.errNote(block, inst_src, msg, "consider using '.?', 'orelse', or 'if'", .{}); | |
| 25150 | 25227 | } |
| 25151 | 25228 | |
| 25152 | 25229 | try in_memory_result.report(sema, block, inst_src, msg); |
| ... | ... | @@ -26072,7 +26149,7 @@ fn coerceVarArgParam( |
| 26072 | 26149 | .Array => return sema.fail(block, inst_src, "arrays must be passed by reference to variadic function", .{}), |
| 26073 | 26150 | .Float => float: { |
| 26074 | 26151 | const target = sema.mod.getTarget(); |
| 26075 | const double_bits = @import("type.zig").CType.sizeInBits(.double, target); | |
| 26152 | const double_bits = target.c_type_bit_size(.double); | |
| 26076 | 26153 | const inst_bits = uncasted_ty.floatBits(sema.mod.getTarget()); |
| 26077 | 26154 | if (inst_bits >= double_bits) break :float inst; |
| 26078 | 26155 | switch (double_bits) { |
src/TypedValue.zig+4-1| ... | ... | @@ -176,7 +176,9 @@ pub fn print( |
| 176 | 176 | |
| 177 | 177 | var i: u32 = 0; |
| 178 | 178 | while (i < max_len) : (i += 1) { |
| 179 | buf[i] = std.math.cast(u8, val.fieldValue(ty, i).toUnsignedInt(target)) orelse break :str; | |
| 179 | const elem = val.fieldValue(ty, i); | |
| 180 | if (elem.isUndef()) break :str; | |
| 181 | buf[i] = std.math.cast(u8, elem.toUnsignedInt(target)) orelse break :str; | |
| 180 | 182 | } |
| 181 | 183 | |
| 182 | 184 | const truncated = if (len > max_string_len) " (truncated)" else ""; |
| ... | ... | @@ -390,6 +392,7 @@ pub fn print( |
| 390 | 392 | while (i < max_len) : (i += 1) { |
| 391 | 393 | var elem_buf: Value.ElemValueBuffer = undefined; |
| 392 | 394 | const elem_val = payload.ptr.elemValueBuffer(mod, i, &elem_buf); |
| 395 | if (elem_val.isUndef()) break :str; | |
| 393 | 396 | buf[i] = std.math.cast(u8, elem_val.toUnsignedInt(target)) orelse break :str; |
| 394 | 397 | } |
| 395 | 398 |
src/Zir.zig+6| ... | ... | @@ -857,6 +857,9 @@ pub const Inst = struct { |
| 857 | 857 | /// Implements the `@ptrCast` builtin. |
| 858 | 858 | /// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand. |
| 859 | 859 | ptr_cast, |
| 860 | /// Implements the `@qualCast` builtin. | |
| 861 | /// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand. | |
| 862 | qual_cast, | |
| 860 | 863 | /// Implements the `@truncate` builtin. |
| 861 | 864 | /// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand. |
| 862 | 865 | truncate, |
| ... | ... | @@ -1195,6 +1198,7 @@ pub const Inst = struct { |
| 1195 | 1198 | .float_cast, |
| 1196 | 1199 | .int_cast, |
| 1197 | 1200 | .ptr_cast, |
| 1201 | .qual_cast, | |
| 1198 | 1202 | .truncate, |
| 1199 | 1203 | .align_cast, |
| 1200 | 1204 | .has_field, |
| ... | ... | @@ -1484,6 +1488,7 @@ pub const Inst = struct { |
| 1484 | 1488 | .float_cast, |
| 1485 | 1489 | .int_cast, |
| 1486 | 1490 | .ptr_cast, |
| 1491 | .qual_cast, | |
| 1487 | 1492 | .truncate, |
| 1488 | 1493 | .align_cast, |
| 1489 | 1494 | .has_field, |
| ... | ... | @@ -1755,6 +1760,7 @@ pub const Inst = struct { |
| 1755 | 1760 | .float_cast = .pl_node, |
| 1756 | 1761 | .int_cast = .pl_node, |
| 1757 | 1762 | .ptr_cast = .pl_node, |
| 1763 | .qual_cast = .pl_node, | |
| 1758 | 1764 | .truncate = .pl_node, |
| 1759 | 1765 | .align_cast = .pl_node, |
| 1760 | 1766 | .typeof_builtin = .pl_node, |
src/arch/aarch64/CodeGen.zig+143-175| ... | ... | @@ -24,7 +24,7 @@ const log = std.log.scoped(.codegen); |
| 24 | 24 | const build_options = @import("build_options"); |
| 25 | 25 | |
| 26 | 26 | const GenerateSymbolError = codegen.GenerateSymbolError; |
| 27 | const FnResult = codegen.FnResult; | |
| 27 | const Result = codegen.Result; | |
| 28 | 28 | const DebugInfoOutput = codegen.DebugInfoOutput; |
| 29 | 29 | |
| 30 | 30 | const bits = @import("bits.zig"); |
| ... | ... | @@ -181,6 +181,7 @@ const DbgInfoReloc = struct { |
| 181 | 181 | else => unreachable, |
| 182 | 182 | } |
| 183 | 183 | } |
| 184 | ||
| 184 | 185 | fn genArgDbgInfo(reloc: DbgInfoReloc, function: Self) error{OutOfMemory}!void { |
| 185 | 186 | switch (function.debug_output) { |
| 186 | 187 | .dwarf => |dw| { |
| ... | ... | @@ -202,13 +203,7 @@ const DbgInfoReloc = struct { |
| 202 | 203 | else => unreachable, // not a possible argument |
| 203 | 204 | |
| 204 | 205 | }; |
| 205 | try dw.genArgDbgInfo( | |
| 206 | reloc.name, | |
| 207 | reloc.ty, | |
| 208 | function.bin_file.tag, | |
| 209 | function.mod_fn.owner_decl, | |
| 210 | loc, | |
| 211 | ); | |
| 206 | try dw.genArgDbgInfo(reloc.name, reloc.ty, function.mod_fn.owner_decl, loc); | |
| 212 | 207 | }, |
| 213 | 208 | .plan9 => {}, |
| 214 | 209 | .none => {}, |
| ... | ... | @@ -254,14 +249,7 @@ const DbgInfoReloc = struct { |
| 254 | 249 | break :blk .nop; |
| 255 | 250 | }, |
| 256 | 251 | }; |
| 257 | try dw.genVarDbgInfo( | |
| 258 | reloc.name, | |
| 259 | reloc.ty, | |
| 260 | function.bin_file.tag, | |
| 261 | function.mod_fn.owner_decl, | |
| 262 | is_ptr, | |
| 263 | loc, | |
| 264 | ); | |
| 252 | try dw.genVarDbgInfo(reloc.name, reloc.ty, function.mod_fn.owner_decl, is_ptr, loc); | |
| 265 | 253 | }, |
| 266 | 254 | .plan9 => {}, |
| 267 | 255 | .none => {}, |
| ... | ... | @@ -349,7 +337,7 @@ pub fn generate( |
| 349 | 337 | liveness: Liveness, |
| 350 | 338 | code: *std.ArrayList(u8), |
| 351 | 339 | debug_output: DebugInfoOutput, |
| 352 | ) GenerateSymbolError!FnResult { | |
| 340 | ) GenerateSymbolError!Result { | |
| 353 | 341 | if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) { |
| 354 | 342 | @panic("Attempted to compile for architecture that was disabled by build configuration"); |
| 355 | 343 | } |
| ... | ... | @@ -392,8 +380,8 @@ pub fn generate( |
| 392 | 380 | defer function.dbg_info_relocs.deinit(bin_file.allocator); |
| 393 | 381 | |
| 394 | 382 | var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) { |
| 395 | error.CodegenFail => return FnResult{ .fail = function.err_msg.? }, | |
| 396 | error.OutOfRegisters => return FnResult{ | |
| 383 | error.CodegenFail => return Result{ .fail = function.err_msg.? }, | |
| 384 | error.OutOfRegisters => return Result{ | |
| 397 | 385 | .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), |
| 398 | 386 | }, |
| 399 | 387 | else => |e| return e, |
| ... | ... | @@ -406,8 +394,8 @@ pub fn generate( |
| 406 | 394 | function.max_end_stack = call_info.stack_byte_count; |
| 407 | 395 | |
| 408 | 396 | function.gen() catch |err| switch (err) { |
| 409 | error.CodegenFail => return FnResult{ .fail = function.err_msg.? }, | |
| 410 | error.OutOfRegisters => return FnResult{ | |
| 397 | error.CodegenFail => return Result{ .fail = function.err_msg.? }, | |
| 398 | error.OutOfRegisters => return Result{ | |
| 411 | 399 | .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), |
| 412 | 400 | }, |
| 413 | 401 | else => |e| return e, |
| ... | ... | @@ -439,14 +427,14 @@ pub fn generate( |
| 439 | 427 | defer emit.deinit(); |
| 440 | 428 | |
| 441 | 429 | emit.emitMir() catch |err| switch (err) { |
| 442 | error.EmitFail => return FnResult{ .fail = emit.err_msg.? }, | |
| 430 | error.EmitFail => return Result{ .fail = emit.err_msg.? }, | |
| 443 | 431 | else => |e| return e, |
| 444 | 432 | }; |
| 445 | 433 | |
| 446 | 434 | if (function.err_msg) |em| { |
| 447 | return FnResult{ .fail = em }; | |
| 435 | return Result{ .fail = em }; | |
| 448 | 436 | } else { |
| 449 | return FnResult{ .appended = {} }; | |
| 437 | return Result.ok; | |
| 450 | 438 | } |
| 451 | 439 | } |
| 452 | 440 | |
| ... | ... | @@ -527,6 +515,28 @@ fn gen(self: *Self) !void { |
| 527 | 515 | self.ret_mcv = MCValue{ .stack_offset = stack_offset }; |
| 528 | 516 | } |
| 529 | 517 | |
| 518 | for (self.args) |*arg, arg_index| { | |
| 519 | // Copy register arguments to the stack | |
| 520 | switch (arg.*) { | |
| 521 | .register => |reg| { | |
| 522 | // The first AIR instructions of the main body are guaranteed | |
| 523 | // to be the functions arguments | |
| 524 | const inst = self.air.getMainBody()[arg_index]; | |
| 525 | assert(self.air.instructions.items(.tag)[inst] == .arg); | |
| 526 | ||
| 527 | const ty = self.air.typeOfIndex(inst); | |
| 528 | ||
| 529 | const abi_size = @intCast(u32, ty.abiSize(self.target.*)); | |
| 530 | const abi_align = ty.abiAlignment(self.target.*); | |
| 531 | const stack_offset = try self.allocMem(abi_size, abi_align, inst); | |
| 532 | try self.genSetStack(ty, stack_offset, MCValue{ .register = reg }); | |
| 533 | ||
| 534 | arg.* = MCValue{ .stack_offset = stack_offset }; | |
| 535 | }, | |
| 536 | else => {}, | |
| 537 | } | |
| 538 | } | |
| 539 | ||
| 530 | 540 | _ = try self.addInst(.{ |
| 531 | 541 | .tag = .dbg_prologue_end, |
| 532 | 542 | .data = .{ .nop = {} }, |
| ... | ... | @@ -3996,11 +4006,17 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type |
| 3996 | 4006 | .direct => .load_memory_ptr_direct, |
| 3997 | 4007 | .import => unreachable, |
| 3998 | 4008 | }; |
| 3999 | const mod = self.bin_file.options.module.?; | |
| 4000 | const owner_decl = mod.declPtr(self.mod_fn.owner_decl); | |
| 4001 | 4009 | const atom_index = switch (self.bin_file.tag) { |
| 4002 | .macho => owner_decl.link.macho.sym_index, | |
| 4003 | .coff => owner_decl.link.coff.sym_index, | |
| 4010 | .macho => blk: { | |
| 4011 | const macho_file = self.bin_file.cast(link.File.MachO).?; | |
| 4012 | const atom = try macho_file.getOrCreateAtomForDecl(self.mod_fn.owner_decl); | |
| 4013 | break :blk macho_file.getAtom(atom).getSymbolIndex().?; | |
| 4014 | }, | |
| 4015 | .coff => blk: { | |
| 4016 | const coff_file = self.bin_file.cast(link.File.Coff).?; | |
| 4017 | const atom = try coff_file.getOrCreateAtomForDecl(self.mod_fn.owner_decl); | |
| 4018 | break :blk coff_file.getAtom(atom).getSymbolIndex().?; | |
| 4019 | }, | |
| 4004 | 4020 | else => unreachable, // unsupported target format |
| 4005 | 4021 | }; |
| 4006 | 4022 | _ = try self.addInst(.{ |
| ... | ... | @@ -4163,45 +4179,19 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { |
| 4163 | 4179 | self.arg_index += 1; |
| 4164 | 4180 | |
| 4165 | 4181 | const ty = self.air.typeOfIndex(inst); |
| 4166 | const result = self.args[arg_index]; | |
| 4182 | const tag = self.air.instructions.items(.tag)[inst]; | |
| 4167 | 4183 | const src_index = self.air.instructions.items(.data)[inst].arg.src_index; |
| 4168 | 4184 | const name = self.mod_fn.getParamName(self.bin_file.options.module.?, src_index); |
| 4169 | 4185 | |
| 4170 | const mcv = switch (result) { | |
| 4171 | // Copy registers to the stack | |
| 4172 | .register => |reg| blk: { | |
| 4173 | const mod = self.bin_file.options.module.?; | |
| 4174 | const abi_size = math.cast(u32, ty.abiSize(self.target.*)) orelse { | |
| 4175 | return self.fail("type '{}' too big to fit into stack frame", .{ty.fmt(mod)}); | |
| 4176 | }; | |
| 4177 | const abi_align = ty.abiAlignment(self.target.*); | |
| 4178 | const stack_offset = try self.allocMem(abi_size, abi_align, inst); | |
| 4179 | try self.genSetStack(ty, stack_offset, MCValue{ .register = reg }); | |
| 4180 | ||
| 4181 | break :blk MCValue{ .stack_offset = stack_offset }; | |
| 4182 | }, | |
| 4183 | else => result, | |
| 4184 | }; | |
| 4185 | ||
| 4186 | const tag = self.air.instructions.items(.tag)[inst]; | |
| 4187 | 4186 | try self.dbg_info_relocs.append(self.gpa, .{ |
| 4188 | 4187 | .tag = tag, |
| 4189 | 4188 | .ty = ty, |
| 4190 | 4189 | .name = name, |
| 4191 | .mcv = result, | |
| 4190 | .mcv = self.args[arg_index], | |
| 4192 | 4191 | }); |
| 4193 | 4192 | |
| 4194 | if (self.liveness.isUnused(inst)) | |
| 4195 | return self.finishAirBookkeeping(); | |
| 4196 | ||
| 4197 | switch (mcv) { | |
| 4198 | .register => |reg| { | |
| 4199 | self.register_manager.getRegAssumeFree(reg, inst); | |
| 4200 | }, | |
| 4201 | else => {}, | |
| 4202 | } | |
| 4203 | ||
| 4204 | return self.finishAir(inst, mcv, .{ .none, .none, .none }); | |
| 4193 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else self.args[arg_index]; | |
| 4194 | return self.finishAir(inst, result, .{ .none, .none, .none }); | |
| 4205 | 4195 | } |
| 4206 | 4196 | |
| 4207 | 4197 | fn airBreakpoint(self: *Self) !void { |
| ... | ... | @@ -4302,90 +4292,71 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier |
| 4302 | 4292 | // on linking. |
| 4303 | 4293 | const mod = self.bin_file.options.module.?; |
| 4304 | 4294 | if (self.air.value(callee)) |func_value| { |
| 4305 | if (self.bin_file.cast(link.File.Elf)) |elf_file| { | |
| 4306 | if (func_value.castTag(.function)) |func_payload| { | |
| 4307 | const func = func_payload.data; | |
| 4308 | const ptr_bits = self.target.cpu.arch.ptrBitWidth(); | |
| 4309 | const ptr_bytes: u64 = @divExact(ptr_bits, 8); | |
| 4310 | const fn_owner_decl = mod.declPtr(func.owner_decl); | |
| 4311 | const got_addr = blk: { | |
| 4312 | const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?]; | |
| 4313 | break :blk @intCast(u32, got.p_vaddr + fn_owner_decl.link.elf.offset_table_index * ptr_bytes); | |
| 4314 | }; | |
| 4295 | if (func_value.castTag(.function)) |func_payload| { | |
| 4296 | const func = func_payload.data; | |
| 4315 | 4297 | |
| 4298 | if (self.bin_file.cast(link.File.Elf)) |elf_file| { | |
| 4299 | const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl); | |
| 4300 | const atom = elf_file.getAtom(atom_index); | |
| 4301 | const got_addr = @intCast(u32, atom.getOffsetTableAddress(elf_file)); | |
| 4316 | 4302 | try self.genSetReg(Type.initTag(.usize), .x30, .{ .memory = got_addr }); |
| 4317 | ||
| 4318 | _ = try self.addInst(.{ | |
| 4319 | .tag = .blr, | |
| 4320 | .data = .{ .reg = .x30 }, | |
| 4303 | } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { | |
| 4304 | const atom = try macho_file.getOrCreateAtomForDecl(func.owner_decl); | |
| 4305 | const sym_index = macho_file.getAtom(atom).getSymbolIndex().?; | |
| 4306 | try self.genSetReg(Type.initTag(.u64), .x30, .{ | |
| 4307 | .linker_load = .{ | |
| 4308 | .type = .got, | |
| 4309 | .sym_index = sym_index, | |
| 4310 | }, | |
| 4321 | 4311 | }); |
| 4322 | } else if (func_value.castTag(.extern_fn)) |_| { | |
| 4323 | return self.fail("TODO implement calling extern functions", .{}); | |
| 4324 | } else { | |
| 4325 | return self.fail("TODO implement calling bitcasted functions", .{}); | |
| 4326 | } | |
| 4327 | } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { | |
| 4328 | if (func_value.castTag(.function)) |func_payload| { | |
| 4329 | const func = func_payload.data; | |
| 4330 | const fn_owner_decl = mod.declPtr(func.owner_decl); | |
| 4312 | } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { | |
| 4313 | const atom = try coff_file.getOrCreateAtomForDecl(func.owner_decl); | |
| 4314 | const sym_index = coff_file.getAtom(atom).getSymbolIndex().?; | |
| 4331 | 4315 | try self.genSetReg(Type.initTag(.u64), .x30, .{ |
| 4332 | 4316 | .linker_load = .{ |
| 4333 | 4317 | .type = .got, |
| 4334 | .sym_index = fn_owner_decl.link.macho.sym_index, | |
| 4318 | .sym_index = sym_index, | |
| 4335 | 4319 | }, |
| 4336 | 4320 | }); |
| 4337 | // blr x30 | |
| 4338 | _ = try self.addInst(.{ | |
| 4339 | .tag = .blr, | |
| 4340 | .data = .{ .reg = .x30 }, | |
| 4321 | } else if (self.bin_file.cast(link.File.Plan9)) |p9| { | |
| 4322 | const decl_block_index = try p9.seeDecl(func.owner_decl); | |
| 4323 | const decl_block = p9.getDeclBlock(decl_block_index); | |
| 4324 | const ptr_bits = self.target.cpu.arch.ptrBitWidth(); | |
| 4325 | const ptr_bytes: u64 = @divExact(ptr_bits, 8); | |
| 4326 | const got_addr = p9.bases.data; | |
| 4327 | const got_index = decl_block.got_index.?; | |
| 4328 | const fn_got_addr = got_addr + got_index * ptr_bytes; | |
| 4329 | try self.genSetReg(Type.initTag(.usize), .x30, .{ .memory = fn_got_addr }); | |
| 4330 | } else unreachable; | |
| 4331 | ||
| 4332 | _ = try self.addInst(.{ | |
| 4333 | .tag = .blr, | |
| 4334 | .data = .{ .reg = .x30 }, | |
| 4335 | }); | |
| 4336 | } else if (func_value.castTag(.extern_fn)) |func_payload| { | |
| 4337 | const extern_fn = func_payload.data; | |
| 4338 | const decl_name = mod.declPtr(extern_fn.owner_decl).name; | |
| 4339 | if (extern_fn.lib_name) |lib_name| { | |
| 4340 | log.debug("TODO enforce that '{s}' is expected in '{s}' library", .{ | |
| 4341 | decl_name, | |
| 4342 | lib_name, | |
| 4341 | 4343 | }); |
| 4342 | } else if (func_value.castTag(.extern_fn)) |func_payload| { | |
| 4343 | const extern_fn = func_payload.data; | |
| 4344 | const decl_name = mod.declPtr(extern_fn.owner_decl).name; | |
| 4345 | if (extern_fn.lib_name) |lib_name| { | |
| 4346 | log.debug("TODO enforce that '{s}' is expected in '{s}' library", .{ | |
| 4347 | decl_name, | |
| 4348 | lib_name, | |
| 4349 | }); | |
| 4350 | } | |
| 4351 | const sym_index = try macho_file.getGlobalSymbol(mem.sliceTo(decl_name, 0)); | |
| 4344 | } | |
| 4352 | 4345 | |
| 4346 | if (self.bin_file.cast(link.File.MachO)) |macho_file| { | |
| 4347 | const sym_index = try macho_file.getGlobalSymbol(mem.sliceTo(decl_name, 0)); | |
| 4348 | const atom = try macho_file.getOrCreateAtomForDecl(self.mod_fn.owner_decl); | |
| 4349 | const atom_index = macho_file.getAtom(atom).getSymbolIndex().?; | |
| 4353 | 4350 | _ = try self.addInst(.{ |
| 4354 | 4351 | .tag = .call_extern, |
| 4355 | 4352 | .data = .{ |
| 4356 | 4353 | .relocation = .{ |
| 4357 | .atom_index = mod.declPtr(self.mod_fn.owner_decl).link.macho.sym_index, | |
| 4354 | .atom_index = atom_index, | |
| 4358 | 4355 | .sym_index = sym_index, |
| 4359 | 4356 | }, |
| 4360 | 4357 | }, |
| 4361 | 4358 | }); |
| 4362 | } else { | |
| 4363 | return self.fail("TODO implement calling bitcasted functions", .{}); | |
| 4364 | } | |
| 4365 | } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { | |
| 4366 | if (func_value.castTag(.function)) |func_payload| { | |
| 4367 | const func = func_payload.data; | |
| 4368 | const fn_owner_decl = mod.declPtr(func.owner_decl); | |
| 4369 | try self.genSetReg(Type.initTag(.u64), .x30, .{ | |
| 4370 | .linker_load = .{ | |
| 4371 | .type = .got, | |
| 4372 | .sym_index = fn_owner_decl.link.coff.sym_index, | |
| 4373 | }, | |
| 4374 | }); | |
| 4375 | // blr x30 | |
| 4376 | _ = try self.addInst(.{ | |
| 4377 | .tag = .blr, | |
| 4378 | .data = .{ .reg = .x30 }, | |
| 4379 | }); | |
| 4380 | } else if (func_value.castTag(.extern_fn)) |func_payload| { | |
| 4381 | const extern_fn = func_payload.data; | |
| 4382 | const decl_name = mod.declPtr(extern_fn.owner_decl).name; | |
| 4383 | if (extern_fn.lib_name) |lib_name| { | |
| 4384 | log.debug("TODO enforce that '{s}' is expected in '{s}' library", .{ | |
| 4385 | decl_name, | |
| 4386 | lib_name, | |
| 4387 | }); | |
| 4388 | } | |
| 4359 | } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { | |
| 4389 | 4360 | const sym_index = try coff_file.getGlobalSymbol(mem.sliceTo(decl_name, 0)); |
| 4390 | 4361 | try self.genSetReg(Type.initTag(.u64), .x30, .{ |
| 4391 | 4362 | .linker_load = .{ |
| ... | ... | @@ -4393,35 +4364,16 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier |
| 4393 | 4364 | .sym_index = sym_index, |
| 4394 | 4365 | }, |
| 4395 | 4366 | }); |
| 4396 | // blr x30 | |
| 4397 | 4367 | _ = try self.addInst(.{ |
| 4398 | 4368 | .tag = .blr, |
| 4399 | 4369 | .data = .{ .reg = .x30 }, |
| 4400 | 4370 | }); |
| 4401 | 4371 | } else { |
| 4402 | return self.fail("TODO implement calling bitcasted functions", .{}); | |
| 4403 | } | |
| 4404 | } else if (self.bin_file.cast(link.File.Plan9)) |p9| { | |
| 4405 | if (func_value.castTag(.function)) |func_payload| { | |
| 4406 | try p9.seeDecl(func_payload.data.owner_decl); | |
| 4407 | const ptr_bits = self.target.cpu.arch.ptrBitWidth(); | |
| 4408 | const ptr_bytes: u64 = @divExact(ptr_bits, 8); | |
| 4409 | const got_addr = p9.bases.data; | |
| 4410 | const got_index = mod.declPtr(func_payload.data.owner_decl).link.plan9.got_index.?; | |
| 4411 | const fn_got_addr = got_addr + got_index * ptr_bytes; | |
| 4412 | ||
| 4413 | try self.genSetReg(Type.initTag(.usize), .x30, .{ .memory = fn_got_addr }); | |
| 4414 | ||
| 4415 | _ = try self.addInst(.{ | |
| 4416 | .tag = .blr, | |
| 4417 | .data = .{ .reg = .x30 }, | |
| 4418 | }); | |
| 4419 | } else if (func_value.castTag(.extern_fn)) |_| { | |
| 4420 | 4372 | return self.fail("TODO implement calling extern functions", .{}); |
| 4421 | } else { | |
| 4422 | return self.fail("TODO implement calling bitcasted functions", .{}); | |
| 4423 | 4373 | } |
| 4424 | } else unreachable; | |
| 4374 | } else { | |
| 4375 | return self.fail("TODO implement calling bitcasted functions", .{}); | |
| 4376 | } | |
| 4425 | 4377 | } else { |
| 4426 | 4378 | assert(ty.zigTypeTag() == .Pointer); |
| 4427 | 4379 | const mcv = try self.resolveInst(callee); |
| ... | ... | @@ -5534,11 +5486,17 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro |
| 5534 | 5486 | .direct => .load_memory_ptr_direct, |
| 5535 | 5487 | .import => unreachable, |
| 5536 | 5488 | }; |
| 5537 | const mod = self.bin_file.options.module.?; | |
| 5538 | const owner_decl = mod.declPtr(self.mod_fn.owner_decl); | |
| 5539 | 5489 | const atom_index = switch (self.bin_file.tag) { |
| 5540 | .macho => owner_decl.link.macho.sym_index, | |
| 5541 | .coff => owner_decl.link.coff.sym_index, | |
| 5490 | .macho => blk: { | |
| 5491 | const macho_file = self.bin_file.cast(link.File.MachO).?; | |
| 5492 | const atom = try macho_file.getOrCreateAtomForDecl(self.mod_fn.owner_decl); | |
| 5493 | break :blk macho_file.getAtom(atom).getSymbolIndex().?; | |
| 5494 | }, | |
| 5495 | .coff => blk: { | |
| 5496 | const coff_file = self.bin_file.cast(link.File.Coff).?; | |
| 5497 | const atom = try coff_file.getOrCreateAtomForDecl(self.mod_fn.owner_decl); | |
| 5498 | break :blk coff_file.getAtom(atom).getSymbolIndex().?; | |
| 5499 | }, | |
| 5542 | 5500 | else => unreachable, // unsupported target format |
| 5543 | 5501 | }; |
| 5544 | 5502 | _ = try self.addInst(.{ |
| ... | ... | @@ -5648,11 +5606,17 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 5648 | 5606 | .direct => .load_memory_direct, |
| 5649 | 5607 | .import => .load_memory_import, |
| 5650 | 5608 | }; |
| 5651 | const mod = self.bin_file.options.module.?; | |
| 5652 | const owner_decl = mod.declPtr(self.mod_fn.owner_decl); | |
| 5653 | 5609 | const atom_index = switch (self.bin_file.tag) { |
| 5654 | .macho => owner_decl.link.macho.sym_index, | |
| 5655 | .coff => owner_decl.link.coff.sym_index, | |
| 5610 | .macho => blk: { | |
| 5611 | const macho_file = self.bin_file.cast(link.File.MachO).?; | |
| 5612 | const atom = try macho_file.getOrCreateAtomForDecl(self.mod_fn.owner_decl); | |
| 5613 | break :blk macho_file.getAtom(atom).getSymbolIndex().?; | |
| 5614 | }, | |
| 5615 | .coff => blk: { | |
| 5616 | const coff_file = self.bin_file.cast(link.File.Coff).?; | |
| 5617 | const atom = try coff_file.getOrCreateAtomForDecl(self.mod_fn.owner_decl); | |
| 5618 | break :blk coff_file.getAtom(atom).getSymbolIndex().?; | |
| 5619 | }, | |
| 5656 | 5620 | else => unreachable, // unsupported target format |
| 5657 | 5621 | }; |
| 5658 | 5622 | _ = try self.addInst(.{ |
| ... | ... | @@ -5842,11 +5806,17 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I |
| 5842 | 5806 | .direct => .load_memory_ptr_direct, |
| 5843 | 5807 | .import => unreachable, |
| 5844 | 5808 | }; |
| 5845 | const mod = self.bin_file.options.module.?; | |
| 5846 | const owner_decl = mod.declPtr(self.mod_fn.owner_decl); | |
| 5847 | 5809 | const atom_index = switch (self.bin_file.tag) { |
| 5848 | .macho => owner_decl.link.macho.sym_index, | |
| 5849 | .coff => owner_decl.link.coff.sym_index, | |
| 5810 | .macho => blk: { | |
| 5811 | const macho_file = self.bin_file.cast(link.File.MachO).?; | |
| 5812 | const atom = try macho_file.getOrCreateAtomForDecl(self.mod_fn.owner_decl); | |
| 5813 | break :blk macho_file.getAtom(atom).getSymbolIndex().?; | |
| 5814 | }, | |
| 5815 | .coff => blk: { | |
| 5816 | const coff_file = self.bin_file.cast(link.File.Coff).?; | |
| 5817 | const atom = try coff_file.getOrCreateAtomForDecl(self.mod_fn.owner_decl); | |
| 5818 | break :blk coff_file.getAtom(atom).getSymbolIndex().?; | |
| 5819 | }, | |
| 5850 | 5820 | else => unreachable, // unsupported target format |
| 5851 | 5821 | }; |
| 5852 | 5822 | _ = try self.addInst(.{ |
| ... | ... | @@ -6165,28 +6135,27 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne |
| 6165 | 6135 | mod.markDeclAlive(decl); |
| 6166 | 6136 | |
| 6167 | 6137 | if (self.bin_file.cast(link.File.Elf)) |elf_file| { |
| 6168 | const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?]; | |
| 6169 | const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes; | |
| 6170 | return MCValue{ .memory = got_addr }; | |
| 6171 | } else if (self.bin_file.cast(link.File.MachO)) |_| { | |
| 6172 | // Because MachO is PIE-always-on, we defer memory address resolution until | |
| 6173 | // the linker has enough info to perform relocations. | |
| 6174 | assert(decl.link.macho.sym_index != 0); | |
| 6138 | const atom_index = try elf_file.getOrCreateAtomForDecl(decl_index); | |
| 6139 | const atom = elf_file.getAtom(atom_index); | |
| 6140 | return MCValue{ .memory = atom.getOffsetTableAddress(elf_file) }; | |
| 6141 | } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { | |
| 6142 | const atom = try macho_file.getOrCreateAtomForDecl(decl_index); | |
| 6143 | const sym_index = macho_file.getAtom(atom).getSymbolIndex().?; | |
| 6175 | 6144 | return MCValue{ .linker_load = .{ |
| 6176 | 6145 | .type = .got, |
| 6177 | .sym_index = decl.link.macho.sym_index, | |
| 6146 | .sym_index = sym_index, | |
| 6178 | 6147 | } }; |
| 6179 | } else if (self.bin_file.cast(link.File.Coff)) |_| { | |
| 6180 | // Because COFF is PIE-always-on, we defer memory address resolution until | |
| 6181 | // the linker has enough info to perform relocations. | |
| 6182 | assert(decl.link.coff.sym_index != 0); | |
| 6148 | } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { | |
| 6149 | const atom_index = try coff_file.getOrCreateAtomForDecl(decl_index); | |
| 6150 | const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?; | |
| 6183 | 6151 | return MCValue{ .linker_load = .{ |
| 6184 | 6152 | .type = .got, |
| 6185 | .sym_index = decl.link.coff.sym_index, | |
| 6153 | .sym_index = sym_index, | |
| 6186 | 6154 | } }; |
| 6187 | 6155 | } else if (self.bin_file.cast(link.File.Plan9)) |p9| { |
| 6188 | try p9.seeDecl(decl_index); | |
| 6189 | const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes; | |
| 6156 | const decl_block_index = try p9.seeDecl(decl_index); | |
| 6157 | const decl_block = p9.getDeclBlock(decl_block_index); | |
| 6158 | const got_addr = p9.bases.data + decl_block.got_index.? * ptr_bytes; | |
| 6190 | 6159 | return MCValue{ .memory = got_addr }; |
| 6191 | 6160 | } else { |
| 6192 | 6161 | return self.fail("TODO codegen non-ELF const Decl pointer", .{}); |
| ... | ... | @@ -6199,8 +6168,7 @@ fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue { |
| 6199 | 6168 | return self.fail("lowering unnamed constant failed: {s}", .{@errorName(err)}); |
| 6200 | 6169 | }; |
| 6201 | 6170 | if (self.bin_file.cast(link.File.Elf)) |elf_file| { |
| 6202 | const vaddr = elf_file.local_symbols.items[local_sym_index].st_value; | |
| 6203 | return MCValue{ .memory = vaddr }; | |
| 6171 | return MCValue{ .memory = elf_file.getSymbol(local_sym_index).st_value }; | |
| 6204 | 6172 | } else if (self.bin_file.cast(link.File.MachO)) |_| { |
| 6205 | 6173 | return MCValue{ .linker_load = .{ |
| 6206 | 6174 | .type = .direct, |
src/arch/aarch64/Emit.zig+8-8| ... | ... | @@ -670,9 +670,9 @@ fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) !void { |
| 670 | 670 | |
| 671 | 671 | if (emit.bin_file.cast(link.File.MachO)) |macho_file| { |
| 672 | 672 | // Add relocation to the decl. |
| 673 | const atom = macho_file.getAtomForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?; | |
| 673 | const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?; | |
| 674 | 674 | const target = macho_file.getGlobalByIndex(relocation.sym_index); |
| 675 | try atom.addRelocation(macho_file, .{ | |
| 675 | try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{ | |
| 676 | 676 | .type = @enumToInt(std.macho.reloc_type_arm64.ARM64_RELOC_BRANCH26), |
| 677 | 677 | .target = target, |
| 678 | 678 | .offset = offset, |
| ... | ... | @@ -883,10 +883,10 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void { |
| 883 | 883 | } |
| 884 | 884 | |
| 885 | 885 | if (emit.bin_file.cast(link.File.MachO)) |macho_file| { |
| 886 | const atom = macho_file.getAtomForSymbol(.{ .sym_index = data.atom_index, .file = null }).?; | |
| 886 | const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = data.atom_index, .file = null }).?; | |
| 887 | 887 | // TODO this causes segfault in stage1 |
| 888 | 888 | // try atom.addRelocations(macho_file, 2, .{ |
| 889 | try atom.addRelocation(macho_file, .{ | |
| 889 | try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{ | |
| 890 | 890 | .target = .{ .sym_index = data.sym_index, .file = null }, |
| 891 | 891 | .offset = offset, |
| 892 | 892 | .addend = 0, |
| ... | ... | @@ -902,7 +902,7 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void { |
| 902 | 902 | else => unreachable, |
| 903 | 903 | }, |
| 904 | 904 | }); |
| 905 | try atom.addRelocation(macho_file, .{ | |
| 905 | try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{ | |
| 906 | 906 | .target = .{ .sym_index = data.sym_index, .file = null }, |
| 907 | 907 | .offset = offset + 4, |
| 908 | 908 | .addend = 0, |
| ... | ... | @@ -919,7 +919,7 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void { |
| 919 | 919 | }, |
| 920 | 920 | }); |
| 921 | 921 | } else if (emit.bin_file.cast(link.File.Coff)) |coff_file| { |
| 922 | const atom = coff_file.getAtomForSymbol(.{ .sym_index = data.atom_index, .file = null }).?; | |
| 922 | const atom_index = coff_file.getAtomIndexForSymbol(.{ .sym_index = data.atom_index, .file = null }).?; | |
| 923 | 923 | const target = switch (tag) { |
| 924 | 924 | .load_memory_got, |
| 925 | 925 | .load_memory_ptr_got, |
| ... | ... | @@ -929,7 +929,7 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void { |
| 929 | 929 | .load_memory_import => coff_file.getGlobalByIndex(data.sym_index), |
| 930 | 930 | else => unreachable, |
| 931 | 931 | }; |
| 932 | try atom.addRelocation(coff_file, .{ | |
| 932 | try link.File.Coff.Atom.addRelocation(coff_file, atom_index, .{ | |
| 933 | 933 | .target = target, |
| 934 | 934 | .offset = offset, |
| 935 | 935 | .addend = 0, |
| ... | ... | @@ -946,7 +946,7 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void { |
| 946 | 946 | else => unreachable, |
| 947 | 947 | }, |
| 948 | 948 | }); |
| 949 | try atom.addRelocation(coff_file, .{ | |
| 949 | try link.File.Coff.Atom.addRelocation(coff_file, atom_index, .{ | |
| 950 | 950 | .target = target, |
| 951 | 951 | .offset = offset + 4, |
| 952 | 952 | .addend = 0, |
src/arch/arm/CodeGen.zig+65-81| ... | ... | @@ -23,7 +23,7 @@ const leb128 = std.leb; |
| 23 | 23 | const log = std.log.scoped(.codegen); |
| 24 | 24 | const build_options = @import("build_options"); |
| 25 | 25 | |
| 26 | const FnResult = codegen.FnResult; | |
| 26 | const Result = codegen.Result; | |
| 27 | 27 | const GenerateSymbolError = codegen.GenerateSymbolError; |
| 28 | 28 | const DebugInfoOutput = codegen.DebugInfoOutput; |
| 29 | 29 | |
| ... | ... | @@ -282,13 +282,7 @@ const DbgInfoReloc = struct { |
| 282 | 282 | else => unreachable, // not a possible argument |
| 283 | 283 | }; |
| 284 | 284 | |
| 285 | try dw.genArgDbgInfo( | |
| 286 | reloc.name, | |
| 287 | reloc.ty, | |
| 288 | function.bin_file.tag, | |
| 289 | function.mod_fn.owner_decl, | |
| 290 | loc, | |
| 291 | ); | |
| 285 | try dw.genArgDbgInfo(reloc.name, reloc.ty, function.mod_fn.owner_decl, loc); | |
| 292 | 286 | }, |
| 293 | 287 | .plan9 => {}, |
| 294 | 288 | .none => {}, |
| ... | ... | @@ -331,14 +325,7 @@ const DbgInfoReloc = struct { |
| 331 | 325 | break :blk .nop; |
| 332 | 326 | }, |
| 333 | 327 | }; |
| 334 | try dw.genVarDbgInfo( | |
| 335 | reloc.name, | |
| 336 | reloc.ty, | |
| 337 | function.bin_file.tag, | |
| 338 | function.mod_fn.owner_decl, | |
| 339 | is_ptr, | |
| 340 | loc, | |
| 341 | ); | |
| 328 | try dw.genVarDbgInfo(reloc.name, reloc.ty, function.mod_fn.owner_decl, is_ptr, loc); | |
| 342 | 329 | }, |
| 343 | 330 | .plan9 => {}, |
| 344 | 331 | .none => {}, |
| ... | ... | @@ -356,7 +343,7 @@ pub fn generate( |
| 356 | 343 | liveness: Liveness, |
| 357 | 344 | code: *std.ArrayList(u8), |
| 358 | 345 | debug_output: DebugInfoOutput, |
| 359 | ) GenerateSymbolError!FnResult { | |
| 346 | ) GenerateSymbolError!Result { | |
| 360 | 347 | if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) { |
| 361 | 348 | @panic("Attempted to compile for architecture that was disabled by build configuration"); |
| 362 | 349 | } |
| ... | ... | @@ -399,8 +386,8 @@ pub fn generate( |
| 399 | 386 | defer function.dbg_info_relocs.deinit(bin_file.allocator); |
| 400 | 387 | |
| 401 | 388 | var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) { |
| 402 | error.CodegenFail => return FnResult{ .fail = function.err_msg.? }, | |
| 403 | error.OutOfRegisters => return FnResult{ | |
| 389 | error.CodegenFail => return Result{ .fail = function.err_msg.? }, | |
| 390 | error.OutOfRegisters => return Result{ | |
| 404 | 391 | .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), |
| 405 | 392 | }, |
| 406 | 393 | else => |e| return e, |
| ... | ... | @@ -413,8 +400,8 @@ pub fn generate( |
| 413 | 400 | function.max_end_stack = call_info.stack_byte_count; |
| 414 | 401 | |
| 415 | 402 | function.gen() catch |err| switch (err) { |
| 416 | error.CodegenFail => return FnResult{ .fail = function.err_msg.? }, | |
| 417 | error.OutOfRegisters => return FnResult{ | |
| 403 | error.CodegenFail => return Result{ .fail = function.err_msg.? }, | |
| 404 | error.OutOfRegisters => return Result{ | |
| 418 | 405 | .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), |
| 419 | 406 | }, |
| 420 | 407 | else => |e| return e, |
| ... | ... | @@ -446,14 +433,14 @@ pub fn generate( |
| 446 | 433 | defer emit.deinit(); |
| 447 | 434 | |
| 448 | 435 | emit.emitMir() catch |err| switch (err) { |
| 449 | error.EmitFail => return FnResult{ .fail = emit.err_msg.? }, | |
| 436 | error.EmitFail => return Result{ .fail = emit.err_msg.? }, | |
| 450 | 437 | else => |e| return e, |
| 451 | 438 | }; |
| 452 | 439 | |
| 453 | 440 | if (function.err_msg) |em| { |
| 454 | return FnResult{ .fail = em }; | |
| 441 | return Result{ .fail = em }; | |
| 455 | 442 | } else { |
| 456 | return FnResult{ .appended = {} }; | |
| 443 | return Result.ok; | |
| 457 | 444 | } |
| 458 | 445 | } |
| 459 | 446 | |
| ... | ... | @@ -4253,59 +4240,56 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier |
| 4253 | 4240 | |
| 4254 | 4241 | // Due to incremental compilation, how function calls are generated depends |
| 4255 | 4242 | // on linking. |
| 4256 | switch (self.bin_file.tag) { | |
| 4257 | .elf => { | |
| 4258 | if (self.air.value(callee)) |func_value| { | |
| 4259 | if (func_value.castTag(.function)) |func_payload| { | |
| 4260 | const func = func_payload.data; | |
| 4261 | const ptr_bits = self.target.cpu.arch.ptrBitWidth(); | |
| 4262 | const ptr_bytes: u64 = @divExact(ptr_bits, 8); | |
| 4263 | const mod = self.bin_file.options.module.?; | |
| 4264 | const fn_owner_decl = mod.declPtr(func.owner_decl); | |
| 4265 | const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: { | |
| 4266 | const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?]; | |
| 4267 | break :blk @intCast(u32, got.p_vaddr + fn_owner_decl.link.elf.offset_table_index * ptr_bytes); | |
| 4268 | } else unreachable; | |
| 4269 | try self.genSetReg(Type.initTag(.usize), .lr, .{ .memory = got_addr }); | |
| 4270 | } else if (func_value.castTag(.extern_fn)) |_| { | |
| 4271 | return self.fail("TODO implement calling extern functions", .{}); | |
| 4272 | } else { | |
| 4273 | return self.fail("TODO implement calling bitcasted functions", .{}); | |
| 4274 | } | |
| 4243 | if (self.air.value(callee)) |func_value| { | |
| 4244 | if (func_value.castTag(.function)) |func_payload| { | |
| 4245 | const func = func_payload.data; | |
| 4246 | ||
| 4247 | if (self.bin_file.cast(link.File.Elf)) |elf_file| { | |
| 4248 | const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl); | |
| 4249 | const atom = elf_file.getAtom(atom_index); | |
| 4250 | const got_addr = @intCast(u32, atom.getOffsetTableAddress(elf_file)); | |
| 4251 | try self.genSetReg(Type.initTag(.usize), .lr, .{ .memory = got_addr }); | |
| 4252 | } else if (self.bin_file.cast(link.File.MachO)) |_| { | |
| 4253 | unreachable; // unsupported architecture for MachO | |
| 4275 | 4254 | } else { |
| 4276 | assert(ty.zigTypeTag() == .Pointer); | |
| 4277 | const mcv = try self.resolveInst(callee); | |
| 4278 | ||
| 4279 | try self.genSetReg(Type.initTag(.usize), .lr, mcv); | |
| 4280 | } | |
| 4281 | ||
| 4282 | // TODO: add Instruction.supportedOn | |
| 4283 | // function for ARM | |
| 4284 | if (Target.arm.featureSetHas(self.target.cpu.features, .has_v5t)) { | |
| 4285 | _ = try self.addInst(.{ | |
| 4286 | .tag = .blx, | |
| 4287 | .data = .{ .reg = .lr }, | |
| 4255 | return self.fail("TODO implement call on {s} for {s}", .{ | |
| 4256 | @tagName(self.bin_file.tag), | |
| 4257 | @tagName(self.target.cpu.arch), | |
| 4288 | 4258 | }); |
| 4289 | } else { | |
| 4290 | return self.fail("TODO fix blx emulation for ARM <v5", .{}); | |
| 4291 | // _ = try self.addInst(.{ | |
| 4292 | // .tag = .mov, | |
| 4293 | // .data = .{ .rr_op = .{ | |
| 4294 | // .rd = .lr, | |
| 4295 | // .rn = .r0, | |
| 4296 | // .op = Instruction.Operand.reg(.pc, Instruction.Operand.Shift.none), | |
| 4297 | // } }, | |
| 4298 | // }); | |
| 4299 | // _ = try self.addInst(.{ | |
| 4300 | // .tag = .bx, | |
| 4301 | // .data = .{ .reg = .lr }, | |
| 4302 | // }); | |
| 4303 | 4259 | } |
| 4304 | }, | |
| 4305 | .macho => unreachable, // unsupported architecture for MachO | |
| 4306 | .coff => return self.fail("TODO implement call in COFF for {}", .{self.target.cpu.arch}), | |
| 4307 | .plan9 => return self.fail("TODO implement call on plan9 for {}", .{self.target.cpu.arch}), | |
| 4308 | else => unreachable, | |
| 4260 | } else if (func_value.castTag(.extern_fn)) |_| { | |
| 4261 | return self.fail("TODO implement calling extern functions", .{}); | |
| 4262 | } else { | |
| 4263 | return self.fail("TODO implement calling bitcasted functions", .{}); | |
| 4264 | } | |
| 4265 | } else { | |
| 4266 | assert(ty.zigTypeTag() == .Pointer); | |
| 4267 | const mcv = try self.resolveInst(callee); | |
| 4268 | ||
| 4269 | try self.genSetReg(Type.initTag(.usize), .lr, mcv); | |
| 4270 | } | |
| 4271 | ||
| 4272 | // TODO: add Instruction.supportedOn | |
| 4273 | // function for ARM | |
| 4274 | if (Target.arm.featureSetHas(self.target.cpu.features, .has_v5t)) { | |
| 4275 | _ = try self.addInst(.{ | |
| 4276 | .tag = .blx, | |
| 4277 | .data = .{ .reg = .lr }, | |
| 4278 | }); | |
| 4279 | } else { | |
| 4280 | return self.fail("TODO fix blx emulation for ARM <v5", .{}); | |
| 4281 | // _ = try self.addInst(.{ | |
| 4282 | // .tag = .mov, | |
| 4283 | // .data = .{ .rr_op = .{ | |
| 4284 | // .rd = .lr, | |
| 4285 | // .rn = .r0, | |
| 4286 | // .op = Instruction.Operand.reg(.pc, Instruction.Operand.Shift.none), | |
| 4287 | // } }, | |
| 4288 | // }); | |
| 4289 | // _ = try self.addInst(.{ | |
| 4290 | // .tag = .bx, | |
| 4291 | // .data = .{ .reg = .lr }, | |
| 4292 | // }); | |
| 4309 | 4293 | } |
| 4310 | 4294 | |
| 4311 | 4295 | const result: MCValue = result: { |
| ... | ... | @@ -6086,16 +6070,17 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne |
| 6086 | 6070 | mod.markDeclAlive(decl); |
| 6087 | 6071 | |
| 6088 | 6072 | if (self.bin_file.cast(link.File.Elf)) |elf_file| { |
| 6089 | const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?]; | |
| 6090 | const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes; | |
| 6091 | return MCValue{ .memory = got_addr }; | |
| 6073 | const atom_index = try elf_file.getOrCreateAtomForDecl(decl_index); | |
| 6074 | const atom = elf_file.getAtom(atom_index); | |
| 6075 | return MCValue{ .memory = atom.getOffsetTableAddress(elf_file) }; | |
| 6092 | 6076 | } else if (self.bin_file.cast(link.File.MachO)) |_| { |
| 6093 | 6077 | unreachable; // unsupported architecture for MachO |
| 6094 | 6078 | } else if (self.bin_file.cast(link.File.Coff)) |_| { |
| 6095 | 6079 | return self.fail("TODO codegen COFF const Decl pointer", .{}); |
| 6096 | 6080 | } else if (self.bin_file.cast(link.File.Plan9)) |p9| { |
| 6097 | try p9.seeDecl(decl_index); | |
| 6098 | const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes; | |
| 6081 | const decl_block_index = try p9.seeDecl(decl_index); | |
| 6082 | const decl_block = p9.getDeclBlock(decl_block_index); | |
| 6083 | const got_addr = p9.bases.data + decl_block.got_index.? * ptr_bytes; | |
| 6099 | 6084 | return MCValue{ .memory = got_addr }; |
| 6100 | 6085 | } else { |
| 6101 | 6086 | return self.fail("TODO codegen non-ELF const Decl pointer", .{}); |
| ... | ... | @@ -6109,8 +6094,7 @@ fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue { |
| 6109 | 6094 | return self.fail("lowering unnamed constant failed: {s}", .{@errorName(err)}); |
| 6110 | 6095 | }; |
| 6111 | 6096 | if (self.bin_file.cast(link.File.Elf)) |elf_file| { |
| 6112 | const vaddr = elf_file.local_symbols.items[local_sym_index].st_value; | |
| 6113 | return MCValue{ .memory = vaddr }; | |
| 6097 | return MCValue{ .memory = elf_file.getSymbol(local_sym_index).st_value }; | |
| 6114 | 6098 | } else if (self.bin_file.cast(link.File.MachO)) |_| { |
| 6115 | 6099 | unreachable; |
| 6116 | 6100 | } else if (self.bin_file.cast(link.File.Coff)) |_| { |
src/arch/riscv64/CodeGen.zig+22-34| ... | ... | @@ -22,7 +22,7 @@ const leb128 = std.leb; |
| 22 | 22 | const log = std.log.scoped(.codegen); |
| 23 | 23 | const build_options = @import("build_options"); |
| 24 | 24 | |
| 25 | const FnResult = @import("../../codegen.zig").FnResult; | |
| 25 | const Result = @import("../../codegen.zig").Result; | |
| 26 | 26 | const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError; |
| 27 | 27 | const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; |
| 28 | 28 | |
| ... | ... | @@ -225,7 +225,7 @@ pub fn generate( |
| 225 | 225 | liveness: Liveness, |
| 226 | 226 | code: *std.ArrayList(u8), |
| 227 | 227 | debug_output: DebugInfoOutput, |
| 228 | ) GenerateSymbolError!FnResult { | |
| 228 | ) GenerateSymbolError!Result { | |
| 229 | 229 | if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) { |
| 230 | 230 | @panic("Attempted to compile for architecture that was disabled by build configuration"); |
| 231 | 231 | } |
| ... | ... | @@ -268,8 +268,8 @@ pub fn generate( |
| 268 | 268 | defer function.exitlude_jump_relocs.deinit(bin_file.allocator); |
| 269 | 269 | |
| 270 | 270 | var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) { |
| 271 | error.CodegenFail => return FnResult{ .fail = function.err_msg.? }, | |
| 272 | error.OutOfRegisters => return FnResult{ | |
| 271 | error.CodegenFail => return Result{ .fail = function.err_msg.? }, | |
| 272 | error.OutOfRegisters => return Result{ | |
| 273 | 273 | .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), |
| 274 | 274 | }, |
| 275 | 275 | else => |e| return e, |
| ... | ... | @@ -282,8 +282,8 @@ pub fn generate( |
| 282 | 282 | function.max_end_stack = call_info.stack_byte_count; |
| 283 | 283 | |
| 284 | 284 | function.gen() catch |err| switch (err) { |
| 285 | error.CodegenFail => return FnResult{ .fail = function.err_msg.? }, | |
| 286 | error.OutOfRegisters => return FnResult{ | |
| 285 | error.CodegenFail => return Result{ .fail = function.err_msg.? }, | |
| 286 | error.OutOfRegisters => return Result{ | |
| 287 | 287 | .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), |
| 288 | 288 | }, |
| 289 | 289 | else => |e| return e, |
| ... | ... | @@ -309,14 +309,14 @@ pub fn generate( |
| 309 | 309 | defer emit.deinit(); |
| 310 | 310 | |
| 311 | 311 | emit.emitMir() catch |err| switch (err) { |
| 312 | error.EmitFail => return FnResult{ .fail = emit.err_msg.? }, | |
| 312 | error.EmitFail => return Result{ .fail = emit.err_msg.? }, | |
| 313 | 313 | else => |e| return e, |
| 314 | 314 | }; |
| 315 | 315 | |
| 316 | 316 | if (function.err_msg) |em| { |
| 317 | return FnResult{ .fail = em }; | |
| 317 | return Result{ .fail = em }; | |
| 318 | 318 | } else { |
| 319 | return FnResult{ .appended = {} }; | |
| 319 | return Result.ok; | |
| 320 | 320 | } |
| 321 | 321 | } |
| 322 | 322 | |
| ... | ... | @@ -1615,13 +1615,9 @@ fn genArgDbgInfo(self: Self, inst: Air.Inst.Index, mcv: MCValue) !void { |
| 1615 | 1615 | |
| 1616 | 1616 | switch (self.debug_output) { |
| 1617 | 1617 | .dwarf => |dw| switch (mcv) { |
| 1618 | .register => |reg| try dw.genArgDbgInfo( | |
| 1619 | name, | |
| 1620 | ty, | |
| 1621 | self.bin_file.tag, | |
| 1622 | self.mod_fn.owner_decl, | |
| 1623 | .{ .register = reg.dwarfLocOp() }, | |
| 1624 | ), | |
| 1618 | .register => |reg| try dw.genArgDbgInfo(name, ty, self.mod_fn.owner_decl, .{ | |
| 1619 | .register = reg.dwarfLocOp(), | |
| 1620 | }), | |
| 1625 | 1621 | .stack_offset => {}, |
| 1626 | 1622 | else => {}, |
| 1627 | 1623 | }, |
| ... | ... | @@ -1721,16 +1717,9 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier |
| 1721 | 1717 | if (self.air.value(callee)) |func_value| { |
| 1722 | 1718 | if (func_value.castTag(.function)) |func_payload| { |
| 1723 | 1719 | const func = func_payload.data; |
| 1724 | ||
| 1725 | const ptr_bits = self.target.cpu.arch.ptrBitWidth(); | |
| 1726 | const ptr_bytes: u64 = @divExact(ptr_bits, 8); | |
| 1727 | const mod = self.bin_file.options.module.?; | |
| 1728 | const fn_owner_decl = mod.declPtr(func.owner_decl); | |
| 1729 | const got_addr = blk: { | |
| 1730 | const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?]; | |
| 1731 | break :blk @intCast(u32, got.p_vaddr + fn_owner_decl.link.elf.offset_table_index * ptr_bytes); | |
| 1732 | }; | |
| 1733 | ||
| 1720 | const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl); | |
| 1721 | const atom = elf_file.getAtom(atom_index); | |
| 1722 | const got_addr = @intCast(u32, atom.getOffsetTableAddress(elf_file)); | |
| 1734 | 1723 | try self.genSetReg(Type.initTag(.usize), .ra, .{ .memory = got_addr }); |
| 1735 | 1724 | _ = try self.addInst(.{ |
| 1736 | 1725 | .tag = .jalr, |
| ... | ... | @@ -2557,18 +2546,17 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne |
| 2557 | 2546 | const decl = mod.declPtr(decl_index); |
| 2558 | 2547 | mod.markDeclAlive(decl); |
| 2559 | 2548 | if (self.bin_file.cast(link.File.Elf)) |elf_file| { |
| 2560 | const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?]; | |
| 2561 | const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes; | |
| 2562 | return MCValue{ .memory = got_addr }; | |
| 2549 | const atom_index = try elf_file.getOrCreateAtomForDecl(decl_index); | |
| 2550 | const atom = elf_file.getAtom(atom_index); | |
| 2551 | return MCValue{ .memory = atom.getOffsetTableAddress(elf_file) }; | |
| 2563 | 2552 | } else if (self.bin_file.cast(link.File.MachO)) |_| { |
| 2564 | // TODO I'm hacking my way through here by repurposing .memory for storing | |
| 2565 | // index to the GOT target symbol index. | |
| 2566 | return MCValue{ .memory = decl.link.macho.sym_index }; | |
| 2553 | unreachable; | |
| 2567 | 2554 | } else if (self.bin_file.cast(link.File.Coff)) |_| { |
| 2568 | 2555 | return self.fail("TODO codegen COFF const Decl pointer", .{}); |
| 2569 | 2556 | } else if (self.bin_file.cast(link.File.Plan9)) |p9| { |
| 2570 | try p9.seeDecl(decl_index); | |
| 2571 | const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes; | |
| 2557 | const decl_block_index = try p9.seeDecl(decl_index); | |
| 2558 | const decl_block = p9.getDeclBlock(decl_block_index); | |
| 2559 | const got_addr = p9.bases.data + decl_block.got_index.? * ptr_bytes; | |
| 2572 | 2560 | return MCValue{ .memory = got_addr }; |
| 2573 | 2561 | } else { |
| 2574 | 2562 | return self.fail("TODO codegen non-ELF const Decl pointer", .{}); |
src/arch/sparc64/CodeGen.zig+18-27| ... | ... | @@ -20,7 +20,7 @@ const Emit = @import("Emit.zig"); |
| 20 | 20 | const Liveness = @import("../../Liveness.zig"); |
| 21 | 21 | const Type = @import("../../type.zig").Type; |
| 22 | 22 | const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError; |
| 23 | const FnResult = @import("../../codegen.zig").FnResult; | |
| 23 | const Result = @import("../../codegen.zig").Result; | |
| 24 | 24 | const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; |
| 25 | 25 | |
| 26 | 26 | const build_options = @import("build_options"); |
| ... | ... | @@ -265,7 +265,7 @@ pub fn generate( |
| 265 | 265 | liveness: Liveness, |
| 266 | 266 | code: *std.ArrayList(u8), |
| 267 | 267 | debug_output: DebugInfoOutput, |
| 268 | ) GenerateSymbolError!FnResult { | |
| 268 | ) GenerateSymbolError!Result { | |
| 269 | 269 | if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) { |
| 270 | 270 | @panic("Attempted to compile for architecture that was disabled by build configuration"); |
| 271 | 271 | } |
| ... | ... | @@ -310,8 +310,8 @@ pub fn generate( |
| 310 | 310 | defer function.exitlude_jump_relocs.deinit(bin_file.allocator); |
| 311 | 311 | |
| 312 | 312 | var call_info = function.resolveCallingConventionValues(fn_type, .callee) catch |err| switch (err) { |
| 313 | error.CodegenFail => return FnResult{ .fail = function.err_msg.? }, | |
| 314 | error.OutOfRegisters => return FnResult{ | |
| 313 | error.CodegenFail => return Result{ .fail = function.err_msg.? }, | |
| 314 | error.OutOfRegisters => return Result{ | |
| 315 | 315 | .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), |
| 316 | 316 | }, |
| 317 | 317 | else => |e| return e, |
| ... | ... | @@ -324,8 +324,8 @@ pub fn generate( |
| 324 | 324 | function.max_end_stack = call_info.stack_byte_count; |
| 325 | 325 | |
| 326 | 326 | function.gen() catch |err| switch (err) { |
| 327 | error.CodegenFail => return FnResult{ .fail = function.err_msg.? }, | |
| 328 | error.OutOfRegisters => return FnResult{ | |
| 327 | error.CodegenFail => return Result{ .fail = function.err_msg.? }, | |
| 328 | error.OutOfRegisters => return Result{ | |
| 329 | 329 | .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), |
| 330 | 330 | }, |
| 331 | 331 | else => |e| return e, |
| ... | ... | @@ -351,14 +351,14 @@ pub fn generate( |
| 351 | 351 | defer emit.deinit(); |
| 352 | 352 | |
| 353 | 353 | emit.emitMir() catch |err| switch (err) { |
| 354 | error.EmitFail => return FnResult{ .fail = emit.err_msg.? }, | |
| 354 | error.EmitFail => return Result{ .fail = emit.err_msg.? }, | |
| 355 | 355 | else => |e| return e, |
| 356 | 356 | }; |
| 357 | 357 | |
| 358 | 358 | if (function.err_msg) |em| { |
| 359 | return FnResult{ .fail = em }; | |
| 359 | return Result{ .fail = em }; | |
| 360 | 360 | } else { |
| 361 | return FnResult{ .appended = {} }; | |
| 361 | return Result.ok; | |
| 362 | 362 | } |
| 363 | 363 | } |
| 364 | 364 | |
| ... | ... | @@ -1216,12 +1216,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier |
| 1216 | 1216 | if (self.bin_file.tag == link.File.Elf.base_tag) { |
| 1217 | 1217 | if (func_value.castTag(.function)) |func_payload| { |
| 1218 | 1218 | const func = func_payload.data; |
| 1219 | const ptr_bits = self.target.cpu.arch.ptrBitWidth(); | |
| 1220 | const ptr_bytes: u64 = @divExact(ptr_bits, 8); | |
| 1221 | 1219 | const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: { |
| 1222 | const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?]; | |
| 1223 | const mod = self.bin_file.options.module.?; | |
| 1224 | break :blk @intCast(u32, got.p_vaddr + mod.declPtr(func.owner_decl).link.elf.offset_table_index * ptr_bytes); | |
| 1220 | const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl); | |
| 1221 | const atom = elf_file.getAtom(atom_index); | |
| 1222 | break :blk @intCast(u32, atom.getOffsetTableAddress(elf_file)); | |
| 1225 | 1223 | } else unreachable; |
| 1226 | 1224 | |
| 1227 | 1225 | try self.genSetReg(Type.initTag(.usize), .o7, .{ .memory = got_addr }); |
| ... | ... | @@ -3414,13 +3412,9 @@ fn genArgDbgInfo(self: Self, inst: Air.Inst.Index, mcv: MCValue) !void { |
| 3414 | 3412 | |
| 3415 | 3413 | switch (self.debug_output) { |
| 3416 | 3414 | .dwarf => |dw| switch (mcv) { |
| 3417 | .register => |reg| try dw.genArgDbgInfo( | |
| 3418 | name, | |
| 3419 | ty, | |
| 3420 | self.bin_file.tag, | |
| 3421 | self.mod_fn.owner_decl, | |
| 3422 | .{ .register = reg.dwarfLocOp() }, | |
| 3423 | ), | |
| 3415 | .register => |reg| try dw.genArgDbgInfo(name, ty, self.mod_fn.owner_decl, .{ | |
| 3416 | .register = reg.dwarfLocOp(), | |
| 3417 | }), | |
| 3424 | 3418 | else => {}, |
| 3425 | 3419 | }, |
| 3426 | 3420 | else => {}, |
| ... | ... | @@ -4193,9 +4187,6 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo |
| 4193 | 4187 | } |
| 4194 | 4188 | |
| 4195 | 4189 | fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) InnerError!MCValue { |
| 4196 | const ptr_bits = self.target.cpu.arch.ptrBitWidth(); | |
| 4197 | const ptr_bytes: u64 = @divExact(ptr_bits, 8); | |
| 4198 | ||
| 4199 | 4190 | // TODO this feels clunky. Perhaps we should check for it in `genTypedValue`? |
| 4200 | 4191 | if (tv.ty.zigTypeTag() == .Pointer) blk: { |
| 4201 | 4192 | if (tv.ty.castPtrToFn()) |_| break :blk; |
| ... | ... | @@ -4209,9 +4200,9 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne |
| 4209 | 4200 | |
| 4210 | 4201 | mod.markDeclAlive(decl); |
| 4211 | 4202 | if (self.bin_file.cast(link.File.Elf)) |elf_file| { |
| 4212 | const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?]; | |
| 4213 | const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes; | |
| 4214 | return MCValue{ .memory = got_addr }; | |
| 4203 | const atom_index = try elf_file.getOrCreateAtomForDecl(decl_index); | |
| 4204 | const atom = elf_file.getAtom(atom_index); | |
| 4205 | return MCValue{ .memory = atom.getOffsetTableAddress(elf_file) }; | |
| 4215 | 4206 | } else { |
| 4216 | 4207 | return self.fail("TODO codegen non-ELF const Decl pointer", .{}); |
| 4217 | 4208 | } |
src/arch/wasm/CodeGen.zig+36-28| ... | ... | @@ -627,13 +627,6 @@ test "Wasm - buildOpcode" { |
| 627 | 627 | try testing.expectEqual(@as(wasm.Opcode, .f64_reinterpret_i64), f64_reinterpret_i64); |
| 628 | 628 | } |
| 629 | 629 | |
| 630 | pub const Result = union(enum) { | |
| 631 | /// The codegen bytes have been appended to `Context.code` | |
| 632 | appended: void, | |
| 633 | /// The data is managed externally and are part of the `Result` | |
| 634 | externally_managed: []const u8, | |
| 635 | }; | |
| 636 | ||
| 637 | 630 | /// Hashmap to store generated `WValue` for each `Air.Inst.Ref` |
| 638 | 631 | pub const ValueTable = std.AutoArrayHashMapUnmanaged(Air.Inst.Ref, WValue); |
| 639 | 632 | |
| ... | ... | @@ -1171,7 +1164,7 @@ pub fn generate( |
| 1171 | 1164 | liveness: Liveness, |
| 1172 | 1165 | code: *std.ArrayList(u8), |
| 1173 | 1166 | debug_output: codegen.DebugInfoOutput, |
| 1174 | ) codegen.GenerateSymbolError!codegen.FnResult { | |
| 1167 | ) codegen.GenerateSymbolError!codegen.Result { | |
| 1175 | 1168 | _ = src_loc; |
| 1176 | 1169 | var code_gen: CodeGen = .{ |
| 1177 | 1170 | .gpa = bin_file.allocator, |
| ... | ... | @@ -1190,18 +1183,18 @@ pub fn generate( |
| 1190 | 1183 | defer code_gen.deinit(); |
| 1191 | 1184 | |
| 1192 | 1185 | genFunc(&code_gen) catch |err| switch (err) { |
| 1193 | error.CodegenFail => return codegen.FnResult{ .fail = code_gen.err_msg }, | |
| 1186 | error.CodegenFail => return codegen.Result{ .fail = code_gen.err_msg }, | |
| 1194 | 1187 | else => |e| return e, |
| 1195 | 1188 | }; |
| 1196 | 1189 | |
| 1197 | return codegen.FnResult{ .appended = {} }; | |
| 1190 | return codegen.Result.ok; | |
| 1198 | 1191 | } |
| 1199 | 1192 | |
| 1200 | 1193 | fn genFunc(func: *CodeGen) InnerError!void { |
| 1201 | 1194 | const fn_info = func.decl.ty.fnInfo(); |
| 1202 | 1195 | var func_type = try genFunctype(func.gpa, fn_info.cc, fn_info.param_types, fn_info.return_type, func.target); |
| 1203 | 1196 | defer func_type.deinit(func.gpa); |
| 1204 | func.decl.fn_link.wasm.type_index = try func.bin_file.putOrGetFuncType(func_type); | |
| 1197 | func.decl.fn_link.?.type_index = try func.bin_file.putOrGetFuncType(func_type); | |
| 1205 | 1198 | |
| 1206 | 1199 | var cc_result = try func.resolveCallingConventionValues(func.decl.ty); |
| 1207 | 1200 | defer cc_result.deinit(func.gpa); |
| ... | ... | @@ -1276,10 +1269,10 @@ fn genFunc(func: *CodeGen) InnerError!void { |
| 1276 | 1269 | |
| 1277 | 1270 | var emit: Emit = .{ |
| 1278 | 1271 | .mir = mir, |
| 1279 | .bin_file = &func.bin_file.base, | |
| 1272 | .bin_file = func.bin_file, | |
| 1280 | 1273 | .code = func.code, |
| 1281 | 1274 | .locals = func.locals.items, |
| 1282 | .decl = func.decl, | |
| 1275 | .decl_index = func.decl_index, | |
| 1283 | 1276 | .dbg_output = func.debug_output, |
| 1284 | 1277 | .prev_di_line = 0, |
| 1285 | 1278 | .prev_di_column = 0, |
| ... | ... | @@ -1713,9 +1706,11 @@ fn isByRef(ty: Type, target: std.Target) bool { |
| 1713 | 1706 | return true; |
| 1714 | 1707 | }, |
| 1715 | 1708 | .Optional => { |
| 1716 | if (ty.optionalReprIsPayload()) return false; | |
| 1709 | if (ty.isPtrLikeOptional()) return false; | |
| 1717 | 1710 | var buf: Type.Payload.ElemType = undefined; |
| 1718 | return ty.optionalChild(&buf).hasRuntimeBitsIgnoreComptime(); | |
| 1711 | const pl_type = ty.optionalChild(&buf); | |
| 1712 | if (pl_type.zigTypeTag() == .ErrorSet) return false; | |
| 1713 | return pl_type.hasRuntimeBitsIgnoreComptime(); | |
| 1719 | 1714 | }, |
| 1720 | 1715 | .Pointer => { |
| 1721 | 1716 | // Slices act like struct and will be passed by reference |
| ... | ... | @@ -2122,27 +2117,31 @@ fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModif |
| 2122 | 2117 | const fn_info = fn_ty.fnInfo(); |
| 2123 | 2118 | const first_param_sret = firstParamSRet(fn_info.cc, fn_info.return_type, func.target); |
| 2124 | 2119 | |
| 2125 | const callee: ?*Decl = blk: { | |
| 2120 | const callee: ?Decl.Index = blk: { | |
| 2126 | 2121 | const func_val = func.air.value(pl_op.operand) orelse break :blk null; |
| 2127 | 2122 | const module = func.bin_file.base.options.module.?; |
| 2128 | 2123 | |
| 2129 | 2124 | if (func_val.castTag(.function)) |function| { |
| 2130 | break :blk module.declPtr(function.data.owner_decl); | |
| 2125 | _ = try func.bin_file.getOrCreateAtomForDecl(function.data.owner_decl); | |
| 2126 | break :blk function.data.owner_decl; | |
| 2131 | 2127 | } else if (func_val.castTag(.extern_fn)) |extern_fn| { |
| 2132 | 2128 | const ext_decl = module.declPtr(extern_fn.data.owner_decl); |
| 2133 | 2129 | const ext_info = ext_decl.ty.fnInfo(); |
| 2134 | 2130 | var func_type = try genFunctype(func.gpa, ext_info.cc, ext_info.param_types, ext_info.return_type, func.target); |
| 2135 | 2131 | defer func_type.deinit(func.gpa); |
| 2136 | ext_decl.fn_link.wasm.type_index = try func.bin_file.putOrGetFuncType(func_type); | |
| 2132 | const atom_index = try func.bin_file.getOrCreateAtomForDecl(extern_fn.data.owner_decl); | |
| 2133 | const atom = func.bin_file.getAtomPtr(atom_index); | |
| 2134 | ext_decl.fn_link.?.type_index = try func.bin_file.putOrGetFuncType(func_type); | |
| 2137 | 2135 | try func.bin_file.addOrUpdateImport( |
| 2138 | 2136 | mem.sliceTo(ext_decl.name, 0), |
| 2139 | ext_decl.link.wasm.sym_index, | |
| 2137 | atom.getSymbolIndex().?, | |
| 2140 | 2138 | ext_decl.getExternFn().?.lib_name, |
| 2141 | ext_decl.fn_link.wasm.type_index, | |
| 2139 | ext_decl.fn_link.?.type_index, | |
| 2142 | 2140 | ); |
| 2143 | break :blk ext_decl; | |
| 2141 | break :blk extern_fn.data.owner_decl; | |
| 2144 | 2142 | } else if (func_val.castTag(.decl_ref)) |decl_ref| { |
| 2145 | break :blk module.declPtr(decl_ref.data); | |
| 2143 | _ = try func.bin_file.getOrCreateAtomForDecl(decl_ref.data); | |
| 2144 | break :blk decl_ref.data; | |
| 2146 | 2145 | } |
| 2147 | 2146 | return func.fail("Expected a function, but instead found type '{}'", .{func_val.tag()}); |
| 2148 | 2147 | }; |
| ... | ... | @@ -2163,7 +2162,8 @@ fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModif |
| 2163 | 2162 | } |
| 2164 | 2163 | |
| 2165 | 2164 | if (callee) |direct| { |
| 2166 | try func.addLabel(.call, direct.link.wasm.sym_index); | |
| 2165 | const atom_index = func.bin_file.decls.get(direct).?; | |
| 2166 | try func.addLabel(.call, func.bin_file.getAtom(atom_index).sym_index); | |
| 2167 | 2167 | } else { |
| 2168 | 2168 | // in this case we call a function pointer |
| 2169 | 2169 | // so load its value onto the stack |
| ... | ... | @@ -2476,7 +2476,7 @@ fn airArg(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 2476 | 2476 | .dwarf => |dwarf| { |
| 2477 | 2477 | const src_index = func.air.instructions.items(.data)[inst].arg.src_index; |
| 2478 | 2478 | const name = func.mod_fn.getParamName(func.bin_file.base.options.module.?, src_index); |
| 2479 | try dwarf.genArgDbgInfo(name, arg_ty, .wasm, func.mod_fn.owner_decl, .{ | |
| 2479 | try dwarf.genArgDbgInfo(name, arg_ty, func.mod_fn.owner_decl, .{ | |
| 2480 | 2480 | .wasm_local = arg.local.value, |
| 2481 | 2481 | }); |
| 2482 | 2482 | }, |
| ... | ... | @@ -2759,8 +2759,10 @@ fn lowerDeclRefValue(func: *CodeGen, tv: TypedValue, decl_index: Module.Decl.Ind |
| 2759 | 2759 | } |
| 2760 | 2760 | |
| 2761 | 2761 | module.markDeclAlive(decl); |
| 2762 | const atom_index = try func.bin_file.getOrCreateAtomForDecl(decl_index); | |
| 2763 | const atom = func.bin_file.getAtom(atom_index); | |
| 2762 | 2764 | |
| 2763 | const target_sym_index = decl.link.wasm.sym_index; | |
| 2765 | const target_sym_index = atom.sym_index; | |
| 2764 | 2766 | if (decl.ty.zigTypeTag() == .Fn) { |
| 2765 | 2767 | try func.bin_file.addTableFunction(target_sym_index); |
| 2766 | 2768 | return WValue{ .function_index = target_sym_index }; |
| ... | ... | @@ -3869,14 +3871,20 @@ fn airIsNull(func: *CodeGen, inst: Air.Inst.Index, opcode: wasm.Opcode, op_kind: |
| 3869 | 3871 | /// NOTE: Leaves the result on the stack |
| 3870 | 3872 | fn isNull(func: *CodeGen, operand: WValue, optional_ty: Type, opcode: wasm.Opcode) InnerError!WValue { |
| 3871 | 3873 | try func.emitWValue(operand); |
| 3874 | var buf: Type.Payload.ElemType = undefined; | |
| 3875 | const payload_ty = optional_ty.optionalChild(&buf); | |
| 3872 | 3876 | if (!optional_ty.optionalReprIsPayload()) { |
| 3873 | var buf: Type.Payload.ElemType = undefined; | |
| 3874 | const payload_ty = optional_ty.optionalChild(&buf); | |
| 3875 | 3877 | // When payload is zero-bits, we can treat operand as a value, rather than |
| 3876 | 3878 | // a pointer to the stack value |
| 3877 | 3879 | if (payload_ty.hasRuntimeBitsIgnoreComptime()) { |
| 3878 | 3880 | try func.addMemArg(.i32_load8_u, .{ .offset = operand.offset(), .alignment = 1 }); |
| 3879 | 3881 | } |
| 3882 | } else if (payload_ty.isSlice()) { | |
| 3883 | switch (func.arch()) { | |
| 3884 | .wasm32 => try func.addMemArg(.i32_load, .{ .offset = operand.offset(), .alignment = 4 }), | |
| 3885 | .wasm64 => try func.addMemArg(.i64_load, .{ .offset = operand.offset(), .alignment = 8 }), | |
| 3886 | else => unreachable, | |
| 3887 | } | |
| 3880 | 3888 | } |
| 3881 | 3889 | |
| 3882 | 3890 | // Compare the null value with '0' |
| ... | ... | @@ -5539,7 +5547,7 @@ fn airDbgVar(func: *CodeGen, inst: Air.Inst.Index, is_ptr: bool) !void { |
| 5539 | 5547 | break :blk .nop; |
| 5540 | 5548 | }, |
| 5541 | 5549 | }; |
| 5542 | try func.debug_output.dwarf.genVarDbgInfo(name, ty, .wasm, func.mod_fn.owner_decl, is_ptr, loc); | |
| 5550 | try func.debug_output.dwarf.genVarDbgInfo(name, ty, func.mod_fn.owner_decl, is_ptr, loc); | |
| 5543 | 5551 | |
| 5544 | 5552 | func.finishAir(inst, .none, &.{}); |
| 5545 | 5553 | } |
src/arch/wasm/Emit.zig+18-11| ... | ... | @@ -11,8 +11,8 @@ const leb128 = std.leb; |
| 11 | 11 | |
| 12 | 12 | /// Contains our list of instructions |
| 13 | 13 | mir: Mir, |
| 14 | /// Reference to the file handler | |
| 15 | bin_file: *link.File, | |
| 14 | /// Reference to the Wasm module linker | |
| 15 | bin_file: *link.File.Wasm, | |
| 16 | 16 | /// Possible error message. When set, the value is allocated and |
| 17 | 17 | /// must be freed manually. |
| 18 | 18 | error_msg: ?*Module.ErrorMsg = null, |
| ... | ... | @@ -21,7 +21,7 @@ code: *std.ArrayList(u8), |
| 21 | 21 | /// List of allocated locals. |
| 22 | 22 | locals: []const u8, |
| 23 | 23 | /// The declaration that code is being generated for. |
| 24 | decl: *Module.Decl, | |
| 24 | decl_index: Module.Decl.Index, | |
| 25 | 25 | |
| 26 | 26 | // Debug information |
| 27 | 27 | /// Holds the debug information for this emission |
| ... | ... | @@ -252,8 +252,8 @@ fn offset(self: Emit) u32 { |
| 252 | 252 | fn fail(emit: *Emit, comptime format: []const u8, args: anytype) InnerError { |
| 253 | 253 | @setCold(true); |
| 254 | 254 | std.debug.assert(emit.error_msg == null); |
| 255 | // TODO: Determine the source location. | |
| 256 | emit.error_msg = try Module.ErrorMsg.create(emit.bin_file.allocator, emit.decl.srcLoc(), format, args); | |
| 255 | const mod = emit.bin_file.base.options.module.?; | |
| 256 | emit.error_msg = try Module.ErrorMsg.create(emit.bin_file.base.allocator, mod.declPtr(emit.decl_index).srcLoc(), format, args); | |
| 257 | 257 | return error.EmitFail; |
| 258 | 258 | } |
| 259 | 259 | |
| ... | ... | @@ -304,8 +304,9 @@ fn emitGlobal(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) !void { |
| 304 | 304 | const global_offset = emit.offset(); |
| 305 | 305 | try emit.code.appendSlice(&buf); |
| 306 | 306 | |
| 307 | // globals can have index 0 as it represents the stack pointer | |
| 308 | try emit.decl.link.wasm.relocs.append(emit.bin_file.allocator, .{ | |
| 307 | const atom_index = emit.bin_file.decls.get(emit.decl_index).?; | |
| 308 | const atom = emit.bin_file.getAtomPtr(atom_index); | |
| 309 | try atom.relocs.append(emit.bin_file.base.allocator, .{ | |
| 309 | 310 | .index = label, |
| 310 | 311 | .offset = global_offset, |
| 311 | 312 | .relocation_type = .R_WASM_GLOBAL_INDEX_LEB, |
| ... | ... | @@ -361,7 +362,9 @@ fn emitCall(emit: *Emit, inst: Mir.Inst.Index) !void { |
| 361 | 362 | try emit.code.appendSlice(&buf); |
| 362 | 363 | |
| 363 | 364 | if (label != 0) { |
| 364 | try emit.decl.link.wasm.relocs.append(emit.bin_file.allocator, .{ | |
| 365 | const atom_index = emit.bin_file.decls.get(emit.decl_index).?; | |
| 366 | const atom = emit.bin_file.getAtomPtr(atom_index); | |
| 367 | try atom.relocs.append(emit.bin_file.base.allocator, .{ | |
| 365 | 368 | .offset = call_offset, |
| 366 | 369 | .index = label, |
| 367 | 370 | .relocation_type = .R_WASM_FUNCTION_INDEX_LEB, |
| ... | ... | @@ -387,7 +390,9 @@ fn emitFunctionIndex(emit: *Emit, inst: Mir.Inst.Index) !void { |
| 387 | 390 | try emit.code.appendSlice(&buf); |
| 388 | 391 | |
| 389 | 392 | if (symbol_index != 0) { |
| 390 | try emit.decl.link.wasm.relocs.append(emit.bin_file.allocator, .{ | |
| 393 | const atom_index = emit.bin_file.decls.get(emit.decl_index).?; | |
| 394 | const atom = emit.bin_file.getAtomPtr(atom_index); | |
| 395 | try atom.relocs.append(emit.bin_file.base.allocator, .{ | |
| 391 | 396 | .offset = index_offset, |
| 392 | 397 | .index = symbol_index, |
| 393 | 398 | .relocation_type = .R_WASM_TABLE_INDEX_SLEB, |
| ... | ... | @@ -399,7 +404,7 @@ fn emitMemAddress(emit: *Emit, inst: Mir.Inst.Index) !void { |
| 399 | 404 | const extra_index = emit.mir.instructions.items(.data)[inst].payload; |
| 400 | 405 | const mem = emit.mir.extraData(Mir.Memory, extra_index).data; |
| 401 | 406 | const mem_offset = emit.offset() + 1; |
| 402 | const is_wasm32 = emit.bin_file.options.target.cpu.arch == .wasm32; | |
| 407 | const is_wasm32 = emit.bin_file.base.options.target.cpu.arch == .wasm32; | |
| 403 | 408 | if (is_wasm32) { |
| 404 | 409 | try emit.code.append(std.wasm.opcode(.i32_const)); |
| 405 | 410 | var buf: [5]u8 = undefined; |
| ... | ... | @@ -413,7 +418,9 @@ fn emitMemAddress(emit: *Emit, inst: Mir.Inst.Index) !void { |
| 413 | 418 | } |
| 414 | 419 | |
| 415 | 420 | if (mem.pointer != 0) { |
| 416 | try emit.decl.link.wasm.relocs.append(emit.bin_file.allocator, .{ | |
| 421 | const atom_index = emit.bin_file.decls.get(emit.decl_index).?; | |
| 422 | const atom = emit.bin_file.getAtomPtr(atom_index); | |
| 423 | try atom.relocs.append(emit.bin_file.base.allocator, .{ | |
| 417 | 424 | .offset = mem_offset, |
| 418 | 425 | .index = mem.pointer, |
| 419 | 426 | .relocation_type = if (is_wasm32) .R_WASM_MEMORY_ADDR_LEB else .R_WASM_MEMORY_ADDR_LEB64, |
src/arch/x86_64/CodeGen.zig+98-156| ... | ... | @@ -16,7 +16,7 @@ const Compilation = @import("../../Compilation.zig"); |
| 16 | 16 | const DebugInfoOutput = codegen.DebugInfoOutput; |
| 17 | 17 | const DW = std.dwarf; |
| 18 | 18 | const ErrorMsg = Module.ErrorMsg; |
| 19 | const FnResult = codegen.FnResult; | |
| 19 | const Result = codegen.Result; | |
| 20 | 20 | const GenerateSymbolError = codegen.GenerateSymbolError; |
| 21 | 21 | const Emit = @import("Emit.zig"); |
| 22 | 22 | const Liveness = @import("../../Liveness.zig"); |
| ... | ... | @@ -257,7 +257,7 @@ pub fn generate( |
| 257 | 257 | liveness: Liveness, |
| 258 | 258 | code: *std.ArrayList(u8), |
| 259 | 259 | debug_output: DebugInfoOutput, |
| 260 | ) GenerateSymbolError!FnResult { | |
| 260 | ) GenerateSymbolError!Result { | |
| 261 | 261 | if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) { |
| 262 | 262 | @panic("Attempted to compile for architecture that was disabled by build configuration"); |
| 263 | 263 | } |
| ... | ... | @@ -305,8 +305,8 @@ pub fn generate( |
| 305 | 305 | defer if (builtin.mode == .Debug) function.mir_to_air_map.deinit(); |
| 306 | 306 | |
| 307 | 307 | var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) { |
| 308 | error.CodegenFail => return FnResult{ .fail = function.err_msg.? }, | |
| 309 | error.OutOfRegisters => return FnResult{ | |
| 308 | error.CodegenFail => return Result{ .fail = function.err_msg.? }, | |
| 309 | error.OutOfRegisters => return Result{ | |
| 310 | 310 | .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), |
| 311 | 311 | }, |
| 312 | 312 | else => |e| return e, |
| ... | ... | @@ -319,8 +319,8 @@ pub fn generate( |
| 319 | 319 | function.max_end_stack = call_info.stack_byte_count; |
| 320 | 320 | |
| 321 | 321 | function.gen() catch |err| switch (err) { |
| 322 | error.CodegenFail => return FnResult{ .fail = function.err_msg.? }, | |
| 323 | error.OutOfRegisters => return FnResult{ | |
| 322 | error.CodegenFail => return Result{ .fail = function.err_msg.? }, | |
| 323 | error.OutOfRegisters => return Result{ | |
| 324 | 324 | .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), |
| 325 | 325 | }, |
| 326 | 326 | else => |e| return e, |
| ... | ... | @@ -345,14 +345,14 @@ pub fn generate( |
| 345 | 345 | }; |
| 346 | 346 | defer emit.deinit(); |
| 347 | 347 | emit.lowerMir() catch |err| switch (err) { |
| 348 | error.EmitFail => return FnResult{ .fail = emit.err_msg.? }, | |
| 348 | error.EmitFail => return Result{ .fail = emit.err_msg.? }, | |
| 349 | 349 | else => |e| return e, |
| 350 | 350 | }; |
| 351 | 351 | |
| 352 | 352 | if (function.err_msg) |em| { |
| 353 | return FnResult{ .fail = em }; | |
| 353 | return Result{ .fail = em }; | |
| 354 | 354 | } else { |
| 355 | return FnResult{ .appended = {} }; | |
| 355 | return Result.ok; | |
| 356 | 356 | } |
| 357 | 357 | } |
| 358 | 358 | |
| ... | ... | @@ -2668,12 +2668,13 @@ fn loadMemPtrIntoRegister(self: *Self, reg: Register, ptr_ty: Type, ptr: MCValue |
| 2668 | 2668 | switch (ptr) { |
| 2669 | 2669 | .linker_load => |load_struct| { |
| 2670 | 2670 | const abi_size = @intCast(u32, ptr_ty.abiSize(self.target.*)); |
| 2671 | const mod = self.bin_file.options.module.?; | |
| 2672 | const fn_owner_decl = mod.declPtr(self.mod_fn.owner_decl); | |
| 2673 | const atom_index = if (self.bin_file.tag == link.File.MachO.base_tag) | |
| 2674 | fn_owner_decl.link.macho.sym_index | |
| 2675 | else | |
| 2676 | fn_owner_decl.link.coff.sym_index; | |
| 2671 | const atom_index = if (self.bin_file.cast(link.File.MachO)) |macho_file| blk: { | |
| 2672 | const atom = try macho_file.getOrCreateAtomForDecl(self.mod_fn.owner_decl); | |
| 2673 | break :blk macho_file.getAtom(atom).getSymbolIndex().?; | |
| 2674 | } else if (self.bin_file.cast(link.File.Coff)) |coff_file| blk: { | |
| 2675 | const atom = try coff_file.getOrCreateAtomForDecl(self.mod_fn.owner_decl); | |
| 2676 | break :blk coff_file.getAtom(atom).getSymbolIndex().?; | |
| 2677 | } else unreachable; | |
| 2677 | 2678 | const flags: u2 = switch (load_struct.type) { |
| 2678 | 2679 | .got => 0b00, |
| 2679 | 2680 | .direct => 0b01, |
| ... | ... | @@ -3835,7 +3836,7 @@ fn genArgDbgInfo(self: Self, ty: Type, name: [:0]const u8, mcv: MCValue) !void { |
| 3835 | 3836 | }, |
| 3836 | 3837 | else => unreachable, // not a valid function parameter |
| 3837 | 3838 | }; |
| 3838 | try dw.genArgDbgInfo(name, ty, self.bin_file.tag, self.mod_fn.owner_decl, loc); | |
| 3839 | try dw.genArgDbgInfo(name, ty, self.mod_fn.owner_decl, loc); | |
| 3839 | 3840 | }, |
| 3840 | 3841 | .plan9 => {}, |
| 3841 | 3842 | .none => {}, |
| ... | ... | @@ -3875,7 +3876,7 @@ fn genVarDbgInfo( |
| 3875 | 3876 | break :blk .nop; |
| 3876 | 3877 | }, |
| 3877 | 3878 | }; |
| 3878 | try dw.genVarDbgInfo(name, ty, self.bin_file.tag, self.mod_fn.owner_decl, is_ptr, loc); | |
| 3879 | try dw.genVarDbgInfo(name, ty, self.mod_fn.owner_decl, is_ptr, loc); | |
| 3879 | 3880 | }, |
| 3880 | 3881 | .plan9 => {}, |
| 3881 | 3882 | .none => {}, |
| ... | ... | @@ -3992,49 +3993,26 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier |
| 3992 | 3993 | // Due to incremental compilation, how function calls are generated depends |
| 3993 | 3994 | // on linking. |
| 3994 | 3995 | const mod = self.bin_file.options.module.?; |
| 3995 | if (self.bin_file.cast(link.File.Elf)) |elf_file| { | |
| 3996 | if (self.air.value(callee)) |func_value| { | |
| 3997 | if (func_value.castTag(.function)) |func_payload| { | |
| 3998 | const func = func_payload.data; | |
| 3999 | const ptr_bits = self.target.cpu.arch.ptrBitWidth(); | |
| 4000 | const ptr_bytes: u64 = @divExact(ptr_bits, 8); | |
| 4001 | const fn_owner_decl = mod.declPtr(func.owner_decl); | |
| 4002 | const got_addr = blk: { | |
| 4003 | const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?]; | |
| 4004 | break :blk @intCast(u32, got.p_vaddr + fn_owner_decl.link.elf.offset_table_index * ptr_bytes); | |
| 4005 | }; | |
| 3996 | if (self.air.value(callee)) |func_value| { | |
| 3997 | if (func_value.castTag(.function)) |func_payload| { | |
| 3998 | const func = func_payload.data; | |
| 3999 | ||
| 4000 | if (self.bin_file.cast(link.File.Elf)) |elf_file| { | |
| 4001 | const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl); | |
| 4002 | const atom = elf_file.getAtom(atom_index); | |
| 4003 | const got_addr = @intCast(u32, atom.getOffsetTableAddress(elf_file)); | |
| 4006 | 4004 | _ = try self.addInst(.{ |
| 4007 | 4005 | .tag = .call, |
| 4008 | 4006 | .ops = Mir.Inst.Ops.encode(.{ .flags = 0b01 }), |
| 4009 | .data = .{ .imm = @truncate(u32, got_addr) }, | |
| 4007 | .data = .{ .imm = got_addr }, | |
| 4010 | 4008 | }); |
| 4011 | } else if (func_value.castTag(.extern_fn)) |_| { | |
| 4012 | return self.fail("TODO implement calling extern functions", .{}); | |
| 4013 | } else { | |
| 4014 | return self.fail("TODO implement calling bitcasted functions", .{}); | |
| 4015 | } | |
| 4016 | } else { | |
| 4017 | assert(ty.zigTypeTag() == .Pointer); | |
| 4018 | const mcv = try self.resolveInst(callee); | |
| 4019 | try self.genSetReg(Type.initTag(.usize), .rax, mcv); | |
| 4020 | _ = try self.addInst(.{ | |
| 4021 | .tag = .call, | |
| 4022 | .ops = Mir.Inst.Ops.encode(.{ | |
| 4023 | .reg1 = .rax, | |
| 4024 | .flags = 0b01, | |
| 4025 | }), | |
| 4026 | .data = undefined, | |
| 4027 | }); | |
| 4028 | } | |
| 4029 | } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { | |
| 4030 | if (self.air.value(callee)) |func_value| { | |
| 4031 | if (func_value.castTag(.function)) |func_payload| { | |
| 4032 | const func = func_payload.data; | |
| 4033 | const fn_owner_decl = mod.declPtr(func.owner_decl); | |
| 4009 | } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { | |
| 4010 | const atom_index = try coff_file.getOrCreateAtomForDecl(func.owner_decl); | |
| 4011 | const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?; | |
| 4034 | 4012 | try self.genSetReg(Type.initTag(.usize), .rax, .{ |
| 4035 | 4013 | .linker_load = .{ |
| 4036 | 4014 | .type = .got, |
| 4037 | .sym_index = fn_owner_decl.link.coff.sym_index, | |
| 4015 | .sym_index = sym_index, | |
| 4038 | 4016 | }, |
| 4039 | 4017 | }); |
| 4040 | 4018 | _ = try self.addInst(.{ |
| ... | ... | @@ -4045,19 +4023,12 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier |
| 4045 | 4023 | }), |
| 4046 | 4024 | .data = undefined, |
| 4047 | 4025 | }); |
| 4048 | } else if (func_value.castTag(.extern_fn)) |func_payload| { | |
| 4049 | const extern_fn = func_payload.data; | |
| 4050 | const decl_name = mod.declPtr(extern_fn.owner_decl).name; | |
| 4051 | if (extern_fn.lib_name) |lib_name| { | |
| 4052 | log.debug("TODO enforce that '{s}' is expected in '{s}' library", .{ | |
| 4053 | decl_name, | |
| 4054 | lib_name, | |
| 4055 | }); | |
| 4056 | } | |
| 4057 | const sym_index = try coff_file.getGlobalSymbol(mem.sliceTo(decl_name, 0)); | |
| 4026 | } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { | |
| 4027 | const atom_index = try macho_file.getOrCreateAtomForDecl(func.owner_decl); | |
| 4028 | const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?; | |
| 4058 | 4029 | try self.genSetReg(Type.initTag(.usize), .rax, .{ |
| 4059 | 4030 | .linker_load = .{ |
| 4060 | .type = .import, | |
| 4031 | .type = .got, | |
| 4061 | 4032 | .sym_index = sym_index, |
| 4062 | 4033 | }, |
| 4063 | 4034 | }); |
| ... | ... | @@ -4069,35 +4040,38 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier |
| 4069 | 4040 | }), |
| 4070 | 4041 | .data = undefined, |
| 4071 | 4042 | }); |
| 4072 | } else { | |
| 4073 | return self.fail("TODO implement calling bitcasted functions", .{}); | |
| 4043 | } else if (self.bin_file.cast(link.File.Plan9)) |p9| { | |
| 4044 | const decl_block_index = try p9.seeDecl(func.owner_decl); | |
| 4045 | const decl_block = p9.getDeclBlock(decl_block_index); | |
| 4046 | const ptr_bits = self.target.cpu.arch.ptrBitWidth(); | |
| 4047 | const ptr_bytes: u64 = @divExact(ptr_bits, 8); | |
| 4048 | const got_addr = p9.bases.data; | |
| 4049 | const got_index = decl_block.got_index.?; | |
| 4050 | const fn_got_addr = got_addr + got_index * ptr_bytes; | |
| 4051 | _ = try self.addInst(.{ | |
| 4052 | .tag = .call, | |
| 4053 | .ops = Mir.Inst.Ops.encode(.{ .flags = 0b01 }), | |
| 4054 | .data = .{ .imm = @intCast(u32, fn_got_addr) }, | |
| 4055 | }); | |
| 4056 | } else unreachable; | |
| 4057 | } else if (func_value.castTag(.extern_fn)) |func_payload| { | |
| 4058 | const extern_fn = func_payload.data; | |
| 4059 | const decl_name = mod.declPtr(extern_fn.owner_decl).name; | |
| 4060 | if (extern_fn.lib_name) |lib_name| { | |
| 4061 | log.debug("TODO enforce that '{s}' is expected in '{s}' library", .{ | |
| 4062 | decl_name, | |
| 4063 | lib_name, | |
| 4064 | }); | |
| 4074 | 4065 | } |
| 4075 | } else { | |
| 4076 | assert(ty.zigTypeTag() == .Pointer); | |
| 4077 | const mcv = try self.resolveInst(callee); | |
| 4078 | try self.genSetReg(Type.initTag(.usize), .rax, mcv); | |
| 4079 | _ = try self.addInst(.{ | |
| 4080 | .tag = .call, | |
| 4081 | .ops = Mir.Inst.Ops.encode(.{ | |
| 4082 | .reg1 = .rax, | |
| 4083 | .flags = 0b01, | |
| 4084 | }), | |
| 4085 | .data = undefined, | |
| 4086 | }); | |
| 4087 | } | |
| 4088 | } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { | |
| 4089 | if (self.air.value(callee)) |func_value| { | |
| 4090 | if (func_value.castTag(.function)) |func_payload| { | |
| 4091 | const func = func_payload.data; | |
| 4092 | const fn_owner_decl = mod.declPtr(func.owner_decl); | |
| 4093 | const sym_index = fn_owner_decl.link.macho.sym_index; | |
| 4066 | ||
| 4067 | if (self.bin_file.cast(link.File.Coff)) |coff_file| { | |
| 4068 | const sym_index = try coff_file.getGlobalSymbol(mem.sliceTo(decl_name, 0)); | |
| 4094 | 4069 | try self.genSetReg(Type.initTag(.usize), .rax, .{ |
| 4095 | 4070 | .linker_load = .{ |
| 4096 | .type = .got, | |
| 4071 | .type = .import, | |
| 4097 | 4072 | .sym_index = sym_index, |
| 4098 | 4073 | }, |
| 4099 | 4074 | }); |
| 4100 | // callq *%rax | |
| 4101 | 4075 | _ = try self.addInst(.{ |
| 4102 | 4076 | .tag = .call, |
| 4103 | 4077 | .ops = Mir.Inst.Ops.encode(.{ |
| ... | ... | @@ -4106,71 +4080,37 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier |
| 4106 | 4080 | }), |
| 4107 | 4081 | .data = undefined, |
| 4108 | 4082 | }); |
| 4109 | } else if (func_value.castTag(.extern_fn)) |func_payload| { | |
| 4110 | const extern_fn = func_payload.data; | |
| 4111 | const decl_name = mod.declPtr(extern_fn.owner_decl).name; | |
| 4112 | if (extern_fn.lib_name) |lib_name| { | |
| 4113 | log.debug("TODO enforce that '{s}' is expected in '{s}' library", .{ | |
| 4114 | decl_name, | |
| 4115 | lib_name, | |
| 4116 | }); | |
| 4117 | } | |
| 4083 | } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { | |
| 4118 | 4084 | const sym_index = try macho_file.getGlobalSymbol(mem.sliceTo(decl_name, 0)); |
| 4085 | const atom = try macho_file.getOrCreateAtomForDecl(self.mod_fn.owner_decl); | |
| 4086 | const atom_index = macho_file.getAtom(atom).getSymbolIndex().?; | |
| 4119 | 4087 | _ = try self.addInst(.{ |
| 4120 | 4088 | .tag = .call_extern, |
| 4121 | 4089 | .ops = undefined, |
| 4122 | .data = .{ | |
| 4123 | .relocation = .{ | |
| 4124 | .atom_index = mod.declPtr(self.mod_fn.owner_decl).link.macho.sym_index, | |
| 4125 | .sym_index = sym_index, | |
| 4126 | }, | |
| 4127 | }, | |
| 4090 | .data = .{ .relocation = .{ | |
| 4091 | .atom_index = atom_index, | |
| 4092 | .sym_index = sym_index, | |
| 4093 | } }, | |
| 4128 | 4094 | }); |
| 4129 | 4095 | } else { |
| 4130 | return self.fail("TODO implement calling bitcasted functions", .{}); | |
| 4096 | return self.fail("TODO implement calling extern functions", .{}); | |
| 4131 | 4097 | } |
| 4132 | 4098 | } else { |
| 4133 | assert(ty.zigTypeTag() == .Pointer); | |
| 4134 | const mcv = try self.resolveInst(callee); | |
| 4135 | try self.genSetReg(Type.initTag(.usize), .rax, mcv); | |
| 4136 | _ = try self.addInst(.{ | |
| 4137 | .tag = .call, | |
| 4138 | .ops = Mir.Inst.Ops.encode(.{ | |
| 4139 | .reg1 = .rax, | |
| 4140 | .flags = 0b01, | |
| 4141 | }), | |
| 4142 | .data = undefined, | |
| 4143 | }); | |
| 4099 | return self.fail("TODO implement calling bitcasted functions", .{}); | |
| 4144 | 4100 | } |
| 4145 | } else if (self.bin_file.cast(link.File.Plan9)) |p9| { | |
| 4146 | if (self.air.value(callee)) |func_value| { | |
| 4147 | if (func_value.castTag(.function)) |func_payload| { | |
| 4148 | try p9.seeDecl(func_payload.data.owner_decl); | |
| 4149 | const ptr_bits = self.target.cpu.arch.ptrBitWidth(); | |
| 4150 | const ptr_bytes: u64 = @divExact(ptr_bits, 8); | |
| 4151 | const got_addr = p9.bases.data; | |
| 4152 | const got_index = mod.declPtr(func_payload.data.owner_decl).link.plan9.got_index.?; | |
| 4153 | const fn_got_addr = got_addr + got_index * ptr_bytes; | |
| 4154 | _ = try self.addInst(.{ | |
| 4155 | .tag = .call, | |
| 4156 | .ops = Mir.Inst.Ops.encode(.{ .flags = 0b01 }), | |
| 4157 | .data = .{ .imm = @intCast(u32, fn_got_addr) }, | |
| 4158 | }); | |
| 4159 | } else return self.fail("TODO implement calling extern fn on plan9", .{}); | |
| 4160 | } else { | |
| 4161 | assert(ty.zigTypeTag() == .Pointer); | |
| 4162 | const mcv = try self.resolveInst(callee); | |
| 4163 | try self.genSetReg(Type.initTag(.usize), .rax, mcv); | |
| 4164 | _ = try self.addInst(.{ | |
| 4165 | .tag = .call, | |
| 4166 | .ops = Mir.Inst.Ops.encode(.{ | |
| 4167 | .reg1 = .rax, | |
| 4168 | .flags = 0b01, | |
| 4169 | }), | |
| 4170 | .data = undefined, | |
| 4171 | }); | |
| 4172 | } | |
| 4173 | } else unreachable; | |
| 4101 | } else { | |
| 4102 | assert(ty.zigTypeTag() == .Pointer); | |
| 4103 | const mcv = try self.resolveInst(callee); | |
| 4104 | try self.genSetReg(Type.initTag(.usize), .rax, mcv); | |
| 4105 | _ = try self.addInst(.{ | |
| 4106 | .tag = .call, | |
| 4107 | .ops = Mir.Inst.Ops.encode(.{ | |
| 4108 | .reg1 = .rax, | |
| 4109 | .flags = 0b01, | |
| 4110 | }), | |
| 4111 | .data = undefined, | |
| 4112 | }); | |
| 4113 | } | |
| 4174 | 4114 | |
| 4175 | 4115 | if (info.stack_byte_count > 0) { |
| 4176 | 4116 | // Readjust the stack |
| ... | ... | @@ -6781,24 +6721,27 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne |
| 6781 | 6721 | module.markDeclAlive(decl); |
| 6782 | 6722 | |
| 6783 | 6723 | if (self.bin_file.cast(link.File.Elf)) |elf_file| { |
| 6784 | const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?]; | |
| 6785 | const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes; | |
| 6786 | return MCValue{ .memory = got_addr }; | |
| 6787 | } else if (self.bin_file.cast(link.File.MachO)) |_| { | |
| 6788 | assert(decl.link.macho.sym_index != 0); | |
| 6724 | const atom_index = try elf_file.getOrCreateAtomForDecl(decl_index); | |
| 6725 | const atom = elf_file.getAtom(atom_index); | |
| 6726 | return MCValue{ .memory = atom.getOffsetTableAddress(elf_file) }; | |
| 6727 | } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { | |
| 6728 | const atom_index = try macho_file.getOrCreateAtomForDecl(decl_index); | |
| 6729 | const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?; | |
| 6789 | 6730 | return MCValue{ .linker_load = .{ |
| 6790 | 6731 | .type = .got, |
| 6791 | .sym_index = decl.link.macho.sym_index, | |
| 6732 | .sym_index = sym_index, | |
| 6792 | 6733 | } }; |
| 6793 | } else if (self.bin_file.cast(link.File.Coff)) |_| { | |
| 6794 | assert(decl.link.coff.sym_index != 0); | |
| 6734 | } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { | |
| 6735 | const atom_index = try coff_file.getOrCreateAtomForDecl(decl_index); | |
| 6736 | const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?; | |
| 6795 | 6737 | return MCValue{ .linker_load = .{ |
| 6796 | 6738 | .type = .got, |
| 6797 | .sym_index = decl.link.coff.sym_index, | |
| 6739 | .sym_index = sym_index, | |
| 6798 | 6740 | } }; |
| 6799 | 6741 | } else if (self.bin_file.cast(link.File.Plan9)) |p9| { |
| 6800 | try p9.seeDecl(decl_index); | |
| 6801 | const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes; | |
| 6742 | const decl_block_index = try p9.seeDecl(decl_index); | |
| 6743 | const decl_block = p9.getDeclBlock(decl_block_index); | |
| 6744 | const got_addr = p9.bases.data + decl_block.got_index.? * ptr_bytes; | |
| 6802 | 6745 | return MCValue{ .memory = got_addr }; |
| 6803 | 6746 | } else { |
| 6804 | 6747 | return self.fail("TODO codegen non-ELF const Decl pointer", .{}); |
| ... | ... | @@ -6811,8 +6754,7 @@ fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue { |
| 6811 | 6754 | return self.fail("lowering unnamed constant failed: {s}", .{@errorName(err)}); |
| 6812 | 6755 | }; |
| 6813 | 6756 | if (self.bin_file.cast(link.File.Elf)) |elf_file| { |
| 6814 | const vaddr = elf_file.local_symbols.items[local_sym_index].st_value; | |
| 6815 | return MCValue{ .memory = vaddr }; | |
| 6757 | return MCValue{ .memory = elf_file.getSymbol(local_sym_index).st_value }; | |
| 6816 | 6758 | } else if (self.bin_file.cast(link.File.MachO)) |_| { |
| 6817 | 6759 | return MCValue{ .linker_load = .{ |
| 6818 | 6760 | .type = .direct, |
src/arch/x86_64/Emit.zig+8-8| ... | ... | @@ -1001,8 +1001,8 @@ fn mirLeaPic(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| 1001 | 1001 | 0b01 => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_SIGNED), |
| 1002 | 1002 | else => unreachable, |
| 1003 | 1003 | }; |
| 1004 | const atom = macho_file.getAtomForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?; | |
| 1005 | try atom.addRelocation(macho_file, .{ | |
| 1004 | const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?; | |
| 1005 | try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{ | |
| 1006 | 1006 | .type = reloc_type, |
| 1007 | 1007 | .target = .{ .sym_index = relocation.sym_index, .file = null }, |
| 1008 | 1008 | .offset = @intCast(u32, end_offset - 4), |
| ... | ... | @@ -1011,8 +1011,8 @@ fn mirLeaPic(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| 1011 | 1011 | .length = 2, |
| 1012 | 1012 | }); |
| 1013 | 1013 | } else if (emit.bin_file.cast(link.File.Coff)) |coff_file| { |
| 1014 | const atom = coff_file.getAtomForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?; | |
| 1015 | try atom.addRelocation(coff_file, .{ | |
| 1014 | const atom_index = coff_file.getAtomIndexForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?; | |
| 1015 | try link.File.Coff.Atom.addRelocation(coff_file, atom_index, .{ | |
| 1016 | 1016 | .type = switch (ops.flags) { |
| 1017 | 1017 | 0b00 => .got, |
| 1018 | 1018 | 0b01 => .direct, |
| ... | ... | @@ -1140,9 +1140,9 @@ fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| 1140 | 1140 | |
| 1141 | 1141 | if (emit.bin_file.cast(link.File.MachO)) |macho_file| { |
| 1142 | 1142 | // Add relocation to the decl. |
| 1143 | const atom = macho_file.getAtomForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?; | |
| 1143 | const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?; | |
| 1144 | 1144 | const target = macho_file.getGlobalByIndex(relocation.sym_index); |
| 1145 | try atom.addRelocation(macho_file, .{ | |
| 1145 | try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{ | |
| 1146 | 1146 | .type = @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_BRANCH), |
| 1147 | 1147 | .target = target, |
| 1148 | 1148 | .offset = offset, |
| ... | ... | @@ -1152,9 +1152,9 @@ fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| 1152 | 1152 | }); |
| 1153 | 1153 | } else if (emit.bin_file.cast(link.File.Coff)) |coff_file| { |
| 1154 | 1154 | // Add relocation to the decl. |
| 1155 | const atom = coff_file.getAtomForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?; | |
| 1155 | const atom_index = coff_file.getAtomIndexForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?; | |
| 1156 | 1156 | const target = coff_file.getGlobalByIndex(relocation.sym_index); |
| 1157 | try atom.addRelocation(coff_file, .{ | |
| 1157 | try link.File.Coff.Atom.addRelocation(coff_file, atom_index, .{ | |
| 1158 | 1158 | .type = .direct, |
| 1159 | 1159 | .target = target, |
| 1160 | 1160 | .offset = offset, |
src/codegen.zig+58-131| ... | ... | @@ -21,16 +21,11 @@ const TypedValue = @import("TypedValue.zig"); |
| 21 | 21 | const Value = @import("value.zig").Value; |
| 22 | 22 | const Zir = @import("Zir.zig"); |
| 23 | 23 | |
| 24 | pub const FnResult = union(enum) { | |
| 25 | /// The `code` parameter passed to `generateSymbol` has the value appended. | |
| 26 | appended: void, | |
| 27 | fail: *ErrorMsg, | |
| 28 | }; | |
| 29 | 24 | pub const Result = union(enum) { |
| 30 | /// The `code` parameter passed to `generateSymbol` has the value appended. | |
| 31 | appended: void, | |
| 32 | /// The value is available externally, `code` is unused. | |
| 33 | externally_managed: []const u8, | |
| 25 | /// The `code` parameter passed to `generateSymbol` has the value ok. | |
| 26 | ok: void, | |
| 27 | ||
| 28 | /// There was a codegen error. | |
| 34 | 29 | fail: *ErrorMsg, |
| 35 | 30 | }; |
| 36 | 31 | |
| ... | ... | @@ -89,7 +84,7 @@ pub fn generateFunction( |
| 89 | 84 | liveness: Liveness, |
| 90 | 85 | code: *std.ArrayList(u8), |
| 91 | 86 | debug_output: DebugInfoOutput, |
| 92 | ) GenerateSymbolError!FnResult { | |
| 87 | ) GenerateSymbolError!Result { | |
| 93 | 88 | switch (bin_file.options.target.cpu.arch) { |
| 94 | 89 | .arm, |
| 95 | 90 | .armeb, |
| ... | ... | @@ -145,7 +140,7 @@ pub fn generateSymbol( |
| 145 | 140 | if (typed_value.val.isUndefDeep()) { |
| 146 | 141 | const abi_size = math.cast(usize, typed_value.ty.abiSize(target)) orelse return error.Overflow; |
| 147 | 142 | try code.appendNTimes(0xaa, abi_size); |
| 148 | return Result{ .appended = {} }; | |
| 143 | return Result.ok; | |
| 149 | 144 | } |
| 150 | 145 | |
| 151 | 146 | switch (typed_value.ty.zigTypeTag()) { |
| ... | ... | @@ -176,7 +171,7 @@ pub fn generateSymbol( |
| 176 | 171 | 128 => writeFloat(f128, typed_value.val.toFloat(f128), target, endian, try code.addManyAsArray(16)), |
| 177 | 172 | else => unreachable, |
| 178 | 173 | } |
| 179 | return Result{ .appended = {} }; | |
| 174 | return Result.ok; | |
| 180 | 175 | }, |
| 181 | 176 | .Array => switch (typed_value.val.tag()) { |
| 182 | 177 | .bytes => { |
| ... | ... | @@ -185,7 +180,7 @@ pub fn generateSymbol( |
| 185 | 180 | // The bytes payload already includes the sentinel, if any |
| 186 | 181 | try code.ensureUnusedCapacity(len); |
| 187 | 182 | code.appendSliceAssumeCapacity(bytes[0..len]); |
| 188 | return Result{ .appended = {} }; | |
| 183 | return Result.ok; | |
| 189 | 184 | }, |
| 190 | 185 | .str_lit => { |
| 191 | 186 | const str_lit = typed_value.val.castTag(.str_lit).?.data; |
| ... | ... | @@ -197,7 +192,7 @@ pub fn generateSymbol( |
| 197 | 192 | const byte = @intCast(u8, sent_val.toUnsignedInt(target)); |
| 198 | 193 | code.appendAssumeCapacity(byte); |
| 199 | 194 | } |
| 200 | return Result{ .appended = {} }; | |
| 195 | return Result.ok; | |
| 201 | 196 | }, |
| 202 | 197 | .aggregate => { |
| 203 | 198 | const elem_vals = typed_value.val.castTag(.aggregate).?.data; |
| ... | ... | @@ -208,14 +203,11 @@ pub fn generateSymbol( |
| 208 | 203 | .ty = elem_ty, |
| 209 | 204 | .val = elem_val, |
| 210 | 205 | }, code, debug_output, reloc_info)) { |
| 211 | .appended => {}, | |
| 212 | .externally_managed => |slice| { | |
| 213 | code.appendSliceAssumeCapacity(slice); | |
| 214 | }, | |
| 206 | .ok => {}, | |
| 215 | 207 | .fail => |em| return Result{ .fail = em }, |
| 216 | 208 | } |
| 217 | 209 | } |
| 218 | return Result{ .appended = {} }; | |
| 210 | return Result.ok; | |
| 219 | 211 | }, |
| 220 | 212 | .repeated => { |
| 221 | 213 | const array = typed_value.val.castTag(.repeated).?.data; |
| ... | ... | @@ -229,10 +221,7 @@ pub fn generateSymbol( |
| 229 | 221 | .ty = elem_ty, |
| 230 | 222 | .val = array, |
| 231 | 223 | }, code, debug_output, reloc_info)) { |
| 232 | .appended => {}, | |
| 233 | .externally_managed => |slice| { | |
| 234 | code.appendSliceAssumeCapacity(slice); | |
| 235 | }, | |
| 224 | .ok => {}, | |
| 236 | 225 | .fail => |em| return Result{ .fail = em }, |
| 237 | 226 | } |
| 238 | 227 | } |
| ... | ... | @@ -242,15 +231,12 @@ pub fn generateSymbol( |
| 242 | 231 | .ty = elem_ty, |
| 243 | 232 | .val = sentinel_val, |
| 244 | 233 | }, code, debug_output, reloc_info)) { |
| 245 | .appended => {}, | |
| 246 | .externally_managed => |slice| { | |
| 247 | code.appendSliceAssumeCapacity(slice); | |
| 248 | }, | |
| 234 | .ok => {}, | |
| 249 | 235 | .fail => |em| return Result{ .fail = em }, |
| 250 | 236 | } |
| 251 | 237 | } |
| 252 | 238 | |
| 253 | return Result{ .appended = {} }; | |
| 239 | return Result.ok; | |
| 254 | 240 | }, |
| 255 | 241 | .empty_array_sentinel => { |
| 256 | 242 | const elem_ty = typed_value.ty.childType(); |
| ... | ... | @@ -259,13 +245,10 @@ pub fn generateSymbol( |
| 259 | 245 | .ty = elem_ty, |
| 260 | 246 | .val = sentinel_val, |
| 261 | 247 | }, code, debug_output, reloc_info)) { |
| 262 | .appended => {}, | |
| 263 | .externally_managed => |slice| { | |
| 264 | code.appendSliceAssumeCapacity(slice); | |
| 265 | }, | |
| 248 | .ok => {}, | |
| 266 | 249 | .fail => |em| return Result{ .fail = em }, |
| 267 | 250 | } |
| 268 | return Result{ .appended = {} }; | |
| 251 | return Result.ok; | |
| 269 | 252 | }, |
| 270 | 253 | else => return Result{ |
| 271 | 254 | .fail = try ErrorMsg.create( |
| ... | ... | @@ -289,7 +272,7 @@ pub fn generateSymbol( |
| 289 | 272 | }, |
| 290 | 273 | else => unreachable, |
| 291 | 274 | } |
| 292 | return Result{ .appended = {} }; | |
| 275 | return Result.ok; | |
| 293 | 276 | }, |
| 294 | 277 | .variable => { |
| 295 | 278 | const decl = typed_value.val.castTag(.variable).?.data.owner_decl; |
| ... | ... | @@ -309,10 +292,7 @@ pub fn generateSymbol( |
| 309 | 292 | .ty = slice_ptr_field_type, |
| 310 | 293 | .val = slice.ptr, |
| 311 | 294 | }, code, debug_output, reloc_info)) { |
| 312 | .appended => {}, | |
| 313 | .externally_managed => |external_slice| { | |
| 314 | code.appendSliceAssumeCapacity(external_slice); | |
| 315 | }, | |
| 295 | .ok => {}, | |
| 316 | 296 | .fail => |em| return Result{ .fail = em }, |
| 317 | 297 | } |
| 318 | 298 | |
| ... | ... | @@ -321,14 +301,11 @@ pub fn generateSymbol( |
| 321 | 301 | .ty = Type.initTag(.usize), |
| 322 | 302 | .val = slice.len, |
| 323 | 303 | }, code, debug_output, reloc_info)) { |
| 324 | .appended => {}, | |
| 325 | .externally_managed => |external_slice| { | |
| 326 | code.appendSliceAssumeCapacity(external_slice); | |
| 327 | }, | |
| 304 | .ok => {}, | |
| 328 | 305 | .fail => |em| return Result{ .fail = em }, |
| 329 | 306 | } |
| 330 | 307 | |
| 331 | return Result{ .appended = {} }; | |
| 308 | return Result.ok; | |
| 332 | 309 | }, |
| 333 | 310 | .field_ptr => { |
| 334 | 311 | const field_ptr = typed_value.val.castTag(.field_ptr).?.data; |
| ... | ... | @@ -375,13 +352,10 @@ pub fn generateSymbol( |
| 375 | 352 | .ty = typed_value.ty, |
| 376 | 353 | .val = container_ptr, |
| 377 | 354 | }, code, debug_output, reloc_info)) { |
| 378 | .appended => {}, | |
| 379 | .externally_managed => |external_slice| { | |
| 380 | code.appendSliceAssumeCapacity(external_slice); | |
| 381 | }, | |
| 355 | .ok => {}, | |
| 382 | 356 | .fail => |em| return Result{ .fail = em }, |
| 383 | 357 | } |
| 384 | return Result{ .appended = {} }; | |
| 358 | return Result.ok; | |
| 385 | 359 | }, |
| 386 | 360 | else => return Result{ |
| 387 | 361 | .fail = try ErrorMsg.create( |
| ... | ... | @@ -434,7 +408,7 @@ pub fn generateSymbol( |
| 434 | 408 | .signed => @bitCast(u8, @intCast(i8, typed_value.val.toSignedInt(target))), |
| 435 | 409 | }; |
| 436 | 410 | try code.append(x); |
| 437 | return Result{ .appended = {} }; | |
| 411 | return Result.ok; | |
| 438 | 412 | } |
| 439 | 413 | if (info.bits > 64) { |
| 440 | 414 | var bigint_buffer: Value.BigIntSpace = undefined; |
| ... | ... | @@ -443,7 +417,7 @@ pub fn generateSymbol( |
| 443 | 417 | const start = code.items.len; |
| 444 | 418 | try code.resize(start + abi_size); |
| 445 | 419 | bigint.writeTwosComplement(code.items[start..][0..abi_size], endian); |
| 446 | return Result{ .appended = {} }; | |
| 420 | return Result.ok; | |
| 447 | 421 | } |
| 448 | 422 | switch (info.signedness) { |
| 449 | 423 | .unsigned => { |
| ... | ... | @@ -471,7 +445,7 @@ pub fn generateSymbol( |
| 471 | 445 | } |
| 472 | 446 | }, |
| 473 | 447 | } |
| 474 | return Result{ .appended = {} }; | |
| 448 | return Result.ok; | |
| 475 | 449 | }, |
| 476 | 450 | .Enum => { |
| 477 | 451 | var int_buffer: Value.Payload.U64 = undefined; |
| ... | ... | @@ -481,7 +455,7 @@ pub fn generateSymbol( |
| 481 | 455 | if (info.bits <= 8) { |
| 482 | 456 | const x = @intCast(u8, int_val.toUnsignedInt(target)); |
| 483 | 457 | try code.append(x); |
| 484 | return Result{ .appended = {} }; | |
| 458 | return Result.ok; | |
| 485 | 459 | } |
| 486 | 460 | if (info.bits > 64) { |
| 487 | 461 | return Result{ |
| ... | ... | @@ -519,12 +493,12 @@ pub fn generateSymbol( |
| 519 | 493 | } |
| 520 | 494 | }, |
| 521 | 495 | } |
| 522 | return Result{ .appended = {} }; | |
| 496 | return Result.ok; | |
| 523 | 497 | }, |
| 524 | 498 | .Bool => { |
| 525 | 499 | const x: u8 = @boolToInt(typed_value.val.toBool()); |
| 526 | 500 | try code.append(x); |
| 527 | return Result{ .appended = {} }; | |
| 501 | return Result.ok; | |
| 528 | 502 | }, |
| 529 | 503 | .Struct => { |
| 530 | 504 | if (typed_value.ty.containerLayout() == .Packed) { |
| ... | ... | @@ -549,12 +523,7 @@ pub fn generateSymbol( |
| 549 | 523 | .ty = field_ty, |
| 550 | 524 | .val = field_val, |
| 551 | 525 | }, &tmp_list, debug_output, reloc_info)) { |
| 552 | .appended => { | |
| 553 | mem.copy(u8, code.items[current_pos..], tmp_list.items); | |
| 554 | }, | |
| 555 | .externally_managed => |external_slice| { | |
| 556 | mem.copy(u8, code.items[current_pos..], external_slice); | |
| 557 | }, | |
| 526 | .ok => mem.copy(u8, code.items[current_pos..], tmp_list.items), | |
| 558 | 527 | .fail => |em| return Result{ .fail = em }, |
| 559 | 528 | } |
| 560 | 529 | } else { |
| ... | ... | @@ -563,7 +532,7 @@ pub fn generateSymbol( |
| 563 | 532 | bits += @intCast(u16, field_ty.bitSize(target)); |
| 564 | 533 | } |
| 565 | 534 | |
| 566 | return Result{ .appended = {} }; | |
| 535 | return Result.ok; | |
| 567 | 536 | } |
| 568 | 537 | |
| 569 | 538 | const struct_begin = code.items.len; |
| ... | ... | @@ -576,10 +545,7 @@ pub fn generateSymbol( |
| 576 | 545 | .ty = field_ty, |
| 577 | 546 | .val = field_val, |
| 578 | 547 | }, code, debug_output, reloc_info)) { |
| 579 | .appended => {}, | |
| 580 | .externally_managed => |external_slice| { | |
| 581 | code.appendSliceAssumeCapacity(external_slice); | |
| 582 | }, | |
| 548 | .ok => {}, | |
| 583 | 549 | .fail => |em| return Result{ .fail = em }, |
| 584 | 550 | } |
| 585 | 551 | const unpadded_field_end = code.items.len - struct_begin; |
| ... | ... | @@ -593,7 +559,7 @@ pub fn generateSymbol( |
| 593 | 559 | } |
| 594 | 560 | } |
| 595 | 561 | |
| 596 | return Result{ .appended = {} }; | |
| 562 | return Result.ok; | |
| 597 | 563 | }, |
| 598 | 564 | .Union => { |
| 599 | 565 | const union_obj = typed_value.val.castTag(.@"union").?.data; |
| ... | ... | @@ -612,10 +578,7 @@ pub fn generateSymbol( |
| 612 | 578 | .ty = typed_value.ty.unionTagType().?, |
| 613 | 579 | .val = union_obj.tag, |
| 614 | 580 | }, code, debug_output, reloc_info)) { |
| 615 | .appended => {}, | |
| 616 | .externally_managed => |external_slice| { | |
| 617 | code.appendSliceAssumeCapacity(external_slice); | |
| 618 | }, | |
| 581 | .ok => {}, | |
| 619 | 582 | .fail => |em| return Result{ .fail = em }, |
| 620 | 583 | } |
| 621 | 584 | } |
| ... | ... | @@ -632,10 +595,7 @@ pub fn generateSymbol( |
| 632 | 595 | .ty = field_ty, |
| 633 | 596 | .val = union_obj.val, |
| 634 | 597 | }, code, debug_output, reloc_info)) { |
| 635 | .appended => {}, | |
| 636 | .externally_managed => |external_slice| { | |
| 637 | code.appendSliceAssumeCapacity(external_slice); | |
| 638 | }, | |
| 598 | .ok => {}, | |
| 639 | 599 | .fail => |em| return Result{ .fail = em }, |
| 640 | 600 | } |
| 641 | 601 | |
| ... | ... | @@ -650,15 +610,12 @@ pub fn generateSymbol( |
| 650 | 610 | .ty = union_ty.tag_ty, |
| 651 | 611 | .val = union_obj.tag, |
| 652 | 612 | }, code, debug_output, reloc_info)) { |
| 653 | .appended => {}, | |
| 654 | .externally_managed => |external_slice| { | |
| 655 | code.appendSliceAssumeCapacity(external_slice); | |
| 656 | }, | |
| 613 | .ok => {}, | |
| 657 | 614 | .fail => |em| return Result{ .fail = em }, |
| 658 | 615 | } |
| 659 | 616 | } |
| 660 | 617 | |
| 661 | return Result{ .appended = {} }; | |
| 618 | return Result.ok; | |
| 662 | 619 | }, |
| 663 | 620 | .Optional => { |
| 664 | 621 | var opt_buf: Type.Payload.ElemType = undefined; |
| ... | ... | @@ -669,7 +626,7 @@ pub fn generateSymbol( |
| 669 | 626 | |
| 670 | 627 | if (!payload_type.hasRuntimeBits()) { |
| 671 | 628 | try code.writer().writeByteNTimes(@boolToInt(is_pl), abi_size); |
| 672 | return Result{ .appended = {} }; | |
| 629 | return Result.ok; | |
| 673 | 630 | } |
| 674 | 631 | |
| 675 | 632 | if (typed_value.ty.optionalReprIsPayload()) { |
| ... | ... | @@ -678,10 +635,7 @@ pub fn generateSymbol( |
| 678 | 635 | .ty = payload_type, |
| 679 | 636 | .val = payload.data, |
| 680 | 637 | }, code, debug_output, reloc_info)) { |
| 681 | .appended => {}, | |
| 682 | .externally_managed => |external_slice| { | |
| 683 | code.appendSliceAssumeCapacity(external_slice); | |
| 684 | }, | |
| 638 | .ok => {}, | |
| 685 | 639 | .fail => |em| return Result{ .fail = em }, |
| 686 | 640 | } |
| 687 | 641 | } else if (!typed_value.val.isNull()) { |
| ... | ... | @@ -689,17 +643,14 @@ pub fn generateSymbol( |
| 689 | 643 | .ty = payload_type, |
| 690 | 644 | .val = typed_value.val, |
| 691 | 645 | }, code, debug_output, reloc_info)) { |
| 692 | .appended => {}, | |
| 693 | .externally_managed => |external_slice| { | |
| 694 | code.appendSliceAssumeCapacity(external_slice); | |
| 695 | }, | |
| 646 | .ok => {}, | |
| 696 | 647 | .fail => |em| return Result{ .fail = em }, |
| 697 | 648 | } |
| 698 | 649 | } else { |
| 699 | 650 | try code.writer().writeByteNTimes(0, abi_size); |
| 700 | 651 | } |
| 701 | 652 | |
| 702 | return Result{ .appended = {} }; | |
| 653 | return Result.ok; | |
| 703 | 654 | } |
| 704 | 655 | |
| 705 | 656 | const value = if (typed_value.val.castTag(.opt_payload)) |payload| payload.data else Value.initTag(.undef); |
| ... | ... | @@ -708,14 +659,11 @@ pub fn generateSymbol( |
| 708 | 659 | .ty = payload_type, |
| 709 | 660 | .val = value, |
| 710 | 661 | }, code, debug_output, reloc_info)) { |
| 711 | .appended => {}, | |
| 712 | .externally_managed => |external_slice| { | |
| 713 | code.appendSliceAssumeCapacity(external_slice); | |
| 714 | }, | |
| 662 | .ok => {}, | |
| 715 | 663 | .fail => |em| return Result{ .fail = em }, |
| 716 | 664 | } |
| 717 | 665 | |
| 718 | return Result{ .appended = {} }; | |
| 666 | return Result.ok; | |
| 719 | 667 | }, |
| 720 | 668 | .ErrorUnion => { |
| 721 | 669 | const error_ty = typed_value.ty.errorUnionSet(); |
| ... | ... | @@ -740,10 +688,7 @@ pub fn generateSymbol( |
| 740 | 688 | .ty = error_ty, |
| 741 | 689 | .val = if (is_payload) Value.initTag(.zero) else typed_value.val, |
| 742 | 690 | }, code, debug_output, reloc_info)) { |
| 743 | .appended => {}, | |
| 744 | .externally_managed => |external_slice| { | |
| 745 | code.appendSliceAssumeCapacity(external_slice); | |
| 746 | }, | |
| 691 | .ok => {}, | |
| 747 | 692 | .fail => |em| return Result{ .fail = em }, |
| 748 | 693 | } |
| 749 | 694 | } |
| ... | ... | @@ -756,10 +701,7 @@ pub fn generateSymbol( |
| 756 | 701 | .ty = payload_ty, |
| 757 | 702 | .val = payload_val, |
| 758 | 703 | }, code, debug_output, reloc_info)) { |
| 759 | .appended => {}, | |
| 760 | .externally_managed => |external_slice| { | |
| 761 | code.appendSliceAssumeCapacity(external_slice); | |
| 762 | }, | |
| 704 | .ok => {}, | |
| 763 | 705 | .fail => |em| return Result{ .fail = em }, |
| 764 | 706 | } |
| 765 | 707 | const unpadded_end = code.items.len - begin; |
| ... | ... | @@ -778,10 +720,7 @@ pub fn generateSymbol( |
| 778 | 720 | .ty = error_ty, |
| 779 | 721 | .val = if (is_payload) Value.initTag(.zero) else typed_value.val, |
| 780 | 722 | }, code, debug_output, reloc_info)) { |
| 781 | .appended => {}, | |
| 782 | .externally_managed => |external_slice| { | |
| 783 | code.appendSliceAssumeCapacity(external_slice); | |
| 784 | }, | |
| 723 | .ok => {}, | |
| 785 | 724 | .fail => |em| return Result{ .fail = em }, |
| 786 | 725 | } |
| 787 | 726 | const unpadded_end = code.items.len - begin; |
| ... | ... | @@ -793,7 +732,7 @@ pub fn generateSymbol( |
| 793 | 732 | } |
| 794 | 733 | } |
| 795 | 734 | |
| 796 | return Result{ .appended = {} }; | |
| 735 | return Result.ok; | |
| 797 | 736 | }, |
| 798 | 737 | .ErrorSet => { |
| 799 | 738 | switch (typed_value.val.tag()) { |
| ... | ... | @@ -806,7 +745,7 @@ pub fn generateSymbol( |
| 806 | 745 | try code.writer().writeByteNTimes(0, @intCast(usize, Type.anyerror.abiSize(target))); |
| 807 | 746 | }, |
| 808 | 747 | } |
| 809 | return Result{ .appended = {} }; | |
| 748 | return Result.ok; | |
| 810 | 749 | }, |
| 811 | 750 | .Vector => switch (typed_value.val.tag()) { |
| 812 | 751 | .bytes => { |
| ... | ... | @@ -814,7 +753,7 @@ pub fn generateSymbol( |
| 814 | 753 | const len = @intCast(usize, typed_value.ty.arrayLen()); |
| 815 | 754 | try code.ensureUnusedCapacity(len); |
| 816 | 755 | code.appendSliceAssumeCapacity(bytes[0..len]); |
| 817 | return Result{ .appended = {} }; | |
| 756 | return Result.ok; | |
| 818 | 757 | }, |
| 819 | 758 | .aggregate => { |
| 820 | 759 | const elem_vals = typed_value.val.castTag(.aggregate).?.data; |
| ... | ... | @@ -825,14 +764,11 @@ pub fn generateSymbol( |
| 825 | 764 | .ty = elem_ty, |
| 826 | 765 | .val = elem_val, |
| 827 | 766 | }, code, debug_output, reloc_info)) { |
| 828 | .appended => {}, | |
| 829 | .externally_managed => |slice| { | |
| 830 | code.appendSliceAssumeCapacity(slice); | |
| 831 | }, | |
| 767 | .ok => {}, | |
| 832 | 768 | .fail => |em| return Result{ .fail = em }, |
| 833 | 769 | } |
| 834 | 770 | } |
| 835 | return Result{ .appended = {} }; | |
| 771 | return Result.ok; | |
| 836 | 772 | }, |
| 837 | 773 | .repeated => { |
| 838 | 774 | const array = typed_value.val.castTag(.repeated).?.data; |
| ... | ... | @@ -845,14 +781,11 @@ pub fn generateSymbol( |
| 845 | 781 | .ty = elem_ty, |
| 846 | 782 | .val = array, |
| 847 | 783 | }, code, debug_output, reloc_info)) { |
| 848 | .appended => {}, | |
| 849 | .externally_managed => |slice| { | |
| 850 | code.appendSliceAssumeCapacity(slice); | |
| 851 | }, | |
| 784 | .ok => {}, | |
| 852 | 785 | .fail => |em| return Result{ .fail = em }, |
| 853 | 786 | } |
| 854 | 787 | } |
| 855 | return Result{ .appended = {} }; | |
| 788 | return Result.ok; | |
| 856 | 789 | }, |
| 857 | 790 | .str_lit => { |
| 858 | 791 | const str_lit = typed_value.val.castTag(.str_lit).?.data; |
| ... | ... | @@ -860,7 +793,7 @@ pub fn generateSymbol( |
| 860 | 793 | const bytes = mod.string_literal_bytes.items[str_lit.index..][0..str_lit.len]; |
| 861 | 794 | try code.ensureUnusedCapacity(str_lit.len); |
| 862 | 795 | code.appendSliceAssumeCapacity(bytes); |
| 863 | return Result{ .appended = {} }; | |
| 796 | return Result.ok; | |
| 864 | 797 | }, |
| 865 | 798 | else => unreachable, |
| 866 | 799 | }, |
| ... | ... | @@ -901,10 +834,7 @@ fn lowerDeclRef( |
| 901 | 834 | .ty = slice_ptr_field_type, |
| 902 | 835 | .val = typed_value.val, |
| 903 | 836 | }, code, debug_output, reloc_info)) { |
| 904 | .appended => {}, | |
| 905 | .externally_managed => |external_slice| { | |
| 906 | code.appendSliceAssumeCapacity(external_slice); | |
| 907 | }, | |
| 837 | .ok => {}, | |
| 908 | 838 | .fail => |em| return Result{ .fail = em }, |
| 909 | 839 | } |
| 910 | 840 | |
| ... | ... | @@ -917,14 +847,11 @@ fn lowerDeclRef( |
| 917 | 847 | .ty = Type.usize, |
| 918 | 848 | .val = Value.initPayload(&slice_len.base), |
| 919 | 849 | }, code, debug_output, reloc_info)) { |
| 920 | .appended => {}, | |
| 921 | .externally_managed => |external_slice| { | |
| 922 | code.appendSliceAssumeCapacity(external_slice); | |
| 923 | }, | |
| 850 | .ok => {}, | |
| 924 | 851 | .fail => |em| return Result{ .fail = em }, |
| 925 | 852 | } |
| 926 | 853 | |
| 927 | return Result{ .appended = {} }; | |
| 854 | return Result.ok; | |
| 928 | 855 | } |
| 929 | 856 | |
| 930 | 857 | const ptr_width = target.cpu.arch.ptrBitWidth(); |
| ... | ... | @@ -932,7 +859,7 @@ fn lowerDeclRef( |
| 932 | 859 | const is_fn_body = decl.ty.zigTypeTag() == .Fn; |
| 933 | 860 | if (!is_fn_body and !decl.ty.hasRuntimeBits()) { |
| 934 | 861 | try code.writer().writeByteNTimes(0xaa, @divExact(ptr_width, 8)); |
| 935 | return Result{ .appended = {} }; | |
| 862 | return Result.ok; | |
| 936 | 863 | } |
| 937 | 864 | |
| 938 | 865 | module.markDeclAlive(decl); |
| ... | ... | @@ -950,7 +877,7 @@ fn lowerDeclRef( |
| 950 | 877 | else => unreachable, |
| 951 | 878 | } |
| 952 | 879 | |
| 953 | return Result{ .appended = {} }; | |
| 880 | return Result.ok; | |
| 954 | 881 | } |
| 955 | 882 | |
| 956 | 883 | pub fn errUnionPayloadOffset(payload_ty: Type, target: std.Target) u64 { |
src/codegen/c.zig+48-22| ... | ... | @@ -16,7 +16,6 @@ const trace = @import("../tracy.zig").trace; |
| 16 | 16 | const LazySrcLoc = Module.LazySrcLoc; |
| 17 | 17 | const Air = @import("../Air.zig"); |
| 18 | 18 | const Liveness = @import("../Liveness.zig"); |
| 19 | const CType = @import("../type.zig").CType; | |
| 20 | 19 | |
| 21 | 20 | const target_util = @import("../target.zig"); |
| 22 | 21 | const libcFloatPrefix = target_util.libcFloatPrefix; |
| ... | ... | @@ -1663,6 +1662,22 @@ pub const DeclGen = struct { |
| 1663 | 1662 | defer buffer.deinit(); |
| 1664 | 1663 | |
| 1665 | 1664 | try buffer.appendSlice("struct "); |
| 1665 | ||
| 1666 | var needs_pack_attr = false; | |
| 1667 | { | |
| 1668 | var it = t.structFields().iterator(); | |
| 1669 | while (it.next()) |field| { | |
| 1670 | const field_ty = field.value_ptr.ty; | |
| 1671 | if (!field_ty.hasRuntimeBits()) continue; | |
| 1672 | const alignment = field.value_ptr.abi_align; | |
| 1673 | if (alignment != 0 and alignment < field_ty.abiAlignment(dg.module.getTarget())) { | |
| 1674 | needs_pack_attr = true; | |
| 1675 | try buffer.appendSlice("zig_packed("); | |
| 1676 | break; | |
| 1677 | } | |
| 1678 | } | |
| 1679 | } | |
| 1680 | ||
| 1666 | 1681 | try buffer.appendSlice(name); |
| 1667 | 1682 | try buffer.appendSlice(" {\n"); |
| 1668 | 1683 | { |
| ... | ... | @@ -1672,7 +1687,7 @@ pub const DeclGen = struct { |
| 1672 | 1687 | const field_ty = field.value_ptr.ty; |
| 1673 | 1688 | if (!field_ty.hasRuntimeBits()) continue; |
| 1674 | 1689 | |
| 1675 | const alignment = field.value_ptr.abi_align; | |
| 1690 | const alignment = field.value_ptr.alignment(dg.module.getTarget(), t.containerLayout()); | |
| 1676 | 1691 | const field_name = CValue{ .identifier = field.key_ptr.* }; |
| 1677 | 1692 | try buffer.append(' '); |
| 1678 | 1693 | try dg.renderTypeAndName(buffer.writer(), field_ty, field_name, .Mut, alignment, .Complete); |
| ... | ... | @@ -1682,7 +1697,7 @@ pub const DeclGen = struct { |
| 1682 | 1697 | } |
| 1683 | 1698 | if (empty) try buffer.appendSlice(" char empty_struct;\n"); |
| 1684 | 1699 | } |
| 1685 | try buffer.appendSlice("};\n"); | |
| 1700 | if (needs_pack_attr) try buffer.appendSlice("});\n") else try buffer.appendSlice("};\n"); | |
| 1686 | 1701 | |
| 1687 | 1702 | const rendered = try buffer.toOwnedSlice(); |
| 1688 | 1703 | errdefer dg.typedefs.allocator.free(rendered); |
| ... | ... | @@ -2367,8 +2382,13 @@ pub const DeclGen = struct { |
| 2367 | 2382 | depth += 1; |
| 2368 | 2383 | } |
| 2369 | 2384 | |
| 2370 | if (alignment != 0 and alignment > ty.abiAlignment(target)) { | |
| 2371 | try w.print("zig_align({}) ", .{alignment}); | |
| 2385 | if (alignment != 0) { | |
| 2386 | const abi_alignment = ty.abiAlignment(target); | |
| 2387 | if (alignment < abi_alignment) { | |
| 2388 | try w.print("zig_under_align({}) ", .{alignment}); | |
| 2389 | } else if (alignment > abi_alignment) { | |
| 2390 | try w.print("zig_align({}) ", .{alignment}); | |
| 2391 | } | |
| 2372 | 2392 | } |
| 2373 | 2393 | try dg.renderType(w, render_ty, kind); |
| 2374 | 2394 | |
| ... | ... | @@ -2860,27 +2880,30 @@ pub fn genDecl(o: *Object) !void { |
| 2860 | 2880 | const w = o.writer(); |
| 2861 | 2881 | if (!is_global) try w.writeAll("static "); |
| 2862 | 2882 | if (variable.is_threadlocal) try w.writeAll("zig_threadlocal "); |
| 2883 | if (o.dg.decl.@"linksection") |section| try w.print("zig_linksection(\"{s}\", ", .{section}); | |
| 2863 | 2884 | try o.dg.renderTypeAndName(w, o.dg.decl.ty, decl_c_value, .Mut, o.dg.decl.@"align", .Complete); |
| 2885 | if (o.dg.decl.@"linksection" != null) try w.writeAll(", read, write)"); | |
| 2864 | 2886 | try w.writeAll(" = "); |
| 2865 | 2887 | try o.dg.renderValue(w, tv.ty, variable.init, .StaticInitializer); |
| 2866 | 2888 | try w.writeByte(';'); |
| 2867 | 2889 | try o.indent_writer.insertNewline(); |
| 2868 | 2890 | } else { |
| 2891 | const is_global = o.dg.module.decl_exports.contains(o.dg.decl_index); | |
| 2892 | const fwd_decl_writer = o.dg.fwd_decl.writer(); | |
| 2869 | 2893 | const decl_c_value: CValue = .{ .decl = o.dg.decl_index }; |
| 2870 | 2894 | |
| 2871 | const fwd_decl_writer = o.dg.fwd_decl.writer(); | |
| 2872 | try fwd_decl_writer.writeAll("static "); | |
| 2873 | try o.dg.renderTypeAndName(fwd_decl_writer, tv.ty, decl_c_value, .Mut, o.dg.decl.@"align", .Complete); | |
| 2895 | try fwd_decl_writer.writeAll(if (is_global) "zig_extern " else "static "); | |
| 2896 | try o.dg.renderTypeAndName(fwd_decl_writer, tv.ty, decl_c_value, .Const, o.dg.decl.@"align", .Complete); | |
| 2874 | 2897 | try fwd_decl_writer.writeAll(";\n"); |
| 2875 | 2898 | |
| 2876 | const writer = o.writer(); | |
| 2877 | try writer.writeAll("static "); | |
| 2878 | // TODO ask the Decl if it is const | |
| 2879 | // https://github.com/ziglang/zig/issues/7582 | |
| 2880 | try o.dg.renderTypeAndName(writer, tv.ty, decl_c_value, .Mut, o.dg.decl.@"align", .Complete); | |
| 2881 | try writer.writeAll(" = "); | |
| 2882 | try o.dg.renderValue(writer, tv.ty, tv.val, .StaticInitializer); | |
| 2883 | try writer.writeAll(";\n"); | |
| 2899 | const w = o.writer(); | |
| 2900 | if (!is_global) try w.writeAll("static "); | |
| 2901 | if (o.dg.decl.@"linksection") |section| try w.print("zig_linksection(\"{s}\", ", .{section}); | |
| 2902 | try o.dg.renderTypeAndName(w, tv.ty, decl_c_value, .Const, o.dg.decl.@"align", .Complete); | |
| 2903 | if (o.dg.decl.@"linksection" != null) try w.writeAll(", read)"); | |
| 2904 | try w.writeAll(" = "); | |
| 2905 | try o.dg.renderValue(w, tv.ty, tv.val, .StaticInitializer); | |
| 2906 | try w.writeAll(";\n"); | |
| 2884 | 2907 | } |
| 2885 | 2908 | } |
| 2886 | 2909 | |
| ... | ... | @@ -3726,16 +3749,15 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3726 | 3749 | |
| 3727 | 3750 | const ptr_val = try f.resolveInst(bin_op.lhs); |
| 3728 | 3751 | const src_ty = f.air.typeOf(bin_op.rhs); |
| 3729 | const src_val = try f.resolveInst(bin_op.rhs); | |
| 3730 | ||
| 3731 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); | |
| 3732 | 3752 | |
| 3733 | 3753 | // TODO Sema should emit a different instruction when the store should |
| 3734 | 3754 | // possibly do the safety 0xaa bytes for undefined. |
| 3735 | 3755 | const src_val_is_undefined = |
| 3736 | 3756 | if (f.air.value(bin_op.rhs)) |v| v.isUndefDeep() else false; |
| 3737 | if (src_val_is_undefined) | |
| 3757 | if (src_val_is_undefined) { | |
| 3758 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); | |
| 3738 | 3759 | return try storeUndefined(f, ptr_info.pointee_type, ptr_val); |
| 3760 | } | |
| 3739 | 3761 | |
| 3740 | 3762 | const target = f.object.dg.module.getTarget(); |
| 3741 | 3763 | const is_aligned = ptr_info.@"align" == 0 or |
| ... | ... | @@ -3744,6 +3766,9 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3744 | 3766 | const need_memcpy = !is_aligned or is_array; |
| 3745 | 3767 | const writer = f.object.writer(); |
| 3746 | 3768 | |
| 3769 | const src_val = try f.resolveInst(bin_op.rhs); | |
| 3770 | try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); | |
| 3771 | ||
| 3747 | 3772 | if (need_memcpy) { |
| 3748 | 3773 | // For this memcpy to safely work we need the rhs to have the same |
| 3749 | 3774 | // underlying type as the lhs (i.e. they must both be arrays of the same underlying type). |
| ... | ... | @@ -4344,8 +4369,9 @@ fn airDbgInline(f: *Function, inst: Air.Inst.Index) !CValue { |
| 4344 | 4369 | fn airDbgVar(f: *Function, inst: Air.Inst.Index) !CValue { |
| 4345 | 4370 | const pl_op = f.air.instructions.items(.data)[inst].pl_op; |
| 4346 | 4371 | const name = f.air.nullTerminatedString(pl_op.payload); |
| 4347 | const operand = try f.resolveInst(pl_op.operand); | |
| 4348 | _ = operand; | |
| 4372 | const operand_is_undef = if (f.air.value(pl_op.operand)) |v| v.isUndefDeep() else false; | |
| 4373 | if (!operand_is_undef) _ = try f.resolveInst(pl_op.operand); | |
| 4374 | ||
| 4349 | 4375 | try reap(f, inst, &.{pl_op.operand}); |
| 4350 | 4376 | const writer = f.object.writer(); |
| 4351 | 4377 | try writer.print("/* var:{s} */\n", .{name}); |
src/codegen/llvm.zig+2-3| ... | ... | @@ -19,7 +19,6 @@ const Liveness = @import("../Liveness.zig"); |
| 19 | 19 | const Value = @import("../value.zig").Value; |
| 20 | 20 | const Type = @import("../type.zig").Type; |
| 21 | 21 | const LazySrcLoc = Module.LazySrcLoc; |
| 22 | const CType = @import("../type.zig").CType; | |
| 23 | 22 | const x86_64_abi = @import("../arch/x86_64/abi.zig"); |
| 24 | 23 | const wasm_c_abi = @import("../arch/wasm/abi.zig"); |
| 25 | 24 | const aarch64_c_abi = @import("../arch/aarch64/abi.zig"); |
| ... | ... | @@ -11057,8 +11056,8 @@ fn backendSupportsF128(target: std.Target) bool { |
| 11057 | 11056 | fn intrinsicsAllowed(scalar_ty: Type, target: std.Target) bool { |
| 11058 | 11057 | return switch (scalar_ty.tag()) { |
| 11059 | 11058 | .f16 => backendSupportsF16(target), |
| 11060 | .f80 => (CType.longdouble.sizeInBits(target) == 80) and backendSupportsF80(target), | |
| 11061 | .f128 => (CType.longdouble.sizeInBits(target) == 128) and backendSupportsF128(target), | |
| 11059 | .f80 => (target.c_type_bit_size(.longdouble) == 80) and backendSupportsF80(target), | |
| 11060 | .f128 => (target.c_type_bit_size(.longdouble) == 128) and backendSupportsF128(target), | |
| 11062 | 11061 | else => true, |
| 11063 | 11062 | }; |
| 11064 | 11063 | } |
src/codegen/spirv.zig+19-11| ... | ... | @@ -49,7 +49,7 @@ pub const DeclGen = struct { |
| 49 | 49 | spv: *SpvModule, |
| 50 | 50 | |
| 51 | 51 | /// The decl we are currently generating code for. |
| 52 | decl: *Decl, | |
| 52 | decl_index: Decl.Index, | |
| 53 | 53 | |
| 54 | 54 | /// The intermediate code of the declaration we are currently generating. Note: If |
| 55 | 55 | /// the declaration is not a function, this value will be undefined! |
| ... | ... | @@ -59,6 +59,8 @@ pub const DeclGen = struct { |
| 59 | 59 | /// Note: If the declaration is not a function, this value will be undefined! |
| 60 | 60 | liveness: Liveness, |
| 61 | 61 | |
| 62 | ids: *const std.AutoHashMap(Decl.Index, IdResult), | |
| 63 | ||
| 62 | 64 | /// An array of function argument result-ids. Each index corresponds with the |
| 63 | 65 | /// function argument of the same index. |
| 64 | 66 | args: std.ArrayListUnmanaged(IdRef) = .{}, |
| ... | ... | @@ -133,14 +135,20 @@ pub const DeclGen = struct { |
| 133 | 135 | |
| 134 | 136 | /// Initialize the common resources of a DeclGen. Some fields are left uninitialized, |
| 135 | 137 | /// only set when `gen` is called. |
| 136 | pub fn init(allocator: Allocator, module: *Module, spv: *SpvModule) DeclGen { | |
| 138 | pub fn init( | |
| 139 | allocator: Allocator, | |
| 140 | module: *Module, | |
| 141 | spv: *SpvModule, | |
| 142 | ids: *const std.AutoHashMap(Decl.Index, IdResult), | |
| 143 | ) DeclGen { | |
| 137 | 144 | return .{ |
| 138 | 145 | .gpa = allocator, |
| 139 | 146 | .module = module, |
| 140 | 147 | .spv = spv, |
| 141 | .decl = undefined, | |
| 148 | .decl_index = undefined, | |
| 142 | 149 | .air = undefined, |
| 143 | 150 | .liveness = undefined, |
| 151 | .ids = ids, | |
| 144 | 152 | .next_arg_index = undefined, |
| 145 | 153 | .current_block_label_id = undefined, |
| 146 | 154 | .error_msg = undefined, |
| ... | ... | @@ -150,9 +158,9 @@ pub const DeclGen = struct { |
| 150 | 158 | /// Generate the code for `decl`. If a reportable error occurred during code generation, |
| 151 | 159 | /// a message is returned by this function. Callee owns the memory. If this function |
| 152 | 160 | /// returns such a reportable error, it is valid to be called again for a different decl. |
| 153 | pub fn gen(self: *DeclGen, decl: *Decl, air: Air, liveness: Liveness) !?*Module.ErrorMsg { | |
| 161 | pub fn gen(self: *DeclGen, decl_index: Decl.Index, air: Air, liveness: Liveness) !?*Module.ErrorMsg { | |
| 154 | 162 | // Reset internal resources, we don't want to re-allocate these. |
| 155 | self.decl = decl; | |
| 163 | self.decl_index = decl_index; | |
| 156 | 164 | self.air = air; |
| 157 | 165 | self.liveness = liveness; |
| 158 | 166 | self.args.items.len = 0; |
| ... | ... | @@ -194,7 +202,7 @@ pub const DeclGen = struct { |
| 194 | 202 | pub fn fail(self: *DeclGen, comptime format: []const u8, args: anytype) Error { |
| 195 | 203 | @setCold(true); |
| 196 | 204 | const src = LazySrcLoc.nodeOffset(0); |
| 197 | const src_loc = src.toSrcLoc(self.decl); | |
| 205 | const src_loc = src.toSrcLoc(self.module.declPtr(self.decl_index)); | |
| 198 | 206 | assert(self.error_msg == null); |
| 199 | 207 | self.error_msg = try Module.ErrorMsg.create(self.module.gpa, src_loc, format, args); |
| 200 | 208 | return error.CodegenFail; |
| ... | ... | @@ -332,7 +340,7 @@ pub const DeclGen = struct { |
| 332 | 340 | }; |
| 333 | 341 | const decl = self.module.declPtr(fn_decl_index); |
| 334 | 342 | self.module.markDeclAlive(decl); |
| 335 | return decl.fn_link.spirv.id.toRef(); | |
| 343 | return self.ids.get(fn_decl_index).?.toRef(); | |
| 336 | 344 | } |
| 337 | 345 | |
| 338 | 346 | const target = self.getTarget(); |
| ... | ... | @@ -553,8 +561,8 @@ pub const DeclGen = struct { |
| 553 | 561 | } |
| 554 | 562 | |
| 555 | 563 | fn genDecl(self: *DeclGen) !void { |
| 556 | const decl = self.decl; | |
| 557 | const result_id = decl.fn_link.spirv.id; | |
| 564 | const result_id = self.ids.get(self.decl_index).?; | |
| 565 | const decl = self.module.declPtr(self.decl_index); | |
| 558 | 566 | |
| 559 | 567 | if (decl.val.castTag(.function)) |_| { |
| 560 | 568 | assert(decl.ty.zigTypeTag() == .Fn); |
| ... | ... | @@ -945,7 +953,7 @@ pub const DeclGen = struct { |
| 945 | 953 | |
| 946 | 954 | fn airDbgStmt(self: *DeclGen, inst: Air.Inst.Index) !void { |
| 947 | 955 | const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt; |
| 948 | const src_fname_id = try self.spv.resolveSourceFileName(self.decl); | |
| 956 | const src_fname_id = try self.spv.resolveSourceFileName(self.module.declPtr(self.decl_index)); | |
| 949 | 957 | try self.func.body.emit(self.spv.gpa, .OpLine, .{ |
| 950 | 958 | .file = src_fname_id, |
| 951 | 959 | .line = dbg_stmt.line, |
| ... | ... | @@ -1106,7 +1114,7 @@ pub const DeclGen = struct { |
| 1106 | 1114 | assert(as.errors.items.len != 0); |
| 1107 | 1115 | assert(self.error_msg == null); |
| 1108 | 1116 | const loc = LazySrcLoc.nodeOffset(0); |
| 1109 | const src_loc = loc.toSrcLoc(self.decl); | |
| 1117 | const src_loc = loc.toSrcLoc(self.module.declPtr(self.decl_index)); | |
| 1110 | 1118 | self.error_msg = try Module.ErrorMsg.create(self.module.gpa, src_loc, "failed to assemble SPIR-V inline assembly", .{}); |
| 1111 | 1119 | const notes = try self.module.gpa.alloc(Module.ErrorMsg, as.errors.items.len); |
| 1112 | 1120 |
src/link.zig+14-69| ... | ... | @@ -261,39 +261,6 @@ pub const File = struct { |
| 261 | 261 | /// of this linking operation. |
| 262 | 262 | lock: ?Cache.Lock = null, |
| 263 | 263 | |
| 264 | pub const LinkBlock = union { | |
| 265 | elf: Elf.TextBlock, | |
| 266 | coff: Coff.Atom, | |
| 267 | macho: MachO.Atom, | |
| 268 | plan9: Plan9.DeclBlock, | |
| 269 | c: void, | |
| 270 | wasm: Wasm.DeclBlock, | |
| 271 | spirv: void, | |
| 272 | nvptx: void, | |
| 273 | }; | |
| 274 | ||
| 275 | pub const LinkFn = union { | |
| 276 | elf: Dwarf.SrcFn, | |
| 277 | coff: Coff.SrcFn, | |
| 278 | macho: Dwarf.SrcFn, | |
| 279 | plan9: void, | |
| 280 | c: void, | |
| 281 | wasm: Wasm.FnData, | |
| 282 | spirv: SpirV.FnData, | |
| 283 | nvptx: void, | |
| 284 | }; | |
| 285 | ||
| 286 | pub const Export = union { | |
| 287 | elf: Elf.Export, | |
| 288 | coff: Coff.Export, | |
| 289 | macho: MachO.Export, | |
| 290 | plan9: Plan9.Export, | |
| 291 | c: void, | |
| 292 | wasm: Wasm.Export, | |
| 293 | spirv: void, | |
| 294 | nvptx: void, | |
| 295 | }; | |
| 296 | ||
| 297 | 264 | /// Attempts incremental linking, if the file already exists. If |
| 298 | 265 | /// incremental linking fails, falls back to truncating the file and |
| 299 | 266 | /// rewriting it. A malicious file is detected as incremental link failure |
| ... | ... | @@ -533,8 +500,7 @@ pub const File = struct { |
| 533 | 500 | } |
| 534 | 501 | } |
| 535 | 502 | |
| 536 | /// May be called before or after updateDeclExports but must be called | |
| 537 | /// after allocateDeclIndexes for any given Decl. | |
| 503 | /// May be called before or after updateDeclExports for any given Decl. | |
| 538 | 504 | pub fn updateDecl(base: *File, module: *Module, decl_index: Module.Decl.Index) UpdateDeclError!void { |
| 539 | 505 | const decl = module.declPtr(decl_index); |
| 540 | 506 | log.debug("updateDecl {*} ({s}), type={}", .{ decl, decl.name, decl.ty.fmtDebug() }); |
| ... | ... | @@ -557,8 +523,7 @@ pub const File = struct { |
| 557 | 523 | } |
| 558 | 524 | } |
| 559 | 525 | |
| 560 | /// May be called before or after updateDeclExports but must be called | |
| 561 | /// after allocateDeclIndexes for any given Decl. | |
| 526 | /// May be called before or after updateDeclExports for any given Decl. | |
| 562 | 527 | pub fn updateFunc(base: *File, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) UpdateDeclError!void { |
| 563 | 528 | const owner_decl = module.declPtr(func.owner_decl); |
| 564 | 529 | log.debug("updateFunc {*} ({s}), type={}", .{ |
| ... | ... | @@ -582,48 +547,27 @@ pub const File = struct { |
| 582 | 547 | } |
| 583 | 548 | } |
| 584 | 549 | |
| 585 | pub fn updateDeclLineNumber(base: *File, module: *Module, decl: *Module.Decl) UpdateDeclError!void { | |
| 550 | pub fn updateDeclLineNumber(base: *File, module: *Module, decl_index: Module.Decl.Index) UpdateDeclError!void { | |
| 551 | const decl = module.declPtr(decl_index); | |
| 586 | 552 | log.debug("updateDeclLineNumber {*} ({s}), line={}", .{ |
| 587 | 553 | decl, decl.name, decl.src_line + 1, |
| 588 | 554 | }); |
| 589 | 555 | assert(decl.has_tv); |
| 590 | 556 | if (build_options.only_c) { |
| 591 | 557 | assert(base.tag == .c); |
| 592 | return @fieldParentPtr(C, "base", base).updateDeclLineNumber(module, decl); | |
| 558 | return @fieldParentPtr(C, "base", base).updateDeclLineNumber(module, decl_index); | |
| 593 | 559 | } |
| 594 | 560 | switch (base.tag) { |
| 595 | .coff => return @fieldParentPtr(Coff, "base", base).updateDeclLineNumber(module, decl), | |
| 596 | .elf => return @fieldParentPtr(Elf, "base", base).updateDeclLineNumber(module, decl), | |
| 597 | .macho => return @fieldParentPtr(MachO, "base", base).updateDeclLineNumber(module, decl), | |
| 598 | .c => return @fieldParentPtr(C, "base", base).updateDeclLineNumber(module, decl), | |
| 599 | .wasm => return @fieldParentPtr(Wasm, "base", base).updateDeclLineNumber(module, decl), | |
| 600 | .plan9 => return @fieldParentPtr(Plan9, "base", base).updateDeclLineNumber(module, decl), | |
| 561 | .coff => return @fieldParentPtr(Coff, "base", base).updateDeclLineNumber(module, decl_index), | |
| 562 | .elf => return @fieldParentPtr(Elf, "base", base).updateDeclLineNumber(module, decl_index), | |
| 563 | .macho => return @fieldParentPtr(MachO, "base", base).updateDeclLineNumber(module, decl_index), | |
| 564 | .c => return @fieldParentPtr(C, "base", base).updateDeclLineNumber(module, decl_index), | |
| 565 | .wasm => return @fieldParentPtr(Wasm, "base", base).updateDeclLineNumber(module, decl_index), | |
| 566 | .plan9 => return @fieldParentPtr(Plan9, "base", base).updateDeclLineNumber(module, decl_index), | |
| 601 | 567 | .spirv, .nvptx => {}, |
| 602 | 568 | } |
| 603 | 569 | } |
| 604 | 570 | |
| 605 | /// Must be called before any call to updateDecl or updateDeclExports for | |
| 606 | /// any given Decl. | |
| 607 | /// TODO we're transitioning to deleting this function and instead having | |
| 608 | /// each linker backend notice the first time updateDecl or updateFunc is called, or | |
| 609 | /// a callee referenced from AIR. | |
| 610 | pub fn allocateDeclIndexes(base: *File, decl_index: Module.Decl.Index) error{OutOfMemory}!void { | |
| 611 | const decl = base.options.module.?.declPtr(decl_index); | |
| 612 | log.debug("allocateDeclIndexes {*} ({s})", .{ decl, decl.name }); | |
| 613 | if (build_options.only_c) { | |
| 614 | assert(base.tag == .c); | |
| 615 | return; | |
| 616 | } | |
| 617 | switch (base.tag) { | |
| 618 | .coff => return @fieldParentPtr(Coff, "base", base).allocateDeclIndexes(decl_index), | |
| 619 | .elf => return @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl_index), | |
| 620 | .macho => return @fieldParentPtr(MachO, "base", base).allocateDeclIndexes(decl_index), | |
| 621 | .wasm => return @fieldParentPtr(Wasm, "base", base).allocateDeclIndexes(decl_index), | |
| 622 | .plan9 => return @fieldParentPtr(Plan9, "base", base).allocateDeclIndexes(decl_index), | |
| 623 | .c, .spirv, .nvptx => {}, | |
| 624 | } | |
| 625 | } | |
| 626 | ||
| 627 | 571 | pub fn releaseLock(self: *File) void { |
| 628 | 572 | if (self.lock) |*lock| { |
| 629 | 573 | lock.release(); |
| ... | ... | @@ -874,8 +818,7 @@ pub const File = struct { |
| 874 | 818 | AnalysisFail, |
| 875 | 819 | }; |
| 876 | 820 | |
| 877 | /// May be called before or after updateDecl, but must be called after | |
| 878 | /// allocateDeclIndexes for any given Decl. | |
| 821 | /// May be called before or after updateDecl for any given Decl. | |
| 879 | 822 | pub fn updateDeclExports( |
| 880 | 823 | base: *File, |
| 881 | 824 | module: *Module, |
| ... | ... | @@ -911,6 +854,8 @@ pub const File = struct { |
| 911 | 854 | /// The linker is passed information about the containing atom, `parent_atom_index`, and offset within it's |
| 912 | 855 | /// memory buffer, `offset`, so that it can make a note of potential relocation sites, should the |
| 913 | 856 | /// `Decl`'s address was not yet resolved, or the containing atom gets moved in virtual memory. |
| 857 | /// May be called before or after updateFunc/updateDecl therefore it is up to the linker to allocate | |
| 858 | /// the block/atom. | |
| 914 | 859 | pub fn getDeclVAddr(base: *File, decl_index: Module.Decl.Index, reloc_info: RelocInfo) !u64 { |
| 915 | 860 | if (build_options.only_c) unreachable; |
| 916 | 861 | switch (base.tag) { |
src/link/C.zig+2-2| ... | ... | @@ -219,12 +219,12 @@ pub fn updateDecl(self: *C, module: *Module, decl_index: Module.Decl.Index) !voi |
| 219 | 219 | code.shrinkAndFree(module.gpa, code.items.len); |
| 220 | 220 | } |
| 221 | 221 | |
| 222 | pub fn updateDeclLineNumber(self: *C, module: *Module, decl: *Module.Decl) !void { | |
| 222 | pub fn updateDeclLineNumber(self: *C, module: *Module, decl_index: Module.Decl.Index) !void { | |
| 223 | 223 | // The C backend does not have the ability to fix line numbers without re-generating |
| 224 | 224 | // the entire Decl. |
| 225 | 225 | _ = self; |
| 226 | 226 | _ = module; |
| 227 | _ = decl; | |
| 227 | _ = decl_index; | |
| 228 | 228 | } |
| 229 | 229 | |
| 230 | 230 | pub fn flush(self: *C, comp: *Compilation, prog_node: *std.Progress.Node) !void { |
src/link/Coff.zig+301-242| ... | ... | @@ -79,13 +79,13 @@ entry_addr: ?u32 = null, |
| 79 | 79 | /// We store them here so that we can properly dispose of any allocated |
| 80 | 80 | /// memory within the atom in the incremental linker. |
| 81 | 81 | /// TODO consolidate this. |
| 82 | decls: std.AutoHashMapUnmanaged(Module.Decl.Index, ?u16) = .{}, | |
| 82 | decls: std.AutoHashMapUnmanaged(Module.Decl.Index, DeclMetadata) = .{}, | |
| 83 | 83 | |
| 84 | 84 | /// List of atoms that are either synthetic or map directly to the Zig source program. |
| 85 | managed_atoms: std.ArrayListUnmanaged(*Atom) = .{}, | |
| 85 | atoms: std.ArrayListUnmanaged(Atom) = .{}, | |
| 86 | 86 | |
| 87 | 87 | /// Table of atoms indexed by the symbol index. |
| 88 | atom_by_index_table: std.AutoHashMapUnmanaged(u32, *Atom) = .{}, | |
| 88 | atom_by_index_table: std.AutoHashMapUnmanaged(u32, Atom.Index) = .{}, | |
| 89 | 89 | |
| 90 | 90 | /// Table of unnamed constants associated with a parent `Decl`. |
| 91 | 91 | /// We store them here so that we can free the constants whenever the `Decl` |
| ... | ... | @@ -124,9 +124,9 @@ const Entry = struct { |
| 124 | 124 | sym_index: u32, |
| 125 | 125 | }; |
| 126 | 126 | |
| 127 | const RelocTable = std.AutoHashMapUnmanaged(*Atom, std.ArrayListUnmanaged(Relocation)); | |
| 128 | const BaseRelocationTable = std.AutoHashMapUnmanaged(*Atom, std.ArrayListUnmanaged(u32)); | |
| 129 | const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(*Atom)); | |
| 127 | const RelocTable = std.AutoHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(Relocation)); | |
| 128 | const BaseRelocationTable = std.AutoHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(u32)); | |
| 129 | const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(Atom.Index)); | |
| 130 | 130 | |
| 131 | 131 | const default_file_alignment: u16 = 0x200; |
| 132 | 132 | const default_size_of_stack_reserve: u32 = 0x1000000; |
| ... | ... | @@ -137,7 +137,7 @@ const default_size_of_heap_commit: u32 = 0x1000; |
| 137 | 137 | const Section = struct { |
| 138 | 138 | header: coff.SectionHeader, |
| 139 | 139 | |
| 140 | last_atom: ?*Atom = null, | |
| 140 | last_atom_index: ?Atom.Index = null, | |
| 141 | 141 | |
| 142 | 142 | /// A list of atoms that have surplus capacity. This list can have false |
| 143 | 143 | /// positives, as functions grow and shrink over time, only sometimes being added |
| ... | ... | @@ -154,7 +154,34 @@ const Section = struct { |
| 154 | 154 | /// overcapacity can be negative. A simple way to have negative overcapacity is to |
| 155 | 155 | /// allocate a fresh atom, which will have ideal capacity, and then grow it |
| 156 | 156 | /// by 1 byte. It will then have -1 overcapacity. |
| 157 | free_list: std.ArrayListUnmanaged(*Atom) = .{}, | |
| 157 | free_list: std.ArrayListUnmanaged(Atom.Index) = .{}, | |
| 158 | }; | |
| 159 | ||
| 160 | const DeclMetadata = struct { | |
| 161 | atom: Atom.Index, | |
| 162 | section: u16, | |
| 163 | /// A list of all exports aliases of this Decl. | |
| 164 | exports: std.ArrayListUnmanaged(u32) = .{}, | |
| 165 | ||
| 166 | fn getExport(m: DeclMetadata, coff_file: *const Coff, name: []const u8) ?u32 { | |
| 167 | for (m.exports.items) |exp| { | |
| 168 | if (mem.eql(u8, name, coff_file.getSymbolName(.{ | |
| 169 | .sym_index = exp, | |
| 170 | .file = null, | |
| 171 | }))) return exp; | |
| 172 | } | |
| 173 | return null; | |
| 174 | } | |
| 175 | ||
| 176 | fn getExportPtr(m: *DeclMetadata, coff_file: *Coff, name: []const u8) ?*u32 { | |
| 177 | for (m.exports.items) |*exp| { | |
| 178 | if (mem.eql(u8, name, coff_file.getSymbolName(.{ | |
| 179 | .sym_index = exp.*, | |
| 180 | .file = null, | |
| 181 | }))) return exp; | |
| 182 | } | |
| 183 | return null; | |
| 184 | } | |
| 158 | 185 | }; |
| 159 | 186 | |
| 160 | 187 | pub const PtrWidth = enum { |
| ... | ... | @@ -168,11 +195,6 @@ pub const PtrWidth = enum { |
| 168 | 195 | }; |
| 169 | 196 | } |
| 170 | 197 | }; |
| 171 | pub const SrcFn = void; | |
| 172 | ||
| 173 | pub const Export = struct { | |
| 174 | sym_index: ?u32 = null, | |
| 175 | }; | |
| 176 | 198 | |
| 177 | 199 | pub const SymbolWithLoc = struct { |
| 178 | 200 | // Index into the respective symbol table. |
| ... | ... | @@ -271,11 +293,7 @@ pub fn deinit(self: *Coff) void { |
| 271 | 293 | } |
| 272 | 294 | self.sections.deinit(gpa); |
| 273 | 295 | |
| 274 | for (self.managed_atoms.items) |atom| { | |
| 275 | gpa.destroy(atom); | |
| 276 | } | |
| 277 | self.managed_atoms.deinit(gpa); | |
| 278 | ||
| 296 | self.atoms.deinit(gpa); | |
| 279 | 297 | self.locals.deinit(gpa); |
| 280 | 298 | self.globals.deinit(gpa); |
| 281 | 299 | |
| ... | ... | @@ -297,7 +315,15 @@ pub fn deinit(self: *Coff) void { |
| 297 | 315 | self.imports.deinit(gpa); |
| 298 | 316 | self.imports_free_list.deinit(gpa); |
| 299 | 317 | self.imports_table.deinit(gpa); |
| 300 | self.decls.deinit(gpa); | |
| 318 | ||
| 319 | { | |
| 320 | var it = self.decls.iterator(); | |
| 321 | while (it.next()) |entry| { | |
| 322 | entry.value_ptr.exports.deinit(gpa); | |
| 323 | } | |
| 324 | self.decls.deinit(gpa); | |
| 325 | } | |
| 326 | ||
| 301 | 327 | self.atom_by_index_table.deinit(gpa); |
| 302 | 328 | |
| 303 | 329 | { |
| ... | ... | @@ -461,17 +487,18 @@ fn growSectionVM(self: *Coff, sect_id: u32, needed_size: u32) !void { |
| 461 | 487 | // TODO: enforce order by increasing VM addresses in self.sections container. |
| 462 | 488 | // This is required by the loader anyhow as far as I can tell. |
| 463 | 489 | for (self.sections.items(.header)[sect_id + 1 ..]) |*next_header, next_sect_id| { |
| 464 | const maybe_last_atom = &self.sections.items(.last_atom)[sect_id + 1 + next_sect_id]; | |
| 490 | const maybe_last_atom_index = self.sections.items(.last_atom_index)[sect_id + 1 + next_sect_id]; | |
| 465 | 491 | next_header.virtual_address += diff; |
| 466 | 492 | |
| 467 | if (maybe_last_atom.*) |last_atom| { | |
| 468 | var atom = last_atom; | |
| 493 | if (maybe_last_atom_index) |last_atom_index| { | |
| 494 | var atom_index = last_atom_index; | |
| 469 | 495 | while (true) { |
| 496 | const atom = self.getAtom(atom_index); | |
| 470 | 497 | const sym = atom.getSymbolPtr(self); |
| 471 | 498 | sym.value += diff; |
| 472 | 499 | |
| 473 | if (atom.prev) |prev| { | |
| 474 | atom = prev; | |
| 500 | if (atom.prev_index) |prev_index| { | |
| 501 | atom_index = prev_index; | |
| 475 | 502 | } else break; |
| 476 | 503 | } |
| 477 | 504 | } |
| ... | ... | @@ -480,24 +507,15 @@ fn growSectionVM(self: *Coff, sect_id: u32, needed_size: u32) !void { |
| 480 | 507 | header.virtual_size = increased_size; |
| 481 | 508 | } |
| 482 | 509 | |
| 483 | pub fn allocateDeclIndexes(self: *Coff, decl_index: Module.Decl.Index) !void { | |
| 484 | if (self.llvm_object) |_| return; | |
| 485 | const decl = self.base.options.module.?.declPtr(decl_index); | |
| 486 | if (decl.link.coff.sym_index != 0) return; | |
| 487 | decl.link.coff.sym_index = try self.allocateSymbol(); | |
| 488 | const gpa = self.base.allocator; | |
| 489 | try self.atom_by_index_table.putNoClobber(gpa, decl.link.coff.sym_index, &decl.link.coff); | |
| 490 | try self.decls.putNoClobber(gpa, decl_index, null); | |
| 491 | } | |
| 492 | ||
| 493 | fn allocateAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u32 { | |
| 510 | fn allocateAtom(self: *Coff, atom_index: Atom.Index, new_atom_size: u32, alignment: u32) !u32 { | |
| 494 | 511 | const tracy = trace(@src()); |
| 495 | 512 | defer tracy.end(); |
| 496 | 513 | |
| 514 | const atom = self.getAtom(atom_index); | |
| 497 | 515 | const sect_id = @enumToInt(atom.getSymbol(self).section_number) - 1; |
| 498 | 516 | const header = &self.sections.items(.header)[sect_id]; |
| 499 | 517 | const free_list = &self.sections.items(.free_list)[sect_id]; |
| 500 | const maybe_last_atom = &self.sections.items(.last_atom)[sect_id]; | |
| 518 | const maybe_last_atom_index = &self.sections.items(.last_atom_index)[sect_id]; | |
| 501 | 519 | const new_atom_ideal_capacity = if (header.isCode()) padToIdeal(new_atom_size) else new_atom_size; |
| 502 | 520 | |
| 503 | 521 | // We use these to indicate our intention to update metadata, placing the new atom, |
| ... | ... | @@ -505,7 +523,7 @@ fn allocateAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u |
| 505 | 523 | // It would be simpler to do it inside the for loop below, but that would cause a |
| 506 | 524 | // problem if an error was returned later in the function. So this action |
| 507 | 525 | // is actually carried out at the end of the function, when errors are no longer possible. |
| 508 | var atom_placement: ?*Atom = null; | |
| 526 | var atom_placement: ?Atom.Index = null; | |
| 509 | 527 | var free_list_removal: ?usize = null; |
| 510 | 528 | |
| 511 | 529 | // First we look for an appropriately sized free list node. |
| ... | ... | @@ -513,7 +531,8 @@ fn allocateAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u |
| 513 | 531 | var vaddr = blk: { |
| 514 | 532 | var i: usize = 0; |
| 515 | 533 | while (i < free_list.items.len) { |
| 516 | const big_atom = free_list.items[i]; | |
| 534 | const big_atom_index = free_list.items[i]; | |
| 535 | const big_atom = self.getAtom(big_atom_index); | |
| 517 | 536 | // We now have a pointer to a live atom that has too much capacity. |
| 518 | 537 | // Is it enough that we could fit this new atom? |
| 519 | 538 | const sym = big_atom.getSymbol(self); |
| ... | ... | @@ -541,34 +560,43 @@ fn allocateAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u |
| 541 | 560 | const keep_free_list_node = remaining_capacity >= min_text_capacity; |
| 542 | 561 | |
| 543 | 562 | // Set up the metadata to be updated, after errors are no longer possible. |
| 544 | atom_placement = big_atom; | |
| 563 | atom_placement = big_atom_index; | |
| 545 | 564 | if (!keep_free_list_node) { |
| 546 | 565 | free_list_removal = i; |
| 547 | 566 | } |
| 548 | 567 | break :blk new_start_vaddr; |
| 549 | } else if (maybe_last_atom.*) |last| { | |
| 568 | } else if (maybe_last_atom_index.*) |last_index| { | |
| 569 | const last = self.getAtom(last_index); | |
| 550 | 570 | const last_symbol = last.getSymbol(self); |
| 551 | 571 | const ideal_capacity = if (header.isCode()) padToIdeal(last.size) else last.size; |
| 552 | 572 | const ideal_capacity_end_vaddr = last_symbol.value + ideal_capacity; |
| 553 | 573 | const new_start_vaddr = mem.alignForwardGeneric(u32, ideal_capacity_end_vaddr, alignment); |
| 554 | atom_placement = last; | |
| 574 | atom_placement = last_index; | |
| 555 | 575 | break :blk new_start_vaddr; |
| 556 | 576 | } else { |
| 557 | 577 | break :blk mem.alignForwardGeneric(u32, header.virtual_address, alignment); |
| 558 | 578 | } |
| 559 | 579 | }; |
| 560 | 580 | |
| 561 | const expand_section = atom_placement == null or atom_placement.?.next == null; | |
| 581 | const expand_section = if (atom_placement) |placement_index| | |
| 582 | self.getAtom(placement_index).next_index == null | |
| 583 | else | |
| 584 | true; | |
| 562 | 585 | if (expand_section) { |
| 563 | 586 | const sect_capacity = self.allocatedSize(header.pointer_to_raw_data); |
| 564 | 587 | const needed_size: u32 = (vaddr + new_atom_size) - header.virtual_address; |
| 565 | 588 | if (needed_size > sect_capacity) { |
| 566 | 589 | const new_offset = self.findFreeSpace(needed_size, default_file_alignment); |
| 567 | const current_size = if (maybe_last_atom.*) |last_atom| blk: { | |
| 590 | const current_size = if (maybe_last_atom_index.*) |last_atom_index| blk: { | |
| 591 | const last_atom = self.getAtom(last_atom_index); | |
| 568 | 592 | const sym = last_atom.getSymbol(self); |
| 569 | 593 | break :blk (sym.value + last_atom.size) - header.virtual_address; |
| 570 | 594 | } else 0; |
| 571 | log.debug("moving {s} from 0x{x} to 0x{x}", .{ self.getSectionName(header), header.pointer_to_raw_data, new_offset }); | |
| 595 | log.debug("moving {s} from 0x{x} to 0x{x}", .{ | |
| 596 | self.getSectionName(header), | |
| 597 | header.pointer_to_raw_data, | |
| 598 | new_offset, | |
| 599 | }); | |
| 572 | 600 | const amt = try self.base.file.?.copyRangeAll( |
| 573 | 601 | header.pointer_to_raw_data, |
| 574 | 602 | self.base.file.?, |
| ... | ... | @@ -587,26 +615,34 @@ fn allocateAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u |
| 587 | 615 | |
| 588 | 616 | header.virtual_size = @max(header.virtual_size, needed_size); |
| 589 | 617 | header.size_of_raw_data = needed_size; |
| 590 | maybe_last_atom.* = atom; | |
| 618 | maybe_last_atom_index.* = atom_index; | |
| 591 | 619 | } |
| 592 | 620 | |
| 593 | atom.size = new_atom_size; | |
| 594 | atom.alignment = alignment; | |
| 621 | { | |
| 622 | const atom_ptr = self.getAtomPtr(atom_index); | |
| 623 | atom_ptr.size = new_atom_size; | |
| 624 | atom_ptr.alignment = alignment; | |
| 625 | } | |
| 595 | 626 | |
| 596 | if (atom.prev) |prev| { | |
| 597 | prev.next = atom.next; | |
| 627 | if (atom.prev_index) |prev_index| { | |
| 628 | const prev = self.getAtomPtr(prev_index); | |
| 629 | prev.next_index = atom.next_index; | |
| 598 | 630 | } |
| 599 | if (atom.next) |next| { | |
| 600 | next.prev = atom.prev; | |
| 631 | if (atom.next_index) |next_index| { | |
| 632 | const next = self.getAtomPtr(next_index); | |
| 633 | next.prev_index = atom.prev_index; | |
| 601 | 634 | } |
| 602 | 635 | |
| 603 | if (atom_placement) |big_atom| { | |
| 604 | atom.prev = big_atom; | |
| 605 | atom.next = big_atom.next; | |
| 606 | big_atom.next = atom; | |
| 636 | if (atom_placement) |big_atom_index| { | |
| 637 | const big_atom = self.getAtomPtr(big_atom_index); | |
| 638 | const atom_ptr = self.getAtomPtr(atom_index); | |
| 639 | atom_ptr.prev_index = big_atom_index; | |
| 640 | atom_ptr.next_index = big_atom.next_index; | |
| 641 | big_atom.next_index = atom_index; | |
| 607 | 642 | } else { |
| 608 | atom.prev = null; | |
| 609 | atom.next = null; | |
| 643 | const atom_ptr = self.getAtomPtr(atom_index); | |
| 644 | atom_ptr.prev_index = null; | |
| 645 | atom_ptr.next_index = null; | |
| 610 | 646 | } |
| 611 | 647 | if (free_list_removal) |i| { |
| 612 | 648 | _ = free_list.swapRemove(i); |
| ... | ... | @@ -615,7 +651,7 @@ fn allocateAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u |
| 615 | 651 | return vaddr; |
| 616 | 652 | } |
| 617 | 653 | |
| 618 | fn allocateSymbol(self: *Coff) !u32 { | |
| 654 | pub fn allocateSymbol(self: *Coff) !u32 { | |
| 619 | 655 | const gpa = self.base.allocator; |
| 620 | 656 | try self.locals.ensureUnusedCapacity(gpa, 1); |
| 621 | 657 | |
| ... | ... | @@ -711,25 +747,37 @@ pub fn allocateImportEntry(self: *Coff, target: SymbolWithLoc) !u32 { |
| 711 | 747 | return index; |
| 712 | 748 | } |
| 713 | 749 | |
| 714 | fn createGotAtom(self: *Coff, target: SymbolWithLoc) !*Atom { | |
| 750 | pub fn createAtom(self: *Coff) !Atom.Index { | |
| 715 | 751 | const gpa = self.base.allocator; |
| 716 | const atom = try gpa.create(Atom); | |
| 717 | errdefer gpa.destroy(atom); | |
| 718 | atom.* = Atom.empty; | |
| 719 | atom.sym_index = try self.allocateSymbol(); | |
| 752 | const atom_index = @intCast(Atom.Index, self.atoms.items.len); | |
| 753 | const atom = try self.atoms.addOne(gpa); | |
| 754 | const sym_index = try self.allocateSymbol(); | |
| 755 | try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom_index); | |
| 756 | atom.* = .{ | |
| 757 | .sym_index = sym_index, | |
| 758 | .file = null, | |
| 759 | .size = 0, | |
| 760 | .alignment = 0, | |
| 761 | .prev_index = null, | |
| 762 | .next_index = null, | |
| 763 | }; | |
| 764 | log.debug("creating ATOM(%{d}) at index {d}", .{ sym_index, atom_index }); | |
| 765 | return atom_index; | |
| 766 | } | |
| 767 | ||
| 768 | fn createGotAtom(self: *Coff, target: SymbolWithLoc) !Atom.Index { | |
| 769 | const atom_index = try self.createAtom(); | |
| 770 | const atom = self.getAtomPtr(atom_index); | |
| 720 | 771 | atom.size = @sizeOf(u64); |
| 721 | 772 | atom.alignment = @alignOf(u64); |
| 722 | 773 | |
| 723 | try self.managed_atoms.append(gpa, atom); | |
| 724 | try self.atom_by_index_table.putNoClobber(gpa, atom.sym_index, atom); | |
| 725 | ||
| 726 | 774 | const sym = atom.getSymbolPtr(self); |
| 727 | 775 | sym.section_number = @intToEnum(coff.SectionNumber, self.got_section_index.? + 1); |
| 728 | sym.value = try self.allocateAtom(atom, atom.size, atom.alignment); | |
| 776 | sym.value = try self.allocateAtom(atom_index, atom.size, atom.alignment); | |
| 729 | 777 | |
| 730 | 778 | log.debug("allocated GOT atom at 0x{x}", .{sym.value}); |
| 731 | 779 | |
| 732 | try atom.addRelocation(self, .{ | |
| 780 | try Atom.addRelocation(self, atom_index, .{ | |
| 733 | 781 | .type = .direct, |
| 734 | 782 | .target = target, |
| 735 | 783 | .offset = 0, |
| ... | ... | @@ -743,67 +791,67 @@ fn createGotAtom(self: *Coff, target: SymbolWithLoc) !*Atom { |
| 743 | 791 | .UNDEFINED => @panic("TODO generate a binding for undefined GOT target"), |
| 744 | 792 | .ABSOLUTE => {}, |
| 745 | 793 | .DEBUG => unreachable, // not possible |
| 746 | else => try atom.addBaseRelocation(self, 0), | |
| 794 | else => try Atom.addBaseRelocation(self, atom_index, 0), | |
| 747 | 795 | } |
| 748 | 796 | |
| 749 | return atom; | |
| 797 | return atom_index; | |
| 750 | 798 | } |
| 751 | 799 | |
| 752 | fn createImportAtom(self: *Coff) !*Atom { | |
| 753 | const gpa = self.base.allocator; | |
| 754 | const atom = try gpa.create(Atom); | |
| 755 | errdefer gpa.destroy(atom); | |
| 756 | atom.* = Atom.empty; | |
| 757 | atom.sym_index = try self.allocateSymbol(); | |
| 800 | fn createImportAtom(self: *Coff) !Atom.Index { | |
| 801 | const atom_index = try self.createAtom(); | |
| 802 | const atom = self.getAtomPtr(atom_index); | |
| 758 | 803 | atom.size = @sizeOf(u64); |
| 759 | 804 | atom.alignment = @alignOf(u64); |
| 760 | 805 | |
| 761 | try self.managed_atoms.append(gpa, atom); | |
| 762 | try self.atom_by_index_table.putNoClobber(gpa, atom.sym_index, atom); | |
| 763 | ||
| 764 | 806 | const sym = atom.getSymbolPtr(self); |
| 765 | 807 | sym.section_number = @intToEnum(coff.SectionNumber, self.idata_section_index.? + 1); |
| 766 | sym.value = try self.allocateAtom(atom, atom.size, atom.alignment); | |
| 808 | sym.value = try self.allocateAtom(atom_index, atom.size, atom.alignment); | |
| 767 | 809 | |
| 768 | 810 | log.debug("allocated import atom at 0x{x}", .{sym.value}); |
| 769 | 811 | |
| 770 | return atom; | |
| 812 | return atom_index; | |
| 771 | 813 | } |
| 772 | 814 | |
| 773 | fn growAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u32 { | |
| 815 | fn growAtom(self: *Coff, atom_index: Atom.Index, new_atom_size: u32, alignment: u32) !u32 { | |
| 816 | const atom = self.getAtom(atom_index); | |
| 774 | 817 | const sym = atom.getSymbol(self); |
| 775 | 818 | const align_ok = mem.alignBackwardGeneric(u32, sym.value, alignment) == sym.value; |
| 776 | 819 | const need_realloc = !align_ok or new_atom_size > atom.capacity(self); |
| 777 | 820 | if (!need_realloc) return sym.value; |
| 778 | return self.allocateAtom(atom, new_atom_size, alignment); | |
| 821 | return self.allocateAtom(atom_index, new_atom_size, alignment); | |
| 779 | 822 | } |
| 780 | 823 | |
| 781 | fn shrinkAtom(self: *Coff, atom: *Atom, new_block_size: u32) void { | |
| 824 | fn shrinkAtom(self: *Coff, atom_index: Atom.Index, new_block_size: u32) void { | |
| 782 | 825 | _ = self; |
| 783 | _ = atom; | |
| 826 | _ = atom_index; | |
| 784 | 827 | _ = new_block_size; |
| 785 | 828 | // TODO check the new capacity, and if it crosses the size threshold into a big enough |
| 786 | 829 | // capacity, insert a free list node for it. |
| 787 | 830 | } |
| 788 | 831 | |
| 789 | fn writeAtom(self: *Coff, atom: *Atom, code: []const u8) !void { | |
| 832 | fn writeAtom(self: *Coff, atom_index: Atom.Index, code: []const u8) !void { | |
| 833 | const atom = self.getAtom(atom_index); | |
| 790 | 834 | const sym = atom.getSymbol(self); |
| 791 | 835 | const section = self.sections.get(@enumToInt(sym.section_number) - 1); |
| 792 | 836 | const file_offset = section.header.pointer_to_raw_data + sym.value - section.header.virtual_address; |
| 793 | log.debug("writing atom for symbol {s} at file offset 0x{x} to 0x{x}", .{ atom.getName(self), file_offset, file_offset + code.len }); | |
| 837 | log.debug("writing atom for symbol {s} at file offset 0x{x} to 0x{x}", .{ | |
| 838 | atom.getName(self), | |
| 839 | file_offset, | |
| 840 | file_offset + code.len, | |
| 841 | }); | |
| 794 | 842 | try self.base.file.?.pwriteAll(code, file_offset); |
| 795 | try self.resolveRelocs(atom); | |
| 843 | try self.resolveRelocs(atom_index); | |
| 796 | 844 | } |
| 797 | 845 | |
| 798 | fn writePtrWidthAtom(self: *Coff, atom: *Atom) !void { | |
| 846 | fn writePtrWidthAtom(self: *Coff, atom_index: Atom.Index) !void { | |
| 799 | 847 | switch (self.ptr_width) { |
| 800 | 848 | .p32 => { |
| 801 | 849 | var buffer: [@sizeOf(u32)]u8 = [_]u8{0} ** @sizeOf(u32); |
| 802 | try self.writeAtom(atom, &buffer); | |
| 850 | try self.writeAtom(atom_index, &buffer); | |
| 803 | 851 | }, |
| 804 | 852 | .p64 => { |
| 805 | 853 | var buffer: [@sizeOf(u64)]u8 = [_]u8{0} ** @sizeOf(u64); |
| 806 | try self.writeAtom(atom, &buffer); | |
| 854 | try self.writeAtom(atom_index, &buffer); | |
| 807 | 855 | }, |
| 808 | 856 | } |
| 809 | 857 | } |
| ... | ... | @@ -823,7 +871,8 @@ fn markRelocsDirtyByAddress(self: *Coff, addr: u32) void { |
| 823 | 871 | var it = self.relocs.valueIterator(); |
| 824 | 872 | while (it.next()) |relocs| { |
| 825 | 873 | for (relocs.items) |*reloc| { |
| 826 | const target_atom = reloc.getTargetAtom(self) orelse continue; | |
| 874 | const target_atom_index = reloc.getTargetAtomIndex(self) orelse continue; | |
| 875 | const target_atom = self.getAtom(target_atom_index); | |
| 827 | 876 | const target_sym = target_atom.getSymbol(self); |
| 828 | 877 | if (target_sym.value < addr) continue; |
| 829 | 878 | reloc.dirty = true; |
| ... | ... | @@ -831,23 +880,26 @@ fn markRelocsDirtyByAddress(self: *Coff, addr: u32) void { |
| 831 | 880 | } |
| 832 | 881 | } |
| 833 | 882 | |
| 834 | fn resolveRelocs(self: *Coff, atom: *Atom) !void { | |
| 835 | const relocs = self.relocs.get(atom) orelse return; | |
| 883 | fn resolveRelocs(self: *Coff, atom_index: Atom.Index) !void { | |
| 884 | const relocs = self.relocs.get(atom_index) orelse return; | |
| 836 | 885 | |
| 837 | log.debug("relocating '{s}'", .{atom.getName(self)}); | |
| 886 | log.debug("relocating '{s}'", .{self.getAtom(atom_index).getName(self)}); | |
| 838 | 887 | |
| 839 | 888 | for (relocs.items) |*reloc| { |
| 840 | 889 | if (!reloc.dirty) continue; |
| 841 | try reloc.resolve(atom, self); | |
| 890 | try reloc.resolve(atom_index, self); | |
| 842 | 891 | } |
| 843 | 892 | } |
| 844 | 893 | |
| 845 | fn freeAtom(self: *Coff, atom: *Atom) void { | |
| 846 | log.debug("freeAtom {*}", .{atom}); | |
| 894 | fn freeAtom(self: *Coff, atom_index: Atom.Index) void { | |
| 895 | log.debug("freeAtom {d}", .{atom_index}); | |
| 896 | ||
| 897 | const gpa = self.base.allocator; | |
| 847 | 898 | |
| 848 | 899 | // Remove any relocs and base relocs associated with this Atom |
| 849 | self.freeRelocationsForAtom(atom); | |
| 900 | Atom.freeRelocations(self, atom_index); | |
| 850 | 901 | |
| 902 | const atom = self.getAtom(atom_index); | |
| 851 | 903 | const sym = atom.getSymbol(self); |
| 852 | 904 | const sect_id = @enumToInt(sym.section_number) - 1; |
| 853 | 905 | const free_list = &self.sections.items(.free_list)[sect_id]; |
| ... | ... | @@ -856,46 +908,69 @@ fn freeAtom(self: *Coff, atom: *Atom) void { |
| 856 | 908 | var i: usize = 0; |
| 857 | 909 | // TODO turn free_list into a hash map |
| 858 | 910 | while (i < free_list.items.len) { |
| 859 | if (free_list.items[i] == atom) { | |
| 911 | if (free_list.items[i] == atom_index) { | |
| 860 | 912 | _ = free_list.swapRemove(i); |
| 861 | 913 | continue; |
| 862 | 914 | } |
| 863 | if (free_list.items[i] == atom.prev) { | |
| 915 | if (free_list.items[i] == atom.prev_index) { | |
| 864 | 916 | already_have_free_list_node = true; |
| 865 | 917 | } |
| 866 | 918 | i += 1; |
| 867 | 919 | } |
| 868 | 920 | } |
| 869 | 921 | |
| 870 | const maybe_last_atom = &self.sections.items(.last_atom)[sect_id]; | |
| 871 | if (maybe_last_atom.*) |last_atom| { | |
| 872 | if (last_atom == atom) { | |
| 873 | if (atom.prev) |prev| { | |
| 922 | const maybe_last_atom_index = &self.sections.items(.last_atom_index)[sect_id]; | |
| 923 | if (maybe_last_atom_index.*) |last_atom_index| { | |
| 924 | if (last_atom_index == atom_index) { | |
| 925 | if (atom.prev_index) |prev_index| { | |
| 874 | 926 | // TODO shrink the section size here |
| 875 | maybe_last_atom.* = prev; | |
| 927 | maybe_last_atom_index.* = prev_index; | |
| 876 | 928 | } else { |
| 877 | maybe_last_atom.* = null; | |
| 929 | maybe_last_atom_index.* = null; | |
| 878 | 930 | } |
| 879 | 931 | } |
| 880 | 932 | } |
| 881 | 933 | |
| 882 | if (atom.prev) |prev| { | |
| 883 | prev.next = atom.next; | |
| 934 | if (atom.prev_index) |prev_index| { | |
| 935 | const prev = self.getAtomPtr(prev_index); | |
| 936 | prev.next_index = atom.next_index; | |
| 884 | 937 | |
| 885 | if (!already_have_free_list_node and prev.freeListEligible(self)) { | |
| 938 | if (!already_have_free_list_node and prev.*.freeListEligible(self)) { | |
| 886 | 939 | // The free list is heuristics, it doesn't have to be perfect, so we can |
| 887 | 940 | // ignore the OOM here. |
| 888 | free_list.append(self.base.allocator, prev) catch {}; | |
| 941 | free_list.append(gpa, prev_index) catch {}; | |
| 889 | 942 | } |
| 890 | 943 | } else { |
| 891 | atom.prev = null; | |
| 944 | self.getAtomPtr(atom_index).prev_index = null; | |
| 892 | 945 | } |
| 893 | 946 | |
| 894 | if (atom.next) |next| { | |
| 895 | next.prev = atom.prev; | |
| 947 | if (atom.next_index) |next_index| { | |
| 948 | self.getAtomPtr(next_index).prev_index = atom.prev_index; | |
| 896 | 949 | } else { |
| 897 | atom.next = null; | |
| 950 | self.getAtomPtr(atom_index).next_index = null; | |
| 951 | } | |
| 952 | ||
| 953 | // Appending to free lists is allowed to fail because the free lists are heuristics based anyway. | |
| 954 | const sym_index = atom.getSymbolIndex().?; | |
| 955 | self.locals_free_list.append(gpa, sym_index) catch {}; | |
| 956 | ||
| 957 | // Try freeing GOT atom if this decl had one | |
| 958 | const got_target = SymbolWithLoc{ .sym_index = sym_index, .file = null }; | |
| 959 | if (self.got_entries_table.get(got_target)) |got_index| { | |
| 960 | self.got_entries_free_list.append(gpa, @intCast(u32, got_index)) catch {}; | |
| 961 | self.got_entries.items[got_index] = .{ | |
| 962 | .target = .{ .sym_index = 0, .file = null }, | |
| 963 | .sym_index = 0, | |
| 964 | }; | |
| 965 | _ = self.got_entries_table.remove(got_target); | |
| 966 | ||
| 967 | log.debug(" adding GOT index {d} to free list (target local@{d})", .{ got_index, sym_index }); | |
| 898 | 968 | } |
| 969 | ||
| 970 | self.locals.items[sym_index].section_number = .UNDEFINED; | |
| 971 | _ = self.atom_by_index_table.remove(sym_index); | |
| 972 | log.debug(" adding local symbol index {d} to free list", .{sym_index}); | |
| 973 | self.getAtomPtr(atom_index).sym_index = 0; | |
| 899 | 974 | } |
| 900 | 975 | |
| 901 | 976 | pub fn updateFunc(self: *Coff, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void { |
| ... | ... | @@ -912,8 +987,10 @@ pub fn updateFunc(self: *Coff, module: *Module, func: *Module.Fn, air: Air, live |
| 912 | 987 | |
| 913 | 988 | const decl_index = func.owner_decl; |
| 914 | 989 | const decl = module.declPtr(decl_index); |
| 990 | ||
| 991 | const atom_index = try self.getOrCreateAtomForDecl(decl_index); | |
| 915 | 992 | self.freeUnnamedConsts(decl_index); |
| 916 | self.freeRelocationsForAtom(&decl.link.coff); | |
| 993 | Atom.freeRelocations(self, atom_index); | |
| 917 | 994 | |
| 918 | 995 | var code_buffer = std.ArrayList(u8).init(self.base.allocator); |
| 919 | 996 | defer code_buffer.deinit(); |
| ... | ... | @@ -928,7 +1005,7 @@ pub fn updateFunc(self: *Coff, module: *Module, func: *Module.Fn, air: Air, live |
| 928 | 1005 | .none, |
| 929 | 1006 | ); |
| 930 | 1007 | const code = switch (res) { |
| 931 | .appended => code_buffer.items, | |
| 1008 | .ok => code_buffer.items, | |
| 932 | 1009 | .fail => |em| { |
| 933 | 1010 | decl.analysis = .codegen_failure; |
| 934 | 1011 | try module.failed_decls.put(module.gpa, decl_index, em); |
| ... | ... | @@ -957,12 +1034,8 @@ pub fn lowerUnnamedConst(self: *Coff, tv: TypedValue, decl_index: Module.Decl.In |
| 957 | 1034 | } |
| 958 | 1035 | const unnamed_consts = gop.value_ptr; |
| 959 | 1036 | |
| 960 | const atom = try gpa.create(Atom); | |
| 961 | errdefer gpa.destroy(atom); | |
| 962 | atom.* = Atom.empty; | |
| 1037 | const atom_index = try self.createAtom(); | |
| 963 | 1038 | |
| 964 | atom.sym_index = try self.allocateSymbol(); | |
| 965 | const sym = atom.getSymbolPtr(self); | |
| 966 | 1039 | const sym_name = blk: { |
| 967 | 1040 | const decl_name = try decl.getFullyQualifiedName(mod); |
| 968 | 1041 | defer gpa.free(decl_name); |
| ... | ... | @@ -971,18 +1044,18 @@ pub fn lowerUnnamedConst(self: *Coff, tv: TypedValue, decl_index: Module.Decl.In |
| 971 | 1044 | break :blk try std.fmt.allocPrint(gpa, "__unnamed_{s}_{d}", .{ decl_name, index }); |
| 972 | 1045 | }; |
| 973 | 1046 | defer gpa.free(sym_name); |
| 974 | try self.setSymbolName(sym, sym_name); | |
| 975 | sym.section_number = @intToEnum(coff.SectionNumber, self.rdata_section_index.? + 1); | |
| 976 | ||
| 977 | try self.managed_atoms.append(gpa, atom); | |
| 978 | try self.atom_by_index_table.putNoClobber(gpa, atom.sym_index, atom); | |
| 1047 | { | |
| 1048 | const atom = self.getAtom(atom_index); | |
| 1049 | const sym = atom.getSymbolPtr(self); | |
| 1050 | try self.setSymbolName(sym, sym_name); | |
| 1051 | sym.section_number = @intToEnum(coff.SectionNumber, self.rdata_section_index.? + 1); | |
| 1052 | } | |
| 979 | 1053 | |
| 980 | 1054 | const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), tv, &code_buffer, .none, .{ |
| 981 | .parent_atom_index = atom.sym_index, | |
| 1055 | .parent_atom_index = self.getAtom(atom_index).getSymbolIndex().?, | |
| 982 | 1056 | }); |
| 983 | 1057 | const code = switch (res) { |
| 984 | .externally_managed => |x| x, | |
| 985 | .appended => code_buffer.items, | |
| 1058 | .ok => code_buffer.items, | |
| 986 | 1059 | .fail => |em| { |
| 987 | 1060 | decl.analysis = .codegen_failure; |
| 988 | 1061 | try mod.failed_decls.put(mod.gpa, decl_index, em); |
| ... | ... | @@ -992,19 +1065,20 @@ pub fn lowerUnnamedConst(self: *Coff, tv: TypedValue, decl_index: Module.Decl.In |
| 992 | 1065 | }; |
| 993 | 1066 | |
| 994 | 1067 | const required_alignment = tv.ty.abiAlignment(self.base.options.target); |
| 1068 | const atom = self.getAtomPtr(atom_index); | |
| 995 | 1069 | atom.alignment = required_alignment; |
| 996 | 1070 | atom.size = @intCast(u32, code.len); |
| 997 | sym.value = try self.allocateAtom(atom, atom.size, atom.alignment); | |
| 998 | errdefer self.freeAtom(atom); | |
| 1071 | atom.getSymbolPtr(self).value = try self.allocateAtom(atom_index, atom.size, atom.alignment); | |
| 1072 | errdefer self.freeAtom(atom_index); | |
| 999 | 1073 | |
| 1000 | try unnamed_consts.append(gpa, atom); | |
| 1074 | try unnamed_consts.append(gpa, atom_index); | |
| 1001 | 1075 | |
| 1002 | log.debug("allocated atom for {s} at 0x{x}", .{ sym_name, sym.value }); | |
| 1076 | log.debug("allocated atom for {s} at 0x{x}", .{ sym_name, atom.getSymbol(self).value }); | |
| 1003 | 1077 | log.debug(" (required alignment 0x{x})", .{required_alignment}); |
| 1004 | 1078 | |
| 1005 | try self.writeAtom(atom, code); | |
| 1079 | try self.writeAtom(atom_index, code); | |
| 1006 | 1080 | |
| 1007 | return atom.sym_index; | |
| 1081 | return atom.getSymbolIndex().?; | |
| 1008 | 1082 | } |
| 1009 | 1083 | |
| 1010 | 1084 | pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) !void { |
| ... | ... | @@ -1029,7 +1103,9 @@ pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) ! |
| 1029 | 1103 | } |
| 1030 | 1104 | } |
| 1031 | 1105 | |
| 1032 | self.freeRelocationsForAtom(&decl.link.coff); | |
| 1106 | const atom_index = try self.getOrCreateAtomForDecl(decl_index); | |
| 1107 | Atom.freeRelocations(self, atom_index); | |
| 1108 | const atom = self.getAtom(atom_index); | |
| 1033 | 1109 | |
| 1034 | 1110 | var code_buffer = std.ArrayList(u8).init(self.base.allocator); |
| 1035 | 1111 | defer code_buffer.deinit(); |
| ... | ... | @@ -1039,11 +1115,10 @@ pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) ! |
| 1039 | 1115 | .ty = decl.ty, |
| 1040 | 1116 | .val = decl_val, |
| 1041 | 1117 | }, &code_buffer, .none, .{ |
| 1042 | .parent_atom_index = decl.link.coff.sym_index, | |
| 1118 | .parent_atom_index = atom.getSymbolIndex().?, | |
| 1043 | 1119 | }); |
| 1044 | 1120 | const code = switch (res) { |
| 1045 | .externally_managed => |x| x, | |
| 1046 | .appended => code_buffer.items, | |
| 1121 | .ok => code_buffer.items, | |
| 1047 | 1122 | .fail => |em| { |
| 1048 | 1123 | decl.analysis = .codegen_failure; |
| 1049 | 1124 | try module.failed_decls.put(module.gpa, decl_index, em); |
| ... | ... | @@ -1058,7 +1133,20 @@ pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) ! |
| 1058 | 1133 | return self.updateDeclExports(module, decl_index, module.getDeclExports(decl_index)); |
| 1059 | 1134 | } |
| 1060 | 1135 | |
| 1061 | fn getDeclOutputSection(self: *Coff, decl: *Module.Decl) u16 { | |
| 1136 | pub fn getOrCreateAtomForDecl(self: *Coff, decl_index: Module.Decl.Index) !Atom.Index { | |
| 1137 | const gop = try self.decls.getOrPut(self.base.allocator, decl_index); | |
| 1138 | if (!gop.found_existing) { | |
| 1139 | gop.value_ptr.* = .{ | |
| 1140 | .atom = try self.createAtom(), | |
| 1141 | .section = self.getDeclOutputSection(decl_index), | |
| 1142 | .exports = .{}, | |
| 1143 | }; | |
| 1144 | } | |
| 1145 | return gop.value_ptr.atom; | |
| 1146 | } | |
| 1147 | ||
| 1148 | fn getDeclOutputSection(self: *Coff, decl_index: Module.Decl.Index) u16 { | |
| 1149 | const decl = self.base.options.module.?.declPtr(decl_index); | |
| 1062 | 1150 | const ty = decl.ty; |
| 1063 | 1151 | const zig_ty = ty.zigTypeTag(); |
| 1064 | 1152 | const val = decl.val; |
| ... | ... | @@ -1093,15 +1181,12 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []const u8, |
| 1093 | 1181 | log.debug("updateDeclCode {s}{*}", .{ decl_name, decl }); |
| 1094 | 1182 | const required_alignment = decl.getAlignment(self.base.options.target); |
| 1095 | 1183 | |
| 1096 | const decl_ptr = self.decls.getPtr(decl_index).?; | |
| 1097 | if (decl_ptr.* == null) { | |
| 1098 | decl_ptr.* = self.getDeclOutputSection(decl); | |
| 1099 | } | |
| 1100 | const sect_index = decl_ptr.*.?; | |
| 1101 | ||
| 1184 | const decl_metadata = self.decls.get(decl_index).?; | |
| 1185 | const atom_index = decl_metadata.atom; | |
| 1186 | const atom = self.getAtom(atom_index); | |
| 1187 | const sect_index = decl_metadata.section; | |
| 1102 | 1188 | const code_len = @intCast(u32, code.len); |
| 1103 | const atom = &decl.link.coff; | |
| 1104 | assert(atom.sym_index != 0); // Caller forgot to allocateDeclIndexes() | |
| 1189 | ||
| 1105 | 1190 | if (atom.size != 0) { |
| 1106 | 1191 | const sym = atom.getSymbolPtr(self); |
| 1107 | 1192 | try self.setSymbolName(sym, decl_name); |
| ... | ... | @@ -1111,62 +1196,51 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []const u8, |
| 1111 | 1196 | const capacity = atom.capacity(self); |
| 1112 | 1197 | const need_realloc = code.len > capacity or !mem.isAlignedGeneric(u64, sym.value, required_alignment); |
| 1113 | 1198 | if (need_realloc) { |
| 1114 | const vaddr = try self.growAtom(atom, code_len, required_alignment); | |
| 1199 | const vaddr = try self.growAtom(atom_index, code_len, required_alignment); | |
| 1115 | 1200 | log.debug("growing {s} from 0x{x} to 0x{x}", .{ decl_name, sym.value, vaddr }); |
| 1116 | 1201 | log.debug(" (required alignment 0x{x}", .{required_alignment}); |
| 1117 | 1202 | |
| 1118 | 1203 | if (vaddr != sym.value) { |
| 1119 | 1204 | sym.value = vaddr; |
| 1120 | 1205 | log.debug(" (updating GOT entry)", .{}); |
| 1121 | const got_target = SymbolWithLoc{ .sym_index = atom.sym_index, .file = null }; | |
| 1122 | const got_atom = self.getGotAtomForSymbol(got_target).?; | |
| 1206 | const got_target = SymbolWithLoc{ .sym_index = atom.getSymbolIndex().?, .file = null }; | |
| 1207 | const got_atom_index = self.getGotAtomIndexForSymbol(got_target).?; | |
| 1123 | 1208 | self.markRelocsDirtyByTarget(got_target); |
| 1124 | try self.writePtrWidthAtom(got_atom); | |
| 1209 | try self.writePtrWidthAtom(got_atom_index); | |
| 1125 | 1210 | } |
| 1126 | 1211 | } else if (code_len < atom.size) { |
| 1127 | self.shrinkAtom(atom, code_len); | |
| 1212 | self.shrinkAtom(atom_index, code_len); | |
| 1128 | 1213 | } |
| 1129 | atom.size = code_len; | |
| 1214 | self.getAtomPtr(atom_index).size = code_len; | |
| 1130 | 1215 | } else { |
| 1131 | 1216 | const sym = atom.getSymbolPtr(self); |
| 1132 | 1217 | try self.setSymbolName(sym, decl_name); |
| 1133 | 1218 | sym.section_number = @intToEnum(coff.SectionNumber, sect_index + 1); |
| 1134 | 1219 | sym.type = .{ .complex_type = complex_type, .base_type = .NULL }; |
| 1135 | 1220 | |
| 1136 | const vaddr = try self.allocateAtom(atom, code_len, required_alignment); | |
| 1137 | errdefer self.freeAtom(atom); | |
| 1221 | const vaddr = try self.allocateAtom(atom_index, code_len, required_alignment); | |
| 1222 | errdefer self.freeAtom(atom_index); | |
| 1138 | 1223 | log.debug("allocated atom for {s} at 0x{x}", .{ decl_name, vaddr }); |
| 1139 | atom.size = code_len; | |
| 1224 | self.getAtomPtr(atom_index).size = code_len; | |
| 1140 | 1225 | sym.value = vaddr; |
| 1141 | 1226 | |
| 1142 | const got_target = SymbolWithLoc{ .sym_index = atom.sym_index, .file = null }; | |
| 1227 | const got_target = SymbolWithLoc{ .sym_index = atom.getSymbolIndex().?, .file = null }; | |
| 1143 | 1228 | const got_index = try self.allocateGotEntry(got_target); |
| 1144 | const got_atom = try self.createGotAtom(got_target); | |
| 1145 | self.got_entries.items[got_index].sym_index = got_atom.sym_index; | |
| 1146 | try self.writePtrWidthAtom(got_atom); | |
| 1229 | const got_atom_index = try self.createGotAtom(got_target); | |
| 1230 | const got_atom = self.getAtom(got_atom_index); | |
| 1231 | self.got_entries.items[got_index].sym_index = got_atom.getSymbolIndex().?; | |
| 1232 | try self.writePtrWidthAtom(got_atom_index); | |
| 1147 | 1233 | } |
| 1148 | 1234 | |
| 1149 | 1235 | self.markRelocsDirtyByTarget(atom.getSymbolWithLoc()); |
| 1150 | try self.writeAtom(atom, code); | |
| 1151 | } | |
| 1152 | ||
| 1153 | fn freeRelocationsForAtom(self: *Coff, atom: *Atom) void { | |
| 1154 | var removed_relocs = self.relocs.fetchRemove(atom); | |
| 1155 | if (removed_relocs) |*relocs| relocs.value.deinit(self.base.allocator); | |
| 1156 | var removed_base_relocs = self.base_relocs.fetchRemove(atom); | |
| 1157 | if (removed_base_relocs) |*base_relocs| base_relocs.value.deinit(self.base.allocator); | |
| 1236 | try self.writeAtom(atom_index, code); | |
| 1158 | 1237 | } |
| 1159 | 1238 | |
| 1160 | 1239 | fn freeUnnamedConsts(self: *Coff, decl_index: Module.Decl.Index) void { |
| 1161 | 1240 | const gpa = self.base.allocator; |
| 1162 | 1241 | const unnamed_consts = self.unnamed_const_atoms.getPtr(decl_index) orelse return; |
| 1163 | for (unnamed_consts.items) |atom| { | |
| 1164 | self.freeAtom(atom); | |
| 1165 | self.locals_free_list.append(gpa, atom.sym_index) catch {}; | |
| 1166 | self.locals.items[atom.sym_index].section_number = .UNDEFINED; | |
| 1167 | _ = self.atom_by_index_table.remove(atom.sym_index); | |
| 1168 | log.debug(" adding local symbol index {d} to free list", .{atom.sym_index}); | |
| 1169 | atom.sym_index = 0; | |
| 1242 | for (unnamed_consts.items) |atom_index| { | |
| 1243 | self.freeAtom(atom_index); | |
| 1170 | 1244 | } |
| 1171 | 1245 | unnamed_consts.clearAndFree(gpa); |
| 1172 | 1246 | } |
| ... | ... | @@ -1181,35 +1255,11 @@ pub fn freeDecl(self: *Coff, decl_index: Module.Decl.Index) void { |
| 1181 | 1255 | |
| 1182 | 1256 | log.debug("freeDecl {*}", .{decl}); |
| 1183 | 1257 | |
| 1184 | const kv = self.decls.fetchRemove(decl_index); | |
| 1185 | if (kv.?.value) |_| { | |
| 1186 | self.freeAtom(&decl.link.coff); | |
| 1258 | if (self.decls.fetchRemove(decl_index)) |const_kv| { | |
| 1259 | var kv = const_kv; | |
| 1260 | self.freeAtom(kv.value.atom); | |
| 1187 | 1261 | self.freeUnnamedConsts(decl_index); |
| 1188 | } | |
| 1189 | ||
| 1190 | // Appending to free lists is allowed to fail because the free lists are heuristics based anyway. | |
| 1191 | const gpa = self.base.allocator; | |
| 1192 | const sym_index = decl.link.coff.sym_index; | |
| 1193 | if (sym_index != 0) { | |
| 1194 | self.locals_free_list.append(gpa, sym_index) catch {}; | |
| 1195 | ||
| 1196 | // Try freeing GOT atom if this decl had one | |
| 1197 | const got_target = SymbolWithLoc{ .sym_index = sym_index, .file = null }; | |
| 1198 | if (self.got_entries_table.get(got_target)) |got_index| { | |
| 1199 | self.got_entries_free_list.append(gpa, @intCast(u32, got_index)) catch {}; | |
| 1200 | self.got_entries.items[got_index] = .{ | |
| 1201 | .target = .{ .sym_index = 0, .file = null }, | |
| 1202 | .sym_index = 0, | |
| 1203 | }; | |
| 1204 | _ = self.got_entries_table.remove(got_target); | |
| 1205 | ||
| 1206 | log.debug(" adding GOT index {d} to free list (target local@{d})", .{ got_index, sym_index }); | |
| 1207 | } | |
| 1208 | ||
| 1209 | self.locals.items[sym_index].section_number = .UNDEFINED; | |
| 1210 | _ = self.atom_by_index_table.remove(sym_index); | |
| 1211 | log.debug(" adding local symbol index {d} to free list", .{sym_index}); | |
| 1212 | decl.link.coff.sym_index = 0; | |
| 1262 | kv.value.exports.deinit(self.base.allocator); | |
| 1213 | 1263 | } |
| 1214 | 1264 | } |
| 1215 | 1265 | |
| ... | ... | @@ -1262,9 +1312,10 @@ pub fn updateDeclExports( |
| 1262 | 1312 | const gpa = self.base.allocator; |
| 1263 | 1313 | |
| 1264 | 1314 | const decl = module.declPtr(decl_index); |
| 1265 | const atom = &decl.link.coff; | |
| 1266 | if (atom.sym_index == 0) return; | |
| 1315 | const atom_index = try self.getOrCreateAtomForDecl(decl_index); | |
| 1316 | const atom = self.getAtom(atom_index); | |
| 1267 | 1317 | const decl_sym = atom.getSymbol(self); |
| 1318 | const decl_metadata = self.decls.getPtr(decl_index).?; | |
| 1268 | 1319 | |
| 1269 | 1320 | for (exports) |exp| { |
| 1270 | 1321 | log.debug("adding new export '{s}'", .{exp.options.name}); |
| ... | ... | @@ -1299,9 +1350,9 @@ pub fn updateDeclExports( |
| 1299 | 1350 | continue; |
| 1300 | 1351 | } |
| 1301 | 1352 | |
| 1302 | const sym_index = exp.link.coff.sym_index orelse blk: { | |
| 1353 | const sym_index = decl_metadata.getExport(self, exp.options.name) orelse blk: { | |
| 1303 | 1354 | const sym_index = try self.allocateSymbol(); |
| 1304 | exp.link.coff.sym_index = sym_index; | |
| 1355 | try decl_metadata.exports.append(gpa, sym_index); | |
| 1305 | 1356 | break :blk sym_index; |
| 1306 | 1357 | }; |
| 1307 | 1358 | const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = null }; |
| ... | ... | @@ -1324,16 +1375,15 @@ pub fn updateDeclExports( |
| 1324 | 1375 | } |
| 1325 | 1376 | } |
| 1326 | 1377 | |
| 1327 | pub fn deleteExport(self: *Coff, exp: Export) void { | |
| 1378 | pub fn deleteDeclExport(self: *Coff, decl_index: Module.Decl.Index, name: []const u8) void { | |
| 1328 | 1379 | if (self.llvm_object) |_| return; |
| 1329 | const sym_index = exp.sym_index orelse return; | |
| 1380 | const metadata = self.decls.getPtr(decl_index) orelse return; | |
| 1381 | const sym_index = metadata.getExportPtr(self, name) orelse return; | |
| 1330 | 1382 | |
| 1331 | 1383 | const gpa = self.base.allocator; |
| 1332 | ||
| 1333 | const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = null }; | |
| 1384 | const sym_loc = SymbolWithLoc{ .sym_index = sym_index.*, .file = null }; | |
| 1334 | 1385 | const sym = self.getSymbolPtr(sym_loc); |
| 1335 | const sym_name = self.getSymbolName(sym_loc); | |
| 1336 | log.debug("deleting export '{s}'", .{sym_name}); | |
| 1386 | log.debug("deleting export '{s}'", .{name}); | |
| 1337 | 1387 | assert(sym.storage_class == .EXTERNAL and sym.section_number != .UNDEFINED); |
| 1338 | 1388 | sym.* = .{ |
| 1339 | 1389 | .name = [_]u8{0} ** 8, |
| ... | ... | @@ -1343,9 +1393,9 @@ pub fn deleteExport(self: *Coff, exp: Export) void { |
| 1343 | 1393 | .storage_class = .NULL, |
| 1344 | 1394 | .number_of_aux_symbols = 0, |
| 1345 | 1395 | }; |
| 1346 | self.locals_free_list.append(gpa, sym_index) catch {}; | |
| 1396 | self.locals_free_list.append(gpa, sym_index.*) catch {}; | |
| 1347 | 1397 | |
| 1348 | if (self.resolver.fetchRemove(sym_name)) |entry| { | |
| 1398 | if (self.resolver.fetchRemove(name)) |entry| { | |
| 1349 | 1399 | defer gpa.free(entry.key); |
| 1350 | 1400 | self.globals_free_list.append(gpa, entry.value) catch {}; |
| 1351 | 1401 | self.globals.items[entry.value] = .{ |
| ... | ... | @@ -1353,6 +1403,8 @@ pub fn deleteExport(self: *Coff, exp: Export) void { |
| 1353 | 1403 | .file = null, |
| 1354 | 1404 | }; |
| 1355 | 1405 | } |
| 1406 | ||
| 1407 | sym_index.* = 0; | |
| 1356 | 1408 | } |
| 1357 | 1409 | |
| 1358 | 1410 | fn resolveGlobalSymbol(self: *Coff, current: SymbolWithLoc) !void { |
| ... | ... | @@ -1417,9 +1469,10 @@ pub fn flushModule(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod |
| 1417 | 1469 | if (self.imports_table.contains(global)) continue; |
| 1418 | 1470 | |
| 1419 | 1471 | const import_index = try self.allocateImportEntry(global); |
| 1420 | const import_atom = try self.createImportAtom(); | |
| 1421 | self.imports.items[import_index].sym_index = import_atom.sym_index; | |
| 1422 | try self.writePtrWidthAtom(import_atom); | |
| 1472 | const import_atom_index = try self.createImportAtom(); | |
| 1473 | const import_atom = self.getAtom(import_atom_index); | |
| 1474 | self.imports.items[import_index].sym_index = import_atom.getSymbolIndex().?; | |
| 1475 | try self.writePtrWidthAtom(import_atom_index); | |
| 1423 | 1476 | } |
| 1424 | 1477 | |
| 1425 | 1478 | if (build_options.enable_logging) { |
| ... | ... | @@ -1453,20 +1506,14 @@ pub fn flushModule(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod |
| 1453 | 1506 | } |
| 1454 | 1507 | } |
| 1455 | 1508 | |
| 1456 | pub fn getDeclVAddr( | |
| 1457 | self: *Coff, | |
| 1458 | decl_index: Module.Decl.Index, | |
| 1459 | reloc_info: link.File.RelocInfo, | |
| 1460 | ) !u64 { | |
| 1461 | const mod = self.base.options.module.?; | |
| 1462 | const decl = mod.declPtr(decl_index); | |
| 1463 | ||
| 1509 | pub fn getDeclVAddr(self: *Coff, decl_index: Module.Decl.Index, reloc_info: link.File.RelocInfo) !u64 { | |
| 1464 | 1510 | assert(self.llvm_object == null); |
| 1465 | assert(decl.link.coff.sym_index != 0); | |
| 1466 | 1511 | |
| 1467 | const atom = self.getAtomForSymbol(.{ .sym_index = reloc_info.parent_atom_index, .file = null }).?; | |
| 1468 | const target = SymbolWithLoc{ .sym_index = decl.link.coff.sym_index, .file = null }; | |
| 1469 | try atom.addRelocation(self, .{ | |
| 1512 | const this_atom_index = try self.getOrCreateAtomForDecl(decl_index); | |
| 1513 | const sym_index = self.getAtom(this_atom_index).getSymbolIndex().?; | |
| 1514 | const atom_index = self.getAtomIndexForSymbol(.{ .sym_index = reloc_info.parent_atom_index, .file = null }).?; | |
| 1515 | const target = SymbolWithLoc{ .sym_index = sym_index, .file = null }; | |
| 1516 | try Atom.addRelocation(self, atom_index, .{ | |
| 1470 | 1517 | .type = .direct, |
| 1471 | 1518 | .target = target, |
| 1472 | 1519 | .offset = @intCast(u32, reloc_info.offset), |
| ... | ... | @@ -1474,7 +1521,7 @@ pub fn getDeclVAddr( |
| 1474 | 1521 | .pcrel = false, |
| 1475 | 1522 | .length = 3, |
| 1476 | 1523 | }); |
| 1477 | try atom.addBaseRelocation(self, @intCast(u32, reloc_info.offset)); | |
| 1524 | try Atom.addBaseRelocation(self, atom_index, @intCast(u32, reloc_info.offset)); | |
| 1478 | 1525 | |
| 1479 | 1526 | return 0; |
| 1480 | 1527 | } |
| ... | ... | @@ -1501,10 +1548,10 @@ pub fn getGlobalSymbol(self: *Coff, name: []const u8) !u32 { |
| 1501 | 1548 | return global_index; |
| 1502 | 1549 | } |
| 1503 | 1550 | |
| 1504 | pub fn updateDeclLineNumber(self: *Coff, module: *Module, decl: *Module.Decl) !void { | |
| 1551 | pub fn updateDeclLineNumber(self: *Coff, module: *Module, decl_index: Module.Decl.Index) !void { | |
| 1505 | 1552 | _ = self; |
| 1506 | 1553 | _ = module; |
| 1507 | _ = decl; | |
| 1554 | _ = decl_index; | |
| 1508 | 1555 | log.debug("TODO implement updateDeclLineNumber", .{}); |
| 1509 | 1556 | } |
| 1510 | 1557 | |
| ... | ... | @@ -1525,7 +1572,8 @@ fn writeBaseRelocations(self: *Coff) !void { |
| 1525 | 1572 | |
| 1526 | 1573 | var it = self.base_relocs.iterator(); |
| 1527 | 1574 | while (it.next()) |entry| { |
| 1528 | const atom = entry.key_ptr.*; | |
| 1575 | const atom_index = entry.key_ptr.*; | |
| 1576 | const atom = self.getAtom(atom_index); | |
| 1529 | 1577 | const offsets = entry.value_ptr.*; |
| 1530 | 1578 | |
| 1531 | 1579 | for (offsets.items) |offset| { |
| ... | ... | @@ -1609,7 +1657,8 @@ fn writeImportTable(self: *Coff) !void { |
| 1609 | 1657 | const gpa = self.base.allocator; |
| 1610 | 1658 | |
| 1611 | 1659 | const section = self.sections.get(self.idata_section_index.?); |
| 1612 | const last_atom = section.last_atom orelse return; | |
| 1660 | const last_atom_index = section.last_atom_index orelse return; | |
| 1661 | const last_atom = self.getAtom(last_atom_index); | |
| 1613 | 1662 | |
| 1614 | 1663 | const iat_rva = section.header.virtual_address; |
| 1615 | 1664 | const iat_size = last_atom.getSymbol(self).value + last_atom.size * 2 - iat_rva; // account for sentinel zero pointer |
| ... | ... | @@ -2047,27 +2096,37 @@ pub fn getOrPutGlobalPtr(self: *Coff, name: []const u8) !GetOrPutGlobalPtrResult |
| 2047 | 2096 | return GetOrPutGlobalPtrResult{ .found_existing = false, .value_ptr = ptr }; |
| 2048 | 2097 | } |
| 2049 | 2098 | |
| 2099 | pub fn getAtom(self: *const Coff, atom_index: Atom.Index) Atom { | |
| 2100 | assert(atom_index < self.atoms.items.len); | |
| 2101 | return self.atoms.items[atom_index]; | |
| 2102 | } | |
| 2103 | ||
| 2104 | pub fn getAtomPtr(self: *Coff, atom_index: Atom.Index) *Atom { | |
| 2105 | assert(atom_index < self.atoms.items.len); | |
| 2106 | return &self.atoms.items[atom_index]; | |
| 2107 | } | |
| 2108 | ||
| 2050 | 2109 | /// Returns atom if there is an atom referenced by the symbol described by `sym_loc` descriptor. |
| 2051 | 2110 | /// Returns null on failure. |
| 2052 | pub fn getAtomForSymbol(self: *Coff, sym_loc: SymbolWithLoc) ?*Atom { | |
| 2111 | pub fn getAtomIndexForSymbol(self: *const Coff, sym_loc: SymbolWithLoc) ?Atom.Index { | |
| 2053 | 2112 | assert(sym_loc.file == null); // TODO linking with object files |
| 2054 | 2113 | return self.atom_by_index_table.get(sym_loc.sym_index); |
| 2055 | 2114 | } |
| 2056 | 2115 | |
| 2057 | 2116 | /// Returns GOT atom that references `sym_loc` if one exists. |
| 2058 | 2117 | /// Returns null otherwise. |
| 2059 | pub fn getGotAtomForSymbol(self: *Coff, sym_loc: SymbolWithLoc) ?*Atom { | |
| 2118 | pub fn getGotAtomIndexForSymbol(self: *const Coff, sym_loc: SymbolWithLoc) ?Atom.Index { | |
| 2060 | 2119 | const got_index = self.got_entries_table.get(sym_loc) orelse return null; |
| 2061 | 2120 | const got_entry = self.got_entries.items[got_index]; |
| 2062 | return self.getAtomForSymbol(.{ .sym_index = got_entry.sym_index, .file = null }); | |
| 2121 | return self.getAtomIndexForSymbol(.{ .sym_index = got_entry.sym_index, .file = null }); | |
| 2063 | 2122 | } |
| 2064 | 2123 | |
| 2065 | 2124 | /// Returns import atom that references `sym_loc` if one exists. |
| 2066 | 2125 | /// Returns null otherwise. |
| 2067 | pub fn getImportAtomForSymbol(self: *Coff, sym_loc: SymbolWithLoc) ?*Atom { | |
| 2126 | pub fn getImportAtomIndexForSymbol(self: *const Coff, sym_loc: SymbolWithLoc) ?Atom.Index { | |
| 2068 | 2127 | const imports_index = self.imports_table.get(sym_loc) orelse return null; |
| 2069 | 2128 | const imports_entry = self.imports.items[imports_index]; |
| 2070 | return self.getAtomForSymbol(.{ .sym_index = imports_entry.sym_index, .file = null }); | |
| 2129 | return self.getAtomIndexForSymbol(.{ .sym_index = imports_entry.sym_index, .file = null }); | |
| 2071 | 2130 | } |
| 2072 | 2131 | |
| 2073 | 2132 | fn setSectionName(self: *Coff, header: *coff.SectionHeader, name: []const u8) !void { |
src/link/Coff/Atom.zig+37-22| ... | ... | @@ -27,42 +27,44 @@ alignment: u32, |
| 27 | 27 | |
| 28 | 28 | /// Points to the previous and next neighbors, based on the `text_offset`. |
| 29 | 29 | /// This can be used to find, for example, the capacity of this `Atom`. |
| 30 | prev: ?*Atom, | |
| 31 | next: ?*Atom, | |
| 32 | ||
| 33 | pub const empty = Atom{ | |
| 34 | .sym_index = 0, | |
| 35 | .file = null, | |
| 36 | .size = 0, | |
| 37 | .alignment = 0, | |
| 38 | .prev = null, | |
| 39 | .next = null, | |
| 40 | }; | |
| 30 | prev_index: ?Index, | |
| 31 | next_index: ?Index, | |
| 32 | ||
| 33 | pub const Index = u32; | |
| 34 | ||
| 35 | pub fn getSymbolIndex(self: Atom) ?u32 { | |
| 36 | if (self.sym_index == 0) return null; | |
| 37 | return self.sym_index; | |
| 38 | } | |
| 41 | 39 | |
| 42 | 40 | /// Returns symbol referencing this atom. |
| 43 | 41 | pub fn getSymbol(self: Atom, coff_file: *const Coff) *const coff.Symbol { |
| 42 | const sym_index = self.getSymbolIndex().?; | |
| 44 | 43 | return coff_file.getSymbol(.{ |
| 45 | .sym_index = self.sym_index, | |
| 44 | .sym_index = sym_index, | |
| 46 | 45 | .file = self.file, |
| 47 | 46 | }); |
| 48 | 47 | } |
| 49 | 48 | |
| 50 | 49 | /// Returns pointer-to-symbol referencing this atom. |
| 51 | 50 | pub fn getSymbolPtr(self: Atom, coff_file: *Coff) *coff.Symbol { |
| 51 | const sym_index = self.getSymbolIndex().?; | |
| 52 | 52 | return coff_file.getSymbolPtr(.{ |
| 53 | .sym_index = self.sym_index, | |
| 53 | .sym_index = sym_index, | |
| 54 | 54 | .file = self.file, |
| 55 | 55 | }); |
| 56 | 56 | } |
| 57 | 57 | |
| 58 | 58 | pub fn getSymbolWithLoc(self: Atom) SymbolWithLoc { |
| 59 | return .{ .sym_index = self.sym_index, .file = self.file }; | |
| 59 | const sym_index = self.getSymbolIndex().?; | |
| 60 | return .{ .sym_index = sym_index, .file = self.file }; | |
| 60 | 61 | } |
| 61 | 62 | |
| 62 | 63 | /// Returns the name of this atom. |
| 63 | 64 | pub fn getName(self: Atom, coff_file: *const Coff) []const u8 { |
| 65 | const sym_index = self.getSymbolIndex().?; | |
| 64 | 66 | return coff_file.getSymbolName(.{ |
| 65 | .sym_index = self.sym_index, | |
| 67 | .sym_index = sym_index, | |
| 66 | 68 | .file = self.file, |
| 67 | 69 | }); |
| 68 | 70 | } |
| ... | ... | @@ -70,7 +72,8 @@ pub fn getName(self: Atom, coff_file: *const Coff) []const u8 { |
| 70 | 72 | /// Returns how much room there is to grow in virtual address space. |
| 71 | 73 | pub fn capacity(self: Atom, coff_file: *const Coff) u32 { |
| 72 | 74 | const self_sym = self.getSymbol(coff_file); |
| 73 | if (self.next) |next| { | |
| 75 | if (self.next_index) |next_index| { | |
| 76 | const next = coff_file.getAtom(next_index); | |
| 74 | 77 | const next_sym = next.getSymbol(coff_file); |
| 75 | 78 | return next_sym.value - self_sym.value; |
| 76 | 79 | } else { |
| ... | ... | @@ -82,7 +85,8 @@ pub fn capacity(self: Atom, coff_file: *const Coff) u32 { |
| 82 | 85 | |
| 83 | 86 | pub fn freeListEligible(self: Atom, coff_file: *const Coff) bool { |
| 84 | 87 | // No need to keep a free list node for the last atom. |
| 85 | const next = self.next orelse return false; | |
| 88 | const next_index = self.next_index orelse return false; | |
| 89 | const next = coff_file.getAtom(next_index); | |
| 86 | 90 | const self_sym = self.getSymbol(coff_file); |
| 87 | 91 | const next_sym = next.getSymbol(coff_file); |
| 88 | 92 | const cap = next_sym.value - self_sym.value; |
| ... | ... | @@ -92,22 +96,33 @@ pub fn freeListEligible(self: Atom, coff_file: *const Coff) bool { |
| 92 | 96 | return surplus >= Coff.min_text_capacity; |
| 93 | 97 | } |
| 94 | 98 | |
| 95 | pub fn addRelocation(self: *Atom, coff_file: *Coff, reloc: Relocation) !void { | |
| 99 | pub fn addRelocation(coff_file: *Coff, atom_index: Index, reloc: Relocation) !void { | |
| 96 | 100 | const gpa = coff_file.base.allocator; |
| 97 | 101 | log.debug(" (adding reloc of type {s} to target %{d})", .{ @tagName(reloc.type), reloc.target.sym_index }); |
| 98 | const gop = try coff_file.relocs.getOrPut(gpa, self); | |
| 102 | const gop = try coff_file.relocs.getOrPut(gpa, atom_index); | |
| 99 | 103 | if (!gop.found_existing) { |
| 100 | 104 | gop.value_ptr.* = .{}; |
| 101 | 105 | } |
| 102 | 106 | try gop.value_ptr.append(gpa, reloc); |
| 103 | 107 | } |
| 104 | 108 | |
| 105 | pub fn addBaseRelocation(self: *Atom, coff_file: *Coff, offset: u32) !void { | |
| 109 | pub fn addBaseRelocation(coff_file: *Coff, atom_index: Index, offset: u32) !void { | |
| 106 | 110 | const gpa = coff_file.base.allocator; |
| 107 | log.debug(" (adding base relocation at offset 0x{x} in %{d})", .{ offset, self.sym_index }); | |
| 108 | const gop = try coff_file.base_relocs.getOrPut(gpa, self); | |
| 111 | log.debug(" (adding base relocation at offset 0x{x} in %{d})", .{ | |
| 112 | offset, | |
| 113 | coff_file.getAtom(atom_index).getSymbolIndex().?, | |
| 114 | }); | |
| 115 | const gop = try coff_file.base_relocs.getOrPut(gpa, atom_index); | |
| 109 | 116 | if (!gop.found_existing) { |
| 110 | 117 | gop.value_ptr.* = .{}; |
| 111 | 118 | } |
| 112 | 119 | try gop.value_ptr.append(gpa, offset); |
| 113 | 120 | } |
| 121 | ||
| 122 | pub fn freeRelocations(coff_file: *Coff, atom_index: Index) void { | |
| 123 | const gpa = coff_file.base.allocator; | |
| 124 | var removed_relocs = coff_file.relocs.fetchRemove(atom_index); | |
| 125 | if (removed_relocs) |*relocs| relocs.value.deinit(gpa); | |
| 126 | var removed_base_relocs = coff_file.base_relocs.fetchRemove(atom_index); | |
| 127 | if (removed_base_relocs) |*base_relocs| base_relocs.value.deinit(gpa); | |
| 128 | } |
src/link/Coff/Relocation.zig+10-8| ... | ... | @@ -46,33 +46,35 @@ length: u2, |
| 46 | 46 | dirty: bool = true, |
| 47 | 47 | |
| 48 | 48 | /// Returns an Atom which is the target node of this relocation edge (if any). |
| 49 | pub fn getTargetAtom(self: Relocation, coff_file: *Coff) ?*Atom { | |
| 49 | pub fn getTargetAtomIndex(self: Relocation, coff_file: *const Coff) ?Atom.Index { | |
| 50 | 50 | switch (self.type) { |
| 51 | 51 | .got, |
| 52 | 52 | .got_page, |
| 53 | 53 | .got_pageoff, |
| 54 | => return coff_file.getGotAtomForSymbol(self.target), | |
| 54 | => return coff_file.getGotAtomIndexForSymbol(self.target), | |
| 55 | 55 | |
| 56 | 56 | .direct, |
| 57 | 57 | .page, |
| 58 | 58 | .pageoff, |
| 59 | => return coff_file.getAtomForSymbol(self.target), | |
| 59 | => return coff_file.getAtomIndexForSymbol(self.target), | |
| 60 | 60 | |
| 61 | 61 | .import, |
| 62 | 62 | .import_page, |
| 63 | 63 | .import_pageoff, |
| 64 | => return coff_file.getImportAtomForSymbol(self.target), | |
| 64 | => return coff_file.getImportAtomIndexForSymbol(self.target), | |
| 65 | 65 | } |
| 66 | 66 | } |
| 67 | 67 | |
| 68 | pub fn resolve(self: *Relocation, atom: *Atom, coff_file: *Coff) !void { | |
| 68 | pub fn resolve(self: *Relocation, atom_index: Atom.Index, coff_file: *Coff) !void { | |
| 69 | const atom = coff_file.getAtom(atom_index); | |
| 69 | 70 | const source_sym = atom.getSymbol(coff_file); |
| 70 | 71 | const source_section = coff_file.sections.get(@enumToInt(source_sym.section_number) - 1).header; |
| 71 | 72 | const source_vaddr = source_sym.value + self.offset; |
| 72 | 73 | |
| 73 | 74 | const file_offset = source_section.pointer_to_raw_data + source_sym.value - source_section.virtual_address; |
| 74 | 75 | |
| 75 | const target_atom = self.getTargetAtom(coff_file) orelse return; | |
| 76 | const target_atom_index = self.getTargetAtomIndex(coff_file) orelse return; | |
| 77 | const target_atom = coff_file.getAtom(target_atom_index); | |
| 76 | 78 | const target_vaddr = target_atom.getSymbol(coff_file).value; |
| 77 | 79 | const target_vaddr_with_addend = target_vaddr + self.addend; |
| 78 | 80 | |
| ... | ... | @@ -107,7 +109,7 @@ const Context = struct { |
| 107 | 109 | image_base: u64, |
| 108 | 110 | }; |
| 109 | 111 | |
| 110 | fn resolveAarch64(self: *Relocation, ctx: Context, coff_file: *Coff) !void { | |
| 112 | fn resolveAarch64(self: Relocation, ctx: Context, coff_file: *Coff) !void { | |
| 111 | 113 | var buffer: [@sizeOf(u64)]u8 = undefined; |
| 112 | 114 | switch (self.length) { |
| 113 | 115 | 2 => { |
| ... | ... | @@ -197,7 +199,7 @@ fn resolveAarch64(self: *Relocation, ctx: Context, coff_file: *Coff) !void { |
| 197 | 199 | } |
| 198 | 200 | } |
| 199 | 201 | |
| 200 | fn resolveX86(self: *Relocation, ctx: Context, coff_file: *Coff) !void { | |
| 202 | fn resolveX86(self: Relocation, ctx: Context, coff_file: *Coff) !void { | |
| 201 | 203 | switch (self.type) { |
| 202 | 204 | .got_page => unreachable, |
| 203 | 205 | .got_pageoff => unreachable, |
src/link/Dwarf.zig+320-259| ... | ... | @@ -18,31 +18,36 @@ const LinkBlock = File.LinkBlock; |
| 18 | 18 | const LinkFn = File.LinkFn; |
| 19 | 19 | const LinkerLoad = @import("../codegen.zig").LinkerLoad; |
| 20 | 20 | const Module = @import("../Module.zig"); |
| 21 | const Value = @import("../value.zig").Value; | |
| 21 | const StringTable = @import("strtab.zig").StringTable; | |
| 22 | 22 | const Type = @import("../type.zig").Type; |
| 23 | const Value = @import("../value.zig").Value; | |
| 23 | 24 | |
| 24 | 25 | allocator: Allocator, |
| 25 | 26 | bin_file: *File, |
| 26 | 27 | ptr_width: PtrWidth, |
| 27 | 28 | target: std.Target, |
| 28 | 29 | |
| 29 | /// A list of `File.LinkFn` whose Line Number Programs have surplus capacity. | |
| 30 | /// This is the same concept as `text_block_free_list`; see those doc comments. | |
| 31 | dbg_line_fn_free_list: std.AutoHashMapUnmanaged(*SrcFn, void) = .{}, | |
| 32 | dbg_line_fn_first: ?*SrcFn = null, | |
| 33 | dbg_line_fn_last: ?*SrcFn = null, | |
| 30 | /// A list of `Atom`s whose Line Number Programs have surplus capacity. | |
| 31 | /// This is the same concept as `Section.free_list` in Elf; see those doc comments. | |
| 32 | src_fn_free_list: std.AutoHashMapUnmanaged(Atom.Index, void) = .{}, | |
| 33 | src_fn_first_index: ?Atom.Index = null, | |
| 34 | src_fn_last_index: ?Atom.Index = null, | |
| 35 | src_fns: std.ArrayListUnmanaged(Atom) = .{}, | |
| 36 | src_fn_decls: AtomTable = .{}, | |
| 34 | 37 | |
| 35 | 38 | /// A list of `Atom`s whose corresponding .debug_info tags have surplus capacity. |
| 36 | 39 | /// This is the same concept as `text_block_free_list`; see those doc comments. |
| 37 | atom_free_list: std.AutoHashMapUnmanaged(*Atom, void) = .{}, | |
| 38 | atom_first: ?*Atom = null, | |
| 39 | atom_last: ?*Atom = null, | |
| 40 | di_atom_free_list: std.AutoHashMapUnmanaged(Atom.Index, void) = .{}, | |
| 41 | di_atom_first_index: ?Atom.Index = null, | |
| 42 | di_atom_last_index: ?Atom.Index = null, | |
| 43 | di_atoms: std.ArrayListUnmanaged(Atom) = .{}, | |
| 44 | di_atom_decls: AtomTable = .{}, | |
| 40 | 45 | |
| 41 | 46 | abbrev_table_offset: ?u64 = null, |
| 42 | 47 | |
| 43 | 48 | /// TODO replace with InternPool |
| 44 | 49 | /// Table of debug symbol names. |
| 45 | strtab: std.ArrayListUnmanaged(u8) = .{}, | |
| 50 | strtab: StringTable(.strtab) = .{}, | |
| 46 | 51 | |
| 47 | 52 | /// Quick lookup array of all defined source files referenced by at least one Decl. |
| 48 | 53 | /// They will end up in the DWARF debug_line header as two lists: |
| ... | ... | @@ -50,22 +55,23 @@ strtab: std.ArrayListUnmanaged(u8) = .{}, |
| 50 | 55 | /// * []file_names |
| 51 | 56 | di_files: std.AutoArrayHashMapUnmanaged(*const Module.File, void) = .{}, |
| 52 | 57 | |
| 53 | /// List of atoms that are owned directly by the DWARF module. | |
| 54 | /// TODO convert links in DebugInfoAtom into indices and make | |
| 55 | /// sure every atom is owned by this module. | |
| 56 | managed_atoms: std.ArrayListUnmanaged(*Atom) = .{}, | |
| 57 | ||
| 58 | 58 | global_abbrev_relocs: std.ArrayListUnmanaged(AbbrevRelocation) = .{}, |
| 59 | 59 | |
| 60 | pub const Atom = struct { | |
| 61 | /// Previous/next linked list pointers. | |
| 62 | /// This is the linked list node for this Decl's corresponding .debug_info tag. | |
| 63 | prev: ?*Atom, | |
| 64 | next: ?*Atom, | |
| 65 | /// Offset into .debug_info pointing to the tag for this Decl. | |
| 60 | const AtomTable = std.AutoHashMapUnmanaged(Module.Decl.Index, Atom.Index); | |
| 61 | ||
| 62 | const Atom = struct { | |
| 63 | /// Offset into .debug_info pointing to the tag for this Decl, or | |
| 64 | /// offset from the beginning of the Debug Line Program header that contains this function. | |
| 66 | 65 | off: u32, |
| 67 | /// Size of the .debug_info tag for this Decl, not including padding. | |
| 66 | /// Size of the .debug_info tag for this Decl, not including padding, or | |
| 67 | /// size of the line number program component belonging to this function, not | |
| 68 | /// including padding. | |
| 68 | 69 | len: u32, |
| 70 | ||
| 71 | prev_index: ?Index, | |
| 72 | next_index: ?Index, | |
| 73 | ||
| 74 | pub const Index = u32; | |
| 69 | 75 | }; |
| 70 | 76 | |
| 71 | 77 | /// Represents state of the analysed Decl. |
| ... | ... | @@ -75,6 +81,7 @@ pub const Atom = struct { |
| 75 | 81 | pub const DeclState = struct { |
| 76 | 82 | gpa: Allocator, |
| 77 | 83 | mod: *Module, |
| 84 | di_atom_decls: *const AtomTable, | |
| 78 | 85 | dbg_line: std.ArrayList(u8), |
| 79 | 86 | dbg_info: std.ArrayList(u8), |
| 80 | 87 | abbrev_type_arena: std.heap.ArenaAllocator, |
| ... | ... | @@ -88,10 +95,11 @@ pub const DeclState = struct { |
| 88 | 95 | abbrev_relocs: std.ArrayListUnmanaged(AbbrevRelocation) = .{}, |
| 89 | 96 | exprloc_relocs: std.ArrayListUnmanaged(ExprlocRelocation) = .{}, |
| 90 | 97 | |
| 91 | fn init(gpa: Allocator, mod: *Module) DeclState { | |
| 98 | fn init(gpa: Allocator, mod: *Module, di_atom_decls: *const AtomTable) DeclState { | |
| 92 | 99 | return .{ |
| 93 | 100 | .gpa = gpa, |
| 94 | 101 | .mod = mod, |
| 102 | .di_atom_decls = di_atom_decls, | |
| 95 | 103 | .dbg_line = std.ArrayList(u8).init(gpa), |
| 96 | 104 | .dbg_info = std.ArrayList(u8).init(gpa), |
| 97 | 105 | .abbrev_type_arena = std.heap.ArenaAllocator.init(gpa), |
| ... | ... | @@ -119,11 +127,11 @@ pub const DeclState = struct { |
| 119 | 127 | |
| 120 | 128 | /// Adds local type relocation of the form: @offset => @this + addend |
| 121 | 129 | /// @this signifies the offset within the .debug_abbrev section of the containing atom. |
| 122 | fn addTypeRelocLocal(self: *DeclState, atom: *const Atom, offset: u32, addend: u32) !void { | |
| 130 | fn addTypeRelocLocal(self: *DeclState, atom_index: Atom.Index, offset: u32, addend: u32) !void { | |
| 123 | 131 | log.debug("{x}: @this + {x}", .{ offset, addend }); |
| 124 | 132 | try self.abbrev_relocs.append(self.gpa, .{ |
| 125 | 133 | .target = null, |
| 126 | .atom = atom, | |
| 134 | .atom_index = atom_index, | |
| 127 | 135 | .offset = offset, |
| 128 | 136 | .addend = addend, |
| 129 | 137 | }); |
| ... | ... | @@ -132,13 +140,13 @@ pub const DeclState = struct { |
| 132 | 140 | /// Adds global type relocation of the form: @offset => @symbol + 0 |
| 133 | 141 | /// @symbol signifies a type abbreviation posititioned somewhere in the .debug_abbrev section |
| 134 | 142 | /// which we use as our target of the relocation. |
| 135 | fn addTypeRelocGlobal(self: *DeclState, atom: *const Atom, ty: Type, offset: u32) !void { | |
| 143 | fn addTypeRelocGlobal(self: *DeclState, atom_index: Atom.Index, ty: Type, offset: u32) !void { | |
| 136 | 144 | const resolv = self.abbrev_resolver.getContext(ty, .{ |
| 137 | 145 | .mod = self.mod, |
| 138 | 146 | }) orelse blk: { |
| 139 | 147 | const sym_index = @intCast(u32, self.abbrev_table.items.len); |
| 140 | 148 | try self.abbrev_table.append(self.gpa, .{ |
| 141 | .atom = atom, | |
| 149 | .atom_index = atom_index, | |
| 142 | 150 | .type = ty, |
| 143 | 151 | .offset = undefined, |
| 144 | 152 | }); |
| ... | ... | @@ -153,7 +161,7 @@ pub const DeclState = struct { |
| 153 | 161 | log.debug("{x}: %{d} + 0", .{ offset, resolv }); |
| 154 | 162 | try self.abbrev_relocs.append(self.gpa, .{ |
| 155 | 163 | .target = resolv, |
| 156 | .atom = atom, | |
| 164 | .atom_index = atom_index, | |
| 157 | 165 | .offset = offset, |
| 158 | 166 | .addend = 0, |
| 159 | 167 | }); |
| ... | ... | @@ -162,7 +170,7 @@ pub const DeclState = struct { |
| 162 | 170 | fn addDbgInfoType( |
| 163 | 171 | self: *DeclState, |
| 164 | 172 | module: *Module, |
| 165 | atom: *Atom, | |
| 173 | atom_index: Atom.Index, | |
| 166 | 174 | ty: Type, |
| 167 | 175 | ) error{OutOfMemory}!void { |
| 168 | 176 | const arena = self.abbrev_type_arena.allocator(); |
| ... | ... | @@ -227,7 +235,7 @@ pub const DeclState = struct { |
| 227 | 235 | // DW.AT.type, DW.FORM.ref4 |
| 228 | 236 | var index = dbg_info_buffer.items.len; |
| 229 | 237 | try dbg_info_buffer.resize(index + 4); |
| 230 | try self.addTypeRelocGlobal(atom, Type.bool, @intCast(u32, index)); | |
| 238 | try self.addTypeRelocGlobal(atom_index, Type.bool, @intCast(u32, index)); | |
| 231 | 239 | // DW.AT.data_member_location, DW.FORM.sdata |
| 232 | 240 | try dbg_info_buffer.ensureUnusedCapacity(6); |
| 233 | 241 | dbg_info_buffer.appendAssumeCapacity(0); |
| ... | ... | @@ -239,7 +247,7 @@ pub const DeclState = struct { |
| 239 | 247 | // DW.AT.type, DW.FORM.ref4 |
| 240 | 248 | index = dbg_info_buffer.items.len; |
| 241 | 249 | try dbg_info_buffer.resize(index + 4); |
| 242 | try self.addTypeRelocGlobal(atom, payload_ty, @intCast(u32, index)); | |
| 250 | try self.addTypeRelocGlobal(atom_index, payload_ty, @intCast(u32, index)); | |
| 243 | 251 | // DW.AT.data_member_location, DW.FORM.sdata |
| 244 | 252 | const offset = abi_size - payload_ty.abiSize(target); |
| 245 | 253 | try leb128.writeULEB128(dbg_info_buffer.writer(), offset); |
| ... | ... | @@ -270,7 +278,7 @@ pub const DeclState = struct { |
| 270 | 278 | try dbg_info_buffer.resize(index + 4); |
| 271 | 279 | var buf = try arena.create(Type.SlicePtrFieldTypeBuffer); |
| 272 | 280 | const ptr_ty = ty.slicePtrFieldType(buf); |
| 273 | try self.addTypeRelocGlobal(atom, ptr_ty, @intCast(u32, index)); | |
| 281 | try self.addTypeRelocGlobal(atom_index, ptr_ty, @intCast(u32, index)); | |
| 274 | 282 | // DW.AT.data_member_location, DW.FORM.sdata |
| 275 | 283 | try dbg_info_buffer.ensureUnusedCapacity(6); |
| 276 | 284 | dbg_info_buffer.appendAssumeCapacity(0); |
| ... | ... | @@ -282,7 +290,7 @@ pub const DeclState = struct { |
| 282 | 290 | // DW.AT.type, DW.FORM.ref4 |
| 283 | 291 | index = dbg_info_buffer.items.len; |
| 284 | 292 | try dbg_info_buffer.resize(index + 4); |
| 285 | try self.addTypeRelocGlobal(atom, Type.usize, @intCast(u32, index)); | |
| 293 | try self.addTypeRelocGlobal(atom_index, Type.usize, @intCast(u32, index)); | |
| 286 | 294 | // DW.AT.data_member_location, DW.FORM.sdata |
| 287 | 295 | try dbg_info_buffer.ensureUnusedCapacity(2); |
| 288 | 296 | dbg_info_buffer.appendAssumeCapacity(ptr_bytes); |
| ... | ... | @@ -294,7 +302,7 @@ pub const DeclState = struct { |
| 294 | 302 | // DW.AT.type, DW.FORM.ref4 |
| 295 | 303 | const index = dbg_info_buffer.items.len; |
| 296 | 304 | try dbg_info_buffer.resize(index + 4); |
| 297 | try self.addTypeRelocGlobal(atom, ty.childType(), @intCast(u32, index)); | |
| 305 | try self.addTypeRelocGlobal(atom_index, ty.childType(), @intCast(u32, index)); | |
| 298 | 306 | } |
| 299 | 307 | }, |
| 300 | 308 | .Array => { |
| ... | ... | @@ -305,13 +313,13 @@ pub const DeclState = struct { |
| 305 | 313 | // DW.AT.type, DW.FORM.ref4 |
| 306 | 314 | var index = dbg_info_buffer.items.len; |
| 307 | 315 | try dbg_info_buffer.resize(index + 4); |
| 308 | try self.addTypeRelocGlobal(atom, ty.childType(), @intCast(u32, index)); | |
| 316 | try self.addTypeRelocGlobal(atom_index, ty.childType(), @intCast(u32, index)); | |
| 309 | 317 | // DW.AT.subrange_type |
| 310 | 318 | try dbg_info_buffer.append(@enumToInt(AbbrevKind.array_dim)); |
| 311 | 319 | // DW.AT.type, DW.FORM.ref4 |
| 312 | 320 | index = dbg_info_buffer.items.len; |
| 313 | 321 | try dbg_info_buffer.resize(index + 4); |
| 314 | try self.addTypeRelocGlobal(atom, Type.usize, @intCast(u32, index)); | |
| 322 | try self.addTypeRelocGlobal(atom_index, Type.usize, @intCast(u32, index)); | |
| 315 | 323 | // DW.AT.count, DW.FORM.udata |
| 316 | 324 | const len = ty.arrayLenIncludingSentinel(); |
| 317 | 325 | try leb128.writeULEB128(dbg_info_buffer.writer(), len); |
| ... | ... | @@ -339,7 +347,7 @@ pub const DeclState = struct { |
| 339 | 347 | // DW.AT.type, DW.FORM.ref4 |
| 340 | 348 | var index = dbg_info_buffer.items.len; |
| 341 | 349 | try dbg_info_buffer.resize(index + 4); |
| 342 | try self.addTypeRelocGlobal(atom, field, @intCast(u32, index)); | |
| 350 | try self.addTypeRelocGlobal(atom_index, field, @intCast(u32, index)); | |
| 343 | 351 | // DW.AT.data_member_location, DW.FORM.sdata |
| 344 | 352 | const field_off = ty.structFieldOffset(field_index, target); |
| 345 | 353 | try leb128.writeULEB128(dbg_info_buffer.writer(), field_off); |
| ... | ... | @@ -371,7 +379,7 @@ pub const DeclState = struct { |
| 371 | 379 | // DW.AT.type, DW.FORM.ref4 |
| 372 | 380 | var index = dbg_info_buffer.items.len; |
| 373 | 381 | try dbg_info_buffer.resize(index + 4); |
| 374 | try self.addTypeRelocGlobal(atom, field.ty, @intCast(u32, index)); | |
| 382 | try self.addTypeRelocGlobal(atom_index, field.ty, @intCast(u32, index)); | |
| 375 | 383 | // DW.AT.data_member_location, DW.FORM.sdata |
| 376 | 384 | const field_off = ty.structFieldOffset(field_index, target); |
| 377 | 385 | try leb128.writeULEB128(dbg_info_buffer.writer(), field_off); |
| ... | ... | @@ -454,7 +462,7 @@ pub const DeclState = struct { |
| 454 | 462 | // DW.AT.type, DW.FORM.ref4 |
| 455 | 463 | const inner_union_index = dbg_info_buffer.items.len; |
| 456 | 464 | try dbg_info_buffer.resize(inner_union_index + 4); |
| 457 | try self.addTypeRelocLocal(atom, @intCast(u32, inner_union_index), 5); | |
| 465 | try self.addTypeRelocLocal(atom_index, @intCast(u32, inner_union_index), 5); | |
| 458 | 466 | // DW.AT.data_member_location, DW.FORM.sdata |
| 459 | 467 | try leb128.writeULEB128(dbg_info_buffer.writer(), payload_offset); |
| 460 | 468 | } |
| ... | ... | @@ -481,7 +489,7 @@ pub const DeclState = struct { |
| 481 | 489 | // DW.AT.type, DW.FORM.ref4 |
| 482 | 490 | const index = dbg_info_buffer.items.len; |
| 483 | 491 | try dbg_info_buffer.resize(index + 4); |
| 484 | try self.addTypeRelocGlobal(atom, field.ty, @intCast(u32, index)); | |
| 492 | try self.addTypeRelocGlobal(atom_index, field.ty, @intCast(u32, index)); | |
| 485 | 493 | // DW.AT.data_member_location, DW.FORM.sdata |
| 486 | 494 | try dbg_info_buffer.append(0); |
| 487 | 495 | } |
| ... | ... | @@ -498,7 +506,7 @@ pub const DeclState = struct { |
| 498 | 506 | // DW.AT.type, DW.FORM.ref4 |
| 499 | 507 | const index = dbg_info_buffer.items.len; |
| 500 | 508 | try dbg_info_buffer.resize(index + 4); |
| 501 | try self.addTypeRelocGlobal(atom, union_obj.tag_ty, @intCast(u32, index)); | |
| 509 | try self.addTypeRelocGlobal(atom_index, union_obj.tag_ty, @intCast(u32, index)); | |
| 502 | 510 | // DW.AT.data_member_location, DW.FORM.sdata |
| 503 | 511 | try leb128.writeULEB128(dbg_info_buffer.writer(), tag_offset); |
| 504 | 512 | |
| ... | ... | @@ -541,7 +549,7 @@ pub const DeclState = struct { |
| 541 | 549 | // DW.AT.type, DW.FORM.ref4 |
| 542 | 550 | var index = dbg_info_buffer.items.len; |
| 543 | 551 | try dbg_info_buffer.resize(index + 4); |
| 544 | try self.addTypeRelocGlobal(atom, payload_ty, @intCast(u32, index)); | |
| 552 | try self.addTypeRelocGlobal(atom_index, payload_ty, @intCast(u32, index)); | |
| 545 | 553 | // DW.AT.data_member_location, DW.FORM.sdata |
| 546 | 554 | try leb128.writeULEB128(dbg_info_buffer.writer(), payload_off); |
| 547 | 555 | |
| ... | ... | @@ -554,7 +562,7 @@ pub const DeclState = struct { |
| 554 | 562 | // DW.AT.type, DW.FORM.ref4 |
| 555 | 563 | index = dbg_info_buffer.items.len; |
| 556 | 564 | try dbg_info_buffer.resize(index + 4); |
| 557 | try self.addTypeRelocGlobal(atom, error_ty, @intCast(u32, index)); | |
| 565 | try self.addTypeRelocGlobal(atom_index, error_ty, @intCast(u32, index)); | |
| 558 | 566 | // DW.AT.data_member_location, DW.FORM.sdata |
| 559 | 567 | try leb128.writeULEB128(dbg_info_buffer.writer(), error_off); |
| 560 | 568 | |
| ... | ... | @@ -587,12 +595,11 @@ pub const DeclState = struct { |
| 587 | 595 | self: *DeclState, |
| 588 | 596 | name: [:0]const u8, |
| 589 | 597 | ty: Type, |
| 590 | tag: File.Tag, | |
| 591 | 598 | owner_decl: Module.Decl.Index, |
| 592 | 599 | loc: DbgInfoLoc, |
| 593 | 600 | ) error{OutOfMemory}!void { |
| 594 | 601 | const dbg_info = &self.dbg_info; |
| 595 | const atom = getDbgInfoAtom(tag, self.mod, owner_decl); | |
| 602 | const atom_index = self.di_atom_decls.get(owner_decl).?; | |
| 596 | 603 | const name_with_null = name.ptr[0 .. name.len + 1]; |
| 597 | 604 | |
| 598 | 605 | switch (loc) { |
| ... | ... | @@ -637,7 +644,7 @@ pub const DeclState = struct { |
| 637 | 644 | try dbg_info.ensureUnusedCapacity(5 + name_with_null.len); |
| 638 | 645 | const index = dbg_info.items.len; |
| 639 | 646 | try dbg_info.resize(index + 4); // dw.at.type, dw.form.ref4 |
| 640 | try self.addTypeRelocGlobal(atom, ty, @intCast(u32, index)); // DW.AT.type, DW.FORM.ref4 | |
| 647 | try self.addTypeRelocGlobal(atom_index, ty, @intCast(u32, index)); // DW.AT.type, DW.FORM.ref4 | |
| 641 | 648 | dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string |
| 642 | 649 | } |
| 643 | 650 | |
| ... | ... | @@ -645,13 +652,12 @@ pub const DeclState = struct { |
| 645 | 652 | self: *DeclState, |
| 646 | 653 | name: [:0]const u8, |
| 647 | 654 | ty: Type, |
| 648 | tag: File.Tag, | |
| 649 | 655 | owner_decl: Module.Decl.Index, |
| 650 | 656 | is_ptr: bool, |
| 651 | 657 | loc: DbgInfoLoc, |
| 652 | 658 | ) error{OutOfMemory}!void { |
| 653 | 659 | const dbg_info = &self.dbg_info; |
| 654 | const atom = getDbgInfoAtom(tag, self.mod, owner_decl); | |
| 660 | const atom_index = self.di_atom_decls.get(owner_decl).?; | |
| 655 | 661 | const name_with_null = name.ptr[0 .. name.len + 1]; |
| 656 | 662 | try dbg_info.append(@enumToInt(AbbrevKind.variable)); |
| 657 | 663 | const target = self.mod.getTarget(); |
| ... | ... | @@ -781,7 +787,7 @@ pub const DeclState = struct { |
| 781 | 787 | try dbg_info.ensureUnusedCapacity(5 + name_with_null.len); |
| 782 | 788 | const index = dbg_info.items.len; |
| 783 | 789 | try dbg_info.resize(index + 4); // dw.at.type, dw.form.ref4 |
| 784 | try self.addTypeRelocGlobal(atom, child_ty, @intCast(u32, index)); | |
| 790 | try self.addTypeRelocGlobal(atom_index, child_ty, @intCast(u32, index)); | |
| 785 | 791 | dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string |
| 786 | 792 | } |
| 787 | 793 | |
| ... | ... | @@ -814,7 +820,7 @@ pub const DeclState = struct { |
| 814 | 820 | }; |
| 815 | 821 | |
| 816 | 822 | pub const AbbrevEntry = struct { |
| 817 | atom: *const Atom, | |
| 823 | atom_index: Atom.Index, | |
| 818 | 824 | type: Type, |
| 819 | 825 | offset: u32, |
| 820 | 826 | }; |
| ... | ... | @@ -823,7 +829,7 @@ pub const AbbrevRelocation = struct { |
| 823 | 829 | /// If target is null, we deal with a local relocation that is based on simple offset + addend |
| 824 | 830 | /// only. |
| 825 | 831 | target: ?u32, |
| 826 | atom: *const Atom, | |
| 832 | atom_index: Atom.Index, | |
| 827 | 833 | offset: u32, |
| 828 | 834 | addend: u32, |
| 829 | 835 | }; |
| ... | ... | @@ -840,26 +846,6 @@ pub const ExprlocRelocation = struct { |
| 840 | 846 | offset: u32, |
| 841 | 847 | }; |
| 842 | 848 | |
| 843 | pub const SrcFn = struct { | |
| 844 | /// Offset from the beginning of the Debug Line Program header that contains this function. | |
| 845 | off: u32, | |
| 846 | /// Size of the line number program component belonging to this function, not | |
| 847 | /// including padding. | |
| 848 | len: u32, | |
| 849 | ||
| 850 | /// Points to the previous and next neighbors, based on the offset from .debug_line. | |
| 851 | /// This can be used to find, for example, the capacity of this `SrcFn`. | |
| 852 | prev: ?*SrcFn, | |
| 853 | next: ?*SrcFn, | |
| 854 | ||
| 855 | pub const empty: SrcFn = .{ | |
| 856 | .off = 0, | |
| 857 | .len = 0, | |
| 858 | .prev = null, | |
| 859 | .next = null, | |
| 860 | }; | |
| 861 | }; | |
| 862 | ||
| 863 | 849 | pub const PtrWidth = enum { p32, p64 }; |
| 864 | 850 | |
| 865 | 851 | pub const AbbrevKind = enum(u8) { |
| ... | ... | @@ -909,16 +895,18 @@ pub fn init(allocator: Allocator, bin_file: *File, target: std.Target) Dwarf { |
| 909 | 895 | |
| 910 | 896 | pub fn deinit(self: *Dwarf) void { |
| 911 | 897 | const gpa = self.allocator; |
| 912 | self.dbg_line_fn_free_list.deinit(gpa); | |
| 913 | self.atom_free_list.deinit(gpa); | |
| 898 | ||
| 899 | self.src_fn_free_list.deinit(gpa); | |
| 900 | self.src_fns.deinit(gpa); | |
| 901 | self.src_fn_decls.deinit(gpa); | |
| 902 | ||
| 903 | self.di_atom_free_list.deinit(gpa); | |
| 904 | self.di_atoms.deinit(gpa); | |
| 905 | self.di_atom_decls.deinit(gpa); | |
| 906 | ||
| 914 | 907 | self.strtab.deinit(gpa); |
| 915 | 908 | self.di_files.deinit(gpa); |
| 916 | 909 | self.global_abbrev_relocs.deinit(gpa); |
| 917 | ||
| 918 | for (self.managed_atoms.items) |atom| { | |
| 919 | gpa.destroy(atom); | |
| 920 | } | |
| 921 | self.managed_atoms.deinit(gpa); | |
| 922 | 910 | } |
| 923 | 911 | |
| 924 | 912 | /// Initializes Decl's state and its matching output buffers. |
| ... | ... | @@ -934,15 +922,19 @@ pub fn initDeclState(self: *Dwarf, mod: *Module, decl_index: Module.Decl.Index) |
| 934 | 922 | log.debug("initDeclState {s}{*}", .{ decl_name, decl }); |
| 935 | 923 | |
| 936 | 924 | const gpa = self.allocator; |
| 937 | var decl_state = DeclState.init(gpa, mod); | |
| 925 | var decl_state = DeclState.init(gpa, mod, &self.di_atom_decls); | |
| 938 | 926 | errdefer decl_state.deinit(); |
| 939 | 927 | const dbg_line_buffer = &decl_state.dbg_line; |
| 940 | 928 | const dbg_info_buffer = &decl_state.dbg_info; |
| 941 | 929 | |
| 930 | const di_atom_index = try self.getOrCreateAtomForDecl(.di_atom, decl_index); | |
| 931 | ||
| 942 | 932 | assert(decl.has_tv); |
| 943 | 933 | |
| 944 | 934 | switch (decl.ty.zigTypeTag()) { |
| 945 | 935 | .Fn => { |
| 936 | _ = try self.getOrCreateAtomForDecl(.src_fn, decl_index); | |
| 937 | ||
| 946 | 938 | // For functions we need to add a prologue to the debug line program. |
| 947 | 939 | try dbg_line_buffer.ensureTotalCapacity(26); |
| 948 | 940 | |
| ... | ... | @@ -1002,8 +994,7 @@ pub fn initDeclState(self: *Dwarf, mod: *Module, decl_index: Module.Decl.Index) |
| 1002 | 994 | dbg_info_buffer.items.len += 4; // DW.AT.high_pc, DW.FORM.data4 |
| 1003 | 995 | // |
| 1004 | 996 | if (fn_ret_has_bits) { |
| 1005 | const atom = getDbgInfoAtom(self.bin_file.tag, mod, decl_index); | |
| 1006 | try decl_state.addTypeRelocGlobal(atom, fn_ret_type, @intCast(u32, dbg_info_buffer.items.len)); | |
| 997 | try decl_state.addTypeRelocGlobal(di_atom_index, fn_ret_type, @intCast(u32, dbg_info_buffer.items.len)); | |
| 1007 | 998 | dbg_info_buffer.items.len += 4; // DW.AT.type, DW.FORM.ref4 |
| 1008 | 999 | } |
| 1009 | 1000 | |
| ... | ... | @@ -1075,31 +1066,28 @@ pub fn commitDeclState( |
| 1075 | 1066 | // This logic is nearly identical to the logic below in `updateDeclDebugInfo` for |
| 1076 | 1067 | // `TextBlock` and the .debug_info. If you are editing this logic, you |
| 1077 | 1068 | // probably need to edit that logic too. |
| 1078 | const src_fn = switch (self.bin_file.tag) { | |
| 1079 | .elf => &decl.fn_link.elf, | |
| 1080 | .macho => &decl.fn_link.macho, | |
| 1081 | .wasm => &decl.fn_link.wasm.src_fn, | |
| 1082 | else => unreachable, // TODO | |
| 1083 | }; | |
| 1069 | const src_fn_index = self.src_fn_decls.get(decl_index).?; | |
| 1070 | const src_fn = self.getAtomPtr(.src_fn, src_fn_index); | |
| 1084 | 1071 | src_fn.len = @intCast(u32, dbg_line_buffer.items.len); |
| 1085 | 1072 | |
| 1086 | if (self.dbg_line_fn_last) |last| blk: { | |
| 1087 | if (src_fn == last) break :blk; | |
| 1088 | if (src_fn.next) |next| { | |
| 1073 | if (self.src_fn_last_index) |last_index| blk: { | |
| 1074 | if (src_fn_index == last_index) break :blk; | |
| 1075 | if (src_fn.next_index) |next_index| { | |
| 1076 | const next = self.getAtomPtr(.src_fn, next_index); | |
| 1089 | 1077 | // Update existing function - non-last item. |
| 1090 | 1078 | if (src_fn.off + src_fn.len + min_nop_size > next.off) { |
| 1091 | 1079 | // It grew too big, so we move it to a new location. |
| 1092 | if (src_fn.prev) |prev| { | |
| 1093 | self.dbg_line_fn_free_list.put(gpa, prev, {}) catch {}; | |
| 1094 | prev.next = src_fn.next; | |
| 1080 | if (src_fn.prev_index) |prev_index| { | |
| 1081 | self.src_fn_free_list.put(gpa, prev_index, {}) catch {}; | |
| 1082 | self.getAtomPtr(.src_fn, prev_index).next_index = src_fn.next_index; | |
| 1095 | 1083 | } |
| 1096 | next.prev = src_fn.prev; | |
| 1097 | src_fn.next = null; | |
| 1084 | next.prev_index = src_fn.prev_index; | |
| 1085 | src_fn.next_index = null; | |
| 1098 | 1086 | // Populate where it used to be with NOPs. |
| 1099 | 1087 | switch (self.bin_file.tag) { |
| 1100 | 1088 | .elf => { |
| 1101 | 1089 | const elf_file = self.bin_file.cast(File.Elf).?; |
| 1102 | const debug_line_sect = &elf_file.sections.items[elf_file.debug_line_section_index.?]; | |
| 1090 | const debug_line_sect = &elf_file.sections.items(.shdr)[elf_file.debug_line_section_index.?]; | |
| 1103 | 1091 | const file_pos = debug_line_sect.sh_offset + src_fn.off; |
| 1104 | 1092 | try pwriteDbgLineNops(elf_file.base.file.?, file_pos, 0, &[0]u8{}, src_fn.len); |
| 1105 | 1093 | }, |
| ... | ... | @@ -1111,39 +1099,48 @@ pub fn commitDeclState( |
| 1111 | 1099 | }, |
| 1112 | 1100 | .wasm => { |
| 1113 | 1101 | const wasm_file = self.bin_file.cast(File.Wasm).?; |
| 1114 | const debug_line = wasm_file.debug_line_atom.?.code; | |
| 1102 | const debug_line = wasm_file.getAtomPtr(wasm_file.debug_line_atom.?).code; | |
| 1115 | 1103 | writeDbgLineNopsBuffered(debug_line.items, src_fn.off, 0, &.{}, src_fn.len); |
| 1116 | 1104 | }, |
| 1117 | 1105 | else => unreachable, |
| 1118 | 1106 | } |
| 1119 | 1107 | // TODO Look at the free list before appending at the end. |
| 1120 | src_fn.prev = last; | |
| 1121 | last.next = src_fn; | |
| 1122 | self.dbg_line_fn_last = src_fn; | |
| 1108 | src_fn.prev_index = last_index; | |
| 1109 | const last = self.getAtomPtr(.src_fn, last_index); | |
| 1110 | last.next_index = src_fn_index; | |
| 1111 | self.src_fn_last_index = src_fn_index; | |
| 1123 | 1112 | |
| 1124 | 1113 | src_fn.off = last.off + padToIdeal(last.len); |
| 1125 | 1114 | } |
| 1126 | } else if (src_fn.prev == null) { | |
| 1115 | } else if (src_fn.prev_index == null) { | |
| 1127 | 1116 | // Append new function. |
| 1128 | 1117 | // TODO Look at the free list before appending at the end. |
| 1129 | src_fn.prev = last; | |
| 1130 | last.next = src_fn; | |
| 1131 | self.dbg_line_fn_last = src_fn; | |
| 1118 | src_fn.prev_index = last_index; | |
| 1119 | const last = self.getAtomPtr(.src_fn, last_index); | |
| 1120 | last.next_index = src_fn_index; | |
| 1121 | self.src_fn_last_index = src_fn_index; | |
| 1132 | 1122 | |
| 1133 | 1123 | src_fn.off = last.off + padToIdeal(last.len); |
| 1134 | 1124 | } |
| 1135 | 1125 | } else { |
| 1136 | 1126 | // This is the first function of the Line Number Program. |
| 1137 | self.dbg_line_fn_first = src_fn; | |
| 1138 | self.dbg_line_fn_last = src_fn; | |
| 1127 | self.src_fn_first_index = src_fn_index; | |
| 1128 | self.src_fn_last_index = src_fn_index; | |
| 1139 | 1129 | |
| 1140 | 1130 | src_fn.off = padToIdeal(self.dbgLineNeededHeaderBytes(&[0][]u8{}, &[0][]u8{})); |
| 1141 | 1131 | } |
| 1142 | 1132 | |
| 1143 | const last_src_fn = self.dbg_line_fn_last.?; | |
| 1133 | const last_src_fn_index = self.src_fn_last_index.?; | |
| 1134 | const last_src_fn = self.getAtom(.src_fn, last_src_fn_index); | |
| 1144 | 1135 | const needed_size = last_src_fn.off + last_src_fn.len; |
| 1145 | const prev_padding_size: u32 = if (src_fn.prev) |prev| src_fn.off - (prev.off + prev.len) else 0; | |
| 1146 | const next_padding_size: u32 = if (src_fn.next) |next| next.off - (src_fn.off + src_fn.len) else 0; | |
| 1136 | const prev_padding_size: u32 = if (src_fn.prev_index) |prev_index| blk: { | |
| 1137 | const prev = self.getAtom(.src_fn, prev_index); | |
| 1138 | break :blk src_fn.off - (prev.off + prev.len); | |
| 1139 | } else 0; | |
| 1140 | const next_padding_size: u32 = if (src_fn.next_index) |next_index| blk: { | |
| 1141 | const next = self.getAtom(.src_fn, next_index); | |
| 1142 | break :blk next.off - (src_fn.off + src_fn.len); | |
| 1143 | } else 0; | |
| 1147 | 1144 | |
| 1148 | 1145 | // We only have support for one compilation unit so far, so the offsets are directly |
| 1149 | 1146 | // from the .debug_line section. |
| ... | ... | @@ -1152,7 +1149,7 @@ pub fn commitDeclState( |
| 1152 | 1149 | const elf_file = self.bin_file.cast(File.Elf).?; |
| 1153 | 1150 | const shdr_index = elf_file.debug_line_section_index.?; |
| 1154 | 1151 | try elf_file.growNonAllocSection(shdr_index, needed_size, 1, true); |
| 1155 | const debug_line_sect = elf_file.sections.items[shdr_index]; | |
| 1152 | const debug_line_sect = elf_file.sections.items(.shdr)[shdr_index]; | |
| 1156 | 1153 | const file_pos = debug_line_sect.sh_offset + src_fn.off; |
| 1157 | 1154 | try pwriteDbgLineNops( |
| 1158 | 1155 | elf_file.base.file.?, |
| ... | ... | @@ -1180,7 +1177,7 @@ pub fn commitDeclState( |
| 1180 | 1177 | |
| 1181 | 1178 | .wasm => { |
| 1182 | 1179 | const wasm_file = self.bin_file.cast(File.Wasm).?; |
| 1183 | const atom = wasm_file.debug_line_atom.?; | |
| 1180 | const atom = wasm_file.getAtomPtr(wasm_file.debug_line_atom.?); | |
| 1184 | 1181 | const debug_line = &atom.code; |
| 1185 | 1182 | const segment_size = debug_line.items.len; |
| 1186 | 1183 | if (needed_size != segment_size) { |
| ... | ... | @@ -1212,7 +1209,7 @@ pub fn commitDeclState( |
| 1212 | 1209 | if (dbg_info_buffer.items.len == 0) |
| 1213 | 1210 | return; |
| 1214 | 1211 | |
| 1215 | const atom = getDbgInfoAtom(self.bin_file.tag, module, decl_index); | |
| 1212 | const di_atom_index = self.di_atom_decls.get(decl_index).?; | |
| 1216 | 1213 | if (decl_state.abbrev_table.items.len > 0) { |
| 1217 | 1214 | // Now we emit the .debug_info types of the Decl. These will count towards the size of |
| 1218 | 1215 | // the buffer, so we have to do it before computing the offset, and we can't perform the actual |
| ... | ... | @@ -1234,12 +1231,12 @@ pub fn commitDeclState( |
| 1234 | 1231 | if (deferred) continue; |
| 1235 | 1232 | |
| 1236 | 1233 | symbol.offset = @intCast(u32, dbg_info_buffer.items.len); |
| 1237 | try decl_state.addDbgInfoType(module, atom, ty); | |
| 1234 | try decl_state.addDbgInfoType(module, di_atom_index, ty); | |
| 1238 | 1235 | } |
| 1239 | 1236 | } |
| 1240 | 1237 | |
| 1241 | 1238 | log.debug("updateDeclDebugInfoAllocation for '{s}'", .{decl.name}); |
| 1242 | try self.updateDeclDebugInfoAllocation(atom, @intCast(u32, dbg_info_buffer.items.len)); | |
| 1239 | try self.updateDeclDebugInfoAllocation(di_atom_index, @intCast(u32, dbg_info_buffer.items.len)); | |
| 1243 | 1240 | |
| 1244 | 1241 | while (decl_state.abbrev_relocs.popOrNull()) |reloc| { |
| 1245 | 1242 | if (reloc.target) |target| { |
| ... | ... | @@ -1260,11 +1257,12 @@ pub fn commitDeclState( |
| 1260 | 1257 | try self.global_abbrev_relocs.append(gpa, .{ |
| 1261 | 1258 | .target = null, |
| 1262 | 1259 | .offset = reloc.offset, |
| 1263 | .atom = reloc.atom, | |
| 1260 | .atom_index = reloc.atom_index, | |
| 1264 | 1261 | .addend = reloc.addend, |
| 1265 | 1262 | }); |
| 1266 | 1263 | } else { |
| 1267 | const value = symbol.atom.off + symbol.offset + reloc.addend; | |
| 1264 | const atom = self.getAtom(.di_atom, symbol.atom_index); | |
| 1265 | const value = atom.off + symbol.offset + reloc.addend; | |
| 1268 | 1266 | log.debug("{x}: [() => {x}] (%{d}, '{}')", .{ reloc.offset, value, target, ty.fmtDebug() }); |
| 1269 | 1267 | mem.writeInt( |
| 1270 | 1268 | u32, |
| ... | ... | @@ -1274,10 +1272,11 @@ pub fn commitDeclState( |
| 1274 | 1272 | ); |
| 1275 | 1273 | } |
| 1276 | 1274 | } else { |
| 1275 | const atom = self.getAtom(.di_atom, reloc.atom_index); | |
| 1277 | 1276 | mem.writeInt( |
| 1278 | 1277 | u32, |
| 1279 | 1278 | dbg_info_buffer.items[reloc.offset..][0..@sizeOf(u32)], |
| 1280 | reloc.atom.off + reloc.offset + reloc.addend, | |
| 1279 | atom.off + reloc.offset + reloc.addend, | |
| 1281 | 1280 | target_endian, |
| 1282 | 1281 | ); |
| 1283 | 1282 | } |
| ... | ... | @@ -1293,7 +1292,7 @@ pub fn commitDeclState( |
| 1293 | 1292 | .got_load => .got_load, |
| 1294 | 1293 | }, |
| 1295 | 1294 | .target = reloc.target, |
| 1296 | .offset = reloc.offset + atom.off, | |
| 1295 | .offset = reloc.offset + self.getAtom(.di_atom, di_atom_index).off, | |
| 1297 | 1296 | .addend = 0, |
| 1298 | 1297 | .prev_vaddr = 0, |
| 1299 | 1298 | }); |
| ... | ... | @@ -1303,10 +1302,10 @@ pub fn commitDeclState( |
| 1303 | 1302 | } |
| 1304 | 1303 | |
| 1305 | 1304 | log.debug("writeDeclDebugInfo for '{s}", .{decl.name}); |
| 1306 | try self.writeDeclDebugInfo(atom, dbg_info_buffer.items); | |
| 1305 | try self.writeDeclDebugInfo(di_atom_index, dbg_info_buffer.items); | |
| 1307 | 1306 | } |
| 1308 | 1307 | |
| 1309 | fn updateDeclDebugInfoAllocation(self: *Dwarf, atom: *Atom, len: u32) !void { | |
| 1308 | fn updateDeclDebugInfoAllocation(self: *Dwarf, atom_index: Atom.Index, len: u32) !void { | |
| 1310 | 1309 | const tracy = trace(@src()); |
| 1311 | 1310 | defer tracy.end(); |
| 1312 | 1311 | |
| ... | ... | @@ -1315,24 +1314,26 @@ fn updateDeclDebugInfoAllocation(self: *Dwarf, atom: *Atom, len: u32) !void { |
| 1315 | 1314 | // probably need to edit that logic too. |
| 1316 | 1315 | const gpa = self.allocator; |
| 1317 | 1316 | |
| 1317 | const atom = self.getAtomPtr(.di_atom, atom_index); | |
| 1318 | 1318 | atom.len = len; |
| 1319 | if (self.atom_last) |last| blk: { | |
| 1320 | if (atom == last) break :blk; | |
| 1321 | if (atom.next) |next| { | |
| 1319 | if (self.di_atom_last_index) |last_index| blk: { | |
| 1320 | if (atom_index == last_index) break :blk; | |
| 1321 | if (atom.next_index) |next_index| { | |
| 1322 | const next = self.getAtomPtr(.di_atom, next_index); | |
| 1322 | 1323 | // Update existing Decl - non-last item. |
| 1323 | 1324 | if (atom.off + atom.len + min_nop_size > next.off) { |
| 1324 | 1325 | // It grew too big, so we move it to a new location. |
| 1325 | if (atom.prev) |prev| { | |
| 1326 | self.atom_free_list.put(gpa, prev, {}) catch {}; | |
| 1327 | prev.next = atom.next; | |
| 1326 | if (atom.prev_index) |prev_index| { | |
| 1327 | self.di_atom_free_list.put(gpa, prev_index, {}) catch {}; | |
| 1328 | self.getAtomPtr(.di_atom, prev_index).next_index = atom.next_index; | |
| 1328 | 1329 | } |
| 1329 | next.prev = atom.prev; | |
| 1330 | atom.next = null; | |
| 1330 | next.prev_index = atom.prev_index; | |
| 1331 | atom.next_index = null; | |
| 1331 | 1332 | // Populate where it used to be with NOPs. |
| 1332 | 1333 | switch (self.bin_file.tag) { |
| 1333 | 1334 | .elf => { |
| 1334 | 1335 | const elf_file = self.bin_file.cast(File.Elf).?; |
| 1335 | const debug_info_sect = &elf_file.sections.items[elf_file.debug_info_section_index.?]; | |
| 1336 | const debug_info_sect = &elf_file.sections.items(.shdr)[elf_file.debug_info_section_index.?]; | |
| 1336 | 1337 | const file_pos = debug_info_sect.sh_offset + atom.off; |
| 1337 | 1338 | try pwriteDbgInfoNops(elf_file.base.file.?, file_pos, 0, &[0]u8{}, atom.len, false); |
| 1338 | 1339 | }, |
| ... | ... | @@ -1344,37 +1345,40 @@ fn updateDeclDebugInfoAllocation(self: *Dwarf, atom: *Atom, len: u32) !void { |
| 1344 | 1345 | }, |
| 1345 | 1346 | .wasm => { |
| 1346 | 1347 | const wasm_file = self.bin_file.cast(File.Wasm).?; |
| 1347 | const debug_info = &wasm_file.debug_info_atom.?.code; | |
| 1348 | const debug_info_index = wasm_file.debug_info_atom.?; | |
| 1349 | const debug_info = &wasm_file.getAtomPtr(debug_info_index).code; | |
| 1348 | 1350 | try writeDbgInfoNopsToArrayList(gpa, debug_info, atom.off, 0, &.{0}, atom.len, false); |
| 1349 | 1351 | }, |
| 1350 | 1352 | else => unreachable, |
| 1351 | 1353 | } |
| 1352 | 1354 | // TODO Look at the free list before appending at the end. |
| 1353 | atom.prev = last; | |
| 1354 | last.next = atom; | |
| 1355 | self.atom_last = atom; | |
| 1355 | atom.prev_index = last_index; | |
| 1356 | const last = self.getAtomPtr(.di_atom, last_index); | |
| 1357 | last.next_index = atom_index; | |
| 1358 | self.di_atom_last_index = atom_index; | |
| 1356 | 1359 | |
| 1357 | 1360 | atom.off = last.off + padToIdeal(last.len); |
| 1358 | 1361 | } |
| 1359 | } else if (atom.prev == null) { | |
| 1362 | } else if (atom.prev_index == null) { | |
| 1360 | 1363 | // Append new Decl. |
| 1361 | 1364 | // TODO Look at the free list before appending at the end. |
| 1362 | atom.prev = last; | |
| 1363 | last.next = atom; | |
| 1364 | self.atom_last = atom; | |
| 1365 | atom.prev_index = last_index; | |
| 1366 | const last = self.getAtomPtr(.di_atom, last_index); | |
| 1367 | last.next_index = atom_index; | |
| 1368 | self.di_atom_last_index = atom_index; | |
| 1365 | 1369 | |
| 1366 | 1370 | atom.off = last.off + padToIdeal(last.len); |
| 1367 | 1371 | } |
| 1368 | 1372 | } else { |
| 1369 | 1373 | // This is the first Decl of the .debug_info |
| 1370 | self.atom_first = atom; | |
| 1371 | self.atom_last = atom; | |
| 1374 | self.di_atom_first_index = atom_index; | |
| 1375 | self.di_atom_last_index = atom_index; | |
| 1372 | 1376 | |
| 1373 | 1377 | atom.off = @intCast(u32, padToIdeal(self.dbgInfoHeaderBytes())); |
| 1374 | 1378 | } |
| 1375 | 1379 | } |
| 1376 | 1380 | |
| 1377 | fn writeDeclDebugInfo(self: *Dwarf, atom: *Atom, dbg_info_buf: []const u8) !void { | |
| 1381 | fn writeDeclDebugInfo(self: *Dwarf, atom_index: Atom.Index, dbg_info_buf: []const u8) !void { | |
| 1378 | 1382 | const tracy = trace(@src()); |
| 1379 | 1383 | defer tracy.end(); |
| 1380 | 1384 | |
| ... | ... | @@ -1383,14 +1387,22 @@ fn writeDeclDebugInfo(self: *Dwarf, atom: *Atom, dbg_info_buf: []const u8) !void |
| 1383 | 1387 | // probably need to edit that logic too. |
| 1384 | 1388 | const gpa = self.allocator; |
| 1385 | 1389 | |
| 1386 | const last_decl = self.atom_last.?; | |
| 1390 | const atom = self.getAtom(.di_atom, atom_index); | |
| 1391 | const last_decl_index = self.di_atom_last_index.?; | |
| 1392 | const last_decl = self.getAtom(.di_atom, last_decl_index); | |
| 1387 | 1393 | // +1 for a trailing zero to end the children of the decl tag. |
| 1388 | 1394 | const needed_size = last_decl.off + last_decl.len + 1; |
| 1389 | const prev_padding_size: u32 = if (atom.prev) |prev| atom.off - (prev.off + prev.len) else 0; | |
| 1390 | const next_padding_size: u32 = if (atom.next) |next| next.off - (atom.off + atom.len) else 0; | |
| 1395 | const prev_padding_size: u32 = if (atom.prev_index) |prev_index| blk: { | |
| 1396 | const prev = self.getAtom(.di_atom, prev_index); | |
| 1397 | break :blk atom.off - (prev.off + prev.len); | |
| 1398 | } else 0; | |
| 1399 | const next_padding_size: u32 = if (atom.next_index) |next_index| blk: { | |
| 1400 | const next = self.getAtom(.di_atom, next_index); | |
| 1401 | break :blk next.off - (atom.off + atom.len); | |
| 1402 | } else 0; | |
| 1391 | 1403 | |
| 1392 | 1404 | // To end the children of the decl tag. |
| 1393 | const trailing_zero = atom.next == null; | |
| 1405 | const trailing_zero = atom.next_index == null; | |
| 1394 | 1406 | |
| 1395 | 1407 | // We only have support for one compilation unit so far, so the offsets are directly |
| 1396 | 1408 | // from the .debug_info section. |
| ... | ... | @@ -1399,7 +1411,7 @@ fn writeDeclDebugInfo(self: *Dwarf, atom: *Atom, dbg_info_buf: []const u8) !void |
| 1399 | 1411 | const elf_file = self.bin_file.cast(File.Elf).?; |
| 1400 | 1412 | const shdr_index = elf_file.debug_info_section_index.?; |
| 1401 | 1413 | try elf_file.growNonAllocSection(shdr_index, needed_size, 1, true); |
| 1402 | const debug_info_sect = elf_file.sections.items[shdr_index]; | |
| 1414 | const debug_info_sect = elf_file.sections.items(.shdr)[shdr_index]; | |
| 1403 | 1415 | const file_pos = debug_info_sect.sh_offset + atom.off; |
| 1404 | 1416 | try pwriteDbgInfoNops( |
| 1405 | 1417 | elf_file.base.file.?, |
| ... | ... | @@ -1430,7 +1442,7 @@ fn writeDeclDebugInfo(self: *Dwarf, atom: *Atom, dbg_info_buf: []const u8) !void |
| 1430 | 1442 | .wasm => { |
| 1431 | 1443 | const wasm_file = self.bin_file.cast(File.Wasm).?; |
| 1432 | 1444 | const info_atom = wasm_file.debug_info_atom.?; |
| 1433 | const debug_info = &info_atom.code; | |
| 1445 | const debug_info = &wasm_file.getAtomPtr(info_atom).code; | |
| 1434 | 1446 | const segment_size = debug_info.items.len; |
| 1435 | 1447 | if (needed_size != segment_size) { |
| 1436 | 1448 | log.debug(" needed size does not equal allocated size: {d}", .{needed_size}); |
| ... | ... | @@ -1458,10 +1470,15 @@ fn writeDeclDebugInfo(self: *Dwarf, atom: *Atom, dbg_info_buf: []const u8) !void |
| 1458 | 1470 | } |
| 1459 | 1471 | } |
| 1460 | 1472 | |
| 1461 | pub fn updateDeclLineNumber(self: *Dwarf, decl: *const Module.Decl) !void { | |
| 1473 | pub fn updateDeclLineNumber(self: *Dwarf, module: *Module, decl_index: Module.Decl.Index) !void { | |
| 1462 | 1474 | const tracy = trace(@src()); |
| 1463 | 1475 | defer tracy.end(); |
| 1464 | 1476 | |
| 1477 | const atom_index = try self.getOrCreateAtomForDecl(.src_fn, decl_index); | |
| 1478 | const atom = self.getAtom(.src_fn, atom_index); | |
| 1479 | if (atom.len == 0) return; | |
| 1480 | ||
| 1481 | const decl = module.declPtr(decl_index); | |
| 1465 | 1482 | const func = decl.val.castTag(.function).?.data; |
| 1466 | 1483 | log.debug("decl.src_line={d}, func.lbrace_line={d}, func.rbrace_line={d}", .{ |
| 1467 | 1484 | decl.src_line, |
| ... | ... | @@ -1475,79 +1492,81 @@ pub fn updateDeclLineNumber(self: *Dwarf, decl: *const Module.Decl) !void { |
| 1475 | 1492 | switch (self.bin_file.tag) { |
| 1476 | 1493 | .elf => { |
| 1477 | 1494 | const elf_file = self.bin_file.cast(File.Elf).?; |
| 1478 | const shdr = elf_file.sections.items[elf_file.debug_line_section_index.?]; | |
| 1479 | const file_pos = shdr.sh_offset + decl.fn_link.elf.off + self.getRelocDbgLineOff(); | |
| 1495 | const shdr = elf_file.sections.items(.shdr)[elf_file.debug_line_section_index.?]; | |
| 1496 | const file_pos = shdr.sh_offset + atom.off + self.getRelocDbgLineOff(); | |
| 1480 | 1497 | try elf_file.base.file.?.pwriteAll(&data, file_pos); |
| 1481 | 1498 | }, |
| 1482 | 1499 | .macho => { |
| 1483 | 1500 | const d_sym = self.bin_file.cast(File.MachO).?.getDebugSymbols().?; |
| 1484 | 1501 | const sect = d_sym.getSection(d_sym.debug_line_section_index.?); |
| 1485 | const file_pos = sect.offset + decl.fn_link.macho.off + self.getRelocDbgLineOff(); | |
| 1502 | const file_pos = sect.offset + atom.off + self.getRelocDbgLineOff(); | |
| 1486 | 1503 | try d_sym.file.pwriteAll(&data, file_pos); |
| 1487 | 1504 | }, |
| 1488 | 1505 | .wasm => { |
| 1489 | 1506 | const wasm_file = self.bin_file.cast(File.Wasm).?; |
| 1490 | const offset = decl.fn_link.wasm.src_fn.off + self.getRelocDbgLineOff(); | |
| 1491 | const atom = wasm_file.debug_line_atom.?; | |
| 1492 | mem.copy(u8, atom.code.items[offset..], &data); | |
| 1507 | const offset = atom.off + self.getRelocDbgLineOff(); | |
| 1508 | const line_atom_index = wasm_file.debug_line_atom.?; | |
| 1509 | mem.copy(u8, wasm_file.getAtomPtr(line_atom_index).code.items[offset..], &data); | |
| 1493 | 1510 | }, |
| 1494 | 1511 | else => unreachable, |
| 1495 | 1512 | } |
| 1496 | 1513 | } |
| 1497 | 1514 | |
| 1498 | pub fn freeAtom(self: *Dwarf, atom: *Atom) void { | |
| 1499 | if (self.atom_first == atom) { | |
| 1500 | self.atom_first = atom.next; | |
| 1501 | } | |
| 1502 | if (self.atom_last == atom) { | |
| 1503 | // TODO shrink the .debug_info section size here | |
| 1504 | self.atom_last = atom.prev; | |
| 1505 | } | |
| 1506 | ||
| 1507 | if (atom.prev) |prev| { | |
| 1508 | prev.next = atom.next; | |
| 1515 | pub fn freeDecl(self: *Dwarf, decl_index: Module.Decl.Index) void { | |
| 1516 | const gpa = self.allocator; | |
| 1509 | 1517 | |
| 1510 | // TODO the free list logic like we do for text blocks above | |
| 1511 | } else { | |
| 1512 | atom.prev = null; | |
| 1518 | // Free SrcFn atom | |
| 1519 | if (self.src_fn_decls.fetchRemove(decl_index)) |kv| { | |
| 1520 | const src_fn_index = kv.value; | |
| 1521 | const src_fn = self.getAtom(.src_fn, src_fn_index); | |
| 1522 | _ = self.src_fn_free_list.remove(src_fn_index); | |
| 1523 | ||
| 1524 | if (src_fn.prev_index) |prev_index| { | |
| 1525 | self.src_fn_free_list.put(gpa, prev_index, {}) catch {}; | |
| 1526 | const prev = self.getAtomPtr(.src_fn, prev_index); | |
| 1527 | prev.next_index = src_fn.next_index; | |
| 1528 | if (src_fn.next_index) |next_index| { | |
| 1529 | self.getAtomPtr(.src_fn, next_index).prev_index = prev_index; | |
| 1530 | } else { | |
| 1531 | self.src_fn_last_index = prev_index; | |
| 1532 | } | |
| 1533 | } else if (src_fn.next_index) |next_index| { | |
| 1534 | self.src_fn_first_index = next_index; | |
| 1535 | self.getAtomPtr(.src_fn, next_index).prev_index = null; | |
| 1536 | } | |
| 1537 | if (self.src_fn_first_index == src_fn_index) { | |
| 1538 | self.src_fn_first_index = src_fn.next_index; | |
| 1539 | } | |
| 1540 | if (self.src_fn_last_index == src_fn_index) { | |
| 1541 | self.src_fn_last_index = src_fn.prev_index; | |
| 1542 | } | |
| 1513 | 1543 | } |
| 1514 | 1544 | |
| 1515 | if (atom.next) |next| { | |
| 1516 | next.prev = atom.prev; | |
| 1517 | } else { | |
| 1518 | atom.next = null; | |
| 1519 | } | |
| 1520 | } | |
| 1545 | // Free DI atom | |
| 1546 | if (self.di_atom_decls.fetchRemove(decl_index)) |kv| { | |
| 1547 | const di_atom_index = kv.value; | |
| 1548 | const di_atom = self.getAtomPtr(.di_atom, di_atom_index); | |
| 1521 | 1549 | |
| 1522 | pub fn freeDecl(self: *Dwarf, decl: *Module.Decl) void { | |
| 1523 | // TODO make this logic match freeTextBlock. Maybe abstract the logic out since the same thing | |
| 1524 | // is desired for both. | |
| 1525 | const gpa = self.allocator; | |
| 1526 | const fn_link = switch (self.bin_file.tag) { | |
| 1527 | .elf => &decl.fn_link.elf, | |
| 1528 | .macho => &decl.fn_link.macho, | |
| 1529 | .wasm => &decl.fn_link.wasm.src_fn, | |
| 1530 | else => unreachable, | |
| 1531 | }; | |
| 1532 | _ = self.dbg_line_fn_free_list.remove(fn_link); | |
| 1550 | if (self.di_atom_first_index == di_atom_index) { | |
| 1551 | self.di_atom_first_index = di_atom.next_index; | |
| 1552 | } | |
| 1553 | if (self.di_atom_last_index == di_atom_index) { | |
| 1554 | // TODO shrink the .debug_info section size here | |
| 1555 | self.di_atom_last_index = di_atom.prev_index; | |
| 1556 | } | |
| 1533 | 1557 | |
| 1534 | if (fn_link.prev) |prev| { | |
| 1535 | self.dbg_line_fn_free_list.put(gpa, prev, {}) catch {}; | |
| 1536 | prev.next = fn_link.next; | |
| 1537 | if (fn_link.next) |next| { | |
| 1538 | next.prev = prev; | |
| 1558 | if (di_atom.prev_index) |prev_index| { | |
| 1559 | self.getAtomPtr(.di_atom, prev_index).next_index = di_atom.next_index; | |
| 1560 | // TODO the free list logic like we do for SrcFn above | |
| 1539 | 1561 | } else { |
| 1540 | self.dbg_line_fn_last = prev; | |
| 1562 | di_atom.prev_index = null; | |
| 1563 | } | |
| 1564 | ||
| 1565 | if (di_atom.next_index) |next_index| { | |
| 1566 | self.getAtomPtr(.di_atom, next_index).prev_index = di_atom.prev_index; | |
| 1567 | } else { | |
| 1568 | di_atom.next_index = null; | |
| 1541 | 1569 | } |
| 1542 | } else if (fn_link.next) |next| { | |
| 1543 | self.dbg_line_fn_first = next; | |
| 1544 | next.prev = null; | |
| 1545 | } | |
| 1546 | if (self.dbg_line_fn_first == fn_link) { | |
| 1547 | self.dbg_line_fn_first = fn_link.next; | |
| 1548 | } | |
| 1549 | if (self.dbg_line_fn_last == fn_link) { | |
| 1550 | self.dbg_line_fn_last = fn_link.prev; | |
| 1551 | 1570 | } |
| 1552 | 1571 | } |
| 1553 | 1572 | |
| ... | ... | @@ -1690,7 +1709,7 @@ pub fn writeDbgAbbrev(self: *Dwarf) !void { |
| 1690 | 1709 | const elf_file = self.bin_file.cast(File.Elf).?; |
| 1691 | 1710 | const shdr_index = elf_file.debug_abbrev_section_index.?; |
| 1692 | 1711 | try elf_file.growNonAllocSection(shdr_index, needed_size, 1, false); |
| 1693 | const debug_abbrev_sect = elf_file.sections.items[shdr_index]; | |
| 1712 | const debug_abbrev_sect = elf_file.sections.items(.shdr)[shdr_index]; | |
| 1694 | 1713 | const file_pos = debug_abbrev_sect.sh_offset + abbrev_offset; |
| 1695 | 1714 | try elf_file.base.file.?.pwriteAll(&abbrev_buf, file_pos); |
| 1696 | 1715 | }, |
| ... | ... | @@ -1704,7 +1723,7 @@ pub fn writeDbgAbbrev(self: *Dwarf) !void { |
| 1704 | 1723 | }, |
| 1705 | 1724 | .wasm => { |
| 1706 | 1725 | const wasm_file = self.bin_file.cast(File.Wasm).?; |
| 1707 | const debug_abbrev = &wasm_file.debug_abbrev_atom.?.code; | |
| 1726 | const debug_abbrev = &wasm_file.getAtomPtr(wasm_file.debug_abbrev_atom.?).code; | |
| 1708 | 1727 | try debug_abbrev.resize(wasm_file.base.allocator, needed_size); |
| 1709 | 1728 | mem.copy(u8, debug_abbrev.items, &abbrev_buf); |
| 1710 | 1729 | }, |
| ... | ... | @@ -1770,11 +1789,11 @@ pub fn writeDbgInfoHeader(self: *Dwarf, module: *Module, low_pc: u64, high_pc: u |
| 1770 | 1789 | }, |
| 1771 | 1790 | } |
| 1772 | 1791 | // Write the form for the compile unit, which must match the abbrev table above. |
| 1773 | const name_strp = try self.makeString(module.root_pkg.root_src_path); | |
| 1792 | const name_strp = try self.strtab.insert(self.allocator, module.root_pkg.root_src_path); | |
| 1774 | 1793 | var compile_unit_dir_buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined; |
| 1775 | 1794 | const compile_unit_dir = resolveCompilationDir(module, &compile_unit_dir_buffer); |
| 1776 | const comp_dir_strp = try self.makeString(compile_unit_dir); | |
| 1777 | const producer_strp = try self.makeString(link.producer_string); | |
| 1795 | const comp_dir_strp = try self.strtab.insert(self.allocator, compile_unit_dir); | |
| 1796 | const producer_strp = try self.strtab.insert(self.allocator, link.producer_string); | |
| 1778 | 1797 | |
| 1779 | 1798 | di_buf.appendAssumeCapacity(@enumToInt(AbbrevKind.compile_unit)); |
| 1780 | 1799 | if (self.bin_file.tag == .macho) { |
| ... | ... | @@ -1805,7 +1824,7 @@ pub fn writeDbgInfoHeader(self: *Dwarf, module: *Module, low_pc: u64, high_pc: u |
| 1805 | 1824 | switch (self.bin_file.tag) { |
| 1806 | 1825 | .elf => { |
| 1807 | 1826 | const elf_file = self.bin_file.cast(File.Elf).?; |
| 1808 | const debug_info_sect = elf_file.sections.items[elf_file.debug_info_section_index.?]; | |
| 1827 | const debug_info_sect = elf_file.sections.items(.shdr)[elf_file.debug_info_section_index.?]; | |
| 1809 | 1828 | const file_pos = debug_info_sect.sh_offset; |
| 1810 | 1829 | try pwriteDbgInfoNops(elf_file.base.file.?, file_pos, 0, di_buf.items, jmp_amt, false); |
| 1811 | 1830 | }, |
| ... | ... | @@ -1817,7 +1836,7 @@ pub fn writeDbgInfoHeader(self: *Dwarf, module: *Module, low_pc: u64, high_pc: u |
| 1817 | 1836 | }, |
| 1818 | 1837 | .wasm => { |
| 1819 | 1838 | const wasm_file = self.bin_file.cast(File.Wasm).?; |
| 1820 | const debug_info = &wasm_file.debug_info_atom.?.code; | |
| 1839 | const debug_info = &wasm_file.getAtomPtr(wasm_file.debug_info_atom.?).code; | |
| 1821 | 1840 | try writeDbgInfoNopsToArrayList(self.allocator, debug_info, 0, 0, di_buf.items, jmp_amt, false); |
| 1822 | 1841 | }, |
| 1823 | 1842 | else => unreachable, |
| ... | ... | @@ -2124,7 +2143,7 @@ pub fn writeDbgAranges(self: *Dwarf, addr: u64, size: u64) !void { |
| 2124 | 2143 | const elf_file = self.bin_file.cast(File.Elf).?; |
| 2125 | 2144 | const shdr_index = elf_file.debug_aranges_section_index.?; |
| 2126 | 2145 | try elf_file.growNonAllocSection(shdr_index, needed_size, 16, false); |
| 2127 | const debug_aranges_sect = elf_file.sections.items[shdr_index]; | |
| 2146 | const debug_aranges_sect = elf_file.sections.items(.shdr)[shdr_index]; | |
| 2128 | 2147 | const file_pos = debug_aranges_sect.sh_offset; |
| 2129 | 2148 | try elf_file.base.file.?.pwriteAll(di_buf.items, file_pos); |
| 2130 | 2149 | }, |
| ... | ... | @@ -2138,7 +2157,7 @@ pub fn writeDbgAranges(self: *Dwarf, addr: u64, size: u64) !void { |
| 2138 | 2157 | }, |
| 2139 | 2158 | .wasm => { |
| 2140 | 2159 | const wasm_file = self.bin_file.cast(File.Wasm).?; |
| 2141 | const debug_ranges = &wasm_file.debug_ranges_atom.?.code; | |
| 2160 | const debug_ranges = &wasm_file.getAtomPtr(wasm_file.debug_ranges_atom.?).code; | |
| 2142 | 2161 | try debug_ranges.resize(wasm_file.base.allocator, needed_size); |
| 2143 | 2162 | mem.copy(u8, debug_ranges.items, di_buf.items); |
| 2144 | 2163 | }, |
| ... | ... | @@ -2275,19 +2294,23 @@ pub fn writeDbgLineHeader(self: *Dwarf) !void { |
| 2275 | 2294 | const needed_with_padding = padToIdeal(needed_bytes); |
| 2276 | 2295 | const delta = needed_with_padding - dbg_line_prg_off; |
| 2277 | 2296 | |
| 2278 | var src_fn = self.dbg_line_fn_first.?; | |
| 2279 | const last_fn = self.dbg_line_fn_last.?; | |
| 2297 | const first_fn_index = self.src_fn_first_index.?; | |
| 2298 | const first_fn = self.getAtom(.src_fn, first_fn_index); | |
| 2299 | const last_fn_index = self.src_fn_last_index.?; | |
| 2300 | const last_fn = self.getAtom(.src_fn, last_fn_index); | |
| 2301 | ||
| 2302 | var src_fn_index = first_fn_index; | |
| 2280 | 2303 | |
| 2281 | var buffer = try gpa.alloc(u8, last_fn.off + last_fn.len - src_fn.off); | |
| 2304 | var buffer = try gpa.alloc(u8, last_fn.off + last_fn.len - first_fn.off); | |
| 2282 | 2305 | defer gpa.free(buffer); |
| 2283 | 2306 | |
| 2284 | 2307 | switch (self.bin_file.tag) { |
| 2285 | 2308 | .elf => { |
| 2286 | 2309 | const elf_file = self.bin_file.cast(File.Elf).?; |
| 2287 | 2310 | const shdr_index = elf_file.debug_line_section_index.?; |
| 2288 | const needed_size = elf_file.sections.items[shdr_index].sh_size + delta; | |
| 2311 | const needed_size = elf_file.sections.items(.shdr)[shdr_index].sh_size + delta; | |
| 2289 | 2312 | try elf_file.growNonAllocSection(shdr_index, needed_size, 1, true); |
| 2290 | const file_pos = elf_file.sections.items[shdr_index].sh_offset + src_fn.off; | |
| 2313 | const file_pos = elf_file.sections.items(.shdr)[shdr_index].sh_offset + first_fn.off; | |
| 2291 | 2314 | |
| 2292 | 2315 | const amt = try elf_file.base.file.?.preadAll(buffer, file_pos); |
| 2293 | 2316 | if (amt != buffer.len) return error.InputOutput; |
| ... | ... | @@ -2299,7 +2322,7 @@ pub fn writeDbgLineHeader(self: *Dwarf) !void { |
| 2299 | 2322 | const sect_index = d_sym.debug_line_section_index.?; |
| 2300 | 2323 | const needed_size = @intCast(u32, d_sym.getSection(sect_index).size + delta); |
| 2301 | 2324 | try d_sym.growSection(sect_index, needed_size, true); |
| 2302 | const file_pos = d_sym.getSection(sect_index).offset + src_fn.off; | |
| 2325 | const file_pos = d_sym.getSection(sect_index).offset + first_fn.off; | |
| 2303 | 2326 | |
| 2304 | 2327 | const amt = try d_sym.file.preadAll(buffer, file_pos); |
| 2305 | 2328 | if (amt != buffer.len) return error.InputOutput; |
| ... | ... | @@ -2308,19 +2331,20 @@ pub fn writeDbgLineHeader(self: *Dwarf) !void { |
| 2308 | 2331 | }, |
| 2309 | 2332 | .wasm => { |
| 2310 | 2333 | const wasm_file = self.bin_file.cast(File.Wasm).?; |
| 2311 | const debug_line = &wasm_file.debug_line_atom.?.code; | |
| 2312 | mem.copy(u8, buffer, debug_line.items[src_fn.off..]); | |
| 2334 | const debug_line = &wasm_file.getAtomPtr(wasm_file.debug_line_atom.?).code; | |
| 2335 | mem.copy(u8, buffer, debug_line.items[first_fn.off..]); | |
| 2313 | 2336 | try debug_line.resize(self.allocator, debug_line.items.len + delta); |
| 2314 | mem.copy(u8, debug_line.items[src_fn.off + delta ..], buffer); | |
| 2337 | mem.copy(u8, debug_line.items[first_fn.off + delta ..], buffer); | |
| 2315 | 2338 | }, |
| 2316 | 2339 | else => unreachable, |
| 2317 | 2340 | } |
| 2318 | 2341 | |
| 2319 | 2342 | while (true) { |
| 2343 | const src_fn = self.getAtomPtr(.src_fn, src_fn_index); | |
| 2320 | 2344 | src_fn.off += delta; |
| 2321 | 2345 | |
| 2322 | if (src_fn.next) |next| { | |
| 2323 | src_fn = next; | |
| 2346 | if (src_fn.next_index) |next_index| { | |
| 2347 | src_fn_index = next_index; | |
| 2324 | 2348 | } else break; |
| 2325 | 2349 | } |
| 2326 | 2350 | } |
| ... | ... | @@ -2346,7 +2370,7 @@ pub fn writeDbgLineHeader(self: *Dwarf) !void { |
| 2346 | 2370 | switch (self.bin_file.tag) { |
| 2347 | 2371 | .elf => { |
| 2348 | 2372 | const elf_file = self.bin_file.cast(File.Elf).?; |
| 2349 | const debug_line_sect = elf_file.sections.items[elf_file.debug_line_section_index.?]; | |
| 2373 | const debug_line_sect = elf_file.sections.items(.shdr)[elf_file.debug_line_section_index.?]; | |
| 2350 | 2374 | const file_pos = debug_line_sect.sh_offset; |
| 2351 | 2375 | try pwriteDbgLineNops(elf_file.base.file.?, file_pos, 0, di_buf.items, jmp_amt); |
| 2352 | 2376 | }, |
| ... | ... | @@ -2358,7 +2382,7 @@ pub fn writeDbgLineHeader(self: *Dwarf) !void { |
| 2358 | 2382 | }, |
| 2359 | 2383 | .wasm => { |
| 2360 | 2384 | const wasm_file = self.bin_file.cast(File.Wasm).?; |
| 2361 | const debug_line = wasm_file.debug_line_atom.?.code; | |
| 2385 | const debug_line = &wasm_file.getAtomPtr(wasm_file.debug_line_atom.?).code; | |
| 2362 | 2386 | writeDbgLineNopsBuffered(debug_line.items, 0, 0, di_buf.items, jmp_amt); |
| 2363 | 2387 | }, |
| 2364 | 2388 | else => unreachable, |
| ... | ... | @@ -2366,22 +2390,26 @@ pub fn writeDbgLineHeader(self: *Dwarf) !void { |
| 2366 | 2390 | } |
| 2367 | 2391 | |
| 2368 | 2392 | fn getDebugInfoOff(self: Dwarf) ?u32 { |
| 2369 | const first = self.atom_first orelse return null; | |
| 2393 | const first_index = self.di_atom_first_index orelse return null; | |
| 2394 | const first = self.getAtom(.di_atom, first_index); | |
| 2370 | 2395 | return first.off; |
| 2371 | 2396 | } |
| 2372 | 2397 | |
| 2373 | 2398 | fn getDebugInfoEnd(self: Dwarf) ?u32 { |
| 2374 | const last = self.atom_last orelse return null; | |
| 2399 | const last_index = self.di_atom_last_index orelse return null; | |
| 2400 | const last = self.getAtom(.di_atom, last_index); | |
| 2375 | 2401 | return last.off + last.len; |
| 2376 | 2402 | } |
| 2377 | 2403 | |
| 2378 | 2404 | fn getDebugLineProgramOff(self: Dwarf) ?u32 { |
| 2379 | const first = self.dbg_line_fn_first orelse return null; | |
| 2405 | const first_index = self.src_fn_first_index orelse return null; | |
| 2406 | const first = self.getAtom(.src_fn, first_index); | |
| 2380 | 2407 | return first.off; |
| 2381 | 2408 | } |
| 2382 | 2409 | |
| 2383 | 2410 | fn getDebugLineProgramEnd(self: Dwarf) ?u32 { |
| 2384 | const last = self.dbg_line_fn_last orelse return null; | |
| 2411 | const last_index = self.src_fn_last_index orelse return null; | |
| 2412 | const last = self.getAtom(.src_fn, last_index); | |
| 2385 | 2413 | return last.off + last.len; |
| 2386 | 2414 | } |
| 2387 | 2415 | |
| ... | ... | @@ -2435,15 +2463,6 @@ fn getRelocDbgInfoSubprogramHighPC(self: Dwarf) u32 { |
| 2435 | 2463 | return dbg_info_low_pc_reloc_index + self.ptrWidthBytes(); |
| 2436 | 2464 | } |
| 2437 | 2465 | |
| 2438 | /// TODO Improve this to use a table. | |
| 2439 | fn makeString(self: *Dwarf, bytes: []const u8) !u32 { | |
| 2440 | try self.strtab.ensureUnusedCapacity(self.allocator, bytes.len + 1); | |
| 2441 | const result = self.strtab.items.len; | |
| 2442 | self.strtab.appendSliceAssumeCapacity(bytes); | |
| 2443 | self.strtab.appendAssumeCapacity(0); | |
| 2444 | return @intCast(u32, result); | |
| 2445 | } | |
| 2446 | ||
| 2447 | 2466 | fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) { |
| 2448 | 2467 | return actual_size +| (actual_size / ideal_factor); |
| 2449 | 2468 | } |
| ... | ... | @@ -2465,29 +2484,20 @@ pub fn flushModule(self: *Dwarf, module: *Module) !void { |
| 2465 | 2484 | } |
| 2466 | 2485 | error_set.names = names; |
| 2467 | 2486 | |
| 2468 | const atom = try gpa.create(Atom); | |
| 2469 | errdefer gpa.destroy(atom); | |
| 2470 | atom.* = .{ | |
| 2471 | .prev = null, | |
| 2472 | .next = null, | |
| 2473 | .off = 0, | |
| 2474 | .len = 0, | |
| 2475 | }; | |
| 2476 | ||
| 2477 | 2487 | var dbg_info_buffer = std.ArrayList(u8).init(arena); |
| 2478 | 2488 | try addDbgInfoErrorSet(arena, module, error_ty, self.target, &dbg_info_buffer); |
| 2479 | 2489 | |
| 2480 | try self.managed_atoms.append(gpa, atom); | |
| 2490 | const di_atom_index = try self.createAtom(.di_atom); | |
| 2481 | 2491 | log.debug("updateDeclDebugInfoAllocation in flushModule", .{}); |
| 2482 | try self.updateDeclDebugInfoAllocation(atom, @intCast(u32, dbg_info_buffer.items.len)); | |
| 2492 | try self.updateDeclDebugInfoAllocation(di_atom_index, @intCast(u32, dbg_info_buffer.items.len)); | |
| 2483 | 2493 | log.debug("writeDeclDebugInfo in flushModule", .{}); |
| 2484 | try self.writeDeclDebugInfo(atom, dbg_info_buffer.items); | |
| 2494 | try self.writeDeclDebugInfo(di_atom_index, dbg_info_buffer.items); | |
| 2485 | 2495 | |
| 2486 | 2496 | const file_pos = blk: { |
| 2487 | 2497 | switch (self.bin_file.tag) { |
| 2488 | 2498 | .elf => { |
| 2489 | 2499 | const elf_file = self.bin_file.cast(File.Elf).?; |
| 2490 | const debug_info_sect = &elf_file.sections.items[elf_file.debug_info_section_index.?]; | |
| 2500 | const debug_info_sect = &elf_file.sections.items(.shdr)[elf_file.debug_info_section_index.?]; | |
| 2491 | 2501 | break :blk debug_info_sect.sh_offset; |
| 2492 | 2502 | }, |
| 2493 | 2503 | .macho => { |
| ... | ... | @@ -2502,22 +2512,23 @@ pub fn flushModule(self: *Dwarf, module: *Module) !void { |
| 2502 | 2512 | }; |
| 2503 | 2513 | |
| 2504 | 2514 | var buf: [@sizeOf(u32)]u8 = undefined; |
| 2505 | mem.writeInt(u32, &buf, atom.off, self.target.cpu.arch.endian()); | |
| 2515 | mem.writeInt(u32, &buf, self.getAtom(.di_atom, di_atom_index).off, self.target.cpu.arch.endian()); | |
| 2506 | 2516 | |
| 2507 | 2517 | while (self.global_abbrev_relocs.popOrNull()) |reloc| { |
| 2518 | const atom = self.getAtom(.di_atom, reloc.atom_index); | |
| 2508 | 2519 | switch (self.bin_file.tag) { |
| 2509 | 2520 | .elf => { |
| 2510 | 2521 | const elf_file = self.bin_file.cast(File.Elf).?; |
| 2511 | try elf_file.base.file.?.pwriteAll(&buf, file_pos + reloc.atom.off + reloc.offset); | |
| 2522 | try elf_file.base.file.?.pwriteAll(&buf, file_pos + atom.off + reloc.offset); | |
| 2512 | 2523 | }, |
| 2513 | 2524 | .macho => { |
| 2514 | 2525 | const d_sym = self.bin_file.cast(File.MachO).?.getDebugSymbols().?; |
| 2515 | try d_sym.file.pwriteAll(&buf, file_pos + reloc.atom.off + reloc.offset); | |
| 2526 | try d_sym.file.pwriteAll(&buf, file_pos + atom.off + reloc.offset); | |
| 2516 | 2527 | }, |
| 2517 | 2528 | .wasm => { |
| 2518 | 2529 | const wasm_file = self.bin_file.cast(File.Wasm).?; |
| 2519 | const debug_info = wasm_file.debug_info_atom.?.code; | |
| 2520 | mem.copy(u8, debug_info.items[reloc.atom.off + reloc.offset ..], &buf); | |
| 2530 | const debug_info = wasm_file.getAtomPtr(wasm_file.debug_info_atom.?).code; | |
| 2531 | mem.copy(u8, debug_info.items[atom.off + reloc.offset ..], &buf); | |
| 2521 | 2532 | }, |
| 2522 | 2533 | else => unreachable, |
| 2523 | 2534 | } |
| ... | ... | @@ -2635,12 +2646,62 @@ fn addDbgInfoErrorSet( |
| 2635 | 2646 | try dbg_info_buffer.append(0); |
| 2636 | 2647 | } |
| 2637 | 2648 | |
| 2638 | fn getDbgInfoAtom(tag: File.Tag, mod: *Module, decl_index: Module.Decl.Index) *Atom { | |
| 2639 | const decl = mod.declPtr(decl_index); | |
| 2640 | return switch (tag) { | |
| 2641 | .elf => &decl.link.elf.dbg_info_atom, | |
| 2642 | .macho => &decl.link.macho.dbg_info_atom, | |
| 2643 | .wasm => &decl.link.wasm.dbg_info_atom, | |
| 2644 | else => unreachable, | |
| 2649 | const Kind = enum { src_fn, di_atom }; | |
| 2650 | ||
| 2651 | fn createAtom(self: *Dwarf, comptime kind: Kind) !Atom.Index { | |
| 2652 | const index = blk: { | |
| 2653 | switch (kind) { | |
| 2654 | .src_fn => { | |
| 2655 | const index = @intCast(Atom.Index, self.src_fns.items.len); | |
| 2656 | _ = try self.src_fns.addOne(self.allocator); | |
| 2657 | break :blk index; | |
| 2658 | }, | |
| 2659 | .di_atom => { | |
| 2660 | const index = @intCast(Atom.Index, self.di_atoms.items.len); | |
| 2661 | _ = try self.di_atoms.addOne(self.allocator); | |
| 2662 | break :blk index; | |
| 2663 | }, | |
| 2664 | } | |
| 2665 | }; | |
| 2666 | const atom = self.getAtomPtr(kind, index); | |
| 2667 | atom.* = .{ | |
| 2668 | .off = 0, | |
| 2669 | .len = 0, | |
| 2670 | .prev_index = null, | |
| 2671 | .next_index = null, | |
| 2672 | }; | |
| 2673 | return index; | |
| 2674 | } | |
| 2675 | ||
| 2676 | fn getOrCreateAtomForDecl(self: *Dwarf, comptime kind: Kind, decl_index: Module.Decl.Index) !Atom.Index { | |
| 2677 | switch (kind) { | |
| 2678 | .src_fn => { | |
| 2679 | const gop = try self.src_fn_decls.getOrPut(self.allocator, decl_index); | |
| 2680 | if (!gop.found_existing) { | |
| 2681 | gop.value_ptr.* = try self.createAtom(kind); | |
| 2682 | } | |
| 2683 | return gop.value_ptr.*; | |
| 2684 | }, | |
| 2685 | .di_atom => { | |
| 2686 | const gop = try self.di_atom_decls.getOrPut(self.allocator, decl_index); | |
| 2687 | if (!gop.found_existing) { | |
| 2688 | gop.value_ptr.* = try self.createAtom(kind); | |
| 2689 | } | |
| 2690 | return gop.value_ptr.*; | |
| 2691 | }, | |
| 2692 | } | |
| 2693 | } | |
| 2694 | ||
| 2695 | fn getAtom(self: *const Dwarf, comptime kind: Kind, index: Atom.Index) Atom { | |
| 2696 | return switch (kind) { | |
| 2697 | .src_fn => self.src_fns.items[index], | |
| 2698 | .di_atom => self.di_atoms.items[index], | |
| 2699 | }; | |
| 2700 | } | |
| 2701 | ||
| 2702 | fn getAtomPtr(self: *Dwarf, comptime kind: Kind, index: Atom.Index) *Atom { | |
| 2703 | return switch (kind) { | |
| 2704 | .src_fn => &self.src_fns.items[index], | |
| 2705 | .di_atom => &self.di_atoms.items[index], | |
| 2645 | 2706 | }; |
| 2646 | 2707 | } |
src/link/Elf.zig+665-633| ... | ... | @@ -1,40 +1,89 @@ |
| 1 | 1 | const Elf = @This(); |
| 2 | 2 | |
| 3 | 3 | const std = @import("std"); |
| 4 | const build_options = @import("build_options"); | |
| 4 | 5 | const builtin = @import("builtin"); |
| 5 | const math = std.math; | |
| 6 | const mem = std.mem; | |
| 7 | 6 | const assert = std.debug.assert; |
| 8 | const Allocator = std.mem.Allocator; | |
| 9 | const fs = std.fs; | |
| 10 | 7 | const elf = std.elf; |
| 8 | const fs = std.fs; | |
| 11 | 9 | const log = std.log.scoped(.link); |
| 10 | const math = std.math; | |
| 11 | const mem = std.mem; | |
| 12 | 12 | |
| 13 | const Module = @import("../Module.zig"); | |
| 14 | const Compilation = @import("../Compilation.zig"); | |
| 15 | const Dwarf = @import("Dwarf.zig"); | |
| 16 | 13 | const codegen = @import("../codegen.zig"); |
| 17 | const lldMain = @import("../main.zig").lldMain; | |
| 18 | const trace = @import("../tracy.zig").trace; | |
| 19 | const Package = @import("../Package.zig"); | |
| 20 | const Value = @import("../value.zig").Value; | |
| 21 | const Type = @import("../type.zig").Type; | |
| 22 | const TypedValue = @import("../TypedValue.zig"); | |
| 23 | const link = @import("../link.zig"); | |
| 24 | const File = link.File; | |
| 25 | const build_options = @import("build_options"); | |
| 26 | const target_util = @import("../target.zig"); | |
| 27 | 14 | const glibc = @import("../glibc.zig"); |
| 15 | const link = @import("../link.zig"); | |
| 16 | const lldMain = @import("../main.zig").lldMain; | |
| 28 | 17 | const musl = @import("../musl.zig"); |
| 29 | const Cache = @import("../Cache.zig"); | |
| 18 | const target_util = @import("../target.zig"); | |
| 19 | const trace = @import("../tracy.zig").trace; | |
| 20 | ||
| 30 | 21 | const Air = @import("../Air.zig"); |
| 22 | const Allocator = std.mem.Allocator; | |
| 23 | pub const Atom = @import("Elf/Atom.zig"); | |
| 24 | const Cache = @import("../Cache.zig"); | |
| 25 | const Compilation = @import("../Compilation.zig"); | |
| 26 | const Dwarf = @import("Dwarf.zig"); | |
| 27 | const File = link.File; | |
| 31 | 28 | const Liveness = @import("../Liveness.zig"); |
| 32 | 29 | const LlvmObject = @import("../codegen/llvm.zig").Object; |
| 30 | const Module = @import("../Module.zig"); | |
| 31 | const Package = @import("../Package.zig"); | |
| 32 | const StringTable = @import("strtab.zig").StringTable; | |
| 33 | const Type = @import("../type.zig").Type; | |
| 34 | const TypedValue = @import("../TypedValue.zig"); | |
| 35 | const Value = @import("../value.zig").Value; | |
| 33 | 36 | |
| 34 | 37 | const default_entry_addr = 0x8000000; |
| 35 | 38 | |
| 36 | 39 | pub const base_tag: File.Tag = .elf; |
| 37 | 40 | |
| 41 | const Section = struct { | |
| 42 | shdr: elf.Elf64_Shdr, | |
| 43 | phdr_index: u16, | |
| 44 | ||
| 45 | /// Index of the last allocated atom in this section. | |
| 46 | last_atom_index: ?Atom.Index = null, | |
| 47 | ||
| 48 | /// A list of atoms that have surplus capacity. This list can have false | |
| 49 | /// positives, as functions grow and shrink over time, only sometimes being added | |
| 50 | /// or removed from the freelist. | |
| 51 | /// | |
| 52 | /// An atom has surplus capacity when its overcapacity value is greater than | |
| 53 | /// padToIdeal(minimum_atom_size). That is, when it has so | |
| 54 | /// much extra capacity, that we could fit a small new symbol in it, itself with | |
| 55 | /// ideal_capacity or more. | |
| 56 | /// | |
| 57 | /// Ideal capacity is defined by size + (size / ideal_factor) | |
| 58 | /// | |
| 59 | /// Overcapacity is measured by actual_capacity - ideal_capacity. Note that | |
| 60 | /// overcapacity can be negative. A simple way to have negative overcapacity is to | |
| 61 | /// allocate a fresh text block, which will have ideal capacity, and then grow it | |
| 62 | /// by 1 byte. It will then have -1 overcapacity. | |
| 63 | free_list: std.ArrayListUnmanaged(Atom.Index) = .{}, | |
| 64 | }; | |
| 65 | ||
| 66 | const DeclMetadata = struct { | |
| 67 | atom: Atom.Index, | |
| 68 | shdr: u16, | |
| 69 | /// A list of all exports aliases of this Decl. | |
| 70 | exports: std.ArrayListUnmanaged(u32) = .{}, | |
| 71 | ||
| 72 | fn getExport(m: DeclMetadata, elf_file: *const Elf, name: []const u8) ?u32 { | |
| 73 | for (m.exports.items) |exp| { | |
| 74 | if (mem.eql(u8, name, elf_file.getGlobalName(exp))) return exp; | |
| 75 | } | |
| 76 | return null; | |
| 77 | } | |
| 78 | ||
| 79 | fn getExportPtr(m: *DeclMetadata, elf_file: *Elf, name: []const u8) ?*u32 { | |
| 80 | for (m.exports.items) |*exp| { | |
| 81 | if (mem.eql(u8, name, elf_file.getGlobalName(exp.*))) return exp; | |
| 82 | } | |
| 83 | return null; | |
| 84 | } | |
| 85 | }; | |
| 86 | ||
| 38 | 87 | base: File, |
| 39 | 88 | dwarf: ?Dwarf = null, |
| 40 | 89 | |
| ... | ... | @@ -45,12 +94,12 @@ llvm_object: ?*LlvmObject = null, |
| 45 | 94 | |
| 46 | 95 | /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write. |
| 47 | 96 | /// Same order as in the file. |
| 48 | sections: std.ArrayListUnmanaged(elf.Elf64_Shdr) = std.ArrayListUnmanaged(elf.Elf64_Shdr){}, | |
| 97 | sections: std.MultiArrayList(Section) = .{}, | |
| 49 | 98 | shdr_table_offset: ?u64 = null, |
| 50 | 99 | |
| 51 | 100 | /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write. |
| 52 | 101 | /// Same order as in the file. |
| 53 | program_headers: std.ArrayListUnmanaged(elf.Elf64_Phdr) = std.ArrayListUnmanaged(elf.Elf64_Phdr){}, | |
| 102 | program_headers: std.ArrayListUnmanaged(elf.Elf64_Phdr) = .{}, | |
| 54 | 103 | phdr_table_offset: ?u64 = null, |
| 55 | 104 | /// The index into the program headers of a PT_LOAD program header with Read and Execute flags |
| 56 | 105 | phdr_load_re_index: ?u16 = null, |
| ... | ... | @@ -62,12 +111,10 @@ phdr_load_ro_index: ?u16 = null, |
| 62 | 111 | /// The index into the program headers of a PT_LOAD program header with Write flag |
| 63 | 112 | phdr_load_rw_index: ?u16 = null, |
| 64 | 113 | |
| 65 | phdr_shdr_table: std.AutoHashMapUnmanaged(u16, u16) = .{}, | |
| 66 | ||
| 67 | 114 | entry_addr: ?u64 = null, |
| 68 | 115 | page_size: u32, |
| 69 | 116 | |
| 70 | shstrtab: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8){}, | |
| 117 | shstrtab: StringTable(.strtab) = .{}, | |
| 71 | 118 | shstrtab_index: ?u16 = null, |
| 72 | 119 | |
| 73 | 120 | symtab_section_index: ?u16 = null, |
| ... | ... | @@ -110,39 +157,14 @@ debug_line_header_dirty: bool = false, |
| 110 | 157 | |
| 111 | 158 | error_flags: File.ErrorFlags = File.ErrorFlags{}, |
| 112 | 159 | |
| 113 | /// Pointer to the last allocated atom | |
| 114 | atoms: std.AutoHashMapUnmanaged(u16, *TextBlock) = .{}, | |
| 115 | ||
| 116 | /// A list of text blocks that have surplus capacity. This list can have false | |
| 117 | /// positives, as functions grow and shrink over time, only sometimes being added | |
| 118 | /// or removed from the freelist. | |
| 119 | /// | |
| 120 | /// A text block has surplus capacity when its overcapacity value is greater than | |
| 121 | /// padToIdeal(minimum_text_block_size). That is, when it has so | |
| 122 | /// much extra capacity, that we could fit a small new symbol in it, itself with | |
| 123 | /// ideal_capacity or more. | |
| 124 | /// | |
| 125 | /// Ideal capacity is defined by size + (size / ideal_factor) | |
| 126 | /// | |
| 127 | /// Overcapacity is measured by actual_capacity - ideal_capacity. Note that | |
| 128 | /// overcapacity can be negative. A simple way to have negative overcapacity is to | |
| 129 | /// allocate a fresh text block, which will have ideal capacity, and then grow it | |
| 130 | /// by 1 byte. It will then have -1 overcapacity. | |
| 131 | atom_free_lists: std.AutoHashMapUnmanaged(u16, std.ArrayListUnmanaged(*TextBlock)) = .{}, | |
| 132 | ||
| 133 | /// Table of Decls that are currently alive. | |
| 134 | /// We store them here so that we can properly dispose of any allocated | |
| 135 | /// memory within the atom in the incremental linker. | |
| 136 | /// TODO consolidate this. | |
| 137 | decls: std.AutoHashMapUnmanaged(Module.Decl.Index, ?u16) = .{}, | |
| 160 | /// Table of tracked Decls. | |
| 161 | decls: std.AutoHashMapUnmanaged(Module.Decl.Index, DeclMetadata) = .{}, | |
| 138 | 162 | |
| 139 | 163 | /// List of atoms that are owned directly by the linker. |
| 140 | /// Currently these are only atoms that are the result of linking | |
| 141 | /// object files. Atoms which take part in incremental linking are | |
| 142 | /// at present owned by Module.Decl. | |
| 143 | /// TODO consolidate this. | |
| 144 | managed_atoms: std.ArrayListUnmanaged(*TextBlock) = .{}, | |
| 145 | atom_by_index_table: std.AutoHashMapUnmanaged(u32, *TextBlock) = .{}, | |
| 164 | atoms: std.ArrayListUnmanaged(Atom) = .{}, | |
| 165 | ||
| 166 | /// Table of atoms indexed by the symbol index. | |
| 167 | atom_by_index_table: std.AutoHashMapUnmanaged(u32, Atom.Index) = .{}, | |
| 146 | 168 | |
| 147 | 169 | /// Table of unnamed constants associated with a parent `Decl`. |
| 148 | 170 | /// We store them here so that we can free the constants whenever the `Decl` |
| ... | ... | @@ -170,15 +192,8 @@ unnamed_const_atoms: UnnamedConstTable = .{}, |
| 170 | 192 | /// this will be a table indexed by index into the list of Atoms. |
| 171 | 193 | relocs: RelocTable = .{}, |
| 172 | 194 | |
| 173 | const Reloc = struct { | |
| 174 | target: u32, | |
| 175 | offset: u64, | |
| 176 | addend: u32, | |
| 177 | prev_vaddr: u64, | |
| 178 | }; | |
| 179 | ||
| 180 | const RelocTable = std.AutoHashMapUnmanaged(*TextBlock, std.ArrayListUnmanaged(Reloc)); | |
| 181 | const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(*TextBlock)); | |
| 195 | const RelocTable = std.AutoHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(Atom.Reloc)); | |
| 196 | const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(Atom.Index)); | |
| 182 | 197 | |
| 183 | 198 | /// When allocating, the ideal_capacity is calculated by |
| 184 | 199 | /// actual_capacity + (actual_capacity / ideal_factor) |
| ... | ... | @@ -187,67 +202,11 @@ const ideal_factor = 3; |
| 187 | 202 | /// In order for a slice of bytes to be considered eligible to keep metadata pointing at |
| 188 | 203 | /// it as a possible place to put new symbols, it must have enough room for this many bytes |
| 189 | 204 | /// (plus extra for reserved capacity). |
| 190 | const minimum_text_block_size = 64; | |
| 191 | const min_text_capacity = padToIdeal(minimum_text_block_size); | |
| 205 | const minimum_atom_size = 64; | |
| 206 | pub const min_text_capacity = padToIdeal(minimum_atom_size); | |
| 192 | 207 | |
| 193 | 208 | pub const PtrWidth = enum { p32, p64 }; |
| 194 | 209 | |
| 195 | pub const TextBlock = struct { | |
| 196 | /// Each decl always gets a local symbol with the fully qualified name. | |
| 197 | /// The vaddr and size are found here directly. | |
| 198 | /// The file offset is found by computing the vaddr offset from the section vaddr | |
| 199 | /// the symbol references, and adding that to the file offset of the section. | |
| 200 | /// If this field is 0, it means the codegen size = 0 and there is no symbol or | |
| 201 | /// offset table entry. | |
| 202 | local_sym_index: u32, | |
| 203 | /// This field is undefined for symbols with size = 0. | |
| 204 | offset_table_index: u32, | |
| 205 | /// Points to the previous and next neighbors, based on the `text_offset`. | |
| 206 | /// This can be used to find, for example, the capacity of this `TextBlock`. | |
| 207 | prev: ?*TextBlock, | |
| 208 | next: ?*TextBlock, | |
| 209 | ||
| 210 | dbg_info_atom: Dwarf.Atom, | |
| 211 | ||
| 212 | pub const empty = TextBlock{ | |
| 213 | .local_sym_index = 0, | |
| 214 | .offset_table_index = undefined, | |
| 215 | .prev = null, | |
| 216 | .next = null, | |
| 217 | .dbg_info_atom = undefined, | |
| 218 | }; | |
| 219 | ||
| 220 | /// Returns how much room there is to grow in virtual address space. | |
| 221 | /// File offset relocation happens transparently, so it is not included in | |
| 222 | /// this calculation. | |
| 223 | fn capacity(self: TextBlock, elf_file: Elf) u64 { | |
| 224 | const self_sym = elf_file.local_symbols.items[self.local_sym_index]; | |
| 225 | if (self.next) |next| { | |
| 226 | const next_sym = elf_file.local_symbols.items[next.local_sym_index]; | |
| 227 | return next_sym.st_value - self_sym.st_value; | |
| 228 | } else { | |
| 229 | // We are the last block. The capacity is limited only by virtual address space. | |
| 230 | return std.math.maxInt(u32) - self_sym.st_value; | |
| 231 | } | |
| 232 | } | |
| 233 | ||
| 234 | fn freeListEligible(self: TextBlock, elf_file: Elf) bool { | |
| 235 | // No need to keep a free list node for the last block. | |
| 236 | const next = self.next orelse return false; | |
| 237 | const self_sym = elf_file.local_symbols.items[self.local_sym_index]; | |
| 238 | const next_sym = elf_file.local_symbols.items[next.local_sym_index]; | |
| 239 | const cap = next_sym.st_value - self_sym.st_value; | |
| 240 | const ideal_cap = padToIdeal(self_sym.st_size); | |
| 241 | if (cap <= ideal_cap) return false; | |
| 242 | const surplus = cap - ideal_cap; | |
| 243 | return surplus >= min_text_capacity; | |
| 244 | } | |
| 245 | }; | |
| 246 | ||
| 247 | pub const Export = struct { | |
| 248 | sym_index: ?u32 = null, | |
| 249 | }; | |
| 250 | ||
| 251 | 210 | pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Options) !*Elf { |
| 252 | 211 | assert(options.target.ofmt == .elf); |
| 253 | 212 | |
| ... | ... | @@ -279,16 +238,19 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option |
| 279 | 238 | |
| 280 | 239 | // There must always be a null section in index 0 |
| 281 | 240 | try self.sections.append(allocator, .{ |
| 282 | .sh_name = 0, | |
| 283 | .sh_type = elf.SHT_NULL, | |
| 284 | .sh_flags = 0, | |
| 285 | .sh_addr = 0, | |
| 286 | .sh_offset = 0, | |
| 287 | .sh_size = 0, | |
| 288 | .sh_link = 0, | |
| 289 | .sh_info = 0, | |
| 290 | .sh_addralign = 0, | |
| 291 | .sh_entsize = 0, | |
| 241 | .shdr = .{ | |
| 242 | .sh_name = 0, | |
| 243 | .sh_type = elf.SHT_NULL, | |
| 244 | .sh_flags = 0, | |
| 245 | .sh_addr = 0, | |
| 246 | .sh_offset = 0, | |
| 247 | .sh_size = 0, | |
| 248 | .sh_link = 0, | |
| 249 | .sh_info = 0, | |
| 250 | .sh_addralign = 0, | |
| 251 | .sh_entsize = 0, | |
| 252 | }, | |
| 253 | .phdr_index = undefined, | |
| 292 | 254 | }); |
| 293 | 255 | |
| 294 | 256 | try self.populateMissingMetadata(); |
| ... | ... | @@ -335,74 +297,67 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Elf { |
| 335 | 297 | } |
| 336 | 298 | |
| 337 | 299 | pub fn deinit(self: *Elf) void { |
| 300 | const gpa = self.base.allocator; | |
| 301 | ||
| 338 | 302 | if (build_options.have_llvm) { |
| 339 | if (self.llvm_object) |llvm_object| llvm_object.destroy(self.base.allocator); | |
| 340 | } | |
| 341 | ||
| 342 | self.sections.deinit(self.base.allocator); | |
| 343 | self.program_headers.deinit(self.base.allocator); | |
| 344 | self.shstrtab.deinit(self.base.allocator); | |
| 345 | self.local_symbols.deinit(self.base.allocator); | |
| 346 | self.global_symbols.deinit(self.base.allocator); | |
| 347 | self.global_symbol_free_list.deinit(self.base.allocator); | |
| 348 | self.local_symbol_free_list.deinit(self.base.allocator); | |
| 349 | self.offset_table_free_list.deinit(self.base.allocator); | |
| 350 | self.offset_table.deinit(self.base.allocator); | |
| 351 | self.phdr_shdr_table.deinit(self.base.allocator); | |
| 352 | self.decls.deinit(self.base.allocator); | |
| 353 | ||
| 354 | self.atoms.deinit(self.base.allocator); | |
| 303 | if (self.llvm_object) |llvm_object| llvm_object.destroy(gpa); | |
| 304 | } | |
| 305 | ||
| 306 | for (self.sections.items(.free_list)) |*free_list| { | |
| 307 | free_list.deinit(gpa); | |
| 308 | } | |
| 309 | self.sections.deinit(gpa); | |
| 310 | ||
| 311 | self.program_headers.deinit(gpa); | |
| 312 | self.shstrtab.deinit(gpa); | |
| 313 | self.local_symbols.deinit(gpa); | |
| 314 | self.global_symbols.deinit(gpa); | |
| 315 | self.global_symbol_free_list.deinit(gpa); | |
| 316 | self.local_symbol_free_list.deinit(gpa); | |
| 317 | self.offset_table_free_list.deinit(gpa); | |
| 318 | self.offset_table.deinit(gpa); | |
| 319 | ||
| 355 | 320 | { |
| 356 | var it = self.atom_free_lists.valueIterator(); | |
| 357 | while (it.next()) |free_list| { | |
| 358 | free_list.deinit(self.base.allocator); | |
| 321 | var it = self.decls.iterator(); | |
| 322 | while (it.next()) |entry| { | |
| 323 | entry.value_ptr.exports.deinit(gpa); | |
| 359 | 324 | } |
| 360 | self.atom_free_lists.deinit(self.base.allocator); | |
| 325 | self.decls.deinit(gpa); | |
| 361 | 326 | } |
| 362 | 327 | |
| 363 | for (self.managed_atoms.items) |atom| { | |
| 364 | self.base.allocator.destroy(atom); | |
| 365 | } | |
| 366 | self.managed_atoms.deinit(self.base.allocator); | |
| 328 | self.atoms.deinit(gpa); | |
| 329 | self.atom_by_index_table.deinit(gpa); | |
| 367 | 330 | |
| 368 | 331 | { |
| 369 | 332 | var it = self.unnamed_const_atoms.valueIterator(); |
| 370 | 333 | while (it.next()) |atoms| { |
| 371 | atoms.deinit(self.base.allocator); | |
| 334 | atoms.deinit(gpa); | |
| 372 | 335 | } |
| 373 | self.unnamed_const_atoms.deinit(self.base.allocator); | |
| 336 | self.unnamed_const_atoms.deinit(gpa); | |
| 374 | 337 | } |
| 375 | 338 | |
| 376 | 339 | { |
| 377 | 340 | var it = self.relocs.valueIterator(); |
| 378 | 341 | while (it.next()) |relocs| { |
| 379 | relocs.deinit(self.base.allocator); | |
| 342 | relocs.deinit(gpa); | |
| 380 | 343 | } |
| 381 | self.relocs.deinit(self.base.allocator); | |
| 344 | self.relocs.deinit(gpa); | |
| 382 | 345 | } |
| 383 | 346 | |
| 384 | self.atom_by_index_table.deinit(self.base.allocator); | |
| 385 | ||
| 386 | 347 | if (self.dwarf) |*dw| { |
| 387 | 348 | dw.deinit(); |
| 388 | 349 | } |
| 389 | 350 | } |
| 390 | 351 | |
| 391 | 352 | pub fn getDeclVAddr(self: *Elf, decl_index: Module.Decl.Index, reloc_info: File.RelocInfo) !u64 { |
| 392 | const mod = self.base.options.module.?; | |
| 393 | const decl = mod.declPtr(decl_index); | |
| 394 | ||
| 395 | 353 | assert(self.llvm_object == null); |
| 396 | assert(decl.link.elf.local_sym_index != 0); | |
| 397 | 354 | |
| 398 | const target = decl.link.elf.local_sym_index; | |
| 399 | const vaddr = self.local_symbols.items[target].st_value; | |
| 400 | const atom = self.atom_by_index_table.get(reloc_info.parent_atom_index).?; | |
| 401 | const gop = try self.relocs.getOrPut(self.base.allocator, atom); | |
| 402 | if (!gop.found_existing) { | |
| 403 | gop.value_ptr.* = .{}; | |
| 404 | } | |
| 405 | try gop.value_ptr.append(self.base.allocator, .{ | |
| 355 | const this_atom_index = try self.getOrCreateAtomForDecl(decl_index); | |
| 356 | const this_atom = self.getAtom(this_atom_index); | |
| 357 | const target = this_atom.getSymbolIndex().?; | |
| 358 | const vaddr = this_atom.getSymbol(self).st_value; | |
| 359 | const atom_index = self.getAtomIndexForSymbol(reloc_info.parent_atom_index).?; | |
| 360 | try Atom.addRelocation(self, atom_index, .{ | |
| 406 | 361 | .target = target, |
| 407 | 362 | .offset = reloc_info.offset, |
| 408 | 363 | .addend = reloc_info.addend, |
| ... | ... | @@ -423,7 +378,7 @@ fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 { |
| 423 | 378 | |
| 424 | 379 | if (self.shdr_table_offset) |off| { |
| 425 | 380 | const shdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Shdr) else @sizeOf(elf.Elf64_Shdr); |
| 426 | const tight_size = self.sections.items.len * shdr_size; | |
| 381 | const tight_size = self.sections.slice().len * shdr_size; | |
| 427 | 382 | const increased_size = padToIdeal(tight_size); |
| 428 | 383 | const test_end = off + increased_size; |
| 429 | 384 | if (end > off and start < test_end) { |
| ... | ... | @@ -433,7 +388,7 @@ fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 { |
| 433 | 388 | |
| 434 | 389 | if (self.phdr_table_offset) |off| { |
| 435 | 390 | const phdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Phdr) else @sizeOf(elf.Elf64_Phdr); |
| 436 | const tight_size = self.sections.items.len * phdr_size; | |
| 391 | const tight_size = self.sections.slice().len * phdr_size; | |
| 437 | 392 | const increased_size = padToIdeal(tight_size); |
| 438 | 393 | const test_end = off + increased_size; |
| 439 | 394 | if (end > off and start < test_end) { |
| ... | ... | @@ -441,7 +396,7 @@ fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 { |
| 441 | 396 | } |
| 442 | 397 | } |
| 443 | 398 | |
| 444 | for (self.sections.items) |section| { | |
| 399 | for (self.sections.items(.shdr)) |section| { | |
| 445 | 400 | const increased_size = padToIdeal(section.sh_size); |
| 446 | 401 | const test_end = section.sh_offset + increased_size; |
| 447 | 402 | if (end > section.sh_offset and start < test_end) { |
| ... | ... | @@ -468,7 +423,7 @@ pub fn allocatedSize(self: *Elf, start: u64) u64 { |
| 468 | 423 | if (self.phdr_table_offset) |off| { |
| 469 | 424 | if (off > start and off < min_pos) min_pos = off; |
| 470 | 425 | } |
| 471 | for (self.sections.items) |section| { | |
| 426 | for (self.sections.items(.shdr)) |section| { | |
| 472 | 427 | if (section.sh_offset <= start) continue; |
| 473 | 428 | if (section.sh_offset < min_pos) min_pos = section.sh_offset; |
| 474 | 429 | } |
| ... | ... | @@ -487,31 +442,10 @@ pub fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u32) u64 { |
| 487 | 442 | return start; |
| 488 | 443 | } |
| 489 | 444 | |
| 490 | /// TODO Improve this to use a table. | |
| 491 | fn makeString(self: *Elf, bytes: []const u8) !u32 { | |
| 492 | try self.shstrtab.ensureUnusedCapacity(self.base.allocator, bytes.len + 1); | |
| 493 | const result = self.shstrtab.items.len; | |
| 494 | self.shstrtab.appendSliceAssumeCapacity(bytes); | |
| 495 | self.shstrtab.appendAssumeCapacity(0); | |
| 496 | return @intCast(u32, result); | |
| 497 | } | |
| 498 | ||
| 499 | fn getString(self: Elf, str_off: u32) []const u8 { | |
| 500 | assert(str_off < self.shstrtab.items.len); | |
| 501 | return mem.sliceTo(@ptrCast([*:0]const u8, self.shstrtab.items.ptr + str_off), 0); | |
| 502 | } | |
| 503 | ||
| 504 | fn updateString(self: *Elf, old_str_off: u32, new_name: []const u8) !u32 { | |
| 505 | const existing_name = self.getString(old_str_off); | |
| 506 | if (mem.eql(u8, existing_name, new_name)) { | |
| 507 | return old_str_off; | |
| 508 | } | |
| 509 | return self.makeString(new_name); | |
| 510 | } | |
| 511 | ||
| 512 | 445 | pub fn populateMissingMetadata(self: *Elf) !void { |
| 513 | 446 | assert(self.llvm_object == null); |
| 514 | 447 | |
| 448 | const gpa = self.base.allocator; | |
| 515 | 449 | const small_ptr = switch (self.ptr_width) { |
| 516 | 450 | .p32 => true, |
| 517 | 451 | .p64 => false, |
| ... | ... | @@ -525,7 +459,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { |
| 525 | 459 | const off = self.findFreeSpace(file_size, p_align); |
| 526 | 460 | log.debug("found PT_LOAD RE free space 0x{x} to 0x{x}", .{ off, off + file_size }); |
| 527 | 461 | const entry_addr: u64 = self.entry_addr orelse if (self.base.options.target.cpu.arch == .spu_2) @as(u64, 0) else default_entry_addr; |
| 528 | try self.program_headers.append(self.base.allocator, .{ | |
| 462 | try self.program_headers.append(gpa, .{ | |
| 529 | 463 | .p_type = elf.PT_LOAD, |
| 530 | 464 | .p_offset = off, |
| 531 | 465 | .p_filesz = file_size, |
| ... | ... | @@ -535,7 +469,6 @@ pub fn populateMissingMetadata(self: *Elf) !void { |
| 535 | 469 | .p_align = p_align, |
| 536 | 470 | .p_flags = elf.PF_X | elf.PF_R, |
| 537 | 471 | }); |
| 538 | try self.atom_free_lists.putNoClobber(self.base.allocator, self.phdr_load_re_index.?, .{}); | |
| 539 | 472 | self.entry_addr = null; |
| 540 | 473 | self.phdr_table_dirty = true; |
| 541 | 474 | } |
| ... | ... | @@ -552,7 +485,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { |
| 552 | 485 | // we'll need to re-use that function anyway, in case the GOT grows and overlaps something |
| 553 | 486 | // else in virtual memory. |
| 554 | 487 | const got_addr: u32 = if (self.base.options.target.cpu.arch.ptrBitWidth() >= 32) 0x4000000 else 0x8000; |
| 555 | try self.program_headers.append(self.base.allocator, .{ | |
| 488 | try self.program_headers.append(gpa, .{ | |
| 556 | 489 | .p_type = elf.PT_LOAD, |
| 557 | 490 | .p_offset = off, |
| 558 | 491 | .p_filesz = file_size, |
| ... | ... | @@ -575,7 +508,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { |
| 575 | 508 | log.debug("found PT_LOAD RO free space 0x{x} to 0x{x}", .{ off, off + file_size }); |
| 576 | 509 | // TODO Same as for GOT |
| 577 | 510 | const rodata_addr: u32 = if (self.base.options.target.cpu.arch.ptrBitWidth() >= 32) 0xc000000 else 0xa000; |
| 578 | try self.program_headers.append(self.base.allocator, .{ | |
| 511 | try self.program_headers.append(gpa, .{ | |
| 579 | 512 | .p_type = elf.PT_LOAD, |
| 580 | 513 | .p_offset = off, |
| 581 | 514 | .p_filesz = file_size, |
| ... | ... | @@ -585,7 +518,6 @@ pub fn populateMissingMetadata(self: *Elf) !void { |
| 585 | 518 | .p_align = p_align, |
| 586 | 519 | .p_flags = elf.PF_R, |
| 587 | 520 | }); |
| 588 | try self.atom_free_lists.putNoClobber(self.base.allocator, self.phdr_load_ro_index.?, .{}); | |
| 589 | 521 | self.phdr_table_dirty = true; |
| 590 | 522 | } |
| 591 | 523 | |
| ... | ... | @@ -599,7 +531,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { |
| 599 | 531 | log.debug("found PT_LOAD RW free space 0x{x} to 0x{x}", .{ off, off + file_size }); |
| 600 | 532 | // TODO Same as for GOT |
| 601 | 533 | const rwdata_addr: u32 = if (self.base.options.target.cpu.arch.ptrBitWidth() >= 32) 0x10000000 else 0xc000; |
| 602 | try self.program_headers.append(self.base.allocator, .{ | |
| 534 | try self.program_headers.append(gpa, .{ | |
| 603 | 535 | .p_type = elf.PT_LOAD, |
| 604 | 536 | .p_offset = off, |
| 605 | 537 | .p_filesz = file_size, |
| ... | ... | @@ -609,148 +541,145 @@ pub fn populateMissingMetadata(self: *Elf) !void { |
| 609 | 541 | .p_align = p_align, |
| 610 | 542 | .p_flags = elf.PF_R | elf.PF_W, |
| 611 | 543 | }); |
| 612 | try self.atom_free_lists.putNoClobber(self.base.allocator, self.phdr_load_rw_index.?, .{}); | |
| 613 | 544 | self.phdr_table_dirty = true; |
| 614 | 545 | } |
| 615 | 546 | |
| 616 | 547 | if (self.shstrtab_index == null) { |
| 617 | self.shstrtab_index = @intCast(u16, self.sections.items.len); | |
| 618 | assert(self.shstrtab.items.len == 0); | |
| 619 | try self.shstrtab.append(self.base.allocator, 0); // need a 0 at position 0 | |
| 620 | const off = self.findFreeSpace(self.shstrtab.items.len, 1); | |
| 621 | log.debug("found shstrtab free space 0x{x} to 0x{x}", .{ off, off + self.shstrtab.items.len }); | |
| 622 | try self.sections.append(self.base.allocator, .{ | |
| 623 | .sh_name = try self.makeString(".shstrtab"), | |
| 624 | .sh_type = elf.SHT_STRTAB, | |
| 625 | .sh_flags = 0, | |
| 626 | .sh_addr = 0, | |
| 627 | .sh_offset = off, | |
| 628 | .sh_size = self.shstrtab.items.len, | |
| 629 | .sh_link = 0, | |
| 630 | .sh_info = 0, | |
| 631 | .sh_addralign = 1, | |
| 632 | .sh_entsize = 0, | |
| 548 | self.shstrtab_index = @intCast(u16, self.sections.slice().len); | |
| 549 | assert(self.shstrtab.buffer.items.len == 0); | |
| 550 | try self.shstrtab.buffer.append(gpa, 0); // need a 0 at position 0 | |
| 551 | const off = self.findFreeSpace(self.shstrtab.buffer.items.len, 1); | |
| 552 | log.debug("found shstrtab free space 0x{x} to 0x{x}", .{ off, off + self.shstrtab.buffer.items.len }); | |
| 553 | try self.sections.append(gpa, .{ | |
| 554 | .shdr = .{ | |
| 555 | .sh_name = try self.shstrtab.insert(gpa, ".shstrtab"), | |
| 556 | .sh_type = elf.SHT_STRTAB, | |
| 557 | .sh_flags = 0, | |
| 558 | .sh_addr = 0, | |
| 559 | .sh_offset = off, | |
| 560 | .sh_size = self.shstrtab.buffer.items.len, | |
| 561 | .sh_link = 0, | |
| 562 | .sh_info = 0, | |
| 563 | .sh_addralign = 1, | |
| 564 | .sh_entsize = 0, | |
| 565 | }, | |
| 566 | .phdr_index = undefined, | |
| 633 | 567 | }); |
| 634 | 568 | self.shstrtab_dirty = true; |
| 635 | 569 | self.shdr_table_dirty = true; |
| 636 | 570 | } |
| 637 | 571 | |
| 638 | 572 | if (self.text_section_index == null) { |
| 639 | self.text_section_index = @intCast(u16, self.sections.items.len); | |
| 573 | self.text_section_index = @intCast(u16, self.sections.slice().len); | |
| 640 | 574 | const phdr = &self.program_headers.items[self.phdr_load_re_index.?]; |
| 641 | 575 | |
| 642 | try self.sections.append(self.base.allocator, .{ | |
| 643 | .sh_name = try self.makeString(".text"), | |
| 644 | .sh_type = elf.SHT_PROGBITS, | |
| 645 | .sh_flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR, | |
| 646 | .sh_addr = phdr.p_vaddr, | |
| 647 | .sh_offset = phdr.p_offset, | |
| 648 | .sh_size = phdr.p_filesz, | |
| 649 | .sh_link = 0, | |
| 650 | .sh_info = 0, | |
| 651 | .sh_addralign = 1, | |
| 652 | .sh_entsize = 0, | |
| 576 | try self.sections.append(gpa, .{ | |
| 577 | .shdr = .{ | |
| 578 | .sh_name = try self.shstrtab.insert(gpa, ".text"), | |
| 579 | .sh_type = elf.SHT_PROGBITS, | |
| 580 | .sh_flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR, | |
| 581 | .sh_addr = phdr.p_vaddr, | |
| 582 | .sh_offset = phdr.p_offset, | |
| 583 | .sh_size = phdr.p_filesz, | |
| 584 | .sh_link = 0, | |
| 585 | .sh_info = 0, | |
| 586 | .sh_addralign = 1, | |
| 587 | .sh_entsize = 0, | |
| 588 | }, | |
| 589 | .phdr_index = self.phdr_load_re_index.?, | |
| 653 | 590 | }); |
| 654 | try self.phdr_shdr_table.putNoClobber( | |
| 655 | self.base.allocator, | |
| 656 | self.phdr_load_re_index.?, | |
| 657 | self.text_section_index.?, | |
| 658 | ); | |
| 659 | 591 | self.shdr_table_dirty = true; |
| 660 | 592 | } |
| 661 | 593 | |
| 662 | 594 | if (self.got_section_index == null) { |
| 663 | self.got_section_index = @intCast(u16, self.sections.items.len); | |
| 595 | self.got_section_index = @intCast(u16, self.sections.slice().len); | |
| 664 | 596 | const phdr = &self.program_headers.items[self.phdr_got_index.?]; |
| 665 | 597 | |
| 666 | try self.sections.append(self.base.allocator, .{ | |
| 667 | .sh_name = try self.makeString(".got"), | |
| 668 | .sh_type = elf.SHT_PROGBITS, | |
| 669 | .sh_flags = elf.SHF_ALLOC, | |
| 670 | .sh_addr = phdr.p_vaddr, | |
| 671 | .sh_offset = phdr.p_offset, | |
| 672 | .sh_size = phdr.p_filesz, | |
| 673 | .sh_link = 0, | |
| 674 | .sh_info = 0, | |
| 675 | .sh_addralign = @as(u16, ptr_size), | |
| 676 | .sh_entsize = 0, | |
| 598 | try self.sections.append(gpa, .{ | |
| 599 | .shdr = .{ | |
| 600 | .sh_name = try self.shstrtab.insert(gpa, ".got"), | |
| 601 | .sh_type = elf.SHT_PROGBITS, | |
| 602 | .sh_flags = elf.SHF_ALLOC, | |
| 603 | .sh_addr = phdr.p_vaddr, | |
| 604 | .sh_offset = phdr.p_offset, | |
| 605 | .sh_size = phdr.p_filesz, | |
| 606 | .sh_link = 0, | |
| 607 | .sh_info = 0, | |
| 608 | .sh_addralign = @as(u16, ptr_size), | |
| 609 | .sh_entsize = 0, | |
| 610 | }, | |
| 611 | .phdr_index = self.phdr_got_index.?, | |
| 677 | 612 | }); |
| 678 | try self.phdr_shdr_table.putNoClobber( | |
| 679 | self.base.allocator, | |
| 680 | self.phdr_got_index.?, | |
| 681 | self.got_section_index.?, | |
| 682 | ); | |
| 683 | 613 | self.shdr_table_dirty = true; |
| 684 | 614 | } |
| 685 | 615 | |
| 686 | 616 | if (self.rodata_section_index == null) { |
| 687 | self.rodata_section_index = @intCast(u16, self.sections.items.len); | |
| 617 | self.rodata_section_index = @intCast(u16, self.sections.slice().len); | |
| 688 | 618 | const phdr = &self.program_headers.items[self.phdr_load_ro_index.?]; |
| 689 | 619 | |
| 690 | try self.sections.append(self.base.allocator, .{ | |
| 691 | .sh_name = try self.makeString(".rodata"), | |
| 692 | .sh_type = elf.SHT_PROGBITS, | |
| 693 | .sh_flags = elf.SHF_ALLOC, | |
| 694 | .sh_addr = phdr.p_vaddr, | |
| 695 | .sh_offset = phdr.p_offset, | |
| 696 | .sh_size = phdr.p_filesz, | |
| 697 | .sh_link = 0, | |
| 698 | .sh_info = 0, | |
| 699 | .sh_addralign = 1, | |
| 700 | .sh_entsize = 0, | |
| 620 | try self.sections.append(gpa, .{ | |
| 621 | .shdr = .{ | |
| 622 | .sh_name = try self.shstrtab.insert(gpa, ".rodata"), | |
| 623 | .sh_type = elf.SHT_PROGBITS, | |
| 624 | .sh_flags = elf.SHF_ALLOC, | |
| 625 | .sh_addr = phdr.p_vaddr, | |
| 626 | .sh_offset = phdr.p_offset, | |
| 627 | .sh_size = phdr.p_filesz, | |
| 628 | .sh_link = 0, | |
| 629 | .sh_info = 0, | |
| 630 | .sh_addralign = 1, | |
| 631 | .sh_entsize = 0, | |
| 632 | }, | |
| 633 | .phdr_index = self.phdr_load_ro_index.?, | |
| 701 | 634 | }); |
| 702 | try self.phdr_shdr_table.putNoClobber( | |
| 703 | self.base.allocator, | |
| 704 | self.phdr_load_ro_index.?, | |
| 705 | self.rodata_section_index.?, | |
| 706 | ); | |
| 707 | 635 | self.shdr_table_dirty = true; |
| 708 | 636 | } |
| 709 | 637 | |
| 710 | 638 | if (self.data_section_index == null) { |
| 711 | self.data_section_index = @intCast(u16, self.sections.items.len); | |
| 639 | self.data_section_index = @intCast(u16, self.sections.slice().len); | |
| 712 | 640 | const phdr = &self.program_headers.items[self.phdr_load_rw_index.?]; |
| 713 | 641 | |
| 714 | try self.sections.append(self.base.allocator, .{ | |
| 715 | .sh_name = try self.makeString(".data"), | |
| 716 | .sh_type = elf.SHT_PROGBITS, | |
| 717 | .sh_flags = elf.SHF_WRITE | elf.SHF_ALLOC, | |
| 718 | .sh_addr = phdr.p_vaddr, | |
| 719 | .sh_offset = phdr.p_offset, | |
| 720 | .sh_size = phdr.p_filesz, | |
| 721 | .sh_link = 0, | |
| 722 | .sh_info = 0, | |
| 723 | .sh_addralign = @as(u16, ptr_size), | |
| 724 | .sh_entsize = 0, | |
| 642 | try self.sections.append(gpa, .{ | |
| 643 | .shdr = .{ | |
| 644 | .sh_name = try self.shstrtab.insert(gpa, ".data"), | |
| 645 | .sh_type = elf.SHT_PROGBITS, | |
| 646 | .sh_flags = elf.SHF_WRITE | elf.SHF_ALLOC, | |
| 647 | .sh_addr = phdr.p_vaddr, | |
| 648 | .sh_offset = phdr.p_offset, | |
| 649 | .sh_size = phdr.p_filesz, | |
| 650 | .sh_link = 0, | |
| 651 | .sh_info = 0, | |
| 652 | .sh_addralign = @as(u16, ptr_size), | |
| 653 | .sh_entsize = 0, | |
| 654 | }, | |
| 655 | .phdr_index = self.phdr_load_rw_index.?, | |
| 725 | 656 | }); |
| 726 | try self.phdr_shdr_table.putNoClobber( | |
| 727 | self.base.allocator, | |
| 728 | self.phdr_load_rw_index.?, | |
| 729 | self.data_section_index.?, | |
| 730 | ); | |
| 731 | 657 | self.shdr_table_dirty = true; |
| 732 | 658 | } |
| 733 | 659 | |
| 734 | 660 | if (self.symtab_section_index == null) { |
| 735 | self.symtab_section_index = @intCast(u16, self.sections.items.len); | |
| 661 | self.symtab_section_index = @intCast(u16, self.sections.slice().len); | |
| 736 | 662 | const min_align: u16 = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym); |
| 737 | 663 | const each_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Sym) else @sizeOf(elf.Elf64_Sym); |
| 738 | 664 | const file_size = self.base.options.symbol_count_hint * each_size; |
| 739 | 665 | const off = self.findFreeSpace(file_size, min_align); |
| 740 | 666 | log.debug("found symtab free space 0x{x} to 0x{x}", .{ off, off + file_size }); |
| 741 | 667 | |
| 742 | try self.sections.append(self.base.allocator, .{ | |
| 743 | .sh_name = try self.makeString(".symtab"), | |
| 744 | .sh_type = elf.SHT_SYMTAB, | |
| 745 | .sh_flags = 0, | |
| 746 | .sh_addr = 0, | |
| 747 | .sh_offset = off, | |
| 748 | .sh_size = file_size, | |
| 749 | // The section header index of the associated string table. | |
| 750 | .sh_link = self.shstrtab_index.?, | |
| 751 | .sh_info = @intCast(u32, self.local_symbols.items.len), | |
| 752 | .sh_addralign = min_align, | |
| 753 | .sh_entsize = each_size, | |
| 668 | try self.sections.append(gpa, .{ | |
| 669 | .shdr = .{ | |
| 670 | .sh_name = try self.shstrtab.insert(gpa, ".symtab"), | |
| 671 | .sh_type = elf.SHT_SYMTAB, | |
| 672 | .sh_flags = 0, | |
| 673 | .sh_addr = 0, | |
| 674 | .sh_offset = off, | |
| 675 | .sh_size = file_size, | |
| 676 | // The section header index of the associated string table. | |
| 677 | .sh_link = self.shstrtab_index.?, | |
| 678 | .sh_info = @intCast(u32, self.local_symbols.items.len), | |
| 679 | .sh_addralign = min_align, | |
| 680 | .sh_entsize = each_size, | |
| 681 | }, | |
| 682 | .phdr_index = undefined, | |
| 754 | 683 | }); |
| 755 | 684 | self.shdr_table_dirty = true; |
| 756 | 685 | try self.writeSymbol(0); |
| ... | ... | @@ -758,27 +687,30 @@ pub fn populateMissingMetadata(self: *Elf) !void { |
| 758 | 687 | |
| 759 | 688 | if (self.dwarf) |*dw| { |
| 760 | 689 | if (self.debug_str_section_index == null) { |
| 761 | self.debug_str_section_index = @intCast(u16, self.sections.items.len); | |
| 762 | assert(dw.strtab.items.len == 0); | |
| 763 | try dw.strtab.append(self.base.allocator, 0); | |
| 764 | try self.sections.append(self.base.allocator, .{ | |
| 765 | .sh_name = try self.makeString(".debug_str"), | |
| 766 | .sh_type = elf.SHT_PROGBITS, | |
| 767 | .sh_flags = elf.SHF_MERGE | elf.SHF_STRINGS, | |
| 768 | .sh_addr = 0, | |
| 769 | .sh_offset = 0, | |
| 770 | .sh_size = 0, | |
| 771 | .sh_link = 0, | |
| 772 | .sh_info = 0, | |
| 773 | .sh_addralign = 1, | |
| 774 | .sh_entsize = 1, | |
| 690 | self.debug_str_section_index = @intCast(u16, self.sections.slice().len); | |
| 691 | assert(dw.strtab.buffer.items.len == 0); | |
| 692 | try dw.strtab.buffer.append(gpa, 0); | |
| 693 | try self.sections.append(gpa, .{ | |
| 694 | .shdr = .{ | |
| 695 | .sh_name = try self.shstrtab.insert(gpa, ".debug_str"), | |
| 696 | .sh_type = elf.SHT_PROGBITS, | |
| 697 | .sh_flags = elf.SHF_MERGE | elf.SHF_STRINGS, | |
| 698 | .sh_addr = 0, | |
| 699 | .sh_offset = 0, | |
| 700 | .sh_size = 0, | |
| 701 | .sh_link = 0, | |
| 702 | .sh_info = 0, | |
| 703 | .sh_addralign = 1, | |
| 704 | .sh_entsize = 1, | |
| 705 | }, | |
| 706 | .phdr_index = undefined, | |
| 775 | 707 | }); |
| 776 | 708 | self.debug_strtab_dirty = true; |
| 777 | 709 | self.shdr_table_dirty = true; |
| 778 | 710 | } |
| 779 | 711 | |
| 780 | 712 | if (self.debug_info_section_index == null) { |
| 781 | self.debug_info_section_index = @intCast(u16, self.sections.items.len); | |
| 713 | self.debug_info_section_index = @intCast(u16, self.sections.slice().len); | |
| 782 | 714 | |
| 783 | 715 | const file_size_hint = 200; |
| 784 | 716 | const p_align = 1; |
| ... | ... | @@ -787,24 +719,27 @@ pub fn populateMissingMetadata(self: *Elf) !void { |
| 787 | 719 | off, |
| 788 | 720 | off + file_size_hint, |
| 789 | 721 | }); |
| 790 | try self.sections.append(self.base.allocator, .{ | |
| 791 | .sh_name = try self.makeString(".debug_info"), | |
| 792 | .sh_type = elf.SHT_PROGBITS, | |
| 793 | .sh_flags = 0, | |
| 794 | .sh_addr = 0, | |
| 795 | .sh_offset = off, | |
| 796 | .sh_size = file_size_hint, | |
| 797 | .sh_link = 0, | |
| 798 | .sh_info = 0, | |
| 799 | .sh_addralign = p_align, | |
| 800 | .sh_entsize = 0, | |
| 722 | try self.sections.append(gpa, .{ | |
| 723 | .shdr = .{ | |
| 724 | .sh_name = try self.shstrtab.insert(gpa, ".debug_info"), | |
| 725 | .sh_type = elf.SHT_PROGBITS, | |
| 726 | .sh_flags = 0, | |
| 727 | .sh_addr = 0, | |
| 728 | .sh_offset = off, | |
| 729 | .sh_size = file_size_hint, | |
| 730 | .sh_link = 0, | |
| 731 | .sh_info = 0, | |
| 732 | .sh_addralign = p_align, | |
| 733 | .sh_entsize = 0, | |
| 734 | }, | |
| 735 | .phdr_index = undefined, | |
| 801 | 736 | }); |
| 802 | 737 | self.shdr_table_dirty = true; |
| 803 | 738 | self.debug_info_header_dirty = true; |
| 804 | 739 | } |
| 805 | 740 | |
| 806 | 741 | if (self.debug_abbrev_section_index == null) { |
| 807 | self.debug_abbrev_section_index = @intCast(u16, self.sections.items.len); | |
| 742 | self.debug_abbrev_section_index = @intCast(u16, self.sections.slice().len); | |
| 808 | 743 | |
| 809 | 744 | const file_size_hint = 128; |
| 810 | 745 | const p_align = 1; |
| ... | ... | @@ -813,24 +748,27 @@ pub fn populateMissingMetadata(self: *Elf) !void { |
| 813 | 748 | off, |
| 814 | 749 | off + file_size_hint, |
| 815 | 750 | }); |
| 816 | try self.sections.append(self.base.allocator, .{ | |
| 817 | .sh_name = try self.makeString(".debug_abbrev"), | |
| 818 | .sh_type = elf.SHT_PROGBITS, | |
| 819 | .sh_flags = 0, | |
| 820 | .sh_addr = 0, | |
| 821 | .sh_offset = off, | |
| 822 | .sh_size = file_size_hint, | |
| 823 | .sh_link = 0, | |
| 824 | .sh_info = 0, | |
| 825 | .sh_addralign = p_align, | |
| 826 | .sh_entsize = 0, | |
| 751 | try self.sections.append(gpa, .{ | |
| 752 | .shdr = .{ | |
| 753 | .sh_name = try self.shstrtab.insert(gpa, ".debug_abbrev"), | |
| 754 | .sh_type = elf.SHT_PROGBITS, | |
| 755 | .sh_flags = 0, | |
| 756 | .sh_addr = 0, | |
| 757 | .sh_offset = off, | |
| 758 | .sh_size = file_size_hint, | |
| 759 | .sh_link = 0, | |
| 760 | .sh_info = 0, | |
| 761 | .sh_addralign = p_align, | |
| 762 | .sh_entsize = 0, | |
| 763 | }, | |
| 764 | .phdr_index = undefined, | |
| 827 | 765 | }); |
| 828 | 766 | self.shdr_table_dirty = true; |
| 829 | 767 | self.debug_abbrev_section_dirty = true; |
| 830 | 768 | } |
| 831 | 769 | |
| 832 | 770 | if (self.debug_aranges_section_index == null) { |
| 833 | self.debug_aranges_section_index = @intCast(u16, self.sections.items.len); | |
| 771 | self.debug_aranges_section_index = @intCast(u16, self.sections.slice().len); | |
| 834 | 772 | |
| 835 | 773 | const file_size_hint = 160; |
| 836 | 774 | const p_align = 16; |
| ... | ... | @@ -839,24 +777,27 @@ pub fn populateMissingMetadata(self: *Elf) !void { |
| 839 | 777 | off, |
| 840 | 778 | off + file_size_hint, |
| 841 | 779 | }); |
| 842 | try self.sections.append(self.base.allocator, .{ | |
| 843 | .sh_name = try self.makeString(".debug_aranges"), | |
| 844 | .sh_type = elf.SHT_PROGBITS, | |
| 845 | .sh_flags = 0, | |
| 846 | .sh_addr = 0, | |
| 847 | .sh_offset = off, | |
| 848 | .sh_size = file_size_hint, | |
| 849 | .sh_link = 0, | |
| 850 | .sh_info = 0, | |
| 851 | .sh_addralign = p_align, | |
| 852 | .sh_entsize = 0, | |
| 780 | try self.sections.append(gpa, .{ | |
| 781 | .shdr = .{ | |
| 782 | .sh_name = try self.shstrtab.insert(gpa, ".debug_aranges"), | |
| 783 | .sh_type = elf.SHT_PROGBITS, | |
| 784 | .sh_flags = 0, | |
| 785 | .sh_addr = 0, | |
| 786 | .sh_offset = off, | |
| 787 | .sh_size = file_size_hint, | |
| 788 | .sh_link = 0, | |
| 789 | .sh_info = 0, | |
| 790 | .sh_addralign = p_align, | |
| 791 | .sh_entsize = 0, | |
| 792 | }, | |
| 793 | .phdr_index = undefined, | |
| 853 | 794 | }); |
| 854 | 795 | self.shdr_table_dirty = true; |
| 855 | 796 | self.debug_aranges_section_dirty = true; |
| 856 | 797 | } |
| 857 | 798 | |
| 858 | 799 | if (self.debug_line_section_index == null) { |
| 859 | self.debug_line_section_index = @intCast(u16, self.sections.items.len); | |
| 800 | self.debug_line_section_index = @intCast(u16, self.sections.slice().len); | |
| 860 | 801 | |
| 861 | 802 | const file_size_hint = 250; |
| 862 | 803 | const p_align = 1; |
| ... | ... | @@ -865,17 +806,20 @@ pub fn populateMissingMetadata(self: *Elf) !void { |
| 865 | 806 | off, |
| 866 | 807 | off + file_size_hint, |
| 867 | 808 | }); |
| 868 | try self.sections.append(self.base.allocator, .{ | |
| 869 | .sh_name = try self.makeString(".debug_line"), | |
| 870 | .sh_type = elf.SHT_PROGBITS, | |
| 871 | .sh_flags = 0, | |
| 872 | .sh_addr = 0, | |
| 873 | .sh_offset = off, | |
| 874 | .sh_size = file_size_hint, | |
| 875 | .sh_link = 0, | |
| 876 | .sh_info = 0, | |
| 877 | .sh_addralign = p_align, | |
| 878 | .sh_entsize = 0, | |
| 809 | try self.sections.append(gpa, .{ | |
| 810 | .shdr = .{ | |
| 811 | .sh_name = try self.shstrtab.insert(gpa, ".debug_line"), | |
| 812 | .sh_type = elf.SHT_PROGBITS, | |
| 813 | .sh_flags = 0, | |
| 814 | .sh_addr = 0, | |
| 815 | .sh_offset = off, | |
| 816 | .sh_size = file_size_hint, | |
| 817 | .sh_link = 0, | |
| 818 | .sh_info = 0, | |
| 819 | .sh_addralign = p_align, | |
| 820 | .sh_entsize = 0, | |
| 821 | }, | |
| 822 | .phdr_index = undefined, | |
| 879 | 823 | }); |
| 880 | 824 | self.shdr_table_dirty = true; |
| 881 | 825 | self.debug_line_header_dirty = true; |
| ... | ... | @@ -891,7 +835,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { |
| 891 | 835 | .p64 => @alignOf(elf.Elf64_Shdr), |
| 892 | 836 | }; |
| 893 | 837 | if (self.shdr_table_offset == null) { |
| 894 | self.shdr_table_offset = self.findFreeSpace(self.sections.items.len * shsize, shalign); | |
| 838 | self.shdr_table_offset = self.findFreeSpace(self.sections.slice().len * shsize, shalign); | |
| 895 | 839 | self.shdr_table_dirty = true; |
| 896 | 840 | } |
| 897 | 841 | |
| ... | ... | @@ -922,7 +866,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { |
| 922 | 866 | // offset + it's filesize. |
| 923 | 867 | var max_file_offset: u64 = 0; |
| 924 | 868 | |
| 925 | for (self.sections.items) |shdr| { | |
| 869 | for (self.sections.items(.shdr)) |shdr| { | |
| 926 | 870 | if (shdr.sh_offset + shdr.sh_size > max_file_offset) { |
| 927 | 871 | max_file_offset = shdr.sh_offset + shdr.sh_size; |
| 928 | 872 | } |
| ... | ... | @@ -932,24 +876,27 @@ pub fn populateMissingMetadata(self: *Elf) !void { |
| 932 | 876 | } |
| 933 | 877 | } |
| 934 | 878 | |
| 935 | fn growAllocSection(self: *Elf, shdr_index: u16, phdr_index: u16, needed_size: u64) !void { | |
| 879 | fn growAllocSection(self: *Elf, shdr_index: u16, needed_size: u64) !void { | |
| 936 | 880 | // TODO Also detect virtual address collisions. |
| 937 | const shdr = &self.sections.items[shdr_index]; | |
| 881 | const shdr = &self.sections.items(.shdr)[shdr_index]; | |
| 882 | const phdr_index = self.sections.items(.phdr_index)[shdr_index]; | |
| 938 | 883 | const phdr = &self.program_headers.items[phdr_index]; |
| 884 | const maybe_last_atom_index = self.sections.items(.last_atom_index)[shdr_index]; | |
| 939 | 885 | |
| 940 | 886 | if (needed_size > self.allocatedSize(shdr.sh_offset)) { |
| 941 | 887 | // Must move the entire section. |
| 942 | 888 | const new_offset = self.findFreeSpace(needed_size, self.page_size); |
| 943 | const existing_size = if (self.atoms.get(phdr_index)) |last| blk: { | |
| 944 | const sym = self.local_symbols.items[last.local_sym_index]; | |
| 889 | const existing_size = if (maybe_last_atom_index) |last_atom_index| blk: { | |
| 890 | const last = self.getAtom(last_atom_index); | |
| 891 | const sym = last.getSymbol(self); | |
| 945 | 892 | break :blk (sym.st_value + sym.st_size) - phdr.p_vaddr; |
| 946 | 893 | } else if (shdr_index == self.got_section_index.?) blk: { |
| 947 | 894 | break :blk shdr.sh_size; |
| 948 | 895 | } else 0; |
| 949 | 896 | shdr.sh_size = 0; |
| 950 | 897 | |
| 951 | log.debug("new '{s}' file offset 0x{x} to 0x{x}", .{ | |
| 952 | self.getString(shdr.sh_name), | |
| 898 | log.debug("new '{?s}' file offset 0x{x} to 0x{x}", .{ | |
| 899 | self.shstrtab.get(shdr.sh_name), | |
| 953 | 900 | new_offset, |
| 954 | 901 | new_offset + existing_size, |
| 955 | 902 | }); |
| ... | ... | @@ -975,7 +922,7 @@ pub fn growNonAllocSection( |
| 975 | 922 | min_alignment: u32, |
| 976 | 923 | requires_file_copy: bool, |
| 977 | 924 | ) !void { |
| 978 | const shdr = &self.sections.items[shdr_index]; | |
| 925 | const shdr = &self.sections.items(.shdr)[shdr_index]; | |
| 979 | 926 | |
| 980 | 927 | if (needed_size > self.allocatedSize(shdr.sh_offset)) { |
| 981 | 928 | const existing_size = if (self.symtab_section_index.? == shdr_index) blk: { |
| ... | ... | @@ -988,7 +935,7 @@ pub fn growNonAllocSection( |
| 988 | 935 | shdr.sh_size = 0; |
| 989 | 936 | // Move all the symbols to a new file location. |
| 990 | 937 | const new_offset = self.findFreeSpace(needed_size, min_alignment); |
| 991 | log.debug("moving '{s}' from 0x{x} to 0x{x}", .{ self.getString(shdr.sh_name), shdr.sh_offset, new_offset }); | |
| 938 | log.debug("moving '{?s}' from 0x{x} to 0x{x}", .{ self.shstrtab.get(shdr.sh_name), shdr.sh_offset, new_offset }); | |
| 992 | 939 | |
| 993 | 940 | if (requires_file_copy) { |
| 994 | 941 | const amt = try self.base.file.?.copyRangeAll( |
| ... | ... | @@ -1059,6 +1006,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node |
| 1059 | 1006 | } |
| 1060 | 1007 | } |
| 1061 | 1008 | |
| 1009 | const gpa = self.base.allocator; | |
| 1062 | 1010 | var sub_prog_node = prog_node.start("ELF Flush", 0); |
| 1063 | 1011 | sub_prog_node.activate(); |
| 1064 | 1012 | defer sub_prog_node.end(); |
| ... | ... | @@ -1077,12 +1025,13 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node |
| 1077 | 1025 | { |
| 1078 | 1026 | var it = self.relocs.iterator(); |
| 1079 | 1027 | while (it.next()) |entry| { |
| 1080 | const atom = entry.key_ptr.*; | |
| 1028 | const atom_index = entry.key_ptr.*; | |
| 1081 | 1029 | const relocs = entry.value_ptr.*; |
| 1082 | const source_sym = self.local_symbols.items[atom.local_sym_index]; | |
| 1083 | const source_shdr = self.sections.items[source_sym.st_shndx]; | |
| 1030 | const atom = self.getAtom(atom_index); | |
| 1031 | const source_sym = atom.getSymbol(self); | |
| 1032 | const source_shdr = self.sections.items(.shdr)[source_sym.st_shndx]; | |
| 1084 | 1033 | |
| 1085 | log.debug("relocating '{s}'", .{self.getString(source_sym.st_name)}); | |
| 1034 | log.debug("relocating '{?s}'", .{self.shstrtab.get(source_sym.st_name)}); | |
| 1086 | 1035 | |
| 1087 | 1036 | for (relocs.items) |*reloc| { |
| 1088 | 1037 | const target_sym = self.local_symbols.items[reloc.target]; |
| ... | ... | @@ -1093,10 +1042,10 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node |
| 1093 | 1042 | const section_offset = (source_sym.st_value + reloc.offset) - source_shdr.sh_addr; |
| 1094 | 1043 | const file_offset = source_shdr.sh_offset + section_offset; |
| 1095 | 1044 | |
| 1096 | log.debug(" ({x}: [() => 0x{x}] ({s}))", .{ | |
| 1045 | log.debug(" ({x}: [() => 0x{x}] ({?s}))", .{ | |
| 1097 | 1046 | reloc.offset, |
| 1098 | 1047 | target_vaddr, |
| 1099 | self.getString(target_sym.st_name), | |
| 1048 | self.shstrtab.get(target_sym.st_name), | |
| 1100 | 1049 | }); |
| 1101 | 1050 | |
| 1102 | 1051 | switch (self.ptr_width) { |
| ... | ... | @@ -1174,8 +1123,8 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node |
| 1174 | 1123 | |
| 1175 | 1124 | switch (self.ptr_width) { |
| 1176 | 1125 | .p32 => { |
| 1177 | const buf = try self.base.allocator.alloc(elf.Elf32_Phdr, self.program_headers.items.len); | |
| 1178 | defer self.base.allocator.free(buf); | |
| 1126 | const buf = try gpa.alloc(elf.Elf32_Phdr, self.program_headers.items.len); | |
| 1127 | defer gpa.free(buf); | |
| 1179 | 1128 | |
| 1180 | 1129 | for (buf) |*phdr, i| { |
| 1181 | 1130 | phdr.* = progHeaderTo32(self.program_headers.items[i]); |
| ... | ... | @@ -1186,8 +1135,8 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node |
| 1186 | 1135 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?); |
| 1187 | 1136 | }, |
| 1188 | 1137 | .p64 => { |
| 1189 | const buf = try self.base.allocator.alloc(elf.Elf64_Phdr, self.program_headers.items.len); | |
| 1190 | defer self.base.allocator.free(buf); | |
| 1138 | const buf = try gpa.alloc(elf.Elf64_Phdr, self.program_headers.items.len); | |
| 1139 | defer gpa.free(buf); | |
| 1191 | 1140 | |
| 1192 | 1141 | for (buf) |*phdr, i| { |
| 1193 | 1142 | phdr.* = self.program_headers.items[i]; |
| ... | ... | @@ -1203,20 +1152,20 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node |
| 1203 | 1152 | |
| 1204 | 1153 | { |
| 1205 | 1154 | const shdr_index = self.shstrtab_index.?; |
| 1206 | if (self.shstrtab_dirty or self.shstrtab.items.len != self.sections.items[shdr_index].sh_size) { | |
| 1207 | try self.growNonAllocSection(shdr_index, self.shstrtab.items.len, 1, false); | |
| 1208 | const shstrtab_sect = self.sections.items[shdr_index]; | |
| 1209 | try self.base.file.?.pwriteAll(self.shstrtab.items, shstrtab_sect.sh_offset); | |
| 1155 | if (self.shstrtab_dirty or self.shstrtab.buffer.items.len != self.sections.items(.shdr)[shdr_index].sh_size) { | |
| 1156 | try self.growNonAllocSection(shdr_index, self.shstrtab.buffer.items.len, 1, false); | |
| 1157 | const shstrtab_sect = self.sections.items(.shdr)[shdr_index]; | |
| 1158 | try self.base.file.?.pwriteAll(self.shstrtab.buffer.items, shstrtab_sect.sh_offset); | |
| 1210 | 1159 | self.shstrtab_dirty = false; |
| 1211 | 1160 | } |
| 1212 | 1161 | } |
| 1213 | 1162 | |
| 1214 | 1163 | if (self.dwarf) |dwarf| { |
| 1215 | 1164 | const shdr_index = self.debug_str_section_index.?; |
| 1216 | if (self.debug_strtab_dirty or dwarf.strtab.items.len != self.sections.items[shdr_index].sh_size) { | |
| 1217 | try self.growNonAllocSection(shdr_index, dwarf.strtab.items.len, 1, false); | |
| 1218 | const debug_strtab_sect = self.sections.items[shdr_index]; | |
| 1219 | try self.base.file.?.pwriteAll(dwarf.strtab.items, debug_strtab_sect.sh_offset); | |
| 1165 | if (self.debug_strtab_dirty or dwarf.strtab.buffer.items.len != self.sections.items(.shdr)[shdr_index].sh_size) { | |
| 1166 | try self.growNonAllocSection(shdr_index, dwarf.strtab.buffer.items.len, 1, false); | |
| 1167 | const debug_strtab_sect = self.sections.items(.shdr)[shdr_index]; | |
| 1168 | try self.base.file.?.pwriteAll(dwarf.strtab.buffer.items, debug_strtab_sect.sh_offset); | |
| 1220 | 1169 | self.debug_strtab_dirty = false; |
| 1221 | 1170 | } |
| 1222 | 1171 | } |
| ... | ... | @@ -1231,7 +1180,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node |
| 1231 | 1180 | .p64 => @alignOf(elf.Elf64_Shdr), |
| 1232 | 1181 | }; |
| 1233 | 1182 | const allocated_size = self.allocatedSize(self.shdr_table_offset.?); |
| 1234 | const needed_size = self.sections.items.len * shsize; | |
| 1183 | const needed_size = self.sections.slice().len * shsize; | |
| 1235 | 1184 | |
| 1236 | 1185 | if (needed_size > allocated_size) { |
| 1237 | 1186 | self.shdr_table_offset = null; // free the space |
| ... | ... | @@ -1240,12 +1189,13 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node |
| 1240 | 1189 | |
| 1241 | 1190 | switch (self.ptr_width) { |
| 1242 | 1191 | .p32 => { |
| 1243 | const buf = try self.base.allocator.alloc(elf.Elf32_Shdr, self.sections.items.len); | |
| 1244 | defer self.base.allocator.free(buf); | |
| 1192 | const slice = self.sections.slice(); | |
| 1193 | const buf = try gpa.alloc(elf.Elf32_Shdr, slice.len); | |
| 1194 | defer gpa.free(buf); | |
| 1245 | 1195 | |
| 1246 | 1196 | for (buf) |*shdr, i| { |
| 1247 | shdr.* = sectHeaderTo32(self.sections.items[i]); | |
| 1248 | log.debug("writing section {s}: {}", .{ self.getString(shdr.sh_name), shdr.* }); | |
| 1197 | shdr.* = sectHeaderTo32(slice.items(.shdr)[i]); | |
| 1198 | log.debug("writing section {?s}: {}", .{ self.shstrtab.get(shdr.sh_name), shdr.* }); | |
| 1249 | 1199 | if (foreign_endian) { |
| 1250 | 1200 | mem.byteSwapAllFields(elf.Elf32_Shdr, shdr); |
| 1251 | 1201 | } |
| ... | ... | @@ -1253,12 +1203,13 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node |
| 1253 | 1203 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?); |
| 1254 | 1204 | }, |
| 1255 | 1205 | .p64 => { |
| 1256 | const buf = try self.base.allocator.alloc(elf.Elf64_Shdr, self.sections.items.len); | |
| 1257 | defer self.base.allocator.free(buf); | |
| 1206 | const slice = self.sections.slice(); | |
| 1207 | const buf = try gpa.alloc(elf.Elf64_Shdr, slice.len); | |
| 1208 | defer gpa.free(buf); | |
| 1258 | 1209 | |
| 1259 | 1210 | for (buf) |*shdr, i| { |
| 1260 | shdr.* = self.sections.items[i]; | |
| 1261 | log.debug("writing section {s}: {}", .{ self.getString(shdr.sh_name), shdr.* }); | |
| 1211 | shdr.* = slice.items(.shdr)[i]; | |
| 1212 | log.debug("writing section {?s}: {}", .{ self.shstrtab.get(shdr.sh_name), shdr.* }); | |
| 1262 | 1213 | if (foreign_endian) { |
| 1263 | 1214 | mem.byteSwapAllFields(elf.Elf64_Shdr, shdr); |
| 1264 | 1215 | } |
| ... | ... | @@ -2069,7 +2020,7 @@ fn writeElfHeader(self: *Elf) !void { |
| 2069 | 2020 | mem.writeInt(u16, hdr_buf[index..][0..2], e_shentsize, endian); |
| 2070 | 2021 | index += 2; |
| 2071 | 2022 | |
| 2072 | const e_shnum = @intCast(u16, self.sections.items.len); | |
| 2023 | const e_shnum = @intCast(u16, self.sections.slice().len); | |
| 2073 | 2024 | mem.writeInt(u16, hdr_buf[index..][0..2], e_shnum, endian); |
| 2074 | 2025 | index += 2; |
| 2075 | 2026 | |
| ... | ... | @@ -2081,113 +2032,145 @@ fn writeElfHeader(self: *Elf) !void { |
| 2081 | 2032 | try self.base.file.?.pwriteAll(hdr_buf[0..index], 0); |
| 2082 | 2033 | } |
| 2083 | 2034 | |
| 2084 | fn freeTextBlock(self: *Elf, text_block: *TextBlock, phdr_index: u16) void { | |
| 2085 | const local_sym = self.local_symbols.items[text_block.local_sym_index]; | |
| 2086 | const name_str_index = local_sym.st_name; | |
| 2087 | const name = self.getString(name_str_index); | |
| 2088 | log.debug("freeTextBlock {*} ({s})", .{ text_block, name }); | |
| 2035 | fn freeAtom(self: *Elf, atom_index: Atom.Index) void { | |
| 2036 | const atom = self.getAtom(atom_index); | |
| 2037 | log.debug("freeAtom {d} ({s})", .{ atom_index, atom.getName(self) }); | |
| 2089 | 2038 | |
| 2090 | const free_list = self.atom_free_lists.getPtr(phdr_index).?; | |
| 2039 | Atom.freeRelocations(self, atom_index); | |
| 2040 | ||
| 2041 | const gpa = self.base.allocator; | |
| 2042 | const shndx = atom.getSymbol(self).st_shndx; | |
| 2043 | const free_list = &self.sections.items(.free_list)[shndx]; | |
| 2091 | 2044 | var already_have_free_list_node = false; |
| 2092 | 2045 | { |
| 2093 | 2046 | var i: usize = 0; |
| 2094 | 2047 | // TODO turn free_list into a hash map |
| 2095 | 2048 | while (i < free_list.items.len) { |
| 2096 | if (free_list.items[i] == text_block) { | |
| 2049 | if (free_list.items[i] == atom_index) { | |
| 2097 | 2050 | _ = free_list.swapRemove(i); |
| 2098 | 2051 | continue; |
| 2099 | 2052 | } |
| 2100 | if (free_list.items[i] == text_block.prev) { | |
| 2053 | if (free_list.items[i] == atom.prev_index) { | |
| 2101 | 2054 | already_have_free_list_node = true; |
| 2102 | 2055 | } |
| 2103 | 2056 | i += 1; |
| 2104 | 2057 | } |
| 2105 | 2058 | } |
| 2106 | 2059 | |
| 2107 | if (self.atoms.getPtr(phdr_index)) |last_block| { | |
| 2108 | if (last_block.* == text_block) { | |
| 2109 | if (text_block.prev) |prev| { | |
| 2060 | const maybe_last_atom_index = &self.sections.items(.last_atom_index)[shndx]; | |
| 2061 | if (maybe_last_atom_index.*) |last_atom_index| { | |
| 2062 | if (last_atom_index == atom_index) { | |
| 2063 | if (atom.prev_index) |prev_index| { | |
| 2110 | 2064 | // TODO shrink the section size here |
| 2111 | last_block.* = prev; | |
| 2065 | maybe_last_atom_index.* = prev_index; | |
| 2112 | 2066 | } else { |
| 2113 | _ = self.atoms.fetchRemove(phdr_index); | |
| 2067 | maybe_last_atom_index.* = null; | |
| 2114 | 2068 | } |
| 2115 | 2069 | } |
| 2116 | 2070 | } |
| 2117 | 2071 | |
| 2118 | if (text_block.prev) |prev| { | |
| 2119 | prev.next = text_block.next; | |
| 2072 | if (atom.prev_index) |prev_index| { | |
| 2073 | const prev = self.getAtomPtr(prev_index); | |
| 2074 | prev.next_index = atom.next_index; | |
| 2120 | 2075 | |
| 2121 | if (!already_have_free_list_node and prev.freeListEligible(self.*)) { | |
| 2076 | if (!already_have_free_list_node and prev.*.freeListEligible(self)) { | |
| 2122 | 2077 | // The free list is heuristics, it doesn't have to be perfect, so we can |
| 2123 | 2078 | // ignore the OOM here. |
| 2124 | free_list.append(self.base.allocator, prev) catch {}; | |
| 2079 | free_list.append(gpa, prev_index) catch {}; | |
| 2125 | 2080 | } |
| 2126 | 2081 | } else { |
| 2127 | text_block.prev = null; | |
| 2082 | self.getAtomPtr(atom_index).prev_index = null; | |
| 2128 | 2083 | } |
| 2129 | 2084 | |
| 2130 | if (text_block.next) |next| { | |
| 2131 | next.prev = text_block.prev; | |
| 2085 | if (atom.next_index) |next_index| { | |
| 2086 | self.getAtomPtr(next_index).prev_index = atom.prev_index; | |
| 2132 | 2087 | } else { |
| 2133 | text_block.next = null; | |
| 2088 | self.getAtomPtr(atom_index).next_index = null; | |
| 2134 | 2089 | } |
| 2135 | 2090 | |
| 2136 | if (self.dwarf) |*dw| { | |
| 2137 | dw.freeAtom(&text_block.dbg_info_atom); | |
| 2138 | } | |
| 2091 | // Appending to free lists is allowed to fail because the free lists are heuristics based anyway. | |
| 2092 | const local_sym_index = atom.getSymbolIndex().?; | |
| 2093 | ||
| 2094 | self.local_symbol_free_list.append(gpa, local_sym_index) catch {}; | |
| 2095 | self.local_symbols.items[local_sym_index].st_info = 0; | |
| 2096 | self.local_symbols.items[local_sym_index].st_shndx = 0; | |
| 2097 | _ = self.atom_by_index_table.remove(local_sym_index); | |
| 2098 | self.getAtomPtr(atom_index).local_sym_index = 0; | |
| 2099 | ||
| 2100 | self.offset_table_free_list.append(self.base.allocator, atom.offset_table_index) catch {}; | |
| 2139 | 2101 | } |
| 2140 | 2102 | |
| 2141 | fn shrinkTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, phdr_index: u16) void { | |
| 2103 | fn shrinkAtom(self: *Elf, atom_index: Atom.Index, new_block_size: u64) void { | |
| 2142 | 2104 | _ = self; |
| 2143 | _ = text_block; | |
| 2105 | _ = atom_index; | |
| 2144 | 2106 | _ = new_block_size; |
| 2145 | _ = phdr_index; | |
| 2146 | 2107 | } |
| 2147 | 2108 | |
| 2148 | fn growTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, alignment: u64, phdr_index: u16) !u64 { | |
| 2149 | const sym = self.local_symbols.items[text_block.local_sym_index]; | |
| 2109 | fn growAtom(self: *Elf, atom_index: Atom.Index, new_block_size: u64, alignment: u64) !u64 { | |
| 2110 | const atom = self.getAtom(atom_index); | |
| 2111 | const sym = atom.getSymbol(self); | |
| 2150 | 2112 | const align_ok = mem.alignBackwardGeneric(u64, sym.st_value, alignment) == sym.st_value; |
| 2151 | const need_realloc = !align_ok or new_block_size > text_block.capacity(self.*); | |
| 2113 | const need_realloc = !align_ok or new_block_size > atom.capacity(self); | |
| 2152 | 2114 | if (!need_realloc) return sym.st_value; |
| 2153 | return self.allocateTextBlock(text_block, new_block_size, alignment, phdr_index); | |
| 2115 | return self.allocateAtom(atom_index, new_block_size, alignment); | |
| 2154 | 2116 | } |
| 2155 | 2117 | |
| 2156 | fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, alignment: u64, phdr_index: u16) !u64 { | |
| 2157 | const shdr_index = self.phdr_shdr_table.get(phdr_index).?; | |
| 2118 | pub fn createAtom(self: *Elf) !Atom.Index { | |
| 2119 | const gpa = self.base.allocator; | |
| 2120 | const atom_index = @intCast(Atom.Index, self.atoms.items.len); | |
| 2121 | const atom = try self.atoms.addOne(gpa); | |
| 2122 | const local_sym_index = try self.allocateLocalSymbol(); | |
| 2123 | const offset_table_index = try self.allocateGotOffset(); | |
| 2124 | try self.atom_by_index_table.putNoClobber(gpa, local_sym_index, atom_index); | |
| 2125 | atom.* = .{ | |
| 2126 | .local_sym_index = local_sym_index, | |
| 2127 | .offset_table_index = offset_table_index, | |
| 2128 | .prev_index = null, | |
| 2129 | .next_index = null, | |
| 2130 | }; | |
| 2131 | log.debug("creating ATOM(%{d}) at index {d}", .{ local_sym_index, atom_index }); | |
| 2132 | return atom_index; | |
| 2133 | } | |
| 2134 | ||
| 2135 | fn allocateAtom(self: *Elf, atom_index: Atom.Index, new_block_size: u64, alignment: u64) !u64 { | |
| 2136 | const atom = self.getAtom(atom_index); | |
| 2137 | const sym = atom.getSymbol(self); | |
| 2138 | const phdr_index = self.sections.items(.phdr_index)[sym.st_shndx]; | |
| 2158 | 2139 | const phdr = &self.program_headers.items[phdr_index]; |
| 2159 | const shdr = &self.sections.items[shdr_index]; | |
| 2160 | const new_block_ideal_capacity = padToIdeal(new_block_size); | |
| 2140 | const shdr = &self.sections.items(.shdr)[sym.st_shndx]; | |
| 2141 | const free_list = &self.sections.items(.free_list)[sym.st_shndx]; | |
| 2142 | const maybe_last_atom_index = &self.sections.items(.last_atom_index)[sym.st_shndx]; | |
| 2143 | const new_atom_ideal_capacity = padToIdeal(new_block_size); | |
| 2161 | 2144 | |
| 2162 | // We use these to indicate our intention to update metadata, placing the new block, | |
| 2145 | // We use these to indicate our intention to update metadata, placing the new atom, | |
| 2163 | 2146 | // and possibly removing a free list node. |
| 2164 | 2147 | // It would be simpler to do it inside the for loop below, but that would cause a |
| 2165 | 2148 | // problem if an error was returned later in the function. So this action |
| 2166 | 2149 | // is actually carried out at the end of the function, when errors are no longer possible. |
| 2167 | var block_placement: ?*TextBlock = null; | |
| 2150 | var atom_placement: ?Atom.Index = null; | |
| 2168 | 2151 | var free_list_removal: ?usize = null; |
| 2169 | var free_list = self.atom_free_lists.get(phdr_index).?; | |
| 2170 | 2152 | |
| 2171 | 2153 | // First we look for an appropriately sized free list node. |
| 2172 | 2154 | // The list is unordered. We'll just take the first thing that works. |
| 2173 | 2155 | const vaddr = blk: { |
| 2174 | 2156 | var i: usize = 0; |
| 2175 | 2157 | while (i < free_list.items.len) { |
| 2176 | const big_block = free_list.items[i]; | |
| 2177 | // We now have a pointer to a live text block that has too much capacity. | |
| 2178 | // Is it enough that we could fit this new text block? | |
| 2179 | const sym = self.local_symbols.items[big_block.local_sym_index]; | |
| 2180 | const capacity = big_block.capacity(self.*); | |
| 2158 | const big_atom_index = free_list.items[i]; | |
| 2159 | const big_atom = self.getAtom(big_atom_index); | |
| 2160 | // We now have a pointer to a live atom that has too much capacity. | |
| 2161 | // Is it enough that we could fit this new atom? | |
| 2162 | const big_atom_sym = big_atom.getSymbol(self); | |
| 2163 | const capacity = big_atom.capacity(self); | |
| 2181 | 2164 | const ideal_capacity = padToIdeal(capacity); |
| 2182 | const ideal_capacity_end_vaddr = std.math.add(u64, sym.st_value, ideal_capacity) catch ideal_capacity; | |
| 2183 | const capacity_end_vaddr = sym.st_value + capacity; | |
| 2184 | const new_start_vaddr_unaligned = capacity_end_vaddr - new_block_ideal_capacity; | |
| 2165 | const ideal_capacity_end_vaddr = std.math.add(u64, big_atom_sym.st_value, ideal_capacity) catch ideal_capacity; | |
| 2166 | const capacity_end_vaddr = big_atom_sym.st_value + capacity; | |
| 2167 | const new_start_vaddr_unaligned = capacity_end_vaddr - new_atom_ideal_capacity; | |
| 2185 | 2168 | const new_start_vaddr = mem.alignBackwardGeneric(u64, new_start_vaddr_unaligned, alignment); |
| 2186 | 2169 | if (new_start_vaddr < ideal_capacity_end_vaddr) { |
| 2187 | 2170 | // Additional bookkeeping here to notice if this free list node |
| 2188 | 2171 | // should be deleted because the block that it points to has grown to take up |
| 2189 | 2172 | // more of the extra capacity. |
| 2190 | if (!big_block.freeListEligible(self.*)) { | |
| 2173 | if (!big_atom.freeListEligible(self)) { | |
| 2191 | 2174 | _ = free_list.swapRemove(i); |
| 2192 | 2175 | } else { |
| 2193 | 2176 | i += 1; |
| ... | ... | @@ -2201,29 +2184,33 @@ fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, al |
| 2201 | 2184 | const keep_free_list_node = remaining_capacity >= min_text_capacity; |
| 2202 | 2185 | |
| 2203 | 2186 | // Set up the metadata to be updated, after errors are no longer possible. |
| 2204 | block_placement = big_block; | |
| 2187 | atom_placement = big_atom_index; | |
| 2205 | 2188 | if (!keep_free_list_node) { |
| 2206 | 2189 | free_list_removal = i; |
| 2207 | 2190 | } |
| 2208 | 2191 | break :blk new_start_vaddr; |
| 2209 | } else if (self.atoms.get(phdr_index)) |last| { | |
| 2210 | const sym = self.local_symbols.items[last.local_sym_index]; | |
| 2211 | const ideal_capacity = padToIdeal(sym.st_size); | |
| 2212 | const ideal_capacity_end_vaddr = sym.st_value + ideal_capacity; | |
| 2192 | } else if (maybe_last_atom_index.*) |last_index| { | |
| 2193 | const last = self.getAtom(last_index); | |
| 2194 | const last_sym = last.getSymbol(self); | |
| 2195 | const ideal_capacity = padToIdeal(last_sym.st_size); | |
| 2196 | const ideal_capacity_end_vaddr = last_sym.st_value + ideal_capacity; | |
| 2213 | 2197 | const new_start_vaddr = mem.alignForwardGeneric(u64, ideal_capacity_end_vaddr, alignment); |
| 2214 | 2198 | // Set up the metadata to be updated, after errors are no longer possible. |
| 2215 | block_placement = last; | |
| 2199 | atom_placement = last_index; | |
| 2216 | 2200 | break :blk new_start_vaddr; |
| 2217 | 2201 | } else { |
| 2218 | 2202 | break :blk phdr.p_vaddr; |
| 2219 | 2203 | } |
| 2220 | 2204 | }; |
| 2221 | 2205 | |
| 2222 | const expand_text_section = block_placement == null or block_placement.?.next == null; | |
| 2223 | if (expand_text_section) { | |
| 2206 | const expand_section = if (atom_placement) |placement_index| | |
| 2207 | self.getAtom(placement_index).next_index == null | |
| 2208 | else | |
| 2209 | true; | |
| 2210 | if (expand_section) { | |
| 2224 | 2211 | const needed_size = (vaddr + new_block_size) - phdr.p_vaddr; |
| 2225 | try self.growAllocSection(shdr_index, phdr_index, needed_size); | |
| 2226 | _ = try self.atoms.put(self.base.allocator, phdr_index, text_block); | |
| 2212 | try self.growAllocSection(sym.st_shndx, needed_size); | |
| 2213 | maybe_last_atom_index.* = atom_index; | |
| 2227 | 2214 | |
| 2228 | 2215 | if (self.dwarf) |_| { |
| 2229 | 2216 | // The .debug_info section has `low_pc` and `high_pc` values which is the virtual address |
| ... | ... | @@ -2238,23 +2225,28 @@ fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, al |
| 2238 | 2225 | } |
| 2239 | 2226 | shdr.sh_addralign = math.max(shdr.sh_addralign, alignment); |
| 2240 | 2227 | |
| 2241 | // This function can also reallocate a text block. | |
| 2228 | // This function can also reallocate an atom. | |
| 2242 | 2229 | // In this case we need to "unplug" it from its previous location before |
| 2243 | 2230 | // plugging it in to its new location. |
| 2244 | if (text_block.prev) |prev| { | |
| 2245 | prev.next = text_block.next; | |
| 2231 | if (atom.prev_index) |prev_index| { | |
| 2232 | const prev = self.getAtomPtr(prev_index); | |
| 2233 | prev.next_index = atom.next_index; | |
| 2246 | 2234 | } |
| 2247 | if (text_block.next) |next| { | |
| 2248 | next.prev = text_block.prev; | |
| 2235 | if (atom.next_index) |next_index| { | |
| 2236 | const next = self.getAtomPtr(next_index); | |
| 2237 | next.prev_index = atom.prev_index; | |
| 2249 | 2238 | } |
| 2250 | 2239 | |
| 2251 | if (block_placement) |big_block| { | |
| 2252 | text_block.prev = big_block; | |
| 2253 | text_block.next = big_block.next; | |
| 2254 | big_block.next = text_block; | |
| 2240 | if (atom_placement) |big_atom_index| { | |
| 2241 | const big_atom = self.getAtomPtr(big_atom_index); | |
| 2242 | const atom_ptr = self.getAtomPtr(atom_index); | |
| 2243 | atom_ptr.prev_index = big_atom_index; | |
| 2244 | atom_ptr.next_index = big_atom.next_index; | |
| 2245 | big_atom.next_index = atom_index; | |
| 2255 | 2246 | } else { |
| 2256 | text_block.prev = null; | |
| 2257 | text_block.next = null; | |
| 2247 | const atom_ptr = self.getAtomPtr(atom_index); | |
| 2248 | atom_ptr.prev_index = null; | |
| 2249 | atom_ptr.next_index = null; | |
| 2258 | 2250 | } |
| 2259 | 2251 | if (free_list_removal) |i| { |
| 2260 | 2252 | _ = free_list.swapRemove(i); |
| ... | ... | @@ -2262,7 +2254,7 @@ fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, al |
| 2262 | 2254 | return vaddr; |
| 2263 | 2255 | } |
| 2264 | 2256 | |
| 2265 | fn allocateLocalSymbol(self: *Elf) !u32 { | |
| 2257 | pub fn allocateLocalSymbol(self: *Elf) !u32 { | |
| 2266 | 2258 | try self.local_symbols.ensureUnusedCapacity(self.base.allocator, 1); |
| 2267 | 2259 | |
| 2268 | 2260 | const index = blk: { |
| ... | ... | @@ -2289,40 +2281,30 @@ fn allocateLocalSymbol(self: *Elf) !u32 { |
| 2289 | 2281 | return index; |
| 2290 | 2282 | } |
| 2291 | 2283 | |
| 2292 | pub fn allocateDeclIndexes(self: *Elf, decl_index: Module.Decl.Index) !void { | |
| 2293 | if (self.llvm_object) |_| return; | |
| 2294 | ||
| 2295 | const mod = self.base.options.module.?; | |
| 2296 | const decl = mod.declPtr(decl_index); | |
| 2297 | if (decl.link.elf.local_sym_index != 0) return; | |
| 2298 | ||
| 2284 | pub fn allocateGotOffset(self: *Elf) !u32 { | |
| 2299 | 2285 | try self.offset_table.ensureUnusedCapacity(self.base.allocator, 1); |
| 2300 | try self.decls.putNoClobber(self.base.allocator, decl_index, null); | |
| 2301 | 2286 | |
| 2302 | const decl_name = try decl.getFullyQualifiedName(mod); | |
| 2303 | defer self.base.allocator.free(decl_name); | |
| 2304 | ||
| 2305 | log.debug("allocating symbol indexes for {s}", .{decl_name}); | |
| 2306 | decl.link.elf.local_sym_index = try self.allocateLocalSymbol(); | |
| 2307 | try self.atom_by_index_table.putNoClobber(self.base.allocator, decl.link.elf.local_sym_index, &decl.link.elf); | |
| 2287 | const index = blk: { | |
| 2288 | if (self.offset_table_free_list.popOrNull()) |index| { | |
| 2289 | log.debug(" (reusing GOT offset at index {d})", .{index}); | |
| 2290 | break :blk index; | |
| 2291 | } else { | |
| 2292 | log.debug(" (allocating GOT offset at index {d})", .{self.offset_table.items.len}); | |
| 2293 | const index = @intCast(u32, self.offset_table.items.len); | |
| 2294 | _ = self.offset_table.addOneAssumeCapacity(); | |
| 2295 | self.offset_table_count_dirty = true; | |
| 2296 | break :blk index; | |
| 2297 | } | |
| 2298 | }; | |
| 2308 | 2299 | |
| 2309 | if (self.offset_table_free_list.popOrNull()) |i| { | |
| 2310 | decl.link.elf.offset_table_index = i; | |
| 2311 | } else { | |
| 2312 | decl.link.elf.offset_table_index = @intCast(u32, self.offset_table.items.len); | |
| 2313 | _ = self.offset_table.addOneAssumeCapacity(); | |
| 2314 | self.offset_table_count_dirty = true; | |
| 2315 | } | |
| 2316 | self.offset_table.items[decl.link.elf.offset_table_index] = 0; | |
| 2300 | self.offset_table.items[index] = 0; | |
| 2301 | return index; | |
| 2317 | 2302 | } |
| 2318 | 2303 | |
| 2319 | 2304 | fn freeUnnamedConsts(self: *Elf, decl_index: Module.Decl.Index) void { |
| 2320 | 2305 | const unnamed_consts = self.unnamed_const_atoms.getPtr(decl_index) orelse return; |
| 2321 | 2306 | for (unnamed_consts.items) |atom| { |
| 2322 | self.freeTextBlock(atom, self.phdr_load_ro_index.?); | |
| 2323 | self.local_symbol_free_list.append(self.base.allocator, atom.local_sym_index) catch {}; | |
| 2324 | self.local_symbols.items[atom.local_sym_index].st_info = 0; | |
| 2325 | _ = self.atom_by_index_table.remove(atom.local_sym_index); | |
| 2307 | self.freeAtom(atom); | |
| 2326 | 2308 | } |
| 2327 | 2309 | unnamed_consts.clearAndFree(self.base.allocator); |
| 2328 | 2310 | } |
| ... | ... | @@ -2335,52 +2317,59 @@ pub fn freeDecl(self: *Elf, decl_index: Module.Decl.Index) void { |
| 2335 | 2317 | const mod = self.base.options.module.?; |
| 2336 | 2318 | const decl = mod.declPtr(decl_index); |
| 2337 | 2319 | |
| 2338 | const kv = self.decls.fetchRemove(decl_index); | |
| 2339 | if (kv.?.value) |index| { | |
| 2340 | self.freeTextBlock(&decl.link.elf, index); | |
| 2320 | log.debug("freeDecl {*}", .{decl}); | |
| 2321 | ||
| 2322 | if (self.decls.fetchRemove(decl_index)) |const_kv| { | |
| 2323 | var kv = const_kv; | |
| 2324 | self.freeAtom(kv.value.atom); | |
| 2341 | 2325 | self.freeUnnamedConsts(decl_index); |
| 2326 | kv.value.exports.deinit(self.base.allocator); | |
| 2342 | 2327 | } |
| 2343 | 2328 | |
| 2344 | // Appending to free lists is allowed to fail because the free lists are heuristics based anyway. | |
| 2345 | if (decl.link.elf.local_sym_index != 0) { | |
| 2346 | self.local_symbol_free_list.append(self.base.allocator, decl.link.elf.local_sym_index) catch {}; | |
| 2347 | self.local_symbols.items[decl.link.elf.local_sym_index].st_info = 0; | |
| 2348 | _ = self.atom_by_index_table.remove(decl.link.elf.local_sym_index); | |
| 2349 | decl.link.elf.local_sym_index = 0; | |
| 2350 | ||
| 2351 | self.offset_table_free_list.append(self.base.allocator, decl.link.elf.offset_table_index) catch {}; | |
| 2329 | if (self.dwarf) |*dw| { | |
| 2330 | dw.freeDecl(decl_index); | |
| 2352 | 2331 | } |
| 2332 | } | |
| 2353 | 2333 | |
| 2354 | if (self.dwarf) |*dw| { | |
| 2355 | dw.freeDecl(decl); | |
| 2334 | pub fn getOrCreateAtomForDecl(self: *Elf, decl_index: Module.Decl.Index) !Atom.Index { | |
| 2335 | const gop = try self.decls.getOrPut(self.base.allocator, decl_index); | |
| 2336 | if (!gop.found_existing) { | |
| 2337 | gop.value_ptr.* = .{ | |
| 2338 | .atom = try self.createAtom(), | |
| 2339 | .shdr = self.getDeclShdrIndex(decl_index), | |
| 2340 | .exports = .{}, | |
| 2341 | }; | |
| 2356 | 2342 | } |
| 2343 | return gop.value_ptr.atom; | |
| 2357 | 2344 | } |
| 2358 | 2345 | |
| 2359 | fn getDeclPhdrIndex(self: *Elf, decl: *Module.Decl) !u16 { | |
| 2346 | fn getDeclShdrIndex(self: *Elf, decl_index: Module.Decl.Index) u16 { | |
| 2347 | const decl = self.base.options.module.?.declPtr(decl_index); | |
| 2360 | 2348 | const ty = decl.ty; |
| 2361 | 2349 | const zig_ty = ty.zigTypeTag(); |
| 2362 | 2350 | const val = decl.val; |
| 2363 | const phdr_index: u16 = blk: { | |
| 2351 | const shdr_index: u16 = blk: { | |
| 2364 | 2352 | if (val.isUndefDeep()) { |
| 2365 | 2353 | // TODO in release-fast and release-small, we should put undef in .bss |
| 2366 | break :blk self.phdr_load_rw_index.?; | |
| 2354 | break :blk self.data_section_index.?; | |
| 2367 | 2355 | } |
| 2368 | 2356 | |
| 2369 | 2357 | switch (zig_ty) { |
| 2370 | 2358 | // TODO: what if this is a function pointer? |
| 2371 | .Fn => break :blk self.phdr_load_re_index.?, | |
| 2359 | .Fn => break :blk self.text_section_index.?, | |
| 2372 | 2360 | else => { |
| 2373 | 2361 | if (val.castTag(.variable)) |_| { |
| 2374 | break :blk self.phdr_load_rw_index.?; | |
| 2362 | break :blk self.data_section_index.?; | |
| 2375 | 2363 | } |
| 2376 | break :blk self.phdr_load_ro_index.?; | |
| 2364 | break :blk self.rodata_section_index.?; | |
| 2377 | 2365 | }, |
| 2378 | 2366 | } |
| 2379 | 2367 | }; |
| 2380 | return phdr_index; | |
| 2368 | return shdr_index; | |
| 2381 | 2369 | } |
| 2382 | 2370 | |
| 2383 | 2371 | fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, stt_bits: u8) !*elf.Elf64_Sym { |
| 2372 | const gpa = self.base.allocator; | |
| 2384 | 2373 | const mod = self.base.options.module.?; |
| 2385 | 2374 | const decl = mod.declPtr(decl_index); |
| 2386 | 2375 | |
| ... | ... | @@ -2390,61 +2379,65 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s |
| 2390 | 2379 | log.debug("updateDeclCode {s}{*}", .{ decl_name, decl }); |
| 2391 | 2380 | const required_alignment = decl.getAlignment(self.base.options.target); |
| 2392 | 2381 | |
| 2393 | const decl_ptr = self.decls.getPtr(decl_index).?; | |
| 2394 | if (decl_ptr.* == null) { | |
| 2395 | decl_ptr.* = try self.getDeclPhdrIndex(decl); | |
| 2396 | } | |
| 2397 | const phdr_index = decl_ptr.*.?; | |
| 2398 | const shdr_index = self.phdr_shdr_table.get(phdr_index).?; | |
| 2382 | const decl_metadata = self.decls.get(decl_index).?; | |
| 2383 | const atom_index = decl_metadata.atom; | |
| 2384 | const atom = self.getAtom(atom_index); | |
| 2399 | 2385 | |
| 2400 | assert(decl.link.elf.local_sym_index != 0); // Caller forgot to allocateDeclIndexes() | |
| 2401 | const local_sym = &self.local_symbols.items[decl.link.elf.local_sym_index]; | |
| 2402 | if (local_sym.st_size != 0) { | |
| 2403 | const capacity = decl.link.elf.capacity(self.*); | |
| 2386 | const shdr_index = decl_metadata.shdr; | |
| 2387 | if (atom.getSymbol(self).st_size != 0) { | |
| 2388 | const local_sym = atom.getSymbolPtr(self); | |
| 2389 | local_sym.st_name = try self.shstrtab.insert(gpa, decl_name); | |
| 2390 | local_sym.st_info = (elf.STB_LOCAL << 4) | stt_bits; | |
| 2391 | local_sym.st_other = 0; | |
| 2392 | local_sym.st_shndx = shdr_index; | |
| 2393 | ||
| 2394 | const capacity = atom.capacity(self); | |
| 2404 | 2395 | const need_realloc = code.len > capacity or |
| 2405 | 2396 | !mem.isAlignedGeneric(u64, local_sym.st_value, required_alignment); |
| 2397 | ||
| 2406 | 2398 | if (need_realloc) { |
| 2407 | const vaddr = try self.growTextBlock(&decl.link.elf, code.len, required_alignment, phdr_index); | |
| 2399 | const vaddr = try self.growAtom(atom_index, code.len, required_alignment); | |
| 2408 | 2400 | log.debug("growing {s} from 0x{x} to 0x{x}", .{ decl_name, local_sym.st_value, vaddr }); |
| 2409 | 2401 | if (vaddr != local_sym.st_value) { |
| 2410 | 2402 | local_sym.st_value = vaddr; |
| 2411 | 2403 | |
| 2412 | 2404 | log.debug(" (writing new offset table entry)", .{}); |
| 2413 | self.offset_table.items[decl.link.elf.offset_table_index] = vaddr; | |
| 2414 | try self.writeOffsetTableEntry(decl.link.elf.offset_table_index); | |
| 2405 | self.offset_table.items[atom.offset_table_index] = vaddr; | |
| 2406 | try self.writeOffsetTableEntry(atom.offset_table_index); | |
| 2415 | 2407 | } |
| 2416 | 2408 | } else if (code.len < local_sym.st_size) { |
| 2417 | self.shrinkTextBlock(&decl.link.elf, code.len, phdr_index); | |
| 2409 | self.shrinkAtom(atom_index, code.len); | |
| 2418 | 2410 | } |
| 2419 | 2411 | local_sym.st_size = code.len; |
| 2420 | local_sym.st_name = try self.updateString(local_sym.st_name, decl_name); | |
| 2421 | local_sym.st_info = (elf.STB_LOCAL << 4) | stt_bits; | |
| 2422 | local_sym.st_other = 0; | |
| 2423 | local_sym.st_shndx = shdr_index; | |
| 2412 | ||
| 2424 | 2413 | // TODO this write could be avoided if no fields of the symbol were changed. |
| 2425 | try self.writeSymbol(decl.link.elf.local_sym_index); | |
| 2414 | try self.writeSymbol(atom.getSymbolIndex().?); | |
| 2426 | 2415 | } else { |
| 2427 | const name_str_index = try self.makeString(decl_name); | |
| 2428 | const vaddr = try self.allocateTextBlock(&decl.link.elf, code.len, required_alignment, phdr_index); | |
| 2429 | errdefer self.freeTextBlock(&decl.link.elf, phdr_index); | |
| 2430 | log.debug("allocated text block for {s} at 0x{x}", .{ decl_name, vaddr }); | |
| 2431 | ||
| 2416 | const local_sym = atom.getSymbolPtr(self); | |
| 2432 | 2417 | local_sym.* = .{ |
| 2433 | .st_name = name_str_index, | |
| 2418 | .st_name = try self.shstrtab.insert(gpa, decl_name), | |
| 2434 | 2419 | .st_info = (elf.STB_LOCAL << 4) | stt_bits, |
| 2435 | 2420 | .st_other = 0, |
| 2436 | 2421 | .st_shndx = shdr_index, |
| 2437 | .st_value = vaddr, | |
| 2438 | .st_size = code.len, | |
| 2422 | .st_value = 0, | |
| 2423 | .st_size = 0, | |
| 2439 | 2424 | }; |
| 2440 | self.offset_table.items[decl.link.elf.offset_table_index] = vaddr; | |
| 2425 | const vaddr = try self.allocateAtom(atom_index, code.len, required_alignment); | |
| 2426 | errdefer self.freeAtom(atom_index); | |
| 2427 | log.debug("allocated text block for {s} at 0x{x}", .{ decl_name, vaddr }); | |
| 2428 | ||
| 2429 | self.offset_table.items[atom.offset_table_index] = vaddr; | |
| 2430 | local_sym.st_value = vaddr; | |
| 2431 | local_sym.st_size = code.len; | |
| 2441 | 2432 | |
| 2442 | try self.writeSymbol(decl.link.elf.local_sym_index); | |
| 2443 | try self.writeOffsetTableEntry(decl.link.elf.offset_table_index); | |
| 2433 | try self.writeSymbol(atom.getSymbolIndex().?); | |
| 2434 | try self.writeOffsetTableEntry(atom.offset_table_index); | |
| 2444 | 2435 | } |
| 2445 | 2436 | |
| 2437 | const local_sym = atom.getSymbolPtr(self); | |
| 2438 | const phdr_index = self.sections.items(.phdr_index)[shdr_index]; | |
| 2446 | 2439 | const section_offset = local_sym.st_value - self.program_headers.items[phdr_index].p_vaddr; |
| 2447 | const file_offset = self.sections.items[shdr_index].sh_offset + section_offset; | |
| 2440 | const file_offset = self.sections.items(.shdr)[shdr_index].sh_offset + section_offset; | |
| 2448 | 2441 | try self.base.file.?.pwriteAll(code, file_offset); |
| 2449 | 2442 | |
| 2450 | 2443 | return local_sym; |
| ... | ... | @@ -2461,12 +2454,15 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven |
| 2461 | 2454 | const tracy = trace(@src()); |
| 2462 | 2455 | defer tracy.end(); |
| 2463 | 2456 | |
| 2464 | var code_buffer = std.ArrayList(u8).init(self.base.allocator); | |
| 2465 | defer code_buffer.deinit(); | |
| 2466 | ||
| 2467 | 2457 | const decl_index = func.owner_decl; |
| 2468 | 2458 | const decl = module.declPtr(decl_index); |
| 2459 | ||
| 2460 | const atom_index = try self.getOrCreateAtomForDecl(decl_index); | |
| 2469 | 2461 | self.freeUnnamedConsts(decl_index); |
| 2462 | Atom.freeRelocations(self, atom_index); | |
| 2463 | ||
| 2464 | var code_buffer = std.ArrayList(u8).init(self.base.allocator); | |
| 2465 | defer code_buffer.deinit(); | |
| 2470 | 2466 | |
| 2471 | 2467 | var decl_state: ?Dwarf.DeclState = if (self.dwarf) |*dw| try dw.initDeclState(module, decl_index) else null; |
| 2472 | 2468 | defer if (decl_state) |*ds| ds.deinit(); |
| ... | ... | @@ -2479,7 +2475,7 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven |
| 2479 | 2475 | try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .none); |
| 2480 | 2476 | |
| 2481 | 2477 | const code = switch (res) { |
| 2482 | .appended => code_buffer.items, | |
| 2478 | .ok => code_buffer.items, | |
| 2483 | 2479 | .fail => |em| { |
| 2484 | 2480 | decl.analysis = .codegen_failure; |
| 2485 | 2481 | try module.failed_decls.put(module.gpa, decl_index, em); |
| ... | ... | @@ -2525,7 +2521,9 @@ pub fn updateDecl(self: *Elf, module: *Module, decl_index: Module.Decl.Index) !v |
| 2525 | 2521 | } |
| 2526 | 2522 | } |
| 2527 | 2523 | |
| 2528 | assert(!self.unnamed_const_atoms.contains(decl_index)); | |
| 2524 | const atom_index = try self.getOrCreateAtomForDecl(decl_index); | |
| 2525 | Atom.freeRelocations(self, atom_index); | |
| 2526 | const atom = self.getAtom(atom_index); | |
| 2529 | 2527 | |
| 2530 | 2528 | var code_buffer = std.ArrayList(u8).init(self.base.allocator); |
| 2531 | 2529 | defer code_buffer.deinit(); |
| ... | ... | @@ -2542,19 +2540,18 @@ pub fn updateDecl(self: *Elf, module: *Module, decl_index: Module.Decl.Index) !v |
| 2542 | 2540 | }, &code_buffer, .{ |
| 2543 | 2541 | .dwarf = ds, |
| 2544 | 2542 | }, .{ |
| 2545 | .parent_atom_index = decl.link.elf.local_sym_index, | |
| 2543 | .parent_atom_index = atom.getSymbolIndex().?, | |
| 2546 | 2544 | }) |
| 2547 | 2545 | else |
| 2548 | 2546 | try codegen.generateSymbol(&self.base, decl.srcLoc(), .{ |
| 2549 | 2547 | .ty = decl.ty, |
| 2550 | 2548 | .val = decl_val, |
| 2551 | 2549 | }, &code_buffer, .none, .{ |
| 2552 | .parent_atom_index = decl.link.elf.local_sym_index, | |
| 2550 | .parent_atom_index = atom.getSymbolIndex().?, | |
| 2553 | 2551 | }); |
| 2554 | 2552 | |
| 2555 | 2553 | const code = switch (res) { |
| 2556 | .externally_managed => |x| x, | |
| 2557 | .appended => code_buffer.items, | |
| 2554 | .ok => code_buffer.items, | |
| 2558 | 2555 | .fail => |em| { |
| 2559 | 2556 | decl.analysis = .codegen_failure; |
| 2560 | 2557 | try module.failed_decls.put(module.gpa, decl_index, em); |
| ... | ... | @@ -2579,47 +2576,38 @@ pub fn updateDecl(self: *Elf, module: *Module, decl_index: Module.Decl.Index) !v |
| 2579 | 2576 | } |
| 2580 | 2577 | |
| 2581 | 2578 | pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module.Decl.Index) !u32 { |
| 2582 | var code_buffer = std.ArrayList(u8).init(self.base.allocator); | |
| 2579 | const gpa = self.base.allocator; | |
| 2580 | ||
| 2581 | var code_buffer = std.ArrayList(u8).init(gpa); | |
| 2583 | 2582 | defer code_buffer.deinit(); |
| 2584 | 2583 | |
| 2585 | 2584 | const mod = self.base.options.module.?; |
| 2586 | const decl = mod.declPtr(decl_index); | |
| 2587 | ||
| 2588 | const gop = try self.unnamed_const_atoms.getOrPut(self.base.allocator, decl_index); | |
| 2585 | const gop = try self.unnamed_const_atoms.getOrPut(gpa, decl_index); | |
| 2589 | 2586 | if (!gop.found_existing) { |
| 2590 | 2587 | gop.value_ptr.* = .{}; |
| 2591 | 2588 | } |
| 2592 | 2589 | const unnamed_consts = gop.value_ptr; |
| 2593 | 2590 | |
| 2594 | const atom = try self.base.allocator.create(TextBlock); | |
| 2595 | errdefer self.base.allocator.destroy(atom); | |
| 2596 | atom.* = TextBlock.empty; | |
| 2597 | try self.managed_atoms.append(self.base.allocator, atom); | |
| 2598 | ||
| 2591 | const decl = mod.declPtr(decl_index); | |
| 2599 | 2592 | const name_str_index = blk: { |
| 2600 | 2593 | const decl_name = try decl.getFullyQualifiedName(mod); |
| 2601 | defer self.base.allocator.free(decl_name); | |
| 2602 | ||
| 2594 | defer gpa.free(decl_name); | |
| 2603 | 2595 | const index = unnamed_consts.items.len; |
| 2604 | const name = try std.fmt.allocPrint(self.base.allocator, "__unnamed_{s}_{d}", .{ decl_name, index }); | |
| 2605 | defer self.base.allocator.free(name); | |
| 2606 | ||
| 2607 | break :blk try self.makeString(name); | |
| 2596 | const name = try std.fmt.allocPrint(gpa, "__unnamed_{s}_{d}", .{ decl_name, index }); | |
| 2597 | defer gpa.free(name); | |
| 2598 | break :blk try self.shstrtab.insert(gpa, name); | |
| 2608 | 2599 | }; |
| 2609 | const name = self.getString(name_str_index); | |
| 2600 | const name = self.shstrtab.get(name_str_index).?; | |
| 2610 | 2601 | |
| 2611 | log.debug("allocating symbol indexes for {s}", .{name}); | |
| 2612 | atom.local_sym_index = try self.allocateLocalSymbol(); | |
| 2613 | try self.atom_by_index_table.putNoClobber(self.base.allocator, atom.local_sym_index, atom); | |
| 2602 | const atom_index = try self.createAtom(); | |
| 2614 | 2603 | |
| 2615 | 2604 | const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), typed_value, &code_buffer, .{ |
| 2616 | 2605 | .none = {}, |
| 2617 | 2606 | }, .{ |
| 2618 | .parent_atom_index = atom.local_sym_index, | |
| 2607 | .parent_atom_index = self.getAtom(atom_index).getSymbolIndex().?, | |
| 2619 | 2608 | }); |
| 2620 | 2609 | const code = switch (res) { |
| 2621 | .externally_managed => |x| x, | |
| 2622 | .appended => code_buffer.items, | |
| 2610 | .ok => code_buffer.items, | |
| 2623 | 2611 | .fail => |em| { |
| 2624 | 2612 | decl.analysis = .codegen_failure; |
| 2625 | 2613 | try mod.failed_decls.put(mod.gpa, decl_index, em); |
| ... | ... | @@ -2629,31 +2617,27 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module |
| 2629 | 2617 | }; |
| 2630 | 2618 | |
| 2631 | 2619 | const required_alignment = typed_value.ty.abiAlignment(self.base.options.target); |
| 2632 | const phdr_index = self.phdr_load_ro_index.?; | |
| 2633 | const shdr_index = self.phdr_shdr_table.get(phdr_index).?; | |
| 2634 | const vaddr = try self.allocateTextBlock(atom, code.len, required_alignment, phdr_index); | |
| 2635 | errdefer self.freeTextBlock(atom, phdr_index); | |
| 2636 | ||
| 2637 | log.debug("allocated text block for {s} at 0x{x}", .{ name, vaddr }); | |
| 2638 | ||
| 2639 | const local_sym = &self.local_symbols.items[atom.local_sym_index]; | |
| 2640 | local_sym.* = .{ | |
| 2641 | .st_name = name_str_index, | |
| 2642 | .st_info = (elf.STB_LOCAL << 4) | elf.STT_OBJECT, | |
| 2643 | .st_other = 0, | |
| 2644 | .st_shndx = shdr_index, | |
| 2645 | .st_value = vaddr, | |
| 2646 | .st_size = code.len, | |
| 2647 | }; | |
| 2648 | ||
| 2649 | try self.writeSymbol(atom.local_sym_index); | |
| 2650 | try unnamed_consts.append(self.base.allocator, atom); | |
| 2620 | const shdr_index = self.rodata_section_index.?; | |
| 2621 | const phdr_index = self.sections.items(.phdr_index)[shdr_index]; | |
| 2622 | const local_sym = self.getAtom(atom_index).getSymbolPtr(self); | |
| 2623 | local_sym.st_name = name_str_index; | |
| 2624 | local_sym.st_info = (elf.STB_LOCAL << 4) | elf.STT_OBJECT; | |
| 2625 | local_sym.st_other = 0; | |
| 2626 | local_sym.st_shndx = shdr_index; | |
| 2627 | local_sym.st_size = code.len; | |
| 2628 | local_sym.st_value = try self.allocateAtom(atom_index, code.len, required_alignment); | |
| 2629 | errdefer self.freeAtom(atom_index); | |
| 2630 | ||
| 2631 | log.debug("allocated text block for {s} at 0x{x}", .{ name, local_sym.st_value }); | |
| 2632 | ||
| 2633 | try self.writeSymbol(self.getAtom(atom_index).getSymbolIndex().?); | |
| 2634 | try unnamed_consts.append(gpa, atom_index); | |
| 2651 | 2635 | |
| 2652 | 2636 | const section_offset = local_sym.st_value - self.program_headers.items[phdr_index].p_vaddr; |
| 2653 | const file_offset = self.sections.items[shdr_index].sh_offset + section_offset; | |
| 2637 | const file_offset = self.sections.items(.shdr)[shdr_index].sh_offset + section_offset; | |
| 2654 | 2638 | try self.base.file.?.pwriteAll(code, file_offset); |
| 2655 | 2639 | |
| 2656 | return atom.local_sym_index; | |
| 2640 | return self.getAtom(atom_index).getSymbolIndex().?; | |
| 2657 | 2641 | } |
| 2658 | 2642 | |
| 2659 | 2643 | pub fn updateDeclExports( |
| ... | ... | @@ -2672,17 +2656,16 @@ pub fn updateDeclExports( |
| 2672 | 2656 | const tracy = trace(@src()); |
| 2673 | 2657 | defer tracy.end(); |
| 2674 | 2658 | |
| 2675 | try self.global_symbols.ensureUnusedCapacity(self.base.allocator, exports.len); | |
| 2659 | const gpa = self.base.allocator; | |
| 2660 | ||
| 2676 | 2661 | const decl = module.declPtr(decl_index); |
| 2677 | if (decl.link.elf.local_sym_index == 0) return; | |
| 2678 | const decl_sym = self.local_symbols.items[decl.link.elf.local_sym_index]; | |
| 2662 | const atom_index = try self.getOrCreateAtomForDecl(decl_index); | |
| 2663 | const atom = self.getAtom(atom_index); | |
| 2664 | const decl_sym = atom.getSymbol(self); | |
| 2665 | const decl_metadata = self.decls.getPtr(decl_index).?; | |
| 2666 | const shdr_index = decl_metadata.shdr; | |
| 2679 | 2667 | |
| 2680 | const decl_ptr = self.decls.getPtr(decl_index).?; | |
| 2681 | if (decl_ptr.* == null) { | |
| 2682 | decl_ptr.* = try self.getDeclPhdrIndex(decl); | |
| 2683 | } | |
| 2684 | const phdr_index = decl_ptr.*.?; | |
| 2685 | const shdr_index = self.phdr_shdr_table.get(phdr_index).?; | |
| 2668 | try self.global_symbols.ensureUnusedCapacity(gpa, exports.len); | |
| 2686 | 2669 | |
| 2687 | 2670 | for (exports) |exp| { |
| 2688 | 2671 | if (exp.options.section) |section_name| { |
| ... | ... | @@ -2715,10 +2698,10 @@ pub fn updateDeclExports( |
| 2715 | 2698 | }, |
| 2716 | 2699 | }; |
| 2717 | 2700 | const stt_bits: u8 = @truncate(u4, decl_sym.st_info); |
| 2718 | if (exp.link.elf.sym_index) |i| { | |
| 2701 | if (decl_metadata.getExport(self, exp.options.name)) |i| { | |
| 2719 | 2702 | const sym = &self.global_symbols.items[i]; |
| 2720 | 2703 | sym.* = .{ |
| 2721 | .st_name = try self.updateString(sym.st_name, exp.options.name), | |
| 2704 | .st_name = try self.shstrtab.insert(gpa, exp.options.name), | |
| 2722 | 2705 | .st_info = (stb_bits << 4) | stt_bits, |
| 2723 | 2706 | .st_other = 0, |
| 2724 | 2707 | .st_shndx = shdr_index, |
| ... | ... | @@ -2726,30 +2709,29 @@ pub fn updateDeclExports( |
| 2726 | 2709 | .st_size = decl_sym.st_size, |
| 2727 | 2710 | }; |
| 2728 | 2711 | } else { |
| 2729 | const name = try self.makeString(exp.options.name); | |
| 2730 | 2712 | const i = if (self.global_symbol_free_list.popOrNull()) |i| i else blk: { |
| 2731 | 2713 | _ = self.global_symbols.addOneAssumeCapacity(); |
| 2732 | 2714 | break :blk self.global_symbols.items.len - 1; |
| 2733 | 2715 | }; |
| 2716 | try decl_metadata.exports.append(gpa, @intCast(u32, i)); | |
| 2734 | 2717 | self.global_symbols.items[i] = .{ |
| 2735 | .st_name = name, | |
| 2718 | .st_name = try self.shstrtab.insert(gpa, exp.options.name), | |
| 2736 | 2719 | .st_info = (stb_bits << 4) | stt_bits, |
| 2737 | 2720 | .st_other = 0, |
| 2738 | 2721 | .st_shndx = shdr_index, |
| 2739 | 2722 | .st_value = decl_sym.st_value, |
| 2740 | 2723 | .st_size = decl_sym.st_size, |
| 2741 | 2724 | }; |
| 2742 | ||
| 2743 | exp.link.elf.sym_index = @intCast(u32, i); | |
| 2744 | 2725 | } |
| 2745 | 2726 | } |
| 2746 | 2727 | } |
| 2747 | 2728 | |
| 2748 | 2729 | /// Must be called only after a successful call to `updateDecl`. |
| 2749 | pub fn updateDeclLineNumber(self: *Elf, mod: *Module, decl: *const Module.Decl) !void { | |
| 2730 | pub fn updateDeclLineNumber(self: *Elf, mod: *Module, decl_index: Module.Decl.Index) !void { | |
| 2750 | 2731 | const tracy = trace(@src()); |
| 2751 | 2732 | defer tracy.end(); |
| 2752 | 2733 | |
| 2734 | const decl = mod.declPtr(decl_index); | |
| 2753 | 2735 | const decl_name = try decl.getFullyQualifiedName(mod); |
| 2754 | 2736 | defer self.base.allocator.free(decl_name); |
| 2755 | 2737 | |
| ... | ... | @@ -2757,16 +2739,18 @@ pub fn updateDeclLineNumber(self: *Elf, mod: *Module, decl: *const Module.Decl) |
| 2757 | 2739 | |
| 2758 | 2740 | if (self.llvm_object) |_| return; |
| 2759 | 2741 | if (self.dwarf) |*dw| { |
| 2760 | try dw.updateDeclLineNumber(decl); | |
| 2742 | try dw.updateDeclLineNumber(mod, decl_index); | |
| 2761 | 2743 | } |
| 2762 | 2744 | } |
| 2763 | 2745 | |
| 2764 | pub fn deleteExport(self: *Elf, exp: Export) void { | |
| 2746 | pub fn deleteDeclExport(self: *Elf, decl_index: Module.Decl.Index, name: []const u8) void { | |
| 2765 | 2747 | if (self.llvm_object) |_| return; |
| 2766 | ||
| 2767 | const sym_index = exp.sym_index orelse return; | |
| 2768 | self.global_symbol_free_list.append(self.base.allocator, sym_index) catch {}; | |
| 2769 | self.global_symbols.items[sym_index].st_info = 0; | |
| 2748 | const metadata = self.decls.getPtr(decl_index) orelse return; | |
| 2749 | const sym_index = metadata.getExportPtr(self, name) orelse return; | |
| 2750 | log.debug("deleting export '{s}'", .{name}); | |
| 2751 | self.global_symbol_free_list.append(self.base.allocator, sym_index.*) catch {}; | |
| 2752 | self.global_symbols.items[sym_index.*].st_info = 0; | |
| 2753 | sym_index.* = 0; | |
| 2770 | 2754 | } |
| 2771 | 2755 | |
| 2772 | 2756 | fn writeProgHeader(self: *Elf, index: usize) !void { |
| ... | ... | @@ -2795,7 +2779,7 @@ fn writeSectHeader(self: *Elf, index: usize) !void { |
| 2795 | 2779 | switch (self.ptr_width) { |
| 2796 | 2780 | .p32 => { |
| 2797 | 2781 | var shdr: [1]elf.Elf32_Shdr = undefined; |
| 2798 | shdr[0] = sectHeaderTo32(self.sections.items[index]); | |
| 2782 | shdr[0] = sectHeaderTo32(self.sections.items(.shdr)[index]); | |
| 2799 | 2783 | if (foreign_endian) { |
| 2800 | 2784 | mem.byteSwapAllFields(elf.Elf32_Shdr, &shdr[0]); |
| 2801 | 2785 | } |
| ... | ... | @@ -2803,7 +2787,7 @@ fn writeSectHeader(self: *Elf, index: usize) !void { |
| 2803 | 2787 | return self.base.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset); |
| 2804 | 2788 | }, |
| 2805 | 2789 | .p64 => { |
| 2806 | var shdr = [1]elf.Elf64_Shdr{self.sections.items[index]}; | |
| 2790 | var shdr = [1]elf.Elf64_Shdr{self.sections.items(.shdr)[index]}; | |
| 2807 | 2791 | if (foreign_endian) { |
| 2808 | 2792 | mem.byteSwapAllFields(elf.Elf64_Shdr, &shdr[0]); |
| 2809 | 2793 | } |
| ... | ... | @@ -2817,11 +2801,11 @@ fn writeOffsetTableEntry(self: *Elf, index: usize) !void { |
| 2817 | 2801 | const entry_size: u16 = self.archPtrWidthBytes(); |
| 2818 | 2802 | if (self.offset_table_count_dirty) { |
| 2819 | 2803 | const needed_size = self.offset_table.items.len * entry_size; |
| 2820 | try self.growAllocSection(self.got_section_index.?, self.phdr_got_index.?, needed_size); | |
| 2804 | try self.growAllocSection(self.got_section_index.?, needed_size); | |
| 2821 | 2805 | self.offset_table_count_dirty = false; |
| 2822 | 2806 | } |
| 2823 | 2807 | const endian = self.base.options.target.cpu.arch.endian(); |
| 2824 | const shdr = &self.sections.items[self.got_section_index.?]; | |
| 2808 | const shdr = &self.sections.items(.shdr)[self.got_section_index.?]; | |
| 2825 | 2809 | const off = shdr.sh_offset + @as(u64, entry_size) * index; |
| 2826 | 2810 | switch (entry_size) { |
| 2827 | 2811 | 2 => { |
| ... | ... | @@ -2847,7 +2831,7 @@ fn writeSymbol(self: *Elf, index: usize) !void { |
| 2847 | 2831 | const tracy = trace(@src()); |
| 2848 | 2832 | defer tracy.end(); |
| 2849 | 2833 | |
| 2850 | const syms_sect = &self.sections.items[self.symtab_section_index.?]; | |
| 2834 | const syms_sect = &self.sections.items(.shdr)[self.symtab_section_index.?]; | |
| 2851 | 2835 | // Make sure we are not pointlessly writing symbol data that will have to get relocated |
| 2852 | 2836 | // due to running out of space. |
| 2853 | 2837 | if (self.local_symbols.items.len != syms_sect.sh_info) { |
| ... | ... | @@ -2869,7 +2853,7 @@ fn writeSymbol(self: *Elf, index: usize) !void { |
| 2869 | 2853 | .p64 => syms_sect.sh_offset + @sizeOf(elf.Elf64_Sym) * index, |
| 2870 | 2854 | }; |
| 2871 | 2855 | const local = self.local_symbols.items[index]; |
| 2872 | log.debug("writing symbol {d}, '{s}' at 0x{x}", .{ index, self.getString(local.st_name), off }); | |
| 2856 | log.debug("writing symbol {d}, '{?s}' at 0x{x}", .{ index, self.shstrtab.get(local.st_name), off }); | |
| 2873 | 2857 | log.debug(" ({})", .{local}); |
| 2874 | 2858 | switch (self.ptr_width) { |
| 2875 | 2859 | .p32 => { |
| ... | ... | @@ -2899,7 +2883,7 @@ fn writeSymbol(self: *Elf, index: usize) !void { |
| 2899 | 2883 | } |
| 2900 | 2884 | |
| 2901 | 2885 | fn writeAllGlobalSymbols(self: *Elf) !void { |
| 2902 | const syms_sect = &self.sections.items[self.symtab_section_index.?]; | |
| 2886 | const syms_sect = &self.sections.items(.shdr)[self.symtab_section_index.?]; | |
| 2903 | 2887 | const sym_size: u64 = switch (self.ptr_width) { |
| 2904 | 2888 | .p32 => @sizeOf(elf.Elf32_Sym), |
| 2905 | 2889 | .p64 => @sizeOf(elf.Elf64_Sym), |
| ... | ... | @@ -3042,7 +3026,7 @@ fn getLDMOption(target: std.Target) ?[]const u8 { |
| 3042 | 3026 | } |
| 3043 | 3027 | } |
| 3044 | 3028 | |
| 3045 | fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) { | |
| 3029 | pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) { | |
| 3046 | 3030 | return actual_size +| (actual_size / ideal_factor); |
| 3047 | 3031 | } |
| 3048 | 3032 | |
| ... | ... | @@ -3249,10 +3233,58 @@ const CsuObjects = struct { |
| 3249 | 3233 | fn logSymtab(self: Elf) void { |
| 3250 | 3234 | log.debug("locals:", .{}); |
| 3251 | 3235 | for (self.local_symbols.items) |sym, id| { |
| 3252 | log.debug(" {d}: {s}: @{x} in {d}", .{ id, self.getString(sym.st_name), sym.st_value, sym.st_shndx }); | |
| 3236 | log.debug(" {d}: {?s}: @{x} in {d}", .{ id, self.shstrtab.get(sym.st_name), sym.st_value, sym.st_shndx }); | |
| 3253 | 3237 | } |
| 3254 | 3238 | log.debug("globals:", .{}); |
| 3255 | 3239 | for (self.global_symbols.items) |sym, id| { |
| 3256 | log.debug(" {d}: {s}: @{x} in {d}", .{ id, self.getString(sym.st_name), sym.st_value, sym.st_shndx }); | |
| 3240 | log.debug(" {d}: {?s}: @{x} in {d}", .{ id, self.shstrtab.get(sym.st_name), sym.st_value, sym.st_shndx }); | |
| 3257 | 3241 | } |
| 3258 | 3242 | } |
| 3243 | ||
| 3244 | pub fn getProgramHeader(self: *const Elf, shdr_index: u16) elf.Elf64_Phdr { | |
| 3245 | const index = self.sections.items(.phdr_index)[shdr_index]; | |
| 3246 | return self.program_headers.items[index]; | |
| 3247 | } | |
| 3248 | ||
| 3249 | pub fn getProgramHeaderPtr(self: *Elf, shdr_index: u16) *elf.Elf64_Phdr { | |
| 3250 | const index = self.sections.items(.phdr_index)[shdr_index]; | |
| 3251 | return &self.program_headers.items[index]; | |
| 3252 | } | |
| 3253 | ||
| 3254 | /// Returns pointer-to-symbol described at sym_index. | |
| 3255 | pub fn getSymbolPtr(self: *Elf, sym_index: u32) *elf.Elf64_Sym { | |
| 3256 | return &self.local_symbols.items[sym_index]; | |
| 3257 | } | |
| 3258 | ||
| 3259 | /// Returns symbol at sym_index. | |
| 3260 | pub fn getSymbol(self: *const Elf, sym_index: u32) elf.Elf64_Sym { | |
| 3261 | return self.local_symbols.items[sym_index]; | |
| 3262 | } | |
| 3263 | ||
| 3264 | /// Returns name of the symbol at sym_index. | |
| 3265 | pub fn getSymbolName(self: *const Elf, sym_index: u32) []const u8 { | |
| 3266 | const sym = self.local_symbols.items[sym_index]; | |
| 3267 | return self.shstrtab.get(sym.st_name).?; | |
| 3268 | } | |
| 3269 | ||
| 3270 | /// Returns name of the global symbol at index. | |
| 3271 | pub fn getGlobalName(self: *const Elf, index: u32) []const u8 { | |
| 3272 | const sym = self.global_symbols.items[index]; | |
| 3273 | return self.shstrtab.get(sym.st_name).?; | |
| 3274 | } | |
| 3275 | ||
| 3276 | pub fn getAtom(self: *const Elf, atom_index: Atom.Index) Atom { | |
| 3277 | assert(atom_index < self.atoms.items.len); | |
| 3278 | return self.atoms.items[atom_index]; | |
| 3279 | } | |
| 3280 | ||
| 3281 | pub fn getAtomPtr(self: *Elf, atom_index: Atom.Index) *Atom { | |
| 3282 | assert(atom_index < self.atoms.items.len); | |
| 3283 | return &self.atoms.items[atom_index]; | |
| 3284 | } | |
| 3285 | ||
| 3286 | /// Returns atom if there is an atom referenced by the symbol. | |
| 3287 | /// Returns null on failure. | |
| 3288 | pub fn getAtomIndexForSymbol(self: *Elf, sym_index: u32) ?Atom.Index { | |
| 3289 | return self.atom_by_index_table.get(sym_index); | |
| 3290 | } |
src/link/Elf/Atom.zig created+100| ... | ... | @@ -0,0 +1,100 @@ |
| 1 | const Atom = @This(); | |
| 2 | ||
| 3 | const std = @import("std"); | |
| 4 | const assert = std.debug.assert; | |
| 5 | const elf = std.elf; | |
| 6 | ||
| 7 | const Elf = @import("../Elf.zig"); | |
| 8 | ||
| 9 | /// Each decl always gets a local symbol with the fully qualified name. | |
| 10 | /// The vaddr and size are found here directly. | |
| 11 | /// The file offset is found by computing the vaddr offset from the section vaddr | |
| 12 | /// the symbol references, and adding that to the file offset of the section. | |
| 13 | /// If this field is 0, it means the codegen size = 0 and there is no symbol or | |
| 14 | /// offset table entry. | |
| 15 | local_sym_index: u32, | |
| 16 | ||
| 17 | /// This field is undefined for symbols with size = 0. | |
| 18 | offset_table_index: u32, | |
| 19 | ||
| 20 | /// Points to the previous and next neighbors, based on the `text_offset`. | |
| 21 | /// This can be used to find, for example, the capacity of this `TextBlock`. | |
| 22 | prev_index: ?Index, | |
| 23 | next_index: ?Index, | |
| 24 | ||
| 25 | pub const Index = u32; | |
| 26 | ||
| 27 | pub const Reloc = struct { | |
| 28 | target: u32, | |
| 29 | offset: u64, | |
| 30 | addend: u32, | |
| 31 | prev_vaddr: u64, | |
| 32 | }; | |
| 33 | ||
| 34 | pub fn getSymbolIndex(self: Atom) ?u32 { | |
| 35 | if (self.local_sym_index == 0) return null; | |
| 36 | return self.local_sym_index; | |
| 37 | } | |
| 38 | ||
| 39 | pub fn getSymbol(self: Atom, elf_file: *const Elf) elf.Elf64_Sym { | |
| 40 | return elf_file.getSymbol(self.getSymbolIndex().?); | |
| 41 | } | |
| 42 | ||
| 43 | pub fn getSymbolPtr(self: Atom, elf_file: *Elf) *elf.Elf64_Sym { | |
| 44 | return elf_file.getSymbolPtr(self.getSymbolIndex().?); | |
| 45 | } | |
| 46 | ||
| 47 | pub fn getName(self: Atom, elf_file: *const Elf) []const u8 { | |
| 48 | return elf_file.getSymbolName(self.getSymbolIndex().?); | |
| 49 | } | |
| 50 | ||
| 51 | pub fn getOffsetTableAddress(self: Atom, elf_file: *Elf) u64 { | |
| 52 | assert(self.getSymbolIndex() != null); | |
| 53 | const target = elf_file.base.options.target; | |
| 54 | const ptr_bits = target.cpu.arch.ptrBitWidth(); | |
| 55 | const ptr_bytes: u64 = @divExact(ptr_bits, 8); | |
| 56 | const got = elf_file.program_headers.items[elf_file.phdr_got_index.?]; | |
| 57 | return got.p_vaddr + self.offset_table_index * ptr_bytes; | |
| 58 | } | |
| 59 | ||
| 60 | /// Returns how much room there is to grow in virtual address space. | |
| 61 | /// File offset relocation happens transparently, so it is not included in | |
| 62 | /// this calculation. | |
| 63 | pub fn capacity(self: Atom, elf_file: *const Elf) u64 { | |
| 64 | const self_sym = self.getSymbol(elf_file); | |
| 65 | if (self.next_index) |next_index| { | |
| 66 | const next = elf_file.getAtom(next_index); | |
| 67 | const next_sym = next.getSymbol(elf_file); | |
| 68 | return next_sym.st_value - self_sym.st_value; | |
| 69 | } else { | |
| 70 | // We are the last block. The capacity is limited only by virtual address space. | |
| 71 | return std.math.maxInt(u32) - self_sym.st_value; | |
| 72 | } | |
| 73 | } | |
| 74 | ||
| 75 | pub fn freeListEligible(self: Atom, elf_file: *const Elf) bool { | |
| 76 | // No need to keep a free list node for the last block. | |
| 77 | const next_index = self.next_index orelse return false; | |
| 78 | const next = elf_file.getAtom(next_index); | |
| 79 | const self_sym = self.getSymbol(elf_file); | |
| 80 | const next_sym = next.getSymbol(elf_file); | |
| 81 | const cap = next_sym.st_value - self_sym.st_value; | |
| 82 | const ideal_cap = Elf.padToIdeal(self_sym.st_size); | |
| 83 | if (cap <= ideal_cap) return false; | |
| 84 | const surplus = cap - ideal_cap; | |
| 85 | return surplus >= Elf.min_text_capacity; | |
| 86 | } | |
| 87 | ||
| 88 | pub fn addRelocation(elf_file: *Elf, atom_index: Index, reloc: Reloc) !void { | |
| 89 | const gpa = elf_file.base.allocator; | |
| 90 | const gop = try elf_file.relocs.getOrPut(gpa, atom_index); | |
| 91 | if (!gop.found_existing) { | |
| 92 | gop.value_ptr.* = .{}; | |
| 93 | } | |
| 94 | try gop.value_ptr.append(gpa, reloc); | |
| 95 | } | |
| 96 | ||
| 97 | pub fn freeRelocations(elf_file: *Elf, atom_index: Index) void { | |
| 98 | var removed_relocs = elf_file.relocs.fetchRemove(atom_index); | |
| 99 | if (removed_relocs) |*relocs| relocs.value.deinit(elf_file.base.allocator); | |
| 100 | } |
src/link/MachO.zig+385-380| ... | ... | @@ -66,7 +66,7 @@ const Section = struct { |
| 66 | 66 | |
| 67 | 67 | // TODO is null here necessary, or can we do away with tracking via section |
| 68 | 68 | // size in incremental context? |
| 69 | last_atom: ?*Atom = null, | |
| 69 | last_atom_index: ?Atom.Index = null, | |
| 70 | 70 | |
| 71 | 71 | /// A list of atoms that have surplus capacity. This list can have false |
| 72 | 72 | /// positives, as functions grow and shrink over time, only sometimes being added |
| ... | ... | @@ -83,7 +83,7 @@ const Section = struct { |
| 83 | 83 | /// overcapacity can be negative. A simple way to have negative overcapacity is to |
| 84 | 84 | /// allocate a fresh atom, which will have ideal capacity, and then grow it |
| 85 | 85 | /// by 1 byte. It will then have -1 overcapacity. |
| 86 | free_list: std.ArrayListUnmanaged(*Atom) = .{}, | |
| 86 | free_list: std.ArrayListUnmanaged(Atom.Index) = .{}, | |
| 87 | 87 | }; |
| 88 | 88 | |
| 89 | 89 | base: File, |
| ... | ... | @@ -140,8 +140,8 @@ locals_free_list: std.ArrayListUnmanaged(u32) = .{}, |
| 140 | 140 | globals_free_list: std.ArrayListUnmanaged(u32) = .{}, |
| 141 | 141 | |
| 142 | 142 | dyld_stub_binder_index: ?u32 = null, |
| 143 | dyld_private_atom: ?*Atom = null, | |
| 144 | stub_helper_preamble_atom: ?*Atom = null, | |
| 143 | dyld_private_atom_index: ?Atom.Index = null, | |
| 144 | stub_helper_preamble_atom_index: ?Atom.Index = null, | |
| 145 | 145 | |
| 146 | 146 | strtab: StringTable(.strtab) = .{}, |
| 147 | 147 | |
| ... | ... | @@ -164,10 +164,10 @@ segment_table_dirty: bool = false, |
| 164 | 164 | cold_start: bool = true, |
| 165 | 165 | |
| 166 | 166 | /// List of atoms that are either synthetic or map directly to the Zig source program. |
| 167 | managed_atoms: std.ArrayListUnmanaged(*Atom) = .{}, | |
| 167 | atoms: std.ArrayListUnmanaged(Atom) = .{}, | |
| 168 | 168 | |
| 169 | 169 | /// Table of atoms indexed by the symbol index. |
| 170 | atom_by_index_table: std.AutoHashMapUnmanaged(u32, *Atom) = .{}, | |
| 170 | atom_by_index_table: std.AutoHashMapUnmanaged(u32, Atom.Index) = .{}, | |
| 171 | 171 | |
| 172 | 172 | /// Table of unnamed constants associated with a parent `Decl`. |
| 173 | 173 | /// We store them here so that we can free the constants whenever the `Decl` |
| ... | ... | @@ -210,11 +210,36 @@ bindings: BindingTable = .{}, |
| 210 | 210 | /// this will be a table indexed by index into the list of Atoms. |
| 211 | 211 | lazy_bindings: BindingTable = .{}, |
| 212 | 212 | |
| 213 | /// Table of Decls that are currently alive. | |
| 214 | /// We store them here so that we can properly dispose of any allocated | |
| 215 | /// memory within the atom in the incremental linker. | |
| 216 | /// TODO consolidate this. | |
| 217 | decls: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, ?u8) = .{}, | |
| 213 | /// Table of tracked Decls. | |
| 214 | decls: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, DeclMetadata) = .{}, | |
| 215 | ||
| 216 | const DeclMetadata = struct { | |
| 217 | atom: Atom.Index, | |
| 218 | section: u8, | |
| 219 | /// A list of all exports aliases of this Decl. | |
| 220 | /// TODO do we actually need this at all? | |
| 221 | exports: std.ArrayListUnmanaged(u32) = .{}, | |
| 222 | ||
| 223 | fn getExport(m: DeclMetadata, macho_file: *const MachO, name: []const u8) ?u32 { | |
| 224 | for (m.exports.items) |exp| { | |
| 225 | if (mem.eql(u8, name, macho_file.getSymbolName(.{ | |
| 226 | .sym_index = exp, | |
| 227 | .file = null, | |
| 228 | }))) return exp; | |
| 229 | } | |
| 230 | return null; | |
| 231 | } | |
| 232 | ||
| 233 | fn getExportPtr(m: *DeclMetadata, macho_file: *MachO, name: []const u8) ?*u32 { | |
| 234 | for (m.exports.items) |*exp| { | |
| 235 | if (mem.eql(u8, name, macho_file.getSymbolName(.{ | |
| 236 | .sym_index = exp.*, | |
| 237 | .file = null, | |
| 238 | }))) return exp; | |
| 239 | } | |
| 240 | return null; | |
| 241 | } | |
| 242 | }; | |
| 218 | 243 | |
| 219 | 244 | const Entry = struct { |
| 220 | 245 | target: SymbolWithLoc, |
| ... | ... | @@ -229,8 +254,8 @@ const Entry = struct { |
| 229 | 254 | return macho_file.getSymbolPtr(.{ .sym_index = entry.sym_index, .file = null }); |
| 230 | 255 | } |
| 231 | 256 | |
| 232 | pub fn getAtom(entry: Entry, macho_file: *MachO) ?*Atom { | |
| 233 | return macho_file.getAtomForSymbol(.{ .sym_index = entry.sym_index, .file = null }); | |
| 257 | pub fn getAtomIndex(entry: Entry, macho_file: *MachO) ?Atom.Index { | |
| 258 | return macho_file.getAtomIndexForSymbol(.{ .sym_index = entry.sym_index, .file = null }); | |
| 234 | 259 | } |
| 235 | 260 | |
| 236 | 261 | pub fn getName(entry: Entry, macho_file: *MachO) []const u8 { |
| ... | ... | @@ -238,10 +263,10 @@ const Entry = struct { |
| 238 | 263 | } |
| 239 | 264 | }; |
| 240 | 265 | |
| 241 | const BindingTable = std.AutoArrayHashMapUnmanaged(*Atom, std.ArrayListUnmanaged(Atom.Binding)); | |
| 242 | const UnnamedConstTable = std.AutoArrayHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(*Atom)); | |
| 243 | const RebaseTable = std.AutoArrayHashMapUnmanaged(*Atom, std.ArrayListUnmanaged(u32)); | |
| 244 | const RelocationTable = std.AutoArrayHashMapUnmanaged(*Atom, std.ArrayListUnmanaged(Relocation)); | |
| 266 | const BindingTable = std.AutoArrayHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(Atom.Binding)); | |
| 267 | const UnnamedConstTable = std.AutoArrayHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(Atom.Index)); | |
| 268 | const RebaseTable = std.AutoArrayHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(u32)); | |
| 269 | const RelocationTable = std.AutoArrayHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(Relocation)); | |
| 245 | 270 | |
| 246 | 271 | const PendingUpdate = union(enum) { |
| 247 | 272 | resolve_undef: u32, |
| ... | ... | @@ -286,10 +311,6 @@ pub const default_pagezero_vmsize: u64 = 0x100000000; |
| 286 | 311 | /// potential future extensions. |
| 287 | 312 | pub const default_headerpad_size: u32 = 0x1000; |
| 288 | 313 | |
| 289 | pub const Export = struct { | |
| 290 | sym_index: ?u32 = null, | |
| 291 | }; | |
| 292 | ||
| 293 | 314 | pub fn openPath(allocator: Allocator, options: link.Options) !*MachO { |
| 294 | 315 | assert(options.target.ofmt == .macho); |
| 295 | 316 | |
| ... | ... | @@ -547,8 +568,8 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No |
| 547 | 568 | |
| 548 | 569 | try self.allocateSpecialSymbols(); |
| 549 | 570 | |
| 550 | for (self.relocs.keys()) |atom| { | |
| 551 | try atom.resolveRelocations(self); | |
| 571 | for (self.relocs.keys()) |atom_index| { | |
| 572 | try Atom.resolveRelocations(self, atom_index); | |
| 552 | 573 | } |
| 553 | 574 | |
| 554 | 575 | if (build_options.enable_logging) { |
| ... | ... | @@ -999,18 +1020,19 @@ pub fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs: |
| 999 | 1020 | } |
| 1000 | 1021 | } |
| 1001 | 1022 | |
| 1002 | pub fn writeAtom(self: *MachO, atom: *Atom, code: []const u8) !void { | |
| 1023 | pub fn writeAtom(self: *MachO, atom_index: Atom.Index, code: []const u8) !void { | |
| 1024 | const atom = self.getAtom(atom_index); | |
| 1003 | 1025 | const sym = atom.getSymbol(self); |
| 1004 | 1026 | const section = self.sections.get(sym.n_sect - 1); |
| 1005 | 1027 | const file_offset = section.header.offset + sym.n_value - section.header.addr; |
| 1006 | 1028 | log.debug("writing atom for symbol {s} at file offset 0x{x}", .{ atom.getName(self), file_offset }); |
| 1007 | 1029 | try self.base.file.?.pwriteAll(code, file_offset); |
| 1008 | try atom.resolveRelocations(self); | |
| 1030 | try Atom.resolveRelocations(self, atom_index); | |
| 1009 | 1031 | } |
| 1010 | 1032 | |
| 1011 | fn writePtrWidthAtom(self: *MachO, atom: *Atom) !void { | |
| 1033 | fn writePtrWidthAtom(self: *MachO, atom_index: Atom.Index) !void { | |
| 1012 | 1034 | var buffer: [@sizeOf(u64)]u8 = [_]u8{0} ** @sizeOf(u64); |
| 1013 | try self.writeAtom(atom, &buffer); | |
| 1035 | try self.writeAtom(atom_index, &buffer); | |
| 1014 | 1036 | } |
| 1015 | 1037 | |
| 1016 | 1038 | fn markRelocsDirtyByTarget(self: *MachO, target: SymbolWithLoc) void { |
| ... | ... | @@ -1026,7 +1048,8 @@ fn markRelocsDirtyByTarget(self: *MachO, target: SymbolWithLoc) void { |
| 1026 | 1048 | fn markRelocsDirtyByAddress(self: *MachO, addr: u64) void { |
| 1027 | 1049 | for (self.relocs.values()) |*relocs| { |
| 1028 | 1050 | for (relocs.items) |*reloc| { |
| 1029 | const target_atom = reloc.getTargetAtom(self) orelse continue; | |
| 1051 | const target_atom_index = reloc.getTargetAtomIndex(self) orelse continue; | |
| 1052 | const target_atom = self.getAtom(target_atom_index); | |
| 1030 | 1053 | const target_sym = target_atom.getSymbol(self); |
| 1031 | 1054 | if (target_sym.n_value < addr) continue; |
| 1032 | 1055 | reloc.dirty = true; |
| ... | ... | @@ -1053,31 +1076,38 @@ pub fn allocateSpecialSymbols(self: *MachO) !void { |
| 1053 | 1076 | } |
| 1054 | 1077 | } |
| 1055 | 1078 | |
| 1056 | pub fn createGotAtom(self: *MachO, target: SymbolWithLoc) !*Atom { | |
| 1079 | pub fn createAtom(self: *MachO) !Atom.Index { | |
| 1057 | 1080 | const gpa = self.base.allocator; |
| 1058 | ||
| 1081 | const atom_index = @intCast(Atom.Index, self.atoms.items.len); | |
| 1082 | const atom = try self.atoms.addOne(gpa); | |
| 1059 | 1083 | const sym_index = try self.allocateSymbol(); |
| 1060 | const atom = blk: { | |
| 1061 | const atom = try gpa.create(Atom); | |
| 1062 | atom.* = Atom.empty; | |
| 1063 | atom.sym_index = sym_index; | |
| 1064 | atom.size = @sizeOf(u64); | |
| 1065 | atom.alignment = @alignOf(u64); | |
| 1066 | break :blk atom; | |
| 1084 | try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom_index); | |
| 1085 | atom.* = .{ | |
| 1086 | .sym_index = sym_index, | |
| 1087 | .file = null, | |
| 1088 | .size = 0, | |
| 1089 | .alignment = 0, | |
| 1090 | .prev_index = null, | |
| 1091 | .next_index = null, | |
| 1067 | 1092 | }; |
| 1068 | errdefer gpa.destroy(atom); | |
| 1093 | log.debug("creating ATOM(%{d}) at index {d}", .{ sym_index, atom_index }); | |
| 1094 | return atom_index; | |
| 1095 | } | |
| 1069 | 1096 | |
| 1070 | try self.managed_atoms.append(gpa, atom); | |
| 1071 | try self.atom_by_index_table.putNoClobber(gpa, atom.sym_index, atom); | |
| 1097 | pub fn createGotAtom(self: *MachO, target: SymbolWithLoc) !Atom.Index { | |
| 1098 | const atom_index = try self.createAtom(); | |
| 1099 | const atom = self.getAtomPtr(atom_index); | |
| 1100 | atom.size = @sizeOf(u64); | |
| 1101 | atom.alignment = @alignOf(u64); | |
| 1072 | 1102 | |
| 1073 | 1103 | const sym = atom.getSymbolPtr(self); |
| 1074 | 1104 | sym.n_type = macho.N_SECT; |
| 1075 | 1105 | sym.n_sect = self.got_section_index.? + 1; |
| 1076 | sym.n_value = try self.allocateAtom(atom, atom.size, @alignOf(u64)); | |
| 1106 | sym.n_value = try self.allocateAtom(atom_index, atom.size, @alignOf(u64)); | |
| 1077 | 1107 | |
| 1078 | 1108 | log.debug("allocated GOT atom at 0x{x}", .{sym.n_value}); |
| 1079 | 1109 | |
| 1080 | try atom.addRelocation(self, .{ | |
| 1110 | try Atom.addRelocation(self, atom_index, .{ | |
| 1081 | 1111 | .type = switch (self.base.options.target.cpu.arch) { |
| 1082 | 1112 | .aarch64 => @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED), |
| 1083 | 1113 | .x86_64 => @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED), |
| ... | ... | @@ -1092,50 +1122,39 @@ pub fn createGotAtom(self: *MachO, target: SymbolWithLoc) !*Atom { |
| 1092 | 1122 | |
| 1093 | 1123 | const target_sym = self.getSymbol(target); |
| 1094 | 1124 | if (target_sym.undf()) { |
| 1095 | try atom.addBinding(self, .{ | |
| 1125 | try Atom.addBinding(self, atom_index, .{ | |
| 1096 | 1126 | .target = self.getGlobal(self.getSymbolName(target)).?, |
| 1097 | 1127 | .offset = 0, |
| 1098 | 1128 | }); |
| 1099 | 1129 | } else { |
| 1100 | try atom.addRebase(self, 0); | |
| 1130 | try Atom.addRebase(self, atom_index, 0); | |
| 1101 | 1131 | } |
| 1102 | 1132 | |
| 1103 | return atom; | |
| 1133 | return atom_index; | |
| 1104 | 1134 | } |
| 1105 | 1135 | |
| 1106 | 1136 | pub fn createDyldPrivateAtom(self: *MachO) !void { |
| 1107 | 1137 | if (self.dyld_stub_binder_index == null) return; |
| 1108 | if (self.dyld_private_atom != null) return; | |
| 1109 | ||
| 1110 | const gpa = self.base.allocator; | |
| 1138 | if (self.dyld_private_atom_index != null) return; | |
| 1111 | 1139 | |
| 1112 | const sym_index = try self.allocateSymbol(); | |
| 1113 | const atom = blk: { | |
| 1114 | const atom = try gpa.create(Atom); | |
| 1115 | atom.* = Atom.empty; | |
| 1116 | atom.sym_index = sym_index; | |
| 1117 | atom.size = @sizeOf(u64); | |
| 1118 | atom.alignment = @alignOf(u64); | |
| 1119 | break :blk atom; | |
| 1120 | }; | |
| 1121 | errdefer gpa.destroy(atom); | |
| 1140 | const atom_index = try self.createAtom(); | |
| 1141 | const atom = self.getAtomPtr(atom_index); | |
| 1142 | atom.size = @sizeOf(u64); | |
| 1143 | atom.alignment = @alignOf(u64); | |
| 1122 | 1144 | |
| 1123 | 1145 | const sym = atom.getSymbolPtr(self); |
| 1124 | 1146 | sym.n_type = macho.N_SECT; |
| 1125 | 1147 | sym.n_sect = self.data_section_index.? + 1; |
| 1126 | self.dyld_private_atom = atom; | |
| 1148 | self.dyld_private_atom_index = atom_index; | |
| 1127 | 1149 | |
| 1128 | try self.managed_atoms.append(gpa, atom); | |
| 1129 | try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom); | |
| 1130 | ||
| 1131 | sym.n_value = try self.allocateAtom(atom, atom.size, @alignOf(u64)); | |
| 1150 | sym.n_value = try self.allocateAtom(atom_index, atom.size, @alignOf(u64)); | |
| 1132 | 1151 | log.debug("allocated dyld_private atom at 0x{x}", .{sym.n_value}); |
| 1133 | try self.writePtrWidthAtom(atom); | |
| 1152 | try self.writePtrWidthAtom(atom_index); | |
| 1134 | 1153 | } |
| 1135 | 1154 | |
| 1136 | 1155 | pub fn createStubHelperPreambleAtom(self: *MachO) !void { |
| 1137 | 1156 | if (self.dyld_stub_binder_index == null) return; |
| 1138 | if (self.stub_helper_preamble_atom != null) return; | |
| 1157 | if (self.stub_helper_preamble_atom_index != null) return; | |
| 1139 | 1158 | |
| 1140 | 1159 | const gpa = self.base.allocator; |
| 1141 | 1160 | const arch = self.base.options.target.cpu.arch; |
| ... | ... | @@ -1144,26 +1163,23 @@ pub fn createStubHelperPreambleAtom(self: *MachO) !void { |
| 1144 | 1163 | .aarch64 => 6 * @sizeOf(u32), |
| 1145 | 1164 | else => unreachable, |
| 1146 | 1165 | }; |
| 1147 | const sym_index = try self.allocateSymbol(); | |
| 1148 | const atom = blk: { | |
| 1149 | const atom = try gpa.create(Atom); | |
| 1150 | atom.* = Atom.empty; | |
| 1151 | atom.sym_index = sym_index; | |
| 1152 | atom.size = size; | |
| 1153 | atom.alignment = switch (arch) { | |
| 1154 | .x86_64 => 1, | |
| 1155 | .aarch64 => @alignOf(u32), | |
| 1156 | else => unreachable, | |
| 1157 | }; | |
| 1158 | break :blk atom; | |
| 1166 | const atom_index = try self.createAtom(); | |
| 1167 | const atom = self.getAtomPtr(atom_index); | |
| 1168 | atom.size = size; | |
| 1169 | atom.alignment = switch (arch) { | |
| 1170 | .x86_64 => 1, | |
| 1171 | .aarch64 => @alignOf(u32), | |
| 1172 | else => unreachable, | |
| 1159 | 1173 | }; |
| 1160 | errdefer gpa.destroy(atom); | |
| 1161 | 1174 | |
| 1162 | 1175 | const sym = atom.getSymbolPtr(self); |
| 1163 | 1176 | sym.n_type = macho.N_SECT; |
| 1164 | 1177 | sym.n_sect = self.stub_helper_section_index.? + 1; |
| 1165 | 1178 | |
| 1166 | const dyld_private_sym_index = self.dyld_private_atom.?.sym_index; | |
| 1179 | const dyld_private_sym_index = if (self.dyld_private_atom_index) |dyld_index| | |
| 1180 | self.getAtom(dyld_index).getSymbolIndex().? | |
| 1181 | else | |
| 1182 | unreachable; | |
| 1167 | 1183 | |
| 1168 | 1184 | const code = try gpa.alloc(u8, size); |
| 1169 | 1185 | defer gpa.free(code); |
| ... | ... | @@ -1182,7 +1198,7 @@ pub fn createStubHelperPreambleAtom(self: *MachO) !void { |
| 1182 | 1198 | code[9] = 0xff; |
| 1183 | 1199 | code[10] = 0x25; |
| 1184 | 1200 | |
| 1185 | try atom.addRelocations(self, 2, .{ .{ | |
| 1201 | try Atom.addRelocations(self, atom_index, 2, .{ .{ | |
| 1186 | 1202 | .type = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_SIGNED), |
| 1187 | 1203 | .target = .{ .sym_index = dyld_private_sym_index, .file = null }, |
| 1188 | 1204 | .offset = 3, |
| ... | ... | @@ -1222,7 +1238,7 @@ pub fn createStubHelperPreambleAtom(self: *MachO) !void { |
| 1222 | 1238 | // br x16 |
| 1223 | 1239 | mem.writeIntLittle(u32, code[20..][0..4], aarch64.Instruction.br(.x16).toU32()); |
| 1224 | 1240 | |
| 1225 | try atom.addRelocations(self, 4, .{ .{ | |
| 1241 | try Atom.addRelocations(self, atom_index, 4, .{ .{ | |
| 1226 | 1242 | .type = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGE21), |
| 1227 | 1243 | .target = .{ .sym_index = dyld_private_sym_index, .file = null }, |
| 1228 | 1244 | .offset = 0, |
| ... | ... | @@ -1255,17 +1271,14 @@ pub fn createStubHelperPreambleAtom(self: *MachO) !void { |
| 1255 | 1271 | |
| 1256 | 1272 | else => unreachable, |
| 1257 | 1273 | } |
| 1258 | self.stub_helper_preamble_atom = atom; | |
| 1259 | ||
| 1260 | try self.managed_atoms.append(gpa, atom); | |
| 1261 | try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom); | |
| 1274 | self.stub_helper_preamble_atom_index = atom_index; | |
| 1262 | 1275 | |
| 1263 | sym.n_value = try self.allocateAtom(atom, size, atom.alignment); | |
| 1276 | sym.n_value = try self.allocateAtom(atom_index, size, atom.alignment); | |
| 1264 | 1277 | log.debug("allocated stub preamble atom at 0x{x}", .{sym.n_value}); |
| 1265 | try self.writeAtom(atom, code); | |
| 1278 | try self.writeAtom(atom_index, code); | |
| 1266 | 1279 | } |
| 1267 | 1280 | |
| 1268 | pub fn createStubHelperAtom(self: *MachO) !*Atom { | |
| 1281 | pub fn createStubHelperAtom(self: *MachO) !Atom.Index { | |
| 1269 | 1282 | const gpa = self.base.allocator; |
| 1270 | 1283 | const arch = self.base.options.target.cpu.arch; |
| 1271 | 1284 | const size: u4 = switch (arch) { |
| ... | ... | @@ -1273,20 +1286,14 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom { |
| 1273 | 1286 | .aarch64 => 3 * @sizeOf(u32), |
| 1274 | 1287 | else => unreachable, |
| 1275 | 1288 | }; |
| 1276 | const sym_index = try self.allocateSymbol(); | |
| 1277 | const atom = blk: { | |
| 1278 | const atom = try gpa.create(Atom); | |
| 1279 | atom.* = Atom.empty; | |
| 1280 | atom.sym_index = sym_index; | |
| 1281 | atom.size = size; | |
| 1282 | atom.alignment = switch (arch) { | |
| 1283 | .x86_64 => 1, | |
| 1284 | .aarch64 => @alignOf(u32), | |
| 1285 | else => unreachable, | |
| 1286 | }; | |
| 1287 | break :blk atom; | |
| 1289 | const atom_index = try self.createAtom(); | |
| 1290 | const atom = self.getAtomPtr(atom_index); | |
| 1291 | atom.size = size; | |
| 1292 | atom.alignment = switch (arch) { | |
| 1293 | .x86_64 => 1, | |
| 1294 | .aarch64 => @alignOf(u32), | |
| 1295 | else => unreachable, | |
| 1288 | 1296 | }; |
| 1289 | errdefer gpa.destroy(atom); | |
| 1290 | 1297 | |
| 1291 | 1298 | const sym = atom.getSymbolPtr(self); |
| 1292 | 1299 | sym.n_type = macho.N_SECT; |
| ... | ... | @@ -1296,6 +1303,11 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom { |
| 1296 | 1303 | defer gpa.free(code); |
| 1297 | 1304 | mem.set(u8, code, 0); |
| 1298 | 1305 | |
| 1306 | const stub_helper_preamble_atom_sym_index = if (self.stub_helper_preamble_atom_index) |stub_index| | |
| 1307 | self.getAtom(stub_index).getSymbolIndex().? | |
| 1308 | else | |
| 1309 | unreachable; | |
| 1310 | ||
| 1299 | 1311 | switch (arch) { |
| 1300 | 1312 | .x86_64 => { |
| 1301 | 1313 | // pushq |
| ... | ... | @@ -1304,9 +1316,9 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom { |
| 1304 | 1316 | // jmpq |
| 1305 | 1317 | code[5] = 0xe9; |
| 1306 | 1318 | |
| 1307 | try atom.addRelocation(self, .{ | |
| 1319 | try Atom.addRelocation(self, atom_index, .{ | |
| 1308 | 1320 | .type = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_BRANCH), |
| 1309 | .target = .{ .sym_index = self.stub_helper_preamble_atom.?.sym_index, .file = null }, | |
| 1321 | .target = .{ .sym_index = stub_helper_preamble_atom_sym_index, .file = null }, | |
| 1310 | 1322 | .offset = 6, |
| 1311 | 1323 | .addend = 0, |
| 1312 | 1324 | .pcrel = true, |
| ... | ... | @@ -1327,9 +1339,9 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom { |
| 1327 | 1339 | mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.b(0).toU32()); |
| 1328 | 1340 | // Next 4 bytes 8..12 are just a placeholder populated in `populateLazyBindOffsetsInStubHelper`. |
| 1329 | 1341 | |
| 1330 | try atom.addRelocation(self, .{ | |
| 1342 | try Atom.addRelocation(self, atom_index, .{ | |
| 1331 | 1343 | .type = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_BRANCH26), |
| 1332 | .target = .{ .sym_index = self.stub_helper_preamble_atom.?.sym_index, .file = null }, | |
| 1344 | .target = .{ .sym_index = stub_helper_preamble_atom_sym_index, .file = null }, | |
| 1333 | 1345 | .offset = 4, |
| 1334 | 1346 | .addend = 0, |
| 1335 | 1347 | .pcrel = true, |
| ... | ... | @@ -1339,34 +1351,24 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom { |
| 1339 | 1351 | else => unreachable, |
| 1340 | 1352 | } |
| 1341 | 1353 | |
| 1342 | try self.managed_atoms.append(gpa, atom); | |
| 1343 | try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom); | |
| 1344 | ||
| 1345 | sym.n_value = try self.allocateAtom(atom, size, atom.alignment); | |
| 1354 | sym.n_value = try self.allocateAtom(atom_index, size, atom.alignment); | |
| 1346 | 1355 | log.debug("allocated stub helper atom at 0x{x}", .{sym.n_value}); |
| 1347 | try self.writeAtom(atom, code); | |
| 1356 | try self.writeAtom(atom_index, code); | |
| 1348 | 1357 | |
| 1349 | return atom; | |
| 1358 | return atom_index; | |
| 1350 | 1359 | } |
| 1351 | 1360 | |
| 1352 | pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, target: SymbolWithLoc) !*Atom { | |
| 1353 | const gpa = self.base.allocator; | |
| 1354 | const sym_index = try self.allocateSymbol(); | |
| 1355 | const atom = blk: { | |
| 1356 | const atom = try gpa.create(Atom); | |
| 1357 | atom.* = Atom.empty; | |
| 1358 | atom.sym_index = sym_index; | |
| 1359 | atom.size = @sizeOf(u64); | |
| 1360 | atom.alignment = @alignOf(u64); | |
| 1361 | break :blk atom; | |
| 1362 | }; | |
| 1363 | errdefer gpa.destroy(atom); | |
| 1361 | pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, target: SymbolWithLoc) !Atom.Index { | |
| 1362 | const atom_index = try self.createAtom(); | |
| 1363 | const atom = self.getAtomPtr(atom_index); | |
| 1364 | atom.size = @sizeOf(u64); | |
| 1365 | atom.alignment = @alignOf(u64); | |
| 1364 | 1366 | |
| 1365 | 1367 | const sym = atom.getSymbolPtr(self); |
| 1366 | 1368 | sym.n_type = macho.N_SECT; |
| 1367 | 1369 | sym.n_sect = self.la_symbol_ptr_section_index.? + 1; |
| 1368 | 1370 | |
| 1369 | try atom.addRelocation(self, .{ | |
| 1371 | try Atom.addRelocation(self, atom_index, .{ | |
| 1370 | 1372 | .type = switch (self.base.options.target.cpu.arch) { |
| 1371 | 1373 | .aarch64 => @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED), |
| 1372 | 1374 | .x86_64 => @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED), |
| ... | ... | @@ -1378,23 +1380,20 @@ pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, target: SymbolWi |
| 1378 | 1380 | .pcrel = false, |
| 1379 | 1381 | .length = 3, |
| 1380 | 1382 | }); |
| 1381 | try atom.addRebase(self, 0); | |
| 1382 | try atom.addLazyBinding(self, .{ | |
| 1383 | try Atom.addRebase(self, atom_index, 0); | |
| 1384 | try Atom.addLazyBinding(self, atom_index, .{ | |
| 1383 | 1385 | .target = self.getGlobal(self.getSymbolName(target)).?, |
| 1384 | 1386 | .offset = 0, |
| 1385 | 1387 | }); |
| 1386 | 1388 | |
| 1387 | try self.managed_atoms.append(gpa, atom); | |
| 1388 | try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom); | |
| 1389 | ||
| 1390 | sym.n_value = try self.allocateAtom(atom, atom.size, @alignOf(u64)); | |
| 1389 | sym.n_value = try self.allocateAtom(atom_index, atom.size, @alignOf(u64)); | |
| 1391 | 1390 | log.debug("allocated lazy pointer atom at 0x{x} ({s})", .{ sym.n_value, self.getSymbolName(target) }); |
| 1392 | try self.writePtrWidthAtom(atom); | |
| 1391 | try self.writePtrWidthAtom(atom_index); | |
| 1393 | 1392 | |
| 1394 | return atom; | |
| 1393 | return atom_index; | |
| 1395 | 1394 | } |
| 1396 | 1395 | |
| 1397 | pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom { | |
| 1396 | pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !Atom.Index { | |
| 1398 | 1397 | const gpa = self.base.allocator; |
| 1399 | 1398 | const arch = self.base.options.target.cpu.arch; |
| 1400 | 1399 | const size: u4 = switch (arch) { |
| ... | ... | @@ -1402,21 +1401,15 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom { |
| 1402 | 1401 | .aarch64 => 3 * @sizeOf(u32), |
| 1403 | 1402 | else => unreachable, // unhandled architecture type |
| 1404 | 1403 | }; |
| 1405 | const sym_index = try self.allocateSymbol(); | |
| 1406 | const atom = blk: { | |
| 1407 | const atom = try gpa.create(Atom); | |
| 1408 | atom.* = Atom.empty; | |
| 1409 | atom.sym_index = sym_index; | |
| 1410 | atom.size = size; | |
| 1411 | atom.alignment = switch (arch) { | |
| 1412 | .x86_64 => 1, | |
| 1413 | .aarch64 => @alignOf(u32), | |
| 1414 | else => unreachable, // unhandled architecture type | |
| 1404 | const atom_index = try self.createAtom(); | |
| 1405 | const atom = self.getAtomPtr(atom_index); | |
| 1406 | atom.size = size; | |
| 1407 | atom.alignment = switch (arch) { | |
| 1408 | .x86_64 => 1, | |
| 1409 | .aarch64 => @alignOf(u32), | |
| 1410 | else => unreachable, // unhandled architecture type | |
| 1415 | 1411 | |
| 1416 | }; | |
| 1417 | break :blk atom; | |
| 1418 | 1412 | }; |
| 1419 | errdefer gpa.destroy(atom); | |
| 1420 | 1413 | |
| 1421 | 1414 | const sym = atom.getSymbolPtr(self); |
| 1422 | 1415 | sym.n_type = macho.N_SECT; |
| ... | ... | @@ -1432,7 +1425,7 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom { |
| 1432 | 1425 | code[0] = 0xff; |
| 1433 | 1426 | code[1] = 0x25; |
| 1434 | 1427 | |
| 1435 | try atom.addRelocation(self, .{ | |
| 1428 | try Atom.addRelocation(self, atom_index, .{ | |
| 1436 | 1429 | .type = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_BRANCH), |
| 1437 | 1430 | .target = .{ .sym_index = laptr_sym_index, .file = null }, |
| 1438 | 1431 | .offset = 2, |
| ... | ... | @@ -1453,7 +1446,7 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom { |
| 1453 | 1446 | // br x16 |
| 1454 | 1447 | mem.writeIntLittle(u32, code[8..12], aarch64.Instruction.br(.x16).toU32()); |
| 1455 | 1448 | |
| 1456 | try atom.addRelocations(self, 2, .{ | |
| 1449 | try Atom.addRelocations(self, atom_index, 2, .{ | |
| 1457 | 1450 | .{ |
| 1458 | 1451 | .type = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGE21), |
| 1459 | 1452 | .target = .{ .sym_index = laptr_sym_index, .file = null }, |
| ... | ... | @@ -1475,14 +1468,11 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom { |
| 1475 | 1468 | else => unreachable, |
| 1476 | 1469 | } |
| 1477 | 1470 | |
| 1478 | try self.managed_atoms.append(gpa, atom); | |
| 1479 | try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom); | |
| 1480 | ||
| 1481 | sym.n_value = try self.allocateAtom(atom, size, atom.alignment); | |
| 1471 | sym.n_value = try self.allocateAtom(atom_index, size, atom.alignment); | |
| 1482 | 1472 | log.debug("allocated stub atom at 0x{x}", .{sym.n_value}); |
| 1483 | try self.writeAtom(atom, code); | |
| 1473 | try self.writeAtom(atom_index, code); | |
| 1484 | 1474 | |
| 1485 | return atom; | |
| 1475 | return atom_index; | |
| 1486 | 1476 | } |
| 1487 | 1477 | |
| 1488 | 1478 | pub fn createMhExecuteHeaderSymbol(self: *MachO) !void { |
| ... | ... | @@ -1616,10 +1606,13 @@ pub fn resolveSymbolsInDylibs(self: *MachO) !void { |
| 1616 | 1606 | if (self.stubs_table.contains(global)) break :blk; |
| 1617 | 1607 | |
| 1618 | 1608 | const stub_index = try self.allocateStubEntry(global); |
| 1619 | const stub_helper_atom = try self.createStubHelperAtom(); | |
| 1620 | const laptr_atom = try self.createLazyPointerAtom(stub_helper_atom.sym_index, global); | |
| 1621 | const stub_atom = try self.createStubAtom(laptr_atom.sym_index); | |
| 1622 | self.stubs.items[stub_index].sym_index = stub_atom.sym_index; | |
| 1609 | const stub_helper_atom_index = try self.createStubHelperAtom(); | |
| 1610 | const stub_helper_atom = self.getAtom(stub_helper_atom_index); | |
| 1611 | const laptr_atom_index = try self.createLazyPointerAtom(stub_helper_atom.getSymbolIndex().?, global); | |
| 1612 | const laptr_atom = self.getAtom(laptr_atom_index); | |
| 1613 | const stub_atom_index = try self.createStubAtom(laptr_atom.getSymbolIndex().?); | |
| 1614 | const stub_atom = self.getAtom(stub_atom_index); | |
| 1615 | self.stubs.items[stub_index].sym_index = stub_atom.getSymbolIndex().?; | |
| 1623 | 1616 | self.markRelocsDirtyByTarget(global); |
| 1624 | 1617 | } |
| 1625 | 1618 | |
| ... | ... | @@ -1716,10 +1709,11 @@ pub fn resolveDyldStubBinder(self: *MachO) !void { |
| 1716 | 1709 | |
| 1717 | 1710 | // Add dyld_stub_binder as the final GOT entry. |
| 1718 | 1711 | const got_index = try self.allocateGotEntry(global); |
| 1719 | const got_atom = try self.createGotAtom(global); | |
| 1720 | self.got_entries.items[got_index].sym_index = got_atom.sym_index; | |
| 1712 | const got_atom_index = try self.createGotAtom(global); | |
| 1713 | const got_atom = self.getAtom(got_atom_index); | |
| 1714 | self.got_entries.items[got_index].sym_index = got_atom.getSymbolIndex().?; | |
| 1721 | 1715 | |
| 1722 | try self.writePtrWidthAtom(got_atom); | |
| 1716 | try self.writePtrWidthAtom(got_atom_index); | |
| 1723 | 1717 | } |
| 1724 | 1718 | |
| 1725 | 1719 | pub fn deinit(self: *MachO) void { |
| ... | ... | @@ -1769,12 +1763,12 @@ pub fn deinit(self: *MachO) void { |
| 1769 | 1763 | } |
| 1770 | 1764 | self.sections.deinit(gpa); |
| 1771 | 1765 | |
| 1772 | for (self.managed_atoms.items) |atom| { | |
| 1773 | gpa.destroy(atom); | |
| 1774 | } | |
| 1775 | self.managed_atoms.deinit(gpa); | |
| 1766 | self.atoms.deinit(gpa); | |
| 1776 | 1767 | |
| 1777 | 1768 | if (self.base.options.module) |_| { |
| 1769 | for (self.decls.values()) |*m| { | |
| 1770 | m.exports.deinit(gpa); | |
| 1771 | } | |
| 1778 | 1772 | self.decls.deinit(gpa); |
| 1779 | 1773 | } else { |
| 1780 | 1774 | assert(self.decls.count() == 0); |
| ... | ... | @@ -1808,12 +1802,14 @@ pub fn deinit(self: *MachO) void { |
| 1808 | 1802 | self.lazy_bindings.deinit(gpa); |
| 1809 | 1803 | } |
| 1810 | 1804 | |
| 1811 | fn freeAtom(self: *MachO, atom: *Atom) void { | |
| 1812 | log.debug("freeAtom {*}", .{atom}); | |
| 1805 | fn freeAtom(self: *MachO, atom_index: Atom.Index) void { | |
| 1806 | const gpa = self.base.allocator; | |
| 1807 | log.debug("freeAtom {d}", .{atom_index}); | |
| 1813 | 1808 | |
| 1814 | 1809 | // Remove any relocs and base relocs associated with this Atom |
| 1815 | self.freeRelocationsForAtom(atom); | |
| 1810 | Atom.freeRelocations(self, atom_index); | |
| 1816 | 1811 | |
| 1812 | const atom = self.getAtom(atom_index); | |
| 1817 | 1813 | const sect_id = atom.getSymbol(self).n_sect - 1; |
| 1818 | 1814 | const free_list = &self.sections.items(.free_list)[sect_id]; |
| 1819 | 1815 | var already_have_free_list_node = false; |
| ... | ... | @@ -1821,69 +1817,94 @@ fn freeAtom(self: *MachO, atom: *Atom) void { |
| 1821 | 1817 | var i: usize = 0; |
| 1822 | 1818 | // TODO turn free_list into a hash map |
| 1823 | 1819 | while (i < free_list.items.len) { |
| 1824 | if (free_list.items[i] == atom) { | |
| 1820 | if (free_list.items[i] == atom_index) { | |
| 1825 | 1821 | _ = free_list.swapRemove(i); |
| 1826 | 1822 | continue; |
| 1827 | 1823 | } |
| 1828 | if (free_list.items[i] == atom.prev) { | |
| 1824 | if (free_list.items[i] == atom.prev_index) { | |
| 1829 | 1825 | already_have_free_list_node = true; |
| 1830 | 1826 | } |
| 1831 | 1827 | i += 1; |
| 1832 | 1828 | } |
| 1833 | 1829 | } |
| 1834 | 1830 | |
| 1835 | const maybe_last_atom = &self.sections.items(.last_atom)[sect_id]; | |
| 1836 | if (maybe_last_atom.*) |last_atom| { | |
| 1837 | if (last_atom == atom) { | |
| 1838 | if (atom.prev) |prev| { | |
| 1831 | const maybe_last_atom_index = &self.sections.items(.last_atom_index)[sect_id]; | |
| 1832 | if (maybe_last_atom_index.*) |last_atom_index| { | |
| 1833 | if (last_atom_index == atom_index) { | |
| 1834 | if (atom.prev_index) |prev_index| { | |
| 1839 | 1835 | // TODO shrink the section size here |
| 1840 | maybe_last_atom.* = prev; | |
| 1836 | maybe_last_atom_index.* = prev_index; | |
| 1841 | 1837 | } else { |
| 1842 | maybe_last_atom.* = null; | |
| 1838 | maybe_last_atom_index.* = null; | |
| 1843 | 1839 | } |
| 1844 | 1840 | } |
| 1845 | 1841 | } |
| 1846 | 1842 | |
| 1847 | if (atom.prev) |prev| { | |
| 1848 | prev.next = atom.next; | |
| 1843 | if (atom.prev_index) |prev_index| { | |
| 1844 | const prev = self.getAtomPtr(prev_index); | |
| 1845 | prev.next_index = atom.next_index; | |
| 1849 | 1846 | |
| 1850 | if (!already_have_free_list_node and prev.freeListEligible(self)) { | |
| 1847 | if (!already_have_free_list_node and prev.*.freeListEligible(self)) { | |
| 1851 | 1848 | // The free list is heuristics, it doesn't have to be perfect, so we can ignore |
| 1852 | 1849 | // the OOM here. |
| 1853 | free_list.append(self.base.allocator, prev) catch {}; | |
| 1850 | free_list.append(gpa, prev_index) catch {}; | |
| 1854 | 1851 | } |
| 1855 | 1852 | } else { |
| 1856 | atom.prev = null; | |
| 1853 | self.getAtomPtr(atom_index).prev_index = null; | |
| 1857 | 1854 | } |
| 1858 | 1855 | |
| 1859 | if (atom.next) |next| { | |
| 1860 | next.prev = atom.prev; | |
| 1856 | if (atom.next_index) |next_index| { | |
| 1857 | self.getAtomPtr(next_index).prev_index = atom.prev_index; | |
| 1861 | 1858 | } else { |
| 1862 | atom.next = null; | |
| 1859 | self.getAtomPtr(atom_index).next_index = null; | |
| 1863 | 1860 | } |
| 1864 | 1861 | |
| 1865 | if (self.d_sym) |*d_sym| { | |
| 1866 | d_sym.dwarf.freeAtom(&atom.dbg_info_atom); | |
| 1862 | // Appending to free lists is allowed to fail because the free lists are heuristics based anyway. | |
| 1863 | const sym_index = atom.getSymbolIndex().?; | |
| 1864 | ||
| 1865 | self.locals_free_list.append(gpa, sym_index) catch {}; | |
| 1866 | ||
| 1867 | // Try freeing GOT atom if this decl had one | |
| 1868 | const got_target = SymbolWithLoc{ .sym_index = sym_index, .file = null }; | |
| 1869 | if (self.got_entries_table.get(got_target)) |got_index| { | |
| 1870 | self.got_entries_free_list.append(gpa, @intCast(u32, got_index)) catch {}; | |
| 1871 | self.got_entries.items[got_index] = .{ | |
| 1872 | .target = .{ .sym_index = 0, .file = null }, | |
| 1873 | .sym_index = 0, | |
| 1874 | }; | |
| 1875 | _ = self.got_entries_table.remove(got_target); | |
| 1876 | ||
| 1877 | if (self.d_sym) |*d_sym| { | |
| 1878 | d_sym.swapRemoveRelocs(sym_index); | |
| 1879 | } | |
| 1880 | ||
| 1881 | log.debug(" adding GOT index {d} to free list (target local@{d})", .{ got_index, sym_index }); | |
| 1867 | 1882 | } |
| 1883 | ||
| 1884 | self.locals.items[sym_index].n_type = 0; | |
| 1885 | _ = self.atom_by_index_table.remove(sym_index); | |
| 1886 | log.debug(" adding local symbol index {d} to free list", .{sym_index}); | |
| 1887 | self.getAtomPtr(atom_index).sym_index = 0; | |
| 1868 | 1888 | } |
| 1869 | 1889 | |
| 1870 | fn shrinkAtom(self: *MachO, atom: *Atom, new_block_size: u64) void { | |
| 1890 | fn shrinkAtom(self: *MachO, atom_index: Atom.Index, new_block_size: u64) void { | |
| 1871 | 1891 | _ = self; |
| 1872 | _ = atom; | |
| 1892 | _ = atom_index; | |
| 1873 | 1893 | _ = new_block_size; |
| 1874 | 1894 | // TODO check the new capacity, and if it crosses the size threshold into a big enough |
| 1875 | 1895 | // capacity, insert a free list node for it. |
| 1876 | 1896 | } |
| 1877 | 1897 | |
| 1878 | fn growAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) !u64 { | |
| 1898 | fn growAtom(self: *MachO, atom_index: Atom.Index, new_atom_size: u64, alignment: u64) !u64 { | |
| 1899 | const atom = self.getAtom(atom_index); | |
| 1879 | 1900 | const sym = atom.getSymbol(self); |
| 1880 | 1901 | const align_ok = mem.alignBackwardGeneric(u64, sym.n_value, alignment) == sym.n_value; |
| 1881 | 1902 | const need_realloc = !align_ok or new_atom_size > atom.capacity(self); |
| 1882 | 1903 | if (!need_realloc) return sym.n_value; |
| 1883 | return self.allocateAtom(atom, new_atom_size, alignment); | |
| 1904 | return self.allocateAtom(atom_index, new_atom_size, alignment); | |
| 1884 | 1905 | } |
| 1885 | 1906 | |
| 1886 | fn allocateSymbol(self: *MachO) !u32 { | |
| 1907 | pub fn allocateSymbol(self: *MachO) !u32 { | |
| 1887 | 1908 | try self.locals.ensureUnusedCapacity(self.base.allocator, 1); |
| 1888 | 1909 | |
| 1889 | 1910 | const index = blk: { |
| ... | ... | @@ -1975,16 +1996,6 @@ pub fn allocateStubEntry(self: *MachO, target: SymbolWithLoc) !u32 { |
| 1975 | 1996 | return index; |
| 1976 | 1997 | } |
| 1977 | 1998 | |
| 1978 | pub fn allocateDeclIndexes(self: *MachO, decl_index: Module.Decl.Index) !void { | |
| 1979 | if (self.llvm_object) |_| return; | |
| 1980 | const decl = self.base.options.module.?.declPtr(decl_index); | |
| 1981 | if (decl.link.macho.sym_index != 0) return; | |
| 1982 | ||
| 1983 | decl.link.macho.sym_index = try self.allocateSymbol(); | |
| 1984 | try self.atom_by_index_table.putNoClobber(self.base.allocator, decl.link.macho.sym_index, &decl.link.macho); | |
| 1985 | try self.decls.putNoClobber(self.base.allocator, decl_index, null); | |
| 1986 | } | |
| 1987 | ||
| 1988 | 1999 | pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void { |
| 1989 | 2000 | if (build_options.skip_non_native and builtin.object_format != .macho) { |
| 1990 | 2001 | @panic("Attempted to compile for object format that was disabled by build configuration"); |
| ... | ... | @@ -1997,8 +2008,12 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv |
| 1997 | 2008 | |
| 1998 | 2009 | const decl_index = func.owner_decl; |
| 1999 | 2010 | const decl = module.declPtr(decl_index); |
| 2011 | ||
| 2012 | const atom_index = try self.getOrCreateAtomForDecl(decl_index); | |
| 2000 | 2013 | self.freeUnnamedConsts(decl_index); |
| 2001 | self.freeRelocationsForAtom(&decl.link.macho); | |
| 2014 | Atom.freeRelocations(self, atom_index); | |
| 2015 | ||
| 2016 | const atom = self.getAtom(atom_index); | |
| 2002 | 2017 | |
| 2003 | 2018 | var code_buffer = std.ArrayList(u8).init(self.base.allocator); |
| 2004 | 2019 | defer code_buffer.deinit(); |
| ... | ... | @@ -2017,7 +2032,7 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv |
| 2017 | 2032 | try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .none); |
| 2018 | 2033 | |
| 2019 | 2034 | const code = switch (res) { |
| 2020 | .appended => code_buffer.items, | |
| 2035 | .ok => code_buffer.items, | |
| 2021 | 2036 | .fail => |em| { |
| 2022 | 2037 | decl.analysis = .codegen_failure; |
| 2023 | 2038 | try module.failed_decls.put(module.gpa, decl_index, em); |
| ... | ... | @@ -2028,13 +2043,7 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv |
| 2028 | 2043 | const addr = try self.updateDeclCode(decl_index, code); |
| 2029 | 2044 | |
| 2030 | 2045 | if (decl_state) |*ds| { |
| 2031 | try self.d_sym.?.dwarf.commitDeclState( | |
| 2032 | module, | |
| 2033 | decl_index, | |
| 2034 | addr, | |
| 2035 | decl.link.macho.size, | |
| 2036 | ds, | |
| 2037 | ); | |
| 2046 | try self.d_sym.?.dwarf.commitDeclState(module, decl_index, addr, atom.size, ds); | |
| 2038 | 2047 | } |
| 2039 | 2048 | |
| 2040 | 2049 | // Since we updated the vaddr and the size, each corresponding export symbol also |
| ... | ... | @@ -2069,21 +2078,13 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu |
| 2069 | 2078 | |
| 2070 | 2079 | log.debug("allocating symbol indexes for {?s}", .{name}); |
| 2071 | 2080 | |
| 2072 | const atom = try gpa.create(Atom); | |
| 2073 | errdefer gpa.destroy(atom); | |
| 2074 | atom.* = Atom.empty; | |
| 2075 | ||
| 2076 | atom.sym_index = try self.allocateSymbol(); | |
| 2077 | ||
| 2078 | try self.managed_atoms.append(gpa, atom); | |
| 2079 | try self.atom_by_index_table.putNoClobber(gpa, atom.sym_index, atom); | |
| 2081 | const atom_index = try self.createAtom(); | |
| 2080 | 2082 | |
| 2081 | 2083 | const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), typed_value, &code_buffer, .none, .{ |
| 2082 | .parent_atom_index = atom.sym_index, | |
| 2084 | .parent_atom_index = self.getAtom(atom_index).getSymbolIndex().?, | |
| 2083 | 2085 | }); |
| 2084 | 2086 | const code = switch (res) { |
| 2085 | .externally_managed => |x| x, | |
| 2086 | .appended => code_buffer.items, | |
| 2087 | .ok => code_buffer.items, | |
| 2087 | 2088 | .fail => |em| { |
| 2088 | 2089 | decl.analysis = .codegen_failure; |
| 2089 | 2090 | try module.failed_decls.put(module.gpa, decl_index, em); |
| ... | ... | @@ -2093,26 +2094,27 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu |
| 2093 | 2094 | }; |
| 2094 | 2095 | |
| 2095 | 2096 | const required_alignment = typed_value.ty.abiAlignment(self.base.options.target); |
| 2097 | const atom = self.getAtomPtr(atom_index); | |
| 2096 | 2098 | atom.size = code.len; |
| 2097 | 2099 | atom.alignment = required_alignment; |
| 2098 | 2100 | // TODO: work out logic for disambiguating functions from function pointers |
| 2099 | // const sect_id = self.getDeclOutputSection(decl); | |
| 2101 | // const sect_id = self.getDeclOutputSection(decl_index); | |
| 2100 | 2102 | const sect_id = self.data_const_section_index.?; |
| 2101 | 2103 | const symbol = atom.getSymbolPtr(self); |
| 2102 | 2104 | symbol.n_strx = name_str_index; |
| 2103 | 2105 | symbol.n_type = macho.N_SECT; |
| 2104 | 2106 | symbol.n_sect = sect_id + 1; |
| 2105 | symbol.n_value = try self.allocateAtom(atom, code.len, required_alignment); | |
| 2106 | errdefer self.freeAtom(atom); | |
| 2107 | symbol.n_value = try self.allocateAtom(atom_index, code.len, required_alignment); | |
| 2108 | errdefer self.freeAtom(atom_index); | |
| 2107 | 2109 | |
| 2108 | try unnamed_consts.append(gpa, atom); | |
| 2110 | try unnamed_consts.append(gpa, atom_index); | |
| 2109 | 2111 | |
| 2110 | 2112 | log.debug("allocated atom for {?s} at 0x{x}", .{ name, symbol.n_value }); |
| 2111 | 2113 | log.debug(" (required alignment 0x{x})", .{required_alignment}); |
| 2112 | 2114 | |
| 2113 | try self.writeAtom(atom, code); | |
| 2115 | try self.writeAtom(atom_index, code); | |
| 2114 | 2116 | |
| 2115 | return atom.sym_index; | |
| 2117 | return atom.getSymbolIndex().?; | |
| 2116 | 2118 | } |
| 2117 | 2119 | |
| 2118 | 2120 | pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index) !void { |
| ... | ... | @@ -2137,7 +2139,9 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index) |
| 2137 | 2139 | } |
| 2138 | 2140 | } |
| 2139 | 2141 | |
| 2140 | self.freeRelocationsForAtom(&decl.link.macho); | |
| 2142 | const atom_index = try self.getOrCreateAtomForDecl(decl_index); | |
| 2143 | Atom.freeRelocations(self, atom_index); | |
| 2144 | const atom = self.getAtom(atom_index); | |
| 2141 | 2145 | |
| 2142 | 2146 | var code_buffer = std.ArrayList(u8).init(self.base.allocator); |
| 2143 | 2147 | defer code_buffer.deinit(); |
| ... | ... | @@ -2156,19 +2160,18 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index) |
| 2156 | 2160 | }, &code_buffer, .{ |
| 2157 | 2161 | .dwarf = ds, |
| 2158 | 2162 | }, .{ |
| 2159 | .parent_atom_index = decl.link.macho.sym_index, | |
| 2163 | .parent_atom_index = atom.getSymbolIndex().?, | |
| 2160 | 2164 | }) |
| 2161 | 2165 | else |
| 2162 | 2166 | try codegen.generateSymbol(&self.base, decl.srcLoc(), .{ |
| 2163 | 2167 | .ty = decl.ty, |
| 2164 | 2168 | .val = decl_val, |
| 2165 | 2169 | }, &code_buffer, .none, .{ |
| 2166 | .parent_atom_index = decl.link.macho.sym_index, | |
| 2170 | .parent_atom_index = atom.getSymbolIndex().?, | |
| 2167 | 2171 | }); |
| 2168 | 2172 | |
| 2169 | 2173 | const code = switch (res) { |
| 2170 | .externally_managed => |x| x, | |
| 2171 | .appended => code_buffer.items, | |
| 2174 | .ok => code_buffer.items, | |
| 2172 | 2175 | .fail => |em| { |
| 2173 | 2176 | decl.analysis = .codegen_failure; |
| 2174 | 2177 | try module.failed_decls.put(module.gpa, decl_index, em); |
| ... | ... | @@ -2178,13 +2181,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index) |
| 2178 | 2181 | const addr = try self.updateDeclCode(decl_index, code); |
| 2179 | 2182 | |
| 2180 | 2183 | if (decl_state) |*ds| { |
| 2181 | try self.d_sym.?.dwarf.commitDeclState( | |
| 2182 | module, | |
| 2183 | decl_index, | |
| 2184 | addr, | |
| 2185 | decl.link.macho.size, | |
| 2186 | ds, | |
| 2187 | ); | |
| 2184 | try self.d_sym.?.dwarf.commitDeclState(module, decl_index, addr, atom.size, ds); | |
| 2188 | 2185 | } |
| 2189 | 2186 | |
| 2190 | 2187 | // Since we updated the vaddr and the size, each corresponding export symbol also |
| ... | ... | @@ -2192,7 +2189,20 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index) |
| 2192 | 2189 | try self.updateDeclExports(module, decl_index, module.getDeclExports(decl_index)); |
| 2193 | 2190 | } |
| 2194 | 2191 | |
| 2195 | fn getDeclOutputSection(self: *MachO, decl: *Module.Decl) u8 { | |
| 2192 | pub fn getOrCreateAtomForDecl(self: *MachO, decl_index: Module.Decl.Index) !Atom.Index { | |
| 2193 | const gop = try self.decls.getOrPut(self.base.allocator, decl_index); | |
| 2194 | if (!gop.found_existing) { | |
| 2195 | gop.value_ptr.* = .{ | |
| 2196 | .atom = try self.createAtom(), | |
| 2197 | .section = self.getDeclOutputSection(decl_index), | |
| 2198 | .exports = .{}, | |
| 2199 | }; | |
| 2200 | } | |
| 2201 | return gop.value_ptr.atom; | |
| 2202 | } | |
| 2203 | ||
| 2204 | fn getDeclOutputSection(self: *MachO, decl_index: Module.Decl.Index) u8 { | |
| 2205 | const decl = self.base.options.module.?.declPtr(decl_index); | |
| 2196 | 2206 | const ty = decl.ty; |
| 2197 | 2207 | const val = decl.val; |
| 2198 | 2208 | const zig_ty = ty.zigTypeTag(); |
| ... | ... | @@ -2339,17 +2349,15 @@ fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []const u8) |
| 2339 | 2349 | const decl = mod.declPtr(decl_index); |
| 2340 | 2350 | |
| 2341 | 2351 | const required_alignment = decl.getAlignment(self.base.options.target); |
| 2342 | assert(decl.link.macho.sym_index != 0); // Caller forgot to call allocateDeclIndexes() | |
| 2343 | 2352 | |
| 2344 | 2353 | const sym_name = try decl.getFullyQualifiedName(mod); |
| 2345 | 2354 | defer self.base.allocator.free(sym_name); |
| 2346 | 2355 | |
| 2347 | const atom = &decl.link.macho; | |
| 2348 | const decl_ptr = self.decls.getPtr(decl_index).?; | |
| 2349 | if (decl_ptr.* == null) { | |
| 2350 | decl_ptr.* = self.getDeclOutputSection(decl); | |
| 2351 | } | |
| 2352 | const sect_id = decl_ptr.*.?; | |
| 2356 | const decl_metadata = self.decls.get(decl_index).?; | |
| 2357 | const atom_index = decl_metadata.atom; | |
| 2358 | const atom = self.getAtom(atom_index); | |
| 2359 | const sym_index = atom.getSymbolIndex().?; | |
| 2360 | const sect_id = decl_metadata.section; | |
| 2353 | 2361 | const code_len = code.len; |
| 2354 | 2362 | |
| 2355 | 2363 | if (atom.size != 0) { |
| ... | ... | @@ -2359,31 +2367,31 @@ fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []const u8) |
| 2359 | 2367 | sym.n_sect = sect_id + 1; |
| 2360 | 2368 | sym.n_desc = 0; |
| 2361 | 2369 | |
| 2362 | const capacity = decl.link.macho.capacity(self); | |
| 2370 | const capacity = atom.capacity(self); | |
| 2363 | 2371 | const need_realloc = code_len > capacity or !mem.isAlignedGeneric(u64, sym.n_value, required_alignment); |
| 2364 | 2372 | |
| 2365 | 2373 | if (need_realloc) { |
| 2366 | const vaddr = try self.growAtom(atom, code_len, required_alignment); | |
| 2374 | const vaddr = try self.growAtom(atom_index, code_len, required_alignment); | |
| 2367 | 2375 | log.debug("growing {s} and moving from 0x{x} to 0x{x}", .{ sym_name, sym.n_value, vaddr }); |
| 2368 | 2376 | log.debug(" (required alignment 0x{x})", .{required_alignment}); |
| 2369 | 2377 | |
| 2370 | 2378 | if (vaddr != sym.n_value) { |
| 2371 | 2379 | sym.n_value = vaddr; |
| 2372 | 2380 | log.debug(" (updating GOT entry)", .{}); |
| 2373 | const got_target = SymbolWithLoc{ .sym_index = atom.sym_index, .file = null }; | |
| 2374 | const got_atom = self.getGotAtomForSymbol(got_target).?; | |
| 2381 | const got_target = SymbolWithLoc{ .sym_index = sym_index, .file = null }; | |
| 2382 | const got_atom_index = self.getGotAtomIndexForSymbol(got_target).?; | |
| 2375 | 2383 | self.markRelocsDirtyByTarget(got_target); |
| 2376 | try self.writePtrWidthAtom(got_atom); | |
| 2384 | try self.writePtrWidthAtom(got_atom_index); | |
| 2377 | 2385 | } |
| 2378 | 2386 | } else if (code_len < atom.size) { |
| 2379 | self.shrinkAtom(atom, code_len); | |
| 2380 | } else if (atom.next == null) { | |
| 2387 | self.shrinkAtom(atom_index, code_len); | |
| 2388 | } else if (atom.next_index == null) { | |
| 2381 | 2389 | const header = &self.sections.items(.header)[sect_id]; |
| 2382 | 2390 | const segment = self.getSegment(sect_id); |
| 2383 | 2391 | const needed_size = (sym.n_value + code_len) - segment.vmaddr; |
| 2384 | 2392 | header.size = needed_size; |
| 2385 | 2393 | } |
| 2386 | atom.size = code_len; | |
| 2394 | self.getAtomPtr(atom_index).size = code_len; | |
| 2387 | 2395 | } else { |
| 2388 | 2396 | const name_str_index = try self.strtab.insert(gpa, sym_name); |
| 2389 | 2397 | const sym = atom.getSymbolPtr(self); |
| ... | ... | @@ -2392,32 +2400,32 @@ fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []const u8) |
| 2392 | 2400 | sym.n_sect = sect_id + 1; |
| 2393 | 2401 | sym.n_desc = 0; |
| 2394 | 2402 | |
| 2395 | const vaddr = try self.allocateAtom(atom, code_len, required_alignment); | |
| 2396 | errdefer self.freeAtom(atom); | |
| 2403 | const vaddr = try self.allocateAtom(atom_index, code_len, required_alignment); | |
| 2404 | errdefer self.freeAtom(atom_index); | |
| 2397 | 2405 | |
| 2398 | 2406 | log.debug("allocated atom for {s} at 0x{x}", .{ sym_name, vaddr }); |
| 2399 | 2407 | log.debug(" (required alignment 0x{x})", .{required_alignment}); |
| 2400 | 2408 | |
| 2401 | atom.size = code_len; | |
| 2409 | self.getAtomPtr(atom_index).size = code_len; | |
| 2402 | 2410 | sym.n_value = vaddr; |
| 2403 | 2411 | |
| 2404 | const got_target = SymbolWithLoc{ .sym_index = atom.sym_index, .file = null }; | |
| 2412 | const got_target = SymbolWithLoc{ .sym_index = sym_index, .file = null }; | |
| 2405 | 2413 | const got_index = try self.allocateGotEntry(got_target); |
| 2406 | const got_atom = try self.createGotAtom(got_target); | |
| 2407 | self.got_entries.items[got_index].sym_index = got_atom.sym_index; | |
| 2408 | try self.writePtrWidthAtom(got_atom); | |
| 2414 | const got_atom_index = try self.createGotAtom(got_target); | |
| 2415 | const got_atom = self.getAtom(got_atom_index); | |
| 2416 | self.got_entries.items[got_index].sym_index = got_atom.getSymbolIndex().?; | |
| 2417 | try self.writePtrWidthAtom(got_atom_index); | |
| 2409 | 2418 | } |
| 2410 | 2419 | |
| 2411 | 2420 | self.markRelocsDirtyByTarget(atom.getSymbolWithLoc()); |
| 2412 | try self.writeAtom(atom, code); | |
| 2421 | try self.writeAtom(atom_index, code); | |
| 2413 | 2422 | |
| 2414 | 2423 | return atom.getSymbol(self).n_value; |
| 2415 | 2424 | } |
| 2416 | 2425 | |
| 2417 | pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl: *const Module.Decl) !void { | |
| 2418 | _ = module; | |
| 2426 | pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl_index: Module.Decl.Index) !void { | |
| 2419 | 2427 | if (self.d_sym) |*d_sym| { |
| 2420 | try d_sym.dwarf.updateDeclLineNumber(decl); | |
| 2428 | try d_sym.dwarf.updateDeclLineNumber(module, decl_index); | |
| 2421 | 2429 | } |
| 2422 | 2430 | } |
| 2423 | 2431 | |
| ... | ... | @@ -2434,14 +2442,17 @@ pub fn updateDeclExports( |
| 2434 | 2442 | if (self.llvm_object) |llvm_object| |
| 2435 | 2443 | return llvm_object.updateDeclExports(module, decl_index, exports); |
| 2436 | 2444 | } |
| 2445 | ||
| 2437 | 2446 | const tracy = trace(@src()); |
| 2438 | 2447 | defer tracy.end(); |
| 2439 | 2448 | |
| 2440 | 2449 | const gpa = self.base.allocator; |
| 2441 | 2450 | |
| 2442 | 2451 | const decl = module.declPtr(decl_index); |
| 2443 | if (decl.link.macho.sym_index == 0) return; | |
| 2444 | const decl_sym = decl.link.macho.getSymbol(self); | |
| 2452 | const atom_index = try self.getOrCreateAtomForDecl(decl_index); | |
| 2453 | const atom = self.getAtom(atom_index); | |
| 2454 | const decl_sym = atom.getSymbol(self); | |
| 2455 | const decl_metadata = self.decls.getPtr(decl_index).?; | |
| 2445 | 2456 | |
| 2446 | 2457 | for (exports) |exp| { |
| 2447 | 2458 | const exp_name = try std.fmt.allocPrint(gpa, "_{s}", .{exp.options.name}); |
| ... | ... | @@ -2479,9 +2490,9 @@ pub fn updateDeclExports( |
| 2479 | 2490 | continue; |
| 2480 | 2491 | } |
| 2481 | 2492 | |
| 2482 | const sym_index = exp.link.macho.sym_index orelse blk: { | |
| 2493 | const sym_index = decl_metadata.getExport(self, exp_name) orelse blk: { | |
| 2483 | 2494 | const sym_index = try self.allocateSymbol(); |
| 2484 | exp.link.macho.sym_index = sym_index; | |
| 2495 | try decl_metadata.exports.append(gpa, sym_index); | |
| 2485 | 2496 | break :blk sym_index; |
| 2486 | 2497 | }; |
| 2487 | 2498 | const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = null }; |
| ... | ... | @@ -2529,16 +2540,18 @@ pub fn updateDeclExports( |
| 2529 | 2540 | } |
| 2530 | 2541 | } |
| 2531 | 2542 | |
| 2532 | pub fn deleteExport(self: *MachO, exp: Export) void { | |
| 2543 | pub fn deleteDeclExport(self: *MachO, decl_index: Module.Decl.Index, name: []const u8) Allocator.Error!void { | |
| 2533 | 2544 | if (self.llvm_object) |_| return; |
| 2534 | const sym_index = exp.sym_index orelse return; | |
| 2545 | const metadata = self.decls.getPtr(decl_index) orelse return; | |
| 2535 | 2546 | |
| 2536 | 2547 | const gpa = self.base.allocator; |
| 2548 | const exp_name = try std.fmt.allocPrint(gpa, "_{s}", .{name}); | |
| 2549 | defer gpa.free(exp_name); | |
| 2550 | const sym_index = metadata.getExportPtr(self, exp_name) orelse return; | |
| 2537 | 2551 | |
| 2538 | const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = null }; | |
| 2552 | const sym_loc = SymbolWithLoc{ .sym_index = sym_index.*, .file = null }; | |
| 2539 | 2553 | const sym = self.getSymbolPtr(sym_loc); |
| 2540 | const sym_name = self.getSymbolName(sym_loc); | |
| 2541 | log.debug("deleting export '{s}'", .{sym_name}); | |
| 2554 | log.debug("deleting export '{s}'", .{exp_name}); | |
| 2542 | 2555 | assert(sym.sect() and sym.ext()); |
| 2543 | 2556 | sym.* = .{ |
| 2544 | 2557 | .n_strx = 0, |
| ... | ... | @@ -2547,9 +2560,9 @@ pub fn deleteExport(self: *MachO, exp: Export) void { |
| 2547 | 2560 | .n_desc = 0, |
| 2548 | 2561 | .n_value = 0, |
| 2549 | 2562 | }; |
| 2550 | self.locals_free_list.append(gpa, sym_index) catch {}; | |
| 2563 | self.locals_free_list.append(gpa, sym_index.*) catch {}; | |
| 2551 | 2564 | |
| 2552 | if (self.resolver.fetchRemove(sym_name)) |entry| { | |
| 2565 | if (self.resolver.fetchRemove(exp_name)) |entry| { | |
| 2553 | 2566 | defer gpa.free(entry.key); |
| 2554 | 2567 | self.globals_free_list.append(gpa, entry.value) catch {}; |
| 2555 | 2568 | self.globals.items[entry.value] = .{ |
| ... | ... | @@ -2557,17 +2570,8 @@ pub fn deleteExport(self: *MachO, exp: Export) void { |
| 2557 | 2570 | .file = null, |
| 2558 | 2571 | }; |
| 2559 | 2572 | } |
| 2560 | } | |
| 2561 | 2573 | |
| 2562 | fn freeRelocationsForAtom(self: *MachO, atom: *Atom) void { | |
| 2563 | var removed_relocs = self.relocs.fetchOrderedRemove(atom); | |
| 2564 | if (removed_relocs) |*relocs| relocs.value.deinit(self.base.allocator); | |
| 2565 | var removed_rebases = self.rebases.fetchOrderedRemove(atom); | |
| 2566 | if (removed_rebases) |*rebases| rebases.value.deinit(self.base.allocator); | |
| 2567 | var removed_bindings = self.bindings.fetchOrderedRemove(atom); | |
| 2568 | if (removed_bindings) |*bindings| bindings.value.deinit(self.base.allocator); | |
| 2569 | var removed_lazy_bindings = self.lazy_bindings.fetchOrderedRemove(atom); | |
| 2570 | if (removed_lazy_bindings) |*lazy_bindings| lazy_bindings.value.deinit(self.base.allocator); | |
| 2574 | sym_index.* = 0; | |
| 2571 | 2575 | } |
| 2572 | 2576 | |
| 2573 | 2577 | fn freeUnnamedConsts(self: *MachO, decl_index: Module.Decl.Index) void { |
| ... | ... | @@ -2575,11 +2579,6 @@ fn freeUnnamedConsts(self: *MachO, decl_index: Module.Decl.Index) void { |
| 2575 | 2579 | const unnamed_consts = self.unnamed_const_atoms.getPtr(decl_index) orelse return; |
| 2576 | 2580 | for (unnamed_consts.items) |atom| { |
| 2577 | 2581 | self.freeAtom(atom); |
| 2578 | self.locals_free_list.append(gpa, atom.sym_index) catch {}; | |
| 2579 | self.locals.items[atom.sym_index].n_type = 0; | |
| 2580 | _ = self.atom_by_index_table.remove(atom.sym_index); | |
| 2581 | log.debug(" adding local symbol index {d} to free list", .{atom.sym_index}); | |
| 2582 | atom.sym_index = 0; | |
| 2583 | 2582 | } |
| 2584 | 2583 | unnamed_consts.clearAndFree(gpa); |
| 2585 | 2584 | } |
| ... | ... | @@ -2593,67 +2592,37 @@ pub fn freeDecl(self: *MachO, decl_index: Module.Decl.Index) void { |
| 2593 | 2592 | |
| 2594 | 2593 | log.debug("freeDecl {*}", .{decl}); |
| 2595 | 2594 | |
| 2596 | const kv = self.decls.fetchSwapRemove(decl_index); | |
| 2597 | if (kv.?.value) |_| { | |
| 2598 | self.freeAtom(&decl.link.macho); | |
| 2595 | if (self.decls.fetchSwapRemove(decl_index)) |const_kv| { | |
| 2596 | var kv = const_kv; | |
| 2597 | self.freeAtom(kv.value.atom); | |
| 2599 | 2598 | self.freeUnnamedConsts(decl_index); |
| 2600 | } | |
| 2601 | ||
| 2602 | // Appending to free lists is allowed to fail because the free lists are heuristics based anyway. | |
| 2603 | const gpa = self.base.allocator; | |
| 2604 | const sym_index = decl.link.macho.sym_index; | |
| 2605 | if (sym_index != 0) { | |
| 2606 | self.locals_free_list.append(gpa, sym_index) catch {}; | |
| 2607 | ||
| 2608 | // Try freeing GOT atom if this decl had one | |
| 2609 | const got_target = SymbolWithLoc{ .sym_index = sym_index, .file = null }; | |
| 2610 | if (self.got_entries_table.get(got_target)) |got_index| { | |
| 2611 | self.got_entries_free_list.append(gpa, @intCast(u32, got_index)) catch {}; | |
| 2612 | self.got_entries.items[got_index] = .{ | |
| 2613 | .target = .{ .sym_index = 0, .file = null }, | |
| 2614 | .sym_index = 0, | |
| 2615 | }; | |
| 2616 | _ = self.got_entries_table.remove(got_target); | |
| 2617 | ||
| 2618 | if (self.d_sym) |*d_sym| { | |
| 2619 | d_sym.swapRemoveRelocs(sym_index); | |
| 2620 | } | |
| 2621 | ||
| 2622 | log.debug(" adding GOT index {d} to free list (target local@{d})", .{ got_index, sym_index }); | |
| 2623 | } | |
| 2624 | ||
| 2625 | self.locals.items[sym_index].n_type = 0; | |
| 2626 | _ = self.atom_by_index_table.remove(sym_index); | |
| 2627 | log.debug(" adding local symbol index {d} to free list", .{sym_index}); | |
| 2628 | decl.link.macho.sym_index = 0; | |
| 2599 | kv.value.exports.deinit(self.base.allocator); | |
| 2629 | 2600 | } |
| 2630 | 2601 | |
| 2631 | 2602 | if (self.d_sym) |*d_sym| { |
| 2632 | d_sym.dwarf.freeDecl(decl); | |
| 2603 | d_sym.dwarf.freeDecl(decl_index); | |
| 2633 | 2604 | } |
| 2634 | 2605 | } |
| 2635 | 2606 | |
| 2636 | 2607 | pub fn getDeclVAddr(self: *MachO, decl_index: Module.Decl.Index, reloc_info: File.RelocInfo) !u64 { |
| 2637 | const mod = self.base.options.module.?; | |
| 2638 | const decl = mod.declPtr(decl_index); | |
| 2639 | ||
| 2640 | 2608 | assert(self.llvm_object == null); |
| 2641 | assert(decl.link.macho.sym_index != 0); | |
| 2642 | 2609 | |
| 2643 | const atom = self.getAtomForSymbol(.{ .sym_index = reloc_info.parent_atom_index, .file = null }).?; | |
| 2644 | try atom.addRelocation(self, .{ | |
| 2610 | const this_atom_index = try self.getOrCreateAtomForDecl(decl_index); | |
| 2611 | const sym_index = self.getAtom(this_atom_index).getSymbolIndex().?; | |
| 2612 | const atom_index = self.getAtomIndexForSymbol(.{ .sym_index = reloc_info.parent_atom_index, .file = null }).?; | |
| 2613 | try Atom.addRelocation(self, atom_index, .{ | |
| 2645 | 2614 | .type = switch (self.base.options.target.cpu.arch) { |
| 2646 | 2615 | .aarch64 => @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED), |
| 2647 | 2616 | .x86_64 => @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED), |
| 2648 | 2617 | else => unreachable, |
| 2649 | 2618 | }, |
| 2650 | .target = .{ .sym_index = decl.link.macho.sym_index, .file = null }, | |
| 2619 | .target = .{ .sym_index = sym_index, .file = null }, | |
| 2651 | 2620 | .offset = @intCast(u32, reloc_info.offset), |
| 2652 | 2621 | .addend = reloc_info.addend, |
| 2653 | 2622 | .pcrel = false, |
| 2654 | 2623 | .length = 3, |
| 2655 | 2624 | }); |
| 2656 | try atom.addRebase(self, @intCast(u32, reloc_info.offset)); | |
| 2625 | try Atom.addRebase(self, atom_index, @intCast(u32, reloc_info.offset)); | |
| 2657 | 2626 | |
| 2658 | 2627 | return 0; |
| 2659 | 2628 | } |
| ... | ... | @@ -2885,34 +2854,36 @@ fn moveSectionInVirtualMemory(self: *MachO, sect_id: u8, needed_size: u64) !void |
| 2885 | 2854 | // TODO: enforce order by increasing VM addresses in self.sections container. |
| 2886 | 2855 | for (self.sections.items(.header)[sect_id + 1 ..]) |*next_header, next_sect_id| { |
| 2887 | 2856 | const index = @intCast(u8, sect_id + 1 + next_sect_id); |
| 2888 | const maybe_last_atom = &self.sections.items(.last_atom)[index]; | |
| 2889 | 2857 | const next_segment = self.getSegmentPtr(index); |
| 2890 | 2858 | next_header.addr += diff; |
| 2891 | 2859 | next_segment.vmaddr += diff; |
| 2892 | 2860 | |
| 2893 | if (maybe_last_atom.*) |last_atom| { | |
| 2894 | var atom = last_atom; | |
| 2861 | const maybe_last_atom_index = &self.sections.items(.last_atom_index)[index]; | |
| 2862 | if (maybe_last_atom_index.*) |last_atom_index| { | |
| 2863 | var atom_index = last_atom_index; | |
| 2895 | 2864 | while (true) { |
| 2865 | const atom = self.getAtom(atom_index); | |
| 2896 | 2866 | const sym = atom.getSymbolPtr(self); |
| 2897 | 2867 | sym.n_value += diff; |
| 2898 | 2868 | |
| 2899 | if (atom.prev) |prev| { | |
| 2900 | atom = prev; | |
| 2869 | if (atom.prev_index) |prev_index| { | |
| 2870 | atom_index = prev_index; | |
| 2901 | 2871 | } else break; |
| 2902 | 2872 | } |
| 2903 | 2873 | } |
| 2904 | 2874 | } |
| 2905 | 2875 | } |
| 2906 | 2876 | |
| 2907 | fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) !u64 { | |
| 2877 | fn allocateAtom(self: *MachO, atom_index: Atom.Index, new_atom_size: u64, alignment: u64) !u64 { | |
| 2908 | 2878 | const tracy = trace(@src()); |
| 2909 | 2879 | defer tracy.end(); |
| 2910 | 2880 | |
| 2881 | const atom = self.getAtom(atom_index); | |
| 2911 | 2882 | const sect_id = atom.getSymbol(self).n_sect - 1; |
| 2912 | 2883 | const segment = self.getSegmentPtr(sect_id); |
| 2913 | 2884 | const header = &self.sections.items(.header)[sect_id]; |
| 2914 | 2885 | const free_list = &self.sections.items(.free_list)[sect_id]; |
| 2915 | const maybe_last_atom = &self.sections.items(.last_atom)[sect_id]; | |
| 2886 | const maybe_last_atom_index = &self.sections.items(.last_atom_index)[sect_id]; | |
| 2916 | 2887 | const requires_padding = blk: { |
| 2917 | 2888 | if (!header.isCode()) break :blk false; |
| 2918 | 2889 | if (header.isSymbolStubs()) break :blk false; |
| ... | ... | @@ -2926,7 +2897,7 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) ! |
| 2926 | 2897 | // It would be simpler to do it inside the for loop below, but that would cause a |
| 2927 | 2898 | // problem if an error was returned later in the function. So this action |
| 2928 | 2899 | // is actually carried out at the end of the function, when errors are no longer possible. |
| 2929 | var atom_placement: ?*Atom = null; | |
| 2900 | var atom_placement: ?Atom.Index = null; | |
| 2930 | 2901 | var free_list_removal: ?usize = null; |
| 2931 | 2902 | |
| 2932 | 2903 | // First we look for an appropriately sized free list node. |
| ... | ... | @@ -2934,7 +2905,8 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) ! |
| 2934 | 2905 | var vaddr = blk: { |
| 2935 | 2906 | var i: usize = 0; |
| 2936 | 2907 | while (i < free_list.items.len) { |
| 2937 | const big_atom = free_list.items[i]; | |
| 2908 | const big_atom_index = free_list.items[i]; | |
| 2909 | const big_atom = self.getAtom(big_atom_index); | |
| 2938 | 2910 | // We now have a pointer to a live atom that has too much capacity. |
| 2939 | 2911 | // Is it enough that we could fit this new atom? |
| 2940 | 2912 | const sym = big_atom.getSymbol(self); |
| ... | ... | @@ -2962,30 +2934,35 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) ! |
| 2962 | 2934 | const keep_free_list_node = remaining_capacity >= min_text_capacity; |
| 2963 | 2935 | |
| 2964 | 2936 | // Set up the metadata to be updated, after errors are no longer possible. |
| 2965 | atom_placement = big_atom; | |
| 2937 | atom_placement = big_atom_index; | |
| 2966 | 2938 | if (!keep_free_list_node) { |
| 2967 | 2939 | free_list_removal = i; |
| 2968 | 2940 | } |
| 2969 | 2941 | break :blk new_start_vaddr; |
| 2970 | } else if (maybe_last_atom.*) |last| { | |
| 2942 | } else if (maybe_last_atom_index.*) |last_index| { | |
| 2943 | const last = self.getAtom(last_index); | |
| 2971 | 2944 | const last_symbol = last.getSymbol(self); |
| 2972 | 2945 | const ideal_capacity = if (requires_padding) padToIdeal(last.size) else last.size; |
| 2973 | 2946 | const ideal_capacity_end_vaddr = last_symbol.n_value + ideal_capacity; |
| 2974 | 2947 | const new_start_vaddr = mem.alignForwardGeneric(u64, ideal_capacity_end_vaddr, alignment); |
| 2975 | atom_placement = last; | |
| 2948 | atom_placement = last_index; | |
| 2976 | 2949 | break :blk new_start_vaddr; |
| 2977 | 2950 | } else { |
| 2978 | 2951 | break :blk mem.alignForwardGeneric(u64, segment.vmaddr, alignment); |
| 2979 | 2952 | } |
| 2980 | 2953 | }; |
| 2981 | 2954 | |
| 2982 | const expand_section = atom_placement == null or atom_placement.?.next == null; | |
| 2955 | const expand_section = if (atom_placement) |placement_index| | |
| 2956 | self.getAtom(placement_index).next_index == null | |
| 2957 | else | |
| 2958 | true; | |
| 2983 | 2959 | if (expand_section) { |
| 2984 | 2960 | const sect_capacity = self.allocatedSize(header.offset); |
| 2985 | 2961 | const needed_size = (vaddr + new_atom_size) - segment.vmaddr; |
| 2986 | 2962 | if (needed_size > sect_capacity) { |
| 2987 | 2963 | const new_offset = self.findFreeSpace(needed_size, self.page_size); |
| 2988 | const current_size = if (maybe_last_atom.*) |last_atom| blk: { | |
| 2964 | const current_size = if (maybe_last_atom_index.*) |last_atom_index| blk: { | |
| 2965 | const last_atom = self.getAtom(last_atom_index); | |
| 2989 | 2966 | const sym = last_atom.getSymbol(self); |
| 2990 | 2967 | break :blk (sym.n_value + last_atom.size) - segment.vmaddr; |
| 2991 | 2968 | } else 0; |
| ... | ... | @@ -3017,7 +2994,7 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) ! |
| 3017 | 2994 | header.size = needed_size; |
| 3018 | 2995 | segment.filesize = mem.alignForwardGeneric(u64, needed_size, self.page_size); |
| 3019 | 2996 | segment.vmsize = mem.alignForwardGeneric(u64, needed_size, self.page_size); |
| 3020 | maybe_last_atom.* = atom; | |
| 2997 | maybe_last_atom_index.* = atom_index; | |
| 3021 | 2998 | |
| 3022 | 2999 | self.segment_table_dirty = true; |
| 3023 | 3000 | } |
| ... | ... | @@ -3026,21 +3003,31 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) ! |
| 3026 | 3003 | if (header.@"align" < align_pow) { |
| 3027 | 3004 | header.@"align" = align_pow; |
| 3028 | 3005 | } |
| 3006 | { | |
| 3007 | const atom_ptr = self.getAtomPtr(atom_index); | |
| 3008 | atom_ptr.size = new_atom_size; | |
| 3009 | atom_ptr.alignment = @intCast(u32, alignment); | |
| 3010 | } | |
| 3029 | 3011 | |
| 3030 | if (atom.prev) |prev| { | |
| 3031 | prev.next = atom.next; | |
| 3012 | if (atom.prev_index) |prev_index| { | |
| 3013 | const prev = self.getAtomPtr(prev_index); | |
| 3014 | prev.next_index = atom.next_index; | |
| 3032 | 3015 | } |
| 3033 | if (atom.next) |next| { | |
| 3034 | next.prev = atom.prev; | |
| 3016 | if (atom.next_index) |next_index| { | |
| 3017 | const next = self.getAtomPtr(next_index); | |
| 3018 | next.prev_index = atom.prev_index; | |
| 3035 | 3019 | } |
| 3036 | 3020 | |
| 3037 | if (atom_placement) |big_atom| { | |
| 3038 | atom.prev = big_atom; | |
| 3039 | atom.next = big_atom.next; | |
| 3040 | big_atom.next = atom; | |
| 3021 | if (atom_placement) |big_atom_index| { | |
| 3022 | const big_atom = self.getAtomPtr(big_atom_index); | |
| 3023 | const atom_ptr = self.getAtomPtr(atom_index); | |
| 3024 | atom_ptr.prev_index = big_atom_index; | |
| 3025 | atom_ptr.next_index = big_atom.next_index; | |
| 3026 | big_atom.next_index = atom_index; | |
| 3041 | 3027 | } else { |
| 3042 | atom.prev = null; | |
| 3043 | atom.next = null; | |
| 3028 | const atom_ptr = self.getAtomPtr(atom_index); | |
| 3029 | atom_ptr.prev_index = null; | |
| 3030 | atom_ptr.next_index = null; | |
| 3044 | 3031 | } |
| 3045 | 3032 | if (free_list_removal) |i| { |
| 3046 | 3033 | _ = free_list.swapRemove(i); |
| ... | ... | @@ -3180,8 +3167,9 @@ fn collectRebaseData(self: *MachO, rebase: *Rebase) !void { |
| 3180 | 3167 | const gpa = self.base.allocator; |
| 3181 | 3168 | const slice = self.sections.slice(); |
| 3182 | 3169 | |
| 3183 | for (self.rebases.keys()) |atom, i| { | |
| 3184 | log.debug(" ATOM(%{d}, '{s}')", .{ atom.sym_index, atom.getName(self) }); | |
| 3170 | for (self.rebases.keys()) |atom_index, i| { | |
| 3171 | const atom = self.getAtom(atom_index); | |
| 3172 | log.debug(" ATOM(%{?d}, '{s}')", .{ atom.getSymbolIndex(), atom.getName(self) }); | |
| 3185 | 3173 | |
| 3186 | 3174 | const sym = atom.getSymbol(self); |
| 3187 | 3175 | const segment_index = slice.items(.segment_index)[sym.n_sect - 1]; |
| ... | ... | @@ -3209,8 +3197,9 @@ fn collectBindData(self: *MachO, bind: anytype, raw_bindings: anytype) !void { |
| 3209 | 3197 | const gpa = self.base.allocator; |
| 3210 | 3198 | const slice = self.sections.slice(); |
| 3211 | 3199 | |
| 3212 | for (raw_bindings.keys()) |atom, i| { | |
| 3213 | log.debug(" ATOM(%{d}, '{s}')", .{ atom.sym_index, atom.getName(self) }); | |
| 3200 | for (raw_bindings.keys()) |atom_index, i| { | |
| 3201 | const atom = self.getAtom(atom_index); | |
| 3202 | log.debug(" ATOM(%{?d}, '{s}')", .{ atom.getSymbolIndex(), atom.getName(self) }); | |
| 3214 | 3203 | |
| 3215 | 3204 | const sym = atom.getSymbol(self); |
| 3216 | 3205 | const segment_index = slice.items(.segment_index)[sym.n_sect - 1]; |
| ... | ... | @@ -3384,7 +3373,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, lazy_bind: LazyBind) !void |
| 3384 | 3373 | if (lazy_bind.size() == 0) return; |
| 3385 | 3374 | |
| 3386 | 3375 | const stub_helper_section_index = self.stub_helper_section_index.?; |
| 3387 | assert(self.stub_helper_preamble_atom != null); | |
| 3376 | assert(self.stub_helper_preamble_atom_index != null); | |
| 3388 | 3377 | |
| 3389 | 3378 | const section = self.sections.get(stub_helper_section_index); |
| 3390 | 3379 | |
| ... | ... | @@ -3394,10 +3383,11 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, lazy_bind: LazyBind) !void |
| 3394 | 3383 | else => unreachable, |
| 3395 | 3384 | }; |
| 3396 | 3385 | const header = section.header; |
| 3397 | var atom = section.last_atom.?; | |
| 3386 | var atom_index = section.last_atom_index.?; | |
| 3398 | 3387 | |
| 3399 | 3388 | var index: usize = lazy_bind.offsets.items.len; |
| 3400 | 3389 | while (index > 0) : (index -= 1) { |
| 3390 | const atom = self.getAtom(atom_index); | |
| 3401 | 3391 | const sym = atom.getSymbol(self); |
| 3402 | 3392 | const file_offset = header.offset + sym.n_value - header.addr + stub_offset; |
| 3403 | 3393 | const bind_offset = lazy_bind.offsets.items[index - 1]; |
| ... | ... | @@ -3410,7 +3400,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, lazy_bind: LazyBind) !void |
| 3410 | 3400 | |
| 3411 | 3401 | try self.base.file.?.pwriteAll(mem.asBytes(&bind_offset), file_offset); |
| 3412 | 3402 | |
| 3413 | atom = atom.prev.?; | |
| 3403 | atom_index = atom.prev_index.?; | |
| 3414 | 3404 | } |
| 3415 | 3405 | } |
| 3416 | 3406 | |
| ... | ... | @@ -3853,25 +3843,35 @@ pub fn getOrPutGlobalPtr(self: *MachO, name: []const u8) !GetOrPutGlobalPtrResul |
| 3853 | 3843 | return GetOrPutGlobalPtrResult{ .found_existing = false, .value_ptr = ptr }; |
| 3854 | 3844 | } |
| 3855 | 3845 | |
| 3846 | pub fn getAtom(self: *MachO, atom_index: Atom.Index) Atom { | |
| 3847 | assert(atom_index < self.atoms.items.len); | |
| 3848 | return self.atoms.items[atom_index]; | |
| 3849 | } | |
| 3850 | ||
| 3851 | pub fn getAtomPtr(self: *MachO, atom_index: Atom.Index) *Atom { | |
| 3852 | assert(atom_index < self.atoms.items.len); | |
| 3853 | return &self.atoms.items[atom_index]; | |
| 3854 | } | |
| 3855 | ||
| 3856 | 3856 | /// Returns atom if there is an atom referenced by the symbol described by `sym_with_loc` descriptor. |
| 3857 | 3857 | /// Returns null on failure. |
| 3858 | pub fn getAtomForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?*Atom { | |
| 3858 | pub fn getAtomIndexForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?Atom.Index { | |
| 3859 | 3859 | assert(sym_with_loc.file == null); |
| 3860 | 3860 | return self.atom_by_index_table.get(sym_with_loc.sym_index); |
| 3861 | 3861 | } |
| 3862 | 3862 | |
| 3863 | 3863 | /// Returns GOT atom that references `sym_with_loc` if one exists. |
| 3864 | 3864 | /// Returns null otherwise. |
| 3865 | pub fn getGotAtomForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?*Atom { | |
| 3865 | pub fn getGotAtomIndexForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?Atom.Index { | |
| 3866 | 3866 | const got_index = self.got_entries_table.get(sym_with_loc) orelse return null; |
| 3867 | return self.got_entries.items[got_index].getAtom(self); | |
| 3867 | return self.got_entries.items[got_index].getAtomIndex(self); | |
| 3868 | 3868 | } |
| 3869 | 3869 | |
| 3870 | 3870 | /// Returns stubs atom that references `sym_with_loc` if one exists. |
| 3871 | 3871 | /// Returns null otherwise. |
| 3872 | pub fn getStubsAtomForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?*Atom { | |
| 3872 | pub fn getStubsAtomIndexForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?Atom.Index { | |
| 3873 | 3873 | const stubs_index = self.stubs_table.get(sym_with_loc) orelse return null; |
| 3874 | return self.stubs.items[stubs_index].getAtom(self); | |
| 3874 | return self.stubs.items[stubs_index].getAtomIndex(self); | |
| 3875 | 3875 | } |
| 3876 | 3876 | |
| 3877 | 3877 | /// Returns symbol location corresponding to the set entrypoint. |
| ... | ... | @@ -4257,30 +4257,35 @@ pub fn logAtoms(self: *MachO) void { |
| 4257 | 4257 | log.debug("atoms:", .{}); |
| 4258 | 4258 | |
| 4259 | 4259 | const slice = self.sections.slice(); |
| 4260 | for (slice.items(.last_atom)) |last, i| { | |
| 4261 | var atom = last orelse continue; | |
| 4260 | for (slice.items(.last_atom_index)) |last_atom_index, i| { | |
| 4261 | var atom_index = last_atom_index orelse continue; | |
| 4262 | 4262 | const header = slice.items(.header)[i]; |
| 4263 | 4263 | |
| 4264 | while (atom.prev) |prev| { | |
| 4265 | atom = prev; | |
| 4264 | while (true) { | |
| 4265 | const atom = self.getAtom(atom_index); | |
| 4266 | if (atom.prev_index) |prev_index| { | |
| 4267 | atom_index = prev_index; | |
| 4268 | } else break; | |
| 4266 | 4269 | } |
| 4267 | 4270 | |
| 4268 | 4271 | log.debug("{s},{s}", .{ header.segName(), header.sectName() }); |
| 4269 | 4272 | |
| 4270 | 4273 | while (true) { |
| 4271 | self.logAtom(atom); | |
| 4272 | if (atom.next) |next| { | |
| 4273 | atom = next; | |
| 4274 | self.logAtom(atom_index); | |
| 4275 | const atom = self.getAtom(atom_index); | |
| 4276 | if (atom.next_index) |next_index| { | |
| 4277 | atom_index = next_index; | |
| 4274 | 4278 | } else break; |
| 4275 | 4279 | } |
| 4276 | 4280 | } |
| 4277 | 4281 | } |
| 4278 | 4282 | |
| 4279 | pub fn logAtom(self: *MachO, atom: *const Atom) void { | |
| 4283 | pub fn logAtom(self: *MachO, atom_index: Atom.Index) void { | |
| 4284 | const atom = self.getAtom(atom_index); | |
| 4280 | 4285 | const sym = atom.getSymbol(self); |
| 4281 | 4286 | const sym_name = atom.getName(self); |
| 4282 | log.debug(" ATOM(%{d}, '{s}') @ {x} (sizeof({x}), alignof({x})) in object({?d}) in sect({d})", .{ | |
| 4283 | atom.sym_index, | |
| 4287 | log.debug(" ATOM(%{?d}, '{s}') @ {x} (sizeof({x}), alignof({x})) in object({?d}) in sect({d})", .{ | |
| 4288 | atom.getSymbolIndex(), | |
| 4284 | 4289 | sym_name, |
| 4285 | 4290 | sym.n_value, |
| 4286 | 4291 | atom.size, |
src/link/MachO/Atom.zig+54-38| ... | ... | @@ -13,7 +13,6 @@ const trace = @import("../../tracy.zig").trace; |
| 13 | 13 | |
| 14 | 14 | const Allocator = mem.Allocator; |
| 15 | 15 | const Arch = std.Target.Cpu.Arch; |
| 16 | const Dwarf = @import("../Dwarf.zig"); | |
| 17 | 16 | const MachO = @import("../MachO.zig"); |
| 18 | 17 | const Relocation = @import("Relocation.zig"); |
| 19 | 18 | const SymbolWithLoc = MachO.SymbolWithLoc; |
| ... | ... | @@ -39,10 +38,11 @@ size: u64, |
| 39 | 38 | alignment: u32, |
| 40 | 39 | |
| 41 | 40 | /// Points to the previous and next neighbours |
| 42 | next: ?*Atom, | |
| 43 | prev: ?*Atom, | |
| 41 | /// TODO use the same trick as with symbols: reserve index 0 as null atom | |
| 42 | next_index: ?Index, | |
| 43 | prev_index: ?Index, | |
| 44 | 44 | |
| 45 | dbg_info_atom: Dwarf.Atom, | |
| 45 | pub const Index = u32; | |
| 46 | 46 | |
| 47 | 47 | pub const Binding = struct { |
| 48 | 48 | target: SymbolWithLoc, |
| ... | ... | @@ -54,15 +54,10 @@ pub const SymbolAtOffset = struct { |
| 54 | 54 | offset: u64, |
| 55 | 55 | }; |
| 56 | 56 | |
| 57 | pub const empty = Atom{ | |
| 58 | .sym_index = 0, | |
| 59 | .file = null, | |
| 60 | .size = 0, | |
| 61 | .alignment = 0, | |
| 62 | .prev = null, | |
| 63 | .next = null, | |
| 64 | .dbg_info_atom = undefined, | |
| 65 | }; | |
| 57 | pub fn getSymbolIndex(self: Atom) ?u32 { | |
| 58 | if (self.sym_index == 0) return null; | |
| 59 | return self.sym_index; | |
| 60 | } | |
| 66 | 61 | |
| 67 | 62 | /// Returns symbol referencing this atom. |
| 68 | 63 | pub fn getSymbol(self: Atom, macho_file: *MachO) macho.nlist_64 { |
| ... | ... | @@ -71,20 +66,23 @@ pub fn getSymbol(self: Atom, macho_file: *MachO) macho.nlist_64 { |
| 71 | 66 | |
| 72 | 67 | /// Returns pointer-to-symbol referencing this atom. |
| 73 | 68 | pub fn getSymbolPtr(self: Atom, macho_file: *MachO) *macho.nlist_64 { |
| 69 | const sym_index = self.getSymbolIndex().?; | |
| 74 | 70 | return macho_file.getSymbolPtr(.{ |
| 75 | .sym_index = self.sym_index, | |
| 71 | .sym_index = sym_index, | |
| 76 | 72 | .file = self.file, |
| 77 | 73 | }); |
| 78 | 74 | } |
| 79 | 75 | |
| 80 | 76 | pub fn getSymbolWithLoc(self: Atom) SymbolWithLoc { |
| 81 | return .{ .sym_index = self.sym_index, .file = self.file }; | |
| 77 | const sym_index = self.getSymbolIndex().?; | |
| 78 | return .{ .sym_index = sym_index, .file = self.file }; | |
| 82 | 79 | } |
| 83 | 80 | |
| 84 | 81 | /// Returns the name of this atom. |
| 85 | 82 | pub fn getName(self: Atom, macho_file: *MachO) []const u8 { |
| 83 | const sym_index = self.getSymbolIndex().?; | |
| 86 | 84 | return macho_file.getSymbolName(.{ |
| 87 | .sym_index = self.sym_index, | |
| 85 | .sym_index = sym_index, | |
| 88 | 86 | .file = self.file, |
| 89 | 87 | }); |
| 90 | 88 | } |
| ... | ... | @@ -94,7 +92,8 @@ pub fn getName(self: Atom, macho_file: *MachO) []const u8 { |
| 94 | 92 | /// this calculation. |
| 95 | 93 | pub fn capacity(self: Atom, macho_file: *MachO) u64 { |
| 96 | 94 | const self_sym = self.getSymbol(macho_file); |
| 97 | if (self.next) |next| { | |
| 95 | if (self.next_index) |next_index| { | |
| 96 | const next = macho_file.getAtom(next_index); | |
| 98 | 97 | const next_sym = next.getSymbol(macho_file); |
| 99 | 98 | return next_sym.n_value - self_sym.n_value; |
| 100 | 99 | } else { |
| ... | ... | @@ -106,7 +105,8 @@ pub fn capacity(self: Atom, macho_file: *MachO) u64 { |
| 106 | 105 | |
| 107 | 106 | pub fn freeListEligible(self: Atom, macho_file: *MachO) bool { |
| 108 | 107 | // No need to keep a free list node for the last atom. |
| 109 | const next = self.next orelse return false; | |
| 108 | const next_index = self.next_index orelse return false; | |
| 109 | const next = macho_file.getAtom(next_index); | |
| 110 | 110 | const self_sym = self.getSymbol(macho_file); |
| 111 | 111 | const next_sym = next.getSymbol(macho_file); |
| 112 | 112 | const cap = next_sym.n_value - self_sym.n_value; |
| ... | ... | @@ -116,19 +116,19 @@ pub fn freeListEligible(self: Atom, macho_file: *MachO) bool { |
| 116 | 116 | return surplus >= MachO.min_text_capacity; |
| 117 | 117 | } |
| 118 | 118 | |
| 119 | pub fn addRelocation(self: *Atom, macho_file: *MachO, reloc: Relocation) !void { | |
| 120 | return self.addRelocations(macho_file, 1, .{reloc}); | |
| 119 | pub fn addRelocation(macho_file: *MachO, atom_index: Index, reloc: Relocation) !void { | |
| 120 | return addRelocations(macho_file, atom_index, 1, .{reloc}); | |
| 121 | 121 | } |
| 122 | 122 | |
| 123 | 123 | pub fn addRelocations( |
| 124 | self: *Atom, | |
| 125 | 124 | macho_file: *MachO, |
| 125 | atom_index: Index, | |
| 126 | 126 | comptime count: comptime_int, |
| 127 | 127 | relocs: [count]Relocation, |
| 128 | 128 | ) !void { |
| 129 | 129 | const gpa = macho_file.base.allocator; |
| 130 | 130 | const target = macho_file.base.options.target; |
| 131 | const gop = try macho_file.relocs.getOrPut(gpa, self); | |
| 131 | const gop = try macho_file.relocs.getOrPut(gpa, atom_index); | |
| 132 | 132 | if (!gop.found_existing) { |
| 133 | 133 | gop.value_ptr.* = .{}; |
| 134 | 134 | } |
| ... | ... | @@ -142,56 +142,72 @@ pub fn addRelocations( |
| 142 | 142 | } |
| 143 | 143 | } |
| 144 | 144 | |
| 145 | pub fn addRebase(self: *Atom, macho_file: *MachO, offset: u32) !void { | |
| 145 | pub fn addRebase(macho_file: *MachO, atom_index: Index, offset: u32) !void { | |
| 146 | 146 | const gpa = macho_file.base.allocator; |
| 147 | log.debug(" (adding rebase at offset 0x{x} in %{d})", .{ offset, self.sym_index }); | |
| 148 | const gop = try macho_file.rebases.getOrPut(gpa, self); | |
| 147 | const atom = macho_file.getAtom(atom_index); | |
| 148 | log.debug(" (adding rebase at offset 0x{x} in %{?d})", .{ offset, atom.getSymbolIndex() }); | |
| 149 | const gop = try macho_file.rebases.getOrPut(gpa, atom_index); | |
| 149 | 150 | if (!gop.found_existing) { |
| 150 | 151 | gop.value_ptr.* = .{}; |
| 151 | 152 | } |
| 152 | 153 | try gop.value_ptr.append(gpa, offset); |
| 153 | 154 | } |
| 154 | 155 | |
| 155 | pub fn addBinding(self: *Atom, macho_file: *MachO, binding: Binding) !void { | |
| 156 | pub fn addBinding(macho_file: *MachO, atom_index: Index, binding: Binding) !void { | |
| 156 | 157 | const gpa = macho_file.base.allocator; |
| 157 | log.debug(" (adding binding to symbol {s} at offset 0x{x} in %{d})", .{ | |
| 158 | const atom = macho_file.getAtom(atom_index); | |
| 159 | log.debug(" (adding binding to symbol {s} at offset 0x{x} in %{?d})", .{ | |
| 158 | 160 | macho_file.getSymbolName(binding.target), |
| 159 | 161 | binding.offset, |
| 160 | self.sym_index, | |
| 162 | atom.getSymbolIndex(), | |
| 161 | 163 | }); |
| 162 | const gop = try macho_file.bindings.getOrPut(gpa, self); | |
| 164 | const gop = try macho_file.bindings.getOrPut(gpa, atom_index); | |
| 163 | 165 | if (!gop.found_existing) { |
| 164 | 166 | gop.value_ptr.* = .{}; |
| 165 | 167 | } |
| 166 | 168 | try gop.value_ptr.append(gpa, binding); |
| 167 | 169 | } |
| 168 | 170 | |
| 169 | pub fn addLazyBinding(self: *Atom, macho_file: *MachO, binding: Binding) !void { | |
| 171 | pub fn addLazyBinding(macho_file: *MachO, atom_index: Index, binding: Binding) !void { | |
| 170 | 172 | const gpa = macho_file.base.allocator; |
| 171 | log.debug(" (adding lazy binding to symbol {s} at offset 0x{x} in %{d})", .{ | |
| 173 | const atom = macho_file.getAtom(atom_index); | |
| 174 | log.debug(" (adding lazy binding to symbol {s} at offset 0x{x} in %{?d})", .{ | |
| 172 | 175 | macho_file.getSymbolName(binding.target), |
| 173 | 176 | binding.offset, |
| 174 | self.sym_index, | |
| 177 | atom.getSymbolIndex(), | |
| 175 | 178 | }); |
| 176 | const gop = try macho_file.lazy_bindings.getOrPut(gpa, self); | |
| 179 | const gop = try macho_file.lazy_bindings.getOrPut(gpa, atom_index); | |
| 177 | 180 | if (!gop.found_existing) { |
| 178 | 181 | gop.value_ptr.* = .{}; |
| 179 | 182 | } |
| 180 | 183 | try gop.value_ptr.append(gpa, binding); |
| 181 | 184 | } |
| 182 | 185 | |
| 183 | pub fn resolveRelocations(self: *Atom, macho_file: *MachO) !void { | |
| 184 | const relocs = macho_file.relocs.get(self) orelse return; | |
| 185 | const source_sym = self.getSymbol(macho_file); | |
| 186 | pub fn resolveRelocations(macho_file: *MachO, atom_index: Index) !void { | |
| 187 | const atom = macho_file.getAtom(atom_index); | |
| 188 | const relocs = macho_file.relocs.get(atom_index) orelse return; | |
| 189 | const source_sym = atom.getSymbol(macho_file); | |
| 186 | 190 | const source_section = macho_file.sections.get(source_sym.n_sect - 1).header; |
| 187 | 191 | const file_offset = source_section.offset + source_sym.n_value - source_section.addr; |
| 188 | 192 | |
| 189 | log.debug("relocating '{s}'", .{self.getName(macho_file)}); | |
| 193 | log.debug("relocating '{s}'", .{atom.getName(macho_file)}); | |
| 190 | 194 | |
| 191 | 195 | for (relocs.items) |*reloc| { |
| 192 | 196 | if (!reloc.dirty) continue; |
| 193 | 197 | |
| 194 | try reloc.resolve(self, macho_file, file_offset); | |
| 198 | try reloc.resolve(macho_file, atom_index, file_offset); | |
| 195 | 199 | reloc.dirty = false; |
| 196 | 200 | } |
| 197 | 201 | } |
| 202 | ||
| 203 | pub fn freeRelocations(macho_file: *MachO, atom_index: Index) void { | |
| 204 | const gpa = macho_file.base.allocator; | |
| 205 | var removed_relocs = macho_file.relocs.fetchOrderedRemove(atom_index); | |
| 206 | if (removed_relocs) |*relocs| relocs.value.deinit(gpa); | |
| 207 | var removed_rebases = macho_file.rebases.fetchOrderedRemove(atom_index); | |
| 208 | if (removed_rebases) |*rebases| rebases.value.deinit(gpa); | |
| 209 | var removed_bindings = macho_file.bindings.fetchOrderedRemove(atom_index); | |
| 210 | if (removed_bindings) |*bindings| bindings.value.deinit(gpa); | |
| 211 | var removed_lazy_bindings = macho_file.lazy_bindings.fetchOrderedRemove(atom_index); | |
| 212 | if (removed_lazy_bindings) |*lazy_bindings| lazy_bindings.value.deinit(gpa); | |
| 213 | } |
src/link/MachO/DebugSymbols.zig+6-6| ... | ... | @@ -82,11 +82,11 @@ pub fn populateMissingMetadata(self: *DebugSymbols) !void { |
| 82 | 82 | } |
| 83 | 83 | |
| 84 | 84 | if (self.debug_str_section_index == null) { |
| 85 | assert(self.dwarf.strtab.items.len == 0); | |
| 86 | try self.dwarf.strtab.append(self.allocator, 0); | |
| 85 | assert(self.dwarf.strtab.buffer.items.len == 0); | |
| 86 | try self.dwarf.strtab.buffer.append(self.allocator, 0); | |
| 87 | 87 | self.debug_str_section_index = try self.allocateSection( |
| 88 | 88 | "__debug_str", |
| 89 | @intCast(u32, self.dwarf.strtab.items.len), | |
| 89 | @intCast(u32, self.dwarf.strtab.buffer.items.len), | |
| 90 | 90 | 0, |
| 91 | 91 | ); |
| 92 | 92 | self.debug_string_table_dirty = true; |
| ... | ... | @@ -291,10 +291,10 @@ pub fn flushModule(self: *DebugSymbols, macho_file: *MachO) !void { |
| 291 | 291 | |
| 292 | 292 | { |
| 293 | 293 | const sect_index = self.debug_str_section_index.?; |
| 294 | if (self.debug_string_table_dirty or self.dwarf.strtab.items.len != self.getSection(sect_index).size) { | |
| 295 | const needed_size = @intCast(u32, self.dwarf.strtab.items.len); | |
| 294 | if (self.debug_string_table_dirty or self.dwarf.strtab.buffer.items.len != self.getSection(sect_index).size) { | |
| 295 | const needed_size = @intCast(u32, self.dwarf.strtab.buffer.items.len); | |
| 296 | 296 | try self.growSection(sect_index, needed_size, false); |
| 297 | try self.file.pwriteAll(self.dwarf.strtab.items, self.getSection(sect_index).offset); | |
| 297 | try self.file.pwriteAll(self.dwarf.strtab.buffer.items, self.getSection(sect_index).offset); | |
| 298 | 298 | self.debug_string_table_dirty = false; |
| 299 | 299 | } |
| 300 | 300 | } |
src/link/MachO/Relocation.zig+9-7| ... | ... | @@ -29,33 +29,35 @@ pub fn fmtType(self: Relocation, target: std.Target) []const u8 { |
| 29 | 29 | } |
| 30 | 30 | } |
| 31 | 31 | |
| 32 | pub fn getTargetAtom(self: Relocation, macho_file: *MachO) ?*Atom { | |
| 32 | pub fn getTargetAtomIndex(self: Relocation, macho_file: *MachO) ?Atom.Index { | |
| 33 | 33 | switch (macho_file.base.options.target.cpu.arch) { |
| 34 | 34 | .aarch64 => switch (@intToEnum(macho.reloc_type_arm64, self.type)) { |
| 35 | 35 | .ARM64_RELOC_GOT_LOAD_PAGE21, |
| 36 | 36 | .ARM64_RELOC_GOT_LOAD_PAGEOFF12, |
| 37 | 37 | .ARM64_RELOC_POINTER_TO_GOT, |
| 38 | => return macho_file.getGotAtomForSymbol(self.target), | |
| 38 | => return macho_file.getGotAtomIndexForSymbol(self.target), | |
| 39 | 39 | else => {}, |
| 40 | 40 | }, |
| 41 | 41 | .x86_64 => switch (@intToEnum(macho.reloc_type_x86_64, self.type)) { |
| 42 | 42 | .X86_64_RELOC_GOT, |
| 43 | 43 | .X86_64_RELOC_GOT_LOAD, |
| 44 | => return macho_file.getGotAtomForSymbol(self.target), | |
| 44 | => return macho_file.getGotAtomIndexForSymbol(self.target), | |
| 45 | 45 | else => {}, |
| 46 | 46 | }, |
| 47 | 47 | else => unreachable, |
| 48 | 48 | } |
| 49 | if (macho_file.getStubsAtomForSymbol(self.target)) |stubs_atom| return stubs_atom; | |
| 50 | return macho_file.getAtomForSymbol(self.target); | |
| 49 | if (macho_file.getStubsAtomIndexForSymbol(self.target)) |stubs_atom| return stubs_atom; | |
| 50 | return macho_file.getAtomIndexForSymbol(self.target); | |
| 51 | 51 | } |
| 52 | 52 | |
| 53 | pub fn resolve(self: Relocation, atom: *Atom, macho_file: *MachO, base_offset: u64) !void { | |
| 53 | pub fn resolve(self: Relocation, macho_file: *MachO, atom_index: Atom.Index, base_offset: u64) !void { | |
| 54 | 54 | const arch = macho_file.base.options.target.cpu.arch; |
| 55 | const atom = macho_file.getAtom(atom_index); | |
| 55 | 56 | const source_sym = atom.getSymbol(macho_file); |
| 56 | 57 | const source_addr = source_sym.n_value + self.offset; |
| 57 | 58 | |
| 58 | const target_atom = self.getTargetAtom(macho_file) orelse return; | |
| 59 | const target_atom_index = self.getTargetAtomIndex(macho_file) orelse return; | |
| 60 | const target_atom = macho_file.getAtom(target_atom_index); | |
| 59 | 61 | const target_addr = @intCast(i64, target_atom.getSymbol(macho_file).n_value) + self.addend; |
| 60 | 62 | |
| 61 | 63 | log.debug(" ({x}: [() => 0x{x} ({s})) ({s})", .{ |
src/link/MachO/load_commands.zig+1-1| ... | ... | @@ -12,7 +12,7 @@ pub const default_dyld_path: [*:0]const u8 = "/usr/lib/dyld"; |
| 12 | 12 | |
| 13 | 13 | fn calcInstallNameLen(cmd_size: u64, name: []const u8, assume_max_path_len: bool) u64 { |
| 14 | 14 | const darwin_path_max = 1024; |
| 15 | const name_len = if (assume_max_path_len) darwin_path_max else std.mem.len(name) + 1; | |
| 15 | const name_len = if (assume_max_path_len) darwin_path_max else name.len + 1; | |
| 16 | 16 | return mem.alignForwardGeneric(u64, cmd_size + name_len, @alignOf(u64)); |
| 17 | 17 | } |
| 18 | 18 |
src/link/MachO/zld.zig+7-4| ... | ... | @@ -3596,7 +3596,8 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr |
| 3596 | 3596 | man.hash.addOptionalBytes(options.sysroot); |
| 3597 | 3597 | try man.addOptionalFile(options.entitlements); |
| 3598 | 3598 | |
| 3599 | // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock. | |
| 3599 | // We don't actually care whether it's a cache hit or miss; we just | |
| 3600 | // need the digest and the lock. | |
| 3600 | 3601 | _ = try man.hit(); |
| 3601 | 3602 | digest = man.final(); |
| 3602 | 3603 | |
| ... | ... | @@ -4177,9 +4178,11 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr |
| 4177 | 4178 | log.debug("failed to save linking hash digest file: {s}", .{@errorName(err)}); |
| 4178 | 4179 | }; |
| 4179 | 4180 | // Again failure here only means an unnecessary cache miss. |
| 4180 | man.writeManifest() catch |err| { | |
| 4181 | log.debug("failed to write cache manifest when linking: {s}", .{@errorName(err)}); | |
| 4182 | }; | |
| 4181 | if (man.have_exclusive_lock) { | |
| 4182 | man.writeManifest() catch |err| { | |
| 4183 | log.debug("failed to write cache manifest when linking: {s}", .{@errorName(err)}); | |
| 4184 | }; | |
| 4185 | } | |
| 4183 | 4186 | // We hang on to this lock so that the output file path can be used without |
| 4184 | 4187 | // other processes clobbering it. |
| 4185 | 4188 | macho_file.base.lock = man.toOwnedLock(); |
src/link/Plan9.zig+154-95| ... | ... | @@ -21,14 +21,7 @@ const Allocator = std.mem.Allocator; |
| 21 | 21 | const log = std.log.scoped(.link); |
| 22 | 22 | const assert = std.debug.assert; |
| 23 | 23 | |
| 24 | const FnDeclOutput = struct { | |
| 25 | /// this code is modified when relocated so it is mutable | |
| 26 | code: []u8, | |
| 27 | /// this might have to be modified in the linker, so thats why its mutable | |
| 28 | lineinfo: []u8, | |
| 29 | start_line: u32, | |
| 30 | end_line: u32, | |
| 31 | }; | |
| 24 | pub const base_tag = .plan9; | |
| 32 | 25 | |
| 33 | 26 | base: link.File, |
| 34 | 27 | sixtyfour_bit: bool, |
| ... | ... | @@ -101,6 +94,9 @@ got_index_free_list: std.ArrayListUnmanaged(usize) = .{}, |
| 101 | 94 | |
| 102 | 95 | syms_index_free_list: std.ArrayListUnmanaged(usize) = .{}, |
| 103 | 96 | |
| 97 | decl_blocks: std.ArrayListUnmanaged(DeclBlock) = .{}, | |
| 98 | decls: std.AutoHashMapUnmanaged(Module.Decl.Index, DeclMetadata) = .{}, | |
| 99 | ||
| 104 | 100 | const Reloc = struct { |
| 105 | 101 | target: Module.Decl.Index, |
| 106 | 102 | offset: u64, |
| ... | ... | @@ -115,6 +111,42 @@ const Bases = struct { |
| 115 | 111 | |
| 116 | 112 | const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(struct { info: DeclBlock, code: []const u8 })); |
| 117 | 113 | |
| 114 | pub const PtrWidth = enum { p32, p64 }; | |
| 115 | ||
| 116 | pub const DeclBlock = struct { | |
| 117 | type: aout.Sym.Type, | |
| 118 | /// offset in the text or data sects | |
| 119 | offset: ?u64, | |
| 120 | /// offset into syms | |
| 121 | sym_index: ?usize, | |
| 122 | /// offset into got | |
| 123 | got_index: ?usize, | |
| 124 | ||
| 125 | pub const Index = u32; | |
| 126 | }; | |
| 127 | ||
| 128 | const DeclMetadata = struct { | |
| 129 | index: DeclBlock.Index, | |
| 130 | exports: std.ArrayListUnmanaged(usize) = .{}, | |
| 131 | ||
| 132 | fn getExport(m: DeclMetadata, p9: *const Plan9, name: []const u8) ?usize { | |
| 133 | for (m.exports.items) |exp| { | |
| 134 | const sym = p9.syms.items[exp]; | |
| 135 | if (mem.eql(u8, name, sym.name)) return exp; | |
| 136 | } | |
| 137 | return null; | |
| 138 | } | |
| 139 | }; | |
| 140 | ||
| 141 | const FnDeclOutput = struct { | |
| 142 | /// this code is modified when relocated so it is mutable | |
| 143 | code: []u8, | |
| 144 | /// this might have to be modified in the linker, so thats why its mutable | |
| 145 | lineinfo: []u8, | |
| 146 | start_line: u32, | |
| 147 | end_line: u32, | |
| 148 | }; | |
| 149 | ||
| 118 | 150 | fn getAddr(self: Plan9, addr: u64, t: aout.Sym.Type) u64 { |
| 119 | 151 | return addr + switch (t) { |
| 120 | 152 | .T, .t, .l, .L => self.bases.text, |
| ... | ... | @@ -127,22 +159,6 @@ fn getSymAddr(self: Plan9, s: aout.Sym) u64 { |
| 127 | 159 | return self.getAddr(s.value, s.type); |
| 128 | 160 | } |
| 129 | 161 | |
| 130 | pub const DeclBlock = struct { | |
| 131 | type: aout.Sym.Type, | |
| 132 | /// offset in the text or data sects | |
| 133 | offset: ?u64, | |
| 134 | /// offset into syms | |
| 135 | sym_index: ?usize, | |
| 136 | /// offset into got | |
| 137 | got_index: ?usize, | |
| 138 | pub const empty = DeclBlock{ | |
| 139 | .type = .t, | |
| 140 | .offset = null, | |
| 141 | .sym_index = null, | |
| 142 | .got_index = null, | |
| 143 | }; | |
| 144 | }; | |
| 145 | ||
| 146 | 162 | pub fn defaultBaseAddrs(arch: std.Target.Cpu.Arch) Bases { |
| 147 | 163 | return switch (arch) { |
| 148 | 164 | .x86_64 => .{ |
| ... | ... | @@ -164,8 +180,6 @@ pub fn defaultBaseAddrs(arch: std.Target.Cpu.Arch) Bases { |
| 164 | 180 | }; |
| 165 | 181 | } |
| 166 | 182 | |
| 167 | pub const PtrWidth = enum { p32, p64 }; | |
| 168 | ||
| 169 | 183 | pub fn createEmpty(gpa: Allocator, options: link.Options) !*Plan9 { |
| 170 | 184 | if (options.use_llvm) |
| 171 | 185 | return error.LLVMBackendDoesNotSupportPlan9; |
| ... | ... | @@ -271,7 +285,7 @@ pub fn updateFunc(self: *Plan9, module: *Module, func: *Module.Fn, air: Air, liv |
| 271 | 285 | const decl = module.declPtr(decl_index); |
| 272 | 286 | self.freeUnnamedConsts(decl_index); |
| 273 | 287 | |
| 274 | try self.seeDecl(decl_index); | |
| 288 | _ = try self.seeDecl(decl_index); | |
| 275 | 289 | log.debug("codegen decl {*} ({s})", .{ decl, decl.name }); |
| 276 | 290 | |
| 277 | 291 | var code_buffer = std.ArrayList(u8).init(self.base.allocator); |
| ... | ... | @@ -299,7 +313,7 @@ pub fn updateFunc(self: *Plan9, module: *Module, func: *Module.Fn, air: Air, liv |
| 299 | 313 | }, |
| 300 | 314 | ); |
| 301 | 315 | const code = switch (res) { |
| 302 | .appended => try code_buffer.toOwnedSlice(), | |
| 316 | .ok => try code_buffer.toOwnedSlice(), | |
| 303 | 317 | .fail => |em| { |
| 304 | 318 | decl.analysis = .codegen_failure; |
| 305 | 319 | try module.failed_decls.put(module.gpa, decl_index, em); |
| ... | ... | @@ -313,11 +327,11 @@ pub fn updateFunc(self: *Plan9, module: *Module, func: *Module.Fn, air: Air, liv |
| 313 | 327 | .end_line = end_line, |
| 314 | 328 | }; |
| 315 | 329 | try self.putFn(decl_index, out); |
| 316 | return self.updateFinish(decl); | |
| 330 | return self.updateFinish(decl_index); | |
| 317 | 331 | } |
| 318 | 332 | |
| 319 | 333 | pub fn lowerUnnamedConst(self: *Plan9, tv: TypedValue, decl_index: Module.Decl.Index) !u32 { |
| 320 | try self.seeDecl(decl_index); | |
| 334 | _ = try self.seeDecl(decl_index); | |
| 321 | 335 | var code_buffer = std.ArrayList(u8).init(self.base.allocator); |
| 322 | 336 | defer code_buffer.deinit(); |
| 323 | 337 | |
| ... | ... | @@ -358,8 +372,7 @@ pub fn lowerUnnamedConst(self: *Plan9, tv: TypedValue, decl_index: Module.Decl.I |
| 358 | 372 | .parent_atom_index = @enumToInt(decl_index), |
| 359 | 373 | }); |
| 360 | 374 | const code = switch (res) { |
| 361 | .externally_managed => |x| x, | |
| 362 | .appended => code_buffer.items, | |
| 375 | .ok => code_buffer.items, | |
| 363 | 376 | .fail => |em| { |
| 364 | 377 | decl.analysis = .codegen_failure; |
| 365 | 378 | try mod.failed_decls.put(mod.gpa, decl_index, em); |
| ... | ... | @@ -388,7 +401,7 @@ pub fn updateDecl(self: *Plan9, module: *Module, decl_index: Module.Decl.Index) |
| 388 | 401 | } |
| 389 | 402 | } |
| 390 | 403 | |
| 391 | try self.seeDecl(decl_index); | |
| 404 | _ = try self.seeDecl(decl_index); | |
| 392 | 405 | |
| 393 | 406 | log.debug("codegen decl {*} ({s}) ({d})", .{ decl, decl.name, decl_index }); |
| 394 | 407 | |
| ... | ... | @@ -403,8 +416,7 @@ pub fn updateDecl(self: *Plan9, module: *Module, decl_index: Module.Decl.Index) |
| 403 | 416 | .parent_atom_index = @enumToInt(decl_index), |
| 404 | 417 | }); |
| 405 | 418 | const code = switch (res) { |
| 406 | .externally_managed => |x| x, | |
| 407 | .appended => code_buffer.items, | |
| 419 | .ok => code_buffer.items, | |
| 408 | 420 | .fail => |em| { |
| 409 | 421 | decl.analysis = .codegen_failure; |
| 410 | 422 | try module.failed_decls.put(module.gpa, decl_index, em); |
| ... | ... | @@ -416,28 +428,31 @@ pub fn updateDecl(self: *Plan9, module: *Module, decl_index: Module.Decl.Index) |
| 416 | 428 | if (self.data_decl_table.fetchPutAssumeCapacity(decl_index, duped_code)) |old_entry| { |
| 417 | 429 | self.base.allocator.free(old_entry.value); |
| 418 | 430 | } |
| 419 | return self.updateFinish(decl); | |
| 431 | return self.updateFinish(decl_index); | |
| 420 | 432 | } |
| 421 | 433 | /// called at the end of update{Decl,Func} |
| 422 | fn updateFinish(self: *Plan9, decl: *Module.Decl) !void { | |
| 434 | fn updateFinish(self: *Plan9, decl_index: Module.Decl.Index) !void { | |
| 435 | const decl = self.base.options.module.?.declPtr(decl_index); | |
| 423 | 436 | const is_fn = (decl.ty.zigTypeTag() == .Fn); |
| 424 | 437 | log.debug("update the symbol table and got for decl {*} ({s})", .{ decl, decl.name }); |
| 425 | 438 | const sym_t: aout.Sym.Type = if (is_fn) .t else .d; |
| 439 | ||
| 440 | const decl_block = self.getDeclBlockPtr(self.decls.get(decl_index).?.index); | |
| 426 | 441 | // write the internal linker metadata |
| 427 | decl.link.plan9.type = sym_t; | |
| 442 | decl_block.type = sym_t; | |
| 428 | 443 | // write the symbol |
| 429 | // we already have the got index because that got allocated in allocateDeclIndexes | |
| 444 | // we already have the got index | |
| 430 | 445 | const sym: aout.Sym = .{ |
| 431 | 446 | .value = undefined, // the value of stuff gets filled in in flushModule |
| 432 | .type = decl.link.plan9.type, | |
| 447 | .type = decl_block.type, | |
| 433 | 448 | .name = mem.span(decl.name), |
| 434 | 449 | }; |
| 435 | 450 | |
| 436 | if (decl.link.plan9.sym_index) |s| { | |
| 451 | if (decl_block.sym_index) |s| { | |
| 437 | 452 | self.syms.items[s] = sym; |
| 438 | 453 | } else { |
| 439 | 454 | const s = try self.allocateSymbolIndex(); |
| 440 | decl.link.plan9.sym_index = s; | |
| 455 | decl_block.sym_index = s; | |
| 441 | 456 | self.syms.items[s] = sym; |
| 442 | 457 | } |
| 443 | 458 | } |
| ... | ... | @@ -552,6 +567,7 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No |
| 552 | 567 | while (it.next()) |entry| { |
| 553 | 568 | const decl_index = entry.key_ptr.*; |
| 554 | 569 | const decl = mod.declPtr(decl_index); |
| 570 | const decl_block = self.getDeclBlockPtr(self.decls.get(decl_index).?.index); | |
| 555 | 571 | const out = entry.value_ptr.*; |
| 556 | 572 | log.debug("write text decl {*} ({s}), lines {d} to {d}", .{ decl, decl.name, out.start_line + 1, out.end_line }); |
| 557 | 573 | { |
| ... | ... | @@ -570,16 +586,16 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No |
| 570 | 586 | iovecs_i += 1; |
| 571 | 587 | const off = self.getAddr(text_i, .t); |
| 572 | 588 | text_i += out.code.len; |
| 573 | decl.link.plan9.offset = off; | |
| 589 | decl_block.offset = off; | |
| 574 | 590 | if (!self.sixtyfour_bit) { |
| 575 | mem.writeIntNative(u32, got_table[decl.link.plan9.got_index.? * 4 ..][0..4], @intCast(u32, off)); | |
| 576 | mem.writeInt(u32, got_table[decl.link.plan9.got_index.? * 4 ..][0..4], @intCast(u32, off), self.base.options.target.cpu.arch.endian()); | |
| 591 | mem.writeIntNative(u32, got_table[decl_block.got_index.? * 4 ..][0..4], @intCast(u32, off)); | |
| 592 | mem.writeInt(u32, got_table[decl_block.got_index.? * 4 ..][0..4], @intCast(u32, off), self.base.options.target.cpu.arch.endian()); | |
| 577 | 593 | } else { |
| 578 | mem.writeInt(u64, got_table[decl.link.plan9.got_index.? * 8 ..][0..8], off, self.base.options.target.cpu.arch.endian()); | |
| 594 | mem.writeInt(u64, got_table[decl_block.got_index.? * 8 ..][0..8], off, self.base.options.target.cpu.arch.endian()); | |
| 579 | 595 | } |
| 580 | self.syms.items[decl.link.plan9.sym_index.?].value = off; | |
| 596 | self.syms.items[decl_block.sym_index.?].value = off; | |
| 581 | 597 | if (mod.decl_exports.get(decl_index)) |exports| { |
| 582 | try self.addDeclExports(mod, decl, exports.items); | |
| 598 | try self.addDeclExports(mod, decl_index, exports.items); | |
| 583 | 599 | } |
| 584 | 600 | } |
| 585 | 601 | } |
| ... | ... | @@ -600,6 +616,7 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No |
| 600 | 616 | while (it.next()) |entry| { |
| 601 | 617 | const decl_index = entry.key_ptr.*; |
| 602 | 618 | const decl = mod.declPtr(decl_index); |
| 619 | const decl_block = self.getDeclBlockPtr(self.decls.get(decl_index).?.index); | |
| 603 | 620 | const code = entry.value_ptr.*; |
| 604 | 621 | log.debug("write data decl {*} ({s})", .{ decl, decl.name }); |
| 605 | 622 | |
| ... | ... | @@ -608,15 +625,15 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No |
| 608 | 625 | iovecs_i += 1; |
| 609 | 626 | const off = self.getAddr(data_i, .d); |
| 610 | 627 | data_i += code.len; |
| 611 | decl.link.plan9.offset = off; | |
| 628 | decl_block.offset = off; | |
| 612 | 629 | if (!self.sixtyfour_bit) { |
| 613 | mem.writeInt(u32, got_table[decl.link.plan9.got_index.? * 4 ..][0..4], @intCast(u32, off), self.base.options.target.cpu.arch.endian()); | |
| 630 | mem.writeInt(u32, got_table[decl_block.got_index.? * 4 ..][0..4], @intCast(u32, off), self.base.options.target.cpu.arch.endian()); | |
| 614 | 631 | } else { |
| 615 | mem.writeInt(u64, got_table[decl.link.plan9.got_index.? * 8 ..][0..8], off, self.base.options.target.cpu.arch.endian()); | |
| 632 | mem.writeInt(u64, got_table[decl_block.got_index.? * 8 ..][0..8], off, self.base.options.target.cpu.arch.endian()); | |
| 616 | 633 | } |
| 617 | self.syms.items[decl.link.plan9.sym_index.?].value = off; | |
| 634 | self.syms.items[decl_block.sym_index.?].value = off; | |
| 618 | 635 | if (mod.decl_exports.get(decl_index)) |exports| { |
| 619 | try self.addDeclExports(mod, decl, exports.items); | |
| 636 | try self.addDeclExports(mod, decl_index, exports.items); | |
| 620 | 637 | } |
| 621 | 638 | } |
| 622 | 639 | // write the unnamed constants after the other data decls |
| ... | ... | @@ -678,7 +695,8 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No |
| 678 | 695 | for (kv.value_ptr.items) |reloc| { |
| 679 | 696 | const target_decl_index = reloc.target; |
| 680 | 697 | const target_decl = mod.declPtr(target_decl_index); |
| 681 | const target_decl_offset = target_decl.link.plan9.offset.?; | |
| 698 | const target_decl_block = self.getDeclBlock(self.decls.get(target_decl_index).?.index); | |
| 699 | const target_decl_offset = target_decl_block.offset.?; | |
| 682 | 700 | |
| 683 | 701 | const offset = reloc.offset; |
| 684 | 702 | const addend = reloc.addend; |
| ... | ... | @@ -711,35 +729,43 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No |
| 711 | 729 | fn addDeclExports( |
| 712 | 730 | self: *Plan9, |
| 713 | 731 | module: *Module, |
| 714 | decl: *Module.Decl, | |
| 732 | decl_index: Module.Decl.Index, | |
| 715 | 733 | exports: []const *Module.Export, |
| 716 | 734 | ) !void { |
| 735 | const metadata = self.decls.getPtr(decl_index).?; | |
| 736 | const decl_block = self.getDeclBlock(metadata.index); | |
| 737 | ||
| 717 | 738 | for (exports) |exp| { |
| 718 | 739 | // plan9 does not support custom sections |
| 719 | 740 | if (exp.options.section) |section_name| { |
| 720 | 741 | if (!mem.eql(u8, section_name, ".text") or !mem.eql(u8, section_name, ".data")) { |
| 721 | try module.failed_exports.put(module.gpa, exp, try Module.ErrorMsg.create(self.base.allocator, decl.srcLoc(), "plan9 does not support extra sections", .{})); | |
| 742 | try module.failed_exports.put(module.gpa, exp, try Module.ErrorMsg.create( | |
| 743 | self.base.allocator, | |
| 744 | module.declPtr(decl_index).srcLoc(), | |
| 745 | "plan9 does not support extra sections", | |
| 746 | .{}, | |
| 747 | )); | |
| 722 | 748 | break; |
| 723 | 749 | } |
| 724 | 750 | } |
| 725 | 751 | const sym = .{ |
| 726 | .value = decl.link.plan9.offset.?, | |
| 727 | .type = decl.link.plan9.type.toGlobal(), | |
| 752 | .value = decl_block.offset.?, | |
| 753 | .type = decl_block.type.toGlobal(), | |
| 728 | 754 | .name = exp.options.name, |
| 729 | 755 | }; |
| 730 | 756 | |
| 731 | if (exp.link.plan9) |i| { | |
| 757 | if (metadata.getExport(self, exp.options.name)) |i| { | |
| 732 | 758 | self.syms.items[i] = sym; |
| 733 | 759 | } else { |
| 734 | 760 | try self.syms.append(self.base.allocator, sym); |
| 735 | exp.link.plan9 = self.syms.items.len - 1; | |
| 761 | try metadata.exports.append(self.base.allocator, self.syms.items.len - 1); | |
| 736 | 762 | } |
| 737 | 763 | } |
| 738 | 764 | } |
| 739 | 765 | |
| 740 | 766 | pub fn freeDecl(self: *Plan9, decl_index: Module.Decl.Index) void { |
| 741 | 767 | // TODO audit the lifetimes of decls table entries. It's possible to get |
| 742 | // allocateDeclIndexes and then freeDecl without any updateDecl in between. | |
| 768 | // freeDecl without any updateDecl in between. | |
| 743 | 769 | // However that is planned to change, see the TODO comment in Module.zig |
| 744 | 770 | // in the deleteUnusedDecl function. |
| 745 | 771 | const mod = self.base.options.module.?; |
| ... | ... | @@ -762,13 +788,18 @@ pub fn freeDecl(self: *Plan9, decl_index: Module.Decl.Index) void { |
| 762 | 788 | self.base.allocator.free(removed_entry.value); |
| 763 | 789 | } |
| 764 | 790 | } |
| 765 | if (decl.link.plan9.got_index) |i| { | |
| 766 | // TODO: if this catch {} is triggered, an assertion in flushModule will be triggered, because got_index_free_list will have the wrong length | |
| 767 | self.got_index_free_list.append(self.base.allocator, i) catch {}; | |
| 768 | } | |
| 769 | if (decl.link.plan9.sym_index) |i| { | |
| 770 | self.syms_index_free_list.append(self.base.allocator, i) catch {}; | |
| 771 | self.syms.items[i] = aout.Sym.undefined_symbol; | |
| 791 | if (self.decls.fetchRemove(decl_index)) |const_kv| { | |
| 792 | var kv = const_kv; | |
| 793 | const decl_block = self.getDeclBlock(kv.value.index); | |
| 794 | if (decl_block.got_index) |i| { | |
| 795 | // TODO: if this catch {} is triggered, an assertion in flushModule will be triggered, because got_index_free_list will have the wrong length | |
| 796 | self.got_index_free_list.append(self.base.allocator, i) catch {}; | |
| 797 | } | |
| 798 | if (decl_block.sym_index) |i| { | |
| 799 | self.syms_index_free_list.append(self.base.allocator, i) catch {}; | |
| 800 | self.syms.items[i] = aout.Sym.undefined_symbol; | |
| 801 | } | |
| 802 | kv.value.exports.deinit(self.base.allocator); | |
| 772 | 803 | } |
| 773 | 804 | self.freeUnnamedConsts(decl_index); |
| 774 | 805 | { |
| ... | ... | @@ -788,12 +819,30 @@ fn freeUnnamedConsts(self: *Plan9, decl_index: Module.Decl.Index) void { |
| 788 | 819 | unnamed_consts.clearAndFree(self.base.allocator); |
| 789 | 820 | } |
| 790 | 821 | |
| 791 | pub fn seeDecl(self: *Plan9, decl_index: Module.Decl.Index) !void { | |
| 792 | const mod = self.base.options.module.?; | |
| 793 | const decl = mod.declPtr(decl_index); | |
| 794 | if (decl.link.plan9.got_index == null) { | |
| 795 | decl.link.plan9.got_index = self.allocateGotIndex(); | |
| 822 | fn createDeclBlock(self: *Plan9) !DeclBlock.Index { | |
| 823 | const gpa = self.base.allocator; | |
| 824 | const index = @intCast(DeclBlock.Index, self.decl_blocks.items.len); | |
| 825 | const decl_block = try self.decl_blocks.addOne(gpa); | |
| 826 | decl_block.* = .{ | |
| 827 | .type = .t, | |
| 828 | .offset = null, | |
| 829 | .sym_index = null, | |
| 830 | .got_index = null, | |
| 831 | }; | |
| 832 | return index; | |
| 833 | } | |
| 834 | ||
| 835 | pub fn seeDecl(self: *Plan9, decl_index: Module.Decl.Index) !DeclBlock.Index { | |
| 836 | const gop = try self.decls.getOrPut(self.base.allocator, decl_index); | |
| 837 | if (!gop.found_existing) { | |
| 838 | const index = try self.createDeclBlock(); | |
| 839 | self.getDeclBlockPtr(index).got_index = self.allocateGotIndex(); | |
| 840 | gop.value_ptr.* = .{ | |
| 841 | .index = index, | |
| 842 | .exports = .{}, | |
| 843 | }; | |
| 796 | 844 | } |
| 845 | return gop.value_ptr.index; | |
| 797 | 846 | } |
| 798 | 847 | |
| 799 | 848 | pub fn updateDeclExports( |
| ... | ... | @@ -802,7 +851,7 @@ pub fn updateDeclExports( |
| 802 | 851 | decl_index: Module.Decl.Index, |
| 803 | 852 | exports: []const *Module.Export, |
| 804 | 853 | ) !void { |
| 805 | try self.seeDecl(decl_index); | |
| 854 | _ = try self.seeDecl(decl_index); | |
| 806 | 855 | // we do all the things in flush |
| 807 | 856 | _ = module; |
| 808 | 857 | _ = exports; |
| ... | ... | @@ -844,10 +893,17 @@ pub fn deinit(self: *Plan9) void { |
| 844 | 893 | self.syms_index_free_list.deinit(gpa); |
| 845 | 894 | self.file_segments.deinit(gpa); |
| 846 | 895 | self.path_arena.deinit(); |
| 896 | self.decl_blocks.deinit(gpa); | |
| 897 | ||
| 898 | { | |
| 899 | var it = self.decls.iterator(); | |
| 900 | while (it.next()) |entry| { | |
| 901 | entry.value_ptr.exports.deinit(gpa); | |
| 902 | } | |
| 903 | self.decls.deinit(gpa); | |
| 904 | } | |
| 847 | 905 | } |
| 848 | 906 | |
| 849 | pub const Export = ?usize; | |
| 850 | pub const base_tag = .plan9; | |
| 851 | 907 | pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Options) !*Plan9 { |
| 852 | 908 | if (options.use_llvm) |
| 853 | 909 | return error.LLVMBackendDoesNotSupportPlan9; |
| ... | ... | @@ -913,20 +969,19 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void { |
| 913 | 969 | } |
| 914 | 970 | } |
| 915 | 971 | |
| 916 | const mod = self.base.options.module.?; | |
| 917 | ||
| 918 | 972 | // write the data symbols |
| 919 | 973 | { |
| 920 | 974 | var it = self.data_decl_table.iterator(); |
| 921 | 975 | while (it.next()) |entry| { |
| 922 | 976 | const decl_index = entry.key_ptr.*; |
| 923 | const decl = mod.declPtr(decl_index); | |
| 924 | const sym = self.syms.items[decl.link.plan9.sym_index.?]; | |
| 977 | const decl_metadata = self.decls.get(decl_index).?; | |
| 978 | const decl_block = self.getDeclBlock(decl_metadata.index); | |
| 979 | const sym = self.syms.items[decl_block.sym_index.?]; | |
| 925 | 980 | try self.writeSym(writer, sym); |
| 926 | 981 | if (self.base.options.module.?.decl_exports.get(decl_index)) |exports| { |
| 927 | for (exports.items) |e| { | |
| 928 | try self.writeSym(writer, self.syms.items[e.link.plan9.?]); | |
| 929 | } | |
| 982 | for (exports.items) |e| if (decl_metadata.getExport(self, e.options.name)) |exp_i| { | |
| 983 | try self.writeSym(writer, self.syms.items[exp_i]); | |
| 984 | }; | |
| 930 | 985 | } |
| 931 | 986 | } |
| 932 | 987 | } |
| ... | ... | @@ -945,32 +1000,28 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void { |
| 945 | 1000 | var submap_it = symidx_and_submap.functions.iterator(); |
| 946 | 1001 | while (submap_it.next()) |entry| { |
| 947 | 1002 | const decl_index = entry.key_ptr.*; |
| 948 | const decl = mod.declPtr(decl_index); | |
| 949 | const sym = self.syms.items[decl.link.plan9.sym_index.?]; | |
| 1003 | const decl_metadata = self.decls.get(decl_index).?; | |
| 1004 | const decl_block = self.getDeclBlock(decl_metadata.index); | |
| 1005 | const sym = self.syms.items[decl_block.sym_index.?]; | |
| 950 | 1006 | try self.writeSym(writer, sym); |
| 951 | 1007 | if (self.base.options.module.?.decl_exports.get(decl_index)) |exports| { |
| 952 | for (exports.items) |e| { | |
| 953 | const s = self.syms.items[e.link.plan9.?]; | |
| 1008 | for (exports.items) |e| if (decl_metadata.getExport(self, e.options.name)) |exp_i| { | |
| 1009 | const s = self.syms.items[exp_i]; | |
| 954 | 1010 | if (mem.eql(u8, s.name, "_start")) |
| 955 | 1011 | self.entry_val = s.value; |
| 956 | 1012 | try self.writeSym(writer, s); |
| 957 | } | |
| 1013 | }; | |
| 958 | 1014 | } |
| 959 | 1015 | } |
| 960 | 1016 | } |
| 961 | 1017 | } |
| 962 | 1018 | } |
| 963 | 1019 | |
| 964 | /// this will be removed, moved to updateFinish | |
| 965 | pub fn allocateDeclIndexes(self: *Plan9, decl_index: Module.Decl.Index) !void { | |
| 966 | _ = self; | |
| 967 | _ = decl_index; | |
| 968 | } | |
| 969 | 1020 | /// Must be called only after a successful call to `updateDecl`. |
| 970 | pub fn updateDeclLineNumber(self: *Plan9, mod: *Module, decl: *const Module.Decl) !void { | |
| 1021 | pub fn updateDeclLineNumber(self: *Plan9, mod: *Module, decl_index: Module.Decl.Index) !void { | |
| 971 | 1022 | _ = self; |
| 972 | 1023 | _ = mod; |
| 973 | _ = decl; | |
| 1024 | _ = decl_index; | |
| 974 | 1025 | } |
| 975 | 1026 | |
| 976 | 1027 | pub fn getDeclVAddr( |
| ... | ... | @@ -1011,3 +1062,11 @@ pub fn getDeclVAddr( |
| 1011 | 1062 | }); |
| 1012 | 1063 | return undefined; |
| 1013 | 1064 | } |
| 1065 | ||
| 1066 | pub fn getDeclBlock(self: *const Plan9, index: DeclBlock.Index) DeclBlock { | |
| 1067 | return self.decl_blocks.items[index]; | |
| 1068 | } | |
| 1069 | ||
| 1070 | fn getDeclBlockPtr(self: *Plan9, index: DeclBlock.Index) *DeclBlock { | |
| 1071 | return &self.decl_blocks.items[index]; | |
| 1072 | } |
src/link/SpirV.zig+7-11| ... | ... | @@ -42,13 +42,6 @@ const SpvModule = @import("../codegen/spirv/Module.zig"); |
| 42 | 42 | const spec = @import("../codegen/spirv/spec.zig"); |
| 43 | 43 | const IdResult = spec.IdResult; |
| 44 | 44 | |
| 45 | // TODO: Should this struct be used at all rather than just a hashmap of aux data for every decl? | |
| 46 | pub const FnData = struct { | |
| 47 | // We're going to fill these in flushModule, and we're going to fill them unconditionally, | |
| 48 | // so just set it to undefined. | |
| 49 | id: IdResult = undefined, | |
| 50 | }; | |
| 51 | ||
| 52 | 45 | base: link.File, |
| 53 | 46 | |
| 54 | 47 | /// This linker backend does not try to incrementally link output SPIR-V code. |
| ... | ... | @@ -209,16 +202,19 @@ pub fn flushModule(self: *SpirV, comp: *Compilation, prog_node: *std.Progress.No |
| 209 | 202 | // so that we can access them before processing them. |
| 210 | 203 | // TODO: We're allocating an ID unconditionally now, are there |
| 211 | 204 | // declarations which don't generate a result? |
| 212 | // TODO: fn_link is used here, but thats probably not the right field. It will work anyway though. | |
| 205 | var ids = std.AutoHashMap(Module.Decl.Index, IdResult).init(self.base.allocator); | |
| 206 | defer ids.deinit(); | |
| 207 | try ids.ensureTotalCapacity(@intCast(u32, self.decl_table.count())); | |
| 208 | ||
| 213 | 209 | for (self.decl_table.keys()) |decl_index| { |
| 214 | 210 | const decl = module.declPtr(decl_index); |
| 215 | 211 | if (decl.has_tv) { |
| 216 | decl.fn_link.spirv.id = spv.allocId(); | |
| 212 | ids.putAssumeCapacityNoClobber(decl_index, spv.allocId()); | |
| 217 | 213 | } |
| 218 | 214 | } |
| 219 | 215 | |
| 220 | 216 | // Now, actually generate the code for all declarations. |
| 221 | var decl_gen = codegen.DeclGen.init(self.base.allocator, module, &spv); | |
| 217 | var decl_gen = codegen.DeclGen.init(self.base.allocator, module, &spv, &ids); | |
| 222 | 218 | defer decl_gen.deinit(); |
| 223 | 219 | |
| 224 | 220 | var it = self.decl_table.iterator(); |
| ... | ... | @@ -231,7 +227,7 @@ pub fn flushModule(self: *SpirV, comp: *Compilation, prog_node: *std.Progress.No |
| 231 | 227 | const liveness = entry.value_ptr.liveness; |
| 232 | 228 | |
| 233 | 229 | // Note, if `decl` is not a function, air/liveness may be undefined. |
| 234 | if (try decl_gen.gen(decl, air, liveness)) |msg| { | |
| 230 | if (try decl_gen.gen(decl_index, air, liveness)) |msg| { | |
| 235 | 231 | try module.failed_decls.put(module.gpa, decl_index, msg); |
| 236 | 232 | return; // TODO: Attempt to generate more decls? |
| 237 | 233 | } |
src/link/Wasm.zig+292-281| ... | ... | @@ -9,7 +9,7 @@ const fs = std.fs; |
| 9 | 9 | const leb = std.leb; |
| 10 | 10 | const log = std.log.scoped(.link); |
| 11 | 11 | |
| 12 | const Atom = @import("Wasm/Atom.zig"); | |
| 12 | pub const Atom = @import("Wasm/Atom.zig"); | |
| 13 | 13 | const Dwarf = @import("Dwarf.zig"); |
| 14 | 14 | const Module = @import("../Module.zig"); |
| 15 | 15 | const Compilation = @import("../Compilation.zig"); |
| ... | ... | @@ -31,10 +31,7 @@ const Object = @import("Wasm/Object.zig"); |
| 31 | 31 | const Archive = @import("Wasm/Archive.zig"); |
| 32 | 32 | const types = @import("Wasm/types.zig"); |
| 33 | 33 | |
| 34 | pub const base_tag = link.File.Tag.wasm; | |
| 35 | ||
| 36 | /// deprecated: Use `@import("Wasm/Atom.zig");` | |
| 37 | pub const DeclBlock = Atom; | |
| 34 | pub const base_tag: link.File.Tag = .wasm; | |
| 38 | 35 | |
| 39 | 36 | base: link.File, |
| 40 | 37 | /// Output name of the file |
| ... | ... | @@ -47,18 +44,16 @@ llvm_object: ?*LlvmObject = null, |
| 47 | 44 | /// TODO: Allow setting this through a flag? |
| 48 | 45 | host_name: []const u8 = "env", |
| 49 | 46 | /// List of all `Decl` that are currently alive. |
| 50 | /// This is ment for bookkeeping so we can safely cleanup all codegen memory | |
| 51 | /// when calling `deinit` | |
| 52 | decls: std.AutoHashMapUnmanaged(Module.Decl.Index, void) = .{}, | |
| 47 | /// Each index maps to the corresponding `Atom.Index`. | |
| 48 | decls: std.AutoHashMapUnmanaged(Module.Decl.Index, Atom.Index) = .{}, | |
| 53 | 49 | /// List of all symbols generated by Zig code. |
| 54 | 50 | symbols: std.ArrayListUnmanaged(Symbol) = .{}, |
| 55 | 51 | /// List of symbol indexes which are free to be used. |
| 56 | 52 | symbols_free_list: std.ArrayListUnmanaged(u32) = .{}, |
| 57 | 53 | /// Maps atoms to their segment index |
| 58 | atoms: std.AutoHashMapUnmanaged(u32, *Atom) = .{}, | |
| 59 | /// Atoms managed and created by the linker. This contains atoms | |
| 60 | /// from object files, and not Atoms generated by a Decl. | |
| 61 | managed_atoms: std.ArrayListUnmanaged(*Atom) = .{}, | |
| 54 | atoms: std.AutoHashMapUnmanaged(u32, Atom.Index) = .{}, | |
| 55 | /// List of all atoms. | |
| 56 | managed_atoms: std.ArrayListUnmanaged(Atom) = .{}, | |
| 62 | 57 | /// Represents the index into `segments` where the 'code' section |
| 63 | 58 | /// lives. |
| 64 | 59 | code_section_index: ?u32 = null, |
| ... | ... | @@ -148,7 +143,7 @@ undefs: std.StringArrayHashMapUnmanaged(SymbolLoc) = .{}, |
| 148 | 143 | /// Maps a symbol's location to an atom. This can be used to find meta |
| 149 | 144 | /// data of a symbol, such as its size, or its offset to perform a relocation. |
| 150 | 145 | /// Undefined (and synthetic) symbols do not have an Atom and therefore cannot be mapped. |
| 151 | symbol_atom: std.AutoHashMapUnmanaged(SymbolLoc, *Atom) = .{}, | |
| 146 | symbol_atom: std.AutoHashMapUnmanaged(SymbolLoc, Atom.Index) = .{}, | |
| 152 | 147 | /// Maps a symbol's location to its export name, which may differ from the decl's name |
| 153 | 148 | /// which does the exporting. |
| 154 | 149 | /// Note: The value represents the offset into the string table, rather than the actual string. |
| ... | ... | @@ -165,14 +160,14 @@ error_table_symbol: ?u32 = null, |
| 165 | 160 | // unit contains Zig code. The lifetime of these atoms are extended |
| 166 | 161 | // until the end of the compiler's lifetime. Meaning they're not freed |
| 167 | 162 | // during `flush()` in incremental-mode. |
| 168 | debug_info_atom: ?*Atom = null, | |
| 169 | debug_line_atom: ?*Atom = null, | |
| 170 | debug_loc_atom: ?*Atom = null, | |
| 171 | debug_ranges_atom: ?*Atom = null, | |
| 172 | debug_abbrev_atom: ?*Atom = null, | |
| 173 | debug_str_atom: ?*Atom = null, | |
| 174 | debug_pubnames_atom: ?*Atom = null, | |
| 175 | debug_pubtypes_atom: ?*Atom = null, | |
| 163 | debug_info_atom: ?Atom.Index = null, | |
| 164 | debug_line_atom: ?Atom.Index = null, | |
| 165 | debug_loc_atom: ?Atom.Index = null, | |
| 166 | debug_ranges_atom: ?Atom.Index = null, | |
| 167 | debug_abbrev_atom: ?Atom.Index = null, | |
| 168 | debug_str_atom: ?Atom.Index = null, | |
| 169 | debug_pubnames_atom: ?Atom.Index = null, | |
| 170 | debug_pubtypes_atom: ?Atom.Index = null, | |
| 176 | 171 | |
| 177 | 172 | pub const Segment = struct { |
| 178 | 173 | alignment: u32, |
| ... | ... | @@ -183,13 +178,9 @@ pub const Segment = struct { |
| 183 | 178 | pub const FnData = struct { |
| 184 | 179 | /// Reference to the wasm type that represents this function. |
| 185 | 180 | type_index: u32, |
| 186 | /// Contains debug information related to this function. | |
| 187 | /// For Wasm, the offset is relative to the code-section. | |
| 188 | src_fn: Dwarf.SrcFn, | |
| 189 | 181 | |
| 190 | 182 | pub const empty: FnData = .{ |
| 191 | 183 | .type_index = undefined, |
| 192 | .src_fn = Dwarf.SrcFn.empty, | |
| 193 | 184 | }; |
| 194 | 185 | }; |
| 195 | 186 | |
| ... | ... | @@ -434,10 +425,10 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option |
| 434 | 425 | // at the end during `initializeCallCtorsFunction`. |
| 435 | 426 | } |
| 436 | 427 | |
| 437 | if (!options.strip and options.module != null) { | |
| 438 | wasm_bin.dwarf = Dwarf.init(allocator, &wasm_bin.base, options.target); | |
| 439 | try wasm_bin.initDebugSections(); | |
| 440 | } | |
| 428 | // if (!options.strip and options.module != null) { | |
| 429 | // wasm_bin.dwarf = Dwarf.init(allocator, &wasm_bin.base, options.target); | |
| 430 | // try wasm_bin.initDebugSections(); | |
| 431 | // } | |
| 441 | 432 | |
| 442 | 433 | return wasm_bin; |
| 443 | 434 | } |
| ... | ... | @@ -478,6 +469,7 @@ fn createSyntheticSymbol(wasm: *Wasm, name: []const u8, tag: Symbol.Tag) !Symbol |
| 478 | 469 | try wasm.globals.put(wasm.base.allocator, name_offset, loc); |
| 479 | 470 | return loc; |
| 480 | 471 | } |
| 472 | ||
| 481 | 473 | /// Initializes symbols and atoms for the debug sections |
| 482 | 474 | /// Initialization is only done when compiling Zig code. |
| 483 | 475 | /// When Zig is invoked as a linker instead, the atoms |
| ... | ... | @@ -520,6 +512,36 @@ fn parseObjectFile(wasm: *Wasm, path: []const u8) !bool { |
| 520 | 512 | return true; |
| 521 | 513 | } |
| 522 | 514 | |
| 515 | /// For a given `Module.Decl.Index` returns its corresponding `Atom.Index`. | |
| 516 | /// When the index was not found, a new `Atom` will be created, and its index will be returned. | |
| 517 | /// The newly created Atom is empty with default fields as specified by `Atom.empty`. | |
| 518 | pub fn getOrCreateAtomForDecl(wasm: *Wasm, decl_index: Module.Decl.Index) !Atom.Index { | |
| 519 | const gop = try wasm.decls.getOrPut(wasm.base.allocator, decl_index); | |
| 520 | if (!gop.found_existing) { | |
| 521 | gop.value_ptr.* = try wasm.createAtom(); | |
| 522 | } | |
| 523 | return gop.value_ptr.*; | |
| 524 | } | |
| 525 | ||
| 526 | /// Creates a new empty `Atom` and returns its `Atom.Index` | |
| 527 | fn createAtom(wasm: *Wasm) !Atom.Index { | |
| 528 | const index = @intCast(Atom.Index, wasm.managed_atoms.items.len); | |
| 529 | const atom = try wasm.managed_atoms.addOne(wasm.base.allocator); | |
| 530 | atom.* = Atom.empty; | |
| 531 | atom.sym_index = try wasm.allocateSymbol(); | |
| 532 | try wasm.symbol_atom.putNoClobber(wasm.base.allocator, .{ .file = null, .index = atom.sym_index }, index); | |
| 533 | ||
| 534 | return index; | |
| 535 | } | |
| 536 | ||
| 537 | pub inline fn getAtom(wasm: *const Wasm, index: Atom.Index) Atom { | |
| 538 | return wasm.managed_atoms.items[index]; | |
| 539 | } | |
| 540 | ||
| 541 | pub inline fn getAtomPtr(wasm: *Wasm, index: Atom.Index) *Atom { | |
| 542 | return &wasm.managed_atoms.items[index]; | |
| 543 | } | |
| 544 | ||
| 523 | 545 | /// Parses an archive file and will then parse each object file |
| 524 | 546 | /// that was found in the archive file. |
| 525 | 547 | /// Returns false when the file is not an archive file. |
| ... | ... | @@ -861,15 +883,16 @@ fn resolveLazySymbols(wasm: *Wasm) !void { |
| 861 | 883 | try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc); |
| 862 | 884 | _ = wasm.resolved_symbols.swapRemove(loc); // we don't want to emit this symbol, only use it for relocations. |
| 863 | 885 | |
| 864 | const atom = try wasm.base.allocator.create(Atom); | |
| 865 | errdefer wasm.base.allocator.destroy(atom); | |
| 866 | try wasm.managed_atoms.append(wasm.base.allocator, atom); | |
| 886 | // TODO: Can we use `createAtom` here while also re-using the symbol | |
| 887 | // from `createSyntheticSymbol`. | |
| 888 | const atom_index = @intCast(Atom.Index, wasm.managed_atoms.items.len); | |
| 889 | const atom = try wasm.managed_atoms.addOne(wasm.base.allocator); | |
| 867 | 890 | atom.* = Atom.empty; |
| 868 | 891 | atom.sym_index = loc.index; |
| 869 | 892 | atom.alignment = 1; |
| 870 | 893 | |
| 871 | try wasm.parseAtom(atom, .{ .data = .synthetic }); | |
| 872 | try wasm.symbol_atom.putNoClobber(wasm.base.allocator, loc, atom); | |
| 894 | try wasm.parseAtom(atom_index, .{ .data = .synthetic }); | |
| 895 | try wasm.symbol_atom.putNoClobber(wasm.base.allocator, loc, atom_index); | |
| 873 | 896 | } |
| 874 | 897 | |
| 875 | 898 | if (wasm.undefs.fetchSwapRemove("__heap_end")) |kv| { |
| ... | ... | @@ -877,15 +900,14 @@ fn resolveLazySymbols(wasm: *Wasm) !void { |
| 877 | 900 | try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc); |
| 878 | 901 | _ = wasm.resolved_symbols.swapRemove(loc); |
| 879 | 902 | |
| 880 | const atom = try wasm.base.allocator.create(Atom); | |
| 881 | errdefer wasm.base.allocator.destroy(atom); | |
| 882 | try wasm.managed_atoms.append(wasm.base.allocator, atom); | |
| 903 | const atom_index = @intCast(Atom.Index, wasm.managed_atoms.items.len); | |
| 904 | const atom = try wasm.managed_atoms.addOne(wasm.base.allocator); | |
| 883 | 905 | atom.* = Atom.empty; |
| 884 | 906 | atom.sym_index = loc.index; |
| 885 | 907 | atom.alignment = 1; |
| 886 | 908 | |
| 887 | try wasm.parseAtom(atom, .{ .data = .synthetic }); | |
| 888 | try wasm.symbol_atom.putNoClobber(wasm.base.allocator, loc, atom); | |
| 909 | try wasm.parseAtom(atom_index, .{ .data = .synthetic }); | |
| 910 | try wasm.symbol_atom.putNoClobber(wasm.base.allocator, loc, atom_index); | |
| 889 | 911 | } |
| 890 | 912 | } |
| 891 | 913 | |
| ... | ... | @@ -924,16 +946,6 @@ pub fn deinit(wasm: *Wasm) void { |
| 924 | 946 | if (wasm.llvm_object) |llvm_object| llvm_object.destroy(gpa); |
| 925 | 947 | } |
| 926 | 948 | |
| 927 | if (wasm.base.options.module) |mod| { | |
| 928 | var decl_it = wasm.decls.keyIterator(); | |
| 929 | while (decl_it.next()) |decl_index_ptr| { | |
| 930 | const decl = mod.declPtr(decl_index_ptr.*); | |
| 931 | decl.link.wasm.deinit(gpa); | |
| 932 | } | |
| 933 | } else { | |
| 934 | assert(wasm.decls.count() == 0); | |
| 935 | } | |
| 936 | ||
| 937 | 949 | for (wasm.func_types.items) |*func_type| { |
| 938 | 950 | func_type.deinit(gpa); |
| 939 | 951 | } |
| ... | ... | @@ -958,9 +970,8 @@ pub fn deinit(wasm: *Wasm) void { |
| 958 | 970 | wasm.symbol_atom.deinit(gpa); |
| 959 | 971 | wasm.export_names.deinit(gpa); |
| 960 | 972 | wasm.atoms.deinit(gpa); |
| 961 | for (wasm.managed_atoms.items) |managed_atom| { | |
| 962 | managed_atom.deinit(gpa); | |
| 963 | gpa.destroy(managed_atom); | |
| 973 | for (wasm.managed_atoms.items) |*managed_atom| { | |
| 974 | managed_atom.deinit(wasm); | |
| 964 | 975 | } |
| 965 | 976 | wasm.managed_atoms.deinit(gpa); |
| 966 | 977 | wasm.segments.deinit(gpa); |
| ... | ... | @@ -986,31 +997,23 @@ pub fn deinit(wasm: *Wasm) void { |
| 986 | 997 | } |
| 987 | 998 | } |
| 988 | 999 | |
| 989 | pub fn allocateDeclIndexes(wasm: *Wasm, decl_index: Module.Decl.Index) !void { | |
| 990 | if (wasm.llvm_object) |_| return; | |
| 991 | const decl = wasm.base.options.module.?.declPtr(decl_index); | |
| 992 | if (decl.link.wasm.sym_index != 0) return; | |
| 993 | ||
| 1000 | /// Allocates a new symbol and returns its index. | |
| 1001 | /// Will re-use slots when a symbol was freed at an earlier stage. | |
| 1002 | pub fn allocateSymbol(wasm: *Wasm) !u32 { | |
| 994 | 1003 | try wasm.symbols.ensureUnusedCapacity(wasm.base.allocator, 1); |
| 995 | try wasm.decls.putNoClobber(wasm.base.allocator, decl_index, {}); | |
| 996 | ||
| 997 | const atom = &decl.link.wasm; | |
| 998 | ||
| 999 | 1004 | var symbol: Symbol = .{ |
| 1000 | 1005 | .name = undefined, // will be set after updateDecl |
| 1001 | 1006 | .flags = @enumToInt(Symbol.Flag.WASM_SYM_BINDING_LOCAL), |
| 1002 | 1007 | .tag = undefined, // will be set after updateDecl |
| 1003 | 1008 | .index = undefined, // will be set after updateDecl |
| 1004 | 1009 | }; |
| 1005 | ||
| 1006 | 1010 | if (wasm.symbols_free_list.popOrNull()) |index| { |
| 1007 | atom.sym_index = index; | |
| 1008 | 1011 | wasm.symbols.items[index] = symbol; |
| 1009 | } else { | |
| 1010 | atom.sym_index = @intCast(u32, wasm.symbols.items.len); | |
| 1011 | wasm.symbols.appendAssumeCapacity(symbol); | |
| 1012 | return index; | |
| 1012 | 1013 | } |
| 1013 | try wasm.symbol_atom.putNoClobber(wasm.base.allocator, atom.symbolLoc(), atom); | |
| 1014 | const index = @intCast(u32, wasm.symbols.items.len); | |
| 1015 | wasm.symbols.appendAssumeCapacity(symbol); | |
| 1016 | return index; | |
| 1014 | 1017 | } |
| 1015 | 1018 | |
| 1016 | 1019 | pub fn updateFunc(wasm: *Wasm, mod: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void { |
| ... | ... | @@ -1026,15 +1029,24 @@ pub fn updateFunc(wasm: *Wasm, mod: *Module, func: *Module.Fn, air: Air, livenes |
| 1026 | 1029 | |
| 1027 | 1030 | const decl_index = func.owner_decl; |
| 1028 | 1031 | const decl = mod.declPtr(decl_index); |
| 1029 | assert(decl.link.wasm.sym_index != 0); // Must call allocateDeclIndexes() | |
| 1032 | const atom_index = try wasm.getOrCreateAtomForDecl(decl_index); | |
| 1033 | const atom = wasm.getAtomPtr(atom_index); | |
| 1034 | atom.clear(); | |
| 1030 | 1035 | |
| 1031 | decl.link.wasm.clear(); | |
| 1032 | ||
| 1033 | var decl_state: ?Dwarf.DeclState = if (wasm.dwarf) |*dwarf| try dwarf.initDeclState(mod, decl_index) else null; | |
| 1034 | defer if (decl_state) |*ds| ds.deinit(); | |
| 1036 | // var decl_state: ?Dwarf.DeclState = if (wasm.dwarf) |*dwarf| try dwarf.initDeclState(mod, decl_index) else null; | |
| 1037 | // defer if (decl_state) |*ds| ds.deinit(); | |
| 1035 | 1038 | |
| 1036 | 1039 | var code_writer = std.ArrayList(u8).init(wasm.base.allocator); |
| 1037 | 1040 | defer code_writer.deinit(); |
| 1041 | // const result = try codegen.generateFunction( | |
| 1042 | // &wasm.base, | |
| 1043 | // decl.srcLoc(), | |
| 1044 | // func, | |
| 1045 | // air, | |
| 1046 | // liveness, | |
| 1047 | // &code_writer, | |
| 1048 | // if (decl_state) |*ds| .{ .dwarf = ds } else .none, | |
| 1049 | // ); | |
| 1038 | 1050 | const result = try codegen.generateFunction( |
| 1039 | 1051 | &wasm.base, |
| 1040 | 1052 | decl.srcLoc(), |
| ... | ... | @@ -1042,11 +1054,11 @@ pub fn updateFunc(wasm: *Wasm, mod: *Module, func: *Module.Fn, air: Air, livenes |
| 1042 | 1054 | air, |
| 1043 | 1055 | liveness, |
| 1044 | 1056 | &code_writer, |
| 1045 | if (decl_state) |*ds| .{ .dwarf = ds } else .none, | |
| 1057 | .none, | |
| 1046 | 1058 | ); |
| 1047 | 1059 | |
| 1048 | 1060 | const code = switch (result) { |
| 1049 | .appended => code_writer.items, | |
| 1061 | .ok => code_writer.items, | |
| 1050 | 1062 | .fail => |em| { |
| 1051 | 1063 | decl.analysis = .codegen_failure; |
| 1052 | 1064 | try mod.failed_decls.put(mod.gpa, decl_index, em); |
| ... | ... | @@ -1054,19 +1066,19 @@ pub fn updateFunc(wasm: *Wasm, mod: *Module, func: *Module.Fn, air: Air, livenes |
| 1054 | 1066 | }, |
| 1055 | 1067 | }; |
| 1056 | 1068 | |
| 1057 | if (wasm.dwarf) |*dwarf| { | |
| 1058 | try dwarf.commitDeclState( | |
| 1059 | mod, | |
| 1060 | decl_index, | |
| 1061 | // Actual value will be written after relocation. | |
| 1062 | // For Wasm, this is the offset relative to the code section | |
| 1063 | // which isn't known until flush(). | |
| 1064 | 0, | |
| 1065 | code.len, | |
| 1066 | &decl_state.?, | |
| 1067 | ); | |
| 1068 | } | |
| 1069 | return wasm.finishUpdateDecl(decl, code); | |
| 1069 | // if (wasm.dwarf) |*dwarf| { | |
| 1070 | // try dwarf.commitDeclState( | |
| 1071 | // mod, | |
| 1072 | // decl_index, | |
| 1073 | // // Actual value will be written after relocation. | |
| 1074 | // // For Wasm, this is the offset relative to the code section | |
| 1075 | // // which isn't known until flush(). | |
| 1076 | // 0, | |
| 1077 | // code.len, | |
| 1078 | // &decl_state.?, | |
| 1079 | // ); | |
| 1080 | // } | |
| 1081 | return wasm.finishUpdateDecl(decl_index, code); | |
| 1070 | 1082 | } |
| 1071 | 1083 | |
| 1072 | 1084 | // Generate code for the Decl, storing it in memory to be later written to |
| ... | ... | @@ -1083,20 +1095,20 @@ pub fn updateDecl(wasm: *Wasm, mod: *Module, decl_index: Module.Decl.Index) !voi |
| 1083 | 1095 | defer tracy.end(); |
| 1084 | 1096 | |
| 1085 | 1097 | const decl = mod.declPtr(decl_index); |
| 1086 | assert(decl.link.wasm.sym_index != 0); // Must call allocateDeclIndexes() | |
| 1087 | ||
| 1088 | decl.link.wasm.clear(); | |
| 1089 | ||
| 1090 | 1098 | if (decl.val.castTag(.function)) |_| { |
| 1091 | 1099 | return; |
| 1092 | 1100 | } else if (decl.val.castTag(.extern_fn)) |_| { |
| 1093 | 1101 | return; |
| 1094 | 1102 | } |
| 1095 | 1103 | |
| 1104 | const atom_index = try wasm.getOrCreateAtomForDecl(decl_index); | |
| 1105 | const atom = wasm.getAtomPtr(atom_index); | |
| 1106 | atom.clear(); | |
| 1107 | ||
| 1096 | 1108 | if (decl.isExtern()) { |
| 1097 | 1109 | const variable = decl.getVariable().?; |
| 1098 | 1110 | const name = mem.sliceTo(decl.name, 0); |
| 1099 | return wasm.addOrUpdateImport(name, decl.link.wasm.sym_index, variable.lib_name, null); | |
| 1111 | return wasm.addOrUpdateImport(name, atom.sym_index, variable.lib_name, null); | |
| 1100 | 1112 | } |
| 1101 | 1113 | const val = if (decl.val.castTag(.variable)) |payload| payload.data.init else decl.val; |
| 1102 | 1114 | |
| ... | ... | @@ -1109,12 +1121,11 @@ pub fn updateDecl(wasm: *Wasm, mod: *Module, decl_index: Module.Decl.Index) !voi |
| 1109 | 1121 | .{ .ty = decl.ty, .val = val }, |
| 1110 | 1122 | &code_writer, |
| 1111 | 1123 | .none, |
| 1112 | .{ .parent_atom_index = decl.link.wasm.sym_index }, | |
| 1124 | .{ .parent_atom_index = atom.sym_index }, | |
| 1113 | 1125 | ); |
| 1114 | 1126 | |
| 1115 | 1127 | const code = switch (res) { |
| 1116 | .externally_managed => |x| x, | |
| 1117 | .appended => code_writer.items, | |
| 1128 | .ok => code_writer.items, | |
| 1118 | 1129 | .fail => |em| { |
| 1119 | 1130 | decl.analysis = .codegen_failure; |
| 1120 | 1131 | try mod.failed_decls.put(mod.gpa, decl_index, em); |
| ... | ... | @@ -1122,26 +1133,29 @@ pub fn updateDecl(wasm: *Wasm, mod: *Module, decl_index: Module.Decl.Index) !voi |
| 1122 | 1133 | }, |
| 1123 | 1134 | }; |
| 1124 | 1135 | |
| 1125 | return wasm.finishUpdateDecl(decl, code); | |
| 1136 | return wasm.finishUpdateDecl(decl_index, code); | |
| 1126 | 1137 | } |
| 1127 | 1138 | |
| 1128 | pub fn updateDeclLineNumber(wasm: *Wasm, mod: *Module, decl: *const Module.Decl) !void { | |
| 1139 | pub fn updateDeclLineNumber(wasm: *Wasm, mod: *Module, decl_index: Module.Decl.Index) !void { | |
| 1129 | 1140 | if (wasm.llvm_object) |_| return; |
| 1130 | 1141 | if (wasm.dwarf) |*dw| { |
| 1131 | 1142 | const tracy = trace(@src()); |
| 1132 | 1143 | defer tracy.end(); |
| 1133 | 1144 | |
| 1145 | const decl = mod.declPtr(decl_index); | |
| 1134 | 1146 | const decl_name = try decl.getFullyQualifiedName(mod); |
| 1135 | 1147 | defer wasm.base.allocator.free(decl_name); |
| 1136 | 1148 | |
| 1137 | 1149 | log.debug("updateDeclLineNumber {s}{*}", .{ decl_name, decl }); |
| 1138 | try dw.updateDeclLineNumber(decl); | |
| 1150 | try dw.updateDeclLineNumber(mod, decl_index); | |
| 1139 | 1151 | } |
| 1140 | 1152 | } |
| 1141 | 1153 | |
| 1142 | fn finishUpdateDecl(wasm: *Wasm, decl: *Module.Decl, code: []const u8) !void { | |
| 1154 | fn finishUpdateDecl(wasm: *Wasm, decl_index: Module.Decl.Index, code: []const u8) !void { | |
| 1143 | 1155 | const mod = wasm.base.options.module.?; |
| 1144 | const atom: *Atom = &decl.link.wasm; | |
| 1156 | const decl = mod.declPtr(decl_index); | |
| 1157 | const atom_index = wasm.decls.get(decl_index).?; | |
| 1158 | const atom = wasm.getAtomPtr(atom_index); | |
| 1145 | 1159 | const symbol = &wasm.symbols.items[atom.sym_index]; |
| 1146 | 1160 | const full_name = try decl.getFullyQualifiedName(mod); |
| 1147 | 1161 | defer wasm.base.allocator.free(full_name); |
| ... | ... | @@ -1149,8 +1163,8 @@ fn finishUpdateDecl(wasm: *Wasm, decl: *Module.Decl, code: []const u8) !void { |
| 1149 | 1163 | try atom.code.appendSlice(wasm.base.allocator, code); |
| 1150 | 1164 | try wasm.resolved_symbols.put(wasm.base.allocator, atom.symbolLoc(), {}); |
| 1151 | 1165 | |
| 1152 | if (code.len == 0) return; | |
| 1153 | 1166 | atom.size = @intCast(u32, code.len); |
| 1167 | if (code.len == 0) return; | |
| 1154 | 1168 | atom.alignment = decl.ty.abiAlignment(wasm.base.options.target); |
| 1155 | 1169 | } |
| 1156 | 1170 | |
| ... | ... | @@ -1207,58 +1221,51 @@ pub fn lowerUnnamedConst(wasm: *Wasm, tv: TypedValue, decl_index: Module.Decl.In |
| 1207 | 1221 | const decl = mod.declPtr(decl_index); |
| 1208 | 1222 | |
| 1209 | 1223 | // Create and initialize a new local symbol and atom |
| 1210 | const local_index = decl.link.wasm.locals.items.len; | |
| 1224 | const atom_index = try wasm.createAtom(); | |
| 1225 | const parent_atom_index = try wasm.getOrCreateAtomForDecl(decl_index); | |
| 1226 | const parent_atom = wasm.getAtomPtr(parent_atom_index); | |
| 1227 | const local_index = parent_atom.locals.items.len; | |
| 1228 | try parent_atom.locals.append(wasm.base.allocator, atom_index); | |
| 1211 | 1229 | const fqdn = try decl.getFullyQualifiedName(mod); |
| 1212 | 1230 | defer wasm.base.allocator.free(fqdn); |
| 1213 | 1231 | const name = try std.fmt.allocPrintZ(wasm.base.allocator, "__unnamed_{s}_{d}", .{ fqdn, local_index }); |
| 1214 | 1232 | defer wasm.base.allocator.free(name); |
| 1215 | var symbol: Symbol = .{ | |
| 1216 | .name = try wasm.string_table.put(wasm.base.allocator, name), | |
| 1217 | .flags = 0, | |
| 1218 | .tag = .data, | |
| 1219 | .index = undefined, | |
| 1220 | }; | |
| 1221 | symbol.setFlag(.WASM_SYM_BINDING_LOCAL); | |
| 1222 | ||
| 1223 | const atom = try decl.link.wasm.locals.addOne(wasm.base.allocator); | |
| 1224 | atom.* = Atom.empty; | |
| 1225 | atom.alignment = tv.ty.abiAlignment(wasm.base.options.target); | |
| 1226 | try wasm.symbols.ensureUnusedCapacity(wasm.base.allocator, 1); | |
| 1227 | ||
| 1228 | if (wasm.symbols_free_list.popOrNull()) |index| { | |
| 1229 | atom.sym_index = index; | |
| 1230 | wasm.symbols.items[index] = symbol; | |
| 1231 | } else { | |
| 1232 | atom.sym_index = @intCast(u32, wasm.symbols.items.len); | |
| 1233 | wasm.symbols.appendAssumeCapacity(symbol); | |
| 1234 | } | |
| 1235 | try wasm.resolved_symbols.putNoClobber(wasm.base.allocator, atom.symbolLoc(), {}); | |
| 1236 | try wasm.symbol_atom.putNoClobber(wasm.base.allocator, atom.symbolLoc(), atom); | |
| 1237 | ||
| 1238 | 1233 | var value_bytes = std.ArrayList(u8).init(wasm.base.allocator); |
| 1239 | 1234 | defer value_bytes.deinit(); |
| 1240 | 1235 | |
| 1241 | const result = try codegen.generateSymbol( | |
| 1242 | &wasm.base, | |
| 1243 | decl.srcLoc(), | |
| 1244 | tv, | |
| 1245 | &value_bytes, | |
| 1246 | .none, | |
| 1247 | .{ | |
| 1248 | .parent_atom_index = atom.sym_index, | |
| 1249 | .addend = null, | |
| 1250 | }, | |
| 1251 | ); | |
| 1252 | const code = switch (result) { | |
| 1253 | .externally_managed => |x| x, | |
| 1254 | .appended => value_bytes.items, | |
| 1255 | .fail => |em| { | |
| 1256 | decl.analysis = .codegen_failure; | |
| 1257 | try mod.failed_decls.put(mod.gpa, decl_index, em); | |
| 1258 | return error.AnalysisFail; | |
| 1259 | }, | |
| 1236 | const code = code: { | |
| 1237 | const atom = wasm.getAtomPtr(atom_index); | |
| 1238 | atom.alignment = tv.ty.abiAlignment(wasm.base.options.target); | |
| 1239 | wasm.symbols.items[atom.sym_index] = .{ | |
| 1240 | .name = try wasm.string_table.put(wasm.base.allocator, name), | |
| 1241 | .flags = @enumToInt(Symbol.Flag.WASM_SYM_BINDING_LOCAL), | |
| 1242 | .tag = .data, | |
| 1243 | .index = undefined, | |
| 1244 | }; | |
| 1245 | try wasm.resolved_symbols.putNoClobber(wasm.base.allocator, atom.symbolLoc(), {}); | |
| 1246 | ||
| 1247 | const result = try codegen.generateSymbol( | |
| 1248 | &wasm.base, | |
| 1249 | decl.srcLoc(), | |
| 1250 | tv, | |
| 1251 | &value_bytes, | |
| 1252 | .none, | |
| 1253 | .{ | |
| 1254 | .parent_atom_index = atom.sym_index, | |
| 1255 | .addend = null, | |
| 1256 | }, | |
| 1257 | ); | |
| 1258 | break :code switch (result) { | |
| 1259 | .ok => value_bytes.items, | |
| 1260 | .fail => |em| { | |
| 1261 | decl.analysis = .codegen_failure; | |
| 1262 | try mod.failed_decls.put(mod.gpa, decl_index, em); | |
| 1263 | return error.AnalysisFail; | |
| 1264 | }, | |
| 1265 | }; | |
| 1260 | 1266 | }; |
| 1261 | 1267 | |
| 1268 | const atom = wasm.getAtomPtr(atom_index); | |
| 1262 | 1269 | atom.size = @intCast(u32, code.len); |
| 1263 | 1270 | try atom.code.appendSlice(wasm.base.allocator, code); |
| 1264 | 1271 | return atom.sym_index; |
| ... | ... | @@ -1306,10 +1313,13 @@ pub fn getDeclVAddr( |
| 1306 | 1313 | ) !u64 { |
| 1307 | 1314 | const mod = wasm.base.options.module.?; |
| 1308 | 1315 | const decl = mod.declPtr(decl_index); |
| 1309 | const target_symbol_index = decl.link.wasm.sym_index; | |
| 1310 | assert(target_symbol_index != 0); | |
| 1316 | ||
| 1317 | const target_atom_index = try wasm.getOrCreateAtomForDecl(decl_index); | |
| 1318 | const target_symbol_index = wasm.getAtom(target_atom_index).sym_index; | |
| 1319 | ||
| 1311 | 1320 | assert(reloc_info.parent_atom_index != 0); |
| 1312 | const atom = wasm.symbol_atom.get(.{ .file = null, .index = reloc_info.parent_atom_index }).?; | |
| 1321 | const atom_index = wasm.symbol_atom.get(.{ .file = null, .index = reloc_info.parent_atom_index }).?; | |
| 1322 | const atom = wasm.getAtomPtr(atom_index); | |
| 1313 | 1323 | const is_wasm32 = wasm.base.options.target.cpu.arch == .wasm32; |
| 1314 | 1324 | if (decl.ty.zigTypeTag() == .Fn) { |
| 1315 | 1325 | assert(reloc_info.addend == 0); // addend not allowed for function relocations |
| ... | ... | @@ -1337,9 +1347,10 @@ pub fn getDeclVAddr( |
| 1337 | 1347 | return target_symbol_index; |
| 1338 | 1348 | } |
| 1339 | 1349 | |
| 1340 | pub fn deleteExport(wasm: *Wasm, exp: Export) void { | |
| 1350 | pub fn deleteDeclExport(wasm: *Wasm, decl_index: Module.Decl.Index) void { | |
| 1341 | 1351 | if (wasm.llvm_object) |_| return; |
| 1342 | const sym_index = exp.sym_index orelse return; | |
| 1352 | const atom_index = wasm.decls.get(decl_index) orelse return; | |
| 1353 | const sym_index = wasm.getAtom(atom_index).sym_index; | |
| 1343 | 1354 | const loc: SymbolLoc = .{ .file = null, .index = sym_index }; |
| 1344 | 1355 | const symbol = loc.getSymbol(wasm); |
| 1345 | 1356 | const symbol_name = wasm.string_table.get(symbol.name); |
| ... | ... | @@ -1365,6 +1376,8 @@ pub fn updateDeclExports( |
| 1365 | 1376 | } |
| 1366 | 1377 | |
| 1367 | 1378 | const decl = mod.declPtr(decl_index); |
| 1379 | const atom_index = try wasm.getOrCreateAtomForDecl(decl_index); | |
| 1380 | const atom = wasm.getAtom(atom_index); | |
| 1368 | 1381 | |
| 1369 | 1382 | for (exports) |exp| { |
| 1370 | 1383 | if (exp.options.section) |section| { |
| ... | ... | @@ -1379,7 +1392,7 @@ pub fn updateDeclExports( |
| 1379 | 1392 | |
| 1380 | 1393 | const export_name = try wasm.string_table.put(wasm.base.allocator, exp.options.name); |
| 1381 | 1394 | if (wasm.globals.getPtr(export_name)) |existing_loc| { |
| 1382 | if (existing_loc.index == decl.link.wasm.sym_index) continue; | |
| 1395 | if (existing_loc.index == atom.sym_index) continue; | |
| 1383 | 1396 | const existing_sym: Symbol = existing_loc.getSymbol(wasm).*; |
| 1384 | 1397 | |
| 1385 | 1398 | const exp_is_weak = exp.options.linkage == .Internal or exp.options.linkage == .Weak; |
| ... | ... | @@ -1400,15 +1413,16 @@ pub fn updateDeclExports( |
| 1400 | 1413 | } else if (exp_is_weak) { |
| 1401 | 1414 | continue; // to-be-exported symbol is weak, so we keep the existing symbol |
| 1402 | 1415 | } else { |
| 1403 | existing_loc.index = decl.link.wasm.sym_index; | |
| 1416 | // TODO: Revisit this, why was this needed? | |
| 1417 | existing_loc.index = atom.sym_index; | |
| 1404 | 1418 | existing_loc.file = null; |
| 1405 | exp.link.wasm.sym_index = existing_loc.index; | |
| 1419 | // exp.link.wasm.sym_index = existing_loc.index; | |
| 1406 | 1420 | } |
| 1407 | 1421 | } |
| 1408 | 1422 | |
| 1409 | const exported_decl = mod.declPtr(exp.exported_decl); | |
| 1410 | const sym_index = exported_decl.link.wasm.sym_index; | |
| 1411 | const sym_loc = exported_decl.link.wasm.symbolLoc(); | |
| 1423 | const exported_atom_index = try wasm.getOrCreateAtomForDecl(exp.exported_decl); | |
| 1424 | const exported_atom = wasm.getAtom(exported_atom_index); | |
| 1425 | const sym_loc = exported_atom.symbolLoc(); | |
| 1412 | 1426 | const symbol = sym_loc.getSymbol(wasm); |
| 1413 | 1427 | switch (exp.options.linkage) { |
| 1414 | 1428 | .Internal => { |
| ... | ... | @@ -1444,7 +1458,6 @@ pub fn updateDeclExports( |
| 1444 | 1458 | // if the symbol was previously undefined, remove it as an import |
| 1445 | 1459 | _ = wasm.imports.remove(sym_loc); |
| 1446 | 1460 | _ = wasm.undefs.swapRemove(exp.options.name); |
| 1447 | exp.link.wasm.sym_index = sym_index; | |
| 1448 | 1461 | } |
| 1449 | 1462 | } |
| 1450 | 1463 | |
| ... | ... | @@ -1454,11 +1467,13 @@ pub fn freeDecl(wasm: *Wasm, decl_index: Module.Decl.Index) void { |
| 1454 | 1467 | } |
| 1455 | 1468 | const mod = wasm.base.options.module.?; |
| 1456 | 1469 | const decl = mod.declPtr(decl_index); |
| 1457 | const atom = &decl.link.wasm; | |
| 1470 | const atom_index = wasm.decls.get(decl_index).?; | |
| 1471 | const atom = wasm.getAtomPtr(atom_index); | |
| 1458 | 1472 | wasm.symbols_free_list.append(wasm.base.allocator, atom.sym_index) catch {}; |
| 1459 | 1473 | _ = wasm.decls.remove(decl_index); |
| 1460 | 1474 | wasm.symbols.items[atom.sym_index].tag = .dead; |
| 1461 | for (atom.locals.items) |local_atom| { | |
| 1475 | for (atom.locals.items) |local_atom_index| { | |
| 1476 | const local_atom = wasm.getAtom(local_atom_index); | |
| 1462 | 1477 | const local_symbol = &wasm.symbols.items[local_atom.sym_index]; |
| 1463 | 1478 | local_symbol.tag = .dead; // also for any local symbol |
| 1464 | 1479 | wasm.symbols_free_list.append(wasm.base.allocator, local_atom.sym_index) catch {}; |
| ... | ... | @@ -1472,12 +1487,20 @@ pub fn freeDecl(wasm: *Wasm, decl_index: Module.Decl.Index) void { |
| 1472 | 1487 | _ = wasm.resolved_symbols.swapRemove(atom.symbolLoc()); |
| 1473 | 1488 | _ = wasm.symbol_atom.remove(atom.symbolLoc()); |
| 1474 | 1489 | |
| 1475 | if (wasm.dwarf) |*dwarf| { | |
| 1476 | dwarf.freeDecl(decl); | |
| 1477 | dwarf.freeAtom(&atom.dbg_info_atom); | |
| 1478 | } | |
| 1490 | // if (wasm.dwarf) |*dwarf| { | |
| 1491 | // dwarf.freeDecl(decl_index); | |
| 1492 | // } | |
| 1479 | 1493 | |
| 1480 | atom.deinit(wasm.base.allocator); | |
| 1494 | if (atom.next) |next_atom_index| { | |
| 1495 | const next_atom = wasm.getAtomPtr(next_atom_index); | |
| 1496 | next_atom.prev = atom.prev; | |
| 1497 | atom.next = null; | |
| 1498 | } | |
| 1499 | if (atom.prev) |prev_index| { | |
| 1500 | const prev_atom = wasm.getAtomPtr(prev_index); | |
| 1501 | prev_atom.next = atom.next; | |
| 1502 | atom.prev = null; | |
| 1503 | } | |
| 1481 | 1504 | } |
| 1482 | 1505 | |
| 1483 | 1506 | /// Appends a new entry to the indirect function table |
| ... | ... | @@ -1599,7 +1622,8 @@ const Kind = union(enum) { |
| 1599 | 1622 | }; |
| 1600 | 1623 | |
| 1601 | 1624 | /// Parses an Atom and inserts its metadata into the corresponding sections. |
| 1602 | fn parseAtom(wasm: *Wasm, atom: *Atom, kind: Kind) !void { | |
| 1625 | fn parseAtom(wasm: *Wasm, atom_index: Atom.Index, kind: Kind) !void { | |
| 1626 | const atom = wasm.getAtomPtr(atom_index); | |
| 1603 | 1627 | const symbol = (SymbolLoc{ .file = null, .index = atom.sym_index }).getSymbol(wasm); |
| 1604 | 1628 | const final_index: u32 = switch (kind) { |
| 1605 | 1629 | .function => |fn_data| result: { |
| ... | ... | @@ -1674,18 +1698,20 @@ fn parseAtom(wasm: *Wasm, atom: *Atom, kind: Kind) !void { |
| 1674 | 1698 | const segment: *Segment = &wasm.segments.items[final_index]; |
| 1675 | 1699 | segment.alignment = std.math.max(segment.alignment, atom.alignment); |
| 1676 | 1700 | |
| 1677 | try wasm.appendAtomAtIndex(final_index, atom); | |
| 1701 | try wasm.appendAtomAtIndex(final_index, atom_index); | |
| 1678 | 1702 | } |
| 1679 | 1703 | |
| 1680 | 1704 | /// From a given index, append the given `Atom` at the back of the linked list. |
| 1681 | 1705 | /// Simply inserts it into the map of atoms when it doesn't exist yet. |
| 1682 | pub fn appendAtomAtIndex(wasm: *Wasm, index: u32, atom: *Atom) !void { | |
| 1683 | if (wasm.atoms.getPtr(index)) |last| { | |
| 1684 | last.*.next = atom; | |
| 1685 | atom.prev = last.*; | |
| 1686 | last.* = atom; | |
| 1706 | pub fn appendAtomAtIndex(wasm: *Wasm, index: u32, atom_index: Atom.Index) !void { | |
| 1707 | const atom = wasm.getAtomPtr(atom_index); | |
| 1708 | if (wasm.atoms.getPtr(index)) |last_index_ptr| { | |
| 1709 | const last = wasm.getAtomPtr(last_index_ptr.*); | |
| 1710 | last.*.next = atom_index; | |
| 1711 | atom.prev = last_index_ptr.*; | |
| 1712 | last_index_ptr.* = atom_index; | |
| 1687 | 1713 | } else { |
| 1688 | try wasm.atoms.putNoClobber(wasm.base.allocator, index, atom); | |
| 1714 | try wasm.atoms.putNoClobber(wasm.base.allocator, index, atom_index); | |
| 1689 | 1715 | } |
| 1690 | 1716 | } |
| 1691 | 1717 | |
| ... | ... | @@ -1695,16 +1721,17 @@ fn allocateDebugAtoms(wasm: *Wasm) !void { |
| 1695 | 1721 | if (wasm.dwarf == null) return; |
| 1696 | 1722 | |
| 1697 | 1723 | const allocAtom = struct { |
| 1698 | fn f(bin: *Wasm, maybe_index: *?u32, atom: *Atom) !void { | |
| 1724 | fn f(bin: *Wasm, maybe_index: *?u32, atom_index: Atom.Index) !void { | |
| 1699 | 1725 | const index = maybe_index.* orelse idx: { |
| 1700 | 1726 | const index = @intCast(u32, bin.segments.items.len); |
| 1701 | 1727 | try bin.appendDummySegment(); |
| 1702 | 1728 | maybe_index.* = index; |
| 1703 | 1729 | break :idx index; |
| 1704 | 1730 | }; |
| 1731 | const atom = bin.getAtomPtr(atom_index); | |
| 1705 | 1732 | atom.size = @intCast(u32, atom.code.items.len); |
| 1706 | 1733 | bin.symbols.items[atom.sym_index].index = index; |
| 1707 | try bin.appendAtomAtIndex(index, atom); | |
| 1734 | try bin.appendAtomAtIndex(index, atom_index); | |
| 1708 | 1735 | } |
| 1709 | 1736 | }.f; |
| 1710 | 1737 | |
| ... | ... | @@ -1726,15 +1753,16 @@ fn allocateAtoms(wasm: *Wasm) !void { |
| 1726 | 1753 | var it = wasm.atoms.iterator(); |
| 1727 | 1754 | while (it.next()) |entry| { |
| 1728 | 1755 | const segment = &wasm.segments.items[entry.key_ptr.*]; |
| 1729 | var atom: *Atom = entry.value_ptr.*.getFirst(); | |
| 1756 | var atom_index = entry.value_ptr.*; | |
| 1730 | 1757 | var offset: u32 = 0; |
| 1731 | 1758 | while (true) { |
| 1759 | const atom = wasm.getAtomPtr(atom_index); | |
| 1732 | 1760 | const symbol_loc = atom.symbolLoc(); |
| 1733 | 1761 | if (wasm.code_section_index) |index| { |
| 1734 | 1762 | if (index == entry.key_ptr.*) { |
| 1735 | 1763 | if (!wasm.resolved_symbols.contains(symbol_loc)) { |
| 1736 | 1764 | // only allocate resolved function body's. |
| 1737 | atom = atom.next orelse break; | |
| 1765 | atom_index = atom.prev orelse break; | |
| 1738 | 1766 | continue; |
| 1739 | 1767 | } |
| 1740 | 1768 | } |
| ... | ... | @@ -1748,8 +1776,7 @@ fn allocateAtoms(wasm: *Wasm) !void { |
| 1748 | 1776 | atom.size, |
| 1749 | 1777 | }); |
| 1750 | 1778 | offset += atom.size; |
| 1751 | try wasm.symbol_atom.put(wasm.base.allocator, symbol_loc, atom); // Update atom pointers | |
| 1752 | atom = atom.next orelse break; | |
| 1779 | atom_index = atom.prev orelse break; | |
| 1753 | 1780 | } |
| 1754 | 1781 | segment.size = std.mem.alignForwardGeneric(u32, offset, segment.alignment); |
| 1755 | 1782 | } |
| ... | ... | @@ -1883,8 +1910,8 @@ fn initializeCallCtorsFunction(wasm: *Wasm) !void { |
| 1883 | 1910 | symbol.index = func_index; |
| 1884 | 1911 | |
| 1885 | 1912 | // create the atom that will be output into the final binary |
| 1886 | const atom = try wasm.base.allocator.create(Atom); | |
| 1887 | errdefer wasm.base.allocator.destroy(atom); | |
| 1913 | const atom_index = @intCast(Atom.Index, wasm.managed_atoms.items.len); | |
| 1914 | const atom = try wasm.managed_atoms.addOne(wasm.base.allocator); | |
| 1888 | 1915 | atom.* = .{ |
| 1889 | 1916 | .size = @intCast(u32, function_body.items.len), |
| 1890 | 1917 | .offset = 0, |
| ... | ... | @@ -1894,15 +1921,14 @@ fn initializeCallCtorsFunction(wasm: *Wasm) !void { |
| 1894 | 1921 | .next = null, |
| 1895 | 1922 | .prev = null, |
| 1896 | 1923 | .code = function_body.moveToUnmanaged(), |
| 1897 | .dbg_info_atom = undefined, | |
| 1898 | 1924 | }; |
| 1899 | try wasm.managed_atoms.append(wasm.base.allocator, atom); | |
| 1900 | try wasm.appendAtomAtIndex(wasm.code_section_index.?, atom); | |
| 1901 | try wasm.symbol_atom.putNoClobber(wasm.base.allocator, loc, atom); | |
| 1925 | try wasm.appendAtomAtIndex(wasm.code_section_index.?, atom_index); | |
| 1926 | try wasm.symbol_atom.putNoClobber(wasm.base.allocator, loc, atom_index); | |
| 1902 | 1927 | |
| 1903 | 1928 | // `allocateAtoms` has already been called, set the atom's offset manually. |
| 1904 | 1929 | // This is fine to do manually as we insert the atom at the very end. |
| 1905 | atom.offset = atom.prev.?.offset + atom.prev.?.size; | |
| 1930 | const prev_atom = wasm.getAtom(atom.prev.?); | |
| 1931 | atom.offset = prev_atom.offset + prev_atom.size; | |
| 1906 | 1932 | } |
| 1907 | 1933 | |
| 1908 | 1934 | fn setupImports(wasm: *Wasm) !void { |
| ... | ... | @@ -2105,7 +2131,8 @@ fn setupExports(wasm: *Wasm) !void { |
| 2105 | 2131 | break :blk try wasm.string_table.put(wasm.base.allocator, sym_name); |
| 2106 | 2132 | }; |
| 2107 | 2133 | const exp: types.Export = if (symbol.tag == .data) exp: { |
| 2108 | const atom = wasm.symbol_atom.get(sym_loc).?; | |
| 2134 | const atom_index = wasm.symbol_atom.get(sym_loc).?; | |
| 2135 | const atom = wasm.getAtom(atom_index); | |
| 2109 | 2136 | const va = atom.getVA(wasm, symbol); |
| 2110 | 2137 | const global_index = @intCast(u32, wasm.imported_globals_count + wasm.wasm_globals.items.len); |
| 2111 | 2138 | try wasm.wasm_globals.append(wasm.base.allocator, .{ |
| ... | ... | @@ -2210,7 +2237,8 @@ fn setupMemory(wasm: *Wasm) !void { |
| 2210 | 2237 | const segment_index = wasm.data_segments.get(".synthetic").?; |
| 2211 | 2238 | const segment = &wasm.segments.items[segment_index]; |
| 2212 | 2239 | segment.offset = 0; // for simplicity we store the entire VA into atom's offset. |
| 2213 | const atom = wasm.symbol_atom.get(loc).?; | |
| 2240 | const atom_index = wasm.symbol_atom.get(loc).?; | |
| 2241 | const atom = wasm.getAtomPtr(atom_index); | |
| 2214 | 2242 | atom.offset = @intCast(u32, mem.alignForwardGeneric(u64, memory_ptr, heap_alignment)); |
| 2215 | 2243 | } |
| 2216 | 2244 | |
| ... | ... | @@ -2243,7 +2271,8 @@ fn setupMemory(wasm: *Wasm) !void { |
| 2243 | 2271 | const segment_index = wasm.data_segments.get(".synthetic").?; |
| 2244 | 2272 | const segment = &wasm.segments.items[segment_index]; |
| 2245 | 2273 | segment.offset = 0; |
| 2246 | const atom = wasm.symbol_atom.get(loc).?; | |
| 2274 | const atom_index = wasm.symbol_atom.get(loc).?; | |
| 2275 | const atom = wasm.getAtomPtr(atom_index); | |
| 2247 | 2276 | atom.offset = @intCast(u32, memory_ptr); |
| 2248 | 2277 | } |
| 2249 | 2278 | |
| ... | ... | @@ -2369,15 +2398,14 @@ pub fn getErrorTableSymbol(wasm: *Wasm) !u32 { |
| 2369 | 2398 | // and then return said symbol's index. The final table will be populated |
| 2370 | 2399 | // during `flush` when we know all possible error names. |
| 2371 | 2400 | |
| 2372 | // As sym_index '0' is reserved, we use it for our stack pointer symbol | |
| 2373 | const symbol_index = wasm.symbols_free_list.popOrNull() orelse blk: { | |
| 2374 | const index = @intCast(u32, wasm.symbols.items.len); | |
| 2375 | _ = try wasm.symbols.addOne(wasm.base.allocator); | |
| 2376 | break :blk index; | |
| 2377 | }; | |
| 2401 | const atom_index = try wasm.createAtom(); | |
| 2402 | const atom = wasm.getAtomPtr(atom_index); | |
| 2403 | const slice_ty = Type.initTag(.const_slice_u8_sentinel_0); | |
| 2404 | atom.alignment = slice_ty.abiAlignment(wasm.base.options.target); | |
| 2405 | const sym_index = atom.sym_index; | |
| 2378 | 2406 | |
| 2379 | 2407 | const sym_name = try wasm.string_table.put(wasm.base.allocator, "__zig_err_name_table"); |
| 2380 | const symbol = &wasm.symbols.items[symbol_index]; | |
| 2408 | const symbol = &wasm.symbols.items[sym_index]; | |
| 2381 | 2409 | symbol.* = .{ |
| 2382 | 2410 | .name = sym_name, |
| 2383 | 2411 | .tag = .data, |
| ... | ... | @@ -2386,20 +2414,11 @@ pub fn getErrorTableSymbol(wasm: *Wasm) !u32 { |
| 2386 | 2414 | }; |
| 2387 | 2415 | symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); |
| 2388 | 2416 | |
| 2389 | const slice_ty = Type.initTag(.const_slice_u8_sentinel_0); | |
| 2417 | try wasm.resolved_symbols.put(wasm.base.allocator, atom.symbolLoc(), {}); | |
| 2390 | 2418 | |
| 2391 | const atom = try wasm.base.allocator.create(Atom); | |
| 2392 | atom.* = Atom.empty; | |
| 2393 | atom.sym_index = symbol_index; | |
| 2394 | atom.alignment = slice_ty.abiAlignment(wasm.base.options.target); | |
| 2395 | try wasm.managed_atoms.append(wasm.base.allocator, atom); | |
| 2396 | const loc = atom.symbolLoc(); | |
| 2397 | try wasm.resolved_symbols.put(wasm.base.allocator, loc, {}); | |
| 2398 | try wasm.symbol_atom.put(wasm.base.allocator, loc, atom); | |
| 2399 | ||
| 2400 | log.debug("Error name table was created with symbol index: ({d})", .{symbol_index}); | |
| 2401 | wasm.error_table_symbol = symbol_index; | |
| 2402 | return symbol_index; | |
| 2419 | log.debug("Error name table was created with symbol index: ({d})", .{sym_index}); | |
| 2420 | wasm.error_table_symbol = sym_index; | |
| 2421 | return sym_index; | |
| 2403 | 2422 | } |
| 2404 | 2423 | |
| 2405 | 2424 | /// Populates the error name table, when `error_table_symbol` is not null. |
| ... | ... | @@ -2408,22 +2427,17 @@ pub fn getErrorTableSymbol(wasm: *Wasm) !u32 { |
| 2408 | 2427 | /// The table is what is being pointed to within the runtime bodies that are generated. |
| 2409 | 2428 | fn populateErrorNameTable(wasm: *Wasm) !void { |
| 2410 | 2429 | const symbol_index = wasm.error_table_symbol orelse return; |
| 2411 | const atom: *Atom = wasm.symbol_atom.get(.{ .file = null, .index = symbol_index }).?; | |
| 2430 | const atom_index = wasm.symbol_atom.get(.{ .file = null, .index = symbol_index }).?; | |
| 2431 | const atom = wasm.getAtomPtr(atom_index); | |
| 2432 | ||
| 2412 | 2433 | // Rather than creating a symbol for each individual error name, |
| 2413 | 2434 | // we create a symbol for the entire region of error names. We then calculate |
| 2414 | 2435 | // the pointers into the list using addends which are appended to the relocation. |
| 2415 | const names_atom = try wasm.base.allocator.create(Atom); | |
| 2416 | names_atom.* = Atom.empty; | |
| 2417 | try wasm.managed_atoms.append(wasm.base.allocator, names_atom); | |
| 2418 | const names_symbol_index = wasm.symbols_free_list.popOrNull() orelse blk: { | |
| 2419 | const index = @intCast(u32, wasm.symbols.items.len); | |
| 2420 | _ = try wasm.symbols.addOne(wasm.base.allocator); | |
| 2421 | break :blk index; | |
| 2422 | }; | |
| 2423 | names_atom.sym_index = names_symbol_index; | |
| 2436 | const names_atom_index = try wasm.createAtom(); | |
| 2437 | const names_atom = wasm.getAtomPtr(names_atom_index); | |
| 2424 | 2438 | names_atom.alignment = 1; |
| 2425 | 2439 | const sym_name = try wasm.string_table.put(wasm.base.allocator, "__zig_err_names"); |
| 2426 | const names_symbol = &wasm.symbols.items[names_symbol_index]; | |
| 2440 | const names_symbol = &wasm.symbols.items[names_atom.sym_index]; | |
| 2427 | 2441 | names_symbol.* = .{ |
| 2428 | 2442 | .name = sym_name, |
| 2429 | 2443 | .tag = .data, |
| ... | ... | @@ -2447,7 +2461,7 @@ fn populateErrorNameTable(wasm: *Wasm) !void { |
| 2447 | 2461 | try atom.code.writer(wasm.base.allocator).writeIntLittle(u32, len - 1); |
| 2448 | 2462 | // create relocation to the error name |
| 2449 | 2463 | try atom.relocs.append(wasm.base.allocator, .{ |
| 2450 | .index = names_symbol_index, | |
| 2464 | .index = names_atom.sym_index, | |
| 2451 | 2465 | .relocation_type = .R_WASM_MEMORY_ADDR_I32, |
| 2452 | 2466 | .offset = offset, |
| 2453 | 2467 | .addend = @intCast(i32, addend), |
| ... | ... | @@ -2466,61 +2480,53 @@ fn populateErrorNameTable(wasm: *Wasm) !void { |
| 2466 | 2480 | |
| 2467 | 2481 | const name_loc = names_atom.symbolLoc(); |
| 2468 | 2482 | try wasm.resolved_symbols.put(wasm.base.allocator, name_loc, {}); |
| 2469 | try wasm.symbol_atom.put(wasm.base.allocator, name_loc, names_atom); | |
| 2483 | try wasm.symbol_atom.put(wasm.base.allocator, name_loc, names_atom_index); | |
| 2470 | 2484 | |
| 2471 | 2485 | // link the atoms with the rest of the binary so they can be allocated |
| 2472 | 2486 | // and relocations will be performed. |
| 2473 | try wasm.parseAtom(atom, .{ .data = .read_only }); | |
| 2474 | try wasm.parseAtom(names_atom, .{ .data = .read_only }); | |
| 2487 | try wasm.parseAtom(atom_index, .{ .data = .read_only }); | |
| 2488 | try wasm.parseAtom(names_atom_index, .{ .data = .read_only }); | |
| 2475 | 2489 | } |
| 2476 | 2490 | |
| 2477 | 2491 | /// From a given index variable, creates a new debug section. |
| 2478 | 2492 | /// This initializes the index, appends a new segment, |
| 2479 | 2493 | /// and finally, creates a managed `Atom`. |
| 2480 | pub fn createDebugSectionForIndex(wasm: *Wasm, index: *?u32, name: []const u8) !*Atom { | |
| 2494 | pub fn createDebugSectionForIndex(wasm: *Wasm, index: *?u32, name: []const u8) !Atom.Index { | |
| 2481 | 2495 | const new_index = @intCast(u32, wasm.segments.items.len); |
| 2482 | 2496 | index.* = new_index; |
| 2483 | 2497 | try wasm.appendDummySegment(); |
| 2484 | 2498 | |
| 2485 | const sym_index = wasm.symbols_free_list.popOrNull() orelse idx: { | |
| 2486 | const tmp_index = @intCast(u32, wasm.symbols.items.len); | |
| 2487 | _ = try wasm.symbols.addOne(wasm.base.allocator); | |
| 2488 | break :idx tmp_index; | |
| 2489 | }; | |
| 2490 | wasm.symbols.items[sym_index] = .{ | |
| 2499 | const atom_index = try wasm.createAtom(); | |
| 2500 | const atom = wasm.getAtomPtr(atom_index); | |
| 2501 | wasm.symbols.items[atom.sym_index] = .{ | |
| 2491 | 2502 | .tag = .section, |
| 2492 | 2503 | .name = try wasm.string_table.put(wasm.base.allocator, name), |
| 2493 | 2504 | .index = 0, |
| 2494 | 2505 | .flags = @enumToInt(Symbol.Flag.WASM_SYM_BINDING_LOCAL), |
| 2495 | 2506 | }; |
| 2496 | 2507 | |
| 2497 | const atom = try wasm.base.allocator.create(Atom); | |
| 2498 | atom.* = Atom.empty; | |
| 2499 | 2508 | atom.alignment = 1; // debug sections are always 1-byte-aligned |
| 2500 | atom.sym_index = sym_index; | |
| 2501 | try wasm.managed_atoms.append(wasm.base.allocator, atom); | |
| 2502 | try wasm.symbol_atom.put(wasm.base.allocator, atom.symbolLoc(), atom); | |
| 2503 | return atom; | |
| 2509 | return atom_index; | |
| 2504 | 2510 | } |
| 2505 | 2511 | |
| 2506 | 2512 | fn resetState(wasm: *Wasm) void { |
| 2507 | 2513 | for (wasm.segment_info.values()) |segment_info| { |
| 2508 | 2514 | wasm.base.allocator.free(segment_info.name); |
| 2509 | 2515 | } |
| 2510 | if (wasm.base.options.module) |mod| { | |
| 2511 | var decl_it = wasm.decls.keyIterator(); | |
| 2512 | while (decl_it.next()) |decl_index_ptr| { | |
| 2513 | const decl = mod.declPtr(decl_index_ptr.*); | |
| 2514 | const atom = &decl.link.wasm; | |
| 2515 | atom.next = null; | |
| 2516 | atom.prev = null; | |
| 2517 | ||
| 2518 | for (atom.locals.items) |*local_atom| { | |
| 2519 | local_atom.next = null; | |
| 2520 | local_atom.prev = null; | |
| 2521 | } | |
| 2516 | ||
| 2517 | var atom_it = wasm.decls.valueIterator(); | |
| 2518 | while (atom_it.next()) |atom_index| { | |
| 2519 | const atom = wasm.getAtomPtr(atom_index.*); | |
| 2520 | atom.next = null; | |
| 2521 | atom.prev = null; | |
| 2522 | ||
| 2523 | for (atom.locals.items) |local_atom_index| { | |
| 2524 | const local_atom = wasm.getAtomPtr(local_atom_index); | |
| 2525 | local_atom.next = null; | |
| 2526 | local_atom.prev = null; | |
| 2522 | 2527 | } |
| 2523 | 2528 | } |
| 2529 | ||
| 2524 | 2530 | wasm.functions.clearRetainingCapacity(); |
| 2525 | 2531 | wasm.exports.clearRetainingCapacity(); |
| 2526 | 2532 | wasm.segments.clearRetainingCapacity(); |
| ... | ... | @@ -2817,28 +2823,29 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod |
| 2817 | 2823 | try wasm.setupStart(); |
| 2818 | 2824 | try wasm.setupImports(); |
| 2819 | 2825 | if (wasm.base.options.module) |mod| { |
| 2820 | var decl_it = wasm.decls.keyIterator(); | |
| 2821 | while (decl_it.next()) |decl_index_ptr| { | |
| 2822 | const decl = mod.declPtr(decl_index_ptr.*); | |
| 2826 | var decl_it = wasm.decls.iterator(); | |
| 2827 | while (decl_it.next()) |entry| { | |
| 2828 | const decl = mod.declPtr(entry.key_ptr.*); | |
| 2823 | 2829 | if (decl.isExtern()) continue; |
| 2824 | const atom = &decl.*.link.wasm; | |
| 2830 | const atom_index = entry.value_ptr.*; | |
| 2825 | 2831 | if (decl.ty.zigTypeTag() == .Fn) { |
| 2826 | try wasm.parseAtom(atom, .{ .function = decl.fn_link.wasm }); | |
| 2832 | try wasm.parseAtom(atom_index, .{ .function = decl.fn_link.? }); | |
| 2827 | 2833 | } else if (decl.getVariable()) |variable| { |
| 2828 | 2834 | if (!variable.is_mutable) { |
| 2829 | try wasm.parseAtom(atom, .{ .data = .read_only }); | |
| 2835 | try wasm.parseAtom(atom_index, .{ .data = .read_only }); | |
| 2830 | 2836 | } else if (variable.init.isUndefDeep()) { |
| 2831 | try wasm.parseAtom(atom, .{ .data = .uninitialized }); | |
| 2837 | try wasm.parseAtom(atom_index, .{ .data = .uninitialized }); | |
| 2832 | 2838 | } else { |
| 2833 | try wasm.parseAtom(atom, .{ .data = .initialized }); | |
| 2839 | try wasm.parseAtom(atom_index, .{ .data = .initialized }); | |
| 2834 | 2840 | } |
| 2835 | 2841 | } else { |
| 2836 | try wasm.parseAtom(atom, .{ .data = .read_only }); | |
| 2842 | try wasm.parseAtom(atom_index, .{ .data = .read_only }); | |
| 2837 | 2843 | } |
| 2838 | 2844 | |
| 2839 | 2845 | // also parse atoms for a decl's locals |
| 2840 | for (atom.locals.items) |*local_atom| { | |
| 2841 | try wasm.parseAtom(local_atom, .{ .data = .read_only }); | |
| 2846 | const atom = wasm.getAtomPtr(atom_index); | |
| 2847 | for (atom.locals.items) |local_atom_index| { | |
| 2848 | try wasm.parseAtom(local_atom_index, .{ .data = .read_only }); | |
| 2842 | 2849 | } |
| 2843 | 2850 | } |
| 2844 | 2851 | |
| ... | ... | @@ -3083,20 +3090,22 @@ fn writeToFile( |
| 3083 | 3090 | var code_section_size: u32 = 0; |
| 3084 | 3091 | if (wasm.code_section_index) |code_index| { |
| 3085 | 3092 | const header_offset = try reserveVecSectionHeader(&binary_bytes); |
| 3086 | var atom: *Atom = wasm.atoms.get(code_index).?.getFirst(); | |
| 3093 | var atom_index = wasm.atoms.get(code_index).?; | |
| 3087 | 3094 | |
| 3088 | 3095 | // The code section must be sorted in line with the function order. |
| 3089 | 3096 | var sorted_atoms = try std.ArrayList(*Atom).initCapacity(wasm.base.allocator, wasm.functions.count()); |
| 3090 | 3097 | defer sorted_atoms.deinit(); |
| 3091 | 3098 | |
| 3092 | 3099 | while (true) { |
| 3100 | var atom = wasm.getAtomPtr(atom_index); | |
| 3093 | 3101 | if (wasm.resolved_symbols.contains(atom.symbolLoc())) { |
| 3094 | 3102 | if (!is_obj) { |
| 3095 | 3103 | atom.resolveRelocs(wasm); |
| 3096 | 3104 | } |
| 3097 | 3105 | sorted_atoms.appendAssumeCapacity(atom); |
| 3098 | 3106 | } |
| 3099 | atom = atom.next orelse break; | |
| 3107 | // atom = if (atom.prev) |prev| wasm.getAtomPtr(prev) else break; | |
| 3108 | atom_index = atom.prev orelse break; | |
| 3100 | 3109 | } |
| 3101 | 3110 | |
| 3102 | 3111 | const atom_sort_fn = struct { |
| ... | ... | @@ -3136,11 +3145,11 @@ fn writeToFile( |
| 3136 | 3145 | // do not output 'bss' section unless we import memory and therefore |
| 3137 | 3146 | // want to guarantee the data is zero initialized |
| 3138 | 3147 | if (!import_memory and std.mem.eql(u8, entry.key_ptr.*, ".bss")) continue; |
| 3139 | const atom_index = entry.value_ptr.*; | |
| 3140 | const segment = wasm.segments.items[atom_index]; | |
| 3148 | const segment_index = entry.value_ptr.*; | |
| 3149 | const segment = wasm.segments.items[segment_index]; | |
| 3141 | 3150 | if (segment.size == 0) continue; // do not emit empty segments |
| 3142 | 3151 | segment_count += 1; |
| 3143 | var atom: *Atom = wasm.atoms.getPtr(atom_index).?.*.getFirst(); | |
| 3152 | var atom_index = wasm.atoms.get(segment_index).?; | |
| 3144 | 3153 | |
| 3145 | 3154 | // flag and index to memory section (currently, there can only be 1 memory section in wasm) |
| 3146 | 3155 | try leb.writeULEB128(binary_writer, @as(u32, 0)); |
| ... | ... | @@ -3151,6 +3160,7 @@ fn writeToFile( |
| 3151 | 3160 | // fill in the offset table and the data segments |
| 3152 | 3161 | var current_offset: u32 = 0; |
| 3153 | 3162 | while (true) { |
| 3163 | const atom = wasm.getAtomPtr(atom_index); | |
| 3154 | 3164 | if (!is_obj) { |
| 3155 | 3165 | atom.resolveRelocs(wasm); |
| 3156 | 3166 | } |
| ... | ... | @@ -3166,8 +3176,8 @@ fn writeToFile( |
| 3166 | 3176 | try binary_writer.writeAll(atom.code.items); |
| 3167 | 3177 | |
| 3168 | 3178 | current_offset += atom.size; |
| 3169 | if (atom.next) |next| { | |
| 3170 | atom = next; | |
| 3179 | if (atom.prev) |prev| { | |
| 3180 | atom_index = prev; | |
| 3171 | 3181 | } else { |
| 3172 | 3182 | // also pad with zeroes when last atom to ensure |
| 3173 | 3183 | // segments are aligned. |
| ... | ... | @@ -3209,15 +3219,15 @@ fn writeToFile( |
| 3209 | 3219 | } |
| 3210 | 3220 | |
| 3211 | 3221 | if (!wasm.base.options.strip) { |
| 3212 | if (wasm.dwarf) |*dwarf| { | |
| 3213 | const mod = wasm.base.options.module.?; | |
| 3214 | try dwarf.writeDbgAbbrev(); | |
| 3215 | // for debug info and ranges, the address is always 0, | |
| 3216 | // as locations are always offsets relative to 'code' section. | |
| 3217 | try dwarf.writeDbgInfoHeader(mod, 0, code_section_size); | |
| 3218 | try dwarf.writeDbgAranges(0, code_section_size); | |
| 3219 | try dwarf.writeDbgLineHeader(); | |
| 3220 | } | |
| 3222 | // if (wasm.dwarf) |*dwarf| { | |
| 3223 | // const mod = wasm.base.options.module.?; | |
| 3224 | // try dwarf.writeDbgAbbrev(); | |
| 3225 | // // for debug info and ranges, the address is always 0, | |
| 3226 | // // as locations are always offsets relative to 'code' section. | |
| 3227 | // try dwarf.writeDbgInfoHeader(mod, 0, code_section_size); | |
| 3228 | // try dwarf.writeDbgAranges(0, code_section_size); | |
| 3229 | // try dwarf.writeDbgLineHeader(); | |
| 3230 | // } | |
| 3221 | 3231 | |
| 3222 | 3232 | var debug_bytes = std.ArrayList(u8).init(wasm.base.allocator); |
| 3223 | 3233 | defer debug_bytes.deinit(); |
| ... | ... | @@ -3240,11 +3250,11 @@ fn writeToFile( |
| 3240 | 3250 | |
| 3241 | 3251 | for (debug_sections) |item| { |
| 3242 | 3252 | if (item.index) |index| { |
| 3243 | var atom = wasm.atoms.get(index).?.getFirst(); | |
| 3253 | var atom = wasm.getAtomPtr(wasm.atoms.get(index).?); | |
| 3244 | 3254 | while (true) { |
| 3245 | 3255 | atom.resolveRelocs(wasm); |
| 3246 | 3256 | try debug_bytes.appendSlice(atom.code.items); |
| 3247 | atom = atom.next orelse break; | |
| 3257 | atom = if (atom.prev) |prev| wasm.getAtomPtr(prev) else break; | |
| 3248 | 3258 | } |
| 3249 | 3259 | try emitDebugSection(&binary_bytes, debug_bytes.items, item.name); |
| 3250 | 3260 | debug_bytes.clearRetainingCapacity(); |
| ... | ... | @@ -3976,7 +3986,8 @@ fn emitSymbolTable(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), symbol_table: |
| 3976 | 3986 | |
| 3977 | 3987 | if (symbol.isDefined()) { |
| 3978 | 3988 | try leb.writeULEB128(writer, symbol.index); |
| 3979 | const atom = wasm.symbol_atom.get(sym_loc).?; | |
| 3989 | const atom_index = wasm.symbol_atom.get(sym_loc).?; | |
| 3990 | const atom = wasm.getAtom(atom_index); | |
| 3980 | 3991 | try leb.writeULEB128(writer, @as(u32, atom.offset)); |
| 3981 | 3992 | try leb.writeULEB128(writer, @as(u32, atom.size)); |
| 3982 | 3993 | } |
| ... | ... | @@ -4054,7 +4065,7 @@ fn emitCodeRelocations( |
| 4054 | 4065 | const reloc_start = binary_bytes.items.len; |
| 4055 | 4066 | |
| 4056 | 4067 | var count: u32 = 0; |
| 4057 | var atom: *Atom = wasm.atoms.get(code_index).?.getFirst(); | |
| 4068 | var atom: *Atom = wasm.getAtomPtr(wasm.atoms.get(code_index).?); | |
| 4058 | 4069 | // for each atom, we calculate the uleb size and append that |
| 4059 | 4070 | var size_offset: u32 = 5; // account for code section size leb128 |
| 4060 | 4071 | while (true) { |
| ... | ... | @@ -4072,7 +4083,7 @@ fn emitCodeRelocations( |
| 4072 | 4083 | } |
| 4073 | 4084 | log.debug("Emit relocation: {}", .{relocation}); |
| 4074 | 4085 | } |
| 4075 | atom = atom.next orelse break; | |
| 4086 | atom = if (atom.prev) |prev| wasm.getAtomPtr(prev) else break; | |
| 4076 | 4087 | } |
| 4077 | 4088 | if (count == 0) return; |
| 4078 | 4089 | var buf: [5]u8 = undefined; |
| ... | ... | @@ -4103,7 +4114,7 @@ fn emitDataRelocations( |
| 4103 | 4114 | // for each atom, we calculate the uleb size and append that |
| 4104 | 4115 | var size_offset: u32 = 5; // account for code section size leb128 |
| 4105 | 4116 | for (wasm.data_segments.values()) |segment_index| { |
| 4106 | var atom: *Atom = wasm.atoms.get(segment_index).?.getFirst(); | |
| 4117 | var atom: *Atom = wasm.getAtomPtr(wasm.atoms.get(segment_index).?); | |
| 4107 | 4118 | while (true) { |
| 4108 | 4119 | size_offset += getULEB128Size(atom.size); |
| 4109 | 4120 | for (atom.relocs.items) |relocation| { |
| ... | ... | @@ -4122,7 +4133,7 @@ fn emitDataRelocations( |
| 4122 | 4133 | } |
| 4123 | 4134 | log.debug("Emit relocation: {}", .{relocation}); |
| 4124 | 4135 | } |
| 4125 | atom = atom.next orelse break; | |
| 4136 | atom = if (atom.prev) |prev| wasm.getAtomPtr(prev) else break; | |
| 4126 | 4137 | } |
| 4127 | 4138 | } |
| 4128 | 4139 | if (count == 0) return; |
src/link/Wasm/Atom.zig+24-22| ... | ... | @@ -4,7 +4,6 @@ const std = @import("std"); |
| 4 | 4 | const types = @import("types.zig"); |
| 5 | 5 | const Wasm = @import("../Wasm.zig"); |
| 6 | 6 | const Symbol = @import("Symbol.zig"); |
| 7 | const Dwarf = @import("../Dwarf.zig"); | |
| 8 | 7 | |
| 9 | 8 | const leb = std.leb; |
| 10 | 9 | const log = std.log.scoped(.link); |
| ... | ... | @@ -30,17 +29,17 @@ file: ?u16, |
| 30 | 29 | |
| 31 | 30 | /// Next atom in relation to this atom. |
| 32 | 31 | /// When null, this atom is the last atom |
| 33 | next: ?*Atom, | |
| 32 | next: ?Atom.Index, | |
| 34 | 33 | /// Previous atom in relation to this atom. |
| 35 | 34 | /// is null when this atom is the first in its order |
| 36 | prev: ?*Atom, | |
| 35 | prev: ?Atom.Index, | |
| 37 | 36 | |
| 38 | 37 | /// Contains atoms local to a decl, all managed by this `Atom`. |
| 39 | 38 | /// When the parent atom is being freed, it will also do so for all local atoms. |
| 40 | locals: std.ArrayListUnmanaged(Atom) = .{}, | |
| 39 | locals: std.ArrayListUnmanaged(Atom.Index) = .{}, | |
| 41 | 40 | |
| 42 | /// Represents the debug Atom that holds all debug information of this Atom. | |
| 43 | dbg_info_atom: Dwarf.Atom, | |
| 41 | /// Alias to an unsigned 32-bit integer | |
| 42 | pub const Index = u32; | |
| 44 | 43 | |
| 45 | 44 | /// Represents a default empty wasm `Atom` |
| 46 | 45 | pub const empty: Atom = .{ |
| ... | ... | @@ -51,18 +50,15 @@ pub const empty: Atom = .{ |
| 51 | 50 | .prev = null, |
| 52 | 51 | .size = 0, |
| 53 | 52 | .sym_index = 0, |
| 54 | .dbg_info_atom = undefined, | |
| 55 | 53 | }; |
| 56 | 54 | |
| 57 | 55 | /// Frees all resources owned by this `Atom`. |
| 58 | pub fn deinit(atom: *Atom, gpa: Allocator) void { | |
| 56 | pub fn deinit(atom: *Atom, wasm: *Wasm) void { | |
| 57 | const gpa = wasm.base.allocator; | |
| 59 | 58 | atom.relocs.deinit(gpa); |
| 60 | 59 | atom.code.deinit(gpa); |
| 61 | ||
| 62 | for (atom.locals.items) |*local| { | |
| 63 | local.deinit(gpa); | |
| 64 | } | |
| 65 | 60 | atom.locals.deinit(gpa); |
| 61 | atom.* = undefined; | |
| 66 | 62 | } |
| 67 | 63 | |
| 68 | 64 | /// Sets the length of relocations and code to '0', |
| ... | ... | @@ -83,18 +79,16 @@ pub fn format(atom: Atom, comptime fmt: []const u8, options: std.fmt.FormatOptio |
| 83 | 79 | }); |
| 84 | 80 | } |
| 85 | 81 | |
| 86 | /// Returns the first `Atom` from a given atom | |
| 87 | pub fn getFirst(atom: *Atom) *Atom { | |
| 88 | var tmp = atom; | |
| 89 | while (tmp.prev) |prev| tmp = prev; | |
| 90 | return tmp; | |
| 91 | } | |
| 92 | ||
| 93 | 82 | /// Returns the location of the symbol that represents this `Atom` |
| 94 | 83 | pub fn symbolLoc(atom: Atom) Wasm.SymbolLoc { |
| 95 | 84 | return .{ .file = atom.file, .index = atom.sym_index }; |
| 96 | 85 | } |
| 97 | 86 | |
| 87 | pub fn getSymbolIndex(atom: Atom) ?u32 { | |
| 88 | if (atom.sym_index == 0) return null; | |
| 89 | return atom.sym_index; | |
| 90 | } | |
| 91 | ||
| 98 | 92 | /// Returns the virtual address of the `Atom`. This is the address starting |
| 99 | 93 | /// from the first entry within a section. |
| 100 | 94 | pub fn getVA(atom: Atom, wasm: *const Wasm, symbol: *const Symbol) u32 { |
| ... | ... | @@ -192,20 +186,28 @@ fn relocationValue(atom: Atom, relocation: types.Relocation, wasm_bin: *const Wa |
| 192 | 186 | if (symbol.isUndefined()) { |
| 193 | 187 | return 0; |
| 194 | 188 | } |
| 195 | const target_atom = wasm_bin.symbol_atom.get(target_loc).?; | |
| 189 | const target_atom_index = wasm_bin.symbol_atom.get(target_loc) orelse { | |
| 190 | // this can only occur during incremental-compilation when a relocation | |
| 191 | // still points to a freed decl. It is fine to emit the value 0 here | |
| 192 | // as no actual code will point towards it. | |
| 193 | return 0; | |
| 194 | }; | |
| 195 | const target_atom = wasm_bin.getAtom(target_atom_index); | |
| 196 | 196 | const va = @intCast(i32, target_atom.getVA(wasm_bin, symbol)); |
| 197 | 197 | return @intCast(u32, va + relocation.addend); |
| 198 | 198 | }, |
| 199 | 199 | .R_WASM_EVENT_INDEX_LEB => return symbol.index, |
| 200 | 200 | .R_WASM_SECTION_OFFSET_I32 => { |
| 201 | const target_atom = wasm_bin.symbol_atom.get(target_loc).?; | |
| 201 | const target_atom_index = wasm_bin.symbol_atom.get(target_loc).?; | |
| 202 | const target_atom = wasm_bin.getAtom(target_atom_index); | |
| 202 | 203 | const rel_value = @intCast(i32, target_atom.offset) + relocation.addend; |
| 203 | 204 | return @intCast(u32, rel_value); |
| 204 | 205 | }, |
| 205 | 206 | .R_WASM_FUNCTION_OFFSET_I32 => { |
| 206 | const target_atom = wasm_bin.symbol_atom.get(target_loc) orelse { | |
| 207 | const target_atom_index = wasm_bin.symbol_atom.get(target_loc) orelse { | |
| 207 | 208 | return @bitCast(u32, @as(i32, -1)); |
| 208 | 209 | }; |
| 210 | const target_atom = wasm_bin.getAtom(target_atom_index); | |
| 209 | 211 | const offset: u32 = 11 + Wasm.getULEB128Size(target_atom.size); // Header (11 bytes fixed-size) + body size (leb-encoded) |
| 210 | 212 | const rel_value = @intCast(i32, target_atom.offset + offset) + relocation.addend; |
| 211 | 213 | return @intCast(u32, rel_value); |
src/link/Wasm/Object.zig+5-10| ... | ... | @@ -901,14 +901,9 @@ pub fn parseIntoAtoms(object: *Object, gpa: Allocator, object_index: u16, wasm_b |
| 901 | 901 | continue; // found unknown section, so skip parsing into atom as we do not know how to handle it. |
| 902 | 902 | }; |
| 903 | 903 | |
| 904 | const atom = try gpa.create(Atom); | |
| 904 | const atom_index = @intCast(Atom.Index, wasm_bin.managed_atoms.items.len); | |
| 905 | const atom = try wasm_bin.managed_atoms.addOne(gpa); | |
| 905 | 906 | atom.* = Atom.empty; |
| 906 | errdefer { | |
| 907 | atom.deinit(gpa); | |
| 908 | gpa.destroy(atom); | |
| 909 | } | |
| 910 | ||
| 911 | try wasm_bin.managed_atoms.append(gpa, atom); | |
| 912 | 907 | atom.file = object_index; |
| 913 | 908 | atom.size = relocatable_data.size; |
| 914 | 909 | atom.alignment = relocatable_data.getAlignment(object); |
| ... | ... | @@ -938,12 +933,12 @@ pub fn parseIntoAtoms(object: *Object, gpa: Allocator, object_index: u16, wasm_b |
| 938 | 933 | .index = relocatable_data.getIndex(), |
| 939 | 934 | })) |symbols| { |
| 940 | 935 | atom.sym_index = symbols.pop(); |
| 941 | try wasm_bin.symbol_atom.putNoClobber(gpa, atom.symbolLoc(), atom); | |
| 936 | try wasm_bin.symbol_atom.putNoClobber(gpa, atom.symbolLoc(), atom_index); | |
| 942 | 937 | |
| 943 | 938 | // symbols referencing the same atom will be added as alias |
| 944 | 939 | // or as 'parent' when they are global. |
| 945 | 940 | while (symbols.popOrNull()) |idx| { |
| 946 | try wasm_bin.symbol_atom.putNoClobber(gpa, .{ .file = atom.file, .index = idx }, atom); | |
| 941 | try wasm_bin.symbol_atom.putNoClobber(gpa, .{ .file = atom.file, .index = idx }, atom_index); | |
| 947 | 942 | const alias_symbol = object.symtable[idx]; |
| 948 | 943 | if (alias_symbol.isGlobal()) { |
| 949 | 944 | atom.sym_index = idx; |
| ... | ... | @@ -956,7 +951,7 @@ pub fn parseIntoAtoms(object: *Object, gpa: Allocator, object_index: u16, wasm_b |
| 956 | 951 | segment.alignment = std.math.max(segment.alignment, atom.alignment); |
| 957 | 952 | } |
| 958 | 953 | |
| 959 | try wasm_bin.appendAtomAtIndex(final_index, atom); | |
| 954 | try wasm_bin.appendAtomAtIndex(final_index, atom_index); | |
| 960 | 955 | log.debug("Parsed into atom: '{s}' at segment index {d}", .{ object.string_table.get(object.symtable[atom.sym_index].name), final_index }); |
| 961 | 956 | } |
| 962 | 957 | } |
src/main.zig+19-16| ... | ... | @@ -893,7 +893,7 @@ fn buildOutputType( |
| 893 | 893 | i: usize = 0, |
| 894 | 894 | fn next(it: *@This()) ?[]const u8 { |
| 895 | 895 | if (it.i >= it.args.len) { |
| 896 | if (it.resp_file) |*resp| return if (resp.next()) |sentinel| std.mem.span(sentinel) else null; | |
| 896 | if (it.resp_file) |*resp| return resp.next(); | |
| 897 | 897 | return null; |
| 898 | 898 | } |
| 899 | 899 | defer it.i += 1; |
| ... | ... | @@ -901,7 +901,7 @@ fn buildOutputType( |
| 901 | 901 | } |
| 902 | 902 | fn nextOrFatal(it: *@This()) []const u8 { |
| 903 | 903 | if (it.i >= it.args.len) { |
| 904 | if (it.resp_file) |*resp| if (resp.next()) |sentinel| return std.mem.span(sentinel); | |
| 904 | if (it.resp_file) |*resp| if (resp.next()) |ret| return ret; | |
| 905 | 905 | fatal("expected parameter after {s}", .{it.args[it.i - 1]}); |
| 906 | 906 | } |
| 907 | 907 | defer it.i += 1; |
| ... | ... | @@ -3915,6 +3915,7 @@ pub const usage_build = |
| 3915 | 3915 | ; |
| 3916 | 3916 | |
| 3917 | 3917 | pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { |
| 3918 | var color: Color = .auto; | |
| 3918 | 3919 | var prominent_compile_errors: bool = false; |
| 3919 | 3920 | |
| 3920 | 3921 | // We want to release all the locks before executing the child process, so we make a nice |
| ... | ... | @@ -4117,6 +4118,7 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi |
| 4117 | 4118 | // Here we borrow main package's table and will replace it with a fresh |
| 4118 | 4119 | // one after this process completes. |
| 4119 | 4120 | main_pkg.fetchAndAddDependencies( |
| 4121 | arena, | |
| 4120 | 4122 | &thread_pool, |
| 4121 | 4123 | &http_client, |
| 4122 | 4124 | build_directory, |
| ... | ... | @@ -4125,6 +4127,7 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi |
| 4125 | 4127 | &dependencies_source, |
| 4126 | 4128 | &build_roots_source, |
| 4127 | 4129 | "", |
| 4130 | color, | |
| 4128 | 4131 | ) catch |err| switch (err) { |
| 4129 | 4132 | error.PackageFetchFailed => process.exit(1), |
| 4130 | 4133 | else => |e| return e, |
| ... | ... | @@ -4361,12 +4364,12 @@ pub fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void |
| 4361 | 4364 | }; |
| 4362 | 4365 | defer gpa.free(source_code); |
| 4363 | 4366 | |
| 4364 | var tree = std.zig.parse(gpa, source_code) catch |err| { | |
| 4367 | var tree = Ast.parse(gpa, source_code, .zig) catch |err| { | |
| 4365 | 4368 | fatal("error parsing stdin: {}", .{err}); |
| 4366 | 4369 | }; |
| 4367 | 4370 | defer tree.deinit(gpa); |
| 4368 | 4371 | |
| 4369 | try printErrsMsgToStdErr(gpa, arena, tree.errors, tree, "<stdin>", color); | |
| 4372 | try printErrsMsgToStdErr(gpa, arena, tree, "<stdin>", color); | |
| 4370 | 4373 | var has_ast_error = false; |
| 4371 | 4374 | if (check_ast_flag) { |
| 4372 | 4375 | const Module = @import("Module.zig"); |
| ... | ... | @@ -4566,10 +4569,10 @@ fn fmtPathFile( |
| 4566 | 4569 | // Add to set after no longer possible to get error.IsDir. |
| 4567 | 4570 | if (try fmt.seen.fetchPut(stat.inode, {})) |_| return; |
| 4568 | 4571 | |
| 4569 | var tree = try std.zig.parse(fmt.gpa, source_code); | |
| 4572 | var tree = try Ast.parse(fmt.gpa, source_code, .zig); | |
| 4570 | 4573 | defer tree.deinit(fmt.gpa); |
| 4571 | 4574 | |
| 4572 | try printErrsMsgToStdErr(fmt.gpa, fmt.arena, tree.errors, tree, file_path, fmt.color); | |
| 4575 | try printErrsMsgToStdErr(fmt.gpa, fmt.arena, tree, file_path, fmt.color); | |
| 4573 | 4576 | if (tree.errors.len != 0) { |
| 4574 | 4577 | fmt.any_error = true; |
| 4575 | 4578 | return; |
| ... | ... | @@ -4649,14 +4652,14 @@ fn fmtPathFile( |
| 4649 | 4652 | } |
| 4650 | 4653 | } |
| 4651 | 4654 | |
| 4652 | fn printErrsMsgToStdErr( | |
| 4655 | pub fn printErrsMsgToStdErr( | |
| 4653 | 4656 | gpa: mem.Allocator, |
| 4654 | 4657 | arena: mem.Allocator, |
| 4655 | parse_errors: []const Ast.Error, | |
| 4656 | 4658 | tree: Ast, |
| 4657 | 4659 | path: []const u8, |
| 4658 | 4660 | color: Color, |
| 4659 | 4661 | ) !void { |
| 4662 | const parse_errors: []const Ast.Error = tree.errors; | |
| 4660 | 4663 | var i: usize = 0; |
| 4661 | 4664 | while (i < parse_errors.len) : (i += 1) { |
| 4662 | 4665 | const parse_error = parse_errors[i]; |
| ... | ... | @@ -4973,7 +4976,7 @@ pub const ClangArgIterator = struct { |
| 4973 | 4976 | // rather than an argument to a parameter. |
| 4974 | 4977 | // We adjust the len below when necessary. |
| 4975 | 4978 | self.other_args = (self.argv.ptr + self.next_index)[0..1]; |
| 4976 | var arg = mem.span(self.argv[self.next_index]); | |
| 4979 | var arg = self.argv[self.next_index]; | |
| 4977 | 4980 | self.incrementArgIndex(); |
| 4978 | 4981 | |
| 4979 | 4982 | if (mem.startsWith(u8, arg, "@")) { |
| ... | ... | @@ -5017,7 +5020,7 @@ pub const ClangArgIterator = struct { |
| 5017 | 5020 | |
| 5018 | 5021 | self.has_next = true; |
| 5019 | 5022 | self.other_args = (self.argv.ptr + self.next_index)[0..1]; // We adjust len below when necessary. |
| 5020 | arg = mem.span(self.argv[self.next_index]); | |
| 5023 | arg = self.argv[self.next_index]; | |
| 5021 | 5024 | self.incrementArgIndex(); |
| 5022 | 5025 | } |
| 5023 | 5026 | |
| ... | ... | @@ -5312,11 +5315,11 @@ pub fn cmdAstCheck( |
| 5312 | 5315 | file.pkg = try Package.create(gpa, "root", null, file.sub_file_path); |
| 5313 | 5316 | defer file.pkg.destroy(gpa); |
| 5314 | 5317 | |
| 5315 | file.tree = try std.zig.parse(gpa, file.source); | |
| 5318 | file.tree = try Ast.parse(gpa, file.source, .zig); | |
| 5316 | 5319 | file.tree_loaded = true; |
| 5317 | 5320 | defer file.tree.deinit(gpa); |
| 5318 | 5321 | |
| 5319 | try printErrsMsgToStdErr(gpa, arena, file.tree.errors, file.tree, file.sub_file_path, color); | |
| 5322 | try printErrsMsgToStdErr(gpa, arena, file.tree, file.sub_file_path, color); | |
| 5320 | 5323 | if (file.tree.errors.len != 0) { |
| 5321 | 5324 | process.exit(1); |
| 5322 | 5325 | } |
| ... | ... | @@ -5438,11 +5441,11 @@ pub fn cmdChangelist( |
| 5438 | 5441 | file.source = source; |
| 5439 | 5442 | file.source_loaded = true; |
| 5440 | 5443 | |
| 5441 | file.tree = try std.zig.parse(gpa, file.source); | |
| 5444 | file.tree = try Ast.parse(gpa, file.source, .zig); | |
| 5442 | 5445 | file.tree_loaded = true; |
| 5443 | 5446 | defer file.tree.deinit(gpa); |
| 5444 | 5447 | |
| 5445 | try printErrsMsgToStdErr(gpa, arena, file.tree.errors, file.tree, old_source_file, .auto); | |
| 5448 | try printErrsMsgToStdErr(gpa, arena, file.tree, old_source_file, .auto); | |
| 5446 | 5449 | if (file.tree.errors.len != 0) { |
| 5447 | 5450 | process.exit(1); |
| 5448 | 5451 | } |
| ... | ... | @@ -5476,10 +5479,10 @@ pub fn cmdChangelist( |
| 5476 | 5479 | if (new_amt != new_stat.size) |
| 5477 | 5480 | return error.UnexpectedEndOfFile; |
| 5478 | 5481 | |
| 5479 | var new_tree = try std.zig.parse(gpa, new_source); | |
| 5482 | var new_tree = try Ast.parse(gpa, new_source, .zig); | |
| 5480 | 5483 | defer new_tree.deinit(gpa); |
| 5481 | 5484 | |
| 5482 | try printErrsMsgToStdErr(gpa, arena, new_tree.errors, new_tree, new_source_file, .auto); | |
| 5485 | try printErrsMsgToStdErr(gpa, arena, new_tree, new_source_file, .auto); | |
| 5483 | 5486 | if (new_tree.errors.len != 0) { |
| 5484 | 5487 | process.exit(1); |
| 5485 | 5488 | } |
src/mingw.zig+1| ... | ... | @@ -106,6 +106,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { |
| 106 | 106 | .msvcrt_os_lib => { |
| 107 | 107 | const extra_flags = try arena.dupe([]const u8, &[_][]const u8{ |
| 108 | 108 | "-DHAVE_CONFIG_H", |
| 109 | "-D__LIBMSVCRT__", | |
| 109 | 110 | "-D__LIBMSVCRT_OS__", |
| 110 | 111 | |
| 111 | 112 | "-I", |
src/print_zir.zig+1| ... | ... | @@ -332,6 +332,7 @@ const Writer = struct { |
| 332 | 332 | .float_cast, |
| 333 | 333 | .int_cast, |
| 334 | 334 | .ptr_cast, |
| 335 | .qual_cast, | |
| 335 | 336 | .truncate, |
| 336 | 337 | .align_cast, |
| 337 | 338 | .div_exact, |
src/translate_c.zig+4-1| ... | ... | @@ -4519,7 +4519,10 @@ fn transCreateNodeAssign( |
| 4519 | 4519 | defer block_scope.deinit(); |
| 4520 | 4520 | |
| 4521 | 4521 | const tmp = try block_scope.makeMangledName(c, "tmp"); |
| 4522 | const rhs_node = try transExpr(c, &block_scope.base, rhs, .used); | |
| 4522 | var rhs_node = try transExpr(c, &block_scope.base, rhs, .used); | |
| 4523 | if (!exprIsBooleanType(lhs) and isBoolRes(rhs_node)) { | |
| 4524 | rhs_node = try Tag.bool_to_int.create(c.arena, rhs_node); | |
| 4525 | } | |
| 4523 | 4526 | const tmp_decl = try Tag.var_simple.create(c.arena, .{ .name = tmp, .init = rhs_node }); |
| 4524 | 4527 | try block_scope.statements.append(tmp_decl); |
| 4525 | 4528 |
src/type.zig+45-586| ... | ... | @@ -2937,24 +2937,24 @@ pub const Type = extern union { |
| 2937 | 2937 | .anyframe_T, |
| 2938 | 2938 | => return AbiAlignmentAdvanced{ .scalar = @divExact(target.cpu.arch.ptrBitWidth(), 8) }, |
| 2939 | 2939 | |
| 2940 | .c_short => return AbiAlignmentAdvanced{ .scalar = CType.short.alignment(target) }, | |
| 2941 | .c_ushort => return AbiAlignmentAdvanced{ .scalar = CType.ushort.alignment(target) }, | |
| 2942 | .c_int => return AbiAlignmentAdvanced{ .scalar = CType.int.alignment(target) }, | |
| 2943 | .c_uint => return AbiAlignmentAdvanced{ .scalar = CType.uint.alignment(target) }, | |
| 2944 | .c_long => return AbiAlignmentAdvanced{ .scalar = CType.long.alignment(target) }, | |
| 2945 | .c_ulong => return AbiAlignmentAdvanced{ .scalar = CType.ulong.alignment(target) }, | |
| 2946 | .c_longlong => return AbiAlignmentAdvanced{ .scalar = CType.longlong.alignment(target) }, | |
| 2947 | .c_ulonglong => return AbiAlignmentAdvanced{ .scalar = CType.ulonglong.alignment(target) }, | |
| 2948 | .c_longdouble => return AbiAlignmentAdvanced{ .scalar = CType.longdouble.alignment(target) }, | |
| 2940 | .c_short => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.short) }, | |
| 2941 | .c_ushort => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.ushort) }, | |
| 2942 | .c_int => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.int) }, | |
| 2943 | .c_uint => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.uint) }, | |
| 2944 | .c_long => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.long) }, | |
| 2945 | .c_ulong => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.ulong) }, | |
| 2946 | .c_longlong => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.longlong) }, | |
| 2947 | .c_ulonglong => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.ulonglong) }, | |
| 2948 | .c_longdouble => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.longdouble) }, | |
| 2949 | 2949 | |
| 2950 | 2950 | .f16 => return AbiAlignmentAdvanced{ .scalar = 2 }, |
| 2951 | .f32 => return AbiAlignmentAdvanced{ .scalar = CType.float.alignment(target) }, | |
| 2952 | .f64 => switch (CType.double.sizeInBits(target)) { | |
| 2953 | 64 => return AbiAlignmentAdvanced{ .scalar = CType.double.alignment(target) }, | |
| 2951 | .f32 => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.float) }, | |
| 2952 | .f64 => switch (target.c_type_bit_size(.double)) { | |
| 2953 | 64 => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.double) }, | |
| 2954 | 2954 | else => return AbiAlignmentAdvanced{ .scalar = 8 }, |
| 2955 | 2955 | }, |
| 2956 | .f80 => switch (CType.longdouble.sizeInBits(target)) { | |
| 2957 | 80 => return AbiAlignmentAdvanced{ .scalar = CType.longdouble.alignment(target) }, | |
| 2956 | .f80 => switch (target.c_type_bit_size(.longdouble)) { | |
| 2957 | 80 => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.longdouble) }, | |
| 2958 | 2958 | else => { |
| 2959 | 2959 | var payload: Payload.Bits = .{ |
| 2960 | 2960 | .base = .{ .tag = .int_unsigned }, |
| ... | ... | @@ -2964,8 +2964,8 @@ pub const Type = extern union { |
| 2964 | 2964 | return AbiAlignmentAdvanced{ .scalar = abiAlignment(u80_ty, target) }; |
| 2965 | 2965 | }, |
| 2966 | 2966 | }, |
| 2967 | .f128 => switch (CType.longdouble.sizeInBits(target)) { | |
| 2968 | 128 => return AbiAlignmentAdvanced{ .scalar = CType.longdouble.alignment(target) }, | |
| 2967 | .f128 => switch (target.c_type_bit_size(.longdouble)) { | |
| 2968 | 128 => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.longdouble) }, | |
| 2969 | 2969 | else => return AbiAlignmentAdvanced{ .scalar = 16 }, |
| 2970 | 2970 | }, |
| 2971 | 2971 | |
| ... | ... | @@ -3434,21 +3434,22 @@ pub const Type = extern union { |
| 3434 | 3434 | else => return AbiSizeAdvanced{ .scalar = @divExact(target.cpu.arch.ptrBitWidth(), 8) }, |
| 3435 | 3435 | }, |
| 3436 | 3436 | |
| 3437 | .c_short => return AbiSizeAdvanced{ .scalar = @divExact(CType.short.sizeInBits(target), 8) }, | |
| 3438 | .c_ushort => return AbiSizeAdvanced{ .scalar = @divExact(CType.ushort.sizeInBits(target), 8) }, | |
| 3439 | .c_int => return AbiSizeAdvanced{ .scalar = @divExact(CType.int.sizeInBits(target), 8) }, | |
| 3440 | .c_uint => return AbiSizeAdvanced{ .scalar = @divExact(CType.uint.sizeInBits(target), 8) }, | |
| 3441 | .c_long => return AbiSizeAdvanced{ .scalar = @divExact(CType.long.sizeInBits(target), 8) }, | |
| 3442 | .c_ulong => return AbiSizeAdvanced{ .scalar = @divExact(CType.ulong.sizeInBits(target), 8) }, | |
| 3443 | .c_longlong => return AbiSizeAdvanced{ .scalar = @divExact(CType.longlong.sizeInBits(target), 8) }, | |
| 3444 | .c_ulonglong => return AbiSizeAdvanced{ .scalar = @divExact(CType.ulonglong.sizeInBits(target), 8) }, | |
| 3437 | .c_short => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.short) }, | |
| 3438 | .c_ushort => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.ushort) }, | |
| 3439 | .c_int => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.int) }, | |
| 3440 | .c_uint => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.uint) }, | |
| 3441 | .c_long => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.long) }, | |
| 3442 | .c_ulong => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.ulong) }, | |
| 3443 | .c_longlong => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.longlong) }, | |
| 3444 | .c_ulonglong => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.ulonglong) }, | |
| 3445 | .c_longdouble => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.longdouble) }, | |
| 3445 | 3446 | |
| 3446 | 3447 | .f16 => return AbiSizeAdvanced{ .scalar = 2 }, |
| 3447 | 3448 | .f32 => return AbiSizeAdvanced{ .scalar = 4 }, |
| 3448 | 3449 | .f64 => return AbiSizeAdvanced{ .scalar = 8 }, |
| 3449 | 3450 | .f128 => return AbiSizeAdvanced{ .scalar = 16 }, |
| 3450 | .f80 => switch (CType.longdouble.sizeInBits(target)) { | |
| 3451 | 80 => return AbiSizeAdvanced{ .scalar = std.mem.alignForward(10, CType.longdouble.alignment(target)) }, | |
| 3451 | .f80 => switch (target.c_type_bit_size(.longdouble)) { | |
| 3452 | 80 => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.longdouble) }, | |
| 3452 | 3453 | else => { |
| 3453 | 3454 | var payload: Payload.Bits = .{ |
| 3454 | 3455 | .base = .{ .tag = .int_unsigned }, |
| ... | ... | @@ -3458,14 +3459,6 @@ pub const Type = extern union { |
| 3458 | 3459 | return AbiSizeAdvanced{ .scalar = abiSize(u80_ty, target) }; |
| 3459 | 3460 | }, |
| 3460 | 3461 | }, |
| 3461 | .c_longdouble => switch (CType.longdouble.sizeInBits(target)) { | |
| 3462 | 16 => return AbiSizeAdvanced{ .scalar = abiSize(Type.f16, target) }, | |
| 3463 | 32 => return AbiSizeAdvanced{ .scalar = abiSize(Type.f32, target) }, | |
| 3464 | 64 => return AbiSizeAdvanced{ .scalar = abiSize(Type.f64, target) }, | |
| 3465 | 80 => return AbiSizeAdvanced{ .scalar = abiSize(Type.f80, target) }, | |
| 3466 | 128 => return AbiSizeAdvanced{ .scalar = abiSize(Type.f128, target) }, | |
| 3467 | else => unreachable, | |
| 3468 | }, | |
| 3469 | 3462 | |
| 3470 | 3463 | // TODO revisit this when we have the concept of the error tag type |
| 3471 | 3464 | .anyerror_void_error_union, |
| ... | ... | @@ -3748,15 +3741,15 @@ pub const Type = extern union { |
| 3748 | 3741 | .manyptr_const_u8_sentinel_0, |
| 3749 | 3742 | => return target.cpu.arch.ptrBitWidth(), |
| 3750 | 3743 | |
| 3751 | .c_short => return CType.short.sizeInBits(target), | |
| 3752 | .c_ushort => return CType.ushort.sizeInBits(target), | |
| 3753 | .c_int => return CType.int.sizeInBits(target), | |
| 3754 | .c_uint => return CType.uint.sizeInBits(target), | |
| 3755 | .c_long => return CType.long.sizeInBits(target), | |
| 3756 | .c_ulong => return CType.ulong.sizeInBits(target), | |
| 3757 | .c_longlong => return CType.longlong.sizeInBits(target), | |
| 3758 | .c_ulonglong => return CType.ulonglong.sizeInBits(target), | |
| 3759 | .c_longdouble => return CType.longdouble.sizeInBits(target), | |
| 3744 | .c_short => return target.c_type_bit_size(.short), | |
| 3745 | .c_ushort => return target.c_type_bit_size(.ushort), | |
| 3746 | .c_int => return target.c_type_bit_size(.int), | |
| 3747 | .c_uint => return target.c_type_bit_size(.uint), | |
| 3748 | .c_long => return target.c_type_bit_size(.long), | |
| 3749 | .c_ulong => return target.c_type_bit_size(.ulong), | |
| 3750 | .c_longlong => return target.c_type_bit_size(.longlong), | |
| 3751 | .c_ulonglong => return target.c_type_bit_size(.ulonglong), | |
| 3752 | .c_longdouble => return target.c_type_bit_size(.longdouble), | |
| 3760 | 3753 | |
| 3761 | 3754 | .error_set, |
| 3762 | 3755 | .error_set_single, |
| ... | ... | @@ -4631,14 +4624,14 @@ pub const Type = extern union { |
| 4631 | 4624 | .i128 => return .{ .signedness = .signed, .bits = 128 }, |
| 4632 | 4625 | .usize => return .{ .signedness = .unsigned, .bits = target.cpu.arch.ptrBitWidth() }, |
| 4633 | 4626 | .isize => return .{ .signedness = .signed, .bits = target.cpu.arch.ptrBitWidth() }, |
| 4634 | .c_short => return .{ .signedness = .signed, .bits = CType.short.sizeInBits(target) }, | |
| 4635 | .c_ushort => return .{ .signedness = .unsigned, .bits = CType.ushort.sizeInBits(target) }, | |
| 4636 | .c_int => return .{ .signedness = .signed, .bits = CType.int.sizeInBits(target) }, | |
| 4637 | .c_uint => return .{ .signedness = .unsigned, .bits = CType.uint.sizeInBits(target) }, | |
| 4638 | .c_long => return .{ .signedness = .signed, .bits = CType.long.sizeInBits(target) }, | |
| 4639 | .c_ulong => return .{ .signedness = .unsigned, .bits = CType.ulong.sizeInBits(target) }, | |
| 4640 | .c_longlong => return .{ .signedness = .signed, .bits = CType.longlong.sizeInBits(target) }, | |
| 4641 | .c_ulonglong => return .{ .signedness = .unsigned, .bits = CType.ulonglong.sizeInBits(target) }, | |
| 4627 | .c_short => return .{ .signedness = .signed, .bits = target.c_type_bit_size(.short) }, | |
| 4628 | .c_ushort => return .{ .signedness = .unsigned, .bits = target.c_type_bit_size(.ushort) }, | |
| 4629 | .c_int => return .{ .signedness = .signed, .bits = target.c_type_bit_size(.int) }, | |
| 4630 | .c_uint => return .{ .signedness = .unsigned, .bits = target.c_type_bit_size(.uint) }, | |
| 4631 | .c_long => return .{ .signedness = .signed, .bits = target.c_type_bit_size(.long) }, | |
| 4632 | .c_ulong => return .{ .signedness = .unsigned, .bits = target.c_type_bit_size(.ulong) }, | |
| 4633 | .c_longlong => return .{ .signedness = .signed, .bits = target.c_type_bit_size(.longlong) }, | |
| 4634 | .c_ulonglong => return .{ .signedness = .unsigned, .bits = target.c_type_bit_size(.ulonglong) }, | |
| 4642 | 4635 | |
| 4643 | 4636 | .enum_full, .enum_nonexhaustive => ty = ty.cast(Payload.EnumFull).?.data.tag_ty, |
| 4644 | 4637 | .enum_numbered => ty = ty.castTag(.enum_numbered).?.data.tag_ty, |
| ... | ... | @@ -4724,7 +4717,7 @@ pub const Type = extern union { |
| 4724 | 4717 | .f64 => 64, |
| 4725 | 4718 | .f80 => 80, |
| 4726 | 4719 | .f128, .comptime_float => 128, |
| 4727 | .c_longdouble => CType.longdouble.sizeInBits(target), | |
| 4720 | .c_longdouble => target.c_type_bit_size(.longdouble), | |
| 4728 | 4721 | |
| 4729 | 4722 | else => unreachable, |
| 4730 | 4723 | }; |
| ... | ... | @@ -6689,537 +6682,3 @@ pub const Type = extern union { |
| 6689 | 6682 | /// to packed struct layout to find out all the places in the codebase you need to edit! |
| 6690 | 6683 | pub const packed_struct_layout_version = 2; |
| 6691 | 6684 | }; |
| 6692 | ||
| 6693 | pub const CType = enum { | |
| 6694 | short, | |
| 6695 | ushort, | |
| 6696 | int, | |
| 6697 | uint, | |
| 6698 | long, | |
| 6699 | ulong, | |
| 6700 | longlong, | |
| 6701 | ulonglong, | |
| 6702 | longdouble, | |
| 6703 | ||
| 6704 | // We don't have a `c_float`/`c_double` type in Zig, but these | |
| 6705 | // are useful for querying target-correct alignment and checking | |
| 6706 | // whether C's double is f64 or f32 | |
| 6707 | float, | |
| 6708 | double, | |
| 6709 | ||
| 6710 | pub fn sizeInBits(self: CType, target: Target) u16 { | |
| 6711 | switch (target.os.tag) { | |
| 6712 | .freestanding, .other => switch (target.cpu.arch) { | |
| 6713 | .msp430 => switch (self) { | |
| 6714 | .short, .ushort, .int, .uint => return 16, | |
| 6715 | .float, .long, .ulong => return 32, | |
| 6716 | .longlong, .ulonglong, .double, .longdouble => return 64, | |
| 6717 | }, | |
| 6718 | .avr => switch (self) { | |
| 6719 | .short, .ushort, .int, .uint => return 16, | |
| 6720 | .long, .ulong, .float, .double, .longdouble => return 32, | |
| 6721 | .longlong, .ulonglong => return 64, | |
| 6722 | }, | |
| 6723 | .tce, .tcele => switch (self) { | |
| 6724 | .short, .ushort => return 16, | |
| 6725 | .int, .uint, .long, .ulong, .longlong, .ulonglong => return 32, | |
| 6726 | .float, .double, .longdouble => return 32, | |
| 6727 | }, | |
| 6728 | .mips64, .mips64el => switch (self) { | |
| 6729 | .short, .ushort => return 16, | |
| 6730 | .int, .uint, .float => return 32, | |
| 6731 | .long, .ulong => return if (target.abi != .gnuabin32) 64 else 32, | |
| 6732 | .longlong, .ulonglong, .double => return 64, | |
| 6733 | .longdouble => return 128, | |
| 6734 | }, | |
| 6735 | .x86_64 => switch (self) { | |
| 6736 | .short, .ushort => return 16, | |
| 6737 | .int, .uint, .float => return 32, | |
| 6738 | .long, .ulong => switch (target.abi) { | |
| 6739 | .gnux32, .muslx32 => return 32, | |
| 6740 | else => return 64, | |
| 6741 | }, | |
| 6742 | .longlong, .ulonglong, .double => return 64, | |
| 6743 | .longdouble => return 80, | |
| 6744 | }, | |
| 6745 | else => switch (self) { | |
| 6746 | .short, .ushort => return 16, | |
| 6747 | .int, .uint, .float => return 32, | |
| 6748 | .long, .ulong => return target.cpu.arch.ptrBitWidth(), | |
| 6749 | .longlong, .ulonglong, .double => return 64, | |
| 6750 | .longdouble => switch (target.cpu.arch) { | |
| 6751 | .x86 => switch (target.abi) { | |
| 6752 | .android => return 64, | |
| 6753 | else => return 80, | |
| 6754 | }, | |
| 6755 | ||
| 6756 | .powerpc, | |
| 6757 | .powerpcle, | |
| 6758 | .powerpc64, | |
| 6759 | .powerpc64le, | |
| 6760 | => switch (target.abi) { | |
| 6761 | .musl, | |
| 6762 | .musleabi, | |
| 6763 | .musleabihf, | |
| 6764 | .muslx32, | |
| 6765 | => return 64, | |
| 6766 | else => return 128, | |
| 6767 | }, | |
| 6768 | ||
| 6769 | .riscv32, | |
| 6770 | .riscv64, | |
| 6771 | .aarch64, | |
| 6772 | .aarch64_be, | |
| 6773 | .aarch64_32, | |
| 6774 | .s390x, | |
| 6775 | .sparc, | |
| 6776 | .sparc64, | |
| 6777 | .sparcel, | |
| 6778 | .wasm32, | |
| 6779 | .wasm64, | |
| 6780 | => return 128, | |
| 6781 | ||
| 6782 | else => return 64, | |
| 6783 | }, | |
| 6784 | }, | |
| 6785 | }, | |
| 6786 | ||
| 6787 | .linux, | |
| 6788 | .freebsd, | |
| 6789 | .netbsd, | |
| 6790 | .dragonfly, | |
| 6791 | .openbsd, | |
| 6792 | .wasi, | |
| 6793 | .emscripten, | |
| 6794 | .plan9, | |
| 6795 | .solaris, | |
| 6796 | .haiku, | |
| 6797 | .ananas, | |
| 6798 | .fuchsia, | |
| 6799 | .minix, | |
| 6800 | => switch (target.cpu.arch) { | |
| 6801 | .msp430 => switch (self) { | |
| 6802 | .short, .ushort, .int, .uint => return 16, | |
| 6803 | .long, .ulong, .float => return 32, | |
| 6804 | .longlong, .ulonglong, .double, .longdouble => return 64, | |
| 6805 | }, | |
| 6806 | .avr => switch (self) { | |
| 6807 | .short, .ushort, .int, .uint => return 16, | |
| 6808 | .long, .ulong, .float, .double, .longdouble => return 32, | |
| 6809 | .longlong, .ulonglong => return 64, | |
| 6810 | }, | |
| 6811 | .tce, .tcele => switch (self) { | |
| 6812 | .short, .ushort => return 16, | |
| 6813 | .int, .uint, .long, .ulong, .longlong, .ulonglong => return 32, | |
| 6814 | .float, .double, .longdouble => return 32, | |
| 6815 | }, | |
| 6816 | .mips64, .mips64el => switch (self) { | |
| 6817 | .short, .ushort => return 16, | |
| 6818 | .int, .uint, .float => return 32, | |
| 6819 | .long, .ulong => return if (target.abi != .gnuabin32) 64 else 32, | |
| 6820 | .longlong, .ulonglong, .double => return 64, | |
| 6821 | .longdouble => if (target.os.tag == .freebsd) return 64 else return 128, | |
| 6822 | }, | |
| 6823 | .x86_64 => switch (self) { | |
| 6824 | .short, .ushort => return 16, | |
| 6825 | .int, .uint, .float => return 32, | |
| 6826 | .long, .ulong => switch (target.abi) { | |
| 6827 | .gnux32, .muslx32 => return 32, | |
| 6828 | else => return 64, | |
| 6829 | }, | |
| 6830 | .longlong, .ulonglong, .double => return 64, | |
| 6831 | .longdouble => return 80, | |
| 6832 | }, | |
| 6833 | else => switch (self) { | |
| 6834 | .short, .ushort => return 16, | |
| 6835 | .int, .uint, .float => return 32, | |
| 6836 | .long, .ulong => return target.cpu.arch.ptrBitWidth(), | |
| 6837 | .longlong, .ulonglong, .double => return 64, | |
| 6838 | .longdouble => switch (target.cpu.arch) { | |
| 6839 | .x86 => switch (target.abi) { | |
| 6840 | .android => return 64, | |
| 6841 | else => return 80, | |
| 6842 | }, | |
| 6843 | ||
| 6844 | .powerpc, | |
| 6845 | .powerpcle, | |
| 6846 | => switch (target.abi) { | |
| 6847 | .musl, | |
| 6848 | .musleabi, | |
| 6849 | .musleabihf, | |
| 6850 | .muslx32, | |
| 6851 | => return 64, | |
| 6852 | else => switch (target.os.tag) { | |
| 6853 | .freebsd, .netbsd, .openbsd => return 64, | |
| 6854 | else => return 128, | |
| 6855 | }, | |
| 6856 | }, | |
| 6857 | ||
| 6858 | .powerpc64, | |
| 6859 | .powerpc64le, | |
| 6860 | => switch (target.abi) { | |
| 6861 | .musl, | |
| 6862 | .musleabi, | |
| 6863 | .musleabihf, | |
| 6864 | .muslx32, | |
| 6865 | => return 64, | |
| 6866 | else => switch (target.os.tag) { | |
| 6867 | .freebsd, .openbsd => return 64, | |
| 6868 | else => return 128, | |
| 6869 | }, | |
| 6870 | }, | |
| 6871 | ||
| 6872 | .riscv32, | |
| 6873 | .riscv64, | |
| 6874 | .aarch64, | |
| 6875 | .aarch64_be, | |
| 6876 | .aarch64_32, | |
| 6877 | .s390x, | |
| 6878 | .mips64, | |
| 6879 | .mips64el, | |
| 6880 | .sparc, | |
| 6881 | .sparc64, | |
| 6882 | .sparcel, | |
| 6883 | .wasm32, | |
| 6884 | .wasm64, | |
| 6885 | => return 128, | |
| 6886 | ||
| 6887 | else => return 64, | |
| 6888 | }, | |
| 6889 | }, | |
| 6890 | }, | |
| 6891 | ||
| 6892 | .windows, .uefi => switch (target.cpu.arch) { | |
| 6893 | .x86 => switch (self) { | |
| 6894 | .short, .ushort => return 16, | |
| 6895 | .int, .uint, .float => return 32, | |
| 6896 | .long, .ulong => return 32, | |
| 6897 | .longlong, .ulonglong, .double => return 64, | |
| 6898 | .longdouble => switch (target.abi) { | |
| 6899 | .gnu, .gnuilp32, .cygnus => return 80, | |
| 6900 | else => return 64, | |
| 6901 | }, | |
| 6902 | }, | |
| 6903 | .x86_64 => switch (self) { | |
| 6904 | .short, .ushort => return 16, | |
| 6905 | .int, .uint, .float => return 32, | |
| 6906 | .long, .ulong => switch (target.abi) { | |
| 6907 | .cygnus => return 64, | |
| 6908 | else => return 32, | |
| 6909 | }, | |
| 6910 | .longlong, .ulonglong, .double => return 64, | |
| 6911 | .longdouble => switch (target.abi) { | |
| 6912 | .gnu, .gnuilp32, .cygnus => return 80, | |
| 6913 | else => return 64, | |
| 6914 | }, | |
| 6915 | }, | |
| 6916 | else => switch (self) { | |
| 6917 | .short, .ushort => return 16, | |
| 6918 | .int, .uint, .float => return 32, | |
| 6919 | .long, .ulong => return 32, | |
| 6920 | .longlong, .ulonglong, .double => return 64, | |
| 6921 | .longdouble => return 64, | |
| 6922 | }, | |
| 6923 | }, | |
| 6924 | ||
| 6925 | .macos, .ios, .tvos, .watchos => switch (self) { | |
| 6926 | .short, .ushort => return 16, | |
| 6927 | .int, .uint, .float => return 32, | |
| 6928 | .long, .ulong => switch (target.cpu.arch) { | |
| 6929 | .x86, .arm, .aarch64_32 => return 32, | |
| 6930 | .x86_64 => switch (target.abi) { | |
| 6931 | .gnux32, .muslx32 => return 32, | |
| 6932 | else => return 64, | |
| 6933 | }, | |
| 6934 | else => return 64, | |
| 6935 | }, | |
| 6936 | .longlong, .ulonglong, .double => return 64, | |
| 6937 | .longdouble => switch (target.cpu.arch) { | |
| 6938 | .x86 => switch (target.abi) { | |
| 6939 | .android => return 64, | |
| 6940 | else => return 80, | |
| 6941 | }, | |
| 6942 | .x86_64 => return 80, | |
| 6943 | else => return 64, | |
| 6944 | }, | |
| 6945 | }, | |
| 6946 | ||
| 6947 | .nvcl, .cuda => switch (self) { | |
| 6948 | .short, .ushort => return 16, | |
| 6949 | .int, .uint, .float => return 32, | |
| 6950 | .long, .ulong => switch (target.cpu.arch) { | |
| 6951 | .nvptx => return 32, | |
| 6952 | .nvptx64 => return 64, | |
| 6953 | else => return 64, | |
| 6954 | }, | |
| 6955 | .longlong, .ulonglong, .double => return 64, | |
| 6956 | .longdouble => return 64, | |
| 6957 | }, | |
| 6958 | ||
| 6959 | .amdhsa, .amdpal => switch (self) { | |
| 6960 | .short, .ushort => return 16, | |
| 6961 | .int, .uint, .float => return 32, | |
| 6962 | .long, .ulong, .longlong, .ulonglong, .double => return 64, | |
| 6963 | .longdouble => return 128, | |
| 6964 | }, | |
| 6965 | ||
| 6966 | .cloudabi, | |
| 6967 | .kfreebsd, | |
| 6968 | .lv2, | |
| 6969 | .zos, | |
| 6970 | .rtems, | |
| 6971 | .nacl, | |
| 6972 | .aix, | |
| 6973 | .ps4, | |
| 6974 | .ps5, | |
| 6975 | .elfiamcu, | |
| 6976 | .mesa3d, | |
| 6977 | .contiki, | |
| 6978 | .hermit, | |
| 6979 | .hurd, | |
| 6980 | .opencl, | |
| 6981 | .glsl450, | |
| 6982 | .vulkan, | |
| 6983 | .driverkit, | |
| 6984 | .shadermodel, | |
| 6985 | => @panic("TODO specify the C integer and float type sizes for this OS"), | |
| 6986 | } | |
| 6987 | } | |
| 6988 | ||
| 6989 | pub fn alignment(self: CType, target: Target) u16 { | |
| 6990 | ||
| 6991 | // Overrides for unusual alignments | |
| 6992 | switch (target.cpu.arch) { | |
| 6993 | .avr => switch (self) { | |
| 6994 | .short, .ushort => return 2, | |
| 6995 | else => return 1, | |
| 6996 | }, | |
| 6997 | .x86 => switch (target.os.tag) { | |
| 6998 | .windows, .uefi => switch (self) { | |
| 6999 | .longlong, .ulonglong, .double => return 8, | |
| 7000 | .longdouble => switch (target.abi) { | |
| 7001 | .gnu, .gnuilp32, .cygnus => return 4, | |
| 7002 | else => return 8, | |
| 7003 | }, | |
| 7004 | else => {}, | |
| 7005 | }, | |
| 7006 | else => {}, | |
| 7007 | }, | |
| 7008 | else => {}, | |
| 7009 | } | |
| 7010 | ||
| 7011 | // Next-power-of-two-aligned, up to a maximum. | |
| 7012 | return @min( | |
| 7013 | std.math.ceilPowerOfTwoAssert(u16, (self.sizeInBits(target) + 7) / 8), | |
| 7014 | switch (target.cpu.arch) { | |
| 7015 | .arm, .armeb, .thumb, .thumbeb => switch (target.os.tag) { | |
| 7016 | .netbsd => switch (target.abi) { | |
| 7017 | .gnueabi, | |
| 7018 | .gnueabihf, | |
| 7019 | .eabi, | |
| 7020 | .eabihf, | |
| 7021 | .android, | |
| 7022 | .musleabi, | |
| 7023 | .musleabihf, | |
| 7024 | => 8, | |
| 7025 | ||
| 7026 | else => @as(u16, 4), | |
| 7027 | }, | |
| 7028 | .ios, .tvos, .watchos => 4, | |
| 7029 | else => 8, | |
| 7030 | }, | |
| 7031 | ||
| 7032 | .msp430, | |
| 7033 | .avr, | |
| 7034 | => 2, | |
| 7035 | ||
| 7036 | .arc, | |
| 7037 | .csky, | |
| 7038 | .x86, | |
| 7039 | .xcore, | |
| 7040 | .dxil, | |
| 7041 | .loongarch32, | |
| 7042 | .tce, | |
| 7043 | .tcele, | |
| 7044 | .le32, | |
| 7045 | .amdil, | |
| 7046 | .hsail, | |
| 7047 | .spir, | |
| 7048 | .spirv32, | |
| 7049 | .kalimba, | |
| 7050 | .shave, | |
| 7051 | .renderscript32, | |
| 7052 | .ve, | |
| 7053 | .spu_2, | |
| 7054 | .xtensa, | |
| 7055 | => 4, | |
| 7056 | ||
| 7057 | .aarch64_32, | |
| 7058 | .amdgcn, | |
| 7059 | .amdil64, | |
| 7060 | .bpfel, | |
| 7061 | .bpfeb, | |
| 7062 | .hexagon, | |
| 7063 | .hsail64, | |
| 7064 | .loongarch64, | |
| 7065 | .m68k, | |
| 7066 | .mips, | |
| 7067 | .mipsel, | |
| 7068 | .sparc, | |
| 7069 | .sparcel, | |
| 7070 | .sparc64, | |
| 7071 | .lanai, | |
| 7072 | .le64, | |
| 7073 | .nvptx, | |
| 7074 | .nvptx64, | |
| 7075 | .r600, | |
| 7076 | .s390x, | |
| 7077 | .spir64, | |
| 7078 | .spirv64, | |
| 7079 | .renderscript64, | |
| 7080 | => 8, | |
| 7081 | ||
| 7082 | .aarch64, | |
| 7083 | .aarch64_be, | |
| 7084 | .mips64, | |
| 7085 | .mips64el, | |
| 7086 | .powerpc, | |
| 7087 | .powerpcle, | |
| 7088 | .powerpc64, | |
| 7089 | .powerpc64le, | |
| 7090 | .riscv32, | |
| 7091 | .riscv64, | |
| 7092 | .x86_64, | |
| 7093 | .wasm32, | |
| 7094 | .wasm64, | |
| 7095 | => 16, | |
| 7096 | }, | |
| 7097 | ); | |
| 7098 | } | |
| 7099 | ||
| 7100 | pub fn preferredAlignment(self: CType, target: Target) u16 { | |
| 7101 | ||
| 7102 | // Overrides for unusual alignments | |
| 7103 | switch (target.cpu.arch) { | |
| 7104 | .arm, .armeb, .thumb, .thumbeb => switch (target.os.tag) { | |
| 7105 | .netbsd => switch (target.abi) { | |
| 7106 | .gnueabi, | |
| 7107 | .gnueabihf, | |
| 7108 | .eabi, | |
| 7109 | .eabihf, | |
| 7110 | .android, | |
| 7111 | .musleabi, | |
| 7112 | .musleabihf, | |
| 7113 | => {}, | |
| 7114 | ||
| 7115 | else => switch (self) { | |
| 7116 | .longdouble => return 4, | |
| 7117 | else => {}, | |
| 7118 | }, | |
| 7119 | }, | |
| 7120 | .ios, .tvos, .watchos => switch (self) { | |
| 7121 | .longdouble => return 4, | |
| 7122 | else => {}, | |
| 7123 | }, | |
| 7124 | else => {}, | |
| 7125 | }, | |
| 7126 | .arc => switch (self) { | |
| 7127 | .longdouble => return 4, | |
| 7128 | else => {}, | |
| 7129 | }, | |
| 7130 | .avr => switch (self) { | |
| 7131 | .int, .uint, .long, .ulong, .float, .longdouble => return 1, | |
| 7132 | .short, .ushort => return 2, | |
| 7133 | .double => return 4, | |
| 7134 | .longlong, .ulonglong => return 8, | |
| 7135 | }, | |
| 7136 | .x86 => switch (target.os.tag) { | |
| 7137 | .windows, .uefi => switch (self) { | |
| 7138 | .longdouble => switch (target.abi) { | |
| 7139 | .gnu, .gnuilp32, .cygnus => return 4, | |
| 7140 | else => return 8, | |
| 7141 | }, | |
| 7142 | else => {}, | |
| 7143 | }, | |
| 7144 | else => switch (self) { | |
| 7145 | .longdouble => return 4, | |
| 7146 | else => {}, | |
| 7147 | }, | |
| 7148 | }, | |
| 7149 | else => {}, | |
| 7150 | } | |
| 7151 | ||
| 7152 | // Next-power-of-two-aligned, up to a maximum. | |
| 7153 | return @min( | |
| 7154 | std.math.ceilPowerOfTwoAssert(u16, (self.sizeInBits(target) + 7) / 8), | |
| 7155 | switch (target.cpu.arch) { | |
| 7156 | .msp430 => @as(u16, 2), | |
| 7157 | ||
| 7158 | .csky, | |
| 7159 | .xcore, | |
| 7160 | .dxil, | |
| 7161 | .loongarch32, | |
| 7162 | .tce, | |
| 7163 | .tcele, | |
| 7164 | .le32, | |
| 7165 | .amdil, | |
| 7166 | .hsail, | |
| 7167 | .spir, | |
| 7168 | .spirv32, | |
| 7169 | .kalimba, | |
| 7170 | .shave, | |
| 7171 | .renderscript32, | |
| 7172 | .ve, | |
| 7173 | .spu_2, | |
| 7174 | => 4, | |
| 7175 | ||
| 7176 | .arc, | |
| 7177 | .arm, | |
| 7178 | .armeb, | |
| 7179 | .avr, | |
| 7180 | .thumb, | |
| 7181 | .thumbeb, | |
| 7182 | .aarch64_32, | |
| 7183 | .amdgcn, | |
| 7184 | .amdil64, | |
| 7185 | .bpfel, | |
| 7186 | .bpfeb, | |
| 7187 | .hexagon, | |
| 7188 | .hsail64, | |
| 7189 | .x86, | |
| 7190 | .loongarch64, | |
| 7191 | .m68k, | |
| 7192 | .mips, | |
| 7193 | .mipsel, | |
| 7194 | .sparc, | |
| 7195 | .sparcel, | |
| 7196 | .sparc64, | |
| 7197 | .lanai, | |
| 7198 | .le64, | |
| 7199 | .nvptx, | |
| 7200 | .nvptx64, | |
| 7201 | .r600, | |
| 7202 | .s390x, | |
| 7203 | .spir64, | |
| 7204 | .spirv64, | |
| 7205 | .renderscript64, | |
| 7206 | => 8, | |
| 7207 | ||
| 7208 | .aarch64, | |
| 7209 | .aarch64_be, | |
| 7210 | .mips64, | |
| 7211 | .mips64el, | |
| 7212 | .powerpc, | |
| 7213 | .powerpcle, | |
| 7214 | .powerpc64, | |
| 7215 | .powerpc64le, | |
| 7216 | .riscv32, | |
| 7217 | .riscv64, | |
| 7218 | .x86_64, | |
| 7219 | .wasm32, | |
| 7220 | .wasm64, | |
| 7221 | => 16, | |
| 7222 | }, | |
| 7223 | ); | |
| 7224 | } | |
| 7225 | }; |
stage1/zig1.wasm| Binary files a/stage1/zig1.wasm and b/stage1/zig1.wasm differ |
test/behavior/basic.zig+19-1| ... | ... | @@ -703,7 +703,7 @@ test "string concatenation" { |
| 703 | 703 | comptime try expect(@TypeOf(a) == *const [12:0]u8); |
| 704 | 704 | comptime try expect(@TypeOf(b) == *const [12:0]u8); |
| 705 | 705 | |
| 706 | const len = mem.len(b); | |
| 706 | const len = b.len; | |
| 707 | 707 | const len_with_null = len + 1; |
| 708 | 708 | { |
| 709 | 709 | var i: u32 = 0; |
| ... | ... | @@ -1125,3 +1125,21 @@ test "returning an opaque type from a function" { |
| 1125 | 1125 | }; |
| 1126 | 1126 | try expect(S.foo(123).b == 123); |
| 1127 | 1127 | } |
| 1128 | ||
| 1129 | test "orelse coercion as function argument" { | |
| 1130 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | |
| 1131 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 1132 | ||
| 1133 | const Loc = struct { start: i32 = -1 }; | |
| 1134 | const Container = struct { | |
| 1135 | a: ?Loc = null, | |
| 1136 | fn init(a: Loc) @This() { | |
| 1137 | return .{ | |
| 1138 | .a = a, | |
| 1139 | }; | |
| 1140 | } | |
| 1141 | }; | |
| 1142 | var optional: ?Loc = .{}; | |
| 1143 | var foo = Container.init(optional orelse .{}); | |
| 1144 | try expect(foo.a.?.start == -1); | |
| 1145 | } |
test/behavior/cast.zig-3| ... | ... | @@ -1179,7 +1179,6 @@ fn peerTypeEmptyArrayAndSlice(a: bool, slice: []const u8) []const u8 { |
| 1179 | 1179 | test "implicitly cast from [N]T to ?[]const T" { |
| 1180 | 1180 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 1181 | 1181 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 1182 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | |
| 1183 | 1182 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 1184 | 1183 | |
| 1185 | 1184 | try expect(mem.eql(u8, castToOptionalSlice().?, "hi")); |
| ... | ... | @@ -1264,7 +1263,6 @@ test "cast from array reference to fn: runtime fn ptr" { |
| 1264 | 1263 | test "*const [N]null u8 to ?[]const u8" { |
| 1265 | 1264 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 1266 | 1265 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 1267 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | |
| 1268 | 1266 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 1269 | 1267 | |
| 1270 | 1268 | const S = struct { |
| ... | ... | @@ -1413,7 +1411,6 @@ test "cast i8 fn call peers to i32 result" { |
| 1413 | 1411 | test "cast compatible optional types" { |
| 1414 | 1412 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 1415 | 1413 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 1416 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | |
| 1417 | 1414 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 1418 | 1415 | |
| 1419 | 1416 | var a: ?[:0]const u8 = null; |
test/behavior/error.zig+15| ... | ... | @@ -896,3 +896,18 @@ test "optional error union return type" { |
| 896 | 896 | }; |
| 897 | 897 | try expect(1234 == try S.foo().?); |
| 898 | 898 | } |
| 899 | ||
| 900 | test "optional error set return type" { | |
| 901 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 902 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | |
| 903 | ||
| 904 | const E = error{ A, B }; | |
| 905 | const S = struct { | |
| 906 | fn foo(return_null: bool) ?E { | |
| 907 | return if (return_null) null else E.A; | |
| 908 | } | |
| 909 | }; | |
| 910 | ||
| 911 | try expect(null == S.foo(true)); | |
| 912 | try expect(E.A == S.foo(false).?); | |
| 913 | } |
test/behavior/math.zig-3| ... | ... | @@ -1332,7 +1332,6 @@ test "float remainder division using @rem" { |
| 1332 | 1332 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 1333 | 1333 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 1334 | 1334 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 1335 | if (builtin.zig_backend == .stage2_llvm and builtin.os.tag == .windows) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/12602 | |
| 1336 | 1335 | |
| 1337 | 1336 | comptime try frem(f16); |
| 1338 | 1337 | comptime try frem(f32); |
| ... | ... | @@ -1375,7 +1374,6 @@ test "float modulo division using @mod" { |
| 1375 | 1374 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 1376 | 1375 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 1377 | 1376 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 1378 | if (builtin.zig_backend == .stage2_llvm and builtin.os.tag == .windows) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/12602 | |
| 1379 | 1377 | |
| 1380 | 1378 | comptime try fmod(f16); |
| 1381 | 1379 | comptime try fmod(f32); |
| ... | ... | @@ -1438,7 +1436,6 @@ test "@round f80" { |
| 1438 | 1436 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 1439 | 1437 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 1440 | 1438 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 1441 | if (builtin.zig_backend == .stage2_llvm and builtin.os.tag == .windows) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/12602 | |
| 1442 | 1439 | |
| 1443 | 1440 | try testRound(f80, 12.0); |
| 1444 | 1441 | comptime try testRound(f80, 12.0); |
test/behavior/muladd.zig-2| ... | ... | @@ -50,7 +50,6 @@ test "@mulAdd f80" { |
| 50 | 50 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 51 | 51 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 52 | 52 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 53 | if (builtin.zig_backend == .stage2_llvm and builtin.os.tag == .windows) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/12602 | |
| 54 | 53 | |
| 55 | 54 | comptime try testMulAdd80(); |
| 56 | 55 | try testMulAdd80(); |
| ... | ... | @@ -178,7 +177,6 @@ test "vector f80" { |
| 178 | 177 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 179 | 178 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 180 | 179 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 181 | if (builtin.zig_backend == .stage2_llvm and builtin.os.tag == .windows) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/12602 | |
| 182 | 180 | |
| 183 | 181 | comptime try vector80(); |
| 184 | 182 | try vector80(); |
test/behavior/optional.zig-2| ... | ... | @@ -439,7 +439,6 @@ test "Optional slice size is optimized" { |
| 439 | 439 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 440 | 440 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 441 | 441 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; |
| 442 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; | |
| 443 | 442 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 444 | 443 | |
| 445 | 444 | try expect(@sizeOf(?[]u8) == @sizeOf([]u8)); |
| ... | ... | @@ -479,7 +478,6 @@ test "cast slice to const slice nested in error union and optional" { |
| 479 | 478 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 480 | 479 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 481 | 480 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; |
| 482 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; | |
| 483 | 481 | |
| 484 | 482 | const S = struct { |
| 485 | 483 | fn inner() !?[]u8 { |
test/behavior/sizeof_and_typeof.zig+9| ... | ... | @@ -292,3 +292,12 @@ test "@sizeOf optional of previously unresolved union" { |
| 292 | 292 | const Node = union { a: usize }; |
| 293 | 293 | try expect(@sizeOf(?Node) == @sizeOf(Node) + @alignOf(Node)); |
| 294 | 294 | } |
| 295 | ||
| 296 | test "@offsetOf zero-bit field" { | |
| 297 | const S = packed struct { | |
| 298 | a: u32, | |
| 299 | b: u0, | |
| 300 | c: u32, | |
| 301 | }; | |
| 302 | try expect(@offsetOf(S, "b") == @offsetOf(S, "c")); | |
| 303 | } |
test/behavior/threadlocal.zig+12-6| ... | ... | @@ -7,8 +7,10 @@ test "thread local variable" { |
| 7 | 7 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 8 | 8 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 9 | 9 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 10 | if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch != .x86_64) return error.SkipZigTest; // TODO | |
| 11 | if (builtin.zig_backend == .stage2_c and builtin.os.tag == .windows) return error.SkipZigTest; // TODO | |
| 10 | if (builtin.zig_backend == .stage2_llvm) switch (builtin.cpu.arch) { | |
| 11 | .x86_64, .x86 => {}, | |
| 12 | else => return error.SkipZigTest, | |
| 13 | }; // TODO | |
| 12 | 14 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 13 | 15 | |
| 14 | 16 | const S = struct { |
| ... | ... | @@ -23,8 +25,10 @@ test "pointer to thread local array" { |
| 23 | 25 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 24 | 26 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 25 | 27 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 26 | if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch != .x86_64) return error.SkipZigTest; // TODO | |
| 27 | if (builtin.zig_backend == .stage2_c and builtin.os.tag == .windows) return error.SkipZigTest; // TODO | |
| 28 | if (builtin.zig_backend == .stage2_llvm) switch (builtin.cpu.arch) { | |
| 29 | .x86_64, .x86 => {}, | |
| 30 | else => return error.SkipZigTest, | |
| 31 | }; // TODO | |
| 28 | 32 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 29 | 33 | |
| 30 | 34 | const s = "Hello world"; |
| ... | ... | @@ -39,8 +43,10 @@ test "reference a global threadlocal variable" { |
| 39 | 43 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 40 | 44 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 41 | 45 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 42 | if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch != .x86_64) return error.SkipZigTest; // TODO | |
| 43 | if (builtin.zig_backend == .stage2_c and builtin.os.tag == .windows) return error.SkipZigTest; // TODO | |
| 46 | if (builtin.zig_backend == .stage2_llvm) switch (builtin.cpu.arch) { | |
| 47 | .x86_64, .x86 => {}, | |
| 48 | else => return error.SkipZigTest, | |
| 49 | }; // TODO | |
| 44 | 50 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 45 | 51 | |
| 46 | 52 | _ = nrfx_uart_rx(&g_uart0); |
test/cases/compile_errors/assigning_to_struct_or_union_fields_that_are_not_optionals_with_a_function_that_returns_an_optional.zig+1-1| ... | ... | @@ -20,4 +20,4 @@ export fn entry() void { |
| 20 | 20 | // |
| 21 | 21 | // :11:27: error: expected type 'u8', found '?u8' |
| 22 | 22 | // :11:27: note: cannot convert optional to payload type |
| 23 | // :11:27: note: consider using `.?`, `orelse`, or `if` | |
| 23 | // :11:27: note: consider using '.?', 'orelse', or 'if' |
test/cases/compile_errors/comptime_arg_to_generic_fn_callee_error.zig created+21| ... | ... | @@ -0,0 +1,21 @@ |
| 1 | const std = @import("std"); | |
| 2 | const MyStruct = struct { | |
| 3 | a: i32, | |
| 4 | b: i32, | |
| 5 | ||
| 6 | pub fn getA(self: *List) i32 { | |
| 7 | return self.items(.c); | |
| 8 | } | |
| 9 | }; | |
| 10 | const List = std.MultiArrayList(MyStruct); | |
| 11 | pub export fn entry() void { | |
| 12 | var list = List{}; | |
| 13 | _ = MyStruct.getA(&list); | |
| 14 | } | |
| 15 | ||
| 16 | // error | |
| 17 | // backend=stage2 | |
| 18 | // target=native | |
| 19 | // | |
| 20 | // :7:28: error: no field named 'c' in enum 'meta.FieldEnum(tmp.MyStruct)' | |
| 21 | // :?:?: note: enum declared here |
test/cases/compile_errors/comptime_call_of_function_pointer.zig created+10| ... | ... | @@ -0,0 +1,10 @@ |
| 1 | export fn entry() void { | |
| 2 | const fn_ptr = @intToPtr(*align(1) fn () void, 0xffd2); | |
| 3 | comptime fn_ptr(); | |
| 4 | } | |
| 5 | ||
| 6 | // error | |
| 7 | // backend=stage2 | |
| 8 | // target=native | |
| 9 | // | |
| 10 | // :3:20: error: comptime call of function pointer |
test/cases/compile_errors/discarding_error_value.zig+1-1| ... | ... | @@ -10,4 +10,4 @@ fn foo() !void { |
| 10 | 10 | // target=native |
| 11 | 11 | // |
| 12 | 12 | // :2:12: error: error is discarded |
| 13 | // :2:12: note: consider using `try`, `catch`, or `if` | |
| 13 | // :2:12: note: consider using 'try', 'catch', or 'if' |
test/cases/compile_errors/helpful_return_type_error_message.zig+2-2| ... | ... | @@ -26,7 +26,7 @@ export fn quux() u32 { |
| 26 | 26 | // :11:15: error: expected type 'u32', found '@typeInfo(@typeInfo(@TypeOf(tmp.bar)).Fn.return_type.?).ErrorUnion.error_set!u32' |
| 27 | 27 | // :10:17: note: function cannot return an error |
| 28 | 28 | // :11:15: note: cannot convert error union to payload type |
| 29 | // :11:15: note: consider using `try`, `catch`, or `if` | |
| 29 | // :11:15: note: consider using 'try', 'catch', or 'if' | |
| 30 | 30 | // :15:14: error: expected type 'u32', found '@typeInfo(@typeInfo(@TypeOf(tmp.bar)).Fn.return_type.?).ErrorUnion.error_set!u32' |
| 31 | 31 | // :15:14: note: cannot convert error union to payload type |
| 32 | // :15:14: note: consider using `try`, `catch`, or `if` | |
| 32 | // :15:14: note: consider using 'try', 'catch', or 'if' |
test/cases/compile_errors/ignored_deferred_function_call.zig+1-1| ... | ... | @@ -8,4 +8,4 @@ fn bar() anyerror!i32 { return 0; } |
| 8 | 8 | // target=native |
| 9 | 9 | // |
| 10 | 10 | // :2:14: error: error is ignored |
| 11 | // :2:14: note: consider using `try`, `catch`, or `if` | |
| 11 | // :2:14: note: consider using 'try', 'catch', or 'if' |
test/cases/compile_errors/ignored_expression_in_while_continuation.zig+3-3| ... | ... | @@ -18,8 +18,8 @@ fn bad() anyerror!void { |
| 18 | 18 | // target=native |
| 19 | 19 | // |
| 20 | 20 | // :2:24: error: error is ignored |
| 21 | // :2:24: note: consider using `try`, `catch`, or `if` | |
| 21 | // :2:24: note: consider using 'try', 'catch', or 'if' | |
| 22 | 22 | // :6:25: error: error is ignored |
| 23 | // :6:25: note: consider using `try`, `catch`, or `if` | |
| 23 | // :6:25: note: consider using 'try', 'catch', or 'if' | |
| 24 | 24 | // :10:25: error: error is ignored |
| 25 | // :10:25: note: consider using `try`, `catch`, or `if` | |
| 25 | // :10:25: note: consider using 'try', 'catch', or 'if' |
test/cases/compile_errors/increase_pointer_alignment_in_ptrCast.zig+1| ... | ... | @@ -11,3 +11,4 @@ export fn entry() u32 { |
| 11 | 11 | // :3:17: error: cast increases pointer alignment |
| 12 | 12 | // :3:32: note: '*u8' has alignment '1' |
| 13 | 13 | // :3:26: note: '*u32' has alignment '4' |
| 14 | // :3:17: note: consider using '@alignCast' |
test/cases/compile_errors/inline_call_runtime_value_to_comptime_param.zig created+17| ... | ... | @@ -0,0 +1,17 @@ |
| 1 | inline fn needComptime(comptime a: u64) void { | |
| 2 | if (a != 0) @compileError("foo"); | |
| 3 | } | |
| 4 | fn acceptRuntime(value: u64) void { | |
| 5 | needComptime(value); | |
| 6 | } | |
| 7 | pub export fn entry() void { | |
| 8 | var value: u64 = 0; | |
| 9 | acceptRuntime(value); | |
| 10 | } | |
| 11 | ||
| 12 | // error | |
| 13 | // backend=stage2 | |
| 14 | // target=native | |
| 15 | // | |
| 16 | // :5:18: error: unable to resolve comptime value | |
| 17 | // :5:18: note: parameter is comptime |
test/cases/compile_errors/invalid_decltest.zig created+13| ... | ... | @@ -0,0 +1,13 @@ |
| 1 | export fn foo() void { | |
| 2 | const a = 1; | |
| 3 | struct { | |
| 4 | test a {} | |
| 5 | }; | |
| 6 | } | |
| 7 | ||
| 8 | // error | |
| 9 | // backend=stage2 | |
| 10 | // target=native | |
| 11 | // | |
| 12 | // :4:14: error: cannot test a local constant | |
| 13 | // :2:11: note: local constant declared here |
test/cases/compile_errors/invalid_member_of_builtin_enum.zig+2-2| ... | ... | @@ -1,6 +1,6 @@ |
| 1 | 1 | const builtin = @import("std").builtin; |
| 2 | 2 | export fn entry() void { |
| 3 | const foo = builtin.Mode.x86; | |
| 3 | const foo = builtin.OptimizeMode.x86; | |
| 4 | 4 | _ = foo; |
| 5 | 5 | } |
| 6 | 6 | |
| ... | ... | @@ -8,5 +8,5 @@ export fn entry() void { |
| 8 | 8 | // backend=stage2 |
| 9 | 9 | // target=native |
| 10 | 10 | // |
| 11 | // :3:30: error: enum 'builtin.Mode' has no member named 'x86' | |
| 11 | // :3:38: error: enum 'builtin.OptimizeMode' has no member named 'x86' | |
| 12 | 12 | // :?:18: note: enum declared here |
test/cases/compile_errors/invalid_qualcast.zig created+12| ... | ... | @@ -0,0 +1,12 @@ |
| 1 | pub export fn entry() void { | |
| 2 | var a: [*:0]const volatile u16 = undefined; | |
| 3 | _ = @qualCast([*]u16, a); | |
| 4 | } | |
| 5 | ||
| 6 | // error | |
| 7 | // backend=stage2 | |
| 8 | // target=native | |
| 9 | // | |
| 10 | // :3:9: error: '@qualCast' can only modify 'const' and 'volatile' qualifiers | |
| 11 | // :3:9: note: expected type '[*]const volatile u16' | |
| 12 | // :3:9: note: got type '[*:0]const volatile u16' |
test/cases/compile_errors/issue_5618_coercion_of_optional_anyopaque_to_anyopaque_must_fail.zig+1-1| ... | ... | @@ -10,5 +10,5 @@ export fn foo() void { |
| 10 | 10 | // |
| 11 | 11 | // :4:9: error: expected type '*anyopaque', found '?*anyopaque' |
| 12 | 12 | // :4:9: note: cannot convert optional to payload type |
| 13 | // :4:9: note: consider using `.?`, `orelse`, or `if` | |
| 13 | // :4:9: note: consider using '.?', 'orelse', or 'if' | |
| 14 | 14 | // :4:9: note: '?*anyopaque' could have null values which are illegal in type '*anyopaque' |
test/cases/compile_errors/ptrCast_discards_const_qualifier.zig+1| ... | ... | @@ -9,3 +9,4 @@ export fn entry() void { |
| 9 | 9 | // target=native |
| 10 | 10 | // |
| 11 | 11 | // :3:15: error: cast discards const qualifier |
| 12 | // :3:15: note: consider using '@qualCast' |
test/cases/compile_errors/regression_test_2980_base_type_u32_is_not_type_checked_properly_when_assigning_a_value_within_a_struct.zig+1-1| ... | ... | @@ -20,4 +20,4 @@ export fn entry() void { |
| 20 | 20 | // |
| 21 | 21 | // :12:25: error: expected type 'u32', found '@typeInfo(@typeInfo(@TypeOf(tmp.get_uval)).Fn.return_type.?).ErrorUnion.error_set!u32' |
| 22 | 22 | // :12:25: note: cannot convert error union to payload type |
| 23 | // :12:25: note: consider using `try`, `catch`, or `if` | |
| 23 | // :12:25: note: consider using 'try', 'catch', or 'if' |
test/cases/compile_errors/result_location_incompatibility_mismatching_handle_is_ptr.zig+1-1| ... | ... | @@ -17,4 +17,4 @@ pub const Container = struct { |
| 17 | 17 | // |
| 18 | 18 | // :3:36: error: expected type 'i32', found '?i32' |
| 19 | 19 | // :3:36: note: cannot convert optional to payload type |
| 20 | // :3:36: note: consider using `.?`, `orelse`, or `if` | |
| 20 | // :3:36: note: consider using '.?', 'orelse', or 'if' |
test/cases/compile_errors/result_location_incompatibility_mismatching_handle_is_ptr_generic_call.zig+1-1| ... | ... | @@ -17,4 +17,4 @@ pub const Container = struct { |
| 17 | 17 | // |
| 18 | 18 | // :3:36: error: expected type 'i32', found '?i32' |
| 19 | 19 | // :3:36: note: cannot convert optional to payload type |
| 20 | // :3:36: note: consider using `.?`, `orelse`, or `if` | |
| 20 | // :3:36: note: consider using '.?', 'orelse', or 'if' |
test/link/bss/build.zig+8-5| ... | ... | @@ -1,12 +1,15 @@ |
| 1 | const Builder = @import("std").build.Builder; | |
| 1 | const std = @import("std"); | |
| 2 | 2 | |
| 3 | pub fn build(b: *Builder) void { | |
| 4 | const mode = b.standardReleaseOptions(); | |
| 3 | pub fn build(b: *std.Build) void { | |
| 4 | const optimize = b.standardOptimizeOption(.{}); | |
| 5 | 5 | const test_step = b.step("test", "Test"); |
| 6 | 6 | |
| 7 | const exe = b.addExecutable("bss", "main.zig"); | |
| 7 | const exe = b.addExecutable(.{ | |
| 8 | .name = "bss", | |
| 9 | .root_source_file = .{ .path = "main.zig" }, | |
| 10 | .optimize = optimize, | |
| 11 | }); | |
| 8 | 12 | b.default_step.dependOn(&exe.step); |
| 9 | exe.setBuildMode(mode); | |
| 10 | 13 | |
| 11 | 14 | const run = exe.run(); |
| 12 | 15 | run.expectStdOutEqual("0, 1, 0\n"); |
test/link/common_symbols/build.zig+12-7| ... | ... | @@ -1,14 +1,19 @@ |
| 1 | const Builder = @import("std").build.Builder; | |
| 1 | const std = @import("std"); | |
| 2 | 2 | |
| 3 | pub fn build(b: *Builder) void { | |
| 4 | const mode = b.standardReleaseOptions(); | |
| 3 | pub fn build(b: *std.Build) void { | |
| 4 | const optimize = b.standardOptimizeOption(.{}); | |
| 5 | 5 | |
| 6 | const lib_a = b.addStaticLibrary("a", null); | |
| 6 | const lib_a = b.addStaticLibrary(.{ | |
| 7 | .name = "a", | |
| 8 | .optimize = optimize, | |
| 9 | .target = .{}, | |
| 10 | }); | |
| 7 | 11 | lib_a.addCSourceFiles(&.{ "c.c", "a.c", "b.c" }, &.{"-fcommon"}); |
| 8 | lib_a.setBuildMode(mode); | |
| 9 | 12 | |
| 10 | const test_exe = b.addTest("main.zig"); | |
| 11 | test_exe.setBuildMode(mode); | |
| 13 | const test_exe = b.addTest(.{ | |
| 14 | .root_source_file = .{ .path = "main.zig" }, | |
| 15 | .optimize = optimize, | |
| 16 | }); | |
| 12 | 17 | test_exe.linkLibrary(lib_a); |
| 13 | 18 | |
| 14 | 19 | const test_step = b.step("test", "Test it"); |
test/link/common_symbols_alignment/build.zig+14-7| ... | ... | @@ -1,14 +1,21 @@ |
| 1 | const Builder = @import("std").build.Builder; | |
| 1 | const std = @import("std"); | |
| 2 | 2 | |
| 3 | pub fn build(b: *Builder) void { | |
| 4 | const mode = b.standardReleaseOptions(); | |
| 3 | pub fn build(b: *std.Build) void { | |
| 4 | const optimize = b.standardOptimizeOption(.{}); | |
| 5 | const target = b.standardTargetOptions(.{}); | |
| 5 | 6 | |
| 6 | const lib_a = b.addStaticLibrary("a", null); | |
| 7 | const lib_a = b.addStaticLibrary(.{ | |
| 8 | .name = "a", | |
| 9 | .optimize = optimize, | |
| 10 | .target = target, | |
| 11 | }); | |
| 7 | 12 | lib_a.addCSourceFiles(&.{"a.c"}, &.{"-fcommon"}); |
| 8 | lib_a.setBuildMode(mode); | |
| 9 | 13 | |
| 10 | const test_exe = b.addTest("main.zig"); | |
| 11 | test_exe.setBuildMode(mode); | |
| 14 | const test_exe = b.addTest(.{ | |
| 15 | .root_source_file = .{ .path = "main.zig" }, | |
| 16 | .optimize = optimize, | |
| 17 | .target = target, | |
| 18 | }); | |
| 12 | 19 | test_exe.linkLibrary(lib_a); |
| 13 | 20 | |
| 14 | 21 | const test_step = b.step("test", "Test it"); |
test/link/interdependent_static_c_libs/build.zig+19-9| ... | ... | @@ -1,20 +1,30 @@ |
| 1 | const Builder = @import("std").build.Builder; | |
| 1 | const std = @import("std"); | |
| 2 | 2 | |
| 3 | pub fn build(b: *Builder) void { | |
| 4 | const mode = b.standardReleaseOptions(); | |
| 3 | pub fn build(b: *std.Build) void { | |
| 4 | const optimize = b.standardOptimizeOption(.{}); | |
| 5 | const target = b.standardTargetOptions(.{}); | |
| 5 | 6 | |
| 6 | const lib_a = b.addStaticLibrary("a", null); | |
| 7 | const lib_a = b.addStaticLibrary(.{ | |
| 8 | .name = "a", | |
| 9 | .optimize = optimize, | |
| 10 | .target = target, | |
| 11 | }); | |
| 7 | 12 | lib_a.addCSourceFile("a.c", &[_][]const u8{}); |
| 8 | lib_a.setBuildMode(mode); | |
| 9 | 13 | lib_a.addIncludePath("."); |
| 10 | 14 | |
| 11 | const lib_b = b.addStaticLibrary("b", null); | |
| 15 | const lib_b = b.addStaticLibrary(.{ | |
| 16 | .name = "b", | |
| 17 | .optimize = optimize, | |
| 18 | .target = target, | |
| 19 | }); | |
| 12 | 20 | lib_b.addCSourceFile("b.c", &[_][]const u8{}); |
| 13 | lib_b.setBuildMode(mode); | |
| 14 | 21 | lib_b.addIncludePath("."); |
| 15 | 22 | |
| 16 | const test_exe = b.addTest("main.zig"); | |
| 17 | test_exe.setBuildMode(mode); | |
| 23 | const test_exe = b.addTest(.{ | |
| 24 | .root_source_file = .{ .path = "main.zig" }, | |
| 25 | .optimize = optimize, | |
| 26 | .target = target, | |
| 27 | }); | |
| 18 | 28 | test_exe.linkLibrary(lib_a); |
| 19 | 29 | test_exe.linkLibrary(lib_b); |
| 20 | 30 | test_exe.addIncludePath("."); |
test/link/macho/bugs/13056/build.zig+6-5| ... | ... | @@ -1,8 +1,7 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | const Builder = std.build.Builder; | |
| 3 | 2 | |
| 4 | pub fn build(b: *Builder) void { | |
| 5 | const mode = b.standardReleaseOptions(); | |
| 3 | pub fn build(b: *std.Build) void { | |
| 4 | const optimize = b.standardOptimizeOption(.{}); | |
| 6 | 5 | |
| 7 | 6 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; |
| 8 | 7 | const target_info = std.zig.system.NativeTargetInfo.detect(target) catch unreachable; |
| ... | ... | @@ -11,7 +10,10 @@ pub fn build(b: *Builder) void { |
| 11 | 10 | |
| 12 | 11 | const test_step = b.step("test", "Test the program"); |
| 13 | 12 | |
| 14 | const exe = b.addExecutable("test", null); | |
| 13 | const exe = b.addExecutable(.{ | |
| 14 | .name = "test", | |
| 15 | .optimize = optimize, | |
| 16 | }); | |
| 15 | 17 | b.default_step.dependOn(&exe.step); |
| 16 | 18 | exe.addIncludePath(std.fs.path.join(b.allocator, &.{ sdk.path, "/usr/include" }) catch unreachable); |
| 17 | 19 | exe.addIncludePath(std.fs.path.join(b.allocator, &.{ sdk.path, "/usr/include/c++/v1" }) catch unreachable); |
| ... | ... | @@ -20,7 +22,6 @@ pub fn build(b: *Builder) void { |
| 20 | 22 | "-nostdinc++", |
| 21 | 23 | }); |
| 22 | 24 | exe.addObjectFile(std.fs.path.join(b.allocator, &.{ sdk.path, "/usr/lib/libc++.tbd" }) catch unreachable); |
| 23 | exe.setBuildMode(mode); | |
| 24 | 25 | |
| 25 | 26 | const run_cmd = exe.run(); |
| 26 | 27 | run_cmd.expectStdErrEqual("x: 5\n"); |
test/link/macho/bugs/13457/build.zig+8-7| ... | ... | @@ -1,16 +1,17 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | const Builder = std.build.Builder; | |
| 3 | const LibExeObjectStep = std.build.LibExeObjStep; | |
| 4 | 2 | |
| 5 | pub fn build(b: *Builder) void { | |
| 6 | const mode = b.standardReleaseOptions(); | |
| 3 | pub fn build(b: *std.Build) void { | |
| 4 | const optimize = b.standardOptimizeOption(.{}); | |
| 7 | 5 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; |
| 8 | 6 | |
| 9 | 7 | const test_step = b.step("test", "Test the program"); |
| 10 | 8 | |
| 11 | const exe = b.addExecutable("test", "main.zig"); | |
| 12 | exe.setBuildMode(mode); | |
| 13 | exe.setTarget(target); | |
| 9 | const exe = b.addExecutable(.{ | |
| 10 | .name = "test", | |
| 11 | .root_source_file = .{ .path = "main.zig" }, | |
| 12 | .optimize = optimize, | |
| 13 | .target = target, | |
| 14 | }); | |
| 14 | 15 | |
| 15 | 16 | const run = exe.runEmulatable(); |
| 16 | 17 | test_step.dependOn(&run.step); |
test/link/macho/dead_strip/build.zig+14-10| ... | ... | @@ -1,9 +1,7 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | const Builder = std.build.Builder; | |
| 3 | const LibExeObjectStep = std.build.LibExeObjStep; | |
| 4 | 2 | |
| 5 | pub fn build(b: *Builder) void { | |
| 6 | const mode = b.standardReleaseOptions(); | |
| 3 | pub fn build(b: *std.Build) void { | |
| 4 | const optimize = b.standardOptimizeOption(.{}); | |
| 7 | 5 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; |
| 8 | 6 | |
| 9 | 7 | const test_step = b.step("test", "Test the program"); |
| ... | ... | @@ -11,7 +9,7 @@ pub fn build(b: *Builder) void { |
| 11 | 9 | |
| 12 | 10 | { |
| 13 | 11 | // Without -dead_strip, we expect `iAmUnused` symbol present |
| 14 | const exe = createScenario(b, mode, target); | |
| 12 | const exe = createScenario(b, optimize, target); | |
| 15 | 13 | |
| 16 | 14 | const check = exe.checkObject(.macho); |
| 17 | 15 | check.checkInSymtab(); |
| ... | ... | @@ -24,7 +22,7 @@ pub fn build(b: *Builder) void { |
| 24 | 22 | |
| 25 | 23 | { |
| 26 | 24 | // With -dead_strip, no `iAmUnused` symbol should be present |
| 27 | const exe = createScenario(b, mode, target); | |
| 25 | const exe = createScenario(b, optimize, target); | |
| 28 | 26 | exe.link_gc_sections = true; |
| 29 | 27 | |
| 30 | 28 | const check = exe.checkObject(.macho); |
| ... | ... | @@ -37,11 +35,17 @@ pub fn build(b: *Builder) void { |
| 37 | 35 | } |
| 38 | 36 | } |
| 39 | 37 | |
| 40 | fn createScenario(b: *Builder, mode: std.builtin.Mode, target: std.zig.CrossTarget) *LibExeObjectStep { | |
| 41 | const exe = b.addExecutable("test", null); | |
| 38 | fn createScenario( | |
| 39 | b: *std.Build, | |
| 40 | optimize: std.builtin.OptimizeMode, | |
| 41 | target: std.zig.CrossTarget, | |
| 42 | ) *std.Build.CompileStep { | |
| 43 | const exe = b.addExecutable(.{ | |
| 44 | .name = "test", | |
| 45 | .optimize = optimize, | |
| 46 | .target = target, | |
| 47 | }); | |
| 42 | 48 | exe.addCSourceFile("main.c", &[0][]const u8{}); |
| 43 | exe.setBuildMode(mode); | |
| 44 | exe.setTarget(target); | |
| 45 | 49 | exe.linkLibC(); |
| 46 | 50 | return exe; |
| 47 | 51 | } |
test/link/macho/dead_strip_dylibs/build.zig+9-9| ... | ... | @@ -1,16 +1,14 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | const Builder = std.build.Builder; | |
| 3 | const LibExeObjectStep = std.build.LibExeObjStep; | |
| 4 | 2 | |
| 5 | pub fn build(b: *Builder) void { | |
| 6 | const mode = b.standardReleaseOptions(); | |
| 3 | pub fn build(b: *std.Build) void { | |
| 4 | const optimize = b.standardOptimizeOption(.{}); | |
| 7 | 5 | |
| 8 | 6 | const test_step = b.step("test", "Test the program"); |
| 9 | 7 | test_step.dependOn(b.getInstallStep()); |
| 10 | 8 | |
| 11 | 9 | { |
| 12 | 10 | // Without -dead_strip_dylibs we expect `-la` to include liba.dylib in the final executable |
| 13 | const exe = createScenario(b, mode); | |
| 11 | const exe = createScenario(b, optimize); | |
| 14 | 12 | |
| 15 | 13 | const check = exe.checkObject(.macho); |
| 16 | 14 | check.checkStart("cmd LOAD_DYLIB"); |
| ... | ... | @@ -27,7 +25,7 @@ pub fn build(b: *Builder) void { |
| 27 | 25 | |
| 28 | 26 | { |
| 29 | 27 | // With -dead_strip_dylibs, we should include liba.dylib as it's unreachable |
| 30 | const exe = createScenario(b, mode); | |
| 28 | const exe = createScenario(b, optimize); | |
| 31 | 29 | exe.dead_strip_dylibs = true; |
| 32 | 30 | |
| 33 | 31 | const run_cmd = exe.run(); |
| ... | ... | @@ -36,10 +34,12 @@ pub fn build(b: *Builder) void { |
| 36 | 34 | } |
| 37 | 35 | } |
| 38 | 36 | |
| 39 | fn createScenario(b: *Builder, mode: std.builtin.Mode) *LibExeObjectStep { | |
| 40 | const exe = b.addExecutable("test", null); | |
| 37 | fn createScenario(b: *std.Build, optimize: std.builtin.OptimizeMode) *std.Build.CompileStep { | |
| 38 | const exe = b.addExecutable(.{ | |
| 39 | .name = "test", | |
| 40 | .optimize = optimize, | |
| 41 | }); | |
| 41 | 42 | exe.addCSourceFile("main.c", &[0][]const u8{}); |
| 42 | exe.setBuildMode(mode); | |
| 43 | 43 | exe.linkLibC(); |
| 44 | 44 | exe.linkFramework("Cocoa"); |
| 45 | 45 | return exe; |
test/link/macho/dylib/build.zig+13-9| ... | ... | @@ -1,16 +1,18 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | const Builder = std.build.Builder; | |
| 3 | 2 | |
| 4 | pub fn build(b: *Builder) void { | |
| 5 | const mode = b.standardReleaseOptions(); | |
| 3 | pub fn build(b: *std.Build) void { | |
| 4 | const optimize = b.standardOptimizeOption(.{}); | |
| 6 | 5 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; |
| 7 | 6 | |
| 8 | 7 | const test_step = b.step("test", "Test"); |
| 9 | 8 | test_step.dependOn(b.getInstallStep()); |
| 10 | 9 | |
| 11 | const dylib = b.addSharedLibrary("a", null, b.version(1, 0, 0)); | |
| 12 | dylib.setBuildMode(mode); | |
| 13 | dylib.setTarget(target); | |
| 10 | const dylib = b.addSharedLibrary(.{ | |
| 11 | .name = "a", | |
| 12 | .version = .{ .major = 1, .minor = 0 }, | |
| 13 | .optimize = optimize, | |
| 14 | .target = target, | |
| 15 | }); | |
| 14 | 16 | dylib.addCSourceFile("a.c", &.{}); |
| 15 | 17 | dylib.linkLibC(); |
| 16 | 18 | dylib.install(); |
| ... | ... | @@ -24,9 +26,11 @@ pub fn build(b: *Builder) void { |
| 24 | 26 | |
| 25 | 27 | test_step.dependOn(&check_dylib.step); |
| 26 | 28 | |
| 27 | const exe = b.addExecutable("main", null); | |
| 28 | exe.setTarget(target); | |
| 29 | exe.setBuildMode(mode); | |
| 29 | const exe = b.addExecutable(.{ | |
| 30 | .name = "main", | |
| 31 | .optimize = optimize, | |
| 32 | .target = target, | |
| 33 | }); | |
| 30 | 34 | exe.addCSourceFile("main.c", &.{}); |
| 31 | 35 | exe.linkSystemLibrary("a"); |
| 32 | 36 | exe.linkLibC(); |
test/link/macho/empty/build.zig+8-7| ... | ... | @@ -1,21 +1,22 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | const Builder = std.build.Builder; | |
| 3 | 2 | |
| 4 | pub fn build(b: *Builder) void { | |
| 5 | const mode = b.standardReleaseOptions(); | |
| 3 | pub fn build(b: *std.Build) void { | |
| 4 | const optimize = b.standardOptimizeOption(.{}); | |
| 6 | 5 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; |
| 7 | 6 | |
| 8 | 7 | const test_step = b.step("test", "Test the program"); |
| 9 | 8 | test_step.dependOn(b.getInstallStep()); |
| 10 | 9 | |
| 11 | const exe = b.addExecutable("test", null); | |
| 10 | const exe = b.addExecutable(.{ | |
| 11 | .name = "test", | |
| 12 | .optimize = optimize, | |
| 13 | .target = target, | |
| 14 | }); | |
| 12 | 15 | exe.addCSourceFile("main.c", &[0][]const u8{}); |
| 13 | 16 | exe.addCSourceFile("empty.c", &[0][]const u8{}); |
| 14 | exe.setBuildMode(mode); | |
| 15 | exe.setTarget(target); | |
| 16 | 17 | exe.linkLibC(); |
| 17 | 18 | |
| 18 | const run_cmd = std.build.EmulatableRunStep.create(b, "run", exe); | |
| 19 | const run_cmd = std.Build.EmulatableRunStep.create(b, "run", exe); | |
| 19 | 20 | run_cmd.expectStdOutEqual("Hello!\n"); |
| 20 | 21 | test_step.dependOn(&run_cmd.step); |
| 21 | 22 | } |
test/link/macho/entry/build.zig+7-6| ... | ... | @@ -1,15 +1,16 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | const Builder = std.build.Builder; | |
| 3 | 2 | |
| 4 | pub fn build(b: *Builder) void { | |
| 5 | const mode = b.standardReleaseOptions(); | |
| 3 | pub fn build(b: *std.Build) void { | |
| 4 | const optimize = b.standardOptimizeOption(.{}); | |
| 6 | 5 | |
| 7 | 6 | const test_step = b.step("test", "Test"); |
| 8 | 7 | test_step.dependOn(b.getInstallStep()); |
| 9 | 8 | |
| 10 | const exe = b.addExecutable("main", null); | |
| 11 | exe.setTarget(.{ .os_tag = .macos }); | |
| 12 | exe.setBuildMode(mode); | |
| 9 | const exe = b.addExecutable(.{ | |
| 10 | .name = "main", | |
| 11 | .optimize = optimize, | |
| 12 | .target = .{ .os_tag = .macos }, | |
| 13 | }); | |
| 13 | 14 | exe.addCSourceFile("main.c", &.{}); |
| 14 | 15 | exe.linkLibC(); |
| 15 | 16 | exe.entry_symbol_name = "_non_main"; |
test/link/macho/headerpad/build.zig+11-11| ... | ... | @@ -1,17 +1,15 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | const Builder = std.build.Builder; | |
| 4 | const LibExeObjectStep = std.build.LibExeObjStep; | |
| 5 | 3 | |
| 6 | pub fn build(b: *Builder) void { | |
| 7 | const mode = b.standardReleaseOptions(); | |
| 4 | pub fn build(b: *std.Build) void { | |
| 5 | const optimize = b.standardOptimizeOption(.{}); | |
| 8 | 6 | |
| 9 | 7 | const test_step = b.step("test", "Test"); |
| 10 | 8 | test_step.dependOn(b.getInstallStep()); |
| 11 | 9 | |
| 12 | 10 | { |
| 13 | 11 | // Test -headerpad_max_install_names |
| 14 | const exe = simpleExe(b, mode); | |
| 12 | const exe = simpleExe(b, optimize); | |
| 15 | 13 | exe.headerpad_max_install_names = true; |
| 16 | 14 | |
| 17 | 15 | const check = exe.checkObject(.macho); |
| ... | ... | @@ -36,7 +34,7 @@ pub fn build(b: *Builder) void { |
| 36 | 34 | |
| 37 | 35 | { |
| 38 | 36 | // Test -headerpad |
| 39 | const exe = simpleExe(b, mode); | |
| 37 | const exe = simpleExe(b, optimize); | |
| 40 | 38 | exe.headerpad_size = 0x10000; |
| 41 | 39 | |
| 42 | 40 | const check = exe.checkObject(.macho); |
| ... | ... | @@ -52,7 +50,7 @@ pub fn build(b: *Builder) void { |
| 52 | 50 | |
| 53 | 51 | { |
| 54 | 52 | // Test both flags with -headerpad overriding -headerpad_max_install_names |
| 55 | const exe = simpleExe(b, mode); | |
| 53 | const exe = simpleExe(b, optimize); | |
| 56 | 54 | exe.headerpad_max_install_names = true; |
| 57 | 55 | exe.headerpad_size = 0x10000; |
| 58 | 56 | |
| ... | ... | @@ -69,7 +67,7 @@ pub fn build(b: *Builder) void { |
| 69 | 67 | |
| 70 | 68 | { |
| 71 | 69 | // Test both flags with -headerpad_max_install_names overriding -headerpad |
| 72 | const exe = simpleExe(b, mode); | |
| 70 | const exe = simpleExe(b, optimize); | |
| 73 | 71 | exe.headerpad_size = 0x1000; |
| 74 | 72 | exe.headerpad_max_install_names = true; |
| 75 | 73 | |
| ... | ... | @@ -94,9 +92,11 @@ pub fn build(b: *Builder) void { |
| 94 | 92 | } |
| 95 | 93 | } |
| 96 | 94 | |
| 97 | fn simpleExe(b: *Builder, mode: std.builtin.Mode) *LibExeObjectStep { | |
| 98 | const exe = b.addExecutable("main", null); | |
| 99 | exe.setBuildMode(mode); | |
| 95 | fn simpleExe(b: *std.Build, optimize: std.builtin.OptimizeMode) *std.Build.CompileStep { | |
| 96 | const exe = b.addExecutable(.{ | |
| 97 | .name = "main", | |
| 98 | .optimize = optimize, | |
| 99 | }); | |
| 100 | 100 | exe.addCSourceFile("main.c", &.{}); |
| 101 | 101 | exe.linkLibC(); |
| 102 | 102 | exe.linkFramework("CoreFoundation"); |
test/link/macho/linksection/build.zig+9-6| ... | ... | @@ -1,15 +1,18 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | |
| 3 | pub fn build(b: *std.build.Builder) void { | |
| 4 | const mode = b.standardReleaseOptions(); | |
| 3 | pub fn build(b: *std.Build) void { | |
| 4 | const optimize = b.standardOptimizeOption(.{}); | |
| 5 | 5 | const target = std.zig.CrossTarget{ .os_tag = .macos }; |
| 6 | 6 | |
| 7 | 7 | const test_step = b.step("test", "Test"); |
| 8 | 8 | test_step.dependOn(b.getInstallStep()); |
| 9 | 9 | |
| 10 | const obj = b.addObject("test", "main.zig"); | |
| 11 | obj.setBuildMode(mode); | |
| 12 | obj.setTarget(target); | |
| 10 | const obj = b.addObject(.{ | |
| 11 | .name = "test", | |
| 12 | .root_source_file = .{ .path = "main.zig" }, | |
| 13 | .optimize = optimize, | |
| 14 | .target = target, | |
| 15 | }); | |
| 13 | 16 | |
| 14 | 17 | const check = obj.checkObject(.macho); |
| 15 | 18 | |
| ... | ... | @@ -19,7 +22,7 @@ pub fn build(b: *std.build.Builder) void { |
| 19 | 22 | check.checkInSymtab(); |
| 20 | 23 | check.checkNext("{*} (__TEXT,__TestFn) external _testFn"); |
| 21 | 24 | |
| 22 | if (mode == .Debug) { | |
| 25 | if (optimize == .Debug) { | |
| 23 | 26 | check.checkInSymtab(); |
| 24 | 27 | check.checkNext("{*} (__TEXT,__TestGenFnA) _main.testGenericFn__anon_{*}"); |
| 25 | 28 | } |
test/link/macho/needed_framework/build.zig+6-6| ... | ... | @@ -1,18 +1,18 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | const Builder = std.build.Builder; | |
| 3 | const LibExeObjectStep = std.build.LibExeObjStep; | |
| 4 | 2 | |
| 5 | pub fn build(b: *Builder) void { | |
| 6 | const mode = b.standardReleaseOptions(); | |
| 3 | pub fn build(b: *std.Build) void { | |
| 4 | const optimize = b.standardOptimizeOption(.{}); | |
| 7 | 5 | |
| 8 | 6 | const test_step = b.step("test", "Test the program"); |
| 9 | 7 | test_step.dependOn(b.getInstallStep()); |
| 10 | 8 | |
| 11 | 9 | // -dead_strip_dylibs |
| 12 | 10 | // -needed_framework Cocoa |
| 13 | const exe = b.addExecutable("test", null); | |
| 11 | const exe = b.addExecutable(.{ | |
| 12 | .name = "test", | |
| 13 | .optimize = optimize, | |
| 14 | }); | |
| 14 | 15 | exe.addCSourceFile("main.c", &[0][]const u8{}); |
| 15 | exe.setBuildMode(mode); | |
| 16 | 16 | exe.linkLibC(); |
| 17 | 17 | exe.linkFrameworkNeeded("Cocoa"); |
| 18 | 18 | exe.dead_strip_dylibs = true; |
test/link/macho/needed_library/build.zig+13-10| ... | ... | @@ -1,27 +1,30 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | const Builder = std.build.Builder; | |
| 3 | const LibExeObjectStep = std.build.LibExeObjStep; | |
| 4 | 2 | |
| 5 | pub fn build(b: *Builder) void { | |
| 6 | const mode = b.standardReleaseOptions(); | |
| 3 | pub fn build(b: *std.Build) void { | |
| 4 | const optimize = b.standardOptimizeOption(.{}); | |
| 7 | 5 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; |
| 8 | 6 | |
| 9 | 7 | const test_step = b.step("test", "Test the program"); |
| 10 | 8 | test_step.dependOn(b.getInstallStep()); |
| 11 | 9 | |
| 12 | const dylib = b.addSharedLibrary("a", null, b.version(1, 0, 0)); | |
| 13 | dylib.setTarget(target); | |
| 14 | dylib.setBuildMode(mode); | |
| 10 | const dylib = b.addSharedLibrary(.{ | |
| 11 | .name = "a", | |
| 12 | .version = .{ .major = 1, .minor = 0 }, | |
| 13 | .optimize = optimize, | |
| 14 | .target = target, | |
| 15 | }); | |
| 15 | 16 | dylib.addCSourceFile("a.c", &.{}); |
| 16 | 17 | dylib.linkLibC(); |
| 17 | 18 | dylib.install(); |
| 18 | 19 | |
| 19 | 20 | // -dead_strip_dylibs |
| 20 | 21 | // -needed-la |
| 21 | const exe = b.addExecutable("test", null); | |
| 22 | const exe = b.addExecutable(.{ | |
| 23 | .name = "test", | |
| 24 | .optimize = optimize, | |
| 25 | .target = target, | |
| 26 | }); | |
| 22 | 27 | exe.addCSourceFile("main.c", &[0][]const u8{}); |
| 23 | exe.setBuildMode(mode); | |
| 24 | exe.setTarget(target); | |
| 25 | 28 | exe.linkLibC(); |
| 26 | 29 | exe.linkSystemLibraryNeeded("a"); |
| 27 | 30 | exe.addLibraryPath(b.pathFromRoot("zig-out/lib")); |
test/link/macho/objc/build.zig+7-6| ... | ... | @@ -1,21 +1,22 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | const Builder = std.build.Builder; | |
| 3 | 2 | |
| 4 | pub fn build(b: *Builder) void { | |
| 5 | const mode = b.standardReleaseOptions(); | |
| 3 | pub fn build(b: *std.Build) void { | |
| 4 | const optimize = b.standardOptimizeOption(.{}); | |
| 6 | 5 | |
| 7 | 6 | const test_step = b.step("test", "Test the program"); |
| 8 | 7 | |
| 9 | const exe = b.addExecutable("test", null); | |
| 8 | const exe = b.addExecutable(.{ | |
| 9 | .name = "test", | |
| 10 | .optimize = optimize, | |
| 11 | }); | |
| 10 | 12 | exe.addIncludePath("."); |
| 11 | 13 | exe.addCSourceFile("Foo.m", &[0][]const u8{}); |
| 12 | 14 | exe.addCSourceFile("test.m", &[0][]const u8{}); |
| 13 | exe.setBuildMode(mode); | |
| 14 | 15 | exe.linkLibC(); |
| 15 | 16 | // TODO when we figure out how to ship framework stubs for cross-compilation, |
| 16 | 17 | // populate paths to the sysroot here. |
| 17 | 18 | exe.linkFramework("Foundation"); |
| 18 | 19 | |
| 19 | const run_cmd = std.build.EmulatableRunStep.create(b, "run", exe); | |
| 20 | const run_cmd = std.Build.EmulatableRunStep.create(b, "run", exe); | |
| 20 | 21 | test_step.dependOn(&run_cmd.step); |
| 21 | 22 | } |
test/link/macho/objcpp/build.zig+6-5| ... | ... | @@ -1,17 +1,18 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | const Builder = std.build.Builder; | |
| 3 | 2 | |
| 4 | pub fn build(b: *Builder) void { | |
| 5 | const mode = b.standardReleaseOptions(); | |
| 3 | pub fn build(b: *std.Build) void { | |
| 4 | const optimize = b.standardOptimizeOption(.{}); | |
| 6 | 5 | |
| 7 | 6 | const test_step = b.step("test", "Test the program"); |
| 8 | 7 | |
| 9 | const exe = b.addExecutable("test", null); | |
| 8 | const exe = b.addExecutable(.{ | |
| 9 | .name = "test", | |
| 10 | .optimize = optimize, | |
| 11 | }); | |
| 10 | 12 | b.default_step.dependOn(&exe.step); |
| 11 | 13 | exe.addIncludePath("."); |
| 12 | 14 | exe.addCSourceFile("Foo.mm", &[0][]const u8{}); |
| 13 | 15 | exe.addCSourceFile("test.mm", &[0][]const u8{}); |
| 14 | exe.setBuildMode(mode); | |
| 15 | 16 | exe.linkLibCpp(); |
| 16 | 17 | // TODO when we figure out how to ship framework stubs for cross-compilation, |
| 17 | 18 | // populate paths to the sysroot here. |
test/link/macho/pagezero/build.zig+12-9| ... | ... | @@ -1,17 +1,18 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | const Builder = std.build.Builder; | |
| 3 | 2 | |
| 4 | pub fn build(b: *Builder) void { | |
| 5 | const mode = b.standardReleaseOptions(); | |
| 3 | pub fn build(b: *std.Build) void { | |
| 4 | const optimize = b.standardOptimizeOption(.{}); | |
| 6 | 5 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; |
| 7 | 6 | |
| 8 | 7 | const test_step = b.step("test", "Test"); |
| 9 | 8 | test_step.dependOn(b.getInstallStep()); |
| 10 | 9 | |
| 11 | 10 | { |
| 12 | const exe = b.addExecutable("pagezero", null); | |
| 13 | exe.setTarget(target); | |
| 14 | exe.setBuildMode(mode); | |
| 11 | const exe = b.addExecutable(.{ | |
| 12 | .name = "pagezero", | |
| 13 | .optimize = optimize, | |
| 14 | .target = target, | |
| 15 | }); | |
| 15 | 16 | exe.addCSourceFile("main.c", &.{}); |
| 16 | 17 | exe.linkLibC(); |
| 17 | 18 | exe.pagezero_size = 0x4000; |
| ... | ... | @@ -29,9 +30,11 @@ pub fn build(b: *Builder) void { |
| 29 | 30 | } |
| 30 | 31 | |
| 31 | 32 | { |
| 32 | const exe = b.addExecutable("no_pagezero", null); | |
| 33 | exe.setTarget(target); | |
| 34 | exe.setBuildMode(mode); | |
| 33 | const exe = b.addExecutable(.{ | |
| 34 | .name = "no_pagezero", | |
| 35 | .optimize = optimize, | |
| 36 | .target = target, | |
| 37 | }); | |
| 35 | 38 | exe.addCSourceFile("main.c", &.{}); |
| 36 | 39 | exe.linkLibC(); |
| 37 | 40 | exe.pagezero_size = 0; |
test/link/macho/search_strategy/build.zig+28-19| ... | ... | @@ -1,9 +1,7 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | const Builder = std.build.Builder; | |
| 3 | const LibExeObjectStep = std.build.LibExeObjStep; | |
| 4 | 2 | |
| 5 | pub fn build(b: *Builder) void { | |
| 6 | const mode = b.standardReleaseOptions(); | |
| 3 | pub fn build(b: *std.Build) void { | |
| 4 | const optimize = b.standardOptimizeOption(.{}); | |
| 7 | 5 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; |
| 8 | 6 | |
| 9 | 7 | const test_step = b.step("test", "Test"); |
| ... | ... | @@ -11,7 +9,7 @@ pub fn build(b: *Builder) void { |
| 11 | 9 | |
| 12 | 10 | { |
| 13 | 11 | // -search_dylibs_first |
| 14 | const exe = createScenario(b, mode, target); | |
| 12 | const exe = createScenario(b, optimize, target); | |
| 15 | 13 | exe.search_strategy = .dylibs_first; |
| 16 | 14 | |
| 17 | 15 | const check = exe.checkObject(.macho); |
| ... | ... | @@ -26,40 +24,51 @@ pub fn build(b: *Builder) void { |
| 26 | 24 | |
| 27 | 25 | { |
| 28 | 26 | // -search_paths_first |
| 29 | const exe = createScenario(b, mode, target); | |
| 27 | const exe = createScenario(b, optimize, target); | |
| 30 | 28 | exe.search_strategy = .paths_first; |
| 31 | 29 | |
| 32 | const run = std.build.EmulatableRunStep.create(b, "run", exe); | |
| 30 | const run = std.Build.EmulatableRunStep.create(b, "run", exe); | |
| 33 | 31 | run.cwd = b.pathFromRoot("."); |
| 34 | 32 | run.expectStdOutEqual("Hello world"); |
| 35 | 33 | test_step.dependOn(&run.step); |
| 36 | 34 | } |
| 37 | 35 | } |
| 38 | 36 | |
| 39 | fn createScenario(b: *Builder, mode: std.builtin.Mode, target: std.zig.CrossTarget) *LibExeObjectStep { | |
| 40 | const static = b.addStaticLibrary("a", null); | |
| 41 | static.setTarget(target); | |
| 42 | static.setBuildMode(mode); | |
| 37 | fn createScenario( | |
| 38 | b: *std.Build, | |
| 39 | optimize: std.builtin.OptimizeMode, | |
| 40 | target: std.zig.CrossTarget, | |
| 41 | ) *std.Build.CompileStep { | |
| 42 | const static = b.addStaticLibrary(.{ | |
| 43 | .name = "a", | |
| 44 | .optimize = optimize, | |
| 45 | .target = target, | |
| 46 | }); | |
| 43 | 47 | static.addCSourceFile("a.c", &.{}); |
| 44 | 48 | static.linkLibC(); |
| 45 | static.override_dest_dir = std.build.InstallDir{ | |
| 49 | static.override_dest_dir = std.Build.InstallDir{ | |
| 46 | 50 | .custom = "static", |
| 47 | 51 | }; |
| 48 | 52 | static.install(); |
| 49 | 53 | |
| 50 | const dylib = b.addSharedLibrary("a", null, b.version(1, 0, 0)); | |
| 51 | dylib.setTarget(target); | |
| 52 | dylib.setBuildMode(mode); | |
| 54 | const dylib = b.addSharedLibrary(.{ | |
| 55 | .name = "a", | |
| 56 | .version = .{ .major = 1, .minor = 0 }, | |
| 57 | .optimize = optimize, | |
| 58 | .target = target, | |
| 59 | }); | |
| 53 | 60 | dylib.addCSourceFile("a.c", &.{}); |
| 54 | 61 | dylib.linkLibC(); |
| 55 | dylib.override_dest_dir = std.build.InstallDir{ | |
| 62 | dylib.override_dest_dir = std.Build.InstallDir{ | |
| 56 | 63 | .custom = "dynamic", |
| 57 | 64 | }; |
| 58 | 65 | dylib.install(); |
| 59 | 66 | |
| 60 | const exe = b.addExecutable("main", null); | |
| 61 | exe.setTarget(target); | |
| 62 | exe.setBuildMode(mode); | |
| 67 | const exe = b.addExecutable(.{ | |
| 68 | .name = "main", | |
| 69 | .optimize = optimize, | |
| 70 | .target = target, | |
| 71 | }); | |
| 63 | 72 | exe.addCSourceFile("main.c", &.{}); |
| 64 | 73 | exe.linkSystemLibraryName("a"); |
| 65 | 74 | exe.linkLibC(); |
test/link/macho/stack_size/build.zig+7-6| ... | ... | @@ -1,16 +1,17 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | const Builder = std.build.Builder; | |
| 3 | 2 | |
| 4 | pub fn build(b: *Builder) void { | |
| 5 | const mode = b.standardReleaseOptions(); | |
| 3 | pub fn build(b: *std.Build) void { | |
| 4 | const optimize = b.standardOptimizeOption(.{}); | |
| 6 | 5 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; |
| 7 | 6 | |
| 8 | 7 | const test_step = b.step("test", "Test"); |
| 9 | 8 | test_step.dependOn(b.getInstallStep()); |
| 10 | 9 | |
| 11 | const exe = b.addExecutable("main", null); | |
| 12 | exe.setTarget(target); | |
| 13 | exe.setBuildMode(mode); | |
| 10 | const exe = b.addExecutable(.{ | |
| 11 | .name = "main", | |
| 12 | .optimize = optimize, | |
| 13 | .target = target, | |
| 14 | }); | |
| 14 | 15 | exe.addCSourceFile("main.c", &.{}); |
| 15 | 16 | exe.linkLibC(); |
| 16 | 17 | exe.stack_size = 0x100000000; |
test/link/macho/strict_validation/build.zig+8-7| ... | ... | @@ -1,18 +1,19 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | const Builder = std.build.Builder; | |
| 4 | const LibExeObjectStep = std.build.LibExeObjStep; | |
| 5 | 3 | |
| 6 | pub fn build(b: *Builder) void { | |
| 7 | const mode = b.standardReleaseOptions(); | |
| 4 | pub fn build(b: *std.Build) void { | |
| 5 | const optimize = b.standardOptimizeOption(.{}); | |
| 8 | 6 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; |
| 9 | 7 | |
| 10 | 8 | const test_step = b.step("test", "Test"); |
| 11 | 9 | test_step.dependOn(b.getInstallStep()); |
| 12 | 10 | |
| 13 | const exe = b.addExecutable("main", "main.zig"); | |
| 14 | exe.setBuildMode(mode); | |
| 15 | exe.setTarget(target); | |
| 11 | const exe = b.addExecutable(.{ | |
| 12 | .name = "main", | |
| 13 | .root_source_file = .{ .path = "main.zig" }, | |
| 14 | .optimize = optimize, | |
| 15 | .target = target, | |
| 16 | }); | |
| 16 | 17 | exe.linkLibC(); |
| 17 | 18 | |
| 18 | 19 | const check_exe = exe.checkObject(.macho); |
test/link/macho/tls/build.zig+13-9| ... | ... | @@ -1,19 +1,23 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | const Builder = std.build.Builder; | |
| 3 | 2 | |
| 4 | pub fn build(b: *Builder) void { | |
| 5 | const mode = b.standardReleaseOptions(); | |
| 3 | pub fn build(b: *std.Build) void { | |
| 4 | const optimize = b.standardOptimizeOption(.{}); | |
| 6 | 5 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; |
| 7 | 6 | |
| 8 | const lib = b.addSharedLibrary("a", null, b.version(1, 0, 0)); | |
| 9 | lib.setBuildMode(mode); | |
| 10 | lib.setTarget(target); | |
| 7 | const lib = b.addSharedLibrary(.{ | |
| 8 | .name = "a", | |
| 9 | .version = .{ .major = 1, .minor = 0 }, | |
| 10 | .optimize = optimize, | |
| 11 | .target = target, | |
| 12 | }); | |
| 11 | 13 | lib.addCSourceFile("a.c", &.{}); |
| 12 | 14 | lib.linkLibC(); |
| 13 | 15 | |
| 14 | const test_exe = b.addTest("main.zig"); | |
| 15 | test_exe.setBuildMode(mode); | |
| 16 | test_exe.setTarget(target); | |
| 16 | const test_exe = b.addTest(.{ | |
| 17 | .root_source_file = .{ .path = "main.zig" }, | |
| 18 | .optimize = optimize, | |
| 19 | .target = target, | |
| 20 | }); | |
| 17 | 21 | test_exe.linkLibrary(lib); |
| 18 | 22 | test_exe.linkLibC(); |
| 19 | 23 |
test/link/macho/unwind_info/build.zig+18-14| ... | ... | @@ -1,26 +1,24 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | const Builder = std.build.Builder; | |
| 4 | const LibExeObjectStep = std.build.LibExeObjStep; | |
| 5 | 3 | |
| 6 | pub fn build(b: *Builder) void { | |
| 7 | const mode = b.standardReleaseOptions(); | |
| 4 | pub fn build(b: *std.Build) void { | |
| 5 | const optimize = b.standardOptimizeOption(.{}); | |
| 8 | 6 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; |
| 9 | 7 | |
| 10 | 8 | const test_step = b.step("test", "Test the program"); |
| 11 | 9 | |
| 12 | testUnwindInfo(b, test_step, mode, target, false); | |
| 13 | testUnwindInfo(b, test_step, mode, target, true); | |
| 10 | testUnwindInfo(b, test_step, optimize, target, false); | |
| 11 | testUnwindInfo(b, test_step, optimize, target, true); | |
| 14 | 12 | } |
| 15 | 13 | |
| 16 | 14 | fn testUnwindInfo( |
| 17 | b: *Builder, | |
| 18 | test_step: *std.build.Step, | |
| 19 | mode: std.builtin.Mode, | |
| 15 | b: *std.Build, | |
| 16 | test_step: *std.Build.Step, | |
| 17 | optimize: std.builtin.OptimizeMode, | |
| 20 | 18 | target: std.zig.CrossTarget, |
| 21 | 19 | dead_strip: bool, |
| 22 | 20 | ) void { |
| 23 | const exe = createScenario(b, mode, target); | |
| 21 | const exe = createScenario(b, optimize, target); | |
| 24 | 22 | exe.link_gc_sections = dead_strip; |
| 25 | 23 | |
| 26 | 24 | const check = exe.checkObject(.macho); |
| ... | ... | @@ -52,8 +50,16 @@ fn testUnwindInfo( |
| 52 | 50 | test_step.dependOn(&run_cmd.step); |
| 53 | 51 | } |
| 54 | 52 | |
| 55 | fn createScenario(b: *Builder, mode: std.builtin.Mode, target: std.zig.CrossTarget) *LibExeObjectStep { | |
| 56 | const exe = b.addExecutable("test", null); | |
| 53 | fn createScenario( | |
| 54 | b: *std.Build, | |
| 55 | optimize: std.builtin.OptimizeMode, | |
| 56 | target: std.zig.CrossTarget, | |
| 57 | ) *std.Build.CompileStep { | |
| 58 | const exe = b.addExecutable(.{ | |
| 59 | .name = "test", | |
| 60 | .optimize = optimize, | |
| 61 | .target = target, | |
| 62 | }); | |
| 57 | 63 | b.default_step.dependOn(&exe.step); |
| 58 | 64 | exe.addIncludePath("."); |
| 59 | 65 | exe.addCSourceFiles(&[_][]const u8{ |
| ... | ... | @@ -61,8 +67,6 @@ fn createScenario(b: *Builder, mode: std.builtin.Mode, target: std.zig.CrossTarg |
| 61 | 67 | "simple_string.cpp", |
| 62 | 68 | "simple_string_owner.cpp", |
| 63 | 69 | }, &[0][]const u8{}); |
| 64 | exe.setBuildMode(mode); | |
| 65 | exe.setTarget(target); | |
| 66 | 70 | exe.linkLibCpp(); |
| 67 | 71 | return exe; |
| 68 | 72 | } |
test/link/macho/uuid/build.zig+17-12| ... | ... | @@ -1,8 +1,6 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | const Builder = std.build.Builder; | |
| 3 | const LibExeObjectStep = std.build.LibExeObjStep; | |
| 4 | 2 | |
| 5 | pub fn build(b: *Builder) void { | |
| 3 | pub fn build(b: *std.Build) void { | |
| 6 | 4 | const test_step = b.step("test", "Test"); |
| 7 | 5 | test_step.dependOn(b.getInstallStep()); |
| 8 | 6 | |
| ... | ... | @@ -27,23 +25,23 @@ pub fn build(b: *Builder) void { |
| 27 | 25 | } |
| 28 | 26 | |
| 29 | 27 | fn testUuid( |
| 30 | b: *Builder, | |
| 31 | test_step: *std.build.Step, | |
| 32 | mode: std.builtin.Mode, | |
| 28 | b: *std.Build, | |
| 29 | test_step: *std.Build.Step, | |
| 30 | optimize: std.builtin.OptimizeMode, | |
| 33 | 31 | target: std.zig.CrossTarget, |
| 34 | 32 | comptime exp: []const u8, |
| 35 | 33 | ) void { |
| 36 | 34 | // The calculated UUID value is independent of debug info and so it should |
| 37 | 35 | // stay the same across builds. |
| 38 | 36 | { |
| 39 | const dylib = simpleDylib(b, mode, target); | |
| 37 | const dylib = simpleDylib(b, optimize, target); | |
| 40 | 38 | const check_dylib = dylib.checkObject(.macho); |
| 41 | 39 | check_dylib.checkStart("cmd UUID"); |
| 42 | 40 | check_dylib.checkNext("uuid " ++ exp); |
| 43 | 41 | test_step.dependOn(&check_dylib.step); |
| 44 | 42 | } |
| 45 | 43 | { |
| 46 | const dylib = simpleDylib(b, mode, target); | |
| 44 | const dylib = simpleDylib(b, optimize, target); | |
| 47 | 45 | dylib.strip = true; |
| 48 | 46 | const check_dylib = dylib.checkObject(.macho); |
| 49 | 47 | check_dylib.checkStart("cmd UUID"); |
| ... | ... | @@ -52,10 +50,17 @@ fn testUuid( |
| 52 | 50 | } |
| 53 | 51 | } |
| 54 | 52 | |
| 55 | fn simpleDylib(b: *Builder, mode: std.builtin.Mode, target: std.zig.CrossTarget) *LibExeObjectStep { | |
| 56 | const dylib = b.addSharedLibrary("test", null, b.version(1, 0, 0)); | |
| 57 | dylib.setTarget(target); | |
| 58 | dylib.setBuildMode(mode); | |
| 53 | fn simpleDylib( | |
| 54 | b: *std.Build, | |
| 55 | optimize: std.builtin.OptimizeMode, | |
| 56 | target: std.zig.CrossTarget, | |
| 57 | ) *std.Build.CompileStep { | |
| 58 | const dylib = b.addSharedLibrary(.{ | |
| 59 | .name = "test", | |
| 60 | .version = .{ .major = 1, .minor = 0 }, | |
| 61 | .optimize = optimize, | |
| 62 | .target = target, | |
| 63 | }); | |
| 59 | 64 | dylib.addCSourceFile("test.c", &.{}); |
| 60 | 65 | dylib.linkLibC(); |
| 61 | 66 | return dylib; |
test/link/macho/weak_framework/build.zig+6-6| ... | ... | @@ -1,16 +1,16 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | const Builder = std.build.Builder; | |
| 3 | const LibExeObjectStep = std.build.LibExeObjStep; | |
| 4 | 2 | |
| 5 | pub fn build(b: *Builder) void { | |
| 6 | const mode = b.standardReleaseOptions(); | |
| 3 | pub fn build(b: *std.Build) void { | |
| 4 | const optimize = b.standardOptimizeOption(.{}); | |
| 7 | 5 | |
| 8 | 6 | const test_step = b.step("test", "Test the program"); |
| 9 | 7 | test_step.dependOn(b.getInstallStep()); |
| 10 | 8 | |
| 11 | const exe = b.addExecutable("test", null); | |
| 9 | const exe = b.addExecutable(.{ | |
| 10 | .name = "test", | |
| 11 | .optimize = optimize, | |
| 12 | }); | |
| 12 | 13 | exe.addCSourceFile("main.c", &[0][]const u8{}); |
| 13 | exe.setBuildMode(mode); | |
| 14 | 14 | exe.linkLibC(); |
| 15 | 15 | exe.linkFrameworkWeak("Cocoa"); |
| 16 | 16 |
test/link/macho/weak_library/build.zig+13-10| ... | ... | @@ -1,25 +1,28 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | const Builder = std.build.Builder; | |
| 3 | const LibExeObjectStep = std.build.LibExeObjStep; | |
| 4 | 2 | |
| 5 | pub fn build(b: *Builder) void { | |
| 6 | const mode = b.standardReleaseOptions(); | |
| 3 | pub fn build(b: *std.Build) void { | |
| 4 | const optimize = b.standardOptimizeOption(.{}); | |
| 7 | 5 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; |
| 8 | 6 | |
| 9 | 7 | const test_step = b.step("test", "Test the program"); |
| 10 | 8 | test_step.dependOn(b.getInstallStep()); |
| 11 | 9 | |
| 12 | const dylib = b.addSharedLibrary("a", null, b.version(1, 0, 0)); | |
| 13 | dylib.setTarget(target); | |
| 14 | dylib.setBuildMode(mode); | |
| 10 | const dylib = b.addSharedLibrary(.{ | |
| 11 | .name = "a", | |
| 12 | .version = .{ .major = 1, .minor = 0, .patch = 0 }, | |
| 13 | .target = target, | |
| 14 | .optimize = optimize, | |
| 15 | }); | |
| 15 | 16 | dylib.addCSourceFile("a.c", &.{}); |
| 16 | 17 | dylib.linkLibC(); |
| 17 | 18 | dylib.install(); |
| 18 | 19 | |
| 19 | const exe = b.addExecutable("test", null); | |
| 20 | const exe = b.addExecutable(.{ | |
| 21 | .name = "test", | |
| 22 | .target = target, | |
| 23 | .optimize = optimize, | |
| 24 | }); | |
| 20 | 25 | exe.addCSourceFile("main.c", &[0][]const u8{}); |
| 21 | exe.setTarget(target); | |
| 22 | exe.setBuildMode(mode); | |
| 23 | 26 | exe.linkLibC(); |
| 24 | 27 | exe.linkSystemLibraryWeak("a"); |
| 25 | 28 | exe.addLibraryPath(b.pathFromRoot("zig-out/lib")); |
test/link/static_lib_as_system_lib/build.zig+13-7| ... | ... | @@ -1,17 +1,23 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | const Builder = std.build.Builder; | |
| 3 | 2 | |
| 4 | pub fn build(b: *Builder) void { | |
| 5 | const mode = b.standardReleaseOptions(); | |
| 3 | pub fn build(b: *std.Build) void { | |
| 4 | const optimize = b.standardOptimizeOption(.{}); | |
| 5 | const target = b.standardTargetOptions(.{}); | |
| 6 | 6 | |
| 7 | const lib_a = b.addStaticLibrary("a", null); | |
| 7 | const lib_a = b.addStaticLibrary(.{ | |
| 8 | .name = "a", | |
| 9 | .optimize = optimize, | |
| 10 | .target = target, | |
| 11 | }); | |
| 8 | 12 | lib_a.addCSourceFile("a.c", &[_][]const u8{}); |
| 9 | lib_a.setBuildMode(mode); | |
| 10 | 13 | lib_a.addIncludePath("."); |
| 11 | 14 | lib_a.install(); |
| 12 | 15 | |
| 13 | const test_exe = b.addTest("main.zig"); | |
| 14 | test_exe.setBuildMode(mode); | |
| 16 | const test_exe = b.addTest(.{ | |
| 17 | .root_source_file = .{ .path = "main.zig" }, | |
| 18 | .optimize = optimize, | |
| 19 | .target = target, | |
| 20 | }); | |
| 15 | 21 | test_exe.linkSystemLibrary("a"); // force linking liba.a as -la |
| 16 | 22 | test_exe.addSystemIncludePath("."); |
| 17 | 23 | const search_path = std.fs.path.join(b.allocator, &[_][]const u8{ b.install_path, "lib" }) catch unreachable; |
test/link/wasm/archive/build.zig+7-7| ... | ... | @@ -1,17 +1,17 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | const Builder = std.build.Builder; | |
| 3 | ||
| 4 | pub fn build(b: *Builder) void { | |
| 5 | const mode = b.standardReleaseOptions(); | |
| 6 | 2 | |
| 3 | pub fn build(b: *std.Build) void { | |
| 7 | 4 | const test_step = b.step("test", "Test"); |
| 8 | 5 | test_step.dependOn(b.getInstallStep()); |
| 9 | 6 | |
| 10 | 7 | // The code in question will pull-in compiler-rt, |
| 11 | 8 | // and therefore link with its archive file. |
| 12 | const lib = b.addSharedLibrary("main", "main.zig", .unversioned); | |
| 13 | lib.setBuildMode(mode); | |
| 14 | lib.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }); | |
| 9 | const lib = b.addSharedLibrary(.{ | |
| 10 | .name = "main", | |
| 11 | .root_source_file = .{ .path = "main.zig" }, | |
| 12 | .optimize = b.standardOptimizeOption(.{}), | |
| 13 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, | |
| 14 | }); | |
| 15 | 15 | lib.use_llvm = false; |
| 16 | 16 | lib.use_lld = false; |
| 17 | 17 | lib.strip = false; |
test/link/wasm/basic-features/build.zig+12-8| ... | ... | @@ -1,14 +1,18 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | |
| 3 | pub fn build(b: *std.build.Builder) void { | |
| 4 | const mode = b.standardReleaseOptions(); | |
| 5 | ||
| 3 | pub fn build(b: *std.Build) void { | |
| 6 | 4 | // Library with explicitly set cpu features |
| 7 | const lib = b.addSharedLibrary("lib", "main.zig", .unversioned); | |
| 8 | lib.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }); | |
| 9 | lib.target.cpu_model = .{ .explicit = &std.Target.wasm.cpu.mvp }; | |
| 10 | lib.target.cpu_features_add.addFeature(0); // index 0 == atomics (see std.Target.wasm.Features) | |
| 11 | lib.setBuildMode(mode); | |
| 5 | const lib = b.addSharedLibrary(.{ | |
| 6 | .name = "lib", | |
| 7 | .root_source_file = .{ .path = "main.zig" }, | |
| 8 | .optimize = b.standardOptimizeOption(.{}), | |
| 9 | .target = .{ | |
| 10 | .cpu_arch = .wasm32, | |
| 11 | .cpu_model = .{ .explicit = &std.Target.wasm.cpu.mvp }, | |
| 12 | .cpu_features_add = std.Target.wasm.featureSet(&.{.atomics}), | |
| 13 | .os_tag = .freestanding, | |
| 14 | }, | |
| 15 | }); | |
| 12 | 16 | lib.use_llvm = false; |
| 13 | 17 | lib.use_lld = false; |
| 14 | 18 |
test/link/wasm/bss/build.zig+7-7| ... | ... | @@ -1,15 +1,15 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | const Builder = std.build.Builder; | |
| 3 | ||
| 4 | pub fn build(b: *Builder) void { | |
| 5 | const mode = b.standardReleaseOptions(); | |
| 6 | 2 | |
| 3 | pub fn build(b: *std.Build) void { | |
| 7 | 4 | const test_step = b.step("test", "Test"); |
| 8 | 5 | test_step.dependOn(b.getInstallStep()); |
| 9 | 6 | |
| 10 | const lib = b.addSharedLibrary("lib", "lib.zig", .unversioned); | |
| 11 | lib.setBuildMode(mode); | |
| 12 | lib.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }); | |
| 7 | const lib = b.addSharedLibrary(.{ | |
| 8 | .name = "lib", | |
| 9 | .root_source_file = .{ .path = "lib.zig" }, | |
| 10 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, | |
| 11 | .optimize = b.standardOptimizeOption(.{}), | |
| 12 | }); | |
| 13 | 13 | lib.use_llvm = false; |
| 14 | 14 | lib.use_lld = false; |
| 15 | 15 | lib.strip = false; |
test/link/wasm/export-data/build.zig+9-7| ... | ... | @@ -1,13 +1,15 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | const Builder = std.build.Builder; | |
| 3 | 2 | |
| 4 | pub fn build(b: *Builder) void { | |
| 3 | pub fn build(b: *std.Build) void { | |
| 5 | 4 | const test_step = b.step("test", "Test"); |
| 6 | 5 | test_step.dependOn(b.getInstallStep()); |
| 7 | 6 | |
| 8 | const lib = b.addSharedLibrary("lib", "lib.zig", .unversioned); | |
| 9 | lib.setBuildMode(.ReleaseSafe); // to make the output deterministic in address positions | |
| 10 | lib.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }); | |
| 7 | const lib = b.addSharedLibrary(.{ | |
| 8 | .name = "lib", | |
| 9 | .root_source_file = .{ .path = "lib.zig" }, | |
| 10 | .optimize = .ReleaseSafe, // to make the output deterministic in address positions | |
| 11 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, | |
| 12 | }); | |
| 11 | 13 | lib.use_lld = false; |
| 12 | 14 | lib.export_symbol_names = &.{ "foo", "bar" }; |
| 13 | 15 | lib.global_base = 0; // put data section at address 0 to make data symbols easier to parse |
| ... | ... | @@ -23,8 +25,8 @@ pub fn build(b: *Builder) void { |
| 23 | 25 | check_lib.checkNext("type i32"); |
| 24 | 26 | check_lib.checkNext("mutable false"); |
| 25 | 27 | check_lib.checkNext("i32.const {bar_address}"); |
| 26 | check_lib.checkComputeCompare("foo_address", .{ .op = .eq, .value = .{ .literal = 0 } }); | |
| 27 | check_lib.checkComputeCompare("bar_address", .{ .op = .eq, .value = .{ .literal = 4 } }); | |
| 28 | check_lib.checkComputeCompare("foo_address", .{ .op = .eq, .value = .{ .literal = 4 } }); | |
| 29 | check_lib.checkComputeCompare("bar_address", .{ .op = .eq, .value = .{ .literal = 0 } }); | |
| 28 | 30 | |
| 29 | 31 | check_lib.checkStart("Section export"); |
| 30 | 32 | check_lib.checkNext("entries 3"); |
test/link/wasm/export/build.zig+21-12| ... | ... | @@ -1,24 +1,33 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | |
| 3 | pub fn build(b: *std.build.Builder) void { | |
| 4 | const mode = b.standardReleaseOptions(); | |
| 5 | ||
| 6 | const no_export = b.addSharedLibrary("no-export", "main.zig", .unversioned); | |
| 7 | no_export.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }); | |
| 8 | no_export.setBuildMode(mode); | |
| 3 | pub fn build(b: *std.Build) void { | |
| 4 | const optimize = b.standardOptimizeOption(.{}); | |
| 5 | ||
| 6 | const no_export = b.addSharedLibrary(.{ | |
| 7 | .name = "no-export", | |
| 8 | .root_source_file = .{ .path = "main.zig" }, | |
| 9 | .optimize = optimize, | |
| 10 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, | |
| 11 | }); | |
| 9 | 12 | no_export.use_llvm = false; |
| 10 | 13 | no_export.use_lld = false; |
| 11 | 14 | |
| 12 | const dynamic_export = b.addSharedLibrary("dynamic", "main.zig", .unversioned); | |
| 13 | dynamic_export.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }); | |
| 14 | dynamic_export.setBuildMode(mode); | |
| 15 | const dynamic_export = b.addSharedLibrary(.{ | |
| 16 | .name = "dynamic", | |
| 17 | .root_source_file = .{ .path = "main.zig" }, | |
| 18 | .optimize = optimize, | |
| 19 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, | |
| 20 | }); | |
| 15 | 21 | dynamic_export.rdynamic = true; |
| 16 | 22 | dynamic_export.use_llvm = false; |
| 17 | 23 | dynamic_export.use_lld = false; |
| 18 | 24 | |
| 19 | const force_export = b.addSharedLibrary("force", "main.zig", .unversioned); | |
| 20 | force_export.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }); | |
| 21 | force_export.setBuildMode(mode); | |
| 25 | const force_export = b.addSharedLibrary(.{ | |
| 26 | .name = "force", | |
| 27 | .root_source_file = .{ .path = "main.zig" }, | |
| 28 | .optimize = optimize, | |
| 29 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, | |
| 30 | }); | |
| 22 | 31 | force_export.export_symbol_names = &.{"foo"}; |
| 23 | 32 | force_export.use_llvm = false; |
| 24 | 33 | force_export.use_lld = false; |
test/link/wasm/extern-mangle/build.zig+7-7| ... | ... | @@ -1,15 +1,15 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | const Builder = std.build.Builder; | |
| 3 | ||
| 4 | pub fn build(b: *Builder) void { | |
| 5 | const mode = b.standardReleaseOptions(); | |
| 6 | 2 | |
| 3 | pub fn build(b: *std.Build) void { | |
| 7 | 4 | const test_step = b.step("test", "Test"); |
| 8 | 5 | test_step.dependOn(b.getInstallStep()); |
| 9 | 6 | |
| 10 | const lib = b.addSharedLibrary("lib", "lib.zig", .unversioned); | |
| 11 | lib.setBuildMode(mode); | |
| 12 | lib.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }); | |
| 7 | const lib = b.addSharedLibrary(.{ | |
| 8 | .name = "lib", | |
| 9 | .root_source_file = .{ .path = "lib.zig" }, | |
| 10 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, | |
| 11 | .optimize = b.standardOptimizeOption(.{}), | |
| 12 | }); | |
| 13 | 13 | lib.import_symbols = true; // import `a` and `b` |
| 14 | 14 | lib.rdynamic = true; // export `foo` |
| 15 | 15 | lib.install(); |
test/link/wasm/extern/build.zig+7-5| ... | ... | @@ -1,10 +1,12 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | |
| 3 | pub fn build(b: *std.build.Builder) void { | |
| 4 | const mode = b.standardReleaseOptions(); | |
| 5 | const exe = b.addExecutable("extern", "main.zig"); | |
| 6 | exe.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .wasi }); | |
| 7 | exe.setBuildMode(mode); | |
| 3 | pub fn build(b: *std.Build) void { | |
| 4 | const exe = b.addExecutable(.{ | |
| 5 | .name = "extern", | |
| 6 | .root_source_file = .{ .path = "main.zig" }, | |
| 7 | .optimize = b.standardOptimizeOption(.{}), | |
| 8 | .target = .{ .cpu_arch = .wasm32, .os_tag = .wasi }, | |
| 9 | }); | |
| 8 | 10 | exe.addCSourceFile("foo.c", &.{}); |
| 9 | 11 | exe.use_llvm = false; |
| 10 | 12 | exe.use_lld = false; |
test/link/wasm/function-table/build.zig+20-12| ... | ... | @@ -1,29 +1,37 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | const Builder = std.build.Builder; | |
| 3 | 2 | |
| 4 | pub fn build(b: *Builder) void { | |
| 5 | const mode = b.standardReleaseOptions(); | |
| 3 | pub fn build(b: *std.Build) void { | |
| 4 | const optimize = b.standardOptimizeOption(.{}); | |
| 6 | 5 | |
| 7 | 6 | const test_step = b.step("test", "Test"); |
| 8 | 7 | test_step.dependOn(b.getInstallStep()); |
| 9 | 8 | |
| 10 | const import_table = b.addSharedLibrary("lib", "lib.zig", .unversioned); | |
| 11 | import_table.setBuildMode(mode); | |
| 12 | import_table.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }); | |
| 9 | const import_table = b.addSharedLibrary(.{ | |
| 10 | .name = "lib", | |
| 11 | .root_source_file = .{ .path = "lib.zig" }, | |
| 12 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, | |
| 13 | .optimize = optimize, | |
| 14 | }); | |
| 13 | 15 | import_table.use_llvm = false; |
| 14 | 16 | import_table.use_lld = false; |
| 15 | 17 | import_table.import_table = true; |
| 16 | 18 | |
| 17 | const export_table = b.addSharedLibrary("lib", "lib.zig", .unversioned); | |
| 18 | export_table.setBuildMode(mode); | |
| 19 | export_table.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }); | |
| 19 | const export_table = b.addSharedLibrary(.{ | |
| 20 | .name = "lib", | |
| 21 | .root_source_file = .{ .path = "lib.zig" }, | |
| 22 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, | |
| 23 | .optimize = optimize, | |
| 24 | }); | |
| 20 | 25 | export_table.use_llvm = false; |
| 21 | 26 | export_table.use_lld = false; |
| 22 | 27 | export_table.export_table = true; |
| 23 | 28 | |
| 24 | const regular_table = b.addSharedLibrary("lib", "lib.zig", .unversioned); | |
| 25 | regular_table.setBuildMode(mode); | |
| 26 | regular_table.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }); | |
| 29 | const regular_table = b.addSharedLibrary(.{ | |
| 30 | .name = "lib", | |
| 31 | .root_source_file = .{ .path = "lib.zig" }, | |
| 32 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, | |
| 33 | .optimize = optimize, | |
| 34 | }); | |
| 27 | 35 | regular_table.use_llvm = false; |
| 28 | 36 | regular_table.use_lld = false; |
| 29 | 37 |
test/link/wasm/infer-features/build.zig+21-10| ... | ... | @@ -1,21 +1,32 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | |
| 3 | pub fn build(b: *std.build.Builder) void { | |
| 4 | const mode = b.standardReleaseOptions(); | |
| 3 | pub fn build(b: *std.Build) void { | |
| 4 | const optimize = b.standardOptimizeOption(.{}); | |
| 5 | 5 | |
| 6 | 6 | // Wasm Object file which we will use to infer the features from |
| 7 | const c_obj = b.addObject("c_obj", null); | |
| 8 | c_obj.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }); | |
| 9 | c_obj.target.cpu_model = .{ .explicit = &std.Target.wasm.cpu.bleeding_edge }; | |
| 7 | const c_obj = b.addObject(.{ | |
| 8 | .name = "c_obj", | |
| 9 | .optimize = optimize, | |
| 10 | .target = .{ | |
| 11 | .cpu_arch = .wasm32, | |
| 12 | .cpu_model = .{ .explicit = &std.Target.wasm.cpu.bleeding_edge }, | |
| 13 | .os_tag = .freestanding, | |
| 14 | }, | |
| 15 | }); | |
| 10 | 16 | c_obj.addCSourceFile("foo.c", &.{}); |
| 11 | c_obj.setBuildMode(mode); | |
| 12 | 17 | |
| 13 | 18 | // Wasm library that doesn't have any features specified. This will |
| 14 | 19 | // infer its featureset from other linked object files. |
| 15 | const lib = b.addSharedLibrary("lib", "main.zig", .unversioned); | |
| 16 | lib.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }); | |
| 17 | lib.target.cpu_model = .{ .explicit = &std.Target.wasm.cpu.mvp }; | |
| 18 | lib.setBuildMode(mode); | |
| 20 | const lib = b.addSharedLibrary(.{ | |
| 21 | .name = "lib", | |
| 22 | .root_source_file = .{ .path = "main.zig" }, | |
| 23 | .optimize = optimize, | |
| 24 | .target = .{ | |
| 25 | .cpu_arch = .wasm32, | |
| 26 | .cpu_model = .{ .explicit = &std.Target.wasm.cpu.mvp }, | |
| 27 | .os_tag = .freestanding, | |
| 28 | }, | |
| 29 | }); | |
| 19 | 30 | lib.use_llvm = false; |
| 20 | 31 | lib.use_lld = false; |
| 21 | 32 | lib.addObject(c_obj); |
test/link/wasm/producers/build.zig+7-7| ... | ... | @@ -1,16 +1,16 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | const Builder = std.build.Builder; | |
| 4 | ||
| 5 | pub fn build(b: *Builder) void { | |
| 6 | const mode = b.standardReleaseOptions(); | |
| 7 | 3 | |
| 4 | pub fn build(b: *std.Build) void { | |
| 8 | 5 | const test_step = b.step("test", "Test"); |
| 9 | 6 | test_step.dependOn(b.getInstallStep()); |
| 10 | 7 | |
| 11 | const lib = b.addSharedLibrary("lib", "lib.zig", .unversioned); | |
| 12 | lib.setBuildMode(mode); | |
| 13 | lib.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }); | |
| 8 | const lib = b.addSharedLibrary(.{ | |
| 9 | .name = "lib", | |
| 10 | .root_source_file = .{ .path = "lib.zig" }, | |
| 11 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, | |
| 12 | .optimize = b.standardOptimizeOption(.{}), | |
| 13 | }); | |
| 14 | 14 | lib.use_llvm = false; |
| 15 | 15 | lib.use_lld = false; |
| 16 | 16 | lib.strip = false; |
test/link/wasm/segments/build.zig+7-7| ... | ... | @@ -1,15 +1,15 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | const Builder = std.build.Builder; | |
| 3 | ||
| 4 | pub fn build(b: *Builder) void { | |
| 5 | const mode = b.standardReleaseOptions(); | |
| 6 | 2 | |
| 3 | pub fn build(b: *std.Build) void { | |
| 7 | 4 | const test_step = b.step("test", "Test"); |
| 8 | 5 | test_step.dependOn(b.getInstallStep()); |
| 9 | 6 | |
| 10 | const lib = b.addSharedLibrary("lib", "lib.zig", .unversioned); | |
| 11 | lib.setBuildMode(mode); | |
| 12 | lib.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }); | |
| 7 | const lib = b.addSharedLibrary(.{ | |
| 8 | .name = "lib", | |
| 9 | .root_source_file = .{ .path = "lib.zig" }, | |
| 10 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, | |
| 11 | .optimize = b.standardOptimizeOption(.{}), | |
| 12 | }); | |
| 13 | 13 | lib.use_llvm = false; |
| 14 | 14 | lib.use_lld = false; |
| 15 | 15 | lib.strip = false; |
test/link/wasm/stack_pointer/build.zig+7-7| ... | ... | @@ -1,15 +1,15 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | const Builder = std.build.Builder; | |
| 3 | ||
| 4 | pub fn build(b: *Builder) void { | |
| 5 | const mode = b.standardReleaseOptions(); | |
| 6 | 2 | |
| 3 | pub fn build(b: *std.Build) void { | |
| 7 | 4 | const test_step = b.step("test", "Test"); |
| 8 | 5 | test_step.dependOn(b.getInstallStep()); |
| 9 | 6 | |
| 10 | const lib = b.addSharedLibrary("lib", "lib.zig", .unversioned); | |
| 11 | lib.setBuildMode(mode); | |
| 12 | lib.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }); | |
| 7 | const lib = b.addSharedLibrary(.{ | |
| 8 | .name = "lib", | |
| 9 | .root_source_file = .{ .path = "lib.zig" }, | |
| 10 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, | |
| 11 | .optimize = b.standardOptimizeOption(.{}), | |
| 12 | }); | |
| 13 | 13 | lib.use_llvm = false; |
| 14 | 14 | lib.use_lld = false; |
| 15 | 15 | lib.strip = false; |
test/link/wasm/type/build.zig+7-7| ... | ... | @@ -1,15 +1,15 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | const Builder = std.build.Builder; | |
| 3 | ||
| 4 | pub fn build(b: *Builder) void { | |
| 5 | const mode = b.standardReleaseOptions(); | |
| 6 | 2 | |
| 3 | pub fn build(b: *std.Build) void { | |
| 7 | 4 | const test_step = b.step("test", "Test"); |
| 8 | 5 | test_step.dependOn(b.getInstallStep()); |
| 9 | 6 | |
| 10 | const lib = b.addSharedLibrary("lib", "lib.zig", .unversioned); | |
| 11 | lib.setBuildMode(mode); | |
| 12 | lib.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }); | |
| 7 | const lib = b.addSharedLibrary(.{ | |
| 8 | .name = "lib", | |
| 9 | .root_source_file = .{ .path = "lib.zig" }, | |
| 10 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, | |
| 11 | .optimize = b.standardOptimizeOption(.{}), | |
| 12 | }); | |
| 13 | 13 | lib.use_llvm = false; |
| 14 | 14 | lib.use_lld = false; |
| 15 | 15 | lib.strip = false; |
test/src/compare_output.zig+25-11| ... | ... | @@ -1,19 +1,18 @@ |
| 1 | 1 | // This is the implementation of the test harness. |
| 2 | 2 | // For the actual test cases, see test/compare_output.zig. |
| 3 | 3 | const std = @import("std"); |
| 4 | const build = std.build; | |
| 5 | 4 | const ArrayList = std.ArrayList; |
| 6 | 5 | const fmt = std.fmt; |
| 7 | 6 | const mem = std.mem; |
| 8 | 7 | const fs = std.fs; |
| 9 | const Mode = std.builtin.Mode; | |
| 8 | const OptimizeMode = std.builtin.OptimizeMode; | |
| 10 | 9 | |
| 11 | 10 | pub const CompareOutputContext = struct { |
| 12 | b: *build.Builder, | |
| 13 | step: *build.Step, | |
| 11 | b: *std.Build, | |
| 12 | step: *std.Build.Step, | |
| 14 | 13 | test_index: usize, |
| 15 | 14 | test_filter: ?[]const u8, |
| 16 | modes: []const Mode, | |
| 15 | optimize_modes: []const OptimizeMode, | |
| 17 | 16 | |
| 18 | 17 | const Special = enum { |
| 19 | 18 | None, |
| ... | ... | @@ -102,7 +101,11 @@ pub const CompareOutputContext = struct { |
| 102 | 101 | if (mem.indexOf(u8, annotated_case_name, filter) == null) return; |
| 103 | 102 | } |
| 104 | 103 | |
| 105 | const exe = b.addExecutable("test", null); | |
| 104 | const exe = b.addExecutable(.{ | |
| 105 | .name = "test", | |
| 106 | .target = .{}, | |
| 107 | .optimize = .Debug, | |
| 108 | }); | |
| 106 | 109 | exe.addAssemblyFileSource(write_src.getFileSource(case.sources.items[0].filename).?); |
| 107 | 110 | |
| 108 | 111 | const run = exe.run(); |
| ... | ... | @@ -113,19 +116,23 @@ pub const CompareOutputContext = struct { |
| 113 | 116 | self.step.dependOn(&run.step); |
| 114 | 117 | }, |
| 115 | 118 | Special.None => { |
| 116 | for (self.modes) |mode| { | |
| 119 | for (self.optimize_modes) |optimize| { | |
| 117 | 120 | const annotated_case_name = fmt.allocPrint(self.b.allocator, "{s} {s} ({s})", .{ |
| 118 | 121 | "compare-output", |
| 119 | 122 | case.name, |
| 120 | @tagName(mode), | |
| 123 | @tagName(optimize), | |
| 121 | 124 | }) catch unreachable; |
| 122 | 125 | if (self.test_filter) |filter| { |
| 123 | 126 | if (mem.indexOf(u8, annotated_case_name, filter) == null) continue; |
| 124 | 127 | } |
| 125 | 128 | |
| 126 | 129 | const basename = case.sources.items[0].filename; |
| 127 | const exe = b.addExecutableSource("test", write_src.getFileSource(basename).?); | |
| 128 | exe.setBuildMode(mode); | |
| 130 | const exe = b.addExecutable(.{ | |
| 131 | .name = "test", | |
| 132 | .root_source_file = write_src.getFileSource(basename).?, | |
| 133 | .optimize = optimize, | |
| 134 | .target = .{}, | |
| 135 | }); | |
| 129 | 136 | if (case.link_libc) { |
| 130 | 137 | exe.linkSystemLibrary("c"); |
| 131 | 138 | } |
| ... | ... | @@ -139,13 +146,20 @@ pub const CompareOutputContext = struct { |
| 139 | 146 | } |
| 140 | 147 | }, |
| 141 | 148 | Special.RuntimeSafety => { |
| 149 | // TODO iterate over self.optimize_modes and test this in both | |
| 150 | // debug and release safe mode | |
| 142 | 151 | const annotated_case_name = fmt.allocPrint(self.b.allocator, "safety {s}", .{case.name}) catch unreachable; |
| 143 | 152 | if (self.test_filter) |filter| { |
| 144 | 153 | if (mem.indexOf(u8, annotated_case_name, filter) == null) return; |
| 145 | 154 | } |
| 146 | 155 | |
| 147 | 156 | const basename = case.sources.items[0].filename; |
| 148 | const exe = b.addExecutableSource("test", write_src.getFileSource(basename).?); | |
| 157 | const exe = b.addExecutable(.{ | |
| 158 | .name = "test", | |
| 159 | .root_source_file = write_src.getFileSource(basename).?, | |
| 160 | .target = .{}, | |
| 161 | .optimize = .Debug, | |
| 162 | }); | |
| 149 | 163 | if (case.link_libc) { |
| 150 | 164 | exe.linkSystemLibrary("c"); |
| 151 | 165 | } |
test/src/run_translated_c.zig+8-6| ... | ... | @@ -1,15 +1,14 @@ |
| 1 | 1 | // This is the implementation of the test harness for running translated |
| 2 | 2 | // C code. For the actual test cases, see test/run_translated_c.zig. |
| 3 | 3 | const std = @import("std"); |
| 4 | const build = std.build; | |
| 5 | 4 | const ArrayList = std.ArrayList; |
| 6 | 5 | const fmt = std.fmt; |
| 7 | 6 | const mem = std.mem; |
| 8 | 7 | const fs = std.fs; |
| 9 | 8 | |
| 10 | 9 | pub const RunTranslatedCContext = struct { |
| 11 | b: *build.Builder, | |
| 12 | step: *build.Step, | |
| 10 | b: *std.Build, | |
| 11 | step: *std.Build.Step, | |
| 13 | 12 | test_index: usize, |
| 14 | 13 | test_filter: ?[]const u8, |
| 15 | 14 | target: std.zig.CrossTarget, |
| ... | ... | @@ -85,11 +84,14 @@ pub const RunTranslatedCContext = struct { |
| 85 | 84 | for (case.sources.items) |src_file| { |
| 86 | 85 | write_src.add(src_file.filename, src_file.source); |
| 87 | 86 | } |
| 88 | const translate_c = b.addTranslateC(write_src.getFileSource(case.sources.items[0].filename).?); | |
| 87 | const translate_c = b.addTranslateC(.{ | |
| 88 | .source_file = write_src.getFileSource(case.sources.items[0].filename).?, | |
| 89 | .target = .{}, | |
| 90 | .optimize = .Debug, | |
| 91 | }); | |
| 89 | 92 | |
| 90 | 93 | translate_c.step.name = b.fmt("{s} translate-c", .{annotated_case_name}); |
| 91 | const exe = translate_c.addExecutable(); | |
| 92 | exe.setTarget(self.target); | |
| 94 | const exe = translate_c.addExecutable(.{}); | |
| 93 | 95 | exe.step.name = b.fmt("{s} build-exe", .{annotated_case_name}); |
| 94 | 96 | exe.linkLibC(); |
| 95 | 97 | const run = exe.run(); |
test/src/translate_c.zig+7-5| ... | ... | @@ -1,7 +1,6 @@ |
| 1 | 1 | // This is the implementation of the test harness. |
| 2 | 2 | // For the actual test cases, see test/translate_c.zig. |
| 3 | 3 | const std = @import("std"); |
| 4 | const build = std.build; | |
| 5 | 4 | const ArrayList = std.ArrayList; |
| 6 | 5 | const fmt = std.fmt; |
| 7 | 6 | const mem = std.mem; |
| ... | ... | @@ -9,8 +8,8 @@ const fs = std.fs; |
| 9 | 8 | const CrossTarget = std.zig.CrossTarget; |
| 10 | 9 | |
| 11 | 10 | pub const TranslateCContext = struct { |
| 12 | b: *build.Builder, | |
| 13 | step: *build.Step, | |
| 11 | b: *std.Build, | |
| 12 | step: *std.Build.Step, | |
| 14 | 13 | test_index: usize, |
| 15 | 14 | test_filter: ?[]const u8, |
| 16 | 15 | |
| ... | ... | @@ -108,10 +107,13 @@ pub const TranslateCContext = struct { |
| 108 | 107 | write_src.add(src_file.filename, src_file.source); |
| 109 | 108 | } |
| 110 | 109 | |
| 111 | const translate_c = b.addTranslateC(write_src.getFileSource(case.sources.items[0].filename).?); | |
| 110 | const translate_c = b.addTranslateC(.{ | |
| 111 | .source_file = write_src.getFileSource(case.sources.items[0].filename).?, | |
| 112 | .target = case.target, | |
| 113 | .optimize = .Debug, | |
| 114 | }); | |
| 112 | 115 | |
| 113 | 116 | translate_c.step.name = annotated_case_name; |
| 114 | translate_c.setTarget(case.target); | |
| 115 | 117 | |
| 116 | 118 | const check_file = translate_c.addCheckFile(case.expected_lines.items); |
| 117 | 119 |
test/standalone/brace_expansion/build.zig+6-4| ... | ... | @@ -1,8 +1,10 @@ |
| 1 | const Builder = @import("std").build.Builder; | |
| 1 | const std = @import("std"); | |
| 2 | 2 | |
| 3 | pub fn build(b: *Builder) void { | |
| 4 | const main = b.addTest("main.zig"); | |
| 5 | main.setBuildMode(b.standardReleaseOptions()); | |
| 3 | pub fn build(b: *std.Build) void { | |
| 4 | const main = b.addTest(.{ | |
| 5 | .root_source_file = .{ .path = "main.zig" }, | |
| 6 | .optimize = b.standardOptimizeOption(.{}), | |
| 7 | }); | |
| 6 | 8 | |
| 7 | 9 | const test_step = b.step("test", "Test it"); |
| 8 | 10 | test_step.dependOn(&main.step); |
test/standalone/c_compiler/build.zig+13-10| ... | ... | @@ -1,9 +1,8 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | const Builder = std.build.Builder; | |
| 4 | 3 | const CrossTarget = std.zig.CrossTarget; |
| 5 | 4 | |
| 6 | // TODO integrate this with the std.build executor API | |
| 5 | // TODO integrate this with the std.Build executor API | |
| 7 | 6 | fn isRunnableTarget(t: CrossTarget) bool { |
| 8 | 7 | if (t.isNative()) return true; |
| 9 | 8 | |
| ... | ... | @@ -11,24 +10,28 @@ fn isRunnableTarget(t: CrossTarget) bool { |
| 11 | 10 | t.getCpuArch() == builtin.cpu.arch); |
| 12 | 11 | } |
| 13 | 12 | |
| 14 | pub fn build(b: *Builder) void { | |
| 15 | const mode = b.standardReleaseOptions(); | |
| 13 | pub fn build(b: *std.Build) void { | |
| 14 | const optimize = b.standardOptimizeOption(.{}); | |
| 16 | 15 | const target = b.standardTargetOptions(.{}); |
| 17 | 16 | |
| 18 | 17 | const test_step = b.step("test", "Test the program"); |
| 19 | 18 | |
| 20 | const exe_c = b.addExecutable("test_c", null); | |
| 19 | const exe_c = b.addExecutable(.{ | |
| 20 | .name = "test_c", | |
| 21 | .optimize = optimize, | |
| 22 | .target = target, | |
| 23 | }); | |
| 21 | 24 | b.default_step.dependOn(&exe_c.step); |
| 22 | 25 | exe_c.addCSourceFile("test.c", &[0][]const u8{}); |
| 23 | exe_c.setBuildMode(mode); | |
| 24 | exe_c.setTarget(target); | |
| 25 | 26 | exe_c.linkLibC(); |
| 26 | 27 | |
| 27 | const exe_cpp = b.addExecutable("test_cpp", null); | |
| 28 | const exe_cpp = b.addExecutable(.{ | |
| 29 | .name = "test_cpp", | |
| 30 | .optimize = optimize, | |
| 31 | .target = target, | |
| 32 | }); | |
| 28 | 33 | b.default_step.dependOn(&exe_cpp.step); |
| 29 | 34 | exe_cpp.addCSourceFile("test.cpp", &[0][]const u8{}); |
| 30 | exe_cpp.setBuildMode(mode); | |
| 31 | exe_cpp.setTarget(target); | |
| 32 | 35 | exe_cpp.linkLibCpp(); |
| 33 | 36 | |
| 34 | 37 | switch (target.getOsTag()) { |
test/standalone/emit_asm_and_bin/build.zig+6-4| ... | ... | @@ -1,8 +1,10 @@ |
| 1 | const Builder = @import("std").build.Builder; | |
| 1 | const std = @import("std"); | |
| 2 | 2 | |
| 3 | pub fn build(b: *Builder) void { | |
| 4 | const main = b.addTest("main.zig"); | |
| 5 | main.setBuildMode(b.standardReleaseOptions()); | |
| 3 | pub fn build(b: *std.Build) void { | |
| 4 | const main = b.addTest(.{ | |
| 5 | .root_source_file = .{ .path = "main.zig" }, | |
| 6 | .optimize = b.standardOptimizeOption(.{}), | |
| 7 | }); | |
| 6 | 8 | main.emit_asm = .{ .emit_to = b.pathFromRoot("main.s") }; |
| 7 | 9 | main.emit_bin = .{ .emit_to = b.pathFromRoot("main") }; |
| 8 | 10 |
test/standalone/empty_env/build.zig+7-4| ... | ... | @@ -1,8 +1,11 @@ |
| 1 | const Builder = @import("std").build.Builder; | |
| 1 | const std = @import("std"); | |
| 2 | 2 | |
| 3 | pub fn build(b: *Builder) void { | |
| 4 | const main = b.addExecutable("main", "main.zig"); | |
| 5 | main.setBuildMode(b.standardReleaseOptions()); | |
| 3 | pub fn build(b: *std.Build) void { | |
| 4 | const main = b.addExecutable(.{ | |
| 5 | .name = "main", | |
| 6 | .root_source_file = .{ .path = "main.zig" }, | |
| 7 | .optimize = b.standardOptimizeOption(.{}), | |
| 8 | }); | |
| 6 | 9 | |
| 7 | 10 | const run = main.run(); |
| 8 | 11 | run.clearEnvironment(); |
test/standalone/global_linkage/build.zig+19-9| ... | ... | @@ -1,16 +1,26 @@ |
| 1 | const Builder = @import("std").build.Builder; | |
| 1 | const std = @import("std"); | |
| 2 | 2 | |
| 3 | pub fn build(b: *Builder) void { | |
| 4 | const mode = b.standardReleaseOptions(); | |
| 3 | pub fn build(b: *std.Build) void { | |
| 4 | const optimize = b.standardOptimizeOption(.{}); | |
| 5 | 5 | |
| 6 | const obj1 = b.addStaticLibrary("obj1", "obj1.zig"); | |
| 7 | obj1.setBuildMode(mode); | |
| 6 | const obj1 = b.addStaticLibrary(.{ | |
| 7 | .name = "obj1", | |
| 8 | .root_source_file = .{ .path = "obj1.zig" }, | |
| 9 | .optimize = optimize, | |
| 10 | .target = .{}, | |
| 11 | }); | |
| 8 | 12 | |
| 9 | const obj2 = b.addStaticLibrary("obj2", "obj2.zig"); | |
| 10 | obj2.setBuildMode(mode); | |
| 13 | const obj2 = b.addStaticLibrary(.{ | |
| 14 | .name = "obj2", | |
| 15 | .root_source_file = .{ .path = "obj2.zig" }, | |
| 16 | .optimize = optimize, | |
| 17 | .target = .{}, | |
| 18 | }); | |
| 11 | 19 | |
| 12 | const main = b.addTest("main.zig"); | |
| 13 | main.setBuildMode(mode); | |
| 20 | const main = b.addTest(.{ | |
| 21 | .root_source_file = .{ .path = "main.zig" }, | |
| 22 | .optimize = optimize, | |
| 23 | }); | |
| 14 | 24 | main.linkLibrary(obj1); |
| 15 | 25 | main.linkLibrary(obj2); |
| 16 | 26 |
test/standalone/install_raw_hex/build.zig+9-6| ... | ... | @@ -1,8 +1,8 @@ |
| 1 | 1 | const builtin = @import("builtin"); |
| 2 | 2 | const std = @import("std"); |
| 3 | const CheckFileStep = std.build.CheckFileStep; | |
| 3 | const CheckFileStep = std.Build.CheckFileStep; | |
| 4 | 4 | |
| 5 | pub fn build(b: *std.build.Builder) void { | |
| 5 | pub fn build(b: *std.Build) void { | |
| 6 | 6 | const target = .{ |
| 7 | 7 | .cpu_arch = .thumb, |
| 8 | 8 | .cpu_model = .{ .explicit = &std.Target.arm.cpu.cortex_m4 }, |
| ... | ... | @@ -10,11 +10,14 @@ pub fn build(b: *std.build.Builder) void { |
| 10 | 10 | .abi = .gnueabihf, |
| 11 | 11 | }; |
| 12 | 12 | |
| 13 | const mode = b.standardReleaseOptions(); | |
| 13 | const optimize = b.standardOptimizeOption(.{}); | |
| 14 | 14 | |
| 15 | const elf = b.addExecutable("zig-nrf52-blink.elf", "main.zig"); | |
| 16 | elf.setTarget(target); | |
| 17 | elf.setBuildMode(mode); | |
| 15 | const elf = b.addExecutable(.{ | |
| 16 | .name = "zig-nrf52-blink.elf", | |
| 17 | .root_source_file = .{ .path = "main.zig" }, | |
| 18 | .target = target, | |
| 19 | .optimize = optimize, | |
| 20 | }); | |
| 18 | 21 | |
| 19 | 22 | const test_step = b.step("test", "Test the program"); |
| 20 | 23 | b.default_step.dependOn(test_step); |
test/standalone/issue_11595/build.zig+9-7| ... | ... | @@ -1,9 +1,8 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | const Builder = std.build.Builder; | |
| 4 | 3 | const CrossTarget = std.zig.CrossTarget; |
| 5 | 4 | |
| 6 | // TODO integrate this with the std.build executor API | |
| 5 | // TODO integrate this with the std.Build executor API | |
| 7 | 6 | fn isRunnableTarget(t: CrossTarget) bool { |
| 8 | 7 | if (t.isNative()) return true; |
| 9 | 8 | |
| ... | ... | @@ -11,12 +10,16 @@ fn isRunnableTarget(t: CrossTarget) bool { |
| 11 | 10 | t.getCpuArch() == builtin.cpu.arch); |
| 12 | 11 | } |
| 13 | 12 | |
| 14 | pub fn build(b: *Builder) void { | |
| 15 | const mode = b.standardReleaseOptions(); | |
| 13 | pub fn build(b: *std.Build) void { | |
| 14 | const optimize = b.standardOptimizeOption(.{}); | |
| 16 | 15 | const target = b.standardTargetOptions(.{}); |
| 17 | 16 | |
| 18 | const exe = b.addExecutable("zigtest", "main.zig"); | |
| 19 | exe.setBuildMode(mode); | |
| 17 | const exe = b.addExecutable(.{ | |
| 18 | .name = "zigtest", | |
| 19 | .root_source_file = .{ .path = "main.zig" }, | |
| 20 | .target = target, | |
| 21 | .optimize = optimize, | |
| 22 | }); | |
| 20 | 23 | exe.install(); |
| 21 | 24 | |
| 22 | 25 | const c_sources = [_][]const u8{ |
| ... | ... | @@ -39,7 +42,6 @@ pub fn build(b: *Builder) void { |
| 39 | 42 | exe.defineCMacro("QUX", "\"Q\" \"UX\""); |
| 40 | 43 | exe.defineCMacro("QUUX", "\"QU\\\"UX\""); |
| 41 | 44 | |
| 42 | exe.setTarget(target); | |
| 43 | 45 | b.default_step.dependOn(&exe.step); |
| 44 | 46 | |
| 45 | 47 | const test_step = b.step("test", "Test the program"); |
test/standalone/issue_12588/build.zig+8-6| ... | ... | @@ -1,13 +1,15 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | const Builder = std.build.Builder; | |
| 3 | 2 | |
| 4 | pub fn build(b: *Builder) void { | |
| 5 | const mode = b.standardReleaseOptions(); | |
| 3 | pub fn build(b: *std.Build) void { | |
| 4 | const optimize = b.standardOptimizeOption(.{}); | |
| 6 | 5 | const target = b.standardTargetOptions(.{}); |
| 7 | 6 | |
| 8 | const obj = b.addObject("main", "main.zig"); | |
| 9 | obj.setBuildMode(mode); | |
| 10 | obj.setTarget(target); | |
| 7 | const obj = b.addObject(.{ | |
| 8 | .name = "main", | |
| 9 | .root_source_file = .{ .path = "main.zig" }, | |
| 10 | .optimize = optimize, | |
| 11 | .target = target, | |
| 12 | }); | |
| 11 | 13 | obj.emit_llvm_ir = .{ .emit_to = b.pathFromRoot("main.ll") }; |
| 12 | 14 | obj.emit_llvm_bc = .{ .emit_to = b.pathFromRoot("main.bc") }; |
| 13 | 15 | obj.emit_bin = .no_emit; |
test/standalone/issue_12706/build.zig+9-7| ... | ... | @@ -1,9 +1,8 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | const Builder = std.build.Builder; | |
| 4 | 3 | const CrossTarget = std.zig.CrossTarget; |
| 5 | 4 | |
| 6 | // TODO integrate this with the std.build executor API | |
| 5 | // TODO integrate this with the std.Build executor API | |
| 7 | 6 | fn isRunnableTarget(t: CrossTarget) bool { |
| 8 | 7 | if (t.isNative()) return true; |
| 9 | 8 | |
| ... | ... | @@ -11,12 +10,16 @@ fn isRunnableTarget(t: CrossTarget) bool { |
| 11 | 10 | t.getCpuArch() == builtin.cpu.arch); |
| 12 | 11 | } |
| 13 | 12 | |
| 14 | pub fn build(b: *Builder) void { | |
| 15 | const mode = b.standardReleaseOptions(); | |
| 13 | pub fn build(b: *std.Build) void { | |
| 14 | const optimize = b.standardOptimizeOption(.{}); | |
| 16 | 15 | const target = b.standardTargetOptions(.{}); |
| 17 | 16 | |
| 18 | const exe = b.addExecutable("main", "main.zig"); | |
| 19 | exe.setBuildMode(mode); | |
| 17 | const exe = b.addExecutable(.{ | |
| 18 | .name = "main", | |
| 19 | .root_source_file = .{ .path = "main.zig" }, | |
| 20 | .optimize = optimize, | |
| 21 | .target = target, | |
| 22 | }); | |
| 20 | 23 | exe.install(); |
| 21 | 24 | |
| 22 | 25 | const c_sources = [_][]const u8{ |
| ... | ... | @@ -26,7 +29,6 @@ pub fn build(b: *Builder) void { |
| 26 | 29 | exe.addCSourceFiles(&c_sources, &.{}); |
| 27 | 30 | exe.linkLibC(); |
| 28 | 31 | |
| 29 | exe.setTarget(target); | |
| 30 | 32 | b.default_step.dependOn(&exe.step); |
| 31 | 33 | |
| 32 | 34 | const test_step = b.step("test", "Test the program"); |
test/standalone/issue_13030/build.zig+8-7| ... | ... | @@ -1,16 +1,17 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | const Builder = std.build.Builder; | |
| 4 | 3 | const CrossTarget = std.zig.CrossTarget; |
| 5 | 4 | |
| 6 | pub fn build(b: *Builder) void { | |
| 7 | const mode = b.standardReleaseOptions(); | |
| 5 | pub fn build(b: *std.Build) void { | |
| 6 | const optimize = b.standardOptimizeOption(.{}); | |
| 8 | 7 | const target = b.standardTargetOptions(.{}); |
| 9 | 8 | |
| 10 | const obj = b.addObject("main", "main.zig"); | |
| 11 | obj.setBuildMode(mode); | |
| 12 | ||
| 13 | obj.setTarget(target); | |
| 9 | const obj = b.addObject(.{ | |
| 10 | .name = "main", | |
| 11 | .root_source_file = .{ .path = "main.zig" }, | |
| 12 | .optimize = optimize, | |
| 13 | .target = target, | |
| 14 | }); | |
| 14 | 15 | b.default_step.dependOn(&obj.step); |
| 15 | 16 | |
| 16 | 17 | const test_step = b.step("test", "Test the program"); |
test/standalone/issue_339/build.zig+8-3| ... | ... | @@ -1,7 +1,12 @@ |
| 1 | const Builder = @import("std").build.Builder; | |
| 1 | const std = @import("std"); | |
| 2 | 2 | |
| 3 | pub fn build(b: *Builder) void { | |
| 4 | const obj = b.addObject("test", "test.zig"); | |
| 3 | pub fn build(b: *std.Build) void { | |
| 4 | const obj = b.addObject(.{ | |
| 5 | .name = "test", | |
| 6 | .root_source_file = .{ .path = "test.zig" }, | |
| 7 | .target = b.standardTargetOptions(.{}), | |
| 8 | .optimize = b.standardOptimizeOption(.{}), | |
| 9 | }); | |
| 5 | 10 | |
| 6 | 11 | const test_step = b.step("test", "Test the program"); |
| 7 | 12 | test_step.dependOn(&obj.step); |
test/standalone/issue_5825/build.zig+14-9| ... | ... | @@ -1,22 +1,27 @@ |
| 1 | const Builder = @import("std").build.Builder; | |
| 1 | const std = @import("std"); | |
| 2 | 2 | |
| 3 | pub fn build(b: *Builder) void { | |
| 3 | pub fn build(b: *std.Build) void { | |
| 4 | 4 | const target = .{ |
| 5 | 5 | .cpu_arch = .x86_64, |
| 6 | 6 | .os_tag = .windows, |
| 7 | 7 | .abi = .msvc, |
| 8 | 8 | }; |
| 9 | const mode = b.standardReleaseOptions(); | |
| 10 | const obj = b.addObject("issue_5825", "main.zig"); | |
| 11 | obj.setTarget(target); | |
| 12 | obj.setBuildMode(mode); | |
| 9 | const optimize = b.standardOptimizeOption(.{}); | |
| 10 | const obj = b.addObject(.{ | |
| 11 | .name = "issue_5825", | |
| 12 | .root_source_file = .{ .path = "main.zig" }, | |
| 13 | .optimize = optimize, | |
| 14 | .target = target, | |
| 15 | }); | |
| 13 | 16 | |
| 14 | const exe = b.addExecutable("issue_5825", null); | |
| 17 | const exe = b.addExecutable(.{ | |
| 18 | .name = "issue_5825", | |
| 19 | .optimize = optimize, | |
| 20 | .target = target, | |
| 21 | }); | |
| 15 | 22 | exe.subsystem = .Console; |
| 16 | 23 | exe.linkSystemLibrary("kernel32"); |
| 17 | 24 | exe.linkSystemLibrary("ntdll"); |
| 18 | exe.setTarget(target); | |
| 19 | exe.setBuildMode(mode); | |
| 20 | 25 | exe.addObject(obj); |
| 21 | 26 | |
| 22 | 27 | const test_step = b.step("test", "Test the program"); |
test/standalone/issue_7030/build.zig+9-6| ... | ... | @@ -1,10 +1,13 @@ |
| 1 | const Builder = @import("std").build.Builder; | |
| 1 | const std = @import("std"); | |
| 2 | 2 | |
| 3 | pub fn build(b: *Builder) void { | |
| 4 | const exe = b.addExecutable("issue_7030", "main.zig"); | |
| 5 | exe.setTarget(.{ | |
| 6 | .cpu_arch = .wasm32, | |
| 7 | .os_tag = .freestanding, | |
| 3 | pub fn build(b: *std.Build) void { | |
| 4 | const exe = b.addExecutable(.{ | |
| 5 | .name = "issue_7030", | |
| 6 | .root_source_file = .{ .path = "main.zig" }, | |
| 7 | .target = .{ | |
| 8 | .cpu_arch = .wasm32, | |
| 9 | .os_tag = .freestanding, | |
| 10 | }, | |
| 8 | 11 | }); |
| 9 | 12 | exe.install(); |
| 10 | 13 | b.default_step.dependOn(&exe.step); |
test/standalone/issue_794/build.zig+5-3| ... | ... | @@ -1,7 +1,9 @@ |
| 1 | const Builder = @import("std").build.Builder; | |
| 1 | const std = @import("std"); | |
| 2 | 2 | |
| 3 | pub fn build(b: *Builder) void { | |
| 4 | const test_artifact = b.addTest("main.zig"); | |
| 3 | pub fn build(b: *std.Build) void { | |
| 4 | const test_artifact = b.addTest(.{ | |
| 5 | .root_source_file = .{ .path = "main.zig" }, | |
| 6 | }); | |
| 5 | 7 | test_artifact.addIncludePath("a_directory"); |
| 6 | 8 | |
| 7 | 9 | b.default_step.dependOn(&test_artifact.step); |
test/standalone/issue_8550/build.zig+8-5| ... | ... | @@ -1,6 +1,6 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | |
| 3 | pub fn build(b: *std.build.Builder) !void { | |
| 3 | pub fn build(b: *std.Build) !void { | |
| 4 | 4 | const target = std.zig.CrossTarget{ |
| 5 | 5 | .os_tag = .freestanding, |
| 6 | 6 | .cpu_arch = .arm, |
| ... | ... | @@ -8,12 +8,15 @@ pub fn build(b: *std.build.Builder) !void { |
| 8 | 8 | .explicit = &std.Target.arm.cpu.arm1176jz_s, |
| 9 | 9 | }, |
| 10 | 10 | }; |
| 11 | const mode = b.standardReleaseOptions(); | |
| 12 | const kernel = b.addExecutable("kernel", "./main.zig"); | |
| 11 | const optimize = b.standardOptimizeOption(.{}); | |
| 12 | const kernel = b.addExecutable(.{ | |
| 13 | .name = "kernel", | |
| 14 | .root_source_file = .{ .path = "./main.zig" }, | |
| 15 | .optimize = optimize, | |
| 16 | .target = target, | |
| 17 | }); | |
| 13 | 18 | kernel.addObjectFile("./boot.S"); |
| 14 | 19 | kernel.setLinkerScriptPath(.{ .path = "./linker.ld" }); |
| 15 | kernel.setBuildMode(mode); | |
| 16 | kernel.setTarget(target); | |
| 17 | 20 | kernel.install(); |
| 18 | 21 | |
| 19 | 22 | const test_step = b.step("test", "Test it"); |
test/standalone/issue_9812/build.zig+6-4| ... | ... | @@ -1,9 +1,11 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | |
| 3 | pub fn build(b: *std.build.Builder) !void { | |
| 4 | const mode = b.standardReleaseOptions(); | |
| 5 | const zip_add = b.addTest("main.zig"); | |
| 6 | zip_add.setBuildMode(mode); | |
| 3 | pub fn build(b: *std.Build) !void { | |
| 4 | const optimize = b.standardOptimizeOption(.{}); | |
| 5 | const zip_add = b.addTest(.{ | |
| 6 | .root_source_file = .{ .path = "main.zig" }, | |
| 7 | .optimize = optimize, | |
| 8 | }); | |
| 7 | 9 | zip_add.addCSourceFile("vendor/kuba-zip/zip.c", &[_][]const u8{ |
| 8 | 10 | "-std=c99", |
| 9 | 11 | "-fno-sanitize=undefined", |
test/standalone/load_dynamic_library/build.zig+17-7| ... | ... | @@ -1,13 +1,23 @@ |
| 1 | const Builder = @import("std").build.Builder; | |
| 1 | const std = @import("std"); | |
| 2 | 2 | |
| 3 | pub fn build(b: *Builder) void { | |
| 4 | const opts = b.standardReleaseOptions(); | |
| 3 | pub fn build(b: *std.Build) void { | |
| 4 | const target = b.standardTargetOptions(.{}); | |
| 5 | const optimize = b.standardOptimizeOption(.{}); | |
| 5 | 6 | |
| 6 | const lib = b.addSharedLibrary("add", "add.zig", b.version(1, 0, 0)); | |
| 7 | lib.setBuildMode(opts); | |
| 7 | const lib = b.addSharedLibrary(.{ | |
| 8 | .name = "add", | |
| 9 | .root_source_file = .{ .path = "add.zig" }, | |
| 10 | .version = .{ .major = 1, .minor = 0 }, | |
| 11 | .optimize = optimize, | |
| 12 | .target = target, | |
| 13 | }); | |
| 8 | 14 | |
| 9 | const main = b.addExecutable("main", "main.zig"); | |
| 10 | main.setBuildMode(opts); | |
| 15 | const main = b.addExecutable(.{ | |
| 16 | .name = "main", | |
| 17 | .root_source_file = .{ .path = "main.zig" }, | |
| 18 | .optimize = optimize, | |
| 19 | .target = target, | |
| 20 | }); | |
| 11 | 21 | |
| 12 | 22 | const run = main.run(); |
| 13 | 23 | run.addArtifactArg(lib); |
test/standalone/main_pkg_path/build.zig+5-3| ... | ... | @@ -1,7 +1,9 @@ |
| 1 | const Builder = @import("std").build.Builder; | |
| 1 | const std = @import("std"); | |
| 2 | 2 | |
| 3 | pub fn build(b: *Builder) void { | |
| 4 | const test_exe = b.addTest("a/test.zig"); | |
| 3 | pub fn build(b: *std.Build) void { | |
| 4 | const test_exe = b.addTest(.{ | |
| 5 | .root_source_file = .{ .path = "a/test.zig" }, | |
| 6 | }); | |
| 5 | 7 | test_exe.setMainPkgPath("."); |
| 6 | 8 | |
| 7 | 9 | const test_step = b.step("test", "Test the program"); |
test/standalone/mix_c_files/build.zig+9-7| ... | ... | @@ -1,9 +1,8 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | const Builder = std.build.Builder; | |
| 4 | 3 | const CrossTarget = std.zig.CrossTarget; |
| 5 | 4 | |
| 6 | // TODO integrate this with the std.build executor API | |
| 5 | // TODO integrate this with the std.Build executor API | |
| 7 | 6 | fn isRunnableTarget(t: CrossTarget) bool { |
| 8 | 7 | if (t.isNative()) return true; |
| 9 | 8 | |
| ... | ... | @@ -11,15 +10,18 @@ fn isRunnableTarget(t: CrossTarget) bool { |
| 11 | 10 | t.getCpuArch() == builtin.cpu.arch); |
| 12 | 11 | } |
| 13 | 12 | |
| 14 | pub fn build(b: *Builder) void { | |
| 15 | const mode = b.standardReleaseOptions(); | |
| 13 | pub fn build(b: *std.Build) void { | |
| 14 | const optimize = b.standardOptimizeOption(.{}); | |
| 16 | 15 | const target = b.standardTargetOptions(.{}); |
| 17 | 16 | |
| 18 | const exe = b.addExecutable("test", "main.zig"); | |
| 17 | const exe = b.addExecutable(.{ | |
| 18 | .name = "test", | |
| 19 | .root_source_file = .{ .path = "main.zig" }, | |
| 20 | .optimize = optimize, | |
| 21 | .target = target, | |
| 22 | }); | |
| 19 | 23 | exe.addCSourceFile("test.c", &[_][]const u8{"-std=c11"}); |
| 20 | exe.setBuildMode(mode); | |
| 21 | 24 | exe.linkLibC(); |
| 22 | exe.setTarget(target); | |
| 23 | 25 | b.default_step.dependOn(&exe.step); |
| 24 | 26 | |
| 25 | 27 | const test_step = b.step("test", "Test the program"); |
test/standalone/mix_o_files/build.zig+14-4| ... | ... | @@ -1,9 +1,19 @@ |
| 1 | const Builder = @import("std").build.Builder; | |
| 1 | const std = @import("std"); | |
| 2 | 2 | |
| 3 | pub fn build(b: *Builder) void { | |
| 4 | const obj = b.addObject("base64", "base64.zig"); | |
| 3 | pub fn build(b: *std.Build) void { | |
| 4 | const optimize = b.standardOptimizeOption(.{}); | |
| 5 | 5 | |
| 6 | const exe = b.addExecutable("test", null); | |
| 6 | const obj = b.addObject(.{ | |
| 7 | .name = "base64", | |
| 8 | .root_source_file = .{ .path = "base64.zig" }, | |
| 9 | .optimize = optimize, | |
| 10 | .target = .{}, | |
| 11 | }); | |
| 12 | ||
| 13 | const exe = b.addExecutable(.{ | |
| 14 | .name = "test", | |
| 15 | .optimize = optimize, | |
| 16 | }); | |
| 7 | 17 | exe.addCSourceFile("test.c", &[_][]const u8{"-std=c99"}); |
| 8 | 18 | exe.addObject(obj); |
| 9 | 19 | exe.linkSystemLibrary("c"); |
test/standalone/options/build.zig+7-5| ... | ... | @@ -1,12 +1,14 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | |
| 3 | pub fn build(b: *std.build.Builder) void { | |
| 3 | pub fn build(b: *std.Build) void { | |
| 4 | 4 | const target = b.standardTargetOptions(.{}); |
| 5 | const mode = b.standardReleaseOptions(); | |
| 5 | const optimize = b.standardOptimizeOption(.{}); | |
| 6 | 6 | |
| 7 | const main = b.addTest("src/main.zig"); | |
| 8 | main.setTarget(target); | |
| 9 | main.setBuildMode(mode); | |
| 7 | const main = b.addTest(.{ | |
| 8 | .root_source_file = .{ .path = "src/main.zig" }, | |
| 9 | .target = target, | |
| 10 | .optimize = optimize, | |
| 11 | }); | |
| 10 | 12 | |
| 11 | 13 | const options = b.addOptions(); |
| 12 | 14 | main.addOptions("build_options", options); |
test/standalone/pie/build.zig+6-4| ... | ... | @@ -1,8 +1,10 @@ |
| 1 | const Builder = @import("std").build.Builder; | |
| 1 | const std = @import("std"); | |
| 2 | 2 | |
| 3 | pub fn build(b: *Builder) void { | |
| 4 | const main = b.addTest("main.zig"); | |
| 5 | main.setBuildMode(b.standardReleaseOptions()); | |
| 3 | pub fn build(b: *std.Build) void { | |
| 4 | const main = b.addTest(.{ | |
| 5 | .root_source_file = .{ .path = "main.zig" }, | |
| 6 | .optimize = b.standardOptimizeOption(.{}), | |
| 7 | }); | |
| 6 | 8 | main.pie = true; |
| 7 | 9 | |
| 8 | 10 | const test_step = b.step("test", "Test the program"); |
test/standalone/pkg_import/build.zig+9-8| ... | ... | @@ -1,13 +1,14 @@ |
| 1 | const Builder = @import("std").build.Builder; | |
| 1 | const std = @import("std"); | |
| 2 | 2 | |
| 3 | pub fn build(b: *Builder) void { | |
| 4 | const exe = b.addExecutable("test", "test.zig"); | |
| 5 | exe.addPackagePath("my_pkg", "pkg.zig"); | |
| 3 | pub fn build(b: *std.Build) void { | |
| 4 | const optimize = b.standardOptimizeOption(.{}); | |
| 6 | 5 | |
| 7 | // This is duplicated to test that you are allowed to call | |
| 8 | // b.standardReleaseOptions() twice. | |
| 9 | exe.setBuildMode(b.standardReleaseOptions()); | |
| 10 | exe.setBuildMode(b.standardReleaseOptions()); | |
| 6 | const exe = b.addExecutable(.{ | |
| 7 | .name = "test", | |
| 8 | .root_source_file = .{ .path = "test.zig" }, | |
| 9 | .optimize = optimize, | |
| 10 | }); | |
| 11 | exe.addPackagePath("my_pkg", "pkg.zig"); | |
| 11 | 12 | |
| 12 | 13 | const run = exe.run(); |
| 13 | 14 |
test/standalone/shared_library/build.zig+15-6| ... | ... | @@ -1,12 +1,21 @@ |
| 1 | const Builder = @import("std").build.Builder; | |
| 1 | const std = @import("std"); | |
| 2 | 2 | |
| 3 | pub fn build(b: *Builder) void { | |
| 3 | pub fn build(b: *std.Build) void { | |
| 4 | const optimize = b.standardOptimizeOption(.{}); | |
| 4 | 5 | const target = b.standardTargetOptions(.{}); |
| 5 | const lib = b.addSharedLibrary("mathtest", "mathtest.zig", b.version(1, 0, 0)); | |
| 6 | lib.setTarget(target); | |
| 6 | const lib = b.addSharedLibrary(.{ | |
| 7 | .name = "mathtest", | |
| 8 | .root_source_file = .{ .path = "mathtest.zig" }, | |
| 9 | .version = .{ .major = 1, .minor = 0 }, | |
| 10 | .target = target, | |
| 11 | .optimize = optimize, | |
| 12 | }); | |
| 7 | 13 | |
| 8 | const exe = b.addExecutable("test", null); | |
| 9 | exe.setTarget(target); | |
| 14 | const exe = b.addExecutable(.{ | |
| 15 | .name = "test", | |
| 16 | .target = target, | |
| 17 | .optimize = optimize, | |
| 18 | }); | |
| 10 | 19 | exe.addCSourceFile("test.c", &[_][]const u8{"-std=c99"}); |
| 11 | 20 | exe.linkLibrary(lib); |
| 12 | 21 | exe.linkSystemLibrary("c"); |
test/standalone/static_c_lib/build.zig+12-7| ... | ... | @@ -1,15 +1,20 @@ |
| 1 | const Builder = @import("std").build.Builder; | |
| 1 | const std = @import("std"); | |
| 2 | 2 | |
| 3 | pub fn build(b: *Builder) void { | |
| 4 | const mode = b.standardReleaseOptions(); | |
| 3 | pub fn build(b: *std.Build) void { | |
| 4 | const optimize = b.standardOptimizeOption(.{}); | |
| 5 | 5 | |
| 6 | const foo = b.addStaticLibrary("foo", null); | |
| 6 | const foo = b.addStaticLibrary(.{ | |
| 7 | .name = "foo", | |
| 8 | .optimize = optimize, | |
| 9 | .target = .{}, | |
| 10 | }); | |
| 7 | 11 | foo.addCSourceFile("foo.c", &[_][]const u8{}); |
| 8 | foo.setBuildMode(mode); | |
| 9 | 12 | foo.addIncludePath("."); |
| 10 | 13 | |
| 11 | const test_exe = b.addTest("foo.zig"); | |
| 12 | test_exe.setBuildMode(mode); | |
| 14 | const test_exe = b.addTest(.{ | |
| 15 | .root_source_file = .{ .path = "foo.zig" }, | |
| 16 | .optimize = optimize, | |
| 17 | }); | |
| 13 | 18 | test_exe.linkLibrary(foo); |
| 14 | 19 | test_exe.addIncludePath("."); |
| 15 | 20 |
test/standalone/test_runner_path/build.zig+6-3| ... | ... | @@ -1,7 +1,10 @@ |
| 1 | const Builder = @import("std").build.Builder; | |
| 1 | const std = @import("std"); | |
| 2 | 2 | |
| 3 | pub fn build(b: *Builder) void { | |
| 4 | const test_exe = b.addTestExe("test", "test.zig"); | |
| 3 | pub fn build(b: *std.Build) void { | |
| 4 | const test_exe = b.addTest(.{ | |
| 5 | .root_source_file = .{ .path = "test.zig" }, | |
| 6 | .kind = .test_exe, | |
| 7 | }); | |
| 5 | 8 | test_exe.test_runner = "test_runner.zig"; |
| 6 | 9 | |
| 7 | 10 | const test_run = test_exe.run(); |
test/standalone/use_alias/build.zig+6-4| ... | ... | @@ -1,8 +1,10 @@ |
| 1 | const Builder = @import("std").build.Builder; | |
| 1 | const std = @import("std"); | |
| 2 | 2 | |
| 3 | pub fn build(b: *Builder) void { | |
| 4 | const main = b.addTest("main.zig"); | |
| 5 | main.setBuildMode(b.standardReleaseOptions()); | |
| 3 | pub fn build(b: *std.Build) void { | |
| 4 | const main = b.addTest(.{ | |
| 5 | .root_source_file = .{ .path = "main.zig" }, | |
| 6 | .optimize = b.standardOptimizeOption(.{}), | |
| 7 | }); | |
| 6 | 8 | main.addIncludePath("."); |
| 7 | 9 | |
| 8 | 10 | const test_step = b.step("test", "Test it"); |
test/standalone/windows_spawn/build.zig+14-7| ... | ... | @@ -1,13 +1,20 @@ |
| 1 | const Builder = @import("std").build.Builder; | |
| 1 | const std = @import("std"); | |
| 2 | 2 | |
| 3 | pub fn build(b: *Builder) void { | |
| 4 | const mode = b.standardReleaseOptions(); | |
| 3 | pub fn build(b: *std.Build) void { | |
| 4 | const optimize = b.standardOptimizeOption(.{}); | |
| 5 | 5 | |
| 6 | const hello = b.addExecutable("hello", "hello.zig"); | |
| 7 | hello.setBuildMode(mode); | |
| 6 | const hello = b.addExecutable(.{ | |
| 7 | .name = "hello", | |
| 8 | .root_source_file = .{ .path = "hello.zig" }, | |
| 9 | .optimize = optimize, | |
| 10 | }); | |
| 11 | ||
| 12 | const main = b.addExecutable(.{ | |
| 13 | .name = "main", | |
| 14 | .root_source_file = .{ .path = "main.zig" }, | |
| 15 | .optimize = optimize, | |
| 16 | }); | |
| 8 | 17 | |
| 9 | const main = b.addExecutable("main", "main.zig"); | |
| 10 | main.setBuildMode(mode); | |
| 11 | 18 | const run = main.run(); |
| 12 | 19 | run.addArtifactArg(hello); |
| 13 | 20 |
test/tests.zig+117-100| ... | ... | @@ -1,17 +1,17 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | const debug = std.debug; |
| 4 | const build = std.build; | |
| 5 | 4 | const CrossTarget = std.zig.CrossTarget; |
| 6 | 5 | const io = std.io; |
| 7 | 6 | const fs = std.fs; |
| 8 | 7 | const mem = std.mem; |
| 9 | 8 | const fmt = std.fmt; |
| 10 | 9 | const ArrayList = std.ArrayList; |
| 11 | const Mode = std.builtin.Mode; | |
| 12 | const LibExeObjStep = build.LibExeObjStep; | |
| 10 | const OptimizeMode = std.builtin.OptimizeMode; | |
| 11 | const CompileStep = std.Build.CompileStep; | |
| 13 | 12 | const Allocator = mem.Allocator; |
| 14 | const ExecError = build.Builder.ExecError; | |
| 13 | const ExecError = std.Build.ExecError; | |
| 14 | const Step = std.Build.Step; | |
| 15 | 15 | |
| 16 | 16 | // Cases |
| 17 | 17 | const compare_output = @import("compare_output.zig"); |
| ... | ... | @@ -30,7 +30,7 @@ pub const CompareOutputContext = @import("src/compare_output.zig").CompareOutput |
| 30 | 30 | |
| 31 | 31 | const TestTarget = struct { |
| 32 | 32 | target: CrossTarget = @as(CrossTarget, .{}), |
| 33 | mode: std.builtin.Mode = .Debug, | |
| 33 | optimize_mode: std.builtin.OptimizeMode = .Debug, | |
| 34 | 34 | link_libc: bool = false, |
| 35 | 35 | single_threaded: bool = false, |
| 36 | 36 | disable_native: bool = false, |
| ... | ... | @@ -423,38 +423,38 @@ const test_targets = blk: { |
| 423 | 423 | |
| 424 | 424 | // Do the release tests last because they take a long time |
| 425 | 425 | .{ |
| 426 | .mode = .ReleaseFast, | |
| 426 | .optimize_mode = .ReleaseFast, | |
| 427 | 427 | }, |
| 428 | 428 | .{ |
| 429 | 429 | .link_libc = true, |
| 430 | .mode = .ReleaseFast, | |
| 430 | .optimize_mode = .ReleaseFast, | |
| 431 | 431 | }, |
| 432 | 432 | .{ |
| 433 | .mode = .ReleaseFast, | |
| 433 | .optimize_mode = .ReleaseFast, | |
| 434 | 434 | .single_threaded = true, |
| 435 | 435 | }, |
| 436 | 436 | |
| 437 | 437 | .{ |
| 438 | .mode = .ReleaseSafe, | |
| 438 | .optimize_mode = .ReleaseSafe, | |
| 439 | 439 | }, |
| 440 | 440 | .{ |
| 441 | 441 | .link_libc = true, |
| 442 | .mode = .ReleaseSafe, | |
| 442 | .optimize_mode = .ReleaseSafe, | |
| 443 | 443 | }, |
| 444 | 444 | .{ |
| 445 | .mode = .ReleaseSafe, | |
| 445 | .optimize_mode = .ReleaseSafe, | |
| 446 | 446 | .single_threaded = true, |
| 447 | 447 | }, |
| 448 | 448 | |
| 449 | 449 | .{ |
| 450 | .mode = .ReleaseSmall, | |
| 450 | .optimize_mode = .ReleaseSmall, | |
| 451 | 451 | }, |
| 452 | 452 | .{ |
| 453 | 453 | .link_libc = true, |
| 454 | .mode = .ReleaseSmall, | |
| 454 | .optimize_mode = .ReleaseSmall, | |
| 455 | 455 | }, |
| 456 | 456 | .{ |
| 457 | .mode = .ReleaseSmall, | |
| 457 | .optimize_mode = .ReleaseSmall, | |
| 458 | 458 | .single_threaded = true, |
| 459 | 459 | }, |
| 460 | 460 | }; |
| ... | ... | @@ -462,14 +462,14 @@ const test_targets = blk: { |
| 462 | 462 | |
| 463 | 463 | const max_stdout_size = 1 * 1024 * 1024; // 1 MB |
| 464 | 464 | |
| 465 | pub fn addCompareOutputTests(b: *build.Builder, test_filter: ?[]const u8, modes: []const Mode) *build.Step { | |
| 465 | pub fn addCompareOutputTests(b: *std.Build, test_filter: ?[]const u8, optimize_modes: []const OptimizeMode) *Step { | |
| 466 | 466 | const cases = b.allocator.create(CompareOutputContext) catch unreachable; |
| 467 | 467 | cases.* = CompareOutputContext{ |
| 468 | 468 | .b = b, |
| 469 | 469 | .step = b.step("test-compare-output", "Run the compare output tests"), |
| 470 | 470 | .test_index = 0, |
| 471 | 471 | .test_filter = test_filter, |
| 472 | .modes = modes, | |
| 472 | .optimize_modes = optimize_modes, | |
| 473 | 473 | }; |
| 474 | 474 | |
| 475 | 475 | compare_output.addCases(cases); |
| ... | ... | @@ -477,14 +477,14 @@ pub fn addCompareOutputTests(b: *build.Builder, test_filter: ?[]const u8, modes: |
| 477 | 477 | return cases.step; |
| 478 | 478 | } |
| 479 | 479 | |
| 480 | pub fn addStackTraceTests(b: *build.Builder, test_filter: ?[]const u8, modes: []const Mode) *build.Step { | |
| 480 | pub fn addStackTraceTests(b: *std.Build, test_filter: ?[]const u8, optimize_modes: []const OptimizeMode) *Step { | |
| 481 | 481 | const cases = b.allocator.create(StackTracesContext) catch unreachable; |
| 482 | 482 | cases.* = StackTracesContext{ |
| 483 | 483 | .b = b, |
| 484 | 484 | .step = b.step("test-stack-traces", "Run the stack trace tests"), |
| 485 | 485 | .test_index = 0, |
| 486 | 486 | .test_filter = test_filter, |
| 487 | .modes = modes, | |
| 487 | .optimize_modes = optimize_modes, | |
| 488 | 488 | }; |
| 489 | 489 | |
| 490 | 490 | stack_traces.addCases(cases); |
| ... | ... | @@ -493,9 +493,9 @@ pub fn addStackTraceTests(b: *build.Builder, test_filter: ?[]const u8, modes: [] |
| 493 | 493 | } |
| 494 | 494 | |
| 495 | 495 | pub fn addStandaloneTests( |
| 496 | b: *build.Builder, | |
| 496 | b: *std.Build, | |
| 497 | 497 | test_filter: ?[]const u8, |
| 498 | modes: []const Mode, | |
| 498 | optimize_modes: []const OptimizeMode, | |
| 499 | 499 | skip_non_native: bool, |
| 500 | 500 | enable_macos_sdk: bool, |
| 501 | 501 | target: std.zig.CrossTarget, |
| ... | ... | @@ -506,14 +506,14 @@ pub fn addStandaloneTests( |
| 506 | 506 | enable_wasmtime: bool, |
| 507 | 507 | enable_wine: bool, |
| 508 | 508 | enable_symlinks_windows: bool, |
| 509 | ) *build.Step { | |
| 509 | ) *Step { | |
| 510 | 510 | const cases = b.allocator.create(StandaloneContext) catch unreachable; |
| 511 | 511 | cases.* = StandaloneContext{ |
| 512 | 512 | .b = b, |
| 513 | 513 | .step = b.step("test-standalone", "Run the standalone tests"), |
| 514 | 514 | .test_index = 0, |
| 515 | 515 | .test_filter = test_filter, |
| 516 | .modes = modes, | |
| 516 | .optimize_modes = optimize_modes, | |
| 517 | 517 | .skip_non_native = skip_non_native, |
| 518 | 518 | .enable_macos_sdk = enable_macos_sdk, |
| 519 | 519 | .target = target, |
| ... | ... | @@ -532,20 +532,20 @@ pub fn addStandaloneTests( |
| 532 | 532 | } |
| 533 | 533 | |
| 534 | 534 | pub fn addLinkTests( |
| 535 | b: *build.Builder, | |
| 535 | b: *std.Build, | |
| 536 | 536 | test_filter: ?[]const u8, |
| 537 | modes: []const Mode, | |
| 537 | optimize_modes: []const OptimizeMode, | |
| 538 | 538 | enable_macos_sdk: bool, |
| 539 | 539 | omit_stage2: bool, |
| 540 | 540 | enable_symlinks_windows: bool, |
| 541 | ) *build.Step { | |
| 541 | ) *Step { | |
| 542 | 542 | const cases = b.allocator.create(StandaloneContext) catch unreachable; |
| 543 | 543 | cases.* = StandaloneContext{ |
| 544 | 544 | .b = b, |
| 545 | 545 | .step = b.step("test-link", "Run the linker tests"), |
| 546 | 546 | .test_index = 0, |
| 547 | 547 | .test_filter = test_filter, |
| 548 | .modes = modes, | |
| 548 | .optimize_modes = optimize_modes, | |
| 549 | 549 | .skip_non_native = true, |
| 550 | 550 | .enable_macos_sdk = enable_macos_sdk, |
| 551 | 551 | .target = .{}, |
| ... | ... | @@ -556,12 +556,17 @@ pub fn addLinkTests( |
| 556 | 556 | return cases.step; |
| 557 | 557 | } |
| 558 | 558 | |
| 559 | pub fn addCliTests(b: *build.Builder, test_filter: ?[]const u8, modes: []const Mode) *build.Step { | |
| 559 | pub fn addCliTests(b: *std.Build, test_filter: ?[]const u8, optimize_modes: []const OptimizeMode) *Step { | |
| 560 | 560 | _ = test_filter; |
| 561 | _ = modes; | |
| 561 | _ = optimize_modes; | |
| 562 | 562 | const step = b.step("test-cli", "Test the command line interface"); |
| 563 | 563 | |
| 564 | const exe = b.addExecutable("test-cli", "test/cli.zig"); | |
| 564 | const exe = b.addExecutable(.{ | |
| 565 | .name = "test-cli", | |
| 566 | .root_source_file = .{ .path = "test/cli.zig" }, | |
| 567 | .target = .{}, | |
| 568 | .optimize = .Debug, | |
| 569 | }); | |
| 565 | 570 | const run_cmd = exe.run(); |
| 566 | 571 | run_cmd.addArgs(&[_][]const u8{ |
| 567 | 572 | fs.realpathAlloc(b.allocator, b.zig_exe) catch unreachable, |
| ... | ... | @@ -572,14 +577,14 @@ pub fn addCliTests(b: *build.Builder, test_filter: ?[]const u8, modes: []const M |
| 572 | 577 | return step; |
| 573 | 578 | } |
| 574 | 579 | |
| 575 | pub fn addAssembleAndLinkTests(b: *build.Builder, test_filter: ?[]const u8, modes: []const Mode) *build.Step { | |
| 580 | pub fn addAssembleAndLinkTests(b: *std.Build, test_filter: ?[]const u8, optimize_modes: []const OptimizeMode) *Step { | |
| 576 | 581 | const cases = b.allocator.create(CompareOutputContext) catch unreachable; |
| 577 | 582 | cases.* = CompareOutputContext{ |
| 578 | 583 | .b = b, |
| 579 | 584 | .step = b.step("test-asm-link", "Run the assemble and link tests"), |
| 580 | 585 | .test_index = 0, |
| 581 | 586 | .test_filter = test_filter, |
| 582 | .modes = modes, | |
| 587 | .optimize_modes = optimize_modes, | |
| 583 | 588 | }; |
| 584 | 589 | |
| 585 | 590 | assemble_and_link.addCases(cases); |
| ... | ... | @@ -587,7 +592,7 @@ pub fn addAssembleAndLinkTests(b: *build.Builder, test_filter: ?[]const u8, mode |
| 587 | 592 | return cases.step; |
| 588 | 593 | } |
| 589 | 594 | |
| 590 | pub fn addTranslateCTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step { | |
| 595 | pub fn addTranslateCTests(b: *std.Build, test_filter: ?[]const u8) *Step { | |
| 591 | 596 | const cases = b.allocator.create(TranslateCContext) catch unreachable; |
| 592 | 597 | cases.* = TranslateCContext{ |
| 593 | 598 | .b = b, |
| ... | ... | @@ -602,10 +607,10 @@ pub fn addTranslateCTests(b: *build.Builder, test_filter: ?[]const u8) *build.St |
| 602 | 607 | } |
| 603 | 608 | |
| 604 | 609 | pub fn addRunTranslatedCTests( |
| 605 | b: *build.Builder, | |
| 610 | b: *std.Build, | |
| 606 | 611 | test_filter: ?[]const u8, |
| 607 | 612 | target: std.zig.CrossTarget, |
| 608 | ) *build.Step { | |
| 613 | ) *Step { | |
| 609 | 614 | const cases = b.allocator.create(RunTranslatedCContext) catch unreachable; |
| 610 | 615 | cases.* = .{ |
| 611 | 616 | .b = b, |
| ... | ... | @@ -620,7 +625,7 @@ pub fn addRunTranslatedCTests( |
| 620 | 625 | return cases.step; |
| 621 | 626 | } |
| 622 | 627 | |
| 623 | pub fn addGenHTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step { | |
| 628 | pub fn addGenHTests(b: *std.Build, test_filter: ?[]const u8) *Step { | |
| 624 | 629 | const cases = b.allocator.create(GenHContext) catch unreachable; |
| 625 | 630 | cases.* = GenHContext{ |
| 626 | 631 | .b = b, |
| ... | ... | @@ -635,18 +640,18 @@ pub fn addGenHTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step { |
| 635 | 640 | } |
| 636 | 641 | |
| 637 | 642 | pub fn addPkgTests( |
| 638 | b: *build.Builder, | |
| 643 | b: *std.Build, | |
| 639 | 644 | test_filter: ?[]const u8, |
| 640 | 645 | root_src: []const u8, |
| 641 | 646 | name: []const u8, |
| 642 | 647 | desc: []const u8, |
| 643 | modes: []const Mode, | |
| 648 | optimize_modes: []const OptimizeMode, | |
| 644 | 649 | skip_single_threaded: bool, |
| 645 | 650 | skip_non_native: bool, |
| 646 | 651 | skip_libc: bool, |
| 647 | 652 | skip_stage1: bool, |
| 648 | 653 | skip_stage2: bool, |
| 649 | ) *build.Step { | |
| 654 | ) *Step { | |
| 650 | 655 | const step = b.step(b.fmt("test-{s}", .{name}), desc); |
| 651 | 656 | |
| 652 | 657 | for (test_targets) |test_target| { |
| ... | ... | @@ -677,8 +682,8 @@ pub fn addPkgTests( |
| 677 | 682 | else => if (skip_stage2) continue, |
| 678 | 683 | }; |
| 679 | 684 | |
| 680 | const want_this_mode = for (modes) |m| { | |
| 681 | if (m == test_target.mode) break true; | |
| 685 | const want_this_mode = for (optimize_modes) |m| { | |
| 686 | if (m == test_target.optimize_mode) break true; | |
| 682 | 687 | } else false; |
| 683 | 688 | if (!want_this_mode) continue; |
| 684 | 689 | |
| ... | ... | @@ -691,21 +696,23 @@ pub fn addPkgTests( |
| 691 | 696 | |
| 692 | 697 | const triple_prefix = test_target.target.zigTriple(b.allocator) catch unreachable; |
| 693 | 698 | |
| 694 | const these_tests = b.addTest(root_src); | |
| 699 | const these_tests = b.addTest(.{ | |
| 700 | .root_source_file = .{ .path = root_src }, | |
| 701 | .optimize = test_target.optimize_mode, | |
| 702 | .target = test_target.target, | |
| 703 | }); | |
| 695 | 704 | const single_threaded_txt = if (test_target.single_threaded) "single" else "multi"; |
| 696 | 705 | const backend_txt = if (test_target.backend) |backend| @tagName(backend) else "default"; |
| 697 | 706 | these_tests.setNamePrefix(b.fmt("{s}-{s}-{s}-{s}-{s}-{s} ", .{ |
| 698 | 707 | name, |
| 699 | 708 | triple_prefix, |
| 700 | @tagName(test_target.mode), | |
| 709 | @tagName(test_target.optimize_mode), | |
| 701 | 710 | libc_prefix, |
| 702 | 711 | single_threaded_txt, |
| 703 | 712 | backend_txt, |
| 704 | 713 | })); |
| 705 | 714 | these_tests.single_threaded = test_target.single_threaded; |
| 706 | 715 | these_tests.setFilter(test_filter); |
| 707 | these_tests.setBuildMode(test_target.mode); | |
| 708 | these_tests.setTarget(test_target.target); | |
| 709 | 716 | if (test_target.link_libc) { |
| 710 | 717 | these_tests.linkSystemLibrary("c"); |
| 711 | 718 | } |
| ... | ... | @@ -735,13 +742,13 @@ pub fn addPkgTests( |
| 735 | 742 | } |
| 736 | 743 | |
| 737 | 744 | pub const StackTracesContext = struct { |
| 738 | b: *build.Builder, | |
| 739 | step: *build.Step, | |
| 745 | b: *std.Build, | |
| 746 | step: *Step, | |
| 740 | 747 | test_index: usize, |
| 741 | 748 | test_filter: ?[]const u8, |
| 742 | modes: []const Mode, | |
| 749 | optimize_modes: []const OptimizeMode, | |
| 743 | 750 | |
| 744 | const Expect = [@typeInfo(Mode).Enum.fields.len][]const u8; | |
| 751 | const Expect = [@typeInfo(OptimizeMode).Enum.fields.len][]const u8; | |
| 745 | 752 | |
| 746 | 753 | pub fn addCase(self: *StackTracesContext, config: anytype) void { |
| 747 | 754 | if (@hasField(@TypeOf(config), "exclude")) { |
| ... | ... | @@ -755,26 +762,26 @@ pub const StackTracesContext = struct { |
| 755 | 762 | const exclude_os: []const std.Target.Os.Tag = &config.exclude_os; |
| 756 | 763 | for (exclude_os) |os| if (os == builtin.os.tag) return; |
| 757 | 764 | } |
| 758 | for (self.modes) |mode| { | |
| 759 | switch (mode) { | |
| 765 | for (self.optimize_modes) |optimize_mode| { | |
| 766 | switch (optimize_mode) { | |
| 760 | 767 | .Debug => { |
| 761 | 768 | if (@hasField(@TypeOf(config), "Debug")) { |
| 762 | self.addExpect(config.name, config.source, mode, config.Debug); | |
| 769 | self.addExpect(config.name, config.source, optimize_mode, config.Debug); | |
| 763 | 770 | } |
| 764 | 771 | }, |
| 765 | 772 | .ReleaseSafe => { |
| 766 | 773 | if (@hasField(@TypeOf(config), "ReleaseSafe")) { |
| 767 | self.addExpect(config.name, config.source, mode, config.ReleaseSafe); | |
| 774 | self.addExpect(config.name, config.source, optimize_mode, config.ReleaseSafe); | |
| 768 | 775 | } |
| 769 | 776 | }, |
| 770 | 777 | .ReleaseFast => { |
| 771 | 778 | if (@hasField(@TypeOf(config), "ReleaseFast")) { |
| 772 | self.addExpect(config.name, config.source, mode, config.ReleaseFast); | |
| 779 | self.addExpect(config.name, config.source, optimize_mode, config.ReleaseFast); | |
| 773 | 780 | } |
| 774 | 781 | }, |
| 775 | 782 | .ReleaseSmall => { |
| 776 | 783 | if (@hasField(@TypeOf(config), "ReleaseSmall")) { |
| 777 | self.addExpect(config.name, config.source, mode, config.ReleaseSmall); | |
| 784 | self.addExpect(config.name, config.source, optimize_mode, config.ReleaseSmall); | |
| 778 | 785 | } |
| 779 | 786 | }, |
| 780 | 787 | } |
| ... | ... | @@ -785,7 +792,7 @@ pub const StackTracesContext = struct { |
| 785 | 792 | self: *StackTracesContext, |
| 786 | 793 | name: []const u8, |
| 787 | 794 | source: []const u8, |
| 788 | mode: Mode, | |
| 795 | optimize_mode: OptimizeMode, | |
| 789 | 796 | mode_config: anytype, |
| 790 | 797 | ) void { |
| 791 | 798 | if (@hasField(@TypeOf(mode_config), "exclude")) { |
| ... | ... | @@ -803,7 +810,7 @@ pub const StackTracesContext = struct { |
| 803 | 810 | const annotated_case_name = fmt.allocPrint(self.b.allocator, "{s} {s} ({s})", .{ |
| 804 | 811 | "stack-trace", |
| 805 | 812 | name, |
| 806 | @tagName(mode), | |
| 813 | @tagName(optimize_mode), | |
| 807 | 814 | }) catch unreachable; |
| 808 | 815 | if (self.test_filter) |filter| { |
| 809 | 816 | if (mem.indexOf(u8, annotated_case_name, filter) == null) return; |
| ... | ... | @@ -812,14 +819,18 @@ pub const StackTracesContext = struct { |
| 812 | 819 | const b = self.b; |
| 813 | 820 | const src_basename = "source.zig"; |
| 814 | 821 | const write_src = b.addWriteFile(src_basename, source); |
| 815 | const exe = b.addExecutableSource("test", write_src.getFileSource(src_basename).?); | |
| 816 | exe.setBuildMode(mode); | |
| 822 | const exe = b.addExecutable(.{ | |
| 823 | .name = "test", | |
| 824 | .root_source_file = write_src.getFileSource(src_basename).?, | |
| 825 | .optimize = optimize_mode, | |
| 826 | .target = .{}, | |
| 827 | }); | |
| 817 | 828 | |
| 818 | 829 | const run_and_compare = RunAndCompareStep.create( |
| 819 | 830 | self, |
| 820 | 831 | exe, |
| 821 | 832 | annotated_case_name, |
| 822 | mode, | |
| 833 | optimize_mode, | |
| 823 | 834 | mode_config.expect, |
| 824 | 835 | ); |
| 825 | 836 | |
| ... | ... | @@ -829,29 +840,29 @@ pub const StackTracesContext = struct { |
| 829 | 840 | const RunAndCompareStep = struct { |
| 830 | 841 | pub const base_id = .custom; |
| 831 | 842 | |
| 832 | step: build.Step, | |
| 843 | step: Step, | |
| 833 | 844 | context: *StackTracesContext, |
| 834 | exe: *LibExeObjStep, | |
| 845 | exe: *CompileStep, | |
| 835 | 846 | name: []const u8, |
| 836 | mode: Mode, | |
| 847 | optimize_mode: OptimizeMode, | |
| 837 | 848 | expect_output: []const u8, |
| 838 | 849 | test_index: usize, |
| 839 | 850 | |
| 840 | 851 | pub fn create( |
| 841 | 852 | context: *StackTracesContext, |
| 842 | exe: *LibExeObjStep, | |
| 853 | exe: *CompileStep, | |
| 843 | 854 | name: []const u8, |
| 844 | mode: Mode, | |
| 855 | optimize_mode: OptimizeMode, | |
| 845 | 856 | expect_output: []const u8, |
| 846 | 857 | ) *RunAndCompareStep { |
| 847 | 858 | const allocator = context.b.allocator; |
| 848 | 859 | const ptr = allocator.create(RunAndCompareStep) catch unreachable; |
| 849 | 860 | ptr.* = RunAndCompareStep{ |
| 850 | .step = build.Step.init(.custom, "StackTraceCompareOutputStep", allocator, make), | |
| 861 | .step = Step.init(.custom, "StackTraceCompareOutputStep", allocator, make), | |
| 851 | 862 | .context = context, |
| 852 | 863 | .exe = exe, |
| 853 | 864 | .name = name, |
| 854 | .mode = mode, | |
| 865 | .optimize_mode = optimize_mode, | |
| 855 | 866 | .expect_output = expect_output, |
| 856 | 867 | .test_index = context.test_index, |
| 857 | 868 | }; |
| ... | ... | @@ -860,7 +871,7 @@ pub const StackTracesContext = struct { |
| 860 | 871 | return ptr; |
| 861 | 872 | } |
| 862 | 873 | |
| 863 | fn make(step: *build.Step) !void { | |
| 874 | fn make(step: *Step) !void { | |
| 864 | 875 | const self = @fieldParentPtr(RunAndCompareStep, "step", step); |
| 865 | 876 | const b = self.context.b; |
| 866 | 877 | |
| ... | ... | @@ -932,7 +943,7 @@ pub const StackTracesContext = struct { |
| 932 | 943 | // process result |
| 933 | 944 | // - keep only basename of source file path |
| 934 | 945 | // - replace address with symbolic string |
| 935 | // - replace function name with symbolic string when mode != .Debug | |
| 946 | // - replace function name with symbolic string when optimize_mode != .Debug | |
| 936 | 947 | // - skip empty lines |
| 937 | 948 | const got: []const u8 = got_result: { |
| 938 | 949 | var buf = ArrayList(u8).init(b.allocator); |
| ... | ... | @@ -968,7 +979,7 @@ pub const StackTracesContext = struct { |
| 968 | 979 | // emit substituted line |
| 969 | 980 | try buf.appendSlice(line[pos + 1 .. marks[2] + delims[2].len]); |
| 970 | 981 | try buf.appendSlice(" [address]"); |
| 971 | if (self.mode == .Debug) { | |
| 982 | if (self.optimize_mode == .Debug) { | |
| 972 | 983 | // On certain platforms (windows) or possibly depending on how we choose to link main |
| 973 | 984 | // the object file extension may be present so we simply strip any extension. |
| 974 | 985 | if (mem.indexOfScalar(u8, line[marks[4]..marks[5]], '.')) |idot| { |
| ... | ... | @@ -1003,11 +1014,11 @@ pub const StackTracesContext = struct { |
| 1003 | 1014 | }; |
| 1004 | 1015 | |
| 1005 | 1016 | pub const StandaloneContext = struct { |
| 1006 | b: *build.Builder, | |
| 1007 | step: *build.Step, | |
| 1017 | b: *std.Build, | |
| 1018 | step: *Step, | |
| 1008 | 1019 | test_index: usize, |
| 1009 | 1020 | test_filter: ?[]const u8, |
| 1010 | modes: []const Mode, | |
| 1021 | optimize_modes: []const OptimizeMode, | |
| 1011 | 1022 | skip_non_native: bool, |
| 1012 | 1023 | enable_macos_sdk: bool, |
| 1013 | 1024 | target: std.zig.CrossTarget, |
| ... | ... | @@ -1087,13 +1098,13 @@ pub const StandaloneContext = struct { |
| 1087 | 1098 | } |
| 1088 | 1099 | } |
| 1089 | 1100 | |
| 1090 | const modes = if (features.build_modes) self.modes else &[1]Mode{.Debug}; | |
| 1091 | for (modes) |mode| { | |
| 1092 | const arg = switch (mode) { | |
| 1101 | const optimize_modes = if (features.build_modes) self.optimize_modes else &[1]OptimizeMode{.Debug}; | |
| 1102 | for (optimize_modes) |optimize_mode| { | |
| 1103 | const arg = switch (optimize_mode) { | |
| 1093 | 1104 | .Debug => "", |
| 1094 | .ReleaseFast => "-Drelease-fast", | |
| 1095 | .ReleaseSafe => "-Drelease-safe", | |
| 1096 | .ReleaseSmall => "-Drelease-small", | |
| 1105 | .ReleaseFast => "-Doptimize=ReleaseFast", | |
| 1106 | .ReleaseSafe => "-Doptimize=ReleaseSafe", | |
| 1107 | .ReleaseSmall => "-Doptimize=ReleaseSmall", | |
| 1097 | 1108 | }; |
| 1098 | 1109 | const zig_args_base_len = zig_args.items.len; |
| 1099 | 1110 | if (arg.len > 0) |
| ... | ... | @@ -1101,7 +1112,7 @@ pub const StandaloneContext = struct { |
| 1101 | 1112 | defer zig_args.resize(zig_args_base_len) catch unreachable; |
| 1102 | 1113 | |
| 1103 | 1114 | const run_cmd = b.addSystemCommand(zig_args.items); |
| 1104 | const log_step = b.addLog("PASS {s} ({s})", .{ annotated_case_name, @tagName(mode) }); | |
| 1115 | const log_step = b.addLog("PASS {s} ({s})", .{ annotated_case_name, @tagName(optimize_mode) }); | |
| 1105 | 1116 | log_step.step.dependOn(&run_cmd.step); |
| 1106 | 1117 | |
| 1107 | 1118 | self.step.dependOn(&log_step.step); |
| ... | ... | @@ -1111,17 +1122,21 @@ pub const StandaloneContext = struct { |
| 1111 | 1122 | pub fn addAllArgs(self: *StandaloneContext, root_src: []const u8, link_libc: bool) void { |
| 1112 | 1123 | const b = self.b; |
| 1113 | 1124 | |
| 1114 | for (self.modes) |mode| { | |
| 1125 | for (self.optimize_modes) |optimize| { | |
| 1115 | 1126 | const annotated_case_name = fmt.allocPrint(self.b.allocator, "build {s} ({s})", .{ |
| 1116 | 1127 | root_src, |
| 1117 | @tagName(mode), | |
| 1128 | @tagName(optimize), | |
| 1118 | 1129 | }) catch unreachable; |
| 1119 | 1130 | if (self.test_filter) |filter| { |
| 1120 | 1131 | if (mem.indexOf(u8, annotated_case_name, filter) == null) continue; |
| 1121 | 1132 | } |
| 1122 | 1133 | |
| 1123 | const exe = b.addExecutable("test", root_src); | |
| 1124 | exe.setBuildMode(mode); | |
| 1134 | const exe = b.addExecutable(.{ | |
| 1135 | .name = "test", | |
| 1136 | .root_source_file = .{ .path = root_src }, | |
| 1137 | .optimize = optimize, | |
| 1138 | .target = .{}, | |
| 1139 | }); | |
| 1125 | 1140 | if (link_libc) { |
| 1126 | 1141 | exe.linkSystemLibrary("c"); |
| 1127 | 1142 | } |
| ... | ... | @@ -1135,8 +1150,8 @@ pub const StandaloneContext = struct { |
| 1135 | 1150 | }; |
| 1136 | 1151 | |
| 1137 | 1152 | pub const GenHContext = struct { |
| 1138 | b: *build.Builder, | |
| 1139 | step: *build.Step, | |
| 1153 | b: *std.Build, | |
| 1154 | step: *Step, | |
| 1140 | 1155 | test_index: usize, |
| 1141 | 1156 | test_filter: ?[]const u8, |
| 1142 | 1157 | |
| ... | ... | @@ -1163,23 +1178,23 @@ pub const GenHContext = struct { |
| 1163 | 1178 | }; |
| 1164 | 1179 | |
| 1165 | 1180 | const GenHCmpOutputStep = struct { |
| 1166 | step: build.Step, | |
| 1181 | step: Step, | |
| 1167 | 1182 | context: *GenHContext, |
| 1168 | obj: *LibExeObjStep, | |
| 1183 | obj: *CompileStep, | |
| 1169 | 1184 | name: []const u8, |
| 1170 | 1185 | test_index: usize, |
| 1171 | 1186 | case: *const TestCase, |
| 1172 | 1187 | |
| 1173 | 1188 | pub fn create( |
| 1174 | 1189 | context: *GenHContext, |
| 1175 | obj: *LibExeObjStep, | |
| 1190 | obj: *CompileStep, | |
| 1176 | 1191 | name: []const u8, |
| 1177 | 1192 | case: *const TestCase, |
| 1178 | 1193 | ) *GenHCmpOutputStep { |
| 1179 | 1194 | const allocator = context.b.allocator; |
| 1180 | 1195 | const ptr = allocator.create(GenHCmpOutputStep) catch unreachable; |
| 1181 | 1196 | ptr.* = GenHCmpOutputStep{ |
| 1182 | .step = build.Step.init(.Custom, "ParseCCmpOutput", allocator, make), | |
| 1197 | .step = Step.init(.Custom, "ParseCCmpOutput", allocator, make), | |
| 1183 | 1198 | .context = context, |
| 1184 | 1199 | .obj = obj, |
| 1185 | 1200 | .name = name, |
| ... | ... | @@ -1191,7 +1206,7 @@ pub const GenHContext = struct { |
| 1191 | 1206 | return ptr; |
| 1192 | 1207 | } |
| 1193 | 1208 | |
| 1194 | fn make(step: *build.Step) !void { | |
| 1209 | fn make(step: *Step) !void { | |
| 1195 | 1210 | const self = @fieldParentPtr(GenHCmpOutputStep, "step", step); |
| 1196 | 1211 | const b = self.context.b; |
| 1197 | 1212 | |
| ... | ... | @@ -1247,8 +1262,8 @@ pub const GenHContext = struct { |
| 1247 | 1262 | pub fn addCase(self: *GenHContext, case: *const TestCase) void { |
| 1248 | 1263 | const b = self.b; |
| 1249 | 1264 | |
| 1250 | const mode = std.builtin.Mode.Debug; | |
| 1251 | const annotated_case_name = fmt.allocPrint(self.b.allocator, "gen-h {s} ({s})", .{ case.name, @tagName(mode) }) catch unreachable; | |
| 1265 | const optimize_mode = std.builtin.OptimizeMode.Debug; | |
| 1266 | const annotated_case_name = fmt.allocPrint(self.b.allocator, "gen-h {s} ({s})", .{ case.name, @tagName(optimize_mode) }) catch unreachable; | |
| 1252 | 1267 | if (self.test_filter) |filter| { |
| 1253 | 1268 | if (mem.indexOf(u8, annotated_case_name, filter) == null) return; |
| 1254 | 1269 | } |
| ... | ... | @@ -1259,7 +1274,7 @@ pub const GenHContext = struct { |
| 1259 | 1274 | } |
| 1260 | 1275 | |
| 1261 | 1276 | const obj = b.addObjectFromWriteFileStep("test", write_src, case.sources.items[0].filename); |
| 1262 | obj.setBuildMode(mode); | |
| 1277 | obj.setBuildMode(optimize_mode); | |
| 1263 | 1278 | |
| 1264 | 1279 | const cmp_h = GenHCmpOutputStep.create(self, obj, annotated_case_name, case); |
| 1265 | 1280 | |
| ... | ... | @@ -1333,17 +1348,20 @@ const c_abi_targets = [_]CrossTarget{ |
| 1333 | 1348 | }, |
| 1334 | 1349 | }; |
| 1335 | 1350 | |
| 1336 | pub fn addCAbiTests(b: *build.Builder, skip_non_native: bool, skip_release: bool) *build.Step { | |
| 1351 | pub fn addCAbiTests(b: *std.Build, skip_non_native: bool, skip_release: bool) *Step { | |
| 1337 | 1352 | const step = b.step("test-c-abi", "Run the C ABI tests"); |
| 1338 | 1353 | |
| 1339 | const modes: [2]Mode = .{ .Debug, .ReleaseFast }; | |
| 1354 | const optimize_modes: [2]OptimizeMode = .{ .Debug, .ReleaseFast }; | |
| 1340 | 1355 | |
| 1341 | for (modes[0 .. @as(u8, 1) + @boolToInt(!skip_release)]) |mode| for (c_abi_targets) |c_abi_target| { | |
| 1356 | for (optimize_modes[0 .. @as(u8, 1) + @boolToInt(!skip_release)]) |optimize_mode| for (c_abi_targets) |c_abi_target| { | |
| 1342 | 1357 | if (skip_non_native and !c_abi_target.isNative()) |
| 1343 | 1358 | continue; |
| 1344 | 1359 | |
| 1345 | const test_step = b.addTest("test/c_abi/main.zig"); | |
| 1346 | test_step.setTarget(c_abi_target); | |
| 1360 | const test_step = b.addTest(.{ | |
| 1361 | .root_source_file = .{ .path = "test/c_abi/main.zig" }, | |
| 1362 | .optimize = optimize_mode, | |
| 1363 | .target = c_abi_target, | |
| 1364 | }); | |
| 1347 | 1365 | if (c_abi_target.abi != null and c_abi_target.abi.?.isMusl()) { |
| 1348 | 1366 | // TODO NativeTargetInfo insists on dynamically linking musl |
| 1349 | 1367 | // for some reason? |
| ... | ... | @@ -1351,7 +1369,6 @@ pub fn addCAbiTests(b: *build.Builder, skip_non_native: bool, skip_release: bool |
| 1351 | 1369 | } |
| 1352 | 1370 | test_step.linkLibC(); |
| 1353 | 1371 | test_step.addCSourceFile("test/c_abi/cfuncs.c", &.{"-std=c99"}); |
| 1354 | test_step.setBuildMode(mode); | |
| 1355 | 1372 | |
| 1356 | 1373 | if (c_abi_target.isWindows() and (c_abi_target.getCpuArch() == .x86 or builtin.target.os.tag == .linux)) { |
| 1357 | 1374 | // LTO currently incorrectly strips stdcall name-mangled functions |
| ... | ... | @@ -1363,7 +1380,7 @@ pub fn addCAbiTests(b: *build.Builder, skip_non_native: bool, skip_release: bool |
| 1363 | 1380 | test_step.setNamePrefix(b.fmt("{s}-{s}-{s} ", .{ |
| 1364 | 1381 | "test-c-abi", |
| 1365 | 1382 | triple_prefix, |
| 1366 | @tagName(mode), | |
| 1383 | @tagName(optimize_mode), | |
| 1367 | 1384 | })); |
| 1368 | 1385 | |
| 1369 | 1386 | step.dependOn(&test_step.step); |
test/translate_c.zig+16| ... | ... | @@ -3900,4 +3900,20 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 3900 | 3900 | \\pub const ZERO = @as(c_int, 0); |
| 3901 | 3901 | \\pub const WORLD = @as(c_int, 0o0000123); |
| 3902 | 3902 | }); |
| 3903 | ||
| 3904 | cases.add("Assign expression from bool to int", | |
| 3905 | \\void foo(void) { | |
| 3906 | \\ int a; | |
| 3907 | \\ if (a = 1 > 0) {} | |
| 3908 | \\} | |
| 3909 | , &[_][]const u8{ | |
| 3910 | \\pub export fn foo() void { | |
| 3911 | \\ var a: c_int = undefined; | |
| 3912 | \\ if ((blk: { | |
| 3913 | \\ const tmp = @boolToInt(@as(c_int, 1) > @as(c_int, 0)); | |
| 3914 | \\ a = tmp; | |
| 3915 | \\ break :blk tmp; | |
| 3916 | \\ }) != 0) {} | |
| 3917 | \\} | |
| 3918 | }); | |
| 3903 | 3919 | } |