authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-02-03 12:49:40-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-02-03 12:49:40-07:00
logfab9b7110ed1fa7bb082aad5e095047441db2b24
tree81fef60aa45e7980dab8f3e23e5b5e92b40ee0a9
parentd20d69b59e6b65a99f45cb6a45c14e887034dd18
parent60935decd318498529a016eeb1379d943a7e830d

Merge remote-tracking branch 'origin/master' into llvm16


231 files changed, 17563 insertions(+), 15987 deletions(-)

.github/workflows/ci.yaml+1
......@@ -19,6 +19,7 @@ jobs:
1919 - name: Build and Test
2020 run: sh ci/x86_64-linux-debug.sh
2121 x86_64-linux-release:
22 timeout-minutes: 420
2223 runs-on: [self-hosted, Linux, x86_64]
2324 steps:
2425 - name: Checkout
CMakeLists.txt+10-37
......@@ -513,7 +513,7 @@ set(ZIG_STAGE2_SOURCES
513513 "${CMAKE_SOURCE_DIR}/lib/std/zig/Ast.zig"
514514 "${CMAKE_SOURCE_DIR}/lib/std/zig/CrossTarget.zig"
515515 "${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"
517517 "${CMAKE_SOURCE_DIR}/lib/std/zig/render.zig"
518518 "${CMAKE_SOURCE_DIR}/lib/std/zig/string_literal.zig"
519519 "${CMAKE_SOURCE_DIR}/lib/std/zig/system.zig"
......@@ -654,46 +654,19 @@ include_directories(
654654 "${CMAKE_SOURCE_DIR}/src"
655655)
656656
657# These have to go before the -Wno- flags
658657if(MSVC)
659658 set(EXE_CXX_FLAGS "/std:c++17")
660else(MSVC)
661 set(EXE_CXX_FLAGS "-std=c++17")
662endif(MSVC)
663
664if("${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()
678endif()
679
680if(MSVC)
681 set(EXE_CXX_FLAGS "${EXE_CXX_FLAGS}")
659 set(EXE_LDFLAGS "/STACK:16777216")
660 if(NOT "${CMAKE_BUILD_TYPE}" STREQUAL "Release" AND NOT "${CMAKE_BUILD_TYPE}" STREQUAL "MinSizeRel")
661 set(EXE_LDFLAGS "${EXE_LDFLAGS} /debug:fastlink")
662 endif()
682663else()
683 set(EXE_CXX_FLAGS "${EXE_CXX_FLAGS} -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -D_GNU_SOURCE -fvisibility-inlines-hidden -fno-exceptions -fno-rtti -Werror=type-limits -Wno-missing-braces -Wno-comment")
684 if(MINGW)
685 set(EXE_CXX_FLAGS "${EXE_CXX_FLAGS} -Wno-format")
686 endif()
687endif()
688
689set(EXE_LDFLAGS " ")
690if(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()
695elseif(MINGW)
664 set(EXE_CXX_FLAGS "-std=c++17 -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -D_GNU_SOURCE -fvisibility-inlines-hidden -fno-exceptions -fno-rtti -Werror=type-limits -Wno-missing-braces -Wno-comment")
665 set(EXE_LDFLAGS " ")
666 if(MINGW)
667 set(EXE_CXX_FLAGS "${EXE_CXX_FLAGS} -Wno-format")
696668 set(EXE_LDFLAGS "${EXE_LDFLAGS} -Wl,--stack,16777216")
669 endif()
697670endif()
698671
699672if(ZIG_STATIC)
build.zig+55-204
......@@ -1,19 +1,18 @@
11const std = @import("std");
22const builtin = std.builtin;
3const Builder = std.build.Builder;
43const tests = @import("test/tests.zig");
54const BufMap = std.BufMap;
65const mem = std.mem;
76const ArrayList = std.ArrayList;
87const io = std.io;
98const fs = std.fs;
10const InstallDirectoryOptions = std.build.InstallDirectoryOptions;
9const InstallDirectoryOptions = std.Build.InstallDirectoryOptions;
1110const assert = std.debug.assert;
1211
1312const zig_version = std.builtin.Version{ .major = 0, .minor = 11, .patch = 0 };
1413const stack_size = 32 * 1024 * 1024;
1514
16pub fn build(b: *Builder) !void {
15pub fn build(b: *std.Build) !void {
1716 const release = b.option(bool, "release", "Build in release mode") orelse false;
1817 const only_c = b.option(bool, "only-c", "Translate the Zig compiler to C code, with only the C backend enabled") orelse false;
1918 const target = t: {
......@@ -23,7 +22,7 @@ pub fn build(b: *Builder) !void {
2322 }
2423 break :t b.standardTargetOptions(.{ .default_target = default_target });
2524 };
26 const mode: std.builtin.Mode = if (release) switch (target.getCpuArch()) {
25 const optimize: std.builtin.OptimizeMode = if (release) switch (target.getCpuArch()) {
2726 .wasm32 => .ReleaseSmall,
2827 else => .ReleaseFast,
2928 } else .Debug;
......@@ -33,7 +32,12 @@ pub fn build(b: *Builder) !void {
3332
3433 const test_step = b.step("test", "Run all the tests");
3534
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 });
3741 docgen_exe.single_threaded = single_threaded;
3842
3943 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 {
5357 const docs_step = b.step("docs", "Build documentation");
5458 docs_step.dependOn(&docgen_cmd.step);
5559
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 });
5764 test_cases.main_pkg_path = ".";
5865 test_cases.stack_size = stack_size;
59 test_cases.setBuildMode(mode);
6066 test_cases.single_threaded = single_threaded;
6167
6268 const fmt_build_zig = b.addFmt(&[_][]const u8{"build.zig"});
......@@ -154,17 +160,15 @@ pub fn build(b: *Builder) !void {
154160
155161 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: {
156162 if (strip == true) break :blk @as(u32, 0);
157 if (mode != .Debug) break :blk 0;
163 if (optimize != .Debug) break :blk 0;
158164 break :blk 4;
159165 };
160166
161 const exe = addCompilerStep(b);
167 const exe = addCompilerStep(b, optimize, target);
162168 exe.strip = strip;
163169 exe.sanitize_thread = sanitize_thread;
164170 exe.build_id = b.option(bool, "build-id", "Include a build id note") orelse false;
165171 exe.install();
166 exe.setBuildMode(mode);
167 exe.setTarget(target);
168172
169173 const compile_step = b.step("compile", "Build the self-hosted compiler");
170174 compile_step.dependOn(&exe.step);
......@@ -201,7 +205,7 @@ pub fn build(b: *Builder) !void {
201205 test_cases.linkLibC();
202206 }
203207
204 const is_debug = mode == .Debug;
208 const is_debug = optimize == .Debug;
205209 const enable_logging = b.option(bool, "log", "Enable debug logging with --debug-log") orelse is_debug;
206210 const enable_link_snapshots = b.option(bool, "link-snapshot", "Whether to enable linker state snapshots") orelse false;
207211
......@@ -367,25 +371,25 @@ pub fn build(b: *Builder) !void {
367371 test_step.dependOn(test_cases_step);
368372 }
369373
370 var chosen_modes: [4]builtin.Mode = undefined;
374 var chosen_opt_modes_buf: [4]builtin.Mode = undefined;
371375 var chosen_mode_index: usize = 0;
372376 if (!skip_debug) {
373 chosen_modes[chosen_mode_index] = builtin.Mode.Debug;
377 chosen_opt_modes_buf[chosen_mode_index] = builtin.Mode.Debug;
374378 chosen_mode_index += 1;
375379 }
376380 if (!skip_release_safe) {
377 chosen_modes[chosen_mode_index] = builtin.Mode.ReleaseSafe;
381 chosen_opt_modes_buf[chosen_mode_index] = builtin.Mode.ReleaseSafe;
378382 chosen_mode_index += 1;
379383 }
380384 if (!skip_release_fast) {
381 chosen_modes[chosen_mode_index] = builtin.Mode.ReleaseFast;
385 chosen_opt_modes_buf[chosen_mode_index] = builtin.Mode.ReleaseFast;
382386 chosen_mode_index += 1;
383387 }
384388 if (!skip_release_small) {
385 chosen_modes[chosen_mode_index] = builtin.Mode.ReleaseSmall;
389 chosen_opt_modes_buf[chosen_mode_index] = builtin.Mode.ReleaseSmall;
386390 chosen_mode_index += 1;
387391 }
388 const modes = chosen_modes[0..chosen_mode_index];
392 const optimization_modes = chosen_opt_modes_buf[0..chosen_mode_index];
389393
390394 // run stage1 `zig fmt` on this build.zig file just to make sure it works
391395 test_step.dependOn(&fmt_build_zig.step);
......@@ -398,7 +402,7 @@ pub fn build(b: *Builder) !void {
398402 "test/behavior.zig",
399403 "behavior",
400404 "Run the behavior tests",
401 modes,
405 optimization_modes,
402406 skip_single_threaded,
403407 skip_non_native,
404408 skip_libc,
......@@ -412,7 +416,7 @@ pub fn build(b: *Builder) !void {
412416 "lib/compiler_rt.zig",
413417 "compiler-rt",
414418 "Run the compiler_rt tests",
415 modes,
419 optimization_modes,
416420 true, // skip_single_threaded
417421 skip_non_native,
418422 true, // skip_libc
......@@ -426,7 +430,7 @@ pub fn build(b: *Builder) !void {
426430 "lib/c.zig",
427431 "universal-libc",
428432 "Run the universal libc tests",
429 modes,
433 optimization_modes,
430434 true, // skip_single_threaded
431435 skip_non_native,
432436 true, // skip_libc
......@@ -434,11 +438,11 @@ pub fn build(b: *Builder) !void {
434438 skip_stage2_tests or true, // TODO get these all passing
435439 ));
436440
437 test_step.dependOn(tests.addCompareOutputTests(b, test_filter, modes));
441 test_step.dependOn(tests.addCompareOutputTests(b, test_filter, optimization_modes));
438442 test_step.dependOn(tests.addStandaloneTests(
439443 b,
440444 test_filter,
441 modes,
445 optimization_modes,
442446 skip_non_native,
443447 enable_macos_sdk,
444448 target,
......@@ -451,10 +455,10 @@ pub fn build(b: *Builder) !void {
451455 enable_symlinks_windows,
452456 ));
453457 test_step.dependOn(tests.addCAbiTests(b, skip_non_native, skip_release));
454 test_step.dependOn(tests.addLinkTests(b, test_filter, modes, enable_macos_sdk, skip_stage2_tests, enable_symlinks_windows));
455 test_step.dependOn(tests.addStackTraceTests(b, test_filter, modes));
456 test_step.dependOn(tests.addCliTests(b, test_filter, modes));
457 test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, modes));
458 test_step.dependOn(tests.addLinkTests(b, test_filter, optimization_modes, enable_macos_sdk, skip_stage2_tests, enable_symlinks_windows));
459 test_step.dependOn(tests.addStackTraceTests(b, test_filter, optimization_modes));
460 test_step.dependOn(tests.addCliTests(b, test_filter, optimization_modes));
461 test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, optimization_modes));
458462 test_step.dependOn(tests.addTranslateCTests(b, test_filter));
459463 if (!skip_run_translated_c) {
460464 test_step.dependOn(tests.addRunTranslatedCTests(b, test_filter, target));
......@@ -468,7 +472,7 @@ pub fn build(b: *Builder) !void {
468472 "lib/std/std.zig",
469473 "std",
470474 "Run the standard library tests",
471 modes,
475 optimization_modes,
472476 skip_single_threaded,
473477 skip_non_native,
474478 skip_libc,
......@@ -479,7 +483,7 @@ pub fn build(b: *Builder) !void {
479483 try addWasiUpdateStep(b, version);
480484}
481485
482fn addWasiUpdateStep(b: *Builder, version: [:0]const u8) !void {
486fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void {
483487 const semver = try std.SemanticVersion.parse(version);
484488
485489 var target: std.zig.CrossTarget = .{
......@@ -488,9 +492,7 @@ fn addWasiUpdateStep(b: *Builder, version: [:0]const u8) !void {
488492 };
489493 target.cpu_features_add.addFeature(@enumToInt(std.Target.wasm.Feature.bulk_memory));
490494
491 const exe = addCompilerStep(b);
492 exe.setBuildMode(.ReleaseSmall);
493 exe.setTarget(target);
495 const exe = addCompilerStep(b, .ReleaseSmall, target);
494496
495497 const exe_options = b.addOptions();
496498 exe.addOptions("build_options", exe_options);
......@@ -517,8 +519,17 @@ fn addWasiUpdateStep(b: *Builder, version: [:0]const u8) !void {
517519 update_zig1_step.dependOn(&run_opt.step);
518520}
519521
520fn addCompilerStep(b: *Builder) *std.build.LibExeObjStep {
521 const exe = b.addExecutable("zig", "src/main.zig");
522fn addCompilerStep(
523 b: *std.Build,
524 optimize: std.builtin.OptimizeMode,
525 target: std.zig.CrossTarget,
526) *std.Build.CompileStep {
527 const exe = b.addExecutable(.{
528 .name = "zig",
529 .root_source_file = .{ .path = "src/main.zig" },
530 .target = target,
531 .optimize = optimize,
532 });
522533 exe.stack_size = stack_size;
523534 return exe;
524535}
......@@ -538,9 +549,9 @@ const exe_cflags = [_][]const u8{
538549};
539550
540551fn addCmakeCfgOptionsToExe(
541 b: *Builder,
552 b: *std.Build,
542553 cfg: CMakeConfig,
543 exe: *std.build.LibExeObjStep,
554 exe: *std.Build.CompileStep,
544555 use_zig_libcxx: bool,
545556) !void {
546557 if (exe.target.isDarwin()) {
......@@ -619,7 +630,7 @@ fn addCmakeCfgOptionsToExe(
619630 }
620631}
621632
622fn addStaticLlvmOptionsToExe(exe: *std.build.LibExeObjStep) !void {
633fn addStaticLlvmOptionsToExe(exe: *std.Build.CompileStep) !void {
623634 // Adds the Zig C++ sources which both stage1 and stage2 need.
624635 //
625636 // We need this because otherwise zig_clang_cc1_main.cpp ends up pulling
......@@ -656,9 +667,9 @@ fn addStaticLlvmOptionsToExe(exe: *std.build.LibExeObjStep) !void {
656667}
657668
658669fn addCxxKnownPath(
659 b: *Builder,
670 b: *std.Build,
660671 ctx: CMakeConfig,
661 exe: *std.build.LibExeObjStep,
672 exe: *std.Build.CompileStep,
662673 objname: []const u8,
663674 errtxt: ?[]const u8,
664675 need_cpp_includes: bool,
......@@ -691,7 +702,7 @@ fn addCxxKnownPath(
691702 }
692703}
693704
694fn addCMakeLibraryList(exe: *std.build.LibExeObjStep, list: []const u8) void {
705fn addCMakeLibraryList(exe: *std.Build.CompileStep, list: []const u8) void {
695706 var it = mem.tokenize(u8, list, ";");
696707 while (it.next()) |lib| {
697708 if (mem.startsWith(u8, lib, "-l")) {
......@@ -705,7 +716,7 @@ fn addCMakeLibraryList(exe: *std.build.LibExeObjStep, list: []const u8) void {
705716}
706717
707718const CMakeConfig = struct {
708 llvm_linkage: std.build.LibExeObjStep.Linkage,
719 llvm_linkage: std.Build.CompileStep.Linkage,
709720 cmake_binary_dir: []const u8,
710721 cmake_prefix_path: []const u8,
711722 cmake_static_library_prefix: []const u8,
......@@ -722,7 +733,7 @@ const CMakeConfig = struct {
722733
723734const max_config_h_bytes = 1 * 1024 * 1024;
724735
725fn findConfigH(b: *Builder, config_h_path_option: ?[]const u8) ?[]const u8 {
736fn findConfigH(b: *std.Build, config_h_path_option: ?[]const u8) ?[]const u8 {
726737 if (config_h_path_option) |path| {
727738 var config_h_or_err = fs.cwd().openFile(path, .{});
728739 if (config_h_or_err) |*file| {
......@@ -768,7 +779,7 @@ fn findConfigH(b: *Builder, config_h_path_option: ?[]const u8) ?[]const u8 {
768779 } else unreachable; // TODO should not need `else unreachable`.
769780}
770781
771fn parseConfigH(b: *Builder, config_h_text: []const u8) ?CMakeConfig {
782fn parseConfigH(b: *std.Build, config_h_text: []const u8) ?CMakeConfig {
772783 var ctx: CMakeConfig = .{
773784 .llvm_linkage = undefined,
774785 .cmake_binary_dir = undefined,
......@@ -857,7 +868,7 @@ fn parseConfigH(b: *Builder, config_h_text: []const u8) ?CMakeConfig {
857868 return ctx;
858869}
859870
860fn toNativePathSep(b: *Builder, s: []const u8) []u8 {
871fn toNativePathSep(b: *std.Build, s: []const u8) []u8 {
861872 const duplicated = b.allocator.dupe(u8, s) catch unreachable;
862873 for (duplicated) |*byte| switch (byte.*) {
863874 '/' => byte.* = fs.path.sep,
......@@ -866,166 +877,6 @@ fn toNativePathSep(b: *Builder, s: []const u8) []u8 {
866877 return duplicated;
867878}
868879
869const 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
1029880const zig_cpp_sources = [_][]const u8{
1030881 // These are planned to stay even when we are self-hosted.
1031882 "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..."
7676 -ofmt=c `
7777 -femit-bin="test-x86_64-windows-msvc.c" `
7878 --test-no-exec `
79 -target x86_64-windows-msvc
79 -target x86_64-windows-msvc `
80 -lc
8081CheckLastExitCode
8182
8283& "stage3-debug\bin\zig.exe" build-obj `
......@@ -99,7 +100,7 @@ Enter-VsDevShell -VsInstallPath "C:\Program Files\Microsoft Visual Studio\2022\E
99100CheckLastExitCode
100101
101102Write-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
103104CheckLastExitCode
104105
105106& .\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..."
7676 -ofmt=c `
7777 -femit-bin="test-x86_64-windows-msvc.c" `
7878 --test-no-exec `
79 -target x86_64-windows-msvc
79 -target x86_64-windows-msvc `
80 -lc
8081CheckLastExitCode
8182
8283& "stage3-release\bin\zig.exe" build-obj `
......@@ -99,7 +100,7 @@ Enter-VsDevShell -VsInstallPath "C:\Program Files\Microsoft Visual Studio\2022\E
99100CheckLastExitCode
100101
101102Write-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
103104CheckLastExitCode
104105
105106& .\test-x86_64-windows-msvc.exe
doc/langref.html.in+93-58
......@@ -871,6 +871,13 @@ pub fn main() void {
871871 However, it is possible to embed non-UTF-8 bytes into a string literal using <code>\xNN</code> notation.
872872 </p>
873873 <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>
874881 Unicode code point literals have type {#syntax#}comptime_int{#endsyntax#}, the same as
875882 {#link|Integer Literals#}. All {#link|Escape Sequences#} are valid in both string literals
876883 and Unicode code point literals.
......@@ -894,9 +901,12 @@ pub fn main() void {
894901 print("{}\n", .{'e' == '\x65'}); // true
895902 print("{d}\n", .{'\u{1f4a9}'}); // 128169
896903 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.
899904 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
900910}
901911 {#code_end#}
902912 {#see_also|Arrays|Source Encoding#}
......@@ -8799,6 +8809,15 @@ pub const PrefetchOptions = struct {
87998809 {#link|Optional Pointers#} are allowed. Casting an optional pointer which is {#link|null#}
88008810 to a non-optional pointer invokes safety-checked {#link|Undefined Behavior#}.
88018811 </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>
88028821 {#header_close#}
88038822
88048823 {#header_open|@ptrToInt#}
......@@ -8811,6 +8830,13 @@ pub const PrefetchOptions = struct {
88118830
88128831 {#header_close#}
88138832
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
88148840 {#header_open|@rem#}
88158841 <pre>{#syntax#}@rem(numerator: T, denominator: T) T{#endsyntax#}</pre>
88168842 <p>
......@@ -9180,8 +9206,7 @@ fn doTheTest() !void {
91809206 when available.
91819207 </p>
91829208 <p>
9183 Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that
9184 <a href="https://github.com/ziglang/zig/issues/4026">some float operations are not yet implemented for all float types</a>.
9209 Supports {#link|Floats#} and {#link|Vectors#} of floats.
91859210 </p>
91869211 {#header_close#}
91879212 {#header_open|@sin#}
......@@ -9191,8 +9216,7 @@ fn doTheTest() !void {
91919216 when available.
91929217 </p>
91939218 <p>
9194 Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that
9195 <a href="https://github.com/ziglang/zig/issues/4026">some float operations are not yet implemented for all float types</a>.
9219 Supports {#link|Floats#} and {#link|Vectors#} of floats.
91969220 </p>
91979221 {#header_close#}
91989222
......@@ -9203,8 +9227,7 @@ fn doTheTest() !void {
92039227 when available.
92049228 </p>
92059229 <p>
9206 Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that
9207 <a href="https://github.com/ziglang/zig/issues/4026">some float operations are not yet implemented for all float types</a>.
9230 Supports {#link|Floats#} and {#link|Vectors#} of floats.
92089231 </p>
92099232 {#header_close#}
92109233
......@@ -9215,8 +9238,7 @@ fn doTheTest() !void {
92159238 Uses a dedicated hardware instruction when available.
92169239 </p>
92179240 <p>
9218 Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that
9219 <a href="https://github.com/ziglang/zig/issues/4026">some float operations are not yet implemented for all float types</a>.
9241 Supports {#link|Floats#} and {#link|Vectors#} of floats.
92209242 </p>
92219243 {#header_close#}
92229244
......@@ -9227,8 +9249,7 @@ fn doTheTest() !void {
92279249 when available.
92289250 </p>
92299251 <p>
9230 Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that
9231 <a href="https://github.com/ziglang/zig/issues/4026">some float operations are not yet implemented for all float types</a>.
9252 Supports {#link|Floats#} and {#link|Vectors#} of floats.
92329253 </p>
92339254 {#header_close#}
92349255 {#header_open|@exp2#}
......@@ -9238,8 +9259,7 @@ fn doTheTest() !void {
92389259 when available.
92399260 </p>
92409261 <p>
9241 Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that
9242 <a href="https://github.com/ziglang/zig/issues/4026">some float operations are not yet implemented for all float types</a>.
9262 Supports {#link|Floats#} and {#link|Vectors#} of floats.
92439263 </p>
92449264 {#header_close#}
92459265 {#header_open|@log#}
......@@ -9249,8 +9269,7 @@ fn doTheTest() !void {
92499269 when available.
92509270 </p>
92519271 <p>
9252 Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that
9253 <a href="https://github.com/ziglang/zig/issues/4026">some float operations are not yet implemented for all float types</a>.
9272 Supports {#link|Floats#} and {#link|Vectors#} of floats.
92549273 </p>
92559274 {#header_close#}
92569275 {#header_open|@log2#}
......@@ -9260,8 +9279,7 @@ fn doTheTest() !void {
92609279 when available.
92619280 </p>
92629281 <p>
9263 Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that
9264 <a href="https://github.com/ziglang/zig/issues/4026">some float operations are not yet implemented for all float types</a>.
9282 Supports {#link|Floats#} and {#link|Vectors#} of floats.
92659283 </p>
92669284 {#header_close#}
92679285 {#header_open|@log10#}
......@@ -9271,8 +9289,7 @@ fn doTheTest() !void {
92719289 when available.
92729290 </p>
92739291 <p>
9274 Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that
9275 <a href="https://github.com/ziglang/zig/issues/4026">some float operations are not yet implemented for all float types</a>.
9292 Supports {#link|Floats#} and {#link|Vectors#} of floats.
92769293 </p>
92779294 {#header_close#}
92789295 {#header_open|@fabs#}
......@@ -9282,8 +9299,7 @@ fn doTheTest() !void {
92829299 when available.
92839300 </p>
92849301 <p>
9285 Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that
9286 <a href="https://github.com/ziglang/zig/issues/4026">some float operations are not yet implemented for all float types</a>.
9302 Supports {#link|Floats#} and {#link|Vectors#} of floats.
92879303 </p>
92889304 {#header_close#}
92899305 {#header_open|@floor#}
......@@ -9293,8 +9309,7 @@ fn doTheTest() !void {
92939309 Uses a dedicated hardware instruction when available.
92949310 </p>
92959311 <p>
9296 Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that
9297 <a href="https://github.com/ziglang/zig/issues/4026">some float operations are not yet implemented for all float types</a>.
9312 Supports {#link|Floats#} and {#link|Vectors#} of floats.
92989313 </p>
92999314 {#header_close#}
93009315 {#header_open|@ceil#}
......@@ -9304,8 +9319,7 @@ fn doTheTest() !void {
93049319 Uses a dedicated hardware instruction when available.
93059320 </p>
93069321 <p>
9307 Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that
9308 <a href="https://github.com/ziglang/zig/issues/4026">some float operations are not yet implemented for all float types</a>.
9322 Supports {#link|Floats#} and {#link|Vectors#} of floats.
93099323 </p>
93109324 {#header_close#}
93119325 {#header_open|@trunc#}
......@@ -9315,8 +9329,7 @@ fn doTheTest() !void {
93159329 Uses a dedicated hardware instruction when available.
93169330 </p>
93179331 <p>
9318 Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that
9319 <a href="https://github.com/ziglang/zig/issues/4026">some float operations are not yet implemented for all float types</a>.
9332 Supports {#link|Floats#} and {#link|Vectors#} of floats.
93209333 </p>
93219334 {#header_close#}
93229335 {#header_open|@round#}
......@@ -9326,8 +9339,7 @@ fn doTheTest() !void {
93269339 when available.
93279340 </p>
93289341 <p>
9329 Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that
9330 <a href="https://github.com/ziglang/zig/issues/4026">some float operations are not yet implemented for all float types</a>.
9342 Supports {#link|Floats#} and {#link|Vectors#} of floats.
93319343 </p>
93329344 {#header_close#}
93339345
......@@ -9528,11 +9540,15 @@ fn foo(comptime T: type, ptr: *T) T {
95289540 To add standard build options to a <code class="file">build.zig</code> file:
95299541 </p>
95309542 {#code_begin|syntax|build#}
9531const Builder = @import("std").build.Builder;
9543const std = @import("std");
95329544
9533pub fn build(b: *Builder) void {
9534 const exe = b.addExecutable("example", "example.zig");
9535 exe.setBuildMode(b.standardReleaseOptions());
9545pub fn build(b: *std.Build) void {
9546 const optimize = b.standardOptimizeOption(.{});
9547 const exe = b.addExecutable(.{
9548 .name = "example",
9549 .root_source_file = .{ .path = "example.zig" },
9550 .optimize = optimize,
9551 });
95369552 b.default_step.dependOn(&exe.step);
95379553}
95389554 {#code_end#}
......@@ -10547,22 +10563,26 @@ const separator = if (builtin.os.tag == .windows) '\\' else '/';
1054710563 <p>This <code class="file">build.zig</code> file is automatically generated
1054810564 by <kbd>zig init-exe</kbd>.</p>
1054910565 {#code_begin|syntax|build_executable#}
10550const Builder = @import("std").build.Builder;
10566const std = @import("std");
1055110567
10552pub fn build(b: *Builder) void {
10568pub fn build(b: *std.Build) void {
1055310569 // Standard target options allows the person running `zig build` to choose
1055410570 // what target to build for. Here we do not override the defaults, which
1055510571 // means any target is allowed, and the default is native. Other options
1055610572 // for restricting supported target set are available.
1055710573 const target = b.standardTargetOptions(.{});
1055810574
10559 // Standard release options allow the person running `zig build` to select
10560 // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall.
10561 const mode = b.standardReleaseOptions();
10575 // Standard optimization options allow the person running `zig build` to select
10576 // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not
10577 // set a preferred release mode, allowing the user to decide how to optimize.
10578 const optimize = b.standardOptimizeOption(.{});
1056210579
10563 const exe = b.addExecutable("example", "src/main.zig");
10564 exe.setTarget(target);
10565 exe.setBuildMode(mode);
10580 const exe = b.addExecutable(.{
10581 .name = "example",
10582 .root_source_file = .{ .path = "src/main.zig" },
10583 .target = target,
10584 .optimize = optimize,
10585 });
1056610586 exe.install();
1056710587
1056810588 const run_cmd = exe.run();
......@@ -10581,16 +10601,21 @@ pub fn build(b: *Builder) void {
1058110601 <p>This <code class="file">build.zig</code> file is automatically generated
1058210602 by <kbd>zig init-lib</kbd>.</p>
1058310603 {#code_begin|syntax|build_library#}
10584const Builder = @import("std").build.Builder;
10604const std = @import("std");
1058510605
10586pub fn build(b: *Builder) void {
10587 const mode = b.standardReleaseOptions();
10588 const lib = b.addStaticLibrary("example", "src/main.zig");
10589 lib.setBuildMode(mode);
10606pub fn build(b: *std.Build) void {
10607 const optimize = b.standardOptimizeOption(.{});
10608 const lib = b.addStaticLibrary(.{
10609 .name = "example",
10610 .root_source_file = .{ .path = "src/main.zig" },
10611 .optimize = optimize,
10612 });
1059010613 lib.install();
1059110614
10592 var main_tests = b.addTest("src/main.zig");
10593 main_tests.setBuildMode(mode);
10615 const main_tests = b.addTest(.{
10616 .root_source_file = .{ .path = "src/main.zig" },
10617 .optimize = optimize,
10618 });
1059410619
1059510620 const test_step = b.step("test", "Run library tests");
1059610621 test_step.dependOn(&main_tests.step);
......@@ -10949,12 +10974,17 @@ int main(int argc, char **argv) {
1094910974}
1095010975 {#end_syntax_block#}
1095110976 {#code_begin|syntax|build_c#}
10952const Builder = @import("std").build.Builder;
10953
10954pub fn build(b: *Builder) void {
10955 const lib = b.addSharedLibrary("mathtest", "mathtest.zig", b.version(1, 0, 0));
10977const std = @import("std");
1095610978
10957 const exe = b.addExecutable("test", null);
10979pub 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 });
1095810988 exe.addCSourceFile("test.c", &[_][]const u8{"-std=c99"});
1095910989 exe.linkLibrary(lib);
1096010990 exe.linkSystemLibrary("c");
......@@ -11011,12 +11041,17 @@ int main(int argc, char **argv) {
1101111041}
1101211042 {#end_syntax_block#}
1101311043 {#code_begin|syntax|build_object#}
11014const Builder = @import("std").build.Builder;
11044const std = @import("std");
1101511045
11016pub fn build(b: *Builder) void {
11017 const obj = b.addObject("base64", "base64.zig");
11046pub fn build(b: *std.Build) void {
11047 const obj = b.addObject(.{
11048 .name = "base64",
11049 .root_source_file = .{ .path = "base64.zig" },
11050 });
1101811051
11019 const exe = b.addExecutable("test", null);
11052 const exe = b.addExecutable(.{
11053 .name = "test",
11054 });
1102011055 exe.addCSourceFile("test.c", &[_][]const u8{"-std=c99"});
1102111056 exe.addObject(obj);
1102211057 exe.linkSystemLibrary("c");
lib/build_runner.zig+7-5
......@@ -3,7 +3,6 @@ const std = @import("std");
33const builtin = @import("builtin");
44const io = std.io;
55const fmt = std.fmt;
6const Builder = std.build.Builder;
76const mem = std.mem;
87const process = std.process;
98const ArrayList = std.ArrayList;
......@@ -42,12 +41,15 @@ pub fn main() !void {
4241 return error.InvalidArgs;
4342 };
4443
45 const builder = try Builder.create(
44 const host = try std.zig.system.NativeTargetInfo.detect(.{});
45
46 const builder = try std.Build.create(
4647 allocator,
4748 zig_exe,
4849 build_root,
4950 cache_root,
5051 global_cache_root,
52 host,
5153 );
5254 defer builder.destroy();
5355
......@@ -58,7 +60,7 @@ pub fn main() !void {
5860 const stdout_stream = io.getStdOut().writer();
5961
6062 var install_prefix: ?[]const u8 = null;
61 var dir_list = Builder.DirList{};
63 var dir_list = std.Build.DirList{};
6264
6365 // before arg parsing, check for the NO_COLOR environment variable
6466 // if it exists, default the color setting to .off
......@@ -230,7 +232,7 @@ pub fn main() !void {
230232 };
231233}
232234
233fn usage(builder: *Builder, already_ran_build: bool, out_stream: anytype) !void {
235fn usage(builder: *std.Build, already_ran_build: bool, out_stream: anytype) !void {
234236 // run the build script to collect the options
235237 if (!already_ran_build) {
236238 builder.resolveInstallPrefix(null, .{});
......@@ -330,7 +332,7 @@ fn usage(builder: *Builder, already_ran_build: bool, out_stream: anytype) !void
330332 );
331333}
332334
333fn usageAndErr(builder: *Builder, already_ran_build: bool, out_stream: anytype) void {
335fn usageAndErr(builder: *std.Build, already_ran_build: bool, out_stream: anytype) void {
334336 usage(builder, already_ran_build, out_stream) catch {};
335337 process.exit(1);
336338}
lib/compiler_rt/README.md+534-471
......@@ -27,482 +27,545 @@ then statically linked and therefore is a transparent dependency for the
2727programmer.
2828For details see `../compiler_rt.zig`.
2929
30The routines in this folder are listed below.
31Routines are annotated as `type source routine // description`, with `routine`
32being the name used in aforementioned `compiler_rt.zig`.
33`dev` means deviating from compiler_rt, `port` ported, `source` is the
34information source for the implementation, `none` means unimplemented.
35Some 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/)
40If the library or information source is uncommon, use the entry `other` for `source`.
41Please do not break the search by inserting entries in another format than `impl space source`.
42
4330Bugs should be solved by trying to duplicate the bug upstream, if possible.
4431 * If the bug exists upstream, get it fixed upstream and port the fix downstream to Zig.
4532 * If the bug only exists in Zig, use the corresponding C code and debug
4633 both implementations side by side to figure out what is wrong.
4734
48## Integer library routines
49
50#### Integer Bit operations
51
52- dev HackersDelight __clzsi2 // count leading zeros
53- dev HackersDelight __clzdi2 // count leading zeros
54- dev HackersDelight __clzti2 // count leading zeros
55- dev HackersDelight __ctzsi2 // count trailing zeros
56- dev HackersDelight __ctzdi2 // count trailing zeros
57- dev HackersDelight __ctzti2 // count trailing zeros
58- dev __ctzsi2 __ffssi2 // find least significant 1 bit
59- dev __ctzsi2 __ffsdi2 // find least significant 1 bit
60- dev __ctzsi2 __ffsti2 // find least significant 1 bit
61- dev BitTwiddlingHacks __paritysi2 // bit parity
62- dev BitTwiddlingHacks __paritydi2 // bit parity
63- dev BitTwiddlingHacks __parityti2 // bit parity
64- dev TAOCP __popcountsi2 // bit population
65- dev TAOCP __popcountdi2 // bit population
66- dev TAOCP __popcountti2 // bit population
67- dev other __bswapsi2 // a byteswapped
68- dev other __bswapdi2 // a byteswapped
69- dev other __bswapti2 // a byteswapped
70
71#### Integer Comparison
72
73- port llvm __cmpsi2 // a,b: i32, (a<b)-> 0, (a==b) -> 1, (a>b) -> 2
74- port llvm __cmpdi2 // a,b: i64
75- port llvm __cmpti2 // a,b: i128
76- port llvm __ucmpsi2 // a,b: u32, (a<b)-> 0, (a==b) -> 1, (a>b) -> 2
77- port llvm __ucmpdi2 // a,b: u64
78- port llvm __ucmpti2 // a,b: u128
79
80#### Integer Arithmetic
81
82- none none __ashlsi3 // a,b: i32, a << b unused in llvm, TODO (e.g. used by rl78)
83- port llvm __ashldi3 // a,b: u64
84- port llvm __ashlti3 // a,b: u128
85- none none __ashrsi3 // a,b: i32, a >> b arithmetic (sign fill) TODO (e.g. used by rl78)
86- port llvm __ashrdi3 // ..
87- port llvm __ashrti3 //
88- none none __lshrsi3 // a,b: i32, a >> b logical (zero fill) TODO (e.g. used by rl78)
89- port llvm __lshrdi3 //
90- port llvm __lshrti3 //
91- port llvm __negdi2 // a: i32, -a, symbol-level compatibility with libgcc
92- port llvm __negti2 // unnecessary: unused in backends
93- port llvm __mulsi3 // a,b: i32, a * b
94- port llvm __muldi3 //
95- port llvm __multi3 //
96- port llvm __divsi3 // a,b: i32, a / b
97- port llvm __divdi3 //
98- port llvm __divti3 //
99- port llvm __udivsi3 // a,b: u32, a / b
100- port llvm __udivdi3 //
101- port llvm __udivti3 //
102- port llvm __modsi3 // a,b: i32, a % b
103- port llvm __moddi3 //
104- port llvm __modti3 //
105- port llvm __umodsi3 // a,b: u32, a % b
106- port llvm __umoddi3 //
107- port llvm __umodti3 //
108- port llvm __udivmoddi4 // a,b: u32, a / b, rem.* = a % b unsigned
109- port llvm __udivmodti4 //
110- port llvm __udivmodsi4 //
111- port llvm __divmodsi4 // a,b: i32, a / b, rem.* = a % b signed, ARM
112- port llvm __divmoddi4 //
113
114#### Integer Arithmetic with trapping overflow
115
116- dev BitTwiddlingHacks __absvsi2 // abs(a)
117- dev BitTwiddlingHacks __absvdi2 // abs(a)
118- dev BitTwiddlingHacks __absvti2 // abs(a)
119- port llvm __negvsi2 // -a symbol-level compatibility: libgcc
120- port llvm __negvdi2 // -a unnecessary: unused in backends
121- port llvm __negvti2 // -a
122- TODO upstreaming __addvsi3..__mulvti3 after testing panics works
123- dev HackersDelight __addvsi3 // a + b
124- dev HackersDelight __addvdi3 //
125- dev HackersDelight __addvti3 //
126- dev HackersDelight __subvsi3 // a - b
127- dev HackersDelight __subvdi3 //
128- dev HackersDelight __subvti3 //
129- dev HackersDelight __mulvsi3 // a * b
130- dev HackersDelight __mulvdi3 //
131- dev HackersDelight __mulvti3 //
132
133#### Integer Arithmetic which returns if overflow (would be faster without pointer)
134
135- dev HackersDelight __addosi4 // a + b, overflow->ov.*=1 else 0
136- dev HackersDelight __addodi4 // (completeness + performance, llvm does not use them)
137- dev HackersDelight __addoti4 //
138- dev HackersDelight __subosi4 // a - b, overflow->ov.*=1 else 0
139- dev HackersDelight __subodi4 // (completeness + performance, llvm does not use them)
140- dev HackersDelight __suboti4 //
141- dev HackersDelight __mulosi4 // a * b, overflow->ov.*=1 else 0
142- dev HackersDelight __mulodi4 // (required by llvm)
143- dev HackersDelight __muloti4 //
144
145## Float library routines
146
147TODO: review source of implementation
148
149#### Float Conversion
150
151- dev other __extendsfdf2 // a: f32 -> f64, TODO: missing tests
152- dev other __extendsftf2 // a: f32 -> f128
153- dev llvm __extendsfxf2 // a: f32 -> f80, TODO: missing tests
154- dev other __extenddftf2 // a: f64 -> f128
155- dev llvm __extenddfxf2 // a: f64 -> f80
156- dev other __truncdfsf2 // a: f64 -> f32, rounding towards zero
157- dev other __trunctfdf2 // a: f128-> f64
158- dev other __trunctfsf2 // a: f128-> f32
159- dev llvm __truncxfsf2 // a: f80 -> f32, TODO: missing tests
160- dev llvm __truncxfdf2 // a: f80 -> f64, TODO: missing tests
161
162- dev unclear __fixsfsi // a: f32 -> i32, rounding towards zero
163- dev unclear __fixdfsi // a: f64 -> i32
164- dev unclear __fixtfsi // a: f128-> i32
165- dev unclear __fixxfsi // a: f80 -> i32, TODO: missing tests
166- dev unclear __fixsfdi // a: f32 -> i64, rounding towards zero
167- dev unclear __fixdfdi // ..
168- dev unclear __fixtfdi //
169- dev unclear __fixxfdi // TODO: missing tests
170- dev unclear __fixsfti // a: f32 -> i128, rounding towards zero
171- dev unclear __fixdfti // ..
172- dev unclear __fixtfdi //
173- dev unclear __fixxfti // TODO: missing tests
174
175- dev unclear __fixunssfsi // a: f32 -> u32, rounding towards zero. negative values become 0.
176- dev unclear __fixunsdfsi // ..
177- dev unclear __fixunstfsi //
178- dev unclear __fixunsxfsi // TODO: missing tests
179- dev unclear __fixunssfdi // a: f32 -> u64, rounding towards zero. negative values become 0.
180- dev unclear __fixunsdfdi //
181- dev unclear __fixunstfdi //
182- dev unclear __fixunsxfdi // TODO: missing tests
183- dev unclear __fixunssfti // a: f32 -> u128, rounding towards zero. negative values become 0.
184- dev unclear __fixunsdfti //
185- dev unclear __fixunstfdi //
186- dev unclear __fixunsxfti // TODO: some more tests needed for base coverage
187
188- dev unclear __floatsisf // a: i32 -> f32
189- dev unclear __floatsidf // a: i32 -> f64, TODO: missing tests
190- dev unclear __floatsitf // ..
191- dev unclear __floatsixf // TODO: missing tests
192- dev unclear __floatdisf // a: i64 -> f32
193- dev unclear __floatdidf //
194- dev unclear __floatditf //
195- dev unclear __floatdixf // TODO: missing tests
196- dev unclear __floattisf // a: i128-> f32
197- dev unclear __floattidf //
198- dev unclear __floattitf //
199- dev unclear __floattixf // TODO: missing tests
200
201- dev unclear __floatunsisf // a: u32 -> f32
202- dev unclear __floatunsidf // TODO: missing tests
203- dev unclear __floatunsitf //
204- dev unclear __floatunsixf // TODO: missing tests
205- dev unclear __floatundisf // a: u64 -> f32
206- dev unclear __floatundidf //
207- dev unclear __floatunditf //
208- dev unclear __floatundixf // TODO: missing tests
209- dev unclear __floatuntisf // a: u128-> f32
210- dev unclear __floatuntidf //
211- dev unclear __floatuntitf //
212- dev unclear __floatuntixf // TODO: missing tests
213
214#### Float Comparison
215
216- dev other __cmpsf2 // a,b:f32, (a<b)->-1,(a==b)->0,(a>b)->1,Nan->1
217- dev other __cmpdf2 // exported from __lesf2, __ledf2, __letf2 (below)
218- dev other __cmptf2 // But: if NaN is a possibility, use another routine.
219- dev other __unordsf2 // a,b:f32, (a==+-NaN or b==+-NaN) -> !=0, else -> 0
220- dev other __unorddf2 // __only reliable for (input!=NaN)__
221- dev other __unordtf2 // TODO: missing tests
222- dev other __eqsf2 // (a!=NaN) and (b!=Nan) and (a==b) -> output=0
223- dev other __eqdf2 //
224- dev other __eqtf2 //
225- dev other __nesf2 // (a==NaN) or (b==Nan) or (a!=b) -> output!=0
226- dev other __nedf2 //
227- dev other __netf2 // __eqtf2 and __netf2 have same return value -> tested with __eqsf2
228- dev other __gesf2 // (a!=Nan) and (b!=Nan) and (a>=b) -> output>=0
229- dev other __gedf2 //
230- dev other __getf2 // TODO: missing tests
231- dev other __ltsf2 // (a!=Nan) and (b!=Nan) and (a<b) -> output<0
232- dev other __ltdf2 //
233- dev other __lttf2 // TODO: missing tests
234- dev other __lesf2 // (a!=Nan) and (b!=Nan) and (a<=b) -> output<=0
235- dev other __ledf2 //
236- dev other __letf2 // TODO: missing tests
237- dev other __gtsf2 // (a!=Nan) and (b!=Nan) and (a>b) -> output>0
238- dev other __gtdf2 //
239- dev other __gttf2 // TODO: missing tests
240
241#### Float Arithmetic
242
243- dev unclear __addsf3 // a + b f32, TODO: missing tests
244- dev unclear __adddf3 // a + b f64, TODO: missing tests
245- dev unclear __addtf3 // a + b f128
246- dev unclear __addxf3 // a + b f80
247- dev unclear __aeabi_fadd // a + b f64 ARM: AAPCS
248- dev unclear __aeabi_dadd // a + b f64 ARM: AAPCS
249- dev unclear __subsf3 // a - b, TODO: missing tests
250- dev unclear __subdf3 // a - b, TODO: missing tests
251- dev unclear __subtf3 // a - b
252- dev unclear __subxf3 // a - b f80, TODO: missing tests
253- dev unclear __aeabi_fsub // a - b f64 ARM: AAPCS
254- dev unclear __aeabi_dsub // a - b f64 ARM: AAPCS
255- dev unclear __mulsf3 // a * b, TODO: missing tests
256- dev unclear __muldf3 // a * b, TODO: missing tests
257- dev unclear __multf3 // a * b
258- dev unclear __mulxf3 // a * b
259- dev unclear __divsf3 // a / b, TODO: review tests
260- dev unclear __divdf3 // a / b, TODO: review tests
261- dev unclear __divtf3 // a / b
262- dev unclear __divxf3 // a / b
263- dev unclear __negsf2 // -a symbol-level compatibility: libgcc uses this for the rl78
264- dev unclear __negdf2 // -a unnecessary: can be lowered directly to a xor
265- dev unclear __negtf2 // -a, TODO: missing tests
266- dev unclear __negxf2 // -a, TODO: missing tests
267
268#### Floating point raised to integer power
269- dev unclear __powisf2 // a ^ b, TODO
270- dev unclear __powidf2 //
271- dev unclear __powitf2 //
272- dev unclear __powixf2 //
273- dev unclear __mulsc3 // (a+ib) * (c+id)
274- dev unclear __muldc3 //
275- dev unclear __multc3 //
276- dev unclear __mulxc3 //
277- dev unclear __divsc3 // (a+ib) * / (c+id)
278- dev unclear __divdc3 //
279- dev unclear __divtc3 //
280- dev unclear __divxc3 //
281
282## Decimal float library routines
35Routines with status are given below. Sources were besides
36"The Art of Computer Programming" by Donald E. Knuth, "HackersDelight" by Henry S. Warren,
37"Bit Twiddling Hacks" collected by Sean Eron Anderson, "Berkeley SoftFloat" by John R. Hauser,
38LLVM "compiler-rt" as it was MIT-licensed, "musl libc" and thoughts + work of contributors.
39
40The compiler-rt routines have not yet been audited.
41See https://github.com/ziglang/zig/issues/1504.
42
43From left to right the columns mean 1. if the routine is implemented (✗ or ✓),
442. the name, 3. input (`a`), 4. input (`b`), 5. return value,
456. an explanation of the functionality, .. to repeat the comment from the
46column a row above and/or additional return values.
47Some routines have more extensive comments supplemented with a reference text.
48
49Integer and Float Operations
50
51| Done | Name | a | b | Out | Comment |
52| ------ | ------------- | ---- | ---- | ---- | ------------------------------ |
53| | | | | | **Integer Bit Operations** |
54| ✓ | __clzsi2 | u32 | ∅ | i32 | count leading zeroes |
55| ✓ | __clzdi2 | u64 | ∅ | i32 | count leading zeroes |
56| ✓ | __clzti2 | u128 | ∅ | i32 | count trailing zeros |
57| ✓ | __ctzsi2 | u32 | ∅ | i32 | count trailing zeros |
58| ✓ | __ctzdi2 | u64 | ∅ | i32 | count trailing zeros |
59| ✓ | __ctzti2 | u128 | ∅ | i32 | count leading zeroes |
60| ✓ | __ffssi2 | u32 | ∅ | i32 | count leading zeroes |
61| ✓ | __ffsdi2 | u64 | ∅ | i32 | count leading zeroes |
62| ✓ | __ffsti2 | u128 | ∅ | i32 | count leading zeroes |
63| ✓ | __paritysi2 | u32 | ∅ | i32 | find least significant 1 bit |
64| ✓ | __paritydi2 | u64 | ∅ | i32 | find least significant 1 bit |
65| ✓ | __parityti2 | u128 | ∅ | i32 | find least significant 1 bit |
66| ✓ | __popcountsi2 | u32 | ∅ | i32 | bit population |
67| ✓ | __popcountdi2 | u64 | ∅ | i32 | bit population |
68| ✓ | __popcountti2 | u128 | ∅ | i32 | bit population |
69| ✓ | __bswapsi2 | u32 | ∅ | i32 | bit parity |
70| ✓ | __bswapdi2 | u64 | ∅ | i32 | bit parity |
71| ✓ | __bswapti2 | u128 | ∅ | i32 | bit parity |
72| | | | | | **Integer Comparison** |
73| ✓ | __cmpsi2 | i32 | i32 | i32 | `(a<b) -> 0, (a==b) -> 1, (a>b) -> 2` |
74| ✓ | __cmpdi2 | i64 | i64 | i32 | .. |
75| ✓ | __cmpti2 | i128 | i128 | i32 | .. |
76| ✓ | __ucmpsi2 | i32 | i32 | i32 | `(a<b) -> 0, (a==b) -> 1, (a>b) -> 2` |
77| ✓ | __ucmpdi2 | i64 | i64 | i32 | .. |
78| ✓ | __ucmpti2 | i128 | i128 | i32 | .. |
79| | | | | | **Integer Arithmetic** |
80| ✗ | __ashlsi3 | i32 | i32 | i32 | `a << b` [^unused_rl78] |
81| ✓ | __ashldi3 | i64 | i32 | i64 | .. |
82| ✓ | __ashlti3 | i128 | i32 | i128 | .. |
83| ✓ | __aeabi_llsl | i32 | i32 | i32 | .. ARM |
84| ✗ | __ashrsi3 | i32 | i32 | i32 | `a >> b` arithmetic (sign fill) [^unused_rl78] |
85| ✓ | __ashrdi3 | i64 | i32 | i64 | .. |
86| ✓ | __ashrti3 | i128 | i32 | i128 | .. |
87| ✓ | __aeabi_lasr | i64 | i32 | i64 | .. ARM |
88| ✗ | __lshrsi3 | i32 | i32 | i32 | `a >> b` logical (zero fill) [^unused_rl78] |
89| ✓ | __lshrdi3 | i64 | i32 | i64 | .. |
90| ✓ | __lshrti3 | i128 | i32 | i128 | .. |
91| ✓ | __aeabi_llsr | i64 | i32 | i64 | .. ARM |
92| ✓ | __negsi2 | i32 | i32 | i32 | `-a` [^libgcc_compat] |
93| ✓ | __negdi2 | i64 | i64 | i64 | .. |
94| ✓ | __negti2 | i128 | i128 | i128 | .. |
95| ✓ | __mulsi3 | i32 | i32 | i32 | `a * b` |
96| ✓ | __muldi3 | i64 | i64 | i64 | .. |
97| ✓ | __multi3 | i128 | i128 | i128 | .. |
98| ✓ | __divsi3 | i32 | i32 | i32 | `a / b` |
99| ✓ | __divdi3 | i64 | i64 | i64 | .. |
100| ✓ | __divti3 | i128 | i128 | i128 | .. |
101| ✓ | __aeabi_idiv | i32 | i32 | i32 | .. ARM |
102| ✓ | __udivsi3 | u32 | u32 | u32 | `a / b` |
103| ✓ | __udivdi3 | u64 | u64 | u64 | .. |
104| ✓ | __udivti3 | u128 | u128 | u128 | .. |
105| ✓ | __aeabi_uidiv | i32 | i32 | i32 | .. ARM |
106| ✓ | __modsi3 | i32 | i32 | i32 | `a % b` |
107| ✓ | __moddi3 | i64 | i64 | i64 | .. |
108| ✓ | __modti3 | i128 | i128 | i128 | .. |
109| ✓ | __umodsi3 | u32 | u32 | u32 | `a % b` |
110| ✓ | __umoddi3 | u64 | u64 | u64 | .. |
111| ✓ | __umodti3 | u128 | u128 | u128 | .. |
112| ✓ | __udivmodsi4 | u32 | u32 | u32 | `a / b, rem.* = a % b` |
113| ✓ | __udivmoddi4 | u64 | u64 | u64 | .. |
114| ✓ | __udivmodti4 | u128 | u128 | u128 | .. |
115| ✓ | __divmodsi4 | i32 | i32 | i32 | `a / b, rem.* = a % b` |
116| ✓ | __divmoddi4 | i64 | i64 | i64 | .. |
117| ✗ | __divmodti4 | i128 | i128 | i128 | .. [^libgcc_compat] |
118| | | | | | **Integer Arithmetic with Trapping Overflow**|
119| ✓ | __absvsi2 | i32 | i32 | i32 | abs(a) |
120| ✓ | __absvdi2 | i64 | i64 | i64 | .. |
121| ✓ | __absvti2 | i128 | i128 | i128 | .. |
122| ✓ | __negvsi2 | i32 | i32 | i32 | `-a` [^libgcc_compat] |
123| ✓ | __negvdi2 | i64 | i64 | i64 | .. |
124| ✓ | __negvti2 | i128 | i128 | i128 | .. |
125| ✗ | __addvsi3 | i32 | i32 | i32 | `a + b` |
126| ✗ | __addvdi3 | i64 | i64 | i64 | .. |
127| ✗ | __addvti3 | i128 | i128 | i128 | .. |
128| ✗ | __subvsi3 | i32 | i32 | i32 | `a - b` |
129| ✗ | __subvdi3 | i64 | i64 | i64 | .. |
130| ✗ | __subvti3 | i128 | i128 | i128 | .. |
131| ✗ | __mulvsi3 | i32 | i32 | i32 | `a * b` |
132| ✗ | __mulvdi3 | i64 | i64 | i64 | .. |
133| ✗ | __mulvti3 | i128 | i128 | i128 | .. |
134| | | | | | **Integer Arithmetic which Return on Overflow** [^noptr_faster] |
135| ✓ | __addosi4 | i32 | i32 | i32 | `a + b`, overflow->ov.*=1 else 0 [^perf_addition] |
136| ✓ | __addodi4 | i64 | i64 | i64 | .. |
137| ✓ | __addoti4 | i128 | i128 | i128 | .. |
138| ✓ | __subosi4 | i32 | i32 | i32 | `a - b`, overflow->ov.*=1 else 0 [^perf_addition] |
139| ✓ | __subodi4 | i64 | i64 | i64 | .. |
140| ✓ | __suboti4 | i128 | i128 | i128 | .. |
141| ✓ | __mulosi4 | i32 | i32 | i32 | `a * b`, overflow->ov.*=1 else 0 |
142| ✓ | __mulodi4 | i64 | i64 | i64 | .. |
143| ✓ | __muloti4 | i128 | i128 | i128 | .. |
144| | | | | | **Float Conversion** |
145| ✓ | __extendsfdf2 | f32 | ∅ | f64 | .. |
146| ✓ | __extendsftf2 | f32 | ∅ | f128 | .. |
147| ✓ | __extendsfxf2 | f32 | ∅ | f80 | .. |
148| ✓ | __extenddftf2 | f64 | ∅ | f128 | .. |
149| ✓ | __extenddfxf2 | f64 | ∅ | f80 | .. |
150| ✓ | __truncsfhf2 | f32 | ∅ | f16 | rounding towards zero |
151| ✓ | __truncdfhf2 | f64 | ∅ | f16 | .. |
152| ✓ | __truncdfsf2 | f64 | ∅ | f32 | .. |
153| ✓ | __trunctfhf2 | f128 | ∅ | f16 | .. |
154| ✓ | __trunctfsf2 | f128 | ∅ | f32 | .. |
155| ✓ | __trunctfdf2 | f128 | ∅ | f64 | .. |
156| ✓ | __trunctfxf2 | f128 | ∅ | f80 | .. |
157| ✓ | __truncxfhf2 | f80 | ∅ | f16 | .. |
158| ✓ | __truncxfsf2 | f80 | ∅ | f32 | .. |
159| ✓ | __truncxfdf2 | f80 | ∅ | f64 | .. |
160| ✓ | __aeabi_f2h | f32 | ∅ | f16 | .. ARM |
161| ✓ | __gnu_f2h_ieee | f32 | ∅ | f16 | ..GNU naming convention |
162| ✓ | __aeabi_d2h | f64 | ∅ | f16 | .. ARM |
163| ✓ | __aeabi_d2f | f64 | ∅ | f32 | .. ARM |
164| ✓ | __trunckfsf2 | f128 | ∅ | f32 | .. PPC |
165| ✓ | _Qp_qtos |*f128 | ∅ | f32 | .. SPARC |
166| ✓ | __trunckfdf2 | f128 | ∅ | f64 | .. PPC |
167| ✓ | _Qp_qtod |*f128 | ∅ | f64 | .. SPARC |
168| ✓ | __fixhfsi | f16 | ∅ | i32 | rounding towards zero |
169| ✓ | __fixsfsi | f32 | ∅ | i32 | .. |
170| ✓ | __fixdfsi | f64 | ∅ | i32 | .. |
171| ✓ | __fixtfsi | f128 | ∅ | i32 | .. |
172| ✓ | __fixxfsi | f80 | ∅ | i32 | .. |
173| ✓ | __fixhfdi | f16 | ∅ | i64 | .. |
174| ✓ | __fixsfdi | f32 | ∅ | i64 | .. |
175| ✓ | __fixdfdi | f64 | ∅ | i64 | .. |
176| ✓ | __fixtfdi | f128 | ∅ | i64 | .. |
177| ✓ | __fixxfdi | f80 | ∅ | i64 | .. |
178| ✓ | __fixhfti | f16 | ∅ | i128 | .. |
179| ✓ | __fixsfti | f32 | ∅ | i128 | .. |
180| ✓ | __fixdfti | f64 | ∅ | i128 | .. |
181| ✓ | __fixtfti | f128 | ∅ | i128 | .. |
182| ✓ | __fixxfti | f80 | ∅ | i128 | .. |
183| ✓ | __fixunshfsi | f16 | ∅ | u32 | rounding towards zero. negative values become 0. |
184| ✓ | __fixunssfsi | f32 | ∅ | u32 | .. |
185| ✓ | __fixunsdfsi | f64 | ∅ | u32 | .. |
186| ✓ | __fixunstfsi | f128 | ∅ | u32 | .. |
187| ✓ | __fixunsxfsi | f80 | ∅ | u32 | .. |
188| ✓ | __fixunshfdi | f16 | ∅ | u64 | .. |
189| ✓ | __fixunssfdi | f32 | ∅ | u64 | .. |
190| ✓ | __fixunsdfdi | f64 | ∅ | u64 | .. |
191| ✓ | __fixunstfdi | f128 | ∅ | u64 | .. |
192| ✓ | __fixunsxfdi | f80 | ∅ | u64 | .. |
193| ✓ | __fixunshfti | f16 | ∅ | u128 | .. |
194| ✓ | __fixunssfti | f32 | ∅ | u128 | .. |
195| ✓ | __fixunsdfti | f64 | ∅ | u128 | .. |
196| ✓ | __fixunstfti | f128 | ∅ | u128 | .. |
197| ✓ | __fixunsxfti | f80 | ∅ | u128 | .. |
198| ✓ | __floatsihf | i32 | ∅ | f16 | int_to_float conversions |
199| ✓ | __floatsisf | i32 | ∅ | f32 | .. |
200| ✓ | __floatsidf | i32 | ∅ | f64 | .. |
201| ✓ | __floatsitf | i32 | ∅ | f128 | .. |
202| ✓ | __floatsixf | i32 | ∅ | f80 | .. |
203| ✓ | __floatdisf | i64 | ∅ | f32 | .. |
204| ✓ | __floatdidf | i64 | ∅ | f64 | .. |
205| ✓ | __floatditf | i64 | ∅ | f128 | .. |
206| ✓ | __floatdixf | i64 | ∅ | f80 | .. |
207| ✓ | __floattihf | i128 | ∅ | f16 | .. |
208| ✓ | __floattisf | i128 | ∅ | f32 | .. |
209| ✓ | __floattidf | i128 | ∅ | f64 | .. |
210| ✓ | __floattitf | i128 | ∅ | f128 | .. |
211| ✓ | __floattixf | i128 | ∅ | f80 | .. |
212| ✓ | __floatunsihf | u32 | ∅ | f16 | uint_to_float conversions |
213| ✓ | __floatunsisf | u32 | ∅ | f32 | .. |
214| ✓ | __floatunsidf | u32 | ∅ | f64 | .. |
215| ✓ | __floatunsitf | u32 | ∅ | f128 | .. |
216| ✓ | __floatunsixf | u32 | ∅ | f80 | .. |
217| ✓ | __floatundihf | u64 | ∅ | f16 | .. |
218| ✓ | __floatundisf | u64 | ∅ | f32 | .. |
219| ✓ | __floatundidf | u64 | ∅ | f64 | .. |
220| ✓ | __floatunditf | u64 | ∅ | f128 | .. |
221| ✓ | __floatundixf | u64 | ∅ | f80 | .. |
222| ✓ | __floatuntihf | u128 | ∅ | f16 | .. |
223| ✓ | __floatuntisf | u128 | ∅ | f32 | .. |
224| ✓ | __floatuntidf | u128 | ∅ | f64 | .. |
225| ✓ | __floatuntitf | u128 | ∅ | f128 | .. |
226| ✓ | __floatuntixf | u128 | ∅ | f80 | .. |
227| | | | | | **Float Comparison** |
228| ✓ | __cmphf2 | f16 | f16 | i32 | `(a<b)->-1, (a==b)->0, (a>b)->1, Nan->1` |
229| ✓ | __cmpsf2 | f32 | f32 | i32 | exported from __lesf2, __ledf2, __letf2 (below) |
230| ✓ | __cmpdf2 | f64 | f64 | i32 | But: if NaN is a possibility, use another routine. |
231| ✓ | __cmptf2 | f128 | f128 | i32 | .. |
232| ✓ | __cmpxf2 | f80 | f80 | i32 | .. |
233| ✓ | _Qp_cmp |*f128 |*f128 | i32 | .. SPARC |
234| ✓ | __unordhf2 | f16 | f16 | i32 | `(a==+-NaN or b==+-NaN) -> !=0, else -> 0` |
235| ✓ | __unordsf2 | f32 | f32 | i32 | .. |
236| ✓ | __unorddf2 | f64 | f64 | i32 | Note: only reliable for (input!=NaN) |
237| ✓ | __unordtf2 | f128 | f128 | i32 | .. |
238| ✓ | __unordxf2 | f80 | f80 | i32 | .. |
239| ✓ | __aeabi_fcmpun | f32 | f32 | i32 | .. ARM |
240| ✓ | __aeabi_dcmpun | f32 | f32 | i32 | .. ARM |
241| ✓ | __unordkf2 | f128 | f128 | i32 | .. PPC |
242| ✓ | __eqhf2 | f16 | f16 | i32 | `(a!=NaN) and (b!=Nan) and (a==b) -> output=0` |
243| ✓ | __eqsf2 | f32 | f32 | i32 | .. |
244| ✓ | __eqdf2 | f64 | f64 | i32 | .. |
245| ✓ | __eqtf2 | f128 | f128 | i32 | .. |
246| ✓ | __eqxf2 | f80 | f80 | i32 | .. |
247| ✓ | __aeabi_fcmpeq | f32 | f32 | i32 | .. ARM |
248| ✓ | __aeabi_dcmpeq | f32 | f32 | i32 | .. ARM |
249| ✓ | __eqkf2 | f128 | f128 | i32 | .. PPC |
250| ✓ | _Qp_feq |*f128 |*f128 | bool | .. SPARC |
251| ✓ | __nehf2 | f16 | f16 | i32 | `(a==NaN) or (b==Nan) or (a!=b) -> output!=0` |
252| ✓ | __nesf2 | f32 | f32 | i32 | Note: __eqXf2 and __neXf2 have same return value |
253| ✓ | __nedf2 | f64 | f64 | i32 | .. |
254| ✓ | __netf2 | f128 | f128 | i32 | .. |
255| ✓ | __nexf2 | f80 | f80 | i32 | .. |
256| ✓ | __nekf2 | f128 | f128 | i32 | .. PPC |
257| ✓ | _Qp_fne |*f128 |*f128 | bool | .. SPARC |
258| ✓ | __gehf2 | f16 | f16 | i32 | `(a!=Nan) and (b!=Nan) and (a>=b) -> output>=0` |
259| ✓ | __gesf2 | f32 | f32 | i32 | .. |
260| ✓ | __gedf2 | f64 | f64 | i32 | .. |
261| ✓ | __getf2 | f128 | f128 | i32 | .. |
262| ✓ | __gexf2 | f80 | f80 | i32 | .. |
263| ✓ | __gekf2 | f128 | f128 | i32 | .. PPC |
264| ✓ | _Qp_fge |*f128 |*f128 | bool | .. SPARC |
265| ✓ | __lthf2 | f16 | f16 | i32 | `(a!=Nan) and (b!=Nan) and (a<b) -> output<0` |
266| ✓ | __ltsf2 | f32 | f32 | i32 | .. |
267| ✓ | __ltdf2 | f64 | f64 | i32 | .. |
268| ✓ | __lttf2 | f128 | f128 | i32 | .. |
269| ✓ | __ltxf2 | f80 | f80 | i32 | .. |
270| ✓ | __ltkf2 | f128 | f128 | i32 | .. PPC |
271| ✓ | __aeabi_fcmplt | f32 | f32 | i32 | .. ARM |
272| ✓ | __aeabi_dcmplt | f32 | f32 | i32 | .. ARM |
273| ✓ | _Qp_flt |*f128 |*f128 | bool | .. SPARC |
274| ✓ | __lehf2 | f16 | f16 | i32 | `(a!=Nan) and (b!=Nan) and (a<=b) -> output<=0` |
275| ✓ | __lesf2 | f32 | f32 | i32 | .. |
276| ✓ | __ledf2 | f64 | f64 | i32 | .. |
277| ✓ | __letf2 | f128 | f128 | i32 | .. |
278| ✓ | __lexf2 | f80 | f80 | i32 | .. |
279| ✓ | __aeabi_fcmple | f32 | f32 | i32 | .. ARM |
280| ✓ | __aeabi_dcmple | f32 | f32 | i32 | .. ARM |
281| ✓ | __lekf2 | f128 | f128 | i32 | .. PPC |
282| ✓ | _Qp_fle |*f128 |*f128 | bool | .. SPARC |
283| ✓ | __gthf2 | f16 | f16 | i32 | `(a!=Nan) and (b!=Nan) and (a>b) -> output>0` |
284| ✓ | __gtsf2 | f32 | f32 | i32 | .. |
285| ✓ | __gtdf2 | f64 | f64 | i32 | .. |
286| ✓ | __gttf2 | f128 | f128 | i32 | .. |
287| ✓ | __gtxf2 | f80 | f80 | i32 | .. |
288| ✓ | __gtkf2 | f128 | f128 | i32 | .. PPC |
289| ✓ | _Qp_fgt |*f128 |*f128 | bool | .. SPARC |
290| | | | | | **Float Arithmetic** |
291| ✓ | __addhf3 | f32 | f32 | f32 | `a + b` |
292| ✓ | __addsf3 | f32 | f32 | f32 | .. |
293| ✓ | __adddf3 | f64 | f64 | f64 | .. |
294| ✓ | __addtf3 | f128 | f128 | f128 | .. |
295| ✓ | __addxf3 | f80 | f80 | f80 | .. |
296| ✓ | __aeabi_fadd | f32 | f32 | f32 | .. ARM |
297| ✓ | __aeabi_dadd | f64 | f64 | f64 | .. ARM |
298| ✓ | __addkf3 | f128 | f128 | f128 | .. PPC |
299| ✓ | _Qp_add |*f128 |*f128 | void | .. SPARC args *c,*a,*b c=a+b |
300| ✓ | __subhf3 | f32 | f32 | f32 | `a - b` |
301| ✓ | __subsf3 | f32 | f32 | f32 | .. |
302| ✓ | __subdf3 | f64 | f64 | f64 | .. |
303| ✓ | __subtf3 | f128 | f128 | f128 | .. |
304| ✓ | __subxf3 | f80 | f80 | f80 | .. |
305| ✓ | __aeabi_fsub | f32 | f32 | f32 | .. ARM |
306| ✓ | __aeabi_dsub | f64 | f64 | f64 | .. ARM |
307| ✓ | __subkf3 | f128 | f128 | f128 | .. PPC |
308| ✓ | _Qp_sub |*f128 |*f128 | void | .. SPARC args *c,*a,*b c=a-b |
309| ✓ | __mulhf3 | f32 | f32 | f32 | `a * b` |
310| ✓ | __mulsf3 | f32 | f32 | f32 | .. |
311| ✓ | __muldf3 | f64 | f64 | f64 | .. |
312| ✓ | __multf3 | f128 | f128 | f128 | .. |
313| ✓ | __mulxf3 | f80 | f80 | f80 | .. |
314| ✓ | __aeabi_fmul | f32 | f32 | f32 | .. ARM |
315| ✓ | __aeabi_dmul | f64 | f64 | f64 | .. ARM |
316| ✓ | __mulkf3 | f128 | f128 | f128 | .. PPC |
317| ✓ | _Qp_mul |*f128 |*f128 | void | .. SPARC args *c,*a,*b c=a*b |
318| ✓ | __divsf3 | f32 | f32 | f32 | `a / b` |
319| ✓ | __divdf3 | f64 | f64 | f64 | .. |
320| ✓ | __divtf3 | f128 | f128 | f128 | .. |
321| ✓ | __divxf3 | f80 | f80 | f80 | .. |
322| ✓ | __aeabi_fdiv | f32 | f32 | f32 | .. ARM |
323| ✓ | __aeabi_ddiv | f64 | f64 | f64 | .. ARM |
324| ✓ | __divkf3 | f128 | f128 | f128 | .. PPC |
325| ✓ | _Qp_div |*f128 |*f128 | void | .. SPARC args *c,*a,*b c=a*b |
326| ✓ | __negsf2 | f32 | ∅ | f32[^unused_rl78] | -a (can be lowered directly to a xor) |
327| ✓ | __negdf2 | f64 | ∅ | f64 | .. |
328| ✓ | __negtf2 | f128 | ∅ | f128 | .. |
329| ✓ | __negxf2 | f80 | ∅ | f80 | .. |
330| | | | | | **Floating point raised to integer power** |
331| ✗ | __powihf2 | f16 | f16 | f16 | `a ^ b` |
332| ✗ | __powisf2 | f32 | f32 | f32 | .. |
333| ✗ | __powidf2 | f64 | f64 | f64 | .. |
334| ✗ | __powitf2 | f128 | f128 | f128 | .. |
335| ✗ | __powixf2 | f80 | f80 | f80 | .. |
336| ✓ | __mulhc3 | all4 | f16 | f16 | `(a+ib) * (c+id)` |
337| ✓ | __mulsc3 | all4 | f32 | f32 | .. |
338| ✓ | __muldc3 | all4 | f64 | f64 | .. |
339| ✓ | __multc3 | all4 | f128 | f128 | .. |
340| ✓ | __mulxc3 | all4 | f80 | f80 | .. |
341| ✓ | __divhc3 | all4 | f16 | f16 | `(a+ib) / (c+id)` |
342| ✓ | __divsc3 | all4 | f32 | f32 | .. |
343| ✓ | __divdc3 | all4 | f64 | f64 | .. |
344| ✓ | __divtc3 | all4 | f128 | f128 | .. |
345| ✓ | __divxc3 | all4 | f80 | f80 | .. |
346
347[^unused_rl78]: Unused in LLVM, but used for example by rl78.
348[^libgcc_compat]: Unused in backends and for symbol-level compatibility with libgcc.
349[^noptr_faster]: Operations without pointer and without C struct semantics lead to better optimizations.
350[^perf_addition]: Has better performance than standard method due to 2s complement semantics.
351Not provided by LLVM and libgcc.
352
353Decimal float library routines
283354
284355BID means Binary Integer Decimal encoding, DPD means Densely Packed Decimal encoding.
285356BID should be only chosen for binary data, DPD for decimal data (ASCII, Unicode etc).
286If possible, use BCD instead of DPD to represent numbers not accurately representable
287in binary like the number 0.2.
288
289All routines are TODO.
290
291#### Decimal float Conversion
292
293- __dpd_extendsddd2 // dec32->dec64
294- __bid_extendsddd2 // dec32->dec64
295- __dpd_extendsdtd2 // dec32->dec128
296- __bid_extendsdtd2 // dec32->dec128
297- __dpd_extendddtd2 // dec64->dec128
298- __bid_extendddtd2 // dec64->dec128
299- __dpd_truncddsd2 // dec64->dec32
300- __bid_truncddsd2 // dec64->dec32
301- __dpd_trunctdsd2 // dec128->dec32
302- __bid_trunctdsd2 // dec128->dec32
303- __dpd_trunctddd2 // dec128->dec64
304- __bid_trunctddd2 // dec128->dec64
305
306- __dpd_extendsfdd // float->dec64
307- __bid_extendsfdd // float->dec64
308- __dpd_extendsftd // float->dec128
309- __bid_extendsftd // float->dec128
310- __dpd_extenddftd // double->dec128
311- __bid_extenddftd // double->dec128
312- __dpd_extendxftd // long double->dec128
313- __bid_extendxftd // long double->dec128
314- __dpd_truncdfsd // double->dec32
315- __bid_truncdfsd // double->dec32
316- __dpd_truncxfsd // long double->dec32
317- __bid_truncxfsd // long double->dec32
318- __dpd_trunctfsd // long double->dec32
319- __bid_trunctfsd // long double->dec32
320- __dpd_truncxfdd // long double->dec64
321- __bid_truncxfdd // long double->dec64
322- __dpd_trunctfdd // long double->dec64
323- __bid_trunctfdd // long double->dec64
324
325- __dpd_truncddsf // dec64->float
326- __bid_truncddsf // dec64->float
327- __dpd_trunctdsf // dec128->float
328- __bid_trunctdsf // dec128->float
329- __dpd_extendsddf // dec32->double
330- __bid_extendsddf // dec32->double
331- __dpd_trunctddf // dec128->double
332- __bid_trunctddf // dec128->double
333- __dpd_extendsdxf // dec32->long double
334- __bid_extendsdxf // dec32->long double
335- __dpd_extendddxf // dec64->long double
336- __bid_extendddxf // dec64->long double
337- __dpd_trunctdxf // dec128->long double
338- __bid_trunctdxf // dec128->long double
339- __dpd_extendsdtf // dec32->long double
340- __bid_extendsdtf // dec32->long double
341- __dpd_extendddtf // dec64->long double
342- __bid_extendddtf // dec64->long double
343
344Same size conversion:
345- __dpd_extendsfsd // float->dec32
346- __bid_extendsfsd // float->dec32
347- __dpd_extenddfdd // double->dec64
348- __bid_extenddfdd // double->dec64
349- __dpd_extendtftd //long double->dec128
350- __bid_extendtftd //long double->dec128
351- __dpd_truncsdsf // dec32->float
352- __bid_truncsdsf // dec32->float
353- __dpd_truncdddf // dec64->float
354- __bid_truncdddf // dec64->float
355- __dpd_trunctdtf // dec128->long double
356- __bid_trunctdtf // dec128->long double
357
358- __dpd_fixsdsi // dec32->int
359- __bid_fixsdsi // dec32->int
360- __dpd_fixddsi // dec64->int
361- __bid_fixddsi // dec64->int
362- __dpd_fixtdsi // dec128->int
363- __bid_fixtdsi // dec128->int
364
365- __dpd_fixsddi // dec32->long
366- __bid_fixsddi // dec32->long
367- __dpd_fixdddi // dec64->long
368- __bid_fixdddi // dec64->long
369- __dpd_fixtddi // dec128->long
370- __bid_fixtddi // dec128->long
371
372- __dpd_fixunssdsi // dec32->unsigned int, All negative values become zero.
373- __bid_fixunssdsi // dec32->unsigned int
374- __dpd_fixunsddsi // dec64->unsigned int
375- __bid_fixunsddsi // dec64->unsigned int
376- __dpd_fixunstdsi // dec128->unsigned int
377- __bid_fixunstdsi // dec128->unsigned int
378
379- __dpd_fixunssddi // dec32->unsigned long, All negative values become zero.
380- __bid_fixunssddi // dec32->unsigned long
381- __dpd_fixunsdddi // dec64->unsigned long
382- __bid_fixunsdddi // dec64->unsigned long
383- __dpd_fixunstddi // dec128->unsigned long
384- __bid_fixunstddi // dec128->unsigned long
385
386- __dpd_floatsisd // int->dec32
387- __bid_floatsisd // int->dec32
388- __dpd_floatsidd // int->dec64
389- __bid_floatsidd // int->dec64
390- __dpd_floatsitd // int->dec128
391- __bid_floatsitd // int->dec128
392
393- __dpd_floatdisd // long->dec32
394- __bid_floatdisd // long->dec32
395- __dpd_floatdidd // long->dec64
396- __bid_floatdidd // long->dec64
397- __dpd_floatditd // long->dec128
398- __bid_floatditd // long->dec128
399
400- __dpd_floatunssisd // unsigned int->dec32
401- __bid_floatunssisd // unsigned int->dec32
402- __dpd_floatunssidd // unsigned int->dec64
403- __bid_floatunssidd // unsigned int->dec64
404- __dpd_floatunssitd // unsigned int->dec128
405- __bid_floatunssitd // unsigned int->dec128
406
407- __dpd_floatunsdisd // unsigned long->dec32
408- __bid_floatunsdisd // unsigned long->dec32
409- __dpd_floatunsdidd // unsigned long->dec64
410- __bid_floatunsdidd // unsigned long->dec64
411- __dpd_floatunsditd // unsigned long->dec128
412- __bid_floatunsditd // unsigned long->dec128
413
414#### Decimal float Comparison
415
416All decimal float comparison routines return c_int.
417
418- __dpd_unordsd2 // a,b: dec32, a +-NaN or a +-NaN -> 1(nonzero), else -> 0
419- __bid_unordsd2 // a,b: dec32
420- __dpd_unorddd2 // a,b: dec64
421- __bid_unorddd2 // a,b: dec64
422- __dpd_unordtd2 // a,b: dec128
423- __bid_unordtd2 // a,b: dec128
424
425- __dpd_eqsd2 // a,b: dec32, a!=+-NaN and b!=+-Nan and a==b -> 0, else -> 1(nonzero)
426- __bid_eqsd2 // a,b: dec32
427- __dpd_eqdd2 // a,b: dec64
428- __bid_eqdd2 // a,b: dec64
429- __dpd_eqtd2 // a,b: dec128
430- __bid_eqtd2 // a,b: dec128
431
432- __dpd_nesd2 // a,b: dec32, a==+-NaN or b==+-NaN or a!=b -> 1(nonzero), else -> 0
433- __bid_nesd2 // a,b: dec32
434- __dpd_nedd2 // a,b: dec64
435- __bid_nedd2 // a,b: dec64
436- __dpd_netd2 // a,b: dec128
437- __bid_netd2 // a,b: dec128
438
439- __dpd_gesd2 // a,b: dec32, a!=+-NaN and b!=+-NaN and a>=b -> >=0, else -> <0
440- __bid_gesd2 // a,b: dec32
441- __dpd_gedd2 // a,b: dec64
442- __bid_gedd2 // a,b: dec64
443- __dpd_getd2 // a,b: dec128
444- __bid_getd2 // a,b: dec128
445
446- __dpd_ltsd2 // a,b: dec32, a!=+-NaN and b!=+-NaN and a<b -> <0, else -> >=0
447- __bid_ltsd2 // a,b: dec32
448- __dpd_ltdd2 // a,b: dec64
449- __bid_ltdd2 // a,b: dec64
450- __dpd_lttd2 // a,b: dec128
451- __bid_lttd2 // a,b: dec128
452
453- __dpd_lesd2 // a,b: dec32, a!=+-NaN and b!=+-NaN and a<=b -> <=0, else -> >=0
454- __bid_lesd2 // a,b: dec32
455- __dpd_ledd2 // a,b: dec64
456- __bid_ledd2 // a,b: dec64
457- __dpd_letd2 // a,b: dec128
458- __bid_letd2 // a,b: dec128
459
460- __dpd_gtsd2 // a,b: dec32, a!=+-NaN and b!=+-NaN and a>b -> >0, else -> <=0
461- __bid_gtsd2 // a,b: dec32
462- __dpd_gtdd2 // a,b: dec64
463- __bid_gtdd2 // a,b: dec64
464- __dpd_gttd2 // a,b: dec128
465- __bid_gttd2 // a,b: dec128
466
467#### Decimal float Arithmetic
468
469These numbers include options with routines for +-0 and +-Nan.
470
471- __dpd_addsd3 // a,b: dec32 -> dec32, a + b
472- __bid_addsd3 // a,b: dec32 -> dec32
473- __dpd_adddd3 // a,b: dec64 -> dec64
474- __bid_adddd3 // a,b: dec64 -> dec64
475- __dpd_addtd3 // a,b: dec128-> dec128
476- __bid_addtd3 // a,b: dec128-> dec128
477- __dpd_subsd3 // a,b: dec32, a - b
478- __bid_subsd3 // a,b: dec32 -> dec32
479- __dpd_subdd3 // a,b: dec64 ..
480- __bid_subdd3 // a,b: dec64
481- __dpd_subtd3 // a,b: dec128
482- __bid_subtd3 // a,b: dec128
483- __dpd_mulsd3 // a,b: dec32, a * b
484- __bid_mulsd3 // a,b: dec32 -> dec32
485- __dpd_muldd3 // a,b: dec64 ..
486- __bid_muldd3 // a,b: dec64
487- __dpd_multd3 // a,b: dec128
488- __bid_multd3 // a,b: dec128
489- __dpd_divsd3 // a,b: dec32, a / b
490- __bid_divsd3 // a,b: dec32 -> dec32
491- __dpd_divdd3 // a,b: dec64 ..
492- __bid_divdd3 // a,b: dec64
493- __dpd_divtd3 // a,b: dec128
494- __bid_divtd3 // a,b: dec128
495- __dpd_negsd2 // a,b: dec32, -a
496- __bid_negsd2 // a,b: dec32 -> dec32
497- __dpd_negdd2 // a,b: dec64 ..
498- __bid_negdd2 // a,b: dec64
499- __dpd_negtd2 // a,b: dec128
500- __bid_negtd2 // a,b: dec128
501
502## Fixed-point fractional library routines
503
504TODO
505
506Too 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"
357For example the number 0.2 is not accurately representable in binary data.
358
359| Done | Name | a | b | Out | Comment |
360| ------ | ------------- | --------- | --------- | --------- | ---------------------------- |
361| | | | | | **Decimal Float Conversion** |
362| ✗ | __dpd_extendsddd2 | dec32 | ∅ | dec64 | conversion |
363| ✗ | __bid_extendsddd2 | dec32 | ∅ | dec64 | .. |
364| ✗ | __dpd_extendsdtd2 | dec32 | ∅ | dec128| .. |
365| ✗ | __bid_extendsdtd2 | dec32 | ∅ | dec128| .. |
366| ✗ | __dpd_extendddtd2 | dec64 | ∅ | dec128| .. |
367| ✗ | __bid_extendddtd2 | dec64 | ∅ | dec128| .. |
368| ✗ | __dpd_truncddsd2 | dec64 | ∅ | dec32 | .. |
369| ✗ | __bid_truncddsd2 | dec64 | ∅ | dec32 | .. |
370| ✗ | __dpd_trunctdsd2 | dec128 | ∅ | dec32 | .. |
371| ✗ | __bid_trunctdsd2 | dec128 | ∅ | dec32 | .. |
372| ✗ | __dpd_trunctddd2 | dec128 | ∅ | dec64 | .. |
373| ✗ | __bid_trunctddd2 | dec128 | ∅ | dec64 | .. |
374| ✗ | __dpd_extendsfdd | float | ∅ | dec64 | .. |
375| ✗ | __bid_extendsfdd | float | ∅ | dec64 | .. |
376| ✗ | __dpd_extendsftd | float | ∅ | dec128| .. |
377| ✗ | __bid_extendsftd | float | ∅ | dec128| .. |
378| ✗ | __dpd_extenddftd | double | ∅ | dec128| .. |
379| ✗ | __bid_extenddftd | double | ∅ | dec128| .. |
380| ✗ | __dpd_extendxftd |long double | ∅ | dec128| .. |
381| ✗ | __bid_extendxftd |long double | ∅ | dec128| .. |
382| ✗ | __dpd_truncdfsd | double | ∅ | dec32 | .. |
383| ✗ | __bid_truncdfsd | double | ∅ | dec32 | .. |
384| ✗ | __dpd_truncxfsd |long double | ∅ | dec32 | .. |
385| ✗ | __bid_truncxfsd |long double | ∅ | dec32 | .. |
386| ✗ | __dpd_trunctfsd |long double | ∅ | dec32 | .. |
387| ✗ | __bid_trunctfsd |long double | ∅ | dec32 | .. |
388| ✗ | __dpd_truncxfdd |long double | ∅ | dec64 | .. |
389| ✗ | __bid_truncxfdd |long double | ∅ | dec64 | .. |
390| ✗ | __dpd_trunctfdd |long double | ∅ | dec64 | .. |
391| ✗ | __bid_trunctfdd |long double | ∅ | dec64 | .. |
392| ✗ | __dpd_truncddsf | dec64 | ∅ | float | .. |
393| ✗ | __bid_truncddsf | dec64 | ∅ | float | .. |
394| ✗ | __dpd_trunctdsf | dec128 | ∅ | float | .. |
395| ✗ | __bid_trunctdsf | dec128 | ∅ | float | .. |
396| ✗ | __dpd_extendsddf | dec32 | ∅ | double| .. |
397| ✗ | __bid_extendsddf | dec32 | ∅ | double| .. |
398| ✗ | __dpd_trunctddf | dec128 | ∅ | double| .. |
399| ✗ | __bid_trunctddf | dec128 | ∅ | double| .. |
400| ✗ | __dpd_extendsdxf | dec32 | ∅ |long double| .. |
401| ✗ | __bid_extendsdxf | dec32 | ∅ |long double| .. |
402| ✗ | __dpd_extendddxf | dec64 | ∅ |long double| .. |
403| ✗ | __bid_extendddxf | dec64 | ∅ |long double| .. |
404| ✗ | __dpd_trunctdxf | dec128 | ∅ |long double| .. |
405| ✗ | __bid_trunctdxf | dec128 | ∅ |long double| .. |
406| ✗ | __dpd_extendsdtf | dec32 | ∅ |long double| .. |
407| ✗ | __bid_extendsdtf | dec32 | ∅ |long double| .. |
408| ✗ | __dpd_extendddtf | dec64 | ∅ |long double| .. |
409| ✗ | __bid_extendddtf | dec64 | ∅ |long double| .. |
410| ✗ | __dpd_extendsfsd | float | ∅ | dec32 | same size conversions |
411| ✗ | __bid_extendsfsd | float | ∅ | dec32 | .. |
412| ✗ | __dpd_extenddfdd | double | ∅ | dec64 | .. |
413| ✗ | __bid_extenddfdd | double | ∅ | dec64 | .. |
414| ✗ | __dpd_extendtftd |long double | ∅ | dec128| .. |
415| ✗ | __bid_extendtftd |long double | ∅ | dec128| .. |
416| ✗ | __dpd_truncsdsf | dec32 | ∅ | float | .. |
417| ✗ | __bid_truncsdsf | dec32 | ∅ | float | .. |
418| ✗ | __dpd_truncdddf | dec64 | ∅ | float | conversion |
419| ✗ | __bid_truncdddf | dec64 | ∅ | float | .. |
420| ✗ | __dpd_trunctdtf | dec128 | ∅ |long double| .. |
421| ✗ | __bid_trunctdtf | dec128 | ∅ |long double| .. |
422| ✗ | __dpd_fixsdsi | dec32 | ∅ | int | .. |
423| ✗ | __bid_fixsdsi | dec32 | ∅ | int | .. |
424| ✗ | __dpd_fixddsi | dec64 | ∅ | int | .. |
425| ✗ | __bid_fixddsi | dec64 | ∅ | int | .. |
426| ✗ | __dpd_fixtdsi | dec128 | ∅ | int | .. |
427| ✗ | __bid_fixtdsi | dec128 | ∅ | int | .. |
428| ✗ | __dpd_fixsddi | dec32 | ∅ | long | .. |
429| ✗ | __bid_fixsddi | dec32 | ∅ | long | .. |
430| ✗ | __dpd_fixdddi | dec64 | ∅ | long | .. |
431| ✗ | __bid_fixdddi | dec64 | ∅ | long | .. |
432| ✗ | __dpd_fixtddi | dec128 | ∅ | long | .. |
433| ✗ | __bid_fixtddi | dec128 | ∅ | long | .. |
434| ✗ | __dpd_fixunssdsi | dec32 | ∅ |unsigned int | .. All negative values become zero. |
435| ✗ | __bid_fixunssdsi | dec32 | ∅ |unsigned int | .. |
436| ✗ | __dpd_fixunsddsi | dec64 | ∅ |unsigned int | .. |
437| ✗ | __bid_fixunsddsi | dec64 | ∅ |unsigned int | .. |
438| ✗ | __dpd_fixunstdsi | dec128 | ∅ |unsigned int | .. |
439| ✗ | __bid_fixunstdsi | dec128 | ∅ |unsigned int | .. |
440| ✗ | __dpd_fixunssddi | dec32 | ∅ |unsigned long| .. |
441| ✗ | __bid_fixunssddi | dec32 | ∅ |unsigned long| .. |
442| ✗ | __dpd_fixunsdddi | dec64 | ∅ |unsigned long| .. |
443| ✗ | __bid_fixunsdddi | dec64 | ∅ |unsigned long| .. |
444| ✗ | __dpd_fixunstddi | dec128 | ∅ |unsigned long| .. |
445| ✗ | __bid_fixunstddi | dec128 | ∅ |unsigned long| .. |
446| ✗ | __dpd_floatsisd | int | ∅ | dec32 | .. |
447| ✗ | __bid_floatsisd | int | ∅ | dec32 | .. |
448| ✗ | __dpd_floatsidd | int | ∅ | dec64 | .. |
449| ✗ | __bid_floatsidd | int | ∅ | dec64 | .. |
450| ✗ | __dpd_floatsitd | int | ∅ | dec128 | .. |
451| ✗ | __bid_floatsitd | int | ∅ | dec128 | .. |
452| ✗ | __dpd_floatdisd | long | ∅ | dec32 | .. |
453| ✗ | __bid_floatdisd | long | ∅ | dec32 | .. |
454| ✗ | __dpd_floatdidd | long | ∅ | dec64 | .. |
455| ✗ | __bid_floatdidd | long | ∅ | dec64 | .. |
456| ✗ | __dpd_floatditd | long | ∅ | dec128 | .. |
457| ✗ | __bid_floatditd | long | ∅ | dec128 | .. |
458| ✗ | __dpd_floatunssisd | unsigned int| ∅ | dec32 | .. |
459| ✗ | __bid_floatunssisd | unsigned int| ∅ | dec32 | .. |
460| ✗ | __dpd_floatunssidd | unsigned int| ∅ | dec64 | .. |
461| ✗ | __bid_floatunssidd | unsigned int| ∅ | dec64 | .. |
462| ✗ | __dpd_floatunssitd | unsigned int| ∅ | dec128 | .. |
463| ✗ | __bid_floatunssitd | unsigned int| ∅ | dec128 | .. |
464| ✗ | __dpd_floatunsdisd |unsigned long| ∅ | dec32 | .. |
465| ✗ | __bid_floatunsdisd |unsigned long| ∅ | dec32 | .. |
466| ✗ | __dpd_floatunsdidd |unsigned long| ∅ | dec64 | .. |
467| ✗ | __bid_floatunsdidd |unsigned long| ∅ | dec64 | .. |
468| ✗ | __dpd_floatunsditd |unsigned long| ∅ | dec128 | .. |
469| ✗ | __bid_floatunsditd |unsigned long| ∅ | dec128 | .. |
470| | | | | | **Decimal Float Comparison** |
471| ✗ | __dpd_unordsd2 | dec32 | dec32 | c_int | `a +-NaN or a +-NaN -> 1(nonzero), else -> 0` |
472| ✗ | __bid_unordsd2 | dec32 | dec32 | c_int | .. |
473| ✗ | __dpd_unorddd2 | dec64 | dec64 | c_int | .. |
474| ✗ | __bid_unorddd2 | dec64 | dec64 | c_int | .. |
475| ✗ | __dpd_unordtd2 | dec128 | dec128 | c_int | .. |
476| ✗ | __bid_unordtd2 | dec128 | dec128 | c_int | .. |
477| ✗ | __dpd_eqsd2 | dec32 | dec32 | c_int |`a!=+-NaN and b!=+-Nan and a==b -> 0, else -> 1(nonzero)`|
478| ✗ | __bid_eqsd2 | dec32 | dec32 | c_int | .. |
479| ✗ | __dpd_eqdd2 | dec64 | dec64 | c_int | .. |
480| ✗ | __bid_eqdd2 | dec64 | dec64 | c_int | .. |
481| ✗ | __dpd_eqtd2 | dec128 | dec128 | c_int | .. |
482| ✗ | __bid_eqtd2 | dec128 | dec128 | c_int | .. |
483| ✗ | __dpd_nesd2 | dec32 | dec32 | c_int | `a==+-NaN or b==+-NaN or a!=b -> 1(nonzero), else -> 0` |
484| ✗ | __bid_nesd2 | dec32 | dec32 | c_int | .. |
485| ✗ | __dpd_nedd2 | dec64 | dec64 | c_int | .. |
486| ✗ | __bid_nedd2 | dec64 | dec64 | c_int | .. |
487| ✗ | __dpd_netd2 | dec128 | dec128 | c_int | .. |
488| ✗ | __bid_netd2 | dec128 | dec128 | c_int | .. |
489| ✗ | __dpd_gesd2 | dec32 | dec32 | c_int | `a!=+-NaN and b!=+-NaN and a>=b -> >=0, else -> <0` |
490| ✗ | __bid_gesd2 | dec32 | dec32 | c_int | .. |
491| ✗ | __dpd_gedd2 | dec64 | dec64 | c_int | .. |
492| ✗ | __bid_gedd2 | dec64 | dec64 | c_int | .. |
493| ✗ | __dpd_getd2 | dec128 | dec128 | c_int | .. |
494| ✗ | __bid_getd2 | dec128 | dec128 | c_int | .. |
495| ✗ | __dpd_ltsd2 | dec32 | dec32 | c_int | `a!=+-NaN and b!=+-NaN and a<b -> <0, else -> >=0` |
496| ✗ | __bid_ltsd2 | dec32 | dec32 | c_int | .. |
497| ✗ | __dpd_ltdd2 | dec64 | dec64 | c_int | .. |
498| ✗ | __bid_ltdd2 | dec64 | dec64 | c_int | .. |
499| ✗ | __dpd_lttd2 | dec128 | dec128 | c_int | .. |
500| ✗ | __bid_lttd2 | dec128 | dec128 | c_int | .. |
501| ✗ | __dpd_lesd2 | dec32 | dec32 | c_int | `a!=+-NaN and b!=+-NaN and a<=b -> <=0, else -> >=0` |
502| ✗ | __bid_lesd2 | dec32 | dec32 | c_int | .. |
503| ✗ | __dpd_ledd2 | dec64 | dec64 | c_int | .. |
504| ✗ | __bid_ledd2 | dec64 | dec64 | c_int | .. |
505| ✗ | __dpd_letd2 | dec128 | dec128 | c_int | .. |
506| ✗ | __bid_letd2 | dec128 | dec128 | c_int | .. |
507| ✗ | __dpd_gtsd2 | dec32 | dec32 | c_int | `a!=+-NaN and b!=+-NaN and a>b -> >0, else -> <=0` |
508| ✗ | __bid_gtsd2 | dec32 | dec32 | c_int | .. |
509| ✗ | __dpd_gtdd2 | dec64 | dec64 | c_int | .. |
510| ✗ | __bid_gtdd2 | dec64 | dec64 | c_int | .. |
511| ✗ | __dpd_gttd2 | dec128 | dec128 | c_int | .. |
512| ✗ | __bid_gttd2 | dec128 | dec128 | c_int | .. |
513| | | | | | **Decimal Float Arithmetic**[^options] |
514| ✗ | __dpd_addsd3 | dec32 | dec32 | dec32 |`a + b`|
515| ✗ | __bid_addsd3 | dec32 | dec32 | dec32 | .. |
516| ✗ | __dpd_adddd3 | dec64 | dec64 | dec64 | .. |
517| ✗ | __bid_adddd3 | dec64 | dec64 | dec64 | .. |
518| ✗ | __dpd_addtd3 | dec128 | dec128 | dec128 | .. |
519| ✗ | __bid_addtd3 | dec128 | dec128 | dec128 | .. |
520| ✗ | __dpd_subsd3 | dec32 | dec32 | dec32 |`a - b`|
521| ✗ | __bid_subsd3 | dec32 | dec32 | dec32 | .. |
522| ✗ | __dpd_subdd3 | dec64 | dec64 | dec64 | .. |
523| ✗ | __bid_subdd3 | dec64 | dec64 | dec64 | .. |
524| ✗ | __dpd_subtd3 | dec128 | dec128 | dec128 | .. |
525| ✗ | __bid_subtd3 | dec128 | dec128 | dec128 | .. |
526| ✗ | __dpd_mulsd3 | dec32 | dec32 | dec32 |`a * b`|
527| ✗ | __bid_mulsd3 | dec32 | dec32 | dec32 | .. |
528| ✗ | __dpd_muldd3 | dec64 | dec64 | dec64 | .. |
529| ✗ | __bid_muldd3 | dec64 | dec64 | dec64 | .. |
530| ✗ | __dpd_multd3 | dec128 | dec128 | dec128 | .. |
531| ✗ | __bid_multd3 | dec128 | dec128 | dec128 | .. |
532| ✗ | __dpd_divsd3 | dec32 | dec32 | dec32 |`a / b`|
533| ✗ | __bid_divsd3 | dec32 | dec32 | dec32 | .. |
534| ✗ | __dpd_divdd3 | dec64 | dec64 | dec64 | .. |
535| ✗ | __bid_divdd3 | dec64 | dec64 | dec64 | .. |
536| ✗ | __dpd_divtd3 | dec128 | dec128 | dec128 | .. |
537| ✗ | __bid_divtd3 | dec128 | dec128 | dec128 | .. |
538| ✗ | __dpd_negsd2 | dec32 | dec32 | dec32 | `-a` |
539| ✗ | __bid_negsd2 | dec32 | dec32 | dec32 | .. |
540| ✗ | __dpd_negdd2 | dec64 | dec64 | dec64 | .. |
541| ✗ | __bid_negdd2 | dec64 | dec64 | dec64 | .. |
542| ✗ | __dpd_negtd2 | dec128 | dec128 | dec128 | .. |
543| ✗ | __bid_negtd2 | dec128 | dec128 | dec128 | .. |
544
545[^options]: These numbers include options with routines for +-0 and +-Nan.
546
547Fixed-point fractional library routines
548
549TODO brief explanation + implementation
550
551| Done | Name | a | b | Out | Comment |
552| ------ | ------------- | --------- | --------- | --------- | -------------------------- |
553| | | | | | **Fixed-Point Fractional** |
554
555Further content:
556- aarch64 outline atomics
557- atomics
558- msvc things like _alldiv, _aulldiv, _allrem
559- clear cache
560- tls emulation
561- math routines (cos, sin, tan, ceil, floor, exp, exp2, fabs, log, log10, log2, sincos, sqrt)
562- bcmp
563- ieee float routines (fma, fmax, fmin, fmod, fabs, float rounding, )
564- arm routines (memory routines + memclr [setting to 0], divmod routines and stubs for unwind_cpp)
565- memory routines (memcmp, memcpy, memset, memmove)
566- objective-c __isPlatformVersionAtLeast check
567- stack probe routines
568
569Future work
570
571Arbitrary 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 {
192192 return atomic_load_N(u64, src, model);
193193}
194194
195fn __atomic_load_16(src: *u128, model: i32) callconv(.C) u128 {
196 return atomic_load_N(u128, src, model);
197}
198
195199inline fn atomic_store_N(comptime T: type, dst: *T, value: T, model: i32) void {
196200 _ = model;
197201 if (@sizeOf(T) > largest_atomic_size) {
......@@ -219,6 +223,10 @@ fn __atomic_store_8(dst: *u64, value: u64, model: i32) callconv(.C) void {
219223 return atomic_store_N(u64, dst, value, model);
220224}
221225
226fn __atomic_store_16(dst: *u128, value: u128, model: i32) callconv(.C) void {
227 return atomic_store_N(u128, dst, value, model);
228}
229
222230fn wideUpdate(comptime T: type, ptr: *T, val: T, update: anytype) T {
223231 const WideAtomic = std.meta.Int(.unsigned, smallest_atomic_fetch_exch_size * 8);
224232
......@@ -282,6 +290,10 @@ fn __atomic_exchange_8(ptr: *u64, val: u64, model: i32) callconv(.C) u64 {
282290 return atomic_exchange_N(u64, ptr, val, model);
283291}
284292
293fn __atomic_exchange_16(ptr: *u128, val: u128, model: i32) callconv(.C) u128 {
294 return atomic_exchange_N(u128, ptr, val, model);
295}
296
285297inline fn atomic_compare_exchange_N(
286298 comptime T: type,
287299 ptr: *T,
......@@ -327,6 +339,10 @@ fn __atomic_compare_exchange_8(ptr: *u64, expected: *u64, desired: u64, success:
327339 return atomic_compare_exchange_N(u64, ptr, expected, desired, success, failure);
328340}
329341
342fn __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
330346inline fn fetch_op_N(comptime T: type, comptime op: std.builtin.AtomicRmwOp, ptr: *T, val: T, model: i32) T {
331347 _ = model;
332348 const Updater = struct {
......@@ -338,6 +354,8 @@ inline fn fetch_op_N(comptime T: type, comptime op: std.builtin.AtomicRmwOp, ptr
338354 .Nand => ~(old & new),
339355 .Or => old | new,
340356 .Xor => old ^ new,
357 .Max => @max(old, new),
358 .Min => @min(old, new),
341359 else => @compileError("unsupported atomic op"),
342360 };
343361 }
......@@ -374,6 +392,10 @@ fn __atomic_fetch_add_8(ptr: *u64, val: u64, model: i32) callconv(.C) u64 {
374392 return fetch_op_N(u64, .Add, ptr, val, model);
375393}
376394
395fn __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
377399fn __atomic_fetch_sub_1(ptr: *u8, val: u8, model: i32) callconv(.C) u8 {
378400 return fetch_op_N(u8, .Sub, ptr, val, model);
379401}
......@@ -390,6 +412,10 @@ fn __atomic_fetch_sub_8(ptr: *u64, val: u64, model: i32) callconv(.C) u64 {
390412 return fetch_op_N(u64, .Sub, ptr, val, model);
391413}
392414
415fn __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
393419fn __atomic_fetch_and_1(ptr: *u8, val: u8, model: i32) callconv(.C) u8 {
394420 return fetch_op_N(u8, .And, ptr, val, model);
395421}
......@@ -406,6 +432,10 @@ fn __atomic_fetch_and_8(ptr: *u64, val: u64, model: i32) callconv(.C) u64 {
406432 return fetch_op_N(u64, .And, ptr, val, model);
407433}
408434
435fn __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
409439fn __atomic_fetch_or_1(ptr: *u8, val: u8, model: i32) callconv(.C) u8 {
410440 return fetch_op_N(u8, .Or, ptr, val, model);
411441}
......@@ -422,6 +452,10 @@ fn __atomic_fetch_or_8(ptr: *u64, val: u64, model: i32) callconv(.C) u64 {
422452 return fetch_op_N(u64, .Or, ptr, val, model);
423453}
424454
455fn __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
425459fn __atomic_fetch_xor_1(ptr: *u8, val: u8, model: i32) callconv(.C) u8 {
426460 return fetch_op_N(u8, .Xor, ptr, val, model);
427461}
......@@ -438,6 +472,10 @@ fn __atomic_fetch_xor_8(ptr: *u64, val: u64, model: i32) callconv(.C) u64 {
438472 return fetch_op_N(u64, .Xor, ptr, val, model);
439473}
440474
475fn __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
441479fn __atomic_fetch_nand_1(ptr: *u8, val: u8, model: i32) callconv(.C) u8 {
442480 return fetch_op_N(u8, .Nand, ptr, val, model);
443481}
......@@ -454,6 +492,50 @@ fn __atomic_fetch_nand_8(ptr: *u64, val: u64, model: i32) callconv(.C) u64 {
454492 return fetch_op_N(u64, .Nand, ptr, val, model);
455493}
456494
495fn __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
499fn __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
503fn __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
507fn __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
511fn __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
515fn __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
519fn __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
523fn __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
527fn __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
531fn __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
535fn __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
457539comptime {
458540 if (supports_atomic_ops and builtin.object_format != .c) {
459541 @export(__atomic_load, .{ .name = "__atomic_load", .linkage = linkage, .visibility = visibility });
......@@ -465,50 +547,72 @@ comptime {
465547 @export(__atomic_fetch_add_2, .{ .name = "__atomic_fetch_add_2", .linkage = linkage, .visibility = visibility });
466548 @export(__atomic_fetch_add_4, .{ .name = "__atomic_fetch_add_4", .linkage = linkage, .visibility = visibility });
467549 @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 });
468551
469552 @export(__atomic_fetch_sub_1, .{ .name = "__atomic_fetch_sub_1", .linkage = linkage, .visibility = visibility });
470553 @export(__atomic_fetch_sub_2, .{ .name = "__atomic_fetch_sub_2", .linkage = linkage, .visibility = visibility });
471554 @export(__atomic_fetch_sub_4, .{ .name = "__atomic_fetch_sub_4", .linkage = linkage, .visibility = visibility });
472555 @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 });
473557
474558 @export(__atomic_fetch_and_1, .{ .name = "__atomic_fetch_and_1", .linkage = linkage, .visibility = visibility });
475559 @export(__atomic_fetch_and_2, .{ .name = "__atomic_fetch_and_2", .linkage = linkage, .visibility = visibility });
476560 @export(__atomic_fetch_and_4, .{ .name = "__atomic_fetch_and_4", .linkage = linkage, .visibility = visibility });
477561 @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 });
478563
479564 @export(__atomic_fetch_or_1, .{ .name = "__atomic_fetch_or_1", .linkage = linkage, .visibility = visibility });
480565 @export(__atomic_fetch_or_2, .{ .name = "__atomic_fetch_or_2", .linkage = linkage, .visibility = visibility });
481566 @export(__atomic_fetch_or_4, .{ .name = "__atomic_fetch_or_4", .linkage = linkage, .visibility = visibility });
482567 @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 });
483569
484570 @export(__atomic_fetch_xor_1, .{ .name = "__atomic_fetch_xor_1", .linkage = linkage, .visibility = visibility });
485571 @export(__atomic_fetch_xor_2, .{ .name = "__atomic_fetch_xor_2", .linkage = linkage, .visibility = visibility });
486572 @export(__atomic_fetch_xor_4, .{ .name = "__atomic_fetch_xor_4", .linkage = linkage, .visibility = visibility });
487573 @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 });
488575
489576 @export(__atomic_fetch_nand_1, .{ .name = "__atomic_fetch_nand_1", .linkage = linkage, .visibility = visibility });
490577 @export(__atomic_fetch_nand_2, .{ .name = "__atomic_fetch_nand_2", .linkage = linkage, .visibility = visibility });
491578 @export(__atomic_fetch_nand_4, .{ .name = "__atomic_fetch_nand_4", .linkage = linkage, .visibility = visibility });
492579 @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 });
493593
494594 @export(__atomic_load_1, .{ .name = "__atomic_load_1", .linkage = linkage, .visibility = visibility });
495595 @export(__atomic_load_2, .{ .name = "__atomic_load_2", .linkage = linkage, .visibility = visibility });
496596 @export(__atomic_load_4, .{ .name = "__atomic_load_4", .linkage = linkage, .visibility = visibility });
497597 @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 });
498599
499600 @export(__atomic_store_1, .{ .name = "__atomic_store_1", .linkage = linkage, .visibility = visibility });
500601 @export(__atomic_store_2, .{ .name = "__atomic_store_2", .linkage = linkage, .visibility = visibility });
501602 @export(__atomic_store_4, .{ .name = "__atomic_store_4", .linkage = linkage, .visibility = visibility });
502603 @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 });
503605
504606 @export(__atomic_exchange_1, .{ .name = "__atomic_exchange_1", .linkage = linkage, .visibility = visibility });
505607 @export(__atomic_exchange_2, .{ .name = "__atomic_exchange_2", .linkage = linkage, .visibility = visibility });
506608 @export(__atomic_exchange_4, .{ .name = "__atomic_exchange_4", .linkage = linkage, .visibility = visibility });
507609 @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 });
508611
509612 @export(__atomic_compare_exchange_1, .{ .name = "__atomic_compare_exchange_1", .linkage = linkage, .visibility = visibility });
510613 @export(__atomic_compare_exchange_2, .{ .name = "__atomic_compare_exchange_2", .linkage = linkage, .visibility = visibility });
511614 @export(__atomic_compare_exchange_4, .{ .name = "__atomic_compare_exchange_4", .linkage = linkage, .visibility = visibility });
512615 @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 });
513617 }
514618}
lib/docs/main.js+4-1
......@@ -1354,6 +1354,10 @@ const NAV_MODES = {
13541354 payloadHtml += "ptrCast";
13551355 break;
13561356 }
1357 case "qual_cast": {
1358 payloadHtml += "qualCast";
1359 break;
1360 }
13571361 case "truncate": {
13581362 payloadHtml += "truncate";
13591363 break;
......@@ -3158,7 +3162,6 @@ const NAV_MODES = {
31583162 canonTypeDecls = new Array(zigAnalysis.types.length);
31593163
31603164 for (let pkgI = 0; pkgI < zigAnalysis.packages.length; pkgI += 1) {
3161 if (pkgI === zigAnalysis.rootPkg && rootIsStd) continue;
31623165 let pkg = zigAnalysis.packages[pkgI];
31633166 let pkgNames = canonPkgPaths[pkgI];
31643167 if (pkgNames === undefined) continue;
lib/init-exe/build.zig+43-10
......@@ -1,34 +1,67 @@
11const std = @import("std");
22
3pub 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.
6pub fn build(b: *std.Build) void {
47 // Standard target options allows the person running `zig build` to choose
58 // what target to build for. Here we do not override the defaults, which
69 // means any target is allowed, and the default is native. Other options
710 // for restricting supported target set are available.
811 const target = b.standardTargetOptions(.{});
912
10 // Standard release options allow the person running `zig build` to select
11 // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall.
12 const mode = b.standardReleaseOptions();
13 // Standard optimization options allow the person running `zig build` to select
14 // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not
15 // set a preferred release mode, allowing the user to decide how to optimize.
16 const optimize = b.standardOptimizeOption(.{});
1317
14 const exe = b.addExecutable("$", "src/main.zig");
15 exe.setTarget(target);
16 exe.setBuildMode(mode);
18 const exe = b.addExecutable(.{
19 .name = "$",
20 // In this case the main source file is merely a path, however, in more
21 // complicated build scripts, this could be a generated file.
22 .root_source_file = .{ .path = "src/main.zig" },
23 .target = target,
24 .optimize = optimize,
25 });
26
27 // This declares intent for the executable to be installed into the
28 // standard location when the user invokes the "install" step (the default
29 // step when running `zig build`).
1730 exe.install();
1831
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.
1935 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.
2041 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`
2145 if (b.args) |args| {
2246 run_cmd.addArgs(args);
2347 }
2448
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".
2552 const run_step = b.step("run", "Run the app");
2653 run_step.dependOn(&run_cmd.step);
2754
28 const exe_tests = b.addTest("src/main.zig");
29 exe_tests.setTarget(target);
30 exe_tests.setBuildMode(mode);
55 // Creates a step for unit testing.
56 const exe_tests = b.addTest(.{
57 .root_source_file = .{ .path = "src/main.zig" },
58 .target = target,
59 .optimize = optimize,
60 });
3161
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.
3265 const test_step = b.step("test", "Run unit tests");
3366 test_step.dependOn(&exe_tests.step);
3467}
lib/init-lib/build.zig+35-8
......@@ -1,17 +1,44 @@
11const std = @import("std");
22
3pub fn build(b: *std.build.Builder) void {
4 // Standard release options allow the person running `zig build` to select
5 // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall.
6 const mode = b.standardReleaseOptions();
3// Although this function looks imperative, note that its job is to
4// declaratively construct a build graph that will be executed by an external
5// runner.
6pub 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(.{});
712
8 const lib = b.addStaticLibrary("$", "src/main.zig");
9 lib.setBuildMode(mode);
13 // Standard optimization options allow the person running `zig build` to select
14 // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not
15 // set a preferred release mode, allowing the user to decide how to optimize.
16 const optimize = b.standardOptimizeOption(.{});
17
18 const lib = b.addStaticLibrary(.{
19 .name = "$",
20 // In this case the main source file is merely a path, however, in more
21 // complicated build scripts, this could be a generated file.
22 .root_source_file = .{ .path = "src/main.zig" },
23 .target = target,
24 .optimize = optimize,
25 });
26
27 // This declares intent for the library to be installed into the standard
28 // location when the user invokes the "install" step (the default step when
29 // running `zig build`).
1030 lib.install();
1131
12 const main_tests = b.addTest("src/main.zig");
13 main_tests.setBuildMode(mode);
32 // Creates a step for unit testing.
33 const main_tests = b.addTest(.{
34 .root_source_file = .{ .path = "src/main.zig" },
35 .target = target,
36 .optimize = optimize,
37 });
1438
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".
1542 const test_step = b.step("test", "Run library tests");
1643 test_step.dependOn(&main_tests.step);
1744}
lib/libc/mingw/misc/strtoimax.c+1-4
......@@ -31,10 +31,7 @@
3131#define valid(n, b) ((n) >= 0 && (n) < (b))
3232
3333intmax_t
34strtoimax(nptr, endptr, base)
35 register const char * __restrict__ nptr;
36 char ** __restrict__ endptr;
37 register int base;
34strtoimax(const char * __restrict__ nptr, char ** __restrict__ endptr, int base)
3835 {
3936 register uintmax_t accum; /* accumulates converted value */
4037 register int n; /* numeral from digit character */
lib/libc/mingw/misc/strtoumax.c+1-4
......@@ -31,10 +31,7 @@
3131#define valid(n, b) ((n) >= 0 && (n) < (b))
3232
3333uintmax_t
34strtoumax(nptr, endptr, base)
35 register const char * __restrict__ nptr;
36 char ** __restrict__ endptr;
37 register int base;
34strtoumax(const char * __restrict__ nptr, char ** __restrict__ endptr, int base)
3835 {
3936 register uintmax_t accum; /* accumulates converted value */
4037 register uintmax_t next; /* for computing next value of accum */
lib/libc/mingw/misc/wcstoimax.c+1-4
......@@ -33,10 +33,7 @@
3333#define valid(n, b) ((n) >= 0 && (n) < (b))
3434
3535intmax_t
36wcstoimax(nptr, endptr, base)
37 register const wchar_t * __restrict__ nptr;
38 wchar_t ** __restrict__ endptr;
39 register int base;
36wcstoimax(const wchar_t * __restrict__ nptr, wchar_t ** __restrict__ endptr, int base)
4037 {
4138 register uintmax_t accum; /* accumulates converted value */
4239 register int n; /* numeral from digit character */
lib/libc/mingw/misc/wcstoumax.c+1-4
......@@ -33,10 +33,7 @@
3333#define valid(n, b) ((n) >= 0 && (n) < (b))
3434
3535uintmax_t
36wcstoumax(nptr, endptr, base)
37 register const wchar_t * __restrict__ nptr;
38 wchar_t ** __restrict__ endptr;
39 register int base;
36wcstoumax(const wchar_t * __restrict__ nptr, wchar_t ** __restrict__ endptr, int base)
4037 {
4138 register uintmax_t accum; /* accumulates converted value */
4239 register uintmax_t next; /* for computing next value of accum */
lib/std/Build.zig created+1780
......@@ -0,0 +1,1780 @@
1const std = @import("std.zig");
2const builtin = @import("builtin");
3const io = std.io;
4const fs = std.fs;
5const mem = std.mem;
6const debug = std.debug;
7const panic = std.debug.panic;
8const assert = debug.assert;
9const log = std.log;
10const ArrayList = std.ArrayList;
11const StringHashMap = std.StringHashMap;
12const Allocator = mem.Allocator;
13const process = std.process;
14const EnvMap = std.process.EnvMap;
15const fmt_lib = std.fmt;
16const File = std.fs.File;
17const CrossTarget = std.zig.CrossTarget;
18const NativeTargetInfo = std.zig.system.NativeTargetInfo;
19const Sha256 = std.crypto.hash.sha2.Sha256;
20const Build = @This();
21
22/// deprecated: use `CompileStep`.
23pub const LibExeObjStep = CompileStep;
24/// deprecated: use `Build`.
25pub const Builder = Build;
26/// deprecated: use `InstallDirStep.Options`
27pub const InstallDirectoryOptions = InstallDirStep.Options;
28
29pub const Step = @import("Build/Step.zig");
30pub const CheckFileStep = @import("Build/CheckFileStep.zig");
31pub const CheckObjectStep = @import("Build/CheckObjectStep.zig");
32pub const ConfigHeaderStep = @import("Build/ConfigHeaderStep.zig");
33pub const EmulatableRunStep = @import("Build/EmulatableRunStep.zig");
34pub const FmtStep = @import("Build/FmtStep.zig");
35pub const InstallArtifactStep = @import("Build/InstallArtifactStep.zig");
36pub const InstallDirStep = @import("Build/InstallDirStep.zig");
37pub const InstallFileStep = @import("Build/InstallFileStep.zig");
38pub const InstallRawStep = @import("Build/InstallRawStep.zig");
39pub const CompileStep = @import("Build/CompileStep.zig");
40pub const LogStep = @import("Build/LogStep.zig");
41pub const OptionsStep = @import("Build/OptionsStep.zig");
42pub const RemoveDirStep = @import("Build/RemoveDirStep.zig");
43pub const RunStep = @import("Build/RunStep.zig");
44pub const TranslateCStep = @import("Build/TranslateCStep.zig");
45pub const WriteFileStep = @import("Build/WriteFileStep.zig");
46
47install_tls: TopLevelStep,
48uninstall_tls: TopLevelStep,
49allocator: Allocator,
50user_input_options: UserInputOptionsMap,
51available_options_map: AvailableOptionsMap,
52available_options_list: ArrayList(AvailableOption),
53verbose: bool,
54verbose_link: bool,
55verbose_cc: bool,
56verbose_air: bool,
57verbose_llvm_ir: bool,
58verbose_cimport: bool,
59verbose_llvm_cpu_features: bool,
60/// The purpose of executing the command is for a human to read compile errors from the terminal
61prominent_compile_errors: bool,
62color: enum { auto, on, off } = .auto,
63reference_trace: ?u32 = null,
64invalid_user_input: bool,
65zig_exe: []const u8,
66default_step: *Step,
67env_map: *EnvMap,
68top_level_steps: ArrayList(*TopLevelStep),
69install_prefix: []const u8,
70dest_dir: ?[]const u8,
71lib_dir: []const u8,
72exe_dir: []const u8,
73h_dir: []const u8,
74install_path: []const u8,
75sysroot: ?[]const u8 = null,
76search_prefixes: ArrayList([]const u8),
77libc_file: ?[]const u8 = null,
78installed_files: ArrayList(InstalledFile),
79/// Path to the directory containing build.zig.
80build_root: []const u8,
81cache_root: []const u8,
82global_cache_root: []const u8,
83/// zig lib dir
84override_lib_dir: ?[]const u8,
85vcpkg_root: VcpkgRoot = .unattempted,
86pkg_config_pkg_list: ?(PkgConfigError![]const PkgConfigPkg) = null,
87args: ?[][]const u8 = null,
88debug_log_scopes: []const []const u8 = &.{},
89debug_compile_errors: bool = false,
90
91/// Experimental. Use system Darling installation to run cross compiled macOS build artifacts.
92enable_darling: bool = false,
93/// Use system QEMU installation to run cross compiled foreign architecture build artifacts.
94enable_qemu: bool = false,
95/// Darwin. Use Rosetta to run x86_64 macOS build artifacts on arm64 macOS.
96enable_rosetta: bool = false,
97/// Use system Wasmtime installation to run cross compiled wasm/wasi build artifacts.
98enable_wasmtime: bool = false,
99/// Use system Wine installation to run cross compiled Windows build artifacts.
100enable_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`.
105glibc_runtimes_dir: ?[]const u8 = null,
106
107/// Information about the native target. Computed before build() is invoked.
108host: NativeTargetInfo,
109
110dep_prefix: []const u8 = "",
111
112pub const ExecError = error{
113 ReadFailure,
114 ExitCodeFailure,
115 ProcessTerminated,
116 ExecNotSupported,
117} || std.ChildProcess.SpawnError;
118
119pub const PkgConfigError = error{
120 PkgConfigCrashed,
121 PkgConfigFailed,
122 PkgConfigNotInstalled,
123 PkgConfigInvalidOutput,
124};
125
126pub const PkgConfigPkg = struct {
127 name: []const u8,
128 desc: []const u8,
129};
130
131pub const CStd = enum {
132 C89,
133 C99,
134 C11,
135};
136
137const UserInputOptionsMap = StringHashMap(UserInputOption);
138const AvailableOptionsMap = StringHashMap(AvailableOption);
139
140const 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
148const UserInputOption = struct {
149 name: []const u8,
150 value: UserValue,
151 used: bool,
152};
153
154const UserValue = union(enum) {
155 flag: void,
156 scalar: []const u8,
157 list: ArrayList([]const u8),
158 map: StringHashMap(*const UserValue),
159};
160
161const TypeId = enum {
162 bool,
163 int,
164 float,
165 @"enum",
166 string,
167 list,
168};
169
170const TopLevelStep = struct {
171 pub const base_id = .top_level;
172
173 step: Step,
174 description: []const u8,
175};
176
177pub const DirList = struct {
178 lib_dir: ?[]const u8 = null,
179 exe_dir: ?[]const u8 = null,
180 include_dir: ?[]const u8 = null,
181};
182
183pub 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
242fn 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
253fn 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
315fn 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
382pub 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.
389pub 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
423pub fn addOptions(self: *Build) *OptionsStep {
424 return OptionsStep.create(self);
425}
426
427pub 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
436pub 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
448pub const ObjectOptions = struct {
449 name: []const u8,
450 root_source_file: ?FileSource = null,
451 target: CrossTarget,
452 optimize: std.builtin.Mode,
453};
454
455pub 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
465pub 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
473pub 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
485pub 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
493pub 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
505pub 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
514pub 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
524pub const AssemblyOptions = struct {
525 name: []const u8,
526 source_file: FileSource,
527 target: CrossTarget,
528 optimize: std.builtin.Mode,
529};
530
531pub 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`.
547pub 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
554pub 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.
566pub 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.
571pub 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.
580pub 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.
592pub 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
609pub 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
615pub 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
621pub 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
628pub 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
634pub fn addFmt(self: *Build, paths: []const []const u8) *FmtStep {
635 return FmtStep.create(self, paths);
636}
637
638pub fn addTranslateC(self: *Build, options: TranslateCStep.Options) *TranslateCStep {
639 return TranslateCStep.create(self, options);
640}
641
642pub 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
662pub fn getInstallStep(self: *Build) *Step {
663 return &self.install_tls.step;
664}
665
666pub fn getUninstallStep(self: *Build) *Step {
667 return &self.uninstall_tls.step;
668}
669
670fn 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
685fn 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
706fn 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
716pub 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
850pub 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
860pub const StandardOptimizeOptionOptions = struct {
861 preferred_optimize_mode: ?std.builtin.Mode = null,
862};
863
864pub 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
880pub 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.
887pub 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
1018pub 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
1066pub 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
1093fn 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
1107fn markInvalidUserInput(self: *Build) void {
1108 self.invalid_user_input = true;
1109}
1110
1111pub 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
1124pub fn spawnChild(self: *Build, argv: []const []const u8) !void {
1125 return self.spawnChildEnvMap(null, self.env_map, argv);
1126}
1127
1128fn 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
1136pub 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
1170pub 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
1177pub fn installArtifact(self: *Build, artifact: *CompileStep) void {
1178 self.getInstallStep().dependOn(&self.addInstallArtifact(artifact).step);
1179}
1180
1181pub fn addInstallArtifact(self: *Build, artifact: *CompileStep) *InstallArtifactStep {
1182 return InstallArtifactStep.create(self, artifact);
1183}
1184
1185///`dest_rel_path` is relative to prefix path
1186pub 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
1190pub 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
1195pub 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
1200pub 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
1205pub 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
1212pub 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
1217pub 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
1222pub 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
1226pub 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
1230pub 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
1234pub 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
1248pub 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
1254pub 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
1262pub 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
1274pub 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
1291pub 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
1295pub fn pathJoin(self: *Build, paths: []const []const u8) []u8 {
1296 return fs.path.join(self.allocator, paths) catch @panic("OOM");
1297}
1298
1299pub 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
1303pub 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
1349pub 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
1390pub 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
1439pub fn exec(self: *Build, argv: []const []const u8) ![]u8 {
1440 return self.execFromStep(argv, null);
1441}
1442
1443pub fn addSearchPrefix(self: *Build, search_prefix: []const u8) void {
1444 self.search_prefixes.append(self.dupePath(search_prefix)) catch @panic("OOM");
1445}
1446
1447pub 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
1462pub 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
1484pub 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
1504fn 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
1523pub 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
1531test "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
1551pub 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.
1559pub 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///
1577pub 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.
1630pub 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
1643pub const VcpkgRoot = union(VcpkgRootStatus) {
1644 unattempted: void,
1645 not_found: void,
1646 found: []const u8,
1647};
1648
1649pub const VcpkgRootStatus = enum {
1650 unattempted,
1651 not_found,
1652 found,
1653};
1654
1655pub 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
1675pub 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
1688pub 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
1716test "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
1764test {
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 @@
1const std = @import("../std.zig");
2const Step = std.Build.Step;
3const fs = std.fs;
4const mem = std.mem;
5
6const CheckFileStep = @This();
7
8pub const base_id = .check_file;
9
10step: Step,
11builder: *std.Build,
12expected_matches: []const []const u8,
13source: std.Build.FileSource,
14max_bytes: usize = 20 * 1024 * 1024,
15
16pub 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
32fn 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 @@
1const std = @import("../std.zig");
2const assert = std.debug.assert;
3const fs = std.fs;
4const macho = std.macho;
5const math = std.math;
6const mem = std.mem;
7const testing = std.testing;
8
9const CheckObjectStep = @This();
10
11const Allocator = mem.Allocator;
12const Step = std.Build.Step;
13const EmulatableRunStep = std.Build.EmulatableRunStep;
14
15pub const base_id = .check_object;
16
17step: Step,
18builder: *std.Build,
19source: std.Build.FileSource,
20max_bytes: usize = 20 * 1024 * 1024,
21checks: std.ArrayList(Check),
22dump_symtab: bool = false,
23obj_format: std.Target.ObjectFormat,
24
25pub 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.
41pub 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 +`.
63const 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
193const 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
216const 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.
251pub 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.
259pub 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.
268pub 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.
277pub 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.
289pub 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
299fn 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
392const Opts = struct {
393 gpa: ?Allocator = null,
394 dump_symtab: bool = false,
395};
396
397const 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
681const 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 @@
1const builtin = @import("builtin");
2const std = @import("../std.zig");
3const mem = std.mem;
4const log = std.log;
5const fs = std.fs;
6const assert = std.debug.assert;
7const panic = std.debug.panic;
8const ArrayList = std.ArrayList;
9const StringHashMap = std.StringHashMap;
10const Sha256 = std.crypto.hash.sha2.Sha256;
11const Allocator = mem.Allocator;
12const Step = std.Build.Step;
13const CrossTarget = std.zig.CrossTarget;
14const NativeTargetInfo = std.zig.system.NativeTargetInfo;
15const FileSource = std.Build.FileSource;
16const PkgConfigPkg = std.Build.PkgConfigPkg;
17const PkgConfigError = std.Build.PkgConfigError;
18const ExecError = std.Build.ExecError;
19const Pkg = std.Build.Pkg;
20const VcpkgRoot = std.Build.VcpkgRoot;
21const InstallDir = std.Build.InstallDir;
22const InstallArtifactStep = std.Build.InstallArtifactStep;
23const GeneratedFile = std.Build.GeneratedFile;
24const InstallRawStep = std.Build.InstallRawStep;
25const EmulatableRunStep = std.Build.EmulatableRunStep;
26const CheckObjectStep = std.Build.CheckObjectStep;
27const RunStep = std.Build.RunStep;
28const OptionsStep = std.Build.OptionsStep;
29const ConfigHeaderStep = std.Build.ConfigHeaderStep;
30const CompileStep = @This();
31
32pub const base_id: Step.Id = .compile;
33
34step: Step,
35builder: *std.Build,
36name: []const u8,
37target: CrossTarget,
38target_info: NativeTargetInfo,
39optimize: std.builtin.Mode,
40linker_script: ?FileSource = null,
41version_script: ?[]const u8 = null,
42out_filename: []const u8,
43linkage: ?Linkage = null,
44version: ?std.builtin.Version,
45kind: Kind,
46major_only_filename: ?[]const u8,
47name_only_filename: ?[]const u8,
48strip: ?bool,
49unwind_tables: ?bool,
50// keep in sync with src/link.zig:CompressDebugSections
51compress_debug_sections: enum { none, zlib } = .none,
52lib_paths: ArrayList([]const u8),
53rpaths: ArrayList([]const u8),
54framework_dirs: ArrayList([]const u8),
55frameworks: StringHashMap(FrameworkLinkInfo),
56verbose_link: bool,
57verbose_cc: bool,
58emit_analysis: EmitOption = .default,
59emit_asm: EmitOption = .default,
60emit_bin: EmitOption = .default,
61emit_docs: EmitOption = .default,
62emit_implib: EmitOption = .default,
63emit_llvm_bc: EmitOption = .default,
64emit_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.
67emit_h: bool = false,
68bundle_compiler_rt: ?bool = null,
69single_threaded: ?bool = null,
70stack_protector: ?bool = null,
71disable_stack_probing: bool,
72disable_sanitize_c: bool,
73sanitize_thread: bool,
74rdynamic: bool,
75import_memory: bool = false,
76/// For WebAssembly targets, this will allow for undefined symbols to
77/// be imported from the host environment.
78import_symbols: bool = false,
79import_table: bool = false,
80export_table: bool = false,
81initial_memory: ?u64 = null,
82max_memory: ?u64 = null,
83shared_memory: bool = false,
84global_base: ?u64 = null,
85c_std: std.Build.CStd,
86override_lib_dir: ?[]const u8,
87main_pkg_path: ?[]const u8,
88exec_cmd_args: ?[]const ?[]const u8,
89name_prefix: []const u8,
90filter: ?[]const u8,
91test_evented_io: bool = false,
92test_runner: ?[]const u8,
93code_model: std.builtin.CodeModel = .default,
94wasi_exec_model: ?std.builtin.WasiExecModel = null,
95/// Symbols to be exported when compiling to wasm
96export_symbol_names: []const []const u8 = &.{},
97
98root_src: ?FileSource,
99out_h_filename: []const u8,
100out_lib_filename: []const u8,
101out_pdb_filename: []const u8,
102packages: ArrayList(Pkg),
103
104object_src: []const u8,
105
106link_objects: ArrayList(LinkObject),
107include_dirs: ArrayList(IncludeDir),
108c_macros: ArrayList([]const u8),
109installed_headers: ArrayList(*Step),
110output_dir: ?[]const u8,
111is_linking_libc: bool = false,
112is_linking_libcpp: bool = false,
113vcpkg_bin_path: ?[]const u8 = null,
114
115/// This may be set in order to override the default install directory
116override_dest_dir: ?InstallDir,
117installed_path: ?[]const u8,
118install_step: ?*InstallArtifactStep,
119
120/// Base address for an executable image.
121image_base: ?u64 = null,
122
123libc_file: ?FileSource = null,
124
125valgrind_support: ?bool = null,
126each_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.
132build_id: ?bool = null,
133
134/// Create a .eh_frame_hdr section and a PT_GNU_EH_FRAME segment in the ELF
135/// file.
136link_eh_frame_hdr: bool = false,
137link_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.
141link_function_sections: bool = false,
142
143/// Remove functions and data that are unreachable by the entry point or
144/// exported symbols.
145link_gc_sections: ?bool = null,
146
147linker_allow_shlib_undefined: ?bool = null,
148
149/// Permit read-only relocations in read-only segments. Disallowed by default.
150link_z_notext: bool = false,
151
152/// Force all relocations to be read-only after processing.
153link_z_relro: bool = true,
154
155/// Allow relocations to be lazily processed after load.
156link_z_lazy: bool = false,
157
158/// Common page size
159link_z_common_page_size: ?u64 = null,
160
161/// Maximum page size
162link_z_max_page_size: ?u64 = null,
163
164/// (Darwin) Install name for the dylib
165install_name: ?[]const u8 = null,
166
167/// (Darwin) Path to entitlements file
168entitlements: ?[]const u8 = null,
169
170/// (Darwin) Size of the pagezero segment.
171pagezero_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.
178search_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.
182headerpad_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.
186headerpad_max_install_names: bool = false,
187
188/// (Darwin) Remove dylibs that are unreachable by the entry point or exported symbols.
189dead_strip_dylibs: bool = false,
190
191/// Position Independent Code
192force_pic: ?bool = null,
193
194/// Position Independent Executable
195pie: ?bool = null,
196
197red_zone: ?bool = null,
198
199omit_frame_pointer: ?bool = null,
200dll_export_fns: ?bool = null,
201
202subsystem: ?std.Target.SubSystem = null,
203
204entry_symbol_name: ?[]const u8 = null,
205
206/// Overrides the default stack size
207stack_size: ?u64 = null,
208
209want_lto: ?bool = null,
210use_llvm: ?bool = null,
211use_lld: ?bool = null,
212
213output_path_source: GeneratedFile,
214output_lib_path_source: GeneratedFile,
215output_h_path_source: GeneratedFile,
216output_pdb_path_source: GeneratedFile,
217
218pub const CSourceFiles = struct {
219 files: []const []const u8,
220 flags: []const []const u8,
221};
222
223pub 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
235pub 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
244pub 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
260const FrameworkLinkInfo = struct {
261 needed: bool = false,
262 weak: bool = false,
263};
264
265pub 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
272pub 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
282pub const Kind = enum {
283 exe,
284 lib,
285 obj,
286 @"test",
287 test_exe,
288};
289
290pub const Linkage = enum { dynamic, static };
291
292pub 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
308pub 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
374fn 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
427pub fn setOutputDir(self: *CompileStep, dir: []const u8) void {
428 self.output_dir = self.builder.dupePath(dir);
429}
430
431pub fn install(self: *CompileStep) void {
432 self.builder.installArtifact(self);
433}
434
435pub fn installRaw(self: *CompileStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep {
436 return self.builder.installRaw(self, dest_filename, options);
437}
438
439pub 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
445pub 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
457pub 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
466pub 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`.
491pub 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.
515pub 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
525pub fn checkObject(self: *CompileStep, obj_format: std.Target.ObjectFormat) *CheckObjectStep {
526 return CheckObjectStep.create(self.builder, self.getOutputSource(), obj_format);
527}
528
529pub fn setLinkerScriptPath(self: *CompileStep, source: FileSource) void {
530 self.linker_script = source.dupe(self.builder);
531 source.addStepDependencies(&self.step);
532}
533
534pub fn linkFramework(self: *CompileStep, framework_name: []const u8) void {
535 self.frameworks.put(self.builder.dupe(framework_name), .{}) catch @panic("OOM");
536}
537
538pub 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
544pub 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.
551pub 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
567pub fn linkLibrary(self: *CompileStep, lib: *CompileStep) void {
568 assert(lib.kind == .lib);
569 self.linkLibraryOrObject(lib);
570}
571
572pub fn isDynamicLibrary(self: *CompileStep) bool {
573 return self.kind == .lib and self.linkage == Linkage.dynamic;
574}
575
576pub fn isStaticLibrary(self: *CompileStep) bool {
577 return self.kind == .lib and self.linkage != Linkage.dynamic;
578}
579
580pub 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
587pub fn linkLibC(self: *CompileStep) void {
588 self.is_linking_libc = true;
589}
590
591pub 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.
597pub 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.
603pub 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.
609pub 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.
622pub 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.
635pub 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.
648pub 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.
661pub 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.
674pub 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
768pub fn linkSystemLibrary(self: *CompileStep, name: []const u8) void {
769 self.linkSystemLibraryInner(name, .{});
770}
771
772pub fn linkSystemLibraryNeeded(self: *CompileStep, name: []const u8) void {
773 self.linkSystemLibraryInner(name, .{ .needed = true });
774}
775
776pub fn linkSystemLibraryWeak(self: *CompileStep, name: []const u8) void {
777 self.linkSystemLibraryInner(name, .{ .weak = true });
778}
779
780fn 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
803pub 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
808pub 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
813pub 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.
819pub 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
832pub 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
839pub 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
846pub fn setVerboseLink(self: *CompileStep, value: bool) void {
847 self.verbose_link = value;
848}
849
850pub fn setVerboseCC(self: *CompileStep, value: bool) void {
851 self.verbose_cc = value;
852}
853
854pub fn overrideZigLibDir(self: *CompileStep, dir_path: []const u8) void {
855 self.override_lib_dir = self.builder.dupePath(dir_path);
856}
857
858pub fn setMainPkgPath(self: *CompileStep, dir_path: []const u8) void {
859 self.main_pkg_path = self.builder.dupePath(dir_path);
860}
861
862pub 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.
868pub 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.
873pub 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.
880pub 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.
887pub 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
893pub 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
899pub 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
905pub fn addObjectFile(self: *CompileStep, source_file: []const u8) void {
906 self.addObjectFileSource(.{ .path = source_file });
907}
908
909pub 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
914pub fn addObject(self: *CompileStep, obj: *CompileStep) void {
915 assert(obj.kind == .obj);
916 self.linkLibraryOrObject(obj);
917}
918
919pub const addSystemIncludeDir = @compileError("deprecated; use addSystemIncludePath");
920pub const addIncludeDir = @compileError("deprecated; use addIncludePath");
921pub const addLibPath = @compileError("deprecated, use addLibraryPath");
922pub const addFrameworkDir = @compileError("deprecated, use addFrameworkPath");
923
924pub 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
928pub 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
932pub 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
937pub fn addLibraryPath(self: *CompileStep, path: []const u8) void {
938 self.lib_paths.append(self.builder.dupe(path)) catch @panic("OOM");
939}
940
941pub fn addRPath(self: *CompileStep, path: []const u8) void {
942 self.rpaths.append(self.builder.dupe(path)) catch @panic("OOM");
943}
944
945pub fn addFrameworkPath(self: *CompileStep, dir_path: []const u8) void {
946 self.framework_dirs.append(self.builder.dupe(dir_path)) catch @panic("OOM");
947}
948
949pub fn addPackage(self: *CompileStep, package: Pkg) void {
950 self.packages.append(self.builder.dupePkg(package)) catch @panic("OOM");
951 self.addRecursiveBuildDeps(package);
952}
953
954pub fn addOptions(self: *CompileStep, package_name: []const u8, options: *OptionsStep) void {
955 self.addPackage(options.getPackage(package_name));
956}
957
958fn 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
967pub 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.
976pub 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
1011pub 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
1020fn 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
1026fn 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
1042fn 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
1840fn 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
1849fn 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.
1859fn 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
1877pub 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
1905fn 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
1921fn 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
1945test "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
1985fn 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
1995const 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 @@
1const std = @import("../std.zig");
2const ConfigHeaderStep = @This();
3const Step = std.Build.Step;
4
5pub const base_id: Step.Id = .config_header;
6
7pub 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
16pub const Value = union(enum) {
17 undef,
18 defined,
19 boolean: bool,
20 int: i64,
21 ident: []const u8,
22 string: []const u8,
23};
24
25step: Step,
26builder: *std.Build,
27source: std.Build.FileSource,
28style: Style,
29values: std.StringHashMap(Value),
30max_bytes: usize = 2 * 1024 * 1024,
31output_dir: []const u8,
32output_basename: []const u8,
33
34pub 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
58pub fn addValues(self: *ConfigHeaderStep, values: anytype) void {
59 return addValuesInner(self, values) catch @panic("OOM");
60}
61
62fn 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
68fn 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
112fn 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
172fn 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
218fn 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
270fn 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
7const std = @import("../std.zig");
8const Step = std.Build.Step;
9const CompileStep = std.Build.CompileStep;
10const RunStep = std.Build.RunStep;
11
12const fs = std.fs;
13const process = std.process;
14const EnvMap = process.EnvMap;
15
16const EmulatableRunStep = @This();
17
18pub const base_id = .emulatable_run;
19
20const max_stdout_size = 1 * 1024 * 1024; // 1 MiB
21
22step: Step,
23builder: *std.Build,
24
25/// The artifact (executable) to be run by this step
26exe: *CompileStep,
27
28/// Set this to `null` to ignore the exit code for the purpose of determining a successful execution
29expected_exit_code: ?u8 = 0,
30
31/// Override this field to modify the environment
32env_map: ?*EnvMap,
33
34/// Set this to modify the current working directory
35cwd: ?[]const u8,
36
37stdout_action: RunStep.StdIoAction = .inherit,
38stderr_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.
42hide_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.
48pub 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
70fn 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
144pub fn expectStdErrEqual(self: *EmulatableRunStep, bytes: []const u8) void {
145 self.stderr_action = .{ .expect_exact = self.builder.dupe(bytes) };
146}
147
148pub fn expectStdOutEqual(self: *EmulatableRunStep, bytes: []const u8) void {
149 self.stdout_action = .{ .expect_exact = self.builder.dupe(bytes) };
150}
151
152fn 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 @@
1const std = @import("../std.zig");
2const Step = std.Build.Step;
3const FmtStep = @This();
4
5pub const base_id = .fmt;
6
7step: Step,
8builder: *std.Build,
9argv: [][]const u8,
10
11pub 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
28fn 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 @@
1const std = @import("../std.zig");
2const Step = std.Build.Step;
3const CompileStep = std.Build.CompileStep;
4const InstallDir = std.Build.InstallDir;
5const InstallArtifactStep = @This();
6
7pub const base_id = .install_artifact;
8
9step: Step,
10builder: *std.Build,
11artifact: *CompileStep,
12dest_dir: InstallDir,
13pdb_dir: ?InstallDir,
14h_dir: ?InstallDir,
15
16pub 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
63fn 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 @@
1const std = @import("../std.zig");
2const mem = std.mem;
3const fs = std.fs;
4const Step = std.Build.Step;
5const InstallDir = std.Build.InstallDir;
6const InstallDirStep = @This();
7const log = std.log;
8
9step: Step,
10builder: *std.Build,
11options: 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.
14override_source_builder: ?*std.Build = null,
15
16pub const base_id = .install_dir;
17
18pub 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
43pub 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
55fn 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 @@
1const std = @import("../std.zig");
2const Step = std.Build.Step;
3const FileSource = std.Build.FileSource;
4const InstallDir = std.Build.InstallDir;
5const InstallFileStep = @This();
6
7pub const base_id = .install_file;
8
9step: Step,
10builder: *std.Build,
11source: FileSource,
12dir: InstallDir,
13dest_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.
16override_source_builder: ?*std.Build = null,
17
18pub 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
34fn 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
4const std = @import("std");
5const InstallRawStep = @This();
6
7const Allocator = std.mem.Allocator;
8const ArenaAllocator = std.heap.ArenaAllocator;
9const ArrayListUnmanaged = std.ArrayListUnmanaged;
10const File = std.fs.File;
11const InstallDir = std.Build.InstallDir;
12const CompileStep = std.Build.CompileStep;
13const Step = std.Build.Step;
14const elf = std.elf;
15const fs = std.fs;
16const io = std.io;
17const sort = std.sort;
18
19pub const base_id = .install_raw;
20
21pub const RawFormat = enum {
22 bin,
23 hex,
24};
25
26step: Step,
27builder: *std.Build,
28artifact: *CompileStep,
29dest_dir: InstallDir,
30dest_filename: []const u8,
31options: CreateOptions,
32output_file: std.Build.GeneratedFile,
33
34pub 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
41pub 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
68pub fn getOutputSource(self: *const InstallRawStep) std.Build.FileSource {
69 return std.Build.FileSource{ .generated = &self.output_file };
70}
71
72fn 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
108test {
109 std.testing.refAllDecls(InstallRawStep);
110}
lib/std/Build/LogStep.zig created+23
......@@ -0,0 +1,23 @@
1const std = @import("../std.zig");
2const log = std.log;
3const Step = std.Build.Step;
4const LogStep = @This();
5
6pub const base_id = .log;
7
8step: Step,
9builder: *std.Build,
10data: []const u8,
11
12pub 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
20fn 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 @@
1const std = @import("../std.zig");
2const builtin = @import("builtin");
3const fs = std.fs;
4const Step = std.Build.Step;
5const GeneratedFile = std.Build.GeneratedFile;
6const CompileStep = std.Build.CompileStep;
7const FileSource = std.Build.FileSource;
8
9const OptionsStep = @This();
10
11pub const base_id = .options;
12
13step: Step,
14generated_file: GeneratedFile,
15builder: *std.Build,
16
17contents: std.ArrayList(u8),
18artifact_args: std.ArrayList(OptionArtifactArg),
19file_source_args: std.ArrayList(OptionFileSourceArg),
20
21pub 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
36pub 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
40fn 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?
142fn 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.
188pub 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.
202pub 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
207pub fn getPackage(self: *OptionsStep, package_name: []const u8) std.Build.Pkg {
208 return .{ .name = package_name, .source = self.getSource() };
209}
210
211pub fn getSource(self: *OptionsStep) FileSource {
212 return .{ .generated = &self.generated_file };
213}
214
215fn 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
253fn 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
271const OptionArtifactArg = struct {
272 name: []const u8,
273 artifact: *CompileStep,
274};
275
276const OptionFileSourceArg = struct {
277 name: []const u8,
278 source: FileSource,
279};
280
281test "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 @@
1const std = @import("../std.zig");
2const log = std.log;
3const fs = std.fs;
4const Step = std.Build.Step;
5const RemoveDirStep = @This();
6
7pub const base_id = .remove_dir;
8
9step: Step,
10builder: *std.Build,
11dir_path: []const u8,
12
13pub 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
21fn 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 @@
1const std = @import("../std.zig");
2const builtin = @import("builtin");
3const Step = std.Build.Step;
4const CompileStep = std.Build.CompileStep;
5const WriteFileStep = std.Build.WriteFileStep;
6const fs = std.fs;
7const mem = std.mem;
8const process = std.process;
9const ArrayList = std.ArrayList;
10const EnvMap = process.EnvMap;
11const Allocator = mem.Allocator;
12const ExecError = std.Build.ExecError;
13
14const max_stdout_size = 1 * 1024 * 1024; // 1 MiB
15
16const RunStep = @This();
17
18pub const base_id: Step.Id = .run;
19
20step: Step,
21builder: *std.Build,
22
23/// See also addArg and addArgs to modifying this directly
24argv: ArrayList(Arg),
25
26/// Set this to modify the current working directory
27cwd: ?[]const u8,
28
29/// Override this field to modify the environment, or use setEnvironmentVariable
30env_map: ?*EnvMap,
31
32stdout_action: StdIoAction = .inherit,
33stderr_action: StdIoAction = .inherit,
34
35stdin_behavior: std.ChildProcess.StdIo = .Inherit,
36
37/// Set this to `null` to ignore the exit code for the purpose of determining a successful execution
38expected_exit_code: ?u8 = 0,
39
40/// Print the command before running it
41print: bool,
42
43pub const StdIoAction = union(enum) {
44 inherit,
45 ignore,
46 expect_exact: []const u8,
47 expect_matches: []const []const u8,
48};
49
50pub const Arg = union(enum) {
51 artifact: *CompileStep,
52 file_source: std.Build.FileSource,
53 bytes: []u8,
54};
55
56pub 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
69pub 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
74pub 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
81pub fn addArg(self: *RunStep, arg: []const u8) void {
82 self.argv.append(Arg{ .bytes = self.builder.dupe(arg) }) catch @panic("OOM");
83}
84
85pub fn addArgs(self: *RunStep, args: []const []const u8) void {
86 for (args) |arg| {
87 self.addArg(arg);
88 }
89}
90
91pub 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
97pub 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.
102pub 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
116pub fn getEnvMap(self: *RunStep) *EnvMap {
117 return getEnvMapInternal(&self.step, self.builder.allocator);
118}
119
120fn 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
138pub 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
146pub fn expectStdErrEqual(self: *RunStep, bytes: []const u8) void {
147 self.stderr_action = .{ .expect_exact = self.builder.dupe(bytes) };
148}
149
150pub fn expectStdOutEqual(self: *RunStep, bytes: []const u8) void {
151 self.stdout_action = .{ .expect_exact = self.builder.dupe(bytes) };
152}
153
154fn 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
162fn 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
194pub 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
350fn 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
358fn 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.
364pub 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 @@
1id: Id,
2name: []const u8,
3makeFn: *const fn (self: *Step) anyerror!void,
4dependencies: std.ArrayList(*Step),
5loop_flag: bool,
6done_flag: bool,
7
8pub 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
52pub 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
68pub fn initNoOp(id: Id, name: []const u8, allocator: Allocator) Step {
69 return init(id, name, allocator, makeNoOp);
70}
71
72pub fn make(self: *Step) !void {
73 if (self.done_flag) return;
74
75 try self.makeFn(self);
76 self.done_flag = true;
77}
78
79pub fn dependOn(self: *Step, other: *Step) void {
80 self.dependencies.append(other) catch @panic("OOM");
81}
82
83fn makeNoOp(self: *Step) anyerror!void {
84 _ = self;
85}
86
87pub 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
94const Step = @This();
95const std = @import("../std.zig");
96const Build = std.Build;
97const Allocator = std.mem.Allocator;
lib/std/Build/TranslateCStep.zig created+136
......@@ -0,0 +1,136 @@
1const std = @import("../std.zig");
2const Step = std.Build.Step;
3const CompileStep = std.Build.CompileStep;
4const CheckFileStep = std.Build.CheckFileStep;
5const fs = std.fs;
6const mem = std.mem;
7const CrossTarget = std.zig.CrossTarget;
8
9const TranslateCStep = @This();
10
11pub const base_id = .translate_c;
12
13step: Step,
14builder: *std.Build,
15source: std.Build.FileSource,
16include_dirs: std.ArrayList([]const u8),
17c_macros: std.ArrayList([]const u8),
18output_dir: ?[]const u8,
19out_basename: []const u8,
20target: CrossTarget,
21optimize: std.builtin.OptimizeMode,
22output_file: std.Build.GeneratedFile,
23
24pub const Options = struct {
25 source_file: std.Build.FileSource,
26 target: CrossTarget,
27 optimize: std.builtin.OptimizeMode,
28};
29
30pub 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
49pub 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.
58pub 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
69pub fn addIncludeDir(self: *TranslateCStep, include_dir: []const u8) void {
70 self.include_dirs.append(self.builder.dupePath(include_dir)) catch @panic("OOM");
71}
72
73pub 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.
79pub 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.
85pub 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
89fn 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 @@
1const std = @import("../std.zig");
2const Step = std.Build.Step;
3const fs = std.fs;
4const ArrayList = std.ArrayList;
5
6const WriteFileStep = @This();
7
8pub const base_id = .write_file;
9
10step: Step,
11builder: *std.Build,
12output_dir: []const u8,
13files: std.TailQueue(File),
14
15pub const File = struct {
16 source: std.Build.GeneratedFile,
17 basename: []const u8,
18 bytes: []const u8,
19};
20
21pub 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
30pub 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`.
44pub 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
53fn 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{
166166
167167pub fn getName(self: Thread, buffer_ptr: *[max_name_len:0]u8) GetNameError!?[]const u8 {
168168 buffer_ptr[max_name_len] = 0;
169 var buffer = std.mem.span(buffer_ptr);
169 var buffer: [:0]u8 = buffer_ptr;
170170
171171 switch (target.os.tag) {
172172 .linux => if (use_pthreads and is_gnu) {
lib/std/array_hash_map.zig+2-1
......@@ -1145,7 +1145,8 @@ pub fn ArrayHashMapUnmanaged(
11451145 }
11461146
11471147 /// 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.
11491150 pub fn clone(self: Self, allocator: Allocator) !Self {
11501151 if (@sizeOf(ByIndexContext) != 0)
11511152 @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 {
2929 }
3030
3131 /// 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 } {
3337 return self.buffer[0..self.len];
3438 }
3539
lib/std/build.zig deleted-1781
......@@ -1,1781 +0,0 @@
1const std = @import("std.zig");
2const builtin = @import("builtin");
3const io = std.io;
4const fs = std.fs;
5const mem = std.mem;
6const debug = std.debug;
7const panic = std.debug.panic;
8const assert = debug.assert;
9const log = std.log;
10const ArrayList = std.ArrayList;
11const StringHashMap = std.StringHashMap;
12const Allocator = mem.Allocator;
13const process = std.process;
14const EnvMap = std.process.EnvMap;
15const fmt_lib = std.fmt;
16const File = std.fs.File;
17const CrossTarget = std.zig.CrossTarget;
18const NativeTargetInfo = std.zig.system.NativeTargetInfo;
19const Sha256 = std.crypto.hash.sha2.Sha256;
20const ThisModule = @This();
21
22pub const CheckFileStep = @import("build/CheckFileStep.zig");
23pub const CheckObjectStep = @import("build/CheckObjectStep.zig");
24pub const ConfigHeaderStep = @import("build/ConfigHeaderStep.zig");
25pub const EmulatableRunStep = @import("build/EmulatableRunStep.zig");
26pub const FmtStep = @import("build/FmtStep.zig");
27pub const InstallArtifactStep = @import("build/InstallArtifactStep.zig");
28pub const InstallDirStep = @import("build/InstallDirStep.zig");
29pub const InstallFileStep = @import("build/InstallFileStep.zig");
30pub const InstallRawStep = @import("build/InstallRawStep.zig");
31pub const LibExeObjStep = @import("build/LibExeObjStep.zig");
32pub const LogStep = @import("build/LogStep.zig");
33pub const OptionsStep = @import("build/OptionsStep.zig");
34pub const RemoveDirStep = @import("build/RemoveDirStep.zig");
35pub const RunStep = @import("build/RunStep.zig");
36pub const TranslateCStep = @import("build/TranslateCStep.zig");
37pub const WriteFileStep = @import("build/WriteFileStep.zig");
38
39pub 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
1473test "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
1490pub 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.
1498pub 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///
1516pub 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.
1569pub 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`
1583pub const InstallDirectoryOptions = InstallDirStep.Options;
1584
1585pub 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
1676pub const VcpkgRoot = union(VcpkgRootStatus) {
1677 unattempted: void,
1678 not_found: void,
1679 found: []const u8,
1680};
1681
1682pub const VcpkgRootStatus = enum {
1683 unattempted,
1684 not_found,
1685 found,
1686};
1687
1688pub 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
1708pub 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
1721test "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
1765test {
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 @@
1const std = @import("../std.zig");
2const build = std.build;
3const Step = build.Step;
4const Builder = build.Builder;
5const fs = std.fs;
6const mem = std.mem;
7
8const CheckFileStep = @This();
9
10pub const base_id = .check_file;
11
12step: Step,
13builder: *Builder,
14expected_matches: []const []const u8,
15source: build.FileSource,
16max_bytes: usize = 20 * 1024 * 1024,
17
18pub 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
34fn 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 @@
1const std = @import("../std.zig");
2const assert = std.debug.assert;
3const build = std.build;
4const fs = std.fs;
5const macho = std.macho;
6const math = std.math;
7const mem = std.mem;
8const testing = std.testing;
9
10const CheckObjectStep = @This();
11
12const Allocator = mem.Allocator;
13const Builder = build.Builder;
14const Step = build.Step;
15const EmulatableRunStep = build.EmulatableRunStep;
16
17pub const base_id = .check_object;
18
19step: Step,
20builder: *Builder,
21source: build.FileSource,
22max_bytes: usize = 20 * 1024 * 1024,
23checks: std.ArrayList(Check),
24dump_symtab: bool = false,
25obj_format: std.Target.ObjectFormat,
26
27pub 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.
43pub 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 +`.
65const 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
195const 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
218const 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.
253pub 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.
261pub 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.
270pub 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.
279pub 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.
291pub 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
301fn 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
394const Opts = struct {
395 gpa: ?Allocator = null,
396 dump_symtab: bool = false,
397};
398
399const 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
683const 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 @@
1const std = @import("../std.zig");
2const ConfigHeaderStep = @This();
3const Step = std.build.Step;
4const Builder = std.build.Builder;
5
6pub const base_id: Step.Id = .config_header;
7
8pub 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
17pub const Value = union(enum) {
18 undef,
19 defined,
20 boolean: bool,
21 int: i64,
22 ident: []const u8,
23 string: []const u8,
24};
25
26step: Step,
27builder: *Builder,
28source: std.build.FileSource,
29style: Style,
30values: std.StringHashMap(Value),
31max_bytes: usize = 2 * 1024 * 1024,
32output_dir: []const u8,
33output_basename: []const u8,
34
35pub 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
59pub fn addValues(self: *ConfigHeaderStep, values: anytype) void {
60 return addValuesInner(self, values) catch @panic("OOM");
61}
62
63fn 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
101fn 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
161fn 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
207fn 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
259fn 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
7const std = @import("../std.zig");
8const build = std.build;
9const Step = std.build.Step;
10const Builder = std.build.Builder;
11const LibExeObjStep = std.build.LibExeObjStep;
12const RunStep = std.build.RunStep;
13
14const fs = std.fs;
15const process = std.process;
16const EnvMap = process.EnvMap;
17
18const EmulatableRunStep = @This();
19
20pub const base_id = .emulatable_run;
21
22const max_stdout_size = 1 * 1024 * 1024; // 1 MiB
23
24step: Step,
25builder: *Builder,
26
27/// The artifact (executable) to be run by this step
28exe: *LibExeObjStep,
29
30/// Set this to `null` to ignore the exit code for the purpose of determining a successful execution
31expected_exit_code: ?u8 = 0,
32
33/// Override this field to modify the environment
34env_map: ?*EnvMap,
35
36/// Set this to modify the current working directory
37cwd: ?[]const u8,
38
39stdout_action: RunStep.StdIoAction = .inherit,
40stderr_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.
44hide_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.
50pub 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
72fn 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
146pub fn expectStdErrEqual(self: *EmulatableRunStep, bytes: []const u8) void {
147 self.stderr_action = .{ .expect_exact = self.builder.dupe(bytes) };
148}
149
150pub fn expectStdOutEqual(self: *EmulatableRunStep, bytes: []const u8) void {
151 self.stdout_action = .{ .expect_exact = self.builder.dupe(bytes) };
152}
153
154fn 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 @@
1const std = @import("../std.zig");
2const build = @import("../build.zig");
3const Step = build.Step;
4const Builder = build.Builder;
5const BufMap = std.BufMap;
6const mem = std.mem;
7
8const FmtStep = @This();
9
10pub const base_id = .fmt;
11
12step: Step,
13builder: *Builder,
14argv: [][]const u8,
15
16pub 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
33fn 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 @@
1const std = @import("../std.zig");
2const build = @import("../build.zig");
3const Step = build.Step;
4const Builder = build.Builder;
5const LibExeObjStep = std.build.LibExeObjStep;
6const InstallDir = std.build.InstallDir;
7
8pub const base_id = .install_artifact;
9
10step: Step,
11builder: *Builder,
12artifact: *LibExeObjStep,
13dest_dir: InstallDir,
14pdb_dir: ?InstallDir,
15h_dir: ?InstallDir,
16
17const Self = @This();
18
19pub 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
66fn 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 @@
1const std = @import("../std.zig");
2const mem = std.mem;
3const fs = std.fs;
4const build = @import("../build.zig");
5const Step = build.Step;
6const Builder = build.Builder;
7const InstallDir = std.build.InstallDir;
8const InstallDirStep = @This();
9const log = std.log;
10
11step: Step,
12builder: *Builder,
13options: 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.
16override_source_builder: ?*Builder = null,
17
18pub const base_id = .install_dir;
19
20pub 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
45pub 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
57fn 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 @@
1const std = @import("../std.zig");
2const build = @import("../build.zig");
3const Step = build.Step;
4const Builder = build.Builder;
5const FileSource = std.build.FileSource;
6const InstallDir = std.build.InstallDir;
7const InstallFileStep = @This();
8
9pub const base_id = .install_file;
10
11step: Step,
12builder: *Builder,
13source: FileSource,
14dir: InstallDir,
15dest_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.
18override_source_builder: ?*Builder = null,
19
20pub 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
36fn 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
4const std = @import("std");
5const InstallRawStep = @This();
6
7const Allocator = std.mem.Allocator;
8const ArenaAllocator = std.heap.ArenaAllocator;
9const ArrayListUnmanaged = std.ArrayListUnmanaged;
10const Builder = std.build.Builder;
11const File = std.fs.File;
12const InstallDir = std.build.InstallDir;
13const LibExeObjStep = std.build.LibExeObjStep;
14const Step = std.build.Step;
15const elf = std.elf;
16const fs = std.fs;
17const io = std.io;
18const sort = std.sort;
19
20pub const base_id = .install_raw;
21
22pub const RawFormat = enum {
23 bin,
24 hex,
25};
26
27step: Step,
28builder: *Builder,
29artifact: *LibExeObjStep,
30dest_dir: InstallDir,
31dest_filename: []const u8,
32options: CreateOptions,
33output_file: std.build.GeneratedFile,
34
35pub 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
42pub 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
64pub fn getOutputSource(self: *const InstallRawStep) std.build.FileSource {
65 return std.build.FileSource{ .generated = &self.output_file };
66}
67
68fn 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
104test {
105 std.testing.refAllDecls(InstallRawStep);
106}
lib/std/build/LibExeObjStep.zig deleted-2110
......@@ -1,2110 +0,0 @@
1const builtin = @import("builtin");
2const std = @import("../std.zig");
3const mem = std.mem;
4const log = std.log;
5const fs = std.fs;
6const assert = std.debug.assert;
7const panic = std.debug.panic;
8const ArrayList = std.ArrayList;
9const StringHashMap = std.StringHashMap;
10const Sha256 = std.crypto.hash.sha2.Sha256;
11const Allocator = mem.Allocator;
12const build = @import("../build.zig");
13const Step = build.Step;
14const Builder = build.Builder;
15const CrossTarget = std.zig.CrossTarget;
16const NativeTargetInfo = std.zig.system.NativeTargetInfo;
17const FileSource = std.build.FileSource;
18const PkgConfigPkg = Builder.PkgConfigPkg;
19const PkgConfigError = Builder.PkgConfigError;
20const ExecError = Builder.ExecError;
21const Pkg = std.build.Pkg;
22const VcpkgRoot = std.build.VcpkgRoot;
23const InstallDir = std.build.InstallDir;
24const InstallArtifactStep = std.build.InstallArtifactStep;
25const GeneratedFile = std.build.GeneratedFile;
26const InstallRawStep = std.build.InstallRawStep;
27const EmulatableRunStep = std.build.EmulatableRunStep;
28const CheckObjectStep = std.build.CheckObjectStep;
29const RunStep = std.build.RunStep;
30const OptionsStep = std.build.OptionsStep;
31const ConfigHeaderStep = std.build.ConfigHeaderStep;
32const LibExeObjStep = @This();
33
34pub const base_id = .lib_exe_obj;
35
36step: Step,
37builder: *Builder,
38name: []const u8,
39target: CrossTarget = CrossTarget{},
40target_info: NativeTargetInfo,
41linker_script: ?FileSource = null,
42version_script: ?[]const u8 = null,
43out_filename: []const u8,
44linkage: ?Linkage = null,
45version: ?std.builtin.Version,
46build_mode: std.builtin.Mode,
47kind: Kind,
48major_only_filename: ?[]const u8,
49name_only_filename: ?[]const u8,
50strip: ?bool,
51unwind_tables: ?bool,
52// keep in sync with src/link.zig:CompressDebugSections
53compress_debug_sections: enum { none, zlib } = .none,
54lib_paths: ArrayList([]const u8),
55rpaths: ArrayList([]const u8),
56framework_dirs: ArrayList([]const u8),
57frameworks: StringHashMap(FrameworkLinkInfo),
58verbose_link: bool,
59verbose_cc: bool,
60emit_analysis: EmitOption = .default,
61emit_asm: EmitOption = .default,
62emit_bin: EmitOption = .default,
63emit_docs: EmitOption = .default,
64emit_implib: EmitOption = .default,
65emit_llvm_bc: EmitOption = .default,
66emit_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.
69emit_h: bool = false,
70bundle_compiler_rt: ?bool = null,
71single_threaded: ?bool = null,
72stack_protector: ?bool = null,
73disable_stack_probing: bool,
74disable_sanitize_c: bool,
75sanitize_thread: bool,
76rdynamic: bool,
77import_memory: bool = false,
78/// For WebAssembly targets, this will allow for undefined symbols to
79/// be imported from the host environment.
80import_symbols: bool = false,
81import_table: bool = false,
82export_table: bool = false,
83initial_memory: ?u64 = null,
84max_memory: ?u64 = null,
85shared_memory: bool = false,
86global_base: ?u64 = null,
87c_std: Builder.CStd,
88override_lib_dir: ?[]const u8,
89main_pkg_path: ?[]const u8,
90exec_cmd_args: ?[]const ?[]const u8,
91name_prefix: []const u8,
92filter: ?[]const u8,
93test_evented_io: bool = false,
94test_runner: ?[]const u8,
95code_model: std.builtin.CodeModel = .default,
96wasi_exec_model: ?std.builtin.WasiExecModel = null,
97/// Symbols to be exported when compiling to wasm
98export_symbol_names: []const []const u8 = &.{},
99
100root_src: ?FileSource,
101out_h_filename: []const u8,
102out_lib_filename: []const u8,
103out_pdb_filename: []const u8,
104packages: ArrayList(Pkg),
105
106object_src: []const u8,
107
108link_objects: ArrayList(LinkObject),
109include_dirs: ArrayList(IncludeDir),
110c_macros: ArrayList([]const u8),
111installed_headers: ArrayList(*std.build.Step),
112output_dir: ?[]const u8,
113is_linking_libc: bool = false,
114is_linking_libcpp: bool = false,
115vcpkg_bin_path: ?[]const u8 = null,
116
117/// This may be set in order to override the default install directory
118override_dest_dir: ?InstallDir,
119installed_path: ?[]const u8,
120install_step: ?*InstallArtifactStep,
121
122/// Base address for an executable image.
123image_base: ?u64 = null,
124
125libc_file: ?FileSource = null,
126
127valgrind_support: ?bool = null,
128each_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.
134build_id: ?bool = null,
135
136/// Create a .eh_frame_hdr section and a PT_GNU_EH_FRAME segment in the ELF
137/// file.
138link_eh_frame_hdr: bool = false,
139link_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.
143link_function_sections: bool = false,
144
145/// Remove functions and data that are unreachable by the entry point or
146/// exported symbols.
147link_gc_sections: ?bool = null,
148
149linker_allow_shlib_undefined: ?bool = null,
150
151/// Permit read-only relocations in read-only segments. Disallowed by default.
152link_z_notext: bool = false,
153
154/// Force all relocations to be read-only after processing.
155link_z_relro: bool = true,
156
157/// Allow relocations to be lazily processed after load.
158link_z_lazy: bool = false,
159
160/// Common page size
161link_z_common_page_size: ?u64 = null,
162
163/// Maximum page size
164link_z_max_page_size: ?u64 = null,
165
166/// (Darwin) Install name for the dylib
167install_name: ?[]const u8 = null,
168
169/// (Darwin) Path to entitlements file
170entitlements: ?[]const u8 = null,
171
172/// (Darwin) Size of the pagezero segment.
173pagezero_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.
180search_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.
184headerpad_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.
188headerpad_max_install_names: bool = false,
189
190/// (Darwin) Remove dylibs that are unreachable by the entry point or exported symbols.
191dead_strip_dylibs: bool = false,
192
193/// Position Independent Code
194force_pic: ?bool = null,
195
196/// Position Independent Executable
197pie: ?bool = null,
198
199red_zone: ?bool = null,
200
201omit_frame_pointer: ?bool = null,
202dll_export_fns: ?bool = null,
203
204subsystem: ?std.Target.SubSystem = null,
205
206entry_symbol_name: ?[]const u8 = null,
207
208/// Overrides the default stack size
209stack_size: ?u64 = null,
210
211want_lto: ?bool = null,
212use_llvm: ?bool = null,
213use_lld: ?bool = null,
214
215output_path_source: GeneratedFile,
216output_lib_path_source: GeneratedFile,
217output_h_path_source: GeneratedFile,
218output_pdb_path_source: GeneratedFile,
219
220pub const CSourceFiles = struct {
221 files: []const []const u8,
222 flags: []const []const u8,
223};
224
225pub 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
237pub 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
246pub 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
262const FrameworkLinkInfo = struct {
263 needed: bool = false,
264 weak: bool = false,
265};
266
267pub 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
274pub const Kind = enum {
275 exe,
276 lib,
277 obj,
278 @"test",
279 test_exe,
280};
281
282pub const SharedLibKind = union(enum) {
283 versioned: std.builtin.Version,
284 unversioned: void,
285};
286
287pub const Linkage = enum { dynamic, static };
288
289pub 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
305pub 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
312pub fn createStaticLibrary(builder: *Builder, name: []const u8, root_src: ?FileSource) *LibExeObjStep {
313 return initExtraArgs(builder, name, root_src, .lib, .static, null);
314}
315
316pub fn createObject(builder: *Builder, name: []const u8, root_src: ?FileSource) *LibExeObjStep {
317 return initExtraArgs(builder, name, root_src, .obj, null, null);
318}
319
320pub fn createExecutable(builder: *Builder, name: []const u8, root_src: ?FileSource) *LibExeObjStep {
321 return initExtraArgs(builder, name, root_src, .exe, null, null);
322}
323
324pub fn createTest(builder: *Builder, name: []const u8, root_src: FileSource) *LibExeObjStep {
325 return initExtraArgs(builder, name, root_src, .@"test", null, null);
326}
327
328pub fn createTestExe(builder: *Builder, name: []const u8, root_src: FileSource) *LibExeObjStep {
329 return initExtraArgs(builder, name, root_src, .test_exe, null, null);
330}
331
332fn 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
404fn 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
460pub fn setTarget(self: *LibExeObjStep, target: CrossTarget) void {
461 self.target = target;
462 self.computeOutFileNames();
463}
464
465pub fn setOutputDir(self: *LibExeObjStep, dir: []const u8) void {
466 self.output_dir = self.builder.dupePath(dir);
467}
468
469pub fn install(self: *LibExeObjStep) void {
470 self.builder.installArtifact(self);
471}
472
473pub fn installRaw(self: *LibExeObjStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep {
474 return self.builder.installRaw(self, dest_filename, options);
475}
476
477pub 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
483pub 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
495pub 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
504pub 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`.
529pub 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.
553pub 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
563pub fn checkObject(self: *LibExeObjStep, obj_format: std.Target.ObjectFormat) *CheckObjectStep {
564 return CheckObjectStep.create(self.builder, self.getOutputSource(), obj_format);
565}
566
567pub fn setLinkerScriptPath(self: *LibExeObjStep, source: FileSource) void {
568 self.linker_script = source.dupe(self.builder);
569 source.addStepDependencies(&self.step);
570}
571
572pub fn linkFramework(self: *LibExeObjStep, framework_name: []const u8) void {
573 self.frameworks.put(self.builder.dupe(framework_name), .{}) catch unreachable;
574}
575
576pub 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
582pub 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.
589pub 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
605pub fn linkLibrary(self: *LibExeObjStep, lib: *LibExeObjStep) void {
606 assert(lib.kind == .lib);
607 self.linkLibraryOrObject(lib);
608}
609
610pub fn isDynamicLibrary(self: *LibExeObjStep) bool {
611 return self.kind == .lib and self.linkage == Linkage.dynamic;
612}
613
614pub fn isStaticLibrary(self: *LibExeObjStep) bool {
615 return self.kind == .lib and self.linkage != Linkage.dynamic;
616}
617
618pub 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
624pub fn linkLibC(self: *LibExeObjStep) void {
625 self.is_linking_libc = true;
626}
627
628pub 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.
634pub 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.
640pub 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.
646pub 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.
659pub 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.
672pub 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.
685pub 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.
698pub 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.
711pub 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
805pub fn linkSystemLibrary(self: *LibExeObjStep, name: []const u8) void {
806 self.linkSystemLibraryInner(name, .{});
807}
808
809pub fn linkSystemLibraryNeeded(self: *LibExeObjStep, name: []const u8) void {
810 self.linkSystemLibraryInner(name, .{ .needed = true });
811}
812
813pub fn linkSystemLibraryWeak(self: *LibExeObjStep, name: []const u8) void {
814 self.linkSystemLibraryInner(name, .{ .weak = true });
815}
816
817fn 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
840pub 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
845pub 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
850pub 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.
856pub 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
869pub 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
876pub 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
883pub fn setVerboseLink(self: *LibExeObjStep, value: bool) void {
884 self.verbose_link = value;
885}
886
887pub fn setVerboseCC(self: *LibExeObjStep, value: bool) void {
888 self.verbose_cc = value;
889}
890
891pub fn setBuildMode(self: *LibExeObjStep, mode: std.builtin.Mode) void {
892 self.build_mode = mode;
893}
894
895pub fn overrideZigLibDir(self: *LibExeObjStep, dir_path: []const u8) void {
896 self.override_lib_dir = self.builder.dupePath(dir_path);
897}
898
899pub fn setMainPkgPath(self: *LibExeObjStep, dir_path: []const u8) void {
900 self.main_pkg_path = self.builder.dupePath(dir_path);
901}
902
903pub 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.
909pub 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.
914pub 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.
921pub 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.
928pub 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
934pub 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
940pub 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
946pub fn addObjectFile(self: *LibExeObjStep, source_file: []const u8) void {
947 self.addObjectFileSource(.{ .path = source_file });
948}
949
950pub 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
955pub fn addObject(self: *LibExeObjStep, obj: *LibExeObjStep) void {
956 assert(obj.kind == .obj);
957 self.linkLibraryOrObject(obj);
958}
959
960pub const addSystemIncludeDir = @compileError("deprecated; use addSystemIncludePath");
961pub const addIncludeDir = @compileError("deprecated; use addIncludePath");
962pub const addLibPath = @compileError("deprecated, use addLibraryPath");
963pub const addFrameworkDir = @compileError("deprecated, use addFrameworkPath");
964
965pub 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
969pub fn addIncludePath(self: *LibExeObjStep, path: []const u8) void {
970 self.include_dirs.append(IncludeDir{ .raw_path = self.builder.dupe(path) }) catch unreachable;
971}
972
973pub 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
978pub fn addLibraryPath(self: *LibExeObjStep, path: []const u8) void {
979 self.lib_paths.append(self.builder.dupe(path)) catch unreachable;
980}
981
982pub fn addRPath(self: *LibExeObjStep, path: []const u8) void {
983 self.rpaths.append(self.builder.dupe(path)) catch unreachable;
984}
985
986pub fn addFrameworkPath(self: *LibExeObjStep, dir_path: []const u8) void {
987 self.framework_dirs.append(self.builder.dupe(dir_path)) catch unreachable;
988}
989
990pub fn addPackage(self: *LibExeObjStep, package: Pkg) void {
991 self.packages.append(self.builder.dupePkg(package)) catch unreachable;
992 self.addRecursiveBuildDeps(package);
993}
994
995pub fn addOptions(self: *LibExeObjStep, package_name: []const u8, options: *OptionsStep) void {
996 self.addPackage(options.getPackage(package_name));
997}
998
999fn 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
1008pub 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.
1017pub 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
1052pub 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
1061fn 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
1067fn 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
1083fn 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
1908fn 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
1917fn 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.
1927fn 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
1945pub 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
1968fn 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
1984fn 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
2008test "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
2042fn 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
2052const 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 @@
1const std = @import("../std.zig");
2const log = std.log;
3const build = @import("../build.zig");
4const Step = build.Step;
5const Builder = build.Builder;
6const LogStep = @This();
7
8pub const base_id = .log;
9
10step: Step,
11builder: *Builder,
12data: []const u8,
13
14pub 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
22fn 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 @@
1const std = @import("../std.zig");
2const builtin = @import("builtin");
3const build = std.build;
4const fs = std.fs;
5const Step = build.Step;
6const Builder = build.Builder;
7const GeneratedFile = build.GeneratedFile;
8const LibExeObjStep = build.LibExeObjStep;
9const FileSource = build.FileSource;
10
11const OptionsStep = @This();
12
13pub const base_id = .options;
14
15step: Step,
16generated_file: GeneratedFile,
17builder: *Builder,
18
19contents: std.ArrayList(u8),
20artifact_args: std.ArrayList(OptionArtifactArg),
21file_source_args: std.ArrayList(OptionFileSourceArg),
22
23pub 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
38pub 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?
140fn 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.
186pub 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.
200pub 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
205pub fn getPackage(self: *OptionsStep, package_name: []const u8) build.Pkg {
206 return .{ .name = package_name, .source = self.getSource() };
207}
208
209pub fn getSource(self: *OptionsStep) FileSource {
210 return .{ .generated = &self.generated_file };
211}
212
213fn 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
251fn 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
269const OptionArtifactArg = struct {
270 name: []const u8,
271 artifact: *LibExeObjStep,
272};
273
274const OptionFileSourceArg = struct {
275 name: []const u8,
276 source: FileSource,
277};
278
279test "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 @@
1const std = @import("../std.zig");
2const log = std.log;
3const fs = std.fs;
4const build = @import("../build.zig");
5const Step = build.Step;
6const Builder = build.Builder;
7const RemoveDirStep = @This();
8
9pub const base_id = .remove_dir;
10
11step: Step,
12builder: *Builder,
13dir_path: []const u8,
14
15pub 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
23fn 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 @@
1const std = @import("../std.zig");
2const builtin = @import("builtin");
3const build = std.build;
4const Step = build.Step;
5const Builder = build.Builder;
6const LibExeObjStep = build.LibExeObjStep;
7const WriteFileStep = build.WriteFileStep;
8const fs = std.fs;
9const mem = std.mem;
10const process = std.process;
11const ArrayList = std.ArrayList;
12const EnvMap = process.EnvMap;
13const Allocator = mem.Allocator;
14const ExecError = build.Builder.ExecError;
15
16const max_stdout_size = 1 * 1024 * 1024; // 1 MiB
17
18const RunStep = @This();
19
20pub const base_id: Step.Id = .run;
21
22step: Step,
23builder: *Builder,
24
25/// See also addArg and addArgs to modifying this directly
26argv: ArrayList(Arg),
27
28/// Set this to modify the current working directory
29cwd: ?[]const u8,
30
31/// Override this field to modify the environment, or use setEnvironmentVariable
32env_map: ?*EnvMap,
33
34stdout_action: StdIoAction = .inherit,
35stderr_action: StdIoAction = .inherit,
36
37stdin_behavior: std.ChildProcess.StdIo = .Inherit,
38
39/// Set this to `null` to ignore the exit code for the purpose of determining a successful execution
40expected_exit_code: ?u8 = 0,
41
42/// Print the command before running it
43print: bool,
44
45pub const StdIoAction = union(enum) {
46 inherit,
47 ignore,
48 expect_exact: []const u8,
49 expect_matches: []const []const u8,
50};
51
52pub const Arg = union(enum) {
53 artifact: *LibExeObjStep,
54 file_source: build.FileSource,
55 bytes: []u8,
56};
57
58pub 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
71pub fn addArtifactArg(self: *RunStep, artifact: *LibExeObjStep) void {
72 self.argv.append(Arg{ .artifact = artifact }) catch unreachable;
73 self.step.dependOn(&artifact.step);
74}
75
76pub 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
83pub fn addArg(self: *RunStep, arg: []const u8) void {
84 self.argv.append(Arg{ .bytes = self.builder.dupe(arg) }) catch unreachable;
85}
86
87pub fn addArgs(self: *RunStep, args: []const []const u8) void {
88 for (args) |arg| {
89 self.addArg(arg);
90 }
91}
92
93pub 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
99pub 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.
104pub 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
118pub fn getEnvMap(self: *RunStep) *EnvMap {
119 return getEnvMapInternal(&self.step, self.builder.allocator);
120}
121
122fn 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
140pub 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
148pub fn expectStdErrEqual(self: *RunStep, bytes: []const u8) void {
149 self.stderr_action = .{ .expect_exact = self.builder.dupe(bytes) };
150}
151
152pub fn expectStdOutEqual(self: *RunStep, bytes: []const u8) void {
153 self.stdout_action = .{ .expect_exact = self.builder.dupe(bytes) };
154}
155
156fn 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
164fn 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
196pub 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
352fn 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
360fn 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.
366pub 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 @@
1const std = @import("../std.zig");
2const build = std.build;
3const Step = build.Step;
4const Builder = build.Builder;
5const LibExeObjStep = build.LibExeObjStep;
6const CheckFileStep = build.CheckFileStep;
7const fs = std.fs;
8const mem = std.mem;
9const CrossTarget = std.zig.CrossTarget;
10
11const TranslateCStep = @This();
12
13pub const base_id = .translate_c;
14
15step: Step,
16builder: *Builder,
17source: build.FileSource,
18include_dirs: std.ArrayList([]const u8),
19c_macros: std.ArrayList([]const u8),
20output_dir: ?[]const u8,
21out_basename: []const u8,
22target: CrossTarget = CrossTarget{},
23output_file: build.GeneratedFile,
24
25pub 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
41pub 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.
46pub fn addExecutable(self: *TranslateCStep) *LibExeObjStep {
47 return self.builder.addExecutableSource("translated_c", build.FileSource{ .generated = &self.output_file });
48}
49
50pub fn addIncludeDir(self: *TranslateCStep, include_dir: []const u8) void {
51 self.include_dirs.append(self.builder.dupePath(include_dir)) catch unreachable;
52}
53
54pub 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.
60pub 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.
66pub 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
70fn 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 @@
1const std = @import("../std.zig");
2const build = @import("../build.zig");
3const Step = build.Step;
4const Builder = build.Builder;
5const fs = std.fs;
6const ArrayList = std.ArrayList;
7
8const WriteFileStep = @This();
9
10pub const base_id = .write_file;
11
12step: Step,
13builder: *Builder,
14output_dir: []const u8,
15files: std.TailQueue(File),
16
17pub const File = struct {
18 source: build.GeneratedFile,
19 basename: []const u8,
20 bytes: []const u8,
21};
22
23pub 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
32pub 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`.
46pub 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
55fn 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 {
131131
132132/// This data structure is used by the Zig language code generation and
133133/// therefore must be kept in sync with the compiler implementation.
134pub const Mode = enum {
134pub const OptimizeMode = enum {
135135 Debug,
136136 ReleaseSafe,
137137 ReleaseFast,
138138 ReleaseSmall,
139139};
140140
141/// Deprecated; use OptimizeMode.
142pub const Mode = OptimizeMode;
143
141144/// This data structure is used by the Zig language code generation and
142145/// therefore must be kept in sync with the compiler implementation.
143146pub const CallingConvention = enum {
lib/std/c.zig+2-1
......@@ -90,6 +90,8 @@ pub usingnamespace switch (builtin.os.tag) {
9090 pub extern "c" fn stat(noalias path: [*:0]const u8, noalias buf: *c.Stat) c_int;
9191
9292 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;
9395 },
9496};
9597
......@@ -145,7 +147,6 @@ pub extern "c" fn write(fd: c.fd_t, buf: [*]const u8, nbyte: usize) isize;
145147pub extern "c" fn pwrite(fd: c.fd_t, buf: [*]const u8, nbyte: usize, offset: c.off_t) isize;
146148pub 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;
147149pub extern "c" fn munmap(addr: *align(page_size) const anyopaque, len: usize) c_int;
148pub extern "c" fn msync(addr: *align(page_size) const anyopaque, len: usize, flags: c_int) c_int;
149150pub extern "c" fn mprotect(addr: *align(page_size) anyopaque, len: usize, prot: c_uint) c_int;
150151pub extern "c" fn link(oldpath: [*:0]const u8, newpath: [*:0]const u8, flags: c_int) c_int;
151152pub 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;
5959
6060pub extern "c" fn posix_memalign(memptr: *?*anyopaque, alignment: usize, size: usize) c_int;
6161
62pub extern "c" fn __msync13(addr: *align(std.mem.page_size) const anyopaque, len: usize, flags: c_int) c_int;
63pub const msync = __msync13;
64
6265pub const pthread_mutex_t = extern struct {
6366 magic: u32 = 0x33330003,
6467 errorcheck: padded_pthread_spin_t = 0,
lib/std/child_process.zig+2-2
......@@ -1164,7 +1164,7 @@ fn windowsCreateProcessPathExt(
11641164 var app_name_unicode_string = windows.UNICODE_STRING{
11651165 .Length = app_name_len_bytes,
11661166 .MaximumLength = app_name_len_bytes,
1167 .Buffer = @intToPtr([*]u16, @ptrToInt(app_name_wildcard.ptr)),
1167 .Buffer = @qualCast([*:0]u16, app_name_wildcard.ptr),
11681168 };
11691169 const rc = windows.ntdll.NtQueryDirectoryFile(
11701170 dir.fd,
......@@ -1261,7 +1261,7 @@ fn windowsCreateProcessPathExt(
12611261 var app_name_unicode_string = windows.UNICODE_STRING{
12621262 .Length = app_name_len_bytes,
12631263 .MaximumLength = app_name_len_bytes,
1264 .Buffer = @intToPtr([*]u16, @ptrToInt(app_name_appended.ptr)),
1264 .Buffer = @qualCast([*:0]u16, app_name_appended.ptr),
12651265 };
12661266
12671267 // 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" {
2828
2929fn testCStrFnsImpl() !void {
3030 try testing.expect(cmp("aoeu", "aoez") == -1);
31 try testing.expect(mem.len("123456789") == 9);
3231}
3332
3433/// 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 {
20602060test "manage resources correctly" {
20612061 if (builtin.os.tag == .wasi) return error.SkipZigTest;
20622062
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
20632068 const writer = std.io.null_writer;
20642069 var di = try openSelfDebugInfo(testing.allocator);
20652070 defer di.deinit();
lib/std/fmt.zig+6-5
......@@ -1,11 +1,12 @@
11const std = @import("std.zig");
2const builtin = @import("builtin");
3
24const io = std.io;
35const math = std.math;
46const assert = std.debug.assert;
57const mem = std.mem;
68const unicode = std.unicode;
79const meta = std.meta;
8const builtin = @import("builtin");
910const errol = @import("fmt/errol.zig");
1011const lossyCast = std.math.lossyCast;
1112const expectFmt = std.testing.expectFmt;
......@@ -190,7 +191,7 @@ pub fn format(
190191 .precision = precision,
191192 },
192193 writer,
193 default_max_depth,
194 std.options.fmt_max_depth,
194195 );
195196 }
196197
......@@ -2140,15 +2141,15 @@ test "buffer" {
21402141 {
21412142 var buf1: [32]u8 = undefined;
21422143 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);
21442145 try std.testing.expect(mem.eql(u8, fbs.getWritten(), "1234"));
21452146
21462147 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);
21482149 try std.testing.expect(mem.eql(u8, fbs.getWritten(), "a"));
21492150
21502151 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);
21522153 try std.testing.expect(mem.eql(u8, fbs.getWritten(), "1100"));
21532154 }
21542155}
lib/std/fs.zig+2-2
......@@ -834,7 +834,7 @@ pub const IterableDir = struct {
834834 self.end_index = self.index; // Force fd_readdir in the next loop.
835835 continue :start_over;
836836 }
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];
838838
839839 const next_index = name_index + entry.d_namlen;
840840 self.index = next_index;
......@@ -1763,7 +1763,7 @@ pub const Dir = struct {
17631763 var nt_name = w.UNICODE_STRING{
17641764 .Length = path_len_bytes,
17651765 .MaximumLength = path_len_bytes,
1766 .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w)),
1766 .Buffer = @qualCast([*:0]u16, sub_path_w),
17671767 };
17681768 var attr = w.OBJECT_ATTRIBUTES{
17691769 .Length = @sizeOf(w.OBJECT_ATTRIBUTES),
lib/std/fs/file.zig+2-1
......@@ -179,7 +179,7 @@ pub const File = struct {
179179 lock_nonblocking: bool = false,
180180
181181 /// 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.
183183 mode: Mode = default_mode,
184184
185185 /// Setting this to `.blocking` prevents `O.NONBLOCK` from being passed even
......@@ -307,6 +307,7 @@ pub const File = struct {
307307 /// is unique to each filesystem.
308308 inode: INode,
309309 size: u64,
310 /// This is available on POSIX systems and is always 0 otherwise.
310311 mode: Mode,
311312 kind: Kind,
312313
lib/std/io/fixed_buffer_stream.zig+19-6
......@@ -113,14 +113,27 @@ pub fn FixedBufferStream(comptime Buffer: type) type {
113113 };
114114}
115115
116pub fn fixedBufferStream(buffer: anytype) FixedBufferStream(NonSentinelSpan(@TypeOf(buffer))) {
117 return .{ .buffer = mem.span(buffer), .pos = 0 };
116pub fn fixedBufferStream(buffer: anytype) FixedBufferStream(Slice(@TypeOf(buffer))) {
117 return .{ .buffer = buffer, .pos = 0 };
118118}
119119
120fn NonSentinelSpan(comptime T: type) type {
121 var ptr_info = @typeInfo(mem.Span(T)).Pointer;
122 ptr_info.sentinel = null;
123 return @Type(.{ .Pointer = ptr_info });
120fn Slice(comptime T: type) type {
121 switch (@typeInfo(T)) {
122 .Pointer => |ptr_info| {
123 var new_ptr_info = ptr_info;
124 switch (ptr_info.size) {
125 .Slice => {},
126 .One => switch (@typeInfo(ptr_info.child)) {
127 .Array => |info| new_ptr_info.child = info.child,
128 else => @compileError("invalid type given to fixedBufferStream"),
129 },
130 else => @compileError("invalid type given to fixedBufferStream"),
131 }
132 new_ptr_info.size = .Slice;
133 return @Type(.{ .Pointer = new_ptr_info });
134 },
135 else => @compileError("invalid type given to fixedBufferStream"),
136 }
124137}
125138
126139test "FixedBufferStream output" {
lib/std/json.zig+2-1
......@@ -1384,7 +1384,7 @@ fn ParseInternalErrorImpl(comptime T: type, comptime inferred_types: []const typ
13841384 return errors;
13851385 },
13861386 .Array => |arrayInfo| {
1387 return error{ UnexpectedEndOfJson, UnexpectedToken } || TokenStream.Error ||
1387 return error{ UnexpectedEndOfJson, UnexpectedToken, LengthMismatch } || TokenStream.Error ||
13881388 UnescapeValidStringError ||
13891389 ParseInternalErrorImpl(arrayInfo.child, inferred_types ++ [_]type{T});
13901390 },
......@@ -1625,6 +1625,7 @@ fn parseInternal(
16251625 if (arrayInfo.child != u8) return error.UnexpectedToken;
16261626 var r: T = undefined;
16271627 const source_slice = stringToken.slice(tokens.slice, tokens.i - 1);
1628 if (r.len != stringToken.decodedLength()) return error.LengthMismatch;
16281629 switch (stringToken.escapes) {
16291630 .None => mem.copy(u8, &r, source_slice),
16301631 .Some => try unescapeValidString(&r, source_slice),
lib/std/json/test.zig+6
......@@ -2238,6 +2238,12 @@ test "parse into struct with no fields" {
22382238 try testing.expectEqual(T{}, try parse(T, &ts, ParseOptions{}));
22392239}
22402240
2241test "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
22412247test "parse into struct with misc fields" {
22422248 @setEvalBranchQuota(10000);
22432249 const options = ParseOptions{ .allocator = testing.allocator };
lib/std/mem.zig+24-78
......@@ -636,12 +636,9 @@ test "indexOfDiff" {
636636 try testing.expectEqual(indexOfDiff(u8, "xne", "one"), 0);
637637}
638638
639/// Takes a pointer to an array, a sentinel-terminated pointer, or a slice, and
640/// returns a slice. If there is a sentinel on the input type, there will be a
641/// sentinel on the output type. The constness of the output type matches
642/// the constness of the input type. `[*c]` pointers are assumed to be 0-terminated,
643/// and assumed to not allow null.
644pub fn Span(comptime T: type) type {
639/// Takes a sentinel-terminated pointer and returns a slice preserving pointer attributes.
640/// `[*c]` pointers are assumed to be 0-terminated and assumed to not be allowzero.
641fn Span(comptime T: type) type {
645642 switch (@typeInfo(T)) {
646643 .Optional => |optional_info| {
647644 return ?Span(optional_info.child);
......@@ -649,39 +646,22 @@ pub fn Span(comptime T: type) type {
649646 .Pointer => |ptr_info| {
650647 var new_ptr_info = ptr_info;
651648 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 },
659649 .C => {
660650 new_ptr_info.sentinel = &@as(ptr_info.child, 0);
661651 new_ptr_info.is_allowzero = false;
662652 },
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)),
664655 }
665656 new_ptr_info.size = .Slice;
666657 return @Type(.{ .Pointer = new_ptr_info });
667658 },
668 else => @compileError("invalid type given to std.mem.Span"),
659 else => {},
669660 }
661 @compileError("invalid type given to std.mem.span: " ++ @typeName(T));
670662}
671663
672664test "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);
685665 try testing.expect(Span([*:1]u16) == [:1]u16);
686666 try testing.expect(Span(?[*:1]u16) == ?[:1]u16);
687667 try testing.expect(Span([*:1]const u8) == [:1]const u8);
......@@ -692,13 +672,10 @@ test "Span" {
692672 try testing.expect(Span(?[*c]const u8) == ?[:0]const u8);
693673}
694674
695/// Takes a pointer to an array, a sentinel-terminated pointer, or a slice, and
696/// returns a slice. If there is a sentinel on the input type, there will be a
697/// sentinel on the output type. The constness of the output type matches
698/// the constness of the input type.
699///
700/// When there is both a sentinel and an array length or slice length, the
701/// length value is used instead of the sentinel.
675/// Takes a sentinel-terminated pointer and returns a slice, iterating over the
676/// memory to find the sentinel and determine the length.
677/// Ponter attributes such as const are preserved.
678/// `[*c]` pointers are assumed to be non-null and 0-terminated.
702679pub fn span(ptr: anytype) Span(@TypeOf(ptr)) {
703680 if (@typeInfo(@TypeOf(ptr)) == .Optional) {
704681 if (ptr) |non_null| {
......@@ -722,7 +699,6 @@ test "span" {
722699 var array: [5]u16 = [_]u16{ 1, 2, 3, 4, 5 };
723700 const ptr = @as([*:3]u16, array[0..2 :3]);
724701 try testing.expect(eql(u16, span(ptr), &[_]u16{ 1, 2 }));
725 try testing.expect(eql(u16, span(&array), &[_]u16{ 1, 2, 3, 4, 5 }));
726702 try testing.expectEqual(@as(?[:0]u16, null), span(@as(?[*:0]u16, null)));
727703}
728704
......@@ -919,22 +895,15 @@ test "lenSliceTo" {
919895 }
920896}
921897
922/// Takes a pointer to an array, an array, a vector, a sentinel-terminated pointer,
923/// a slice or a tuple, and returns the length.
924/// In the case of a sentinel-terminated array, it uses the array length.
925/// For C pointers it assumes it is a pointer-to-many with a 0 sentinel.
898/// Takes a sentinel-terminated pointer and iterates over the memory to find the
899/// sentinel and determine the length.
900/// `[*c]` pointers are assumed to be non-null and 0-terminated.
926901pub fn len(value: anytype) usize {
927 return switch (@typeInfo(@TypeOf(value))) {
928 .Array => |info| info.len,
929 .Vector => |info| info.len,
902 switch (@typeInfo(@TypeOf(value))) {
930903 .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 },
935904 .Many => {
936905 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)));
938907 const sentinel = @ptrCast(*align(1) const info.child, sentinel_ptr).*;
939908 return indexOfSentinel(info.child, sentinel, value);
940909 },
......@@ -942,41 +911,18 @@ pub fn len(value: anytype) usize {
942911 assert(value != null);
943912 return indexOfSentinel(info.child, 0, value);
944913 },
945 .Slice => value.len,
914 else => @compileError("invalid type given to std.mem.len: " ++ @typeName(@TypeOf(value))),
946915 },
947 .Struct => |info| if (info.is_tuple) {
948 return info.fields.len;
949 } else @compileError("invalid type given to std.mem.len"),
950 else => @compileError("invalid type given to std.mem.len"),
951 };
916 else => @compileError("invalid type given to std.mem.len: " ++ @typeName(@TypeOf(value))),
917 }
952918}
953919
954920test "len" {
955 try testing.expect(len("aoeu") == 4);
956
957 {
958 var array: [5]u16 = [_]u16{ 1, 2, 3, 4, 5 };
959 try testing.expect(len(&array) == 5);
960 try testing.expect(len(array[0..3]) == 3);
961 array[2] = 0;
962 const ptr = @as([*:0]u16, array[0..2 :0]);
963 try testing.expect(len(ptr) == 2);
964 }
965 {
966 var array: [5:0]u16 = [_:0]u16{ 1, 2, 3, 4, 5 };
967 try testing.expect(len(&array) == 5);
968 array[2] = 0;
969 try testing.expect(len(&array) == 5);
970 }
971 {
972 const vector: meta.Vector(2, u32) = [2]u32{ 1, 2 };
973 try testing.expect(len(vector) == 2);
974 }
975 {
976 const tuple = .{ 1, 2 };
977 try testing.expect(len(tuple) == 2);
978 try testing.expect(tuple[0] == 1);
979 }
921 var array: [5]u16 = [_]u16{ 1, 2, 0, 4, 5 };
922 const ptr = @as([*:4]u16, array[0..3 :4]);
923 try testing.expect(len(ptr) == 3);
924 const c_ptr = @as([*c]u16, ptr);
925 try testing.expect(len(c_ptr) == 2);
980926}
981927
982928pub 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 {
550550 exit(0); // TODO choose appropriate exit code
551551 }
552552 if (builtin.os.tag == .wasi) {
553 @breakpoint();
554553 exit(1);
555554 }
556555 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
45144513 var nt_name = windows.UNICODE_STRING{
45154514 .Length = path_len_bytes,
45164515 .MaximumLength = path_len_bytes,
4517 .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w)),
4516 .Buffer = @qualCast([*:0]u16, sub_path_w),
45184517 };
45194518 var attr = windows.OBJECT_ATTRIBUTES{
45204519 .Length = @sizeOf(windows.OBJECT_ATTRIBUTES),
......@@ -6029,7 +6028,7 @@ pub fn sendfile(
60296028 .BADF => unreachable, // Always a race condition.
60306029 .FAULT => unreachable, // Segmentation fault.
60316030 .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
60336032
60346033 .INVAL, .NOSYS => {
60356034 // EINVAL could be any of the following situations:
......@@ -6097,7 +6096,7 @@ pub fn sendfile(
60976096
60986097 .BADF => unreachable, // Always a race condition.
60996098 .FAULT => unreachable, // Segmentation fault.
6100 .NOTCONN => unreachable, // `out_fd` is an unconnected socket.
6099 .NOTCONN => return error.BrokenPipe, // `out_fd` is an unconnected socket
61016100
61026101 .INVAL, .OPNOTSUPP, .NOTSOCK, .NOSYS => {
61036102 // EINVAL could be any of the following situations:
......@@ -6179,7 +6178,7 @@ pub fn sendfile(
61796178 .BADF => unreachable, // Always a race condition.
61806179 .FAULT => unreachable, // Segmentation fault.
61816180 .INVAL => unreachable,
6182 .NOTCONN => unreachable, // `out_fd` is an unconnected socket.
6181 .NOTCONN => return error.BrokenPipe, // `out_fd` is an unconnected socket
61836182
61846183 .OPNOTSUPP, .NOTSOCK, .NOSYS => break :sf,
61856184
......@@ -6473,7 +6472,7 @@ pub fn recvfrom(
64736472 .BADF => unreachable, // always a race condition
64746473 .FAULT => unreachable,
64756474 .INVAL => unreachable,
6476 .NOTCONN => unreachable,
6475 .NOTCONN => return error.SocketNotConnected,
64776476 .NOTSOCK => unreachable,
64786477 .INTR => continue,
64796478 .AGAIN => return error.WouldBlock,
lib/std/os/uefi/pool_allocator.zig+1-1
......@@ -22,7 +22,7 @@ const UefiPoolAllocator = struct {
2222
2323 assert(len > 0);
2424
25 const ptr_align = 1 << log2_ptr_align;
25 const ptr_align = @as(usize, 1) << @intCast(Allocator.Log2Align, log2_ptr_align);
2626
2727 const metadata_len = mem.alignForward(@sizeOf(usize), ptr_align);
2828
lib/std/os/windows.zig+7-7
......@@ -85,7 +85,7 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN
8585 var nt_name = UNICODE_STRING{
8686 .Length = path_len_bytes,
8787 .MaximumLength = path_len_bytes,
88 .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w.ptr)),
88 .Buffer = @qualCast([*]u16, sub_path_w.ptr),
8989 };
9090 var attr = OBJECT_ATTRIBUTES{
9191 .Length = @sizeOf(OBJECT_ATTRIBUTES),
......@@ -634,7 +634,7 @@ pub fn SetCurrentDirectory(path_name: []const u16) SetCurrentDirectoryError!void
634634 var nt_name = UNICODE_STRING{
635635 .Length = path_len_bytes,
636636 .MaximumLength = path_len_bytes,
637 .Buffer = @intToPtr([*]u16, @ptrToInt(path_name.ptr)),
637 .Buffer = @qualCast([*]u16, path_name.ptr),
638638 };
639639
640640 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
766766 var nt_name = UNICODE_STRING{
767767 .Length = path_len_bytes,
768768 .MaximumLength = path_len_bytes,
769 .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w.ptr)),
769 .Buffer = @qualCast([*]u16, sub_path_w.ptr),
770770 };
771771 var attr = OBJECT_ATTRIBUTES{
772772 .Length = @sizeOf(OBJECT_ATTRIBUTES),
......@@ -876,7 +876,7 @@ pub fn DeleteFile(sub_path_w: []const u16, options: DeleteFileOptions) DeleteFil
876876 .Length = path_len_bytes,
877877 .MaximumLength = path_len_bytes,
878878 // 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),
880880 };
881881
882882 if (sub_path_w[0] == '.' and sub_path_w[1] == 0) {
......@@ -1414,7 +1414,7 @@ pub fn sendmsg(
14141414}
14151415
14161416pub 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) };
14181418 var bytes_send: DWORD = undefined;
14191419 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) {
14201420 return ws2_32.SOCKET_ERROR;
......@@ -1876,13 +1876,13 @@ pub fn eqlIgnoreCaseWTF16(a: []const u16, b: []const u16) bool {
18761876 const a_string = UNICODE_STRING{
18771877 .Length = a_bytes,
18781878 .MaximumLength = a_bytes,
1879 .Buffer = @intToPtr([*]u16, @ptrToInt(a.ptr)),
1879 .Buffer = @qualCast([*]u16, a.ptr),
18801880 };
18811881 const b_bytes = @intCast(u16, b.len * 2);
18821882 const b_string = UNICODE_STRING{
18831883 .Length = b_bytes,
18841884 .MaximumLength = b_bytes,
1885 .Buffer = @intToPtr([*]u16, @ptrToInt(b.ptr)),
1885 .Buffer = @qualCast([*]u16, b.ptr),
18861886 };
18871887 return ntdll.RtlEqualUnicodeString(&a_string, &b_string, TRUE) == TRUE;
18881888}
lib/std/start_windows_tls.zig+5-3
......@@ -7,12 +7,14 @@ export var _tls_end: u8 linksection(".tls$ZZZ") = 0;
77export var __xl_a: std.os.windows.PIMAGE_TLS_CALLBACK linksection(".CRT$XLA") = null;
88export var __xl_z: std.os.windows.PIMAGE_TLS_CALLBACK linksection(".CRT$XLZ") = null;
99
10const tls_array: u32 = 0x2c;
1110comptime {
12 if (builtin.target.cpu.arch == .x86) {
11 if (builtin.target.cpu.arch == .x86 and builtin.zig_backend != .stage2_c) {
1312 // The __tls_array is the offset of the ThreadLocalStoragePointer field
1413 // 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 );
1618 }
1719}
1820
lib/std/std.zig+9-1
......@@ -9,6 +9,7 @@ pub const AutoArrayHashMapUnmanaged = array_hash_map.AutoArrayHashMapUnmanaged;
99pub const AutoHashMap = hash_map.AutoHashMap;
1010pub const AutoHashMapUnmanaged = hash_map.AutoHashMapUnmanaged;
1111pub const BoundedArray = @import("bounded_array.zig").BoundedArray;
12pub const Build = @import("Build.zig");
1213pub const BufMap = @import("buf_map.zig").BufMap;
1314pub const BufSet = @import("buf_set.zig").BufSet;
1415pub const ChildProcess = @import("child_process.zig").ChildProcess;
......@@ -49,7 +50,6 @@ pub const array_hash_map = @import("array_hash_map.zig");
4950pub const atomic = @import("atomic.zig");
5051pub const base64 = @import("base64.zig");
5152pub const bit_set = @import("bit_set.zig");
52pub const build = @import("build.zig");
5353pub const builtin = @import("builtin.zig");
5454pub const c = @import("c.zig");
5555pub const coff = @import("coff.zig");
......@@ -96,6 +96,9 @@ pub const wasm = @import("wasm.zig");
9696pub const zig = @import("zig.zig");
9797pub const start = @import("start.zig");
9898
99/// deprecated: use `Build`.
100pub const build = Build;
101
99102const root = @import("root");
100103const options_override = if (@hasDecl(root, "std_options")) root.std_options else struct {};
101104
......@@ -150,6 +153,11 @@ pub const options = struct {
150153 else
151154 log.defaultLog;
152155
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
153161 pub const cryptoRandomSeed: fn (buffer: []u8) void = if (@hasDecl(options_override, "cryptoRandomSeed"))
154162 options_override.cryptoRandomSeed
155163 else
lib/std/tar.zig+23
......@@ -1,6 +1,18 @@
11pub const Options = struct {
22 /// Number of directory levels to skip when extracting files.
33 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 };
416};
517
618pub const Header = struct {
......@@ -72,6 +84,17 @@ pub const Header = struct {
7284};
7385
7486pub 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 }
7598 var file_name_buffer: [255]u8 = undefined;
7699 var buffer: [512 * 8]u8 = undefined;
77100 var start: usize = 0;
lib/std/target.zig+556-4
......@@ -702,9 +702,6 @@ pub const Target = struct {
702702 pub const ShiftInt = std.math.Log2Int(usize);
703703
704704 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 }
708705
709706 pub fn isEmpty(set: Set) bool {
710707 return for (set.ints) |x| {
......@@ -787,7 +784,7 @@ pub const Target = struct {
787784 return struct {
788785 /// Populates only the feature bits specified.
789786 pub fn featureSet(features: []const F) Set {
790 var x = Set.empty_workaround(); // TODO remove empty_workaround
787 var x = Set.empty;
791788 for (features) |feature| {
792789 x.addFeature(@enumToInt(feature));
793790 }
......@@ -1907,6 +1904,561 @@ pub const Target = struct {
19071904 => 16,
19081905 };
19091906 }
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 }
19102462};
19112463
19122464test {
lib/std/testing.zig+246
......@@ -670,6 +670,252 @@ pub fn expectStringEndsWith(actual: []const u8, expected_ends_with: []const u8)
670670 return error.TestExpectedEndsWith;
671671}
672672
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)
685pub 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
857test "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
882test "expectEqualDeep pointer" {
883 const a = 1;
884 const b = 1;
885 try expectEqualDeep(&a, &b);
886}
887
888test "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
673919fn printIndicatorLine(source: []const u8, indicator_index: usize) void {
674920 const line_begin_index = if (std.mem.lastIndexOfScalar(u8, source[0..indicator_index], '\n')) |line_begin|
675921 line_begin + 1
lib/std/zig.zig-1
......@@ -8,7 +8,6 @@ pub const Tokenizer = tokenizer.Tokenizer;
88pub const fmtId = fmt.fmtId;
99pub const fmtEscapes = fmt.fmtEscapes;
1010pub const isValidId = fmt.isValidId;
11pub const parse = @import("zig/parse.zig").parse;
1211pub const string_literal = @import("zig/string_literal.zig");
1312pub const number_literal = @import("zig/number_literal.zig");
1413pub const primitives = @import("zig/primitives.zig");
lib/std/zig/Ast.zig+73-9
......@@ -1,4 +1,8 @@
11//! 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.
26
37/// Reference to externally-owned data.
48source: [:0]const u8,
......@@ -11,13 +15,6 @@ extra_data: []Node.Index,
1115
1216errors: []const Error,
1317
14const std = @import("../std.zig");
15const assert = std.debug.assert;
16const testing = std.testing;
17const mem = std.mem;
18const Token = std.zig.Token;
19const Ast = @This();
20
2118pub const TokenIndex = u32;
2219pub const ByteOffset = u32;
2320
......@@ -34,7 +31,7 @@ pub const Location = struct {
3431 line_end: usize,
3532};
3633
37pub fn deinit(tree: *Ast, gpa: mem.Allocator) void {
34pub fn deinit(tree: *Ast, gpa: Allocator) void {
3835 tree.tokens.deinit(gpa);
3936 tree.nodes.deinit(gpa);
4037 gpa.free(tree.extra_data);
......@@ -48,11 +45,69 @@ pub const RenderError = error{
4845 OutOfMemory,
4946};
5047
48pub 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.
52pub 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
51106/// `gpa` is used for allocating the resulting formatted source code, as well as
52107/// for allocating extra stack memory if needed, because this function utilizes recursion.
53108/// Note: that's not actually true yet, see https://github.com/ziglang/zig/issues/1006.
54109/// Caller owns the returned slice of bytes, allocated with `gpa`.
55pub fn render(tree: Ast, gpa: mem.Allocator) RenderError![]u8 {
110pub fn render(tree: Ast, gpa: Allocator) RenderError![]u8 {
56111 var buffer = std.ArrayList(u8).init(gpa);
57112 defer buffer.deinit();
58113
......@@ -3347,3 +3402,12 @@ pub const Node = struct {
33473402 rparen: TokenIndex,
33483403 };
33493404};
3405
3406const std = @import("../std.zig");
3407const assert = std.debug.assert;
3408const testing = std.testing;
3409const mem = std.mem;
3410const Token = std.zig.Token;
3411const Ast = @This();
3412const Allocator = std.mem.Allocator;
3413const 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
3pub const Error = error{ParseError} || Allocator.Error;
4
5gpa: Allocator,
6source: []const u8,
7token_tags: []const Token.Tag,
8token_starts: []const Ast.ByteOffset,
9tok_i: TokenIndex,
10errors: std.ArrayListUnmanaged(AstError),
11nodes: Ast.NodeList,
12extra_data: std.ArrayListUnmanaged(Node.Index),
13scratch: std.ArrayListUnmanaged(Node.Index),
14
15const SmallSpan = union(enum) {
16 zero_or_one: Node.Index,
17 multi: Node.SubRange,
18};
19
20const 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
36fn 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
44fn 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
50fn 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
55fn 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
61fn 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
72fn 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
83fn 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
92fn 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
97fn 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
141fn 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
146fn 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
155fn 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
162pub 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.
183pub 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
215fn 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
482fn 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
540fn 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
568fn 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
589fn 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
603fn 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
676fn 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
686fn 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
700fn 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
711fn 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
794fn 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)?
868fn 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
929fn 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
1021fn 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.
1032fn 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 )
1051fn 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)
1110fn 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)
1137fn 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 )
1157fn 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 )
1219fn 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
1310fn 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
1323fn 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
1332fn 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
1370fn 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
1405fn 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
1413fn parseExpr(p: *Parse) Error!Node.Index {
1414 return p.parseExprPrecedence(0);
1415}
1416
1417fn 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
1426const Assoc = enum {
1427 left,
1428 none,
1429};
1430
1431const 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.
1440const 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
1479fn 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
1549fn 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
1570fn 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
1595fn 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
1867fn 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
1887fn 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)?
2002fn parseIfExpr(p: *Parse) !Node.Index {
2003 return p.parseIf(expectExpr);
2004}
2005
2006/// Block <- LBRACE Statement* RBRACE
2007fn 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)?
2062fn 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)?
2098fn 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
2153fn 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)?
2250fn 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?
2271fn 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)
2428fn 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
2747fn 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)?
2758fn 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)?
2794fn 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
2844fn 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?
2880fn 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
2955fn 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
2981fn 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
3000fn parseBreakLabel(p: *Parse) !TokenIndex {
3001 _ = p.eatToken(.colon) orelse return @as(TokenIndex, 0);
3002 return p.expectToken(.identifier);
3003}
3004
3005/// BlockLabel <- IDENTIFIER COLON
3006fn 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
3018fn 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
3030fn 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
3041fn 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
3056fn 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
3065fn 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
3074fn 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
3093fn 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
3118fn 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
3126fn 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
3137fn 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
3153fn 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)?
3204fn 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
3221const 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
3228fn 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
3294fn 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)?
3410fn 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 {};'.
3544fn 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
3576fn 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?
3585fn 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?
3607fn 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?
3646fn 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?
3719fn 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.
3755fn 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
3771fn 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
3775fn eatToken(p: *Parse, tag: Token.Tag) ?TokenIndex {
3776 return if (p.token_tags[p.tok_i] == tag) p.nextToken() else null;
3777}
3778
3779fn assertToken(p: *Parse, tag: Token.Tag) TokenIndex {
3780 const token = p.nextToken();
3781 assert(p.token_tags[token] == tag);
3782 return token;
3783}
3784
3785fn 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
3796fn 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
3805fn nextToken(p: *Parse) TokenIndex {
3806 const result = p.tok_i;
3807 p.tok_i += 1;
3808 return result;
3809}
3810
3811const null_node: Node.Index = 0;
3812
3813const Parse = @This();
3814const std = @import("../std.zig");
3815const assert = std.debug.assert;
3816const Allocator = std.mem.Allocator;
3817const Ast = std.zig.Ast;
3818const Node = Ast.Node;
3819const AstError = Ast.Error;
3820const TokenIndex = Ast.TokenIndex;
3821const Token = std.zig.Token;
3822
3823test {
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 {
7575 const source = ptrInfo(@TypeOf(target));
7676
7777 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)
7979 else if (@typeInfo(dest.child) == .Opaque)
8080 // dest.alignment would error out
8181 return @ptrCast(DestType, target)
lib/std/zig/parse.zig deleted-3852
......@@ -1,3852 +0,0 @@
1const std = @import("../std.zig");
2const assert = std.debug.assert;
3const Allocator = std.mem.Allocator;
4const Ast = std.zig.Ast;
5const Node = Ast.Node;
6const AstError = Ast.Error;
7const TokenIndex = Ast.TokenIndex;
8const Token = std.zig.Token;
9
10pub 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.
14pub 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
65const null_node: Node.Index = 0;
66
67/// Represents in-progress parsing, will be converted to an Ast after completion.
68const 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
3850test {
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" {
186186 );
187187}
188188
189test "zig fmt: file ends in multi line comment" {
190 try testTransform(
191 \\ \\foobar
192 ,
193 \\\\foobar
194 \\
195 );
196}
197
189198test "zig fmt: file ends in comment after var decl" {
190199 try testTransform(
191200 \\const x = 42;
......@@ -6064,7 +6073,7 @@ var fixed_buffer_mem: [100 * 1024]u8 = undefined;
60646073fn testParse(source: [:0]const u8, allocator: mem.Allocator, anything_changed: *bool) ![]u8 {
60656074 const stderr = io.getStdErr().writer();
60666075
6067 var tree = try std.zig.parse(allocator, source);
6076 var tree = try std.zig.Ast.parse(allocator, source, .zig);
60686077 defer tree.deinit(allocator);
60696078
60706079 for (tree.errors) |parse_error| {
......@@ -6115,7 +6124,7 @@ fn testCanonical(source: [:0]const u8) !void {
61156124const Error = std.zig.Ast.Error.Tag;
61166125
61176126fn 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);
61196128 defer tree.deinit(std.testing.allocator);
61206129
61216130 std.testing.expectEqual(expected_errors.len, tree.errors.len) catch |err| {
lib/std/zig/perf_test.zig+1-2
......@@ -1,7 +1,6 @@
11const std = @import("std");
22const mem = std.mem;
33const Tokenizer = std.zig.Tokenizer;
4const Parser = std.zig.Parser;
54const io = std.io;
65const fmtIntSizeBin = std.fmt.fmtIntSizeBin;
76
......@@ -34,6 +33,6 @@ pub fn main() !void {
3433fn testOnce() usize {
3534 var fixed_buf_alloc = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
3635 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");
3837 return fixed_buf_alloc.end_index;
3938}
lib/std/zig/render.zig+1-2
......@@ -2759,8 +2759,7 @@ fn tokenSliceForRender(tree: Ast, token_index: Ast.TokenIndex) []const u8 {
27592759 var ret = tree.tokenSlice(token_index);
27602760 switch (tree.tokens.items(.tag)[token_index]) {
27612761 .multiline_string_literal_line => {
2762 assert(ret[ret.len - 1] == '\n');
2763 ret.len -= 1;
2762 if (ret[ret.len - 1] == '\n') ret.len -= 1;
27642763 },
27652764 .container_doc_comment, .doc_comment => {
27662765 ret = mem.trimRight(u8, ret, &std.ascii.whitespace);
lib/test_runner.zig+1-1
......@@ -11,7 +11,7 @@ var log_err_count: usize = 0;
1111
1212pub fn main() void {
1313 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
1515 builtin.zig_backend != .stage2_c)
1616 {
1717 return main2() catch @panic("test failure");
lib/zig.h+24
......@@ -93,6 +93,14 @@ typedef char bool;
9393#define zig_align zig_align_unavailable
9494#endif
9595
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
96104#if zig_has_attribute(aligned)
97105#define zig_align_fn(alignment) __attribute__((aligned(alignment)))
98106#elif _MSC_VER
......@@ -101,6 +109,22 @@ typedef char bool;
101109#define zig_align_fn zig_align_fn_unavailable
102110#endif
103111
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
104128#if zig_has_builtin(unreachable) || defined(zig_gnuc)
105129#define zig_unreachable() __builtin_unreachable()
106130#else
src/AstGen.zig+37-2
......@@ -2530,6 +2530,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
25302530 .bit_size_of,
25312531 .typeof_log2_int_type,
25322532 .ptr_to_int,
2533 .qual_cast,
25332534 .align_of,
25342535 .bool_to_int,
25352536 .embed_file,
......@@ -4278,7 +4279,34 @@ fn testDecl(
42784279 var num_namespaces_out: u32 = 0;
42794280 var capturing_namespace: ?*Scope.Namespace = null;
42804281 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 },
42824310 .gen_zir => s = s.cast(GenZir).?.parent,
42834311 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
42844312 .namespace, .enum_namespace => {
......@@ -8010,6 +8038,7 @@ fn builtinCall(
80108038 .float_cast => return typeCast(gz, scope, ri, node, params[0], params[1], .float_cast),
80118039 .int_cast => return typeCast(gz, scope, ri, node, params[0], params[1], .int_cast),
80128040 .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),
80138042 .truncate => return typeCast(gz, scope, ri, node, params[0], params[1], .truncate),
80148043 // zig fmt: on
80158044
......@@ -8692,6 +8721,7 @@ fn callExpr(
86928721 defer arg_block.unstack();
86938722
86948723 // `call_inst` is reused to provide the param type.
8724 arg_block.rl_ty_inst = call_inst;
86958725 const arg_ref = try expr(&arg_block, &arg_block.base, .{ .rl = .{ .coerced_ty = call_inst }, .ctx = .fn_arg }, param_node);
86968726 _ = try arg_block.addBreak(.break_inline, call_index, arg_ref);
86978727
......@@ -10840,7 +10870,12 @@ const GenZir = struct {
1084010870 // we emit ZIR for the block break instructions to have the result values,
1084110871 // and then rvalue() on that to pass the value to the result location.
1084210872 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| {
1084410879 gz.rl_ty_inst = ty_inst;
1084510880 gz.break_result_info = parent_ri;
1084610881 },
src/Autodoc.zig+6-11
......@@ -1400,6 +1400,7 @@ fn walkInstruction(
14001400 .float_cast,
14011401 .int_cast,
14021402 .ptr_cast,
1403 .qual_cast,
14031404 .truncate,
14041405 .align_cast,
14051406 .has_decl,
......@@ -2200,17 +2201,10 @@ fn walkInstruction(
22002201 false,
22012202 );
22022203
2203 _ = operand;
2204
2205 // WIP
2206
2207 printWithContext(
2208 file,
2209 inst_index,
2210 "TODO: implement `{s}` for walkInstruction\n\n",
2211 .{@tagName(tags[inst_index])},
2212 );
2213 return self.cteTodo(@tagName(tags[inst_index]));
2204 return DocData.WalkResult{
2205 .typeRef = operand.expr,
2206 .expr = .{ .@"struct" = &.{} },
2207 };
22142208 },
22152209 .struct_init_anon => {
22162210 const pl_node = data[inst_index].pl_node;
......@@ -2537,6 +2531,7 @@ fn walkInstruction(
25372531 const var_init_ref = @intToEnum(Ref, file.zir.extra[extra_index]);
25382532 const var_init = try self.walkRef(file, parent_scope, parent_src, var_init_ref, need_type);
25392533 value.expr = var_init.expr;
2534 value.typeRef = var_init.typeRef;
25402535 }
25412536
25422537 return value;
src/BuiltinFn.zig+8
......@@ -75,6 +75,7 @@ pub const Tag = enum {
7575 prefetch,
7676 ptr_cast,
7777 ptr_to_int,
78 qual_cast,
7879 rem,
7980 return_address,
8081 select,
......@@ -674,6 +675,13 @@ pub const list = list: {
674675 .param_count = 1,
675676 },
676677 },
678 .{
679 "@qualCast",
680 .{
681 .tag = .qual_cast,
682 .param_count = 2,
683 },
684 },
677685 .{
678686 "@rem",
679687 .{
src/Compilation.zig+2-2
......@@ -385,7 +385,7 @@ pub const AllErrors = struct {
385385 count: u32 = 1,
386386 /// Does not include the trailing newline.
387387 source_line: ?[]const u8,
388 notes: []Message = &.{},
388 notes: []const Message = &.{},
389389 reference_trace: []Message = &.{},
390390
391391 /// Splits the error message up into lines to properly indent them
......@@ -3299,7 +3299,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void {
32993299 const gpa = comp.gpa;
33003300 const module = comp.bin_file.options.module.?;
33013301 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| {
33033303 try module.failed_decls.ensureUnusedCapacity(gpa, 1);
33043304 module.failed_decls.putAssumeCapacityNoClobber(decl_index, try Module.ErrorMsg.create(
33053305 gpa,
src/Manifest.zig created+499
......@@ -0,0 +1,499 @@
1pub const basename = "build.zig.zon";
2pub const Hash = std.crypto.hash.sha2.Sha256;
3
4pub const Dependency = struct {
5 url: []const u8,
6 url_tok: Ast.TokenIndex,
7 hash: ?[]const u8,
8 hash_tok: Ast.TokenIndex,
9};
10
11pub const ErrorMessage = struct {
12 msg: []const u8,
13 tok: Ast.TokenIndex,
14 off: u32,
15};
16
17pub 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
35pub const multihash_function: MultihashFunction = switch (Hash) {
36 std.crypto.hash.sha2.Sha256 => .@"sha2-256",
37 else => @compileError("unreachable"),
38};
39comptime {
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}
45pub const multihash_len = 1 + 1 + Hash.digest_length;
46
47name: []const u8,
48version: std.SemanticVersion,
49dependencies: std.StringArrayHashMapUnmanaged(Dependency),
50
51errors: []ErrorMessage,
52arena_state: std.heap.ArenaAllocator.State,
53
54pub const Error = Allocator.Error;
55
56pub 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
94pub fn deinit(man: *Manifest, gpa: Allocator) void {
95 man.arena_state.promote(gpa).deinit();
96 man.* = undefined;
97}
98
99const hex_charset = "0123456789abcdef";
100
101pub 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
112test hex64 {
113 const s = "[" ++ hex64(0x12345678_abcdef00) ++ "]";
114 try std.testing.expectEqualStrings("[00efcdab78563412]", s);
115}
116
117pub 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
133const 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
449const Manifest = @This();
450const std = @import("std");
451const mem = std.mem;
452const Allocator = std.mem.Allocator;
453const assert = std.debug.assert;
454const Ast = std.zig.Ast;
455const testing = std.testing;
456
457test "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;
328328pub const Export = struct {
329329 options: std.builtin.ExportOptions,
330330 src: LazySrcLoc,
331 /// Represents the position of the export, if any, in the output file.
332 link: link.File.Export,
333331 /// The Decl that performs the export. Note that this is *not* the Decl being exported.
334332 owner_decl: Decl.Index,
335333 /// The Decl containing the export statement. Inline function calls
......@@ -533,16 +531,8 @@ pub const Decl = struct {
533531 /// What kind of a declaration is this.
534532 kind: Kind,
535533
536 /// Represents the position of the code in the output file.
537 /// This is populated regardless of semantic analysis and code generation.
538 link: link.File.LinkBlock,
539
540 /// Represents the function in the linked output file, if the `Decl` is a function.
541 /// This is stored here and not in `Fn` because `Decl` survives across updates but
542 /// `Fn` does not.
543 /// TODO Look into making `Fn` a longer lived structure and moving this field there
544 /// to save on memory usage.
545 fn_link: link.File.LinkFn,
534 /// TODO remove this once Wasm backend catches up
535 fn_link: ?link.File.Wasm.FnData = null,
546536
547537 /// The shallow set of other decls whose typed_value could possibly change if this Decl's
548538 /// typed_value is modified.
......@@ -2067,7 +2057,7 @@ pub const File = struct {
20672057 if (file.tree_loaded) return &file.tree;
20682058
20692059 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);
20712061 file.tree_loaded = true;
20722062 return &file.tree;
20732063 }
......@@ -3672,7 +3662,7 @@ pub fn astGenFile(mod: *Module, file: *File) !void {
36723662 file.source = source;
36733663 file.source_loaded = true;
36743664
3675 file.tree = try std.zig.parse(gpa, source);
3665 file.tree = try Ast.parse(gpa, source, .zig);
36763666 defer if (!file.tree_loaded) file.tree.deinit(gpa);
36773667
36783668 if (file.tree.errors.len != 0) {
......@@ -3987,7 +3977,7 @@ pub fn populateBuiltinFile(mod: *Module) !void {
39873977 else => |e| return e,
39883978 }
39893979
3990 file.tree = try std.zig.parse(gpa, file.source);
3980 file.tree = try Ast.parse(gpa, file.source, .zig);
39913981 file.tree_loaded = true;
39923982 assert(file.tree.errors.len == 0); // builtin.zig must parse
39933983
......@@ -4098,7 +4088,7 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl_index: Decl.Index) SemaError!void {
40984088
40994089 // The exports this Decl performs will be re-discovered, so we remove them here
41004090 // prior to re-analysis.
4101 mod.deleteDeclExports(decl_index);
4091 try mod.deleteDeclExports(decl_index);
41024092
41034093 // Similarly, `@setAlignStack` invocations will be re-discovered.
41044094 if (decl.getFunction()) |func| {
......@@ -4585,7 +4575,6 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {
45854575 // We don't fully codegen the decl until later, but we do need to reserve a global
45864576 // offset table index for it. This allows us to codegen decls out of dependency
45874577 // order, increasing how many computations can be done in parallel.
4588 try mod.comp.bin_file.allocateDeclIndexes(decl_index);
45894578 try mod.comp.work_queue.writeItem(.{ .codegen_func = func });
45904579 if (type_changed and mod.emit_h != null) {
45914580 try mod.comp.work_queue.writeItem(.{ .emit_h_decl = decl_index });
......@@ -4697,7 +4686,6 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {
46974686 // codegen backend wants full access to the Decl Type.
46984687 try sema.resolveTypeFully(decl.ty);
46994688
4700 try mod.comp.bin_file.allocateDeclIndexes(decl_index);
47014689 try mod.comp.work_queue.writeItem(.{ .codegen_decl = decl_index });
47024690
47034691 if (type_changed and mod.emit_h != null) {
......@@ -5185,20 +5173,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) Allocator.Err
51855173 decl.zir_decl_index = @intCast(u32, decl_sub_index);
51865174 if (decl.getFunction()) |_| {
51875175 switch (comp.bin_file.tag) {
5188 .coff => {
5189 // TODO Implement for COFF
5190 },
5191 .elf => if (decl.fn_link.elf.len != 0) {
5192 // TODO Look into detecting when this would be unnecessary by storing enough state
5193 // in `Decl` to notice that the line number did not change.
5194 comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl_index });
5195 },
5196 .macho => if (decl.fn_link.macho.len != 0) {
5197 // TODO Look into detecting when this would be unnecessary by storing enough state
5198 // in `Decl` to notice that the line number did not change.
5199 comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl_index });
5200 },
5201 .plan9 => {
5176 .coff, .elf, .macho, .plan9 => {
52025177 // TODO Look into detecting when this would be unnecessary by storing enough state
52035178 // in `Decl` to notice that the line number did not change.
52045179 comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl_index });
......@@ -5267,33 +5242,15 @@ pub fn clearDecl(
52675242 assert(emit_h.decl_table.swapRemove(decl_index));
52685243 }
52695244 _ = mod.compile_log_decls.swapRemove(decl_index);
5270 mod.deleteDeclExports(decl_index);
5245 try mod.deleteDeclExports(decl_index);
52715246
52725247 if (decl.has_tv) {
52735248 if (decl.ty.isFnOrHasRuntimeBits()) {
52745249 mod.comp.bin_file.freeDecl(decl_index);
52755250
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 };
52885251 decl.fn_link = switch (mod.comp.bin_file.tag) {
5289 .coff => .{ .coff = {} },
5290 .elf => .{ .elf = link.File.Dwarf.SrcFn.empty },
5291 .macho => .{ .macho = link.File.Dwarf.SrcFn.empty },
5292 .plan9 => .{ .plan9 = {} },
5293 .c => .{ .c = {} },
5294 .wasm => .{ .wasm = link.File.Wasm.FnData.empty },
5295 .spirv => .{ .spirv = .{} },
5296 .nvptx => .{ .nvptx = {} },
5252 .wasm => link.File.Wasm.FnData.empty,
5253 else => null,
52975254 };
52985255 }
52995256 if (decl.getInnerNamespace()) |namespace| {
......@@ -5315,23 +5272,6 @@ pub fn deleteUnusedDecl(mod: *Module, decl_index: Decl.Index) void {
53155272 const decl = mod.declPtr(decl_index);
53165273 log.debug("deleteUnusedDecl {d} ({s})", .{ decl_index, decl.name });
53175274
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
53355275 assert(!mod.declIsRoot(decl_index));
53365276 assert(decl.src_namespace.anon_decls.swapRemove(decl_index));
53375277
......@@ -5377,7 +5317,7 @@ pub fn abortAnonDecl(mod: *Module, decl_index: Decl.Index) void {
53775317
53785318/// Delete all the Export objects that are caused by this Decl. Re-analysis of
53795319/// this Decl will cause them to be re-created (or not).
5380fn deleteDeclExports(mod: *Module, decl_index: Decl.Index) void {
5320fn deleteDeclExports(mod: *Module, decl_index: Decl.Index) Allocator.Error!void {
53815321 var export_owners = (mod.export_owners.fetchSwapRemove(decl_index) orelse return).value;
53825322
53835323 for (export_owners.items) |exp| {
......@@ -5400,16 +5340,16 @@ fn deleteDeclExports(mod: *Module, decl_index: Decl.Index) void {
54005340 }
54015341 }
54025342 if (mod.comp.bin_file.cast(link.File.Elf)) |elf| {
5403 elf.deleteExport(exp.link.elf);
5343 elf.deleteDeclExport(decl_index, exp.options.name);
54045344 }
54055345 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);
54075347 }
54085348 if (mod.comp.bin_file.cast(link.File.Wasm)) |wasm| {
5409 wasm.deleteExport(exp.link.wasm);
5349 wasm.deleteDeclExport(decl_index);
54105350 }
54115351 if (mod.comp.bin_file.cast(link.File.Coff)) |coff| {
5412 coff.deleteExport(exp.link.coff);
5352 coff.deleteDeclExport(decl_index, exp.options.name);
54135353 }
54145354 if (mod.failed_exports.fetchSwapRemove(exp)) |failed_kv| {
54155355 failed_kv.value.destroy(mod.gpa);
......@@ -5712,25 +5652,9 @@ pub fn allocateNewDecl(
57125652 .deletion_flag = false,
57135653 .zir_decl_index = 0,
57145654 .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 },
57255655 .fn_link = switch (mod.comp.bin_file.tag) {
5726 .coff => .{ .coff = {} },
5727 .elf => .{ .elf = link.File.Dwarf.SrcFn.empty },
5728 .macho => .{ .macho = link.File.Dwarf.SrcFn.empty },
5729 .plan9 => .{ .plan9 = {} },
5730 .c => .{ .c = {} },
5731 .wasm => .{ .wasm = link.File.Wasm.FnData.empty },
5732 .spirv => .{ .spirv = .{} },
5733 .nvptx => .{ .nvptx = {} },
5656 .wasm => link.File.Wasm.FnData.empty,
5657 else => null,
57345658 },
57355659 .generation = 0,
57365660 .is_pub = false,
......@@ -5816,7 +5740,6 @@ pub fn initNewAnonDecl(
58165740 // the Decl will be garbage collected by the `codegen_decl` task instead of sent
58175741 // to the linker.
58185742 if (typed_value.ty.isFnOrHasRuntimeBits()) {
5819 try mod.comp.bin_file.allocateDeclIndexes(new_decl_index);
58205743 try mod.comp.anon_work_queue.writeItem(.{ .codegen_decl = new_decl_index });
58215744 }
58225745}
src/Package.zig+157-152
......@@ -1,12 +1,13 @@
11const Package = @This();
22
3const builtin = @import("builtin");
34const std = @import("std");
45const fs = std.fs;
56const mem = std.mem;
67const Allocator = mem.Allocator;
78const assert = std.debug.assert;
8const Hash = std.crypto.hash.sha2.Sha256;
99const log = std.log.scoped(.package);
10const main = @import("main.zig");
1011
1112const Compilation = @import("Compilation.zig");
1213const Module = @import("Module.zig");
......@@ -14,6 +15,7 @@ const ThreadPool = @import("ThreadPool.zig");
1415const WaitGroup = @import("WaitGroup.zig");
1516const Cache = @import("Cache.zig");
1617const build_options = @import("build_options");
18const Manifest = @import("Manifest.zig");
1719
1820pub const Table = std.StringHashMapUnmanaged(*Package);
1921
......@@ -140,10 +142,10 @@ pub fn addAndAdopt(parent: *Package, gpa: Allocator, child: *Package) !void {
140142}
141143
142144pub const build_zig_basename = "build.zig";
143pub const ini_basename = build_zig_basename ++ ".ini";
144145
145146pub fn fetchAndAddDependencies(
146147 pkg: *Package,
148 arena: Allocator,
147149 thread_pool: *ThreadPool,
148150 http_client: *std.http.Client,
149151 directory: Compilation.Directory,
......@@ -152,89 +154,77 @@ pub fn fetchAndAddDependencies(
152154 dependencies_source: *std.ArrayList(u8),
153155 build_roots_source: *std.ArrayList(u8),
154156 name_prefix: []const u8,
157 color: main.Color,
155158) !void {
156159 const max_bytes = 10 * 1024 * 1024;
157160 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) {
159169 error.FileNotFound => {
160170 // Handle the same as no dependencies.
161171 return;
162172 },
163173 else => |e| return e,
164174 };
165 defer gpa.free(build_zig_ini);
166175
167 const ini: std.Ini = .{ .bytes = build_zig_ini };
168 var any_error = false;
169 var it = ini.iterateSection("\n[dependency]\n");
170 while (it.next()) |dep| {
171 var line_it = mem.split(u8, dep, "\n");
172 var opt_name: ?[]const u8 = null;
173 var opt_url: ?[]const u8 = null;
174 var expected_hash: ?[]const u8 = null;
175 while (line_it.next()) |kv| {
176 const eq_pos = mem.indexOfScalar(u8, kv, '=') orelse continue;
177 const key = kv[0..eq_pos];
178 const value = kv[eq_pos + 1 ..];
179 if (mem.eql(u8, key, "name")) {
180 opt_name = value;
181 } else if (mem.eql(u8, key, "url")) {
182 opt_url = value;
183 } else if (mem.eql(u8, key, "hash")) {
184 expected_hash = value;
185 } else {
186 const loc = std.zig.findLineColumn(ini.bytes, @ptrToInt(key.ptr) - @ptrToInt(ini.bytes.ptr));
187 std.log.warn("{s}/{s}:{d}:{d} unrecognized key: '{s}'", .{
188 directory.path orelse ".",
189 "build.zig.ini",
190 loc.line,
191 loc.column,
192 key,
193 });
194 }
195 }
176 var ast = try std.zig.Ast.parse(gpa, build_zig_zon_bytes, .zon);
177 defer ast.deinit(gpa);
196178
197 const name = opt_name orelse {
198 const loc = std.zig.findLineColumn(ini.bytes, @ptrToInt(dep.ptr) - @ptrToInt(ini.bytes.ptr));
199 std.log.err("{s}/{s}:{d}:{d} missing key: 'name'", .{
200 directory.path orelse ".",
201 "build.zig.ini",
202 loc.line,
203 loc.column,
204 });
205 any_error = true;
206 continue;
207 };
179 if (ast.errors.len > 0) {
180 const file_path = try directory.join(arena, &.{Manifest.basename});
181 try main.printErrsMsgToStdErr(gpa, arena, ast, file_path, color);
182 return error.PackageFetchFailed;
183 }
208184
209 const url = opt_url orelse {
210 const loc = std.zig.findLineColumn(ini.bytes, @ptrToInt(dep.ptr) - @ptrToInt(ini.bytes.ptr));
211 std.log.err("{s}/{s}:{d}:{d} missing key: 'name'", .{
212 directory.path orelse ".",
213 "build.zig.ini",
214 loc.line,
215 loc.column,
216 });
217 any_error = true;
218 continue;
185 var manifest = try Manifest.parse(gpa, ast);
186 defer manifest.deinit(gpa);
187
188 if (manifest.errors.len > 0) {
189 const ttyconf: std.debug.TTY.Config = switch (color) {
190 .auto => std.debug.detectTTYConfig(std.io.getStdErr()),
191 .on => .escape_codes,
192 .off => .no_color,
219193 };
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 }
220200
221 const sub_prefix = try std.fmt.allocPrint(gpa, "{s}{s}.", .{ name_prefix, name });
222 defer gpa.free(sub_prefix);
201 const report: Report = .{
202 .ast = &ast,
203 .directory = directory,
204 .color = color,
205 .arena = arena,
206 };
207
208 var any_error = false;
209 const deps_list = manifest.dependencies.values();
210 for (manifest.dependencies.keys()) |name, i| {
211 const dep = deps_list[i];
212
213 const sub_prefix = try std.fmt.allocPrint(arena, "{s}{s}.", .{ name_prefix, name });
223214 const fqn = sub_prefix[0 .. sub_prefix.len - 1];
224215
225216 const sub_pkg = try fetchAndUnpack(
226217 thread_pool,
227218 http_client,
228219 global_cache_directory,
229 url,
230 expected_hash,
231 ini,
232 directory,
220 dep,
221 report,
233222 build_roots_source,
234223 fqn,
235224 );
236225
237226 try pkg.fetchAndAddDependencies(
227 arena,
238228 thread_pool,
239229 http_client,
240230 sub_pkg.root_src_directory,
......@@ -243,6 +233,7 @@ pub fn fetchAndAddDependencies(
243233 dependencies_source,
244234 build_roots_source,
245235 sub_prefix,
236 color,
246237 );
247238
248239 try addAndAdopt(pkg, gpa, sub_pkg);
......@@ -252,7 +243,7 @@ pub fn fetchAndAddDependencies(
252243 });
253244 }
254245
255 if (any_error) return error.InvalidBuildZigIniFile;
246 if (any_error) return error.InvalidBuildManifestFile;
256247}
257248
258249pub fn createFilePkg(
......@@ -263,7 +254,7 @@ pub fn createFilePkg(
263254 contents: []const u8,
264255) !*Package {
265256 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);
267258 {
268259 var tmp_dir = try cache_directory.handle.makeOpenPath(tmp_dir_sub_path, .{});
269260 defer tmp_dir.close();
......@@ -281,14 +272,73 @@ pub fn createFilePkg(
281272 return createWithDir(gpa, name, cache_directory, o_dir_sub_path, basename);
282273}
283274
275const 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
284336fn fetchAndUnpack(
285337 thread_pool: *ThreadPool,
286338 http_client: *std.http.Client,
287339 global_cache_directory: Compilation.Directory,
288 url: []const u8,
289 expected_hash: ?[]const u8,
290 ini: std.Ini,
291 comp_directory: Compilation.Directory,
340 dep: Manifest.Dependency,
341 report: Report,
292342 build_roots_source: *std.ArrayList(u8),
293343 fqn: []const u8,
294344) !*Package {
......@@ -297,17 +347,9 @@ fn fetchAndUnpack(
297347
298348 // Check if the expected_hash is already present in the global package
299349 // cache, and thereby avoid both fetching and unpacking.
300 if (expected_hash) |h| cached: {
301 if (h.len != 2 * Hash.digest_length) {
302 return reportError(
303 ini,
304 comp_directory,
305 h.ptr,
306 "wrong hash size. expected: {d}, found: {d}",
307 .{ Hash.digest_length, h.len },
308 );
309 }
310 const hex_digest = h[0 .. 2 * Hash.digest_length];
350 if (dep.hash) |h| cached: {
351 const hex_multihash_len = 2 * Manifest.multihash_len;
352 const hex_digest = h[0..hex_multihash_len];
311353 const pkg_dir_sub_path = "p" ++ s ++ hex_digest;
312354 var pkg_dir = global_cache_directory.handle.openDir(pkg_dir_sub_path, .{}) catch |err| switch (err) {
313355 error.FileNotFound => break :cached,
......@@ -344,10 +386,10 @@ fn fetchAndUnpack(
344386 return ptr;
345387 }
346388
347 const uri = try std.Uri.parse(url);
389 const uri = try std.Uri.parse(dep.url);
348390
349391 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);
351393
352394 const actual_hash = a: {
353395 var tmp_directory: Compilation.Directory = d: {
......@@ -376,13 +418,9 @@ fn fetchAndUnpack(
376418 // by default, so the same logic applies for buffering the reader as for gzip.
377419 try unpackTarball(gpa, &req, tmp_directory.handle, std.compress.xz);
378420 } else {
379 return reportError(
380 ini,
381 comp_directory,
382 uri.path.ptr,
383 "unknown file extension for path '{s}'",
384 .{uri.path},
385 );
421 return report.fail(dep.url_tok, "unknown file extension for path '{s}'", .{
422 uri.path,
423 });
386424 }
387425
388426 // TODO: delete files not included in the package prior to computing the package hash.
......@@ -393,28 +431,21 @@ fn fetchAndUnpack(
393431 break :a try computePackageHash(thread_pool, .{ .dir = tmp_directory.handle });
394432 };
395433
396 const pkg_dir_sub_path = "p" ++ s ++ hexDigest(actual_hash);
434 const pkg_dir_sub_path = "p" ++ s ++ Manifest.hexDigest(actual_hash);
397435 try renameTmpIntoCache(global_cache_directory.handle, tmp_dir_sub_path, pkg_dir_sub_path);
398436
399 if (expected_hash) |h| {
400 const actual_hex = hexDigest(actual_hash);
437 const actual_hex = Manifest.hexDigest(actual_hash);
438 if (dep.hash) |h| {
401439 if (!mem.eql(u8, h, &actual_hex)) {
402 return reportError(
403 ini,
404 comp_directory,
405 h.ptr,
406 "hash mismatch: expected: {s}, found: {s}",
407 .{ h, actual_hex },
408 );
440 return report.fail(dep.hash_tok, "hash mismatch: expected: {s}, found: {s}", .{
441 h, actual_hex,
442 });
409443 }
410444 } else {
411 return reportError(
412 ini,
413 comp_directory,
414 url.ptr,
415 "url field is missing corresponding hash field: hash={s}",
416 .{std.fmt.fmtSliceHexLower(&actual_hash)},
417 );
445 const notes: [1]Compilation.AllErrors.Message = .{.{ .plain = .{
446 .msg = try std.fmt.allocPrint(report.arena, "expected .hash = \"{s}\",", .{&actual_hex}),
447 } }};
448 return report.failWithNotes(&notes, dep.url_tok, "url field is missing corresponding hash field", .{});
418449 }
419450
420451 const build_root = try global_cache_directory.join(gpa, &.{pkg_dir_sub_path});
......@@ -440,35 +471,21 @@ fn unpackTarball(
440471
441472 try std.tar.pipeToFileSystem(out_dir, decompress.reader(), .{
442473 .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,
443480 });
444481}
445482
446fn 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
466483const HashedFile = struct {
467484 path: []const u8,
468 hash: [Hash.digest_length]u8,
485 hash: [Manifest.Hash.digest_length]u8,
469486 failure: Error!void,
470487
471 const Error = fs.File.OpenError || fs.File.ReadError;
488 const Error = fs.File.OpenError || fs.File.ReadError || fs.File.StatError;
472489
473490 fn lessThan(context: void, lhs: *const HashedFile, rhs: *const HashedFile) bool {
474491 _ = context;
......@@ -479,7 +496,7 @@ const HashedFile = struct {
479496fn computePackageHash(
480497 thread_pool: *ThreadPool,
481498 pkg_dir: fs.IterableDir,
482) ![Hash.digest_length]u8 {
499) ![Manifest.Hash.digest_length]u8 {
483500 const gpa = thread_pool.allocator;
484501
485502 // We'll use an arena allocator for the path name strings since they all
......@@ -522,7 +539,7 @@ fn computePackageHash(
522539
523540 std.sort.sort(*HashedFile, all_files.items, {}, HashedFile.lessThan);
524541
525 var hasher = Hash.init(.{});
542 var hasher = Manifest.Hash.init(.{});
526543 var any_failures = false;
527544 for (all_files.items) |hashed_file| {
528545 hashed_file.failure catch |err| {
......@@ -543,7 +560,9 @@ fn workerHashFile(dir: fs.Dir, hashed_file: *HashedFile, wg: *WaitGroup) void {
543560fn hashFileFallible(dir: fs.Dir, hashed_file: *HashedFile) HashedFile.Error!void {
544561 var buf: [8000]u8 = undefined;
545562 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)) });
547566 while (true) {
548567 const bytes_read = try file.read(&buf);
549568 if (bytes_read == 0) break;
......@@ -552,31 +571,17 @@ fn hashFileFallible(dir: fs.Dir, hashed_file: *HashedFile) HashedFile.Error!void
552571 hasher.final(&hashed_file.hash);
553572}
554573
555const hex_charset = "0123456789abcdef";
556
557fn hex64(x: u64) [16]u8 {
558 var result: [16]u8 = undefined;
559 var i: usize = 0;
560 while (i < 8) : (i += 1) {
561 const byte = @truncate(u8, x >> @intCast(u6, 8 * i));
562 result[i * 2 + 0] = hex_charset[byte >> 4];
563 result[i * 2 + 1] = hex_charset[byte & 15];
564 }
565 return result;
566}
567
568test hex64 {
569 const s = "[" ++ hex64(0x12345678_abcdef00) ++ "]";
570 try std.testing.expectEqualStrings("[00efcdab78563412]", s);
571}
572
573fn 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];
574fn isExecutable(file: fs.File) !bool {
575 if (builtin.os.tag == .windows) {
576 // TODO check the ACL on Windows.
577 // Until this is implemented, this could be a false negative on
578 // Windows, which is why we do not yet set executable_bit_only above
579 // when unpacking the tarball.
580 return false;
581 } else {
582 const stat = try file.stat();
583 return (stat.mode & std.os.S.IXUSR) != 0;
578584 }
579 return result;
580585}
581586
582587fn renameTmpIntoCache(
src/Sema.zig+99-22
......@@ -1015,6 +1015,7 @@ fn analyzeBodyInner(
10151015 .float_cast => try sema.zirFloatCast(block, inst),
10161016 .int_cast => try sema.zirIntCast(block, inst),
10171017 .ptr_cast => try sema.zirPtrCast(block, inst),
1018 .qual_cast => try sema.zirQualCast(block, inst),
10181019 .truncate => try sema.zirTruncate(block, inst),
10191020 .align_cast => try sema.zirAlignCast(block, inst),
10201021 .has_decl => try sema.zirHasDecl(block, inst),
......@@ -3294,7 +3295,7 @@ fn ensureResultUsed(
32943295 const msg = msg: {
32953296 const msg = try sema.errMsg(block, src, "error is ignored", .{});
32963297 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'", .{});
32983299 break :msg msg;
32993300 };
33003301 return sema.failWithOwnedErrorMsg(msg);
......@@ -3325,7 +3326,7 @@ fn zirEnsureResultNonError(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
33253326 const msg = msg: {
33263327 const msg = try sema.errMsg(block, src, "error is discarded", .{});
33273328 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'", .{});
33293330 break :msg msg;
33303331 };
33313332 return sema.failWithOwnedErrorMsg(msg);
......@@ -5564,16 +5565,6 @@ pub fn analyzeExport(
55645565 .visibility = borrowed_options.visibility,
55655566 },
55665567 .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 },
55775568 .owner_decl = sema.owner_decl_index,
55785569 .src_decl = block.src_decl,
55795570 .exported_decl = exported_decl_index,
......@@ -6446,7 +6437,12 @@ fn analyzeCall(
64466437 .extern_fn => return sema.fail(block, call_src, "{s} call of extern function", .{
64476438 @as([]const u8, if (is_comptime_call) "comptime" else "inline"),
64486439 }),
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 },
64506446 };
64516447 if (func_ty_info.is_var_args) {
64526448 return sema.fail(block, call_src, "{s} call of variadic function", .{
......@@ -6879,6 +6875,8 @@ fn analyzeInlineCallArg(
68796875 if (err == error.AnalysisFail and param_block.comptime_reason != null) try param_block.comptime_reason.?.explain(sema, sema.err);
68806876 return err;
68816877 };
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");
68826880 }
68836881 const casted_arg = sema.coerceExtra(arg_block, param_ty, uncasted_arg, arg_src, .{ .param_src = .{
68846882 .func_inst = func_inst,
......@@ -6952,6 +6950,9 @@ fn analyzeInlineCallArg(
69526950 .val = arg_val,
69536951 };
69546952 } else {
6953 if (zir_tags[inst] == .param_anytype_comptime) {
6954 _ = try sema.resolveConstMaybeUndefVal(arg_block, arg_src, uncasted_arg, "parameter is comptime");
6955 }
69556956 sema.inst_map.putAssumeCapacityNoClobber(inst, uncasted_arg);
69566957 }
69576958
......@@ -7510,7 +7511,6 @@ fn resolveGenericInstantiationType(
75107511 // Queue up a `codegen_func` work item for the new Fn. The `comptime_args` field
75117512 // will be populated, ensuring it will have `analyzeBody` called with the ZIR
75127513 // parameters mapped appropriately.
7513 try mod.comp.bin_file.allocateDeclIndexes(new_decl_index);
75147514 try mod.comp.work_queue.writeItem(.{ .codegen_func = new_func });
75157515 return new_func;
75167516}
......@@ -8473,7 +8473,7 @@ fn handleExternLibName(
84738473 return sema.fail(
84748474 block,
84758475 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'.",
84778477 .{ lib_name, lib_name },
84788478 );
84798479 }
......@@ -9010,7 +9010,18 @@ fn zirParam(
90109010 if (is_comptime and sema.preallocated_new_func != null) {
90119011 // We have a comptime value for this parameter so it should be elided from the
90129012 // 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 };
90149025 sema.inst_map.putAssumeCapacity(inst, coerced_arg);
90159026 return;
90169027 }
......@@ -19525,13 +19536,34 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
1952519536 const operand_info = operand_ty.ptrInfo().data;
1952619537 const dest_info = dest_ty.ptrInfo().data;
1952719538 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);
1952919547 }
1953019548 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);
1953219557 }
1953319558 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);
1953519567 }
1953619568
1953719569 const dest_is_slice = dest_ty.isSlice();
......@@ -19586,6 +19618,8 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
1958619618 try sema.errNote(block, dest_ty_src, msg, "'{}' has alignment '{d}'", .{
1958719619 dest_ty.fmt(sema.mod), dest_align,
1958819620 });
19621
19622 try sema.errNote(block, src, msg, "consider using '@alignCast'", .{});
1958919623 break :msg msg;
1959019624 };
1959119625 return sema.failWithOwnedErrorMsg(msg);
......@@ -19621,6 +19655,49 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
1962119655 return block.addBitCast(aligned_dest_ty, ptr);
1962219656}
1962319657
19658fn 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
1962419701fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
1962519702 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
1962619703 const src = inst_data.src();
......@@ -25137,7 +25214,7 @@ fn coerceExtra(
2513725214 (try sema.coerceInMemoryAllowed(block, inst_ty.errorUnionPayload(), dest_ty, false, target, dest_ty_src, inst_src)) == .ok)
2513825215 {
2513925216 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'", .{});
2514125218 }
2514225219
2514325220 // ?T to T
......@@ -25146,7 +25223,7 @@ fn coerceExtra(
2514625223 (try sema.coerceInMemoryAllowed(block, inst_ty.optionalChild(&buf), dest_ty, false, target, dest_ty_src, inst_src)) == .ok)
2514725224 {
2514825225 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'", .{});
2515025227 }
2515125228
2515225229 try in_memory_result.report(sema, block, inst_src, msg);
......@@ -26072,7 +26149,7 @@ fn coerceVarArgParam(
2607226149 .Array => return sema.fail(block, inst_src, "arrays must be passed by reference to variadic function", .{}),
2607326150 .Float => float: {
2607426151 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);
2607626153 const inst_bits = uncasted_ty.floatBits(sema.mod.getTarget());
2607726154 if (inst_bits >= double_bits) break :float inst;
2607826155 switch (double_bits) {
src/TypedValue.zig+4-1
......@@ -176,7 +176,9 @@ pub fn print(
176176
177177 var i: u32 = 0;
178178 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;
180182 }
181183
182184 const truncated = if (len > max_string_len) " (truncated)" else "";
......@@ -390,6 +392,7 @@ pub fn print(
390392 while (i < max_len) : (i += 1) {
391393 var elem_buf: Value.ElemValueBuffer = undefined;
392394 const elem_val = payload.ptr.elemValueBuffer(mod, i, &elem_buf);
395 if (elem_val.isUndef()) break :str;
393396 buf[i] = std.math.cast(u8, elem_val.toUnsignedInt(target)) orelse break :str;
394397 }
395398
src/Zir.zig+6
......@@ -857,6 +857,9 @@ pub const Inst = struct {
857857 /// Implements the `@ptrCast` builtin.
858858 /// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand.
859859 ptr_cast,
860 /// Implements the `@qualCast` builtin.
861 /// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand.
862 qual_cast,
860863 /// Implements the `@truncate` builtin.
861864 /// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand.
862865 truncate,
......@@ -1195,6 +1198,7 @@ pub const Inst = struct {
11951198 .float_cast,
11961199 .int_cast,
11971200 .ptr_cast,
1201 .qual_cast,
11981202 .truncate,
11991203 .align_cast,
12001204 .has_field,
......@@ -1484,6 +1488,7 @@ pub const Inst = struct {
14841488 .float_cast,
14851489 .int_cast,
14861490 .ptr_cast,
1491 .qual_cast,
14871492 .truncate,
14881493 .align_cast,
14891494 .has_field,
......@@ -1755,6 +1760,7 @@ pub const Inst = struct {
17551760 .float_cast = .pl_node,
17561761 .int_cast = .pl_node,
17571762 .ptr_cast = .pl_node,
1763 .qual_cast = .pl_node,
17581764 .truncate = .pl_node,
17591765 .align_cast = .pl_node,
17601766 .typeof_builtin = .pl_node,
src/arch/aarch64/CodeGen.zig+143-175
......@@ -24,7 +24,7 @@ const log = std.log.scoped(.codegen);
2424const build_options = @import("build_options");
2525
2626const GenerateSymbolError = codegen.GenerateSymbolError;
27const FnResult = codegen.FnResult;
27const Result = codegen.Result;
2828const DebugInfoOutput = codegen.DebugInfoOutput;
2929
3030const bits = @import("bits.zig");
......@@ -181,6 +181,7 @@ const DbgInfoReloc = struct {
181181 else => unreachable,
182182 }
183183 }
184
184185 fn genArgDbgInfo(reloc: DbgInfoReloc, function: Self) error{OutOfMemory}!void {
185186 switch (function.debug_output) {
186187 .dwarf => |dw| {
......@@ -202,13 +203,7 @@ const DbgInfoReloc = struct {
202203 else => unreachable, // not a possible argument
203204
204205 };
205 try dw.genArgDbgInfo(
206 reloc.name,
207 reloc.ty,
208 function.bin_file.tag,
209 function.mod_fn.owner_decl,
210 loc,
211 );
206 try dw.genArgDbgInfo(reloc.name, reloc.ty, function.mod_fn.owner_decl, loc);
212207 },
213208 .plan9 => {},
214209 .none => {},
......@@ -254,14 +249,7 @@ const DbgInfoReloc = struct {
254249 break :blk .nop;
255250 },
256251 };
257 try dw.genVarDbgInfo(
258 reloc.name,
259 reloc.ty,
260 function.bin_file.tag,
261 function.mod_fn.owner_decl,
262 is_ptr,
263 loc,
264 );
252 try dw.genVarDbgInfo(reloc.name, reloc.ty, function.mod_fn.owner_decl, is_ptr, loc);
265253 },
266254 .plan9 => {},
267255 .none => {},
......@@ -349,7 +337,7 @@ pub fn generate(
349337 liveness: Liveness,
350338 code: *std.ArrayList(u8),
351339 debug_output: DebugInfoOutput,
352) GenerateSymbolError!FnResult {
340) GenerateSymbolError!Result {
353341 if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) {
354342 @panic("Attempted to compile for architecture that was disabled by build configuration");
355343 }
......@@ -392,8 +380,8 @@ pub fn generate(
392380 defer function.dbg_info_relocs.deinit(bin_file.allocator);
393381
394382 var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) {
395 error.CodegenFail => return FnResult{ .fail = function.err_msg.? },
396 error.OutOfRegisters => return FnResult{
383 error.CodegenFail => return Result{ .fail = function.err_msg.? },
384 error.OutOfRegisters => return Result{
397385 .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
398386 },
399387 else => |e| return e,
......@@ -406,8 +394,8 @@ pub fn generate(
406394 function.max_end_stack = call_info.stack_byte_count;
407395
408396 function.gen() catch |err| switch (err) {
409 error.CodegenFail => return FnResult{ .fail = function.err_msg.? },
410 error.OutOfRegisters => return FnResult{
397 error.CodegenFail => return Result{ .fail = function.err_msg.? },
398 error.OutOfRegisters => return Result{
411399 .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
412400 },
413401 else => |e| return e,
......@@ -439,14 +427,14 @@ pub fn generate(
439427 defer emit.deinit();
440428
441429 emit.emitMir() catch |err| switch (err) {
442 error.EmitFail => return FnResult{ .fail = emit.err_msg.? },
430 error.EmitFail => return Result{ .fail = emit.err_msg.? },
443431 else => |e| return e,
444432 };
445433
446434 if (function.err_msg) |em| {
447 return FnResult{ .fail = em };
435 return Result{ .fail = em };
448436 } else {
449 return FnResult{ .appended = {} };
437 return Result.ok;
450438 }
451439}
452440
......@@ -527,6 +515,28 @@ fn gen(self: *Self) !void {
527515 self.ret_mcv = MCValue{ .stack_offset = stack_offset };
528516 }
529517
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
530540 _ = try self.addInst(.{
531541 .tag = .dbg_prologue_end,
532542 .data = .{ .nop = {} },
......@@ -3996,11 +4006,17 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
39964006 .direct => .load_memory_ptr_direct,
39974007 .import => unreachable,
39984008 };
3999 const mod = self.bin_file.options.module.?;
4000 const owner_decl = mod.declPtr(self.mod_fn.owner_decl);
40014009 const atom_index = switch (self.bin_file.tag) {
4002 .macho => owner_decl.link.macho.sym_index,
4003 .coff => owner_decl.link.coff.sym_index,
4010 .macho => blk: {
4011 const macho_file = self.bin_file.cast(link.File.MachO).?;
4012 const atom = try macho_file.getOrCreateAtomForDecl(self.mod_fn.owner_decl);
4013 break :blk macho_file.getAtom(atom).getSymbolIndex().?;
4014 },
4015 .coff => blk: {
4016 const coff_file = self.bin_file.cast(link.File.Coff).?;
4017 const atom = try coff_file.getOrCreateAtomForDecl(self.mod_fn.owner_decl);
4018 break :blk coff_file.getAtom(atom).getSymbolIndex().?;
4019 },
40044020 else => unreachable, // unsupported target format
40054021 };
40064022 _ = try self.addInst(.{
......@@ -4163,45 +4179,19 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
41634179 self.arg_index += 1;
41644180
41654181 const ty = self.air.typeOfIndex(inst);
4166 const result = self.args[arg_index];
4182 const tag = self.air.instructions.items(.tag)[inst];
41674183 const src_index = self.air.instructions.items(.data)[inst].arg.src_index;
41684184 const name = self.mod_fn.getParamName(self.bin_file.options.module.?, src_index);
41694185
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];
41874186 try self.dbg_info_relocs.append(self.gpa, .{
41884187 .tag = tag,
41894188 .ty = ty,
41904189 .name = name,
4191 .mcv = result,
4190 .mcv = self.args[arg_index],
41924191 });
41934192
4194 if (self.liveness.isUnused(inst))
4195 return self.finishAirBookkeeping();
4196
4197 switch (mcv) {
4198 .register => |reg| {
4199 self.register_manager.getRegAssumeFree(reg, inst);
4200 },
4201 else => {},
4202 }
4203
4204 return self.finishAir(inst, mcv, .{ .none, .none, .none });
4193 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else self.args[arg_index];
4194 return self.finishAir(inst, result, .{ .none, .none, .none });
42054195}
42064196
42074197fn airBreakpoint(self: *Self) !void {
......@@ -4302,90 +4292,71 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
43024292 // on linking.
43034293 const mod = self.bin_file.options.module.?;
43044294 if (self.air.value(callee)) |func_value| {
4305 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
4306 if (func_value.castTag(.function)) |func_payload| {
4307 const func = func_payload.data;
4308 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
4309 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
4310 const fn_owner_decl = mod.declPtr(func.owner_decl);
4311 const got_addr = blk: {
4312 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
4313 break :blk @intCast(u32, got.p_vaddr + fn_owner_decl.link.elf.offset_table_index * ptr_bytes);
4314 };
4295 if (func_value.castTag(.function)) |func_payload| {
4296 const func = func_payload.data;
43154297
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));
43164302 try self.genSetReg(Type.initTag(.usize), .x30, .{ .memory = got_addr });
4317
4318 _ = try self.addInst(.{
4319 .tag = .blr,
4320 .data = .{ .reg = .x30 },
4303 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
4304 const atom = try macho_file.getOrCreateAtomForDecl(func.owner_decl);
4305 const sym_index = macho_file.getAtom(atom).getSymbolIndex().?;
4306 try self.genSetReg(Type.initTag(.u64), .x30, .{
4307 .linker_load = .{
4308 .type = .got,
4309 .sym_index = sym_index,
4310 },
43214311 });
4322 } else if (func_value.castTag(.extern_fn)) |_| {
4323 return self.fail("TODO implement calling extern functions", .{});
4324 } else {
4325 return self.fail("TODO implement calling bitcasted functions", .{});
4326 }
4327 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
4328 if (func_value.castTag(.function)) |func_payload| {
4329 const func = func_payload.data;
4330 const fn_owner_decl = mod.declPtr(func.owner_decl);
4312 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
4313 const atom = try coff_file.getOrCreateAtomForDecl(func.owner_decl);
4314 const sym_index = coff_file.getAtom(atom).getSymbolIndex().?;
43314315 try self.genSetReg(Type.initTag(.u64), .x30, .{
43324316 .linker_load = .{
43334317 .type = .got,
4334 .sym_index = fn_owner_decl.link.macho.sym_index,
4318 .sym_index = sym_index,
43354319 },
43364320 });
4337 // blr x30
4338 _ = try self.addInst(.{
4339 .tag = .blr,
4340 .data = .{ .reg = .x30 },
4321 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
4322 const decl_block_index = try p9.seeDecl(func.owner_decl);
4323 const decl_block = p9.getDeclBlock(decl_block_index);
4324 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
4325 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
4326 const got_addr = p9.bases.data;
4327 const got_index = decl_block.got_index.?;
4328 const fn_got_addr = got_addr + got_index * ptr_bytes;
4329 try self.genSetReg(Type.initTag(.usize), .x30, .{ .memory = fn_got_addr });
4330 } else unreachable;
4331
4332 _ = try self.addInst(.{
4333 .tag = .blr,
4334 .data = .{ .reg = .x30 },
4335 });
4336 } else if (func_value.castTag(.extern_fn)) |func_payload| {
4337 const extern_fn = func_payload.data;
4338 const decl_name = mod.declPtr(extern_fn.owner_decl).name;
4339 if (extern_fn.lib_name) |lib_name| {
4340 log.debug("TODO enforce that '{s}' is expected in '{s}' library", .{
4341 decl_name,
4342 lib_name,
43414343 });
4342 } else if (func_value.castTag(.extern_fn)) |func_payload| {
4343 const extern_fn = func_payload.data;
4344 const decl_name = mod.declPtr(extern_fn.owner_decl).name;
4345 if (extern_fn.lib_name) |lib_name| {
4346 log.debug("TODO enforce that '{s}' is expected in '{s}' library", .{
4347 decl_name,
4348 lib_name,
4349 });
4350 }
4351 const sym_index = try macho_file.getGlobalSymbol(mem.sliceTo(decl_name, 0));
4344 }
43524345
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().?;
43534350 _ = try self.addInst(.{
43544351 .tag = .call_extern,
43554352 .data = .{
43564353 .relocation = .{
4357 .atom_index = mod.declPtr(self.mod_fn.owner_decl).link.macho.sym_index,
4354 .atom_index = atom_index,
43584355 .sym_index = sym_index,
43594356 },
43604357 },
43614358 });
4362 } else {
4363 return self.fail("TODO implement calling bitcasted functions", .{});
4364 }
4365 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
4366 if (func_value.castTag(.function)) |func_payload| {
4367 const func = func_payload.data;
4368 const fn_owner_decl = mod.declPtr(func.owner_decl);
4369 try self.genSetReg(Type.initTag(.u64), .x30, .{
4370 .linker_load = .{
4371 .type = .got,
4372 .sym_index = fn_owner_decl.link.coff.sym_index,
4373 },
4374 });
4375 // blr x30
4376 _ = try self.addInst(.{
4377 .tag = .blr,
4378 .data = .{ .reg = .x30 },
4379 });
4380 } else if (func_value.castTag(.extern_fn)) |func_payload| {
4381 const extern_fn = func_payload.data;
4382 const decl_name = mod.declPtr(extern_fn.owner_decl).name;
4383 if (extern_fn.lib_name) |lib_name| {
4384 log.debug("TODO enforce that '{s}' is expected in '{s}' library", .{
4385 decl_name,
4386 lib_name,
4387 });
4388 }
4359 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
43894360 const sym_index = try coff_file.getGlobalSymbol(mem.sliceTo(decl_name, 0));
43904361 try self.genSetReg(Type.initTag(.u64), .x30, .{
43914362 .linker_load = .{
......@@ -4393,35 +4364,16 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
43934364 .sym_index = sym_index,
43944365 },
43954366 });
4396 // blr x30
43974367 _ = try self.addInst(.{
43984368 .tag = .blr,
43994369 .data = .{ .reg = .x30 },
44004370 });
44014371 } 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)) |_| {
44204372 return self.fail("TODO implement calling extern functions", .{});
4421 } else {
4422 return self.fail("TODO implement calling bitcasted functions", .{});
44234373 }
4424 } else unreachable;
4374 } else {
4375 return self.fail("TODO implement calling bitcasted functions", .{});
4376 }
44254377 } else {
44264378 assert(ty.zigTypeTag() == .Pointer);
44274379 const mcv = try self.resolveInst(callee);
......@@ -5534,11 +5486,17 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
55345486 .direct => .load_memory_ptr_direct,
55355487 .import => unreachable,
55365488 };
5537 const mod = self.bin_file.options.module.?;
5538 const owner_decl = mod.declPtr(self.mod_fn.owner_decl);
55395489 const atom_index = switch (self.bin_file.tag) {
5540 .macho => owner_decl.link.macho.sym_index,
5541 .coff => owner_decl.link.coff.sym_index,
5490 .macho => blk: {
5491 const macho_file = self.bin_file.cast(link.File.MachO).?;
5492 const atom = try macho_file.getOrCreateAtomForDecl(self.mod_fn.owner_decl);
5493 break :blk macho_file.getAtom(atom).getSymbolIndex().?;
5494 },
5495 .coff => blk: {
5496 const coff_file = self.bin_file.cast(link.File.Coff).?;
5497 const atom = try coff_file.getOrCreateAtomForDecl(self.mod_fn.owner_decl);
5498 break :blk coff_file.getAtom(atom).getSymbolIndex().?;
5499 },
55425500 else => unreachable, // unsupported target format
55435501 };
55445502 _ = try self.addInst(.{
......@@ -5648,11 +5606,17 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
56485606 .direct => .load_memory_direct,
56495607 .import => .load_memory_import,
56505608 };
5651 const mod = self.bin_file.options.module.?;
5652 const owner_decl = mod.declPtr(self.mod_fn.owner_decl);
56535609 const atom_index = switch (self.bin_file.tag) {
5654 .macho => owner_decl.link.macho.sym_index,
5655 .coff => owner_decl.link.coff.sym_index,
5610 .macho => blk: {
5611 const macho_file = self.bin_file.cast(link.File.MachO).?;
5612 const atom = try macho_file.getOrCreateAtomForDecl(self.mod_fn.owner_decl);
5613 break :blk macho_file.getAtom(atom).getSymbolIndex().?;
5614 },
5615 .coff => blk: {
5616 const coff_file = self.bin_file.cast(link.File.Coff).?;
5617 const atom = try coff_file.getOrCreateAtomForDecl(self.mod_fn.owner_decl);
5618 break :blk coff_file.getAtom(atom).getSymbolIndex().?;
5619 },
56565620 else => unreachable, // unsupported target format
56575621 };
56585622 _ = try self.addInst(.{
......@@ -5842,11 +5806,17 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I
58425806 .direct => .load_memory_ptr_direct,
58435807 .import => unreachable,
58445808 };
5845 const mod = self.bin_file.options.module.?;
5846 const owner_decl = mod.declPtr(self.mod_fn.owner_decl);
58475809 const atom_index = switch (self.bin_file.tag) {
5848 .macho => owner_decl.link.macho.sym_index,
5849 .coff => owner_decl.link.coff.sym_index,
5810 .macho => blk: {
5811 const macho_file = self.bin_file.cast(link.File.MachO).?;
5812 const atom = try macho_file.getOrCreateAtomForDecl(self.mod_fn.owner_decl);
5813 break :blk macho_file.getAtom(atom).getSymbolIndex().?;
5814 },
5815 .coff => blk: {
5816 const coff_file = self.bin_file.cast(link.File.Coff).?;
5817 const atom = try coff_file.getOrCreateAtomForDecl(self.mod_fn.owner_decl);
5818 break :blk coff_file.getAtom(atom).getSymbolIndex().?;
5819 },
58505820 else => unreachable, // unsupported target format
58515821 };
58525822 _ = try self.addInst(.{
......@@ -6165,28 +6135,27 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne
61656135 mod.markDeclAlive(decl);
61666136
61676137 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
6168 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
6169 const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes;
6170 return MCValue{ .memory = got_addr };
6171 } else if (self.bin_file.cast(link.File.MachO)) |_| {
6172 // Because MachO is PIE-always-on, we defer memory address resolution until
6173 // the linker has enough info to perform relocations.
6174 assert(decl.link.macho.sym_index != 0);
6138 const atom_index = try elf_file.getOrCreateAtomForDecl(decl_index);
6139 const atom = elf_file.getAtom(atom_index);
6140 return MCValue{ .memory = atom.getOffsetTableAddress(elf_file) };
6141 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
6142 const atom = try macho_file.getOrCreateAtomForDecl(decl_index);
6143 const sym_index = macho_file.getAtom(atom).getSymbolIndex().?;
61756144 return MCValue{ .linker_load = .{
61766145 .type = .got,
6177 .sym_index = decl.link.macho.sym_index,
6146 .sym_index = sym_index,
61786147 } };
6179 } else if (self.bin_file.cast(link.File.Coff)) |_| {
6180 // Because COFF is PIE-always-on, we defer memory address resolution until
6181 // the linker has enough info to perform relocations.
6182 assert(decl.link.coff.sym_index != 0);
6148 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
6149 const atom_index = try coff_file.getOrCreateAtomForDecl(decl_index);
6150 const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?;
61836151 return MCValue{ .linker_load = .{
61846152 .type = .got,
6185 .sym_index = decl.link.coff.sym_index,
6153 .sym_index = sym_index,
61866154 } };
61876155 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
6188 try p9.seeDecl(decl_index);
6189 const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes;
6156 const decl_block_index = try p9.seeDecl(decl_index);
6157 const decl_block = p9.getDeclBlock(decl_block_index);
6158 const got_addr = p9.bases.data + decl_block.got_index.? * ptr_bytes;
61906159 return MCValue{ .memory = got_addr };
61916160 } else {
61926161 return self.fail("TODO codegen non-ELF const Decl pointer", .{});
......@@ -6199,8 +6168,7 @@ fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue {
61996168 return self.fail("lowering unnamed constant failed: {s}", .{@errorName(err)});
62006169 };
62016170 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
6202 const vaddr = elf_file.local_symbols.items[local_sym_index].st_value;
6203 return MCValue{ .memory = vaddr };
6171 return MCValue{ .memory = elf_file.getSymbol(local_sym_index).st_value };
62046172 } else if (self.bin_file.cast(link.File.MachO)) |_| {
62056173 return MCValue{ .linker_load = .{
62066174 .type = .direct,
src/arch/aarch64/Emit.zig+8-8
......@@ -670,9 +670,9 @@ fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) !void {
670670
671671 if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
672672 // 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 }).?;
674674 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, .{
676676 .type = @enumToInt(std.macho.reloc_type_arm64.ARM64_RELOC_BRANCH26),
677677 .target = target,
678678 .offset = offset,
......@@ -883,10 +883,10 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void {
883883 }
884884
885885 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 }).?;
887887 // TODO this causes segfault in stage1
888888 // try atom.addRelocations(macho_file, 2, .{
889 try atom.addRelocation(macho_file, .{
889 try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{
890890 .target = .{ .sym_index = data.sym_index, .file = null },
891891 .offset = offset,
892892 .addend = 0,
......@@ -902,7 +902,7 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void {
902902 else => unreachable,
903903 },
904904 });
905 try atom.addRelocation(macho_file, .{
905 try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{
906906 .target = .{ .sym_index = data.sym_index, .file = null },
907907 .offset = offset + 4,
908908 .addend = 0,
......@@ -919,7 +919,7 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void {
919919 },
920920 });
921921 } 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 }).?;
923923 const target = switch (tag) {
924924 .load_memory_got,
925925 .load_memory_ptr_got,
......@@ -929,7 +929,7 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void {
929929 .load_memory_import => coff_file.getGlobalByIndex(data.sym_index),
930930 else => unreachable,
931931 };
932 try atom.addRelocation(coff_file, .{
932 try link.File.Coff.Atom.addRelocation(coff_file, atom_index, .{
933933 .target = target,
934934 .offset = offset,
935935 .addend = 0,
......@@ -946,7 +946,7 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void {
946946 else => unreachable,
947947 },
948948 });
949 try atom.addRelocation(coff_file, .{
949 try link.File.Coff.Atom.addRelocation(coff_file, atom_index, .{
950950 .target = target,
951951 .offset = offset + 4,
952952 .addend = 0,
src/arch/arm/CodeGen.zig+65-81
......@@ -23,7 +23,7 @@ const leb128 = std.leb;
2323const log = std.log.scoped(.codegen);
2424const build_options = @import("build_options");
2525
26const FnResult = codegen.FnResult;
26const Result = codegen.Result;
2727const GenerateSymbolError = codegen.GenerateSymbolError;
2828const DebugInfoOutput = codegen.DebugInfoOutput;
2929
......@@ -282,13 +282,7 @@ const DbgInfoReloc = struct {
282282 else => unreachable, // not a possible argument
283283 };
284284
285 try dw.genArgDbgInfo(
286 reloc.name,
287 reloc.ty,
288 function.bin_file.tag,
289 function.mod_fn.owner_decl,
290 loc,
291 );
285 try dw.genArgDbgInfo(reloc.name, reloc.ty, function.mod_fn.owner_decl, loc);
292286 },
293287 .plan9 => {},
294288 .none => {},
......@@ -331,14 +325,7 @@ const DbgInfoReloc = struct {
331325 break :blk .nop;
332326 },
333327 };
334 try dw.genVarDbgInfo(
335 reloc.name,
336 reloc.ty,
337 function.bin_file.tag,
338 function.mod_fn.owner_decl,
339 is_ptr,
340 loc,
341 );
328 try dw.genVarDbgInfo(reloc.name, reloc.ty, function.mod_fn.owner_decl, is_ptr, loc);
342329 },
343330 .plan9 => {},
344331 .none => {},
......@@ -356,7 +343,7 @@ pub fn generate(
356343 liveness: Liveness,
357344 code: *std.ArrayList(u8),
358345 debug_output: DebugInfoOutput,
359) GenerateSymbolError!FnResult {
346) GenerateSymbolError!Result {
360347 if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) {
361348 @panic("Attempted to compile for architecture that was disabled by build configuration");
362349 }
......@@ -399,8 +386,8 @@ pub fn generate(
399386 defer function.dbg_info_relocs.deinit(bin_file.allocator);
400387
401388 var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) {
402 error.CodegenFail => return FnResult{ .fail = function.err_msg.? },
403 error.OutOfRegisters => return FnResult{
389 error.CodegenFail => return Result{ .fail = function.err_msg.? },
390 error.OutOfRegisters => return Result{
404391 .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
405392 },
406393 else => |e| return e,
......@@ -413,8 +400,8 @@ pub fn generate(
413400 function.max_end_stack = call_info.stack_byte_count;
414401
415402 function.gen() catch |err| switch (err) {
416 error.CodegenFail => return FnResult{ .fail = function.err_msg.? },
417 error.OutOfRegisters => return FnResult{
403 error.CodegenFail => return Result{ .fail = function.err_msg.? },
404 error.OutOfRegisters => return Result{
418405 .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
419406 },
420407 else => |e| return e,
......@@ -446,14 +433,14 @@ pub fn generate(
446433 defer emit.deinit();
447434
448435 emit.emitMir() catch |err| switch (err) {
449 error.EmitFail => return FnResult{ .fail = emit.err_msg.? },
436 error.EmitFail => return Result{ .fail = emit.err_msg.? },
450437 else => |e| return e,
451438 };
452439
453440 if (function.err_msg) |em| {
454 return FnResult{ .fail = em };
441 return Result{ .fail = em };
455442 } else {
456 return FnResult{ .appended = {} };
443 return Result.ok;
457444 }
458445}
459446
......@@ -4253,59 +4240,56 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
42534240
42544241 // Due to incremental compilation, how function calls are generated depends
42554242 // on linking.
4256 switch (self.bin_file.tag) {
4257 .elf => {
4258 if (self.air.value(callee)) |func_value| {
4259 if (func_value.castTag(.function)) |func_payload| {
4260 const func = func_payload.data;
4261 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
4262 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
4263 const mod = self.bin_file.options.module.?;
4264 const fn_owner_decl = mod.declPtr(func.owner_decl);
4265 const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: {
4266 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
4267 break :blk @intCast(u32, got.p_vaddr + fn_owner_decl.link.elf.offset_table_index * ptr_bytes);
4268 } else unreachable;
4269 try self.genSetReg(Type.initTag(.usize), .lr, .{ .memory = got_addr });
4270 } else if (func_value.castTag(.extern_fn)) |_| {
4271 return self.fail("TODO implement calling extern functions", .{});
4272 } else {
4273 return self.fail("TODO implement calling bitcasted functions", .{});
4274 }
4243 if (self.air.value(callee)) |func_value| {
4244 if (func_value.castTag(.function)) |func_payload| {
4245 const func = func_payload.data;
4246
4247 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
4248 const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl);
4249 const atom = elf_file.getAtom(atom_index);
4250 const got_addr = @intCast(u32, atom.getOffsetTableAddress(elf_file));
4251 try self.genSetReg(Type.initTag(.usize), .lr, .{ .memory = got_addr });
4252 } else if (self.bin_file.cast(link.File.MachO)) |_| {
4253 unreachable; // unsupported architecture for MachO
42754254 } else {
4276 assert(ty.zigTypeTag() == .Pointer);
4277 const mcv = try self.resolveInst(callee);
4278
4279 try self.genSetReg(Type.initTag(.usize), .lr, mcv);
4280 }
4281
4282 // TODO: add Instruction.supportedOn
4283 // function for ARM
4284 if (Target.arm.featureSetHas(self.target.cpu.features, .has_v5t)) {
4285 _ = try self.addInst(.{
4286 .tag = .blx,
4287 .data = .{ .reg = .lr },
4255 return self.fail("TODO implement call on {s} for {s}", .{
4256 @tagName(self.bin_file.tag),
4257 @tagName(self.target.cpu.arch),
42884258 });
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 // });
43034259 }
4304 },
4305 .macho => unreachable, // unsupported architecture for MachO
4306 .coff => return self.fail("TODO implement call in COFF for {}", .{self.target.cpu.arch}),
4307 .plan9 => return self.fail("TODO implement call on plan9 for {}", .{self.target.cpu.arch}),
4308 else => unreachable,
4260 } else if (func_value.castTag(.extern_fn)) |_| {
4261 return self.fail("TODO implement calling extern functions", .{});
4262 } else {
4263 return self.fail("TODO implement calling bitcasted functions", .{});
4264 }
4265 } else {
4266 assert(ty.zigTypeTag() == .Pointer);
4267 const mcv = try self.resolveInst(callee);
4268
4269 try self.genSetReg(Type.initTag(.usize), .lr, mcv);
4270 }
4271
4272 // TODO: add Instruction.supportedOn
4273 // function for ARM
4274 if (Target.arm.featureSetHas(self.target.cpu.features, .has_v5t)) {
4275 _ = try self.addInst(.{
4276 .tag = .blx,
4277 .data = .{ .reg = .lr },
4278 });
4279 } else {
4280 return self.fail("TODO fix blx emulation for ARM <v5", .{});
4281 // _ = try self.addInst(.{
4282 // .tag = .mov,
4283 // .data = .{ .rr_op = .{
4284 // .rd = .lr,
4285 // .rn = .r0,
4286 // .op = Instruction.Operand.reg(.pc, Instruction.Operand.Shift.none),
4287 // } },
4288 // });
4289 // _ = try self.addInst(.{
4290 // .tag = .bx,
4291 // .data = .{ .reg = .lr },
4292 // });
43094293 }
43104294
43114295 const result: MCValue = result: {
......@@ -6086,16 +6070,17 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne
60866070 mod.markDeclAlive(decl);
60876071
60886072 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
6089 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
6090 const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes;
6091 return MCValue{ .memory = got_addr };
6073 const atom_index = try elf_file.getOrCreateAtomForDecl(decl_index);
6074 const atom = elf_file.getAtom(atom_index);
6075 return MCValue{ .memory = atom.getOffsetTableAddress(elf_file) };
60926076 } else if (self.bin_file.cast(link.File.MachO)) |_| {
60936077 unreachable; // unsupported architecture for MachO
60946078 } else if (self.bin_file.cast(link.File.Coff)) |_| {
60956079 return self.fail("TODO codegen COFF const Decl pointer", .{});
60966080 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
6097 try p9.seeDecl(decl_index);
6098 const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes;
6081 const decl_block_index = try p9.seeDecl(decl_index);
6082 const decl_block = p9.getDeclBlock(decl_block_index);
6083 const got_addr = p9.bases.data + decl_block.got_index.? * ptr_bytes;
60996084 return MCValue{ .memory = got_addr };
61006085 } else {
61016086 return self.fail("TODO codegen non-ELF const Decl pointer", .{});
......@@ -6109,8 +6094,7 @@ fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue {
61096094 return self.fail("lowering unnamed constant failed: {s}", .{@errorName(err)});
61106095 };
61116096 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
6112 const vaddr = elf_file.local_symbols.items[local_sym_index].st_value;
6113 return MCValue{ .memory = vaddr };
6097 return MCValue{ .memory = elf_file.getSymbol(local_sym_index).st_value };
61146098 } else if (self.bin_file.cast(link.File.MachO)) |_| {
61156099 unreachable;
61166100 } else if (self.bin_file.cast(link.File.Coff)) |_| {
src/arch/riscv64/CodeGen.zig+22-34
......@@ -22,7 +22,7 @@ const leb128 = std.leb;
2222const log = std.log.scoped(.codegen);
2323const build_options = @import("build_options");
2424
25const FnResult = @import("../../codegen.zig").FnResult;
25const Result = @import("../../codegen.zig").Result;
2626const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError;
2727const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;
2828
......@@ -225,7 +225,7 @@ pub fn generate(
225225 liveness: Liveness,
226226 code: *std.ArrayList(u8),
227227 debug_output: DebugInfoOutput,
228) GenerateSymbolError!FnResult {
228) GenerateSymbolError!Result {
229229 if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) {
230230 @panic("Attempted to compile for architecture that was disabled by build configuration");
231231 }
......@@ -268,8 +268,8 @@ pub fn generate(
268268 defer function.exitlude_jump_relocs.deinit(bin_file.allocator);
269269
270270 var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) {
271 error.CodegenFail => return FnResult{ .fail = function.err_msg.? },
272 error.OutOfRegisters => return FnResult{
271 error.CodegenFail => return Result{ .fail = function.err_msg.? },
272 error.OutOfRegisters => return Result{
273273 .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
274274 },
275275 else => |e| return e,
......@@ -282,8 +282,8 @@ pub fn generate(
282282 function.max_end_stack = call_info.stack_byte_count;
283283
284284 function.gen() catch |err| switch (err) {
285 error.CodegenFail => return FnResult{ .fail = function.err_msg.? },
286 error.OutOfRegisters => return FnResult{
285 error.CodegenFail => return Result{ .fail = function.err_msg.? },
286 error.OutOfRegisters => return Result{
287287 .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
288288 },
289289 else => |e| return e,
......@@ -309,14 +309,14 @@ pub fn generate(
309309 defer emit.deinit();
310310
311311 emit.emitMir() catch |err| switch (err) {
312 error.EmitFail => return FnResult{ .fail = emit.err_msg.? },
312 error.EmitFail => return Result{ .fail = emit.err_msg.? },
313313 else => |e| return e,
314314 };
315315
316316 if (function.err_msg) |em| {
317 return FnResult{ .fail = em };
317 return Result{ .fail = em };
318318 } else {
319 return FnResult{ .appended = {} };
319 return Result.ok;
320320 }
321321}
322322
......@@ -1615,13 +1615,9 @@ fn genArgDbgInfo(self: Self, inst: Air.Inst.Index, mcv: MCValue) !void {
16151615
16161616 switch (self.debug_output) {
16171617 .dwarf => |dw| switch (mcv) {
1618 .register => |reg| try dw.genArgDbgInfo(
1619 name,
1620 ty,
1621 self.bin_file.tag,
1622 self.mod_fn.owner_decl,
1623 .{ .register = reg.dwarfLocOp() },
1624 ),
1618 .register => |reg| try dw.genArgDbgInfo(name, ty, self.mod_fn.owner_decl, .{
1619 .register = reg.dwarfLocOp(),
1620 }),
16251621 .stack_offset => {},
16261622 else => {},
16271623 },
......@@ -1721,16 +1717,9 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
17211717 if (self.air.value(callee)) |func_value| {
17221718 if (func_value.castTag(.function)) |func_payload| {
17231719 const func = func_payload.data;
1724
1725 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
1726 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
1727 const mod = self.bin_file.options.module.?;
1728 const fn_owner_decl = mod.declPtr(func.owner_decl);
1729 const got_addr = blk: {
1730 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
1731 break :blk @intCast(u32, got.p_vaddr + fn_owner_decl.link.elf.offset_table_index * ptr_bytes);
1732 };
1733
1720 const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl);
1721 const atom = elf_file.getAtom(atom_index);
1722 const got_addr = @intCast(u32, atom.getOffsetTableAddress(elf_file));
17341723 try self.genSetReg(Type.initTag(.usize), .ra, .{ .memory = got_addr });
17351724 _ = try self.addInst(.{
17361725 .tag = .jalr,
......@@ -2557,18 +2546,17 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne
25572546 const decl = mod.declPtr(decl_index);
25582547 mod.markDeclAlive(decl);
25592548 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
2560 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
2561 const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes;
2562 return MCValue{ .memory = got_addr };
2549 const atom_index = try elf_file.getOrCreateAtomForDecl(decl_index);
2550 const atom = elf_file.getAtom(atom_index);
2551 return MCValue{ .memory = atom.getOffsetTableAddress(elf_file) };
25632552 } else if (self.bin_file.cast(link.File.MachO)) |_| {
2564 // TODO I'm hacking my way through here by repurposing .memory for storing
2565 // index to the GOT target symbol index.
2566 return MCValue{ .memory = decl.link.macho.sym_index };
2553 unreachable;
25672554 } else if (self.bin_file.cast(link.File.Coff)) |_| {
25682555 return self.fail("TODO codegen COFF const Decl pointer", .{});
25692556 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
2570 try p9.seeDecl(decl_index);
2571 const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes;
2557 const decl_block_index = try p9.seeDecl(decl_index);
2558 const decl_block = p9.getDeclBlock(decl_block_index);
2559 const got_addr = p9.bases.data + decl_block.got_index.? * ptr_bytes;
25722560 return MCValue{ .memory = got_addr };
25732561 } else {
25742562 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");
2020const Liveness = @import("../../Liveness.zig");
2121const Type = @import("../../type.zig").Type;
2222const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError;
23const FnResult = @import("../../codegen.zig").FnResult;
23const Result = @import("../../codegen.zig").Result;
2424const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;
2525
2626const build_options = @import("build_options");
......@@ -265,7 +265,7 @@ pub fn generate(
265265 liveness: Liveness,
266266 code: *std.ArrayList(u8),
267267 debug_output: DebugInfoOutput,
268) GenerateSymbolError!FnResult {
268) GenerateSymbolError!Result {
269269 if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) {
270270 @panic("Attempted to compile for architecture that was disabled by build configuration");
271271 }
......@@ -310,8 +310,8 @@ pub fn generate(
310310 defer function.exitlude_jump_relocs.deinit(bin_file.allocator);
311311
312312 var call_info = function.resolveCallingConventionValues(fn_type, .callee) catch |err| switch (err) {
313 error.CodegenFail => return FnResult{ .fail = function.err_msg.? },
314 error.OutOfRegisters => return FnResult{
313 error.CodegenFail => return Result{ .fail = function.err_msg.? },
314 error.OutOfRegisters => return Result{
315315 .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
316316 },
317317 else => |e| return e,
......@@ -324,8 +324,8 @@ pub fn generate(
324324 function.max_end_stack = call_info.stack_byte_count;
325325
326326 function.gen() catch |err| switch (err) {
327 error.CodegenFail => return FnResult{ .fail = function.err_msg.? },
328 error.OutOfRegisters => return FnResult{
327 error.CodegenFail => return Result{ .fail = function.err_msg.? },
328 error.OutOfRegisters => return Result{
329329 .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
330330 },
331331 else => |e| return e,
......@@ -351,14 +351,14 @@ pub fn generate(
351351 defer emit.deinit();
352352
353353 emit.emitMir() catch |err| switch (err) {
354 error.EmitFail => return FnResult{ .fail = emit.err_msg.? },
354 error.EmitFail => return Result{ .fail = emit.err_msg.? },
355355 else => |e| return e,
356356 };
357357
358358 if (function.err_msg) |em| {
359 return FnResult{ .fail = em };
359 return Result{ .fail = em };
360360 } else {
361 return FnResult{ .appended = {} };
361 return Result.ok;
362362 }
363363}
364364
......@@ -1216,12 +1216,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
12161216 if (self.bin_file.tag == link.File.Elf.base_tag) {
12171217 if (func_value.castTag(.function)) |func_payload| {
12181218 const func = func_payload.data;
1219 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
1220 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
12211219 const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: {
1222 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
1223 const mod = self.bin_file.options.module.?;
1224 break :blk @intCast(u32, got.p_vaddr + mod.declPtr(func.owner_decl).link.elf.offset_table_index * ptr_bytes);
1220 const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl);
1221 const atom = elf_file.getAtom(atom_index);
1222 break :blk @intCast(u32, atom.getOffsetTableAddress(elf_file));
12251223 } else unreachable;
12261224
12271225 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 {
34143412
34153413 switch (self.debug_output) {
34163414 .dwarf => |dw| switch (mcv) {
3417 .register => |reg| try dw.genArgDbgInfo(
3418 name,
3419 ty,
3420 self.bin_file.tag,
3421 self.mod_fn.owner_decl,
3422 .{ .register = reg.dwarfLocOp() },
3423 ),
3415 .register => |reg| try dw.genArgDbgInfo(name, ty, self.mod_fn.owner_decl, .{
3416 .register = reg.dwarfLocOp(),
3417 }),
34243418 else => {},
34253419 },
34263420 else => {},
......@@ -4193,9 +4187,6 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
41934187}
41944188
41954189fn 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
41994190 // TODO this feels clunky. Perhaps we should check for it in `genTypedValue`?
42004191 if (tv.ty.zigTypeTag() == .Pointer) blk: {
42014192 if (tv.ty.castPtrToFn()) |_| break :blk;
......@@ -4209,9 +4200,9 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne
42094200
42104201 mod.markDeclAlive(decl);
42114202 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
4212 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
4213 const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes;
4214 return MCValue{ .memory = got_addr };
4203 const atom_index = try elf_file.getOrCreateAtomForDecl(decl_index);
4204 const atom = elf_file.getAtom(atom_index);
4205 return MCValue{ .memory = atom.getOffsetTableAddress(elf_file) };
42154206 } else {
42164207 return self.fail("TODO codegen non-ELF const Decl pointer", .{});
42174208 }
src/arch/wasm/CodeGen.zig+36-28
......@@ -627,13 +627,6 @@ test "Wasm - buildOpcode" {
627627 try testing.expectEqual(@as(wasm.Opcode, .f64_reinterpret_i64), f64_reinterpret_i64);
628628}
629629
630pub 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
637630/// Hashmap to store generated `WValue` for each `Air.Inst.Ref`
638631pub const ValueTable = std.AutoArrayHashMapUnmanaged(Air.Inst.Ref, WValue);
639632
......@@ -1171,7 +1164,7 @@ pub fn generate(
11711164 liveness: Liveness,
11721165 code: *std.ArrayList(u8),
11731166 debug_output: codegen.DebugInfoOutput,
1174) codegen.GenerateSymbolError!codegen.FnResult {
1167) codegen.GenerateSymbolError!codegen.Result {
11751168 _ = src_loc;
11761169 var code_gen: CodeGen = .{
11771170 .gpa = bin_file.allocator,
......@@ -1190,18 +1183,18 @@ pub fn generate(
11901183 defer code_gen.deinit();
11911184
11921185 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 },
11941187 else => |e| return e,
11951188 };
11961189
1197 return codegen.FnResult{ .appended = {} };
1190 return codegen.Result.ok;
11981191}
11991192
12001193fn genFunc(func: *CodeGen) InnerError!void {
12011194 const fn_info = func.decl.ty.fnInfo();
12021195 var func_type = try genFunctype(func.gpa, fn_info.cc, fn_info.param_types, fn_info.return_type, func.target);
12031196 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);
12051198
12061199 var cc_result = try func.resolveCallingConventionValues(func.decl.ty);
12071200 defer cc_result.deinit(func.gpa);
......@@ -1276,10 +1269,10 @@ fn genFunc(func: *CodeGen) InnerError!void {
12761269
12771270 var emit: Emit = .{
12781271 .mir = mir,
1279 .bin_file = &func.bin_file.base,
1272 .bin_file = func.bin_file,
12801273 .code = func.code,
12811274 .locals = func.locals.items,
1282 .decl = func.decl,
1275 .decl_index = func.decl_index,
12831276 .dbg_output = func.debug_output,
12841277 .prev_di_line = 0,
12851278 .prev_di_column = 0,
......@@ -1713,9 +1706,11 @@ fn isByRef(ty: Type, target: std.Target) bool {
17131706 return true;
17141707 },
17151708 .Optional => {
1716 if (ty.optionalReprIsPayload()) return false;
1709 if (ty.isPtrLikeOptional()) return false;
17171710 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();
17191714 },
17201715 .Pointer => {
17211716 // 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
21222117 const fn_info = fn_ty.fnInfo();
21232118 const first_param_sret = firstParamSRet(fn_info.cc, fn_info.return_type, func.target);
21242119
2125 const callee: ?*Decl = blk: {
2120 const callee: ?Decl.Index = blk: {
21262121 const func_val = func.air.value(pl_op.operand) orelse break :blk null;
21272122 const module = func.bin_file.base.options.module.?;
21282123
21292124 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;
21312127 } else if (func_val.castTag(.extern_fn)) |extern_fn| {
21322128 const ext_decl = module.declPtr(extern_fn.data.owner_decl);
21332129 const ext_info = ext_decl.ty.fnInfo();
21342130 var func_type = try genFunctype(func.gpa, ext_info.cc, ext_info.param_types, ext_info.return_type, func.target);
21352131 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);
21372135 try func.bin_file.addOrUpdateImport(
21382136 mem.sliceTo(ext_decl.name, 0),
2139 ext_decl.link.wasm.sym_index,
2137 atom.getSymbolIndex().?,
21402138 ext_decl.getExternFn().?.lib_name,
2141 ext_decl.fn_link.wasm.type_index,
2139 ext_decl.fn_link.?.type_index,
21422140 );
2143 break :blk ext_decl;
2141 break :blk extern_fn.data.owner_decl;
21442142 } 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;
21462145 }
21472146 return func.fail("Expected a function, but instead found type '{}'", .{func_val.tag()});
21482147 };
......@@ -2163,7 +2162,8 @@ fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModif
21632162 }
21642163
21652164 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);
21672167 } else {
21682168 // in this case we call a function pointer
21692169 // so load its value onto the stack
......@@ -2476,7 +2476,7 @@ fn airArg(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
24762476 .dwarf => |dwarf| {
24772477 const src_index = func.air.instructions.items(.data)[inst].arg.src_index;
24782478 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, .{
24802480 .wasm_local = arg.local.value,
24812481 });
24822482 },
......@@ -2759,8 +2759,10 @@ fn lowerDeclRefValue(func: *CodeGen, tv: TypedValue, decl_index: Module.Decl.Ind
27592759 }
27602760
27612761 module.markDeclAlive(decl);
2762 const atom_index = try func.bin_file.getOrCreateAtomForDecl(decl_index);
2763 const atom = func.bin_file.getAtom(atom_index);
27622764
2763 const target_sym_index = decl.link.wasm.sym_index;
2765 const target_sym_index = atom.sym_index;
27642766 if (decl.ty.zigTypeTag() == .Fn) {
27652767 try func.bin_file.addTableFunction(target_sym_index);
27662768 return WValue{ .function_index = target_sym_index };
......@@ -3869,14 +3871,20 @@ fn airIsNull(func: *CodeGen, inst: Air.Inst.Index, opcode: wasm.Opcode, op_kind:
38693871/// NOTE: Leaves the result on the stack
38703872fn isNull(func: *CodeGen, operand: WValue, optional_ty: Type, opcode: wasm.Opcode) InnerError!WValue {
38713873 try func.emitWValue(operand);
3874 var buf: Type.Payload.ElemType = undefined;
3875 const payload_ty = optional_ty.optionalChild(&buf);
38723876 if (!optional_ty.optionalReprIsPayload()) {
3873 var buf: Type.Payload.ElemType = undefined;
3874 const payload_ty = optional_ty.optionalChild(&buf);
38753877 // When payload is zero-bits, we can treat operand as a value, rather than
38763878 // a pointer to the stack value
38773879 if (payload_ty.hasRuntimeBitsIgnoreComptime()) {
38783880 try func.addMemArg(.i32_load8_u, .{ .offset = operand.offset(), .alignment = 1 });
38793881 }
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 }
38803888 }
38813889
38823890 // Compare the null value with '0'
......@@ -5539,7 +5547,7 @@ fn airDbgVar(func: *CodeGen, inst: Air.Inst.Index, is_ptr: bool) !void {
55395547 break :blk .nop;
55405548 },
55415549 };
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);
55435551
55445552 func.finishAir(inst, .none, &.{});
55455553}
src/arch/wasm/Emit.zig+18-11
......@@ -11,8 +11,8 @@ const leb128 = std.leb;
1111
1212/// Contains our list of instructions
1313mir: Mir,
14/// Reference to the file handler
15bin_file: *link.File,
14/// Reference to the Wasm module linker
15bin_file: *link.File.Wasm,
1616/// Possible error message. When set, the value is allocated and
1717/// must be freed manually.
1818error_msg: ?*Module.ErrorMsg = null,
......@@ -21,7 +21,7 @@ code: *std.ArrayList(u8),
2121/// List of allocated locals.
2222locals: []const u8,
2323/// The declaration that code is being generated for.
24decl: *Module.Decl,
24decl_index: Module.Decl.Index,
2525
2626// Debug information
2727/// Holds the debug information for this emission
......@@ -252,8 +252,8 @@ fn offset(self: Emit) u32 {
252252fn fail(emit: *Emit, comptime format: []const u8, args: anytype) InnerError {
253253 @setCold(true);
254254 std.debug.assert(emit.error_msg == null);
255 // TODO: Determine the source location.
256 emit.error_msg = try Module.ErrorMsg.create(emit.bin_file.allocator, emit.decl.srcLoc(), format, args);
255 const mod = emit.bin_file.base.options.module.?;
256 emit.error_msg = try Module.ErrorMsg.create(emit.bin_file.base.allocator, mod.declPtr(emit.decl_index).srcLoc(), format, args);
257257 return error.EmitFail;
258258}
259259
......@@ -304,8 +304,9 @@ fn emitGlobal(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) !void {
304304 const global_offset = emit.offset();
305305 try emit.code.appendSlice(&buf);
306306
307 // globals can have index 0 as it represents the stack pointer
308 try emit.decl.link.wasm.relocs.append(emit.bin_file.allocator, .{
307 const atom_index = emit.bin_file.decls.get(emit.decl_index).?;
308 const atom = emit.bin_file.getAtomPtr(atom_index);
309 try atom.relocs.append(emit.bin_file.base.allocator, .{
309310 .index = label,
310311 .offset = global_offset,
311312 .relocation_type = .R_WASM_GLOBAL_INDEX_LEB,
......@@ -361,7 +362,9 @@ fn emitCall(emit: *Emit, inst: Mir.Inst.Index) !void {
361362 try emit.code.appendSlice(&buf);
362363
363364 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, .{
365368 .offset = call_offset,
366369 .index = label,
367370 .relocation_type = .R_WASM_FUNCTION_INDEX_LEB,
......@@ -387,7 +390,9 @@ fn emitFunctionIndex(emit: *Emit, inst: Mir.Inst.Index) !void {
387390 try emit.code.appendSlice(&buf);
388391
389392 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, .{
391396 .offset = index_offset,
392397 .index = symbol_index,
393398 .relocation_type = .R_WASM_TABLE_INDEX_SLEB,
......@@ -399,7 +404,7 @@ fn emitMemAddress(emit: *Emit, inst: Mir.Inst.Index) !void {
399404 const extra_index = emit.mir.instructions.items(.data)[inst].payload;
400405 const mem = emit.mir.extraData(Mir.Memory, extra_index).data;
401406 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;
403408 if (is_wasm32) {
404409 try emit.code.append(std.wasm.opcode(.i32_const));
405410 var buf: [5]u8 = undefined;
......@@ -413,7 +418,9 @@ fn emitMemAddress(emit: *Emit, inst: Mir.Inst.Index) !void {
413418 }
414419
415420 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, .{
417424 .offset = mem_offset,
418425 .index = mem.pointer,
419426 .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");
1616const DebugInfoOutput = codegen.DebugInfoOutput;
1717const DW = std.dwarf;
1818const ErrorMsg = Module.ErrorMsg;
19const FnResult = codegen.FnResult;
19const Result = codegen.Result;
2020const GenerateSymbolError = codegen.GenerateSymbolError;
2121const Emit = @import("Emit.zig");
2222const Liveness = @import("../../Liveness.zig");
......@@ -257,7 +257,7 @@ pub fn generate(
257257 liveness: Liveness,
258258 code: *std.ArrayList(u8),
259259 debug_output: DebugInfoOutput,
260) GenerateSymbolError!FnResult {
260) GenerateSymbolError!Result {
261261 if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) {
262262 @panic("Attempted to compile for architecture that was disabled by build configuration");
263263 }
......@@ -305,8 +305,8 @@ pub fn generate(
305305 defer if (builtin.mode == .Debug) function.mir_to_air_map.deinit();
306306
307307 var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) {
308 error.CodegenFail => return FnResult{ .fail = function.err_msg.? },
309 error.OutOfRegisters => return FnResult{
308 error.CodegenFail => return Result{ .fail = function.err_msg.? },
309 error.OutOfRegisters => return Result{
310310 .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
311311 },
312312 else => |e| return e,
......@@ -319,8 +319,8 @@ pub fn generate(
319319 function.max_end_stack = call_info.stack_byte_count;
320320
321321 function.gen() catch |err| switch (err) {
322 error.CodegenFail => return FnResult{ .fail = function.err_msg.? },
323 error.OutOfRegisters => return FnResult{
322 error.CodegenFail => return Result{ .fail = function.err_msg.? },
323 error.OutOfRegisters => return Result{
324324 .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
325325 },
326326 else => |e| return e,
......@@ -345,14 +345,14 @@ pub fn generate(
345345 };
346346 defer emit.deinit();
347347 emit.lowerMir() catch |err| switch (err) {
348 error.EmitFail => return FnResult{ .fail = emit.err_msg.? },
348 error.EmitFail => return Result{ .fail = emit.err_msg.? },
349349 else => |e| return e,
350350 };
351351
352352 if (function.err_msg) |em| {
353 return FnResult{ .fail = em };
353 return Result{ .fail = em };
354354 } else {
355 return FnResult{ .appended = {} };
355 return Result.ok;
356356 }
357357}
358358
......@@ -2668,12 +2668,13 @@ fn loadMemPtrIntoRegister(self: *Self, reg: Register, ptr_ty: Type, ptr: MCValue
26682668 switch (ptr) {
26692669 .linker_load => |load_struct| {
26702670 const abi_size = @intCast(u32, ptr_ty.abiSize(self.target.*));
2671 const mod = self.bin_file.options.module.?;
2672 const fn_owner_decl = mod.declPtr(self.mod_fn.owner_decl);
2673 const atom_index = if (self.bin_file.tag == link.File.MachO.base_tag)
2674 fn_owner_decl.link.macho.sym_index
2675 else
2676 fn_owner_decl.link.coff.sym_index;
2671 const atom_index = if (self.bin_file.cast(link.File.MachO)) |macho_file| blk: {
2672 const atom = try macho_file.getOrCreateAtomForDecl(self.mod_fn.owner_decl);
2673 break :blk macho_file.getAtom(atom).getSymbolIndex().?;
2674 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| blk: {
2675 const atom = try coff_file.getOrCreateAtomForDecl(self.mod_fn.owner_decl);
2676 break :blk coff_file.getAtom(atom).getSymbolIndex().?;
2677 } else unreachable;
26772678 const flags: u2 = switch (load_struct.type) {
26782679 .got => 0b00,
26792680 .direct => 0b01,
......@@ -3835,7 +3836,7 @@ fn genArgDbgInfo(self: Self, ty: Type, name: [:0]const u8, mcv: MCValue) !void {
38353836 },
38363837 else => unreachable, // not a valid function parameter
38373838 };
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);
38393840 },
38403841 .plan9 => {},
38413842 .none => {},
......@@ -3875,7 +3876,7 @@ fn genVarDbgInfo(
38753876 break :blk .nop;
38763877 },
38773878 };
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);
38793880 },
38803881 .plan9 => {},
38813882 .none => {},
......@@ -3992,49 +3993,26 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
39923993 // Due to incremental compilation, how function calls are generated depends
39933994 // on linking.
39943995 const mod = self.bin_file.options.module.?;
3995 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
3996 if (self.air.value(callee)) |func_value| {
3997 if (func_value.castTag(.function)) |func_payload| {
3998 const func = func_payload.data;
3999 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
4000 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
4001 const fn_owner_decl = mod.declPtr(func.owner_decl);
4002 const got_addr = blk: {
4003 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
4004 break :blk @intCast(u32, got.p_vaddr + fn_owner_decl.link.elf.offset_table_index * ptr_bytes);
4005 };
3996 if (self.air.value(callee)) |func_value| {
3997 if (func_value.castTag(.function)) |func_payload| {
3998 const func = func_payload.data;
3999
4000 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
4001 const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl);
4002 const atom = elf_file.getAtom(atom_index);
4003 const got_addr = @intCast(u32, atom.getOffsetTableAddress(elf_file));
40064004 _ = try self.addInst(.{
40074005 .tag = .call,
40084006 .ops = Mir.Inst.Ops.encode(.{ .flags = 0b01 }),
4009 .data = .{ .imm = @truncate(u32, got_addr) },
4007 .data = .{ .imm = got_addr },
40104008 });
4011 } else if (func_value.castTag(.extern_fn)) |_| {
4012 return self.fail("TODO implement calling extern functions", .{});
4013 } else {
4014 return self.fail("TODO implement calling bitcasted functions", .{});
4015 }
4016 } else {
4017 assert(ty.zigTypeTag() == .Pointer);
4018 const mcv = try self.resolveInst(callee);
4019 try self.genSetReg(Type.initTag(.usize), .rax, mcv);
4020 _ = try self.addInst(.{
4021 .tag = .call,
4022 .ops = Mir.Inst.Ops.encode(.{
4023 .reg1 = .rax,
4024 .flags = 0b01,
4025 }),
4026 .data = undefined,
4027 });
4028 }
4029 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
4030 if (self.air.value(callee)) |func_value| {
4031 if (func_value.castTag(.function)) |func_payload| {
4032 const func = func_payload.data;
4033 const fn_owner_decl = mod.declPtr(func.owner_decl);
4009 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
4010 const atom_index = try coff_file.getOrCreateAtomForDecl(func.owner_decl);
4011 const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?;
40344012 try self.genSetReg(Type.initTag(.usize), .rax, .{
40354013 .linker_load = .{
40364014 .type = .got,
4037 .sym_index = fn_owner_decl.link.coff.sym_index,
4015 .sym_index = sym_index,
40384016 },
40394017 });
40404018 _ = try self.addInst(.{
......@@ -4045,19 +4023,12 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
40454023 }),
40464024 .data = undefined,
40474025 });
4048 } else if (func_value.castTag(.extern_fn)) |func_payload| {
4049 const extern_fn = func_payload.data;
4050 const decl_name = mod.declPtr(extern_fn.owner_decl).name;
4051 if (extern_fn.lib_name) |lib_name| {
4052 log.debug("TODO enforce that '{s}' is expected in '{s}' library", .{
4053 decl_name,
4054 lib_name,
4055 });
4056 }
4057 const sym_index = try coff_file.getGlobalSymbol(mem.sliceTo(decl_name, 0));
4026 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
4027 const atom_index = try macho_file.getOrCreateAtomForDecl(func.owner_decl);
4028 const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?;
40584029 try self.genSetReg(Type.initTag(.usize), .rax, .{
40594030 .linker_load = .{
4060 .type = .import,
4031 .type = .got,
40614032 .sym_index = sym_index,
40624033 },
40634034 });
......@@ -4069,35 +4040,38 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
40694040 }),
40704041 .data = undefined,
40714042 });
4072 } else {
4073 return self.fail("TODO implement calling bitcasted functions", .{});
4043 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
4044 const decl_block_index = try p9.seeDecl(func.owner_decl);
4045 const decl_block = p9.getDeclBlock(decl_block_index);
4046 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
4047 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
4048 const got_addr = p9.bases.data;
4049 const got_index = decl_block.got_index.?;
4050 const fn_got_addr = got_addr + got_index * ptr_bytes;
4051 _ = try self.addInst(.{
4052 .tag = .call,
4053 .ops = Mir.Inst.Ops.encode(.{ .flags = 0b01 }),
4054 .data = .{ .imm = @intCast(u32, fn_got_addr) },
4055 });
4056 } else unreachable;
4057 } else if (func_value.castTag(.extern_fn)) |func_payload| {
4058 const extern_fn = func_payload.data;
4059 const decl_name = mod.declPtr(extern_fn.owner_decl).name;
4060 if (extern_fn.lib_name) |lib_name| {
4061 log.debug("TODO enforce that '{s}' is expected in '{s}' library", .{
4062 decl_name,
4063 lib_name,
4064 });
40744065 }
4075 } else {
4076 assert(ty.zigTypeTag() == .Pointer);
4077 const mcv = try self.resolveInst(callee);
4078 try self.genSetReg(Type.initTag(.usize), .rax, mcv);
4079 _ = try self.addInst(.{
4080 .tag = .call,
4081 .ops = Mir.Inst.Ops.encode(.{
4082 .reg1 = .rax,
4083 .flags = 0b01,
4084 }),
4085 .data = undefined,
4086 });
4087 }
4088 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
4089 if (self.air.value(callee)) |func_value| {
4090 if (func_value.castTag(.function)) |func_payload| {
4091 const func = func_payload.data;
4092 const fn_owner_decl = mod.declPtr(func.owner_decl);
4093 const sym_index = fn_owner_decl.link.macho.sym_index;
4066
4067 if (self.bin_file.cast(link.File.Coff)) |coff_file| {
4068 const sym_index = try coff_file.getGlobalSymbol(mem.sliceTo(decl_name, 0));
40944069 try self.genSetReg(Type.initTag(.usize), .rax, .{
40954070 .linker_load = .{
4096 .type = .got,
4071 .type = .import,
40974072 .sym_index = sym_index,
40984073 },
40994074 });
4100 // callq *%rax
41014075 _ = try self.addInst(.{
41024076 .tag = .call,
41034077 .ops = Mir.Inst.Ops.encode(.{
......@@ -4106,71 +4080,37 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
41064080 }),
41074081 .data = undefined,
41084082 });
4109 } else if (func_value.castTag(.extern_fn)) |func_payload| {
4110 const extern_fn = func_payload.data;
4111 const decl_name = mod.declPtr(extern_fn.owner_decl).name;
4112 if (extern_fn.lib_name) |lib_name| {
4113 log.debug("TODO enforce that '{s}' is expected in '{s}' library", .{
4114 decl_name,
4115 lib_name,
4116 });
4117 }
4083 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
41184084 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().?;
41194087 _ = try self.addInst(.{
41204088 .tag = .call_extern,
41214089 .ops = undefined,
4122 .data = .{
4123 .relocation = .{
4124 .atom_index = mod.declPtr(self.mod_fn.owner_decl).link.macho.sym_index,
4125 .sym_index = sym_index,
4126 },
4127 },
4090 .data = .{ .relocation = .{
4091 .atom_index = atom_index,
4092 .sym_index = sym_index,
4093 } },
41284094 });
41294095 } else {
4130 return self.fail("TODO implement calling bitcasted functions", .{});
4096 return self.fail("TODO implement calling extern functions", .{});
41314097 }
41324098 } else {
4133 assert(ty.zigTypeTag() == .Pointer);
4134 const mcv = try self.resolveInst(callee);
4135 try self.genSetReg(Type.initTag(.usize), .rax, mcv);
4136 _ = try self.addInst(.{
4137 .tag = .call,
4138 .ops = Mir.Inst.Ops.encode(.{
4139 .reg1 = .rax,
4140 .flags = 0b01,
4141 }),
4142 .data = undefined,
4143 });
4099 return self.fail("TODO implement calling bitcasted functions", .{});
41444100 }
4145 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
4146 if (self.air.value(callee)) |func_value| {
4147 if (func_value.castTag(.function)) |func_payload| {
4148 try p9.seeDecl(func_payload.data.owner_decl);
4149 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
4150 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
4151 const got_addr = p9.bases.data;
4152 const got_index = mod.declPtr(func_payload.data.owner_decl).link.plan9.got_index.?;
4153 const fn_got_addr = got_addr + got_index * ptr_bytes;
4154 _ = try self.addInst(.{
4155 .tag = .call,
4156 .ops = Mir.Inst.Ops.encode(.{ .flags = 0b01 }),
4157 .data = .{ .imm = @intCast(u32, fn_got_addr) },
4158 });
4159 } else return self.fail("TODO implement calling extern fn on plan9", .{});
4160 } else {
4161 assert(ty.zigTypeTag() == .Pointer);
4162 const mcv = try self.resolveInst(callee);
4163 try self.genSetReg(Type.initTag(.usize), .rax, mcv);
4164 _ = try self.addInst(.{
4165 .tag = .call,
4166 .ops = Mir.Inst.Ops.encode(.{
4167 .reg1 = .rax,
4168 .flags = 0b01,
4169 }),
4170 .data = undefined,
4171 });
4172 }
4173 } else unreachable;
4101 } else {
4102 assert(ty.zigTypeTag() == .Pointer);
4103 const mcv = try self.resolveInst(callee);
4104 try self.genSetReg(Type.initTag(.usize), .rax, mcv);
4105 _ = try self.addInst(.{
4106 .tag = .call,
4107 .ops = Mir.Inst.Ops.encode(.{
4108 .reg1 = .rax,
4109 .flags = 0b01,
4110 }),
4111 .data = undefined,
4112 });
4113 }
41744114
41754115 if (info.stack_byte_count > 0) {
41764116 // Readjust the stack
......@@ -6781,24 +6721,27 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne
67816721 module.markDeclAlive(decl);
67826722
67836723 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
6784 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
6785 const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes;
6786 return MCValue{ .memory = got_addr };
6787 } else if (self.bin_file.cast(link.File.MachO)) |_| {
6788 assert(decl.link.macho.sym_index != 0);
6724 const atom_index = try elf_file.getOrCreateAtomForDecl(decl_index);
6725 const atom = elf_file.getAtom(atom_index);
6726 return MCValue{ .memory = atom.getOffsetTableAddress(elf_file) };
6727 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
6728 const atom_index = try macho_file.getOrCreateAtomForDecl(decl_index);
6729 const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?;
67896730 return MCValue{ .linker_load = .{
67906731 .type = .got,
6791 .sym_index = decl.link.macho.sym_index,
6732 .sym_index = sym_index,
67926733 } };
6793 } else if (self.bin_file.cast(link.File.Coff)) |_| {
6794 assert(decl.link.coff.sym_index != 0);
6734 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
6735 const atom_index = try coff_file.getOrCreateAtomForDecl(decl_index);
6736 const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?;
67956737 return MCValue{ .linker_load = .{
67966738 .type = .got,
6797 .sym_index = decl.link.coff.sym_index,
6739 .sym_index = sym_index,
67986740 } };
67996741 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
6800 try p9.seeDecl(decl_index);
6801 const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes;
6742 const decl_block_index = try p9.seeDecl(decl_index);
6743 const decl_block = p9.getDeclBlock(decl_block_index);
6744 const got_addr = p9.bases.data + decl_block.got_index.? * ptr_bytes;
68026745 return MCValue{ .memory = got_addr };
68036746 } else {
68046747 return self.fail("TODO codegen non-ELF const Decl pointer", .{});
......@@ -6811,8 +6754,7 @@ fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue {
68116754 return self.fail("lowering unnamed constant failed: {s}", .{@errorName(err)});
68126755 };
68136756 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
6814 const vaddr = elf_file.local_symbols.items[local_sym_index].st_value;
6815 return MCValue{ .memory = vaddr };
6757 return MCValue{ .memory = elf_file.getSymbol(local_sym_index).st_value };
68166758 } else if (self.bin_file.cast(link.File.MachO)) |_| {
68176759 return MCValue{ .linker_load = .{
68186760 .type = .direct,
src/arch/x86_64/Emit.zig+8-8
......@@ -1001,8 +1001,8 @@ fn mirLeaPic(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
10011001 0b01 => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_SIGNED),
10021002 else => unreachable,
10031003 };
1004 const atom = macho_file.getAtomForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?;
1005 try atom.addRelocation(macho_file, .{
1004 const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?;
1005 try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{
10061006 .type = reloc_type,
10071007 .target = .{ .sym_index = relocation.sym_index, .file = null },
10081008 .offset = @intCast(u32, end_offset - 4),
......@@ -1011,8 +1011,8 @@ fn mirLeaPic(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
10111011 .length = 2,
10121012 });
10131013 } else if (emit.bin_file.cast(link.File.Coff)) |coff_file| {
1014 const atom = coff_file.getAtomForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?;
1015 try atom.addRelocation(coff_file, .{
1014 const atom_index = coff_file.getAtomIndexForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?;
1015 try link.File.Coff.Atom.addRelocation(coff_file, atom_index, .{
10161016 .type = switch (ops.flags) {
10171017 0b00 => .got,
10181018 0b01 => .direct,
......@@ -1140,9 +1140,9 @@ fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
11401140
11411141 if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
11421142 // 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 }).?;
11441144 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, .{
11461146 .type = @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_BRANCH),
11471147 .target = target,
11481148 .offset = offset,
......@@ -1152,9 +1152,9 @@ fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
11521152 });
11531153 } else if (emit.bin_file.cast(link.File.Coff)) |coff_file| {
11541154 // 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 }).?;
11561156 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, .{
11581158 .type = .direct,
11591159 .target = target,
11601160 .offset = offset,
src/codegen.zig+58-131
......@@ -21,16 +21,11 @@ const TypedValue = @import("TypedValue.zig");
2121const Value = @import("value.zig").Value;
2222const Zir = @import("Zir.zig");
2323
24pub const FnResult = union(enum) {
25 /// The `code` parameter passed to `generateSymbol` has the value appended.
26 appended: void,
27 fail: *ErrorMsg,
28};
2924pub const Result = union(enum) {
30 /// The `code` parameter passed to `generateSymbol` has the value appended.
31 appended: void,
32 /// The value is available externally, `code` is unused.
33 externally_managed: []const u8,
25 /// The `code` parameter passed to `generateSymbol` has the value ok.
26 ok: void,
27
28 /// There was a codegen error.
3429 fail: *ErrorMsg,
3530};
3631
......@@ -89,7 +84,7 @@ pub fn generateFunction(
8984 liveness: Liveness,
9085 code: *std.ArrayList(u8),
9186 debug_output: DebugInfoOutput,
92) GenerateSymbolError!FnResult {
87) GenerateSymbolError!Result {
9388 switch (bin_file.options.target.cpu.arch) {
9489 .arm,
9590 .armeb,
......@@ -145,7 +140,7 @@ pub fn generateSymbol(
145140 if (typed_value.val.isUndefDeep()) {
146141 const abi_size = math.cast(usize, typed_value.ty.abiSize(target)) orelse return error.Overflow;
147142 try code.appendNTimes(0xaa, abi_size);
148 return Result{ .appended = {} };
143 return Result.ok;
149144 }
150145
151146 switch (typed_value.ty.zigTypeTag()) {
......@@ -176,7 +171,7 @@ pub fn generateSymbol(
176171 128 => writeFloat(f128, typed_value.val.toFloat(f128), target, endian, try code.addManyAsArray(16)),
177172 else => unreachable,
178173 }
179 return Result{ .appended = {} };
174 return Result.ok;
180175 },
181176 .Array => switch (typed_value.val.tag()) {
182177 .bytes => {
......@@ -185,7 +180,7 @@ pub fn generateSymbol(
185180 // The bytes payload already includes the sentinel, if any
186181 try code.ensureUnusedCapacity(len);
187182 code.appendSliceAssumeCapacity(bytes[0..len]);
188 return Result{ .appended = {} };
183 return Result.ok;
189184 },
190185 .str_lit => {
191186 const str_lit = typed_value.val.castTag(.str_lit).?.data;
......@@ -197,7 +192,7 @@ pub fn generateSymbol(
197192 const byte = @intCast(u8, sent_val.toUnsignedInt(target));
198193 code.appendAssumeCapacity(byte);
199194 }
200 return Result{ .appended = {} };
195 return Result.ok;
201196 },
202197 .aggregate => {
203198 const elem_vals = typed_value.val.castTag(.aggregate).?.data;
......@@ -208,14 +203,11 @@ pub fn generateSymbol(
208203 .ty = elem_ty,
209204 .val = elem_val,
210205 }, code, debug_output, reloc_info)) {
211 .appended => {},
212 .externally_managed => |slice| {
213 code.appendSliceAssumeCapacity(slice);
214 },
206 .ok => {},
215207 .fail => |em| return Result{ .fail = em },
216208 }
217209 }
218 return Result{ .appended = {} };
210 return Result.ok;
219211 },
220212 .repeated => {
221213 const array = typed_value.val.castTag(.repeated).?.data;
......@@ -229,10 +221,7 @@ pub fn generateSymbol(
229221 .ty = elem_ty,
230222 .val = array,
231223 }, code, debug_output, reloc_info)) {
232 .appended => {},
233 .externally_managed => |slice| {
234 code.appendSliceAssumeCapacity(slice);
235 },
224 .ok => {},
236225 .fail => |em| return Result{ .fail = em },
237226 }
238227 }
......@@ -242,15 +231,12 @@ pub fn generateSymbol(
242231 .ty = elem_ty,
243232 .val = sentinel_val,
244233 }, code, debug_output, reloc_info)) {
245 .appended => {},
246 .externally_managed => |slice| {
247 code.appendSliceAssumeCapacity(slice);
248 },
234 .ok => {},
249235 .fail => |em| return Result{ .fail = em },
250236 }
251237 }
252238
253 return Result{ .appended = {} };
239 return Result.ok;
254240 },
255241 .empty_array_sentinel => {
256242 const elem_ty = typed_value.ty.childType();
......@@ -259,13 +245,10 @@ pub fn generateSymbol(
259245 .ty = elem_ty,
260246 .val = sentinel_val,
261247 }, code, debug_output, reloc_info)) {
262 .appended => {},
263 .externally_managed => |slice| {
264 code.appendSliceAssumeCapacity(slice);
265 },
248 .ok => {},
266249 .fail => |em| return Result{ .fail = em },
267250 }
268 return Result{ .appended = {} };
251 return Result.ok;
269252 },
270253 else => return Result{
271254 .fail = try ErrorMsg.create(
......@@ -289,7 +272,7 @@ pub fn generateSymbol(
289272 },
290273 else => unreachable,
291274 }
292 return Result{ .appended = {} };
275 return Result.ok;
293276 },
294277 .variable => {
295278 const decl = typed_value.val.castTag(.variable).?.data.owner_decl;
......@@ -309,10 +292,7 @@ pub fn generateSymbol(
309292 .ty = slice_ptr_field_type,
310293 .val = slice.ptr,
311294 }, code, debug_output, reloc_info)) {
312 .appended => {},
313 .externally_managed => |external_slice| {
314 code.appendSliceAssumeCapacity(external_slice);
315 },
295 .ok => {},
316296 .fail => |em| return Result{ .fail = em },
317297 }
318298
......@@ -321,14 +301,11 @@ pub fn generateSymbol(
321301 .ty = Type.initTag(.usize),
322302 .val = slice.len,
323303 }, code, debug_output, reloc_info)) {
324 .appended => {},
325 .externally_managed => |external_slice| {
326 code.appendSliceAssumeCapacity(external_slice);
327 },
304 .ok => {},
328305 .fail => |em| return Result{ .fail = em },
329306 }
330307
331 return Result{ .appended = {} };
308 return Result.ok;
332309 },
333310 .field_ptr => {
334311 const field_ptr = typed_value.val.castTag(.field_ptr).?.data;
......@@ -375,13 +352,10 @@ pub fn generateSymbol(
375352 .ty = typed_value.ty,
376353 .val = container_ptr,
377354 }, code, debug_output, reloc_info)) {
378 .appended => {},
379 .externally_managed => |external_slice| {
380 code.appendSliceAssumeCapacity(external_slice);
381 },
355 .ok => {},
382356 .fail => |em| return Result{ .fail = em },
383357 }
384 return Result{ .appended = {} };
358 return Result.ok;
385359 },
386360 else => return Result{
387361 .fail = try ErrorMsg.create(
......@@ -434,7 +408,7 @@ pub fn generateSymbol(
434408 .signed => @bitCast(u8, @intCast(i8, typed_value.val.toSignedInt(target))),
435409 };
436410 try code.append(x);
437 return Result{ .appended = {} };
411 return Result.ok;
438412 }
439413 if (info.bits > 64) {
440414 var bigint_buffer: Value.BigIntSpace = undefined;
......@@ -443,7 +417,7 @@ pub fn generateSymbol(
443417 const start = code.items.len;
444418 try code.resize(start + abi_size);
445419 bigint.writeTwosComplement(code.items[start..][0..abi_size], endian);
446 return Result{ .appended = {} };
420 return Result.ok;
447421 }
448422 switch (info.signedness) {
449423 .unsigned => {
......@@ -471,7 +445,7 @@ pub fn generateSymbol(
471445 }
472446 },
473447 }
474 return Result{ .appended = {} };
448 return Result.ok;
475449 },
476450 .Enum => {
477451 var int_buffer: Value.Payload.U64 = undefined;
......@@ -481,7 +455,7 @@ pub fn generateSymbol(
481455 if (info.bits <= 8) {
482456 const x = @intCast(u8, int_val.toUnsignedInt(target));
483457 try code.append(x);
484 return Result{ .appended = {} };
458 return Result.ok;
485459 }
486460 if (info.bits > 64) {
487461 return Result{
......@@ -519,12 +493,12 @@ pub fn generateSymbol(
519493 }
520494 },
521495 }
522 return Result{ .appended = {} };
496 return Result.ok;
523497 },
524498 .Bool => {
525499 const x: u8 = @boolToInt(typed_value.val.toBool());
526500 try code.append(x);
527 return Result{ .appended = {} };
501 return Result.ok;
528502 },
529503 .Struct => {
530504 if (typed_value.ty.containerLayout() == .Packed) {
......@@ -549,12 +523,7 @@ pub fn generateSymbol(
549523 .ty = field_ty,
550524 .val = field_val,
551525 }, &tmp_list, debug_output, reloc_info)) {
552 .appended => {
553 mem.copy(u8, code.items[current_pos..], tmp_list.items);
554 },
555 .externally_managed => |external_slice| {
556 mem.copy(u8, code.items[current_pos..], external_slice);
557 },
526 .ok => mem.copy(u8, code.items[current_pos..], tmp_list.items),
558527 .fail => |em| return Result{ .fail = em },
559528 }
560529 } else {
......@@ -563,7 +532,7 @@ pub fn generateSymbol(
563532 bits += @intCast(u16, field_ty.bitSize(target));
564533 }
565534
566 return Result{ .appended = {} };
535 return Result.ok;
567536 }
568537
569538 const struct_begin = code.items.len;
......@@ -576,10 +545,7 @@ pub fn generateSymbol(
576545 .ty = field_ty,
577546 .val = field_val,
578547 }, code, debug_output, reloc_info)) {
579 .appended => {},
580 .externally_managed => |external_slice| {
581 code.appendSliceAssumeCapacity(external_slice);
582 },
548 .ok => {},
583549 .fail => |em| return Result{ .fail = em },
584550 }
585551 const unpadded_field_end = code.items.len - struct_begin;
......@@ -593,7 +559,7 @@ pub fn generateSymbol(
593559 }
594560 }
595561
596 return Result{ .appended = {} };
562 return Result.ok;
597563 },
598564 .Union => {
599565 const union_obj = typed_value.val.castTag(.@"union").?.data;
......@@ -612,10 +578,7 @@ pub fn generateSymbol(
612578 .ty = typed_value.ty.unionTagType().?,
613579 .val = union_obj.tag,
614580 }, code, debug_output, reloc_info)) {
615 .appended => {},
616 .externally_managed => |external_slice| {
617 code.appendSliceAssumeCapacity(external_slice);
618 },
581 .ok => {},
619582 .fail => |em| return Result{ .fail = em },
620583 }
621584 }
......@@ -632,10 +595,7 @@ pub fn generateSymbol(
632595 .ty = field_ty,
633596 .val = union_obj.val,
634597 }, code, debug_output, reloc_info)) {
635 .appended => {},
636 .externally_managed => |external_slice| {
637 code.appendSliceAssumeCapacity(external_slice);
638 },
598 .ok => {},
639599 .fail => |em| return Result{ .fail = em },
640600 }
641601
......@@ -650,15 +610,12 @@ pub fn generateSymbol(
650610 .ty = union_ty.tag_ty,
651611 .val = union_obj.tag,
652612 }, code, debug_output, reloc_info)) {
653 .appended => {},
654 .externally_managed => |external_slice| {
655 code.appendSliceAssumeCapacity(external_slice);
656 },
613 .ok => {},
657614 .fail => |em| return Result{ .fail = em },
658615 }
659616 }
660617
661 return Result{ .appended = {} };
618 return Result.ok;
662619 },
663620 .Optional => {
664621 var opt_buf: Type.Payload.ElemType = undefined;
......@@ -669,7 +626,7 @@ pub fn generateSymbol(
669626
670627 if (!payload_type.hasRuntimeBits()) {
671628 try code.writer().writeByteNTimes(@boolToInt(is_pl), abi_size);
672 return Result{ .appended = {} };
629 return Result.ok;
673630 }
674631
675632 if (typed_value.ty.optionalReprIsPayload()) {
......@@ -678,10 +635,7 @@ pub fn generateSymbol(
678635 .ty = payload_type,
679636 .val = payload.data,
680637 }, code, debug_output, reloc_info)) {
681 .appended => {},
682 .externally_managed => |external_slice| {
683 code.appendSliceAssumeCapacity(external_slice);
684 },
638 .ok => {},
685639 .fail => |em| return Result{ .fail = em },
686640 }
687641 } else if (!typed_value.val.isNull()) {
......@@ -689,17 +643,14 @@ pub fn generateSymbol(
689643 .ty = payload_type,
690644 .val = typed_value.val,
691645 }, code, debug_output, reloc_info)) {
692 .appended => {},
693 .externally_managed => |external_slice| {
694 code.appendSliceAssumeCapacity(external_slice);
695 },
646 .ok => {},
696647 .fail => |em| return Result{ .fail = em },
697648 }
698649 } else {
699650 try code.writer().writeByteNTimes(0, abi_size);
700651 }
701652
702 return Result{ .appended = {} };
653 return Result.ok;
703654 }
704655
705656 const value = if (typed_value.val.castTag(.opt_payload)) |payload| payload.data else Value.initTag(.undef);
......@@ -708,14 +659,11 @@ pub fn generateSymbol(
708659 .ty = payload_type,
709660 .val = value,
710661 }, code, debug_output, reloc_info)) {
711 .appended => {},
712 .externally_managed => |external_slice| {
713 code.appendSliceAssumeCapacity(external_slice);
714 },
662 .ok => {},
715663 .fail => |em| return Result{ .fail = em },
716664 }
717665
718 return Result{ .appended = {} };
666 return Result.ok;
719667 },
720668 .ErrorUnion => {
721669 const error_ty = typed_value.ty.errorUnionSet();
......@@ -740,10 +688,7 @@ pub fn generateSymbol(
740688 .ty = error_ty,
741689 .val = if (is_payload) Value.initTag(.zero) else typed_value.val,
742690 }, code, debug_output, reloc_info)) {
743 .appended => {},
744 .externally_managed => |external_slice| {
745 code.appendSliceAssumeCapacity(external_slice);
746 },
691 .ok => {},
747692 .fail => |em| return Result{ .fail = em },
748693 }
749694 }
......@@ -756,10 +701,7 @@ pub fn generateSymbol(
756701 .ty = payload_ty,
757702 .val = payload_val,
758703 }, code, debug_output, reloc_info)) {
759 .appended => {},
760 .externally_managed => |external_slice| {
761 code.appendSliceAssumeCapacity(external_slice);
762 },
704 .ok => {},
763705 .fail => |em| return Result{ .fail = em },
764706 }
765707 const unpadded_end = code.items.len - begin;
......@@ -778,10 +720,7 @@ pub fn generateSymbol(
778720 .ty = error_ty,
779721 .val = if (is_payload) Value.initTag(.zero) else typed_value.val,
780722 }, code, debug_output, reloc_info)) {
781 .appended => {},
782 .externally_managed => |external_slice| {
783 code.appendSliceAssumeCapacity(external_slice);
784 },
723 .ok => {},
785724 .fail => |em| return Result{ .fail = em },
786725 }
787726 const unpadded_end = code.items.len - begin;
......@@ -793,7 +732,7 @@ pub fn generateSymbol(
793732 }
794733 }
795734
796 return Result{ .appended = {} };
735 return Result.ok;
797736 },
798737 .ErrorSet => {
799738 switch (typed_value.val.tag()) {
......@@ -806,7 +745,7 @@ pub fn generateSymbol(
806745 try code.writer().writeByteNTimes(0, @intCast(usize, Type.anyerror.abiSize(target)));
807746 },
808747 }
809 return Result{ .appended = {} };
748 return Result.ok;
810749 },
811750 .Vector => switch (typed_value.val.tag()) {
812751 .bytes => {
......@@ -814,7 +753,7 @@ pub fn generateSymbol(
814753 const len = @intCast(usize, typed_value.ty.arrayLen());
815754 try code.ensureUnusedCapacity(len);
816755 code.appendSliceAssumeCapacity(bytes[0..len]);
817 return Result{ .appended = {} };
756 return Result.ok;
818757 },
819758 .aggregate => {
820759 const elem_vals = typed_value.val.castTag(.aggregate).?.data;
......@@ -825,14 +764,11 @@ pub fn generateSymbol(
825764 .ty = elem_ty,
826765 .val = elem_val,
827766 }, code, debug_output, reloc_info)) {
828 .appended => {},
829 .externally_managed => |slice| {
830 code.appendSliceAssumeCapacity(slice);
831 },
767 .ok => {},
832768 .fail => |em| return Result{ .fail = em },
833769 }
834770 }
835 return Result{ .appended = {} };
771 return Result.ok;
836772 },
837773 .repeated => {
838774 const array = typed_value.val.castTag(.repeated).?.data;
......@@ -845,14 +781,11 @@ pub fn generateSymbol(
845781 .ty = elem_ty,
846782 .val = array,
847783 }, code, debug_output, reloc_info)) {
848 .appended => {},
849 .externally_managed => |slice| {
850 code.appendSliceAssumeCapacity(slice);
851 },
784 .ok => {},
852785 .fail => |em| return Result{ .fail = em },
853786 }
854787 }
855 return Result{ .appended = {} };
788 return Result.ok;
856789 },
857790 .str_lit => {
858791 const str_lit = typed_value.val.castTag(.str_lit).?.data;
......@@ -860,7 +793,7 @@ pub fn generateSymbol(
860793 const bytes = mod.string_literal_bytes.items[str_lit.index..][0..str_lit.len];
861794 try code.ensureUnusedCapacity(str_lit.len);
862795 code.appendSliceAssumeCapacity(bytes);
863 return Result{ .appended = {} };
796 return Result.ok;
864797 },
865798 else => unreachable,
866799 },
......@@ -901,10 +834,7 @@ fn lowerDeclRef(
901834 .ty = slice_ptr_field_type,
902835 .val = typed_value.val,
903836 }, code, debug_output, reloc_info)) {
904 .appended => {},
905 .externally_managed => |external_slice| {
906 code.appendSliceAssumeCapacity(external_slice);
907 },
837 .ok => {},
908838 .fail => |em| return Result{ .fail = em },
909839 }
910840
......@@ -917,14 +847,11 @@ fn lowerDeclRef(
917847 .ty = Type.usize,
918848 .val = Value.initPayload(&slice_len.base),
919849 }, code, debug_output, reloc_info)) {
920 .appended => {},
921 .externally_managed => |external_slice| {
922 code.appendSliceAssumeCapacity(external_slice);
923 },
850 .ok => {},
924851 .fail => |em| return Result{ .fail = em },
925852 }
926853
927 return Result{ .appended = {} };
854 return Result.ok;
928855 }
929856
930857 const ptr_width = target.cpu.arch.ptrBitWidth();
......@@ -932,7 +859,7 @@ fn lowerDeclRef(
932859 const is_fn_body = decl.ty.zigTypeTag() == .Fn;
933860 if (!is_fn_body and !decl.ty.hasRuntimeBits()) {
934861 try code.writer().writeByteNTimes(0xaa, @divExact(ptr_width, 8));
935 return Result{ .appended = {} };
862 return Result.ok;
936863 }
937864
938865 module.markDeclAlive(decl);
......@@ -950,7 +877,7 @@ fn lowerDeclRef(
950877 else => unreachable,
951878 }
952879
953 return Result{ .appended = {} };
880 return Result.ok;
954881}
955882
956883pub 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;
1616const LazySrcLoc = Module.LazySrcLoc;
1717const Air = @import("../Air.zig");
1818const Liveness = @import("../Liveness.zig");
19const CType = @import("../type.zig").CType;
2019
2120const target_util = @import("../target.zig");
2221const libcFloatPrefix = target_util.libcFloatPrefix;
......@@ -1663,6 +1662,22 @@ pub const DeclGen = struct {
16631662 defer buffer.deinit();
16641663
16651664 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
16661681 try buffer.appendSlice(name);
16671682 try buffer.appendSlice(" {\n");
16681683 {
......@@ -1672,7 +1687,7 @@ pub const DeclGen = struct {
16721687 const field_ty = field.value_ptr.ty;
16731688 if (!field_ty.hasRuntimeBits()) continue;
16741689
1675 const alignment = field.value_ptr.abi_align;
1690 const alignment = field.value_ptr.alignment(dg.module.getTarget(), t.containerLayout());
16761691 const field_name = CValue{ .identifier = field.key_ptr.* };
16771692 try buffer.append(' ');
16781693 try dg.renderTypeAndName(buffer.writer(), field_ty, field_name, .Mut, alignment, .Complete);
......@@ -1682,7 +1697,7 @@ pub const DeclGen = struct {
16821697 }
16831698 if (empty) try buffer.appendSlice(" char empty_struct;\n");
16841699 }
1685 try buffer.appendSlice("};\n");
1700 if (needs_pack_attr) try buffer.appendSlice("});\n") else try buffer.appendSlice("};\n");
16861701
16871702 const rendered = try buffer.toOwnedSlice();
16881703 errdefer dg.typedefs.allocator.free(rendered);
......@@ -2367,8 +2382,13 @@ pub const DeclGen = struct {
23672382 depth += 1;
23682383 }
23692384
2370 if (alignment != 0 and alignment > ty.abiAlignment(target)) {
2371 try w.print("zig_align({}) ", .{alignment});
2385 if (alignment != 0) {
2386 const abi_alignment = ty.abiAlignment(target);
2387 if (alignment < abi_alignment) {
2388 try w.print("zig_under_align({}) ", .{alignment});
2389 } else if (alignment > abi_alignment) {
2390 try w.print("zig_align({}) ", .{alignment});
2391 }
23722392 }
23732393 try dg.renderType(w, render_ty, kind);
23742394
......@@ -2860,27 +2880,30 @@ pub fn genDecl(o: *Object) !void {
28602880 const w = o.writer();
28612881 if (!is_global) try w.writeAll("static ");
28622882 if (variable.is_threadlocal) try w.writeAll("zig_threadlocal ");
2883 if (o.dg.decl.@"linksection") |section| try w.print("zig_linksection(\"{s}\", ", .{section});
28632884 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)");
28642886 try w.writeAll(" = ");
28652887 try o.dg.renderValue(w, tv.ty, variable.init, .StaticInitializer);
28662888 try w.writeByte(';');
28672889 try o.indent_writer.insertNewline();
28682890 } 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();
28692893 const decl_c_value: CValue = .{ .decl = o.dg.decl_index };
28702894
2871 const fwd_decl_writer = o.dg.fwd_decl.writer();
2872 try fwd_decl_writer.writeAll("static ");
2873 try o.dg.renderTypeAndName(fwd_decl_writer, tv.ty, decl_c_value, .Mut, o.dg.decl.@"align", .Complete);
2895 try fwd_decl_writer.writeAll(if (is_global) "zig_extern " else "static ");
2896 try o.dg.renderTypeAndName(fwd_decl_writer, tv.ty, decl_c_value, .Const, o.dg.decl.@"align", .Complete);
28742897 try fwd_decl_writer.writeAll(";\n");
28752898
2876 const writer = o.writer();
2877 try writer.writeAll("static ");
2878 // TODO ask the Decl if it is const
2879 // https://github.com/ziglang/zig/issues/7582
2880 try o.dg.renderTypeAndName(writer, tv.ty, decl_c_value, .Mut, o.dg.decl.@"align", .Complete);
2881 try writer.writeAll(" = ");
2882 try o.dg.renderValue(writer, tv.ty, tv.val, .StaticInitializer);
2883 try writer.writeAll(";\n");
2899 const w = o.writer();
2900 if (!is_global) try w.writeAll("static ");
2901 if (o.dg.decl.@"linksection") |section| try w.print("zig_linksection(\"{s}\", ", .{section});
2902 try o.dg.renderTypeAndName(w, tv.ty, decl_c_value, .Const, o.dg.decl.@"align", .Complete);
2903 if (o.dg.decl.@"linksection" != null) try w.writeAll(", read)");
2904 try w.writeAll(" = ");
2905 try o.dg.renderValue(w, tv.ty, tv.val, .StaticInitializer);
2906 try w.writeAll(";\n");
28842907 }
28852908}
28862909
......@@ -3726,16 +3749,15 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue {
37263749
37273750 const ptr_val = try f.resolveInst(bin_op.lhs);
37283751 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 });
37323752
37333753 // TODO Sema should emit a different instruction when the store should
37343754 // possibly do the safety 0xaa bytes for undefined.
37353755 const src_val_is_undefined =
37363756 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 });
37383759 return try storeUndefined(f, ptr_info.pointee_type, ptr_val);
3760 }
37393761
37403762 const target = f.object.dg.module.getTarget();
37413763 const is_aligned = ptr_info.@"align" == 0 or
......@@ -3744,6 +3766,9 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue {
37443766 const need_memcpy = !is_aligned or is_array;
37453767 const writer = f.object.writer();
37463768
3769 const src_val = try f.resolveInst(bin_op.rhs);
3770 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
3771
37473772 if (need_memcpy) {
37483773 // For this memcpy to safely work we need the rhs to have the same
37493774 // 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 {
43444369fn airDbgVar(f: *Function, inst: Air.Inst.Index) !CValue {
43454370 const pl_op = f.air.instructions.items(.data)[inst].pl_op;
43464371 const name = f.air.nullTerminatedString(pl_op.payload);
4347 const operand = try f.resolveInst(pl_op.operand);
4348 _ = operand;
4372 const operand_is_undef = if (f.air.value(pl_op.operand)) |v| v.isUndefDeep() else false;
4373 if (!operand_is_undef) _ = try f.resolveInst(pl_op.operand);
4374
43494375 try reap(f, inst, &.{pl_op.operand});
43504376 const writer = f.object.writer();
43514377 try writer.print("/* var:{s} */\n", .{name});
src/codegen/llvm.zig+2-3
......@@ -19,7 +19,6 @@ const Liveness = @import("../Liveness.zig");
1919const Value = @import("../value.zig").Value;
2020const Type = @import("../type.zig").Type;
2121const LazySrcLoc = Module.LazySrcLoc;
22const CType = @import("../type.zig").CType;
2322const x86_64_abi = @import("../arch/x86_64/abi.zig");
2423const wasm_c_abi = @import("../arch/wasm/abi.zig");
2524const aarch64_c_abi = @import("../arch/aarch64/abi.zig");
......@@ -11057,8 +11056,8 @@ fn backendSupportsF128(target: std.Target) bool {
1105711056fn intrinsicsAllowed(scalar_ty: Type, target: std.Target) bool {
1105811057 return switch (scalar_ty.tag()) {
1105911058 .f16 => backendSupportsF16(target),
11060 .f80 => (CType.longdouble.sizeInBits(target) == 80) and backendSupportsF80(target),
11061 .f128 => (CType.longdouble.sizeInBits(target) == 128) and backendSupportsF128(target),
11059 .f80 => (target.c_type_bit_size(.longdouble) == 80) and backendSupportsF80(target),
11060 .f128 => (target.c_type_bit_size(.longdouble) == 128) and backendSupportsF128(target),
1106211061 else => true,
1106311062 };
1106411063}
src/codegen/spirv.zig+19-11
......@@ -49,7 +49,7 @@ pub const DeclGen = struct {
4949 spv: *SpvModule,
5050
5151 /// The decl we are currently generating code for.
52 decl: *Decl,
52 decl_index: Decl.Index,
5353
5454 /// The intermediate code of the declaration we are currently generating. Note: If
5555 /// the declaration is not a function, this value will be undefined!
......@@ -59,6 +59,8 @@ pub const DeclGen = struct {
5959 /// Note: If the declaration is not a function, this value will be undefined!
6060 liveness: Liveness,
6161
62 ids: *const std.AutoHashMap(Decl.Index, IdResult),
63
6264 /// An array of function argument result-ids. Each index corresponds with the
6365 /// function argument of the same index.
6466 args: std.ArrayListUnmanaged(IdRef) = .{},
......@@ -133,14 +135,20 @@ pub const DeclGen = struct {
133135
134136 /// Initialize the common resources of a DeclGen. Some fields are left uninitialized,
135137 /// 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 {
137144 return .{
138145 .gpa = allocator,
139146 .module = module,
140147 .spv = spv,
141 .decl = undefined,
148 .decl_index = undefined,
142149 .air = undefined,
143150 .liveness = undefined,
151 .ids = ids,
144152 .next_arg_index = undefined,
145153 .current_block_label_id = undefined,
146154 .error_msg = undefined,
......@@ -150,9 +158,9 @@ pub const DeclGen = struct {
150158 /// Generate the code for `decl`. If a reportable error occurred during code generation,
151159 /// a message is returned by this function. Callee owns the memory. If this function
152160 /// 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 {
154162 // Reset internal resources, we don't want to re-allocate these.
155 self.decl = decl;
163 self.decl_index = decl_index;
156164 self.air = air;
157165 self.liveness = liveness;
158166 self.args.items.len = 0;
......@@ -194,7 +202,7 @@ pub const DeclGen = struct {
194202 pub fn fail(self: *DeclGen, comptime format: []const u8, args: anytype) Error {
195203 @setCold(true);
196204 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));
198206 assert(self.error_msg == null);
199207 self.error_msg = try Module.ErrorMsg.create(self.module.gpa, src_loc, format, args);
200208 return error.CodegenFail;
......@@ -332,7 +340,7 @@ pub const DeclGen = struct {
332340 };
333341 const decl = self.module.declPtr(fn_decl_index);
334342 self.module.markDeclAlive(decl);
335 return decl.fn_link.spirv.id.toRef();
343 return self.ids.get(fn_decl_index).?.toRef();
336344 }
337345
338346 const target = self.getTarget();
......@@ -553,8 +561,8 @@ pub const DeclGen = struct {
553561 }
554562
555563 fn genDecl(self: *DeclGen) !void {
556 const decl = self.decl;
557 const result_id = decl.fn_link.spirv.id;
564 const result_id = self.ids.get(self.decl_index).?;
565 const decl = self.module.declPtr(self.decl_index);
558566
559567 if (decl.val.castTag(.function)) |_| {
560568 assert(decl.ty.zigTypeTag() == .Fn);
......@@ -945,7 +953,7 @@ pub const DeclGen = struct {
945953
946954 fn airDbgStmt(self: *DeclGen, inst: Air.Inst.Index) !void {
947955 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));
949957 try self.func.body.emit(self.spv.gpa, .OpLine, .{
950958 .file = src_fname_id,
951959 .line = dbg_stmt.line,
......@@ -1106,7 +1114,7 @@ pub const DeclGen = struct {
11061114 assert(as.errors.items.len != 0);
11071115 assert(self.error_msg == null);
11081116 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));
11101118 self.error_msg = try Module.ErrorMsg.create(self.module.gpa, src_loc, "failed to assemble SPIR-V inline assembly", .{});
11111119 const notes = try self.module.gpa.alloc(Module.ErrorMsg, as.errors.items.len);
11121120
src/link.zig+14-69
......@@ -261,39 +261,6 @@ pub const File = struct {
261261 /// of this linking operation.
262262 lock: ?Cache.Lock = null,
263263
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
297264 /// Attempts incremental linking, if the file already exists. If
298265 /// incremental linking fails, falls back to truncating the file and
299266 /// rewriting it. A malicious file is detected as incremental link failure
......@@ -533,8 +500,7 @@ pub const File = struct {
533500 }
534501 }
535502
536 /// May be called before or after updateDeclExports but must be called
537 /// after allocateDeclIndexes for any given Decl.
503 /// May be called before or after updateDeclExports for any given Decl.
538504 pub fn updateDecl(base: *File, module: *Module, decl_index: Module.Decl.Index) UpdateDeclError!void {
539505 const decl = module.declPtr(decl_index);
540506 log.debug("updateDecl {*} ({s}), type={}", .{ decl, decl.name, decl.ty.fmtDebug() });
......@@ -557,8 +523,7 @@ pub const File = struct {
557523 }
558524 }
559525
560 /// May be called before or after updateDeclExports but must be called
561 /// after allocateDeclIndexes for any given Decl.
526 /// May be called before or after updateDeclExports for any given Decl.
562527 pub fn updateFunc(base: *File, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) UpdateDeclError!void {
563528 const owner_decl = module.declPtr(func.owner_decl);
564529 log.debug("updateFunc {*} ({s}), type={}", .{
......@@ -582,48 +547,27 @@ pub const File = struct {
582547 }
583548 }
584549
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);
586552 log.debug("updateDeclLineNumber {*} ({s}), line={}", .{
587553 decl, decl.name, decl.src_line + 1,
588554 });
589555 assert(decl.has_tv);
590556 if (build_options.only_c) {
591557 assert(base.tag == .c);
592 return @fieldParentPtr(C, "base", base).updateDeclLineNumber(module, decl);
558 return @fieldParentPtr(C, "base", base).updateDeclLineNumber(module, decl_index);
593559 }
594560 switch (base.tag) {
595 .coff => return @fieldParentPtr(Coff, "base", base).updateDeclLineNumber(module, decl),
596 .elf => return @fieldParentPtr(Elf, "base", base).updateDeclLineNumber(module, decl),
597 .macho => return @fieldParentPtr(MachO, "base", base).updateDeclLineNumber(module, decl),
598 .c => return @fieldParentPtr(C, "base", base).updateDeclLineNumber(module, decl),
599 .wasm => return @fieldParentPtr(Wasm, "base", base).updateDeclLineNumber(module, decl),
600 .plan9 => return @fieldParentPtr(Plan9, "base", base).updateDeclLineNumber(module, decl),
561 .coff => return @fieldParentPtr(Coff, "base", base).updateDeclLineNumber(module, decl_index),
562 .elf => return @fieldParentPtr(Elf, "base", base).updateDeclLineNumber(module, decl_index),
563 .macho => return @fieldParentPtr(MachO, "base", base).updateDeclLineNumber(module, decl_index),
564 .c => return @fieldParentPtr(C, "base", base).updateDeclLineNumber(module, decl_index),
565 .wasm => return @fieldParentPtr(Wasm, "base", base).updateDeclLineNumber(module, decl_index),
566 .plan9 => return @fieldParentPtr(Plan9, "base", base).updateDeclLineNumber(module, decl_index),
601567 .spirv, .nvptx => {},
602568 }
603569 }
604570
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
627571 pub fn releaseLock(self: *File) void {
628572 if (self.lock) |*lock| {
629573 lock.release();
......@@ -874,8 +818,7 @@ pub const File = struct {
874818 AnalysisFail,
875819 };
876820
877 /// May be called before or after updateDecl, but must be called after
878 /// allocateDeclIndexes for any given Decl.
821 /// May be called before or after updateDecl for any given Decl.
879822 pub fn updateDeclExports(
880823 base: *File,
881824 module: *Module,
......@@ -911,6 +854,8 @@ pub const File = struct {
911854 /// The linker is passed information about the containing atom, `parent_atom_index`, and offset within it's
912855 /// memory buffer, `offset`, so that it can make a note of potential relocation sites, should the
913856 /// `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.
914859 pub fn getDeclVAddr(base: *File, decl_index: Module.Decl.Index, reloc_info: RelocInfo) !u64 {
915860 if (build_options.only_c) unreachable;
916861 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
219219 code.shrinkAndFree(module.gpa, code.items.len);
220220}
221221
222pub fn updateDeclLineNumber(self: *C, module: *Module, decl: *Module.Decl) !void {
222pub fn updateDeclLineNumber(self: *C, module: *Module, decl_index: Module.Decl.Index) !void {
223223 // The C backend does not have the ability to fix line numbers without re-generating
224224 // the entire Decl.
225225 _ = self;
226226 _ = module;
227 _ = decl;
227 _ = decl_index;
228228}
229229
230230pub 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,
7979/// We store them here so that we can properly dispose of any allocated
8080/// memory within the atom in the incremental linker.
8181/// TODO consolidate this.
82decls: std.AutoHashMapUnmanaged(Module.Decl.Index, ?u16) = .{},
82decls: std.AutoHashMapUnmanaged(Module.Decl.Index, DeclMetadata) = .{},
8383
8484/// List of atoms that are either synthetic or map directly to the Zig source program.
85managed_atoms: std.ArrayListUnmanaged(*Atom) = .{},
85atoms: std.ArrayListUnmanaged(Atom) = .{},
8686
8787/// Table of atoms indexed by the symbol index.
88atom_by_index_table: std.AutoHashMapUnmanaged(u32, *Atom) = .{},
88atom_by_index_table: std.AutoHashMapUnmanaged(u32, Atom.Index) = .{},
8989
9090/// Table of unnamed constants associated with a parent `Decl`.
9191/// We store them here so that we can free the constants whenever the `Decl`
......@@ -124,9 +124,9 @@ const Entry = struct {
124124 sym_index: u32,
125125};
126126
127const RelocTable = std.AutoHashMapUnmanaged(*Atom, std.ArrayListUnmanaged(Relocation));
128const BaseRelocationTable = std.AutoHashMapUnmanaged(*Atom, std.ArrayListUnmanaged(u32));
129const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(*Atom));
127const RelocTable = std.AutoHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(Relocation));
128const BaseRelocationTable = std.AutoHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(u32));
129const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(Atom.Index));
130130
131131const default_file_alignment: u16 = 0x200;
132132const default_size_of_stack_reserve: u32 = 0x1000000;
......@@ -137,7 +137,7 @@ const default_size_of_heap_commit: u32 = 0x1000;
137137const Section = struct {
138138 header: coff.SectionHeader,
139139
140 last_atom: ?*Atom = null,
140 last_atom_index: ?Atom.Index = null,
141141
142142 /// A list of atoms that have surplus capacity. This list can have false
143143 /// positives, as functions grow and shrink over time, only sometimes being added
......@@ -154,7 +154,34 @@ const Section = struct {
154154 /// overcapacity can be negative. A simple way to have negative overcapacity is to
155155 /// allocate a fresh atom, which will have ideal capacity, and then grow it
156156 /// by 1 byte. It will then have -1 overcapacity.
157 free_list: std.ArrayListUnmanaged(*Atom) = .{},
157 free_list: std.ArrayListUnmanaged(Atom.Index) = .{},
158};
159
160const 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 }
158185};
159186
160187pub const PtrWidth = enum {
......@@ -168,11 +195,6 @@ pub const PtrWidth = enum {
168195 };
169196 }
170197};
171pub const SrcFn = void;
172
173pub const Export = struct {
174 sym_index: ?u32 = null,
175};
176198
177199pub const SymbolWithLoc = struct {
178200 // Index into the respective symbol table.
......@@ -271,11 +293,7 @@ pub fn deinit(self: *Coff) void {
271293 }
272294 self.sections.deinit(gpa);
273295
274 for (self.managed_atoms.items) |atom| {
275 gpa.destroy(atom);
276 }
277 self.managed_atoms.deinit(gpa);
278
296 self.atoms.deinit(gpa);
279297 self.locals.deinit(gpa);
280298 self.globals.deinit(gpa);
281299
......@@ -297,7 +315,15 @@ pub fn deinit(self: *Coff) void {
297315 self.imports.deinit(gpa);
298316 self.imports_free_list.deinit(gpa);
299317 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
301327 self.atom_by_index_table.deinit(gpa);
302328
303329 {
......@@ -461,17 +487,18 @@ fn growSectionVM(self: *Coff, sect_id: u32, needed_size: u32) !void {
461487 // TODO: enforce order by increasing VM addresses in self.sections container.
462488 // This is required by the loader anyhow as far as I can tell.
463489 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];
465491 next_header.virtual_address += diff;
466492
467 if (maybe_last_atom.*) |last_atom| {
468 var atom = last_atom;
493 if (maybe_last_atom_index) |last_atom_index| {
494 var atom_index = last_atom_index;
469495 while (true) {
496 const atom = self.getAtom(atom_index);
470497 const sym = atom.getSymbolPtr(self);
471498 sym.value += diff;
472499
473 if (atom.prev) |prev| {
474 atom = prev;
500 if (atom.prev_index) |prev_index| {
501 atom_index = prev_index;
475502 } else break;
476503 }
477504 }
......@@ -480,24 +507,15 @@ fn growSectionVM(self: *Coff, sect_id: u32, needed_size: u32) !void {
480507 header.virtual_size = increased_size;
481508}
482509
483pub fn allocateDeclIndexes(self: *Coff, decl_index: Module.Decl.Index) !void {
484 if (self.llvm_object) |_| return;
485 const decl = self.base.options.module.?.declPtr(decl_index);
486 if (decl.link.coff.sym_index != 0) return;
487 decl.link.coff.sym_index = try self.allocateSymbol();
488 const gpa = self.base.allocator;
489 try self.atom_by_index_table.putNoClobber(gpa, decl.link.coff.sym_index, &decl.link.coff);
490 try self.decls.putNoClobber(gpa, decl_index, null);
491}
492
493fn allocateAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u32 {
510fn allocateAtom(self: *Coff, atom_index: Atom.Index, new_atom_size: u32, alignment: u32) !u32 {
494511 const tracy = trace(@src());
495512 defer tracy.end();
496513
514 const atom = self.getAtom(atom_index);
497515 const sect_id = @enumToInt(atom.getSymbol(self).section_number) - 1;
498516 const header = &self.sections.items(.header)[sect_id];
499517 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];
501519 const new_atom_ideal_capacity = if (header.isCode()) padToIdeal(new_atom_size) else new_atom_size;
502520
503521 // 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
505523 // It would be simpler to do it inside the for loop below, but that would cause a
506524 // problem if an error was returned later in the function. So this action
507525 // 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;
509527 var free_list_removal: ?usize = null;
510528
511529 // 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
513531 var vaddr = blk: {
514532 var i: usize = 0;
515533 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);
517536 // We now have a pointer to a live atom that has too much capacity.
518537 // Is it enough that we could fit this new atom?
519538 const sym = big_atom.getSymbol(self);
......@@ -541,34 +560,43 @@ fn allocateAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u
541560 const keep_free_list_node = remaining_capacity >= min_text_capacity;
542561
543562 // Set up the metadata to be updated, after errors are no longer possible.
544 atom_placement = big_atom;
563 atom_placement = big_atom_index;
545564 if (!keep_free_list_node) {
546565 free_list_removal = i;
547566 }
548567 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);
550570 const last_symbol = last.getSymbol(self);
551571 const ideal_capacity = if (header.isCode()) padToIdeal(last.size) else last.size;
552572 const ideal_capacity_end_vaddr = last_symbol.value + ideal_capacity;
553573 const new_start_vaddr = mem.alignForwardGeneric(u32, ideal_capacity_end_vaddr, alignment);
554 atom_placement = last;
574 atom_placement = last_index;
555575 break :blk new_start_vaddr;
556576 } else {
557577 break :blk mem.alignForwardGeneric(u32, header.virtual_address, alignment);
558578 }
559579 };
560580
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;
562585 if (expand_section) {
563586 const sect_capacity = self.allocatedSize(header.pointer_to_raw_data);
564587 const needed_size: u32 = (vaddr + new_atom_size) - header.virtual_address;
565588 if (needed_size > sect_capacity) {
566589 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);
568592 const sym = last_atom.getSymbol(self);
569593 break :blk (sym.value + last_atom.size) - header.virtual_address;
570594 } 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 });
572600 const amt = try self.base.file.?.copyRangeAll(
573601 header.pointer_to_raw_data,
574602 self.base.file.?,
......@@ -587,26 +615,34 @@ fn allocateAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u
587615
588616 header.virtual_size = @max(header.virtual_size, needed_size);
589617 header.size_of_raw_data = needed_size;
590 maybe_last_atom.* = atom;
618 maybe_last_atom_index.* = atom_index;
591619 }
592620
593 atom.size = new_atom_size;
594 atom.alignment = alignment;
621 {
622 const atom_ptr = self.getAtomPtr(atom_index);
623 atom_ptr.size = new_atom_size;
624 atom_ptr.alignment = alignment;
625 }
595626
596 if (atom.prev) |prev| {
597 prev.next = atom.next;
627 if (atom.prev_index) |prev_index| {
628 const prev = self.getAtomPtr(prev_index);
629 prev.next_index = atom.next_index;
598630 }
599 if (atom.next) |next| {
600 next.prev = atom.prev;
631 if (atom.next_index) |next_index| {
632 const next = self.getAtomPtr(next_index);
633 next.prev_index = atom.prev_index;
601634 }
602635
603 if (atom_placement) |big_atom| {
604 atom.prev = big_atom;
605 atom.next = big_atom.next;
606 big_atom.next = atom;
636 if (atom_placement) |big_atom_index| {
637 const big_atom = self.getAtomPtr(big_atom_index);
638 const atom_ptr = self.getAtomPtr(atom_index);
639 atom_ptr.prev_index = big_atom_index;
640 atom_ptr.next_index = big_atom.next_index;
641 big_atom.next_index = atom_index;
607642 } else {
608 atom.prev = null;
609 atom.next = null;
643 const atom_ptr = self.getAtomPtr(atom_index);
644 atom_ptr.prev_index = null;
645 atom_ptr.next_index = null;
610646 }
611647 if (free_list_removal) |i| {
612648 _ = free_list.swapRemove(i);
......@@ -615,7 +651,7 @@ fn allocateAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u
615651 return vaddr;
616652}
617653
618fn allocateSymbol(self: *Coff) !u32 {
654pub fn allocateSymbol(self: *Coff) !u32 {
619655 const gpa = self.base.allocator;
620656 try self.locals.ensureUnusedCapacity(gpa, 1);
621657
......@@ -711,25 +747,37 @@ pub fn allocateImportEntry(self: *Coff, target: SymbolWithLoc) !u32 {
711747 return index;
712748}
713749
714fn createGotAtom(self: *Coff, target: SymbolWithLoc) !*Atom {
750pub fn createAtom(self: *Coff) !Atom.Index {
715751 const gpa = self.base.allocator;
716 const atom = try gpa.create(Atom);
717 errdefer gpa.destroy(atom);
718 atom.* = Atom.empty;
719 atom.sym_index = try self.allocateSymbol();
752 const atom_index = @intCast(Atom.Index, self.atoms.items.len);
753 const atom = try self.atoms.addOne(gpa);
754 const sym_index = try self.allocateSymbol();
755 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom_index);
756 atom.* = .{
757 .sym_index = sym_index,
758 .file = null,
759 .size = 0,
760 .alignment = 0,
761 .prev_index = null,
762 .next_index = null,
763 };
764 log.debug("creating ATOM(%{d}) at index {d}", .{ sym_index, atom_index });
765 return atom_index;
766}
767
768fn createGotAtom(self: *Coff, target: SymbolWithLoc) !Atom.Index {
769 const atom_index = try self.createAtom();
770 const atom = self.getAtomPtr(atom_index);
720771 atom.size = @sizeOf(u64);
721772 atom.alignment = @alignOf(u64);
722773
723 try self.managed_atoms.append(gpa, atom);
724 try self.atom_by_index_table.putNoClobber(gpa, atom.sym_index, atom);
725
726774 const sym = atom.getSymbolPtr(self);
727775 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);
729777
730778 log.debug("allocated GOT atom at 0x{x}", .{sym.value});
731779
732 try atom.addRelocation(self, .{
780 try Atom.addRelocation(self, atom_index, .{
733781 .type = .direct,
734782 .target = target,
735783 .offset = 0,
......@@ -743,67 +791,67 @@ fn createGotAtom(self: *Coff, target: SymbolWithLoc) !*Atom {
743791 .UNDEFINED => @panic("TODO generate a binding for undefined GOT target"),
744792 .ABSOLUTE => {},
745793 .DEBUG => unreachable, // not possible
746 else => try atom.addBaseRelocation(self, 0),
794 else => try Atom.addBaseRelocation(self, atom_index, 0),
747795 }
748796
749 return atom;
797 return atom_index;
750798}
751799
752fn createImportAtom(self: *Coff) !*Atom {
753 const gpa = self.base.allocator;
754 const atom = try gpa.create(Atom);
755 errdefer gpa.destroy(atom);
756 atom.* = Atom.empty;
757 atom.sym_index = try self.allocateSymbol();
800fn createImportAtom(self: *Coff) !Atom.Index {
801 const atom_index = try self.createAtom();
802 const atom = self.getAtomPtr(atom_index);
758803 atom.size = @sizeOf(u64);
759804 atom.alignment = @alignOf(u64);
760805
761 try self.managed_atoms.append(gpa, atom);
762 try self.atom_by_index_table.putNoClobber(gpa, atom.sym_index, atom);
763
764806 const sym = atom.getSymbolPtr(self);
765807 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);
767809
768810 log.debug("allocated import atom at 0x{x}", .{sym.value});
769811
770 return atom;
812 return atom_index;
771813}
772814
773fn growAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u32 {
815fn growAtom(self: *Coff, atom_index: Atom.Index, new_atom_size: u32, alignment: u32) !u32 {
816 const atom = self.getAtom(atom_index);
774817 const sym = atom.getSymbol(self);
775818 const align_ok = mem.alignBackwardGeneric(u32, sym.value, alignment) == sym.value;
776819 const need_realloc = !align_ok or new_atom_size > atom.capacity(self);
777820 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);
779822}
780823
781fn shrinkAtom(self: *Coff, atom: *Atom, new_block_size: u32) void {
824fn shrinkAtom(self: *Coff, atom_index: Atom.Index, new_block_size: u32) void {
782825 _ = self;
783 _ = atom;
826 _ = atom_index;
784827 _ = new_block_size;
785828 // TODO check the new capacity, and if it crosses the size threshold into a big enough
786829 // capacity, insert a free list node for it.
787830}
788831
789fn writeAtom(self: *Coff, atom: *Atom, code: []const u8) !void {
832fn writeAtom(self: *Coff, atom_index: Atom.Index, code: []const u8) !void {
833 const atom = self.getAtom(atom_index);
790834 const sym = atom.getSymbol(self);
791835 const section = self.sections.get(@enumToInt(sym.section_number) - 1);
792836 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 });
794842 try self.base.file.?.pwriteAll(code, file_offset);
795 try self.resolveRelocs(atom);
843 try self.resolveRelocs(atom_index);
796844}
797845
798fn writePtrWidthAtom(self: *Coff, atom: *Atom) !void {
846fn writePtrWidthAtom(self: *Coff, atom_index: Atom.Index) !void {
799847 switch (self.ptr_width) {
800848 .p32 => {
801849 var buffer: [@sizeOf(u32)]u8 = [_]u8{0} ** @sizeOf(u32);
802 try self.writeAtom(atom, &buffer);
850 try self.writeAtom(atom_index, &buffer);
803851 },
804852 .p64 => {
805853 var buffer: [@sizeOf(u64)]u8 = [_]u8{0} ** @sizeOf(u64);
806 try self.writeAtom(atom, &buffer);
854 try self.writeAtom(atom_index, &buffer);
807855 },
808856 }
809857}
......@@ -823,7 +871,8 @@ fn markRelocsDirtyByAddress(self: *Coff, addr: u32) void {
823871 var it = self.relocs.valueIterator();
824872 while (it.next()) |relocs| {
825873 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);
827876 const target_sym = target_atom.getSymbol(self);
828877 if (target_sym.value < addr) continue;
829878 reloc.dirty = true;
......@@ -831,23 +880,26 @@ fn markRelocsDirtyByAddress(self: *Coff, addr: u32) void {
831880 }
832881}
833882
834fn resolveRelocs(self: *Coff, atom: *Atom) !void {
835 const relocs = self.relocs.get(atom) orelse return;
883fn resolveRelocs(self: *Coff, atom_index: Atom.Index) !void {
884 const relocs = self.relocs.get(atom_index) orelse return;
836885
837 log.debug("relocating '{s}'", .{atom.getName(self)});
886 log.debug("relocating '{s}'", .{self.getAtom(atom_index).getName(self)});
838887
839888 for (relocs.items) |*reloc| {
840889 if (!reloc.dirty) continue;
841 try reloc.resolve(atom, self);
890 try reloc.resolve(atom_index, self);
842891 }
843892}
844893
845fn freeAtom(self: *Coff, atom: *Atom) void {
846 log.debug("freeAtom {*}", .{atom});
894fn freeAtom(self: *Coff, atom_index: Atom.Index) void {
895 log.debug("freeAtom {d}", .{atom_index});
896
897 const gpa = self.base.allocator;
847898
848899 // Remove any relocs and base relocs associated with this Atom
849 self.freeRelocationsForAtom(atom);
900 Atom.freeRelocations(self, atom_index);
850901
902 const atom = self.getAtom(atom_index);
851903 const sym = atom.getSymbol(self);
852904 const sect_id = @enumToInt(sym.section_number) - 1;
853905 const free_list = &self.sections.items(.free_list)[sect_id];
......@@ -856,46 +908,69 @@ fn freeAtom(self: *Coff, atom: *Atom) void {
856908 var i: usize = 0;
857909 // TODO turn free_list into a hash map
858910 while (i < free_list.items.len) {
859 if (free_list.items[i] == atom) {
911 if (free_list.items[i] == atom_index) {
860912 _ = free_list.swapRemove(i);
861913 continue;
862914 }
863 if (free_list.items[i] == atom.prev) {
915 if (free_list.items[i] == atom.prev_index) {
864916 already_have_free_list_node = true;
865917 }
866918 i += 1;
867919 }
868920 }
869921
870 const maybe_last_atom = &self.sections.items(.last_atom)[sect_id];
871 if (maybe_last_atom.*) |last_atom| {
872 if (last_atom == atom) {
873 if (atom.prev) |prev| {
922 const maybe_last_atom_index = &self.sections.items(.last_atom_index)[sect_id];
923 if (maybe_last_atom_index.*) |last_atom_index| {
924 if (last_atom_index == atom_index) {
925 if (atom.prev_index) |prev_index| {
874926 // TODO shrink the section size here
875 maybe_last_atom.* = prev;
927 maybe_last_atom_index.* = prev_index;
876928 } else {
877 maybe_last_atom.* = null;
929 maybe_last_atom_index.* = null;
878930 }
879931 }
880932 }
881933
882 if (atom.prev) |prev| {
883 prev.next = atom.next;
934 if (atom.prev_index) |prev_index| {
935 const prev = self.getAtomPtr(prev_index);
936 prev.next_index = atom.next_index;
884937
885 if (!already_have_free_list_node and prev.freeListEligible(self)) {
938 if (!already_have_free_list_node and prev.*.freeListEligible(self)) {
886939 // The free list is heuristics, it doesn't have to be perfect, so we can
887940 // ignore the OOM here.
888 free_list.append(self.base.allocator, prev) catch {};
941 free_list.append(gpa, prev_index) catch {};
889942 }
890943 } else {
891 atom.prev = null;
944 self.getAtomPtr(atom_index).prev_index = null;
892945 }
893946
894 if (atom.next) |next| {
895 next.prev = atom.prev;
947 if (atom.next_index) |next_index| {
948 self.getAtomPtr(next_index).prev_index = atom.prev_index;
896949 } 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 });
898968 }
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;
899974}
900975
901976pub 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
912987
913988 const decl_index = func.owner_decl;
914989 const decl = module.declPtr(decl_index);
990
991 const atom_index = try self.getOrCreateAtomForDecl(decl_index);
915992 self.freeUnnamedConsts(decl_index);
916 self.freeRelocationsForAtom(&decl.link.coff);
993 Atom.freeRelocations(self, atom_index);
917994
918995 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
919996 defer code_buffer.deinit();
......@@ -928,7 +1005,7 @@ pub fn updateFunc(self: *Coff, module: *Module, func: *Module.Fn, air: Air, live
9281005 .none,
9291006 );
9301007 const code = switch (res) {
931 .appended => code_buffer.items,
1008 .ok => code_buffer.items,
9321009 .fail => |em| {
9331010 decl.analysis = .codegen_failure;
9341011 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
9571034 }
9581035 const unnamed_consts = gop.value_ptr;
9591036
960 const atom = try gpa.create(Atom);
961 errdefer gpa.destroy(atom);
962 atom.* = Atom.empty;
1037 const atom_index = try self.createAtom();
9631038
964 atom.sym_index = try self.allocateSymbol();
965 const sym = atom.getSymbolPtr(self);
9661039 const sym_name = blk: {
9671040 const decl_name = try decl.getFullyQualifiedName(mod);
9681041 defer gpa.free(decl_name);
......@@ -971,18 +1044,18 @@ pub fn lowerUnnamedConst(self: *Coff, tv: TypedValue, decl_index: Module.Decl.In
9711044 break :blk try std.fmt.allocPrint(gpa, "__unnamed_{s}_{d}", .{ decl_name, index });
9721045 };
9731046 defer gpa.free(sym_name);
974 try self.setSymbolName(sym, sym_name);
975 sym.section_number = @intToEnum(coff.SectionNumber, self.rdata_section_index.? + 1);
976
977 try self.managed_atoms.append(gpa, atom);
978 try self.atom_by_index_table.putNoClobber(gpa, atom.sym_index, atom);
1047 {
1048 const atom = self.getAtom(atom_index);
1049 const sym = atom.getSymbolPtr(self);
1050 try self.setSymbolName(sym, sym_name);
1051 sym.section_number = @intToEnum(coff.SectionNumber, self.rdata_section_index.? + 1);
1052 }
9791053
9801054 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().?,
9821056 });
9831057 const code = switch (res) {
984 .externally_managed => |x| x,
985 .appended => code_buffer.items,
1058 .ok => code_buffer.items,
9861059 .fail => |em| {
9871060 decl.analysis = .codegen_failure;
9881061 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
9921065 };
9931066
9941067 const required_alignment = tv.ty.abiAlignment(self.base.options.target);
1068 const atom = self.getAtomPtr(atom_index);
9951069 atom.alignment = required_alignment;
9961070 atom.size = @intCast(u32, code.len);
997 sym.value = try self.allocateAtom(atom, atom.size, atom.alignment);
998 errdefer self.freeAtom(atom);
1071 atom.getSymbolPtr(self).value = try self.allocateAtom(atom_index, atom.size, atom.alignment);
1072 errdefer self.freeAtom(atom_index);
9991073
1000 try unnamed_consts.append(gpa, atom);
1074 try unnamed_consts.append(gpa, atom_index);
10011075
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 });
10031077 log.debug(" (required alignment 0x{x})", .{required_alignment});
10041078
1005 try self.writeAtom(atom, code);
1079 try self.writeAtom(atom_index, code);
10061080
1007 return atom.sym_index;
1081 return atom.getSymbolIndex().?;
10081082}
10091083
10101084pub 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) !
10291103 }
10301104 }
10311105
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);
10331109
10341110 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
10351111 defer code_buffer.deinit();
......@@ -1039,11 +1115,10 @@ pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) !
10391115 .ty = decl.ty,
10401116 .val = decl_val,
10411117 }, &code_buffer, .none, .{
1042 .parent_atom_index = decl.link.coff.sym_index,
1118 .parent_atom_index = atom.getSymbolIndex().?,
10431119 });
10441120 const code = switch (res) {
1045 .externally_managed => |x| x,
1046 .appended => code_buffer.items,
1121 .ok => code_buffer.items,
10471122 .fail => |em| {
10481123 decl.analysis = .codegen_failure;
10491124 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) !
10581133 return self.updateDeclExports(module, decl_index, module.getDeclExports(decl_index));
10591134}
10601135
1061fn getDeclOutputSection(self: *Coff, decl: *Module.Decl) u16 {
1136pub 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
1148fn getDeclOutputSection(self: *Coff, decl_index: Module.Decl.Index) u16 {
1149 const decl = self.base.options.module.?.declPtr(decl_index);
10621150 const ty = decl.ty;
10631151 const zig_ty = ty.zigTypeTag();
10641152 const val = decl.val;
......@@ -1093,15 +1181,12 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []const u8,
10931181 log.debug("updateDeclCode {s}{*}", .{ decl_name, decl });
10941182 const required_alignment = decl.getAlignment(self.base.options.target);
10951183
1096 const decl_ptr = self.decls.getPtr(decl_index).?;
1097 if (decl_ptr.* == null) {
1098 decl_ptr.* = self.getDeclOutputSection(decl);
1099 }
1100 const sect_index = decl_ptr.*.?;
1101
1184 const decl_metadata = self.decls.get(decl_index).?;
1185 const atom_index = decl_metadata.atom;
1186 const atom = self.getAtom(atom_index);
1187 const sect_index = decl_metadata.section;
11021188 const code_len = @intCast(u32, code.len);
1103 const atom = &decl.link.coff;
1104 assert(atom.sym_index != 0); // Caller forgot to allocateDeclIndexes()
1189
11051190 if (atom.size != 0) {
11061191 const sym = atom.getSymbolPtr(self);
11071192 try self.setSymbolName(sym, decl_name);
......@@ -1111,62 +1196,51 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []const u8,
11111196 const capacity = atom.capacity(self);
11121197 const need_realloc = code.len > capacity or !mem.isAlignedGeneric(u64, sym.value, required_alignment);
11131198 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);
11151200 log.debug("growing {s} from 0x{x} to 0x{x}", .{ decl_name, sym.value, vaddr });
11161201 log.debug(" (required alignment 0x{x}", .{required_alignment});
11171202
11181203 if (vaddr != sym.value) {
11191204 sym.value = vaddr;
11201205 log.debug(" (updating GOT entry)", .{});
1121 const got_target = SymbolWithLoc{ .sym_index = atom.sym_index, .file = null };
1122 const got_atom = self.getGotAtomForSymbol(got_target).?;
1206 const got_target = SymbolWithLoc{ .sym_index = atom.getSymbolIndex().?, .file = null };
1207 const got_atom_index = self.getGotAtomIndexForSymbol(got_target).?;
11231208 self.markRelocsDirtyByTarget(got_target);
1124 try self.writePtrWidthAtom(got_atom);
1209 try self.writePtrWidthAtom(got_atom_index);
11251210 }
11261211 } else if (code_len < atom.size) {
1127 self.shrinkAtom(atom, code_len);
1212 self.shrinkAtom(atom_index, code_len);
11281213 }
1129 atom.size = code_len;
1214 self.getAtomPtr(atom_index).size = code_len;
11301215 } else {
11311216 const sym = atom.getSymbolPtr(self);
11321217 try self.setSymbolName(sym, decl_name);
11331218 sym.section_number = @intToEnum(coff.SectionNumber, sect_index + 1);
11341219 sym.type = .{ .complex_type = complex_type, .base_type = .NULL };
11351220
1136 const vaddr = try self.allocateAtom(atom, code_len, required_alignment);
1137 errdefer self.freeAtom(atom);
1221 const vaddr = try self.allocateAtom(atom_index, code_len, required_alignment);
1222 errdefer self.freeAtom(atom_index);
11381223 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;
11401225 sym.value = vaddr;
11411226
1142 const got_target = SymbolWithLoc{ .sym_index = atom.sym_index, .file = null };
1227 const got_target = SymbolWithLoc{ .sym_index = atom.getSymbolIndex().?, .file = null };
11431228 const got_index = try self.allocateGotEntry(got_target);
1144 const got_atom = try self.createGotAtom(got_target);
1145 self.got_entries.items[got_index].sym_index = got_atom.sym_index;
1146 try self.writePtrWidthAtom(got_atom);
1229 const got_atom_index = try self.createGotAtom(got_target);
1230 const got_atom = self.getAtom(got_atom_index);
1231 self.got_entries.items[got_index].sym_index = got_atom.getSymbolIndex().?;
1232 try self.writePtrWidthAtom(got_atom_index);
11471233 }
11481234
11491235 self.markRelocsDirtyByTarget(atom.getSymbolWithLoc());
1150 try self.writeAtom(atom, code);
1151}
1152
1153fn freeRelocationsForAtom(self: *Coff, atom: *Atom) void {
1154 var removed_relocs = self.relocs.fetchRemove(atom);
1155 if (removed_relocs) |*relocs| relocs.value.deinit(self.base.allocator);
1156 var removed_base_relocs = self.base_relocs.fetchRemove(atom);
1157 if (removed_base_relocs) |*base_relocs| base_relocs.value.deinit(self.base.allocator);
1236 try self.writeAtom(atom_index, code);
11581237}
11591238
11601239fn freeUnnamedConsts(self: *Coff, decl_index: Module.Decl.Index) void {
11611240 const gpa = self.base.allocator;
11621241 const unnamed_consts = self.unnamed_const_atoms.getPtr(decl_index) orelse return;
1163 for (unnamed_consts.items) |atom| {
1164 self.freeAtom(atom);
1165 self.locals_free_list.append(gpa, atom.sym_index) catch {};
1166 self.locals.items[atom.sym_index].section_number = .UNDEFINED;
1167 _ = self.atom_by_index_table.remove(atom.sym_index);
1168 log.debug(" adding local symbol index {d} to free list", .{atom.sym_index});
1169 atom.sym_index = 0;
1242 for (unnamed_consts.items) |atom_index| {
1243 self.freeAtom(atom_index);
11701244 }
11711245 unnamed_consts.clearAndFree(gpa);
11721246}
......@@ -1181,35 +1255,11 @@ pub fn freeDecl(self: *Coff, decl_index: Module.Decl.Index) void {
11811255
11821256 log.debug("freeDecl {*}", .{decl});
11831257
1184 const kv = self.decls.fetchRemove(decl_index);
1185 if (kv.?.value) |_| {
1186 self.freeAtom(&decl.link.coff);
1258 if (self.decls.fetchRemove(decl_index)) |const_kv| {
1259 var kv = const_kv;
1260 self.freeAtom(kv.value.atom);
11871261 self.freeUnnamedConsts(decl_index);
1188 }
1189
1190 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
1191 const gpa = self.base.allocator;
1192 const sym_index = decl.link.coff.sym_index;
1193 if (sym_index != 0) {
1194 self.locals_free_list.append(gpa, sym_index) catch {};
1195
1196 // Try freeing GOT atom if this decl had one
1197 const got_target = SymbolWithLoc{ .sym_index = sym_index, .file = null };
1198 if (self.got_entries_table.get(got_target)) |got_index| {
1199 self.got_entries_free_list.append(gpa, @intCast(u32, got_index)) catch {};
1200 self.got_entries.items[got_index] = .{
1201 .target = .{ .sym_index = 0, .file = null },
1202 .sym_index = 0,
1203 };
1204 _ = self.got_entries_table.remove(got_target);
1205
1206 log.debug(" adding GOT index {d} to free list (target local@{d})", .{ got_index, sym_index });
1207 }
1208
1209 self.locals.items[sym_index].section_number = .UNDEFINED;
1210 _ = self.atom_by_index_table.remove(sym_index);
1211 log.debug(" adding local symbol index {d} to free list", .{sym_index});
1212 decl.link.coff.sym_index = 0;
1262 kv.value.exports.deinit(self.base.allocator);
12131263 }
12141264}
12151265
......@@ -1262,9 +1312,10 @@ pub fn updateDeclExports(
12621312 const gpa = self.base.allocator;
12631313
12641314 const decl = module.declPtr(decl_index);
1265 const atom = &decl.link.coff;
1266 if (atom.sym_index == 0) return;
1315 const atom_index = try self.getOrCreateAtomForDecl(decl_index);
1316 const atom = self.getAtom(atom_index);
12671317 const decl_sym = atom.getSymbol(self);
1318 const decl_metadata = self.decls.getPtr(decl_index).?;
12681319
12691320 for (exports) |exp| {
12701321 log.debug("adding new export '{s}'", .{exp.options.name});
......@@ -1299,9 +1350,9 @@ pub fn updateDeclExports(
12991350 continue;
13001351 }
13011352
1302 const sym_index = exp.link.coff.sym_index orelse blk: {
1353 const sym_index = decl_metadata.getExport(self, exp.options.name) orelse blk: {
13031354 const sym_index = try self.allocateSymbol();
1304 exp.link.coff.sym_index = sym_index;
1355 try decl_metadata.exports.append(gpa, sym_index);
13051356 break :blk sym_index;
13061357 };
13071358 const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = null };
......@@ -1324,16 +1375,15 @@ pub fn updateDeclExports(
13241375 }
13251376}
13261377
1327pub fn deleteExport(self: *Coff, exp: Export) void {
1378pub fn deleteDeclExport(self: *Coff, decl_index: Module.Decl.Index, name: []const u8) void {
13281379 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;
13301382
13311383 const gpa = self.base.allocator;
1332
1333 const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = null };
1384 const sym_loc = SymbolWithLoc{ .sym_index = sym_index.*, .file = null };
13341385 const sym = self.getSymbolPtr(sym_loc);
1335 const sym_name = self.getSymbolName(sym_loc);
1336 log.debug("deleting export '{s}'", .{sym_name});
1386 log.debug("deleting export '{s}'", .{name});
13371387 assert(sym.storage_class == .EXTERNAL and sym.section_number != .UNDEFINED);
13381388 sym.* = .{
13391389 .name = [_]u8{0} ** 8,
......@@ -1343,9 +1393,9 @@ pub fn deleteExport(self: *Coff, exp: Export) void {
13431393 .storage_class = .NULL,
13441394 .number_of_aux_symbols = 0,
13451395 };
1346 self.locals_free_list.append(gpa, sym_index) catch {};
1396 self.locals_free_list.append(gpa, sym_index.*) catch {};
13471397
1348 if (self.resolver.fetchRemove(sym_name)) |entry| {
1398 if (self.resolver.fetchRemove(name)) |entry| {
13491399 defer gpa.free(entry.key);
13501400 self.globals_free_list.append(gpa, entry.value) catch {};
13511401 self.globals.items[entry.value] = .{
......@@ -1353,6 +1403,8 @@ pub fn deleteExport(self: *Coff, exp: Export) void {
13531403 .file = null,
13541404 };
13551405 }
1406
1407 sym_index.* = 0;
13561408}
13571409
13581410fn resolveGlobalSymbol(self: *Coff, current: SymbolWithLoc) !void {
......@@ -1417,9 +1469,10 @@ pub fn flushModule(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
14171469 if (self.imports_table.contains(global)) continue;
14181470
14191471 const import_index = try self.allocateImportEntry(global);
1420 const import_atom = try self.createImportAtom();
1421 self.imports.items[import_index].sym_index = import_atom.sym_index;
1422 try self.writePtrWidthAtom(import_atom);
1472 const import_atom_index = try self.createImportAtom();
1473 const import_atom = self.getAtom(import_atom_index);
1474 self.imports.items[import_index].sym_index = import_atom.getSymbolIndex().?;
1475 try self.writePtrWidthAtom(import_atom_index);
14231476 }
14241477
14251478 if (build_options.enable_logging) {
......@@ -1453,20 +1506,14 @@ pub fn flushModule(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
14531506 }
14541507}
14551508
1456pub fn getDeclVAddr(
1457 self: *Coff,
1458 decl_index: Module.Decl.Index,
1459 reloc_info: link.File.RelocInfo,
1460) !u64 {
1461 const mod = self.base.options.module.?;
1462 const decl = mod.declPtr(decl_index);
1463
1509pub fn getDeclVAddr(self: *Coff, decl_index: Module.Decl.Index, reloc_info: link.File.RelocInfo) !u64 {
14641510 assert(self.llvm_object == null);
1465 assert(decl.link.coff.sym_index != 0);
14661511
1467 const atom = self.getAtomForSymbol(.{ .sym_index = reloc_info.parent_atom_index, .file = null }).?;
1468 const target = SymbolWithLoc{ .sym_index = decl.link.coff.sym_index, .file = null };
1469 try atom.addRelocation(self, .{
1512 const this_atom_index = try self.getOrCreateAtomForDecl(decl_index);
1513 const sym_index = self.getAtom(this_atom_index).getSymbolIndex().?;
1514 const atom_index = self.getAtomIndexForSymbol(.{ .sym_index = reloc_info.parent_atom_index, .file = null }).?;
1515 const target = SymbolWithLoc{ .sym_index = sym_index, .file = null };
1516 try Atom.addRelocation(self, atom_index, .{
14701517 .type = .direct,
14711518 .target = target,
14721519 .offset = @intCast(u32, reloc_info.offset),
......@@ -1474,7 +1521,7 @@ pub fn getDeclVAddr(
14741521 .pcrel = false,
14751522 .length = 3,
14761523 });
1477 try atom.addBaseRelocation(self, @intCast(u32, reloc_info.offset));
1524 try Atom.addBaseRelocation(self, atom_index, @intCast(u32, reloc_info.offset));
14781525
14791526 return 0;
14801527}
......@@ -1501,10 +1548,10 @@ pub fn getGlobalSymbol(self: *Coff, name: []const u8) !u32 {
15011548 return global_index;
15021549}
15031550
1504pub fn updateDeclLineNumber(self: *Coff, module: *Module, decl: *Module.Decl) !void {
1551pub fn updateDeclLineNumber(self: *Coff, module: *Module, decl_index: Module.Decl.Index) !void {
15051552 _ = self;
15061553 _ = module;
1507 _ = decl;
1554 _ = decl_index;
15081555 log.debug("TODO implement updateDeclLineNumber", .{});
15091556}
15101557
......@@ -1525,7 +1572,8 @@ fn writeBaseRelocations(self: *Coff) !void {
15251572
15261573 var it = self.base_relocs.iterator();
15271574 while (it.next()) |entry| {
1528 const atom = entry.key_ptr.*;
1575 const atom_index = entry.key_ptr.*;
1576 const atom = self.getAtom(atom_index);
15291577 const offsets = entry.value_ptr.*;
15301578
15311579 for (offsets.items) |offset| {
......@@ -1609,7 +1657,8 @@ fn writeImportTable(self: *Coff) !void {
16091657 const gpa = self.base.allocator;
16101658
16111659 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);
16131662
16141663 const iat_rva = section.header.virtual_address;
16151664 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
20472096 return GetOrPutGlobalPtrResult{ .found_existing = false, .value_ptr = ptr };
20482097}
20492098
2099pub 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
2104pub 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
20502109/// Returns atom if there is an atom referenced by the symbol described by `sym_loc` descriptor.
20512110/// Returns null on failure.
2052pub fn getAtomForSymbol(self: *Coff, sym_loc: SymbolWithLoc) ?*Atom {
2111pub fn getAtomIndexForSymbol(self: *const Coff, sym_loc: SymbolWithLoc) ?Atom.Index {
20532112 assert(sym_loc.file == null); // TODO linking with object files
20542113 return self.atom_by_index_table.get(sym_loc.sym_index);
20552114}
20562115
20572116/// Returns GOT atom that references `sym_loc` if one exists.
20582117/// Returns null otherwise.
2059pub fn getGotAtomForSymbol(self: *Coff, sym_loc: SymbolWithLoc) ?*Atom {
2118pub fn getGotAtomIndexForSymbol(self: *const Coff, sym_loc: SymbolWithLoc) ?Atom.Index {
20602119 const got_index = self.got_entries_table.get(sym_loc) orelse return null;
20612120 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 });
20632122}
20642123
20652124/// Returns import atom that references `sym_loc` if one exists.
20662125/// Returns null otherwise.
2067pub fn getImportAtomForSymbol(self: *Coff, sym_loc: SymbolWithLoc) ?*Atom {
2126pub fn getImportAtomIndexForSymbol(self: *const Coff, sym_loc: SymbolWithLoc) ?Atom.Index {
20682127 const imports_index = self.imports_table.get(sym_loc) orelse return null;
20692128 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 });
20712130}
20722131
20732132fn setSectionName(self: *Coff, header: *coff.SectionHeader, name: []const u8) !void {
src/link/Coff/Atom.zig+37-22
......@@ -27,42 +27,44 @@ alignment: u32,
2727
2828/// Points to the previous and next neighbors, based on the `text_offset`.
2929/// This can be used to find, for example, the capacity of this `Atom`.
30prev: ?*Atom,
31next: ?*Atom,
32
33pub const empty = Atom{
34 .sym_index = 0,
35 .file = null,
36 .size = 0,
37 .alignment = 0,
38 .prev = null,
39 .next = null,
40};
30prev_index: ?Index,
31next_index: ?Index,
32
33pub const Index = u32;
34
35pub fn getSymbolIndex(self: Atom) ?u32 {
36 if (self.sym_index == 0) return null;
37 return self.sym_index;
38}
4139
4240/// Returns symbol referencing this atom.
4341pub fn getSymbol(self: Atom, coff_file: *const Coff) *const coff.Symbol {
42 const sym_index = self.getSymbolIndex().?;
4443 return coff_file.getSymbol(.{
45 .sym_index = self.sym_index,
44 .sym_index = sym_index,
4645 .file = self.file,
4746 });
4847}
4948
5049/// Returns pointer-to-symbol referencing this atom.
5150pub fn getSymbolPtr(self: Atom, coff_file: *Coff) *coff.Symbol {
51 const sym_index = self.getSymbolIndex().?;
5252 return coff_file.getSymbolPtr(.{
53 .sym_index = self.sym_index,
53 .sym_index = sym_index,
5454 .file = self.file,
5555 });
5656}
5757
5858pub 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 };
6061}
6162
6263/// Returns the name of this atom.
6364pub fn getName(self: Atom, coff_file: *const Coff) []const u8 {
65 const sym_index = self.getSymbolIndex().?;
6466 return coff_file.getSymbolName(.{
65 .sym_index = self.sym_index,
67 .sym_index = sym_index,
6668 .file = self.file,
6769 });
6870}
......@@ -70,7 +72,8 @@ pub fn getName(self: Atom, coff_file: *const Coff) []const u8 {
7072/// Returns how much room there is to grow in virtual address space.
7173pub fn capacity(self: Atom, coff_file: *const Coff) u32 {
7274 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);
7477 const next_sym = next.getSymbol(coff_file);
7578 return next_sym.value - self_sym.value;
7679 } else {
......@@ -82,7 +85,8 @@ pub fn capacity(self: Atom, coff_file: *const Coff) u32 {
8285
8386pub fn freeListEligible(self: Atom, coff_file: *const Coff) bool {
8487 // 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);
8690 const self_sym = self.getSymbol(coff_file);
8791 const next_sym = next.getSymbol(coff_file);
8892 const cap = next_sym.value - self_sym.value;
......@@ -92,22 +96,33 @@ pub fn freeListEligible(self: Atom, coff_file: *const Coff) bool {
9296 return surplus >= Coff.min_text_capacity;
9397}
9498
95pub fn addRelocation(self: *Atom, coff_file: *Coff, reloc: Relocation) !void {
99pub fn addRelocation(coff_file: *Coff, atom_index: Index, reloc: Relocation) !void {
96100 const gpa = coff_file.base.allocator;
97101 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);
99103 if (!gop.found_existing) {
100104 gop.value_ptr.* = .{};
101105 }
102106 try gop.value_ptr.append(gpa, reloc);
103107}
104108
105pub fn addBaseRelocation(self: *Atom, coff_file: *Coff, offset: u32) !void {
109pub fn addBaseRelocation(coff_file: *Coff, atom_index: Index, offset: u32) !void {
106110 const gpa = coff_file.base.allocator;
107 log.debug(" (adding base relocation at offset 0x{x} in %{d})", .{ offset, self.sym_index });
108 const gop = try coff_file.base_relocs.getOrPut(gpa, self);
111 log.debug(" (adding base relocation at offset 0x{x} in %{d})", .{
112 offset,
113 coff_file.getAtom(atom_index).getSymbolIndex().?,
114 });
115 const gop = try coff_file.base_relocs.getOrPut(gpa, atom_index);
109116 if (!gop.found_existing) {
110117 gop.value_ptr.* = .{};
111118 }
112119 try gop.value_ptr.append(gpa, offset);
113120}
121
122pub 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,
4646dirty: bool = true,
4747
4848/// Returns an Atom which is the target node of this relocation edge (if any).
49pub fn getTargetAtom(self: Relocation, coff_file: *Coff) ?*Atom {
49pub fn getTargetAtomIndex(self: Relocation, coff_file: *const Coff) ?Atom.Index {
5050 switch (self.type) {
5151 .got,
5252 .got_page,
5353 .got_pageoff,
54 => return coff_file.getGotAtomForSymbol(self.target),
54 => return coff_file.getGotAtomIndexForSymbol(self.target),
5555
5656 .direct,
5757 .page,
5858 .pageoff,
59 => return coff_file.getAtomForSymbol(self.target),
59 => return coff_file.getAtomIndexForSymbol(self.target),
6060
6161 .import,
6262 .import_page,
6363 .import_pageoff,
64 => return coff_file.getImportAtomForSymbol(self.target),
64 => return coff_file.getImportAtomIndexForSymbol(self.target),
6565 }
6666}
6767
68pub fn resolve(self: *Relocation, atom: *Atom, coff_file: *Coff) !void {
68pub fn resolve(self: *Relocation, atom_index: Atom.Index, coff_file: *Coff) !void {
69 const atom = coff_file.getAtom(atom_index);
6970 const source_sym = atom.getSymbol(coff_file);
7071 const source_section = coff_file.sections.get(@enumToInt(source_sym.section_number) - 1).header;
7172 const source_vaddr = source_sym.value + self.offset;
7273
7374 const file_offset = source_section.pointer_to_raw_data + source_sym.value - source_section.virtual_address;
7475
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);
7678 const target_vaddr = target_atom.getSymbol(coff_file).value;
7779 const target_vaddr_with_addend = target_vaddr + self.addend;
7880
......@@ -107,7 +109,7 @@ const Context = struct {
107109 image_base: u64,
108110};
109111
110fn resolveAarch64(self: *Relocation, ctx: Context, coff_file: *Coff) !void {
112fn resolveAarch64(self: Relocation, ctx: Context, coff_file: *Coff) !void {
111113 var buffer: [@sizeOf(u64)]u8 = undefined;
112114 switch (self.length) {
113115 2 => {
......@@ -197,7 +199,7 @@ fn resolveAarch64(self: *Relocation, ctx: Context, coff_file: *Coff) !void {
197199 }
198200}
199201
200fn resolveX86(self: *Relocation, ctx: Context, coff_file: *Coff) !void {
202fn resolveX86(self: Relocation, ctx: Context, coff_file: *Coff) !void {
201203 switch (self.type) {
202204 .got_page => unreachable,
203205 .got_pageoff => unreachable,
src/link/Dwarf.zig+320-259
......@@ -18,31 +18,36 @@ const LinkBlock = File.LinkBlock;
1818const LinkFn = File.LinkFn;
1919const LinkerLoad = @import("../codegen.zig").LinkerLoad;
2020const Module = @import("../Module.zig");
21const Value = @import("../value.zig").Value;
21const StringTable = @import("strtab.zig").StringTable;
2222const Type = @import("../type.zig").Type;
23const Value = @import("../value.zig").Value;
2324
2425allocator: Allocator,
2526bin_file: *File,
2627ptr_width: PtrWidth,
2728target: std.Target,
2829
29/// A list of `File.LinkFn` whose Line Number Programs have surplus capacity.
30/// This is the same concept as `text_block_free_list`; see those doc comments.
31dbg_line_fn_free_list: std.AutoHashMapUnmanaged(*SrcFn, void) = .{},
32dbg_line_fn_first: ?*SrcFn = null,
33dbg_line_fn_last: ?*SrcFn = null,
30/// A list of `Atom`s whose Line Number Programs have surplus capacity.
31/// This is the same concept as `Section.free_list` in Elf; see those doc comments.
32src_fn_free_list: std.AutoHashMapUnmanaged(Atom.Index, void) = .{},
33src_fn_first_index: ?Atom.Index = null,
34src_fn_last_index: ?Atom.Index = null,
35src_fns: std.ArrayListUnmanaged(Atom) = .{},
36src_fn_decls: AtomTable = .{},
3437
3538/// A list of `Atom`s whose corresponding .debug_info tags have surplus capacity.
3639/// This is the same concept as `text_block_free_list`; see those doc comments.
37atom_free_list: std.AutoHashMapUnmanaged(*Atom, void) = .{},
38atom_first: ?*Atom = null,
39atom_last: ?*Atom = null,
40di_atom_free_list: std.AutoHashMapUnmanaged(Atom.Index, void) = .{},
41di_atom_first_index: ?Atom.Index = null,
42di_atom_last_index: ?Atom.Index = null,
43di_atoms: std.ArrayListUnmanaged(Atom) = .{},
44di_atom_decls: AtomTable = .{},
4045
4146abbrev_table_offset: ?u64 = null,
4247
4348/// TODO replace with InternPool
4449/// Table of debug symbol names.
45strtab: std.ArrayListUnmanaged(u8) = .{},
50strtab: StringTable(.strtab) = .{},
4651
4752/// Quick lookup array of all defined source files referenced by at least one Decl.
4853/// They will end up in the DWARF debug_line header as two lists:
......@@ -50,22 +55,23 @@ strtab: std.ArrayListUnmanaged(u8) = .{},
5055/// * []file_names
5156di_files: std.AutoArrayHashMapUnmanaged(*const Module.File, void) = .{},
5257
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.
56managed_atoms: std.ArrayListUnmanaged(*Atom) = .{},
57
5858global_abbrev_relocs: std.ArrayListUnmanaged(AbbrevRelocation) = .{},
5959
60pub const Atom = struct {
61 /// Previous/next linked list pointers.
62 /// This is the linked list node for this Decl's corresponding .debug_info tag.
63 prev: ?*Atom,
64 next: ?*Atom,
65 /// Offset into .debug_info pointing to the tag for this Decl.
60const AtomTable = std.AutoHashMapUnmanaged(Module.Decl.Index, Atom.Index);
61
62const Atom = struct {
63 /// Offset into .debug_info pointing to the tag for this Decl, or
64 /// offset from the beginning of the Debug Line Program header that contains this function.
6665 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.
6869 len: u32,
70
71 prev_index: ?Index,
72 next_index: ?Index,
73
74 pub const Index = u32;
6975};
7076
7177/// Represents state of the analysed Decl.
......@@ -75,6 +81,7 @@ pub const Atom = struct {
7581pub const DeclState = struct {
7682 gpa: Allocator,
7783 mod: *Module,
84 di_atom_decls: *const AtomTable,
7885 dbg_line: std.ArrayList(u8),
7986 dbg_info: std.ArrayList(u8),
8087 abbrev_type_arena: std.heap.ArenaAllocator,
......@@ -88,10 +95,11 @@ pub const DeclState = struct {
8895 abbrev_relocs: std.ArrayListUnmanaged(AbbrevRelocation) = .{},
8996 exprloc_relocs: std.ArrayListUnmanaged(ExprlocRelocation) = .{},
9097
91 fn init(gpa: Allocator, mod: *Module) DeclState {
98 fn init(gpa: Allocator, mod: *Module, di_atom_decls: *const AtomTable) DeclState {
9299 return .{
93100 .gpa = gpa,
94101 .mod = mod,
102 .di_atom_decls = di_atom_decls,
95103 .dbg_line = std.ArrayList(u8).init(gpa),
96104 .dbg_info = std.ArrayList(u8).init(gpa),
97105 .abbrev_type_arena = std.heap.ArenaAllocator.init(gpa),
......@@ -119,11 +127,11 @@ pub const DeclState = struct {
119127
120128 /// Adds local type relocation of the form: @offset => @this + addend
121129 /// @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 {
123131 log.debug("{x}: @this + {x}", .{ offset, addend });
124132 try self.abbrev_relocs.append(self.gpa, .{
125133 .target = null,
126 .atom = atom,
134 .atom_index = atom_index,
127135 .offset = offset,
128136 .addend = addend,
129137 });
......@@ -132,13 +140,13 @@ pub const DeclState = struct {
132140 /// Adds global type relocation of the form: @offset => @symbol + 0
133141 /// @symbol signifies a type abbreviation posititioned somewhere in the .debug_abbrev section
134142 /// 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 {
136144 const resolv = self.abbrev_resolver.getContext(ty, .{
137145 .mod = self.mod,
138146 }) orelse blk: {
139147 const sym_index = @intCast(u32, self.abbrev_table.items.len);
140148 try self.abbrev_table.append(self.gpa, .{
141 .atom = atom,
149 .atom_index = atom_index,
142150 .type = ty,
143151 .offset = undefined,
144152 });
......@@ -153,7 +161,7 @@ pub const DeclState = struct {
153161 log.debug("{x}: %{d} + 0", .{ offset, resolv });
154162 try self.abbrev_relocs.append(self.gpa, .{
155163 .target = resolv,
156 .atom = atom,
164 .atom_index = atom_index,
157165 .offset = offset,
158166 .addend = 0,
159167 });
......@@ -162,7 +170,7 @@ pub const DeclState = struct {
162170 fn addDbgInfoType(
163171 self: *DeclState,
164172 module: *Module,
165 atom: *Atom,
173 atom_index: Atom.Index,
166174 ty: Type,
167175 ) error{OutOfMemory}!void {
168176 const arena = self.abbrev_type_arena.allocator();
......@@ -227,7 +235,7 @@ pub const DeclState = struct {
227235 // DW.AT.type, DW.FORM.ref4
228236 var index = dbg_info_buffer.items.len;
229237 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));
231239 // DW.AT.data_member_location, DW.FORM.sdata
232240 try dbg_info_buffer.ensureUnusedCapacity(6);
233241 dbg_info_buffer.appendAssumeCapacity(0);
......@@ -239,7 +247,7 @@ pub const DeclState = struct {
239247 // DW.AT.type, DW.FORM.ref4
240248 index = dbg_info_buffer.items.len;
241249 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));
243251 // DW.AT.data_member_location, DW.FORM.sdata
244252 const offset = abi_size - payload_ty.abiSize(target);
245253 try leb128.writeULEB128(dbg_info_buffer.writer(), offset);
......@@ -270,7 +278,7 @@ pub const DeclState = struct {
270278 try dbg_info_buffer.resize(index + 4);
271279 var buf = try arena.create(Type.SlicePtrFieldTypeBuffer);
272280 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));
274282 // DW.AT.data_member_location, DW.FORM.sdata
275283 try dbg_info_buffer.ensureUnusedCapacity(6);
276284 dbg_info_buffer.appendAssumeCapacity(0);
......@@ -282,7 +290,7 @@ pub const DeclState = struct {
282290 // DW.AT.type, DW.FORM.ref4
283291 index = dbg_info_buffer.items.len;
284292 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));
286294 // DW.AT.data_member_location, DW.FORM.sdata
287295 try dbg_info_buffer.ensureUnusedCapacity(2);
288296 dbg_info_buffer.appendAssumeCapacity(ptr_bytes);
......@@ -294,7 +302,7 @@ pub const DeclState = struct {
294302 // DW.AT.type, DW.FORM.ref4
295303 const index = dbg_info_buffer.items.len;
296304 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));
298306 }
299307 },
300308 .Array => {
......@@ -305,13 +313,13 @@ pub const DeclState = struct {
305313 // DW.AT.type, DW.FORM.ref4
306314 var index = dbg_info_buffer.items.len;
307315 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));
309317 // DW.AT.subrange_type
310318 try dbg_info_buffer.append(@enumToInt(AbbrevKind.array_dim));
311319 // DW.AT.type, DW.FORM.ref4
312320 index = dbg_info_buffer.items.len;
313321 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));
315323 // DW.AT.count, DW.FORM.udata
316324 const len = ty.arrayLenIncludingSentinel();
317325 try leb128.writeULEB128(dbg_info_buffer.writer(), len);
......@@ -339,7 +347,7 @@ pub const DeclState = struct {
339347 // DW.AT.type, DW.FORM.ref4
340348 var index = dbg_info_buffer.items.len;
341349 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));
343351 // DW.AT.data_member_location, DW.FORM.sdata
344352 const field_off = ty.structFieldOffset(field_index, target);
345353 try leb128.writeULEB128(dbg_info_buffer.writer(), field_off);
......@@ -371,7 +379,7 @@ pub const DeclState = struct {
371379 // DW.AT.type, DW.FORM.ref4
372380 var index = dbg_info_buffer.items.len;
373381 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));
375383 // DW.AT.data_member_location, DW.FORM.sdata
376384 const field_off = ty.structFieldOffset(field_index, target);
377385 try leb128.writeULEB128(dbg_info_buffer.writer(), field_off);
......@@ -454,7 +462,7 @@ pub const DeclState = struct {
454462 // DW.AT.type, DW.FORM.ref4
455463 const inner_union_index = dbg_info_buffer.items.len;
456464 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);
458466 // DW.AT.data_member_location, DW.FORM.sdata
459467 try leb128.writeULEB128(dbg_info_buffer.writer(), payload_offset);
460468 }
......@@ -481,7 +489,7 @@ pub const DeclState = struct {
481489 // DW.AT.type, DW.FORM.ref4
482490 const index = dbg_info_buffer.items.len;
483491 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));
485493 // DW.AT.data_member_location, DW.FORM.sdata
486494 try dbg_info_buffer.append(0);
487495 }
......@@ -498,7 +506,7 @@ pub const DeclState = struct {
498506 // DW.AT.type, DW.FORM.ref4
499507 const index = dbg_info_buffer.items.len;
500508 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));
502510 // DW.AT.data_member_location, DW.FORM.sdata
503511 try leb128.writeULEB128(dbg_info_buffer.writer(), tag_offset);
504512
......@@ -541,7 +549,7 @@ pub const DeclState = struct {
541549 // DW.AT.type, DW.FORM.ref4
542550 var index = dbg_info_buffer.items.len;
543551 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));
545553 // DW.AT.data_member_location, DW.FORM.sdata
546554 try leb128.writeULEB128(dbg_info_buffer.writer(), payload_off);
547555
......@@ -554,7 +562,7 @@ pub const DeclState = struct {
554562 // DW.AT.type, DW.FORM.ref4
555563 index = dbg_info_buffer.items.len;
556564 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));
558566 // DW.AT.data_member_location, DW.FORM.sdata
559567 try leb128.writeULEB128(dbg_info_buffer.writer(), error_off);
560568
......@@ -587,12 +595,11 @@ pub const DeclState = struct {
587595 self: *DeclState,
588596 name: [:0]const u8,
589597 ty: Type,
590 tag: File.Tag,
591598 owner_decl: Module.Decl.Index,
592599 loc: DbgInfoLoc,
593600 ) error{OutOfMemory}!void {
594601 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).?;
596603 const name_with_null = name.ptr[0 .. name.len + 1];
597604
598605 switch (loc) {
......@@ -637,7 +644,7 @@ pub const DeclState = struct {
637644 try dbg_info.ensureUnusedCapacity(5 + name_with_null.len);
638645 const index = dbg_info.items.len;
639646 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
641648 dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string
642649 }
643650
......@@ -645,13 +652,12 @@ pub const DeclState = struct {
645652 self: *DeclState,
646653 name: [:0]const u8,
647654 ty: Type,
648 tag: File.Tag,
649655 owner_decl: Module.Decl.Index,
650656 is_ptr: bool,
651657 loc: DbgInfoLoc,
652658 ) error{OutOfMemory}!void {
653659 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).?;
655661 const name_with_null = name.ptr[0 .. name.len + 1];
656662 try dbg_info.append(@enumToInt(AbbrevKind.variable));
657663 const target = self.mod.getTarget();
......@@ -781,7 +787,7 @@ pub const DeclState = struct {
781787 try dbg_info.ensureUnusedCapacity(5 + name_with_null.len);
782788 const index = dbg_info.items.len;
783789 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));
785791 dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string
786792 }
787793
......@@ -814,7 +820,7 @@ pub const DeclState = struct {
814820};
815821
816822pub const AbbrevEntry = struct {
817 atom: *const Atom,
823 atom_index: Atom.Index,
818824 type: Type,
819825 offset: u32,
820826};
......@@ -823,7 +829,7 @@ pub const AbbrevRelocation = struct {
823829 /// If target is null, we deal with a local relocation that is based on simple offset + addend
824830 /// only.
825831 target: ?u32,
826 atom: *const Atom,
832 atom_index: Atom.Index,
827833 offset: u32,
828834 addend: u32,
829835};
......@@ -840,26 +846,6 @@ pub const ExprlocRelocation = struct {
840846 offset: u32,
841847};
842848
843pub 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
863849pub const PtrWidth = enum { p32, p64 };
864850
865851pub const AbbrevKind = enum(u8) {
......@@ -909,16 +895,18 @@ pub fn init(allocator: Allocator, bin_file: *File, target: std.Target) Dwarf {
909895
910896pub fn deinit(self: *Dwarf) void {
911897 const gpa = self.allocator;
912 self.dbg_line_fn_free_list.deinit(gpa);
913 self.atom_free_list.deinit(gpa);
898
899 self.src_fn_free_list.deinit(gpa);
900 self.src_fns.deinit(gpa);
901 self.src_fn_decls.deinit(gpa);
902
903 self.di_atom_free_list.deinit(gpa);
904 self.di_atoms.deinit(gpa);
905 self.di_atom_decls.deinit(gpa);
906
914907 self.strtab.deinit(gpa);
915908 self.di_files.deinit(gpa);
916909 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);
922910}
923911
924912/// 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)
934922 log.debug("initDeclState {s}{*}", .{ decl_name, decl });
935923
936924 const gpa = self.allocator;
937 var decl_state = DeclState.init(gpa, mod);
925 var decl_state = DeclState.init(gpa, mod, &self.di_atom_decls);
938926 errdefer decl_state.deinit();
939927 const dbg_line_buffer = &decl_state.dbg_line;
940928 const dbg_info_buffer = &decl_state.dbg_info;
941929
930 const di_atom_index = try self.getOrCreateAtomForDecl(.di_atom, decl_index);
931
942932 assert(decl.has_tv);
943933
944934 switch (decl.ty.zigTypeTag()) {
945935 .Fn => {
936 _ = try self.getOrCreateAtomForDecl(.src_fn, decl_index);
937
946938 // For functions we need to add a prologue to the debug line program.
947939 try dbg_line_buffer.ensureTotalCapacity(26);
948940
......@@ -1002,8 +994,7 @@ pub fn initDeclState(self: *Dwarf, mod: *Module, decl_index: Module.Decl.Index)
1002994 dbg_info_buffer.items.len += 4; // DW.AT.high_pc, DW.FORM.data4
1003995 //
1004996 if (fn_ret_has_bits) {
1005 const atom = getDbgInfoAtom(self.bin_file.tag, mod, decl_index);
1006 try decl_state.addTypeRelocGlobal(atom, fn_ret_type, @intCast(u32, dbg_info_buffer.items.len));
997 try decl_state.addTypeRelocGlobal(di_atom_index, fn_ret_type, @intCast(u32, dbg_info_buffer.items.len));
1007998 dbg_info_buffer.items.len += 4; // DW.AT.type, DW.FORM.ref4
1008999 }
10091000
......@@ -1075,31 +1066,28 @@ pub fn commitDeclState(
10751066 // This logic is nearly identical to the logic below in `updateDeclDebugInfo` for
10761067 // `TextBlock` and the .debug_info. If you are editing this logic, you
10771068 // probably need to edit that logic too.
1078 const src_fn = switch (self.bin_file.tag) {
1079 .elf => &decl.fn_link.elf,
1080 .macho => &decl.fn_link.macho,
1081 .wasm => &decl.fn_link.wasm.src_fn,
1082 else => unreachable, // TODO
1083 };
1069 const src_fn_index = self.src_fn_decls.get(decl_index).?;
1070 const src_fn = self.getAtomPtr(.src_fn, src_fn_index);
10841071 src_fn.len = @intCast(u32, dbg_line_buffer.items.len);
10851072
1086 if (self.dbg_line_fn_last) |last| blk: {
1087 if (src_fn == last) break :blk;
1088 if (src_fn.next) |next| {
1073 if (self.src_fn_last_index) |last_index| blk: {
1074 if (src_fn_index == last_index) break :blk;
1075 if (src_fn.next_index) |next_index| {
1076 const next = self.getAtomPtr(.src_fn, next_index);
10891077 // Update existing function - non-last item.
10901078 if (src_fn.off + src_fn.len + min_nop_size > next.off) {
10911079 // It grew too big, so we move it to a new location.
1092 if (src_fn.prev) |prev| {
1093 self.dbg_line_fn_free_list.put(gpa, prev, {}) catch {};
1094 prev.next = src_fn.next;
1080 if (src_fn.prev_index) |prev_index| {
1081 self.src_fn_free_list.put(gpa, prev_index, {}) catch {};
1082 self.getAtomPtr(.src_fn, prev_index).next_index = src_fn.next_index;
10951083 }
1096 next.prev = src_fn.prev;
1097 src_fn.next = null;
1084 next.prev_index = src_fn.prev_index;
1085 src_fn.next_index = null;
10981086 // Populate where it used to be with NOPs.
10991087 switch (self.bin_file.tag) {
11001088 .elf => {
11011089 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.?];
11031091 const file_pos = debug_line_sect.sh_offset + src_fn.off;
11041092 try pwriteDbgLineNops(elf_file.base.file.?, file_pos, 0, &[0]u8{}, src_fn.len);
11051093 },
......@@ -1111,39 +1099,48 @@ pub fn commitDeclState(
11111099 },
11121100 .wasm => {
11131101 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;
11151103 writeDbgLineNopsBuffered(debug_line.items, src_fn.off, 0, &.{}, src_fn.len);
11161104 },
11171105 else => unreachable,
11181106 }
11191107 // TODO Look at the free list before appending at the end.
1120 src_fn.prev = last;
1121 last.next = src_fn;
1122 self.dbg_line_fn_last = src_fn;
1108 src_fn.prev_index = last_index;
1109 const last = self.getAtomPtr(.src_fn, last_index);
1110 last.next_index = src_fn_index;
1111 self.src_fn_last_index = src_fn_index;
11231112
11241113 src_fn.off = last.off + padToIdeal(last.len);
11251114 }
1126 } else if (src_fn.prev == null) {
1115 } else if (src_fn.prev_index == null) {
11271116 // Append new function.
11281117 // TODO Look at the free list before appending at the end.
1129 src_fn.prev = last;
1130 last.next = src_fn;
1131 self.dbg_line_fn_last = src_fn;
1118 src_fn.prev_index = last_index;
1119 const last = self.getAtomPtr(.src_fn, last_index);
1120 last.next_index = src_fn_index;
1121 self.src_fn_last_index = src_fn_index;
11321122
11331123 src_fn.off = last.off + padToIdeal(last.len);
11341124 }
11351125 } else {
11361126 // This is the first function of the Line Number Program.
1137 self.dbg_line_fn_first = src_fn;
1138 self.dbg_line_fn_last = src_fn;
1127 self.src_fn_first_index = src_fn_index;
1128 self.src_fn_last_index = src_fn_index;
11391129
11401130 src_fn.off = padToIdeal(self.dbgLineNeededHeaderBytes(&[0][]u8{}, &[0][]u8{}));
11411131 }
11421132
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);
11441135 const needed_size = last_src_fn.off + last_src_fn.len;
1145 const prev_padding_size: u32 = if (src_fn.prev) |prev| src_fn.off - (prev.off + prev.len) else 0;
1146 const next_padding_size: u32 = if (src_fn.next) |next| next.off - (src_fn.off + src_fn.len) else 0;
1136 const prev_padding_size: u32 = if (src_fn.prev_index) |prev_index| blk: {
1137 const prev = self.getAtom(.src_fn, prev_index);
1138 break :blk src_fn.off - (prev.off + prev.len);
1139 } else 0;
1140 const next_padding_size: u32 = if (src_fn.next_index) |next_index| blk: {
1141 const next = self.getAtom(.src_fn, next_index);
1142 break :blk next.off - (src_fn.off + src_fn.len);
1143 } else 0;
11471144
11481145 // We only have support for one compilation unit so far, so the offsets are directly
11491146 // from the .debug_line section.
......@@ -1152,7 +1149,7 @@ pub fn commitDeclState(
11521149 const elf_file = self.bin_file.cast(File.Elf).?;
11531150 const shdr_index = elf_file.debug_line_section_index.?;
11541151 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];
11561153 const file_pos = debug_line_sect.sh_offset + src_fn.off;
11571154 try pwriteDbgLineNops(
11581155 elf_file.base.file.?,
......@@ -1180,7 +1177,7 @@ pub fn commitDeclState(
11801177
11811178 .wasm => {
11821179 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.?);
11841181 const debug_line = &atom.code;
11851182 const segment_size = debug_line.items.len;
11861183 if (needed_size != segment_size) {
......@@ -1212,7 +1209,7 @@ pub fn commitDeclState(
12121209 if (dbg_info_buffer.items.len == 0)
12131210 return;
12141211
1215 const atom = getDbgInfoAtom(self.bin_file.tag, module, decl_index);
1212 const di_atom_index = self.di_atom_decls.get(decl_index).?;
12161213 if (decl_state.abbrev_table.items.len > 0) {
12171214 // Now we emit the .debug_info types of the Decl. These will count towards the size of
12181215 // 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(
12341231 if (deferred) continue;
12351232
12361233 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);
12381235 }
12391236 }
12401237
12411238 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));
12431240
12441241 while (decl_state.abbrev_relocs.popOrNull()) |reloc| {
12451242 if (reloc.target) |target| {
......@@ -1260,11 +1257,12 @@ pub fn commitDeclState(
12601257 try self.global_abbrev_relocs.append(gpa, .{
12611258 .target = null,
12621259 .offset = reloc.offset,
1263 .atom = reloc.atom,
1260 .atom_index = reloc.atom_index,
12641261 .addend = reloc.addend,
12651262 });
12661263 } 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;
12681266 log.debug("{x}: [() => {x}] (%{d}, '{}')", .{ reloc.offset, value, target, ty.fmtDebug() });
12691267 mem.writeInt(
12701268 u32,
......@@ -1274,10 +1272,11 @@ pub fn commitDeclState(
12741272 );
12751273 }
12761274 } else {
1275 const atom = self.getAtom(.di_atom, reloc.atom_index);
12771276 mem.writeInt(
12781277 u32,
12791278 dbg_info_buffer.items[reloc.offset..][0..@sizeOf(u32)],
1280 reloc.atom.off + reloc.offset + reloc.addend,
1279 atom.off + reloc.offset + reloc.addend,
12811280 target_endian,
12821281 );
12831282 }
......@@ -1293,7 +1292,7 @@ pub fn commitDeclState(
12931292 .got_load => .got_load,
12941293 },
12951294 .target = reloc.target,
1296 .offset = reloc.offset + atom.off,
1295 .offset = reloc.offset + self.getAtom(.di_atom, di_atom_index).off,
12971296 .addend = 0,
12981297 .prev_vaddr = 0,
12991298 });
......@@ -1303,10 +1302,10 @@ pub fn commitDeclState(
13031302 }
13041303
13051304 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);
13071306}
13081307
1309fn updateDeclDebugInfoAllocation(self: *Dwarf, atom: *Atom, len: u32) !void {
1308fn updateDeclDebugInfoAllocation(self: *Dwarf, atom_index: Atom.Index, len: u32) !void {
13101309 const tracy = trace(@src());
13111310 defer tracy.end();
13121311
......@@ -1315,24 +1314,26 @@ fn updateDeclDebugInfoAllocation(self: *Dwarf, atom: *Atom, len: u32) !void {
13151314 // probably need to edit that logic too.
13161315 const gpa = self.allocator;
13171316
1317 const atom = self.getAtomPtr(.di_atom, atom_index);
13181318 atom.len = len;
1319 if (self.atom_last) |last| blk: {
1320 if (atom == last) break :blk;
1321 if (atom.next) |next| {
1319 if (self.di_atom_last_index) |last_index| blk: {
1320 if (atom_index == last_index) break :blk;
1321 if (atom.next_index) |next_index| {
1322 const next = self.getAtomPtr(.di_atom, next_index);
13221323 // Update existing Decl - non-last item.
13231324 if (atom.off + atom.len + min_nop_size > next.off) {
13241325 // It grew too big, so we move it to a new location.
1325 if (atom.prev) |prev| {
1326 self.atom_free_list.put(gpa, prev, {}) catch {};
1327 prev.next = atom.next;
1326 if (atom.prev_index) |prev_index| {
1327 self.di_atom_free_list.put(gpa, prev_index, {}) catch {};
1328 self.getAtomPtr(.di_atom, prev_index).next_index = atom.next_index;
13281329 }
1329 next.prev = atom.prev;
1330 atom.next = null;
1330 next.prev_index = atom.prev_index;
1331 atom.next_index = null;
13311332 // Populate where it used to be with NOPs.
13321333 switch (self.bin_file.tag) {
13331334 .elf => {
13341335 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.?];
13361337 const file_pos = debug_info_sect.sh_offset + atom.off;
13371338 try pwriteDbgInfoNops(elf_file.base.file.?, file_pos, 0, &[0]u8{}, atom.len, false);
13381339 },
......@@ -1344,37 +1345,40 @@ fn updateDeclDebugInfoAllocation(self: *Dwarf, atom: *Atom, len: u32) !void {
13441345 },
13451346 .wasm => {
13461347 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;
13481350 try writeDbgInfoNopsToArrayList(gpa, debug_info, atom.off, 0, &.{0}, atom.len, false);
13491351 },
13501352 else => unreachable,
13511353 }
13521354 // TODO Look at the free list before appending at the end.
1353 atom.prev = last;
1354 last.next = atom;
1355 self.atom_last = atom;
1355 atom.prev_index = last_index;
1356 const last = self.getAtomPtr(.di_atom, last_index);
1357 last.next_index = atom_index;
1358 self.di_atom_last_index = atom_index;
13561359
13571360 atom.off = last.off + padToIdeal(last.len);
13581361 }
1359 } else if (atom.prev == null) {
1362 } else if (atom.prev_index == null) {
13601363 // Append new Decl.
13611364 // TODO Look at the free list before appending at the end.
1362 atom.prev = last;
1363 last.next = atom;
1364 self.atom_last = atom;
1365 atom.prev_index = last_index;
1366 const last = self.getAtomPtr(.di_atom, last_index);
1367 last.next_index = atom_index;
1368 self.di_atom_last_index = atom_index;
13651369
13661370 atom.off = last.off + padToIdeal(last.len);
13671371 }
13681372 } else {
13691373 // This is the first Decl of the .debug_info
1370 self.atom_first = atom;
1371 self.atom_last = atom;
1374 self.di_atom_first_index = atom_index;
1375 self.di_atom_last_index = atom_index;
13721376
13731377 atom.off = @intCast(u32, padToIdeal(self.dbgInfoHeaderBytes()));
13741378 }
13751379}
13761380
1377fn writeDeclDebugInfo(self: *Dwarf, atom: *Atom, dbg_info_buf: []const u8) !void {
1381fn writeDeclDebugInfo(self: *Dwarf, atom_index: Atom.Index, dbg_info_buf: []const u8) !void {
13781382 const tracy = trace(@src());
13791383 defer tracy.end();
13801384
......@@ -1383,14 +1387,22 @@ fn writeDeclDebugInfo(self: *Dwarf, atom: *Atom, dbg_info_buf: []const u8) !void
13831387 // probably need to edit that logic too.
13841388 const gpa = self.allocator;
13851389
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);
13871393 // +1 for a trailing zero to end the children of the decl tag.
13881394 const needed_size = last_decl.off + last_decl.len + 1;
1389 const prev_padding_size: u32 = if (atom.prev) |prev| atom.off - (prev.off + prev.len) else 0;
1390 const next_padding_size: u32 = if (atom.next) |next| next.off - (atom.off + atom.len) else 0;
1395 const prev_padding_size: u32 = if (atom.prev_index) |prev_index| blk: {
1396 const prev = self.getAtom(.di_atom, prev_index);
1397 break :blk atom.off - (prev.off + prev.len);
1398 } else 0;
1399 const next_padding_size: u32 = if (atom.next_index) |next_index| blk: {
1400 const next = self.getAtom(.di_atom, next_index);
1401 break :blk next.off - (atom.off + atom.len);
1402 } else 0;
13911403
13921404 // To end the children of the decl tag.
1393 const trailing_zero = atom.next == null;
1405 const trailing_zero = atom.next_index == null;
13941406
13951407 // We only have support for one compilation unit so far, so the offsets are directly
13961408 // from the .debug_info section.
......@@ -1399,7 +1411,7 @@ fn writeDeclDebugInfo(self: *Dwarf, atom: *Atom, dbg_info_buf: []const u8) !void
13991411 const elf_file = self.bin_file.cast(File.Elf).?;
14001412 const shdr_index = elf_file.debug_info_section_index.?;
14011413 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];
14031415 const file_pos = debug_info_sect.sh_offset + atom.off;
14041416 try pwriteDbgInfoNops(
14051417 elf_file.base.file.?,
......@@ -1430,7 +1442,7 @@ fn writeDeclDebugInfo(self: *Dwarf, atom: *Atom, dbg_info_buf: []const u8) !void
14301442 .wasm => {
14311443 const wasm_file = self.bin_file.cast(File.Wasm).?;
14321444 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;
14341446 const segment_size = debug_info.items.len;
14351447 if (needed_size != segment_size) {
14361448 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
14581470 }
14591471}
14601472
1461pub fn updateDeclLineNumber(self: *Dwarf, decl: *const Module.Decl) !void {
1473pub fn updateDeclLineNumber(self: *Dwarf, module: *Module, decl_index: Module.Decl.Index) !void {
14621474 const tracy = trace(@src());
14631475 defer tracy.end();
14641476
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);
14651482 const func = decl.val.castTag(.function).?.data;
14661483 log.debug("decl.src_line={d}, func.lbrace_line={d}, func.rbrace_line={d}", .{
14671484 decl.src_line,
......@@ -1475,79 +1492,81 @@ pub fn updateDeclLineNumber(self: *Dwarf, decl: *const Module.Decl) !void {
14751492 switch (self.bin_file.tag) {
14761493 .elf => {
14771494 const elf_file = self.bin_file.cast(File.Elf).?;
1478 const shdr = elf_file.sections.items[elf_file.debug_line_section_index.?];
1479 const file_pos = shdr.sh_offset + decl.fn_link.elf.off + self.getRelocDbgLineOff();
1495 const shdr = elf_file.sections.items(.shdr)[elf_file.debug_line_section_index.?];
1496 const file_pos = shdr.sh_offset + atom.off + self.getRelocDbgLineOff();
14801497 try elf_file.base.file.?.pwriteAll(&data, file_pos);
14811498 },
14821499 .macho => {
14831500 const d_sym = self.bin_file.cast(File.MachO).?.getDebugSymbols().?;
14841501 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();
14861503 try d_sym.file.pwriteAll(&data, file_pos);
14871504 },
14881505 .wasm => {
14891506 const wasm_file = self.bin_file.cast(File.Wasm).?;
1490 const offset = decl.fn_link.wasm.src_fn.off + self.getRelocDbgLineOff();
1491 const atom = wasm_file.debug_line_atom.?;
1492 mem.copy(u8, atom.code.items[offset..], &data);
1507 const offset = atom.off + self.getRelocDbgLineOff();
1508 const line_atom_index = wasm_file.debug_line_atom.?;
1509 mem.copy(u8, wasm_file.getAtomPtr(line_atom_index).code.items[offset..], &data);
14931510 },
14941511 else => unreachable,
14951512 }
14961513}
14971514
1498pub fn freeAtom(self: *Dwarf, atom: *Atom) void {
1499 if (self.atom_first == atom) {
1500 self.atom_first = atom.next;
1501 }
1502 if (self.atom_last == atom) {
1503 // TODO shrink the .debug_info section size here
1504 self.atom_last = atom.prev;
1505 }
1506
1507 if (atom.prev) |prev| {
1508 prev.next = atom.next;
1515pub fn freeDecl(self: *Dwarf, decl_index: Module.Decl.Index) void {
1516 const gpa = self.allocator;
15091517
1510 // TODO the free list logic like we do for text blocks above
1511 } else {
1512 atom.prev = null;
1518 // Free SrcFn atom
1519 if (self.src_fn_decls.fetchRemove(decl_index)) |kv| {
1520 const src_fn_index = kv.value;
1521 const src_fn = self.getAtom(.src_fn, src_fn_index);
1522 _ = self.src_fn_free_list.remove(src_fn_index);
1523
1524 if (src_fn.prev_index) |prev_index| {
1525 self.src_fn_free_list.put(gpa, prev_index, {}) catch {};
1526 const prev = self.getAtomPtr(.src_fn, prev_index);
1527 prev.next_index = src_fn.next_index;
1528 if (src_fn.next_index) |next_index| {
1529 self.getAtomPtr(.src_fn, next_index).prev_index = prev_index;
1530 } else {
1531 self.src_fn_last_index = prev_index;
1532 }
1533 } else if (src_fn.next_index) |next_index| {
1534 self.src_fn_first_index = next_index;
1535 self.getAtomPtr(.src_fn, next_index).prev_index = null;
1536 }
1537 if (self.src_fn_first_index == src_fn_index) {
1538 self.src_fn_first_index = src_fn.next_index;
1539 }
1540 if (self.src_fn_last_index == src_fn_index) {
1541 self.src_fn_last_index = src_fn.prev_index;
1542 }
15131543 }
15141544
1515 if (atom.next) |next| {
1516 next.prev = atom.prev;
1517 } else {
1518 atom.next = null;
1519 }
1520}
1545 // Free DI atom
1546 if (self.di_atom_decls.fetchRemove(decl_index)) |kv| {
1547 const di_atom_index = kv.value;
1548 const di_atom = self.getAtomPtr(.di_atom, di_atom_index);
15211549
1522pub fn freeDecl(self: *Dwarf, decl: *Module.Decl) void {
1523 // TODO make this logic match freeTextBlock. Maybe abstract the logic out since the same thing
1524 // is desired for both.
1525 const gpa = self.allocator;
1526 const fn_link = switch (self.bin_file.tag) {
1527 .elf => &decl.fn_link.elf,
1528 .macho => &decl.fn_link.macho,
1529 .wasm => &decl.fn_link.wasm.src_fn,
1530 else => unreachable,
1531 };
1532 _ = self.dbg_line_fn_free_list.remove(fn_link);
1550 if (self.di_atom_first_index == di_atom_index) {
1551 self.di_atom_first_index = di_atom.next_index;
1552 }
1553 if (self.di_atom_last_index == di_atom_index) {
1554 // TODO shrink the .debug_info section size here
1555 self.di_atom_last_index = di_atom.prev_index;
1556 }
15331557
1534 if (fn_link.prev) |prev| {
1535 self.dbg_line_fn_free_list.put(gpa, prev, {}) catch {};
1536 prev.next = fn_link.next;
1537 if (fn_link.next) |next| {
1538 next.prev = prev;
1558 if (di_atom.prev_index) |prev_index| {
1559 self.getAtomPtr(.di_atom, prev_index).next_index = di_atom.next_index;
1560 // TODO the free list logic like we do for SrcFn above
15391561 } 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;
15411569 }
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;
15511570 }
15521571}
15531572
......@@ -1690,7 +1709,7 @@ pub fn writeDbgAbbrev(self: *Dwarf) !void {
16901709 const elf_file = self.bin_file.cast(File.Elf).?;
16911710 const shdr_index = elf_file.debug_abbrev_section_index.?;
16921711 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];
16941713 const file_pos = debug_abbrev_sect.sh_offset + abbrev_offset;
16951714 try elf_file.base.file.?.pwriteAll(&abbrev_buf, file_pos);
16961715 },
......@@ -1704,7 +1723,7 @@ pub fn writeDbgAbbrev(self: *Dwarf) !void {
17041723 },
17051724 .wasm => {
17061725 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;
17081727 try debug_abbrev.resize(wasm_file.base.allocator, needed_size);
17091728 mem.copy(u8, debug_abbrev.items, &abbrev_buf);
17101729 },
......@@ -1770,11 +1789,11 @@ pub fn writeDbgInfoHeader(self: *Dwarf, module: *Module, low_pc: u64, high_pc: u
17701789 },
17711790 }
17721791 // 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);
17741793 var compile_unit_dir_buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;
17751794 const compile_unit_dir = resolveCompilationDir(module, &compile_unit_dir_buffer);
1776 const comp_dir_strp = try self.makeString(compile_unit_dir);
1777 const producer_strp = try self.makeString(link.producer_string);
1795 const comp_dir_strp = try self.strtab.insert(self.allocator, compile_unit_dir);
1796 const producer_strp = try self.strtab.insert(self.allocator, link.producer_string);
17781797
17791798 di_buf.appendAssumeCapacity(@enumToInt(AbbrevKind.compile_unit));
17801799 if (self.bin_file.tag == .macho) {
......@@ -1805,7 +1824,7 @@ pub fn writeDbgInfoHeader(self: *Dwarf, module: *Module, low_pc: u64, high_pc: u
18051824 switch (self.bin_file.tag) {
18061825 .elf => {
18071826 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.?];
18091828 const file_pos = debug_info_sect.sh_offset;
18101829 try pwriteDbgInfoNops(elf_file.base.file.?, file_pos, 0, di_buf.items, jmp_amt, false);
18111830 },
......@@ -1817,7 +1836,7 @@ pub fn writeDbgInfoHeader(self: *Dwarf, module: *Module, low_pc: u64, high_pc: u
18171836 },
18181837 .wasm => {
18191838 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;
18211840 try writeDbgInfoNopsToArrayList(self.allocator, debug_info, 0, 0, di_buf.items, jmp_amt, false);
18221841 },
18231842 else => unreachable,
......@@ -2124,7 +2143,7 @@ pub fn writeDbgAranges(self: *Dwarf, addr: u64, size: u64) !void {
21242143 const elf_file = self.bin_file.cast(File.Elf).?;
21252144 const shdr_index = elf_file.debug_aranges_section_index.?;
21262145 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];
21282147 const file_pos = debug_aranges_sect.sh_offset;
21292148 try elf_file.base.file.?.pwriteAll(di_buf.items, file_pos);
21302149 },
......@@ -2138,7 +2157,7 @@ pub fn writeDbgAranges(self: *Dwarf, addr: u64, size: u64) !void {
21382157 },
21392158 .wasm => {
21402159 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;
21422161 try debug_ranges.resize(wasm_file.base.allocator, needed_size);
21432162 mem.copy(u8, debug_ranges.items, di_buf.items);
21442163 },
......@@ -2275,19 +2294,23 @@ pub fn writeDbgLineHeader(self: *Dwarf) !void {
22752294 const needed_with_padding = padToIdeal(needed_bytes);
22762295 const delta = needed_with_padding - dbg_line_prg_off;
22772296
2278 var src_fn = self.dbg_line_fn_first.?;
2279 const last_fn = self.dbg_line_fn_last.?;
2297 const first_fn_index = self.src_fn_first_index.?;
2298 const first_fn = self.getAtom(.src_fn, first_fn_index);
2299 const last_fn_index = self.src_fn_last_index.?;
2300 const last_fn = self.getAtom(.src_fn, last_fn_index);
2301
2302 var src_fn_index = first_fn_index;
22802303
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);
22822305 defer gpa.free(buffer);
22832306
22842307 switch (self.bin_file.tag) {
22852308 .elf => {
22862309 const elf_file = self.bin_file.cast(File.Elf).?;
22872310 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;
22892312 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;
22912314
22922315 const amt = try elf_file.base.file.?.preadAll(buffer, file_pos);
22932316 if (amt != buffer.len) return error.InputOutput;
......@@ -2299,7 +2322,7 @@ pub fn writeDbgLineHeader(self: *Dwarf) !void {
22992322 const sect_index = d_sym.debug_line_section_index.?;
23002323 const needed_size = @intCast(u32, d_sym.getSection(sect_index).size + delta);
23012324 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;
23032326
23042327 const amt = try d_sym.file.preadAll(buffer, file_pos);
23052328 if (amt != buffer.len) return error.InputOutput;
......@@ -2308,19 +2331,20 @@ pub fn writeDbgLineHeader(self: *Dwarf) !void {
23082331 },
23092332 .wasm => {
23102333 const wasm_file = self.bin_file.cast(File.Wasm).?;
2311 const debug_line = &wasm_file.debug_line_atom.?.code;
2312 mem.copy(u8, buffer, debug_line.items[src_fn.off..]);
2334 const debug_line = &wasm_file.getAtomPtr(wasm_file.debug_line_atom.?).code;
2335 mem.copy(u8, buffer, debug_line.items[first_fn.off..]);
23132336 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);
23152338 },
23162339 else => unreachable,
23172340 }
23182341
23192342 while (true) {
2343 const src_fn = self.getAtomPtr(.src_fn, src_fn_index);
23202344 src_fn.off += delta;
23212345
2322 if (src_fn.next) |next| {
2323 src_fn = next;
2346 if (src_fn.next_index) |next_index| {
2347 src_fn_index = next_index;
23242348 } else break;
23252349 }
23262350 }
......@@ -2346,7 +2370,7 @@ pub fn writeDbgLineHeader(self: *Dwarf) !void {
23462370 switch (self.bin_file.tag) {
23472371 .elf => {
23482372 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.?];
23502374 const file_pos = debug_line_sect.sh_offset;
23512375 try pwriteDbgLineNops(elf_file.base.file.?, file_pos, 0, di_buf.items, jmp_amt);
23522376 },
......@@ -2358,7 +2382,7 @@ pub fn writeDbgLineHeader(self: *Dwarf) !void {
23582382 },
23592383 .wasm => {
23602384 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;
23622386 writeDbgLineNopsBuffered(debug_line.items, 0, 0, di_buf.items, jmp_amt);
23632387 },
23642388 else => unreachable,
......@@ -2366,22 +2390,26 @@ pub fn writeDbgLineHeader(self: *Dwarf) !void {
23662390}
23672391
23682392fn 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);
23702395 return first.off;
23712396}
23722397
23732398fn 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);
23752401 return last.off + last.len;
23762402}
23772403
23782404fn 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);
23802407 return first.off;
23812408}
23822409
23832410fn 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);
23852413 return last.off + last.len;
23862414}
23872415
......@@ -2435,15 +2463,6 @@ fn getRelocDbgInfoSubprogramHighPC(self: Dwarf) u32 {
24352463 return dbg_info_low_pc_reloc_index + self.ptrWidthBytes();
24362464}
24372465
2438/// TODO Improve this to use a table.
2439fn 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
24472466fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) {
24482467 return actual_size +| (actual_size / ideal_factor);
24492468}
......@@ -2465,29 +2484,20 @@ pub fn flushModule(self: *Dwarf, module: *Module) !void {
24652484 }
24662485 error_set.names = names;
24672486
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
24772487 var dbg_info_buffer = std.ArrayList(u8).init(arena);
24782488 try addDbgInfoErrorSet(arena, module, error_ty, self.target, &dbg_info_buffer);
24792489
2480 try self.managed_atoms.append(gpa, atom);
2490 const di_atom_index = try self.createAtom(.di_atom);
24812491 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));
24832493 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);
24852495
24862496 const file_pos = blk: {
24872497 switch (self.bin_file.tag) {
24882498 .elf => {
24892499 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.?];
24912501 break :blk debug_info_sect.sh_offset;
24922502 },
24932503 .macho => {
......@@ -2502,22 +2512,23 @@ pub fn flushModule(self: *Dwarf, module: *Module) !void {
25022512 };
25032513
25042514 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());
25062516
25072517 while (self.global_abbrev_relocs.popOrNull()) |reloc| {
2518 const atom = self.getAtom(.di_atom, reloc.atom_index);
25082519 switch (self.bin_file.tag) {
25092520 .elf => {
25102521 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);
25122523 },
25132524 .macho => {
25142525 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);
25162527 },
25172528 .wasm => {
25182529 const wasm_file = self.bin_file.cast(File.Wasm).?;
2519 const debug_info = wasm_file.debug_info_atom.?.code;
2520 mem.copy(u8, debug_info.items[reloc.atom.off + reloc.offset ..], &buf);
2530 const debug_info = wasm_file.getAtomPtr(wasm_file.debug_info_atom.?).code;
2531 mem.copy(u8, debug_info.items[atom.off + reloc.offset ..], &buf);
25212532 },
25222533 else => unreachable,
25232534 }
......@@ -2635,12 +2646,62 @@ fn addDbgInfoErrorSet(
26352646 try dbg_info_buffer.append(0);
26362647}
26372648
2638fn getDbgInfoAtom(tag: File.Tag, mod: *Module, decl_index: Module.Decl.Index) *Atom {
2639 const decl = mod.declPtr(decl_index);
2640 return switch (tag) {
2641 .elf => &decl.link.elf.dbg_info_atom,
2642 .macho => &decl.link.macho.dbg_info_atom,
2643 .wasm => &decl.link.wasm.dbg_info_atom,
2644 else => unreachable,
2649const Kind = enum { src_fn, di_atom };
2650
2651fn createAtom(self: *Dwarf, comptime kind: Kind) !Atom.Index {
2652 const index = blk: {
2653 switch (kind) {
2654 .src_fn => {
2655 const index = @intCast(Atom.Index, self.src_fns.items.len);
2656 _ = try self.src_fns.addOne(self.allocator);
2657 break :blk index;
2658 },
2659 .di_atom => {
2660 const index = @intCast(Atom.Index, self.di_atoms.items.len);
2661 _ = try self.di_atoms.addOne(self.allocator);
2662 break :blk index;
2663 },
2664 }
2665 };
2666 const atom = self.getAtomPtr(kind, index);
2667 atom.* = .{
2668 .off = 0,
2669 .len = 0,
2670 .prev_index = null,
2671 .next_index = null,
2672 };
2673 return index;
2674}
2675
2676fn 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
2695fn 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
2702fn 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],
26452706 };
26462707}
src/link/Elf.zig+665-633
......@@ -1,40 +1,89 @@
11const Elf = @This();
22
33const std = @import("std");
4const build_options = @import("build_options");
45const builtin = @import("builtin");
5const math = std.math;
6const mem = std.mem;
76const assert = std.debug.assert;
8const Allocator = std.mem.Allocator;
9const fs = std.fs;
107const elf = std.elf;
8const fs = std.fs;
119const log = std.log.scoped(.link);
10const math = std.math;
11const mem = std.mem;
1212
13const Module = @import("../Module.zig");
14const Compilation = @import("../Compilation.zig");
15const Dwarf = @import("Dwarf.zig");
1613const codegen = @import("../codegen.zig");
17const lldMain = @import("../main.zig").lldMain;
18const trace = @import("../tracy.zig").trace;
19const Package = @import("../Package.zig");
20const Value = @import("../value.zig").Value;
21const Type = @import("../type.zig").Type;
22const TypedValue = @import("../TypedValue.zig");
23const link = @import("../link.zig");
24const File = link.File;
25const build_options = @import("build_options");
26const target_util = @import("../target.zig");
2714const glibc = @import("../glibc.zig");
15const link = @import("../link.zig");
16const lldMain = @import("../main.zig").lldMain;
2817const musl = @import("../musl.zig");
29const Cache = @import("../Cache.zig");
18const target_util = @import("../target.zig");
19const trace = @import("../tracy.zig").trace;
20
3021const Air = @import("../Air.zig");
22const Allocator = std.mem.Allocator;
23pub const Atom = @import("Elf/Atom.zig");
24const Cache = @import("../Cache.zig");
25const Compilation = @import("../Compilation.zig");
26const Dwarf = @import("Dwarf.zig");
27const File = link.File;
3128const Liveness = @import("../Liveness.zig");
3229const LlvmObject = @import("../codegen/llvm.zig").Object;
30const Module = @import("../Module.zig");
31const Package = @import("../Package.zig");
32const StringTable = @import("strtab.zig").StringTable;
33const Type = @import("../type.zig").Type;
34const TypedValue = @import("../TypedValue.zig");
35const Value = @import("../value.zig").Value;
3336
3437const default_entry_addr = 0x8000000;
3538
3639pub const base_tag: File.Tag = .elf;
3740
41const 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
66const 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
3887base: File,
3988dwarf: ?Dwarf = null,
4089
......@@ -45,12 +94,12 @@ llvm_object: ?*LlvmObject = null,
4594
4695/// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
4796/// Same order as in the file.
48sections: std.ArrayListUnmanaged(elf.Elf64_Shdr) = std.ArrayListUnmanaged(elf.Elf64_Shdr){},
97sections: std.MultiArrayList(Section) = .{},
4998shdr_table_offset: ?u64 = null,
5099
51100/// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
52101/// Same order as in the file.
53program_headers: std.ArrayListUnmanaged(elf.Elf64_Phdr) = std.ArrayListUnmanaged(elf.Elf64_Phdr){},
102program_headers: std.ArrayListUnmanaged(elf.Elf64_Phdr) = .{},
54103phdr_table_offset: ?u64 = null,
55104/// The index into the program headers of a PT_LOAD program header with Read and Execute flags
56105phdr_load_re_index: ?u16 = null,
......@@ -62,12 +111,10 @@ phdr_load_ro_index: ?u16 = null,
62111/// The index into the program headers of a PT_LOAD program header with Write flag
63112phdr_load_rw_index: ?u16 = null,
64113
65phdr_shdr_table: std.AutoHashMapUnmanaged(u16, u16) = .{},
66
67114entry_addr: ?u64 = null,
68115page_size: u32,
69116
70shstrtab: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8){},
117shstrtab: StringTable(.strtab) = .{},
71118shstrtab_index: ?u16 = null,
72119
73120symtab_section_index: ?u16 = null,
......@@ -110,39 +157,14 @@ debug_line_header_dirty: bool = false,
110157
111158error_flags: File.ErrorFlags = File.ErrorFlags{},
112159
113/// Pointer to the last allocated atom
114atoms: std.AutoHashMapUnmanaged(u16, *TextBlock) = .{},
115
116/// A list of text blocks that have surplus capacity. This list can have false
117/// positives, as functions grow and shrink over time, only sometimes being added
118/// or removed from the freelist.
119///
120/// A text block has surplus capacity when its overcapacity value is greater than
121/// padToIdeal(minimum_text_block_size). That is, when it has so
122/// much extra capacity, that we could fit a small new symbol in it, itself with
123/// ideal_capacity or more.
124///
125/// Ideal capacity is defined by size + (size / ideal_factor)
126///
127/// Overcapacity is measured by actual_capacity - ideal_capacity. Note that
128/// overcapacity can be negative. A simple way to have negative overcapacity is to
129/// allocate a fresh text block, which will have ideal capacity, and then grow it
130/// by 1 byte. It will then have -1 overcapacity.
131atom_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.
137decls: std.AutoHashMapUnmanaged(Module.Decl.Index, ?u16) = .{},
160/// Table of tracked Decls.
161decls: std.AutoHashMapUnmanaged(Module.Decl.Index, DeclMetadata) = .{},
138162
139163/// List of atoms that are owned directly by the linker.
140/// Currently these are only atoms that are the result of linking
141/// object files. Atoms which take part in incremental linking are
142/// at present owned by Module.Decl.
143/// TODO consolidate this.
144managed_atoms: std.ArrayListUnmanaged(*TextBlock) = .{},
145atom_by_index_table: std.AutoHashMapUnmanaged(u32, *TextBlock) = .{},
164atoms: std.ArrayListUnmanaged(Atom) = .{},
165
166/// Table of atoms indexed by the symbol index.
167atom_by_index_table: std.AutoHashMapUnmanaged(u32, Atom.Index) = .{},
146168
147169/// Table of unnamed constants associated with a parent `Decl`.
148170/// We store them here so that we can free the constants whenever the `Decl`
......@@ -170,15 +192,8 @@ unnamed_const_atoms: UnnamedConstTable = .{},
170192/// this will be a table indexed by index into the list of Atoms.
171193relocs: RelocTable = .{},
172194
173const Reloc = struct {
174 target: u32,
175 offset: u64,
176 addend: u32,
177 prev_vaddr: u64,
178};
179
180const RelocTable = std.AutoHashMapUnmanaged(*TextBlock, std.ArrayListUnmanaged(Reloc));
181const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(*TextBlock));
195const RelocTable = std.AutoHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(Atom.Reloc));
196const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(Atom.Index));
182197
183198/// When allocating, the ideal_capacity is calculated by
184199/// actual_capacity + (actual_capacity / ideal_factor)
......@@ -187,67 +202,11 @@ const ideal_factor = 3;
187202/// In order for a slice of bytes to be considered eligible to keep metadata pointing at
188203/// it as a possible place to put new symbols, it must have enough room for this many bytes
189204/// (plus extra for reserved capacity).
190const minimum_text_block_size = 64;
191const min_text_capacity = padToIdeal(minimum_text_block_size);
205const minimum_atom_size = 64;
206pub const min_text_capacity = padToIdeal(minimum_atom_size);
192207
193208pub const PtrWidth = enum { p32, p64 };
194209
195pub 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
247pub const Export = struct {
248 sym_index: ?u32 = null,
249};
250
251210pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Options) !*Elf {
252211 assert(options.target.ofmt == .elf);
253212
......@@ -279,16 +238,19 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
279238
280239 // There must always be a null section in index 0
281240 try self.sections.append(allocator, .{
282 .sh_name = 0,
283 .sh_type = elf.SHT_NULL,
284 .sh_flags = 0,
285 .sh_addr = 0,
286 .sh_offset = 0,
287 .sh_size = 0,
288 .sh_link = 0,
289 .sh_info = 0,
290 .sh_addralign = 0,
291 .sh_entsize = 0,
241 .shdr = .{
242 .sh_name = 0,
243 .sh_type = elf.SHT_NULL,
244 .sh_flags = 0,
245 .sh_addr = 0,
246 .sh_offset = 0,
247 .sh_size = 0,
248 .sh_link = 0,
249 .sh_info = 0,
250 .sh_addralign = 0,
251 .sh_entsize = 0,
252 },
253 .phdr_index = undefined,
292254 });
293255
294256 try self.populateMissingMetadata();
......@@ -335,74 +297,67 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Elf {
335297}
336298
337299pub fn deinit(self: *Elf) void {
300 const gpa = self.base.allocator;
301
338302 if (build_options.have_llvm) {
339 if (self.llvm_object) |llvm_object| llvm_object.destroy(self.base.allocator);
340 }
341
342 self.sections.deinit(self.base.allocator);
343 self.program_headers.deinit(self.base.allocator);
344 self.shstrtab.deinit(self.base.allocator);
345 self.local_symbols.deinit(self.base.allocator);
346 self.global_symbols.deinit(self.base.allocator);
347 self.global_symbol_free_list.deinit(self.base.allocator);
348 self.local_symbol_free_list.deinit(self.base.allocator);
349 self.offset_table_free_list.deinit(self.base.allocator);
350 self.offset_table.deinit(self.base.allocator);
351 self.phdr_shdr_table.deinit(self.base.allocator);
352 self.decls.deinit(self.base.allocator);
353
354 self.atoms.deinit(self.base.allocator);
303 if (self.llvm_object) |llvm_object| llvm_object.destroy(gpa);
304 }
305
306 for (self.sections.items(.free_list)) |*free_list| {
307 free_list.deinit(gpa);
308 }
309 self.sections.deinit(gpa);
310
311 self.program_headers.deinit(gpa);
312 self.shstrtab.deinit(gpa);
313 self.local_symbols.deinit(gpa);
314 self.global_symbols.deinit(gpa);
315 self.global_symbol_free_list.deinit(gpa);
316 self.local_symbol_free_list.deinit(gpa);
317 self.offset_table_free_list.deinit(gpa);
318 self.offset_table.deinit(gpa);
319
355320 {
356 var it = self.atom_free_lists.valueIterator();
357 while (it.next()) |free_list| {
358 free_list.deinit(self.base.allocator);
321 var it = self.decls.iterator();
322 while (it.next()) |entry| {
323 entry.value_ptr.exports.deinit(gpa);
359324 }
360 self.atom_free_lists.deinit(self.base.allocator);
325 self.decls.deinit(gpa);
361326 }
362327
363 for (self.managed_atoms.items) |atom| {
364 self.base.allocator.destroy(atom);
365 }
366 self.managed_atoms.deinit(self.base.allocator);
328 self.atoms.deinit(gpa);
329 self.atom_by_index_table.deinit(gpa);
367330
368331 {
369332 var it = self.unnamed_const_atoms.valueIterator();
370333 while (it.next()) |atoms| {
371 atoms.deinit(self.base.allocator);
334 atoms.deinit(gpa);
372335 }
373 self.unnamed_const_atoms.deinit(self.base.allocator);
336 self.unnamed_const_atoms.deinit(gpa);
374337 }
375338
376339 {
377340 var it = self.relocs.valueIterator();
378341 while (it.next()) |relocs| {
379 relocs.deinit(self.base.allocator);
342 relocs.deinit(gpa);
380343 }
381 self.relocs.deinit(self.base.allocator);
344 self.relocs.deinit(gpa);
382345 }
383346
384 self.atom_by_index_table.deinit(self.base.allocator);
385
386347 if (self.dwarf) |*dw| {
387348 dw.deinit();
388349 }
389350}
390351
391352pub 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
395353 assert(self.llvm_object == null);
396 assert(decl.link.elf.local_sym_index != 0);
397354
398 const target = decl.link.elf.local_sym_index;
399 const vaddr = self.local_symbols.items[target].st_value;
400 const atom = self.atom_by_index_table.get(reloc_info.parent_atom_index).?;
401 const gop = try self.relocs.getOrPut(self.base.allocator, atom);
402 if (!gop.found_existing) {
403 gop.value_ptr.* = .{};
404 }
405 try gop.value_ptr.append(self.base.allocator, .{
355 const this_atom_index = try self.getOrCreateAtomForDecl(decl_index);
356 const this_atom = self.getAtom(this_atom_index);
357 const target = this_atom.getSymbolIndex().?;
358 const vaddr = this_atom.getSymbol(self).st_value;
359 const atom_index = self.getAtomIndexForSymbol(reloc_info.parent_atom_index).?;
360 try Atom.addRelocation(self, atom_index, .{
406361 .target = target,
407362 .offset = reloc_info.offset,
408363 .addend = reloc_info.addend,
......@@ -423,7 +378,7 @@ fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 {
423378
424379 if (self.shdr_table_offset) |off| {
425380 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;
427382 const increased_size = padToIdeal(tight_size);
428383 const test_end = off + increased_size;
429384 if (end > off and start < test_end) {
......@@ -433,7 +388,7 @@ fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 {
433388
434389 if (self.phdr_table_offset) |off| {
435390 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;
437392 const increased_size = padToIdeal(tight_size);
438393 const test_end = off + increased_size;
439394 if (end > off and start < test_end) {
......@@ -441,7 +396,7 @@ fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 {
441396 }
442397 }
443398
444 for (self.sections.items) |section| {
399 for (self.sections.items(.shdr)) |section| {
445400 const increased_size = padToIdeal(section.sh_size);
446401 const test_end = section.sh_offset + increased_size;
447402 if (end > section.sh_offset and start < test_end) {
......@@ -468,7 +423,7 @@ pub fn allocatedSize(self: *Elf, start: u64) u64 {
468423 if (self.phdr_table_offset) |off| {
469424 if (off > start and off < min_pos) min_pos = off;
470425 }
471 for (self.sections.items) |section| {
426 for (self.sections.items(.shdr)) |section| {
472427 if (section.sh_offset <= start) continue;
473428 if (section.sh_offset < min_pos) min_pos = section.sh_offset;
474429 }
......@@ -487,31 +442,10 @@ pub fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u32) u64 {
487442 return start;
488443}
489444
490/// TODO Improve this to use a table.
491fn 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
499fn 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
504fn 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
512445pub fn populateMissingMetadata(self: *Elf) !void {
513446 assert(self.llvm_object == null);
514447
448 const gpa = self.base.allocator;
515449 const small_ptr = switch (self.ptr_width) {
516450 .p32 => true,
517451 .p64 => false,
......@@ -525,7 +459,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {
525459 const off = self.findFreeSpace(file_size, p_align);
526460 log.debug("found PT_LOAD RE free space 0x{x} to 0x{x}", .{ off, off + file_size });
527461 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, .{
529463 .p_type = elf.PT_LOAD,
530464 .p_offset = off,
531465 .p_filesz = file_size,
......@@ -535,7 +469,6 @@ pub fn populateMissingMetadata(self: *Elf) !void {
535469 .p_align = p_align,
536470 .p_flags = elf.PF_X | elf.PF_R,
537471 });
538 try self.atom_free_lists.putNoClobber(self.base.allocator, self.phdr_load_re_index.?, .{});
539472 self.entry_addr = null;
540473 self.phdr_table_dirty = true;
541474 }
......@@ -552,7 +485,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {
552485 // we'll need to re-use that function anyway, in case the GOT grows and overlaps something
553486 // else in virtual memory.
554487 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, .{
556489 .p_type = elf.PT_LOAD,
557490 .p_offset = off,
558491 .p_filesz = file_size,
......@@ -575,7 +508,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {
575508 log.debug("found PT_LOAD RO free space 0x{x} to 0x{x}", .{ off, off + file_size });
576509 // TODO Same as for GOT
577510 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, .{
579512 .p_type = elf.PT_LOAD,
580513 .p_offset = off,
581514 .p_filesz = file_size,
......@@ -585,7 +518,6 @@ pub fn populateMissingMetadata(self: *Elf) !void {
585518 .p_align = p_align,
586519 .p_flags = elf.PF_R,
587520 });
588 try self.atom_free_lists.putNoClobber(self.base.allocator, self.phdr_load_ro_index.?, .{});
589521 self.phdr_table_dirty = true;
590522 }
591523
......@@ -599,7 +531,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {
599531 log.debug("found PT_LOAD RW free space 0x{x} to 0x{x}", .{ off, off + file_size });
600532 // TODO Same as for GOT
601533 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, .{
603535 .p_type = elf.PT_LOAD,
604536 .p_offset = off,
605537 .p_filesz = file_size,
......@@ -609,148 +541,145 @@ pub fn populateMissingMetadata(self: *Elf) !void {
609541 .p_align = p_align,
610542 .p_flags = elf.PF_R | elf.PF_W,
611543 });
612 try self.atom_free_lists.putNoClobber(self.base.allocator, self.phdr_load_rw_index.?, .{});
613544 self.phdr_table_dirty = true;
614545 }
615546
616547 if (self.shstrtab_index == null) {
617 self.shstrtab_index = @intCast(u16, self.sections.items.len);
618 assert(self.shstrtab.items.len == 0);
619 try self.shstrtab.append(self.base.allocator, 0); // need a 0 at position 0
620 const off = self.findFreeSpace(self.shstrtab.items.len, 1);
621 log.debug("found shstrtab free space 0x{x} to 0x{x}", .{ off, off + self.shstrtab.items.len });
622 try self.sections.append(self.base.allocator, .{
623 .sh_name = try self.makeString(".shstrtab"),
624 .sh_type = elf.SHT_STRTAB,
625 .sh_flags = 0,
626 .sh_addr = 0,
627 .sh_offset = off,
628 .sh_size = self.shstrtab.items.len,
629 .sh_link = 0,
630 .sh_info = 0,
631 .sh_addralign = 1,
632 .sh_entsize = 0,
548 self.shstrtab_index = @intCast(u16, self.sections.slice().len);
549 assert(self.shstrtab.buffer.items.len == 0);
550 try self.shstrtab.buffer.append(gpa, 0); // need a 0 at position 0
551 const off = self.findFreeSpace(self.shstrtab.buffer.items.len, 1);
552 log.debug("found shstrtab free space 0x{x} to 0x{x}", .{ off, off + self.shstrtab.buffer.items.len });
553 try self.sections.append(gpa, .{
554 .shdr = .{
555 .sh_name = try self.shstrtab.insert(gpa, ".shstrtab"),
556 .sh_type = elf.SHT_STRTAB,
557 .sh_flags = 0,
558 .sh_addr = 0,
559 .sh_offset = off,
560 .sh_size = self.shstrtab.buffer.items.len,
561 .sh_link = 0,
562 .sh_info = 0,
563 .sh_addralign = 1,
564 .sh_entsize = 0,
565 },
566 .phdr_index = undefined,
633567 });
634568 self.shstrtab_dirty = true;
635569 self.shdr_table_dirty = true;
636570 }
637571
638572 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);
640574 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
641575
642 try self.sections.append(self.base.allocator, .{
643 .sh_name = try self.makeString(".text"),
644 .sh_type = elf.SHT_PROGBITS,
645 .sh_flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR,
646 .sh_addr = phdr.p_vaddr,
647 .sh_offset = phdr.p_offset,
648 .sh_size = phdr.p_filesz,
649 .sh_link = 0,
650 .sh_info = 0,
651 .sh_addralign = 1,
652 .sh_entsize = 0,
576 try self.sections.append(gpa, .{
577 .shdr = .{
578 .sh_name = try self.shstrtab.insert(gpa, ".text"),
579 .sh_type = elf.SHT_PROGBITS,
580 .sh_flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR,
581 .sh_addr = phdr.p_vaddr,
582 .sh_offset = phdr.p_offset,
583 .sh_size = phdr.p_filesz,
584 .sh_link = 0,
585 .sh_info = 0,
586 .sh_addralign = 1,
587 .sh_entsize = 0,
588 },
589 .phdr_index = self.phdr_load_re_index.?,
653590 });
654 try self.phdr_shdr_table.putNoClobber(
655 self.base.allocator,
656 self.phdr_load_re_index.?,
657 self.text_section_index.?,
658 );
659591 self.shdr_table_dirty = true;
660592 }
661593
662594 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);
664596 const phdr = &self.program_headers.items[self.phdr_got_index.?];
665597
666 try self.sections.append(self.base.allocator, .{
667 .sh_name = try self.makeString(".got"),
668 .sh_type = elf.SHT_PROGBITS,
669 .sh_flags = elf.SHF_ALLOC,
670 .sh_addr = phdr.p_vaddr,
671 .sh_offset = phdr.p_offset,
672 .sh_size = phdr.p_filesz,
673 .sh_link = 0,
674 .sh_info = 0,
675 .sh_addralign = @as(u16, ptr_size),
676 .sh_entsize = 0,
598 try self.sections.append(gpa, .{
599 .shdr = .{
600 .sh_name = try self.shstrtab.insert(gpa, ".got"),
601 .sh_type = elf.SHT_PROGBITS,
602 .sh_flags = elf.SHF_ALLOC,
603 .sh_addr = phdr.p_vaddr,
604 .sh_offset = phdr.p_offset,
605 .sh_size = phdr.p_filesz,
606 .sh_link = 0,
607 .sh_info = 0,
608 .sh_addralign = @as(u16, ptr_size),
609 .sh_entsize = 0,
610 },
611 .phdr_index = self.phdr_got_index.?,
677612 });
678 try self.phdr_shdr_table.putNoClobber(
679 self.base.allocator,
680 self.phdr_got_index.?,
681 self.got_section_index.?,
682 );
683613 self.shdr_table_dirty = true;
684614 }
685615
686616 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);
688618 const phdr = &self.program_headers.items[self.phdr_load_ro_index.?];
689619
690 try self.sections.append(self.base.allocator, .{
691 .sh_name = try self.makeString(".rodata"),
692 .sh_type = elf.SHT_PROGBITS,
693 .sh_flags = elf.SHF_ALLOC,
694 .sh_addr = phdr.p_vaddr,
695 .sh_offset = phdr.p_offset,
696 .sh_size = phdr.p_filesz,
697 .sh_link = 0,
698 .sh_info = 0,
699 .sh_addralign = 1,
700 .sh_entsize = 0,
620 try self.sections.append(gpa, .{
621 .shdr = .{
622 .sh_name = try self.shstrtab.insert(gpa, ".rodata"),
623 .sh_type = elf.SHT_PROGBITS,
624 .sh_flags = elf.SHF_ALLOC,
625 .sh_addr = phdr.p_vaddr,
626 .sh_offset = phdr.p_offset,
627 .sh_size = phdr.p_filesz,
628 .sh_link = 0,
629 .sh_info = 0,
630 .sh_addralign = 1,
631 .sh_entsize = 0,
632 },
633 .phdr_index = self.phdr_load_ro_index.?,
701634 });
702 try self.phdr_shdr_table.putNoClobber(
703 self.base.allocator,
704 self.phdr_load_ro_index.?,
705 self.rodata_section_index.?,
706 );
707635 self.shdr_table_dirty = true;
708636 }
709637
710638 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);
712640 const phdr = &self.program_headers.items[self.phdr_load_rw_index.?];
713641
714 try self.sections.append(self.base.allocator, .{
715 .sh_name = try self.makeString(".data"),
716 .sh_type = elf.SHT_PROGBITS,
717 .sh_flags = elf.SHF_WRITE | elf.SHF_ALLOC,
718 .sh_addr = phdr.p_vaddr,
719 .sh_offset = phdr.p_offset,
720 .sh_size = phdr.p_filesz,
721 .sh_link = 0,
722 .sh_info = 0,
723 .sh_addralign = @as(u16, ptr_size),
724 .sh_entsize = 0,
642 try self.sections.append(gpa, .{
643 .shdr = .{
644 .sh_name = try self.shstrtab.insert(gpa, ".data"),
645 .sh_type = elf.SHT_PROGBITS,
646 .sh_flags = elf.SHF_WRITE | elf.SHF_ALLOC,
647 .sh_addr = phdr.p_vaddr,
648 .sh_offset = phdr.p_offset,
649 .sh_size = phdr.p_filesz,
650 .sh_link = 0,
651 .sh_info = 0,
652 .sh_addralign = @as(u16, ptr_size),
653 .sh_entsize = 0,
654 },
655 .phdr_index = self.phdr_load_rw_index.?,
725656 });
726 try self.phdr_shdr_table.putNoClobber(
727 self.base.allocator,
728 self.phdr_load_rw_index.?,
729 self.data_section_index.?,
730 );
731657 self.shdr_table_dirty = true;
732658 }
733659
734660 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);
736662 const min_align: u16 = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym);
737663 const each_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Sym) else @sizeOf(elf.Elf64_Sym);
738664 const file_size = self.base.options.symbol_count_hint * each_size;
739665 const off = self.findFreeSpace(file_size, min_align);
740666 log.debug("found symtab free space 0x{x} to 0x{x}", .{ off, off + file_size });
741667
742 try self.sections.append(self.base.allocator, .{
743 .sh_name = try self.makeString(".symtab"),
744 .sh_type = elf.SHT_SYMTAB,
745 .sh_flags = 0,
746 .sh_addr = 0,
747 .sh_offset = off,
748 .sh_size = file_size,
749 // The section header index of the associated string table.
750 .sh_link = self.shstrtab_index.?,
751 .sh_info = @intCast(u32, self.local_symbols.items.len),
752 .sh_addralign = min_align,
753 .sh_entsize = each_size,
668 try self.sections.append(gpa, .{
669 .shdr = .{
670 .sh_name = try self.shstrtab.insert(gpa, ".symtab"),
671 .sh_type = elf.SHT_SYMTAB,
672 .sh_flags = 0,
673 .sh_addr = 0,
674 .sh_offset = off,
675 .sh_size = file_size,
676 // The section header index of the associated string table.
677 .sh_link = self.shstrtab_index.?,
678 .sh_info = @intCast(u32, self.local_symbols.items.len),
679 .sh_addralign = min_align,
680 .sh_entsize = each_size,
681 },
682 .phdr_index = undefined,
754683 });
755684 self.shdr_table_dirty = true;
756685 try self.writeSymbol(0);
......@@ -758,27 +687,30 @@ pub fn populateMissingMetadata(self: *Elf) !void {
758687
759688 if (self.dwarf) |*dw| {
760689 if (self.debug_str_section_index == null) {
761 self.debug_str_section_index = @intCast(u16, self.sections.items.len);
762 assert(dw.strtab.items.len == 0);
763 try dw.strtab.append(self.base.allocator, 0);
764 try self.sections.append(self.base.allocator, .{
765 .sh_name = try self.makeString(".debug_str"),
766 .sh_type = elf.SHT_PROGBITS,
767 .sh_flags = elf.SHF_MERGE | elf.SHF_STRINGS,
768 .sh_addr = 0,
769 .sh_offset = 0,
770 .sh_size = 0,
771 .sh_link = 0,
772 .sh_info = 0,
773 .sh_addralign = 1,
774 .sh_entsize = 1,
690 self.debug_str_section_index = @intCast(u16, self.sections.slice().len);
691 assert(dw.strtab.buffer.items.len == 0);
692 try dw.strtab.buffer.append(gpa, 0);
693 try self.sections.append(gpa, .{
694 .shdr = .{
695 .sh_name = try self.shstrtab.insert(gpa, ".debug_str"),
696 .sh_type = elf.SHT_PROGBITS,
697 .sh_flags = elf.SHF_MERGE | elf.SHF_STRINGS,
698 .sh_addr = 0,
699 .sh_offset = 0,
700 .sh_size = 0,
701 .sh_link = 0,
702 .sh_info = 0,
703 .sh_addralign = 1,
704 .sh_entsize = 1,
705 },
706 .phdr_index = undefined,
775707 });
776708 self.debug_strtab_dirty = true;
777709 self.shdr_table_dirty = true;
778710 }
779711
780712 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);
782714
783715 const file_size_hint = 200;
784716 const p_align = 1;
......@@ -787,24 +719,27 @@ pub fn populateMissingMetadata(self: *Elf) !void {
787719 off,
788720 off + file_size_hint,
789721 });
790 try self.sections.append(self.base.allocator, .{
791 .sh_name = try self.makeString(".debug_info"),
792 .sh_type = elf.SHT_PROGBITS,
793 .sh_flags = 0,
794 .sh_addr = 0,
795 .sh_offset = off,
796 .sh_size = file_size_hint,
797 .sh_link = 0,
798 .sh_info = 0,
799 .sh_addralign = p_align,
800 .sh_entsize = 0,
722 try self.sections.append(gpa, .{
723 .shdr = .{
724 .sh_name = try self.shstrtab.insert(gpa, ".debug_info"),
725 .sh_type = elf.SHT_PROGBITS,
726 .sh_flags = 0,
727 .sh_addr = 0,
728 .sh_offset = off,
729 .sh_size = file_size_hint,
730 .sh_link = 0,
731 .sh_info = 0,
732 .sh_addralign = p_align,
733 .sh_entsize = 0,
734 },
735 .phdr_index = undefined,
801736 });
802737 self.shdr_table_dirty = true;
803738 self.debug_info_header_dirty = true;
804739 }
805740
806741 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);
808743
809744 const file_size_hint = 128;
810745 const p_align = 1;
......@@ -813,24 +748,27 @@ pub fn populateMissingMetadata(self: *Elf) !void {
813748 off,
814749 off + file_size_hint,
815750 });
816 try self.sections.append(self.base.allocator, .{
817 .sh_name = try self.makeString(".debug_abbrev"),
818 .sh_type = elf.SHT_PROGBITS,
819 .sh_flags = 0,
820 .sh_addr = 0,
821 .sh_offset = off,
822 .sh_size = file_size_hint,
823 .sh_link = 0,
824 .sh_info = 0,
825 .sh_addralign = p_align,
826 .sh_entsize = 0,
751 try self.sections.append(gpa, .{
752 .shdr = .{
753 .sh_name = try self.shstrtab.insert(gpa, ".debug_abbrev"),
754 .sh_type = elf.SHT_PROGBITS,
755 .sh_flags = 0,
756 .sh_addr = 0,
757 .sh_offset = off,
758 .sh_size = file_size_hint,
759 .sh_link = 0,
760 .sh_info = 0,
761 .sh_addralign = p_align,
762 .sh_entsize = 0,
763 },
764 .phdr_index = undefined,
827765 });
828766 self.shdr_table_dirty = true;
829767 self.debug_abbrev_section_dirty = true;
830768 }
831769
832770 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);
834772
835773 const file_size_hint = 160;
836774 const p_align = 16;
......@@ -839,24 +777,27 @@ pub fn populateMissingMetadata(self: *Elf) !void {
839777 off,
840778 off + file_size_hint,
841779 });
842 try self.sections.append(self.base.allocator, .{
843 .sh_name = try self.makeString(".debug_aranges"),
844 .sh_type = elf.SHT_PROGBITS,
845 .sh_flags = 0,
846 .sh_addr = 0,
847 .sh_offset = off,
848 .sh_size = file_size_hint,
849 .sh_link = 0,
850 .sh_info = 0,
851 .sh_addralign = p_align,
852 .sh_entsize = 0,
780 try self.sections.append(gpa, .{
781 .shdr = .{
782 .sh_name = try self.shstrtab.insert(gpa, ".debug_aranges"),
783 .sh_type = elf.SHT_PROGBITS,
784 .sh_flags = 0,
785 .sh_addr = 0,
786 .sh_offset = off,
787 .sh_size = file_size_hint,
788 .sh_link = 0,
789 .sh_info = 0,
790 .sh_addralign = p_align,
791 .sh_entsize = 0,
792 },
793 .phdr_index = undefined,
853794 });
854795 self.shdr_table_dirty = true;
855796 self.debug_aranges_section_dirty = true;
856797 }
857798
858799 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);
860801
861802 const file_size_hint = 250;
862803 const p_align = 1;
......@@ -865,17 +806,20 @@ pub fn populateMissingMetadata(self: *Elf) !void {
865806 off,
866807 off + file_size_hint,
867808 });
868 try self.sections.append(self.base.allocator, .{
869 .sh_name = try self.makeString(".debug_line"),
870 .sh_type = elf.SHT_PROGBITS,
871 .sh_flags = 0,
872 .sh_addr = 0,
873 .sh_offset = off,
874 .sh_size = file_size_hint,
875 .sh_link = 0,
876 .sh_info = 0,
877 .sh_addralign = p_align,
878 .sh_entsize = 0,
809 try self.sections.append(gpa, .{
810 .shdr = .{
811 .sh_name = try self.shstrtab.insert(gpa, ".debug_line"),
812 .sh_type = elf.SHT_PROGBITS,
813 .sh_flags = 0,
814 .sh_addr = 0,
815 .sh_offset = off,
816 .sh_size = file_size_hint,
817 .sh_link = 0,
818 .sh_info = 0,
819 .sh_addralign = p_align,
820 .sh_entsize = 0,
821 },
822 .phdr_index = undefined,
879823 });
880824 self.shdr_table_dirty = true;
881825 self.debug_line_header_dirty = true;
......@@ -891,7 +835,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {
891835 .p64 => @alignOf(elf.Elf64_Shdr),
892836 };
893837 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);
895839 self.shdr_table_dirty = true;
896840 }
897841
......@@ -922,7 +866,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {
922866 // offset + it's filesize.
923867 var max_file_offset: u64 = 0;
924868
925 for (self.sections.items) |shdr| {
869 for (self.sections.items(.shdr)) |shdr| {
926870 if (shdr.sh_offset + shdr.sh_size > max_file_offset) {
927871 max_file_offset = shdr.sh_offset + shdr.sh_size;
928872 }
......@@ -932,24 +876,27 @@ pub fn populateMissingMetadata(self: *Elf) !void {
932876 }
933877}
934878
935fn growAllocSection(self: *Elf, shdr_index: u16, phdr_index: u16, needed_size: u64) !void {
879fn growAllocSection(self: *Elf, shdr_index: u16, needed_size: u64) !void {
936880 // 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];
938883 const phdr = &self.program_headers.items[phdr_index];
884 const maybe_last_atom_index = self.sections.items(.last_atom_index)[shdr_index];
939885
940886 if (needed_size > self.allocatedSize(shdr.sh_offset)) {
941887 // Must move the entire section.
942888 const new_offset = self.findFreeSpace(needed_size, self.page_size);
943 const existing_size = if (self.atoms.get(phdr_index)) |last| blk: {
944 const sym = self.local_symbols.items[last.local_sym_index];
889 const existing_size = if (maybe_last_atom_index) |last_atom_index| blk: {
890 const last = self.getAtom(last_atom_index);
891 const sym = last.getSymbol(self);
945892 break :blk (sym.st_value + sym.st_size) - phdr.p_vaddr;
946893 } else if (shdr_index == self.got_section_index.?) blk: {
947894 break :blk shdr.sh_size;
948895 } else 0;
949896 shdr.sh_size = 0;
950897
951 log.debug("new '{s}' file offset 0x{x} to 0x{x}", .{
952 self.getString(shdr.sh_name),
898 log.debug("new '{?s}' file offset 0x{x} to 0x{x}", .{
899 self.shstrtab.get(shdr.sh_name),
953900 new_offset,
954901 new_offset + existing_size,
955902 });
......@@ -975,7 +922,7 @@ pub fn growNonAllocSection(
975922 min_alignment: u32,
976923 requires_file_copy: bool,
977924) !void {
978 const shdr = &self.sections.items[shdr_index];
925 const shdr = &self.sections.items(.shdr)[shdr_index];
979926
980927 if (needed_size > self.allocatedSize(shdr.sh_offset)) {
981928 const existing_size = if (self.symtab_section_index.? == shdr_index) blk: {
......@@ -988,7 +935,7 @@ pub fn growNonAllocSection(
988935 shdr.sh_size = 0;
989936 // Move all the symbols to a new file location.
990937 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 });
992939
993940 if (requires_file_copy) {
994941 const amt = try self.base.file.?.copyRangeAll(
......@@ -1059,6 +1006,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
10591006 }
10601007 }
10611008
1009 const gpa = self.base.allocator;
10621010 var sub_prog_node = prog_node.start("ELF Flush", 0);
10631011 sub_prog_node.activate();
10641012 defer sub_prog_node.end();
......@@ -1077,12 +1025,13 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
10771025 {
10781026 var it = self.relocs.iterator();
10791027 while (it.next()) |entry| {
1080 const atom = entry.key_ptr.*;
1028 const atom_index = entry.key_ptr.*;
10811029 const relocs = entry.value_ptr.*;
1082 const source_sym = self.local_symbols.items[atom.local_sym_index];
1083 const source_shdr = self.sections.items[source_sym.st_shndx];
1030 const atom = self.getAtom(atom_index);
1031 const source_sym = atom.getSymbol(self);
1032 const source_shdr = self.sections.items(.shdr)[source_sym.st_shndx];
10841033
1085 log.debug("relocating '{s}'", .{self.getString(source_sym.st_name)});
1034 log.debug("relocating '{?s}'", .{self.shstrtab.get(source_sym.st_name)});
10861035
10871036 for (relocs.items) |*reloc| {
10881037 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
10931042 const section_offset = (source_sym.st_value + reloc.offset) - source_shdr.sh_addr;
10941043 const file_offset = source_shdr.sh_offset + section_offset;
10951044
1096 log.debug(" ({x}: [() => 0x{x}] ({s}))", .{
1045 log.debug(" ({x}: [() => 0x{x}] ({?s}))", .{
10971046 reloc.offset,
10981047 target_vaddr,
1099 self.getString(target_sym.st_name),
1048 self.shstrtab.get(target_sym.st_name),
11001049 });
11011050
11021051 switch (self.ptr_width) {
......@@ -1174,8 +1123,8 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
11741123
11751124 switch (self.ptr_width) {
11761125 .p32 => {
1177 const buf = try self.base.allocator.alloc(elf.Elf32_Phdr, self.program_headers.items.len);
1178 defer self.base.allocator.free(buf);
1126 const buf = try gpa.alloc(elf.Elf32_Phdr, self.program_headers.items.len);
1127 defer gpa.free(buf);
11791128
11801129 for (buf) |*phdr, i| {
11811130 phdr.* = progHeaderTo32(self.program_headers.items[i]);
......@@ -1186,8 +1135,8 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
11861135 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?);
11871136 },
11881137 .p64 => {
1189 const buf = try self.base.allocator.alloc(elf.Elf64_Phdr, self.program_headers.items.len);
1190 defer self.base.allocator.free(buf);
1138 const buf = try gpa.alloc(elf.Elf64_Phdr, self.program_headers.items.len);
1139 defer gpa.free(buf);
11911140
11921141 for (buf) |*phdr, i| {
11931142 phdr.* = self.program_headers.items[i];
......@@ -1203,20 +1152,20 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
12031152
12041153 {
12051154 const shdr_index = self.shstrtab_index.?;
1206 if (self.shstrtab_dirty or self.shstrtab.items.len != self.sections.items[shdr_index].sh_size) {
1207 try self.growNonAllocSection(shdr_index, self.shstrtab.items.len, 1, false);
1208 const shstrtab_sect = self.sections.items[shdr_index];
1209 try self.base.file.?.pwriteAll(self.shstrtab.items, shstrtab_sect.sh_offset);
1155 if (self.shstrtab_dirty or self.shstrtab.buffer.items.len != self.sections.items(.shdr)[shdr_index].sh_size) {
1156 try self.growNonAllocSection(shdr_index, self.shstrtab.buffer.items.len, 1, false);
1157 const shstrtab_sect = self.sections.items(.shdr)[shdr_index];
1158 try self.base.file.?.pwriteAll(self.shstrtab.buffer.items, shstrtab_sect.sh_offset);
12101159 self.shstrtab_dirty = false;
12111160 }
12121161 }
12131162
12141163 if (self.dwarf) |dwarf| {
12151164 const shdr_index = self.debug_str_section_index.?;
1216 if (self.debug_strtab_dirty or dwarf.strtab.items.len != self.sections.items[shdr_index].sh_size) {
1217 try self.growNonAllocSection(shdr_index, dwarf.strtab.items.len, 1, false);
1218 const debug_strtab_sect = self.sections.items[shdr_index];
1219 try self.base.file.?.pwriteAll(dwarf.strtab.items, debug_strtab_sect.sh_offset);
1165 if (self.debug_strtab_dirty or dwarf.strtab.buffer.items.len != self.sections.items(.shdr)[shdr_index].sh_size) {
1166 try self.growNonAllocSection(shdr_index, dwarf.strtab.buffer.items.len, 1, false);
1167 const debug_strtab_sect = self.sections.items(.shdr)[shdr_index];
1168 try self.base.file.?.pwriteAll(dwarf.strtab.buffer.items, debug_strtab_sect.sh_offset);
12201169 self.debug_strtab_dirty = false;
12211170 }
12221171 }
......@@ -1231,7 +1180,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
12311180 .p64 => @alignOf(elf.Elf64_Shdr),
12321181 };
12331182 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;
12351184
12361185 if (needed_size > allocated_size) {
12371186 self.shdr_table_offset = null; // free the space
......@@ -1240,12 +1189,13 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
12401189
12411190 switch (self.ptr_width) {
12421191 .p32 => {
1243 const buf = try self.base.allocator.alloc(elf.Elf32_Shdr, self.sections.items.len);
1244 defer self.base.allocator.free(buf);
1192 const slice = self.sections.slice();
1193 const buf = try gpa.alloc(elf.Elf32_Shdr, slice.len);
1194 defer gpa.free(buf);
12451195
12461196 for (buf) |*shdr, i| {
1247 shdr.* = sectHeaderTo32(self.sections.items[i]);
1248 log.debug("writing section {s}: {}", .{ self.getString(shdr.sh_name), shdr.* });
1197 shdr.* = sectHeaderTo32(slice.items(.shdr)[i]);
1198 log.debug("writing section {?s}: {}", .{ self.shstrtab.get(shdr.sh_name), shdr.* });
12491199 if (foreign_endian) {
12501200 mem.byteSwapAllFields(elf.Elf32_Shdr, shdr);
12511201 }
......@@ -1253,12 +1203,13 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
12531203 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);
12541204 },
12551205 .p64 => {
1256 const buf = try self.base.allocator.alloc(elf.Elf64_Shdr, self.sections.items.len);
1257 defer self.base.allocator.free(buf);
1206 const slice = self.sections.slice();
1207 const buf = try gpa.alloc(elf.Elf64_Shdr, slice.len);
1208 defer gpa.free(buf);
12581209
12591210 for (buf) |*shdr, i| {
1260 shdr.* = self.sections.items[i];
1261 log.debug("writing section {s}: {}", .{ self.getString(shdr.sh_name), shdr.* });
1211 shdr.* = slice.items(.shdr)[i];
1212 log.debug("writing section {?s}: {}", .{ self.shstrtab.get(shdr.sh_name), shdr.* });
12621213 if (foreign_endian) {
12631214 mem.byteSwapAllFields(elf.Elf64_Shdr, shdr);
12641215 }
......@@ -2069,7 +2020,7 @@ fn writeElfHeader(self: *Elf) !void {
20692020 mem.writeInt(u16, hdr_buf[index..][0..2], e_shentsize, endian);
20702021 index += 2;
20712022
2072 const e_shnum = @intCast(u16, self.sections.items.len);
2023 const e_shnum = @intCast(u16, self.sections.slice().len);
20732024 mem.writeInt(u16, hdr_buf[index..][0..2], e_shnum, endian);
20742025 index += 2;
20752026
......@@ -2081,113 +2032,145 @@ fn writeElfHeader(self: *Elf) !void {
20812032 try self.base.file.?.pwriteAll(hdr_buf[0..index], 0);
20822033}
20832034
2084fn freeTextBlock(self: *Elf, text_block: *TextBlock, phdr_index: u16) void {
2085 const local_sym = self.local_symbols.items[text_block.local_sym_index];
2086 const name_str_index = local_sym.st_name;
2087 const name = self.getString(name_str_index);
2088 log.debug("freeTextBlock {*} ({s})", .{ text_block, name });
2035fn freeAtom(self: *Elf, atom_index: Atom.Index) void {
2036 const atom = self.getAtom(atom_index);
2037 log.debug("freeAtom {d} ({s})", .{ atom_index, atom.getName(self) });
20892038
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];
20912044 var already_have_free_list_node = false;
20922045 {
20932046 var i: usize = 0;
20942047 // TODO turn free_list into a hash map
20952048 while (i < free_list.items.len) {
2096 if (free_list.items[i] == text_block) {
2049 if (free_list.items[i] == atom_index) {
20972050 _ = free_list.swapRemove(i);
20982051 continue;
20992052 }
2100 if (free_list.items[i] == text_block.prev) {
2053 if (free_list.items[i] == atom.prev_index) {
21012054 already_have_free_list_node = true;
21022055 }
21032056 i += 1;
21042057 }
21052058 }
21062059
2107 if (self.atoms.getPtr(phdr_index)) |last_block| {
2108 if (last_block.* == text_block) {
2109 if (text_block.prev) |prev| {
2060 const maybe_last_atom_index = &self.sections.items(.last_atom_index)[shndx];
2061 if (maybe_last_atom_index.*) |last_atom_index| {
2062 if (last_atom_index == atom_index) {
2063 if (atom.prev_index) |prev_index| {
21102064 // TODO shrink the section size here
2111 last_block.* = prev;
2065 maybe_last_atom_index.* = prev_index;
21122066 } else {
2113 _ = self.atoms.fetchRemove(phdr_index);
2067 maybe_last_atom_index.* = null;
21142068 }
21152069 }
21162070 }
21172071
2118 if (text_block.prev) |prev| {
2119 prev.next = text_block.next;
2072 if (atom.prev_index) |prev_index| {
2073 const prev = self.getAtomPtr(prev_index);
2074 prev.next_index = atom.next_index;
21202075
2121 if (!already_have_free_list_node and prev.freeListEligible(self.*)) {
2076 if (!already_have_free_list_node and prev.*.freeListEligible(self)) {
21222077 // The free list is heuristics, it doesn't have to be perfect, so we can
21232078 // ignore the OOM here.
2124 free_list.append(self.base.allocator, prev) catch {};
2079 free_list.append(gpa, prev_index) catch {};
21252080 }
21262081 } else {
2127 text_block.prev = null;
2082 self.getAtomPtr(atom_index).prev_index = null;
21282083 }
21292084
2130 if (text_block.next) |next| {
2131 next.prev = text_block.prev;
2085 if (atom.next_index) |next_index| {
2086 self.getAtomPtr(next_index).prev_index = atom.prev_index;
21322087 } else {
2133 text_block.next = null;
2088 self.getAtomPtr(atom_index).next_index = null;
21342089 }
21352090
2136 if (self.dwarf) |*dw| {
2137 dw.freeAtom(&text_block.dbg_info_atom);
2138 }
2091 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
2092 const local_sym_index = atom.getSymbolIndex().?;
2093
2094 self.local_symbol_free_list.append(gpa, local_sym_index) catch {};
2095 self.local_symbols.items[local_sym_index].st_info = 0;
2096 self.local_symbols.items[local_sym_index].st_shndx = 0;
2097 _ = self.atom_by_index_table.remove(local_sym_index);
2098 self.getAtomPtr(atom_index).local_sym_index = 0;
2099
2100 self.offset_table_free_list.append(self.base.allocator, atom.offset_table_index) catch {};
21392101}
21402102
2141fn shrinkTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, phdr_index: u16) void {
2103fn shrinkAtom(self: *Elf, atom_index: Atom.Index, new_block_size: u64) void {
21422104 _ = self;
2143 _ = text_block;
2105 _ = atom_index;
21442106 _ = new_block_size;
2145 _ = phdr_index;
21462107}
21472108
2148fn growTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, alignment: u64, phdr_index: u16) !u64 {
2149 const sym = self.local_symbols.items[text_block.local_sym_index];
2109fn growAtom(self: *Elf, atom_index: Atom.Index, new_block_size: u64, alignment: u64) !u64 {
2110 const atom = self.getAtom(atom_index);
2111 const sym = atom.getSymbol(self);
21502112 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);
21522114 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);
21542116}
21552117
2156fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, alignment: u64, phdr_index: u16) !u64 {
2157 const shdr_index = self.phdr_shdr_table.get(phdr_index).?;
2118pub fn createAtom(self: *Elf) !Atom.Index {
2119 const gpa = self.base.allocator;
2120 const atom_index = @intCast(Atom.Index, self.atoms.items.len);
2121 const atom = try self.atoms.addOne(gpa);
2122 const local_sym_index = try self.allocateLocalSymbol();
2123 const offset_table_index = try self.allocateGotOffset();
2124 try self.atom_by_index_table.putNoClobber(gpa, local_sym_index, atom_index);
2125 atom.* = .{
2126 .local_sym_index = local_sym_index,
2127 .offset_table_index = offset_table_index,
2128 .prev_index = null,
2129 .next_index = null,
2130 };
2131 log.debug("creating ATOM(%{d}) at index {d}", .{ local_sym_index, atom_index });
2132 return atom_index;
2133}
2134
2135fn 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];
21582139 const phdr = &self.program_headers.items[phdr_index];
2159 const shdr = &self.sections.items[shdr_index];
2160 const new_block_ideal_capacity = padToIdeal(new_block_size);
2140 const shdr = &self.sections.items(.shdr)[sym.st_shndx];
2141 const free_list = &self.sections.items(.free_list)[sym.st_shndx];
2142 const maybe_last_atom_index = &self.sections.items(.last_atom_index)[sym.st_shndx];
2143 const new_atom_ideal_capacity = padToIdeal(new_block_size);
21612144
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,
21632146 // and possibly removing a free list node.
21642147 // It would be simpler to do it inside the for loop below, but that would cause a
21652148 // problem if an error was returned later in the function. So this action
21662149 // 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;
21682151 var free_list_removal: ?usize = null;
2169 var free_list = self.atom_free_lists.get(phdr_index).?;
21702152
21712153 // First we look for an appropriately sized free list node.
21722154 // The list is unordered. We'll just take the first thing that works.
21732155 const vaddr = blk: {
21742156 var i: usize = 0;
21752157 while (i < free_list.items.len) {
2176 const big_block = free_list.items[i];
2177 // We now have a pointer to a live text block that has too much capacity.
2178 // Is it enough that we could fit this new text block?
2179 const sym = self.local_symbols.items[big_block.local_sym_index];
2180 const capacity = big_block.capacity(self.*);
2158 const big_atom_index = free_list.items[i];
2159 const big_atom = self.getAtom(big_atom_index);
2160 // We now have a pointer to a live atom that has too much capacity.
2161 // Is it enough that we could fit this new atom?
2162 const big_atom_sym = big_atom.getSymbol(self);
2163 const capacity = big_atom.capacity(self);
21812164 const ideal_capacity = padToIdeal(capacity);
2182 const ideal_capacity_end_vaddr = std.math.add(u64, sym.st_value, ideal_capacity) catch ideal_capacity;
2183 const capacity_end_vaddr = sym.st_value + capacity;
2184 const new_start_vaddr_unaligned = capacity_end_vaddr - new_block_ideal_capacity;
2165 const ideal_capacity_end_vaddr = std.math.add(u64, big_atom_sym.st_value, ideal_capacity) catch ideal_capacity;
2166 const capacity_end_vaddr = big_atom_sym.st_value + capacity;
2167 const new_start_vaddr_unaligned = capacity_end_vaddr - new_atom_ideal_capacity;
21852168 const new_start_vaddr = mem.alignBackwardGeneric(u64, new_start_vaddr_unaligned, alignment);
21862169 if (new_start_vaddr < ideal_capacity_end_vaddr) {
21872170 // Additional bookkeeping here to notice if this free list node
21882171 // should be deleted because the block that it points to has grown to take up
21892172 // more of the extra capacity.
2190 if (!big_block.freeListEligible(self.*)) {
2173 if (!big_atom.freeListEligible(self)) {
21912174 _ = free_list.swapRemove(i);
21922175 } else {
21932176 i += 1;
......@@ -2201,29 +2184,33 @@ fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, al
22012184 const keep_free_list_node = remaining_capacity >= min_text_capacity;
22022185
22032186 // Set up the metadata to be updated, after errors are no longer possible.
2204 block_placement = big_block;
2187 atom_placement = big_atom_index;
22052188 if (!keep_free_list_node) {
22062189 free_list_removal = i;
22072190 }
22082191 break :blk new_start_vaddr;
2209 } else if (self.atoms.get(phdr_index)) |last| {
2210 const sym = self.local_symbols.items[last.local_sym_index];
2211 const ideal_capacity = padToIdeal(sym.st_size);
2212 const ideal_capacity_end_vaddr = sym.st_value + ideal_capacity;
2192 } else if (maybe_last_atom_index.*) |last_index| {
2193 const last = self.getAtom(last_index);
2194 const last_sym = last.getSymbol(self);
2195 const ideal_capacity = padToIdeal(last_sym.st_size);
2196 const ideal_capacity_end_vaddr = last_sym.st_value + ideal_capacity;
22132197 const new_start_vaddr = mem.alignForwardGeneric(u64, ideal_capacity_end_vaddr, alignment);
22142198 // Set up the metadata to be updated, after errors are no longer possible.
2215 block_placement = last;
2199 atom_placement = last_index;
22162200 break :blk new_start_vaddr;
22172201 } else {
22182202 break :blk phdr.p_vaddr;
22192203 }
22202204 };
22212205
2222 const expand_text_section = block_placement == null or block_placement.?.next == null;
2223 if (expand_text_section) {
2206 const expand_section = if (atom_placement) |placement_index|
2207 self.getAtom(placement_index).next_index == null
2208 else
2209 true;
2210 if (expand_section) {
22242211 const needed_size = (vaddr + new_block_size) - phdr.p_vaddr;
2225 try self.growAllocSection(shdr_index, phdr_index, needed_size);
2226 _ = try self.atoms.put(self.base.allocator, phdr_index, text_block);
2212 try self.growAllocSection(sym.st_shndx, needed_size);
2213 maybe_last_atom_index.* = atom_index;
22272214
22282215 if (self.dwarf) |_| {
22292216 // 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
22382225 }
22392226 shdr.sh_addralign = math.max(shdr.sh_addralign, alignment);
22402227
2241 // This function can also reallocate a text block.
2228 // This function can also reallocate an atom.
22422229 // In this case we need to "unplug" it from its previous location before
22432230 // plugging it in to its new location.
2244 if (text_block.prev) |prev| {
2245 prev.next = text_block.next;
2231 if (atom.prev_index) |prev_index| {
2232 const prev = self.getAtomPtr(prev_index);
2233 prev.next_index = atom.next_index;
22462234 }
2247 if (text_block.next) |next| {
2248 next.prev = text_block.prev;
2235 if (atom.next_index) |next_index| {
2236 const next = self.getAtomPtr(next_index);
2237 next.prev_index = atom.prev_index;
22492238 }
22502239
2251 if (block_placement) |big_block| {
2252 text_block.prev = big_block;
2253 text_block.next = big_block.next;
2254 big_block.next = text_block;
2240 if (atom_placement) |big_atom_index| {
2241 const big_atom = self.getAtomPtr(big_atom_index);
2242 const atom_ptr = self.getAtomPtr(atom_index);
2243 atom_ptr.prev_index = big_atom_index;
2244 atom_ptr.next_index = big_atom.next_index;
2245 big_atom.next_index = atom_index;
22552246 } else {
2256 text_block.prev = null;
2257 text_block.next = null;
2247 const atom_ptr = self.getAtomPtr(atom_index);
2248 atom_ptr.prev_index = null;
2249 atom_ptr.next_index = null;
22582250 }
22592251 if (free_list_removal) |i| {
22602252 _ = free_list.swapRemove(i);
......@@ -2262,7 +2254,7 @@ fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, al
22622254 return vaddr;
22632255}
22642256
2265fn allocateLocalSymbol(self: *Elf) !u32 {
2257pub fn allocateLocalSymbol(self: *Elf) !u32 {
22662258 try self.local_symbols.ensureUnusedCapacity(self.base.allocator, 1);
22672259
22682260 const index = blk: {
......@@ -2289,40 +2281,30 @@ fn allocateLocalSymbol(self: *Elf) !u32 {
22892281 return index;
22902282}
22912283
2292pub fn allocateDeclIndexes(self: *Elf, decl_index: Module.Decl.Index) !void {
2293 if (self.llvm_object) |_| return;
2294
2295 const mod = self.base.options.module.?;
2296 const decl = mod.declPtr(decl_index);
2297 if (decl.link.elf.local_sym_index != 0) return;
2298
2284pub fn allocateGotOffset(self: *Elf) !u32 {
22992285 try self.offset_table.ensureUnusedCapacity(self.base.allocator, 1);
2300 try self.decls.putNoClobber(self.base.allocator, decl_index, null);
23012286
2302 const decl_name = try decl.getFullyQualifiedName(mod);
2303 defer self.base.allocator.free(decl_name);
2304
2305 log.debug("allocating symbol indexes for {s}", .{decl_name});
2306 decl.link.elf.local_sym_index = try self.allocateLocalSymbol();
2307 try self.atom_by_index_table.putNoClobber(self.base.allocator, decl.link.elf.local_sym_index, &decl.link.elf);
2287 const index = blk: {
2288 if (self.offset_table_free_list.popOrNull()) |index| {
2289 log.debug(" (reusing GOT offset at index {d})", .{index});
2290 break :blk index;
2291 } else {
2292 log.debug(" (allocating GOT offset at index {d})", .{self.offset_table.items.len});
2293 const index = @intCast(u32, self.offset_table.items.len);
2294 _ = self.offset_table.addOneAssumeCapacity();
2295 self.offset_table_count_dirty = true;
2296 break :blk index;
2297 }
2298 };
23082299
2309 if (self.offset_table_free_list.popOrNull()) |i| {
2310 decl.link.elf.offset_table_index = i;
2311 } else {
2312 decl.link.elf.offset_table_index = @intCast(u32, self.offset_table.items.len);
2313 _ = self.offset_table.addOneAssumeCapacity();
2314 self.offset_table_count_dirty = true;
2315 }
2316 self.offset_table.items[decl.link.elf.offset_table_index] = 0;
2300 self.offset_table.items[index] = 0;
2301 return index;
23172302}
23182303
23192304fn freeUnnamedConsts(self: *Elf, decl_index: Module.Decl.Index) void {
23202305 const unnamed_consts = self.unnamed_const_atoms.getPtr(decl_index) orelse return;
23212306 for (unnamed_consts.items) |atom| {
2322 self.freeTextBlock(atom, self.phdr_load_ro_index.?);
2323 self.local_symbol_free_list.append(self.base.allocator, atom.local_sym_index) catch {};
2324 self.local_symbols.items[atom.local_sym_index].st_info = 0;
2325 _ = self.atom_by_index_table.remove(atom.local_sym_index);
2307 self.freeAtom(atom);
23262308 }
23272309 unnamed_consts.clearAndFree(self.base.allocator);
23282310}
......@@ -2335,52 +2317,59 @@ pub fn freeDecl(self: *Elf, decl_index: Module.Decl.Index) void {
23352317 const mod = self.base.options.module.?;
23362318 const decl = mod.declPtr(decl_index);
23372319
2338 const kv = self.decls.fetchRemove(decl_index);
2339 if (kv.?.value) |index| {
2340 self.freeTextBlock(&decl.link.elf, index);
2320 log.debug("freeDecl {*}", .{decl});
2321
2322 if (self.decls.fetchRemove(decl_index)) |const_kv| {
2323 var kv = const_kv;
2324 self.freeAtom(kv.value.atom);
23412325 self.freeUnnamedConsts(decl_index);
2326 kv.value.exports.deinit(self.base.allocator);
23422327 }
23432328
2344 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
2345 if (decl.link.elf.local_sym_index != 0) {
2346 self.local_symbol_free_list.append(self.base.allocator, decl.link.elf.local_sym_index) catch {};
2347 self.local_symbols.items[decl.link.elf.local_sym_index].st_info = 0;
2348 _ = self.atom_by_index_table.remove(decl.link.elf.local_sym_index);
2349 decl.link.elf.local_sym_index = 0;
2350
2351 self.offset_table_free_list.append(self.base.allocator, decl.link.elf.offset_table_index) catch {};
2329 if (self.dwarf) |*dw| {
2330 dw.freeDecl(decl_index);
23522331 }
2332}
23532333
2354 if (self.dwarf) |*dw| {
2355 dw.freeDecl(decl);
2334pub fn getOrCreateAtomForDecl(self: *Elf, decl_index: Module.Decl.Index) !Atom.Index {
2335 const gop = try self.decls.getOrPut(self.base.allocator, decl_index);
2336 if (!gop.found_existing) {
2337 gop.value_ptr.* = .{
2338 .atom = try self.createAtom(),
2339 .shdr = self.getDeclShdrIndex(decl_index),
2340 .exports = .{},
2341 };
23562342 }
2343 return gop.value_ptr.atom;
23572344}
23582345
2359fn getDeclPhdrIndex(self: *Elf, decl: *Module.Decl) !u16 {
2346fn getDeclShdrIndex(self: *Elf, decl_index: Module.Decl.Index) u16 {
2347 const decl = self.base.options.module.?.declPtr(decl_index);
23602348 const ty = decl.ty;
23612349 const zig_ty = ty.zigTypeTag();
23622350 const val = decl.val;
2363 const phdr_index: u16 = blk: {
2351 const shdr_index: u16 = blk: {
23642352 if (val.isUndefDeep()) {
23652353 // 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.?;
23672355 }
23682356
23692357 switch (zig_ty) {
23702358 // 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.?,
23722360 else => {
23732361 if (val.castTag(.variable)) |_| {
2374 break :blk self.phdr_load_rw_index.?;
2362 break :blk self.data_section_index.?;
23752363 }
2376 break :blk self.phdr_load_ro_index.?;
2364 break :blk self.rodata_section_index.?;
23772365 },
23782366 }
23792367 };
2380 return phdr_index;
2368 return shdr_index;
23812369}
23822370
23832371fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, stt_bits: u8) !*elf.Elf64_Sym {
2372 const gpa = self.base.allocator;
23842373 const mod = self.base.options.module.?;
23852374 const decl = mod.declPtr(decl_index);
23862375
......@@ -2390,61 +2379,65 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s
23902379 log.debug("updateDeclCode {s}{*}", .{ decl_name, decl });
23912380 const required_alignment = decl.getAlignment(self.base.options.target);
23922381
2393 const decl_ptr = self.decls.getPtr(decl_index).?;
2394 if (decl_ptr.* == null) {
2395 decl_ptr.* = try self.getDeclPhdrIndex(decl);
2396 }
2397 const phdr_index = decl_ptr.*.?;
2398 const shdr_index = self.phdr_shdr_table.get(phdr_index).?;
2382 const decl_metadata = self.decls.get(decl_index).?;
2383 const atom_index = decl_metadata.atom;
2384 const atom = self.getAtom(atom_index);
23992385
2400 assert(decl.link.elf.local_sym_index != 0); // Caller forgot to allocateDeclIndexes()
2401 const local_sym = &self.local_symbols.items[decl.link.elf.local_sym_index];
2402 if (local_sym.st_size != 0) {
2403 const capacity = decl.link.elf.capacity(self.*);
2386 const shdr_index = decl_metadata.shdr;
2387 if (atom.getSymbol(self).st_size != 0) {
2388 const local_sym = atom.getSymbolPtr(self);
2389 local_sym.st_name = try self.shstrtab.insert(gpa, decl_name);
2390 local_sym.st_info = (elf.STB_LOCAL << 4) | stt_bits;
2391 local_sym.st_other = 0;
2392 local_sym.st_shndx = shdr_index;
2393
2394 const capacity = atom.capacity(self);
24042395 const need_realloc = code.len > capacity or
24052396 !mem.isAlignedGeneric(u64, local_sym.st_value, required_alignment);
2397
24062398 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);
24082400 log.debug("growing {s} from 0x{x} to 0x{x}", .{ decl_name, local_sym.st_value, vaddr });
24092401 if (vaddr != local_sym.st_value) {
24102402 local_sym.st_value = vaddr;
24112403
24122404 log.debug(" (writing new offset table entry)", .{});
2413 self.offset_table.items[decl.link.elf.offset_table_index] = vaddr;
2414 try self.writeOffsetTableEntry(decl.link.elf.offset_table_index);
2405 self.offset_table.items[atom.offset_table_index] = vaddr;
2406 try self.writeOffsetTableEntry(atom.offset_table_index);
24152407 }
24162408 } 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);
24182410 }
24192411 local_sym.st_size = code.len;
2420 local_sym.st_name = try self.updateString(local_sym.st_name, decl_name);
2421 local_sym.st_info = (elf.STB_LOCAL << 4) | stt_bits;
2422 local_sym.st_other = 0;
2423 local_sym.st_shndx = shdr_index;
2412
24242413 // 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().?);
24262415 } else {
2427 const name_str_index = try self.makeString(decl_name);
2428 const vaddr = try self.allocateTextBlock(&decl.link.elf, code.len, required_alignment, phdr_index);
2429 errdefer self.freeTextBlock(&decl.link.elf, phdr_index);
2430 log.debug("allocated text block for {s} at 0x{x}", .{ decl_name, vaddr });
2431
2416 const local_sym = atom.getSymbolPtr(self);
24322417 local_sym.* = .{
2433 .st_name = name_str_index,
2418 .st_name = try self.shstrtab.insert(gpa, decl_name),
24342419 .st_info = (elf.STB_LOCAL << 4) | stt_bits,
24352420 .st_other = 0,
24362421 .st_shndx = shdr_index,
2437 .st_value = vaddr,
2438 .st_size = code.len,
2422 .st_value = 0,
2423 .st_size = 0,
24392424 };
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;
24412432
2442 try self.writeSymbol(decl.link.elf.local_sym_index);
2443 try self.writeOffsetTableEntry(decl.link.elf.offset_table_index);
2433 try self.writeSymbol(atom.getSymbolIndex().?);
2434 try self.writeOffsetTableEntry(atom.offset_table_index);
24442435 }
24452436
2437 const local_sym = atom.getSymbolPtr(self);
2438 const phdr_index = self.sections.items(.phdr_index)[shdr_index];
24462439 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;
24482441 try self.base.file.?.pwriteAll(code, file_offset);
24492442
24502443 return local_sym;
......@@ -2461,12 +2454,15 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven
24612454 const tracy = trace(@src());
24622455 defer tracy.end();
24632456
2464 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
2465 defer code_buffer.deinit();
2466
24672457 const decl_index = func.owner_decl;
24682458 const decl = module.declPtr(decl_index);
2459
2460 const atom_index = try self.getOrCreateAtomForDecl(decl_index);
24692461 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();
24702466
24712467 var decl_state: ?Dwarf.DeclState = if (self.dwarf) |*dw| try dw.initDeclState(module, decl_index) else null;
24722468 defer if (decl_state) |*ds| ds.deinit();
......@@ -2479,7 +2475,7 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven
24792475 try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .none);
24802476
24812477 const code = switch (res) {
2482 .appended => code_buffer.items,
2478 .ok => code_buffer.items,
24832479 .fail => |em| {
24842480 decl.analysis = .codegen_failure;
24852481 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
25252521 }
25262522 }
25272523
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);
25292527
25302528 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
25312529 defer code_buffer.deinit();
......@@ -2542,19 +2540,18 @@ pub fn updateDecl(self: *Elf, module: *Module, decl_index: Module.Decl.Index) !v
25422540 }, &code_buffer, .{
25432541 .dwarf = ds,
25442542 }, .{
2545 .parent_atom_index = decl.link.elf.local_sym_index,
2543 .parent_atom_index = atom.getSymbolIndex().?,
25462544 })
25472545 else
25482546 try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
25492547 .ty = decl.ty,
25502548 .val = decl_val,
25512549 }, &code_buffer, .none, .{
2552 .parent_atom_index = decl.link.elf.local_sym_index,
2550 .parent_atom_index = atom.getSymbolIndex().?,
25532551 });
25542552
25552553 const code = switch (res) {
2556 .externally_managed => |x| x,
2557 .appended => code_buffer.items,
2554 .ok => code_buffer.items,
25582555 .fail => |em| {
25592556 decl.analysis = .codegen_failure;
25602557 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
25792576}
25802577
25812578pub 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);
25832582 defer code_buffer.deinit();
25842583
25852584 const mod = self.base.options.module.?;
2586 const decl = mod.declPtr(decl_index);
2587
2588 const gop = try self.unnamed_const_atoms.getOrPut(self.base.allocator, decl_index);
2585 const gop = try self.unnamed_const_atoms.getOrPut(gpa, decl_index);
25892586 if (!gop.found_existing) {
25902587 gop.value_ptr.* = .{};
25912588 }
25922589 const unnamed_consts = gop.value_ptr;
25932590
2594 const atom = try self.base.allocator.create(TextBlock);
2595 errdefer self.base.allocator.destroy(atom);
2596 atom.* = TextBlock.empty;
2597 try self.managed_atoms.append(self.base.allocator, atom);
2598
2591 const decl = mod.declPtr(decl_index);
25992592 const name_str_index = blk: {
26002593 const decl_name = try decl.getFullyQualifiedName(mod);
2601 defer self.base.allocator.free(decl_name);
2602
2594 defer gpa.free(decl_name);
26032595 const index = unnamed_consts.items.len;
2604 const name = try std.fmt.allocPrint(self.base.allocator, "__unnamed_{s}_{d}", .{ decl_name, index });
2605 defer self.base.allocator.free(name);
2606
2607 break :blk try self.makeString(name);
2596 const name = try std.fmt.allocPrint(gpa, "__unnamed_{s}_{d}", .{ decl_name, index });
2597 defer gpa.free(name);
2598 break :blk try self.shstrtab.insert(gpa, name);
26082599 };
2609 const name = self.getString(name_str_index);
2600 const name = self.shstrtab.get(name_str_index).?;
26102601
2611 log.debug("allocating symbol indexes for {s}", .{name});
2612 atom.local_sym_index = try self.allocateLocalSymbol();
2613 try self.atom_by_index_table.putNoClobber(self.base.allocator, atom.local_sym_index, atom);
2602 const atom_index = try self.createAtom();
26142603
26152604 const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), typed_value, &code_buffer, .{
26162605 .none = {},
26172606 }, .{
2618 .parent_atom_index = atom.local_sym_index,
2607 .parent_atom_index = self.getAtom(atom_index).getSymbolIndex().?,
26192608 });
26202609 const code = switch (res) {
2621 .externally_managed => |x| x,
2622 .appended => code_buffer.items,
2610 .ok => code_buffer.items,
26232611 .fail => |em| {
26242612 decl.analysis = .codegen_failure;
26252613 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
26292617 };
26302618
26312619 const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);
2632 const phdr_index = self.phdr_load_ro_index.?;
2633 const shdr_index = self.phdr_shdr_table.get(phdr_index).?;
2634 const vaddr = try self.allocateTextBlock(atom, code.len, required_alignment, phdr_index);
2635 errdefer self.freeTextBlock(atom, phdr_index);
2636
2637 log.debug("allocated text block for {s} at 0x{x}", .{ name, vaddr });
2638
2639 const local_sym = &self.local_symbols.items[atom.local_sym_index];
2640 local_sym.* = .{
2641 .st_name = name_str_index,
2642 .st_info = (elf.STB_LOCAL << 4) | elf.STT_OBJECT,
2643 .st_other = 0,
2644 .st_shndx = shdr_index,
2645 .st_value = vaddr,
2646 .st_size = code.len,
2647 };
2648
2649 try self.writeSymbol(atom.local_sym_index);
2650 try unnamed_consts.append(self.base.allocator, atom);
2620 const shdr_index = self.rodata_section_index.?;
2621 const phdr_index = self.sections.items(.phdr_index)[shdr_index];
2622 const local_sym = self.getAtom(atom_index).getSymbolPtr(self);
2623 local_sym.st_name = name_str_index;
2624 local_sym.st_info = (elf.STB_LOCAL << 4) | elf.STT_OBJECT;
2625 local_sym.st_other = 0;
2626 local_sym.st_shndx = shdr_index;
2627 local_sym.st_size = code.len;
2628 local_sym.st_value = try self.allocateAtom(atom_index, code.len, required_alignment);
2629 errdefer self.freeAtom(atom_index);
2630
2631 log.debug("allocated text block for {s} at 0x{x}", .{ name, local_sym.st_value });
2632
2633 try self.writeSymbol(self.getAtom(atom_index).getSymbolIndex().?);
2634 try unnamed_consts.append(gpa, atom_index);
26512635
26522636 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;
26542638 try self.base.file.?.pwriteAll(code, file_offset);
26552639
2656 return atom.local_sym_index;
2640 return self.getAtom(atom_index).getSymbolIndex().?;
26572641}
26582642
26592643pub fn updateDeclExports(
......@@ -2672,17 +2656,16 @@ pub fn updateDeclExports(
26722656 const tracy = trace(@src());
26732657 defer tracy.end();
26742658
2675 try self.global_symbols.ensureUnusedCapacity(self.base.allocator, exports.len);
2659 const gpa = self.base.allocator;
2660
26762661 const decl = module.declPtr(decl_index);
2677 if (decl.link.elf.local_sym_index == 0) return;
2678 const decl_sym = self.local_symbols.items[decl.link.elf.local_sym_index];
2662 const atom_index = try self.getOrCreateAtomForDecl(decl_index);
2663 const atom = self.getAtom(atom_index);
2664 const decl_sym = atom.getSymbol(self);
2665 const decl_metadata = self.decls.getPtr(decl_index).?;
2666 const shdr_index = decl_metadata.shdr;
26792667
2680 const decl_ptr = self.decls.getPtr(decl_index).?;
2681 if (decl_ptr.* == null) {
2682 decl_ptr.* = try self.getDeclPhdrIndex(decl);
2683 }
2684 const phdr_index = decl_ptr.*.?;
2685 const shdr_index = self.phdr_shdr_table.get(phdr_index).?;
2668 try self.global_symbols.ensureUnusedCapacity(gpa, exports.len);
26862669
26872670 for (exports) |exp| {
26882671 if (exp.options.section) |section_name| {
......@@ -2715,10 +2698,10 @@ pub fn updateDeclExports(
27152698 },
27162699 };
27172700 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| {
27192702 const sym = &self.global_symbols.items[i];
27202703 sym.* = .{
2721 .st_name = try self.updateString(sym.st_name, exp.options.name),
2704 .st_name = try self.shstrtab.insert(gpa, exp.options.name),
27222705 .st_info = (stb_bits << 4) | stt_bits,
27232706 .st_other = 0,
27242707 .st_shndx = shdr_index,
......@@ -2726,30 +2709,29 @@ pub fn updateDeclExports(
27262709 .st_size = decl_sym.st_size,
27272710 };
27282711 } else {
2729 const name = try self.makeString(exp.options.name);
27302712 const i = if (self.global_symbol_free_list.popOrNull()) |i| i else blk: {
27312713 _ = self.global_symbols.addOneAssumeCapacity();
27322714 break :blk self.global_symbols.items.len - 1;
27332715 };
2716 try decl_metadata.exports.append(gpa, @intCast(u32, i));
27342717 self.global_symbols.items[i] = .{
2735 .st_name = name,
2718 .st_name = try self.shstrtab.insert(gpa, exp.options.name),
27362719 .st_info = (stb_bits << 4) | stt_bits,
27372720 .st_other = 0,
27382721 .st_shndx = shdr_index,
27392722 .st_value = decl_sym.st_value,
27402723 .st_size = decl_sym.st_size,
27412724 };
2742
2743 exp.link.elf.sym_index = @intCast(u32, i);
27442725 }
27452726 }
27462727}
27472728
27482729/// Must be called only after a successful call to `updateDecl`.
2749pub fn updateDeclLineNumber(self: *Elf, mod: *Module, decl: *const Module.Decl) !void {
2730pub fn updateDeclLineNumber(self: *Elf, mod: *Module, decl_index: Module.Decl.Index) !void {
27502731 const tracy = trace(@src());
27512732 defer tracy.end();
27522733
2734 const decl = mod.declPtr(decl_index);
27532735 const decl_name = try decl.getFullyQualifiedName(mod);
27542736 defer self.base.allocator.free(decl_name);
27552737
......@@ -2757,16 +2739,18 @@ pub fn updateDeclLineNumber(self: *Elf, mod: *Module, decl: *const Module.Decl)
27572739
27582740 if (self.llvm_object) |_| return;
27592741 if (self.dwarf) |*dw| {
2760 try dw.updateDeclLineNumber(decl);
2742 try dw.updateDeclLineNumber(mod, decl_index);
27612743 }
27622744}
27632745
2764pub fn deleteExport(self: *Elf, exp: Export) void {
2746pub fn deleteDeclExport(self: *Elf, decl_index: Module.Decl.Index, name: []const u8) void {
27652747 if (self.llvm_object) |_| return;
2766
2767 const sym_index = exp.sym_index orelse return;
2768 self.global_symbol_free_list.append(self.base.allocator, sym_index) catch {};
2769 self.global_symbols.items[sym_index].st_info = 0;
2748 const metadata = self.decls.getPtr(decl_index) orelse return;
2749 const sym_index = metadata.getExportPtr(self, name) orelse return;
2750 log.debug("deleting export '{s}'", .{name});
2751 self.global_symbol_free_list.append(self.base.allocator, sym_index.*) catch {};
2752 self.global_symbols.items[sym_index.*].st_info = 0;
2753 sym_index.* = 0;
27702754}
27712755
27722756fn writeProgHeader(self: *Elf, index: usize) !void {
......@@ -2795,7 +2779,7 @@ fn writeSectHeader(self: *Elf, index: usize) !void {
27952779 switch (self.ptr_width) {
27962780 .p32 => {
27972781 var shdr: [1]elf.Elf32_Shdr = undefined;
2798 shdr[0] = sectHeaderTo32(self.sections.items[index]);
2782 shdr[0] = sectHeaderTo32(self.sections.items(.shdr)[index]);
27992783 if (foreign_endian) {
28002784 mem.byteSwapAllFields(elf.Elf32_Shdr, &shdr[0]);
28012785 }
......@@ -2803,7 +2787,7 @@ fn writeSectHeader(self: *Elf, index: usize) !void {
28032787 return self.base.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);
28042788 },
28052789 .p64 => {
2806 var shdr = [1]elf.Elf64_Shdr{self.sections.items[index]};
2790 var shdr = [1]elf.Elf64_Shdr{self.sections.items(.shdr)[index]};
28072791 if (foreign_endian) {
28082792 mem.byteSwapAllFields(elf.Elf64_Shdr, &shdr[0]);
28092793 }
......@@ -2817,11 +2801,11 @@ fn writeOffsetTableEntry(self: *Elf, index: usize) !void {
28172801 const entry_size: u16 = self.archPtrWidthBytes();
28182802 if (self.offset_table_count_dirty) {
28192803 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);
28212805 self.offset_table_count_dirty = false;
28222806 }
28232807 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.?];
28252809 const off = shdr.sh_offset + @as(u64, entry_size) * index;
28262810 switch (entry_size) {
28272811 2 => {
......@@ -2847,7 +2831,7 @@ fn writeSymbol(self: *Elf, index: usize) !void {
28472831 const tracy = trace(@src());
28482832 defer tracy.end();
28492833
2850 const syms_sect = &self.sections.items[self.symtab_section_index.?];
2834 const syms_sect = &self.sections.items(.shdr)[self.symtab_section_index.?];
28512835 // Make sure we are not pointlessly writing symbol data that will have to get relocated
28522836 // due to running out of space.
28532837 if (self.local_symbols.items.len != syms_sect.sh_info) {
......@@ -2869,7 +2853,7 @@ fn writeSymbol(self: *Elf, index: usize) !void {
28692853 .p64 => syms_sect.sh_offset + @sizeOf(elf.Elf64_Sym) * index,
28702854 };
28712855 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 });
28732857 log.debug(" ({})", .{local});
28742858 switch (self.ptr_width) {
28752859 .p32 => {
......@@ -2899,7 +2883,7 @@ fn writeSymbol(self: *Elf, index: usize) !void {
28992883}
29002884
29012885fn 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.?];
29032887 const sym_size: u64 = switch (self.ptr_width) {
29042888 .p32 => @sizeOf(elf.Elf32_Sym),
29052889 .p64 => @sizeOf(elf.Elf64_Sym),
......@@ -3042,7 +3026,7 @@ fn getLDMOption(target: std.Target) ?[]const u8 {
30423026 }
30433027}
30443028
3045fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) {
3029pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) {
30463030 return actual_size +| (actual_size / ideal_factor);
30473031}
30483032
......@@ -3249,10 +3233,58 @@ const CsuObjects = struct {
32493233fn logSymtab(self: Elf) void {
32503234 log.debug("locals:", .{});
32513235 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 });
32533237 }
32543238 log.debug("globals:", .{});
32553239 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 });
32573241 }
32583242}
3243
3244pub 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
3249pub 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.
3255pub 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.
3260pub 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.
3265pub 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.
3271pub 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
3276pub 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
3281pub 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.
3288pub 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 @@
1const Atom = @This();
2
3const std = @import("std");
4const assert = std.debug.assert;
5const elf = std.elf;
6
7const 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.
15local_sym_index: u32,
16
17/// This field is undefined for symbols with size = 0.
18offset_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`.
22prev_index: ?Index,
23next_index: ?Index,
24
25pub const Index = u32;
26
27pub const Reloc = struct {
28 target: u32,
29 offset: u64,
30 addend: u32,
31 prev_vaddr: u64,
32};
33
34pub fn getSymbolIndex(self: Atom) ?u32 {
35 if (self.local_sym_index == 0) return null;
36 return self.local_sym_index;
37}
38
39pub fn getSymbol(self: Atom, elf_file: *const Elf) elf.Elf64_Sym {
40 return elf_file.getSymbol(self.getSymbolIndex().?);
41}
42
43pub fn getSymbolPtr(self: Atom, elf_file: *Elf) *elf.Elf64_Sym {
44 return elf_file.getSymbolPtr(self.getSymbolIndex().?);
45}
46
47pub fn getName(self: Atom, elf_file: *const Elf) []const u8 {
48 return elf_file.getSymbolName(self.getSymbolIndex().?);
49}
50
51pub 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.
63pub 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
75pub 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
88pub 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
97pub 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 {
6666
6767 // TODO is null here necessary, or can we do away with tracking via section
6868 // size in incremental context?
69 last_atom: ?*Atom = null,
69 last_atom_index: ?Atom.Index = null,
7070
7171 /// A list of atoms that have surplus capacity. This list can have false
7272 /// positives, as functions grow and shrink over time, only sometimes being added
......@@ -83,7 +83,7 @@ const Section = struct {
8383 /// overcapacity can be negative. A simple way to have negative overcapacity is to
8484 /// allocate a fresh atom, which will have ideal capacity, and then grow it
8585 /// by 1 byte. It will then have -1 overcapacity.
86 free_list: std.ArrayListUnmanaged(*Atom) = .{},
86 free_list: std.ArrayListUnmanaged(Atom.Index) = .{},
8787};
8888
8989base: File,
......@@ -140,8 +140,8 @@ locals_free_list: std.ArrayListUnmanaged(u32) = .{},
140140globals_free_list: std.ArrayListUnmanaged(u32) = .{},
141141
142142dyld_stub_binder_index: ?u32 = null,
143dyld_private_atom: ?*Atom = null,
144stub_helper_preamble_atom: ?*Atom = null,
143dyld_private_atom_index: ?Atom.Index = null,
144stub_helper_preamble_atom_index: ?Atom.Index = null,
145145
146146strtab: StringTable(.strtab) = .{},
147147
......@@ -164,10 +164,10 @@ segment_table_dirty: bool = false,
164164cold_start: bool = true,
165165
166166/// List of atoms that are either synthetic or map directly to the Zig source program.
167managed_atoms: std.ArrayListUnmanaged(*Atom) = .{},
167atoms: std.ArrayListUnmanaged(Atom) = .{},
168168
169169/// Table of atoms indexed by the symbol index.
170atom_by_index_table: std.AutoHashMapUnmanaged(u32, *Atom) = .{},
170atom_by_index_table: std.AutoHashMapUnmanaged(u32, Atom.Index) = .{},
171171
172172/// Table of unnamed constants associated with a parent `Decl`.
173173/// We store them here so that we can free the constants whenever the `Decl`
......@@ -210,11 +210,36 @@ bindings: BindingTable = .{},
210210/// this will be a table indexed by index into the list of Atoms.
211211lazy_bindings: BindingTable = .{},
212212
213/// Table of Decls that are currently alive.
214/// We store them here so that we can properly dispose of any allocated
215/// memory within the atom in the incremental linker.
216/// TODO consolidate this.
217decls: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, ?u8) = .{},
213/// Table of tracked Decls.
214decls: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, DeclMetadata) = .{},
215
216const DeclMetadata = struct {
217 atom: Atom.Index,
218 section: u8,
219 /// A list of all exports aliases of this Decl.
220 /// TODO do we actually need this at all?
221 exports: std.ArrayListUnmanaged(u32) = .{},
222
223 fn getExport(m: DeclMetadata, macho_file: *const MachO, name: []const u8) ?u32 {
224 for (m.exports.items) |exp| {
225 if (mem.eql(u8, name, macho_file.getSymbolName(.{
226 .sym_index = exp,
227 .file = null,
228 }))) return exp;
229 }
230 return null;
231 }
232
233 fn getExportPtr(m: *DeclMetadata, macho_file: *MachO, name: []const u8) ?*u32 {
234 for (m.exports.items) |*exp| {
235 if (mem.eql(u8, name, macho_file.getSymbolName(.{
236 .sym_index = exp.*,
237 .file = null,
238 }))) return exp;
239 }
240 return null;
241 }
242};
218243
219244const Entry = struct {
220245 target: SymbolWithLoc,
......@@ -229,8 +254,8 @@ const Entry = struct {
229254 return macho_file.getSymbolPtr(.{ .sym_index = entry.sym_index, .file = null });
230255 }
231256
232 pub fn getAtom(entry: Entry, macho_file: *MachO) ?*Atom {
233 return macho_file.getAtomForSymbol(.{ .sym_index = entry.sym_index, .file = null });
257 pub fn getAtomIndex(entry: Entry, macho_file: *MachO) ?Atom.Index {
258 return macho_file.getAtomIndexForSymbol(.{ .sym_index = entry.sym_index, .file = null });
234259 }
235260
236261 pub fn getName(entry: Entry, macho_file: *MachO) []const u8 {
......@@ -238,10 +263,10 @@ const Entry = struct {
238263 }
239264};
240265
241const BindingTable = std.AutoArrayHashMapUnmanaged(*Atom, std.ArrayListUnmanaged(Atom.Binding));
242const UnnamedConstTable = std.AutoArrayHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(*Atom));
243const RebaseTable = std.AutoArrayHashMapUnmanaged(*Atom, std.ArrayListUnmanaged(u32));
244const RelocationTable = std.AutoArrayHashMapUnmanaged(*Atom, std.ArrayListUnmanaged(Relocation));
266const BindingTable = std.AutoArrayHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(Atom.Binding));
267const UnnamedConstTable = std.AutoArrayHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(Atom.Index));
268const RebaseTable = std.AutoArrayHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(u32));
269const RelocationTable = std.AutoArrayHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(Relocation));
245270
246271const PendingUpdate = union(enum) {
247272 resolve_undef: u32,
......@@ -286,10 +311,6 @@ pub const default_pagezero_vmsize: u64 = 0x100000000;
286311/// potential future extensions.
287312pub const default_headerpad_size: u32 = 0x1000;
288313
289pub const Export = struct {
290 sym_index: ?u32 = null,
291};
292
293314pub fn openPath(allocator: Allocator, options: link.Options) !*MachO {
294315 assert(options.target.ofmt == .macho);
295316
......@@ -547,8 +568,8 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
547568
548569 try self.allocateSpecialSymbols();
549570
550 for (self.relocs.keys()) |atom| {
551 try atom.resolveRelocations(self);
571 for (self.relocs.keys()) |atom_index| {
572 try Atom.resolveRelocations(self, atom_index);
552573 }
553574
554575 if (build_options.enable_logging) {
......@@ -999,18 +1020,19 @@ pub fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs:
9991020 }
10001021}
10011022
1002pub fn writeAtom(self: *MachO, atom: *Atom, code: []const u8) !void {
1023pub fn writeAtom(self: *MachO, atom_index: Atom.Index, code: []const u8) !void {
1024 const atom = self.getAtom(atom_index);
10031025 const sym = atom.getSymbol(self);
10041026 const section = self.sections.get(sym.n_sect - 1);
10051027 const file_offset = section.header.offset + sym.n_value - section.header.addr;
10061028 log.debug("writing atom for symbol {s} at file offset 0x{x}", .{ atom.getName(self), file_offset });
10071029 try self.base.file.?.pwriteAll(code, file_offset);
1008 try atom.resolveRelocations(self);
1030 try Atom.resolveRelocations(self, atom_index);
10091031}
10101032
1011fn writePtrWidthAtom(self: *MachO, atom: *Atom) !void {
1033fn writePtrWidthAtom(self: *MachO, atom_index: Atom.Index) !void {
10121034 var buffer: [@sizeOf(u64)]u8 = [_]u8{0} ** @sizeOf(u64);
1013 try self.writeAtom(atom, &buffer);
1035 try self.writeAtom(atom_index, &buffer);
10141036}
10151037
10161038fn markRelocsDirtyByTarget(self: *MachO, target: SymbolWithLoc) void {
......@@ -1026,7 +1048,8 @@ fn markRelocsDirtyByTarget(self: *MachO, target: SymbolWithLoc) void {
10261048fn markRelocsDirtyByAddress(self: *MachO, addr: u64) void {
10271049 for (self.relocs.values()) |*relocs| {
10281050 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);
10301053 const target_sym = target_atom.getSymbol(self);
10311054 if (target_sym.n_value < addr) continue;
10321055 reloc.dirty = true;
......@@ -1053,31 +1076,38 @@ pub fn allocateSpecialSymbols(self: *MachO) !void {
10531076 }
10541077}
10551078
1056pub fn createGotAtom(self: *MachO, target: SymbolWithLoc) !*Atom {
1079pub fn createAtom(self: *MachO) !Atom.Index {
10571080 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);
10591083 const sym_index = try self.allocateSymbol();
1060 const atom = blk: {
1061 const atom = try gpa.create(Atom);
1062 atom.* = Atom.empty;
1063 atom.sym_index = sym_index;
1064 atom.size = @sizeOf(u64);
1065 atom.alignment = @alignOf(u64);
1066 break :blk atom;
1084 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom_index);
1085 atom.* = .{
1086 .sym_index = sym_index,
1087 .file = null,
1088 .size = 0,
1089 .alignment = 0,
1090 .prev_index = null,
1091 .next_index = null,
10671092 };
1068 errdefer gpa.destroy(atom);
1093 log.debug("creating ATOM(%{d}) at index {d}", .{ sym_index, atom_index });
1094 return atom_index;
1095}
10691096
1070 try self.managed_atoms.append(gpa, atom);
1071 try self.atom_by_index_table.putNoClobber(gpa, atom.sym_index, atom);
1097pub fn createGotAtom(self: *MachO, target: SymbolWithLoc) !Atom.Index {
1098 const atom_index = try self.createAtom();
1099 const atom = self.getAtomPtr(atom_index);
1100 atom.size = @sizeOf(u64);
1101 atom.alignment = @alignOf(u64);
10721102
10731103 const sym = atom.getSymbolPtr(self);
10741104 sym.n_type = macho.N_SECT;
10751105 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));
10771107
10781108 log.debug("allocated GOT atom at 0x{x}", .{sym.n_value});
10791109
1080 try atom.addRelocation(self, .{
1110 try Atom.addRelocation(self, atom_index, .{
10811111 .type = switch (self.base.options.target.cpu.arch) {
10821112 .aarch64 => @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),
10831113 .x86_64 => @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED),
......@@ -1092,50 +1122,39 @@ pub fn createGotAtom(self: *MachO, target: SymbolWithLoc) !*Atom {
10921122
10931123 const target_sym = self.getSymbol(target);
10941124 if (target_sym.undf()) {
1095 try atom.addBinding(self, .{
1125 try Atom.addBinding(self, atom_index, .{
10961126 .target = self.getGlobal(self.getSymbolName(target)).?,
10971127 .offset = 0,
10981128 });
10991129 } else {
1100 try atom.addRebase(self, 0);
1130 try Atom.addRebase(self, atom_index, 0);
11011131 }
11021132
1103 return atom;
1133 return atom_index;
11041134}
11051135
11061136pub fn createDyldPrivateAtom(self: *MachO) !void {
11071137 if (self.dyld_stub_binder_index == null) return;
1108 if (self.dyld_private_atom != null) return;
1109
1110 const gpa = self.base.allocator;
1138 if (self.dyld_private_atom_index != null) return;
11111139
1112 const sym_index = try self.allocateSymbol();
1113 const atom = blk: {
1114 const atom = try gpa.create(Atom);
1115 atom.* = Atom.empty;
1116 atom.sym_index = sym_index;
1117 atom.size = @sizeOf(u64);
1118 atom.alignment = @alignOf(u64);
1119 break :blk atom;
1120 };
1121 errdefer gpa.destroy(atom);
1140 const atom_index = try self.createAtom();
1141 const atom = self.getAtomPtr(atom_index);
1142 atom.size = @sizeOf(u64);
1143 atom.alignment = @alignOf(u64);
11221144
11231145 const sym = atom.getSymbolPtr(self);
11241146 sym.n_type = macho.N_SECT;
11251147 sym.n_sect = self.data_section_index.? + 1;
1126 self.dyld_private_atom = atom;
1148 self.dyld_private_atom_index = atom_index;
11271149
1128 try self.managed_atoms.append(gpa, atom);
1129 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
1130
1131 sym.n_value = try self.allocateAtom(atom, atom.size, @alignOf(u64));
1150 sym.n_value = try self.allocateAtom(atom_index, atom.size, @alignOf(u64));
11321151 log.debug("allocated dyld_private atom at 0x{x}", .{sym.n_value});
1133 try self.writePtrWidthAtom(atom);
1152 try self.writePtrWidthAtom(atom_index);
11341153}
11351154
11361155pub fn createStubHelperPreambleAtom(self: *MachO) !void {
11371156 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;
11391158
11401159 const gpa = self.base.allocator;
11411160 const arch = self.base.options.target.cpu.arch;
......@@ -1144,26 +1163,23 @@ pub fn createStubHelperPreambleAtom(self: *MachO) !void {
11441163 .aarch64 => 6 * @sizeOf(u32),
11451164 else => unreachable,
11461165 };
1147 const sym_index = try self.allocateSymbol();
1148 const atom = blk: {
1149 const atom = try gpa.create(Atom);
1150 atom.* = Atom.empty;
1151 atom.sym_index = sym_index;
1152 atom.size = size;
1153 atom.alignment = switch (arch) {
1154 .x86_64 => 1,
1155 .aarch64 => @alignOf(u32),
1156 else => unreachable,
1157 };
1158 break :blk atom;
1166 const atom_index = try self.createAtom();
1167 const atom = self.getAtomPtr(atom_index);
1168 atom.size = size;
1169 atom.alignment = switch (arch) {
1170 .x86_64 => 1,
1171 .aarch64 => @alignOf(u32),
1172 else => unreachable,
11591173 };
1160 errdefer gpa.destroy(atom);
11611174
11621175 const sym = atom.getSymbolPtr(self);
11631176 sym.n_type = macho.N_SECT;
11641177 sym.n_sect = self.stub_helper_section_index.? + 1;
11651178
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;
11671183
11681184 const code = try gpa.alloc(u8, size);
11691185 defer gpa.free(code);
......@@ -1182,7 +1198,7 @@ pub fn createStubHelperPreambleAtom(self: *MachO) !void {
11821198 code[9] = 0xff;
11831199 code[10] = 0x25;
11841200
1185 try atom.addRelocations(self, 2, .{ .{
1201 try Atom.addRelocations(self, atom_index, 2, .{ .{
11861202 .type = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_SIGNED),
11871203 .target = .{ .sym_index = dyld_private_sym_index, .file = null },
11881204 .offset = 3,
......@@ -1222,7 +1238,7 @@ pub fn createStubHelperPreambleAtom(self: *MachO) !void {
12221238 // br x16
12231239 mem.writeIntLittle(u32, code[20..][0..4], aarch64.Instruction.br(.x16).toU32());
12241240
1225 try atom.addRelocations(self, 4, .{ .{
1241 try Atom.addRelocations(self, atom_index, 4, .{ .{
12261242 .type = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGE21),
12271243 .target = .{ .sym_index = dyld_private_sym_index, .file = null },
12281244 .offset = 0,
......@@ -1255,17 +1271,14 @@ pub fn createStubHelperPreambleAtom(self: *MachO) !void {
12551271
12561272 else => unreachable,
12571273 }
1258 self.stub_helper_preamble_atom = atom;
1259
1260 try self.managed_atoms.append(gpa, atom);
1261 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
1274 self.stub_helper_preamble_atom_index = atom_index;
12621275
1263 sym.n_value = try self.allocateAtom(atom, size, atom.alignment);
1276 sym.n_value = try self.allocateAtom(atom_index, size, atom.alignment);
12641277 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);
12661279}
12671280
1268pub fn createStubHelperAtom(self: *MachO) !*Atom {
1281pub fn createStubHelperAtom(self: *MachO) !Atom.Index {
12691282 const gpa = self.base.allocator;
12701283 const arch = self.base.options.target.cpu.arch;
12711284 const size: u4 = switch (arch) {
......@@ -1273,20 +1286,14 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
12731286 .aarch64 => 3 * @sizeOf(u32),
12741287 else => unreachable,
12751288 };
1276 const sym_index = try self.allocateSymbol();
1277 const atom = blk: {
1278 const atom = try gpa.create(Atom);
1279 atom.* = Atom.empty;
1280 atom.sym_index = sym_index;
1281 atom.size = size;
1282 atom.alignment = switch (arch) {
1283 .x86_64 => 1,
1284 .aarch64 => @alignOf(u32),
1285 else => unreachable,
1286 };
1287 break :blk atom;
1289 const atom_index = try self.createAtom();
1290 const atom = self.getAtomPtr(atom_index);
1291 atom.size = size;
1292 atom.alignment = switch (arch) {
1293 .x86_64 => 1,
1294 .aarch64 => @alignOf(u32),
1295 else => unreachable,
12881296 };
1289 errdefer gpa.destroy(atom);
12901297
12911298 const sym = atom.getSymbolPtr(self);
12921299 sym.n_type = macho.N_SECT;
......@@ -1296,6 +1303,11 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
12961303 defer gpa.free(code);
12971304 mem.set(u8, code, 0);
12981305
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
12991311 switch (arch) {
13001312 .x86_64 => {
13011313 // pushq
......@@ -1304,9 +1316,9 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
13041316 // jmpq
13051317 code[5] = 0xe9;
13061318
1307 try atom.addRelocation(self, .{
1319 try Atom.addRelocation(self, atom_index, .{
13081320 .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 },
13101322 .offset = 6,
13111323 .addend = 0,
13121324 .pcrel = true,
......@@ -1327,9 +1339,9 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
13271339 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.b(0).toU32());
13281340 // Next 4 bytes 8..12 are just a placeholder populated in `populateLazyBindOffsetsInStubHelper`.
13291341
1330 try atom.addRelocation(self, .{
1342 try Atom.addRelocation(self, atom_index, .{
13311343 .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 },
13331345 .offset = 4,
13341346 .addend = 0,
13351347 .pcrel = true,
......@@ -1339,34 +1351,24 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
13391351 else => unreachable,
13401352 }
13411353
1342 try self.managed_atoms.append(gpa, atom);
1343 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
1344
1345 sym.n_value = try self.allocateAtom(atom, size, atom.alignment);
1354 sym.n_value = try self.allocateAtom(atom_index, size, atom.alignment);
13461355 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);
13481357
1349 return atom;
1358 return atom_index;
13501359}
13511360
1352pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, target: SymbolWithLoc) !*Atom {
1353 const gpa = self.base.allocator;
1354 const sym_index = try self.allocateSymbol();
1355 const atom = blk: {
1356 const atom = try gpa.create(Atom);
1357 atom.* = Atom.empty;
1358 atom.sym_index = sym_index;
1359 atom.size = @sizeOf(u64);
1360 atom.alignment = @alignOf(u64);
1361 break :blk atom;
1362 };
1363 errdefer gpa.destroy(atom);
1361pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, target: SymbolWithLoc) !Atom.Index {
1362 const atom_index = try self.createAtom();
1363 const atom = self.getAtomPtr(atom_index);
1364 atom.size = @sizeOf(u64);
1365 atom.alignment = @alignOf(u64);
13641366
13651367 const sym = atom.getSymbolPtr(self);
13661368 sym.n_type = macho.N_SECT;
13671369 sym.n_sect = self.la_symbol_ptr_section_index.? + 1;
13681370
1369 try atom.addRelocation(self, .{
1371 try Atom.addRelocation(self, atom_index, .{
13701372 .type = switch (self.base.options.target.cpu.arch) {
13711373 .aarch64 => @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),
13721374 .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
13781380 .pcrel = false,
13791381 .length = 3,
13801382 });
1381 try atom.addRebase(self, 0);
1382 try atom.addLazyBinding(self, .{
1383 try Atom.addRebase(self, atom_index, 0);
1384 try Atom.addLazyBinding(self, atom_index, .{
13831385 .target = self.getGlobal(self.getSymbolName(target)).?,
13841386 .offset = 0,
13851387 });
13861388
1387 try self.managed_atoms.append(gpa, atom);
1388 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
1389
1390 sym.n_value = try self.allocateAtom(atom, atom.size, @alignOf(u64));
1389 sym.n_value = try self.allocateAtom(atom_index, atom.size, @alignOf(u64));
13911390 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);
13931392
1394 return atom;
1393 return atom_index;
13951394}
13961395
1397pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
1396pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !Atom.Index {
13981397 const gpa = self.base.allocator;
13991398 const arch = self.base.options.target.cpu.arch;
14001399 const size: u4 = switch (arch) {
......@@ -1402,21 +1401,15 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
14021401 .aarch64 => 3 * @sizeOf(u32),
14031402 else => unreachable, // unhandled architecture type
14041403 };
1405 const sym_index = try self.allocateSymbol();
1406 const atom = blk: {
1407 const atom = try gpa.create(Atom);
1408 atom.* = Atom.empty;
1409 atom.sym_index = sym_index;
1410 atom.size = size;
1411 atom.alignment = switch (arch) {
1412 .x86_64 => 1,
1413 .aarch64 => @alignOf(u32),
1414 else => unreachable, // unhandled architecture type
1404 const atom_index = try self.createAtom();
1405 const atom = self.getAtomPtr(atom_index);
1406 atom.size = size;
1407 atom.alignment = switch (arch) {
1408 .x86_64 => 1,
1409 .aarch64 => @alignOf(u32),
1410 else => unreachable, // unhandled architecture type
14151411
1416 };
1417 break :blk atom;
14181412 };
1419 errdefer gpa.destroy(atom);
14201413
14211414 const sym = atom.getSymbolPtr(self);
14221415 sym.n_type = macho.N_SECT;
......@@ -1432,7 +1425,7 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
14321425 code[0] = 0xff;
14331426 code[1] = 0x25;
14341427
1435 try atom.addRelocation(self, .{
1428 try Atom.addRelocation(self, atom_index, .{
14361429 .type = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_BRANCH),
14371430 .target = .{ .sym_index = laptr_sym_index, .file = null },
14381431 .offset = 2,
......@@ -1453,7 +1446,7 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
14531446 // br x16
14541447 mem.writeIntLittle(u32, code[8..12], aarch64.Instruction.br(.x16).toU32());
14551448
1456 try atom.addRelocations(self, 2, .{
1449 try Atom.addRelocations(self, atom_index, 2, .{
14571450 .{
14581451 .type = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGE21),
14591452 .target = .{ .sym_index = laptr_sym_index, .file = null },
......@@ -1475,14 +1468,11 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
14751468 else => unreachable,
14761469 }
14771470
1478 try self.managed_atoms.append(gpa, atom);
1479 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
1480
1481 sym.n_value = try self.allocateAtom(atom, size, atom.alignment);
1471 sym.n_value = try self.allocateAtom(atom_index, size, atom.alignment);
14821472 log.debug("allocated stub atom at 0x{x}", .{sym.n_value});
1483 try self.writeAtom(atom, code);
1473 try self.writeAtom(atom_index, code);
14841474
1485 return atom;
1475 return atom_index;
14861476}
14871477
14881478pub fn createMhExecuteHeaderSymbol(self: *MachO) !void {
......@@ -1616,10 +1606,13 @@ pub fn resolveSymbolsInDylibs(self: *MachO) !void {
16161606 if (self.stubs_table.contains(global)) break :blk;
16171607
16181608 const stub_index = try self.allocateStubEntry(global);
1619 const stub_helper_atom = try self.createStubHelperAtom();
1620 const laptr_atom = try self.createLazyPointerAtom(stub_helper_atom.sym_index, global);
1621 const stub_atom = try self.createStubAtom(laptr_atom.sym_index);
1622 self.stubs.items[stub_index].sym_index = stub_atom.sym_index;
1609 const stub_helper_atom_index = try self.createStubHelperAtom();
1610 const stub_helper_atom = self.getAtom(stub_helper_atom_index);
1611 const laptr_atom_index = try self.createLazyPointerAtom(stub_helper_atom.getSymbolIndex().?, global);
1612 const laptr_atom = self.getAtom(laptr_atom_index);
1613 const stub_atom_index = try self.createStubAtom(laptr_atom.getSymbolIndex().?);
1614 const stub_atom = self.getAtom(stub_atom_index);
1615 self.stubs.items[stub_index].sym_index = stub_atom.getSymbolIndex().?;
16231616 self.markRelocsDirtyByTarget(global);
16241617 }
16251618
......@@ -1716,10 +1709,11 @@ pub fn resolveDyldStubBinder(self: *MachO) !void {
17161709
17171710 // Add dyld_stub_binder as the final GOT entry.
17181711 const got_index = try self.allocateGotEntry(global);
1719 const got_atom = try self.createGotAtom(global);
1720 self.got_entries.items[got_index].sym_index = got_atom.sym_index;
1712 const got_atom_index = try self.createGotAtom(global);
1713 const got_atom = self.getAtom(got_atom_index);
1714 self.got_entries.items[got_index].sym_index = got_atom.getSymbolIndex().?;
17211715
1722 try self.writePtrWidthAtom(got_atom);
1716 try self.writePtrWidthAtom(got_atom_index);
17231717}
17241718
17251719pub fn deinit(self: *MachO) void {
......@@ -1769,12 +1763,12 @@ pub fn deinit(self: *MachO) void {
17691763 }
17701764 self.sections.deinit(gpa);
17711765
1772 for (self.managed_atoms.items) |atom| {
1773 gpa.destroy(atom);
1774 }
1775 self.managed_atoms.deinit(gpa);
1766 self.atoms.deinit(gpa);
17761767
17771768 if (self.base.options.module) |_| {
1769 for (self.decls.values()) |*m| {
1770 m.exports.deinit(gpa);
1771 }
17781772 self.decls.deinit(gpa);
17791773 } else {
17801774 assert(self.decls.count() == 0);
......@@ -1808,12 +1802,14 @@ pub fn deinit(self: *MachO) void {
18081802 self.lazy_bindings.deinit(gpa);
18091803}
18101804
1811fn freeAtom(self: *MachO, atom: *Atom) void {
1812 log.debug("freeAtom {*}", .{atom});
1805fn freeAtom(self: *MachO, atom_index: Atom.Index) void {
1806 const gpa = self.base.allocator;
1807 log.debug("freeAtom {d}", .{atom_index});
18131808
18141809 // Remove any relocs and base relocs associated with this Atom
1815 self.freeRelocationsForAtom(atom);
1810 Atom.freeRelocations(self, atom_index);
18161811
1812 const atom = self.getAtom(atom_index);
18171813 const sect_id = atom.getSymbol(self).n_sect - 1;
18181814 const free_list = &self.sections.items(.free_list)[sect_id];
18191815 var already_have_free_list_node = false;
......@@ -1821,69 +1817,94 @@ fn freeAtom(self: *MachO, atom: *Atom) void {
18211817 var i: usize = 0;
18221818 // TODO turn free_list into a hash map
18231819 while (i < free_list.items.len) {
1824 if (free_list.items[i] == atom) {
1820 if (free_list.items[i] == atom_index) {
18251821 _ = free_list.swapRemove(i);
18261822 continue;
18271823 }
1828 if (free_list.items[i] == atom.prev) {
1824 if (free_list.items[i] == atom.prev_index) {
18291825 already_have_free_list_node = true;
18301826 }
18311827 i += 1;
18321828 }
18331829 }
18341830
1835 const maybe_last_atom = &self.sections.items(.last_atom)[sect_id];
1836 if (maybe_last_atom.*) |last_atom| {
1837 if (last_atom == atom) {
1838 if (atom.prev) |prev| {
1831 const maybe_last_atom_index = &self.sections.items(.last_atom_index)[sect_id];
1832 if (maybe_last_atom_index.*) |last_atom_index| {
1833 if (last_atom_index == atom_index) {
1834 if (atom.prev_index) |prev_index| {
18391835 // TODO shrink the section size here
1840 maybe_last_atom.* = prev;
1836 maybe_last_atom_index.* = prev_index;
18411837 } else {
1842 maybe_last_atom.* = null;
1838 maybe_last_atom_index.* = null;
18431839 }
18441840 }
18451841 }
18461842
1847 if (atom.prev) |prev| {
1848 prev.next = atom.next;
1843 if (atom.prev_index) |prev_index| {
1844 const prev = self.getAtomPtr(prev_index);
1845 prev.next_index = atom.next_index;
18491846
1850 if (!already_have_free_list_node and prev.freeListEligible(self)) {
1847 if (!already_have_free_list_node and prev.*.freeListEligible(self)) {
18511848 // The free list is heuristics, it doesn't have to be perfect, so we can ignore
18521849 // the OOM here.
1853 free_list.append(self.base.allocator, prev) catch {};
1850 free_list.append(gpa, prev_index) catch {};
18541851 }
18551852 } else {
1856 atom.prev = null;
1853 self.getAtomPtr(atom_index).prev_index = null;
18571854 }
18581855
1859 if (atom.next) |next| {
1860 next.prev = atom.prev;
1856 if (atom.next_index) |next_index| {
1857 self.getAtomPtr(next_index).prev_index = atom.prev_index;
18611858 } else {
1862 atom.next = null;
1859 self.getAtomPtr(atom_index).next_index = null;
18631860 }
18641861
1865 if (self.d_sym) |*d_sym| {
1866 d_sym.dwarf.freeAtom(&atom.dbg_info_atom);
1862 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
1863 const sym_index = atom.getSymbolIndex().?;
1864
1865 self.locals_free_list.append(gpa, sym_index) catch {};
1866
1867 // Try freeing GOT atom if this decl had one
1868 const got_target = SymbolWithLoc{ .sym_index = sym_index, .file = null };
1869 if (self.got_entries_table.get(got_target)) |got_index| {
1870 self.got_entries_free_list.append(gpa, @intCast(u32, got_index)) catch {};
1871 self.got_entries.items[got_index] = .{
1872 .target = .{ .sym_index = 0, .file = null },
1873 .sym_index = 0,
1874 };
1875 _ = self.got_entries_table.remove(got_target);
1876
1877 if (self.d_sym) |*d_sym| {
1878 d_sym.swapRemoveRelocs(sym_index);
1879 }
1880
1881 log.debug(" adding GOT index {d} to free list (target local@{d})", .{ got_index, sym_index });
18671882 }
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;
18681888}
18691889
1870fn shrinkAtom(self: *MachO, atom: *Atom, new_block_size: u64) void {
1890fn shrinkAtom(self: *MachO, atom_index: Atom.Index, new_block_size: u64) void {
18711891 _ = self;
1872 _ = atom;
1892 _ = atom_index;
18731893 _ = new_block_size;
18741894 // TODO check the new capacity, and if it crosses the size threshold into a big enough
18751895 // capacity, insert a free list node for it.
18761896}
18771897
1878fn growAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) !u64 {
1898fn growAtom(self: *MachO, atom_index: Atom.Index, new_atom_size: u64, alignment: u64) !u64 {
1899 const atom = self.getAtom(atom_index);
18791900 const sym = atom.getSymbol(self);
18801901 const align_ok = mem.alignBackwardGeneric(u64, sym.n_value, alignment) == sym.n_value;
18811902 const need_realloc = !align_ok or new_atom_size > atom.capacity(self);
18821903 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);
18841905}
18851906
1886fn allocateSymbol(self: *MachO) !u32 {
1907pub fn allocateSymbol(self: *MachO) !u32 {
18871908 try self.locals.ensureUnusedCapacity(self.base.allocator, 1);
18881909
18891910 const index = blk: {
......@@ -1975,16 +1996,6 @@ pub fn allocateStubEntry(self: *MachO, target: SymbolWithLoc) !u32 {
19751996 return index;
19761997}
19771998
1978pub 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
19881999pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void {
19892000 if (build_options.skip_non_native and builtin.object_format != .macho) {
19902001 @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
19972008
19982009 const decl_index = func.owner_decl;
19992010 const decl = module.declPtr(decl_index);
2011
2012 const atom_index = try self.getOrCreateAtomForDecl(decl_index);
20002013 self.freeUnnamedConsts(decl_index);
2001 self.freeRelocationsForAtom(&decl.link.macho);
2014 Atom.freeRelocations(self, atom_index);
2015
2016 const atom = self.getAtom(atom_index);
20022017
20032018 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
20042019 defer code_buffer.deinit();
......@@ -2017,7 +2032,7 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv
20172032 try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .none);
20182033
20192034 const code = switch (res) {
2020 .appended => code_buffer.items,
2035 .ok => code_buffer.items,
20212036 .fail => |em| {
20222037 decl.analysis = .codegen_failure;
20232038 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
20282043 const addr = try self.updateDeclCode(decl_index, code);
20292044
20302045 if (decl_state) |*ds| {
2031 try self.d_sym.?.dwarf.commitDeclState(
2032 module,
2033 decl_index,
2034 addr,
2035 decl.link.macho.size,
2036 ds,
2037 );
2046 try self.d_sym.?.dwarf.commitDeclState(module, decl_index, addr, atom.size, ds);
20382047 }
20392048
20402049 // 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
20692078
20702079 log.debug("allocating symbol indexes for {?s}", .{name});
20712080
2072 const atom = try gpa.create(Atom);
2073 errdefer gpa.destroy(atom);
2074 atom.* = Atom.empty;
2075
2076 atom.sym_index = try self.allocateSymbol();
2077
2078 try self.managed_atoms.append(gpa, atom);
2079 try self.atom_by_index_table.putNoClobber(gpa, atom.sym_index, atom);
2081 const atom_index = try self.createAtom();
20802082
20812083 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().?,
20832085 });
20842086 const code = switch (res) {
2085 .externally_managed => |x| x,
2086 .appended => code_buffer.items,
2087 .ok => code_buffer.items,
20872088 .fail => |em| {
20882089 decl.analysis = .codegen_failure;
20892090 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
20932094 };
20942095
20952096 const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);
2097 const atom = self.getAtomPtr(atom_index);
20962098 atom.size = code.len;
20972099 atom.alignment = required_alignment;
20982100 // 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);
21002102 const sect_id = self.data_const_section_index.?;
21012103 const symbol = atom.getSymbolPtr(self);
21022104 symbol.n_strx = name_str_index;
21032105 symbol.n_type = macho.N_SECT;
21042106 symbol.n_sect = sect_id + 1;
2105 symbol.n_value = try self.allocateAtom(atom, code.len, required_alignment);
2106 errdefer self.freeAtom(atom);
2107 symbol.n_value = try self.allocateAtom(atom_index, code.len, required_alignment);
2108 errdefer self.freeAtom(atom_index);
21072109
2108 try unnamed_consts.append(gpa, atom);
2110 try unnamed_consts.append(gpa, atom_index);
21092111
21102112 log.debug("allocated atom for {?s} at 0x{x}", .{ name, symbol.n_value });
21112113 log.debug(" (required alignment 0x{x})", .{required_alignment});
21122114
2113 try self.writeAtom(atom, code);
2115 try self.writeAtom(atom_index, code);
21142116
2115 return atom.sym_index;
2117 return atom.getSymbolIndex().?;
21162118}
21172119
21182120pub 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)
21372139 }
21382140 }
21392141
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);
21412145
21422146 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
21432147 defer code_buffer.deinit();
......@@ -2156,19 +2160,18 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index)
21562160 }, &code_buffer, .{
21572161 .dwarf = ds,
21582162 }, .{
2159 .parent_atom_index = decl.link.macho.sym_index,
2163 .parent_atom_index = atom.getSymbolIndex().?,
21602164 })
21612165 else
21622166 try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
21632167 .ty = decl.ty,
21642168 .val = decl_val,
21652169 }, &code_buffer, .none, .{
2166 .parent_atom_index = decl.link.macho.sym_index,
2170 .parent_atom_index = atom.getSymbolIndex().?,
21672171 });
21682172
21692173 const code = switch (res) {
2170 .externally_managed => |x| x,
2171 .appended => code_buffer.items,
2174 .ok => code_buffer.items,
21722175 .fail => |em| {
21732176 decl.analysis = .codegen_failure;
21742177 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)
21782181 const addr = try self.updateDeclCode(decl_index, code);
21792182
21802183 if (decl_state) |*ds| {
2181 try self.d_sym.?.dwarf.commitDeclState(
2182 module,
2183 decl_index,
2184 addr,
2185 decl.link.macho.size,
2186 ds,
2187 );
2184 try self.d_sym.?.dwarf.commitDeclState(module, decl_index, addr, atom.size, ds);
21882185 }
21892186
21902187 // 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)
21922189 try self.updateDeclExports(module, decl_index, module.getDeclExports(decl_index));
21932190}
21942191
2195fn getDeclOutputSection(self: *MachO, decl: *Module.Decl) u8 {
2192pub 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
2204fn getDeclOutputSection(self: *MachO, decl_index: Module.Decl.Index) u8 {
2205 const decl = self.base.options.module.?.declPtr(decl_index);
21962206 const ty = decl.ty;
21972207 const val = decl.val;
21982208 const zig_ty = ty.zigTypeTag();
......@@ -2339,17 +2349,15 @@ fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []const u8)
23392349 const decl = mod.declPtr(decl_index);
23402350
23412351 const required_alignment = decl.getAlignment(self.base.options.target);
2342 assert(decl.link.macho.sym_index != 0); // Caller forgot to call allocateDeclIndexes()
23432352
23442353 const sym_name = try decl.getFullyQualifiedName(mod);
23452354 defer self.base.allocator.free(sym_name);
23462355
2347 const atom = &decl.link.macho;
2348 const decl_ptr = self.decls.getPtr(decl_index).?;
2349 if (decl_ptr.* == null) {
2350 decl_ptr.* = self.getDeclOutputSection(decl);
2351 }
2352 const sect_id = decl_ptr.*.?;
2356 const decl_metadata = self.decls.get(decl_index).?;
2357 const atom_index = decl_metadata.atom;
2358 const atom = self.getAtom(atom_index);
2359 const sym_index = atom.getSymbolIndex().?;
2360 const sect_id = decl_metadata.section;
23532361 const code_len = code.len;
23542362
23552363 if (atom.size != 0) {
......@@ -2359,31 +2367,31 @@ fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []const u8)
23592367 sym.n_sect = sect_id + 1;
23602368 sym.n_desc = 0;
23612369
2362 const capacity = decl.link.macho.capacity(self);
2370 const capacity = atom.capacity(self);
23632371 const need_realloc = code_len > capacity or !mem.isAlignedGeneric(u64, sym.n_value, required_alignment);
23642372
23652373 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);
23672375 log.debug("growing {s} and moving from 0x{x} to 0x{x}", .{ sym_name, sym.n_value, vaddr });
23682376 log.debug(" (required alignment 0x{x})", .{required_alignment});
23692377
23702378 if (vaddr != sym.n_value) {
23712379 sym.n_value = vaddr;
23722380 log.debug(" (updating GOT entry)", .{});
2373 const got_target = SymbolWithLoc{ .sym_index = atom.sym_index, .file = null };
2374 const got_atom = self.getGotAtomForSymbol(got_target).?;
2381 const got_target = SymbolWithLoc{ .sym_index = sym_index, .file = null };
2382 const got_atom_index = self.getGotAtomIndexForSymbol(got_target).?;
23752383 self.markRelocsDirtyByTarget(got_target);
2376 try self.writePtrWidthAtom(got_atom);
2384 try self.writePtrWidthAtom(got_atom_index);
23772385 }
23782386 } else if (code_len < atom.size) {
2379 self.shrinkAtom(atom, code_len);
2380 } else if (atom.next == null) {
2387 self.shrinkAtom(atom_index, code_len);
2388 } else if (atom.next_index == null) {
23812389 const header = &self.sections.items(.header)[sect_id];
23822390 const segment = self.getSegment(sect_id);
23832391 const needed_size = (sym.n_value + code_len) - segment.vmaddr;
23842392 header.size = needed_size;
23852393 }
2386 atom.size = code_len;
2394 self.getAtomPtr(atom_index).size = code_len;
23872395 } else {
23882396 const name_str_index = try self.strtab.insert(gpa, sym_name);
23892397 const sym = atom.getSymbolPtr(self);
......@@ -2392,32 +2400,32 @@ fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []const u8)
23922400 sym.n_sect = sect_id + 1;
23932401 sym.n_desc = 0;
23942402
2395 const vaddr = try self.allocateAtom(atom, code_len, required_alignment);
2396 errdefer self.freeAtom(atom);
2403 const vaddr = try self.allocateAtom(atom_index, code_len, required_alignment);
2404 errdefer self.freeAtom(atom_index);
23972405
23982406 log.debug("allocated atom for {s} at 0x{x}", .{ sym_name, vaddr });
23992407 log.debug(" (required alignment 0x{x})", .{required_alignment});
24002408
2401 atom.size = code_len;
2409 self.getAtomPtr(atom_index).size = code_len;
24022410 sym.n_value = vaddr;
24032411
2404 const got_target = SymbolWithLoc{ .sym_index = atom.sym_index, .file = null };
2412 const got_target = SymbolWithLoc{ .sym_index = sym_index, .file = null };
24052413 const got_index = try self.allocateGotEntry(got_target);
2406 const got_atom = try self.createGotAtom(got_target);
2407 self.got_entries.items[got_index].sym_index = got_atom.sym_index;
2408 try self.writePtrWidthAtom(got_atom);
2414 const got_atom_index = try self.createGotAtom(got_target);
2415 const got_atom = self.getAtom(got_atom_index);
2416 self.got_entries.items[got_index].sym_index = got_atom.getSymbolIndex().?;
2417 try self.writePtrWidthAtom(got_atom_index);
24092418 }
24102419
24112420 self.markRelocsDirtyByTarget(atom.getSymbolWithLoc());
2412 try self.writeAtom(atom, code);
2421 try self.writeAtom(atom_index, code);
24132422
24142423 return atom.getSymbol(self).n_value;
24152424}
24162425
2417pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl: *const Module.Decl) !void {
2418 _ = module;
2426pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl_index: Module.Decl.Index) !void {
24192427 if (self.d_sym) |*d_sym| {
2420 try d_sym.dwarf.updateDeclLineNumber(decl);
2428 try d_sym.dwarf.updateDeclLineNumber(module, decl_index);
24212429 }
24222430}
24232431
......@@ -2434,14 +2442,17 @@ pub fn updateDeclExports(
24342442 if (self.llvm_object) |llvm_object|
24352443 return llvm_object.updateDeclExports(module, decl_index, exports);
24362444 }
2445
24372446 const tracy = trace(@src());
24382447 defer tracy.end();
24392448
24402449 const gpa = self.base.allocator;
24412450
24422451 const decl = module.declPtr(decl_index);
2443 if (decl.link.macho.sym_index == 0) return;
2444 const decl_sym = decl.link.macho.getSymbol(self);
2452 const atom_index = try self.getOrCreateAtomForDecl(decl_index);
2453 const atom = self.getAtom(atom_index);
2454 const decl_sym = atom.getSymbol(self);
2455 const decl_metadata = self.decls.getPtr(decl_index).?;
24452456
24462457 for (exports) |exp| {
24472458 const exp_name = try std.fmt.allocPrint(gpa, "_{s}", .{exp.options.name});
......@@ -2479,9 +2490,9 @@ pub fn updateDeclExports(
24792490 continue;
24802491 }
24812492
2482 const sym_index = exp.link.macho.sym_index orelse blk: {
2493 const sym_index = decl_metadata.getExport(self, exp_name) orelse blk: {
24832494 const sym_index = try self.allocateSymbol();
2484 exp.link.macho.sym_index = sym_index;
2495 try decl_metadata.exports.append(gpa, sym_index);
24852496 break :blk sym_index;
24862497 };
24872498 const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = null };
......@@ -2529,16 +2540,18 @@ pub fn updateDeclExports(
25292540 }
25302541}
25312542
2532pub fn deleteExport(self: *MachO, exp: Export) void {
2543pub fn deleteDeclExport(self: *MachO, decl_index: Module.Decl.Index, name: []const u8) Allocator.Error!void {
25332544 if (self.llvm_object) |_| return;
2534 const sym_index = exp.sym_index orelse return;
2545 const metadata = self.decls.getPtr(decl_index) orelse return;
25352546
25362547 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;
25372551
2538 const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = null };
2552 const sym_loc = SymbolWithLoc{ .sym_index = sym_index.*, .file = null };
25392553 const sym = self.getSymbolPtr(sym_loc);
2540 const sym_name = self.getSymbolName(sym_loc);
2541 log.debug("deleting export '{s}'", .{sym_name});
2554 log.debug("deleting export '{s}'", .{exp_name});
25422555 assert(sym.sect() and sym.ext());
25432556 sym.* = .{
25442557 .n_strx = 0,
......@@ -2547,9 +2560,9 @@ pub fn deleteExport(self: *MachO, exp: Export) void {
25472560 .n_desc = 0,
25482561 .n_value = 0,
25492562 };
2550 self.locals_free_list.append(gpa, sym_index) catch {};
2563 self.locals_free_list.append(gpa, sym_index.*) catch {};
25512564
2552 if (self.resolver.fetchRemove(sym_name)) |entry| {
2565 if (self.resolver.fetchRemove(exp_name)) |entry| {
25532566 defer gpa.free(entry.key);
25542567 self.globals_free_list.append(gpa, entry.value) catch {};
25552568 self.globals.items[entry.value] = .{
......@@ -2557,17 +2570,8 @@ pub fn deleteExport(self: *MachO, exp: Export) void {
25572570 .file = null,
25582571 };
25592572 }
2560}
25612573
2562fn freeRelocationsForAtom(self: *MachO, atom: *Atom) void {
2563 var removed_relocs = self.relocs.fetchOrderedRemove(atom);
2564 if (removed_relocs) |*relocs| relocs.value.deinit(self.base.allocator);
2565 var removed_rebases = self.rebases.fetchOrderedRemove(atom);
2566 if (removed_rebases) |*rebases| rebases.value.deinit(self.base.allocator);
2567 var removed_bindings = self.bindings.fetchOrderedRemove(atom);
2568 if (removed_bindings) |*bindings| bindings.value.deinit(self.base.allocator);
2569 var removed_lazy_bindings = self.lazy_bindings.fetchOrderedRemove(atom);
2570 if (removed_lazy_bindings) |*lazy_bindings| lazy_bindings.value.deinit(self.base.allocator);
2574 sym_index.* = 0;
25712575}
25722576
25732577fn freeUnnamedConsts(self: *MachO, decl_index: Module.Decl.Index) void {
......@@ -2575,11 +2579,6 @@ fn freeUnnamedConsts(self: *MachO, decl_index: Module.Decl.Index) void {
25752579 const unnamed_consts = self.unnamed_const_atoms.getPtr(decl_index) orelse return;
25762580 for (unnamed_consts.items) |atom| {
25772581 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;
25832582 }
25842583 unnamed_consts.clearAndFree(gpa);
25852584}
......@@ -2593,67 +2592,37 @@ pub fn freeDecl(self: *MachO, decl_index: Module.Decl.Index) void {
25932592
25942593 log.debug("freeDecl {*}", .{decl});
25952594
2596 const kv = self.decls.fetchSwapRemove(decl_index);
2597 if (kv.?.value) |_| {
2598 self.freeAtom(&decl.link.macho);
2595 if (self.decls.fetchSwapRemove(decl_index)) |const_kv| {
2596 var kv = const_kv;
2597 self.freeAtom(kv.value.atom);
25992598 self.freeUnnamedConsts(decl_index);
2600 }
2601
2602 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
2603 const gpa = self.base.allocator;
2604 const sym_index = decl.link.macho.sym_index;
2605 if (sym_index != 0) {
2606 self.locals_free_list.append(gpa, sym_index) catch {};
2607
2608 // Try freeing GOT atom if this decl had one
2609 const got_target = SymbolWithLoc{ .sym_index = sym_index, .file = null };
2610 if (self.got_entries_table.get(got_target)) |got_index| {
2611 self.got_entries_free_list.append(gpa, @intCast(u32, got_index)) catch {};
2612 self.got_entries.items[got_index] = .{
2613 .target = .{ .sym_index = 0, .file = null },
2614 .sym_index = 0,
2615 };
2616 _ = self.got_entries_table.remove(got_target);
2617
2618 if (self.d_sym) |*d_sym| {
2619 d_sym.swapRemoveRelocs(sym_index);
2620 }
2621
2622 log.debug(" adding GOT index {d} to free list (target local@{d})", .{ got_index, sym_index });
2623 }
2624
2625 self.locals.items[sym_index].n_type = 0;
2626 _ = self.atom_by_index_table.remove(sym_index);
2627 log.debug(" adding local symbol index {d} to free list", .{sym_index});
2628 decl.link.macho.sym_index = 0;
2599 kv.value.exports.deinit(self.base.allocator);
26292600 }
26302601
26312602 if (self.d_sym) |*d_sym| {
2632 d_sym.dwarf.freeDecl(decl);
2603 d_sym.dwarf.freeDecl(decl_index);
26332604 }
26342605}
26352606
26362607pub 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
26402608 assert(self.llvm_object == null);
2641 assert(decl.link.macho.sym_index != 0);
26422609
2643 const atom = self.getAtomForSymbol(.{ .sym_index = reloc_info.parent_atom_index, .file = null }).?;
2644 try atom.addRelocation(self, .{
2610 const this_atom_index = try self.getOrCreateAtomForDecl(decl_index);
2611 const sym_index = self.getAtom(this_atom_index).getSymbolIndex().?;
2612 const atom_index = self.getAtomIndexForSymbol(.{ .sym_index = reloc_info.parent_atom_index, .file = null }).?;
2613 try Atom.addRelocation(self, atom_index, .{
26452614 .type = switch (self.base.options.target.cpu.arch) {
26462615 .aarch64 => @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),
26472616 .x86_64 => @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED),
26482617 else => unreachable,
26492618 },
2650 .target = .{ .sym_index = decl.link.macho.sym_index, .file = null },
2619 .target = .{ .sym_index = sym_index, .file = null },
26512620 .offset = @intCast(u32, reloc_info.offset),
26522621 .addend = reloc_info.addend,
26532622 .pcrel = false,
26542623 .length = 3,
26552624 });
2656 try atom.addRebase(self, @intCast(u32, reloc_info.offset));
2625 try Atom.addRebase(self, atom_index, @intCast(u32, reloc_info.offset));
26572626
26582627 return 0;
26592628}
......@@ -2885,34 +2854,36 @@ fn moveSectionInVirtualMemory(self: *MachO, sect_id: u8, needed_size: u64) !void
28852854 // TODO: enforce order by increasing VM addresses in self.sections container.
28862855 for (self.sections.items(.header)[sect_id + 1 ..]) |*next_header, next_sect_id| {
28872856 const index = @intCast(u8, sect_id + 1 + next_sect_id);
2888 const maybe_last_atom = &self.sections.items(.last_atom)[index];
28892857 const next_segment = self.getSegmentPtr(index);
28902858 next_header.addr += diff;
28912859 next_segment.vmaddr += diff;
28922860
2893 if (maybe_last_atom.*) |last_atom| {
2894 var atom = last_atom;
2861 const maybe_last_atom_index = &self.sections.items(.last_atom_index)[index];
2862 if (maybe_last_atom_index.*) |last_atom_index| {
2863 var atom_index = last_atom_index;
28952864 while (true) {
2865 const atom = self.getAtom(atom_index);
28962866 const sym = atom.getSymbolPtr(self);
28972867 sym.n_value += diff;
28982868
2899 if (atom.prev) |prev| {
2900 atom = prev;
2869 if (atom.prev_index) |prev_index| {
2870 atom_index = prev_index;
29012871 } else break;
29022872 }
29032873 }
29042874 }
29052875}
29062876
2907fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) !u64 {
2877fn allocateAtom(self: *MachO, atom_index: Atom.Index, new_atom_size: u64, alignment: u64) !u64 {
29082878 const tracy = trace(@src());
29092879 defer tracy.end();
29102880
2881 const atom = self.getAtom(atom_index);
29112882 const sect_id = atom.getSymbol(self).n_sect - 1;
29122883 const segment = self.getSegmentPtr(sect_id);
29132884 const header = &self.sections.items(.header)[sect_id];
29142885 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];
29162887 const requires_padding = blk: {
29172888 if (!header.isCode()) break :blk false;
29182889 if (header.isSymbolStubs()) break :blk false;
......@@ -2926,7 +2897,7 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) !
29262897 // It would be simpler to do it inside the for loop below, but that would cause a
29272898 // problem if an error was returned later in the function. So this action
29282899 // 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;
29302901 var free_list_removal: ?usize = null;
29312902
29322903 // 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) !
29342905 var vaddr = blk: {
29352906 var i: usize = 0;
29362907 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);
29382910 // We now have a pointer to a live atom that has too much capacity.
29392911 // Is it enough that we could fit this new atom?
29402912 const sym = big_atom.getSymbol(self);
......@@ -2962,30 +2934,35 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) !
29622934 const keep_free_list_node = remaining_capacity >= min_text_capacity;
29632935
29642936 // Set up the metadata to be updated, after errors are no longer possible.
2965 atom_placement = big_atom;
2937 atom_placement = big_atom_index;
29662938 if (!keep_free_list_node) {
29672939 free_list_removal = i;
29682940 }
29692941 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);
29712944 const last_symbol = last.getSymbol(self);
29722945 const ideal_capacity = if (requires_padding) padToIdeal(last.size) else last.size;
29732946 const ideal_capacity_end_vaddr = last_symbol.n_value + ideal_capacity;
29742947 const new_start_vaddr = mem.alignForwardGeneric(u64, ideal_capacity_end_vaddr, alignment);
2975 atom_placement = last;
2948 atom_placement = last_index;
29762949 break :blk new_start_vaddr;
29772950 } else {
29782951 break :blk mem.alignForwardGeneric(u64, segment.vmaddr, alignment);
29792952 }
29802953 };
29812954
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;
29832959 if (expand_section) {
29842960 const sect_capacity = self.allocatedSize(header.offset);
29852961 const needed_size = (vaddr + new_atom_size) - segment.vmaddr;
29862962 if (needed_size > sect_capacity) {
29872963 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);
29892966 const sym = last_atom.getSymbol(self);
29902967 break :blk (sym.n_value + last_atom.size) - segment.vmaddr;
29912968 } else 0;
......@@ -3017,7 +2994,7 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) !
30172994 header.size = needed_size;
30182995 segment.filesize = mem.alignForwardGeneric(u64, needed_size, self.page_size);
30192996 segment.vmsize = mem.alignForwardGeneric(u64, needed_size, self.page_size);
3020 maybe_last_atom.* = atom;
2997 maybe_last_atom_index.* = atom_index;
30212998
30222999 self.segment_table_dirty = true;
30233000 }
......@@ -3026,21 +3003,31 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) !
30263003 if (header.@"align" < align_pow) {
30273004 header.@"align" = align_pow;
30283005 }
3006 {
3007 const atom_ptr = self.getAtomPtr(atom_index);
3008 atom_ptr.size = new_atom_size;
3009 atom_ptr.alignment = @intCast(u32, alignment);
3010 }
30293011
3030 if (atom.prev) |prev| {
3031 prev.next = atom.next;
3012 if (atom.prev_index) |prev_index| {
3013 const prev = self.getAtomPtr(prev_index);
3014 prev.next_index = atom.next_index;
30323015 }
3033 if (atom.next) |next| {
3034 next.prev = atom.prev;
3016 if (atom.next_index) |next_index| {
3017 const next = self.getAtomPtr(next_index);
3018 next.prev_index = atom.prev_index;
30353019 }
30363020
3037 if (atom_placement) |big_atom| {
3038 atom.prev = big_atom;
3039 atom.next = big_atom.next;
3040 big_atom.next = atom;
3021 if (atom_placement) |big_atom_index| {
3022 const big_atom = self.getAtomPtr(big_atom_index);
3023 const atom_ptr = self.getAtomPtr(atom_index);
3024 atom_ptr.prev_index = big_atom_index;
3025 atom_ptr.next_index = big_atom.next_index;
3026 big_atom.next_index = atom_index;
30413027 } else {
3042 atom.prev = null;
3043 atom.next = null;
3028 const atom_ptr = self.getAtomPtr(atom_index);
3029 atom_ptr.prev_index = null;
3030 atom_ptr.next_index = null;
30443031 }
30453032 if (free_list_removal) |i| {
30463033 _ = free_list.swapRemove(i);
......@@ -3180,8 +3167,9 @@ fn collectRebaseData(self: *MachO, rebase: *Rebase) !void {
31803167 const gpa = self.base.allocator;
31813168 const slice = self.sections.slice();
31823169
3183 for (self.rebases.keys()) |atom, i| {
3184 log.debug(" ATOM(%{d}, '{s}')", .{ atom.sym_index, atom.getName(self) });
3170 for (self.rebases.keys()) |atom_index, i| {
3171 const atom = self.getAtom(atom_index);
3172 log.debug(" ATOM(%{?d}, '{s}')", .{ atom.getSymbolIndex(), atom.getName(self) });
31853173
31863174 const sym = atom.getSymbol(self);
31873175 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 {
32093197 const gpa = self.base.allocator;
32103198 const slice = self.sections.slice();
32113199
3212 for (raw_bindings.keys()) |atom, i| {
3213 log.debug(" ATOM(%{d}, '{s}')", .{ atom.sym_index, atom.getName(self) });
3200 for (raw_bindings.keys()) |atom_index, i| {
3201 const atom = self.getAtom(atom_index);
3202 log.debug(" ATOM(%{?d}, '{s}')", .{ atom.getSymbolIndex(), atom.getName(self) });
32143203
32153204 const sym = atom.getSymbol(self);
32163205 const segment_index = slice.items(.segment_index)[sym.n_sect - 1];
......@@ -3384,7 +3373,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, lazy_bind: LazyBind) !void
33843373 if (lazy_bind.size() == 0) return;
33853374
33863375 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);
33883377
33893378 const section = self.sections.get(stub_helper_section_index);
33903379
......@@ -3394,10 +3383,11 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, lazy_bind: LazyBind) !void
33943383 else => unreachable,
33953384 };
33963385 const header = section.header;
3397 var atom = section.last_atom.?;
3386 var atom_index = section.last_atom_index.?;
33983387
33993388 var index: usize = lazy_bind.offsets.items.len;
34003389 while (index > 0) : (index -= 1) {
3390 const atom = self.getAtom(atom_index);
34013391 const sym = atom.getSymbol(self);
34023392 const file_offset = header.offset + sym.n_value - header.addr + stub_offset;
34033393 const bind_offset = lazy_bind.offsets.items[index - 1];
......@@ -3410,7 +3400,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, lazy_bind: LazyBind) !void
34103400
34113401 try self.base.file.?.pwriteAll(mem.asBytes(&bind_offset), file_offset);
34123402
3413 atom = atom.prev.?;
3403 atom_index = atom.prev_index.?;
34143404 }
34153405}
34163406
......@@ -3853,25 +3843,35 @@ pub fn getOrPutGlobalPtr(self: *MachO, name: []const u8) !GetOrPutGlobalPtrResul
38533843 return GetOrPutGlobalPtrResult{ .found_existing = false, .value_ptr = ptr };
38543844}
38553845
3846pub 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
3851pub 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
38563856/// Returns atom if there is an atom referenced by the symbol described by `sym_with_loc` descriptor.
38573857/// Returns null on failure.
3858pub fn getAtomForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?*Atom {
3858pub fn getAtomIndexForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?Atom.Index {
38593859 assert(sym_with_loc.file == null);
38603860 return self.atom_by_index_table.get(sym_with_loc.sym_index);
38613861}
38623862
38633863/// Returns GOT atom that references `sym_with_loc` if one exists.
38643864/// Returns null otherwise.
3865pub fn getGotAtomForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?*Atom {
3865pub fn getGotAtomIndexForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?Atom.Index {
38663866 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);
38683868}
38693869
38703870/// Returns stubs atom that references `sym_with_loc` if one exists.
38713871/// Returns null otherwise.
3872pub fn getStubsAtomForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?*Atom {
3872pub fn getStubsAtomIndexForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?Atom.Index {
38733873 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);
38753875}
38763876
38773877/// Returns symbol location corresponding to the set entrypoint.
......@@ -4257,30 +4257,35 @@ pub fn logAtoms(self: *MachO) void {
42574257 log.debug("atoms:", .{});
42584258
42594259 const slice = self.sections.slice();
4260 for (slice.items(.last_atom)) |last, i| {
4261 var atom = last orelse continue;
4260 for (slice.items(.last_atom_index)) |last_atom_index, i| {
4261 var atom_index = last_atom_index orelse continue;
42624262 const header = slice.items(.header)[i];
42634263
4264 while (atom.prev) |prev| {
4265 atom = prev;
4264 while (true) {
4265 const atom = self.getAtom(atom_index);
4266 if (atom.prev_index) |prev_index| {
4267 atom_index = prev_index;
4268 } else break;
42664269 }
42674270
42684271 log.debug("{s},{s}", .{ header.segName(), header.sectName() });
42694272
42704273 while (true) {
4271 self.logAtom(atom);
4272 if (atom.next) |next| {
4273 atom = next;
4274 self.logAtom(atom_index);
4275 const atom = self.getAtom(atom_index);
4276 if (atom.next_index) |next_index| {
4277 atom_index = next_index;
42744278 } else break;
42754279 }
42764280 }
42774281}
42784282
4279pub fn logAtom(self: *MachO, atom: *const Atom) void {
4283pub fn logAtom(self: *MachO, atom_index: Atom.Index) void {
4284 const atom = self.getAtom(atom_index);
42804285 const sym = atom.getSymbol(self);
42814286 const sym_name = atom.getName(self);
4282 log.debug(" ATOM(%{d}, '{s}') @ {x} (sizeof({x}), alignof({x})) in object({?d}) in sect({d})", .{
4283 atom.sym_index,
4287 log.debug(" ATOM(%{?d}, '{s}') @ {x} (sizeof({x}), alignof({x})) in object({?d}) in sect({d})", .{
4288 atom.getSymbolIndex(),
42844289 sym_name,
42854290 sym.n_value,
42864291 atom.size,
src/link/MachO/Atom.zig+54-38
......@@ -13,7 +13,6 @@ const trace = @import("../../tracy.zig").trace;
1313
1414const Allocator = mem.Allocator;
1515const Arch = std.Target.Cpu.Arch;
16const Dwarf = @import("../Dwarf.zig");
1716const MachO = @import("../MachO.zig");
1817const Relocation = @import("Relocation.zig");
1918const SymbolWithLoc = MachO.SymbolWithLoc;
......@@ -39,10 +38,11 @@ size: u64,
3938alignment: u32,
4039
4140/// Points to the previous and next neighbours
42next: ?*Atom,
43prev: ?*Atom,
41/// TODO use the same trick as with symbols: reserve index 0 as null atom
42next_index: ?Index,
43prev_index: ?Index,
4444
45dbg_info_atom: Dwarf.Atom,
45pub const Index = u32;
4646
4747pub const Binding = struct {
4848 target: SymbolWithLoc,
......@@ -54,15 +54,10 @@ pub const SymbolAtOffset = struct {
5454 offset: u64,
5555};
5656
57pub const empty = Atom{
58 .sym_index = 0,
59 .file = null,
60 .size = 0,
61 .alignment = 0,
62 .prev = null,
63 .next = null,
64 .dbg_info_atom = undefined,
65};
57pub fn getSymbolIndex(self: Atom) ?u32 {
58 if (self.sym_index == 0) return null;
59 return self.sym_index;
60}
6661
6762/// Returns symbol referencing this atom.
6863pub 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 {
7166
7267/// Returns pointer-to-symbol referencing this atom.
7368pub fn getSymbolPtr(self: Atom, macho_file: *MachO) *macho.nlist_64 {
69 const sym_index = self.getSymbolIndex().?;
7470 return macho_file.getSymbolPtr(.{
75 .sym_index = self.sym_index,
71 .sym_index = sym_index,
7672 .file = self.file,
7773 });
7874}
7975
8076pub 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 };
8279}
8380
8481/// Returns the name of this atom.
8582pub fn getName(self: Atom, macho_file: *MachO) []const u8 {
83 const sym_index = self.getSymbolIndex().?;
8684 return macho_file.getSymbolName(.{
87 .sym_index = self.sym_index,
85 .sym_index = sym_index,
8886 .file = self.file,
8987 });
9088}
......@@ -94,7 +92,8 @@ pub fn getName(self: Atom, macho_file: *MachO) []const u8 {
9492/// this calculation.
9593pub fn capacity(self: Atom, macho_file: *MachO) u64 {
9694 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);
9897 const next_sym = next.getSymbol(macho_file);
9998 return next_sym.n_value - self_sym.n_value;
10099 } else {
......@@ -106,7 +105,8 @@ pub fn capacity(self: Atom, macho_file: *MachO) u64 {
106105
107106pub fn freeListEligible(self: Atom, macho_file: *MachO) bool {
108107 // 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);
110110 const self_sym = self.getSymbol(macho_file);
111111 const next_sym = next.getSymbol(macho_file);
112112 const cap = next_sym.n_value - self_sym.n_value;
......@@ -116,19 +116,19 @@ pub fn freeListEligible(self: Atom, macho_file: *MachO) bool {
116116 return surplus >= MachO.min_text_capacity;
117117}
118118
119pub fn addRelocation(self: *Atom, macho_file: *MachO, reloc: Relocation) !void {
120 return self.addRelocations(macho_file, 1, .{reloc});
119pub fn addRelocation(macho_file: *MachO, atom_index: Index, reloc: Relocation) !void {
120 return addRelocations(macho_file, atom_index, 1, .{reloc});
121121}
122122
123123pub fn addRelocations(
124 self: *Atom,
125124 macho_file: *MachO,
125 atom_index: Index,
126126 comptime count: comptime_int,
127127 relocs: [count]Relocation,
128128) !void {
129129 const gpa = macho_file.base.allocator;
130130 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);
132132 if (!gop.found_existing) {
133133 gop.value_ptr.* = .{};
134134 }
......@@ -142,56 +142,72 @@ pub fn addRelocations(
142142 }
143143}
144144
145pub fn addRebase(self: *Atom, macho_file: *MachO, offset: u32) !void {
145pub fn addRebase(macho_file: *MachO, atom_index: Index, offset: u32) !void {
146146 const gpa = macho_file.base.allocator;
147 log.debug(" (adding rebase at offset 0x{x} in %{d})", .{ offset, self.sym_index });
148 const gop = try macho_file.rebases.getOrPut(gpa, self);
147 const atom = macho_file.getAtom(atom_index);
148 log.debug(" (adding rebase at offset 0x{x} in %{?d})", .{ offset, atom.getSymbolIndex() });
149 const gop = try macho_file.rebases.getOrPut(gpa, atom_index);
149150 if (!gop.found_existing) {
150151 gop.value_ptr.* = .{};
151152 }
152153 try gop.value_ptr.append(gpa, offset);
153154}
154155
155pub fn addBinding(self: *Atom, macho_file: *MachO, binding: Binding) !void {
156pub fn addBinding(macho_file: *MachO, atom_index: Index, binding: Binding) !void {
156157 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})", .{
158160 macho_file.getSymbolName(binding.target),
159161 binding.offset,
160 self.sym_index,
162 atom.getSymbolIndex(),
161163 });
162 const gop = try macho_file.bindings.getOrPut(gpa, self);
164 const gop = try macho_file.bindings.getOrPut(gpa, atom_index);
163165 if (!gop.found_existing) {
164166 gop.value_ptr.* = .{};
165167 }
166168 try gop.value_ptr.append(gpa, binding);
167169}
168170
169pub fn addLazyBinding(self: *Atom, macho_file: *MachO, binding: Binding) !void {
171pub fn addLazyBinding(macho_file: *MachO, atom_index: Index, binding: Binding) !void {
170172 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})", .{
172175 macho_file.getSymbolName(binding.target),
173176 binding.offset,
174 self.sym_index,
177 atom.getSymbolIndex(),
175178 });
176 const gop = try macho_file.lazy_bindings.getOrPut(gpa, self);
179 const gop = try macho_file.lazy_bindings.getOrPut(gpa, atom_index);
177180 if (!gop.found_existing) {
178181 gop.value_ptr.* = .{};
179182 }
180183 try gop.value_ptr.append(gpa, binding);
181184}
182185
183pub fn resolveRelocations(self: *Atom, macho_file: *MachO) !void {
184 const relocs = macho_file.relocs.get(self) orelse return;
185 const source_sym = self.getSymbol(macho_file);
186pub fn resolveRelocations(macho_file: *MachO, atom_index: Index) !void {
187 const atom = macho_file.getAtom(atom_index);
188 const relocs = macho_file.relocs.get(atom_index) orelse return;
189 const source_sym = atom.getSymbol(macho_file);
186190 const source_section = macho_file.sections.get(source_sym.n_sect - 1).header;
187191 const file_offset = source_section.offset + source_sym.n_value - source_section.addr;
188192
189 log.debug("relocating '{s}'", .{self.getName(macho_file)});
193 log.debug("relocating '{s}'", .{atom.getName(macho_file)});
190194
191195 for (relocs.items) |*reloc| {
192196 if (!reloc.dirty) continue;
193197
194 try reloc.resolve(self, macho_file, file_offset);
198 try reloc.resolve(macho_file, atom_index, file_offset);
195199 reloc.dirty = false;
196200 }
197201}
202
203pub 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 {
8282 }
8383
8484 if (self.debug_str_section_index == null) {
85 assert(self.dwarf.strtab.items.len == 0);
86 try self.dwarf.strtab.append(self.allocator, 0);
85 assert(self.dwarf.strtab.buffer.items.len == 0);
86 try self.dwarf.strtab.buffer.append(self.allocator, 0);
8787 self.debug_str_section_index = try self.allocateSection(
8888 "__debug_str",
89 @intCast(u32, self.dwarf.strtab.items.len),
89 @intCast(u32, self.dwarf.strtab.buffer.items.len),
9090 0,
9191 );
9292 self.debug_string_table_dirty = true;
......@@ -291,10 +291,10 @@ pub fn flushModule(self: *DebugSymbols, macho_file: *MachO) !void {
291291
292292 {
293293 const sect_index = self.debug_str_section_index.?;
294 if (self.debug_string_table_dirty or self.dwarf.strtab.items.len != self.getSection(sect_index).size) {
295 const needed_size = @intCast(u32, self.dwarf.strtab.items.len);
294 if (self.debug_string_table_dirty or self.dwarf.strtab.buffer.items.len != self.getSection(sect_index).size) {
295 const needed_size = @intCast(u32, self.dwarf.strtab.buffer.items.len);
296296 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);
298298 self.debug_string_table_dirty = false;
299299 }
300300 }
src/link/MachO/Relocation.zig+9-7
......@@ -29,33 +29,35 @@ pub fn fmtType(self: Relocation, target: std.Target) []const u8 {
2929 }
3030}
3131
32pub fn getTargetAtom(self: Relocation, macho_file: *MachO) ?*Atom {
32pub fn getTargetAtomIndex(self: Relocation, macho_file: *MachO) ?Atom.Index {
3333 switch (macho_file.base.options.target.cpu.arch) {
3434 .aarch64 => switch (@intToEnum(macho.reloc_type_arm64, self.type)) {
3535 .ARM64_RELOC_GOT_LOAD_PAGE21,
3636 .ARM64_RELOC_GOT_LOAD_PAGEOFF12,
3737 .ARM64_RELOC_POINTER_TO_GOT,
38 => return macho_file.getGotAtomForSymbol(self.target),
38 => return macho_file.getGotAtomIndexForSymbol(self.target),
3939 else => {},
4040 },
4141 .x86_64 => switch (@intToEnum(macho.reloc_type_x86_64, self.type)) {
4242 .X86_64_RELOC_GOT,
4343 .X86_64_RELOC_GOT_LOAD,
44 => return macho_file.getGotAtomForSymbol(self.target),
44 => return macho_file.getGotAtomIndexForSymbol(self.target),
4545 else => {},
4646 },
4747 else => unreachable,
4848 }
49 if (macho_file.getStubsAtomForSymbol(self.target)) |stubs_atom| return stubs_atom;
50 return macho_file.getAtomForSymbol(self.target);
49 if (macho_file.getStubsAtomIndexForSymbol(self.target)) |stubs_atom| return stubs_atom;
50 return macho_file.getAtomIndexForSymbol(self.target);
5151}
5252
53pub fn resolve(self: Relocation, atom: *Atom, macho_file: *MachO, base_offset: u64) !void {
53pub fn resolve(self: Relocation, macho_file: *MachO, atom_index: Atom.Index, base_offset: u64) !void {
5454 const arch = macho_file.base.options.target.cpu.arch;
55 const atom = macho_file.getAtom(atom_index);
5556 const source_sym = atom.getSymbol(macho_file);
5657 const source_addr = source_sym.n_value + self.offset;
5758
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);
5961 const target_addr = @intCast(i64, target_atom.getSymbol(macho_file).n_value) + self.addend;
6062
6163 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";
1212
1313fn calcInstallNameLen(cmd_size: u64, name: []const u8, assume_max_path_len: bool) u64 {
1414 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;
1616 return mem.alignForwardGeneric(u64, cmd_size + name_len, @alignOf(u64));
1717}
1818
src/link/MachO/zld.zig+7-4
......@@ -3596,7 +3596,8 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
35963596 man.hash.addOptionalBytes(options.sysroot);
35973597 try man.addOptionalFile(options.entitlements);
35983598
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.
36003601 _ = try man.hit();
36013602 digest = man.final();
36023603
......@@ -4177,9 +4178,11 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
41774178 log.debug("failed to save linking hash digest file: {s}", .{@errorName(err)});
41784179 };
41794180 // Again failure here only means an unnecessary cache miss.
4180 man.writeManifest() catch |err| {
4181 log.debug("failed to write cache manifest when linking: {s}", .{@errorName(err)});
4182 };
4181 if (man.have_exclusive_lock) {
4182 man.writeManifest() catch |err| {
4183 log.debug("failed to write cache manifest when linking: {s}", .{@errorName(err)});
4184 };
4185 }
41834186 // We hang on to this lock so that the output file path can be used without
41844187 // other processes clobbering it.
41854188 macho_file.base.lock = man.toOwnedLock();
src/link/Plan9.zig+154-95
......@@ -21,14 +21,7 @@ const Allocator = std.mem.Allocator;
2121const log = std.log.scoped(.link);
2222const assert = std.debug.assert;
2323
24const FnDeclOutput = struct {
25 /// this code is modified when relocated so it is mutable
26 code: []u8,
27 /// this might have to be modified in the linker, so thats why its mutable
28 lineinfo: []u8,
29 start_line: u32,
30 end_line: u32,
31};
24pub const base_tag = .plan9;
3225
3326base: link.File,
3427sixtyfour_bit: bool,
......@@ -101,6 +94,9 @@ got_index_free_list: std.ArrayListUnmanaged(usize) = .{},
10194
10295syms_index_free_list: std.ArrayListUnmanaged(usize) = .{},
10396
97decl_blocks: std.ArrayListUnmanaged(DeclBlock) = .{},
98decls: std.AutoHashMapUnmanaged(Module.Decl.Index, DeclMetadata) = .{},
99
104100const Reloc = struct {
105101 target: Module.Decl.Index,
106102 offset: u64,
......@@ -115,6 +111,42 @@ const Bases = struct {
115111
116112const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(struct { info: DeclBlock, code: []const u8 }));
117113
114pub const PtrWidth = enum { p32, p64 };
115
116pub 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
128const 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
141const 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
118150fn getAddr(self: Plan9, addr: u64, t: aout.Sym.Type) u64 {
119151 return addr + switch (t) {
120152 .T, .t, .l, .L => self.bases.text,
......@@ -127,22 +159,6 @@ fn getSymAddr(self: Plan9, s: aout.Sym) u64 {
127159 return self.getAddr(s.value, s.type);
128160}
129161
130pub 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
146162pub fn defaultBaseAddrs(arch: std.Target.Cpu.Arch) Bases {
147163 return switch (arch) {
148164 .x86_64 => .{
......@@ -164,8 +180,6 @@ pub fn defaultBaseAddrs(arch: std.Target.Cpu.Arch) Bases {
164180 };
165181}
166182
167pub const PtrWidth = enum { p32, p64 };
168
169183pub fn createEmpty(gpa: Allocator, options: link.Options) !*Plan9 {
170184 if (options.use_llvm)
171185 return error.LLVMBackendDoesNotSupportPlan9;
......@@ -271,7 +285,7 @@ pub fn updateFunc(self: *Plan9, module: *Module, func: *Module.Fn, air: Air, liv
271285 const decl = module.declPtr(decl_index);
272286 self.freeUnnamedConsts(decl_index);
273287
274 try self.seeDecl(decl_index);
288 _ = try self.seeDecl(decl_index);
275289 log.debug("codegen decl {*} ({s})", .{ decl, decl.name });
276290
277291 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
299313 },
300314 );
301315 const code = switch (res) {
302 .appended => try code_buffer.toOwnedSlice(),
316 .ok => try code_buffer.toOwnedSlice(),
303317 .fail => |em| {
304318 decl.analysis = .codegen_failure;
305319 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
313327 .end_line = end_line,
314328 };
315329 try self.putFn(decl_index, out);
316 return self.updateFinish(decl);
330 return self.updateFinish(decl_index);
317331}
318332
319333pub fn lowerUnnamedConst(self: *Plan9, tv: TypedValue, decl_index: Module.Decl.Index) !u32 {
320 try self.seeDecl(decl_index);
334 _ = try self.seeDecl(decl_index);
321335 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
322336 defer code_buffer.deinit();
323337
......@@ -358,8 +372,7 @@ pub fn lowerUnnamedConst(self: *Plan9, tv: TypedValue, decl_index: Module.Decl.I
358372 .parent_atom_index = @enumToInt(decl_index),
359373 });
360374 const code = switch (res) {
361 .externally_managed => |x| x,
362 .appended => code_buffer.items,
375 .ok => code_buffer.items,
363376 .fail => |em| {
364377 decl.analysis = .codegen_failure;
365378 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)
388401 }
389402 }
390403
391 try self.seeDecl(decl_index);
404 _ = try self.seeDecl(decl_index);
392405
393406 log.debug("codegen decl {*} ({s}) ({d})", .{ decl, decl.name, decl_index });
394407
......@@ -403,8 +416,7 @@ pub fn updateDecl(self: *Plan9, module: *Module, decl_index: Module.Decl.Index)
403416 .parent_atom_index = @enumToInt(decl_index),
404417 });
405418 const code = switch (res) {
406 .externally_managed => |x| x,
407 .appended => code_buffer.items,
419 .ok => code_buffer.items,
408420 .fail => |em| {
409421 decl.analysis = .codegen_failure;
410422 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)
416428 if (self.data_decl_table.fetchPutAssumeCapacity(decl_index, duped_code)) |old_entry| {
417429 self.base.allocator.free(old_entry.value);
418430 }
419 return self.updateFinish(decl);
431 return self.updateFinish(decl_index);
420432}
421433/// called at the end of update{Decl,Func}
422fn updateFinish(self: *Plan9, decl: *Module.Decl) !void {
434fn updateFinish(self: *Plan9, decl_index: Module.Decl.Index) !void {
435 const decl = self.base.options.module.?.declPtr(decl_index);
423436 const is_fn = (decl.ty.zigTypeTag() == .Fn);
424437 log.debug("update the symbol table and got for decl {*} ({s})", .{ decl, decl.name });
425438 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);
426441 // write the internal linker metadata
427 decl.link.plan9.type = sym_t;
442 decl_block.type = sym_t;
428443 // write the symbol
429 // we already have the got index because that got allocated in allocateDeclIndexes
444 // we already have the got index
430445 const sym: aout.Sym = .{
431446 .value = undefined, // the value of stuff gets filled in in flushModule
432 .type = decl.link.plan9.type,
447 .type = decl_block.type,
433448 .name = mem.span(decl.name),
434449 };
435450
436 if (decl.link.plan9.sym_index) |s| {
451 if (decl_block.sym_index) |s| {
437452 self.syms.items[s] = sym;
438453 } else {
439454 const s = try self.allocateSymbolIndex();
440 decl.link.plan9.sym_index = s;
455 decl_block.sym_index = s;
441456 self.syms.items[s] = sym;
442457 }
443458}
......@@ -552,6 +567,7 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No
552567 while (it.next()) |entry| {
553568 const decl_index = entry.key_ptr.*;
554569 const decl = mod.declPtr(decl_index);
570 const decl_block = self.getDeclBlockPtr(self.decls.get(decl_index).?.index);
555571 const out = entry.value_ptr.*;
556572 log.debug("write text decl {*} ({s}), lines {d} to {d}", .{ decl, decl.name, out.start_line + 1, out.end_line });
557573 {
......@@ -570,16 +586,16 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No
570586 iovecs_i += 1;
571587 const off = self.getAddr(text_i, .t);
572588 text_i += out.code.len;
573 decl.link.plan9.offset = off;
589 decl_block.offset = off;
574590 if (!self.sixtyfour_bit) {
575 mem.writeIntNative(u32, got_table[decl.link.plan9.got_index.? * 4 ..][0..4], @intCast(u32, off));
576 mem.writeInt(u32, got_table[decl.link.plan9.got_index.? * 4 ..][0..4], @intCast(u32, off), self.base.options.target.cpu.arch.endian());
591 mem.writeIntNative(u32, got_table[decl_block.got_index.? * 4 ..][0..4], @intCast(u32, off));
592 mem.writeInt(u32, got_table[decl_block.got_index.? * 4 ..][0..4], @intCast(u32, off), self.base.options.target.cpu.arch.endian());
577593 } 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());
579595 }
580 self.syms.items[decl.link.plan9.sym_index.?].value = off;
596 self.syms.items[decl_block.sym_index.?].value = off;
581597 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);
583599 }
584600 }
585601 }
......@@ -600,6 +616,7 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No
600616 while (it.next()) |entry| {
601617 const decl_index = entry.key_ptr.*;
602618 const decl = mod.declPtr(decl_index);
619 const decl_block = self.getDeclBlockPtr(self.decls.get(decl_index).?.index);
603620 const code = entry.value_ptr.*;
604621 log.debug("write data decl {*} ({s})", .{ decl, decl.name });
605622
......@@ -608,15 +625,15 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No
608625 iovecs_i += 1;
609626 const off = self.getAddr(data_i, .d);
610627 data_i += code.len;
611 decl.link.plan9.offset = off;
628 decl_block.offset = off;
612629 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());
614631 } 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());
616633 }
617 self.syms.items[decl.link.plan9.sym_index.?].value = off;
634 self.syms.items[decl_block.sym_index.?].value = off;
618635 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);
620637 }
621638 }
622639 // 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
678695 for (kv.value_ptr.items) |reloc| {
679696 const target_decl_index = reloc.target;
680697 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.?;
682700
683701 const offset = reloc.offset;
684702 const addend = reloc.addend;
......@@ -711,35 +729,43 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No
711729fn addDeclExports(
712730 self: *Plan9,
713731 module: *Module,
714 decl: *Module.Decl,
732 decl_index: Module.Decl.Index,
715733 exports: []const *Module.Export,
716734) !void {
735 const metadata = self.decls.getPtr(decl_index).?;
736 const decl_block = self.getDeclBlock(metadata.index);
737
717738 for (exports) |exp| {
718739 // plan9 does not support custom sections
719740 if (exp.options.section) |section_name| {
720741 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 ));
722748 break;
723749 }
724750 }
725751 const sym = .{
726 .value = decl.link.plan9.offset.?,
727 .type = decl.link.plan9.type.toGlobal(),
752 .value = decl_block.offset.?,
753 .type = decl_block.type.toGlobal(),
728754 .name = exp.options.name,
729755 };
730756
731 if (exp.link.plan9) |i| {
757 if (metadata.getExport(self, exp.options.name)) |i| {
732758 self.syms.items[i] = sym;
733759 } else {
734760 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);
736762 }
737763 }
738764}
739765
740766pub fn freeDecl(self: *Plan9, decl_index: Module.Decl.Index) void {
741767 // 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.
743769 // However that is planned to change, see the TODO comment in Module.zig
744770 // in the deleteUnusedDecl function.
745771 const mod = self.base.options.module.?;
......@@ -762,13 +788,18 @@ pub fn freeDecl(self: *Plan9, decl_index: Module.Decl.Index) void {
762788 self.base.allocator.free(removed_entry.value);
763789 }
764790 }
765 if (decl.link.plan9.got_index) |i| {
766 // TODO: if this catch {} is triggered, an assertion in flushModule will be triggered, because got_index_free_list will have the wrong length
767 self.got_index_free_list.append(self.base.allocator, i) catch {};
768 }
769 if (decl.link.plan9.sym_index) |i| {
770 self.syms_index_free_list.append(self.base.allocator, i) catch {};
771 self.syms.items[i] = aout.Sym.undefined_symbol;
791 if (self.decls.fetchRemove(decl_index)) |const_kv| {
792 var kv = const_kv;
793 const decl_block = self.getDeclBlock(kv.value.index);
794 if (decl_block.got_index) |i| {
795 // TODO: if this catch {} is triggered, an assertion in flushModule will be triggered, because got_index_free_list will have the wrong length
796 self.got_index_free_list.append(self.base.allocator, i) catch {};
797 }
798 if (decl_block.sym_index) |i| {
799 self.syms_index_free_list.append(self.base.allocator, i) catch {};
800 self.syms.items[i] = aout.Sym.undefined_symbol;
801 }
802 kv.value.exports.deinit(self.base.allocator);
772803 }
773804 self.freeUnnamedConsts(decl_index);
774805 {
......@@ -788,12 +819,30 @@ fn freeUnnamedConsts(self: *Plan9, decl_index: Module.Decl.Index) void {
788819 unnamed_consts.clearAndFree(self.base.allocator);
789820}
790821
791pub fn seeDecl(self: *Plan9, decl_index: Module.Decl.Index) !void {
792 const mod = self.base.options.module.?;
793 const decl = mod.declPtr(decl_index);
794 if (decl.link.plan9.got_index == null) {
795 decl.link.plan9.got_index = self.allocateGotIndex();
822fn createDeclBlock(self: *Plan9) !DeclBlock.Index {
823 const gpa = self.base.allocator;
824 const index = @intCast(DeclBlock.Index, self.decl_blocks.items.len);
825 const decl_block = try self.decl_blocks.addOne(gpa);
826 decl_block.* = .{
827 .type = .t,
828 .offset = null,
829 .sym_index = null,
830 .got_index = null,
831 };
832 return index;
833}
834
835pub 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 };
796844 }
845 return gop.value_ptr.index;
797846}
798847
799848pub fn updateDeclExports(
......@@ -802,7 +851,7 @@ pub fn updateDeclExports(
802851 decl_index: Module.Decl.Index,
803852 exports: []const *Module.Export,
804853) !void {
805 try self.seeDecl(decl_index);
854 _ = try self.seeDecl(decl_index);
806855 // we do all the things in flush
807856 _ = module;
808857 _ = exports;
......@@ -844,10 +893,17 @@ pub fn deinit(self: *Plan9) void {
844893 self.syms_index_free_list.deinit(gpa);
845894 self.file_segments.deinit(gpa);
846895 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 }
847905}
848906
849pub const Export = ?usize;
850pub const base_tag = .plan9;
851907pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Options) !*Plan9 {
852908 if (options.use_llvm)
853909 return error.LLVMBackendDoesNotSupportPlan9;
......@@ -913,20 +969,19 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void {
913969 }
914970 }
915971
916 const mod = self.base.options.module.?;
917
918972 // write the data symbols
919973 {
920974 var it = self.data_decl_table.iterator();
921975 while (it.next()) |entry| {
922976 const decl_index = entry.key_ptr.*;
923 const decl = mod.declPtr(decl_index);
924 const sym = self.syms.items[decl.link.plan9.sym_index.?];
977 const decl_metadata = self.decls.get(decl_index).?;
978 const decl_block = self.getDeclBlock(decl_metadata.index);
979 const sym = self.syms.items[decl_block.sym_index.?];
925980 try self.writeSym(writer, sym);
926981 if (self.base.options.module.?.decl_exports.get(decl_index)) |exports| {
927 for (exports.items) |e| {
928 try self.writeSym(writer, self.syms.items[e.link.plan9.?]);
929 }
982 for (exports.items) |e| if (decl_metadata.getExport(self, e.options.name)) |exp_i| {
983 try self.writeSym(writer, self.syms.items[exp_i]);
984 };
930985 }
931986 }
932987 }
......@@ -945,32 +1000,28 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void {
9451000 var submap_it = symidx_and_submap.functions.iterator();
9461001 while (submap_it.next()) |entry| {
9471002 const decl_index = entry.key_ptr.*;
948 const decl = mod.declPtr(decl_index);
949 const sym = self.syms.items[decl.link.plan9.sym_index.?];
1003 const decl_metadata = self.decls.get(decl_index).?;
1004 const decl_block = self.getDeclBlock(decl_metadata.index);
1005 const sym = self.syms.items[decl_block.sym_index.?];
9501006 try self.writeSym(writer, sym);
9511007 if (self.base.options.module.?.decl_exports.get(decl_index)) |exports| {
952 for (exports.items) |e| {
953 const s = self.syms.items[e.link.plan9.?];
1008 for (exports.items) |e| if (decl_metadata.getExport(self, e.options.name)) |exp_i| {
1009 const s = self.syms.items[exp_i];
9541010 if (mem.eql(u8, s.name, "_start"))
9551011 self.entry_val = s.value;
9561012 try self.writeSym(writer, s);
957 }
1013 };
9581014 }
9591015 }
9601016 }
9611017 }
9621018}
9631019
964/// this will be removed, moved to updateFinish
965pub fn allocateDeclIndexes(self: *Plan9, decl_index: Module.Decl.Index) !void {
966 _ = self;
967 _ = decl_index;
968}
9691020/// Must be called only after a successful call to `updateDecl`.
970pub fn updateDeclLineNumber(self: *Plan9, mod: *Module, decl: *const Module.Decl) !void {
1021pub fn updateDeclLineNumber(self: *Plan9, mod: *Module, decl_index: Module.Decl.Index) !void {
9711022 _ = self;
9721023 _ = mod;
973 _ = decl;
1024 _ = decl_index;
9741025}
9751026
9761027pub fn getDeclVAddr(
......@@ -1011,3 +1062,11 @@ pub fn getDeclVAddr(
10111062 });
10121063 return undefined;
10131064}
1065
1066pub fn getDeclBlock(self: *const Plan9, index: DeclBlock.Index) DeclBlock {
1067 return self.decl_blocks.items[index];
1068}
1069
1070fn 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");
4242const spec = @import("../codegen/spirv/spec.zig");
4343const IdResult = spec.IdResult;
4444
45// TODO: Should this struct be used at all rather than just a hashmap of aux data for every decl?
46pub 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
5245base: link.File,
5346
5447/// 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
209202 // so that we can access them before processing them.
210203 // TODO: We're allocating an ID unconditionally now, are there
211204 // 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
213209 for (self.decl_table.keys()) |decl_index| {
214210 const decl = module.declPtr(decl_index);
215211 if (decl.has_tv) {
216 decl.fn_link.spirv.id = spv.allocId();
212 ids.putAssumeCapacityNoClobber(decl_index, spv.allocId());
217213 }
218214 }
219215
220216 // 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);
222218 defer decl_gen.deinit();
223219
224220 var it = self.decl_table.iterator();
......@@ -231,7 +227,7 @@ pub fn flushModule(self: *SpirV, comp: *Compilation, prog_node: *std.Progress.No
231227 const liveness = entry.value_ptr.liveness;
232228
233229 // 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| {
235231 try module.failed_decls.put(module.gpa, decl_index, msg);
236232 return; // TODO: Attempt to generate more decls?
237233 }
src/link/Wasm.zig+292-281
......@@ -9,7 +9,7 @@ const fs = std.fs;
99const leb = std.leb;
1010const log = std.log.scoped(.link);
1111
12const Atom = @import("Wasm/Atom.zig");
12pub const Atom = @import("Wasm/Atom.zig");
1313const Dwarf = @import("Dwarf.zig");
1414const Module = @import("../Module.zig");
1515const Compilation = @import("../Compilation.zig");
......@@ -31,10 +31,7 @@ const Object = @import("Wasm/Object.zig");
3131const Archive = @import("Wasm/Archive.zig");
3232const types = @import("Wasm/types.zig");
3333
34pub const base_tag = link.File.Tag.wasm;
35
36/// deprecated: Use `@import("Wasm/Atom.zig");`
37pub const DeclBlock = Atom;
34pub const base_tag: link.File.Tag = .wasm;
3835
3936base: link.File,
4037/// Output name of the file
......@@ -47,18 +44,16 @@ llvm_object: ?*LlvmObject = null,
4744/// TODO: Allow setting this through a flag?
4845host_name: []const u8 = "env",
4946/// List of all `Decl` that are currently alive.
50/// This is ment for bookkeeping so we can safely cleanup all codegen memory
51/// when calling `deinit`
52decls: std.AutoHashMapUnmanaged(Module.Decl.Index, void) = .{},
47/// Each index maps to the corresponding `Atom.Index`.
48decls: std.AutoHashMapUnmanaged(Module.Decl.Index, Atom.Index) = .{},
5349/// List of all symbols generated by Zig code.
5450symbols: std.ArrayListUnmanaged(Symbol) = .{},
5551/// List of symbol indexes which are free to be used.
5652symbols_free_list: std.ArrayListUnmanaged(u32) = .{},
5753/// Maps atoms to their segment index
58atoms: std.AutoHashMapUnmanaged(u32, *Atom) = .{},
59/// Atoms managed and created by the linker. This contains atoms
60/// from object files, and not Atoms generated by a Decl.
61managed_atoms: std.ArrayListUnmanaged(*Atom) = .{},
54atoms: std.AutoHashMapUnmanaged(u32, Atom.Index) = .{},
55/// List of all atoms.
56managed_atoms: std.ArrayListUnmanaged(Atom) = .{},
6257/// Represents the index into `segments` where the 'code' section
6358/// lives.
6459code_section_index: ?u32 = null,
......@@ -148,7 +143,7 @@ undefs: std.StringArrayHashMapUnmanaged(SymbolLoc) = .{},
148143/// Maps a symbol's location to an atom. This can be used to find meta
149144/// data of a symbol, such as its size, or its offset to perform a relocation.
150145/// Undefined (and synthetic) symbols do not have an Atom and therefore cannot be mapped.
151symbol_atom: std.AutoHashMapUnmanaged(SymbolLoc, *Atom) = .{},
146symbol_atom: std.AutoHashMapUnmanaged(SymbolLoc, Atom.Index) = .{},
152147/// Maps a symbol's location to its export name, which may differ from the decl's name
153148/// which does the exporting.
154149/// Note: The value represents the offset into the string table, rather than the actual string.
......@@ -165,14 +160,14 @@ error_table_symbol: ?u32 = null,
165160// unit contains Zig code. The lifetime of these atoms are extended
166161// until the end of the compiler's lifetime. Meaning they're not freed
167162// during `flush()` in incremental-mode.
168debug_info_atom: ?*Atom = null,
169debug_line_atom: ?*Atom = null,
170debug_loc_atom: ?*Atom = null,
171debug_ranges_atom: ?*Atom = null,
172debug_abbrev_atom: ?*Atom = null,
173debug_str_atom: ?*Atom = null,
174debug_pubnames_atom: ?*Atom = null,
175debug_pubtypes_atom: ?*Atom = null,
163debug_info_atom: ?Atom.Index = null,
164debug_line_atom: ?Atom.Index = null,
165debug_loc_atom: ?Atom.Index = null,
166debug_ranges_atom: ?Atom.Index = null,
167debug_abbrev_atom: ?Atom.Index = null,
168debug_str_atom: ?Atom.Index = null,
169debug_pubnames_atom: ?Atom.Index = null,
170debug_pubtypes_atom: ?Atom.Index = null,
176171
177172pub const Segment = struct {
178173 alignment: u32,
......@@ -183,13 +178,9 @@ pub const Segment = struct {
183178pub const FnData = struct {
184179 /// Reference to the wasm type that represents this function.
185180 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,
189181
190182 pub const empty: FnData = .{
191183 .type_index = undefined,
192 .src_fn = Dwarf.SrcFn.empty,
193184 };
194185};
195186
......@@ -434,10 +425,10 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
434425 // at the end during `initializeCallCtorsFunction`.
435426 }
436427
437 if (!options.strip and options.module != null) {
438 wasm_bin.dwarf = Dwarf.init(allocator, &wasm_bin.base, options.target);
439 try wasm_bin.initDebugSections();
440 }
428 // if (!options.strip and options.module != null) {
429 // wasm_bin.dwarf = Dwarf.init(allocator, &wasm_bin.base, options.target);
430 // try wasm_bin.initDebugSections();
431 // }
441432
442433 return wasm_bin;
443434}
......@@ -478,6 +469,7 @@ fn createSyntheticSymbol(wasm: *Wasm, name: []const u8, tag: Symbol.Tag) !Symbol
478469 try wasm.globals.put(wasm.base.allocator, name_offset, loc);
479470 return loc;
480471}
472
481473/// Initializes symbols and atoms for the debug sections
482474/// Initialization is only done when compiling Zig code.
483475/// When Zig is invoked as a linker instead, the atoms
......@@ -520,6 +512,36 @@ fn parseObjectFile(wasm: *Wasm, path: []const u8) !bool {
520512 return true;
521513}
522514
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`.
518pub 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`
527fn 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
537pub inline fn getAtom(wasm: *const Wasm, index: Atom.Index) Atom {
538 return wasm.managed_atoms.items[index];
539}
540
541pub inline fn getAtomPtr(wasm: *Wasm, index: Atom.Index) *Atom {
542 return &wasm.managed_atoms.items[index];
543}
544
523545/// Parses an archive file and will then parse each object file
524546/// that was found in the archive file.
525547/// Returns false when the file is not an archive file.
......@@ -861,15 +883,16 @@ fn resolveLazySymbols(wasm: *Wasm) !void {
861883 try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc);
862884 _ = wasm.resolved_symbols.swapRemove(loc); // we don't want to emit this symbol, only use it for relocations.
863885
864 const atom = try wasm.base.allocator.create(Atom);
865 errdefer wasm.base.allocator.destroy(atom);
866 try wasm.managed_atoms.append(wasm.base.allocator, atom);
886 // TODO: Can we use `createAtom` here while also re-using the symbol
887 // from `createSyntheticSymbol`.
888 const atom_index = @intCast(Atom.Index, wasm.managed_atoms.items.len);
889 const atom = try wasm.managed_atoms.addOne(wasm.base.allocator);
867890 atom.* = Atom.empty;
868891 atom.sym_index = loc.index;
869892 atom.alignment = 1;
870893
871 try wasm.parseAtom(atom, .{ .data = .synthetic });
872 try wasm.symbol_atom.putNoClobber(wasm.base.allocator, loc, atom);
894 try wasm.parseAtom(atom_index, .{ .data = .synthetic });
895 try wasm.symbol_atom.putNoClobber(wasm.base.allocator, loc, atom_index);
873896 }
874897
875898 if (wasm.undefs.fetchSwapRemove("__heap_end")) |kv| {
......@@ -877,15 +900,14 @@ fn resolveLazySymbols(wasm: *Wasm) !void {
877900 try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc);
878901 _ = wasm.resolved_symbols.swapRemove(loc);
879902
880 const atom = try wasm.base.allocator.create(Atom);
881 errdefer wasm.base.allocator.destroy(atom);
882 try wasm.managed_atoms.append(wasm.base.allocator, atom);
903 const atom_index = @intCast(Atom.Index, wasm.managed_atoms.items.len);
904 const atom = try wasm.managed_atoms.addOne(wasm.base.allocator);
883905 atom.* = Atom.empty;
884906 atom.sym_index = loc.index;
885907 atom.alignment = 1;
886908
887 try wasm.parseAtom(atom, .{ .data = .synthetic });
888 try wasm.symbol_atom.putNoClobber(wasm.base.allocator, loc, atom);
909 try wasm.parseAtom(atom_index, .{ .data = .synthetic });
910 try wasm.symbol_atom.putNoClobber(wasm.base.allocator, loc, atom_index);
889911 }
890912}
891913
......@@ -924,16 +946,6 @@ pub fn deinit(wasm: *Wasm) void {
924946 if (wasm.llvm_object) |llvm_object| llvm_object.destroy(gpa);
925947 }
926948
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
937949 for (wasm.func_types.items) |*func_type| {
938950 func_type.deinit(gpa);
939951 }
......@@ -958,9 +970,8 @@ pub fn deinit(wasm: *Wasm) void {
958970 wasm.symbol_atom.deinit(gpa);
959971 wasm.export_names.deinit(gpa);
960972 wasm.atoms.deinit(gpa);
961 for (wasm.managed_atoms.items) |managed_atom| {
962 managed_atom.deinit(gpa);
963 gpa.destroy(managed_atom);
973 for (wasm.managed_atoms.items) |*managed_atom| {
974 managed_atom.deinit(wasm);
964975 }
965976 wasm.managed_atoms.deinit(gpa);
966977 wasm.segments.deinit(gpa);
......@@ -986,31 +997,23 @@ pub fn deinit(wasm: *Wasm) void {
986997 }
987998}
988999
989pub fn allocateDeclIndexes(wasm: *Wasm, decl_index: Module.Decl.Index) !void {
990 if (wasm.llvm_object) |_| return;
991 const decl = wasm.base.options.module.?.declPtr(decl_index);
992 if (decl.link.wasm.sym_index != 0) return;
993
1000/// Allocates a new symbol and returns its index.
1001/// Will re-use slots when a symbol was freed at an earlier stage.
1002pub fn allocateSymbol(wasm: *Wasm) !u32 {
9941003 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
9991004 var symbol: Symbol = .{
10001005 .name = undefined, // will be set after updateDecl
10011006 .flags = @enumToInt(Symbol.Flag.WASM_SYM_BINDING_LOCAL),
10021007 .tag = undefined, // will be set after updateDecl
10031008 .index = undefined, // will be set after updateDecl
10041009 };
1005
10061010 if (wasm.symbols_free_list.popOrNull()) |index| {
1007 atom.sym_index = index;
10081011 wasm.symbols.items[index] = symbol;
1009 } else {
1010 atom.sym_index = @intCast(u32, wasm.symbols.items.len);
1011 wasm.symbols.appendAssumeCapacity(symbol);
1012 return index;
10121013 }
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;
10141017}
10151018
10161019pub 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
10261029
10271030 const decl_index = func.owner_decl;
10281031 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();
10301035
1031 decl.link.wasm.clear();
1032
1033 var decl_state: ?Dwarf.DeclState = if (wasm.dwarf) |*dwarf| try dwarf.initDeclState(mod, decl_index) else null;
1034 defer if (decl_state) |*ds| ds.deinit();
1036 // var decl_state: ?Dwarf.DeclState = if (wasm.dwarf) |*dwarf| try dwarf.initDeclState(mod, decl_index) else null;
1037 // defer if (decl_state) |*ds| ds.deinit();
10351038
10361039 var code_writer = std.ArrayList(u8).init(wasm.base.allocator);
10371040 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 // );
10381050 const result = try codegen.generateFunction(
10391051 &wasm.base,
10401052 decl.srcLoc(),
......@@ -1042,11 +1054,11 @@ pub fn updateFunc(wasm: *Wasm, mod: *Module, func: *Module.Fn, air: Air, livenes
10421054 air,
10431055 liveness,
10441056 &code_writer,
1045 if (decl_state) |*ds| .{ .dwarf = ds } else .none,
1057 .none,
10461058 );
10471059
10481060 const code = switch (result) {
1049 .appended => code_writer.items,
1061 .ok => code_writer.items,
10501062 .fail => |em| {
10511063 decl.analysis = .codegen_failure;
10521064 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
10541066 },
10551067 };
10561068
1057 if (wasm.dwarf) |*dwarf| {
1058 try dwarf.commitDeclState(
1059 mod,
1060 decl_index,
1061 // Actual value will be written after relocation.
1062 // For Wasm, this is the offset relative to the code section
1063 // which isn't known until flush().
1064 0,
1065 code.len,
1066 &decl_state.?,
1067 );
1068 }
1069 return wasm.finishUpdateDecl(decl, code);
1069 // if (wasm.dwarf) |*dwarf| {
1070 // try dwarf.commitDeclState(
1071 // mod,
1072 // decl_index,
1073 // // Actual value will be written after relocation.
1074 // // For Wasm, this is the offset relative to the code section
1075 // // which isn't known until flush().
1076 // 0,
1077 // code.len,
1078 // &decl_state.?,
1079 // );
1080 // }
1081 return wasm.finishUpdateDecl(decl_index, code);
10701082}
10711083
10721084// 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
10831095 defer tracy.end();
10841096
10851097 const decl = mod.declPtr(decl_index);
1086 assert(decl.link.wasm.sym_index != 0); // Must call allocateDeclIndexes()
1087
1088 decl.link.wasm.clear();
1089
10901098 if (decl.val.castTag(.function)) |_| {
10911099 return;
10921100 } else if (decl.val.castTag(.extern_fn)) |_| {
10931101 return;
10941102 }
10951103
1104 const atom_index = try wasm.getOrCreateAtomForDecl(decl_index);
1105 const atom = wasm.getAtomPtr(atom_index);
1106 atom.clear();
1107
10961108 if (decl.isExtern()) {
10971109 const variable = decl.getVariable().?;
10981110 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);
11001112 }
11011113 const val = if (decl.val.castTag(.variable)) |payload| payload.data.init else decl.val;
11021114
......@@ -1109,12 +1121,11 @@ pub fn updateDecl(wasm: *Wasm, mod: *Module, decl_index: Module.Decl.Index) !voi
11091121 .{ .ty = decl.ty, .val = val },
11101122 &code_writer,
11111123 .none,
1112 .{ .parent_atom_index = decl.link.wasm.sym_index },
1124 .{ .parent_atom_index = atom.sym_index },
11131125 );
11141126
11151127 const code = switch (res) {
1116 .externally_managed => |x| x,
1117 .appended => code_writer.items,
1128 .ok => code_writer.items,
11181129 .fail => |em| {
11191130 decl.analysis = .codegen_failure;
11201131 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
11221133 },
11231134 };
11241135
1125 return wasm.finishUpdateDecl(decl, code);
1136 return wasm.finishUpdateDecl(decl_index, code);
11261137}
11271138
1128pub fn updateDeclLineNumber(wasm: *Wasm, mod: *Module, decl: *const Module.Decl) !void {
1139pub fn updateDeclLineNumber(wasm: *Wasm, mod: *Module, decl_index: Module.Decl.Index) !void {
11291140 if (wasm.llvm_object) |_| return;
11301141 if (wasm.dwarf) |*dw| {
11311142 const tracy = trace(@src());
11321143 defer tracy.end();
11331144
1145 const decl = mod.declPtr(decl_index);
11341146 const decl_name = try decl.getFullyQualifiedName(mod);
11351147 defer wasm.base.allocator.free(decl_name);
11361148
11371149 log.debug("updateDeclLineNumber {s}{*}", .{ decl_name, decl });
1138 try dw.updateDeclLineNumber(decl);
1150 try dw.updateDeclLineNumber(mod, decl_index);
11391151 }
11401152}
11411153
1142fn finishUpdateDecl(wasm: *Wasm, decl: *Module.Decl, code: []const u8) !void {
1154fn finishUpdateDecl(wasm: *Wasm, decl_index: Module.Decl.Index, code: []const u8) !void {
11431155 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);
11451159 const symbol = &wasm.symbols.items[atom.sym_index];
11461160 const full_name = try decl.getFullyQualifiedName(mod);
11471161 defer wasm.base.allocator.free(full_name);
......@@ -1149,8 +1163,8 @@ fn finishUpdateDecl(wasm: *Wasm, decl: *Module.Decl, code: []const u8) !void {
11491163 try atom.code.appendSlice(wasm.base.allocator, code);
11501164 try wasm.resolved_symbols.put(wasm.base.allocator, atom.symbolLoc(), {});
11511165
1152 if (code.len == 0) return;
11531166 atom.size = @intCast(u32, code.len);
1167 if (code.len == 0) return;
11541168 atom.alignment = decl.ty.abiAlignment(wasm.base.options.target);
11551169}
11561170
......@@ -1207,58 +1221,51 @@ pub fn lowerUnnamedConst(wasm: *Wasm, tv: TypedValue, decl_index: Module.Decl.In
12071221 const decl = mod.declPtr(decl_index);
12081222
12091223 // 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);
12111229 const fqdn = try decl.getFullyQualifiedName(mod);
12121230 defer wasm.base.allocator.free(fqdn);
12131231 const name = try std.fmt.allocPrintZ(wasm.base.allocator, "__unnamed_{s}_{d}", .{ fqdn, local_index });
12141232 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
12381233 var value_bytes = std.ArrayList(u8).init(wasm.base.allocator);
12391234 defer value_bytes.deinit();
12401235
1241 const result = try codegen.generateSymbol(
1242 &wasm.base,
1243 decl.srcLoc(),
1244 tv,
1245 &value_bytes,
1246 .none,
1247 .{
1248 .parent_atom_index = atom.sym_index,
1249 .addend = null,
1250 },
1251 );
1252 const code = switch (result) {
1253 .externally_managed => |x| x,
1254 .appended => value_bytes.items,
1255 .fail => |em| {
1256 decl.analysis = .codegen_failure;
1257 try mod.failed_decls.put(mod.gpa, decl_index, em);
1258 return error.AnalysisFail;
1259 },
1236 const code = code: {
1237 const atom = wasm.getAtomPtr(atom_index);
1238 atom.alignment = tv.ty.abiAlignment(wasm.base.options.target);
1239 wasm.symbols.items[atom.sym_index] = .{
1240 .name = try wasm.string_table.put(wasm.base.allocator, name),
1241 .flags = @enumToInt(Symbol.Flag.WASM_SYM_BINDING_LOCAL),
1242 .tag = .data,
1243 .index = undefined,
1244 };
1245 try wasm.resolved_symbols.putNoClobber(wasm.base.allocator, atom.symbolLoc(), {});
1246
1247 const result = try codegen.generateSymbol(
1248 &wasm.base,
1249 decl.srcLoc(),
1250 tv,
1251 &value_bytes,
1252 .none,
1253 .{
1254 .parent_atom_index = atom.sym_index,
1255 .addend = null,
1256 },
1257 );
1258 break :code switch (result) {
1259 .ok => value_bytes.items,
1260 .fail => |em| {
1261 decl.analysis = .codegen_failure;
1262 try mod.failed_decls.put(mod.gpa, decl_index, em);
1263 return error.AnalysisFail;
1264 },
1265 };
12601266 };
12611267
1268 const atom = wasm.getAtomPtr(atom_index);
12621269 atom.size = @intCast(u32, code.len);
12631270 try atom.code.appendSlice(wasm.base.allocator, code);
12641271 return atom.sym_index;
......@@ -1306,10 +1313,13 @@ pub fn getDeclVAddr(
13061313) !u64 {
13071314 const mod = wasm.base.options.module.?;
13081315 const decl = mod.declPtr(decl_index);
1309 const target_symbol_index = decl.link.wasm.sym_index;
1310 assert(target_symbol_index != 0);
1316
1317 const target_atom_index = try wasm.getOrCreateAtomForDecl(decl_index);
1318 const target_symbol_index = wasm.getAtom(target_atom_index).sym_index;
1319
13111320 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);
13131323 const is_wasm32 = wasm.base.options.target.cpu.arch == .wasm32;
13141324 if (decl.ty.zigTypeTag() == .Fn) {
13151325 assert(reloc_info.addend == 0); // addend not allowed for function relocations
......@@ -1337,9 +1347,10 @@ pub fn getDeclVAddr(
13371347 return target_symbol_index;
13381348}
13391349
1340pub fn deleteExport(wasm: *Wasm, exp: Export) void {
1350pub fn deleteDeclExport(wasm: *Wasm, decl_index: Module.Decl.Index) void {
13411351 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;
13431354 const loc: SymbolLoc = .{ .file = null, .index = sym_index };
13441355 const symbol = loc.getSymbol(wasm);
13451356 const symbol_name = wasm.string_table.get(symbol.name);
......@@ -1365,6 +1376,8 @@ pub fn updateDeclExports(
13651376 }
13661377
13671378 const decl = mod.declPtr(decl_index);
1379 const atom_index = try wasm.getOrCreateAtomForDecl(decl_index);
1380 const atom = wasm.getAtom(atom_index);
13681381
13691382 for (exports) |exp| {
13701383 if (exp.options.section) |section| {
......@@ -1379,7 +1392,7 @@ pub fn updateDeclExports(
13791392
13801393 const export_name = try wasm.string_table.put(wasm.base.allocator, exp.options.name);
13811394 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;
13831396 const existing_sym: Symbol = existing_loc.getSymbol(wasm).*;
13841397
13851398 const exp_is_weak = exp.options.linkage == .Internal or exp.options.linkage == .Weak;
......@@ -1400,15 +1413,16 @@ pub fn updateDeclExports(
14001413 } else if (exp_is_weak) {
14011414 continue; // to-be-exported symbol is weak, so we keep the existing symbol
14021415 } 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;
14041418 existing_loc.file = null;
1405 exp.link.wasm.sym_index = existing_loc.index;
1419 // exp.link.wasm.sym_index = existing_loc.index;
14061420 }
14071421 }
14081422
1409 const exported_decl = mod.declPtr(exp.exported_decl);
1410 const sym_index = exported_decl.link.wasm.sym_index;
1411 const sym_loc = exported_decl.link.wasm.symbolLoc();
1423 const exported_atom_index = try wasm.getOrCreateAtomForDecl(exp.exported_decl);
1424 const exported_atom = wasm.getAtom(exported_atom_index);
1425 const sym_loc = exported_atom.symbolLoc();
14121426 const symbol = sym_loc.getSymbol(wasm);
14131427 switch (exp.options.linkage) {
14141428 .Internal => {
......@@ -1444,7 +1458,6 @@ pub fn updateDeclExports(
14441458 // if the symbol was previously undefined, remove it as an import
14451459 _ = wasm.imports.remove(sym_loc);
14461460 _ = wasm.undefs.swapRemove(exp.options.name);
1447 exp.link.wasm.sym_index = sym_index;
14481461 }
14491462}
14501463
......@@ -1454,11 +1467,13 @@ pub fn freeDecl(wasm: *Wasm, decl_index: Module.Decl.Index) void {
14541467 }
14551468 const mod = wasm.base.options.module.?;
14561469 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);
14581472 wasm.symbols_free_list.append(wasm.base.allocator, atom.sym_index) catch {};
14591473 _ = wasm.decls.remove(decl_index);
14601474 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);
14621477 const local_symbol = &wasm.symbols.items[local_atom.sym_index];
14631478 local_symbol.tag = .dead; // also for any local symbol
14641479 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 {
14721487 _ = wasm.resolved_symbols.swapRemove(atom.symbolLoc());
14731488 _ = wasm.symbol_atom.remove(atom.symbolLoc());
14741489
1475 if (wasm.dwarf) |*dwarf| {
1476 dwarf.freeDecl(decl);
1477 dwarf.freeAtom(&atom.dbg_info_atom);
1478 }
1490 // if (wasm.dwarf) |*dwarf| {
1491 // dwarf.freeDecl(decl_index);
1492 // }
14791493
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 }
14811504}
14821505
14831506/// Appends a new entry to the indirect function table
......@@ -1599,7 +1622,8 @@ const Kind = union(enum) {
15991622};
16001623
16011624/// Parses an Atom and inserts its metadata into the corresponding sections.
1602fn parseAtom(wasm: *Wasm, atom: *Atom, kind: Kind) !void {
1625fn parseAtom(wasm: *Wasm, atom_index: Atom.Index, kind: Kind) !void {
1626 const atom = wasm.getAtomPtr(atom_index);
16031627 const symbol = (SymbolLoc{ .file = null, .index = atom.sym_index }).getSymbol(wasm);
16041628 const final_index: u32 = switch (kind) {
16051629 .function => |fn_data| result: {
......@@ -1674,18 +1698,20 @@ fn parseAtom(wasm: *Wasm, atom: *Atom, kind: Kind) !void {
16741698 const segment: *Segment = &wasm.segments.items[final_index];
16751699 segment.alignment = std.math.max(segment.alignment, atom.alignment);
16761700
1677 try wasm.appendAtomAtIndex(final_index, atom);
1701 try wasm.appendAtomAtIndex(final_index, atom_index);
16781702}
16791703
16801704/// From a given index, append the given `Atom` at the back of the linked list.
16811705/// Simply inserts it into the map of atoms when it doesn't exist yet.
1682pub fn appendAtomAtIndex(wasm: *Wasm, index: u32, atom: *Atom) !void {
1683 if (wasm.atoms.getPtr(index)) |last| {
1684 last.*.next = atom;
1685 atom.prev = last.*;
1686 last.* = atom;
1706pub fn appendAtomAtIndex(wasm: *Wasm, index: u32, atom_index: Atom.Index) !void {
1707 const atom = wasm.getAtomPtr(atom_index);
1708 if (wasm.atoms.getPtr(index)) |last_index_ptr| {
1709 const last = wasm.getAtomPtr(last_index_ptr.*);
1710 last.*.next = atom_index;
1711 atom.prev = last_index_ptr.*;
1712 last_index_ptr.* = atom_index;
16871713 } else {
1688 try wasm.atoms.putNoClobber(wasm.base.allocator, index, atom);
1714 try wasm.atoms.putNoClobber(wasm.base.allocator, index, atom_index);
16891715 }
16901716}
16911717
......@@ -1695,16 +1721,17 @@ fn allocateDebugAtoms(wasm: *Wasm) !void {
16951721 if (wasm.dwarf == null) return;
16961722
16971723 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 {
16991725 const index = maybe_index.* orelse idx: {
17001726 const index = @intCast(u32, bin.segments.items.len);
17011727 try bin.appendDummySegment();
17021728 maybe_index.* = index;
17031729 break :idx index;
17041730 };
1731 const atom = bin.getAtomPtr(atom_index);
17051732 atom.size = @intCast(u32, atom.code.items.len);
17061733 bin.symbols.items[atom.sym_index].index = index;
1707 try bin.appendAtomAtIndex(index, atom);
1734 try bin.appendAtomAtIndex(index, atom_index);
17081735 }
17091736 }.f;
17101737
......@@ -1726,15 +1753,16 @@ fn allocateAtoms(wasm: *Wasm) !void {
17261753 var it = wasm.atoms.iterator();
17271754 while (it.next()) |entry| {
17281755 const segment = &wasm.segments.items[entry.key_ptr.*];
1729 var atom: *Atom = entry.value_ptr.*.getFirst();
1756 var atom_index = entry.value_ptr.*;
17301757 var offset: u32 = 0;
17311758 while (true) {
1759 const atom = wasm.getAtomPtr(atom_index);
17321760 const symbol_loc = atom.symbolLoc();
17331761 if (wasm.code_section_index) |index| {
17341762 if (index == entry.key_ptr.*) {
17351763 if (!wasm.resolved_symbols.contains(symbol_loc)) {
17361764 // only allocate resolved function body's.
1737 atom = atom.next orelse break;
1765 atom_index = atom.prev orelse break;
17381766 continue;
17391767 }
17401768 }
......@@ -1748,8 +1776,7 @@ fn allocateAtoms(wasm: *Wasm) !void {
17481776 atom.size,
17491777 });
17501778 offset += atom.size;
1751 try wasm.symbol_atom.put(wasm.base.allocator, symbol_loc, atom); // Update atom pointers
1752 atom = atom.next orelse break;
1779 atom_index = atom.prev orelse break;
17531780 }
17541781 segment.size = std.mem.alignForwardGeneric(u32, offset, segment.alignment);
17551782 }
......@@ -1883,8 +1910,8 @@ fn initializeCallCtorsFunction(wasm: *Wasm) !void {
18831910 symbol.index = func_index;
18841911
18851912 // create the atom that will be output into the final binary
1886 const atom = try wasm.base.allocator.create(Atom);
1887 errdefer wasm.base.allocator.destroy(atom);
1913 const atom_index = @intCast(Atom.Index, wasm.managed_atoms.items.len);
1914 const atom = try wasm.managed_atoms.addOne(wasm.base.allocator);
18881915 atom.* = .{
18891916 .size = @intCast(u32, function_body.items.len),
18901917 .offset = 0,
......@@ -1894,15 +1921,14 @@ fn initializeCallCtorsFunction(wasm: *Wasm) !void {
18941921 .next = null,
18951922 .prev = null,
18961923 .code = function_body.moveToUnmanaged(),
1897 .dbg_info_atom = undefined,
18981924 };
1899 try wasm.managed_atoms.append(wasm.base.allocator, atom);
1900 try wasm.appendAtomAtIndex(wasm.code_section_index.?, atom);
1901 try wasm.symbol_atom.putNoClobber(wasm.base.allocator, loc, atom);
1925 try wasm.appendAtomAtIndex(wasm.code_section_index.?, atom_index);
1926 try wasm.symbol_atom.putNoClobber(wasm.base.allocator, loc, atom_index);
19021927
19031928 // `allocateAtoms` has already been called, set the atom's offset manually.
19041929 // 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;
19061932}
19071933
19081934fn setupImports(wasm: *Wasm) !void {
......@@ -2105,7 +2131,8 @@ fn setupExports(wasm: *Wasm) !void {
21052131 break :blk try wasm.string_table.put(wasm.base.allocator, sym_name);
21062132 };
21072133 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);
21092136 const va = atom.getVA(wasm, symbol);
21102137 const global_index = @intCast(u32, wasm.imported_globals_count + wasm.wasm_globals.items.len);
21112138 try wasm.wasm_globals.append(wasm.base.allocator, .{
......@@ -2210,7 +2237,8 @@ fn setupMemory(wasm: *Wasm) !void {
22102237 const segment_index = wasm.data_segments.get(".synthetic").?;
22112238 const segment = &wasm.segments.items[segment_index];
22122239 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);
22142242 atom.offset = @intCast(u32, mem.alignForwardGeneric(u64, memory_ptr, heap_alignment));
22152243 }
22162244
......@@ -2243,7 +2271,8 @@ fn setupMemory(wasm: *Wasm) !void {
22432271 const segment_index = wasm.data_segments.get(".synthetic").?;
22442272 const segment = &wasm.segments.items[segment_index];
22452273 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);
22472276 atom.offset = @intCast(u32, memory_ptr);
22482277 }
22492278
......@@ -2369,15 +2398,14 @@ pub fn getErrorTableSymbol(wasm: *Wasm) !u32 {
23692398 // and then return said symbol's index. The final table will be populated
23702399 // during `flush` when we know all possible error names.
23712400
2372 // As sym_index '0' is reserved, we use it for our stack pointer symbol
2373 const symbol_index = wasm.symbols_free_list.popOrNull() orelse blk: {
2374 const index = @intCast(u32, wasm.symbols.items.len);
2375 _ = try wasm.symbols.addOne(wasm.base.allocator);
2376 break :blk index;
2377 };
2401 const atom_index = try wasm.createAtom();
2402 const atom = wasm.getAtomPtr(atom_index);
2403 const slice_ty = Type.initTag(.const_slice_u8_sentinel_0);
2404 atom.alignment = slice_ty.abiAlignment(wasm.base.options.target);
2405 const sym_index = atom.sym_index;
23782406
23792407 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];
23812409 symbol.* = .{
23822410 .name = sym_name,
23832411 .tag = .data,
......@@ -2386,20 +2414,11 @@ pub fn getErrorTableSymbol(wasm: *Wasm) !u32 {
23862414 };
23872415 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
23882416
2389 const slice_ty = Type.initTag(.const_slice_u8_sentinel_0);
2417 try wasm.resolved_symbols.put(wasm.base.allocator, atom.symbolLoc(), {});
23902418
2391 const atom = try wasm.base.allocator.create(Atom);
2392 atom.* = Atom.empty;
2393 atom.sym_index = symbol_index;
2394 atom.alignment = slice_ty.abiAlignment(wasm.base.options.target);
2395 try wasm.managed_atoms.append(wasm.base.allocator, atom);
2396 const loc = atom.symbolLoc();
2397 try wasm.resolved_symbols.put(wasm.base.allocator, loc, {});
2398 try wasm.symbol_atom.put(wasm.base.allocator, loc, atom);
2399
2400 log.debug("Error name table was created with symbol index: ({d})", .{symbol_index});
2401 wasm.error_table_symbol = symbol_index;
2402 return symbol_index;
2419 log.debug("Error name table was created with symbol index: ({d})", .{sym_index});
2420 wasm.error_table_symbol = sym_index;
2421 return sym_index;
24032422}
24042423
24052424/// Populates the error name table, when `error_table_symbol` is not null.
......@@ -2408,22 +2427,17 @@ pub fn getErrorTableSymbol(wasm: *Wasm) !u32 {
24082427/// The table is what is being pointed to within the runtime bodies that are generated.
24092428fn populateErrorNameTable(wasm: *Wasm) !void {
24102429 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
24122433 // Rather than creating a symbol for each individual error name,
24132434 // we create a symbol for the entire region of error names. We then calculate
24142435 // the pointers into the list using addends which are appended to the relocation.
2415 const names_atom = try wasm.base.allocator.create(Atom);
2416 names_atom.* = Atom.empty;
2417 try wasm.managed_atoms.append(wasm.base.allocator, names_atom);
2418 const names_symbol_index = wasm.symbols_free_list.popOrNull() orelse blk: {
2419 const index = @intCast(u32, wasm.symbols.items.len);
2420 _ = try wasm.symbols.addOne(wasm.base.allocator);
2421 break :blk index;
2422 };
2423 names_atom.sym_index = names_symbol_index;
2436 const names_atom_index = try wasm.createAtom();
2437 const names_atom = wasm.getAtomPtr(names_atom_index);
24242438 names_atom.alignment = 1;
24252439 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];
24272441 names_symbol.* = .{
24282442 .name = sym_name,
24292443 .tag = .data,
......@@ -2447,7 +2461,7 @@ fn populateErrorNameTable(wasm: *Wasm) !void {
24472461 try atom.code.writer(wasm.base.allocator).writeIntLittle(u32, len - 1);
24482462 // create relocation to the error name
24492463 try atom.relocs.append(wasm.base.allocator, .{
2450 .index = names_symbol_index,
2464 .index = names_atom.sym_index,
24512465 .relocation_type = .R_WASM_MEMORY_ADDR_I32,
24522466 .offset = offset,
24532467 .addend = @intCast(i32, addend),
......@@ -2466,61 +2480,53 @@ fn populateErrorNameTable(wasm: *Wasm) !void {
24662480
24672481 const name_loc = names_atom.symbolLoc();
24682482 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);
24702484
24712485 // link the atoms with the rest of the binary so they can be allocated
24722486 // and relocations will be performed.
2473 try wasm.parseAtom(atom, .{ .data = .read_only });
2474 try wasm.parseAtom(names_atom, .{ .data = .read_only });
2487 try wasm.parseAtom(atom_index, .{ .data = .read_only });
2488 try wasm.parseAtom(names_atom_index, .{ .data = .read_only });
24752489}
24762490
24772491/// From a given index variable, creates a new debug section.
24782492/// This initializes the index, appends a new segment,
24792493/// and finally, creates a managed `Atom`.
2480pub fn createDebugSectionForIndex(wasm: *Wasm, index: *?u32, name: []const u8) !*Atom {
2494pub fn createDebugSectionForIndex(wasm: *Wasm, index: *?u32, name: []const u8) !Atom.Index {
24812495 const new_index = @intCast(u32, wasm.segments.items.len);
24822496 index.* = new_index;
24832497 try wasm.appendDummySegment();
24842498
2485 const sym_index = wasm.symbols_free_list.popOrNull() orelse idx: {
2486 const tmp_index = @intCast(u32, wasm.symbols.items.len);
2487 _ = try wasm.symbols.addOne(wasm.base.allocator);
2488 break :idx tmp_index;
2489 };
2490 wasm.symbols.items[sym_index] = .{
2499 const atom_index = try wasm.createAtom();
2500 const atom = wasm.getAtomPtr(atom_index);
2501 wasm.symbols.items[atom.sym_index] = .{
24912502 .tag = .section,
24922503 .name = try wasm.string_table.put(wasm.base.allocator, name),
24932504 .index = 0,
24942505 .flags = @enumToInt(Symbol.Flag.WASM_SYM_BINDING_LOCAL),
24952506 };
24962507
2497 const atom = try wasm.base.allocator.create(Atom);
2498 atom.* = Atom.empty;
24992508 atom.alignment = 1; // debug sections are always 1-byte-aligned
2500 atom.sym_index = sym_index;
2501 try wasm.managed_atoms.append(wasm.base.allocator, atom);
2502 try wasm.symbol_atom.put(wasm.base.allocator, atom.symbolLoc(), atom);
2503 return atom;
2509 return atom_index;
25042510}
25052511
25062512fn resetState(wasm: *Wasm) void {
25072513 for (wasm.segment_info.values()) |segment_info| {
25082514 wasm.base.allocator.free(segment_info.name);
25092515 }
2510 if (wasm.base.options.module) |mod| {
2511 var decl_it = wasm.decls.keyIterator();
2512 while (decl_it.next()) |decl_index_ptr| {
2513 const decl = mod.declPtr(decl_index_ptr.*);
2514 const atom = &decl.link.wasm;
2515 atom.next = null;
2516 atom.prev = null;
2517
2518 for (atom.locals.items) |*local_atom| {
2519 local_atom.next = null;
2520 local_atom.prev = null;
2521 }
2516
2517 var atom_it = wasm.decls.valueIterator();
2518 while (atom_it.next()) |atom_index| {
2519 const atom = wasm.getAtomPtr(atom_index.*);
2520 atom.next = null;
2521 atom.prev = null;
2522
2523 for (atom.locals.items) |local_atom_index| {
2524 const local_atom = wasm.getAtomPtr(local_atom_index);
2525 local_atom.next = null;
2526 local_atom.prev = null;
25222527 }
25232528 }
2529
25242530 wasm.functions.clearRetainingCapacity();
25252531 wasm.exports.clearRetainingCapacity();
25262532 wasm.segments.clearRetainingCapacity();
......@@ -2817,28 +2823,29 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
28172823 try wasm.setupStart();
28182824 try wasm.setupImports();
28192825 if (wasm.base.options.module) |mod| {
2820 var decl_it = wasm.decls.keyIterator();
2821 while (decl_it.next()) |decl_index_ptr| {
2822 const decl = mod.declPtr(decl_index_ptr.*);
2826 var decl_it = wasm.decls.iterator();
2827 while (decl_it.next()) |entry| {
2828 const decl = mod.declPtr(entry.key_ptr.*);
28232829 if (decl.isExtern()) continue;
2824 const atom = &decl.*.link.wasm;
2830 const atom_index = entry.value_ptr.*;
28252831 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.? });
28272833 } else if (decl.getVariable()) |variable| {
28282834 if (!variable.is_mutable) {
2829 try wasm.parseAtom(atom, .{ .data = .read_only });
2835 try wasm.parseAtom(atom_index, .{ .data = .read_only });
28302836 } else if (variable.init.isUndefDeep()) {
2831 try wasm.parseAtom(atom, .{ .data = .uninitialized });
2837 try wasm.parseAtom(atom_index, .{ .data = .uninitialized });
28322838 } else {
2833 try wasm.parseAtom(atom, .{ .data = .initialized });
2839 try wasm.parseAtom(atom_index, .{ .data = .initialized });
28342840 }
28352841 } else {
2836 try wasm.parseAtom(atom, .{ .data = .read_only });
2842 try wasm.parseAtom(atom_index, .{ .data = .read_only });
28372843 }
28382844
28392845 // also parse atoms for a decl's locals
2840 for (atom.locals.items) |*local_atom| {
2841 try wasm.parseAtom(local_atom, .{ .data = .read_only });
2846 const atom = wasm.getAtomPtr(atom_index);
2847 for (atom.locals.items) |local_atom_index| {
2848 try wasm.parseAtom(local_atom_index, .{ .data = .read_only });
28422849 }
28432850 }
28442851
......@@ -3083,20 +3090,22 @@ fn writeToFile(
30833090 var code_section_size: u32 = 0;
30843091 if (wasm.code_section_index) |code_index| {
30853092 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).?;
30873094
30883095 // The code section must be sorted in line with the function order.
30893096 var sorted_atoms = try std.ArrayList(*Atom).initCapacity(wasm.base.allocator, wasm.functions.count());
30903097 defer sorted_atoms.deinit();
30913098
30923099 while (true) {
3100 var atom = wasm.getAtomPtr(atom_index);
30933101 if (wasm.resolved_symbols.contains(atom.symbolLoc())) {
30943102 if (!is_obj) {
30953103 atom.resolveRelocs(wasm);
30963104 }
30973105 sorted_atoms.appendAssumeCapacity(atom);
30983106 }
3099 atom = atom.next orelse break;
3107 // atom = if (atom.prev) |prev| wasm.getAtomPtr(prev) else break;
3108 atom_index = atom.prev orelse break;
31003109 }
31013110
31023111 const atom_sort_fn = struct {
......@@ -3136,11 +3145,11 @@ fn writeToFile(
31363145 // do not output 'bss' section unless we import memory and therefore
31373146 // want to guarantee the data is zero initialized
31383147 if (!import_memory and std.mem.eql(u8, entry.key_ptr.*, ".bss")) continue;
3139 const atom_index = entry.value_ptr.*;
3140 const segment = wasm.segments.items[atom_index];
3148 const segment_index = entry.value_ptr.*;
3149 const segment = wasm.segments.items[segment_index];
31413150 if (segment.size == 0) continue; // do not emit empty segments
31423151 segment_count += 1;
3143 var atom: *Atom = wasm.atoms.getPtr(atom_index).?.*.getFirst();
3152 var atom_index = wasm.atoms.get(segment_index).?;
31443153
31453154 // flag and index to memory section (currently, there can only be 1 memory section in wasm)
31463155 try leb.writeULEB128(binary_writer, @as(u32, 0));
......@@ -3151,6 +3160,7 @@ fn writeToFile(
31513160 // fill in the offset table and the data segments
31523161 var current_offset: u32 = 0;
31533162 while (true) {
3163 const atom = wasm.getAtomPtr(atom_index);
31543164 if (!is_obj) {
31553165 atom.resolveRelocs(wasm);
31563166 }
......@@ -3166,8 +3176,8 @@ fn writeToFile(
31663176 try binary_writer.writeAll(atom.code.items);
31673177
31683178 current_offset += atom.size;
3169 if (atom.next) |next| {
3170 atom = next;
3179 if (atom.prev) |prev| {
3180 atom_index = prev;
31713181 } else {
31723182 // also pad with zeroes when last atom to ensure
31733183 // segments are aligned.
......@@ -3209,15 +3219,15 @@ fn writeToFile(
32093219 }
32103220
32113221 if (!wasm.base.options.strip) {
3212 if (wasm.dwarf) |*dwarf| {
3213 const mod = wasm.base.options.module.?;
3214 try dwarf.writeDbgAbbrev();
3215 // for debug info and ranges, the address is always 0,
3216 // as locations are always offsets relative to 'code' section.
3217 try dwarf.writeDbgInfoHeader(mod, 0, code_section_size);
3218 try dwarf.writeDbgAranges(0, code_section_size);
3219 try dwarf.writeDbgLineHeader();
3220 }
3222 // if (wasm.dwarf) |*dwarf| {
3223 // const mod = wasm.base.options.module.?;
3224 // try dwarf.writeDbgAbbrev();
3225 // // for debug info and ranges, the address is always 0,
3226 // // as locations are always offsets relative to 'code' section.
3227 // try dwarf.writeDbgInfoHeader(mod, 0, code_section_size);
3228 // try dwarf.writeDbgAranges(0, code_section_size);
3229 // try dwarf.writeDbgLineHeader();
3230 // }
32213231
32223232 var debug_bytes = std.ArrayList(u8).init(wasm.base.allocator);
32233233 defer debug_bytes.deinit();
......@@ -3240,11 +3250,11 @@ fn writeToFile(
32403250
32413251 for (debug_sections) |item| {
32423252 if (item.index) |index| {
3243 var atom = wasm.atoms.get(index).?.getFirst();
3253 var atom = wasm.getAtomPtr(wasm.atoms.get(index).?);
32443254 while (true) {
32453255 atom.resolveRelocs(wasm);
32463256 try debug_bytes.appendSlice(atom.code.items);
3247 atom = atom.next orelse break;
3257 atom = if (atom.prev) |prev| wasm.getAtomPtr(prev) else break;
32483258 }
32493259 try emitDebugSection(&binary_bytes, debug_bytes.items, item.name);
32503260 debug_bytes.clearRetainingCapacity();
......@@ -3976,7 +3986,8 @@ fn emitSymbolTable(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), symbol_table:
39763986
39773987 if (symbol.isDefined()) {
39783988 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);
39803991 try leb.writeULEB128(writer, @as(u32, atom.offset));
39813992 try leb.writeULEB128(writer, @as(u32, atom.size));
39823993 }
......@@ -4054,7 +4065,7 @@ fn emitCodeRelocations(
40544065 const reloc_start = binary_bytes.items.len;
40554066
40564067 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).?);
40584069 // for each atom, we calculate the uleb size and append that
40594070 var size_offset: u32 = 5; // account for code section size leb128
40604071 while (true) {
......@@ -4072,7 +4083,7 @@ fn emitCodeRelocations(
40724083 }
40734084 log.debug("Emit relocation: {}", .{relocation});
40744085 }
4075 atom = atom.next orelse break;
4086 atom = if (atom.prev) |prev| wasm.getAtomPtr(prev) else break;
40764087 }
40774088 if (count == 0) return;
40784089 var buf: [5]u8 = undefined;
......@@ -4103,7 +4114,7 @@ fn emitDataRelocations(
41034114 // for each atom, we calculate the uleb size and append that
41044115 var size_offset: u32 = 5; // account for code section size leb128
41054116 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).?);
41074118 while (true) {
41084119 size_offset += getULEB128Size(atom.size);
41094120 for (atom.relocs.items) |relocation| {
......@@ -4122,7 +4133,7 @@ fn emitDataRelocations(
41224133 }
41234134 log.debug("Emit relocation: {}", .{relocation});
41244135 }
4125 atom = atom.next orelse break;
4136 atom = if (atom.prev) |prev| wasm.getAtomPtr(prev) else break;
41264137 }
41274138 }
41284139 if (count == 0) return;
src/link/Wasm/Atom.zig+24-22
......@@ -4,7 +4,6 @@ const std = @import("std");
44const types = @import("types.zig");
55const Wasm = @import("../Wasm.zig");
66const Symbol = @import("Symbol.zig");
7const Dwarf = @import("../Dwarf.zig");
87
98const leb = std.leb;
109const log = std.log.scoped(.link);
......@@ -30,17 +29,17 @@ file: ?u16,
3029
3130/// Next atom in relation to this atom.
3231/// When null, this atom is the last atom
33next: ?*Atom,
32next: ?Atom.Index,
3433/// Previous atom in relation to this atom.
3534/// is null when this atom is the first in its order
36prev: ?*Atom,
35prev: ?Atom.Index,
3736
3837/// Contains atoms local to a decl, all managed by this `Atom`.
3938/// When the parent atom is being freed, it will also do so for all local atoms.
40locals: std.ArrayListUnmanaged(Atom) = .{},
39locals: std.ArrayListUnmanaged(Atom.Index) = .{},
4140
42/// Represents the debug Atom that holds all debug information of this Atom.
43dbg_info_atom: Dwarf.Atom,
41/// Alias to an unsigned 32-bit integer
42pub const Index = u32;
4443
4544/// Represents a default empty wasm `Atom`
4645pub const empty: Atom = .{
......@@ -51,18 +50,15 @@ pub const empty: Atom = .{
5150 .prev = null,
5251 .size = 0,
5352 .sym_index = 0,
54 .dbg_info_atom = undefined,
5553};
5654
5755/// Frees all resources owned by this `Atom`.
58pub fn deinit(atom: *Atom, gpa: Allocator) void {
56pub fn deinit(atom: *Atom, wasm: *Wasm) void {
57 const gpa = wasm.base.allocator;
5958 atom.relocs.deinit(gpa);
6059 atom.code.deinit(gpa);
61
62 for (atom.locals.items) |*local| {
63 local.deinit(gpa);
64 }
6560 atom.locals.deinit(gpa);
61 atom.* = undefined;
6662}
6763
6864/// 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
8379 });
8480}
8581
86/// Returns the first `Atom` from a given atom
87pub fn getFirst(atom: *Atom) *Atom {
88 var tmp = atom;
89 while (tmp.prev) |prev| tmp = prev;
90 return tmp;
91}
92
9382/// Returns the location of the symbol that represents this `Atom`
9483pub fn symbolLoc(atom: Atom) Wasm.SymbolLoc {
9584 return .{ .file = atom.file, .index = atom.sym_index };
9685}
9786
87pub fn getSymbolIndex(atom: Atom) ?u32 {
88 if (atom.sym_index == 0) return null;
89 return atom.sym_index;
90}
91
9892/// Returns the virtual address of the `Atom`. This is the address starting
9993/// from the first entry within a section.
10094pub 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
192186 if (symbol.isUndefined()) {
193187 return 0;
194188 }
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);
196196 const va = @intCast(i32, target_atom.getVA(wasm_bin, symbol));
197197 return @intCast(u32, va + relocation.addend);
198198 },
199199 .R_WASM_EVENT_INDEX_LEB => return symbol.index,
200200 .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);
202203 const rel_value = @intCast(i32, target_atom.offset) + relocation.addend;
203204 return @intCast(u32, rel_value);
204205 },
205206 .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 {
207208 return @bitCast(u32, @as(i32, -1));
208209 };
210 const target_atom = wasm_bin.getAtom(target_atom_index);
209211 const offset: u32 = 11 + Wasm.getULEB128Size(target_atom.size); // Header (11 bytes fixed-size) + body size (leb-encoded)
210212 const rel_value = @intCast(i32, target_atom.offset + offset) + relocation.addend;
211213 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
901901 continue; // found unknown section, so skip parsing into atom as we do not know how to handle it.
902902 };
903903
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);
905906 atom.* = Atom.empty;
906 errdefer {
907 atom.deinit(gpa);
908 gpa.destroy(atom);
909 }
910
911 try wasm_bin.managed_atoms.append(gpa, atom);
912907 atom.file = object_index;
913908 atom.size = relocatable_data.size;
914909 atom.alignment = relocatable_data.getAlignment(object);
......@@ -938,12 +933,12 @@ pub fn parseIntoAtoms(object: *Object, gpa: Allocator, object_index: u16, wasm_b
938933 .index = relocatable_data.getIndex(),
939934 })) |symbols| {
940935 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);
942937
943938 // symbols referencing the same atom will be added as alias
944939 // or as 'parent' when they are global.
945940 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);
947942 const alias_symbol = object.symtable[idx];
948943 if (alias_symbol.isGlobal()) {
949944 atom.sym_index = idx;
......@@ -956,7 +951,7 @@ pub fn parseIntoAtoms(object: *Object, gpa: Allocator, object_index: u16, wasm_b
956951 segment.alignment = std.math.max(segment.alignment, atom.alignment);
957952 }
958953
959 try wasm_bin.appendAtomAtIndex(final_index, atom);
954 try wasm_bin.appendAtomAtIndex(final_index, atom_index);
960955 log.debug("Parsed into atom: '{s}' at segment index {d}", .{ object.string_table.get(object.symtable[atom.sym_index].name), final_index });
961956 }
962957}
src/main.zig+19-16
......@@ -893,7 +893,7 @@ fn buildOutputType(
893893 i: usize = 0,
894894 fn next(it: *@This()) ?[]const u8 {
895895 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();
897897 return null;
898898 }
899899 defer it.i += 1;
......@@ -901,7 +901,7 @@ fn buildOutputType(
901901 }
902902 fn nextOrFatal(it: *@This()) []const u8 {
903903 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;
905905 fatal("expected parameter after {s}", .{it.args[it.i - 1]});
906906 }
907907 defer it.i += 1;
......@@ -3915,6 +3915,7 @@ pub const usage_build =
39153915;
39163916
39173917pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
3918 var color: Color = .auto;
39183919 var prominent_compile_errors: bool = false;
39193920
39203921 // 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
41174118 // Here we borrow main package's table and will replace it with a fresh
41184119 // one after this process completes.
41194120 main_pkg.fetchAndAddDependencies(
4121 arena,
41204122 &thread_pool,
41214123 &http_client,
41224124 build_directory,
......@@ -4125,6 +4127,7 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
41254127 &dependencies_source,
41264128 &build_roots_source,
41274129 "",
4130 color,
41284131 ) catch |err| switch (err) {
41294132 error.PackageFetchFailed => process.exit(1),
41304133 else => |e| return e,
......@@ -4361,12 +4364,12 @@ pub fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void
43614364 };
43624365 defer gpa.free(source_code);
43634366
4364 var tree = std.zig.parse(gpa, source_code) catch |err| {
4367 var tree = Ast.parse(gpa, source_code, .zig) catch |err| {
43654368 fatal("error parsing stdin: {}", .{err});
43664369 };
43674370 defer tree.deinit(gpa);
43684371
4369 try printErrsMsgToStdErr(gpa, arena, tree.errors, tree, "<stdin>", color);
4372 try printErrsMsgToStdErr(gpa, arena, tree, "<stdin>", color);
43704373 var has_ast_error = false;
43714374 if (check_ast_flag) {
43724375 const Module = @import("Module.zig");
......@@ -4566,10 +4569,10 @@ fn fmtPathFile(
45664569 // Add to set after no longer possible to get error.IsDir.
45674570 if (try fmt.seen.fetchPut(stat.inode, {})) |_| return;
45684571
4569 var tree = try std.zig.parse(fmt.gpa, source_code);
4572 var tree = try Ast.parse(fmt.gpa, source_code, .zig);
45704573 defer tree.deinit(fmt.gpa);
45714574
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);
45734576 if (tree.errors.len != 0) {
45744577 fmt.any_error = true;
45754578 return;
......@@ -4649,14 +4652,14 @@ fn fmtPathFile(
46494652 }
46504653}
46514654
4652fn printErrsMsgToStdErr(
4655pub fn printErrsMsgToStdErr(
46534656 gpa: mem.Allocator,
46544657 arena: mem.Allocator,
4655 parse_errors: []const Ast.Error,
46564658 tree: Ast,
46574659 path: []const u8,
46584660 color: Color,
46594661) !void {
4662 const parse_errors: []const Ast.Error = tree.errors;
46604663 var i: usize = 0;
46614664 while (i < parse_errors.len) : (i += 1) {
46624665 const parse_error = parse_errors[i];
......@@ -4973,7 +4976,7 @@ pub const ClangArgIterator = struct {
49734976 // rather than an argument to a parameter.
49744977 // We adjust the len below when necessary.
49754978 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];
49774980 self.incrementArgIndex();
49784981
49794982 if (mem.startsWith(u8, arg, "@")) {
......@@ -5017,7 +5020,7 @@ pub const ClangArgIterator = struct {
50175020
50185021 self.has_next = true;
50195022 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];
50215024 self.incrementArgIndex();
50225025 }
50235026
......@@ -5312,11 +5315,11 @@ pub fn cmdAstCheck(
53125315 file.pkg = try Package.create(gpa, "root", null, file.sub_file_path);
53135316 defer file.pkg.destroy(gpa);
53145317
5315 file.tree = try std.zig.parse(gpa, file.source);
5318 file.tree = try Ast.parse(gpa, file.source, .zig);
53165319 file.tree_loaded = true;
53175320 defer file.tree.deinit(gpa);
53185321
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);
53205323 if (file.tree.errors.len != 0) {
53215324 process.exit(1);
53225325 }
......@@ -5438,11 +5441,11 @@ pub fn cmdChangelist(
54385441 file.source = source;
54395442 file.source_loaded = true;
54405443
5441 file.tree = try std.zig.parse(gpa, file.source);
5444 file.tree = try Ast.parse(gpa, file.source, .zig);
54425445 file.tree_loaded = true;
54435446 defer file.tree.deinit(gpa);
54445447
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);
54465449 if (file.tree.errors.len != 0) {
54475450 process.exit(1);
54485451 }
......@@ -5476,10 +5479,10 @@ pub fn cmdChangelist(
54765479 if (new_amt != new_stat.size)
54775480 return error.UnexpectedEndOfFile;
54785481
5479 var new_tree = try std.zig.parse(gpa, new_source);
5482 var new_tree = try Ast.parse(gpa, new_source, .zig);
54805483 defer new_tree.deinit(gpa);
54815484
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);
54835486 if (new_tree.errors.len != 0) {
54845487 process.exit(1);
54855488 }
src/mingw.zig+1
......@@ -106,6 +106,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
106106 .msvcrt_os_lib => {
107107 const extra_flags = try arena.dupe([]const u8, &[_][]const u8{
108108 "-DHAVE_CONFIG_H",
109 "-D__LIBMSVCRT__",
109110 "-D__LIBMSVCRT_OS__",
110111
111112 "-I",
src/print_zir.zig+1
......@@ -332,6 +332,7 @@ const Writer = struct {
332332 .float_cast,
333333 .int_cast,
334334 .ptr_cast,
335 .qual_cast,
335336 .truncate,
336337 .align_cast,
337338 .div_exact,
src/translate_c.zig+4-1
......@@ -4519,7 +4519,10 @@ fn transCreateNodeAssign(
45194519 defer block_scope.deinit();
45204520
45214521 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 }
45234526 const tmp_decl = try Tag.var_simple.create(c.arena, .{ .name = tmp, .init = rhs_node });
45244527 try block_scope.statements.append(tmp_decl);
45254528
src/type.zig+45-586
......@@ -2937,24 +2937,24 @@ pub const Type = extern union {
29372937 .anyframe_T,
29382938 => return AbiAlignmentAdvanced{ .scalar = @divExact(target.cpu.arch.ptrBitWidth(), 8) },
29392939
2940 .c_short => return AbiAlignmentAdvanced{ .scalar = CType.short.alignment(target) },
2941 .c_ushort => return AbiAlignmentAdvanced{ .scalar = CType.ushort.alignment(target) },
2942 .c_int => return AbiAlignmentAdvanced{ .scalar = CType.int.alignment(target) },
2943 .c_uint => return AbiAlignmentAdvanced{ .scalar = CType.uint.alignment(target) },
2944 .c_long => return AbiAlignmentAdvanced{ .scalar = CType.long.alignment(target) },
2945 .c_ulong => return AbiAlignmentAdvanced{ .scalar = CType.ulong.alignment(target) },
2946 .c_longlong => return AbiAlignmentAdvanced{ .scalar = CType.longlong.alignment(target) },
2947 .c_ulonglong => return AbiAlignmentAdvanced{ .scalar = CType.ulonglong.alignment(target) },
2948 .c_longdouble => return AbiAlignmentAdvanced{ .scalar = CType.longdouble.alignment(target) },
2940 .c_short => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.short) },
2941 .c_ushort => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.ushort) },
2942 .c_int => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.int) },
2943 .c_uint => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.uint) },
2944 .c_long => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.long) },
2945 .c_ulong => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.ulong) },
2946 .c_longlong => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.longlong) },
2947 .c_ulonglong => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.ulonglong) },
2948 .c_longdouble => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.longdouble) },
29492949
29502950 .f16 => return AbiAlignmentAdvanced{ .scalar = 2 },
2951 .f32 => return AbiAlignmentAdvanced{ .scalar = CType.float.alignment(target) },
2952 .f64 => switch (CType.double.sizeInBits(target)) {
2953 64 => return AbiAlignmentAdvanced{ .scalar = CType.double.alignment(target) },
2951 .f32 => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.float) },
2952 .f64 => switch (target.c_type_bit_size(.double)) {
2953 64 => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.double) },
29542954 else => return AbiAlignmentAdvanced{ .scalar = 8 },
29552955 },
2956 .f80 => switch (CType.longdouble.sizeInBits(target)) {
2957 80 => return AbiAlignmentAdvanced{ .scalar = CType.longdouble.alignment(target) },
2956 .f80 => switch (target.c_type_bit_size(.longdouble)) {
2957 80 => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.longdouble) },
29582958 else => {
29592959 var payload: Payload.Bits = .{
29602960 .base = .{ .tag = .int_unsigned },
......@@ -2964,8 +2964,8 @@ pub const Type = extern union {
29642964 return AbiAlignmentAdvanced{ .scalar = abiAlignment(u80_ty, target) };
29652965 },
29662966 },
2967 .f128 => switch (CType.longdouble.sizeInBits(target)) {
2968 128 => return AbiAlignmentAdvanced{ .scalar = CType.longdouble.alignment(target) },
2967 .f128 => switch (target.c_type_bit_size(.longdouble)) {
2968 128 => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.longdouble) },
29692969 else => return AbiAlignmentAdvanced{ .scalar = 16 },
29702970 },
29712971
......@@ -3434,21 +3434,22 @@ pub const Type = extern union {
34343434 else => return AbiSizeAdvanced{ .scalar = @divExact(target.cpu.arch.ptrBitWidth(), 8) },
34353435 },
34363436
3437 .c_short => return AbiSizeAdvanced{ .scalar = @divExact(CType.short.sizeInBits(target), 8) },
3438 .c_ushort => return AbiSizeAdvanced{ .scalar = @divExact(CType.ushort.sizeInBits(target), 8) },
3439 .c_int => return AbiSizeAdvanced{ .scalar = @divExact(CType.int.sizeInBits(target), 8) },
3440 .c_uint => return AbiSizeAdvanced{ .scalar = @divExact(CType.uint.sizeInBits(target), 8) },
3441 .c_long => return AbiSizeAdvanced{ .scalar = @divExact(CType.long.sizeInBits(target), 8) },
3442 .c_ulong => return AbiSizeAdvanced{ .scalar = @divExact(CType.ulong.sizeInBits(target), 8) },
3443 .c_longlong => return AbiSizeAdvanced{ .scalar = @divExact(CType.longlong.sizeInBits(target), 8) },
3444 .c_ulonglong => return AbiSizeAdvanced{ .scalar = @divExact(CType.ulonglong.sizeInBits(target), 8) },
3437 .c_short => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.short) },
3438 .c_ushort => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.ushort) },
3439 .c_int => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.int) },
3440 .c_uint => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.uint) },
3441 .c_long => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.long) },
3442 .c_ulong => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.ulong) },
3443 .c_longlong => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.longlong) },
3444 .c_ulonglong => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.ulonglong) },
3445 .c_longdouble => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.longdouble) },
34453446
34463447 .f16 => return AbiSizeAdvanced{ .scalar = 2 },
34473448 .f32 => return AbiSizeAdvanced{ .scalar = 4 },
34483449 .f64 => return AbiSizeAdvanced{ .scalar = 8 },
34493450 .f128 => return AbiSizeAdvanced{ .scalar = 16 },
3450 .f80 => switch (CType.longdouble.sizeInBits(target)) {
3451 80 => return AbiSizeAdvanced{ .scalar = std.mem.alignForward(10, CType.longdouble.alignment(target)) },
3451 .f80 => switch (target.c_type_bit_size(.longdouble)) {
3452 80 => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.longdouble) },
34523453 else => {
34533454 var payload: Payload.Bits = .{
34543455 .base = .{ .tag = .int_unsigned },
......@@ -3458,14 +3459,6 @@ pub const Type = extern union {
34583459 return AbiSizeAdvanced{ .scalar = abiSize(u80_ty, target) };
34593460 },
34603461 },
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 },
34693462
34703463 // TODO revisit this when we have the concept of the error tag type
34713464 .anyerror_void_error_union,
......@@ -3748,15 +3741,15 @@ pub const Type = extern union {
37483741 .manyptr_const_u8_sentinel_0,
37493742 => return target.cpu.arch.ptrBitWidth(),
37503743
3751 .c_short => return CType.short.sizeInBits(target),
3752 .c_ushort => return CType.ushort.sizeInBits(target),
3753 .c_int => return CType.int.sizeInBits(target),
3754 .c_uint => return CType.uint.sizeInBits(target),
3755 .c_long => return CType.long.sizeInBits(target),
3756 .c_ulong => return CType.ulong.sizeInBits(target),
3757 .c_longlong => return CType.longlong.sizeInBits(target),
3758 .c_ulonglong => return CType.ulonglong.sizeInBits(target),
3759 .c_longdouble => return CType.longdouble.sizeInBits(target),
3744 .c_short => return target.c_type_bit_size(.short),
3745 .c_ushort => return target.c_type_bit_size(.ushort),
3746 .c_int => return target.c_type_bit_size(.int),
3747 .c_uint => return target.c_type_bit_size(.uint),
3748 .c_long => return target.c_type_bit_size(.long),
3749 .c_ulong => return target.c_type_bit_size(.ulong),
3750 .c_longlong => return target.c_type_bit_size(.longlong),
3751 .c_ulonglong => return target.c_type_bit_size(.ulonglong),
3752 .c_longdouble => return target.c_type_bit_size(.longdouble),
37603753
37613754 .error_set,
37623755 .error_set_single,
......@@ -4631,14 +4624,14 @@ pub const Type = extern union {
46314624 .i128 => return .{ .signedness = .signed, .bits = 128 },
46324625 .usize => return .{ .signedness = .unsigned, .bits = target.cpu.arch.ptrBitWidth() },
46334626 .isize => return .{ .signedness = .signed, .bits = target.cpu.arch.ptrBitWidth() },
4634 .c_short => return .{ .signedness = .signed, .bits = CType.short.sizeInBits(target) },
4635 .c_ushort => return .{ .signedness = .unsigned, .bits = CType.ushort.sizeInBits(target) },
4636 .c_int => return .{ .signedness = .signed, .bits = CType.int.sizeInBits(target) },
4637 .c_uint => return .{ .signedness = .unsigned, .bits = CType.uint.sizeInBits(target) },
4638 .c_long => return .{ .signedness = .signed, .bits = CType.long.sizeInBits(target) },
4639 .c_ulong => return .{ .signedness = .unsigned, .bits = CType.ulong.sizeInBits(target) },
4640 .c_longlong => return .{ .signedness = .signed, .bits = CType.longlong.sizeInBits(target) },
4641 .c_ulonglong => return .{ .signedness = .unsigned, .bits = CType.ulonglong.sizeInBits(target) },
4627 .c_short => return .{ .signedness = .signed, .bits = target.c_type_bit_size(.short) },
4628 .c_ushort => return .{ .signedness = .unsigned, .bits = target.c_type_bit_size(.ushort) },
4629 .c_int => return .{ .signedness = .signed, .bits = target.c_type_bit_size(.int) },
4630 .c_uint => return .{ .signedness = .unsigned, .bits = target.c_type_bit_size(.uint) },
4631 .c_long => return .{ .signedness = .signed, .bits = target.c_type_bit_size(.long) },
4632 .c_ulong => return .{ .signedness = .unsigned, .bits = target.c_type_bit_size(.ulong) },
4633 .c_longlong => return .{ .signedness = .signed, .bits = target.c_type_bit_size(.longlong) },
4634 .c_ulonglong => return .{ .signedness = .unsigned, .bits = target.c_type_bit_size(.ulonglong) },
46424635
46434636 .enum_full, .enum_nonexhaustive => ty = ty.cast(Payload.EnumFull).?.data.tag_ty,
46444637 .enum_numbered => ty = ty.castTag(.enum_numbered).?.data.tag_ty,
......@@ -4724,7 +4717,7 @@ pub const Type = extern union {
47244717 .f64 => 64,
47254718 .f80 => 80,
47264719 .f128, .comptime_float => 128,
4727 .c_longdouble => CType.longdouble.sizeInBits(target),
4720 .c_longdouble => target.c_type_bit_size(.longdouble),
47284721
47294722 else => unreachable,
47304723 };
......@@ -6689,537 +6682,3 @@ pub const Type = extern union {
66896682 /// to packed struct layout to find out all the places in the codebase you need to edit!
66906683 pub const packed_struct_layout_version = 2;
66916684};
6692
6693pub 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" {
703703 comptime try expect(@TypeOf(a) == *const [12:0]u8);
704704 comptime try expect(@TypeOf(b) == *const [12:0]u8);
705705
706 const len = mem.len(b);
706 const len = b.len;
707707 const len_with_null = len + 1;
708708 {
709709 var i: u32 = 0;
......@@ -1125,3 +1125,21 @@ test "returning an opaque type from a function" {
11251125 };
11261126 try expect(S.foo(123).b == 123);
11271127}
1128
1129test "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 {
11791179test "implicitly cast from [N]T to ?[]const T" {
11801180 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
11811181 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1182 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
11831182 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
11841183
11851184 try expect(mem.eql(u8, castToOptionalSlice().?, "hi"));
......@@ -1264,7 +1263,6 @@ test "cast from array reference to fn: runtime fn ptr" {
12641263test "*const [N]null u8 to ?[]const u8" {
12651264 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
12661265 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1267 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
12681266 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
12691267
12701268 const S = struct {
......@@ -1413,7 +1411,6 @@ test "cast i8 fn call peers to i32 result" {
14131411test "cast compatible optional types" {
14141412 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
14151413 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1416 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
14171414 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
14181415
14191416 var a: ?[:0]const u8 = null;
test/behavior/error.zig+15
......@@ -896,3 +896,18 @@ test "optional error union return type" {
896896 };
897897 try expect(1234 == try S.foo().?);
898898}
899
900test "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" {
13321332 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
13331333 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
13341334 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
13361335
13371336 comptime try frem(f16);
13381337 comptime try frem(f32);
......@@ -1375,7 +1374,6 @@ test "float modulo division using @mod" {
13751374 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
13761375 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
13771376 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
13791377
13801378 comptime try fmod(f16);
13811379 comptime try fmod(f32);
......@@ -1438,7 +1436,6 @@ test "@round f80" {
14381436 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
14391437 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
14401438 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
14421439
14431440 try testRound(f80, 12.0);
14441441 comptime try testRound(f80, 12.0);
test/behavior/muladd.zig-2
......@@ -50,7 +50,6 @@ test "@mulAdd f80" {
5050 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
5151 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
5252 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
5453
5554 comptime try testMulAdd80();
5655 try testMulAdd80();
......@@ -178,7 +177,6 @@ test "vector f80" {
178177 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
179178 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
180179 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
182180
183181 comptime try vector80();
184182 try vector80();
test/behavior/optional.zig-2
......@@ -439,7 +439,6 @@ test "Optional slice size is optimized" {
439439 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
440440 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
441441 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
442 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
443442 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
444443
445444 try expect(@sizeOf(?[]u8) == @sizeOf([]u8));
......@@ -479,7 +478,6 @@ test "cast slice to const slice nested in error union and optional" {
479478 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
480479 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
481480 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
482 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
483481
484482 const S = struct {
485483 fn inner() !?[]u8 {
test/behavior/sizeof_and_typeof.zig+9
......@@ -292,3 +292,12 @@ test "@sizeOf optional of previously unresolved union" {
292292 const Node = union { a: usize };
293293 try expect(@sizeOf(?Node) == @sizeOf(Node) + @alignOf(Node));
294294}
295
296test "@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" {
77 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
88 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
99 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
10 if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch != .x86_64) return error.SkipZigTest; // TODO
11 if (builtin.zig_backend == .stage2_c and builtin.os.tag == .windows) return error.SkipZigTest; // TODO
10 if (builtin.zig_backend == .stage2_llvm) switch (builtin.cpu.arch) {
11 .x86_64, .x86 => {},
12 else => return error.SkipZigTest,
13 }; // TODO
1214 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1315
1416 const S = struct {
......@@ -23,8 +25,10 @@ test "pointer to thread local array" {
2325 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
2426 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
2527 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
26 if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch != .x86_64) return error.SkipZigTest; // TODO
27 if (builtin.zig_backend == .stage2_c and builtin.os.tag == .windows) return error.SkipZigTest; // TODO
28 if (builtin.zig_backend == .stage2_llvm) switch (builtin.cpu.arch) {
29 .x86_64, .x86 => {},
30 else => return error.SkipZigTest,
31 }; // TODO
2832 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
2933
3034 const s = "Hello world";
......@@ -39,8 +43,10 @@ test "reference a global threadlocal variable" {
3943 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
4044 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
4145 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
42 if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch != .x86_64) return error.SkipZigTest; // TODO
43 if (builtin.zig_backend == .stage2_c and builtin.os.tag == .windows) return error.SkipZigTest; // TODO
46 if (builtin.zig_backend == .stage2_llvm) switch (builtin.cpu.arch) {
47 .x86_64, .x86 => {},
48 else => return error.SkipZigTest,
49 }; // TODO
4450 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
4551
4652 _ = 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 {
2020//
2121// :11:27: error: expected type 'u8', found '?u8'
2222// :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 @@
1const std = @import("std");
2const MyStruct = struct {
3 a: i32,
4 b: i32,
5
6 pub fn getA(self: *List) i32 {
7 return self.items(.c);
8 }
9};
10const List = std.MultiArrayList(MyStruct);
11pub 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 @@
1export 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 {
1010// target=native
1111//
1212// :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 {
2626// :11:15: error: expected type 'u32', found '@typeInfo(@typeInfo(@TypeOf(tmp.bar)).Fn.return_type.?).ErrorUnion.error_set!u32'
2727// :10:17: note: function cannot return an error
2828// :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'
3030// :15:14: error: expected type 'u32', found '@typeInfo(@typeInfo(@TypeOf(tmp.bar)).Fn.return_type.?).ErrorUnion.error_set!u32'
3131// :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; }
88// target=native
99//
1010// :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 {
1818// target=native
1919//
2020// :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'
2222// :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'
2424// :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 {
1111// :3:17: error: cast increases pointer alignment
1212// :3:32: note: '*u8' has alignment '1'
1313// :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 @@
1inline fn needComptime(comptime a: u64) void {
2 if (a != 0) @compileError("foo");
3}
4fn acceptRuntime(value: u64) void {
5 needComptime(value);
6}
7pub 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 @@
1export 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 @@
11const builtin = @import("std").builtin;
22export fn entry() void {
3 const foo = builtin.Mode.x86;
3 const foo = builtin.OptimizeMode.x86;
44 _ = foo;
55}
66
......@@ -8,5 +8,5 @@ export fn entry() void {
88// backend=stage2
99// target=native
1010//
11// :3:30: error: enum 'builtin.Mode' has no member named 'x86'
11// :3:38: error: enum 'builtin.OptimizeMode' has no member named 'x86'
1212// :?:18: note: enum declared here
test/cases/compile_errors/invalid_qualcast.zig created+12
......@@ -0,0 +1,12 @@
1pub 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 {
1010//
1111// :4:9: error: expected type '*anyopaque', found '?*anyopaque'
1212// :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'
1414// :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 {
99// target=native
1010//
1111// :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 {
2020//
2121// :12:25: error: expected type 'u32', found '@typeInfo(@typeInfo(@TypeOf(tmp.get_uval)).Fn.return_type.?).ErrorUnion.error_set!u32'
2222// :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 {
1717//
1818// :3:36: error: expected type 'i32', found '?i32'
1919// :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 {
1717//
1818// :3:36: error: expected type 'i32', found '?i32'
1919// :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 @@
1const Builder = @import("std").build.Builder;
1const std = @import("std");
22
3pub fn build(b: *Builder) void {
4 const mode = b.standardReleaseOptions();
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
55 const test_step = b.step("test", "Test");
66
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 });
812 b.default_step.dependOn(&exe.step);
9 exe.setBuildMode(mode);
1013
1114 const run = exe.run();
1215 run.expectStdOutEqual("0, 1, 0\n");
test/link/common_symbols/build.zig+12-7
......@@ -1,14 +1,19 @@
1const Builder = @import("std").build.Builder;
1const std = @import("std");
22
3pub fn build(b: *Builder) void {
4 const mode = b.standardReleaseOptions();
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
55
6 const lib_a = b.addStaticLibrary("a", null);
6 const lib_a = b.addStaticLibrary(.{
7 .name = "a",
8 .optimize = optimize,
9 .target = .{},
10 });
711 lib_a.addCSourceFiles(&.{ "c.c", "a.c", "b.c" }, &.{"-fcommon"});
8 lib_a.setBuildMode(mode);
912
10 const test_exe = b.addTest("main.zig");
11 test_exe.setBuildMode(mode);
13 const test_exe = b.addTest(.{
14 .root_source_file = .{ .path = "main.zig" },
15 .optimize = optimize,
16 });
1217 test_exe.linkLibrary(lib_a);
1318
1419 const test_step = b.step("test", "Test it");
test/link/common_symbols_alignment/build.zig+14-7
......@@ -1,14 +1,21 @@
1const Builder = @import("std").build.Builder;
1const std = @import("std");
22
3pub fn build(b: *Builder) void {
4 const mode = b.standardReleaseOptions();
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
5 const target = b.standardTargetOptions(.{});
56
6 const lib_a = b.addStaticLibrary("a", null);
7 const lib_a = b.addStaticLibrary(.{
8 .name = "a",
9 .optimize = optimize,
10 .target = target,
11 });
712 lib_a.addCSourceFiles(&.{"a.c"}, &.{"-fcommon"});
8 lib_a.setBuildMode(mode);
913
10 const test_exe = b.addTest("main.zig");
11 test_exe.setBuildMode(mode);
14 const test_exe = b.addTest(.{
15 .root_source_file = .{ .path = "main.zig" },
16 .optimize = optimize,
17 .target = target,
18 });
1219 test_exe.linkLibrary(lib_a);
1320
1421 const test_step = b.step("test", "Test it");
test/link/interdependent_static_c_libs/build.zig+19-9
......@@ -1,20 +1,30 @@
1const Builder = @import("std").build.Builder;
1const std = @import("std");
22
3pub fn build(b: *Builder) void {
4 const mode = b.standardReleaseOptions();
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
5 const target = b.standardTargetOptions(.{});
56
6 const lib_a = b.addStaticLibrary("a", null);
7 const lib_a = b.addStaticLibrary(.{
8 .name = "a",
9 .optimize = optimize,
10 .target = target,
11 });
712 lib_a.addCSourceFile("a.c", &[_][]const u8{});
8 lib_a.setBuildMode(mode);
913 lib_a.addIncludePath(".");
1014
11 const lib_b = b.addStaticLibrary("b", null);
15 const lib_b = b.addStaticLibrary(.{
16 .name = "b",
17 .optimize = optimize,
18 .target = target,
19 });
1220 lib_b.addCSourceFile("b.c", &[_][]const u8{});
13 lib_b.setBuildMode(mode);
1421 lib_b.addIncludePath(".");
1522
16 const test_exe = b.addTest("main.zig");
17 test_exe.setBuildMode(mode);
23 const test_exe = b.addTest(.{
24 .root_source_file = .{ .path = "main.zig" },
25 .optimize = optimize,
26 .target = target,
27 });
1828 test_exe.linkLibrary(lib_a);
1929 test_exe.linkLibrary(lib_b);
2030 test_exe.addIncludePath(".");
test/link/macho/bugs/13056/build.zig+6-5
......@@ -1,8 +1,7 @@
11const std = @import("std");
2const Builder = std.build.Builder;
32
4pub fn build(b: *Builder) void {
5 const mode = b.standardReleaseOptions();
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
65
76 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
87 const target_info = std.zig.system.NativeTargetInfo.detect(target) catch unreachable;
......@@ -11,7 +10,10 @@ pub fn build(b: *Builder) void {
1110
1211 const test_step = b.step("test", "Test the program");
1312
14 const exe = b.addExecutable("test", null);
13 const exe = b.addExecutable(.{
14 .name = "test",
15 .optimize = optimize,
16 });
1517 b.default_step.dependOn(&exe.step);
1618 exe.addIncludePath(std.fs.path.join(b.allocator, &.{ sdk.path, "/usr/include" }) catch unreachable);
1719 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 {
2022 "-nostdinc++",
2123 });
2224 exe.addObjectFile(std.fs.path.join(b.allocator, &.{ sdk.path, "/usr/lib/libc++.tbd" }) catch unreachable);
23 exe.setBuildMode(mode);
2425
2526 const run_cmd = exe.run();
2627 run_cmd.expectStdErrEqual("x: 5\n");
test/link/macho/bugs/13457/build.zig+8-7
......@@ -1,16 +1,17 @@
11const std = @import("std");
2const Builder = std.build.Builder;
3const LibExeObjectStep = std.build.LibExeObjStep;
42
5pub fn build(b: *Builder) void {
6 const mode = b.standardReleaseOptions();
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
75 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
86
97 const test_step = b.step("test", "Test the program");
108
11 const exe = b.addExecutable("test", "main.zig");
12 exe.setBuildMode(mode);
13 exe.setTarget(target);
9 const exe = b.addExecutable(.{
10 .name = "test",
11 .root_source_file = .{ .path = "main.zig" },
12 .optimize = optimize,
13 .target = target,
14 });
1415
1516 const run = exe.runEmulatable();
1617 test_step.dependOn(&run.step);
test/link/macho/dead_strip/build.zig+14-10
......@@ -1,9 +1,7 @@
11const std = @import("std");
2const Builder = std.build.Builder;
3const LibExeObjectStep = std.build.LibExeObjStep;
42
5pub fn build(b: *Builder) void {
6 const mode = b.standardReleaseOptions();
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
75 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
86
97 const test_step = b.step("test", "Test the program");
......@@ -11,7 +9,7 @@ pub fn build(b: *Builder) void {
119
1210 {
1311 // Without -dead_strip, we expect `iAmUnused` symbol present
14 const exe = createScenario(b, mode, target);
12 const exe = createScenario(b, optimize, target);
1513
1614 const check = exe.checkObject(.macho);
1715 check.checkInSymtab();
......@@ -24,7 +22,7 @@ pub fn build(b: *Builder) void {
2422
2523 {
2624 // With -dead_strip, no `iAmUnused` symbol should be present
27 const exe = createScenario(b, mode, target);
25 const exe = createScenario(b, optimize, target);
2826 exe.link_gc_sections = true;
2927
3028 const check = exe.checkObject(.macho);
......@@ -37,11 +35,17 @@ pub fn build(b: *Builder) void {
3735 }
3836}
3937
40fn createScenario(b: *Builder, mode: std.builtin.Mode, target: std.zig.CrossTarget) *LibExeObjectStep {
41 const exe = b.addExecutable("test", null);
38fn createScenario(
39 b: *std.Build,
40 optimize: std.builtin.OptimizeMode,
41 target: std.zig.CrossTarget,
42) *std.Build.CompileStep {
43 const exe = b.addExecutable(.{
44 .name = "test",
45 .optimize = optimize,
46 .target = target,
47 });
4248 exe.addCSourceFile("main.c", &[0][]const u8{});
43 exe.setBuildMode(mode);
44 exe.setTarget(target);
4549 exe.linkLibC();
4650 return exe;
4751}
test/link/macho/dead_strip_dylibs/build.zig+9-9
......@@ -1,16 +1,14 @@
11const std = @import("std");
2const Builder = std.build.Builder;
3const LibExeObjectStep = std.build.LibExeObjStep;
42
5pub fn build(b: *Builder) void {
6 const mode = b.standardReleaseOptions();
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
75
86 const test_step = b.step("test", "Test the program");
97 test_step.dependOn(b.getInstallStep());
108
119 {
1210 // 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);
1412
1513 const check = exe.checkObject(.macho);
1614 check.checkStart("cmd LOAD_DYLIB");
......@@ -27,7 +25,7 @@ pub fn build(b: *Builder) void {
2725
2826 {
2927 // 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);
3129 exe.dead_strip_dylibs = true;
3230
3331 const run_cmd = exe.run();
......@@ -36,10 +34,12 @@ pub fn build(b: *Builder) void {
3634 }
3735}
3836
39fn createScenario(b: *Builder, mode: std.builtin.Mode) *LibExeObjectStep {
40 const exe = b.addExecutable("test", null);
37fn createScenario(b: *std.Build, optimize: std.builtin.OptimizeMode) *std.Build.CompileStep {
38 const exe = b.addExecutable(.{
39 .name = "test",
40 .optimize = optimize,
41 });
4142 exe.addCSourceFile("main.c", &[0][]const u8{});
42 exe.setBuildMode(mode);
4343 exe.linkLibC();
4444 exe.linkFramework("Cocoa");
4545 return exe;
test/link/macho/dylib/build.zig+13-9
......@@ -1,16 +1,18 @@
11const std = @import("std");
2const Builder = std.build.Builder;
32
4pub fn build(b: *Builder) void {
5 const mode = b.standardReleaseOptions();
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
65 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
76
87 const test_step = b.step("test", "Test");
98 test_step.dependOn(b.getInstallStep());
109
11 const dylib = b.addSharedLibrary("a", null, b.version(1, 0, 0));
12 dylib.setBuildMode(mode);
13 dylib.setTarget(target);
10 const dylib = b.addSharedLibrary(.{
11 .name = "a",
12 .version = .{ .major = 1, .minor = 0 },
13 .optimize = optimize,
14 .target = target,
15 });
1416 dylib.addCSourceFile("a.c", &.{});
1517 dylib.linkLibC();
1618 dylib.install();
......@@ -24,9 +26,11 @@ pub fn build(b: *Builder) void {
2426
2527 test_step.dependOn(&check_dylib.step);
2628
27 const exe = b.addExecutable("main", null);
28 exe.setTarget(target);
29 exe.setBuildMode(mode);
29 const exe = b.addExecutable(.{
30 .name = "main",
31 .optimize = optimize,
32 .target = target,
33 });
3034 exe.addCSourceFile("main.c", &.{});
3135 exe.linkSystemLibrary("a");
3236 exe.linkLibC();
test/link/macho/empty/build.zig+8-7
......@@ -1,21 +1,22 @@
11const std = @import("std");
2const Builder = std.build.Builder;
32
4pub fn build(b: *Builder) void {
5 const mode = b.standardReleaseOptions();
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
65 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
76
87 const test_step = b.step("test", "Test the program");
98 test_step.dependOn(b.getInstallStep());
109
11 const exe = b.addExecutable("test", null);
10 const exe = b.addExecutable(.{
11 .name = "test",
12 .optimize = optimize,
13 .target = target,
14 });
1215 exe.addCSourceFile("main.c", &[0][]const u8{});
1316 exe.addCSourceFile("empty.c", &[0][]const u8{});
14 exe.setBuildMode(mode);
15 exe.setTarget(target);
1617 exe.linkLibC();
1718
18 const run_cmd = std.build.EmulatableRunStep.create(b, "run", exe);
19 const run_cmd = std.Build.EmulatableRunStep.create(b, "run", exe);
1920 run_cmd.expectStdOutEqual("Hello!\n");
2021 test_step.dependOn(&run_cmd.step);
2122}
test/link/macho/entry/build.zig+7-6
......@@ -1,15 +1,16 @@
11const std = @import("std");
2const Builder = std.build.Builder;
32
4pub fn build(b: *Builder) void {
5 const mode = b.standardReleaseOptions();
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
65
76 const test_step = b.step("test", "Test");
87 test_step.dependOn(b.getInstallStep());
98
10 const exe = b.addExecutable("main", null);
11 exe.setTarget(.{ .os_tag = .macos });
12 exe.setBuildMode(mode);
9 const exe = b.addExecutable(.{
10 .name = "main",
11 .optimize = optimize,
12 .target = .{ .os_tag = .macos },
13 });
1314 exe.addCSourceFile("main.c", &.{});
1415 exe.linkLibC();
1516 exe.entry_symbol_name = "_non_main";
test/link/macho/headerpad/build.zig+11-11
......@@ -1,17 +1,15 @@
11const std = @import("std");
22const builtin = @import("builtin");
3const Builder = std.build.Builder;
4const LibExeObjectStep = std.build.LibExeObjStep;
53
6pub fn build(b: *Builder) void {
7 const mode = b.standardReleaseOptions();
4pub fn build(b: *std.Build) void {
5 const optimize = b.standardOptimizeOption(.{});
86
97 const test_step = b.step("test", "Test");
108 test_step.dependOn(b.getInstallStep());
119
1210 {
1311 // Test -headerpad_max_install_names
14 const exe = simpleExe(b, mode);
12 const exe = simpleExe(b, optimize);
1513 exe.headerpad_max_install_names = true;
1614
1715 const check = exe.checkObject(.macho);
......@@ -36,7 +34,7 @@ pub fn build(b: *Builder) void {
3634
3735 {
3836 // Test -headerpad
39 const exe = simpleExe(b, mode);
37 const exe = simpleExe(b, optimize);
4038 exe.headerpad_size = 0x10000;
4139
4240 const check = exe.checkObject(.macho);
......@@ -52,7 +50,7 @@ pub fn build(b: *Builder) void {
5250
5351 {
5452 // Test both flags with -headerpad overriding -headerpad_max_install_names
55 const exe = simpleExe(b, mode);
53 const exe = simpleExe(b, optimize);
5654 exe.headerpad_max_install_names = true;
5755 exe.headerpad_size = 0x10000;
5856
......@@ -69,7 +67,7 @@ pub fn build(b: *Builder) void {
6967
7068 {
7169 // Test both flags with -headerpad_max_install_names overriding -headerpad
72 const exe = simpleExe(b, mode);
70 const exe = simpleExe(b, optimize);
7371 exe.headerpad_size = 0x1000;
7472 exe.headerpad_max_install_names = true;
7573
......@@ -94,9 +92,11 @@ pub fn build(b: *Builder) void {
9492 }
9593}
9694
97fn simpleExe(b: *Builder, mode: std.builtin.Mode) *LibExeObjectStep {
98 const exe = b.addExecutable("main", null);
99 exe.setBuildMode(mode);
95fn simpleExe(b: *std.Build, optimize: std.builtin.OptimizeMode) *std.Build.CompileStep {
96 const exe = b.addExecutable(.{
97 .name = "main",
98 .optimize = optimize,
99 });
100100 exe.addCSourceFile("main.c", &.{});
101101 exe.linkLibC();
102102 exe.linkFramework("CoreFoundation");
test/link/macho/linksection/build.zig+9-6
......@@ -1,15 +1,18 @@
11const std = @import("std");
22
3pub fn build(b: *std.build.Builder) void {
4 const mode = b.standardReleaseOptions();
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
55 const target = std.zig.CrossTarget{ .os_tag = .macos };
66
77 const test_step = b.step("test", "Test");
88 test_step.dependOn(b.getInstallStep());
99
10 const obj = b.addObject("test", "main.zig");
11 obj.setBuildMode(mode);
12 obj.setTarget(target);
10 const obj = b.addObject(.{
11 .name = "test",
12 .root_source_file = .{ .path = "main.zig" },
13 .optimize = optimize,
14 .target = target,
15 });
1316
1417 const check = obj.checkObject(.macho);
1518
......@@ -19,7 +22,7 @@ pub fn build(b: *std.build.Builder) void {
1922 check.checkInSymtab();
2023 check.checkNext("{*} (__TEXT,__TestFn) external _testFn");
2124
22 if (mode == .Debug) {
25 if (optimize == .Debug) {
2326 check.checkInSymtab();
2427 check.checkNext("{*} (__TEXT,__TestGenFnA) _main.testGenericFn__anon_{*}");
2528 }
test/link/macho/needed_framework/build.zig+6-6
......@@ -1,18 +1,18 @@
11const std = @import("std");
2const Builder = std.build.Builder;
3const LibExeObjectStep = std.build.LibExeObjStep;
42
5pub fn build(b: *Builder) void {
6 const mode = b.standardReleaseOptions();
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
75
86 const test_step = b.step("test", "Test the program");
97 test_step.dependOn(b.getInstallStep());
108
119 // -dead_strip_dylibs
1210 // -needed_framework Cocoa
13 const exe = b.addExecutable("test", null);
11 const exe = b.addExecutable(.{
12 .name = "test",
13 .optimize = optimize,
14 });
1415 exe.addCSourceFile("main.c", &[0][]const u8{});
15 exe.setBuildMode(mode);
1616 exe.linkLibC();
1717 exe.linkFrameworkNeeded("Cocoa");
1818 exe.dead_strip_dylibs = true;
test/link/macho/needed_library/build.zig+13-10
......@@ -1,27 +1,30 @@
11const std = @import("std");
2const Builder = std.build.Builder;
3const LibExeObjectStep = std.build.LibExeObjStep;
42
5pub fn build(b: *Builder) void {
6 const mode = b.standardReleaseOptions();
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
75 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
86
97 const test_step = b.step("test", "Test the program");
108 test_step.dependOn(b.getInstallStep());
119
12 const dylib = b.addSharedLibrary("a", null, b.version(1, 0, 0));
13 dylib.setTarget(target);
14 dylib.setBuildMode(mode);
10 const dylib = b.addSharedLibrary(.{
11 .name = "a",
12 .version = .{ .major = 1, .minor = 0 },
13 .optimize = optimize,
14 .target = target,
15 });
1516 dylib.addCSourceFile("a.c", &.{});
1617 dylib.linkLibC();
1718 dylib.install();
1819
1920 // -dead_strip_dylibs
2021 // -needed-la
21 const exe = b.addExecutable("test", null);
22 const exe = b.addExecutable(.{
23 .name = "test",
24 .optimize = optimize,
25 .target = target,
26 });
2227 exe.addCSourceFile("main.c", &[0][]const u8{});
23 exe.setBuildMode(mode);
24 exe.setTarget(target);
2528 exe.linkLibC();
2629 exe.linkSystemLibraryNeeded("a");
2730 exe.addLibraryPath(b.pathFromRoot("zig-out/lib"));
test/link/macho/objc/build.zig+7-6
......@@ -1,21 +1,22 @@
11const std = @import("std");
2const Builder = std.build.Builder;
32
4pub fn build(b: *Builder) void {
5 const mode = b.standardReleaseOptions();
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
65
76 const test_step = b.step("test", "Test the program");
87
9 const exe = b.addExecutable("test", null);
8 const exe = b.addExecutable(.{
9 .name = "test",
10 .optimize = optimize,
11 });
1012 exe.addIncludePath(".");
1113 exe.addCSourceFile("Foo.m", &[0][]const u8{});
1214 exe.addCSourceFile("test.m", &[0][]const u8{});
13 exe.setBuildMode(mode);
1415 exe.linkLibC();
1516 // TODO when we figure out how to ship framework stubs for cross-compilation,
1617 // populate paths to the sysroot here.
1718 exe.linkFramework("Foundation");
1819
19 const run_cmd = std.build.EmulatableRunStep.create(b, "run", exe);
20 const run_cmd = std.Build.EmulatableRunStep.create(b, "run", exe);
2021 test_step.dependOn(&run_cmd.step);
2122}
test/link/macho/objcpp/build.zig+6-5
......@@ -1,17 +1,18 @@
11const std = @import("std");
2const Builder = std.build.Builder;
32
4pub fn build(b: *Builder) void {
5 const mode = b.standardReleaseOptions();
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
65
76 const test_step = b.step("test", "Test the program");
87
9 const exe = b.addExecutable("test", null);
8 const exe = b.addExecutable(.{
9 .name = "test",
10 .optimize = optimize,
11 });
1012 b.default_step.dependOn(&exe.step);
1113 exe.addIncludePath(".");
1214 exe.addCSourceFile("Foo.mm", &[0][]const u8{});
1315 exe.addCSourceFile("test.mm", &[0][]const u8{});
14 exe.setBuildMode(mode);
1516 exe.linkLibCpp();
1617 // TODO when we figure out how to ship framework stubs for cross-compilation,
1718 // populate paths to the sysroot here.
test/link/macho/pagezero/build.zig+12-9
......@@ -1,17 +1,18 @@
11const std = @import("std");
2const Builder = std.build.Builder;
32
4pub fn build(b: *Builder) void {
5 const mode = b.standardReleaseOptions();
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
65 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
76
87 const test_step = b.step("test", "Test");
98 test_step.dependOn(b.getInstallStep());
109
1110 {
12 const exe = b.addExecutable("pagezero", null);
13 exe.setTarget(target);
14 exe.setBuildMode(mode);
11 const exe = b.addExecutable(.{
12 .name = "pagezero",
13 .optimize = optimize,
14 .target = target,
15 });
1516 exe.addCSourceFile("main.c", &.{});
1617 exe.linkLibC();
1718 exe.pagezero_size = 0x4000;
......@@ -29,9 +30,11 @@ pub fn build(b: *Builder) void {
2930 }
3031
3132 {
32 const exe = b.addExecutable("no_pagezero", null);
33 exe.setTarget(target);
34 exe.setBuildMode(mode);
33 const exe = b.addExecutable(.{
34 .name = "no_pagezero",
35 .optimize = optimize,
36 .target = target,
37 });
3538 exe.addCSourceFile("main.c", &.{});
3639 exe.linkLibC();
3740 exe.pagezero_size = 0;
test/link/macho/search_strategy/build.zig+28-19
......@@ -1,9 +1,7 @@
11const std = @import("std");
2const Builder = std.build.Builder;
3const LibExeObjectStep = std.build.LibExeObjStep;
42
5pub fn build(b: *Builder) void {
6 const mode = b.standardReleaseOptions();
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
75 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
86
97 const test_step = b.step("test", "Test");
......@@ -11,7 +9,7 @@ pub fn build(b: *Builder) void {
119
1210 {
1311 // -search_dylibs_first
14 const exe = createScenario(b, mode, target);
12 const exe = createScenario(b, optimize, target);
1513 exe.search_strategy = .dylibs_first;
1614
1715 const check = exe.checkObject(.macho);
......@@ -26,40 +24,51 @@ pub fn build(b: *Builder) void {
2624
2725 {
2826 // -search_paths_first
29 const exe = createScenario(b, mode, target);
27 const exe = createScenario(b, optimize, target);
3028 exe.search_strategy = .paths_first;
3129
32 const run = std.build.EmulatableRunStep.create(b, "run", exe);
30 const run = std.Build.EmulatableRunStep.create(b, "run", exe);
3331 run.cwd = b.pathFromRoot(".");
3432 run.expectStdOutEqual("Hello world");
3533 test_step.dependOn(&run.step);
3634 }
3735}
3836
39fn createScenario(b: *Builder, mode: std.builtin.Mode, target: std.zig.CrossTarget) *LibExeObjectStep {
40 const static = b.addStaticLibrary("a", null);
41 static.setTarget(target);
42 static.setBuildMode(mode);
37fn createScenario(
38 b: *std.Build,
39 optimize: std.builtin.OptimizeMode,
40 target: std.zig.CrossTarget,
41) *std.Build.CompileStep {
42 const static = b.addStaticLibrary(.{
43 .name = "a",
44 .optimize = optimize,
45 .target = target,
46 });
4347 static.addCSourceFile("a.c", &.{});
4448 static.linkLibC();
45 static.override_dest_dir = std.build.InstallDir{
49 static.override_dest_dir = std.Build.InstallDir{
4650 .custom = "static",
4751 };
4852 static.install();
4953
50 const dylib = b.addSharedLibrary("a", null, b.version(1, 0, 0));
51 dylib.setTarget(target);
52 dylib.setBuildMode(mode);
54 const dylib = b.addSharedLibrary(.{
55 .name = "a",
56 .version = .{ .major = 1, .minor = 0 },
57 .optimize = optimize,
58 .target = target,
59 });
5360 dylib.addCSourceFile("a.c", &.{});
5461 dylib.linkLibC();
55 dylib.override_dest_dir = std.build.InstallDir{
62 dylib.override_dest_dir = std.Build.InstallDir{
5663 .custom = "dynamic",
5764 };
5865 dylib.install();
5966
60 const exe = b.addExecutable("main", null);
61 exe.setTarget(target);
62 exe.setBuildMode(mode);
67 const exe = b.addExecutable(.{
68 .name = "main",
69 .optimize = optimize,
70 .target = target,
71 });
6372 exe.addCSourceFile("main.c", &.{});
6473 exe.linkSystemLibraryName("a");
6574 exe.linkLibC();
test/link/macho/stack_size/build.zig+7-6
......@@ -1,16 +1,17 @@
11const std = @import("std");
2const Builder = std.build.Builder;
32
4pub fn build(b: *Builder) void {
5 const mode = b.standardReleaseOptions();
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
65 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
76
87 const test_step = b.step("test", "Test");
98 test_step.dependOn(b.getInstallStep());
109
11 const exe = b.addExecutable("main", null);
12 exe.setTarget(target);
13 exe.setBuildMode(mode);
10 const exe = b.addExecutable(.{
11 .name = "main",
12 .optimize = optimize,
13 .target = target,
14 });
1415 exe.addCSourceFile("main.c", &.{});
1516 exe.linkLibC();
1617 exe.stack_size = 0x100000000;
test/link/macho/strict_validation/build.zig+8-7
......@@ -1,18 +1,19 @@
11const std = @import("std");
22const builtin = @import("builtin");
3const Builder = std.build.Builder;
4const LibExeObjectStep = std.build.LibExeObjStep;
53
6pub fn build(b: *Builder) void {
7 const mode = b.standardReleaseOptions();
4pub fn build(b: *std.Build) void {
5 const optimize = b.standardOptimizeOption(.{});
86 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
97
108 const test_step = b.step("test", "Test");
119 test_step.dependOn(b.getInstallStep());
1210
13 const exe = b.addExecutable("main", "main.zig");
14 exe.setBuildMode(mode);
15 exe.setTarget(target);
11 const exe = b.addExecutable(.{
12 .name = "main",
13 .root_source_file = .{ .path = "main.zig" },
14 .optimize = optimize,
15 .target = target,
16 });
1617 exe.linkLibC();
1718
1819 const check_exe = exe.checkObject(.macho);
test/link/macho/tls/build.zig+13-9
......@@ -1,19 +1,23 @@
11const std = @import("std");
2const Builder = std.build.Builder;
32
4pub fn build(b: *Builder) void {
5 const mode = b.standardReleaseOptions();
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
65 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
76
8 const lib = b.addSharedLibrary("a", null, b.version(1, 0, 0));
9 lib.setBuildMode(mode);
10 lib.setTarget(target);
7 const lib = b.addSharedLibrary(.{
8 .name = "a",
9 .version = .{ .major = 1, .minor = 0 },
10 .optimize = optimize,
11 .target = target,
12 });
1113 lib.addCSourceFile("a.c", &.{});
1214 lib.linkLibC();
1315
14 const test_exe = b.addTest("main.zig");
15 test_exe.setBuildMode(mode);
16 test_exe.setTarget(target);
16 const test_exe = b.addTest(.{
17 .root_source_file = .{ .path = "main.zig" },
18 .optimize = optimize,
19 .target = target,
20 });
1721 test_exe.linkLibrary(lib);
1822 test_exe.linkLibC();
1923
test/link/macho/unwind_info/build.zig+18-14
......@@ -1,26 +1,24 @@
11const std = @import("std");
22const builtin = @import("builtin");
3const Builder = std.build.Builder;
4const LibExeObjectStep = std.build.LibExeObjStep;
53
6pub fn build(b: *Builder) void {
7 const mode = b.standardReleaseOptions();
4pub fn build(b: *std.Build) void {
5 const optimize = b.standardOptimizeOption(.{});
86 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
97
108 const test_step = b.step("test", "Test the program");
119
12 testUnwindInfo(b, test_step, mode, target, false);
13 testUnwindInfo(b, test_step, mode, target, true);
10 testUnwindInfo(b, test_step, optimize, target, false);
11 testUnwindInfo(b, test_step, optimize, target, true);
1412}
1513
1614fn testUnwindInfo(
17 b: *Builder,
18 test_step: *std.build.Step,
19 mode: std.builtin.Mode,
15 b: *std.Build,
16 test_step: *std.Build.Step,
17 optimize: std.builtin.OptimizeMode,
2018 target: std.zig.CrossTarget,
2119 dead_strip: bool,
2220) void {
23 const exe = createScenario(b, mode, target);
21 const exe = createScenario(b, optimize, target);
2422 exe.link_gc_sections = dead_strip;
2523
2624 const check = exe.checkObject(.macho);
......@@ -52,8 +50,16 @@ fn testUnwindInfo(
5250 test_step.dependOn(&run_cmd.step);
5351}
5452
55fn createScenario(b: *Builder, mode: std.builtin.Mode, target: std.zig.CrossTarget) *LibExeObjectStep {
56 const exe = b.addExecutable("test", null);
53fn createScenario(
54 b: *std.Build,
55 optimize: std.builtin.OptimizeMode,
56 target: std.zig.CrossTarget,
57) *std.Build.CompileStep {
58 const exe = b.addExecutable(.{
59 .name = "test",
60 .optimize = optimize,
61 .target = target,
62 });
5763 b.default_step.dependOn(&exe.step);
5864 exe.addIncludePath(".");
5965 exe.addCSourceFiles(&[_][]const u8{
......@@ -61,8 +67,6 @@ fn createScenario(b: *Builder, mode: std.builtin.Mode, target: std.zig.CrossTarg
6167 "simple_string.cpp",
6268 "simple_string_owner.cpp",
6369 }, &[0][]const u8{});
64 exe.setBuildMode(mode);
65 exe.setTarget(target);
6670 exe.linkLibCpp();
6771 return exe;
6872}
test/link/macho/uuid/build.zig+17-12
......@@ -1,8 +1,6 @@
11const std = @import("std");
2const Builder = std.build.Builder;
3const LibExeObjectStep = std.build.LibExeObjStep;
42
5pub fn build(b: *Builder) void {
3pub fn build(b: *std.Build) void {
64 const test_step = b.step("test", "Test");
75 test_step.dependOn(b.getInstallStep());
86
......@@ -27,23 +25,23 @@ pub fn build(b: *Builder) void {
2725}
2826
2927fn testUuid(
30 b: *Builder,
31 test_step: *std.build.Step,
32 mode: std.builtin.Mode,
28 b: *std.Build,
29 test_step: *std.Build.Step,
30 optimize: std.builtin.OptimizeMode,
3331 target: std.zig.CrossTarget,
3432 comptime exp: []const u8,
3533) void {
3634 // The calculated UUID value is independent of debug info and so it should
3735 // stay the same across builds.
3836 {
39 const dylib = simpleDylib(b, mode, target);
37 const dylib = simpleDylib(b, optimize, target);
4038 const check_dylib = dylib.checkObject(.macho);
4139 check_dylib.checkStart("cmd UUID");
4240 check_dylib.checkNext("uuid " ++ exp);
4341 test_step.dependOn(&check_dylib.step);
4442 }
4543 {
46 const dylib = simpleDylib(b, mode, target);
44 const dylib = simpleDylib(b, optimize, target);
4745 dylib.strip = true;
4846 const check_dylib = dylib.checkObject(.macho);
4947 check_dylib.checkStart("cmd UUID");
......@@ -52,10 +50,17 @@ fn testUuid(
5250 }
5351}
5452
55fn simpleDylib(b: *Builder, mode: std.builtin.Mode, target: std.zig.CrossTarget) *LibExeObjectStep {
56 const dylib = b.addSharedLibrary("test", null, b.version(1, 0, 0));
57 dylib.setTarget(target);
58 dylib.setBuildMode(mode);
53fn simpleDylib(
54 b: *std.Build,
55 optimize: std.builtin.OptimizeMode,
56 target: std.zig.CrossTarget,
57) *std.Build.CompileStep {
58 const dylib = b.addSharedLibrary(.{
59 .name = "test",
60 .version = .{ .major = 1, .minor = 0 },
61 .optimize = optimize,
62 .target = target,
63 });
5964 dylib.addCSourceFile("test.c", &.{});
6065 dylib.linkLibC();
6166 return dylib;
test/link/macho/weak_framework/build.zig+6-6
......@@ -1,16 +1,16 @@
11const std = @import("std");
2const Builder = std.build.Builder;
3const LibExeObjectStep = std.build.LibExeObjStep;
42
5pub fn build(b: *Builder) void {
6 const mode = b.standardReleaseOptions();
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
75
86 const test_step = b.step("test", "Test the program");
97 test_step.dependOn(b.getInstallStep());
108
11 const exe = b.addExecutable("test", null);
9 const exe = b.addExecutable(.{
10 .name = "test",
11 .optimize = optimize,
12 });
1213 exe.addCSourceFile("main.c", &[0][]const u8{});
13 exe.setBuildMode(mode);
1414 exe.linkLibC();
1515 exe.linkFrameworkWeak("Cocoa");
1616
test/link/macho/weak_library/build.zig+13-10
......@@ -1,25 +1,28 @@
11const std = @import("std");
2const Builder = std.build.Builder;
3const LibExeObjectStep = std.build.LibExeObjStep;
42
5pub fn build(b: *Builder) void {
6 const mode = b.standardReleaseOptions();
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
75 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
86
97 const test_step = b.step("test", "Test the program");
108 test_step.dependOn(b.getInstallStep());
119
12 const dylib = b.addSharedLibrary("a", null, b.version(1, 0, 0));
13 dylib.setTarget(target);
14 dylib.setBuildMode(mode);
10 const dylib = b.addSharedLibrary(.{
11 .name = "a",
12 .version = .{ .major = 1, .minor = 0, .patch = 0 },
13 .target = target,
14 .optimize = optimize,
15 });
1516 dylib.addCSourceFile("a.c", &.{});
1617 dylib.linkLibC();
1718 dylib.install();
1819
19 const exe = b.addExecutable("test", null);
20 const exe = b.addExecutable(.{
21 .name = "test",
22 .target = target,
23 .optimize = optimize,
24 });
2025 exe.addCSourceFile("main.c", &[0][]const u8{});
21 exe.setTarget(target);
22 exe.setBuildMode(mode);
2326 exe.linkLibC();
2427 exe.linkSystemLibraryWeak("a");
2528 exe.addLibraryPath(b.pathFromRoot("zig-out/lib"));
test/link/static_lib_as_system_lib/build.zig+13-7
......@@ -1,17 +1,23 @@
11const std = @import("std");
2const Builder = std.build.Builder;
32
4pub fn build(b: *Builder) void {
5 const mode = b.standardReleaseOptions();
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
5 const target = b.standardTargetOptions(.{});
66
7 const lib_a = b.addStaticLibrary("a", null);
7 const lib_a = b.addStaticLibrary(.{
8 .name = "a",
9 .optimize = optimize,
10 .target = target,
11 });
812 lib_a.addCSourceFile("a.c", &[_][]const u8{});
9 lib_a.setBuildMode(mode);
1013 lib_a.addIncludePath(".");
1114 lib_a.install();
1215
13 const test_exe = b.addTest("main.zig");
14 test_exe.setBuildMode(mode);
16 const test_exe = b.addTest(.{
17 .root_source_file = .{ .path = "main.zig" },
18 .optimize = optimize,
19 .target = target,
20 });
1521 test_exe.linkSystemLibrary("a"); // force linking liba.a as -la
1622 test_exe.addSystemIncludePath(".");
1723 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 @@
11const std = @import("std");
2const Builder = std.build.Builder;
3
4pub fn build(b: *Builder) void {
5 const mode = b.standardReleaseOptions();
62
3pub fn build(b: *std.Build) void {
74 const test_step = b.step("test", "Test");
85 test_step.dependOn(b.getInstallStep());
96
107 // The code in question will pull-in compiler-rt,
118 // and therefore link with its archive file.
12 const lib = b.addSharedLibrary("main", "main.zig", .unversioned);
13 lib.setBuildMode(mode);
14 lib.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });
9 const lib = b.addSharedLibrary(.{
10 .name = "main",
11 .root_source_file = .{ .path = "main.zig" },
12 .optimize = b.standardOptimizeOption(.{}),
13 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
14 });
1515 lib.use_llvm = false;
1616 lib.use_lld = false;
1717 lib.strip = false;
test/link/wasm/basic-features/build.zig+12-8
......@@ -1,14 +1,18 @@
11const std = @import("std");
22
3pub fn build(b: *std.build.Builder) void {
4 const mode = b.standardReleaseOptions();
5
3pub fn build(b: *std.Build) void {
64 // Library with explicitly set cpu features
7 const lib = b.addSharedLibrary("lib", "main.zig", .unversioned);
8 lib.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });
9 lib.target.cpu_model = .{ .explicit = &std.Target.wasm.cpu.mvp };
10 lib.target.cpu_features_add.addFeature(0); // index 0 == atomics (see std.Target.wasm.Features)
11 lib.setBuildMode(mode);
5 const lib = b.addSharedLibrary(.{
6 .name = "lib",
7 .root_source_file = .{ .path = "main.zig" },
8 .optimize = b.standardOptimizeOption(.{}),
9 .target = .{
10 .cpu_arch = .wasm32,
11 .cpu_model = .{ .explicit = &std.Target.wasm.cpu.mvp },
12 .cpu_features_add = std.Target.wasm.featureSet(&.{.atomics}),
13 .os_tag = .freestanding,
14 },
15 });
1216 lib.use_llvm = false;
1317 lib.use_lld = false;
1418
test/link/wasm/bss/build.zig+7-7
......@@ -1,15 +1,15 @@
11const std = @import("std");
2const Builder = std.build.Builder;
3
4pub fn build(b: *Builder) void {
5 const mode = b.standardReleaseOptions();
62
3pub fn build(b: *std.Build) void {
74 const test_step = b.step("test", "Test");
85 test_step.dependOn(b.getInstallStep());
96
10 const lib = b.addSharedLibrary("lib", "lib.zig", .unversioned);
11 lib.setBuildMode(mode);
12 lib.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });
7 const lib = b.addSharedLibrary(.{
8 .name = "lib",
9 .root_source_file = .{ .path = "lib.zig" },
10 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
11 .optimize = b.standardOptimizeOption(.{}),
12 });
1313 lib.use_llvm = false;
1414 lib.use_lld = false;
1515 lib.strip = false;
test/link/wasm/export-data/build.zig+9-7
......@@ -1,13 +1,15 @@
11const std = @import("std");
2const Builder = std.build.Builder;
32
4pub fn build(b: *Builder) void {
3pub fn build(b: *std.Build) void {
54 const test_step = b.step("test", "Test");
65 test_step.dependOn(b.getInstallStep());
76
8 const lib = b.addSharedLibrary("lib", "lib.zig", .unversioned);
9 lib.setBuildMode(.ReleaseSafe); // to make the output deterministic in address positions
10 lib.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });
7 const lib = b.addSharedLibrary(.{
8 .name = "lib",
9 .root_source_file = .{ .path = "lib.zig" },
10 .optimize = .ReleaseSafe, // to make the output deterministic in address positions
11 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
12 });
1113 lib.use_lld = false;
1214 lib.export_symbol_names = &.{ "foo", "bar" };
1315 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 {
2325 check_lib.checkNext("type i32");
2426 check_lib.checkNext("mutable false");
2527 check_lib.checkNext("i32.const {bar_address}");
26 check_lib.checkComputeCompare("foo_address", .{ .op = .eq, .value = .{ .literal = 0 } });
27 check_lib.checkComputeCompare("bar_address", .{ .op = .eq, .value = .{ .literal = 4 } });
28 check_lib.checkComputeCompare("foo_address", .{ .op = .eq, .value = .{ .literal = 4 } });
29 check_lib.checkComputeCompare("bar_address", .{ .op = .eq, .value = .{ .literal = 0 } });
2830
2931 check_lib.checkStart("Section export");
3032 check_lib.checkNext("entries 3");
test/link/wasm/export/build.zig+21-12
......@@ -1,24 +1,33 @@
11const std = @import("std");
22
3pub fn build(b: *std.build.Builder) void {
4 const mode = b.standardReleaseOptions();
5
6 const no_export = b.addSharedLibrary("no-export", "main.zig", .unversioned);
7 no_export.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });
8 no_export.setBuildMode(mode);
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
5
6 const no_export = b.addSharedLibrary(.{
7 .name = "no-export",
8 .root_source_file = .{ .path = "main.zig" },
9 .optimize = optimize,
10 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
11 });
912 no_export.use_llvm = false;
1013 no_export.use_lld = false;
1114
12 const dynamic_export = b.addSharedLibrary("dynamic", "main.zig", .unversioned);
13 dynamic_export.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });
14 dynamic_export.setBuildMode(mode);
15 const dynamic_export = b.addSharedLibrary(.{
16 .name = "dynamic",
17 .root_source_file = .{ .path = "main.zig" },
18 .optimize = optimize,
19 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
20 });
1521 dynamic_export.rdynamic = true;
1622 dynamic_export.use_llvm = false;
1723 dynamic_export.use_lld = false;
1824
19 const force_export = b.addSharedLibrary("force", "main.zig", .unversioned);
20 force_export.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });
21 force_export.setBuildMode(mode);
25 const force_export = b.addSharedLibrary(.{
26 .name = "force",
27 .root_source_file = .{ .path = "main.zig" },
28 .optimize = optimize,
29 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
30 });
2231 force_export.export_symbol_names = &.{"foo"};
2332 force_export.use_llvm = false;
2433 force_export.use_lld = false;
test/link/wasm/extern-mangle/build.zig+7-7
......@@ -1,15 +1,15 @@
11const std = @import("std");
2const Builder = std.build.Builder;
3
4pub fn build(b: *Builder) void {
5 const mode = b.standardReleaseOptions();
62
3pub fn build(b: *std.Build) void {
74 const test_step = b.step("test", "Test");
85 test_step.dependOn(b.getInstallStep());
96
10 const lib = b.addSharedLibrary("lib", "lib.zig", .unversioned);
11 lib.setBuildMode(mode);
12 lib.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });
7 const lib = b.addSharedLibrary(.{
8 .name = "lib",
9 .root_source_file = .{ .path = "lib.zig" },
10 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
11 .optimize = b.standardOptimizeOption(.{}),
12 });
1313 lib.import_symbols = true; // import `a` and `b`
1414 lib.rdynamic = true; // export `foo`
1515 lib.install();
test/link/wasm/extern/build.zig+7-5
......@@ -1,10 +1,12 @@
11const std = @import("std");
22
3pub fn build(b: *std.build.Builder) void {
4 const mode = b.standardReleaseOptions();
5 const exe = b.addExecutable("extern", "main.zig");
6 exe.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .wasi });
7 exe.setBuildMode(mode);
3pub fn build(b: *std.Build) void {
4 const exe = b.addExecutable(.{
5 .name = "extern",
6 .root_source_file = .{ .path = "main.zig" },
7 .optimize = b.standardOptimizeOption(.{}),
8 .target = .{ .cpu_arch = .wasm32, .os_tag = .wasi },
9 });
810 exe.addCSourceFile("foo.c", &.{});
911 exe.use_llvm = false;
1012 exe.use_lld = false;
test/link/wasm/function-table/build.zig+20-12
......@@ -1,29 +1,37 @@
11const std = @import("std");
2const Builder = std.build.Builder;
32
4pub fn build(b: *Builder) void {
5 const mode = b.standardReleaseOptions();
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
65
76 const test_step = b.step("test", "Test");
87 test_step.dependOn(b.getInstallStep());
98
10 const import_table = b.addSharedLibrary("lib", "lib.zig", .unversioned);
11 import_table.setBuildMode(mode);
12 import_table.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });
9 const import_table = b.addSharedLibrary(.{
10 .name = "lib",
11 .root_source_file = .{ .path = "lib.zig" },
12 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
13 .optimize = optimize,
14 });
1315 import_table.use_llvm = false;
1416 import_table.use_lld = false;
1517 import_table.import_table = true;
1618
17 const export_table = b.addSharedLibrary("lib", "lib.zig", .unversioned);
18 export_table.setBuildMode(mode);
19 export_table.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });
19 const export_table = b.addSharedLibrary(.{
20 .name = "lib",
21 .root_source_file = .{ .path = "lib.zig" },
22 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
23 .optimize = optimize,
24 });
2025 export_table.use_llvm = false;
2126 export_table.use_lld = false;
2227 export_table.export_table = true;
2328
24 const regular_table = b.addSharedLibrary("lib", "lib.zig", .unversioned);
25 regular_table.setBuildMode(mode);
26 regular_table.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });
29 const regular_table = b.addSharedLibrary(.{
30 .name = "lib",
31 .root_source_file = .{ .path = "lib.zig" },
32 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
33 .optimize = optimize,
34 });
2735 regular_table.use_llvm = false;
2836 regular_table.use_lld = false;
2937
test/link/wasm/infer-features/build.zig+21-10
......@@ -1,21 +1,32 @@
11const std = @import("std");
22
3pub fn build(b: *std.build.Builder) void {
4 const mode = b.standardReleaseOptions();
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
55
66 // Wasm Object file which we will use to infer the features from
7 const c_obj = b.addObject("c_obj", null);
8 c_obj.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });
9 c_obj.target.cpu_model = .{ .explicit = &std.Target.wasm.cpu.bleeding_edge };
7 const c_obj = b.addObject(.{
8 .name = "c_obj",
9 .optimize = optimize,
10 .target = .{
11 .cpu_arch = .wasm32,
12 .cpu_model = .{ .explicit = &std.Target.wasm.cpu.bleeding_edge },
13 .os_tag = .freestanding,
14 },
15 });
1016 c_obj.addCSourceFile("foo.c", &.{});
11 c_obj.setBuildMode(mode);
1217
1318 // Wasm library that doesn't have any features specified. This will
1419 // infer its featureset from other linked object files.
15 const lib = b.addSharedLibrary("lib", "main.zig", .unversioned);
16 lib.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });
17 lib.target.cpu_model = .{ .explicit = &std.Target.wasm.cpu.mvp };
18 lib.setBuildMode(mode);
20 const lib = b.addSharedLibrary(.{
21 .name = "lib",
22 .root_source_file = .{ .path = "main.zig" },
23 .optimize = optimize,
24 .target = .{
25 .cpu_arch = .wasm32,
26 .cpu_model = .{ .explicit = &std.Target.wasm.cpu.mvp },
27 .os_tag = .freestanding,
28 },
29 });
1930 lib.use_llvm = false;
2031 lib.use_lld = false;
2132 lib.addObject(c_obj);
test/link/wasm/producers/build.zig+7-7
......@@ -1,16 +1,16 @@
11const std = @import("std");
22const builtin = @import("builtin");
3const Builder = std.build.Builder;
4
5pub fn build(b: *Builder) void {
6 const mode = b.standardReleaseOptions();
73
4pub fn build(b: *std.Build) void {
85 const test_step = b.step("test", "Test");
96 test_step.dependOn(b.getInstallStep());
107
11 const lib = b.addSharedLibrary("lib", "lib.zig", .unversioned);
12 lib.setBuildMode(mode);
13 lib.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });
8 const lib = b.addSharedLibrary(.{
9 .name = "lib",
10 .root_source_file = .{ .path = "lib.zig" },
11 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
12 .optimize = b.standardOptimizeOption(.{}),
13 });
1414 lib.use_llvm = false;
1515 lib.use_lld = false;
1616 lib.strip = false;
test/link/wasm/segments/build.zig+7-7
......@@ -1,15 +1,15 @@
11const std = @import("std");
2const Builder = std.build.Builder;
3
4pub fn build(b: *Builder) void {
5 const mode = b.standardReleaseOptions();
62
3pub fn build(b: *std.Build) void {
74 const test_step = b.step("test", "Test");
85 test_step.dependOn(b.getInstallStep());
96
10 const lib = b.addSharedLibrary("lib", "lib.zig", .unversioned);
11 lib.setBuildMode(mode);
12 lib.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });
7 const lib = b.addSharedLibrary(.{
8 .name = "lib",
9 .root_source_file = .{ .path = "lib.zig" },
10 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
11 .optimize = b.standardOptimizeOption(.{}),
12 });
1313 lib.use_llvm = false;
1414 lib.use_lld = false;
1515 lib.strip = false;
test/link/wasm/stack_pointer/build.zig+7-7
......@@ -1,15 +1,15 @@
11const std = @import("std");
2const Builder = std.build.Builder;
3
4pub fn build(b: *Builder) void {
5 const mode = b.standardReleaseOptions();
62
3pub fn build(b: *std.Build) void {
74 const test_step = b.step("test", "Test");
85 test_step.dependOn(b.getInstallStep());
96
10 const lib = b.addSharedLibrary("lib", "lib.zig", .unversioned);
11 lib.setBuildMode(mode);
12 lib.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });
7 const lib = b.addSharedLibrary(.{
8 .name = "lib",
9 .root_source_file = .{ .path = "lib.zig" },
10 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
11 .optimize = b.standardOptimizeOption(.{}),
12 });
1313 lib.use_llvm = false;
1414 lib.use_lld = false;
1515 lib.strip = false;
test/link/wasm/type/build.zig+7-7
......@@ -1,15 +1,15 @@
11const std = @import("std");
2const Builder = std.build.Builder;
3
4pub fn build(b: *Builder) void {
5 const mode = b.standardReleaseOptions();
62
3pub fn build(b: *std.Build) void {
74 const test_step = b.step("test", "Test");
85 test_step.dependOn(b.getInstallStep());
96
10 const lib = b.addSharedLibrary("lib", "lib.zig", .unversioned);
11 lib.setBuildMode(mode);
12 lib.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });
7 const lib = b.addSharedLibrary(.{
8 .name = "lib",
9 .root_source_file = .{ .path = "lib.zig" },
10 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
11 .optimize = b.standardOptimizeOption(.{}),
12 });
1313 lib.use_llvm = false;
1414 lib.use_lld = false;
1515 lib.strip = false;
test/src/compare_output.zig+25-11
......@@ -1,19 +1,18 @@
11// This is the implementation of the test harness.
22// For the actual test cases, see test/compare_output.zig.
33const std = @import("std");
4const build = std.build;
54const ArrayList = std.ArrayList;
65const fmt = std.fmt;
76const mem = std.mem;
87const fs = std.fs;
9const Mode = std.builtin.Mode;
8const OptimizeMode = std.builtin.OptimizeMode;
109
1110pub const CompareOutputContext = struct {
12 b: *build.Builder,
13 step: *build.Step,
11 b: *std.Build,
12 step: *std.Build.Step,
1413 test_index: usize,
1514 test_filter: ?[]const u8,
16 modes: []const Mode,
15 optimize_modes: []const OptimizeMode,
1716
1817 const Special = enum {
1918 None,
......@@ -102,7 +101,11 @@ pub const CompareOutputContext = struct {
102101 if (mem.indexOf(u8, annotated_case_name, filter) == null) return;
103102 }
104103
105 const exe = b.addExecutable("test", null);
104 const exe = b.addExecutable(.{
105 .name = "test",
106 .target = .{},
107 .optimize = .Debug,
108 });
106109 exe.addAssemblyFileSource(write_src.getFileSource(case.sources.items[0].filename).?);
107110
108111 const run = exe.run();
......@@ -113,19 +116,23 @@ pub const CompareOutputContext = struct {
113116 self.step.dependOn(&run.step);
114117 },
115118 Special.None => {
116 for (self.modes) |mode| {
119 for (self.optimize_modes) |optimize| {
117120 const annotated_case_name = fmt.allocPrint(self.b.allocator, "{s} {s} ({s})", .{
118121 "compare-output",
119122 case.name,
120 @tagName(mode),
123 @tagName(optimize),
121124 }) catch unreachable;
122125 if (self.test_filter) |filter| {
123126 if (mem.indexOf(u8, annotated_case_name, filter) == null) continue;
124127 }
125128
126129 const basename = case.sources.items[0].filename;
127 const exe = b.addExecutableSource("test", write_src.getFileSource(basename).?);
128 exe.setBuildMode(mode);
130 const exe = b.addExecutable(.{
131 .name = "test",
132 .root_source_file = write_src.getFileSource(basename).?,
133 .optimize = optimize,
134 .target = .{},
135 });
129136 if (case.link_libc) {
130137 exe.linkSystemLibrary("c");
131138 }
......@@ -139,13 +146,20 @@ pub const CompareOutputContext = struct {
139146 }
140147 },
141148 Special.RuntimeSafety => {
149 // TODO iterate over self.optimize_modes and test this in both
150 // debug and release safe mode
142151 const annotated_case_name = fmt.allocPrint(self.b.allocator, "safety {s}", .{case.name}) catch unreachable;
143152 if (self.test_filter) |filter| {
144153 if (mem.indexOf(u8, annotated_case_name, filter) == null) return;
145154 }
146155
147156 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 });
149163 if (case.link_libc) {
150164 exe.linkSystemLibrary("c");
151165 }
test/src/run_translated_c.zig+8-6
......@@ -1,15 +1,14 @@
11// This is the implementation of the test harness for running translated
22// C code. For the actual test cases, see test/run_translated_c.zig.
33const std = @import("std");
4const build = std.build;
54const ArrayList = std.ArrayList;
65const fmt = std.fmt;
76const mem = std.mem;
87const fs = std.fs;
98
109pub const RunTranslatedCContext = struct {
11 b: *build.Builder,
12 step: *build.Step,
10 b: *std.Build,
11 step: *std.Build.Step,
1312 test_index: usize,
1413 test_filter: ?[]const u8,
1514 target: std.zig.CrossTarget,
......@@ -85,11 +84,14 @@ pub const RunTranslatedCContext = struct {
8584 for (case.sources.items) |src_file| {
8685 write_src.add(src_file.filename, src_file.source);
8786 }
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 });
8992
9093 translate_c.step.name = b.fmt("{s} translate-c", .{annotated_case_name});
91 const exe = translate_c.addExecutable();
92 exe.setTarget(self.target);
94 const exe = translate_c.addExecutable(.{});
9395 exe.step.name = b.fmt("{s} build-exe", .{annotated_case_name});
9496 exe.linkLibC();
9597 const run = exe.run();
test/src/translate_c.zig+7-5
......@@ -1,7 +1,6 @@
11// This is the implementation of the test harness.
22// For the actual test cases, see test/translate_c.zig.
33const std = @import("std");
4const build = std.build;
54const ArrayList = std.ArrayList;
65const fmt = std.fmt;
76const mem = std.mem;
......@@ -9,8 +8,8 @@ const fs = std.fs;
98const CrossTarget = std.zig.CrossTarget;
109
1110pub const TranslateCContext = struct {
12 b: *build.Builder,
13 step: *build.Step,
11 b: *std.Build,
12 step: *std.Build.Step,
1413 test_index: usize,
1514 test_filter: ?[]const u8,
1615
......@@ -108,10 +107,13 @@ pub const TranslateCContext = struct {
108107 write_src.add(src_file.filename, src_file.source);
109108 }
110109
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 });
112115
113116 translate_c.step.name = annotated_case_name;
114 translate_c.setTarget(case.target);
115117
116118 const check_file = translate_c.addCheckFile(case.expected_lines.items);
117119
test/standalone/brace_expansion/build.zig+6-4
......@@ -1,8 +1,10 @@
1const Builder = @import("std").build.Builder;
1const std = @import("std");
22
3pub fn build(b: *Builder) void {
4 const main = b.addTest("main.zig");
5 main.setBuildMode(b.standardReleaseOptions());
3pub fn build(b: *std.Build) void {
4 const main = b.addTest(.{
5 .root_source_file = .{ .path = "main.zig" },
6 .optimize = b.standardOptimizeOption(.{}),
7 });
68
79 const test_step = b.step("test", "Test it");
810 test_step.dependOn(&main.step);
test/standalone/c_compiler/build.zig+13-10
......@@ -1,9 +1,8 @@
11const std = @import("std");
22const builtin = @import("builtin");
3const Builder = std.build.Builder;
43const CrossTarget = std.zig.CrossTarget;
54
6// TODO integrate this with the std.build executor API
5// TODO integrate this with the std.Build executor API
76fn isRunnableTarget(t: CrossTarget) bool {
87 if (t.isNative()) return true;
98
......@@ -11,24 +10,28 @@ fn isRunnableTarget(t: CrossTarget) bool {
1110 t.getCpuArch() == builtin.cpu.arch);
1211}
1312
14pub fn build(b: *Builder) void {
15 const mode = b.standardReleaseOptions();
13pub fn build(b: *std.Build) void {
14 const optimize = b.standardOptimizeOption(.{});
1615 const target = b.standardTargetOptions(.{});
1716
1817 const test_step = b.step("test", "Test the program");
1918
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 });
2124 b.default_step.dependOn(&exe_c.step);
2225 exe_c.addCSourceFile("test.c", &[0][]const u8{});
23 exe_c.setBuildMode(mode);
24 exe_c.setTarget(target);
2526 exe_c.linkLibC();
2627
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 });
2833 b.default_step.dependOn(&exe_cpp.step);
2934 exe_cpp.addCSourceFile("test.cpp", &[0][]const u8{});
30 exe_cpp.setBuildMode(mode);
31 exe_cpp.setTarget(target);
3235 exe_cpp.linkLibCpp();
3336
3437 switch (target.getOsTag()) {
test/standalone/emit_asm_and_bin/build.zig+6-4
......@@ -1,8 +1,10 @@
1const Builder = @import("std").build.Builder;
1const std = @import("std");
22
3pub fn build(b: *Builder) void {
4 const main = b.addTest("main.zig");
5 main.setBuildMode(b.standardReleaseOptions());
3pub fn build(b: *std.Build) void {
4 const main = b.addTest(.{
5 .root_source_file = .{ .path = "main.zig" },
6 .optimize = b.standardOptimizeOption(.{}),
7 });
68 main.emit_asm = .{ .emit_to = b.pathFromRoot("main.s") };
79 main.emit_bin = .{ .emit_to = b.pathFromRoot("main") };
810
test/standalone/empty_env/build.zig+7-4
......@@ -1,8 +1,11 @@
1const Builder = @import("std").build.Builder;
1const std = @import("std");
22
3pub fn build(b: *Builder) void {
4 const main = b.addExecutable("main", "main.zig");
5 main.setBuildMode(b.standardReleaseOptions());
3pub fn build(b: *std.Build) void {
4 const main = b.addExecutable(.{
5 .name = "main",
6 .root_source_file = .{ .path = "main.zig" },
7 .optimize = b.standardOptimizeOption(.{}),
8 });
69
710 const run = main.run();
811 run.clearEnvironment();
test/standalone/global_linkage/build.zig+19-9
......@@ -1,16 +1,26 @@
1const Builder = @import("std").build.Builder;
1const std = @import("std");
22
3pub fn build(b: *Builder) void {
4 const mode = b.standardReleaseOptions();
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
55
6 const obj1 = b.addStaticLibrary("obj1", "obj1.zig");
7 obj1.setBuildMode(mode);
6 const obj1 = b.addStaticLibrary(.{
7 .name = "obj1",
8 .root_source_file = .{ .path = "obj1.zig" },
9 .optimize = optimize,
10 .target = .{},
11 });
812
9 const obj2 = b.addStaticLibrary("obj2", "obj2.zig");
10 obj2.setBuildMode(mode);
13 const obj2 = b.addStaticLibrary(.{
14 .name = "obj2",
15 .root_source_file = .{ .path = "obj2.zig" },
16 .optimize = optimize,
17 .target = .{},
18 });
1119
12 const main = b.addTest("main.zig");
13 main.setBuildMode(mode);
20 const main = b.addTest(.{
21 .root_source_file = .{ .path = "main.zig" },
22 .optimize = optimize,
23 });
1424 main.linkLibrary(obj1);
1525 main.linkLibrary(obj2);
1626
test/standalone/install_raw_hex/build.zig+9-6
......@@ -1,8 +1,8 @@
11const builtin = @import("builtin");
22const std = @import("std");
3const CheckFileStep = std.build.CheckFileStep;
3const CheckFileStep = std.Build.CheckFileStep;
44
5pub fn build(b: *std.build.Builder) void {
5pub fn build(b: *std.Build) void {
66 const target = .{
77 .cpu_arch = .thumb,
88 .cpu_model = .{ .explicit = &std.Target.arm.cpu.cortex_m4 },
......@@ -10,11 +10,14 @@ pub fn build(b: *std.build.Builder) void {
1010 .abi = .gnueabihf,
1111 };
1212
13 const mode = b.standardReleaseOptions();
13 const optimize = b.standardOptimizeOption(.{});
1414
15 const elf = b.addExecutable("zig-nrf52-blink.elf", "main.zig");
16 elf.setTarget(target);
17 elf.setBuildMode(mode);
15 const elf = b.addExecutable(.{
16 .name = "zig-nrf52-blink.elf",
17 .root_source_file = .{ .path = "main.zig" },
18 .target = target,
19 .optimize = optimize,
20 });
1821
1922 const test_step = b.step("test", "Test the program");
2023 b.default_step.dependOn(test_step);
test/standalone/issue_11595/build.zig+9-7
......@@ -1,9 +1,8 @@
11const std = @import("std");
22const builtin = @import("builtin");
3const Builder = std.build.Builder;
43const CrossTarget = std.zig.CrossTarget;
54
6// TODO integrate this with the std.build executor API
5// TODO integrate this with the std.Build executor API
76fn isRunnableTarget(t: CrossTarget) bool {
87 if (t.isNative()) return true;
98
......@@ -11,12 +10,16 @@ fn isRunnableTarget(t: CrossTarget) bool {
1110 t.getCpuArch() == builtin.cpu.arch);
1211}
1312
14pub fn build(b: *Builder) void {
15 const mode = b.standardReleaseOptions();
13pub fn build(b: *std.Build) void {
14 const optimize = b.standardOptimizeOption(.{});
1615 const target = b.standardTargetOptions(.{});
1716
18 const exe = b.addExecutable("zigtest", "main.zig");
19 exe.setBuildMode(mode);
17 const exe = b.addExecutable(.{
18 .name = "zigtest",
19 .root_source_file = .{ .path = "main.zig" },
20 .target = target,
21 .optimize = optimize,
22 });
2023 exe.install();
2124
2225 const c_sources = [_][]const u8{
......@@ -39,7 +42,6 @@ pub fn build(b: *Builder) void {
3942 exe.defineCMacro("QUX", "\"Q\" \"UX\"");
4043 exe.defineCMacro("QUUX", "\"QU\\\"UX\"");
4144
42 exe.setTarget(target);
4345 b.default_step.dependOn(&exe.step);
4446
4547 const test_step = b.step("test", "Test the program");
test/standalone/issue_12588/build.zig+8-6
......@@ -1,13 +1,15 @@
11const std = @import("std");
2const Builder = std.build.Builder;
32
4pub fn build(b: *Builder) void {
5 const mode = b.standardReleaseOptions();
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
65 const target = b.standardTargetOptions(.{});
76
8 const obj = b.addObject("main", "main.zig");
9 obj.setBuildMode(mode);
10 obj.setTarget(target);
7 const obj = b.addObject(.{
8 .name = "main",
9 .root_source_file = .{ .path = "main.zig" },
10 .optimize = optimize,
11 .target = target,
12 });
1113 obj.emit_llvm_ir = .{ .emit_to = b.pathFromRoot("main.ll") };
1214 obj.emit_llvm_bc = .{ .emit_to = b.pathFromRoot("main.bc") };
1315 obj.emit_bin = .no_emit;
test/standalone/issue_12706/build.zig+9-7
......@@ -1,9 +1,8 @@
11const std = @import("std");
22const builtin = @import("builtin");
3const Builder = std.build.Builder;
43const CrossTarget = std.zig.CrossTarget;
54
6// TODO integrate this with the std.build executor API
5// TODO integrate this with the std.Build executor API
76fn isRunnableTarget(t: CrossTarget) bool {
87 if (t.isNative()) return true;
98
......@@ -11,12 +10,16 @@ fn isRunnableTarget(t: CrossTarget) bool {
1110 t.getCpuArch() == builtin.cpu.arch);
1211}
1312
14pub fn build(b: *Builder) void {
15 const mode = b.standardReleaseOptions();
13pub fn build(b: *std.Build) void {
14 const optimize = b.standardOptimizeOption(.{});
1615 const target = b.standardTargetOptions(.{});
1716
18 const exe = b.addExecutable("main", "main.zig");
19 exe.setBuildMode(mode);
17 const exe = b.addExecutable(.{
18 .name = "main",
19 .root_source_file = .{ .path = "main.zig" },
20 .optimize = optimize,
21 .target = target,
22 });
2023 exe.install();
2124
2225 const c_sources = [_][]const u8{
......@@ -26,7 +29,6 @@ pub fn build(b: *Builder) void {
2629 exe.addCSourceFiles(&c_sources, &.{});
2730 exe.linkLibC();
2831
29 exe.setTarget(target);
3032 b.default_step.dependOn(&exe.step);
3133
3234 const test_step = b.step("test", "Test the program");
test/standalone/issue_13030/build.zig+8-7
......@@ -1,16 +1,17 @@
11const std = @import("std");
22const builtin = @import("builtin");
3const Builder = std.build.Builder;
43const CrossTarget = std.zig.CrossTarget;
54
6pub fn build(b: *Builder) void {
7 const mode = b.standardReleaseOptions();
5pub fn build(b: *std.Build) void {
6 const optimize = b.standardOptimizeOption(.{});
87 const target = b.standardTargetOptions(.{});
98
10 const obj = b.addObject("main", "main.zig");
11 obj.setBuildMode(mode);
12
13 obj.setTarget(target);
9 const obj = b.addObject(.{
10 .name = "main",
11 .root_source_file = .{ .path = "main.zig" },
12 .optimize = optimize,
13 .target = target,
14 });
1415 b.default_step.dependOn(&obj.step);
1516
1617 const test_step = b.step("test", "Test the program");
test/standalone/issue_339/build.zig+8-3
......@@ -1,7 +1,12 @@
1const Builder = @import("std").build.Builder;
1const std = @import("std");
22
3pub fn build(b: *Builder) void {
4 const obj = b.addObject("test", "test.zig");
3pub fn build(b: *std.Build) void {
4 const obj = b.addObject(.{
5 .name = "test",
6 .root_source_file = .{ .path = "test.zig" },
7 .target = b.standardTargetOptions(.{}),
8 .optimize = b.standardOptimizeOption(.{}),
9 });
510
611 const test_step = b.step("test", "Test the program");
712 test_step.dependOn(&obj.step);
test/standalone/issue_5825/build.zig+14-9
......@@ -1,22 +1,27 @@
1const Builder = @import("std").build.Builder;
1const std = @import("std");
22
3pub fn build(b: *Builder) void {
3pub fn build(b: *std.Build) void {
44 const target = .{
55 .cpu_arch = .x86_64,
66 .os_tag = .windows,
77 .abi = .msvc,
88 };
9 const mode = b.standardReleaseOptions();
10 const obj = b.addObject("issue_5825", "main.zig");
11 obj.setTarget(target);
12 obj.setBuildMode(mode);
9 const optimize = b.standardOptimizeOption(.{});
10 const obj = b.addObject(.{
11 .name = "issue_5825",
12 .root_source_file = .{ .path = "main.zig" },
13 .optimize = optimize,
14 .target = target,
15 });
1316
14 const exe = b.addExecutable("issue_5825", null);
17 const exe = b.addExecutable(.{
18 .name = "issue_5825",
19 .optimize = optimize,
20 .target = target,
21 });
1522 exe.subsystem = .Console;
1623 exe.linkSystemLibrary("kernel32");
1724 exe.linkSystemLibrary("ntdll");
18 exe.setTarget(target);
19 exe.setBuildMode(mode);
2025 exe.addObject(obj);
2126
2227 const test_step = b.step("test", "Test the program");
test/standalone/issue_7030/build.zig+9-6
......@@ -1,10 +1,13 @@
1const Builder = @import("std").build.Builder;
1const std = @import("std");
22
3pub fn build(b: *Builder) void {
4 const exe = b.addExecutable("issue_7030", "main.zig");
5 exe.setTarget(.{
6 .cpu_arch = .wasm32,
7 .os_tag = .freestanding,
3pub fn build(b: *std.Build) void {
4 const exe = b.addExecutable(.{
5 .name = "issue_7030",
6 .root_source_file = .{ .path = "main.zig" },
7 .target = .{
8 .cpu_arch = .wasm32,
9 .os_tag = .freestanding,
10 },
811 });
912 exe.install();
1013 b.default_step.dependOn(&exe.step);
test/standalone/issue_794/build.zig+5-3
......@@ -1,7 +1,9 @@
1const Builder = @import("std").build.Builder;
1const std = @import("std");
22
3pub fn build(b: *Builder) void {
4 const test_artifact = b.addTest("main.zig");
3pub fn build(b: *std.Build) void {
4 const test_artifact = b.addTest(.{
5 .root_source_file = .{ .path = "main.zig" },
6 });
57 test_artifact.addIncludePath("a_directory");
68
79 b.default_step.dependOn(&test_artifact.step);
test/standalone/issue_8550/build.zig+8-5
......@@ -1,6 +1,6 @@
11const std = @import("std");
22
3pub fn build(b: *std.build.Builder) !void {
3pub fn build(b: *std.Build) !void {
44 const target = std.zig.CrossTarget{
55 .os_tag = .freestanding,
66 .cpu_arch = .arm,
......@@ -8,12 +8,15 @@ pub fn build(b: *std.build.Builder) !void {
88 .explicit = &std.Target.arm.cpu.arm1176jz_s,
99 },
1010 };
11 const mode = b.standardReleaseOptions();
12 const kernel = b.addExecutable("kernel", "./main.zig");
11 const optimize = b.standardOptimizeOption(.{});
12 const kernel = b.addExecutable(.{
13 .name = "kernel",
14 .root_source_file = .{ .path = "./main.zig" },
15 .optimize = optimize,
16 .target = target,
17 });
1318 kernel.addObjectFile("./boot.S");
1419 kernel.setLinkerScriptPath(.{ .path = "./linker.ld" });
15 kernel.setBuildMode(mode);
16 kernel.setTarget(target);
1720 kernel.install();
1821
1922 const test_step = b.step("test", "Test it");
test/standalone/issue_9812/build.zig+6-4
......@@ -1,9 +1,11 @@
11const std = @import("std");
22
3pub fn build(b: *std.build.Builder) !void {
4 const mode = b.standardReleaseOptions();
5 const zip_add = b.addTest("main.zig");
6 zip_add.setBuildMode(mode);
3pub fn build(b: *std.Build) !void {
4 const optimize = b.standardOptimizeOption(.{});
5 const zip_add = b.addTest(.{
6 .root_source_file = .{ .path = "main.zig" },
7 .optimize = optimize,
8 });
79 zip_add.addCSourceFile("vendor/kuba-zip/zip.c", &[_][]const u8{
810 "-std=c99",
911 "-fno-sanitize=undefined",
test/standalone/load_dynamic_library/build.zig+17-7
......@@ -1,13 +1,23 @@
1const Builder = @import("std").build.Builder;
1const std = @import("std");
22
3pub fn build(b: *Builder) void {
4 const opts = b.standardReleaseOptions();
3pub fn build(b: *std.Build) void {
4 const target = b.standardTargetOptions(.{});
5 const optimize = b.standardOptimizeOption(.{});
56
6 const lib = b.addSharedLibrary("add", "add.zig", b.version(1, 0, 0));
7 lib.setBuildMode(opts);
7 const lib = b.addSharedLibrary(.{
8 .name = "add",
9 .root_source_file = .{ .path = "add.zig" },
10 .version = .{ .major = 1, .minor = 0 },
11 .optimize = optimize,
12 .target = target,
13 });
814
9 const main = b.addExecutable("main", "main.zig");
10 main.setBuildMode(opts);
15 const main = b.addExecutable(.{
16 .name = "main",
17 .root_source_file = .{ .path = "main.zig" },
18 .optimize = optimize,
19 .target = target,
20 });
1121
1222 const run = main.run();
1323 run.addArtifactArg(lib);
test/standalone/main_pkg_path/build.zig+5-3
......@@ -1,7 +1,9 @@
1const Builder = @import("std").build.Builder;
1const std = @import("std");
22
3pub fn build(b: *Builder) void {
4 const test_exe = b.addTest("a/test.zig");
3pub fn build(b: *std.Build) void {
4 const test_exe = b.addTest(.{
5 .root_source_file = .{ .path = "a/test.zig" },
6 });
57 test_exe.setMainPkgPath(".");
68
79 const test_step = b.step("test", "Test the program");
test/standalone/mix_c_files/build.zig+9-7
......@@ -1,9 +1,8 @@
11const std = @import("std");
22const builtin = @import("builtin");
3const Builder = std.build.Builder;
43const CrossTarget = std.zig.CrossTarget;
54
6// TODO integrate this with the std.build executor API
5// TODO integrate this with the std.Build executor API
76fn isRunnableTarget(t: CrossTarget) bool {
87 if (t.isNative()) return true;
98
......@@ -11,15 +10,18 @@ fn isRunnableTarget(t: CrossTarget) bool {
1110 t.getCpuArch() == builtin.cpu.arch);
1211}
1312
14pub fn build(b: *Builder) void {
15 const mode = b.standardReleaseOptions();
13pub fn build(b: *std.Build) void {
14 const optimize = b.standardOptimizeOption(.{});
1615 const target = b.standardTargetOptions(.{});
1716
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 });
1923 exe.addCSourceFile("test.c", &[_][]const u8{"-std=c11"});
20 exe.setBuildMode(mode);
2124 exe.linkLibC();
22 exe.setTarget(target);
2325 b.default_step.dependOn(&exe.step);
2426
2527 const test_step = b.step("test", "Test the program");
test/standalone/mix_o_files/build.zig+14-4
......@@ -1,9 +1,19 @@
1const Builder = @import("std").build.Builder;
1const std = @import("std");
22
3pub fn build(b: *Builder) void {
4 const obj = b.addObject("base64", "base64.zig");
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
55
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 });
717 exe.addCSourceFile("test.c", &[_][]const u8{"-std=c99"});
818 exe.addObject(obj);
919 exe.linkSystemLibrary("c");
test/standalone/options/build.zig+7-5
......@@ -1,12 +1,14 @@
11const std = @import("std");
22
3pub fn build(b: *std.build.Builder) void {
3pub fn build(b: *std.Build) void {
44 const target = b.standardTargetOptions(.{});
5 const mode = b.standardReleaseOptions();
5 const optimize = b.standardOptimizeOption(.{});
66
7 const main = b.addTest("src/main.zig");
8 main.setTarget(target);
9 main.setBuildMode(mode);
7 const main = b.addTest(.{
8 .root_source_file = .{ .path = "src/main.zig" },
9 .target = target,
10 .optimize = optimize,
11 });
1012
1113 const options = b.addOptions();
1214 main.addOptions("build_options", options);
test/standalone/pie/build.zig+6-4
......@@ -1,8 +1,10 @@
1const Builder = @import("std").build.Builder;
1const std = @import("std");
22
3pub fn build(b: *Builder) void {
4 const main = b.addTest("main.zig");
5 main.setBuildMode(b.standardReleaseOptions());
3pub fn build(b: *std.Build) void {
4 const main = b.addTest(.{
5 .root_source_file = .{ .path = "main.zig" },
6 .optimize = b.standardOptimizeOption(.{}),
7 });
68 main.pie = true;
79
810 const test_step = b.step("test", "Test the program");
test/standalone/pkg_import/build.zig+9-8
......@@ -1,13 +1,14 @@
1const Builder = @import("std").build.Builder;
1const std = @import("std");
22
3pub fn build(b: *Builder) void {
4 const exe = b.addExecutable("test", "test.zig");
5 exe.addPackagePath("my_pkg", "pkg.zig");
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
65
7 // This is duplicated to test that you are allowed to call
8 // b.standardReleaseOptions() twice.
9 exe.setBuildMode(b.standardReleaseOptions());
10 exe.setBuildMode(b.standardReleaseOptions());
6 const exe = b.addExecutable(.{
7 .name = "test",
8 .root_source_file = .{ .path = "test.zig" },
9 .optimize = optimize,
10 });
11 exe.addPackagePath("my_pkg", "pkg.zig");
1112
1213 const run = exe.run();
1314
test/standalone/shared_library/build.zig+15-6
......@@ -1,12 +1,21 @@
1const Builder = @import("std").build.Builder;
1const std = @import("std");
22
3pub fn build(b: *Builder) void {
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
45 const target = b.standardTargetOptions(.{});
5 const lib = b.addSharedLibrary("mathtest", "mathtest.zig", b.version(1, 0, 0));
6 lib.setTarget(target);
6 const lib = b.addSharedLibrary(.{
7 .name = "mathtest",
8 .root_source_file = .{ .path = "mathtest.zig" },
9 .version = .{ .major = 1, .minor = 0 },
10 .target = target,
11 .optimize = optimize,
12 });
713
8 const exe = b.addExecutable("test", null);
9 exe.setTarget(target);
14 const exe = b.addExecutable(.{
15 .name = "test",
16 .target = target,
17 .optimize = optimize,
18 });
1019 exe.addCSourceFile("test.c", &[_][]const u8{"-std=c99"});
1120 exe.linkLibrary(lib);
1221 exe.linkSystemLibrary("c");
test/standalone/static_c_lib/build.zig+12-7
......@@ -1,15 +1,20 @@
1const Builder = @import("std").build.Builder;
1const std = @import("std");
22
3pub fn build(b: *Builder) void {
4 const mode = b.standardReleaseOptions();
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
55
6 const foo = b.addStaticLibrary("foo", null);
6 const foo = b.addStaticLibrary(.{
7 .name = "foo",
8 .optimize = optimize,
9 .target = .{},
10 });
711 foo.addCSourceFile("foo.c", &[_][]const u8{});
8 foo.setBuildMode(mode);
912 foo.addIncludePath(".");
1013
11 const test_exe = b.addTest("foo.zig");
12 test_exe.setBuildMode(mode);
14 const test_exe = b.addTest(.{
15 .root_source_file = .{ .path = "foo.zig" },
16 .optimize = optimize,
17 });
1318 test_exe.linkLibrary(foo);
1419 test_exe.addIncludePath(".");
1520
test/standalone/test_runner_path/build.zig+6-3
......@@ -1,7 +1,10 @@
1const Builder = @import("std").build.Builder;
1const std = @import("std");
22
3pub fn build(b: *Builder) void {
4 const test_exe = b.addTestExe("test", "test.zig");
3pub fn build(b: *std.Build) void {
4 const test_exe = b.addTest(.{
5 .root_source_file = .{ .path = "test.zig" },
6 .kind = .test_exe,
7 });
58 test_exe.test_runner = "test_runner.zig";
69
710 const test_run = test_exe.run();
test/standalone/use_alias/build.zig+6-4
......@@ -1,8 +1,10 @@
1const Builder = @import("std").build.Builder;
1const std = @import("std");
22
3pub fn build(b: *Builder) void {
4 const main = b.addTest("main.zig");
5 main.setBuildMode(b.standardReleaseOptions());
3pub fn build(b: *std.Build) void {
4 const main = b.addTest(.{
5 .root_source_file = .{ .path = "main.zig" },
6 .optimize = b.standardOptimizeOption(.{}),
7 });
68 main.addIncludePath(".");
79
810 const test_step = b.step("test", "Test it");
test/standalone/windows_spawn/build.zig+14-7
......@@ -1,13 +1,20 @@
1const Builder = @import("std").build.Builder;
1const std = @import("std");
22
3pub fn build(b: *Builder) void {
4 const mode = b.standardReleaseOptions();
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
55
6 const hello = b.addExecutable("hello", "hello.zig");
7 hello.setBuildMode(mode);
6 const hello = b.addExecutable(.{
7 .name = "hello",
8 .root_source_file = .{ .path = "hello.zig" },
9 .optimize = optimize,
10 });
11
12 const main = b.addExecutable(.{
13 .name = "main",
14 .root_source_file = .{ .path = "main.zig" },
15 .optimize = optimize,
16 });
817
9 const main = b.addExecutable("main", "main.zig");
10 main.setBuildMode(mode);
1118 const run = main.run();
1219 run.addArtifactArg(hello);
1320
test/tests.zig+117-100
......@@ -1,17 +1,17 @@
11const std = @import("std");
22const builtin = @import("builtin");
33const debug = std.debug;
4const build = std.build;
54const CrossTarget = std.zig.CrossTarget;
65const io = std.io;
76const fs = std.fs;
87const mem = std.mem;
98const fmt = std.fmt;
109const ArrayList = std.ArrayList;
11const Mode = std.builtin.Mode;
12const LibExeObjStep = build.LibExeObjStep;
10const OptimizeMode = std.builtin.OptimizeMode;
11const CompileStep = std.Build.CompileStep;
1312const Allocator = mem.Allocator;
14const ExecError = build.Builder.ExecError;
13const ExecError = std.Build.ExecError;
14const Step = std.Build.Step;
1515
1616// Cases
1717const compare_output = @import("compare_output.zig");
......@@ -30,7 +30,7 @@ pub const CompareOutputContext = @import("src/compare_output.zig").CompareOutput
3030
3131const TestTarget = struct {
3232 target: CrossTarget = @as(CrossTarget, .{}),
33 mode: std.builtin.Mode = .Debug,
33 optimize_mode: std.builtin.OptimizeMode = .Debug,
3434 link_libc: bool = false,
3535 single_threaded: bool = false,
3636 disable_native: bool = false,
......@@ -423,38 +423,38 @@ const test_targets = blk: {
423423
424424 // Do the release tests last because they take a long time
425425 .{
426 .mode = .ReleaseFast,
426 .optimize_mode = .ReleaseFast,
427427 },
428428 .{
429429 .link_libc = true,
430 .mode = .ReleaseFast,
430 .optimize_mode = .ReleaseFast,
431431 },
432432 .{
433 .mode = .ReleaseFast,
433 .optimize_mode = .ReleaseFast,
434434 .single_threaded = true,
435435 },
436436
437437 .{
438 .mode = .ReleaseSafe,
438 .optimize_mode = .ReleaseSafe,
439439 },
440440 .{
441441 .link_libc = true,
442 .mode = .ReleaseSafe,
442 .optimize_mode = .ReleaseSafe,
443443 },
444444 .{
445 .mode = .ReleaseSafe,
445 .optimize_mode = .ReleaseSafe,
446446 .single_threaded = true,
447447 },
448448
449449 .{
450 .mode = .ReleaseSmall,
450 .optimize_mode = .ReleaseSmall,
451451 },
452452 .{
453453 .link_libc = true,
454 .mode = .ReleaseSmall,
454 .optimize_mode = .ReleaseSmall,
455455 },
456456 .{
457 .mode = .ReleaseSmall,
457 .optimize_mode = .ReleaseSmall,
458458 .single_threaded = true,
459459 },
460460 };
......@@ -462,14 +462,14 @@ const test_targets = blk: {
462462
463463const max_stdout_size = 1 * 1024 * 1024; // 1 MB
464464
465pub fn addCompareOutputTests(b: *build.Builder, test_filter: ?[]const u8, modes: []const Mode) *build.Step {
465pub fn addCompareOutputTests(b: *std.Build, test_filter: ?[]const u8, optimize_modes: []const OptimizeMode) *Step {
466466 const cases = b.allocator.create(CompareOutputContext) catch unreachable;
467467 cases.* = CompareOutputContext{
468468 .b = b,
469469 .step = b.step("test-compare-output", "Run the compare output tests"),
470470 .test_index = 0,
471471 .test_filter = test_filter,
472 .modes = modes,
472 .optimize_modes = optimize_modes,
473473 };
474474
475475 compare_output.addCases(cases);
......@@ -477,14 +477,14 @@ pub fn addCompareOutputTests(b: *build.Builder, test_filter: ?[]const u8, modes:
477477 return cases.step;
478478}
479479
480pub fn addStackTraceTests(b: *build.Builder, test_filter: ?[]const u8, modes: []const Mode) *build.Step {
480pub fn addStackTraceTests(b: *std.Build, test_filter: ?[]const u8, optimize_modes: []const OptimizeMode) *Step {
481481 const cases = b.allocator.create(StackTracesContext) catch unreachable;
482482 cases.* = StackTracesContext{
483483 .b = b,
484484 .step = b.step("test-stack-traces", "Run the stack trace tests"),
485485 .test_index = 0,
486486 .test_filter = test_filter,
487 .modes = modes,
487 .optimize_modes = optimize_modes,
488488 };
489489
490490 stack_traces.addCases(cases);
......@@ -493,9 +493,9 @@ pub fn addStackTraceTests(b: *build.Builder, test_filter: ?[]const u8, modes: []
493493}
494494
495495pub fn addStandaloneTests(
496 b: *build.Builder,
496 b: *std.Build,
497497 test_filter: ?[]const u8,
498 modes: []const Mode,
498 optimize_modes: []const OptimizeMode,
499499 skip_non_native: bool,
500500 enable_macos_sdk: bool,
501501 target: std.zig.CrossTarget,
......@@ -506,14 +506,14 @@ pub fn addStandaloneTests(
506506 enable_wasmtime: bool,
507507 enable_wine: bool,
508508 enable_symlinks_windows: bool,
509) *build.Step {
509) *Step {
510510 const cases = b.allocator.create(StandaloneContext) catch unreachable;
511511 cases.* = StandaloneContext{
512512 .b = b,
513513 .step = b.step("test-standalone", "Run the standalone tests"),
514514 .test_index = 0,
515515 .test_filter = test_filter,
516 .modes = modes,
516 .optimize_modes = optimize_modes,
517517 .skip_non_native = skip_non_native,
518518 .enable_macos_sdk = enable_macos_sdk,
519519 .target = target,
......@@ -532,20 +532,20 @@ pub fn addStandaloneTests(
532532}
533533
534534pub fn addLinkTests(
535 b: *build.Builder,
535 b: *std.Build,
536536 test_filter: ?[]const u8,
537 modes: []const Mode,
537 optimize_modes: []const OptimizeMode,
538538 enable_macos_sdk: bool,
539539 omit_stage2: bool,
540540 enable_symlinks_windows: bool,
541) *build.Step {
541) *Step {
542542 const cases = b.allocator.create(StandaloneContext) catch unreachable;
543543 cases.* = StandaloneContext{
544544 .b = b,
545545 .step = b.step("test-link", "Run the linker tests"),
546546 .test_index = 0,
547547 .test_filter = test_filter,
548 .modes = modes,
548 .optimize_modes = optimize_modes,
549549 .skip_non_native = true,
550550 .enable_macos_sdk = enable_macos_sdk,
551551 .target = .{},
......@@ -556,12 +556,17 @@ pub fn addLinkTests(
556556 return cases.step;
557557}
558558
559pub fn addCliTests(b: *build.Builder, test_filter: ?[]const u8, modes: []const Mode) *build.Step {
559pub fn addCliTests(b: *std.Build, test_filter: ?[]const u8, optimize_modes: []const OptimizeMode) *Step {
560560 _ = test_filter;
561 _ = modes;
561 _ = optimize_modes;
562562 const step = b.step("test-cli", "Test the command line interface");
563563
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 });
565570 const run_cmd = exe.run();
566571 run_cmd.addArgs(&[_][]const u8{
567572 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
572577 return step;
573578}
574579
575pub fn addAssembleAndLinkTests(b: *build.Builder, test_filter: ?[]const u8, modes: []const Mode) *build.Step {
580pub fn addAssembleAndLinkTests(b: *std.Build, test_filter: ?[]const u8, optimize_modes: []const OptimizeMode) *Step {
576581 const cases = b.allocator.create(CompareOutputContext) catch unreachable;
577582 cases.* = CompareOutputContext{
578583 .b = b,
579584 .step = b.step("test-asm-link", "Run the assemble and link tests"),
580585 .test_index = 0,
581586 .test_filter = test_filter,
582 .modes = modes,
587 .optimize_modes = optimize_modes,
583588 };
584589
585590 assemble_and_link.addCases(cases);
......@@ -587,7 +592,7 @@ pub fn addAssembleAndLinkTests(b: *build.Builder, test_filter: ?[]const u8, mode
587592 return cases.step;
588593}
589594
590pub fn addTranslateCTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step {
595pub fn addTranslateCTests(b: *std.Build, test_filter: ?[]const u8) *Step {
591596 const cases = b.allocator.create(TranslateCContext) catch unreachable;
592597 cases.* = TranslateCContext{
593598 .b = b,
......@@ -602,10 +607,10 @@ pub fn addTranslateCTests(b: *build.Builder, test_filter: ?[]const u8) *build.St
602607}
603608
604609pub fn addRunTranslatedCTests(
605 b: *build.Builder,
610 b: *std.Build,
606611 test_filter: ?[]const u8,
607612 target: std.zig.CrossTarget,
608) *build.Step {
613) *Step {
609614 const cases = b.allocator.create(RunTranslatedCContext) catch unreachable;
610615 cases.* = .{
611616 .b = b,
......@@ -620,7 +625,7 @@ pub fn addRunTranslatedCTests(
620625 return cases.step;
621626}
622627
623pub fn addGenHTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step {
628pub fn addGenHTests(b: *std.Build, test_filter: ?[]const u8) *Step {
624629 const cases = b.allocator.create(GenHContext) catch unreachable;
625630 cases.* = GenHContext{
626631 .b = b,
......@@ -635,18 +640,18 @@ pub fn addGenHTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step {
635640}
636641
637642pub fn addPkgTests(
638 b: *build.Builder,
643 b: *std.Build,
639644 test_filter: ?[]const u8,
640645 root_src: []const u8,
641646 name: []const u8,
642647 desc: []const u8,
643 modes: []const Mode,
648 optimize_modes: []const OptimizeMode,
644649 skip_single_threaded: bool,
645650 skip_non_native: bool,
646651 skip_libc: bool,
647652 skip_stage1: bool,
648653 skip_stage2: bool,
649) *build.Step {
654) *Step {
650655 const step = b.step(b.fmt("test-{s}", .{name}), desc);
651656
652657 for (test_targets) |test_target| {
......@@ -677,8 +682,8 @@ pub fn addPkgTests(
677682 else => if (skip_stage2) continue,
678683 };
679684
680 const want_this_mode = for (modes) |m| {
681 if (m == test_target.mode) break true;
685 const want_this_mode = for (optimize_modes) |m| {
686 if (m == test_target.optimize_mode) break true;
682687 } else false;
683688 if (!want_this_mode) continue;
684689
......@@ -691,21 +696,23 @@ pub fn addPkgTests(
691696
692697 const triple_prefix = test_target.target.zigTriple(b.allocator) catch unreachable;
693698
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 });
695704 const single_threaded_txt = if (test_target.single_threaded) "single" else "multi";
696705 const backend_txt = if (test_target.backend) |backend| @tagName(backend) else "default";
697706 these_tests.setNamePrefix(b.fmt("{s}-{s}-{s}-{s}-{s}-{s} ", .{
698707 name,
699708 triple_prefix,
700 @tagName(test_target.mode),
709 @tagName(test_target.optimize_mode),
701710 libc_prefix,
702711 single_threaded_txt,
703712 backend_txt,
704713 }));
705714 these_tests.single_threaded = test_target.single_threaded;
706715 these_tests.setFilter(test_filter);
707 these_tests.setBuildMode(test_target.mode);
708 these_tests.setTarget(test_target.target);
709716 if (test_target.link_libc) {
710717 these_tests.linkSystemLibrary("c");
711718 }
......@@ -735,13 +742,13 @@ pub fn addPkgTests(
735742}
736743
737744pub const StackTracesContext = struct {
738 b: *build.Builder,
739 step: *build.Step,
745 b: *std.Build,
746 step: *Step,
740747 test_index: usize,
741748 test_filter: ?[]const u8,
742 modes: []const Mode,
749 optimize_modes: []const OptimizeMode,
743750
744 const Expect = [@typeInfo(Mode).Enum.fields.len][]const u8;
751 const Expect = [@typeInfo(OptimizeMode).Enum.fields.len][]const u8;
745752
746753 pub fn addCase(self: *StackTracesContext, config: anytype) void {
747754 if (@hasField(@TypeOf(config), "exclude")) {
......@@ -755,26 +762,26 @@ pub const StackTracesContext = struct {
755762 const exclude_os: []const std.Target.Os.Tag = &config.exclude_os;
756763 for (exclude_os) |os| if (os == builtin.os.tag) return;
757764 }
758 for (self.modes) |mode| {
759 switch (mode) {
765 for (self.optimize_modes) |optimize_mode| {
766 switch (optimize_mode) {
760767 .Debug => {
761768 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);
763770 }
764771 },
765772 .ReleaseSafe => {
766773 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);
768775 }
769776 },
770777 .ReleaseFast => {
771778 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);
773780 }
774781 },
775782 .ReleaseSmall => {
776783 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);
778785 }
779786 },
780787 }
......@@ -785,7 +792,7 @@ pub const StackTracesContext = struct {
785792 self: *StackTracesContext,
786793 name: []const u8,
787794 source: []const u8,
788 mode: Mode,
795 optimize_mode: OptimizeMode,
789796 mode_config: anytype,
790797 ) void {
791798 if (@hasField(@TypeOf(mode_config), "exclude")) {
......@@ -803,7 +810,7 @@ pub const StackTracesContext = struct {
803810 const annotated_case_name = fmt.allocPrint(self.b.allocator, "{s} {s} ({s})", .{
804811 "stack-trace",
805812 name,
806 @tagName(mode),
813 @tagName(optimize_mode),
807814 }) catch unreachable;
808815 if (self.test_filter) |filter| {
809816 if (mem.indexOf(u8, annotated_case_name, filter) == null) return;
......@@ -812,14 +819,18 @@ pub const StackTracesContext = struct {
812819 const b = self.b;
813820 const src_basename = "source.zig";
814821 const write_src = b.addWriteFile(src_basename, source);
815 const exe = b.addExecutableSource("test", write_src.getFileSource(src_basename).?);
816 exe.setBuildMode(mode);
822 const exe = b.addExecutable(.{
823 .name = "test",
824 .root_source_file = write_src.getFileSource(src_basename).?,
825 .optimize = optimize_mode,
826 .target = .{},
827 });
817828
818829 const run_and_compare = RunAndCompareStep.create(
819830 self,
820831 exe,
821832 annotated_case_name,
822 mode,
833 optimize_mode,
823834 mode_config.expect,
824835 );
825836
......@@ -829,29 +840,29 @@ pub const StackTracesContext = struct {
829840 const RunAndCompareStep = struct {
830841 pub const base_id = .custom;
831842
832 step: build.Step,
843 step: Step,
833844 context: *StackTracesContext,
834 exe: *LibExeObjStep,
845 exe: *CompileStep,
835846 name: []const u8,
836 mode: Mode,
847 optimize_mode: OptimizeMode,
837848 expect_output: []const u8,
838849 test_index: usize,
839850
840851 pub fn create(
841852 context: *StackTracesContext,
842 exe: *LibExeObjStep,
853 exe: *CompileStep,
843854 name: []const u8,
844 mode: Mode,
855 optimize_mode: OptimizeMode,
845856 expect_output: []const u8,
846857 ) *RunAndCompareStep {
847858 const allocator = context.b.allocator;
848859 const ptr = allocator.create(RunAndCompareStep) catch unreachable;
849860 ptr.* = RunAndCompareStep{
850 .step = build.Step.init(.custom, "StackTraceCompareOutputStep", allocator, make),
861 .step = Step.init(.custom, "StackTraceCompareOutputStep", allocator, make),
851862 .context = context,
852863 .exe = exe,
853864 .name = name,
854 .mode = mode,
865 .optimize_mode = optimize_mode,
855866 .expect_output = expect_output,
856867 .test_index = context.test_index,
857868 };
......@@ -860,7 +871,7 @@ pub const StackTracesContext = struct {
860871 return ptr;
861872 }
862873
863 fn make(step: *build.Step) !void {
874 fn make(step: *Step) !void {
864875 const self = @fieldParentPtr(RunAndCompareStep, "step", step);
865876 const b = self.context.b;
866877
......@@ -932,7 +943,7 @@ pub const StackTracesContext = struct {
932943 // process result
933944 // - keep only basename of source file path
934945 // - 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
936947 // - skip empty lines
937948 const got: []const u8 = got_result: {
938949 var buf = ArrayList(u8).init(b.allocator);
......@@ -968,7 +979,7 @@ pub const StackTracesContext = struct {
968979 // emit substituted line
969980 try buf.appendSlice(line[pos + 1 .. marks[2] + delims[2].len]);
970981 try buf.appendSlice(" [address]");
971 if (self.mode == .Debug) {
982 if (self.optimize_mode == .Debug) {
972983 // On certain platforms (windows) or possibly depending on how we choose to link main
973984 // the object file extension may be present so we simply strip any extension.
974985 if (mem.indexOfScalar(u8, line[marks[4]..marks[5]], '.')) |idot| {
......@@ -1003,11 +1014,11 @@ pub const StackTracesContext = struct {
10031014};
10041015
10051016pub const StandaloneContext = struct {
1006 b: *build.Builder,
1007 step: *build.Step,
1017 b: *std.Build,
1018 step: *Step,
10081019 test_index: usize,
10091020 test_filter: ?[]const u8,
1010 modes: []const Mode,
1021 optimize_modes: []const OptimizeMode,
10111022 skip_non_native: bool,
10121023 enable_macos_sdk: bool,
10131024 target: std.zig.CrossTarget,
......@@ -1087,13 +1098,13 @@ pub const StandaloneContext = struct {
10871098 }
10881099 }
10891100
1090 const modes = if (features.build_modes) self.modes else &[1]Mode{.Debug};
1091 for (modes) |mode| {
1092 const arg = switch (mode) {
1101 const optimize_modes = if (features.build_modes) self.optimize_modes else &[1]OptimizeMode{.Debug};
1102 for (optimize_modes) |optimize_mode| {
1103 const arg = switch (optimize_mode) {
10931104 .Debug => "",
1094 .ReleaseFast => "-Drelease-fast",
1095 .ReleaseSafe => "-Drelease-safe",
1096 .ReleaseSmall => "-Drelease-small",
1105 .ReleaseFast => "-Doptimize=ReleaseFast",
1106 .ReleaseSafe => "-Doptimize=ReleaseSafe",
1107 .ReleaseSmall => "-Doptimize=ReleaseSmall",
10971108 };
10981109 const zig_args_base_len = zig_args.items.len;
10991110 if (arg.len > 0)
......@@ -1101,7 +1112,7 @@ pub const StandaloneContext = struct {
11011112 defer zig_args.resize(zig_args_base_len) catch unreachable;
11021113
11031114 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) });
11051116 log_step.step.dependOn(&run_cmd.step);
11061117
11071118 self.step.dependOn(&log_step.step);
......@@ -1111,17 +1122,21 @@ pub const StandaloneContext = struct {
11111122 pub fn addAllArgs(self: *StandaloneContext, root_src: []const u8, link_libc: bool) void {
11121123 const b = self.b;
11131124
1114 for (self.modes) |mode| {
1125 for (self.optimize_modes) |optimize| {
11151126 const annotated_case_name = fmt.allocPrint(self.b.allocator, "build {s} ({s})", .{
11161127 root_src,
1117 @tagName(mode),
1128 @tagName(optimize),
11181129 }) catch unreachable;
11191130 if (self.test_filter) |filter| {
11201131 if (mem.indexOf(u8, annotated_case_name, filter) == null) continue;
11211132 }
11221133
1123 const exe = b.addExecutable("test", root_src);
1124 exe.setBuildMode(mode);
1134 const exe = b.addExecutable(.{
1135 .name = "test",
1136 .root_source_file = .{ .path = root_src },
1137 .optimize = optimize,
1138 .target = .{},
1139 });
11251140 if (link_libc) {
11261141 exe.linkSystemLibrary("c");
11271142 }
......@@ -1135,8 +1150,8 @@ pub const StandaloneContext = struct {
11351150};
11361151
11371152pub const GenHContext = struct {
1138 b: *build.Builder,
1139 step: *build.Step,
1153 b: *std.Build,
1154 step: *Step,
11401155 test_index: usize,
11411156 test_filter: ?[]const u8,
11421157
......@@ -1163,23 +1178,23 @@ pub const GenHContext = struct {
11631178 };
11641179
11651180 const GenHCmpOutputStep = struct {
1166 step: build.Step,
1181 step: Step,
11671182 context: *GenHContext,
1168 obj: *LibExeObjStep,
1183 obj: *CompileStep,
11691184 name: []const u8,
11701185 test_index: usize,
11711186 case: *const TestCase,
11721187
11731188 pub fn create(
11741189 context: *GenHContext,
1175 obj: *LibExeObjStep,
1190 obj: *CompileStep,
11761191 name: []const u8,
11771192 case: *const TestCase,
11781193 ) *GenHCmpOutputStep {
11791194 const allocator = context.b.allocator;
11801195 const ptr = allocator.create(GenHCmpOutputStep) catch unreachable;
11811196 ptr.* = GenHCmpOutputStep{
1182 .step = build.Step.init(.Custom, "ParseCCmpOutput", allocator, make),
1197 .step = Step.init(.Custom, "ParseCCmpOutput", allocator, make),
11831198 .context = context,
11841199 .obj = obj,
11851200 .name = name,
......@@ -1191,7 +1206,7 @@ pub const GenHContext = struct {
11911206 return ptr;
11921207 }
11931208
1194 fn make(step: *build.Step) !void {
1209 fn make(step: *Step) !void {
11951210 const self = @fieldParentPtr(GenHCmpOutputStep, "step", step);
11961211 const b = self.context.b;
11971212
......@@ -1247,8 +1262,8 @@ pub const GenHContext = struct {
12471262 pub fn addCase(self: *GenHContext, case: *const TestCase) void {
12481263 const b = self.b;
12491264
1250 const mode = std.builtin.Mode.Debug;
1251 const annotated_case_name = fmt.allocPrint(self.b.allocator, "gen-h {s} ({s})", .{ case.name, @tagName(mode) }) catch unreachable;
1265 const optimize_mode = std.builtin.OptimizeMode.Debug;
1266 const annotated_case_name = fmt.allocPrint(self.b.allocator, "gen-h {s} ({s})", .{ case.name, @tagName(optimize_mode) }) catch unreachable;
12521267 if (self.test_filter) |filter| {
12531268 if (mem.indexOf(u8, annotated_case_name, filter) == null) return;
12541269 }
......@@ -1259,7 +1274,7 @@ pub const GenHContext = struct {
12591274 }
12601275
12611276 const obj = b.addObjectFromWriteFileStep("test", write_src, case.sources.items[0].filename);
1262 obj.setBuildMode(mode);
1277 obj.setBuildMode(optimize_mode);
12631278
12641279 const cmp_h = GenHCmpOutputStep.create(self, obj, annotated_case_name, case);
12651280
......@@ -1333,17 +1348,20 @@ const c_abi_targets = [_]CrossTarget{
13331348 },
13341349};
13351350
1336pub fn addCAbiTests(b: *build.Builder, skip_non_native: bool, skip_release: bool) *build.Step {
1351pub fn addCAbiTests(b: *std.Build, skip_non_native: bool, skip_release: bool) *Step {
13371352 const step = b.step("test-c-abi", "Run the C ABI tests");
13381353
1339 const modes: [2]Mode = .{ .Debug, .ReleaseFast };
1354 const optimize_modes: [2]OptimizeMode = .{ .Debug, .ReleaseFast };
13401355
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| {
13421357 if (skip_non_native and !c_abi_target.isNative())
13431358 continue;
13441359
1345 const test_step = b.addTest("test/c_abi/main.zig");
1346 test_step.setTarget(c_abi_target);
1360 const test_step = b.addTest(.{
1361 .root_source_file = .{ .path = "test/c_abi/main.zig" },
1362 .optimize = optimize_mode,
1363 .target = c_abi_target,
1364 });
13471365 if (c_abi_target.abi != null and c_abi_target.abi.?.isMusl()) {
13481366 // TODO NativeTargetInfo insists on dynamically linking musl
13491367 // for some reason?
......@@ -1351,7 +1369,6 @@ pub fn addCAbiTests(b: *build.Builder, skip_non_native: bool, skip_release: bool
13511369 }
13521370 test_step.linkLibC();
13531371 test_step.addCSourceFile("test/c_abi/cfuncs.c", &.{"-std=c99"});
1354 test_step.setBuildMode(mode);
13551372
13561373 if (c_abi_target.isWindows() and (c_abi_target.getCpuArch() == .x86 or builtin.target.os.tag == .linux)) {
13571374 // 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
13631380 test_step.setNamePrefix(b.fmt("{s}-{s}-{s} ", .{
13641381 "test-c-abi",
13651382 triple_prefix,
1366 @tagName(mode),
1383 @tagName(optimize_mode),
13671384 }));
13681385
13691386 step.dependOn(&test_step.step);
test/translate_c.zig+16
......@@ -3900,4 +3900,20 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
39003900 \\pub const ZERO = @as(c_int, 0);
39013901 \\pub const WORLD = @as(c_int, 0o0000123);
39023902 });
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 });
39033919}