| 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,6 +19,7 @@ jobs: |
| 19 | - name: Build and Test | 19 | - name: Build and Test |
| 20 | run: sh ci/x86_64-linux-debug.sh | 20 | run: sh ci/x86_64-linux-debug.sh |
| 21 | x86_64-linux-release: | 21 | x86_64-linux-release: |
| 22 | timeout-minutes: 420 | ||
| 22 | runs-on: [self-hosted, Linux, x86_64] | 23 | runs-on: [self-hosted, Linux, x86_64] |
| 23 | steps: | 24 | steps: |
| 24 | - name: Checkout | 25 | - name: Checkout |
CMakeLists.txt+10-37| ... | @@ -513,7 +513,7 @@ set(ZIG_STAGE2_SOURCES | ... | @@ -513,7 +513,7 @@ set(ZIG_STAGE2_SOURCES |
| 513 | "${CMAKE_SOURCE_DIR}/lib/std/zig/Ast.zig" | 513 | "${CMAKE_SOURCE_DIR}/lib/std/zig/Ast.zig" |
| 514 | "${CMAKE_SOURCE_DIR}/lib/std/zig/CrossTarget.zig" | 514 | "${CMAKE_SOURCE_DIR}/lib/std/zig/CrossTarget.zig" |
| 515 | "${CMAKE_SOURCE_DIR}/lib/std/zig/c_builtins.zig" | 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 | "${CMAKE_SOURCE_DIR}/lib/std/zig/render.zig" | 517 | "${CMAKE_SOURCE_DIR}/lib/std/zig/render.zig" |
| 518 | "${CMAKE_SOURCE_DIR}/lib/std/zig/string_literal.zig" | 518 | "${CMAKE_SOURCE_DIR}/lib/std/zig/string_literal.zig" |
| 519 | "${CMAKE_SOURCE_DIR}/lib/std/zig/system.zig" | 519 | "${CMAKE_SOURCE_DIR}/lib/std/zig/system.zig" |
| ... | @@ -654,46 +654,19 @@ include_directories( | ... | @@ -654,46 +654,19 @@ include_directories( |
| 654 | "${CMAKE_SOURCE_DIR}/src" | 654 | "${CMAKE_SOURCE_DIR}/src" |
| 655 | ) | 655 | ) |
| 656 | 656 | ||
| 657 | # These have to go before the -Wno- flags | ||
| 658 | if(MSVC) | 657 | if(MSVC) |
| 659 | set(EXE_CXX_FLAGS "/std:c++17") | 658 | set(EXE_CXX_FLAGS "/std:c++17") |
| 660 | else(MSVC) | 659 | set(EXE_LDFLAGS "/STACK:16777216") |
| 661 | set(EXE_CXX_FLAGS "-std=c++17") | 660 | if(NOT "${CMAKE_BUILD_TYPE}" STREQUAL "Release" AND NOT "${CMAKE_BUILD_TYPE}" STREQUAL "MinSizeRel") |
| 662 | endif(MSVC) | 661 | set(EXE_LDFLAGS "${EXE_LDFLAGS} /debug:fastlink") |
| 663 | 662 | endif() | |
| 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}") | ||
| 682 | else() | 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") | 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") |
| 684 | if(MINGW) | 665 | set(EXE_LDFLAGS " ") |
| 685 | set(EXE_CXX_FLAGS "${EXE_CXX_FLAGS} -Wno-format") | 666 | if(MINGW) |
| 686 | endif() | 667 | set(EXE_CXX_FLAGS "${EXE_CXX_FLAGS} -Wno-format") |
| 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) | ||
| 696 | set(EXE_LDFLAGS "${EXE_LDFLAGS} -Wl,--stack,16777216") | 668 | set(EXE_LDFLAGS "${EXE_LDFLAGS} -Wl,--stack,16777216") |
| 669 | endif() | ||
| 697 | endif() | 670 | endif() |
| 698 | 671 | ||
| 699 | if(ZIG_STATIC) | 672 | if(ZIG_STATIC) |
build.zig+55-204| ... | @@ -1,19 +1,18 @@ | ... | @@ -1,19 +1,18 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const builtin = std.builtin; | 2 | const builtin = std.builtin; |
| 3 | const Builder = std.build.Builder; | ||
| 4 | const tests = @import("test/tests.zig"); | 3 | const tests = @import("test/tests.zig"); |
| 5 | const BufMap = std.BufMap; | 4 | const BufMap = std.BufMap; |
| 6 | const mem = std.mem; | 5 | const mem = std.mem; |
| 7 | const ArrayList = std.ArrayList; | 6 | const ArrayList = std.ArrayList; |
| 8 | const io = std.io; | 7 | const io = std.io; |
| 9 | const fs = std.fs; | 8 | const fs = std.fs; |
| 10 | const InstallDirectoryOptions = std.build.InstallDirectoryOptions; | 9 | const InstallDirectoryOptions = std.Build.InstallDirectoryOptions; |
| 11 | const assert = std.debug.assert; | 10 | const assert = std.debug.assert; |
| 12 | 11 | ||
| 13 | const zig_version = std.builtin.Version{ .major = 0, .minor = 11, .patch = 0 }; | 12 | const zig_version = std.builtin.Version{ .major = 0, .minor = 11, .patch = 0 }; |
| 14 | const stack_size = 32 * 1024 * 1024; | 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 | const release = b.option(bool, "release", "Build in release mode") orelse false; | 16 | const release = b.option(bool, "release", "Build in release mode") orelse false; |
| 18 | const only_c = b.option(bool, "only-c", "Translate the Zig compiler to C code, with only the C backend enabled") orelse false; | 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 | const target = t: { | 18 | const target = t: { |
| ... | @@ -23,7 +22,7 @@ pub fn build(b: *Builder) !void { | ... | @@ -23,7 +22,7 @@ pub fn build(b: *Builder) !void { |
| 23 | } | 22 | } |
| 24 | break :t b.standardTargetOptions(.{ .default_target = default_target }); | 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 | .wasm32 => .ReleaseSmall, | 26 | .wasm32 => .ReleaseSmall, |
| 28 | else => .ReleaseFast, | 27 | else => .ReleaseFast, |
| 29 | } else .Debug; | 28 | } else .Debug; |
| ... | @@ -33,7 +32,12 @@ pub fn build(b: *Builder) !void { | ... | @@ -33,7 +32,12 @@ pub fn build(b: *Builder) !void { |
| 33 | 32 | ||
| 34 | const test_step = b.step("test", "Run all the tests"); | 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 | docgen_exe.single_threaded = single_threaded; | 41 | docgen_exe.single_threaded = single_threaded; |
| 38 | 42 | ||
| 39 | const rel_zig_exe = try fs.path.relative(b.allocator, b.build_root, b.zig_exe); | 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,10 +57,12 @@ pub fn build(b: *Builder) !void { |
| 53 | const docs_step = b.step("docs", "Build documentation"); | 57 | const docs_step = b.step("docs", "Build documentation"); |
| 54 | docs_step.dependOn(&docgen_cmd.step); | 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 | test_cases.main_pkg_path = "."; | 64 | test_cases.main_pkg_path = "."; |
| 58 | test_cases.stack_size = stack_size; | 65 | test_cases.stack_size = stack_size; |
| 59 | test_cases.setBuildMode(mode); | ||
| 60 | test_cases.single_threaded = single_threaded; | 66 | test_cases.single_threaded = single_threaded; |
| 61 | 67 | ||
| 62 | const fmt_build_zig = b.addFmt(&[_][]const u8{"build.zig"}); | 68 | const fmt_build_zig = b.addFmt(&[_][]const u8{"build.zig"}); |
| ... | @@ -154,17 +160,15 @@ pub fn build(b: *Builder) !void { | ... | @@ -154,17 +160,15 @@ pub fn build(b: *Builder) !void { |
| 154 | 160 | ||
| 155 | 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: { | 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 | if (strip == true) break :blk @as(u32, 0); | 162 | if (strip == true) break :blk @as(u32, 0); |
| 157 | if (mode != .Debug) break :blk 0; | 163 | if (optimize != .Debug) break :blk 0; |
| 158 | break :blk 4; | 164 | break :blk 4; |
| 159 | }; | 165 | }; |
| 160 | 166 | ||
| 161 | const exe = addCompilerStep(b); | 167 | const exe = addCompilerStep(b, optimize, target); |
| 162 | exe.strip = strip; | 168 | exe.strip = strip; |
| 163 | exe.sanitize_thread = sanitize_thread; | 169 | exe.sanitize_thread = sanitize_thread; |
| 164 | exe.build_id = b.option(bool, "build-id", "Include a build id note") orelse false; | 170 | exe.build_id = b.option(bool, "build-id", "Include a build id note") orelse false; |
| 165 | exe.install(); | 171 | exe.install(); |
| 166 | exe.setBuildMode(mode); | ||
| 167 | exe.setTarget(target); | ||
| 168 | 172 | ||
| 169 | const compile_step = b.step("compile", "Build the self-hosted compiler"); | 173 | const compile_step = b.step("compile", "Build the self-hosted compiler"); |
| 170 | compile_step.dependOn(&exe.step); | 174 | compile_step.dependOn(&exe.step); |
| ... | @@ -201,7 +205,7 @@ pub fn build(b: *Builder) !void { | ... | @@ -201,7 +205,7 @@ pub fn build(b: *Builder) !void { |
| 201 | test_cases.linkLibC(); | 205 | test_cases.linkLibC(); |
| 202 | } | 206 | } |
| 203 | 207 | ||
| 204 | const is_debug = mode == .Debug; | 208 | const is_debug = optimize == .Debug; |
| 205 | const enable_logging = b.option(bool, "log", "Enable debug logging with --debug-log") orelse is_debug; | 209 | const enable_logging = b.option(bool, "log", "Enable debug logging with --debug-log") orelse is_debug; |
| 206 | const enable_link_snapshots = b.option(bool, "link-snapshot", "Whether to enable linker state snapshots") orelse false; | 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,25 +371,25 @@ pub fn build(b: *Builder) !void { |
| 367 | test_step.dependOn(test_cases_step); | 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 | var chosen_mode_index: usize = 0; | 375 | var chosen_mode_index: usize = 0; |
| 372 | if (!skip_debug) { | 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 | chosen_mode_index += 1; | 378 | chosen_mode_index += 1; |
| 375 | } | 379 | } |
| 376 | if (!skip_release_safe) { | 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 | chosen_mode_index += 1; | 382 | chosen_mode_index += 1; |
| 379 | } | 383 | } |
| 380 | if (!skip_release_fast) { | 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 | chosen_mode_index += 1; | 386 | chosen_mode_index += 1; |
| 383 | } | 387 | } |
| 384 | if (!skip_release_small) { | 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 | chosen_mode_index += 1; | 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 | // run stage1 `zig fmt` on this build.zig file just to make sure it works | 394 | // run stage1 `zig fmt` on this build.zig file just to make sure it works |
| 391 | test_step.dependOn(&fmt_build_zig.step); | 395 | test_step.dependOn(&fmt_build_zig.step); |
| ... | @@ -398,7 +402,7 @@ pub fn build(b: *Builder) !void { | ... | @@ -398,7 +402,7 @@ pub fn build(b: *Builder) !void { |
| 398 | "test/behavior.zig", | 402 | "test/behavior.zig", |
| 399 | "behavior", | 403 | "behavior", |
| 400 | "Run the behavior tests", | 404 | "Run the behavior tests", |
| 401 | modes, | 405 | optimization_modes, |
| 402 | skip_single_threaded, | 406 | skip_single_threaded, |
| 403 | skip_non_native, | 407 | skip_non_native, |
| 404 | skip_libc, | 408 | skip_libc, |
| ... | @@ -412,7 +416,7 @@ pub fn build(b: *Builder) !void { | ... | @@ -412,7 +416,7 @@ pub fn build(b: *Builder) !void { |
| 412 | "lib/compiler_rt.zig", | 416 | "lib/compiler_rt.zig", |
| 413 | "compiler-rt", | 417 | "compiler-rt", |
| 414 | "Run the compiler_rt tests", | 418 | "Run the compiler_rt tests", |
| 415 | modes, | 419 | optimization_modes, |
| 416 | true, // skip_single_threaded | 420 | true, // skip_single_threaded |
| 417 | skip_non_native, | 421 | skip_non_native, |
| 418 | true, // skip_libc | 422 | true, // skip_libc |
| ... | @@ -426,7 +430,7 @@ pub fn build(b: *Builder) !void { | ... | @@ -426,7 +430,7 @@ pub fn build(b: *Builder) !void { |
| 426 | "lib/c.zig", | 430 | "lib/c.zig", |
| 427 | "universal-libc", | 431 | "universal-libc", |
| 428 | "Run the universal libc tests", | 432 | "Run the universal libc tests", |
| 429 | modes, | 433 | optimization_modes, |
| 430 | true, // skip_single_threaded | 434 | true, // skip_single_threaded |
| 431 | skip_non_native, | 435 | skip_non_native, |
| 432 | true, // skip_libc | 436 | true, // skip_libc |
| ... | @@ -434,11 +438,11 @@ pub fn build(b: *Builder) !void { | ... | @@ -434,11 +438,11 @@ pub fn build(b: *Builder) !void { |
| 434 | skip_stage2_tests or true, // TODO get these all passing | 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 | test_step.dependOn(tests.addStandaloneTests( | 442 | test_step.dependOn(tests.addStandaloneTests( |
| 439 | b, | 443 | b, |
| 440 | test_filter, | 444 | test_filter, |
| 441 | modes, | 445 | optimization_modes, |
| 442 | skip_non_native, | 446 | skip_non_native, |
| 443 | enable_macos_sdk, | 447 | enable_macos_sdk, |
| 444 | target, | 448 | target, |
| ... | @@ -451,10 +455,10 @@ pub fn build(b: *Builder) !void { | ... | @@ -451,10 +455,10 @@ pub fn build(b: *Builder) !void { |
| 451 | enable_symlinks_windows, | 455 | enable_symlinks_windows, |
| 452 | )); | 456 | )); |
| 453 | test_step.dependOn(tests.addCAbiTests(b, skip_non_native, skip_release)); | 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)); | 458 | test_step.dependOn(tests.addLinkTests(b, test_filter, optimization_modes, enable_macos_sdk, skip_stage2_tests, enable_symlinks_windows)); |
| 455 | test_step.dependOn(tests.addStackTraceTests(b, test_filter, modes)); | 459 | test_step.dependOn(tests.addStackTraceTests(b, test_filter, optimization_modes)); |
| 456 | test_step.dependOn(tests.addCliTests(b, test_filter, modes)); | 460 | test_step.dependOn(tests.addCliTests(b, test_filter, optimization_modes)); |
| 457 | test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, modes)); | 461 | test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, optimization_modes)); |
| 458 | test_step.dependOn(tests.addTranslateCTests(b, test_filter)); | 462 | test_step.dependOn(tests.addTranslateCTests(b, test_filter)); |
| 459 | if (!skip_run_translated_c) { | 463 | if (!skip_run_translated_c) { |
| 460 | test_step.dependOn(tests.addRunTranslatedCTests(b, test_filter, target)); | 464 | test_step.dependOn(tests.addRunTranslatedCTests(b, test_filter, target)); |
| ... | @@ -468,7 +472,7 @@ pub fn build(b: *Builder) !void { | ... | @@ -468,7 +472,7 @@ pub fn build(b: *Builder) !void { |
| 468 | "lib/std/std.zig", | 472 | "lib/std/std.zig", |
| 469 | "std", | 473 | "std", |
| 470 | "Run the standard library tests", | 474 | "Run the standard library tests", |
| 471 | modes, | 475 | optimization_modes, |
| 472 | skip_single_threaded, | 476 | skip_single_threaded, |
| 473 | skip_non_native, | 477 | skip_non_native, |
| 474 | skip_libc, | 478 | skip_libc, |
| ... | @@ -479,7 +483,7 @@ pub fn build(b: *Builder) !void { | ... | @@ -479,7 +483,7 @@ pub fn build(b: *Builder) !void { |
| 479 | try addWasiUpdateStep(b, version); | 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 | const semver = try std.SemanticVersion.parse(version); | 487 | const semver = try std.SemanticVersion.parse(version); |
| 484 | 488 | ||
| 485 | var target: std.zig.CrossTarget = .{ | 489 | var target: std.zig.CrossTarget = .{ |
| ... | @@ -488,9 +492,7 @@ fn addWasiUpdateStep(b: *Builder, version: [:0]const u8) !void { | ... | @@ -488,9 +492,7 @@ fn addWasiUpdateStep(b: *Builder, version: [:0]const u8) !void { |
| 488 | }; | 492 | }; |
| 489 | target.cpu_features_add.addFeature(@enumToInt(std.Target.wasm.Feature.bulk_memory)); | 493 | target.cpu_features_add.addFeature(@enumToInt(std.Target.wasm.Feature.bulk_memory)); |
| 490 | 494 | ||
| 491 | const exe = addCompilerStep(b); | 495 | const exe = addCompilerStep(b, .ReleaseSmall, target); |
| 492 | exe.setBuildMode(.ReleaseSmall); | ||
| 493 | exe.setTarget(target); | ||
| 494 | 496 | ||
| 495 | const exe_options = b.addOptions(); | 497 | const exe_options = b.addOptions(); |
| 496 | exe.addOptions("build_options", exe_options); | 498 | exe.addOptions("build_options", exe_options); |
| ... | @@ -517,8 +519,17 @@ fn addWasiUpdateStep(b: *Builder, version: [:0]const u8) !void { | ... | @@ -517,8 +519,17 @@ fn addWasiUpdateStep(b: *Builder, version: [:0]const u8) !void { |
| 517 | update_zig1_step.dependOn(&run_opt.step); | 519 | update_zig1_step.dependOn(&run_opt.step); |
| 518 | } | 520 | } |
| 519 | 521 | ||
| 520 | fn addCompilerStep(b: *Builder) *std.build.LibExeObjStep { | 522 | fn addCompilerStep( |
| 521 | const exe = b.addExecutable("zig", "src/main.zig"); | 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 | exe.stack_size = stack_size; | 533 | exe.stack_size = stack_size; |
| 523 | return exe; | 534 | return exe; |
| 524 | } | 535 | } |
| ... | @@ -538,9 +549,9 @@ const exe_cflags = [_][]const u8{ | ... | @@ -538,9 +549,9 @@ const exe_cflags = [_][]const u8{ |
| 538 | }; | 549 | }; |
| 539 | 550 | ||
| 540 | fn addCmakeCfgOptionsToExe( | 551 | fn addCmakeCfgOptionsToExe( |
| 541 | b: *Builder, | 552 | b: *std.Build, |
| 542 | cfg: CMakeConfig, | 553 | cfg: CMakeConfig, |
| 543 | exe: *std.build.LibExeObjStep, | 554 | exe: *std.Build.CompileStep, |
| 544 | use_zig_libcxx: bool, | 555 | use_zig_libcxx: bool, |
| 545 | ) !void { | 556 | ) !void { |
| 546 | if (exe.target.isDarwin()) { | 557 | if (exe.target.isDarwin()) { |
| ... | @@ -619,7 +630,7 @@ fn addCmakeCfgOptionsToExe( | ... | @@ -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 | // Adds the Zig C++ sources which both stage1 and stage2 need. | 634 | // Adds the Zig C++ sources which both stage1 and stage2 need. |
| 624 | // | 635 | // |
| 625 | // We need this because otherwise zig_clang_cc1_main.cpp ends up pulling | 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,9 +667,9 @@ fn addStaticLlvmOptionsToExe(exe: *std.build.LibExeObjStep) !void { |
| 656 | } | 667 | } |
| 657 | 668 | ||
| 658 | fn addCxxKnownPath( | 669 | fn addCxxKnownPath( |
| 659 | b: *Builder, | 670 | b: *std.Build, |
| 660 | ctx: CMakeConfig, | 671 | ctx: CMakeConfig, |
| 661 | exe: *std.build.LibExeObjStep, | 672 | exe: *std.Build.CompileStep, |
| 662 | objname: []const u8, | 673 | objname: []const u8, |
| 663 | errtxt: ?[]const u8, | 674 | errtxt: ?[]const u8, |
| 664 | need_cpp_includes: bool, | 675 | need_cpp_includes: bool, |
| ... | @@ -691,7 +702,7 @@ fn addCxxKnownPath( | ... | @@ -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 | var it = mem.tokenize(u8, list, ";"); | 706 | var it = mem.tokenize(u8, list, ";"); |
| 696 | while (it.next()) |lib| { | 707 | while (it.next()) |lib| { |
| 697 | if (mem.startsWith(u8, lib, "-l")) { | 708 | if (mem.startsWith(u8, lib, "-l")) { |
| ... | @@ -705,7 +716,7 @@ fn addCMakeLibraryList(exe: *std.build.LibExeObjStep, list: []const u8) void { | ... | @@ -705,7 +716,7 @@ fn addCMakeLibraryList(exe: *std.build.LibExeObjStep, list: []const u8) void { |
| 705 | } | 716 | } |
| 706 | 717 | ||
| 707 | const CMakeConfig = struct { | 718 | const CMakeConfig = struct { |
| 708 | llvm_linkage: std.build.LibExeObjStep.Linkage, | 719 | llvm_linkage: std.Build.CompileStep.Linkage, |
| 709 | cmake_binary_dir: []const u8, | 720 | cmake_binary_dir: []const u8, |
| 710 | cmake_prefix_path: []const u8, | 721 | cmake_prefix_path: []const u8, |
| 711 | cmake_static_library_prefix: []const u8, | 722 | cmake_static_library_prefix: []const u8, |
| ... | @@ -722,7 +733,7 @@ const CMakeConfig = struct { | ... | @@ -722,7 +733,7 @@ const CMakeConfig = struct { |
| 722 | 733 | ||
| 723 | const max_config_h_bytes = 1 * 1024 * 1024; | 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 | if (config_h_path_option) |path| { | 737 | if (config_h_path_option) |path| { |
| 727 | var config_h_or_err = fs.cwd().openFile(path, .{}); | 738 | var config_h_or_err = fs.cwd().openFile(path, .{}); |
| 728 | if (config_h_or_err) |*file| { | 739 | if (config_h_or_err) |*file| { |
| ... | @@ -768,7 +779,7 @@ fn findConfigH(b: *Builder, config_h_path_option: ?[]const u8) ?[]const u8 { | ... | @@ -768,7 +779,7 @@ fn findConfigH(b: *Builder, config_h_path_option: ?[]const u8) ?[]const u8 { |
| 768 | } else unreachable; // TODO should not need `else unreachable`. | 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 | var ctx: CMakeConfig = .{ | 783 | var ctx: CMakeConfig = .{ |
| 773 | .llvm_linkage = undefined, | 784 | .llvm_linkage = undefined, |
| 774 | .cmake_binary_dir = undefined, | 785 | .cmake_binary_dir = undefined, |
| ... | @@ -857,7 +868,7 @@ fn parseConfigH(b: *Builder, config_h_text: []const u8) ?CMakeConfig { | ... | @@ -857,7 +868,7 @@ fn parseConfigH(b: *Builder, config_h_text: []const u8) ?CMakeConfig { |
| 857 | return ctx; | 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 | const duplicated = b.allocator.dupe(u8, s) catch unreachable; | 872 | const duplicated = b.allocator.dupe(u8, s) catch unreachable; |
| 862 | for (duplicated) |*byte| switch (byte.*) { | 873 | for (duplicated) |*byte| switch (byte.*) { |
| 863 | '/' => byte.* = fs.path.sep, | 874 | '/' => byte.* = fs.path.sep, |
| ... | @@ -866,166 +877,6 @@ fn toNativePathSep(b: *Builder, s: []const u8) []u8 { | ... | @@ -866,166 +877,6 @@ fn toNativePathSep(b: *Builder, s: []const u8) []u8 { |
| 866 | return duplicated; | 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 | const zig_cpp_sources = [_][]const u8{ | 880 | const zig_cpp_sources = [_][]const u8{ |
| 1030 | // These are planned to stay even when we are self-hosted. | 881 | // These are planned to stay even when we are self-hosted. |
| 1031 | "src/zig_llvm.cpp", | 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,7 +76,8 @@ Write-Output "Build x86_64-windows-msvc behavior tests using the C backend..." |
| 76 | -ofmt=c ` | 76 | -ofmt=c ` |
| 77 | -femit-bin="test-x86_64-windows-msvc.c" ` | 77 | -femit-bin="test-x86_64-windows-msvc.c" ` |
| 78 | --test-no-exec ` | 78 | --test-no-exec ` |
| 79 | -target x86_64-windows-msvc | 79 | -target x86_64-windows-msvc ` |
| 80 | -lc | ||
| 80 | CheckLastExitCode | 81 | CheckLastExitCode |
| 81 | 82 | ||
| 82 | & "stage3-debug\bin\zig.exe" build-obj ` | 83 | & "stage3-debug\bin\zig.exe" build-obj ` |
| ... | @@ -99,7 +100,7 @@ Enter-VsDevShell -VsInstallPath "C:\Program Files\Microsoft Visual Studio\2022\E | ... | @@ -99,7 +100,7 @@ Enter-VsDevShell -VsInstallPath "C:\Program Files\Microsoft Visual Studio\2022\E |
| 99 | CheckLastExitCode | 100 | CheckLastExitCode |
| 100 | 101 | ||
| 101 | Write-Output "Build and run behavior tests with msvc..." | 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 | CheckLastExitCode | 104 | CheckLastExitCode |
| 104 | 105 | ||
| 105 | & .\test-x86_64-windows-msvc.exe | 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,7 +76,8 @@ Write-Output "Build x86_64-windows-msvc behavior tests using the C backend..." |
| 76 | -ofmt=c ` | 76 | -ofmt=c ` |
| 77 | -femit-bin="test-x86_64-windows-msvc.c" ` | 77 | -femit-bin="test-x86_64-windows-msvc.c" ` |
| 78 | --test-no-exec ` | 78 | --test-no-exec ` |
| 79 | -target x86_64-windows-msvc | 79 | -target x86_64-windows-msvc ` |
| 80 | -lc | ||
| 80 | CheckLastExitCode | 81 | CheckLastExitCode |
| 81 | 82 | ||
| 82 | & "stage3-release\bin\zig.exe" build-obj ` | 83 | & "stage3-release\bin\zig.exe" build-obj ` |
| ... | @@ -99,7 +100,7 @@ Enter-VsDevShell -VsInstallPath "C:\Program Files\Microsoft Visual Studio\2022\E | ... | @@ -99,7 +100,7 @@ Enter-VsDevShell -VsInstallPath "C:\Program Files\Microsoft Visual Studio\2022\E |
| 99 | CheckLastExitCode | 100 | CheckLastExitCode |
| 100 | 101 | ||
| 101 | Write-Output "Build and run behavior tests with msvc..." | 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 | CheckLastExitCode | 104 | CheckLastExitCode |
| 104 | 105 | ||
| 105 | & .\test-x86_64-windows-msvc.exe | 106 | & .\test-x86_64-windows-msvc.exe |
doc/langref.html.in+93-58| ... | @@ -871,6 +871,13 @@ pub fn main() void { | ... | @@ -871,6 +871,13 @@ pub fn main() void { |
| 871 | However, it is possible to embed non-UTF-8 bytes into a string literal using <code>\xNN</code> notation. | 871 | However, it is possible to embed non-UTF-8 bytes into a string literal using <code>\xNN</code> notation. |
| 872 | </p> | 872 | </p> |
| 873 | <p> | 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 | Unicode code point literals have type {#syntax#}comptime_int{#endsyntax#}, the same as | 881 | Unicode code point literals have type {#syntax#}comptime_int{#endsyntax#}, the same as |
| 875 | {#link|Integer Literals#}. All {#link|Escape Sequences#} are valid in both string literals | 882 | {#link|Integer Literals#}. All {#link|Escape Sequences#} are valid in both string literals |
| 876 | and Unicode code point literals. | 883 | and Unicode code point literals. |
| ... | @@ -894,9 +901,12 @@ pub fn main() void { | ... | @@ -894,9 +901,12 @@ pub fn main() void { |
| 894 | print("{}\n", .{'e' == '\x65'}); // true | 901 | print("{}\n", .{'e' == '\x65'}); // true |
| 895 | print("{d}\n", .{'\u{1f4a9}'}); // 128169 | 902 | print("{d}\n", .{'\u{1f4a9}'}); // 128169 |
| 896 | print("{d}\n", .{'💯'}); // 128175 | 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 | print("{u}\n", .{'âš¡'}); | 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 | {#code_end#} | 911 | {#code_end#} |
| 902 | {#see_also|Arrays|Source Encoding#} | 912 | {#see_also|Arrays|Source Encoding#} |
| ... | @@ -8799,6 +8809,15 @@ pub const PrefetchOptions = struct { | ... | @@ -8799,6 +8809,15 @@ pub const PrefetchOptions = struct { |
| 8799 | {#link|Optional Pointers#} are allowed. Casting an optional pointer which is {#link|null#} | 8809 | {#link|Optional Pointers#} are allowed. Casting an optional pointer which is {#link|null#} |
| 8800 | to a non-optional pointer invokes safety-checked {#link|Undefined Behavior#}. | 8810 | to a non-optional pointer invokes safety-checked {#link|Undefined Behavior#}. |
| 8801 | </p> | 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 | {#header_close#} | 8821 | {#header_close#} |
| 8803 | 8822 | ||
| 8804 | {#header_open|@ptrToInt#} | 8823 | {#header_open|@ptrToInt#} |
| ... | @@ -8811,6 +8830,13 @@ pub const PrefetchOptions = struct { | ... | @@ -8811,6 +8830,13 @@ pub const PrefetchOptions = struct { |
| 8811 | 8830 | ||
| 8812 | {#header_close#} | 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 | {#header_open|@rem#} | 8840 | {#header_open|@rem#} |
| 8815 | <pre>{#syntax#}@rem(numerator: T, denominator: T) T{#endsyntax#}</pre> | 8841 | <pre>{#syntax#}@rem(numerator: T, denominator: T) T{#endsyntax#}</pre> |
| 8816 | <p> | 8842 | <p> |
| ... | @@ -9180,8 +9206,7 @@ fn doTheTest() !void { | ... | @@ -9180,8 +9206,7 @@ fn doTheTest() !void { |
| 9180 | when available. | 9206 | when available. |
| 9181 | </p> | 9207 | </p> |
| 9182 | <p> | 9208 | <p> |
| 9183 | Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that | 9209 | Supports {#link|Floats#} and {#link|Vectors#} of floats. |
| 9184 | <a href="https://github.com/ziglang/zig/issues/4026">some float operations are not yet implemented for all float types</a>. | ||
| 9185 | </p> | 9210 | </p> |
| 9186 | {#header_close#} | 9211 | {#header_close#} |
| 9187 | {#header_open|@sin#} | 9212 | {#header_open|@sin#} |
| ... | @@ -9191,8 +9216,7 @@ fn doTheTest() !void { | ... | @@ -9191,8 +9216,7 @@ fn doTheTest() !void { |
| 9191 | when available. | 9216 | when available. |
| 9192 | </p> | 9217 | </p> |
| 9193 | <p> | 9218 | <p> |
| 9194 | Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that | 9219 | Supports {#link|Floats#} and {#link|Vectors#} of floats. |
| 9195 | <a href="https://github.com/ziglang/zig/issues/4026">some float operations are not yet implemented for all float types</a>. | ||
| 9196 | </p> | 9220 | </p> |
| 9197 | {#header_close#} | 9221 | {#header_close#} |
| 9198 | 9222 | ||
| ... | @@ -9203,8 +9227,7 @@ fn doTheTest() !void { | ... | @@ -9203,8 +9227,7 @@ fn doTheTest() !void { |
| 9203 | when available. | 9227 | when available. |
| 9204 | </p> | 9228 | </p> |
| 9205 | <p> | 9229 | <p> |
| 9206 | Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that | 9230 | Supports {#link|Floats#} and {#link|Vectors#} of floats. |
| 9207 | <a href="https://github.com/ziglang/zig/issues/4026">some float operations are not yet implemented for all float types</a>. | ||
| 9208 | </p> | 9231 | </p> |
| 9209 | {#header_close#} | 9232 | {#header_close#} |
| 9210 | 9233 | ||
| ... | @@ -9215,8 +9238,7 @@ fn doTheTest() !void { | ... | @@ -9215,8 +9238,7 @@ fn doTheTest() !void { |
| 9215 | Uses a dedicated hardware instruction when available. | 9238 | Uses a dedicated hardware instruction when available. |
| 9216 | </p> | 9239 | </p> |
| 9217 | <p> | 9240 | <p> |
| 9218 | Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that | 9241 | Supports {#link|Floats#} and {#link|Vectors#} of floats. |
| 9219 | <a href="https://github.com/ziglang/zig/issues/4026">some float operations are not yet implemented for all float types</a>. | ||
| 9220 | </p> | 9242 | </p> |
| 9221 | {#header_close#} | 9243 | {#header_close#} |
| 9222 | 9244 | ||
| ... | @@ -9227,8 +9249,7 @@ fn doTheTest() !void { | ... | @@ -9227,8 +9249,7 @@ fn doTheTest() !void { |
| 9227 | when available. | 9249 | when available. |
| 9228 | </p> | 9250 | </p> |
| 9229 | <p> | 9251 | <p> |
| 9230 | Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that | 9252 | Supports {#link|Floats#} and {#link|Vectors#} of floats. |
| 9231 | <a href="https://github.com/ziglang/zig/issues/4026">some float operations are not yet implemented for all float types</a>. | ||
| 9232 | </p> | 9253 | </p> |
| 9233 | {#header_close#} | 9254 | {#header_close#} |
| 9234 | {#header_open|@exp2#} | 9255 | {#header_open|@exp2#} |
| ... | @@ -9238,8 +9259,7 @@ fn doTheTest() !void { | ... | @@ -9238,8 +9259,7 @@ fn doTheTest() !void { |
| 9238 | when available. | 9259 | when available. |
| 9239 | </p> | 9260 | </p> |
| 9240 | <p> | 9261 | <p> |
| 9241 | Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that | 9262 | Supports {#link|Floats#} and {#link|Vectors#} of floats. |
| 9242 | <a href="https://github.com/ziglang/zig/issues/4026">some float operations are not yet implemented for all float types</a>. | ||
| 9243 | </p> | 9263 | </p> |
| 9244 | {#header_close#} | 9264 | {#header_close#} |
| 9245 | {#header_open|@log#} | 9265 | {#header_open|@log#} |
| ... | @@ -9249,8 +9269,7 @@ fn doTheTest() !void { | ... | @@ -9249,8 +9269,7 @@ fn doTheTest() !void { |
| 9249 | when available. | 9269 | when available. |
| 9250 | </p> | 9270 | </p> |
| 9251 | <p> | 9271 | <p> |
| 9252 | Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that | 9272 | Supports {#link|Floats#} and {#link|Vectors#} of floats. |
| 9253 | <a href="https://github.com/ziglang/zig/issues/4026">some float operations are not yet implemented for all float types</a>. | ||
| 9254 | </p> | 9273 | </p> |
| 9255 | {#header_close#} | 9274 | {#header_close#} |
| 9256 | {#header_open|@log2#} | 9275 | {#header_open|@log2#} |
| ... | @@ -9260,8 +9279,7 @@ fn doTheTest() !void { | ... | @@ -9260,8 +9279,7 @@ fn doTheTest() !void { |
| 9260 | when available. | 9279 | when available. |
| 9261 | </p> | 9280 | </p> |
| 9262 | <p> | 9281 | <p> |
| 9263 | Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that | 9282 | Supports {#link|Floats#} and {#link|Vectors#} of floats. |
| 9264 | <a href="https://github.com/ziglang/zig/issues/4026">some float operations are not yet implemented for all float types</a>. | ||
| 9265 | </p> | 9283 | </p> |
| 9266 | {#header_close#} | 9284 | {#header_close#} |
| 9267 | {#header_open|@log10#} | 9285 | {#header_open|@log10#} |
| ... | @@ -9271,8 +9289,7 @@ fn doTheTest() !void { | ... | @@ -9271,8 +9289,7 @@ fn doTheTest() !void { |
| 9271 | when available. | 9289 | when available. |
| 9272 | </p> | 9290 | </p> |
| 9273 | <p> | 9291 | <p> |
| 9274 | Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that | 9292 | Supports {#link|Floats#} and {#link|Vectors#} of floats. |
| 9275 | <a href="https://github.com/ziglang/zig/issues/4026">some float operations are not yet implemented for all float types</a>. | ||
| 9276 | </p> | 9293 | </p> |
| 9277 | {#header_close#} | 9294 | {#header_close#} |
| 9278 | {#header_open|@fabs#} | 9295 | {#header_open|@fabs#} |
| ... | @@ -9282,8 +9299,7 @@ fn doTheTest() !void { | ... | @@ -9282,8 +9299,7 @@ fn doTheTest() !void { |
| 9282 | when available. | 9299 | when available. |
| 9283 | </p> | 9300 | </p> |
| 9284 | <p> | 9301 | <p> |
| 9285 | Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that | 9302 | Supports {#link|Floats#} and {#link|Vectors#} of floats. |
| 9286 | <a href="https://github.com/ziglang/zig/issues/4026">some float operations are not yet implemented for all float types</a>. | ||
| 9287 | </p> | 9303 | </p> |
| 9288 | {#header_close#} | 9304 | {#header_close#} |
| 9289 | {#header_open|@floor#} | 9305 | {#header_open|@floor#} |
| ... | @@ -9293,8 +9309,7 @@ fn doTheTest() !void { | ... | @@ -9293,8 +9309,7 @@ fn doTheTest() !void { |
| 9293 | Uses a dedicated hardware instruction when available. | 9309 | Uses a dedicated hardware instruction when available. |
| 9294 | </p> | 9310 | </p> |
| 9295 | <p> | 9311 | <p> |
| 9296 | Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that | 9312 | Supports {#link|Floats#} and {#link|Vectors#} of floats. |
| 9297 | <a href="https://github.com/ziglang/zig/issues/4026">some float operations are not yet implemented for all float types</a>. | ||
| 9298 | </p> | 9313 | </p> |
| 9299 | {#header_close#} | 9314 | {#header_close#} |
| 9300 | {#header_open|@ceil#} | 9315 | {#header_open|@ceil#} |
| ... | @@ -9304,8 +9319,7 @@ fn doTheTest() !void { | ... | @@ -9304,8 +9319,7 @@ fn doTheTest() !void { |
| 9304 | Uses a dedicated hardware instruction when available. | 9319 | Uses a dedicated hardware instruction when available. |
| 9305 | </p> | 9320 | </p> |
| 9306 | <p> | 9321 | <p> |
| 9307 | Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that | 9322 | Supports {#link|Floats#} and {#link|Vectors#} of floats. |
| 9308 | <a href="https://github.com/ziglang/zig/issues/4026">some float operations are not yet implemented for all float types</a>. | ||
| 9309 | </p> | 9323 | </p> |
| 9310 | {#header_close#} | 9324 | {#header_close#} |
| 9311 | {#header_open|@trunc#} | 9325 | {#header_open|@trunc#} |
| ... | @@ -9315,8 +9329,7 @@ fn doTheTest() !void { | ... | @@ -9315,8 +9329,7 @@ fn doTheTest() !void { |
| 9315 | Uses a dedicated hardware instruction when available. | 9329 | Uses a dedicated hardware instruction when available. |
| 9316 | </p> | 9330 | </p> |
| 9317 | <p> | 9331 | <p> |
| 9318 | Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that | 9332 | Supports {#link|Floats#} and {#link|Vectors#} of floats. |
| 9319 | <a href="https://github.com/ziglang/zig/issues/4026">some float operations are not yet implemented for all float types</a>. | ||
| 9320 | </p> | 9333 | </p> |
| 9321 | {#header_close#} | 9334 | {#header_close#} |
| 9322 | {#header_open|@round#} | 9335 | {#header_open|@round#} |
| ... | @@ -9326,8 +9339,7 @@ fn doTheTest() !void { | ... | @@ -9326,8 +9339,7 @@ fn doTheTest() !void { |
| 9326 | when available. | 9339 | when available. |
| 9327 | </p> | 9340 | </p> |
| 9328 | <p> | 9341 | <p> |
| 9329 | Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that | 9342 | Supports {#link|Floats#} and {#link|Vectors#} of floats. |
| 9330 | <a href="https://github.com/ziglang/zig/issues/4026">some float operations are not yet implemented for all float types</a>. | ||
| 9331 | </p> | 9343 | </p> |
| 9332 | {#header_close#} | 9344 | {#header_close#} |
| 9333 | 9345 | ||
| ... | @@ -9528,11 +9540,15 @@ fn foo(comptime T: type, ptr: *T) T { | ... | @@ -9528,11 +9540,15 @@ fn foo(comptime T: type, ptr: *T) T { |
| 9528 | To add standard build options to a <code class="file">build.zig</code> file: | 9540 | To add standard build options to a <code class="file">build.zig</code> file: |
| 9529 | </p> | 9541 | </p> |
| 9530 | {#code_begin|syntax|build#} | 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 { | 9545 | pub fn build(b: *std.Build) void { |
| 9534 | const exe = b.addExecutable("example", "example.zig"); | 9546 | const optimize = b.standardOptimizeOption(.{}); |
| 9535 | exe.setBuildMode(b.standardReleaseOptions()); | 9547 | const exe = b.addExecutable(.{ |
| 9548 | .name = "example", | ||
| 9549 | .root_source_file = .{ .path = "example.zig" }, | ||
| 9550 | .optimize = optimize, | ||
| 9551 | }); | ||
| 9536 | b.default_step.dependOn(&exe.step); | 9552 | b.default_step.dependOn(&exe.step); |
| 9537 | } | 9553 | } |
| 9538 | {#code_end#} | 9554 | {#code_end#} |
| ... | @@ -10547,22 +10563,26 @@ const separator = if (builtin.os.tag == .windows) '\\' else '/'; | ... | @@ -10547,22 +10563,26 @@ const separator = if (builtin.os.tag == .windows) '\\' else '/'; |
| 10547 | <p>This <code class="file">build.zig</code> file is automatically generated | 10563 | <p>This <code class="file">build.zig</code> file is automatically generated |
| 10548 | by <kbd>zig init-exe</kbd>.</p> | 10564 | by <kbd>zig init-exe</kbd>.</p> |
| 10549 | {#code_begin|syntax|build_executable#} | 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 | // Standard target options allows the person running `zig build` to choose | 10569 | // Standard target options allows the person running `zig build` to choose |
| 10554 | // what target to build for. Here we do not override the defaults, which | 10570 | // what target to build for. Here we do not override the defaults, which |
| 10555 | // means any target is allowed, and the default is native. Other options | 10571 | // means any target is allowed, and the default is native. Other options |
| 10556 | // for restricting supported target set are available. | 10572 | // for restricting supported target set are available. |
| 10557 | const target = b.standardTargetOptions(.{}); | 10573 | const target = b.standardTargetOptions(.{}); |
| 10558 | 10574 | ||
| 10559 | // Standard release options allow the person running `zig build` to select | 10575 | // Standard optimization options allow the person running `zig build` to select |
| 10560 | // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. | 10576 | // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not |
| 10561 | const mode = b.standardReleaseOptions(); | 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"); | 10580 | const exe = b.addExecutable(.{ |
| 10564 | exe.setTarget(target); | 10581 | .name = "example", |
| 10565 | exe.setBuildMode(mode); | 10582 | .root_source_file = .{ .path = "src/main.zig" }, |
| 10583 | .target = target, | ||
| 10584 | .optimize = optimize, | ||
| 10585 | }); | ||
| 10566 | exe.install(); | 10586 | exe.install(); |
| 10567 | 10587 | ||
| 10568 | const run_cmd = exe.run(); | 10588 | const run_cmd = exe.run(); |
| ... | @@ -10581,16 +10601,21 @@ pub fn build(b: *Builder) void { | ... | @@ -10581,16 +10601,21 @@ pub fn build(b: *Builder) void { |
| 10581 | <p>This <code class="file">build.zig</code> file is automatically generated | 10601 | <p>This <code class="file">build.zig</code> file is automatically generated |
| 10582 | by <kbd>zig init-lib</kbd>.</p> | 10602 | by <kbd>zig init-lib</kbd>.</p> |
| 10583 | {#code_begin|syntax|build_library#} | 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 { | 10606 | pub fn build(b: *std.Build) void { |
| 10587 | const mode = b.standardReleaseOptions(); | 10607 | const optimize = b.standardOptimizeOption(.{}); |
| 10588 | const lib = b.addStaticLibrary("example", "src/main.zig"); | 10608 | const lib = b.addStaticLibrary(.{ |
| 10589 | lib.setBuildMode(mode); | 10609 | .name = "example", |
| 10610 | .root_source_file = .{ .path = "src/main.zig" }, | ||
| 10611 | .optimize = optimize, | ||
| 10612 | }); | ||
| 10590 | lib.install(); | 10613 | lib.install(); |
| 10591 | 10614 | ||
| 10592 | var main_tests = b.addTest("src/main.zig"); | 10615 | const main_tests = b.addTest(.{ |
| 10593 | main_tests.setBuildMode(mode); | 10616 | .root_source_file = .{ .path = "src/main.zig" }, |
| 10617 | .optimize = optimize, | ||
| 10618 | }); | ||
| 10594 | 10619 | ||
| 10595 | const test_step = b.step("test", "Run library tests"); | 10620 | const test_step = b.step("test", "Run library tests"); |
| 10596 | test_step.dependOn(&main_tests.step); | 10621 | test_step.dependOn(&main_tests.step); |
| ... | @@ -10949,12 +10974,17 @@ int main(int argc, char **argv) { | ... | @@ -10949,12 +10974,17 @@ int main(int argc, char **argv) { |
| 10949 | } | 10974 | } |
| 10950 | {#end_syntax_block#} | 10975 | {#end_syntax_block#} |
| 10951 | {#code_begin|syntax|build_c#} | 10976 | {#code_begin|syntax|build_c#} |
| 10952 | const Builder = @import("std").build.Builder; | 10977 | const std = @import("std"); |
| 10953 | |||
| 10954 | pub fn build(b: *Builder) void { | ||
| 10955 | const lib = b.addSharedLibrary("mathtest", "mathtest.zig", b.version(1, 0, 0)); | ||
| 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 | exe.addCSourceFile("test.c", &[_][]const u8{"-std=c99"}); | 10988 | exe.addCSourceFile("test.c", &[_][]const u8{"-std=c99"}); |
| 10959 | exe.linkLibrary(lib); | 10989 | exe.linkLibrary(lib); |
| 10960 | exe.linkSystemLibrary("c"); | 10990 | exe.linkSystemLibrary("c"); |
| ... | @@ -11011,12 +11041,17 @@ int main(int argc, char **argv) { | ... | @@ -11011,12 +11041,17 @@ int main(int argc, char **argv) { |
| 11011 | } | 11041 | } |
| 11012 | {#end_syntax_block#} | 11042 | {#end_syntax_block#} |
| 11013 | {#code_begin|syntax|build_object#} | 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 { | 11046 | pub fn build(b: *std.Build) void { |
| 11017 | const obj = b.addObject("base64", "base64.zig"); | 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 | exe.addCSourceFile("test.c", &[_][]const u8{"-std=c99"}); | 11055 | exe.addCSourceFile("test.c", &[_][]const u8{"-std=c99"}); |
| 11021 | exe.addObject(obj); | 11056 | exe.addObject(obj); |
| 11022 | exe.linkSystemLibrary("c"); | 11057 | exe.linkSystemLibrary("c"); |
lib/build_runner.zig+7-5| ... | @@ -3,7 +3,6 @@ const std = @import("std"); | ... | @@ -3,7 +3,6 @@ const std = @import("std"); |
| 3 | const builtin = @import("builtin"); | 3 | const builtin = @import("builtin"); |
| 4 | const io = std.io; | 4 | const io = std.io; |
| 5 | const fmt = std.fmt; | 5 | const fmt = std.fmt; |
| 6 | const Builder = std.build.Builder; | ||
| 7 | const mem = std.mem; | 6 | const mem = std.mem; |
| 8 | const process = std.process; | 7 | const process = std.process; |
| 9 | const ArrayList = std.ArrayList; | 8 | const ArrayList = std.ArrayList; |
| ... | @@ -42,12 +41,15 @@ pub fn main() !void { | ... | @@ -42,12 +41,15 @@ pub fn main() !void { |
| 42 | return error.InvalidArgs; | 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 | allocator, | 47 | allocator, |
| 47 | zig_exe, | 48 | zig_exe, |
| 48 | build_root, | 49 | build_root, |
| 49 | cache_root, | 50 | cache_root, |
| 50 | global_cache_root, | 51 | global_cache_root, |
| 52 | host, | ||
| 51 | ); | 53 | ); |
| 52 | defer builder.destroy(); | 54 | defer builder.destroy(); |
| 53 | 55 | ||
| ... | @@ -58,7 +60,7 @@ pub fn main() !void { | ... | @@ -58,7 +60,7 @@ pub fn main() !void { |
| 58 | const stdout_stream = io.getStdOut().writer(); | 60 | const stdout_stream = io.getStdOut().writer(); |
| 59 | 61 | ||
| 60 | var install_prefix: ?[]const u8 = null; | 62 | var install_prefix: ?[]const u8 = null; |
| 61 | var dir_list = Builder.DirList{}; | 63 | var dir_list = std.Build.DirList{}; |
| 62 | 64 | ||
| 63 | // before arg parsing, check for the NO_COLOR environment variable | 65 | // before arg parsing, check for the NO_COLOR environment variable |
| 64 | // if it exists, default the color setting to .off | 66 | // if it exists, default the color setting to .off |
| ... | @@ -230,7 +232,7 @@ pub fn main() !void { | ... | @@ -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 | // run the build script to collect the options | 236 | // run the build script to collect the options |
| 235 | if (!already_ran_build) { | 237 | if (!already_ran_build) { |
| 236 | builder.resolveInstallPrefix(null, .{}); | 238 | builder.resolveInstallPrefix(null, .{}); |
| ... | @@ -330,7 +332,7 @@ fn usage(builder: *Builder, already_ran_build: bool, out_stream: anytype) !void | ... | @@ -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 | usage(builder, already_ran_build, out_stream) catch {}; | 336 | usage(builder, already_ran_build, out_stream) catch {}; |
| 335 | process.exit(1); | 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,482 +27,545 @@ then statically linked and therefore is a transparent dependency for the |
| 27 | programmer. | 27 | programmer. |
| 28 | For details see `../compiler_rt.zig`. | 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 | Bugs should be solved by trying to duplicate the bug upstream, if possible. | 30 | Bugs should be solved by trying to duplicate the bug upstream, if possible. |
| 44 | * If the bug exists upstream, get it fixed upstream and port the fix downstream to Zig. | 31 | * If the bug exists upstream, get it fixed upstream and port the fix downstream to Zig. |
| 45 | * If the bug only exists in Zig, use the corresponding C code and debug | 32 | * If the bug only exists in Zig, use the corresponding C code and debug |
| 46 | both implementations side by side to figure out what is wrong. | 33 | both implementations side by side to figure out what is wrong. |
| 47 | 34 | ||
| 48 | ## Integer library routines | 35 | Routines with status are given below. Sources were besides |
| 49 | 36 | "The Art of Computer Programming" by Donald E. Knuth, "HackersDelight" by Henry S. Warren, | |
| 50 | #### Integer Bit operations | 37 | "Bit Twiddling Hacks" collected by Sean Eron Anderson, "Berkeley SoftFloat" by John R. Hauser, |
| 51 | 38 | LLVM "compiler-rt" as it was MIT-licensed, "musl libc" and thoughts + work of contributors. | |
| 52 | - dev HackersDelight __clzsi2 // count leading zeros | 39 | |
| 53 | - dev HackersDelight __clzdi2 // count leading zeros | 40 | The compiler-rt routines have not yet been audited. |
| 54 | - dev HackersDelight __clzti2 // count leading zeros | 41 | See https://github.com/ziglang/zig/issues/1504. |
| 55 | - dev HackersDelight __ctzsi2 // count trailing zeros | 42 | |
| 56 | - dev HackersDelight __ctzdi2 // count trailing zeros | 43 | From left to right the columns mean 1. if the routine is implemented (✗ or ✓), |
| 57 | - dev HackersDelight __ctzti2 // count trailing zeros | 44 | 2. the name, 3. input (`a`), 4. input (`b`), 5. return value, |
| 58 | - dev __ctzsi2 __ffssi2 // find least significant 1 bit | 45 | 6. an explanation of the functionality, .. to repeat the comment from the |
| 59 | - dev __ctzsi2 __ffsdi2 // find least significant 1 bit | 46 | column a row above and/or additional return values. |
| 60 | - dev __ctzsi2 __ffsti2 // find least significant 1 bit | 47 | Some routines have more extensive comments supplemented with a reference text. |
| 61 | - dev BitTwiddlingHacks __paritysi2 // bit parity | 48 | |
| 62 | - dev BitTwiddlingHacks __paritydi2 // bit parity | 49 | Integer and Float Operations |
| 63 | - dev BitTwiddlingHacks __parityti2 // bit parity | 50 | |
| 64 | - dev TAOCP __popcountsi2 // bit population | 51 | | Done | Name | a | b | Out | Comment | |
| 65 | - dev TAOCP __popcountdi2 // bit population | 52 | | ------ | ------------- | ---- | ---- | ---- | ------------------------------ | |
| 66 | - dev TAOCP __popcountti2 // bit population | 53 | | | | | | | **Integer Bit Operations** | |
| 67 | - dev other __bswapsi2 // a byteswapped | 54 | | ✓ | __clzsi2 | u32 | ∅ | i32 | count leading zeroes | |
| 68 | - dev other __bswapdi2 // a byteswapped | 55 | | ✓ | __clzdi2 | u64 | ∅ | i32 | count leading zeroes | |
| 69 | - dev other __bswapti2 // a byteswapped | 56 | | ✓ | __clzti2 | u128 | ∅ | i32 | count trailing zeros | |
| 70 | 57 | | ✓ | __ctzsi2 | u32 | ∅ | i32 | count trailing zeros | | |
| 71 | #### Integer Comparison | 58 | | ✓ | __ctzdi2 | u64 | ∅ | i32 | count trailing zeros | |
| 72 | 59 | | ✓ | __ctzti2 | u128 | ∅ | i32 | count leading zeroes | | |
| 73 | - port llvm __cmpsi2 // a,b: i32, (a<b)-> 0, (a==b) -> 1, (a>b) -> 2 | 60 | | ✓ | __ffssi2 | u32 | ∅ | i32 | count leading zeroes | |
| 74 | - port llvm __cmpdi2 // a,b: i64 | 61 | | ✓ | __ffsdi2 | u64 | ∅ | i32 | count leading zeroes | |
| 75 | - port llvm __cmpti2 // a,b: i128 | 62 | | ✓ | __ffsti2 | u128 | ∅ | i32 | count leading zeroes | |
| 76 | - port llvm __ucmpsi2 // a,b: u32, (a<b)-> 0, (a==b) -> 1, (a>b) -> 2 | 63 | | ✓ | __paritysi2 | u32 | ∅ | i32 | find least significant 1 bit | |
| 77 | - port llvm __ucmpdi2 // a,b: u64 | 64 | | ✓ | __paritydi2 | u64 | ∅ | i32 | find least significant 1 bit | |
| 78 | - port llvm __ucmpti2 // a,b: u128 | 65 | | ✓ | __parityti2 | u128 | ∅ | i32 | find least significant 1 bit | |
| 79 | 66 | | ✓ | __popcountsi2 | u32 | ∅ | i32 | bit population | | |
| 80 | #### Integer Arithmetic | 67 | | ✓ | __popcountdi2 | u64 | ∅ | i32 | bit population | |
| 81 | 68 | | ✓ | __popcountti2 | u128 | ∅ | i32 | bit population | | |
| 82 | - none none __ashlsi3 // a,b: i32, a << b unused in llvm, TODO (e.g. used by rl78) | 69 | | ✓ | __bswapsi2 | u32 | ∅ | i32 | bit parity | |
| 83 | - port llvm __ashldi3 // a,b: u64 | 70 | | ✓ | __bswapdi2 | u64 | ∅ | i32 | bit parity | |
| 84 | - port llvm __ashlti3 // a,b: u128 | 71 | | ✓ | __bswapti2 | u128 | ∅ | i32 | bit parity | |
| 85 | - none none __ashrsi3 // a,b: i32, a >> b arithmetic (sign fill) TODO (e.g. used by rl78) | 72 | | | | | | | **Integer Comparison** | |
| 86 | - port llvm __ashrdi3 // .. | 73 | | ✓ | __cmpsi2 | i32 | i32 | i32 | `(a<b) -> 0, (a==b) -> 1, (a>b) -> 2` | |
| 87 | - port llvm __ashrti3 // | 74 | | ✓ | __cmpdi2 | i64 | i64 | i32 | .. | |
| 88 | - none none __lshrsi3 // a,b: i32, a >> b logical (zero fill) TODO (e.g. used by rl78) | 75 | | ✓ | __cmpti2 | i128 | i128 | i32 | .. | |
| 89 | - port llvm __lshrdi3 // | 76 | | ✓ | __ucmpsi2 | i32 | i32 | i32 | `(a<b) -> 0, (a==b) -> 1, (a>b) -> 2` | |
| 90 | - port llvm __lshrti3 // | 77 | | ✓ | __ucmpdi2 | i64 | i64 | i32 | .. | |
| 91 | - port llvm __negdi2 // a: i32, -a, symbol-level compatibility with libgcc | 78 | | ✓ | __ucmpti2 | i128 | i128 | i32 | .. | |
| 92 | - port llvm __negti2 // unnecessary: unused in backends | 79 | | | | | | | **Integer Arithmetic** | |
| 93 | - port llvm __mulsi3 // a,b: i32, a * b | 80 | | ✗ | __ashlsi3 | i32 | i32 | i32 | `a << b` [^unused_rl78] | |
| 94 | - port llvm __muldi3 // | 81 | | ✓ | __ashldi3 | i64 | i32 | i64 | .. | |
| 95 | - port llvm __multi3 // | 82 | | ✓ | __ashlti3 | i128 | i32 | i128 | .. | |
| 96 | - port llvm __divsi3 // a,b: i32, a / b | 83 | | ✓ | __aeabi_llsl | i32 | i32 | i32 | .. ARM | |
| 97 | - port llvm __divdi3 // | 84 | | ✗ | __ashrsi3 | i32 | i32 | i32 | `a >> b` arithmetic (sign fill) [^unused_rl78] | |
| 98 | - port llvm __divti3 // | 85 | | ✓ | __ashrdi3 | i64 | i32 | i64 | .. | |
| 99 | - port llvm __udivsi3 // a,b: u32, a / b | 86 | | ✓ | __ashrti3 | i128 | i32 | i128 | .. | |
| 100 | - port llvm __udivdi3 // | 87 | | ✓ | __aeabi_lasr | i64 | i32 | i64 | .. ARM | |
| 101 | - port llvm __udivti3 // | 88 | | ✗ | __lshrsi3 | i32 | i32 | i32 | `a >> b` logical (zero fill) [^unused_rl78] | |
| 102 | - port llvm __modsi3 // a,b: i32, a % b | 89 | | ✓ | __lshrdi3 | i64 | i32 | i64 | .. | |
| 103 | - port llvm __moddi3 // | 90 | | ✓ | __lshrti3 | i128 | i32 | i128 | .. | |
| 104 | - port llvm __modti3 // | 91 | | ✓ | __aeabi_llsr | i64 | i32 | i64 | .. ARM | |
| 105 | - port llvm __umodsi3 // a,b: u32, a % b | 92 | | ✓ | __negsi2 | i32 | i32 | i32 | `-a` [^libgcc_compat] | |
| 106 | - port llvm __umoddi3 // | 93 | | ✓ | __negdi2 | i64 | i64 | i64 | .. | |
| 107 | - port llvm __umodti3 // | 94 | | ✓ | __negti2 | i128 | i128 | i128 | .. | |
| 108 | - port llvm __udivmoddi4 // a,b: u32, a / b, rem.* = a % b unsigned | 95 | | ✓ | __mulsi3 | i32 | i32 | i32 | `a * b` | |
| 109 | - port llvm __udivmodti4 // | 96 | | ✓ | __muldi3 | i64 | i64 | i64 | .. | |
| 110 | - port llvm __udivmodsi4 // | 97 | | ✓ | __multi3 | i128 | i128 | i128 | .. | |
| 111 | - port llvm __divmodsi4 // a,b: i32, a / b, rem.* = a % b signed, ARM | 98 | | ✓ | __divsi3 | i32 | i32 | i32 | `a / b` | |
| 112 | - port llvm __divmoddi4 // | 99 | | ✓ | __divdi3 | i64 | i64 | i64 | .. | |
| 113 | 100 | | ✓ | __divti3 | i128 | i128 | i128 | .. | | |
| 114 | #### Integer Arithmetic with trapping overflow | 101 | | ✓ | __aeabi_idiv | i32 | i32 | i32 | .. ARM | |
| 115 | 102 | | ✓ | __udivsi3 | u32 | u32 | u32 | `a / b` | | |
| 116 | - dev BitTwiddlingHacks __absvsi2 // abs(a) | 103 | | ✓ | __udivdi3 | u64 | u64 | u64 | .. | |
| 117 | - dev BitTwiddlingHacks __absvdi2 // abs(a) | 104 | | ✓ | __udivti3 | u128 | u128 | u128 | .. | |
| 118 | - dev BitTwiddlingHacks __absvti2 // abs(a) | 105 | | ✓ | __aeabi_uidiv | i32 | i32 | i32 | .. ARM | |
| 119 | - port llvm __negvsi2 // -a symbol-level compatibility: libgcc | 106 | | ✓ | __modsi3 | i32 | i32 | i32 | `a % b` | |
| 120 | - port llvm __negvdi2 // -a unnecessary: unused in backends | 107 | | ✓ | __moddi3 | i64 | i64 | i64 | .. | |
| 121 | - port llvm __negvti2 // -a | 108 | | ✓ | __modti3 | i128 | i128 | i128 | .. | |
| 122 | - TODO upstreaming __addvsi3..__mulvti3 after testing panics works | 109 | | ✓ | __umodsi3 | u32 | u32 | u32 | `a % b` | |
| 123 | - dev HackersDelight __addvsi3 // a + b | 110 | | ✓ | __umoddi3 | u64 | u64 | u64 | .. | |
| 124 | - dev HackersDelight __addvdi3 // | 111 | | ✓ | __umodti3 | u128 | u128 | u128 | .. | |
| 125 | - dev HackersDelight __addvti3 // | 112 | | ✓ | __udivmodsi4 | u32 | u32 | u32 | `a / b, rem.* = a % b` | |
| 126 | - dev HackersDelight __subvsi3 // a - b | 113 | | ✓ | __udivmoddi4 | u64 | u64 | u64 | .. | |
| 127 | - dev HackersDelight __subvdi3 // | 114 | | ✓ | __udivmodti4 | u128 | u128 | u128 | .. | |
| 128 | - dev HackersDelight __subvti3 // | 115 | | ✓ | __divmodsi4 | i32 | i32 | i32 | `a / b, rem.* = a % b` | |
| 129 | - dev HackersDelight __mulvsi3 // a * b | 116 | | ✓ | __divmoddi4 | i64 | i64 | i64 | .. | |
| 130 | - dev HackersDelight __mulvdi3 // | 117 | | ✗ | __divmodti4 | i128 | i128 | i128 | .. [^libgcc_compat] | |
| 131 | - dev HackersDelight __mulvti3 // | 118 | | | | | | | **Integer Arithmetic with Trapping Overflow**| |
| 132 | 119 | | ✓ | __absvsi2 | i32 | i32 | i32 | abs(a) | | |
| 133 | #### Integer Arithmetic which returns if overflow (would be faster without pointer) | 120 | | ✓ | __absvdi2 | i64 | i64 | i64 | .. | |
| 134 | 121 | | ✓ | __absvti2 | i128 | i128 | i128 | .. | | |
| 135 | - dev HackersDelight __addosi4 // a + b, overflow->ov.*=1 else 0 | 122 | | ✓ | __negvsi2 | i32 | i32 | i32 | `-a` [^libgcc_compat] | |
| 136 | - dev HackersDelight __addodi4 // (completeness + performance, llvm does not use them) | 123 | | ✓ | __negvdi2 | i64 | i64 | i64 | .. | |
| 137 | - dev HackersDelight __addoti4 // | 124 | | ✓ | __negvti2 | i128 | i128 | i128 | .. | |
| 138 | - dev HackersDelight __subosi4 // a - b, overflow->ov.*=1 else 0 | 125 | | ✗ | __addvsi3 | i32 | i32 | i32 | `a + b` | |
| 139 | - dev HackersDelight __subodi4 // (completeness + performance, llvm does not use them) | 126 | | ✗ | __addvdi3 | i64 | i64 | i64 | .. | |
| 140 | - dev HackersDelight __suboti4 // | 127 | | ✗ | __addvti3 | i128 | i128 | i128 | .. | |
| 141 | - dev HackersDelight __mulosi4 // a * b, overflow->ov.*=1 else 0 | 128 | | ✗ | __subvsi3 | i32 | i32 | i32 | `a - b` | |
| 142 | - dev HackersDelight __mulodi4 // (required by llvm) | 129 | | ✗ | __subvdi3 | i64 | i64 | i64 | .. | |
| 143 | - dev HackersDelight __muloti4 // | 130 | | ✗ | __subvti3 | i128 | i128 | i128 | .. | |
| 144 | 131 | | ✗ | __mulvsi3 | i32 | i32 | i32 | `a * b` | | |
| 145 | ## Float library routines | 132 | | ✗ | __mulvdi3 | i64 | i64 | i64 | .. | |
| 146 | 133 | | ✗ | __mulvti3 | i128 | i128 | i128 | .. | | |
| 147 | TODO: review source of implementation | 134 | | | | | | | **Integer Arithmetic which Return on Overflow** [^noptr_faster] | |
| 148 | 135 | | ✓ | __addosi4 | i32 | i32 | i32 | `a + b`, overflow->ov.*=1 else 0 [^perf_addition] | | |
| 149 | #### Float Conversion | 136 | | ✓ | __addodi4 | i64 | i64 | i64 | .. | |
| 150 | 137 | | ✓ | __addoti4 | i128 | i128 | i128 | .. | | |
| 151 | - dev other __extendsfdf2 // a: f32 -> f64, TODO: missing tests | 138 | | ✓ | __subosi4 | i32 | i32 | i32 | `a - b`, overflow->ov.*=1 else 0 [^perf_addition] | |
| 152 | - dev other __extendsftf2 // a: f32 -> f128 | 139 | | ✓ | __subodi4 | i64 | i64 | i64 | .. | |
| 153 | - dev llvm __extendsfxf2 // a: f32 -> f80, TODO: missing tests | 140 | | ✓ | __suboti4 | i128 | i128 | i128 | .. | |
| 154 | - dev other __extenddftf2 // a: f64 -> f128 | 141 | | ✓ | __mulosi4 | i32 | i32 | i32 | `a * b`, overflow->ov.*=1 else 0 | |
| 155 | - dev llvm __extenddfxf2 // a: f64 -> f80 | 142 | | ✓ | __mulodi4 | i64 | i64 | i64 | .. | |
| 156 | - dev other __truncdfsf2 // a: f64 -> f32, rounding towards zero | 143 | | ✓ | __muloti4 | i128 | i128 | i128 | .. | |
| 157 | - dev other __trunctfdf2 // a: f128-> f64 | 144 | | | | | | | **Float Conversion** | |
| 158 | - dev other __trunctfsf2 // a: f128-> f32 | 145 | | ✓ | __extendsfdf2 | f32 | ∅ | f64 | .. | |
| 159 | - dev llvm __truncxfsf2 // a: f80 -> f32, TODO: missing tests | 146 | | ✓ | __extendsftf2 | f32 | ∅ | f128 | .. | |
| 160 | - dev llvm __truncxfdf2 // a: f80 -> f64, TODO: missing tests | 147 | | ✓ | __extendsfxf2 | f32 | ∅ | f80 | .. | |
| 161 | 148 | | ✓ | __extenddftf2 | f64 | ∅ | f128 | .. | | |
| 162 | - dev unclear __fixsfsi // a: f32 -> i32, rounding towards zero | 149 | | ✓ | __extenddfxf2 | f64 | ∅ | f80 | .. | |
| 163 | - dev unclear __fixdfsi // a: f64 -> i32 | 150 | | ✓ | __truncsfhf2 | f32 | ∅ | f16 | rounding towards zero | |
| 164 | - dev unclear __fixtfsi // a: f128-> i32 | 151 | | ✓ | __truncdfhf2 | f64 | ∅ | f16 | .. | |
| 165 | - dev unclear __fixxfsi // a: f80 -> i32, TODO: missing tests | 152 | | ✓ | __truncdfsf2 | f64 | ∅ | f32 | .. | |
| 166 | - dev unclear __fixsfdi // a: f32 -> i64, rounding towards zero | 153 | | ✓ | __trunctfhf2 | f128 | ∅ | f16 | .. | |
| 167 | - dev unclear __fixdfdi // .. | 154 | | ✓ | __trunctfsf2 | f128 | ∅ | f32 | .. | |
| 168 | - dev unclear __fixtfdi // | 155 | | ✓ | __trunctfdf2 | f128 | ∅ | f64 | .. | |
| 169 | - dev unclear __fixxfdi // TODO: missing tests | 156 | | ✓ | __trunctfxf2 | f128 | ∅ | f80 | .. | |
| 170 | - dev unclear __fixsfti // a: f32 -> i128, rounding towards zero | 157 | | ✓ | __truncxfhf2 | f80 | ∅ | f16 | .. | |
| 171 | - dev unclear __fixdfti // .. | 158 | | ✓ | __truncxfsf2 | f80 | ∅ | f32 | .. | |
| 172 | - dev unclear __fixtfdi // | 159 | | ✓ | __truncxfdf2 | f80 | ∅ | f64 | .. | |
| 173 | - dev unclear __fixxfti // TODO: missing tests | 160 | | ✓ | __aeabi_f2h | f32 | ∅ | f16 | .. ARM | |
| 174 | 161 | | ✓ | __gnu_f2h_ieee | f32 | ∅ | f16 | ..GNU naming convention | | |
| 175 | - dev unclear __fixunssfsi // a: f32 -> u32, rounding towards zero. negative values become 0. | 162 | | ✓ | __aeabi_d2h | f64 | ∅ | f16 | .. ARM | |
| 176 | - dev unclear __fixunsdfsi // .. | 163 | | ✓ | __aeabi_d2f | f64 | ∅ | f32 | .. ARM | |
| 177 | - dev unclear __fixunstfsi // | 164 | | ✓ | __trunckfsf2 | f128 | ∅ | f32 | .. PPC | |
| 178 | - dev unclear __fixunsxfsi // TODO: missing tests | 165 | | ✓ | _Qp_qtos |*f128 | ∅ | f32 | .. SPARC | |
| 179 | - dev unclear __fixunssfdi // a: f32 -> u64, rounding towards zero. negative values become 0. | 166 | | ✓ | __trunckfdf2 | f128 | ∅ | f64 | .. PPC | |
| 180 | - dev unclear __fixunsdfdi // | 167 | | ✓ | _Qp_qtod |*f128 | ∅ | f64 | .. SPARC | |
| 181 | - dev unclear __fixunstfdi // | 168 | | ✓ | __fixhfsi | f16 | ∅ | i32 | rounding towards zero | |
| 182 | - dev unclear __fixunsxfdi // TODO: missing tests | 169 | | ✓ | __fixsfsi | f32 | ∅ | i32 | .. | |
| 183 | - dev unclear __fixunssfti // a: f32 -> u128, rounding towards zero. negative values become 0. | 170 | | ✓ | __fixdfsi | f64 | ∅ | i32 | .. | |
| 184 | - dev unclear __fixunsdfti // | 171 | | ✓ | __fixtfsi | f128 | ∅ | i32 | .. | |
| 185 | - dev unclear __fixunstfdi // | 172 | | ✓ | __fixxfsi | f80 | ∅ | i32 | .. | |
| 186 | - dev unclear __fixunsxfti // TODO: some more tests needed for base coverage | 173 | | ✓ | __fixhfdi | f16 | ∅ | i64 | .. | |
| 187 | 174 | | ✓ | __fixsfdi | f32 | ∅ | i64 | .. | | |
| 188 | - dev unclear __floatsisf // a: i32 -> f32 | 175 | | ✓ | __fixdfdi | f64 | ∅ | i64 | .. | |
| 189 | - dev unclear __floatsidf // a: i32 -> f64, TODO: missing tests | 176 | | ✓ | __fixtfdi | f128 | ∅ | i64 | .. | |
| 190 | - dev unclear __floatsitf // .. | 177 | | ✓ | __fixxfdi | f80 | ∅ | i64 | .. | |
| 191 | - dev unclear __floatsixf // TODO: missing tests | 178 | | ✓ | __fixhfti | f16 | ∅ | i128 | .. | |
| 192 | - dev unclear __floatdisf // a: i64 -> f32 | 179 | | ✓ | __fixsfti | f32 | ∅ | i128 | .. | |
| 193 | - dev unclear __floatdidf // | 180 | | ✓ | __fixdfti | f64 | ∅ | i128 | .. | |
| 194 | - dev unclear __floatditf // | 181 | | ✓ | __fixtfti | f128 | ∅ | i128 | .. | |
| 195 | - dev unclear __floatdixf // TODO: missing tests | 182 | | ✓ | __fixxfti | f80 | ∅ | i128 | .. | |
| 196 | - dev unclear __floattisf // a: i128-> f32 | 183 | | ✓ | __fixunshfsi | f16 | ∅ | u32 | rounding towards zero. negative values become 0. | |
| 197 | - dev unclear __floattidf // | 184 | | ✓ | __fixunssfsi | f32 | ∅ | u32 | .. | |
| 198 | - dev unclear __floattitf // | 185 | | ✓ | __fixunsdfsi | f64 | ∅ | u32 | .. | |
| 199 | - dev unclear __floattixf // TODO: missing tests | 186 | | ✓ | __fixunstfsi | f128 | ∅ | u32 | .. | |
| 200 | 187 | | ✓ | __fixunsxfsi | f80 | ∅ | u32 | .. | | |
| 201 | - dev unclear __floatunsisf // a: u32 -> f32 | 188 | | ✓ | __fixunshfdi | f16 | ∅ | u64 | .. | |
| 202 | - dev unclear __floatunsidf // TODO: missing tests | 189 | | ✓ | __fixunssfdi | f32 | ∅ | u64 | .. | |
| 203 | - dev unclear __floatunsitf // | 190 | | ✓ | __fixunsdfdi | f64 | ∅ | u64 | .. | |
| 204 | - dev unclear __floatunsixf // TODO: missing tests | 191 | | ✓ | __fixunstfdi | f128 | ∅ | u64 | .. | |
| 205 | - dev unclear __floatundisf // a: u64 -> f32 | 192 | | ✓ | __fixunsxfdi | f80 | ∅ | u64 | .. | |
| 206 | - dev unclear __floatundidf // | 193 | | ✓ | __fixunshfti | f16 | ∅ | u128 | .. | |
| 207 | - dev unclear __floatunditf // | 194 | | ✓ | __fixunssfti | f32 | ∅ | u128 | .. | |
| 208 | - dev unclear __floatundixf // TODO: missing tests | 195 | | ✓ | __fixunsdfti | f64 | ∅ | u128 | .. | |
| 209 | - dev unclear __floatuntisf // a: u128-> f32 | 196 | | ✓ | __fixunstfti | f128 | ∅ | u128 | .. | |
| 210 | - dev unclear __floatuntidf // | 197 | | ✓ | __fixunsxfti | f80 | ∅ | u128 | .. | |
| 211 | - dev unclear __floatuntitf // | 198 | | ✓ | __floatsihf | i32 | ∅ | f16 | int_to_float conversions | |
| 212 | - dev unclear __floatuntixf // TODO: missing tests | 199 | | ✓ | __floatsisf | i32 | ∅ | f32 | .. | |
| 213 | 200 | | ✓ | __floatsidf | i32 | ∅ | f64 | .. | | |
| 214 | #### Float Comparison | 201 | | ✓ | __floatsitf | i32 | ∅ | f128 | .. | |
| 215 | 202 | | ✓ | __floatsixf | i32 | ∅ | f80 | .. | | |
| 216 | - dev other __cmpsf2 // a,b:f32, (a<b)->-1,(a==b)->0,(a>b)->1,Nan->1 | 203 | | ✓ | __floatdisf | i64 | ∅ | f32 | .. | |
| 217 | - dev other __cmpdf2 // exported from __lesf2, __ledf2, __letf2 (below) | 204 | | ✓ | __floatdidf | i64 | ∅ | f64 | .. | |
| 218 | - dev other __cmptf2 // But: if NaN is a possibility, use another routine. | 205 | | ✓ | __floatditf | i64 | ∅ | f128 | .. | |
| 219 | - dev other __unordsf2 // a,b:f32, (a==+-NaN or b==+-NaN) -> !=0, else -> 0 | 206 | | ✓ | __floatdixf | i64 | ∅ | f80 | .. | |
| 220 | - dev other __unorddf2 // __only reliable for (input!=NaN)__ | 207 | | ✓ | __floattihf | i128 | ∅ | f16 | .. | |
| 221 | - dev other __unordtf2 // TODO: missing tests | 208 | | ✓ | __floattisf | i128 | ∅ | f32 | .. | |
| 222 | - dev other __eqsf2 // (a!=NaN) and (b!=Nan) and (a==b) -> output=0 | 209 | | ✓ | __floattidf | i128 | ∅ | f64 | .. | |
| 223 | - dev other __eqdf2 // | 210 | | ✓ | __floattitf | i128 | ∅ | f128 | .. | |
| 224 | - dev other __eqtf2 // | 211 | | ✓ | __floattixf | i128 | ∅ | f80 | .. | |
| 225 | - dev other __nesf2 // (a==NaN) or (b==Nan) or (a!=b) -> output!=0 | 212 | | ✓ | __floatunsihf | u32 | ∅ | f16 | uint_to_float conversions | |
| 226 | - dev other __nedf2 // | 213 | | ✓ | __floatunsisf | u32 | ∅ | f32 | .. | |
| 227 | - dev other __netf2 // __eqtf2 and __netf2 have same return value -> tested with __eqsf2 | 214 | | ✓ | __floatunsidf | u32 | ∅ | f64 | .. | |
| 228 | - dev other __gesf2 // (a!=Nan) and (b!=Nan) and (a>=b) -> output>=0 | 215 | | ✓ | __floatunsitf | u32 | ∅ | f128 | .. | |
| 229 | - dev other __gedf2 // | 216 | | ✓ | __floatunsixf | u32 | ∅ | f80 | .. | |
| 230 | - dev other __getf2 // TODO: missing tests | 217 | | ✓ | __floatundihf | u64 | ∅ | f16 | .. | |
| 231 | - dev other __ltsf2 // (a!=Nan) and (b!=Nan) and (a<b) -> output<0 | 218 | | ✓ | __floatundisf | u64 | ∅ | f32 | .. | |
| 232 | - dev other __ltdf2 // | 219 | | ✓ | __floatundidf | u64 | ∅ | f64 | .. | |
| 233 | - dev other __lttf2 // TODO: missing tests | 220 | | ✓ | __floatunditf | u64 | ∅ | f128 | .. | |
| 234 | - dev other __lesf2 // (a!=Nan) and (b!=Nan) and (a<=b) -> output<=0 | 221 | | ✓ | __floatundixf | u64 | ∅ | f80 | .. | |
| 235 | - dev other __ledf2 // | 222 | | ✓ | __floatuntihf | u128 | ∅ | f16 | .. | |
| 236 | - dev other __letf2 // TODO: missing tests | 223 | | ✓ | __floatuntisf | u128 | ∅ | f32 | .. | |
| 237 | - dev other __gtsf2 // (a!=Nan) and (b!=Nan) and (a>b) -> output>0 | 224 | | ✓ | __floatuntidf | u128 | ∅ | f64 | .. | |
| 238 | - dev other __gtdf2 // | 225 | | ✓ | __floatuntitf | u128 | ∅ | f128 | .. | |
| 239 | - dev other __gttf2 // TODO: missing tests | 226 | | ✓ | __floatuntixf | u128 | ∅ | f80 | .. | |
| 240 | 227 | | | | | | | **Float Comparison** | | |
| 241 | #### Float Arithmetic | 228 | | ✓ | __cmphf2 | f16 | f16 | i32 | `(a<b)->-1, (a==b)->0, (a>b)->1, Nan->1` | |
| 242 | 229 | | ✓ | __cmpsf2 | f32 | f32 | i32 | exported from __lesf2, __ledf2, __letf2 (below) | | |
| 243 | - dev unclear __addsf3 // a + b f32, TODO: missing tests | 230 | | ✓ | __cmpdf2 | f64 | f64 | i32 | But: if NaN is a possibility, use another routine. | |
| 244 | - dev unclear __adddf3 // a + b f64, TODO: missing tests | 231 | | ✓ | __cmptf2 | f128 | f128 | i32 | .. | |
| 245 | - dev unclear __addtf3 // a + b f128 | 232 | | ✓ | __cmpxf2 | f80 | f80 | i32 | .. | |
| 246 | - dev unclear __addxf3 // a + b f80 | 233 | | ✓ | _Qp_cmp |*f128 |*f128 | i32 | .. SPARC | |
| 247 | - dev unclear __aeabi_fadd // a + b f64 ARM: AAPCS | 234 | | ✓ | __unordhf2 | f16 | f16 | i32 | `(a==+-NaN or b==+-NaN) -> !=0, else -> 0` | |
| 248 | - dev unclear __aeabi_dadd // a + b f64 ARM: AAPCS | 235 | | ✓ | __unordsf2 | f32 | f32 | i32 | .. | |
| 249 | - dev unclear __subsf3 // a - b, TODO: missing tests | 236 | | ✓ | __unorddf2 | f64 | f64 | i32 | Note: only reliable for (input!=NaN) | |
| 250 | - dev unclear __subdf3 // a - b, TODO: missing tests | 237 | | ✓ | __unordtf2 | f128 | f128 | i32 | .. | |
| 251 | - dev unclear __subtf3 // a - b | 238 | | ✓ | __unordxf2 | f80 | f80 | i32 | .. | |
| 252 | - dev unclear __subxf3 // a - b f80, TODO: missing tests | 239 | | ✓ | __aeabi_fcmpun | f32 | f32 | i32 | .. ARM | |
| 253 | - dev unclear __aeabi_fsub // a - b f64 ARM: AAPCS | 240 | | ✓ | __aeabi_dcmpun | f32 | f32 | i32 | .. ARM | |
| 254 | - dev unclear __aeabi_dsub // a - b f64 ARM: AAPCS | 241 | | ✓ | __unordkf2 | f128 | f128 | i32 | .. PPC | |
| 255 | - dev unclear __mulsf3 // a * b, TODO: missing tests | 242 | | ✓ | __eqhf2 | f16 | f16 | i32 | `(a!=NaN) and (b!=Nan) and (a==b) -> output=0` | |
| 256 | - dev unclear __muldf3 // a * b, TODO: missing tests | 243 | | ✓ | __eqsf2 | f32 | f32 | i32 | .. | |
| 257 | - dev unclear __multf3 // a * b | 244 | | ✓ | __eqdf2 | f64 | f64 | i32 | .. | |
| 258 | - dev unclear __mulxf3 // a * b | 245 | | ✓ | __eqtf2 | f128 | f128 | i32 | .. | |
| 259 | - dev unclear __divsf3 // a / b, TODO: review tests | 246 | | ✓ | __eqxf2 | f80 | f80 | i32 | .. | |
| 260 | - dev unclear __divdf3 // a / b, TODO: review tests | 247 | | ✓ | __aeabi_fcmpeq | f32 | f32 | i32 | .. ARM | |
| 261 | - dev unclear __divtf3 // a / b | 248 | | ✓ | __aeabi_dcmpeq | f32 | f32 | i32 | .. ARM | |
| 262 | - dev unclear __divxf3 // a / b | 249 | | ✓ | __eqkf2 | f128 | f128 | i32 | .. PPC | |
| 263 | - dev unclear __negsf2 // -a symbol-level compatibility: libgcc uses this for the rl78 | 250 | | ✓ | _Qp_feq |*f128 |*f128 | bool | .. SPARC | |
| 264 | - dev unclear __negdf2 // -a unnecessary: can be lowered directly to a xor | 251 | | ✓ | __nehf2 | f16 | f16 | i32 | `(a==NaN) or (b==Nan) or (a!=b) -> output!=0` | |
| 265 | - dev unclear __negtf2 // -a, TODO: missing tests | 252 | | ✓ | __nesf2 | f32 | f32 | i32 | Note: __eqXf2 and __neXf2 have same return value | |
| 266 | - dev unclear __negxf2 // -a, TODO: missing tests | 253 | | ✓ | __nedf2 | f64 | f64 | i32 | .. | |
| 267 | 254 | | ✓ | __netf2 | f128 | f128 | i32 | .. | | |
| 268 | #### Floating point raised to integer power | 255 | | ✓ | __nexf2 | f80 | f80 | i32 | .. | |
| 269 | - dev unclear __powisf2 // a ^ b, TODO | 256 | | ✓ | __nekf2 | f128 | f128 | i32 | .. PPC | |
| 270 | - dev unclear __powidf2 // | 257 | | ✓ | _Qp_fne |*f128 |*f128 | bool | .. SPARC | |
| 271 | - dev unclear __powitf2 // | 258 | | ✓ | __gehf2 | f16 | f16 | i32 | `(a!=Nan) and (b!=Nan) and (a>=b) -> output>=0` | |
| 272 | - dev unclear __powixf2 // | 259 | | ✓ | __gesf2 | f32 | f32 | i32 | .. | |
| 273 | - dev unclear __mulsc3 // (a+ib) * (c+id) | 260 | | ✓ | __gedf2 | f64 | f64 | i32 | .. | |
| 274 | - dev unclear __muldc3 // | 261 | | ✓ | __getf2 | f128 | f128 | i32 | .. | |
| 275 | - dev unclear __multc3 // | 262 | | ✓ | __gexf2 | f80 | f80 | i32 | .. | |
| 276 | - dev unclear __mulxc3 // | 263 | | ✓ | __gekf2 | f128 | f128 | i32 | .. PPC | |
| 277 | - dev unclear __divsc3 // (a+ib) * / (c+id) | 264 | | ✓ | _Qp_fge |*f128 |*f128 | bool | .. SPARC | |
| 278 | - dev unclear __divdc3 // | 265 | | ✓ | __lthf2 | f16 | f16 | i32 | `(a!=Nan) and (b!=Nan) and (a<b) -> output<0` | |
| 279 | - dev unclear __divtc3 // | 266 | | ✓ | __ltsf2 | f32 | f32 | i32 | .. | |
| 280 | - dev unclear __divxc3 // | 267 | | ✓ | __ltdf2 | f64 | f64 | i32 | .. | |
| 281 | 268 | | ✓ | __lttf2 | f128 | f128 | i32 | .. | | |
| 282 | ## Decimal float library routines | 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 | BID means Binary Integer Decimal encoding, DPD means Densely Packed Decimal encoding. | 355 | BID means Binary Integer Decimal encoding, DPD means Densely Packed Decimal encoding. |
| 285 | BID should be only chosen for binary data, DPD for decimal data (ASCII, Unicode etc). | 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 | 357 | For example the number 0.2 is not accurately representable in binary data. |
| 287 | in binary like the number 0.2. | 358 | |
| 288 | 359 | | Done | Name | a | b | Out | Comment | | |
| 289 | All routines are TODO. | 360 | | ------ | ------------- | --------- | --------- | --------- | ---------------------------- | |
| 290 | 361 | | | | | | | **Decimal Float Conversion** | | |
| 291 | #### Decimal float Conversion | 362 | | ✗ | __dpd_extendsddd2 | dec32 | ∅ | dec64 | conversion | |
| 292 | 363 | | ✗ | __bid_extendsddd2 | dec32 | ∅ | dec64 | .. | | |
| 293 | - __dpd_extendsddd2 // dec32->dec64 | 364 | | ✗ | __dpd_extendsdtd2 | dec32 | ∅ | dec128| .. | |
| 294 | - __bid_extendsddd2 // dec32->dec64 | 365 | | ✗ | __bid_extendsdtd2 | dec32 | ∅ | dec128| .. | |
| 295 | - __dpd_extendsdtd2 // dec32->dec128 | 366 | | ✗ | __dpd_extendddtd2 | dec64 | ∅ | dec128| .. | |
| 296 | - __bid_extendsdtd2 // dec32->dec128 | 367 | | ✗ | __bid_extendddtd2 | dec64 | ∅ | dec128| .. | |
| 297 | - __dpd_extendddtd2 // dec64->dec128 | 368 | | ✗ | __dpd_truncddsd2 | dec64 | ∅ | dec32 | .. | |
| 298 | - __bid_extendddtd2 // dec64->dec128 | 369 | | ✗ | __bid_truncddsd2 | dec64 | ∅ | dec32 | .. | |
| 299 | - __dpd_truncddsd2 // dec64->dec32 | 370 | | ✗ | __dpd_trunctdsd2 | dec128 | ∅ | dec32 | .. | |
| 300 | - __bid_truncddsd2 // dec64->dec32 | 371 | | ✗ | __bid_trunctdsd2 | dec128 | ∅ | dec32 | .. | |
| 301 | - __dpd_trunctdsd2 // dec128->dec32 | 372 | | ✗ | __dpd_trunctddd2 | dec128 | ∅ | dec64 | .. | |
| 302 | - __bid_trunctdsd2 // dec128->dec32 | 373 | | ✗ | __bid_trunctddd2 | dec128 | ∅ | dec64 | .. | |
| 303 | - __dpd_trunctddd2 // dec128->dec64 | 374 | | ✗ | __dpd_extendsfdd | float | ∅ | dec64 | .. | |
| 304 | - __bid_trunctddd2 // dec128->dec64 | 375 | | ✗ | __bid_extendsfdd | float | ∅ | dec64 | .. | |
| 305 | 376 | | ✗ | __dpd_extendsftd | float | ∅ | dec128| .. | | |
| 306 | - __dpd_extendsfdd // float->dec64 | 377 | | ✗ | __bid_extendsftd | float | ∅ | dec128| .. | |
| 307 | - __bid_extendsfdd // float->dec64 | 378 | | ✗ | __dpd_extenddftd | double | ∅ | dec128| .. | |
| 308 | - __dpd_extendsftd // float->dec128 | 379 | | ✗ | __bid_extenddftd | double | ∅ | dec128| .. | |
| 309 | - __bid_extendsftd // float->dec128 | 380 | | ✗ | __dpd_extendxftd |long double | ∅ | dec128| .. | |
| 310 | - __dpd_extenddftd // double->dec128 | 381 | | ✗ | __bid_extendxftd |long double | ∅ | dec128| .. | |
| 311 | - __bid_extenddftd // double->dec128 | 382 | | ✗ | __dpd_truncdfsd | double | ∅ | dec32 | .. | |
| 312 | - __dpd_extendxftd // long double->dec128 | 383 | | ✗ | __bid_truncdfsd | double | ∅ | dec32 | .. | |
| 313 | - __bid_extendxftd // long double->dec128 | 384 | | ✗ | __dpd_truncxfsd |long double | ∅ | dec32 | .. | |
| 314 | - __dpd_truncdfsd // double->dec32 | 385 | | ✗ | __bid_truncxfsd |long double | ∅ | dec32 | .. | |
| 315 | - __bid_truncdfsd // double->dec32 | 386 | | ✗ | __dpd_trunctfsd |long double | ∅ | dec32 | .. | |
| 316 | - __dpd_truncxfsd // long double->dec32 | 387 | | ✗ | __bid_trunctfsd |long double | ∅ | dec32 | .. | |
| 317 | - __bid_truncxfsd // long double->dec32 | 388 | | ✗ | __dpd_truncxfdd |long double | ∅ | dec64 | .. | |
| 318 | - __dpd_trunctfsd // long double->dec32 | 389 | | ✗ | __bid_truncxfdd |long double | ∅ | dec64 | .. | |
| 319 | - __bid_trunctfsd // long double->dec32 | 390 | | ✗ | __dpd_trunctfdd |long double | ∅ | dec64 | .. | |
| 320 | - __dpd_truncxfdd // long double->dec64 | 391 | | ✗ | __bid_trunctfdd |long double | ∅ | dec64 | .. | |
| 321 | - __bid_truncxfdd // long double->dec64 | 392 | | ✗ | __dpd_truncddsf | dec64 | ∅ | float | .. | |
| 322 | - __dpd_trunctfdd // long double->dec64 | 393 | | ✗ | __bid_truncddsf | dec64 | ∅ | float | .. | |
| 323 | - __bid_trunctfdd // long double->dec64 | 394 | | ✗ | __dpd_trunctdsf | dec128 | ∅ | float | .. | |
| 324 | 395 | | ✗ | __bid_trunctdsf | dec128 | ∅ | float | .. | | |
| 325 | - __dpd_truncddsf // dec64->float | 396 | | ✗ | __dpd_extendsddf | dec32 | ∅ | double| .. | |
| 326 | - __bid_truncddsf // dec64->float | 397 | | ✗ | __bid_extendsddf | dec32 | ∅ | double| .. | |
| 327 | - __dpd_trunctdsf // dec128->float | 398 | | ✗ | __dpd_trunctddf | dec128 | ∅ | double| .. | |
| 328 | - __bid_trunctdsf // dec128->float | 399 | | ✗ | __bid_trunctddf | dec128 | ∅ | double| .. | |
| 329 | - __dpd_extendsddf // dec32->double | 400 | | ✗ | __dpd_extendsdxf | dec32 | ∅ |long double| .. | |
| 330 | - __bid_extendsddf // dec32->double | 401 | | ✗ | __bid_extendsdxf | dec32 | ∅ |long double| .. | |
| 331 | - __dpd_trunctddf // dec128->double | 402 | | ✗ | __dpd_extendddxf | dec64 | ∅ |long double| .. | |
| 332 | - __bid_trunctddf // dec128->double | 403 | | ✗ | __bid_extendddxf | dec64 | ∅ |long double| .. | |
| 333 | - __dpd_extendsdxf // dec32->long double | 404 | | ✗ | __dpd_trunctdxf | dec128 | ∅ |long double| .. | |
| 334 | - __bid_extendsdxf // dec32->long double | 405 | | ✗ | __bid_trunctdxf | dec128 | ∅ |long double| .. | |
| 335 | - __dpd_extendddxf // dec64->long double | 406 | | ✗ | __dpd_extendsdtf | dec32 | ∅ |long double| .. | |
| 336 | - __bid_extendddxf // dec64->long double | 407 | | ✗ | __bid_extendsdtf | dec32 | ∅ |long double| .. | |
| 337 | - __dpd_trunctdxf // dec128->long double | 408 | | ✗ | __dpd_extendddtf | dec64 | ∅ |long double| .. | |
| 338 | - __bid_trunctdxf // dec128->long double | 409 | | ✗ | __bid_extendddtf | dec64 | ∅ |long double| .. | |
| 339 | - __dpd_extendsdtf // dec32->long double | 410 | | ✗ | __dpd_extendsfsd | float | ∅ | dec32 | same size conversions | |
| 340 | - __bid_extendsdtf // dec32->long double | 411 | | ✗ | __bid_extendsfsd | float | ∅ | dec32 | .. | |
| 341 | - __dpd_extendddtf // dec64->long double | 412 | | ✗ | __dpd_extenddfdd | double | ∅ | dec64 | .. | |
| 342 | - __bid_extendddtf // dec64->long double | 413 | | ✗ | __bid_extenddfdd | double | ∅ | dec64 | .. | |
| 343 | 414 | | ✗ | __dpd_extendtftd |long double | ∅ | dec128| .. | | |
| 344 | Same size conversion: | 415 | | ✗ | __bid_extendtftd |long double | ∅ | dec128| .. | |
| 345 | - __dpd_extendsfsd // float->dec32 | 416 | | ✗ | __dpd_truncsdsf | dec32 | ∅ | float | .. | |
| 346 | - __bid_extendsfsd // float->dec32 | 417 | | ✗ | __bid_truncsdsf | dec32 | ∅ | float | .. | |
| 347 | - __dpd_extenddfdd // double->dec64 | 418 | | ✗ | __dpd_truncdddf | dec64 | ∅ | float | conversion | |
| 348 | - __bid_extenddfdd // double->dec64 | 419 | | ✗ | __bid_truncdddf | dec64 | ∅ | float | .. | |
| 349 | - __dpd_extendtftd //long double->dec128 | 420 | | ✗ | __dpd_trunctdtf | dec128 | ∅ |long double| .. | |
| 350 | - __bid_extendtftd //long double->dec128 | 421 | | ✗ | __bid_trunctdtf | dec128 | ∅ |long double| .. | |
| 351 | - __dpd_truncsdsf // dec32->float | 422 | | ✗ | __dpd_fixsdsi | dec32 | ∅ | int | .. | |
| 352 | - __bid_truncsdsf // dec32->float | 423 | | ✗ | __bid_fixsdsi | dec32 | ∅ | int | .. | |
| 353 | - __dpd_truncdddf // dec64->float | 424 | | ✗ | __dpd_fixddsi | dec64 | ∅ | int | .. | |
| 354 | - __bid_truncdddf // dec64->float | 425 | | ✗ | __bid_fixddsi | dec64 | ∅ | int | .. | |
| 355 | - __dpd_trunctdtf // dec128->long double | 426 | | ✗ | __dpd_fixtdsi | dec128 | ∅ | int | .. | |
| 356 | - __bid_trunctdtf // dec128->long double | 427 | | ✗ | __bid_fixtdsi | dec128 | ∅ | int | .. | |
| 357 | 428 | | ✗ | __dpd_fixsddi | dec32 | ∅ | long | .. | | |
| 358 | - __dpd_fixsdsi // dec32->int | 429 | | ✗ | __bid_fixsddi | dec32 | ∅ | long | .. | |
| 359 | - __bid_fixsdsi // dec32->int | 430 | | ✗ | __dpd_fixdddi | dec64 | ∅ | long | .. | |
| 360 | - __dpd_fixddsi // dec64->int | 431 | | ✗ | __bid_fixdddi | dec64 | ∅ | long | .. | |
| 361 | - __bid_fixddsi // dec64->int | 432 | | ✗ | __dpd_fixtddi | dec128 | ∅ | long | .. | |
| 362 | - __dpd_fixtdsi // dec128->int | 433 | | ✗ | __bid_fixtddi | dec128 | ∅ | long | .. | |
| 363 | - __bid_fixtdsi // dec128->int | 434 | | ✗ | __dpd_fixunssdsi | dec32 | ∅ |unsigned int | .. All negative values become zero. | |
| 364 | 435 | | ✗ | __bid_fixunssdsi | dec32 | ∅ |unsigned int | .. | | |
| 365 | - __dpd_fixsddi // dec32->long | 436 | | ✗ | __dpd_fixunsddsi | dec64 | ∅ |unsigned int | .. | |
| 366 | - __bid_fixsddi // dec32->long | 437 | | ✗ | __bid_fixunsddsi | dec64 | ∅ |unsigned int | .. | |
| 367 | - __dpd_fixdddi // dec64->long | 438 | | ✗ | __dpd_fixunstdsi | dec128 | ∅ |unsigned int | .. | |
| 368 | - __bid_fixdddi // dec64->long | 439 | | ✗ | __bid_fixunstdsi | dec128 | ∅ |unsigned int | .. | |
| 369 | - __dpd_fixtddi // dec128->long | 440 | | ✗ | __dpd_fixunssddi | dec32 | ∅ |unsigned long| .. | |
| 370 | - __bid_fixtddi // dec128->long | 441 | | ✗ | __bid_fixunssddi | dec32 | ∅ |unsigned long| .. | |
| 371 | 442 | | ✗ | __dpd_fixunsdddi | dec64 | ∅ |unsigned long| .. | | |
| 372 | - __dpd_fixunssdsi // dec32->unsigned int, All negative values become zero. | 443 | | ✗ | __bid_fixunsdddi | dec64 | ∅ |unsigned long| .. | |
| 373 | - __bid_fixunssdsi // dec32->unsigned int | 444 | | ✗ | __dpd_fixunstddi | dec128 | ∅ |unsigned long| .. | |
| 374 | - __dpd_fixunsddsi // dec64->unsigned int | 445 | | ✗ | __bid_fixunstddi | dec128 | ∅ |unsigned long| .. | |
| 375 | - __bid_fixunsddsi // dec64->unsigned int | 446 | | ✗ | __dpd_floatsisd | int | ∅ | dec32 | .. | |
| 376 | - __dpd_fixunstdsi // dec128->unsigned int | 447 | | ✗ | __bid_floatsisd | int | ∅ | dec32 | .. | |
| 377 | - __bid_fixunstdsi // dec128->unsigned int | 448 | | ✗ | __dpd_floatsidd | int | ∅ | dec64 | .. | |
| 378 | 449 | | ✗ | __bid_floatsidd | int | ∅ | dec64 | .. | | |
| 379 | - __dpd_fixunssddi // dec32->unsigned long, All negative values become zero. | 450 | | ✗ | __dpd_floatsitd | int | ∅ | dec128 | .. | |
| 380 | - __bid_fixunssddi // dec32->unsigned long | 451 | | ✗ | __bid_floatsitd | int | ∅ | dec128 | .. | |
| 381 | - __dpd_fixunsdddi // dec64->unsigned long | 452 | | ✗ | __dpd_floatdisd | long | ∅ | dec32 | .. | |
| 382 | - __bid_fixunsdddi // dec64->unsigned long | 453 | | ✗ | __bid_floatdisd | long | ∅ | dec32 | .. | |
| 383 | - __dpd_fixunstddi // dec128->unsigned long | 454 | | ✗ | __dpd_floatdidd | long | ∅ | dec64 | .. | |
| 384 | - __bid_fixunstddi // dec128->unsigned long | 455 | | ✗ | __bid_floatdidd | long | ∅ | dec64 | .. | |
| 385 | 456 | | ✗ | __dpd_floatditd | long | ∅ | dec128 | .. | | |
| 386 | - __dpd_floatsisd // int->dec32 | 457 | | ✗ | __bid_floatditd | long | ∅ | dec128 | .. | |
| 387 | - __bid_floatsisd // int->dec32 | 458 | | ✗ | __dpd_floatunssisd | unsigned int| ∅ | dec32 | .. | |
| 388 | - __dpd_floatsidd // int->dec64 | 459 | | ✗ | __bid_floatunssisd | unsigned int| ∅ | dec32 | .. | |
| 389 | - __bid_floatsidd // int->dec64 | 460 | | ✗ | __dpd_floatunssidd | unsigned int| ∅ | dec64 | .. | |
| 390 | - __dpd_floatsitd // int->dec128 | 461 | | ✗ | __bid_floatunssidd | unsigned int| ∅ | dec64 | .. | |
| 391 | - __bid_floatsitd // int->dec128 | 462 | | ✗ | __dpd_floatunssitd | unsigned int| ∅ | dec128 | .. | |
| 392 | 463 | | ✗ | __bid_floatunssitd | unsigned int| ∅ | dec128 | .. | | |
| 393 | - __dpd_floatdisd // long->dec32 | 464 | | ✗ | __dpd_floatunsdisd |unsigned long| ∅ | dec32 | .. | |
| 394 | - __bid_floatdisd // long->dec32 | 465 | | ✗ | __bid_floatunsdisd |unsigned long| ∅ | dec32 | .. | |
| 395 | - __dpd_floatdidd // long->dec64 | 466 | | ✗ | __dpd_floatunsdidd |unsigned long| ∅ | dec64 | .. | |
| 396 | - __bid_floatdidd // long->dec64 | 467 | | ✗ | __bid_floatunsdidd |unsigned long| ∅ | dec64 | .. | |
| 397 | - __dpd_floatditd // long->dec128 | 468 | | ✗ | __dpd_floatunsditd |unsigned long| ∅ | dec128 | .. | |
| 398 | - __bid_floatditd // long->dec128 | 469 | | ✗ | __bid_floatunsditd |unsigned long| ∅ | dec128 | .. | |
| 399 | 470 | | | | | | | **Decimal Float Comparison** | | |
| 400 | - __dpd_floatunssisd // unsigned int->dec32 | 471 | | ✗ | __dpd_unordsd2 | dec32 | dec32 | c_int | `a +-NaN or a +-NaN -> 1(nonzero), else -> 0` | |
| 401 | - __bid_floatunssisd // unsigned int->dec32 | 472 | | ✗ | __bid_unordsd2 | dec32 | dec32 | c_int | .. | |
| 402 | - __dpd_floatunssidd // unsigned int->dec64 | 473 | | ✗ | __dpd_unorddd2 | dec64 | dec64 | c_int | .. | |
| 403 | - __bid_floatunssidd // unsigned int->dec64 | 474 | | ✗ | __bid_unorddd2 | dec64 | dec64 | c_int | .. | |
| 404 | - __dpd_floatunssitd // unsigned int->dec128 | 475 | | ✗ | __dpd_unordtd2 | dec128 | dec128 | c_int | .. | |
| 405 | - __bid_floatunssitd // unsigned int->dec128 | 476 | | ✗ | __bid_unordtd2 | dec128 | dec128 | c_int | .. | |
| 406 | 477 | | ✗ | __dpd_eqsd2 | dec32 | dec32 | c_int |`a!=+-NaN and b!=+-Nan and a==b -> 0, else -> 1(nonzero)`| | |
| 407 | - __dpd_floatunsdisd // unsigned long->dec32 | 478 | | ✗ | __bid_eqsd2 | dec32 | dec32 | c_int | .. | |
| 408 | - __bid_floatunsdisd // unsigned long->dec32 | 479 | | ✗ | __dpd_eqdd2 | dec64 | dec64 | c_int | .. | |
| 409 | - __dpd_floatunsdidd // unsigned long->dec64 | 480 | | ✗ | __bid_eqdd2 | dec64 | dec64 | c_int | .. | |
| 410 | - __bid_floatunsdidd // unsigned long->dec64 | 481 | | ✗ | __dpd_eqtd2 | dec128 | dec128 | c_int | .. | |
| 411 | - __dpd_floatunsditd // unsigned long->dec128 | 482 | | ✗ | __bid_eqtd2 | dec128 | dec128 | c_int | .. | |
| 412 | - __bid_floatunsditd // unsigned long->dec128 | 483 | | ✗ | __dpd_nesd2 | dec32 | dec32 | c_int | `a==+-NaN or b==+-NaN or a!=b -> 1(nonzero), else -> 0` | |
| 413 | 484 | | ✗ | __bid_nesd2 | dec32 | dec32 | c_int | .. | | |
| 414 | #### Decimal float Comparison | 485 | | ✗ | __dpd_nedd2 | dec64 | dec64 | c_int | .. | |
| 415 | 486 | | ✗ | __bid_nedd2 | dec64 | dec64 | c_int | .. | | |
| 416 | All decimal float comparison routines return c_int. | 487 | | ✗ | __dpd_netd2 | dec128 | dec128 | c_int | .. | |
| 417 | 488 | | ✗ | __bid_netd2 | dec128 | dec128 | c_int | .. | | |
| 418 | - __dpd_unordsd2 // a,b: dec32, a +-NaN or a +-NaN -> 1(nonzero), else -> 0 | 489 | | ✗ | __dpd_gesd2 | dec32 | dec32 | c_int | `a!=+-NaN and b!=+-NaN and a>=b -> >=0, else -> <0` | |
| 419 | - __bid_unordsd2 // a,b: dec32 | 490 | | ✗ | __bid_gesd2 | dec32 | dec32 | c_int | .. | |
| 420 | - __dpd_unorddd2 // a,b: dec64 | 491 | | ✗ | __dpd_gedd2 | dec64 | dec64 | c_int | .. | |
| 421 | - __bid_unorddd2 // a,b: dec64 | 492 | | ✗ | __bid_gedd2 | dec64 | dec64 | c_int | .. | |
| 422 | - __dpd_unordtd2 // a,b: dec128 | 493 | | ✗ | __dpd_getd2 | dec128 | dec128 | c_int | .. | |
| 423 | - __bid_unordtd2 // a,b: dec128 | 494 | | ✗ | __bid_getd2 | dec128 | dec128 | c_int | .. | |
| 424 | 495 | | ✗ | __dpd_ltsd2 | dec32 | dec32 | c_int | `a!=+-NaN and b!=+-NaN and a<b -> <0, else -> >=0` | | |
| 425 | - __dpd_eqsd2 // a,b: dec32, a!=+-NaN and b!=+-Nan and a==b -> 0, else -> 1(nonzero) | 496 | | ✗ | __bid_ltsd2 | dec32 | dec32 | c_int | .. | |
| 426 | - __bid_eqsd2 // a,b: dec32 | 497 | | ✗ | __dpd_ltdd2 | dec64 | dec64 | c_int | .. | |
| 427 | - __dpd_eqdd2 // a,b: dec64 | 498 | | ✗ | __bid_ltdd2 | dec64 | dec64 | c_int | .. | |
| 428 | - __bid_eqdd2 // a,b: dec64 | 499 | | ✗ | __dpd_lttd2 | dec128 | dec128 | c_int | .. | |
| 429 | - __dpd_eqtd2 // a,b: dec128 | 500 | | ✗ | __bid_lttd2 | dec128 | dec128 | c_int | .. | |
| 430 | - __bid_eqtd2 // a,b: dec128 | 501 | | ✗ | __dpd_lesd2 | dec32 | dec32 | c_int | `a!=+-NaN and b!=+-NaN and a<=b -> <=0, else -> >=0` | |
| 431 | 502 | | ✗ | __bid_lesd2 | dec32 | dec32 | c_int | .. | | |
| 432 | - __dpd_nesd2 // a,b: dec32, a==+-NaN or b==+-NaN or a!=b -> 1(nonzero), else -> 0 | 503 | | ✗ | __dpd_ledd2 | dec64 | dec64 | c_int | .. | |
| 433 | - __bid_nesd2 // a,b: dec32 | 504 | | ✗ | __bid_ledd2 | dec64 | dec64 | c_int | .. | |
| 434 | - __dpd_nedd2 // a,b: dec64 | 505 | | ✗ | __dpd_letd2 | dec128 | dec128 | c_int | .. | |
| 435 | - __bid_nedd2 // a,b: dec64 | 506 | | ✗ | __bid_letd2 | dec128 | dec128 | c_int | .. | |
| 436 | - __dpd_netd2 // a,b: dec128 | 507 | | ✗ | __dpd_gtsd2 | dec32 | dec32 | c_int | `a!=+-NaN and b!=+-NaN and a>b -> >0, else -> <=0` | |
| 437 | - __bid_netd2 // a,b: dec128 | 508 | | ✗ | __bid_gtsd2 | dec32 | dec32 | c_int | .. | |
| 438 | 509 | | ✗ | __dpd_gtdd2 | dec64 | dec64 | c_int | .. | | |
| 439 | - __dpd_gesd2 // a,b: dec32, a!=+-NaN and b!=+-NaN and a>=b -> >=0, else -> <0 | 510 | | ✗ | __bid_gtdd2 | dec64 | dec64 | c_int | .. | |
| 440 | - __bid_gesd2 // a,b: dec32 | 511 | | ✗ | __dpd_gttd2 | dec128 | dec128 | c_int | .. | |
| 441 | - __dpd_gedd2 // a,b: dec64 | 512 | | ✗ | __bid_gttd2 | dec128 | dec128 | c_int | .. | |
| 442 | - __bid_gedd2 // a,b: dec64 | 513 | | | | | | | **Decimal Float Arithmetic**[^options] | |
| 443 | - __dpd_getd2 // a,b: dec128 | 514 | | ✗ | __dpd_addsd3 | dec32 | dec32 | dec32 |`a + b`| |
| 444 | - __bid_getd2 // a,b: dec128 | 515 | | ✗ | __bid_addsd3 | dec32 | dec32 | dec32 | .. | |
| 445 | 516 | | ✗ | __dpd_adddd3 | dec64 | dec64 | dec64 | .. | | |
| 446 | - __dpd_ltsd2 // a,b: dec32, a!=+-NaN and b!=+-NaN and a<b -> <0, else -> >=0 | 517 | | ✗ | __bid_adddd3 | dec64 | dec64 | dec64 | .. | |
| 447 | - __bid_ltsd2 // a,b: dec32 | 518 | | ✗ | __dpd_addtd3 | dec128 | dec128 | dec128 | .. | |
| 448 | - __dpd_ltdd2 // a,b: dec64 | 519 | | ✗ | __bid_addtd3 | dec128 | dec128 | dec128 | .. | |
| 449 | - __bid_ltdd2 // a,b: dec64 | 520 | | ✗ | __dpd_subsd3 | dec32 | dec32 | dec32 |`a - b`| |
| 450 | - __dpd_lttd2 // a,b: dec128 | 521 | | ✗ | __bid_subsd3 | dec32 | dec32 | dec32 | .. | |
| 451 | - __bid_lttd2 // a,b: dec128 | 522 | | ✗ | __dpd_subdd3 | dec64 | dec64 | dec64 | .. | |
| 452 | 523 | | ✗ | __bid_subdd3 | dec64 | dec64 | dec64 | .. | | |
| 453 | - __dpd_lesd2 // a,b: dec32, a!=+-NaN and b!=+-NaN and a<=b -> <=0, else -> >=0 | 524 | | ✗ | __dpd_subtd3 | dec128 | dec128 | dec128 | .. | |
| 454 | - __bid_lesd2 // a,b: dec32 | 525 | | ✗ | __bid_subtd3 | dec128 | dec128 | dec128 | .. | |
| 455 | - __dpd_ledd2 // a,b: dec64 | 526 | | ✗ | __dpd_mulsd3 | dec32 | dec32 | dec32 |`a * b`| |
| 456 | - __bid_ledd2 // a,b: dec64 | 527 | | ✗ | __bid_mulsd3 | dec32 | dec32 | dec32 | .. | |
| 457 | - __dpd_letd2 // a,b: dec128 | 528 | | ✗ | __dpd_muldd3 | dec64 | dec64 | dec64 | .. | |
| 458 | - __bid_letd2 // a,b: dec128 | 529 | | ✗ | __bid_muldd3 | dec64 | dec64 | dec64 | .. | |
| 459 | 530 | | ✗ | __dpd_multd3 | dec128 | dec128 | dec128 | .. | | |
| 460 | - __dpd_gtsd2 // a,b: dec32, a!=+-NaN and b!=+-NaN and a>b -> >0, else -> <=0 | 531 | | ✗ | __bid_multd3 | dec128 | dec128 | dec128 | .. | |
| 461 | - __bid_gtsd2 // a,b: dec32 | 532 | | ✗ | __dpd_divsd3 | dec32 | dec32 | dec32 |`a / b`| |
| 462 | - __dpd_gtdd2 // a,b: dec64 | 533 | | ✗ | __bid_divsd3 | dec32 | dec32 | dec32 | .. | |
| 463 | - __bid_gtdd2 // a,b: dec64 | 534 | | ✗ | __dpd_divdd3 | dec64 | dec64 | dec64 | .. | |
| 464 | - __dpd_gttd2 // a,b: dec128 | 535 | | ✗ | __bid_divdd3 | dec64 | dec64 | dec64 | .. | |
| 465 | - __bid_gttd2 // a,b: dec128 | 536 | | ✗ | __dpd_divtd3 | dec128 | dec128 | dec128 | .. | |
| 466 | 537 | | ✗ | __bid_divtd3 | dec128 | dec128 | dec128 | .. | | |
| 467 | #### Decimal float Arithmetic | 538 | | ✗ | __dpd_negsd2 | dec32 | dec32 | dec32 | `-a` | |
| 468 | 539 | | ✗ | __bid_negsd2 | dec32 | dec32 | dec32 | .. | | |
| 469 | These numbers include options with routines for +-0 and +-Nan. | 540 | | ✗ | __dpd_negdd2 | dec64 | dec64 | dec64 | .. | |
| 470 | 541 | | ✗ | __bid_negdd2 | dec64 | dec64 | dec64 | .. | | |
| 471 | - __dpd_addsd3 // a,b: dec32 -> dec32, a + b | 542 | | ✗ | __dpd_negtd2 | dec128 | dec128 | dec128 | .. | |
| 472 | - __bid_addsd3 // a,b: dec32 -> dec32 | 543 | | ✗ | __bid_negtd2 | dec128 | dec128 | dec128 | .. | |
| 473 | - __dpd_adddd3 // a,b: dec64 -> dec64 | 544 | |
| 474 | - __bid_adddd3 // a,b: dec64 -> dec64 | 545 | [^options]: These numbers include options with routines for +-0 and +-Nan. |
| 475 | - __dpd_addtd3 // a,b: dec128-> dec128 | 546 | |
| 476 | - __bid_addtd3 // a,b: dec128-> dec128 | 547 | Fixed-point fractional library routines |
| 477 | - __dpd_subsd3 // a,b: dec32, a - b | 548 | |
| 478 | - __bid_subsd3 // a,b: dec32 -> dec32 | 549 | TODO brief explanation + implementation |
| 479 | - __dpd_subdd3 // a,b: dec64 .. | 550 | |
| 480 | - __bid_subdd3 // a,b: dec64 | 551 | | Done | Name | a | b | Out | Comment | |
| 481 | - __dpd_subtd3 // a,b: dec128 | 552 | | ------ | ------------- | --------- | --------- | --------- | -------------------------- | |
| 482 | - __bid_subtd3 // a,b: dec128 | 553 | | | | | | | **Fixed-Point Fractional** | |
| 483 | - __dpd_mulsd3 // a,b: dec32, a * b | 554 | |
| 484 | - __bid_mulsd3 // a,b: dec32 -> dec32 | 555 | Further content: |
| 485 | - __dpd_muldd3 // a,b: dec64 .. | 556 | - aarch64 outline atomics |
| 486 | - __bid_muldd3 // a,b: dec64 | 557 | - atomics |
| 487 | - __dpd_multd3 // a,b: dec128 | 558 | - msvc things like _alldiv, _aulldiv, _allrem |
| 488 | - __bid_multd3 // a,b: dec128 | 559 | - clear cache |
| 489 | - __dpd_divsd3 // a,b: dec32, a / b | 560 | - tls emulation |
| 490 | - __bid_divsd3 // a,b: dec32 -> dec32 | 561 | - math routines (cos, sin, tan, ceil, floor, exp, exp2, fabs, log, log10, log2, sincos, sqrt) |
| 491 | - __dpd_divdd3 // a,b: dec64 .. | 562 | - bcmp |
| 492 | - __bid_divdd3 // a,b: dec64 | 563 | - ieee float routines (fma, fmax, fmin, fmod, fabs, float rounding, ) |
| 493 | - __dpd_divtd3 // a,b: dec128 | 564 | - arm routines (memory routines + memclr [setting to 0], divmod routines and stubs for unwind_cpp) |
| 494 | - __bid_divtd3 // a,b: dec128 | 565 | - memory routines (memcmp, memcpy, memset, memmove) |
| 495 | - __dpd_negsd2 // a,b: dec32, -a | 566 | - objective-c __isPlatformVersionAtLeast check |
| 496 | - __bid_negsd2 // a,b: dec32 -> dec32 | 567 | - stack probe routines |
| 497 | - __dpd_negdd2 // a,b: dec64 .. | 568 | |
| 498 | - __bid_negdd2 // a,b: dec64 | 569 | Future work |
| 499 | - __dpd_negtd2 // a,b: dec128 | 570 | |
| 500 | - __bid_negtd2 // a,b: dec128 | 571 | Arbitrary length integer library routines |
| 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" |
lib/compiler_rt/atomics.zig+104| ... | @@ -192,6 +192,10 @@ fn __atomic_load_8(src: *u64, model: i32) callconv(.C) u64 { | ... | @@ -192,6 +192,10 @@ fn __atomic_load_8(src: *u64, model: i32) callconv(.C) u64 { |
| 192 | return atomic_load_N(u64, src, model); | 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 | inline fn atomic_store_N(comptime T: type, dst: *T, value: T, model: i32) void { | 199 | inline fn atomic_store_N(comptime T: type, dst: *T, value: T, model: i32) void { |
| 196 | _ = model; | 200 | _ = model; |
| 197 | if (@sizeOf(T) > largest_atomic_size) { | 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,6 +223,10 @@ fn __atomic_store_8(dst: *u64, value: u64, model: i32) callconv(.C) void { |
| 219 | return atomic_store_N(u64, dst, value, model); | 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 | fn wideUpdate(comptime T: type, ptr: *T, val: T, update: anytype) T { | 230 | fn wideUpdate(comptime T: type, ptr: *T, val: T, update: anytype) T { |
| 223 | const WideAtomic = std.meta.Int(.unsigned, smallest_atomic_fetch_exch_size * 8); | 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,6 +290,10 @@ fn __atomic_exchange_8(ptr: *u64, val: u64, model: i32) callconv(.C) u64 { |
| 282 | return atomic_exchange_N(u64, ptr, val, model); | 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 | inline fn atomic_compare_exchange_N( | 297 | inline fn atomic_compare_exchange_N( |
| 286 | comptime T: type, | 298 | comptime T: type, |
| 287 | ptr: *T, | 299 | ptr: *T, |
| ... | @@ -327,6 +339,10 @@ fn __atomic_compare_exchange_8(ptr: *u64, expected: *u64, desired: u64, success: | ... | @@ -327,6 +339,10 @@ fn __atomic_compare_exchange_8(ptr: *u64, expected: *u64, desired: u64, success: |
| 327 | return atomic_compare_exchange_N(u64, ptr, expected, desired, success, failure); | 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 | inline fn fetch_op_N(comptime T: type, comptime op: std.builtin.AtomicRmwOp, ptr: *T, val: T, model: i32) T { | 346 | inline fn fetch_op_N(comptime T: type, comptime op: std.builtin.AtomicRmwOp, ptr: *T, val: T, model: i32) T { |
| 331 | _ = model; | 347 | _ = model; |
| 332 | const Updater = struct { | 348 | const Updater = struct { |
| ... | @@ -338,6 +354,8 @@ inline fn fetch_op_N(comptime T: type, comptime op: std.builtin.AtomicRmwOp, ptr | ... | @@ -338,6 +354,8 @@ inline fn fetch_op_N(comptime T: type, comptime op: std.builtin.AtomicRmwOp, ptr |
| 338 | .Nand => ~(old & new), | 354 | .Nand => ~(old & new), |
| 339 | .Or => old | new, | 355 | .Or => old | new, |
| 340 | .Xor => old ^ new, | 356 | .Xor => old ^ new, |
| 357 | .Max => @max(old, new), | ||
| 358 | .Min => @min(old, new), | ||
| 341 | else => @compileError("unsupported atomic op"), | 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,6 +392,10 @@ fn __atomic_fetch_add_8(ptr: *u64, val: u64, model: i32) callconv(.C) u64 { |
| 374 | return fetch_op_N(u64, .Add, ptr, val, model); | 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 | fn __atomic_fetch_sub_1(ptr: *u8, val: u8, model: i32) callconv(.C) u8 { | 399 | fn __atomic_fetch_sub_1(ptr: *u8, val: u8, model: i32) callconv(.C) u8 { |
| 378 | return fetch_op_N(u8, .Sub, ptr, val, model); | 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,6 +412,10 @@ fn __atomic_fetch_sub_8(ptr: *u64, val: u64, model: i32) callconv(.C) u64 { |
| 390 | return fetch_op_N(u64, .Sub, ptr, val, model); | 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 | fn __atomic_fetch_and_1(ptr: *u8, val: u8, model: i32) callconv(.C) u8 { | 419 | fn __atomic_fetch_and_1(ptr: *u8, val: u8, model: i32) callconv(.C) u8 { |
| 394 | return fetch_op_N(u8, .And, ptr, val, model); | 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,6 +432,10 @@ fn __atomic_fetch_and_8(ptr: *u64, val: u64, model: i32) callconv(.C) u64 { |
| 406 | return fetch_op_N(u64, .And, ptr, val, model); | 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 | fn __atomic_fetch_or_1(ptr: *u8, val: u8, model: i32) callconv(.C) u8 { | 439 | fn __atomic_fetch_or_1(ptr: *u8, val: u8, model: i32) callconv(.C) u8 { |
| 410 | return fetch_op_N(u8, .Or, ptr, val, model); | 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,6 +452,10 @@ fn __atomic_fetch_or_8(ptr: *u64, val: u64, model: i32) callconv(.C) u64 { |
| 422 | return fetch_op_N(u64, .Or, ptr, val, model); | 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 | fn __atomic_fetch_xor_1(ptr: *u8, val: u8, model: i32) callconv(.C) u8 { | 459 | fn __atomic_fetch_xor_1(ptr: *u8, val: u8, model: i32) callconv(.C) u8 { |
| 426 | return fetch_op_N(u8, .Xor, ptr, val, model); | 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,6 +472,10 @@ fn __atomic_fetch_xor_8(ptr: *u64, val: u64, model: i32) callconv(.C) u64 { |
| 438 | return fetch_op_N(u64, .Xor, ptr, val, model); | 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 | fn __atomic_fetch_nand_1(ptr: *u8, val: u8, model: i32) callconv(.C) u8 { | 479 | fn __atomic_fetch_nand_1(ptr: *u8, val: u8, model: i32) callconv(.C) u8 { |
| 442 | return fetch_op_N(u8, .Nand, ptr, val, model); | 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,6 +492,50 @@ fn __atomic_fetch_nand_8(ptr: *u64, val: u64, model: i32) callconv(.C) u64 { |
| 454 | return fetch_op_N(u64, .Nand, ptr, val, model); | 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 | comptime { | 539 | comptime { |
| 458 | if (supports_atomic_ops and builtin.object_format != .c) { | 540 | if (supports_atomic_ops and builtin.object_format != .c) { |
| 459 | @export(__atomic_load, .{ .name = "__atomic_load", .linkage = linkage, .visibility = visibility }); | 541 | @export(__atomic_load, .{ .name = "__atomic_load", .linkage = linkage, .visibility = visibility }); |
| ... | @@ -465,50 +547,72 @@ comptime { | ... | @@ -465,50 +547,72 @@ comptime { |
| 465 | @export(__atomic_fetch_add_2, .{ .name = "__atomic_fetch_add_2", .linkage = linkage, .visibility = visibility }); | 547 | @export(__atomic_fetch_add_2, .{ .name = "__atomic_fetch_add_2", .linkage = linkage, .visibility = visibility }); |
| 466 | @export(__atomic_fetch_add_4, .{ .name = "__atomic_fetch_add_4", .linkage = linkage, .visibility = visibility }); | 548 | @export(__atomic_fetch_add_4, .{ .name = "__atomic_fetch_add_4", .linkage = linkage, .visibility = visibility }); |
| 467 | @export(__atomic_fetch_add_8, .{ .name = "__atomic_fetch_add_8", .linkage = linkage, .visibility = visibility }); | 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 | @export(__atomic_fetch_sub_1, .{ .name = "__atomic_fetch_sub_1", .linkage = linkage, .visibility = visibility }); | 552 | @export(__atomic_fetch_sub_1, .{ .name = "__atomic_fetch_sub_1", .linkage = linkage, .visibility = visibility }); |
| 470 | @export(__atomic_fetch_sub_2, .{ .name = "__atomic_fetch_sub_2", .linkage = linkage, .visibility = visibility }); | 553 | @export(__atomic_fetch_sub_2, .{ .name = "__atomic_fetch_sub_2", .linkage = linkage, .visibility = visibility }); |
| 471 | @export(__atomic_fetch_sub_4, .{ .name = "__atomic_fetch_sub_4", .linkage = linkage, .visibility = visibility }); | 554 | @export(__atomic_fetch_sub_4, .{ .name = "__atomic_fetch_sub_4", .linkage = linkage, .visibility = visibility }); |
| 472 | @export(__atomic_fetch_sub_8, .{ .name = "__atomic_fetch_sub_8", .linkage = linkage, .visibility = visibility }); | 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 | @export(__atomic_fetch_and_1, .{ .name = "__atomic_fetch_and_1", .linkage = linkage, .visibility = visibility }); | 558 | @export(__atomic_fetch_and_1, .{ .name = "__atomic_fetch_and_1", .linkage = linkage, .visibility = visibility }); |
| 475 | @export(__atomic_fetch_and_2, .{ .name = "__atomic_fetch_and_2", .linkage = linkage, .visibility = visibility }); | 559 | @export(__atomic_fetch_and_2, .{ .name = "__atomic_fetch_and_2", .linkage = linkage, .visibility = visibility }); |
| 476 | @export(__atomic_fetch_and_4, .{ .name = "__atomic_fetch_and_4", .linkage = linkage, .visibility = visibility }); | 560 | @export(__atomic_fetch_and_4, .{ .name = "__atomic_fetch_and_4", .linkage = linkage, .visibility = visibility }); |
| 477 | @export(__atomic_fetch_and_8, .{ .name = "__atomic_fetch_and_8", .linkage = linkage, .visibility = visibility }); | 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 | @export(__atomic_fetch_or_1, .{ .name = "__atomic_fetch_or_1", .linkage = linkage, .visibility = visibility }); | 564 | @export(__atomic_fetch_or_1, .{ .name = "__atomic_fetch_or_1", .linkage = linkage, .visibility = visibility }); |
| 480 | @export(__atomic_fetch_or_2, .{ .name = "__atomic_fetch_or_2", .linkage = linkage, .visibility = visibility }); | 565 | @export(__atomic_fetch_or_2, .{ .name = "__atomic_fetch_or_2", .linkage = linkage, .visibility = visibility }); |
| 481 | @export(__atomic_fetch_or_4, .{ .name = "__atomic_fetch_or_4", .linkage = linkage, .visibility = visibility }); | 566 | @export(__atomic_fetch_or_4, .{ .name = "__atomic_fetch_or_4", .linkage = linkage, .visibility = visibility }); |
| 482 | @export(__atomic_fetch_or_8, .{ .name = "__atomic_fetch_or_8", .linkage = linkage, .visibility = visibility }); | 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 | @export(__atomic_fetch_xor_1, .{ .name = "__atomic_fetch_xor_1", .linkage = linkage, .visibility = visibility }); | 570 | @export(__atomic_fetch_xor_1, .{ .name = "__atomic_fetch_xor_1", .linkage = linkage, .visibility = visibility }); |
| 485 | @export(__atomic_fetch_xor_2, .{ .name = "__atomic_fetch_xor_2", .linkage = linkage, .visibility = visibility }); | 571 | @export(__atomic_fetch_xor_2, .{ .name = "__atomic_fetch_xor_2", .linkage = linkage, .visibility = visibility }); |
| 486 | @export(__atomic_fetch_xor_4, .{ .name = "__atomic_fetch_xor_4", .linkage = linkage, .visibility = visibility }); | 572 | @export(__atomic_fetch_xor_4, .{ .name = "__atomic_fetch_xor_4", .linkage = linkage, .visibility = visibility }); |
| 487 | @export(__atomic_fetch_xor_8, .{ .name = "__atomic_fetch_xor_8", .linkage = linkage, .visibility = visibility }); | 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 | @export(__atomic_fetch_nand_1, .{ .name = "__atomic_fetch_nand_1", .linkage = linkage, .visibility = visibility }); | 576 | @export(__atomic_fetch_nand_1, .{ .name = "__atomic_fetch_nand_1", .linkage = linkage, .visibility = visibility }); |
| 490 | @export(__atomic_fetch_nand_2, .{ .name = "__atomic_fetch_nand_2", .linkage = linkage, .visibility = visibility }); | 577 | @export(__atomic_fetch_nand_2, .{ .name = "__atomic_fetch_nand_2", .linkage = linkage, .visibility = visibility }); |
| 491 | @export(__atomic_fetch_nand_4, .{ .name = "__atomic_fetch_nand_4", .linkage = linkage, .visibility = visibility }); | 578 | @export(__atomic_fetch_nand_4, .{ .name = "__atomic_fetch_nand_4", .linkage = linkage, .visibility = visibility }); |
| 492 | @export(__atomic_fetch_nand_8, .{ .name = "__atomic_fetch_nand_8", .linkage = linkage, .visibility = visibility }); | 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 | @export(__atomic_load_1, .{ .name = "__atomic_load_1", .linkage = linkage, .visibility = visibility }); | 594 | @export(__atomic_load_1, .{ .name = "__atomic_load_1", .linkage = linkage, .visibility = visibility }); |
| 495 | @export(__atomic_load_2, .{ .name = "__atomic_load_2", .linkage = linkage, .visibility = visibility }); | 595 | @export(__atomic_load_2, .{ .name = "__atomic_load_2", .linkage = linkage, .visibility = visibility }); |
| 496 | @export(__atomic_load_4, .{ .name = "__atomic_load_4", .linkage = linkage, .visibility = visibility }); | 596 | @export(__atomic_load_4, .{ .name = "__atomic_load_4", .linkage = linkage, .visibility = visibility }); |
| 497 | @export(__atomic_load_8, .{ .name = "__atomic_load_8", .linkage = linkage, .visibility = visibility }); | 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 | @export(__atomic_store_1, .{ .name = "__atomic_store_1", .linkage = linkage, .visibility = visibility }); | 600 | @export(__atomic_store_1, .{ .name = "__atomic_store_1", .linkage = linkage, .visibility = visibility }); |
| 500 | @export(__atomic_store_2, .{ .name = "__atomic_store_2", .linkage = linkage, .visibility = visibility }); | 601 | @export(__atomic_store_2, .{ .name = "__atomic_store_2", .linkage = linkage, .visibility = visibility }); |
| 501 | @export(__atomic_store_4, .{ .name = "__atomic_store_4", .linkage = linkage, .visibility = visibility }); | 602 | @export(__atomic_store_4, .{ .name = "__atomic_store_4", .linkage = linkage, .visibility = visibility }); |
| 502 | @export(__atomic_store_8, .{ .name = "__atomic_store_8", .linkage = linkage, .visibility = visibility }); | 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 | @export(__atomic_exchange_1, .{ .name = "__atomic_exchange_1", .linkage = linkage, .visibility = visibility }); | 606 | @export(__atomic_exchange_1, .{ .name = "__atomic_exchange_1", .linkage = linkage, .visibility = visibility }); |
| 505 | @export(__atomic_exchange_2, .{ .name = "__atomic_exchange_2", .linkage = linkage, .visibility = visibility }); | 607 | @export(__atomic_exchange_2, .{ .name = "__atomic_exchange_2", .linkage = linkage, .visibility = visibility }); |
| 506 | @export(__atomic_exchange_4, .{ .name = "__atomic_exchange_4", .linkage = linkage, .visibility = visibility }); | 608 | @export(__atomic_exchange_4, .{ .name = "__atomic_exchange_4", .linkage = linkage, .visibility = visibility }); |
| 507 | @export(__atomic_exchange_8, .{ .name = "__atomic_exchange_8", .linkage = linkage, .visibility = visibility }); | 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 | @export(__atomic_compare_exchange_1, .{ .name = "__atomic_compare_exchange_1", .linkage = linkage, .visibility = visibility }); | 612 | @export(__atomic_compare_exchange_1, .{ .name = "__atomic_compare_exchange_1", .linkage = linkage, .visibility = visibility }); |
| 510 | @export(__atomic_compare_exchange_2, .{ .name = "__atomic_compare_exchange_2", .linkage = linkage, .visibility = visibility }); | 613 | @export(__atomic_compare_exchange_2, .{ .name = "__atomic_compare_exchange_2", .linkage = linkage, .visibility = visibility }); |
| 511 | @export(__atomic_compare_exchange_4, .{ .name = "__atomic_compare_exchange_4", .linkage = linkage, .visibility = visibility }); | 614 | @export(__atomic_compare_exchange_4, .{ .name = "__atomic_compare_exchange_4", .linkage = linkage, .visibility = visibility }); |
| 512 | @export(__atomic_compare_exchange_8, .{ .name = "__atomic_compare_exchange_8", .linkage = linkage, .visibility = visibility }); | 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,6 +1354,10 @@ const NAV_MODES = { |
| 1354 | payloadHtml += "ptrCast"; | 1354 | payloadHtml += "ptrCast"; |
| 1355 | break; | 1355 | break; |
| 1356 | } | 1356 | } |
| 1357 | case "qual_cast": { | ||
| 1358 | payloadHtml += "qualCast"; | ||
| 1359 | break; | ||
| 1360 | } | ||
| 1357 | case "truncate": { | 1361 | case "truncate": { |
| 1358 | payloadHtml += "truncate"; | 1362 | payloadHtml += "truncate"; |
| 1359 | break; | 1363 | break; |
| ... | @@ -3158,7 +3162,6 @@ const NAV_MODES = { | ... | @@ -3158,7 +3162,6 @@ const NAV_MODES = { |
| 3158 | canonTypeDecls = new Array(zigAnalysis.types.length); | 3162 | canonTypeDecls = new Array(zigAnalysis.types.length); |
| 3159 | 3163 | ||
| 3160 | for (let pkgI = 0; pkgI < zigAnalysis.packages.length; pkgI += 1) { | 3164 | for (let pkgI = 0; pkgI < zigAnalysis.packages.length; pkgI += 1) { |
| 3161 | if (pkgI === zigAnalysis.rootPkg && rootIsStd) continue; | ||
| 3162 | let pkg = zigAnalysis.packages[pkgI]; | 3165 | let pkg = zigAnalysis.packages[pkgI]; |
| 3163 | let pkgNames = canonPkgPaths[pkgI]; | 3166 | let pkgNames = canonPkgPaths[pkgI]; |
| 3164 | if (pkgNames === undefined) continue; | 3167 | if (pkgNames === undefined) continue; |
lib/init-exe/build.zig+43-10| ... | @@ -1,34 +1,67 @@ | ... | @@ -1,34 +1,67 @@ |
| 1 | const std = @import("std"); | 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 | // Standard target options allows the person running `zig build` to choose | 7 | // Standard target options allows the person running `zig build` to choose |
| 5 | // what target to build for. Here we do not override the defaults, which | 8 | // what target to build for. Here we do not override the defaults, which |
| 6 | // means any target is allowed, and the default is native. Other options | 9 | // means any target is allowed, and the default is native. Other options |
| 7 | // for restricting supported target set are available. | 10 | // for restricting supported target set are available. |
| 8 | const target = b.standardTargetOptions(.{}); | 11 | const target = b.standardTargetOptions(.{}); |
| 9 | 12 | ||
| 10 | // Standard release options allow the person running `zig build` to select | 13 | // Standard optimization options allow the person running `zig build` to select |
| 11 | // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. | 14 | // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not |
| 12 | const mode = b.standardReleaseOptions(); | 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"); | 18 | const exe = b.addExecutable(.{ |
| 15 | exe.setTarget(target); | 19 | .name = "$", |
| 16 | exe.setBuildMode(mode); | 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 | exe.install(); | 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 | const run_cmd = exe.run(); | 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 | run_cmd.step.dependOn(b.getInstallStep()); | 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 | if (b.args) |args| { | 45 | if (b.args) |args| { |
| 22 | run_cmd.addArgs(args); | 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 | const run_step = b.step("run", "Run the app"); | 52 | const run_step = b.step("run", "Run the app"); |
| 26 | run_step.dependOn(&run_cmd.step); | 53 | run_step.dependOn(&run_cmd.step); |
| 27 | 54 | ||
| 28 | const exe_tests = b.addTest("src/main.zig"); | 55 | // Creates a step for unit testing. |
| 29 | exe_tests.setTarget(target); | 56 | const exe_tests = b.addTest(.{ |
| 30 | exe_tests.setBuildMode(mode); | 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 | const test_step = b.step("test", "Run unit tests"); | 65 | const test_step = b.step("test", "Run unit tests"); |
| 33 | test_step.dependOn(&exe_tests.step); | 66 | test_step.dependOn(&exe_tests.step); |
| 34 | } | 67 | } |
lib/init-lib/build.zig+35-8| ... | @@ -1,17 +1,44 @@ | ... | @@ -1,17 +1,44 @@ |
| 1 | const std = @import("std"); | 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 | // Standard release options allow the person running `zig build` to select | 4 | // declaratively construct a build graph that will be executed by an external |
| 5 | // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. | 5 | // runner. |
| 6 | const mode = b.standardReleaseOptions(); | 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"); | 13 | // Standard optimization options allow the person running `zig build` to select |
| 9 | lib.setBuildMode(mode); | 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 | lib.install(); | 30 | lib.install(); |
| 11 | 31 | ||
| 12 | const main_tests = b.addTest("src/main.zig"); | 32 | // Creates a step for unit testing. |
| 13 | main_tests.setBuildMode(mode); | 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 | const test_step = b.step("test", "Run library tests"); | 42 | const test_step = b.step("test", "Run library tests"); |
| 16 | test_step.dependOn(&main_tests.step); | 43 | test_step.dependOn(&main_tests.step); |
| 17 | } | 44 | } |
lib/libc/mingw/misc/strtoimax.c+1-4| ... | @@ -31,10 +31,7 @@ | ... | @@ -31,10 +31,7 @@ |
| 31 | #define valid(n, b)	((n) >= 0 && (n) < (b)) | 31 | #define valid(n, b)	((n) >= 0 && (n) < (b)) |
| 32 | 32 | ||
| 33 | intmax_t | 33 | intmax_t |
| 34 | strtoimax(nptr, endptr, base) | 34 | strtoimax(const char * __restrict__ nptr, char ** __restrict__ endptr, int base) |
| 35 | 	register const char * __restrict__	nptr; | ||
| 36 | 	char ** __restrict__			endptr; | ||
| 37 | 	register int				base; | ||
| 38 | 	{ | 35 | 	{ |
| 39 | 	register uintmax_t	accum;	/* accumulates converted value */ | 36 | 	register uintmax_t	accum;	/* accumulates converted value */ |
| 40 | 	register int		n;	/* numeral from digit character */ | 37 | 	register int		n;	/* numeral from digit character */ |
lib/libc/mingw/misc/strtoumax.c+1-4| ... | @@ -31,10 +31,7 @@ | ... | @@ -31,10 +31,7 @@ |
| 31 | #define valid(n, b)	((n) >= 0 && (n) < (b)) | 31 | #define valid(n, b)	((n) >= 0 && (n) < (b)) |
| 32 | 32 | ||
| 33 | uintmax_t | 33 | uintmax_t |
| 34 | strtoumax(nptr, endptr, base) | 34 | strtoumax(const char * __restrict__ nptr, char ** __restrict__ endptr, int base) |
| 35 | 	register const char * __restrict__	nptr; | ||
| 36 | 	char ** __restrict__			endptr; | ||
| 37 | 	register int				base; | ||
| 38 | 	{ | 35 | 	{ |
| 39 | 	register uintmax_t	accum;	/* accumulates converted value */ | 36 | 	register uintmax_t	accum;	/* accumulates converted value */ |
| 40 | 	register uintmax_t	next;	/* for computing next value of accum */ | 37 | 	register uintmax_t	next;	/* for computing next value of accum */ |
lib/libc/mingw/misc/wcstoimax.c+1-4| ... | @@ -33,10 +33,7 @@ | ... | @@ -33,10 +33,7 @@ |
| 33 | #define valid(n, b)	((n) >= 0 && (n) < (b)) | 33 | #define valid(n, b)	((n) >= 0 && (n) < (b)) |
| 34 | 34 | ||
| 35 | intmax_t | 35 | intmax_t |
| 36 | wcstoimax(nptr, endptr, base) | 36 | wcstoimax(const wchar_t * __restrict__ nptr, wchar_t ** __restrict__ endptr, int base) |
| 37 | 	register const wchar_t * __restrict__	nptr; | ||
| 38 | 	wchar_t ** __restrict__				endptr; | ||
| 39 | 	register int					base; | ||
| 40 | 	{ | 37 | 	{ |
| 41 | 	register uintmax_t	accum;	/* accumulates converted value */ | 38 | 	register uintmax_t	accum;	/* accumulates converted value */ |
| 42 | 	register int		n;	/* numeral from digit character */ | 39 | 	register int		n;	/* numeral from digit character */ |
lib/libc/mingw/misc/wcstoumax.c+1-4| ... | @@ -33,10 +33,7 @@ | ... | @@ -33,10 +33,7 @@ |
| 33 | #define valid(n, b)	((n) >= 0 && (n) < (b)) | 33 | #define valid(n, b)	((n) >= 0 && (n) < (b)) |
| 34 | 34 | ||
| 35 | uintmax_t | 35 | uintmax_t |
| 36 | wcstoumax(nptr, endptr, base) | 36 | wcstoumax(const wchar_t * __restrict__ nptr, wchar_t ** __restrict__ endptr, int base) |
| 37 | 	register const wchar_t * __restrict__	nptr; | ||
| 38 | 	wchar_t ** __restrict__				endptr; | ||
| 39 | 	register int					base; | ||
| 40 | 	{ | 37 | 	{ |
| 41 | 	register uintmax_t	accum;	/* accumulates converted value */ | 38 | 	register uintmax_t	accum;	/* accumulates converted value */ |
| 42 | 	register uintmax_t	next;	/* for computing next value of accum */ | 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,7 +166,7 @@ pub const GetNameError = error{ |
| 166 | 166 | ||
| 167 | pub fn getName(self: Thread, buffer_ptr: *[max_name_len:0]u8) GetNameError!?[]const u8 { | 167 | pub fn getName(self: Thread, buffer_ptr: *[max_name_len:0]u8) GetNameError!?[]const u8 { |
| 168 | buffer_ptr[max_name_len] = 0; | 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 | switch (target.os.tag) { | 171 | switch (target.os.tag) { |
| 172 | .linux => if (use_pthreads and is_gnu) { | 172 | .linux => if (use_pthreads and is_gnu) { |
lib/std/array_hash_map.zig+2-1| ... | @@ -1145,7 +1145,8 @@ pub fn ArrayHashMapUnmanaged( | ... | @@ -1145,7 +1145,8 @@ pub fn ArrayHashMapUnmanaged( |
| 1145 | } | 1145 | } |
| 1146 | 1146 | ||
| 1147 | /// Create a copy of the hash map which can be modified separately. | 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 | pub fn clone(self: Self, allocator: Allocator) !Self { | 1150 | pub fn clone(self: Self, allocator: Allocator) !Self { |
| 1150 | if (@sizeOf(ByIndexContext) != 0) | 1151 | if (@sizeOf(ByIndexContext) != 0) |
| 1151 | @compileError("Cannot infer context " ++ @typeName(Context) ++ ", call cloneContext instead."); | 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,7 +29,11 @@ pub fn BoundedArray(comptime T: type, comptime buffer_capacity: usize) type { |
| 29 | } | 29 | } |
| 30 | 30 | ||
| 31 | /// View the internal array as a slice whose size was previously set. | 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 | return self.buffer[0..self.len]; | 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,13 +131,16 @@ pub const CodeModel = enum { |
| 131 | 131 | ||
| 132 | /// This data structure is used by the Zig language code generation and | 132 | /// This data structure is used by the Zig language code generation and |
| 133 | /// therefore must be kept in sync with the compiler implementation. | 133 | /// therefore must be kept in sync with the compiler implementation. |
| 134 | pub const Mode = enum { | 134 | pub const OptimizeMode = enum { |
| 135 | Debug, | 135 | Debug, |
| 136 | ReleaseSafe, | 136 | ReleaseSafe, |
| 137 | ReleaseFast, | 137 | ReleaseFast, |
| 138 | ReleaseSmall, | 138 | ReleaseSmall, |
| 139 | }; | 139 | }; |
| 140 | 140 | ||
| 141 | /// Deprecated; use OptimizeMode. | ||
| 142 | pub const Mode = OptimizeMode; | ||
| 143 | |||
| 141 | /// This data structure is used by the Zig language code generation and | 144 | /// This data structure is used by the Zig language code generation and |
| 142 | /// therefore must be kept in sync with the compiler implementation. | 145 | /// therefore must be kept in sync with the compiler implementation. |
| 143 | pub const CallingConvention = enum { | 146 | pub const CallingConvention = enum { |
lib/std/c.zig+2-1| ... | @@ -90,6 +90,8 @@ pub usingnamespace switch (builtin.os.tag) { | ... | @@ -90,6 +90,8 @@ pub usingnamespace switch (builtin.os.tag) { |
| 90 | pub extern "c" fn stat(noalias path: [*:0]const u8, noalias buf: *c.Stat) c_int; | 90 | pub extern "c" fn stat(noalias path: [*:0]const u8, noalias buf: *c.Stat) c_int; |
| 91 | 91 | ||
| 92 | pub extern "c" fn alarm(seconds: c_uint) c_uint; | 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,7 +147,6 @@ pub extern "c" fn write(fd: c.fd_t, buf: [*]const u8, nbyte: usize) isize; |
| 145 | pub extern "c" fn pwrite(fd: c.fd_t, buf: [*]const u8, nbyte: usize, offset: c.off_t) isize; | 147 | pub extern "c" fn pwrite(fd: c.fd_t, buf: [*]const u8, nbyte: usize, offset: c.off_t) isize; |
| 146 | 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; | 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 | pub extern "c" fn munmap(addr: *align(page_size) const anyopaque, len: usize) c_int; | 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 | pub extern "c" fn mprotect(addr: *align(page_size) anyopaque, len: usize, prot: c_uint) c_int; | 150 | pub extern "c" fn mprotect(addr: *align(page_size) anyopaque, len: usize, prot: c_uint) c_int; |
| 150 | pub extern "c" fn link(oldpath: [*:0]const u8, newpath: [*:0]const u8, flags: c_int) c_int; | 151 | pub extern "c" fn link(oldpath: [*:0]const u8, newpath: [*:0]const u8, flags: c_int) c_int; |
| 151 | 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; | 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,6 +59,9 @@ pub const sched_yield = __libc_thr_yield; |
| 59 | 59 | ||
| 60 | pub extern "c" fn posix_memalign(memptr: *?*anyopaque, alignment: usize, size: usize) c_int; | 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 | pub const pthread_mutex_t = extern struct { | 65 | pub const pthread_mutex_t = extern struct { |
| 63 | magic: u32 = 0x33330003, | 66 | magic: u32 = 0x33330003, |
| 64 | errorcheck: padded_pthread_spin_t = 0, | 67 | errorcheck: padded_pthread_spin_t = 0, |
lib/std/child_process.zig+2-2| ... | @@ -1164,7 +1164,7 @@ fn windowsCreateProcessPathExt( | ... | @@ -1164,7 +1164,7 @@ fn windowsCreateProcessPathExt( |
| 1164 | var app_name_unicode_string = windows.UNICODE_STRING{ | 1164 | var app_name_unicode_string = windows.UNICODE_STRING{ |
| 1165 | .Length = app_name_len_bytes, | 1165 | .Length = app_name_len_bytes, |
| 1166 | .MaximumLength = app_name_len_bytes, | 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 | const rc = windows.ntdll.NtQueryDirectoryFile( | 1169 | const rc = windows.ntdll.NtQueryDirectoryFile( |
| 1170 | dir.fd, | 1170 | dir.fd, |
| ... | @@ -1261,7 +1261,7 @@ fn windowsCreateProcessPathExt( | ... | @@ -1261,7 +1261,7 @@ fn windowsCreateProcessPathExt( |
| 1261 | var app_name_unicode_string = windows.UNICODE_STRING{ | 1261 | var app_name_unicode_string = windows.UNICODE_STRING{ |
| 1262 | .Length = app_name_len_bytes, | 1262 | .Length = app_name_len_bytes, |
| 1263 | .MaximumLength = app_name_len_bytes, | 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 | // Re-use the directory handle but this time we call with the appended app name | 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,7 +28,6 @@ test "cstr fns" { |
| 28 | 28 | ||
| 29 | fn testCStrFnsImpl() !void { | 29 | fn testCStrFnsImpl() !void { |
| 30 | try testing.expect(cmp("aoeu", "aoez") == -1); | 30 | try testing.expect(cmp("aoeu", "aoez") == -1); |
| 31 | try testing.expect(mem.len("123456789") == 9); | ||
| 32 | } | 31 | } |
| 33 | 32 | ||
| 34 | /// Returns a mutable, null-terminated slice with the same length as `slice`. | 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,6 +2060,11 @@ pub fn dumpStackPointerAddr(prefix: []const u8) void { |
| 2060 | test "manage resources correctly" { | 2060 | test "manage resources correctly" { |
| 2061 | if (builtin.os.tag == .wasi) return error.SkipZigTest; | 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 | const writer = std.io.null_writer; | 2068 | const writer = std.io.null_writer; |
| 2064 | var di = try openSelfDebugInfo(testing.allocator); | 2069 | var di = try openSelfDebugInfo(testing.allocator); |
| 2065 | defer di.deinit(); | 2070 | defer di.deinit(); |
lib/std/fmt.zig+6-5| ... | @@ -1,11 +1,12 @@ | ... | @@ -1,11 +1,12 @@ |
| 1 | const std = @import("std.zig"); | 1 | const std = @import("std.zig"); |
| 2 | const builtin = @import("builtin"); | ||
| 3 | |||
| 2 | const io = std.io; | 4 | const io = std.io; |
| 3 | const math = std.math; | 5 | const math = std.math; |
| 4 | const assert = std.debug.assert; | 6 | const assert = std.debug.assert; |
| 5 | const mem = std.mem; | 7 | const mem = std.mem; |
| 6 | const unicode = std.unicode; | 8 | const unicode = std.unicode; |
| 7 | const meta = std.meta; | 9 | const meta = std.meta; |
| 8 | const builtin = @import("builtin"); | ||
| 9 | const errol = @import("fmt/errol.zig"); | 10 | const errol = @import("fmt/errol.zig"); |
| 10 | const lossyCast = std.math.lossyCast; | 11 | const lossyCast = std.math.lossyCast; |
| 11 | const expectFmt = std.testing.expectFmt; | 12 | const expectFmt = std.testing.expectFmt; |
| ... | @@ -190,7 +191,7 @@ pub fn format( | ... | @@ -190,7 +191,7 @@ pub fn format( |
| 190 | .precision = precision, | 191 | .precision = precision, |
| 191 | }, | 192 | }, |
| 192 | writer, | 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,15 +2141,15 @@ test "buffer" { |
| 2140 | { | 2141 | { |
| 2141 | var buf1: [32]u8 = undefined; | 2142 | var buf1: [32]u8 = undefined; |
| 2142 | var fbs = std.io.fixedBufferStream(&buf1); | 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 | try std.testing.expect(mem.eql(u8, fbs.getWritten(), "1234")); | 2145 | try std.testing.expect(mem.eql(u8, fbs.getWritten(), "1234")); |
| 2145 | 2146 | ||
| 2146 | fbs.reset(); | 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 | try std.testing.expect(mem.eql(u8, fbs.getWritten(), "a")); | 2149 | try std.testing.expect(mem.eql(u8, fbs.getWritten(), "a")); |
| 2149 | 2150 | ||
| 2150 | fbs.reset(); | 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 | try std.testing.expect(mem.eql(u8, fbs.getWritten(), "1100")); | 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,7 +834,7 @@ pub const IterableDir = struct { |
| 834 | self.end_index = self.index; // Force fd_readdir in the next loop. | 834 | self.end_index = self.index; // Force fd_readdir in the next loop. |
| 835 | continue :start_over; | 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 | const next_index = name_index + entry.d_namlen; | 839 | const next_index = name_index + entry.d_namlen; |
| 840 | self.index = next_index; | 840 | self.index = next_index; |
| ... | @@ -1763,7 +1763,7 @@ pub const Dir = struct { | ... | @@ -1763,7 +1763,7 @@ pub const Dir = struct { |
| 1763 | var nt_name = w.UNICODE_STRING{ | 1763 | var nt_name = w.UNICODE_STRING{ |
| 1764 | .Length = path_len_bytes, | 1764 | .Length = path_len_bytes, |
| 1765 | .MaximumLength = path_len_bytes, | 1765 | .MaximumLength = path_len_bytes, |
| 1766 | .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w)), | 1766 | .Buffer = @qualCast([*:0]u16, sub_path_w), |
| 1767 | }; | 1767 | }; |
| 1768 | var attr = w.OBJECT_ATTRIBUTES{ | 1768 | var attr = w.OBJECT_ATTRIBUTES{ |
| 1769 | .Length = @sizeOf(w.OBJECT_ATTRIBUTES), | 1769 | .Length = @sizeOf(w.OBJECT_ATTRIBUTES), |
lib/std/fs/file.zig+2-1| ... | @@ -179,7 +179,7 @@ pub const File = struct { | ... | @@ -179,7 +179,7 @@ pub const File = struct { |
| 179 | lock_nonblocking: bool = false, | 179 | lock_nonblocking: bool = false, |
| 180 | 180 | ||
| 181 | /// For POSIX systems this is the file system mode the file will | 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 | mode: Mode = default_mode, | 183 | mode: Mode = default_mode, |
| 184 | 184 | ||
| 185 | /// Setting this to `.blocking` prevents `O.NONBLOCK` from being passed even | 185 | /// Setting this to `.blocking` prevents `O.NONBLOCK` from being passed even |
| ... | @@ -307,6 +307,7 @@ pub const File = struct { | ... | @@ -307,6 +307,7 @@ pub const File = struct { |
| 307 | /// is unique to each filesystem. | 307 | /// is unique to each filesystem. |
| 308 | inode: INode, | 308 | inode: INode, |
| 309 | size: u64, | 309 | size: u64, |
| 310 | /// This is available on POSIX systems and is always 0 otherwise. | ||
| 310 | mode: Mode, | 311 | mode: Mode, |
| 311 | kind: Kind, | 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,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))) { | 116 | pub fn fixedBufferStream(buffer: anytype) FixedBufferStream(Slice(@TypeOf(buffer))) { |
| 117 | return .{ .buffer = mem.span(buffer), .pos = 0 }; | 117 | return .{ .buffer = buffer, .pos = 0 }; |
| 118 | } | 118 | } |
| 119 | 119 | ||
| 120 | fn NonSentinelSpan(comptime T: type) type { | 120 | fn Slice(comptime T: type) type { |
| 121 | var ptr_info = @typeInfo(mem.Span(T)).Pointer; | 121 | switch (@typeInfo(T)) { |
| 122 | ptr_info.sentinel = null; | 122 | .Pointer => |ptr_info| { |
| 123 | return @Type(.{ .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 | test "FixedBufferStream output" { | 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,7 +1384,7 @@ fn ParseInternalErrorImpl(comptime T: type, comptime inferred_types: []const typ |
| 1384 | return errors; | 1384 | return errors; |
| 1385 | }, | 1385 | }, |
| 1386 | .Array => |arrayInfo| { | 1386 | .Array => |arrayInfo| { |
| 1387 | return error{ UnexpectedEndOfJson, UnexpectedToken } || TokenStream.Error || | 1387 | return error{ UnexpectedEndOfJson, UnexpectedToken, LengthMismatch } || TokenStream.Error || |
| 1388 | UnescapeValidStringError || | 1388 | UnescapeValidStringError || |
| 1389 | ParseInternalErrorImpl(arrayInfo.child, inferred_types ++ [_]type{T}); | 1389 | ParseInternalErrorImpl(arrayInfo.child, inferred_types ++ [_]type{T}); |
| 1390 | }, | 1390 | }, |
| ... | @@ -1625,6 +1625,7 @@ fn parseInternal( | ... | @@ -1625,6 +1625,7 @@ fn parseInternal( |
| 1625 | if (arrayInfo.child != u8) return error.UnexpectedToken; | 1625 | if (arrayInfo.child != u8) return error.UnexpectedToken; |
| 1626 | var r: T = undefined; | 1626 | var r: T = undefined; |
| 1627 | const source_slice = stringToken.slice(tokens.slice, tokens.i - 1); | 1627 | const source_slice = stringToken.slice(tokens.slice, tokens.i - 1); |
| 1628 | if (r.len != stringToken.decodedLength()) return error.LengthMismatch; | ||
| 1628 | switch (stringToken.escapes) { | 1629 | switch (stringToken.escapes) { |
| 1629 | .None => mem.copy(u8, &r, source_slice), | 1630 | .None => mem.copy(u8, &r, source_slice), |
| 1630 | .Some => try unescapeValidString(&r, source_slice), | 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,6 +2238,12 @@ test "parse into struct with no fields" { |
| 2238 | try testing.expectEqual(T{}, try parse(T, &ts, ParseOptions{})); | 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 | test "parse into struct with misc fields" { | 2247 | test "parse into struct with misc fields" { |
| 2242 | @setEvalBranchQuota(10000); | 2248 | @setEvalBranchQuota(10000); |
| 2243 | const options = ParseOptions{ .allocator = testing.allocator }; | 2249 | const options = ParseOptions{ .allocator = testing.allocator }; |
lib/std/mem.zig+24-78| ... | @@ -636,12 +636,9 @@ test "indexOfDiff" { | ... | @@ -636,12 +636,9 @@ test "indexOfDiff" { |
| 636 | try testing.expectEqual(indexOfDiff(u8, "xne", "one"), 0); | 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 | 639 | /// Takes a sentinel-terminated pointer and returns a slice preserving pointer attributes. |
| 640 | /// returns a slice. If there is a sentinel on the input type, there will be a | 640 | /// `[*c]` pointers are assumed to be 0-terminated and assumed to not be allowzero. |
| 641 | /// sentinel on the output type. The constness of the output type matches | 641 | fn Span(comptime T: type) type { |
| 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 { | ||
| 645 | switch (@typeInfo(T)) { | 642 | switch (@typeInfo(T)) { |
| 646 | .Optional => |optional_info| { | 643 | .Optional => |optional_info| { |
| 647 | return ?Span(optional_info.child); | 644 | return ?Span(optional_info.child); |
| ... | @@ -649,39 +646,22 @@ pub fn Span(comptime T: type) type { | ... | @@ -649,39 +646,22 @@ pub fn Span(comptime T: type) type { |
| 649 | .Pointer => |ptr_info| { | 646 | .Pointer => |ptr_info| { |
| 650 | var new_ptr_info = ptr_info; | 647 | var new_ptr_info = ptr_info; |
| 651 | switch (ptr_info.size) { | 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 | .C => { | 649 | .C => { |
| 660 | new_ptr_info.sentinel = &@as(ptr_info.child, 0); | 650 | new_ptr_info.sentinel = &@as(ptr_info.child, 0); |
| 661 | new_ptr_info.is_allowzero = false; | 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 | new_ptr_info.size = .Slice; | 656 | new_ptr_info.size = .Slice; |
| 666 | return @Type(.{ .Pointer = new_ptr_info }); | 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 | test "Span" { | 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 | try testing.expect(Span([*:1]u16) == [:1]u16); | 665 | try testing.expect(Span([*:1]u16) == [:1]u16); |
| 686 | try testing.expect(Span(?[*:1]u16) == ?[:1]u16); | 666 | try testing.expect(Span(?[*:1]u16) == ?[:1]u16); |
| 687 | try testing.expect(Span([*:1]const u8) == [:1]const u8); | 667 | try testing.expect(Span([*:1]const u8) == [:1]const u8); |
| ... | @@ -692,13 +672,10 @@ test "Span" { | ... | @@ -692,13 +672,10 @@ test "Span" { |
| 692 | try testing.expect(Span(?[*c]const u8) == ?[:0]const u8); | 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 | 675 | /// Takes a sentinel-terminated pointer and returns a slice, iterating over the |
| 696 | /// returns a slice. If there is a sentinel on the input type, there will be a | 676 | /// memory to find the sentinel and determine the length. |
| 697 | /// sentinel on the output type. The constness of the output type matches | 677 | /// Ponter attributes such as const are preserved. |
| 698 | /// the constness of the input type. | 678 | /// `[*c]` pointers are assumed to be non-null and 0-terminated. |
| 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. | ||
| 702 | pub fn span(ptr: anytype) Span(@TypeOf(ptr)) { | 679 | pub fn span(ptr: anytype) Span(@TypeOf(ptr)) { |
| 703 | if (@typeInfo(@TypeOf(ptr)) == .Optional) { | 680 | if (@typeInfo(@TypeOf(ptr)) == .Optional) { |
| 704 | if (ptr) |non_null| { | 681 | if (ptr) |non_null| { |
| ... | @@ -722,7 +699,6 @@ test "span" { | ... | @@ -722,7 +699,6 @@ test "span" { |
| 722 | var array: [5]u16 = [_]u16{ 1, 2, 3, 4, 5 }; | 699 | var array: [5]u16 = [_]u16{ 1, 2, 3, 4, 5 }; |
| 723 | const ptr = @as([*:3]u16, array[0..2 :3]); | 700 | const ptr = @as([*:3]u16, array[0..2 :3]); |
| 724 | try testing.expect(eql(u16, span(ptr), &[_]u16{ 1, 2 })); | 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 | try testing.expectEqual(@as(?[:0]u16, null), span(@as(?[*:0]u16, null))); | 702 | try testing.expectEqual(@as(?[:0]u16, null), span(@as(?[*:0]u16, null))); |
| 727 | } | 703 | } |
| 728 | 704 | ||
| ... | @@ -919,22 +895,15 @@ test "lenSliceTo" { | ... | @@ -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, | 898 | /// Takes a sentinel-terminated pointer and iterates over the memory to find the |
| 923 | /// a slice or a tuple, and returns the length. | 899 | /// sentinel and determine the length. |
| 924 | /// In the case of a sentinel-terminated array, it uses the array length. | 900 | /// `[*c]` pointers are assumed to be non-null and 0-terminated. |
| 925 | /// For C pointers it assumes it is a pointer-to-many with a 0 sentinel. | ||
| 926 | pub fn len(value: anytype) usize { | 901 | pub fn len(value: anytype) usize { |
| 927 | return switch (@typeInfo(@TypeOf(value))) { | 902 | switch (@typeInfo(@TypeOf(value))) { |
| 928 | .Array => |info| info.len, | ||
| 929 | .Vector => |info| info.len, | ||
| 930 | .Pointer => |info| switch (info.size) { | 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 | .Many => { | 904 | .Many => { |
| 936 | const sentinel_ptr = info.sentinel orelse | 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 | const sentinel = @ptrCast(*align(1) const info.child, sentinel_ptr).*; | 907 | const sentinel = @ptrCast(*align(1) const info.child, sentinel_ptr).*; |
| 939 | return indexOfSentinel(info.child, sentinel, value); | 908 | return indexOfSentinel(info.child, sentinel, value); |
| 940 | }, | 909 | }, |
| ... | @@ -942,41 +911,18 @@ pub fn len(value: anytype) usize { | ... | @@ -942,41 +911,18 @@ pub fn len(value: anytype) usize { |
| 942 | assert(value != null); | 911 | assert(value != null); |
| 943 | return indexOfSentinel(info.child, 0, value); | 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) { | 916 | else => @compileError("invalid type given to std.mem.len: " ++ @typeName(@TypeOf(value))), |
| 948 | return info.fields.len; | 917 | } |
| 949 | } else @compileError("invalid type given to std.mem.len"), | ||
| 950 | else => @compileError("invalid type given to std.mem.len"), | ||
| 951 | }; | ||
| 952 | } | 918 | } |
| 953 | 919 | ||
| 954 | test "len" { | 920 | test "len" { |
| 955 | try testing.expect(len("aoeu") == 4); | 921 | var array: [5]u16 = [_]u16{ 1, 2, 0, 4, 5 }; |
| 956 | 922 | const ptr = @as([*:4]u16, array[0..3 :4]); | |
| 957 | { | 923 | try testing.expect(len(ptr) == 3); |
| 958 | var array: [5]u16 = [_]u16{ 1, 2, 3, 4, 5 }; | 924 | const c_ptr = @as([*c]u16, ptr); |
| 959 | try testing.expect(len(&array) == 5); | 925 | try testing.expect(len(c_ptr) == 2); |
| 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 | } | ||
| 980 | } | 926 | } |
| 981 | 927 | ||
| 982 | pub fn indexOfSentinel(comptime Elem: type, comptime sentinel: Elem, ptr: [*:sentinel]const Elem) usize { | 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,7 +550,6 @@ pub fn abort() noreturn { |
| 550 | exit(0); // TODO choose appropriate exit code | 550 | exit(0); // TODO choose appropriate exit code |
| 551 | } | 551 | } |
| 552 | if (builtin.os.tag == .wasi) { | 552 | if (builtin.os.tag == .wasi) { |
| 553 | @breakpoint(); | ||
| 554 | exit(1); | 553 | exit(1); |
| 555 | } | 554 | } |
| 556 | if (builtin.os.tag == .cuda) { | 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,7 +4513,7 @@ pub fn faccessatW(dirfd: fd_t, sub_path_w: [*:0]const u16, mode: u32, flags: u32 |
| 4514 | var nt_name = windows.UNICODE_STRING{ | 4513 | var nt_name = windows.UNICODE_STRING{ |
| 4515 | .Length = path_len_bytes, | 4514 | .Length = path_len_bytes, |
| 4516 | .MaximumLength = path_len_bytes, | 4515 | .MaximumLength = path_len_bytes, |
| 4517 | .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w)), | 4516 | .Buffer = @qualCast([*:0]u16, sub_path_w), |
| 4518 | }; | 4517 | }; |
| 4519 | var attr = windows.OBJECT_ATTRIBUTES{ | 4518 | var attr = windows.OBJECT_ATTRIBUTES{ |
| 4520 | .Length = @sizeOf(windows.OBJECT_ATTRIBUTES), | 4519 | .Length = @sizeOf(windows.OBJECT_ATTRIBUTES), |
| ... | @@ -6029,7 +6028,7 @@ pub fn sendfile( | ... | @@ -6029,7 +6028,7 @@ pub fn sendfile( |
| 6029 | .BADF => unreachable, // Always a race condition. | 6028 | .BADF => unreachable, // Always a race condition. |
| 6030 | .FAULT => unreachable, // Segmentation fault. | 6029 | .FAULT => unreachable, // Segmentation fault. |
| 6031 | .OVERFLOW => unreachable, // We avoid passing too large of a `count`. | 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 | .INVAL, .NOSYS => { | 6033 | .INVAL, .NOSYS => { |
| 6035 | // EINVAL could be any of the following situations: | 6034 | // EINVAL could be any of the following situations: |
| ... | @@ -6097,7 +6096,7 @@ pub fn sendfile( | ... | @@ -6097,7 +6096,7 @@ pub fn sendfile( |
| 6097 | 6096 | ||
| 6098 | .BADF => unreachable, // Always a race condition. | 6097 | .BADF => unreachable, // Always a race condition. |
| 6099 | .FAULT => unreachable, // Segmentation fault. | 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 | .INVAL, .OPNOTSUPP, .NOTSOCK, .NOSYS => { | 6101 | .INVAL, .OPNOTSUPP, .NOTSOCK, .NOSYS => { |
| 6103 | // EINVAL could be any of the following situations: | 6102 | // EINVAL could be any of the following situations: |
| ... | @@ -6179,7 +6178,7 @@ pub fn sendfile( | ... | @@ -6179,7 +6178,7 @@ pub fn sendfile( |
| 6179 | .BADF => unreachable, // Always a race condition. | 6178 | .BADF => unreachable, // Always a race condition. |
| 6180 | .FAULT => unreachable, // Segmentation fault. | 6179 | .FAULT => unreachable, // Segmentation fault. |
| 6181 | .INVAL => unreachable, | 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 | .OPNOTSUPP, .NOTSOCK, .NOSYS => break :sf, | 6183 | .OPNOTSUPP, .NOTSOCK, .NOSYS => break :sf, |
| 6185 | 6184 | ||
| ... | @@ -6473,7 +6472,7 @@ pub fn recvfrom( | ... | @@ -6473,7 +6472,7 @@ pub fn recvfrom( |
| 6473 | .BADF => unreachable, // always a race condition | 6472 | .BADF => unreachable, // always a race condition |
| 6474 | .FAULT => unreachable, | 6473 | .FAULT => unreachable, |
| 6475 | .INVAL => unreachable, | 6474 | .INVAL => unreachable, |
| 6476 | .NOTCONN => unreachable, | 6475 | .NOTCONN => return error.SocketNotConnected, |
| 6477 | .NOTSOCK => unreachable, | 6476 | .NOTSOCK => unreachable, |
| 6478 | .INTR => continue, | 6477 | .INTR => continue, |
| 6479 | .AGAIN => return error.WouldBlock, | 6478 | .AGAIN => return error.WouldBlock, |
lib/std/os/uefi/pool_allocator.zig+1-1| ... | @@ -22,7 +22,7 @@ const UefiPoolAllocator = struct { | ... | @@ -22,7 +22,7 @@ const UefiPoolAllocator = struct { |
| 22 | 22 | ||
| 23 | assert(len > 0); | 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 | const metadata_len = mem.alignForward(@sizeOf(usize), ptr_align); | 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,7 +85,7 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN |
| 85 | var nt_name = UNICODE_STRING{ | 85 | var nt_name = UNICODE_STRING{ |
| 86 | .Length = path_len_bytes, | 86 | .Length = path_len_bytes, |
| 87 | .MaximumLength = path_len_bytes, | 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 | var attr = OBJECT_ATTRIBUTES{ | 90 | var attr = OBJECT_ATTRIBUTES{ |
| 91 | .Length = @sizeOf(OBJECT_ATTRIBUTES), | 91 | .Length = @sizeOf(OBJECT_ATTRIBUTES), |
| ... | @@ -634,7 +634,7 @@ pub fn SetCurrentDirectory(path_name: []const u16) SetCurrentDirectoryError!void | ... | @@ -634,7 +634,7 @@ pub fn SetCurrentDirectory(path_name: []const u16) SetCurrentDirectoryError!void |
| 634 | var nt_name = UNICODE_STRING{ | 634 | var nt_name = UNICODE_STRING{ |
| 635 | .Length = path_len_bytes, | 635 | .Length = path_len_bytes, |
| 636 | .MaximumLength = path_len_bytes, | 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 | const rc = ntdll.RtlSetCurrentDirectory_U(&nt_name); | 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,7 +766,7 @@ pub fn ReadLink(dir: ?HANDLE, sub_path_w: []const u16, out_buffer: []u8) ReadLin |
| 766 | var nt_name = UNICODE_STRING{ | 766 | var nt_name = UNICODE_STRING{ |
| 767 | .Length = path_len_bytes, | 767 | .Length = path_len_bytes, |
| 768 | .MaximumLength = path_len_bytes, | 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 | var attr = OBJECT_ATTRIBUTES{ | 771 | var attr = OBJECT_ATTRIBUTES{ |
| 772 | .Length = @sizeOf(OBJECT_ATTRIBUTES), | 772 | .Length = @sizeOf(OBJECT_ATTRIBUTES), |
| ... | @@ -876,7 +876,7 @@ pub fn DeleteFile(sub_path_w: []const u16, options: DeleteFileOptions) DeleteFil | ... | @@ -876,7 +876,7 @@ pub fn DeleteFile(sub_path_w: []const u16, options: DeleteFileOptions) DeleteFil |
| 876 | .Length = path_len_bytes, | 876 | .Length = path_len_bytes, |
| 877 | .MaximumLength = path_len_bytes, | 877 | .MaximumLength = path_len_bytes, |
| 878 | // The Windows API makes this mutable, but it will not mutate here. | 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 | if (sub_path_w[0] == '.' and sub_path_w[1] == 0) { | 882 | if (sub_path_w[0] == '.' and sub_path_w[1] == 0) { |
| ... | @@ -1414,7 +1414,7 @@ pub fn sendmsg( | ... | @@ -1414,7 +1414,7 @@ pub fn sendmsg( |
| 1414 | } | 1414 | } |
| 1415 | 1415 | ||
| 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 { | 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 | var bytes_send: DWORD = undefined; | 1418 | var bytes_send: DWORD = undefined; |
| 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) { | 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 | return ws2_32.SOCKET_ERROR; | 1420 | return ws2_32.SOCKET_ERROR; |
| ... | @@ -1876,13 +1876,13 @@ pub fn eqlIgnoreCaseWTF16(a: []const u16, b: []const u16) bool { | ... | @@ -1876,13 +1876,13 @@ pub fn eqlIgnoreCaseWTF16(a: []const u16, b: []const u16) bool { |
| 1876 | const a_string = UNICODE_STRING{ | 1876 | const a_string = UNICODE_STRING{ |
| 1877 | .Length = a_bytes, | 1877 | .Length = a_bytes, |
| 1878 | .MaximumLength = a_bytes, | 1878 | .MaximumLength = a_bytes, |
| 1879 | .Buffer = @intToPtr([*]u16, @ptrToInt(a.ptr)), | 1879 | .Buffer = @qualCast([*]u16, a.ptr), |
| 1880 | }; | 1880 | }; |
| 1881 | const b_bytes = @intCast(u16, b.len * 2); | 1881 | const b_bytes = @intCast(u16, b.len * 2); |
| 1882 | const b_string = UNICODE_STRING{ | 1882 | const b_string = UNICODE_STRING{ |
| 1883 | .Length = b_bytes, | 1883 | .Length = b_bytes, |
| 1884 | .MaximumLength = b_bytes, | 1884 | .MaximumLength = b_bytes, |
| 1885 | .Buffer = @intToPtr([*]u16, @ptrToInt(b.ptr)), | 1885 | .Buffer = @qualCast([*]u16, b.ptr), |
| 1886 | }; | 1886 | }; |
| 1887 | return ntdll.RtlEqualUnicodeString(&a_string, &b_string, TRUE) == TRUE; | 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,12 +7,14 @@ export var _tls_end: u8 linksection(".tls$ZZZ") = 0; |
| 7 | export var __xl_a: std.os.windows.PIMAGE_TLS_CALLBACK linksection(".CRT$XLA") = null; | 7 | export var __xl_a: std.os.windows.PIMAGE_TLS_CALLBACK linksection(".CRT$XLA") = null; |
| 8 | export var __xl_z: std.os.windows.PIMAGE_TLS_CALLBACK linksection(".CRT$XLZ") = null; | 8 | export var __xl_z: std.os.windows.PIMAGE_TLS_CALLBACK linksection(".CRT$XLZ") = null; |
| 9 | 9 | ||
| 10 | const tls_array: u32 = 0x2c; | ||
| 11 | comptime { | 10 | comptime { |
| 12 | if (builtin.target.cpu.arch == .x86) { | 11 | if (builtin.target.cpu.arch == .x86 and builtin.zig_backend != .stage2_c) { |
| 13 | // The __tls_array is the offset of the ThreadLocalStoragePointer field | 12 | // The __tls_array is the offset of the ThreadLocalStoragePointer field |
| 14 | // in the TEB block whose base address held in the %fs segment. | 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,6 +9,7 @@ pub const AutoArrayHashMapUnmanaged = array_hash_map.AutoArrayHashMapUnmanaged; |
| 9 | pub const AutoHashMap = hash_map.AutoHashMap; | 9 | pub const AutoHashMap = hash_map.AutoHashMap; |
| 10 | pub const AutoHashMapUnmanaged = hash_map.AutoHashMapUnmanaged; | 10 | pub const AutoHashMapUnmanaged = hash_map.AutoHashMapUnmanaged; |
| 11 | pub const BoundedArray = @import("bounded_array.zig").BoundedArray; | 11 | pub const BoundedArray = @import("bounded_array.zig").BoundedArray; |
| 12 | pub const Build = @import("Build.zig"); | ||
| 12 | pub const BufMap = @import("buf_map.zig").BufMap; | 13 | pub const BufMap = @import("buf_map.zig").BufMap; |
| 13 | pub const BufSet = @import("buf_set.zig").BufSet; | 14 | pub const BufSet = @import("buf_set.zig").BufSet; |
| 14 | pub const ChildProcess = @import("child_process.zig").ChildProcess; | 15 | pub const ChildProcess = @import("child_process.zig").ChildProcess; |
| ... | @@ -49,7 +50,6 @@ pub const array_hash_map = @import("array_hash_map.zig"); | ... | @@ -49,7 +50,6 @@ pub const array_hash_map = @import("array_hash_map.zig"); |
| 49 | pub const atomic = @import("atomic.zig"); | 50 | pub const atomic = @import("atomic.zig"); |
| 50 | pub const base64 = @import("base64.zig"); | 51 | pub const base64 = @import("base64.zig"); |
| 51 | pub const bit_set = @import("bit_set.zig"); | 52 | pub const bit_set = @import("bit_set.zig"); |
| 52 | pub const build = @import("build.zig"); | ||
| 53 | pub const builtin = @import("builtin.zig"); | 53 | pub const builtin = @import("builtin.zig"); |
| 54 | pub const c = @import("c.zig"); | 54 | pub const c = @import("c.zig"); |
| 55 | pub const coff = @import("coff.zig"); | 55 | pub const coff = @import("coff.zig"); |
| ... | @@ -96,6 +96,9 @@ pub const wasm = @import("wasm.zig"); | ... | @@ -96,6 +96,9 @@ pub const wasm = @import("wasm.zig"); |
| 96 | pub const zig = @import("zig.zig"); | 96 | pub const zig = @import("zig.zig"); |
| 97 | pub const start = @import("start.zig"); | 97 | pub const start = @import("start.zig"); |
| 98 | 98 | ||
| 99 | /// deprecated: use `Build`. | ||
| 100 | pub const build = Build; | ||
| 101 | |||
| 99 | const root = @import("root"); | 102 | const root = @import("root"); |
| 100 | const options_override = if (@hasDecl(root, "std_options")) root.std_options else struct {}; | 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,6 +153,11 @@ pub const options = struct { |
| 150 | else | 153 | else |
| 151 | log.defaultLog; | 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 | pub const cryptoRandomSeed: fn (buffer: []u8) void = if (@hasDecl(options_override, "cryptoRandomSeed")) | 161 | pub const cryptoRandomSeed: fn (buffer: []u8) void = if (@hasDecl(options_override, "cryptoRandomSeed")) |
| 154 | options_override.cryptoRandomSeed | 162 | options_override.cryptoRandomSeed |
| 155 | else | 163 | else |
lib/std/tar.zig+23| ... | @@ -1,6 +1,18 @@ | ... | @@ -1,6 +1,18 @@ |
| 1 | pub const Options = struct { | 1 | pub const Options = struct { |
| 2 | /// Number of directory levels to skip when extracting files. | 2 | /// Number of directory levels to skip when extracting files. |
| 3 | strip_components: u32 = 0, | 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 | pub const Header = struct { | 18 | pub const Header = struct { |
| ... | @@ -72,6 +84,17 @@ pub const Header = struct { | ... | @@ -72,6 +84,17 @@ pub const Header = struct { |
| 72 | }; | 84 | }; |
| 73 | 85 | ||
| 74 | pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: Options) !void { | 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 | var file_name_buffer: [255]u8 = undefined; | 98 | var file_name_buffer: [255]u8 = undefined; |
| 76 | var buffer: [512 * 8]u8 = undefined; | 99 | var buffer: [512 * 8]u8 = undefined; |
| 77 | var start: usize = 0; | 100 | var start: usize = 0; |
lib/std/target.zig+556-4| ... | @@ -702,9 +702,6 @@ pub const Target = struct { | ... | @@ -702,9 +702,6 @@ pub const Target = struct { |
| 702 | pub const ShiftInt = std.math.Log2Int(usize); | 702 | pub const ShiftInt = std.math.Log2Int(usize); |
| 703 | 703 | ||
| 704 | pub const empty = Set{ .ints = [1]usize{0} ** usize_count }; | 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 | pub fn isEmpty(set: Set) bool { | 706 | pub fn isEmpty(set: Set) bool { |
| 710 | return for (set.ints) |x| { | 707 | return for (set.ints) |x| { |
| ... | @@ -787,7 +784,7 @@ pub const Target = struct { | ... | @@ -787,7 +784,7 @@ pub const Target = struct { |
| 787 | return struct { | 784 | return struct { |
| 788 | /// Populates only the feature bits specified. | 785 | /// Populates only the feature bits specified. |
| 789 | pub fn featureSet(features: []const F) Set { | 786 | pub fn featureSet(features: []const F) Set { |
| 790 | var x = Set.empty_workaround(); // TODO remove empty_workaround | 787 | var x = Set.empty; |
| 791 | for (features) |feature| { | 788 | for (features) |feature| { |
| 792 | x.addFeature(@enumToInt(feature)); | 789 | x.addFeature(@enumToInt(feature)); |
| 793 | } | 790 | } |
| ... | @@ -1907,6 +1904,561 @@ pub const Target = struct { | ... | @@ -1907,6 +1904,561 @@ pub const Target = struct { |
| 1907 | => 16, | 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 | test { | 2464 | test { |
lib/std/testing.zig+246| ... | @@ -670,6 +670,252 @@ pub fn expectStringEndsWith(actual: []const u8, expected_ends_with: []const u8) | ... | @@ -670,6 +670,252 @@ pub fn expectStringEndsWith(actual: []const u8, expected_ends_with: []const u8) |
| 670 | return error.TestExpectedEndsWith; | 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 | fn printIndicatorLine(source: []const u8, indicator_index: usize) void { | 919 | fn printIndicatorLine(source: []const u8, indicator_index: usize) void { |
| 674 | const line_begin_index = if (std.mem.lastIndexOfScalar(u8, source[0..indicator_index], '\n')) |line_begin| | 920 | const line_begin_index = if (std.mem.lastIndexOfScalar(u8, source[0..indicator_index], '\n')) |line_begin| |
| 675 | line_begin + 1 | 921 | line_begin + 1 |
lib/std/zig.zig-1| ... | @@ -8,7 +8,6 @@ pub const Tokenizer = tokenizer.Tokenizer; | ... | @@ -8,7 +8,6 @@ pub const Tokenizer = tokenizer.Tokenizer; |
| 8 | pub const fmtId = fmt.fmtId; | 8 | pub const fmtId = fmt.fmtId; |
| 9 | pub const fmtEscapes = fmt.fmtEscapes; | 9 | pub const fmtEscapes = fmt.fmtEscapes; |
| 10 | pub const isValidId = fmt.isValidId; | 10 | pub const isValidId = fmt.isValidId; |
| 11 | pub const parse = @import("zig/parse.zig").parse; | ||
| 12 | pub const string_literal = @import("zig/string_literal.zig"); | 11 | pub const string_literal = @import("zig/string_literal.zig"); |
| 13 | pub const number_literal = @import("zig/number_literal.zig"); | 12 | pub const number_literal = @import("zig/number_literal.zig"); |
| 14 | pub const primitives = @import("zig/primitives.zig"); | 13 | pub const primitives = @import("zig/primitives.zig"); |
lib/std/zig/Ast.zig+73-9| ... | @@ -1,4 +1,8 @@ | ... | @@ -1,4 +1,8 @@ |
| 1 | //! Abstract Syntax Tree for Zig source code. | 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 | /// Reference to externally-owned data. | 7 | /// Reference to externally-owned data. |
| 4 | source: [:0]const u8, | 8 | source: [:0]const u8, |
| ... | @@ -11,13 +15,6 @@ extra_data: []Node.Index, | ... | @@ -11,13 +15,6 @@ extra_data: []Node.Index, |
| 11 | 15 | ||
| 12 | errors: []const Error, | 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 | pub const TokenIndex = u32; | 18 | pub const TokenIndex = u32; |
| 22 | pub const ByteOffset = u32; | 19 | pub const ByteOffset = u32; |
| 23 | 20 | ||
| ... | @@ -34,7 +31,7 @@ pub const Location = struct { | ... | @@ -34,7 +31,7 @@ pub const Location = struct { |
| 34 | line_end: usize, | 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 | tree.tokens.deinit(gpa); | 35 | tree.tokens.deinit(gpa); |
| 39 | tree.nodes.deinit(gpa); | 36 | tree.nodes.deinit(gpa); |
| 40 | gpa.free(tree.extra_data); | 37 | gpa.free(tree.extra_data); |
| ... | @@ -48,11 +45,69 @@ pub const RenderError = error{ | ... | @@ -48,11 +45,69 @@ pub const RenderError = error{ |
| 48 | OutOfMemory, | 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 | /// `gpa` is used for allocating the resulting formatted source code, as well as | 106 | /// `gpa` is used for allocating the resulting formatted source code, as well as |
| 52 | /// for allocating extra stack memory if needed, because this function utilizes recursion. | 107 | /// for allocating extra stack memory if needed, because this function utilizes recursion. |
| 53 | /// Note: that's not actually true yet, see https://github.com/ziglang/zig/issues/1006. | 108 | /// Note: that's not actually true yet, see https://github.com/ziglang/zig/issues/1006. |
| 54 | /// Caller owns the returned slice of bytes, allocated with `gpa`. | 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 | var buffer = std.ArrayList(u8).init(gpa); | 111 | var buffer = std.ArrayList(u8).init(gpa); |
| 57 | defer buffer.deinit(); | 112 | defer buffer.deinit(); |
| 58 | 113 | ||
| ... | @@ -3347,3 +3402,12 @@ pub const Node = struct { | ... | @@ -3347,3 +3402,12 @@ pub const Node = struct { |
| 3347 | rparen: TokenIndex, | 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,7 +75,7 @@ fn castPtr(comptime DestType: type, target: anytype) DestType { |
| 75 | const source = ptrInfo(@TypeOf(target)); | 75 | const source = ptrInfo(@TypeOf(target)); |
| 76 | 76 | ||
| 77 | if (source.is_const and !dest.is_const or source.is_volatile and !dest.is_volatile) | 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 | else if (@typeInfo(dest.child) == .Opaque) | 79 | else if (@typeInfo(dest.child) == .Opaque) |
| 80 | // dest.alignment would error out | 80 | // dest.alignment would error out |
| 81 | return @ptrCast(DestType, target) | 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,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 | test "zig fmt: file ends in comment after var decl" { | 198 | test "zig fmt: file ends in comment after var decl" { |
| 190 | try testTransform( | 199 | try testTransform( |
| 191 | \\const x = 42; | 200 | \\const x = 42; |
| ... | @@ -6064,7 +6073,7 @@ var fixed_buffer_mem: [100 * 1024]u8 = undefined; | ... | @@ -6064,7 +6073,7 @@ var fixed_buffer_mem: [100 * 1024]u8 = undefined; |
| 6064 | fn testParse(source: [:0]const u8, allocator: mem.Allocator, anything_changed: *bool) ![]u8 { | 6073 | fn testParse(source: [:0]const u8, allocator: mem.Allocator, anything_changed: *bool) ![]u8 { |
| 6065 | const stderr = io.getStdErr().writer(); | 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 | defer tree.deinit(allocator); | 6077 | defer tree.deinit(allocator); |
| 6069 | 6078 | ||
| 6070 | for (tree.errors) |parse_error| { | 6079 | for (tree.errors) |parse_error| { |
| ... | @@ -6115,7 +6124,7 @@ fn testCanonical(source: [:0]const u8) !void { | ... | @@ -6115,7 +6124,7 @@ fn testCanonical(source: [:0]const u8) !void { |
| 6115 | const Error = std.zig.Ast.Error.Tag; | 6124 | const Error = std.zig.Ast.Error.Tag; |
| 6116 | 6125 | ||
| 6117 | fn testError(source: [:0]const u8, expected_errors: []const Error) !void { | 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 | defer tree.deinit(std.testing.allocator); | 6128 | defer tree.deinit(std.testing.allocator); |
| 6120 | 6129 | ||
| 6121 | std.testing.expectEqual(expected_errors.len, tree.errors.len) catch |err| { | 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,7 +1,6 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const mem = std.mem; | 2 | const mem = std.mem; |
| 3 | const Tokenizer = std.zig.Tokenizer; | 3 | const Tokenizer = std.zig.Tokenizer; |
| 4 | const Parser = std.zig.Parser; | ||
| 5 | const io = std.io; | 4 | const io = std.io; |
| 6 | const fmtIntSizeBin = std.fmt.fmtIntSizeBin; | 5 | const fmtIntSizeBin = std.fmt.fmtIntSizeBin; |
| 7 | 6 | ||
| ... | @@ -34,6 +33,6 @@ pub fn main() !void { | ... | @@ -34,6 +33,6 @@ pub fn main() !void { |
| 34 | fn testOnce() usize { | 33 | fn testOnce() usize { |
| 35 | var fixed_buf_alloc = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]); | 34 | var fixed_buf_alloc = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]); |
| 36 | var allocator = fixed_buf_alloc.allocator(); | 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 | return fixed_buf_alloc.end_index; | 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,8 +2759,7 @@ fn tokenSliceForRender(tree: Ast, token_index: Ast.TokenIndex) []const u8 { |
| 2759 | var ret = tree.tokenSlice(token_index); | 2759 | var ret = tree.tokenSlice(token_index); |
| 2760 | switch (tree.tokens.items(.tag)[token_index]) { | 2760 | switch (tree.tokens.items(.tag)[token_index]) { |
| 2761 | .multiline_string_literal_line => { | 2761 | .multiline_string_literal_line => { |
| 2762 | assert(ret[ret.len - 1] == '\n'); | 2762 | if (ret[ret.len - 1] == '\n') ret.len -= 1; |
| 2763 | ret.len -= 1; | ||
| 2764 | }, | 2763 | }, |
| 2765 | .container_doc_comment, .doc_comment => { | 2764 | .container_doc_comment, .doc_comment => { |
| 2766 | ret = mem.trimRight(u8, ret, &std.ascii.whitespace); | 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,7 +11,7 @@ var log_err_count: usize = 0; |
| 11 | 11 | ||
| 12 | pub fn main() void { | 12 | pub fn main() void { |
| 13 | if (builtin.zig_backend != .stage1 and | 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 | builtin.zig_backend != .stage2_c) | 15 | builtin.zig_backend != .stage2_c) |
| 16 | { | 16 | { |
| 17 | return main2() catch @panic("test failure"); | 17 | return main2() catch @panic("test failure"); |
lib/zig.h+24| ... | @@ -93,6 +93,14 @@ typedef char bool; | ... | @@ -93,6 +93,14 @@ typedef char bool; |
| 93 | #define zig_align zig_align_unavailable | 93 | #define zig_align zig_align_unavailable |
| 94 | #endif | 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 | #if zig_has_attribute(aligned) | 104 | #if zig_has_attribute(aligned) |
| 97 | #define zig_align_fn(alignment) __attribute__((aligned(alignment))) | 105 | #define zig_align_fn(alignment) __attribute__((aligned(alignment))) |
| 98 | #elif _MSC_VER | 106 | #elif _MSC_VER |
| ... | @@ -101,6 +109,22 @@ typedef char bool; | ... | @@ -101,6 +109,22 @@ typedef char bool; |
| 101 | #define zig_align_fn zig_align_fn_unavailable | 109 | #define zig_align_fn zig_align_fn_unavailable |
| 102 | #endif | 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 | #if zig_has_builtin(unreachable) || defined(zig_gnuc) | 128 | #if zig_has_builtin(unreachable) || defined(zig_gnuc) |
| 105 | #define zig_unreachable() __builtin_unreachable() | 129 | #define zig_unreachable() __builtin_unreachable() |
| 106 | #else | 130 | #else |
src/AstGen.zig+37-2| ... | @@ -2530,6 +2530,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As | ... | @@ -2530,6 +2530,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As |
| 2530 | .bit_size_of, | 2530 | .bit_size_of, |
| 2531 | .typeof_log2_int_type, | 2531 | .typeof_log2_int_type, |
| 2532 | .ptr_to_int, | 2532 | .ptr_to_int, |
| 2533 | .qual_cast, | ||
| 2533 | .align_of, | 2534 | .align_of, |
| 2534 | .bool_to_int, | 2535 | .bool_to_int, |
| 2535 | .embed_file, | 2536 | .embed_file, |
| ... | @@ -4278,7 +4279,34 @@ fn testDecl( | ... | @@ -4278,7 +4279,34 @@ fn testDecl( |
| 4278 | var num_namespaces_out: u32 = 0; | 4279 | var num_namespaces_out: u32 = 0; |
| 4279 | var capturing_namespace: ?*Scope.Namespace = null; | 4280 | var capturing_namespace: ?*Scope.Namespace = null; |
| 4280 | while (true) switch (s.tag) { | 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 | .gen_zir => s = s.cast(GenZir).?.parent, | 4310 | .gen_zir => s = s.cast(GenZir).?.parent, |
| 4283 | .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent, | 4311 | .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent, |
| 4284 | .namespace, .enum_namespace => { | 4312 | .namespace, .enum_namespace => { |
| ... | @@ -8010,6 +8038,7 @@ fn builtinCall( | ... | @@ -8010,6 +8038,7 @@ fn builtinCall( |
| 8010 | .float_cast => return typeCast(gz, scope, ri, node, params[0], params[1], .float_cast), | 8038 | .float_cast => return typeCast(gz, scope, ri, node, params[0], params[1], .float_cast), |
| 8011 | .int_cast => return typeCast(gz, scope, ri, node, params[0], params[1], .int_cast), | 8039 | .int_cast => return typeCast(gz, scope, ri, node, params[0], params[1], .int_cast), |
| 8012 | .ptr_cast => return typeCast(gz, scope, ri, node, params[0], params[1], .ptr_cast), | 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 | .truncate => return typeCast(gz, scope, ri, node, params[0], params[1], .truncate), | 8042 | .truncate => return typeCast(gz, scope, ri, node, params[0], params[1], .truncate), |
| 8014 | // zig fmt: on | 8043 | // zig fmt: on |
| 8015 | 8044 | ||
| ... | @@ -8692,6 +8721,7 @@ fn callExpr( | ... | @@ -8692,6 +8721,7 @@ fn callExpr( |
| 8692 | defer arg_block.unstack(); | 8721 | defer arg_block.unstack(); |
| 8693 | 8722 | ||
| 8694 | // `call_inst` is reused to provide the param type. | 8723 | // `call_inst` is reused to provide the param type. |
| 8724 | arg_block.rl_ty_inst = call_inst; | ||
| 8695 | const arg_ref = try expr(&arg_block, &arg_block.base, .{ .rl = .{ .coerced_ty = call_inst }, .ctx = .fn_arg }, param_node); | 8725 | const arg_ref = try expr(&arg_block, &arg_block.base, .{ .rl = .{ .coerced_ty = call_inst }, .ctx = .fn_arg }, param_node); |
| 8696 | _ = try arg_block.addBreak(.break_inline, call_index, arg_ref); | 8726 | _ = try arg_block.addBreak(.break_inline, call_index, arg_ref); |
| 8697 | 8727 | ||
| ... | @@ -10840,7 +10870,12 @@ const GenZir = struct { | ... | @@ -10840,7 +10870,12 @@ const GenZir = struct { |
| 10840 | // we emit ZIR for the block break instructions to have the result values, | 10870 | // we emit ZIR for the block break instructions to have the result values, |
| 10841 | // and then rvalue() on that to pass the value to the result location. | 10871 | // and then rvalue() on that to pass the value to the result location. |
| 10842 | switch (parent_ri.rl) { | 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 | gz.rl_ty_inst = ty_inst; | 10879 | gz.rl_ty_inst = ty_inst; |
| 10845 | gz.break_result_info = parent_ri; | 10880 | gz.break_result_info = parent_ri; |
| 10846 | }, | 10881 | }, |
src/Autodoc.zig+6-11| ... | @@ -1400,6 +1400,7 @@ fn walkInstruction( | ... | @@ -1400,6 +1400,7 @@ fn walkInstruction( |
| 1400 | .float_cast, | 1400 | .float_cast, |
| 1401 | .int_cast, | 1401 | .int_cast, |
| 1402 | .ptr_cast, | 1402 | .ptr_cast, |
| 1403 | .qual_cast, | ||
| 1403 | .truncate, | 1404 | .truncate, |
| 1404 | .align_cast, | 1405 | .align_cast, |
| 1405 | .has_decl, | 1406 | .has_decl, |
| ... | @@ -2200,17 +2201,10 @@ fn walkInstruction( | ... | @@ -2200,17 +2201,10 @@ fn walkInstruction( |
| 2200 | false, | 2201 | false, |
| 2201 | ); | 2202 | ); |
| 2202 | 2203 | ||
| 2203 | _ = operand; | 2204 | return DocData.WalkResult{ |
| 2204 | 2205 | .typeRef = operand.expr, | |
| 2205 | // WIP | 2206 | .expr = .{ .@"struct" = &.{} }, |
| 2206 | 2207 | }; | |
| 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])); | ||
| 2214 | }, | 2208 | }, |
| 2215 | .struct_init_anon => { | 2209 | .struct_init_anon => { |
| 2216 | const pl_node = data[inst_index].pl_node; | 2210 | const pl_node = data[inst_index].pl_node; |
| ... | @@ -2537,6 +2531,7 @@ fn walkInstruction( | ... | @@ -2537,6 +2531,7 @@ fn walkInstruction( |
| 2537 | const var_init_ref = @intToEnum(Ref, file.zir.extra[extra_index]); | 2531 | const var_init_ref = @intToEnum(Ref, file.zir.extra[extra_index]); |
| 2538 | const var_init = try self.walkRef(file, parent_scope, parent_src, var_init_ref, need_type); | 2532 | const var_init = try self.walkRef(file, parent_scope, parent_src, var_init_ref, need_type); |
| 2539 | value.expr = var_init.expr; | 2533 | value.expr = var_init.expr; |
| 2534 | value.typeRef = var_init.typeRef; | ||
| 2540 | } | 2535 | } |
| 2541 | 2536 | ||
| 2542 | return value; | 2537 | return value; |
src/BuiltinFn.zig+8| ... | @@ -75,6 +75,7 @@ pub const Tag = enum { | ... | @@ -75,6 +75,7 @@ pub const Tag = enum { |
| 75 | prefetch, | 75 | prefetch, |
| 76 | ptr_cast, | 76 | ptr_cast, |
| 77 | ptr_to_int, | 77 | ptr_to_int, |
| 78 | qual_cast, | ||
| 78 | rem, | 79 | rem, |
| 79 | return_address, | 80 | return_address, |
| 80 | select, | 81 | select, |
| ... | @@ -674,6 +675,13 @@ pub const list = list: { | ... | @@ -674,6 +675,13 @@ pub const list = list: { |
| 674 | .param_count = 1, | 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 | "@rem", | 686 | "@rem", |
| 679 | .{ | 687 | .{ |
src/Compilation.zig+2-2| ... | @@ -385,7 +385,7 @@ pub const AllErrors = struct { | ... | @@ -385,7 +385,7 @@ pub const AllErrors = struct { |
| 385 | count: u32 = 1, | 385 | count: u32 = 1, |
| 386 | /// Does not include the trailing newline. | 386 | /// Does not include the trailing newline. |
| 387 | source_line: ?[]const u8, | 387 | source_line: ?[]const u8, |
| 388 | notes: []Message = &.{}, | 388 | notes: []const Message = &.{}, |
| 389 | reference_trace: []Message = &.{}, | 389 | reference_trace: []Message = &.{}, |
| 390 | 390 | ||
| 391 | /// Splits the error message up into lines to properly indent them | 391 | /// Splits the error message up into lines to properly indent them |
| ... | @@ -3299,7 +3299,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { | ... | @@ -3299,7 +3299,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void { |
| 3299 | const gpa = comp.gpa; | 3299 | const gpa = comp.gpa; |
| 3300 | const module = comp.bin_file.options.module.?; | 3300 | const module = comp.bin_file.options.module.?; |
| 3301 | const decl = module.declPtr(decl_index); | 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 | try module.failed_decls.ensureUnusedCapacity(gpa, 1); | 3303 | try module.failed_decls.ensureUnusedCapacity(gpa, 1); |
| 3304 | module.failed_decls.putAssumeCapacityNoClobber(decl_index, try Module.ErrorMsg.create( | 3304 | module.failed_decls.putAssumeCapacityNoClobber(decl_index, try Module.ErrorMsg.create( |
| 3305 | gpa, | 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,8 +328,6 @@ pub const ErrorInt = u32; |
| 328 | pub const Export = struct { | 328 | pub const Export = struct { |
| 329 | options: std.builtin.ExportOptions, | 329 | options: std.builtin.ExportOptions, |
| 330 | src: LazySrcLoc, | 330 | src: LazySrcLoc, |
| 331 | /// Represents the position of the export, if any, in the output file. | ||
| 332 | link: link.File.Export, | ||
| 333 | /// The Decl that performs the export. Note that this is *not* the Decl being exported. | 331 | /// The Decl that performs the export. Note that this is *not* the Decl being exported. |
| 334 | owner_decl: Decl.Index, | 332 | owner_decl: Decl.Index, |
| 335 | /// The Decl containing the export statement. Inline function calls | 333 | /// The Decl containing the export statement. Inline function calls |
| ... | @@ -533,16 +531,8 @@ pub const Decl = struct { | ... | @@ -533,16 +531,8 @@ pub const Decl = struct { |
| 533 | /// What kind of a declaration is this. | 531 | /// What kind of a declaration is this. |
| 534 | kind: Kind, | 532 | kind: Kind, |
| 535 | 533 | ||
| 536 | /// Represents the position of the code in the output file. | 534 | /// TODO remove this once Wasm backend catches up |
| 537 | /// This is populated regardless of semantic analysis and code generation. | 535 | fn_link: ?link.File.Wasm.FnData = null, |
| 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, | ||
| 546 | 536 | ||
| 547 | /// The shallow set of other decls whose typed_value could possibly change if this Decl's | 537 | /// The shallow set of other decls whose typed_value could possibly change if this Decl's |
| 548 | /// typed_value is modified. | 538 | /// typed_value is modified. |
| ... | @@ -2067,7 +2057,7 @@ pub const File = struct { | ... | @@ -2067,7 +2057,7 @@ pub const File = struct { |
| 2067 | if (file.tree_loaded) return &file.tree; | 2057 | if (file.tree_loaded) return &file.tree; |
| 2068 | 2058 | ||
| 2069 | const source = try file.getSource(gpa); | 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 | file.tree_loaded = true; | 2061 | file.tree_loaded = true; |
| 2072 | return &file.tree; | 2062 | return &file.tree; |
| 2073 | } | 2063 | } |
| ... | @@ -3672,7 +3662,7 @@ pub fn astGenFile(mod: *Module, file: *File) !void { | ... | @@ -3672,7 +3662,7 @@ pub fn astGenFile(mod: *Module, file: *File) !void { |
| 3672 | file.source = source; | 3662 | file.source = source; |
| 3673 | file.source_loaded = true; | 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 | defer if (!file.tree_loaded) file.tree.deinit(gpa); | 3666 | defer if (!file.tree_loaded) file.tree.deinit(gpa); |
| 3677 | 3667 | ||
| 3678 | if (file.tree.errors.len != 0) { | 3668 | if (file.tree.errors.len != 0) { |
| ... | @@ -3987,7 +3977,7 @@ pub fn populateBuiltinFile(mod: *Module) !void { | ... | @@ -3987,7 +3977,7 @@ pub fn populateBuiltinFile(mod: *Module) !void { |
| 3987 | else => |e| return e, | 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 | file.tree_loaded = true; | 3981 | file.tree_loaded = true; |
| 3992 | assert(file.tree.errors.len == 0); // builtin.zig must parse | 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,7 +4088,7 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl_index: Decl.Index) SemaError!void { |
| 4098 | 4088 | ||
| 4099 | // The exports this Decl performs will be re-discovered, so we remove them here | 4089 | // The exports this Decl performs will be re-discovered, so we remove them here |
| 4100 | // prior to re-analysis. | 4090 | // prior to re-analysis. |
| 4101 | mod.deleteDeclExports(decl_index); | 4091 | try mod.deleteDeclExports(decl_index); |
| 4102 | 4092 | ||
| 4103 | // Similarly, `@setAlignStack` invocations will be re-discovered. | 4093 | // Similarly, `@setAlignStack` invocations will be re-discovered. |
| 4104 | if (decl.getFunction()) |func| { | 4094 | if (decl.getFunction()) |func| { |
| ... | @@ -4585,7 +4575,6 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool { | ... | @@ -4585,7 +4575,6 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool { |
| 4585 | // We don't fully codegen the decl until later, but we do need to reserve a global | 4575 | // We don't fully codegen the decl until later, but we do need to reserve a global |
| 4586 | // offset table index for it. This allows us to codegen decls out of dependency | 4576 | // offset table index for it. This allows us to codegen decls out of dependency |
| 4587 | // order, increasing how many computations can be done in parallel. | 4577 | // order, increasing how many computations can be done in parallel. |
| 4588 | try mod.comp.bin_file.allocateDeclIndexes(decl_index); | ||
| 4589 | try mod.comp.work_queue.writeItem(.{ .codegen_func = func }); | 4578 | try mod.comp.work_queue.writeItem(.{ .codegen_func = func }); |
| 4590 | if (type_changed and mod.emit_h != null) { | 4579 | if (type_changed and mod.emit_h != null) { |
| 4591 | try mod.comp.work_queue.writeItem(.{ .emit_h_decl = decl_index }); | 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,7 +4686,6 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool { |
| 4697 | // codegen backend wants full access to the Decl Type. | 4686 | // codegen backend wants full access to the Decl Type. |
| 4698 | try sema.resolveTypeFully(decl.ty); | 4687 | try sema.resolveTypeFully(decl.ty); |
| 4699 | 4688 | ||
| 4700 | try mod.comp.bin_file.allocateDeclIndexes(decl_index); | ||
| 4701 | try mod.comp.work_queue.writeItem(.{ .codegen_decl = decl_index }); | 4689 | try mod.comp.work_queue.writeItem(.{ .codegen_decl = decl_index }); |
| 4702 | 4690 | ||
| 4703 | if (type_changed and mod.emit_h != null) { | 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,20 +5173,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) Allocator.Err |
| 5185 | decl.zir_decl_index = @intCast(u32, decl_sub_index); | 5173 | decl.zir_decl_index = @intCast(u32, decl_sub_index); |
| 5186 | if (decl.getFunction()) |_| { | 5174 | if (decl.getFunction()) |_| { |
| 5187 | switch (comp.bin_file.tag) { | 5175 | switch (comp.bin_file.tag) { |
| 5188 | .coff => { | 5176 | .coff, .elf, .macho, .plan9 => { |
| 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 => { | ||
| 5202 | // TODO Look into detecting when this would be unnecessary by storing enough state | 5177 | // TODO Look into detecting when this would be unnecessary by storing enough state |
| 5203 | // in `Decl` to notice that the line number did not change. | 5178 | // in `Decl` to notice that the line number did not change. |
| 5204 | comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl_index }); | 5179 | comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl_index }); |
| ... | @@ -5267,33 +5242,15 @@ pub fn clearDecl( | ... | @@ -5267,33 +5242,15 @@ pub fn clearDecl( |
| 5267 | assert(emit_h.decl_table.swapRemove(decl_index)); | 5242 | assert(emit_h.decl_table.swapRemove(decl_index)); |
| 5268 | } | 5243 | } |
| 5269 | _ = mod.compile_log_decls.swapRemove(decl_index); | 5244 | _ = mod.compile_log_decls.swapRemove(decl_index); |
| 5270 | mod.deleteDeclExports(decl_index); | 5245 | try mod.deleteDeclExports(decl_index); |
| 5271 | 5246 | ||
| 5272 | if (decl.has_tv) { | 5247 | if (decl.has_tv) { |
| 5273 | if (decl.ty.isFnOrHasRuntimeBits()) { | 5248 | if (decl.ty.isFnOrHasRuntimeBits()) { |
| 5274 | mod.comp.bin_file.freeDecl(decl_index); | 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 | decl.fn_link = switch (mod.comp.bin_file.tag) { | 5251 | decl.fn_link = switch (mod.comp.bin_file.tag) { |
| 5289 | .coff => .{ .coff = {} }, | 5252 | .wasm => link.File.Wasm.FnData.empty, |
| 5290 | .elf => .{ .elf = link.File.Dwarf.SrcFn.empty }, | 5253 | else => null, |
| 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 = {} }, | ||
| 5297 | }; | 5254 | }; |
| 5298 | } | 5255 | } |
| 5299 | if (decl.getInnerNamespace()) |namespace| { | 5256 | if (decl.getInnerNamespace()) |namespace| { |
| ... | @@ -5315,23 +5272,6 @@ pub fn deleteUnusedDecl(mod: *Module, decl_index: Decl.Index) void { | ... | @@ -5315,23 +5272,6 @@ pub fn deleteUnusedDecl(mod: *Module, decl_index: Decl.Index) void { |
| 5315 | const decl = mod.declPtr(decl_index); | 5272 | const decl = mod.declPtr(decl_index); |
| 5316 | log.debug("deleteUnusedDecl {d} ({s})", .{ decl_index, decl.name }); | 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 | assert(!mod.declIsRoot(decl_index)); | 5275 | assert(!mod.declIsRoot(decl_index)); |
| 5336 | assert(decl.src_namespace.anon_decls.swapRemove(decl_index)); | 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,7 +5317,7 @@ pub fn abortAnonDecl(mod: *Module, decl_index: Decl.Index) void { |
| 5377 | 5317 | ||
| 5378 | /// Delete all the Export objects that are caused by this Decl. Re-analysis of | 5318 | /// Delete all the Export objects that are caused by this Decl. Re-analysis of |
| 5379 | /// this Decl will cause them to be re-created (or not). | 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 | var export_owners = (mod.export_owners.fetchSwapRemove(decl_index) orelse return).value; | 5321 | var export_owners = (mod.export_owners.fetchSwapRemove(decl_index) orelse return).value; |
| 5382 | 5322 | ||
| 5383 | for (export_owners.items) |exp| { | 5323 | for (export_owners.items) |exp| { |
| ... | @@ -5400,16 +5340,16 @@ fn deleteDeclExports(mod: *Module, decl_index: Decl.Index) void { | ... | @@ -5400,16 +5340,16 @@ fn deleteDeclExports(mod: *Module, decl_index: Decl.Index) void { |
| 5400 | } | 5340 | } |
| 5401 | } | 5341 | } |
| 5402 | if (mod.comp.bin_file.cast(link.File.Elf)) |elf| { | 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 | if (mod.comp.bin_file.cast(link.File.MachO)) |macho| { | 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 | if (mod.comp.bin_file.cast(link.File.Wasm)) |wasm| { | 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 | if (mod.comp.bin_file.cast(link.File.Coff)) |coff| { | 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 | if (mod.failed_exports.fetchSwapRemove(exp)) |failed_kv| { | 5354 | if (mod.failed_exports.fetchSwapRemove(exp)) |failed_kv| { |
| 5415 | failed_kv.value.destroy(mod.gpa); | 5355 | failed_kv.value.destroy(mod.gpa); |
| ... | @@ -5712,25 +5652,9 @@ pub fn allocateNewDecl( | ... | @@ -5712,25 +5652,9 @@ pub fn allocateNewDecl( |
| 5712 | .deletion_flag = false, | 5652 | .deletion_flag = false, |
| 5713 | .zir_decl_index = 0, | 5653 | .zir_decl_index = 0, |
| 5714 | .src_scope = src_scope, | 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 | .fn_link = switch (mod.comp.bin_file.tag) { | 5655 | .fn_link = switch (mod.comp.bin_file.tag) { |
| 5726 | .coff => .{ .coff = {} }, | 5656 | .wasm => link.File.Wasm.FnData.empty, |
| 5727 | .elf => .{ .elf = link.File.Dwarf.SrcFn.empty }, | 5657 | else => null, |
| 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 = {} }, | ||
| 5734 | }, | 5658 | }, |
| 5735 | .generation = 0, | 5659 | .generation = 0, |
| 5736 | .is_pub = false, | 5660 | .is_pub = false, |
| ... | @@ -5816,7 +5740,6 @@ pub fn initNewAnonDecl( | ... | @@ -5816,7 +5740,6 @@ pub fn initNewAnonDecl( |
| 5816 | // the Decl will be garbage collected by the `codegen_decl` task instead of sent | 5740 | // the Decl will be garbage collected by the `codegen_decl` task instead of sent |
| 5817 | // to the linker. | 5741 | // to the linker. |
| 5818 | if (typed_value.ty.isFnOrHasRuntimeBits()) { | 5742 | if (typed_value.ty.isFnOrHasRuntimeBits()) { |
| 5819 | try mod.comp.bin_file.allocateDeclIndexes(new_decl_index); | ||
| 5820 | try mod.comp.anon_work_queue.writeItem(.{ .codegen_decl = new_decl_index }); | 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,12 +1,13 @@ |
| 1 | const Package = @This(); | 1 | const Package = @This(); |
| 2 | 2 | ||
| 3 | const builtin = @import("builtin"); | ||
| 3 | const std = @import("std"); | 4 | const std = @import("std"); |
| 4 | const fs = std.fs; | 5 | const fs = std.fs; |
| 5 | const mem = std.mem; | 6 | const mem = std.mem; |
| 6 | const Allocator = mem.Allocator; | 7 | const Allocator = mem.Allocator; |
| 7 | const assert = std.debug.assert; | 8 | const assert = std.debug.assert; |
| 8 | const Hash = std.crypto.hash.sha2.Sha256; | ||
| 9 | const log = std.log.scoped(.package); | 9 | const log = std.log.scoped(.package); |
| 10 | const main = @import("main.zig"); | ||
| 10 | 11 | ||
| 11 | const Compilation = @import("Compilation.zig"); | 12 | const Compilation = @import("Compilation.zig"); |
| 12 | const Module = @import("Module.zig"); | 13 | const Module = @import("Module.zig"); |
| ... | @@ -14,6 +15,7 @@ const ThreadPool = @import("ThreadPool.zig"); | ... | @@ -14,6 +15,7 @@ const ThreadPool = @import("ThreadPool.zig"); |
| 14 | const WaitGroup = @import("WaitGroup.zig"); | 15 | const WaitGroup = @import("WaitGroup.zig"); |
| 15 | const Cache = @import("Cache.zig"); | 16 | const Cache = @import("Cache.zig"); |
| 16 | const build_options = @import("build_options"); | 17 | const build_options = @import("build_options"); |
| 18 | const Manifest = @import("Manifest.zig"); | ||
| 17 | 19 | ||
| 18 | pub const Table = std.StringHashMapUnmanaged(*Package); | 20 | pub const Table = std.StringHashMapUnmanaged(*Package); |
| 19 | 21 | ||
| ... | @@ -140,10 +142,10 @@ pub fn addAndAdopt(parent: *Package, gpa: Allocator, child: *Package) !void { | ... | @@ -140,10 +142,10 @@ pub fn addAndAdopt(parent: *Package, gpa: Allocator, child: *Package) !void { |
| 140 | } | 142 | } |
| 141 | 143 | ||
| 142 | pub const build_zig_basename = "build.zig"; | 144 | pub const build_zig_basename = "build.zig"; |
| 143 | pub const ini_basename = build_zig_basename ++ ".ini"; | ||
| 144 | 145 | ||
| 145 | pub fn fetchAndAddDependencies( | 146 | pub fn fetchAndAddDependencies( |
| 146 | pkg: *Package, | 147 | pkg: *Package, |
| 148 | arena: Allocator, | ||
| 147 | thread_pool: *ThreadPool, | 149 | thread_pool: *ThreadPool, |
| 148 | http_client: *std.http.Client, | 150 | http_client: *std.http.Client, |
| 149 | directory: Compilation.Directory, | 151 | directory: Compilation.Directory, |
| ... | @@ -152,89 +154,77 @@ pub fn fetchAndAddDependencies( | ... | @@ -152,89 +154,77 @@ pub fn fetchAndAddDependencies( |
| 152 | dependencies_source: *std.ArrayList(u8), | 154 | dependencies_source: *std.ArrayList(u8), |
| 153 | build_roots_source: *std.ArrayList(u8), | 155 | build_roots_source: *std.ArrayList(u8), |
| 154 | name_prefix: []const u8, | 156 | name_prefix: []const u8, |
| 157 | color: main.Color, | ||
| 155 | ) !void { | 158 | ) !void { |
| 156 | const max_bytes = 10 * 1024 * 1024; | 159 | const max_bytes = 10 * 1024 * 1024; |
| 157 | const gpa = thread_pool.allocator; | 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 | error.FileNotFound => { | 169 | error.FileNotFound => { |
| 160 | // Handle the same as no dependencies. | 170 | // Handle the same as no dependencies. |
| 161 | return; | 171 | return; |
| 162 | }, | 172 | }, |
| 163 | else => |e| return e, | 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 }; | 176 | var ast = try std.zig.Ast.parse(gpa, build_zig_zon_bytes, .zon); |
| 168 | var any_error = false; | 177 | defer ast.deinit(gpa); |
| 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 | } | ||
| 196 | 178 | ||
| 197 | const name = opt_name orelse { | 179 | if (ast.errors.len > 0) { |
| 198 | const loc = std.zig.findLineColumn(ini.bytes, @ptrToInt(dep.ptr) - @ptrToInt(ini.bytes.ptr)); | 180 | const file_path = try directory.join(arena, &.{Manifest.basename}); |
| 199 | std.log.err("{s}/{s}:{d}:{d} missing key: 'name'", .{ | 181 | try main.printErrsMsgToStdErr(gpa, arena, ast, file_path, color); |
| 200 | directory.path orelse ".", | 182 | return error.PackageFetchFailed; |
| 201 | "build.zig.ini", | 183 | } |
| 202 | loc.line, | ||
| 203 | loc.column, | ||
| 204 | }); | ||
| 205 | any_error = true; | ||
| 206 | continue; | ||
| 207 | }; | ||
| 208 | 184 | ||
| 209 | const url = opt_url orelse { | 185 | var manifest = try Manifest.parse(gpa, ast); |
| 210 | const loc = std.zig.findLineColumn(ini.bytes, @ptrToInt(dep.ptr) - @ptrToInt(ini.bytes.ptr)); | 186 | defer manifest.deinit(gpa); |
| 211 | std.log.err("{s}/{s}:{d}:{d} missing key: 'name'", .{ | 187 | |
| 212 | directory.path orelse ".", | 188 | if (manifest.errors.len > 0) { |
| 213 | "build.zig.ini", | 189 | const ttyconf: std.debug.TTY.Config = switch (color) { |
| 214 | loc.line, | 190 | .auto => std.debug.detectTTYConfig(std.io.getStdErr()), |
| 215 | loc.column, | 191 | .on => .escape_codes, |
| 216 | }); | 192 | .off => .no_color, |
| 217 | any_error = true; | ||
| 218 | continue; | ||
| 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 }); | 201 | const report: Report = .{ |
| 222 | defer gpa.free(sub_prefix); | 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 | const fqn = sub_prefix[0 .. sub_prefix.len - 1]; | 214 | const fqn = sub_prefix[0 .. sub_prefix.len - 1]; |
| 224 | 215 | ||
| 225 | const sub_pkg = try fetchAndUnpack( | 216 | const sub_pkg = try fetchAndUnpack( |
| 226 | thread_pool, | 217 | thread_pool, |
| 227 | http_client, | 218 | http_client, |
| 228 | global_cache_directory, | 219 | global_cache_directory, |
| 229 | url, | 220 | dep, |
| 230 | expected_hash, | 221 | report, |
| 231 | ini, | ||
| 232 | directory, | ||
| 233 | build_roots_source, | 222 | build_roots_source, |
| 234 | fqn, | 223 | fqn, |
| 235 | ); | 224 | ); |
| 236 | 225 | ||
| 237 | try pkg.fetchAndAddDependencies( | 226 | try pkg.fetchAndAddDependencies( |
| 227 | arena, | ||
| 238 | thread_pool, | 228 | thread_pool, |
| 239 | http_client, | 229 | http_client, |
| 240 | sub_pkg.root_src_directory, | 230 | sub_pkg.root_src_directory, |
| ... | @@ -243,6 +233,7 @@ pub fn fetchAndAddDependencies( | ... | @@ -243,6 +233,7 @@ pub fn fetchAndAddDependencies( |
| 243 | dependencies_source, | 233 | dependencies_source, |
| 244 | build_roots_source, | 234 | build_roots_source, |
| 245 | sub_prefix, | 235 | sub_prefix, |
| 236 | color, | ||
| 246 | ); | 237 | ); |
| 247 | 238 | ||
| 248 | try addAndAdopt(pkg, gpa, sub_pkg); | 239 | try addAndAdopt(pkg, gpa, sub_pkg); |
| ... | @@ -252,7 +243,7 @@ pub fn fetchAndAddDependencies( | ... | @@ -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 | pub fn createFilePkg( | 249 | pub fn createFilePkg( |
| ... | @@ -263,7 +254,7 @@ pub fn createFilePkg( | ... | @@ -263,7 +254,7 @@ pub fn createFilePkg( |
| 263 | contents: []const u8, | 254 | contents: []const u8, |
| 264 | ) !*Package { | 255 | ) !*Package { |
| 265 | const rand_int = std.crypto.random.int(u64); | 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 | var tmp_dir = try cache_directory.handle.makeOpenPath(tmp_dir_sub_path, .{}); | 259 | var tmp_dir = try cache_directory.handle.makeOpenPath(tmp_dir_sub_path, .{}); |
| 269 | defer tmp_dir.close(); | 260 | defer tmp_dir.close(); |
| ... | @@ -281,14 +272,73 @@ pub fn createFilePkg( | ... | @@ -281,14 +272,73 @@ pub fn createFilePkg( |
| 281 | return createWithDir(gpa, name, cache_directory, o_dir_sub_path, basename); | 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 | fn fetchAndUnpack( | 336 | fn fetchAndUnpack( |
| 285 | thread_pool: *ThreadPool, | 337 | thread_pool: *ThreadPool, |
| 286 | http_client: *std.http.Client, | 338 | http_client: *std.http.Client, |
| 287 | global_cache_directory: Compilation.Directory, | 339 | global_cache_directory: Compilation.Directory, |
| 288 | url: []const u8, | 340 | dep: Manifest.Dependency, |
| 289 | expected_hash: ?[]const u8, | 341 | report: Report, |
| 290 | ini: std.Ini, | ||
| 291 | comp_directory: Compilation.Directory, | ||
| 292 | build_roots_source: *std.ArrayList(u8), | 342 | build_roots_source: *std.ArrayList(u8), |
| 293 | fqn: []const u8, | 343 | fqn: []const u8, |
| 294 | ) !*Package { | 344 | ) !*Package { |
| ... | @@ -297,17 +347,9 @@ fn fetchAndUnpack( | ... | @@ -297,17 +347,9 @@ fn fetchAndUnpack( |
| 297 | 347 | ||
| 298 | // Check if the expected_hash is already present in the global package | 348 | // Check if the expected_hash is already present in the global package |
| 299 | // cache, and thereby avoid both fetching and unpacking. | 349 | // cache, and thereby avoid both fetching and unpacking. |
| 300 | if (expected_hash) |h| cached: { | 350 | if (dep.hash) |h| cached: { |
| 301 | if (h.len != 2 * Hash.digest_length) { | 351 | const hex_multihash_len = 2 * Manifest.multihash_len; |
| 302 | return reportError( | 352 | const hex_digest = h[0..hex_multihash_len]; |
| 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]; | ||
| 311 | const pkg_dir_sub_path = "p" ++ s ++ hex_digest; | 353 | const pkg_dir_sub_path = "p" ++ s ++ hex_digest; |
| 312 | var pkg_dir = global_cache_directory.handle.openDir(pkg_dir_sub_path, .{}) catch |err| switch (err) { | 354 | var pkg_dir = global_cache_directory.handle.openDir(pkg_dir_sub_path, .{}) catch |err| switch (err) { |
| 313 | error.FileNotFound => break :cached, | 355 | error.FileNotFound => break :cached, |
| ... | @@ -344,10 +386,10 @@ fn fetchAndUnpack( | ... | @@ -344,10 +386,10 @@ fn fetchAndUnpack( |
| 344 | return ptr; | 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 | const rand_int = std.crypto.random.int(u64); | 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 | const actual_hash = a: { | 394 | const actual_hash = a: { |
| 353 | var tmp_directory: Compilation.Directory = d: { | 395 | var tmp_directory: Compilation.Directory = d: { |
| ... | @@ -376,13 +418,9 @@ fn fetchAndUnpack( | ... | @@ -376,13 +418,9 @@ fn fetchAndUnpack( |
| 376 | // by default, so the same logic applies for buffering the reader as for gzip. | 418 | // by default, so the same logic applies for buffering the reader as for gzip. |
| 377 | try unpackTarball(gpa, &req, tmp_directory.handle, std.compress.xz); | 419 | try unpackTarball(gpa, &req, tmp_directory.handle, std.compress.xz); |
| 378 | } else { | 420 | } else { |
| 379 | return reportError( | 421 | return report.fail(dep.url_tok, "unknown file extension for path '{s}'", .{ |
| 380 | ini, | 422 | uri.path, |
| 381 | comp_directory, | 423 | }); |
| 382 | uri.path.ptr, | ||
| 383 | "unknown file extension for path '{s}'", | ||
| 384 | .{uri.path}, | ||
| 385 | ); | ||
| 386 | } | 424 | } |
| 387 | 425 | ||
| 388 | // TODO: delete files not included in the package prior to computing the package hash. | 426 | // TODO: delete files not included in the package prior to computing the package hash. |
| ... | @@ -393,28 +431,21 @@ fn fetchAndUnpack( | ... | @@ -393,28 +431,21 @@ fn fetchAndUnpack( |
| 393 | break :a try computePackageHash(thread_pool, .{ .dir = tmp_directory.handle }); | 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 | try renameTmpIntoCache(global_cache_directory.handle, tmp_dir_sub_path, pkg_dir_sub_path); | 435 | try renameTmpIntoCache(global_cache_directory.handle, tmp_dir_sub_path, pkg_dir_sub_path); |
| 398 | 436 | ||
| 399 | if (expected_hash) |h| { | 437 | const actual_hex = Manifest.hexDigest(actual_hash); |
| 400 | const actual_hex = hexDigest(actual_hash); | 438 | if (dep.hash) |h| { |
| 401 | if (!mem.eql(u8, h, &actual_hex)) { | 439 | if (!mem.eql(u8, h, &actual_hex)) { |
| 402 | return reportError( | 440 | return report.fail(dep.hash_tok, "hash mismatch: expected: {s}, found: {s}", .{ |
| 403 | ini, | 441 | h, actual_hex, |
| 404 | comp_directory, | 442 | }); |
| 405 | h.ptr, | ||
| 406 | "hash mismatch: expected: {s}, found: {s}", | ||
| 407 | .{ h, actual_hex }, | ||
| 408 | ); | ||
| 409 | } | 443 | } |
| 410 | } else { | 444 | } else { |
| 411 | return reportError( | 445 | const notes: [1]Compilation.AllErrors.Message = .{.{ .plain = .{ |
| 412 | ini, | 446 | .msg = try std.fmt.allocPrint(report.arena, "expected .hash = \"{s}\",", .{&actual_hex}), |
| 413 | comp_directory, | 447 | } }}; |
| 414 | url.ptr, | 448 | return report.failWithNotes(&notes, dep.url_tok, "url field is missing corresponding hash field", .{}); |
| 415 | "url field is missing corresponding hash field: hash={s}", | ||
| 416 | .{std.fmt.fmtSliceHexLower(&actual_hash)}, | ||
| 417 | ); | ||
| 418 | } | 449 | } |
| 419 | 450 | ||
| 420 | const build_root = try global_cache_directory.join(gpa, &.{pkg_dir_sub_path}); | 451 | const build_root = try global_cache_directory.join(gpa, &.{pkg_dir_sub_path}); |
| ... | @@ -440,35 +471,21 @@ fn unpackTarball( | ... | @@ -440,35 +471,21 @@ fn unpackTarball( |
| 440 | 471 | ||
| 441 | try std.tar.pipeToFileSystem(out_dir, decompress.reader(), .{ | 472 | try std.tar.pipeToFileSystem(out_dir, decompress.reader(), .{ |
| 442 | .strip_components = 1, | 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 | const HashedFile = struct { | 483 | const HashedFile = struct { |
| 467 | path: []const u8, | 484 | path: []const u8, |
| 468 | hash: [Hash.digest_length]u8, | 485 | hash: [Manifest.Hash.digest_length]u8, |
| 469 | failure: Error!void, | 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 | fn lessThan(context: void, lhs: *const HashedFile, rhs: *const HashedFile) bool { | 490 | fn lessThan(context: void, lhs: *const HashedFile, rhs: *const HashedFile) bool { |
| 474 | _ = context; | 491 | _ = context; |
| ... | @@ -479,7 +496,7 @@ const HashedFile = struct { | ... | @@ -479,7 +496,7 @@ const HashedFile = struct { |
| 479 | fn computePackageHash( | 496 | fn computePackageHash( |
| 480 | thread_pool: *ThreadPool, | 497 | thread_pool: *ThreadPool, |
| 481 | pkg_dir: fs.IterableDir, | 498 | pkg_dir: fs.IterableDir, |
| 482 | ) ![Hash.digest_length]u8 { | 499 | ) ![Manifest.Hash.digest_length]u8 { |
| 483 | const gpa = thread_pool.allocator; | 500 | const gpa = thread_pool.allocator; |
| 484 | 501 | ||
| 485 | // We'll use an arena allocator for the path name strings since they all | 502 | // We'll use an arena allocator for the path name strings since they all |
| ... | @@ -522,7 +539,7 @@ fn computePackageHash( | ... | @@ -522,7 +539,7 @@ fn computePackageHash( |
| 522 | 539 | ||
| 523 | std.sort.sort(*HashedFile, all_files.items, {}, HashedFile.lessThan); | 540 | std.sort.sort(*HashedFile, all_files.items, {}, HashedFile.lessThan); |
| 524 | 541 | ||
| 525 | var hasher = Hash.init(.{}); | 542 | var hasher = Manifest.Hash.init(.{}); |
| 526 | var any_failures = false; | 543 | var any_failures = false; |
| 527 | for (all_files.items) |hashed_file| { | 544 | for (all_files.items) |hashed_file| { |
| 528 | hashed_file.failure catch |err| { | 545 | hashed_file.failure catch |err| { |
| ... | @@ -543,7 +560,9 @@ fn workerHashFile(dir: fs.Dir, hashed_file: *HashedFile, wg: *WaitGroup) void { | ... | @@ -543,7 +560,9 @@ fn workerHashFile(dir: fs.Dir, hashed_file: *HashedFile, wg: *WaitGroup) void { |
| 543 | fn hashFileFallible(dir: fs.Dir, hashed_file: *HashedFile) HashedFile.Error!void { | 560 | fn hashFileFallible(dir: fs.Dir, hashed_file: *HashedFile) HashedFile.Error!void { |
| 544 | var buf: [8000]u8 = undefined; | 561 | var buf: [8000]u8 = undefined; |
| 545 | var file = try dir.openFile(hashed_file.path, .{}); | 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 | while (true) { | 566 | while (true) { |
| 548 | const bytes_read = try file.read(&buf); | 567 | const bytes_read = try file.read(&buf); |
| 549 | if (bytes_read == 0) break; | 568 | if (bytes_read == 0) break; |
| ... | @@ -552,31 +571,17 @@ fn hashFileFallible(dir: fs.Dir, hashed_file: *HashedFile) HashedFile.Error!void | ... | @@ -552,31 +571,17 @@ fn hashFileFallible(dir: fs.Dir, hashed_file: *HashedFile) HashedFile.Error!void |
| 552 | hasher.final(&hashed_file.hash); | 571 | hasher.final(&hashed_file.hash); |
| 553 | } | 572 | } |
| 554 | 573 | ||
| 555 | const hex_charset = "0123456789abcdef"; | 574 | fn isExecutable(file: fs.File) !bool { |
| 556 | 575 | if (builtin.os.tag == .windows) { | |
| 557 | fn hex64(x: u64) [16]u8 { | 576 | // TODO check the ACL on Windows. |
| 558 | var result: [16]u8 = undefined; | 577 | // Until this is implemented, this could be a false negative on |
| 559 | var i: usize = 0; | 578 | // Windows, which is why we do not yet set executable_bit_only above |
| 560 | while (i < 8) : (i += 1) { | 579 | // when unpacking the tarball. |
| 561 | const byte = @truncate(u8, x >> @intCast(u6, 8 * i)); | 580 | return false; |
| 562 | result[i * 2 + 0] = hex_charset[byte >> 4]; | 581 | } else { |
| 563 | result[i * 2 + 1] = hex_charset[byte & 15]; | 582 | const stat = try file.stat(); |
| 564 | } | 583 | return (stat.mode & std.os.S.IXUSR) != 0; |
| 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]; | ||
| 578 | } | 584 | } |
| 579 | return result; | ||
| 580 | } | 585 | } |
| 581 | 586 | ||
| 582 | fn renameTmpIntoCache( | 587 | fn renameTmpIntoCache( |
src/Sema.zig+99-22| ... | @@ -1015,6 +1015,7 @@ fn analyzeBodyInner( | ... | @@ -1015,6 +1015,7 @@ fn analyzeBodyInner( |
| 1015 | .float_cast => try sema.zirFloatCast(block, inst), | 1015 | .float_cast => try sema.zirFloatCast(block, inst), |
| 1016 | .int_cast => try sema.zirIntCast(block, inst), | 1016 | .int_cast => try sema.zirIntCast(block, inst), |
| 1017 | .ptr_cast => try sema.zirPtrCast(block, inst), | 1017 | .ptr_cast => try sema.zirPtrCast(block, inst), |
| 1018 | .qual_cast => try sema.zirQualCast(block, inst), | ||
| 1018 | .truncate => try sema.zirTruncate(block, inst), | 1019 | .truncate => try sema.zirTruncate(block, inst), |
| 1019 | .align_cast => try sema.zirAlignCast(block, inst), | 1020 | .align_cast => try sema.zirAlignCast(block, inst), |
| 1020 | .has_decl => try sema.zirHasDecl(block, inst), | 1021 | .has_decl => try sema.zirHasDecl(block, inst), |
| ... | @@ -3294,7 +3295,7 @@ fn ensureResultUsed( | ... | @@ -3294,7 +3295,7 @@ fn ensureResultUsed( |
| 3294 | const msg = msg: { | 3295 | const msg = msg: { |
| 3295 | const msg = try sema.errMsg(block, src, "error is ignored", .{}); | 3296 | const msg = try sema.errMsg(block, src, "error is ignored", .{}); |
| 3296 | errdefer msg.destroy(sema.gpa); | 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 | break :msg msg; | 3299 | break :msg msg; |
| 3299 | }; | 3300 | }; |
| 3300 | return sema.failWithOwnedErrorMsg(msg); | 3301 | return sema.failWithOwnedErrorMsg(msg); |
| ... | @@ -3325,7 +3326,7 @@ fn zirEnsureResultNonError(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com | ... | @@ -3325,7 +3326,7 @@ fn zirEnsureResultNonError(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com |
| 3325 | const msg = msg: { | 3326 | const msg = msg: { |
| 3326 | const msg = try sema.errMsg(block, src, "error is discarded", .{}); | 3327 | const msg = try sema.errMsg(block, src, "error is discarded", .{}); |
| 3327 | errdefer msg.destroy(sema.gpa); | 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 | break :msg msg; | 3330 | break :msg msg; |
| 3330 | }; | 3331 | }; |
| 3331 | return sema.failWithOwnedErrorMsg(msg); | 3332 | return sema.failWithOwnedErrorMsg(msg); |
| ... | @@ -5564,16 +5565,6 @@ pub fn analyzeExport( | ... | @@ -5564,16 +5565,6 @@ pub fn analyzeExport( |
| 5564 | .visibility = borrowed_options.visibility, | 5565 | .visibility = borrowed_options.visibility, |
| 5565 | }, | 5566 | }, |
| 5566 | .src = src, | 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 | .owner_decl = sema.owner_decl_index, | 5568 | .owner_decl = sema.owner_decl_index, |
| 5578 | .src_decl = block.src_decl, | 5569 | .src_decl = block.src_decl, |
| 5579 | .exported_decl = exported_decl_index, | 5570 | .exported_decl = exported_decl_index, |
| ... | @@ -6446,7 +6437,12 @@ fn analyzeCall( | ... | @@ -6446,7 +6437,12 @@ fn analyzeCall( |
| 6446 | .extern_fn => return sema.fail(block, call_src, "{s} call of extern function", .{ | 6437 | .extern_fn => return sema.fail(block, call_src, "{s} call of extern function", .{ |
| 6447 | @as([]const u8, if (is_comptime_call) "comptime" else "inline"), | 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 | if (func_ty_info.is_var_args) { | 6447 | if (func_ty_info.is_var_args) { |
| 6452 | return sema.fail(block, call_src, "{s} call of variadic function", .{ | 6448 | return sema.fail(block, call_src, "{s} call of variadic function", .{ |
| ... | @@ -6879,6 +6875,8 @@ fn analyzeInlineCallArg( | ... | @@ -6879,6 +6875,8 @@ fn analyzeInlineCallArg( |
| 6879 | if (err == error.AnalysisFail and param_block.comptime_reason != null) try param_block.comptime_reason.?.explain(sema, sema.err); | 6875 | if (err == error.AnalysisFail and param_block.comptime_reason != null) try param_block.comptime_reason.?.explain(sema, sema.err); |
| 6880 | return err; | 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 | const casted_arg = sema.coerceExtra(arg_block, param_ty, uncasted_arg, arg_src, .{ .param_src = .{ | 6881 | const casted_arg = sema.coerceExtra(arg_block, param_ty, uncasted_arg, arg_src, .{ .param_src = .{ |
| 6884 | .func_inst = func_inst, | 6882 | .func_inst = func_inst, |
| ... | @@ -6952,6 +6950,9 @@ fn analyzeInlineCallArg( | ... | @@ -6952,6 +6950,9 @@ fn analyzeInlineCallArg( |
| 6952 | .val = arg_val, | 6950 | .val = arg_val, |
| 6953 | }; | 6951 | }; |
| 6954 | } else { | 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 | sema.inst_map.putAssumeCapacityNoClobber(inst, uncasted_arg); | 6956 | sema.inst_map.putAssumeCapacityNoClobber(inst, uncasted_arg); |
| 6956 | } | 6957 | } |
| 6957 | 6958 | ||
| ... | @@ -7510,7 +7511,6 @@ fn resolveGenericInstantiationType( | ... | @@ -7510,7 +7511,6 @@ fn resolveGenericInstantiationType( |
| 7510 | // Queue up a `codegen_func` work item for the new Fn. The `comptime_args` field | 7511 | // Queue up a `codegen_func` work item for the new Fn. The `comptime_args` field |
| 7511 | // will be populated, ensuring it will have `analyzeBody` called with the ZIR | 7512 | // will be populated, ensuring it will have `analyzeBody` called with the ZIR |
| 7512 | // parameters mapped appropriately. | 7513 | // parameters mapped appropriately. |
| 7513 | try mod.comp.bin_file.allocateDeclIndexes(new_decl_index); | ||
| 7514 | try mod.comp.work_queue.writeItem(.{ .codegen_func = new_func }); | 7514 | try mod.comp.work_queue.writeItem(.{ .codegen_func = new_func }); |
| 7515 | return new_func; | 7515 | return new_func; |
| 7516 | } | 7516 | } |
| ... | @@ -8473,7 +8473,7 @@ fn handleExternLibName( | ... | @@ -8473,7 +8473,7 @@ fn handleExternLibName( |
| 8473 | return sema.fail( | 8473 | return sema.fail( |
| 8474 | block, | 8474 | block, |
| 8475 | src_loc, | 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 | .{ lib_name, lib_name }, | 8477 | .{ lib_name, lib_name }, |
| 8478 | ); | 8478 | ); |
| 8479 | } | 8479 | } |
| ... | @@ -9010,7 +9010,18 @@ fn zirParam( | ... | @@ -9010,7 +9010,18 @@ fn zirParam( |
| 9010 | if (is_comptime and sema.preallocated_new_func != null) { | 9010 | if (is_comptime and sema.preallocated_new_func != null) { |
| 9011 | // We have a comptime value for this parameter so it should be elided from the | 9011 | // We have a comptime value for this parameter so it should be elided from the |
| 9012 | // function type of the function instruction in this block. | 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 | sema.inst_map.putAssumeCapacity(inst, coerced_arg); | 9025 | sema.inst_map.putAssumeCapacity(inst, coerced_arg); |
| 9015 | return; | 9026 | return; |
| 9016 | } | 9027 | } |
| ... | @@ -19525,13 +19536,34 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air | ... | @@ -19525,13 +19536,34 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air |
| 19525 | const operand_info = operand_ty.ptrInfo().data; | 19536 | const operand_info = operand_ty.ptrInfo().data; |
| 19526 | const dest_info = dest_ty.ptrInfo().data; | 19537 | const dest_info = dest_ty.ptrInfo().data; |
| 19527 | if (!operand_info.mutable and dest_info.mutable) { | 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 | if (operand_info.@"volatile" and !dest_info.@"volatile") { | 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 | if (operand_info.@"addrspace" != dest_info.@"addrspace") { | 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 | const dest_is_slice = dest_ty.isSlice(); | 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,6 +19618,8 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air |
| 19586 | try sema.errNote(block, dest_ty_src, msg, "'{}' has alignment '{d}'", .{ | 19618 | try sema.errNote(block, dest_ty_src, msg, "'{}' has alignment '{d}'", .{ |
| 19587 | dest_ty.fmt(sema.mod), dest_align, | 19619 | dest_ty.fmt(sema.mod), dest_align, |
| 19588 | }); | 19620 | }); |
| 19621 | |||
| 19622 | try sema.errNote(block, src, msg, "consider using '@alignCast'", .{}); | ||
| 19589 | break :msg msg; | 19623 | break :msg msg; |
| 19590 | }; | 19624 | }; |
| 19591 | return sema.failWithOwnedErrorMsg(msg); | 19625 | return sema.failWithOwnedErrorMsg(msg); |
| ... | @@ -19621,6 +19655,49 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air | ... | @@ -19621,6 +19655,49 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air |
| 19621 | return block.addBitCast(aligned_dest_ty, ptr); | 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 | fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | 19701 | fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 19625 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | 19702 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; |
| 19626 | const src = inst_data.src(); | 19703 | const src = inst_data.src(); |
| ... | @@ -25137,7 +25214,7 @@ fn coerceExtra( | ... | @@ -25137,7 +25214,7 @@ fn coerceExtra( |
| 25137 | (try sema.coerceInMemoryAllowed(block, inst_ty.errorUnionPayload(), dest_ty, false, target, dest_ty_src, inst_src)) == .ok) | 25214 | (try sema.coerceInMemoryAllowed(block, inst_ty.errorUnionPayload(), dest_ty, false, target, dest_ty_src, inst_src)) == .ok) |
| 25138 | { | 25215 | { |
| 25139 | try sema.errNote(block, inst_src, msg, "cannot convert error union to payload type", .{}); | 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 | // ?T to T | 25220 | // ?T to T |
| ... | @@ -25146,7 +25223,7 @@ fn coerceExtra( | ... | @@ -25146,7 +25223,7 @@ fn coerceExtra( |
| 25146 | (try sema.coerceInMemoryAllowed(block, inst_ty.optionalChild(&buf), dest_ty, false, target, dest_ty_src, inst_src)) == .ok) | 25223 | (try sema.coerceInMemoryAllowed(block, inst_ty.optionalChild(&buf), dest_ty, false, target, dest_ty_src, inst_src)) == .ok) |
| 25147 | { | 25224 | { |
| 25148 | try sema.errNote(block, inst_src, msg, "cannot convert optional to payload type", .{}); | 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 | try in_memory_result.report(sema, block, inst_src, msg); | 25229 | try in_memory_result.report(sema, block, inst_src, msg); |
| ... | @@ -26072,7 +26149,7 @@ fn coerceVarArgParam( | ... | @@ -26072,7 +26149,7 @@ fn coerceVarArgParam( |
| 26072 | .Array => return sema.fail(block, inst_src, "arrays must be passed by reference to variadic function", .{}), | 26149 | .Array => return sema.fail(block, inst_src, "arrays must be passed by reference to variadic function", .{}), |
| 26073 | .Float => float: { | 26150 | .Float => float: { |
| 26074 | const target = sema.mod.getTarget(); | 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 | const inst_bits = uncasted_ty.floatBits(sema.mod.getTarget()); | 26153 | const inst_bits = uncasted_ty.floatBits(sema.mod.getTarget()); |
| 26077 | if (inst_bits >= double_bits) break :float inst; | 26154 | if (inst_bits >= double_bits) break :float inst; |
| 26078 | switch (double_bits) { | 26155 | switch (double_bits) { |
src/TypedValue.zig+4-1| ... | @@ -176,7 +176,9 @@ pub fn print( | ... | @@ -176,7 +176,9 @@ pub fn print( |
| 176 | 176 | ||
| 177 | var i: u32 = 0; | 177 | var i: u32 = 0; |
| 178 | while (i < max_len) : (i += 1) { | 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 | const truncated = if (len > max_string_len) " (truncated)" else ""; | 184 | const truncated = if (len > max_string_len) " (truncated)" else ""; |
| ... | @@ -390,6 +392,7 @@ pub fn print( | ... | @@ -390,6 +392,7 @@ pub fn print( |
| 390 | while (i < max_len) : (i += 1) { | 392 | while (i < max_len) : (i += 1) { |
| 391 | var elem_buf: Value.ElemValueBuffer = undefined; | 393 | var elem_buf: Value.ElemValueBuffer = undefined; |
| 392 | const elem_val = payload.ptr.elemValueBuffer(mod, i, &elem_buf); | 394 | const elem_val = payload.ptr.elemValueBuffer(mod, i, &elem_buf); |
| 395 | if (elem_val.isUndef()) break :str; | ||
| 393 | buf[i] = std.math.cast(u8, elem_val.toUnsignedInt(target)) orelse break :str; | 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,6 +857,9 @@ pub const Inst = struct { |
| 857 | /// Implements the `@ptrCast` builtin. | 857 | /// Implements the `@ptrCast` builtin. |
| 858 | /// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand. | 858 | /// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand. |
| 859 | ptr_cast, | 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 | /// Implements the `@truncate` builtin. | 863 | /// Implements the `@truncate` builtin. |
| 861 | /// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand. | 864 | /// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand. |
| 862 | truncate, | 865 | truncate, |
| ... | @@ -1195,6 +1198,7 @@ pub const Inst = struct { | ... | @@ -1195,6 +1198,7 @@ pub const Inst = struct { |
| 1195 | .float_cast, | 1198 | .float_cast, |
| 1196 | .int_cast, | 1199 | .int_cast, |
| 1197 | .ptr_cast, | 1200 | .ptr_cast, |
| 1201 | .qual_cast, | ||
| 1198 | .truncate, | 1202 | .truncate, |
| 1199 | .align_cast, | 1203 | .align_cast, |
| 1200 | .has_field, | 1204 | .has_field, |
| ... | @@ -1484,6 +1488,7 @@ pub const Inst = struct { | ... | @@ -1484,6 +1488,7 @@ pub const Inst = struct { |
| 1484 | .float_cast, | 1488 | .float_cast, |
| 1485 | .int_cast, | 1489 | .int_cast, |
| 1486 | .ptr_cast, | 1490 | .ptr_cast, |
| 1491 | .qual_cast, | ||
| 1487 | .truncate, | 1492 | .truncate, |
| 1488 | .align_cast, | 1493 | .align_cast, |
| 1489 | .has_field, | 1494 | .has_field, |
| ... | @@ -1755,6 +1760,7 @@ pub const Inst = struct { | ... | @@ -1755,6 +1760,7 @@ pub const Inst = struct { |
| 1755 | .float_cast = .pl_node, | 1760 | .float_cast = .pl_node, |
| 1756 | .int_cast = .pl_node, | 1761 | .int_cast = .pl_node, |
| 1757 | .ptr_cast = .pl_node, | 1762 | .ptr_cast = .pl_node, |
| 1763 | .qual_cast = .pl_node, | ||
| 1758 | .truncate = .pl_node, | 1764 | .truncate = .pl_node, |
| 1759 | .align_cast = .pl_node, | 1765 | .align_cast = .pl_node, |
| 1760 | .typeof_builtin = .pl_node, | 1766 | .typeof_builtin = .pl_node, |
src/arch/aarch64/CodeGen.zig+143-175| ... | @@ -24,7 +24,7 @@ const log = std.log.scoped(.codegen); | ... | @@ -24,7 +24,7 @@ const log = std.log.scoped(.codegen); |
| 24 | const build_options = @import("build_options"); | 24 | const build_options = @import("build_options"); |
| 25 | 25 | ||
| 26 | const GenerateSymbolError = codegen.GenerateSymbolError; | 26 | const GenerateSymbolError = codegen.GenerateSymbolError; |
| 27 | const FnResult = codegen.FnResult; | 27 | const Result = codegen.Result; |
| 28 | const DebugInfoOutput = codegen.DebugInfoOutput; | 28 | const DebugInfoOutput = codegen.DebugInfoOutput; |
| 29 | 29 | ||
| 30 | const bits = @import("bits.zig"); | 30 | const bits = @import("bits.zig"); |
| ... | @@ -181,6 +181,7 @@ const DbgInfoReloc = struct { | ... | @@ -181,6 +181,7 @@ const DbgInfoReloc = struct { |
| 181 | else => unreachable, | 181 | else => unreachable, |
| 182 | } | 182 | } |
| 183 | } | 183 | } |
| 184 | |||
| 184 | fn genArgDbgInfo(reloc: DbgInfoReloc, function: Self) error{OutOfMemory}!void { | 185 | fn genArgDbgInfo(reloc: DbgInfoReloc, function: Self) error{OutOfMemory}!void { |
| 185 | switch (function.debug_output) { | 186 | switch (function.debug_output) { |
| 186 | .dwarf => |dw| { | 187 | .dwarf => |dw| { |
| ... | @@ -202,13 +203,7 @@ const DbgInfoReloc = struct { | ... | @@ -202,13 +203,7 @@ const DbgInfoReloc = struct { |
| 202 | else => unreachable, // not a possible argument | 203 | else => unreachable, // not a possible argument |
| 203 | 204 | ||
| 204 | }; | 205 | }; |
| 205 | try dw.genArgDbgInfo( | 206 | try dw.genArgDbgInfo(reloc.name, reloc.ty, function.mod_fn.owner_decl, loc); |
| 206 | reloc.name, | ||
| 207 | reloc.ty, | ||
| 208 | function.bin_file.tag, | ||
| 209 | function.mod_fn.owner_decl, | ||
| 210 | loc, | ||
| 211 | ); | ||
| 212 | }, | 207 | }, |
| 213 | .plan9 => {}, | 208 | .plan9 => {}, |
| 214 | .none => {}, | 209 | .none => {}, |
| ... | @@ -254,14 +249,7 @@ const DbgInfoReloc = struct { | ... | @@ -254,14 +249,7 @@ const DbgInfoReloc = struct { |
| 254 | break :blk .nop; | 249 | break :blk .nop; |
| 255 | }, | 250 | }, |
| 256 | }; | 251 | }; |
| 257 | try dw.genVarDbgInfo( | 252 | try dw.genVarDbgInfo(reloc.name, reloc.ty, function.mod_fn.owner_decl, is_ptr, loc); |
| 258 | reloc.name, | ||
| 259 | reloc.ty, | ||
| 260 | function.bin_file.tag, | ||
| 261 | function.mod_fn.owner_decl, | ||
| 262 | is_ptr, | ||
| 263 | loc, | ||
| 264 | ); | ||
| 265 | }, | 253 | }, |
| 266 | .plan9 => {}, | 254 | .plan9 => {}, |
| 267 | .none => {}, | 255 | .none => {}, |
| ... | @@ -349,7 +337,7 @@ pub fn generate( | ... | @@ -349,7 +337,7 @@ pub fn generate( |
| 349 | liveness: Liveness, | 337 | liveness: Liveness, |
| 350 | code: *std.ArrayList(u8), | 338 | code: *std.ArrayList(u8), |
| 351 | debug_output: DebugInfoOutput, | 339 | debug_output: DebugInfoOutput, |
| 352 | ) GenerateSymbolError!FnResult { | 340 | ) GenerateSymbolError!Result { |
| 353 | if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) { | 341 | if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) { |
| 354 | @panic("Attempted to compile for architecture that was disabled by build configuration"); | 342 | @panic("Attempted to compile for architecture that was disabled by build configuration"); |
| 355 | } | 343 | } |
| ... | @@ -392,8 +380,8 @@ pub fn generate( | ... | @@ -392,8 +380,8 @@ pub fn generate( |
| 392 | defer function.dbg_info_relocs.deinit(bin_file.allocator); | 380 | defer function.dbg_info_relocs.deinit(bin_file.allocator); |
| 393 | 381 | ||
| 394 | var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) { | 382 | var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) { |
| 395 | error.CodegenFail => return FnResult{ .fail = function.err_msg.? }, | 383 | error.CodegenFail => return Result{ .fail = function.err_msg.? }, |
| 396 | error.OutOfRegisters => return FnResult{ | 384 | error.OutOfRegisters => return Result{ |
| 397 | .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), | 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 | else => |e| return e, | 387 | else => |e| return e, |
| ... | @@ -406,8 +394,8 @@ pub fn generate( | ... | @@ -406,8 +394,8 @@ pub fn generate( |
| 406 | function.max_end_stack = call_info.stack_byte_count; | 394 | function.max_end_stack = call_info.stack_byte_count; |
| 407 | 395 | ||
| 408 | function.gen() catch |err| switch (err) { | 396 | function.gen() catch |err| switch (err) { |
| 409 | error.CodegenFail => return FnResult{ .fail = function.err_msg.? }, | 397 | error.CodegenFail => return Result{ .fail = function.err_msg.? }, |
| 410 | error.OutOfRegisters => return FnResult{ | 398 | error.OutOfRegisters => return Result{ |
| 411 | .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), | 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 | else => |e| return e, | 401 | else => |e| return e, |
| ... | @@ -439,14 +427,14 @@ pub fn generate( | ... | @@ -439,14 +427,14 @@ pub fn generate( |
| 439 | defer emit.deinit(); | 427 | defer emit.deinit(); |
| 440 | 428 | ||
| 441 | emit.emitMir() catch |err| switch (err) { | 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 | else => |e| return e, | 431 | else => |e| return e, |
| 444 | }; | 432 | }; |
| 445 | 433 | ||
| 446 | if (function.err_msg) |em| { | 434 | if (function.err_msg) |em| { |
| 447 | return FnResult{ .fail = em }; | 435 | return Result{ .fail = em }; |
| 448 | } else { | 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,6 +515,28 @@ fn gen(self: *Self) !void { |
| 527 | self.ret_mcv = MCValue{ .stack_offset = stack_offset }; | 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 | _ = try self.addInst(.{ | 540 | _ = try self.addInst(.{ |
| 531 | .tag = .dbg_prologue_end, | 541 | .tag = .dbg_prologue_end, |
| 532 | .data = .{ .nop = {} }, | 542 | .data = .{ .nop = {} }, |
| ... | @@ -3996,11 +4006,17 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type | ... | @@ -3996,11 +4006,17 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type |
| 3996 | .direct => .load_memory_ptr_direct, | 4006 | .direct => .load_memory_ptr_direct, |
| 3997 | .import => unreachable, | 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 | const atom_index = switch (self.bin_file.tag) { | 4009 | const atom_index = switch (self.bin_file.tag) { |
| 4002 | .macho => owner_decl.link.macho.sym_index, | 4010 | .macho => blk: { |
| 4003 | .coff => owner_decl.link.coff.sym_index, | 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 | else => unreachable, // unsupported target format | 4020 | else => unreachable, // unsupported target format |
| 4005 | }; | 4021 | }; |
| 4006 | _ = try self.addInst(.{ | 4022 | _ = try self.addInst(.{ |
| ... | @@ -4163,45 +4179,19 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -4163,45 +4179,19 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { |
| 4163 | self.arg_index += 1; | 4179 | self.arg_index += 1; |
| 4164 | 4180 | ||
| 4165 | const ty = self.air.typeOfIndex(inst); | 4181 | const ty = self.air.typeOfIndex(inst); |
| 4166 | const result = self.args[arg_index]; | 4182 | const tag = self.air.instructions.items(.tag)[inst]; |
| 4167 | const src_index = self.air.instructions.items(.data)[inst].arg.src_index; | 4183 | const src_index = self.air.instructions.items(.data)[inst].arg.src_index; |
| 4168 | const name = self.mod_fn.getParamName(self.bin_file.options.module.?, src_index); | 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 | try self.dbg_info_relocs.append(self.gpa, .{ | 4186 | try self.dbg_info_relocs.append(self.gpa, .{ |
| 4188 | .tag = tag, | 4187 | .tag = tag, |
| 4189 | .ty = ty, | 4188 | .ty = ty, |
| 4190 | .name = name, | 4189 | .name = name, |
| 4191 | .mcv = result, | 4190 | .mcv = self.args[arg_index], |
| 4192 | }); | 4191 | }); |
| 4193 | 4192 | ||
| 4194 | if (self.liveness.isUnused(inst)) | 4193 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else self.args[arg_index]; |
| 4195 | return self.finishAirBookkeeping(); | 4194 | return self.finishAir(inst, result, .{ .none, .none, .none }); |
| 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 }); | ||
| 4205 | } | 4195 | } |
| 4206 | 4196 | ||
| 4207 | fn airBreakpoint(self: *Self) !void { | 4197 | fn airBreakpoint(self: *Self) !void { |
| ... | @@ -4302,90 +4292,71 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier | ... | @@ -4302,90 +4292,71 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier |
| 4302 | // on linking. | 4292 | // on linking. |
| 4303 | const mod = self.bin_file.options.module.?; | 4293 | const mod = self.bin_file.options.module.?; |
| 4304 | if (self.air.value(callee)) |func_value| { | 4294 | if (self.air.value(callee)) |func_value| { |
| 4305 | if (self.bin_file.cast(link.File.Elf)) |elf_file| { | 4295 | if (func_value.castTag(.function)) |func_payload| { |
| 4306 | if (func_value.castTag(.function)) |func_payload| { | 4296 | const func = func_payload.data; |
| 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 | }; | ||
| 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 | try self.genSetReg(Type.initTag(.usize), .x30, .{ .memory = got_addr }); | 4302 | try self.genSetReg(Type.initTag(.usize), .x30, .{ .memory = got_addr }); |
| 4317 | 4303 | } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { | |
| 4318 | _ = try self.addInst(.{ | 4304 | const atom = try macho_file.getOrCreateAtomForDecl(func.owner_decl); |
| 4319 | .tag = .blr, | 4305 | const sym_index = macho_file.getAtom(atom).getSymbolIndex().?; |
| 4320 | .data = .{ .reg = .x30 }, | 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)) |_| { | 4312 | } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { |
| 4323 | return self.fail("TODO implement calling extern functions", .{}); | 4313 | const atom = try coff_file.getOrCreateAtomForDecl(func.owner_decl); |
| 4324 | } else { | 4314 | const sym_index = coff_file.getAtom(atom).getSymbolIndex().?; |
| 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); | ||
| 4331 | try self.genSetReg(Type.initTag(.u64), .x30, .{ | 4315 | try self.genSetReg(Type.initTag(.u64), .x30, .{ |
| 4332 | .linker_load = .{ | 4316 | .linker_load = .{ |
| 4333 | .type = .got, | 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 | 4321 | } else if (self.bin_file.cast(link.File.Plan9)) |p9| { |
| 4338 | _ = try self.addInst(.{ | 4322 | const decl_block_index = try p9.seeDecl(func.owner_decl); |
| 4339 | .tag = .blr, | 4323 | const decl_block = p9.getDeclBlock(decl_block_index); |
| 4340 | .data = .{ .reg = .x30 }, | 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| { | 4344 | } |
| 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)); | ||
| 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 | _ = try self.addInst(.{ | 4350 | _ = try self.addInst(.{ |
| 4354 | .tag = .call_extern, | 4351 | .tag = .call_extern, |
| 4355 | .data = .{ | 4352 | .data = .{ |
| 4356 | .relocation = .{ | 4353 | .relocation = .{ |
| 4357 | .atom_index = mod.declPtr(self.mod_fn.owner_decl).link.macho.sym_index, | 4354 | .atom_index = atom_index, |
| 4358 | .sym_index = sym_index, | 4355 | .sym_index = sym_index, |
| 4359 | }, | 4356 | }, |
| 4360 | }, | 4357 | }, |
| 4361 | }); | 4358 | }); |
| 4362 | } else { | 4359 | } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { |
| 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 | } | ||
| 4389 | const sym_index = try coff_file.getGlobalSymbol(mem.sliceTo(decl_name, 0)); | 4360 | const sym_index = try coff_file.getGlobalSymbol(mem.sliceTo(decl_name, 0)); |
| 4390 | try self.genSetReg(Type.initTag(.u64), .x30, .{ | 4361 | try self.genSetReg(Type.initTag(.u64), .x30, .{ |
| 4391 | .linker_load = .{ | 4362 | .linker_load = .{ |
| ... | @@ -4393,35 +4364,16 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier | ... | @@ -4393,35 +4364,16 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier |
| 4393 | .sym_index = sym_index, | 4364 | .sym_index = sym_index, |
| 4394 | }, | 4365 | }, |
| 4395 | }); | 4366 | }); |
| 4396 | // blr x30 | ||
| 4397 | _ = try self.addInst(.{ | 4367 | _ = try self.addInst(.{ |
| 4398 | .tag = .blr, | 4368 | .tag = .blr, |
| 4399 | .data = .{ .reg = .x30 }, | 4369 | .data = .{ .reg = .x30 }, |
| 4400 | }); | 4370 | }); |
| 4401 | } else { | 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 | return self.fail("TODO implement calling extern functions", .{}); | 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 | } else { | 4377 | } else { |
| 4426 | assert(ty.zigTypeTag() == .Pointer); | 4378 | assert(ty.zigTypeTag() == .Pointer); |
| 4427 | const mcv = try self.resolveInst(callee); | 4379 | const mcv = try self.resolveInst(callee); |
| ... | @@ -5534,11 +5486,17 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro | ... | @@ -5534,11 +5486,17 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro |
| 5534 | .direct => .load_memory_ptr_direct, | 5486 | .direct => .load_memory_ptr_direct, |
| 5535 | .import => unreachable, | 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 | const atom_index = switch (self.bin_file.tag) { | 5489 | const atom_index = switch (self.bin_file.tag) { |
| 5540 | .macho => owner_decl.link.macho.sym_index, | 5490 | .macho => blk: { |
| 5541 | .coff => owner_decl.link.coff.sym_index, | 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 | else => unreachable, // unsupported target format | 5500 | else => unreachable, // unsupported target format |
| 5543 | }; | 5501 | }; |
| 5544 | _ = try self.addInst(.{ | 5502 | _ = try self.addInst(.{ |
| ... | @@ -5648,11 +5606,17 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void | ... | @@ -5648,11 +5606,17 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 5648 | .direct => .load_memory_direct, | 5606 | .direct => .load_memory_direct, |
| 5649 | .import => .load_memory_import, | 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 | const atom_index = switch (self.bin_file.tag) { | 5609 | const atom_index = switch (self.bin_file.tag) { |
| 5654 | .macho => owner_decl.link.macho.sym_index, | 5610 | .macho => blk: { |
| 5655 | .coff => owner_decl.link.coff.sym_index, | 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 | else => unreachable, // unsupported target format | 5620 | else => unreachable, // unsupported target format |
| 5657 | }; | 5621 | }; |
| 5658 | _ = try self.addInst(.{ | 5622 | _ = try self.addInst(.{ |
| ... | @@ -5842,11 +5806,17 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I | ... | @@ -5842,11 +5806,17 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I |
| 5842 | .direct => .load_memory_ptr_direct, | 5806 | .direct => .load_memory_ptr_direct, |
| 5843 | .import => unreachable, | 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 | const atom_index = switch (self.bin_file.tag) { | 5809 | const atom_index = switch (self.bin_file.tag) { |
| 5848 | .macho => owner_decl.link.macho.sym_index, | 5810 | .macho => blk: { |
| 5849 | .coff => owner_decl.link.coff.sym_index, | 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 | else => unreachable, // unsupported target format | 5820 | else => unreachable, // unsupported target format |
| 5851 | }; | 5821 | }; |
| 5852 | _ = try self.addInst(.{ | 5822 | _ = try self.addInst(.{ |
| ... | @@ -6165,28 +6135,27 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne | ... | @@ -6165,28 +6135,27 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne |
| 6165 | mod.markDeclAlive(decl); | 6135 | mod.markDeclAlive(decl); |
| 6166 | 6136 | ||
| 6167 | if (self.bin_file.cast(link.File.Elf)) |elf_file| { | 6137 | if (self.bin_file.cast(link.File.Elf)) |elf_file| { |
| 6168 | const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?]; | 6138 | const atom_index = try elf_file.getOrCreateAtomForDecl(decl_index); |
| 6169 | const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes; | 6139 | const atom = elf_file.getAtom(atom_index); |
| 6170 | return MCValue{ .memory = got_addr }; | 6140 | return MCValue{ .memory = atom.getOffsetTableAddress(elf_file) }; |
| 6171 | } else if (self.bin_file.cast(link.File.MachO)) |_| { | 6141 | } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { |
| 6172 | // Because MachO is PIE-always-on, we defer memory address resolution until | 6142 | const atom = try macho_file.getOrCreateAtomForDecl(decl_index); |
| 6173 | // the linker has enough info to perform relocations. | 6143 | const sym_index = macho_file.getAtom(atom).getSymbolIndex().?; |
| 6174 | assert(decl.link.macho.sym_index != 0); | ||
| 6175 | return MCValue{ .linker_load = .{ | 6144 | return MCValue{ .linker_load = .{ |
| 6176 | .type = .got, | 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)) |_| { | 6148 | } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { |
| 6180 | // Because COFF is PIE-always-on, we defer memory address resolution until | 6149 | const atom_index = try coff_file.getOrCreateAtomForDecl(decl_index); |
| 6181 | // the linker has enough info to perform relocations. | 6150 | const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?; |
| 6182 | assert(decl.link.coff.sym_index != 0); | ||
| 6183 | return MCValue{ .linker_load = .{ | 6151 | return MCValue{ .linker_load = .{ |
| 6184 | .type = .got, | 6152 | .type = .got, |
| 6185 | .sym_index = decl.link.coff.sym_index, | 6153 | .sym_index = sym_index, |
| 6186 | } }; | 6154 | } }; |
| 6187 | } else if (self.bin_file.cast(link.File.Plan9)) |p9| { | 6155 | } else if (self.bin_file.cast(link.File.Plan9)) |p9| { |
| 6188 | try p9.seeDecl(decl_index); | 6156 | const decl_block_index = try p9.seeDecl(decl_index); |
| 6189 | const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes; | 6157 | const decl_block = p9.getDeclBlock(decl_block_index); |
| 6158 | const got_addr = p9.bases.data + decl_block.got_index.? * ptr_bytes; | ||
| 6190 | return MCValue{ .memory = got_addr }; | 6159 | return MCValue{ .memory = got_addr }; |
| 6191 | } else { | 6160 | } else { |
| 6192 | return self.fail("TODO codegen non-ELF const Decl pointer", .{}); | 6161 | return self.fail("TODO codegen non-ELF const Decl pointer", .{}); |
| ... | @@ -6199,8 +6168,7 @@ fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue { | ... | @@ -6199,8 +6168,7 @@ fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue { |
| 6199 | return self.fail("lowering unnamed constant failed: {s}", .{@errorName(err)}); | 6168 | return self.fail("lowering unnamed constant failed: {s}", .{@errorName(err)}); |
| 6200 | }; | 6169 | }; |
| 6201 | if (self.bin_file.cast(link.File.Elf)) |elf_file| { | 6170 | if (self.bin_file.cast(link.File.Elf)) |elf_file| { |
| 6202 | const vaddr = elf_file.local_symbols.items[local_sym_index].st_value; | 6171 | return MCValue{ .memory = elf_file.getSymbol(local_sym_index).st_value }; |
| 6203 | return MCValue{ .memory = vaddr }; | ||
| 6204 | } else if (self.bin_file.cast(link.File.MachO)) |_| { | 6172 | } else if (self.bin_file.cast(link.File.MachO)) |_| { |
| 6205 | return MCValue{ .linker_load = .{ | 6173 | return MCValue{ .linker_load = .{ |
| 6206 | .type = .direct, | 6174 | .type = .direct, |
src/arch/aarch64/Emit.zig+8-8| ... | @@ -670,9 +670,9 @@ fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) !void { | ... | @@ -670,9 +670,9 @@ fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) !void { |
| 670 | 670 | ||
| 671 | if (emit.bin_file.cast(link.File.MachO)) |macho_file| { | 671 | if (emit.bin_file.cast(link.File.MachO)) |macho_file| { |
| 672 | // Add relocation to the decl. | 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 | const target = macho_file.getGlobalByIndex(relocation.sym_index); | 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 | .type = @enumToInt(std.macho.reloc_type_arm64.ARM64_RELOC_BRANCH26), | 676 | .type = @enumToInt(std.macho.reloc_type_arm64.ARM64_RELOC_BRANCH26), |
| 677 | .target = target, | 677 | .target = target, |
| 678 | .offset = offset, | 678 | .offset = offset, |
| ... | @@ -883,10 +883,10 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void { | ... | @@ -883,10 +883,10 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void { |
| 883 | } | 883 | } |
| 884 | 884 | ||
| 885 | if (emit.bin_file.cast(link.File.MachO)) |macho_file| { | 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 | // TODO this causes segfault in stage1 | 887 | // TODO this causes segfault in stage1 |
| 888 | // try atom.addRelocations(macho_file, 2, .{ | 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 | .target = .{ .sym_index = data.sym_index, .file = null }, | 890 | .target = .{ .sym_index = data.sym_index, .file = null }, |
| 891 | .offset = offset, | 891 | .offset = offset, |
| 892 | .addend = 0, | 892 | .addend = 0, |
| ... | @@ -902,7 +902,7 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void { | ... | @@ -902,7 +902,7 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void { |
| 902 | else => unreachable, | 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 | .target = .{ .sym_index = data.sym_index, .file = null }, | 906 | .target = .{ .sym_index = data.sym_index, .file = null }, |
| 907 | .offset = offset + 4, | 907 | .offset = offset + 4, |
| 908 | .addend = 0, | 908 | .addend = 0, |
| ... | @@ -919,7 +919,7 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void { | ... | @@ -919,7 +919,7 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void { |
| 919 | }, | 919 | }, |
| 920 | }); | 920 | }); |
| 921 | } else if (emit.bin_file.cast(link.File.Coff)) |coff_file| { | 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 | const target = switch (tag) { | 923 | const target = switch (tag) { |
| 924 | .load_memory_got, | 924 | .load_memory_got, |
| 925 | .load_memory_ptr_got, | 925 | .load_memory_ptr_got, |
| ... | @@ -929,7 +929,7 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void { | ... | @@ -929,7 +929,7 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void { |
| 929 | .load_memory_import => coff_file.getGlobalByIndex(data.sym_index), | 929 | .load_memory_import => coff_file.getGlobalByIndex(data.sym_index), |
| 930 | else => unreachable, | 930 | else => unreachable, |
| 931 | }; | 931 | }; |
| 932 | try atom.addRelocation(coff_file, .{ | 932 | try link.File.Coff.Atom.addRelocation(coff_file, atom_index, .{ |
| 933 | .target = target, | 933 | .target = target, |
| 934 | .offset = offset, | 934 | .offset = offset, |
| 935 | .addend = 0, | 935 | .addend = 0, |
| ... | @@ -946,7 +946,7 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void { | ... | @@ -946,7 +946,7 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void { |
| 946 | else => unreachable, | 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 | .target = target, | 950 | .target = target, |
| 951 | .offset = offset + 4, | 951 | .offset = offset + 4, |
| 952 | .addend = 0, | 952 | .addend = 0, |
src/arch/arm/CodeGen.zig+65-81| ... | @@ -23,7 +23,7 @@ const leb128 = std.leb; | ... | @@ -23,7 +23,7 @@ const leb128 = std.leb; |
| 23 | const log = std.log.scoped(.codegen); | 23 | const log = std.log.scoped(.codegen); |
| 24 | const build_options = @import("build_options"); | 24 | const build_options = @import("build_options"); |
| 25 | 25 | ||
| 26 | const FnResult = codegen.FnResult; | 26 | const Result = codegen.Result; |
| 27 | const GenerateSymbolError = codegen.GenerateSymbolError; | 27 | const GenerateSymbolError = codegen.GenerateSymbolError; |
| 28 | const DebugInfoOutput = codegen.DebugInfoOutput; | 28 | const DebugInfoOutput = codegen.DebugInfoOutput; |
| 29 | 29 | ||
| ... | @@ -282,13 +282,7 @@ const DbgInfoReloc = struct { | ... | @@ -282,13 +282,7 @@ const DbgInfoReloc = struct { |
| 282 | else => unreachable, // not a possible argument | 282 | else => unreachable, // not a possible argument |
| 283 | }; | 283 | }; |
| 284 | 284 | ||
| 285 | try dw.genArgDbgInfo( | 285 | try dw.genArgDbgInfo(reloc.name, reloc.ty, function.mod_fn.owner_decl, loc); |
| 286 | reloc.name, | ||
| 287 | reloc.ty, | ||
| 288 | function.bin_file.tag, | ||
| 289 | function.mod_fn.owner_decl, | ||
| 290 | loc, | ||
| 291 | ); | ||
| 292 | }, | 286 | }, |
| 293 | .plan9 => {}, | 287 | .plan9 => {}, |
| 294 | .none => {}, | 288 | .none => {}, |
| ... | @@ -331,14 +325,7 @@ const DbgInfoReloc = struct { | ... | @@ -331,14 +325,7 @@ const DbgInfoReloc = struct { |
| 331 | break :blk .nop; | 325 | break :blk .nop; |
| 332 | }, | 326 | }, |
| 333 | }; | 327 | }; |
| 334 | try dw.genVarDbgInfo( | 328 | try dw.genVarDbgInfo(reloc.name, reloc.ty, function.mod_fn.owner_decl, is_ptr, loc); |
| 335 | reloc.name, | ||
| 336 | reloc.ty, | ||
| 337 | function.bin_file.tag, | ||
| 338 | function.mod_fn.owner_decl, | ||
| 339 | is_ptr, | ||
| 340 | loc, | ||
| 341 | ); | ||
| 342 | }, | 329 | }, |
| 343 | .plan9 => {}, | 330 | .plan9 => {}, |
| 344 | .none => {}, | 331 | .none => {}, |
| ... | @@ -356,7 +343,7 @@ pub fn generate( | ... | @@ -356,7 +343,7 @@ pub fn generate( |
| 356 | liveness: Liveness, | 343 | liveness: Liveness, |
| 357 | code: *std.ArrayList(u8), | 344 | code: *std.ArrayList(u8), |
| 358 | debug_output: DebugInfoOutput, | 345 | debug_output: DebugInfoOutput, |
| 359 | ) GenerateSymbolError!FnResult { | 346 | ) GenerateSymbolError!Result { |
| 360 | if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) { | 347 | if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) { |
| 361 | @panic("Attempted to compile for architecture that was disabled by build configuration"); | 348 | @panic("Attempted to compile for architecture that was disabled by build configuration"); |
| 362 | } | 349 | } |
| ... | @@ -399,8 +386,8 @@ pub fn generate( | ... | @@ -399,8 +386,8 @@ pub fn generate( |
| 399 | defer function.dbg_info_relocs.deinit(bin_file.allocator); | 386 | defer function.dbg_info_relocs.deinit(bin_file.allocator); |
| 400 | 387 | ||
| 401 | var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) { | 388 | var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) { |
| 402 | error.CodegenFail => return FnResult{ .fail = function.err_msg.? }, | 389 | error.CodegenFail => return Result{ .fail = function.err_msg.? }, |
| 403 | error.OutOfRegisters => return FnResult{ | 390 | error.OutOfRegisters => return Result{ |
| 404 | .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), | 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 | else => |e| return e, | 393 | else => |e| return e, |
| ... | @@ -413,8 +400,8 @@ pub fn generate( | ... | @@ -413,8 +400,8 @@ pub fn generate( |
| 413 | function.max_end_stack = call_info.stack_byte_count; | 400 | function.max_end_stack = call_info.stack_byte_count; |
| 414 | 401 | ||
| 415 | function.gen() catch |err| switch (err) { | 402 | function.gen() catch |err| switch (err) { |
| 416 | error.CodegenFail => return FnResult{ .fail = function.err_msg.? }, | 403 | error.CodegenFail => return Result{ .fail = function.err_msg.? }, |
| 417 | error.OutOfRegisters => return FnResult{ | 404 | error.OutOfRegisters => return Result{ |
| 418 | .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), | 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 | else => |e| return e, | 407 | else => |e| return e, |
| ... | @@ -446,14 +433,14 @@ pub fn generate( | ... | @@ -446,14 +433,14 @@ pub fn generate( |
| 446 | defer emit.deinit(); | 433 | defer emit.deinit(); |
| 447 | 434 | ||
| 448 | emit.emitMir() catch |err| switch (err) { | 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 | else => |e| return e, | 437 | else => |e| return e, |
| 451 | }; | 438 | }; |
| 452 | 439 | ||
| 453 | if (function.err_msg) |em| { | 440 | if (function.err_msg) |em| { |
| 454 | return FnResult{ .fail = em }; | 441 | return Result{ .fail = em }; |
| 455 | } else { | 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,59 +4240,56 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier |
| 4253 | 4240 | ||
| 4254 | // Due to incremental compilation, how function calls are generated depends | 4241 | // Due to incremental compilation, how function calls are generated depends |
| 4255 | // on linking. | 4242 | // on linking. |
| 4256 | switch (self.bin_file.tag) { | 4243 | if (self.air.value(callee)) |func_value| { |
| 4257 | .elf => { | 4244 | if (func_value.castTag(.function)) |func_payload| { |
| 4258 | if (self.air.value(callee)) |func_value| { | 4245 | const func = func_payload.data; |
| 4259 | if (func_value.castTag(.function)) |func_payload| { | 4246 | |
| 4260 | const func = func_payload.data; | 4247 | if (self.bin_file.cast(link.File.Elf)) |elf_file| { |
| 4261 | const ptr_bits = self.target.cpu.arch.ptrBitWidth(); | 4248 | const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl); |
| 4262 | const ptr_bytes: u64 = @divExact(ptr_bits, 8); | 4249 | const atom = elf_file.getAtom(atom_index); |
| 4263 | const mod = self.bin_file.options.module.?; | 4250 | const got_addr = @intCast(u32, atom.getOffsetTableAddress(elf_file)); |
| 4264 | const fn_owner_decl = mod.declPtr(func.owner_decl); | 4251 | try self.genSetReg(Type.initTag(.usize), .lr, .{ .memory = got_addr }); |
| 4265 | const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: { | 4252 | } else if (self.bin_file.cast(link.File.MachO)) |_| { |
| 4266 | const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?]; | 4253 | unreachable; // unsupported architecture for MachO |
| 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 | } | ||
| 4275 | } else { | 4254 | } else { |
| 4276 | assert(ty.zigTypeTag() == .Pointer); | 4255 | return self.fail("TODO implement call on {s} for {s}", .{ |
| 4277 | const mcv = try self.resolveInst(callee); | 4256 | @tagName(self.bin_file.tag), |
| 4278 | 4257 | @tagName(self.target.cpu.arch), | |
| 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 }, | ||
| 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 | }, | 4260 | } else if (func_value.castTag(.extern_fn)) |_| { |
| 4305 | .macho => unreachable, // unsupported architecture for MachO | 4261 | return self.fail("TODO implement calling extern functions", .{}); |
| 4306 | .coff => return self.fail("TODO implement call in COFF for {}", .{self.target.cpu.arch}), | 4262 | } else { |
| 4307 | .plan9 => return self.fail("TODO implement call on plan9 for {}", .{self.target.cpu.arch}), | 4263 | return self.fail("TODO implement calling bitcasted functions", .{}); |
| 4308 | else => unreachable, | 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 | const result: MCValue = result: { | 4295 | const result: MCValue = result: { |
| ... | @@ -6086,16 +6070,17 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne | ... | @@ -6086,16 +6070,17 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne |
| 6086 | mod.markDeclAlive(decl); | 6070 | mod.markDeclAlive(decl); |
| 6087 | 6071 | ||
| 6088 | if (self.bin_file.cast(link.File.Elf)) |elf_file| { | 6072 | if (self.bin_file.cast(link.File.Elf)) |elf_file| { |
| 6089 | const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?]; | 6073 | const atom_index = try elf_file.getOrCreateAtomForDecl(decl_index); |
| 6090 | const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes; | 6074 | const atom = elf_file.getAtom(atom_index); |
| 6091 | return MCValue{ .memory = got_addr }; | 6075 | return MCValue{ .memory = atom.getOffsetTableAddress(elf_file) }; |
| 6092 | } else if (self.bin_file.cast(link.File.MachO)) |_| { | 6076 | } else if (self.bin_file.cast(link.File.MachO)) |_| { |
| 6093 | unreachable; // unsupported architecture for MachO | 6077 | unreachable; // unsupported architecture for MachO |
| 6094 | } else if (self.bin_file.cast(link.File.Coff)) |_| { | 6078 | } else if (self.bin_file.cast(link.File.Coff)) |_| { |
| 6095 | return self.fail("TODO codegen COFF const Decl pointer", .{}); | 6079 | return self.fail("TODO codegen COFF const Decl pointer", .{}); |
| 6096 | } else if (self.bin_file.cast(link.File.Plan9)) |p9| { | 6080 | } else if (self.bin_file.cast(link.File.Plan9)) |p9| { |
| 6097 | try p9.seeDecl(decl_index); | 6081 | const decl_block_index = try p9.seeDecl(decl_index); |
| 6098 | const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes; | 6082 | const decl_block = p9.getDeclBlock(decl_block_index); |
| 6083 | const got_addr = p9.bases.data + decl_block.got_index.? * ptr_bytes; | ||
| 6099 | return MCValue{ .memory = got_addr }; | 6084 | return MCValue{ .memory = got_addr }; |
| 6100 | } else { | 6085 | } else { |
| 6101 | return self.fail("TODO codegen non-ELF const Decl pointer", .{}); | 6086 | return self.fail("TODO codegen non-ELF const Decl pointer", .{}); |
| ... | @@ -6109,8 +6094,7 @@ fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue { | ... | @@ -6109,8 +6094,7 @@ fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue { |
| 6109 | return self.fail("lowering unnamed constant failed: {s}", .{@errorName(err)}); | 6094 | return self.fail("lowering unnamed constant failed: {s}", .{@errorName(err)}); |
| 6110 | }; | 6095 | }; |
| 6111 | if (self.bin_file.cast(link.File.Elf)) |elf_file| { | 6096 | if (self.bin_file.cast(link.File.Elf)) |elf_file| { |
| 6112 | const vaddr = elf_file.local_symbols.items[local_sym_index].st_value; | 6097 | return MCValue{ .memory = elf_file.getSymbol(local_sym_index).st_value }; |
| 6113 | return MCValue{ .memory = vaddr }; | ||
| 6114 | } else if (self.bin_file.cast(link.File.MachO)) |_| { | 6098 | } else if (self.bin_file.cast(link.File.MachO)) |_| { |
| 6115 | unreachable; | 6099 | unreachable; |
| 6116 | } else if (self.bin_file.cast(link.File.Coff)) |_| { | 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,7 +22,7 @@ const leb128 = std.leb; |
| 22 | const log = std.log.scoped(.codegen); | 22 | const log = std.log.scoped(.codegen); |
| 23 | const build_options = @import("build_options"); | 23 | const build_options = @import("build_options"); |
| 24 | 24 | ||
| 25 | const FnResult = @import("../../codegen.zig").FnResult; | 25 | const Result = @import("../../codegen.zig").Result; |
| 26 | const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError; | 26 | const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError; |
| 27 | const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; | 27 | const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; |
| 28 | 28 | ||
| ... | @@ -225,7 +225,7 @@ pub fn generate( | ... | @@ -225,7 +225,7 @@ pub fn generate( |
| 225 | liveness: Liveness, | 225 | liveness: Liveness, |
| 226 | code: *std.ArrayList(u8), | 226 | code: *std.ArrayList(u8), |
| 227 | debug_output: DebugInfoOutput, | 227 | debug_output: DebugInfoOutput, |
| 228 | ) GenerateSymbolError!FnResult { | 228 | ) GenerateSymbolError!Result { |
| 229 | if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) { | 229 | if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) { |
| 230 | @panic("Attempted to compile for architecture that was disabled by build configuration"); | 230 | @panic("Attempted to compile for architecture that was disabled by build configuration"); |
| 231 | } | 231 | } |
| ... | @@ -268,8 +268,8 @@ pub fn generate( | ... | @@ -268,8 +268,8 @@ pub fn generate( |
| 268 | defer function.exitlude_jump_relocs.deinit(bin_file.allocator); | 268 | defer function.exitlude_jump_relocs.deinit(bin_file.allocator); |
| 269 | 269 | ||
| 270 | var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) { | 270 | var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) { |
| 271 | error.CodegenFail => return FnResult{ .fail = function.err_msg.? }, | 271 | error.CodegenFail => return Result{ .fail = function.err_msg.? }, |
| 272 | error.OutOfRegisters => return FnResult{ | 272 | error.OutOfRegisters => return Result{ |
| 273 | .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), | 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 | else => |e| return e, | 275 | else => |e| return e, |
| ... | @@ -282,8 +282,8 @@ pub fn generate( | ... | @@ -282,8 +282,8 @@ pub fn generate( |
| 282 | function.max_end_stack = call_info.stack_byte_count; | 282 | function.max_end_stack = call_info.stack_byte_count; |
| 283 | 283 | ||
| 284 | function.gen() catch |err| switch (err) { | 284 | function.gen() catch |err| switch (err) { |
| 285 | error.CodegenFail => return FnResult{ .fail = function.err_msg.? }, | 285 | error.CodegenFail => return Result{ .fail = function.err_msg.? }, |
| 286 | error.OutOfRegisters => return FnResult{ | 286 | error.OutOfRegisters => return Result{ |
| 287 | .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), | 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 | else => |e| return e, | 289 | else => |e| return e, |
| ... | @@ -309,14 +309,14 @@ pub fn generate( | ... | @@ -309,14 +309,14 @@ pub fn generate( |
| 309 | defer emit.deinit(); | 309 | defer emit.deinit(); |
| 310 | 310 | ||
| 311 | emit.emitMir() catch |err| switch (err) { | 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 | else => |e| return e, | 313 | else => |e| return e, |
| 314 | }; | 314 | }; |
| 315 | 315 | ||
| 316 | if (function.err_msg) |em| { | 316 | if (function.err_msg) |em| { |
| 317 | return FnResult{ .fail = em }; | 317 | return Result{ .fail = em }; |
| 318 | } else { | 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,13 +1615,9 @@ fn genArgDbgInfo(self: Self, inst: Air.Inst.Index, mcv: MCValue) !void { |
| 1615 | 1615 | ||
| 1616 | switch (self.debug_output) { | 1616 | switch (self.debug_output) { |
| 1617 | .dwarf => |dw| switch (mcv) { | 1617 | .dwarf => |dw| switch (mcv) { |
| 1618 | .register => |reg| try dw.genArgDbgInfo( | 1618 | .register => |reg| try dw.genArgDbgInfo(name, ty, self.mod_fn.owner_decl, .{ |
| 1619 | name, | 1619 | .register = reg.dwarfLocOp(), |
| 1620 | ty, | 1620 | }), |
| 1621 | self.bin_file.tag, | ||
| 1622 | self.mod_fn.owner_decl, | ||
| 1623 | .{ .register = reg.dwarfLocOp() }, | ||
| 1624 | ), | ||
| 1625 | .stack_offset => {}, | 1621 | .stack_offset => {}, |
| 1626 | else => {}, | 1622 | else => {}, |
| 1627 | }, | 1623 | }, |
| ... | @@ -1721,16 +1717,9 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier | ... | @@ -1721,16 +1717,9 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier |
| 1721 | if (self.air.value(callee)) |func_value| { | 1717 | if (self.air.value(callee)) |func_value| { |
| 1722 | if (func_value.castTag(.function)) |func_payload| { | 1718 | if (func_value.castTag(.function)) |func_payload| { |
| 1723 | const func = func_payload.data; | 1719 | const func = func_payload.data; |
| 1724 | 1720 | const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl); | |
| 1725 | const ptr_bits = self.target.cpu.arch.ptrBitWidth(); | 1721 | const atom = elf_file.getAtom(atom_index); |
| 1726 | const ptr_bytes: u64 = @divExact(ptr_bits, 8); | 1722 | const got_addr = @intCast(u32, atom.getOffsetTableAddress(elf_file)); |
| 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 | |||
| 1734 | try self.genSetReg(Type.initTag(.usize), .ra, .{ .memory = got_addr }); | 1723 | try self.genSetReg(Type.initTag(.usize), .ra, .{ .memory = got_addr }); |
| 1735 | _ = try self.addInst(.{ | 1724 | _ = try self.addInst(.{ |
| 1736 | .tag = .jalr, | 1725 | .tag = .jalr, |
| ... | @@ -2557,18 +2546,17 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne | ... | @@ -2557,18 +2546,17 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne |
| 2557 | const decl = mod.declPtr(decl_index); | 2546 | const decl = mod.declPtr(decl_index); |
| 2558 | mod.markDeclAlive(decl); | 2547 | mod.markDeclAlive(decl); |
| 2559 | if (self.bin_file.cast(link.File.Elf)) |elf_file| { | 2548 | if (self.bin_file.cast(link.File.Elf)) |elf_file| { |
| 2560 | const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?]; | 2549 | const atom_index = try elf_file.getOrCreateAtomForDecl(decl_index); |
| 2561 | const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes; | 2550 | const atom = elf_file.getAtom(atom_index); |
| 2562 | return MCValue{ .memory = got_addr }; | 2551 | return MCValue{ .memory = atom.getOffsetTableAddress(elf_file) }; |
| 2563 | } else if (self.bin_file.cast(link.File.MachO)) |_| { | 2552 | } else if (self.bin_file.cast(link.File.MachO)) |_| { |
| 2564 | // TODO I'm hacking my way through here by repurposing .memory for storing | 2553 | unreachable; |
| 2565 | // index to the GOT target symbol index. | ||
| 2566 | return MCValue{ .memory = decl.link.macho.sym_index }; | ||
| 2567 | } else if (self.bin_file.cast(link.File.Coff)) |_| { | 2554 | } else if (self.bin_file.cast(link.File.Coff)) |_| { |
| 2568 | return self.fail("TODO codegen COFF const Decl pointer", .{}); | 2555 | return self.fail("TODO codegen COFF const Decl pointer", .{}); |
| 2569 | } else if (self.bin_file.cast(link.File.Plan9)) |p9| { | 2556 | } else if (self.bin_file.cast(link.File.Plan9)) |p9| { |
| 2570 | try p9.seeDecl(decl_index); | 2557 | const decl_block_index = try p9.seeDecl(decl_index); |
| 2571 | const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes; | 2558 | const decl_block = p9.getDeclBlock(decl_block_index); |
| 2559 | const got_addr = p9.bases.data + decl_block.got_index.? * ptr_bytes; | ||
| 2572 | return MCValue{ .memory = got_addr }; | 2560 | return MCValue{ .memory = got_addr }; |
| 2573 | } else { | 2561 | } else { |
| 2574 | return self.fail("TODO codegen non-ELF const Decl pointer", .{}); | 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,7 +20,7 @@ const Emit = @import("Emit.zig"); |
| 20 | const Liveness = @import("../../Liveness.zig"); | 20 | const Liveness = @import("../../Liveness.zig"); |
| 21 | const Type = @import("../../type.zig").Type; | 21 | const Type = @import("../../type.zig").Type; |
| 22 | const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError; | 22 | const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError; |
| 23 | const FnResult = @import("../../codegen.zig").FnResult; | 23 | const Result = @import("../../codegen.zig").Result; |
| 24 | const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; | 24 | const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; |
| 25 | 25 | ||
| 26 | const build_options = @import("build_options"); | 26 | const build_options = @import("build_options"); |
| ... | @@ -265,7 +265,7 @@ pub fn generate( | ... | @@ -265,7 +265,7 @@ pub fn generate( |
| 265 | liveness: Liveness, | 265 | liveness: Liveness, |
| 266 | code: *std.ArrayList(u8), | 266 | code: *std.ArrayList(u8), |
| 267 | debug_output: DebugInfoOutput, | 267 | debug_output: DebugInfoOutput, |
| 268 | ) GenerateSymbolError!FnResult { | 268 | ) GenerateSymbolError!Result { |
| 269 | if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) { | 269 | if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) { |
| 270 | @panic("Attempted to compile for architecture that was disabled by build configuration"); | 270 | @panic("Attempted to compile for architecture that was disabled by build configuration"); |
| 271 | } | 271 | } |
| ... | @@ -310,8 +310,8 @@ pub fn generate( | ... | @@ -310,8 +310,8 @@ pub fn generate( |
| 310 | defer function.exitlude_jump_relocs.deinit(bin_file.allocator); | 310 | defer function.exitlude_jump_relocs.deinit(bin_file.allocator); |
| 311 | 311 | ||
| 312 | var call_info = function.resolveCallingConventionValues(fn_type, .callee) catch |err| switch (err) { | 312 | var call_info = function.resolveCallingConventionValues(fn_type, .callee) catch |err| switch (err) { |
| 313 | error.CodegenFail => return FnResult{ .fail = function.err_msg.? }, | 313 | error.CodegenFail => return Result{ .fail = function.err_msg.? }, |
| 314 | error.OutOfRegisters => return FnResult{ | 314 | error.OutOfRegisters => return Result{ |
| 315 | .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), | 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 | else => |e| return e, | 317 | else => |e| return e, |
| ... | @@ -324,8 +324,8 @@ pub fn generate( | ... | @@ -324,8 +324,8 @@ pub fn generate( |
| 324 | function.max_end_stack = call_info.stack_byte_count; | 324 | function.max_end_stack = call_info.stack_byte_count; |
| 325 | 325 | ||
| 326 | function.gen() catch |err| switch (err) { | 326 | function.gen() catch |err| switch (err) { |
| 327 | error.CodegenFail => return FnResult{ .fail = function.err_msg.? }, | 327 | error.CodegenFail => return Result{ .fail = function.err_msg.? }, |
| 328 | error.OutOfRegisters => return FnResult{ | 328 | error.OutOfRegisters => return Result{ |
| 329 | .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), | 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 | else => |e| return e, | 331 | else => |e| return e, |
| ... | @@ -351,14 +351,14 @@ pub fn generate( | ... | @@ -351,14 +351,14 @@ pub fn generate( |
| 351 | defer emit.deinit(); | 351 | defer emit.deinit(); |
| 352 | 352 | ||
| 353 | emit.emitMir() catch |err| switch (err) { | 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 | else => |e| return e, | 355 | else => |e| return e, |
| 356 | }; | 356 | }; |
| 357 | 357 | ||
| 358 | if (function.err_msg) |em| { | 358 | if (function.err_msg) |em| { |
| 359 | return FnResult{ .fail = em }; | 359 | return Result{ .fail = em }; |
| 360 | } else { | 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,12 +1216,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier |
| 1216 | if (self.bin_file.tag == link.File.Elf.base_tag) { | 1216 | if (self.bin_file.tag == link.File.Elf.base_tag) { |
| 1217 | if (func_value.castTag(.function)) |func_payload| { | 1217 | if (func_value.castTag(.function)) |func_payload| { |
| 1218 | const func = func_payload.data; | 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 | const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: { | 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.?]; | 1220 | const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl); |
| 1223 | const mod = self.bin_file.options.module.?; | 1221 | const atom = elf_file.getAtom(atom_index); |
| 1224 | break :blk @intCast(u32, got.p_vaddr + mod.declPtr(func.owner_decl).link.elf.offset_table_index * ptr_bytes); | 1222 | break :blk @intCast(u32, atom.getOffsetTableAddress(elf_file)); |
| 1225 | } else unreachable; | 1223 | } else unreachable; |
| 1226 | 1224 | ||
| 1227 | try self.genSetReg(Type.initTag(.usize), .o7, .{ .memory = got_addr }); | 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,13 +3412,9 @@ fn genArgDbgInfo(self: Self, inst: Air.Inst.Index, mcv: MCValue) !void { |
| 3414 | 3412 | ||
| 3415 | switch (self.debug_output) { | 3413 | switch (self.debug_output) { |
| 3416 | .dwarf => |dw| switch (mcv) { | 3414 | .dwarf => |dw| switch (mcv) { |
| 3417 | .register => |reg| try dw.genArgDbgInfo( | 3415 | .register => |reg| try dw.genArgDbgInfo(name, ty, self.mod_fn.owner_decl, .{ |
| 3418 | name, | 3416 | .register = reg.dwarfLocOp(), |
| 3419 | ty, | 3417 | }), |
| 3420 | self.bin_file.tag, | ||
| 3421 | self.mod_fn.owner_decl, | ||
| 3422 | .{ .register = reg.dwarfLocOp() }, | ||
| 3423 | ), | ||
| 3424 | else => {}, | 3418 | else => {}, |
| 3425 | }, | 3419 | }, |
| 3426 | else => {}, | 3420 | else => {}, |
| ... | @@ -4193,9 +4187,6 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo | ... | @@ -4193,9 +4187,6 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo |
| 4193 | } | 4187 | } |
| 4194 | 4188 | ||
| 4195 | fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) InnerError!MCValue { | 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 | // TODO this feels clunky. Perhaps we should check for it in `genTypedValue`? | 4190 | // TODO this feels clunky. Perhaps we should check for it in `genTypedValue`? |
| 4200 | if (tv.ty.zigTypeTag() == .Pointer) blk: { | 4191 | if (tv.ty.zigTypeTag() == .Pointer) blk: { |
| 4201 | if (tv.ty.castPtrToFn()) |_| break :blk; | 4192 | if (tv.ty.castPtrToFn()) |_| break :blk; |
| ... | @@ -4209,9 +4200,9 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne | ... | @@ -4209,9 +4200,9 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne |
| 4209 | 4200 | ||
| 4210 | mod.markDeclAlive(decl); | 4201 | mod.markDeclAlive(decl); |
| 4211 | if (self.bin_file.cast(link.File.Elf)) |elf_file| { | 4202 | if (self.bin_file.cast(link.File.Elf)) |elf_file| { |
| 4212 | const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?]; | 4203 | const atom_index = try elf_file.getOrCreateAtomForDecl(decl_index); |
| 4213 | const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes; | 4204 | const atom = elf_file.getAtom(atom_index); |
| 4214 | return MCValue{ .memory = got_addr }; | 4205 | return MCValue{ .memory = atom.getOffsetTableAddress(elf_file) }; |
| 4215 | } else { | 4206 | } else { |
| 4216 | return self.fail("TODO codegen non-ELF const Decl pointer", .{}); | 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,13 +627,6 @@ test "Wasm - buildOpcode" { |
| 627 | try testing.expectEqual(@as(wasm.Opcode, .f64_reinterpret_i64), f64_reinterpret_i64); | 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 | /// Hashmap to store generated `WValue` for each `Air.Inst.Ref` | 630 | /// Hashmap to store generated `WValue` for each `Air.Inst.Ref` |
| 638 | pub const ValueTable = std.AutoArrayHashMapUnmanaged(Air.Inst.Ref, WValue); | 631 | pub const ValueTable = std.AutoArrayHashMapUnmanaged(Air.Inst.Ref, WValue); |
| 639 | 632 | ||
| ... | @@ -1171,7 +1164,7 @@ pub fn generate( | ... | @@ -1171,7 +1164,7 @@ pub fn generate( |
| 1171 | liveness: Liveness, | 1164 | liveness: Liveness, |
| 1172 | code: *std.ArrayList(u8), | 1165 | code: *std.ArrayList(u8), |
| 1173 | debug_output: codegen.DebugInfoOutput, | 1166 | debug_output: codegen.DebugInfoOutput, |
| 1174 | ) codegen.GenerateSymbolError!codegen.FnResult { | 1167 | ) codegen.GenerateSymbolError!codegen.Result { |
| 1175 | _ = src_loc; | 1168 | _ = src_loc; |
| 1176 | var code_gen: CodeGen = .{ | 1169 | var code_gen: CodeGen = .{ |
| 1177 | .gpa = bin_file.allocator, | 1170 | .gpa = bin_file.allocator, |
| ... | @@ -1190,18 +1183,18 @@ pub fn generate( | ... | @@ -1190,18 +1183,18 @@ pub fn generate( |
| 1190 | defer code_gen.deinit(); | 1183 | defer code_gen.deinit(); |
| 1191 | 1184 | ||
| 1192 | genFunc(&code_gen) catch |err| switch (err) { | 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 | else => |e| return e, | 1187 | else => |e| return e, |
| 1195 | }; | 1188 | }; |
| 1196 | 1189 | ||
| 1197 | return codegen.FnResult{ .appended = {} }; | 1190 | return codegen.Result.ok; |
| 1198 | } | 1191 | } |
| 1199 | 1192 | ||
| 1200 | fn genFunc(func: *CodeGen) InnerError!void { | 1193 | fn genFunc(func: *CodeGen) InnerError!void { |
| 1201 | const fn_info = func.decl.ty.fnInfo(); | 1194 | const fn_info = func.decl.ty.fnInfo(); |
| 1202 | var func_type = try genFunctype(func.gpa, fn_info.cc, fn_info.param_types, fn_info.return_type, func.target); | 1195 | var func_type = try genFunctype(func.gpa, fn_info.cc, fn_info.param_types, fn_info.return_type, func.target); |
| 1203 | defer func_type.deinit(func.gpa); | 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 | var cc_result = try func.resolveCallingConventionValues(func.decl.ty); | 1199 | var cc_result = try func.resolveCallingConventionValues(func.decl.ty); |
| 1207 | defer cc_result.deinit(func.gpa); | 1200 | defer cc_result.deinit(func.gpa); |
| ... | @@ -1276,10 +1269,10 @@ fn genFunc(func: *CodeGen) InnerError!void { | ... | @@ -1276,10 +1269,10 @@ fn genFunc(func: *CodeGen) InnerError!void { |
| 1276 | 1269 | ||
| 1277 | var emit: Emit = .{ | 1270 | var emit: Emit = .{ |
| 1278 | .mir = mir, | 1271 | .mir = mir, |
| 1279 | .bin_file = &func.bin_file.base, | 1272 | .bin_file = func.bin_file, |
| 1280 | .code = func.code, | 1273 | .code = func.code, |
| 1281 | .locals = func.locals.items, | 1274 | .locals = func.locals.items, |
| 1282 | .decl = func.decl, | 1275 | .decl_index = func.decl_index, |
| 1283 | .dbg_output = func.debug_output, | 1276 | .dbg_output = func.debug_output, |
| 1284 | .prev_di_line = 0, | 1277 | .prev_di_line = 0, |
| 1285 | .prev_di_column = 0, | 1278 | .prev_di_column = 0, |
| ... | @@ -1713,9 +1706,11 @@ fn isByRef(ty: Type, target: std.Target) bool { | ... | @@ -1713,9 +1706,11 @@ fn isByRef(ty: Type, target: std.Target) bool { |
| 1713 | return true; | 1706 | return true; |
| 1714 | }, | 1707 | }, |
| 1715 | .Optional => { | 1708 | .Optional => { |
| 1716 | if (ty.optionalReprIsPayload()) return false; | 1709 | if (ty.isPtrLikeOptional()) return false; |
| 1717 | var buf: Type.Payload.ElemType = undefined; | 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 | .Pointer => { | 1715 | .Pointer => { |
| 1721 | // Slices act like struct and will be passed by reference | 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,27 +2117,31 @@ fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModif |
| 2122 | const fn_info = fn_ty.fnInfo(); | 2117 | const fn_info = fn_ty.fnInfo(); |
| 2123 | const first_param_sret = firstParamSRet(fn_info.cc, fn_info.return_type, func.target); | 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 | const func_val = func.air.value(pl_op.operand) orelse break :blk null; | 2121 | const func_val = func.air.value(pl_op.operand) orelse break :blk null; |
| 2127 | const module = func.bin_file.base.options.module.?; | 2122 | const module = func.bin_file.base.options.module.?; |
| 2128 | 2123 | ||
| 2129 | if (func_val.castTag(.function)) |function| { | 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 | } else if (func_val.castTag(.extern_fn)) |extern_fn| { | 2127 | } else if (func_val.castTag(.extern_fn)) |extern_fn| { |
| 2132 | const ext_decl = module.declPtr(extern_fn.data.owner_decl); | 2128 | const ext_decl = module.declPtr(extern_fn.data.owner_decl); |
| 2133 | const ext_info = ext_decl.ty.fnInfo(); | 2129 | const ext_info = ext_decl.ty.fnInfo(); |
| 2134 | var func_type = try genFunctype(func.gpa, ext_info.cc, ext_info.param_types, ext_info.return_type, func.target); | 2130 | var func_type = try genFunctype(func.gpa, ext_info.cc, ext_info.param_types, ext_info.return_type, func.target); |
| 2135 | defer func_type.deinit(func.gpa); | 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 | try func.bin_file.addOrUpdateImport( | 2135 | try func.bin_file.addOrUpdateImport( |
| 2138 | mem.sliceTo(ext_decl.name, 0), | 2136 | mem.sliceTo(ext_decl.name, 0), |
| 2139 | ext_decl.link.wasm.sym_index, | 2137 | atom.getSymbolIndex().?, |
| 2140 | ext_decl.getExternFn().?.lib_name, | 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 | } else if (func_val.castTag(.decl_ref)) |decl_ref| { | 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 | return func.fail("Expected a function, but instead found type '{}'", .{func_val.tag()}); | 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,7 +2162,8 @@ fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModif |
| 2163 | } | 2162 | } |
| 2164 | 2163 | ||
| 2165 | if (callee) |direct| { | 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 | } else { | 2167 | } else { |
| 2168 | // in this case we call a function pointer | 2168 | // in this case we call a function pointer |
| 2169 | // so load its value onto the stack | 2169 | // so load its value onto the stack |
| ... | @@ -2476,7 +2476,7 @@ fn airArg(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | ... | @@ -2476,7 +2476,7 @@ fn airArg(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 2476 | .dwarf => |dwarf| { | 2476 | .dwarf => |dwarf| { |
| 2477 | const src_index = func.air.instructions.items(.data)[inst].arg.src_index; | 2477 | const src_index = func.air.instructions.items(.data)[inst].arg.src_index; |
| 2478 | const name = func.mod_fn.getParamName(func.bin_file.base.options.module.?, src_index); | 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 | .wasm_local = arg.local.value, | 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,8 +2759,10 @@ fn lowerDeclRefValue(func: *CodeGen, tv: TypedValue, decl_index: Module.Decl.Ind |
| 2759 | } | 2759 | } |
| 2760 | 2760 | ||
| 2761 | module.markDeclAlive(decl); | 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 | if (decl.ty.zigTypeTag() == .Fn) { | 2766 | if (decl.ty.zigTypeTag() == .Fn) { |
| 2765 | try func.bin_file.addTableFunction(target_sym_index); | 2767 | try func.bin_file.addTableFunction(target_sym_index); |
| 2766 | return WValue{ .function_index = target_sym_index }; | 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,14 +3871,20 @@ fn airIsNull(func: *CodeGen, inst: Air.Inst.Index, opcode: wasm.Opcode, op_kind: |
| 3869 | /// NOTE: Leaves the result on the stack | 3871 | /// NOTE: Leaves the result on the stack |
| 3870 | fn isNull(func: *CodeGen, operand: WValue, optional_ty: Type, opcode: wasm.Opcode) InnerError!WValue { | 3872 | fn isNull(func: *CodeGen, operand: WValue, optional_ty: Type, opcode: wasm.Opcode) InnerError!WValue { |
| 3871 | try func.emitWValue(operand); | 3873 | try func.emitWValue(operand); |
| 3874 | var buf: Type.Payload.ElemType = undefined; | ||
| 3875 | const payload_ty = optional_ty.optionalChild(&buf); | ||
| 3872 | if (!optional_ty.optionalReprIsPayload()) { | 3876 | if (!optional_ty.optionalReprIsPayload()) { |
| 3873 | var buf: Type.Payload.ElemType = undefined; | ||
| 3874 | const payload_ty = optional_ty.optionalChild(&buf); | ||
| 3875 | // When payload is zero-bits, we can treat operand as a value, rather than | 3877 | // When payload is zero-bits, we can treat operand as a value, rather than |
| 3876 | // a pointer to the stack value | 3878 | // a pointer to the stack value |
| 3877 | if (payload_ty.hasRuntimeBitsIgnoreComptime()) { | 3879 | if (payload_ty.hasRuntimeBitsIgnoreComptime()) { |
| 3878 | try func.addMemArg(.i32_load8_u, .{ .offset = operand.offset(), .alignment = 1 }); | 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 | // Compare the null value with '0' | 3890 | // Compare the null value with '0' |
| ... | @@ -5539,7 +5547,7 @@ fn airDbgVar(func: *CodeGen, inst: Air.Inst.Index, is_ptr: bool) !void { | ... | @@ -5539,7 +5547,7 @@ fn airDbgVar(func: *CodeGen, inst: Air.Inst.Index, is_ptr: bool) !void { |
| 5539 | break :blk .nop; | 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 | func.finishAir(inst, .none, &.{}); | 5552 | func.finishAir(inst, .none, &.{}); |
| 5545 | } | 5553 | } |
src/arch/wasm/Emit.zig+18-11| ... | @@ -11,8 +11,8 @@ const leb128 = std.leb; | ... | @@ -11,8 +11,8 @@ const leb128 = std.leb; |
| 11 | 11 | ||
| 12 | /// Contains our list of instructions | 12 | /// Contains our list of instructions |
| 13 | mir: Mir, | 13 | mir: Mir, |
| 14 | /// Reference to the file handler | 14 | /// Reference to the Wasm module linker |
| 15 | bin_file: *link.File, | 15 | bin_file: *link.File.Wasm, |
| 16 | /// Possible error message. When set, the value is allocated and | 16 | /// Possible error message. When set, the value is allocated and |
| 17 | /// must be freed manually. | 17 | /// must be freed manually. |
| 18 | error_msg: ?*Module.ErrorMsg = null, | 18 | error_msg: ?*Module.ErrorMsg = null, |
| ... | @@ -21,7 +21,7 @@ code: *std.ArrayList(u8), | ... | @@ -21,7 +21,7 @@ code: *std.ArrayList(u8), |
| 21 | /// List of allocated locals. | 21 | /// List of allocated locals. |
| 22 | locals: []const u8, | 22 | locals: []const u8, |
| 23 | /// The declaration that code is being generated for. | 23 | /// The declaration that code is being generated for. |
| 24 | decl: *Module.Decl, | 24 | decl_index: Module.Decl.Index, |
| 25 | 25 | ||
| 26 | // Debug information | 26 | // Debug information |
| 27 | /// Holds the debug information for this emission | 27 | /// Holds the debug information for this emission |
| ... | @@ -252,8 +252,8 @@ fn offset(self: Emit) u32 { | ... | @@ -252,8 +252,8 @@ fn offset(self: Emit) u32 { |
| 252 | fn fail(emit: *Emit, comptime format: []const u8, args: anytype) InnerError { | 252 | fn fail(emit: *Emit, comptime format: []const u8, args: anytype) InnerError { |
| 253 | @setCold(true); | 253 | @setCold(true); |
| 254 | std.debug.assert(emit.error_msg == null); | 254 | std.debug.assert(emit.error_msg == null); |
| 255 | // TODO: Determine the source location. | 255 | const mod = emit.bin_file.base.options.module.?; |
| 256 | emit.error_msg = try Module.ErrorMsg.create(emit.bin_file.allocator, emit.decl.srcLoc(), format, args); | 256 | emit.error_msg = try Module.ErrorMsg.create(emit.bin_file.base.allocator, mod.declPtr(emit.decl_index).srcLoc(), format, args); |
| 257 | return error.EmitFail; | 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,8 +304,9 @@ fn emitGlobal(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) !void { |
| 304 | const global_offset = emit.offset(); | 304 | const global_offset = emit.offset(); |
| 305 | try emit.code.appendSlice(&buf); | 305 | try emit.code.appendSlice(&buf); |
| 306 | 306 | ||
| 307 | // globals can have index 0 as it represents the stack pointer | 307 | const atom_index = emit.bin_file.decls.get(emit.decl_index).?; |
| 308 | try emit.decl.link.wasm.relocs.append(emit.bin_file.allocator, .{ | 308 | const atom = emit.bin_file.getAtomPtr(atom_index); |
| 309 | try atom.relocs.append(emit.bin_file.base.allocator, .{ | ||
| 309 | .index = label, | 310 | .index = label, |
| 310 | .offset = global_offset, | 311 | .offset = global_offset, |
| 311 | .relocation_type = .R_WASM_GLOBAL_INDEX_LEB, | 312 | .relocation_type = .R_WASM_GLOBAL_INDEX_LEB, |
| ... | @@ -361,7 +362,9 @@ fn emitCall(emit: *Emit, inst: Mir.Inst.Index) !void { | ... | @@ -361,7 +362,9 @@ fn emitCall(emit: *Emit, inst: Mir.Inst.Index) !void { |
| 361 | try emit.code.appendSlice(&buf); | 362 | try emit.code.appendSlice(&buf); |
| 362 | 363 | ||
| 363 | if (label != 0) { | 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 | .offset = call_offset, | 368 | .offset = call_offset, |
| 366 | .index = label, | 369 | .index = label, |
| 367 | .relocation_type = .R_WASM_FUNCTION_INDEX_LEB, | 370 | .relocation_type = .R_WASM_FUNCTION_INDEX_LEB, |
| ... | @@ -387,7 +390,9 @@ fn emitFunctionIndex(emit: *Emit, inst: Mir.Inst.Index) !void { | ... | @@ -387,7 +390,9 @@ fn emitFunctionIndex(emit: *Emit, inst: Mir.Inst.Index) !void { |
| 387 | try emit.code.appendSlice(&buf); | 390 | try emit.code.appendSlice(&buf); |
| 388 | 391 | ||
| 389 | if (symbol_index != 0) { | 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 | .offset = index_offset, | 396 | .offset = index_offset, |
| 392 | .index = symbol_index, | 397 | .index = symbol_index, |
| 393 | .relocation_type = .R_WASM_TABLE_INDEX_SLEB, | 398 | .relocation_type = .R_WASM_TABLE_INDEX_SLEB, |
| ... | @@ -399,7 +404,7 @@ fn emitMemAddress(emit: *Emit, inst: Mir.Inst.Index) !void { | ... | @@ -399,7 +404,7 @@ fn emitMemAddress(emit: *Emit, inst: Mir.Inst.Index) !void { |
| 399 | const extra_index = emit.mir.instructions.items(.data)[inst].payload; | 404 | const extra_index = emit.mir.instructions.items(.data)[inst].payload; |
| 400 | const mem = emit.mir.extraData(Mir.Memory, extra_index).data; | 405 | const mem = emit.mir.extraData(Mir.Memory, extra_index).data; |
| 401 | const mem_offset = emit.offset() + 1; | 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 | if (is_wasm32) { | 408 | if (is_wasm32) { |
| 404 | try emit.code.append(std.wasm.opcode(.i32_const)); | 409 | try emit.code.append(std.wasm.opcode(.i32_const)); |
| 405 | var buf: [5]u8 = undefined; | 410 | var buf: [5]u8 = undefined; |
| ... | @@ -413,7 +418,9 @@ fn emitMemAddress(emit: *Emit, inst: Mir.Inst.Index) !void { | ... | @@ -413,7 +418,9 @@ fn emitMemAddress(emit: *Emit, inst: Mir.Inst.Index) !void { |
| 413 | } | 418 | } |
| 414 | 419 | ||
| 415 | if (mem.pointer != 0) { | 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 | .offset = mem_offset, | 424 | .offset = mem_offset, |
| 418 | .index = mem.pointer, | 425 | .index = mem.pointer, |
| 419 | .relocation_type = if (is_wasm32) .R_WASM_MEMORY_ADDR_LEB else .R_WASM_MEMORY_ADDR_LEB64, | 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,7 +16,7 @@ const Compilation = @import("../../Compilation.zig"); |
| 16 | const DebugInfoOutput = codegen.DebugInfoOutput; | 16 | const DebugInfoOutput = codegen.DebugInfoOutput; |
| 17 | const DW = std.dwarf; | 17 | const DW = std.dwarf; |
| 18 | const ErrorMsg = Module.ErrorMsg; | 18 | const ErrorMsg = Module.ErrorMsg; |
| 19 | const FnResult = codegen.FnResult; | 19 | const Result = codegen.Result; |
| 20 | const GenerateSymbolError = codegen.GenerateSymbolError; | 20 | const GenerateSymbolError = codegen.GenerateSymbolError; |
| 21 | const Emit = @import("Emit.zig"); | 21 | const Emit = @import("Emit.zig"); |
| 22 | const Liveness = @import("../../Liveness.zig"); | 22 | const Liveness = @import("../../Liveness.zig"); |
| ... | @@ -257,7 +257,7 @@ pub fn generate( | ... | @@ -257,7 +257,7 @@ pub fn generate( |
| 257 | liveness: Liveness, | 257 | liveness: Liveness, |
| 258 | code: *std.ArrayList(u8), | 258 | code: *std.ArrayList(u8), |
| 259 | debug_output: DebugInfoOutput, | 259 | debug_output: DebugInfoOutput, |
| 260 | ) GenerateSymbolError!FnResult { | 260 | ) GenerateSymbolError!Result { |
| 261 | if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) { | 261 | if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) { |
| 262 | @panic("Attempted to compile for architecture that was disabled by build configuration"); | 262 | @panic("Attempted to compile for architecture that was disabled by build configuration"); |
| 263 | } | 263 | } |
| ... | @@ -305,8 +305,8 @@ pub fn generate( | ... | @@ -305,8 +305,8 @@ pub fn generate( |
| 305 | defer if (builtin.mode == .Debug) function.mir_to_air_map.deinit(); | 305 | defer if (builtin.mode == .Debug) function.mir_to_air_map.deinit(); |
| 306 | 306 | ||
| 307 | var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) { | 307 | var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) { |
| 308 | error.CodegenFail => return FnResult{ .fail = function.err_msg.? }, | 308 | error.CodegenFail => return Result{ .fail = function.err_msg.? }, |
| 309 | error.OutOfRegisters => return FnResult{ | 309 | error.OutOfRegisters => return Result{ |
| 310 | .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), | 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 | else => |e| return e, | 312 | else => |e| return e, |
| ... | @@ -319,8 +319,8 @@ pub fn generate( | ... | @@ -319,8 +319,8 @@ pub fn generate( |
| 319 | function.max_end_stack = call_info.stack_byte_count; | 319 | function.max_end_stack = call_info.stack_byte_count; |
| 320 | 320 | ||
| 321 | function.gen() catch |err| switch (err) { | 321 | function.gen() catch |err| switch (err) { |
| 322 | error.CodegenFail => return FnResult{ .fail = function.err_msg.? }, | 322 | error.CodegenFail => return Result{ .fail = function.err_msg.? }, |
| 323 | error.OutOfRegisters => return FnResult{ | 323 | error.OutOfRegisters => return Result{ |
| 324 | .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), | 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 | else => |e| return e, | 326 | else => |e| return e, |
| ... | @@ -345,14 +345,14 @@ pub fn generate( | ... | @@ -345,14 +345,14 @@ pub fn generate( |
| 345 | }; | 345 | }; |
| 346 | defer emit.deinit(); | 346 | defer emit.deinit(); |
| 347 | emit.lowerMir() catch |err| switch (err) { | 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 | else => |e| return e, | 349 | else => |e| return e, |
| 350 | }; | 350 | }; |
| 351 | 351 | ||
| 352 | if (function.err_msg) |em| { | 352 | if (function.err_msg) |em| { |
| 353 | return FnResult{ .fail = em }; | 353 | return Result{ .fail = em }; |
| 354 | } else { | 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,12 +2668,13 @@ fn loadMemPtrIntoRegister(self: *Self, reg: Register, ptr_ty: Type, ptr: MCValue |
| 2668 | switch (ptr) { | 2668 | switch (ptr) { |
| 2669 | .linker_load => |load_struct| { | 2669 | .linker_load => |load_struct| { |
| 2670 | const abi_size = @intCast(u32, ptr_ty.abiSize(self.target.*)); | 2670 | const abi_size = @intCast(u32, ptr_ty.abiSize(self.target.*)); |
| 2671 | const mod = self.bin_file.options.module.?; | 2671 | const atom_index = if (self.bin_file.cast(link.File.MachO)) |macho_file| blk: { |
| 2672 | const fn_owner_decl = mod.declPtr(self.mod_fn.owner_decl); | 2672 | const atom = try macho_file.getOrCreateAtomForDecl(self.mod_fn.owner_decl); |
| 2673 | const atom_index = if (self.bin_file.tag == link.File.MachO.base_tag) | 2673 | break :blk macho_file.getAtom(atom).getSymbolIndex().?; |
| 2674 | fn_owner_decl.link.macho.sym_index | 2674 | } else if (self.bin_file.cast(link.File.Coff)) |coff_file| blk: { |
| 2675 | else | 2675 | const atom = try coff_file.getOrCreateAtomForDecl(self.mod_fn.owner_decl); |
| 2676 | fn_owner_decl.link.coff.sym_index; | 2676 | break :blk coff_file.getAtom(atom).getSymbolIndex().?; |
| 2677 | } else unreachable; | ||
| 2677 | const flags: u2 = switch (load_struct.type) { | 2678 | const flags: u2 = switch (load_struct.type) { |
| 2678 | .got => 0b00, | 2679 | .got => 0b00, |
| 2679 | .direct => 0b01, | 2680 | .direct => 0b01, |
| ... | @@ -3835,7 +3836,7 @@ fn genArgDbgInfo(self: Self, ty: Type, name: [:0]const u8, mcv: MCValue) !void { | ... | @@ -3835,7 +3836,7 @@ fn genArgDbgInfo(self: Self, ty: Type, name: [:0]const u8, mcv: MCValue) !void { |
| 3835 | }, | 3836 | }, |
| 3836 | else => unreachable, // not a valid function parameter | 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 | .plan9 => {}, | 3841 | .plan9 => {}, |
| 3841 | .none => {}, | 3842 | .none => {}, |
| ... | @@ -3875,7 +3876,7 @@ fn genVarDbgInfo( | ... | @@ -3875,7 +3876,7 @@ fn genVarDbgInfo( |
| 3875 | break :blk .nop; | 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 | .plan9 => {}, | 3881 | .plan9 => {}, |
| 3881 | .none => {}, | 3882 | .none => {}, |
| ... | @@ -3992,49 +3993,26 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier | ... | @@ -3992,49 +3993,26 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier |
| 3992 | // Due to incremental compilation, how function calls are generated depends | 3993 | // Due to incremental compilation, how function calls are generated depends |
| 3993 | // on linking. | 3994 | // on linking. |
| 3994 | const mod = self.bin_file.options.module.?; | 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| { |
| 3996 | if (self.air.value(callee)) |func_value| { | 3997 | if (func_value.castTag(.function)) |func_payload| { |
| 3997 | if (func_value.castTag(.function)) |func_payload| { | 3998 | const func = func_payload.data; |
| 3998 | const func = func_payload.data; | 3999 | |
| 3999 | const ptr_bits = self.target.cpu.arch.ptrBitWidth(); | 4000 | if (self.bin_file.cast(link.File.Elf)) |elf_file| { |
| 4000 | const ptr_bytes: u64 = @divExact(ptr_bits, 8); | 4001 | const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl); |
| 4001 | const fn_owner_decl = mod.declPtr(func.owner_decl); | 4002 | const atom = elf_file.getAtom(atom_index); |
| 4002 | const got_addr = blk: { | 4003 | const got_addr = @intCast(u32, atom.getOffsetTableAddress(elf_file)); |
| 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 | }; | ||
| 4006 | _ = try self.addInst(.{ | 4004 | _ = try self.addInst(.{ |
| 4007 | .tag = .call, | 4005 | .tag = .call, |
| 4008 | .ops = Mir.Inst.Ops.encode(.{ .flags = 0b01 }), | 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)) |_| { | 4009 | } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { |
| 4012 | return self.fail("TODO implement calling extern functions", .{}); | 4010 | const atom_index = try coff_file.getOrCreateAtomForDecl(func.owner_decl); |
| 4013 | } else { | 4011 | const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?; |
| 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); | ||
| 4034 | try self.genSetReg(Type.initTag(.usize), .rax, .{ | 4012 | try self.genSetReg(Type.initTag(.usize), .rax, .{ |
| 4035 | .linker_load = .{ | 4013 | .linker_load = .{ |
| 4036 | .type = .got, | 4014 | .type = .got, |
| 4037 | .sym_index = fn_owner_decl.link.coff.sym_index, | 4015 | .sym_index = sym_index, |
| 4038 | }, | 4016 | }, |
| 4039 | }); | 4017 | }); |
| 4040 | _ = try self.addInst(.{ | 4018 | _ = try self.addInst(.{ |
| ... | @@ -4045,19 +4023,12 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier | ... | @@ -4045,19 +4023,12 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier |
| 4045 | }), | 4023 | }), |
| 4046 | .data = undefined, | 4024 | .data = undefined, |
| 4047 | }); | 4025 | }); |
| 4048 | } else if (func_value.castTag(.extern_fn)) |func_payload| { | 4026 | } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { |
| 4049 | const extern_fn = func_payload.data; | 4027 | const atom_index = try macho_file.getOrCreateAtomForDecl(func.owner_decl); |
| 4050 | const decl_name = mod.declPtr(extern_fn.owner_decl).name; | 4028 | const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?; |
| 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)); | ||
| 4058 | try self.genSetReg(Type.initTag(.usize), .rax, .{ | 4029 | try self.genSetReg(Type.initTag(.usize), .rax, .{ |
| 4059 | .linker_load = .{ | 4030 | .linker_load = .{ |
| 4060 | .type = .import, | 4031 | .type = .got, |
| 4061 | .sym_index = sym_index, | 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,35 +4040,38 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier |
| 4069 | }), | 4040 | }), |
| 4070 | .data = undefined, | 4041 | .data = undefined, |
| 4071 | }); | 4042 | }); |
| 4072 | } else { | 4043 | } else if (self.bin_file.cast(link.File.Plan9)) |p9| { |
| 4073 | return self.fail("TODO implement calling bitcasted functions", .{}); | 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 { | 4066 | |
| 4076 | assert(ty.zigTypeTag() == .Pointer); | 4067 | if (self.bin_file.cast(link.File.Coff)) |coff_file| { |
| 4077 | const mcv = try self.resolveInst(callee); | 4068 | const sym_index = try coff_file.getGlobalSymbol(mem.sliceTo(decl_name, 0)); |
| 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; | ||
| 4094 | try self.genSetReg(Type.initTag(.usize), .rax, .{ | 4069 | try self.genSetReg(Type.initTag(.usize), .rax, .{ |
| 4095 | .linker_load = .{ | 4070 | .linker_load = .{ |
| 4096 | .type = .got, | 4071 | .type = .import, |
| 4097 | .sym_index = sym_index, | 4072 | .sym_index = sym_index, |
| 4098 | }, | 4073 | }, |
| 4099 | }); | 4074 | }); |
| 4100 | // callq *%rax | ||
| 4101 | _ = try self.addInst(.{ | 4075 | _ = try self.addInst(.{ |
| 4102 | .tag = .call, | 4076 | .tag = .call, |
| 4103 | .ops = Mir.Inst.Ops.encode(.{ | 4077 | .ops = Mir.Inst.Ops.encode(.{ |
| ... | @@ -4106,71 +4080,37 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier | ... | @@ -4106,71 +4080,37 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier |
| 4106 | }), | 4080 | }), |
| 4107 | .data = undefined, | 4081 | .data = undefined, |
| 4108 | }); | 4082 | }); |
| 4109 | } else if (func_value.castTag(.extern_fn)) |func_payload| { | 4083 | } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { |
| 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 | } | ||
| 4118 | const sym_index = try macho_file.getGlobalSymbol(mem.sliceTo(decl_name, 0)); | 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 | _ = try self.addInst(.{ | 4087 | _ = try self.addInst(.{ |
| 4120 | .tag = .call_extern, | 4088 | .tag = .call_extern, |
| 4121 | .ops = undefined, | 4089 | .ops = undefined, |
| 4122 | .data = .{ | 4090 | .data = .{ .relocation = .{ |
| 4123 | .relocation = .{ | 4091 | .atom_index = atom_index, |
| 4124 | .atom_index = mod.declPtr(self.mod_fn.owner_decl).link.macho.sym_index, | 4092 | .sym_index = sym_index, |
| 4125 | .sym_index = sym_index, | 4093 | } }, |
| 4126 | }, | ||
| 4127 | }, | ||
| 4128 | }); | 4094 | }); |
| 4129 | } else { | 4095 | } else { |
| 4130 | return self.fail("TODO implement calling bitcasted functions", .{}); | 4096 | return self.fail("TODO implement calling extern functions", .{}); |
| 4131 | } | 4097 | } |
| 4132 | } else { | 4098 | } else { |
| 4133 | assert(ty.zigTypeTag() == .Pointer); | 4099 | return self.fail("TODO implement calling bitcasted functions", .{}); |
| 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 | }); | ||
| 4144 | } | 4100 | } |
| 4145 | } else if (self.bin_file.cast(link.File.Plan9)) |p9| { | 4101 | } else { |
| 4146 | if (self.air.value(callee)) |func_value| { | 4102 | assert(ty.zigTypeTag() == .Pointer); |
| 4147 | if (func_value.castTag(.function)) |func_payload| { | 4103 | const mcv = try self.resolveInst(callee); |
| 4148 | try p9.seeDecl(func_payload.data.owner_decl); | 4104 | try self.genSetReg(Type.initTag(.usize), .rax, mcv); |
| 4149 | const ptr_bits = self.target.cpu.arch.ptrBitWidth(); | 4105 | _ = try self.addInst(.{ |
| 4150 | const ptr_bytes: u64 = @divExact(ptr_bits, 8); | 4106 | .tag = .call, |
| 4151 | const got_addr = p9.bases.data; | 4107 | .ops = Mir.Inst.Ops.encode(.{ |
| 4152 | const got_index = mod.declPtr(func_payload.data.owner_decl).link.plan9.got_index.?; | 4108 | .reg1 = .rax, |
| 4153 | const fn_got_addr = got_addr + got_index * ptr_bytes; | 4109 | .flags = 0b01, |
| 4154 | _ = try self.addInst(.{ | 4110 | }), |
| 4155 | .tag = .call, | 4111 | .data = undefined, |
| 4156 | .ops = Mir.Inst.Ops.encode(.{ .flags = 0b01 }), | 4112 | }); |
| 4157 | .data = .{ .imm = @intCast(u32, fn_got_addr) }, | 4113 | } |
| 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; | ||
| 4174 | 4114 | ||
| 4175 | if (info.stack_byte_count > 0) { | 4115 | if (info.stack_byte_count > 0) { |
| 4176 | // Readjust the stack | 4116 | // Readjust the stack |
| ... | @@ -6781,24 +6721,27 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne | ... | @@ -6781,24 +6721,27 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne |
| 6781 | module.markDeclAlive(decl); | 6721 | module.markDeclAlive(decl); |
| 6782 | 6722 | ||
| 6783 | if (self.bin_file.cast(link.File.Elf)) |elf_file| { | 6723 | if (self.bin_file.cast(link.File.Elf)) |elf_file| { |
| 6784 | const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?]; | 6724 | const atom_index = try elf_file.getOrCreateAtomForDecl(decl_index); |
| 6785 | const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes; | 6725 | const atom = elf_file.getAtom(atom_index); |
| 6786 | return MCValue{ .memory = got_addr }; | 6726 | return MCValue{ .memory = atom.getOffsetTableAddress(elf_file) }; |
| 6787 | } else if (self.bin_file.cast(link.File.MachO)) |_| { | 6727 | } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { |
| 6788 | assert(decl.link.macho.sym_index != 0); | 6728 | const atom_index = try macho_file.getOrCreateAtomForDecl(decl_index); |
| 6729 | const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?; | ||
| 6789 | return MCValue{ .linker_load = .{ | 6730 | return MCValue{ .linker_load = .{ |
| 6790 | .type = .got, | 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)) |_| { | 6734 | } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { |
| 6794 | assert(decl.link.coff.sym_index != 0); | 6735 | const atom_index = try coff_file.getOrCreateAtomForDecl(decl_index); |
| 6736 | const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?; | ||
| 6795 | return MCValue{ .linker_load = .{ | 6737 | return MCValue{ .linker_load = .{ |
| 6796 | .type = .got, | 6738 | .type = .got, |
| 6797 | .sym_index = decl.link.coff.sym_index, | 6739 | .sym_index = sym_index, |
| 6798 | } }; | 6740 | } }; |
| 6799 | } else if (self.bin_file.cast(link.File.Plan9)) |p9| { | 6741 | } else if (self.bin_file.cast(link.File.Plan9)) |p9| { |
| 6800 | try p9.seeDecl(decl_index); | 6742 | const decl_block_index = try p9.seeDecl(decl_index); |
| 6801 | const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes; | 6743 | const decl_block = p9.getDeclBlock(decl_block_index); |
| 6744 | const got_addr = p9.bases.data + decl_block.got_index.? * ptr_bytes; | ||
| 6802 | return MCValue{ .memory = got_addr }; | 6745 | return MCValue{ .memory = got_addr }; |
| 6803 | } else { | 6746 | } else { |
| 6804 | return self.fail("TODO codegen non-ELF const Decl pointer", .{}); | 6747 | return self.fail("TODO codegen non-ELF const Decl pointer", .{}); |
| ... | @@ -6811,8 +6754,7 @@ fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue { | ... | @@ -6811,8 +6754,7 @@ fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue { |
| 6811 | return self.fail("lowering unnamed constant failed: {s}", .{@errorName(err)}); | 6754 | return self.fail("lowering unnamed constant failed: {s}", .{@errorName(err)}); |
| 6812 | }; | 6755 | }; |
| 6813 | if (self.bin_file.cast(link.File.Elf)) |elf_file| { | 6756 | if (self.bin_file.cast(link.File.Elf)) |elf_file| { |
| 6814 | const vaddr = elf_file.local_symbols.items[local_sym_index].st_value; | 6757 | return MCValue{ .memory = elf_file.getSymbol(local_sym_index).st_value }; |
| 6815 | return MCValue{ .memory = vaddr }; | ||
| 6816 | } else if (self.bin_file.cast(link.File.MachO)) |_| { | 6758 | } else if (self.bin_file.cast(link.File.MachO)) |_| { |
| 6817 | return MCValue{ .linker_load = .{ | 6759 | return MCValue{ .linker_load = .{ |
| 6818 | .type = .direct, | 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,8 +1001,8 @@ fn mirLeaPic(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| 1001 | 0b01 => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_SIGNED), | 1001 | 0b01 => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_SIGNED), |
| 1002 | else => unreachable, | 1002 | else => unreachable, |
| 1003 | }; | 1003 | }; |
| 1004 | const atom = macho_file.getAtomForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?; | 1004 | const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?; |
| 1005 | try atom.addRelocation(macho_file, .{ | 1005 | try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{ |
| 1006 | .type = reloc_type, | 1006 | .type = reloc_type, |
| 1007 | .target = .{ .sym_index = relocation.sym_index, .file = null }, | 1007 | .target = .{ .sym_index = relocation.sym_index, .file = null }, |
| 1008 | .offset = @intCast(u32, end_offset - 4), | 1008 | .offset = @intCast(u32, end_offset - 4), |
| ... | @@ -1011,8 +1011,8 @@ fn mirLeaPic(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { | ... | @@ -1011,8 +1011,8 @@ fn mirLeaPic(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| 1011 | .length = 2, | 1011 | .length = 2, |
| 1012 | }); | 1012 | }); |
| 1013 | } else if (emit.bin_file.cast(link.File.Coff)) |coff_file| { | 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 }).?; | 1014 | const atom_index = coff_file.getAtomIndexForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?; |
| 1015 | try atom.addRelocation(coff_file, .{ | 1015 | try link.File.Coff.Atom.addRelocation(coff_file, atom_index, .{ |
| 1016 | .type = switch (ops.flags) { | 1016 | .type = switch (ops.flags) { |
| 1017 | 0b00 => .got, | 1017 | 0b00 => .got, |
| 1018 | 0b01 => .direct, | 1018 | 0b01 => .direct, |
| ... | @@ -1140,9 +1140,9 @@ fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { | ... | @@ -1140,9 +1140,9 @@ fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| 1140 | 1140 | ||
| 1141 | if (emit.bin_file.cast(link.File.MachO)) |macho_file| { | 1141 | if (emit.bin_file.cast(link.File.MachO)) |macho_file| { |
| 1142 | // Add relocation to the decl. | 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 | const target = macho_file.getGlobalByIndex(relocation.sym_index); | 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 | .type = @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_BRANCH), | 1146 | .type = @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_BRANCH), |
| 1147 | .target = target, | 1147 | .target = target, |
| 1148 | .offset = offset, | 1148 | .offset = offset, |
| ... | @@ -1152,9 +1152,9 @@ fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { | ... | @@ -1152,9 +1152,9 @@ fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| 1152 | }); | 1152 | }); |
| 1153 | } else if (emit.bin_file.cast(link.File.Coff)) |coff_file| { | 1153 | } else if (emit.bin_file.cast(link.File.Coff)) |coff_file| { |
| 1154 | // Add relocation to the decl. | 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 | const target = coff_file.getGlobalByIndex(relocation.sym_index); | 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 | .type = .direct, | 1158 | .type = .direct, |
| 1159 | .target = target, | 1159 | .target = target, |
| 1160 | .offset = offset, | 1160 | .offset = offset, |
src/codegen.zig+58-131| ... | @@ -21,16 +21,11 @@ const TypedValue = @import("TypedValue.zig"); | ... | @@ -21,16 +21,11 @@ const TypedValue = @import("TypedValue.zig"); |
| 21 | const Value = @import("value.zig").Value; | 21 | const Value = @import("value.zig").Value; |
| 22 | const Zir = @import("Zir.zig"); | 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 | pub const Result = union(enum) { | 24 | pub const Result = union(enum) { |
| 30 | /// The `code` parameter passed to `generateSymbol` has the value appended. | 25 | /// The `code` parameter passed to `generateSymbol` has the value ok. |
| 31 | appended: void, | 26 | ok: void, |
| 32 | /// The value is available externally, `code` is unused. | 27 | |
| 33 | externally_managed: []const u8, | 28 | /// There was a codegen error. |
| 34 | fail: *ErrorMsg, | 29 | fail: *ErrorMsg, |
| 35 | }; | 30 | }; |
| 36 | 31 | ||
| ... | @@ -89,7 +84,7 @@ pub fn generateFunction( | ... | @@ -89,7 +84,7 @@ pub fn generateFunction( |
| 89 | liveness: Liveness, | 84 | liveness: Liveness, |
| 90 | code: *std.ArrayList(u8), | 85 | code: *std.ArrayList(u8), |
| 91 | debug_output: DebugInfoOutput, | 86 | debug_output: DebugInfoOutput, |
| 92 | ) GenerateSymbolError!FnResult { | 87 | ) GenerateSymbolError!Result { |
| 93 | switch (bin_file.options.target.cpu.arch) { | 88 | switch (bin_file.options.target.cpu.arch) { |
| 94 | .arm, | 89 | .arm, |
| 95 | .armeb, | 90 | .armeb, |
| ... | @@ -145,7 +140,7 @@ pub fn generateSymbol( | ... | @@ -145,7 +140,7 @@ pub fn generateSymbol( |
| 145 | if (typed_value.val.isUndefDeep()) { | 140 | if (typed_value.val.isUndefDeep()) { |
| 146 | const abi_size = math.cast(usize, typed_value.ty.abiSize(target)) orelse return error.Overflow; | 141 | const abi_size = math.cast(usize, typed_value.ty.abiSize(target)) orelse return error.Overflow; |
| 147 | try code.appendNTimes(0xaa, abi_size); | 142 | try code.appendNTimes(0xaa, abi_size); |
| 148 | return Result{ .appended = {} }; | 143 | return Result.ok; |
| 149 | } | 144 | } |
| 150 | 145 | ||
| 151 | switch (typed_value.ty.zigTypeTag()) { | 146 | switch (typed_value.ty.zigTypeTag()) { |
| ... | @@ -176,7 +171,7 @@ pub fn generateSymbol( | ... | @@ -176,7 +171,7 @@ pub fn generateSymbol( |
| 176 | 128 => writeFloat(f128, typed_value.val.toFloat(f128), target, endian, try code.addManyAsArray(16)), | 171 | 128 => writeFloat(f128, typed_value.val.toFloat(f128), target, endian, try code.addManyAsArray(16)), |
| 177 | else => unreachable, | 172 | else => unreachable, |
| 178 | } | 173 | } |
| 179 | return Result{ .appended = {} }; | 174 | return Result.ok; |
| 180 | }, | 175 | }, |
| 181 | .Array => switch (typed_value.val.tag()) { | 176 | .Array => switch (typed_value.val.tag()) { |
| 182 | .bytes => { | 177 | .bytes => { |
| ... | @@ -185,7 +180,7 @@ pub fn generateSymbol( | ... | @@ -185,7 +180,7 @@ pub fn generateSymbol( |
| 185 | // The bytes payload already includes the sentinel, if any | 180 | // The bytes payload already includes the sentinel, if any |
| 186 | try code.ensureUnusedCapacity(len); | 181 | try code.ensureUnusedCapacity(len); |
| 187 | code.appendSliceAssumeCapacity(bytes[0..len]); | 182 | code.appendSliceAssumeCapacity(bytes[0..len]); |
| 188 | return Result{ .appended = {} }; | 183 | return Result.ok; |
| 189 | }, | 184 | }, |
| 190 | .str_lit => { | 185 | .str_lit => { |
| 191 | const str_lit = typed_value.val.castTag(.str_lit).?.data; | 186 | const str_lit = typed_value.val.castTag(.str_lit).?.data; |
| ... | @@ -197,7 +192,7 @@ pub fn generateSymbol( | ... | @@ -197,7 +192,7 @@ pub fn generateSymbol( |
| 197 | const byte = @intCast(u8, sent_val.toUnsignedInt(target)); | 192 | const byte = @intCast(u8, sent_val.toUnsignedInt(target)); |
| 198 | code.appendAssumeCapacity(byte); | 193 | code.appendAssumeCapacity(byte); |
| 199 | } | 194 | } |
| 200 | return Result{ .appended = {} }; | 195 | return Result.ok; |
| 201 | }, | 196 | }, |
| 202 | .aggregate => { | 197 | .aggregate => { |
| 203 | const elem_vals = typed_value.val.castTag(.aggregate).?.data; | 198 | const elem_vals = typed_value.val.castTag(.aggregate).?.data; |
| ... | @@ -208,14 +203,11 @@ pub fn generateSymbol( | ... | @@ -208,14 +203,11 @@ pub fn generateSymbol( |
| 208 | .ty = elem_ty, | 203 | .ty = elem_ty, |
| 209 | .val = elem_val, | 204 | .val = elem_val, |
| 210 | }, code, debug_output, reloc_info)) { | 205 | }, code, debug_output, reloc_info)) { |
| 211 | .appended => {}, | 206 | .ok => {}, |
| 212 | .externally_managed => |slice| { | ||
| 213 | code.appendSliceAssumeCapacity(slice); | ||
| 214 | }, | ||
| 215 | .fail => |em| return Result{ .fail = em }, | 207 | .fail => |em| return Result{ .fail = em }, |
| 216 | } | 208 | } |
| 217 | } | 209 | } |
| 218 | return Result{ .appended = {} }; | 210 | return Result.ok; |
| 219 | }, | 211 | }, |
| 220 | .repeated => { | 212 | .repeated => { |
| 221 | const array = typed_value.val.castTag(.repeated).?.data; | 213 | const array = typed_value.val.castTag(.repeated).?.data; |
| ... | @@ -229,10 +221,7 @@ pub fn generateSymbol( | ... | @@ -229,10 +221,7 @@ pub fn generateSymbol( |
| 229 | .ty = elem_ty, | 221 | .ty = elem_ty, |
| 230 | .val = array, | 222 | .val = array, |
| 231 | }, code, debug_output, reloc_info)) { | 223 | }, code, debug_output, reloc_info)) { |
| 232 | .appended => {}, | 224 | .ok => {}, |
| 233 | .externally_managed => |slice| { | ||
| 234 | code.appendSliceAssumeCapacity(slice); | ||
| 235 | }, | ||
| 236 | .fail => |em| return Result{ .fail = em }, | 225 | .fail => |em| return Result{ .fail = em }, |
| 237 | } | 226 | } |
| 238 | } | 227 | } |
| ... | @@ -242,15 +231,12 @@ pub fn generateSymbol( | ... | @@ -242,15 +231,12 @@ pub fn generateSymbol( |
| 242 | .ty = elem_ty, | 231 | .ty = elem_ty, |
| 243 | .val = sentinel_val, | 232 | .val = sentinel_val, |
| 244 | }, code, debug_output, reloc_info)) { | 233 | }, code, debug_output, reloc_info)) { |
| 245 | .appended => {}, | 234 | .ok => {}, |
| 246 | .externally_managed => |slice| { | ||
| 247 | code.appendSliceAssumeCapacity(slice); | ||
| 248 | }, | ||
| 249 | .fail => |em| return Result{ .fail = em }, | 235 | .fail => |em| return Result{ .fail = em }, |
| 250 | } | 236 | } |
| 251 | } | 237 | } |
| 252 | 238 | ||
| 253 | return Result{ .appended = {} }; | 239 | return Result.ok; |
| 254 | }, | 240 | }, |
| 255 | .empty_array_sentinel => { | 241 | .empty_array_sentinel => { |
| 256 | const elem_ty = typed_value.ty.childType(); | 242 | const elem_ty = typed_value.ty.childType(); |
| ... | @@ -259,13 +245,10 @@ pub fn generateSymbol( | ... | @@ -259,13 +245,10 @@ pub fn generateSymbol( |
| 259 | .ty = elem_ty, | 245 | .ty = elem_ty, |
| 260 | .val = sentinel_val, | 246 | .val = sentinel_val, |
| 261 | }, code, debug_output, reloc_info)) { | 247 | }, code, debug_output, reloc_info)) { |
| 262 | .appended => {}, | 248 | .ok => {}, |
| 263 | .externally_managed => |slice| { | ||
| 264 | code.appendSliceAssumeCapacity(slice); | ||
| 265 | }, | ||
| 266 | .fail => |em| return Result{ .fail = em }, | 249 | .fail => |em| return Result{ .fail = em }, |
| 267 | } | 250 | } |
| 268 | return Result{ .appended = {} }; | 251 | return Result.ok; |
| 269 | }, | 252 | }, |
| 270 | else => return Result{ | 253 | else => return Result{ |
| 271 | .fail = try ErrorMsg.create( | 254 | .fail = try ErrorMsg.create( |
| ... | @@ -289,7 +272,7 @@ pub fn generateSymbol( | ... | @@ -289,7 +272,7 @@ pub fn generateSymbol( |
| 289 | }, | 272 | }, |
| 290 | else => unreachable, | 273 | else => unreachable, |
| 291 | } | 274 | } |
| 292 | return Result{ .appended = {} }; | 275 | return Result.ok; |
| 293 | }, | 276 | }, |
| 294 | .variable => { | 277 | .variable => { |
| 295 | const decl = typed_value.val.castTag(.variable).?.data.owner_decl; | 278 | const decl = typed_value.val.castTag(.variable).?.data.owner_decl; |
| ... | @@ -309,10 +292,7 @@ pub fn generateSymbol( | ... | @@ -309,10 +292,7 @@ pub fn generateSymbol( |
| 309 | .ty = slice_ptr_field_type, | 292 | .ty = slice_ptr_field_type, |
| 310 | .val = slice.ptr, | 293 | .val = slice.ptr, |
| 311 | }, code, debug_output, reloc_info)) { | 294 | }, code, debug_output, reloc_info)) { |
| 312 | .appended => {}, | 295 | .ok => {}, |
| 313 | .externally_managed => |external_slice| { | ||
| 314 | code.appendSliceAssumeCapacity(external_slice); | ||
| 315 | }, | ||
| 316 | .fail => |em| return Result{ .fail = em }, | 296 | .fail => |em| return Result{ .fail = em }, |
| 317 | } | 297 | } |
| 318 | 298 | ||
| ... | @@ -321,14 +301,11 @@ pub fn generateSymbol( | ... | @@ -321,14 +301,11 @@ pub fn generateSymbol( |
| 321 | .ty = Type.initTag(.usize), | 301 | .ty = Type.initTag(.usize), |
| 322 | .val = slice.len, | 302 | .val = slice.len, |
| 323 | }, code, debug_output, reloc_info)) { | 303 | }, code, debug_output, reloc_info)) { |
| 324 | .appended => {}, | 304 | .ok => {}, |
| 325 | .externally_managed => |external_slice| { | ||
| 326 | code.appendSliceAssumeCapacity(external_slice); | ||
| 327 | }, | ||
| 328 | .fail => |em| return Result{ .fail = em }, | 305 | .fail => |em| return Result{ .fail = em }, |
| 329 | } | 306 | } |
| 330 | 307 | ||
| 331 | return Result{ .appended = {} }; | 308 | return Result.ok; |
| 332 | }, | 309 | }, |
| 333 | .field_ptr => { | 310 | .field_ptr => { |
| 334 | const field_ptr = typed_value.val.castTag(.field_ptr).?.data; | 311 | const field_ptr = typed_value.val.castTag(.field_ptr).?.data; |
| ... | @@ -375,13 +352,10 @@ pub fn generateSymbol( | ... | @@ -375,13 +352,10 @@ pub fn generateSymbol( |
| 375 | .ty = typed_value.ty, | 352 | .ty = typed_value.ty, |
| 376 | .val = container_ptr, | 353 | .val = container_ptr, |
| 377 | }, code, debug_output, reloc_info)) { | 354 | }, code, debug_output, reloc_info)) { |
| 378 | .appended => {}, | 355 | .ok => {}, |
| 379 | .externally_managed => |external_slice| { | ||
| 380 | code.appendSliceAssumeCapacity(external_slice); | ||
| 381 | }, | ||
| 382 | .fail => |em| return Result{ .fail = em }, | 356 | .fail => |em| return Result{ .fail = em }, |
| 383 | } | 357 | } |
| 384 | return Result{ .appended = {} }; | 358 | return Result.ok; |
| 385 | }, | 359 | }, |
| 386 | else => return Result{ | 360 | else => return Result{ |
| 387 | .fail = try ErrorMsg.create( | 361 | .fail = try ErrorMsg.create( |
| ... | @@ -434,7 +408,7 @@ pub fn generateSymbol( | ... | @@ -434,7 +408,7 @@ pub fn generateSymbol( |
| 434 | .signed => @bitCast(u8, @intCast(i8, typed_value.val.toSignedInt(target))), | 408 | .signed => @bitCast(u8, @intCast(i8, typed_value.val.toSignedInt(target))), |
| 435 | }; | 409 | }; |
| 436 | try code.append(x); | 410 | try code.append(x); |
| 437 | return Result{ .appended = {} }; | 411 | return Result.ok; |
| 438 | } | 412 | } |
| 439 | if (info.bits > 64) { | 413 | if (info.bits > 64) { |
| 440 | var bigint_buffer: Value.BigIntSpace = undefined; | 414 | var bigint_buffer: Value.BigIntSpace = undefined; |
| ... | @@ -443,7 +417,7 @@ pub fn generateSymbol( | ... | @@ -443,7 +417,7 @@ pub fn generateSymbol( |
| 443 | const start = code.items.len; | 417 | const start = code.items.len; |
| 444 | try code.resize(start + abi_size); | 418 | try code.resize(start + abi_size); |
| 445 | bigint.writeTwosComplement(code.items[start..][0..abi_size], endian); | 419 | bigint.writeTwosComplement(code.items[start..][0..abi_size], endian); |
| 446 | return Result{ .appended = {} }; | 420 | return Result.ok; |
| 447 | } | 421 | } |
| 448 | switch (info.signedness) { | 422 | switch (info.signedness) { |
| 449 | .unsigned => { | 423 | .unsigned => { |
| ... | @@ -471,7 +445,7 @@ pub fn generateSymbol( | ... | @@ -471,7 +445,7 @@ pub fn generateSymbol( |
| 471 | } | 445 | } |
| 472 | }, | 446 | }, |
| 473 | } | 447 | } |
| 474 | return Result{ .appended = {} }; | 448 | return Result.ok; |
| 475 | }, | 449 | }, |
| 476 | .Enum => { | 450 | .Enum => { |
| 477 | var int_buffer: Value.Payload.U64 = undefined; | 451 | var int_buffer: Value.Payload.U64 = undefined; |
| ... | @@ -481,7 +455,7 @@ pub fn generateSymbol( | ... | @@ -481,7 +455,7 @@ pub fn generateSymbol( |
| 481 | if (info.bits <= 8) { | 455 | if (info.bits <= 8) { |
| 482 | const x = @intCast(u8, int_val.toUnsignedInt(target)); | 456 | const x = @intCast(u8, int_val.toUnsignedInt(target)); |
| 483 | try code.append(x); | 457 | try code.append(x); |
| 484 | return Result{ .appended = {} }; | 458 | return Result.ok; |
| 485 | } | 459 | } |
| 486 | if (info.bits > 64) { | 460 | if (info.bits > 64) { |
| 487 | return Result{ | 461 | return Result{ |
| ... | @@ -519,12 +493,12 @@ pub fn generateSymbol( | ... | @@ -519,12 +493,12 @@ pub fn generateSymbol( |
| 519 | } | 493 | } |
| 520 | }, | 494 | }, |
| 521 | } | 495 | } |
| 522 | return Result{ .appended = {} }; | 496 | return Result.ok; |
| 523 | }, | 497 | }, |
| 524 | .Bool => { | 498 | .Bool => { |
| 525 | const x: u8 = @boolToInt(typed_value.val.toBool()); | 499 | const x: u8 = @boolToInt(typed_value.val.toBool()); |
| 526 | try code.append(x); | 500 | try code.append(x); |
| 527 | return Result{ .appended = {} }; | 501 | return Result.ok; |
| 528 | }, | 502 | }, |
| 529 | .Struct => { | 503 | .Struct => { |
| 530 | if (typed_value.ty.containerLayout() == .Packed) { | 504 | if (typed_value.ty.containerLayout() == .Packed) { |
| ... | @@ -549,12 +523,7 @@ pub fn generateSymbol( | ... | @@ -549,12 +523,7 @@ pub fn generateSymbol( |
| 549 | .ty = field_ty, | 523 | .ty = field_ty, |
| 550 | .val = field_val, | 524 | .val = field_val, |
| 551 | }, &tmp_list, debug_output, reloc_info)) { | 525 | }, &tmp_list, debug_output, reloc_info)) { |
| 552 | .appended => { | 526 | .ok => mem.copy(u8, code.items[current_pos..], tmp_list.items), |
| 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 | }, | ||
| 558 | .fail => |em| return Result{ .fail = em }, | 527 | .fail => |em| return Result{ .fail = em }, |
| 559 | } | 528 | } |
| 560 | } else { | 529 | } else { |
| ... | @@ -563,7 +532,7 @@ pub fn generateSymbol( | ... | @@ -563,7 +532,7 @@ pub fn generateSymbol( |
| 563 | bits += @intCast(u16, field_ty.bitSize(target)); | 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 | const struct_begin = code.items.len; | 538 | const struct_begin = code.items.len; |
| ... | @@ -576,10 +545,7 @@ pub fn generateSymbol( | ... | @@ -576,10 +545,7 @@ pub fn generateSymbol( |
| 576 | .ty = field_ty, | 545 | .ty = field_ty, |
| 577 | .val = field_val, | 546 | .val = field_val, |
| 578 | }, code, debug_output, reloc_info)) { | 547 | }, code, debug_output, reloc_info)) { |
| 579 | .appended => {}, | 548 | .ok => {}, |
| 580 | .externally_managed => |external_slice| { | ||
| 581 | code.appendSliceAssumeCapacity(external_slice); | ||
| 582 | }, | ||
| 583 | .fail => |em| return Result{ .fail = em }, | 549 | .fail => |em| return Result{ .fail = em }, |
| 584 | } | 550 | } |
| 585 | const unpadded_field_end = code.items.len - struct_begin; | 551 | const unpadded_field_end = code.items.len - struct_begin; |
| ... | @@ -593,7 +559,7 @@ pub fn generateSymbol( | ... | @@ -593,7 +559,7 @@ pub fn generateSymbol( |
| 593 | } | 559 | } |
| 594 | } | 560 | } |
| 595 | 561 | ||
| 596 | return Result{ .appended = {} }; | 562 | return Result.ok; |
| 597 | }, | 563 | }, |
| 598 | .Union => { | 564 | .Union => { |
| 599 | const union_obj = typed_value.val.castTag(.@"union").?.data; | 565 | const union_obj = typed_value.val.castTag(.@"union").?.data; |
| ... | @@ -612,10 +578,7 @@ pub fn generateSymbol( | ... | @@ -612,10 +578,7 @@ pub fn generateSymbol( |
| 612 | .ty = typed_value.ty.unionTagType().?, | 578 | .ty = typed_value.ty.unionTagType().?, |
| 613 | .val = union_obj.tag, | 579 | .val = union_obj.tag, |
| 614 | }, code, debug_output, reloc_info)) { | 580 | }, code, debug_output, reloc_info)) { |
| 615 | .appended => {}, | 581 | .ok => {}, |
| 616 | .externally_managed => |external_slice| { | ||
| 617 | code.appendSliceAssumeCapacity(external_slice); | ||
| 618 | }, | ||
| 619 | .fail => |em| return Result{ .fail = em }, | 582 | .fail => |em| return Result{ .fail = em }, |
| 620 | } | 583 | } |
| 621 | } | 584 | } |
| ... | @@ -632,10 +595,7 @@ pub fn generateSymbol( | ... | @@ -632,10 +595,7 @@ pub fn generateSymbol( |
| 632 | .ty = field_ty, | 595 | .ty = field_ty, |
| 633 | .val = union_obj.val, | 596 | .val = union_obj.val, |
| 634 | }, code, debug_output, reloc_info)) { | 597 | }, code, debug_output, reloc_info)) { |
| 635 | .appended => {}, | 598 | .ok => {}, |
| 636 | .externally_managed => |external_slice| { | ||
| 637 | code.appendSliceAssumeCapacity(external_slice); | ||
| 638 | }, | ||
| 639 | .fail => |em| return Result{ .fail = em }, | 599 | .fail => |em| return Result{ .fail = em }, |
| 640 | } | 600 | } |
| 641 | 601 | ||
| ... | @@ -650,15 +610,12 @@ pub fn generateSymbol( | ... | @@ -650,15 +610,12 @@ pub fn generateSymbol( |
| 650 | .ty = union_ty.tag_ty, | 610 | .ty = union_ty.tag_ty, |
| 651 | .val = union_obj.tag, | 611 | .val = union_obj.tag, |
| 652 | }, code, debug_output, reloc_info)) { | 612 | }, code, debug_output, reloc_info)) { |
| 653 | .appended => {}, | 613 | .ok => {}, |
| 654 | .externally_managed => |external_slice| { | ||
| 655 | code.appendSliceAssumeCapacity(external_slice); | ||
| 656 | }, | ||
| 657 | .fail => |em| return Result{ .fail = em }, | 614 | .fail => |em| return Result{ .fail = em }, |
| 658 | } | 615 | } |
| 659 | } | 616 | } |
| 660 | 617 | ||
| 661 | return Result{ .appended = {} }; | 618 | return Result.ok; |
| 662 | }, | 619 | }, |
| 663 | .Optional => { | 620 | .Optional => { |
| 664 | var opt_buf: Type.Payload.ElemType = undefined; | 621 | var opt_buf: Type.Payload.ElemType = undefined; |
| ... | @@ -669,7 +626,7 @@ pub fn generateSymbol( | ... | @@ -669,7 +626,7 @@ pub fn generateSymbol( |
| 669 | 626 | ||
| 670 | if (!payload_type.hasRuntimeBits()) { | 627 | if (!payload_type.hasRuntimeBits()) { |
| 671 | try code.writer().writeByteNTimes(@boolToInt(is_pl), abi_size); | 628 | try code.writer().writeByteNTimes(@boolToInt(is_pl), abi_size); |
| 672 | return Result{ .appended = {} }; | 629 | return Result.ok; |
| 673 | } | 630 | } |
| 674 | 631 | ||
| 675 | if (typed_value.ty.optionalReprIsPayload()) { | 632 | if (typed_value.ty.optionalReprIsPayload()) { |
| ... | @@ -678,10 +635,7 @@ pub fn generateSymbol( | ... | @@ -678,10 +635,7 @@ pub fn generateSymbol( |
| 678 | .ty = payload_type, | 635 | .ty = payload_type, |
| 679 | .val = payload.data, | 636 | .val = payload.data, |
| 680 | }, code, debug_output, reloc_info)) { | 637 | }, code, debug_output, reloc_info)) { |
| 681 | .appended => {}, | 638 | .ok => {}, |
| 682 | .externally_managed => |external_slice| { | ||
| 683 | code.appendSliceAssumeCapacity(external_slice); | ||
| 684 | }, | ||
| 685 | .fail => |em| return Result{ .fail = em }, | 639 | .fail => |em| return Result{ .fail = em }, |
| 686 | } | 640 | } |
| 687 | } else if (!typed_value.val.isNull()) { | 641 | } else if (!typed_value.val.isNull()) { |
| ... | @@ -689,17 +643,14 @@ pub fn generateSymbol( | ... | @@ -689,17 +643,14 @@ pub fn generateSymbol( |
| 689 | .ty = payload_type, | 643 | .ty = payload_type, |
| 690 | .val = typed_value.val, | 644 | .val = typed_value.val, |
| 691 | }, code, debug_output, reloc_info)) { | 645 | }, code, debug_output, reloc_info)) { |
| 692 | .appended => {}, | 646 | .ok => {}, |
| 693 | .externally_managed => |external_slice| { | ||
| 694 | code.appendSliceAssumeCapacity(external_slice); | ||
| 695 | }, | ||
| 696 | .fail => |em| return Result{ .fail = em }, | 647 | .fail => |em| return Result{ .fail = em }, |
| 697 | } | 648 | } |
| 698 | } else { | 649 | } else { |
| 699 | try code.writer().writeByteNTimes(0, abi_size); | 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 | const value = if (typed_value.val.castTag(.opt_payload)) |payload| payload.data else Value.initTag(.undef); | 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,14 +659,11 @@ pub fn generateSymbol( |
| 708 | .ty = payload_type, | 659 | .ty = payload_type, |
| 709 | .val = value, | 660 | .val = value, |
| 710 | }, code, debug_output, reloc_info)) { | 661 | }, code, debug_output, reloc_info)) { |
| 711 | .appended => {}, | 662 | .ok => {}, |
| 712 | .externally_managed => |external_slice| { | ||
| 713 | code.appendSliceAssumeCapacity(external_slice); | ||
| 714 | }, | ||
| 715 | .fail => |em| return Result{ .fail = em }, | 663 | .fail => |em| return Result{ .fail = em }, |
| 716 | } | 664 | } |
| 717 | 665 | ||
| 718 | return Result{ .appended = {} }; | 666 | return Result.ok; |
| 719 | }, | 667 | }, |
| 720 | .ErrorUnion => { | 668 | .ErrorUnion => { |
| 721 | const error_ty = typed_value.ty.errorUnionSet(); | 669 | const error_ty = typed_value.ty.errorUnionSet(); |
| ... | @@ -740,10 +688,7 @@ pub fn generateSymbol( | ... | @@ -740,10 +688,7 @@ pub fn generateSymbol( |
| 740 | .ty = error_ty, | 688 | .ty = error_ty, |
| 741 | .val = if (is_payload) Value.initTag(.zero) else typed_value.val, | 689 | .val = if (is_payload) Value.initTag(.zero) else typed_value.val, |
| 742 | }, code, debug_output, reloc_info)) { | 690 | }, code, debug_output, reloc_info)) { |
| 743 | .appended => {}, | 691 | .ok => {}, |
| 744 | .externally_managed => |external_slice| { | ||
| 745 | code.appendSliceAssumeCapacity(external_slice); | ||
| 746 | }, | ||
| 747 | .fail => |em| return Result{ .fail = em }, | 692 | .fail => |em| return Result{ .fail = em }, |
| 748 | } | 693 | } |
| 749 | } | 694 | } |
| ... | @@ -756,10 +701,7 @@ pub fn generateSymbol( | ... | @@ -756,10 +701,7 @@ pub fn generateSymbol( |
| 756 | .ty = payload_ty, | 701 | .ty = payload_ty, |
| 757 | .val = payload_val, | 702 | .val = payload_val, |
| 758 | }, code, debug_output, reloc_info)) { | 703 | }, code, debug_output, reloc_info)) { |
| 759 | .appended => {}, | 704 | .ok => {}, |
| 760 | .externally_managed => |external_slice| { | ||
| 761 | code.appendSliceAssumeCapacity(external_slice); | ||
| 762 | }, | ||
| 763 | .fail => |em| return Result{ .fail = em }, | 705 | .fail => |em| return Result{ .fail = em }, |
| 764 | } | 706 | } |
| 765 | const unpadded_end = code.items.len - begin; | 707 | const unpadded_end = code.items.len - begin; |
| ... | @@ -778,10 +720,7 @@ pub fn generateSymbol( | ... | @@ -778,10 +720,7 @@ pub fn generateSymbol( |
| 778 | .ty = error_ty, | 720 | .ty = error_ty, |
| 779 | .val = if (is_payload) Value.initTag(.zero) else typed_value.val, | 721 | .val = if (is_payload) Value.initTag(.zero) else typed_value.val, |
| 780 | }, code, debug_output, reloc_info)) { | 722 | }, code, debug_output, reloc_info)) { |
| 781 | .appended => {}, | 723 | .ok => {}, |
| 782 | .externally_managed => |external_slice| { | ||
| 783 | code.appendSliceAssumeCapacity(external_slice); | ||
| 784 | }, | ||
| 785 | .fail => |em| return Result{ .fail = em }, | 724 | .fail => |em| return Result{ .fail = em }, |
| 786 | } | 725 | } |
| 787 | const unpadded_end = code.items.len - begin; | 726 | const unpadded_end = code.items.len - begin; |
| ... | @@ -793,7 +732,7 @@ pub fn generateSymbol( | ... | @@ -793,7 +732,7 @@ pub fn generateSymbol( |
| 793 | } | 732 | } |
| 794 | } | 733 | } |
| 795 | 734 | ||
| 796 | return Result{ .appended = {} }; | 735 | return Result.ok; |
| 797 | }, | 736 | }, |
| 798 | .ErrorSet => { | 737 | .ErrorSet => { |
| 799 | switch (typed_value.val.tag()) { | 738 | switch (typed_value.val.tag()) { |
| ... | @@ -806,7 +745,7 @@ pub fn generateSymbol( | ... | @@ -806,7 +745,7 @@ pub fn generateSymbol( |
| 806 | try code.writer().writeByteNTimes(0, @intCast(usize, Type.anyerror.abiSize(target))); | 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 | .Vector => switch (typed_value.val.tag()) { | 750 | .Vector => switch (typed_value.val.tag()) { |
| 812 | .bytes => { | 751 | .bytes => { |
| ... | @@ -814,7 +753,7 @@ pub fn generateSymbol( | ... | @@ -814,7 +753,7 @@ pub fn generateSymbol( |
| 814 | const len = @intCast(usize, typed_value.ty.arrayLen()); | 753 | const len = @intCast(usize, typed_value.ty.arrayLen()); |
| 815 | try code.ensureUnusedCapacity(len); | 754 | try code.ensureUnusedCapacity(len); |
| 816 | code.appendSliceAssumeCapacity(bytes[0..len]); | 755 | code.appendSliceAssumeCapacity(bytes[0..len]); |
| 817 | return Result{ .appended = {} }; | 756 | return Result.ok; |
| 818 | }, | 757 | }, |
| 819 | .aggregate => { | 758 | .aggregate => { |
| 820 | const elem_vals = typed_value.val.castTag(.aggregate).?.data; | 759 | const elem_vals = typed_value.val.castTag(.aggregate).?.data; |
| ... | @@ -825,14 +764,11 @@ pub fn generateSymbol( | ... | @@ -825,14 +764,11 @@ pub fn generateSymbol( |
| 825 | .ty = elem_ty, | 764 | .ty = elem_ty, |
| 826 | .val = elem_val, | 765 | .val = elem_val, |
| 827 | }, code, debug_output, reloc_info)) { | 766 | }, code, debug_output, reloc_info)) { |
| 828 | .appended => {}, | 767 | .ok => {}, |
| 829 | .externally_managed => |slice| { | ||
| 830 | code.appendSliceAssumeCapacity(slice); | ||
| 831 | }, | ||
| 832 | .fail => |em| return Result{ .fail = em }, | 768 | .fail => |em| return Result{ .fail = em }, |
| 833 | } | 769 | } |
| 834 | } | 770 | } |
| 835 | return Result{ .appended = {} }; | 771 | return Result.ok; |
| 836 | }, | 772 | }, |
| 837 | .repeated => { | 773 | .repeated => { |
| 838 | const array = typed_value.val.castTag(.repeated).?.data; | 774 | const array = typed_value.val.castTag(.repeated).?.data; |
| ... | @@ -845,14 +781,11 @@ pub fn generateSymbol( | ... | @@ -845,14 +781,11 @@ pub fn generateSymbol( |
| 845 | .ty = elem_ty, | 781 | .ty = elem_ty, |
| 846 | .val = array, | 782 | .val = array, |
| 847 | }, code, debug_output, reloc_info)) { | 783 | }, code, debug_output, reloc_info)) { |
| 848 | .appended => {}, | 784 | .ok => {}, |
| 849 | .externally_managed => |slice| { | ||
| 850 | code.appendSliceAssumeCapacity(slice); | ||
| 851 | }, | ||
| 852 | .fail => |em| return Result{ .fail = em }, | 785 | .fail => |em| return Result{ .fail = em }, |
| 853 | } | 786 | } |
| 854 | } | 787 | } |
| 855 | return Result{ .appended = {} }; | 788 | return Result.ok; |
| 856 | }, | 789 | }, |
| 857 | .str_lit => { | 790 | .str_lit => { |
| 858 | const str_lit = typed_value.val.castTag(.str_lit).?.data; | 791 | const str_lit = typed_value.val.castTag(.str_lit).?.data; |
| ... | @@ -860,7 +793,7 @@ pub fn generateSymbol( | ... | @@ -860,7 +793,7 @@ pub fn generateSymbol( |
| 860 | const bytes = mod.string_literal_bytes.items[str_lit.index..][0..str_lit.len]; | 793 | const bytes = mod.string_literal_bytes.items[str_lit.index..][0..str_lit.len]; |
| 861 | try code.ensureUnusedCapacity(str_lit.len); | 794 | try code.ensureUnusedCapacity(str_lit.len); |
| 862 | code.appendSliceAssumeCapacity(bytes); | 795 | code.appendSliceAssumeCapacity(bytes); |
| 863 | return Result{ .appended = {} }; | 796 | return Result.ok; |
| 864 | }, | 797 | }, |
| 865 | else => unreachable, | 798 | else => unreachable, |
| 866 | }, | 799 | }, |
| ... | @@ -901,10 +834,7 @@ fn lowerDeclRef( | ... | @@ -901,10 +834,7 @@ fn lowerDeclRef( |
| 901 | .ty = slice_ptr_field_type, | 834 | .ty = slice_ptr_field_type, |
| 902 | .val = typed_value.val, | 835 | .val = typed_value.val, |
| 903 | }, code, debug_output, reloc_info)) { | 836 | }, code, debug_output, reloc_info)) { |
| 904 | .appended => {}, | 837 | .ok => {}, |
| 905 | .externally_managed => |external_slice| { | ||
| 906 | code.appendSliceAssumeCapacity(external_slice); | ||
| 907 | }, | ||
| 908 | .fail => |em| return Result{ .fail = em }, | 838 | .fail => |em| return Result{ .fail = em }, |
| 909 | } | 839 | } |
| 910 | 840 | ||
| ... | @@ -917,14 +847,11 @@ fn lowerDeclRef( | ... | @@ -917,14 +847,11 @@ fn lowerDeclRef( |
| 917 | .ty = Type.usize, | 847 | .ty = Type.usize, |
| 918 | .val = Value.initPayload(&slice_len.base), | 848 | .val = Value.initPayload(&slice_len.base), |
| 919 | }, code, debug_output, reloc_info)) { | 849 | }, code, debug_output, reloc_info)) { |
| 920 | .appended => {}, | 850 | .ok => {}, |
| 921 | .externally_managed => |external_slice| { | ||
| 922 | code.appendSliceAssumeCapacity(external_slice); | ||
| 923 | }, | ||
| 924 | .fail => |em| return Result{ .fail = em }, | 851 | .fail => |em| return Result{ .fail = em }, |
| 925 | } | 852 | } |
| 926 | 853 | ||
| 927 | return Result{ .appended = {} }; | 854 | return Result.ok; |
| 928 | } | 855 | } |
| 929 | 856 | ||
| 930 | const ptr_width = target.cpu.arch.ptrBitWidth(); | 857 | const ptr_width = target.cpu.arch.ptrBitWidth(); |
| ... | @@ -932,7 +859,7 @@ fn lowerDeclRef( | ... | @@ -932,7 +859,7 @@ fn lowerDeclRef( |
| 932 | const is_fn_body = decl.ty.zigTypeTag() == .Fn; | 859 | const is_fn_body = decl.ty.zigTypeTag() == .Fn; |
| 933 | if (!is_fn_body and !decl.ty.hasRuntimeBits()) { | 860 | if (!is_fn_body and !decl.ty.hasRuntimeBits()) { |
| 934 | try code.writer().writeByteNTimes(0xaa, @divExact(ptr_width, 8)); | 861 | try code.writer().writeByteNTimes(0xaa, @divExact(ptr_width, 8)); |
| 935 | return Result{ .appended = {} }; | 862 | return Result.ok; |
| 936 | } | 863 | } |
| 937 | 864 | ||
| 938 | module.markDeclAlive(decl); | 865 | module.markDeclAlive(decl); |
| ... | @@ -950,7 +877,7 @@ fn lowerDeclRef( | ... | @@ -950,7 +877,7 @@ fn lowerDeclRef( |
| 950 | else => unreachable, | 877 | else => unreachable, |
| 951 | } | 878 | } |
| 952 | 879 | ||
| 953 | return Result{ .appended = {} }; | 880 | return Result.ok; |
| 954 | } | 881 | } |
| 955 | 882 | ||
| 956 | pub fn errUnionPayloadOffset(payload_ty: Type, target: std.Target) u64 { | 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,7 +16,6 @@ const trace = @import("../tracy.zig").trace; |
| 16 | const LazySrcLoc = Module.LazySrcLoc; | 16 | const LazySrcLoc = Module.LazySrcLoc; |
| 17 | const Air = @import("../Air.zig"); | 17 | const Air = @import("../Air.zig"); |
| 18 | const Liveness = @import("../Liveness.zig"); | 18 | const Liveness = @import("../Liveness.zig"); |
| 19 | const CType = @import("../type.zig").CType; | ||
| 20 | 19 | ||
| 21 | const target_util = @import("../target.zig"); | 20 | const target_util = @import("../target.zig"); |
| 22 | const libcFloatPrefix = target_util.libcFloatPrefix; | 21 | const libcFloatPrefix = target_util.libcFloatPrefix; |
| ... | @@ -1663,6 +1662,22 @@ pub const DeclGen = struct { | ... | @@ -1663,6 +1662,22 @@ pub const DeclGen = struct { |
| 1663 | defer buffer.deinit(); | 1662 | defer buffer.deinit(); |
| 1664 | 1663 | ||
| 1665 | try buffer.appendSlice("struct "); | 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 | try buffer.appendSlice(name); | 1681 | try buffer.appendSlice(name); |
| 1667 | try buffer.appendSlice(" {\n"); | 1682 | try buffer.appendSlice(" {\n"); |
| 1668 | { | 1683 | { |
| ... | @@ -1672,7 +1687,7 @@ pub const DeclGen = struct { | ... | @@ -1672,7 +1687,7 @@ pub const DeclGen = struct { |
| 1672 | const field_ty = field.value_ptr.ty; | 1687 | const field_ty = field.value_ptr.ty; |
| 1673 | if (!field_ty.hasRuntimeBits()) continue; | 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 | const field_name = CValue{ .identifier = field.key_ptr.* }; | 1691 | const field_name = CValue{ .identifier = field.key_ptr.* }; |
| 1677 | try buffer.append(' '); | 1692 | try buffer.append(' '); |
| 1678 | try dg.renderTypeAndName(buffer.writer(), field_ty, field_name, .Mut, alignment, .Complete); | 1693 | try dg.renderTypeAndName(buffer.writer(), field_ty, field_name, .Mut, alignment, .Complete); |
| ... | @@ -1682,7 +1697,7 @@ pub const DeclGen = struct { | ... | @@ -1682,7 +1697,7 @@ pub const DeclGen = struct { |
| 1682 | } | 1697 | } |
| 1683 | if (empty) try buffer.appendSlice(" char empty_struct;\n"); | 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 | const rendered = try buffer.toOwnedSlice(); | 1702 | const rendered = try buffer.toOwnedSlice(); |
| 1688 | errdefer dg.typedefs.allocator.free(rendered); | 1703 | errdefer dg.typedefs.allocator.free(rendered); |
| ... | @@ -2367,8 +2382,13 @@ pub const DeclGen = struct { | ... | @@ -2367,8 +2382,13 @@ pub const DeclGen = struct { |
| 2367 | depth += 1; | 2382 | depth += 1; |
| 2368 | } | 2383 | } |
| 2369 | 2384 | ||
| 2370 | if (alignment != 0 and alignment > ty.abiAlignment(target)) { | 2385 | if (alignment != 0) { |
| 2371 | try w.print("zig_align({}) ", .{alignment}); | 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 | try dg.renderType(w, render_ty, kind); | 2393 | try dg.renderType(w, render_ty, kind); |
| 2374 | 2394 | ||
| ... | @@ -2860,27 +2880,30 @@ pub fn genDecl(o: *Object) !void { | ... | @@ -2860,27 +2880,30 @@ pub fn genDecl(o: *Object) !void { |
| 2860 | const w = o.writer(); | 2880 | const w = o.writer(); |
| 2861 | if (!is_global) try w.writeAll("static "); | 2881 | if (!is_global) try w.writeAll("static "); |
| 2862 | if (variable.is_threadlocal) try w.writeAll("zig_threadlocal "); | 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 | try o.dg.renderTypeAndName(w, o.dg.decl.ty, decl_c_value, .Mut, o.dg.decl.@"align", .Complete); | 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 | try w.writeAll(" = "); | 2886 | try w.writeAll(" = "); |
| 2865 | try o.dg.renderValue(w, tv.ty, variable.init, .StaticInitializer); | 2887 | try o.dg.renderValue(w, tv.ty, variable.init, .StaticInitializer); |
| 2866 | try w.writeByte(';'); | 2888 | try w.writeByte(';'); |
| 2867 | try o.indent_writer.insertNewline(); | 2889 | try o.indent_writer.insertNewline(); |
| 2868 | } else { | 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 | const decl_c_value: CValue = .{ .decl = o.dg.decl_index }; | 2893 | const decl_c_value: CValue = .{ .decl = o.dg.decl_index }; |
| 2870 | 2894 | ||
| 2871 | const fwd_decl_writer = o.dg.fwd_decl.writer(); | 2895 | try fwd_decl_writer.writeAll(if (is_global) "zig_extern " else "static "); |
| 2872 | try fwd_decl_writer.writeAll("static "); | 2896 | try o.dg.renderTypeAndName(fwd_decl_writer, tv.ty, decl_c_value, .Const, o.dg.decl.@"align", .Complete); |
| 2873 | try o.dg.renderTypeAndName(fwd_decl_writer, tv.ty, decl_c_value, .Mut, o.dg.decl.@"align", .Complete); | ||
| 2874 | try fwd_decl_writer.writeAll(";\n"); | 2897 | try fwd_decl_writer.writeAll(";\n"); |
| 2875 | 2898 | ||
| 2876 | const writer = o.writer(); | 2899 | const w = o.writer(); |
| 2877 | try writer.writeAll("static "); | 2900 | if (!is_global) try w.writeAll("static "); |
| 2878 | // TODO ask the Decl if it is const | 2901 | if (o.dg.decl.@"linksection") |section| try w.print("zig_linksection(\"{s}\", ", .{section}); |
| 2879 | // https://github.com/ziglang/zig/issues/7582 | 2902 | try o.dg.renderTypeAndName(w, tv.ty, decl_c_value, .Const, o.dg.decl.@"align", .Complete); |
| 2880 | try o.dg.renderTypeAndName(writer, tv.ty, decl_c_value, .Mut, o.dg.decl.@"align", .Complete); | 2903 | if (o.dg.decl.@"linksection" != null) try w.writeAll(", read)"); |
| 2881 | try writer.writeAll(" = "); | 2904 | try w.writeAll(" = "); |
| 2882 | try o.dg.renderValue(writer, tv.ty, tv.val, .StaticInitializer); | 2905 | try o.dg.renderValue(w, tv.ty, tv.val, .StaticInitializer); |
| 2883 | try writer.writeAll(";\n"); | 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,16 +3749,15 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3726 | 3749 | ||
| 3727 | const ptr_val = try f.resolveInst(bin_op.lhs); | 3750 | const ptr_val = try f.resolveInst(bin_op.lhs); |
| 3728 | const src_ty = f.air.typeOf(bin_op.rhs); | 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 | // TODO Sema should emit a different instruction when the store should | 3753 | // TODO Sema should emit a different instruction when the store should |
| 3734 | // possibly do the safety 0xaa bytes for undefined. | 3754 | // possibly do the safety 0xaa bytes for undefined. |
| 3735 | const src_val_is_undefined = | 3755 | const src_val_is_undefined = |
| 3736 | if (f.air.value(bin_op.rhs)) |v| v.isUndefDeep() else false; | 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 | return try storeUndefined(f, ptr_info.pointee_type, ptr_val); | 3759 | return try storeUndefined(f, ptr_info.pointee_type, ptr_val); |
| 3760 | } | ||
| 3739 | 3761 | ||
| 3740 | const target = f.object.dg.module.getTarget(); | 3762 | const target = f.object.dg.module.getTarget(); |
| 3741 | const is_aligned = ptr_info.@"align" == 0 or | 3763 | const is_aligned = ptr_info.@"align" == 0 or |
| ... | @@ -3744,6 +3766,9 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { | ... | @@ -3744,6 +3766,9 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue { |
| 3744 | const need_memcpy = !is_aligned or is_array; | 3766 | const need_memcpy = !is_aligned or is_array; |
| 3745 | const writer = f.object.writer(); | 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 | if (need_memcpy) { | 3772 | if (need_memcpy) { |
| 3748 | // For this memcpy to safely work we need the rhs to have the same | 3773 | // For this memcpy to safely work we need the rhs to have the same |
| 3749 | // underlying type as the lhs (i.e. they must both be arrays of the same underlying type). | 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,8 +4369,9 @@ fn airDbgInline(f: *Function, inst: Air.Inst.Index) !CValue { |
| 4344 | fn airDbgVar(f: *Function, inst: Air.Inst.Index) !CValue { | 4369 | fn airDbgVar(f: *Function, inst: Air.Inst.Index) !CValue { |
| 4345 | const pl_op = f.air.instructions.items(.data)[inst].pl_op; | 4370 | const pl_op = f.air.instructions.items(.data)[inst].pl_op; |
| 4346 | const name = f.air.nullTerminatedString(pl_op.payload); | 4371 | const name = f.air.nullTerminatedString(pl_op.payload); |
| 4347 | const operand = try f.resolveInst(pl_op.operand); | 4372 | const operand_is_undef = if (f.air.value(pl_op.operand)) |v| v.isUndefDeep() else false; |
| 4348 | _ = operand; | 4373 | if (!operand_is_undef) _ = try f.resolveInst(pl_op.operand); |
| 4374 | |||
| 4349 | try reap(f, inst, &.{pl_op.operand}); | 4375 | try reap(f, inst, &.{pl_op.operand}); |
| 4350 | const writer = f.object.writer(); | 4376 | const writer = f.object.writer(); |
| 4351 | try writer.print("/* var:{s} */\n", .{name}); | 4377 | try writer.print("/* var:{s} */\n", .{name}); |
src/codegen/llvm.zig+2-3| ... | @@ -19,7 +19,6 @@ const Liveness = @import("../Liveness.zig"); | ... | @@ -19,7 +19,6 @@ const Liveness = @import("../Liveness.zig"); |
| 19 | const Value = @import("../value.zig").Value; | 19 | const Value = @import("../value.zig").Value; |
| 20 | const Type = @import("../type.zig").Type; | 20 | const Type = @import("../type.zig").Type; |
| 21 | const LazySrcLoc = Module.LazySrcLoc; | 21 | const LazySrcLoc = Module.LazySrcLoc; |
| 22 | const CType = @import("../type.zig").CType; | ||
| 23 | const x86_64_abi = @import("../arch/x86_64/abi.zig"); | 22 | const x86_64_abi = @import("../arch/x86_64/abi.zig"); |
| 24 | const wasm_c_abi = @import("../arch/wasm/abi.zig"); | 23 | const wasm_c_abi = @import("../arch/wasm/abi.zig"); |
| 25 | const aarch64_c_abi = @import("../arch/aarch64/abi.zig"); | 24 | const aarch64_c_abi = @import("../arch/aarch64/abi.zig"); |
| ... | @@ -11057,8 +11056,8 @@ fn backendSupportsF128(target: std.Target) bool { | ... | @@ -11057,8 +11056,8 @@ fn backendSupportsF128(target: std.Target) bool { |
| 11057 | fn intrinsicsAllowed(scalar_ty: Type, target: std.Target) bool { | 11056 | fn intrinsicsAllowed(scalar_ty: Type, target: std.Target) bool { |
| 11058 | return switch (scalar_ty.tag()) { | 11057 | return switch (scalar_ty.tag()) { |
| 11059 | .f16 => backendSupportsF16(target), | 11058 | .f16 => backendSupportsF16(target), |
| 11060 | .f80 => (CType.longdouble.sizeInBits(target) == 80) and backendSupportsF80(target), | 11059 | .f80 => (target.c_type_bit_size(.longdouble) == 80) and backendSupportsF80(target), |
| 11061 | .f128 => (CType.longdouble.sizeInBits(target) == 128) and backendSupportsF128(target), | 11060 | .f128 => (target.c_type_bit_size(.longdouble) == 128) and backendSupportsF128(target), |
| 11062 | else => true, | 11061 | else => true, |
| 11063 | }; | 11062 | }; |
| 11064 | } | 11063 | } |
src/codegen/spirv.zig+19-11| ... | @@ -49,7 +49,7 @@ pub const DeclGen = struct { | ... | @@ -49,7 +49,7 @@ pub const DeclGen = struct { |
| 49 | spv: *SpvModule, | 49 | spv: *SpvModule, |
| 50 | 50 | ||
| 51 | /// The decl we are currently generating code for. | 51 | /// The decl we are currently generating code for. |
| 52 | decl: *Decl, | 52 | decl_index: Decl.Index, |
| 53 | 53 | ||
| 54 | /// The intermediate code of the declaration we are currently generating. Note: If | 54 | /// The intermediate code of the declaration we are currently generating. Note: If |
| 55 | /// the declaration is not a function, this value will be undefined! | 55 | /// the declaration is not a function, this value will be undefined! |
| ... | @@ -59,6 +59,8 @@ pub const DeclGen = struct { | ... | @@ -59,6 +59,8 @@ pub const DeclGen = struct { |
| 59 | /// Note: If the declaration is not a function, this value will be undefined! | 59 | /// Note: If the declaration is not a function, this value will be undefined! |
| 60 | liveness: Liveness, | 60 | liveness: Liveness, |
| 61 | 61 | ||
| 62 | ids: *const std.AutoHashMap(Decl.Index, IdResult), | ||
| 63 | |||
| 62 | /// An array of function argument result-ids. Each index corresponds with the | 64 | /// An array of function argument result-ids. Each index corresponds with the |
| 63 | /// function argument of the same index. | 65 | /// function argument of the same index. |
| 64 | args: std.ArrayListUnmanaged(IdRef) = .{}, | 66 | args: std.ArrayListUnmanaged(IdRef) = .{}, |
| ... | @@ -133,14 +135,20 @@ pub const DeclGen = struct { | ... | @@ -133,14 +135,20 @@ pub const DeclGen = struct { |
| 133 | 135 | ||
| 134 | /// Initialize the common resources of a DeclGen. Some fields are left uninitialized, | 136 | /// Initialize the common resources of a DeclGen. Some fields are left uninitialized, |
| 135 | /// only set when `gen` is called. | 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 | return .{ | 144 | return .{ |
| 138 | .gpa = allocator, | 145 | .gpa = allocator, |
| 139 | .module = module, | 146 | .module = module, |
| 140 | .spv = spv, | 147 | .spv = spv, |
| 141 | .decl = undefined, | 148 | .decl_index = undefined, |
| 142 | .air = undefined, | 149 | .air = undefined, |
| 143 | .liveness = undefined, | 150 | .liveness = undefined, |
| 151 | .ids = ids, | ||
| 144 | .next_arg_index = undefined, | 152 | .next_arg_index = undefined, |
| 145 | .current_block_label_id = undefined, | 153 | .current_block_label_id = undefined, |
| 146 | .error_msg = undefined, | 154 | .error_msg = undefined, |
| ... | @@ -150,9 +158,9 @@ pub const DeclGen = struct { | ... | @@ -150,9 +158,9 @@ pub const DeclGen = struct { |
| 150 | /// Generate the code for `decl`. If a reportable error occurred during code generation, | 158 | /// Generate the code for `decl`. If a reportable error occurred during code generation, |
| 151 | /// a message is returned by this function. Callee owns the memory. If this function | 159 | /// a message is returned by this function. Callee owns the memory. If this function |
| 152 | /// returns such a reportable error, it is valid to be called again for a different decl. | 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 | // Reset internal resources, we don't want to re-allocate these. | 162 | // Reset internal resources, we don't want to re-allocate these. |
| 155 | self.decl = decl; | 163 | self.decl_index = decl_index; |
| 156 | self.air = air; | 164 | self.air = air; |
| 157 | self.liveness = liveness; | 165 | self.liveness = liveness; |
| 158 | self.args.items.len = 0; | 166 | self.args.items.len = 0; |
| ... | @@ -194,7 +202,7 @@ pub const DeclGen = struct { | ... | @@ -194,7 +202,7 @@ pub const DeclGen = struct { |
| 194 | pub fn fail(self: *DeclGen, comptime format: []const u8, args: anytype) Error { | 202 | pub fn fail(self: *DeclGen, comptime format: []const u8, args: anytype) Error { |
| 195 | @setCold(true); | 203 | @setCold(true); |
| 196 | const src = LazySrcLoc.nodeOffset(0); | 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 | assert(self.error_msg == null); | 206 | assert(self.error_msg == null); |
| 199 | self.error_msg = try Module.ErrorMsg.create(self.module.gpa, src_loc, format, args); | 207 | self.error_msg = try Module.ErrorMsg.create(self.module.gpa, src_loc, format, args); |
| 200 | return error.CodegenFail; | 208 | return error.CodegenFail; |
| ... | @@ -332,7 +340,7 @@ pub const DeclGen = struct { | ... | @@ -332,7 +340,7 @@ pub const DeclGen = struct { |
| 332 | }; | 340 | }; |
| 333 | const decl = self.module.declPtr(fn_decl_index); | 341 | const decl = self.module.declPtr(fn_decl_index); |
| 334 | self.module.markDeclAlive(decl); | 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 | const target = self.getTarget(); | 346 | const target = self.getTarget(); |
| ... | @@ -553,8 +561,8 @@ pub const DeclGen = struct { | ... | @@ -553,8 +561,8 @@ pub const DeclGen = struct { |
| 553 | } | 561 | } |
| 554 | 562 | ||
| 555 | fn genDecl(self: *DeclGen) !void { | 563 | fn genDecl(self: *DeclGen) !void { |
| 556 | const decl = self.decl; | 564 | const result_id = self.ids.get(self.decl_index).?; |
| 557 | const result_id = decl.fn_link.spirv.id; | 565 | const decl = self.module.declPtr(self.decl_index); |
| 558 | 566 | ||
| 559 | if (decl.val.castTag(.function)) |_| { | 567 | if (decl.val.castTag(.function)) |_| { |
| 560 | assert(decl.ty.zigTypeTag() == .Fn); | 568 | assert(decl.ty.zigTypeTag() == .Fn); |
| ... | @@ -945,7 +953,7 @@ pub const DeclGen = struct { | ... | @@ -945,7 +953,7 @@ pub const DeclGen = struct { |
| 945 | 953 | ||
| 946 | fn airDbgStmt(self: *DeclGen, inst: Air.Inst.Index) !void { | 954 | fn airDbgStmt(self: *DeclGen, inst: Air.Inst.Index) !void { |
| 947 | const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt; | 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 | try self.func.body.emit(self.spv.gpa, .OpLine, .{ | 957 | try self.func.body.emit(self.spv.gpa, .OpLine, .{ |
| 950 | .file = src_fname_id, | 958 | .file = src_fname_id, |
| 951 | .line = dbg_stmt.line, | 959 | .line = dbg_stmt.line, |
| ... | @@ -1106,7 +1114,7 @@ pub const DeclGen = struct { | ... | @@ -1106,7 +1114,7 @@ pub const DeclGen = struct { |
| 1106 | assert(as.errors.items.len != 0); | 1114 | assert(as.errors.items.len != 0); |
| 1107 | assert(self.error_msg == null); | 1115 | assert(self.error_msg == null); |
| 1108 | const loc = LazySrcLoc.nodeOffset(0); | 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 | self.error_msg = try Module.ErrorMsg.create(self.module.gpa, src_loc, "failed to assemble SPIR-V inline assembly", .{}); | 1118 | self.error_msg = try Module.ErrorMsg.create(self.module.gpa, src_loc, "failed to assemble SPIR-V inline assembly", .{}); |
| 1111 | const notes = try self.module.gpa.alloc(Module.ErrorMsg, as.errors.items.len); | 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,39 +261,6 @@ pub const File = struct { |
| 261 | /// of this linking operation. | 261 | /// of this linking operation. |
| 262 | lock: ?Cache.Lock = null, | 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 | /// Attempts incremental linking, if the file already exists. If | 264 | /// Attempts incremental linking, if the file already exists. If |
| 298 | /// incremental linking fails, falls back to truncating the file and | 265 | /// incremental linking fails, falls back to truncating the file and |
| 299 | /// rewriting it. A malicious file is detected as incremental link failure | 266 | /// rewriting it. A malicious file is detected as incremental link failure |
| ... | @@ -533,8 +500,7 @@ pub const File = struct { | ... | @@ -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 | 503 | /// May be called before or after updateDeclExports for any given Decl. |
| 537 | /// after allocateDeclIndexes for any given Decl. | ||
| 538 | pub fn updateDecl(base: *File, module: *Module, decl_index: Module.Decl.Index) UpdateDeclError!void { | 504 | pub fn updateDecl(base: *File, module: *Module, decl_index: Module.Decl.Index) UpdateDeclError!void { |
| 539 | const decl = module.declPtr(decl_index); | 505 | const decl = module.declPtr(decl_index); |
| 540 | log.debug("updateDecl {*} ({s}), type={}", .{ decl, decl.name, decl.ty.fmtDebug() }); | 506 | log.debug("updateDecl {*} ({s}), type={}", .{ decl, decl.name, decl.ty.fmtDebug() }); |
| ... | @@ -557,8 +523,7 @@ pub const File = struct { | ... | @@ -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 | 526 | /// May be called before or after updateDeclExports for any given Decl. |
| 561 | /// after allocateDeclIndexes for any given Decl. | ||
| 562 | pub fn updateFunc(base: *File, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) UpdateDeclError!void { | 527 | pub fn updateFunc(base: *File, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) UpdateDeclError!void { |
| 563 | const owner_decl = module.declPtr(func.owner_decl); | 528 | const owner_decl = module.declPtr(func.owner_decl); |
| 564 | log.debug("updateFunc {*} ({s}), type={}", .{ | 529 | log.debug("updateFunc {*} ({s}), type={}", .{ |
| ... | @@ -582,48 +547,27 @@ pub const File = struct { | ... | @@ -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 | log.debug("updateDeclLineNumber {*} ({s}), line={}", .{ | 552 | log.debug("updateDeclLineNumber {*} ({s}), line={}", .{ |
| 587 | decl, decl.name, decl.src_line + 1, | 553 | decl, decl.name, decl.src_line + 1, |
| 588 | }); | 554 | }); |
| 589 | assert(decl.has_tv); | 555 | assert(decl.has_tv); |
| 590 | if (build_options.only_c) { | 556 | if (build_options.only_c) { |
| 591 | assert(base.tag == .c); | 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 | switch (base.tag) { | 560 | switch (base.tag) { |
| 595 | .coff => return @fieldParentPtr(Coff, "base", base).updateDeclLineNumber(module, decl), | 561 | .coff => return @fieldParentPtr(Coff, "base", base).updateDeclLineNumber(module, decl_index), |
| 596 | .elf => return @fieldParentPtr(Elf, "base", base).updateDeclLineNumber(module, decl), | 562 | .elf => return @fieldParentPtr(Elf, "base", base).updateDeclLineNumber(module, decl_index), |
| 597 | .macho => return @fieldParentPtr(MachO, "base", base).updateDeclLineNumber(module, decl), | 563 | .macho => return @fieldParentPtr(MachO, "base", base).updateDeclLineNumber(module, decl_index), |
| 598 | .c => return @fieldParentPtr(C, "base", base).updateDeclLineNumber(module, decl), | 564 | .c => return @fieldParentPtr(C, "base", base).updateDeclLineNumber(module, decl_index), |
| 599 | .wasm => return @fieldParentPtr(Wasm, "base", base).updateDeclLineNumber(module, decl), | 565 | .wasm => return @fieldParentPtr(Wasm, "base", base).updateDeclLineNumber(module, decl_index), |
| 600 | .plan9 => return @fieldParentPtr(Plan9, "base", base).updateDeclLineNumber(module, decl), | 566 | .plan9 => return @fieldParentPtr(Plan9, "base", base).updateDeclLineNumber(module, decl_index), |
| 601 | .spirv, .nvptx => {}, | 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 | pub fn releaseLock(self: *File) void { | 571 | pub fn releaseLock(self: *File) void { |
| 628 | if (self.lock) |*lock| { | 572 | if (self.lock) |*lock| { |
| 629 | lock.release(); | 573 | lock.release(); |
| ... | @@ -874,8 +818,7 @@ pub const File = struct { | ... | @@ -874,8 +818,7 @@ pub const File = struct { |
| 874 | AnalysisFail, | 818 | AnalysisFail, |
| 875 | }; | 819 | }; |
| 876 | 820 | ||
| 877 | /// May be called before or after updateDecl, but must be called after | 821 | /// May be called before or after updateDecl for any given Decl. |
| 878 | /// allocateDeclIndexes for any given Decl. | ||
| 879 | pub fn updateDeclExports( | 822 | pub fn updateDeclExports( |
| 880 | base: *File, | 823 | base: *File, |
| 881 | module: *Module, | 824 | module: *Module, |
| ... | @@ -911,6 +854,8 @@ pub const File = struct { | ... | @@ -911,6 +854,8 @@ pub const File = struct { |
| 911 | /// The linker is passed information about the containing atom, `parent_atom_index`, and offset within it's | 854 | /// The linker is passed information about the containing atom, `parent_atom_index`, and offset within it's |
| 912 | /// memory buffer, `offset`, so that it can make a note of potential relocation sites, should the | 855 | /// memory buffer, `offset`, so that it can make a note of potential relocation sites, should the |
| 913 | /// `Decl`'s address was not yet resolved, or the containing atom gets moved in virtual memory. | 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 | pub fn getDeclVAddr(base: *File, decl_index: Module.Decl.Index, reloc_info: RelocInfo) !u64 { | 859 | pub fn getDeclVAddr(base: *File, decl_index: Module.Decl.Index, reloc_info: RelocInfo) !u64 { |
| 915 | if (build_options.only_c) unreachable; | 860 | if (build_options.only_c) unreachable; |
| 916 | switch (base.tag) { | 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,12 +219,12 @@ pub fn updateDecl(self: *C, module: *Module, decl_index: Module.Decl.Index) !voi |
| 219 | code.shrinkAndFree(module.gpa, code.items.len); | 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 | // The C backend does not have the ability to fix line numbers without re-generating | 223 | // The C backend does not have the ability to fix line numbers without re-generating |
| 224 | // the entire Decl. | 224 | // the entire Decl. |
| 225 | _ = self; | 225 | _ = self; |
| 226 | _ = module; | 226 | _ = module; |
| 227 | _ = decl; | 227 | _ = decl_index; |
| 228 | } | 228 | } |
| 229 | 229 | ||
| 230 | pub fn flush(self: *C, comp: *Compilation, prog_node: *std.Progress.Node) !void { | 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,13 +79,13 @@ entry_addr: ?u32 = null, |
| 79 | /// We store them here so that we can properly dispose of any allocated | 79 | /// We store them here so that we can properly dispose of any allocated |
| 80 | /// memory within the atom in the incremental linker. | 80 | /// memory within the atom in the incremental linker. |
| 81 | /// TODO consolidate this. | 81 | /// TODO consolidate this. |
| 82 | decls: std.AutoHashMapUnmanaged(Module.Decl.Index, ?u16) = .{}, | 82 | decls: std.AutoHashMapUnmanaged(Module.Decl.Index, DeclMetadata) = .{}, |
| 83 | 83 | ||
| 84 | /// List of atoms that are either synthetic or map directly to the Zig source program. | 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 | /// Table of atoms indexed by the symbol index. | 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 | /// Table of unnamed constants associated with a parent `Decl`. | 90 | /// Table of unnamed constants associated with a parent `Decl`. |
| 91 | /// We store them here so that we can free the constants whenever the `Decl` | 91 | /// We store them here so that we can free the constants whenever the `Decl` |
| ... | @@ -124,9 +124,9 @@ const Entry = struct { | ... | @@ -124,9 +124,9 @@ const Entry = struct { |
| 124 | sym_index: u32, | 124 | sym_index: u32, |
| 125 | }; | 125 | }; |
| 126 | 126 | ||
| 127 | const RelocTable = std.AutoHashMapUnmanaged(*Atom, std.ArrayListUnmanaged(Relocation)); | 127 | const RelocTable = std.AutoHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(Relocation)); |
| 128 | const BaseRelocationTable = std.AutoHashMapUnmanaged(*Atom, std.ArrayListUnmanaged(u32)); | 128 | const BaseRelocationTable = std.AutoHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(u32)); |
| 129 | const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(*Atom)); | 129 | const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(Atom.Index)); |
| 130 | 130 | ||
| 131 | const default_file_alignment: u16 = 0x200; | 131 | const default_file_alignment: u16 = 0x200; |
| 132 | const default_size_of_stack_reserve: u32 = 0x1000000; | 132 | const default_size_of_stack_reserve: u32 = 0x1000000; |
| ... | @@ -137,7 +137,7 @@ const default_size_of_heap_commit: u32 = 0x1000; | ... | @@ -137,7 +137,7 @@ const default_size_of_heap_commit: u32 = 0x1000; |
| 137 | const Section = struct { | 137 | const Section = struct { |
| 138 | header: coff.SectionHeader, | 138 | header: coff.SectionHeader, |
| 139 | 139 | ||
| 140 | last_atom: ?*Atom = null, | 140 | last_atom_index: ?Atom.Index = null, |
| 141 | 141 | ||
| 142 | /// A list of atoms that have surplus capacity. This list can have false | 142 | /// A list of atoms that have surplus capacity. This list can have false |
| 143 | /// positives, as functions grow and shrink over time, only sometimes being added | 143 | /// positives, as functions grow and shrink over time, only sometimes being added |
| ... | @@ -154,7 +154,34 @@ const Section = struct { | ... | @@ -154,7 +154,34 @@ const Section = struct { |
| 154 | /// overcapacity can be negative. A simple way to have negative overcapacity is to | 154 | /// overcapacity can be negative. A simple way to have negative overcapacity is to |
| 155 | /// allocate a fresh atom, which will have ideal capacity, and then grow it | 155 | /// allocate a fresh atom, which will have ideal capacity, and then grow it |
| 156 | /// by 1 byte. It will then have -1 overcapacity. | 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 | pub const PtrWidth = enum { | 187 | pub const PtrWidth = enum { |
| ... | @@ -168,11 +195,6 @@ 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 | pub const SymbolWithLoc = struct { | 199 | pub const SymbolWithLoc = struct { |
| 178 | // Index into the respective symbol table. | 200 | // Index into the respective symbol table. |
| ... | @@ -271,11 +293,7 @@ pub fn deinit(self: *Coff) void { | ... | @@ -271,11 +293,7 @@ pub fn deinit(self: *Coff) void { |
| 271 | } | 293 | } |
| 272 | self.sections.deinit(gpa); | 294 | self.sections.deinit(gpa); |
| 273 | 295 | ||
| 274 | for (self.managed_atoms.items) |atom| { | 296 | self.atoms.deinit(gpa); |
| 275 | gpa.destroy(atom); | ||
| 276 | } | ||
| 277 | self.managed_atoms.deinit(gpa); | ||
| 278 | |||
| 279 | self.locals.deinit(gpa); | 297 | self.locals.deinit(gpa); |
| 280 | self.globals.deinit(gpa); | 298 | self.globals.deinit(gpa); |
| 281 | 299 | ||
| ... | @@ -297,7 +315,15 @@ pub fn deinit(self: *Coff) void { | ... | @@ -297,7 +315,15 @@ pub fn deinit(self: *Coff) void { |
| 297 | self.imports.deinit(gpa); | 315 | self.imports.deinit(gpa); |
| 298 | self.imports_free_list.deinit(gpa); | 316 | self.imports_free_list.deinit(gpa); |
| 299 | self.imports_table.deinit(gpa); | 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 | self.atom_by_index_table.deinit(gpa); | 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,17 +487,18 @@ fn growSectionVM(self: *Coff, sect_id: u32, needed_size: u32) !void { |
| 461 | // TODO: enforce order by increasing VM addresses in self.sections container. | 487 | // TODO: enforce order by increasing VM addresses in self.sections container. |
| 462 | // This is required by the loader anyhow as far as I can tell. | 488 | // This is required by the loader anyhow as far as I can tell. |
| 463 | for (self.sections.items(.header)[sect_id + 1 ..]) |*next_header, next_sect_id| { | 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 | next_header.virtual_address += diff; | 491 | next_header.virtual_address += diff; |
| 466 | 492 | ||
| 467 | if (maybe_last_atom.*) |last_atom| { | 493 | if (maybe_last_atom_index) |last_atom_index| { |
| 468 | var atom = last_atom; | 494 | var atom_index = last_atom_index; |
| 469 | while (true) { | 495 | while (true) { |
| 496 | const atom = self.getAtom(atom_index); | ||
| 470 | const sym = atom.getSymbolPtr(self); | 497 | const sym = atom.getSymbolPtr(self); |
| 471 | sym.value += diff; | 498 | sym.value += diff; |
| 472 | 499 | ||
| 473 | if (atom.prev) |prev| { | 500 | if (atom.prev_index) |prev_index| { |
| 474 | atom = prev; | 501 | atom_index = prev_index; |
| 475 | } else break; | 502 | } else break; |
| 476 | } | 503 | } |
| 477 | } | 504 | } |
| ... | @@ -480,24 +507,15 @@ fn growSectionVM(self: *Coff, sect_id: u32, needed_size: u32) !void { | ... | @@ -480,24 +507,15 @@ fn growSectionVM(self: *Coff, sect_id: u32, needed_size: u32) !void { |
| 480 | header.virtual_size = increased_size; | 507 | header.virtual_size = increased_size; |
| 481 | } | 508 | } |
| 482 | 509 | ||
| 483 | pub fn allocateDeclIndexes(self: *Coff, decl_index: Module.Decl.Index) !void { | 510 | fn allocateAtom(self: *Coff, atom_index: Atom.Index, new_atom_size: u32, alignment: u32) !u32 { |
| 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 { | ||
| 494 | const tracy = trace(@src()); | 511 | const tracy = trace(@src()); |
| 495 | defer tracy.end(); | 512 | defer tracy.end(); |
| 496 | 513 | ||
| 514 | const atom = self.getAtom(atom_index); | ||
| 497 | const sect_id = @enumToInt(atom.getSymbol(self).section_number) - 1; | 515 | const sect_id = @enumToInt(atom.getSymbol(self).section_number) - 1; |
| 498 | const header = &self.sections.items(.header)[sect_id]; | 516 | const header = &self.sections.items(.header)[sect_id]; |
| 499 | const free_list = &self.sections.items(.free_list)[sect_id]; | 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 | const new_atom_ideal_capacity = if (header.isCode()) padToIdeal(new_atom_size) else new_atom_size; | 519 | const new_atom_ideal_capacity = if (header.isCode()) padToIdeal(new_atom_size) else new_atom_size; |
| 502 | 520 | ||
| 503 | // We use these to indicate our intention to update metadata, placing the new atom, | 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,7 +523,7 @@ fn allocateAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u |
| 505 | // It would be simpler to do it inside the for loop below, but that would cause a | 523 | // It would be simpler to do it inside the for loop below, but that would cause a |
| 506 | // problem if an error was returned later in the function. So this action | 524 | // problem if an error was returned later in the function. So this action |
| 507 | // is actually carried out at the end of the function, when errors are no longer possible. | 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 | var free_list_removal: ?usize = null; | 527 | var free_list_removal: ?usize = null; |
| 510 | 528 | ||
| 511 | // First we look for an appropriately sized free list node. | 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,7 +531,8 @@ fn allocateAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u |
| 513 | var vaddr = blk: { | 531 | var vaddr = blk: { |
| 514 | var i: usize = 0; | 532 | var i: usize = 0; |
| 515 | while (i < free_list.items.len) { | 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 | // We now have a pointer to a live atom that has too much capacity. | 536 | // We now have a pointer to a live atom that has too much capacity. |
| 518 | // Is it enough that we could fit this new atom? | 537 | // Is it enough that we could fit this new atom? |
| 519 | const sym = big_atom.getSymbol(self); | 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,34 +560,43 @@ fn allocateAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u |
| 541 | const keep_free_list_node = remaining_capacity >= min_text_capacity; | 560 | const keep_free_list_node = remaining_capacity >= min_text_capacity; |
| 542 | 561 | ||
| 543 | // Set up the metadata to be updated, after errors are no longer possible. | 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 | if (!keep_free_list_node) { | 564 | if (!keep_free_list_node) { |
| 546 | free_list_removal = i; | 565 | free_list_removal = i; |
| 547 | } | 566 | } |
| 548 | break :blk new_start_vaddr; | 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 | const last_symbol = last.getSymbol(self); | 570 | const last_symbol = last.getSymbol(self); |
| 551 | const ideal_capacity = if (header.isCode()) padToIdeal(last.size) else last.size; | 571 | const ideal_capacity = if (header.isCode()) padToIdeal(last.size) else last.size; |
| 552 | const ideal_capacity_end_vaddr = last_symbol.value + ideal_capacity; | 572 | const ideal_capacity_end_vaddr = last_symbol.value + ideal_capacity; |
| 553 | const new_start_vaddr = mem.alignForwardGeneric(u32, ideal_capacity_end_vaddr, alignment); | 573 | const new_start_vaddr = mem.alignForwardGeneric(u32, ideal_capacity_end_vaddr, alignment); |
| 554 | atom_placement = last; | 574 | atom_placement = last_index; |
| 555 | break :blk new_start_vaddr; | 575 | break :blk new_start_vaddr; |
| 556 | } else { | 576 | } else { |
| 557 | break :blk mem.alignForwardGeneric(u32, header.virtual_address, alignment); | 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 | if (expand_section) { | 585 | if (expand_section) { |
| 563 | const sect_capacity = self.allocatedSize(header.pointer_to_raw_data); | 586 | const sect_capacity = self.allocatedSize(header.pointer_to_raw_data); |
| 564 | const needed_size: u32 = (vaddr + new_atom_size) - header.virtual_address; | 587 | const needed_size: u32 = (vaddr + new_atom_size) - header.virtual_address; |
| 565 | if (needed_size > sect_capacity) { | 588 | if (needed_size > sect_capacity) { |
| 566 | const new_offset = self.findFreeSpace(needed_size, default_file_alignment); | 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 | const sym = last_atom.getSymbol(self); | 592 | const sym = last_atom.getSymbol(self); |
| 569 | break :blk (sym.value + last_atom.size) - header.virtual_address; | 593 | break :blk (sym.value + last_atom.size) - header.virtual_address; |
| 570 | } else 0; | 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 | const amt = try self.base.file.?.copyRangeAll( | 600 | const amt = try self.base.file.?.copyRangeAll( |
| 573 | header.pointer_to_raw_data, | 601 | header.pointer_to_raw_data, |
| 574 | self.base.file.?, | 602 | self.base.file.?, |
| ... | @@ -587,26 +615,34 @@ fn allocateAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u | ... | @@ -587,26 +615,34 @@ fn allocateAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u |
| 587 | 615 | ||
| 588 | header.virtual_size = @max(header.virtual_size, needed_size); | 616 | header.virtual_size = @max(header.virtual_size, needed_size); |
| 589 | header.size_of_raw_data = needed_size; | 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; | 621 | { |
| 594 | atom.alignment = alignment; | 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| { | 627 | if (atom.prev_index) |prev_index| { |
| 597 | prev.next = atom.next; | 628 | const prev = self.getAtomPtr(prev_index); |
| 629 | prev.next_index = atom.next_index; | ||
| 598 | } | 630 | } |
| 599 | if (atom.next) |next| { | 631 | if (atom.next_index) |next_index| { |
| 600 | next.prev = atom.prev; | 632 | const next = self.getAtomPtr(next_index); |
| 633 | next.prev_index = atom.prev_index; | ||
| 601 | } | 634 | } |
| 602 | 635 | ||
| 603 | if (atom_placement) |big_atom| { | 636 | if (atom_placement) |big_atom_index| { |
| 604 | atom.prev = big_atom; | 637 | const big_atom = self.getAtomPtr(big_atom_index); |
| 605 | atom.next = big_atom.next; | 638 | const atom_ptr = self.getAtomPtr(atom_index); |
| 606 | big_atom.next = atom; | 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 | } else { | 642 | } else { |
| 608 | atom.prev = null; | 643 | const atom_ptr = self.getAtomPtr(atom_index); |
| 609 | atom.next = null; | 644 | atom_ptr.prev_index = null; |
| 645 | atom_ptr.next_index = null; | ||
| 610 | } | 646 | } |
| 611 | if (free_list_removal) |i| { | 647 | if (free_list_removal) |i| { |
| 612 | _ = free_list.swapRemove(i); | 648 | _ = free_list.swapRemove(i); |
| ... | @@ -615,7 +651,7 @@ fn allocateAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u | ... | @@ -615,7 +651,7 @@ fn allocateAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u |
| 615 | return vaddr; | 651 | return vaddr; |
| 616 | } | 652 | } |
| 617 | 653 | ||
| 618 | fn allocateSymbol(self: *Coff) !u32 { | 654 | pub fn allocateSymbol(self: *Coff) !u32 { |
| 619 | const gpa = self.base.allocator; | 655 | const gpa = self.base.allocator; |
| 620 | try self.locals.ensureUnusedCapacity(gpa, 1); | 656 | try self.locals.ensureUnusedCapacity(gpa, 1); |
| 621 | 657 | ||
| ... | @@ -711,25 +747,37 @@ pub fn allocateImportEntry(self: *Coff, target: SymbolWithLoc) !u32 { | ... | @@ -711,25 +747,37 @@ pub fn allocateImportEntry(self: *Coff, target: SymbolWithLoc) !u32 { |
| 711 | return index; | 747 | return index; |
| 712 | } | 748 | } |
| 713 | 749 | ||
| 714 | fn createGotAtom(self: *Coff, target: SymbolWithLoc) !*Atom { | 750 | pub fn createAtom(self: *Coff) !Atom.Index { |
| 715 | const gpa = self.base.allocator; | 751 | const gpa = self.base.allocator; |
| 716 | const atom = try gpa.create(Atom); | 752 | const atom_index = @intCast(Atom.Index, self.atoms.items.len); |
| 717 | errdefer gpa.destroy(atom); | 753 | const atom = try self.atoms.addOne(gpa); |
| 718 | atom.* = Atom.empty; | 754 | const sym_index = try self.allocateSymbol(); |
| 719 | atom.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 | atom.size = @sizeOf(u64); | 771 | atom.size = @sizeOf(u64); |
| 721 | atom.alignment = @alignOf(u64); | 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 | const sym = atom.getSymbolPtr(self); | 774 | const sym = atom.getSymbolPtr(self); |
| 727 | sym.section_number = @intToEnum(coff.SectionNumber, self.got_section_index.? + 1); | 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 | log.debug("allocated GOT atom at 0x{x}", .{sym.value}); | 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 | .type = .direct, | 781 | .type = .direct, |
| 734 | .target = target, | 782 | .target = target, |
| 735 | .offset = 0, | 783 | .offset = 0, |
| ... | @@ -743,67 +791,67 @@ fn createGotAtom(self: *Coff, target: SymbolWithLoc) !*Atom { | ... | @@ -743,67 +791,67 @@ fn createGotAtom(self: *Coff, target: SymbolWithLoc) !*Atom { |
| 743 | .UNDEFINED => @panic("TODO generate a binding for undefined GOT target"), | 791 | .UNDEFINED => @panic("TODO generate a binding for undefined GOT target"), |
| 744 | .ABSOLUTE => {}, | 792 | .ABSOLUTE => {}, |
| 745 | .DEBUG => unreachable, // not possible | 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 { | 800 | fn createImportAtom(self: *Coff) !Atom.Index { |
| 753 | const gpa = self.base.allocator; | 801 | const atom_index = try self.createAtom(); |
| 754 | const atom = try gpa.create(Atom); | 802 | const atom = self.getAtomPtr(atom_index); |
| 755 | errdefer gpa.destroy(atom); | ||
| 756 | atom.* = Atom.empty; | ||
| 757 | atom.sym_index = try self.allocateSymbol(); | ||
| 758 | atom.size = @sizeOf(u64); | 803 | atom.size = @sizeOf(u64); |
| 759 | atom.alignment = @alignOf(u64); | 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 | const sym = atom.getSymbolPtr(self); | 806 | const sym = atom.getSymbolPtr(self); |
| 765 | sym.section_number = @intToEnum(coff.SectionNumber, self.idata_section_index.? + 1); | 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 | log.debug("allocated import atom at 0x{x}", .{sym.value}); | 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 | const sym = atom.getSymbol(self); | 817 | const sym = atom.getSymbol(self); |
| 775 | const align_ok = mem.alignBackwardGeneric(u32, sym.value, alignment) == sym.value; | 818 | const align_ok = mem.alignBackwardGeneric(u32, sym.value, alignment) == sym.value; |
| 776 | const need_realloc = !align_ok or new_atom_size > atom.capacity(self); | 819 | const need_realloc = !align_ok or new_atom_size > atom.capacity(self); |
| 777 | if (!need_realloc) return sym.value; | 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 | _ = self; | 825 | _ = self; |
| 783 | _ = atom; | 826 | _ = atom_index; |
| 784 | _ = new_block_size; | 827 | _ = new_block_size; |
| 785 | // TODO check the new capacity, and if it crosses the size threshold into a big enough | 828 | // TODO check the new capacity, and if it crosses the size threshold into a big enough |
| 786 | // capacity, insert a free list node for it. | 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 | const sym = atom.getSymbol(self); | 834 | const sym = atom.getSymbol(self); |
| 791 | const section = self.sections.get(@enumToInt(sym.section_number) - 1); | 835 | const section = self.sections.get(@enumToInt(sym.section_number) - 1); |
| 792 | const file_offset = section.header.pointer_to_raw_data + sym.value - section.header.virtual_address; | 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 | try self.base.file.?.pwriteAll(code, file_offset); | 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 | switch (self.ptr_width) { | 847 | switch (self.ptr_width) { |
| 800 | .p32 => { | 848 | .p32 => { |
| 801 | var buffer: [@sizeOf(u32)]u8 = [_]u8{0} ** @sizeOf(u32); | 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 | .p64 => { | 852 | .p64 => { |
| 805 | var buffer: [@sizeOf(u64)]u8 = [_]u8{0} ** @sizeOf(u64); | 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,7 +871,8 @@ fn markRelocsDirtyByAddress(self: *Coff, addr: u32) void { |
| 823 | var it = self.relocs.valueIterator(); | 871 | var it = self.relocs.valueIterator(); |
| 824 | while (it.next()) |relocs| { | 872 | while (it.next()) |relocs| { |
| 825 | for (relocs.items) |*reloc| { | 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 | const target_sym = target_atom.getSymbol(self); | 876 | const target_sym = target_atom.getSymbol(self); |
| 828 | if (target_sym.value < addr) continue; | 877 | if (target_sym.value < addr) continue; |
| 829 | reloc.dirty = true; | 878 | reloc.dirty = true; |
| ... | @@ -831,23 +880,26 @@ fn markRelocsDirtyByAddress(self: *Coff, addr: u32) void { | ... | @@ -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 { | 883 | fn resolveRelocs(self: *Coff, atom_index: Atom.Index) !void { |
| 835 | const relocs = self.relocs.get(atom) orelse return; | 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 | for (relocs.items) |*reloc| { | 888 | for (relocs.items) |*reloc| { |
| 840 | if (!reloc.dirty) continue; | 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 { | 894 | fn freeAtom(self: *Coff, atom_index: Atom.Index) void { |
| 846 | log.debug("freeAtom {*}", .{atom}); | 895 | log.debug("freeAtom {d}", .{atom_index}); |
| 896 | |||
| 897 | const gpa = self.base.allocator; | ||
| 847 | 898 | ||
| 848 | // Remove any relocs and base relocs associated with this Atom | 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 | const sym = atom.getSymbol(self); | 903 | const sym = atom.getSymbol(self); |
| 852 | const sect_id = @enumToInt(sym.section_number) - 1; | 904 | const sect_id = @enumToInt(sym.section_number) - 1; |
| 853 | const free_list = &self.sections.items(.free_list)[sect_id]; | 905 | const free_list = &self.sections.items(.free_list)[sect_id]; |
| ... | @@ -856,46 +908,69 @@ fn freeAtom(self: *Coff, atom: *Atom) void { | ... | @@ -856,46 +908,69 @@ fn freeAtom(self: *Coff, atom: *Atom) void { |
| 856 | var i: usize = 0; | 908 | var i: usize = 0; |
| 857 | // TODO turn free_list into a hash map | 909 | // TODO turn free_list into a hash map |
| 858 | while (i < free_list.items.len) { | 910 | while (i < free_list.items.len) { |
| 859 | if (free_list.items[i] == atom) { | 911 | if (free_list.items[i] == atom_index) { |
| 860 | _ = free_list.swapRemove(i); | 912 | _ = free_list.swapRemove(i); |
| 861 | continue; | 913 | continue; |
| 862 | } | 914 | } |
| 863 | if (free_list.items[i] == atom.prev) { | 915 | if (free_list.items[i] == atom.prev_index) { |
| 864 | already_have_free_list_node = true; | 916 | already_have_free_list_node = true; |
| 865 | } | 917 | } |
| 866 | i += 1; | 918 | i += 1; |
| 867 | } | 919 | } |
| 868 | } | 920 | } |
| 869 | 921 | ||
| 870 | const maybe_last_atom = &self.sections.items(.last_atom)[sect_id]; | 922 | const maybe_last_atom_index = &self.sections.items(.last_atom_index)[sect_id]; |
| 871 | if (maybe_last_atom.*) |last_atom| { | 923 | if (maybe_last_atom_index.*) |last_atom_index| { |
| 872 | if (last_atom == atom) { | 924 | if (last_atom_index == atom_index) { |
| 873 | if (atom.prev) |prev| { | 925 | if (atom.prev_index) |prev_index| { |
| 874 | // TODO shrink the section size here | 926 | // TODO shrink the section size here |
| 875 | maybe_last_atom.* = prev; | 927 | maybe_last_atom_index.* = prev_index; |
| 876 | } else { | 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| { | 934 | if (atom.prev_index) |prev_index| { |
| 883 | prev.next = atom.next; | 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 | // The free list is heuristics, it doesn't have to be perfect, so we can | 939 | // The free list is heuristics, it doesn't have to be perfect, so we can |
| 887 | // ignore the OOM here. | 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 | } else { | 943 | } else { |
| 891 | atom.prev = null; | 944 | self.getAtomPtr(atom_index).prev_index = null; |
| 892 | } | 945 | } |
| 893 | 946 | ||
| 894 | if (atom.next) |next| { | 947 | if (atom.next_index) |next_index| { |
| 895 | next.prev = atom.prev; | 948 | self.getAtomPtr(next_index).prev_index = atom.prev_index; |
| 896 | } else { | 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 | pub fn updateFunc(self: *Coff, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void { | 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,8 +987,10 @@ pub fn updateFunc(self: *Coff, module: *Module, func: *Module.Fn, air: Air, live |
| 912 | 987 | ||
| 913 | const decl_index = func.owner_decl; | 988 | const decl_index = func.owner_decl; |
| 914 | const decl = module.declPtr(decl_index); | 989 | const decl = module.declPtr(decl_index); |
| 990 | |||
| 991 | const atom_index = try self.getOrCreateAtomForDecl(decl_index); | ||
| 915 | self.freeUnnamedConsts(decl_index); | 992 | self.freeUnnamedConsts(decl_index); |
| 916 | self.freeRelocationsForAtom(&decl.link.coff); | 993 | Atom.freeRelocations(self, atom_index); |
| 917 | 994 | ||
| 918 | var code_buffer = std.ArrayList(u8).init(self.base.allocator); | 995 | var code_buffer = std.ArrayList(u8).init(self.base.allocator); |
| 919 | defer code_buffer.deinit(); | 996 | defer code_buffer.deinit(); |
| ... | @@ -928,7 +1005,7 @@ pub fn updateFunc(self: *Coff, module: *Module, func: *Module.Fn, air: Air, live | ... | @@ -928,7 +1005,7 @@ pub fn updateFunc(self: *Coff, module: *Module, func: *Module.Fn, air: Air, live |
| 928 | .none, | 1005 | .none, |
| 929 | ); | 1006 | ); |
| 930 | const code = switch (res) { | 1007 | const code = switch (res) { |
| 931 | .appended => code_buffer.items, | 1008 | .ok => code_buffer.items, |
| 932 | .fail => |em| { | 1009 | .fail => |em| { |
| 933 | decl.analysis = .codegen_failure; | 1010 | decl.analysis = .codegen_failure; |
| 934 | try module.failed_decls.put(module.gpa, decl_index, em); | 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,12 +1034,8 @@ pub fn lowerUnnamedConst(self: *Coff, tv: TypedValue, decl_index: Module.Decl.In |
| 957 | } | 1034 | } |
| 958 | const unnamed_consts = gop.value_ptr; | 1035 | const unnamed_consts = gop.value_ptr; |
| 959 | 1036 | ||
| 960 | const atom = try gpa.create(Atom); | 1037 | const atom_index = try self.createAtom(); |
| 961 | errdefer gpa.destroy(atom); | ||
| 962 | atom.* = Atom.empty; | ||
| 963 | 1038 | ||
| 964 | atom.sym_index = try self.allocateSymbol(); | ||
| 965 | const sym = atom.getSymbolPtr(self); | ||
| 966 | const sym_name = blk: { | 1039 | const sym_name = blk: { |
| 967 | const decl_name = try decl.getFullyQualifiedName(mod); | 1040 | const decl_name = try decl.getFullyQualifiedName(mod); |
| 968 | defer gpa.free(decl_name); | 1041 | defer gpa.free(decl_name); |
| ... | @@ -971,18 +1044,18 @@ pub fn lowerUnnamedConst(self: *Coff, tv: TypedValue, decl_index: Module.Decl.In | ... | @@ -971,18 +1044,18 @@ pub fn lowerUnnamedConst(self: *Coff, tv: TypedValue, decl_index: Module.Decl.In |
| 971 | break :blk try std.fmt.allocPrint(gpa, "__unnamed_{s}_{d}", .{ decl_name, index }); | 1044 | break :blk try std.fmt.allocPrint(gpa, "__unnamed_{s}_{d}", .{ decl_name, index }); |
| 972 | }; | 1045 | }; |
| 973 | defer gpa.free(sym_name); | 1046 | defer gpa.free(sym_name); |
| 974 | try self.setSymbolName(sym, sym_name); | 1047 | { |
| 975 | sym.section_number = @intToEnum(coff.SectionNumber, self.rdata_section_index.? + 1); | 1048 | const atom = self.getAtom(atom_index); |
| 976 | 1049 | const sym = atom.getSymbolPtr(self); | |
| 977 | try self.managed_atoms.append(gpa, atom); | 1050 | try self.setSymbolName(sym, sym_name); |
| 978 | try self.atom_by_index_table.putNoClobber(gpa, atom.sym_index, atom); | 1051 | sym.section_number = @intToEnum(coff.SectionNumber, self.rdata_section_index.? + 1); |
| 1052 | } | ||
| 979 | 1053 | ||
| 980 | const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), tv, &code_buffer, .none, .{ | 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 | const code = switch (res) { | 1057 | const code = switch (res) { |
| 984 | .externally_managed => |x| x, | 1058 | .ok => code_buffer.items, |
| 985 | .appended => code_buffer.items, | ||
| 986 | .fail => |em| { | 1059 | .fail => |em| { |
| 987 | decl.analysis = .codegen_failure; | 1060 | decl.analysis = .codegen_failure; |
| 988 | try mod.failed_decls.put(mod.gpa, decl_index, em); | 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,19 +1065,20 @@ pub fn lowerUnnamedConst(self: *Coff, tv: TypedValue, decl_index: Module.Decl.In |
| 992 | }; | 1065 | }; |
| 993 | 1066 | ||
| 994 | const required_alignment = tv.ty.abiAlignment(self.base.options.target); | 1067 | const required_alignment = tv.ty.abiAlignment(self.base.options.target); |
| 1068 | const atom = self.getAtomPtr(atom_index); | ||
| 995 | atom.alignment = required_alignment; | 1069 | atom.alignment = required_alignment; |
| 996 | atom.size = @intCast(u32, code.len); | 1070 | atom.size = @intCast(u32, code.len); |
| 997 | sym.value = try self.allocateAtom(atom, atom.size, atom.alignment); | 1071 | atom.getSymbolPtr(self).value = try self.allocateAtom(atom_index, atom.size, atom.alignment); |
| 998 | errdefer self.freeAtom(atom); | 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 | log.debug(" (required alignment 0x{x})", .{required_alignment}); | 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 | pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) !void { | 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,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 | var code_buffer = std.ArrayList(u8).init(self.base.allocator); | 1110 | var code_buffer = std.ArrayList(u8).init(self.base.allocator); |
| 1035 | defer code_buffer.deinit(); | 1111 | defer code_buffer.deinit(); |
| ... | @@ -1039,11 +1115,10 @@ pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) ! | ... | @@ -1039,11 +1115,10 @@ pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) ! |
| 1039 | .ty = decl.ty, | 1115 | .ty = decl.ty, |
| 1040 | .val = decl_val, | 1116 | .val = decl_val, |
| 1041 | }, &code_buffer, .none, .{ | 1117 | }, &code_buffer, .none, .{ |
| 1042 | .parent_atom_index = decl.link.coff.sym_index, | 1118 | .parent_atom_index = atom.getSymbolIndex().?, |
| 1043 | }); | 1119 | }); |
| 1044 | const code = switch (res) { | 1120 | const code = switch (res) { |
| 1045 | .externally_managed => |x| x, | 1121 | .ok => code_buffer.items, |
| 1046 | .appended => code_buffer.items, | ||
| 1047 | .fail => |em| { | 1122 | .fail => |em| { |
| 1048 | decl.analysis = .codegen_failure; | 1123 | decl.analysis = .codegen_failure; |
| 1049 | try module.failed_decls.put(module.gpa, decl_index, em); | 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,7 +1133,20 @@ pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) ! |
| 1058 | return self.updateDeclExports(module, decl_index, module.getDeclExports(decl_index)); | 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 | const ty = decl.ty; | 1150 | const ty = decl.ty; |
| 1063 | const zig_ty = ty.zigTypeTag(); | 1151 | const zig_ty = ty.zigTypeTag(); |
| 1064 | const val = decl.val; | 1152 | const val = decl.val; |
| ... | @@ -1093,15 +1181,12 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []const u8, | ... | @@ -1093,15 +1181,12 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []const u8, |
| 1093 | log.debug("updateDeclCode {s}{*}", .{ decl_name, decl }); | 1181 | log.debug("updateDeclCode {s}{*}", .{ decl_name, decl }); |
| 1094 | const required_alignment = decl.getAlignment(self.base.options.target); | 1182 | const required_alignment = decl.getAlignment(self.base.options.target); |
| 1095 | 1183 | ||
| 1096 | const decl_ptr = self.decls.getPtr(decl_index).?; | 1184 | const decl_metadata = self.decls.get(decl_index).?; |
| 1097 | if (decl_ptr.* == null) { | 1185 | const atom_index = decl_metadata.atom; |
| 1098 | decl_ptr.* = self.getDeclOutputSection(decl); | 1186 | const atom = self.getAtom(atom_index); |
| 1099 | } | 1187 | const sect_index = decl_metadata.section; |
| 1100 | const sect_index = decl_ptr.*.?; | ||
| 1101 | |||
| 1102 | const code_len = @intCast(u32, code.len); | 1188 | const code_len = @intCast(u32, code.len); |
| 1103 | const atom = &decl.link.coff; | 1189 | |
| 1104 | assert(atom.sym_index != 0); // Caller forgot to allocateDeclIndexes() | ||
| 1105 | if (atom.size != 0) { | 1190 | if (atom.size != 0) { |
| 1106 | const sym = atom.getSymbolPtr(self); | 1191 | const sym = atom.getSymbolPtr(self); |
| 1107 | try self.setSymbolName(sym, decl_name); | 1192 | try self.setSymbolName(sym, decl_name); |
| ... | @@ -1111,62 +1196,51 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []const u8, | ... | @@ -1111,62 +1196,51 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []const u8, |
| 1111 | const capacity = atom.capacity(self); | 1196 | const capacity = atom.capacity(self); |
| 1112 | const need_realloc = code.len > capacity or !mem.isAlignedGeneric(u64, sym.value, required_alignment); | 1197 | const need_realloc = code.len > capacity or !mem.isAlignedGeneric(u64, sym.value, required_alignment); |
| 1113 | if (need_realloc) { | 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 | log.debug("growing {s} from 0x{x} to 0x{x}", .{ decl_name, sym.value, vaddr }); | 1200 | log.debug("growing {s} from 0x{x} to 0x{x}", .{ decl_name, sym.value, vaddr }); |
| 1116 | log.debug(" (required alignment 0x{x}", .{required_alignment}); | 1201 | log.debug(" (required alignment 0x{x}", .{required_alignment}); |
| 1117 | 1202 | ||
| 1118 | if (vaddr != sym.value) { | 1203 | if (vaddr != sym.value) { |
| 1119 | sym.value = vaddr; | 1204 | sym.value = vaddr; |
| 1120 | log.debug(" (updating GOT entry)", .{}); | 1205 | log.debug(" (updating GOT entry)", .{}); |
| 1121 | const got_target = SymbolWithLoc{ .sym_index = atom.sym_index, .file = null }; | 1206 | const got_target = SymbolWithLoc{ .sym_index = atom.getSymbolIndex().?, .file = null }; |
| 1122 | const got_atom = self.getGotAtomForSymbol(got_target).?; | 1207 | const got_atom_index = self.getGotAtomIndexForSymbol(got_target).?; |
| 1123 | self.markRelocsDirtyByTarget(got_target); | 1208 | self.markRelocsDirtyByTarget(got_target); |
| 1124 | try self.writePtrWidthAtom(got_atom); | 1209 | try self.writePtrWidthAtom(got_atom_index); |
| 1125 | } | 1210 | } |
| 1126 | } else if (code_len < atom.size) { | 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 | } else { | 1215 | } else { |
| 1131 | const sym = atom.getSymbolPtr(self); | 1216 | const sym = atom.getSymbolPtr(self); |
| 1132 | try self.setSymbolName(sym, decl_name); | 1217 | try self.setSymbolName(sym, decl_name); |
| 1133 | sym.section_number = @intToEnum(coff.SectionNumber, sect_index + 1); | 1218 | sym.section_number = @intToEnum(coff.SectionNumber, sect_index + 1); |
| 1134 | sym.type = .{ .complex_type = complex_type, .base_type = .NULL }; | 1219 | sym.type = .{ .complex_type = complex_type, .base_type = .NULL }; |
| 1135 | 1220 | ||
| 1136 | const vaddr = try self.allocateAtom(atom, code_len, required_alignment); | 1221 | const vaddr = try self.allocateAtom(atom_index, code_len, required_alignment); |
| 1137 | errdefer self.freeAtom(atom); | 1222 | errdefer self.freeAtom(atom_index); |
| 1138 | log.debug("allocated atom for {s} at 0x{x}", .{ decl_name, vaddr }); | 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 | sym.value = vaddr; | 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 | const got_index = try self.allocateGotEntry(got_target); | 1228 | const got_index = try self.allocateGotEntry(got_target); |
| 1144 | const got_atom = try self.createGotAtom(got_target); | 1229 | const got_atom_index = try self.createGotAtom(got_target); |
| 1145 | self.got_entries.items[got_index].sym_index = got_atom.sym_index; | 1230 | const got_atom = self.getAtom(got_atom_index); |
| 1146 | try self.writePtrWidthAtom(got_atom); | 1231 | self.got_entries.items[got_index].sym_index = got_atom.getSymbolIndex().?; |
| 1232 | try self.writePtrWidthAtom(got_atom_index); | ||
| 1147 | } | 1233 | } |
| 1148 | 1234 | ||
| 1149 | self.markRelocsDirtyByTarget(atom.getSymbolWithLoc()); | 1235 | self.markRelocsDirtyByTarget(atom.getSymbolWithLoc()); |
| 1150 | try self.writeAtom(atom, code); | 1236 | try self.writeAtom(atom_index, 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); | ||
| 1158 | } | 1237 | } |
| 1159 | 1238 | ||
| 1160 | fn freeUnnamedConsts(self: *Coff, decl_index: Module.Decl.Index) void { | 1239 | fn freeUnnamedConsts(self: *Coff, decl_index: Module.Decl.Index) void { |
| 1161 | const gpa = self.base.allocator; | 1240 | const gpa = self.base.allocator; |
| 1162 | const unnamed_consts = self.unnamed_const_atoms.getPtr(decl_index) orelse return; | 1241 | const unnamed_consts = self.unnamed_const_atoms.getPtr(decl_index) orelse return; |
| 1163 | for (unnamed_consts.items) |atom| { | 1242 | for (unnamed_consts.items) |atom_index| { |
| 1164 | self.freeAtom(atom); | 1243 | self.freeAtom(atom_index); |
| 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; | ||
| 1170 | } | 1244 | } |
| 1171 | unnamed_consts.clearAndFree(gpa); | 1245 | unnamed_consts.clearAndFree(gpa); |
| 1172 | } | 1246 | } |
| ... | @@ -1181,35 +1255,11 @@ pub fn freeDecl(self: *Coff, decl_index: Module.Decl.Index) void { | ... | @@ -1181,35 +1255,11 @@ pub fn freeDecl(self: *Coff, decl_index: Module.Decl.Index) void { |
| 1181 | 1255 | ||
| 1182 | log.debug("freeDecl {*}", .{decl}); | 1256 | log.debug("freeDecl {*}", .{decl}); |
| 1183 | 1257 | ||
| 1184 | const kv = self.decls.fetchRemove(decl_index); | 1258 | if (self.decls.fetchRemove(decl_index)) |const_kv| { |
| 1185 | if (kv.?.value) |_| { | 1259 | var kv = const_kv; |
| 1186 | self.freeAtom(&decl.link.coff); | 1260 | self.freeAtom(kv.value.atom); |
| 1187 | self.freeUnnamedConsts(decl_index); | 1261 | self.freeUnnamedConsts(decl_index); |
| 1188 | } | 1262 | kv.value.exports.deinit(self.base.allocator); |
| 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; | ||
| 1213 | } | 1263 | } |
| 1214 | } | 1264 | } |
| 1215 | 1265 | ||
| ... | @@ -1262,9 +1312,10 @@ pub fn updateDeclExports( | ... | @@ -1262,9 +1312,10 @@ pub fn updateDeclExports( |
| 1262 | const gpa = self.base.allocator; | 1312 | const gpa = self.base.allocator; |
| 1263 | 1313 | ||
| 1264 | const decl = module.declPtr(decl_index); | 1314 | const decl = module.declPtr(decl_index); |
| 1265 | const atom = &decl.link.coff; | 1315 | const atom_index = try self.getOrCreateAtomForDecl(decl_index); |
| 1266 | if (atom.sym_index == 0) return; | 1316 | const atom = self.getAtom(atom_index); |
| 1267 | const decl_sym = atom.getSymbol(self); | 1317 | const decl_sym = atom.getSymbol(self); |
| 1318 | const decl_metadata = self.decls.getPtr(decl_index).?; | ||
| 1268 | 1319 | ||
| 1269 | for (exports) |exp| { | 1320 | for (exports) |exp| { |
| 1270 | log.debug("adding new export '{s}'", .{exp.options.name}); | 1321 | log.debug("adding new export '{s}'", .{exp.options.name}); |
| ... | @@ -1299,9 +1350,9 @@ pub fn updateDeclExports( | ... | @@ -1299,9 +1350,9 @@ pub fn updateDeclExports( |
| 1299 | continue; | 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 | const sym_index = try self.allocateSymbol(); | 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 | break :blk sym_index; | 1356 | break :blk sym_index; |
| 1306 | }; | 1357 | }; |
| 1307 | const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = null }; | 1358 | const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = null }; |
| ... | @@ -1324,16 +1375,15 @@ pub fn updateDeclExports( | ... | @@ -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 | if (self.llvm_object) |_| return; | 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 | const gpa = self.base.allocator; | 1383 | const gpa = self.base.allocator; |
| 1332 | 1384 | const sym_loc = SymbolWithLoc{ .sym_index = sym_index.*, .file = null }; | |
| 1333 | const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = null }; | ||
| 1334 | const sym = self.getSymbolPtr(sym_loc); | 1385 | const sym = self.getSymbolPtr(sym_loc); |
| 1335 | const sym_name = self.getSymbolName(sym_loc); | 1386 | log.debug("deleting export '{s}'", .{name}); |
| 1336 | log.debug("deleting export '{s}'", .{sym_name}); | ||
| 1337 | assert(sym.storage_class == .EXTERNAL and sym.section_number != .UNDEFINED); | 1387 | assert(sym.storage_class == .EXTERNAL and sym.section_number != .UNDEFINED); |
| 1338 | sym.* = .{ | 1388 | sym.* = .{ |
| 1339 | .name = [_]u8{0} ** 8, | 1389 | .name = [_]u8{0} ** 8, |
| ... | @@ -1343,9 +1393,9 @@ pub fn deleteExport(self: *Coff, exp: Export) void { | ... | @@ -1343,9 +1393,9 @@ pub fn deleteExport(self: *Coff, exp: Export) void { |
| 1343 | .storage_class = .NULL, | 1393 | .storage_class = .NULL, |
| 1344 | .number_of_aux_symbols = 0, | 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 | defer gpa.free(entry.key); | 1399 | defer gpa.free(entry.key); |
| 1350 | self.globals_free_list.append(gpa, entry.value) catch {}; | 1400 | self.globals_free_list.append(gpa, entry.value) catch {}; |
| 1351 | self.globals.items[entry.value] = .{ | 1401 | self.globals.items[entry.value] = .{ |
| ... | @@ -1353,6 +1403,8 @@ pub fn deleteExport(self: *Coff, exp: Export) void { | ... | @@ -1353,6 +1403,8 @@ pub fn deleteExport(self: *Coff, exp: Export) void { |
| 1353 | .file = null, | 1403 | .file = null, |
| 1354 | }; | 1404 | }; |
| 1355 | } | 1405 | } |
| 1406 | |||
| 1407 | sym_index.* = 0; | ||
| 1356 | } | 1408 | } |
| 1357 | 1409 | ||
| 1358 | fn resolveGlobalSymbol(self: *Coff, current: SymbolWithLoc) !void { | 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,9 +1469,10 @@ pub fn flushModule(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod |
| 1417 | if (self.imports_table.contains(global)) continue; | 1469 | if (self.imports_table.contains(global)) continue; |
| 1418 | 1470 | ||
| 1419 | const import_index = try self.allocateImportEntry(global); | 1471 | const import_index = try self.allocateImportEntry(global); |
| 1420 | const import_atom = try self.createImportAtom(); | 1472 | const import_atom_index = try self.createImportAtom(); |
| 1421 | self.imports.items[import_index].sym_index = import_atom.sym_index; | 1473 | const import_atom = self.getAtom(import_atom_index); |
| 1422 | try self.writePtrWidthAtom(import_atom); | 1474 | self.imports.items[import_index].sym_index = import_atom.getSymbolIndex().?; |
| 1475 | try self.writePtrWidthAtom(import_atom_index); | ||
| 1423 | } | 1476 | } |
| 1424 | 1477 | ||
| 1425 | if (build_options.enable_logging) { | 1478 | if (build_options.enable_logging) { |
| ... | @@ -1453,20 +1506,14 @@ pub fn flushModule(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod | ... | @@ -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( | 1509 | pub fn getDeclVAddr(self: *Coff, decl_index: Module.Decl.Index, reloc_info: link.File.RelocInfo) !u64 { |
| 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 | |||
| 1464 | assert(self.llvm_object == null); | 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 }).?; | 1512 | const this_atom_index = try self.getOrCreateAtomForDecl(decl_index); |
| 1468 | const target = SymbolWithLoc{ .sym_index = decl.link.coff.sym_index, .file = null }; | 1513 | const sym_index = self.getAtom(this_atom_index).getSymbolIndex().?; |
| 1469 | try atom.addRelocation(self, .{ | 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 | .type = .direct, | 1517 | .type = .direct, |
| 1471 | .target = target, | 1518 | .target = target, |
| 1472 | .offset = @intCast(u32, reloc_info.offset), | 1519 | .offset = @intCast(u32, reloc_info.offset), |
| ... | @@ -1474,7 +1521,7 @@ pub fn getDeclVAddr( | ... | @@ -1474,7 +1521,7 @@ pub fn getDeclVAddr( |
| 1474 | .pcrel = false, | 1521 | .pcrel = false, |
| 1475 | .length = 3, | 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 | return 0; | 1526 | return 0; |
| 1480 | } | 1527 | } |
| ... | @@ -1501,10 +1548,10 @@ pub fn getGlobalSymbol(self: *Coff, name: []const u8) !u32 { | ... | @@ -1501,10 +1548,10 @@ pub fn getGlobalSymbol(self: *Coff, name: []const u8) !u32 { |
| 1501 | return global_index; | 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 | _ = self; | 1552 | _ = self; |
| 1506 | _ = module; | 1553 | _ = module; |
| 1507 | _ = decl; | 1554 | _ = decl_index; |
| 1508 | log.debug("TODO implement updateDeclLineNumber", .{}); | 1555 | log.debug("TODO implement updateDeclLineNumber", .{}); |
| 1509 | } | 1556 | } |
| 1510 | 1557 | ||
| ... | @@ -1525,7 +1572,8 @@ fn writeBaseRelocations(self: *Coff) !void { | ... | @@ -1525,7 +1572,8 @@ fn writeBaseRelocations(self: *Coff) !void { |
| 1525 | 1572 | ||
| 1526 | var it = self.base_relocs.iterator(); | 1573 | var it = self.base_relocs.iterator(); |
| 1527 | while (it.next()) |entry| { | 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 | const offsets = entry.value_ptr.*; | 1577 | const offsets = entry.value_ptr.*; |
| 1530 | 1578 | ||
| 1531 | for (offsets.items) |offset| { | 1579 | for (offsets.items) |offset| { |
| ... | @@ -1609,7 +1657,8 @@ fn writeImportTable(self: *Coff) !void { | ... | @@ -1609,7 +1657,8 @@ fn writeImportTable(self: *Coff) !void { |
| 1609 | const gpa = self.base.allocator; | 1657 | const gpa = self.base.allocator; |
| 1610 | 1658 | ||
| 1611 | const section = self.sections.get(self.idata_section_index.?); | 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 | const iat_rva = section.header.virtual_address; | 1663 | const iat_rva = section.header.virtual_address; |
| 1615 | const iat_size = last_atom.getSymbol(self).value + last_atom.size * 2 - iat_rva; // account for sentinel zero pointer | 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,27 +2096,37 @@ pub fn getOrPutGlobalPtr(self: *Coff, name: []const u8) !GetOrPutGlobalPtrResult |
| 2047 | return GetOrPutGlobalPtrResult{ .found_existing = false, .value_ptr = ptr }; | 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 | /// Returns atom if there is an atom referenced by the symbol described by `sym_loc` descriptor. | 2109 | /// Returns atom if there is an atom referenced by the symbol described by `sym_loc` descriptor. |
| 2051 | /// Returns null on failure. | 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 | assert(sym_loc.file == null); // TODO linking with object files | 2112 | assert(sym_loc.file == null); // TODO linking with object files |
| 2054 | return self.atom_by_index_table.get(sym_loc.sym_index); | 2113 | return self.atom_by_index_table.get(sym_loc.sym_index); |
| 2055 | } | 2114 | } |
| 2056 | 2115 | ||
| 2057 | /// Returns GOT atom that references `sym_loc` if one exists. | 2116 | /// Returns GOT atom that references `sym_loc` if one exists. |
| 2058 | /// Returns null otherwise. | 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 | const got_index = self.got_entries_table.get(sym_loc) orelse return null; | 2119 | const got_index = self.got_entries_table.get(sym_loc) orelse return null; |
| 2061 | const got_entry = self.got_entries.items[got_index]; | 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 | /// Returns import atom that references `sym_loc` if one exists. | 2124 | /// Returns import atom that references `sym_loc` if one exists. |
| 2066 | /// Returns null otherwise. | 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 | const imports_index = self.imports_table.get(sym_loc) orelse return null; | 2127 | const imports_index = self.imports_table.get(sym_loc) orelse return null; |
| 2069 | const imports_entry = self.imports.items[imports_index]; | 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 | fn setSectionName(self: *Coff, header: *coff.SectionHeader, name: []const u8) !void { | 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,42 +27,44 @@ alignment: u32, |
| 27 | 27 | ||
| 28 | /// Points to the previous and next neighbors, based on the `text_offset`. | 28 | /// Points to the previous and next neighbors, based on the `text_offset`. |
| 29 | /// This can be used to find, for example, the capacity of this `Atom`. | 29 | /// This can be used to find, for example, the capacity of this `Atom`. |
| 30 | prev: ?*Atom, | 30 | prev_index: ?Index, |
| 31 | next: ?*Atom, | 31 | next_index: ?Index, |
| 32 | 32 | ||
| 33 | pub const empty = Atom{ | 33 | pub const Index = u32; |
| 34 | .sym_index = 0, | 34 | |
| 35 | .file = null, | 35 | pub fn getSymbolIndex(self: Atom) ?u32 { |
| 36 | .size = 0, | 36 | if (self.sym_index == 0) return null; |
| 37 | .alignment = 0, | 37 | return self.sym_index; |
| 38 | .prev = null, | 38 | } |
| 39 | .next = null, | ||
| 40 | }; | ||
| 41 | 39 | ||
| 42 | /// Returns symbol referencing this atom. | 40 | /// Returns symbol referencing this atom. |
| 43 | pub fn getSymbol(self: Atom, coff_file: *const Coff) *const coff.Symbol { | 41 | pub fn getSymbol(self: Atom, coff_file: *const Coff) *const coff.Symbol { |
| 42 | const sym_index = self.getSymbolIndex().?; | ||
| 44 | return coff_file.getSymbol(.{ | 43 | return coff_file.getSymbol(.{ |
| 45 | .sym_index = self.sym_index, | 44 | .sym_index = sym_index, |
| 46 | .file = self.file, | 45 | .file = self.file, |
| 47 | }); | 46 | }); |
| 48 | } | 47 | } |
| 49 | 48 | ||
| 50 | /// Returns pointer-to-symbol referencing this atom. | 49 | /// Returns pointer-to-symbol referencing this atom. |
| 51 | pub fn getSymbolPtr(self: Atom, coff_file: *Coff) *coff.Symbol { | 50 | pub fn getSymbolPtr(self: Atom, coff_file: *Coff) *coff.Symbol { |
| 51 | const sym_index = self.getSymbolIndex().?; | ||
| 52 | return coff_file.getSymbolPtr(.{ | 52 | return coff_file.getSymbolPtr(.{ |
| 53 | .sym_index = self.sym_index, | 53 | .sym_index = sym_index, |
| 54 | .file = self.file, | 54 | .file = self.file, |
| 55 | }); | 55 | }); |
| 56 | } | 56 | } |
| 57 | 57 | ||
| 58 | pub fn getSymbolWithLoc(self: Atom) SymbolWithLoc { | 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 | /// Returns the name of this atom. | 63 | /// Returns the name of this atom. |
| 63 | pub fn getName(self: Atom, coff_file: *const Coff) []const u8 { | 64 | pub fn getName(self: Atom, coff_file: *const Coff) []const u8 { |
| 65 | const sym_index = self.getSymbolIndex().?; | ||
| 64 | return coff_file.getSymbolName(.{ | 66 | return coff_file.getSymbolName(.{ |
| 65 | .sym_index = self.sym_index, | 67 | .sym_index = sym_index, |
| 66 | .file = self.file, | 68 | .file = self.file, |
| 67 | }); | 69 | }); |
| 68 | } | 70 | } |
| ... | @@ -70,7 +72,8 @@ pub fn getName(self: Atom, coff_file: *const Coff) []const u8 { | ... | @@ -70,7 +72,8 @@ pub fn getName(self: Atom, coff_file: *const Coff) []const u8 { |
| 70 | /// Returns how much room there is to grow in virtual address space. | 72 | /// Returns how much room there is to grow in virtual address space. |
| 71 | pub fn capacity(self: Atom, coff_file: *const Coff) u32 { | 73 | pub fn capacity(self: Atom, coff_file: *const Coff) u32 { |
| 72 | const self_sym = self.getSymbol(coff_file); | 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 | const next_sym = next.getSymbol(coff_file); | 77 | const next_sym = next.getSymbol(coff_file); |
| 75 | return next_sym.value - self_sym.value; | 78 | return next_sym.value - self_sym.value; |
| 76 | } else { | 79 | } else { |
| ... | @@ -82,7 +85,8 @@ pub fn capacity(self: Atom, coff_file: *const Coff) u32 { | ... | @@ -82,7 +85,8 @@ pub fn capacity(self: Atom, coff_file: *const Coff) u32 { |
| 82 | 85 | ||
| 83 | pub fn freeListEligible(self: Atom, coff_file: *const Coff) bool { | 86 | pub fn freeListEligible(self: Atom, coff_file: *const Coff) bool { |
| 84 | // No need to keep a free list node for the last atom. | 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 | const self_sym = self.getSymbol(coff_file); | 90 | const self_sym = self.getSymbol(coff_file); |
| 87 | const next_sym = next.getSymbol(coff_file); | 91 | const next_sym = next.getSymbol(coff_file); |
| 88 | const cap = next_sym.value - self_sym.value; | 92 | const cap = next_sym.value - self_sym.value; |
| ... | @@ -92,22 +96,33 @@ pub fn freeListEligible(self: Atom, coff_file: *const Coff) bool { | ... | @@ -92,22 +96,33 @@ pub fn freeListEligible(self: Atom, coff_file: *const Coff) bool { |
| 92 | return surplus >= Coff.min_text_capacity; | 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 | const gpa = coff_file.base.allocator; | 100 | const gpa = coff_file.base.allocator; |
| 97 | log.debug(" (adding reloc of type {s} to target %{d})", .{ @tagName(reloc.type), reloc.target.sym_index }); | 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 | if (!gop.found_existing) { | 103 | if (!gop.found_existing) { |
| 100 | gop.value_ptr.* = .{}; | 104 | gop.value_ptr.* = .{}; |
| 101 | } | 105 | } |
| 102 | try gop.value_ptr.append(gpa, reloc); | 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 | const gpa = coff_file.base.allocator; | 110 | const gpa = coff_file.base.allocator; |
| 107 | log.debug(" (adding base relocation at offset 0x{x} in %{d})", .{ offset, self.sym_index }); | 111 | log.debug(" (adding base relocation at offset 0x{x} in %{d})", .{ |
| 108 | const gop = try coff_file.base_relocs.getOrPut(gpa, self); | 112 | offset, |
| 113 | coff_file.getAtom(atom_index).getSymbolIndex().?, | ||
| 114 | }); | ||
| 115 | const gop = try coff_file.base_relocs.getOrPut(gpa, atom_index); | ||
| 109 | if (!gop.found_existing) { | 116 | if (!gop.found_existing) { |
| 110 | gop.value_ptr.* = .{}; | 117 | gop.value_ptr.* = .{}; |
| 111 | } | 118 | } |
| 112 | try gop.value_ptr.append(gpa, offset); | 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,33 +46,35 @@ length: u2, |
| 46 | dirty: bool = true, | 46 | dirty: bool = true, |
| 47 | 47 | ||
| 48 | /// Returns an Atom which is the target node of this relocation edge (if any). | 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 | switch (self.type) { | 50 | switch (self.type) { |
| 51 | .got, | 51 | .got, |
| 52 | .got_page, | 52 | .got_page, |
| 53 | .got_pageoff, | 53 | .got_pageoff, |
| 54 | => return coff_file.getGotAtomForSymbol(self.target), | 54 | => return coff_file.getGotAtomIndexForSymbol(self.target), |
| 55 | 55 | ||
| 56 | .direct, | 56 | .direct, |
| 57 | .page, | 57 | .page, |
| 58 | .pageoff, | 58 | .pageoff, |
| 59 | => return coff_file.getAtomForSymbol(self.target), | 59 | => return coff_file.getAtomIndexForSymbol(self.target), |
| 60 | 60 | ||
| 61 | .import, | 61 | .import, |
| 62 | .import_page, | 62 | .import_page, |
| 63 | .import_pageoff, | 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 | const source_sym = atom.getSymbol(coff_file); | 70 | const source_sym = atom.getSymbol(coff_file); |
| 70 | const source_section = coff_file.sections.get(@enumToInt(source_sym.section_number) - 1).header; | 71 | const source_section = coff_file.sections.get(@enumToInt(source_sym.section_number) - 1).header; |
| 71 | const source_vaddr = source_sym.value + self.offset; | 72 | const source_vaddr = source_sym.value + self.offset; |
| 72 | 73 | ||
| 73 | const file_offset = source_section.pointer_to_raw_data + source_sym.value - source_section.virtual_address; | 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 | const target_vaddr = target_atom.getSymbol(coff_file).value; | 78 | const target_vaddr = target_atom.getSymbol(coff_file).value; |
| 77 | const target_vaddr_with_addend = target_vaddr + self.addend; | 79 | const target_vaddr_with_addend = target_vaddr + self.addend; |
| 78 | 80 | ||
| ... | @@ -107,7 +109,7 @@ const Context = struct { | ... | @@ -107,7 +109,7 @@ const Context = struct { |
| 107 | image_base: u64, | 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 | var buffer: [@sizeOf(u64)]u8 = undefined; | 113 | var buffer: [@sizeOf(u64)]u8 = undefined; |
| 112 | switch (self.length) { | 114 | switch (self.length) { |
| 113 | 2 => { | 115 | 2 => { |
| ... | @@ -197,7 +199,7 @@ fn resolveAarch64(self: *Relocation, ctx: Context, coff_file: *Coff) !void { | ... | @@ -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 | switch (self.type) { | 203 | switch (self.type) { |
| 202 | .got_page => unreachable, | 204 | .got_page => unreachable, |
| 203 | .got_pageoff => unreachable, | 205 | .got_pageoff => unreachable, |
src/link/Dwarf.zig+320-259| ... | @@ -18,31 +18,36 @@ const LinkBlock = File.LinkBlock; | ... | @@ -18,31 +18,36 @@ const LinkBlock = File.LinkBlock; |
| 18 | const LinkFn = File.LinkFn; | 18 | const LinkFn = File.LinkFn; |
| 19 | const LinkerLoad = @import("../codegen.zig").LinkerLoad; | 19 | const LinkerLoad = @import("../codegen.zig").LinkerLoad; |
| 20 | const Module = @import("../Module.zig"); | 20 | const Module = @import("../Module.zig"); |
| 21 | const Value = @import("../value.zig").Value; | 21 | const StringTable = @import("strtab.zig").StringTable; |
| 22 | const Type = @import("../type.zig").Type; | 22 | const Type = @import("../type.zig").Type; |
| 23 | const Value = @import("../value.zig").Value; | ||
| 23 | 24 | ||
| 24 | allocator: Allocator, | 25 | allocator: Allocator, |
| 25 | bin_file: *File, | 26 | bin_file: *File, |
| 26 | ptr_width: PtrWidth, | 27 | ptr_width: PtrWidth, |
| 27 | target: std.Target, | 28 | target: std.Target, |
| 28 | 29 | ||
| 29 | /// A list of `File.LinkFn` whose Line Number Programs have surplus capacity. | 30 | /// A list of `Atom`s whose Line Number Programs have surplus capacity. |
| 30 | /// This is the same concept as `text_block_free_list`; see those doc comments. | 31 | /// This is the same concept as `Section.free_list` in Elf; see those doc comments. |
| 31 | dbg_line_fn_free_list: std.AutoHashMapUnmanaged(*SrcFn, void) = .{}, | 32 | src_fn_free_list: std.AutoHashMapUnmanaged(Atom.Index, void) = .{}, |
| 32 | dbg_line_fn_first: ?*SrcFn = null, | 33 | src_fn_first_index: ?Atom.Index = null, |
| 33 | dbg_line_fn_last: ?*SrcFn = null, | 34 | src_fn_last_index: ?Atom.Index = null, |
| 35 | src_fns: std.ArrayListUnmanaged(Atom) = .{}, | ||
| 36 | src_fn_decls: AtomTable = .{}, | ||
| 34 | 37 | ||
| 35 | /// A list of `Atom`s whose corresponding .debug_info tags have surplus capacity. | 38 | /// A list of `Atom`s whose corresponding .debug_info tags have surplus capacity. |
| 36 | /// This is the same concept as `text_block_free_list`; see those doc comments. | 39 | /// This is the same concept as `text_block_free_list`; see those doc comments. |
| 37 | atom_free_list: std.AutoHashMapUnmanaged(*Atom, void) = .{}, | 40 | di_atom_free_list: std.AutoHashMapUnmanaged(Atom.Index, void) = .{}, |
| 38 | atom_first: ?*Atom = null, | 41 | di_atom_first_index: ?Atom.Index = null, |
| 39 | atom_last: ?*Atom = null, | 42 | di_atom_last_index: ?Atom.Index = null, |
| 43 | di_atoms: std.ArrayListUnmanaged(Atom) = .{}, | ||
| 44 | di_atom_decls: AtomTable = .{}, | ||
| 40 | 45 | ||
| 41 | abbrev_table_offset: ?u64 = null, | 46 | abbrev_table_offset: ?u64 = null, |
| 42 | 47 | ||
| 43 | /// TODO replace with InternPool | 48 | /// TODO replace with InternPool |
| 44 | /// Table of debug symbol names. | 49 | /// Table of debug symbol names. |
| 45 | strtab: std.ArrayListUnmanaged(u8) = .{}, | 50 | strtab: StringTable(.strtab) = .{}, |
| 46 | 51 | ||
| 47 | /// Quick lookup array of all defined source files referenced by at least one Decl. | 52 | /// Quick lookup array of all defined source files referenced by at least one Decl. |
| 48 | /// They will end up in the DWARF debug_line header as two lists: | 53 | /// They will end up in the DWARF debug_line header as two lists: |
| ... | @@ -50,22 +55,23 @@ strtab: std.ArrayListUnmanaged(u8) = .{}, | ... | @@ -50,22 +55,23 @@ strtab: std.ArrayListUnmanaged(u8) = .{}, |
| 50 | /// * []file_names | 55 | /// * []file_names |
| 51 | di_files: std.AutoArrayHashMapUnmanaged(*const Module.File, void) = .{}, | 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 | global_abbrev_relocs: std.ArrayListUnmanaged(AbbrevRelocation) = .{}, | 58 | global_abbrev_relocs: std.ArrayListUnmanaged(AbbrevRelocation) = .{}, |
| 59 | 59 | ||
| 60 | pub const Atom = struct { | 60 | const AtomTable = std.AutoHashMapUnmanaged(Module.Decl.Index, Atom.Index); |
| 61 | /// Previous/next linked list pointers. | 61 | |
| 62 | /// This is the linked list node for this Decl's corresponding .debug_info tag. | 62 | const Atom = struct { |
| 63 | prev: ?*Atom, | 63 | /// Offset into .debug_info pointing to the tag for this Decl, or |
| 64 | next: ?*Atom, | 64 | /// offset from the beginning of the Debug Line Program header that contains this function. |
| 65 | /// Offset into .debug_info pointing to the tag for this Decl. | ||
| 66 | off: u32, | 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 | len: u32, | 69 | len: u32, |
| 70 | |||
| 71 | prev_index: ?Index, | ||
| 72 | next_index: ?Index, | ||
| 73 | |||
| 74 | pub const Index = u32; | ||
| 69 | }; | 75 | }; |
| 70 | 76 | ||
| 71 | /// Represents state of the analysed Decl. | 77 | /// Represents state of the analysed Decl. |
| ... | @@ -75,6 +81,7 @@ pub const Atom = struct { | ... | @@ -75,6 +81,7 @@ pub const Atom = struct { |
| 75 | pub const DeclState = struct { | 81 | pub const DeclState = struct { |
| 76 | gpa: Allocator, | 82 | gpa: Allocator, |
| 77 | mod: *Module, | 83 | mod: *Module, |
| 84 | di_atom_decls: *const AtomTable, | ||
| 78 | dbg_line: std.ArrayList(u8), | 85 | dbg_line: std.ArrayList(u8), |
| 79 | dbg_info: std.ArrayList(u8), | 86 | dbg_info: std.ArrayList(u8), |
| 80 | abbrev_type_arena: std.heap.ArenaAllocator, | 87 | abbrev_type_arena: std.heap.ArenaAllocator, |
| ... | @@ -88,10 +95,11 @@ pub const DeclState = struct { | ... | @@ -88,10 +95,11 @@ pub const DeclState = struct { |
| 88 | abbrev_relocs: std.ArrayListUnmanaged(AbbrevRelocation) = .{}, | 95 | abbrev_relocs: std.ArrayListUnmanaged(AbbrevRelocation) = .{}, |
| 89 | exprloc_relocs: std.ArrayListUnmanaged(ExprlocRelocation) = .{}, | 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 | return .{ | 99 | return .{ |
| 93 | .gpa = gpa, | 100 | .gpa = gpa, |
| 94 | .mod = mod, | 101 | .mod = mod, |
| 102 | .di_atom_decls = di_atom_decls, | ||
| 95 | .dbg_line = std.ArrayList(u8).init(gpa), | 103 | .dbg_line = std.ArrayList(u8).init(gpa), |
| 96 | .dbg_info = std.ArrayList(u8).init(gpa), | 104 | .dbg_info = std.ArrayList(u8).init(gpa), |
| 97 | .abbrev_type_arena = std.heap.ArenaAllocator.init(gpa), | 105 | .abbrev_type_arena = std.heap.ArenaAllocator.init(gpa), |
| ... | @@ -119,11 +127,11 @@ pub const DeclState = struct { | ... | @@ -119,11 +127,11 @@ pub const DeclState = struct { |
| 119 | 127 | ||
| 120 | /// Adds local type relocation of the form: @offset => @this + addend | 128 | /// Adds local type relocation of the form: @offset => @this + addend |
| 121 | /// @this signifies the offset within the .debug_abbrev section of the containing atom. | 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 | log.debug("{x}: @this + {x}", .{ offset, addend }); | 131 | log.debug("{x}: @this + {x}", .{ offset, addend }); |
| 124 | try self.abbrev_relocs.append(self.gpa, .{ | 132 | try self.abbrev_relocs.append(self.gpa, .{ |
| 125 | .target = null, | 133 | .target = null, |
| 126 | .atom = atom, | 134 | .atom_index = atom_index, |
| 127 | .offset = offset, | 135 | .offset = offset, |
| 128 | .addend = addend, | 136 | .addend = addend, |
| 129 | }); | 137 | }); |
| ... | @@ -132,13 +140,13 @@ pub const DeclState = struct { | ... | @@ -132,13 +140,13 @@ pub const DeclState = struct { |
| 132 | /// Adds global type relocation of the form: @offset => @symbol + 0 | 140 | /// Adds global type relocation of the form: @offset => @symbol + 0 |
| 133 | /// @symbol signifies a type abbreviation posititioned somewhere in the .debug_abbrev section | 141 | /// @symbol signifies a type abbreviation posititioned somewhere in the .debug_abbrev section |
| 134 | /// which we use as our target of the relocation. | 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 | const resolv = self.abbrev_resolver.getContext(ty, .{ | 144 | const resolv = self.abbrev_resolver.getContext(ty, .{ |
| 137 | .mod = self.mod, | 145 | .mod = self.mod, |
| 138 | }) orelse blk: { | 146 | }) orelse blk: { |
| 139 | const sym_index = @intCast(u32, self.abbrev_table.items.len); | 147 | const sym_index = @intCast(u32, self.abbrev_table.items.len); |
| 140 | try self.abbrev_table.append(self.gpa, .{ | 148 | try self.abbrev_table.append(self.gpa, .{ |
| 141 | .atom = atom, | 149 | .atom_index = atom_index, |
| 142 | .type = ty, | 150 | .type = ty, |
| 143 | .offset = undefined, | 151 | .offset = undefined, |
| 144 | }); | 152 | }); |
| ... | @@ -153,7 +161,7 @@ pub const DeclState = struct { | ... | @@ -153,7 +161,7 @@ pub const DeclState = struct { |
| 153 | log.debug("{x}: %{d} + 0", .{ offset, resolv }); | 161 | log.debug("{x}: %{d} + 0", .{ offset, resolv }); |
| 154 | try self.abbrev_relocs.append(self.gpa, .{ | 162 | try self.abbrev_relocs.append(self.gpa, .{ |
| 155 | .target = resolv, | 163 | .target = resolv, |
| 156 | .atom = atom, | 164 | .atom_index = atom_index, |
| 157 | .offset = offset, | 165 | .offset = offset, |
| 158 | .addend = 0, | 166 | .addend = 0, |
| 159 | }); | 167 | }); |
| ... | @@ -162,7 +170,7 @@ pub const DeclState = struct { | ... | @@ -162,7 +170,7 @@ pub const DeclState = struct { |
| 162 | fn addDbgInfoType( | 170 | fn addDbgInfoType( |
| 163 | self: *DeclState, | 171 | self: *DeclState, |
| 164 | module: *Module, | 172 | module: *Module, |
| 165 | atom: *Atom, | 173 | atom_index: Atom.Index, |
| 166 | ty: Type, | 174 | ty: Type, |
| 167 | ) error{OutOfMemory}!void { | 175 | ) error{OutOfMemory}!void { |
| 168 | const arena = self.abbrev_type_arena.allocator(); | 176 | const arena = self.abbrev_type_arena.allocator(); |
| ... | @@ -227,7 +235,7 @@ pub const DeclState = struct { | ... | @@ -227,7 +235,7 @@ pub const DeclState = struct { |
| 227 | // DW.AT.type, DW.FORM.ref4 | 235 | // DW.AT.type, DW.FORM.ref4 |
| 228 | var index = dbg_info_buffer.items.len; | 236 | var index = dbg_info_buffer.items.len; |
| 229 | try dbg_info_buffer.resize(index + 4); | 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 | // DW.AT.data_member_location, DW.FORM.sdata | 239 | // DW.AT.data_member_location, DW.FORM.sdata |
| 232 | try dbg_info_buffer.ensureUnusedCapacity(6); | 240 | try dbg_info_buffer.ensureUnusedCapacity(6); |
| 233 | dbg_info_buffer.appendAssumeCapacity(0); | 241 | dbg_info_buffer.appendAssumeCapacity(0); |
| ... | @@ -239,7 +247,7 @@ pub const DeclState = struct { | ... | @@ -239,7 +247,7 @@ pub const DeclState = struct { |
| 239 | // DW.AT.type, DW.FORM.ref4 | 247 | // DW.AT.type, DW.FORM.ref4 |
| 240 | index = dbg_info_buffer.items.len; | 248 | index = dbg_info_buffer.items.len; |
| 241 | try dbg_info_buffer.resize(index + 4); | 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 | // DW.AT.data_member_location, DW.FORM.sdata | 251 | // DW.AT.data_member_location, DW.FORM.sdata |
| 244 | const offset = abi_size - payload_ty.abiSize(target); | 252 | const offset = abi_size - payload_ty.abiSize(target); |
| 245 | try leb128.writeULEB128(dbg_info_buffer.writer(), offset); | 253 | try leb128.writeULEB128(dbg_info_buffer.writer(), offset); |
| ... | @@ -270,7 +278,7 @@ pub const DeclState = struct { | ... | @@ -270,7 +278,7 @@ pub const DeclState = struct { |
| 270 | try dbg_info_buffer.resize(index + 4); | 278 | try dbg_info_buffer.resize(index + 4); |
| 271 | var buf = try arena.create(Type.SlicePtrFieldTypeBuffer); | 279 | var buf = try arena.create(Type.SlicePtrFieldTypeBuffer); |
| 272 | const ptr_ty = ty.slicePtrFieldType(buf); | 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 | // DW.AT.data_member_location, DW.FORM.sdata | 282 | // DW.AT.data_member_location, DW.FORM.sdata |
| 275 | try dbg_info_buffer.ensureUnusedCapacity(6); | 283 | try dbg_info_buffer.ensureUnusedCapacity(6); |
| 276 | dbg_info_buffer.appendAssumeCapacity(0); | 284 | dbg_info_buffer.appendAssumeCapacity(0); |
| ... | @@ -282,7 +290,7 @@ pub const DeclState = struct { | ... | @@ -282,7 +290,7 @@ pub const DeclState = struct { |
| 282 | // DW.AT.type, DW.FORM.ref4 | 290 | // DW.AT.type, DW.FORM.ref4 |
| 283 | index = dbg_info_buffer.items.len; | 291 | index = dbg_info_buffer.items.len; |
| 284 | try dbg_info_buffer.resize(index + 4); | 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 | // DW.AT.data_member_location, DW.FORM.sdata | 294 | // DW.AT.data_member_location, DW.FORM.sdata |
| 287 | try dbg_info_buffer.ensureUnusedCapacity(2); | 295 | try dbg_info_buffer.ensureUnusedCapacity(2); |
| 288 | dbg_info_buffer.appendAssumeCapacity(ptr_bytes); | 296 | dbg_info_buffer.appendAssumeCapacity(ptr_bytes); |
| ... | @@ -294,7 +302,7 @@ pub const DeclState = struct { | ... | @@ -294,7 +302,7 @@ pub const DeclState = struct { |
| 294 | // DW.AT.type, DW.FORM.ref4 | 302 | // DW.AT.type, DW.FORM.ref4 |
| 295 | const index = dbg_info_buffer.items.len; | 303 | const index = dbg_info_buffer.items.len; |
| 296 | try dbg_info_buffer.resize(index + 4); | 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 | .Array => { | 308 | .Array => { |
| ... | @@ -305,13 +313,13 @@ pub const DeclState = struct { | ... | @@ -305,13 +313,13 @@ pub const DeclState = struct { |
| 305 | // DW.AT.type, DW.FORM.ref4 | 313 | // DW.AT.type, DW.FORM.ref4 |
| 306 | var index = dbg_info_buffer.items.len; | 314 | var index = dbg_info_buffer.items.len; |
| 307 | try dbg_info_buffer.resize(index + 4); | 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 | // DW.AT.subrange_type | 317 | // DW.AT.subrange_type |
| 310 | try dbg_info_buffer.append(@enumToInt(AbbrevKind.array_dim)); | 318 | try dbg_info_buffer.append(@enumToInt(AbbrevKind.array_dim)); |
| 311 | // DW.AT.type, DW.FORM.ref4 | 319 | // DW.AT.type, DW.FORM.ref4 |
| 312 | index = dbg_info_buffer.items.len; | 320 | index = dbg_info_buffer.items.len; |
| 313 | try dbg_info_buffer.resize(index + 4); | 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 | // DW.AT.count, DW.FORM.udata | 323 | // DW.AT.count, DW.FORM.udata |
| 316 | const len = ty.arrayLenIncludingSentinel(); | 324 | const len = ty.arrayLenIncludingSentinel(); |
| 317 | try leb128.writeULEB128(dbg_info_buffer.writer(), len); | 325 | try leb128.writeULEB128(dbg_info_buffer.writer(), len); |
| ... | @@ -339,7 +347,7 @@ pub const DeclState = struct { | ... | @@ -339,7 +347,7 @@ pub const DeclState = struct { |
| 339 | // DW.AT.type, DW.FORM.ref4 | 347 | // DW.AT.type, DW.FORM.ref4 |
| 340 | var index = dbg_info_buffer.items.len; | 348 | var index = dbg_info_buffer.items.len; |
| 341 | try dbg_info_buffer.resize(index + 4); | 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 | // DW.AT.data_member_location, DW.FORM.sdata | 351 | // DW.AT.data_member_location, DW.FORM.sdata |
| 344 | const field_off = ty.structFieldOffset(field_index, target); | 352 | const field_off = ty.structFieldOffset(field_index, target); |
| 345 | try leb128.writeULEB128(dbg_info_buffer.writer(), field_off); | 353 | try leb128.writeULEB128(dbg_info_buffer.writer(), field_off); |
| ... | @@ -371,7 +379,7 @@ pub const DeclState = struct { | ... | @@ -371,7 +379,7 @@ pub const DeclState = struct { |
| 371 | // DW.AT.type, DW.FORM.ref4 | 379 | // DW.AT.type, DW.FORM.ref4 |
| 372 | var index = dbg_info_buffer.items.len; | 380 | var index = dbg_info_buffer.items.len; |
| 373 | try dbg_info_buffer.resize(index + 4); | 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 | // DW.AT.data_member_location, DW.FORM.sdata | 383 | // DW.AT.data_member_location, DW.FORM.sdata |
| 376 | const field_off = ty.structFieldOffset(field_index, target); | 384 | const field_off = ty.structFieldOffset(field_index, target); |
| 377 | try leb128.writeULEB128(dbg_info_buffer.writer(), field_off); | 385 | try leb128.writeULEB128(dbg_info_buffer.writer(), field_off); |
| ... | @@ -454,7 +462,7 @@ pub const DeclState = struct { | ... | @@ -454,7 +462,7 @@ pub const DeclState = struct { |
| 454 | // DW.AT.type, DW.FORM.ref4 | 462 | // DW.AT.type, DW.FORM.ref4 |
| 455 | const inner_union_index = dbg_info_buffer.items.len; | 463 | const inner_union_index = dbg_info_buffer.items.len; |
| 456 | try dbg_info_buffer.resize(inner_union_index + 4); | 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 | // DW.AT.data_member_location, DW.FORM.sdata | 466 | // DW.AT.data_member_location, DW.FORM.sdata |
| 459 | try leb128.writeULEB128(dbg_info_buffer.writer(), payload_offset); | 467 | try leb128.writeULEB128(dbg_info_buffer.writer(), payload_offset); |
| 460 | } | 468 | } |
| ... | @@ -481,7 +489,7 @@ pub const DeclState = struct { | ... | @@ -481,7 +489,7 @@ pub const DeclState = struct { |
| 481 | // DW.AT.type, DW.FORM.ref4 | 489 | // DW.AT.type, DW.FORM.ref4 |
| 482 | const index = dbg_info_buffer.items.len; | 490 | const index = dbg_info_buffer.items.len; |
| 483 | try dbg_info_buffer.resize(index + 4); | 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 | // DW.AT.data_member_location, DW.FORM.sdata | 493 | // DW.AT.data_member_location, DW.FORM.sdata |
| 486 | try dbg_info_buffer.append(0); | 494 | try dbg_info_buffer.append(0); |
| 487 | } | 495 | } |
| ... | @@ -498,7 +506,7 @@ pub const DeclState = struct { | ... | @@ -498,7 +506,7 @@ pub const DeclState = struct { |
| 498 | // DW.AT.type, DW.FORM.ref4 | 506 | // DW.AT.type, DW.FORM.ref4 |
| 499 | const index = dbg_info_buffer.items.len; | 507 | const index = dbg_info_buffer.items.len; |
| 500 | try dbg_info_buffer.resize(index + 4); | 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 | // DW.AT.data_member_location, DW.FORM.sdata | 510 | // DW.AT.data_member_location, DW.FORM.sdata |
| 503 | try leb128.writeULEB128(dbg_info_buffer.writer(), tag_offset); | 511 | try leb128.writeULEB128(dbg_info_buffer.writer(), tag_offset); |
| 504 | 512 | ||
| ... | @@ -541,7 +549,7 @@ pub const DeclState = struct { | ... | @@ -541,7 +549,7 @@ pub const DeclState = struct { |
| 541 | // DW.AT.type, DW.FORM.ref4 | 549 | // DW.AT.type, DW.FORM.ref4 |
| 542 | var index = dbg_info_buffer.items.len; | 550 | var index = dbg_info_buffer.items.len; |
| 543 | try dbg_info_buffer.resize(index + 4); | 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 | // DW.AT.data_member_location, DW.FORM.sdata | 553 | // DW.AT.data_member_location, DW.FORM.sdata |
| 546 | try leb128.writeULEB128(dbg_info_buffer.writer(), payload_off); | 554 | try leb128.writeULEB128(dbg_info_buffer.writer(), payload_off); |
| 547 | 555 | ||
| ... | @@ -554,7 +562,7 @@ pub const DeclState = struct { | ... | @@ -554,7 +562,7 @@ pub const DeclState = struct { |
| 554 | // DW.AT.type, DW.FORM.ref4 | 562 | // DW.AT.type, DW.FORM.ref4 |
| 555 | index = dbg_info_buffer.items.len; | 563 | index = dbg_info_buffer.items.len; |
| 556 | try dbg_info_buffer.resize(index + 4); | 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 | // DW.AT.data_member_location, DW.FORM.sdata | 566 | // DW.AT.data_member_location, DW.FORM.sdata |
| 559 | try leb128.writeULEB128(dbg_info_buffer.writer(), error_off); | 567 | try leb128.writeULEB128(dbg_info_buffer.writer(), error_off); |
| 560 | 568 | ||
| ... | @@ -587,12 +595,11 @@ pub const DeclState = struct { | ... | @@ -587,12 +595,11 @@ pub const DeclState = struct { |
| 587 | self: *DeclState, | 595 | self: *DeclState, |
| 588 | name: [:0]const u8, | 596 | name: [:0]const u8, |
| 589 | ty: Type, | 597 | ty: Type, |
| 590 | tag: File.Tag, | ||
| 591 | owner_decl: Module.Decl.Index, | 598 | owner_decl: Module.Decl.Index, |
| 592 | loc: DbgInfoLoc, | 599 | loc: DbgInfoLoc, |
| 593 | ) error{OutOfMemory}!void { | 600 | ) error{OutOfMemory}!void { |
| 594 | const dbg_info = &self.dbg_info; | 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 | const name_with_null = name.ptr[0 .. name.len + 1]; | 603 | const name_with_null = name.ptr[0 .. name.len + 1]; |
| 597 | 604 | ||
| 598 | switch (loc) { | 605 | switch (loc) { |
| ... | @@ -637,7 +644,7 @@ pub const DeclState = struct { | ... | @@ -637,7 +644,7 @@ pub const DeclState = struct { |
| 637 | try dbg_info.ensureUnusedCapacity(5 + name_with_null.len); | 644 | try dbg_info.ensureUnusedCapacity(5 + name_with_null.len); |
| 638 | const index = dbg_info.items.len; | 645 | const index = dbg_info.items.len; |
| 639 | try dbg_info.resize(index + 4); // dw.at.type, dw.form.ref4 | 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 | dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string | 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,13 +652,12 @@ pub const DeclState = struct { |
| 645 | self: *DeclState, | 652 | self: *DeclState, |
| 646 | name: [:0]const u8, | 653 | name: [:0]const u8, |
| 647 | ty: Type, | 654 | ty: Type, |
| 648 | tag: File.Tag, | ||
| 649 | owner_decl: Module.Decl.Index, | 655 | owner_decl: Module.Decl.Index, |
| 650 | is_ptr: bool, | 656 | is_ptr: bool, |
| 651 | loc: DbgInfoLoc, | 657 | loc: DbgInfoLoc, |
| 652 | ) error{OutOfMemory}!void { | 658 | ) error{OutOfMemory}!void { |
| 653 | const dbg_info = &self.dbg_info; | 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 | const name_with_null = name.ptr[0 .. name.len + 1]; | 661 | const name_with_null = name.ptr[0 .. name.len + 1]; |
| 656 | try dbg_info.append(@enumToInt(AbbrevKind.variable)); | 662 | try dbg_info.append(@enumToInt(AbbrevKind.variable)); |
| 657 | const target = self.mod.getTarget(); | 663 | const target = self.mod.getTarget(); |
| ... | @@ -781,7 +787,7 @@ pub const DeclState = struct { | ... | @@ -781,7 +787,7 @@ pub const DeclState = struct { |
| 781 | try dbg_info.ensureUnusedCapacity(5 + name_with_null.len); | 787 | try dbg_info.ensureUnusedCapacity(5 + name_with_null.len); |
| 782 | const index = dbg_info.items.len; | 788 | const index = dbg_info.items.len; |
| 783 | try dbg_info.resize(index + 4); // dw.at.type, dw.form.ref4 | 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 | dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string | 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,7 +820,7 @@ pub const DeclState = struct { |
| 814 | }; | 820 | }; |
| 815 | 821 | ||
| 816 | pub const AbbrevEntry = struct { | 822 | pub const AbbrevEntry = struct { |
| 817 | atom: *const Atom, | 823 | atom_index: Atom.Index, |
| 818 | type: Type, | 824 | type: Type, |
| 819 | offset: u32, | 825 | offset: u32, |
| 820 | }; | 826 | }; |
| ... | @@ -823,7 +829,7 @@ pub const AbbrevRelocation = struct { | ... | @@ -823,7 +829,7 @@ pub const AbbrevRelocation = struct { |
| 823 | /// If target is null, we deal with a local relocation that is based on simple offset + addend | 829 | /// If target is null, we deal with a local relocation that is based on simple offset + addend |
| 824 | /// only. | 830 | /// only. |
| 825 | target: ?u32, | 831 | target: ?u32, |
| 826 | atom: *const Atom, | 832 | atom_index: Atom.Index, |
| 827 | offset: u32, | 833 | offset: u32, |
| 828 | addend: u32, | 834 | addend: u32, |
| 829 | }; | 835 | }; |
| ... | @@ -840,26 +846,6 @@ pub const ExprlocRelocation = struct { | ... | @@ -840,26 +846,6 @@ pub const ExprlocRelocation = struct { |
| 840 | offset: u32, | 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 | pub const PtrWidth = enum { p32, p64 }; | 849 | pub const PtrWidth = enum { p32, p64 }; |
| 864 | 850 | ||
| 865 | pub const AbbrevKind = enum(u8) { | 851 | pub const AbbrevKind = enum(u8) { |
| ... | @@ -909,16 +895,18 @@ pub fn init(allocator: Allocator, bin_file: *File, target: std.Target) Dwarf { | ... | @@ -909,16 +895,18 @@ pub fn init(allocator: Allocator, bin_file: *File, target: std.Target) Dwarf { |
| 909 | 895 | ||
| 910 | pub fn deinit(self: *Dwarf) void { | 896 | pub fn deinit(self: *Dwarf) void { |
| 911 | const gpa = self.allocator; | 897 | const gpa = self.allocator; |
| 912 | self.dbg_line_fn_free_list.deinit(gpa); | 898 | |
| 913 | self.atom_free_list.deinit(gpa); | 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 | self.strtab.deinit(gpa); | 907 | self.strtab.deinit(gpa); |
| 915 | self.di_files.deinit(gpa); | 908 | self.di_files.deinit(gpa); |
| 916 | self.global_abbrev_relocs.deinit(gpa); | 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 | /// Initializes Decl's state and its matching output buffers. | 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,15 +922,19 @@ pub fn initDeclState(self: *Dwarf, mod: *Module, decl_index: Module.Decl.Index) |
| 934 | log.debug("initDeclState {s}{*}", .{ decl_name, decl }); | 922 | log.debug("initDeclState {s}{*}", .{ decl_name, decl }); |
| 935 | 923 | ||
| 936 | const gpa = self.allocator; | 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 | errdefer decl_state.deinit(); | 926 | errdefer decl_state.deinit(); |
| 939 | const dbg_line_buffer = &decl_state.dbg_line; | 927 | const dbg_line_buffer = &decl_state.dbg_line; |
| 940 | const dbg_info_buffer = &decl_state.dbg_info; | 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 | assert(decl.has_tv); | 932 | assert(decl.has_tv); |
| 943 | 933 | ||
| 944 | switch (decl.ty.zigTypeTag()) { | 934 | switch (decl.ty.zigTypeTag()) { |
| 945 | .Fn => { | 935 | .Fn => { |
| 936 | _ = try self.getOrCreateAtomForDecl(.src_fn, decl_index); | ||
| 937 | |||
| 946 | // For functions we need to add a prologue to the debug line program. | 938 | // For functions we need to add a prologue to the debug line program. |
| 947 | try dbg_line_buffer.ensureTotalCapacity(26); | 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,8 +994,7 @@ pub fn initDeclState(self: *Dwarf, mod: *Module, decl_index: Module.Decl.Index) |
| 1002 | dbg_info_buffer.items.len += 4; // DW.AT.high_pc, DW.FORM.data4 | 994 | dbg_info_buffer.items.len += 4; // DW.AT.high_pc, DW.FORM.data4 |
| 1003 | // | 995 | // |
| 1004 | if (fn_ret_has_bits) { | 996 | if (fn_ret_has_bits) { |
| 1005 | const atom = getDbgInfoAtom(self.bin_file.tag, mod, decl_index); | 997 | try decl_state.addTypeRelocGlobal(di_atom_index, fn_ret_type, @intCast(u32, dbg_info_buffer.items.len)); |
| 1006 | try decl_state.addTypeRelocGlobal(atom, fn_ret_type, @intCast(u32, dbg_info_buffer.items.len)); | ||
| 1007 | dbg_info_buffer.items.len += 4; // DW.AT.type, DW.FORM.ref4 | 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,31 +1066,28 @@ pub fn commitDeclState( |
| 1075 | // This logic is nearly identical to the logic below in `updateDeclDebugInfo` for | 1066 | // This logic is nearly identical to the logic below in `updateDeclDebugInfo` for |
| 1076 | // `TextBlock` and the .debug_info. If you are editing this logic, you | 1067 | // `TextBlock` and the .debug_info. If you are editing this logic, you |
| 1077 | // probably need to edit that logic too. | 1068 | // probably need to edit that logic too. |
| 1078 | const src_fn = switch (self.bin_file.tag) { | 1069 | const src_fn_index = self.src_fn_decls.get(decl_index).?; |
| 1079 | .elf => &decl.fn_link.elf, | 1070 | const src_fn = self.getAtomPtr(.src_fn, src_fn_index); |
| 1080 | .macho => &decl.fn_link.macho, | ||
| 1081 | .wasm => &decl.fn_link.wasm.src_fn, | ||
| 1082 | else => unreachable, // TODO | ||
| 1083 | }; | ||
| 1084 | src_fn.len = @intCast(u32, dbg_line_buffer.items.len); | 1071 | src_fn.len = @intCast(u32, dbg_line_buffer.items.len); |
| 1085 | 1072 | ||
| 1086 | if (self.dbg_line_fn_last) |last| blk: { | 1073 | if (self.src_fn_last_index) |last_index| blk: { |
| 1087 | if (src_fn == last) break :blk; | 1074 | if (src_fn_index == last_index) break :blk; |
| 1088 | if (src_fn.next) |next| { | 1075 | if (src_fn.next_index) |next_index| { |
| 1076 | const next = self.getAtomPtr(.src_fn, next_index); | ||
| 1089 | // Update existing function - non-last item. | 1077 | // Update existing function - non-last item. |
| 1090 | if (src_fn.off + src_fn.len + min_nop_size > next.off) { | 1078 | if (src_fn.off + src_fn.len + min_nop_size > next.off) { |
| 1091 | // It grew too big, so we move it to a new location. | 1079 | // It grew too big, so we move it to a new location. |
| 1092 | if (src_fn.prev) |prev| { | 1080 | if (src_fn.prev_index) |prev_index| { |
| 1093 | self.dbg_line_fn_free_list.put(gpa, prev, {}) catch {}; | 1081 | self.src_fn_free_list.put(gpa, prev_index, {}) catch {}; |
| 1094 | prev.next = src_fn.next; | 1082 | self.getAtomPtr(.src_fn, prev_index).next_index = src_fn.next_index; |
| 1095 | } | 1083 | } |
| 1096 | next.prev = src_fn.prev; | 1084 | next.prev_index = src_fn.prev_index; |
| 1097 | src_fn.next = null; | 1085 | src_fn.next_index = null; |
| 1098 | // Populate where it used to be with NOPs. | 1086 | // Populate where it used to be with NOPs. |
| 1099 | switch (self.bin_file.tag) { | 1087 | switch (self.bin_file.tag) { |
| 1100 | .elf => { | 1088 | .elf => { |
| 1101 | const elf_file = self.bin_file.cast(File.Elf).?; | 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 | const file_pos = debug_line_sect.sh_offset + src_fn.off; | 1091 | const file_pos = debug_line_sect.sh_offset + src_fn.off; |
| 1104 | try pwriteDbgLineNops(elf_file.base.file.?, file_pos, 0, &[0]u8{}, src_fn.len); | 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,39 +1099,48 @@ pub fn commitDeclState( |
| 1111 | }, | 1099 | }, |
| 1112 | .wasm => { | 1100 | .wasm => { |
| 1113 | const wasm_file = self.bin_file.cast(File.Wasm).?; | 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 | writeDbgLineNopsBuffered(debug_line.items, src_fn.off, 0, &.{}, src_fn.len); | 1103 | writeDbgLineNopsBuffered(debug_line.items, src_fn.off, 0, &.{}, src_fn.len); |
| 1116 | }, | 1104 | }, |
| 1117 | else => unreachable, | 1105 | else => unreachable, |
| 1118 | } | 1106 | } |
| 1119 | // TODO Look at the free list before appending at the end. | 1107 | // TODO Look at the free list before appending at the end. |
| 1120 | src_fn.prev = last; | 1108 | src_fn.prev_index = last_index; |
| 1121 | last.next = src_fn; | 1109 | const last = self.getAtomPtr(.src_fn, last_index); |
| 1122 | self.dbg_line_fn_last = src_fn; | 1110 | last.next_index = src_fn_index; |
| 1111 | self.src_fn_last_index = src_fn_index; | ||
| 1123 | 1112 | ||
| 1124 | src_fn.off = last.off + padToIdeal(last.len); | 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 | // Append new function. | 1116 | // Append new function. |
| 1128 | // TODO Look at the free list before appending at the end. | 1117 | // TODO Look at the free list before appending at the end. |
| 1129 | src_fn.prev = last; | 1118 | src_fn.prev_index = last_index; |
| 1130 | last.next = src_fn; | 1119 | const last = self.getAtomPtr(.src_fn, last_index); |
| 1131 | self.dbg_line_fn_last = src_fn; | 1120 | last.next_index = src_fn_index; |
| 1121 | self.src_fn_last_index = src_fn_index; | ||
| 1132 | 1122 | ||
| 1133 | src_fn.off = last.off + padToIdeal(last.len); | 1123 | src_fn.off = last.off + padToIdeal(last.len); |
| 1134 | } | 1124 | } |
| 1135 | } else { | 1125 | } else { |
| 1136 | // This is the first function of the Line Number Program. | 1126 | // This is the first function of the Line Number Program. |
| 1137 | self.dbg_line_fn_first = src_fn; | 1127 | self.src_fn_first_index = src_fn_index; |
| 1138 | self.dbg_line_fn_last = src_fn; | 1128 | self.src_fn_last_index = src_fn_index; |
| 1139 | 1129 | ||
| 1140 | src_fn.off = padToIdeal(self.dbgLineNeededHeaderBytes(&[0][]u8{}, &[0][]u8{})); | 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 | const needed_size = last_src_fn.off + last_src_fn.len; | 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; | 1136 | const prev_padding_size: u32 = if (src_fn.prev_index) |prev_index| blk: { |
| 1146 | const next_padding_size: u32 = if (src_fn.next) |next| next.off - (src_fn.off + src_fn.len) else 0; | 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 | // We only have support for one compilation unit so far, so the offsets are directly | 1145 | // We only have support for one compilation unit so far, so the offsets are directly |
| 1149 | // from the .debug_line section. | 1146 | // from the .debug_line section. |
| ... | @@ -1152,7 +1149,7 @@ pub fn commitDeclState( | ... | @@ -1152,7 +1149,7 @@ pub fn commitDeclState( |
| 1152 | const elf_file = self.bin_file.cast(File.Elf).?; | 1149 | const elf_file = self.bin_file.cast(File.Elf).?; |
| 1153 | const shdr_index = elf_file.debug_line_section_index.?; | 1150 | const shdr_index = elf_file.debug_line_section_index.?; |
| 1154 | try elf_file.growNonAllocSection(shdr_index, needed_size, 1, true); | 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 | const file_pos = debug_line_sect.sh_offset + src_fn.off; | 1153 | const file_pos = debug_line_sect.sh_offset + src_fn.off; |
| 1157 | try pwriteDbgLineNops( | 1154 | try pwriteDbgLineNops( |
| 1158 | elf_file.base.file.?, | 1155 | elf_file.base.file.?, |
| ... | @@ -1180,7 +1177,7 @@ pub fn commitDeclState( | ... | @@ -1180,7 +1177,7 @@ pub fn commitDeclState( |
| 1180 | 1177 | ||
| 1181 | .wasm => { | 1178 | .wasm => { |
| 1182 | const wasm_file = self.bin_file.cast(File.Wasm).?; | 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 | const debug_line = &atom.code; | 1181 | const debug_line = &atom.code; |
| 1185 | const segment_size = debug_line.items.len; | 1182 | const segment_size = debug_line.items.len; |
| 1186 | if (needed_size != segment_size) { | 1183 | if (needed_size != segment_size) { |
| ... | @@ -1212,7 +1209,7 @@ pub fn commitDeclState( | ... | @@ -1212,7 +1209,7 @@ pub fn commitDeclState( |
| 1212 | if (dbg_info_buffer.items.len == 0) | 1209 | if (dbg_info_buffer.items.len == 0) |
| 1213 | return; | 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 | if (decl_state.abbrev_table.items.len > 0) { | 1213 | if (decl_state.abbrev_table.items.len > 0) { |
| 1217 | // Now we emit the .debug_info types of the Decl. These will count towards the size of | 1214 | // Now we emit the .debug_info types of the Decl. These will count towards the size of |
| 1218 | // the buffer, so we have to do it before computing the offset, and we can't perform the actual | 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,12 +1231,12 @@ pub fn commitDeclState( |
| 1234 | if (deferred) continue; | 1231 | if (deferred) continue; |
| 1235 | 1232 | ||
| 1236 | symbol.offset = @intCast(u32, dbg_info_buffer.items.len); | 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 | log.debug("updateDeclDebugInfoAllocation for '{s}'", .{decl.name}); | 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 | while (decl_state.abbrev_relocs.popOrNull()) |reloc| { | 1241 | while (decl_state.abbrev_relocs.popOrNull()) |reloc| { |
| 1245 | if (reloc.target) |target| { | 1242 | if (reloc.target) |target| { |
| ... | @@ -1260,11 +1257,12 @@ pub fn commitDeclState( | ... | @@ -1260,11 +1257,12 @@ pub fn commitDeclState( |
| 1260 | try self.global_abbrev_relocs.append(gpa, .{ | 1257 | try self.global_abbrev_relocs.append(gpa, .{ |
| 1261 | .target = null, | 1258 | .target = null, |
| 1262 | .offset = reloc.offset, | 1259 | .offset = reloc.offset, |
| 1263 | .atom = reloc.atom, | 1260 | .atom_index = reloc.atom_index, |
| 1264 | .addend = reloc.addend, | 1261 | .addend = reloc.addend, |
| 1265 | }); | 1262 | }); |
| 1266 | } else { | 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 | log.debug("{x}: [() => {x}] (%{d}, '{}')", .{ reloc.offset, value, target, ty.fmtDebug() }); | 1266 | log.debug("{x}: [() => {x}] (%{d}, '{}')", .{ reloc.offset, value, target, ty.fmtDebug() }); |
| 1269 | mem.writeInt( | 1267 | mem.writeInt( |
| 1270 | u32, | 1268 | u32, |
| ... | @@ -1274,10 +1272,11 @@ pub fn commitDeclState( | ... | @@ -1274,10 +1272,11 @@ pub fn commitDeclState( |
| 1274 | ); | 1272 | ); |
| 1275 | } | 1273 | } |
| 1276 | } else { | 1274 | } else { |
| 1275 | const atom = self.getAtom(.di_atom, reloc.atom_index); | ||
| 1277 | mem.writeInt( | 1276 | mem.writeInt( |
| 1278 | u32, | 1277 | u32, |
| 1279 | dbg_info_buffer.items[reloc.offset..][0..@sizeOf(u32)], | 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 | target_endian, | 1280 | target_endian, |
| 1282 | ); | 1281 | ); |
| 1283 | } | 1282 | } |
| ... | @@ -1293,7 +1292,7 @@ pub fn commitDeclState( | ... | @@ -1293,7 +1292,7 @@ pub fn commitDeclState( |
| 1293 | .got_load => .got_load, | 1292 | .got_load => .got_load, |
| 1294 | }, | 1293 | }, |
| 1295 | .target = reloc.target, | 1294 | .target = reloc.target, |
| 1296 | .offset = reloc.offset + atom.off, | 1295 | .offset = reloc.offset + self.getAtom(.di_atom, di_atom_index).off, |
| 1297 | .addend = 0, | 1296 | .addend = 0, |
| 1298 | .prev_vaddr = 0, | 1297 | .prev_vaddr = 0, |
| 1299 | }); | 1298 | }); |
| ... | @@ -1303,10 +1302,10 @@ pub fn commitDeclState( | ... | @@ -1303,10 +1302,10 @@ pub fn commitDeclState( |
| 1303 | } | 1302 | } |
| 1304 | 1303 | ||
| 1305 | log.debug("writeDeclDebugInfo for '{s}", .{decl.name}); | 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 | const tracy = trace(@src()); | 1309 | const tracy = trace(@src()); |
| 1311 | defer tracy.end(); | 1310 | defer tracy.end(); |
| 1312 | 1311 | ||
| ... | @@ -1315,24 +1314,26 @@ fn updateDeclDebugInfoAllocation(self: *Dwarf, atom: *Atom, len: u32) !void { | ... | @@ -1315,24 +1314,26 @@ fn updateDeclDebugInfoAllocation(self: *Dwarf, atom: *Atom, len: u32) !void { |
| 1315 | // probably need to edit that logic too. | 1314 | // probably need to edit that logic too. |
| 1316 | const gpa = self.allocator; | 1315 | const gpa = self.allocator; |
| 1317 | 1316 | ||
| 1317 | const atom = self.getAtomPtr(.di_atom, atom_index); | ||
| 1318 | atom.len = len; | 1318 | atom.len = len; |
| 1319 | if (self.atom_last) |last| blk: { | 1319 | if (self.di_atom_last_index) |last_index| blk: { |
| 1320 | if (atom == last) break :blk; | 1320 | if (atom_index == last_index) break :blk; |
| 1321 | if (atom.next) |next| { | 1321 | if (atom.next_index) |next_index| { |
| 1322 | const next = self.getAtomPtr(.di_atom, next_index); | ||
| 1322 | // Update existing Decl - non-last item. | 1323 | // Update existing Decl - non-last item. |
| 1323 | if (atom.off + atom.len + min_nop_size > next.off) { | 1324 | if (atom.off + atom.len + min_nop_size > next.off) { |
| 1324 | // It grew too big, so we move it to a new location. | 1325 | // It grew too big, so we move it to a new location. |
| 1325 | if (atom.prev) |prev| { | 1326 | if (atom.prev_index) |prev_index| { |
| 1326 | self.atom_free_list.put(gpa, prev, {}) catch {}; | 1327 | self.di_atom_free_list.put(gpa, prev_index, {}) catch {}; |
| 1327 | prev.next = atom.next; | 1328 | self.getAtomPtr(.di_atom, prev_index).next_index = atom.next_index; |
| 1328 | } | 1329 | } |
| 1329 | next.prev = atom.prev; | 1330 | next.prev_index = atom.prev_index; |
| 1330 | atom.next = null; | 1331 | atom.next_index = null; |
| 1331 | // Populate where it used to be with NOPs. | 1332 | // Populate where it used to be with NOPs. |
| 1332 | switch (self.bin_file.tag) { | 1333 | switch (self.bin_file.tag) { |
| 1333 | .elf => { | 1334 | .elf => { |
| 1334 | const elf_file = self.bin_file.cast(File.Elf).?; | 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 | const file_pos = debug_info_sect.sh_offset + atom.off; | 1337 | const file_pos = debug_info_sect.sh_offset + atom.off; |
| 1337 | try pwriteDbgInfoNops(elf_file.base.file.?, file_pos, 0, &[0]u8{}, atom.len, false); | 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,37 +1345,40 @@ fn updateDeclDebugInfoAllocation(self: *Dwarf, atom: *Atom, len: u32) !void { |
| 1344 | }, | 1345 | }, |
| 1345 | .wasm => { | 1346 | .wasm => { |
| 1346 | const wasm_file = self.bin_file.cast(File.Wasm).?; | 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 | try writeDbgInfoNopsToArrayList(gpa, debug_info, atom.off, 0, &.{0}, atom.len, false); | 1350 | try writeDbgInfoNopsToArrayList(gpa, debug_info, atom.off, 0, &.{0}, atom.len, false); |
| 1349 | }, | 1351 | }, |
| 1350 | else => unreachable, | 1352 | else => unreachable, |
| 1351 | } | 1353 | } |
| 1352 | // TODO Look at the free list before appending at the end. | 1354 | // TODO Look at the free list before appending at the end. |
| 1353 | atom.prev = last; | 1355 | atom.prev_index = last_index; |
| 1354 | last.next = atom; | 1356 | const last = self.getAtomPtr(.di_atom, last_index); |
| 1355 | self.atom_last = atom; | 1357 | last.next_index = atom_index; |
| 1358 | self.di_atom_last_index = atom_index; | ||
| 1356 | 1359 | ||
| 1357 | atom.off = last.off + padToIdeal(last.len); | 1360 | atom.off = last.off + padToIdeal(last.len); |
| 1358 | } | 1361 | } |
| 1359 | } else if (atom.prev == null) { | 1362 | } else if (atom.prev_index == null) { |
| 1360 | // Append new Decl. | 1363 | // Append new Decl. |
| 1361 | // TODO Look at the free list before appending at the end. | 1364 | // TODO Look at the free list before appending at the end. |
| 1362 | atom.prev = last; | 1365 | atom.prev_index = last_index; |
| 1363 | last.next = atom; | 1366 | const last = self.getAtomPtr(.di_atom, last_index); |
| 1364 | self.atom_last = atom; | 1367 | last.next_index = atom_index; |
| 1368 | self.di_atom_last_index = atom_index; | ||
| 1365 | 1369 | ||
| 1366 | atom.off = last.off + padToIdeal(last.len); | 1370 | atom.off = last.off + padToIdeal(last.len); |
| 1367 | } | 1371 | } |
| 1368 | } else { | 1372 | } else { |
| 1369 | // This is the first Decl of the .debug_info | 1373 | // This is the first Decl of the .debug_info |
| 1370 | self.atom_first = atom; | 1374 | self.di_atom_first_index = atom_index; |
| 1371 | self.atom_last = atom; | 1375 | self.di_atom_last_index = atom_index; |
| 1372 | 1376 | ||
| 1373 | atom.off = @intCast(u32, padToIdeal(self.dbgInfoHeaderBytes())); | 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 | const tracy = trace(@src()); | 1382 | const tracy = trace(@src()); |
| 1379 | defer tracy.end(); | 1383 | defer tracy.end(); |
| 1380 | 1384 | ||
| ... | @@ -1383,14 +1387,22 @@ fn writeDeclDebugInfo(self: *Dwarf, atom: *Atom, dbg_info_buf: []const u8) !void | ... | @@ -1383,14 +1387,22 @@ fn writeDeclDebugInfo(self: *Dwarf, atom: *Atom, dbg_info_buf: []const u8) !void |
| 1383 | // probably need to edit that logic too. | 1387 | // probably need to edit that logic too. |
| 1384 | const gpa = self.allocator; | 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 | // +1 for a trailing zero to end the children of the decl tag. | 1393 | // +1 for a trailing zero to end the children of the decl tag. |
| 1388 | const needed_size = last_decl.off + last_decl.len + 1; | 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; | 1395 | const prev_padding_size: u32 = if (atom.prev_index) |prev_index| blk: { |
| 1390 | const next_padding_size: u32 = if (atom.next) |next| next.off - (atom.off + atom.len) else 0; | 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 | // To end the children of the decl tag. | 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 | // We only have support for one compilation unit so far, so the offsets are directly | 1407 | // We only have support for one compilation unit so far, so the offsets are directly |
| 1396 | // from the .debug_info section. | 1408 | // from the .debug_info section. |
| ... | @@ -1399,7 +1411,7 @@ fn writeDeclDebugInfo(self: *Dwarf, atom: *Atom, dbg_info_buf: []const u8) !void | ... | @@ -1399,7 +1411,7 @@ fn writeDeclDebugInfo(self: *Dwarf, atom: *Atom, dbg_info_buf: []const u8) !void |
| 1399 | const elf_file = self.bin_file.cast(File.Elf).?; | 1411 | const elf_file = self.bin_file.cast(File.Elf).?; |
| 1400 | const shdr_index = elf_file.debug_info_section_index.?; | 1412 | const shdr_index = elf_file.debug_info_section_index.?; |
| 1401 | try elf_file.growNonAllocSection(shdr_index, needed_size, 1, true); | 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 | const file_pos = debug_info_sect.sh_offset + atom.off; | 1415 | const file_pos = debug_info_sect.sh_offset + atom.off; |
| 1404 | try pwriteDbgInfoNops( | 1416 | try pwriteDbgInfoNops( |
| 1405 | elf_file.base.file.?, | 1417 | elf_file.base.file.?, |
| ... | @@ -1430,7 +1442,7 @@ fn writeDeclDebugInfo(self: *Dwarf, atom: *Atom, dbg_info_buf: []const u8) !void | ... | @@ -1430,7 +1442,7 @@ fn writeDeclDebugInfo(self: *Dwarf, atom: *Atom, dbg_info_buf: []const u8) !void |
| 1430 | .wasm => { | 1442 | .wasm => { |
| 1431 | const wasm_file = self.bin_file.cast(File.Wasm).?; | 1443 | const wasm_file = self.bin_file.cast(File.Wasm).?; |
| 1432 | const info_atom = wasm_file.debug_info_atom.?; | 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 | const segment_size = debug_info.items.len; | 1446 | const segment_size = debug_info.items.len; |
| 1435 | if (needed_size != segment_size) { | 1447 | if (needed_size != segment_size) { |
| 1436 | log.debug(" needed size does not equal allocated size: {d}", .{needed_size}); | 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,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 | const tracy = trace(@src()); | 1474 | const tracy = trace(@src()); |
| 1463 | defer tracy.end(); | 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 | const func = decl.val.castTag(.function).?.data; | 1482 | const func = decl.val.castTag(.function).?.data; |
| 1466 | log.debug("decl.src_line={d}, func.lbrace_line={d}, func.rbrace_line={d}", .{ | 1483 | log.debug("decl.src_line={d}, func.lbrace_line={d}, func.rbrace_line={d}", .{ |
| 1467 | decl.src_line, | 1484 | decl.src_line, |
| ... | @@ -1475,79 +1492,81 @@ pub fn updateDeclLineNumber(self: *Dwarf, decl: *const Module.Decl) !void { | ... | @@ -1475,79 +1492,81 @@ pub fn updateDeclLineNumber(self: *Dwarf, decl: *const Module.Decl) !void { |
| 1475 | switch (self.bin_file.tag) { | 1492 | switch (self.bin_file.tag) { |
| 1476 | .elf => { | 1493 | .elf => { |
| 1477 | const elf_file = self.bin_file.cast(File.Elf).?; | 1494 | const elf_file = self.bin_file.cast(File.Elf).?; |
| 1478 | const shdr = elf_file.sections.items[elf_file.debug_line_section_index.?]; | 1495 | const shdr = elf_file.sections.items(.shdr)[elf_file.debug_line_section_index.?]; |
| 1479 | const file_pos = shdr.sh_offset + decl.fn_link.elf.off + self.getRelocDbgLineOff(); | 1496 | const file_pos = shdr.sh_offset + atom.off + self.getRelocDbgLineOff(); |
| 1480 | try elf_file.base.file.?.pwriteAll(&data, file_pos); | 1497 | try elf_file.base.file.?.pwriteAll(&data, file_pos); |
| 1481 | }, | 1498 | }, |
| 1482 | .macho => { | 1499 | .macho => { |
| 1483 | const d_sym = self.bin_file.cast(File.MachO).?.getDebugSymbols().?; | 1500 | const d_sym = self.bin_file.cast(File.MachO).?.getDebugSymbols().?; |
| 1484 | const sect = d_sym.getSection(d_sym.debug_line_section_index.?); | 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 | try d_sym.file.pwriteAll(&data, file_pos); | 1503 | try d_sym.file.pwriteAll(&data, file_pos); |
| 1487 | }, | 1504 | }, |
| 1488 | .wasm => { | 1505 | .wasm => { |
| 1489 | const wasm_file = self.bin_file.cast(File.Wasm).?; | 1506 | const wasm_file = self.bin_file.cast(File.Wasm).?; |
| 1490 | const offset = decl.fn_link.wasm.src_fn.off + self.getRelocDbgLineOff(); | 1507 | const offset = atom.off + self.getRelocDbgLineOff(); |
| 1491 | const atom = wasm_file.debug_line_atom.?; | 1508 | const line_atom_index = wasm_file.debug_line_atom.?; |
| 1492 | mem.copy(u8, atom.code.items[offset..], &data); | 1509 | mem.copy(u8, wasm_file.getAtomPtr(line_atom_index).code.items[offset..], &data); |
| 1493 | }, | 1510 | }, |
| 1494 | else => unreachable, | 1511 | else => unreachable, |
| 1495 | } | 1512 | } |
| 1496 | } | 1513 | } |
| 1497 | 1514 | ||
| 1498 | pub fn freeAtom(self: *Dwarf, atom: *Atom) void { | 1515 | pub fn freeDecl(self: *Dwarf, decl_index: Module.Decl.Index) void { |
| 1499 | if (self.atom_first == atom) { | 1516 | const gpa = self.allocator; |
| 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; | ||
| 1509 | 1517 | ||
| 1510 | // TODO the free list logic like we do for text blocks above | 1518 | // Free SrcFn atom |
| 1511 | } else { | 1519 | if (self.src_fn_decls.fetchRemove(decl_index)) |kv| { |
| 1512 | atom.prev = null; | 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| { | 1545 | // Free DI atom |
| 1516 | next.prev = atom.prev; | 1546 | if (self.di_atom_decls.fetchRemove(decl_index)) |kv| { |
| 1517 | } else { | 1547 | const di_atom_index = kv.value; |
| 1518 | atom.next = null; | 1548 | const di_atom = self.getAtomPtr(.di_atom, di_atom_index); |
| 1519 | } | ||
| 1520 | } | ||
| 1521 | 1549 | ||
| 1522 | pub fn freeDecl(self: *Dwarf, decl: *Module.Decl) void { | 1550 | if (self.di_atom_first_index == di_atom_index) { |
| 1523 | // TODO make this logic match freeTextBlock. Maybe abstract the logic out since the same thing | 1551 | self.di_atom_first_index = di_atom.next_index; |
| 1524 | // is desired for both. | 1552 | } |
| 1525 | const gpa = self.allocator; | 1553 | if (self.di_atom_last_index == di_atom_index) { |
| 1526 | const fn_link = switch (self.bin_file.tag) { | 1554 | // TODO shrink the .debug_info section size here |
| 1527 | .elf => &decl.fn_link.elf, | 1555 | self.di_atom_last_index = di_atom.prev_index; |
| 1528 | .macho => &decl.fn_link.macho, | 1556 | } |
| 1529 | .wasm => &decl.fn_link.wasm.src_fn, | ||
| 1530 | else => unreachable, | ||
| 1531 | }; | ||
| 1532 | _ = self.dbg_line_fn_free_list.remove(fn_link); | ||
| 1533 | 1557 | ||
| 1534 | if (fn_link.prev) |prev| { | 1558 | if (di_atom.prev_index) |prev_index| { |
| 1535 | self.dbg_line_fn_free_list.put(gpa, prev, {}) catch {}; | 1559 | self.getAtomPtr(.di_atom, prev_index).next_index = di_atom.next_index; |
| 1536 | prev.next = fn_link.next; | 1560 | // TODO the free list logic like we do for SrcFn above |
| 1537 | if (fn_link.next) |next| { | ||
| 1538 | next.prev = prev; | ||
| 1539 | } else { | 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,7 +1709,7 @@ pub fn writeDbgAbbrev(self: *Dwarf) !void { |
| 1690 | const elf_file = self.bin_file.cast(File.Elf).?; | 1709 | const elf_file = self.bin_file.cast(File.Elf).?; |
| 1691 | const shdr_index = elf_file.debug_abbrev_section_index.?; | 1710 | const shdr_index = elf_file.debug_abbrev_section_index.?; |
| 1692 | try elf_file.growNonAllocSection(shdr_index, needed_size, 1, false); | 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 | const file_pos = debug_abbrev_sect.sh_offset + abbrev_offset; | 1713 | const file_pos = debug_abbrev_sect.sh_offset + abbrev_offset; |
| 1695 | try elf_file.base.file.?.pwriteAll(&abbrev_buf, file_pos); | 1714 | try elf_file.base.file.?.pwriteAll(&abbrev_buf, file_pos); |
| 1696 | }, | 1715 | }, |
| ... | @@ -1704,7 +1723,7 @@ pub fn writeDbgAbbrev(self: *Dwarf) !void { | ... | @@ -1704,7 +1723,7 @@ pub fn writeDbgAbbrev(self: *Dwarf) !void { |
| 1704 | }, | 1723 | }, |
| 1705 | .wasm => { | 1724 | .wasm => { |
| 1706 | const wasm_file = self.bin_file.cast(File.Wasm).?; | 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 | try debug_abbrev.resize(wasm_file.base.allocator, needed_size); | 1727 | try debug_abbrev.resize(wasm_file.base.allocator, needed_size); |
| 1709 | mem.copy(u8, debug_abbrev.items, &abbrev_buf); | 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,11 +1789,11 @@ pub fn writeDbgInfoHeader(self: *Dwarf, module: *Module, low_pc: u64, high_pc: u |
| 1770 | }, | 1789 | }, |
| 1771 | } | 1790 | } |
| 1772 | // Write the form for the compile unit, which must match the abbrev table above. | 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 | var compile_unit_dir_buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined; | 1793 | var compile_unit_dir_buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined; |
| 1775 | const compile_unit_dir = resolveCompilationDir(module, &compile_unit_dir_buffer); | 1794 | const compile_unit_dir = resolveCompilationDir(module, &compile_unit_dir_buffer); |
| 1776 | const comp_dir_strp = try self.makeString(compile_unit_dir); | 1795 | const comp_dir_strp = try self.strtab.insert(self.allocator, compile_unit_dir); |
| 1777 | const producer_strp = try self.makeString(link.producer_string); | 1796 | const producer_strp = try self.strtab.insert(self.allocator, link.producer_string); |
| 1778 | 1797 | ||
| 1779 | di_buf.appendAssumeCapacity(@enumToInt(AbbrevKind.compile_unit)); | 1798 | di_buf.appendAssumeCapacity(@enumToInt(AbbrevKind.compile_unit)); |
| 1780 | if (self.bin_file.tag == .macho) { | 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,7 +1824,7 @@ pub fn writeDbgInfoHeader(self: *Dwarf, module: *Module, low_pc: u64, high_pc: u |
| 1805 | switch (self.bin_file.tag) { | 1824 | switch (self.bin_file.tag) { |
| 1806 | .elf => { | 1825 | .elf => { |
| 1807 | const elf_file = self.bin_file.cast(File.Elf).?; | 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 | const file_pos = debug_info_sect.sh_offset; | 1828 | const file_pos = debug_info_sect.sh_offset; |
| 1810 | try pwriteDbgInfoNops(elf_file.base.file.?, file_pos, 0, di_buf.items, jmp_amt, false); | 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,7 +1836,7 @@ pub fn writeDbgInfoHeader(self: *Dwarf, module: *Module, low_pc: u64, high_pc: u |
| 1817 | }, | 1836 | }, |
| 1818 | .wasm => { | 1837 | .wasm => { |
| 1819 | const wasm_file = self.bin_file.cast(File.Wasm).?; | 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 | try writeDbgInfoNopsToArrayList(self.allocator, debug_info, 0, 0, di_buf.items, jmp_amt, false); | 1840 | try writeDbgInfoNopsToArrayList(self.allocator, debug_info, 0, 0, di_buf.items, jmp_amt, false); |
| 1822 | }, | 1841 | }, |
| 1823 | else => unreachable, | 1842 | else => unreachable, |
| ... | @@ -2124,7 +2143,7 @@ pub fn writeDbgAranges(self: *Dwarf, addr: u64, size: u64) !void { | ... | @@ -2124,7 +2143,7 @@ pub fn writeDbgAranges(self: *Dwarf, addr: u64, size: u64) !void { |
| 2124 | const elf_file = self.bin_file.cast(File.Elf).?; | 2143 | const elf_file = self.bin_file.cast(File.Elf).?; |
| 2125 | const shdr_index = elf_file.debug_aranges_section_index.?; | 2144 | const shdr_index = elf_file.debug_aranges_section_index.?; |
| 2126 | try elf_file.growNonAllocSection(shdr_index, needed_size, 16, false); | 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 | const file_pos = debug_aranges_sect.sh_offset; | 2147 | const file_pos = debug_aranges_sect.sh_offset; |
| 2129 | try elf_file.base.file.?.pwriteAll(di_buf.items, file_pos); | 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,7 +2157,7 @@ pub fn writeDbgAranges(self: *Dwarf, addr: u64, size: u64) !void { |
| 2138 | }, | 2157 | }, |
| 2139 | .wasm => { | 2158 | .wasm => { |
| 2140 | const wasm_file = self.bin_file.cast(File.Wasm).?; | 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 | try debug_ranges.resize(wasm_file.base.allocator, needed_size); | 2161 | try debug_ranges.resize(wasm_file.base.allocator, needed_size); |
| 2143 | mem.copy(u8, debug_ranges.items, di_buf.items); | 2162 | mem.copy(u8, debug_ranges.items, di_buf.items); |
| 2144 | }, | 2163 | }, |
| ... | @@ -2275,19 +2294,23 @@ pub fn writeDbgLineHeader(self: *Dwarf) !void { | ... | @@ -2275,19 +2294,23 @@ pub fn writeDbgLineHeader(self: *Dwarf) !void { |
| 2275 | const needed_with_padding = padToIdeal(needed_bytes); | 2294 | const needed_with_padding = padToIdeal(needed_bytes); |
| 2276 | const delta = needed_with_padding - dbg_line_prg_off; | 2295 | const delta = needed_with_padding - dbg_line_prg_off; |
| 2277 | 2296 | ||
| 2278 | var src_fn = self.dbg_line_fn_first.?; | 2297 | const first_fn_index = self.src_fn_first_index.?; |
| 2279 | const last_fn = self.dbg_line_fn_last.?; | 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 | defer gpa.free(buffer); | 2305 | defer gpa.free(buffer); |
| 2283 | 2306 | ||
| 2284 | switch (self.bin_file.tag) { | 2307 | switch (self.bin_file.tag) { |
| 2285 | .elf => { | 2308 | .elf => { |
| 2286 | const elf_file = self.bin_file.cast(File.Elf).?; | 2309 | const elf_file = self.bin_file.cast(File.Elf).?; |
| 2287 | const shdr_index = elf_file.debug_line_section_index.?; | 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 | try elf_file.growNonAllocSection(shdr_index, needed_size, 1, true); | 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 | const amt = try elf_file.base.file.?.preadAll(buffer, file_pos); | 2315 | const amt = try elf_file.base.file.?.preadAll(buffer, file_pos); |
| 2293 | if (amt != buffer.len) return error.InputOutput; | 2316 | if (amt != buffer.len) return error.InputOutput; |
| ... | @@ -2299,7 +2322,7 @@ pub fn writeDbgLineHeader(self: *Dwarf) !void { | ... | @@ -2299,7 +2322,7 @@ pub fn writeDbgLineHeader(self: *Dwarf) !void { |
| 2299 | const sect_index = d_sym.debug_line_section_index.?; | 2322 | const sect_index = d_sym.debug_line_section_index.?; |
| 2300 | const needed_size = @intCast(u32, d_sym.getSection(sect_index).size + delta); | 2323 | const needed_size = @intCast(u32, d_sym.getSection(sect_index).size + delta); |
| 2301 | try d_sym.growSection(sect_index, needed_size, true); | 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 | const amt = try d_sym.file.preadAll(buffer, file_pos); | 2327 | const amt = try d_sym.file.preadAll(buffer, file_pos); |
| 2305 | if (amt != buffer.len) return error.InputOutput; | 2328 | if (amt != buffer.len) return error.InputOutput; |
| ... | @@ -2308,19 +2331,20 @@ pub fn writeDbgLineHeader(self: *Dwarf) !void { | ... | @@ -2308,19 +2331,20 @@ pub fn writeDbgLineHeader(self: *Dwarf) !void { |
| 2308 | }, | 2331 | }, |
| 2309 | .wasm => { | 2332 | .wasm => { |
| 2310 | const wasm_file = self.bin_file.cast(File.Wasm).?; | 2333 | const wasm_file = self.bin_file.cast(File.Wasm).?; |
| 2311 | const debug_line = &wasm_file.debug_line_atom.?.code; | 2334 | const debug_line = &wasm_file.getAtomPtr(wasm_file.debug_line_atom.?).code; |
| 2312 | mem.copy(u8, buffer, debug_line.items[src_fn.off..]); | 2335 | mem.copy(u8, buffer, debug_line.items[first_fn.off..]); |
| 2313 | try debug_line.resize(self.allocator, debug_line.items.len + delta); | 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 | else => unreachable, | 2339 | else => unreachable, |
| 2317 | } | 2340 | } |
| 2318 | 2341 | ||
| 2319 | while (true) { | 2342 | while (true) { |
| 2343 | const src_fn = self.getAtomPtr(.src_fn, src_fn_index); | ||
| 2320 | src_fn.off += delta; | 2344 | src_fn.off += delta; |
| 2321 | 2345 | ||
| 2322 | if (src_fn.next) |next| { | 2346 | if (src_fn.next_index) |next_index| { |
| 2323 | src_fn = next; | 2347 | src_fn_index = next_index; |
| 2324 | } else break; | 2348 | } else break; |
| 2325 | } | 2349 | } |
| 2326 | } | 2350 | } |
| ... | @@ -2346,7 +2370,7 @@ pub fn writeDbgLineHeader(self: *Dwarf) !void { | ... | @@ -2346,7 +2370,7 @@ pub fn writeDbgLineHeader(self: *Dwarf) !void { |
| 2346 | switch (self.bin_file.tag) { | 2370 | switch (self.bin_file.tag) { |
| 2347 | .elf => { | 2371 | .elf => { |
| 2348 | const elf_file = self.bin_file.cast(File.Elf).?; | 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 | const file_pos = debug_line_sect.sh_offset; | 2374 | const file_pos = debug_line_sect.sh_offset; |
| 2351 | try pwriteDbgLineNops(elf_file.base.file.?, file_pos, 0, di_buf.items, jmp_amt); | 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,7 +2382,7 @@ pub fn writeDbgLineHeader(self: *Dwarf) !void { |
| 2358 | }, | 2382 | }, |
| 2359 | .wasm => { | 2383 | .wasm => { |
| 2360 | const wasm_file = self.bin_file.cast(File.Wasm).?; | 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 | writeDbgLineNopsBuffered(debug_line.items, 0, 0, di_buf.items, jmp_amt); | 2386 | writeDbgLineNopsBuffered(debug_line.items, 0, 0, di_buf.items, jmp_amt); |
| 2363 | }, | 2387 | }, |
| 2364 | else => unreachable, | 2388 | else => unreachable, |
| ... | @@ -2366,22 +2390,26 @@ pub fn writeDbgLineHeader(self: *Dwarf) !void { | ... | @@ -2366,22 +2390,26 @@ pub fn writeDbgLineHeader(self: *Dwarf) !void { |
| 2366 | } | 2390 | } |
| 2367 | 2391 | ||
| 2368 | fn getDebugInfoOff(self: Dwarf) ?u32 { | 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 | return first.off; | 2395 | return first.off; |
| 2371 | } | 2396 | } |
| 2372 | 2397 | ||
| 2373 | fn getDebugInfoEnd(self: Dwarf) ?u32 { | 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 | return last.off + last.len; | 2401 | return last.off + last.len; |
| 2376 | } | 2402 | } |
| 2377 | 2403 | ||
| 2378 | fn getDebugLineProgramOff(self: Dwarf) ?u32 { | 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 | return first.off; | 2407 | return first.off; |
| 2381 | } | 2408 | } |
| 2382 | 2409 | ||
| 2383 | fn getDebugLineProgramEnd(self: Dwarf) ?u32 { | 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 | return last.off + last.len; | 2413 | return last.off + last.len; |
| 2386 | } | 2414 | } |
| 2387 | 2415 | ||
| ... | @@ -2435,15 +2463,6 @@ fn getRelocDbgInfoSubprogramHighPC(self: Dwarf) u32 { | ... | @@ -2435,15 +2463,6 @@ fn getRelocDbgInfoSubprogramHighPC(self: Dwarf) u32 { |
| 2435 | return dbg_info_low_pc_reloc_index + self.ptrWidthBytes(); | 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 | fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) { | 2466 | fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) { |
| 2448 | return actual_size +| (actual_size / ideal_factor); | 2467 | return actual_size +| (actual_size / ideal_factor); |
| 2449 | } | 2468 | } |
| ... | @@ -2465,29 +2484,20 @@ pub fn flushModule(self: *Dwarf, module: *Module) !void { | ... | @@ -2465,29 +2484,20 @@ pub fn flushModule(self: *Dwarf, module: *Module) !void { |
| 2465 | } | 2484 | } |
| 2466 | error_set.names = names; | 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 | var dbg_info_buffer = std.ArrayList(u8).init(arena); | 2487 | var dbg_info_buffer = std.ArrayList(u8).init(arena); |
| 2478 | try addDbgInfoErrorSet(arena, module, error_ty, self.target, &dbg_info_buffer); | 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 | log.debug("updateDeclDebugInfoAllocation in flushModule", .{}); | 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 | log.debug("writeDeclDebugInfo in flushModule", .{}); | 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 | const file_pos = blk: { | 2496 | const file_pos = blk: { |
| 2487 | switch (self.bin_file.tag) { | 2497 | switch (self.bin_file.tag) { |
| 2488 | .elf => { | 2498 | .elf => { |
| 2489 | const elf_file = self.bin_file.cast(File.Elf).?; | 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 | break :blk debug_info_sect.sh_offset; | 2501 | break :blk debug_info_sect.sh_offset; |
| 2492 | }, | 2502 | }, |
| 2493 | .macho => { | 2503 | .macho => { |
| ... | @@ -2502,22 +2512,23 @@ pub fn flushModule(self: *Dwarf, module: *Module) !void { | ... | @@ -2502,22 +2512,23 @@ pub fn flushModule(self: *Dwarf, module: *Module) !void { |
| 2502 | }; | 2512 | }; |
| 2503 | 2513 | ||
| 2504 | var buf: [@sizeOf(u32)]u8 = undefined; | 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 | while (self.global_abbrev_relocs.popOrNull()) |reloc| { | 2517 | while (self.global_abbrev_relocs.popOrNull()) |reloc| { |
| 2518 | const atom = self.getAtom(.di_atom, reloc.atom_index); | ||
| 2508 | switch (self.bin_file.tag) { | 2519 | switch (self.bin_file.tag) { |
| 2509 | .elf => { | 2520 | .elf => { |
| 2510 | const elf_file = self.bin_file.cast(File.Elf).?; | 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 | .macho => { | 2524 | .macho => { |
| 2514 | const d_sym = self.bin_file.cast(File.MachO).?.getDebugSymbols().?; | 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 | .wasm => { | 2528 | .wasm => { |
| 2518 | const wasm_file = self.bin_file.cast(File.Wasm).?; | 2529 | const wasm_file = self.bin_file.cast(File.Wasm).?; |
| 2519 | const debug_info = wasm_file.debug_info_atom.?.code; | 2530 | const debug_info = wasm_file.getAtomPtr(wasm_file.debug_info_atom.?).code; |
| 2520 | mem.copy(u8, debug_info.items[reloc.atom.off + reloc.offset ..], &buf); | 2531 | mem.copy(u8, debug_info.items[atom.off + reloc.offset ..], &buf); |
| 2521 | }, | 2532 | }, |
| 2522 | else => unreachable, | 2533 | else => unreachable, |
| 2523 | } | 2534 | } |
| ... | @@ -2635,12 +2646,62 @@ fn addDbgInfoErrorSet( | ... | @@ -2635,12 +2646,62 @@ fn addDbgInfoErrorSet( |
| 2635 | try dbg_info_buffer.append(0); | 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 { | 2649 | const Kind = enum { src_fn, di_atom }; |
| 2639 | const decl = mod.declPtr(decl_index); | 2650 | |
| 2640 | return switch (tag) { | 2651 | fn createAtom(self: *Dwarf, comptime kind: Kind) !Atom.Index { |
| 2641 | .elf => &decl.link.elf.dbg_info_atom, | 2652 | const index = blk: { |
| 2642 | .macho => &decl.link.macho.dbg_info_atom, | 2653 | switch (kind) { |
| 2643 | .wasm => &decl.link.wasm.dbg_info_atom, | 2654 | .src_fn => { |
| 2644 | else => unreachable, | 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,40 +1,89 @@ |
| 1 | const Elf = @This(); | 1 | const Elf = @This(); |
| 2 | 2 | ||
| 3 | const std = @import("std"); | 3 | const std = @import("std"); |
| 4 | const build_options = @import("build_options"); | ||
| 4 | const builtin = @import("builtin"); | 5 | const builtin = @import("builtin"); |
| 5 | const math = std.math; | ||
| 6 | const mem = std.mem; | ||
| 7 | const assert = std.debug.assert; | 6 | const assert = std.debug.assert; |
| 8 | const Allocator = std.mem.Allocator; | ||
| 9 | const fs = std.fs; | ||
| 10 | const elf = std.elf; | 7 | const elf = std.elf; |
| 8 | const fs = std.fs; | ||
| 11 | const log = std.log.scoped(.link); | 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 | const codegen = @import("../codegen.zig"); | 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 | const glibc = @import("../glibc.zig"); | 14 | const glibc = @import("../glibc.zig"); |
| 15 | const link = @import("../link.zig"); | ||
| 16 | const lldMain = @import("../main.zig").lldMain; | ||
| 28 | const musl = @import("../musl.zig"); | 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 | const Air = @import("../Air.zig"); | 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 | const Liveness = @import("../Liveness.zig"); | 28 | const Liveness = @import("../Liveness.zig"); |
| 32 | const LlvmObject = @import("../codegen/llvm.zig").Object; | 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 | const default_entry_addr = 0x8000000; | 37 | const default_entry_addr = 0x8000000; |
| 35 | 38 | ||
| 36 | pub const base_tag: File.Tag = .elf; | 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 | base: File, | 87 | base: File, |
| 39 | dwarf: ?Dwarf = null, | 88 | dwarf: ?Dwarf = null, |
| 40 | 89 | ||
| ... | @@ -45,12 +94,12 @@ llvm_object: ?*LlvmObject = null, | ... | @@ -45,12 +94,12 @@ llvm_object: ?*LlvmObject = null, |
| 45 | 94 | ||
| 46 | /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write. | 95 | /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write. |
| 47 | /// Same order as in the file. | 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 | shdr_table_offset: ?u64 = null, | 98 | shdr_table_offset: ?u64 = null, |
| 50 | 99 | ||
| 51 | /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write. | 100 | /// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write. |
| 52 | /// Same order as in the file. | 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 | phdr_table_offset: ?u64 = null, | 103 | phdr_table_offset: ?u64 = null, |
| 55 | /// The index into the program headers of a PT_LOAD program header with Read and Execute flags | 104 | /// The index into the program headers of a PT_LOAD program header with Read and Execute flags |
| 56 | phdr_load_re_index: ?u16 = null, | 105 | phdr_load_re_index: ?u16 = null, |
| ... | @@ -62,12 +111,10 @@ phdr_load_ro_index: ?u16 = null, | ... | @@ -62,12 +111,10 @@ phdr_load_ro_index: ?u16 = null, |
| 62 | /// The index into the program headers of a PT_LOAD program header with Write flag | 111 | /// The index into the program headers of a PT_LOAD program header with Write flag |
| 63 | phdr_load_rw_index: ?u16 = null, | 112 | phdr_load_rw_index: ?u16 = null, |
| 64 | 113 | ||
| 65 | phdr_shdr_table: std.AutoHashMapUnmanaged(u16, u16) = .{}, | ||
| 66 | |||
| 67 | entry_addr: ?u64 = null, | 114 | entry_addr: ?u64 = null, |
| 68 | page_size: u32, | 115 | page_size: u32, |
| 69 | 116 | ||
| 70 | shstrtab: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8){}, | 117 | shstrtab: StringTable(.strtab) = .{}, |
| 71 | shstrtab_index: ?u16 = null, | 118 | shstrtab_index: ?u16 = null, |
| 72 | 119 | ||
| 73 | symtab_section_index: ?u16 = null, | 120 | symtab_section_index: ?u16 = null, |
| ... | @@ -110,39 +157,14 @@ debug_line_header_dirty: bool = false, | ... | @@ -110,39 +157,14 @@ debug_line_header_dirty: bool = false, |
| 110 | 157 | ||
| 111 | error_flags: File.ErrorFlags = File.ErrorFlags{}, | 158 | error_flags: File.ErrorFlags = File.ErrorFlags{}, |
| 112 | 159 | ||
| 113 | /// Pointer to the last allocated atom | 160 | /// Table of tracked Decls. |
| 114 | atoms: std.AutoHashMapUnmanaged(u16, *TextBlock) = .{}, | 161 | decls: std.AutoHashMapUnmanaged(Module.Decl.Index, DeclMetadata) = .{}, |
| 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) = .{}, | ||
| 138 | 162 | ||
| 139 | /// List of atoms that are owned directly by the linker. | 163 | /// List of atoms that are owned directly by the linker. |
| 140 | /// Currently these are only atoms that are the result of linking | 164 | atoms: std.ArrayListUnmanaged(Atom) = .{}, |
| 141 | /// object files. Atoms which take part in incremental linking are | 165 | |
| 142 | /// at present owned by Module.Decl. | 166 | /// Table of atoms indexed by the symbol index. |
| 143 | /// TODO consolidate this. | 167 | atom_by_index_table: std.AutoHashMapUnmanaged(u32, Atom.Index) = .{}, |
| 144 | managed_atoms: std.ArrayListUnmanaged(*TextBlock) = .{}, | ||
| 145 | atom_by_index_table: std.AutoHashMapUnmanaged(u32, *TextBlock) = .{}, | ||
| 146 | 168 | ||
| 147 | /// Table of unnamed constants associated with a parent `Decl`. | 169 | /// Table of unnamed constants associated with a parent `Decl`. |
| 148 | /// We store them here so that we can free the constants whenever the `Decl` | 170 | /// We store them here so that we can free the constants whenever the `Decl` |
| ... | @@ -170,15 +192,8 @@ unnamed_const_atoms: UnnamedConstTable = .{}, | ... | @@ -170,15 +192,8 @@ unnamed_const_atoms: UnnamedConstTable = .{}, |
| 170 | /// this will be a table indexed by index into the list of Atoms. | 192 | /// this will be a table indexed by index into the list of Atoms. |
| 171 | relocs: RelocTable = .{}, | 193 | relocs: RelocTable = .{}, |
| 172 | 194 | ||
| 173 | const Reloc = struct { | 195 | const RelocTable = std.AutoHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(Atom.Reloc)); |
| 174 | target: u32, | 196 | const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(Atom.Index)); |
| 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)); | ||
| 182 | 197 | ||
| 183 | /// When allocating, the ideal_capacity is calculated by | 198 | /// When allocating, the ideal_capacity is calculated by |
| 184 | /// actual_capacity + (actual_capacity / ideal_factor) | 199 | /// actual_capacity + (actual_capacity / ideal_factor) |
| ... | @@ -187,67 +202,11 @@ const ideal_factor = 3; | ... | @@ -187,67 +202,11 @@ const ideal_factor = 3; |
| 187 | /// In order for a slice of bytes to be considered eligible to keep metadata pointing at | 202 | /// In order for a slice of bytes to be considered eligible to keep metadata pointing at |
| 188 | /// it as a possible place to put new symbols, it must have enough room for this many bytes | 203 | /// it as a possible place to put new symbols, it must have enough room for this many bytes |
| 189 | /// (plus extra for reserved capacity). | 204 | /// (plus extra for reserved capacity). |
| 190 | const minimum_text_block_size = 64; | 205 | const minimum_atom_size = 64; |
| 191 | const min_text_capacity = padToIdeal(minimum_text_block_size); | 206 | pub const min_text_capacity = padToIdeal(minimum_atom_size); |
| 192 | 207 | ||
| 193 | pub const PtrWidth = enum { p32, p64 }; | 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 | pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Options) !*Elf { | 210 | pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Options) !*Elf { |
| 252 | assert(options.target.ofmt == .elf); | 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,16 +238,19 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option |
| 279 | 238 | ||
| 280 | // There must always be a null section in index 0 | 239 | // There must always be a null section in index 0 |
| 281 | try self.sections.append(allocator, .{ | 240 | try self.sections.append(allocator, .{ |
| 282 | .sh_name = 0, | 241 | .shdr = .{ |
| 283 | .sh_type = elf.SHT_NULL, | 242 | .sh_name = 0, |
| 284 | .sh_flags = 0, | 243 | .sh_type = elf.SHT_NULL, |
| 285 | .sh_addr = 0, | 244 | .sh_flags = 0, |
| 286 | .sh_offset = 0, | 245 | .sh_addr = 0, |
| 287 | .sh_size = 0, | 246 | .sh_offset = 0, |
| 288 | .sh_link = 0, | 247 | .sh_size = 0, |
| 289 | .sh_info = 0, | 248 | .sh_link = 0, |
| 290 | .sh_addralign = 0, | 249 | .sh_info = 0, |
| 291 | .sh_entsize = 0, | 250 | .sh_addralign = 0, |
| 251 | .sh_entsize = 0, | ||
| 252 | }, | ||
| 253 | .phdr_index = undefined, | ||
| 292 | }); | 254 | }); |
| 293 | 255 | ||
| 294 | try self.populateMissingMetadata(); | 256 | try self.populateMissingMetadata(); |
| ... | @@ -335,74 +297,67 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Elf { | ... | @@ -335,74 +297,67 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Elf { |
| 335 | } | 297 | } |
| 336 | 298 | ||
| 337 | pub fn deinit(self: *Elf) void { | 299 | pub fn deinit(self: *Elf) void { |
| 300 | const gpa = self.base.allocator; | ||
| 301 | |||
| 338 | if (build_options.have_llvm) { | 302 | if (build_options.have_llvm) { |
| 339 | if (self.llvm_object) |llvm_object| llvm_object.destroy(self.base.allocator); | 303 | if (self.llvm_object) |llvm_object| llvm_object.destroy(gpa); |
| 340 | } | 304 | } |
| 341 | 305 | ||
| 342 | self.sections.deinit(self.base.allocator); | 306 | for (self.sections.items(.free_list)) |*free_list| { |
| 343 | self.program_headers.deinit(self.base.allocator); | 307 | free_list.deinit(gpa); |
| 344 | self.shstrtab.deinit(self.base.allocator); | 308 | } |
| 345 | self.local_symbols.deinit(self.base.allocator); | 309 | self.sections.deinit(gpa); |
| 346 | self.global_symbols.deinit(self.base.allocator); | 310 | |
| 347 | self.global_symbol_free_list.deinit(self.base.allocator); | 311 | self.program_headers.deinit(gpa); |
| 348 | self.local_symbol_free_list.deinit(self.base.allocator); | 312 | self.shstrtab.deinit(gpa); |
| 349 | self.offset_table_free_list.deinit(self.base.allocator); | 313 | self.local_symbols.deinit(gpa); |
| 350 | self.offset_table.deinit(self.base.allocator); | 314 | self.global_symbols.deinit(gpa); |
| 351 | self.phdr_shdr_table.deinit(self.base.allocator); | 315 | self.global_symbol_free_list.deinit(gpa); |
| 352 | self.decls.deinit(self.base.allocator); | 316 | self.local_symbol_free_list.deinit(gpa); |
| 353 | 317 | self.offset_table_free_list.deinit(gpa); | |
| 354 | self.atoms.deinit(self.base.allocator); | 318 | self.offset_table.deinit(gpa); |
| 319 | |||
| 355 | { | 320 | { |
| 356 | var it = self.atom_free_lists.valueIterator(); | 321 | var it = self.decls.iterator(); |
| 357 | while (it.next()) |free_list| { | 322 | while (it.next()) |entry| { |
| 358 | free_list.deinit(self.base.allocator); | 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| { | 328 | self.atoms.deinit(gpa); |
| 364 | self.base.allocator.destroy(atom); | 329 | self.atom_by_index_table.deinit(gpa); |
| 365 | } | ||
| 366 | self.managed_atoms.deinit(self.base.allocator); | ||
| 367 | 330 | ||
| 368 | { | 331 | { |
| 369 | var it = self.unnamed_const_atoms.valueIterator(); | 332 | var it = self.unnamed_const_atoms.valueIterator(); |
| 370 | while (it.next()) |atoms| { | 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 | var it = self.relocs.valueIterator(); | 340 | var it = self.relocs.valueIterator(); |
| 378 | while (it.next()) |relocs| { | 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 | if (self.dwarf) |*dw| { | 347 | if (self.dwarf) |*dw| { |
| 387 | dw.deinit(); | 348 | dw.deinit(); |
| 388 | } | 349 | } |
| 389 | } | 350 | } |
| 390 | 351 | ||
| 391 | pub fn getDeclVAddr(self: *Elf, decl_index: Module.Decl.Index, reloc_info: File.RelocInfo) !u64 { | 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 | assert(self.llvm_object == null); | 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; | 355 | const this_atom_index = try self.getOrCreateAtomForDecl(decl_index); |
| 399 | const vaddr = self.local_symbols.items[target].st_value; | 356 | const this_atom = self.getAtom(this_atom_index); |
| 400 | const atom = self.atom_by_index_table.get(reloc_info.parent_atom_index).?; | 357 | const target = this_atom.getSymbolIndex().?; |
| 401 | const gop = try self.relocs.getOrPut(self.base.allocator, atom); | 358 | const vaddr = this_atom.getSymbol(self).st_value; |
| 402 | if (!gop.found_existing) { | 359 | const atom_index = self.getAtomIndexForSymbol(reloc_info.parent_atom_index).?; |
| 403 | gop.value_ptr.* = .{}; | 360 | try Atom.addRelocation(self, atom_index, .{ |
| 404 | } | ||
| 405 | try gop.value_ptr.append(self.base.allocator, .{ | ||
| 406 | .target = target, | 361 | .target = target, |
| 407 | .offset = reloc_info.offset, | 362 | .offset = reloc_info.offset, |
| 408 | .addend = reloc_info.addend, | 363 | .addend = reloc_info.addend, |
| ... | @@ -423,7 +378,7 @@ fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 { | ... | @@ -423,7 +378,7 @@ fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 { |
| 423 | 378 | ||
| 424 | if (self.shdr_table_offset) |off| { | 379 | if (self.shdr_table_offset) |off| { |
| 425 | const shdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Shdr) else @sizeOf(elf.Elf64_Shdr); | 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 | const increased_size = padToIdeal(tight_size); | 382 | const increased_size = padToIdeal(tight_size); |
| 428 | const test_end = off + increased_size; | 383 | const test_end = off + increased_size; |
| 429 | if (end > off and start < test_end) { | 384 | if (end > off and start < test_end) { |
| ... | @@ -433,7 +388,7 @@ fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 { | ... | @@ -433,7 +388,7 @@ fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 { |
| 433 | 388 | ||
| 434 | if (self.phdr_table_offset) |off| { | 389 | if (self.phdr_table_offset) |off| { |
| 435 | const phdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Phdr) else @sizeOf(elf.Elf64_Phdr); | 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 | const increased_size = padToIdeal(tight_size); | 392 | const increased_size = padToIdeal(tight_size); |
| 438 | const test_end = off + increased_size; | 393 | const test_end = off + increased_size; |
| 439 | if (end > off and start < test_end) { | 394 | if (end > off and start < test_end) { |
| ... | @@ -441,7 +396,7 @@ fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 { | ... | @@ -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 | const increased_size = padToIdeal(section.sh_size); | 400 | const increased_size = padToIdeal(section.sh_size); |
| 446 | const test_end = section.sh_offset + increased_size; | 401 | const test_end = section.sh_offset + increased_size; |
| 447 | if (end > section.sh_offset and start < test_end) { | 402 | if (end > section.sh_offset and start < test_end) { |
| ... | @@ -468,7 +423,7 @@ pub fn allocatedSize(self: *Elf, start: u64) u64 { | ... | @@ -468,7 +423,7 @@ pub fn allocatedSize(self: *Elf, start: u64) u64 { |
| 468 | if (self.phdr_table_offset) |off| { | 423 | if (self.phdr_table_offset) |off| { |
| 469 | if (off > start and off < min_pos) min_pos = off; | 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 | if (section.sh_offset <= start) continue; | 427 | if (section.sh_offset <= start) continue; |
| 473 | if (section.sh_offset < min_pos) min_pos = section.sh_offset; | 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,31 +442,10 @@ pub fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u32) u64 { |
| 487 | return start; | 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 | pub fn populateMissingMetadata(self: *Elf) !void { | 445 | pub fn populateMissingMetadata(self: *Elf) !void { |
| 513 | assert(self.llvm_object == null); | 446 | assert(self.llvm_object == null); |
| 514 | 447 | ||
| 448 | const gpa = self.base.allocator; | ||
| 515 | const small_ptr = switch (self.ptr_width) { | 449 | const small_ptr = switch (self.ptr_width) { |
| 516 | .p32 => true, | 450 | .p32 => true, |
| 517 | .p64 => false, | 451 | .p64 => false, |
| ... | @@ -525,7 +459,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { | ... | @@ -525,7 +459,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { |
| 525 | const off = self.findFreeSpace(file_size, p_align); | 459 | const off = self.findFreeSpace(file_size, p_align); |
| 526 | log.debug("found PT_LOAD RE free space 0x{x} to 0x{x}", .{ off, off + file_size }); | 460 | log.debug("found PT_LOAD RE free space 0x{x} to 0x{x}", .{ off, off + file_size }); |
| 527 | const entry_addr: u64 = self.entry_addr orelse if (self.base.options.target.cpu.arch == .spu_2) @as(u64, 0) else default_entry_addr; | 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 | .p_type = elf.PT_LOAD, | 463 | .p_type = elf.PT_LOAD, |
| 530 | .p_offset = off, | 464 | .p_offset = off, |
| 531 | .p_filesz = file_size, | 465 | .p_filesz = file_size, |
| ... | @@ -535,7 +469,6 @@ pub fn populateMissingMetadata(self: *Elf) !void { | ... | @@ -535,7 +469,6 @@ pub fn populateMissingMetadata(self: *Elf) !void { |
| 535 | .p_align = p_align, | 469 | .p_align = p_align, |
| 536 | .p_flags = elf.PF_X | elf.PF_R, | 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 | self.entry_addr = null; | 472 | self.entry_addr = null; |
| 540 | self.phdr_table_dirty = true; | 473 | self.phdr_table_dirty = true; |
| 541 | } | 474 | } |
| ... | @@ -552,7 +485,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { | ... | @@ -552,7 +485,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { |
| 552 | // we'll need to re-use that function anyway, in case the GOT grows and overlaps something | 485 | // we'll need to re-use that function anyway, in case the GOT grows and overlaps something |
| 553 | // else in virtual memory. | 486 | // else in virtual memory. |
| 554 | const got_addr: u32 = if (self.base.options.target.cpu.arch.ptrBitWidth() >= 32) 0x4000000 else 0x8000; | 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 | .p_type = elf.PT_LOAD, | 489 | .p_type = elf.PT_LOAD, |
| 557 | .p_offset = off, | 490 | .p_offset = off, |
| 558 | .p_filesz = file_size, | 491 | .p_filesz = file_size, |
| ... | @@ -575,7 +508,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { | ... | @@ -575,7 +508,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { |
| 575 | log.debug("found PT_LOAD RO free space 0x{x} to 0x{x}", .{ off, off + file_size }); | 508 | log.debug("found PT_LOAD RO free space 0x{x} to 0x{x}", .{ off, off + file_size }); |
| 576 | // TODO Same as for GOT | 509 | // TODO Same as for GOT |
| 577 | const rodata_addr: u32 = if (self.base.options.target.cpu.arch.ptrBitWidth() >= 32) 0xc000000 else 0xa000; | 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 | .p_type = elf.PT_LOAD, | 512 | .p_type = elf.PT_LOAD, |
| 580 | .p_offset = off, | 513 | .p_offset = off, |
| 581 | .p_filesz = file_size, | 514 | .p_filesz = file_size, |
| ... | @@ -585,7 +518,6 @@ pub fn populateMissingMetadata(self: *Elf) !void { | ... | @@ -585,7 +518,6 @@ pub fn populateMissingMetadata(self: *Elf) !void { |
| 585 | .p_align = p_align, | 518 | .p_align = p_align, |
| 586 | .p_flags = elf.PF_R, | 519 | .p_flags = elf.PF_R, |
| 587 | }); | 520 | }); |
| 588 | try self.atom_free_lists.putNoClobber(self.base.allocator, self.phdr_load_ro_index.?, .{}); | ||
| 589 | self.phdr_table_dirty = true; | 521 | self.phdr_table_dirty = true; |
| 590 | } | 522 | } |
| 591 | 523 | ||
| ... | @@ -599,7 +531,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { | ... | @@ -599,7 +531,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { |
| 599 | log.debug("found PT_LOAD RW free space 0x{x} to 0x{x}", .{ off, off + file_size }); | 531 | log.debug("found PT_LOAD RW free space 0x{x} to 0x{x}", .{ off, off + file_size }); |
| 600 | // TODO Same as for GOT | 532 | // TODO Same as for GOT |
| 601 | const rwdata_addr: u32 = if (self.base.options.target.cpu.arch.ptrBitWidth() >= 32) 0x10000000 else 0xc000; | 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 | .p_type = elf.PT_LOAD, | 535 | .p_type = elf.PT_LOAD, |
| 604 | .p_offset = off, | 536 | .p_offset = off, |
| 605 | .p_filesz = file_size, | 537 | .p_filesz = file_size, |
| ... | @@ -609,148 +541,145 @@ pub fn populateMissingMetadata(self: *Elf) !void { | ... | @@ -609,148 +541,145 @@ pub fn populateMissingMetadata(self: *Elf) !void { |
| 609 | .p_align = p_align, | 541 | .p_align = p_align, |
| 610 | .p_flags = elf.PF_R | elf.PF_W, | 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 | self.phdr_table_dirty = true; | 544 | self.phdr_table_dirty = true; |
| 614 | } | 545 | } |
| 615 | 546 | ||
| 616 | if (self.shstrtab_index == null) { | 547 | if (self.shstrtab_index == null) { |
| 617 | self.shstrtab_index = @intCast(u16, self.sections.items.len); | 548 | self.shstrtab_index = @intCast(u16, self.sections.slice().len); |
| 618 | assert(self.shstrtab.items.len == 0); | 549 | assert(self.shstrtab.buffer.items.len == 0); |
| 619 | try self.shstrtab.append(self.base.allocator, 0); // need a 0 at position 0 | 550 | try self.shstrtab.buffer.append(gpa, 0); // need a 0 at position 0 |
| 620 | const off = self.findFreeSpace(self.shstrtab.items.len, 1); | 551 | const off = self.findFreeSpace(self.shstrtab.buffer.items.len, 1); |
| 621 | log.debug("found shstrtab free space 0x{x} to 0x{x}", .{ off, off + self.shstrtab.items.len }); | 552 | log.debug("found shstrtab free space 0x{x} to 0x{x}", .{ off, off + self.shstrtab.buffer.items.len }); |
| 622 | try self.sections.append(self.base.allocator, .{ | 553 | try self.sections.append(gpa, .{ |
| 623 | .sh_name = try self.makeString(".shstrtab"), | 554 | .shdr = .{ |
| 624 | .sh_type = elf.SHT_STRTAB, | 555 | .sh_name = try self.shstrtab.insert(gpa, ".shstrtab"), |
| 625 | .sh_flags = 0, | 556 | .sh_type = elf.SHT_STRTAB, |
| 626 | .sh_addr = 0, | 557 | .sh_flags = 0, |
| 627 | .sh_offset = off, | 558 | .sh_addr = 0, |
| 628 | .sh_size = self.shstrtab.items.len, | 559 | .sh_offset = off, |
| 629 | .sh_link = 0, | 560 | .sh_size = self.shstrtab.buffer.items.len, |
| 630 | .sh_info = 0, | 561 | .sh_link = 0, |
| 631 | .sh_addralign = 1, | 562 | .sh_info = 0, |
| 632 | .sh_entsize = 0, | 563 | .sh_addralign = 1, |
| 564 | .sh_entsize = 0, | ||
| 565 | }, | ||
| 566 | .phdr_index = undefined, | ||
| 633 | }); | 567 | }); |
| 634 | self.shstrtab_dirty = true; | 568 | self.shstrtab_dirty = true; |
| 635 | self.shdr_table_dirty = true; | 569 | self.shdr_table_dirty = true; |
| 636 | } | 570 | } |
| 637 | 571 | ||
| 638 | if (self.text_section_index == null) { | 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 | const phdr = &self.program_headers.items[self.phdr_load_re_index.?]; | 574 | const phdr = &self.program_headers.items[self.phdr_load_re_index.?]; |
| 641 | 575 | ||
| 642 | try self.sections.append(self.base.allocator, .{ | 576 | try self.sections.append(gpa, .{ |
| 643 | .sh_name = try self.makeString(".text"), | 577 | .shdr = .{ |
| 644 | .sh_type = elf.SHT_PROGBITS, | 578 | .sh_name = try self.shstrtab.insert(gpa, ".text"), |
| 645 | .sh_flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR, | 579 | .sh_type = elf.SHT_PROGBITS, |
| 646 | .sh_addr = phdr.p_vaddr, | 580 | .sh_flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR, |
| 647 | .sh_offset = phdr.p_offset, | 581 | .sh_addr = phdr.p_vaddr, |
| 648 | .sh_size = phdr.p_filesz, | 582 | .sh_offset = phdr.p_offset, |
| 649 | .sh_link = 0, | 583 | .sh_size = phdr.p_filesz, |
| 650 | .sh_info = 0, | 584 | .sh_link = 0, |
| 651 | .sh_addralign = 1, | 585 | .sh_info = 0, |
| 652 | .sh_entsize = 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 | self.shdr_table_dirty = true; | 591 | self.shdr_table_dirty = true; |
| 660 | } | 592 | } |
| 661 | 593 | ||
| 662 | if (self.got_section_index == null) { | 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 | const phdr = &self.program_headers.items[self.phdr_got_index.?]; | 596 | const phdr = &self.program_headers.items[self.phdr_got_index.?]; |
| 665 | 597 | ||
| 666 | try self.sections.append(self.base.allocator, .{ | 598 | try self.sections.append(gpa, .{ |
| 667 | .sh_name = try self.makeString(".got"), | 599 | .shdr = .{ |
| 668 | .sh_type = elf.SHT_PROGBITS, | 600 | .sh_name = try self.shstrtab.insert(gpa, ".got"), |
| 669 | .sh_flags = elf.SHF_ALLOC, | 601 | .sh_type = elf.SHT_PROGBITS, |
| 670 | .sh_addr = phdr.p_vaddr, | 602 | .sh_flags = elf.SHF_ALLOC, |
| 671 | .sh_offset = phdr.p_offset, | 603 | .sh_addr = phdr.p_vaddr, |
| 672 | .sh_size = phdr.p_filesz, | 604 | .sh_offset = phdr.p_offset, |
| 673 | .sh_link = 0, | 605 | .sh_size = phdr.p_filesz, |
| 674 | .sh_info = 0, | 606 | .sh_link = 0, |
| 675 | .sh_addralign = @as(u16, ptr_size), | 607 | .sh_info = 0, |
| 676 | .sh_entsize = 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 | self.shdr_table_dirty = true; | 613 | self.shdr_table_dirty = true; |
| 684 | } | 614 | } |
| 685 | 615 | ||
| 686 | if (self.rodata_section_index == null) { | 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 | const phdr = &self.program_headers.items[self.phdr_load_ro_index.?]; | 618 | const phdr = &self.program_headers.items[self.phdr_load_ro_index.?]; |
| 689 | 619 | ||
| 690 | try self.sections.append(self.base.allocator, .{ | 620 | try self.sections.append(gpa, .{ |
| 691 | .sh_name = try self.makeString(".rodata"), | 621 | .shdr = .{ |
| 692 | .sh_type = elf.SHT_PROGBITS, | 622 | .sh_name = try self.shstrtab.insert(gpa, ".rodata"), |
| 693 | .sh_flags = elf.SHF_ALLOC, | 623 | .sh_type = elf.SHT_PROGBITS, |
| 694 | .sh_addr = phdr.p_vaddr, | 624 | .sh_flags = elf.SHF_ALLOC, |
| 695 | .sh_offset = phdr.p_offset, | 625 | .sh_addr = phdr.p_vaddr, |
| 696 | .sh_size = phdr.p_filesz, | 626 | .sh_offset = phdr.p_offset, |
| 697 | .sh_link = 0, | 627 | .sh_size = phdr.p_filesz, |
| 698 | .sh_info = 0, | 628 | .sh_link = 0, |
| 699 | .sh_addralign = 1, | 629 | .sh_info = 0, |
| 700 | .sh_entsize = 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 | self.shdr_table_dirty = true; | 635 | self.shdr_table_dirty = true; |
| 708 | } | 636 | } |
| 709 | 637 | ||
| 710 | if (self.data_section_index == null) { | 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 | const phdr = &self.program_headers.items[self.phdr_load_rw_index.?]; | 640 | const phdr = &self.program_headers.items[self.phdr_load_rw_index.?]; |
| 713 | 641 | ||
| 714 | try self.sections.append(self.base.allocator, .{ | 642 | try self.sections.append(gpa, .{ |
| 715 | .sh_name = try self.makeString(".data"), | 643 | .shdr = .{ |
| 716 | .sh_type = elf.SHT_PROGBITS, | 644 | .sh_name = try self.shstrtab.insert(gpa, ".data"), |
| 717 | .sh_flags = elf.SHF_WRITE | elf.SHF_ALLOC, | 645 | .sh_type = elf.SHT_PROGBITS, |
| 718 | .sh_addr = phdr.p_vaddr, | 646 | .sh_flags = elf.SHF_WRITE | elf.SHF_ALLOC, |
| 719 | .sh_offset = phdr.p_offset, | 647 | .sh_addr = phdr.p_vaddr, |
| 720 | .sh_size = phdr.p_filesz, | 648 | .sh_offset = phdr.p_offset, |
| 721 | .sh_link = 0, | 649 | .sh_size = phdr.p_filesz, |
| 722 | .sh_info = 0, | 650 | .sh_link = 0, |
| 723 | .sh_addralign = @as(u16, ptr_size), | 651 | .sh_info = 0, |
| 724 | .sh_entsize = 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 | self.shdr_table_dirty = true; | 657 | self.shdr_table_dirty = true; |
| 732 | } | 658 | } |
| 733 | 659 | ||
| 734 | if (self.symtab_section_index == null) { | 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 | const min_align: u16 = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym); | 662 | const min_align: u16 = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym); |
| 737 | const each_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Sym) else @sizeOf(elf.Elf64_Sym); | 663 | const each_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Sym) else @sizeOf(elf.Elf64_Sym); |
| 738 | const file_size = self.base.options.symbol_count_hint * each_size; | 664 | const file_size = self.base.options.symbol_count_hint * each_size; |
| 739 | const off = self.findFreeSpace(file_size, min_align); | 665 | const off = self.findFreeSpace(file_size, min_align); |
| 740 | log.debug("found symtab free space 0x{x} to 0x{x}", .{ off, off + file_size }); | 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, .{ | 668 | try self.sections.append(gpa, .{ |
| 743 | .sh_name = try self.makeString(".symtab"), | 669 | .shdr = .{ |
| 744 | .sh_type = elf.SHT_SYMTAB, | 670 | .sh_name = try self.shstrtab.insert(gpa, ".symtab"), |
| 745 | .sh_flags = 0, | 671 | .sh_type = elf.SHT_SYMTAB, |
| 746 | .sh_addr = 0, | 672 | .sh_flags = 0, |
| 747 | .sh_offset = off, | 673 | .sh_addr = 0, |
| 748 | .sh_size = file_size, | 674 | .sh_offset = off, |
| 749 | // The section header index of the associated string table. | 675 | .sh_size = file_size, |
| 750 | .sh_link = self.shstrtab_index.?, | 676 | // The section header index of the associated string table. |
| 751 | .sh_info = @intCast(u32, self.local_symbols.items.len), | 677 | .sh_link = self.shstrtab_index.?, |
| 752 | .sh_addralign = min_align, | 678 | .sh_info = @intCast(u32, self.local_symbols.items.len), |
| 753 | .sh_entsize = each_size, | 679 | .sh_addralign = min_align, |
| 680 | .sh_entsize = each_size, | ||
| 681 | }, | ||
| 682 | .phdr_index = undefined, | ||
| 754 | }); | 683 | }); |
| 755 | self.shdr_table_dirty = true; | 684 | self.shdr_table_dirty = true; |
| 756 | try self.writeSymbol(0); | 685 | try self.writeSymbol(0); |
| ... | @@ -758,27 +687,30 @@ pub fn populateMissingMetadata(self: *Elf) !void { | ... | @@ -758,27 +687,30 @@ pub fn populateMissingMetadata(self: *Elf) !void { |
| 758 | 687 | ||
| 759 | if (self.dwarf) |*dw| { | 688 | if (self.dwarf) |*dw| { |
| 760 | if (self.debug_str_section_index == null) { | 689 | if (self.debug_str_section_index == null) { |
| 761 | self.debug_str_section_index = @intCast(u16, self.sections.items.len); | 690 | self.debug_str_section_index = @intCast(u16, self.sections.slice().len); |
| 762 | assert(dw.strtab.items.len == 0); | 691 | assert(dw.strtab.buffer.items.len == 0); |
| 763 | try dw.strtab.append(self.base.allocator, 0); | 692 | try dw.strtab.buffer.append(gpa, 0); |
| 764 | try self.sections.append(self.base.allocator, .{ | 693 | try self.sections.append(gpa, .{ |
| 765 | .sh_name = try self.makeString(".debug_str"), | 694 | .shdr = .{ |
| 766 | .sh_type = elf.SHT_PROGBITS, | 695 | .sh_name = try self.shstrtab.insert(gpa, ".debug_str"), |
| 767 | .sh_flags = elf.SHF_MERGE | elf.SHF_STRINGS, | 696 | .sh_type = elf.SHT_PROGBITS, |
| 768 | .sh_addr = 0, | 697 | .sh_flags = elf.SHF_MERGE | elf.SHF_STRINGS, |
| 769 | .sh_offset = 0, | 698 | .sh_addr = 0, |
| 770 | .sh_size = 0, | 699 | .sh_offset = 0, |
| 771 | .sh_link = 0, | 700 | .sh_size = 0, |
| 772 | .sh_info = 0, | 701 | .sh_link = 0, |
| 773 | .sh_addralign = 1, | 702 | .sh_info = 0, |
| 774 | .sh_entsize = 1, | 703 | .sh_addralign = 1, |
| 704 | .sh_entsize = 1, | ||
| 705 | }, | ||
| 706 | .phdr_index = undefined, | ||
| 775 | }); | 707 | }); |
| 776 | self.debug_strtab_dirty = true; | 708 | self.debug_strtab_dirty = true; |
| 777 | self.shdr_table_dirty = true; | 709 | self.shdr_table_dirty = true; |
| 778 | } | 710 | } |
| 779 | 711 | ||
| 780 | if (self.debug_info_section_index == null) { | 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 | const file_size_hint = 200; | 715 | const file_size_hint = 200; |
| 784 | const p_align = 1; | 716 | const p_align = 1; |
| ... | @@ -787,24 +719,27 @@ pub fn populateMissingMetadata(self: *Elf) !void { | ... | @@ -787,24 +719,27 @@ pub fn populateMissingMetadata(self: *Elf) !void { |
| 787 | off, | 719 | off, |
| 788 | off + file_size_hint, | 720 | off + file_size_hint, |
| 789 | }); | 721 | }); |
| 790 | try self.sections.append(self.base.allocator, .{ | 722 | try self.sections.append(gpa, .{ |
| 791 | .sh_name = try self.makeString(".debug_info"), | 723 | .shdr = .{ |
| 792 | .sh_type = elf.SHT_PROGBITS, | 724 | .sh_name = try self.shstrtab.insert(gpa, ".debug_info"), |
| 793 | .sh_flags = 0, | 725 | .sh_type = elf.SHT_PROGBITS, |
| 794 | .sh_addr = 0, | 726 | .sh_flags = 0, |
| 795 | .sh_offset = off, | 727 | .sh_addr = 0, |
| 796 | .sh_size = file_size_hint, | 728 | .sh_offset = off, |
| 797 | .sh_link = 0, | 729 | .sh_size = file_size_hint, |
| 798 | .sh_info = 0, | 730 | .sh_link = 0, |
| 799 | .sh_addralign = p_align, | 731 | .sh_info = 0, |
| 800 | .sh_entsize = 0, | 732 | .sh_addralign = p_align, |
| 733 | .sh_entsize = 0, | ||
| 734 | }, | ||
| 735 | .phdr_index = undefined, | ||
| 801 | }); | 736 | }); |
| 802 | self.shdr_table_dirty = true; | 737 | self.shdr_table_dirty = true; |
| 803 | self.debug_info_header_dirty = true; | 738 | self.debug_info_header_dirty = true; |
| 804 | } | 739 | } |
| 805 | 740 | ||
| 806 | if (self.debug_abbrev_section_index == null) { | 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 | const file_size_hint = 128; | 744 | const file_size_hint = 128; |
| 810 | const p_align = 1; | 745 | const p_align = 1; |
| ... | @@ -813,24 +748,27 @@ pub fn populateMissingMetadata(self: *Elf) !void { | ... | @@ -813,24 +748,27 @@ pub fn populateMissingMetadata(self: *Elf) !void { |
| 813 | off, | 748 | off, |
| 814 | off + file_size_hint, | 749 | off + file_size_hint, |
| 815 | }); | 750 | }); |
| 816 | try self.sections.append(self.base.allocator, .{ | 751 | try self.sections.append(gpa, .{ |
| 817 | .sh_name = try self.makeString(".debug_abbrev"), | 752 | .shdr = .{ |
| 818 | .sh_type = elf.SHT_PROGBITS, | 753 | .sh_name = try self.shstrtab.insert(gpa, ".debug_abbrev"), |
| 819 | .sh_flags = 0, | 754 | .sh_type = elf.SHT_PROGBITS, |
| 820 | .sh_addr = 0, | 755 | .sh_flags = 0, |
| 821 | .sh_offset = off, | 756 | .sh_addr = 0, |
| 822 | .sh_size = file_size_hint, | 757 | .sh_offset = off, |
| 823 | .sh_link = 0, | 758 | .sh_size = file_size_hint, |
| 824 | .sh_info = 0, | 759 | .sh_link = 0, |
| 825 | .sh_addralign = p_align, | 760 | .sh_info = 0, |
| 826 | .sh_entsize = 0, | 761 | .sh_addralign = p_align, |
| 762 | .sh_entsize = 0, | ||
| 763 | }, | ||
| 764 | .phdr_index = undefined, | ||
| 827 | }); | 765 | }); |
| 828 | self.shdr_table_dirty = true; | 766 | self.shdr_table_dirty = true; |
| 829 | self.debug_abbrev_section_dirty = true; | 767 | self.debug_abbrev_section_dirty = true; |
| 830 | } | 768 | } |
| 831 | 769 | ||
| 832 | if (self.debug_aranges_section_index == null) { | 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 | const file_size_hint = 160; | 773 | const file_size_hint = 160; |
| 836 | const p_align = 16; | 774 | const p_align = 16; |
| ... | @@ -839,24 +777,27 @@ pub fn populateMissingMetadata(self: *Elf) !void { | ... | @@ -839,24 +777,27 @@ pub fn populateMissingMetadata(self: *Elf) !void { |
| 839 | off, | 777 | off, |
| 840 | off + file_size_hint, | 778 | off + file_size_hint, |
| 841 | }); | 779 | }); |
| 842 | try self.sections.append(self.base.allocator, .{ | 780 | try self.sections.append(gpa, .{ |
| 843 | .sh_name = try self.makeString(".debug_aranges"), | 781 | .shdr = .{ |
| 844 | .sh_type = elf.SHT_PROGBITS, | 782 | .sh_name = try self.shstrtab.insert(gpa, ".debug_aranges"), |
| 845 | .sh_flags = 0, | 783 | .sh_type = elf.SHT_PROGBITS, |
| 846 | .sh_addr = 0, | 784 | .sh_flags = 0, |
| 847 | .sh_offset = off, | 785 | .sh_addr = 0, |
| 848 | .sh_size = file_size_hint, | 786 | .sh_offset = off, |
| 849 | .sh_link = 0, | 787 | .sh_size = file_size_hint, |
| 850 | .sh_info = 0, | 788 | .sh_link = 0, |
| 851 | .sh_addralign = p_align, | 789 | .sh_info = 0, |
| 852 | .sh_entsize = 0, | 790 | .sh_addralign = p_align, |
| 791 | .sh_entsize = 0, | ||
| 792 | }, | ||
| 793 | .phdr_index = undefined, | ||
| 853 | }); | 794 | }); |
| 854 | self.shdr_table_dirty = true; | 795 | self.shdr_table_dirty = true; |
| 855 | self.debug_aranges_section_dirty = true; | 796 | self.debug_aranges_section_dirty = true; |
| 856 | } | 797 | } |
| 857 | 798 | ||
| 858 | if (self.debug_line_section_index == null) { | 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 | const file_size_hint = 250; | 802 | const file_size_hint = 250; |
| 862 | const p_align = 1; | 803 | const p_align = 1; |
| ... | @@ -865,17 +806,20 @@ pub fn populateMissingMetadata(self: *Elf) !void { | ... | @@ -865,17 +806,20 @@ pub fn populateMissingMetadata(self: *Elf) !void { |
| 865 | off, | 806 | off, |
| 866 | off + file_size_hint, | 807 | off + file_size_hint, |
| 867 | }); | 808 | }); |
| 868 | try self.sections.append(self.base.allocator, .{ | 809 | try self.sections.append(gpa, .{ |
| 869 | .sh_name = try self.makeString(".debug_line"), | 810 | .shdr = .{ |
| 870 | .sh_type = elf.SHT_PROGBITS, | 811 | .sh_name = try self.shstrtab.insert(gpa, ".debug_line"), |
| 871 | .sh_flags = 0, | 812 | .sh_type = elf.SHT_PROGBITS, |
| 872 | .sh_addr = 0, | 813 | .sh_flags = 0, |
| 873 | .sh_offset = off, | 814 | .sh_addr = 0, |
| 874 | .sh_size = file_size_hint, | 815 | .sh_offset = off, |
| 875 | .sh_link = 0, | 816 | .sh_size = file_size_hint, |
| 876 | .sh_info = 0, | 817 | .sh_link = 0, |
| 877 | .sh_addralign = p_align, | 818 | .sh_info = 0, |
| 878 | .sh_entsize = 0, | 819 | .sh_addralign = p_align, |
| 820 | .sh_entsize = 0, | ||
| 821 | }, | ||
| 822 | .phdr_index = undefined, | ||
| 879 | }); | 823 | }); |
| 880 | self.shdr_table_dirty = true; | 824 | self.shdr_table_dirty = true; |
| 881 | self.debug_line_header_dirty = true; | 825 | self.debug_line_header_dirty = true; |
| ... | @@ -891,7 +835,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { | ... | @@ -891,7 +835,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { |
| 891 | .p64 => @alignOf(elf.Elf64_Shdr), | 835 | .p64 => @alignOf(elf.Elf64_Shdr), |
| 892 | }; | 836 | }; |
| 893 | if (self.shdr_table_offset == null) { | 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 | self.shdr_table_dirty = true; | 839 | self.shdr_table_dirty = true; |
| 896 | } | 840 | } |
| 897 | 841 | ||
| ... | @@ -922,7 +866,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { | ... | @@ -922,7 +866,7 @@ pub fn populateMissingMetadata(self: *Elf) !void { |
| 922 | // offset + it's filesize. | 866 | // offset + it's filesize. |
| 923 | var max_file_offset: u64 = 0; | 867 | var max_file_offset: u64 = 0; |
| 924 | 868 | ||
| 925 | for (self.sections.items) |shdr| { | 869 | for (self.sections.items(.shdr)) |shdr| { |
| 926 | if (shdr.sh_offset + shdr.sh_size > max_file_offset) { | 870 | if (shdr.sh_offset + shdr.sh_size > max_file_offset) { |
| 927 | max_file_offset = shdr.sh_offset + shdr.sh_size; | 871 | max_file_offset = shdr.sh_offset + shdr.sh_size; |
| 928 | } | 872 | } |
| ... | @@ -932,24 +876,27 @@ pub fn populateMissingMetadata(self: *Elf) !void { | ... | @@ -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 | // TODO Also detect virtual address collisions. | 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 | const phdr = &self.program_headers.items[phdr_index]; | 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 | if (needed_size > self.allocatedSize(shdr.sh_offset)) { | 886 | if (needed_size > self.allocatedSize(shdr.sh_offset)) { |
| 941 | // Must move the entire section. | 887 | // Must move the entire section. |
| 942 | const new_offset = self.findFreeSpace(needed_size, self.page_size); | 888 | const new_offset = self.findFreeSpace(needed_size, self.page_size); |
| 943 | const existing_size = if (self.atoms.get(phdr_index)) |last| blk: { | 889 | const existing_size = if (maybe_last_atom_index) |last_atom_index| blk: { |
| 944 | const sym = self.local_symbols.items[last.local_sym_index]; | 890 | const last = self.getAtom(last_atom_index); |
| 891 | const sym = last.getSymbol(self); | ||
| 945 | break :blk (sym.st_value + sym.st_size) - phdr.p_vaddr; | 892 | break :blk (sym.st_value + sym.st_size) - phdr.p_vaddr; |
| 946 | } else if (shdr_index == self.got_section_index.?) blk: { | 893 | } else if (shdr_index == self.got_section_index.?) blk: { |
| 947 | break :blk shdr.sh_size; | 894 | break :blk shdr.sh_size; |
| 948 | } else 0; | 895 | } else 0; |
| 949 | shdr.sh_size = 0; | 896 | shdr.sh_size = 0; |
| 950 | 897 | ||
| 951 | log.debug("new '{s}' file offset 0x{x} to 0x{x}", .{ | 898 | log.debug("new '{?s}' file offset 0x{x} to 0x{x}", .{ |
| 952 | self.getString(shdr.sh_name), | 899 | self.shstrtab.get(shdr.sh_name), |
| 953 | new_offset, | 900 | new_offset, |
| 954 | new_offset + existing_size, | 901 | new_offset + existing_size, |
| 955 | }); | 902 | }); |
| ... | @@ -975,7 +922,7 @@ pub fn growNonAllocSection( | ... | @@ -975,7 +922,7 @@ pub fn growNonAllocSection( |
| 975 | min_alignment: u32, | 922 | min_alignment: u32, |
| 976 | requires_file_copy: bool, | 923 | requires_file_copy: bool, |
| 977 | ) !void { | 924 | ) !void { |
| 978 | const shdr = &self.sections.items[shdr_index]; | 925 | const shdr = &self.sections.items(.shdr)[shdr_index]; |
| 979 | 926 | ||
| 980 | if (needed_size > self.allocatedSize(shdr.sh_offset)) { | 927 | if (needed_size > self.allocatedSize(shdr.sh_offset)) { |
| 981 | const existing_size = if (self.symtab_section_index.? == shdr_index) blk: { | 928 | const existing_size = if (self.symtab_section_index.? == shdr_index) blk: { |
| ... | @@ -988,7 +935,7 @@ pub fn growNonAllocSection( | ... | @@ -988,7 +935,7 @@ pub fn growNonAllocSection( |
| 988 | shdr.sh_size = 0; | 935 | shdr.sh_size = 0; |
| 989 | // Move all the symbols to a new file location. | 936 | // Move all the symbols to a new file location. |
| 990 | const new_offset = self.findFreeSpace(needed_size, min_alignment); | 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 | if (requires_file_copy) { | 940 | if (requires_file_copy) { |
| 994 | const amt = try self.base.file.?.copyRangeAll( | 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,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 | var sub_prog_node = prog_node.start("ELF Flush", 0); | 1010 | var sub_prog_node = prog_node.start("ELF Flush", 0); |
| 1063 | sub_prog_node.activate(); | 1011 | sub_prog_node.activate(); |
| 1064 | defer sub_prog_node.end(); | 1012 | defer sub_prog_node.end(); |
| ... | @@ -1077,12 +1025,13 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node | ... | @@ -1077,12 +1025,13 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node |
| 1077 | { | 1025 | { |
| 1078 | var it = self.relocs.iterator(); | 1026 | var it = self.relocs.iterator(); |
| 1079 | while (it.next()) |entry| { | 1027 | while (it.next()) |entry| { |
| 1080 | const atom = entry.key_ptr.*; | 1028 | const atom_index = entry.key_ptr.*; |
| 1081 | const relocs = entry.value_ptr.*; | 1029 | const relocs = entry.value_ptr.*; |
| 1082 | const source_sym = self.local_symbols.items[atom.local_sym_index]; | 1030 | const atom = self.getAtom(atom_index); |
| 1083 | const source_shdr = self.sections.items[source_sym.st_shndx]; | 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 | for (relocs.items) |*reloc| { | 1036 | for (relocs.items) |*reloc| { |
| 1088 | const target_sym = self.local_symbols.items[reloc.target]; | 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,10 +1042,10 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node |
| 1093 | const section_offset = (source_sym.st_value + reloc.offset) - source_shdr.sh_addr; | 1042 | const section_offset = (source_sym.st_value + reloc.offset) - source_shdr.sh_addr; |
| 1094 | const file_offset = source_shdr.sh_offset + section_offset; | 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 | reloc.offset, | 1046 | reloc.offset, |
| 1098 | target_vaddr, | 1047 | target_vaddr, |
| 1099 | self.getString(target_sym.st_name), | 1048 | self.shstrtab.get(target_sym.st_name), |
| 1100 | }); | 1049 | }); |
| 1101 | 1050 | ||
| 1102 | switch (self.ptr_width) { | 1051 | switch (self.ptr_width) { |
| ... | @@ -1174,8 +1123,8 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node | ... | @@ -1174,8 +1123,8 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node |
| 1174 | 1123 | ||
| 1175 | switch (self.ptr_width) { | 1124 | switch (self.ptr_width) { |
| 1176 | .p32 => { | 1125 | .p32 => { |
| 1177 | const buf = try self.base.allocator.alloc(elf.Elf32_Phdr, self.program_headers.items.len); | 1126 | const buf = try gpa.alloc(elf.Elf32_Phdr, self.program_headers.items.len); |
| 1178 | defer self.base.allocator.free(buf); | 1127 | defer gpa.free(buf); |
| 1179 | 1128 | ||
| 1180 | for (buf) |*phdr, i| { | 1129 | for (buf) |*phdr, i| { |
| 1181 | phdr.* = progHeaderTo32(self.program_headers.items[i]); | 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,8 +1135,8 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node |
| 1186 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?); | 1135 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?); |
| 1187 | }, | 1136 | }, |
| 1188 | .p64 => { | 1137 | .p64 => { |
| 1189 | const buf = try self.base.allocator.alloc(elf.Elf64_Phdr, self.program_headers.items.len); | 1138 | const buf = try gpa.alloc(elf.Elf64_Phdr, self.program_headers.items.len); |
| 1190 | defer self.base.allocator.free(buf); | 1139 | defer gpa.free(buf); |
| 1191 | 1140 | ||
| 1192 | for (buf) |*phdr, i| { | 1141 | for (buf) |*phdr, i| { |
| 1193 | phdr.* = self.program_headers.items[i]; | 1142 | phdr.* = self.program_headers.items[i]; |
| ... | @@ -1203,20 +1152,20 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node | ... | @@ -1203,20 +1152,20 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node |
| 1203 | 1152 | ||
| 1204 | { | 1153 | { |
| 1205 | const shdr_index = self.shstrtab_index.?; | 1154 | const shdr_index = self.shstrtab_index.?; |
| 1206 | if (self.shstrtab_dirty or self.shstrtab.items.len != self.sections.items[shdr_index].sh_size) { | 1155 | if (self.shstrtab_dirty or self.shstrtab.buffer.items.len != self.sections.items(.shdr)[shdr_index].sh_size) { |
| 1207 | try self.growNonAllocSection(shdr_index, self.shstrtab.items.len, 1, false); | 1156 | try self.growNonAllocSection(shdr_index, self.shstrtab.buffer.items.len, 1, false); |
| 1208 | const shstrtab_sect = self.sections.items[shdr_index]; | 1157 | const shstrtab_sect = self.sections.items(.shdr)[shdr_index]; |
| 1209 | try self.base.file.?.pwriteAll(self.shstrtab.items, shstrtab_sect.sh_offset); | 1158 | try self.base.file.?.pwriteAll(self.shstrtab.buffer.items, shstrtab_sect.sh_offset); |
| 1210 | self.shstrtab_dirty = false; | 1159 | self.shstrtab_dirty = false; |
| 1211 | } | 1160 | } |
| 1212 | } | 1161 | } |
| 1213 | 1162 | ||
| 1214 | if (self.dwarf) |dwarf| { | 1163 | if (self.dwarf) |dwarf| { |
| 1215 | const shdr_index = self.debug_str_section_index.?; | 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) { | 1165 | if (self.debug_strtab_dirty or dwarf.strtab.buffer.items.len != self.sections.items(.shdr)[shdr_index].sh_size) { |
| 1217 | try self.growNonAllocSection(shdr_index, dwarf.strtab.items.len, 1, false); | 1166 | try self.growNonAllocSection(shdr_index, dwarf.strtab.buffer.items.len, 1, false); |
| 1218 | const debug_strtab_sect = self.sections.items[shdr_index]; | 1167 | const debug_strtab_sect = self.sections.items(.shdr)[shdr_index]; |
| 1219 | try self.base.file.?.pwriteAll(dwarf.strtab.items, debug_strtab_sect.sh_offset); | 1168 | try self.base.file.?.pwriteAll(dwarf.strtab.buffer.items, debug_strtab_sect.sh_offset); |
| 1220 | self.debug_strtab_dirty = false; | 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,7 +1180,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node |
| 1231 | .p64 => @alignOf(elf.Elf64_Shdr), | 1180 | .p64 => @alignOf(elf.Elf64_Shdr), |
| 1232 | }; | 1181 | }; |
| 1233 | const allocated_size = self.allocatedSize(self.shdr_table_offset.?); | 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 | if (needed_size > allocated_size) { | 1185 | if (needed_size > allocated_size) { |
| 1237 | self.shdr_table_offset = null; // free the space | 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,12 +1189,13 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node |
| 1240 | 1189 | ||
| 1241 | switch (self.ptr_width) { | 1190 | switch (self.ptr_width) { |
| 1242 | .p32 => { | 1191 | .p32 => { |
| 1243 | const buf = try self.base.allocator.alloc(elf.Elf32_Shdr, self.sections.items.len); | 1192 | const slice = self.sections.slice(); |
| 1244 | defer self.base.allocator.free(buf); | 1193 | const buf = try gpa.alloc(elf.Elf32_Shdr, slice.len); |
| 1194 | defer gpa.free(buf); | ||
| 1245 | 1195 | ||
| 1246 | for (buf) |*shdr, i| { | 1196 | for (buf) |*shdr, i| { |
| 1247 | shdr.* = sectHeaderTo32(self.sections.items[i]); | 1197 | shdr.* = sectHeaderTo32(slice.items(.shdr)[i]); |
| 1248 | log.debug("writing section {s}: {}", .{ self.getString(shdr.sh_name), shdr.* }); | 1198 | log.debug("writing section {?s}: {}", .{ self.shstrtab.get(shdr.sh_name), shdr.* }); |
| 1249 | if (foreign_endian) { | 1199 | if (foreign_endian) { |
| 1250 | mem.byteSwapAllFields(elf.Elf32_Shdr, shdr); | 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,12 +1203,13 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node |
| 1253 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?); | 1203 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?); |
| 1254 | }, | 1204 | }, |
| 1255 | .p64 => { | 1205 | .p64 => { |
| 1256 | const buf = try self.base.allocator.alloc(elf.Elf64_Shdr, self.sections.items.len); | 1206 | const slice = self.sections.slice(); |
| 1257 | defer self.base.allocator.free(buf); | 1207 | const buf = try gpa.alloc(elf.Elf64_Shdr, slice.len); |
| 1208 | defer gpa.free(buf); | ||
| 1258 | 1209 | ||
| 1259 | for (buf) |*shdr, i| { | 1210 | for (buf) |*shdr, i| { |
| 1260 | shdr.* = self.sections.items[i]; | 1211 | shdr.* = slice.items(.shdr)[i]; |
| 1261 | log.debug("writing section {s}: {}", .{ self.getString(shdr.sh_name), shdr.* }); | 1212 | log.debug("writing section {?s}: {}", .{ self.shstrtab.get(shdr.sh_name), shdr.* }); |
| 1262 | if (foreign_endian) { | 1213 | if (foreign_endian) { |
| 1263 | mem.byteSwapAllFields(elf.Elf64_Shdr, shdr); | 1214 | mem.byteSwapAllFields(elf.Elf64_Shdr, shdr); |
| 1264 | } | 1215 | } |
| ... | @@ -2069,7 +2020,7 @@ fn writeElfHeader(self: *Elf) !void { | ... | @@ -2069,7 +2020,7 @@ fn writeElfHeader(self: *Elf) !void { |
| 2069 | mem.writeInt(u16, hdr_buf[index..][0..2], e_shentsize, endian); | 2020 | mem.writeInt(u16, hdr_buf[index..][0..2], e_shentsize, endian); |
| 2070 | index += 2; | 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 | mem.writeInt(u16, hdr_buf[index..][0..2], e_shnum, endian); | 2024 | mem.writeInt(u16, hdr_buf[index..][0..2], e_shnum, endian); |
| 2074 | index += 2; | 2025 | index += 2; |
| 2075 | 2026 | ||
| ... | @@ -2081,113 +2032,145 @@ fn writeElfHeader(self: *Elf) !void { | ... | @@ -2081,113 +2032,145 @@ fn writeElfHeader(self: *Elf) !void { |
| 2081 | try self.base.file.?.pwriteAll(hdr_buf[0..index], 0); | 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 { | 2035 | fn freeAtom(self: *Elf, atom_index: Atom.Index) void { |
| 2085 | const local_sym = self.local_symbols.items[text_block.local_sym_index]; | 2036 | const atom = self.getAtom(atom_index); |
| 2086 | const name_str_index = local_sym.st_name; | 2037 | log.debug("freeAtom {d} ({s})", .{ atom_index, atom.getName(self) }); |
| 2087 | const name = self.getString(name_str_index); | ||
| 2088 | log.debug("freeTextBlock {*} ({s})", .{ text_block, name }); | ||
| 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 | var already_have_free_list_node = false; | 2044 | var already_have_free_list_node = false; |
| 2092 | { | 2045 | { |
| 2093 | var i: usize = 0; | 2046 | var i: usize = 0; |
| 2094 | // TODO turn free_list into a hash map | 2047 | // TODO turn free_list into a hash map |
| 2095 | while (i < free_list.items.len) { | 2048 | while (i < free_list.items.len) { |
| 2096 | if (free_list.items[i] == text_block) { | 2049 | if (free_list.items[i] == atom_index) { |
| 2097 | _ = free_list.swapRemove(i); | 2050 | _ = free_list.swapRemove(i); |
| 2098 | continue; | 2051 | continue; |
| 2099 | } | 2052 | } |
| 2100 | if (free_list.items[i] == text_block.prev) { | 2053 | if (free_list.items[i] == atom.prev_index) { |
| 2101 | already_have_free_list_node = true; | 2054 | already_have_free_list_node = true; |
| 2102 | } | 2055 | } |
| 2103 | i += 1; | 2056 | i += 1; |
| 2104 | } | 2057 | } |
| 2105 | } | 2058 | } |
| 2106 | 2059 | ||
| 2107 | if (self.atoms.getPtr(phdr_index)) |last_block| { | 2060 | const maybe_last_atom_index = &self.sections.items(.last_atom_index)[shndx]; |
| 2108 | if (last_block.* == text_block) { | 2061 | if (maybe_last_atom_index.*) |last_atom_index| { |
| 2109 | if (text_block.prev) |prev| { | 2062 | if (last_atom_index == atom_index) { |
| 2063 | if (atom.prev_index) |prev_index| { | ||
| 2110 | // TODO shrink the section size here | 2064 | // TODO shrink the section size here |
| 2111 | last_block.* = prev; | 2065 | maybe_last_atom_index.* = prev_index; |
| 2112 | } else { | 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| { | 2072 | if (atom.prev_index) |prev_index| { |
| 2119 | prev.next = text_block.next; | 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 | // The free list is heuristics, it doesn't have to be perfect, so we can | 2077 | // The free list is heuristics, it doesn't have to be perfect, so we can |
| 2123 | // ignore the OOM here. | 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 | } else { | 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| { | 2085 | if (atom.next_index) |next_index| { |
| 2131 | next.prev = text_block.prev; | 2086 | self.getAtomPtr(next_index).prev_index = atom.prev_index; |
| 2132 | } else { | 2087 | } else { |
| 2133 | text_block.next = null; | 2088 | self.getAtomPtr(atom_index).next_index = null; |
| 2134 | } | 2089 | } |
| 2135 | 2090 | ||
| 2136 | if (self.dwarf) |*dw| { | 2091 | // Appending to free lists is allowed to fail because the free lists are heuristics based anyway. |
| 2137 | dw.freeAtom(&text_block.dbg_info_atom); | 2092 | const local_sym_index = atom.getSymbolIndex().?; |
| 2138 | } | 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 | _ = self; | 2104 | _ = self; |
| 2143 | _ = text_block; | 2105 | _ = atom_index; |
| 2144 | _ = new_block_size; | 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 { | 2109 | fn growAtom(self: *Elf, atom_index: Atom.Index, new_block_size: u64, alignment: u64) !u64 { |
| 2149 | const sym = self.local_symbols.items[text_block.local_sym_index]; | 2110 | const atom = self.getAtom(atom_index); |
| 2111 | const sym = atom.getSymbol(self); | ||
| 2150 | const align_ok = mem.alignBackwardGeneric(u64, sym.st_value, alignment) == sym.st_value; | 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 | if (!need_realloc) return sym.st_value; | 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 { | 2118 | pub fn createAtom(self: *Elf) !Atom.Index { |
| 2157 | const shdr_index = self.phdr_shdr_table.get(phdr_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 | const phdr = &self.program_headers.items[phdr_index]; | 2139 | const phdr = &self.program_headers.items[phdr_index]; |
| 2159 | const shdr = &self.sections.items[shdr_index]; | 2140 | const shdr = &self.sections.items(.shdr)[sym.st_shndx]; |
| 2160 | const new_block_ideal_capacity = padToIdeal(new_block_size); | 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 | // and possibly removing a free list node. | 2146 | // and possibly removing a free list node. |
| 2164 | // It would be simpler to do it inside the for loop below, but that would cause a | 2147 | // It would be simpler to do it inside the for loop below, but that would cause a |
| 2165 | // problem if an error was returned later in the function. So this action | 2148 | // problem if an error was returned later in the function. So this action |
| 2166 | // is actually carried out at the end of the function, when errors are no longer possible. | 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 | var free_list_removal: ?usize = null; | 2151 | var free_list_removal: ?usize = null; |
| 2169 | var free_list = self.atom_free_lists.get(phdr_index).?; | ||
| 2170 | 2152 | ||
| 2171 | // First we look for an appropriately sized free list node. | 2153 | // First we look for an appropriately sized free list node. |
| 2172 | // The list is unordered. We'll just take the first thing that works. | 2154 | // The list is unordered. We'll just take the first thing that works. |
| 2173 | const vaddr = blk: { | 2155 | const vaddr = blk: { |
| 2174 | var i: usize = 0; | 2156 | var i: usize = 0; |
| 2175 | while (i < free_list.items.len) { | 2157 | while (i < free_list.items.len) { |
| 2176 | const big_block = free_list.items[i]; | 2158 | const big_atom_index = free_list.items[i]; |
| 2177 | // We now have a pointer to a live text block that has too much capacity. | 2159 | const big_atom = self.getAtom(big_atom_index); |
| 2178 | // Is it enough that we could fit this new text block? | 2160 | // We now have a pointer to a live atom that has too much capacity. |
| 2179 | const sym = self.local_symbols.items[big_block.local_sym_index]; | 2161 | // Is it enough that we could fit this new atom? |
| 2180 | const capacity = big_block.capacity(self.*); | 2162 | const big_atom_sym = big_atom.getSymbol(self); |
| 2163 | const capacity = big_atom.capacity(self); | ||
| 2181 | const ideal_capacity = padToIdeal(capacity); | 2164 | const ideal_capacity = padToIdeal(capacity); |
| 2182 | const ideal_capacity_end_vaddr = std.math.add(u64, sym.st_value, ideal_capacity) catch ideal_capacity; | 2165 | const ideal_capacity_end_vaddr = std.math.add(u64, big_atom_sym.st_value, ideal_capacity) catch ideal_capacity; |
| 2183 | const capacity_end_vaddr = sym.st_value + capacity; | 2166 | const capacity_end_vaddr = big_atom_sym.st_value + capacity; |
| 2184 | const new_start_vaddr_unaligned = capacity_end_vaddr - new_block_ideal_capacity; | 2167 | const new_start_vaddr_unaligned = capacity_end_vaddr - new_atom_ideal_capacity; |
| 2185 | const new_start_vaddr = mem.alignBackwardGeneric(u64, new_start_vaddr_unaligned, alignment); | 2168 | const new_start_vaddr = mem.alignBackwardGeneric(u64, new_start_vaddr_unaligned, alignment); |
| 2186 | if (new_start_vaddr < ideal_capacity_end_vaddr) { | 2169 | if (new_start_vaddr < ideal_capacity_end_vaddr) { |
| 2187 | // Additional bookkeeping here to notice if this free list node | 2170 | // Additional bookkeeping here to notice if this free list node |
| 2188 | // should be deleted because the block that it points to has grown to take up | 2171 | // should be deleted because the block that it points to has grown to take up |
| 2189 | // more of the extra capacity. | 2172 | // more of the extra capacity. |
| 2190 | if (!big_block.freeListEligible(self.*)) { | 2173 | if (!big_atom.freeListEligible(self)) { |
| 2191 | _ = free_list.swapRemove(i); | 2174 | _ = free_list.swapRemove(i); |
| 2192 | } else { | 2175 | } else { |
| 2193 | i += 1; | 2176 | i += 1; |
| ... | @@ -2201,29 +2184,33 @@ fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, al | ... | @@ -2201,29 +2184,33 @@ fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, al |
| 2201 | const keep_free_list_node = remaining_capacity >= min_text_capacity; | 2184 | const keep_free_list_node = remaining_capacity >= min_text_capacity; |
| 2202 | 2185 | ||
| 2203 | // Set up the metadata to be updated, after errors are no longer possible. | 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 | if (!keep_free_list_node) { | 2188 | if (!keep_free_list_node) { |
| 2206 | free_list_removal = i; | 2189 | free_list_removal = i; |
| 2207 | } | 2190 | } |
| 2208 | break :blk new_start_vaddr; | 2191 | break :blk new_start_vaddr; |
| 2209 | } else if (self.atoms.get(phdr_index)) |last| { | 2192 | } else if (maybe_last_atom_index.*) |last_index| { |
| 2210 | const sym = self.local_symbols.items[last.local_sym_index]; | 2193 | const last = self.getAtom(last_index); |
| 2211 | const ideal_capacity = padToIdeal(sym.st_size); | 2194 | const last_sym = last.getSymbol(self); |
| 2212 | const ideal_capacity_end_vaddr = sym.st_value + ideal_capacity; | 2195 | const ideal_capacity = padToIdeal(last_sym.st_size); |
| 2196 | const ideal_capacity_end_vaddr = last_sym.st_value + ideal_capacity; | ||
| 2213 | const new_start_vaddr = mem.alignForwardGeneric(u64, ideal_capacity_end_vaddr, alignment); | 2197 | const new_start_vaddr = mem.alignForwardGeneric(u64, ideal_capacity_end_vaddr, alignment); |
| 2214 | // Set up the metadata to be updated, after errors are no longer possible. | 2198 | // Set up the metadata to be updated, after errors are no longer possible. |
| 2215 | block_placement = last; | 2199 | atom_placement = last_index; |
| 2216 | break :blk new_start_vaddr; | 2200 | break :blk new_start_vaddr; |
| 2217 | } else { | 2201 | } else { |
| 2218 | break :blk phdr.p_vaddr; | 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; | 2206 | const expand_section = if (atom_placement) |placement_index| |
| 2223 | if (expand_text_section) { | 2207 | self.getAtom(placement_index).next_index == null |
| 2208 | else | ||
| 2209 | true; | ||
| 2210 | if (expand_section) { | ||
| 2224 | const needed_size = (vaddr + new_block_size) - phdr.p_vaddr; | 2211 | const needed_size = (vaddr + new_block_size) - phdr.p_vaddr; |
| 2225 | try self.growAllocSection(shdr_index, phdr_index, needed_size); | 2212 | try self.growAllocSection(sym.st_shndx, needed_size); |
| 2226 | _ = try self.atoms.put(self.base.allocator, phdr_index, text_block); | 2213 | maybe_last_atom_index.* = atom_index; |
| 2227 | 2214 | ||
| 2228 | if (self.dwarf) |_| { | 2215 | if (self.dwarf) |_| { |
| 2229 | // The .debug_info section has `low_pc` and `high_pc` values which is the virtual address | 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,23 +2225,28 @@ fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, al |
| 2238 | } | 2225 | } |
| 2239 | shdr.sh_addralign = math.max(shdr.sh_addralign, alignment); | 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 | // In this case we need to "unplug" it from its previous location before | 2229 | // In this case we need to "unplug" it from its previous location before |
| 2243 | // plugging it in to its new location. | 2230 | // plugging it in to its new location. |
| 2244 | if (text_block.prev) |prev| { | 2231 | if (atom.prev_index) |prev_index| { |
| 2245 | prev.next = text_block.next; | 2232 | const prev = self.getAtomPtr(prev_index); |
| 2233 | prev.next_index = atom.next_index; | ||
| 2246 | } | 2234 | } |
| 2247 | if (text_block.next) |next| { | 2235 | if (atom.next_index) |next_index| { |
| 2248 | next.prev = text_block.prev; | 2236 | const next = self.getAtomPtr(next_index); |
| 2237 | next.prev_index = atom.prev_index; | ||
| 2249 | } | 2238 | } |
| 2250 | 2239 | ||
| 2251 | if (block_placement) |big_block| { | 2240 | if (atom_placement) |big_atom_index| { |
| 2252 | text_block.prev = big_block; | 2241 | const big_atom = self.getAtomPtr(big_atom_index); |
| 2253 | text_block.next = big_block.next; | 2242 | const atom_ptr = self.getAtomPtr(atom_index); |
| 2254 | big_block.next = text_block; | 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 | } else { | 2246 | } else { |
| 2256 | text_block.prev = null; | 2247 | const atom_ptr = self.getAtomPtr(atom_index); |
| 2257 | text_block.next = null; | 2248 | atom_ptr.prev_index = null; |
| 2249 | atom_ptr.next_index = null; | ||
| 2258 | } | 2250 | } |
| 2259 | if (free_list_removal) |i| { | 2251 | if (free_list_removal) |i| { |
| 2260 | _ = free_list.swapRemove(i); | 2252 | _ = free_list.swapRemove(i); |
| ... | @@ -2262,7 +2254,7 @@ fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, al | ... | @@ -2262,7 +2254,7 @@ fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, al |
| 2262 | return vaddr; | 2254 | return vaddr; |
| 2263 | } | 2255 | } |
| 2264 | 2256 | ||
| 2265 | fn allocateLocalSymbol(self: *Elf) !u32 { | 2257 | pub fn allocateLocalSymbol(self: *Elf) !u32 { |
| 2266 | try self.local_symbols.ensureUnusedCapacity(self.base.allocator, 1); | 2258 | try self.local_symbols.ensureUnusedCapacity(self.base.allocator, 1); |
| 2267 | 2259 | ||
| 2268 | const index = blk: { | 2260 | const index = blk: { |
| ... | @@ -2289,40 +2281,30 @@ fn allocateLocalSymbol(self: *Elf) !u32 { | ... | @@ -2289,40 +2281,30 @@ fn allocateLocalSymbol(self: *Elf) !u32 { |
| 2289 | return index; | 2281 | return index; |
| 2290 | } | 2282 | } |
| 2291 | 2283 | ||
| 2292 | pub fn allocateDeclIndexes(self: *Elf, decl_index: Module.Decl.Index) !void { | 2284 | pub fn allocateGotOffset(self: *Elf) !u32 { |
| 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 | |||
| 2299 | try self.offset_table.ensureUnusedCapacity(self.base.allocator, 1); | 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); | 2287 | const index = blk: { |
| 2303 | defer self.base.allocator.free(decl_name); | 2288 | if (self.offset_table_free_list.popOrNull()) |index| { |
| 2304 | 2289 | log.debug(" (reusing GOT offset at index {d})", .{index}); | |
| 2305 | log.debug("allocating symbol indexes for {s}", .{decl_name}); | 2290 | break :blk index; |
| 2306 | decl.link.elf.local_sym_index = try self.allocateLocalSymbol(); | 2291 | } else { |
| 2307 | try self.atom_by_index_table.putNoClobber(self.base.allocator, decl.link.elf.local_sym_index, &decl.link.elf); | 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| { | 2300 | self.offset_table.items[index] = 0; |
| 2310 | decl.link.elf.offset_table_index = i; | 2301 | return index; |
| 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; | ||
| 2317 | } | 2302 | } |
| 2318 | 2303 | ||
| 2319 | fn freeUnnamedConsts(self: *Elf, decl_index: Module.Decl.Index) void { | 2304 | fn freeUnnamedConsts(self: *Elf, decl_index: Module.Decl.Index) void { |
| 2320 | const unnamed_consts = self.unnamed_const_atoms.getPtr(decl_index) orelse return; | 2305 | const unnamed_consts = self.unnamed_const_atoms.getPtr(decl_index) orelse return; |
| 2321 | for (unnamed_consts.items) |atom| { | 2306 | for (unnamed_consts.items) |atom| { |
| 2322 | self.freeTextBlock(atom, self.phdr_load_ro_index.?); | 2307 | self.freeAtom(atom); |
| 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); | ||
| 2326 | } | 2308 | } |
| 2327 | unnamed_consts.clearAndFree(self.base.allocator); | 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,52 +2317,59 @@ pub fn freeDecl(self: *Elf, decl_index: Module.Decl.Index) void { |
| 2335 | const mod = self.base.options.module.?; | 2317 | const mod = self.base.options.module.?; |
| 2336 | const decl = mod.declPtr(decl_index); | 2318 | const decl = mod.declPtr(decl_index); |
| 2337 | 2319 | ||
| 2338 | const kv = self.decls.fetchRemove(decl_index); | 2320 | log.debug("freeDecl {*}", .{decl}); |
| 2339 | if (kv.?.value) |index| { | 2321 | |
| 2340 | self.freeTextBlock(&decl.link.elf, index); | 2322 | if (self.decls.fetchRemove(decl_index)) |const_kv| { |
| 2323 | var kv = const_kv; | ||
| 2324 | self.freeAtom(kv.value.atom); | ||
| 2341 | self.freeUnnamedConsts(decl_index); | 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. | 2329 | if (self.dwarf) |*dw| { |
| 2345 | if (decl.link.elf.local_sym_index != 0) { | 2330 | dw.freeDecl(decl_index); |
| 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 {}; | ||
| 2352 | } | 2331 | } |
| 2332 | } | ||
| 2353 | 2333 | ||
| 2354 | if (self.dwarf) |*dw| { | 2334 | pub fn getOrCreateAtomForDecl(self: *Elf, decl_index: Module.Decl.Index) !Atom.Index { |
| 2355 | dw.freeDecl(decl); | 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 | const ty = decl.ty; | 2348 | const ty = decl.ty; |
| 2361 | const zig_ty = ty.zigTypeTag(); | 2349 | const zig_ty = ty.zigTypeTag(); |
| 2362 | const val = decl.val; | 2350 | const val = decl.val; |
| 2363 | const phdr_index: u16 = blk: { | 2351 | const shdr_index: u16 = blk: { |
| 2364 | if (val.isUndefDeep()) { | 2352 | if (val.isUndefDeep()) { |
| 2365 | // TODO in release-fast and release-small, we should put undef in .bss | 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 | switch (zig_ty) { | 2357 | switch (zig_ty) { |
| 2370 | // TODO: what if this is a function pointer? | 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 | else => { | 2360 | else => { |
| 2373 | if (val.castTag(.variable)) |_| { | 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 | fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, stt_bits: u8) !*elf.Elf64_Sym { | 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 | const mod = self.base.options.module.?; | 2373 | const mod = self.base.options.module.?; |
| 2385 | const decl = mod.declPtr(decl_index); | 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,61 +2379,65 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s |
| 2390 | log.debug("updateDeclCode {s}{*}", .{ decl_name, decl }); | 2379 | log.debug("updateDeclCode {s}{*}", .{ decl_name, decl }); |
| 2391 | const required_alignment = decl.getAlignment(self.base.options.target); | 2380 | const required_alignment = decl.getAlignment(self.base.options.target); |
| 2392 | 2381 | ||
| 2393 | const decl_ptr = self.decls.getPtr(decl_index).?; | 2382 | const decl_metadata = self.decls.get(decl_index).?; |
| 2394 | if (decl_ptr.* == null) { | 2383 | const atom_index = decl_metadata.atom; |
| 2395 | decl_ptr.* = try self.getDeclPhdrIndex(decl); | 2384 | const atom = self.getAtom(atom_index); |
| 2396 | } | ||
| 2397 | const phdr_index = decl_ptr.*.?; | ||
| 2398 | const shdr_index = self.phdr_shdr_table.get(phdr_index).?; | ||
| 2399 | 2385 | ||
| 2400 | assert(decl.link.elf.local_sym_index != 0); // Caller forgot to allocateDeclIndexes() | 2386 | const shdr_index = decl_metadata.shdr; |
| 2401 | const local_sym = &self.local_symbols.items[decl.link.elf.local_sym_index]; | 2387 | if (atom.getSymbol(self).st_size != 0) { |
| 2402 | if (local_sym.st_size != 0) { | 2388 | const local_sym = atom.getSymbolPtr(self); |
| 2403 | const capacity = decl.link.elf.capacity(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 | const need_realloc = code.len > capacity or | 2395 | const need_realloc = code.len > capacity or |
| 2405 | !mem.isAlignedGeneric(u64, local_sym.st_value, required_alignment); | 2396 | !mem.isAlignedGeneric(u64, local_sym.st_value, required_alignment); |
| 2397 | |||
| 2406 | if (need_realloc) { | 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 | log.debug("growing {s} from 0x{x} to 0x{x}", .{ decl_name, local_sym.st_value, vaddr }); | 2400 | log.debug("growing {s} from 0x{x} to 0x{x}", .{ decl_name, local_sym.st_value, vaddr }); |
| 2409 | if (vaddr != local_sym.st_value) { | 2401 | if (vaddr != local_sym.st_value) { |
| 2410 | local_sym.st_value = vaddr; | 2402 | local_sym.st_value = vaddr; |
| 2411 | 2403 | ||
| 2412 | log.debug(" (writing new offset table entry)", .{}); | 2404 | log.debug(" (writing new offset table entry)", .{}); |
| 2413 | self.offset_table.items[decl.link.elf.offset_table_index] = vaddr; | 2405 | self.offset_table.items[atom.offset_table_index] = vaddr; |
| 2414 | try self.writeOffsetTableEntry(decl.link.elf.offset_table_index); | 2406 | try self.writeOffsetTableEntry(atom.offset_table_index); |
| 2415 | } | 2407 | } |
| 2416 | } else if (code.len < local_sym.st_size) { | 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 | local_sym.st_size = code.len; | 2411 | local_sym.st_size = code.len; |
| 2420 | local_sym.st_name = try self.updateString(local_sym.st_name, decl_name); | 2412 | |
| 2421 | local_sym.st_info = (elf.STB_LOCAL << 4) | stt_bits; | ||
| 2422 | local_sym.st_other = 0; | ||
| 2423 | local_sym.st_shndx = shdr_index; | ||
| 2424 | // TODO this write could be avoided if no fields of the symbol were changed. | 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 | } else { | 2415 | } else { |
| 2427 | const name_str_index = try self.makeString(decl_name); | 2416 | const local_sym = atom.getSymbolPtr(self); |
| 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 | |||
| 2432 | local_sym.* = .{ | 2417 | local_sym.* = .{ |
| 2433 | .st_name = name_str_index, | 2418 | .st_name = try self.shstrtab.insert(gpa, decl_name), |
| 2434 | .st_info = (elf.STB_LOCAL << 4) | stt_bits, | 2419 | .st_info = (elf.STB_LOCAL << 4) | stt_bits, |
| 2435 | .st_other = 0, | 2420 | .st_other = 0, |
| 2436 | .st_shndx = shdr_index, | 2421 | .st_shndx = shdr_index, |
| 2437 | .st_value = vaddr, | 2422 | .st_value = 0, |
| 2438 | .st_size = code.len, | 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); | 2433 | try self.writeSymbol(atom.getSymbolIndex().?); |
| 2443 | try self.writeOffsetTableEntry(decl.link.elf.offset_table_index); | 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 | const section_offset = local_sym.st_value - self.program_headers.items[phdr_index].p_vaddr; | 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 | try self.base.file.?.pwriteAll(code, file_offset); | 2441 | try self.base.file.?.pwriteAll(code, file_offset); |
| 2449 | 2442 | ||
| 2450 | return local_sym; | 2443 | return local_sym; |
| ... | @@ -2461,12 +2454,15 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven | ... | @@ -2461,12 +2454,15 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven |
| 2461 | const tracy = trace(@src()); | 2454 | const tracy = trace(@src()); |
| 2462 | defer tracy.end(); | 2455 | defer tracy.end(); |
| 2463 | 2456 | ||
| 2464 | var code_buffer = std.ArrayList(u8).init(self.base.allocator); | ||
| 2465 | defer code_buffer.deinit(); | ||
| 2466 | |||
| 2467 | const decl_index = func.owner_decl; | 2457 | const decl_index = func.owner_decl; |
| 2468 | const decl = module.declPtr(decl_index); | 2458 | const decl = module.declPtr(decl_index); |
| 2459 | |||
| 2460 | const atom_index = try self.getOrCreateAtomForDecl(decl_index); | ||
| 2469 | self.freeUnnamedConsts(decl_index); | 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 | var decl_state: ?Dwarf.DeclState = if (self.dwarf) |*dw| try dw.initDeclState(module, decl_index) else null; | 2467 | var decl_state: ?Dwarf.DeclState = if (self.dwarf) |*dw| try dw.initDeclState(module, decl_index) else null; |
| 2472 | defer if (decl_state) |*ds| ds.deinit(); | 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,7 +2475,7 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven |
| 2479 | try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .none); | 2475 | try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .none); |
| 2480 | 2476 | ||
| 2481 | const code = switch (res) { | 2477 | const code = switch (res) { |
| 2482 | .appended => code_buffer.items, | 2478 | .ok => code_buffer.items, |
| 2483 | .fail => |em| { | 2479 | .fail => |em| { |
| 2484 | decl.analysis = .codegen_failure; | 2480 | decl.analysis = .codegen_failure; |
| 2485 | try module.failed_decls.put(module.gpa, decl_index, em); | 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,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 | var code_buffer = std.ArrayList(u8).init(self.base.allocator); | 2528 | var code_buffer = std.ArrayList(u8).init(self.base.allocator); |
| 2531 | defer code_buffer.deinit(); | 2529 | defer code_buffer.deinit(); |
| ... | @@ -2542,19 +2540,18 @@ pub fn updateDecl(self: *Elf, module: *Module, decl_index: Module.Decl.Index) !v | ... | @@ -2542,19 +2540,18 @@ pub fn updateDecl(self: *Elf, module: *Module, decl_index: Module.Decl.Index) !v |
| 2542 | }, &code_buffer, .{ | 2540 | }, &code_buffer, .{ |
| 2543 | .dwarf = ds, | 2541 | .dwarf = ds, |
| 2544 | }, .{ | 2542 | }, .{ |
| 2545 | .parent_atom_index = decl.link.elf.local_sym_index, | 2543 | .parent_atom_index = atom.getSymbolIndex().?, |
| 2546 | }) | 2544 | }) |
| 2547 | else | 2545 | else |
| 2548 | try codegen.generateSymbol(&self.base, decl.srcLoc(), .{ | 2546 | try codegen.generateSymbol(&self.base, decl.srcLoc(), .{ |
| 2549 | .ty = decl.ty, | 2547 | .ty = decl.ty, |
| 2550 | .val = decl_val, | 2548 | .val = decl_val, |
| 2551 | }, &code_buffer, .none, .{ | 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 | const code = switch (res) { | 2553 | const code = switch (res) { |
| 2556 | .externally_managed => |x| x, | 2554 | .ok => code_buffer.items, |
| 2557 | .appended => code_buffer.items, | ||
| 2558 | .fail => |em| { | 2555 | .fail => |em| { |
| 2559 | decl.analysis = .codegen_failure; | 2556 | decl.analysis = .codegen_failure; |
| 2560 | try module.failed_decls.put(module.gpa, decl_index, em); | 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,47 +2576,38 @@ pub fn updateDecl(self: *Elf, module: *Module, decl_index: Module.Decl.Index) !v |
| 2579 | } | 2576 | } |
| 2580 | 2577 | ||
| 2581 | pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module.Decl.Index) !u32 { | 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 | defer code_buffer.deinit(); | 2582 | defer code_buffer.deinit(); |
| 2584 | 2583 | ||
| 2585 | const mod = self.base.options.module.?; | 2584 | const mod = self.base.options.module.?; |
| 2586 | const decl = mod.declPtr(decl_index); | 2585 | const gop = try self.unnamed_const_atoms.getOrPut(gpa, decl_index); |
| 2587 | |||
| 2588 | const gop = try self.unnamed_const_atoms.getOrPut(self.base.allocator, decl_index); | ||
| 2589 | if (!gop.found_existing) { | 2586 | if (!gop.found_existing) { |
| 2590 | gop.value_ptr.* = .{}; | 2587 | gop.value_ptr.* = .{}; |
| 2591 | } | 2588 | } |
| 2592 | const unnamed_consts = gop.value_ptr; | 2589 | const unnamed_consts = gop.value_ptr; |
| 2593 | 2590 | ||
| 2594 | const atom = try self.base.allocator.create(TextBlock); | 2591 | const decl = mod.declPtr(decl_index); |
| 2595 | errdefer self.base.allocator.destroy(atom); | ||
| 2596 | atom.* = TextBlock.empty; | ||
| 2597 | try self.managed_atoms.append(self.base.allocator, atom); | ||
| 2598 | |||
| 2599 | const name_str_index = blk: { | 2592 | const name_str_index = blk: { |
| 2600 | const decl_name = try decl.getFullyQualifiedName(mod); | 2593 | const decl_name = try decl.getFullyQualifiedName(mod); |
| 2601 | defer self.base.allocator.free(decl_name); | 2594 | defer gpa.free(decl_name); |
| 2602 | |||
| 2603 | const index = unnamed_consts.items.len; | 2595 | const index = unnamed_consts.items.len; |
| 2604 | const name = try std.fmt.allocPrint(self.base.allocator, "__unnamed_{s}_{d}", .{ decl_name, index }); | 2596 | const name = try std.fmt.allocPrint(gpa, "__unnamed_{s}_{d}", .{ decl_name, index }); |
| 2605 | defer self.base.allocator.free(name); | 2597 | defer gpa.free(name); |
| 2606 | 2598 | break :blk try self.shstrtab.insert(gpa, name); | |
| 2607 | break :blk try self.makeString(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}); | 2602 | const atom_index = try self.createAtom(); |
| 2612 | atom.local_sym_index = try self.allocateLocalSymbol(); | ||
| 2613 | try self.atom_by_index_table.putNoClobber(self.base.allocator, atom.local_sym_index, atom); | ||
| 2614 | 2603 | ||
| 2615 | const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), typed_value, &code_buffer, .{ | 2604 | const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), typed_value, &code_buffer, .{ |
| 2616 | .none = {}, | 2605 | .none = {}, |
| 2617 | }, .{ | 2606 | }, .{ |
| 2618 | .parent_atom_index = atom.local_sym_index, | 2607 | .parent_atom_index = self.getAtom(atom_index).getSymbolIndex().?, |
| 2619 | }); | 2608 | }); |
| 2620 | const code = switch (res) { | 2609 | const code = switch (res) { |
| 2621 | .externally_managed => |x| x, | 2610 | .ok => code_buffer.items, |
| 2622 | .appended => code_buffer.items, | ||
| 2623 | .fail => |em| { | 2611 | .fail => |em| { |
| 2624 | decl.analysis = .codegen_failure; | 2612 | decl.analysis = .codegen_failure; |
| 2625 | try mod.failed_decls.put(mod.gpa, decl_index, em); | 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,31 +2617,27 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module |
| 2629 | }; | 2617 | }; |
| 2630 | 2618 | ||
| 2631 | const required_alignment = typed_value.ty.abiAlignment(self.base.options.target); | 2619 | const required_alignment = typed_value.ty.abiAlignment(self.base.options.target); |
| 2632 | const phdr_index = self.phdr_load_ro_index.?; | 2620 | const shdr_index = self.rodata_section_index.?; |
| 2633 | const shdr_index = self.phdr_shdr_table.get(phdr_index).?; | 2621 | const phdr_index = self.sections.items(.phdr_index)[shdr_index]; |
| 2634 | const vaddr = try self.allocateTextBlock(atom, code.len, required_alignment, phdr_index); | 2622 | const local_sym = self.getAtom(atom_index).getSymbolPtr(self); |
| 2635 | errdefer self.freeTextBlock(atom, phdr_index); | 2623 | local_sym.st_name = name_str_index; |
| 2636 | 2624 | local_sym.st_info = (elf.STB_LOCAL << 4) | elf.STT_OBJECT; | |
| 2637 | log.debug("allocated text block for {s} at 0x{x}", .{ name, vaddr }); | 2625 | local_sym.st_other = 0; |
| 2638 | 2626 | local_sym.st_shndx = shdr_index; | |
| 2639 | const local_sym = &self.local_symbols.items[atom.local_sym_index]; | 2627 | local_sym.st_size = code.len; |
| 2640 | local_sym.* = .{ | 2628 | local_sym.st_value = try self.allocateAtom(atom_index, code.len, required_alignment); |
| 2641 | .st_name = name_str_index, | 2629 | errdefer self.freeAtom(atom_index); |
| 2642 | .st_info = (elf.STB_LOCAL << 4) | elf.STT_OBJECT, | 2630 | |
| 2643 | .st_other = 0, | 2631 | log.debug("allocated text block for {s} at 0x{x}", .{ name, local_sym.st_value }); |
| 2644 | .st_shndx = shdr_index, | 2632 | |
| 2645 | .st_value = vaddr, | 2633 | try self.writeSymbol(self.getAtom(atom_index).getSymbolIndex().?); |
| 2646 | .st_size = code.len, | 2634 | try unnamed_consts.append(gpa, atom_index); |
| 2647 | }; | ||
| 2648 | |||
| 2649 | try self.writeSymbol(atom.local_sym_index); | ||
| 2650 | try unnamed_consts.append(self.base.allocator, atom); | ||
| 2651 | 2635 | ||
| 2652 | const section_offset = local_sym.st_value - self.program_headers.items[phdr_index].p_vaddr; | 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 | try self.base.file.?.pwriteAll(code, file_offset); | 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 | pub fn updateDeclExports( | 2643 | pub fn updateDeclExports( |
| ... | @@ -2672,17 +2656,16 @@ pub fn updateDeclExports( | ... | @@ -2672,17 +2656,16 @@ pub fn updateDeclExports( |
| 2672 | const tracy = trace(@src()); | 2656 | const tracy = trace(@src()); |
| 2673 | defer tracy.end(); | 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 | const decl = module.declPtr(decl_index); | 2661 | const decl = module.declPtr(decl_index); |
| 2677 | if (decl.link.elf.local_sym_index == 0) return; | 2662 | const atom_index = try self.getOrCreateAtomForDecl(decl_index); |
| 2678 | const decl_sym = self.local_symbols.items[decl.link.elf.local_sym_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).?; | 2668 | try self.global_symbols.ensureUnusedCapacity(gpa, exports.len); |
| 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).?; | ||
| 2686 | 2669 | ||
| 2687 | for (exports) |exp| { | 2670 | for (exports) |exp| { |
| 2688 | if (exp.options.section) |section_name| { | 2671 | if (exp.options.section) |section_name| { |
| ... | @@ -2715,10 +2698,10 @@ pub fn updateDeclExports( | ... | @@ -2715,10 +2698,10 @@ pub fn updateDeclExports( |
| 2715 | }, | 2698 | }, |
| 2716 | }; | 2699 | }; |
| 2717 | const stt_bits: u8 = @truncate(u4, decl_sym.st_info); | 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 | const sym = &self.global_symbols.items[i]; | 2702 | const sym = &self.global_symbols.items[i]; |
| 2720 | sym.* = .{ | 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 | .st_info = (stb_bits << 4) | stt_bits, | 2705 | .st_info = (stb_bits << 4) | stt_bits, |
| 2723 | .st_other = 0, | 2706 | .st_other = 0, |
| 2724 | .st_shndx = shdr_index, | 2707 | .st_shndx = shdr_index, |
| ... | @@ -2726,30 +2709,29 @@ pub fn updateDeclExports( | ... | @@ -2726,30 +2709,29 @@ pub fn updateDeclExports( |
| 2726 | .st_size = decl_sym.st_size, | 2709 | .st_size = decl_sym.st_size, |
| 2727 | }; | 2710 | }; |
| 2728 | } else { | 2711 | } else { |
| 2729 | const name = try self.makeString(exp.options.name); | ||
| 2730 | const i = if (self.global_symbol_free_list.popOrNull()) |i| i else blk: { | 2712 | const i = if (self.global_symbol_free_list.popOrNull()) |i| i else blk: { |
| 2731 | _ = self.global_symbols.addOneAssumeCapacity(); | 2713 | _ = self.global_symbols.addOneAssumeCapacity(); |
| 2732 | break :blk self.global_symbols.items.len - 1; | 2714 | break :blk self.global_symbols.items.len - 1; |
| 2733 | }; | 2715 | }; |
| 2716 | try decl_metadata.exports.append(gpa, @intCast(u32, i)); | ||
| 2734 | self.global_symbols.items[i] = .{ | 2717 | self.global_symbols.items[i] = .{ |
| 2735 | .st_name = name, | 2718 | .st_name = try self.shstrtab.insert(gpa, exp.options.name), |
| 2736 | .st_info = (stb_bits << 4) | stt_bits, | 2719 | .st_info = (stb_bits << 4) | stt_bits, |
| 2737 | .st_other = 0, | 2720 | .st_other = 0, |
| 2738 | .st_shndx = shdr_index, | 2721 | .st_shndx = shdr_index, |
| 2739 | .st_value = decl_sym.st_value, | 2722 | .st_value = decl_sym.st_value, |
| 2740 | .st_size = decl_sym.st_size, | 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 | /// Must be called only after a successful call to `updateDecl`. | 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 | const tracy = trace(@src()); | 2731 | const tracy = trace(@src()); |
| 2751 | defer tracy.end(); | 2732 | defer tracy.end(); |
| 2752 | 2733 | ||
| 2734 | const decl = mod.declPtr(decl_index); | ||
| 2753 | const decl_name = try decl.getFullyQualifiedName(mod); | 2735 | const decl_name = try decl.getFullyQualifiedName(mod); |
| 2754 | defer self.base.allocator.free(decl_name); | 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,16 +2739,18 @@ pub fn updateDeclLineNumber(self: *Elf, mod: *Module, decl: *const Module.Decl) |
| 2757 | 2739 | ||
| 2758 | if (self.llvm_object) |_| return; | 2740 | if (self.llvm_object) |_| return; |
| 2759 | if (self.dwarf) |*dw| { | 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 | if (self.llvm_object) |_| return; | 2747 | if (self.llvm_object) |_| return; |
| 2766 | 2748 | const metadata = self.decls.getPtr(decl_index) orelse return; | |
| 2767 | const sym_index = exp.sym_index orelse return; | 2749 | const sym_index = metadata.getExportPtr(self, name) orelse return; |
| 2768 | self.global_symbol_free_list.append(self.base.allocator, sym_index) catch {}; | 2750 | log.debug("deleting export '{s}'", .{name}); |
| 2769 | self.global_symbols.items[sym_index].st_info = 0; | 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 | fn writeProgHeader(self: *Elf, index: usize) !void { | 2756 | fn writeProgHeader(self: *Elf, index: usize) !void { |
| ... | @@ -2795,7 +2779,7 @@ fn writeSectHeader(self: *Elf, index: usize) !void { | ... | @@ -2795,7 +2779,7 @@ fn writeSectHeader(self: *Elf, index: usize) !void { |
| 2795 | switch (self.ptr_width) { | 2779 | switch (self.ptr_width) { |
| 2796 | .p32 => { | 2780 | .p32 => { |
| 2797 | var shdr: [1]elf.Elf32_Shdr = undefined; | 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 | if (foreign_endian) { | 2783 | if (foreign_endian) { |
| 2800 | mem.byteSwapAllFields(elf.Elf32_Shdr, &shdr[0]); | 2784 | mem.byteSwapAllFields(elf.Elf32_Shdr, &shdr[0]); |
| 2801 | } | 2785 | } |
| ... | @@ -2803,7 +2787,7 @@ fn writeSectHeader(self: *Elf, index: usize) !void { | ... | @@ -2803,7 +2787,7 @@ fn writeSectHeader(self: *Elf, index: usize) !void { |
| 2803 | return self.base.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset); | 2787 | return self.base.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset); |
| 2804 | }, | 2788 | }, |
| 2805 | .p64 => { | 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 | if (foreign_endian) { | 2791 | if (foreign_endian) { |
| 2808 | mem.byteSwapAllFields(elf.Elf64_Shdr, &shdr[0]); | 2792 | mem.byteSwapAllFields(elf.Elf64_Shdr, &shdr[0]); |
| 2809 | } | 2793 | } |
| ... | @@ -2817,11 +2801,11 @@ fn writeOffsetTableEntry(self: *Elf, index: usize) !void { | ... | @@ -2817,11 +2801,11 @@ fn writeOffsetTableEntry(self: *Elf, index: usize) !void { |
| 2817 | const entry_size: u16 = self.archPtrWidthBytes(); | 2801 | const entry_size: u16 = self.archPtrWidthBytes(); |
| 2818 | if (self.offset_table_count_dirty) { | 2802 | if (self.offset_table_count_dirty) { |
| 2819 | const needed_size = self.offset_table.items.len * entry_size; | 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 | self.offset_table_count_dirty = false; | 2805 | self.offset_table_count_dirty = false; |
| 2822 | } | 2806 | } |
| 2823 | const endian = self.base.options.target.cpu.arch.endian(); | 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 | const off = shdr.sh_offset + @as(u64, entry_size) * index; | 2809 | const off = shdr.sh_offset + @as(u64, entry_size) * index; |
| 2826 | switch (entry_size) { | 2810 | switch (entry_size) { |
| 2827 | 2 => { | 2811 | 2 => { |
| ... | @@ -2847,7 +2831,7 @@ fn writeSymbol(self: *Elf, index: usize) !void { | ... | @@ -2847,7 +2831,7 @@ fn writeSymbol(self: *Elf, index: usize) !void { |
| 2847 | const tracy = trace(@src()); | 2831 | const tracy = trace(@src()); |
| 2848 | defer tracy.end(); | 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 | // Make sure we are not pointlessly writing symbol data that will have to get relocated | 2835 | // Make sure we are not pointlessly writing symbol data that will have to get relocated |
| 2852 | // due to running out of space. | 2836 | // due to running out of space. |
| 2853 | if (self.local_symbols.items.len != syms_sect.sh_info) { | 2837 | if (self.local_symbols.items.len != syms_sect.sh_info) { |
| ... | @@ -2869,7 +2853,7 @@ fn writeSymbol(self: *Elf, index: usize) !void { | ... | @@ -2869,7 +2853,7 @@ fn writeSymbol(self: *Elf, index: usize) !void { |
| 2869 | .p64 => syms_sect.sh_offset + @sizeOf(elf.Elf64_Sym) * index, | 2853 | .p64 => syms_sect.sh_offset + @sizeOf(elf.Elf64_Sym) * index, |
| 2870 | }; | 2854 | }; |
| 2871 | const local = self.local_symbols.items[index]; | 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 | log.debug(" ({})", .{local}); | 2857 | log.debug(" ({})", .{local}); |
| 2874 | switch (self.ptr_width) { | 2858 | switch (self.ptr_width) { |
| 2875 | .p32 => { | 2859 | .p32 => { |
| ... | @@ -2899,7 +2883,7 @@ fn writeSymbol(self: *Elf, index: usize) !void { | ... | @@ -2899,7 +2883,7 @@ fn writeSymbol(self: *Elf, index: usize) !void { |
| 2899 | } | 2883 | } |
| 2900 | 2884 | ||
| 2901 | fn writeAllGlobalSymbols(self: *Elf) !void { | 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 | const sym_size: u64 = switch (self.ptr_width) { | 2887 | const sym_size: u64 = switch (self.ptr_width) { |
| 2904 | .p32 => @sizeOf(elf.Elf32_Sym), | 2888 | .p32 => @sizeOf(elf.Elf32_Sym), |
| 2905 | .p64 => @sizeOf(elf.Elf64_Sym), | 2889 | .p64 => @sizeOf(elf.Elf64_Sym), |
| ... | @@ -3042,7 +3026,7 @@ fn getLDMOption(target: std.Target) ?[]const u8 { | ... | @@ -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 | return actual_size +| (actual_size / ideal_factor); | 3030 | return actual_size +| (actual_size / ideal_factor); |
| 3047 | } | 3031 | } |
| 3048 | 3032 | ||
| ... | @@ -3249,10 +3233,58 @@ const CsuObjects = struct { | ... | @@ -3249,10 +3233,58 @@ const CsuObjects = struct { |
| 3249 | fn logSymtab(self: Elf) void { | 3233 | fn logSymtab(self: Elf) void { |
| 3250 | log.debug("locals:", .{}); | 3234 | log.debug("locals:", .{}); |
| 3251 | for (self.local_symbols.items) |sym, id| { | 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 | log.debug("globals:", .{}); | 3238 | log.debug("globals:", .{}); |
| 3255 | for (self.global_symbols.items) |sym, id| { | 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,7 +66,7 @@ const Section = struct { |
| 66 | 66 | ||
| 67 | // TODO is null here necessary, or can we do away with tracking via section | 67 | // TODO is null here necessary, or can we do away with tracking via section |
| 68 | // size in incremental context? | 68 | // size in incremental context? |
| 69 | last_atom: ?*Atom = null, | 69 | last_atom_index: ?Atom.Index = null, |
| 70 | 70 | ||
| 71 | /// A list of atoms that have surplus capacity. This list can have false | 71 | /// A list of atoms that have surplus capacity. This list can have false |
| 72 | /// positives, as functions grow and shrink over time, only sometimes being added | 72 | /// positives, as functions grow and shrink over time, only sometimes being added |
| ... | @@ -83,7 +83,7 @@ const Section = struct { | ... | @@ -83,7 +83,7 @@ const Section = struct { |
| 83 | /// overcapacity can be negative. A simple way to have negative overcapacity is to | 83 | /// overcapacity can be negative. A simple way to have negative overcapacity is to |
| 84 | /// allocate a fresh atom, which will have ideal capacity, and then grow it | 84 | /// allocate a fresh atom, which will have ideal capacity, and then grow it |
| 85 | /// by 1 byte. It will then have -1 overcapacity. | 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 | base: File, | 89 | base: File, |
| ... | @@ -140,8 +140,8 @@ locals_free_list: std.ArrayListUnmanaged(u32) = .{}, | ... | @@ -140,8 +140,8 @@ locals_free_list: std.ArrayListUnmanaged(u32) = .{}, |
| 140 | globals_free_list: std.ArrayListUnmanaged(u32) = .{}, | 140 | globals_free_list: std.ArrayListUnmanaged(u32) = .{}, |
| 141 | 141 | ||
| 142 | dyld_stub_binder_index: ?u32 = null, | 142 | dyld_stub_binder_index: ?u32 = null, |
| 143 | dyld_private_atom: ?*Atom = null, | 143 | dyld_private_atom_index: ?Atom.Index = null, |
| 144 | stub_helper_preamble_atom: ?*Atom = null, | 144 | stub_helper_preamble_atom_index: ?Atom.Index = null, |
| 145 | 145 | ||
| 146 | strtab: StringTable(.strtab) = .{}, | 146 | strtab: StringTable(.strtab) = .{}, |
| 147 | 147 | ||
| ... | @@ -164,10 +164,10 @@ segment_table_dirty: bool = false, | ... | @@ -164,10 +164,10 @@ segment_table_dirty: bool = false, |
| 164 | cold_start: bool = true, | 164 | cold_start: bool = true, |
| 165 | 165 | ||
| 166 | /// List of atoms that are either synthetic or map directly to the Zig source program. | 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 | /// Table of atoms indexed by the symbol index. | 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 | /// Table of unnamed constants associated with a parent `Decl`. | 172 | /// Table of unnamed constants associated with a parent `Decl`. |
| 173 | /// We store them here so that we can free the constants whenever the `Decl` | 173 | /// We store them here so that we can free the constants whenever the `Decl` |
| ... | @@ -210,11 +210,36 @@ bindings: BindingTable = .{}, | ... | @@ -210,11 +210,36 @@ bindings: BindingTable = .{}, |
| 210 | /// this will be a table indexed by index into the list of Atoms. | 210 | /// this will be a table indexed by index into the list of Atoms. |
| 211 | lazy_bindings: BindingTable = .{}, | 211 | lazy_bindings: BindingTable = .{}, |
| 212 | 212 | ||
| 213 | /// Table of Decls that are currently alive. | 213 | /// Table of tracked Decls. |
| 214 | /// We store them here so that we can properly dispose of any allocated | 214 | decls: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, DeclMetadata) = .{}, |
| 215 | /// memory within the atom in the incremental linker. | 215 | |
| 216 | /// TODO consolidate this. | 216 | const DeclMetadata = struct { |
| 217 | decls: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, ?u8) = .{}, | 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 | const Entry = struct { | 244 | const Entry = struct { |
| 220 | target: SymbolWithLoc, | 245 | target: SymbolWithLoc, |
| ... | @@ -229,8 +254,8 @@ const Entry = struct { | ... | @@ -229,8 +254,8 @@ const Entry = struct { |
| 229 | return macho_file.getSymbolPtr(.{ .sym_index = entry.sym_index, .file = null }); | 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 { | 257 | pub fn getAtomIndex(entry: Entry, macho_file: *MachO) ?Atom.Index { |
| 233 | return macho_file.getAtomForSymbol(.{ .sym_index = entry.sym_index, .file = null }); | 258 | return macho_file.getAtomIndexForSymbol(.{ .sym_index = entry.sym_index, .file = null }); |
| 234 | } | 259 | } |
| 235 | 260 | ||
| 236 | pub fn getName(entry: Entry, macho_file: *MachO) []const u8 { | 261 | pub fn getName(entry: Entry, macho_file: *MachO) []const u8 { |
| ... | @@ -238,10 +263,10 @@ const Entry = struct { | ... | @@ -238,10 +263,10 @@ const Entry = struct { |
| 238 | } | 263 | } |
| 239 | }; | 264 | }; |
| 240 | 265 | ||
| 241 | const BindingTable = std.AutoArrayHashMapUnmanaged(*Atom, std.ArrayListUnmanaged(Atom.Binding)); | 266 | const BindingTable = std.AutoArrayHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(Atom.Binding)); |
| 242 | const UnnamedConstTable = std.AutoArrayHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(*Atom)); | 267 | const UnnamedConstTable = std.AutoArrayHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(Atom.Index)); |
| 243 | const RebaseTable = std.AutoArrayHashMapUnmanaged(*Atom, std.ArrayListUnmanaged(u32)); | 268 | const RebaseTable = std.AutoArrayHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(u32)); |
| 244 | const RelocationTable = std.AutoArrayHashMapUnmanaged(*Atom, std.ArrayListUnmanaged(Relocation)); | 269 | const RelocationTable = std.AutoArrayHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(Relocation)); |
| 245 | 270 | ||
| 246 | const PendingUpdate = union(enum) { | 271 | const PendingUpdate = union(enum) { |
| 247 | resolve_undef: u32, | 272 | resolve_undef: u32, |
| ... | @@ -286,10 +311,6 @@ pub const default_pagezero_vmsize: u64 = 0x100000000; | ... | @@ -286,10 +311,6 @@ pub const default_pagezero_vmsize: u64 = 0x100000000; |
| 286 | /// potential future extensions. | 311 | /// potential future extensions. |
| 287 | pub const default_headerpad_size: u32 = 0x1000; | 312 | pub const default_headerpad_size: u32 = 0x1000; |
| 288 | 313 | ||
| 289 | pub const Export = struct { | ||
| 290 | sym_index: ?u32 = null, | ||
| 291 | }; | ||
| 292 | |||
| 293 | pub fn openPath(allocator: Allocator, options: link.Options) !*MachO { | 314 | pub fn openPath(allocator: Allocator, options: link.Options) !*MachO { |
| 294 | assert(options.target.ofmt == .macho); | 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,8 +568,8 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No |
| 547 | 568 | ||
| 548 | try self.allocateSpecialSymbols(); | 569 | try self.allocateSpecialSymbols(); |
| 549 | 570 | ||
| 550 | for (self.relocs.keys()) |atom| { | 571 | for (self.relocs.keys()) |atom_index| { |
| 551 | try atom.resolveRelocations(self); | 572 | try Atom.resolveRelocations(self, atom_index); |
| 552 | } | 573 | } |
| 553 | 574 | ||
| 554 | if (build_options.enable_logging) { | 575 | if (build_options.enable_logging) { |
| ... | @@ -999,18 +1020,19 @@ pub fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs: | ... | @@ -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 | const sym = atom.getSymbol(self); | 1025 | const sym = atom.getSymbol(self); |
| 1004 | const section = self.sections.get(sym.n_sect - 1); | 1026 | const section = self.sections.get(sym.n_sect - 1); |
| 1005 | const file_offset = section.header.offset + sym.n_value - section.header.addr; | 1027 | const file_offset = section.header.offset + sym.n_value - section.header.addr; |
| 1006 | log.debug("writing atom for symbol {s} at file offset 0x{x}", .{ atom.getName(self), file_offset }); | 1028 | log.debug("writing atom for symbol {s} at file offset 0x{x}", .{ atom.getName(self), file_offset }); |
| 1007 | try self.base.file.?.pwriteAll(code, file_offset); | 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 | var buffer: [@sizeOf(u64)]u8 = [_]u8{0} ** @sizeOf(u64); | 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 | fn markRelocsDirtyByTarget(self: *MachO, target: SymbolWithLoc) void { | 1038 | fn markRelocsDirtyByTarget(self: *MachO, target: SymbolWithLoc) void { |
| ... | @@ -1026,7 +1048,8 @@ fn markRelocsDirtyByTarget(self: *MachO, target: SymbolWithLoc) void { | ... | @@ -1026,7 +1048,8 @@ fn markRelocsDirtyByTarget(self: *MachO, target: SymbolWithLoc) void { |
| 1026 | fn markRelocsDirtyByAddress(self: *MachO, addr: u64) void { | 1048 | fn markRelocsDirtyByAddress(self: *MachO, addr: u64) void { |
| 1027 | for (self.relocs.values()) |*relocs| { | 1049 | for (self.relocs.values()) |*relocs| { |
| 1028 | for (relocs.items) |*reloc| { | 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 | const target_sym = target_atom.getSymbol(self); | 1053 | const target_sym = target_atom.getSymbol(self); |
| 1031 | if (target_sym.n_value < addr) continue; | 1054 | if (target_sym.n_value < addr) continue; |
| 1032 | reloc.dirty = true; | 1055 | reloc.dirty = true; |
| ... | @@ -1053,31 +1076,38 @@ pub fn allocateSpecialSymbols(self: *MachO) !void { | ... | @@ -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 | const gpa = self.base.allocator; | 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 | const sym_index = try self.allocateSymbol(); | 1083 | const sym_index = try self.allocateSymbol(); |
| 1060 | const atom = blk: { | 1084 | try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom_index); |
| 1061 | const atom = try gpa.create(Atom); | 1085 | atom.* = .{ |
| 1062 | atom.* = Atom.empty; | 1086 | .sym_index = sym_index, |
| 1063 | atom.sym_index = sym_index; | 1087 | .file = null, |
| 1064 | atom.size = @sizeOf(u64); | 1088 | .size = 0, |
| 1065 | atom.alignment = @alignOf(u64); | 1089 | .alignment = 0, |
| 1066 | break :blk atom; | 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); | 1097 | pub fn createGotAtom(self: *MachO, target: SymbolWithLoc) !Atom.Index { |
| 1071 | try self.atom_by_index_table.putNoClobber(gpa, atom.sym_index, atom); | 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 | const sym = atom.getSymbolPtr(self); | 1103 | const sym = atom.getSymbolPtr(self); |
| 1074 | sym.n_type = macho.N_SECT; | 1104 | sym.n_type = macho.N_SECT; |
| 1075 | sym.n_sect = self.got_section_index.? + 1; | 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 | log.debug("allocated GOT atom at 0x{x}", .{sym.n_value}); | 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 | .type = switch (self.base.options.target.cpu.arch) { | 1111 | .type = switch (self.base.options.target.cpu.arch) { |
| 1082 | .aarch64 => @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED), | 1112 | .aarch64 => @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED), |
| 1083 | .x86_64 => @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED), | 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,50 +1122,39 @@ pub fn createGotAtom(self: *MachO, target: SymbolWithLoc) !*Atom { |
| 1092 | 1122 | ||
| 1093 | const target_sym = self.getSymbol(target); | 1123 | const target_sym = self.getSymbol(target); |
| 1094 | if (target_sym.undf()) { | 1124 | if (target_sym.undf()) { |
| 1095 | try atom.addBinding(self, .{ | 1125 | try Atom.addBinding(self, atom_index, .{ |
| 1096 | .target = self.getGlobal(self.getSymbolName(target)).?, | 1126 | .target = self.getGlobal(self.getSymbolName(target)).?, |
| 1097 | .offset = 0, | 1127 | .offset = 0, |
| 1098 | }); | 1128 | }); |
| 1099 | } else { | 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 | pub fn createDyldPrivateAtom(self: *MachO) !void { | 1136 | pub fn createDyldPrivateAtom(self: *MachO) !void { |
| 1107 | if (self.dyld_stub_binder_index == null) return; | 1137 | if (self.dyld_stub_binder_index == null) return; |
| 1108 | if (self.dyld_private_atom != null) return; | 1138 | if (self.dyld_private_atom_index != null) return; |
| 1109 | |||
| 1110 | const gpa = self.base.allocator; | ||
| 1111 | 1139 | ||
| 1112 | const sym_index = try self.allocateSymbol(); | 1140 | const atom_index = try self.createAtom(); |
| 1113 | const atom = blk: { | 1141 | const atom = self.getAtomPtr(atom_index); |
| 1114 | const atom = try gpa.create(Atom); | 1142 | atom.size = @sizeOf(u64); |
| 1115 | atom.* = Atom.empty; | 1143 | atom.alignment = @alignOf(u64); |
| 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); | ||
| 1122 | 1144 | ||
| 1123 | const sym = atom.getSymbolPtr(self); | 1145 | const sym = atom.getSymbolPtr(self); |
| 1124 | sym.n_type = macho.N_SECT; | 1146 | sym.n_type = macho.N_SECT; |
| 1125 | sym.n_sect = self.data_section_index.? + 1; | 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); | 1150 | sym.n_value = try self.allocateAtom(atom_index, atom.size, @alignOf(u64)); |
| 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)); | ||
| 1132 | log.debug("allocated dyld_private atom at 0x{x}", .{sym.n_value}); | 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 | pub fn createStubHelperPreambleAtom(self: *MachO) !void { | 1155 | pub fn createStubHelperPreambleAtom(self: *MachO) !void { |
| 1137 | if (self.dyld_stub_binder_index == null) return; | 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 | const gpa = self.base.allocator; | 1159 | const gpa = self.base.allocator; |
| 1141 | const arch = self.base.options.target.cpu.arch; | 1160 | const arch = self.base.options.target.cpu.arch; |
| ... | @@ -1144,26 +1163,23 @@ pub fn createStubHelperPreambleAtom(self: *MachO) !void { | ... | @@ -1144,26 +1163,23 @@ pub fn createStubHelperPreambleAtom(self: *MachO) !void { |
| 1144 | .aarch64 => 6 * @sizeOf(u32), | 1163 | .aarch64 => 6 * @sizeOf(u32), |
| 1145 | else => unreachable, | 1164 | else => unreachable, |
| 1146 | }; | 1165 | }; |
| 1147 | const sym_index = try self.allocateSymbol(); | 1166 | const atom_index = try self.createAtom(); |
| 1148 | const atom = blk: { | 1167 | const atom = self.getAtomPtr(atom_index); |
| 1149 | const atom = try gpa.create(Atom); | 1168 | atom.size = size; |
| 1150 | atom.* = Atom.empty; | 1169 | atom.alignment = switch (arch) { |
| 1151 | atom.sym_index = sym_index; | 1170 | .x86_64 => 1, |
| 1152 | atom.size = size; | 1171 | .aarch64 => @alignOf(u32), |
| 1153 | atom.alignment = switch (arch) { | 1172 | else => unreachable, |
| 1154 | .x86_64 => 1, | ||
| 1155 | .aarch64 => @alignOf(u32), | ||
| 1156 | else => unreachable, | ||
| 1157 | }; | ||
| 1158 | break :blk atom; | ||
| 1159 | }; | 1173 | }; |
| 1160 | errdefer gpa.destroy(atom); | ||
| 1161 | 1174 | ||
| 1162 | const sym = atom.getSymbolPtr(self); | 1175 | const sym = atom.getSymbolPtr(self); |
| 1163 | sym.n_type = macho.N_SECT; | 1176 | sym.n_type = macho.N_SECT; |
| 1164 | sym.n_sect = self.stub_helper_section_index.? + 1; | 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 | const code = try gpa.alloc(u8, size); | 1184 | const code = try gpa.alloc(u8, size); |
| 1169 | defer gpa.free(code); | 1185 | defer gpa.free(code); |
| ... | @@ -1182,7 +1198,7 @@ pub fn createStubHelperPreambleAtom(self: *MachO) !void { | ... | @@ -1182,7 +1198,7 @@ pub fn createStubHelperPreambleAtom(self: *MachO) !void { |
| 1182 | code[9] = 0xff; | 1198 | code[9] = 0xff; |
| 1183 | code[10] = 0x25; | 1199 | code[10] = 0x25; |
| 1184 | 1200 | ||
| 1185 | try atom.addRelocations(self, 2, .{ .{ | 1201 | try Atom.addRelocations(self, atom_index, 2, .{ .{ |
| 1186 | .type = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_SIGNED), | 1202 | .type = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_SIGNED), |
| 1187 | .target = .{ .sym_index = dyld_private_sym_index, .file = null }, | 1203 | .target = .{ .sym_index = dyld_private_sym_index, .file = null }, |
| 1188 | .offset = 3, | 1204 | .offset = 3, |
| ... | @@ -1222,7 +1238,7 @@ pub fn createStubHelperPreambleAtom(self: *MachO) !void { | ... | @@ -1222,7 +1238,7 @@ pub fn createStubHelperPreambleAtom(self: *MachO) !void { |
| 1222 | // br x16 | 1238 | // br x16 |
| 1223 | mem.writeIntLittle(u32, code[20..][0..4], aarch64.Instruction.br(.x16).toU32()); | 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 | .type = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGE21), | 1242 | .type = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGE21), |
| 1227 | .target = .{ .sym_index = dyld_private_sym_index, .file = null }, | 1243 | .target = .{ .sym_index = dyld_private_sym_index, .file = null }, |
| 1228 | .offset = 0, | 1244 | .offset = 0, |
| ... | @@ -1255,17 +1271,14 @@ pub fn createStubHelperPreambleAtom(self: *MachO) !void { | ... | @@ -1255,17 +1271,14 @@ pub fn createStubHelperPreambleAtom(self: *MachO) !void { |
| 1255 | 1271 | ||
| 1256 | else => unreachable, | 1272 | else => unreachable, |
| 1257 | } | 1273 | } |
| 1258 | self.stub_helper_preamble_atom = atom; | 1274 | self.stub_helper_preamble_atom_index = atom_index; |
| 1259 | |||
| 1260 | try self.managed_atoms.append(gpa, atom); | ||
| 1261 | try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom); | ||
| 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 | log.debug("allocated stub preamble atom at 0x{x}", .{sym.n_value}); | 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 | const gpa = self.base.allocator; | 1282 | const gpa = self.base.allocator; |
| 1270 | const arch = self.base.options.target.cpu.arch; | 1283 | const arch = self.base.options.target.cpu.arch; |
| 1271 | const size: u4 = switch (arch) { | 1284 | const size: u4 = switch (arch) { |
| ... | @@ -1273,20 +1286,14 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom { | ... | @@ -1273,20 +1286,14 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom { |
| 1273 | .aarch64 => 3 * @sizeOf(u32), | 1286 | .aarch64 => 3 * @sizeOf(u32), |
| 1274 | else => unreachable, | 1287 | else => unreachable, |
| 1275 | }; | 1288 | }; |
| 1276 | const sym_index = try self.allocateSymbol(); | 1289 | const atom_index = try self.createAtom(); |
| 1277 | const atom = blk: { | 1290 | const atom = self.getAtomPtr(atom_index); |
| 1278 | const atom = try gpa.create(Atom); | 1291 | atom.size = size; |
| 1279 | atom.* = Atom.empty; | 1292 | atom.alignment = switch (arch) { |
| 1280 | atom.sym_index = sym_index; | 1293 | .x86_64 => 1, |
| 1281 | atom.size = size; | 1294 | .aarch64 => @alignOf(u32), |
| 1282 | atom.alignment = switch (arch) { | 1295 | else => unreachable, |
| 1283 | .x86_64 => 1, | ||
| 1284 | .aarch64 => @alignOf(u32), | ||
| 1285 | else => unreachable, | ||
| 1286 | }; | ||
| 1287 | break :blk atom; | ||
| 1288 | }; | 1296 | }; |
| 1289 | errdefer gpa.destroy(atom); | ||
| 1290 | 1297 | ||
| 1291 | const sym = atom.getSymbolPtr(self); | 1298 | const sym = atom.getSymbolPtr(self); |
| 1292 | sym.n_type = macho.N_SECT; | 1299 | sym.n_type = macho.N_SECT; |
| ... | @@ -1296,6 +1303,11 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom { | ... | @@ -1296,6 +1303,11 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom { |
| 1296 | defer gpa.free(code); | 1303 | defer gpa.free(code); |
| 1297 | mem.set(u8, code, 0); | 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 | switch (arch) { | 1311 | switch (arch) { |
| 1300 | .x86_64 => { | 1312 | .x86_64 => { |
| 1301 | // pushq | 1313 | // pushq |
| ... | @@ -1304,9 +1316,9 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom { | ... | @@ -1304,9 +1316,9 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom { |
| 1304 | // jmpq | 1316 | // jmpq |
| 1305 | code[5] = 0xe9; | 1317 | code[5] = 0xe9; |
| 1306 | 1318 | ||
| 1307 | try atom.addRelocation(self, .{ | 1319 | try Atom.addRelocation(self, atom_index, .{ |
| 1308 | .type = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_BRANCH), | 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 | .offset = 6, | 1322 | .offset = 6, |
| 1311 | .addend = 0, | 1323 | .addend = 0, |
| 1312 | .pcrel = true, | 1324 | .pcrel = true, |
| ... | @@ -1327,9 +1339,9 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom { | ... | @@ -1327,9 +1339,9 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom { |
| 1327 | mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.b(0).toU32()); | 1339 | mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.b(0).toU32()); |
| 1328 | // Next 4 bytes 8..12 are just a placeholder populated in `populateLazyBindOffsetsInStubHelper`. | 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 | .type = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_BRANCH26), | 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 | .offset = 4, | 1345 | .offset = 4, |
| 1334 | .addend = 0, | 1346 | .addend = 0, |
| 1335 | .pcrel = true, | 1347 | .pcrel = true, |
| ... | @@ -1339,34 +1351,24 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom { | ... | @@ -1339,34 +1351,24 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom { |
| 1339 | else => unreachable, | 1351 | else => unreachable, |
| 1340 | } | 1352 | } |
| 1341 | 1353 | ||
| 1342 | try self.managed_atoms.append(gpa, atom); | 1354 | sym.n_value = try self.allocateAtom(atom_index, size, atom.alignment); |
| 1343 | try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom); | ||
| 1344 | |||
| 1345 | sym.n_value = try self.allocateAtom(atom, size, atom.alignment); | ||
| 1346 | log.debug("allocated stub helper atom at 0x{x}", .{sym.n_value}); | 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 { | 1361 | pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, target: SymbolWithLoc) !Atom.Index { |
| 1353 | const gpa = self.base.allocator; | 1362 | const atom_index = try self.createAtom(); |
| 1354 | const sym_index = try self.allocateSymbol(); | 1363 | const atom = self.getAtomPtr(atom_index); |
| 1355 | const atom = blk: { | 1364 | atom.size = @sizeOf(u64); |
| 1356 | const atom = try gpa.create(Atom); | 1365 | atom.alignment = @alignOf(u64); |
| 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); | ||
| 1364 | 1366 | ||
| 1365 | const sym = atom.getSymbolPtr(self); | 1367 | const sym = atom.getSymbolPtr(self); |
| 1366 | sym.n_type = macho.N_SECT; | 1368 | sym.n_type = macho.N_SECT; |
| 1367 | sym.n_sect = self.la_symbol_ptr_section_index.? + 1; | 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 | .type = switch (self.base.options.target.cpu.arch) { | 1372 | .type = switch (self.base.options.target.cpu.arch) { |
| 1371 | .aarch64 => @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED), | 1373 | .aarch64 => @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED), |
| 1372 | .x86_64 => @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED), | 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,23 +1380,20 @@ pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, target: SymbolWi |
| 1378 | .pcrel = false, | 1380 | .pcrel = false, |
| 1379 | .length = 3, | 1381 | .length = 3, |
| 1380 | }); | 1382 | }); |
| 1381 | try atom.addRebase(self, 0); | 1383 | try Atom.addRebase(self, atom_index, 0); |
| 1382 | try atom.addLazyBinding(self, .{ | 1384 | try Atom.addLazyBinding(self, atom_index, .{ |
| 1383 | .target = self.getGlobal(self.getSymbolName(target)).?, | 1385 | .target = self.getGlobal(self.getSymbolName(target)).?, |
| 1384 | .offset = 0, | 1386 | .offset = 0, |
| 1385 | }); | 1387 | }); |
| 1386 | 1388 | ||
| 1387 | try self.managed_atoms.append(gpa, atom); | 1389 | sym.n_value = try self.allocateAtom(atom_index, atom.size, @alignOf(u64)); |
| 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)); | ||
| 1391 | log.debug("allocated lazy pointer atom at 0x{x} ({s})", .{ sym.n_value, self.getSymbolName(target) }); | 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 | const gpa = self.base.allocator; | 1397 | const gpa = self.base.allocator; |
| 1399 | const arch = self.base.options.target.cpu.arch; | 1398 | const arch = self.base.options.target.cpu.arch; |
| 1400 | const size: u4 = switch (arch) { | 1399 | const size: u4 = switch (arch) { |
| ... | @@ -1402,21 +1401,15 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom { | ... | @@ -1402,21 +1401,15 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom { |
| 1402 | .aarch64 => 3 * @sizeOf(u32), | 1401 | .aarch64 => 3 * @sizeOf(u32), |
| 1403 | else => unreachable, // unhandled architecture type | 1402 | else => unreachable, // unhandled architecture type |
| 1404 | }; | 1403 | }; |
| 1405 | const sym_index = try self.allocateSymbol(); | 1404 | const atom_index = try self.createAtom(); |
| 1406 | const atom = blk: { | 1405 | const atom = self.getAtomPtr(atom_index); |
| 1407 | const atom = try gpa.create(Atom); | 1406 | atom.size = size; |
| 1408 | atom.* = Atom.empty; | 1407 | atom.alignment = switch (arch) { |
| 1409 | atom.sym_index = sym_index; | 1408 | .x86_64 => 1, |
| 1410 | atom.size = size; | 1409 | .aarch64 => @alignOf(u32), |
| 1411 | atom.alignment = switch (arch) { | 1410 | else => unreachable, // unhandled architecture type |
| 1412 | .x86_64 => 1, | ||
| 1413 | .aarch64 => @alignOf(u32), | ||
| 1414 | else => unreachable, // unhandled architecture type | ||
| 1415 | 1411 | ||
| 1416 | }; | ||
| 1417 | break :blk atom; | ||
| 1418 | }; | 1412 | }; |
| 1419 | errdefer gpa.destroy(atom); | ||
| 1420 | 1413 | ||
| 1421 | const sym = atom.getSymbolPtr(self); | 1414 | const sym = atom.getSymbolPtr(self); |
| 1422 | sym.n_type = macho.N_SECT; | 1415 | sym.n_type = macho.N_SECT; |
| ... | @@ -1432,7 +1425,7 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom { | ... | @@ -1432,7 +1425,7 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom { |
| 1432 | code[0] = 0xff; | 1425 | code[0] = 0xff; |
| 1433 | code[1] = 0x25; | 1426 | code[1] = 0x25; |
| 1434 | 1427 | ||
| 1435 | try atom.addRelocation(self, .{ | 1428 | try Atom.addRelocation(self, atom_index, .{ |
| 1436 | .type = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_BRANCH), | 1429 | .type = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_BRANCH), |
| 1437 | .target = .{ .sym_index = laptr_sym_index, .file = null }, | 1430 | .target = .{ .sym_index = laptr_sym_index, .file = null }, |
| 1438 | .offset = 2, | 1431 | .offset = 2, |
| ... | @@ -1453,7 +1446,7 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom { | ... | @@ -1453,7 +1446,7 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom { |
| 1453 | // br x16 | 1446 | // br x16 |
| 1454 | mem.writeIntLittle(u32, code[8..12], aarch64.Instruction.br(.x16).toU32()); | 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 | .type = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGE21), | 1451 | .type = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGE21), |
| 1459 | .target = .{ .sym_index = laptr_sym_index, .file = null }, | 1452 | .target = .{ .sym_index = laptr_sym_index, .file = null }, |
| ... | @@ -1475,14 +1468,11 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom { | ... | @@ -1475,14 +1468,11 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom { |
| 1475 | else => unreachable, | 1468 | else => unreachable, |
| 1476 | } | 1469 | } |
| 1477 | 1470 | ||
| 1478 | try self.managed_atoms.append(gpa, atom); | 1471 | sym.n_value = try self.allocateAtom(atom_index, size, atom.alignment); |
| 1479 | try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom); | ||
| 1480 | |||
| 1481 | sym.n_value = try self.allocateAtom(atom, size, atom.alignment); | ||
| 1482 | log.debug("allocated stub atom at 0x{x}", .{sym.n_value}); | 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 | pub fn createMhExecuteHeaderSymbol(self: *MachO) !void { | 1478 | pub fn createMhExecuteHeaderSymbol(self: *MachO) !void { |
| ... | @@ -1616,10 +1606,13 @@ pub fn resolveSymbolsInDylibs(self: *MachO) !void { | ... | @@ -1616,10 +1606,13 @@ pub fn resolveSymbolsInDylibs(self: *MachO) !void { |
| 1616 | if (self.stubs_table.contains(global)) break :blk; | 1606 | if (self.stubs_table.contains(global)) break :blk; |
| 1617 | 1607 | ||
| 1618 | const stub_index = try self.allocateStubEntry(global); | 1608 | const stub_index = try self.allocateStubEntry(global); |
| 1619 | const stub_helper_atom = try self.createStubHelperAtom(); | 1609 | const stub_helper_atom_index = try self.createStubHelperAtom(); |
| 1620 | const laptr_atom = try self.createLazyPointerAtom(stub_helper_atom.sym_index, global); | 1610 | const stub_helper_atom = self.getAtom(stub_helper_atom_index); |
| 1621 | const stub_atom = try self.createStubAtom(laptr_atom.sym_index); | 1611 | const laptr_atom_index = try self.createLazyPointerAtom(stub_helper_atom.getSymbolIndex().?, global); |
| 1622 | self.stubs.items[stub_index].sym_index = stub_atom.sym_index; | 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 | self.markRelocsDirtyByTarget(global); | 1616 | self.markRelocsDirtyByTarget(global); |
| 1624 | } | 1617 | } |
| 1625 | 1618 | ||
| ... | @@ -1716,10 +1709,11 @@ pub fn resolveDyldStubBinder(self: *MachO) !void { | ... | @@ -1716,10 +1709,11 @@ pub fn resolveDyldStubBinder(self: *MachO) !void { |
| 1716 | 1709 | ||
| 1717 | // Add dyld_stub_binder as the final GOT entry. | 1710 | // Add dyld_stub_binder as the final GOT entry. |
| 1718 | const got_index = try self.allocateGotEntry(global); | 1711 | const got_index = try self.allocateGotEntry(global); |
| 1719 | const got_atom = try self.createGotAtom(global); | 1712 | const got_atom_index = try self.createGotAtom(global); |
| 1720 | self.got_entries.items[got_index].sym_index = got_atom.sym_index; | 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 | pub fn deinit(self: *MachO) void { | 1719 | pub fn deinit(self: *MachO) void { |
| ... | @@ -1769,12 +1763,12 @@ pub fn deinit(self: *MachO) void { | ... | @@ -1769,12 +1763,12 @@ pub fn deinit(self: *MachO) void { |
| 1769 | } | 1763 | } |
| 1770 | self.sections.deinit(gpa); | 1764 | self.sections.deinit(gpa); |
| 1771 | 1765 | ||
| 1772 | for (self.managed_atoms.items) |atom| { | 1766 | self.atoms.deinit(gpa); |
| 1773 | gpa.destroy(atom); | ||
| 1774 | } | ||
| 1775 | self.managed_atoms.deinit(gpa); | ||
| 1776 | 1767 | ||
| 1777 | if (self.base.options.module) |_| { | 1768 | if (self.base.options.module) |_| { |
| 1769 | for (self.decls.values()) |*m| { | ||
| 1770 | m.exports.deinit(gpa); | ||
| 1771 | } | ||
| 1778 | self.decls.deinit(gpa); | 1772 | self.decls.deinit(gpa); |
| 1779 | } else { | 1773 | } else { |
| 1780 | assert(self.decls.count() == 0); | 1774 | assert(self.decls.count() == 0); |
| ... | @@ -1808,12 +1802,14 @@ pub fn deinit(self: *MachO) void { | ... | @@ -1808,12 +1802,14 @@ pub fn deinit(self: *MachO) void { |
| 1808 | self.lazy_bindings.deinit(gpa); | 1802 | self.lazy_bindings.deinit(gpa); |
| 1809 | } | 1803 | } |
| 1810 | 1804 | ||
| 1811 | fn freeAtom(self: *MachO, atom: *Atom) void { | 1805 | fn freeAtom(self: *MachO, atom_index: Atom.Index) void { |
| 1812 | log.debug("freeAtom {*}", .{atom}); | 1806 | const gpa = self.base.allocator; |
| 1807 | log.debug("freeAtom {d}", .{atom_index}); | ||
| 1813 | 1808 | ||
| 1814 | // Remove any relocs and base relocs associated with this Atom | 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 | const sect_id = atom.getSymbol(self).n_sect - 1; | 1813 | const sect_id = atom.getSymbol(self).n_sect - 1; |
| 1818 | const free_list = &self.sections.items(.free_list)[sect_id]; | 1814 | const free_list = &self.sections.items(.free_list)[sect_id]; |
| 1819 | var already_have_free_list_node = false; | 1815 | var already_have_free_list_node = false; |
| ... | @@ -1821,69 +1817,94 @@ fn freeAtom(self: *MachO, atom: *Atom) void { | ... | @@ -1821,69 +1817,94 @@ fn freeAtom(self: *MachO, atom: *Atom) void { |
| 1821 | var i: usize = 0; | 1817 | var i: usize = 0; |
| 1822 | // TODO turn free_list into a hash map | 1818 | // TODO turn free_list into a hash map |
| 1823 | while (i < free_list.items.len) { | 1819 | while (i < free_list.items.len) { |
| 1824 | if (free_list.items[i] == atom) { | 1820 | if (free_list.items[i] == atom_index) { |
| 1825 | _ = free_list.swapRemove(i); | 1821 | _ = free_list.swapRemove(i); |
| 1826 | continue; | 1822 | continue; |
| 1827 | } | 1823 | } |
| 1828 | if (free_list.items[i] == atom.prev) { | 1824 | if (free_list.items[i] == atom.prev_index) { |
| 1829 | already_have_free_list_node = true; | 1825 | already_have_free_list_node = true; |
| 1830 | } | 1826 | } |
| 1831 | i += 1; | 1827 | i += 1; |
| 1832 | } | 1828 | } |
| 1833 | } | 1829 | } |
| 1834 | 1830 | ||
| 1835 | const maybe_last_atom = &self.sections.items(.last_atom)[sect_id]; | 1831 | const maybe_last_atom_index = &self.sections.items(.last_atom_index)[sect_id]; |
| 1836 | if (maybe_last_atom.*) |last_atom| { | 1832 | if (maybe_last_atom_index.*) |last_atom_index| { |
| 1837 | if (last_atom == atom) { | 1833 | if (last_atom_index == atom_index) { |
| 1838 | if (atom.prev) |prev| { | 1834 | if (atom.prev_index) |prev_index| { |
| 1839 | // TODO shrink the section size here | 1835 | // TODO shrink the section size here |
| 1840 | maybe_last_atom.* = prev; | 1836 | maybe_last_atom_index.* = prev_index; |
| 1841 | } else { | 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| { | 1843 | if (atom.prev_index) |prev_index| { |
| 1848 | prev.next = atom.next; | 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 | // The free list is heuristics, it doesn't have to be perfect, so we can ignore | 1848 | // The free list is heuristics, it doesn't have to be perfect, so we can ignore |
| 1852 | // the OOM here. | 1849 | // the OOM here. |
| 1853 | free_list.append(self.base.allocator, prev) catch {}; | 1850 | free_list.append(gpa, prev_index) catch {}; |
| 1854 | } | 1851 | } |
| 1855 | } else { | 1852 | } else { |
| 1856 | atom.prev = null; | 1853 | self.getAtomPtr(atom_index).prev_index = null; |
| 1857 | } | 1854 | } |
| 1858 | 1855 | ||
| 1859 | if (atom.next) |next| { | 1856 | if (atom.next_index) |next_index| { |
| 1860 | next.prev = atom.prev; | 1857 | self.getAtomPtr(next_index).prev_index = atom.prev_index; |
| 1861 | } else { | 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| { | 1862 | // Appending to free lists is allowed to fail because the free lists are heuristics based anyway. |
| 1866 | d_sym.dwarf.freeAtom(&atom.dbg_info_atom); | 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 | _ = self; | 1891 | _ = self; |
| 1872 | _ = atom; | 1892 | _ = atom_index; |
| 1873 | _ = new_block_size; | 1893 | _ = new_block_size; |
| 1874 | // TODO check the new capacity, and if it crosses the size threshold into a big enough | 1894 | // TODO check the new capacity, and if it crosses the size threshold into a big enough |
| 1875 | // capacity, insert a free list node for it. | 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 | const sym = atom.getSymbol(self); | 1900 | const sym = atom.getSymbol(self); |
| 1880 | const align_ok = mem.alignBackwardGeneric(u64, sym.n_value, alignment) == sym.n_value; | 1901 | const align_ok = mem.alignBackwardGeneric(u64, sym.n_value, alignment) == sym.n_value; |
| 1881 | const need_realloc = !align_ok or new_atom_size > atom.capacity(self); | 1902 | const need_realloc = !align_ok or new_atom_size > atom.capacity(self); |
| 1882 | if (!need_realloc) return sym.n_value; | 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 | try self.locals.ensureUnusedCapacity(self.base.allocator, 1); | 1908 | try self.locals.ensureUnusedCapacity(self.base.allocator, 1); |
| 1888 | 1909 | ||
| 1889 | const index = blk: { | 1910 | const index = blk: { |
| ... | @@ -1975,16 +1996,6 @@ pub fn allocateStubEntry(self: *MachO, target: SymbolWithLoc) !u32 { | ... | @@ -1975,16 +1996,6 @@ pub fn allocateStubEntry(self: *MachO, target: SymbolWithLoc) !u32 { |
| 1975 | return index; | 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 | pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void { | 1999 | pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void { |
| 1989 | if (build_options.skip_non_native and builtin.object_format != .macho) { | 2000 | if (build_options.skip_non_native and builtin.object_format != .macho) { |
| 1990 | @panic("Attempted to compile for object format that was disabled by build configuration"); | 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,8 +2008,12 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv |
| 1997 | 2008 | ||
| 1998 | const decl_index = func.owner_decl; | 2009 | const decl_index = func.owner_decl; |
| 1999 | const decl = module.declPtr(decl_index); | 2010 | const decl = module.declPtr(decl_index); |
| 2011 | |||
| 2012 | const atom_index = try self.getOrCreateAtomForDecl(decl_index); | ||
| 2000 | self.freeUnnamedConsts(decl_index); | 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 | var code_buffer = std.ArrayList(u8).init(self.base.allocator); | 2018 | var code_buffer = std.ArrayList(u8).init(self.base.allocator); |
| 2004 | defer code_buffer.deinit(); | 2019 | defer code_buffer.deinit(); |
| ... | @@ -2017,7 +2032,7 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv | ... | @@ -2017,7 +2032,7 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv |
| 2017 | try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .none); | 2032 | try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .none); |
| 2018 | 2033 | ||
| 2019 | const code = switch (res) { | 2034 | const code = switch (res) { |
| 2020 | .appended => code_buffer.items, | 2035 | .ok => code_buffer.items, |
| 2021 | .fail => |em| { | 2036 | .fail => |em| { |
| 2022 | decl.analysis = .codegen_failure; | 2037 | decl.analysis = .codegen_failure; |
| 2023 | try module.failed_decls.put(module.gpa, decl_index, em); | 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,13 +2043,7 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv |
| 2028 | const addr = try self.updateDeclCode(decl_index, code); | 2043 | const addr = try self.updateDeclCode(decl_index, code); |
| 2029 | 2044 | ||
| 2030 | if (decl_state) |*ds| { | 2045 | if (decl_state) |*ds| { |
| 2031 | try self.d_sym.?.dwarf.commitDeclState( | 2046 | try self.d_sym.?.dwarf.commitDeclState(module, decl_index, addr, atom.size, ds); |
| 2032 | module, | ||
| 2033 | decl_index, | ||
| 2034 | addr, | ||
| 2035 | decl.link.macho.size, | ||
| 2036 | ds, | ||
| 2037 | ); | ||
| 2038 | } | 2047 | } |
| 2039 | 2048 | ||
| 2040 | // Since we updated the vaddr and the size, each corresponding export symbol also | 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,21 +2078,13 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu |
| 2069 | 2078 | ||
| 2070 | log.debug("allocating symbol indexes for {?s}", .{name}); | 2079 | log.debug("allocating symbol indexes for {?s}", .{name}); |
| 2071 | 2080 | ||
| 2072 | const atom = try gpa.create(Atom); | 2081 | const atom_index = try self.createAtom(); |
| 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); | ||
| 2080 | 2082 | ||
| 2081 | const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), typed_value, &code_buffer, .none, .{ | 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 | const code = switch (res) { | 2086 | const code = switch (res) { |
| 2085 | .externally_managed => |x| x, | 2087 | .ok => code_buffer.items, |
| 2086 | .appended => code_buffer.items, | ||
| 2087 | .fail => |em| { | 2088 | .fail => |em| { |
| 2088 | decl.analysis = .codegen_failure; | 2089 | decl.analysis = .codegen_failure; |
| 2089 | try module.failed_decls.put(module.gpa, decl_index, em); | 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,26 +2094,27 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu |
| 2093 | }; | 2094 | }; |
| 2094 | 2095 | ||
| 2095 | const required_alignment = typed_value.ty.abiAlignment(self.base.options.target); | 2096 | const required_alignment = typed_value.ty.abiAlignment(self.base.options.target); |
| 2097 | const atom = self.getAtomPtr(atom_index); | ||
| 2096 | atom.size = code.len; | 2098 | atom.size = code.len; |
| 2097 | atom.alignment = required_alignment; | 2099 | atom.alignment = required_alignment; |
| 2098 | // TODO: work out logic for disambiguating functions from function pointers | 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 | const sect_id = self.data_const_section_index.?; | 2102 | const sect_id = self.data_const_section_index.?; |
| 2101 | const symbol = atom.getSymbolPtr(self); | 2103 | const symbol = atom.getSymbolPtr(self); |
| 2102 | symbol.n_strx = name_str_index; | 2104 | symbol.n_strx = name_str_index; |
| 2103 | symbol.n_type = macho.N_SECT; | 2105 | symbol.n_type = macho.N_SECT; |
| 2104 | symbol.n_sect = sect_id + 1; | 2106 | symbol.n_sect = sect_id + 1; |
| 2105 | symbol.n_value = try self.allocateAtom(atom, code.len, required_alignment); | 2107 | symbol.n_value = try self.allocateAtom(atom_index, code.len, required_alignment); |
| 2106 | errdefer self.freeAtom(atom); | 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 | log.debug("allocated atom for {?s} at 0x{x}", .{ name, symbol.n_value }); | 2112 | log.debug("allocated atom for {?s} at 0x{x}", .{ name, symbol.n_value }); |
| 2111 | log.debug(" (required alignment 0x{x})", .{required_alignment}); | 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 | pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index) !void { | 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,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 | var code_buffer = std.ArrayList(u8).init(self.base.allocator); | 2146 | var code_buffer = std.ArrayList(u8).init(self.base.allocator); |
| 2143 | defer code_buffer.deinit(); | 2147 | defer code_buffer.deinit(); |
| ... | @@ -2156,19 +2160,18 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index) | ... | @@ -2156,19 +2160,18 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index) |
| 2156 | }, &code_buffer, .{ | 2160 | }, &code_buffer, .{ |
| 2157 | .dwarf = ds, | 2161 | .dwarf = ds, |
| 2158 | }, .{ | 2162 | }, .{ |
| 2159 | .parent_atom_index = decl.link.macho.sym_index, | 2163 | .parent_atom_index = atom.getSymbolIndex().?, |
| 2160 | }) | 2164 | }) |
| 2161 | else | 2165 | else |
| 2162 | try codegen.generateSymbol(&self.base, decl.srcLoc(), .{ | 2166 | try codegen.generateSymbol(&self.base, decl.srcLoc(), .{ |
| 2163 | .ty = decl.ty, | 2167 | .ty = decl.ty, |
| 2164 | .val = decl_val, | 2168 | .val = decl_val, |
| 2165 | }, &code_buffer, .none, .{ | 2169 | }, &code_buffer, .none, .{ |
| 2166 | .parent_atom_index = decl.link.macho.sym_index, | 2170 | .parent_atom_index = atom.getSymbolIndex().?, |
| 2167 | }); | 2171 | }); |
| 2168 | 2172 | ||
| 2169 | const code = switch (res) { | 2173 | const code = switch (res) { |
| 2170 | .externally_managed => |x| x, | 2174 | .ok => code_buffer.items, |
| 2171 | .appended => code_buffer.items, | ||
| 2172 | .fail => |em| { | 2175 | .fail => |em| { |
| 2173 | decl.analysis = .codegen_failure; | 2176 | decl.analysis = .codegen_failure; |
| 2174 | try module.failed_decls.put(module.gpa, decl_index, em); | 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,13 +2181,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index) |
| 2178 | const addr = try self.updateDeclCode(decl_index, code); | 2181 | const addr = try self.updateDeclCode(decl_index, code); |
| 2179 | 2182 | ||
| 2180 | if (decl_state) |*ds| { | 2183 | if (decl_state) |*ds| { |
| 2181 | try self.d_sym.?.dwarf.commitDeclState( | 2184 | try self.d_sym.?.dwarf.commitDeclState(module, decl_index, addr, atom.size, ds); |
| 2182 | module, | ||
| 2183 | decl_index, | ||
| 2184 | addr, | ||
| 2185 | decl.link.macho.size, | ||
| 2186 | ds, | ||
| 2187 | ); | ||
| 2188 | } | 2185 | } |
| 2189 | 2186 | ||
| 2190 | // Since we updated the vaddr and the size, each corresponding export symbol also | 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,7 +2189,20 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index) |
| 2192 | try self.updateDeclExports(module, decl_index, module.getDeclExports(decl_index)); | 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 | const ty = decl.ty; | 2206 | const ty = decl.ty; |
| 2197 | const val = decl.val; | 2207 | const val = decl.val; |
| 2198 | const zig_ty = ty.zigTypeTag(); | 2208 | const zig_ty = ty.zigTypeTag(); |
| ... | @@ -2339,17 +2349,15 @@ fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []const u8) | ... | @@ -2339,17 +2349,15 @@ fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []const u8) |
| 2339 | const decl = mod.declPtr(decl_index); | 2349 | const decl = mod.declPtr(decl_index); |
| 2340 | 2350 | ||
| 2341 | const required_alignment = decl.getAlignment(self.base.options.target); | 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 | const sym_name = try decl.getFullyQualifiedName(mod); | 2353 | const sym_name = try decl.getFullyQualifiedName(mod); |
| 2345 | defer self.base.allocator.free(sym_name); | 2354 | defer self.base.allocator.free(sym_name); |
| 2346 | 2355 | ||
| 2347 | const atom = &decl.link.macho; | 2356 | const decl_metadata = self.decls.get(decl_index).?; |
| 2348 | const decl_ptr = self.decls.getPtr(decl_index).?; | 2357 | const atom_index = decl_metadata.atom; |
| 2349 | if (decl_ptr.* == null) { | 2358 | const atom = self.getAtom(atom_index); |
| 2350 | decl_ptr.* = self.getDeclOutputSection(decl); | 2359 | const sym_index = atom.getSymbolIndex().?; |
| 2351 | } | 2360 | const sect_id = decl_metadata.section; |
| 2352 | const sect_id = decl_ptr.*.?; | ||
| 2353 | const code_len = code.len; | 2361 | const code_len = code.len; |
| 2354 | 2362 | ||
| 2355 | if (atom.size != 0) { | 2363 | if (atom.size != 0) { |
| ... | @@ -2359,31 +2367,31 @@ fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []const u8) | ... | @@ -2359,31 +2367,31 @@ fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []const u8) |
| 2359 | sym.n_sect = sect_id + 1; | 2367 | sym.n_sect = sect_id + 1; |
| 2360 | sym.n_desc = 0; | 2368 | sym.n_desc = 0; |
| 2361 | 2369 | ||
| 2362 | const capacity = decl.link.macho.capacity(self); | 2370 | const capacity = atom.capacity(self); |
| 2363 | const need_realloc = code_len > capacity or !mem.isAlignedGeneric(u64, sym.n_value, required_alignment); | 2371 | const need_realloc = code_len > capacity or !mem.isAlignedGeneric(u64, sym.n_value, required_alignment); |
| 2364 | 2372 | ||
| 2365 | if (need_realloc) { | 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 | log.debug("growing {s} and moving from 0x{x} to 0x{x}", .{ sym_name, sym.n_value, vaddr }); | 2375 | log.debug("growing {s} and moving from 0x{x} to 0x{x}", .{ sym_name, sym.n_value, vaddr }); |
| 2368 | log.debug(" (required alignment 0x{x})", .{required_alignment}); | 2376 | log.debug(" (required alignment 0x{x})", .{required_alignment}); |
| 2369 | 2377 | ||
| 2370 | if (vaddr != sym.n_value) { | 2378 | if (vaddr != sym.n_value) { |
| 2371 | sym.n_value = vaddr; | 2379 | sym.n_value = vaddr; |
| 2372 | log.debug(" (updating GOT entry)", .{}); | 2380 | log.debug(" (updating GOT entry)", .{}); |
| 2373 | const got_target = SymbolWithLoc{ .sym_index = atom.sym_index, .file = null }; | 2381 | const got_target = SymbolWithLoc{ .sym_index = sym_index, .file = null }; |
| 2374 | const got_atom = self.getGotAtomForSymbol(got_target).?; | 2382 | const got_atom_index = self.getGotAtomIndexForSymbol(got_target).?; |
| 2375 | self.markRelocsDirtyByTarget(got_target); | 2383 | self.markRelocsDirtyByTarget(got_target); |
| 2376 | try self.writePtrWidthAtom(got_atom); | 2384 | try self.writePtrWidthAtom(got_atom_index); |
| 2377 | } | 2385 | } |
| 2378 | } else if (code_len < atom.size) { | 2386 | } else if (code_len < atom.size) { |
| 2379 | self.shrinkAtom(atom, code_len); | 2387 | self.shrinkAtom(atom_index, code_len); |
| 2380 | } else if (atom.next == null) { | 2388 | } else if (atom.next_index == null) { |
| 2381 | const header = &self.sections.items(.header)[sect_id]; | 2389 | const header = &self.sections.items(.header)[sect_id]; |
| 2382 | const segment = self.getSegment(sect_id); | 2390 | const segment = self.getSegment(sect_id); |
| 2383 | const needed_size = (sym.n_value + code_len) - segment.vmaddr; | 2391 | const needed_size = (sym.n_value + code_len) - segment.vmaddr; |
| 2384 | header.size = needed_size; | 2392 | header.size = needed_size; |
| 2385 | } | 2393 | } |
| 2386 | atom.size = code_len; | 2394 | self.getAtomPtr(atom_index).size = code_len; |
| 2387 | } else { | 2395 | } else { |
| 2388 | const name_str_index = try self.strtab.insert(gpa, sym_name); | 2396 | const name_str_index = try self.strtab.insert(gpa, sym_name); |
| 2389 | const sym = atom.getSymbolPtr(self); | 2397 | const sym = atom.getSymbolPtr(self); |
| ... | @@ -2392,32 +2400,32 @@ fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []const u8) | ... | @@ -2392,32 +2400,32 @@ fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []const u8) |
| 2392 | sym.n_sect = sect_id + 1; | 2400 | sym.n_sect = sect_id + 1; |
| 2393 | sym.n_desc = 0; | 2401 | sym.n_desc = 0; |
| 2394 | 2402 | ||
| 2395 | const vaddr = try self.allocateAtom(atom, code_len, required_alignment); | 2403 | const vaddr = try self.allocateAtom(atom_index, code_len, required_alignment); |
| 2396 | errdefer self.freeAtom(atom); | 2404 | errdefer self.freeAtom(atom_index); |
| 2397 | 2405 | ||
| 2398 | log.debug("allocated atom for {s} at 0x{x}", .{ sym_name, vaddr }); | 2406 | log.debug("allocated atom for {s} at 0x{x}", .{ sym_name, vaddr }); |
| 2399 | log.debug(" (required alignment 0x{x})", .{required_alignment}); | 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 | sym.n_value = vaddr; | 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 | const got_index = try self.allocateGotEntry(got_target); | 2413 | const got_index = try self.allocateGotEntry(got_target); |
| 2406 | const got_atom = try self.createGotAtom(got_target); | 2414 | const got_atom_index = try self.createGotAtom(got_target); |
| 2407 | self.got_entries.items[got_index].sym_index = got_atom.sym_index; | 2415 | const got_atom = self.getAtom(got_atom_index); |
| 2408 | try self.writePtrWidthAtom(got_atom); | 2416 | self.got_entries.items[got_index].sym_index = got_atom.getSymbolIndex().?; |
| 2417 | try self.writePtrWidthAtom(got_atom_index); | ||
| 2409 | } | 2418 | } |
| 2410 | 2419 | ||
| 2411 | self.markRelocsDirtyByTarget(atom.getSymbolWithLoc()); | 2420 | self.markRelocsDirtyByTarget(atom.getSymbolWithLoc()); |
| 2412 | try self.writeAtom(atom, code); | 2421 | try self.writeAtom(atom_index, code); |
| 2413 | 2422 | ||
| 2414 | return atom.getSymbol(self).n_value; | 2423 | return atom.getSymbol(self).n_value; |
| 2415 | } | 2424 | } |
| 2416 | 2425 | ||
| 2417 | pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl: *const Module.Decl) !void { | 2426 | pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl_index: Module.Decl.Index) !void { |
| 2418 | _ = module; | ||
| 2419 | if (self.d_sym) |*d_sym| { | 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,14 +2442,17 @@ pub fn updateDeclExports( |
| 2434 | if (self.llvm_object) |llvm_object| | 2442 | if (self.llvm_object) |llvm_object| |
| 2435 | return llvm_object.updateDeclExports(module, decl_index, exports); | 2443 | return llvm_object.updateDeclExports(module, decl_index, exports); |
| 2436 | } | 2444 | } |
| 2445 | |||
| 2437 | const tracy = trace(@src()); | 2446 | const tracy = trace(@src()); |
| 2438 | defer tracy.end(); | 2447 | defer tracy.end(); |
| 2439 | 2448 | ||
| 2440 | const gpa = self.base.allocator; | 2449 | const gpa = self.base.allocator; |
| 2441 | 2450 | ||
| 2442 | const decl = module.declPtr(decl_index); | 2451 | const decl = module.declPtr(decl_index); |
| 2443 | if (decl.link.macho.sym_index == 0) return; | 2452 | const atom_index = try self.getOrCreateAtomForDecl(decl_index); |
| 2444 | const decl_sym = decl.link.macho.getSymbol(self); | 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 | for (exports) |exp| { | 2457 | for (exports) |exp| { |
| 2447 | const exp_name = try std.fmt.allocPrint(gpa, "_{s}", .{exp.options.name}); | 2458 | const exp_name = try std.fmt.allocPrint(gpa, "_{s}", .{exp.options.name}); |
| ... | @@ -2479,9 +2490,9 @@ pub fn updateDeclExports( | ... | @@ -2479,9 +2490,9 @@ pub fn updateDeclExports( |
| 2479 | continue; | 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 | const sym_index = try self.allocateSymbol(); | 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 | break :blk sym_index; | 2496 | break :blk sym_index; |
| 2486 | }; | 2497 | }; |
| 2487 | const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = null }; | 2498 | const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = null }; |
| ... | @@ -2529,16 +2540,18 @@ pub fn updateDeclExports( | ... | @@ -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 | if (self.llvm_object) |_| return; | 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 | const gpa = self.base.allocator; | 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 | const sym = self.getSymbolPtr(sym_loc); | 2553 | const sym = self.getSymbolPtr(sym_loc); |
| 2540 | const sym_name = self.getSymbolName(sym_loc); | 2554 | log.debug("deleting export '{s}'", .{exp_name}); |
| 2541 | log.debug("deleting export '{s}'", .{sym_name}); | ||
| 2542 | assert(sym.sect() and sym.ext()); | 2555 | assert(sym.sect() and sym.ext()); |
| 2543 | sym.* = .{ | 2556 | sym.* = .{ |
| 2544 | .n_strx = 0, | 2557 | .n_strx = 0, |
| ... | @@ -2547,9 +2560,9 @@ pub fn deleteExport(self: *MachO, exp: Export) void { | ... | @@ -2547,9 +2560,9 @@ pub fn deleteExport(self: *MachO, exp: Export) void { |
| 2547 | .n_desc = 0, | 2560 | .n_desc = 0, |
| 2548 | .n_value = 0, | 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 | defer gpa.free(entry.key); | 2566 | defer gpa.free(entry.key); |
| 2554 | self.globals_free_list.append(gpa, entry.value) catch {}; | 2567 | self.globals_free_list.append(gpa, entry.value) catch {}; |
| 2555 | self.globals.items[entry.value] = .{ | 2568 | self.globals.items[entry.value] = .{ |
| ... | @@ -2557,17 +2570,8 @@ pub fn deleteExport(self: *MachO, exp: Export) void { | ... | @@ -2557,17 +2570,8 @@ pub fn deleteExport(self: *MachO, exp: Export) void { |
| 2557 | .file = null, | 2570 | .file = null, |
| 2558 | }; | 2571 | }; |
| 2559 | } | 2572 | } |
| 2560 | } | ||
| 2561 | 2573 | ||
| 2562 | fn freeRelocationsForAtom(self: *MachO, atom: *Atom) void { | 2574 | sym_index.* = 0; |
| 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); | ||
| 2571 | } | 2575 | } |
| 2572 | 2576 | ||
| 2573 | fn freeUnnamedConsts(self: *MachO, decl_index: Module.Decl.Index) void { | 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,11 +2579,6 @@ fn freeUnnamedConsts(self: *MachO, decl_index: Module.Decl.Index) void { |
| 2575 | const unnamed_consts = self.unnamed_const_atoms.getPtr(decl_index) orelse return; | 2579 | const unnamed_consts = self.unnamed_const_atoms.getPtr(decl_index) orelse return; |
| 2576 | for (unnamed_consts.items) |atom| { | 2580 | for (unnamed_consts.items) |atom| { |
| 2577 | self.freeAtom(atom); | 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 | unnamed_consts.clearAndFree(gpa); | 2583 | unnamed_consts.clearAndFree(gpa); |
| 2585 | } | 2584 | } |
| ... | @@ -2593,67 +2592,37 @@ pub fn freeDecl(self: *MachO, decl_index: Module.Decl.Index) void { | ... | @@ -2593,67 +2592,37 @@ pub fn freeDecl(self: *MachO, decl_index: Module.Decl.Index) void { |
| 2593 | 2592 | ||
| 2594 | log.debug("freeDecl {*}", .{decl}); | 2593 | log.debug("freeDecl {*}", .{decl}); |
| 2595 | 2594 | ||
| 2596 | const kv = self.decls.fetchSwapRemove(decl_index); | 2595 | if (self.decls.fetchSwapRemove(decl_index)) |const_kv| { |
| 2597 | if (kv.?.value) |_| { | 2596 | var kv = const_kv; |
| 2598 | self.freeAtom(&decl.link.macho); | 2597 | self.freeAtom(kv.value.atom); |
| 2599 | self.freeUnnamedConsts(decl_index); | 2598 | self.freeUnnamedConsts(decl_index); |
| 2600 | } | 2599 | kv.value.exports.deinit(self.base.allocator); |
| 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; | ||
| 2629 | } | 2600 | } |
| 2630 | 2601 | ||
| 2631 | if (self.d_sym) |*d_sym| { | 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 | pub fn getDeclVAddr(self: *MachO, decl_index: Module.Decl.Index, reloc_info: File.RelocInfo) !u64 { | 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 | assert(self.llvm_object == null); | 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 }).?; | 2610 | const this_atom_index = try self.getOrCreateAtomForDecl(decl_index); |
| 2644 | try atom.addRelocation(self, .{ | 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 | .type = switch (self.base.options.target.cpu.arch) { | 2614 | .type = switch (self.base.options.target.cpu.arch) { |
| 2646 | .aarch64 => @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED), | 2615 | .aarch64 => @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED), |
| 2647 | .x86_64 => @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED), | 2616 | .x86_64 => @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED), |
| 2648 | else => unreachable, | 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 | .offset = @intCast(u32, reloc_info.offset), | 2620 | .offset = @intCast(u32, reloc_info.offset), |
| 2652 | .addend = reloc_info.addend, | 2621 | .addend = reloc_info.addend, |
| 2653 | .pcrel = false, | 2622 | .pcrel = false, |
| 2654 | .length = 3, | 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 | return 0; | 2627 | return 0; |
| 2659 | } | 2628 | } |
| ... | @@ -2885,34 +2854,36 @@ fn moveSectionInVirtualMemory(self: *MachO, sect_id: u8, needed_size: u64) !void | ... | @@ -2885,34 +2854,36 @@ fn moveSectionInVirtualMemory(self: *MachO, sect_id: u8, needed_size: u64) !void |
| 2885 | // TODO: enforce order by increasing VM addresses in self.sections container. | 2854 | // TODO: enforce order by increasing VM addresses in self.sections container. |
| 2886 | for (self.sections.items(.header)[sect_id + 1 ..]) |*next_header, next_sect_id| { | 2855 | for (self.sections.items(.header)[sect_id + 1 ..]) |*next_header, next_sect_id| { |
| 2887 | const index = @intCast(u8, sect_id + 1 + next_sect_id); | 2856 | const index = @intCast(u8, sect_id + 1 + next_sect_id); |
| 2888 | const maybe_last_atom = &self.sections.items(.last_atom)[index]; | ||
| 2889 | const next_segment = self.getSegmentPtr(index); | 2857 | const next_segment = self.getSegmentPtr(index); |
| 2890 | next_header.addr += diff; | 2858 | next_header.addr += diff; |
| 2891 | next_segment.vmaddr += diff; | 2859 | next_segment.vmaddr += diff; |
| 2892 | 2860 | ||
| 2893 | if (maybe_last_atom.*) |last_atom| { | 2861 | const maybe_last_atom_index = &self.sections.items(.last_atom_index)[index]; |
| 2894 | var atom = last_atom; | 2862 | if (maybe_last_atom_index.*) |last_atom_index| { |
| 2863 | var atom_index = last_atom_index; | ||
| 2895 | while (true) { | 2864 | while (true) { |
| 2865 | const atom = self.getAtom(atom_index); | ||
| 2896 | const sym = atom.getSymbolPtr(self); | 2866 | const sym = atom.getSymbolPtr(self); |
| 2897 | sym.n_value += diff; | 2867 | sym.n_value += diff; |
| 2898 | 2868 | ||
| 2899 | if (atom.prev) |prev| { | 2869 | if (atom.prev_index) |prev_index| { |
| 2900 | atom = prev; | 2870 | atom_index = prev_index; |
| 2901 | } else break; | 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 | const tracy = trace(@src()); | 2878 | const tracy = trace(@src()); |
| 2909 | defer tracy.end(); | 2879 | defer tracy.end(); |
| 2910 | 2880 | ||
| 2881 | const atom = self.getAtom(atom_index); | ||
| 2911 | const sect_id = atom.getSymbol(self).n_sect - 1; | 2882 | const sect_id = atom.getSymbol(self).n_sect - 1; |
| 2912 | const segment = self.getSegmentPtr(sect_id); | 2883 | const segment = self.getSegmentPtr(sect_id); |
| 2913 | const header = &self.sections.items(.header)[sect_id]; | 2884 | const header = &self.sections.items(.header)[sect_id]; |
| 2914 | const free_list = &self.sections.items(.free_list)[sect_id]; | 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 | const requires_padding = blk: { | 2887 | const requires_padding = blk: { |
| 2917 | if (!header.isCode()) break :blk false; | 2888 | if (!header.isCode()) break :blk false; |
| 2918 | if (header.isSymbolStubs()) break :blk false; | 2889 | if (header.isSymbolStubs()) break :blk false; |
| ... | @@ -2926,7 +2897,7 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) ! | ... | @@ -2926,7 +2897,7 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) ! |
| 2926 | // It would be simpler to do it inside the for loop below, but that would cause a | 2897 | // It would be simpler to do it inside the for loop below, but that would cause a |
| 2927 | // problem if an error was returned later in the function. So this action | 2898 | // problem if an error was returned later in the function. So this action |
| 2928 | // is actually carried out at the end of the function, when errors are no longer possible. | 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 | var free_list_removal: ?usize = null; | 2901 | var free_list_removal: ?usize = null; |
| 2931 | 2902 | ||
| 2932 | // First we look for an appropriately sized free list node. | 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,7 +2905,8 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) ! |
| 2934 | var vaddr = blk: { | 2905 | var vaddr = blk: { |
| 2935 | var i: usize = 0; | 2906 | var i: usize = 0; |
| 2936 | while (i < free_list.items.len) { | 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 | // We now have a pointer to a live atom that has too much capacity. | 2910 | // We now have a pointer to a live atom that has too much capacity. |
| 2939 | // Is it enough that we could fit this new atom? | 2911 | // Is it enough that we could fit this new atom? |
| 2940 | const sym = big_atom.getSymbol(self); | 2912 | const sym = big_atom.getSymbol(self); |
| ... | @@ -2962,30 +2934,35 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) ! | ... | @@ -2962,30 +2934,35 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) ! |
| 2962 | const keep_free_list_node = remaining_capacity >= min_text_capacity; | 2934 | const keep_free_list_node = remaining_capacity >= min_text_capacity; |
| 2963 | 2935 | ||
| 2964 | // Set up the metadata to be updated, after errors are no longer possible. | 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 | if (!keep_free_list_node) { | 2938 | if (!keep_free_list_node) { |
| 2967 | free_list_removal = i; | 2939 | free_list_removal = i; |
| 2968 | } | 2940 | } |
| 2969 | break :blk new_start_vaddr; | 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 | const last_symbol = last.getSymbol(self); | 2944 | const last_symbol = last.getSymbol(self); |
| 2972 | const ideal_capacity = if (requires_padding) padToIdeal(last.size) else last.size; | 2945 | const ideal_capacity = if (requires_padding) padToIdeal(last.size) else last.size; |
| 2973 | const ideal_capacity_end_vaddr = last_symbol.n_value + ideal_capacity; | 2946 | const ideal_capacity_end_vaddr = last_symbol.n_value + ideal_capacity; |
| 2974 | const new_start_vaddr = mem.alignForwardGeneric(u64, ideal_capacity_end_vaddr, alignment); | 2947 | const new_start_vaddr = mem.alignForwardGeneric(u64, ideal_capacity_end_vaddr, alignment); |
| 2975 | atom_placement = last; | 2948 | atom_placement = last_index; |
| 2976 | break :blk new_start_vaddr; | 2949 | break :blk new_start_vaddr; |
| 2977 | } else { | 2950 | } else { |
| 2978 | break :blk mem.alignForwardGeneric(u64, segment.vmaddr, alignment); | 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 | if (expand_section) { | 2959 | if (expand_section) { |
| 2984 | const sect_capacity = self.allocatedSize(header.offset); | 2960 | const sect_capacity = self.allocatedSize(header.offset); |
| 2985 | const needed_size = (vaddr + new_atom_size) - segment.vmaddr; | 2961 | const needed_size = (vaddr + new_atom_size) - segment.vmaddr; |
| 2986 | if (needed_size > sect_capacity) { | 2962 | if (needed_size > sect_capacity) { |
| 2987 | const new_offset = self.findFreeSpace(needed_size, self.page_size); | 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 | const sym = last_atom.getSymbol(self); | 2966 | const sym = last_atom.getSymbol(self); |
| 2990 | break :blk (sym.n_value + last_atom.size) - segment.vmaddr; | 2967 | break :blk (sym.n_value + last_atom.size) - segment.vmaddr; |
| 2991 | } else 0; | 2968 | } else 0; |
| ... | @@ -3017,7 +2994,7 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) ! | ... | @@ -3017,7 +2994,7 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) ! |
| 3017 | header.size = needed_size; | 2994 | header.size = needed_size; |
| 3018 | segment.filesize = mem.alignForwardGeneric(u64, needed_size, self.page_size); | 2995 | segment.filesize = mem.alignForwardGeneric(u64, needed_size, self.page_size); |
| 3019 | segment.vmsize = mem.alignForwardGeneric(u64, needed_size, self.page_size); | 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 | self.segment_table_dirty = true; | 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,21 +3003,31 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) ! |
| 3026 | if (header.@"align" < align_pow) { | 3003 | if (header.@"align" < align_pow) { |
| 3027 | header.@"align" = align_pow; | 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| { | 3012 | if (atom.prev_index) |prev_index| { |
| 3031 | prev.next = atom.next; | 3013 | const prev = self.getAtomPtr(prev_index); |
| 3014 | prev.next_index = atom.next_index; | ||
| 3032 | } | 3015 | } |
| 3033 | if (atom.next) |next| { | 3016 | if (atom.next_index) |next_index| { |
| 3034 | next.prev = atom.prev; | 3017 | const next = self.getAtomPtr(next_index); |
| 3018 | next.prev_index = atom.prev_index; | ||
| 3035 | } | 3019 | } |
| 3036 | 3020 | ||
| 3037 | if (atom_placement) |big_atom| { | 3021 | if (atom_placement) |big_atom_index| { |
| 3038 | atom.prev = big_atom; | 3022 | const big_atom = self.getAtomPtr(big_atom_index); |
| 3039 | atom.next = big_atom.next; | 3023 | const atom_ptr = self.getAtomPtr(atom_index); |
| 3040 | big_atom.next = atom; | 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 | } else { | 3027 | } else { |
| 3042 | atom.prev = null; | 3028 | const atom_ptr = self.getAtomPtr(atom_index); |
| 3043 | atom.next = null; | 3029 | atom_ptr.prev_index = null; |
| 3030 | atom_ptr.next_index = null; | ||
| 3044 | } | 3031 | } |
| 3045 | if (free_list_removal) |i| { | 3032 | if (free_list_removal) |i| { |
| 3046 | _ = free_list.swapRemove(i); | 3033 | _ = free_list.swapRemove(i); |
| ... | @@ -3180,8 +3167,9 @@ fn collectRebaseData(self: *MachO, rebase: *Rebase) !void { | ... | @@ -3180,8 +3167,9 @@ fn collectRebaseData(self: *MachO, rebase: *Rebase) !void { |
| 3180 | const gpa = self.base.allocator; | 3167 | const gpa = self.base.allocator; |
| 3181 | const slice = self.sections.slice(); | 3168 | const slice = self.sections.slice(); |
| 3182 | 3169 | ||
| 3183 | for (self.rebases.keys()) |atom, i| { | 3170 | for (self.rebases.keys()) |atom_index, i| { |
| 3184 | log.debug(" ATOM(%{d}, '{s}')", .{ atom.sym_index, atom.getName(self) }); | 3171 | const atom = self.getAtom(atom_index); |
| 3172 | log.debug(" ATOM(%{?d}, '{s}')", .{ atom.getSymbolIndex(), atom.getName(self) }); | ||
| 3185 | 3173 | ||
| 3186 | const sym = atom.getSymbol(self); | 3174 | const sym = atom.getSymbol(self); |
| 3187 | const segment_index = slice.items(.segment_index)[sym.n_sect - 1]; | 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,8 +3197,9 @@ fn collectBindData(self: *MachO, bind: anytype, raw_bindings: anytype) !void { |
| 3209 | const gpa = self.base.allocator; | 3197 | const gpa = self.base.allocator; |
| 3210 | const slice = self.sections.slice(); | 3198 | const slice = self.sections.slice(); |
| 3211 | 3199 | ||
| 3212 | for (raw_bindings.keys()) |atom, i| { | 3200 | for (raw_bindings.keys()) |atom_index, i| { |
| 3213 | log.debug(" ATOM(%{d}, '{s}')", .{ atom.sym_index, atom.getName(self) }); | 3201 | const atom = self.getAtom(atom_index); |
| 3202 | log.debug(" ATOM(%{?d}, '{s}')", .{ atom.getSymbolIndex(), atom.getName(self) }); | ||
| 3214 | 3203 | ||
| 3215 | const sym = atom.getSymbol(self); | 3204 | const sym = atom.getSymbol(self); |
| 3216 | const segment_index = slice.items(.segment_index)[sym.n_sect - 1]; | 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,7 +3373,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, lazy_bind: LazyBind) !void |
| 3384 | if (lazy_bind.size() == 0) return; | 3373 | if (lazy_bind.size() == 0) return; |
| 3385 | 3374 | ||
| 3386 | const stub_helper_section_index = self.stub_helper_section_index.?; | 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 | const section = self.sections.get(stub_helper_section_index); | 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,10 +3383,11 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, lazy_bind: LazyBind) !void |
| 3394 | else => unreachable, | 3383 | else => unreachable, |
| 3395 | }; | 3384 | }; |
| 3396 | const header = section.header; | 3385 | const header = section.header; |
| 3397 | var atom = section.last_atom.?; | 3386 | var atom_index = section.last_atom_index.?; |
| 3398 | 3387 | ||
| 3399 | var index: usize = lazy_bind.offsets.items.len; | 3388 | var index: usize = lazy_bind.offsets.items.len; |
| 3400 | while (index > 0) : (index -= 1) { | 3389 | while (index > 0) : (index -= 1) { |
| 3390 | const atom = self.getAtom(atom_index); | ||
| 3401 | const sym = atom.getSymbol(self); | 3391 | const sym = atom.getSymbol(self); |
| 3402 | const file_offset = header.offset + sym.n_value - header.addr + stub_offset; | 3392 | const file_offset = header.offset + sym.n_value - header.addr + stub_offset; |
| 3403 | const bind_offset = lazy_bind.offsets.items[index - 1]; | 3393 | const bind_offset = lazy_bind.offsets.items[index - 1]; |
| ... | @@ -3410,7 +3400,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, lazy_bind: LazyBind) !void | ... | @@ -3410,7 +3400,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, lazy_bind: LazyBind) !void |
| 3410 | 3400 | ||
| 3411 | try self.base.file.?.pwriteAll(mem.asBytes(&bind_offset), file_offset); | 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,25 +3843,35 @@ pub fn getOrPutGlobalPtr(self: *MachO, name: []const u8) !GetOrPutGlobalPtrResul |
| 3853 | return GetOrPutGlobalPtrResult{ .found_existing = false, .value_ptr = ptr }; | 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 | /// Returns atom if there is an atom referenced by the symbol described by `sym_with_loc` descriptor. | 3856 | /// Returns atom if there is an atom referenced by the symbol described by `sym_with_loc` descriptor. |
| 3857 | /// Returns null on failure. | 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 | assert(sym_with_loc.file == null); | 3859 | assert(sym_with_loc.file == null); |
| 3860 | return self.atom_by_index_table.get(sym_with_loc.sym_index); | 3860 | return self.atom_by_index_table.get(sym_with_loc.sym_index); |
| 3861 | } | 3861 | } |
| 3862 | 3862 | ||
| 3863 | /// Returns GOT atom that references `sym_with_loc` if one exists. | 3863 | /// Returns GOT atom that references `sym_with_loc` if one exists. |
| 3864 | /// Returns null otherwise. | 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 | const got_index = self.got_entries_table.get(sym_with_loc) orelse return null; | 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 | /// Returns stubs atom that references `sym_with_loc` if one exists. | 3870 | /// Returns stubs atom that references `sym_with_loc` if one exists. |
| 3871 | /// Returns null otherwise. | 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 | const stubs_index = self.stubs_table.get(sym_with_loc) orelse return null; | 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 | /// Returns symbol location corresponding to the set entrypoint. | 3877 | /// Returns symbol location corresponding to the set entrypoint. |
| ... | @@ -4257,30 +4257,35 @@ pub fn logAtoms(self: *MachO) void { | ... | @@ -4257,30 +4257,35 @@ pub fn logAtoms(self: *MachO) void { |
| 4257 | log.debug("atoms:", .{}); | 4257 | log.debug("atoms:", .{}); |
| 4258 | 4258 | ||
| 4259 | const slice = self.sections.slice(); | 4259 | const slice = self.sections.slice(); |
| 4260 | for (slice.items(.last_atom)) |last, i| { | 4260 | for (slice.items(.last_atom_index)) |last_atom_index, i| { |
| 4261 | var atom = last orelse continue; | 4261 | var atom_index = last_atom_index orelse continue; |
| 4262 | const header = slice.items(.header)[i]; | 4262 | const header = slice.items(.header)[i]; |
| 4263 | 4263 | ||
| 4264 | while (atom.prev) |prev| { | 4264 | while (true) { |
| 4265 | atom = prev; | 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 | log.debug("{s},{s}", .{ header.segName(), header.sectName() }); | 4271 | log.debug("{s},{s}", .{ header.segName(), header.sectName() }); |
| 4269 | 4272 | ||
| 4270 | while (true) { | 4273 | while (true) { |
| 4271 | self.logAtom(atom); | 4274 | self.logAtom(atom_index); |
| 4272 | if (atom.next) |next| { | 4275 | const atom = self.getAtom(atom_index); |
| 4273 | atom = next; | 4276 | if (atom.next_index) |next_index| { |
| 4277 | atom_index = next_index; | ||
| 4274 | } else break; | 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 | const sym = atom.getSymbol(self); | 4285 | const sym = atom.getSymbol(self); |
| 4281 | const sym_name = atom.getName(self); | 4286 | const sym_name = atom.getName(self); |
| 4282 | log.debug(" ATOM(%{d}, '{s}') @ {x} (sizeof({x}), alignof({x})) in object({?d}) in sect({d})", .{ | 4287 | log.debug(" ATOM(%{?d}, '{s}') @ {x} (sizeof({x}), alignof({x})) in object({?d}) in sect({d})", .{ |
| 4283 | atom.sym_index, | 4288 | atom.getSymbolIndex(), |
| 4284 | sym_name, | 4289 | sym_name, |
| 4285 | sym.n_value, | 4290 | sym.n_value, |
| 4286 | atom.size, | 4291 | atom.size, |
src/link/MachO/Atom.zig+54-38| ... | @@ -13,7 +13,6 @@ const trace = @import("../../tracy.zig").trace; | ... | @@ -13,7 +13,6 @@ const trace = @import("../../tracy.zig").trace; |
| 13 | 13 | ||
| 14 | const Allocator = mem.Allocator; | 14 | const Allocator = mem.Allocator; |
| 15 | const Arch = std.Target.Cpu.Arch; | 15 | const Arch = std.Target.Cpu.Arch; |
| 16 | const Dwarf = @import("../Dwarf.zig"); | ||
| 17 | const MachO = @import("../MachO.zig"); | 16 | const MachO = @import("../MachO.zig"); |
| 18 | const Relocation = @import("Relocation.zig"); | 17 | const Relocation = @import("Relocation.zig"); |
| 19 | const SymbolWithLoc = MachO.SymbolWithLoc; | 18 | const SymbolWithLoc = MachO.SymbolWithLoc; |
| ... | @@ -39,10 +38,11 @@ size: u64, | ... | @@ -39,10 +38,11 @@ size: u64, |
| 39 | alignment: u32, | 38 | alignment: u32, |
| 40 | 39 | ||
| 41 | /// Points to the previous and next neighbours | 40 | /// Points to the previous and next neighbours |
| 42 | next: ?*Atom, | 41 | /// TODO use the same trick as with symbols: reserve index 0 as null atom |
| 43 | prev: ?*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 | pub const Binding = struct { | 47 | pub const Binding = struct { |
| 48 | target: SymbolWithLoc, | 48 | target: SymbolWithLoc, |
| ... | @@ -54,15 +54,10 @@ pub const SymbolAtOffset = struct { | ... | @@ -54,15 +54,10 @@ pub const SymbolAtOffset = struct { |
| 54 | offset: u64, | 54 | offset: u64, |
| 55 | }; | 55 | }; |
| 56 | 56 | ||
| 57 | pub const empty = Atom{ | 57 | pub fn getSymbolIndex(self: Atom) ?u32 { |
| 58 | .sym_index = 0, | 58 | if (self.sym_index == 0) return null; |
| 59 | .file = null, | 59 | return self.sym_index; |
| 60 | .size = 0, | 60 | } |
| 61 | .alignment = 0, | ||
| 62 | .prev = null, | ||
| 63 | .next = null, | ||
| 64 | .dbg_info_atom = undefined, | ||
| 65 | }; | ||
| 66 | 61 | ||
| 67 | /// Returns symbol referencing this atom. | 62 | /// Returns symbol referencing this atom. |
| 68 | pub fn getSymbol(self: Atom, macho_file: *MachO) macho.nlist_64 { | 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,20 +66,23 @@ pub fn getSymbol(self: Atom, macho_file: *MachO) macho.nlist_64 { |
| 71 | 66 | ||
| 72 | /// Returns pointer-to-symbol referencing this atom. | 67 | /// Returns pointer-to-symbol referencing this atom. |
| 73 | pub fn getSymbolPtr(self: Atom, macho_file: *MachO) *macho.nlist_64 { | 68 | pub fn getSymbolPtr(self: Atom, macho_file: *MachO) *macho.nlist_64 { |
| 69 | const sym_index = self.getSymbolIndex().?; | ||
| 74 | return macho_file.getSymbolPtr(.{ | 70 | return macho_file.getSymbolPtr(.{ |
| 75 | .sym_index = self.sym_index, | 71 | .sym_index = sym_index, |
| 76 | .file = self.file, | 72 | .file = self.file, |
| 77 | }); | 73 | }); |
| 78 | } | 74 | } |
| 79 | 75 | ||
| 80 | pub fn getSymbolWithLoc(self: Atom) SymbolWithLoc { | 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 | /// Returns the name of this atom. | 81 | /// Returns the name of this atom. |
| 85 | pub fn getName(self: Atom, macho_file: *MachO) []const u8 { | 82 | pub fn getName(self: Atom, macho_file: *MachO) []const u8 { |
| 83 | const sym_index = self.getSymbolIndex().?; | ||
| 86 | return macho_file.getSymbolName(.{ | 84 | return macho_file.getSymbolName(.{ |
| 87 | .sym_index = self.sym_index, | 85 | .sym_index = sym_index, |
| 88 | .file = self.file, | 86 | .file = self.file, |
| 89 | }); | 87 | }); |
| 90 | } | 88 | } |
| ... | @@ -94,7 +92,8 @@ pub fn getName(self: Atom, macho_file: *MachO) []const u8 { | ... | @@ -94,7 +92,8 @@ pub fn getName(self: Atom, macho_file: *MachO) []const u8 { |
| 94 | /// this calculation. | 92 | /// this calculation. |
| 95 | pub fn capacity(self: Atom, macho_file: *MachO) u64 { | 93 | pub fn capacity(self: Atom, macho_file: *MachO) u64 { |
| 96 | const self_sym = self.getSymbol(macho_file); | 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 | const next_sym = next.getSymbol(macho_file); | 97 | const next_sym = next.getSymbol(macho_file); |
| 99 | return next_sym.n_value - self_sym.n_value; | 98 | return next_sym.n_value - self_sym.n_value; |
| 100 | } else { | 99 | } else { |
| ... | @@ -106,7 +105,8 @@ pub fn capacity(self: Atom, macho_file: *MachO) u64 { | ... | @@ -106,7 +105,8 @@ pub fn capacity(self: Atom, macho_file: *MachO) u64 { |
| 106 | 105 | ||
| 107 | pub fn freeListEligible(self: Atom, macho_file: *MachO) bool { | 106 | pub fn freeListEligible(self: Atom, macho_file: *MachO) bool { |
| 108 | // No need to keep a free list node for the last atom. | 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 | const self_sym = self.getSymbol(macho_file); | 110 | const self_sym = self.getSymbol(macho_file); |
| 111 | const next_sym = next.getSymbol(macho_file); | 111 | const next_sym = next.getSymbol(macho_file); |
| 112 | const cap = next_sym.n_value - self_sym.n_value; | 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,19 +116,19 @@ pub fn freeListEligible(self: Atom, macho_file: *MachO) bool { |
| 116 | return surplus >= MachO.min_text_capacity; | 116 | return surplus >= MachO.min_text_capacity; |
| 117 | } | 117 | } |
| 118 | 118 | ||
| 119 | pub fn addRelocation(self: *Atom, macho_file: *MachO, reloc: Relocation) !void { | 119 | pub fn addRelocation(macho_file: *MachO, atom_index: Index, reloc: Relocation) !void { |
| 120 | return self.addRelocations(macho_file, 1, .{reloc}); | 120 | return addRelocations(macho_file, atom_index, 1, .{reloc}); |
| 121 | } | 121 | } |
| 122 | 122 | ||
| 123 | pub fn addRelocations( | 123 | pub fn addRelocations( |
| 124 | self: *Atom, | ||
| 125 | macho_file: *MachO, | 124 | macho_file: *MachO, |
| 125 | atom_index: Index, | ||
| 126 | comptime count: comptime_int, | 126 | comptime count: comptime_int, |
| 127 | relocs: [count]Relocation, | 127 | relocs: [count]Relocation, |
| 128 | ) !void { | 128 | ) !void { |
| 129 | const gpa = macho_file.base.allocator; | 129 | const gpa = macho_file.base.allocator; |
| 130 | const target = macho_file.base.options.target; | 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 | if (!gop.found_existing) { | 132 | if (!gop.found_existing) { |
| 133 | gop.value_ptr.* = .{}; | 133 | gop.value_ptr.* = .{}; |
| 134 | } | 134 | } |
| ... | @@ -142,56 +142,72 @@ pub fn addRelocations( | ... | @@ -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 | const gpa = macho_file.base.allocator; | 146 | const gpa = macho_file.base.allocator; |
| 147 | log.debug(" (adding rebase at offset 0x{x} in %{d})", .{ offset, self.sym_index }); | 147 | const atom = macho_file.getAtom(atom_index); |
| 148 | const gop = try macho_file.rebases.getOrPut(gpa, self); | 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 | if (!gop.found_existing) { | 150 | if (!gop.found_existing) { |
| 150 | gop.value_ptr.* = .{}; | 151 | gop.value_ptr.* = .{}; |
| 151 | } | 152 | } |
| 152 | try gop.value_ptr.append(gpa, offset); | 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 | const gpa = macho_file.base.allocator; | 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 | macho_file.getSymbolName(binding.target), | 160 | macho_file.getSymbolName(binding.target), |
| 159 | binding.offset, | 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 | if (!gop.found_existing) { | 165 | if (!gop.found_existing) { |
| 164 | gop.value_ptr.* = .{}; | 166 | gop.value_ptr.* = .{}; |
| 165 | } | 167 | } |
| 166 | try gop.value_ptr.append(gpa, binding); | 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 | const gpa = macho_file.base.allocator; | 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 | macho_file.getSymbolName(binding.target), | 175 | macho_file.getSymbolName(binding.target), |
| 173 | binding.offset, | 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 | if (!gop.found_existing) { | 180 | if (!gop.found_existing) { |
| 178 | gop.value_ptr.* = .{}; | 181 | gop.value_ptr.* = .{}; |
| 179 | } | 182 | } |
| 180 | try gop.value_ptr.append(gpa, binding); | 183 | try gop.value_ptr.append(gpa, binding); |
| 181 | } | 184 | } |
| 182 | 185 | ||
| 183 | pub fn resolveRelocations(self: *Atom, macho_file: *MachO) !void { | 186 | pub fn resolveRelocations(macho_file: *MachO, atom_index: Index) !void { |
| 184 | const relocs = macho_file.relocs.get(self) orelse return; | 187 | const atom = macho_file.getAtom(atom_index); |
| 185 | const source_sym = self.getSymbol(macho_file); | 188 | const relocs = macho_file.relocs.get(atom_index) orelse return; |
| 189 | const source_sym = atom.getSymbol(macho_file); | ||
| 186 | const source_section = macho_file.sections.get(source_sym.n_sect - 1).header; | 190 | const source_section = macho_file.sections.get(source_sym.n_sect - 1).header; |
| 187 | const file_offset = source_section.offset + source_sym.n_value - source_section.addr; | 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 | for (relocs.items) |*reloc| { | 195 | for (relocs.items) |*reloc| { |
| 192 | if (!reloc.dirty) continue; | 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 | reloc.dirty = false; | 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,11 +82,11 @@ pub fn populateMissingMetadata(self: *DebugSymbols) !void { |
| 82 | } | 82 | } |
| 83 | 83 | ||
| 84 | if (self.debug_str_section_index == null) { | 84 | if (self.debug_str_section_index == null) { |
| 85 | assert(self.dwarf.strtab.items.len == 0); | 85 | assert(self.dwarf.strtab.buffer.items.len == 0); |
| 86 | try self.dwarf.strtab.append(self.allocator, 0); | 86 | try self.dwarf.strtab.buffer.append(self.allocator, 0); |
| 87 | self.debug_str_section_index = try self.allocateSection( | 87 | self.debug_str_section_index = try self.allocateSection( |
| 88 | "__debug_str", | 88 | "__debug_str", |
| 89 | @intCast(u32, self.dwarf.strtab.items.len), | 89 | @intCast(u32, self.dwarf.strtab.buffer.items.len), |
| 90 | 0, | 90 | 0, |
| 91 | ); | 91 | ); |
| 92 | self.debug_string_table_dirty = true; | 92 | self.debug_string_table_dirty = true; |
| ... | @@ -291,10 +291,10 @@ pub fn flushModule(self: *DebugSymbols, macho_file: *MachO) !void { | ... | @@ -291,10 +291,10 @@ pub fn flushModule(self: *DebugSymbols, macho_file: *MachO) !void { |
| 291 | 291 | ||
| 292 | { | 292 | { |
| 293 | const sect_index = self.debug_str_section_index.?; | 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) { | 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.items.len); | 295 | const needed_size = @intCast(u32, self.dwarf.strtab.buffer.items.len); |
| 296 | try self.growSection(sect_index, needed_size, false); | 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 | self.debug_string_table_dirty = false; | 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,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 | switch (macho_file.base.options.target.cpu.arch) { | 33 | switch (macho_file.base.options.target.cpu.arch) { |
| 34 | .aarch64 => switch (@intToEnum(macho.reloc_type_arm64, self.type)) { | 34 | .aarch64 => switch (@intToEnum(macho.reloc_type_arm64, self.type)) { |
| 35 | .ARM64_RELOC_GOT_LOAD_PAGE21, | 35 | .ARM64_RELOC_GOT_LOAD_PAGE21, |
| 36 | .ARM64_RELOC_GOT_LOAD_PAGEOFF12, | 36 | .ARM64_RELOC_GOT_LOAD_PAGEOFF12, |
| 37 | .ARM64_RELOC_POINTER_TO_GOT, | 37 | .ARM64_RELOC_POINTER_TO_GOT, |
| 38 | => return macho_file.getGotAtomForSymbol(self.target), | 38 | => return macho_file.getGotAtomIndexForSymbol(self.target), |
| 39 | else => {}, | 39 | else => {}, |
| 40 | }, | 40 | }, |
| 41 | .x86_64 => switch (@intToEnum(macho.reloc_type_x86_64, self.type)) { | 41 | .x86_64 => switch (@intToEnum(macho.reloc_type_x86_64, self.type)) { |
| 42 | .X86_64_RELOC_GOT, | 42 | .X86_64_RELOC_GOT, |
| 43 | .X86_64_RELOC_GOT_LOAD, | 43 | .X86_64_RELOC_GOT_LOAD, |
| 44 | => return macho_file.getGotAtomForSymbol(self.target), | 44 | => return macho_file.getGotAtomIndexForSymbol(self.target), |
| 45 | else => {}, | 45 | else => {}, |
| 46 | }, | 46 | }, |
| 47 | else => unreachable, | 47 | else => unreachable, |
| 48 | } | 48 | } |
| 49 | if (macho_file.getStubsAtomForSymbol(self.target)) |stubs_atom| return stubs_atom; | 49 | if (macho_file.getStubsAtomIndexForSymbol(self.target)) |stubs_atom| return stubs_atom; |
| 50 | return macho_file.getAtomForSymbol(self.target); | 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 | const arch = macho_file.base.options.target.cpu.arch; | 54 | const arch = macho_file.base.options.target.cpu.arch; |
| 55 | const atom = macho_file.getAtom(atom_index); | ||
| 55 | const source_sym = atom.getSymbol(macho_file); | 56 | const source_sym = atom.getSymbol(macho_file); |
| 56 | const source_addr = source_sym.n_value + self.offset; | 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 | const target_addr = @intCast(i64, target_atom.getSymbol(macho_file).n_value) + self.addend; | 61 | const target_addr = @intCast(i64, target_atom.getSymbol(macho_file).n_value) + self.addend; |
| 60 | 62 | ||
| 61 | log.debug(" ({x}: [() => 0x{x} ({s})) ({s})", .{ | 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,7 +12,7 @@ pub const default_dyld_path: [*:0]const u8 = "/usr/lib/dyld"; |
| 12 | 12 | ||
| 13 | fn calcInstallNameLen(cmd_size: u64, name: []const u8, assume_max_path_len: bool) u64 { | 13 | fn calcInstallNameLen(cmd_size: u64, name: []const u8, assume_max_path_len: bool) u64 { |
| 14 | const darwin_path_max = 1024; | 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 | return mem.alignForwardGeneric(u64, cmd_size + name_len, @alignOf(u64)); | 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,7 +3596,8 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr |
| 3596 | man.hash.addOptionalBytes(options.sysroot); | 3596 | man.hash.addOptionalBytes(options.sysroot); |
| 3597 | try man.addOptionalFile(options.entitlements); | 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 | _ = try man.hit(); | 3601 | _ = try man.hit(); |
| 3601 | digest = man.final(); | 3602 | digest = man.final(); |
| 3602 | 3603 | ||
| ... | @@ -4177,9 +4178,11 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr | ... | @@ -4177,9 +4178,11 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr |
| 4177 | log.debug("failed to save linking hash digest file: {s}", .{@errorName(err)}); | 4178 | log.debug("failed to save linking hash digest file: {s}", .{@errorName(err)}); |
| 4178 | }; | 4179 | }; |
| 4179 | // Again failure here only means an unnecessary cache miss. | 4180 | // Again failure here only means an unnecessary cache miss. |
| 4180 | man.writeManifest() catch |err| { | 4181 | if (man.have_exclusive_lock) { |
| 4181 | log.debug("failed to write cache manifest when linking: {s}", .{@errorName(err)}); | 4182 | man.writeManifest() catch |err| { |
| 4182 | }; | 4183 | log.debug("failed to write cache manifest when linking: {s}", .{@errorName(err)}); |
| 4184 | }; | ||
| 4185 | } | ||
| 4183 | // We hang on to this lock so that the output file path can be used without | 4186 | // We hang on to this lock so that the output file path can be used without |
| 4184 | // other processes clobbering it. | 4187 | // other processes clobbering it. |
| 4185 | macho_file.base.lock = man.toOwnedLock(); | 4188 | macho_file.base.lock = man.toOwnedLock(); |
src/link/Plan9.zig+154-95| ... | @@ -21,14 +21,7 @@ const Allocator = std.mem.Allocator; | ... | @@ -21,14 +21,7 @@ const Allocator = std.mem.Allocator; |
| 21 | const log = std.log.scoped(.link); | 21 | const log = std.log.scoped(.link); |
| 22 | const assert = std.debug.assert; | 22 | const assert = std.debug.assert; |
| 23 | 23 | ||
| 24 | const FnDeclOutput = struct { | 24 | pub const base_tag = .plan9; |
| 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 | }; | ||
| 32 | 25 | ||
| 33 | base: link.File, | 26 | base: link.File, |
| 34 | sixtyfour_bit: bool, | 27 | sixtyfour_bit: bool, |
| ... | @@ -101,6 +94,9 @@ got_index_free_list: std.ArrayListUnmanaged(usize) = .{}, | ... | @@ -101,6 +94,9 @@ got_index_free_list: std.ArrayListUnmanaged(usize) = .{}, |
| 101 | 94 | ||
| 102 | syms_index_free_list: std.ArrayListUnmanaged(usize) = .{}, | 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 | const Reloc = struct { | 100 | const Reloc = struct { |
| 105 | target: Module.Decl.Index, | 101 | target: Module.Decl.Index, |
| 106 | offset: u64, | 102 | offset: u64, |
| ... | @@ -115,6 +111,42 @@ const Bases = struct { | ... | @@ -115,6 +111,42 @@ const Bases = struct { |
| 115 | 111 | ||
| 116 | const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(struct { info: DeclBlock, code: []const u8 })); | 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 | fn getAddr(self: Plan9, addr: u64, t: aout.Sym.Type) u64 { | 150 | fn getAddr(self: Plan9, addr: u64, t: aout.Sym.Type) u64 { |
| 119 | return addr + switch (t) { | 151 | return addr + switch (t) { |
| 120 | .T, .t, .l, .L => self.bases.text, | 152 | .T, .t, .l, .L => self.bases.text, |
| ... | @@ -127,22 +159,6 @@ fn getSymAddr(self: Plan9, s: aout.Sym) u64 { | ... | @@ -127,22 +159,6 @@ fn getSymAddr(self: Plan9, s: aout.Sym) u64 { |
| 127 | return self.getAddr(s.value, s.type); | 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 | pub fn defaultBaseAddrs(arch: std.Target.Cpu.Arch) Bases { | 162 | pub fn defaultBaseAddrs(arch: std.Target.Cpu.Arch) Bases { |
| 147 | return switch (arch) { | 163 | return switch (arch) { |
| 148 | .x86_64 => .{ | 164 | .x86_64 => .{ |
| ... | @@ -164,8 +180,6 @@ pub fn defaultBaseAddrs(arch: std.Target.Cpu.Arch) Bases { | ... | @@ -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 | pub fn createEmpty(gpa: Allocator, options: link.Options) !*Plan9 { | 183 | pub fn createEmpty(gpa: Allocator, options: link.Options) !*Plan9 { |
| 170 | if (options.use_llvm) | 184 | if (options.use_llvm) |
| 171 | return error.LLVMBackendDoesNotSupportPlan9; | 185 | return error.LLVMBackendDoesNotSupportPlan9; |
| ... | @@ -271,7 +285,7 @@ pub fn updateFunc(self: *Plan9, module: *Module, func: *Module.Fn, air: Air, liv | ... | @@ -271,7 +285,7 @@ pub fn updateFunc(self: *Plan9, module: *Module, func: *Module.Fn, air: Air, liv |
| 271 | const decl = module.declPtr(decl_index); | 285 | const decl = module.declPtr(decl_index); |
| 272 | self.freeUnnamedConsts(decl_index); | 286 | self.freeUnnamedConsts(decl_index); |
| 273 | 287 | ||
| 274 | try self.seeDecl(decl_index); | 288 | _ = try self.seeDecl(decl_index); |
| 275 | log.debug("codegen decl {*} ({s})", .{ decl, decl.name }); | 289 | log.debug("codegen decl {*} ({s})", .{ decl, decl.name }); |
| 276 | 290 | ||
| 277 | var code_buffer = std.ArrayList(u8).init(self.base.allocator); | 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,7 +313,7 @@ pub fn updateFunc(self: *Plan9, module: *Module, func: *Module.Fn, air: Air, liv |
| 299 | }, | 313 | }, |
| 300 | ); | 314 | ); |
| 301 | const code = switch (res) { | 315 | const code = switch (res) { |
| 302 | .appended => try code_buffer.toOwnedSlice(), | 316 | .ok => try code_buffer.toOwnedSlice(), |
| 303 | .fail => |em| { | 317 | .fail => |em| { |
| 304 | decl.analysis = .codegen_failure; | 318 | decl.analysis = .codegen_failure; |
| 305 | try module.failed_decls.put(module.gpa, decl_index, em); | 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,11 +327,11 @@ pub fn updateFunc(self: *Plan9, module: *Module, func: *Module.Fn, air: Air, liv |
| 313 | .end_line = end_line, | 327 | .end_line = end_line, |
| 314 | }; | 328 | }; |
| 315 | try self.putFn(decl_index, out); | 329 | try self.putFn(decl_index, out); |
| 316 | return self.updateFinish(decl); | 330 | return self.updateFinish(decl_index); |
| 317 | } | 331 | } |
| 318 | 332 | ||
| 319 | pub fn lowerUnnamedConst(self: *Plan9, tv: TypedValue, decl_index: Module.Decl.Index) !u32 { | 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 | var code_buffer = std.ArrayList(u8).init(self.base.allocator); | 335 | var code_buffer = std.ArrayList(u8).init(self.base.allocator); |
| 322 | defer code_buffer.deinit(); | 336 | defer code_buffer.deinit(); |
| 323 | 337 | ||
| ... | @@ -358,8 +372,7 @@ pub fn lowerUnnamedConst(self: *Plan9, tv: TypedValue, decl_index: Module.Decl.I | ... | @@ -358,8 +372,7 @@ pub fn lowerUnnamedConst(self: *Plan9, tv: TypedValue, decl_index: Module.Decl.I |
| 358 | .parent_atom_index = @enumToInt(decl_index), | 372 | .parent_atom_index = @enumToInt(decl_index), |
| 359 | }); | 373 | }); |
| 360 | const code = switch (res) { | 374 | const code = switch (res) { |
| 361 | .externally_managed => |x| x, | 375 | .ok => code_buffer.items, |
| 362 | .appended => code_buffer.items, | ||
| 363 | .fail => |em| { | 376 | .fail => |em| { |
| 364 | decl.analysis = .codegen_failure; | 377 | decl.analysis = .codegen_failure; |
| 365 | try mod.failed_decls.put(mod.gpa, decl_index, em); | 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,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 | log.debug("codegen decl {*} ({s}) ({d})", .{ decl, decl.name, decl_index }); | 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,8 +416,7 @@ pub fn updateDecl(self: *Plan9, module: *Module, decl_index: Module.Decl.Index) |
| 403 | .parent_atom_index = @enumToInt(decl_index), | 416 | .parent_atom_index = @enumToInt(decl_index), |
| 404 | }); | 417 | }); |
| 405 | const code = switch (res) { | 418 | const code = switch (res) { |
| 406 | .externally_managed => |x| x, | 419 | .ok => code_buffer.items, |
| 407 | .appended => code_buffer.items, | ||
| 408 | .fail => |em| { | 420 | .fail => |em| { |
| 409 | decl.analysis = .codegen_failure; | 421 | decl.analysis = .codegen_failure; |
| 410 | try module.failed_decls.put(module.gpa, decl_index, em); | 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,28 +428,31 @@ pub fn updateDecl(self: *Plan9, module: *Module, decl_index: Module.Decl.Index) |
| 416 | if (self.data_decl_table.fetchPutAssumeCapacity(decl_index, duped_code)) |old_entry| { | 428 | if (self.data_decl_table.fetchPutAssumeCapacity(decl_index, duped_code)) |old_entry| { |
| 417 | self.base.allocator.free(old_entry.value); | 429 | self.base.allocator.free(old_entry.value); |
| 418 | } | 430 | } |
| 419 | return self.updateFinish(decl); | 431 | return self.updateFinish(decl_index); |
| 420 | } | 432 | } |
| 421 | /// called at the end of update{Decl,Func} | 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 | const is_fn = (decl.ty.zigTypeTag() == .Fn); | 436 | const is_fn = (decl.ty.zigTypeTag() == .Fn); |
| 424 | log.debug("update the symbol table and got for decl {*} ({s})", .{ decl, decl.name }); | 437 | log.debug("update the symbol table and got for decl {*} ({s})", .{ decl, decl.name }); |
| 425 | const sym_t: aout.Sym.Type = if (is_fn) .t else .d; | 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 | // write the internal linker metadata | 441 | // write the internal linker metadata |
| 427 | decl.link.plan9.type = sym_t; | 442 | decl_block.type = sym_t; |
| 428 | // write the symbol | 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 | const sym: aout.Sym = .{ | 445 | const sym: aout.Sym = .{ |
| 431 | .value = undefined, // the value of stuff gets filled in in flushModule | 446 | .value = undefined, // the value of stuff gets filled in in flushModule |
| 432 | .type = decl.link.plan9.type, | 447 | .type = decl_block.type, |
| 433 | .name = mem.span(decl.name), | 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 | self.syms.items[s] = sym; | 452 | self.syms.items[s] = sym; |
| 438 | } else { | 453 | } else { |
| 439 | const s = try self.allocateSymbolIndex(); | 454 | const s = try self.allocateSymbolIndex(); |
| 440 | decl.link.plan9.sym_index = s; | 455 | decl_block.sym_index = s; |
| 441 | self.syms.items[s] = sym; | 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,6 +567,7 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No |
| 552 | while (it.next()) |entry| { | 567 | while (it.next()) |entry| { |
| 553 | const decl_index = entry.key_ptr.*; | 568 | const decl_index = entry.key_ptr.*; |
| 554 | const decl = mod.declPtr(decl_index); | 569 | const decl = mod.declPtr(decl_index); |
| 570 | const decl_block = self.getDeclBlockPtr(self.decls.get(decl_index).?.index); | ||
| 555 | const out = entry.value_ptr.*; | 571 | const out = entry.value_ptr.*; |
| 556 | log.debug("write text decl {*} ({s}), lines {d} to {d}", .{ decl, decl.name, out.start_line + 1, out.end_line }); | 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,16 +586,16 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No |
| 570 | iovecs_i += 1; | 586 | iovecs_i += 1; |
| 571 | const off = self.getAddr(text_i, .t); | 587 | const off = self.getAddr(text_i, .t); |
| 572 | text_i += out.code.len; | 588 | text_i += out.code.len; |
| 573 | decl.link.plan9.offset = off; | 589 | decl_block.offset = off; |
| 574 | if (!self.sixtyfour_bit) { | 590 | if (!self.sixtyfour_bit) { |
| 575 | mem.writeIntNative(u32, got_table[decl.link.plan9.got_index.? * 4 ..][0..4], @intCast(u32, off)); | 591 | mem.writeIntNative(u32, got_table[decl_block.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()); | 592 | mem.writeInt(u32, got_table[decl_block.got_index.? * 4 ..][0..4], @intCast(u32, off), self.base.options.target.cpu.arch.endian()); |
| 577 | } else { | 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 | if (mod.decl_exports.get(decl_index)) |exports| { | 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,6 +616,7 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No |
| 600 | while (it.next()) |entry| { | 616 | while (it.next()) |entry| { |
| 601 | const decl_index = entry.key_ptr.*; | 617 | const decl_index = entry.key_ptr.*; |
| 602 | const decl = mod.declPtr(decl_index); | 618 | const decl = mod.declPtr(decl_index); |
| 619 | const decl_block = self.getDeclBlockPtr(self.decls.get(decl_index).?.index); | ||
| 603 | const code = entry.value_ptr.*; | 620 | const code = entry.value_ptr.*; |
| 604 | log.debug("write data decl {*} ({s})", .{ decl, decl.name }); | 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,15 +625,15 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No |
| 608 | iovecs_i += 1; | 625 | iovecs_i += 1; |
| 609 | const off = self.getAddr(data_i, .d); | 626 | const off = self.getAddr(data_i, .d); |
| 610 | data_i += code.len; | 627 | data_i += code.len; |
| 611 | decl.link.plan9.offset = off; | 628 | decl_block.offset = off; |
| 612 | if (!self.sixtyfour_bit) { | 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 | } else { | 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 | if (mod.decl_exports.get(decl_index)) |exports| { | 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 | // write the unnamed constants after the other data decls | 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,7 +695,8 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No |
| 678 | for (kv.value_ptr.items) |reloc| { | 695 | for (kv.value_ptr.items) |reloc| { |
| 679 | const target_decl_index = reloc.target; | 696 | const target_decl_index = reloc.target; |
| 680 | const target_decl = mod.declPtr(target_decl_index); | 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 | const offset = reloc.offset; | 701 | const offset = reloc.offset; |
| 684 | const addend = reloc.addend; | 702 | const addend = reloc.addend; |
| ... | @@ -711,35 +729,43 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No | ... | @@ -711,35 +729,43 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No |
| 711 | fn addDeclExports( | 729 | fn addDeclExports( |
| 712 | self: *Plan9, | 730 | self: *Plan9, |
| 713 | module: *Module, | 731 | module: *Module, |
| 714 | decl: *Module.Decl, | 732 | decl_index: Module.Decl.Index, |
| 715 | exports: []const *Module.Export, | 733 | exports: []const *Module.Export, |
| 716 | ) !void { | 734 | ) !void { |
| 735 | const metadata = self.decls.getPtr(decl_index).?; | ||
| 736 | const decl_block = self.getDeclBlock(metadata.index); | ||
| 737 | |||
| 717 | for (exports) |exp| { | 738 | for (exports) |exp| { |
| 718 | // plan9 does not support custom sections | 739 | // plan9 does not support custom sections |
| 719 | if (exp.options.section) |section_name| { | 740 | if (exp.options.section) |section_name| { |
| 720 | if (!mem.eql(u8, section_name, ".text") or !mem.eql(u8, section_name, ".data")) { | 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 | break; | 748 | break; |
| 723 | } | 749 | } |
| 724 | } | 750 | } |
| 725 | const sym = .{ | 751 | const sym = .{ |
| 726 | .value = decl.link.plan9.offset.?, | 752 | .value = decl_block.offset.?, |
| 727 | .type = decl.link.plan9.type.toGlobal(), | 753 | .type = decl_block.type.toGlobal(), |
| 728 | .name = exp.options.name, | 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 | self.syms.items[i] = sym; | 758 | self.syms.items[i] = sym; |
| 733 | } else { | 759 | } else { |
| 734 | try self.syms.append(self.base.allocator, sym); | 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 | pub fn freeDecl(self: *Plan9, decl_index: Module.Decl.Index) void { | 766 | pub fn freeDecl(self: *Plan9, decl_index: Module.Decl.Index) void { |
| 741 | // TODO audit the lifetimes of decls table entries. It's possible to get | 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 | // However that is planned to change, see the TODO comment in Module.zig | 769 | // However that is planned to change, see the TODO comment in Module.zig |
| 744 | // in the deleteUnusedDecl function. | 770 | // in the deleteUnusedDecl function. |
| 745 | const mod = self.base.options.module.?; | 771 | const mod = self.base.options.module.?; |
| ... | @@ -762,13 +788,18 @@ pub fn freeDecl(self: *Plan9, decl_index: Module.Decl.Index) void { | ... | @@ -762,13 +788,18 @@ pub fn freeDecl(self: *Plan9, decl_index: Module.Decl.Index) void { |
| 762 | self.base.allocator.free(removed_entry.value); | 788 | self.base.allocator.free(removed_entry.value); |
| 763 | } | 789 | } |
| 764 | } | 790 | } |
| 765 | if (decl.link.plan9.got_index) |i| { | 791 | if (self.decls.fetchRemove(decl_index)) |const_kv| { |
| 766 | // TODO: if this catch {} is triggered, an assertion in flushModule will be triggered, because got_index_free_list will have the wrong length | 792 | var kv = const_kv; |
| 767 | self.got_index_free_list.append(self.base.allocator, i) catch {}; | 793 | const decl_block = self.getDeclBlock(kv.value.index); |
| 768 | } | 794 | if (decl_block.got_index) |i| { |
| 769 | if (decl.link.plan9.sym_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 |
| 770 | self.syms_index_free_list.append(self.base.allocator, i) catch {}; | 796 | self.got_index_free_list.append(self.base.allocator, i) catch {}; |
| 771 | self.syms.items[i] = aout.Sym.undefined_symbol; | 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 | self.freeUnnamedConsts(decl_index); | 804 | self.freeUnnamedConsts(decl_index); |
| 774 | { | 805 | { |
| ... | @@ -788,12 +819,30 @@ fn freeUnnamedConsts(self: *Plan9, decl_index: Module.Decl.Index) void { | ... | @@ -788,12 +819,30 @@ fn freeUnnamedConsts(self: *Plan9, decl_index: Module.Decl.Index) void { |
| 788 | unnamed_consts.clearAndFree(self.base.allocator); | 819 | unnamed_consts.clearAndFree(self.base.allocator); |
| 789 | } | 820 | } |
| 790 | 821 | ||
| 791 | pub fn seeDecl(self: *Plan9, decl_index: Module.Decl.Index) !void { | 822 | fn createDeclBlock(self: *Plan9) !DeclBlock.Index { |
| 792 | const mod = self.base.options.module.?; | 823 | const gpa = self.base.allocator; |
| 793 | const decl = mod.declPtr(decl_index); | 824 | const index = @intCast(DeclBlock.Index, self.decl_blocks.items.len); |
| 794 | if (decl.link.plan9.got_index == null) { | 825 | const decl_block = try self.decl_blocks.addOne(gpa); |
| 795 | decl.link.plan9.got_index = self.allocateGotIndex(); | 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 | pub fn updateDeclExports( | 848 | pub fn updateDeclExports( |
| ... | @@ -802,7 +851,7 @@ pub fn updateDeclExports( | ... | @@ -802,7 +851,7 @@ pub fn updateDeclExports( |
| 802 | decl_index: Module.Decl.Index, | 851 | decl_index: Module.Decl.Index, |
| 803 | exports: []const *Module.Export, | 852 | exports: []const *Module.Export, |
| 804 | ) !void { | 853 | ) !void { |
| 805 | try self.seeDecl(decl_index); | 854 | _ = try self.seeDecl(decl_index); |
| 806 | // we do all the things in flush | 855 | // we do all the things in flush |
| 807 | _ = module; | 856 | _ = module; |
| 808 | _ = exports; | 857 | _ = exports; |
| ... | @@ -844,10 +893,17 @@ pub fn deinit(self: *Plan9) void { | ... | @@ -844,10 +893,17 @@ pub fn deinit(self: *Plan9) void { |
| 844 | self.syms_index_free_list.deinit(gpa); | 893 | self.syms_index_free_list.deinit(gpa); |
| 845 | self.file_segments.deinit(gpa); | 894 | self.file_segments.deinit(gpa); |
| 846 | self.path_arena.deinit(); | 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 | pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Options) !*Plan9 { | 907 | pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Options) !*Plan9 { |
| 852 | if (options.use_llvm) | 908 | if (options.use_llvm) |
| 853 | return error.LLVMBackendDoesNotSupportPlan9; | 909 | return error.LLVMBackendDoesNotSupportPlan9; |
| ... | @@ -913,20 +969,19 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void { | ... | @@ -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 | // write the data symbols | 972 | // write the data symbols |
| 919 | { | 973 | { |
| 920 | var it = self.data_decl_table.iterator(); | 974 | var it = self.data_decl_table.iterator(); |
| 921 | while (it.next()) |entry| { | 975 | while (it.next()) |entry| { |
| 922 | const decl_index = entry.key_ptr.*; | 976 | const decl_index = entry.key_ptr.*; |
| 923 | const decl = mod.declPtr(decl_index); | 977 | const decl_metadata = self.decls.get(decl_index).?; |
| 924 | const sym = self.syms.items[decl.link.plan9.sym_index.?]; | 978 | const decl_block = self.getDeclBlock(decl_metadata.index); |
| 979 | const sym = self.syms.items[decl_block.sym_index.?]; | ||
| 925 | try self.writeSym(writer, sym); | 980 | try self.writeSym(writer, sym); |
| 926 | if (self.base.options.module.?.decl_exports.get(decl_index)) |exports| { | 981 | if (self.base.options.module.?.decl_exports.get(decl_index)) |exports| { |
| 927 | for (exports.items) |e| { | 982 | for (exports.items) |e| if (decl_metadata.getExport(self, e.options.name)) |exp_i| { |
| 928 | try self.writeSym(writer, self.syms.items[e.link.plan9.?]); | 983 | try self.writeSym(writer, self.syms.items[exp_i]); |
| 929 | } | 984 | }; |
| 930 | } | 985 | } |
| 931 | } | 986 | } |
| 932 | } | 987 | } |
| ... | @@ -945,32 +1000,28 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void { | ... | @@ -945,32 +1000,28 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void { |
| 945 | var submap_it = symidx_and_submap.functions.iterator(); | 1000 | var submap_it = symidx_and_submap.functions.iterator(); |
| 946 | while (submap_it.next()) |entry| { | 1001 | while (submap_it.next()) |entry| { |
| 947 | const decl_index = entry.key_ptr.*; | 1002 | const decl_index = entry.key_ptr.*; |
| 948 | const decl = mod.declPtr(decl_index); | 1003 | const decl_metadata = self.decls.get(decl_index).?; |
| 949 | const sym = self.syms.items[decl.link.plan9.sym_index.?]; | 1004 | const decl_block = self.getDeclBlock(decl_metadata.index); |
| 1005 | const sym = self.syms.items[decl_block.sym_index.?]; | ||
| 950 | try self.writeSym(writer, sym); | 1006 | try self.writeSym(writer, sym); |
| 951 | if (self.base.options.module.?.decl_exports.get(decl_index)) |exports| { | 1007 | if (self.base.options.module.?.decl_exports.get(decl_index)) |exports| { |
| 952 | for (exports.items) |e| { | 1008 | for (exports.items) |e| if (decl_metadata.getExport(self, e.options.name)) |exp_i| { |
| 953 | const s = self.syms.items[e.link.plan9.?]; | 1009 | const s = self.syms.items[exp_i]; |
| 954 | if (mem.eql(u8, s.name, "_start")) | 1010 | if (mem.eql(u8, s.name, "_start")) |
| 955 | self.entry_val = s.value; | 1011 | self.entry_val = s.value; |
| 956 | try self.writeSym(writer, s); | 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 | /// Must be called only after a successful call to `updateDecl`. | 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 | _ = self; | 1022 | _ = self; |
| 972 | _ = mod; | 1023 | _ = mod; |
| 973 | _ = decl; | 1024 | _ = decl_index; |
| 974 | } | 1025 | } |
| 975 | 1026 | ||
| 976 | pub fn getDeclVAddr( | 1027 | pub fn getDeclVAddr( |
| ... | @@ -1011,3 +1062,11 @@ pub fn getDeclVAddr( | ... | @@ -1011,3 +1062,11 @@ pub fn getDeclVAddr( |
| 1011 | }); | 1062 | }); |
| 1012 | return undefined; | 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,13 +42,6 @@ const SpvModule = @import("../codegen/spirv/Module.zig"); |
| 42 | const spec = @import("../codegen/spirv/spec.zig"); | 42 | const spec = @import("../codegen/spirv/spec.zig"); |
| 43 | const IdResult = spec.IdResult; | 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 | base: link.File, | 45 | base: link.File, |
| 53 | 46 | ||
| 54 | /// This linker backend does not try to incrementally link output SPIR-V code. | 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,16 +202,19 @@ pub fn flushModule(self: *SpirV, comp: *Compilation, prog_node: *std.Progress.No |
| 209 | // so that we can access them before processing them. | 202 | // so that we can access them before processing them. |
| 210 | // TODO: We're allocating an ID unconditionally now, are there | 203 | // TODO: We're allocating an ID unconditionally now, are there |
| 211 | // declarations which don't generate a result? | 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 | for (self.decl_table.keys()) |decl_index| { | 209 | for (self.decl_table.keys()) |decl_index| { |
| 214 | const decl = module.declPtr(decl_index); | 210 | const decl = module.declPtr(decl_index); |
| 215 | if (decl.has_tv) { | 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 | // Now, actually generate the code for all declarations. | 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 | defer decl_gen.deinit(); | 218 | defer decl_gen.deinit(); |
| 223 | 219 | ||
| 224 | var it = self.decl_table.iterator(); | 220 | var it = self.decl_table.iterator(); |
| ... | @@ -231,7 +227,7 @@ pub fn flushModule(self: *SpirV, comp: *Compilation, prog_node: *std.Progress.No | ... | @@ -231,7 +227,7 @@ pub fn flushModule(self: *SpirV, comp: *Compilation, prog_node: *std.Progress.No |
| 231 | const liveness = entry.value_ptr.liveness; | 227 | const liveness = entry.value_ptr.liveness; |
| 232 | 228 | ||
| 233 | // Note, if `decl` is not a function, air/liveness may be undefined. | 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 | try module.failed_decls.put(module.gpa, decl_index, msg); | 231 | try module.failed_decls.put(module.gpa, decl_index, msg); |
| 236 | return; // TODO: Attempt to generate more decls? | 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,7 +9,7 @@ const fs = std.fs; |
| 9 | const leb = std.leb; | 9 | const leb = std.leb; |
| 10 | const log = std.log.scoped(.link); | 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 | const Dwarf = @import("Dwarf.zig"); | 13 | const Dwarf = @import("Dwarf.zig"); |
| 14 | const Module = @import("../Module.zig"); | 14 | const Module = @import("../Module.zig"); |
| 15 | const Compilation = @import("../Compilation.zig"); | 15 | const Compilation = @import("../Compilation.zig"); |
| ... | @@ -31,10 +31,7 @@ const Object = @import("Wasm/Object.zig"); | ... | @@ -31,10 +31,7 @@ const Object = @import("Wasm/Object.zig"); |
| 31 | const Archive = @import("Wasm/Archive.zig"); | 31 | const Archive = @import("Wasm/Archive.zig"); |
| 32 | const types = @import("Wasm/types.zig"); | 32 | const types = @import("Wasm/types.zig"); |
| 33 | 33 | ||
| 34 | pub const base_tag = link.File.Tag.wasm; | 34 | pub const base_tag: link.File.Tag = .wasm; |
| 35 | |||
| 36 | /// deprecated: Use `@import("Wasm/Atom.zig");` | ||
| 37 | pub const DeclBlock = Atom; | ||
| 38 | 35 | ||
| 39 | base: link.File, | 36 | base: link.File, |
| 40 | /// Output name of the file | 37 | /// Output name of the file |
| ... | @@ -47,18 +44,16 @@ llvm_object: ?*LlvmObject = null, | ... | @@ -47,18 +44,16 @@ llvm_object: ?*LlvmObject = null, |
| 47 | /// TODO: Allow setting this through a flag? | 44 | /// TODO: Allow setting this through a flag? |
| 48 | host_name: []const u8 = "env", | 45 | host_name: []const u8 = "env", |
| 49 | /// List of all `Decl` that are currently alive. | 46 | /// List of all `Decl` that are currently alive. |
| 50 | /// This is ment for bookkeeping so we can safely cleanup all codegen memory | 47 | /// Each index maps to the corresponding `Atom.Index`. |
| 51 | /// when calling `deinit` | 48 | decls: std.AutoHashMapUnmanaged(Module.Decl.Index, Atom.Index) = .{}, |
| 52 | decls: std.AutoHashMapUnmanaged(Module.Decl.Index, void) = .{}, | ||
| 53 | /// List of all symbols generated by Zig code. | 49 | /// List of all symbols generated by Zig code. |
| 54 | symbols: std.ArrayListUnmanaged(Symbol) = .{}, | 50 | symbols: std.ArrayListUnmanaged(Symbol) = .{}, |
| 55 | /// List of symbol indexes which are free to be used. | 51 | /// List of symbol indexes which are free to be used. |
| 56 | symbols_free_list: std.ArrayListUnmanaged(u32) = .{}, | 52 | symbols_free_list: std.ArrayListUnmanaged(u32) = .{}, |
| 57 | /// Maps atoms to their segment index | 53 | /// Maps atoms to their segment index |
| 58 | atoms: std.AutoHashMapUnmanaged(u32, *Atom) = .{}, | 54 | atoms: std.AutoHashMapUnmanaged(u32, Atom.Index) = .{}, |
| 59 | /// Atoms managed and created by the linker. This contains atoms | 55 | /// List of all atoms. |
| 60 | /// from object files, and not Atoms generated by a Decl. | 56 | managed_atoms: std.ArrayListUnmanaged(Atom) = .{}, |
| 61 | managed_atoms: std.ArrayListUnmanaged(*Atom) = .{}, | ||
| 62 | /// Represents the index into `segments` where the 'code' section | 57 | /// Represents the index into `segments` where the 'code' section |
| 63 | /// lives. | 58 | /// lives. |
| 64 | code_section_index: ?u32 = null, | 59 | code_section_index: ?u32 = null, |
| ... | @@ -148,7 +143,7 @@ undefs: std.StringArrayHashMapUnmanaged(SymbolLoc) = .{}, | ... | @@ -148,7 +143,7 @@ undefs: std.StringArrayHashMapUnmanaged(SymbolLoc) = .{}, |
| 148 | /// Maps a symbol's location to an atom. This can be used to find meta | 143 | /// Maps a symbol's location to an atom. This can be used to find meta |
| 149 | /// data of a symbol, such as its size, or its offset to perform a relocation. | 144 | /// data of a symbol, such as its size, or its offset to perform a relocation. |
| 150 | /// Undefined (and synthetic) symbols do not have an Atom and therefore cannot be mapped. | 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 | /// Maps a symbol's location to its export name, which may differ from the decl's name | 147 | /// Maps a symbol's location to its export name, which may differ from the decl's name |
| 153 | /// which does the exporting. | 148 | /// which does the exporting. |
| 154 | /// Note: The value represents the offset into the string table, rather than the actual string. | 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,14 +160,14 @@ error_table_symbol: ?u32 = null, |
| 165 | // unit contains Zig code. The lifetime of these atoms are extended | 160 | // unit contains Zig code. The lifetime of these atoms are extended |
| 166 | // until the end of the compiler's lifetime. Meaning they're not freed | 161 | // until the end of the compiler's lifetime. Meaning they're not freed |
| 167 | // during `flush()` in incremental-mode. | 162 | // during `flush()` in incremental-mode. |
| 168 | debug_info_atom: ?*Atom = null, | 163 | debug_info_atom: ?Atom.Index = null, |
| 169 | debug_line_atom: ?*Atom = null, | 164 | debug_line_atom: ?Atom.Index = null, |
| 170 | debug_loc_atom: ?*Atom = null, | 165 | debug_loc_atom: ?Atom.Index = null, |
| 171 | debug_ranges_atom: ?*Atom = null, | 166 | debug_ranges_atom: ?Atom.Index = null, |
| 172 | debug_abbrev_atom: ?*Atom = null, | 167 | debug_abbrev_atom: ?Atom.Index = null, |
| 173 | debug_str_atom: ?*Atom = null, | 168 | debug_str_atom: ?Atom.Index = null, |
| 174 | debug_pubnames_atom: ?*Atom = null, | 169 | debug_pubnames_atom: ?Atom.Index = null, |
| 175 | debug_pubtypes_atom: ?*Atom = null, | 170 | debug_pubtypes_atom: ?Atom.Index = null, |
| 176 | 171 | ||
| 177 | pub const Segment = struct { | 172 | pub const Segment = struct { |
| 178 | alignment: u32, | 173 | alignment: u32, |
| ... | @@ -183,13 +178,9 @@ pub const Segment = struct { | ... | @@ -183,13 +178,9 @@ pub const Segment = struct { |
| 183 | pub const FnData = struct { | 178 | pub const FnData = struct { |
| 184 | /// Reference to the wasm type that represents this function. | 179 | /// Reference to the wasm type that represents this function. |
| 185 | type_index: u32, | 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 | pub const empty: FnData = .{ | 182 | pub const empty: FnData = .{ |
| 191 | .type_index = undefined, | 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,10 +425,10 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option |
| 434 | // at the end during `initializeCallCtorsFunction`. | 425 | // at the end during `initializeCallCtorsFunction`. |
| 435 | } | 426 | } |
| 436 | 427 | ||
| 437 | if (!options.strip and options.module != null) { | 428 | // if (!options.strip and options.module != null) { |
| 438 | wasm_bin.dwarf = Dwarf.init(allocator, &wasm_bin.base, options.target); | 429 | // wasm_bin.dwarf = Dwarf.init(allocator, &wasm_bin.base, options.target); |
| 439 | try wasm_bin.initDebugSections(); | 430 | // try wasm_bin.initDebugSections(); |
| 440 | } | 431 | // } |
| 441 | 432 | ||
| 442 | return wasm_bin; | 433 | return wasm_bin; |
| 443 | } | 434 | } |
| ... | @@ -478,6 +469,7 @@ fn createSyntheticSymbol(wasm: *Wasm, name: []const u8, tag: Symbol.Tag) !Symbol | ... | @@ -478,6 +469,7 @@ fn createSyntheticSymbol(wasm: *Wasm, name: []const u8, tag: Symbol.Tag) !Symbol |
| 478 | try wasm.globals.put(wasm.base.allocator, name_offset, loc); | 469 | try wasm.globals.put(wasm.base.allocator, name_offset, loc); |
| 479 | return loc; | 470 | return loc; |
| 480 | } | 471 | } |
| 472 | |||
| 481 | /// Initializes symbols and atoms for the debug sections | 473 | /// Initializes symbols and atoms for the debug sections |
| 482 | /// Initialization is only done when compiling Zig code. | 474 | /// Initialization is only done when compiling Zig code. |
| 483 | /// When Zig is invoked as a linker instead, the atoms | 475 | /// When Zig is invoked as a linker instead, the atoms |
| ... | @@ -520,6 +512,36 @@ fn parseObjectFile(wasm: *Wasm, path: []const u8) !bool { | ... | @@ -520,6 +512,36 @@ fn parseObjectFile(wasm: *Wasm, path: []const u8) !bool { |
| 520 | return true; | 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 | /// Parses an archive file and will then parse each object file | 545 | /// Parses an archive file and will then parse each object file |
| 524 | /// that was found in the archive file. | 546 | /// that was found in the archive file. |
| 525 | /// Returns false when the file is not an archive file. | 547 | /// Returns false when the file is not an archive file. |
| ... | @@ -861,15 +883,16 @@ fn resolveLazySymbols(wasm: *Wasm) !void { | ... | @@ -861,15 +883,16 @@ fn resolveLazySymbols(wasm: *Wasm) !void { |
| 861 | try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc); | 883 | try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc); |
| 862 | _ = wasm.resolved_symbols.swapRemove(loc); // we don't want to emit this symbol, only use it for relocations. | 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); | 886 | // TODO: Can we use `createAtom` here while also re-using the symbol |
| 865 | errdefer wasm.base.allocator.destroy(atom); | 887 | // from `createSyntheticSymbol`. |
| 866 | try wasm.managed_atoms.append(wasm.base.allocator, atom); | 888 | const atom_index = @intCast(Atom.Index, wasm.managed_atoms.items.len); |
| 889 | const atom = try wasm.managed_atoms.addOne(wasm.base.allocator); | ||
| 867 | atom.* = Atom.empty; | 890 | atom.* = Atom.empty; |
| 868 | atom.sym_index = loc.index; | 891 | atom.sym_index = loc.index; |
| 869 | atom.alignment = 1; | 892 | atom.alignment = 1; |
| 870 | 893 | ||
| 871 | try wasm.parseAtom(atom, .{ .data = .synthetic }); | 894 | try wasm.parseAtom(atom_index, .{ .data = .synthetic }); |
| 872 | try wasm.symbol_atom.putNoClobber(wasm.base.allocator, loc, atom); | 895 | try wasm.symbol_atom.putNoClobber(wasm.base.allocator, loc, atom_index); |
| 873 | } | 896 | } |
| 874 | 897 | ||
| 875 | if (wasm.undefs.fetchSwapRemove("__heap_end")) |kv| { | 898 | if (wasm.undefs.fetchSwapRemove("__heap_end")) |kv| { |
| ... | @@ -877,15 +900,14 @@ fn resolveLazySymbols(wasm: *Wasm) !void { | ... | @@ -877,15 +900,14 @@ fn resolveLazySymbols(wasm: *Wasm) !void { |
| 877 | try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc); | 900 | try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc); |
| 878 | _ = wasm.resolved_symbols.swapRemove(loc); | 901 | _ = wasm.resolved_symbols.swapRemove(loc); |
| 879 | 902 | ||
| 880 | const atom = try wasm.base.allocator.create(Atom); | 903 | const atom_index = @intCast(Atom.Index, wasm.managed_atoms.items.len); |
| 881 | errdefer wasm.base.allocator.destroy(atom); | 904 | const atom = try wasm.managed_atoms.addOne(wasm.base.allocator); |
| 882 | try wasm.managed_atoms.append(wasm.base.allocator, atom); | ||
| 883 | atom.* = Atom.empty; | 905 | atom.* = Atom.empty; |
| 884 | atom.sym_index = loc.index; | 906 | atom.sym_index = loc.index; |
| 885 | atom.alignment = 1; | 907 | atom.alignment = 1; |
| 886 | 908 | ||
| 887 | try wasm.parseAtom(atom, .{ .data = .synthetic }); | 909 | try wasm.parseAtom(atom_index, .{ .data = .synthetic }); |
| 888 | try wasm.symbol_atom.putNoClobber(wasm.base.allocator, loc, atom); | 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,16 +946,6 @@ pub fn deinit(wasm: *Wasm) void { |
| 924 | if (wasm.llvm_object) |llvm_object| llvm_object.destroy(gpa); | 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 | for (wasm.func_types.items) |*func_type| { | 949 | for (wasm.func_types.items) |*func_type| { |
| 938 | func_type.deinit(gpa); | 950 | func_type.deinit(gpa); |
| 939 | } | 951 | } |
| ... | @@ -958,9 +970,8 @@ pub fn deinit(wasm: *Wasm) void { | ... | @@ -958,9 +970,8 @@ pub fn deinit(wasm: *Wasm) void { |
| 958 | wasm.symbol_atom.deinit(gpa); | 970 | wasm.symbol_atom.deinit(gpa); |
| 959 | wasm.export_names.deinit(gpa); | 971 | wasm.export_names.deinit(gpa); |
| 960 | wasm.atoms.deinit(gpa); | 972 | wasm.atoms.deinit(gpa); |
| 961 | for (wasm.managed_atoms.items) |managed_atom| { | 973 | for (wasm.managed_atoms.items) |*managed_atom| { |
| 962 | managed_atom.deinit(gpa); | 974 | managed_atom.deinit(wasm); |
| 963 | gpa.destroy(managed_atom); | ||
| 964 | } | 975 | } |
| 965 | wasm.managed_atoms.deinit(gpa); | 976 | wasm.managed_atoms.deinit(gpa); |
| 966 | wasm.segments.deinit(gpa); | 977 | wasm.segments.deinit(gpa); |
| ... | @@ -986,31 +997,23 @@ pub fn deinit(wasm: *Wasm) void { | ... | @@ -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 { | 1000 | /// Allocates a new symbol and returns its index. |
| 990 | if (wasm.llvm_object) |_| return; | 1001 | /// Will re-use slots when a symbol was freed at an earlier stage. |
| 991 | const decl = wasm.base.options.module.?.declPtr(decl_index); | 1002 | pub fn allocateSymbol(wasm: *Wasm) !u32 { |
| 992 | if (decl.link.wasm.sym_index != 0) return; | ||
| 993 | |||
| 994 | try wasm.symbols.ensureUnusedCapacity(wasm.base.allocator, 1); | 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 | var symbol: Symbol = .{ | 1004 | var symbol: Symbol = .{ |
| 1000 | .name = undefined, // will be set after updateDecl | 1005 | .name = undefined, // will be set after updateDecl |
| 1001 | .flags = @enumToInt(Symbol.Flag.WASM_SYM_BINDING_LOCAL), | 1006 | .flags = @enumToInt(Symbol.Flag.WASM_SYM_BINDING_LOCAL), |
| 1002 | .tag = undefined, // will be set after updateDecl | 1007 | .tag = undefined, // will be set after updateDecl |
| 1003 | .index = undefined, // will be set after updateDecl | 1008 | .index = undefined, // will be set after updateDecl |
| 1004 | }; | 1009 | }; |
| 1005 | |||
| 1006 | if (wasm.symbols_free_list.popOrNull()) |index| { | 1010 | if (wasm.symbols_free_list.popOrNull()) |index| { |
| 1007 | atom.sym_index = index; | ||
| 1008 | wasm.symbols.items[index] = symbol; | 1011 | wasm.symbols.items[index] = symbol; |
| 1009 | } else { | 1012 | return index; |
| 1010 | atom.sym_index = @intCast(u32, wasm.symbols.items.len); | ||
| 1011 | wasm.symbols.appendAssumeCapacity(symbol); | ||
| 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 | pub fn updateFunc(wasm: *Wasm, mod: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void { | 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,15 +1029,24 @@ pub fn updateFunc(wasm: *Wasm, mod: *Module, func: *Module.Fn, air: Air, livenes |
| 1026 | 1029 | ||
| 1027 | const decl_index = func.owner_decl; | 1030 | const decl_index = func.owner_decl; |
| 1028 | const decl = mod.declPtr(decl_index); | 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(); | 1036 | // var decl_state: ?Dwarf.DeclState = if (wasm.dwarf) |*dwarf| try dwarf.initDeclState(mod, decl_index) else null; |
| 1032 | 1037 | // defer if (decl_state) |*ds| ds.deinit(); | |
| 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(); | ||
| 1035 | 1038 | ||
| 1036 | var code_writer = std.ArrayList(u8).init(wasm.base.allocator); | 1039 | var code_writer = std.ArrayList(u8).init(wasm.base.allocator); |
| 1037 | defer code_writer.deinit(); | 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 | const result = try codegen.generateFunction( | 1050 | const result = try codegen.generateFunction( |
| 1039 | &wasm.base, | 1051 | &wasm.base, |
| 1040 | decl.srcLoc(), | 1052 | decl.srcLoc(), |
| ... | @@ -1042,11 +1054,11 @@ pub fn updateFunc(wasm: *Wasm, mod: *Module, func: *Module.Fn, air: Air, livenes | ... | @@ -1042,11 +1054,11 @@ pub fn updateFunc(wasm: *Wasm, mod: *Module, func: *Module.Fn, air: Air, livenes |
| 1042 | air, | 1054 | air, |
| 1043 | liveness, | 1055 | liveness, |
| 1044 | &code_writer, | 1056 | &code_writer, |
| 1045 | if (decl_state) |*ds| .{ .dwarf = ds } else .none, | 1057 | .none, |
| 1046 | ); | 1058 | ); |
| 1047 | 1059 | ||
| 1048 | const code = switch (result) { | 1060 | const code = switch (result) { |
| 1049 | .appended => code_writer.items, | 1061 | .ok => code_writer.items, |
| 1050 | .fail => |em| { | 1062 | .fail => |em| { |
| 1051 | decl.analysis = .codegen_failure; | 1063 | decl.analysis = .codegen_failure; |
| 1052 | try mod.failed_decls.put(mod.gpa, decl_index, em); | 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,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| { | 1069 | // if (wasm.dwarf) |*dwarf| { |
| 1058 | try dwarf.commitDeclState( | 1070 | // try dwarf.commitDeclState( |
| 1059 | mod, | 1071 | // mod, |
| 1060 | decl_index, | 1072 | // decl_index, |
| 1061 | // Actual value will be written after relocation. | 1073 | // // Actual value will be written after relocation. |
| 1062 | // For Wasm, this is the offset relative to the code section | 1074 | // // For Wasm, this is the offset relative to the code section |
| 1063 | // which isn't known until flush(). | 1075 | // // which isn't known until flush(). |
| 1064 | 0, | 1076 | // 0, |
| 1065 | code.len, | 1077 | // code.len, |
| 1066 | &decl_state.?, | 1078 | // &decl_state.?, |
| 1067 | ); | 1079 | // ); |
| 1068 | } | 1080 | // } |
| 1069 | return wasm.finishUpdateDecl(decl, code); | 1081 | return wasm.finishUpdateDecl(decl_index, code); |
| 1070 | } | 1082 | } |
| 1071 | 1083 | ||
| 1072 | // Generate code for the Decl, storing it in memory to be later written to | 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,20 +1095,20 @@ pub fn updateDecl(wasm: *Wasm, mod: *Module, decl_index: Module.Decl.Index) !voi |
| 1083 | defer tracy.end(); | 1095 | defer tracy.end(); |
| 1084 | 1096 | ||
| 1085 | const decl = mod.declPtr(decl_index); | 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 | if (decl.val.castTag(.function)) |_| { | 1098 | if (decl.val.castTag(.function)) |_| { |
| 1091 | return; | 1099 | return; |
| 1092 | } else if (decl.val.castTag(.extern_fn)) |_| { | 1100 | } else if (decl.val.castTag(.extern_fn)) |_| { |
| 1093 | return; | 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 | if (decl.isExtern()) { | 1108 | if (decl.isExtern()) { |
| 1097 | const variable = decl.getVariable().?; | 1109 | const variable = decl.getVariable().?; |
| 1098 | const name = mem.sliceTo(decl.name, 0); | 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 | const val = if (decl.val.castTag(.variable)) |payload| payload.data.init else decl.val; | 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,12 +1121,11 @@ pub fn updateDecl(wasm: *Wasm, mod: *Module, decl_index: Module.Decl.Index) !voi |
| 1109 | .{ .ty = decl.ty, .val = val }, | 1121 | .{ .ty = decl.ty, .val = val }, |
| 1110 | &code_writer, | 1122 | &code_writer, |
| 1111 | .none, | 1123 | .none, |
| 1112 | .{ .parent_atom_index = decl.link.wasm.sym_index }, | 1124 | .{ .parent_atom_index = atom.sym_index }, |
| 1113 | ); | 1125 | ); |
| 1114 | 1126 | ||
| 1115 | const code = switch (res) { | 1127 | const code = switch (res) { |
| 1116 | .externally_managed => |x| x, | 1128 | .ok => code_writer.items, |
| 1117 | .appended => code_writer.items, | ||
| 1118 | .fail => |em| { | 1129 | .fail => |em| { |
| 1119 | decl.analysis = .codegen_failure; | 1130 | decl.analysis = .codegen_failure; |
| 1120 | try mod.failed_decls.put(mod.gpa, decl_index, em); | 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,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 | if (wasm.llvm_object) |_| return; | 1140 | if (wasm.llvm_object) |_| return; |
| 1130 | if (wasm.dwarf) |*dw| { | 1141 | if (wasm.dwarf) |*dw| { |
| 1131 | const tracy = trace(@src()); | 1142 | const tracy = trace(@src()); |
| 1132 | defer tracy.end(); | 1143 | defer tracy.end(); |
| 1133 | 1144 | ||
| 1145 | const decl = mod.declPtr(decl_index); | ||
| 1134 | const decl_name = try decl.getFullyQualifiedName(mod); | 1146 | const decl_name = try decl.getFullyQualifiedName(mod); |
| 1135 | defer wasm.base.allocator.free(decl_name); | 1147 | defer wasm.base.allocator.free(decl_name); |
| 1136 | 1148 | ||
| 1137 | log.debug("updateDeclLineNumber {s}{*}", .{ decl_name, decl }); | 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 | const mod = wasm.base.options.module.?; | 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 | const symbol = &wasm.symbols.items[atom.sym_index]; | 1159 | const symbol = &wasm.symbols.items[atom.sym_index]; |
| 1146 | const full_name = try decl.getFullyQualifiedName(mod); | 1160 | const full_name = try decl.getFullyQualifiedName(mod); |
| 1147 | defer wasm.base.allocator.free(full_name); | 1161 | defer wasm.base.allocator.free(full_name); |
| ... | @@ -1149,8 +1163,8 @@ fn finishUpdateDecl(wasm: *Wasm, decl: *Module.Decl, code: []const u8) !void { | ... | @@ -1149,8 +1163,8 @@ fn finishUpdateDecl(wasm: *Wasm, decl: *Module.Decl, code: []const u8) !void { |
| 1149 | try atom.code.appendSlice(wasm.base.allocator, code); | 1163 | try atom.code.appendSlice(wasm.base.allocator, code); |
| 1150 | try wasm.resolved_symbols.put(wasm.base.allocator, atom.symbolLoc(), {}); | 1164 | try wasm.resolved_symbols.put(wasm.base.allocator, atom.symbolLoc(), {}); |
| 1151 | 1165 | ||
| 1152 | if (code.len == 0) return; | ||
| 1153 | atom.size = @intCast(u32, code.len); | 1166 | atom.size = @intCast(u32, code.len); |
| 1167 | if (code.len == 0) return; | ||
| 1154 | atom.alignment = decl.ty.abiAlignment(wasm.base.options.target); | 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,58 +1221,51 @@ pub fn lowerUnnamedConst(wasm: *Wasm, tv: TypedValue, decl_index: Module.Decl.In |
| 1207 | const decl = mod.declPtr(decl_index); | 1221 | const decl = mod.declPtr(decl_index); |
| 1208 | 1222 | ||
| 1209 | // Create and initialize a new local symbol and atom | 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 | const fqdn = try decl.getFullyQualifiedName(mod); | 1229 | const fqdn = try decl.getFullyQualifiedName(mod); |
| 1212 | defer wasm.base.allocator.free(fqdn); | 1230 | defer wasm.base.allocator.free(fqdn); |
| 1213 | const name = try std.fmt.allocPrintZ(wasm.base.allocator, "__unnamed_{s}_{d}", .{ fqdn, local_index }); | 1231 | const name = try std.fmt.allocPrintZ(wasm.base.allocator, "__unnamed_{s}_{d}", .{ fqdn, local_index }); |
| 1214 | defer wasm.base.allocator.free(name); | 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 | var value_bytes = std.ArrayList(u8).init(wasm.base.allocator); | 1233 | var value_bytes = std.ArrayList(u8).init(wasm.base.allocator); |
| 1239 | defer value_bytes.deinit(); | 1234 | defer value_bytes.deinit(); |
| 1240 | 1235 | ||
| 1241 | const result = try codegen.generateSymbol( | 1236 | const code = code: { |
| 1242 | &wasm.base, | 1237 | const atom = wasm.getAtomPtr(atom_index); |
| 1243 | decl.srcLoc(), | 1238 | atom.alignment = tv.ty.abiAlignment(wasm.base.options.target); |
| 1244 | tv, | 1239 | wasm.symbols.items[atom.sym_index] = .{ |
| 1245 | &value_bytes, | 1240 | .name = try wasm.string_table.put(wasm.base.allocator, name), |
| 1246 | .none, | 1241 | .flags = @enumToInt(Symbol.Flag.WASM_SYM_BINDING_LOCAL), |
| 1247 | .{ | 1242 | .tag = .data, |
| 1248 | .parent_atom_index = atom.sym_index, | 1243 | .index = undefined, |
| 1249 | .addend = null, | 1244 | }; |
| 1250 | }, | 1245 | try wasm.resolved_symbols.putNoClobber(wasm.base.allocator, atom.symbolLoc(), {}); |
| 1251 | ); | 1246 | |
| 1252 | const code = switch (result) { | 1247 | const result = try codegen.generateSymbol( |
| 1253 | .externally_managed => |x| x, | 1248 | &wasm.base, |
| 1254 | .appended => value_bytes.items, | 1249 | decl.srcLoc(), |
| 1255 | .fail => |em| { | 1250 | tv, |
| 1256 | decl.analysis = .codegen_failure; | 1251 | &value_bytes, |
| 1257 | try mod.failed_decls.put(mod.gpa, decl_index, em); | 1252 | .none, |
| 1258 | return error.AnalysisFail; | 1253 | .{ |
| 1259 | }, | 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 | atom.size = @intCast(u32, code.len); | 1269 | atom.size = @intCast(u32, code.len); |
| 1263 | try atom.code.appendSlice(wasm.base.allocator, code); | 1270 | try atom.code.appendSlice(wasm.base.allocator, code); |
| 1264 | return atom.sym_index; | 1271 | return atom.sym_index; |
| ... | @@ -1306,10 +1313,13 @@ pub fn getDeclVAddr( | ... | @@ -1306,10 +1313,13 @@ pub fn getDeclVAddr( |
| 1306 | ) !u64 { | 1313 | ) !u64 { |
| 1307 | const mod = wasm.base.options.module.?; | 1314 | const mod = wasm.base.options.module.?; |
| 1308 | const decl = mod.declPtr(decl_index); | 1315 | const decl = mod.declPtr(decl_index); |
| 1309 | const target_symbol_index = decl.link.wasm.sym_index; | 1316 | |
| 1310 | assert(target_symbol_index != 0); | 1317 | const target_atom_index = try wasm.getOrCreateAtomForDecl(decl_index); |
| 1318 | const target_symbol_index = wasm.getAtom(target_atom_index).sym_index; | ||
| 1319 | |||
| 1311 | assert(reloc_info.parent_atom_index != 0); | 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 | const is_wasm32 = wasm.base.options.target.cpu.arch == .wasm32; | 1323 | const is_wasm32 = wasm.base.options.target.cpu.arch == .wasm32; |
| 1314 | if (decl.ty.zigTypeTag() == .Fn) { | 1324 | if (decl.ty.zigTypeTag() == .Fn) { |
| 1315 | assert(reloc_info.addend == 0); // addend not allowed for function relocations | 1325 | assert(reloc_info.addend == 0); // addend not allowed for function relocations |
| ... | @@ -1337,9 +1347,10 @@ pub fn getDeclVAddr( | ... | @@ -1337,9 +1347,10 @@ pub fn getDeclVAddr( |
| 1337 | return target_symbol_index; | 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 | if (wasm.llvm_object) |_| return; | 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 | const loc: SymbolLoc = .{ .file = null, .index = sym_index }; | 1354 | const loc: SymbolLoc = .{ .file = null, .index = sym_index }; |
| 1344 | const symbol = loc.getSymbol(wasm); | 1355 | const symbol = loc.getSymbol(wasm); |
| 1345 | const symbol_name = wasm.string_table.get(symbol.name); | 1356 | const symbol_name = wasm.string_table.get(symbol.name); |
| ... | @@ -1365,6 +1376,8 @@ pub fn updateDeclExports( | ... | @@ -1365,6 +1376,8 @@ pub fn updateDeclExports( |
| 1365 | } | 1376 | } |
| 1366 | 1377 | ||
| 1367 | const decl = mod.declPtr(decl_index); | 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 | for (exports) |exp| { | 1382 | for (exports) |exp| { |
| 1370 | if (exp.options.section) |section| { | 1383 | if (exp.options.section) |section| { |
| ... | @@ -1379,7 +1392,7 @@ pub fn updateDeclExports( | ... | @@ -1379,7 +1392,7 @@ pub fn updateDeclExports( |
| 1379 | 1392 | ||
| 1380 | const export_name = try wasm.string_table.put(wasm.base.allocator, exp.options.name); | 1393 | const export_name = try wasm.string_table.put(wasm.base.allocator, exp.options.name); |
| 1381 | if (wasm.globals.getPtr(export_name)) |existing_loc| { | 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 | const existing_sym: Symbol = existing_loc.getSymbol(wasm).*; | 1396 | const existing_sym: Symbol = existing_loc.getSymbol(wasm).*; |
| 1384 | 1397 | ||
| 1385 | const exp_is_weak = exp.options.linkage == .Internal or exp.options.linkage == .Weak; | 1398 | const exp_is_weak = exp.options.linkage == .Internal or exp.options.linkage == .Weak; |
| ... | @@ -1400,15 +1413,16 @@ pub fn updateDeclExports( | ... | @@ -1400,15 +1413,16 @@ pub fn updateDeclExports( |
| 1400 | } else if (exp_is_weak) { | 1413 | } else if (exp_is_weak) { |
| 1401 | continue; // to-be-exported symbol is weak, so we keep the existing symbol | 1414 | continue; // to-be-exported symbol is weak, so we keep the existing symbol |
| 1402 | } else { | 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 | existing_loc.file = null; | 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); | 1423 | const exported_atom_index = try wasm.getOrCreateAtomForDecl(exp.exported_decl); |
| 1410 | const sym_index = exported_decl.link.wasm.sym_index; | 1424 | const exported_atom = wasm.getAtom(exported_atom_index); |
| 1411 | const sym_loc = exported_decl.link.wasm.symbolLoc(); | 1425 | const sym_loc = exported_atom.symbolLoc(); |
| 1412 | const symbol = sym_loc.getSymbol(wasm); | 1426 | const symbol = sym_loc.getSymbol(wasm); |
| 1413 | switch (exp.options.linkage) { | 1427 | switch (exp.options.linkage) { |
| 1414 | .Internal => { | 1428 | .Internal => { |
| ... | @@ -1444,7 +1458,6 @@ pub fn updateDeclExports( | ... | @@ -1444,7 +1458,6 @@ pub fn updateDeclExports( |
| 1444 | // if the symbol was previously undefined, remove it as an import | 1458 | // if the symbol was previously undefined, remove it as an import |
| 1445 | _ = wasm.imports.remove(sym_loc); | 1459 | _ = wasm.imports.remove(sym_loc); |
| 1446 | _ = wasm.undefs.swapRemove(exp.options.name); | 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,11 +1467,13 @@ pub fn freeDecl(wasm: *Wasm, decl_index: Module.Decl.Index) void { |
| 1454 | } | 1467 | } |
| 1455 | const mod = wasm.base.options.module.?; | 1468 | const mod = wasm.base.options.module.?; |
| 1456 | const decl = mod.declPtr(decl_index); | 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 | wasm.symbols_free_list.append(wasm.base.allocator, atom.sym_index) catch {}; | 1472 | wasm.symbols_free_list.append(wasm.base.allocator, atom.sym_index) catch {}; |
| 1459 | _ = wasm.decls.remove(decl_index); | 1473 | _ = wasm.decls.remove(decl_index); |
| 1460 | wasm.symbols.items[atom.sym_index].tag = .dead; | 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 | const local_symbol = &wasm.symbols.items[local_atom.sym_index]; | 1477 | const local_symbol = &wasm.symbols.items[local_atom.sym_index]; |
| 1463 | local_symbol.tag = .dead; // also for any local symbol | 1478 | local_symbol.tag = .dead; // also for any local symbol |
| 1464 | wasm.symbols_free_list.append(wasm.base.allocator, local_atom.sym_index) catch {}; | 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,12 +1487,20 @@ pub fn freeDecl(wasm: *Wasm, decl_index: Module.Decl.Index) void { |
| 1472 | _ = wasm.resolved_symbols.swapRemove(atom.symbolLoc()); | 1487 | _ = wasm.resolved_symbols.swapRemove(atom.symbolLoc()); |
| 1473 | _ = wasm.symbol_atom.remove(atom.symbolLoc()); | 1488 | _ = wasm.symbol_atom.remove(atom.symbolLoc()); |
| 1474 | 1489 | ||
| 1475 | if (wasm.dwarf) |*dwarf| { | 1490 | // if (wasm.dwarf) |*dwarf| { |
| 1476 | dwarf.freeDecl(decl); | 1491 | // dwarf.freeDecl(decl_index); |
| 1477 | dwarf.freeAtom(&atom.dbg_info_atom); | 1492 | // } |
| 1478 | } | ||
| 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 | /// Appends a new entry to the indirect function table | 1506 | /// Appends a new entry to the indirect function table |
| ... | @@ -1599,7 +1622,8 @@ const Kind = union(enum) { | ... | @@ -1599,7 +1622,8 @@ const Kind = union(enum) { |
| 1599 | }; | 1622 | }; |
| 1600 | 1623 | ||
| 1601 | /// Parses an Atom and inserts its metadata into the corresponding sections. | 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 | const symbol = (SymbolLoc{ .file = null, .index = atom.sym_index }).getSymbol(wasm); | 1627 | const symbol = (SymbolLoc{ .file = null, .index = atom.sym_index }).getSymbol(wasm); |
| 1604 | const final_index: u32 = switch (kind) { | 1628 | const final_index: u32 = switch (kind) { |
| 1605 | .function => |fn_data| result: { | 1629 | .function => |fn_data| result: { |
| ... | @@ -1674,18 +1698,20 @@ fn parseAtom(wasm: *Wasm, atom: *Atom, kind: Kind) !void { | ... | @@ -1674,18 +1698,20 @@ fn parseAtom(wasm: *Wasm, atom: *Atom, kind: Kind) !void { |
| 1674 | const segment: *Segment = &wasm.segments.items[final_index]; | 1698 | const segment: *Segment = &wasm.segments.items[final_index]; |
| 1675 | segment.alignment = std.math.max(segment.alignment, atom.alignment); | 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 | /// From a given index, append the given `Atom` at the back of the linked list. | 1704 | /// From a given index, append the given `Atom` at the back of the linked list. |
| 1681 | /// Simply inserts it into the map of atoms when it doesn't exist yet. | 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 { | 1706 | pub fn appendAtomAtIndex(wasm: *Wasm, index: u32, atom_index: Atom.Index) !void { |
| 1683 | if (wasm.atoms.getPtr(index)) |last| { | 1707 | const atom = wasm.getAtomPtr(atom_index); |
| 1684 | last.*.next = atom; | 1708 | if (wasm.atoms.getPtr(index)) |last_index_ptr| { |
| 1685 | atom.prev = last.*; | 1709 | const last = wasm.getAtomPtr(last_index_ptr.*); |
| 1686 | last.* = atom; | 1710 | last.*.next = atom_index; |
| 1711 | atom.prev = last_index_ptr.*; | ||
| 1712 | last_index_ptr.* = atom_index; | ||
| 1687 | } else { | 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,16 +1721,17 @@ fn allocateDebugAtoms(wasm: *Wasm) !void { |
| 1695 | if (wasm.dwarf == null) return; | 1721 | if (wasm.dwarf == null) return; |
| 1696 | 1722 | ||
| 1697 | const allocAtom = struct { | 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 | const index = maybe_index.* orelse idx: { | 1725 | const index = maybe_index.* orelse idx: { |
| 1700 | const index = @intCast(u32, bin.segments.items.len); | 1726 | const index = @intCast(u32, bin.segments.items.len); |
| 1701 | try bin.appendDummySegment(); | 1727 | try bin.appendDummySegment(); |
| 1702 | maybe_index.* = index; | 1728 | maybe_index.* = index; |
| 1703 | break :idx index; | 1729 | break :idx index; |
| 1704 | }; | 1730 | }; |
| 1731 | const atom = bin.getAtomPtr(atom_index); | ||
| 1705 | atom.size = @intCast(u32, atom.code.items.len); | 1732 | atom.size = @intCast(u32, atom.code.items.len); |
| 1706 | bin.symbols.items[atom.sym_index].index = index; | 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 | }.f; | 1736 | }.f; |
| 1710 | 1737 | ||
| ... | @@ -1726,15 +1753,16 @@ fn allocateAtoms(wasm: *Wasm) !void { | ... | @@ -1726,15 +1753,16 @@ fn allocateAtoms(wasm: *Wasm) !void { |
| 1726 | var it = wasm.atoms.iterator(); | 1753 | var it = wasm.atoms.iterator(); |
| 1727 | while (it.next()) |entry| { | 1754 | while (it.next()) |entry| { |
| 1728 | const segment = &wasm.segments.items[entry.key_ptr.*]; | 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 | var offset: u32 = 0; | 1757 | var offset: u32 = 0; |
| 1731 | while (true) { | 1758 | while (true) { |
| 1759 | const atom = wasm.getAtomPtr(atom_index); | ||
| 1732 | const symbol_loc = atom.symbolLoc(); | 1760 | const symbol_loc = atom.symbolLoc(); |
| 1733 | if (wasm.code_section_index) |index| { | 1761 | if (wasm.code_section_index) |index| { |
| 1734 | if (index == entry.key_ptr.*) { | 1762 | if (index == entry.key_ptr.*) { |
| 1735 | if (!wasm.resolved_symbols.contains(symbol_loc)) { | 1763 | if (!wasm.resolved_symbols.contains(symbol_loc)) { |
| 1736 | // only allocate resolved function body's. | 1764 | // only allocate resolved function body's. |
| 1737 | atom = atom.next orelse break; | 1765 | atom_index = atom.prev orelse break; |
| 1738 | continue; | 1766 | continue; |
| 1739 | } | 1767 | } |
| 1740 | } | 1768 | } |
| ... | @@ -1748,8 +1776,7 @@ fn allocateAtoms(wasm: *Wasm) !void { | ... | @@ -1748,8 +1776,7 @@ fn allocateAtoms(wasm: *Wasm) !void { |
| 1748 | atom.size, | 1776 | atom.size, |
| 1749 | }); | 1777 | }); |
| 1750 | offset += atom.size; | 1778 | offset += atom.size; |
| 1751 | try wasm.symbol_atom.put(wasm.base.allocator, symbol_loc, atom); // Update atom pointers | 1779 | atom_index = atom.prev orelse break; |
| 1752 | atom = atom.next orelse break; | ||
| 1753 | } | 1780 | } |
| 1754 | segment.size = std.mem.alignForwardGeneric(u32, offset, segment.alignment); | 1781 | segment.size = std.mem.alignForwardGeneric(u32, offset, segment.alignment); |
| 1755 | } | 1782 | } |
| ... | @@ -1883,8 +1910,8 @@ fn initializeCallCtorsFunction(wasm: *Wasm) !void { | ... | @@ -1883,8 +1910,8 @@ fn initializeCallCtorsFunction(wasm: *Wasm) !void { |
| 1883 | symbol.index = func_index; | 1910 | symbol.index = func_index; |
| 1884 | 1911 | ||
| 1885 | // create the atom that will be output into the final binary | 1912 | // create the atom that will be output into the final binary |
| 1886 | const atom = try wasm.base.allocator.create(Atom); | 1913 | const atom_index = @intCast(Atom.Index, wasm.managed_atoms.items.len); |
| 1887 | errdefer wasm.base.allocator.destroy(atom); | 1914 | const atom = try wasm.managed_atoms.addOne(wasm.base.allocator); |
| 1888 | atom.* = .{ | 1915 | atom.* = .{ |
| 1889 | .size = @intCast(u32, function_body.items.len), | 1916 | .size = @intCast(u32, function_body.items.len), |
| 1890 | .offset = 0, | 1917 | .offset = 0, |
| ... | @@ -1894,15 +1921,14 @@ fn initializeCallCtorsFunction(wasm: *Wasm) !void { | ... | @@ -1894,15 +1921,14 @@ fn initializeCallCtorsFunction(wasm: *Wasm) !void { |
| 1894 | .next = null, | 1921 | .next = null, |
| 1895 | .prev = null, | 1922 | .prev = null, |
| 1896 | .code = function_body.moveToUnmanaged(), | 1923 | .code = function_body.moveToUnmanaged(), |
| 1897 | .dbg_info_atom = undefined, | ||
| 1898 | }; | 1924 | }; |
| 1899 | try wasm.managed_atoms.append(wasm.base.allocator, atom); | 1925 | try wasm.appendAtomAtIndex(wasm.code_section_index.?, atom_index); |
| 1900 | try wasm.appendAtomAtIndex(wasm.code_section_index.?, atom); | 1926 | try wasm.symbol_atom.putNoClobber(wasm.base.allocator, loc, atom_index); |
| 1901 | try wasm.symbol_atom.putNoClobber(wasm.base.allocator, loc, atom); | ||
| 1902 | 1927 | ||
| 1903 | // `allocateAtoms` has already been called, set the atom's offset manually. | 1928 | // `allocateAtoms` has already been called, set the atom's offset manually. |
| 1904 | // This is fine to do manually as we insert the atom at the very end. | 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 | fn setupImports(wasm: *Wasm) !void { | 1934 | fn setupImports(wasm: *Wasm) !void { |
| ... | @@ -2105,7 +2131,8 @@ fn setupExports(wasm: *Wasm) !void { | ... | @@ -2105,7 +2131,8 @@ fn setupExports(wasm: *Wasm) !void { |
| 2105 | break :blk try wasm.string_table.put(wasm.base.allocator, sym_name); | 2131 | break :blk try wasm.string_table.put(wasm.base.allocator, sym_name); |
| 2106 | }; | 2132 | }; |
| 2107 | const exp: types.Export = if (symbol.tag == .data) exp: { | 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 | const va = atom.getVA(wasm, symbol); | 2136 | const va = atom.getVA(wasm, symbol); |
| 2110 | const global_index = @intCast(u32, wasm.imported_globals_count + wasm.wasm_globals.items.len); | 2137 | const global_index = @intCast(u32, wasm.imported_globals_count + wasm.wasm_globals.items.len); |
| 2111 | try wasm.wasm_globals.append(wasm.base.allocator, .{ | 2138 | try wasm.wasm_globals.append(wasm.base.allocator, .{ |
| ... | @@ -2210,7 +2237,8 @@ fn setupMemory(wasm: *Wasm) !void { | ... | @@ -2210,7 +2237,8 @@ fn setupMemory(wasm: *Wasm) !void { |
| 2210 | const segment_index = wasm.data_segments.get(".synthetic").?; | 2237 | const segment_index = wasm.data_segments.get(".synthetic").?; |
| 2211 | const segment = &wasm.segments.items[segment_index]; | 2238 | const segment = &wasm.segments.items[segment_index]; |
| 2212 | segment.offset = 0; // for simplicity we store the entire VA into atom's offset. | 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 | atom.offset = @intCast(u32, mem.alignForwardGeneric(u64, memory_ptr, heap_alignment)); | 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,7 +2271,8 @@ fn setupMemory(wasm: *Wasm) !void { |
| 2243 | const segment_index = wasm.data_segments.get(".synthetic").?; | 2271 | const segment_index = wasm.data_segments.get(".synthetic").?; |
| 2244 | const segment = &wasm.segments.items[segment_index]; | 2272 | const segment = &wasm.segments.items[segment_index]; |
| 2245 | segment.offset = 0; | 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 | atom.offset = @intCast(u32, memory_ptr); | 2276 | atom.offset = @intCast(u32, memory_ptr); |
| 2248 | } | 2277 | } |
| 2249 | 2278 | ||
| ... | @@ -2369,15 +2398,14 @@ pub fn getErrorTableSymbol(wasm: *Wasm) !u32 { | ... | @@ -2369,15 +2398,14 @@ pub fn getErrorTableSymbol(wasm: *Wasm) !u32 { |
| 2369 | // and then return said symbol's index. The final table will be populated | 2398 | // and then return said symbol's index. The final table will be populated |
| 2370 | // during `flush` when we know all possible error names. | 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 | 2401 | const atom_index = try wasm.createAtom(); |
| 2373 | const symbol_index = wasm.symbols_free_list.popOrNull() orelse blk: { | 2402 | const atom = wasm.getAtomPtr(atom_index); |
| 2374 | const index = @intCast(u32, wasm.symbols.items.len); | 2403 | const slice_ty = Type.initTag(.const_slice_u8_sentinel_0); |
| 2375 | _ = try wasm.symbols.addOne(wasm.base.allocator); | 2404 | atom.alignment = slice_ty.abiAlignment(wasm.base.options.target); |
| 2376 | break :blk index; | 2405 | const sym_index = atom.sym_index; |
| 2377 | }; | ||
| 2378 | 2406 | ||
| 2379 | const sym_name = try wasm.string_table.put(wasm.base.allocator, "__zig_err_name_table"); | 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 | symbol.* = .{ | 2409 | symbol.* = .{ |
| 2382 | .name = sym_name, | 2410 | .name = sym_name, |
| 2383 | .tag = .data, | 2411 | .tag = .data, |
| ... | @@ -2386,20 +2414,11 @@ pub fn getErrorTableSymbol(wasm: *Wasm) !u32 { | ... | @@ -2386,20 +2414,11 @@ pub fn getErrorTableSymbol(wasm: *Wasm) !u32 { |
| 2386 | }; | 2414 | }; |
| 2387 | symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN); | 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); | 2419 | log.debug("Error name table was created with symbol index: ({d})", .{sym_index}); |
| 2392 | atom.* = Atom.empty; | 2420 | wasm.error_table_symbol = sym_index; |
| 2393 | atom.sym_index = symbol_index; | 2421 | return sym_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; | ||
| 2403 | } | 2422 | } |
| 2404 | 2423 | ||
| 2405 | /// Populates the error name table, when `error_table_symbol` is not null. | 2424 | /// Populates the error name table, when `error_table_symbol` is not null. |
| ... | @@ -2408,22 +2427,17 @@ pub fn getErrorTableSymbol(wasm: *Wasm) !u32 { | ... | @@ -2408,22 +2427,17 @@ pub fn getErrorTableSymbol(wasm: *Wasm) !u32 { |
| 2408 | /// The table is what is being pointed to within the runtime bodies that are generated. | 2427 | /// The table is what is being pointed to within the runtime bodies that are generated. |
| 2409 | fn populateErrorNameTable(wasm: *Wasm) !void { | 2428 | fn populateErrorNameTable(wasm: *Wasm) !void { |
| 2410 | const symbol_index = wasm.error_table_symbol orelse return; | 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 | // Rather than creating a symbol for each individual error name, | 2433 | // Rather than creating a symbol for each individual error name, |
| 2413 | // we create a symbol for the entire region of error names. We then calculate | 2434 | // we create a symbol for the entire region of error names. We then calculate |
| 2414 | // the pointers into the list using addends which are appended to the relocation. | 2435 | // the pointers into the list using addends which are appended to the relocation. |
| 2415 | const names_atom = try wasm.base.allocator.create(Atom); | 2436 | const names_atom_index = try wasm.createAtom(); |
| 2416 | names_atom.* = Atom.empty; | 2437 | const names_atom = wasm.getAtomPtr(names_atom_index); |
| 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; | ||
| 2424 | names_atom.alignment = 1; | 2438 | names_atom.alignment = 1; |
| 2425 | const sym_name = try wasm.string_table.put(wasm.base.allocator, "__zig_err_names"); | 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 | names_symbol.* = .{ | 2441 | names_symbol.* = .{ |
| 2428 | .name = sym_name, | 2442 | .name = sym_name, |
| 2429 | .tag = .data, | 2443 | .tag = .data, |
| ... | @@ -2447,7 +2461,7 @@ fn populateErrorNameTable(wasm: *Wasm) !void { | ... | @@ -2447,7 +2461,7 @@ fn populateErrorNameTable(wasm: *Wasm) !void { |
| 2447 | try atom.code.writer(wasm.base.allocator).writeIntLittle(u32, len - 1); | 2461 | try atom.code.writer(wasm.base.allocator).writeIntLittle(u32, len - 1); |
| 2448 | // create relocation to the error name | 2462 | // create relocation to the error name |
| 2449 | try atom.relocs.append(wasm.base.allocator, .{ | 2463 | try atom.relocs.append(wasm.base.allocator, .{ |
| 2450 | .index = names_symbol_index, | 2464 | .index = names_atom.sym_index, |
| 2451 | .relocation_type = .R_WASM_MEMORY_ADDR_I32, | 2465 | .relocation_type = .R_WASM_MEMORY_ADDR_I32, |
| 2452 | .offset = offset, | 2466 | .offset = offset, |
| 2453 | .addend = @intCast(i32, addend), | 2467 | .addend = @intCast(i32, addend), |
| ... | @@ -2466,61 +2480,53 @@ fn populateErrorNameTable(wasm: *Wasm) !void { | ... | @@ -2466,61 +2480,53 @@ fn populateErrorNameTable(wasm: *Wasm) !void { |
| 2466 | 2480 | ||
| 2467 | const name_loc = names_atom.symbolLoc(); | 2481 | const name_loc = names_atom.symbolLoc(); |
| 2468 | try wasm.resolved_symbols.put(wasm.base.allocator, name_loc, {}); | 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 | // link the atoms with the rest of the binary so they can be allocated | 2485 | // link the atoms with the rest of the binary so they can be allocated |
| 2472 | // and relocations will be performed. | 2486 | // and relocations will be performed. |
| 2473 | try wasm.parseAtom(atom, .{ .data = .read_only }); | 2487 | try wasm.parseAtom(atom_index, .{ .data = .read_only }); |
| 2474 | try wasm.parseAtom(names_atom, .{ .data = .read_only }); | 2488 | try wasm.parseAtom(names_atom_index, .{ .data = .read_only }); |
| 2475 | } | 2489 | } |
| 2476 | 2490 | ||
| 2477 | /// From a given index variable, creates a new debug section. | 2491 | /// From a given index variable, creates a new debug section. |
| 2478 | /// This initializes the index, appends a new segment, | 2492 | /// This initializes the index, appends a new segment, |
| 2479 | /// and finally, creates a managed `Atom`. | 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 | const new_index = @intCast(u32, wasm.segments.items.len); | 2495 | const new_index = @intCast(u32, wasm.segments.items.len); |
| 2482 | index.* = new_index; | 2496 | index.* = new_index; |
| 2483 | try wasm.appendDummySegment(); | 2497 | try wasm.appendDummySegment(); |
| 2484 | 2498 | ||
| 2485 | const sym_index = wasm.symbols_free_list.popOrNull() orelse idx: { | 2499 | const atom_index = try wasm.createAtom(); |
| 2486 | const tmp_index = @intCast(u32, wasm.symbols.items.len); | 2500 | const atom = wasm.getAtomPtr(atom_index); |
| 2487 | _ = try wasm.symbols.addOne(wasm.base.allocator); | 2501 | wasm.symbols.items[atom.sym_index] = .{ |
| 2488 | break :idx tmp_index; | ||
| 2489 | }; | ||
| 2490 | wasm.symbols.items[sym_index] = .{ | ||
| 2491 | .tag = .section, | 2502 | .tag = .section, |
| 2492 | .name = try wasm.string_table.put(wasm.base.allocator, name), | 2503 | .name = try wasm.string_table.put(wasm.base.allocator, name), |
| 2493 | .index = 0, | 2504 | .index = 0, |
| 2494 | .flags = @enumToInt(Symbol.Flag.WASM_SYM_BINDING_LOCAL), | 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 | atom.alignment = 1; // debug sections are always 1-byte-aligned | 2508 | atom.alignment = 1; // debug sections are always 1-byte-aligned |
| 2500 | atom.sym_index = sym_index; | 2509 | return atom_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; | ||
| 2504 | } | 2510 | } |
| 2505 | 2511 | ||
| 2506 | fn resetState(wasm: *Wasm) void { | 2512 | fn resetState(wasm: *Wasm) void { |
| 2507 | for (wasm.segment_info.values()) |segment_info| { | 2513 | for (wasm.segment_info.values()) |segment_info| { |
| 2508 | wasm.base.allocator.free(segment_info.name); | 2514 | wasm.base.allocator.free(segment_info.name); |
| 2509 | } | 2515 | } |
| 2510 | if (wasm.base.options.module) |mod| { | 2516 | |
| 2511 | var decl_it = wasm.decls.keyIterator(); | 2517 | var atom_it = wasm.decls.valueIterator(); |
| 2512 | while (decl_it.next()) |decl_index_ptr| { | 2518 | while (atom_it.next()) |atom_index| { |
| 2513 | const decl = mod.declPtr(decl_index_ptr.*); | 2519 | const atom = wasm.getAtomPtr(atom_index.*); |
| 2514 | const atom = &decl.link.wasm; | 2520 | atom.next = null; |
| 2515 | atom.next = null; | 2521 | atom.prev = null; |
| 2516 | atom.prev = null; | 2522 | |
| 2517 | 2523 | for (atom.locals.items) |local_atom_index| { | |
| 2518 | for (atom.locals.items) |*local_atom| { | 2524 | const local_atom = wasm.getAtomPtr(local_atom_index); |
| 2519 | local_atom.next = null; | 2525 | local_atom.next = null; |
| 2520 | local_atom.prev = null; | 2526 | local_atom.prev = null; |
| 2521 | } | ||
| 2522 | } | 2527 | } |
| 2523 | } | 2528 | } |
| 2529 | |||
| 2524 | wasm.functions.clearRetainingCapacity(); | 2530 | wasm.functions.clearRetainingCapacity(); |
| 2525 | wasm.exports.clearRetainingCapacity(); | 2531 | wasm.exports.clearRetainingCapacity(); |
| 2526 | wasm.segments.clearRetainingCapacity(); | 2532 | wasm.segments.clearRetainingCapacity(); |
| ... | @@ -2817,28 +2823,29 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod | ... | @@ -2817,28 +2823,29 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod |
| 2817 | try wasm.setupStart(); | 2823 | try wasm.setupStart(); |
| 2818 | try wasm.setupImports(); | 2824 | try wasm.setupImports(); |
| 2819 | if (wasm.base.options.module) |mod| { | 2825 | if (wasm.base.options.module) |mod| { |
| 2820 | var decl_it = wasm.decls.keyIterator(); | 2826 | var decl_it = wasm.decls.iterator(); |
| 2821 | while (decl_it.next()) |decl_index_ptr| { | 2827 | while (decl_it.next()) |entry| { |
| 2822 | const decl = mod.declPtr(decl_index_ptr.*); | 2828 | const decl = mod.declPtr(entry.key_ptr.*); |
| 2823 | if (decl.isExtern()) continue; | 2829 | if (decl.isExtern()) continue; |
| 2824 | const atom = &decl.*.link.wasm; | 2830 | const atom_index = entry.value_ptr.*; |
| 2825 | if (decl.ty.zigTypeTag() == .Fn) { | 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 | } else if (decl.getVariable()) |variable| { | 2833 | } else if (decl.getVariable()) |variable| { |
| 2828 | if (!variable.is_mutable) { | 2834 | if (!variable.is_mutable) { |
| 2829 | try wasm.parseAtom(atom, .{ .data = .read_only }); | 2835 | try wasm.parseAtom(atom_index, .{ .data = .read_only }); |
| 2830 | } else if (variable.init.isUndefDeep()) { | 2836 | } else if (variable.init.isUndefDeep()) { |
| 2831 | try wasm.parseAtom(atom, .{ .data = .uninitialized }); | 2837 | try wasm.parseAtom(atom_index, .{ .data = .uninitialized }); |
| 2832 | } else { | 2838 | } else { |
| 2833 | try wasm.parseAtom(atom, .{ .data = .initialized }); | 2839 | try wasm.parseAtom(atom_index, .{ .data = .initialized }); |
| 2834 | } | 2840 | } |
| 2835 | } else { | 2841 | } else { |
| 2836 | try wasm.parseAtom(atom, .{ .data = .read_only }); | 2842 | try wasm.parseAtom(atom_index, .{ .data = .read_only }); |
| 2837 | } | 2843 | } |
| 2838 | 2844 | ||
| 2839 | // also parse atoms for a decl's locals | 2845 | // also parse atoms for a decl's locals |
| 2840 | for (atom.locals.items) |*local_atom| { | 2846 | const atom = wasm.getAtomPtr(atom_index); |
| 2841 | try wasm.parseAtom(local_atom, .{ .data = .read_only }); | 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,20 +3090,22 @@ fn writeToFile( |
| 3083 | var code_section_size: u32 = 0; | 3090 | var code_section_size: u32 = 0; |
| 3084 | if (wasm.code_section_index) |code_index| { | 3091 | if (wasm.code_section_index) |code_index| { |
| 3085 | const header_offset = try reserveVecSectionHeader(&binary_bytes); | 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 | // The code section must be sorted in line with the function order. | 3095 | // The code section must be sorted in line with the function order. |
| 3089 | var sorted_atoms = try std.ArrayList(*Atom).initCapacity(wasm.base.allocator, wasm.functions.count()); | 3096 | var sorted_atoms = try std.ArrayList(*Atom).initCapacity(wasm.base.allocator, wasm.functions.count()); |
| 3090 | defer sorted_atoms.deinit(); | 3097 | defer sorted_atoms.deinit(); |
| 3091 | 3098 | ||
| 3092 | while (true) { | 3099 | while (true) { |
| 3100 | var atom = wasm.getAtomPtr(atom_index); | ||
| 3093 | if (wasm.resolved_symbols.contains(atom.symbolLoc())) { | 3101 | if (wasm.resolved_symbols.contains(atom.symbolLoc())) { |
| 3094 | if (!is_obj) { | 3102 | if (!is_obj) { |
| 3095 | atom.resolveRelocs(wasm); | 3103 | atom.resolveRelocs(wasm); |
| 3096 | } | 3104 | } |
| 3097 | sorted_atoms.appendAssumeCapacity(atom); | 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 | const atom_sort_fn = struct { | 3111 | const atom_sort_fn = struct { |
| ... | @@ -3136,11 +3145,11 @@ fn writeToFile( | ... | @@ -3136,11 +3145,11 @@ fn writeToFile( |
| 3136 | // do not output 'bss' section unless we import memory and therefore | 3145 | // do not output 'bss' section unless we import memory and therefore |
| 3137 | // want to guarantee the data is zero initialized | 3146 | // want to guarantee the data is zero initialized |
| 3138 | if (!import_memory and std.mem.eql(u8, entry.key_ptr.*, ".bss")) continue; | 3147 | if (!import_memory and std.mem.eql(u8, entry.key_ptr.*, ".bss")) continue; |
| 3139 | const atom_index = entry.value_ptr.*; | 3148 | const segment_index = entry.value_ptr.*; |
| 3140 | const segment = wasm.segments.items[atom_index]; | 3149 | const segment = wasm.segments.items[segment_index]; |
| 3141 | if (segment.size == 0) continue; // do not emit empty segments | 3150 | if (segment.size == 0) continue; // do not emit empty segments |
| 3142 | segment_count += 1; | 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 | // flag and index to memory section (currently, there can only be 1 memory section in wasm) | 3154 | // flag and index to memory section (currently, there can only be 1 memory section in wasm) |
| 3146 | try leb.writeULEB128(binary_writer, @as(u32, 0)); | 3155 | try leb.writeULEB128(binary_writer, @as(u32, 0)); |
| ... | @@ -3151,6 +3160,7 @@ fn writeToFile( | ... | @@ -3151,6 +3160,7 @@ fn writeToFile( |
| 3151 | // fill in the offset table and the data segments | 3160 | // fill in the offset table and the data segments |
| 3152 | var current_offset: u32 = 0; | 3161 | var current_offset: u32 = 0; |
| 3153 | while (true) { | 3162 | while (true) { |
| 3163 | const atom = wasm.getAtomPtr(atom_index); | ||
| 3154 | if (!is_obj) { | 3164 | if (!is_obj) { |
| 3155 | atom.resolveRelocs(wasm); | 3165 | atom.resolveRelocs(wasm); |
| 3156 | } | 3166 | } |
| ... | @@ -3166,8 +3176,8 @@ fn writeToFile( | ... | @@ -3166,8 +3176,8 @@ fn writeToFile( |
| 3166 | try binary_writer.writeAll(atom.code.items); | 3176 | try binary_writer.writeAll(atom.code.items); |
| 3167 | 3177 | ||
| 3168 | current_offset += atom.size; | 3178 | current_offset += atom.size; |
| 3169 | if (atom.next) |next| { | 3179 | if (atom.prev) |prev| { |
| 3170 | atom = next; | 3180 | atom_index = prev; |
| 3171 | } else { | 3181 | } else { |
| 3172 | // also pad with zeroes when last atom to ensure | 3182 | // also pad with zeroes when last atom to ensure |
| 3173 | // segments are aligned. | 3183 | // segments are aligned. |
| ... | @@ -3209,15 +3219,15 @@ fn writeToFile( | ... | @@ -3209,15 +3219,15 @@ fn writeToFile( |
| 3209 | } | 3219 | } |
| 3210 | 3220 | ||
| 3211 | if (!wasm.base.options.strip) { | 3221 | if (!wasm.base.options.strip) { |
| 3212 | if (wasm.dwarf) |*dwarf| { | 3222 | // if (wasm.dwarf) |*dwarf| { |
| 3213 | const mod = wasm.base.options.module.?; | 3223 | // const mod = wasm.base.options.module.?; |
| 3214 | try dwarf.writeDbgAbbrev(); | 3224 | // try dwarf.writeDbgAbbrev(); |
| 3215 | // for debug info and ranges, the address is always 0, | 3225 | // // for debug info and ranges, the address is always 0, |
| 3216 | // as locations are always offsets relative to 'code' section. | 3226 | // // as locations are always offsets relative to 'code' section. |
| 3217 | try dwarf.writeDbgInfoHeader(mod, 0, code_section_size); | 3227 | // try dwarf.writeDbgInfoHeader(mod, 0, code_section_size); |
| 3218 | try dwarf.writeDbgAranges(0, code_section_size); | 3228 | // try dwarf.writeDbgAranges(0, code_section_size); |
| 3219 | try dwarf.writeDbgLineHeader(); | 3229 | // try dwarf.writeDbgLineHeader(); |
| 3220 | } | 3230 | // } |
| 3221 | 3231 | ||
| 3222 | var debug_bytes = std.ArrayList(u8).init(wasm.base.allocator); | 3232 | var debug_bytes = std.ArrayList(u8).init(wasm.base.allocator); |
| 3223 | defer debug_bytes.deinit(); | 3233 | defer debug_bytes.deinit(); |
| ... | @@ -3240,11 +3250,11 @@ fn writeToFile( | ... | @@ -3240,11 +3250,11 @@ fn writeToFile( |
| 3240 | 3250 | ||
| 3241 | for (debug_sections) |item| { | 3251 | for (debug_sections) |item| { |
| 3242 | if (item.index) |index| { | 3252 | if (item.index) |index| { |
| 3243 | var atom = wasm.atoms.get(index).?.getFirst(); | 3253 | var atom = wasm.getAtomPtr(wasm.atoms.get(index).?); |
| 3244 | while (true) { | 3254 | while (true) { |
| 3245 | atom.resolveRelocs(wasm); | 3255 | atom.resolveRelocs(wasm); |
| 3246 | try debug_bytes.appendSlice(atom.code.items); | 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 | try emitDebugSection(&binary_bytes, debug_bytes.items, item.name); | 3259 | try emitDebugSection(&binary_bytes, debug_bytes.items, item.name); |
| 3250 | debug_bytes.clearRetainingCapacity(); | 3260 | debug_bytes.clearRetainingCapacity(); |
| ... | @@ -3976,7 +3986,8 @@ fn emitSymbolTable(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), symbol_table: | ... | @@ -3976,7 +3986,8 @@ fn emitSymbolTable(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), symbol_table: |
| 3976 | 3986 | ||
| 3977 | if (symbol.isDefined()) { | 3987 | if (symbol.isDefined()) { |
| 3978 | try leb.writeULEB128(writer, symbol.index); | 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 | try leb.writeULEB128(writer, @as(u32, atom.offset)); | 3991 | try leb.writeULEB128(writer, @as(u32, atom.offset)); |
| 3981 | try leb.writeULEB128(writer, @as(u32, atom.size)); | 3992 | try leb.writeULEB128(writer, @as(u32, atom.size)); |
| 3982 | } | 3993 | } |
| ... | @@ -4054,7 +4065,7 @@ fn emitCodeRelocations( | ... | @@ -4054,7 +4065,7 @@ fn emitCodeRelocations( |
| 4054 | const reloc_start = binary_bytes.items.len; | 4065 | const reloc_start = binary_bytes.items.len; |
| 4055 | 4066 | ||
| 4056 | var count: u32 = 0; | 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 | // for each atom, we calculate the uleb size and append that | 4069 | // for each atom, we calculate the uleb size and append that |
| 4059 | var size_offset: u32 = 5; // account for code section size leb128 | 4070 | var size_offset: u32 = 5; // account for code section size leb128 |
| 4060 | while (true) { | 4071 | while (true) { |
| ... | @@ -4072,7 +4083,7 @@ fn emitCodeRelocations( | ... | @@ -4072,7 +4083,7 @@ fn emitCodeRelocations( |
| 4072 | } | 4083 | } |
| 4073 | log.debug("Emit relocation: {}", .{relocation}); | 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 | if (count == 0) return; | 4088 | if (count == 0) return; |
| 4078 | var buf: [5]u8 = undefined; | 4089 | var buf: [5]u8 = undefined; |
| ... | @@ -4103,7 +4114,7 @@ fn emitDataRelocations( | ... | @@ -4103,7 +4114,7 @@ fn emitDataRelocations( |
| 4103 | // for each atom, we calculate the uleb size and append that | 4114 | // for each atom, we calculate the uleb size and append that |
| 4104 | var size_offset: u32 = 5; // account for code section size leb128 | 4115 | var size_offset: u32 = 5; // account for code section size leb128 |
| 4105 | for (wasm.data_segments.values()) |segment_index| { | 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 | while (true) { | 4118 | while (true) { |
| 4108 | size_offset += getULEB128Size(atom.size); | 4119 | size_offset += getULEB128Size(atom.size); |
| 4109 | for (atom.relocs.items) |relocation| { | 4120 | for (atom.relocs.items) |relocation| { |
| ... | @@ -4122,7 +4133,7 @@ fn emitDataRelocations( | ... | @@ -4122,7 +4133,7 @@ fn emitDataRelocations( |
| 4122 | } | 4133 | } |
| 4123 | log.debug("Emit relocation: {}", .{relocation}); | 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 | if (count == 0) return; | 4139 | if (count == 0) return; |
src/link/Wasm/Atom.zig+24-22| ... | @@ -4,7 +4,6 @@ const std = @import("std"); | ... | @@ -4,7 +4,6 @@ const std = @import("std"); |
| 4 | const types = @import("types.zig"); | 4 | const types = @import("types.zig"); |
| 5 | const Wasm = @import("../Wasm.zig"); | 5 | const Wasm = @import("../Wasm.zig"); |
| 6 | const Symbol = @import("Symbol.zig"); | 6 | const Symbol = @import("Symbol.zig"); |
| 7 | const Dwarf = @import("../Dwarf.zig"); | ||
| 8 | 7 | ||
| 9 | const leb = std.leb; | 8 | const leb = std.leb; |
| 10 | const log = std.log.scoped(.link); | 9 | const log = std.log.scoped(.link); |
| ... | @@ -30,17 +29,17 @@ file: ?u16, | ... | @@ -30,17 +29,17 @@ file: ?u16, |
| 30 | 29 | ||
| 31 | /// Next atom in relation to this atom. | 30 | /// Next atom in relation to this atom. |
| 32 | /// When null, this atom is the last atom | 31 | /// When null, this atom is the last atom |
| 33 | next: ?*Atom, | 32 | next: ?Atom.Index, |
| 34 | /// Previous atom in relation to this atom. | 33 | /// Previous atom in relation to this atom. |
| 35 | /// is null when this atom is the first in its order | 34 | /// is null when this atom is the first in its order |
| 36 | prev: ?*Atom, | 35 | prev: ?Atom.Index, |
| 37 | 36 | ||
| 38 | /// Contains atoms local to a decl, all managed by this `Atom`. | 37 | /// Contains atoms local to a decl, all managed by this `Atom`. |
| 39 | /// When the parent atom is being freed, it will also do so for all local atoms. | 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. | 41 | /// Alias to an unsigned 32-bit integer |
| 43 | dbg_info_atom: Dwarf.Atom, | 42 | pub const Index = u32; |
| 44 | 43 | ||
| 45 | /// Represents a default empty wasm `Atom` | 44 | /// Represents a default empty wasm `Atom` |
| 46 | pub const empty: Atom = .{ | 45 | pub const empty: Atom = .{ |
| ... | @@ -51,18 +50,15 @@ pub const empty: Atom = .{ | ... | @@ -51,18 +50,15 @@ pub const empty: Atom = .{ |
| 51 | .prev = null, | 50 | .prev = null, |
| 52 | .size = 0, | 51 | .size = 0, |
| 53 | .sym_index = 0, | 52 | .sym_index = 0, |
| 54 | .dbg_info_atom = undefined, | ||
| 55 | }; | 53 | }; |
| 56 | 54 | ||
| 57 | /// Frees all resources owned by this `Atom`. | 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 | atom.relocs.deinit(gpa); | 58 | atom.relocs.deinit(gpa); |
| 60 | atom.code.deinit(gpa); | 59 | atom.code.deinit(gpa); |
| 61 | |||
| 62 | for (atom.locals.items) |*local| { | ||
| 63 | local.deinit(gpa); | ||
| 64 | } | ||
| 65 | atom.locals.deinit(gpa); | 60 | atom.locals.deinit(gpa); |
| 61 | atom.* = undefined; | ||
| 66 | } | 62 | } |
| 67 | 63 | ||
| 68 | /// Sets the length of relocations and code to '0', | 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,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 | /// Returns the location of the symbol that represents this `Atom` | 82 | /// Returns the location of the symbol that represents this `Atom` |
| 94 | pub fn symbolLoc(atom: Atom) Wasm.SymbolLoc { | 83 | pub fn symbolLoc(atom: Atom) Wasm.SymbolLoc { |
| 95 | return .{ .file = atom.file, .index = atom.sym_index }; | 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 | /// Returns the virtual address of the `Atom`. This is the address starting | 92 | /// Returns the virtual address of the `Atom`. This is the address starting |
| 99 | /// from the first entry within a section. | 93 | /// from the first entry within a section. |
| 100 | pub fn getVA(atom: Atom, wasm: *const Wasm, symbol: *const Symbol) u32 { | 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,20 +186,28 @@ fn relocationValue(atom: Atom, relocation: types.Relocation, wasm_bin: *const Wa |
| 192 | if (symbol.isUndefined()) { | 186 | if (symbol.isUndefined()) { |
| 193 | return 0; | 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 | const va = @intCast(i32, target_atom.getVA(wasm_bin, symbol)); | 196 | const va = @intCast(i32, target_atom.getVA(wasm_bin, symbol)); |
| 197 | return @intCast(u32, va + relocation.addend); | 197 | return @intCast(u32, va + relocation.addend); |
| 198 | }, | 198 | }, |
| 199 | .R_WASM_EVENT_INDEX_LEB => return symbol.index, | 199 | .R_WASM_EVENT_INDEX_LEB => return symbol.index, |
| 200 | .R_WASM_SECTION_OFFSET_I32 => { | 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 | const rel_value = @intCast(i32, target_atom.offset) + relocation.addend; | 203 | const rel_value = @intCast(i32, target_atom.offset) + relocation.addend; |
| 203 | return @intCast(u32, rel_value); | 204 | return @intCast(u32, rel_value); |
| 204 | }, | 205 | }, |
| 205 | .R_WASM_FUNCTION_OFFSET_I32 => { | 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 | return @bitCast(u32, @as(i32, -1)); | 208 | return @bitCast(u32, @as(i32, -1)); |
| 208 | }; | 209 | }; |
| 210 | const target_atom = wasm_bin.getAtom(target_atom_index); | ||
| 209 | const offset: u32 = 11 + Wasm.getULEB128Size(target_atom.size); // Header (11 bytes fixed-size) + body size (leb-encoded) | 211 | const offset: u32 = 11 + Wasm.getULEB128Size(target_atom.size); // Header (11 bytes fixed-size) + body size (leb-encoded) |
| 210 | const rel_value = @intCast(i32, target_atom.offset + offset) + relocation.addend; | 212 | const rel_value = @intCast(i32, target_atom.offset + offset) + relocation.addend; |
| 211 | return @intCast(u32, rel_value); | 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,14 +901,9 @@ pub fn parseIntoAtoms(object: *Object, gpa: Allocator, object_index: u16, wasm_b |
| 901 | continue; // found unknown section, so skip parsing into atom as we do not know how to handle it. | 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 | atom.* = Atom.empty; | 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 | atom.file = object_index; | 907 | atom.file = object_index; |
| 913 | atom.size = relocatable_data.size; | 908 | atom.size = relocatable_data.size; |
| 914 | atom.alignment = relocatable_data.getAlignment(object); | 909 | atom.alignment = relocatable_data.getAlignment(object); |
| ... | @@ -938,12 +933,12 @@ pub fn parseIntoAtoms(object: *Object, gpa: Allocator, object_index: u16, wasm_b | ... | @@ -938,12 +933,12 @@ pub fn parseIntoAtoms(object: *Object, gpa: Allocator, object_index: u16, wasm_b |
| 938 | .index = relocatable_data.getIndex(), | 933 | .index = relocatable_data.getIndex(), |
| 939 | })) |symbols| { | 934 | })) |symbols| { |
| 940 | atom.sym_index = symbols.pop(); | 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 | // symbols referencing the same atom will be added as alias | 938 | // symbols referencing the same atom will be added as alias |
| 944 | // or as 'parent' when they are global. | 939 | // or as 'parent' when they are global. |
| 945 | while (symbols.popOrNull()) |idx| { | 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 | const alias_symbol = object.symtable[idx]; | 942 | const alias_symbol = object.symtable[idx]; |
| 948 | if (alias_symbol.isGlobal()) { | 943 | if (alias_symbol.isGlobal()) { |
| 949 | atom.sym_index = idx; | 944 | atom.sym_index = idx; |
| ... | @@ -956,7 +951,7 @@ pub fn parseIntoAtoms(object: *Object, gpa: Allocator, object_index: u16, wasm_b | ... | @@ -956,7 +951,7 @@ pub fn parseIntoAtoms(object: *Object, gpa: Allocator, object_index: u16, wasm_b |
| 956 | segment.alignment = std.math.max(segment.alignment, atom.alignment); | 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 | log.debug("Parsed into atom: '{s}' at segment index {d}", .{ object.string_table.get(object.symtable[atom.sym_index].name), final_index }); | 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,7 +893,7 @@ fn buildOutputType( |
| 893 | i: usize = 0, | 893 | i: usize = 0, |
| 894 | fn next(it: *@This()) ?[]const u8 { | 894 | fn next(it: *@This()) ?[]const u8 { |
| 895 | if (it.i >= it.args.len) { | 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 | return null; | 897 | return null; |
| 898 | } | 898 | } |
| 899 | defer it.i += 1; | 899 | defer it.i += 1; |
| ... | @@ -901,7 +901,7 @@ fn buildOutputType( | ... | @@ -901,7 +901,7 @@ fn buildOutputType( |
| 901 | } | 901 | } |
| 902 | fn nextOrFatal(it: *@This()) []const u8 { | 902 | fn nextOrFatal(it: *@This()) []const u8 { |
| 903 | if (it.i >= it.args.len) { | 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 | fatal("expected parameter after {s}", .{it.args[it.i - 1]}); | 905 | fatal("expected parameter after {s}", .{it.args[it.i - 1]}); |
| 906 | } | 906 | } |
| 907 | defer it.i += 1; | 907 | defer it.i += 1; |
| ... | @@ -3915,6 +3915,7 @@ pub const usage_build = | ... | @@ -3915,6 +3915,7 @@ pub const usage_build = |
| 3915 | ; | 3915 | ; |
| 3916 | 3916 | ||
| 3917 | pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { | 3917 | pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { |
| 3918 | var color: Color = .auto; | ||
| 3918 | var prominent_compile_errors: bool = false; | 3919 | var prominent_compile_errors: bool = false; |
| 3919 | 3920 | ||
| 3920 | // We want to release all the locks before executing the child process, so we make a nice | 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,6 +4118,7 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi |
| 4117 | // Here we borrow main package's table and will replace it with a fresh | 4118 | // Here we borrow main package's table and will replace it with a fresh |
| 4118 | // one after this process completes. | 4119 | // one after this process completes. |
| 4119 | main_pkg.fetchAndAddDependencies( | 4120 | main_pkg.fetchAndAddDependencies( |
| 4121 | arena, | ||
| 4120 | &thread_pool, | 4122 | &thread_pool, |
| 4121 | &http_client, | 4123 | &http_client, |
| 4122 | build_directory, | 4124 | build_directory, |
| ... | @@ -4125,6 +4127,7 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi | ... | @@ -4125,6 +4127,7 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi |
| 4125 | &dependencies_source, | 4127 | &dependencies_source, |
| 4126 | &build_roots_source, | 4128 | &build_roots_source, |
| 4127 | "", | 4129 | "", |
| 4130 | color, | ||
| 4128 | ) catch |err| switch (err) { | 4131 | ) catch |err| switch (err) { |
| 4129 | error.PackageFetchFailed => process.exit(1), | 4132 | error.PackageFetchFailed => process.exit(1), |
| 4130 | else => |e| return e, | 4133 | else => |e| return e, |
| ... | @@ -4361,12 +4364,12 @@ pub fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void | ... | @@ -4361,12 +4364,12 @@ pub fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void |
| 4361 | }; | 4364 | }; |
| 4362 | defer gpa.free(source_code); | 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 | fatal("error parsing stdin: {}", .{err}); | 4368 | fatal("error parsing stdin: {}", .{err}); |
| 4366 | }; | 4369 | }; |
| 4367 | defer tree.deinit(gpa); | 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 | var has_ast_error = false; | 4373 | var has_ast_error = false; |
| 4371 | if (check_ast_flag) { | 4374 | if (check_ast_flag) { |
| 4372 | const Module = @import("Module.zig"); | 4375 | const Module = @import("Module.zig"); |
| ... | @@ -4566,10 +4569,10 @@ fn fmtPathFile( | ... | @@ -4566,10 +4569,10 @@ fn fmtPathFile( |
| 4566 | // Add to set after no longer possible to get error.IsDir. | 4569 | // Add to set after no longer possible to get error.IsDir. |
| 4567 | if (try fmt.seen.fetchPut(stat.inode, {})) |_| return; | 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 | defer tree.deinit(fmt.gpa); | 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 | if (tree.errors.len != 0) { | 4576 | if (tree.errors.len != 0) { |
| 4574 | fmt.any_error = true; | 4577 | fmt.any_error = true; |
| 4575 | return; | 4578 | return; |
| ... | @@ -4649,14 +4652,14 @@ fn fmtPathFile( | ... | @@ -4649,14 +4652,14 @@ fn fmtPathFile( |
| 4649 | } | 4652 | } |
| 4650 | } | 4653 | } |
| 4651 | 4654 | ||
| 4652 | fn printErrsMsgToStdErr( | 4655 | pub fn printErrsMsgToStdErr( |
| 4653 | gpa: mem.Allocator, | 4656 | gpa: mem.Allocator, |
| 4654 | arena: mem.Allocator, | 4657 | arena: mem.Allocator, |
| 4655 | parse_errors: []const Ast.Error, | ||
| 4656 | tree: Ast, | 4658 | tree: Ast, |
| 4657 | path: []const u8, | 4659 | path: []const u8, |
| 4658 | color: Color, | 4660 | color: Color, |
| 4659 | ) !void { | 4661 | ) !void { |
| 4662 | const parse_errors: []const Ast.Error = tree.errors; | ||
| 4660 | var i: usize = 0; | 4663 | var i: usize = 0; |
| 4661 | while (i < parse_errors.len) : (i += 1) { | 4664 | while (i < parse_errors.len) : (i += 1) { |
| 4662 | const parse_error = parse_errors[i]; | 4665 | const parse_error = parse_errors[i]; |
| ... | @@ -4973,7 +4976,7 @@ pub const ClangArgIterator = struct { | ... | @@ -4973,7 +4976,7 @@ pub const ClangArgIterator = struct { |
| 4973 | // rather than an argument to a parameter. | 4976 | // rather than an argument to a parameter. |
| 4974 | // We adjust the len below when necessary. | 4977 | // We adjust the len below when necessary. |
| 4975 | self.other_args = (self.argv.ptr + self.next_index)[0..1]; | 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 | self.incrementArgIndex(); | 4980 | self.incrementArgIndex(); |
| 4978 | 4981 | ||
| 4979 | if (mem.startsWith(u8, arg, "@")) { | 4982 | if (mem.startsWith(u8, arg, "@")) { |
| ... | @@ -5017,7 +5020,7 @@ pub const ClangArgIterator = struct { | ... | @@ -5017,7 +5020,7 @@ pub const ClangArgIterator = struct { |
| 5017 | 5020 | ||
| 5018 | self.has_next = true; | 5021 | self.has_next = true; |
| 5019 | self.other_args = (self.argv.ptr + self.next_index)[0..1]; // We adjust len below when necessary. | 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 | self.incrementArgIndex(); | 5024 | self.incrementArgIndex(); |
| 5022 | } | 5025 | } |
| 5023 | 5026 | ||
| ... | @@ -5312,11 +5315,11 @@ pub fn cmdAstCheck( | ... | @@ -5312,11 +5315,11 @@ pub fn cmdAstCheck( |
| 5312 | file.pkg = try Package.create(gpa, "root", null, file.sub_file_path); | 5315 | file.pkg = try Package.create(gpa, "root", null, file.sub_file_path); |
| 5313 | defer file.pkg.destroy(gpa); | 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 | file.tree_loaded = true; | 5319 | file.tree_loaded = true; |
| 5317 | defer file.tree.deinit(gpa); | 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 | if (file.tree.errors.len != 0) { | 5323 | if (file.tree.errors.len != 0) { |
| 5321 | process.exit(1); | 5324 | process.exit(1); |
| 5322 | } | 5325 | } |
| ... | @@ -5438,11 +5441,11 @@ pub fn cmdChangelist( | ... | @@ -5438,11 +5441,11 @@ pub fn cmdChangelist( |
| 5438 | file.source = source; | 5441 | file.source = source; |
| 5439 | file.source_loaded = true; | 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 | file.tree_loaded = true; | 5445 | file.tree_loaded = true; |
| 5443 | defer file.tree.deinit(gpa); | 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 | if (file.tree.errors.len != 0) { | 5449 | if (file.tree.errors.len != 0) { |
| 5447 | process.exit(1); | 5450 | process.exit(1); |
| 5448 | } | 5451 | } |
| ... | @@ -5476,10 +5479,10 @@ pub fn cmdChangelist( | ... | @@ -5476,10 +5479,10 @@ pub fn cmdChangelist( |
| 5476 | if (new_amt != new_stat.size) | 5479 | if (new_amt != new_stat.size) |
| 5477 | return error.UnexpectedEndOfFile; | 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 | defer new_tree.deinit(gpa); | 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 | if (new_tree.errors.len != 0) { | 5486 | if (new_tree.errors.len != 0) { |
| 5484 | process.exit(1); | 5487 | process.exit(1); |
| 5485 | } | 5488 | } |
src/mingw.zig+1| ... | @@ -106,6 +106,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { | ... | @@ -106,6 +106,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { |
| 106 | .msvcrt_os_lib => { | 106 | .msvcrt_os_lib => { |
| 107 | const extra_flags = try arena.dupe([]const u8, &[_][]const u8{ | 107 | const extra_flags = try arena.dupe([]const u8, &[_][]const u8{ |
| 108 | "-DHAVE_CONFIG_H", | 108 | "-DHAVE_CONFIG_H", |
| 109 | "-D__LIBMSVCRT__", | ||
| 109 | "-D__LIBMSVCRT_OS__", | 110 | "-D__LIBMSVCRT_OS__", |
| 110 | 111 | ||
| 111 | "-I", | 112 | "-I", |
src/print_zir.zig+1| ... | @@ -332,6 +332,7 @@ const Writer = struct { | ... | @@ -332,6 +332,7 @@ const Writer = struct { |
| 332 | .float_cast, | 332 | .float_cast, |
| 333 | .int_cast, | 333 | .int_cast, |
| 334 | .ptr_cast, | 334 | .ptr_cast, |
| 335 | .qual_cast, | ||
| 335 | .truncate, | 336 | .truncate, |
| 336 | .align_cast, | 337 | .align_cast, |
| 337 | .div_exact, | 338 | .div_exact, |
src/translate_c.zig+4-1| ... | @@ -4519,7 +4519,10 @@ fn transCreateNodeAssign( | ... | @@ -4519,7 +4519,10 @@ fn transCreateNodeAssign( |
| 4519 | defer block_scope.deinit(); | 4519 | defer block_scope.deinit(); |
| 4520 | 4520 | ||
| 4521 | const tmp = try block_scope.makeMangledName(c, "tmp"); | 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 | const tmp_decl = try Tag.var_simple.create(c.arena, .{ .name = tmp, .init = rhs_node }); | 4526 | const tmp_decl = try Tag.var_simple.create(c.arena, .{ .name = tmp, .init = rhs_node }); |
| 4524 | try block_scope.statements.append(tmp_decl); | 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,24 +2937,24 @@ pub const Type = extern union { |
| 2937 | .anyframe_T, | 2937 | .anyframe_T, |
| 2938 | => return AbiAlignmentAdvanced{ .scalar = @divExact(target.cpu.arch.ptrBitWidth(), 8) }, | 2938 | => return AbiAlignmentAdvanced{ .scalar = @divExact(target.cpu.arch.ptrBitWidth(), 8) }, |
| 2939 | 2939 | ||
| 2940 | .c_short => return AbiAlignmentAdvanced{ .scalar = CType.short.alignment(target) }, | 2940 | .c_short => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.short) }, |
| 2941 | .c_ushort => return AbiAlignmentAdvanced{ .scalar = CType.ushort.alignment(target) }, | 2941 | .c_ushort => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.ushort) }, |
| 2942 | .c_int => return AbiAlignmentAdvanced{ .scalar = CType.int.alignment(target) }, | 2942 | .c_int => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.int) }, |
| 2943 | .c_uint => return AbiAlignmentAdvanced{ .scalar = CType.uint.alignment(target) }, | 2943 | .c_uint => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.uint) }, |
| 2944 | .c_long => return AbiAlignmentAdvanced{ .scalar = CType.long.alignment(target) }, | 2944 | .c_long => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.long) }, |
| 2945 | .c_ulong => return AbiAlignmentAdvanced{ .scalar = CType.ulong.alignment(target) }, | 2945 | .c_ulong => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.ulong) }, |
| 2946 | .c_longlong => return AbiAlignmentAdvanced{ .scalar = CType.longlong.alignment(target) }, | 2946 | .c_longlong => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.longlong) }, |
| 2947 | .c_ulonglong => return AbiAlignmentAdvanced{ .scalar = CType.ulonglong.alignment(target) }, | 2947 | .c_ulonglong => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.ulonglong) }, |
| 2948 | .c_longdouble => return AbiAlignmentAdvanced{ .scalar = CType.longdouble.alignment(target) }, | 2948 | .c_longdouble => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.longdouble) }, |
| 2949 | 2949 | ||
| 2950 | .f16 => return AbiAlignmentAdvanced{ .scalar = 2 }, | 2950 | .f16 => return AbiAlignmentAdvanced{ .scalar = 2 }, |
| 2951 | .f32 => return AbiAlignmentAdvanced{ .scalar = CType.float.alignment(target) }, | 2951 | .f32 => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.float) }, |
| 2952 | .f64 => switch (CType.double.sizeInBits(target)) { | 2952 | .f64 => switch (target.c_type_bit_size(.double)) { |
| 2953 | 64 => return AbiAlignmentAdvanced{ .scalar = CType.double.alignment(target) }, | 2953 | 64 => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.double) }, |
| 2954 | else => return AbiAlignmentAdvanced{ .scalar = 8 }, | 2954 | else => return AbiAlignmentAdvanced{ .scalar = 8 }, |
| 2955 | }, | 2955 | }, |
| 2956 | .f80 => switch (CType.longdouble.sizeInBits(target)) { | 2956 | .f80 => switch (target.c_type_bit_size(.longdouble)) { |
| 2957 | 80 => return AbiAlignmentAdvanced{ .scalar = CType.longdouble.alignment(target) }, | 2957 | 80 => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.longdouble) }, |
| 2958 | else => { | 2958 | else => { |
| 2959 | var payload: Payload.Bits = .{ | 2959 | var payload: Payload.Bits = .{ |
| 2960 | .base = .{ .tag = .int_unsigned }, | 2960 | .base = .{ .tag = .int_unsigned }, |
| ... | @@ -2964,8 +2964,8 @@ pub const Type = extern union { | ... | @@ -2964,8 +2964,8 @@ pub const Type = extern union { |
| 2964 | return AbiAlignmentAdvanced{ .scalar = abiAlignment(u80_ty, target) }; | 2964 | return AbiAlignmentAdvanced{ .scalar = abiAlignment(u80_ty, target) }; |
| 2965 | }, | 2965 | }, |
| 2966 | }, | 2966 | }, |
| 2967 | .f128 => switch (CType.longdouble.sizeInBits(target)) { | 2967 | .f128 => switch (target.c_type_bit_size(.longdouble)) { |
| 2968 | 128 => return AbiAlignmentAdvanced{ .scalar = CType.longdouble.alignment(target) }, | 2968 | 128 => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.longdouble) }, |
| 2969 | else => return AbiAlignmentAdvanced{ .scalar = 16 }, | 2969 | else => return AbiAlignmentAdvanced{ .scalar = 16 }, |
| 2970 | }, | 2970 | }, |
| 2971 | 2971 | ||
| ... | @@ -3434,21 +3434,22 @@ pub const Type = extern union { | ... | @@ -3434,21 +3434,22 @@ pub const Type = extern union { |
| 3434 | else => return AbiSizeAdvanced{ .scalar = @divExact(target.cpu.arch.ptrBitWidth(), 8) }, | 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) }, | 3437 | .c_short => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.short) }, |
| 3438 | .c_ushort => return AbiSizeAdvanced{ .scalar = @divExact(CType.ushort.sizeInBits(target), 8) }, | 3438 | .c_ushort => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.ushort) }, |
| 3439 | .c_int => return AbiSizeAdvanced{ .scalar = @divExact(CType.int.sizeInBits(target), 8) }, | 3439 | .c_int => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.int) }, |
| 3440 | .c_uint => return AbiSizeAdvanced{ .scalar = @divExact(CType.uint.sizeInBits(target), 8) }, | 3440 | .c_uint => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.uint) }, |
| 3441 | .c_long => return AbiSizeAdvanced{ .scalar = @divExact(CType.long.sizeInBits(target), 8) }, | 3441 | .c_long => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.long) }, |
| 3442 | .c_ulong => return AbiSizeAdvanced{ .scalar = @divExact(CType.ulong.sizeInBits(target), 8) }, | 3442 | .c_ulong => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.ulong) }, |
| 3443 | .c_longlong => return AbiSizeAdvanced{ .scalar = @divExact(CType.longlong.sizeInBits(target), 8) }, | 3443 | .c_longlong => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.longlong) }, |
| 3444 | .c_ulonglong => return AbiSizeAdvanced{ .scalar = @divExact(CType.ulonglong.sizeInBits(target), 8) }, | 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 | .f16 => return AbiSizeAdvanced{ .scalar = 2 }, | 3447 | .f16 => return AbiSizeAdvanced{ .scalar = 2 }, |
| 3447 | .f32 => return AbiSizeAdvanced{ .scalar = 4 }, | 3448 | .f32 => return AbiSizeAdvanced{ .scalar = 4 }, |
| 3448 | .f64 => return AbiSizeAdvanced{ .scalar = 8 }, | 3449 | .f64 => return AbiSizeAdvanced{ .scalar = 8 }, |
| 3449 | .f128 => return AbiSizeAdvanced{ .scalar = 16 }, | 3450 | .f128 => return AbiSizeAdvanced{ .scalar = 16 }, |
| 3450 | .f80 => switch (CType.longdouble.sizeInBits(target)) { | 3451 | .f80 => switch (target.c_type_bit_size(.longdouble)) { |
| 3451 | 80 => return AbiSizeAdvanced{ .scalar = std.mem.alignForward(10, CType.longdouble.alignment(target)) }, | 3452 | 80 => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.longdouble) }, |
| 3452 | else => { | 3453 | else => { |
| 3453 | var payload: Payload.Bits = .{ | 3454 | var payload: Payload.Bits = .{ |
| 3454 | .base = .{ .tag = .int_unsigned }, | 3455 | .base = .{ .tag = .int_unsigned }, |
| ... | @@ -3458,14 +3459,6 @@ pub const Type = extern union { | ... | @@ -3458,14 +3459,6 @@ pub const Type = extern union { |
| 3458 | return AbiSizeAdvanced{ .scalar = abiSize(u80_ty, target) }; | 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 | // TODO revisit this when we have the concept of the error tag type | 3463 | // TODO revisit this when we have the concept of the error tag type |
| 3471 | .anyerror_void_error_union, | 3464 | .anyerror_void_error_union, |
| ... | @@ -3748,15 +3741,15 @@ pub const Type = extern union { | ... | @@ -3748,15 +3741,15 @@ pub const Type = extern union { |
| 3748 | .manyptr_const_u8_sentinel_0, | 3741 | .manyptr_const_u8_sentinel_0, |
| 3749 | => return target.cpu.arch.ptrBitWidth(), | 3742 | => return target.cpu.arch.ptrBitWidth(), |
| 3750 | 3743 | ||
| 3751 | .c_short => return CType.short.sizeInBits(target), | 3744 | .c_short => return target.c_type_bit_size(.short), |
| 3752 | .c_ushort => return CType.ushort.sizeInBits(target), | 3745 | .c_ushort => return target.c_type_bit_size(.ushort), |
| 3753 | .c_int => return CType.int.sizeInBits(target), | 3746 | .c_int => return target.c_type_bit_size(.int), |
| 3754 | .c_uint => return CType.uint.sizeInBits(target), | 3747 | .c_uint => return target.c_type_bit_size(.uint), |
| 3755 | .c_long => return CType.long.sizeInBits(target), | 3748 | .c_long => return target.c_type_bit_size(.long), |
| 3756 | .c_ulong => return CType.ulong.sizeInBits(target), | 3749 | .c_ulong => return target.c_type_bit_size(.ulong), |
| 3757 | .c_longlong => return CType.longlong.sizeInBits(target), | 3750 | .c_longlong => return target.c_type_bit_size(.longlong), |
| 3758 | .c_ulonglong => return CType.ulonglong.sizeInBits(target), | 3751 | .c_ulonglong => return target.c_type_bit_size(.ulonglong), |
| 3759 | .c_longdouble => return CType.longdouble.sizeInBits(target), | 3752 | .c_longdouble => return target.c_type_bit_size(.longdouble), |
| 3760 | 3753 | ||
| 3761 | .error_set, | 3754 | .error_set, |
| 3762 | .error_set_single, | 3755 | .error_set_single, |
| ... | @@ -4631,14 +4624,14 @@ pub const Type = extern union { | ... | @@ -4631,14 +4624,14 @@ pub const Type = extern union { |
| 4631 | .i128 => return .{ .signedness = .signed, .bits = 128 }, | 4624 | .i128 => return .{ .signedness = .signed, .bits = 128 }, |
| 4632 | .usize => return .{ .signedness = .unsigned, .bits = target.cpu.arch.ptrBitWidth() }, | 4625 | .usize => return .{ .signedness = .unsigned, .bits = target.cpu.arch.ptrBitWidth() }, |
| 4633 | .isize => return .{ .signedness = .signed, .bits = target.cpu.arch.ptrBitWidth() }, | 4626 | .isize => return .{ .signedness = .signed, .bits = target.cpu.arch.ptrBitWidth() }, |
| 4634 | .c_short => return .{ .signedness = .signed, .bits = CType.short.sizeInBits(target) }, | 4627 | .c_short => return .{ .signedness = .signed, .bits = target.c_type_bit_size(.short) }, |
| 4635 | .c_ushort => return .{ .signedness = .unsigned, .bits = CType.ushort.sizeInBits(target) }, | 4628 | .c_ushort => return .{ .signedness = .unsigned, .bits = target.c_type_bit_size(.ushort) }, |
| 4636 | .c_int => return .{ .signedness = .signed, .bits = CType.int.sizeInBits(target) }, | 4629 | .c_int => return .{ .signedness = .signed, .bits = target.c_type_bit_size(.int) }, |
| 4637 | .c_uint => return .{ .signedness = .unsigned, .bits = CType.uint.sizeInBits(target) }, | 4630 | .c_uint => return .{ .signedness = .unsigned, .bits = target.c_type_bit_size(.uint) }, |
| 4638 | .c_long => return .{ .signedness = .signed, .bits = CType.long.sizeInBits(target) }, | 4631 | .c_long => return .{ .signedness = .signed, .bits = target.c_type_bit_size(.long) }, |
| 4639 | .c_ulong => return .{ .signedness = .unsigned, .bits = CType.ulong.sizeInBits(target) }, | 4632 | .c_ulong => return .{ .signedness = .unsigned, .bits = target.c_type_bit_size(.ulong) }, |
| 4640 | .c_longlong => return .{ .signedness = .signed, .bits = CType.longlong.sizeInBits(target) }, | 4633 | .c_longlong => return .{ .signedness = .signed, .bits = target.c_type_bit_size(.longlong) }, |
| 4641 | .c_ulonglong => return .{ .signedness = .unsigned, .bits = CType.ulonglong.sizeInBits(target) }, | 4634 | .c_ulonglong => return .{ .signedness = .unsigned, .bits = target.c_type_bit_size(.ulonglong) }, |
| 4642 | 4635 | ||
| 4643 | .enum_full, .enum_nonexhaustive => ty = ty.cast(Payload.EnumFull).?.data.tag_ty, | 4636 | .enum_full, .enum_nonexhaustive => ty = ty.cast(Payload.EnumFull).?.data.tag_ty, |
| 4644 | .enum_numbered => ty = ty.castTag(.enum_numbered).?.data.tag_ty, | 4637 | .enum_numbered => ty = ty.castTag(.enum_numbered).?.data.tag_ty, |
| ... | @@ -4724,7 +4717,7 @@ pub const Type = extern union { | ... | @@ -4724,7 +4717,7 @@ pub const Type = extern union { |
| 4724 | .f64 => 64, | 4717 | .f64 => 64, |
| 4725 | .f80 => 80, | 4718 | .f80 => 80, |
| 4726 | .f128, .comptime_float => 128, | 4719 | .f128, .comptime_float => 128, |
| 4727 | .c_longdouble => CType.longdouble.sizeInBits(target), | 4720 | .c_longdouble => target.c_type_bit_size(.longdouble), |
| 4728 | 4721 | ||
| 4729 | else => unreachable, | 4722 | else => unreachable, |
| 4730 | }; | 4723 | }; |
| ... | @@ -6689,537 +6682,3 @@ pub const Type = extern union { | ... | @@ -6689,537 +6682,3 @@ pub const Type = extern union { |
| 6689 | /// to packed struct layout to find out all the places in the codebase you need to edit! | 6682 | /// to packed struct layout to find out all the places in the codebase you need to edit! |
| 6690 | pub const packed_struct_layout_version = 2; | 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,7 +703,7 @@ test "string concatenation" { |
| 703 | comptime try expect(@TypeOf(a) == *const [12:0]u8); | 703 | comptime try expect(@TypeOf(a) == *const [12:0]u8); |
| 704 | comptime try expect(@TypeOf(b) == *const [12:0]u8); | 704 | comptime try expect(@TypeOf(b) == *const [12:0]u8); |
| 705 | 705 | ||
| 706 | const len = mem.len(b); | 706 | const len = b.len; |
| 707 | const len_with_null = len + 1; | 707 | const len_with_null = len + 1; |
| 708 | { | 708 | { |
| 709 | var i: u32 = 0; | 709 | var i: u32 = 0; |
| ... | @@ -1125,3 +1125,21 @@ test "returning an opaque type from a function" { | ... | @@ -1125,3 +1125,21 @@ test "returning an opaque type from a function" { |
| 1125 | }; | 1125 | }; |
| 1126 | try expect(S.foo(123).b == 123); | 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,7 +1179,6 @@ fn peerTypeEmptyArrayAndSlice(a: bool, slice: []const u8) []const u8 { |
| 1179 | test "implicitly cast from [N]T to ?[]const T" { | 1179 | test "implicitly cast from [N]T to ?[]const T" { |
| 1180 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 1180 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 1181 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 1181 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 1182 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | ||
| 1183 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 1182 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 1184 | 1183 | ||
| 1185 | try expect(mem.eql(u8, castToOptionalSlice().?, "hi")); | 1184 | try expect(mem.eql(u8, castToOptionalSlice().?, "hi")); |
| ... | @@ -1264,7 +1263,6 @@ test "cast from array reference to fn: runtime fn ptr" { | ... | @@ -1264,7 +1263,6 @@ test "cast from array reference to fn: runtime fn ptr" { |
| 1264 | test "*const [N]null u8 to ?[]const u8" { | 1263 | test "*const [N]null u8 to ?[]const u8" { |
| 1265 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 1264 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 1266 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 1265 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 1267 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | ||
| 1268 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 1266 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 1269 | 1267 | ||
| 1270 | const S = struct { | 1268 | const S = struct { |
| ... | @@ -1413,7 +1411,6 @@ test "cast i8 fn call peers to i32 result" { | ... | @@ -1413,7 +1411,6 @@ test "cast i8 fn call peers to i32 result" { |
| 1413 | test "cast compatible optional types" { | 1411 | test "cast compatible optional types" { |
| 1414 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 1412 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 1415 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 1413 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 1416 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | ||
| 1417 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 1414 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 1418 | 1415 | ||
| 1419 | var a: ?[:0]const u8 = null; | 1416 | var a: ?[:0]const u8 = null; |
test/behavior/error.zig+15| ... | @@ -896,3 +896,18 @@ test "optional error union return type" { | ... | @@ -896,3 +896,18 @@ test "optional error union return type" { |
| 896 | }; | 896 | }; |
| 897 | try expect(1234 == try S.foo().?); | 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,7 +1332,6 @@ test "float remainder division using @rem" { |
| 1332 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 1332 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 1333 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 1333 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 1334 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 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 | comptime try frem(f16); | 1336 | comptime try frem(f16); |
| 1338 | comptime try frem(f32); | 1337 | comptime try frem(f32); |
| ... | @@ -1375,7 +1374,6 @@ test "float modulo division using @mod" { | ... | @@ -1375,7 +1374,6 @@ test "float modulo division using @mod" { |
| 1375 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 1374 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 1376 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 1375 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 1377 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 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 | comptime try fmod(f16); | 1378 | comptime try fmod(f16); |
| 1381 | comptime try fmod(f32); | 1379 | comptime try fmod(f32); |
| ... | @@ -1438,7 +1436,6 @@ test "@round f80" { | ... | @@ -1438,7 +1436,6 @@ test "@round f80" { |
| 1438 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 1436 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 1439 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 1437 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 1440 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 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 | try testRound(f80, 12.0); | 1440 | try testRound(f80, 12.0); |
| 1444 | comptime try testRound(f80, 12.0); | 1441 | comptime try testRound(f80, 12.0); |
test/behavior/muladd.zig-2| ... | @@ -50,7 +50,6 @@ test "@mulAdd f80" { | ... | @@ -50,7 +50,6 @@ test "@mulAdd f80" { |
| 50 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 50 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 51 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 51 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 52 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 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 | comptime try testMulAdd80(); | 54 | comptime try testMulAdd80(); |
| 56 | try testMulAdd80(); | 55 | try testMulAdd80(); |
| ... | @@ -178,7 +177,6 @@ test "vector f80" { | ... | @@ -178,7 +177,6 @@ test "vector f80" { |
| 178 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 177 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 179 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 178 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 180 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 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 | comptime try vector80(); | 181 | comptime try vector80(); |
| 184 | try vector80(); | 182 | try vector80(); |
test/behavior/optional.zig-2| ... | @@ -439,7 +439,6 @@ test "Optional slice size is optimized" { | ... | @@ -439,7 +439,6 @@ test "Optional slice size is optimized" { |
| 439 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 439 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 440 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 440 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 441 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | 441 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; |
| 442 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; | ||
| 443 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 442 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 444 | 443 | ||
| 445 | try expect(@sizeOf(?[]u8) == @sizeOf([]u8)); | 444 | try expect(@sizeOf(?[]u8) == @sizeOf([]u8)); |
| ... | @@ -479,7 +478,6 @@ test "cast slice to const slice nested in error union and optional" { | ... | @@ -479,7 +478,6 @@ test "cast slice to const slice nested in error union and optional" { |
| 479 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 478 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 480 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | 479 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; |
| 481 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | 480 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; |
| 482 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; | ||
| 483 | 481 | ||
| 484 | const S = struct { | 482 | const S = struct { |
| 485 | fn inner() !?[]u8 { | 483 | fn inner() !?[]u8 { |
test/behavior/sizeof_and_typeof.zig+9| ... | @@ -292,3 +292,12 @@ test "@sizeOf optional of previously unresolved union" { | ... | @@ -292,3 +292,12 @@ test "@sizeOf optional of previously unresolved union" { |
| 292 | const Node = union { a: usize }; | 292 | const Node = union { a: usize }; |
| 293 | try expect(@sizeOf(?Node) == @sizeOf(Node) + @alignOf(Node)); | 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,8 +7,10 @@ test "thread local variable" { |
| 7 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 7 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 8 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 8 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 9 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 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 | 10 | if (builtin.zig_backend == .stage2_llvm) switch (builtin.cpu.arch) { |
| 11 | if (builtin.zig_backend == .stage2_c and builtin.os.tag == .windows) return error.SkipZigTest; // TODO | 11 | .x86_64, .x86 => {}, |
| 12 | else => return error.SkipZigTest, | ||
| 13 | }; // TODO | ||
| 12 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 14 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 13 | 15 | ||
| 14 | const S = struct { | 16 | const S = struct { |
| ... | @@ -23,8 +25,10 @@ test "pointer to thread local array" { | ... | @@ -23,8 +25,10 @@ test "pointer to thread local array" { |
| 23 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 25 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 24 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 26 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 25 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 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 | 28 | if (builtin.zig_backend == .stage2_llvm) switch (builtin.cpu.arch) { |
| 27 | if (builtin.zig_backend == .stage2_c and builtin.os.tag == .windows) return error.SkipZigTest; // TODO | 29 | .x86_64, .x86 => {}, |
| 30 | else => return error.SkipZigTest, | ||
| 31 | }; // TODO | ||
| 28 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 32 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 29 | 33 | ||
| 30 | const s = "Hello world"; | 34 | const s = "Hello world"; |
| ... | @@ -39,8 +43,10 @@ test "reference a global threadlocal variable" { | ... | @@ -39,8 +43,10 @@ test "reference a global threadlocal variable" { |
| 39 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | 43 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO |
| 40 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 44 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 41 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 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 | 46 | if (builtin.zig_backend == .stage2_llvm) switch (builtin.cpu.arch) { |
| 43 | if (builtin.zig_backend == .stage2_c and builtin.os.tag == .windows) return error.SkipZigTest; // TODO | 47 | .x86_64, .x86 => {}, |
| 48 | else => return error.SkipZigTest, | ||
| 49 | }; // TODO | ||
| 44 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 50 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 45 | 51 | ||
| 46 | _ = nrfx_uart_rx(&g_uart0); | 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,4 +20,4 @@ export fn entry() void { |
| 20 | // | 20 | // |
| 21 | // :11:27: error: expected type 'u8', found '?u8' | 21 | // :11:27: error: expected type 'u8', found '?u8' |
| 22 | // :11:27: note: cannot convert optional to payload type | 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,4 +10,4 @@ fn foo() !void { |
| 10 | // target=native | 10 | // target=native |
| 11 | // | 11 | // |
| 12 | // :2:12: error: error is discarded | 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,7 +26,7 @@ export fn quux() u32 { |
| 26 | // :11:15: error: expected type 'u32', found '@typeInfo(@typeInfo(@TypeOf(tmp.bar)).Fn.return_type.?).ErrorUnion.error_set!u32' | 26 | // :11:15: error: expected type 'u32', found '@typeInfo(@typeInfo(@TypeOf(tmp.bar)).Fn.return_type.?).ErrorUnion.error_set!u32' |
| 27 | // :10:17: note: function cannot return an error | 27 | // :10:17: note: function cannot return an error |
| 28 | // :11:15: note: cannot convert error union to payload type | 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 | // :15:14: error: expected type 'u32', found '@typeInfo(@typeInfo(@TypeOf(tmp.bar)).Fn.return_type.?).ErrorUnion.error_set!u32' | 30 | // :15:14: error: expected type 'u32', found '@typeInfo(@typeInfo(@TypeOf(tmp.bar)).Fn.return_type.?).ErrorUnion.error_set!u32' |
| 31 | // :15:14: note: cannot convert error union to payload type | 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,4 +8,4 @@ fn bar() anyerror!i32 { return 0; } |
| 8 | // target=native | 8 | // target=native |
| 9 | // | 9 | // |
| 10 | // :2:14: error: error is ignored | 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,8 +18,8 @@ fn bad() anyerror!void { |
| 18 | // target=native | 18 | // target=native |
| 19 | // | 19 | // |
| 20 | // :2:24: error: error is ignored | 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 | // :6:25: error: error is ignored | 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 | // :10:25: error: error is ignored | 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,3 +11,4 @@ export fn entry() u32 { |
| 11 | // :3:17: error: cast increases pointer alignment | 11 | // :3:17: error: cast increases pointer alignment |
| 12 | // :3:32: note: '*u8' has alignment '1' | 12 | // :3:32: note: '*u8' has alignment '1' |
| 13 | // :3:26: note: '*u32' has alignment '4' | 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,6 +1,6 @@ |
| 1 | const builtin = @import("std").builtin; | 1 | const builtin = @import("std").builtin; |
| 2 | export fn entry() void { | 2 | export fn entry() void { |
| 3 | const foo = builtin.Mode.x86; | 3 | const foo = builtin.OptimizeMode.x86; |
| 4 | _ = foo; | 4 | _ = foo; |
| 5 | } | 5 | } |
| 6 | 6 | ||
| ... | @@ -8,5 +8,5 @@ export fn entry() void { | ... | @@ -8,5 +8,5 @@ export fn entry() void { |
| 8 | // backend=stage2 | 8 | // backend=stage2 |
| 9 | // target=native | 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 | // :?:18: note: enum declared here | 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,5 +10,5 @@ export fn foo() void { |
| 10 | // | 10 | // |
| 11 | // :4:9: error: expected type '*anyopaque', found '?*anyopaque' | 11 | // :4:9: error: expected type '*anyopaque', found '?*anyopaque' |
| 12 | // :4:9: note: cannot convert optional to payload type | 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 | // :4:9: note: '?*anyopaque' could have null values which are illegal in type '*anyopaque' | 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,3 +9,4 @@ export fn entry() void { |
| 9 | // target=native | 9 | // target=native |
| 10 | // | 10 | // |
| 11 | // :3:15: error: cast discards const qualifier | 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,4 +20,4 @@ export fn entry() void { |
| 20 | // | 20 | // |
| 21 | // :12:25: error: expected type 'u32', found '@typeInfo(@typeInfo(@TypeOf(tmp.get_uval)).Fn.return_type.?).ErrorUnion.error_set!u32' | 21 | // :12:25: error: expected type 'u32', found '@typeInfo(@typeInfo(@TypeOf(tmp.get_uval)).Fn.return_type.?).ErrorUnion.error_set!u32' |
| 22 | // :12:25: note: cannot convert error union to payload type | 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,4 +17,4 @@ pub const Container = struct { |
| 17 | // | 17 | // |
| 18 | // :3:36: error: expected type 'i32', found '?i32' | 18 | // :3:36: error: expected type 'i32', found '?i32' |
| 19 | // :3:36: note: cannot convert optional to payload type | 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,4 +17,4 @@ pub const Container = struct { |
| 17 | // | 17 | // |
| 18 | // :3:36: error: expected type 'i32', found '?i32' | 18 | // :3:36: error: expected type 'i32', found '?i32' |
| 19 | // :3:36: note: cannot convert optional to payload type | 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,12 +1,15 @@ |
| 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 mode = b.standardReleaseOptions(); | 4 | const optimize = b.standardOptimizeOption(.{}); |
| 5 | const test_step = b.step("test", "Test"); | 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 | b.default_step.dependOn(&exe.step); | 12 | b.default_step.dependOn(&exe.step); |
| 9 | exe.setBuildMode(mode); | ||
| 10 | 13 | ||
| 11 | const run = exe.run(); | 14 | const run = exe.run(); |
| 12 | run.expectStdOutEqual("0, 1, 0\n"); | 15 | run.expectStdOutEqual("0, 1, 0\n"); |
test/link/common_symbols/build.zig+12-7| ... | @@ -1,14 +1,19 @@ | ... | @@ -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 { | 3 | pub fn build(b: *std.Build) void { |
| 4 | const mode = b.standardReleaseOptions(); | 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 | lib_a.addCSourceFiles(&.{ "c.c", "a.c", "b.c" }, &.{"-fcommon"}); | 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"); | 13 | const test_exe = b.addTest(.{ |
| 11 | test_exe.setBuildMode(mode); | 14 | .root_source_file = .{ .path = "main.zig" }, |
| 15 | .optimize = optimize, | ||
| 16 | }); | ||
| 12 | test_exe.linkLibrary(lib_a); | 17 | test_exe.linkLibrary(lib_a); |
| 13 | 18 | ||
| 14 | const test_step = b.step("test", "Test it"); | 19 | const test_step = b.step("test", "Test it"); |
test/link/common_symbols_alignment/build.zig+14-7| ... | @@ -1,14 +1,21 @@ | ... | @@ -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 { | 3 | pub fn build(b: *std.Build) void { |
| 4 | const mode = b.standardReleaseOptions(); | 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 | lib_a.addCSourceFiles(&.{"a.c"}, &.{"-fcommon"}); | 12 | lib_a.addCSourceFiles(&.{"a.c"}, &.{"-fcommon"}); |
| 8 | lib_a.setBuildMode(mode); | ||
| 9 | 13 | ||
| 10 | const test_exe = b.addTest("main.zig"); | 14 | const test_exe = b.addTest(.{ |
| 11 | test_exe.setBuildMode(mode); | 15 | .root_source_file = .{ .path = "main.zig" }, |
| 16 | .optimize = optimize, | ||
| 17 | .target = target, | ||
| 18 | }); | ||
| 12 | test_exe.linkLibrary(lib_a); | 19 | test_exe.linkLibrary(lib_a); |
| 13 | 20 | ||
| 14 | const test_step = b.step("test", "Test it"); | 21 | const test_step = b.step("test", "Test it"); |
test/link/interdependent_static_c_libs/build.zig+19-9| ... | @@ -1,20 +1,30 @@ | ... | @@ -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 { | 3 | pub fn build(b: *std.Build) void { |
| 4 | const mode = b.standardReleaseOptions(); | 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 | lib_a.addCSourceFile("a.c", &[_][]const u8{}); | 12 | lib_a.addCSourceFile("a.c", &[_][]const u8{}); |
| 8 | lib_a.setBuildMode(mode); | ||
| 9 | lib_a.addIncludePath("."); | 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 | lib_b.addCSourceFile("b.c", &[_][]const u8{}); | 20 | lib_b.addCSourceFile("b.c", &[_][]const u8{}); |
| 13 | lib_b.setBuildMode(mode); | ||
| 14 | lib_b.addIncludePath("."); | 21 | lib_b.addIncludePath("."); |
| 15 | 22 | ||
| 16 | const test_exe = b.addTest("main.zig"); | 23 | const test_exe = b.addTest(.{ |
| 17 | test_exe.setBuildMode(mode); | 24 | .root_source_file = .{ .path = "main.zig" }, |
| 25 | .optimize = optimize, | ||
| 26 | .target = target, | ||
| 27 | }); | ||
| 18 | test_exe.linkLibrary(lib_a); | 28 | test_exe.linkLibrary(lib_a); |
| 19 | test_exe.linkLibrary(lib_b); | 29 | test_exe.linkLibrary(lib_b); |
| 20 | test_exe.addIncludePath("."); | 30 | test_exe.addIncludePath("."); |
test/link/macho/bugs/13056/build.zig+6-5| ... | @@ -1,8 +1,7 @@ | ... | @@ -1,8 +1,7 @@ |
| 1 | const std = @import("std"); | 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 | const mode = b.standardReleaseOptions(); | 4 | const optimize = b.standardOptimizeOption(.{}); |
| 6 | 5 | ||
| 7 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; | 6 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; |
| 8 | const target_info = std.zig.system.NativeTargetInfo.detect(target) catch unreachable; | 7 | const target_info = std.zig.system.NativeTargetInfo.detect(target) catch unreachable; |
| ... | @@ -11,7 +10,10 @@ pub fn build(b: *Builder) void { | ... | @@ -11,7 +10,10 @@ pub fn build(b: *Builder) void { |
| 11 | 10 | ||
| 12 | const test_step = b.step("test", "Test the program"); | 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 | b.default_step.dependOn(&exe.step); | 17 | b.default_step.dependOn(&exe.step); |
| 16 | exe.addIncludePath(std.fs.path.join(b.allocator, &.{ sdk.path, "/usr/include" }) catch unreachable); | 18 | exe.addIncludePath(std.fs.path.join(b.allocator, &.{ sdk.path, "/usr/include" }) catch unreachable); |
| 17 | exe.addIncludePath(std.fs.path.join(b.allocator, &.{ sdk.path, "/usr/include/c++/v1" }) catch unreachable); | 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,7 +22,6 @@ pub fn build(b: *Builder) void { |
| 20 | "-nostdinc++", | 22 | "-nostdinc++", |
| 21 | }); | 23 | }); |
| 22 | exe.addObjectFile(std.fs.path.join(b.allocator, &.{ sdk.path, "/usr/lib/libc++.tbd" }) catch unreachable); | 24 | exe.addObjectFile(std.fs.path.join(b.allocator, &.{ sdk.path, "/usr/lib/libc++.tbd" }) catch unreachable); |
| 23 | exe.setBuildMode(mode); | ||
| 24 | 25 | ||
| 25 | const run_cmd = exe.run(); | 26 | const run_cmd = exe.run(); |
| 26 | run_cmd.expectStdErrEqual("x: 5\n"); | 27 | run_cmd.expectStdErrEqual("x: 5\n"); |
test/link/macho/bugs/13457/build.zig+8-7| ... | @@ -1,16 +1,17 @@ | ... | @@ -1,16 +1,17 @@ |
| 1 | const std = @import("std"); | 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 | const mode = b.standardReleaseOptions(); | 4 | const optimize = b.standardOptimizeOption(.{}); |
| 7 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; | 5 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; |
| 8 | 6 | ||
| 9 | const test_step = b.step("test", "Test the program"); | 7 | const test_step = b.step("test", "Test the program"); |
| 10 | 8 | ||
| 11 | const exe = b.addExecutable("test", "main.zig"); | 9 | const exe = b.addExecutable(.{ |
| 12 | exe.setBuildMode(mode); | 10 | .name = "test", |
| 13 | exe.setTarget(target); | 11 | .root_source_file = .{ .path = "main.zig" }, |
| 12 | .optimize = optimize, | ||
| 13 | .target = target, | ||
| 14 | }); | ||
| 14 | 15 | ||
| 15 | const run = exe.runEmulatable(); | 16 | const run = exe.runEmulatable(); |
| 16 | test_step.dependOn(&run.step); | 17 | test_step.dependOn(&run.step); |
test/link/macho/dead_strip/build.zig+14-10| ... | @@ -1,9 +1,7 @@ | ... | @@ -1,9 +1,7 @@ |
| 1 | const std = @import("std"); | 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 | const mode = b.standardReleaseOptions(); | 4 | const optimize = b.standardOptimizeOption(.{}); |
| 7 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; | 5 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; |
| 8 | 6 | ||
| 9 | const test_step = b.step("test", "Test the program"); | 7 | const test_step = b.step("test", "Test the program"); |
| ... | @@ -11,7 +9,7 @@ pub fn build(b: *Builder) void { | ... | @@ -11,7 +9,7 @@ pub fn build(b: *Builder) void { |
| 11 | 9 | ||
| 12 | { | 10 | { |
| 13 | // Without -dead_strip, we expect `iAmUnused` symbol present | 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 | const check = exe.checkObject(.macho); | 14 | const check = exe.checkObject(.macho); |
| 17 | check.checkInSymtab(); | 15 | check.checkInSymtab(); |
| ... | @@ -24,7 +22,7 @@ pub fn build(b: *Builder) void { | ... | @@ -24,7 +22,7 @@ pub fn build(b: *Builder) void { |
| 24 | 22 | ||
| 25 | { | 23 | { |
| 26 | // With -dead_strip, no `iAmUnused` symbol should be present | 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 | exe.link_gc_sections = true; | 26 | exe.link_gc_sections = true; |
| 29 | 27 | ||
| 30 | const check = exe.checkObject(.macho); | 28 | const check = exe.checkObject(.macho); |
| ... | @@ -37,11 +35,17 @@ pub fn build(b: *Builder) void { | ... | @@ -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 { | 38 | fn createScenario( |
| 41 | const exe = b.addExecutable("test", null); | 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 | exe.addCSourceFile("main.c", &[0][]const u8{}); | 48 | exe.addCSourceFile("main.c", &[0][]const u8{}); |
| 43 | exe.setBuildMode(mode); | ||
| 44 | exe.setTarget(target); | ||
| 45 | exe.linkLibC(); | 49 | exe.linkLibC(); |
| 46 | return exe; | 50 | return exe; |
| 47 | } | 51 | } |
test/link/macho/dead_strip_dylibs/build.zig+9-9| ... | @@ -1,16 +1,14 @@ | ... | @@ -1,16 +1,14 @@ |
| 1 | const std = @import("std"); | 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 | const mode = b.standardReleaseOptions(); | 4 | const optimize = b.standardOptimizeOption(.{}); |
| 7 | 5 | ||
| 8 | const test_step = b.step("test", "Test the program"); | 6 | const test_step = b.step("test", "Test the program"); |
| 9 | test_step.dependOn(b.getInstallStep()); | 7 | test_step.dependOn(b.getInstallStep()); |
| 10 | 8 | ||
| 11 | { | 9 | { |
| 12 | // Without -dead_strip_dylibs we expect `-la` to include liba.dylib in the final executable | 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 | const check = exe.checkObject(.macho); | 13 | const check = exe.checkObject(.macho); |
| 16 | check.checkStart("cmd LOAD_DYLIB"); | 14 | check.checkStart("cmd LOAD_DYLIB"); |
| ... | @@ -27,7 +25,7 @@ pub fn build(b: *Builder) void { | ... | @@ -27,7 +25,7 @@ pub fn build(b: *Builder) void { |
| 27 | 25 | ||
| 28 | { | 26 | { |
| 29 | // With -dead_strip_dylibs, we should include liba.dylib as it's unreachable | 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 | exe.dead_strip_dylibs = true; | 29 | exe.dead_strip_dylibs = true; |
| 32 | 30 | ||
| 33 | const run_cmd = exe.run(); | 31 | const run_cmd = exe.run(); |
| ... | @@ -36,10 +34,12 @@ pub fn build(b: *Builder) void { | ... | @@ -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 { | 37 | fn createScenario(b: *std.Build, optimize: std.builtin.OptimizeMode) *std.Build.CompileStep { |
| 40 | const exe = b.addExecutable("test", null); | 38 | const exe = b.addExecutable(.{ |
| 39 | .name = "test", | ||
| 40 | .optimize = optimize, | ||
| 41 | }); | ||
| 41 | exe.addCSourceFile("main.c", &[0][]const u8{}); | 42 | exe.addCSourceFile("main.c", &[0][]const u8{}); |
| 42 | exe.setBuildMode(mode); | ||
| 43 | exe.linkLibC(); | 43 | exe.linkLibC(); |
| 44 | exe.linkFramework("Cocoa"); | 44 | exe.linkFramework("Cocoa"); |
| 45 | return exe; | 45 | return exe; |
test/link/macho/dylib/build.zig+13-9| ... | @@ -1,16 +1,18 @@ | ... | @@ -1,16 +1,18 @@ |
| 1 | const std = @import("std"); | 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 | const mode = b.standardReleaseOptions(); | 4 | const optimize = b.standardOptimizeOption(.{}); |
| 6 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; | 5 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; |
| 7 | 6 | ||
| 8 | const test_step = b.step("test", "Test"); | 7 | const test_step = b.step("test", "Test"); |
| 9 | test_step.dependOn(b.getInstallStep()); | 8 | test_step.dependOn(b.getInstallStep()); |
| 10 | 9 | ||
| 11 | const dylib = b.addSharedLibrary("a", null, b.version(1, 0, 0)); | 10 | const dylib = b.addSharedLibrary(.{ |
| 12 | dylib.setBuildMode(mode); | 11 | .name = "a", |
| 13 | dylib.setTarget(target); | 12 | .version = .{ .major = 1, .minor = 0 }, |
| 13 | .optimize = optimize, | ||
| 14 | .target = target, | ||
| 15 | }); | ||
| 14 | dylib.addCSourceFile("a.c", &.{}); | 16 | dylib.addCSourceFile("a.c", &.{}); |
| 15 | dylib.linkLibC(); | 17 | dylib.linkLibC(); |
| 16 | dylib.install(); | 18 | dylib.install(); |
| ... | @@ -24,9 +26,11 @@ pub fn build(b: *Builder) void { | ... | @@ -24,9 +26,11 @@ pub fn build(b: *Builder) void { |
| 24 | 26 | ||
| 25 | test_step.dependOn(&check_dylib.step); | 27 | test_step.dependOn(&check_dylib.step); |
| 26 | 28 | ||
| 27 | const exe = b.addExecutable("main", null); | 29 | const exe = b.addExecutable(.{ |
| 28 | exe.setTarget(target); | 30 | .name = "main", |
| 29 | exe.setBuildMode(mode); | 31 | .optimize = optimize, |
| 32 | .target = target, | ||
| 33 | }); | ||
| 30 | exe.addCSourceFile("main.c", &.{}); | 34 | exe.addCSourceFile("main.c", &.{}); |
| 31 | exe.linkSystemLibrary("a"); | 35 | exe.linkSystemLibrary("a"); |
| 32 | exe.linkLibC(); | 36 | exe.linkLibC(); |
test/link/macho/empty/build.zig+8-7| ... | @@ -1,21 +1,22 @@ | ... | @@ -1,21 +1,22 @@ |
| 1 | const std = @import("std"); | 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 | const mode = b.standardReleaseOptions(); | 4 | const optimize = b.standardOptimizeOption(.{}); |
| 6 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; | 5 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; |
| 7 | 6 | ||
| 8 | const test_step = b.step("test", "Test the program"); | 7 | const test_step = b.step("test", "Test the program"); |
| 9 | test_step.dependOn(b.getInstallStep()); | 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 | exe.addCSourceFile("main.c", &[0][]const u8{}); | 15 | exe.addCSourceFile("main.c", &[0][]const u8{}); |
| 13 | exe.addCSourceFile("empty.c", &[0][]const u8{}); | 16 | exe.addCSourceFile("empty.c", &[0][]const u8{}); |
| 14 | exe.setBuildMode(mode); | ||
| 15 | exe.setTarget(target); | ||
| 16 | exe.linkLibC(); | 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 | run_cmd.expectStdOutEqual("Hello!\n"); | 20 | run_cmd.expectStdOutEqual("Hello!\n"); |
| 20 | test_step.dependOn(&run_cmd.step); | 21 | test_step.dependOn(&run_cmd.step); |
| 21 | } | 22 | } |
test/link/macho/entry/build.zig+7-6| ... | @@ -1,15 +1,16 @@ | ... | @@ -1,15 +1,16 @@ |
| 1 | const std = @import("std"); | 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 | const mode = b.standardReleaseOptions(); | 4 | const optimize = b.standardOptimizeOption(.{}); |
| 6 | 5 | ||
| 7 | const test_step = b.step("test", "Test"); | 6 | const test_step = b.step("test", "Test"); |
| 8 | test_step.dependOn(b.getInstallStep()); | 7 | test_step.dependOn(b.getInstallStep()); |
| 9 | 8 | ||
| 10 | const exe = b.addExecutable("main", null); | 9 | const exe = b.addExecutable(.{ |
| 11 | exe.setTarget(.{ .os_tag = .macos }); | 10 | .name = "main", |
| 12 | exe.setBuildMode(mode); | 11 | .optimize = optimize, |
| 12 | .target = .{ .os_tag = .macos }, | ||
| 13 | }); | ||
| 13 | exe.addCSourceFile("main.c", &.{}); | 14 | exe.addCSourceFile("main.c", &.{}); |
| 14 | exe.linkLibC(); | 15 | exe.linkLibC(); |
| 15 | exe.entry_symbol_name = "_non_main"; | 16 | exe.entry_symbol_name = "_non_main"; |
test/link/macho/headerpad/build.zig+11-11| ... | @@ -1,17 +1,15 @@ | ... | @@ -1,17 +1,15 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | 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 { | 4 | pub fn build(b: *std.Build) void { |
| 7 | const mode = b.standardReleaseOptions(); | 5 | const optimize = b.standardOptimizeOption(.{}); |
| 8 | 6 | ||
| 9 | const test_step = b.step("test", "Test"); | 7 | const test_step = b.step("test", "Test"); |
| 10 | test_step.dependOn(b.getInstallStep()); | 8 | test_step.dependOn(b.getInstallStep()); |
| 11 | 9 | ||
| 12 | { | 10 | { |
| 13 | // Test -headerpad_max_install_names | 11 | // Test -headerpad_max_install_names |
| 14 | const exe = simpleExe(b, mode); | 12 | const exe = simpleExe(b, optimize); |
| 15 | exe.headerpad_max_install_names = true; | 13 | exe.headerpad_max_install_names = true; |
| 16 | 14 | ||
| 17 | const check = exe.checkObject(.macho); | 15 | const check = exe.checkObject(.macho); |
| ... | @@ -36,7 +34,7 @@ pub fn build(b: *Builder) void { | ... | @@ -36,7 +34,7 @@ pub fn build(b: *Builder) void { |
| 36 | 34 | ||
| 37 | { | 35 | { |
| 38 | // Test -headerpad | 36 | // Test -headerpad |
| 39 | const exe = simpleExe(b, mode); | 37 | const exe = simpleExe(b, optimize); |
| 40 | exe.headerpad_size = 0x10000; | 38 | exe.headerpad_size = 0x10000; |
| 41 | 39 | ||
| 42 | const check = exe.checkObject(.macho); | 40 | const check = exe.checkObject(.macho); |
| ... | @@ -52,7 +50,7 @@ pub fn build(b: *Builder) void { | ... | @@ -52,7 +50,7 @@ pub fn build(b: *Builder) void { |
| 52 | 50 | ||
| 53 | { | 51 | { |
| 54 | // Test both flags with -headerpad overriding -headerpad_max_install_names | 52 | // Test both flags with -headerpad overriding -headerpad_max_install_names |
| 55 | const exe = simpleExe(b, mode); | 53 | const exe = simpleExe(b, optimize); |
| 56 | exe.headerpad_max_install_names = true; | 54 | exe.headerpad_max_install_names = true; |
| 57 | exe.headerpad_size = 0x10000; | 55 | exe.headerpad_size = 0x10000; |
| 58 | 56 | ||
| ... | @@ -69,7 +67,7 @@ pub fn build(b: *Builder) void { | ... | @@ -69,7 +67,7 @@ pub fn build(b: *Builder) void { |
| 69 | 67 | ||
| 70 | { | 68 | { |
| 71 | // Test both flags with -headerpad_max_install_names overriding -headerpad | 69 | // Test both flags with -headerpad_max_install_names overriding -headerpad |
| 72 | const exe = simpleExe(b, mode); | 70 | const exe = simpleExe(b, optimize); |
| 73 | exe.headerpad_size = 0x1000; | 71 | exe.headerpad_size = 0x1000; |
| 74 | exe.headerpad_max_install_names = true; | 72 | exe.headerpad_max_install_names = true; |
| 75 | 73 | ||
| ... | @@ -94,9 +92,11 @@ pub fn build(b: *Builder) void { | ... | @@ -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 { | 95 | fn simpleExe(b: *std.Build, optimize: std.builtin.OptimizeMode) *std.Build.CompileStep { |
| 98 | const exe = b.addExecutable("main", null); | 96 | const exe = b.addExecutable(.{ |
| 99 | exe.setBuildMode(mode); | 97 | .name = "main", |
| 98 | .optimize = optimize, | ||
| 99 | }); | ||
| 100 | exe.addCSourceFile("main.c", &.{}); | 100 | exe.addCSourceFile("main.c", &.{}); |
| 101 | exe.linkLibC(); | 101 | exe.linkLibC(); |
| 102 | exe.linkFramework("CoreFoundation"); | 102 | exe.linkFramework("CoreFoundation"); |
test/link/macho/linksection/build.zig+9-6| ... | @@ -1,15 +1,18 @@ | ... | @@ -1,15 +1,18 @@ |
| 1 | const std = @import("std"); | 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 | const mode = b.standardReleaseOptions(); | 4 | const optimize = b.standardOptimizeOption(.{}); |
| 5 | const target = std.zig.CrossTarget{ .os_tag = .macos }; | 5 | const target = std.zig.CrossTarget{ .os_tag = .macos }; |
| 6 | 6 | ||
| 7 | const test_step = b.step("test", "Test"); | 7 | const test_step = b.step("test", "Test"); |
| 8 | test_step.dependOn(b.getInstallStep()); | 8 | test_step.dependOn(b.getInstallStep()); |
| 9 | 9 | ||
| 10 | const obj = b.addObject("test", "main.zig"); | 10 | const obj = b.addObject(.{ |
| 11 | obj.setBuildMode(mode); | 11 | .name = "test", |
| 12 | obj.setTarget(target); | 12 | .root_source_file = .{ .path = "main.zig" }, |
| 13 | .optimize = optimize, | ||
| 14 | .target = target, | ||
| 15 | }); | ||
| 13 | 16 | ||
| 14 | const check = obj.checkObject(.macho); | 17 | const check = obj.checkObject(.macho); |
| 15 | 18 | ||
| ... | @@ -19,7 +22,7 @@ pub fn build(b: *std.build.Builder) void { | ... | @@ -19,7 +22,7 @@ pub fn build(b: *std.build.Builder) void { |
| 19 | check.checkInSymtab(); | 22 | check.checkInSymtab(); |
| 20 | check.checkNext("{*} (__TEXT,__TestFn) external _testFn"); | 23 | check.checkNext("{*} (__TEXT,__TestFn) external _testFn"); |
| 21 | 24 | ||
| 22 | if (mode == .Debug) { | 25 | if (optimize == .Debug) { |
| 23 | check.checkInSymtab(); | 26 | check.checkInSymtab(); |
| 24 | check.checkNext("{*} (__TEXT,__TestGenFnA) _main.testGenericFn__anon_{*}"); | 27 | check.checkNext("{*} (__TEXT,__TestGenFnA) _main.testGenericFn__anon_{*}"); |
| 25 | } | 28 | } |
test/link/macho/needed_framework/build.zig+6-6| ... | @@ -1,18 +1,18 @@ | ... | @@ -1,18 +1,18 @@ |
| 1 | const std = @import("std"); | 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 | const mode = b.standardReleaseOptions(); | 4 | const optimize = b.standardOptimizeOption(.{}); |
| 7 | 5 | ||
| 8 | const test_step = b.step("test", "Test the program"); | 6 | const test_step = b.step("test", "Test the program"); |
| 9 | test_step.dependOn(b.getInstallStep()); | 7 | test_step.dependOn(b.getInstallStep()); |
| 10 | 8 | ||
| 11 | // -dead_strip_dylibs | 9 | // -dead_strip_dylibs |
| 12 | // -needed_framework Cocoa | 10 | // -needed_framework Cocoa |
| 13 | const exe = b.addExecutable("test", null); | 11 | const exe = b.addExecutable(.{ |
| 12 | .name = "test", | ||
| 13 | .optimize = optimize, | ||
| 14 | }); | ||
| 14 | exe.addCSourceFile("main.c", &[0][]const u8{}); | 15 | exe.addCSourceFile("main.c", &[0][]const u8{}); |
| 15 | exe.setBuildMode(mode); | ||
| 16 | exe.linkLibC(); | 16 | exe.linkLibC(); |
| 17 | exe.linkFrameworkNeeded("Cocoa"); | 17 | exe.linkFrameworkNeeded("Cocoa"); |
| 18 | exe.dead_strip_dylibs = true; | 18 | exe.dead_strip_dylibs = true; |
test/link/macho/needed_library/build.zig+13-10| ... | @@ -1,27 +1,30 @@ | ... | @@ -1,27 +1,30 @@ |
| 1 | const std = @import("std"); | 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 | const mode = b.standardReleaseOptions(); | 4 | const optimize = b.standardOptimizeOption(.{}); |
| 7 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; | 5 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; |
| 8 | 6 | ||
| 9 | const test_step = b.step("test", "Test the program"); | 7 | const test_step = b.step("test", "Test the program"); |
| 10 | test_step.dependOn(b.getInstallStep()); | 8 | test_step.dependOn(b.getInstallStep()); |
| 11 | 9 | ||
| 12 | const dylib = b.addSharedLibrary("a", null, b.version(1, 0, 0)); | 10 | const dylib = b.addSharedLibrary(.{ |
| 13 | dylib.setTarget(target); | 11 | .name = "a", |
| 14 | dylib.setBuildMode(mode); | 12 | .version = .{ .major = 1, .minor = 0 }, |
| 13 | .optimize = optimize, | ||
| 14 | .target = target, | ||
| 15 | }); | ||
| 15 | dylib.addCSourceFile("a.c", &.{}); | 16 | dylib.addCSourceFile("a.c", &.{}); |
| 16 | dylib.linkLibC(); | 17 | dylib.linkLibC(); |
| 17 | dylib.install(); | 18 | dylib.install(); |
| 18 | 19 | ||
| 19 | // -dead_strip_dylibs | 20 | // -dead_strip_dylibs |
| 20 | // -needed-la | 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 | exe.addCSourceFile("main.c", &[0][]const u8{}); | 27 | exe.addCSourceFile("main.c", &[0][]const u8{}); |
| 23 | exe.setBuildMode(mode); | ||
| 24 | exe.setTarget(target); | ||
| 25 | exe.linkLibC(); | 28 | exe.linkLibC(); |
| 26 | exe.linkSystemLibraryNeeded("a"); | 29 | exe.linkSystemLibraryNeeded("a"); |
| 27 | exe.addLibraryPath(b.pathFromRoot("zig-out/lib")); | 30 | exe.addLibraryPath(b.pathFromRoot("zig-out/lib")); |
test/link/macho/objc/build.zig+7-6| ... | @@ -1,21 +1,22 @@ | ... | @@ -1,21 +1,22 @@ |
| 1 | const std = @import("std"); | 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 | const mode = b.standardReleaseOptions(); | 4 | const optimize = b.standardOptimizeOption(.{}); |
| 6 | 5 | ||
| 7 | const test_step = b.step("test", "Test the program"); | 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 | exe.addIncludePath("."); | 12 | exe.addIncludePath("."); |
| 11 | exe.addCSourceFile("Foo.m", &[0][]const u8{}); | 13 | exe.addCSourceFile("Foo.m", &[0][]const u8{}); |
| 12 | exe.addCSourceFile("test.m", &[0][]const u8{}); | 14 | exe.addCSourceFile("test.m", &[0][]const u8{}); |
| 13 | exe.setBuildMode(mode); | ||
| 14 | exe.linkLibC(); | 15 | exe.linkLibC(); |
| 15 | // TODO when we figure out how to ship framework stubs for cross-compilation, | 16 | // TODO when we figure out how to ship framework stubs for cross-compilation, |
| 16 | // populate paths to the sysroot here. | 17 | // populate paths to the sysroot here. |
| 17 | exe.linkFramework("Foundation"); | 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 | test_step.dependOn(&run_cmd.step); | 21 | test_step.dependOn(&run_cmd.step); |
| 21 | } | 22 | } |
test/link/macho/objcpp/build.zig+6-5| ... | @@ -1,17 +1,18 @@ | ... | @@ -1,17 +1,18 @@ |
| 1 | const std = @import("std"); | 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 | const mode = b.standardReleaseOptions(); | 4 | const optimize = b.standardOptimizeOption(.{}); |
| 6 | 5 | ||
| 7 | const test_step = b.step("test", "Test the program"); | 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 | b.default_step.dependOn(&exe.step); | 12 | b.default_step.dependOn(&exe.step); |
| 11 | exe.addIncludePath("."); | 13 | exe.addIncludePath("."); |
| 12 | exe.addCSourceFile("Foo.mm", &[0][]const u8{}); | 14 | exe.addCSourceFile("Foo.mm", &[0][]const u8{}); |
| 13 | exe.addCSourceFile("test.mm", &[0][]const u8{}); | 15 | exe.addCSourceFile("test.mm", &[0][]const u8{}); |
| 14 | exe.setBuildMode(mode); | ||
| 15 | exe.linkLibCpp(); | 16 | exe.linkLibCpp(); |
| 16 | // TODO when we figure out how to ship framework stubs for cross-compilation, | 17 | // TODO when we figure out how to ship framework stubs for cross-compilation, |
| 17 | // populate paths to the sysroot here. | 18 | // populate paths to the sysroot here. |
test/link/macho/pagezero/build.zig+12-9| ... | @@ -1,17 +1,18 @@ | ... | @@ -1,17 +1,18 @@ |
| 1 | const std = @import("std"); | 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 | const mode = b.standardReleaseOptions(); | 4 | const optimize = b.standardOptimizeOption(.{}); |
| 6 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; | 5 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; |
| 7 | 6 | ||
| 8 | const test_step = b.step("test", "Test"); | 7 | const test_step = b.step("test", "Test"); |
| 9 | test_step.dependOn(b.getInstallStep()); | 8 | test_step.dependOn(b.getInstallStep()); |
| 10 | 9 | ||
| 11 | { | 10 | { |
| 12 | const exe = b.addExecutable("pagezero", null); | 11 | const exe = b.addExecutable(.{ |
| 13 | exe.setTarget(target); | 12 | .name = "pagezero", |
| 14 | exe.setBuildMode(mode); | 13 | .optimize = optimize, |
| 14 | .target = target, | ||
| 15 | }); | ||
| 15 | exe.addCSourceFile("main.c", &.{}); | 16 | exe.addCSourceFile("main.c", &.{}); |
| 16 | exe.linkLibC(); | 17 | exe.linkLibC(); |
| 17 | exe.pagezero_size = 0x4000; | 18 | exe.pagezero_size = 0x4000; |
| ... | @@ -29,9 +30,11 @@ pub fn build(b: *Builder) void { | ... | @@ -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 | const exe = b.addExecutable(.{ |
| 33 | exe.setTarget(target); | 34 | .name = "no_pagezero", |
| 34 | exe.setBuildMode(mode); | 35 | .optimize = optimize, |
| 36 | .target = target, | ||
| 37 | }); | ||
| 35 | exe.addCSourceFile("main.c", &.{}); | 38 | exe.addCSourceFile("main.c", &.{}); |
| 36 | exe.linkLibC(); | 39 | exe.linkLibC(); |
| 37 | exe.pagezero_size = 0; | 40 | exe.pagezero_size = 0; |
test/link/macho/search_strategy/build.zig+28-19| ... | @@ -1,9 +1,7 @@ | ... | @@ -1,9 +1,7 @@ |
| 1 | const std = @import("std"); | 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 | const mode = b.standardReleaseOptions(); | 4 | const optimize = b.standardOptimizeOption(.{}); |
| 7 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; | 5 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; |
| 8 | 6 | ||
| 9 | const test_step = b.step("test", "Test"); | 7 | const test_step = b.step("test", "Test"); |
| ... | @@ -11,7 +9,7 @@ pub fn build(b: *Builder) void { | ... | @@ -11,7 +9,7 @@ pub fn build(b: *Builder) void { |
| 11 | 9 | ||
| 12 | { | 10 | { |
| 13 | // -search_dylibs_first | 11 | // -search_dylibs_first |
| 14 | const exe = createScenario(b, mode, target); | 12 | const exe = createScenario(b, optimize, target); |
| 15 | exe.search_strategy = .dylibs_first; | 13 | exe.search_strategy = .dylibs_first; |
| 16 | 14 | ||
| 17 | const check = exe.checkObject(.macho); | 15 | const check = exe.checkObject(.macho); |
| ... | @@ -26,40 +24,51 @@ pub fn build(b: *Builder) void { | ... | @@ -26,40 +24,51 @@ pub fn build(b: *Builder) void { |
| 26 | 24 | ||
| 27 | { | 25 | { |
| 28 | // -search_paths_first | 26 | // -search_paths_first |
| 29 | const exe = createScenario(b, mode, target); | 27 | const exe = createScenario(b, optimize, target); |
| 30 | exe.search_strategy = .paths_first; | 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 | run.cwd = b.pathFromRoot("."); | 31 | run.cwd = b.pathFromRoot("."); |
| 34 | run.expectStdOutEqual("Hello world"); | 32 | run.expectStdOutEqual("Hello world"); |
| 35 | test_step.dependOn(&run.step); | 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 { | 37 | fn createScenario( |
| 40 | const static = b.addStaticLibrary("a", null); | 38 | b: *std.Build, |
| 41 | static.setTarget(target); | 39 | optimize: std.builtin.OptimizeMode, |
| 42 | static.setBuildMode(mode); | 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 | static.addCSourceFile("a.c", &.{}); | 47 | static.addCSourceFile("a.c", &.{}); |
| 44 | static.linkLibC(); | 48 | static.linkLibC(); |
| 45 | static.override_dest_dir = std.build.InstallDir{ | 49 | static.override_dest_dir = std.Build.InstallDir{ |
| 46 | .custom = "static", | 50 | .custom = "static", |
| 47 | }; | 51 | }; |
| 48 | static.install(); | 52 | static.install(); |
| 49 | 53 | ||
| 50 | const dylib = b.addSharedLibrary("a", null, b.version(1, 0, 0)); | 54 | const dylib = b.addSharedLibrary(.{ |
| 51 | dylib.setTarget(target); | 55 | .name = "a", |
| 52 | dylib.setBuildMode(mode); | 56 | .version = .{ .major = 1, .minor = 0 }, |
| 57 | .optimize = optimize, | ||
| 58 | .target = target, | ||
| 59 | }); | ||
| 53 | dylib.addCSourceFile("a.c", &.{}); | 60 | dylib.addCSourceFile("a.c", &.{}); |
| 54 | dylib.linkLibC(); | 61 | dylib.linkLibC(); |
| 55 | dylib.override_dest_dir = std.build.InstallDir{ | 62 | dylib.override_dest_dir = std.Build.InstallDir{ |
| 56 | .custom = "dynamic", | 63 | .custom = "dynamic", |
| 57 | }; | 64 | }; |
| 58 | dylib.install(); | 65 | dylib.install(); |
| 59 | 66 | ||
| 60 | const exe = b.addExecutable("main", null); | 67 | const exe = b.addExecutable(.{ |
| 61 | exe.setTarget(target); | 68 | .name = "main", |
| 62 | exe.setBuildMode(mode); | 69 | .optimize = optimize, |
| 70 | .target = target, | ||
| 71 | }); | ||
| 63 | exe.addCSourceFile("main.c", &.{}); | 72 | exe.addCSourceFile("main.c", &.{}); |
| 64 | exe.linkSystemLibraryName("a"); | 73 | exe.linkSystemLibraryName("a"); |
| 65 | exe.linkLibC(); | 74 | exe.linkLibC(); |
test/link/macho/stack_size/build.zig+7-6| ... | @@ -1,16 +1,17 @@ | ... | @@ -1,16 +1,17 @@ |
| 1 | const std = @import("std"); | 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 | const mode = b.standardReleaseOptions(); | 4 | const optimize = b.standardOptimizeOption(.{}); |
| 6 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; | 5 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; |
| 7 | 6 | ||
| 8 | const test_step = b.step("test", "Test"); | 7 | const test_step = b.step("test", "Test"); |
| 9 | test_step.dependOn(b.getInstallStep()); | 8 | test_step.dependOn(b.getInstallStep()); |
| 10 | 9 | ||
| 11 | const exe = b.addExecutable("main", null); | 10 | const exe = b.addExecutable(.{ |
| 12 | exe.setTarget(target); | 11 | .name = "main", |
| 13 | exe.setBuildMode(mode); | 12 | .optimize = optimize, |
| 13 | .target = target, | ||
| 14 | }); | ||
| 14 | exe.addCSourceFile("main.c", &.{}); | 15 | exe.addCSourceFile("main.c", &.{}); |
| 15 | exe.linkLibC(); | 16 | exe.linkLibC(); |
| 16 | exe.stack_size = 0x100000000; | 17 | exe.stack_size = 0x100000000; |
test/link/macho/strict_validation/build.zig+8-7| ... | @@ -1,18 +1,19 @@ | ... | @@ -1,18 +1,19 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | 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 { | 4 | pub fn build(b: *std.Build) void { |
| 7 | const mode = b.standardReleaseOptions(); | 5 | const optimize = b.standardOptimizeOption(.{}); |
| 8 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; | 6 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; |
| 9 | 7 | ||
| 10 | const test_step = b.step("test", "Test"); | 8 | const test_step = b.step("test", "Test"); |
| 11 | test_step.dependOn(b.getInstallStep()); | 9 | test_step.dependOn(b.getInstallStep()); |
| 12 | 10 | ||
| 13 | const exe = b.addExecutable("main", "main.zig"); | 11 | const exe = b.addExecutable(.{ |
| 14 | exe.setBuildMode(mode); | 12 | .name = "main", |
| 15 | exe.setTarget(target); | 13 | .root_source_file = .{ .path = "main.zig" }, |
| 14 | .optimize = optimize, | ||
| 15 | .target = target, | ||
| 16 | }); | ||
| 16 | exe.linkLibC(); | 17 | exe.linkLibC(); |
| 17 | 18 | ||
| 18 | const check_exe = exe.checkObject(.macho); | 19 | const check_exe = exe.checkObject(.macho); |
test/link/macho/tls/build.zig+13-9| ... | @@ -1,19 +1,23 @@ | ... | @@ -1,19 +1,23 @@ |
| 1 | const std = @import("std"); | 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 | const mode = b.standardReleaseOptions(); | 4 | const optimize = b.standardOptimizeOption(.{}); |
| 6 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; | 5 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; |
| 7 | 6 | ||
| 8 | const lib = b.addSharedLibrary("a", null, b.version(1, 0, 0)); | 7 | const lib = b.addSharedLibrary(.{ |
| 9 | lib.setBuildMode(mode); | 8 | .name = "a", |
| 10 | lib.setTarget(target); | 9 | .version = .{ .major = 1, .minor = 0 }, |
| 10 | .optimize = optimize, | ||
| 11 | .target = target, | ||
| 12 | }); | ||
| 11 | lib.addCSourceFile("a.c", &.{}); | 13 | lib.addCSourceFile("a.c", &.{}); |
| 12 | lib.linkLibC(); | 14 | lib.linkLibC(); |
| 13 | 15 | ||
| 14 | const test_exe = b.addTest("main.zig"); | 16 | const test_exe = b.addTest(.{ |
| 15 | test_exe.setBuildMode(mode); | 17 | .root_source_file = .{ .path = "main.zig" }, |
| 16 | test_exe.setTarget(target); | 18 | .optimize = optimize, |
| 19 | .target = target, | ||
| 20 | }); | ||
| 17 | test_exe.linkLibrary(lib); | 21 | test_exe.linkLibrary(lib); |
| 18 | test_exe.linkLibC(); | 22 | test_exe.linkLibC(); |
| 19 | 23 |
test/link/macho/unwind_info/build.zig+18-14| ... | @@ -1,26 +1,24 @@ | ... | @@ -1,26 +1,24 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | 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 { | 4 | pub fn build(b: *std.Build) void { |
| 7 | const mode = b.standardReleaseOptions(); | 5 | const optimize = b.standardOptimizeOption(.{}); |
| 8 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; | 6 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; |
| 9 | 7 | ||
| 10 | const test_step = b.step("test", "Test the program"); | 8 | const test_step = b.step("test", "Test the program"); |
| 11 | 9 | ||
| 12 | testUnwindInfo(b, test_step, mode, target, false); | 10 | testUnwindInfo(b, test_step, optimize, target, false); |
| 13 | testUnwindInfo(b, test_step, mode, target, true); | 11 | testUnwindInfo(b, test_step, optimize, target, true); |
| 14 | } | 12 | } |
| 15 | 13 | ||
| 16 | fn testUnwindInfo( | 14 | fn testUnwindInfo( |
| 17 | b: *Builder, | 15 | b: *std.Build, |
| 18 | test_step: *std.build.Step, | 16 | test_step: *std.Build.Step, |
| 19 | mode: std.builtin.Mode, | 17 | optimize: std.builtin.OptimizeMode, |
| 20 | target: std.zig.CrossTarget, | 18 | target: std.zig.CrossTarget, |
| 21 | dead_strip: bool, | 19 | dead_strip: bool, |
| 22 | ) void { | 20 | ) void { |
| 23 | const exe = createScenario(b, mode, target); | 21 | const exe = createScenario(b, optimize, target); |
| 24 | exe.link_gc_sections = dead_strip; | 22 | exe.link_gc_sections = dead_strip; |
| 25 | 23 | ||
| 26 | const check = exe.checkObject(.macho); | 24 | const check = exe.checkObject(.macho); |
| ... | @@ -52,8 +50,16 @@ fn testUnwindInfo( | ... | @@ -52,8 +50,16 @@ fn testUnwindInfo( |
| 52 | test_step.dependOn(&run_cmd.step); | 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 { | 53 | fn createScenario( |
| 56 | const exe = b.addExecutable("test", null); | 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 | b.default_step.dependOn(&exe.step); | 63 | b.default_step.dependOn(&exe.step); |
| 58 | exe.addIncludePath("."); | 64 | exe.addIncludePath("."); |
| 59 | exe.addCSourceFiles(&[_][]const u8{ | 65 | exe.addCSourceFiles(&[_][]const u8{ |
| ... | @@ -61,8 +67,6 @@ fn createScenario(b: *Builder, mode: std.builtin.Mode, target: std.zig.CrossTarg | ... | @@ -61,8 +67,6 @@ fn createScenario(b: *Builder, mode: std.builtin.Mode, target: std.zig.CrossTarg |
| 61 | "simple_string.cpp", | 67 | "simple_string.cpp", |
| 62 | "simple_string_owner.cpp", | 68 | "simple_string_owner.cpp", |
| 63 | }, &[0][]const u8{}); | 69 | }, &[0][]const u8{}); |
| 64 | exe.setBuildMode(mode); | ||
| 65 | exe.setTarget(target); | ||
| 66 | exe.linkLibCpp(); | 70 | exe.linkLibCpp(); |
| 67 | return exe; | 71 | return exe; |
| 68 | } | 72 | } |
test/link/macho/uuid/build.zig+17-12| ... | @@ -1,8 +1,6 @@ | ... | @@ -1,8 +1,6 @@ |
| 1 | const std = @import("std"); | 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 | const test_step = b.step("test", "Test"); | 4 | const test_step = b.step("test", "Test"); |
| 7 | test_step.dependOn(b.getInstallStep()); | 5 | test_step.dependOn(b.getInstallStep()); |
| 8 | 6 | ||
| ... | @@ -27,23 +25,23 @@ pub fn build(b: *Builder) void { | ... | @@ -27,23 +25,23 @@ pub fn build(b: *Builder) void { |
| 27 | } | 25 | } |
| 28 | 26 | ||
| 29 | fn testUuid( | 27 | fn testUuid( |
| 30 | b: *Builder, | 28 | b: *std.Build, |
| 31 | test_step: *std.build.Step, | 29 | test_step: *std.Build.Step, |
| 32 | mode: std.builtin.Mode, | 30 | optimize: std.builtin.OptimizeMode, |
| 33 | target: std.zig.CrossTarget, | 31 | target: std.zig.CrossTarget, |
| 34 | comptime exp: []const u8, | 32 | comptime exp: []const u8, |
| 35 | ) void { | 33 | ) void { |
| 36 | // The calculated UUID value is independent of debug info and so it should | 34 | // The calculated UUID value is independent of debug info and so it should |
| 37 | // stay the same across builds. | 35 | // stay the same across builds. |
| 38 | { | 36 | { |
| 39 | const dylib = simpleDylib(b, mode, target); | 37 | const dylib = simpleDylib(b, optimize, target); |
| 40 | const check_dylib = dylib.checkObject(.macho); | 38 | const check_dylib = dylib.checkObject(.macho); |
| 41 | check_dylib.checkStart("cmd UUID"); | 39 | check_dylib.checkStart("cmd UUID"); |
| 42 | check_dylib.checkNext("uuid " ++ exp); | 40 | check_dylib.checkNext("uuid " ++ exp); |
| 43 | test_step.dependOn(&check_dylib.step); | 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 | dylib.strip = true; | 45 | dylib.strip = true; |
| 48 | const check_dylib = dylib.checkObject(.macho); | 46 | const check_dylib = dylib.checkObject(.macho); |
| 49 | check_dylib.checkStart("cmd UUID"); | 47 | check_dylib.checkStart("cmd UUID"); |
| ... | @@ -52,10 +50,17 @@ fn testUuid( | ... | @@ -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 { | 53 | fn simpleDylib( |
| 56 | const dylib = b.addSharedLibrary("test", null, b.version(1, 0, 0)); | 54 | b: *std.Build, |
| 57 | dylib.setTarget(target); | 55 | optimize: std.builtin.OptimizeMode, |
| 58 | dylib.setBuildMode(mode); | 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 | dylib.addCSourceFile("test.c", &.{}); | 64 | dylib.addCSourceFile("test.c", &.{}); |
| 60 | dylib.linkLibC(); | 65 | dylib.linkLibC(); |
| 61 | return dylib; | 66 | return dylib; |
test/link/macho/weak_framework/build.zig+6-6| ... | @@ -1,16 +1,16 @@ | ... | @@ -1,16 +1,16 @@ |
| 1 | const std = @import("std"); | 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 | const mode = b.standardReleaseOptions(); | 4 | const optimize = b.standardOptimizeOption(.{}); |
| 7 | 5 | ||
| 8 | const test_step = b.step("test", "Test the program"); | 6 | const test_step = b.step("test", "Test the program"); |
| 9 | test_step.dependOn(b.getInstallStep()); | 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 | exe.addCSourceFile("main.c", &[0][]const u8{}); | 13 | exe.addCSourceFile("main.c", &[0][]const u8{}); |
| 13 | exe.setBuildMode(mode); | ||
| 14 | exe.linkLibC(); | 14 | exe.linkLibC(); |
| 15 | exe.linkFrameworkWeak("Cocoa"); | 15 | exe.linkFrameworkWeak("Cocoa"); |
| 16 | 16 |
test/link/macho/weak_library/build.zig+13-10| ... | @@ -1,25 +1,28 @@ | ... | @@ -1,25 +1,28 @@ |
| 1 | const std = @import("std"); | 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 | const mode = b.standardReleaseOptions(); | 4 | const optimize = b.standardOptimizeOption(.{}); |
| 7 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; | 5 | const target: std.zig.CrossTarget = .{ .os_tag = .macos }; |
| 8 | 6 | ||
| 9 | const test_step = b.step("test", "Test the program"); | 7 | const test_step = b.step("test", "Test the program"); |
| 10 | test_step.dependOn(b.getInstallStep()); | 8 | test_step.dependOn(b.getInstallStep()); |
| 11 | 9 | ||
| 12 | const dylib = b.addSharedLibrary("a", null, b.version(1, 0, 0)); | 10 | const dylib = b.addSharedLibrary(.{ |
| 13 | dylib.setTarget(target); | 11 | .name = "a", |
| 14 | dylib.setBuildMode(mode); | 12 | .version = .{ .major = 1, .minor = 0, .patch = 0 }, |
| 13 | .target = target, | ||
| 14 | .optimize = optimize, | ||
| 15 | }); | ||
| 15 | dylib.addCSourceFile("a.c", &.{}); | 16 | dylib.addCSourceFile("a.c", &.{}); |
| 16 | dylib.linkLibC(); | 17 | dylib.linkLibC(); |
| 17 | dylib.install(); | 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 | exe.addCSourceFile("main.c", &[0][]const u8{}); | 25 | exe.addCSourceFile("main.c", &[0][]const u8{}); |
| 21 | exe.setTarget(target); | ||
| 22 | exe.setBuildMode(mode); | ||
| 23 | exe.linkLibC(); | 26 | exe.linkLibC(); |
| 24 | exe.linkSystemLibraryWeak("a"); | 27 | exe.linkSystemLibraryWeak("a"); |
| 25 | exe.addLibraryPath(b.pathFromRoot("zig-out/lib")); | 28 | exe.addLibraryPath(b.pathFromRoot("zig-out/lib")); |
test/link/static_lib_as_system_lib/build.zig+13-7| ... | @@ -1,17 +1,23 @@ | ... | @@ -1,17 +1,23 @@ |
| 1 | const std = @import("std"); | 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 | const mode = b.standardReleaseOptions(); | 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 | lib_a.addCSourceFile("a.c", &[_][]const u8{}); | 12 | lib_a.addCSourceFile("a.c", &[_][]const u8{}); |
| 9 | lib_a.setBuildMode(mode); | ||
| 10 | lib_a.addIncludePath("."); | 13 | lib_a.addIncludePath("."); |
| 11 | lib_a.install(); | 14 | lib_a.install(); |
| 12 | 15 | ||
| 13 | const test_exe = b.addTest("main.zig"); | 16 | const test_exe = b.addTest(.{ |
| 14 | test_exe.setBuildMode(mode); | 17 | .root_source_file = .{ .path = "main.zig" }, |
| 18 | .optimize = optimize, | ||
| 19 | .target = target, | ||
| 20 | }); | ||
| 15 | test_exe.linkSystemLibrary("a"); // force linking liba.a as -la | 21 | test_exe.linkSystemLibrary("a"); // force linking liba.a as -la |
| 16 | test_exe.addSystemIncludePath("."); | 22 | test_exe.addSystemIncludePath("."); |
| 17 | const search_path = std.fs.path.join(b.allocator, &[_][]const u8{ b.install_path, "lib" }) catch unreachable; | 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,17 +1,17 @@ |
| 1 | const std = @import("std"); | 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 | const test_step = b.step("test", "Test"); | 4 | const test_step = b.step("test", "Test"); |
| 8 | test_step.dependOn(b.getInstallStep()); | 5 | test_step.dependOn(b.getInstallStep()); |
| 9 | 6 | ||
| 10 | // The code in question will pull-in compiler-rt, | 7 | // The code in question will pull-in compiler-rt, |
| 11 | // and therefore link with its archive file. | 8 | // and therefore link with its archive file. |
| 12 | const lib = b.addSharedLibrary("main", "main.zig", .unversioned); | 9 | const lib = b.addSharedLibrary(.{ |
| 13 | lib.setBuildMode(mode); | 10 | .name = "main", |
| 14 | lib.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }); | 11 | .root_source_file = .{ .path = "main.zig" }, |
| 12 | .optimize = b.standardOptimizeOption(.{}), | ||
| 13 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, | ||
| 14 | }); | ||
| 15 | lib.use_llvm = false; | 15 | lib.use_llvm = false; |
| 16 | lib.use_lld = false; | 16 | lib.use_lld = false; |
| 17 | lib.strip = false; | 17 | lib.strip = false; |
test/link/wasm/basic-features/build.zig+12-8| ... | @@ -1,14 +1,18 @@ | ... | @@ -1,14 +1,18 @@ |
| 1 | const std = @import("std"); | 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 | const mode = b.standardReleaseOptions(); | ||
| 5 | |||
| 6 | // Library with explicitly set cpu features | 4 | // Library with explicitly set cpu features |
| 7 | const lib = b.addSharedLibrary("lib", "main.zig", .unversioned); | 5 | const lib = b.addSharedLibrary(.{ |
| 8 | lib.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }); | 6 | .name = "lib", |
| 9 | lib.target.cpu_model = .{ .explicit = &std.Target.wasm.cpu.mvp }; | 7 | .root_source_file = .{ .path = "main.zig" }, |
| 10 | lib.target.cpu_features_add.addFeature(0); // index 0 == atomics (see std.Target.wasm.Features) | 8 | .optimize = b.standardOptimizeOption(.{}), |
| 11 | lib.setBuildMode(mode); | 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 | lib.use_llvm = false; | 16 | lib.use_llvm = false; |
| 13 | lib.use_lld = false; | 17 | lib.use_lld = false; |
| 14 | 18 |
test/link/wasm/bss/build.zig+7-7| ... | @@ -1,15 +1,15 @@ | ... | @@ -1,15 +1,15 @@ |
| 1 | const std = @import("std"); | 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 | const test_step = b.step("test", "Test"); | 4 | const test_step = b.step("test", "Test"); |
| 8 | test_step.dependOn(b.getInstallStep()); | 5 | test_step.dependOn(b.getInstallStep()); |
| 9 | 6 | ||
| 10 | const lib = b.addSharedLibrary("lib", "lib.zig", .unversioned); | 7 | const lib = b.addSharedLibrary(.{ |
| 11 | lib.setBuildMode(mode); | 8 | .name = "lib", |
| 12 | lib.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }); | 9 | .root_source_file = .{ .path = "lib.zig" }, |
| 10 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, | ||
| 11 | .optimize = b.standardOptimizeOption(.{}), | ||
| 12 | }); | ||
| 13 | lib.use_llvm = false; | 13 | lib.use_llvm = false; |
| 14 | lib.use_lld = false; | 14 | lib.use_lld = false; |
| 15 | lib.strip = false; | 15 | lib.strip = false; |
test/link/wasm/export-data/build.zig+9-7| ... | @@ -1,13 +1,15 @@ | ... | @@ -1,13 +1,15 @@ |
| 1 | const std = @import("std"); | 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 | const test_step = b.step("test", "Test"); | 4 | const test_step = b.step("test", "Test"); |
| 6 | test_step.dependOn(b.getInstallStep()); | 5 | test_step.dependOn(b.getInstallStep()); |
| 7 | 6 | ||
| 8 | const lib = b.addSharedLibrary("lib", "lib.zig", .unversioned); | 7 | const lib = b.addSharedLibrary(.{ |
| 9 | lib.setBuildMode(.ReleaseSafe); // to make the output deterministic in address positions | 8 | .name = "lib", |
| 10 | lib.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }); | 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 | lib.use_lld = false; | 13 | lib.use_lld = false; |
| 12 | lib.export_symbol_names = &.{ "foo", "bar" }; | 14 | lib.export_symbol_names = &.{ "foo", "bar" }; |
| 13 | lib.global_base = 0; // put data section at address 0 to make data symbols easier to parse | 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,8 +25,8 @@ pub fn build(b: *Builder) void { |
| 23 | check_lib.checkNext("type i32"); | 25 | check_lib.checkNext("type i32"); |
| 24 | check_lib.checkNext("mutable false"); | 26 | check_lib.checkNext("mutable false"); |
| 25 | check_lib.checkNext("i32.const {bar_address}"); | 27 | check_lib.checkNext("i32.const {bar_address}"); |
| 26 | check_lib.checkComputeCompare("foo_address", .{ .op = .eq, .value = .{ .literal = 0 } }); | 28 | check_lib.checkComputeCompare("foo_address", .{ .op = .eq, .value = .{ .literal = 4 } }); |
| 27 | check_lib.checkComputeCompare("bar_address", .{ .op = .eq, .value = .{ .literal = 4 } }); | 29 | check_lib.checkComputeCompare("bar_address", .{ .op = .eq, .value = .{ .literal = 0 } }); |
| 28 | 30 | ||
| 29 | check_lib.checkStart("Section export"); | 31 | check_lib.checkStart("Section export"); |
| 30 | check_lib.checkNext("entries 3"); | 32 | check_lib.checkNext("entries 3"); |
test/link/wasm/export/build.zig+21-12| ... | @@ -1,24 +1,33 @@ | ... | @@ -1,24 +1,33 @@ |
| 1 | const std = @import("std"); | 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 | const mode = b.standardReleaseOptions(); | 4 | const optimize = b.standardOptimizeOption(.{}); |
| 5 | 5 | ||
| 6 | const no_export = b.addSharedLibrary("no-export", "main.zig", .unversioned); | 6 | const no_export = b.addSharedLibrary(.{ |
| 7 | no_export.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }); | 7 | .name = "no-export", |
| 8 | no_export.setBuildMode(mode); | 8 | .root_source_file = .{ .path = "main.zig" }, |
| 9 | .optimize = optimize, | ||
| 10 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, | ||
| 11 | }); | ||
| 9 | no_export.use_llvm = false; | 12 | no_export.use_llvm = false; |
| 10 | no_export.use_lld = false; | 13 | no_export.use_lld = false; |
| 11 | 14 | ||
| 12 | const dynamic_export = b.addSharedLibrary("dynamic", "main.zig", .unversioned); | 15 | const dynamic_export = b.addSharedLibrary(.{ |
| 13 | dynamic_export.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }); | 16 | .name = "dynamic", |
| 14 | dynamic_export.setBuildMode(mode); | 17 | .root_source_file = .{ .path = "main.zig" }, |
| 18 | .optimize = optimize, | ||
| 19 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, | ||
| 20 | }); | ||
| 15 | dynamic_export.rdynamic = true; | 21 | dynamic_export.rdynamic = true; |
| 16 | dynamic_export.use_llvm = false; | 22 | dynamic_export.use_llvm = false; |
| 17 | dynamic_export.use_lld = false; | 23 | dynamic_export.use_lld = false; |
| 18 | 24 | ||
| 19 | const force_export = b.addSharedLibrary("force", "main.zig", .unversioned); | 25 | const force_export = b.addSharedLibrary(.{ |
| 20 | force_export.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }); | 26 | .name = "force", |
| 21 | force_export.setBuildMode(mode); | 27 | .root_source_file = .{ .path = "main.zig" }, |
| 28 | .optimize = optimize, | ||
| 29 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, | ||
| 30 | }); | ||
| 22 | force_export.export_symbol_names = &.{"foo"}; | 31 | force_export.export_symbol_names = &.{"foo"}; |
| 23 | force_export.use_llvm = false; | 32 | force_export.use_llvm = false; |
| 24 | force_export.use_lld = false; | 33 | force_export.use_lld = false; |
test/link/wasm/extern-mangle/build.zig+7-7| ... | @@ -1,15 +1,15 @@ | ... | @@ -1,15 +1,15 @@ |
| 1 | const std = @import("std"); | 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 | const test_step = b.step("test", "Test"); | 4 | const test_step = b.step("test", "Test"); |
| 8 | test_step.dependOn(b.getInstallStep()); | 5 | test_step.dependOn(b.getInstallStep()); |
| 9 | 6 | ||
| 10 | const lib = b.addSharedLibrary("lib", "lib.zig", .unversioned); | 7 | const lib = b.addSharedLibrary(.{ |
| 11 | lib.setBuildMode(mode); | 8 | .name = "lib", |
| 12 | lib.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }); | 9 | .root_source_file = .{ .path = "lib.zig" }, |
| 10 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, | ||
| 11 | .optimize = b.standardOptimizeOption(.{}), | ||
| 12 | }); | ||
| 13 | lib.import_symbols = true; // import `a` and `b` | 13 | lib.import_symbols = true; // import `a` and `b` |
| 14 | lib.rdynamic = true; // export `foo` | 14 | lib.rdynamic = true; // export `foo` |
| 15 | lib.install(); | 15 | lib.install(); |
test/link/wasm/extern/build.zig+7-5| ... | @@ -1,10 +1,12 @@ | ... | @@ -1,10 +1,12 @@ |
| 1 | const std = @import("std"); | 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 | const mode = b.standardReleaseOptions(); | 4 | const exe = b.addExecutable(.{ |
| 5 | const exe = b.addExecutable("extern", "main.zig"); | 5 | .name = "extern", |
| 6 | exe.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .wasi }); | 6 | .root_source_file = .{ .path = "main.zig" }, |
| 7 | exe.setBuildMode(mode); | 7 | .optimize = b.standardOptimizeOption(.{}), |
| 8 | .target = .{ .cpu_arch = .wasm32, .os_tag = .wasi }, | ||
| 9 | }); | ||
| 8 | exe.addCSourceFile("foo.c", &.{}); | 10 | exe.addCSourceFile("foo.c", &.{}); |
| 9 | exe.use_llvm = false; | 11 | exe.use_llvm = false; |
| 10 | exe.use_lld = false; | 12 | exe.use_lld = false; |
test/link/wasm/function-table/build.zig+20-12| ... | @@ -1,29 +1,37 @@ | ... | @@ -1,29 +1,37 @@ |
| 1 | const std = @import("std"); | 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 | const mode = b.standardReleaseOptions(); | 4 | const optimize = b.standardOptimizeOption(.{}); |
| 6 | 5 | ||
| 7 | const test_step = b.step("test", "Test"); | 6 | const test_step = b.step("test", "Test"); |
| 8 | test_step.dependOn(b.getInstallStep()); | 7 | test_step.dependOn(b.getInstallStep()); |
| 9 | 8 | ||
| 10 | const import_table = b.addSharedLibrary("lib", "lib.zig", .unversioned); | 9 | const import_table = b.addSharedLibrary(.{ |
| 11 | import_table.setBuildMode(mode); | 10 | .name = "lib", |
| 12 | import_table.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }); | 11 | .root_source_file = .{ .path = "lib.zig" }, |
| 12 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, | ||
| 13 | .optimize = optimize, | ||
| 14 | }); | ||
| 13 | import_table.use_llvm = false; | 15 | import_table.use_llvm = false; |
| 14 | import_table.use_lld = false; | 16 | import_table.use_lld = false; |
| 15 | import_table.import_table = true; | 17 | import_table.import_table = true; |
| 16 | 18 | ||
| 17 | const export_table = b.addSharedLibrary("lib", "lib.zig", .unversioned); | 19 | const export_table = b.addSharedLibrary(.{ |
| 18 | export_table.setBuildMode(mode); | 20 | .name = "lib", |
| 19 | export_table.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }); | 21 | .root_source_file = .{ .path = "lib.zig" }, |
| 22 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, | ||
| 23 | .optimize = optimize, | ||
| 24 | }); | ||
| 20 | export_table.use_llvm = false; | 25 | export_table.use_llvm = false; |
| 21 | export_table.use_lld = false; | 26 | export_table.use_lld = false; |
| 22 | export_table.export_table = true; | 27 | export_table.export_table = true; |
| 23 | 28 | ||
| 24 | const regular_table = b.addSharedLibrary("lib", "lib.zig", .unversioned); | 29 | const regular_table = b.addSharedLibrary(.{ |
| 25 | regular_table.setBuildMode(mode); | 30 | .name = "lib", |
| 26 | regular_table.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }); | 31 | .root_source_file = .{ .path = "lib.zig" }, |
| 32 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, | ||
| 33 | .optimize = optimize, | ||
| 34 | }); | ||
| 27 | regular_table.use_llvm = false; | 35 | regular_table.use_llvm = false; |
| 28 | regular_table.use_lld = false; | 36 | regular_table.use_lld = false; |
| 29 | 37 |
test/link/wasm/infer-features/build.zig+21-10| ... | @@ -1,21 +1,32 @@ | ... | @@ -1,21 +1,32 @@ |
| 1 | const std = @import("std"); | 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 | const mode = b.standardReleaseOptions(); | 4 | const optimize = b.standardOptimizeOption(.{}); |
| 5 | 5 | ||
| 6 | // Wasm Object file which we will use to infer the features from | 6 | // Wasm Object file which we will use to infer the features from |
| 7 | const c_obj = b.addObject("c_obj", null); | 7 | const c_obj = b.addObject(.{ |
| 8 | c_obj.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }); | 8 | .name = "c_obj", |
| 9 | c_obj.target.cpu_model = .{ .explicit = &std.Target.wasm.cpu.bleeding_edge }; | 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 | c_obj.addCSourceFile("foo.c", &.{}); | 16 | c_obj.addCSourceFile("foo.c", &.{}); |
| 11 | c_obj.setBuildMode(mode); | ||
| 12 | 17 | ||
| 13 | // Wasm library that doesn't have any features specified. This will | 18 | // Wasm library that doesn't have any features specified. This will |
| 14 | // infer its featureset from other linked object files. | 19 | // infer its featureset from other linked object files. |
| 15 | const lib = b.addSharedLibrary("lib", "main.zig", .unversioned); | 20 | const lib = b.addSharedLibrary(.{ |
| 16 | lib.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }); | 21 | .name = "lib", |
| 17 | lib.target.cpu_model = .{ .explicit = &std.Target.wasm.cpu.mvp }; | 22 | .root_source_file = .{ .path = "main.zig" }, |
| 18 | lib.setBuildMode(mode); | 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 | lib.use_llvm = false; | 30 | lib.use_llvm = false; |
| 20 | lib.use_lld = false; | 31 | lib.use_lld = false; |
| 21 | lib.addObject(c_obj); | 32 | lib.addObject(c_obj); |
test/link/wasm/producers/build.zig+7-7| ... | @@ -1,16 +1,16 @@ | ... | @@ -1,16 +1,16 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | 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 | const test_step = b.step("test", "Test"); | 5 | const test_step = b.step("test", "Test"); |
| 9 | test_step.dependOn(b.getInstallStep()); | 6 | test_step.dependOn(b.getInstallStep()); |
| 10 | 7 | ||
| 11 | const lib = b.addSharedLibrary("lib", "lib.zig", .unversioned); | 8 | const lib = b.addSharedLibrary(.{ |
| 12 | lib.setBuildMode(mode); | 9 | .name = "lib", |
| 13 | lib.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }); | 10 | .root_source_file = .{ .path = "lib.zig" }, |
| 11 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, | ||
| 12 | .optimize = b.standardOptimizeOption(.{}), | ||
| 13 | }); | ||
| 14 | lib.use_llvm = false; | 14 | lib.use_llvm = false; |
| 15 | lib.use_lld = false; | 15 | lib.use_lld = false; |
| 16 | lib.strip = false; | 16 | lib.strip = false; |
test/link/wasm/segments/build.zig+7-7| ... | @@ -1,15 +1,15 @@ | ... | @@ -1,15 +1,15 @@ |
| 1 | const std = @import("std"); | 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 | const test_step = b.step("test", "Test"); | 4 | const test_step = b.step("test", "Test"); |
| 8 | test_step.dependOn(b.getInstallStep()); | 5 | test_step.dependOn(b.getInstallStep()); |
| 9 | 6 | ||
| 10 | const lib = b.addSharedLibrary("lib", "lib.zig", .unversioned); | 7 | const lib = b.addSharedLibrary(.{ |
| 11 | lib.setBuildMode(mode); | 8 | .name = "lib", |
| 12 | lib.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }); | 9 | .root_source_file = .{ .path = "lib.zig" }, |
| 10 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, | ||
| 11 | .optimize = b.standardOptimizeOption(.{}), | ||
| 12 | }); | ||
| 13 | lib.use_llvm = false; | 13 | lib.use_llvm = false; |
| 14 | lib.use_lld = false; | 14 | lib.use_lld = false; |
| 15 | lib.strip = false; | 15 | lib.strip = false; |
test/link/wasm/stack_pointer/build.zig+7-7| ... | @@ -1,15 +1,15 @@ | ... | @@ -1,15 +1,15 @@ |
| 1 | const std = @import("std"); | 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 | const test_step = b.step("test", "Test"); | 4 | const test_step = b.step("test", "Test"); |
| 8 | test_step.dependOn(b.getInstallStep()); | 5 | test_step.dependOn(b.getInstallStep()); |
| 9 | 6 | ||
| 10 | const lib = b.addSharedLibrary("lib", "lib.zig", .unversioned); | 7 | const lib = b.addSharedLibrary(.{ |
| 11 | lib.setBuildMode(mode); | 8 | .name = "lib", |
| 12 | lib.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }); | 9 | .root_source_file = .{ .path = "lib.zig" }, |
| 10 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, | ||
| 11 | .optimize = b.standardOptimizeOption(.{}), | ||
| 12 | }); | ||
| 13 | lib.use_llvm = false; | 13 | lib.use_llvm = false; |
| 14 | lib.use_lld = false; | 14 | lib.use_lld = false; |
| 15 | lib.strip = false; | 15 | lib.strip = false; |
test/link/wasm/type/build.zig+7-7| ... | @@ -1,15 +1,15 @@ | ... | @@ -1,15 +1,15 @@ |
| 1 | const std = @import("std"); | 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 | const test_step = b.step("test", "Test"); | 4 | const test_step = b.step("test", "Test"); |
| 8 | test_step.dependOn(b.getInstallStep()); | 5 | test_step.dependOn(b.getInstallStep()); |
| 9 | 6 | ||
| 10 | const lib = b.addSharedLibrary("lib", "lib.zig", .unversioned); | 7 | const lib = b.addSharedLibrary(.{ |
| 11 | lib.setBuildMode(mode); | 8 | .name = "lib", |
| 12 | lib.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }); | 9 | .root_source_file = .{ .path = "lib.zig" }, |
| 10 | .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding }, | ||
| 11 | .optimize = b.standardOptimizeOption(.{}), | ||
| 12 | }); | ||
| 13 | lib.use_llvm = false; | 13 | lib.use_llvm = false; |
| 14 | lib.use_lld = false; | 14 | lib.use_lld = false; |
| 15 | lib.strip = false; | 15 | lib.strip = false; |
test/src/compare_output.zig+25-11| ... | @@ -1,19 +1,18 @@ | ... | @@ -1,19 +1,18 @@ |
| 1 | // This is the implementation of the test harness. | 1 | // This is the implementation of the test harness. |
| 2 | // For the actual test cases, see test/compare_output.zig. | 2 | // For the actual test cases, see test/compare_output.zig. |
| 3 | const std = @import("std"); | 3 | const std = @import("std"); |
| 4 | const build = std.build; | ||
| 5 | const ArrayList = std.ArrayList; | 4 | const ArrayList = std.ArrayList; |
| 6 | const fmt = std.fmt; | 5 | const fmt = std.fmt; |
| 7 | const mem = std.mem; | 6 | const mem = std.mem; |
| 8 | const fs = std.fs; | 7 | const fs = std.fs; |
| 9 | const Mode = std.builtin.Mode; | 8 | const OptimizeMode = std.builtin.OptimizeMode; |
| 10 | 9 | ||
| 11 | pub const CompareOutputContext = struct { | 10 | pub const CompareOutputContext = struct { |
| 12 | b: *build.Builder, | 11 | b: *std.Build, |
| 13 | step: *build.Step, | 12 | step: *std.Build.Step, |
| 14 | test_index: usize, | 13 | test_index: usize, |
| 15 | test_filter: ?[]const u8, | 14 | test_filter: ?[]const u8, |
| 16 | modes: []const Mode, | 15 | optimize_modes: []const OptimizeMode, |
| 17 | 16 | ||
| 18 | const Special = enum { | 17 | const Special = enum { |
| 19 | None, | 18 | None, |
| ... | @@ -102,7 +101,11 @@ pub const CompareOutputContext = struct { | ... | @@ -102,7 +101,11 @@ pub const CompareOutputContext = struct { |
| 102 | if (mem.indexOf(u8, annotated_case_name, filter) == null) return; | 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 | exe.addAssemblyFileSource(write_src.getFileSource(case.sources.items[0].filename).?); | 109 | exe.addAssemblyFileSource(write_src.getFileSource(case.sources.items[0].filename).?); |
| 107 | 110 | ||
| 108 | const run = exe.run(); | 111 | const run = exe.run(); |
| ... | @@ -113,19 +116,23 @@ pub const CompareOutputContext = struct { | ... | @@ -113,19 +116,23 @@ pub const CompareOutputContext = struct { |
| 113 | self.step.dependOn(&run.step); | 116 | self.step.dependOn(&run.step); |
| 114 | }, | 117 | }, |
| 115 | Special.None => { | 118 | Special.None => { |
| 116 | for (self.modes) |mode| { | 119 | for (self.optimize_modes) |optimize| { |
| 117 | const annotated_case_name = fmt.allocPrint(self.b.allocator, "{s} {s} ({s})", .{ | 120 | const annotated_case_name = fmt.allocPrint(self.b.allocator, "{s} {s} ({s})", .{ |
| 118 | "compare-output", | 121 | "compare-output", |
| 119 | case.name, | 122 | case.name, |
| 120 | @tagName(mode), | 123 | @tagName(optimize), |
| 121 | }) catch unreachable; | 124 | }) catch unreachable; |
| 122 | if (self.test_filter) |filter| { | 125 | if (self.test_filter) |filter| { |
| 123 | if (mem.indexOf(u8, annotated_case_name, filter) == null) continue; | 126 | if (mem.indexOf(u8, annotated_case_name, filter) == null) continue; |
| 124 | } | 127 | } |
| 125 | 128 | ||
| 126 | const basename = case.sources.items[0].filename; | 129 | const basename = case.sources.items[0].filename; |
| 127 | const exe = b.addExecutableSource("test", write_src.getFileSource(basename).?); | 130 | const exe = b.addExecutable(.{ |
| 128 | exe.setBuildMode(mode); | 131 | .name = "test", |
| 132 | .root_source_file = write_src.getFileSource(basename).?, | ||
| 133 | .optimize = optimize, | ||
| 134 | .target = .{}, | ||
| 135 | }); | ||
| 129 | if (case.link_libc) { | 136 | if (case.link_libc) { |
| 130 | exe.linkSystemLibrary("c"); | 137 | exe.linkSystemLibrary("c"); |
| 131 | } | 138 | } |
| ... | @@ -139,13 +146,20 @@ pub const CompareOutputContext = struct { | ... | @@ -139,13 +146,20 @@ pub const CompareOutputContext = struct { |
| 139 | } | 146 | } |
| 140 | }, | 147 | }, |
| 141 | Special.RuntimeSafety => { | 148 | Special.RuntimeSafety => { |
| 149 | // TODO iterate over self.optimize_modes and test this in both | ||
| 150 | // debug and release safe mode | ||
| 142 | const annotated_case_name = fmt.allocPrint(self.b.allocator, "safety {s}", .{case.name}) catch unreachable; | 151 | const annotated_case_name = fmt.allocPrint(self.b.allocator, "safety {s}", .{case.name}) catch unreachable; |
| 143 | if (self.test_filter) |filter| { | 152 | if (self.test_filter) |filter| { |
| 144 | if (mem.indexOf(u8, annotated_case_name, filter) == null) return; | 153 | if (mem.indexOf(u8, annotated_case_name, filter) == null) return; |
| 145 | } | 154 | } |
| 146 | 155 | ||
| 147 | const basename = case.sources.items[0].filename; | 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 | if (case.link_libc) { | 163 | if (case.link_libc) { |
| 150 | exe.linkSystemLibrary("c"); | 164 | exe.linkSystemLibrary("c"); |
| 151 | } | 165 | } |
test/src/run_translated_c.zig+8-6| ... | @@ -1,15 +1,14 @@ | ... | @@ -1,15 +1,14 @@ |
| 1 | // This is the implementation of the test harness for running translated | 1 | // This is the implementation of the test harness for running translated |
| 2 | // C code. For the actual test cases, see test/run_translated_c.zig. | 2 | // C code. For the actual test cases, see test/run_translated_c.zig. |
| 3 | const std = @import("std"); | 3 | const std = @import("std"); |
| 4 | const build = std.build; | ||
| 5 | const ArrayList = std.ArrayList; | 4 | const ArrayList = std.ArrayList; |
| 6 | const fmt = std.fmt; | 5 | const fmt = std.fmt; |
| 7 | const mem = std.mem; | 6 | const mem = std.mem; |
| 8 | const fs = std.fs; | 7 | const fs = std.fs; |
| 9 | 8 | ||
| 10 | pub const RunTranslatedCContext = struct { | 9 | pub const RunTranslatedCContext = struct { |
| 11 | b: *build.Builder, | 10 | b: *std.Build, |
| 12 | step: *build.Step, | 11 | step: *std.Build.Step, |
| 13 | test_index: usize, | 12 | test_index: usize, |
| 14 | test_filter: ?[]const u8, | 13 | test_filter: ?[]const u8, |
| 15 | target: std.zig.CrossTarget, | 14 | target: std.zig.CrossTarget, |
| ... | @@ -85,11 +84,14 @@ pub const RunTranslatedCContext = struct { | ... | @@ -85,11 +84,14 @@ pub const RunTranslatedCContext = struct { |
| 85 | for (case.sources.items) |src_file| { | 84 | for (case.sources.items) |src_file| { |
| 86 | write_src.add(src_file.filename, src_file.source); | 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 | translate_c.step.name = b.fmt("{s} translate-c", .{annotated_case_name}); | 93 | translate_c.step.name = b.fmt("{s} translate-c", .{annotated_case_name}); |
| 91 | const exe = translate_c.addExecutable(); | 94 | const exe = translate_c.addExecutable(.{}); |
| 92 | exe.setTarget(self.target); | ||
| 93 | exe.step.name = b.fmt("{s} build-exe", .{annotated_case_name}); | 95 | exe.step.name = b.fmt("{s} build-exe", .{annotated_case_name}); |
| 94 | exe.linkLibC(); | 96 | exe.linkLibC(); |
| 95 | const run = exe.run(); | 97 | const run = exe.run(); |
test/src/translate_c.zig+7-5| ... | @@ -1,7 +1,6 @@ | ... | @@ -1,7 +1,6 @@ |
| 1 | // This is the implementation of the test harness. | 1 | // This is the implementation of the test harness. |
| 2 | // For the actual test cases, see test/translate_c.zig. | 2 | // For the actual test cases, see test/translate_c.zig. |
| 3 | const std = @import("std"); | 3 | const std = @import("std"); |
| 4 | const build = std.build; | ||
| 5 | const ArrayList = std.ArrayList; | 4 | const ArrayList = std.ArrayList; |
| 6 | const fmt = std.fmt; | 5 | const fmt = std.fmt; |
| 7 | const mem = std.mem; | 6 | const mem = std.mem; |
| ... | @@ -9,8 +8,8 @@ const fs = std.fs; | ... | @@ -9,8 +8,8 @@ const fs = std.fs; |
| 9 | const CrossTarget = std.zig.CrossTarget; | 8 | const CrossTarget = std.zig.CrossTarget; |
| 10 | 9 | ||
| 11 | pub const TranslateCContext = struct { | 10 | pub const TranslateCContext = struct { |
| 12 | b: *build.Builder, | 11 | b: *std.Build, |
| 13 | step: *build.Step, | 12 | step: *std.Build.Step, |
| 14 | test_index: usize, | 13 | test_index: usize, |
| 15 | test_filter: ?[]const u8, | 14 | test_filter: ?[]const u8, |
| 16 | 15 | ||
| ... | @@ -108,10 +107,13 @@ pub const TranslateCContext = struct { | ... | @@ -108,10 +107,13 @@ pub const TranslateCContext = struct { |
| 108 | write_src.add(src_file.filename, src_file.source); | 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 | translate_c.step.name = annotated_case_name; | 116 | translate_c.step.name = annotated_case_name; |
| 114 | translate_c.setTarget(case.target); | ||
| 115 | 117 | ||
| 116 | const check_file = translate_c.addCheckFile(case.expected_lines.items); | 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,8 +1,10 @@ |
| 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 main = b.addTest("main.zig"); | 4 | const main = b.addTest(.{ |
| 5 | main.setBuildMode(b.standardReleaseOptions()); | 5 | .root_source_file = .{ .path = "main.zig" }, |
| 6 | .optimize = b.standardOptimizeOption(.{}), | ||
| 7 | }); | ||
| 6 | 8 | ||
| 7 | const test_step = b.step("test", "Test it"); | 9 | const test_step = b.step("test", "Test it"); |
| 8 | test_step.dependOn(&main.step); | 10 | test_step.dependOn(&main.step); |
test/standalone/c_compiler/build.zig+13-10| ... | @@ -1,9 +1,8 @@ | ... | @@ -1,9 +1,8 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const Builder = std.build.Builder; | ||
| 4 | const CrossTarget = std.zig.CrossTarget; | 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 | fn isRunnableTarget(t: CrossTarget) bool { | 6 | fn isRunnableTarget(t: CrossTarget) bool { |
| 8 | if (t.isNative()) return true; | 7 | if (t.isNative()) return true; |
| 9 | 8 | ||
| ... | @@ -11,24 +10,28 @@ fn isRunnableTarget(t: CrossTarget) bool { | ... | @@ -11,24 +10,28 @@ fn isRunnableTarget(t: CrossTarget) bool { |
| 11 | t.getCpuArch() == builtin.cpu.arch); | 10 | t.getCpuArch() == builtin.cpu.arch); |
| 12 | } | 11 | } |
| 13 | 12 | ||
| 14 | pub fn build(b: *Builder) void { | 13 | pub fn build(b: *std.Build) void { |
| 15 | const mode = b.standardReleaseOptions(); | 14 | const optimize = b.standardOptimizeOption(.{}); |
| 16 | const target = b.standardTargetOptions(.{}); | 15 | const target = b.standardTargetOptions(.{}); |
| 17 | 16 | ||
| 18 | const test_step = b.step("test", "Test the program"); | 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 | b.default_step.dependOn(&exe_c.step); | 24 | b.default_step.dependOn(&exe_c.step); |
| 22 | exe_c.addCSourceFile("test.c", &[0][]const u8{}); | 25 | exe_c.addCSourceFile("test.c", &[0][]const u8{}); |
| 23 | exe_c.setBuildMode(mode); | ||
| 24 | exe_c.setTarget(target); | ||
| 25 | exe_c.linkLibC(); | 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 | b.default_step.dependOn(&exe_cpp.step); | 33 | b.default_step.dependOn(&exe_cpp.step); |
| 29 | exe_cpp.addCSourceFile("test.cpp", &[0][]const u8{}); | 34 | exe_cpp.addCSourceFile("test.cpp", &[0][]const u8{}); |
| 30 | exe_cpp.setBuildMode(mode); | ||
| 31 | exe_cpp.setTarget(target); | ||
| 32 | exe_cpp.linkLibCpp(); | 35 | exe_cpp.linkLibCpp(); |
| 33 | 36 | ||
| 34 | switch (target.getOsTag()) { | 37 | switch (target.getOsTag()) { |
test/standalone/emit_asm_and_bin/build.zig+6-4| ... | @@ -1,8 +1,10 @@ | ... | @@ -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 { | 3 | pub fn build(b: *std.Build) void { |
| 4 | const main = b.addTest("main.zig"); | 4 | const main = b.addTest(.{ |
| 5 | main.setBuildMode(b.standardReleaseOptions()); | 5 | .root_source_file = .{ .path = "main.zig" }, |
| 6 | .optimize = b.standardOptimizeOption(.{}), | ||
| 7 | }); | ||
| 6 | main.emit_asm = .{ .emit_to = b.pathFromRoot("main.s") }; | 8 | main.emit_asm = .{ .emit_to = b.pathFromRoot("main.s") }; |
| 7 | main.emit_bin = .{ .emit_to = b.pathFromRoot("main") }; | 9 | main.emit_bin = .{ .emit_to = b.pathFromRoot("main") }; |
| 8 | 10 |
test/standalone/empty_env/build.zig+7-4| ... | @@ -1,8 +1,11 @@ | ... | @@ -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 { | 3 | pub fn build(b: *std.Build) void { |
| 4 | const main = b.addExecutable("main", "main.zig"); | 4 | const main = b.addExecutable(.{ |
| 5 | main.setBuildMode(b.standardReleaseOptions()); | 5 | .name = "main", |
| 6 | .root_source_file = .{ .path = "main.zig" }, | ||
| 7 | .optimize = b.standardOptimizeOption(.{}), | ||
| 8 | }); | ||
| 6 | 9 | ||
| 7 | const run = main.run(); | 10 | const run = main.run(); |
| 8 | run.clearEnvironment(); | 11 | run.clearEnvironment(); |
test/standalone/global_linkage/build.zig+19-9| ... | @@ -1,16 +1,26 @@ | ... | @@ -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 { | 3 | pub fn build(b: *std.Build) void { |
| 4 | const mode = b.standardReleaseOptions(); | 4 | const optimize = b.standardOptimizeOption(.{}); |
| 5 | 5 | ||
| 6 | const obj1 = b.addStaticLibrary("obj1", "obj1.zig"); | 6 | const obj1 = b.addStaticLibrary(.{ |
| 7 | obj1.setBuildMode(mode); | 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"); | 13 | const obj2 = b.addStaticLibrary(.{ |
| 10 | obj2.setBuildMode(mode); | 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"); | 20 | const main = b.addTest(.{ |
| 13 | main.setBuildMode(mode); | 21 | .root_source_file = .{ .path = "main.zig" }, |
| 22 | .optimize = optimize, | ||
| 23 | }); | ||
| 14 | main.linkLibrary(obj1); | 24 | main.linkLibrary(obj1); |
| 15 | main.linkLibrary(obj2); | 25 | main.linkLibrary(obj2); |
| 16 | 26 |
test/standalone/install_raw_hex/build.zig+9-6| ... | @@ -1,8 +1,8 @@ | ... | @@ -1,8 +1,8 @@ |
| 1 | const builtin = @import("builtin"); | 1 | const builtin = @import("builtin"); |
| 2 | const std = @import("std"); | 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 | const target = .{ | 6 | const target = .{ |
| 7 | .cpu_arch = .thumb, | 7 | .cpu_arch = .thumb, |
| 8 | .cpu_model = .{ .explicit = &std.Target.arm.cpu.cortex_m4 }, | 8 | .cpu_model = .{ .explicit = &std.Target.arm.cpu.cortex_m4 }, |
| ... | @@ -10,11 +10,14 @@ pub fn build(b: *std.build.Builder) void { | ... | @@ -10,11 +10,14 @@ pub fn build(b: *std.build.Builder) void { |
| 10 | .abi = .gnueabihf, | 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"); | 15 | const elf = b.addExecutable(.{ |
| 16 | elf.setTarget(target); | 16 | .name = "zig-nrf52-blink.elf", |
| 17 | elf.setBuildMode(mode); | 17 | .root_source_file = .{ .path = "main.zig" }, |
| 18 | .target = target, | ||
| 19 | .optimize = optimize, | ||
| 20 | }); | ||
| 18 | 21 | ||
| 19 | const test_step = b.step("test", "Test the program"); | 22 | const test_step = b.step("test", "Test the program"); |
| 20 | b.default_step.dependOn(test_step); | 23 | b.default_step.dependOn(test_step); |
test/standalone/issue_11595/build.zig+9-7| ... | @@ -1,9 +1,8 @@ | ... | @@ -1,9 +1,8 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const Builder = std.build.Builder; | ||
| 4 | const CrossTarget = std.zig.CrossTarget; | 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 | fn isRunnableTarget(t: CrossTarget) bool { | 6 | fn isRunnableTarget(t: CrossTarget) bool { |
| 8 | if (t.isNative()) return true; | 7 | if (t.isNative()) return true; |
| 9 | 8 | ||
| ... | @@ -11,12 +10,16 @@ fn isRunnableTarget(t: CrossTarget) bool { | ... | @@ -11,12 +10,16 @@ fn isRunnableTarget(t: CrossTarget) bool { |
| 11 | t.getCpuArch() == builtin.cpu.arch); | 10 | t.getCpuArch() == builtin.cpu.arch); |
| 12 | } | 11 | } |
| 13 | 12 | ||
| 14 | pub fn build(b: *Builder) void { | 13 | pub fn build(b: *std.Build) void { |
| 15 | const mode = b.standardReleaseOptions(); | 14 | const optimize = b.standardOptimizeOption(.{}); |
| 16 | const target = b.standardTargetOptions(.{}); | 15 | const target = b.standardTargetOptions(.{}); |
| 17 | 16 | ||
| 18 | const exe = b.addExecutable("zigtest", "main.zig"); | 17 | const exe = b.addExecutable(.{ |
| 19 | exe.setBuildMode(mode); | 18 | .name = "zigtest", |
| 19 | .root_source_file = .{ .path = "main.zig" }, | ||
| 20 | .target = target, | ||
| 21 | .optimize = optimize, | ||
| 22 | }); | ||
| 20 | exe.install(); | 23 | exe.install(); |
| 21 | 24 | ||
| 22 | const c_sources = [_][]const u8{ | 25 | const c_sources = [_][]const u8{ |
| ... | @@ -39,7 +42,6 @@ pub fn build(b: *Builder) void { | ... | @@ -39,7 +42,6 @@ pub fn build(b: *Builder) void { |
| 39 | exe.defineCMacro("QUX", "\"Q\" \"UX\""); | 42 | exe.defineCMacro("QUX", "\"Q\" \"UX\""); |
| 40 | exe.defineCMacro("QUUX", "\"QU\\\"UX\""); | 43 | exe.defineCMacro("QUUX", "\"QU\\\"UX\""); |
| 41 | 44 | ||
| 42 | exe.setTarget(target); | ||
| 43 | b.default_step.dependOn(&exe.step); | 45 | b.default_step.dependOn(&exe.step); |
| 44 | 46 | ||
| 45 | const test_step = b.step("test", "Test the program"); | 47 | const test_step = b.step("test", "Test the program"); |
test/standalone/issue_12588/build.zig+8-6| ... | @@ -1,13 +1,15 @@ | ... | @@ -1,13 +1,15 @@ |
| 1 | const std = @import("std"); | 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 | const mode = b.standardReleaseOptions(); | 4 | const optimize = b.standardOptimizeOption(.{}); |
| 6 | const target = b.standardTargetOptions(.{}); | 5 | const target = b.standardTargetOptions(.{}); |
| 7 | 6 | ||
| 8 | const obj = b.addObject("main", "main.zig"); | 7 | const obj = b.addObject(.{ |
| 9 | obj.setBuildMode(mode); | 8 | .name = "main", |
| 10 | obj.setTarget(target); | 9 | .root_source_file = .{ .path = "main.zig" }, |
| 10 | .optimize = optimize, | ||
| 11 | .target = target, | ||
| 12 | }); | ||
| 11 | obj.emit_llvm_ir = .{ .emit_to = b.pathFromRoot("main.ll") }; | 13 | obj.emit_llvm_ir = .{ .emit_to = b.pathFromRoot("main.ll") }; |
| 12 | obj.emit_llvm_bc = .{ .emit_to = b.pathFromRoot("main.bc") }; | 14 | obj.emit_llvm_bc = .{ .emit_to = b.pathFromRoot("main.bc") }; |
| 13 | obj.emit_bin = .no_emit; | 15 | obj.emit_bin = .no_emit; |
test/standalone/issue_12706/build.zig+9-7| ... | @@ -1,9 +1,8 @@ | ... | @@ -1,9 +1,8 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const Builder = std.build.Builder; | ||
| 4 | const CrossTarget = std.zig.CrossTarget; | 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 | fn isRunnableTarget(t: CrossTarget) bool { | 6 | fn isRunnableTarget(t: CrossTarget) bool { |
| 8 | if (t.isNative()) return true; | 7 | if (t.isNative()) return true; |
| 9 | 8 | ||
| ... | @@ -11,12 +10,16 @@ fn isRunnableTarget(t: CrossTarget) bool { | ... | @@ -11,12 +10,16 @@ fn isRunnableTarget(t: CrossTarget) bool { |
| 11 | t.getCpuArch() == builtin.cpu.arch); | 10 | t.getCpuArch() == builtin.cpu.arch); |
| 12 | } | 11 | } |
| 13 | 12 | ||
| 14 | pub fn build(b: *Builder) void { | 13 | pub fn build(b: *std.Build) void { |
| 15 | const mode = b.standardReleaseOptions(); | 14 | const optimize = b.standardOptimizeOption(.{}); |
| 16 | const target = b.standardTargetOptions(.{}); | 15 | const target = b.standardTargetOptions(.{}); |
| 17 | 16 | ||
| 18 | const exe = b.addExecutable("main", "main.zig"); | 17 | const exe = b.addExecutable(.{ |
| 19 | exe.setBuildMode(mode); | 18 | .name = "main", |
| 19 | .root_source_file = .{ .path = "main.zig" }, | ||
| 20 | .optimize = optimize, | ||
| 21 | .target = target, | ||
| 22 | }); | ||
| 20 | exe.install(); | 23 | exe.install(); |
| 21 | 24 | ||
| 22 | const c_sources = [_][]const u8{ | 25 | const c_sources = [_][]const u8{ |
| ... | @@ -26,7 +29,6 @@ pub fn build(b: *Builder) void { | ... | @@ -26,7 +29,6 @@ pub fn build(b: *Builder) void { |
| 26 | exe.addCSourceFiles(&c_sources, &.{}); | 29 | exe.addCSourceFiles(&c_sources, &.{}); |
| 27 | exe.linkLibC(); | 30 | exe.linkLibC(); |
| 28 | 31 | ||
| 29 | exe.setTarget(target); | ||
| 30 | b.default_step.dependOn(&exe.step); | 32 | b.default_step.dependOn(&exe.step); |
| 31 | 33 | ||
| 32 | const test_step = b.step("test", "Test the program"); | 34 | const test_step = b.step("test", "Test the program"); |
test/standalone/issue_13030/build.zig+8-7| ... | @@ -1,16 +1,17 @@ | ... | @@ -1,16 +1,17 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const Builder = std.build.Builder; | ||
| 4 | const CrossTarget = std.zig.CrossTarget; | 3 | const CrossTarget = std.zig.CrossTarget; |
| 5 | 4 | ||
| 6 | pub fn build(b: *Builder) void { | 5 | pub fn build(b: *std.Build) void { |
| 7 | const mode = b.standardReleaseOptions(); | 6 | const optimize = b.standardOptimizeOption(.{}); |
| 8 | const target = b.standardTargetOptions(.{}); | 7 | const target = b.standardTargetOptions(.{}); |
| 9 | 8 | ||
| 10 | const obj = b.addObject("main", "main.zig"); | 9 | const obj = b.addObject(.{ |
| 11 | obj.setBuildMode(mode); | 10 | .name = "main", |
| 12 | 11 | .root_source_file = .{ .path = "main.zig" }, | |
| 13 | obj.setTarget(target); | 12 | .optimize = optimize, |
| 13 | .target = target, | ||
| 14 | }); | ||
| 14 | b.default_step.dependOn(&obj.step); | 15 | b.default_step.dependOn(&obj.step); |
| 15 | 16 | ||
| 16 | const test_step = b.step("test", "Test the program"); | 17 | const test_step = b.step("test", "Test the program"); |
test/standalone/issue_339/build.zig+8-3| ... | @@ -1,7 +1,12 @@ | ... | @@ -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 { | 3 | pub fn build(b: *std.Build) void { |
| 4 | const obj = b.addObject("test", "test.zig"); | 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 | const test_step = b.step("test", "Test the program"); | 11 | const test_step = b.step("test", "Test the program"); |
| 7 | test_step.dependOn(&obj.step); | 12 | test_step.dependOn(&obj.step); |
test/standalone/issue_5825/build.zig+14-9| ... | @@ -1,22 +1,27 @@ | ... | @@ -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 | const target = .{ | 4 | const target = .{ |
| 5 | .cpu_arch = .x86_64, | 5 | .cpu_arch = .x86_64, |
| 6 | .os_tag = .windows, | 6 | .os_tag = .windows, |
| 7 | .abi = .msvc, | 7 | .abi = .msvc, |
| 8 | }; | 8 | }; |
| 9 | const mode = b.standardReleaseOptions(); | 9 | const optimize = b.standardOptimizeOption(.{}); |
| 10 | const obj = b.addObject("issue_5825", "main.zig"); | 10 | const obj = b.addObject(.{ |
| 11 | obj.setTarget(target); | 11 | .name = "issue_5825", |
| 12 | obj.setBuildMode(mode); | 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 | exe.subsystem = .Console; | 22 | exe.subsystem = .Console; |
| 16 | exe.linkSystemLibrary("kernel32"); | 23 | exe.linkSystemLibrary("kernel32"); |
| 17 | exe.linkSystemLibrary("ntdll"); | 24 | exe.linkSystemLibrary("ntdll"); |
| 18 | exe.setTarget(target); | ||
| 19 | exe.setBuildMode(mode); | ||
| 20 | exe.addObject(obj); | 25 | exe.addObject(obj); |
| 21 | 26 | ||
| 22 | const test_step = b.step("test", "Test the program"); | 27 | const test_step = b.step("test", "Test the program"); |
test/standalone/issue_7030/build.zig+9-6| ... | @@ -1,10 +1,13 @@ | ... | @@ -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 { | 3 | pub fn build(b: *std.Build) void { |
| 4 | const exe = b.addExecutable("issue_7030", "main.zig"); | 4 | const exe = b.addExecutable(.{ |
| 5 | exe.setTarget(.{ | 5 | .name = "issue_7030", |
| 6 | .cpu_arch = .wasm32, | 6 | .root_source_file = .{ .path = "main.zig" }, |
| 7 | .os_tag = .freestanding, | 7 | .target = .{ |
| 8 | .cpu_arch = .wasm32, | ||
| 9 | .os_tag = .freestanding, | ||
| 10 | }, | ||
| 8 | }); | 11 | }); |
| 9 | exe.install(); | 12 | exe.install(); |
| 10 | b.default_step.dependOn(&exe.step); | 13 | b.default_step.dependOn(&exe.step); |
test/standalone/issue_794/build.zig+5-3| ... | @@ -1,7 +1,9 @@ | ... | @@ -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 { | 3 | pub fn build(b: *std.Build) void { |
| 4 | const test_artifact = b.addTest("main.zig"); | 4 | const test_artifact = b.addTest(.{ |
| 5 | .root_source_file = .{ .path = "main.zig" }, | ||
| 6 | }); | ||
| 5 | test_artifact.addIncludePath("a_directory"); | 7 | test_artifact.addIncludePath("a_directory"); |
| 6 | 8 | ||
| 7 | b.default_step.dependOn(&test_artifact.step); | 9 | b.default_step.dependOn(&test_artifact.step); |
test/standalone/issue_8550/build.zig+8-5| ... | @@ -1,6 +1,6 @@ | ... | @@ -1,6 +1,6 @@ |
| 1 | const std = @import("std"); | 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 | const target = std.zig.CrossTarget{ | 4 | const target = std.zig.CrossTarget{ |
| 5 | .os_tag = .freestanding, | 5 | .os_tag = .freestanding, |
| 6 | .cpu_arch = .arm, | 6 | .cpu_arch = .arm, |
| ... | @@ -8,12 +8,15 @@ pub fn build(b: *std.build.Builder) !void { | ... | @@ -8,12 +8,15 @@ pub fn build(b: *std.build.Builder) !void { |
| 8 | .explicit = &std.Target.arm.cpu.arm1176jz_s, | 8 | .explicit = &std.Target.arm.cpu.arm1176jz_s, |
| 9 | }, | 9 | }, |
| 10 | }; | 10 | }; |
| 11 | const mode = b.standardReleaseOptions(); | 11 | const optimize = b.standardOptimizeOption(.{}); |
| 12 | const kernel = b.addExecutable("kernel", "./main.zig"); | 12 | const kernel = b.addExecutable(.{ |
| 13 | .name = "kernel", | ||
| 14 | .root_source_file = .{ .path = "./main.zig" }, | ||
| 15 | .optimize = optimize, | ||
| 16 | .target = target, | ||
| 17 | }); | ||
| 13 | kernel.addObjectFile("./boot.S"); | 18 | kernel.addObjectFile("./boot.S"); |
| 14 | kernel.setLinkerScriptPath(.{ .path = "./linker.ld" }); | 19 | kernel.setLinkerScriptPath(.{ .path = "./linker.ld" }); |
| 15 | kernel.setBuildMode(mode); | ||
| 16 | kernel.setTarget(target); | ||
| 17 | kernel.install(); | 20 | kernel.install(); |
| 18 | 21 | ||
| 19 | const test_step = b.step("test", "Test it"); | 22 | const test_step = b.step("test", "Test it"); |
test/standalone/issue_9812/build.zig+6-4| ... | @@ -1,9 +1,11 @@ | ... | @@ -1,9 +1,11 @@ |
| 1 | const std = @import("std"); | 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 | const mode = b.standardReleaseOptions(); | 4 | const optimize = b.standardOptimizeOption(.{}); |
| 5 | const zip_add = b.addTest("main.zig"); | 5 | const zip_add = b.addTest(.{ |
| 6 | zip_add.setBuildMode(mode); | 6 | .root_source_file = .{ .path = "main.zig" }, |
| 7 | .optimize = optimize, | ||
| 8 | }); | ||
| 7 | zip_add.addCSourceFile("vendor/kuba-zip/zip.c", &[_][]const u8{ | 9 | zip_add.addCSourceFile("vendor/kuba-zip/zip.c", &[_][]const u8{ |
| 8 | "-std=c99", | 10 | "-std=c99", |
| 9 | "-fno-sanitize=undefined", | 11 | "-fno-sanitize=undefined", |
test/standalone/load_dynamic_library/build.zig+17-7| ... | @@ -1,13 +1,23 @@ | ... | @@ -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 { | 3 | pub fn build(b: *std.Build) void { |
| 4 | const opts = b.standardReleaseOptions(); | 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 | const lib = b.addSharedLibrary(.{ |
| 7 | lib.setBuildMode(opts); | 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"); | 15 | const main = b.addExecutable(.{ |
| 10 | main.setBuildMode(opts); | 16 | .name = "main", |
| 17 | .root_source_file = .{ .path = "main.zig" }, | ||
| 18 | .optimize = optimize, | ||
| 19 | .target = target, | ||
| 20 | }); | ||
| 11 | 21 | ||
| 12 | const run = main.run(); | 22 | const run = main.run(); |
| 13 | run.addArtifactArg(lib); | 23 | run.addArtifactArg(lib); |
test/standalone/main_pkg_path/build.zig+5-3| ... | @@ -1,7 +1,9 @@ | ... | @@ -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 { | 3 | pub fn build(b: *std.Build) void { |
| 4 | const test_exe = b.addTest("a/test.zig"); | 4 | const test_exe = b.addTest(.{ |
| 5 | .root_source_file = .{ .path = "a/test.zig" }, | ||
| 6 | }); | ||
| 5 | test_exe.setMainPkgPath("."); | 7 | test_exe.setMainPkgPath("."); |
| 6 | 8 | ||
| 7 | const test_step = b.step("test", "Test the program"); | 9 | const test_step = b.step("test", "Test the program"); |
test/standalone/mix_c_files/build.zig+9-7| ... | @@ -1,9 +1,8 @@ | ... | @@ -1,9 +1,8 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const Builder = std.build.Builder; | ||
| 4 | const CrossTarget = std.zig.CrossTarget; | 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 | fn isRunnableTarget(t: CrossTarget) bool { | 6 | fn isRunnableTarget(t: CrossTarget) bool { |
| 8 | if (t.isNative()) return true; | 7 | if (t.isNative()) return true; |
| 9 | 8 | ||
| ... | @@ -11,15 +10,18 @@ fn isRunnableTarget(t: CrossTarget) bool { | ... | @@ -11,15 +10,18 @@ fn isRunnableTarget(t: CrossTarget) bool { |
| 11 | t.getCpuArch() == builtin.cpu.arch); | 10 | t.getCpuArch() == builtin.cpu.arch); |
| 12 | } | 11 | } |
| 13 | 12 | ||
| 14 | pub fn build(b: *Builder) void { | 13 | pub fn build(b: *std.Build) void { |
| 15 | const mode = b.standardReleaseOptions(); | 14 | const optimize = b.standardOptimizeOption(.{}); |
| 16 | const target = b.standardTargetOptions(.{}); | 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 | exe.addCSourceFile("test.c", &[_][]const u8{"-std=c11"}); | 23 | exe.addCSourceFile("test.c", &[_][]const u8{"-std=c11"}); |
| 20 | exe.setBuildMode(mode); | ||
| 21 | exe.linkLibC(); | 24 | exe.linkLibC(); |
| 22 | exe.setTarget(target); | ||
| 23 | b.default_step.dependOn(&exe.step); | 25 | b.default_step.dependOn(&exe.step); |
| 24 | 26 | ||
| 25 | const test_step = b.step("test", "Test the program"); | 27 | const test_step = b.step("test", "Test the program"); |
test/standalone/mix_o_files/build.zig+14-4| ... | @@ -1,9 +1,19 @@ | ... | @@ -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 { | 3 | pub fn build(b: *std.Build) void { |
| 4 | const obj = b.addObject("base64", "base64.zig"); | 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 | exe.addCSourceFile("test.c", &[_][]const u8{"-std=c99"}); | 17 | exe.addCSourceFile("test.c", &[_][]const u8{"-std=c99"}); |
| 8 | exe.addObject(obj); | 18 | exe.addObject(obj); |
| 9 | exe.linkSystemLibrary("c"); | 19 | exe.linkSystemLibrary("c"); |
test/standalone/options/build.zig+7-5| ... | @@ -1,12 +1,14 @@ | ... | @@ -1,12 +1,14 @@ |
| 1 | const std = @import("std"); | 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 | const target = b.standardTargetOptions(.{}); | 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"); | 7 | const main = b.addTest(.{ |
| 8 | main.setTarget(target); | 8 | .root_source_file = .{ .path = "src/main.zig" }, |
| 9 | main.setBuildMode(mode); | 9 | .target = target, |
| 10 | .optimize = optimize, | ||
| 11 | }); | ||
| 10 | 12 | ||
| 11 | const options = b.addOptions(); | 13 | const options = b.addOptions(); |
| 12 | main.addOptions("build_options", options); | 14 | main.addOptions("build_options", options); |
test/standalone/pie/build.zig+6-4| ... | @@ -1,8 +1,10 @@ | ... | @@ -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 { | 3 | pub fn build(b: *std.Build) void { |
| 4 | const main = b.addTest("main.zig"); | 4 | const main = b.addTest(.{ |
| 5 | main.setBuildMode(b.standardReleaseOptions()); | 5 | .root_source_file = .{ .path = "main.zig" }, |
| 6 | .optimize = b.standardOptimizeOption(.{}), | ||
| 7 | }); | ||
| 6 | main.pie = true; | 8 | main.pie = true; |
| 7 | 9 | ||
| 8 | const test_step = b.step("test", "Test the program"); | 10 | const test_step = b.step("test", "Test the program"); |
test/standalone/pkg_import/build.zig+9-8| ... | @@ -1,13 +1,14 @@ | ... | @@ -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 { | 3 | pub fn build(b: *std.Build) void { |
| 4 | const exe = b.addExecutable("test", "test.zig"); | 4 | const optimize = b.standardOptimizeOption(.{}); |
| 5 | exe.addPackagePath("my_pkg", "pkg.zig"); | ||
| 6 | 5 | ||
| 7 | // This is duplicated to test that you are allowed to call | 6 | const exe = b.addExecutable(.{ |
| 8 | // b.standardReleaseOptions() twice. | 7 | .name = "test", |
| 9 | exe.setBuildMode(b.standardReleaseOptions()); | 8 | .root_source_file = .{ .path = "test.zig" }, |
| 10 | exe.setBuildMode(b.standardReleaseOptions()); | 9 | .optimize = optimize, |
| 10 | }); | ||
| 11 | exe.addPackagePath("my_pkg", "pkg.zig"); | ||
| 11 | 12 | ||
| 12 | const run = exe.run(); | 13 | const run = exe.run(); |
| 13 | 14 |
test/standalone/shared_library/build.zig+15-6| ... | @@ -1,12 +1,21 @@ | ... | @@ -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 | const target = b.standardTargetOptions(.{}); | 5 | const target = b.standardTargetOptions(.{}); |
| 5 | const lib = b.addSharedLibrary("mathtest", "mathtest.zig", b.version(1, 0, 0)); | 6 | const lib = b.addSharedLibrary(.{ |
| 6 | lib.setTarget(target); | 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); | 14 | const exe = b.addExecutable(.{ |
| 9 | exe.setTarget(target); | 15 | .name = "test", |
| 16 | .target = target, | ||
| 17 | .optimize = optimize, | ||
| 18 | }); | ||
| 10 | exe.addCSourceFile("test.c", &[_][]const u8{"-std=c99"}); | 19 | exe.addCSourceFile("test.c", &[_][]const u8{"-std=c99"}); |
| 11 | exe.linkLibrary(lib); | 20 | exe.linkLibrary(lib); |
| 12 | exe.linkSystemLibrary("c"); | 21 | exe.linkSystemLibrary("c"); |
test/standalone/static_c_lib/build.zig+12-7| ... | @@ -1,15 +1,20 @@ | ... | @@ -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 { | 3 | pub fn build(b: *std.Build) void { |
| 4 | const mode = b.standardReleaseOptions(); | 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 | foo.addCSourceFile("foo.c", &[_][]const u8{}); | 11 | foo.addCSourceFile("foo.c", &[_][]const u8{}); |
| 8 | foo.setBuildMode(mode); | ||
| 9 | foo.addIncludePath("."); | 12 | foo.addIncludePath("."); |
| 10 | 13 | ||
| 11 | const test_exe = b.addTest("foo.zig"); | 14 | const test_exe = b.addTest(.{ |
| 12 | test_exe.setBuildMode(mode); | 15 | .root_source_file = .{ .path = "foo.zig" }, |
| 16 | .optimize = optimize, | ||
| 17 | }); | ||
| 13 | test_exe.linkLibrary(foo); | 18 | test_exe.linkLibrary(foo); |
| 14 | test_exe.addIncludePath("."); | 19 | test_exe.addIncludePath("."); |
| 15 | 20 |
test/standalone/test_runner_path/build.zig+6-3| ... | @@ -1,7 +1,10 @@ | ... | @@ -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 { | 3 | pub fn build(b: *std.Build) void { |
| 4 | const test_exe = b.addTestExe("test", "test.zig"); | 4 | const test_exe = b.addTest(.{ |
| 5 | .root_source_file = .{ .path = "test.zig" }, | ||
| 6 | .kind = .test_exe, | ||
| 7 | }); | ||
| 5 | test_exe.test_runner = "test_runner.zig"; | 8 | test_exe.test_runner = "test_runner.zig"; |
| 6 | 9 | ||
| 7 | const test_run = test_exe.run(); | 10 | const test_run = test_exe.run(); |
test/standalone/use_alias/build.zig+6-4| ... | @@ -1,8 +1,10 @@ | ... | @@ -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 { | 3 | pub fn build(b: *std.Build) void { |
| 4 | const main = b.addTest("main.zig"); | 4 | const main = b.addTest(.{ |
| 5 | main.setBuildMode(b.standardReleaseOptions()); | 5 | .root_source_file = .{ .path = "main.zig" }, |
| 6 | .optimize = b.standardOptimizeOption(.{}), | ||
| 7 | }); | ||
| 6 | main.addIncludePath("."); | 8 | main.addIncludePath("."); |
| 7 | 9 | ||
| 8 | const test_step = b.step("test", "Test it"); | 10 | const test_step = b.step("test", "Test it"); |
test/standalone/windows_spawn/build.zig+14-7| ... | @@ -1,13 +1,20 @@ | ... | @@ -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 { | 3 | pub fn build(b: *std.Build) void { |
| 4 | const mode = b.standardReleaseOptions(); | 4 | const optimize = b.standardOptimizeOption(.{}); |
| 5 | 5 | ||
| 6 | const hello = b.addExecutable("hello", "hello.zig"); | 6 | const hello = b.addExecutable(.{ |
| 7 | hello.setBuildMode(mode); | 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 | const run = main.run(); | 18 | const run = main.run(); |
| 12 | run.addArtifactArg(hello); | 19 | run.addArtifactArg(hello); |
| 13 | 20 |
test/tests.zig+117-100| ... | @@ -1,17 +1,17 @@ | ... | @@ -1,17 +1,17 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const debug = std.debug; | 3 | const debug = std.debug; |
| 4 | const build = std.build; | ||
| 5 | const CrossTarget = std.zig.CrossTarget; | 4 | const CrossTarget = std.zig.CrossTarget; |
| 6 | const io = std.io; | 5 | const io = std.io; |
| 7 | const fs = std.fs; | 6 | const fs = std.fs; |
| 8 | const mem = std.mem; | 7 | const mem = std.mem; |
| 9 | const fmt = std.fmt; | 8 | const fmt = std.fmt; |
| 10 | const ArrayList = std.ArrayList; | 9 | const ArrayList = std.ArrayList; |
| 11 | const Mode = std.builtin.Mode; | 10 | const OptimizeMode = std.builtin.OptimizeMode; |
| 12 | const LibExeObjStep = build.LibExeObjStep; | 11 | const CompileStep = std.Build.CompileStep; |
| 13 | const Allocator = mem.Allocator; | 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 | // Cases | 16 | // Cases |
| 17 | const compare_output = @import("compare_output.zig"); | 17 | const compare_output = @import("compare_output.zig"); |
| ... | @@ -30,7 +30,7 @@ pub const CompareOutputContext = @import("src/compare_output.zig").CompareOutput | ... | @@ -30,7 +30,7 @@ pub const CompareOutputContext = @import("src/compare_output.zig").CompareOutput |
| 30 | 30 | ||
| 31 | const TestTarget = struct { | 31 | const TestTarget = struct { |
| 32 | target: CrossTarget = @as(CrossTarget, .{}), | 32 | target: CrossTarget = @as(CrossTarget, .{}), |
| 33 | mode: std.builtin.Mode = .Debug, | 33 | optimize_mode: std.builtin.OptimizeMode = .Debug, |
| 34 | link_libc: bool = false, | 34 | link_libc: bool = false, |
| 35 | single_threaded: bool = false, | 35 | single_threaded: bool = false, |
| 36 | disable_native: bool = false, | 36 | disable_native: bool = false, |
| ... | @@ -423,38 +423,38 @@ const test_targets = blk: { | ... | @@ -423,38 +423,38 @@ const test_targets = blk: { |
| 423 | 423 | ||
| 424 | // Do the release tests last because they take a long time | 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 | .link_libc = true, | 429 | .link_libc = true, |
| 430 | .mode = .ReleaseFast, | 430 | .optimize_mode = .ReleaseFast, |
| 431 | }, | 431 | }, |
| 432 | .{ | 432 | .{ |
| 433 | .mode = .ReleaseFast, | 433 | .optimize_mode = .ReleaseFast, |
| 434 | .single_threaded = true, | 434 | .single_threaded = true, |
| 435 | }, | 435 | }, |
| 436 | 436 | ||
| 437 | .{ | 437 | .{ |
| 438 | .mode = .ReleaseSafe, | 438 | .optimize_mode = .ReleaseSafe, |
| 439 | }, | 439 | }, |
| 440 | .{ | 440 | .{ |
| 441 | .link_libc = true, | 441 | .link_libc = true, |
| 442 | .mode = .ReleaseSafe, | 442 | .optimize_mode = .ReleaseSafe, |
| 443 | }, | 443 | }, |
| 444 | .{ | 444 | .{ |
| 445 | .mode = .ReleaseSafe, | 445 | .optimize_mode = .ReleaseSafe, |
| 446 | .single_threaded = true, | 446 | .single_threaded = true, |
| 447 | }, | 447 | }, |
| 448 | 448 | ||
| 449 | .{ | 449 | .{ |
| 450 | .mode = .ReleaseSmall, | 450 | .optimize_mode = .ReleaseSmall, |
| 451 | }, | 451 | }, |
| 452 | .{ | 452 | .{ |
| 453 | .link_libc = true, | 453 | .link_libc = true, |
| 454 | .mode = .ReleaseSmall, | 454 | .optimize_mode = .ReleaseSmall, |
| 455 | }, | 455 | }, |
| 456 | .{ | 456 | .{ |
| 457 | .mode = .ReleaseSmall, | 457 | .optimize_mode = .ReleaseSmall, |
| 458 | .single_threaded = true, | 458 | .single_threaded = true, |
| 459 | }, | 459 | }, |
| 460 | }; | 460 | }; |
| ... | @@ -462,14 +462,14 @@ const test_targets = blk: { | ... | @@ -462,14 +462,14 @@ const test_targets = blk: { |
| 462 | 462 | ||
| 463 | const max_stdout_size = 1 * 1024 * 1024; // 1 MB | 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 | const cases = b.allocator.create(CompareOutputContext) catch unreachable; | 466 | const cases = b.allocator.create(CompareOutputContext) catch unreachable; |
| 467 | cases.* = CompareOutputContext{ | 467 | cases.* = CompareOutputContext{ |
| 468 | .b = b, | 468 | .b = b, |
| 469 | .step = b.step("test-compare-output", "Run the compare output tests"), | 469 | .step = b.step("test-compare-output", "Run the compare output tests"), |
| 470 | .test_index = 0, | 470 | .test_index = 0, |
| 471 | .test_filter = test_filter, | 471 | .test_filter = test_filter, |
| 472 | .modes = modes, | 472 | .optimize_modes = optimize_modes, |
| 473 | }; | 473 | }; |
| 474 | 474 | ||
| 475 | compare_output.addCases(cases); | 475 | compare_output.addCases(cases); |
| ... | @@ -477,14 +477,14 @@ pub fn addCompareOutputTests(b: *build.Builder, test_filter: ?[]const u8, modes: | ... | @@ -477,14 +477,14 @@ pub fn addCompareOutputTests(b: *build.Builder, test_filter: ?[]const u8, modes: |
| 477 | return cases.step; | 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 | const cases = b.allocator.create(StackTracesContext) catch unreachable; | 481 | const cases = b.allocator.create(StackTracesContext) catch unreachable; |
| 482 | cases.* = StackTracesContext{ | 482 | cases.* = StackTracesContext{ |
| 483 | .b = b, | 483 | .b = b, |
| 484 | .step = b.step("test-stack-traces", "Run the stack trace tests"), | 484 | .step = b.step("test-stack-traces", "Run the stack trace tests"), |
| 485 | .test_index = 0, | 485 | .test_index = 0, |
| 486 | .test_filter = test_filter, | 486 | .test_filter = test_filter, |
| 487 | .modes = modes, | 487 | .optimize_modes = optimize_modes, |
| 488 | }; | 488 | }; |
| 489 | 489 | ||
| 490 | stack_traces.addCases(cases); | 490 | stack_traces.addCases(cases); |
| ... | @@ -493,9 +493,9 @@ pub fn addStackTraceTests(b: *build.Builder, test_filter: ?[]const u8, modes: [] | ... | @@ -493,9 +493,9 @@ pub fn addStackTraceTests(b: *build.Builder, test_filter: ?[]const u8, modes: [] |
| 493 | } | 493 | } |
| 494 | 494 | ||
| 495 | pub fn addStandaloneTests( | 495 | pub fn addStandaloneTests( |
| 496 | b: *build.Builder, | 496 | b: *std.Build, |
| 497 | test_filter: ?[]const u8, | 497 | test_filter: ?[]const u8, |
| 498 | modes: []const Mode, | 498 | optimize_modes: []const OptimizeMode, |
| 499 | skip_non_native: bool, | 499 | skip_non_native: bool, |
| 500 | enable_macos_sdk: bool, | 500 | enable_macos_sdk: bool, |
| 501 | target: std.zig.CrossTarget, | 501 | target: std.zig.CrossTarget, |
| ... | @@ -506,14 +506,14 @@ pub fn addStandaloneTests( | ... | @@ -506,14 +506,14 @@ pub fn addStandaloneTests( |
| 506 | enable_wasmtime: bool, | 506 | enable_wasmtime: bool, |
| 507 | enable_wine: bool, | 507 | enable_wine: bool, |
| 508 | enable_symlinks_windows: bool, | 508 | enable_symlinks_windows: bool, |
| 509 | ) *build.Step { | 509 | ) *Step { |
| 510 | const cases = b.allocator.create(StandaloneContext) catch unreachable; | 510 | const cases = b.allocator.create(StandaloneContext) catch unreachable; |
| 511 | cases.* = StandaloneContext{ | 511 | cases.* = StandaloneContext{ |
| 512 | .b = b, | 512 | .b = b, |
| 513 | .step = b.step("test-standalone", "Run the standalone tests"), | 513 | .step = b.step("test-standalone", "Run the standalone tests"), |
| 514 | .test_index = 0, | 514 | .test_index = 0, |
| 515 | .test_filter = test_filter, | 515 | .test_filter = test_filter, |
| 516 | .modes = modes, | 516 | .optimize_modes = optimize_modes, |
| 517 | .skip_non_native = skip_non_native, | 517 | .skip_non_native = skip_non_native, |
| 518 | .enable_macos_sdk = enable_macos_sdk, | 518 | .enable_macos_sdk = enable_macos_sdk, |
| 519 | .target = target, | 519 | .target = target, |
| ... | @@ -532,20 +532,20 @@ pub fn addStandaloneTests( | ... | @@ -532,20 +532,20 @@ pub fn addStandaloneTests( |
| 532 | } | 532 | } |
| 533 | 533 | ||
| 534 | pub fn addLinkTests( | 534 | pub fn addLinkTests( |
| 535 | b: *build.Builder, | 535 | b: *std.Build, |
| 536 | test_filter: ?[]const u8, | 536 | test_filter: ?[]const u8, |
| 537 | modes: []const Mode, | 537 | optimize_modes: []const OptimizeMode, |
| 538 | enable_macos_sdk: bool, | 538 | enable_macos_sdk: bool, |
| 539 | omit_stage2: bool, | 539 | omit_stage2: bool, |
| 540 | enable_symlinks_windows: bool, | 540 | enable_symlinks_windows: bool, |
| 541 | ) *build.Step { | 541 | ) *Step { |
| 542 | const cases = b.allocator.create(StandaloneContext) catch unreachable; | 542 | const cases = b.allocator.create(StandaloneContext) catch unreachable; |
| 543 | cases.* = StandaloneContext{ | 543 | cases.* = StandaloneContext{ |
| 544 | .b = b, | 544 | .b = b, |
| 545 | .step = b.step("test-link", "Run the linker tests"), | 545 | .step = b.step("test-link", "Run the linker tests"), |
| 546 | .test_index = 0, | 546 | .test_index = 0, |
| 547 | .test_filter = test_filter, | 547 | .test_filter = test_filter, |
| 548 | .modes = modes, | 548 | .optimize_modes = optimize_modes, |
| 549 | .skip_non_native = true, | 549 | .skip_non_native = true, |
| 550 | .enable_macos_sdk = enable_macos_sdk, | 550 | .enable_macos_sdk = enable_macos_sdk, |
| 551 | .target = .{}, | 551 | .target = .{}, |
| ... | @@ -556,12 +556,17 @@ pub fn addLinkTests( | ... | @@ -556,12 +556,17 @@ pub fn addLinkTests( |
| 556 | return cases.step; | 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 | _ = test_filter; | 560 | _ = test_filter; |
| 561 | _ = modes; | 561 | _ = optimize_modes; |
| 562 | const step = b.step("test-cli", "Test the command line interface"); | 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 | const run_cmd = exe.run(); | 570 | const run_cmd = exe.run(); |
| 566 | run_cmd.addArgs(&[_][]const u8{ | 571 | run_cmd.addArgs(&[_][]const u8{ |
| 567 | fs.realpathAlloc(b.allocator, b.zig_exe) catch unreachable, | 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,14 +577,14 @@ pub fn addCliTests(b: *build.Builder, test_filter: ?[]const u8, modes: []const M |
| 572 | return step; | 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 | const cases = b.allocator.create(CompareOutputContext) catch unreachable; | 581 | const cases = b.allocator.create(CompareOutputContext) catch unreachable; |
| 577 | cases.* = CompareOutputContext{ | 582 | cases.* = CompareOutputContext{ |
| 578 | .b = b, | 583 | .b = b, |
| 579 | .step = b.step("test-asm-link", "Run the assemble and link tests"), | 584 | .step = b.step("test-asm-link", "Run the assemble and link tests"), |
| 580 | .test_index = 0, | 585 | .test_index = 0, |
| 581 | .test_filter = test_filter, | 586 | .test_filter = test_filter, |
| 582 | .modes = modes, | 587 | .optimize_modes = optimize_modes, |
| 583 | }; | 588 | }; |
| 584 | 589 | ||
| 585 | assemble_and_link.addCases(cases); | 590 | assemble_and_link.addCases(cases); |
| ... | @@ -587,7 +592,7 @@ pub fn addAssembleAndLinkTests(b: *build.Builder, test_filter: ?[]const u8, mode | ... | @@ -587,7 +592,7 @@ pub fn addAssembleAndLinkTests(b: *build.Builder, test_filter: ?[]const u8, mode |
| 587 | return cases.step; | 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 | const cases = b.allocator.create(TranslateCContext) catch unreachable; | 596 | const cases = b.allocator.create(TranslateCContext) catch unreachable; |
| 592 | cases.* = TranslateCContext{ | 597 | cases.* = TranslateCContext{ |
| 593 | .b = b, | 598 | .b = b, |
| ... | @@ -602,10 +607,10 @@ pub fn addTranslateCTests(b: *build.Builder, test_filter: ?[]const u8) *build.St | ... | @@ -602,10 +607,10 @@ pub fn addTranslateCTests(b: *build.Builder, test_filter: ?[]const u8) *build.St |
| 602 | } | 607 | } |
| 603 | 608 | ||
| 604 | pub fn addRunTranslatedCTests( | 609 | pub fn addRunTranslatedCTests( |
| 605 | b: *build.Builder, | 610 | b: *std.Build, |
| 606 | test_filter: ?[]const u8, | 611 | test_filter: ?[]const u8, |
| 607 | target: std.zig.CrossTarget, | 612 | target: std.zig.CrossTarget, |
| 608 | ) *build.Step { | 613 | ) *Step { |
| 609 | const cases = b.allocator.create(RunTranslatedCContext) catch unreachable; | 614 | const cases = b.allocator.create(RunTranslatedCContext) catch unreachable; |
| 610 | cases.* = .{ | 615 | cases.* = .{ |
| 611 | .b = b, | 616 | .b = b, |
| ... | @@ -620,7 +625,7 @@ pub fn addRunTranslatedCTests( | ... | @@ -620,7 +625,7 @@ pub fn addRunTranslatedCTests( |
| 620 | return cases.step; | 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 | const cases = b.allocator.create(GenHContext) catch unreachable; | 629 | const cases = b.allocator.create(GenHContext) catch unreachable; |
| 625 | cases.* = GenHContext{ | 630 | cases.* = GenHContext{ |
| 626 | .b = b, | 631 | .b = b, |
| ... | @@ -635,18 +640,18 @@ pub fn addGenHTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step { | ... | @@ -635,18 +640,18 @@ pub fn addGenHTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step { |
| 635 | } | 640 | } |
| 636 | 641 | ||
| 637 | pub fn addPkgTests( | 642 | pub fn addPkgTests( |
| 638 | b: *build.Builder, | 643 | b: *std.Build, |
| 639 | test_filter: ?[]const u8, | 644 | test_filter: ?[]const u8, |
| 640 | root_src: []const u8, | 645 | root_src: []const u8, |
| 641 | name: []const u8, | 646 | name: []const u8, |
| 642 | desc: []const u8, | 647 | desc: []const u8, |
| 643 | modes: []const Mode, | 648 | optimize_modes: []const OptimizeMode, |
| 644 | skip_single_threaded: bool, | 649 | skip_single_threaded: bool, |
| 645 | skip_non_native: bool, | 650 | skip_non_native: bool, |
| 646 | skip_libc: bool, | 651 | skip_libc: bool, |
| 647 | skip_stage1: bool, | 652 | skip_stage1: bool, |
| 648 | skip_stage2: bool, | 653 | skip_stage2: bool, |
| 649 | ) *build.Step { | 654 | ) *Step { |
| 650 | const step = b.step(b.fmt("test-{s}", .{name}), desc); | 655 | const step = b.step(b.fmt("test-{s}", .{name}), desc); |
| 651 | 656 | ||
| 652 | for (test_targets) |test_target| { | 657 | for (test_targets) |test_target| { |
| ... | @@ -677,8 +682,8 @@ pub fn addPkgTests( | ... | @@ -677,8 +682,8 @@ pub fn addPkgTests( |
| 677 | else => if (skip_stage2) continue, | 682 | else => if (skip_stage2) continue, |
| 678 | }; | 683 | }; |
| 679 | 684 | ||
| 680 | const want_this_mode = for (modes) |m| { | 685 | const want_this_mode = for (optimize_modes) |m| { |
| 681 | if (m == test_target.mode) break true; | 686 | if (m == test_target.optimize_mode) break true; |
| 682 | } else false; | 687 | } else false; |
| 683 | if (!want_this_mode) continue; | 688 | if (!want_this_mode) continue; |
| 684 | 689 | ||
| ... | @@ -691,21 +696,23 @@ pub fn addPkgTests( | ... | @@ -691,21 +696,23 @@ pub fn addPkgTests( |
| 691 | 696 | ||
| 692 | const triple_prefix = test_target.target.zigTriple(b.allocator) catch unreachable; | 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 | const single_threaded_txt = if (test_target.single_threaded) "single" else "multi"; | 704 | const single_threaded_txt = if (test_target.single_threaded) "single" else "multi"; |
| 696 | const backend_txt = if (test_target.backend) |backend| @tagName(backend) else "default"; | 705 | const backend_txt = if (test_target.backend) |backend| @tagName(backend) else "default"; |
| 697 | these_tests.setNamePrefix(b.fmt("{s}-{s}-{s}-{s}-{s}-{s} ", .{ | 706 | these_tests.setNamePrefix(b.fmt("{s}-{s}-{s}-{s}-{s}-{s} ", .{ |
| 698 | name, | 707 | name, |
| 699 | triple_prefix, | 708 | triple_prefix, |
| 700 | @tagName(test_target.mode), | 709 | @tagName(test_target.optimize_mode), |
| 701 | libc_prefix, | 710 | libc_prefix, |
| 702 | single_threaded_txt, | 711 | single_threaded_txt, |
| 703 | backend_txt, | 712 | backend_txt, |
| 704 | })); | 713 | })); |
| 705 | these_tests.single_threaded = test_target.single_threaded; | 714 | these_tests.single_threaded = test_target.single_threaded; |
| 706 | these_tests.setFilter(test_filter); | 715 | these_tests.setFilter(test_filter); |
| 707 | these_tests.setBuildMode(test_target.mode); | ||
| 708 | these_tests.setTarget(test_target.target); | ||
| 709 | if (test_target.link_libc) { | 716 | if (test_target.link_libc) { |
| 710 | these_tests.linkSystemLibrary("c"); | 717 | these_tests.linkSystemLibrary("c"); |
| 711 | } | 718 | } |
| ... | @@ -735,13 +742,13 @@ pub fn addPkgTests( | ... | @@ -735,13 +742,13 @@ pub fn addPkgTests( |
| 735 | } | 742 | } |
| 736 | 743 | ||
| 737 | pub const StackTracesContext = struct { | 744 | pub const StackTracesContext = struct { |
| 738 | b: *build.Builder, | 745 | b: *std.Build, |
| 739 | step: *build.Step, | 746 | step: *Step, |
| 740 | test_index: usize, | 747 | test_index: usize, |
| 741 | test_filter: ?[]const u8, | 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 | pub fn addCase(self: *StackTracesContext, config: anytype) void { | 753 | pub fn addCase(self: *StackTracesContext, config: anytype) void { |
| 747 | if (@hasField(@TypeOf(config), "exclude")) { | 754 | if (@hasField(@TypeOf(config), "exclude")) { |
| ... | @@ -755,26 +762,26 @@ pub const StackTracesContext = struct { | ... | @@ -755,26 +762,26 @@ pub const StackTracesContext = struct { |
| 755 | const exclude_os: []const std.Target.Os.Tag = &config.exclude_os; | 762 | const exclude_os: []const std.Target.Os.Tag = &config.exclude_os; |
| 756 | for (exclude_os) |os| if (os == builtin.os.tag) return; | 763 | for (exclude_os) |os| if (os == builtin.os.tag) return; |
| 757 | } | 764 | } |
| 758 | for (self.modes) |mode| { | 765 | for (self.optimize_modes) |optimize_mode| { |
| 759 | switch (mode) { | 766 | switch (optimize_mode) { |
| 760 | .Debug => { | 767 | .Debug => { |
| 761 | if (@hasField(@TypeOf(config), "Debug")) { | 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 | .ReleaseSafe => { | 772 | .ReleaseSafe => { |
| 766 | if (@hasField(@TypeOf(config), "ReleaseSafe")) { | 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 | .ReleaseFast => { | 777 | .ReleaseFast => { |
| 771 | if (@hasField(@TypeOf(config), "ReleaseFast")) { | 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 | .ReleaseSmall => { | 782 | .ReleaseSmall => { |
| 776 | if (@hasField(@TypeOf(config), "ReleaseSmall")) { | 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,7 +792,7 @@ pub const StackTracesContext = struct { |
| 785 | self: *StackTracesContext, | 792 | self: *StackTracesContext, |
| 786 | name: []const u8, | 793 | name: []const u8, |
| 787 | source: []const u8, | 794 | source: []const u8, |
| 788 | mode: Mode, | 795 | optimize_mode: OptimizeMode, |
| 789 | mode_config: anytype, | 796 | mode_config: anytype, |
| 790 | ) void { | 797 | ) void { |
| 791 | if (@hasField(@TypeOf(mode_config), "exclude")) { | 798 | if (@hasField(@TypeOf(mode_config), "exclude")) { |
| ... | @@ -803,7 +810,7 @@ pub const StackTracesContext = struct { | ... | @@ -803,7 +810,7 @@ pub const StackTracesContext = struct { |
| 803 | const annotated_case_name = fmt.allocPrint(self.b.allocator, "{s} {s} ({s})", .{ | 810 | const annotated_case_name = fmt.allocPrint(self.b.allocator, "{s} {s} ({s})", .{ |
| 804 | "stack-trace", | 811 | "stack-trace", |
| 805 | name, | 812 | name, |
| 806 | @tagName(mode), | 813 | @tagName(optimize_mode), |
| 807 | }) catch unreachable; | 814 | }) catch unreachable; |
| 808 | if (self.test_filter) |filter| { | 815 | if (self.test_filter) |filter| { |
| 809 | if (mem.indexOf(u8, annotated_case_name, filter) == null) return; | 816 | if (mem.indexOf(u8, annotated_case_name, filter) == null) return; |
| ... | @@ -812,14 +819,18 @@ pub const StackTracesContext = struct { | ... | @@ -812,14 +819,18 @@ pub const StackTracesContext = struct { |
| 812 | const b = self.b; | 819 | const b = self.b; |
| 813 | const src_basename = "source.zig"; | 820 | const src_basename = "source.zig"; |
| 814 | const write_src = b.addWriteFile(src_basename, source); | 821 | const write_src = b.addWriteFile(src_basename, source); |
| 815 | const exe = b.addExecutableSource("test", write_src.getFileSource(src_basename).?); | 822 | const exe = b.addExecutable(.{ |
| 816 | exe.setBuildMode(mode); | 823 | .name = "test", |
| 824 | .root_source_file = write_src.getFileSource(src_basename).?, | ||
| 825 | .optimize = optimize_mode, | ||
| 826 | .target = .{}, | ||
| 827 | }); | ||
| 817 | 828 | ||
| 818 | const run_and_compare = RunAndCompareStep.create( | 829 | const run_and_compare = RunAndCompareStep.create( |
| 819 | self, | 830 | self, |
| 820 | exe, | 831 | exe, |
| 821 | annotated_case_name, | 832 | annotated_case_name, |
| 822 | mode, | 833 | optimize_mode, |
| 823 | mode_config.expect, | 834 | mode_config.expect, |
| 824 | ); | 835 | ); |
| 825 | 836 | ||
| ... | @@ -829,29 +840,29 @@ pub const StackTracesContext = struct { | ... | @@ -829,29 +840,29 @@ pub const StackTracesContext = struct { |
| 829 | const RunAndCompareStep = struct { | 840 | const RunAndCompareStep = struct { |
| 830 | pub const base_id = .custom; | 841 | pub const base_id = .custom; |
| 831 | 842 | ||
| 832 | step: build.Step, | 843 | step: Step, |
| 833 | context: *StackTracesContext, | 844 | context: *StackTracesContext, |
| 834 | exe: *LibExeObjStep, | 845 | exe: *CompileStep, |
| 835 | name: []const u8, | 846 | name: []const u8, |
| 836 | mode: Mode, | 847 | optimize_mode: OptimizeMode, |
| 837 | expect_output: []const u8, | 848 | expect_output: []const u8, |
| 838 | test_index: usize, | 849 | test_index: usize, |
| 839 | 850 | ||
| 840 | pub fn create( | 851 | pub fn create( |
| 841 | context: *StackTracesContext, | 852 | context: *StackTracesContext, |
| 842 | exe: *LibExeObjStep, | 853 | exe: *CompileStep, |
| 843 | name: []const u8, | 854 | name: []const u8, |
| 844 | mode: Mode, | 855 | optimize_mode: OptimizeMode, |
| 845 | expect_output: []const u8, | 856 | expect_output: []const u8, |
| 846 | ) *RunAndCompareStep { | 857 | ) *RunAndCompareStep { |
| 847 | const allocator = context.b.allocator; | 858 | const allocator = context.b.allocator; |
| 848 | const ptr = allocator.create(RunAndCompareStep) catch unreachable; | 859 | const ptr = allocator.create(RunAndCompareStep) catch unreachable; |
| 849 | ptr.* = RunAndCompareStep{ | 860 | ptr.* = RunAndCompareStep{ |
| 850 | .step = build.Step.init(.custom, "StackTraceCompareOutputStep", allocator, make), | 861 | .step = Step.init(.custom, "StackTraceCompareOutputStep", allocator, make), |
| 851 | .context = context, | 862 | .context = context, |
| 852 | .exe = exe, | 863 | .exe = exe, |
| 853 | .name = name, | 864 | .name = name, |
| 854 | .mode = mode, | 865 | .optimize_mode = optimize_mode, |
| 855 | .expect_output = expect_output, | 866 | .expect_output = expect_output, |
| 856 | .test_index = context.test_index, | 867 | .test_index = context.test_index, |
| 857 | }; | 868 | }; |
| ... | @@ -860,7 +871,7 @@ pub const StackTracesContext = struct { | ... | @@ -860,7 +871,7 @@ pub const StackTracesContext = struct { |
| 860 | return ptr; | 871 | return ptr; |
| 861 | } | 872 | } |
| 862 | 873 | ||
| 863 | fn make(step: *build.Step) !void { | 874 | fn make(step: *Step) !void { |
| 864 | const self = @fieldParentPtr(RunAndCompareStep, "step", step); | 875 | const self = @fieldParentPtr(RunAndCompareStep, "step", step); |
| 865 | const b = self.context.b; | 876 | const b = self.context.b; |
| 866 | 877 | ||
| ... | @@ -932,7 +943,7 @@ pub const StackTracesContext = struct { | ... | @@ -932,7 +943,7 @@ pub const StackTracesContext = struct { |
| 932 | // process result | 943 | // process result |
| 933 | // - keep only basename of source file path | 944 | // - keep only basename of source file path |
| 934 | // - replace address with symbolic string | 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 | // - skip empty lines | 947 | // - skip empty lines |
| 937 | const got: []const u8 = got_result: { | 948 | const got: []const u8 = got_result: { |
| 938 | var buf = ArrayList(u8).init(b.allocator); | 949 | var buf = ArrayList(u8).init(b.allocator); |
| ... | @@ -968,7 +979,7 @@ pub const StackTracesContext = struct { | ... | @@ -968,7 +979,7 @@ pub const StackTracesContext = struct { |
| 968 | // emit substituted line | 979 | // emit substituted line |
| 969 | try buf.appendSlice(line[pos + 1 .. marks[2] + delims[2].len]); | 980 | try buf.appendSlice(line[pos + 1 .. marks[2] + delims[2].len]); |
| 970 | try buf.appendSlice(" [address]"); | 981 | try buf.appendSlice(" [address]"); |
| 971 | if (self.mode == .Debug) { | 982 | if (self.optimize_mode == .Debug) { |
| 972 | // On certain platforms (windows) or possibly depending on how we choose to link main | 983 | // On certain platforms (windows) or possibly depending on how we choose to link main |
| 973 | // the object file extension may be present so we simply strip any extension. | 984 | // the object file extension may be present so we simply strip any extension. |
| 974 | if (mem.indexOfScalar(u8, line[marks[4]..marks[5]], '.')) |idot| { | 985 | if (mem.indexOfScalar(u8, line[marks[4]..marks[5]], '.')) |idot| { |
| ... | @@ -1003,11 +1014,11 @@ pub const StackTracesContext = struct { | ... | @@ -1003,11 +1014,11 @@ pub const StackTracesContext = struct { |
| 1003 | }; | 1014 | }; |
| 1004 | 1015 | ||
| 1005 | pub const StandaloneContext = struct { | 1016 | pub const StandaloneContext = struct { |
| 1006 | b: *build.Builder, | 1017 | b: *std.Build, |
| 1007 | step: *build.Step, | 1018 | step: *Step, |
| 1008 | test_index: usize, | 1019 | test_index: usize, |
| 1009 | test_filter: ?[]const u8, | 1020 | test_filter: ?[]const u8, |
| 1010 | modes: []const Mode, | 1021 | optimize_modes: []const OptimizeMode, |
| 1011 | skip_non_native: bool, | 1022 | skip_non_native: bool, |
| 1012 | enable_macos_sdk: bool, | 1023 | enable_macos_sdk: bool, |
| 1013 | target: std.zig.CrossTarget, | 1024 | target: std.zig.CrossTarget, |
| ... | @@ -1087,13 +1098,13 @@ pub const StandaloneContext = struct { | ... | @@ -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}; | 1101 | const optimize_modes = if (features.build_modes) self.optimize_modes else &[1]OptimizeMode{.Debug}; |
| 1091 | for (modes) |mode| { | 1102 | for (optimize_modes) |optimize_mode| { |
| 1092 | const arg = switch (mode) { | 1103 | const arg = switch (optimize_mode) { |
| 1093 | .Debug => "", | 1104 | .Debug => "", |
| 1094 | .ReleaseFast => "-Drelease-fast", | 1105 | .ReleaseFast => "-Doptimize=ReleaseFast", |
| 1095 | .ReleaseSafe => "-Drelease-safe", | 1106 | .ReleaseSafe => "-Doptimize=ReleaseSafe", |
| 1096 | .ReleaseSmall => "-Drelease-small", | 1107 | .ReleaseSmall => "-Doptimize=ReleaseSmall", |
| 1097 | }; | 1108 | }; |
| 1098 | const zig_args_base_len = zig_args.items.len; | 1109 | const zig_args_base_len = zig_args.items.len; |
| 1099 | if (arg.len > 0) | 1110 | if (arg.len > 0) |
| ... | @@ -1101,7 +1112,7 @@ pub const StandaloneContext = struct { | ... | @@ -1101,7 +1112,7 @@ pub const StandaloneContext = struct { |
| 1101 | defer zig_args.resize(zig_args_base_len) catch unreachable; | 1112 | defer zig_args.resize(zig_args_base_len) catch unreachable; |
| 1102 | 1113 | ||
| 1103 | const run_cmd = b.addSystemCommand(zig_args.items); | 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 | log_step.step.dependOn(&run_cmd.step); | 1116 | log_step.step.dependOn(&run_cmd.step); |
| 1106 | 1117 | ||
| 1107 | self.step.dependOn(&log_step.step); | 1118 | self.step.dependOn(&log_step.step); |
| ... | @@ -1111,17 +1122,21 @@ pub const StandaloneContext = struct { | ... | @@ -1111,17 +1122,21 @@ pub const StandaloneContext = struct { |
| 1111 | pub fn addAllArgs(self: *StandaloneContext, root_src: []const u8, link_libc: bool) void { | 1122 | pub fn addAllArgs(self: *StandaloneContext, root_src: []const u8, link_libc: bool) void { |
| 1112 | const b = self.b; | 1123 | const b = self.b; |
| 1113 | 1124 | ||
| 1114 | for (self.modes) |mode| { | 1125 | for (self.optimize_modes) |optimize| { |
| 1115 | const annotated_case_name = fmt.allocPrint(self.b.allocator, "build {s} ({s})", .{ | 1126 | const annotated_case_name = fmt.allocPrint(self.b.allocator, "build {s} ({s})", .{ |
| 1116 | root_src, | 1127 | root_src, |
| 1117 | @tagName(mode), | 1128 | @tagName(optimize), |
| 1118 | }) catch unreachable; | 1129 | }) catch unreachable; |
| 1119 | if (self.test_filter) |filter| { | 1130 | if (self.test_filter) |filter| { |
| 1120 | if (mem.indexOf(u8, annotated_case_name, filter) == null) continue; | 1131 | if (mem.indexOf(u8, annotated_case_name, filter) == null) continue; |
| 1121 | } | 1132 | } |
| 1122 | 1133 | ||
| 1123 | const exe = b.addExecutable("test", root_src); | 1134 | const exe = b.addExecutable(.{ |
| 1124 | exe.setBuildMode(mode); | 1135 | .name = "test", |
| 1136 | .root_source_file = .{ .path = root_src }, | ||
| 1137 | .optimize = optimize, | ||
| 1138 | .target = .{}, | ||
| 1139 | }); | ||
| 1125 | if (link_libc) { | 1140 | if (link_libc) { |
| 1126 | exe.linkSystemLibrary("c"); | 1141 | exe.linkSystemLibrary("c"); |
| 1127 | } | 1142 | } |
| ... | @@ -1135,8 +1150,8 @@ pub const StandaloneContext = struct { | ... | @@ -1135,8 +1150,8 @@ pub const StandaloneContext = struct { |
| 1135 | }; | 1150 | }; |
| 1136 | 1151 | ||
| 1137 | pub const GenHContext = struct { | 1152 | pub const GenHContext = struct { |
| 1138 | b: *build.Builder, | 1153 | b: *std.Build, |
| 1139 | step: *build.Step, | 1154 | step: *Step, |
| 1140 | test_index: usize, | 1155 | test_index: usize, |
| 1141 | test_filter: ?[]const u8, | 1156 | test_filter: ?[]const u8, |
| 1142 | 1157 | ||
| ... | @@ -1163,23 +1178,23 @@ pub const GenHContext = struct { | ... | @@ -1163,23 +1178,23 @@ pub const GenHContext = struct { |
| 1163 | }; | 1178 | }; |
| 1164 | 1179 | ||
| 1165 | const GenHCmpOutputStep = struct { | 1180 | const GenHCmpOutputStep = struct { |
| 1166 | step: build.Step, | 1181 | step: Step, |
| 1167 | context: *GenHContext, | 1182 | context: *GenHContext, |
| 1168 | obj: *LibExeObjStep, | 1183 | obj: *CompileStep, |
| 1169 | name: []const u8, | 1184 | name: []const u8, |
| 1170 | test_index: usize, | 1185 | test_index: usize, |
| 1171 | case: *const TestCase, | 1186 | case: *const TestCase, |
| 1172 | 1187 | ||
| 1173 | pub fn create( | 1188 | pub fn create( |
| 1174 | context: *GenHContext, | 1189 | context: *GenHContext, |
| 1175 | obj: *LibExeObjStep, | 1190 | obj: *CompileStep, |
| 1176 | name: []const u8, | 1191 | name: []const u8, |
| 1177 | case: *const TestCase, | 1192 | case: *const TestCase, |
| 1178 | ) *GenHCmpOutputStep { | 1193 | ) *GenHCmpOutputStep { |
| 1179 | const allocator = context.b.allocator; | 1194 | const allocator = context.b.allocator; |
| 1180 | const ptr = allocator.create(GenHCmpOutputStep) catch unreachable; | 1195 | const ptr = allocator.create(GenHCmpOutputStep) catch unreachable; |
| 1181 | ptr.* = GenHCmpOutputStep{ | 1196 | ptr.* = GenHCmpOutputStep{ |
| 1182 | .step = build.Step.init(.Custom, "ParseCCmpOutput", allocator, make), | 1197 | .step = Step.init(.Custom, "ParseCCmpOutput", allocator, make), |
| 1183 | .context = context, | 1198 | .context = context, |
| 1184 | .obj = obj, | 1199 | .obj = obj, |
| 1185 | .name = name, | 1200 | .name = name, |
| ... | @@ -1191,7 +1206,7 @@ pub const GenHContext = struct { | ... | @@ -1191,7 +1206,7 @@ pub const GenHContext = struct { |
| 1191 | return ptr; | 1206 | return ptr; |
| 1192 | } | 1207 | } |
| 1193 | 1208 | ||
| 1194 | fn make(step: *build.Step) !void { | 1209 | fn make(step: *Step) !void { |
| 1195 | const self = @fieldParentPtr(GenHCmpOutputStep, "step", step); | 1210 | const self = @fieldParentPtr(GenHCmpOutputStep, "step", step); |
| 1196 | const b = self.context.b; | 1211 | const b = self.context.b; |
| 1197 | 1212 | ||
| ... | @@ -1247,8 +1262,8 @@ pub const GenHContext = struct { | ... | @@ -1247,8 +1262,8 @@ pub const GenHContext = struct { |
| 1247 | pub fn addCase(self: *GenHContext, case: *const TestCase) void { | 1262 | pub fn addCase(self: *GenHContext, case: *const TestCase) void { |
| 1248 | const b = self.b; | 1263 | const b = self.b; |
| 1249 | 1264 | ||
| 1250 | const mode = std.builtin.Mode.Debug; | 1265 | const optimize_mode = std.builtin.OptimizeMode.Debug; |
| 1251 | const annotated_case_name = fmt.allocPrint(self.b.allocator, "gen-h {s} ({s})", .{ case.name, @tagName(mode) }) catch unreachable; | 1266 | const annotated_case_name = fmt.allocPrint(self.b.allocator, "gen-h {s} ({s})", .{ case.name, @tagName(optimize_mode) }) catch unreachable; |
| 1252 | if (self.test_filter) |filter| { | 1267 | if (self.test_filter) |filter| { |
| 1253 | if (mem.indexOf(u8, annotated_case_name, filter) == null) return; | 1268 | if (mem.indexOf(u8, annotated_case_name, filter) == null) return; |
| 1254 | } | 1269 | } |
| ... | @@ -1259,7 +1274,7 @@ pub const GenHContext = struct { | ... | @@ -1259,7 +1274,7 @@ pub const GenHContext = struct { |
| 1259 | } | 1274 | } |
| 1260 | 1275 | ||
| 1261 | const obj = b.addObjectFromWriteFileStep("test", write_src, case.sources.items[0].filename); | 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 | const cmp_h = GenHCmpOutputStep.create(self, obj, annotated_case_name, case); | 1279 | const cmp_h = GenHCmpOutputStep.create(self, obj, annotated_case_name, case); |
| 1265 | 1280 | ||
| ... | @@ -1333,17 +1348,20 @@ const c_abi_targets = [_]CrossTarget{ | ... | @@ -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 | const step = b.step("test-c-abi", "Run the C ABI tests"); | 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 | if (skip_non_native and !c_abi_target.isNative()) | 1357 | if (skip_non_native and !c_abi_target.isNative()) |
| 1343 | continue; | 1358 | continue; |
| 1344 | 1359 | ||
| 1345 | const test_step = b.addTest("test/c_abi/main.zig"); | 1360 | const test_step = b.addTest(.{ |
| 1346 | test_step.setTarget(c_abi_target); | 1361 | .root_source_file = .{ .path = "test/c_abi/main.zig" }, |
| 1362 | .optimize = optimize_mode, | ||
| 1363 | .target = c_abi_target, | ||
| 1364 | }); | ||
| 1347 | if (c_abi_target.abi != null and c_abi_target.abi.?.isMusl()) { | 1365 | if (c_abi_target.abi != null and c_abi_target.abi.?.isMusl()) { |
| 1348 | // TODO NativeTargetInfo insists on dynamically linking musl | 1366 | // TODO NativeTargetInfo insists on dynamically linking musl |
| 1349 | // for some reason? | 1367 | // for some reason? |
| ... | @@ -1351,7 +1369,6 @@ pub fn addCAbiTests(b: *build.Builder, skip_non_native: bool, skip_release: bool | ... | @@ -1351,7 +1369,6 @@ pub fn addCAbiTests(b: *build.Builder, skip_non_native: bool, skip_release: bool |
| 1351 | } | 1369 | } |
| 1352 | test_step.linkLibC(); | 1370 | test_step.linkLibC(); |
| 1353 | test_step.addCSourceFile("test/c_abi/cfuncs.c", &.{"-std=c99"}); | 1371 | test_step.addCSourceFile("test/c_abi/cfuncs.c", &.{"-std=c99"}); |
| 1354 | test_step.setBuildMode(mode); | ||
| 1355 | 1372 | ||
| 1356 | if (c_abi_target.isWindows() and (c_abi_target.getCpuArch() == .x86 or builtin.target.os.tag == .linux)) { | 1373 | if (c_abi_target.isWindows() and (c_abi_target.getCpuArch() == .x86 or builtin.target.os.tag == .linux)) { |
| 1357 | // LTO currently incorrectly strips stdcall name-mangled functions | 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,7 +1380,7 @@ pub fn addCAbiTests(b: *build.Builder, skip_non_native: bool, skip_release: bool |
| 1363 | test_step.setNamePrefix(b.fmt("{s}-{s}-{s} ", .{ | 1380 | test_step.setNamePrefix(b.fmt("{s}-{s}-{s} ", .{ |
| 1364 | "test-c-abi", | 1381 | "test-c-abi", |
| 1365 | triple_prefix, | 1382 | triple_prefix, |
| 1366 | @tagName(mode), | 1383 | @tagName(optimize_mode), |
| 1367 | })); | 1384 | })); |
| 1368 | 1385 | ||
| 1369 | step.dependOn(&test_step.step); | 1386 | step.dependOn(&test_step.step); |
test/translate_c.zig+16| ... | @@ -3900,4 +3900,20 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -3900,4 +3900,20 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 3900 | \\pub const ZERO = @as(c_int, 0); | 3900 | \\pub const ZERO = @as(c_int, 0); |
| 3901 | \\pub const WORLD = @as(c_int, 0o0000123); | 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 | } |