authorgravatar for fncontroloption@noreply.codeberg.orgFnControlOption <fncontroloption@noreply.codeberg.org> 2023-02-05 05:57:58-08:00
committergravatar for fncontroloption@noreply.codeberg.orgFnControlOption <fncontroloption@noreply.codeberg.org> 2023-02-05 05:57:58-08:00
logbaa877fd129c7c6eb3c87c3e219bb4dede67b0a0
treed707ff596a43200239b065d953f80811e5763a03
parent8e2af21cd99d1c033146b1ab15ab743533cbd743
parenta5b34a61ab61882bf55d87e4cbc8186215ecf320

Merge branch 'master' into lzma


219 files changed, 17461 insertions(+), 15159 deletions(-)

CMakeLists.txt+1-1
......@@ -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"
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"});
......@@ -151,17 +157,15 @@ pub fn build(b: *Builder) !void {
151157
152158 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: {
153159 if (strip == true) break :blk @as(u32, 0);
154 if (mode != .Debug) break :blk 0;
160 if (optimize != .Debug) break :blk 0;
155161 break :blk 4;
156162 };
157163
158 const exe = addCompilerStep(b);
164 const exe = addCompilerStep(b, optimize, target);
159165 exe.strip = strip;
160166 exe.sanitize_thread = sanitize_thread;
161167 exe.build_id = b.option(bool, "build-id", "Include a build id note") orelse false;
162168 exe.install();
163 exe.setBuildMode(mode);
164 exe.setTarget(target);
165169
166170 const compile_step = b.step("compile", "Build the self-hosted compiler");
167171 compile_step.dependOn(&exe.step);
......@@ -197,7 +201,7 @@ pub fn build(b: *Builder) !void {
197201 test_cases.linkLibC();
198202 }
199203
200 const is_debug = mode == .Debug;
204 const is_debug = optimize == .Debug;
201205 const enable_logging = b.option(bool, "log", "Enable debug logging with --debug-log") orelse is_debug;
202206 const enable_link_snapshots = b.option(bool, "link-snapshot", "Whether to enable linker state snapshots") orelse false;
203207
......@@ -362,25 +366,25 @@ pub fn build(b: *Builder) !void {
362366 test_step.dependOn(test_cases_step);
363367 }
364368
365 var chosen_modes: [4]builtin.Mode = undefined;
369 var chosen_opt_modes_buf: [4]builtin.Mode = undefined;
366370 var chosen_mode_index: usize = 0;
367371 if (!skip_debug) {
368 chosen_modes[chosen_mode_index] = builtin.Mode.Debug;
372 chosen_opt_modes_buf[chosen_mode_index] = builtin.Mode.Debug;
369373 chosen_mode_index += 1;
370374 }
371375 if (!skip_release_safe) {
372 chosen_modes[chosen_mode_index] = builtin.Mode.ReleaseSafe;
376 chosen_opt_modes_buf[chosen_mode_index] = builtin.Mode.ReleaseSafe;
373377 chosen_mode_index += 1;
374378 }
375379 if (!skip_release_fast) {
376 chosen_modes[chosen_mode_index] = builtin.Mode.ReleaseFast;
380 chosen_opt_modes_buf[chosen_mode_index] = builtin.Mode.ReleaseFast;
377381 chosen_mode_index += 1;
378382 }
379383 if (!skip_release_small) {
380 chosen_modes[chosen_mode_index] = builtin.Mode.ReleaseSmall;
384 chosen_opt_modes_buf[chosen_mode_index] = builtin.Mode.ReleaseSmall;
381385 chosen_mode_index += 1;
382386 }
383 const modes = chosen_modes[0..chosen_mode_index];
387 const optimization_modes = chosen_opt_modes_buf[0..chosen_mode_index];
384388
385389 // run stage1 `zig fmt` on this build.zig file just to make sure it works
386390 test_step.dependOn(&fmt_build_zig.step);
......@@ -393,7 +397,7 @@ pub fn build(b: *Builder) !void {
393397 "test/behavior.zig",
394398 "behavior",
395399 "Run the behavior tests",
396 modes,
400 optimization_modes,
397401 skip_single_threaded,
398402 skip_non_native,
399403 skip_libc,
......@@ -407,7 +411,7 @@ pub fn build(b: *Builder) !void {
407411 "lib/compiler_rt.zig",
408412 "compiler-rt",
409413 "Run the compiler_rt tests",
410 modes,
414 optimization_modes,
411415 true, // skip_single_threaded
412416 skip_non_native,
413417 true, // skip_libc
......@@ -421,7 +425,7 @@ pub fn build(b: *Builder) !void {
421425 "lib/c.zig",
422426 "universal-libc",
423427 "Run the universal libc tests",
424 modes,
428 optimization_modes,
425429 true, // skip_single_threaded
426430 skip_non_native,
427431 true, // skip_libc
......@@ -429,11 +433,11 @@ pub fn build(b: *Builder) !void {
429433 skip_stage2_tests or true, // TODO get these all passing
430434 ));
431435
432 test_step.dependOn(tests.addCompareOutputTests(b, test_filter, modes));
436 test_step.dependOn(tests.addCompareOutputTests(b, test_filter, optimization_modes));
433437 test_step.dependOn(tests.addStandaloneTests(
434438 b,
435439 test_filter,
436 modes,
440 optimization_modes,
437441 skip_non_native,
438442 enable_macos_sdk,
439443 target,
......@@ -446,10 +450,10 @@ pub fn build(b: *Builder) !void {
446450 enable_symlinks_windows,
447451 ));
448452 test_step.dependOn(tests.addCAbiTests(b, skip_non_native, skip_release));
449 test_step.dependOn(tests.addLinkTests(b, test_filter, modes, enable_macos_sdk, skip_stage2_tests, enable_symlinks_windows));
450 test_step.dependOn(tests.addStackTraceTests(b, test_filter, modes));
451 test_step.dependOn(tests.addCliTests(b, test_filter, modes));
452 test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, modes));
453 test_step.dependOn(tests.addLinkTests(b, test_filter, optimization_modes, enable_macos_sdk, skip_stage2_tests, enable_symlinks_windows));
454 test_step.dependOn(tests.addStackTraceTests(b, test_filter, optimization_modes));
455 test_step.dependOn(tests.addCliTests(b, test_filter, optimization_modes));
456 test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, optimization_modes));
453457 test_step.dependOn(tests.addTranslateCTests(b, test_filter));
454458 if (!skip_run_translated_c) {
455459 test_step.dependOn(tests.addRunTranslatedCTests(b, test_filter, target));
......@@ -463,7 +467,7 @@ pub fn build(b: *Builder) !void {
463467 "lib/std/std.zig",
464468 "std",
465469 "Run the standard library tests",
466 modes,
470 optimization_modes,
467471 skip_single_threaded,
468472 skip_non_native,
469473 skip_libc,
......@@ -474,7 +478,7 @@ pub fn build(b: *Builder) !void {
474478 try addWasiUpdateStep(b, version);
475479}
476480
477fn addWasiUpdateStep(b: *Builder, version: [:0]const u8) !void {
481fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void {
478482 const semver = try std.SemanticVersion.parse(version);
479483
480484 var target: std.zig.CrossTarget = .{
......@@ -483,9 +487,7 @@ fn addWasiUpdateStep(b: *Builder, version: [:0]const u8) !void {
483487 };
484488 target.cpu_features_add.addFeature(@enumToInt(std.Target.wasm.Feature.bulk_memory));
485489
486 const exe = addCompilerStep(b);
487 exe.setBuildMode(.ReleaseSmall);
488 exe.setTarget(target);
490 const exe = addCompilerStep(b, .ReleaseSmall, target);
489491
490492 const exe_options = b.addOptions();
491493 exe.addOptions("build_options", exe_options);
......@@ -512,8 +514,17 @@ fn addWasiUpdateStep(b: *Builder, version: [:0]const u8) !void {
512514 update_zig1_step.dependOn(&run_opt.step);
513515}
514516
515fn addCompilerStep(b: *Builder) *std.build.LibExeObjStep {
516 const exe = b.addExecutable("zig", "src/main.zig");
517fn addCompilerStep(
518 b: *std.Build,
519 optimize: std.builtin.OptimizeMode,
520 target: std.zig.CrossTarget,
521) *std.Build.CompileStep {
522 const exe = b.addExecutable(.{
523 .name = "zig",
524 .root_source_file = .{ .path = "src/main.zig" },
525 .target = target,
526 .optimize = optimize,
527 });
517528 exe.stack_size = stack_size;
518529 return exe;
519530}
......@@ -533,9 +544,9 @@ const exe_cflags = [_][]const u8{
533544};
534545
535546fn addCmakeCfgOptionsToExe(
536 b: *Builder,
547 b: *std.Build,
537548 cfg: CMakeConfig,
538 exe: *std.build.LibExeObjStep,
549 exe: *std.Build.CompileStep,
539550 use_zig_libcxx: bool,
540551) !void {
541552 if (exe.target.isDarwin()) {
......@@ -614,7 +625,7 @@ fn addCmakeCfgOptionsToExe(
614625 }
615626}
616627
617fn addStaticLlvmOptionsToExe(exe: *std.build.LibExeObjStep) !void {
628fn addStaticLlvmOptionsToExe(exe: *std.Build.CompileStep) !void {
618629 // Adds the Zig C++ sources which both stage1 and stage2 need.
619630 //
620631 // We need this because otherwise zig_clang_cc1_main.cpp ends up pulling
......@@ -651,9 +662,9 @@ fn addStaticLlvmOptionsToExe(exe: *std.build.LibExeObjStep) !void {
651662}
652663
653664fn addCxxKnownPath(
654 b: *Builder,
665 b: *std.Build,
655666 ctx: CMakeConfig,
656 exe: *std.build.LibExeObjStep,
667 exe: *std.Build.CompileStep,
657668 objname: []const u8,
658669 errtxt: ?[]const u8,
659670 need_cpp_includes: bool,
......@@ -686,7 +697,7 @@ fn addCxxKnownPath(
686697 }
687698}
688699
689fn addCMakeLibraryList(exe: *std.build.LibExeObjStep, list: []const u8) void {
700fn addCMakeLibraryList(exe: *std.Build.CompileStep, list: []const u8) void {
690701 var it = mem.tokenize(u8, list, ";");
691702 while (it.next()) |lib| {
692703 if (mem.startsWith(u8, lib, "-l")) {
......@@ -700,7 +711,7 @@ fn addCMakeLibraryList(exe: *std.build.LibExeObjStep, list: []const u8) void {
700711}
701712
702713const CMakeConfig = struct {
703 llvm_linkage: std.build.LibExeObjStep.Linkage,
714 llvm_linkage: std.Build.CompileStep.Linkage,
704715 cmake_binary_dir: []const u8,
705716 cmake_prefix_path: []const u8,
706717 cmake_static_library_prefix: []const u8,
......@@ -717,7 +728,7 @@ const CMakeConfig = struct {
717728
718729const max_config_h_bytes = 1 * 1024 * 1024;
719730
720fn findConfigH(b: *Builder, config_h_path_option: ?[]const u8) ?[]const u8 {
731fn findConfigH(b: *std.Build, config_h_path_option: ?[]const u8) ?[]const u8 {
721732 if (config_h_path_option) |path| {
722733 var config_h_or_err = fs.cwd().openFile(path, .{});
723734 if (config_h_or_err) |*file| {
......@@ -763,7 +774,7 @@ fn findConfigH(b: *Builder, config_h_path_option: ?[]const u8) ?[]const u8 {
763774 } else unreachable; // TODO should not need `else unreachable`.
764775}
765776
766fn parseConfigH(b: *Builder, config_h_text: []const u8) ?CMakeConfig {
777fn parseConfigH(b: *std.Build, config_h_text: []const u8) ?CMakeConfig {
767778 var ctx: CMakeConfig = .{
768779 .llvm_linkage = undefined,
769780 .cmake_binary_dir = undefined,
......@@ -852,7 +863,7 @@ fn parseConfigH(b: *Builder, config_h_text: []const u8) ?CMakeConfig {
852863 return ctx;
853864}
854865
855fn toNativePathSep(b: *Builder, s: []const u8) []u8 {
866fn toNativePathSep(b: *std.Build, s: []const u8) []u8 {
856867 const duplicated = b.allocator.dupe(u8, s) catch unreachable;
857868 for (duplicated) |*byte| switch (byte.*) {
858869 '/' => byte.* = fs.path.sep,
......@@ -861,166 +872,6 @@ fn toNativePathSep(b: *Builder, s: []const u8) []u8 {
861872 return duplicated;
862873}
863874
864const softfloat_sources = [_][]const u8{
865 "deps/SoftFloat-3e/source/8086/f128M_isSignalingNaN.c",
866 "deps/SoftFloat-3e/source/8086/extF80M_isSignalingNaN.c",
867 "deps/SoftFloat-3e/source/8086/s_commonNaNToF128M.c",
868 "deps/SoftFloat-3e/source/8086/s_commonNaNToExtF80M.c",
869 "deps/SoftFloat-3e/source/8086/s_commonNaNToF16UI.c",
870 "deps/SoftFloat-3e/source/8086/s_commonNaNToF32UI.c",
871 "deps/SoftFloat-3e/source/8086/s_commonNaNToF64UI.c",
872 "deps/SoftFloat-3e/source/8086/s_f128MToCommonNaN.c",
873 "deps/SoftFloat-3e/source/8086/s_extF80MToCommonNaN.c",
874 "deps/SoftFloat-3e/source/8086/s_f16UIToCommonNaN.c",
875 "deps/SoftFloat-3e/source/8086/s_f32UIToCommonNaN.c",
876 "deps/SoftFloat-3e/source/8086/s_f64UIToCommonNaN.c",
877 "deps/SoftFloat-3e/source/8086/s_propagateNaNF128M.c",
878 "deps/SoftFloat-3e/source/8086/s_propagateNaNExtF80M.c",
879 "deps/SoftFloat-3e/source/8086/s_propagateNaNF16UI.c",
880 "deps/SoftFloat-3e/source/8086/softfloat_raiseFlags.c",
881 "deps/SoftFloat-3e/source/f128M_add.c",
882 "deps/SoftFloat-3e/source/f128M_div.c",
883 "deps/SoftFloat-3e/source/f128M_eq.c",
884 "deps/SoftFloat-3e/source/f128M_eq_signaling.c",
885 "deps/SoftFloat-3e/source/f128M_le.c",
886 "deps/SoftFloat-3e/source/f128M_le_quiet.c",
887 "deps/SoftFloat-3e/source/f128M_lt.c",
888 "deps/SoftFloat-3e/source/f128M_lt_quiet.c",
889 "deps/SoftFloat-3e/source/f128M_mul.c",
890 "deps/SoftFloat-3e/source/f128M_mulAdd.c",
891 "deps/SoftFloat-3e/source/f128M_rem.c",
892 "deps/SoftFloat-3e/source/f128M_roundToInt.c",
893 "deps/SoftFloat-3e/source/f128M_sqrt.c",
894 "deps/SoftFloat-3e/source/f128M_sub.c",
895 "deps/SoftFloat-3e/source/f128M_to_f16.c",
896 "deps/SoftFloat-3e/source/f128M_to_f32.c",
897 "deps/SoftFloat-3e/source/f128M_to_f64.c",
898 "deps/SoftFloat-3e/source/f128M_to_extF80M.c",
899 "deps/SoftFloat-3e/source/f128M_to_i32.c",
900 "deps/SoftFloat-3e/source/f128M_to_i32_r_minMag.c",
901 "deps/SoftFloat-3e/source/f128M_to_i64.c",
902 "deps/SoftFloat-3e/source/f128M_to_i64_r_minMag.c",
903 "deps/SoftFloat-3e/source/f128M_to_ui32.c",
904 "deps/SoftFloat-3e/source/f128M_to_ui32_r_minMag.c",
905 "deps/SoftFloat-3e/source/f128M_to_ui64.c",
906 "deps/SoftFloat-3e/source/f128M_to_ui64_r_minMag.c",
907 "deps/SoftFloat-3e/source/extF80M_add.c",
908 "deps/SoftFloat-3e/source/extF80M_div.c",
909 "deps/SoftFloat-3e/source/extF80M_eq.c",
910 "deps/SoftFloat-3e/source/extF80M_le.c",
911 "deps/SoftFloat-3e/source/extF80M_lt.c",
912 "deps/SoftFloat-3e/source/extF80M_mul.c",
913 "deps/SoftFloat-3e/source/extF80M_rem.c",
914 "deps/SoftFloat-3e/source/extF80M_roundToInt.c",
915 "deps/SoftFloat-3e/source/extF80M_sqrt.c",
916 "deps/SoftFloat-3e/source/extF80M_sub.c",
917 "deps/SoftFloat-3e/source/extF80M_to_f16.c",
918 "deps/SoftFloat-3e/source/extF80M_to_f32.c",
919 "deps/SoftFloat-3e/source/extF80M_to_f64.c",
920 "deps/SoftFloat-3e/source/extF80M_to_f128M.c",
921 "deps/SoftFloat-3e/source/f16_add.c",
922 "deps/SoftFloat-3e/source/f16_div.c",
923 "deps/SoftFloat-3e/source/f16_eq.c",
924 "deps/SoftFloat-3e/source/f16_isSignalingNaN.c",
925 "deps/SoftFloat-3e/source/f16_lt.c",
926 "deps/SoftFloat-3e/source/f16_mul.c",
927 "deps/SoftFloat-3e/source/f16_mulAdd.c",
928 "deps/SoftFloat-3e/source/f16_rem.c",
929 "deps/SoftFloat-3e/source/f16_roundToInt.c",
930 "deps/SoftFloat-3e/source/f16_sqrt.c",
931 "deps/SoftFloat-3e/source/f16_sub.c",
932 "deps/SoftFloat-3e/source/f16_to_extF80M.c",
933 "deps/SoftFloat-3e/source/f16_to_f128M.c",
934 "deps/SoftFloat-3e/source/f16_to_f64.c",
935 "deps/SoftFloat-3e/source/f32_to_extF80M.c",
936 "deps/SoftFloat-3e/source/f32_to_f128M.c",
937 "deps/SoftFloat-3e/source/f64_to_extF80M.c",
938 "deps/SoftFloat-3e/source/f64_to_f128M.c",
939 "deps/SoftFloat-3e/source/f64_to_f16.c",
940 "deps/SoftFloat-3e/source/i32_to_f128M.c",
941 "deps/SoftFloat-3e/source/s_add256M.c",
942 "deps/SoftFloat-3e/source/s_addCarryM.c",
943 "deps/SoftFloat-3e/source/s_addComplCarryM.c",
944 "deps/SoftFloat-3e/source/s_addF128M.c",
945 "deps/SoftFloat-3e/source/s_addExtF80M.c",
946 "deps/SoftFloat-3e/source/s_addM.c",
947 "deps/SoftFloat-3e/source/s_addMagsF16.c",
948 "deps/SoftFloat-3e/source/s_addMagsF32.c",
949 "deps/SoftFloat-3e/source/s_addMagsF64.c",
950 "deps/SoftFloat-3e/source/s_approxRecip32_1.c",
951 "deps/SoftFloat-3e/source/s_approxRecipSqrt32_1.c",
952 "deps/SoftFloat-3e/source/s_approxRecipSqrt_1Ks.c",
953 "deps/SoftFloat-3e/source/s_approxRecip_1Ks.c",
954 "deps/SoftFloat-3e/source/s_compare128M.c",
955 "deps/SoftFloat-3e/source/s_compare96M.c",
956 "deps/SoftFloat-3e/source/s_compareNonnormExtF80M.c",
957 "deps/SoftFloat-3e/source/s_countLeadingZeros16.c",
958 "deps/SoftFloat-3e/source/s_countLeadingZeros32.c",
959 "deps/SoftFloat-3e/source/s_countLeadingZeros64.c",
960 "deps/SoftFloat-3e/source/s_countLeadingZeros8.c",
961 "deps/SoftFloat-3e/source/s_eq128.c",
962 "deps/SoftFloat-3e/source/s_invalidF128M.c",
963 "deps/SoftFloat-3e/source/s_invalidExtF80M.c",
964 "deps/SoftFloat-3e/source/s_isNaNF128M.c",
965 "deps/SoftFloat-3e/source/s_le128.c",
966 "deps/SoftFloat-3e/source/s_lt128.c",
967 "deps/SoftFloat-3e/source/s_mul128MTo256M.c",
968 "deps/SoftFloat-3e/source/s_mul64To128M.c",
969 "deps/SoftFloat-3e/source/s_mulAddF128M.c",
970 "deps/SoftFloat-3e/source/s_mulAddF16.c",
971 "deps/SoftFloat-3e/source/s_mulAddF32.c",
972 "deps/SoftFloat-3e/source/s_mulAddF64.c",
973 "deps/SoftFloat-3e/source/s_negXM.c",
974 "deps/SoftFloat-3e/source/s_normExtF80SigM.c",
975 "deps/SoftFloat-3e/source/s_normRoundPackMToF128M.c",
976 "deps/SoftFloat-3e/source/s_normRoundPackMToExtF80M.c",
977 "deps/SoftFloat-3e/source/s_normRoundPackToF16.c",
978 "deps/SoftFloat-3e/source/s_normRoundPackToF32.c",
979 "deps/SoftFloat-3e/source/s_normRoundPackToF64.c",
980 "deps/SoftFloat-3e/source/s_normSubnormalF128SigM.c",
981 "deps/SoftFloat-3e/source/s_normSubnormalF16Sig.c",
982 "deps/SoftFloat-3e/source/s_normSubnormalF32Sig.c",
983 "deps/SoftFloat-3e/source/s_normSubnormalF64Sig.c",
984 "deps/SoftFloat-3e/source/s_remStepMBy32.c",
985 "deps/SoftFloat-3e/source/s_roundMToI64.c",
986 "deps/SoftFloat-3e/source/s_roundMToUI64.c",
987 "deps/SoftFloat-3e/source/s_roundPackMToExtF80M.c",
988 "deps/SoftFloat-3e/source/s_roundPackMToF128M.c",
989 "deps/SoftFloat-3e/source/s_roundPackToF16.c",
990 "deps/SoftFloat-3e/source/s_roundPackToF32.c",
991 "deps/SoftFloat-3e/source/s_roundPackToF64.c",
992 "deps/SoftFloat-3e/source/s_roundToI32.c",
993 "deps/SoftFloat-3e/source/s_roundToI64.c",
994 "deps/SoftFloat-3e/source/s_roundToUI32.c",
995 "deps/SoftFloat-3e/source/s_roundToUI64.c",
996 "deps/SoftFloat-3e/source/s_shiftLeftM.c",
997 "deps/SoftFloat-3e/source/s_shiftNormSigF128M.c",
998 "deps/SoftFloat-3e/source/s_shiftRightJam256M.c",
999 "deps/SoftFloat-3e/source/s_shiftRightJam32.c",
1000 "deps/SoftFloat-3e/source/s_shiftRightJam64.c",
1001 "deps/SoftFloat-3e/source/s_shiftRightJamM.c",
1002 "deps/SoftFloat-3e/source/s_shiftRightM.c",
1003 "deps/SoftFloat-3e/source/s_shortShiftLeft64To96M.c",
1004 "deps/SoftFloat-3e/source/s_shortShiftLeftM.c",
1005 "deps/SoftFloat-3e/source/s_shortShiftRightExtendM.c",
1006 "deps/SoftFloat-3e/source/s_shortShiftRightJam64.c",
1007 "deps/SoftFloat-3e/source/s_shortShiftRightJamM.c",
1008 "deps/SoftFloat-3e/source/s_shortShiftRightM.c",
1009 "deps/SoftFloat-3e/source/s_sub1XM.c",
1010 "deps/SoftFloat-3e/source/s_sub256M.c",
1011 "deps/SoftFloat-3e/source/s_subM.c",
1012 "deps/SoftFloat-3e/source/s_subMagsF16.c",
1013 "deps/SoftFloat-3e/source/s_subMagsF32.c",
1014 "deps/SoftFloat-3e/source/s_subMagsF64.c",
1015 "deps/SoftFloat-3e/source/s_tryPropagateNaNF128M.c",
1016 "deps/SoftFloat-3e/source/s_tryPropagateNaNExtF80M.c",
1017 "deps/SoftFloat-3e/source/softfloat_state.c",
1018 "deps/SoftFloat-3e/source/ui32_to_f128M.c",
1019 "deps/SoftFloat-3e/source/ui64_to_f128M.c",
1020 "deps/SoftFloat-3e/source/ui32_to_extF80M.c",
1021 "deps/SoftFloat-3e/source/ui64_to_extF80M.c",
1022};
1023
1024875const zig_cpp_sources = [_][]const u8{
1025876 // These are planned to stay even when we are self-hosted.
1026877 "src/zig_llvm.cpp",
doc/langref.html.in+94-59
......@@ -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#}
......@@ -9588,7 +9604,7 @@ pub fn build(b: *Builder) void {
95889604 {#header_close#}
95899605
95909606 {#header_open|Single Threaded Builds#}
9591 <p>Zig has a compile option <kbd>--single-threaded</kbd> which has the following effects:</p>
9607 <p>Zig has a compile option <kbd>-fsingle-threaded</kbd> which has the following effects:</p>
95929608 <ul>
95939609 <li>All {#link|Thread Local Variables#} are treated as regular {#link|Container Level Variables#}.</li>
95949610 <li>The overhead of {#link|Async Functions#} becomes equivalent to function call overhead.</li>
......@@ -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/c.zig+1-1
......@@ -354,7 +354,7 @@ fn clone() callconv(.Naked) void {
354354 \\ ecall
355355 );
356356 },
357 .mips, .mipsel => {
357 .mips, .mipsel, .mips64, .mips64el => {
358358 // __clone(func, stack, flags, arg, ptid, tls, ctid)
359359 // 3, 4, 5, 6, 7, 8, 9
360360
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 zeros |
55| ✓ | __clzdi2 | u64 | ∅ | i32 | count leading zeros |
56| ✓ | __clzti2 | u128 | ∅ | i32 | count leading zeros |
57| ✓ | __ctzsi2 | u32 | ∅ | i32 | count trailing zeros |
58| ✓ | __ctzdi2 | u64 | ∅ | i32 | count trailing zeros |
59| ✓ | __ctzti2 | u128 | ∅ | i32 | count trailing zeros |
60| ✓ | __ffssi2 | u32 | ∅ | i32 | find least significant 1 bit |
61| ✓ | __ffsdi2 | u64 | ∅ | i32 | find least significant 1 bit |
62| ✓ | __ffsti2 | u128 | ∅ | i32 | find least significant 1 bit |
63| ✓ | __paritysi2 | u32 | ∅ | i32 | bit parity |
64| ✓ | __paritydi2 | u64 | ∅ | i32 | bit parity |
65| ✓ | __parityti2 | u128 | ∅ | i32 | bit parity |
66| ✓ | __popcountsi2 | u32 | ∅ | i32 | bit population |
67| ✓ | __popcountdi2 | u64 | ∅ | i32 | bit population |
68| ✓ | __popcountti2 | u128 | ∅ | i32 | bit population |
69| ✓ | __bswapsi2 | u32 | ∅ | i32 | byte swap |
70| ✓ | __bswapdi2 | u64 | ∅ | i32 | byte swap |
71| ✓ | __bswapti2 | u128 | ∅ | i32 | byte swap |
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 | u32 | u32 | i32 | `(a<b) -> 0, (a==b) -> 1, (a>b) -> 2` |
77| ✓ | __ucmpdi2 | u64 | u64 | i32 | .. |
78| ✓ | __ucmpti2 | u128 | u128 | 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 | float to int, 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 | float to uint, 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 |
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 |
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/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+1774
......@@ -0,0 +1,1774 @@
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
112modules: std.StringArrayHashMap(*Module),
113
114pub const ExecError = error{
115 ReadFailure,
116 ExitCodeFailure,
117 ProcessTerminated,
118 ExecNotSupported,
119} || std.ChildProcess.SpawnError;
120
121pub const PkgConfigError = error{
122 PkgConfigCrashed,
123 PkgConfigFailed,
124 PkgConfigNotInstalled,
125 PkgConfigInvalidOutput,
126};
127
128pub const PkgConfigPkg = struct {
129 name: []const u8,
130 desc: []const u8,
131};
132
133pub const CStd = enum {
134 C89,
135 C99,
136 C11,
137};
138
139const UserInputOptionsMap = StringHashMap(UserInputOption);
140const AvailableOptionsMap = StringHashMap(AvailableOption);
141
142const AvailableOption = struct {
143 name: []const u8,
144 type_id: TypeId,
145 description: []const u8,
146 /// If the `type_id` is `enum` this provides the list of enum options
147 enum_options: ?[]const []const u8,
148};
149
150const UserInputOption = struct {
151 name: []const u8,
152 value: UserValue,
153 used: bool,
154};
155
156const UserValue = union(enum) {
157 flag: void,
158 scalar: []const u8,
159 list: ArrayList([]const u8),
160 map: StringHashMap(*const UserValue),
161};
162
163const TypeId = enum {
164 bool,
165 int,
166 float,
167 @"enum",
168 string,
169 list,
170};
171
172const TopLevelStep = struct {
173 pub const base_id = .top_level;
174
175 step: Step,
176 description: []const u8,
177};
178
179pub const DirList = struct {
180 lib_dir: ?[]const u8 = null,
181 exe_dir: ?[]const u8 = null,
182 include_dir: ?[]const u8 = null,
183};
184
185pub fn create(
186 allocator: Allocator,
187 zig_exe: []const u8,
188 build_root: []const u8,
189 cache_root: []const u8,
190 global_cache_root: []const u8,
191 host: NativeTargetInfo,
192) !*Build {
193 const env_map = try allocator.create(EnvMap);
194 env_map.* = try process.getEnvMap(allocator);
195
196 const self = try allocator.create(Build);
197 self.* = Build{
198 .zig_exe = zig_exe,
199 .build_root = build_root,
200 .cache_root = try fs.path.relative(allocator, build_root, cache_root),
201 .global_cache_root = global_cache_root,
202 .verbose = false,
203 .verbose_link = false,
204 .verbose_cc = false,
205 .verbose_air = false,
206 .verbose_llvm_ir = false,
207 .verbose_cimport = false,
208 .verbose_llvm_cpu_features = false,
209 .prominent_compile_errors = false,
210 .invalid_user_input = false,
211 .allocator = allocator,
212 .user_input_options = UserInputOptionsMap.init(allocator),
213 .available_options_map = AvailableOptionsMap.init(allocator),
214 .available_options_list = ArrayList(AvailableOption).init(allocator),
215 .top_level_steps = ArrayList(*TopLevelStep).init(allocator),
216 .default_step = undefined,
217 .env_map = env_map,
218 .search_prefixes = ArrayList([]const u8).init(allocator),
219 .install_prefix = undefined,
220 .lib_dir = undefined,
221 .exe_dir = undefined,
222 .h_dir = undefined,
223 .dest_dir = env_map.get("DESTDIR"),
224 .installed_files = ArrayList(InstalledFile).init(allocator),
225 .install_tls = TopLevelStep{
226 .step = Step.initNoOp(.top_level, "install", allocator),
227 .description = "Copy build artifacts to prefix path",
228 },
229 .uninstall_tls = TopLevelStep{
230 .step = Step.init(.top_level, "uninstall", allocator, makeUninstall),
231 .description = "Remove build artifacts from prefix path",
232 },
233 .override_lib_dir = null,
234 .install_path = undefined,
235 .args = null,
236 .host = host,
237 .modules = std.StringArrayHashMap(*Module).init(allocator),
238 };
239 try self.top_level_steps.append(&self.install_tls);
240 try self.top_level_steps.append(&self.uninstall_tls);
241 self.default_step = &self.install_tls.step;
242 return self;
243}
244
245fn createChild(
246 parent: *Build,
247 dep_name: []const u8,
248 build_root: []const u8,
249 args: anytype,
250) !*Build {
251 const child = try createChildOnly(parent, dep_name, build_root);
252 try applyArgs(child, args);
253 return child;
254}
255
256fn createChildOnly(parent: *Build, dep_name: []const u8, build_root: []const u8) !*Build {
257 const allocator = parent.allocator;
258 const child = try allocator.create(Build);
259 child.* = .{
260 .allocator = allocator,
261 .install_tls = .{
262 .step = Step.initNoOp(.top_level, "install", allocator),
263 .description = "Copy build artifacts to prefix path",
264 },
265 .uninstall_tls = .{
266 .step = Step.init(.top_level, "uninstall", allocator, makeUninstall),
267 .description = "Remove build artifacts from prefix path",
268 },
269 .user_input_options = UserInputOptionsMap.init(allocator),
270 .available_options_map = AvailableOptionsMap.init(allocator),
271 .available_options_list = ArrayList(AvailableOption).init(allocator),
272 .verbose = parent.verbose,
273 .verbose_link = parent.verbose_link,
274 .verbose_cc = parent.verbose_cc,
275 .verbose_air = parent.verbose_air,
276 .verbose_llvm_ir = parent.verbose_llvm_ir,
277 .verbose_cimport = parent.verbose_cimport,
278 .verbose_llvm_cpu_features = parent.verbose_llvm_cpu_features,
279 .prominent_compile_errors = parent.prominent_compile_errors,
280 .color = parent.color,
281 .reference_trace = parent.reference_trace,
282 .invalid_user_input = false,
283 .zig_exe = parent.zig_exe,
284 .default_step = undefined,
285 .env_map = parent.env_map,
286 .top_level_steps = ArrayList(*TopLevelStep).init(allocator),
287 .install_prefix = undefined,
288 .dest_dir = parent.dest_dir,
289 .lib_dir = parent.lib_dir,
290 .exe_dir = parent.exe_dir,
291 .h_dir = parent.h_dir,
292 .install_path = parent.install_path,
293 .sysroot = parent.sysroot,
294 .search_prefixes = ArrayList([]const u8).init(allocator),
295 .libc_file = parent.libc_file,
296 .installed_files = ArrayList(InstalledFile).init(allocator),
297 .build_root = build_root,
298 .cache_root = parent.cache_root,
299 .global_cache_root = parent.global_cache_root,
300 .override_lib_dir = parent.override_lib_dir,
301 .debug_log_scopes = parent.debug_log_scopes,
302 .debug_compile_errors = parent.debug_compile_errors,
303 .enable_darling = parent.enable_darling,
304 .enable_qemu = parent.enable_qemu,
305 .enable_rosetta = parent.enable_rosetta,
306 .enable_wasmtime = parent.enable_wasmtime,
307 .enable_wine = parent.enable_wine,
308 .glibc_runtimes_dir = parent.glibc_runtimes_dir,
309 .host = parent.host,
310 .dep_prefix = parent.fmt("{s}{s}.", .{ parent.dep_prefix, dep_name }),
311 .modules = std.StringArrayHashMap(*Module).init(allocator),
312 };
313 try child.top_level_steps.append(&child.install_tls);
314 try child.top_level_steps.append(&child.uninstall_tls);
315 child.default_step = &child.install_tls.step;
316 return child;
317}
318
319fn applyArgs(b: *Build, args: anytype) !void {
320 inline for (@typeInfo(@TypeOf(args)).Struct.fields) |field| {
321 const v = @field(args, field.name);
322 const T = @TypeOf(v);
323 switch (T) {
324 CrossTarget => {
325 try b.user_input_options.put(field.name, .{
326 .name = field.name,
327 .value = .{ .scalar = try v.zigTriple(b.allocator) },
328 .used = false,
329 });
330 try b.user_input_options.put("cpu", .{
331 .name = "cpu",
332 .value = .{ .scalar = try serializeCpu(b.allocator, v.getCpu()) },
333 .used = false,
334 });
335 },
336 []const u8 => {
337 try b.user_input_options.put(field.name, .{
338 .name = field.name,
339 .value = .{ .scalar = v },
340 .used = false,
341 });
342 },
343 else => switch (@typeInfo(T)) {
344 .Bool => {
345 try b.user_input_options.put(field.name, .{
346 .name = field.name,
347 .value = .{ .scalar = if (v) "true" else "false" },
348 .used = false,
349 });
350 },
351 .Enum => {
352 try b.user_input_options.put(field.name, .{
353 .name = field.name,
354 .value = .{ .scalar = @tagName(v) },
355 .used = false,
356 });
357 },
358 .Int => {
359 try b.user_input_options.put(field.name, .{
360 .name = field.name,
361 .value = .{ .scalar = try std.fmt.allocPrint(b.allocator, "{d}", .{v}) },
362 .used = false,
363 });
364 },
365 else => @compileError("option '" ++ field.name ++ "' has unsupported type: " ++ @typeName(T)),
366 },
367 }
368 }
369 const Hasher = std.crypto.auth.siphash.SipHash128(1, 3);
370 // Random bytes to make unique. Refresh this with new random bytes when
371 // implementation is modified in a non-backwards-compatible way.
372 var hash = Hasher.init("ZaEsvQ5ClaA2IdH9");
373 hash.update(b.dep_prefix);
374 // TODO additionally update the hash with `args`.
375
376 var digest: [16]u8 = undefined;
377 hash.final(&digest);
378 var hash_basename: [digest.len * 2]u8 = undefined;
379 _ = std.fmt.bufPrint(&hash_basename, "{s}", .{std.fmt.fmtSliceHexLower(&digest)}) catch
380 unreachable;
381
382 const install_prefix = b.pathJoin(&.{ b.cache_root, "i", &hash_basename });
383 b.resolveInstallPrefix(install_prefix, .{});
384}
385
386pub fn destroy(self: *Build) void {
387 self.env_map.deinit();
388 self.top_level_steps.deinit();
389 self.allocator.destroy(self);
390}
391
392/// This function is intended to be called by lib/build_runner.zig, not a build.zig file.
393pub fn resolveInstallPrefix(self: *Build, install_prefix: ?[]const u8, dir_list: DirList) void {
394 if (self.dest_dir) |dest_dir| {
395 self.install_prefix = install_prefix orelse "/usr";
396 self.install_path = self.pathJoin(&.{ dest_dir, self.install_prefix });
397 } else {
398 self.install_prefix = install_prefix orelse
399 (self.pathJoin(&.{ self.build_root, "zig-out" }));
400 self.install_path = self.install_prefix;
401 }
402
403 var lib_list = [_][]const u8{ self.install_path, "lib" };
404 var exe_list = [_][]const u8{ self.install_path, "bin" };
405 var h_list = [_][]const u8{ self.install_path, "include" };
406
407 if (dir_list.lib_dir) |dir| {
408 if (std.fs.path.isAbsolute(dir)) lib_list[0] = self.dest_dir orelse "";
409 lib_list[1] = dir;
410 }
411
412 if (dir_list.exe_dir) |dir| {
413 if (std.fs.path.isAbsolute(dir)) exe_list[0] = self.dest_dir orelse "";
414 exe_list[1] = dir;
415 }
416
417 if (dir_list.include_dir) |dir| {
418 if (std.fs.path.isAbsolute(dir)) h_list[0] = self.dest_dir orelse "";
419 h_list[1] = dir;
420 }
421
422 self.lib_dir = self.pathJoin(&lib_list);
423 self.exe_dir = self.pathJoin(&exe_list);
424 self.h_dir = self.pathJoin(&h_list);
425}
426
427pub fn addOptions(self: *Build) *OptionsStep {
428 return OptionsStep.create(self);
429}
430
431pub const ExecutableOptions = struct {
432 name: []const u8,
433 root_source_file: ?FileSource = null,
434 version: ?std.builtin.Version = null,
435 target: CrossTarget = .{},
436 optimize: std.builtin.Mode = .Debug,
437 linkage: ?CompileStep.Linkage = null,
438};
439
440pub fn addExecutable(b: *Build, options: ExecutableOptions) *CompileStep {
441 return CompileStep.create(b, .{
442 .name = options.name,
443 .root_source_file = options.root_source_file,
444 .version = options.version,
445 .target = options.target,
446 .optimize = options.optimize,
447 .kind = .exe,
448 .linkage = options.linkage,
449 });
450}
451
452pub const ObjectOptions = struct {
453 name: []const u8,
454 root_source_file: ?FileSource = null,
455 target: CrossTarget,
456 optimize: std.builtin.Mode,
457};
458
459pub fn addObject(b: *Build, options: ObjectOptions) *CompileStep {
460 return CompileStep.create(b, .{
461 .name = options.name,
462 .root_source_file = options.root_source_file,
463 .target = options.target,
464 .optimize = options.optimize,
465 .kind = .obj,
466 });
467}
468
469pub const SharedLibraryOptions = struct {
470 name: []const u8,
471 root_source_file: ?FileSource = null,
472 version: ?std.builtin.Version = null,
473 target: CrossTarget,
474 optimize: std.builtin.Mode,
475};
476
477pub fn addSharedLibrary(b: *Build, options: SharedLibraryOptions) *CompileStep {
478 return CompileStep.create(b, .{
479 .name = options.name,
480 .root_source_file = options.root_source_file,
481 .kind = .lib,
482 .linkage = .dynamic,
483 .version = options.version,
484 .target = options.target,
485 .optimize = options.optimize,
486 });
487}
488
489pub const StaticLibraryOptions = struct {
490 name: []const u8,
491 root_source_file: ?FileSource = null,
492 target: CrossTarget,
493 optimize: std.builtin.Mode,
494 version: ?std.builtin.Version = null,
495};
496
497pub fn addStaticLibrary(b: *Build, options: StaticLibraryOptions) *CompileStep {
498 return CompileStep.create(b, .{
499 .name = options.name,
500 .root_source_file = options.root_source_file,
501 .kind = .lib,
502 .linkage = .static,
503 .version = options.version,
504 .target = options.target,
505 .optimize = options.optimize,
506 });
507}
508
509pub const TestOptions = struct {
510 name: []const u8 = "test",
511 kind: CompileStep.Kind = .@"test",
512 root_source_file: FileSource,
513 target: CrossTarget = .{},
514 optimize: std.builtin.Mode = .Debug,
515 version: ?std.builtin.Version = null,
516};
517
518pub fn addTest(b: *Build, options: TestOptions) *CompileStep {
519 return CompileStep.create(b, .{
520 .name = options.name,
521 .kind = options.kind,
522 .root_source_file = options.root_source_file,
523 .target = options.target,
524 .optimize = options.optimize,
525 });
526}
527
528pub const AssemblyOptions = struct {
529 name: []const u8,
530 source_file: FileSource,
531 target: CrossTarget,
532 optimize: std.builtin.Mode,
533};
534
535pub fn addAssembly(b: *Build, options: AssemblyOptions) *CompileStep {
536 const obj_step = CompileStep.create(b, .{
537 .name = options.name,
538 .root_source_file = null,
539 .target = options.target,
540 .optimize = options.optimize,
541 });
542 obj_step.addAssemblyFileSource(options.source_file.dupe(b));
543 return obj_step;
544}
545
546pub const AddModuleOptions = struct {
547 name: []const u8,
548 source_file: FileSource,
549 dependencies: []const ModuleDependency = &.{},
550};
551
552pub fn addModule(b: *Build, options: AddModuleOptions) void {
553 b.modules.put(b.dupe(options.name), b.createModule(.{
554 .source_file = options.source_file,
555 .dependencies = options.dependencies,
556 })) catch @panic("OOM");
557}
558
559pub const ModuleDependency = struct {
560 name: []const u8,
561 module: *Module,
562};
563
564pub const CreateModuleOptions = struct {
565 source_file: FileSource,
566 dependencies: []const ModuleDependency = &.{},
567};
568
569/// Prefer to use `addModule` which will make the module available to other
570/// packages which depend on this package.
571pub fn createModule(b: *Build, options: CreateModuleOptions) *Module {
572 const module = b.allocator.create(Module) catch @panic("OOM");
573 module.* = .{
574 .builder = b,
575 .source_file = options.source_file,
576 .dependencies = moduleDependenciesToArrayHashMap(b.allocator, options.dependencies),
577 };
578 return module;
579}
580
581fn moduleDependenciesToArrayHashMap(arena: Allocator, deps: []const ModuleDependency) std.StringArrayHashMap(*Module) {
582 var result = std.StringArrayHashMap(*Module).init(arena);
583 for (deps) |dep| {
584 result.put(dep.name, dep.module) catch @panic("OOM");
585 }
586 return result;
587}
588
589/// Initializes a RunStep with argv, which must at least have the path to the
590/// executable. More command line arguments can be added with `addArg`,
591/// `addArgs`, and `addArtifactArg`.
592/// Be careful using this function, as it introduces a system dependency.
593/// To run an executable built with zig build, see `CompileStep.run`.
594pub fn addSystemCommand(self: *Build, argv: []const []const u8) *RunStep {
595 assert(argv.len >= 1);
596 const run_step = RunStep.create(self, self.fmt("run {s}", .{argv[0]}));
597 run_step.addArgs(argv);
598 return run_step;
599}
600
601/// Using the `values` provided, produces a C header file, possibly based on a
602/// template input file (e.g. config.h.in).
603/// When an input template file is provided, this function will fail the build
604/// when an option not found in the input file is provided in `values`, and
605/// when an option found in the input file is missing from `values`.
606pub fn addConfigHeader(
607 b: *Build,
608 options: ConfigHeaderStep.Options,
609 values: anytype,
610) *ConfigHeaderStep {
611 const config_header_step = ConfigHeaderStep.create(b, options);
612 config_header_step.addValues(values);
613 return config_header_step;
614}
615
616/// Allocator.dupe without the need to handle out of memory.
617pub fn dupe(self: *Build, bytes: []const u8) []u8 {
618 return self.allocator.dupe(u8, bytes) catch @panic("OOM");
619}
620
621/// Duplicates an array of strings without the need to handle out of memory.
622pub fn dupeStrings(self: *Build, strings: []const []const u8) [][]u8 {
623 const array = self.allocator.alloc([]u8, strings.len) catch @panic("OOM");
624 for (strings) |s, i| {
625 array[i] = self.dupe(s);
626 }
627 return array;
628}
629
630/// Duplicates a path and converts all slashes to the OS's canonical path separator.
631pub fn dupePath(self: *Build, bytes: []const u8) []u8 {
632 const the_copy = self.dupe(bytes);
633 for (the_copy) |*byte| {
634 switch (byte.*) {
635 '/', '\\' => byte.* = fs.path.sep,
636 else => {},
637 }
638 }
639 return the_copy;
640}
641
642pub fn addWriteFile(self: *Build, file_path: []const u8, data: []const u8) *WriteFileStep {
643 const write_file_step = self.addWriteFiles();
644 write_file_step.add(file_path, data);
645 return write_file_step;
646}
647
648pub fn addWriteFiles(self: *Build) *WriteFileStep {
649 const write_file_step = self.allocator.create(WriteFileStep) catch @panic("OOM");
650 write_file_step.* = WriteFileStep.init(self);
651 return write_file_step;
652}
653
654pub fn addLog(self: *Build, comptime format: []const u8, args: anytype) *LogStep {
655 const data = self.fmt(format, args);
656 const log_step = self.allocator.create(LogStep) catch @panic("OOM");
657 log_step.* = LogStep.init(self, data);
658 return log_step;
659}
660
661pub fn addRemoveDirTree(self: *Build, dir_path: []const u8) *RemoveDirStep {
662 const remove_dir_step = self.allocator.create(RemoveDirStep) catch @panic("OOM");
663 remove_dir_step.* = RemoveDirStep.init(self, dir_path);
664 return remove_dir_step;
665}
666
667pub fn addFmt(self: *Build, paths: []const []const u8) *FmtStep {
668 return FmtStep.create(self, paths);
669}
670
671pub fn addTranslateC(self: *Build, options: TranslateCStep.Options) *TranslateCStep {
672 return TranslateCStep.create(self, options);
673}
674
675pub fn make(self: *Build, step_names: []const []const u8) !void {
676 try self.makePath(self.cache_root);
677
678 var wanted_steps = ArrayList(*Step).init(self.allocator);
679 defer wanted_steps.deinit();
680
681 if (step_names.len == 0) {
682 try wanted_steps.append(self.default_step);
683 } else {
684 for (step_names) |step_name| {
685 const s = try self.getTopLevelStepByName(step_name);
686 try wanted_steps.append(s);
687 }
688 }
689
690 for (wanted_steps.items) |s| {
691 try self.makeOneStep(s);
692 }
693}
694
695pub fn getInstallStep(self: *Build) *Step {
696 return &self.install_tls.step;
697}
698
699pub fn getUninstallStep(self: *Build) *Step {
700 return &self.uninstall_tls.step;
701}
702
703fn makeUninstall(uninstall_step: *Step) anyerror!void {
704 const uninstall_tls = @fieldParentPtr(TopLevelStep, "step", uninstall_step);
705 const self = @fieldParentPtr(Build, "uninstall_tls", uninstall_tls);
706
707 for (self.installed_files.items) |installed_file| {
708 const full_path = self.getInstallPath(installed_file.dir, installed_file.path);
709 if (self.verbose) {
710 log.info("rm {s}", .{full_path});
711 }
712 fs.cwd().deleteTree(full_path) catch {};
713 }
714
715 // TODO remove empty directories
716}
717
718fn makeOneStep(self: *Build, s: *Step) anyerror!void {
719 if (s.loop_flag) {
720 log.err("Dependency loop detected:\n {s}", .{s.name});
721 return error.DependencyLoopDetected;
722 }
723 s.loop_flag = true;
724
725 for (s.dependencies.items) |dep| {
726 self.makeOneStep(dep) catch |err| {
727 if (err == error.DependencyLoopDetected) {
728 log.err(" {s}", .{s.name});
729 }
730 return err;
731 };
732 }
733
734 s.loop_flag = false;
735
736 try s.make();
737}
738
739fn getTopLevelStepByName(self: *Build, name: []const u8) !*Step {
740 for (self.top_level_steps.items) |top_level_step| {
741 if (mem.eql(u8, top_level_step.step.name, name)) {
742 return &top_level_step.step;
743 }
744 }
745 log.err("Cannot run step '{s}' because it does not exist", .{name});
746 return error.InvalidStepName;
747}
748
749pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_raw: []const u8) ?T {
750 const name = self.dupe(name_raw);
751 const description = self.dupe(description_raw);
752 const type_id = comptime typeToEnum(T);
753 const enum_options = if (type_id == .@"enum") blk: {
754 const fields = comptime std.meta.fields(T);
755 var options = ArrayList([]const u8).initCapacity(self.allocator, fields.len) catch @panic("OOM");
756
757 inline for (fields) |field| {
758 options.appendAssumeCapacity(field.name);
759 }
760
761 break :blk options.toOwnedSlice() catch @panic("OOM");
762 } else null;
763 const available_option = AvailableOption{
764 .name = name,
765 .type_id = type_id,
766 .description = description,
767 .enum_options = enum_options,
768 };
769 if ((self.available_options_map.fetchPut(name, available_option) catch @panic("OOM")) != null) {
770 panic("Option '{s}' declared twice", .{name});
771 }
772 self.available_options_list.append(available_option) catch @panic("OOM");
773
774 const option_ptr = self.user_input_options.getPtr(name) orelse return null;
775 option_ptr.used = true;
776 switch (type_id) {
777 .bool => switch (option_ptr.value) {
778 .flag => return true,
779 .scalar => |s| {
780 if (mem.eql(u8, s, "true")) {
781 return true;
782 } else if (mem.eql(u8, s, "false")) {
783 return false;
784 } else {
785 log.err("Expected -D{s} to be a boolean, but received '{s}'\n", .{ name, s });
786 self.markInvalidUserInput();
787 return null;
788 }
789 },
790 .list, .map => {
791 log.err("Expected -D{s} to be a boolean, but received a {s}.\n", .{
792 name, @tagName(option_ptr.value),
793 });
794 self.markInvalidUserInput();
795 return null;
796 },
797 },
798 .int => switch (option_ptr.value) {
799 .flag, .list, .map => {
800 log.err("Expected -D{s} to be an integer, but received a {s}.\n", .{
801 name, @tagName(option_ptr.value),
802 });
803 self.markInvalidUserInput();
804 return null;
805 },
806 .scalar => |s| {
807 const n = std.fmt.parseInt(T, s, 10) catch |err| switch (err) {
808 error.Overflow => {
809 log.err("-D{s} value {s} cannot fit into type {s}.\n", .{ name, s, @typeName(T) });
810 self.markInvalidUserInput();
811 return null;
812 },
813 else => {
814 log.err("Expected -D{s} to be an integer of type {s}.\n", .{ name, @typeName(T) });
815 self.markInvalidUserInput();
816 return null;
817 },
818 };
819 return n;
820 },
821 },
822 .float => switch (option_ptr.value) {
823 .flag, .map, .list => {
824 log.err("Expected -D{s} to be a float, but received a {s}.\n", .{
825 name, @tagName(option_ptr.value),
826 });
827 self.markInvalidUserInput();
828 return null;
829 },
830 .scalar => |s| {
831 const n = std.fmt.parseFloat(T, s) catch {
832 log.err("Expected -D{s} to be a float of type {s}.\n", .{ name, @typeName(T) });
833 self.markInvalidUserInput();
834 return null;
835 };
836 return n;
837 },
838 },
839 .@"enum" => switch (option_ptr.value) {
840 .flag, .map, .list => {
841 log.err("Expected -D{s} to be an enum, but received a {s}.\n", .{
842 name, @tagName(option_ptr.value),
843 });
844 self.markInvalidUserInput();
845 return null;
846 },
847 .scalar => |s| {
848 if (std.meta.stringToEnum(T, s)) |enum_lit| {
849 return enum_lit;
850 } else {
851 log.err("Expected -D{s} to be of type {s}.\n", .{ name, @typeName(T) });
852 self.markInvalidUserInput();
853 return null;
854 }
855 },
856 },
857 .string => switch (option_ptr.value) {
858 .flag, .list, .map => {
859 log.err("Expected -D{s} to be a string, but received a {s}.\n", .{
860 name, @tagName(option_ptr.value),
861 });
862 self.markInvalidUserInput();
863 return null;
864 },
865 .scalar => |s| return s,
866 },
867 .list => switch (option_ptr.value) {
868 .flag, .map => {
869 log.err("Expected -D{s} to be a list, but received a {s}.\n", .{
870 name, @tagName(option_ptr.value),
871 });
872 self.markInvalidUserInput();
873 return null;
874 },
875 .scalar => |s| {
876 return self.allocator.dupe([]const u8, &[_][]const u8{s}) catch @panic("OOM");
877 },
878 .list => |lst| return lst.items,
879 },
880 }
881}
882
883pub fn step(self: *Build, name: []const u8, description: []const u8) *Step {
884 const step_info = self.allocator.create(TopLevelStep) catch @panic("OOM");
885 step_info.* = TopLevelStep{
886 .step = Step.initNoOp(.top_level, name, self.allocator),
887 .description = self.dupe(description),
888 };
889 self.top_level_steps.append(step_info) catch @panic("OOM");
890 return &step_info.step;
891}
892
893pub const StandardOptimizeOptionOptions = struct {
894 preferred_optimize_mode: ?std.builtin.Mode = null,
895};
896
897pub fn standardOptimizeOption(self: *Build, options: StandardOptimizeOptionOptions) std.builtin.Mode {
898 if (options.preferred_optimize_mode) |mode| {
899 if (self.option(bool, "release", "optimize for end users") orelse false) {
900 return mode;
901 } else {
902 return .Debug;
903 }
904 } else {
905 return self.option(
906 std.builtin.Mode,
907 "optimize",
908 "prioritize performance, safety, or binary size (-O flag)",
909 ) orelse .Debug;
910 }
911}
912
913pub const StandardTargetOptionsArgs = struct {
914 whitelist: ?[]const CrossTarget = null,
915
916 default_target: CrossTarget = CrossTarget{},
917};
918
919/// Exposes standard `zig build` options for choosing a target.
920pub fn standardTargetOptions(self: *Build, args: StandardTargetOptionsArgs) CrossTarget {
921 const maybe_triple = self.option(
922 []const u8,
923 "target",
924 "The CPU architecture, OS, and ABI to build for",
925 );
926 const mcpu = self.option([]const u8, "cpu", "Target CPU features to add or subtract");
927
928 if (maybe_triple == null and mcpu == null) {
929 return args.default_target;
930 }
931
932 const triple = maybe_triple orelse "native";
933
934 var diags: CrossTarget.ParseOptions.Diagnostics = .{};
935 const selected_target = CrossTarget.parse(.{
936 .arch_os_abi = triple,
937 .cpu_features = mcpu,
938 .diagnostics = &diags,
939 }) catch |err| switch (err) {
940 error.UnknownCpuModel => {
941 log.err("Unknown CPU: '{s}'\nAvailable CPUs for architecture '{s}':", .{
942 diags.cpu_name.?,
943 @tagName(diags.arch.?),
944 });
945 for (diags.arch.?.allCpuModels()) |cpu| {
946 log.err(" {s}", .{cpu.name});
947 }
948 self.markInvalidUserInput();
949 return args.default_target;
950 },
951 error.UnknownCpuFeature => {
952 log.err(
953 \\Unknown CPU feature: '{s}'
954 \\Available CPU features for architecture '{s}':
955 \\
956 , .{
957 diags.unknown_feature_name.?,
958 @tagName(diags.arch.?),
959 });
960 for (diags.arch.?.allFeaturesList()) |feature| {
961 log.err(" {s}: {s}", .{ feature.name, feature.description });
962 }
963 self.markInvalidUserInput();
964 return args.default_target;
965 },
966 error.UnknownOperatingSystem => {
967 log.err(
968 \\Unknown OS: '{s}'
969 \\Available operating systems:
970 \\
971 , .{diags.os_name.?});
972 inline for (std.meta.fields(std.Target.Os.Tag)) |field| {
973 log.err(" {s}", .{field.name});
974 }
975 self.markInvalidUserInput();
976 return args.default_target;
977 },
978 else => |e| {
979 log.err("Unable to parse target '{s}': {s}\n", .{ triple, @errorName(e) });
980 self.markInvalidUserInput();
981 return args.default_target;
982 },
983 };
984
985 const selected_canonicalized_triple = selected_target.zigTriple(self.allocator) catch @panic("OOM");
986
987 if (args.whitelist) |list| whitelist_check: {
988 // Make sure it's a match of one of the list.
989 var mismatch_triple = true;
990 var mismatch_cpu_features = true;
991 var whitelist_item = CrossTarget{};
992 for (list) |t| {
993 mismatch_cpu_features = true;
994 mismatch_triple = true;
995
996 const t_triple = t.zigTriple(self.allocator) catch @panic("OOM");
997 if (mem.eql(u8, t_triple, selected_canonicalized_triple)) {
998 mismatch_triple = false;
999 whitelist_item = t;
1000 if (t.getCpuFeatures().isSuperSetOf(selected_target.getCpuFeatures())) {
1001 mismatch_cpu_features = false;
1002 break :whitelist_check;
1003 } else {
1004 break;
1005 }
1006 }
1007 }
1008 if (mismatch_triple) {
1009 log.err("Chosen target '{s}' does not match one of the supported targets:", .{
1010 selected_canonicalized_triple,
1011 });
1012 for (list) |t| {
1013 const t_triple = t.zigTriple(self.allocator) catch @panic("OOM");
1014 log.err(" {s}", .{t_triple});
1015 }
1016 } else {
1017 assert(mismatch_cpu_features);
1018 const whitelist_cpu = whitelist_item.getCpu();
1019 const selected_cpu = selected_target.getCpu();
1020 log.err("Chosen CPU model '{s}' does not match one of the supported targets:", .{
1021 selected_cpu.model.name,
1022 });
1023 log.err(" Supported feature Set: ", .{});
1024 const all_features = whitelist_cpu.arch.allFeaturesList();
1025 var populated_cpu_features = whitelist_cpu.model.features;
1026 populated_cpu_features.populateDependencies(all_features);
1027 for (all_features) |feature, i_usize| {
1028 const i = @intCast(std.Target.Cpu.Feature.Set.Index, i_usize);
1029 const in_cpu_set = populated_cpu_features.isEnabled(i);
1030 if (in_cpu_set) {
1031 log.err("{s} ", .{feature.name});
1032 }
1033 }
1034 log.err(" Remove: ", .{});
1035 for (all_features) |feature, i_usize| {
1036 const i = @intCast(std.Target.Cpu.Feature.Set.Index, i_usize);
1037 const in_cpu_set = populated_cpu_features.isEnabled(i);
1038 const in_actual_set = selected_cpu.features.isEnabled(i);
1039 if (in_actual_set and !in_cpu_set) {
1040 log.err("{s} ", .{feature.name});
1041 }
1042 }
1043 }
1044 self.markInvalidUserInput();
1045 return args.default_target;
1046 }
1047
1048 return selected_target;
1049}
1050
1051pub fn addUserInputOption(self: *Build, name_raw: []const u8, value_raw: []const u8) !bool {
1052 const name = self.dupe(name_raw);
1053 const value = self.dupe(value_raw);
1054 const gop = try self.user_input_options.getOrPut(name);
1055 if (!gop.found_existing) {
1056 gop.value_ptr.* = UserInputOption{
1057 .name = name,
1058 .value = .{ .scalar = value },
1059 .used = false,
1060 };
1061 return false;
1062 }
1063
1064 // option already exists
1065 switch (gop.value_ptr.value) {
1066 .scalar => |s| {
1067 // turn it into a list
1068 var list = ArrayList([]const u8).init(self.allocator);
1069 try list.append(s);
1070 try list.append(value);
1071 try self.user_input_options.put(name, .{
1072 .name = name,
1073 .value = .{ .list = list },
1074 .used = false,
1075 });
1076 },
1077 .list => |*list| {
1078 // append to the list
1079 try list.append(value);
1080 try self.user_input_options.put(name, .{
1081 .name = name,
1082 .value = .{ .list = list.* },
1083 .used = false,
1084 });
1085 },
1086 .flag => {
1087 log.warn("Option '-D{s}={s}' conflicts with flag '-D{s}'.", .{ name, value, name });
1088 return true;
1089 },
1090 .map => |*map| {
1091 _ = map;
1092 log.warn("TODO maps as command line arguments is not implemented yet.", .{});
1093 return true;
1094 },
1095 }
1096 return false;
1097}
1098
1099pub fn addUserInputFlag(self: *Build, name_raw: []const u8) !bool {
1100 const name = self.dupe(name_raw);
1101 const gop = try self.user_input_options.getOrPut(name);
1102 if (!gop.found_existing) {
1103 gop.value_ptr.* = .{
1104 .name = name,
1105 .value = .{ .flag = {} },
1106 .used = false,
1107 };
1108 return false;
1109 }
1110
1111 // option already exists
1112 switch (gop.value_ptr.value) {
1113 .scalar => |s| {
1114 log.err("Flag '-D{s}' conflicts with option '-D{s}={s}'.", .{ name, name, s });
1115 return true;
1116 },
1117 .list, .map => {
1118 log.err("Flag '-D{s}' conflicts with multiple options of the same name.", .{name});
1119 return true;
1120 },
1121 .flag => {},
1122 }
1123 return false;
1124}
1125
1126fn typeToEnum(comptime T: type) TypeId {
1127 return switch (@typeInfo(T)) {
1128 .Int => .int,
1129 .Float => .float,
1130 .Bool => .bool,
1131 .Enum => .@"enum",
1132 else => switch (T) {
1133 []const u8 => .string,
1134 []const []const u8 => .list,
1135 else => @compileError("Unsupported type: " ++ @typeName(T)),
1136 },
1137 };
1138}
1139
1140fn markInvalidUserInput(self: *Build) void {
1141 self.invalid_user_input = true;
1142}
1143
1144pub fn validateUserInputDidItFail(self: *Build) bool {
1145 // make sure all args are used
1146 var it = self.user_input_options.iterator();
1147 while (it.next()) |entry| {
1148 if (!entry.value_ptr.used) {
1149 log.err("Invalid option: -D{s}", .{entry.key_ptr.*});
1150 self.markInvalidUserInput();
1151 }
1152 }
1153
1154 return self.invalid_user_input;
1155}
1156
1157pub fn spawnChild(self: *Build, argv: []const []const u8) !void {
1158 return self.spawnChildEnvMap(null, self.env_map, argv);
1159}
1160
1161fn printCmd(cwd: ?[]const u8, argv: []const []const u8) void {
1162 if (cwd) |yes_cwd| std.debug.print("cd {s} && ", .{yes_cwd});
1163 for (argv) |arg| {
1164 std.debug.print("{s} ", .{arg});
1165 }
1166 std.debug.print("\n", .{});
1167}
1168
1169pub fn spawnChildEnvMap(self: *Build, cwd: ?[]const u8, env_map: *const EnvMap, argv: []const []const u8) !void {
1170 if (self.verbose) {
1171 printCmd(cwd, argv);
1172 }
1173
1174 if (!std.process.can_spawn)
1175 return error.ExecNotSupported;
1176
1177 var child = std.ChildProcess.init(argv, self.allocator);
1178 child.cwd = cwd;
1179 child.env_map = env_map;
1180
1181 const term = child.spawnAndWait() catch |err| {
1182 log.err("Unable to spawn {s}: {s}", .{ argv[0], @errorName(err) });
1183 return err;
1184 };
1185
1186 switch (term) {
1187 .Exited => |code| {
1188 if (code != 0) {
1189 log.err("The following command exited with error code {}:", .{code});
1190 printCmd(cwd, argv);
1191 return error.UncleanExit;
1192 }
1193 },
1194 else => {
1195 log.err("The following command terminated unexpectedly:", .{});
1196 printCmd(cwd, argv);
1197
1198 return error.UncleanExit;
1199 },
1200 }
1201}
1202
1203pub fn makePath(self: *Build, path: []const u8) !void {
1204 fs.cwd().makePath(self.pathFromRoot(path)) catch |err| {
1205 log.err("Unable to create path {s}: {s}", .{ path, @errorName(err) });
1206 return err;
1207 };
1208}
1209
1210pub fn installArtifact(self: *Build, artifact: *CompileStep) void {
1211 self.getInstallStep().dependOn(&self.addInstallArtifact(artifact).step);
1212}
1213
1214pub fn addInstallArtifact(self: *Build, artifact: *CompileStep) *InstallArtifactStep {
1215 return InstallArtifactStep.create(self, artifact);
1216}
1217
1218///`dest_rel_path` is relative to prefix path
1219pub fn installFile(self: *Build, src_path: []const u8, dest_rel_path: []const u8) void {
1220 self.getInstallStep().dependOn(&self.addInstallFileWithDir(.{ .path = src_path }, .prefix, dest_rel_path).step);
1221}
1222
1223pub fn installDirectory(self: *Build, options: InstallDirectoryOptions) void {
1224 self.getInstallStep().dependOn(&self.addInstallDirectory(options).step);
1225}
1226
1227///`dest_rel_path` is relative to bin path
1228pub fn installBinFile(self: *Build, src_path: []const u8, dest_rel_path: []const u8) void {
1229 self.getInstallStep().dependOn(&self.addInstallFileWithDir(.{ .path = src_path }, .bin, dest_rel_path).step);
1230}
1231
1232///`dest_rel_path` is relative to lib path
1233pub fn installLibFile(self: *Build, src_path: []const u8, dest_rel_path: []const u8) void {
1234 self.getInstallStep().dependOn(&self.addInstallFileWithDir(.{ .path = src_path }, .lib, dest_rel_path).step);
1235}
1236
1237/// Output format (BIN vs Intel HEX) determined by filename
1238pub fn installRaw(self: *Build, artifact: *CompileStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep {
1239 const raw = self.addInstallRaw(artifact, dest_filename, options);
1240 self.getInstallStep().dependOn(&raw.step);
1241 return raw;
1242}
1243
1244///`dest_rel_path` is relative to install prefix path
1245pub fn addInstallFile(self: *Build, source: FileSource, dest_rel_path: []const u8) *InstallFileStep {
1246 return self.addInstallFileWithDir(source.dupe(self), .prefix, dest_rel_path);
1247}
1248
1249///`dest_rel_path` is relative to bin path
1250pub fn addInstallBinFile(self: *Build, source: FileSource, dest_rel_path: []const u8) *InstallFileStep {
1251 return self.addInstallFileWithDir(source.dupe(self), .bin, dest_rel_path);
1252}
1253
1254///`dest_rel_path` is relative to lib path
1255pub fn addInstallLibFile(self: *Build, source: FileSource, dest_rel_path: []const u8) *InstallFileStep {
1256 return self.addInstallFileWithDir(source.dupe(self), .lib, dest_rel_path);
1257}
1258
1259pub fn addInstallHeaderFile(b: *Build, src_path: []const u8, dest_rel_path: []const u8) *InstallFileStep {
1260 return b.addInstallFileWithDir(.{ .path = src_path }, .header, dest_rel_path);
1261}
1262
1263pub fn addInstallRaw(self: *Build, artifact: *CompileStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep {
1264 return InstallRawStep.create(self, artifact, dest_filename, options);
1265}
1266
1267pub fn addInstallFileWithDir(
1268 self: *Build,
1269 source: FileSource,
1270 install_dir: InstallDir,
1271 dest_rel_path: []const u8,
1272) *InstallFileStep {
1273 if (dest_rel_path.len == 0) {
1274 panic("dest_rel_path must be non-empty", .{});
1275 }
1276 const install_step = self.allocator.create(InstallFileStep) catch @panic("OOM");
1277 install_step.* = InstallFileStep.init(self, source.dupe(self), install_dir, dest_rel_path);
1278 return install_step;
1279}
1280
1281pub fn addInstallDirectory(self: *Build, options: InstallDirectoryOptions) *InstallDirStep {
1282 const install_step = self.allocator.create(InstallDirStep) catch @panic("OOM");
1283 install_step.* = InstallDirStep.init(self, options);
1284 return install_step;
1285}
1286
1287pub fn pushInstalledFile(self: *Build, dir: InstallDir, dest_rel_path: []const u8) void {
1288 const file = InstalledFile{
1289 .dir = dir,
1290 .path = dest_rel_path,
1291 };
1292 self.installed_files.append(file.dupe(self)) catch @panic("OOM");
1293}
1294
1295pub fn updateFile(self: *Build, source_path: []const u8, dest_path: []const u8) !void {
1296 if (self.verbose) {
1297 log.info("cp {s} {s} ", .{ source_path, dest_path });
1298 }
1299 const cwd = fs.cwd();
1300 const prev_status = try fs.Dir.updateFile(cwd, source_path, cwd, dest_path, .{});
1301 if (self.verbose) switch (prev_status) {
1302 .stale => log.info("# installed", .{}),
1303 .fresh => log.info("# up-to-date", .{}),
1304 };
1305}
1306
1307pub fn truncateFile(self: *Build, dest_path: []const u8) !void {
1308 if (self.verbose) {
1309 log.info("truncate {s}", .{dest_path});
1310 }
1311 const cwd = fs.cwd();
1312 var src_file = cwd.createFile(dest_path, .{}) catch |err| switch (err) {
1313 error.FileNotFound => blk: {
1314 if (fs.path.dirname(dest_path)) |dirname| {
1315 try cwd.makePath(dirname);
1316 }
1317 break :blk try cwd.createFile(dest_path, .{});
1318 },
1319 else => |e| return e,
1320 };
1321 src_file.close();
1322}
1323
1324pub fn pathFromRoot(self: *Build, rel_path: []const u8) []u8 {
1325 return fs.path.resolve(self.allocator, &[_][]const u8{ self.build_root, rel_path }) catch @panic("OOM");
1326}
1327
1328pub fn pathJoin(self: *Build, paths: []const []const u8) []u8 {
1329 return fs.path.join(self.allocator, paths) catch @panic("OOM");
1330}
1331
1332pub fn fmt(self: *Build, comptime format: []const u8, args: anytype) []u8 {
1333 return fmt_lib.allocPrint(self.allocator, format, args) catch @panic("OOM");
1334}
1335
1336pub fn findProgram(self: *Build, names: []const []const u8, paths: []const []const u8) ![]const u8 {
1337 // TODO report error for ambiguous situations
1338 const exe_extension = @as(CrossTarget, .{}).exeFileExt();
1339 for (self.search_prefixes.items) |search_prefix| {
1340 for (names) |name| {
1341 if (fs.path.isAbsolute(name)) {
1342 return name;
1343 }
1344 const full_path = self.pathJoin(&.{
1345 search_prefix,
1346 "bin",
1347 self.fmt("{s}{s}", .{ name, exe_extension }),
1348 });
1349 return fs.realpathAlloc(self.allocator, full_path) catch continue;
1350 }
1351 }
1352 if (self.env_map.get("PATH")) |PATH| {
1353 for (names) |name| {
1354 if (fs.path.isAbsolute(name)) {
1355 return name;
1356 }
1357 var it = mem.tokenize(u8, PATH, &[_]u8{fs.path.delimiter});
1358 while (it.next()) |path| {
1359 const full_path = self.pathJoin(&.{
1360 path,
1361 self.fmt("{s}{s}", .{ name, exe_extension }),
1362 });
1363 return fs.realpathAlloc(self.allocator, full_path) catch continue;
1364 }
1365 }
1366 }
1367 for (names) |name| {
1368 if (fs.path.isAbsolute(name)) {
1369 return name;
1370 }
1371 for (paths) |path| {
1372 const full_path = self.pathJoin(&.{
1373 path,
1374 self.fmt("{s}{s}", .{ name, exe_extension }),
1375 });
1376 return fs.realpathAlloc(self.allocator, full_path) catch continue;
1377 }
1378 }
1379 return error.FileNotFound;
1380}
1381
1382pub fn execAllowFail(
1383 self: *Build,
1384 argv: []const []const u8,
1385 out_code: *u8,
1386 stderr_behavior: std.ChildProcess.StdIo,
1387) ExecError![]u8 {
1388 assert(argv.len != 0);
1389
1390 if (!std.process.can_spawn)
1391 return error.ExecNotSupported;
1392
1393 const max_output_size = 400 * 1024;
1394 var child = std.ChildProcess.init(argv, self.allocator);
1395 child.stdin_behavior = .Ignore;
1396 child.stdout_behavior = .Pipe;
1397 child.stderr_behavior = stderr_behavior;
1398 child.env_map = self.env_map;
1399
1400 try child.spawn();
1401
1402 const stdout = child.stdout.?.reader().readAllAlloc(self.allocator, max_output_size) catch {
1403 return error.ReadFailure;
1404 };
1405 errdefer self.allocator.free(stdout);
1406
1407 const term = try child.wait();
1408 switch (term) {
1409 .Exited => |code| {
1410 if (code != 0) {
1411 out_code.* = @truncate(u8, code);
1412 return error.ExitCodeFailure;
1413 }
1414 return stdout;
1415 },
1416 .Signal, .Stopped, .Unknown => |code| {
1417 out_code.* = @truncate(u8, code);
1418 return error.ProcessTerminated;
1419 },
1420 }
1421}
1422
1423pub fn execFromStep(self: *Build, argv: []const []const u8, src_step: ?*Step) ![]u8 {
1424 assert(argv.len != 0);
1425
1426 if (self.verbose) {
1427 printCmd(null, argv);
1428 }
1429
1430 if (!std.process.can_spawn) {
1431 if (src_step) |s| log.err("{s}...", .{s.name});
1432 log.err("Unable to spawn the following command: cannot spawn child process", .{});
1433 printCmd(null, argv);
1434 std.os.abort();
1435 }
1436
1437 var code: u8 = undefined;
1438 return self.execAllowFail(argv, &code, .Inherit) catch |err| switch (err) {
1439 error.ExecNotSupported => {
1440 if (src_step) |s| log.err("{s}...", .{s.name});
1441 log.err("Unable to spawn the following command: cannot spawn child process", .{});
1442 printCmd(null, argv);
1443 std.os.abort();
1444 },
1445 error.FileNotFound => {
1446 if (src_step) |s| log.err("{s}...", .{s.name});
1447 log.err("Unable to spawn the following command: file not found", .{});
1448 printCmd(null, argv);
1449 std.os.exit(@truncate(u8, code));
1450 },
1451 error.ExitCodeFailure => {
1452 if (src_step) |s| log.err("{s}...", .{s.name});
1453 if (self.prominent_compile_errors) {
1454 log.err("The step exited with error code {d}", .{code});
1455 } else {
1456 log.err("The following command exited with error code {d}:", .{code});
1457 printCmd(null, argv);
1458 }
1459
1460 std.os.exit(@truncate(u8, code));
1461 },
1462 error.ProcessTerminated => {
1463 if (src_step) |s| log.err("{s}...", .{s.name});
1464 log.err("The following command terminated unexpectedly:", .{});
1465 printCmd(null, argv);
1466 std.os.exit(@truncate(u8, code));
1467 },
1468 else => |e| return e,
1469 };
1470}
1471
1472pub fn exec(self: *Build, argv: []const []const u8) ![]u8 {
1473 return self.execFromStep(argv, null);
1474}
1475
1476pub fn addSearchPrefix(self: *Build, search_prefix: []const u8) void {
1477 self.search_prefixes.append(self.dupePath(search_prefix)) catch @panic("OOM");
1478}
1479
1480pub fn getInstallPath(self: *Build, dir: InstallDir, dest_rel_path: []const u8) []const u8 {
1481 assert(!fs.path.isAbsolute(dest_rel_path)); // Install paths must be relative to the prefix
1482 const base_dir = switch (dir) {
1483 .prefix => self.install_path,
1484 .bin => self.exe_dir,
1485 .lib => self.lib_dir,
1486 .header => self.h_dir,
1487 .custom => |path| self.pathJoin(&.{ self.install_path, path }),
1488 };
1489 return fs.path.resolve(
1490 self.allocator,
1491 &[_][]const u8{ base_dir, dest_rel_path },
1492 ) catch @panic("OOM");
1493}
1494
1495pub const Dependency = struct {
1496 builder: *Build,
1497
1498 pub fn artifact(d: *Dependency, name: []const u8) *CompileStep {
1499 var found: ?*CompileStep = null;
1500 for (d.builder.install_tls.step.dependencies.items) |dep_step| {
1501 const inst = dep_step.cast(InstallArtifactStep) orelse continue;
1502 if (mem.eql(u8, inst.artifact.name, name)) {
1503 if (found != null) panic("artifact name '{s}' is ambiguous", .{name});
1504 found = inst.artifact;
1505 }
1506 }
1507 return found orelse {
1508 for (d.builder.install_tls.step.dependencies.items) |dep_step| {
1509 const inst = dep_step.cast(InstallArtifactStep) orelse continue;
1510 log.info("available artifact: '{s}'", .{inst.artifact.name});
1511 }
1512 panic("unable to find artifact '{s}'", .{name});
1513 };
1514 }
1515
1516 pub fn module(d: *Dependency, name: []const u8) *Module {
1517 return d.builder.modules.get(name) orelse {
1518 panic("unable to find module '{s}'", .{name});
1519 };
1520 }
1521};
1522
1523pub fn dependency(b: *Build, name: []const u8, args: anytype) *Dependency {
1524 const build_runner = @import("root");
1525 const deps = build_runner.dependencies;
1526
1527 inline for (@typeInfo(deps.imports).Struct.decls) |decl| {
1528 if (mem.startsWith(u8, decl.name, b.dep_prefix) and
1529 mem.endsWith(u8, decl.name, name) and
1530 decl.name.len == b.dep_prefix.len + name.len)
1531 {
1532 const build_zig = @field(deps.imports, decl.name);
1533 const build_root = @field(deps.build_root, decl.name);
1534 return dependencyInner(b, name, build_root, build_zig, args);
1535 }
1536 }
1537
1538 const full_path = b.pathFromRoot("build.zig.zon");
1539 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 });
1540 std.process.exit(1);
1541}
1542
1543fn dependencyInner(
1544 b: *Build,
1545 name: []const u8,
1546 build_root: []const u8,
1547 comptime build_zig: type,
1548 args: anytype,
1549) *Dependency {
1550 const sub_builder = b.createChild(name, build_root, args) catch @panic("unhandled error");
1551 sub_builder.runBuild(build_zig) catch @panic("unhandled error");
1552
1553 if (sub_builder.validateUserInputDidItFail()) {
1554 std.debug.dumpCurrentStackTrace(@returnAddress());
1555 }
1556
1557 const dep = b.allocator.create(Dependency) catch @panic("OOM");
1558 dep.* = .{ .builder = sub_builder };
1559 return dep;
1560}
1561
1562pub fn runBuild(b: *Build, build_zig: anytype) anyerror!void {
1563 switch (@typeInfo(@typeInfo(@TypeOf(build_zig.build)).Fn.return_type.?)) {
1564 .Void => build_zig.build(b),
1565 .ErrorUnion => try build_zig.build(b),
1566 else => @compileError("expected return type of build to be 'void' or '!void'"),
1567 }
1568}
1569
1570test "builder.findProgram compiles" {
1571 if (builtin.os.tag == .wasi) return error.SkipZigTest;
1572
1573 var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
1574 defer arena.deinit();
1575
1576 const host = try NativeTargetInfo.detect(.{});
1577
1578 const builder = try Build.create(
1579 arena.allocator(),
1580 "zig",
1581 "zig-cache",
1582 "zig-cache",
1583 "zig-cache",
1584 host,
1585 );
1586 defer builder.destroy();
1587 _ = builder.findProgram(&[_][]const u8{}, &[_][]const u8{}) catch null;
1588}
1589
1590pub const Module = struct {
1591 builder: *Build,
1592 /// This could either be a generated file, in which case the module
1593 /// contains exactly one file, or it could be a path to the root source
1594 /// file of directory of files which constitute the module.
1595 source_file: FileSource,
1596 dependencies: std.StringArrayHashMap(*Module),
1597};
1598
1599/// A file that is generated by a build step.
1600/// This struct is an interface that is meant to be used with `@fieldParentPtr` to implement the actual path logic.
1601pub const GeneratedFile = struct {
1602 /// The step that generates the file
1603 step: *Step,
1604
1605 /// The path to the generated file. Must be either absolute or relative to the build root.
1606 /// This value must be set in the `fn make()` of the `step` and must not be `null` afterwards.
1607 path: ?[]const u8 = null,
1608
1609 pub fn getPath(self: GeneratedFile) []const u8 {
1610 return self.path orelse std.debug.panic(
1611 "getPath() was called on a GeneratedFile that wasn't build yet. Is there a missing Step dependency on step '{s}'?",
1612 .{self.step.name},
1613 );
1614 }
1615};
1616
1617/// A file source is a reference to an existing or future file.
1618///
1619pub const FileSource = union(enum) {
1620 /// A plain file path, relative to build root or absolute.
1621 path: []const u8,
1622
1623 /// A file that is generated by an interface. Those files usually are
1624 /// not available until built by a build step.
1625 generated: *const GeneratedFile,
1626
1627 /// Returns a new file source that will have a relative path to the build root guaranteed.
1628 /// This should be preferred over setting `.path` directly as it documents that the files are in the project directory.
1629 pub fn relative(path: []const u8) FileSource {
1630 std.debug.assert(!std.fs.path.isAbsolute(path));
1631 return FileSource{ .path = path };
1632 }
1633
1634 /// Returns a string that can be shown to represent the file source.
1635 /// Either returns the path or `"generated"`.
1636 pub fn getDisplayName(self: FileSource) []const u8 {
1637 return switch (self) {
1638 .path => self.path,
1639 .generated => "generated",
1640 };
1641 }
1642
1643 /// Adds dependencies this file source implies to the given step.
1644 pub fn addStepDependencies(self: FileSource, other_step: *Step) void {
1645 switch (self) {
1646 .path => {},
1647 .generated => |gen| other_step.dependOn(gen.step),
1648 }
1649 }
1650
1651 /// Should only be called during make(), returns a path relative to the build root or absolute.
1652 pub fn getPath(self: FileSource, builder: *Build) []const u8 {
1653 const path = switch (self) {
1654 .path => |p| builder.pathFromRoot(p),
1655 .generated => |gen| gen.getPath(),
1656 };
1657 return path;
1658 }
1659
1660 /// Duplicates the file source for a given builder.
1661 pub fn dupe(self: FileSource, b: *Build) FileSource {
1662 return switch (self) {
1663 .path => |p| .{ .path = b.dupePath(p) },
1664 .generated => |gen| .{ .generated = gen },
1665 };
1666 }
1667};
1668
1669/// Allocates a new string for assigning a value to a named macro.
1670/// If the value is omitted, it is set to 1.
1671/// `name` and `value` need not live longer than the function call.
1672pub fn constructCMacro(allocator: Allocator, name: []const u8, value: ?[]const u8) []const u8 {
1673 var macro = allocator.alloc(
1674 u8,
1675 name.len + if (value) |value_slice| value_slice.len + 1 else 0,
1676 ) catch |err| if (err == error.OutOfMemory) @panic("Out of memory") else unreachable;
1677 mem.copy(u8, macro, name);
1678 if (value) |value_slice| {
1679 macro[name.len] = '=';
1680 mem.copy(u8, macro[name.len + 1 ..], value_slice);
1681 }
1682 return macro;
1683}
1684
1685pub const VcpkgRoot = union(VcpkgRootStatus) {
1686 unattempted: void,
1687 not_found: void,
1688 found: []const u8,
1689};
1690
1691pub const VcpkgRootStatus = enum {
1692 unattempted,
1693 not_found,
1694 found,
1695};
1696
1697pub const InstallDir = union(enum) {
1698 prefix: void,
1699 lib: void,
1700 bin: void,
1701 header: void,
1702 /// A path relative to the prefix
1703 custom: []const u8,
1704
1705 /// Duplicates the install directory including the path if set to custom.
1706 pub fn dupe(self: InstallDir, builder: *Build) InstallDir {
1707 if (self == .custom) {
1708 // Written with this temporary to avoid RLS problems
1709 const duped_path = builder.dupe(self.custom);
1710 return .{ .custom = duped_path };
1711 } else {
1712 return self;
1713 }
1714 }
1715};
1716
1717pub const InstalledFile = struct {
1718 dir: InstallDir,
1719 path: []const u8,
1720
1721 /// Duplicates the installed file path and directory.
1722 pub fn dupe(self: InstalledFile, builder: *Build) InstalledFile {
1723 return .{
1724 .dir = self.dir.dupe(builder),
1725 .path = builder.dupe(self.path),
1726 };
1727 }
1728};
1729
1730pub fn serializeCpu(allocator: Allocator, cpu: std.Target.Cpu) ![]const u8 {
1731 // TODO this logic can disappear if cpu model + features becomes part of the target triple
1732 const all_features = cpu.arch.allFeaturesList();
1733 var populated_cpu_features = cpu.model.features;
1734 populated_cpu_features.populateDependencies(all_features);
1735
1736 if (populated_cpu_features.eql(cpu.features)) {
1737 // The CPU name alone is sufficient.
1738 return cpu.model.name;
1739 } else {
1740 var mcpu_buffer = ArrayList(u8).init(allocator);
1741 try mcpu_buffer.appendSlice(cpu.model.name);
1742
1743 for (all_features) |feature, i_usize| {
1744 const i = @intCast(std.Target.Cpu.Feature.Set.Index, i_usize);
1745 const in_cpu_set = populated_cpu_features.isEnabled(i);
1746 const in_actual_set = cpu.features.isEnabled(i);
1747 if (in_cpu_set and !in_actual_set) {
1748 try mcpu_buffer.writer().print("-{s}", .{feature.name});
1749 } else if (!in_cpu_set and in_actual_set) {
1750 try mcpu_buffer.writer().print("+{s}", .{feature.name});
1751 }
1752 }
1753
1754 return try mcpu_buffer.toOwnedSlice();
1755 }
1756}
1757
1758test {
1759 _ = CheckFileStep;
1760 _ = CheckObjectStep;
1761 _ = EmulatableRunStep;
1762 _ = FmtStep;
1763 _ = InstallArtifactStep;
1764 _ = InstallDirStep;
1765 _ = InstallFileStep;
1766 _ = InstallRawStep;
1767 _ = CompileStep;
1768 _ = LogStep;
1769 _ = OptionsStep;
1770 _ = RemoveDirStep;
1771 _ = RunStep;
1772 _ = TranslateCStep;
1773 _ = WriteFileStep;
1774}
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+2043
......@@ -0,0 +1,2043 @@
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 Module = std.Build.Module;
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,
102modules: std.StringArrayHashMap(*Module),
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 .modules = std.StringArrayHashMap(*Module).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 const InstallConfigHeaderOptions = struct {
446 install_dir: InstallDir = .header,
447 dest_rel_path: ?[]const u8 = null,
448};
449
450pub fn installConfigHeader(
451 cs: *CompileStep,
452 config_header: *ConfigHeaderStep,
453 options: InstallConfigHeaderOptions,
454) void {
455 const dest_rel_path = options.dest_rel_path orelse config_header.include_path;
456 const install_file = cs.builder.addInstallFileWithDir(
457 .{ .generated = &config_header.output_file },
458 options.install_dir,
459 dest_rel_path,
460 );
461 cs.builder.getInstallStep().dependOn(&install_file.step);
462 cs.installed_headers.append(&install_file.step) catch @panic("OOM");
463}
464
465pub fn installHeadersDirectory(
466 a: *CompileStep,
467 src_dir_path: []const u8,
468 dest_rel_path: []const u8,
469) void {
470 return installHeadersDirectoryOptions(a, .{
471 .source_dir = src_dir_path,
472 .install_dir = .header,
473 .install_subdir = dest_rel_path,
474 });
475}
476
477pub fn installHeadersDirectoryOptions(
478 a: *CompileStep,
479 options: std.Build.InstallDirStep.Options,
480) void {
481 const install_dir = a.builder.addInstallDirectory(options);
482 a.builder.getInstallStep().dependOn(&install_dir.step);
483 a.installed_headers.append(&install_dir.step) catch @panic("OOM");
484}
485
486pub fn installLibraryHeaders(a: *CompileStep, l: *CompileStep) void {
487 assert(l.kind == .lib);
488 const install_step = a.builder.getInstallStep();
489 // Copy each element from installed_headers, modifying the builder
490 // to be the new parent's builder.
491 for (l.installed_headers.items) |step| {
492 const step_copy = switch (step.id) {
493 inline .install_file, .install_dir => |id| blk: {
494 const T = id.Type();
495 const ptr = a.builder.allocator.create(T) catch @panic("OOM");
496 ptr.* = step.cast(T).?.*;
497 ptr.override_source_builder = ptr.builder;
498 ptr.builder = a.builder;
499 break :blk &ptr.step;
500 },
501 else => unreachable,
502 };
503 a.installed_headers.append(step_copy) catch @panic("OOM");
504 install_step.dependOn(step_copy);
505 }
506 a.installed_headers.appendSlice(l.installed_headers.items) catch @panic("OOM");
507}
508
509/// Creates a `RunStep` with an executable built with `addExecutable`.
510/// Add command line arguments with `addArg`.
511pub fn run(exe: *CompileStep) *RunStep {
512 assert(exe.kind == .exe or exe.kind == .test_exe);
513
514 // It doesn't have to be native. We catch that if you actually try to run it.
515 // Consider that this is declarative; the run step may not be run unless a user
516 // option is supplied.
517 const run_step = RunStep.create(exe.builder, exe.builder.fmt("run {s}", .{exe.step.name}));
518 run_step.addArtifactArg(exe);
519
520 if (exe.kind == .test_exe) {
521 run_step.addArg(exe.builder.zig_exe);
522 }
523
524 if (exe.vcpkg_bin_path) |path| {
525 run_step.addPathDir(path);
526 }
527
528 return run_step;
529}
530
531/// Creates an `EmulatableRunStep` with an executable built with `addExecutable`.
532/// Allows running foreign binaries through emulation platforms such as Qemu or Rosetta.
533/// When a binary cannot be ran through emulation or the option is disabled, a warning
534/// will be printed and the binary will *NOT* be ran.
535pub fn runEmulatable(exe: *CompileStep) *EmulatableRunStep {
536 assert(exe.kind == .exe or exe.kind == .test_exe);
537
538 const run_step = EmulatableRunStep.create(exe.builder, exe.builder.fmt("run {s}", .{exe.step.name}), exe);
539 if (exe.vcpkg_bin_path) |path| {
540 RunStep.addPathDirInternal(&run_step.step, exe.builder, path);
541 }
542 return run_step;
543}
544
545pub fn checkObject(self: *CompileStep, obj_format: std.Target.ObjectFormat) *CheckObjectStep {
546 return CheckObjectStep.create(self.builder, self.getOutputSource(), obj_format);
547}
548
549pub fn setLinkerScriptPath(self: *CompileStep, source: FileSource) void {
550 self.linker_script = source.dupe(self.builder);
551 source.addStepDependencies(&self.step);
552}
553
554pub fn linkFramework(self: *CompileStep, framework_name: []const u8) void {
555 self.frameworks.put(self.builder.dupe(framework_name), .{}) catch @panic("OOM");
556}
557
558pub fn linkFrameworkNeeded(self: *CompileStep, framework_name: []const u8) void {
559 self.frameworks.put(self.builder.dupe(framework_name), .{
560 .needed = true,
561 }) catch @panic("OOM");
562}
563
564pub fn linkFrameworkWeak(self: *CompileStep, framework_name: []const u8) void {
565 self.frameworks.put(self.builder.dupe(framework_name), .{
566 .weak = true,
567 }) catch @panic("OOM");
568}
569
570/// Returns whether the library, executable, or object depends on a particular system library.
571pub fn dependsOnSystemLibrary(self: CompileStep, name: []const u8) bool {
572 if (isLibCLibrary(name)) {
573 return self.is_linking_libc;
574 }
575 if (isLibCppLibrary(name)) {
576 return self.is_linking_libcpp;
577 }
578 for (self.link_objects.items) |link_object| {
579 switch (link_object) {
580 .system_lib => |lib| if (mem.eql(u8, lib.name, name)) return true,
581 else => continue,
582 }
583 }
584 return false;
585}
586
587pub fn linkLibrary(self: *CompileStep, lib: *CompileStep) void {
588 assert(lib.kind == .lib);
589 self.linkLibraryOrObject(lib);
590}
591
592pub fn isDynamicLibrary(self: *CompileStep) bool {
593 return self.kind == .lib and self.linkage == Linkage.dynamic;
594}
595
596pub fn isStaticLibrary(self: *CompileStep) bool {
597 return self.kind == .lib and self.linkage != Linkage.dynamic;
598}
599
600pub fn producesPdbFile(self: *CompileStep) bool {
601 if (!self.target.isWindows() and !self.target.isUefi()) return false;
602 if (self.target.getObjectFormat() == .c) return false;
603 if (self.strip == true) return false;
604 return self.isDynamicLibrary() or self.kind == .exe or self.kind == .test_exe;
605}
606
607pub fn linkLibC(self: *CompileStep) void {
608 self.is_linking_libc = true;
609}
610
611pub fn linkLibCpp(self: *CompileStep) void {
612 self.is_linking_libcpp = true;
613}
614
615/// If the value is omitted, it is set to 1.
616/// `name` and `value` need not live longer than the function call.
617pub fn defineCMacro(self: *CompileStep, name: []const u8, value: ?[]const u8) void {
618 const macro = std.Build.constructCMacro(self.builder.allocator, name, value);
619 self.c_macros.append(macro) catch @panic("OOM");
620}
621
622/// name_and_value looks like [name]=[value]. If the value is omitted, it is set to 1.
623pub fn defineCMacroRaw(self: *CompileStep, name_and_value: []const u8) void {
624 self.c_macros.append(self.builder.dupe(name_and_value)) catch @panic("OOM");
625}
626
627/// This one has no integration with anything, it just puts -lname on the command line.
628/// Prefer to use `linkSystemLibrary` instead.
629pub fn linkSystemLibraryName(self: *CompileStep, name: []const u8) void {
630 self.link_objects.append(.{
631 .system_lib = .{
632 .name = self.builder.dupe(name),
633 .needed = false,
634 .weak = false,
635 .use_pkg_config = .no,
636 },
637 }) catch @panic("OOM");
638}
639
640/// This one has no integration with anything, it just puts -needed-lname on the command line.
641/// Prefer to use `linkSystemLibraryNeeded` instead.
642pub fn linkSystemLibraryNeededName(self: *CompileStep, name: []const u8) void {
643 self.link_objects.append(.{
644 .system_lib = .{
645 .name = self.builder.dupe(name),
646 .needed = true,
647 .weak = false,
648 .use_pkg_config = .no,
649 },
650 }) catch @panic("OOM");
651}
652
653/// Darwin-only. This one has no integration with anything, it just puts -weak-lname on the
654/// command line. Prefer to use `linkSystemLibraryWeak` instead.
655pub fn linkSystemLibraryWeakName(self: *CompileStep, name: []const u8) void {
656 self.link_objects.append(.{
657 .system_lib = .{
658 .name = self.builder.dupe(name),
659 .needed = false,
660 .weak = true,
661 .use_pkg_config = .no,
662 },
663 }) catch @panic("OOM");
664}
665
666/// This links against a system library, exclusively using pkg-config to find the library.
667/// Prefer to use `linkSystemLibrary` instead.
668pub fn linkSystemLibraryPkgConfigOnly(self: *CompileStep, lib_name: []const u8) void {
669 self.link_objects.append(.{
670 .system_lib = .{
671 .name = self.builder.dupe(lib_name),
672 .needed = false,
673 .weak = false,
674 .use_pkg_config = .force,
675 },
676 }) catch @panic("OOM");
677}
678
679/// This links against a system library, exclusively using pkg-config to find the library.
680/// Prefer to use `linkSystemLibraryNeeded` instead.
681pub fn linkSystemLibraryNeededPkgConfigOnly(self: *CompileStep, lib_name: []const u8) void {
682 self.link_objects.append(.{
683 .system_lib = .{
684 .name = self.builder.dupe(lib_name),
685 .needed = true,
686 .weak = false,
687 .use_pkg_config = .force,
688 },
689 }) catch @panic("OOM");
690}
691
692/// Run pkg-config for the given library name and parse the output, returning the arguments
693/// that should be passed to zig to link the given library.
694pub fn runPkgConfig(self: *CompileStep, lib_name: []const u8) ![]const []const u8 {
695 const pkg_name = match: {
696 // First we have to map the library name to pkg config name. Unfortunately,
697 // there are several examples where this is not straightforward:
698 // -lSDL2 -> pkg-config sdl2
699 // -lgdk-3 -> pkg-config gdk-3.0
700 // -latk-1.0 -> pkg-config atk
701 const pkgs = try getPkgConfigList(self.builder);
702
703 // Exact match means instant winner.
704 for (pkgs) |pkg| {
705 if (mem.eql(u8, pkg.name, lib_name)) {
706 break :match pkg.name;
707 }
708 }
709
710 // Next we'll try ignoring case.
711 for (pkgs) |pkg| {
712 if (std.ascii.eqlIgnoreCase(pkg.name, lib_name)) {
713 break :match pkg.name;
714 }
715 }
716
717 // Now try appending ".0".
718 for (pkgs) |pkg| {
719 if (std.ascii.indexOfIgnoreCase(pkg.name, lib_name)) |pos| {
720 if (pos != 0) continue;
721 if (mem.eql(u8, pkg.name[lib_name.len..], ".0")) {
722 break :match pkg.name;
723 }
724 }
725 }
726
727 // Trimming "-1.0".
728 if (mem.endsWith(u8, lib_name, "-1.0")) {
729 const trimmed_lib_name = lib_name[0 .. lib_name.len - "-1.0".len];
730 for (pkgs) |pkg| {
731 if (std.ascii.eqlIgnoreCase(pkg.name, trimmed_lib_name)) {
732 break :match pkg.name;
733 }
734 }
735 }
736
737 return error.PackageNotFound;
738 };
739
740 var code: u8 = undefined;
741 const stdout = if (self.builder.execAllowFail(&[_][]const u8{
742 "pkg-config",
743 pkg_name,
744 "--cflags",
745 "--libs",
746 }, &code, .Ignore)) |stdout| stdout else |err| switch (err) {
747 error.ProcessTerminated => return error.PkgConfigCrashed,
748 error.ExecNotSupported => return error.PkgConfigFailed,
749 error.ExitCodeFailure => return error.PkgConfigFailed,
750 error.FileNotFound => return error.PkgConfigNotInstalled,
751 error.ChildExecFailed => return error.PkgConfigFailed,
752 else => return err,
753 };
754
755 var zig_args = ArrayList([]const u8).init(self.builder.allocator);
756 defer zig_args.deinit();
757
758 var it = mem.tokenize(u8, stdout, " \r\n\t");
759 while (it.next()) |tok| {
760 if (mem.eql(u8, tok, "-I")) {
761 const dir = it.next() orelse return error.PkgConfigInvalidOutput;
762 try zig_args.appendSlice(&[_][]const u8{ "-I", dir });
763 } else if (mem.startsWith(u8, tok, "-I")) {
764 try zig_args.append(tok);
765 } else if (mem.eql(u8, tok, "-L")) {
766 const dir = it.next() orelse return error.PkgConfigInvalidOutput;
767 try zig_args.appendSlice(&[_][]const u8{ "-L", dir });
768 } else if (mem.startsWith(u8, tok, "-L")) {
769 try zig_args.append(tok);
770 } else if (mem.eql(u8, tok, "-l")) {
771 const lib = it.next() orelse return error.PkgConfigInvalidOutput;
772 try zig_args.appendSlice(&[_][]const u8{ "-l", lib });
773 } else if (mem.startsWith(u8, tok, "-l")) {
774 try zig_args.append(tok);
775 } else if (mem.eql(u8, tok, "-D")) {
776 const macro = it.next() orelse return error.PkgConfigInvalidOutput;
777 try zig_args.appendSlice(&[_][]const u8{ "-D", macro });
778 } else if (mem.startsWith(u8, tok, "-D")) {
779 try zig_args.append(tok);
780 } else if (self.builder.verbose) {
781 log.warn("Ignoring pkg-config flag '{s}'", .{tok});
782 }
783 }
784
785 return zig_args.toOwnedSlice();
786}
787
788pub fn linkSystemLibrary(self: *CompileStep, name: []const u8) void {
789 self.linkSystemLibraryInner(name, .{});
790}
791
792pub fn linkSystemLibraryNeeded(self: *CompileStep, name: []const u8) void {
793 self.linkSystemLibraryInner(name, .{ .needed = true });
794}
795
796pub fn linkSystemLibraryWeak(self: *CompileStep, name: []const u8) void {
797 self.linkSystemLibraryInner(name, .{ .weak = true });
798}
799
800fn linkSystemLibraryInner(self: *CompileStep, name: []const u8, opts: struct {
801 needed: bool = false,
802 weak: bool = false,
803}) void {
804 if (isLibCLibrary(name)) {
805 self.linkLibC();
806 return;
807 }
808 if (isLibCppLibrary(name)) {
809 self.linkLibCpp();
810 return;
811 }
812
813 self.link_objects.append(.{
814 .system_lib = .{
815 .name = self.builder.dupe(name),
816 .needed = opts.needed,
817 .weak = opts.weak,
818 .use_pkg_config = .yes,
819 },
820 }) catch @panic("OOM");
821}
822
823pub fn setNamePrefix(self: *CompileStep, text: []const u8) void {
824 assert(self.kind == .@"test" or self.kind == .test_exe);
825 self.name_prefix = self.builder.dupe(text);
826}
827
828pub fn setFilter(self: *CompileStep, text: ?[]const u8) void {
829 assert(self.kind == .@"test" or self.kind == .test_exe);
830 self.filter = if (text) |t| self.builder.dupe(t) else null;
831}
832
833pub fn setTestRunner(self: *CompileStep, path: ?[]const u8) void {
834 assert(self.kind == .@"test" or self.kind == .test_exe);
835 self.test_runner = if (path) |p| self.builder.dupePath(p) else null;
836}
837
838/// Handy when you have many C/C++ source files and want them all to have the same flags.
839pub fn addCSourceFiles(self: *CompileStep, files: []const []const u8, flags: []const []const u8) void {
840 const c_source_files = self.builder.allocator.create(CSourceFiles) catch @panic("OOM");
841
842 const files_copy = self.builder.dupeStrings(files);
843 const flags_copy = self.builder.dupeStrings(flags);
844
845 c_source_files.* = .{
846 .files = files_copy,
847 .flags = flags_copy,
848 };
849 self.link_objects.append(.{ .c_source_files = c_source_files }) catch @panic("OOM");
850}
851
852pub fn addCSourceFile(self: *CompileStep, file: []const u8, flags: []const []const u8) void {
853 self.addCSourceFileSource(.{
854 .args = flags,
855 .source = .{ .path = file },
856 });
857}
858
859pub fn addCSourceFileSource(self: *CompileStep, source: CSourceFile) void {
860 const c_source_file = self.builder.allocator.create(CSourceFile) catch @panic("OOM");
861 c_source_file.* = source.dupe(self.builder);
862 self.link_objects.append(.{ .c_source_file = c_source_file }) catch @panic("OOM");
863 source.source.addStepDependencies(&self.step);
864}
865
866pub fn setVerboseLink(self: *CompileStep, value: bool) void {
867 self.verbose_link = value;
868}
869
870pub fn setVerboseCC(self: *CompileStep, value: bool) void {
871 self.verbose_cc = value;
872}
873
874pub fn overrideZigLibDir(self: *CompileStep, dir_path: []const u8) void {
875 self.override_lib_dir = self.builder.dupePath(dir_path);
876}
877
878pub fn setMainPkgPath(self: *CompileStep, dir_path: []const u8) void {
879 self.main_pkg_path = self.builder.dupePath(dir_path);
880}
881
882pub fn setLibCFile(self: *CompileStep, libc_file: ?FileSource) void {
883 self.libc_file = if (libc_file) |f| f.dupe(self.builder) else null;
884}
885
886/// Returns the generated executable, library or object file.
887/// To run an executable built with zig build, use `run`, or create an install step and invoke it.
888pub fn getOutputSource(self: *CompileStep) FileSource {
889 return FileSource{ .generated = &self.output_path_source };
890}
891
892/// Returns the generated import library. This function can only be called for libraries.
893pub fn getOutputLibSource(self: *CompileStep) FileSource {
894 assert(self.kind == .lib);
895 return FileSource{ .generated = &self.output_lib_path_source };
896}
897
898/// Returns the generated header file.
899/// This function can only be called for libraries or object files which have `emit_h` set.
900pub fn getOutputHSource(self: *CompileStep) FileSource {
901 assert(self.kind != .exe and self.kind != .test_exe and self.kind != .@"test");
902 assert(self.emit_h);
903 return FileSource{ .generated = &self.output_h_path_source };
904}
905
906/// Returns the generated PDB file. This function can only be called for Windows and UEFI.
907pub fn getOutputPdbSource(self: *CompileStep) FileSource {
908 // TODO: Is this right? Isn't PDB for *any* PE/COFF file?
909 assert(self.target.isWindows() or self.target.isUefi());
910 return FileSource{ .generated = &self.output_pdb_path_source };
911}
912
913pub fn addAssemblyFile(self: *CompileStep, path: []const u8) void {
914 self.link_objects.append(.{
915 .assembly_file = .{ .path = self.builder.dupe(path) },
916 }) catch @panic("OOM");
917}
918
919pub fn addAssemblyFileSource(self: *CompileStep, source: FileSource) void {
920 const source_duped = source.dupe(self.builder);
921 self.link_objects.append(.{ .assembly_file = source_duped }) catch @panic("OOM");
922 source_duped.addStepDependencies(&self.step);
923}
924
925pub fn addObjectFile(self: *CompileStep, source_file: []const u8) void {
926 self.addObjectFileSource(.{ .path = source_file });
927}
928
929pub fn addObjectFileSource(self: *CompileStep, source: FileSource) void {
930 self.link_objects.append(.{ .static_path = source.dupe(self.builder) }) catch @panic("OOM");
931 source.addStepDependencies(&self.step);
932}
933
934pub fn addObject(self: *CompileStep, obj: *CompileStep) void {
935 assert(obj.kind == .obj);
936 self.linkLibraryOrObject(obj);
937}
938
939pub const addSystemIncludeDir = @compileError("deprecated; use addSystemIncludePath");
940pub const addIncludeDir = @compileError("deprecated; use addIncludePath");
941pub const addLibPath = @compileError("deprecated, use addLibraryPath");
942pub const addFrameworkDir = @compileError("deprecated, use addFrameworkPath");
943
944pub fn addSystemIncludePath(self: *CompileStep, path: []const u8) void {
945 self.include_dirs.append(IncludeDir{ .raw_path_system = self.builder.dupe(path) }) catch @panic("OOM");
946}
947
948pub fn addIncludePath(self: *CompileStep, path: []const u8) void {
949 self.include_dirs.append(IncludeDir{ .raw_path = self.builder.dupe(path) }) catch @panic("OOM");
950}
951
952pub fn addConfigHeader(self: *CompileStep, config_header: *ConfigHeaderStep) void {
953 self.step.dependOn(&config_header.step);
954 self.include_dirs.append(.{ .config_header_step = config_header }) catch @panic("OOM");
955}
956
957pub fn addLibraryPath(self: *CompileStep, path: []const u8) void {
958 self.lib_paths.append(self.builder.dupe(path)) catch @panic("OOM");
959}
960
961pub fn addRPath(self: *CompileStep, path: []const u8) void {
962 self.rpaths.append(self.builder.dupe(path)) catch @panic("OOM");
963}
964
965pub fn addFrameworkPath(self: *CompileStep, dir_path: []const u8) void {
966 self.framework_dirs.append(self.builder.dupe(dir_path)) catch @panic("OOM");
967}
968
969/// Adds a module to be used with `@import` and exposing it in the current
970/// package's module table using `name`.
971pub fn addModule(cs: *CompileStep, name: []const u8, module: *Module) void {
972 cs.modules.put(cs.builder.dupe(name), module) catch @panic("OOM");
973 cs.addRecursiveBuildDeps(module);
974}
975
976/// Adds a module to be used with `@import` without exposing it in the current
977/// package's module table.
978pub fn addAnonymousModule(cs: *CompileStep, name: []const u8, options: std.Build.CreateModuleOptions) void {
979 const module = cs.builder.createModule(options);
980 return addModule(cs, name, module);
981}
982
983pub fn addOptions(cs: *CompileStep, module_name: []const u8, options: *OptionsStep) void {
984 addModule(cs, module_name, options.createModule());
985}
986
987fn addRecursiveBuildDeps(cs: *CompileStep, module: *Module) void {
988 module.source_file.addStepDependencies(&cs.step);
989 for (module.dependencies.values()) |dep| {
990 cs.addRecursiveBuildDeps(dep);
991 }
992}
993
994/// If Vcpkg was found on the system, it will be added to include and lib
995/// paths for the specified target.
996pub fn addVcpkgPaths(self: *CompileStep, linkage: CompileStep.Linkage) !void {
997 // Ideally in the Unattempted case we would call the function recursively
998 // after findVcpkgRoot and have only one switch statement, but the compiler
999 // cannot resolve the error set.
1000 switch (self.builder.vcpkg_root) {
1001 .unattempted => {
1002 self.builder.vcpkg_root = if (try findVcpkgRoot(self.builder.allocator)) |root|
1003 VcpkgRoot{ .found = root }
1004 else
1005 .not_found;
1006 },
1007 .not_found => return error.VcpkgNotFound,
1008 .found => {},
1009 }
1010
1011 switch (self.builder.vcpkg_root) {
1012 .unattempted => unreachable,
1013 .not_found => return error.VcpkgNotFound,
1014 .found => |root| {
1015 const allocator = self.builder.allocator;
1016 const triplet = try self.target.vcpkgTriplet(allocator, if (linkage == .static) .Static else .Dynamic);
1017 defer self.builder.allocator.free(triplet);
1018
1019 const include_path = self.builder.pathJoin(&.{ root, "installed", triplet, "include" });
1020 errdefer allocator.free(include_path);
1021 try self.include_dirs.append(IncludeDir{ .raw_path = include_path });
1022
1023 const lib_path = self.builder.pathJoin(&.{ root, "installed", triplet, "lib" });
1024 try self.lib_paths.append(lib_path);
1025
1026 self.vcpkg_bin_path = self.builder.pathJoin(&.{ root, "installed", triplet, "bin" });
1027 },
1028 }
1029}
1030
1031pub fn setExecCmd(self: *CompileStep, args: []const ?[]const u8) void {
1032 assert(self.kind == .@"test");
1033 const duped_args = self.builder.allocator.alloc(?[]u8, args.len) catch @panic("OOM");
1034 for (args) |arg, i| {
1035 duped_args[i] = if (arg) |a| self.builder.dupe(a) else null;
1036 }
1037 self.exec_cmd_args = duped_args;
1038}
1039
1040fn linkLibraryOrObject(self: *CompileStep, other: *CompileStep) void {
1041 self.step.dependOn(&other.step);
1042 self.link_objects.append(.{ .other_step = other }) catch @panic("OOM");
1043 self.include_dirs.append(.{ .other_step = other }) catch @panic("OOM");
1044}
1045
1046fn appendModuleArgs(
1047 cs: *CompileStep,
1048 zig_args: *ArrayList([]const u8),
1049 name: []const u8,
1050 module: *Module,
1051) error{OutOfMemory}!void {
1052 try zig_args.append("--pkg-begin");
1053 try zig_args.append(name);
1054 try zig_args.append(module.builder.pathFromRoot(module.source_file.getPath(module.builder)));
1055
1056 {
1057 const keys = module.dependencies.keys();
1058 for (module.dependencies.values()) |sub_module, i| {
1059 const sub_name = keys[i];
1060 try cs.appendModuleArgs(zig_args, sub_name, sub_module);
1061 }
1062 }
1063
1064 try zig_args.append("--pkg-end");
1065}
1066
1067fn make(step: *Step) !void {
1068 const self = @fieldParentPtr(CompileStep, "step", step);
1069 const builder = self.builder;
1070
1071 if (self.root_src == null and self.link_objects.items.len == 0) {
1072 log.err("{s}: linker needs 1 or more objects to link", .{self.step.name});
1073 return error.NeedAnObject;
1074 }
1075
1076 var zig_args = ArrayList([]const u8).init(builder.allocator);
1077 defer zig_args.deinit();
1078
1079 try zig_args.append(builder.zig_exe);
1080
1081 const cmd = switch (self.kind) {
1082 .lib => "build-lib",
1083 .exe => "build-exe",
1084 .obj => "build-obj",
1085 .@"test" => "test",
1086 .test_exe => "test",
1087 };
1088 try zig_args.append(cmd);
1089
1090 if (builder.color != .auto) {
1091 try zig_args.append("--color");
1092 try zig_args.append(@tagName(builder.color));
1093 }
1094
1095 if (builder.reference_trace) |some| {
1096 try zig_args.append(try std.fmt.allocPrint(builder.allocator, "-freference-trace={d}", .{some}));
1097 }
1098
1099 try addFlag(&zig_args, "LLVM", self.use_llvm);
1100 try addFlag(&zig_args, "LLD", self.use_lld);
1101
1102 if (self.target.ofmt) |ofmt| {
1103 try zig_args.append(try std.fmt.allocPrint(builder.allocator, "-ofmt={s}", .{@tagName(ofmt)}));
1104 }
1105
1106 if (self.entry_symbol_name) |entry| {
1107 try zig_args.append("--entry");
1108 try zig_args.append(entry);
1109 }
1110
1111 if (self.stack_size) |stack_size| {
1112 try zig_args.append("--stack");
1113 try zig_args.append(try std.fmt.allocPrint(builder.allocator, "{}", .{stack_size}));
1114 }
1115
1116 if (self.root_src) |root_src| try zig_args.append(root_src.getPath(builder));
1117
1118 // We will add link objects from transitive dependencies, but we want to keep
1119 // all link objects in the same order provided.
1120 // This array is used to keep self.link_objects immutable.
1121 var transitive_deps: TransitiveDeps = .{
1122 .link_objects = ArrayList(LinkObject).init(builder.allocator),
1123 .seen_system_libs = StringHashMap(void).init(builder.allocator),
1124 .seen_steps = std.AutoHashMap(*const Step, void).init(builder.allocator),
1125 .is_linking_libcpp = self.is_linking_libcpp,
1126 .is_linking_libc = self.is_linking_libc,
1127 .frameworks = &self.frameworks,
1128 };
1129
1130 try transitive_deps.seen_steps.put(&self.step, {});
1131 try transitive_deps.add(self.link_objects.items);
1132
1133 var prev_has_extra_flags = false;
1134
1135 for (transitive_deps.link_objects.items) |link_object| {
1136 switch (link_object) {
1137 .static_path => |static_path| try zig_args.append(static_path.getPath(builder)),
1138
1139 .other_step => |other| switch (other.kind) {
1140 .exe => @panic("Cannot link with an executable build artifact"),
1141 .test_exe => @panic("Cannot link with an executable build artifact"),
1142 .@"test" => @panic("Cannot link with a test"),
1143 .obj => {
1144 try zig_args.append(other.getOutputSource().getPath(builder));
1145 },
1146 .lib => l: {
1147 if (self.isStaticLibrary() and other.isStaticLibrary()) {
1148 // Avoid putting a static library inside a static library.
1149 break :l;
1150 }
1151
1152 const full_path_lib = other.getOutputLibSource().getPath(builder);
1153 try zig_args.append(full_path_lib);
1154
1155 if (other.linkage == Linkage.dynamic and !self.target.isWindows()) {
1156 if (fs.path.dirname(full_path_lib)) |dirname| {
1157 try zig_args.append("-rpath");
1158 try zig_args.append(dirname);
1159 }
1160 }
1161 },
1162 },
1163
1164 .system_lib => |system_lib| {
1165 const prefix: []const u8 = prefix: {
1166 if (system_lib.needed) break :prefix "-needed-l";
1167 if (system_lib.weak) {
1168 if (self.target.isDarwin()) break :prefix "-weak-l";
1169 log.warn("Weak library import used for a non-darwin target, this will be converted to normally library import `-lname`", .{});
1170 }
1171 break :prefix "-l";
1172 };
1173 switch (system_lib.use_pkg_config) {
1174 .no => try zig_args.append(builder.fmt("{s}{s}", .{ prefix, system_lib.name })),
1175 .yes, .force => {
1176 if (self.runPkgConfig(system_lib.name)) |args| {
1177 try zig_args.appendSlice(args);
1178 } else |err| switch (err) {
1179 error.PkgConfigInvalidOutput,
1180 error.PkgConfigCrashed,
1181 error.PkgConfigFailed,
1182 error.PkgConfigNotInstalled,
1183 error.PackageNotFound,
1184 => switch (system_lib.use_pkg_config) {
1185 .yes => {
1186 // pkg-config failed, so fall back to linking the library
1187 // by name directly.
1188 try zig_args.append(builder.fmt("{s}{s}", .{
1189 prefix,
1190 system_lib.name,
1191 }));
1192 },
1193 .force => {
1194 panic("pkg-config failed for library {s}", .{system_lib.name});
1195 },
1196 .no => unreachable,
1197 },
1198
1199 else => |e| return e,
1200 }
1201 },
1202 }
1203 },
1204
1205 .assembly_file => |asm_file| {
1206 if (prev_has_extra_flags) {
1207 try zig_args.append("-extra-cflags");
1208 try zig_args.append("--");
1209 prev_has_extra_flags = false;
1210 }
1211 try zig_args.append(asm_file.getPath(builder));
1212 },
1213
1214 .c_source_file => |c_source_file| {
1215 if (c_source_file.args.len == 0) {
1216 if (prev_has_extra_flags) {
1217 try zig_args.append("-cflags");
1218 try zig_args.append("--");
1219 prev_has_extra_flags = false;
1220 }
1221 } else {
1222 try zig_args.append("-cflags");
1223 for (c_source_file.args) |arg| {
1224 try zig_args.append(arg);
1225 }
1226 try zig_args.append("--");
1227 }
1228 try zig_args.append(c_source_file.source.getPath(builder));
1229 },
1230
1231 .c_source_files => |c_source_files| {
1232 if (c_source_files.flags.len == 0) {
1233 if (prev_has_extra_flags) {
1234 try zig_args.append("-cflags");
1235 try zig_args.append("--");
1236 prev_has_extra_flags = false;
1237 }
1238 } else {
1239 try zig_args.append("-cflags");
1240 for (c_source_files.flags) |flag| {
1241 try zig_args.append(flag);
1242 }
1243 try zig_args.append("--");
1244 }
1245 for (c_source_files.files) |file| {
1246 try zig_args.append(builder.pathFromRoot(file));
1247 }
1248 },
1249 }
1250 }
1251
1252 if (transitive_deps.is_linking_libcpp) {
1253 try zig_args.append("-lc++");
1254 }
1255
1256 if (transitive_deps.is_linking_libc) {
1257 try zig_args.append("-lc");
1258 }
1259
1260 if (self.image_base) |image_base| {
1261 try zig_args.append("--image-base");
1262 try zig_args.append(builder.fmt("0x{x}", .{image_base}));
1263 }
1264
1265 if (self.filter) |filter| {
1266 try zig_args.append("--test-filter");
1267 try zig_args.append(filter);
1268 }
1269
1270 if (self.test_evented_io) {
1271 try zig_args.append("--test-evented-io");
1272 }
1273
1274 if (self.name_prefix.len != 0) {
1275 try zig_args.append("--test-name-prefix");
1276 try zig_args.append(self.name_prefix);
1277 }
1278
1279 if (self.test_runner) |test_runner| {
1280 try zig_args.append("--test-runner");
1281 try zig_args.append(builder.pathFromRoot(test_runner));
1282 }
1283
1284 for (builder.debug_log_scopes) |log_scope| {
1285 try zig_args.append("--debug-log");
1286 try zig_args.append(log_scope);
1287 }
1288
1289 if (builder.debug_compile_errors) {
1290 try zig_args.append("--debug-compile-errors");
1291 }
1292
1293 if (builder.verbose_cimport) try zig_args.append("--verbose-cimport");
1294 if (builder.verbose_air) try zig_args.append("--verbose-air");
1295 if (builder.verbose_llvm_ir) try zig_args.append("--verbose-llvm-ir");
1296 if (builder.verbose_link or self.verbose_link) try zig_args.append("--verbose-link");
1297 if (builder.verbose_cc or self.verbose_cc) try zig_args.append("--verbose-cc");
1298 if (builder.verbose_llvm_cpu_features) try zig_args.append("--verbose-llvm-cpu-features");
1299
1300 if (self.emit_analysis.getArg(builder, "emit-analysis")) |arg| try zig_args.append(arg);
1301 if (self.emit_asm.getArg(builder, "emit-asm")) |arg| try zig_args.append(arg);
1302 if (self.emit_bin.getArg(builder, "emit-bin")) |arg| try zig_args.append(arg);
1303 if (self.emit_docs.getArg(builder, "emit-docs")) |arg| try zig_args.append(arg);
1304 if (self.emit_implib.getArg(builder, "emit-implib")) |arg| try zig_args.append(arg);
1305 if (self.emit_llvm_bc.getArg(builder, "emit-llvm-bc")) |arg| try zig_args.append(arg);
1306 if (self.emit_llvm_ir.getArg(builder, "emit-llvm-ir")) |arg| try zig_args.append(arg);
1307
1308 if (self.emit_h) try zig_args.append("-femit-h");
1309
1310 try addFlag(&zig_args, "strip", self.strip);
1311 try addFlag(&zig_args, "unwind-tables", self.unwind_tables);
1312
1313 switch (self.compress_debug_sections) {
1314 .none => {},
1315 .zlib => try zig_args.append("--compress-debug-sections=zlib"),
1316 }
1317
1318 if (self.link_eh_frame_hdr) {
1319 try zig_args.append("--eh-frame-hdr");
1320 }
1321 if (self.link_emit_relocs) {
1322 try zig_args.append("--emit-relocs");
1323 }
1324 if (self.link_function_sections) {
1325 try zig_args.append("-ffunction-sections");
1326 }
1327 if (self.link_gc_sections) |x| {
1328 try zig_args.append(if (x) "--gc-sections" else "--no-gc-sections");
1329 }
1330 if (self.linker_allow_shlib_undefined) |x| {
1331 try zig_args.append(if (x) "-fallow-shlib-undefined" else "-fno-allow-shlib-undefined");
1332 }
1333 if (self.link_z_notext) {
1334 try zig_args.append("-z");
1335 try zig_args.append("notext");
1336 }
1337 if (!self.link_z_relro) {
1338 try zig_args.append("-z");
1339 try zig_args.append("norelro");
1340 }
1341 if (self.link_z_lazy) {
1342 try zig_args.append("-z");
1343 try zig_args.append("lazy");
1344 }
1345 if (self.link_z_common_page_size) |size| {
1346 try zig_args.append("-z");
1347 try zig_args.append(builder.fmt("common-page-size={d}", .{size}));
1348 }
1349 if (self.link_z_max_page_size) |size| {
1350 try zig_args.append("-z");
1351 try zig_args.append(builder.fmt("max-page-size={d}", .{size}));
1352 }
1353
1354 if (self.libc_file) |libc_file| {
1355 try zig_args.append("--libc");
1356 try zig_args.append(libc_file.getPath(builder));
1357 } else if (builder.libc_file) |libc_file| {
1358 try zig_args.append("--libc");
1359 try zig_args.append(libc_file);
1360 }
1361
1362 switch (self.optimize) {
1363 .Debug => {}, // Skip since it's the default.
1364 else => try zig_args.append(builder.fmt("-O{s}", .{@tagName(self.optimize)})),
1365 }
1366
1367 try zig_args.append("--cache-dir");
1368 try zig_args.append(builder.pathFromRoot(builder.cache_root));
1369
1370 try zig_args.append("--global-cache-dir");
1371 try zig_args.append(builder.pathFromRoot(builder.global_cache_root));
1372
1373 try zig_args.append("--name");
1374 try zig_args.append(self.name);
1375
1376 if (self.linkage) |some| switch (some) {
1377 .dynamic => try zig_args.append("-dynamic"),
1378 .static => try zig_args.append("-static"),
1379 };
1380 if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic) {
1381 if (self.version) |version| {
1382 try zig_args.append("--version");
1383 try zig_args.append(builder.fmt("{}", .{version}));
1384 }
1385
1386 if (self.target.isDarwin()) {
1387 const install_name = self.install_name orelse builder.fmt("@rpath/{s}{s}{s}", .{
1388 self.target.libPrefix(),
1389 self.name,
1390 self.target.dynamicLibSuffix(),
1391 });
1392 try zig_args.append("-install_name");
1393 try zig_args.append(install_name);
1394 }
1395 }
1396
1397 if (self.entitlements) |entitlements| {
1398 try zig_args.appendSlice(&[_][]const u8{ "--entitlements", entitlements });
1399 }
1400 if (self.pagezero_size) |pagezero_size| {
1401 const size = try std.fmt.allocPrint(builder.allocator, "{x}", .{pagezero_size});
1402 try zig_args.appendSlice(&[_][]const u8{ "-pagezero_size", size });
1403 }
1404 if (self.search_strategy) |strat| switch (strat) {
1405 .paths_first => try zig_args.append("-search_paths_first"),
1406 .dylibs_first => try zig_args.append("-search_dylibs_first"),
1407 };
1408 if (self.headerpad_size) |headerpad_size| {
1409 const size = try std.fmt.allocPrint(builder.allocator, "{x}", .{headerpad_size});
1410 try zig_args.appendSlice(&[_][]const u8{ "-headerpad", size });
1411 }
1412 if (self.headerpad_max_install_names) {
1413 try zig_args.append("-headerpad_max_install_names");
1414 }
1415 if (self.dead_strip_dylibs) {
1416 try zig_args.append("-dead_strip_dylibs");
1417 }
1418
1419 try addFlag(&zig_args, "compiler-rt", self.bundle_compiler_rt);
1420 try addFlag(&zig_args, "single-threaded", self.single_threaded);
1421 if (self.disable_stack_probing) {
1422 try zig_args.append("-fno-stack-check");
1423 }
1424 try addFlag(&zig_args, "stack-protector", self.stack_protector);
1425 if (self.red_zone) |red_zone| {
1426 if (red_zone) {
1427 try zig_args.append("-mred-zone");
1428 } else {
1429 try zig_args.append("-mno-red-zone");
1430 }
1431 }
1432 try addFlag(&zig_args, "omit-frame-pointer", self.omit_frame_pointer);
1433 try addFlag(&zig_args, "dll-export-fns", self.dll_export_fns);
1434
1435 if (self.disable_sanitize_c) {
1436 try zig_args.append("-fno-sanitize-c");
1437 }
1438 if (self.sanitize_thread) {
1439 try zig_args.append("-fsanitize-thread");
1440 }
1441 if (self.rdynamic) {
1442 try zig_args.append("-rdynamic");
1443 }
1444 if (self.import_memory) {
1445 try zig_args.append("--import-memory");
1446 }
1447 if (self.import_symbols) {
1448 try zig_args.append("--import-symbols");
1449 }
1450 if (self.import_table) {
1451 try zig_args.append("--import-table");
1452 }
1453 if (self.export_table) {
1454 try zig_args.append("--export-table");
1455 }
1456 if (self.initial_memory) |initial_memory| {
1457 try zig_args.append(builder.fmt("--initial-memory={d}", .{initial_memory}));
1458 }
1459 if (self.max_memory) |max_memory| {
1460 try zig_args.append(builder.fmt("--max-memory={d}", .{max_memory}));
1461 }
1462 if (self.shared_memory) {
1463 try zig_args.append("--shared-memory");
1464 }
1465 if (self.global_base) |global_base| {
1466 try zig_args.append(builder.fmt("--global-base={d}", .{global_base}));
1467 }
1468
1469 if (self.code_model != .default) {
1470 try zig_args.append("-mcmodel");
1471 try zig_args.append(@tagName(self.code_model));
1472 }
1473 if (self.wasi_exec_model) |model| {
1474 try zig_args.append(builder.fmt("-mexec-model={s}", .{@tagName(model)}));
1475 }
1476 for (self.export_symbol_names) |symbol_name| {
1477 try zig_args.append(builder.fmt("--export={s}", .{symbol_name}));
1478 }
1479
1480 if (!self.target.isNative()) {
1481 try zig_args.appendSlice(&.{
1482 "-target", try self.target.zigTriple(builder.allocator),
1483 "-mcpu", try std.Build.serializeCpu(builder.allocator, self.target.getCpu()),
1484 });
1485
1486 if (self.target.dynamic_linker.get()) |dynamic_linker| {
1487 try zig_args.append("--dynamic-linker");
1488 try zig_args.append(dynamic_linker);
1489 }
1490 }
1491
1492 if (self.linker_script) |linker_script| {
1493 try zig_args.append("--script");
1494 try zig_args.append(linker_script.getPath(builder));
1495 }
1496
1497 if (self.version_script) |version_script| {
1498 try zig_args.append("--version-script");
1499 try zig_args.append(builder.pathFromRoot(version_script));
1500 }
1501
1502 if (self.kind == .@"test") {
1503 if (self.exec_cmd_args) |exec_cmd_args| {
1504 for (exec_cmd_args) |cmd_arg| {
1505 if (cmd_arg) |arg| {
1506 try zig_args.append("--test-cmd");
1507 try zig_args.append(arg);
1508 } else {
1509 try zig_args.append("--test-cmd-bin");
1510 }
1511 }
1512 } else {
1513 const need_cross_glibc = self.target.isGnuLibC() and transitive_deps.is_linking_libc;
1514
1515 switch (builder.host.getExternalExecutor(self.target_info, .{
1516 .qemu_fixes_dl = need_cross_glibc and builder.glibc_runtimes_dir != null,
1517 .link_libc = transitive_deps.is_linking_libc,
1518 })) {
1519 .native => {},
1520 .bad_dl, .bad_os_or_cpu => {
1521 try zig_args.append("--test-no-exec");
1522 },
1523 .rosetta => if (builder.enable_rosetta) {
1524 try zig_args.append("--test-cmd-bin");
1525 } else {
1526 try zig_args.append("--test-no-exec");
1527 },
1528 .qemu => |bin_name| ok: {
1529 if (builder.enable_qemu) qemu: {
1530 const glibc_dir_arg = if (need_cross_glibc)
1531 builder.glibc_runtimes_dir orelse break :qemu
1532 else
1533 null;
1534 try zig_args.append("--test-cmd");
1535 try zig_args.append(bin_name);
1536 if (glibc_dir_arg) |dir| {
1537 // TODO look into making this a call to `linuxTriple`. This
1538 // needs the directory to be called "i686" rather than
1539 // "x86" which is why we do it manually here.
1540 const fmt_str = "{s}" ++ fs.path.sep_str ++ "{s}-{s}-{s}";
1541 const cpu_arch = self.target.getCpuArch();
1542 const os_tag = self.target.getOsTag();
1543 const abi = self.target.getAbi();
1544 const cpu_arch_name: []const u8 = if (cpu_arch == .x86)
1545 "i686"
1546 else
1547 @tagName(cpu_arch);
1548 const full_dir = try std.fmt.allocPrint(builder.allocator, fmt_str, .{
1549 dir, cpu_arch_name, @tagName(os_tag), @tagName(abi),
1550 });
1551
1552 try zig_args.append("--test-cmd");
1553 try zig_args.append("-L");
1554 try zig_args.append("--test-cmd");
1555 try zig_args.append(full_dir);
1556 }
1557 try zig_args.append("--test-cmd-bin");
1558 break :ok;
1559 }
1560 try zig_args.append("--test-no-exec");
1561 },
1562 .wine => |bin_name| if (builder.enable_wine) {
1563 try zig_args.append("--test-cmd");
1564 try zig_args.append(bin_name);
1565 try zig_args.append("--test-cmd-bin");
1566 } else {
1567 try zig_args.append("--test-no-exec");
1568 },
1569 .wasmtime => |bin_name| if (builder.enable_wasmtime) {
1570 try zig_args.append("--test-cmd");
1571 try zig_args.append(bin_name);
1572 try zig_args.append("--test-cmd");
1573 try zig_args.append("--dir=.");
1574 try zig_args.append("--test-cmd-bin");
1575 } else {
1576 try zig_args.append("--test-no-exec");
1577 },
1578 .darling => |bin_name| if (builder.enable_darling) {
1579 try zig_args.append("--test-cmd");
1580 try zig_args.append(bin_name);
1581 try zig_args.append("--test-cmd-bin");
1582 } else {
1583 try zig_args.append("--test-no-exec");
1584 },
1585 }
1586 }
1587 } else if (self.kind == .test_exe) {
1588 try zig_args.append("--test-no-exec");
1589 }
1590
1591 {
1592 const keys = self.modules.keys();
1593 for (self.modules.values()) |module, i| {
1594 const name = keys[i];
1595 try self.appendModuleArgs(&zig_args, name, module);
1596 }
1597 }
1598
1599 for (self.include_dirs.items) |include_dir| {
1600 switch (include_dir) {
1601 .raw_path => |include_path| {
1602 try zig_args.append("-I");
1603 try zig_args.append(builder.pathFromRoot(include_path));
1604 },
1605 .raw_path_system => |include_path| {
1606 if (builder.sysroot != null) {
1607 try zig_args.append("-iwithsysroot");
1608 } else {
1609 try zig_args.append("-isystem");
1610 }
1611
1612 const resolved_include_path = builder.pathFromRoot(include_path);
1613
1614 const common_include_path = if (builtin.os.tag == .windows and builder.sysroot != null and fs.path.isAbsolute(resolved_include_path)) blk: {
1615 // We need to check for disk designator and strip it out from dir path so
1616 // that zig/clang can concat resolved_include_path with sysroot.
1617 const disk_designator = fs.path.diskDesignatorWindows(resolved_include_path);
1618
1619 if (mem.indexOf(u8, resolved_include_path, disk_designator)) |where| {
1620 break :blk resolved_include_path[where + disk_designator.len ..];
1621 }
1622
1623 break :blk resolved_include_path;
1624 } else resolved_include_path;
1625
1626 try zig_args.append(common_include_path);
1627 },
1628 .other_step => |other| {
1629 if (other.emit_h) {
1630 const h_path = other.getOutputHSource().getPath(builder);
1631 try zig_args.append("-isystem");
1632 try zig_args.append(fs.path.dirname(h_path).?);
1633 }
1634 if (other.installed_headers.items.len > 0) {
1635 for (other.installed_headers.items) |install_step| {
1636 try install_step.make();
1637 }
1638 try zig_args.append("-I");
1639 try zig_args.append(builder.pathJoin(&.{
1640 other.builder.install_prefix, "include",
1641 }));
1642 }
1643 },
1644 .config_header_step => |config_header| {
1645 const full_file_path = config_header.output_file.path.?;
1646 const header_dir_path = full_file_path[0 .. full_file_path.len - config_header.include_path.len];
1647 try zig_args.appendSlice(&.{ "-I", header_dir_path });
1648 },
1649 }
1650 }
1651
1652 for (self.lib_paths.items) |lib_path| {
1653 try zig_args.append("-L");
1654 try zig_args.append(lib_path);
1655 }
1656
1657 for (self.rpaths.items) |rpath| {
1658 try zig_args.append("-rpath");
1659 try zig_args.append(rpath);
1660 }
1661
1662 for (self.c_macros.items) |c_macro| {
1663 try zig_args.append("-D");
1664 try zig_args.append(c_macro);
1665 }
1666
1667 if (self.target.isDarwin()) {
1668 for (self.framework_dirs.items) |dir| {
1669 if (builder.sysroot != null) {
1670 try zig_args.append("-iframeworkwithsysroot");
1671 } else {
1672 try zig_args.append("-iframework");
1673 }
1674 try zig_args.append(dir);
1675 try zig_args.append("-F");
1676 try zig_args.append(dir);
1677 }
1678
1679 var it = self.frameworks.iterator();
1680 while (it.next()) |entry| {
1681 const name = entry.key_ptr.*;
1682 const info = entry.value_ptr.*;
1683 if (info.needed) {
1684 try zig_args.append("-needed_framework");
1685 } else if (info.weak) {
1686 try zig_args.append("-weak_framework");
1687 } else {
1688 try zig_args.append("-framework");
1689 }
1690 try zig_args.append(name);
1691 }
1692 } else {
1693 if (self.framework_dirs.items.len > 0) {
1694 log.info("Framework directories have been added for a non-darwin target, this will have no affect on the build", .{});
1695 }
1696
1697 if (self.frameworks.count() > 0) {
1698 log.info("Frameworks have been added for a non-darwin target, this will have no affect on the build", .{});
1699 }
1700 }
1701
1702 if (builder.sysroot) |sysroot| {
1703 try zig_args.appendSlice(&[_][]const u8{ "--sysroot", sysroot });
1704 }
1705
1706 for (builder.search_prefixes.items) |search_prefix| {
1707 try zig_args.append("-L");
1708 try zig_args.append(builder.pathJoin(&.{
1709 search_prefix, "lib",
1710 }));
1711 try zig_args.append("-I");
1712 try zig_args.append(builder.pathJoin(&.{
1713 search_prefix, "include",
1714 }));
1715 }
1716
1717 try addFlag(&zig_args, "valgrind", self.valgrind_support);
1718 try addFlag(&zig_args, "each-lib-rpath", self.each_lib_rpath);
1719 try addFlag(&zig_args, "build-id", self.build_id);
1720
1721 if (self.override_lib_dir) |dir| {
1722 try zig_args.append("--zig-lib-dir");
1723 try zig_args.append(builder.pathFromRoot(dir));
1724 } else if (builder.override_lib_dir) |dir| {
1725 try zig_args.append("--zig-lib-dir");
1726 try zig_args.append(builder.pathFromRoot(dir));
1727 }
1728
1729 if (self.main_pkg_path) |dir| {
1730 try zig_args.append("--main-pkg-path");
1731 try zig_args.append(builder.pathFromRoot(dir));
1732 }
1733
1734 try addFlag(&zig_args, "PIC", self.force_pic);
1735 try addFlag(&zig_args, "PIE", self.pie);
1736 try addFlag(&zig_args, "lto", self.want_lto);
1737
1738 if (self.subsystem) |subsystem| {
1739 try zig_args.append("--subsystem");
1740 try zig_args.append(switch (subsystem) {
1741 .Console => "console",
1742 .Windows => "windows",
1743 .Posix => "posix",
1744 .Native => "native",
1745 .EfiApplication => "efi_application",
1746 .EfiBootServiceDriver => "efi_boot_service_driver",
1747 .EfiRom => "efi_rom",
1748 .EfiRuntimeDriver => "efi_runtime_driver",
1749 });
1750 }
1751
1752 try zig_args.append("--enable-cache");
1753
1754 // Windows has an argument length limit of 32,766 characters, macOS 262,144 and Linux
1755 // 2,097,152. If our args exceed 30 KiB, we instead write them to a "response file" and
1756 // pass that to zig, e.g. via 'zig build-lib @args.rsp'
1757 // See @file syntax here: https://gcc.gnu.org/onlinedocs/gcc/Overall-Options.html
1758 var args_length: usize = 0;
1759 for (zig_args.items) |arg| {
1760 args_length += arg.len + 1; // +1 to account for null terminator
1761 }
1762 if (args_length >= 30 * 1024) {
1763 const args_dir = try fs.path.join(
1764 builder.allocator,
1765 &[_][]const u8{ builder.pathFromRoot("zig-cache"), "args" },
1766 );
1767 try std.fs.cwd().makePath(args_dir);
1768
1769 var args_arena = std.heap.ArenaAllocator.init(builder.allocator);
1770 defer args_arena.deinit();
1771
1772 const args_to_escape = zig_args.items[2..];
1773 var escaped_args = try ArrayList([]const u8).initCapacity(args_arena.allocator(), args_to_escape.len);
1774
1775 arg_blk: for (args_to_escape) |arg| {
1776 for (arg) |c, arg_idx| {
1777 if (c == '\\' or c == '"') {
1778 // Slow path for arguments that need to be escaped. We'll need to allocate and copy
1779 var escaped = try ArrayList(u8).initCapacity(args_arena.allocator(), arg.len + 1);
1780 const writer = escaped.writer();
1781 try writer.writeAll(arg[0..arg_idx]);
1782 for (arg[arg_idx..]) |to_escape| {
1783 if (to_escape == '\\' or to_escape == '"') try writer.writeByte('\\');
1784 try writer.writeByte(to_escape);
1785 }
1786 escaped_args.appendAssumeCapacity(escaped.items);
1787 continue :arg_blk;
1788 }
1789 }
1790 escaped_args.appendAssumeCapacity(arg); // no escaping needed so just use original argument
1791 }
1792
1793 // Write the args to zig-cache/args/<SHA256 hash of args> to avoid conflicts with
1794 // other zig build commands running in parallel.
1795 const partially_quoted = try std.mem.join(builder.allocator, "\" \"", escaped_args.items);
1796 const args = try std.mem.concat(builder.allocator, u8, &[_][]const u8{ "\"", partially_quoted, "\"" });
1797
1798 var args_hash: [Sha256.digest_length]u8 = undefined;
1799 Sha256.hash(args, &args_hash, .{});
1800 var args_hex_hash: [Sha256.digest_length * 2]u8 = undefined;
1801 _ = try std.fmt.bufPrint(
1802 &args_hex_hash,
1803 "{s}",
1804 .{std.fmt.fmtSliceHexLower(&args_hash)},
1805 );
1806
1807 const args_file = try fs.path.join(builder.allocator, &[_][]const u8{ args_dir, args_hex_hash[0..] });
1808 try std.fs.cwd().writeFile(args_file, args);
1809
1810 zig_args.shrinkRetainingCapacity(2);
1811 try zig_args.append(try std.mem.concat(builder.allocator, u8, &[_][]const u8{ "@", args_file }));
1812 }
1813
1814 const output_dir_nl = try builder.execFromStep(zig_args.items, &self.step);
1815 const build_output_dir = mem.trimRight(u8, output_dir_nl, "\r\n");
1816
1817 if (self.output_dir) |output_dir| {
1818 var src_dir = try std.fs.cwd().openIterableDir(build_output_dir, .{});
1819 defer src_dir.close();
1820
1821 // Create the output directory if it doesn't exist.
1822 try std.fs.cwd().makePath(output_dir);
1823
1824 var dest_dir = try std.fs.cwd().openDir(output_dir, .{});
1825 defer dest_dir.close();
1826
1827 var it = src_dir.iterate();
1828 while (try it.next()) |entry| {
1829 // The compiler can put these files into the same directory, but we don't
1830 // want to copy them over.
1831 if (mem.eql(u8, entry.name, "llvm-ar.id") or
1832 mem.eql(u8, entry.name, "libs.txt") or
1833 mem.eql(u8, entry.name, "builtin.zig") or
1834 mem.eql(u8, entry.name, "zld.id") or
1835 mem.eql(u8, entry.name, "lld.id")) continue;
1836
1837 _ = try src_dir.dir.updateFile(entry.name, dest_dir, entry.name, .{});
1838 }
1839 } else {
1840 self.output_dir = build_output_dir;
1841 }
1842
1843 // This will ensure all output filenames will now have the output_dir available!
1844 self.computeOutFileNames();
1845
1846 // Update generated files
1847 if (self.output_dir != null) {
1848 self.output_path_source.path = builder.pathJoin(
1849 &.{ self.output_dir.?, self.out_filename },
1850 );
1851
1852 if (self.emit_h) {
1853 self.output_h_path_source.path = builder.pathJoin(
1854 &.{ self.output_dir.?, self.out_h_filename },
1855 );
1856 }
1857
1858 if (self.target.isWindows() or self.target.isUefi()) {
1859 self.output_pdb_path_source.path = builder.pathJoin(
1860 &.{ self.output_dir.?, self.out_pdb_filename },
1861 );
1862 }
1863 }
1864
1865 if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic and self.version != null and self.target.wantSharedLibSymLinks()) {
1866 try doAtomicSymLinks(builder.allocator, self.getOutputSource().getPath(builder), self.major_only_filename.?, self.name_only_filename.?);
1867 }
1868}
1869
1870fn isLibCLibrary(name: []const u8) bool {
1871 const libc_libraries = [_][]const u8{ "c", "m", "dl", "rt", "pthread" };
1872 for (libc_libraries) |libc_lib_name| {
1873 if (mem.eql(u8, name, libc_lib_name))
1874 return true;
1875 }
1876 return false;
1877}
1878
1879fn isLibCppLibrary(name: []const u8) bool {
1880 const libcpp_libraries = [_][]const u8{ "c++", "stdc++" };
1881 for (libcpp_libraries) |libcpp_lib_name| {
1882 if (mem.eql(u8, name, libcpp_lib_name))
1883 return true;
1884 }
1885 return false;
1886}
1887
1888/// Returned slice must be freed by the caller.
1889fn findVcpkgRoot(allocator: Allocator) !?[]const u8 {
1890 const appdata_path = try fs.getAppDataDir(allocator, "vcpkg");
1891 defer allocator.free(appdata_path);
1892
1893 const path_file = try fs.path.join(allocator, &[_][]const u8{ appdata_path, "vcpkg.path.txt" });
1894 defer allocator.free(path_file);
1895
1896 const file = fs.cwd().openFile(path_file, .{}) catch return null;
1897 defer file.close();
1898
1899 const size = @intCast(usize, try file.getEndPos());
1900 const vcpkg_path = try allocator.alloc(u8, size);
1901 const size_read = try file.read(vcpkg_path);
1902 std.debug.assert(size == size_read);
1903
1904 return vcpkg_path;
1905}
1906
1907pub fn doAtomicSymLinks(
1908 allocator: Allocator,
1909 output_path: []const u8,
1910 filename_major_only: []const u8,
1911 filename_name_only: []const u8,
1912) !void {
1913 const out_dir = fs.path.dirname(output_path) orelse ".";
1914 const out_basename = fs.path.basename(output_path);
1915 // sym link for libfoo.so.1 to libfoo.so.1.2.3
1916 const major_only_path = try fs.path.join(
1917 allocator,
1918 &[_][]const u8{ out_dir, filename_major_only },
1919 );
1920 fs.atomicSymLink(allocator, out_basename, major_only_path) catch |err| {
1921 log.err("Unable to symlink {s} -> {s}", .{ major_only_path, out_basename });
1922 return err;
1923 };
1924 // sym link for libfoo.so to libfoo.so.1
1925 const name_only_path = try fs.path.join(
1926 allocator,
1927 &[_][]const u8{ out_dir, filename_name_only },
1928 );
1929 fs.atomicSymLink(allocator, filename_major_only, name_only_path) catch |err| {
1930 log.err("Unable to symlink {s} -> {s}", .{ name_only_path, filename_major_only });
1931 return err;
1932 };
1933}
1934
1935fn execPkgConfigList(self: *std.Build, out_code: *u8) (PkgConfigError || ExecError)![]const PkgConfigPkg {
1936 const stdout = try self.execAllowFail(&[_][]const u8{ "pkg-config", "--list-all" }, out_code, .Ignore);
1937 var list = ArrayList(PkgConfigPkg).init(self.allocator);
1938 errdefer list.deinit();
1939 var line_it = mem.tokenize(u8, stdout, "\r\n");
1940 while (line_it.next()) |line| {
1941 if (mem.trim(u8, line, " \t").len == 0) continue;
1942 var tok_it = mem.tokenize(u8, line, " \t");
1943 try list.append(PkgConfigPkg{
1944 .name = tok_it.next() orelse return error.PkgConfigInvalidOutput,
1945 .desc = tok_it.rest(),
1946 });
1947 }
1948 return list.toOwnedSlice();
1949}
1950
1951fn getPkgConfigList(self: *std.Build) ![]const PkgConfigPkg {
1952 if (self.pkg_config_pkg_list) |res| {
1953 return res;
1954 }
1955 var code: u8 = undefined;
1956 if (execPkgConfigList(self, &code)) |list| {
1957 self.pkg_config_pkg_list = list;
1958 return list;
1959 } else |err| {
1960 const result = switch (err) {
1961 error.ProcessTerminated => error.PkgConfigCrashed,
1962 error.ExecNotSupported => error.PkgConfigFailed,
1963 error.ExitCodeFailure => error.PkgConfigFailed,
1964 error.FileNotFound => error.PkgConfigNotInstalled,
1965 error.InvalidName => error.PkgConfigNotInstalled,
1966 error.PkgConfigInvalidOutput => error.PkgConfigInvalidOutput,
1967 error.ChildExecFailed => error.PkgConfigFailed,
1968 else => return err,
1969 };
1970 self.pkg_config_pkg_list = result;
1971 return result;
1972 }
1973}
1974
1975fn addFlag(args: *ArrayList([]const u8), comptime name: []const u8, opt: ?bool) !void {
1976 const cond = opt orelse return;
1977 try args.ensureUnusedCapacity(1);
1978 if (cond) {
1979 args.appendAssumeCapacity("-f" ++ name);
1980 } else {
1981 args.appendAssumeCapacity("-fno-" ++ name);
1982 }
1983}
1984
1985const TransitiveDeps = struct {
1986 link_objects: ArrayList(LinkObject),
1987 seen_system_libs: StringHashMap(void),
1988 seen_steps: std.AutoHashMap(*const Step, void),
1989 is_linking_libcpp: bool,
1990 is_linking_libc: bool,
1991 frameworks: *StringHashMap(FrameworkLinkInfo),
1992
1993 fn add(td: *TransitiveDeps, link_objects: []const LinkObject) !void {
1994 try td.link_objects.ensureUnusedCapacity(link_objects.len);
1995
1996 for (link_objects) |link_object| {
1997 try td.link_objects.append(link_object);
1998 switch (link_object) {
1999 .other_step => |other| try addInner(td, other, other.isDynamicLibrary()),
2000 else => {},
2001 }
2002 }
2003 }
2004
2005 fn addInner(td: *TransitiveDeps, other: *CompileStep, dyn: bool) !void {
2006 // Inherit dependency on libc and libc++
2007 td.is_linking_libcpp = td.is_linking_libcpp or other.is_linking_libcpp;
2008 td.is_linking_libc = td.is_linking_libc or other.is_linking_libc;
2009
2010 // Inherit dependencies on darwin frameworks
2011 if (!dyn) {
2012 var it = other.frameworks.iterator();
2013 while (it.next()) |framework| {
2014 try td.frameworks.put(framework.key_ptr.*, framework.value_ptr.*);
2015 }
2016 }
2017
2018 // Inherit dependencies on system libraries and static libraries.
2019 for (other.link_objects.items) |other_link_object| {
2020 switch (other_link_object) {
2021 .system_lib => |system_lib| {
2022 if ((try td.seen_system_libs.fetchPut(system_lib.name, {})) != null)
2023 continue;
2024
2025 if (dyn)
2026 continue;
2027
2028 try td.link_objects.append(other_link_object);
2029 },
2030 .other_step => |inner_other| {
2031 if ((try td.seen_steps.fetchPut(&inner_other.step, {})) != null)
2032 continue;
2033
2034 if (!dyn)
2035 try td.link_objects.append(other_link_object);
2036
2037 try addInner(td, inner_other, dyn or inner_other.isDynamicLibrary());
2038 },
2039 else => continue,
2040 }
2041 }
2042 }
2043};
lib/std/Build/ConfigHeaderStep.zig created+372
......@@ -0,0 +1,372 @@
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 = union(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: std.Build.FileSource,
11 /// The configure format supported by CMake. It uses `@@FOO@@` and
12 /// `#cmakedefine` for template substitution.
13 cmake: std.Build.FileSource,
14 /// Instead of starting with an input file, start with nothing.
15 blank,
16
17 pub fn getFileSource(style: Style) ?std.Build.FileSource {
18 switch (style) {
19 .autoconf, .cmake => |s| return s,
20 .blank => return null,
21 }
22 }
23};
24
25pub const Value = union(enum) {
26 undef,
27 defined,
28 boolean: bool,
29 int: i64,
30 ident: []const u8,
31 string: []const u8,
32};
33
34step: Step,
35builder: *std.Build,
36values: std.StringArrayHashMap(Value),
37output_file: std.Build.GeneratedFile,
38
39style: Style,
40max_bytes: usize,
41include_path: []const u8,
42
43pub const Options = struct {
44 style: Style = .blank,
45 max_bytes: usize = 2 * 1024 * 1024,
46 include_path: ?[]const u8 = null,
47};
48
49pub fn create(builder: *std.Build, options: Options) *ConfigHeaderStep {
50 const self = builder.allocator.create(ConfigHeaderStep) catch @panic("OOM");
51 const name = if (options.style.getFileSource()) |s|
52 builder.fmt("configure {s} header {s}", .{ @tagName(options.style), s.getDisplayName() })
53 else
54 builder.fmt("configure {s} header", .{@tagName(options.style)});
55 self.* = .{
56 .builder = builder,
57 .step = Step.init(base_id, name, builder.allocator, make),
58 .style = options.style,
59 .values = std.StringArrayHashMap(Value).init(builder.allocator),
60
61 .max_bytes = options.max_bytes,
62 .include_path = "config.h",
63 .output_file = .{ .step = &self.step },
64 };
65
66 if (options.style.getFileSource()) |s| switch (s) {
67 .path => |p| {
68 const basename = std.fs.path.basename(p);
69 if (std.mem.endsWith(u8, basename, ".h.in")) {
70 self.include_path = basename[0 .. basename.len - 3];
71 }
72 },
73 else => {},
74 };
75
76 if (options.include_path) |include_path| {
77 self.include_path = include_path;
78 }
79
80 return self;
81}
82
83pub fn addValues(self: *ConfigHeaderStep, values: anytype) void {
84 return addValuesInner(self, values) catch @panic("OOM");
85}
86
87fn addValuesInner(self: *ConfigHeaderStep, values: anytype) !void {
88 inline for (@typeInfo(@TypeOf(values)).Struct.fields) |field| {
89 try putValue(self, field.name, field.type, @field(values, field.name));
90 }
91}
92
93fn putValue(self: *ConfigHeaderStep, field_name: []const u8, comptime T: type, v: T) !void {
94 switch (@typeInfo(T)) {
95 .Null => {
96 try self.values.put(field_name, .undef);
97 },
98 .Void => {
99 try self.values.put(field_name, .defined);
100 },
101 .Bool => {
102 try self.values.put(field_name, .{ .boolean = v });
103 },
104 .Int => {
105 try self.values.put(field_name, .{ .int = v });
106 },
107 .ComptimeInt => {
108 try self.values.put(field_name, .{ .int = v });
109 },
110 .EnumLiteral => {
111 try self.values.put(field_name, .{ .ident = @tagName(v) });
112 },
113 .Optional => {
114 if (v) |x| {
115 return putValue(self, field_name, @TypeOf(x), x);
116 } else {
117 try self.values.put(field_name, .undef);
118 }
119 },
120 .Pointer => |ptr| {
121 switch (@typeInfo(ptr.child)) {
122 .Array => |array| {
123 if (ptr.size == .One and array.child == u8) {
124 try self.values.put(field_name, .{ .string = v });
125 return;
126 }
127 },
128 else => {},
129 }
130
131 @compileError("unsupported ConfigHeaderStep value type: " ++ @typeName(T));
132 },
133 else => @compileError("unsupported ConfigHeaderStep value type: " ++ @typeName(T)),
134 }
135}
136
137fn make(step: *Step) !void {
138 const self = @fieldParentPtr(ConfigHeaderStep, "step", step);
139 const gpa = self.builder.allocator;
140
141 // The cache is used here not really as a way to speed things up - because writing
142 // the data to a file would probably be very fast - but as a way to find a canonical
143 // location to put build artifacts.
144
145 // If, for example, a hard-coded path was used as the location to put ConfigHeaderStep
146 // files, then two ConfigHeaderStep executing in parallel might clobber each other.
147
148 // TODO port the cache system from the compiler to zig std lib. Until then
149 // we construct the path directly, and no "cache hit" detection happens;
150 // the files are always written.
151 // Note there is very similar code over in WriteFileStep
152 const Hasher = std.crypto.auth.siphash.SipHash128(1, 3);
153 // Random bytes to make ConfigHeaderStep unique. Refresh this with new
154 // random bytes when ConfigHeaderStep implementation is modified in a
155 // non-backwards-compatible way.
156 var hash = Hasher.init("PGuDTpidxyMqnkGM");
157
158 var output = std.ArrayList(u8).init(gpa);
159 defer output.deinit();
160
161 try output.appendSlice("/* This file was generated by ConfigHeaderStep using the Zig Build System. */\n");
162
163 switch (self.style) {
164 .autoconf => |file_source| {
165 const src_path = file_source.getPath(self.builder);
166 const contents = try std.fs.cwd().readFileAlloc(gpa, src_path, self.max_bytes);
167 try render_autoconf(contents, &output, self.values, src_path);
168 },
169 .cmake => |file_source| {
170 const src_path = file_source.getPath(self.builder);
171 const contents = try std.fs.cwd().readFileAlloc(gpa, src_path, self.max_bytes);
172 try render_cmake(contents, &output, self.values, src_path);
173 },
174 .blank => {
175 try render_blank(&output, self.values, self.include_path);
176 },
177 }
178
179 hash.update(output.items);
180
181 var digest: [16]u8 = undefined;
182 hash.final(&digest);
183 var hash_basename: [digest.len * 2]u8 = undefined;
184 _ = std.fmt.bufPrint(
185 &hash_basename,
186 "{s}",
187 .{std.fmt.fmtSliceHexLower(&digest)},
188 ) catch unreachable;
189
190 const output_dir = try std.fs.path.join(gpa, &[_][]const u8{
191 self.builder.cache_root, "o", &hash_basename,
192 });
193
194 // If output_path has directory parts, deal with them. Example:
195 // output_dir is zig-cache/o/HASH
196 // output_path is libavutil/avconfig.h
197 // We want to open directory zig-cache/o/HASH/libavutil/
198 // but keep output_dir as zig-cache/o/HASH for -I include
199 const sub_dir_path = if (std.fs.path.dirname(self.include_path)) |d|
200 try std.fs.path.join(gpa, &.{ output_dir, d })
201 else
202 output_dir;
203
204 var dir = std.fs.cwd().makeOpenPath(sub_dir_path, .{}) catch |err| {
205 std.debug.print("unable to make path {s}: {s}\n", .{ output_dir, @errorName(err) });
206 return err;
207 };
208 defer dir.close();
209
210 try dir.writeFile(std.fs.path.basename(self.include_path), output.items);
211
212 self.output_file.path = try std.fs.path.join(self.builder.allocator, &.{
213 output_dir, self.include_path,
214 });
215}
216
217fn render_autoconf(
218 contents: []const u8,
219 output: *std.ArrayList(u8),
220 values: std.StringArrayHashMap(Value),
221 src_path: []const u8,
222) !void {
223 var values_copy = try values.clone();
224 defer values_copy.deinit();
225
226 var any_errors = false;
227 var line_index: u32 = 0;
228 var line_it = std.mem.split(u8, contents, "\n");
229 while (line_it.next()) |line| : (line_index += 1) {
230 if (!std.mem.startsWith(u8, line, "#")) {
231 try output.appendSlice(line);
232 try output.appendSlice("\n");
233 continue;
234 }
235 var it = std.mem.tokenize(u8, line[1..], " \t\r");
236 const undef = it.next().?;
237 if (!std.mem.eql(u8, undef, "undef")) {
238 try output.appendSlice(line);
239 try output.appendSlice("\n");
240 continue;
241 }
242 const name = it.rest();
243 const kv = values_copy.fetchSwapRemove(name) orelse {
244 std.debug.print("{s}:{d}: error: unspecified config header value: '{s}'\n", .{
245 src_path, line_index + 1, name,
246 });
247 any_errors = true;
248 continue;
249 };
250 try renderValue(output, name, kv.value);
251 }
252
253 for (values_copy.keys()) |name| {
254 std.debug.print("{s}: error: config header value unused: '{s}'\n", .{ src_path, name });
255 }
256
257 if (any_errors) {
258 return error.HeaderConfigFailed;
259 }
260}
261
262fn render_cmake(
263 contents: []const u8,
264 output: *std.ArrayList(u8),
265 values: std.StringArrayHashMap(Value),
266 src_path: []const u8,
267) !void {
268 var values_copy = try values.clone();
269 defer values_copy.deinit();
270
271 var any_errors = false;
272 var line_index: u32 = 0;
273 var line_it = std.mem.split(u8, contents, "\n");
274 while (line_it.next()) |line| : (line_index += 1) {
275 if (!std.mem.startsWith(u8, line, "#")) {
276 try output.appendSlice(line);
277 try output.appendSlice("\n");
278 continue;
279 }
280 var it = std.mem.tokenize(u8, line[1..], " \t\r");
281 const cmakedefine = it.next().?;
282 if (!std.mem.eql(u8, cmakedefine, "cmakedefine")) {
283 try output.appendSlice(line);
284 try output.appendSlice("\n");
285 continue;
286 }
287 const name = it.next() orelse {
288 std.debug.print("{s}:{d}: error: missing define name\n", .{
289 src_path, line_index + 1,
290 });
291 any_errors = true;
292 continue;
293 };
294 const kv = values_copy.fetchSwapRemove(name) orelse {
295 std.debug.print("{s}:{d}: error: unspecified config header value: '{s}'\n", .{
296 src_path, line_index + 1, name,
297 });
298 any_errors = true;
299 continue;
300 };
301 try renderValue(output, name, kv.value);
302 }
303
304 for (values_copy.keys()) |name| {
305 std.debug.print("{s}: error: config header value unused: '{s}'\n", .{ src_path, name });
306 }
307
308 if (any_errors) {
309 return error.HeaderConfigFailed;
310 }
311}
312
313fn render_blank(
314 output: *std.ArrayList(u8),
315 defines: std.StringArrayHashMap(Value),
316 include_path: []const u8,
317) !void {
318 const include_guard_name = try output.allocator.dupe(u8, include_path);
319 for (include_guard_name) |*byte| {
320 switch (byte.*) {
321 'a'...'z' => byte.* = byte.* - 'a' + 'A',
322 'A'...'Z', '0'...'9' => continue,
323 else => byte.* = '_',
324 }
325 }
326
327 try output.appendSlice("#ifndef ");
328 try output.appendSlice(include_guard_name);
329 try output.appendSlice("\n#define ");
330 try output.appendSlice(include_guard_name);
331 try output.appendSlice("\n");
332
333 const values = defines.values();
334 for (defines.keys()) |name, i| {
335 try renderValue(output, name, values[i]);
336 }
337
338 try output.appendSlice("#endif /* ");
339 try output.appendSlice(include_guard_name);
340 try output.appendSlice(" */\n");
341}
342
343fn renderValue(output: *std.ArrayList(u8), name: []const u8, value: Value) !void {
344 switch (value) {
345 .undef => {
346 try output.appendSlice("/* #undef ");
347 try output.appendSlice(name);
348 try output.appendSlice(" */\n");
349 },
350 .defined => {
351 try output.appendSlice("#define ");
352 try output.appendSlice(name);
353 try output.appendSlice("\n");
354 },
355 .boolean => |b| {
356 try output.appendSlice("#define ");
357 try output.appendSlice(name);
358 try output.appendSlice(" ");
359 try output.appendSlice(if (b) "true\n" else "false\n");
360 },
361 .int => |i| {
362 try output.writer().print("#define {s} {d}\n", .{ name, i });
363 },
364 .ident => |ident| {
365 try output.writer().print("#define {s} {s}\n", .{ name, ident });
366 },
367 .string => |string| {
368 // TODO: use C-specific escaping instead of zig string literals
369 try output.writer().print("#define {s} \"{}\"\n", .{ name, std.zig.fmtEscapes(string) });
370 },
371 }
372}
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+374
......@@ -0,0 +1,374 @@
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 createModule(self: *OptionsStep) *std.Build.Module {
208 return self.builder.createModule(.{
209 .source_file = self.getSource(),
210 .dependencies = &.{},
211 });
212}
213
214pub fn getSource(self: *OptionsStep) FileSource {
215 return .{ .generated = &self.generated_file };
216}
217
218fn make(step: *Step) !void {
219 const self = @fieldParentPtr(OptionsStep, "step", step);
220
221 for (self.artifact_args.items) |item| {
222 self.addOption(
223 []const u8,
224 item.name,
225 self.builder.pathFromRoot(item.artifact.getOutputSource().getPath(self.builder)),
226 );
227 }
228
229 for (self.file_source_args.items) |item| {
230 self.addOption(
231 []const u8,
232 item.name,
233 item.source.getPath(self.builder),
234 );
235 }
236
237 const options_directory = self.builder.pathFromRoot(
238 try fs.path.join(
239 self.builder.allocator,
240 &[_][]const u8{ self.builder.cache_root, "options" },
241 ),
242 );
243
244 try fs.cwd().makePath(options_directory);
245
246 const options_file = try fs.path.join(
247 self.builder.allocator,
248 &[_][]const u8{ options_directory, &self.hashContentsToFileName() },
249 );
250
251 try fs.cwd().writeFile(options_file, self.contents.items);
252
253 self.generated_file.path = options_file;
254}
255
256fn hashContentsToFileName(self: *OptionsStep) [64]u8 {
257 // This implementation is copied from `WriteFileStep.make`
258
259 var hash = std.crypto.hash.blake2.Blake2b384.init(.{});
260
261 // Random bytes to make OptionsStep unique. Refresh this with
262 // new random bytes when OptionsStep implementation is modified
263 // in a non-backwards-compatible way.
264 hash.update("yL0Ya4KkmcCjBlP8");
265 hash.update(self.contents.items);
266
267 var digest: [48]u8 = undefined;
268 hash.final(&digest);
269 var hash_basename: [64]u8 = undefined;
270 _ = fs.base64_encoder.encode(&hash_basename, &digest);
271 return hash_basename;
272}
273
274const OptionArtifactArg = struct {
275 name: []const u8,
276 artifact: *CompileStep,
277};
278
279const OptionFileSourceArg = struct {
280 name: []const u8,
281 source: FileSource,
282};
283
284test "OptionsStep" {
285 if (builtin.os.tag == .wasi) return error.SkipZigTest;
286
287 var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
288 defer arena.deinit();
289
290 const host = try std.zig.system.NativeTargetInfo.detect(.{});
291
292 var builder = try std.Build.create(
293 arena.allocator(),
294 "test",
295 "test",
296 "test",
297 "test",
298 host,
299 );
300 defer builder.destroy();
301
302 const options = builder.addOptions();
303
304 // TODO this regressed at some point
305 //const KeywordEnum = enum {
306 // @"0.8.1",
307 //};
308
309 const nested_array = [2][2]u16{
310 [2]u16{ 300, 200 },
311 [2]u16{ 300, 200 },
312 };
313 const nested_slice: []const []const u16 = &[_][]const u16{ &nested_array[0], &nested_array[1] };
314
315 options.addOption(usize, "option1", 1);
316 options.addOption(?usize, "option2", null);
317 options.addOption(?usize, "option3", 3);
318 options.addOption(comptime_int, "option4", 4);
319 options.addOption([]const u8, "string", "zigisthebest");
320 options.addOption(?[]const u8, "optional_string", null);
321 options.addOption([2][2]u16, "nested_array", nested_array);
322 options.addOption([]const []const u16, "nested_slice", nested_slice);
323 //options.addOption(KeywordEnum, "keyword_enum", .@"0.8.1");
324 options.addOption(std.builtin.Version, "version", try std.builtin.Version.parse("0.1.2"));
325 options.addOption(std.SemanticVersion, "semantic_version", try std.SemanticVersion.parse("0.1.2-foo+bar"));
326
327 try std.testing.expectEqualStrings(
328 \\pub const option1: usize = 1;
329 \\pub const option2: ?usize = null;
330 \\pub const option3: ?usize = 3;
331 \\pub const option4: comptime_int = 4;
332 \\pub const string: []const u8 = "zigisthebest";
333 \\pub const optional_string: ?[]const u8 = null;
334 \\pub const nested_array: [2][2]u16 = [2][2]u16 {
335 \\ [2]u16 {
336 \\ 300,
337 \\ 200,
338 \\ },
339 \\ [2]u16 {
340 \\ 300,
341 \\ 200,
342 \\ },
343 \\};
344 \\pub const nested_slice: []const []const u16 = &[_][]const u16 {
345 \\ &[_]u16 {
346 \\ 300,
347 \\ 200,
348 \\ },
349 \\ &[_]u16 {
350 \\ 300,
351 \\ 200,
352 \\ },
353 \\};
354 //\\pub const KeywordEnum = enum {
355 //\\ @"0.8.1",
356 //\\};
357 //\\pub const keyword_enum: KeywordEnum = KeywordEnum.@"0.8.1";
358 \\pub const version: @import("std").builtin.Version = .{
359 \\ .major = 0,
360 \\ .minor = 1,
361 \\ .patch = 2,
362 \\};
363 \\pub const semantic_version: @import("std").SemanticVersion = .{
364 \\ .major = 0,
365 \\ .minor = 1,
366 \\ .patch = 2,
367 \\ .pre = "foo",
368 \\ .build = "bar",
369 \\};
370 \\
371 , options.contents.items);
372
373 _ = try std.zig.Ast.parse(arena.allocator(), try options.contents.toOwnedSliceSentinel(0), .zig);
374}
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+129
......@@ -0,0 +1,129 @@
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),
18out_basename: []const u8,
19target: CrossTarget,
20optimize: std.builtin.OptimizeMode,
21output_file: std.Build.GeneratedFile,
22
23pub const Options = struct {
24 source_file: std.Build.FileSource,
25 target: CrossTarget,
26 optimize: std.builtin.OptimizeMode,
27};
28
29pub fn create(builder: *std.Build, options: Options) *TranslateCStep {
30 const self = builder.allocator.create(TranslateCStep) catch @panic("OOM");
31 const source = options.source_file.dupe(builder);
32 self.* = TranslateCStep{
33 .step = Step.init(.translate_c, "translate-c", builder.allocator, make),
34 .builder = builder,
35 .source = source,
36 .include_dirs = std.ArrayList([]const u8).init(builder.allocator),
37 .c_macros = std.ArrayList([]const u8).init(builder.allocator),
38 .out_basename = undefined,
39 .target = options.target,
40 .optimize = options.optimize,
41 .output_file = std.Build.GeneratedFile{ .step = &self.step },
42 };
43 source.addStepDependencies(&self.step);
44 return self;
45}
46
47pub const AddExecutableOptions = struct {
48 name: ?[]const u8 = null,
49 version: ?std.builtin.Version = null,
50 target: ?CrossTarget = null,
51 optimize: ?std.builtin.Mode = null,
52 linkage: ?CompileStep.Linkage = null,
53};
54
55/// Creates a step to build an executable from the translated source.
56pub fn addExecutable(self: *TranslateCStep, options: AddExecutableOptions) *CompileStep {
57 return self.builder.addExecutable(.{
58 .root_source_file = .{ .generated = &self.output_file },
59 .name = options.name orelse "translated_c",
60 .version = options.version,
61 .target = options.target orelse self.target,
62 .optimize = options.optimize orelse self.optimize,
63 .linkage = options.linkage,
64 });
65}
66
67pub fn addIncludeDir(self: *TranslateCStep, include_dir: []const u8) void {
68 self.include_dirs.append(self.builder.dupePath(include_dir)) catch @panic("OOM");
69}
70
71pub fn addCheckFile(self: *TranslateCStep, expected_matches: []const []const u8) *CheckFileStep {
72 return CheckFileStep.create(self.builder, .{ .generated = &self.output_file }, self.builder.dupeStrings(expected_matches));
73}
74
75/// If the value is omitted, it is set to 1.
76/// `name` and `value` need not live longer than the function call.
77pub fn defineCMacro(self: *TranslateCStep, name: []const u8, value: ?[]const u8) void {
78 const macro = std.Build.constructCMacro(self.builder.allocator, name, value);
79 self.c_macros.append(macro) catch @panic("OOM");
80}
81
82/// name_and_value looks like [name]=[value]. If the value is omitted, it is set to 1.
83pub fn defineCMacroRaw(self: *TranslateCStep, name_and_value: []const u8) void {
84 self.c_macros.append(self.builder.dupe(name_and_value)) catch @panic("OOM");
85}
86
87fn make(step: *Step) !void {
88 const self = @fieldParentPtr(TranslateCStep, "step", step);
89
90 var argv_list = std.ArrayList([]const u8).init(self.builder.allocator);
91 try argv_list.append(self.builder.zig_exe);
92 try argv_list.append("translate-c");
93 try argv_list.append("-lc");
94
95 try argv_list.append("--enable-cache");
96
97 if (!self.target.isNative()) {
98 try argv_list.append("-target");
99 try argv_list.append(try self.target.zigTriple(self.builder.allocator));
100 }
101
102 switch (self.optimize) {
103 .Debug => {}, // Skip since it's the default.
104 else => try argv_list.append(self.builder.fmt("-O{s}", .{@tagName(self.optimize)})),
105 }
106
107 for (self.include_dirs.items) |include_dir| {
108 try argv_list.append("-I");
109 try argv_list.append(include_dir);
110 }
111
112 for (self.c_macros.items) |c_macro| {
113 try argv_list.append("-D");
114 try argv_list.append(c_macro);
115 }
116
117 try argv_list.append(self.source.getPath(self.builder));
118
119 const output_path_nl = try self.builder.execFromStep(argv_list.items, &self.step);
120 const output_path = mem.trimRight(u8, output_path_nl, "\r\n");
121
122 self.out_basename = fs.path.basename(output_path);
123 const output_dir = fs.path.dirname(output_path).?;
124
125 self.output_file.path = try fs.path.join(
126 self.builder.allocator,
127 &[_][]const u8{ output_dir, self.out_basename },
128 );
129}
lib/std/Build/WriteFileStep.zig created+113
......@@ -0,0 +1,113 @@
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,
12files: std.TailQueue(File),
13
14pub const File = struct {
15 source: std.Build.GeneratedFile,
16 basename: []const u8,
17 bytes: []const u8,
18};
19
20pub fn init(builder: *std.Build) WriteFileStep {
21 return WriteFileStep{
22 .builder = builder,
23 .step = Step.init(.write_file, "writefile", builder.allocator, make),
24 .files = .{},
25 };
26}
27
28pub fn add(self: *WriteFileStep, basename: []const u8, bytes: []const u8) void {
29 const node = self.builder.allocator.create(std.TailQueue(File).Node) catch @panic("unhandled error");
30 node.* = .{
31 .data = .{
32 .source = std.Build.GeneratedFile{ .step = &self.step },
33 .basename = self.builder.dupePath(basename),
34 .bytes = self.builder.dupe(bytes),
35 },
36 };
37
38 self.files.append(node);
39}
40
41/// Gets a file source for the given basename. If the file does not exist, returns `null`.
42pub fn getFileSource(step: *WriteFileStep, basename: []const u8) ?std.Build.FileSource {
43 var it = step.files.first;
44 while (it) |node| : (it = node.next) {
45 if (std.mem.eql(u8, node.data.basename, basename))
46 return std.Build.FileSource{ .generated = &node.data.source };
47 }
48 return null;
49}
50
51fn make(step: *Step) !void {
52 const self = @fieldParentPtr(WriteFileStep, "step", step);
53
54 // The cache is used here not really as a way to speed things up - because writing
55 // the data to a file would probably be very fast - but as a way to find a canonical
56 // location to put build artifacts.
57
58 // If, for example, a hard-coded path was used as the location to put WriteFileStep
59 // files, then two WriteFileSteps executing in parallel might clobber each other.
60
61 // TODO port the cache system from the compiler to zig std lib. Until then
62 // we directly construct the path, and no "cache hit" detection happens;
63 // the files are always written.
64 // Note there is similar code over in ConfigHeaderStep.
65 const Hasher = std.crypto.auth.siphash.SipHash128(1, 3);
66 // Random bytes to make WriteFileStep unique. Refresh this with
67 // new random bytes when WriteFileStep implementation is modified
68 // in a non-backwards-compatible way.
69 var hash = Hasher.init("eagVR1dYXoE7ARDP");
70
71 {
72 var it = self.files.first;
73 while (it) |node| : (it = node.next) {
74 hash.update(node.data.basename);
75 hash.update(node.data.bytes);
76 hash.update("|");
77 }
78 }
79 var digest: [16]u8 = undefined;
80 hash.final(&digest);
81 var hash_basename: [digest.len * 2]u8 = undefined;
82 _ = std.fmt.bufPrint(
83 &hash_basename,
84 "{s}",
85 .{std.fmt.fmtSliceHexLower(&digest)},
86 ) catch unreachable;
87
88 const output_dir = try fs.path.join(self.builder.allocator, &[_][]const u8{
89 self.builder.cache_root, "o", &hash_basename,
90 });
91 var dir = fs.cwd().makeOpenPath(output_dir, .{}) catch |err| {
92 std.debug.print("unable to make path {s}: {s}\n", .{ output_dir, @errorName(err) });
93 return err;
94 };
95 defer dir.close();
96 {
97 var it = self.files.first;
98 while (it) |node| : (it = node.next) {
99 dir.writeFile(node.data.basename, node.data.bytes) catch |err| {
100 std.debug.print("unable to write {s} into {s}: {s}\n", .{
101 node.data.basename,
102 output_dir,
103 @errorName(err),
104 });
105 return err;
106 };
107 node.data.source.path = try fs.path.join(
108 self.builder.allocator,
109 &[_][]const u8{ output_dir, node.data.basename },
110 );
111 }
112 }
113}
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/array_list.zig+28-4
......@@ -482,14 +482,14 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
482482
483483 /// Return the last element from the list.
484484 /// Asserts the list has at least one item.
485 pub fn getLast(self: *Self) T {
485 pub fn getLast(self: Self) T {
486486 const val = self.items[self.items.len - 1];
487487 return val;
488488 }
489489
490490 /// Return the last element from the list, or
491491 /// return `null` if list is empty.
492 pub fn getLastOrNull(self: *Self) ?T {
492 pub fn getLastOrNull(self: Self) ?T {
493493 if (self.items.len == 0) return null;
494494 return self.getLast();
495495 }
......@@ -961,14 +961,14 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
961961
962962 /// Return the last element from the list.
963963 /// Asserts the list has at least one item.
964 pub fn getLast(self: *Self) T {
964 pub fn getLast(self: Self) T {
965965 const val = self.items[self.items.len - 1];
966966 return val;
967967 }
968968
969969 /// Return the last element from the list, or
970970 /// return `null` if list is empty.
971 pub fn getLastOrNull(self: *Self) ?T {
971 pub fn getLastOrNull(self: Self) ?T {
972972 if (self.items.len == 0) return null;
973973 return self.getLast();
974974 }
......@@ -1719,3 +1719,27 @@ test "std.ArrayList(?u32).popOrNull()" {
17191719 try testing.expect(list.popOrNull().? == null);
17201720 try testing.expect(list.popOrNull() == null);
17211721}
1722
1723test "std.ArrayList(u32).getLast()" {
1724 const a = testing.allocator;
1725
1726 var list = ArrayList(u32).init(a);
1727 defer list.deinit();
1728
1729 try list.append(2);
1730 const const_list = list;
1731 try testing.expectEqual(const_list.getLast(), 2);
1732}
1733
1734test "std.ArrayList(u32).getLastOrNull()" {
1735 const a = testing.allocator;
1736
1737 var list = ArrayList(u32).init(a);
1738 defer list.deinit();
1739
1740 try testing.expectEqual(list.getLastOrNull(), null);
1741
1742 try list.append(2);
1743 const const_list = list;
1744 try testing.expectEqual(const_list.getLastOrNull().?, 2);
1745}
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-2111
......@@ -1,2111 +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.target.getObjectFormat() == .c) return false;
621 if (self.strip == true) return false;
622 return self.isDynamicLibrary() or self.kind == .exe or self.kind == .test_exe;
623}
624
625pub fn linkLibC(self: *LibExeObjStep) void {
626 self.is_linking_libc = true;
627}
628
629pub fn linkLibCpp(self: *LibExeObjStep) void {
630 self.is_linking_libcpp = true;
631}
632
633/// If the value is omitted, it is set to 1.
634/// `name` and `value` need not live longer than the function call.
635pub fn defineCMacro(self: *LibExeObjStep, name: []const u8, value: ?[]const u8) void {
636 const macro = std.build.constructCMacro(self.builder.allocator, name, value);
637 self.c_macros.append(macro) catch unreachable;
638}
639
640/// name_and_value looks like [name]=[value]. If the value is omitted, it is set to 1.
641pub fn defineCMacroRaw(self: *LibExeObjStep, name_and_value: []const u8) void {
642 self.c_macros.append(self.builder.dupe(name_and_value)) catch unreachable;
643}
644
645/// This one has no integration with anything, it just puts -lname on the command line.
646/// Prefer to use `linkSystemLibrary` instead.
647pub fn linkSystemLibraryName(self: *LibExeObjStep, name: []const u8) void {
648 self.link_objects.append(.{
649 .system_lib = .{
650 .name = self.builder.dupe(name),
651 .needed = false,
652 .weak = false,
653 .use_pkg_config = .no,
654 },
655 }) catch unreachable;
656}
657
658/// This one has no integration with anything, it just puts -needed-lname on the command line.
659/// Prefer to use `linkSystemLibraryNeeded` instead.
660pub fn linkSystemLibraryNeededName(self: *LibExeObjStep, name: []const u8) void {
661 self.link_objects.append(.{
662 .system_lib = .{
663 .name = self.builder.dupe(name),
664 .needed = true,
665 .weak = false,
666 .use_pkg_config = .no,
667 },
668 }) catch unreachable;
669}
670
671/// Darwin-only. This one has no integration with anything, it just puts -weak-lname on the
672/// command line. Prefer to use `linkSystemLibraryWeak` instead.
673pub fn linkSystemLibraryWeakName(self: *LibExeObjStep, name: []const u8) void {
674 self.link_objects.append(.{
675 .system_lib = .{
676 .name = self.builder.dupe(name),
677 .needed = false,
678 .weak = true,
679 .use_pkg_config = .no,
680 },
681 }) catch unreachable;
682}
683
684/// This links against a system library, exclusively using pkg-config to find the library.
685/// Prefer to use `linkSystemLibrary` instead.
686pub fn linkSystemLibraryPkgConfigOnly(self: *LibExeObjStep, lib_name: []const u8) void {
687 self.link_objects.append(.{
688 .system_lib = .{
689 .name = self.builder.dupe(lib_name),
690 .needed = false,
691 .weak = false,
692 .use_pkg_config = .force,
693 },
694 }) catch unreachable;
695}
696
697/// This links against a system library, exclusively using pkg-config to find the library.
698/// Prefer to use `linkSystemLibraryNeeded` instead.
699pub fn linkSystemLibraryNeededPkgConfigOnly(self: *LibExeObjStep, lib_name: []const u8) void {
700 self.link_objects.append(.{
701 .system_lib = .{
702 .name = self.builder.dupe(lib_name),
703 .needed = true,
704 .weak = false,
705 .use_pkg_config = .force,
706 },
707 }) catch unreachable;
708}
709
710/// Run pkg-config for the given library name and parse the output, returning the arguments
711/// that should be passed to zig to link the given library.
712pub fn runPkgConfig(self: *LibExeObjStep, lib_name: []const u8) ![]const []const u8 {
713 const pkg_name = match: {
714 // First we have to map the library name to pkg config name. Unfortunately,
715 // there are several examples where this is not straightforward:
716 // -lSDL2 -> pkg-config sdl2
717 // -lgdk-3 -> pkg-config gdk-3.0
718 // -latk-1.0 -> pkg-config atk
719 const pkgs = try getPkgConfigList(self.builder);
720
721 // Exact match means instant winner.
722 for (pkgs) |pkg| {
723 if (mem.eql(u8, pkg.name, lib_name)) {
724 break :match pkg.name;
725 }
726 }
727
728 // Next we'll try ignoring case.
729 for (pkgs) |pkg| {
730 if (std.ascii.eqlIgnoreCase(pkg.name, lib_name)) {
731 break :match pkg.name;
732 }
733 }
734
735 // Now try appending ".0".
736 for (pkgs) |pkg| {
737 if (std.ascii.indexOfIgnoreCase(pkg.name, lib_name)) |pos| {
738 if (pos != 0) continue;
739 if (mem.eql(u8, pkg.name[lib_name.len..], ".0")) {
740 break :match pkg.name;
741 }
742 }
743 }
744
745 // Trimming "-1.0".
746 if (mem.endsWith(u8, lib_name, "-1.0")) {
747 const trimmed_lib_name = lib_name[0 .. lib_name.len - "-1.0".len];
748 for (pkgs) |pkg| {
749 if (std.ascii.eqlIgnoreCase(pkg.name, trimmed_lib_name)) {
750 break :match pkg.name;
751 }
752 }
753 }
754
755 return error.PackageNotFound;
756 };
757
758 var code: u8 = undefined;
759 const stdout = if (self.builder.execAllowFail(&[_][]const u8{
760 "pkg-config",
761 pkg_name,
762 "--cflags",
763 "--libs",
764 }, &code, .Ignore)) |stdout| stdout else |err| switch (err) {
765 error.ProcessTerminated => return error.PkgConfigCrashed,
766 error.ExecNotSupported => return error.PkgConfigFailed,
767 error.ExitCodeFailure => return error.PkgConfigFailed,
768 error.FileNotFound => return error.PkgConfigNotInstalled,
769 error.ChildExecFailed => return error.PkgConfigFailed,
770 else => return err,
771 };
772
773 var zig_args = ArrayList([]const u8).init(self.builder.allocator);
774 defer zig_args.deinit();
775
776 var it = mem.tokenize(u8, stdout, " \r\n\t");
777 while (it.next()) |tok| {
778 if (mem.eql(u8, tok, "-I")) {
779 const dir = it.next() orelse return error.PkgConfigInvalidOutput;
780 try zig_args.appendSlice(&[_][]const u8{ "-I", dir });
781 } else if (mem.startsWith(u8, tok, "-I")) {
782 try zig_args.append(tok);
783 } else if (mem.eql(u8, tok, "-L")) {
784 const dir = it.next() orelse return error.PkgConfigInvalidOutput;
785 try zig_args.appendSlice(&[_][]const u8{ "-L", dir });
786 } else if (mem.startsWith(u8, tok, "-L")) {
787 try zig_args.append(tok);
788 } else if (mem.eql(u8, tok, "-l")) {
789 const lib = it.next() orelse return error.PkgConfigInvalidOutput;
790 try zig_args.appendSlice(&[_][]const u8{ "-l", lib });
791 } else if (mem.startsWith(u8, tok, "-l")) {
792 try zig_args.append(tok);
793 } else if (mem.eql(u8, tok, "-D")) {
794 const macro = it.next() orelse return error.PkgConfigInvalidOutput;
795 try zig_args.appendSlice(&[_][]const u8{ "-D", macro });
796 } else if (mem.startsWith(u8, tok, "-D")) {
797 try zig_args.append(tok);
798 } else if (self.builder.verbose) {
799 log.warn("Ignoring pkg-config flag '{s}'", .{tok});
800 }
801 }
802
803 return zig_args.toOwnedSlice();
804}
805
806pub fn linkSystemLibrary(self: *LibExeObjStep, name: []const u8) void {
807 self.linkSystemLibraryInner(name, .{});
808}
809
810pub fn linkSystemLibraryNeeded(self: *LibExeObjStep, name: []const u8) void {
811 self.linkSystemLibraryInner(name, .{ .needed = true });
812}
813
814pub fn linkSystemLibraryWeak(self: *LibExeObjStep, name: []const u8) void {
815 self.linkSystemLibraryInner(name, .{ .weak = true });
816}
817
818fn linkSystemLibraryInner(self: *LibExeObjStep, name: []const u8, opts: struct {
819 needed: bool = false,
820 weak: bool = false,
821}) void {
822 if (isLibCLibrary(name)) {
823 self.linkLibC();
824 return;
825 }
826 if (isLibCppLibrary(name)) {
827 self.linkLibCpp();
828 return;
829 }
830
831 self.link_objects.append(.{
832 .system_lib = .{
833 .name = self.builder.dupe(name),
834 .needed = opts.needed,
835 .weak = opts.weak,
836 .use_pkg_config = .yes,
837 },
838 }) catch unreachable;
839}
840
841pub fn setNamePrefix(self: *LibExeObjStep, text: []const u8) void {
842 assert(self.kind == .@"test" or self.kind == .test_exe);
843 self.name_prefix = self.builder.dupe(text);
844}
845
846pub fn setFilter(self: *LibExeObjStep, text: ?[]const u8) void {
847 assert(self.kind == .@"test" or self.kind == .test_exe);
848 self.filter = if (text) |t| self.builder.dupe(t) else null;
849}
850
851pub fn setTestRunner(self: *LibExeObjStep, path: ?[]const u8) void {
852 assert(self.kind == .@"test" or self.kind == .test_exe);
853 self.test_runner = if (path) |p| self.builder.dupePath(p) else null;
854}
855
856/// Handy when you have many C/C++ source files and want them all to have the same flags.
857pub fn addCSourceFiles(self: *LibExeObjStep, files: []const []const u8, flags: []const []const u8) void {
858 const c_source_files = self.builder.allocator.create(CSourceFiles) catch unreachable;
859
860 const files_copy = self.builder.dupeStrings(files);
861 const flags_copy = self.builder.dupeStrings(flags);
862
863 c_source_files.* = .{
864 .files = files_copy,
865 .flags = flags_copy,
866 };
867 self.link_objects.append(.{ .c_source_files = c_source_files }) catch unreachable;
868}
869
870pub fn addCSourceFile(self: *LibExeObjStep, file: []const u8, flags: []const []const u8) void {
871 self.addCSourceFileSource(.{
872 .args = flags,
873 .source = .{ .path = file },
874 });
875}
876
877pub fn addCSourceFileSource(self: *LibExeObjStep, source: CSourceFile) void {
878 const c_source_file = self.builder.allocator.create(CSourceFile) catch unreachable;
879 c_source_file.* = source.dupe(self.builder);
880 self.link_objects.append(.{ .c_source_file = c_source_file }) catch unreachable;
881 source.source.addStepDependencies(&self.step);
882}
883
884pub fn setVerboseLink(self: *LibExeObjStep, value: bool) void {
885 self.verbose_link = value;
886}
887
888pub fn setVerboseCC(self: *LibExeObjStep, value: bool) void {
889 self.verbose_cc = value;
890}
891
892pub fn setBuildMode(self: *LibExeObjStep, mode: std.builtin.Mode) void {
893 self.build_mode = mode;
894}
895
896pub fn overrideZigLibDir(self: *LibExeObjStep, dir_path: []const u8) void {
897 self.override_lib_dir = self.builder.dupePath(dir_path);
898}
899
900pub fn setMainPkgPath(self: *LibExeObjStep, dir_path: []const u8) void {
901 self.main_pkg_path = self.builder.dupePath(dir_path);
902}
903
904pub fn setLibCFile(self: *LibExeObjStep, libc_file: ?FileSource) void {
905 self.libc_file = if (libc_file) |f| f.dupe(self.builder) else null;
906}
907
908/// Returns the generated executable, library or object file.
909/// To run an executable built with zig build, use `run`, or create an install step and invoke it.
910pub fn getOutputSource(self: *LibExeObjStep) FileSource {
911 return FileSource{ .generated = &self.output_path_source };
912}
913
914/// Returns the generated import library. This function can only be called for libraries.
915pub fn getOutputLibSource(self: *LibExeObjStep) FileSource {
916 assert(self.kind == .lib);
917 return FileSource{ .generated = &self.output_lib_path_source };
918}
919
920/// Returns the generated header file.
921/// This function can only be called for libraries or object files which have `emit_h` set.
922pub fn getOutputHSource(self: *LibExeObjStep) FileSource {
923 assert(self.kind != .exe and self.kind != .test_exe and self.kind != .@"test");
924 assert(self.emit_h);
925 return FileSource{ .generated = &self.output_h_path_source };
926}
927
928/// Returns the generated PDB file. This function can only be called for Windows and UEFI.
929pub fn getOutputPdbSource(self: *LibExeObjStep) FileSource {
930 // TODO: Is this right? Isn't PDB for *any* PE/COFF file?
931 assert(self.target.isWindows() or self.target.isUefi());
932 return FileSource{ .generated = &self.output_pdb_path_source };
933}
934
935pub fn addAssemblyFile(self: *LibExeObjStep, path: []const u8) void {
936 self.link_objects.append(.{
937 .assembly_file = .{ .path = self.builder.dupe(path) },
938 }) catch unreachable;
939}
940
941pub fn addAssemblyFileSource(self: *LibExeObjStep, source: FileSource) void {
942 const source_duped = source.dupe(self.builder);
943 self.link_objects.append(.{ .assembly_file = source_duped }) catch unreachable;
944 source_duped.addStepDependencies(&self.step);
945}
946
947pub fn addObjectFile(self: *LibExeObjStep, source_file: []const u8) void {
948 self.addObjectFileSource(.{ .path = source_file });
949}
950
951pub fn addObjectFileSource(self: *LibExeObjStep, source: FileSource) void {
952 self.link_objects.append(.{ .static_path = source.dupe(self.builder) }) catch unreachable;
953 source.addStepDependencies(&self.step);
954}
955
956pub fn addObject(self: *LibExeObjStep, obj: *LibExeObjStep) void {
957 assert(obj.kind == .obj);
958 self.linkLibraryOrObject(obj);
959}
960
961pub const addSystemIncludeDir = @compileError("deprecated; use addSystemIncludePath");
962pub const addIncludeDir = @compileError("deprecated; use addIncludePath");
963pub const addLibPath = @compileError("deprecated, use addLibraryPath");
964pub const addFrameworkDir = @compileError("deprecated, use addFrameworkPath");
965
966pub fn addSystemIncludePath(self: *LibExeObjStep, path: []const u8) void {
967 self.include_dirs.append(IncludeDir{ .raw_path_system = self.builder.dupe(path) }) catch unreachable;
968}
969
970pub fn addIncludePath(self: *LibExeObjStep, path: []const u8) void {
971 self.include_dirs.append(IncludeDir{ .raw_path = self.builder.dupe(path) }) catch unreachable;
972}
973
974pub fn addConfigHeader(self: *LibExeObjStep, config_header: *ConfigHeaderStep) void {
975 self.step.dependOn(&config_header.step);
976 self.include_dirs.append(.{ .config_header_step = config_header }) catch @panic("OOM");
977}
978
979pub fn addLibraryPath(self: *LibExeObjStep, path: []const u8) void {
980 self.lib_paths.append(self.builder.dupe(path)) catch unreachable;
981}
982
983pub fn addRPath(self: *LibExeObjStep, path: []const u8) void {
984 self.rpaths.append(self.builder.dupe(path)) catch unreachable;
985}
986
987pub fn addFrameworkPath(self: *LibExeObjStep, dir_path: []const u8) void {
988 self.framework_dirs.append(self.builder.dupe(dir_path)) catch unreachable;
989}
990
991pub fn addPackage(self: *LibExeObjStep, package: Pkg) void {
992 self.packages.append(self.builder.dupePkg(package)) catch unreachable;
993 self.addRecursiveBuildDeps(package);
994}
995
996pub fn addOptions(self: *LibExeObjStep, package_name: []const u8, options: *OptionsStep) void {
997 self.addPackage(options.getPackage(package_name));
998}
999
1000fn addRecursiveBuildDeps(self: *LibExeObjStep, package: Pkg) void {
1001 package.source.addStepDependencies(&self.step);
1002 if (package.dependencies) |deps| {
1003 for (deps) |dep| {
1004 self.addRecursiveBuildDeps(dep);
1005 }
1006 }
1007}
1008
1009pub fn addPackagePath(self: *LibExeObjStep, name: []const u8, pkg_index_path: []const u8) void {
1010 self.addPackage(Pkg{
1011 .name = self.builder.dupe(name),
1012 .source = .{ .path = self.builder.dupe(pkg_index_path) },
1013 });
1014}
1015
1016/// If Vcpkg was found on the system, it will be added to include and lib
1017/// paths for the specified target.
1018pub fn addVcpkgPaths(self: *LibExeObjStep, linkage: LibExeObjStep.Linkage) !void {
1019 // Ideally in the Unattempted case we would call the function recursively
1020 // after findVcpkgRoot and have only one switch statement, but the compiler
1021 // cannot resolve the error set.
1022 switch (self.builder.vcpkg_root) {
1023 .unattempted => {
1024 self.builder.vcpkg_root = if (try findVcpkgRoot(self.builder.allocator)) |root|
1025 VcpkgRoot{ .found = root }
1026 else
1027 .not_found;
1028 },
1029 .not_found => return error.VcpkgNotFound,
1030 .found => {},
1031 }
1032
1033 switch (self.builder.vcpkg_root) {
1034 .unattempted => unreachable,
1035 .not_found => return error.VcpkgNotFound,
1036 .found => |root| {
1037 const allocator = self.builder.allocator;
1038 const triplet = try self.target.vcpkgTriplet(allocator, if (linkage == .static) .Static else .Dynamic);
1039 defer self.builder.allocator.free(triplet);
1040
1041 const include_path = self.builder.pathJoin(&.{ root, "installed", triplet, "include" });
1042 errdefer allocator.free(include_path);
1043 try self.include_dirs.append(IncludeDir{ .raw_path = include_path });
1044
1045 const lib_path = self.builder.pathJoin(&.{ root, "installed", triplet, "lib" });
1046 try self.lib_paths.append(lib_path);
1047
1048 self.vcpkg_bin_path = self.builder.pathJoin(&.{ root, "installed", triplet, "bin" });
1049 },
1050 }
1051}
1052
1053pub fn setExecCmd(self: *LibExeObjStep, args: []const ?[]const u8) void {
1054 assert(self.kind == .@"test");
1055 const duped_args = self.builder.allocator.alloc(?[]u8, args.len) catch unreachable;
1056 for (args) |arg, i| {
1057 duped_args[i] = if (arg) |a| self.builder.dupe(a) else null;
1058 }
1059 self.exec_cmd_args = duped_args;
1060}
1061
1062fn linkLibraryOrObject(self: *LibExeObjStep, other: *LibExeObjStep) void {
1063 self.step.dependOn(&other.step);
1064 self.link_objects.append(.{ .other_step = other }) catch unreachable;
1065 self.include_dirs.append(.{ .other_step = other }) catch unreachable;
1066}
1067
1068fn makePackageCmd(self: *LibExeObjStep, pkg: Pkg, zig_args: *ArrayList([]const u8)) error{OutOfMemory}!void {
1069 const builder = self.builder;
1070
1071 try zig_args.append("--pkg-begin");
1072 try zig_args.append(pkg.name);
1073 try zig_args.append(builder.pathFromRoot(pkg.source.getPath(self.builder)));
1074
1075 if (pkg.dependencies) |dependencies| {
1076 for (dependencies) |sub_pkg| {
1077 try self.makePackageCmd(sub_pkg, zig_args);
1078 }
1079 }
1080
1081 try zig_args.append("--pkg-end");
1082}
1083
1084fn make(step: *Step) !void {
1085 const self = @fieldParentPtr(LibExeObjStep, "step", step);
1086 const builder = self.builder;
1087
1088 if (self.root_src == null and self.link_objects.items.len == 0) {
1089 log.err("{s}: linker needs 1 or more objects to link", .{self.step.name});
1090 return error.NeedAnObject;
1091 }
1092
1093 var zig_args = ArrayList([]const u8).init(builder.allocator);
1094 defer zig_args.deinit();
1095
1096 zig_args.append(builder.zig_exe) catch unreachable;
1097
1098 const cmd = switch (self.kind) {
1099 .lib => "build-lib",
1100 .exe => "build-exe",
1101 .obj => "build-obj",
1102 .@"test" => "test",
1103 .test_exe => "test",
1104 };
1105 zig_args.append(cmd) catch unreachable;
1106
1107 if (builder.color != .auto) {
1108 try zig_args.append("--color");
1109 try zig_args.append(@tagName(builder.color));
1110 }
1111
1112 if (builder.reference_trace) |some| {
1113 try zig_args.append(try std.fmt.allocPrint(builder.allocator, "-freference-trace={d}", .{some}));
1114 }
1115
1116 try addFlag(&zig_args, "LLVM", self.use_llvm);
1117 try addFlag(&zig_args, "LLD", self.use_lld);
1118
1119 if (self.target.ofmt) |ofmt| {
1120 try zig_args.append(try std.fmt.allocPrint(builder.allocator, "-ofmt={s}", .{@tagName(ofmt)}));
1121 }
1122
1123 if (self.entry_symbol_name) |entry| {
1124 try zig_args.append("--entry");
1125 try zig_args.append(entry);
1126 }
1127
1128 if (self.stack_size) |stack_size| {
1129 try zig_args.append("--stack");
1130 try zig_args.append(try std.fmt.allocPrint(builder.allocator, "{}", .{stack_size}));
1131 }
1132
1133 if (self.root_src) |root_src| try zig_args.append(root_src.getPath(builder));
1134
1135 // We will add link objects from transitive dependencies, but we want to keep
1136 // all link objects in the same order provided.
1137 // This array is used to keep self.link_objects immutable.
1138 var transitive_deps: TransitiveDeps = .{
1139 .link_objects = ArrayList(LinkObject).init(builder.allocator),
1140 .seen_system_libs = StringHashMap(void).init(builder.allocator),
1141 .seen_steps = std.AutoHashMap(*const Step, void).init(builder.allocator),
1142 .is_linking_libcpp = self.is_linking_libcpp,
1143 .is_linking_libc = self.is_linking_libc,
1144 .frameworks = &self.frameworks,
1145 };
1146
1147 try transitive_deps.seen_steps.put(&self.step, {});
1148 try transitive_deps.add(self.link_objects.items);
1149
1150 var prev_has_extra_flags = false;
1151
1152 for (transitive_deps.link_objects.items) |link_object| {
1153 switch (link_object) {
1154 .static_path => |static_path| try zig_args.append(static_path.getPath(builder)),
1155
1156 .other_step => |other| switch (other.kind) {
1157 .exe => @panic("Cannot link with an executable build artifact"),
1158 .test_exe => @panic("Cannot link with an executable build artifact"),
1159 .@"test" => @panic("Cannot link with a test"),
1160 .obj => {
1161 try zig_args.append(other.getOutputSource().getPath(builder));
1162 },
1163 .lib => l: {
1164 if (self.isStaticLibrary() and other.isStaticLibrary()) {
1165 // Avoid putting a static library inside a static library.
1166 break :l;
1167 }
1168
1169 const full_path_lib = other.getOutputLibSource().getPath(builder);
1170 try zig_args.append(full_path_lib);
1171
1172 if (other.linkage == Linkage.dynamic and !self.target.isWindows()) {
1173 if (fs.path.dirname(full_path_lib)) |dirname| {
1174 try zig_args.append("-rpath");
1175 try zig_args.append(dirname);
1176 }
1177 }
1178 },
1179 },
1180
1181 .system_lib => |system_lib| {
1182 const prefix: []const u8 = prefix: {
1183 if (system_lib.needed) break :prefix "-needed-l";
1184 if (system_lib.weak) {
1185 if (self.target.isDarwin()) break :prefix "-weak-l";
1186 log.warn("Weak library import used for a non-darwin target, this will be converted to normally library import `-lname`", .{});
1187 }
1188 break :prefix "-l";
1189 };
1190 switch (system_lib.use_pkg_config) {
1191 .no => try zig_args.append(builder.fmt("{s}{s}", .{ prefix, system_lib.name })),
1192 .yes, .force => {
1193 if (self.runPkgConfig(system_lib.name)) |args| {
1194 try zig_args.appendSlice(args);
1195 } else |err| switch (err) {
1196 error.PkgConfigInvalidOutput,
1197 error.PkgConfigCrashed,
1198 error.PkgConfigFailed,
1199 error.PkgConfigNotInstalled,
1200 error.PackageNotFound,
1201 => switch (system_lib.use_pkg_config) {
1202 .yes => {
1203 // pkg-config failed, so fall back to linking the library
1204 // by name directly.
1205 try zig_args.append(builder.fmt("{s}{s}", .{
1206 prefix,
1207 system_lib.name,
1208 }));
1209 },
1210 .force => {
1211 panic("pkg-config failed for library {s}", .{system_lib.name});
1212 },
1213 .no => unreachable,
1214 },
1215
1216 else => |e| return e,
1217 }
1218 },
1219 }
1220 },
1221
1222 .assembly_file => |asm_file| {
1223 if (prev_has_extra_flags) {
1224 try zig_args.append("-extra-cflags");
1225 try zig_args.append("--");
1226 prev_has_extra_flags = false;
1227 }
1228 try zig_args.append(asm_file.getPath(builder));
1229 },
1230
1231 .c_source_file => |c_source_file| {
1232 if (c_source_file.args.len == 0) {
1233 if (prev_has_extra_flags) {
1234 try zig_args.append("-cflags");
1235 try zig_args.append("--");
1236 prev_has_extra_flags = false;
1237 }
1238 } else {
1239 try zig_args.append("-cflags");
1240 for (c_source_file.args) |arg| {
1241 try zig_args.append(arg);
1242 }
1243 try zig_args.append("--");
1244 }
1245 try zig_args.append(c_source_file.source.getPath(builder));
1246 },
1247
1248 .c_source_files => |c_source_files| {
1249 if (c_source_files.flags.len == 0) {
1250 if (prev_has_extra_flags) {
1251 try zig_args.append("-cflags");
1252 try zig_args.append("--");
1253 prev_has_extra_flags = false;
1254 }
1255 } else {
1256 try zig_args.append("-cflags");
1257 for (c_source_files.flags) |flag| {
1258 try zig_args.append(flag);
1259 }
1260 try zig_args.append("--");
1261 }
1262 for (c_source_files.files) |file| {
1263 try zig_args.append(builder.pathFromRoot(file));
1264 }
1265 },
1266 }
1267 }
1268
1269 if (transitive_deps.is_linking_libcpp) {
1270 try zig_args.append("-lc++");
1271 }
1272
1273 if (transitive_deps.is_linking_libc) {
1274 try zig_args.append("-lc");
1275 }
1276
1277 if (self.image_base) |image_base| {
1278 try zig_args.append("--image-base");
1279 try zig_args.append(builder.fmt("0x{x}", .{image_base}));
1280 }
1281
1282 if (self.filter) |filter| {
1283 try zig_args.append("--test-filter");
1284 try zig_args.append(filter);
1285 }
1286
1287 if (self.test_evented_io) {
1288 try zig_args.append("--test-evented-io");
1289 }
1290
1291 if (self.name_prefix.len != 0) {
1292 try zig_args.append("--test-name-prefix");
1293 try zig_args.append(self.name_prefix);
1294 }
1295
1296 if (self.test_runner) |test_runner| {
1297 try zig_args.append("--test-runner");
1298 try zig_args.append(builder.pathFromRoot(test_runner));
1299 }
1300
1301 for (builder.debug_log_scopes) |log_scope| {
1302 try zig_args.append("--debug-log");
1303 try zig_args.append(log_scope);
1304 }
1305
1306 if (builder.debug_compile_errors) {
1307 try zig_args.append("--debug-compile-errors");
1308 }
1309
1310 if (builder.verbose_cimport) zig_args.append("--verbose-cimport") catch unreachable;
1311 if (builder.verbose_air) zig_args.append("--verbose-air") catch unreachable;
1312 if (builder.verbose_llvm_ir) zig_args.append("--verbose-llvm-ir") catch unreachable;
1313 if (builder.verbose_link or self.verbose_link) zig_args.append("--verbose-link") catch unreachable;
1314 if (builder.verbose_cc or self.verbose_cc) zig_args.append("--verbose-cc") catch unreachable;
1315 if (builder.verbose_llvm_cpu_features) zig_args.append("--verbose-llvm-cpu-features") catch unreachable;
1316
1317 if (self.emit_analysis.getArg(builder, "emit-analysis")) |arg| try zig_args.append(arg);
1318 if (self.emit_asm.getArg(builder, "emit-asm")) |arg| try zig_args.append(arg);
1319 if (self.emit_bin.getArg(builder, "emit-bin")) |arg| try zig_args.append(arg);
1320 if (self.emit_docs.getArg(builder, "emit-docs")) |arg| try zig_args.append(arg);
1321 if (self.emit_implib.getArg(builder, "emit-implib")) |arg| try zig_args.append(arg);
1322 if (self.emit_llvm_bc.getArg(builder, "emit-llvm-bc")) |arg| try zig_args.append(arg);
1323 if (self.emit_llvm_ir.getArg(builder, "emit-llvm-ir")) |arg| try zig_args.append(arg);
1324
1325 if (self.emit_h) try zig_args.append("-femit-h");
1326
1327 try addFlag(&zig_args, "strip", self.strip);
1328 try addFlag(&zig_args, "unwind-tables", self.unwind_tables);
1329
1330 switch (self.compress_debug_sections) {
1331 .none => {},
1332 .zlib => try zig_args.append("--compress-debug-sections=zlib"),
1333 }
1334
1335 if (self.link_eh_frame_hdr) {
1336 try zig_args.append("--eh-frame-hdr");
1337 }
1338 if (self.link_emit_relocs) {
1339 try zig_args.append("--emit-relocs");
1340 }
1341 if (self.link_function_sections) {
1342 try zig_args.append("-ffunction-sections");
1343 }
1344 if (self.link_gc_sections) |x| {
1345 try zig_args.append(if (x) "--gc-sections" else "--no-gc-sections");
1346 }
1347 if (self.linker_allow_shlib_undefined) |x| {
1348 try zig_args.append(if (x) "-fallow-shlib-undefined" else "-fno-allow-shlib-undefined");
1349 }
1350 if (self.link_z_notext) {
1351 try zig_args.append("-z");
1352 try zig_args.append("notext");
1353 }
1354 if (!self.link_z_relro) {
1355 try zig_args.append("-z");
1356 try zig_args.append("norelro");
1357 }
1358 if (self.link_z_lazy) {
1359 try zig_args.append("-z");
1360 try zig_args.append("lazy");
1361 }
1362 if (self.link_z_common_page_size) |size| {
1363 try zig_args.append("-z");
1364 try zig_args.append(builder.fmt("common-page-size={d}", .{size}));
1365 }
1366 if (self.link_z_max_page_size) |size| {
1367 try zig_args.append("-z");
1368 try zig_args.append(builder.fmt("max-page-size={d}", .{size}));
1369 }
1370
1371 if (self.libc_file) |libc_file| {
1372 try zig_args.append("--libc");
1373 try zig_args.append(libc_file.getPath(builder));
1374 } else if (builder.libc_file) |libc_file| {
1375 try zig_args.append("--libc");
1376 try zig_args.append(libc_file);
1377 }
1378
1379 switch (self.build_mode) {
1380 .Debug => {}, // Skip since it's the default.
1381 else => zig_args.append(builder.fmt("-O{s}", .{@tagName(self.build_mode)})) catch unreachable,
1382 }
1383
1384 try zig_args.append("--cache-dir");
1385 try zig_args.append(builder.pathFromRoot(builder.cache_root));
1386
1387 try zig_args.append("--global-cache-dir");
1388 try zig_args.append(builder.pathFromRoot(builder.global_cache_root));
1389
1390 zig_args.append("--name") catch unreachable;
1391 zig_args.append(self.name) catch unreachable;
1392
1393 if (self.linkage) |some| switch (some) {
1394 .dynamic => try zig_args.append("-dynamic"),
1395 .static => try zig_args.append("-static"),
1396 };
1397 if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic) {
1398 if (self.version) |version| {
1399 zig_args.append("--version") catch unreachable;
1400 zig_args.append(builder.fmt("{}", .{version})) catch unreachable;
1401 }
1402
1403 if (self.target.isDarwin()) {
1404 const install_name = self.install_name orelse builder.fmt("@rpath/{s}{s}{s}", .{
1405 self.target.libPrefix(),
1406 self.name,
1407 self.target.dynamicLibSuffix(),
1408 });
1409 try zig_args.append("-install_name");
1410 try zig_args.append(install_name);
1411 }
1412 }
1413
1414 if (self.entitlements) |entitlements| {
1415 try zig_args.appendSlice(&[_][]const u8{ "--entitlements", entitlements });
1416 }
1417 if (self.pagezero_size) |pagezero_size| {
1418 const size = try std.fmt.allocPrint(builder.allocator, "{x}", .{pagezero_size});
1419 try zig_args.appendSlice(&[_][]const u8{ "-pagezero_size", size });
1420 }
1421 if (self.search_strategy) |strat| switch (strat) {
1422 .paths_first => try zig_args.append("-search_paths_first"),
1423 .dylibs_first => try zig_args.append("-search_dylibs_first"),
1424 };
1425 if (self.headerpad_size) |headerpad_size| {
1426 const size = try std.fmt.allocPrint(builder.allocator, "{x}", .{headerpad_size});
1427 try zig_args.appendSlice(&[_][]const u8{ "-headerpad", size });
1428 }
1429 if (self.headerpad_max_install_names) {
1430 try zig_args.append("-headerpad_max_install_names");
1431 }
1432 if (self.dead_strip_dylibs) {
1433 try zig_args.append("-dead_strip_dylibs");
1434 }
1435
1436 try addFlag(&zig_args, "compiler-rt", self.bundle_compiler_rt);
1437 try addFlag(&zig_args, "single-threaded", self.single_threaded);
1438 if (self.disable_stack_probing) {
1439 try zig_args.append("-fno-stack-check");
1440 }
1441 try addFlag(&zig_args, "stack-protector", self.stack_protector);
1442 if (self.red_zone) |red_zone| {
1443 if (red_zone) {
1444 try zig_args.append("-mred-zone");
1445 } else {
1446 try zig_args.append("-mno-red-zone");
1447 }
1448 }
1449 try addFlag(&zig_args, "omit-frame-pointer", self.omit_frame_pointer);
1450 try addFlag(&zig_args, "dll-export-fns", self.dll_export_fns);
1451
1452 if (self.disable_sanitize_c) {
1453 try zig_args.append("-fno-sanitize-c");
1454 }
1455 if (self.sanitize_thread) {
1456 try zig_args.append("-fsanitize-thread");
1457 }
1458 if (self.rdynamic) {
1459 try zig_args.append("-rdynamic");
1460 }
1461 if (self.import_memory) {
1462 try zig_args.append("--import-memory");
1463 }
1464 if (self.import_symbols) {
1465 try zig_args.append("--import-symbols");
1466 }
1467 if (self.import_table) {
1468 try zig_args.append("--import-table");
1469 }
1470 if (self.export_table) {
1471 try zig_args.append("--export-table");
1472 }
1473 if (self.initial_memory) |initial_memory| {
1474 try zig_args.append(builder.fmt("--initial-memory={d}", .{initial_memory}));
1475 }
1476 if (self.max_memory) |max_memory| {
1477 try zig_args.append(builder.fmt("--max-memory={d}", .{max_memory}));
1478 }
1479 if (self.shared_memory) {
1480 try zig_args.append("--shared-memory");
1481 }
1482 if (self.global_base) |global_base| {
1483 try zig_args.append(builder.fmt("--global-base={d}", .{global_base}));
1484 }
1485
1486 if (self.code_model != .default) {
1487 try zig_args.append("-mcmodel");
1488 try zig_args.append(@tagName(self.code_model));
1489 }
1490 if (self.wasi_exec_model) |model| {
1491 try zig_args.append(builder.fmt("-mexec-model={s}", .{@tagName(model)}));
1492 }
1493 for (self.export_symbol_names) |symbol_name| {
1494 try zig_args.append(builder.fmt("--export={s}", .{symbol_name}));
1495 }
1496
1497 if (!self.target.isNative()) {
1498 try zig_args.append("-target");
1499 try zig_args.append(try self.target.zigTriple(builder.allocator));
1500
1501 // TODO this logic can disappear if cpu model + features becomes part of the target triple
1502 const cross = self.target.toTarget();
1503 const all_features = cross.cpu.arch.allFeaturesList();
1504 var populated_cpu_features = cross.cpu.model.features;
1505 populated_cpu_features.populateDependencies(all_features);
1506
1507 if (populated_cpu_features.eql(cross.cpu.features)) {
1508 // The CPU name alone is sufficient.
1509 try zig_args.append("-mcpu");
1510 try zig_args.append(cross.cpu.model.name);
1511 } else {
1512 var mcpu_buffer = ArrayList(u8).init(builder.allocator);
1513
1514 try mcpu_buffer.writer().print("-mcpu={s}", .{cross.cpu.model.name});
1515
1516 for (all_features) |feature, i_usize| {
1517 const i = @intCast(std.Target.Cpu.Feature.Set.Index, i_usize);
1518 const in_cpu_set = populated_cpu_features.isEnabled(i);
1519 const in_actual_set = cross.cpu.features.isEnabled(i);
1520 if (in_cpu_set and !in_actual_set) {
1521 try mcpu_buffer.writer().print("-{s}", .{feature.name});
1522 } else if (!in_cpu_set and in_actual_set) {
1523 try mcpu_buffer.writer().print("+{s}", .{feature.name});
1524 }
1525 }
1526
1527 try zig_args.append(try mcpu_buffer.toOwnedSlice());
1528 }
1529
1530 if (self.target.dynamic_linker.get()) |dynamic_linker| {
1531 try zig_args.append("--dynamic-linker");
1532 try zig_args.append(dynamic_linker);
1533 }
1534 }
1535
1536 if (self.linker_script) |linker_script| {
1537 try zig_args.append("--script");
1538 try zig_args.append(linker_script.getPath(builder));
1539 }
1540
1541 if (self.version_script) |version_script| {
1542 try zig_args.append("--version-script");
1543 try zig_args.append(builder.pathFromRoot(version_script));
1544 }
1545
1546 if (self.kind == .@"test") {
1547 if (self.exec_cmd_args) |exec_cmd_args| {
1548 for (exec_cmd_args) |cmd_arg| {
1549 if (cmd_arg) |arg| {
1550 try zig_args.append("--test-cmd");
1551 try zig_args.append(arg);
1552 } else {
1553 try zig_args.append("--test-cmd-bin");
1554 }
1555 }
1556 } else {
1557 const need_cross_glibc = self.target.isGnuLibC() and transitive_deps.is_linking_libc;
1558
1559 switch (builder.host.getExternalExecutor(self.target_info, .{
1560 .qemu_fixes_dl = need_cross_glibc and builder.glibc_runtimes_dir != null,
1561 .link_libc = transitive_deps.is_linking_libc,
1562 })) {
1563 .native => {},
1564 .bad_dl, .bad_os_or_cpu => {
1565 try zig_args.append("--test-no-exec");
1566 },
1567 .rosetta => if (builder.enable_rosetta) {
1568 try zig_args.append("--test-cmd-bin");
1569 } else {
1570 try zig_args.append("--test-no-exec");
1571 },
1572 .qemu => |bin_name| ok: {
1573 if (builder.enable_qemu) qemu: {
1574 const glibc_dir_arg = if (need_cross_glibc)
1575 builder.glibc_runtimes_dir orelse break :qemu
1576 else
1577 null;
1578 try zig_args.append("--test-cmd");
1579 try zig_args.append(bin_name);
1580 if (glibc_dir_arg) |dir| {
1581 // TODO look into making this a call to `linuxTriple`. This
1582 // needs the directory to be called "i686" rather than
1583 // "x86" which is why we do it manually here.
1584 const fmt_str = "{s}" ++ fs.path.sep_str ++ "{s}-{s}-{s}";
1585 const cpu_arch = self.target.getCpuArch();
1586 const os_tag = self.target.getOsTag();
1587 const abi = self.target.getAbi();
1588 const cpu_arch_name: []const u8 = if (cpu_arch == .x86)
1589 "i686"
1590 else
1591 @tagName(cpu_arch);
1592 const full_dir = try std.fmt.allocPrint(builder.allocator, fmt_str, .{
1593 dir, cpu_arch_name, @tagName(os_tag), @tagName(abi),
1594 });
1595
1596 try zig_args.append("--test-cmd");
1597 try zig_args.append("-L");
1598 try zig_args.append("--test-cmd");
1599 try zig_args.append(full_dir);
1600 }
1601 try zig_args.append("--test-cmd-bin");
1602 break :ok;
1603 }
1604 try zig_args.append("--test-no-exec");
1605 },
1606 .wine => |bin_name| if (builder.enable_wine) {
1607 try zig_args.append("--test-cmd");
1608 try zig_args.append(bin_name);
1609 try zig_args.append("--test-cmd-bin");
1610 } else {
1611 try zig_args.append("--test-no-exec");
1612 },
1613 .wasmtime => |bin_name| if (builder.enable_wasmtime) {
1614 try zig_args.append("--test-cmd");
1615 try zig_args.append(bin_name);
1616 try zig_args.append("--test-cmd");
1617 try zig_args.append("--dir=.");
1618 try zig_args.append("--test-cmd-bin");
1619 } else {
1620 try zig_args.append("--test-no-exec");
1621 },
1622 .darling => |bin_name| if (builder.enable_darling) {
1623 try zig_args.append("--test-cmd");
1624 try zig_args.append(bin_name);
1625 try zig_args.append("--test-cmd-bin");
1626 } else {
1627 try zig_args.append("--test-no-exec");
1628 },
1629 }
1630 }
1631 } else if (self.kind == .test_exe) {
1632 try zig_args.append("--test-no-exec");
1633 }
1634
1635 for (self.packages.items) |pkg| {
1636 try self.makePackageCmd(pkg, &zig_args);
1637 }
1638
1639 for (self.include_dirs.items) |include_dir| {
1640 switch (include_dir) {
1641 .raw_path => |include_path| {
1642 try zig_args.append("-I");
1643 try zig_args.append(builder.pathFromRoot(include_path));
1644 },
1645 .raw_path_system => |include_path| {
1646 if (builder.sysroot != null) {
1647 try zig_args.append("-iwithsysroot");
1648 } else {
1649 try zig_args.append("-isystem");
1650 }
1651
1652 const resolved_include_path = builder.pathFromRoot(include_path);
1653
1654 const common_include_path = if (builtin.os.tag == .windows and builder.sysroot != null and fs.path.isAbsolute(resolved_include_path)) blk: {
1655 // We need to check for disk designator and strip it out from dir path so
1656 // that zig/clang can concat resolved_include_path with sysroot.
1657 const disk_designator = fs.path.diskDesignatorWindows(resolved_include_path);
1658
1659 if (mem.indexOf(u8, resolved_include_path, disk_designator)) |where| {
1660 break :blk resolved_include_path[where + disk_designator.len ..];
1661 }
1662
1663 break :blk resolved_include_path;
1664 } else resolved_include_path;
1665
1666 try zig_args.append(common_include_path);
1667 },
1668 .other_step => |other| {
1669 if (other.emit_h) {
1670 const h_path = other.getOutputHSource().getPath(builder);
1671 try zig_args.append("-isystem");
1672 try zig_args.append(fs.path.dirname(h_path).?);
1673 }
1674 if (other.installed_headers.items.len > 0) {
1675 for (other.installed_headers.items) |install_step| {
1676 try install_step.make();
1677 }
1678 try zig_args.append("-I");
1679 try zig_args.append(builder.pathJoin(&.{
1680 other.builder.install_prefix, "include",
1681 }));
1682 }
1683 },
1684 .config_header_step => |config_header| {
1685 try zig_args.append("-I");
1686 try zig_args.append(config_header.output_dir);
1687 },
1688 }
1689 }
1690
1691 for (self.lib_paths.items) |lib_path| {
1692 try zig_args.append("-L");
1693 try zig_args.append(lib_path);
1694 }
1695
1696 for (self.rpaths.items) |rpath| {
1697 try zig_args.append("-rpath");
1698 try zig_args.append(rpath);
1699 }
1700
1701 for (self.c_macros.items) |c_macro| {
1702 try zig_args.append("-D");
1703 try zig_args.append(c_macro);
1704 }
1705
1706 if (self.target.isDarwin()) {
1707 for (self.framework_dirs.items) |dir| {
1708 if (builder.sysroot != null) {
1709 try zig_args.append("-iframeworkwithsysroot");
1710 } else {
1711 try zig_args.append("-iframework");
1712 }
1713 try zig_args.append(dir);
1714 try zig_args.append("-F");
1715 try zig_args.append(dir);
1716 }
1717
1718 var it = self.frameworks.iterator();
1719 while (it.next()) |entry| {
1720 const name = entry.key_ptr.*;
1721 const info = entry.value_ptr.*;
1722 if (info.needed) {
1723 zig_args.append("-needed_framework") catch unreachable;
1724 } else if (info.weak) {
1725 zig_args.append("-weak_framework") catch unreachable;
1726 } else {
1727 zig_args.append("-framework") catch unreachable;
1728 }
1729 zig_args.append(name) catch unreachable;
1730 }
1731 } else {
1732 if (self.framework_dirs.items.len > 0) {
1733 log.info("Framework directories have been added for a non-darwin target, this will have no affect on the build", .{});
1734 }
1735
1736 if (self.frameworks.count() > 0) {
1737 log.info("Frameworks have been added for a non-darwin target, this will have no affect on the build", .{});
1738 }
1739 }
1740
1741 if (builder.sysroot) |sysroot| {
1742 try zig_args.appendSlice(&[_][]const u8{ "--sysroot", sysroot });
1743 }
1744
1745 for (builder.search_prefixes.items) |search_prefix| {
1746 try zig_args.append("-L");
1747 try zig_args.append(builder.pathJoin(&.{
1748 search_prefix, "lib",
1749 }));
1750 try zig_args.append("-I");
1751 try zig_args.append(builder.pathJoin(&.{
1752 search_prefix, "include",
1753 }));
1754 }
1755
1756 try addFlag(&zig_args, "valgrind", self.valgrind_support);
1757 try addFlag(&zig_args, "each-lib-rpath", self.each_lib_rpath);
1758 try addFlag(&zig_args, "build-id", self.build_id);
1759
1760 if (self.override_lib_dir) |dir| {
1761 try zig_args.append("--zig-lib-dir");
1762 try zig_args.append(builder.pathFromRoot(dir));
1763 } else if (builder.override_lib_dir) |dir| {
1764 try zig_args.append("--zig-lib-dir");
1765 try zig_args.append(builder.pathFromRoot(dir));
1766 }
1767
1768 if (self.main_pkg_path) |dir| {
1769 try zig_args.append("--main-pkg-path");
1770 try zig_args.append(builder.pathFromRoot(dir));
1771 }
1772
1773 try addFlag(&zig_args, "PIC", self.force_pic);
1774 try addFlag(&zig_args, "PIE", self.pie);
1775 try addFlag(&zig_args, "lto", self.want_lto);
1776
1777 if (self.subsystem) |subsystem| {
1778 try zig_args.append("--subsystem");
1779 try zig_args.append(switch (subsystem) {
1780 .Console => "console",
1781 .Windows => "windows",
1782 .Posix => "posix",
1783 .Native => "native",
1784 .EfiApplication => "efi_application",
1785 .EfiBootServiceDriver => "efi_boot_service_driver",
1786 .EfiRom => "efi_rom",
1787 .EfiRuntimeDriver => "efi_runtime_driver",
1788 });
1789 }
1790
1791 try zig_args.append("--enable-cache");
1792
1793 // Windows has an argument length limit of 32,766 characters, macOS 262,144 and Linux
1794 // 2,097,152. If our args exceed 30 KiB, we instead write them to a "response file" and
1795 // pass that to zig, e.g. via 'zig build-lib @args.rsp'
1796 // See @file syntax here: https://gcc.gnu.org/onlinedocs/gcc/Overall-Options.html
1797 var args_length: usize = 0;
1798 for (zig_args.items) |arg| {
1799 args_length += arg.len + 1; // +1 to account for null terminator
1800 }
1801 if (args_length >= 30 * 1024) {
1802 const args_dir = try fs.path.join(
1803 builder.allocator,
1804 &[_][]const u8{ builder.pathFromRoot("zig-cache"), "args" },
1805 );
1806 try std.fs.cwd().makePath(args_dir);
1807
1808 var args_arena = std.heap.ArenaAllocator.init(builder.allocator);
1809 defer args_arena.deinit();
1810
1811 const args_to_escape = zig_args.items[2..];
1812 var escaped_args = try ArrayList([]const u8).initCapacity(args_arena.allocator(), args_to_escape.len);
1813
1814 arg_blk: for (args_to_escape) |arg| {
1815 for (arg) |c, arg_idx| {
1816 if (c == '\\' or c == '"') {
1817 // Slow path for arguments that need to be escaped. We'll need to allocate and copy
1818 var escaped = try ArrayList(u8).initCapacity(args_arena.allocator(), arg.len + 1);
1819 const writer = escaped.writer();
1820 writer.writeAll(arg[0..arg_idx]) catch unreachable;
1821 for (arg[arg_idx..]) |to_escape| {
1822 if (to_escape == '\\' or to_escape == '"') try writer.writeByte('\\');
1823 try writer.writeByte(to_escape);
1824 }
1825 escaped_args.appendAssumeCapacity(escaped.items);
1826 continue :arg_blk;
1827 }
1828 }
1829 escaped_args.appendAssumeCapacity(arg); // no escaping needed so just use original argument
1830 }
1831
1832 // Write the args to zig-cache/args/<SHA256 hash of args> to avoid conflicts with
1833 // other zig build commands running in parallel.
1834 const partially_quoted = try std.mem.join(builder.allocator, "\" \"", escaped_args.items);
1835 const args = try std.mem.concat(builder.allocator, u8, &[_][]const u8{ "\"", partially_quoted, "\"" });
1836
1837 var args_hash: [Sha256.digest_length]u8 = undefined;
1838 Sha256.hash(args, &args_hash, .{});
1839 var args_hex_hash: [Sha256.digest_length * 2]u8 = undefined;
1840 _ = try std.fmt.bufPrint(
1841 &args_hex_hash,
1842 "{s}",
1843 .{std.fmt.fmtSliceHexLower(&args_hash)},
1844 );
1845
1846 const args_file = try fs.path.join(builder.allocator, &[_][]const u8{ args_dir, args_hex_hash[0..] });
1847 try std.fs.cwd().writeFile(args_file, args);
1848
1849 zig_args.shrinkRetainingCapacity(2);
1850 try zig_args.append(try std.mem.concat(builder.allocator, u8, &[_][]const u8{ "@", args_file }));
1851 }
1852
1853 const output_dir_nl = try builder.execFromStep(zig_args.items, &self.step);
1854 const build_output_dir = mem.trimRight(u8, output_dir_nl, "\r\n");
1855
1856 if (self.output_dir) |output_dir| {
1857 var src_dir = try std.fs.cwd().openIterableDir(build_output_dir, .{});
1858 defer src_dir.close();
1859
1860 // Create the output directory if it doesn't exist.
1861 try std.fs.cwd().makePath(output_dir);
1862
1863 var dest_dir = try std.fs.cwd().openDir(output_dir, .{});
1864 defer dest_dir.close();
1865
1866 var it = src_dir.iterate();
1867 while (try it.next()) |entry| {
1868 // The compiler can put these files into the same directory, but we don't
1869 // want to copy them over.
1870 if (mem.eql(u8, entry.name, "llvm-ar.id") or
1871 mem.eql(u8, entry.name, "libs.txt") or
1872 mem.eql(u8, entry.name, "builtin.zig") or
1873 mem.eql(u8, entry.name, "zld.id") or
1874 mem.eql(u8, entry.name, "lld.id")) continue;
1875
1876 _ = try src_dir.dir.updateFile(entry.name, dest_dir, entry.name, .{});
1877 }
1878 } else {
1879 self.output_dir = build_output_dir;
1880 }
1881
1882 // This will ensure all output filenames will now have the output_dir available!
1883 self.computeOutFileNames();
1884
1885 // Update generated files
1886 if (self.output_dir != null) {
1887 self.output_path_source.path = builder.pathJoin(
1888 &.{ self.output_dir.?, self.out_filename },
1889 );
1890
1891 if (self.emit_h) {
1892 self.output_h_path_source.path = builder.pathJoin(
1893 &.{ self.output_dir.?, self.out_h_filename },
1894 );
1895 }
1896
1897 if (self.target.isWindows() or self.target.isUefi()) {
1898 self.output_pdb_path_source.path = builder.pathJoin(
1899 &.{ self.output_dir.?, self.out_pdb_filename },
1900 );
1901 }
1902 }
1903
1904 if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic and self.version != null and self.target.wantSharedLibSymLinks()) {
1905 try doAtomicSymLinks(builder.allocator, self.getOutputSource().getPath(builder), self.major_only_filename.?, self.name_only_filename.?);
1906 }
1907}
1908
1909fn isLibCLibrary(name: []const u8) bool {
1910 const libc_libraries = [_][]const u8{ "c", "m", "dl", "rt", "pthread" };
1911 for (libc_libraries) |libc_lib_name| {
1912 if (mem.eql(u8, name, libc_lib_name))
1913 return true;
1914 }
1915 return false;
1916}
1917
1918fn isLibCppLibrary(name: []const u8) bool {
1919 const libcpp_libraries = [_][]const u8{ "c++", "stdc++" };
1920 for (libcpp_libraries) |libcpp_lib_name| {
1921 if (mem.eql(u8, name, libcpp_lib_name))
1922 return true;
1923 }
1924 return false;
1925}
1926
1927/// Returned slice must be freed by the caller.
1928fn findVcpkgRoot(allocator: Allocator) !?[]const u8 {
1929 const appdata_path = try fs.getAppDataDir(allocator, "vcpkg");
1930 defer allocator.free(appdata_path);
1931
1932 const path_file = try fs.path.join(allocator, &[_][]const u8{ appdata_path, "vcpkg.path.txt" });
1933 defer allocator.free(path_file);
1934
1935 const file = fs.cwd().openFile(path_file, .{}) catch return null;
1936 defer file.close();
1937
1938 const size = @intCast(usize, try file.getEndPos());
1939 const vcpkg_path = try allocator.alloc(u8, size);
1940 const size_read = try file.read(vcpkg_path);
1941 std.debug.assert(size == size_read);
1942
1943 return vcpkg_path;
1944}
1945
1946pub fn doAtomicSymLinks(allocator: Allocator, output_path: []const u8, filename_major_only: []const u8, filename_name_only: []const u8) !void {
1947 const out_dir = fs.path.dirname(output_path) orelse ".";
1948 const out_basename = fs.path.basename(output_path);
1949 // sym link for libfoo.so.1 to libfoo.so.1.2.3
1950 const major_only_path = fs.path.join(
1951 allocator,
1952 &[_][]const u8{ out_dir, filename_major_only },
1953 ) catch unreachable;
1954 fs.atomicSymLink(allocator, out_basename, major_only_path) catch |err| {
1955 log.err("Unable to symlink {s} -> {s}", .{ major_only_path, out_basename });
1956 return err;
1957 };
1958 // sym link for libfoo.so to libfoo.so.1
1959 const name_only_path = fs.path.join(
1960 allocator,
1961 &[_][]const u8{ out_dir, filename_name_only },
1962 ) catch unreachable;
1963 fs.atomicSymLink(allocator, filename_major_only, name_only_path) catch |err| {
1964 log.err("Unable to symlink {s} -> {s}", .{ name_only_path, filename_major_only });
1965 return err;
1966 };
1967}
1968
1969fn execPkgConfigList(self: *Builder, out_code: *u8) (PkgConfigError || ExecError)![]const PkgConfigPkg {
1970 const stdout = try self.execAllowFail(&[_][]const u8{ "pkg-config", "--list-all" }, out_code, .Ignore);
1971 var list = ArrayList(PkgConfigPkg).init(self.allocator);
1972 errdefer list.deinit();
1973 var line_it = mem.tokenize(u8, stdout, "\r\n");
1974 while (line_it.next()) |line| {
1975 if (mem.trim(u8, line, " \t").len == 0) continue;
1976 var tok_it = mem.tokenize(u8, line, " \t");
1977 try list.append(PkgConfigPkg{
1978 .name = tok_it.next() orelse return error.PkgConfigInvalidOutput,
1979 .desc = tok_it.rest(),
1980 });
1981 }
1982 return list.toOwnedSlice();
1983}
1984
1985fn getPkgConfigList(self: *Builder) ![]const PkgConfigPkg {
1986 if (self.pkg_config_pkg_list) |res| {
1987 return res;
1988 }
1989 var code: u8 = undefined;
1990 if (execPkgConfigList(self, &code)) |list| {
1991 self.pkg_config_pkg_list = list;
1992 return list;
1993 } else |err| {
1994 const result = switch (err) {
1995 error.ProcessTerminated => error.PkgConfigCrashed,
1996 error.ExecNotSupported => error.PkgConfigFailed,
1997 error.ExitCodeFailure => error.PkgConfigFailed,
1998 error.FileNotFound => error.PkgConfigNotInstalled,
1999 error.InvalidName => error.PkgConfigNotInstalled,
2000 error.PkgConfigInvalidOutput => error.PkgConfigInvalidOutput,
2001 error.ChildExecFailed => error.PkgConfigFailed,
2002 else => return err,
2003 };
2004 self.pkg_config_pkg_list = result;
2005 return result;
2006 }
2007}
2008
2009test "addPackage" {
2010 if (builtin.os.tag == .wasi) return error.SkipZigTest;
2011
2012 var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
2013 defer arena.deinit();
2014
2015 var builder = try Builder.create(
2016 arena.allocator(),
2017 "test",
2018 "test",
2019 "test",
2020 "test",
2021 );
2022 defer builder.destroy();
2023
2024 const pkg_dep = Pkg{
2025 .name = "pkg_dep",
2026 .source = .{ .path = "/not/a/pkg_dep.zig" },
2027 };
2028 const pkg_top = Pkg{
2029 .name = "pkg_dep",
2030 .source = .{ .path = "/not/a/pkg_top.zig" },
2031 .dependencies = &[_]Pkg{pkg_dep},
2032 };
2033
2034 var exe = builder.addExecutable("not_an_executable", "/not/an/executable.zig");
2035 exe.addPackage(pkg_top);
2036
2037 try std.testing.expectEqual(@as(usize, 1), exe.packages.items.len);
2038
2039 const dupe = exe.packages.items[0];
2040 try std.testing.expectEqualStrings(pkg_top.name, dupe.name);
2041}
2042
2043fn addFlag(args: *ArrayList([]const u8), comptime name: []const u8, opt: ?bool) !void {
2044 const cond = opt orelse return;
2045 try args.ensureUnusedCapacity(1);
2046 if (cond) {
2047 args.appendAssumeCapacity("-f" ++ name);
2048 } else {
2049 args.appendAssumeCapacity("-fno-" ++ name);
2050 }
2051}
2052
2053const TransitiveDeps = struct {
2054 link_objects: ArrayList(LinkObject),
2055 seen_system_libs: StringHashMap(void),
2056 seen_steps: std.AutoHashMap(*const Step, void),
2057 is_linking_libcpp: bool,
2058 is_linking_libc: bool,
2059 frameworks: *StringHashMap(FrameworkLinkInfo),
2060
2061 fn add(td: *TransitiveDeps, link_objects: []const LinkObject) !void {
2062 try td.link_objects.ensureUnusedCapacity(link_objects.len);
2063
2064 for (link_objects) |link_object| {
2065 try td.link_objects.append(link_object);
2066 switch (link_object) {
2067 .other_step => |other| try addInner(td, other, other.isDynamicLibrary()),
2068 else => {},
2069 }
2070 }
2071 }
2072
2073 fn addInner(td: *TransitiveDeps, other: *LibExeObjStep, dyn: bool) !void {
2074 // Inherit dependency on libc and libc++
2075 td.is_linking_libcpp = td.is_linking_libcpp or other.is_linking_libcpp;
2076 td.is_linking_libc = td.is_linking_libc or other.is_linking_libc;
2077
2078 // Inherit dependencies on darwin frameworks
2079 if (!dyn) {
2080 var it = other.frameworks.iterator();
2081 while (it.next()) |framework| {
2082 try td.frameworks.put(framework.key_ptr.*, framework.value_ptr.*);
2083 }
2084 }
2085
2086 // Inherit dependencies on system libraries and static libraries.
2087 for (other.link_objects.items) |other_link_object| {
2088 switch (other_link_object) {
2089 .system_lib => |system_lib| {
2090 if ((try td.seen_system_libs.fetchPut(system_lib.name, {})) != null)
2091 continue;
2092
2093 if (dyn)
2094 continue;
2095
2096 try td.link_objects.append(other_link_object);
2097 },
2098 .other_step => |inner_other| {
2099 if ((try td.seen_steps.fetchPut(&inner_other.step, {})) != null)
2100 continue;
2101
2102 if (!dyn)
2103 try td.link_objects.append(other_link_object);
2104
2105 try addInner(td, inner_other, dyn or inner_other.isDynamicLibrary());
2106 },
2107 else => continue,
2108 }
2109 }
2110 }
2111};
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/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/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+1-1
......@@ -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/math/big.zig-1
......@@ -13,7 +13,6 @@ pub const Log2Limb = std.math.Log2Int(Limb);
1313
1414comptime {
1515 assert(std.math.floorPowerOfTwo(usize, limb_info.bits) == limb_info.bits);
16 assert(limb_info.bits <= 64); // u128 set is unsupported
1716 assert(limb_info.signedness == .unsigned);
1817}
1918
lib/std/math/big/int.zig+3-8
......@@ -30,7 +30,7 @@ pub fn calcLimbLen(scalar: anytype) usize {
3030 }
3131
3232 const w_value = std.math.absCast(scalar);
33 return @divFloor(@intCast(Limb, math.log2(w_value)), limb_bits) + 1;
33 return @intCast(usize, @divFloor(@intCast(Limb, math.log2(w_value)), limb_bits) + 1);
3434}
3535
3636pub fn calcToStringLimbsBufferLen(a_len: usize, base: u8) usize {
......@@ -238,10 +238,7 @@ pub const Mutable = struct {
238238 var i: usize = 0;
239239 while (true) : (i += 1) {
240240 self.limbs[i] = @truncate(Limb, w_value);
241
242 // TODO: shift == 64 at compile-time fails. Fails on u128 limbs.
243 w_value >>= limb_bits / 2;
244 w_value >>= limb_bits / 2;
241 w_value >>= limb_bits;
245242
246243 if (w_value == 0) break;
247244 }
......@@ -258,9 +255,7 @@ pub const Mutable = struct {
258255 comptime var i = 0;
259256 inline while (true) : (i += 1) {
260257 self.limbs[i] = w_value & mask;
261
262 w_value >>= limb_bits / 2;
263 w_value >>= limb_bits / 2;
258 w_value >>= limb_bits;
264259
265260 if (w_value == 0) break;
266261 }
lib/std/meta.zig+1-1
......@@ -332,7 +332,7 @@ pub fn Sentinel(comptime T: type, comptime sentinel_val: Elem(T)) type {
332332 @compileError("Unable to derive a sentinel pointer type from " ++ @typeName(T));
333333}
334334
335const assumeSentinel = @compileError("This function has been removed, consider using std.mem.sliceTo() or if needed a @ptrCast()");
335pub const assumeSentinel = @compileError("This function has been removed, consider using std.mem.sliceTo() or if needed a @ptrCast()");
336336
337337pub fn containerLayout(comptime T: type) Type.ContainerLayout {
338338 return switch (@typeInfo(T)) {
lib/std/os.zig+1-1
......@@ -4513,7 +4513,7 @@ pub fn faccessatW(dirfd: fd_t, sub_path_w: [*:0]const u16, mode: u32, flags: u32
45134513 var nt_name = windows.UNICODE_STRING{
45144514 .Length = path_len_bytes,
45154515 .MaximumLength = path_len_bytes,
4516 .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w)),
4516 .Buffer = @qualCast([*:0]u16, sub_path_w),
45174517 };
45184518 var attr = windows.OBJECT_ATTRIBUTES{
45194519 .Length = @sizeOf(windows.OBJECT_ATTRIBUTES),
lib/std/os/linux.zig+2
......@@ -40,6 +40,7 @@ const arch_bits = switch (native_arch) {
4040 .riscv64 => @import("linux/riscv64.zig"),
4141 .sparc64 => @import("linux/sparc64.zig"),
4242 .mips, .mipsel => @import("linux/mips.zig"),
43 .mips64, .mips64el => @import("linux/mips64.zig"),
4344 .powerpc => @import("linux/powerpc.zig"),
4445 .powerpc64, .powerpc64le => @import("linux/powerpc64.zig"),
4546 else => struct {},
......@@ -101,6 +102,7 @@ pub const SYS = switch (@import("builtin").cpu.arch) {
101102 .riscv64 => syscalls.RiscV64,
102103 .sparc64 => syscalls.Sparc64,
103104 .mips, .mipsel => syscalls.Mips,
105 .mips64, .mips64el => syscalls.Mips64,
104106 .powerpc => syscalls.PowerPC,
105107 .powerpc64, .powerpc64le => syscalls.PowerPC64,
106108 else => @compileError("The Zig Standard Library is missing syscall definitions for the target CPU architecture"),
lib/std/os/linux/mips64.zig created+413
......@@ -0,0 +1,413 @@
1const std = @import("../../std.zig");
2const maxInt = std.math.maxInt;
3const linux = std.os.linux;
4const SYS = linux.SYS;
5const socklen_t = linux.socklen_t;
6const iovec = std.os.iovec;
7const iovec_const = std.os.iovec_const;
8const uid_t = linux.uid_t;
9const gid_t = linux.gid_t;
10const pid_t = linux.pid_t;
11const sockaddr = linux.sockaddr;
12const timespec = linux.timespec;
13
14pub fn syscall0(number: SYS) usize {
15 return asm volatile (
16 \\ syscall
17 \\ blez $7, 1f
18 \\ dsubu $2, $0, $2
19 \\ 1:
20 : [ret] "={$2}" (-> usize),
21 : [number] "{$2}" (@enumToInt(number)),
22 : "$1", "$3", "$4", "$5", "$6", "$7", "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24", "$25", "hi", "lo", "memory"
23 );
24}
25
26pub fn syscall_pipe(fd: *[2]i32) usize {
27 return asm volatile (
28 \\ .set noat
29 \\ .set noreorder
30 \\ syscall
31 \\ blez $7, 1f
32 \\ nop
33 \\ b 2f
34 \\ subu $2, $0, $2
35 \\ 1:
36 \\ sw $2, 0($4)
37 \\ sw $3, 4($4)
38 \\ 2:
39 : [ret] "={$2}" (-> usize),
40 : [number] "{$2}" (@enumToInt(SYS.pipe)),
41 [fd] "{$4}" (fd),
42 : "$1", "$3", "$5", "$6", "$7", "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24", "$25", "hi", "lo", "memory"
43 );
44}
45
46pub fn syscall1(number: SYS, arg1: usize) usize {
47 return asm volatile (
48 \\ syscall
49 \\ blez $7, 1f
50 \\ dsubu $2, $0, $2
51 \\ 1:
52 : [ret] "={$2}" (-> usize),
53 : [number] "{$2}" (@enumToInt(number)),
54 [arg1] "{$4}" (arg1),
55 : "$1", "$3", "$5", "$6", "$7", "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24", "$25", "hi", "lo", "memory"
56 );
57}
58
59pub fn syscall2(number: SYS, arg1: usize, arg2: usize) usize {
60 return asm volatile (
61 \\ syscall
62 \\ blez $7, 1f
63 \\ dsubu $2, $0, $2
64 \\ 1:
65 : [ret] "={$2}" (-> usize),
66 : [number] "{$2}" (@enumToInt(number)),
67 [arg1] "{$4}" (arg1),
68 [arg2] "{$5}" (arg2),
69 : "$1", "$3", "$6", "$7", "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24", "$25", "hi", "lo", "memory"
70 );
71}
72
73pub fn syscall3(number: SYS, arg1: usize, arg2: usize, arg3: usize) usize {
74 return asm volatile (
75 \\ syscall
76 \\ blez $7, 1f
77 \\ dsubu $2, $0, $2
78 \\ 1:
79 : [ret] "={$2}" (-> usize),
80 : [number] "{$2}" (@enumToInt(number)),
81 [arg1] "{$4}" (arg1),
82 [arg2] "{$5}" (arg2),
83 [arg3] "{$6}" (arg3),
84 : "$1", "$3", "$7", "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24", "$25", "hi", "lo", "memory"
85 );
86}
87
88pub fn syscall4(number: SYS, arg1: usize, arg2: usize, arg3: usize, arg4: usize) usize {
89 return asm volatile (
90 \\ syscall
91 \\ blez $7, 1f
92 \\ dsubu $2, $0, $2
93 \\ 1:
94 : [ret] "={$2}" (-> usize),
95 : [number] "{$2}" (@enumToInt(number)),
96 [arg1] "{$4}" (arg1),
97 [arg2] "{$5}" (arg2),
98 [arg3] "{$6}" (arg3),
99 [arg4] "{$7}" (arg4),
100 : "$1", "$3", "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24", "$25", "hi", "lo", "memory"
101 );
102}
103
104pub fn syscall5(number: SYS, arg1: usize, arg2: usize, arg3: usize, arg4: usize, arg5: usize) usize {
105 return asm volatile (
106 \\ syscall
107 \\ blez $7, 1f
108 \\ dsubu $2, $0, $2
109 \\ 1:
110 : [ret] "={$2}" (-> usize),
111 : [number] "{$2}" (@enumToInt(number)),
112 [arg1] "{$4}" (arg1),
113 [arg2] "{$5}" (arg2),
114 [arg3] "{$6}" (arg3),
115 [arg4] "{$7}" (arg4),
116 [arg5] "{$8}" (arg5),
117 : "$1", "$3", "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24", "$25", "hi", "lo", "memory"
118 );
119}
120
121// NOTE: The o32 calling convention requires the callee to reserve 16 bytes for
122// the first four arguments even though they're passed in $a0-$a3.
123
124pub fn syscall6(
125 number: SYS,
126 arg1: usize,
127 arg2: usize,
128 arg3: usize,
129 arg4: usize,
130 arg5: usize,
131 arg6: usize,
132) usize {
133 return asm volatile (
134 \\ syscall
135 \\ blez $7, 1f
136 \\ dsubu $2, $0, $2
137 \\ 1:
138 : [ret] "={$2}" (-> usize),
139 : [number] "{$2}" (@enumToInt(number)),
140 [arg1] "{$4}" (arg1),
141 [arg2] "{$5}" (arg2),
142 [arg3] "{$6}" (arg3),
143 [arg4] "{$7}" (arg4),
144 [arg5] "{$8}" (arg5),
145 [arg6] "{$9}" (arg6),
146 : "$1", "$3", "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24", "$25", "hi", "lo", "memory"
147 );
148}
149
150pub fn syscall7(
151 number: SYS,
152 arg1: usize,
153 arg2: usize,
154 arg3: usize,
155 arg4: usize,
156 arg5: usize,
157 arg6: usize,
158 arg7: usize,
159) usize {
160 return asm volatile (
161 \\ syscall
162 \\ blez $7, 1f
163 \\ dsubu $2, $0, $2
164 \\ 1:
165 : [ret] "={$2}" (-> usize),
166 : [number] "{$2}" (@enumToInt(number)),
167 [arg1] "{$4}" (arg1),
168 [arg2] "{$5}" (arg2),
169 [arg3] "{$6}" (arg3),
170 [arg4] "{$7}" (arg4),
171 [arg5] "{$8}" (arg5),
172 [arg6] "{$9}" (arg6),
173 [arg7] "{$10}" (arg7),
174 : "$1", "$3", "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24", "$25", "hi", "lo", "memory"
175 );
176}
177
178const CloneFn = *const fn (arg: usize) callconv(.C) u8;
179
180/// This matches the libc clone function.
181pub extern fn clone(func: CloneFn, stack: usize, flags: u32, arg: usize, ptid: *i32, tls: usize, ctid: *i32) usize;
182
183pub fn restore() callconv(.Naked) void {
184 return asm volatile ("syscall"
185 :
186 : [number] "{$2}" (@enumToInt(SYS.rt_sigreturn)),
187 : "$1", "$3", "$4", "$5", "$6", "$7", "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24", "$25", "hi", "lo", "memory"
188 );
189}
190
191pub fn restore_rt() callconv(.Naked) void {
192 return asm volatile ("syscall"
193 :
194 : [number] "{$2}" (@enumToInt(SYS.rt_sigreturn)),
195 : "$1", "$3", "$4", "$5", "$6", "$7", "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24", "$25", "hi", "lo", "memory"
196 );
197}
198
199pub const O = struct {
200 pub const CREAT = 0o0400;
201 pub const EXCL = 0o02000;
202 pub const NOCTTY = 0o04000;
203 pub const TRUNC = 0o01000;
204 pub const APPEND = 0o0010;
205 pub const NONBLOCK = 0o0200;
206 pub const DSYNC = 0o0020;
207 pub const SYNC = 0o040020;
208 pub const RSYNC = 0o040020;
209 pub const DIRECTORY = 0o0200000;
210 pub const NOFOLLOW = 0o0400000;
211 pub const CLOEXEC = 0o02000000;
212
213 pub const ASYNC = 0o010000;
214 pub const DIRECT = 0o0100000;
215 pub const LARGEFILE = 0o020000;
216 pub const NOATIME = 0o01000000;
217 pub const PATH = 0o010000000;
218 pub const TMPFILE = 0o020200000;
219 pub const NDELAY = NONBLOCK;
220};
221
222pub const F = struct {
223 pub const DUPFD = 0;
224 pub const GETFD = 1;
225 pub const SETFD = 2;
226 pub const GETFL = 3;
227 pub const SETFL = 4;
228
229 pub const SETOWN = 24;
230 pub const GETOWN = 23;
231 pub const SETSIG = 10;
232 pub const GETSIG = 11;
233
234 pub const GETLK = 33;
235 pub const SETLK = 34;
236 pub const SETLKW = 35;
237
238 pub const RDLCK = 0;
239 pub const WRLCK = 1;
240 pub const UNLCK = 2;
241
242 pub const SETOWN_EX = 15;
243 pub const GETOWN_EX = 16;
244
245 pub const GETOWNER_UIDS = 17;
246};
247
248pub const LOCK = struct {
249 pub const SH = 1;
250 pub const EX = 2;
251 pub const UN = 8;
252 pub const NB = 4;
253};
254
255pub const MMAP2_UNIT = 4096;
256
257pub const MAP = struct {
258 pub const NORESERVE = 0x0400;
259 pub const GROWSDOWN = 0x1000;
260 pub const DENYWRITE = 0x2000;
261 pub const EXECUTABLE = 0x4000;
262 pub const LOCKED = 0x8000;
263 pub const @"32BIT" = 0x40;
264};
265
266pub const VDSO = struct {
267 pub const CGT_SYM = "__kernel_clock_gettime";
268 pub const CGT_VER = "LINUX_2.6.39";
269};
270
271pub const Flock = extern struct {
272 type: i16,
273 whence: i16,
274 __pad0: [4]u8,
275 start: off_t,
276 len: off_t,
277 pid: pid_t,
278 __unused: [4]u8,
279};
280
281pub const msghdr = extern struct {
282 name: ?*sockaddr,
283 namelen: socklen_t,
284 iov: [*]iovec,
285 iovlen: i32,
286 control: ?*anyopaque,
287 controllen: socklen_t,
288 flags: i32,
289};
290
291pub const msghdr_const = extern struct {
292 name: ?*const sockaddr,
293 namelen: socklen_t,
294 iov: [*]const iovec_const,
295 iovlen: i32,
296 control: ?*const anyopaque,
297 controllen: socklen_t,
298 flags: i32,
299};
300
301pub const blksize_t = i32;
302pub const nlink_t = u32;
303pub const time_t = i32;
304pub const mode_t = u32;
305pub const off_t = i64;
306pub const ino_t = u64;
307pub const dev_t = u64;
308pub const blkcnt_t = i64;
309
310// The `stat` definition used by the Linux kernel.
311pub const Stat = extern struct {
312 dev: u32,
313 __pad0: [3]u32, // Reserved for st_dev expansion
314 ino: ino_t,
315 mode: mode_t,
316 nlink: nlink_t,
317 uid: uid_t,
318 gid: gid_t,
319 rdev: u32,
320 __pad1: [3]u32,
321 size: off_t,
322 atim: timespec,
323 mtim: timespec,
324 ctim: timespec,
325 blksize: blksize_t,
326 __pad3: u32,
327 blocks: blkcnt_t,
328 __pad4: [14]usize,
329
330 pub fn atime(self: @This()) timespec {
331 return self.atim;
332 }
333
334 pub fn mtime(self: @This()) timespec {
335 return self.mtim;
336 }
337
338 pub fn ctime(self: @This()) timespec {
339 return self.ctim;
340 }
341};
342
343pub const timeval = extern struct {
344 tv_sec: isize,
345 tv_usec: isize,
346};
347
348pub const timezone = extern struct {
349 tz_minuteswest: i32,
350 tz_dsttime: i32,
351};
352
353pub const Elf_Symndx = u32;
354
355pub const rlimit_resource = enum(c_int) {
356 /// Per-process CPU limit, in seconds.
357 CPU,
358
359 /// Largest file that can be created, in bytes.
360 FSIZE,
361
362 /// Maximum size of data segment, in bytes.
363 DATA,
364
365 /// Maximum size of stack segment, in bytes.
366 STACK,
367
368 /// Largest core file that can be created, in bytes.
369 CORE,
370
371 /// Number of open files.
372 NOFILE,
373
374 /// Address space limit.
375 AS,
376
377 /// Largest resident set size, in bytes.
378 /// This affects swapping; processes that are exceeding their
379 /// resident set size will be more likely to have physical memory
380 /// taken from them.
381 RSS,
382
383 /// Number of processes.
384 NPROC,
385
386 /// Locked-in-memory address space.
387 MEMLOCK,
388
389 /// Maximum number of file locks.
390 LOCKS,
391
392 /// Maximum number of pending signals.
393 SIGPENDING,
394
395 /// Maximum bytes in POSIX message queues.
396 MSGQUEUE,
397
398 /// Maximum nice priority allowed to raise to.
399 /// Nice levels 19 .. -20 correspond to 0 .. 39
400 /// values of this resource limit.
401 NICE,
402
403 /// Maximum realtime priority allowed for non-priviledged
404 /// processes.
405 RTPRIO,
406
407 /// Maximum CPU time in µs that a process scheduled under a real-time
408 /// scheduling policy may consume without making a blocking system
409 /// call before being forcibly descheduled.
410 RTTIME,
411
412 _,
413};
lib/std/os/linux/syscalls.zig+359
......@@ -2032,6 +2032,365 @@ pub const Mips = enum(usize) {
20322032 set_mempolicy_home_node = Linux + 450,
20332033};
20342034
2035pub const Mips64 = enum(usize) {
2036 pub const Linux = 5000;
2037
2038 read = Linux + 0,
2039 write = Linux + 1,
2040 open = Linux + 2,
2041 close = Linux + 3,
2042 stat = Linux + 4,
2043 fstat = Linux + 5,
2044 lstat = Linux + 6,
2045 poll = Linux + 7,
2046 lseek = Linux + 8,
2047 mmap = Linux + 9,
2048 mprotect = Linux + 10,
2049 munmap = Linux + 11,
2050 brk = Linux + 12,
2051 rt_sigaction = Linux + 13,
2052 rt_sigprocmask = Linux + 14,
2053 ioctl = Linux + 15,
2054 pread64 = Linux + 16,
2055 pwrite64 = Linux + 17,
2056 readv = Linux + 18,
2057 writev = Linux + 19,
2058 access = Linux + 20,
2059 pipe = Linux + 21,
2060 _newselect = Linux + 22,
2061 sched_yield = Linux + 23,
2062 mremap = Linux + 24,
2063 msync = Linux + 25,
2064 mincore = Linux + 26,
2065 madvise = Linux + 27,
2066 shmget = Linux + 28,
2067 shmat = Linux + 29,
2068 shmctl = Linux + 30,
2069 dup = Linux + 31,
2070 dup2 = Linux + 32,
2071 pause = Linux + 33,
2072 nanosleep = Linux + 34,
2073 getitimer = Linux + 35,
2074 setitimer = Linux + 36,
2075 alarm = Linux + 37,
2076 getpid = Linux + 38,
2077 sendfile = Linux + 39,
2078 socket = Linux + 40,
2079 connect = Linux + 41,
2080 accept = Linux + 42,
2081 sendto = Linux + 43,
2082 recvfrom = Linux + 44,
2083 sendmsg = Linux + 45,
2084 recvmsg = Linux + 46,
2085 shutdown = Linux + 47,
2086 bind = Linux + 48,
2087 listen = Linux + 49,
2088 getsockname = Linux + 50,
2089 getpeername = Linux + 51,
2090 socketpair = Linux + 52,
2091 setsockopt = Linux + 53,
2092 getsockopt = Linux + 54,
2093 clone = Linux + 55,
2094 fork = Linux + 56,
2095 execve = Linux + 57,
2096 exit = Linux + 58,
2097 wait4 = Linux + 59,
2098 kill = Linux + 60,
2099 uname = Linux + 61,
2100 semget = Linux + 62,
2101 semop = Linux + 63,
2102 semctl = Linux + 64,
2103 shmdt = Linux + 65,
2104 msgget = Linux + 66,
2105 msgsnd = Linux + 67,
2106 msgrcv = Linux + 68,
2107 msgctl = Linux + 69,
2108 fcntl = Linux + 70,
2109 flock = Linux + 71,
2110 fsync = Linux + 72,
2111 fdatasync = Linux + 73,
2112 truncate = Linux + 74,
2113 ftruncate = Linux + 75,
2114 getdents = Linux + 76,
2115 getcwd = Linux + 77,
2116 chdir = Linux + 78,
2117 fchdir = Linux + 79,
2118 rename = Linux + 80,
2119 mkdir = Linux + 81,
2120 rmdir = Linux + 82,
2121 creat = Linux + 83,
2122 link = Linux + 84,
2123 unlink = Linux + 85,
2124 symlink = Linux + 86,
2125 readlink = Linux + 87,
2126 chmod = Linux + 88,
2127 fchmod = Linux + 89,
2128 chown = Linux + 90,
2129 fchown = Linux + 91,
2130 lchown = Linux + 92,
2131 umask = Linux + 93,
2132 gettimeofday = Linux + 94,
2133 getrlimit = Linux + 95,
2134 getrusage = Linux + 96,
2135 sysinfo = Linux + 97,
2136 times = Linux + 98,
2137 ptrace = Linux + 99,
2138 getuid = Linux + 100,
2139 syslog = Linux + 101,
2140 getgid = Linux + 102,
2141 setuid = Linux + 103,
2142 setgid = Linux + 104,
2143 geteuid = Linux + 105,
2144 getegid = Linux + 106,
2145 setpgid = Linux + 107,
2146 getppid = Linux + 108,
2147 getpgrp = Linux + 109,
2148 setsid = Linux + 110,
2149 setreuid = Linux + 111,
2150 setregid = Linux + 112,
2151 getgroups = Linux + 113,
2152 setgroups = Linux + 114,
2153 setresuid = Linux + 115,
2154 getresuid = Linux + 116,
2155 setresgid = Linux + 117,
2156 getresgid = Linux + 118,
2157 getpgid = Linux + 119,
2158 setfsuid = Linux + 120,
2159 setfsgid = Linux + 121,
2160 getsid = Linux + 122,
2161 capget = Linux + 123,
2162 capset = Linux + 124,
2163 rt_sigpending = Linux + 125,
2164 rt_sigtimedwait = Linux + 126,
2165 rt_sigqueueinfo = Linux + 127,
2166 rt_sigsuspend = Linux + 128,
2167 sigaltstack = Linux + 129,
2168 utime = Linux + 130,
2169 mknod = Linux + 131,
2170 personality = Linux + 132,
2171 ustat = Linux + 133,
2172 statfs = Linux + 134,
2173 fstatfs = Linux + 135,
2174 sysfs = Linux + 136,
2175 getpriority = Linux + 137,
2176 setpriority = Linux + 138,
2177 sched_setparam = Linux + 139,
2178 sched_getparam = Linux + 140,
2179 sched_setscheduler = Linux + 141,
2180 sched_getscheduler = Linux + 142,
2181 sched_get_priority_max = Linux + 143,
2182 sched_get_priority_min = Linux + 144,
2183 sched_rr_get_interval = Linux + 145,
2184 mlock = Linux + 146,
2185 munlock = Linux + 147,
2186 mlockall = Linux + 148,
2187 munlockall = Linux + 149,
2188 vhangup = Linux + 150,
2189 pivot_root = Linux + 151,
2190 _sysctl = Linux + 152,
2191 prctl = Linux + 153,
2192 adjtimex = Linux + 154,
2193 setrlimit = Linux + 155,
2194 chroot = Linux + 156,
2195 sync = Linux + 157,
2196 acct = Linux + 158,
2197 settimeofday = Linux + 159,
2198 mount = Linux + 160,
2199 umount2 = Linux + 161,
2200 swapon = Linux + 162,
2201 swapoff = Linux + 163,
2202 reboot = Linux + 164,
2203 sethostname = Linux + 165,
2204 setdomainname = Linux + 166,
2205 create_module = Linux + 167,
2206 init_module = Linux + 168,
2207 delete_module = Linux + 169,
2208 get_kernel_syms = Linux + 170,
2209 query_module = Linux + 171,
2210 quotactl = Linux + 172,
2211 nfsservctl = Linux + 173,
2212 getpmsg = Linux + 174,
2213 putpmsg = Linux + 175,
2214 afs_syscall = Linux + 176,
2215 reserved177 = Linux + 177,
2216 gettid = Linux + 178,
2217 readahead = Linux + 179,
2218 setxattr = Linux + 180,
2219 lsetxattr = Linux + 181,
2220 fsetxattr = Linux + 182,
2221 getxattr = Linux + 183,
2222 lgetxattr = Linux + 184,
2223 fgetxattr = Linux + 185,
2224 listxattr = Linux + 186,
2225 llistxattr = Linux + 187,
2226 flistxattr = Linux + 188,
2227 removexattr = Linux + 189,
2228 lremovexattr = Linux + 190,
2229 fremovexattr = Linux + 191,
2230 tkill = Linux + 192,
2231 reserved193 = Linux + 193,
2232 futex = Linux + 194,
2233 sched_setaffinity = Linux + 195,
2234 sched_getaffinity = Linux + 196,
2235 cacheflush = Linux + 197,
2236 cachectl = Linux + 198,
2237 sysmips = Linux + 199,
2238 io_setup = Linux + 200,
2239 io_destroy = Linux + 201,
2240 io_getevents = Linux + 202,
2241 io_submit = Linux + 203,
2242 io_cancel = Linux + 204,
2243 exit_group = Linux + 205,
2244 lookup_dcookie = Linux + 206,
2245 epoll_create = Linux + 207,
2246 epoll_ctl = Linux + 208,
2247 epoll_wait = Linux + 209,
2248 remap_file_pages = Linux + 210,
2249 rt_sigreturn = Linux + 211,
2250 set_tid_address = Linux + 212,
2251 restart_syscall = Linux + 213,
2252 semtimedop = Linux + 214,
2253 fadvise64 = Linux + 215,
2254 timer_create = Linux + 216,
2255 timer_settime = Linux + 217,
2256 timer_gettime = Linux + 218,
2257 timer_getoverrun = Linux + 219,
2258 timer_delete = Linux + 220,
2259 clock_settime = Linux + 221,
2260 clock_gettime = Linux + 222,
2261 clock_getres = Linux + 223,
2262 clock_nanosleep = Linux + 224,
2263 tgkill = Linux + 225,
2264 utimes = Linux + 226,
2265 mbind = Linux + 227,
2266 get_mempolicy = Linux + 228,
2267 set_mempolicy = Linux + 229,
2268 mq_open = Linux + 230,
2269 mq_unlink = Linux + 231,
2270 mq_timedsend = Linux + 232,
2271 mq_timedreceive = Linux + 233,
2272 mq_notify = Linux + 234,
2273 mq_getsetattr = Linux + 235,
2274 vserver = Linux + 236,
2275 waitid = Linux + 237,
2276 add_key = Linux + 239,
2277 request_key = Linux + 240,
2278 keyctl = Linux + 241,
2279 set_thread_area = Linux + 242,
2280 inotify_init = Linux + 243,
2281 inotify_add_watch = Linux + 244,
2282 inotify_rm_watch = Linux + 245,
2283 migrate_pages = Linux + 246,
2284 openat = Linux + 247,
2285 mkdirat = Linux + 248,
2286 mknodat = Linux + 249,
2287 fchownat = Linux + 250,
2288 futimesat = Linux + 251,
2289 fstatat64 = Linux + 252,
2290 unlinkat = Linux + 253,
2291 renameat = Linux + 254,
2292 linkat = Linux + 255,
2293 symlinkat = Linux + 256,
2294 readlinkat = Linux + 257,
2295 fchmodat = Linux + 258,
2296 faccessat = Linux + 259,
2297 pselect6 = Linux + 260,
2298 ppoll = Linux + 261,
2299 unshare = Linux + 262,
2300 splice = Linux + 263,
2301 sync_file_range = Linux + 264,
2302 tee = Linux + 265,
2303 vmsplice = Linux + 266,
2304 move_pages = Linux + 267,
2305 set_robust_list = Linux + 268,
2306 get_robust_list = Linux + 269,
2307 kexec_load = Linux + 270,
2308 getcpu = Linux + 271,
2309 epoll_pwait = Linux + 272,
2310 ioprio_set = Linux + 273,
2311 ioprio_get = Linux + 274,
2312 utimensat = Linux + 275,
2313 signalfd = Linux + 276,
2314 timerfd = Linux + 277,
2315 eventfd = Linux + 278,
2316 fallocate = Linux + 279,
2317 timerfd_create = Linux + 280,
2318 timerfd_gettime = Linux + 281,
2319 timerfd_settime = Linux + 282,
2320 signalfd4 = Linux + 283,
2321 eventfd2 = Linux + 284,
2322 epoll_create1 = Linux + 285,
2323 dup3 = Linux + 286,
2324 pipe2 = Linux + 287,
2325 inotify_init1 = Linux + 288,
2326 preadv = Linux + 289,
2327 pwritev = Linux + 290,
2328 rt_tgsigqueueinfo = Linux + 291,
2329 perf_event_open = Linux + 292,
2330 accept4 = Linux + 293,
2331 recvmmsg = Linux + 294,
2332 fanotify_init = Linux + 295,
2333 fanotify_mark = Linux + 296,
2334 prlimit64 = Linux + 297,
2335 name_to_handle_at = Linux + 298,
2336 open_by_handle_at = Linux + 299,
2337 clock_adjtime = Linux + 300,
2338 syncfs = Linux + 301,
2339 sendmmsg = Linux + 302,
2340 setns = Linux + 303,
2341 process_vm_readv = Linux + 304,
2342 process_vm_writev = Linux + 305,
2343 kcmp = Linux + 306,
2344 finit_module = Linux + 307,
2345 getdents64 = Linux + 308,
2346 sched_setattr = Linux + 309,
2347 sched_getattr = Linux + 310,
2348 renameat2 = Linux + 311,
2349 seccomp = Linux + 312,
2350 getrandom = Linux + 313,
2351 memfd_create = Linux + 314,
2352 bpf = Linux + 315,
2353 execveat = Linux + 316,
2354 userfaultfd = Linux + 317,
2355 membarrier = Linux + 318,
2356 mlock2 = Linux + 319,
2357 copy_file_range = Linux + 320,
2358 preadv2 = Linux + 321,
2359 pwritev2 = Linux + 322,
2360 pkey_mprotect = Linux + 323,
2361 pkey_alloc = Linux + 324,
2362 pkey_free = Linux + 325,
2363 statx = Linux + 326,
2364 rseq = Linux + 327,
2365 io_pgetevents = Linux + 328,
2366 pidfd_send_signal = Linux + 424,
2367 io_uring_setup = Linux + 425,
2368 io_uring_enter = Linux + 426,
2369 io_uring_register = Linux + 427,
2370 open_tree = Linux + 428,
2371 move_mount = Linux + 429,
2372 fsopen = Linux + 430,
2373 fsconfig = Linux + 431,
2374 fsmount = Linux + 432,
2375 fspick = Linux + 433,
2376 pidfd_open = Linux + 434,
2377 clone3 = Linux + 435,
2378 close_range = Linux + 436,
2379 openat2 = Linux + 437,
2380 pidfd_getfd = Linux + 438,
2381 faccessat2 = Linux + 439,
2382 process_madvise = Linux + 440,
2383 epoll_pwait2 = Linux + 441,
2384 mount_setattr = Linux + 442,
2385 quotactl_fd = Linux + 443,
2386 landlock_create_ruleset = Linux + 444,
2387 landlock_add_rule = Linux + 445,
2388 landlock_restrict_self = Linux + 446,
2389 process_mrelease = Linux + 448,
2390 futex_waitv = Linux + 449,
2391 set_mempolicy_home_node = Linux + 450,
2392};
2393
20352394pub const PowerPC = enum(usize) {
20362395 restart_syscall = 0,
20372396 exit = 1,
lib/std/os/linux/tls.zig+5-5
......@@ -48,7 +48,7 @@ const TLSVariant = enum {
4848};
4949
5050const tls_variant = switch (native_arch) {
51 .arm, .armeb, .thumb, .aarch64, .aarch64_be, .riscv32, .riscv64, .mips, .mipsel, .powerpc, .powerpc64, .powerpc64le => TLSVariant.VariantI,
51 .arm, .armeb, .thumb, .aarch64, .aarch64_be, .riscv32, .riscv64, .mips, .mipsel, .mips64, .mips64el, .powerpc, .powerpc64, .powerpc64le => TLSVariant.VariantI,
5252 .x86_64, .x86, .sparc64 => TLSVariant.VariantII,
5353 else => @compileError("undefined tls_variant for this architecture"),
5454};
......@@ -64,7 +64,7 @@ const tls_tcb_size = switch (native_arch) {
6464
6565// Controls if the TP points to the end of the TCB instead of its beginning
6666const tls_tp_points_past_tcb = switch (native_arch) {
67 .riscv32, .riscv64, .mips, .mipsel, .powerpc, .powerpc64, .powerpc64le => true,
67 .riscv32, .riscv64, .mips, .mipsel, .mips64, .mips64el, .powerpc, .powerpc64, .powerpc64le => true,
6868 else => false,
6969};
7070
......@@ -72,12 +72,12 @@ const tls_tp_points_past_tcb = switch (native_arch) {
7272// make the generated code more efficient
7373
7474const tls_tp_offset = switch (native_arch) {
75 .mips, .mipsel, .powerpc, .powerpc64, .powerpc64le => 0x7000,
75 .mips, .mipsel, .mips64, .mips64el, .powerpc, .powerpc64, .powerpc64le => 0x7000,
7676 else => 0,
7777};
7878
7979const tls_dtv_offset = switch (native_arch) {
80 .mips, .mipsel, .powerpc, .powerpc64, .powerpc64le => 0x8000,
80 .mips, .mipsel, .mips64, .mips64el, .powerpc, .powerpc64, .powerpc64le => 0x8000,
8181 .riscv32, .riscv64 => 0x800,
8282 else => 0,
8383};
......@@ -156,7 +156,7 @@ pub fn setThreadPointer(addr: usize) void {
156156 : [addr] "r" (addr),
157157 );
158158 },
159 .mips, .mipsel => {
159 .mips, .mipsel, .mips64, .mips64el => {
160160 const rc = std.os.linux.syscall1(.set_thread_area, addr);
161161 assert(rc == 0);
162162 },
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.zig+1-1
......@@ -327,7 +327,7 @@ fn _start() callconv(.Naked) noreturn {
327327 : [argc] "={sp}" (-> [*]usize),
328328 );
329329 },
330 .mips, .mipsel => {
330 .mips, .mipsel, .mips64, .mips64el => {
331331 // The lr is already zeroed on entry, as specified by the ABI.
332332 argc_argv_ptr = asm volatile (
333333 \\ move $fp, $0
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+553
......@@ -1880,6 +1880,559 @@ pub const Target = struct {
18801880 => 16,
18811881 };
18821882 }
1883
1884 pub const CType = enum {
1885 short,
1886 ushort,
1887 int,
1888 uint,
1889 long,
1890 ulong,
1891 longlong,
1892 ulonglong,
1893 float,
1894 double,
1895 longdouble,
1896 };
1897
1898 pub fn c_type_byte_size(t: Target, c_type: CType) u16 {
1899 return switch (c_type) {
1900 .short,
1901 .ushort,
1902 .int,
1903 .uint,
1904 .long,
1905 .ulong,
1906 .longlong,
1907 .ulonglong,
1908 => @divExact(c_type_bit_size(t, c_type), 8),
1909
1910 .float => 4,
1911 .double => 8,
1912
1913 .longdouble => switch (c_type_bit_size(t, c_type)) {
1914 16 => 2,
1915 32 => 4,
1916 64 => 8,
1917 80 => @intCast(u16, mem.alignForward(10, c_type_alignment(t, .longdouble))),
1918 128 => 16,
1919 else => unreachable,
1920 },
1921 };
1922 }
1923
1924 pub fn c_type_bit_size(target: Target, c_type: CType) u16 {
1925 switch (target.os.tag) {
1926 .freestanding, .other => switch (target.cpu.arch) {
1927 .msp430 => switch (c_type) {
1928 .short, .ushort, .int, .uint => return 16,
1929 .float, .long, .ulong => return 32,
1930 .longlong, .ulonglong, .double, .longdouble => return 64,
1931 },
1932 .avr => switch (c_type) {
1933 .short, .ushort, .int, .uint => return 16,
1934 .long, .ulong, .float, .double, .longdouble => return 32,
1935 .longlong, .ulonglong => return 64,
1936 },
1937 .tce, .tcele => switch (c_type) {
1938 .short, .ushort => return 16,
1939 .int, .uint, .long, .ulong, .longlong, .ulonglong => return 32,
1940 .float, .double, .longdouble => return 32,
1941 },
1942 .mips64, .mips64el => switch (c_type) {
1943 .short, .ushort => return 16,
1944 .int, .uint, .float => return 32,
1945 .long, .ulong => return if (target.abi != .gnuabin32) 64 else 32,
1946 .longlong, .ulonglong, .double => return 64,
1947 .longdouble => return 128,
1948 },
1949 .x86_64 => switch (c_type) {
1950 .short, .ushort => return 16,
1951 .int, .uint, .float => return 32,
1952 .long, .ulong => switch (target.abi) {
1953 .gnux32, .muslx32 => return 32,
1954 else => return 64,
1955 },
1956 .longlong, .ulonglong, .double => return 64,
1957 .longdouble => return 80,
1958 },
1959 else => switch (c_type) {
1960 .short, .ushort => return 16,
1961 .int, .uint, .float => return 32,
1962 .long, .ulong => return target.cpu.arch.ptrBitWidth(),
1963 .longlong, .ulonglong, .double => return 64,
1964 .longdouble => switch (target.cpu.arch) {
1965 .x86 => switch (target.abi) {
1966 .android => return 64,
1967 else => return 80,
1968 },
1969
1970 .powerpc,
1971 .powerpcle,
1972 .powerpc64,
1973 .powerpc64le,
1974 => switch (target.abi) {
1975 .musl,
1976 .musleabi,
1977 .musleabihf,
1978 .muslx32,
1979 => return 64,
1980 else => return 128,
1981 },
1982
1983 .riscv32,
1984 .riscv64,
1985 .aarch64,
1986 .aarch64_be,
1987 .aarch64_32,
1988 .s390x,
1989 .sparc,
1990 .sparc64,
1991 .sparcel,
1992 .wasm32,
1993 .wasm64,
1994 => return 128,
1995
1996 else => return 64,
1997 },
1998 },
1999 },
2000
2001 .linux,
2002 .freebsd,
2003 .netbsd,
2004 .dragonfly,
2005 .openbsd,
2006 .wasi,
2007 .emscripten,
2008 .plan9,
2009 .solaris,
2010 .haiku,
2011 .ananas,
2012 .fuchsia,
2013 .minix,
2014 => switch (target.cpu.arch) {
2015 .msp430 => switch (c_type) {
2016 .short, .ushort, .int, .uint => return 16,
2017 .long, .ulong, .float => return 32,
2018 .longlong, .ulonglong, .double, .longdouble => return 64,
2019 },
2020 .avr => switch (c_type) {
2021 .short, .ushort, .int, .uint => return 16,
2022 .long, .ulong, .float, .double, .longdouble => return 32,
2023 .longlong, .ulonglong => return 64,
2024 },
2025 .tce, .tcele => switch (c_type) {
2026 .short, .ushort => return 16,
2027 .int, .uint, .long, .ulong, .longlong, .ulonglong => return 32,
2028 .float, .double, .longdouble => return 32,
2029 },
2030 .mips64, .mips64el => switch (c_type) {
2031 .short, .ushort => return 16,
2032 .int, .uint, .float => return 32,
2033 .long, .ulong => return if (target.abi != .gnuabin32) 64 else 32,
2034 .longlong, .ulonglong, .double => return 64,
2035 .longdouble => if (target.os.tag == .freebsd) return 64 else return 128,
2036 },
2037 .x86_64 => switch (c_type) {
2038 .short, .ushort => return 16,
2039 .int, .uint, .float => return 32,
2040 .long, .ulong => switch (target.abi) {
2041 .gnux32, .muslx32 => return 32,
2042 else => return 64,
2043 },
2044 .longlong, .ulonglong, .double => return 64,
2045 .longdouble => return 80,
2046 },
2047 else => switch (c_type) {
2048 .short, .ushort => return 16,
2049 .int, .uint, .float => return 32,
2050 .long, .ulong => return target.cpu.arch.ptrBitWidth(),
2051 .longlong, .ulonglong, .double => return 64,
2052 .longdouble => switch (target.cpu.arch) {
2053 .x86 => switch (target.abi) {
2054 .android => return 64,
2055 else => return 80,
2056 },
2057
2058 .powerpc,
2059 .powerpcle,
2060 => switch (target.abi) {
2061 .musl,
2062 .musleabi,
2063 .musleabihf,
2064 .muslx32,
2065 => return 64,
2066 else => switch (target.os.tag) {
2067 .freebsd, .netbsd, .openbsd => return 64,
2068 else => return 128,
2069 },
2070 },
2071
2072 .powerpc64,
2073 .powerpc64le,
2074 => switch (target.abi) {
2075 .musl,
2076 .musleabi,
2077 .musleabihf,
2078 .muslx32,
2079 => return 64,
2080 else => switch (target.os.tag) {
2081 .freebsd, .openbsd => return 64,
2082 else => return 128,
2083 },
2084 },
2085
2086 .riscv32,
2087 .riscv64,
2088 .aarch64,
2089 .aarch64_be,
2090 .aarch64_32,
2091 .s390x,
2092 .mips64,
2093 .mips64el,
2094 .sparc,
2095 .sparc64,
2096 .sparcel,
2097 .wasm32,
2098 .wasm64,
2099 => return 128,
2100
2101 else => return 64,
2102 },
2103 },
2104 },
2105
2106 .windows, .uefi => switch (target.cpu.arch) {
2107 .x86 => switch (c_type) {
2108 .short, .ushort => return 16,
2109 .int, .uint, .float => return 32,
2110 .long, .ulong => return 32,
2111 .longlong, .ulonglong, .double => return 64,
2112 .longdouble => switch (target.abi) {
2113 .gnu, .gnuilp32, .cygnus => return 80,
2114 else => return 64,
2115 },
2116 },
2117 .x86_64 => switch (c_type) {
2118 .short, .ushort => return 16,
2119 .int, .uint, .float => return 32,
2120 .long, .ulong => switch (target.abi) {
2121 .cygnus => return 64,
2122 else => return 32,
2123 },
2124 .longlong, .ulonglong, .double => return 64,
2125 .longdouble => switch (target.abi) {
2126 .gnu, .gnuilp32, .cygnus => return 80,
2127 else => return 64,
2128 },
2129 },
2130 else => switch (c_type) {
2131 .short, .ushort => return 16,
2132 .int, .uint, .float => return 32,
2133 .long, .ulong => return 32,
2134 .longlong, .ulonglong, .double => return 64,
2135 .longdouble => return 64,
2136 },
2137 },
2138
2139 .macos, .ios, .tvos, .watchos => switch (c_type) {
2140 .short, .ushort => return 16,
2141 .int, .uint, .float => return 32,
2142 .long, .ulong => switch (target.cpu.arch) {
2143 .x86, .arm, .aarch64_32 => return 32,
2144 .x86_64 => switch (target.abi) {
2145 .gnux32, .muslx32 => return 32,
2146 else => return 64,
2147 },
2148 else => return 64,
2149 },
2150 .longlong, .ulonglong, .double => return 64,
2151 .longdouble => switch (target.cpu.arch) {
2152 .x86 => switch (target.abi) {
2153 .android => return 64,
2154 else => return 80,
2155 },
2156 .x86_64 => return 80,
2157 else => return 64,
2158 },
2159 },
2160
2161 .nvcl, .cuda => switch (c_type) {
2162 .short, .ushort => return 16,
2163 .int, .uint, .float => return 32,
2164 .long, .ulong => switch (target.cpu.arch) {
2165 .nvptx => return 32,
2166 .nvptx64 => return 64,
2167 else => return 64,
2168 },
2169 .longlong, .ulonglong, .double => return 64,
2170 .longdouble => return 64,
2171 },
2172
2173 .amdhsa, .amdpal => switch (c_type) {
2174 .short, .ushort => return 16,
2175 .int, .uint, .float => return 32,
2176 .long, .ulong, .longlong, .ulonglong, .double => return 64,
2177 .longdouble => return 128,
2178 },
2179
2180 .cloudabi,
2181 .kfreebsd,
2182 .lv2,
2183 .zos,
2184 .rtems,
2185 .nacl,
2186 .aix,
2187 .ps4,
2188 .ps5,
2189 .elfiamcu,
2190 .mesa3d,
2191 .contiki,
2192 .hermit,
2193 .hurd,
2194 .opencl,
2195 .glsl450,
2196 .vulkan,
2197 .driverkit,
2198 .shadermodel,
2199 => @panic("TODO specify the C integer and float type sizes for this OS"),
2200 }
2201 }
2202
2203 pub fn c_type_alignment(target: Target, c_type: CType) u16 {
2204 // Overrides for unusual alignments
2205 switch (target.cpu.arch) {
2206 .avr => switch (c_type) {
2207 .short, .ushort => return 2,
2208 else => return 1,
2209 },
2210 .x86 => switch (target.os.tag) {
2211 .windows, .uefi => switch (c_type) {
2212 .longlong, .ulonglong, .double => return 8,
2213 .longdouble => switch (target.abi) {
2214 .gnu, .gnuilp32, .cygnus => return 4,
2215 else => return 8,
2216 },
2217 else => {},
2218 },
2219 else => {},
2220 },
2221 else => {},
2222 }
2223
2224 // Next-power-of-two-aligned, up to a maximum.
2225 return @min(
2226 std.math.ceilPowerOfTwoAssert(u16, (c_type_bit_size(target, c_type) + 7) / 8),
2227 switch (target.cpu.arch) {
2228 .arm, .armeb, .thumb, .thumbeb => switch (target.os.tag) {
2229 .netbsd => switch (target.abi) {
2230 .gnueabi,
2231 .gnueabihf,
2232 .eabi,
2233 .eabihf,
2234 .android,
2235 .musleabi,
2236 .musleabihf,
2237 => 8,
2238
2239 else => @as(u16, 4),
2240 },
2241 .ios, .tvos, .watchos => 4,
2242 else => 8,
2243 },
2244
2245 .msp430,
2246 .avr,
2247 => 2,
2248
2249 .arc,
2250 .csky,
2251 .x86,
2252 .xcore,
2253 .dxil,
2254 .loongarch32,
2255 .tce,
2256 .tcele,
2257 .le32,
2258 .amdil,
2259 .hsail,
2260 .spir,
2261 .spirv32,
2262 .kalimba,
2263 .shave,
2264 .renderscript32,
2265 .ve,
2266 .spu_2,
2267 => 4,
2268
2269 .aarch64_32,
2270 .amdgcn,
2271 .amdil64,
2272 .bpfel,
2273 .bpfeb,
2274 .hexagon,
2275 .hsail64,
2276 .loongarch64,
2277 .m68k,
2278 .mips,
2279 .mipsel,
2280 .sparc,
2281 .sparcel,
2282 .sparc64,
2283 .lanai,
2284 .le64,
2285 .nvptx,
2286 .nvptx64,
2287 .r600,
2288 .s390x,
2289 .spir64,
2290 .spirv64,
2291 .renderscript64,
2292 => 8,
2293
2294 .aarch64,
2295 .aarch64_be,
2296 .mips64,
2297 .mips64el,
2298 .powerpc,
2299 .powerpcle,
2300 .powerpc64,
2301 .powerpc64le,
2302 .riscv32,
2303 .riscv64,
2304 .x86_64,
2305 .wasm32,
2306 .wasm64,
2307 => 16,
2308 },
2309 );
2310 }
2311
2312 pub fn c_type_preferred_alignment(target: Target, c_type: CType) u16 {
2313 // Overrides for unusual alignments
2314 switch (target.cpu.arch) {
2315 .arm, .armeb, .thumb, .thumbeb => switch (target.os.tag) {
2316 .netbsd => switch (target.abi) {
2317 .gnueabi,
2318 .gnueabihf,
2319 .eabi,
2320 .eabihf,
2321 .android,
2322 .musleabi,
2323 .musleabihf,
2324 => {},
2325
2326 else => switch (c_type) {
2327 .longdouble => return 4,
2328 else => {},
2329 },
2330 },
2331 .ios, .tvos, .watchos => switch (c_type) {
2332 .longdouble => return 4,
2333 else => {},
2334 },
2335 else => {},
2336 },
2337 .arc => switch (c_type) {
2338 .longdouble => return 4,
2339 else => {},
2340 },
2341 .avr => switch (c_type) {
2342 .int, .uint, .long, .ulong, .float, .longdouble => return 1,
2343 .short, .ushort => return 2,
2344 .double => return 4,
2345 .longlong, .ulonglong => return 8,
2346 },
2347 .x86 => switch (target.os.tag) {
2348 .windows, .uefi => switch (c_type) {
2349 .longdouble => switch (target.abi) {
2350 .gnu, .gnuilp32, .cygnus => return 4,
2351 else => return 8,
2352 },
2353 else => {},
2354 },
2355 else => switch (c_type) {
2356 .longdouble => return 4,
2357 else => {},
2358 },
2359 },
2360 else => {},
2361 }
2362
2363 // Next-power-of-two-aligned, up to a maximum.
2364 return @min(
2365 std.math.ceilPowerOfTwoAssert(u16, (c_type_bit_size(target, c_type) + 7) / 8),
2366 switch (target.cpu.arch) {
2367 .msp430 => @as(u16, 2),
2368
2369 .csky,
2370 .xcore,
2371 .dxil,
2372 .loongarch32,
2373 .tce,
2374 .tcele,
2375 .le32,
2376 .amdil,
2377 .hsail,
2378 .spir,
2379 .spirv32,
2380 .kalimba,
2381 .shave,
2382 .renderscript32,
2383 .ve,
2384 .spu_2,
2385 => 4,
2386
2387 .arc,
2388 .arm,
2389 .armeb,
2390 .avr,
2391 .thumb,
2392 .thumbeb,
2393 .aarch64_32,
2394 .amdgcn,
2395 .amdil64,
2396 .bpfel,
2397 .bpfeb,
2398 .hexagon,
2399 .hsail64,
2400 .x86,
2401 .loongarch64,
2402 .m68k,
2403 .mips,
2404 .mipsel,
2405 .sparc,
2406 .sparcel,
2407 .sparc64,
2408 .lanai,
2409 .le64,
2410 .nvptx,
2411 .nvptx64,
2412 .r600,
2413 .s390x,
2414 .spir64,
2415 .spirv64,
2416 .renderscript64,
2417 => 8,
2418
2419 .aarch64,
2420 .aarch64_be,
2421 .mips64,
2422 .mips64el,
2423 .powerpc,
2424 .powerpcle,
2425 .powerpc64,
2426 .powerpc64le,
2427 .riscv32,
2428 .riscv64,
2429 .x86_64,
2430 .wasm32,
2431 .wasm64,
2432 => 16,
2433 },
2434 );
2435 }
18832436};
18842437
18852438test {
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+2-2
......@@ -6073,7 +6073,7 @@ var fixed_buffer_mem: [100 * 1024]u8 = undefined;
60736073fn testParse(source: [:0]const u8, allocator: mem.Allocator, anything_changed: *bool) ![]u8 {
60746074 const stderr = io.getStdErr().writer();
60756075
6076 var tree = try std.zig.parse(allocator, source);
6076 var tree = try std.zig.Ast.parse(allocator, source, .zig);
60776077 defer tree.deinit(allocator);
60786078
60796079 for (tree.errors) |parse_error| {
......@@ -6124,7 +6124,7 @@ fn testCanonical(source: [:0]const u8) !void {
61246124const Error = std.zig.Ast.Error.Tag;
61256125
61266126fn testError(source: [:0]const u8, expected_errors: []const Error) !void {
6127 var tree = try std.zig.parse(std.testing.allocator, source);
6127 var tree = try std.zig.Ast.parse(std.testing.allocator, source, .zig);
61286128 defer tree.deinit(std.testing.allocator);
61296129
61306130 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}
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+51-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,17 +531,6 @@ 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,
546
547534 /// The shallow set of other decls whose typed_value could possibly change if this Decl's
548535 /// typed_value is modified.
549536 dependants: DepsTable = .{},
......@@ -2067,7 +2054,7 @@ pub const File = struct {
20672054 if (file.tree_loaded) return &file.tree;
20682055
20692056 const source = try file.getSource(gpa);
2070 file.tree = try std.zig.parse(gpa, source.bytes);
2057 file.tree = try Ast.parse(gpa, source.bytes, .zig);
20712058 file.tree_loaded = true;
20722059 return &file.tree;
20732060 }
......@@ -3672,7 +3659,7 @@ pub fn astGenFile(mod: *Module, file: *File) !void {
36723659 file.source = source;
36733660 file.source_loaded = true;
36743661
3675 file.tree = try std.zig.parse(gpa, source);
3662 file.tree = try Ast.parse(gpa, source, .zig);
36763663 defer if (!file.tree_loaded) file.tree.deinit(gpa);
36773664
36783665 if (file.tree.errors.len != 0) {
......@@ -3987,7 +3974,7 @@ pub fn populateBuiltinFile(mod: *Module) !void {
39873974 else => |e| return e,
39883975 }
39893976
3990 file.tree = try std.zig.parse(gpa, file.source);
3977 file.tree = try Ast.parse(gpa, file.source, .zig);
39913978 file.tree_loaded = true;
39923979 assert(file.tree.errors.len == 0); // builtin.zig must parse
39933980
......@@ -4098,7 +4085,7 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl_index: Decl.Index) SemaError!void {
40984085
40994086 // The exports this Decl performs will be re-discovered, so we remove them here
41004087 // prior to re-analysis.
4101 mod.deleteDeclExports(decl_index);
4088 try mod.deleteDeclExports(decl_index);
41024089
41034090 // Similarly, `@setAlignStack` invocations will be re-discovered.
41044091 if (decl.getFunction()) |func| {
......@@ -4878,14 +4865,31 @@ pub fn importFile(
48784865 };
48794866}
48804867
4881pub fn embedFile(mod: *Module, cur_file: *File, rel_file_path: []const u8) !*EmbedFile {
4868pub fn embedFile(mod: *Module, cur_file: *File, import_string: []const u8) !*EmbedFile {
48824869 const gpa = mod.gpa;
48834870
4884 // The resolved path is used as the key in the table, to detect if
4885 // a file refers to the same as another, despite different relative paths.
4871 if (cur_file.pkg.table.get(import_string)) |pkg| {
4872 const resolved_path = try std.fs.path.resolve(gpa, &[_][]const u8{
4873 pkg.root_src_directory.path orelse ".", pkg.root_src_path,
4874 });
4875 var keep_resolved_path = false;
4876 defer if (!keep_resolved_path) gpa.free(resolved_path);
4877
4878 const gop = try mod.embed_table.getOrPut(gpa, resolved_path);
4879 errdefer assert(mod.embed_table.remove(resolved_path));
4880 if (gop.found_existing) return gop.value_ptr.*;
4881
4882 const sub_file_path = try gpa.dupe(u8, pkg.root_src_path);
4883 errdefer gpa.free(sub_file_path);
4884
4885 return newEmbedFile(mod, pkg, sub_file_path, resolved_path, &keep_resolved_path, gop);
4886 }
4887
4888 // The resolved path is used as the key in the table, to detect if a file
4889 // refers to the same as another, despite different relative paths.
48864890 const cur_pkg_dir_path = cur_file.pkg.root_src_directory.path orelse ".";
48874891 const resolved_path = try std.fs.path.resolve(gpa, &[_][]const u8{
4888 cur_pkg_dir_path, cur_file.sub_file_path, "..", rel_file_path,
4892 cur_pkg_dir_path, cur_file.sub_file_path, "..", import_string,
48894893 });
48904894 var keep_resolved_path = false;
48914895 defer if (!keep_resolved_path) gpa.free(resolved_path);
......@@ -4894,9 +4898,6 @@ pub fn embedFile(mod: *Module, cur_file: *File, rel_file_path: []const u8) !*Emb
48944898 errdefer assert(mod.embed_table.remove(resolved_path));
48954899 if (gop.found_existing) return gop.value_ptr.*;
48964900
4897 const new_file = try gpa.create(EmbedFile);
4898 errdefer gpa.destroy(new_file);
4899
49004901 const resolved_root_path = try std.fs.path.resolve(gpa, &[_][]const u8{cur_pkg_dir_path});
49014902 defer gpa.free(resolved_root_path);
49024903
......@@ -4915,7 +4916,23 @@ pub fn embedFile(mod: *Module, cur_file: *File, rel_file_path: []const u8) !*Emb
49154916 };
49164917 errdefer gpa.free(sub_file_path);
49174918
4918 var file = try cur_file.pkg.root_src_directory.handle.openFile(sub_file_path, .{});
4919 return newEmbedFile(mod, cur_file.pkg, sub_file_path, resolved_path, &keep_resolved_path, gop);
4920}
4921
4922fn newEmbedFile(
4923 mod: *Module,
4924 pkg: *Package,
4925 sub_file_path: []const u8,
4926 resolved_path: []const u8,
4927 keep_resolved_path: *bool,
4928 gop: std.StringHashMapUnmanaged(*EmbedFile).GetOrPutResult,
4929) !*EmbedFile {
4930 const gpa = mod.gpa;
4931
4932 const new_file = try gpa.create(EmbedFile);
4933 errdefer gpa.destroy(new_file);
4934
4935 var file = try pkg.root_src_directory.handle.openFile(sub_file_path, .{});
49194936 defer file.close();
49204937
49214938 const actual_stat = try file.stat();
......@@ -4928,10 +4945,6 @@ pub fn embedFile(mod: *Module, cur_file: *File, rel_file_path: []const u8) !*Emb
49284945 const bytes = try file.readToEndAllocOptions(gpa, std.math.maxInt(u32), size_usize, 1, 0);
49294946 errdefer gpa.free(bytes);
49304947
4931 log.debug("new embedFile. resolved_root_path={s}, resolved_path={s}, sub_file_path={s}, rel_file_path={s}", .{
4932 resolved_root_path, resolved_path, sub_file_path, rel_file_path,
4933 });
4934
49354948 if (mod.comp.whole_cache_manifest) |whole_cache_manifest| {
49364949 const copied_resolved_path = try gpa.dupe(u8, resolved_path);
49374950 errdefer gpa.free(copied_resolved_path);
......@@ -4940,13 +4953,13 @@ pub fn embedFile(mod: *Module, cur_file: *File, rel_file_path: []const u8) !*Emb
49404953 try whole_cache_manifest.addFilePostContents(copied_resolved_path, bytes, stat);
49414954 }
49424955
4943 keep_resolved_path = true; // It's now owned by embed_table.
4956 keep_resolved_path.* = true; // It's now owned by embed_table.
49444957 gop.value_ptr.* = new_file;
49454958 new_file.* = .{
49464959 .sub_file_path = sub_file_path,
49474960 .bytes = bytes,
49484961 .stat = stat,
4949 .pkg = cur_file.pkg,
4962 .pkg = pkg,
49504963 .owner_decl = undefined, // Set by Sema immediately after this function returns.
49514964 };
49524965 return new_file;
......@@ -5183,20 +5196,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) Allocator.Err
51835196 decl.zir_decl_index = @intCast(u32, decl_sub_index);
51845197 if (decl.getFunction()) |_| {
51855198 switch (comp.bin_file.tag) {
5186 .coff => {
5187 // TODO Implement for COFF
5188 },
5189 .elf => if (decl.fn_link.elf.len != 0) {
5190 // TODO Look into detecting when this would be unnecessary by storing enough state
5191 // in `Decl` to notice that the line number did not change.
5192 comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl_index });
5193 },
5194 .macho => if (decl.fn_link.macho.len != 0) {
5195 // TODO Look into detecting when this would be unnecessary by storing enough state
5196 // in `Decl` to notice that the line number did not change.
5197 comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl_index });
5198 },
5199 .plan9 => {
5199 .coff, .elf, .macho, .plan9 => {
52005200 // TODO Look into detecting when this would be unnecessary by storing enough state
52015201 // in `Decl` to notice that the line number did not change.
52025202 comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl_index });
......@@ -5265,34 +5265,11 @@ pub fn clearDecl(
52655265 assert(emit_h.decl_table.swapRemove(decl_index));
52665266 }
52675267 _ = mod.compile_log_decls.swapRemove(decl_index);
5268 mod.deleteDeclExports(decl_index);
5268 try mod.deleteDeclExports(decl_index);
52695269
52705270 if (decl.has_tv) {
52715271 if (decl.ty.isFnOrHasRuntimeBits()) {
52725272 mod.comp.bin_file.freeDecl(decl_index);
5273
5274 // TODO instead of a union, put this memory trailing Decl objects,
5275 // and allow it to be variably sized.
5276 decl.link = switch (mod.comp.bin_file.tag) {
5277 .coff => .{ .coff = link.File.Coff.Atom.empty },
5278 .elf => .{ .elf = link.File.Elf.TextBlock.empty },
5279 .macho => .{ .macho = link.File.MachO.Atom.empty },
5280 .plan9 => .{ .plan9 = link.File.Plan9.DeclBlock.empty },
5281 .c => .{ .c = {} },
5282 .wasm => .{ .wasm = link.File.Wasm.DeclBlock.empty },
5283 .spirv => .{ .spirv = {} },
5284 .nvptx => .{ .nvptx = {} },
5285 };
5286 decl.fn_link = switch (mod.comp.bin_file.tag) {
5287 .coff => .{ .coff = {} },
5288 .elf => .{ .elf = link.File.Dwarf.SrcFn.empty },
5289 .macho => .{ .macho = link.File.Dwarf.SrcFn.empty },
5290 .plan9 => .{ .plan9 = {} },
5291 .c => .{ .c = {} },
5292 .wasm => .{ .wasm = link.File.Wasm.FnData.empty },
5293 .spirv => .{ .spirv = .{} },
5294 .nvptx => .{ .nvptx = {} },
5295 };
52965273 }
52975274 if (decl.getInnerNamespace()) |namespace| {
52985275 try namespace.deleteAllDecls(mod, outdated_decls);
......@@ -5358,7 +5335,7 @@ pub fn abortAnonDecl(mod: *Module, decl_index: Decl.Index) void {
53585335
53595336/// Delete all the Export objects that are caused by this Decl. Re-analysis of
53605337/// this Decl will cause them to be re-created (or not).
5361fn deleteDeclExports(mod: *Module, decl_index: Decl.Index) void {
5338fn deleteDeclExports(mod: *Module, decl_index: Decl.Index) Allocator.Error!void {
53625339 var export_owners = (mod.export_owners.fetchSwapRemove(decl_index) orelse return).value;
53635340
53645341 for (export_owners.items) |exp| {
......@@ -5381,16 +5358,16 @@ fn deleteDeclExports(mod: *Module, decl_index: Decl.Index) void {
53815358 }
53825359 }
53835360 if (mod.comp.bin_file.cast(link.File.Elf)) |elf| {
5384 elf.deleteExport(exp.link.elf);
5361 elf.deleteDeclExport(decl_index, exp.options.name);
53855362 }
53865363 if (mod.comp.bin_file.cast(link.File.MachO)) |macho| {
5387 macho.deleteExport(exp.link.macho);
5364 try macho.deleteDeclExport(decl_index, exp.options.name);
53885365 }
53895366 if (mod.comp.bin_file.cast(link.File.Wasm)) |wasm| {
5390 wasm.deleteExport(exp.link.wasm);
5367 wasm.deleteDeclExport(decl_index);
53915368 }
53925369 if (mod.comp.bin_file.cast(link.File.Coff)) |coff| {
5393 coff.deleteExport(exp.link.coff);
5370 coff.deleteDeclExport(decl_index, exp.options.name);
53945371 }
53955372 if (mod.failed_exports.fetchSwapRemove(exp)) |failed_kv| {
53965373 failed_kv.value.destroy(mod.gpa);
......@@ -5693,26 +5670,6 @@ pub fn allocateNewDecl(
56935670 .deletion_flag = false,
56945671 .zir_decl_index = 0,
56955672 .src_scope = src_scope,
5696 .link = switch (mod.comp.bin_file.tag) {
5697 .coff => .{ .coff = link.File.Coff.Atom.empty },
5698 .elf => .{ .elf = link.File.Elf.TextBlock.empty },
5699 .macho => .{ .macho = link.File.MachO.Atom.empty },
5700 .plan9 => .{ .plan9 = link.File.Plan9.DeclBlock.empty },
5701 .c => .{ .c = {} },
5702 .wasm => .{ .wasm = link.File.Wasm.DeclBlock.empty },
5703 .spirv => .{ .spirv = {} },
5704 .nvptx => .{ .nvptx = {} },
5705 },
5706 .fn_link = switch (mod.comp.bin_file.tag) {
5707 .coff => .{ .coff = {} },
5708 .elf => .{ .elf = link.File.Dwarf.SrcFn.empty },
5709 .macho => .{ .macho = link.File.Dwarf.SrcFn.empty },
5710 .plan9 => .{ .plan9 = {} },
5711 .c => .{ .c = {} },
5712 .wasm => .{ .wasm = link.File.Wasm.FnData.empty },
5713 .spirv => .{ .spirv = .{} },
5714 .nvptx => .{ .nvptx = {} },
5715 },
57165673 .generation = 0,
57175674 .is_pub = false,
57185675 .is_exported = false,
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+93-20
......@@ -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,
......@@ -6884,6 +6875,8 @@ fn analyzeInlineCallArg(
68846875 if (err == error.AnalysisFail and param_block.comptime_reason != null) try param_block.comptime_reason.?.explain(sema, sema.err);
68856876 return err;
68866877 };
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");
68876880 }
68886881 const casted_arg = sema.coerceExtra(arg_block, param_ty, uncasted_arg, arg_src, .{ .param_src = .{
68896882 .func_inst = func_inst,
......@@ -6957,6 +6950,9 @@ fn analyzeInlineCallArg(
69576950 .val = arg_val,
69586951 };
69596952 } else {
6953 if (zir_tags[inst] == .param_anytype_comptime) {
6954 _ = try sema.resolveConstMaybeUndefVal(arg_block, arg_src, uncasted_arg, "parameter is comptime");
6955 }
69606956 sema.inst_map.putAssumeCapacityNoClobber(inst, uncasted_arg);
69616957 }
69626958
......@@ -8477,7 +8473,7 @@ fn handleExternLibName(
84778473 return sema.fail(
84788474 block,
84798475 src_loc,
8480 "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'.",
84818477 .{ lib_name, lib_name },
84828478 );
84838479 }
......@@ -9014,7 +9010,18 @@ fn zirParam(
90149010 if (is_comptime and sema.preallocated_new_func != null) {
90159011 // We have a comptime value for this parameter so it should be elided from the
90169012 // function type of the function instruction in this block.
9017 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 };
90189025 sema.inst_map.putAssumeCapacity(inst, coerced_arg);
90199026 return;
90209027 }
......@@ -19529,13 +19536,34 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
1952919536 const operand_info = operand_ty.ptrInfo().data;
1953019537 const dest_info = dest_ty.ptrInfo().data;
1953119538 if (!operand_info.mutable and dest_info.mutable) {
19532 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);
1953319547 }
1953419548 if (operand_info.@"volatile" and !dest_info.@"volatile") {
19535 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);
1953619557 }
1953719558 if (operand_info.@"addrspace" != dest_info.@"addrspace") {
19538 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);
1953919567 }
1954019568
1954119569 const dest_is_slice = dest_ty.isSlice();
......@@ -19590,6 +19618,8 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
1959019618 try sema.errNote(block, dest_ty_src, msg, "'{}' has alignment '{d}'", .{
1959119619 dest_ty.fmt(sema.mod), dest_align,
1959219620 });
19621
19622 try sema.errNote(block, src, msg, "consider using '@alignCast'", .{});
1959319623 break :msg msg;
1959419624 };
1959519625 return sema.failWithOwnedErrorMsg(msg);
......@@ -19625,6 +19655,49 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
1962519655 return block.addBitCast(aligned_dest_ty, ptr);
1962619656}
1962719657
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
1962819701fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
1962919702 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
1963019703 const src = inst_data.src();
......@@ -25141,7 +25214,7 @@ fn coerceExtra(
2514125214 (try sema.coerceInMemoryAllowed(block, inst_ty.errorUnionPayload(), dest_ty, false, target, dest_ty_src, inst_src)) == .ok)
2514225215 {
2514325216 try sema.errNote(block, inst_src, msg, "cannot convert error union to payload type", .{});
25144 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'", .{});
2514525218 }
2514625219
2514725220 // ?T to T
......@@ -25150,7 +25223,7 @@ fn coerceExtra(
2515025223 (try sema.coerceInMemoryAllowed(block, inst_ty.optionalChild(&buf), dest_ty, false, target, dest_ty_src, inst_src)) == .ok)
2515125224 {
2515225225 try sema.errNote(block, inst_src, msg, "cannot convert optional to payload type", .{});
25153 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'", .{});
2515425227 }
2515525228
2515625229 try in_memory_result.report(sema, block, inst_src, msg);
......@@ -26076,7 +26149,7 @@ fn coerceVarArgParam(
2607626149 .Array => return sema.fail(block, inst_src, "arrays must be passed by reference to variadic function", .{}),
2607726150 .Float => float: {
2607826151 const target = sema.mod.getTarget();
26079 const double_bits = @import("type.zig").CType.sizeInBits(.double, target);
26152 const double_bits = target.c_type_bit_size(.double);
2608026153 const inst_bits = uncasted_ty.floatBits(sema.mod.getTarget());
2608126154 if (inst_bits >= double_bits) break :float inst;
2608226155 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+70-51
......@@ -203,13 +203,7 @@ const DbgInfoReloc = struct {
203203 else => unreachable, // not a possible argument
204204
205205 };
206 try dw.genArgDbgInfo(
207 reloc.name,
208 reloc.ty,
209 function.bin_file.tag,
210 function.mod_fn.owner_decl,
211 loc,
212 );
206 try dw.genArgDbgInfo(reloc.name, reloc.ty, function.mod_fn.owner_decl, loc);
213207 },
214208 .plan9 => {},
215209 .none => {},
......@@ -255,14 +249,7 @@ const DbgInfoReloc = struct {
255249 break :blk .nop;
256250 },
257251 };
258 try dw.genVarDbgInfo(
259 reloc.name,
260 reloc.ty,
261 function.bin_file.tag,
262 function.mod_fn.owner_decl,
263 is_ptr,
264 loc,
265 );
252 try dw.genVarDbgInfo(reloc.name, reloc.ty, function.mod_fn.owner_decl, is_ptr, loc);
266253 },
267254 .plan9 => {},
268255 .none => {},
......@@ -4019,11 +4006,17 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
40194006 .direct => .load_memory_ptr_direct,
40204007 .import => unreachable,
40214008 };
4022 const mod = self.bin_file.options.module.?;
4023 const owner_decl = mod.declPtr(self.mod_fn.owner_decl);
40244009 const atom_index = switch (self.bin_file.tag) {
4025 .macho => owner_decl.link.macho.getSymbolIndex().?,
4026 .coff => owner_decl.link.coff.getSymbolIndex().?,
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 },
40274020 else => unreachable, // unsupported target format
40284021 };
40294022 _ = try self.addInst(.{
......@@ -4301,34 +4294,37 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
43014294 if (self.air.value(callee)) |func_value| {
43024295 if (func_value.castTag(.function)) |func_payload| {
43034296 const func = func_payload.data;
4304 const fn_owner_decl = mod.declPtr(func.owner_decl);
43054297
43064298 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
4307 try fn_owner_decl.link.elf.ensureInitialized(elf_file);
4308 const got_addr = @intCast(u32, fn_owner_decl.link.elf.getOffsetTableAddress(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));
43094302 try self.genSetReg(Type.initTag(.usize), .x30, .{ .memory = got_addr });
43104303 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
4311 try fn_owner_decl.link.macho.ensureInitialized(macho_file);
4304 const atom = try macho_file.getOrCreateAtomForDecl(func.owner_decl);
4305 const sym_index = macho_file.getAtom(atom).getSymbolIndex().?;
43124306 try self.genSetReg(Type.initTag(.u64), .x30, .{
43134307 .linker_load = .{
43144308 .type = .got,
4315 .sym_index = fn_owner_decl.link.macho.getSymbolIndex().?,
4309 .sym_index = sym_index,
43164310 },
43174311 });
43184312 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
4319 try fn_owner_decl.link.coff.ensureInitialized(coff_file);
4313 const atom = try coff_file.getOrCreateAtomForDecl(func.owner_decl);
4314 const sym_index = coff_file.getAtom(atom).getSymbolIndex().?;
43204315 try self.genSetReg(Type.initTag(.u64), .x30, .{
43214316 .linker_load = .{
43224317 .type = .got,
4323 .sym_index = fn_owner_decl.link.coff.getSymbolIndex().?,
4318 .sym_index = sym_index,
43244319 },
43254320 });
43264321 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
4327 try p9.seeDecl(func.owner_decl);
4322 const decl_block_index = try p9.seeDecl(func.owner_decl);
4323 const decl_block = p9.getDeclBlock(decl_block_index);
43284324 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
43294325 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
43304326 const got_addr = p9.bases.data;
4331 const got_index = fn_owner_decl.link.plan9.got_index.?;
4327 const got_index = decl_block.got_index.?;
43324328 const fn_got_addr = got_addr + got_index * ptr_bytes;
43334329 try self.genSetReg(Type.initTag(.usize), .x30, .{ .memory = fn_got_addr });
43344330 } else unreachable;
......@@ -4349,11 +4345,13 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
43494345
43504346 if (self.bin_file.cast(link.File.MachO)) |macho_file| {
43514347 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().?;
43524350 _ = try self.addInst(.{
43534351 .tag = .call_extern,
43544352 .data = .{
43554353 .relocation = .{
4356 .atom_index = mod.declPtr(self.mod_fn.owner_decl).link.macho.getSymbolIndex().?,
4354 .atom_index = atom_index,
43574355 .sym_index = sym_index,
43584356 },
43594357 },
......@@ -5488,11 +5486,17 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
54885486 .direct => .load_memory_ptr_direct,
54895487 .import => unreachable,
54905488 };
5491 const mod = self.bin_file.options.module.?;
5492 const owner_decl = mod.declPtr(self.mod_fn.owner_decl);
54935489 const atom_index = switch (self.bin_file.tag) {
5494 .macho => owner_decl.link.macho.getSymbolIndex().?,
5495 .coff => owner_decl.link.coff.getSymbolIndex().?,
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 },
54965500 else => unreachable, // unsupported target format
54975501 };
54985502 _ = try self.addInst(.{
......@@ -5602,11 +5606,17 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
56025606 .direct => .load_memory_direct,
56035607 .import => .load_memory_import,
56045608 };
5605 const mod = self.bin_file.options.module.?;
5606 const owner_decl = mod.declPtr(self.mod_fn.owner_decl);
56075609 const atom_index = switch (self.bin_file.tag) {
5608 .macho => owner_decl.link.macho.getSymbolIndex().?,
5609 .coff => owner_decl.link.coff.getSymbolIndex().?,
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 },
56105620 else => unreachable, // unsupported target format
56115621 };
56125622 _ = try self.addInst(.{
......@@ -5796,11 +5806,17 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I
57965806 .direct => .load_memory_ptr_direct,
57975807 .import => unreachable,
57985808 };
5799 const mod = self.bin_file.options.module.?;
5800 const owner_decl = mod.declPtr(self.mod_fn.owner_decl);
58015809 const atom_index = switch (self.bin_file.tag) {
5802 .macho => owner_decl.link.macho.getSymbolIndex().?,
5803 .coff => owner_decl.link.coff.getSymbolIndex().?,
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 },
58045820 else => unreachable, // unsupported target format
58055821 };
58065822 _ = try self.addInst(.{
......@@ -6119,23 +6135,27 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne
61196135 mod.markDeclAlive(decl);
61206136
61216137 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
6122 try decl.link.elf.ensureInitialized(elf_file);
6123 return MCValue{ .memory = decl.link.elf.getOffsetTableAddress(elf_file) };
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) };
61246141 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
6125 try decl.link.macho.ensureInitialized(macho_file);
6142 const atom = try macho_file.getOrCreateAtomForDecl(decl_index);
6143 const sym_index = macho_file.getAtom(atom).getSymbolIndex().?;
61266144 return MCValue{ .linker_load = .{
61276145 .type = .got,
6128 .sym_index = decl.link.macho.getSymbolIndex().?,
6146 .sym_index = sym_index,
61296147 } };
61306148 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
6131 try decl.link.coff.ensureInitialized(coff_file);
6149 const atom_index = try coff_file.getOrCreateAtomForDecl(decl_index);
6150 const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?;
61326151 return MCValue{ .linker_load = .{
61336152 .type = .got,
6134 .sym_index = decl.link.coff.getSymbolIndex().?,
6153 .sym_index = sym_index,
61356154 } };
61366155 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
6137 try p9.seeDecl(decl_index);
6138 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;
61396159 return MCValue{ .memory = got_addr };
61406160 } else {
61416161 return self.fail("TODO codegen non-ELF const Decl pointer", .{});
......@@ -6148,8 +6168,7 @@ fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue {
61486168 return self.fail("lowering unnamed constant failed: {s}", .{@errorName(err)});
61496169 };
61506170 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
6151 const vaddr = elf_file.local_symbols.items[local_sym_index].st_value;
6152 return MCValue{ .memory = vaddr };
6171 return MCValue{ .memory = elf_file.getSymbol(local_sym_index).st_value };
61536172 } else if (self.bin_file.cast(link.File.MachO)) |_| {
61546173 return MCValue{ .linker_load = .{
61556174 .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+12-25
......@@ -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 => {},
......@@ -4256,12 +4243,11 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
42564243 if (self.air.value(callee)) |func_value| {
42574244 if (func_value.castTag(.function)) |func_payload| {
42584245 const func = func_payload.data;
4259 const mod = self.bin_file.options.module.?;
4260 const fn_owner_decl = mod.declPtr(func.owner_decl);
42614246
42624247 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
4263 try fn_owner_decl.link.elf.ensureInitialized(elf_file);
4264 const got_addr = @intCast(u32, fn_owner_decl.link.elf.getOffsetTableAddress(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));
42654251 try self.genSetReg(Type.initTag(.usize), .lr, .{ .memory = got_addr });
42664252 } else if (self.bin_file.cast(link.File.MachO)) |_| {
42674253 unreachable; // unsupported architecture for MachO
......@@ -6084,15 +6070,17 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne
60846070 mod.markDeclAlive(decl);
60856071
60866072 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
6087 try decl.link.elf.ensureInitialized(elf_file);
6088 return MCValue{ .memory = decl.link.elf.getOffsetTableAddress(elf_file) };
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) };
60896076 } else if (self.bin_file.cast(link.File.MachO)) |_| {
60906077 unreachable; // unsupported architecture for MachO
60916078 } else if (self.bin_file.cast(link.File.Coff)) |_| {
60926079 return self.fail("TODO codegen COFF const Decl pointer", .{});
60936080 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
6094 try p9.seeDecl(decl_index);
6095 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;
60966084 return MCValue{ .memory = got_addr };
60976085 } else {
60986086 return self.fail("TODO codegen non-ELF const Decl pointer", .{});
......@@ -6106,8 +6094,7 @@ fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue {
61066094 return self.fail("lowering unnamed constant failed: {s}", .{@errorName(err)});
61076095 };
61086096 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
6109 const vaddr = elf_file.local_symbols.items[local_sym_index].st_value;
6110 return MCValue{ .memory = vaddr };
6097 return MCValue{ .memory = elf_file.getSymbol(local_sym_index).st_value };
61116098 } else if (self.bin_file.cast(link.File.MachO)) |_| {
61126099 unreachable;
61136100 } else if (self.bin_file.cast(link.File.Coff)) |_| {
src/arch/riscv64/CodeGen.zig+13-20
......@@ -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,12 +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 mod = self.bin_file.options.module.?;
1726 const fn_owner_decl = mod.declPtr(func.owner_decl);
1727 try fn_owner_decl.link.elf.ensureInitialized(elf_file);
1728 const got_addr = @intCast(u32, fn_owner_decl.link.elf.getOffsetTableAddress(elf_file));
1729
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));
17301723 try self.genSetReg(Type.initTag(.usize), .ra, .{ .memory = got_addr });
17311724 _ = try self.addInst(.{
17321725 .tag = .jalr,
......@@ -2553,17 +2546,17 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne
25532546 const decl = mod.declPtr(decl_index);
25542547 mod.markDeclAlive(decl);
25552548 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
2556 try decl.link.elf.ensureInitialized(elf_file);
2557 return MCValue{ .memory = decl.link.elf.getOffsetTableAddress(elf_file) };
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) };
25582552 } else if (self.bin_file.cast(link.File.MachO)) |_| {
2559 // TODO I'm hacking my way through here by repurposing .memory for storing
2560 // index to the GOT target symbol index.
2561 return MCValue{ .memory = decl.link.macho.sym_index };
2553 unreachable;
25622554 } else if (self.bin_file.cast(link.File.Coff)) |_| {
25632555 return self.fail("TODO codegen COFF const Decl pointer", .{});
25642556 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
2565 try p9.seeDecl(decl_index);
2566 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;
25672560 return MCValue{ .memory = got_addr };
25682561 } else {
25692562 return self.fail("TODO codegen non-ELF const Decl pointer", .{});
src/arch/sparc64/CodeGen.zig+9-13
......@@ -1216,11 +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 mod = self.bin_file.options.module.?;
1220 const fn_owner_decl = mod.declPtr(func.owner_decl);
12211219 const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: {
1222 try fn_owner_decl.link.elf.ensureInitialized(elf_file);
1223 break :blk @intCast(u32, fn_owner_decl.link.elf.getOffsetTableAddress(elf_file));
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));
12241223 } else unreachable;
12251224
12261225 try self.genSetReg(Type.initTag(.usize), .o7, .{ .memory = got_addr });
......@@ -3413,13 +3412,9 @@ fn genArgDbgInfo(self: Self, inst: Air.Inst.Index, mcv: MCValue) !void {
34133412
34143413 switch (self.debug_output) {
34153414 .dwarf => |dw| switch (mcv) {
3416 .register => |reg| try dw.genArgDbgInfo(
3417 name,
3418 ty,
3419 self.bin_file.tag,
3420 self.mod_fn.owner_decl,
3421 .{ .register = reg.dwarfLocOp() },
3422 ),
3415 .register => |reg| try dw.genArgDbgInfo(name, ty, self.mod_fn.owner_decl, .{
3416 .register = reg.dwarfLocOp(),
3417 }),
34233418 else => {},
34243419 },
34253420 else => {},
......@@ -4205,8 +4200,9 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne
42054200
42064201 mod.markDeclAlive(decl);
42074202 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
4208 try decl.link.elf.ensureInitialized(elf_file);
4209 return MCValue{ .memory = decl.link.elf.getOffsetTableAddress(elf_file) };
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) };
42104206 } else {
42114207 return self.fail("TODO codegen non-ELF const Decl pointer", .{});
42124208 }
src/arch/wasm/CodeGen.zig+20-20
......@@ -1194,7 +1194,7 @@ fn genFunc(func: *CodeGen) InnerError!void {
11941194 const fn_info = func.decl.ty.fnInfo();
11951195 var func_type = try genFunctype(func.gpa, fn_info.cc, fn_info.param_types, fn_info.return_type, func.target);
11961196 defer func_type.deinit(func.gpa);
1197 func.decl.fn_link.wasm.type_index = try func.bin_file.putOrGetFuncType(func_type);
1197 _ = try func.bin_file.storeDeclType(func.decl_index, func_type);
11981198
11991199 var cc_result = try func.resolveCallingConventionValues(func.decl.ty);
12001200 defer cc_result.deinit(func.gpa);
......@@ -1269,10 +1269,10 @@ fn genFunc(func: *CodeGen) InnerError!void {
12691269
12701270 var emit: Emit = .{
12711271 .mir = mir,
1272 .bin_file = &func.bin_file.base,
1272 .bin_file = func.bin_file,
12731273 .code = func.code,
12741274 .locals = func.locals.items,
1275 .decl = func.decl,
1275 .decl_index = func.decl_index,
12761276 .dbg_output = func.debug_output,
12771277 .prev_di_line = 0,
12781278 .prev_di_column = 0,
......@@ -2117,33 +2117,31 @@ fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModif
21172117 const fn_info = fn_ty.fnInfo();
21182118 const first_param_sret = firstParamSRet(fn_info.cc, fn_info.return_type, func.target);
21192119
2120 const callee: ?*Decl = blk: {
2120 const callee: ?Decl.Index = blk: {
21212121 const func_val = func.air.value(pl_op.operand) orelse break :blk null;
21222122 const module = func.bin_file.base.options.module.?;
21232123
21242124 if (func_val.castTag(.function)) |function| {
2125 const decl = module.declPtr(function.data.owner_decl);
2126 try decl.link.wasm.ensureInitialized(func.bin_file);
2127 break :blk decl;
2125 _ = try func.bin_file.getOrCreateAtomForDecl(function.data.owner_decl);
2126 break :blk function.data.owner_decl;
21282127 } else if (func_val.castTag(.extern_fn)) |extern_fn| {
21292128 const ext_decl = module.declPtr(extern_fn.data.owner_decl);
21302129 const ext_info = ext_decl.ty.fnInfo();
21312130 var func_type = try genFunctype(func.gpa, ext_info.cc, ext_info.param_types, ext_info.return_type, func.target);
21322131 defer func_type.deinit(func.gpa);
2133 const atom = &ext_decl.link.wasm;
2134 try atom.ensureInitialized(func.bin_file);
2135 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 const type_index = try func.bin_file.storeDeclType(extern_fn.data.owner_decl, func_type);
21362135 try func.bin_file.addOrUpdateImport(
21372136 mem.sliceTo(ext_decl.name, 0),
21382137 atom.getSymbolIndex().?,
21392138 ext_decl.getExternFn().?.lib_name,
2140 ext_decl.fn_link.wasm.type_index,
2139 type_index,
21412140 );
2142 break :blk ext_decl;
2141 break :blk extern_fn.data.owner_decl;
21432142 } else if (func_val.castTag(.decl_ref)) |decl_ref| {
2144 const decl = module.declPtr(decl_ref.data);
2145 try decl.link.wasm.ensureInitialized(func.bin_file);
2146 break :blk decl;
2143 _ = try func.bin_file.getOrCreateAtomForDecl(decl_ref.data);
2144 break :blk decl_ref.data;
21472145 }
21482146 return func.fail("Expected a function, but instead found type '{}'", .{func_val.tag()});
21492147 };
......@@ -2164,7 +2162,8 @@ fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModif
21642162 }
21652163
21662164 if (callee) |direct| {
2167 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);
21682167 } else {
21692168 // in this case we call a function pointer
21702169 // so load its value onto the stack
......@@ -2477,7 +2476,7 @@ fn airArg(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
24772476 .dwarf => |dwarf| {
24782477 const src_index = func.air.instructions.items(.data)[inst].arg.src_index;
24792478 const name = func.mod_fn.getParamName(func.bin_file.base.options.module.?, src_index);
2480 try dwarf.genArgDbgInfo(name, arg_ty, .wasm, func.mod_fn.owner_decl, .{
2479 try dwarf.genArgDbgInfo(name, arg_ty, func.mod_fn.owner_decl, .{
24812480 .wasm_local = arg.local.value,
24822481 });
24832482 },
......@@ -2760,9 +2759,10 @@ fn lowerDeclRefValue(func: *CodeGen, tv: TypedValue, decl_index: Module.Decl.Ind
27602759 }
27612760
27622761 module.markDeclAlive(decl);
2763 try decl.link.wasm.ensureInitialized(func.bin_file);
2762 const atom_index = try func.bin_file.getOrCreateAtomForDecl(decl_index);
2763 const atom = func.bin_file.getAtom(atom_index);
27642764
2765 const target_sym_index = decl.link.wasm.sym_index;
2765 const target_sym_index = atom.sym_index;
27662766 if (decl.ty.zigTypeTag() == .Fn) {
27672767 try func.bin_file.addTableFunction(target_sym_index);
27682768 return WValue{ .function_index = target_sym_index };
......@@ -5547,7 +5547,7 @@ fn airDbgVar(func: *CodeGen, inst: Air.Inst.Index, is_ptr: bool) !void {
55475547 break :blk .nop;
55485548 },
55495549 };
5550 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);
55515551
55525552 func.finishAir(inst, .none, &.{});
55535553}
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+38-33
......@@ -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.getSymbolIndex().?
2675 else
2676 fn_owner_decl.link.coff.getSymbolIndex().?;
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 => {},
......@@ -3995,19 +3996,19 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
39953996 if (self.air.value(callee)) |func_value| {
39963997 if (func_value.castTag(.function)) |func_payload| {
39973998 const func = func_payload.data;
3998 const fn_owner_decl = mod.declPtr(func.owner_decl);
39993999
40004000 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
4001 try fn_owner_decl.link.elf.ensureInitialized(elf_file);
4002 const got_addr = @intCast(u32, fn_owner_decl.link.elf.getOffsetTableAddress(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));
40034004 _ = try self.addInst(.{
40044005 .tag = .call,
40054006 .ops = Mir.Inst.Ops.encode(.{ .flags = 0b01 }),
40064007 .data = .{ .imm = got_addr },
40074008 });
40084009 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
4009 try fn_owner_decl.link.coff.ensureInitialized(coff_file);
4010 const sym_index = fn_owner_decl.link.coff.getSymbolIndex().?;
4010 const atom_index = try coff_file.getOrCreateAtomForDecl(func.owner_decl);
4011 const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?;
40114012 try self.genSetReg(Type.initTag(.usize), .rax, .{
40124013 .linker_load = .{
40134014 .type = .got,
......@@ -4023,8 +4024,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
40234024 .data = undefined,
40244025 });
40254026 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
4026 try fn_owner_decl.link.macho.ensureInitialized(macho_file);
4027 const sym_index = fn_owner_decl.link.macho.getSymbolIndex().?;
4027 const atom_index = try macho_file.getOrCreateAtomForDecl(func.owner_decl);
4028 const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?;
40284029 try self.genSetReg(Type.initTag(.usize), .rax, .{
40294030 .linker_load = .{
40304031 .type = .got,
......@@ -4040,11 +4041,12 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
40404041 .data = undefined,
40414042 });
40424043 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
4043 try p9.seeDecl(func.owner_decl);
4044 const decl_block_index = try p9.seeDecl(func.owner_decl);
4045 const decl_block = p9.getDeclBlock(decl_block_index);
40444046 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
40454047 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
40464048 const got_addr = p9.bases.data;
4047 const got_index = fn_owner_decl.link.plan9.got_index.?;
4049 const got_index = decl_block.got_index.?;
40484050 const fn_got_addr = got_addr + got_index * ptr_bytes;
40494051 _ = try self.addInst(.{
40504052 .tag = .call,
......@@ -4080,15 +4082,15 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
40804082 });
40814083 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
40824084 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().?;
40834087 _ = try self.addInst(.{
40844088 .tag = .call_extern,
40854089 .ops = undefined,
4086 .data = .{
4087 .relocation = .{
4088 .atom_index = mod.declPtr(self.mod_fn.owner_decl).link.macho.getSymbolIndex().?,
4089 .sym_index = sym_index,
4090 },
4091 },
4090 .data = .{ .relocation = .{
4091 .atom_index = atom_index,
4092 .sym_index = sym_index,
4093 } },
40924094 });
40934095 } else {
40944096 return self.fail("TODO implement calling extern functions", .{});
......@@ -6719,23 +6721,27 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne
67196721 module.markDeclAlive(decl);
67206722
67216723 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
6722 try decl.link.elf.ensureInitialized(elf_file);
6723 return MCValue{ .memory = decl.link.elf.getOffsetTableAddress(elf_file) };
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) };
67246727 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
6725 try decl.link.macho.ensureInitialized(macho_file);
6728 const atom_index = try macho_file.getOrCreateAtomForDecl(decl_index);
6729 const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?;
67266730 return MCValue{ .linker_load = .{
67276731 .type = .got,
6728 .sym_index = decl.link.macho.getSymbolIndex().?,
6732 .sym_index = sym_index,
67296733 } };
67306734 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
6731 try decl.link.coff.ensureInitialized(coff_file);
6735 const atom_index = try coff_file.getOrCreateAtomForDecl(decl_index);
6736 const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?;
67326737 return MCValue{ .linker_load = .{
67336738 .type = .got,
6734 .sym_index = decl.link.coff.getSymbolIndex().?,
6739 .sym_index = sym_index,
67356740 } };
67366741 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
6737 try p9.seeDecl(decl_index);
6738 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;
67396745 return MCValue{ .memory = got_addr };
67406746 } else {
67416747 return self.fail("TODO codegen non-ELF const Decl pointer", .{});
......@@ -6748,8 +6754,7 @@ fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue {
67486754 return self.fail("lowering unnamed constant failed: {s}", .{@errorName(err)});
67496755 };
67506756 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
6751 const vaddr = elf_file.local_symbols.items[local_sym_index].st_value;
6752 return MCValue{ .memory = vaddr };
6757 return MCValue{ .memory = elf_file.getSymbol(local_sym_index).st_value };
67536758 } else if (self.bin_file.cast(link.File.MachO)) |_| {
67546759 return MCValue{ .linker_load = .{
67556760 .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/c.zig-1
......@@ -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;
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");
......@@ -11043,8 +11042,8 @@ fn backendSupportsF128(target: std.Target) bool {
1104311042fn intrinsicsAllowed(scalar_ty: Type, target: std.Target) bool {
1104411043 return switch (scalar_ty.tag()) {
1104511044 .f16 => backendSupportsF16(target),
11046 .f80 => (CType.longdouble.sizeInBits(target) == 80) and backendSupportsF80(target),
11047 .f128 => (CType.longdouble.sizeInBits(target) == 128) and backendSupportsF128(target),
11045 .f80 => (target.c_type_bit_size(.longdouble) == 80) and backendSupportsF80(target),
11046 .f128 => (target.c_type_bit_size(.longdouble) == 128) and backendSupportsF128(target),
1104811047 else => true,
1104911048 };
1105011049}
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+9-41
......@@ -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
......@@ -580,22 +547,23 @@ pub const File = struct {
580547 }
581548 }
582549
583 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);
584552 log.debug("updateDeclLineNumber {*} ({s}), line={}", .{
585553 decl, decl.name, decl.src_line + 1,
586554 });
587555 assert(decl.has_tv);
588556 if (build_options.only_c) {
589557 assert(base.tag == .c);
590 return @fieldParentPtr(C, "base", base).updateDeclLineNumber(module, decl);
558 return @fieldParentPtr(C, "base", base).updateDeclLineNumber(module, decl_index);
591559 }
592560 switch (base.tag) {
593 .coff => return @fieldParentPtr(Coff, "base", base).updateDeclLineNumber(module, decl),
594 .elf => return @fieldParentPtr(Elf, "base", base).updateDeclLineNumber(module, decl),
595 .macho => return @fieldParentPtr(MachO, "base", base).updateDeclLineNumber(module, decl),
596 .c => return @fieldParentPtr(C, "base", base).updateDeclLineNumber(module, decl),
597 .wasm => return @fieldParentPtr(Wasm, "base", base).updateDeclLineNumber(module, decl),
598 .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),
599567 .spirv, .nvptx => {},
600568 }
601569 }
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+265-210
......@@ -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,14 +507,15 @@ fn growSectionVM(self: *Coff, sect_id: u32, needed_size: u32) !void {
480507 header.virtual_size = increased_size;
481508}
482509
483fn 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 {
484511 const tracy = trace(@src());
485512 defer tracy.end();
486513
514 const atom = self.getAtom(atom_index);
487515 const sect_id = @enumToInt(atom.getSymbol(self).section_number) - 1;
488516 const header = &self.sections.items(.header)[sect_id];
489517 const free_list = &self.sections.items(.free_list)[sect_id];
490 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];
491519 const new_atom_ideal_capacity = if (header.isCode()) padToIdeal(new_atom_size) else new_atom_size;
492520
493521 // We use these to indicate our intention to update metadata, placing the new atom,
......@@ -495,7 +523,7 @@ fn allocateAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u
495523 // It would be simpler to do it inside the for loop below, but that would cause a
496524 // problem if an error was returned later in the function. So this action
497525 // is actually carried out at the end of the function, when errors are no longer possible.
498 var atom_placement: ?*Atom = null;
526 var atom_placement: ?Atom.Index = null;
499527 var free_list_removal: ?usize = null;
500528
501529 // First we look for an appropriately sized free list node.
......@@ -503,7 +531,8 @@ fn allocateAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u
503531 var vaddr = blk: {
504532 var i: usize = 0;
505533 while (i < free_list.items.len) {
506 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);
507536 // We now have a pointer to a live atom that has too much capacity.
508537 // Is it enough that we could fit this new atom?
509538 const sym = big_atom.getSymbol(self);
......@@ -531,34 +560,43 @@ fn allocateAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u
531560 const keep_free_list_node = remaining_capacity >= min_text_capacity;
532561
533562 // Set up the metadata to be updated, after errors are no longer possible.
534 atom_placement = big_atom;
563 atom_placement = big_atom_index;
535564 if (!keep_free_list_node) {
536565 free_list_removal = i;
537566 }
538567 break :blk new_start_vaddr;
539 } else if (maybe_last_atom.*) |last| {
568 } else if (maybe_last_atom_index.*) |last_index| {
569 const last = self.getAtom(last_index);
540570 const last_symbol = last.getSymbol(self);
541571 const ideal_capacity = if (header.isCode()) padToIdeal(last.size) else last.size;
542572 const ideal_capacity_end_vaddr = last_symbol.value + ideal_capacity;
543573 const new_start_vaddr = mem.alignForwardGeneric(u32, ideal_capacity_end_vaddr, alignment);
544 atom_placement = last;
574 atom_placement = last_index;
545575 break :blk new_start_vaddr;
546576 } else {
547577 break :blk mem.alignForwardGeneric(u32, header.virtual_address, alignment);
548578 }
549579 };
550580
551 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;
552585 if (expand_section) {
553586 const sect_capacity = self.allocatedSize(header.pointer_to_raw_data);
554587 const needed_size: u32 = (vaddr + new_atom_size) - header.virtual_address;
555588 if (needed_size > sect_capacity) {
556589 const new_offset = self.findFreeSpace(needed_size, default_file_alignment);
557 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);
558592 const sym = last_atom.getSymbol(self);
559593 break :blk (sym.value + last_atom.size) - header.virtual_address;
560594 } else 0;
561 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 });
562600 const amt = try self.base.file.?.copyRangeAll(
563601 header.pointer_to_raw_data,
564602 self.base.file.?,
......@@ -577,26 +615,34 @@ fn allocateAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u
577615
578616 header.virtual_size = @max(header.virtual_size, needed_size);
579617 header.size_of_raw_data = needed_size;
580 maybe_last_atom.* = atom;
618 maybe_last_atom_index.* = atom_index;
581619 }
582620
583 atom.size = new_atom_size;
584 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 }
585626
586 if (atom.prev) |prev| {
587 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;
588630 }
589 if (atom.next) |next| {
590 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;
591634 }
592635
593 if (atom_placement) |big_atom| {
594 atom.prev = big_atom;
595 atom.next = big_atom.next;
596 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;
597642 } else {
598 atom.prev = null;
599 atom.next = null;
643 const atom_ptr = self.getAtomPtr(atom_index);
644 atom_ptr.prev_index = null;
645 atom_ptr.next_index = null;
600646 }
601647 if (free_list_removal) |i| {
602648 _ = free_list.swapRemove(i);
......@@ -701,24 +747,37 @@ pub fn allocateImportEntry(self: *Coff, target: SymbolWithLoc) !u32 {
701747 return index;
702748}
703749
704fn createGotAtom(self: *Coff, target: SymbolWithLoc) !*Atom {
750pub fn createAtom(self: *Coff) !Atom.Index {
705751 const gpa = self.base.allocator;
706 const atom = try gpa.create(Atom);
707 errdefer gpa.destroy(atom);
708 atom.* = Atom.empty;
709 try atom.ensureInitialized(self);
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);
710771 atom.size = @sizeOf(u64);
711772 atom.alignment = @alignOf(u64);
712773
713 try self.managed_atoms.append(gpa, atom);
714
715774 const sym = atom.getSymbolPtr(self);
716775 sym.section_number = @intToEnum(coff.SectionNumber, self.got_section_index.? + 1);
717 sym.value = try self.allocateAtom(atom, atom.size, atom.alignment);
776 sym.value = try self.allocateAtom(atom_index, atom.size, atom.alignment);
718777
719778 log.debug("allocated GOT atom at 0x{x}", .{sym.value});
720779
721 try atom.addRelocation(self, .{
780 try Atom.addRelocation(self, atom_index, .{
722781 .type = .direct,
723782 .target = target,
724783 .offset = 0,
......@@ -732,49 +791,46 @@ fn createGotAtom(self: *Coff, target: SymbolWithLoc) !*Atom {
732791 .UNDEFINED => @panic("TODO generate a binding for undefined GOT target"),
733792 .ABSOLUTE => {},
734793 .DEBUG => unreachable, // not possible
735 else => try atom.addBaseRelocation(self, 0),
794 else => try Atom.addBaseRelocation(self, atom_index, 0),
736795 }
737796
738 return atom;
797 return atom_index;
739798}
740799
741fn createImportAtom(self: *Coff) !*Atom {
742 const gpa = self.base.allocator;
743 const atom = try gpa.create(Atom);
744 errdefer gpa.destroy(atom);
745 atom.* = Atom.empty;
746 try atom.ensureInitialized(self);
800fn createImportAtom(self: *Coff) !Atom.Index {
801 const atom_index = try self.createAtom();
802 const atom = self.getAtomPtr(atom_index);
747803 atom.size = @sizeOf(u64);
748804 atom.alignment = @alignOf(u64);
749805
750 try self.managed_atoms.append(gpa, atom);
751
752806 const sym = atom.getSymbolPtr(self);
753807 sym.section_number = @intToEnum(coff.SectionNumber, self.idata_section_index.? + 1);
754 sym.value = try self.allocateAtom(atom, atom.size, atom.alignment);
808 sym.value = try self.allocateAtom(atom_index, atom.size, atom.alignment);
755809
756810 log.debug("allocated import atom at 0x{x}", .{sym.value});
757811
758 return atom;
812 return atom_index;
759813}
760814
761fn 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);
762817 const sym = atom.getSymbol(self);
763818 const align_ok = mem.alignBackwardGeneric(u32, sym.value, alignment) == sym.value;
764819 const need_realloc = !align_ok or new_atom_size > atom.capacity(self);
765820 if (!need_realloc) return sym.value;
766 return self.allocateAtom(atom, new_atom_size, alignment);
821 return self.allocateAtom(atom_index, new_atom_size, alignment);
767822}
768823
769fn shrinkAtom(self: *Coff, atom: *Atom, new_block_size: u32) void {
824fn shrinkAtom(self: *Coff, atom_index: Atom.Index, new_block_size: u32) void {
770825 _ = self;
771 _ = atom;
826 _ = atom_index;
772827 _ = new_block_size;
773828 // TODO check the new capacity, and if it crosses the size threshold into a big enough
774829 // capacity, insert a free list node for it.
775830}
776831
777fn 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);
778834 const sym = atom.getSymbol(self);
779835 const section = self.sections.get(@enumToInt(sym.section_number) - 1);
780836 const file_offset = section.header.pointer_to_raw_data + sym.value - section.header.virtual_address;
......@@ -784,18 +840,18 @@ fn writeAtom(self: *Coff, atom: *Atom, code: []const u8) !void {
784840 file_offset + code.len,
785841 });
786842 try self.base.file.?.pwriteAll(code, file_offset);
787 try self.resolveRelocs(atom);
843 try self.resolveRelocs(atom_index);
788844}
789845
790fn writePtrWidthAtom(self: *Coff, atom: *Atom) !void {
846fn writePtrWidthAtom(self: *Coff, atom_index: Atom.Index) !void {
791847 switch (self.ptr_width) {
792848 .p32 => {
793849 var buffer: [@sizeOf(u32)]u8 = [_]u8{0} ** @sizeOf(u32);
794 try self.writeAtom(atom, &buffer);
850 try self.writeAtom(atom_index, &buffer);
795851 },
796852 .p64 => {
797853 var buffer: [@sizeOf(u64)]u8 = [_]u8{0} ** @sizeOf(u64);
798 try self.writeAtom(atom, &buffer);
854 try self.writeAtom(atom_index, &buffer);
799855 },
800856 }
801857}
......@@ -815,7 +871,8 @@ fn markRelocsDirtyByAddress(self: *Coff, addr: u32) void {
815871 var it = self.relocs.valueIterator();
816872 while (it.next()) |relocs| {
817873 for (relocs.items) |*reloc| {
818 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);
819876 const target_sym = target_atom.getSymbol(self);
820877 if (target_sym.value < addr) continue;
821878 reloc.dirty = true;
......@@ -823,24 +880,26 @@ fn markRelocsDirtyByAddress(self: *Coff, addr: u32) void {
823880 }
824881}
825882
826fn resolveRelocs(self: *Coff, atom: *Atom) !void {
827 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;
828885
829 log.debug("relocating '{s}'", .{atom.getName(self)});
886 log.debug("relocating '{s}'", .{self.getAtom(atom_index).getName(self)});
830887
831888 for (relocs.items) |*reloc| {
832889 if (!reloc.dirty) continue;
833 try reloc.resolve(atom, self);
890 try reloc.resolve(atom_index, self);
834891 }
835892}
836893
837fn freeAtom(self: *Coff, atom: *Atom) void {
838 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;
839898
840899 // Remove any relocs and base relocs associated with this Atom
841 self.freeRelocationsForAtom(atom);
900 Atom.freeRelocations(self, atom_index);
842901
843 const gpa = self.base.allocator;
902 const atom = self.getAtom(atom_index);
844903 const sym = atom.getSymbol(self);
845904 const sect_id = @enumToInt(sym.section_number) - 1;
846905 const free_list = &self.sections.items(.free_list)[sect_id];
......@@ -849,45 +908,46 @@ fn freeAtom(self: *Coff, atom: *Atom) void {
849908 var i: usize = 0;
850909 // TODO turn free_list into a hash map
851910 while (i < free_list.items.len) {
852 if (free_list.items[i] == atom) {
911 if (free_list.items[i] == atom_index) {
853912 _ = free_list.swapRemove(i);
854913 continue;
855914 }
856 if (free_list.items[i] == atom.prev) {
915 if (free_list.items[i] == atom.prev_index) {
857916 already_have_free_list_node = true;
858917 }
859918 i += 1;
860919 }
861920 }
862921
863 const maybe_last_atom = &self.sections.items(.last_atom)[sect_id];
864 if (maybe_last_atom.*) |last_atom| {
865 if (last_atom == atom) {
866 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| {
867926 // TODO shrink the section size here
868 maybe_last_atom.* = prev;
927 maybe_last_atom_index.* = prev_index;
869928 } else {
870 maybe_last_atom.* = null;
929 maybe_last_atom_index.* = null;
871930 }
872931 }
873932 }
874933
875 if (atom.prev) |prev| {
876 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;
877937
878 if (!already_have_free_list_node and prev.freeListEligible(self)) {
938 if (!already_have_free_list_node and prev.*.freeListEligible(self)) {
879939 // The free list is heuristics, it doesn't have to be perfect, so we can
880940 // ignore the OOM here.
881 free_list.append(gpa, prev) catch {};
941 free_list.append(gpa, prev_index) catch {};
882942 }
883943 } else {
884 atom.prev = null;
944 self.getAtomPtr(atom_index).prev_index = null;
885945 }
886946
887 if (atom.next) |next| {
888 next.prev = atom.prev;
947 if (atom.next_index) |next_index| {
948 self.getAtomPtr(next_index).prev_index = atom.prev_index;
889949 } else {
890 atom.next = null;
950 self.getAtomPtr(atom_index).next_index = null;
891951 }
892952
893953 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
......@@ -910,7 +970,7 @@ fn freeAtom(self: *Coff, atom: *Atom) void {
910970 self.locals.items[sym_index].section_number = .UNDEFINED;
911971 _ = self.atom_by_index_table.remove(sym_index);
912972 log.debug(" adding local symbol index {d} to free list", .{sym_index});
913 atom.sym_index = 0;
973 self.getAtomPtr(atom_index).sym_index = 0;
914974}
915975
916976pub fn updateFunc(self: *Coff, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void {
......@@ -927,15 +987,10 @@ pub fn updateFunc(self: *Coff, module: *Module, func: *Module.Fn, air: Air, live
927987
928988 const decl_index = func.owner_decl;
929989 const decl = module.declPtr(decl_index);
930 const atom = &decl.link.coff;
931 try atom.ensureInitialized(self);
932 const gop = try self.decls.getOrPut(self.base.allocator, decl_index);
933 if (gop.found_existing) {
934 self.freeUnnamedConsts(decl_index);
935 self.freeRelocationsForAtom(&decl.link.coff);
936 } else {
937 gop.value_ptr.* = null;
938 }
990
991 const atom_index = try self.getOrCreateAtomForDecl(decl_index);
992 self.freeUnnamedConsts(decl_index);
993 Atom.freeRelocations(self, atom_index);
939994
940995 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
941996 defer code_buffer.deinit();
......@@ -979,11 +1034,7 @@ pub fn lowerUnnamedConst(self: *Coff, tv: TypedValue, decl_index: Module.Decl.In
9791034 }
9801035 const unnamed_consts = gop.value_ptr;
9811036
982 const atom = try gpa.create(Atom);
983 errdefer gpa.destroy(atom);
984 atom.* = Atom.empty;
985 try atom.ensureInitialized(self);
986 try self.managed_atoms.append(gpa, atom);
1037 const atom_index = try self.createAtom();
9871038
9881039 const sym_name = blk: {
9891040 const decl_name = try decl.getFullyQualifiedName(mod);
......@@ -993,11 +1044,15 @@ pub fn lowerUnnamedConst(self: *Coff, tv: TypedValue, decl_index: Module.Decl.In
9931044 break :blk try std.fmt.allocPrint(gpa, "__unnamed_{s}_{d}", .{ decl_name, index });
9941045 };
9951046 defer gpa.free(sym_name);
996 try self.setSymbolName(atom.getSymbolPtr(self), sym_name);
997 atom.getSymbolPtr(self).section_number = @intToEnum(coff.SectionNumber, self.rdata_section_index.? + 1);
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 }
9981053
9991054 const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), tv, &code_buffer, .none, .{
1000 .parent_atom_index = atom.getSymbolIndex().?,
1055 .parent_atom_index = self.getAtom(atom_index).getSymbolIndex().?,
10011056 });
10021057 const code = switch (res) {
10031058 .ok => code_buffer.items,
......@@ -1010,17 +1065,18 @@ pub fn lowerUnnamedConst(self: *Coff, tv: TypedValue, decl_index: Module.Decl.In
10101065 };
10111066
10121067 const required_alignment = tv.ty.abiAlignment(self.base.options.target);
1068 const atom = self.getAtomPtr(atom_index);
10131069 atom.alignment = required_alignment;
10141070 atom.size = @intCast(u32, code.len);
1015 atom.getSymbolPtr(self).value = try self.allocateAtom(atom, atom.size, atom.alignment);
1016 errdefer self.freeAtom(atom);
1071 atom.getSymbolPtr(self).value = try self.allocateAtom(atom_index, atom.size, atom.alignment);
1072 errdefer self.freeAtom(atom_index);
10171073
1018 try unnamed_consts.append(gpa, atom);
1074 try unnamed_consts.append(gpa, atom_index);
10191075
10201076 log.debug("allocated atom for {s} at 0x{x}", .{ sym_name, atom.getSymbol(self).value });
10211077 log.debug(" (required alignment 0x{x})", .{required_alignment});
10221078
1023 try self.writeAtom(atom, code);
1079 try self.writeAtom(atom_index, code);
10241080
10251081 return atom.getSymbolIndex().?;
10261082}
......@@ -1047,14 +1103,9 @@ pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) !
10471103 }
10481104 }
10491105
1050 const atom = &decl.link.coff;
1051 try atom.ensureInitialized(self);
1052 const gop = try self.decls.getOrPut(self.base.allocator, decl_index);
1053 if (gop.found_existing) {
1054 self.freeRelocationsForAtom(atom);
1055 } else {
1056 gop.value_ptr.* = null;
1057 }
1106 const atom_index = try self.getOrCreateAtomForDecl(decl_index);
1107 Atom.freeRelocations(self, atom_index);
1108 const atom = self.getAtom(atom_index);
10581109
10591110 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
10601111 defer code_buffer.deinit();
......@@ -1064,7 +1115,7 @@ pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) !
10641115 .ty = decl.ty,
10651116 .val = decl_val,
10661117 }, &code_buffer, .none, .{
1067 .parent_atom_index = decl.link.coff.getSymbolIndex().?,
1118 .parent_atom_index = atom.getSymbolIndex().?,
10681119 });
10691120 const code = switch (res) {
10701121 .ok => code_buffer.items,
......@@ -1082,7 +1133,20 @@ pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) !
10821133 return self.updateDeclExports(module, decl_index, module.getDeclExports(decl_index));
10831134}
10841135
1085fn 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);
10861150 const ty = decl.ty;
10871151 const zig_ty = ty.zigTypeTag();
10881152 const val = decl.val;
......@@ -1117,14 +1181,11 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []const u8,
11171181 log.debug("updateDeclCode {s}{*}", .{ decl_name, decl });
11181182 const required_alignment = decl.getAlignment(self.base.options.target);
11191183
1120 const decl_ptr = self.decls.getPtr(decl_index).?;
1121 if (decl_ptr.* == null) {
1122 decl_ptr.* = self.getDeclOutputSection(decl);
1123 }
1124 const sect_index = decl_ptr.*.?;
1125
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;
11261188 const code_len = @intCast(u32, code.len);
1127 const atom = &decl.link.coff;
11281189
11291190 if (atom.size != 0) {
11301191 const sym = atom.getSymbolPtr(self);
......@@ -1135,7 +1196,7 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []const u8,
11351196 const capacity = atom.capacity(self);
11361197 const need_realloc = code.len > capacity or !mem.isAlignedGeneric(u64, sym.value, required_alignment);
11371198 if (need_realloc) {
1138 const vaddr = try self.growAtom(atom, code_len, required_alignment);
1199 const vaddr = try self.growAtom(atom_index, code_len, required_alignment);
11391200 log.debug("growing {s} from 0x{x} to 0x{x}", .{ decl_name, sym.value, vaddr });
11401201 log.debug(" (required alignment 0x{x}", .{required_alignment});
11411202
......@@ -1143,49 +1204,43 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []const u8,
11431204 sym.value = vaddr;
11441205 log.debug(" (updating GOT entry)", .{});
11451206 const got_target = SymbolWithLoc{ .sym_index = atom.getSymbolIndex().?, .file = null };
1146 const got_atom = self.getGotAtomForSymbol(got_target).?;
1207 const got_atom_index = self.getGotAtomIndexForSymbol(got_target).?;
11471208 self.markRelocsDirtyByTarget(got_target);
1148 try self.writePtrWidthAtom(got_atom);
1209 try self.writePtrWidthAtom(got_atom_index);
11491210 }
11501211 } else if (code_len < atom.size) {
1151 self.shrinkAtom(atom, code_len);
1212 self.shrinkAtom(atom_index, code_len);
11521213 }
1153 atom.size = code_len;
1214 self.getAtomPtr(atom_index).size = code_len;
11541215 } else {
11551216 const sym = atom.getSymbolPtr(self);
11561217 try self.setSymbolName(sym, decl_name);
11571218 sym.section_number = @intToEnum(coff.SectionNumber, sect_index + 1);
11581219 sym.type = .{ .complex_type = complex_type, .base_type = .NULL };
11591220
1160 const vaddr = try self.allocateAtom(atom, code_len, required_alignment);
1161 errdefer self.freeAtom(atom);
1221 const vaddr = try self.allocateAtom(atom_index, code_len, required_alignment);
1222 errdefer self.freeAtom(atom_index);
11621223 log.debug("allocated atom for {s} at 0x{x}", .{ decl_name, vaddr });
1163 atom.size = code_len;
1224 self.getAtomPtr(atom_index).size = code_len;
11641225 sym.value = vaddr;
11651226
11661227 const got_target = SymbolWithLoc{ .sym_index = atom.getSymbolIndex().?, .file = null };
11671228 const got_index = try self.allocateGotEntry(got_target);
1168 const got_atom = try self.createGotAtom(got_target);
1229 const got_atom_index = try self.createGotAtom(got_target);
1230 const got_atom = self.getAtom(got_atom_index);
11691231 self.got_entries.items[got_index].sym_index = got_atom.getSymbolIndex().?;
1170 try self.writePtrWidthAtom(got_atom);
1232 try self.writePtrWidthAtom(got_atom_index);
11711233 }
11721234
11731235 self.markRelocsDirtyByTarget(atom.getSymbolWithLoc());
1174 try self.writeAtom(atom, code);
1175}
1176
1177fn freeRelocationsForAtom(self: *Coff, atom: *Atom) void {
1178 var removed_relocs = self.relocs.fetchRemove(atom);
1179 if (removed_relocs) |*relocs| relocs.value.deinit(self.base.allocator);
1180 var removed_base_relocs = self.base_relocs.fetchRemove(atom);
1181 if (removed_base_relocs) |*base_relocs| base_relocs.value.deinit(self.base.allocator);
1236 try self.writeAtom(atom_index, code);
11821237}
11831238
11841239fn freeUnnamedConsts(self: *Coff, decl_index: Module.Decl.Index) void {
11851240 const gpa = self.base.allocator;
11861241 const unnamed_consts = self.unnamed_const_atoms.getPtr(decl_index) orelse return;
1187 for (unnamed_consts.items) |atom| {
1188 self.freeAtom(atom);
1242 for (unnamed_consts.items) |atom_index| {
1243 self.freeAtom(atom_index);
11891244 }
11901245 unnamed_consts.clearAndFree(gpa);
11911246}
......@@ -1200,11 +1255,11 @@ pub fn freeDecl(self: *Coff, decl_index: Module.Decl.Index) void {
12001255
12011256 log.debug("freeDecl {*}", .{decl});
12021257
1203 if (self.decls.fetchRemove(decl_index)) |kv| {
1204 if (kv.value) |_| {
1205 self.freeAtom(&decl.link.coff);
1206 self.freeUnnamedConsts(decl_index);
1207 }
1258 if (self.decls.fetchRemove(decl_index)) |const_kv| {
1259 var kv = const_kv;
1260 self.freeAtom(kv.value.atom);
1261 self.freeUnnamedConsts(decl_index);
1262 kv.value.exports.deinit(self.base.allocator);
12081263 }
12091264}
12101265
......@@ -1257,16 +1312,10 @@ pub fn updateDeclExports(
12571312 const gpa = self.base.allocator;
12581313
12591314 const decl = module.declPtr(decl_index);
1260 const atom = &decl.link.coff;
1261
1262 if (atom.getSymbolIndex() == null) return;
1263
1264 const gop = try self.decls.getOrPut(gpa, decl_index);
1265 if (!gop.found_existing) {
1266 gop.value_ptr.* = self.getDeclOutputSection(decl);
1267 }
1268
1315 const atom_index = try self.getOrCreateAtomForDecl(decl_index);
1316 const atom = self.getAtom(atom_index);
12691317 const decl_sym = atom.getSymbol(self);
1318 const decl_metadata = self.decls.getPtr(decl_index).?;
12701319
12711320 for (exports) |exp| {
12721321 log.debug("adding new export '{s}'", .{exp.options.name});
......@@ -1301,9 +1350,9 @@ pub fn updateDeclExports(
13011350 continue;
13021351 }
13031352
1304 const sym_index = exp.link.coff.sym_index orelse blk: {
1353 const sym_index = decl_metadata.getExport(self, exp.options.name) orelse blk: {
13051354 const sym_index = try self.allocateSymbol();
1306 exp.link.coff.sym_index = sym_index;
1355 try decl_metadata.exports.append(gpa, sym_index);
13071356 break :blk sym_index;
13081357 };
13091358 const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = null };
......@@ -1326,16 +1375,15 @@ pub fn updateDeclExports(
13261375 }
13271376}
13281377
1329pub fn deleteExport(self: *Coff, exp: Export) void {
1378pub fn deleteDeclExport(self: *Coff, decl_index: Module.Decl.Index, name: []const u8) void {
13301379 if (self.llvm_object) |_| return;
1331 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;
13321382
13331383 const gpa = self.base.allocator;
1334
1335 const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = null };
1384 const sym_loc = SymbolWithLoc{ .sym_index = sym_index.*, .file = null };
13361385 const sym = self.getSymbolPtr(sym_loc);
1337 const sym_name = self.getSymbolName(sym_loc);
1338 log.debug("deleting export '{s}'", .{sym_name});
1386 log.debug("deleting export '{s}'", .{name});
13391387 assert(sym.storage_class == .EXTERNAL and sym.section_number != .UNDEFINED);
13401388 sym.* = .{
13411389 .name = [_]u8{0} ** 8,
......@@ -1345,9 +1393,9 @@ pub fn deleteExport(self: *Coff, exp: Export) void {
13451393 .storage_class = .NULL,
13461394 .number_of_aux_symbols = 0,
13471395 };
1348 self.locals_free_list.append(gpa, sym_index) catch {};
1396 self.locals_free_list.append(gpa, sym_index.*) catch {};
13491397
1350 if (self.resolver.fetchRemove(sym_name)) |entry| {
1398 if (self.resolver.fetchRemove(name)) |entry| {
13511399 defer gpa.free(entry.key);
13521400 self.globals_free_list.append(gpa, entry.value) catch {};
13531401 self.globals.items[entry.value] = .{
......@@ -1355,6 +1403,8 @@ pub fn deleteExport(self: *Coff, exp: Export) void {
13551403 .file = null,
13561404 };
13571405 }
1406
1407 sym_index.* = 0;
13581408}
13591409
13601410fn resolveGlobalSymbol(self: *Coff, current: SymbolWithLoc) !void {
......@@ -1419,9 +1469,10 @@ pub fn flushModule(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
14191469 if (self.imports_table.contains(global)) continue;
14201470
14211471 const import_index = try self.allocateImportEntry(global);
1422 const import_atom = try self.createImportAtom();
1472 const import_atom_index = try self.createImportAtom();
1473 const import_atom = self.getAtom(import_atom_index);
14231474 self.imports.items[import_index].sym_index = import_atom.getSymbolIndex().?;
1424 try self.writePtrWidthAtom(import_atom);
1475 try self.writePtrWidthAtom(import_atom_index);
14251476 }
14261477
14271478 if (build_options.enable_logging) {
......@@ -1455,22 +1506,14 @@ pub fn flushModule(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
14551506 }
14561507}
14571508
1458pub fn getDeclVAddr(
1459 self: *Coff,
1460 decl_index: Module.Decl.Index,
1461 reloc_info: link.File.RelocInfo,
1462) !u64 {
1463 const mod = self.base.options.module.?;
1464 const decl = mod.declPtr(decl_index);
1465
1509pub fn getDeclVAddr(self: *Coff, decl_index: Module.Decl.Index, reloc_info: link.File.RelocInfo) !u64 {
14661510 assert(self.llvm_object == null);
14671511
1468 try decl.link.coff.ensureInitialized(self);
1469 const sym_index = decl.link.coff.getSymbolIndex().?;
1470
1471 const atom = self.getAtomForSymbol(.{ .sym_index = reloc_info.parent_atom_index, .file = null }).?;
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 }).?;
14721515 const target = SymbolWithLoc{ .sym_index = sym_index, .file = null };
1473 try atom.addRelocation(self, .{
1516 try Atom.addRelocation(self, atom_index, .{
14741517 .type = .direct,
14751518 .target = target,
14761519 .offset = @intCast(u32, reloc_info.offset),
......@@ -1478,7 +1521,7 @@ pub fn getDeclVAddr(
14781521 .pcrel = false,
14791522 .length = 3,
14801523 });
1481 try atom.addBaseRelocation(self, @intCast(u32, reloc_info.offset));
1524 try Atom.addBaseRelocation(self, atom_index, @intCast(u32, reloc_info.offset));
14821525
14831526 return 0;
14841527}
......@@ -1505,10 +1548,10 @@ pub fn getGlobalSymbol(self: *Coff, name: []const u8) !u32 {
15051548 return global_index;
15061549}
15071550
1508pub fn updateDeclLineNumber(self: *Coff, module: *Module, decl: *Module.Decl) !void {
1551pub fn updateDeclLineNumber(self: *Coff, module: *Module, decl_index: Module.Decl.Index) !void {
15091552 _ = self;
15101553 _ = module;
1511 _ = decl;
1554 _ = decl_index;
15121555 log.debug("TODO implement updateDeclLineNumber", .{});
15131556}
15141557
......@@ -1529,7 +1572,8 @@ fn writeBaseRelocations(self: *Coff) !void {
15291572
15301573 var it = self.base_relocs.iterator();
15311574 while (it.next()) |entry| {
1532 const atom = entry.key_ptr.*;
1575 const atom_index = entry.key_ptr.*;
1576 const atom = self.getAtom(atom_index);
15331577 const offsets = entry.value_ptr.*;
15341578
15351579 for (offsets.items) |offset| {
......@@ -1613,7 +1657,8 @@ fn writeImportTable(self: *Coff) !void {
16131657 const gpa = self.base.allocator;
16141658
16151659 const section = self.sections.get(self.idata_section_index.?);
1616 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);
16171662
16181663 const iat_rva = section.header.virtual_address;
16191664 const iat_size = last_atom.getSymbol(self).value + last_atom.size * 2 - iat_rva; // account for sentinel zero pointer
......@@ -2051,27 +2096,37 @@ pub fn getOrPutGlobalPtr(self: *Coff, name: []const u8) !GetOrPutGlobalPtrResult
20512096 return GetOrPutGlobalPtrResult{ .found_existing = false, .value_ptr = ptr };
20522097}
20532098
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
20542109/// Returns atom if there is an atom referenced by the symbol described by `sym_loc` descriptor.
20552110/// Returns null on failure.
2056pub fn getAtomForSymbol(self: *Coff, sym_loc: SymbolWithLoc) ?*Atom {
2111pub fn getAtomIndexForSymbol(self: *const Coff, sym_loc: SymbolWithLoc) ?Atom.Index {
20572112 assert(sym_loc.file == null); // TODO linking with object files
20582113 return self.atom_by_index_table.get(sym_loc.sym_index);
20592114}
20602115
20612116/// Returns GOT atom that references `sym_loc` if one exists.
20622117/// Returns null otherwise.
2063pub fn getGotAtomForSymbol(self: *Coff, sym_loc: SymbolWithLoc) ?*Atom {
2118pub fn getGotAtomIndexForSymbol(self: *const Coff, sym_loc: SymbolWithLoc) ?Atom.Index {
20642119 const got_index = self.got_entries_table.get(sym_loc) orelse return null;
20652120 const got_entry = self.got_entries.items[got_index];
2066 return self.getAtomForSymbol(.{ .sym_index = got_entry.sym_index, .file = null });
2121 return self.getAtomIndexForSymbol(.{ .sym_index = got_entry.sym_index, .file = null });
20672122}
20682123
20692124/// Returns import atom that references `sym_loc` if one exists.
20702125/// Returns null otherwise.
2071pub fn getImportAtomForSymbol(self: *Coff, sym_loc: SymbolWithLoc) ?*Atom {
2126pub fn getImportAtomIndexForSymbol(self: *const Coff, sym_loc: SymbolWithLoc) ?Atom.Index {
20722127 const imports_index = self.imports_table.get(sym_loc) orelse return null;
20732128 const imports_entry = self.imports.items[imports_index];
2074 return self.getAtomForSymbol(.{ .sym_index = imports_entry.sym_index, .file = null });
2129 return self.getAtomIndexForSymbol(.{ .sym_index = imports_entry.sym_index, .file = null });
20752130}
20762131
20772132fn setSectionName(self: *Coff, header: *coff.SectionHeader, name: []const u8) !void {
src/link/Coff/Atom.zig+24-24
......@@ -27,23 +27,10 @@ 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};
41
42pub fn ensureInitialized(self: *Atom, coff_file: *Coff) !void {
43 if (self.getSymbolIndex() != null) return; // Already initialized
44 self.sym_index = try coff_file.allocateSymbol();
45 try coff_file.atom_by_index_table.putNoClobber(coff_file.base.allocator, self.sym_index, self);
46}
30prev_index: ?Index,
31next_index: ?Index,
32
33pub const Index = u32;
4734
4835pub fn getSymbolIndex(self: Atom) ?u32 {
4936 if (self.sym_index == 0) return null;
......@@ -85,7 +72,8 @@ pub fn getName(self: Atom, coff_file: *const Coff) []const u8 {
8572/// Returns how much room there is to grow in virtual address space.
8673pub fn capacity(self: Atom, coff_file: *const Coff) u32 {
8774 const self_sym = self.getSymbol(coff_file);
88 if (self.next) |next| {
75 if (self.next_index) |next_index| {
76 const next = coff_file.getAtom(next_index);
8977 const next_sym = next.getSymbol(coff_file);
9078 return next_sym.value - self_sym.value;
9179 } else {
......@@ -97,7 +85,8 @@ pub fn capacity(self: Atom, coff_file: *const Coff) u32 {
9785
9886pub fn freeListEligible(self: Atom, coff_file: *const Coff) bool {
9987 // No need to keep a free list node for the last atom.
100 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);
10190 const self_sym = self.getSymbol(coff_file);
10291 const next_sym = next.getSymbol(coff_file);
10392 const cap = next_sym.value - self_sym.value;
......@@ -107,22 +96,33 @@ pub fn freeListEligible(self: Atom, coff_file: *const Coff) bool {
10796 return surplus >= Coff.min_text_capacity;
10897}
10998
110pub fn addRelocation(self: *Atom, coff_file: *Coff, reloc: Relocation) !void {
99pub fn addRelocation(coff_file: *Coff, atom_index: Index, reloc: Relocation) !void {
111100 const gpa = coff_file.base.allocator;
112101 log.debug(" (adding reloc of type {s} to target %{d})", .{ @tagName(reloc.type), reloc.target.sym_index });
113 const gop = try coff_file.relocs.getOrPut(gpa, self);
102 const gop = try coff_file.relocs.getOrPut(gpa, atom_index);
114103 if (!gop.found_existing) {
115104 gop.value_ptr.* = .{};
116105 }
117106 try gop.value_ptr.append(gpa, reloc);
118107}
119108
120pub fn addBaseRelocation(self: *Atom, coff_file: *Coff, offset: u32) !void {
109pub fn addBaseRelocation(coff_file: *Coff, atom_index: Index, offset: u32) !void {
121110 const gpa = coff_file.base.allocator;
122 log.debug(" (adding base relocation at offset 0x{x} in %{d})", .{ offset, self.sym_index });
123 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);
124116 if (!gop.found_existing) {
125117 gop.value_ptr.* = .{};
126118 }
127119 try gop.value_ptr.append(gpa, offset);
128120}
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+636-570
......@@ -1,43 +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 Atom = @import("Elf/Atom.zig");
14const Module = @import("../Module.zig");
15const Compilation = @import("../Compilation.zig");
16const Dwarf = @import("Dwarf.zig");
1713const codegen = @import("../codegen.zig");
18const lldMain = @import("../main.zig").lldMain;
19const trace = @import("../tracy.zig").trace;
20const Package = @import("../Package.zig");
21const Value = @import("../value.zig").Value;
22const Type = @import("../type.zig").Type;
23const TypedValue = @import("../TypedValue.zig");
24const link = @import("../link.zig");
25const File = link.File;
26const build_options = @import("build_options");
27const target_util = @import("../target.zig");
2814const glibc = @import("../glibc.zig");
15const link = @import("../link.zig");
16const lldMain = @import("../main.zig").lldMain;
2917const musl = @import("../musl.zig");
30const Cache = @import("../Cache.zig");
18const target_util = @import("../target.zig");
19const trace = @import("../tracy.zig").trace;
20
3121const 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;
3228const Liveness = @import("../Liveness.zig");
3329const LlvmObject = @import("../codegen/llvm.zig").Object;
34
35pub const TextBlock = Atom;
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;
3636
3737const default_entry_addr = 0x8000000;
3838
3939pub const base_tag: File.Tag = .elf;
4040
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
4187base: File,
4288dwarf: ?Dwarf = null,
4389
......@@ -48,12 +94,12 @@ llvm_object: ?*LlvmObject = null,
4894
4995/// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
5096/// Same order as in the file.
51sections: std.ArrayListUnmanaged(elf.Elf64_Shdr) = std.ArrayListUnmanaged(elf.Elf64_Shdr){},
97sections: std.MultiArrayList(Section) = .{},
5298shdr_table_offset: ?u64 = null,
5399
54100/// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
55101/// Same order as in the file.
56program_headers: std.ArrayListUnmanaged(elf.Elf64_Phdr) = std.ArrayListUnmanaged(elf.Elf64_Phdr){},
102program_headers: std.ArrayListUnmanaged(elf.Elf64_Phdr) = .{},
57103phdr_table_offset: ?u64 = null,
58104/// The index into the program headers of a PT_LOAD program header with Read and Execute flags
59105phdr_load_re_index: ?u16 = null,
......@@ -65,12 +111,10 @@ phdr_load_ro_index: ?u16 = null,
65111/// The index into the program headers of a PT_LOAD program header with Write flag
66112phdr_load_rw_index: ?u16 = null,
67113
68phdr_shdr_table: std.AutoHashMapUnmanaged(u16, u16) = .{},
69
70114entry_addr: ?u64 = null,
71115page_size: u32,
72116
73shstrtab: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8){},
117shstrtab: StringTable(.strtab) = .{},
74118shstrtab_index: ?u16 = null,
75119
76120symtab_section_index: ?u16 = null,
......@@ -113,39 +157,14 @@ debug_line_header_dirty: bool = false,
113157
114158error_flags: File.ErrorFlags = File.ErrorFlags{},
115159
116/// Pointer to the last allocated atom
117atoms: std.AutoHashMapUnmanaged(u16, *TextBlock) = .{},
118
119/// A list of text blocks that have surplus capacity. This list can have false
120/// positives, as functions grow and shrink over time, only sometimes being added
121/// or removed from the freelist.
122///
123/// A text block has surplus capacity when its overcapacity value is greater than
124/// padToIdeal(minimum_text_block_size). That is, when it has so
125/// much extra capacity, that we could fit a small new symbol in it, itself with
126/// ideal_capacity or more.
127///
128/// Ideal capacity is defined by size + (size / ideal_factor)
129///
130/// Overcapacity is measured by actual_capacity - ideal_capacity. Note that
131/// overcapacity can be negative. A simple way to have negative overcapacity is to
132/// allocate a fresh text block, which will have ideal capacity, and then grow it
133/// by 1 byte. It will then have -1 overcapacity.
134atom_free_lists: std.AutoHashMapUnmanaged(u16, std.ArrayListUnmanaged(*TextBlock)) = .{},
135
136/// Table of Decls that are currently alive.
137/// We store them here so that we can properly dispose of any allocated
138/// memory within the atom in the incremental linker.
139/// TODO consolidate this.
140decls: std.AutoHashMapUnmanaged(Module.Decl.Index, ?u16) = .{},
160/// Table of tracked Decls.
161decls: std.AutoHashMapUnmanaged(Module.Decl.Index, DeclMetadata) = .{},
141162
142163/// List of atoms that are owned directly by the linker.
143/// Currently these are only atoms that are the result of linking
144/// object files. Atoms which take part in incremental linking are
145/// at present owned by Module.Decl.
146/// TODO consolidate this.
147managed_atoms: std.ArrayListUnmanaged(*TextBlock) = .{},
148atom_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) = .{},
149168
150169/// Table of unnamed constants associated with a parent `Decl`.
151170/// We store them here so that we can free the constants whenever the `Decl`
......@@ -173,15 +192,8 @@ unnamed_const_atoms: UnnamedConstTable = .{},
173192/// this will be a table indexed by index into the list of Atoms.
174193relocs: RelocTable = .{},
175194
176const Reloc = struct {
177 target: u32,
178 offset: u64,
179 addend: u32,
180 prev_vaddr: u64,
181};
182
183const RelocTable = std.AutoHashMapUnmanaged(*TextBlock, std.ArrayListUnmanaged(Reloc));
184const 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));
185197
186198/// When allocating, the ideal_capacity is calculated by
187199/// actual_capacity + (actual_capacity / ideal_factor)
......@@ -190,15 +202,11 @@ const ideal_factor = 3;
190202/// In order for a slice of bytes to be considered eligible to keep metadata pointing at
191203/// it as a possible place to put new symbols, it must have enough room for this many bytes
192204/// (plus extra for reserved capacity).
193const minimum_text_block_size = 64;
194pub const min_text_capacity = padToIdeal(minimum_text_block_size);
205const minimum_atom_size = 64;
206pub const min_text_capacity = padToIdeal(minimum_atom_size);
195207
196208pub const PtrWidth = enum { p32, p64 };
197209
198pub const Export = struct {
199 sym_index: ?u32 = null,
200};
201
202210pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Options) !*Elf {
203211 assert(options.target.ofmt == .elf);
204212
......@@ -230,16 +238,19 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
230238
231239 // There must always be a null section in index 0
232240 try self.sections.append(allocator, .{
233 .sh_name = 0,
234 .sh_type = elf.SHT_NULL,
235 .sh_flags = 0,
236 .sh_addr = 0,
237 .sh_offset = 0,
238 .sh_size = 0,
239 .sh_link = 0,
240 .sh_info = 0,
241 .sh_addralign = 0,
242 .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,
243254 });
244255
245256 try self.populateMissingMetadata();
......@@ -286,75 +297,67 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Elf {
286297}
287298
288299pub fn deinit(self: *Elf) void {
300 const gpa = self.base.allocator;
301
289302 if (build_options.have_llvm) {
290 if (self.llvm_object) |llvm_object| llvm_object.destroy(self.base.allocator);
291 }
292
293 self.sections.deinit(self.base.allocator);
294 self.program_headers.deinit(self.base.allocator);
295 self.shstrtab.deinit(self.base.allocator);
296 self.local_symbols.deinit(self.base.allocator);
297 self.global_symbols.deinit(self.base.allocator);
298 self.global_symbol_free_list.deinit(self.base.allocator);
299 self.local_symbol_free_list.deinit(self.base.allocator);
300 self.offset_table_free_list.deinit(self.base.allocator);
301 self.offset_table.deinit(self.base.allocator);
302 self.phdr_shdr_table.deinit(self.base.allocator);
303 self.decls.deinit(self.base.allocator);
304
305 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
306320 {
307 var it = self.atom_free_lists.valueIterator();
308 while (it.next()) |free_list| {
309 free_list.deinit(self.base.allocator);
321 var it = self.decls.iterator();
322 while (it.next()) |entry| {
323 entry.value_ptr.exports.deinit(gpa);
310324 }
311 self.atom_free_lists.deinit(self.base.allocator);
325 self.decls.deinit(gpa);
312326 }
313327
314 for (self.managed_atoms.items) |atom| {
315 self.base.allocator.destroy(atom);
316 }
317 self.managed_atoms.deinit(self.base.allocator);
328 self.atoms.deinit(gpa);
329 self.atom_by_index_table.deinit(gpa);
318330
319331 {
320332 var it = self.unnamed_const_atoms.valueIterator();
321333 while (it.next()) |atoms| {
322 atoms.deinit(self.base.allocator);
334 atoms.deinit(gpa);
323335 }
324 self.unnamed_const_atoms.deinit(self.base.allocator);
336 self.unnamed_const_atoms.deinit(gpa);
325337 }
326338
327339 {
328340 var it = self.relocs.valueIterator();
329341 while (it.next()) |relocs| {
330 relocs.deinit(self.base.allocator);
342 relocs.deinit(gpa);
331343 }
332 self.relocs.deinit(self.base.allocator);
344 self.relocs.deinit(gpa);
333345 }
334346
335 self.atom_by_index_table.deinit(self.base.allocator);
336
337347 if (self.dwarf) |*dw| {
338348 dw.deinit();
339349 }
340350}
341351
342352pub fn getDeclVAddr(self: *Elf, decl_index: Module.Decl.Index, reloc_info: File.RelocInfo) !u64 {
343 const mod = self.base.options.module.?;
344 const decl = mod.declPtr(decl_index);
345
346353 assert(self.llvm_object == null);
347354
348 try decl.link.elf.ensureInitialized(self);
349 const target = decl.link.elf.getSymbolIndex().?;
350
351 const vaddr = self.local_symbols.items[target].st_value;
352 const atom = self.atom_by_index_table.get(reloc_info.parent_atom_index).?;
353 const gop = try self.relocs.getOrPut(self.base.allocator, atom);
354 if (!gop.found_existing) {
355 gop.value_ptr.* = .{};
356 }
357 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, .{
358361 .target = target,
359362 .offset = reloc_info.offset,
360363 .addend = reloc_info.addend,
......@@ -375,7 +378,7 @@ fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 {
375378
376379 if (self.shdr_table_offset) |off| {
377380 const shdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Shdr) else @sizeOf(elf.Elf64_Shdr);
378 const tight_size = self.sections.items.len * shdr_size;
381 const tight_size = self.sections.slice().len * shdr_size;
379382 const increased_size = padToIdeal(tight_size);
380383 const test_end = off + increased_size;
381384 if (end > off and start < test_end) {
......@@ -385,7 +388,7 @@ fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 {
385388
386389 if (self.phdr_table_offset) |off| {
387390 const phdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Phdr) else @sizeOf(elf.Elf64_Phdr);
388 const tight_size = self.sections.items.len * phdr_size;
391 const tight_size = self.sections.slice().len * phdr_size;
389392 const increased_size = padToIdeal(tight_size);
390393 const test_end = off + increased_size;
391394 if (end > off and start < test_end) {
......@@ -393,7 +396,7 @@ fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 {
393396 }
394397 }
395398
396 for (self.sections.items) |section| {
399 for (self.sections.items(.shdr)) |section| {
397400 const increased_size = padToIdeal(section.sh_size);
398401 const test_end = section.sh_offset + increased_size;
399402 if (end > section.sh_offset and start < test_end) {
......@@ -420,7 +423,7 @@ pub fn allocatedSize(self: *Elf, start: u64) u64 {
420423 if (self.phdr_table_offset) |off| {
421424 if (off > start and off < min_pos) min_pos = off;
422425 }
423 for (self.sections.items) |section| {
426 for (self.sections.items(.shdr)) |section| {
424427 if (section.sh_offset <= start) continue;
425428 if (section.sh_offset < min_pos) min_pos = section.sh_offset;
426429 }
......@@ -439,31 +442,10 @@ pub fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u32) u64 {
439442 return start;
440443}
441444
442/// TODO Improve this to use a table.
443fn makeString(self: *Elf, bytes: []const u8) !u32 {
444 try self.shstrtab.ensureUnusedCapacity(self.base.allocator, bytes.len + 1);
445 const result = self.shstrtab.items.len;
446 self.shstrtab.appendSliceAssumeCapacity(bytes);
447 self.shstrtab.appendAssumeCapacity(0);
448 return @intCast(u32, result);
449}
450
451pub fn getString(self: Elf, str_off: u32) []const u8 {
452 assert(str_off < self.shstrtab.items.len);
453 return mem.sliceTo(@ptrCast([*:0]const u8, self.shstrtab.items.ptr + str_off), 0);
454}
455
456fn updateString(self: *Elf, old_str_off: u32, new_name: []const u8) !u32 {
457 const existing_name = self.getString(old_str_off);
458 if (mem.eql(u8, existing_name, new_name)) {
459 return old_str_off;
460 }
461 return self.makeString(new_name);
462}
463
464445pub fn populateMissingMetadata(self: *Elf) !void {
465446 assert(self.llvm_object == null);
466447
448 const gpa = self.base.allocator;
467449 const small_ptr = switch (self.ptr_width) {
468450 .p32 => true,
469451 .p64 => false,
......@@ -477,7 +459,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {
477459 const off = self.findFreeSpace(file_size, p_align);
478460 log.debug("found PT_LOAD RE free space 0x{x} to 0x{x}", .{ off, off + file_size });
479461 const entry_addr: u64 = self.entry_addr orelse if (self.base.options.target.cpu.arch == .spu_2) @as(u64, 0) else default_entry_addr;
480 try self.program_headers.append(self.base.allocator, .{
462 try self.program_headers.append(gpa, .{
481463 .p_type = elf.PT_LOAD,
482464 .p_offset = off,
483465 .p_filesz = file_size,
......@@ -487,7 +469,6 @@ pub fn populateMissingMetadata(self: *Elf) !void {
487469 .p_align = p_align,
488470 .p_flags = elf.PF_X | elf.PF_R,
489471 });
490 try self.atom_free_lists.putNoClobber(self.base.allocator, self.phdr_load_re_index.?, .{});
491472 self.entry_addr = null;
492473 self.phdr_table_dirty = true;
493474 }
......@@ -504,7 +485,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {
504485 // we'll need to re-use that function anyway, in case the GOT grows and overlaps something
505486 // else in virtual memory.
506487 const got_addr: u32 = if (self.base.options.target.cpu.arch.ptrBitWidth() >= 32) 0x4000000 else 0x8000;
507 try self.program_headers.append(self.base.allocator, .{
488 try self.program_headers.append(gpa, .{
508489 .p_type = elf.PT_LOAD,
509490 .p_offset = off,
510491 .p_filesz = file_size,
......@@ -527,7 +508,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {
527508 log.debug("found PT_LOAD RO free space 0x{x} to 0x{x}", .{ off, off + file_size });
528509 // TODO Same as for GOT
529510 const rodata_addr: u32 = if (self.base.options.target.cpu.arch.ptrBitWidth() >= 32) 0xc000000 else 0xa000;
530 try self.program_headers.append(self.base.allocator, .{
511 try self.program_headers.append(gpa, .{
531512 .p_type = elf.PT_LOAD,
532513 .p_offset = off,
533514 .p_filesz = file_size,
......@@ -537,7 +518,6 @@ pub fn populateMissingMetadata(self: *Elf) !void {
537518 .p_align = p_align,
538519 .p_flags = elf.PF_R,
539520 });
540 try self.atom_free_lists.putNoClobber(self.base.allocator, self.phdr_load_ro_index.?, .{});
541521 self.phdr_table_dirty = true;
542522 }
543523
......@@ -551,7 +531,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {
551531 log.debug("found PT_LOAD RW free space 0x{x} to 0x{x}", .{ off, off + file_size });
552532 // TODO Same as for GOT
553533 const rwdata_addr: u32 = if (self.base.options.target.cpu.arch.ptrBitWidth() >= 32) 0x10000000 else 0xc000;
554 try self.program_headers.append(self.base.allocator, .{
534 try self.program_headers.append(gpa, .{
555535 .p_type = elf.PT_LOAD,
556536 .p_offset = off,
557537 .p_filesz = file_size,
......@@ -561,148 +541,145 @@ pub fn populateMissingMetadata(self: *Elf) !void {
561541 .p_align = p_align,
562542 .p_flags = elf.PF_R | elf.PF_W,
563543 });
564 try self.atom_free_lists.putNoClobber(self.base.allocator, self.phdr_load_rw_index.?, .{});
565544 self.phdr_table_dirty = true;
566545 }
567546
568547 if (self.shstrtab_index == null) {
569 self.shstrtab_index = @intCast(u16, self.sections.items.len);
570 assert(self.shstrtab.items.len == 0);
571 try self.shstrtab.append(self.base.allocator, 0); // need a 0 at position 0
572 const off = self.findFreeSpace(self.shstrtab.items.len, 1);
573 log.debug("found shstrtab free space 0x{x} to 0x{x}", .{ off, off + self.shstrtab.items.len });
574 try self.sections.append(self.base.allocator, .{
575 .sh_name = try self.makeString(".shstrtab"),
576 .sh_type = elf.SHT_STRTAB,
577 .sh_flags = 0,
578 .sh_addr = 0,
579 .sh_offset = off,
580 .sh_size = self.shstrtab.items.len,
581 .sh_link = 0,
582 .sh_info = 0,
583 .sh_addralign = 1,
584 .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,
585567 });
586568 self.shstrtab_dirty = true;
587569 self.shdr_table_dirty = true;
588570 }
589571
590572 if (self.text_section_index == null) {
591 self.text_section_index = @intCast(u16, self.sections.items.len);
573 self.text_section_index = @intCast(u16, self.sections.slice().len);
592574 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
593575
594 try self.sections.append(self.base.allocator, .{
595 .sh_name = try self.makeString(".text"),
596 .sh_type = elf.SHT_PROGBITS,
597 .sh_flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR,
598 .sh_addr = phdr.p_vaddr,
599 .sh_offset = phdr.p_offset,
600 .sh_size = phdr.p_filesz,
601 .sh_link = 0,
602 .sh_info = 0,
603 .sh_addralign = 1,
604 .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.?,
605590 });
606 try self.phdr_shdr_table.putNoClobber(
607 self.base.allocator,
608 self.phdr_load_re_index.?,
609 self.text_section_index.?,
610 );
611591 self.shdr_table_dirty = true;
612592 }
613593
614594 if (self.got_section_index == null) {
615 self.got_section_index = @intCast(u16, self.sections.items.len);
595 self.got_section_index = @intCast(u16, self.sections.slice().len);
616596 const phdr = &self.program_headers.items[self.phdr_got_index.?];
617597
618 try self.sections.append(self.base.allocator, .{
619 .sh_name = try self.makeString(".got"),
620 .sh_type = elf.SHT_PROGBITS,
621 .sh_flags = elf.SHF_ALLOC,
622 .sh_addr = phdr.p_vaddr,
623 .sh_offset = phdr.p_offset,
624 .sh_size = phdr.p_filesz,
625 .sh_link = 0,
626 .sh_info = 0,
627 .sh_addralign = @as(u16, ptr_size),
628 .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.?,
629612 });
630 try self.phdr_shdr_table.putNoClobber(
631 self.base.allocator,
632 self.phdr_got_index.?,
633 self.got_section_index.?,
634 );
635613 self.shdr_table_dirty = true;
636614 }
637615
638616 if (self.rodata_section_index == null) {
639 self.rodata_section_index = @intCast(u16, self.sections.items.len);
617 self.rodata_section_index = @intCast(u16, self.sections.slice().len);
640618 const phdr = &self.program_headers.items[self.phdr_load_ro_index.?];
641619
642 try self.sections.append(self.base.allocator, .{
643 .sh_name = try self.makeString(".rodata"),
644 .sh_type = elf.SHT_PROGBITS,
645 .sh_flags = elf.SHF_ALLOC,
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,
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.?,
653634 });
654 try self.phdr_shdr_table.putNoClobber(
655 self.base.allocator,
656 self.phdr_load_ro_index.?,
657 self.rodata_section_index.?,
658 );
659635 self.shdr_table_dirty = true;
660636 }
661637
662638 if (self.data_section_index == null) {
663 self.data_section_index = @intCast(u16, self.sections.items.len);
639 self.data_section_index = @intCast(u16, self.sections.slice().len);
664640 const phdr = &self.program_headers.items[self.phdr_load_rw_index.?];
665641
666 try self.sections.append(self.base.allocator, .{
667 .sh_name = try self.makeString(".data"),
668 .sh_type = elf.SHT_PROGBITS,
669 .sh_flags = elf.SHF_WRITE | 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,
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.?,
677656 });
678 try self.phdr_shdr_table.putNoClobber(
679 self.base.allocator,
680 self.phdr_load_rw_index.?,
681 self.data_section_index.?,
682 );
683657 self.shdr_table_dirty = true;
684658 }
685659
686660 if (self.symtab_section_index == null) {
687 self.symtab_section_index = @intCast(u16, self.sections.items.len);
661 self.symtab_section_index = @intCast(u16, self.sections.slice().len);
688662 const min_align: u16 = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym);
689663 const each_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Sym) else @sizeOf(elf.Elf64_Sym);
690664 const file_size = self.base.options.symbol_count_hint * each_size;
691665 const off = self.findFreeSpace(file_size, min_align);
692666 log.debug("found symtab free space 0x{x} to 0x{x}", .{ off, off + file_size });
693667
694 try self.sections.append(self.base.allocator, .{
695 .sh_name = try self.makeString(".symtab"),
696 .sh_type = elf.SHT_SYMTAB,
697 .sh_flags = 0,
698 .sh_addr = 0,
699 .sh_offset = off,
700 .sh_size = file_size,
701 // The section header index of the associated string table.
702 .sh_link = self.shstrtab_index.?,
703 .sh_info = @intCast(u32, self.local_symbols.items.len),
704 .sh_addralign = min_align,
705 .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,
706683 });
707684 self.shdr_table_dirty = true;
708685 try self.writeSymbol(0);
......@@ -710,27 +687,30 @@ pub fn populateMissingMetadata(self: *Elf) !void {
710687
711688 if (self.dwarf) |*dw| {
712689 if (self.debug_str_section_index == null) {
713 self.debug_str_section_index = @intCast(u16, self.sections.items.len);
714 assert(dw.strtab.items.len == 0);
715 try dw.strtab.append(self.base.allocator, 0);
716 try self.sections.append(self.base.allocator, .{
717 .sh_name = try self.makeString(".debug_str"),
718 .sh_type = elf.SHT_PROGBITS,
719 .sh_flags = elf.SHF_MERGE | elf.SHF_STRINGS,
720 .sh_addr = 0,
721 .sh_offset = 0,
722 .sh_size = 0,
723 .sh_link = 0,
724 .sh_info = 0,
725 .sh_addralign = 1,
726 .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,
727707 });
728708 self.debug_strtab_dirty = true;
729709 self.shdr_table_dirty = true;
730710 }
731711
732712 if (self.debug_info_section_index == null) {
733 self.debug_info_section_index = @intCast(u16, self.sections.items.len);
713 self.debug_info_section_index = @intCast(u16, self.sections.slice().len);
734714
735715 const file_size_hint = 200;
736716 const p_align = 1;
......@@ -739,24 +719,27 @@ pub fn populateMissingMetadata(self: *Elf) !void {
739719 off,
740720 off + file_size_hint,
741721 });
742 try self.sections.append(self.base.allocator, .{
743 .sh_name = try self.makeString(".debug_info"),
744 .sh_type = elf.SHT_PROGBITS,
745 .sh_flags = 0,
746 .sh_addr = 0,
747 .sh_offset = off,
748 .sh_size = file_size_hint,
749 .sh_link = 0,
750 .sh_info = 0,
751 .sh_addralign = p_align,
752 .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,
753736 });
754737 self.shdr_table_dirty = true;
755738 self.debug_info_header_dirty = true;
756739 }
757740
758741 if (self.debug_abbrev_section_index == null) {
759 self.debug_abbrev_section_index = @intCast(u16, self.sections.items.len);
742 self.debug_abbrev_section_index = @intCast(u16, self.sections.slice().len);
760743
761744 const file_size_hint = 128;
762745 const p_align = 1;
......@@ -765,24 +748,27 @@ pub fn populateMissingMetadata(self: *Elf) !void {
765748 off,
766749 off + file_size_hint,
767750 });
768 try self.sections.append(self.base.allocator, .{
769 .sh_name = try self.makeString(".debug_abbrev"),
770 .sh_type = elf.SHT_PROGBITS,
771 .sh_flags = 0,
772 .sh_addr = 0,
773 .sh_offset = off,
774 .sh_size = file_size_hint,
775 .sh_link = 0,
776 .sh_info = 0,
777 .sh_addralign = p_align,
778 .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,
779765 });
780766 self.shdr_table_dirty = true;
781767 self.debug_abbrev_section_dirty = true;
782768 }
783769
784770 if (self.debug_aranges_section_index == null) {
785 self.debug_aranges_section_index = @intCast(u16, self.sections.items.len);
771 self.debug_aranges_section_index = @intCast(u16, self.sections.slice().len);
786772
787773 const file_size_hint = 160;
788774 const p_align = 16;
......@@ -791,24 +777,27 @@ pub fn populateMissingMetadata(self: *Elf) !void {
791777 off,
792778 off + file_size_hint,
793779 });
794 try self.sections.append(self.base.allocator, .{
795 .sh_name = try self.makeString(".debug_aranges"),
796 .sh_type = elf.SHT_PROGBITS,
797 .sh_flags = 0,
798 .sh_addr = 0,
799 .sh_offset = off,
800 .sh_size = file_size_hint,
801 .sh_link = 0,
802 .sh_info = 0,
803 .sh_addralign = p_align,
804 .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,
805794 });
806795 self.shdr_table_dirty = true;
807796 self.debug_aranges_section_dirty = true;
808797 }
809798
810799 if (self.debug_line_section_index == null) {
811 self.debug_line_section_index = @intCast(u16, self.sections.items.len);
800 self.debug_line_section_index = @intCast(u16, self.sections.slice().len);
812801
813802 const file_size_hint = 250;
814803 const p_align = 1;
......@@ -817,17 +806,20 @@ pub fn populateMissingMetadata(self: *Elf) !void {
817806 off,
818807 off + file_size_hint,
819808 });
820 try self.sections.append(self.base.allocator, .{
821 .sh_name = try self.makeString(".debug_line"),
822 .sh_type = elf.SHT_PROGBITS,
823 .sh_flags = 0,
824 .sh_addr = 0,
825 .sh_offset = off,
826 .sh_size = file_size_hint,
827 .sh_link = 0,
828 .sh_info = 0,
829 .sh_addralign = p_align,
830 .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,
831823 });
832824 self.shdr_table_dirty = true;
833825 self.debug_line_header_dirty = true;
......@@ -843,7 +835,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {
843835 .p64 => @alignOf(elf.Elf64_Shdr),
844836 };
845837 if (self.shdr_table_offset == null) {
846 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);
847839 self.shdr_table_dirty = true;
848840 }
849841
......@@ -874,7 +866,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {
874866 // offset + it's filesize.
875867 var max_file_offset: u64 = 0;
876868
877 for (self.sections.items) |shdr| {
869 for (self.sections.items(.shdr)) |shdr| {
878870 if (shdr.sh_offset + shdr.sh_size > max_file_offset) {
879871 max_file_offset = shdr.sh_offset + shdr.sh_size;
880872 }
......@@ -884,15 +876,18 @@ pub fn populateMissingMetadata(self: *Elf) !void {
884876 }
885877}
886878
887fn growAllocSection(self: *Elf, shdr_index: u16, phdr_index: u16, needed_size: u64) !void {
879fn growAllocSection(self: *Elf, shdr_index: u16, needed_size: u64) !void {
888880 // TODO Also detect virtual address collisions.
889 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];
890883 const phdr = &self.program_headers.items[phdr_index];
884 const maybe_last_atom_index = self.sections.items(.last_atom_index)[shdr_index];
891885
892886 if (needed_size > self.allocatedSize(shdr.sh_offset)) {
893887 // Must move the entire section.
894888 const new_offset = self.findFreeSpace(needed_size, self.page_size);
895 const existing_size = if (self.atoms.get(phdr_index)) |last| blk: {
889 const existing_size = if (maybe_last_atom_index) |last_atom_index| blk: {
890 const last = self.getAtom(last_atom_index);
896891 const sym = last.getSymbol(self);
897892 break :blk (sym.st_value + sym.st_size) - phdr.p_vaddr;
898893 } else if (shdr_index == self.got_section_index.?) blk: {
......@@ -900,8 +895,8 @@ fn growAllocSection(self: *Elf, shdr_index: u16, phdr_index: u16, needed_size: u
900895 } else 0;
901896 shdr.sh_size = 0;
902897
903 log.debug("new '{s}' file offset 0x{x} to 0x{x}", .{
904 self.getString(shdr.sh_name),
898 log.debug("new '{?s}' file offset 0x{x} to 0x{x}", .{
899 self.shstrtab.get(shdr.sh_name),
905900 new_offset,
906901 new_offset + existing_size,
907902 });
......@@ -927,7 +922,7 @@ pub fn growNonAllocSection(
927922 min_alignment: u32,
928923 requires_file_copy: bool,
929924) !void {
930 const shdr = &self.sections.items[shdr_index];
925 const shdr = &self.sections.items(.shdr)[shdr_index];
931926
932927 if (needed_size > self.allocatedSize(shdr.sh_offset)) {
933928 const existing_size = if (self.symtab_section_index.? == shdr_index) blk: {
......@@ -940,7 +935,7 @@ pub fn growNonAllocSection(
940935 shdr.sh_size = 0;
941936 // Move all the symbols to a new file location.
942937 const new_offset = self.findFreeSpace(needed_size, min_alignment);
943 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 });
944939
945940 if (requires_file_copy) {
946941 const amt = try self.base.file.?.copyRangeAll(
......@@ -1011,6 +1006,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
10111006 }
10121007 }
10131008
1009 const gpa = self.base.allocator;
10141010 var sub_prog_node = prog_node.start("ELF Flush", 0);
10151011 sub_prog_node.activate();
10161012 defer sub_prog_node.end();
......@@ -1029,12 +1025,13 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
10291025 {
10301026 var it = self.relocs.iterator();
10311027 while (it.next()) |entry| {
1032 const atom = entry.key_ptr.*;
1028 const atom_index = entry.key_ptr.*;
10331029 const relocs = entry.value_ptr.*;
1030 const atom = self.getAtom(atom_index);
10341031 const source_sym = atom.getSymbol(self);
1035 const source_shdr = self.sections.items[source_sym.st_shndx];
1032 const source_shdr = self.sections.items(.shdr)[source_sym.st_shndx];
10361033
1037 log.debug("relocating '{s}'", .{self.getString(source_sym.st_name)});
1034 log.debug("relocating '{?s}'", .{self.shstrtab.get(source_sym.st_name)});
10381035
10391036 for (relocs.items) |*reloc| {
10401037 const target_sym = self.local_symbols.items[reloc.target];
......@@ -1045,10 +1042,10 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
10451042 const section_offset = (source_sym.st_value + reloc.offset) - source_shdr.sh_addr;
10461043 const file_offset = source_shdr.sh_offset + section_offset;
10471044
1048 log.debug(" ({x}: [() => 0x{x}] ({s}))", .{
1045 log.debug(" ({x}: [() => 0x{x}] ({?s}))", .{
10491046 reloc.offset,
10501047 target_vaddr,
1051 self.getString(target_sym.st_name),
1048 self.shstrtab.get(target_sym.st_name),
10521049 });
10531050
10541051 switch (self.ptr_width) {
......@@ -1126,8 +1123,8 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
11261123
11271124 switch (self.ptr_width) {
11281125 .p32 => {
1129 const buf = try self.base.allocator.alloc(elf.Elf32_Phdr, self.program_headers.items.len);
1130 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);
11311128
11321129 for (buf) |*phdr, i| {
11331130 phdr.* = progHeaderTo32(self.program_headers.items[i]);
......@@ -1138,8 +1135,8 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
11381135 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?);
11391136 },
11401137 .p64 => {
1141 const buf = try self.base.allocator.alloc(elf.Elf64_Phdr, self.program_headers.items.len);
1142 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);
11431140
11441141 for (buf) |*phdr, i| {
11451142 phdr.* = self.program_headers.items[i];
......@@ -1155,20 +1152,20 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
11551152
11561153 {
11571154 const shdr_index = self.shstrtab_index.?;
1158 if (self.shstrtab_dirty or self.shstrtab.items.len != self.sections.items[shdr_index].sh_size) {
1159 try self.growNonAllocSection(shdr_index, self.shstrtab.items.len, 1, false);
1160 const shstrtab_sect = self.sections.items[shdr_index];
1161 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);
11621159 self.shstrtab_dirty = false;
11631160 }
11641161 }
11651162
11661163 if (self.dwarf) |dwarf| {
11671164 const shdr_index = self.debug_str_section_index.?;
1168 if (self.debug_strtab_dirty or dwarf.strtab.items.len != self.sections.items[shdr_index].sh_size) {
1169 try self.growNonAllocSection(shdr_index, dwarf.strtab.items.len, 1, false);
1170 const debug_strtab_sect = self.sections.items[shdr_index];
1171 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);
11721169 self.debug_strtab_dirty = false;
11731170 }
11741171 }
......@@ -1183,7 +1180,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
11831180 .p64 => @alignOf(elf.Elf64_Shdr),
11841181 };
11851182 const allocated_size = self.allocatedSize(self.shdr_table_offset.?);
1186 const needed_size = self.sections.items.len * shsize;
1183 const needed_size = self.sections.slice().len * shsize;
11871184
11881185 if (needed_size > allocated_size) {
11891186 self.shdr_table_offset = null; // free the space
......@@ -1192,12 +1189,13 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
11921189
11931190 switch (self.ptr_width) {
11941191 .p32 => {
1195 const buf = try self.base.allocator.alloc(elf.Elf32_Shdr, self.sections.items.len);
1196 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);
11971195
11981196 for (buf) |*shdr, i| {
1199 shdr.* = sectHeaderTo32(self.sections.items[i]);
1200 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.* });
12011199 if (foreign_endian) {
12021200 mem.byteSwapAllFields(elf.Elf32_Shdr, shdr);
12031201 }
......@@ -1205,12 +1203,13 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
12051203 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);
12061204 },
12071205 .p64 => {
1208 const buf = try self.base.allocator.alloc(elf.Elf64_Shdr, self.sections.items.len);
1209 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);
12101209
12111210 for (buf) |*shdr, i| {
1212 shdr.* = self.sections.items[i];
1213 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.* });
12141213 if (foreign_endian) {
12151214 mem.byteSwapAllFields(elf.Elf64_Shdr, shdr);
12161215 }
......@@ -2021,7 +2020,7 @@ fn writeElfHeader(self: *Elf) !void {
20212020 mem.writeInt(u16, hdr_buf[index..][0..2], e_shentsize, endian);
20222021 index += 2;
20232022
2024 const e_shnum = @intCast(u16, self.sections.items.len);
2023 const e_shnum = @intCast(u16, self.sections.slice().len);
20252024 mem.writeInt(u16, hdr_buf[index..][0..2], e_shnum, endian);
20262025 index += 2;
20272026
......@@ -2033,124 +2032,145 @@ fn writeElfHeader(self: *Elf) !void {
20332032 try self.base.file.?.pwriteAll(hdr_buf[0..index], 0);
20342033}
20352034
2036fn freeTextBlock(self: *Elf, text_block: *TextBlock, phdr_index: u16) void {
2037 const local_sym = text_block.getSymbol(self);
2038 const name_str_index = local_sym.st_name;
2039 const name = self.getString(name_str_index);
2040 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) });
20412038
2042 self.freeRelocationsForTextBlock(text_block);
2039 Atom.freeRelocations(self, atom_index);
20432040
2044 const free_list = self.atom_free_lists.getPtr(phdr_index).?;
2041 const gpa = self.base.allocator;
2042 const shndx = atom.getSymbol(self).st_shndx;
2043 const free_list = &self.sections.items(.free_list)[shndx];
20452044 var already_have_free_list_node = false;
20462045 {
20472046 var i: usize = 0;
20482047 // TODO turn free_list into a hash map
20492048 while (i < free_list.items.len) {
2050 if (free_list.items[i] == text_block) {
2049 if (free_list.items[i] == atom_index) {
20512050 _ = free_list.swapRemove(i);
20522051 continue;
20532052 }
2054 if (free_list.items[i] == text_block.prev) {
2053 if (free_list.items[i] == atom.prev_index) {
20552054 already_have_free_list_node = true;
20562055 }
20572056 i += 1;
20582057 }
20592058 }
20602059
2061 if (self.atoms.getPtr(phdr_index)) |last_block| {
2062 if (last_block.* == text_block) {
2063 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| {
20642064 // TODO shrink the section size here
2065 last_block.* = prev;
2065 maybe_last_atom_index.* = prev_index;
20662066 } else {
2067 _ = self.atoms.fetchRemove(phdr_index);
2067 maybe_last_atom_index.* = null;
20682068 }
20692069 }
20702070 }
20712071
2072 if (text_block.prev) |prev| {
2073 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;
20742075
2075 if (!already_have_free_list_node and prev.freeListEligible(self)) {
2076 if (!already_have_free_list_node and prev.*.freeListEligible(self)) {
20762077 // The free list is heuristics, it doesn't have to be perfect, so we can
20772078 // ignore the OOM here.
2078 free_list.append(self.base.allocator, prev) catch {};
2079 free_list.append(gpa, prev_index) catch {};
20792080 }
20802081 } else {
2081 text_block.prev = null;
2082 self.getAtomPtr(atom_index).prev_index = null;
20822083 }
20832084
2084 if (text_block.next) |next| {
2085 next.prev = text_block.prev;
2085 if (atom.next_index) |next_index| {
2086 self.getAtomPtr(next_index).prev_index = atom.prev_index;
20862087 } else {
2087 text_block.next = null;
2088 self.getAtomPtr(atom_index).next_index = null;
20882089 }
20892090
20902091 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
2091 const local_sym_index = text_block.getSymbolIndex().?;
2092 self.local_symbol_free_list.append(self.base.allocator, local_sym_index) catch {};
2092 const local_sym_index = atom.getSymbolIndex().?;
2093
2094 self.local_symbol_free_list.append(gpa, local_sym_index) catch {};
20932095 self.local_symbols.items[local_sym_index].st_info = 0;
2096 self.local_symbols.items[local_sym_index].st_shndx = 0;
20942097 _ = self.atom_by_index_table.remove(local_sym_index);
2095 text_block.local_sym_index = 0;
2098 self.getAtomPtr(atom_index).local_sym_index = 0;
20962099
2097 self.offset_table_free_list.append(self.base.allocator, text_block.offset_table_index) catch {};
2098
2099 if (self.dwarf) |*dw| {
2100 dw.freeAtom(&text_block.dbg_info_atom);
2101 }
2100 self.offset_table_free_list.append(self.base.allocator, atom.offset_table_index) catch {};
21022101}
21032102
2104fn 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 {
21052104 _ = self;
2106 _ = text_block;
2105 _ = atom_index;
21072106 _ = new_block_size;
2108 _ = phdr_index;
21092107}
21102108
2111fn growTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, alignment: u64, phdr_index: u16) !u64 {
2112 const sym = text_block.getSymbol(self);
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);
21132112 const align_ok = mem.alignBackwardGeneric(u64, sym.st_value, alignment) == sym.st_value;
2114 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);
21152114 if (!need_realloc) return sym.st_value;
2116 return self.allocateTextBlock(text_block, new_block_size, alignment, phdr_index);
2115 return self.allocateAtom(atom_index, new_block_size, alignment);
2116}
2117
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;
21172133}
21182134
2119fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, alignment: u64, phdr_index: u16) !u64 {
2120 const shdr_index = self.phdr_shdr_table.get(phdr_index).?;
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];
21212139 const phdr = &self.program_headers.items[phdr_index];
2122 const shdr = &self.sections.items[shdr_index];
2123 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);
21242144
2125 // 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,
21262146 // and possibly removing a free list node.
21272147 // It would be simpler to do it inside the for loop below, but that would cause a
21282148 // problem if an error was returned later in the function. So this action
21292149 // is actually carried out at the end of the function, when errors are no longer possible.
2130 var block_placement: ?*TextBlock = null;
2150 var atom_placement: ?Atom.Index = null;
21312151 var free_list_removal: ?usize = null;
2132 var free_list = self.atom_free_lists.get(phdr_index).?;
21332152
21342153 // First we look for an appropriately sized free list node.
21352154 // The list is unordered. We'll just take the first thing that works.
21362155 const vaddr = blk: {
21372156 var i: usize = 0;
21382157 while (i < free_list.items.len) {
2139 const big_block = free_list.items[i];
2140 // We now have a pointer to a live text block that has too much capacity.
2141 // Is it enough that we could fit this new text block?
2142 const sym = big_block.getSymbol(self);
2143 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);
21442164 const ideal_capacity = padToIdeal(capacity);
2145 const ideal_capacity_end_vaddr = std.math.add(u64, sym.st_value, ideal_capacity) catch ideal_capacity;
2146 const capacity_end_vaddr = sym.st_value + capacity;
2147 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;
21482168 const new_start_vaddr = mem.alignBackwardGeneric(u64, new_start_vaddr_unaligned, alignment);
21492169 if (new_start_vaddr < ideal_capacity_end_vaddr) {
21502170 // Additional bookkeeping here to notice if this free list node
21512171 // should be deleted because the block that it points to has grown to take up
21522172 // more of the extra capacity.
2153 if (!big_block.freeListEligible(self)) {
2173 if (!big_atom.freeListEligible(self)) {
21542174 _ = free_list.swapRemove(i);
21552175 } else {
21562176 i += 1;
......@@ -2164,29 +2184,33 @@ fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, al
21642184 const keep_free_list_node = remaining_capacity >= min_text_capacity;
21652185
21662186 // Set up the metadata to be updated, after errors are no longer possible.
2167 block_placement = big_block;
2187 atom_placement = big_atom_index;
21682188 if (!keep_free_list_node) {
21692189 free_list_removal = i;
21702190 }
21712191 break :blk new_start_vaddr;
2172 } else if (self.atoms.get(phdr_index)) |last| {
2173 const sym = last.getSymbol(self);
2174 const ideal_capacity = padToIdeal(sym.st_size);
2175 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;
21762197 const new_start_vaddr = mem.alignForwardGeneric(u64, ideal_capacity_end_vaddr, alignment);
21772198 // Set up the metadata to be updated, after errors are no longer possible.
2178 block_placement = last;
2199 atom_placement = last_index;
21792200 break :blk new_start_vaddr;
21802201 } else {
21812202 break :blk phdr.p_vaddr;
21822203 }
21832204 };
21842205
2185 const expand_text_section = block_placement == null or block_placement.?.next == null;
2186 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) {
21872211 const needed_size = (vaddr + new_block_size) - phdr.p_vaddr;
2188 try self.growAllocSection(shdr_index, phdr_index, needed_size);
2189 _ = 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;
21902214
21912215 if (self.dwarf) |_| {
21922216 // The .debug_info section has `low_pc` and `high_pc` values which is the virtual address
......@@ -2201,23 +2225,28 @@ fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, al
22012225 }
22022226 shdr.sh_addralign = math.max(shdr.sh_addralign, alignment);
22032227
2204 // This function can also reallocate a text block.
2228 // This function can also reallocate an atom.
22052229 // In this case we need to "unplug" it from its previous location before
22062230 // plugging it in to its new location.
2207 if (text_block.prev) |prev| {
2208 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;
22092234 }
2210 if (text_block.next) |next| {
2211 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;
22122238 }
22132239
2214 if (block_placement) |big_block| {
2215 text_block.prev = big_block;
2216 text_block.next = big_block.next;
2217 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;
22182246 } else {
2219 text_block.prev = null;
2220 text_block.next = null;
2247 const atom_ptr = self.getAtomPtr(atom_index);
2248 atom_ptr.prev_index = null;
2249 atom_ptr.next_index = null;
22212250 }
22222251 if (free_list_removal) |i| {
22232252 _ = free_list.swapRemove(i);
......@@ -2272,15 +2301,10 @@ pub fn allocateGotOffset(self: *Elf) !u32 {
22722301 return index;
22732302}
22742303
2275fn freeRelocationsForTextBlock(self: *Elf, text_block: *TextBlock) void {
2276 var removed_relocs = self.relocs.fetchRemove(text_block);
2277 if (removed_relocs) |*relocs| relocs.value.deinit(self.base.allocator);
2278}
2279
22802304fn freeUnnamedConsts(self: *Elf, decl_index: Module.Decl.Index) void {
22812305 const unnamed_consts = self.unnamed_const_atoms.getPtr(decl_index) orelse return;
22822306 for (unnamed_consts.items) |atom| {
2283 self.freeTextBlock(atom, self.phdr_load_ro_index.?);
2307 self.freeAtom(atom);
22842308 }
22852309 unnamed_consts.clearAndFree(self.base.allocator);
22862310}
......@@ -2295,43 +2319,57 @@ pub fn freeDecl(self: *Elf, decl_index: Module.Decl.Index) void {
22952319
22962320 log.debug("freeDecl {*}", .{decl});
22972321
2298 if (self.decls.fetchRemove(decl_index)) |kv| {
2299 if (kv.value) |index| {
2300 self.freeTextBlock(&decl.link.elf, index);
2301 self.freeUnnamedConsts(decl_index);
2302 }
2322 if (self.decls.fetchRemove(decl_index)) |const_kv| {
2323 var kv = const_kv;
2324 self.freeAtom(kv.value.atom);
2325 self.freeUnnamedConsts(decl_index);
2326 kv.value.exports.deinit(self.base.allocator);
23032327 }
23042328
23052329 if (self.dwarf) |*dw| {
2306 dw.freeDecl(decl);
2330 dw.freeDecl(decl_index);
2331 }
2332}
2333
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 };
23072342 }
2343 return gop.value_ptr.atom;
23082344}
23092345
2310fn 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);
23112348 const ty = decl.ty;
23122349 const zig_ty = ty.zigTypeTag();
23132350 const val = decl.val;
2314 const phdr_index: u16 = blk: {
2351 const shdr_index: u16 = blk: {
23152352 if (val.isUndefDeep()) {
23162353 // TODO in release-fast and release-small, we should put undef in .bss
2317 break :blk self.phdr_load_rw_index.?;
2354 break :blk self.data_section_index.?;
23182355 }
23192356
23202357 switch (zig_ty) {
23212358 // TODO: what if this is a function pointer?
2322 .Fn => break :blk self.phdr_load_re_index.?,
2359 .Fn => break :blk self.text_section_index.?,
23232360 else => {
23242361 if (val.castTag(.variable)) |_| {
2325 break :blk self.phdr_load_rw_index.?;
2362 break :blk self.data_section_index.?;
23262363 }
2327 break :blk self.phdr_load_ro_index.?;
2364 break :blk self.rodata_section_index.?;
23282365 },
23292366 }
23302367 };
2331 return phdr_index;
2368 return shdr_index;
23322369}
23332370
23342371fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, stt_bits: u8) !*elf.Elf64_Sym {
2372 const gpa = self.base.allocator;
23352373 const mod = self.base.options.module.?;
23362374 const decl = mod.declPtr(decl_index);
23372375
......@@ -2341,60 +2379,65 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s
23412379 log.debug("updateDeclCode {s}{*}", .{ decl_name, decl });
23422380 const required_alignment = decl.getAlignment(self.base.options.target);
23432381
2344 const decl_ptr = self.decls.getPtr(decl_index).?;
2345 if (decl_ptr.* == null) {
2346 decl_ptr.* = try self.getDeclPhdrIndex(decl);
2347 }
2348 const phdr_index = decl_ptr.*.?;
2349 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);
2385
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;
23502393
2351 const local_sym = decl.link.elf.getSymbolPtr(self);
2352 if (local_sym.st_size != 0) {
2353 const capacity = decl.link.elf.capacity(self);
2394 const capacity = atom.capacity(self);
23542395 const need_realloc = code.len > capacity or
23552396 !mem.isAlignedGeneric(u64, local_sym.st_value, required_alignment);
2397
23562398 if (need_realloc) {
2357 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);
23582400 log.debug("growing {s} from 0x{x} to 0x{x}", .{ decl_name, local_sym.st_value, vaddr });
23592401 if (vaddr != local_sym.st_value) {
23602402 local_sym.st_value = vaddr;
23612403
23622404 log.debug(" (writing new offset table entry)", .{});
2363 self.offset_table.items[decl.link.elf.offset_table_index] = vaddr;
2364 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);
23652407 }
23662408 } else if (code.len < local_sym.st_size) {
2367 self.shrinkTextBlock(&decl.link.elf, code.len, phdr_index);
2409 self.shrinkAtom(atom_index, code.len);
23682410 }
23692411 local_sym.st_size = code.len;
2370 local_sym.st_name = try self.updateString(local_sym.st_name, decl_name);
2371 local_sym.st_info = (elf.STB_LOCAL << 4) | stt_bits;
2372 local_sym.st_other = 0;
2373 local_sym.st_shndx = shdr_index;
2412
23742413 // TODO this write could be avoided if no fields of the symbol were changed.
2375 try self.writeSymbol(decl.link.elf.getSymbolIndex().?);
2414 try self.writeSymbol(atom.getSymbolIndex().?);
23762415 } else {
2377 const name_str_index = try self.makeString(decl_name);
2378 const vaddr = try self.allocateTextBlock(&decl.link.elf, code.len, required_alignment, phdr_index);
2379 errdefer self.freeTextBlock(&decl.link.elf, phdr_index);
2380 log.debug("allocated text block for {s} at 0x{x}", .{ decl_name, vaddr });
2381
2416 const local_sym = atom.getSymbolPtr(self);
23822417 local_sym.* = .{
2383 .st_name = name_str_index,
2418 .st_name = try self.shstrtab.insert(gpa, decl_name),
23842419 .st_info = (elf.STB_LOCAL << 4) | stt_bits,
23852420 .st_other = 0,
23862421 .st_shndx = shdr_index,
2387 .st_value = vaddr,
2388 .st_size = code.len,
2422 .st_value = 0,
2423 .st_size = 0,
23892424 };
2390 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 });
23912428
2392 try self.writeSymbol(decl.link.elf.getSymbolIndex().?);
2393 try self.writeOffsetTableEntry(decl.link.elf.offset_table_index);
2429 self.offset_table.items[atom.offset_table_index] = vaddr;
2430 local_sym.st_value = vaddr;
2431 local_sym.st_size = code.len;
2432
2433 try self.writeSymbol(atom.getSymbolIndex().?);
2434 try self.writeOffsetTableEntry(atom.offset_table_index);
23942435 }
23952436
2437 const local_sym = atom.getSymbolPtr(self);
2438 const phdr_index = self.sections.items(.phdr_index)[shdr_index];
23962439 const section_offset = local_sym.st_value - self.program_headers.items[phdr_index].p_vaddr;
2397 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;
23982441 try self.base.file.?.pwriteAll(code, file_offset);
23992442
24002443 return local_sym;
......@@ -2413,15 +2456,10 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven
24132456
24142457 const decl_index = func.owner_decl;
24152458 const decl = module.declPtr(decl_index);
2416 const atom = &decl.link.elf;
2417 try atom.ensureInitialized(self);
2418 const gop = try self.decls.getOrPut(self.base.allocator, decl_index);
2419 if (gop.found_existing) {
2420 self.freeUnnamedConsts(decl_index);
2421 self.freeRelocationsForTextBlock(atom);
2422 } else {
2423 gop.value_ptr.* = null;
2424 }
2459
2460 const atom_index = try self.getOrCreateAtomForDecl(decl_index);
2461 self.freeUnnamedConsts(decl_index);
2462 Atom.freeRelocations(self, atom_index);
24252463
24262464 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
24272465 defer code_buffer.deinit();
......@@ -2483,16 +2521,9 @@ pub fn updateDecl(self: *Elf, module: *Module, decl_index: Module.Decl.Index) !v
24832521 }
24842522 }
24852523
2486 assert(!self.unnamed_const_atoms.contains(decl_index));
2487
2488 const atom = &decl.link.elf;
2489 try atom.ensureInitialized(self);
2490 const gop = try self.decls.getOrPut(self.base.allocator, decl_index);
2491 if (gop.found_existing) {
2492 self.freeRelocationsForTextBlock(atom);
2493 } else {
2494 gop.value_ptr.* = null;
2495 }
2524 const atom_index = try self.getOrCreateAtomForDecl(decl_index);
2525 Atom.freeRelocations(self, atom_index);
2526 const atom = self.getAtom(atom_index);
24962527
24972528 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
24982529 defer code_buffer.deinit();
......@@ -2509,14 +2540,14 @@ pub fn updateDecl(self: *Elf, module: *Module, decl_index: Module.Decl.Index) !v
25092540 }, &code_buffer, .{
25102541 .dwarf = ds,
25112542 }, .{
2512 .parent_atom_index = decl.link.elf.getSymbolIndex().?,
2543 .parent_atom_index = atom.getSymbolIndex().?,
25132544 })
25142545 else
25152546 try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
25162547 .ty = decl.ty,
25172548 .val = decl_val,
25182549 }, &code_buffer, .none, .{
2519 .parent_atom_index = decl.link.elf.getSymbolIndex().?,
2550 .parent_atom_index = atom.getSymbolIndex().?,
25202551 });
25212552
25222553 const code = switch (res) {
......@@ -2545,41 +2576,35 @@ pub fn updateDecl(self: *Elf, module: *Module, decl_index: Module.Decl.Index) !v
25452576}
25462577
25472578pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module.Decl.Index) !u32 {
2548 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);
25492582 defer code_buffer.deinit();
25502583
25512584 const mod = self.base.options.module.?;
2552 const decl = mod.declPtr(decl_index);
2553
2554 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);
25552586 if (!gop.found_existing) {
25562587 gop.value_ptr.* = .{};
25572588 }
25582589 const unnamed_consts = gop.value_ptr;
25592590
2560 const atom = try self.base.allocator.create(TextBlock);
2561 errdefer self.base.allocator.destroy(atom);
2562 atom.* = TextBlock.empty;
2563 // TODO for unnamed consts we don't need GOT offset/entry allocated
2564 try atom.ensureInitialized(self);
2565 try self.managed_atoms.append(self.base.allocator, atom);
2566
2591 const decl = mod.declPtr(decl_index);
25672592 const name_str_index = blk: {
25682593 const decl_name = try decl.getFullyQualifiedName(mod);
2569 defer self.base.allocator.free(decl_name);
2570
2594 defer gpa.free(decl_name);
25712595 const index = unnamed_consts.items.len;
2572 const name = try std.fmt.allocPrint(self.base.allocator, "__unnamed_{s}_{d}", .{ decl_name, index });
2573 defer self.base.allocator.free(name);
2574
2575 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);
25762599 };
2577 const name = self.getString(name_str_index);
2600 const name = self.shstrtab.get(name_str_index).?;
2601
2602 const atom_index = try self.createAtom();
25782603
25792604 const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), typed_value, &code_buffer, .{
25802605 .none = {},
25812606 }, .{
2582 .parent_atom_index = atom.getSymbolIndex().?,
2607 .parent_atom_index = self.getAtom(atom_index).getSymbolIndex().?,
25832608 });
25842609 const code = switch (res) {
25852610 .ok => code_buffer.items,
......@@ -2592,31 +2617,27 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module
25922617 };
25932618
25942619 const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);
2595 const phdr_index = self.phdr_load_ro_index.?;
2596 const shdr_index = self.phdr_shdr_table.get(phdr_index).?;
2597 const vaddr = try self.allocateTextBlock(atom, code.len, required_alignment, phdr_index);
2598 errdefer self.freeTextBlock(atom, phdr_index);
2599
2600 log.debug("allocated text block for {s} at 0x{x}", .{ name, vaddr });
2601
2602 const local_sym = atom.getSymbolPtr(self);
2603 local_sym.* = .{
2604 .st_name = name_str_index,
2605 .st_info = (elf.STB_LOCAL << 4) | elf.STT_OBJECT,
2606 .st_other = 0,
2607 .st_shndx = shdr_index,
2608 .st_value = vaddr,
2609 .st_size = code.len,
2610 };
2611
2612 try self.writeSymbol(atom.getSymbolIndex().?);
2613 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);
26142635
26152636 const section_offset = local_sym.st_value - self.program_headers.items[phdr_index].p_vaddr;
2616 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;
26172638 try self.base.file.?.pwriteAll(code, file_offset);
26182639
2619 return atom.getSymbolIndex().?;
2640 return self.getAtom(atom_index).getSymbolIndex().?;
26202641}
26212642
26222643pub fn updateDeclExports(
......@@ -2635,20 +2656,16 @@ pub fn updateDeclExports(
26352656 const tracy = trace(@src());
26362657 defer tracy.end();
26372658
2638 const decl = module.declPtr(decl_index);
2639 const atom = &decl.link.elf;
2640
2641 if (atom.getSymbolIndex() == null) return;
2659 const gpa = self.base.allocator;
26422660
2661 const decl = module.declPtr(decl_index);
2662 const atom_index = try self.getOrCreateAtomForDecl(decl_index);
2663 const atom = self.getAtom(atom_index);
26432664 const decl_sym = atom.getSymbol(self);
2644 try self.global_symbols.ensureUnusedCapacity(self.base.allocator, exports.len);
2665 const decl_metadata = self.decls.getPtr(decl_index).?;
2666 const shdr_index = decl_metadata.shdr;
26452667
2646 const gop = try self.decls.getOrPut(self.base.allocator, decl_index);
2647 if (!gop.found_existing) {
2648 gop.value_ptr.* = try self.getDeclPhdrIndex(decl);
2649 }
2650 const phdr_index = gop.value_ptr.*.?;
2651 const shdr_index = self.phdr_shdr_table.get(phdr_index).?;
2668 try self.global_symbols.ensureUnusedCapacity(gpa, exports.len);
26522669
26532670 for (exports) |exp| {
26542671 if (exp.options.section) |section_name| {
......@@ -2681,10 +2698,10 @@ pub fn updateDeclExports(
26812698 },
26822699 };
26832700 const stt_bits: u8 = @truncate(u4, decl_sym.st_info);
2684 if (exp.link.elf.sym_index) |i| {
2701 if (decl_metadata.getExport(self, exp.options.name)) |i| {
26852702 const sym = &self.global_symbols.items[i];
26862703 sym.* = .{
2687 .st_name = try self.updateString(sym.st_name, exp.options.name),
2704 .st_name = try self.shstrtab.insert(gpa, exp.options.name),
26882705 .st_info = (stb_bits << 4) | stt_bits,
26892706 .st_other = 0,
26902707 .st_shndx = shdr_index,
......@@ -2692,30 +2709,29 @@ pub fn updateDeclExports(
26922709 .st_size = decl_sym.st_size,
26932710 };
26942711 } else {
2695 const name = try self.makeString(exp.options.name);
26962712 const i = if (self.global_symbol_free_list.popOrNull()) |i| i else blk: {
26972713 _ = self.global_symbols.addOneAssumeCapacity();
26982714 break :blk self.global_symbols.items.len - 1;
26992715 };
2716 try decl_metadata.exports.append(gpa, @intCast(u32, i));
27002717 self.global_symbols.items[i] = .{
2701 .st_name = name,
2718 .st_name = try self.shstrtab.insert(gpa, exp.options.name),
27022719 .st_info = (stb_bits << 4) | stt_bits,
27032720 .st_other = 0,
27042721 .st_shndx = shdr_index,
27052722 .st_value = decl_sym.st_value,
27062723 .st_size = decl_sym.st_size,
27072724 };
2708
2709 exp.link.elf.sym_index = @intCast(u32, i);
27102725 }
27112726 }
27122727}
27132728
27142729/// Must be called only after a successful call to `updateDecl`.
2715pub fn updateDeclLineNumber(self: *Elf, mod: *Module, decl: *const Module.Decl) !void {
2730pub fn updateDeclLineNumber(self: *Elf, mod: *Module, decl_index: Module.Decl.Index) !void {
27162731 const tracy = trace(@src());
27172732 defer tracy.end();
27182733
2734 const decl = mod.declPtr(decl_index);
27192735 const decl_name = try decl.getFullyQualifiedName(mod);
27202736 defer self.base.allocator.free(decl_name);
27212737
......@@ -2723,16 +2739,18 @@ pub fn updateDeclLineNumber(self: *Elf, mod: *Module, decl: *const Module.Decl)
27232739
27242740 if (self.llvm_object) |_| return;
27252741 if (self.dwarf) |*dw| {
2726 try dw.updateDeclLineNumber(decl);
2742 try dw.updateDeclLineNumber(mod, decl_index);
27272743 }
27282744}
27292745
2730pub fn deleteExport(self: *Elf, exp: Export) void {
2746pub fn deleteDeclExport(self: *Elf, decl_index: Module.Decl.Index, name: []const u8) void {
27312747 if (self.llvm_object) |_| return;
2732
2733 const sym_index = exp.sym_index orelse return;
2734 self.global_symbol_free_list.append(self.base.allocator, sym_index) catch {};
2735 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;
27362754}
27372755
27382756fn writeProgHeader(self: *Elf, index: usize) !void {
......@@ -2761,7 +2779,7 @@ fn writeSectHeader(self: *Elf, index: usize) !void {
27612779 switch (self.ptr_width) {
27622780 .p32 => {
27632781 var shdr: [1]elf.Elf32_Shdr = undefined;
2764 shdr[0] = sectHeaderTo32(self.sections.items[index]);
2782 shdr[0] = sectHeaderTo32(self.sections.items(.shdr)[index]);
27652783 if (foreign_endian) {
27662784 mem.byteSwapAllFields(elf.Elf32_Shdr, &shdr[0]);
27672785 }
......@@ -2769,7 +2787,7 @@ fn writeSectHeader(self: *Elf, index: usize) !void {
27692787 return self.base.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);
27702788 },
27712789 .p64 => {
2772 var shdr = [1]elf.Elf64_Shdr{self.sections.items[index]};
2790 var shdr = [1]elf.Elf64_Shdr{self.sections.items(.shdr)[index]};
27732791 if (foreign_endian) {
27742792 mem.byteSwapAllFields(elf.Elf64_Shdr, &shdr[0]);
27752793 }
......@@ -2783,11 +2801,11 @@ fn writeOffsetTableEntry(self: *Elf, index: usize) !void {
27832801 const entry_size: u16 = self.archPtrWidthBytes();
27842802 if (self.offset_table_count_dirty) {
27852803 const needed_size = self.offset_table.items.len * entry_size;
2786 try self.growAllocSection(self.got_section_index.?, self.phdr_got_index.?, needed_size);
2804 try self.growAllocSection(self.got_section_index.?, needed_size);
27872805 self.offset_table_count_dirty = false;
27882806 }
27892807 const endian = self.base.options.target.cpu.arch.endian();
2790 const shdr = &self.sections.items[self.got_section_index.?];
2808 const shdr = &self.sections.items(.shdr)[self.got_section_index.?];
27912809 const off = shdr.sh_offset + @as(u64, entry_size) * index;
27922810 switch (entry_size) {
27932811 2 => {
......@@ -2813,7 +2831,7 @@ fn writeSymbol(self: *Elf, index: usize) !void {
28132831 const tracy = trace(@src());
28142832 defer tracy.end();
28152833
2816 const syms_sect = &self.sections.items[self.symtab_section_index.?];
2834 const syms_sect = &self.sections.items(.shdr)[self.symtab_section_index.?];
28172835 // Make sure we are not pointlessly writing symbol data that will have to get relocated
28182836 // due to running out of space.
28192837 if (self.local_symbols.items.len != syms_sect.sh_info) {
......@@ -2835,7 +2853,7 @@ fn writeSymbol(self: *Elf, index: usize) !void {
28352853 .p64 => syms_sect.sh_offset + @sizeOf(elf.Elf64_Sym) * index,
28362854 };
28372855 const local = self.local_symbols.items[index];
2838 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 });
28392857 log.debug(" ({})", .{local});
28402858 switch (self.ptr_width) {
28412859 .p32 => {
......@@ -2865,7 +2883,7 @@ fn writeSymbol(self: *Elf, index: usize) !void {
28652883}
28662884
28672885fn writeAllGlobalSymbols(self: *Elf) !void {
2868 const syms_sect = &self.sections.items[self.symtab_section_index.?];
2886 const syms_sect = &self.sections.items(.shdr)[self.symtab_section_index.?];
28692887 const sym_size: u64 = switch (self.ptr_width) {
28702888 .p32 => @sizeOf(elf.Elf32_Sym),
28712889 .p64 => @sizeOf(elf.Elf64_Sym),
......@@ -3215,10 +3233,58 @@ const CsuObjects = struct {
32153233fn logSymtab(self: Elf) void {
32163234 log.debug("locals:", .{});
32173235 for (self.local_symbols.items) |sym, id| {
3218 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 });
32193237 }
32203238 log.debug("globals:", .{});
32213239 for (self.global_symbols.items) |sym, id| {
3222 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 });
32233241 }
32243242}
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+33-29
......@@ -4,7 +4,6 @@ const std = @import("std");
44const assert = std.debug.assert;
55const elf = std.elf;
66
7const Dwarf = @import("../Dwarf.zig");
87const Elf = @import("../Elf.zig");
98
109/// Each decl always gets a local symbol with the fully qualified name.
......@@ -20,44 +19,33 @@ offset_table_index: u32,
2019
2120/// Points to the previous and next neighbors, based on the `text_offset`.
2221/// This can be used to find, for example, the capacity of this `TextBlock`.
23prev: ?*Atom,
24next: ?*Atom,
22prev_index: ?Index,
23next_index: ?Index,
2524
26dbg_info_atom: Dwarf.Atom,
25pub const Index = u32;
2726
28pub const empty = Atom{
29 .local_sym_index = 0,
30 .offset_table_index = undefined,
31 .prev = null,
32 .next = null,
33 .dbg_info_atom = undefined,
27pub const Reloc = struct {
28 target: u32,
29 offset: u64,
30 addend: u32,
31 prev_vaddr: u64,
3432};
3533
36pub fn ensureInitialized(self: *Atom, elf_file: *Elf) !void {
37 if (self.getSymbolIndex() != null) return; // Already initialized
38 self.local_sym_index = try elf_file.allocateLocalSymbol();
39 self.offset_table_index = try elf_file.allocateGotOffset();
40 try elf_file.atom_by_index_table.putNoClobber(elf_file.base.allocator, self.local_sym_index, self);
41}
42
4334pub fn getSymbolIndex(self: Atom) ?u32 {
4435 if (self.local_sym_index == 0) return null;
4536 return self.local_sym_index;
4637}
4738
48pub fn getSymbol(self: Atom, elf_file: *Elf) elf.Elf64_Sym {
49 const sym_index = self.getSymbolIndex().?;
50 return elf_file.local_symbols.items[sym_index];
39pub fn getSymbol(self: Atom, elf_file: *const Elf) elf.Elf64_Sym {
40 return elf_file.getSymbol(self.getSymbolIndex().?);
5141}
5242
5343pub fn getSymbolPtr(self: Atom, elf_file: *Elf) *elf.Elf64_Sym {
54 const sym_index = self.getSymbolIndex().?;
55 return &elf_file.local_symbols.items[sym_index];
44 return elf_file.getSymbolPtr(self.getSymbolIndex().?);
5645}
5746
58pub fn getName(self: Atom, elf_file: *Elf) []const u8 {
59 const sym = self.getSymbol();
60 return elf_file.getString(sym.st_name);
47pub fn getName(self: Atom, elf_file: *const Elf) []const u8 {
48 return elf_file.getSymbolName(self.getSymbolIndex().?);
6149}
6250
6351pub fn getOffsetTableAddress(self: Atom, elf_file: *Elf) u64 {
......@@ -72,9 +60,10 @@ pub fn getOffsetTableAddress(self: Atom, elf_file: *Elf) u64 {
7260/// Returns how much room there is to grow in virtual address space.
7361/// File offset relocation happens transparently, so it is not included in
7462/// this calculation.
75pub fn capacity(self: Atom, elf_file: *Elf) u64 {
63pub fn capacity(self: Atom, elf_file: *const Elf) u64 {
7664 const self_sym = self.getSymbol(elf_file);
77 if (self.next) |next| {
65 if (self.next_index) |next_index| {
66 const next = elf_file.getAtom(next_index);
7867 const next_sym = next.getSymbol(elf_file);
7968 return next_sym.st_value - self_sym.st_value;
8069 } else {
......@@ -83,9 +72,10 @@ pub fn capacity(self: Atom, elf_file: *Elf) u64 {
8372 }
8473}
8574
86pub fn freeListEligible(self: Atom, elf_file: *Elf) bool {
75pub fn freeListEligible(self: Atom, elf_file: *const Elf) bool {
8776 // No need to keep a free list node for the last block.
88 const next = self.next orelse return false;
77 const next_index = self.next_index orelse return false;
78 const next = elf_file.getAtom(next_index);
8979 const self_sym = self.getSymbol(elf_file);
9080 const next_sym = next.getSymbol(elf_file);
9181 const cap = next_sym.st_value - self_sym.st_value;
......@@ -94,3 +84,17 @@ pub fn freeListEligible(self: Atom, elf_file: *Elf) bool {
9484 const surplus = cap - ideal_cap;
9585 return surplus >= Elf.min_text_capacity;
9686}
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+327-297
......@@ -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,26 +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;
1081 const atom_index = @intCast(Atom.Index, self.atoms.items.len);
1082 const atom = try self.atoms.addOne(gpa);
1083 const sym_index = try self.allocateSymbol();
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,
1092 };
1093 log.debug("creating ATOM(%{d}) at index {d}", .{ sym_index, atom_index });
1094 return atom_index;
1095}
10581096
1059 const atom = try gpa.create(Atom);
1060 atom.* = Atom.empty;
1061 try atom.ensureInitialized(self);
1097pub fn createGotAtom(self: *MachO, target: SymbolWithLoc) !Atom.Index {
1098 const atom_index = try self.createAtom();
1099 const atom = self.getAtomPtr(atom_index);
10621100 atom.size = @sizeOf(u64);
10631101 atom.alignment = @alignOf(u64);
1064 errdefer gpa.destroy(atom);
1065
1066 try self.managed_atoms.append(gpa, atom);
10671102
10681103 const sym = atom.getSymbolPtr(self);
10691104 sym.n_type = macho.N_SECT;
10701105 sym.n_sect = self.got_section_index.? + 1;
1071 sym.n_value = try self.allocateAtom(atom, atom.size, @alignOf(u64));
1106 sym.n_value = try self.allocateAtom(atom_index, atom.size, @alignOf(u64));
10721107
10731108 log.debug("allocated GOT atom at 0x{x}", .{sym.n_value});
10741109
1075 try atom.addRelocation(self, .{
1110 try Atom.addRelocation(self, atom_index, .{
10761111 .type = switch (self.base.options.target.cpu.arch) {
10771112 .aarch64 => @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),
10781113 .x86_64 => @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED),
......@@ -1087,45 +1122,39 @@ pub fn createGotAtom(self: *MachO, target: SymbolWithLoc) !*Atom {
10871122
10881123 const target_sym = self.getSymbol(target);
10891124 if (target_sym.undf()) {
1090 try atom.addBinding(self, .{
1125 try Atom.addBinding(self, atom_index, .{
10911126 .target = self.getGlobal(self.getSymbolName(target)).?,
10921127 .offset = 0,
10931128 });
10941129 } else {
1095 try atom.addRebase(self, 0);
1130 try Atom.addRebase(self, atom_index, 0);
10961131 }
10971132
1098 return atom;
1133 return atom_index;
10991134}
11001135
11011136pub fn createDyldPrivateAtom(self: *MachO) !void {
11021137 if (self.dyld_stub_binder_index == null) return;
1103 if (self.dyld_private_atom != null) return;
1104
1105 const gpa = self.base.allocator;
1138 if (self.dyld_private_atom_index != null) return;
11061139
1107 const atom = try gpa.create(Atom);
1108 atom.* = Atom.empty;
1109 try atom.ensureInitialized(self);
1140 const atom_index = try self.createAtom();
1141 const atom = self.getAtomPtr(atom_index);
11101142 atom.size = @sizeOf(u64);
11111143 atom.alignment = @alignOf(u64);
1112 errdefer gpa.destroy(atom);
11131144
11141145 const sym = atom.getSymbolPtr(self);
11151146 sym.n_type = macho.N_SECT;
11161147 sym.n_sect = self.data_section_index.? + 1;
1117 self.dyld_private_atom = atom;
1148 self.dyld_private_atom_index = atom_index;
11181149
1119 try self.managed_atoms.append(gpa, atom);
1120
1121 sym.n_value = try self.allocateAtom(atom, atom.size, @alignOf(u64));
1150 sym.n_value = try self.allocateAtom(atom_index, atom.size, @alignOf(u64));
11221151 log.debug("allocated dyld_private atom at 0x{x}", .{sym.n_value});
1123 try self.writePtrWidthAtom(atom);
1152 try self.writePtrWidthAtom(atom_index);
11241153}
11251154
11261155pub fn createStubHelperPreambleAtom(self: *MachO) !void {
11271156 if (self.dyld_stub_binder_index == null) return;
1128 if (self.stub_helper_preamble_atom != null) return;
1157 if (self.stub_helper_preamble_atom_index != null) return;
11291158
11301159 const gpa = self.base.allocator;
11311160 const arch = self.base.options.target.cpu.arch;
......@@ -1134,22 +1163,23 @@ pub fn createStubHelperPreambleAtom(self: *MachO) !void {
11341163 .aarch64 => 6 * @sizeOf(u32),
11351164 else => unreachable,
11361165 };
1137 const atom = try gpa.create(Atom);
1138 atom.* = Atom.empty;
1139 try atom.ensureInitialized(self);
1166 const atom_index = try self.createAtom();
1167 const atom = self.getAtomPtr(atom_index);
11401168 atom.size = size;
11411169 atom.alignment = switch (arch) {
11421170 .x86_64 => 1,
11431171 .aarch64 => @alignOf(u32),
11441172 else => unreachable,
11451173 };
1146 errdefer gpa.destroy(atom);
11471174
11481175 const sym = atom.getSymbolPtr(self);
11491176 sym.n_type = macho.N_SECT;
11501177 sym.n_sect = self.stub_helper_section_index.? + 1;
11511178
1152 const dyld_private_sym_index = self.dyld_private_atom.?.getSymbolIndex().?;
1179 const dyld_private_sym_index = if (self.dyld_private_atom_index) |dyld_index|
1180 self.getAtom(dyld_index).getSymbolIndex().?
1181 else
1182 unreachable;
11531183
11541184 const code = try gpa.alloc(u8, size);
11551185 defer gpa.free(code);
......@@ -1168,7 +1198,7 @@ pub fn createStubHelperPreambleAtom(self: *MachO) !void {
11681198 code[9] = 0xff;
11691199 code[10] = 0x25;
11701200
1171 try atom.addRelocations(self, 2, .{ .{
1201 try Atom.addRelocations(self, atom_index, 2, .{ .{
11721202 .type = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_SIGNED),
11731203 .target = .{ .sym_index = dyld_private_sym_index, .file = null },
11741204 .offset = 3,
......@@ -1208,7 +1238,7 @@ pub fn createStubHelperPreambleAtom(self: *MachO) !void {
12081238 // br x16
12091239 mem.writeIntLittle(u32, code[20..][0..4], aarch64.Instruction.br(.x16).toU32());
12101240
1211 try atom.addRelocations(self, 4, .{ .{
1241 try Atom.addRelocations(self, atom_index, 4, .{ .{
12121242 .type = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGE21),
12131243 .target = .{ .sym_index = dyld_private_sym_index, .file = null },
12141244 .offset = 0,
......@@ -1241,16 +1271,14 @@ pub fn createStubHelperPreambleAtom(self: *MachO) !void {
12411271
12421272 else => unreachable,
12431273 }
1244 self.stub_helper_preamble_atom = atom;
1274 self.stub_helper_preamble_atom_index = atom_index;
12451275
1246 try self.managed_atoms.append(gpa, atom);
1247
1248 sym.n_value = try self.allocateAtom(atom, size, atom.alignment);
1276 sym.n_value = try self.allocateAtom(atom_index, size, atom.alignment);
12491277 log.debug("allocated stub preamble atom at 0x{x}", .{sym.n_value});
1250 try self.writeAtom(atom, code);
1278 try self.writeAtom(atom_index, code);
12511279}
12521280
1253pub fn createStubHelperAtom(self: *MachO) !*Atom {
1281pub fn createStubHelperAtom(self: *MachO) !Atom.Index {
12541282 const gpa = self.base.allocator;
12551283 const arch = self.base.options.target.cpu.arch;
12561284 const size: u4 = switch (arch) {
......@@ -1258,16 +1286,14 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
12581286 .aarch64 => 3 * @sizeOf(u32),
12591287 else => unreachable,
12601288 };
1261 const atom = try gpa.create(Atom);
1262 atom.* = Atom.empty;
1263 try atom.ensureInitialized(self);
1289 const atom_index = try self.createAtom();
1290 const atom = self.getAtomPtr(atom_index);
12641291 atom.size = size;
12651292 atom.alignment = switch (arch) {
12661293 .x86_64 => 1,
12671294 .aarch64 => @alignOf(u32),
12681295 else => unreachable,
12691296 };
1270 errdefer gpa.destroy(atom);
12711297
12721298 const sym = atom.getSymbolPtr(self);
12731299 sym.n_type = macho.N_SECT;
......@@ -1277,6 +1303,11 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
12771303 defer gpa.free(code);
12781304 mem.set(u8, code, 0);
12791305
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
12801311 switch (arch) {
12811312 .x86_64 => {
12821313 // pushq
......@@ -1285,9 +1316,9 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
12851316 // jmpq
12861317 code[5] = 0xe9;
12871318
1288 try atom.addRelocation(self, .{
1319 try Atom.addRelocation(self, atom_index, .{
12891320 .type = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_BRANCH),
1290 .target = .{ .sym_index = self.stub_helper_preamble_atom.?.getSymbolIndex().?, .file = null },
1321 .target = .{ .sym_index = stub_helper_preamble_atom_sym_index, .file = null },
12911322 .offset = 6,
12921323 .addend = 0,
12931324 .pcrel = true,
......@@ -1308,9 +1339,9 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
13081339 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.b(0).toU32());
13091340 // Next 4 bytes 8..12 are just a placeholder populated in `populateLazyBindOffsetsInStubHelper`.
13101341
1311 try atom.addRelocation(self, .{
1342 try Atom.addRelocation(self, atom_index, .{
13121343 .type = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_BRANCH26),
1313 .target = .{ .sym_index = self.stub_helper_preamble_atom.?.getSymbolIndex().?, .file = null },
1344 .target = .{ .sym_index = stub_helper_preamble_atom_sym_index, .file = null },
13141345 .offset = 4,
13151346 .addend = 0,
13161347 .pcrel = true,
......@@ -1320,29 +1351,24 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
13201351 else => unreachable,
13211352 }
13221353
1323 try self.managed_atoms.append(gpa, atom);
1324
1325 sym.n_value = try self.allocateAtom(atom, size, atom.alignment);
1354 sym.n_value = try self.allocateAtom(atom_index, size, atom.alignment);
13261355 log.debug("allocated stub helper atom at 0x{x}", .{sym.n_value});
1327 try self.writeAtom(atom, code);
1356 try self.writeAtom(atom_index, code);
13281357
1329 return atom;
1358 return atom_index;
13301359}
13311360
1332pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, target: SymbolWithLoc) !*Atom {
1333 const gpa = self.base.allocator;
1334 const atom = try gpa.create(Atom);
1335 atom.* = Atom.empty;
1336 try atom.ensureInitialized(self);
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);
13371364 atom.size = @sizeOf(u64);
13381365 atom.alignment = @alignOf(u64);
1339 errdefer gpa.destroy(atom);
13401366
13411367 const sym = atom.getSymbolPtr(self);
13421368 sym.n_type = macho.N_SECT;
13431369 sym.n_sect = self.la_symbol_ptr_section_index.? + 1;
13441370
1345 try atom.addRelocation(self, .{
1371 try Atom.addRelocation(self, atom_index, .{
13461372 .type = switch (self.base.options.target.cpu.arch) {
13471373 .aarch64 => @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),
13481374 .x86_64 => @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED),
......@@ -1354,22 +1380,20 @@ pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, target: SymbolWi
13541380 .pcrel = false,
13551381 .length = 3,
13561382 });
1357 try atom.addRebase(self, 0);
1358 try atom.addLazyBinding(self, .{
1383 try Atom.addRebase(self, atom_index, 0);
1384 try Atom.addLazyBinding(self, atom_index, .{
13591385 .target = self.getGlobal(self.getSymbolName(target)).?,
13601386 .offset = 0,
13611387 });
13621388
1363 try self.managed_atoms.append(gpa, atom);
1364
1365 sym.n_value = try self.allocateAtom(atom, atom.size, @alignOf(u64));
1389 sym.n_value = try self.allocateAtom(atom_index, atom.size, @alignOf(u64));
13661390 log.debug("allocated lazy pointer atom at 0x{x} ({s})", .{ sym.n_value, self.getSymbolName(target) });
1367 try self.writePtrWidthAtom(atom);
1391 try self.writePtrWidthAtom(atom_index);
13681392
1369 return atom;
1393 return atom_index;
13701394}
13711395
1372pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
1396pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !Atom.Index {
13731397 const gpa = self.base.allocator;
13741398 const arch = self.base.options.target.cpu.arch;
13751399 const size: u4 = switch (arch) {
......@@ -1377,9 +1401,8 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
13771401 .aarch64 => 3 * @sizeOf(u32),
13781402 else => unreachable, // unhandled architecture type
13791403 };
1380 const atom = try gpa.create(Atom);
1381 atom.* = Atom.empty;
1382 try atom.ensureInitialized(self);
1404 const atom_index = try self.createAtom();
1405 const atom = self.getAtomPtr(atom_index);
13831406 atom.size = size;
13841407 atom.alignment = switch (arch) {
13851408 .x86_64 => 1,
......@@ -1387,7 +1410,6 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
13871410 else => unreachable, // unhandled architecture type
13881411
13891412 };
1390 errdefer gpa.destroy(atom);
13911413
13921414 const sym = atom.getSymbolPtr(self);
13931415 sym.n_type = macho.N_SECT;
......@@ -1403,7 +1425,7 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
14031425 code[0] = 0xff;
14041426 code[1] = 0x25;
14051427
1406 try atom.addRelocation(self, .{
1428 try Atom.addRelocation(self, atom_index, .{
14071429 .type = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_BRANCH),
14081430 .target = .{ .sym_index = laptr_sym_index, .file = null },
14091431 .offset = 2,
......@@ -1424,7 +1446,7 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
14241446 // br x16
14251447 mem.writeIntLittle(u32, code[8..12], aarch64.Instruction.br(.x16).toU32());
14261448
1427 try atom.addRelocations(self, 2, .{
1449 try Atom.addRelocations(self, atom_index, 2, .{
14281450 .{
14291451 .type = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGE21),
14301452 .target = .{ .sym_index = laptr_sym_index, .file = null },
......@@ -1446,13 +1468,11 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
14461468 else => unreachable,
14471469 }
14481470
1449 try self.managed_atoms.append(gpa, atom);
1450
1451 sym.n_value = try self.allocateAtom(atom, size, atom.alignment);
1471 sym.n_value = try self.allocateAtom(atom_index, size, atom.alignment);
14521472 log.debug("allocated stub atom at 0x{x}", .{sym.n_value});
1453 try self.writeAtom(atom, code);
1473 try self.writeAtom(atom_index, code);
14541474
1455 return atom;
1475 return atom_index;
14561476}
14571477
14581478pub fn createMhExecuteHeaderSymbol(self: *MachO) !void {
......@@ -1586,9 +1606,12 @@ pub fn resolveSymbolsInDylibs(self: *MachO) !void {
15861606 if (self.stubs_table.contains(global)) break :blk;
15871607
15881608 const stub_index = try self.allocateStubEntry(global);
1589 const stub_helper_atom = try self.createStubHelperAtom();
1590 const laptr_atom = try self.createLazyPointerAtom(stub_helper_atom.getSymbolIndex().?, global);
1591 const stub_atom = try self.createStubAtom(laptr_atom.getSymbolIndex().?);
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);
15921615 self.stubs.items[stub_index].sym_index = stub_atom.getSymbolIndex().?;
15931616 self.markRelocsDirtyByTarget(global);
15941617 }
......@@ -1686,10 +1709,11 @@ pub fn resolveDyldStubBinder(self: *MachO) !void {
16861709
16871710 // Add dyld_stub_binder as the final GOT entry.
16881711 const got_index = try self.allocateGotEntry(global);
1689 const got_atom = try self.createGotAtom(global);
1712 const got_atom_index = try self.createGotAtom(global);
1713 const got_atom = self.getAtom(got_atom_index);
16901714 self.got_entries.items[got_index].sym_index = got_atom.getSymbolIndex().?;
16911715
1692 try self.writePtrWidthAtom(got_atom);
1716 try self.writePtrWidthAtom(got_atom_index);
16931717}
16941718
16951719pub fn deinit(self: *MachO) void {
......@@ -1739,12 +1763,12 @@ pub fn deinit(self: *MachO) void {
17391763 }
17401764 self.sections.deinit(gpa);
17411765
1742 for (self.managed_atoms.items) |atom| {
1743 gpa.destroy(atom);
1744 }
1745 self.managed_atoms.deinit(gpa);
1766 self.atoms.deinit(gpa);
17461767
17471768 if (self.base.options.module) |_| {
1769 for (self.decls.values()) |*m| {
1770 m.exports.deinit(gpa);
1771 }
17481772 self.decls.deinit(gpa);
17491773 } else {
17501774 assert(self.decls.count() == 0);
......@@ -1778,14 +1802,14 @@ pub fn deinit(self: *MachO) void {
17781802 self.lazy_bindings.deinit(gpa);
17791803}
17801804
1781fn freeAtom(self: *MachO, atom: *Atom) void {
1782 log.debug("freeAtom {*}", .{atom});
1783
1805fn freeAtom(self: *MachO, atom_index: Atom.Index) void {
17841806 const gpa = self.base.allocator;
1807 log.debug("freeAtom {d}", .{atom_index});
17851808
17861809 // Remove any relocs and base relocs associated with this Atom
1787 self.freeRelocationsForAtom(atom);
1810 Atom.freeRelocations(self, atom_index);
17881811
1812 const atom = self.getAtom(atom_index);
17891813 const sect_id = atom.getSymbol(self).n_sect - 1;
17901814 const free_list = &self.sections.items(.free_list)[sect_id];
17911815 var already_have_free_list_node = false;
......@@ -1793,45 +1817,46 @@ fn freeAtom(self: *MachO, atom: *Atom) void {
17931817 var i: usize = 0;
17941818 // TODO turn free_list into a hash map
17951819 while (i < free_list.items.len) {
1796 if (free_list.items[i] == atom) {
1820 if (free_list.items[i] == atom_index) {
17971821 _ = free_list.swapRemove(i);
17981822 continue;
17991823 }
1800 if (free_list.items[i] == atom.prev) {
1824 if (free_list.items[i] == atom.prev_index) {
18011825 already_have_free_list_node = true;
18021826 }
18031827 i += 1;
18041828 }
18051829 }
18061830
1807 const maybe_last_atom = &self.sections.items(.last_atom)[sect_id];
1808 if (maybe_last_atom.*) |last_atom| {
1809 if (last_atom == atom) {
1810 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| {
18111835 // TODO shrink the section size here
1812 maybe_last_atom.* = prev;
1836 maybe_last_atom_index.* = prev_index;
18131837 } else {
1814 maybe_last_atom.* = null;
1838 maybe_last_atom_index.* = null;
18151839 }
18161840 }
18171841 }
18181842
1819 if (atom.prev) |prev| {
1820 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;
18211846
1822 if (!already_have_free_list_node and prev.freeListEligible(self)) {
1847 if (!already_have_free_list_node and prev.*.freeListEligible(self)) {
18231848 // The free list is heuristics, it doesn't have to be perfect, so we can ignore
18241849 // the OOM here.
1825 free_list.append(gpa, prev) catch {};
1850 free_list.append(gpa, prev_index) catch {};
18261851 }
18271852 } else {
1828 atom.prev = null;
1853 self.getAtomPtr(atom_index).prev_index = null;
18291854 }
18301855
1831 if (atom.next) |next| {
1832 next.prev = atom.prev;
1856 if (atom.next_index) |next_index| {
1857 self.getAtomPtr(next_index).prev_index = atom.prev_index;
18331858 } else {
1834 atom.next = null;
1859 self.getAtomPtr(atom_index).next_index = null;
18351860 }
18361861
18371862 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
......@@ -1859,27 +1884,24 @@ fn freeAtom(self: *MachO, atom: *Atom) void {
18591884 self.locals.items[sym_index].n_type = 0;
18601885 _ = self.atom_by_index_table.remove(sym_index);
18611886 log.debug(" adding local symbol index {d} to free list", .{sym_index});
1862 atom.sym_index = 0;
1863
1864 if (self.d_sym) |*d_sym| {
1865 d_sym.dwarf.freeAtom(&atom.dbg_info_atom);
1866 }
1887 self.getAtomPtr(atom_index).sym_index = 0;
18671888}
18681889
1869fn shrinkAtom(self: *MachO, atom: *Atom, new_block_size: u64) void {
1890fn shrinkAtom(self: *MachO, atom_index: Atom.Index, new_block_size: u64) void {
18701891 _ = self;
1871 _ = atom;
1892 _ = atom_index;
18721893 _ = new_block_size;
18731894 // TODO check the new capacity, and if it crosses the size threshold into a big enough
18741895 // capacity, insert a free list node for it.
18751896}
18761897
1877fn 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);
18781900 const sym = atom.getSymbol(self);
18791901 const align_ok = mem.alignBackwardGeneric(u64, sym.n_value, alignment) == sym.n_value;
18801902 const need_realloc = !align_ok or new_atom_size > atom.capacity(self);
18811903 if (!need_realloc) return sym.n_value;
1882 return self.allocateAtom(atom, new_atom_size, alignment);
1904 return self.allocateAtom(atom_index, new_atom_size, alignment);
18831905}
18841906
18851907pub fn allocateSymbol(self: *MachO) !u32 {
......@@ -1986,15 +2008,12 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv
19862008
19872009 const decl_index = func.owner_decl;
19882010 const decl = module.declPtr(decl_index);
1989 const atom = &decl.link.macho;
1990 try atom.ensureInitialized(self);
1991 const gop = try self.decls.getOrPut(self.base.allocator, decl_index);
1992 if (gop.found_existing) {
1993 self.freeUnnamedConsts(decl_index);
1994 self.freeRelocationsForAtom(atom);
1995 } else {
1996 gop.value_ptr.* = null;
1997 }
2011
2012 const atom_index = try self.getOrCreateAtomForDecl(decl_index);
2013 self.freeUnnamedConsts(decl_index);
2014 Atom.freeRelocations(self, atom_index);
2015
2016 const atom = self.getAtom(atom_index);
19982017
19992018 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
20002019 defer code_buffer.deinit();
......@@ -2024,13 +2043,7 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv
20242043 const addr = try self.updateDeclCode(decl_index, code);
20252044
20262045 if (decl_state) |*ds| {
2027 try self.d_sym.?.dwarf.commitDeclState(
2028 module,
2029 decl_index,
2030 addr,
2031 decl.link.macho.size,
2032 ds,
2033 );
2046 try self.d_sym.?.dwarf.commitDeclState(module, decl_index, addr, atom.size, ds);
20342047 }
20352048
20362049 // Since we updated the vaddr and the size, each corresponding export symbol also
......@@ -2065,14 +2078,10 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu
20652078
20662079 log.debug("allocating symbol indexes for {?s}", .{name});
20672080
2068 const atom = try gpa.create(Atom);
2069 errdefer gpa.destroy(atom);
2070 atom.* = Atom.empty;
2071 try atom.ensureInitialized(self);
2072 try self.managed_atoms.append(gpa, atom);
2081 const atom_index = try self.createAtom();
20732082
20742083 const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), typed_value, &code_buffer, .none, .{
2075 .parent_atom_index = atom.getSymbolIndex().?,
2084 .parent_atom_index = self.getAtom(atom_index).getSymbolIndex().?,
20762085 });
20772086 const code = switch (res) {
20782087 .ok => code_buffer.items,
......@@ -2085,24 +2094,25 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu
20852094 };
20862095
20872096 const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);
2097 const atom = self.getAtomPtr(atom_index);
20882098 atom.size = code.len;
20892099 atom.alignment = required_alignment;
20902100 // TODO: work out logic for disambiguating functions from function pointers
2091 // const sect_id = self.getDeclOutputSection(decl);
2101 // const sect_id = self.getDeclOutputSection(decl_index);
20922102 const sect_id = self.data_const_section_index.?;
20932103 const symbol = atom.getSymbolPtr(self);
20942104 symbol.n_strx = name_str_index;
20952105 symbol.n_type = macho.N_SECT;
20962106 symbol.n_sect = sect_id + 1;
2097 symbol.n_value = try self.allocateAtom(atom, code.len, required_alignment);
2098 errdefer self.freeAtom(atom);
2107 symbol.n_value = try self.allocateAtom(atom_index, code.len, required_alignment);
2108 errdefer self.freeAtom(atom_index);
20992109
2100 try unnamed_consts.append(gpa, atom);
2110 try unnamed_consts.append(gpa, atom_index);
21012111
21022112 log.debug("allocated atom for {?s} at 0x{x}", .{ name, symbol.n_value });
21032113 log.debug(" (required alignment 0x{x})", .{required_alignment});
21042114
2105 try self.writeAtom(atom, code);
2115 try self.writeAtom(atom_index, code);
21062116
21072117 return atom.getSymbolIndex().?;
21082118}
......@@ -2129,14 +2139,9 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index)
21292139 }
21302140 }
21312141
2132 const atom = &decl.link.macho;
2133 try atom.ensureInitialized(self);
2134 const gop = try self.decls.getOrPut(self.base.allocator, decl_index);
2135 if (gop.found_existing) {
2136 self.freeRelocationsForAtom(atom);
2137 } else {
2138 gop.value_ptr.* = null;
2139 }
2142 const atom_index = try self.getOrCreateAtomForDecl(decl_index);
2143 Atom.freeRelocations(self, atom_index);
2144 const atom = self.getAtom(atom_index);
21402145
21412146 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
21422147 defer code_buffer.deinit();
......@@ -2155,14 +2160,14 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index)
21552160 }, &code_buffer, .{
21562161 .dwarf = ds,
21572162 }, .{
2158 .parent_atom_index = decl.link.macho.getSymbolIndex().?,
2163 .parent_atom_index = atom.getSymbolIndex().?,
21592164 })
21602165 else
21612166 try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
21622167 .ty = decl.ty,
21632168 .val = decl_val,
21642169 }, &code_buffer, .none, .{
2165 .parent_atom_index = decl.link.macho.getSymbolIndex().?,
2170 .parent_atom_index = atom.getSymbolIndex().?,
21662171 });
21672172
21682173 const code = switch (res) {
......@@ -2176,13 +2181,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index)
21762181 const addr = try self.updateDeclCode(decl_index, code);
21772182
21782183 if (decl_state) |*ds| {
2179 try self.d_sym.?.dwarf.commitDeclState(
2180 module,
2181 decl_index,
2182 addr,
2183 decl.link.macho.size,
2184 ds,
2185 );
2184 try self.d_sym.?.dwarf.commitDeclState(module, decl_index, addr, atom.size, ds);
21862185 }
21872186
21882187 // Since we updated the vaddr and the size, each corresponding export symbol also
......@@ -2190,7 +2189,20 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index)
21902189 try self.updateDeclExports(module, decl_index, module.getDeclExports(decl_index));
21912190}
21922191
2193fn 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);
21942206 const ty = decl.ty;
21952207 const val = decl.val;
21962208 const zig_ty = ty.zigTypeTag();
......@@ -2341,13 +2353,11 @@ fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []const u8)
23412353 const sym_name = try decl.getFullyQualifiedName(mod);
23422354 defer self.base.allocator.free(sym_name);
23432355
2344 const atom = &decl.link.macho;
2345 const sym_index = atom.getSymbolIndex().?; // Atom was not initialized
2346 const decl_ptr = self.decls.getPtr(decl_index).?;
2347 if (decl_ptr.* == null) {
2348 decl_ptr.* = self.getDeclOutputSection(decl);
2349 }
2350 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;
23512361 const code_len = code.len;
23522362
23532363 if (atom.size != 0) {
......@@ -2357,11 +2367,11 @@ fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []const u8)
23572367 sym.n_sect = sect_id + 1;
23582368 sym.n_desc = 0;
23592369
2360 const capacity = decl.link.macho.capacity(self);
2370 const capacity = atom.capacity(self);
23612371 const need_realloc = code_len > capacity or !mem.isAlignedGeneric(u64, sym.n_value, required_alignment);
23622372
23632373 if (need_realloc) {
2364 const vaddr = try self.growAtom(atom, code_len, required_alignment);
2374 const vaddr = try self.growAtom(atom_index, code_len, required_alignment);
23652375 log.debug("growing {s} and moving from 0x{x} to 0x{x}", .{ sym_name, sym.n_value, vaddr });
23662376 log.debug(" (required alignment 0x{x})", .{required_alignment});
23672377
......@@ -2369,19 +2379,19 @@ fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []const u8)
23692379 sym.n_value = vaddr;
23702380 log.debug(" (updating GOT entry)", .{});
23712381 const got_target = SymbolWithLoc{ .sym_index = sym_index, .file = null };
2372 const got_atom = self.getGotAtomForSymbol(got_target).?;
2382 const got_atom_index = self.getGotAtomIndexForSymbol(got_target).?;
23732383 self.markRelocsDirtyByTarget(got_target);
2374 try self.writePtrWidthAtom(got_atom);
2384 try self.writePtrWidthAtom(got_atom_index);
23752385 }
23762386 } else if (code_len < atom.size) {
2377 self.shrinkAtom(atom, code_len);
2378 } else if (atom.next == null) {
2387 self.shrinkAtom(atom_index, code_len);
2388 } else if (atom.next_index == null) {
23792389 const header = &self.sections.items(.header)[sect_id];
23802390 const segment = self.getSegment(sect_id);
23812391 const needed_size = (sym.n_value + code_len) - segment.vmaddr;
23822392 header.size = needed_size;
23832393 }
2384 atom.size = code_len;
2394 self.getAtomPtr(atom_index).size = code_len;
23852395 } else {
23862396 const name_str_index = try self.strtab.insert(gpa, sym_name);
23872397 const sym = atom.getSymbolPtr(self);
......@@ -2390,32 +2400,32 @@ fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []const u8)
23902400 sym.n_sect = sect_id + 1;
23912401 sym.n_desc = 0;
23922402
2393 const vaddr = try self.allocateAtom(atom, code_len, required_alignment);
2394 errdefer self.freeAtom(atom);
2403 const vaddr = try self.allocateAtom(atom_index, code_len, required_alignment);
2404 errdefer self.freeAtom(atom_index);
23952405
23962406 log.debug("allocated atom for {s} at 0x{x}", .{ sym_name, vaddr });
23972407 log.debug(" (required alignment 0x{x})", .{required_alignment});
23982408
2399 atom.size = code_len;
2409 self.getAtomPtr(atom_index).size = code_len;
24002410 sym.n_value = vaddr;
24012411
24022412 const got_target = SymbolWithLoc{ .sym_index = sym_index, .file = null };
24032413 const got_index = try self.allocateGotEntry(got_target);
2404 const got_atom = try self.createGotAtom(got_target);
2414 const got_atom_index = try self.createGotAtom(got_target);
2415 const got_atom = self.getAtom(got_atom_index);
24052416 self.got_entries.items[got_index].sym_index = got_atom.getSymbolIndex().?;
2406 try self.writePtrWidthAtom(got_atom);
2417 try self.writePtrWidthAtom(got_atom_index);
24072418 }
24082419
24092420 self.markRelocsDirtyByTarget(atom.getSymbolWithLoc());
2410 try self.writeAtom(atom, code);
2421 try self.writeAtom(atom_index, code);
24112422
24122423 return atom.getSymbol(self).n_value;
24132424}
24142425
2415pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl: *const Module.Decl) !void {
2416 _ = module;
2426pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl_index: Module.Decl.Index) !void {
24172427 if (self.d_sym) |*d_sym| {
2418 try d_sym.dwarf.updateDeclLineNumber(decl);
2428 try d_sym.dwarf.updateDeclLineNumber(module, decl_index);
24192429 }
24202430}
24212431
......@@ -2432,22 +2442,17 @@ pub fn updateDeclExports(
24322442 if (self.llvm_object) |llvm_object|
24332443 return llvm_object.updateDeclExports(module, decl_index, exports);
24342444 }
2445
24352446 const tracy = trace(@src());
24362447 defer tracy.end();
24372448
24382449 const gpa = self.base.allocator;
24392450
24402451 const decl = module.declPtr(decl_index);
2441 const atom = &decl.link.macho;
2442
2443 if (atom.getSymbolIndex() == null) return;
2444
2445 const gop = try self.decls.getOrPut(gpa, decl_index);
2446 if (!gop.found_existing) {
2447 gop.value_ptr.* = self.getDeclOutputSection(decl);
2448 }
2449
2452 const atom_index = try self.getOrCreateAtomForDecl(decl_index);
2453 const atom = self.getAtom(atom_index);
24502454 const decl_sym = atom.getSymbol(self);
2455 const decl_metadata = self.decls.getPtr(decl_index).?;
24512456
24522457 for (exports) |exp| {
24532458 const exp_name = try std.fmt.allocPrint(gpa, "_{s}", .{exp.options.name});
......@@ -2485,9 +2490,9 @@ pub fn updateDeclExports(
24852490 continue;
24862491 }
24872492
2488 const sym_index = exp.link.macho.sym_index orelse blk: {
2493 const sym_index = decl_metadata.getExport(self, exp_name) orelse blk: {
24892494 const sym_index = try self.allocateSymbol();
2490 exp.link.macho.sym_index = sym_index;
2495 try decl_metadata.exports.append(gpa, sym_index);
24912496 break :blk sym_index;
24922497 };
24932498 const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = null };
......@@ -2535,16 +2540,18 @@ pub fn updateDeclExports(
25352540 }
25362541}
25372542
2538pub fn deleteExport(self: *MachO, exp: Export) void {
2543pub fn deleteDeclExport(self: *MachO, decl_index: Module.Decl.Index, name: []const u8) Allocator.Error!void {
25392544 if (self.llvm_object) |_| return;
2540 const sym_index = exp.sym_index orelse return;
2545 const metadata = self.decls.getPtr(decl_index) orelse return;
25412546
25422547 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;
25432551
2544 const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = null };
2552 const sym_loc = SymbolWithLoc{ .sym_index = sym_index.*, .file = null };
25452553 const sym = self.getSymbolPtr(sym_loc);
2546 const sym_name = self.getSymbolName(sym_loc);
2547 log.debug("deleting export '{s}'", .{sym_name});
2554 log.debug("deleting export '{s}'", .{exp_name});
25482555 assert(sym.sect() and sym.ext());
25492556 sym.* = .{
25502557 .n_strx = 0,
......@@ -2553,9 +2560,9 @@ pub fn deleteExport(self: *MachO, exp: Export) void {
25532560 .n_desc = 0,
25542561 .n_value = 0,
25552562 };
2556 self.locals_free_list.append(gpa, sym_index) catch {};
2563 self.locals_free_list.append(gpa, sym_index.*) catch {};
25572564
2558 if (self.resolver.fetchRemove(sym_name)) |entry| {
2565 if (self.resolver.fetchRemove(exp_name)) |entry| {
25592566 defer gpa.free(entry.key);
25602567 self.globals_free_list.append(gpa, entry.value) catch {};
25612568 self.globals.items[entry.value] = .{
......@@ -2563,17 +2570,8 @@ pub fn deleteExport(self: *MachO, exp: Export) void {
25632570 .file = null,
25642571 };
25652572 }
2566}
25672573
2568fn freeRelocationsForAtom(self: *MachO, atom: *Atom) void {
2569 var removed_relocs = self.relocs.fetchOrderedRemove(atom);
2570 if (removed_relocs) |*relocs| relocs.value.deinit(self.base.allocator);
2571 var removed_rebases = self.rebases.fetchOrderedRemove(atom);
2572 if (removed_rebases) |*rebases| rebases.value.deinit(self.base.allocator);
2573 var removed_bindings = self.bindings.fetchOrderedRemove(atom);
2574 if (removed_bindings) |*bindings| bindings.value.deinit(self.base.allocator);
2575 var removed_lazy_bindings = self.lazy_bindings.fetchOrderedRemove(atom);
2576 if (removed_lazy_bindings) |*lazy_bindings| lazy_bindings.value.deinit(self.base.allocator);
2574 sym_index.* = 0;
25772575}
25782576
25792577fn freeUnnamedConsts(self: *MachO, decl_index: Module.Decl.Index) void {
......@@ -2594,29 +2592,25 @@ pub fn freeDecl(self: *MachO, decl_index: Module.Decl.Index) void {
25942592
25952593 log.debug("freeDecl {*}", .{decl});
25962594
2597 if (self.decls.fetchSwapRemove(decl_index)) |kv| {
2598 if (kv.value) |_| {
2599 self.freeAtom(&decl.link.macho);
2600 self.freeUnnamedConsts(decl_index);
2601 }
2595 if (self.decls.fetchSwapRemove(decl_index)) |const_kv| {
2596 var kv = const_kv;
2597 self.freeAtom(kv.value.atom);
2598 self.freeUnnamedConsts(decl_index);
2599 kv.value.exports.deinit(self.base.allocator);
26022600 }
26032601
26042602 if (self.d_sym) |*d_sym| {
2605 d_sym.dwarf.freeDecl(decl);
2603 d_sym.dwarf.freeDecl(decl_index);
26062604 }
26072605}
26082606
26092607pub fn getDeclVAddr(self: *MachO, decl_index: Module.Decl.Index, reloc_info: File.RelocInfo) !u64 {
2610 const mod = self.base.options.module.?;
2611 const decl = mod.declPtr(decl_index);
2612
26132608 assert(self.llvm_object == null);
26142609
2615 try decl.link.macho.ensureInitialized(self);
2616 const sym_index = decl.link.macho.getSymbolIndex().?;
2617
2618 const atom = self.getAtomForSymbol(.{ .sym_index = reloc_info.parent_atom_index, .file = null }).?;
2619 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, .{
26202614 .type = switch (self.base.options.target.cpu.arch) {
26212615 .aarch64 => @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),
26222616 .x86_64 => @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED),
......@@ -2628,7 +2622,7 @@ pub fn getDeclVAddr(self: *MachO, decl_index: Module.Decl.Index, reloc_info: Fil
26282622 .pcrel = false,
26292623 .length = 3,
26302624 });
2631 try atom.addRebase(self, @intCast(u32, reloc_info.offset));
2625 try Atom.addRebase(self, atom_index, @intCast(u32, reloc_info.offset));
26322626
26332627 return 0;
26342628}
......@@ -2860,34 +2854,36 @@ fn moveSectionInVirtualMemory(self: *MachO, sect_id: u8, needed_size: u64) !void
28602854 // TODO: enforce order by increasing VM addresses in self.sections container.
28612855 for (self.sections.items(.header)[sect_id + 1 ..]) |*next_header, next_sect_id| {
28622856 const index = @intCast(u8, sect_id + 1 + next_sect_id);
2863 const maybe_last_atom = &self.sections.items(.last_atom)[index];
28642857 const next_segment = self.getSegmentPtr(index);
28652858 next_header.addr += diff;
28662859 next_segment.vmaddr += diff;
28672860
2868 if (maybe_last_atom.*) |last_atom| {
2869 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;
28702864 while (true) {
2865 const atom = self.getAtom(atom_index);
28712866 const sym = atom.getSymbolPtr(self);
28722867 sym.n_value += diff;
28732868
2874 if (atom.prev) |prev| {
2875 atom = prev;
2869 if (atom.prev_index) |prev_index| {
2870 atom_index = prev_index;
28762871 } else break;
28772872 }
28782873 }
28792874 }
28802875}
28812876
2882fn 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 {
28832878 const tracy = trace(@src());
28842879 defer tracy.end();
28852880
2881 const atom = self.getAtom(atom_index);
28862882 const sect_id = atom.getSymbol(self).n_sect - 1;
28872883 const segment = self.getSegmentPtr(sect_id);
28882884 const header = &self.sections.items(.header)[sect_id];
28892885 const free_list = &self.sections.items(.free_list)[sect_id];
2890 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];
28912887 const requires_padding = blk: {
28922888 if (!header.isCode()) break :blk false;
28932889 if (header.isSymbolStubs()) break :blk false;
......@@ -2901,7 +2897,7 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) !
29012897 // It would be simpler to do it inside the for loop below, but that would cause a
29022898 // problem if an error was returned later in the function. So this action
29032899 // is actually carried out at the end of the function, when errors are no longer possible.
2904 var atom_placement: ?*Atom = null;
2900 var atom_placement: ?Atom.Index = null;
29052901 var free_list_removal: ?usize = null;
29062902
29072903 // First we look for an appropriately sized free list node.
......@@ -2909,7 +2905,8 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) !
29092905 var vaddr = blk: {
29102906 var i: usize = 0;
29112907 while (i < free_list.items.len) {
2912 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);
29132910 // We now have a pointer to a live atom that has too much capacity.
29142911 // Is it enough that we could fit this new atom?
29152912 const sym = big_atom.getSymbol(self);
......@@ -2937,30 +2934,35 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) !
29372934 const keep_free_list_node = remaining_capacity >= min_text_capacity;
29382935
29392936 // Set up the metadata to be updated, after errors are no longer possible.
2940 atom_placement = big_atom;
2937 atom_placement = big_atom_index;
29412938 if (!keep_free_list_node) {
29422939 free_list_removal = i;
29432940 }
29442941 break :blk new_start_vaddr;
2945 } else if (maybe_last_atom.*) |last| {
2942 } else if (maybe_last_atom_index.*) |last_index| {
2943 const last = self.getAtom(last_index);
29462944 const last_symbol = last.getSymbol(self);
29472945 const ideal_capacity = if (requires_padding) padToIdeal(last.size) else last.size;
29482946 const ideal_capacity_end_vaddr = last_symbol.n_value + ideal_capacity;
29492947 const new_start_vaddr = mem.alignForwardGeneric(u64, ideal_capacity_end_vaddr, alignment);
2950 atom_placement = last;
2948 atom_placement = last_index;
29512949 break :blk new_start_vaddr;
29522950 } else {
29532951 break :blk mem.alignForwardGeneric(u64, segment.vmaddr, alignment);
29542952 }
29552953 };
29562954
2957 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;
29582959 if (expand_section) {
29592960 const sect_capacity = self.allocatedSize(header.offset);
29602961 const needed_size = (vaddr + new_atom_size) - segment.vmaddr;
29612962 if (needed_size > sect_capacity) {
29622963 const new_offset = self.findFreeSpace(needed_size, self.page_size);
2963 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);
29642966 const sym = last_atom.getSymbol(self);
29652967 break :blk (sym.n_value + last_atom.size) - segment.vmaddr;
29662968 } else 0;
......@@ -2992,7 +2994,7 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) !
29922994 header.size = needed_size;
29932995 segment.filesize = mem.alignForwardGeneric(u64, needed_size, self.page_size);
29942996 segment.vmsize = mem.alignForwardGeneric(u64, needed_size, self.page_size);
2995 maybe_last_atom.* = atom;
2997 maybe_last_atom_index.* = atom_index;
29962998
29972999 self.segment_table_dirty = true;
29983000 }
......@@ -3001,21 +3003,31 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) !
30013003 if (header.@"align" < align_pow) {
30023004 header.@"align" = align_pow;
30033005 }
3006 {
3007 const atom_ptr = self.getAtomPtr(atom_index);
3008 atom_ptr.size = new_atom_size;
3009 atom_ptr.alignment = @intCast(u32, alignment);
3010 }
30043011
3005 if (atom.prev) |prev| {
3006 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;
30073015 }
3008 if (atom.next) |next| {
3009 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;
30103019 }
30113020
3012 if (atom_placement) |big_atom| {
3013 atom.prev = big_atom;
3014 atom.next = big_atom.next;
3015 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;
30163027 } else {
3017 atom.prev = null;
3018 atom.next = null;
3028 const atom_ptr = self.getAtomPtr(atom_index);
3029 atom_ptr.prev_index = null;
3030 atom_ptr.next_index = null;
30193031 }
30203032 if (free_list_removal) |i| {
30213033 _ = free_list.swapRemove(i);
......@@ -3155,7 +3167,8 @@ fn collectRebaseData(self: *MachO, rebase: *Rebase) !void {
31553167 const gpa = self.base.allocator;
31563168 const slice = self.sections.slice();
31573169
3158 for (self.rebases.keys()) |atom, i| {
3170 for (self.rebases.keys()) |atom_index, i| {
3171 const atom = self.getAtom(atom_index);
31593172 log.debug(" ATOM(%{?d}, '{s}')", .{ atom.getSymbolIndex(), atom.getName(self) });
31603173
31613174 const sym = atom.getSymbol(self);
......@@ -3184,7 +3197,8 @@ fn collectBindData(self: *MachO, bind: anytype, raw_bindings: anytype) !void {
31843197 const gpa = self.base.allocator;
31853198 const slice = self.sections.slice();
31863199
3187 for (raw_bindings.keys()) |atom, i| {
3200 for (raw_bindings.keys()) |atom_index, i| {
3201 const atom = self.getAtom(atom_index);
31883202 log.debug(" ATOM(%{?d}, '{s}')", .{ atom.getSymbolIndex(), atom.getName(self) });
31893203
31903204 const sym = atom.getSymbol(self);
......@@ -3359,7 +3373,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, lazy_bind: LazyBind) !void
33593373 if (lazy_bind.size() == 0) return;
33603374
33613375 const stub_helper_section_index = self.stub_helper_section_index.?;
3362 assert(self.stub_helper_preamble_atom != null);
3376 assert(self.stub_helper_preamble_atom_index != null);
33633377
33643378 const section = self.sections.get(stub_helper_section_index);
33653379
......@@ -3369,10 +3383,11 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, lazy_bind: LazyBind) !void
33693383 else => unreachable,
33703384 };
33713385 const header = section.header;
3372 var atom = section.last_atom.?;
3386 var atom_index = section.last_atom_index.?;
33733387
33743388 var index: usize = lazy_bind.offsets.items.len;
33753389 while (index > 0) : (index -= 1) {
3390 const atom = self.getAtom(atom_index);
33763391 const sym = atom.getSymbol(self);
33773392 const file_offset = header.offset + sym.n_value - header.addr + stub_offset;
33783393 const bind_offset = lazy_bind.offsets.items[index - 1];
......@@ -3385,7 +3400,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, lazy_bind: LazyBind) !void
33853400
33863401 try self.base.file.?.pwriteAll(mem.asBytes(&bind_offset), file_offset);
33873402
3388 atom = atom.prev.?;
3403 atom_index = atom.prev_index.?;
33893404 }
33903405}
33913406
......@@ -3828,25 +3843,35 @@ pub fn getOrPutGlobalPtr(self: *MachO, name: []const u8) !GetOrPutGlobalPtrResul
38283843 return GetOrPutGlobalPtrResult{ .found_existing = false, .value_ptr = ptr };
38293844}
38303845
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
38313856/// Returns atom if there is an atom referenced by the symbol described by `sym_with_loc` descriptor.
38323857/// Returns null on failure.
3833pub fn getAtomForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?*Atom {
3858pub fn getAtomIndexForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?Atom.Index {
38343859 assert(sym_with_loc.file == null);
38353860 return self.atom_by_index_table.get(sym_with_loc.sym_index);
38363861}
38373862
38383863/// Returns GOT atom that references `sym_with_loc` if one exists.
38393864/// Returns null otherwise.
3840pub fn getGotAtomForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?*Atom {
3865pub fn getGotAtomIndexForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?Atom.Index {
38413866 const got_index = self.got_entries_table.get(sym_with_loc) orelse return null;
3842 return self.got_entries.items[got_index].getAtom(self);
3867 return self.got_entries.items[got_index].getAtomIndex(self);
38433868}
38443869
38453870/// Returns stubs atom that references `sym_with_loc` if one exists.
38463871/// Returns null otherwise.
3847pub fn getStubsAtomForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?*Atom {
3872pub fn getStubsAtomIndexForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?Atom.Index {
38483873 const stubs_index = self.stubs_table.get(sym_with_loc) orelse return null;
3849 return self.stubs.items[stubs_index].getAtom(self);
3874 return self.stubs.items[stubs_index].getAtomIndex(self);
38503875}
38513876
38523877/// Returns symbol location corresponding to the set entrypoint.
......@@ -4232,26 +4257,31 @@ pub fn logAtoms(self: *MachO) void {
42324257 log.debug("atoms:", .{});
42334258
42344259 const slice = self.sections.slice();
4235 for (slice.items(.last_atom)) |last, i| {
4236 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;
42374262 const header = slice.items(.header)[i];
42384263
4239 while (atom.prev) |prev| {
4240 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;
42414269 }
42424270
42434271 log.debug("{s},{s}", .{ header.segName(), header.sectName() });
42444272
42454273 while (true) {
4246 self.logAtom(atom);
4247 if (atom.next) |next| {
4248 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;
42494278 } else break;
42504279 }
42514280 }
42524281}
42534282
4254pub 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);
42554285 const sym = atom.getSymbol(self);
42564286 const sym_name = atom.getName(self);
42574287 log.debug(" ATOM(%{?d}, '{s}') @ {x} (sizeof({x}), alignof({x})) in object({?d}) in sect({d})", .{
src/link/MachO/Atom.zig+42-40
......@@ -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,22 +54,6 @@ 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};
66
67pub fn ensureInitialized(self: *Atom, macho_file: *MachO) !void {
68 if (self.getSymbolIndex() != null) return; // Already initialized
69 self.sym_index = try macho_file.allocateSymbol();
70 try macho_file.atom_by_index_table.putNoClobber(macho_file.base.allocator, self.sym_index, self);
71}
72
7357pub fn getSymbolIndex(self: Atom) ?u32 {
7458 if (self.sym_index == 0) return null;
7559 return self.sym_index;
......@@ -108,7 +92,8 @@ pub fn getName(self: Atom, macho_file: *MachO) []const u8 {
10892/// this calculation.
10993pub fn capacity(self: Atom, macho_file: *MachO) u64 {
11094 const self_sym = self.getSymbol(macho_file);
111 if (self.next) |next| {
95 if (self.next_index) |next_index| {
96 const next = macho_file.getAtom(next_index);
11297 const next_sym = next.getSymbol(macho_file);
11398 return next_sym.n_value - self_sym.n_value;
11499 } else {
......@@ -120,7 +105,8 @@ pub fn capacity(self: Atom, macho_file: *MachO) u64 {
120105
121106pub fn freeListEligible(self: Atom, macho_file: *MachO) bool {
122107 // No need to keep a free list node for the last atom.
123 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);
124110 const self_sym = self.getSymbol(macho_file);
125111 const next_sym = next.getSymbol(macho_file);
126112 const cap = next_sym.n_value - self_sym.n_value;
......@@ -130,19 +116,19 @@ pub fn freeListEligible(self: Atom, macho_file: *MachO) bool {
130116 return surplus >= MachO.min_text_capacity;
131117}
132118
133pub fn addRelocation(self: *Atom, macho_file: *MachO, reloc: Relocation) !void {
134 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});
135121}
136122
137123pub fn addRelocations(
138 self: *Atom,
139124 macho_file: *MachO,
125 atom_index: Index,
140126 comptime count: comptime_int,
141127 relocs: [count]Relocation,
142128) !void {
143129 const gpa = macho_file.base.allocator;
144130 const target = macho_file.base.options.target;
145 const gop = try macho_file.relocs.getOrPut(gpa, self);
131 const gop = try macho_file.relocs.getOrPut(gpa, atom_index);
146132 if (!gop.found_existing) {
147133 gop.value_ptr.* = .{};
148134 }
......@@ -156,56 +142,72 @@ pub fn addRelocations(
156142 }
157143}
158144
159pub fn addRebase(self: *Atom, macho_file: *MachO, offset: u32) !void {
145pub fn addRebase(macho_file: *MachO, atom_index: Index, offset: u32) !void {
160146 const gpa = macho_file.base.allocator;
161 log.debug(" (adding rebase at offset 0x{x} in %{?d})", .{ offset, self.getSymbolIndex() });
162 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);
163150 if (!gop.found_existing) {
164151 gop.value_ptr.* = .{};
165152 }
166153 try gop.value_ptr.append(gpa, offset);
167154}
168155
169pub fn addBinding(self: *Atom, macho_file: *MachO, binding: Binding) !void {
156pub fn addBinding(macho_file: *MachO, atom_index: Index, binding: Binding) !void {
170157 const gpa = macho_file.base.allocator;
158 const atom = macho_file.getAtom(atom_index);
171159 log.debug(" (adding binding to symbol {s} at offset 0x{x} in %{?d})", .{
172160 macho_file.getSymbolName(binding.target),
173161 binding.offset,
174 self.getSymbolIndex(),
162 atom.getSymbolIndex(),
175163 });
176 const gop = try macho_file.bindings.getOrPut(gpa, self);
164 const gop = try macho_file.bindings.getOrPut(gpa, atom_index);
177165 if (!gop.found_existing) {
178166 gop.value_ptr.* = .{};
179167 }
180168 try gop.value_ptr.append(gpa, binding);
181169}
182170
183pub fn addLazyBinding(self: *Atom, macho_file: *MachO, binding: Binding) !void {
171pub fn addLazyBinding(macho_file: *MachO, atom_index: Index, binding: Binding) !void {
184172 const gpa = macho_file.base.allocator;
173 const atom = macho_file.getAtom(atom_index);
185174 log.debug(" (adding lazy binding to symbol {s} at offset 0x{x} in %{?d})", .{
186175 macho_file.getSymbolName(binding.target),
187176 binding.offset,
188 self.getSymbolIndex(),
177 atom.getSymbolIndex(),
189178 });
190 const gop = try macho_file.lazy_bindings.getOrPut(gpa, self);
179 const gop = try macho_file.lazy_bindings.getOrPut(gpa, atom_index);
191180 if (!gop.found_existing) {
192181 gop.value_ptr.* = .{};
193182 }
194183 try gop.value_ptr.append(gpa, binding);
195184}
196185
197pub fn resolveRelocations(self: *Atom, macho_file: *MachO) !void {
198 const relocs = macho_file.relocs.get(self) orelse return;
199 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);
200190 const source_section = macho_file.sections.get(source_sym.n_sect - 1).header;
201191 const file_offset = source_section.offset + source_sym.n_value - source_section.addr;
202192
203 log.debug("relocating '{s}'", .{self.getName(macho_file)});
193 log.debug("relocating '{s}'", .{atom.getName(macho_file)});
204194
205195 for (relocs.items) |*reloc| {
206196 if (!reloc.dirty) continue;
207197
208 try reloc.resolve(self, macho_file, file_offset);
198 try reloc.resolve(macho_file, atom_index, file_offset);
209199 reloc.dirty = false;
210200 }
211201}
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/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+149-83
......@@ -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);
......@@ -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
......@@ -387,7 +401,7 @@ pub fn updateDecl(self: *Plan9, module: *Module, decl_index: Module.Decl.Index)
387401 }
388402 }
389403
390 try self.seeDecl(decl_index);
404 _ = try self.seeDecl(decl_index);
391405
392406 log.debug("codegen decl {*} ({s}) ({d})", .{ decl, decl.name, decl_index });
393407
......@@ -414,28 +428,31 @@ pub fn updateDecl(self: *Plan9, module: *Module, decl_index: Module.Decl.Index)
414428 if (self.data_decl_table.fetchPutAssumeCapacity(decl_index, duped_code)) |old_entry| {
415429 self.base.allocator.free(old_entry.value);
416430 }
417 return self.updateFinish(decl);
431 return self.updateFinish(decl_index);
418432}
419433/// called at the end of update{Decl,Func}
420fn 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);
421436 const is_fn = (decl.ty.zigTypeTag() == .Fn);
422437 log.debug("update the symbol table and got for decl {*} ({s})", .{ decl, decl.name });
423438 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);
424441 // write the internal linker metadata
425 decl.link.plan9.type = sym_t;
442 decl_block.type = sym_t;
426443 // write the symbol
427444 // we already have the got index
428445 const sym: aout.Sym = .{
429446 .value = undefined, // the value of stuff gets filled in in flushModule
430 .type = decl.link.plan9.type,
447 .type = decl_block.type,
431448 .name = mem.span(decl.name),
432449 };
433450
434 if (decl.link.plan9.sym_index) |s| {
451 if (decl_block.sym_index) |s| {
435452 self.syms.items[s] = sym;
436453 } else {
437454 const s = try self.allocateSymbolIndex();
438 decl.link.plan9.sym_index = s;
455 decl_block.sym_index = s;
439456 self.syms.items[s] = sym;
440457 }
441458}
......@@ -550,6 +567,7 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No
550567 while (it.next()) |entry| {
551568 const decl_index = entry.key_ptr.*;
552569 const decl = mod.declPtr(decl_index);
570 const decl_block = self.getDeclBlockPtr(self.decls.get(decl_index).?.index);
553571 const out = entry.value_ptr.*;
554572 log.debug("write text decl {*} ({s}), lines {d} to {d}", .{ decl, decl.name, out.start_line + 1, out.end_line });
555573 {
......@@ -568,16 +586,16 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No
568586 iovecs_i += 1;
569587 const off = self.getAddr(text_i, .t);
570588 text_i += out.code.len;
571 decl.link.plan9.offset = off;
589 decl_block.offset = off;
572590 if (!self.sixtyfour_bit) {
573 mem.writeIntNative(u32, got_table[decl.link.plan9.got_index.? * 4 ..][0..4], @intCast(u32, off));
574 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());
575593 } else {
576 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());
577595 }
578 self.syms.items[decl.link.plan9.sym_index.?].value = off;
596 self.syms.items[decl_block.sym_index.?].value = off;
579597 if (mod.decl_exports.get(decl_index)) |exports| {
580 try self.addDeclExports(mod, decl, exports.items);
598 try self.addDeclExports(mod, decl_index, exports.items);
581599 }
582600 }
583601 }
......@@ -598,6 +616,7 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No
598616 while (it.next()) |entry| {
599617 const decl_index = entry.key_ptr.*;
600618 const decl = mod.declPtr(decl_index);
619 const decl_block = self.getDeclBlockPtr(self.decls.get(decl_index).?.index);
601620 const code = entry.value_ptr.*;
602621 log.debug("write data decl {*} ({s})", .{ decl, decl.name });
603622
......@@ -606,15 +625,15 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No
606625 iovecs_i += 1;
607626 const off = self.getAddr(data_i, .d);
608627 data_i += code.len;
609 decl.link.plan9.offset = off;
628 decl_block.offset = off;
610629 if (!self.sixtyfour_bit) {
611 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());
612631 } else {
613 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());
614633 }
615 self.syms.items[decl.link.plan9.sym_index.?].value = off;
634 self.syms.items[decl_block.sym_index.?].value = off;
616635 if (mod.decl_exports.get(decl_index)) |exports| {
617 try self.addDeclExports(mod, decl, exports.items);
636 try self.addDeclExports(mod, decl_index, exports.items);
618637 }
619638 }
620639 // write the unnamed constants after the other data decls
......@@ -676,7 +695,8 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No
676695 for (kv.value_ptr.items) |reloc| {
677696 const target_decl_index = reloc.target;
678697 const target_decl = mod.declPtr(target_decl_index);
679 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.?;
680700
681701 const offset = reloc.offset;
682702 const addend = reloc.addend;
......@@ -709,28 +729,36 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No
709729fn addDeclExports(
710730 self: *Plan9,
711731 module: *Module,
712 decl: *Module.Decl,
732 decl_index: Module.Decl.Index,
713733 exports: []const *Module.Export,
714734) !void {
735 const metadata = self.decls.getPtr(decl_index).?;
736 const decl_block = self.getDeclBlock(metadata.index);
737
715738 for (exports) |exp| {
716739 // plan9 does not support custom sections
717740 if (exp.options.section) |section_name| {
718741 if (!mem.eql(u8, section_name, ".text") or !mem.eql(u8, section_name, ".data")) {
719 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 ));
720748 break;
721749 }
722750 }
723751 const sym = .{
724 .value = decl.link.plan9.offset.?,
725 .type = decl.link.plan9.type.toGlobal(),
752 .value = decl_block.offset.?,
753 .type = decl_block.type.toGlobal(),
726754 .name = exp.options.name,
727755 };
728756
729 if (exp.link.plan9) |i| {
757 if (metadata.getExport(self, exp.options.name)) |i| {
730758 self.syms.items[i] = sym;
731759 } else {
732760 try self.syms.append(self.base.allocator, sym);
733 exp.link.plan9 = self.syms.items.len - 1;
761 try metadata.exports.append(self.base.allocator, self.syms.items.len - 1);
734762 }
735763 }
736764}
......@@ -760,13 +788,18 @@ pub fn freeDecl(self: *Plan9, decl_index: Module.Decl.Index) void {
760788 self.base.allocator.free(removed_entry.value);
761789 }
762790 }
763 if (decl.link.plan9.got_index) |i| {
764 // TODO: if this catch {} is triggered, an assertion in flushModule will be triggered, because got_index_free_list will have the wrong length
765 self.got_index_free_list.append(self.base.allocator, i) catch {};
766 }
767 if (decl.link.plan9.sym_index) |i| {
768 self.syms_index_free_list.append(self.base.allocator, i) catch {};
769 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);
770803 }
771804 self.freeUnnamedConsts(decl_index);
772805 {
......@@ -786,12 +819,30 @@ fn freeUnnamedConsts(self: *Plan9, decl_index: Module.Decl.Index) void {
786819 unnamed_consts.clearAndFree(self.base.allocator);
787820}
788821
789pub fn seeDecl(self: *Plan9, decl_index: Module.Decl.Index) !void {
790 const mod = self.base.options.module.?;
791 const decl = mod.declPtr(decl_index);
792 if (decl.link.plan9.got_index == null) {
793 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 };
794844 }
845 return gop.value_ptr.index;
795846}
796847
797848pub fn updateDeclExports(
......@@ -800,7 +851,7 @@ pub fn updateDeclExports(
800851 decl_index: Module.Decl.Index,
801852 exports: []const *Module.Export,
802853) !void {
803 try self.seeDecl(decl_index);
854 _ = try self.seeDecl(decl_index);
804855 // we do all the things in flush
805856 _ = module;
806857 _ = exports;
......@@ -842,10 +893,17 @@ pub fn deinit(self: *Plan9) void {
842893 self.syms_index_free_list.deinit(gpa);
843894 self.file_segments.deinit(gpa);
844895 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 }
845905}
846906
847pub const Export = ?usize;
848pub const base_tag = .plan9;
849907pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Options) !*Plan9 {
850908 if (options.use_llvm)
851909 return error.LLVMBackendDoesNotSupportPlan9;
......@@ -911,20 +969,19 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void {
911969 }
912970 }
913971
914 const mod = self.base.options.module.?;
915
916972 // write the data symbols
917973 {
918974 var it = self.data_decl_table.iterator();
919975 while (it.next()) |entry| {
920976 const decl_index = entry.key_ptr.*;
921 const decl = mod.declPtr(decl_index);
922 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.?];
923980 try self.writeSym(writer, sym);
924981 if (self.base.options.module.?.decl_exports.get(decl_index)) |exports| {
925 for (exports.items) |e| {
926 try self.writeSym(writer, self.syms.items[e.link.plan9.?]);
927 }
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 };
928985 }
929986 }
930987 }
......@@ -943,16 +1000,17 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void {
9431000 var submap_it = symidx_and_submap.functions.iterator();
9441001 while (submap_it.next()) |entry| {
9451002 const decl_index = entry.key_ptr.*;
946 const decl = mod.declPtr(decl_index);
947 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.?];
9481006 try self.writeSym(writer, sym);
9491007 if (self.base.options.module.?.decl_exports.get(decl_index)) |exports| {
950 for (exports.items) |e| {
951 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];
9521010 if (mem.eql(u8, s.name, "_start"))
9531011 self.entry_val = s.value;
9541012 try self.writeSym(writer, s);
955 }
1013 };
9561014 }
9571015 }
9581016 }
......@@ -960,10 +1018,10 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void {
9601018}
9611019
9621020/// Must be called only after a successful call to `updateDecl`.
963pub fn updateDeclLineNumber(self: *Plan9, mod: *Module, decl: *const Module.Decl) !void {
1021pub fn updateDeclLineNumber(self: *Plan9, mod: *Module, decl_index: Module.Decl.Index) !void {
9641022 _ = self;
9651023 _ = mod;
966 _ = decl;
1024 _ = decl_index;
9671025}
9681026
9691027pub fn getDeclVAddr(
......@@ -1004,3 +1062,11 @@ pub fn getDeclVAddr(
10041062 });
10051063 return undefined;
10061064}
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+299-270
......@@ -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,19 @@ 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) = .{},
49/// Mapping between an `Atom` and its type index representing the Wasm
50/// type of the function signature.
51atom_types: std.AutoHashMapUnmanaged(Atom.Index, u32) = .{},
5352/// List of all symbols generated by Zig code.
5453symbols: std.ArrayListUnmanaged(Symbol) = .{},
5554/// List of symbol indexes which are free to be used.
5655symbols_free_list: std.ArrayListUnmanaged(u32) = .{},
5756/// 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) = .{},
57atoms: std.AutoHashMapUnmanaged(u32, Atom.Index) = .{},
58/// List of all atoms.
59managed_atoms: std.ArrayListUnmanaged(Atom) = .{},
6260/// Represents the index into `segments` where the 'code' section
6361/// lives.
6462code_section_index: ?u32 = null,
......@@ -148,7 +146,7 @@ undefs: std.StringArrayHashMapUnmanaged(SymbolLoc) = .{},
148146/// Maps a symbol's location to an atom. This can be used to find meta
149147/// data of a symbol, such as its size, or its offset to perform a relocation.
150148/// Undefined (and synthetic) symbols do not have an Atom and therefore cannot be mapped.
151symbol_atom: std.AutoHashMapUnmanaged(SymbolLoc, *Atom) = .{},
149symbol_atom: std.AutoHashMapUnmanaged(SymbolLoc, Atom.Index) = .{},
152150/// Maps a symbol's location to its export name, which may differ from the decl's name
153151/// which does the exporting.
154152/// Note: The value represents the offset into the string table, rather than the actual string.
......@@ -165,14 +163,14 @@ error_table_symbol: ?u32 = null,
165163// unit contains Zig code. The lifetime of these atoms are extended
166164// until the end of the compiler's lifetime. Meaning they're not freed
167165// 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,
166debug_info_atom: ?Atom.Index = null,
167debug_line_atom: ?Atom.Index = null,
168debug_loc_atom: ?Atom.Index = null,
169debug_ranges_atom: ?Atom.Index = null,
170debug_abbrev_atom: ?Atom.Index = null,
171debug_str_atom: ?Atom.Index = null,
172debug_pubnames_atom: ?Atom.Index = null,
173debug_pubtypes_atom: ?Atom.Index = null,
176174
177175pub const Segment = struct {
178176 alignment: u32,
......@@ -180,19 +178,6 @@ pub const Segment = struct {
180178 offset: u32,
181179};
182180
183pub const FnData = struct {
184 /// Reference to the wasm type that represents this function.
185 type_index: u32,
186 /// Contains debug information related to this function.
187 /// For Wasm, the offset is relative to the code-section.
188 src_fn: Dwarf.SrcFn,
189
190 pub const empty: FnData = .{
191 .type_index = undefined,
192 .src_fn = Dwarf.SrcFn.empty,
193 };
194};
195
196181pub const Export = struct {
197182 sym_index: ?u32 = null,
198183};
......@@ -434,10 +419,10 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
434419 // at the end during `initializeCallCtorsFunction`.
435420 }
436421
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 }
422 // if (!options.strip and options.module != null) {
423 // wasm_bin.dwarf = Dwarf.init(allocator, &wasm_bin.base, options.target);
424 // try wasm_bin.initDebugSections();
425 // }
441426
442427 return wasm_bin;
443428}
......@@ -478,6 +463,7 @@ fn createSyntheticSymbol(wasm: *Wasm, name: []const u8, tag: Symbol.Tag) !Symbol
478463 try wasm.globals.put(wasm.base.allocator, name_offset, loc);
479464 return loc;
480465}
466
481467/// Initializes symbols and atoms for the debug sections
482468/// Initialization is only done when compiling Zig code.
483469/// When Zig is invoked as a linker instead, the atoms
......@@ -520,6 +506,36 @@ fn parseObjectFile(wasm: *Wasm, path: []const u8) !bool {
520506 return true;
521507}
522508
509/// For a given `Module.Decl.Index` returns its corresponding `Atom.Index`.
510/// When the index was not found, a new `Atom` will be created, and its index will be returned.
511/// The newly created Atom is empty with default fields as specified by `Atom.empty`.
512pub fn getOrCreateAtomForDecl(wasm: *Wasm, decl_index: Module.Decl.Index) !Atom.Index {
513 const gop = try wasm.decls.getOrPut(wasm.base.allocator, decl_index);
514 if (!gop.found_existing) {
515 gop.value_ptr.* = try wasm.createAtom();
516 }
517 return gop.value_ptr.*;
518}
519
520/// Creates a new empty `Atom` and returns its `Atom.Index`
521fn createAtom(wasm: *Wasm) !Atom.Index {
522 const index = @intCast(Atom.Index, wasm.managed_atoms.items.len);
523 const atom = try wasm.managed_atoms.addOne(wasm.base.allocator);
524 atom.* = Atom.empty;
525 atom.sym_index = try wasm.allocateSymbol();
526 try wasm.symbol_atom.putNoClobber(wasm.base.allocator, .{ .file = null, .index = atom.sym_index }, index);
527
528 return index;
529}
530
531pub inline fn getAtom(wasm: *const Wasm, index: Atom.Index) Atom {
532 return wasm.managed_atoms.items[index];
533}
534
535pub inline fn getAtomPtr(wasm: *Wasm, index: Atom.Index) *Atom {
536 return &wasm.managed_atoms.items[index];
537}
538
523539/// Parses an archive file and will then parse each object file
524540/// that was found in the archive file.
525541/// Returns false when the file is not an archive file.
......@@ -861,15 +877,16 @@ fn resolveLazySymbols(wasm: *Wasm) !void {
861877 try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc);
862878 _ = wasm.resolved_symbols.swapRemove(loc); // we don't want to emit this symbol, only use it for relocations.
863879
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);
880 // TODO: Can we use `createAtom` here while also re-using the symbol
881 // from `createSyntheticSymbol`.
882 const atom_index = @intCast(Atom.Index, wasm.managed_atoms.items.len);
883 const atom = try wasm.managed_atoms.addOne(wasm.base.allocator);
867884 atom.* = Atom.empty;
868885 atom.sym_index = loc.index;
869886 atom.alignment = 1;
870887
871 try wasm.parseAtom(atom, .{ .data = .synthetic });
872 try wasm.symbol_atom.putNoClobber(wasm.base.allocator, loc, atom);
888 try wasm.parseAtom(atom_index, .{ .data = .synthetic });
889 try wasm.symbol_atom.putNoClobber(wasm.base.allocator, loc, atom_index);
873890 }
874891
875892 if (wasm.undefs.fetchSwapRemove("__heap_end")) |kv| {
......@@ -877,15 +894,14 @@ fn resolveLazySymbols(wasm: *Wasm) !void {
877894 try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc);
878895 _ = wasm.resolved_symbols.swapRemove(loc);
879896
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);
897 const atom_index = @intCast(Atom.Index, wasm.managed_atoms.items.len);
898 const atom = try wasm.managed_atoms.addOne(wasm.base.allocator);
883899 atom.* = Atom.empty;
884900 atom.sym_index = loc.index;
885901 atom.alignment = 1;
886902
887 try wasm.parseAtom(atom, .{ .data = .synthetic });
888 try wasm.symbol_atom.putNoClobber(wasm.base.allocator, loc, atom);
903 try wasm.parseAtom(atom_index, .{ .data = .synthetic });
904 try wasm.symbol_atom.putNoClobber(wasm.base.allocator, loc, atom_index);
889905 }
890906}
891907
......@@ -924,16 +940,6 @@ pub fn deinit(wasm: *Wasm) void {
924940 if (wasm.llvm_object) |llvm_object| llvm_object.destroy(gpa);
925941 }
926942
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
937943 for (wasm.func_types.items) |*func_type| {
938944 func_type.deinit(gpa);
939945 }
......@@ -949,6 +955,7 @@ pub fn deinit(wasm: *Wasm) void {
949955 }
950956
951957 wasm.decls.deinit(gpa);
958 wasm.atom_types.deinit(gpa);
952959 wasm.symbols.deinit(gpa);
953960 wasm.symbols_free_list.deinit(gpa);
954961 wasm.globals.deinit(gpa);
......@@ -958,9 +965,8 @@ pub fn deinit(wasm: *Wasm) void {
958965 wasm.symbol_atom.deinit(gpa);
959966 wasm.export_names.deinit(gpa);
960967 wasm.atoms.deinit(gpa);
961 for (wasm.managed_atoms.items) |managed_atom| {
962 managed_atom.deinit(gpa);
963 gpa.destroy(managed_atom);
968 for (wasm.managed_atoms.items) |*managed_atom| {
969 managed_atom.deinit(wasm);
964970 }
965971 wasm.managed_atoms.deinit(gpa);
966972 wasm.segments.deinit(gpa);
......@@ -1018,18 +1024,24 @@ pub fn updateFunc(wasm: *Wasm, mod: *Module, func: *Module.Fn, air: Air, livenes
10181024
10191025 const decl_index = func.owner_decl;
10201026 const decl = mod.declPtr(decl_index);
1021 const atom = &decl.link.wasm;
1022 try atom.ensureInitialized(wasm);
1023 const gop = try wasm.decls.getOrPut(wasm.base.allocator, decl_index);
1024 if (gop.found_existing) {
1025 atom.clear();
1026 } else gop.value_ptr.* = {};
1027 const atom_index = try wasm.getOrCreateAtomForDecl(decl_index);
1028 const atom = wasm.getAtomPtr(atom_index);
1029 atom.clear();
10271030
1028 var decl_state: ?Dwarf.DeclState = if (wasm.dwarf) |*dwarf| try dwarf.initDeclState(mod, decl_index) else null;
1029 defer if (decl_state) |*ds| ds.deinit();
1031 // var decl_state: ?Dwarf.DeclState = if (wasm.dwarf) |*dwarf| try dwarf.initDeclState(mod, decl_index) else null;
1032 // defer if (decl_state) |*ds| ds.deinit();
10301033
10311034 var code_writer = std.ArrayList(u8).init(wasm.base.allocator);
10321035 defer code_writer.deinit();
1036 // const result = try codegen.generateFunction(
1037 // &wasm.base,
1038 // decl.srcLoc(),
1039 // func,
1040 // air,
1041 // liveness,
1042 // &code_writer,
1043 // if (decl_state) |*ds| .{ .dwarf = ds } else .none,
1044 // );
10331045 const result = try codegen.generateFunction(
10341046 &wasm.base,
10351047 decl.srcLoc(),
......@@ -1037,7 +1049,7 @@ pub fn updateFunc(wasm: *Wasm, mod: *Module, func: *Module.Fn, air: Air, livenes
10371049 air,
10381050 liveness,
10391051 &code_writer,
1040 if (decl_state) |*ds| .{ .dwarf = ds } else .none,
1052 .none,
10411053 );
10421054
10431055 const code = switch (result) {
......@@ -1049,19 +1061,19 @@ pub fn updateFunc(wasm: *Wasm, mod: *Module, func: *Module.Fn, air: Air, livenes
10491061 },
10501062 };
10511063
1052 if (wasm.dwarf) |*dwarf| {
1053 try dwarf.commitDeclState(
1054 mod,
1055 decl_index,
1056 // Actual value will be written after relocation.
1057 // For Wasm, this is the offset relative to the code section
1058 // which isn't known until flush().
1059 0,
1060 code.len,
1061 &decl_state.?,
1062 );
1063 }
1064 return wasm.finishUpdateDecl(decl, code);
1064 // if (wasm.dwarf) |*dwarf| {
1065 // try dwarf.commitDeclState(
1066 // mod,
1067 // decl_index,
1068 // // Actual value will be written after relocation.
1069 // // For Wasm, this is the offset relative to the code section
1070 // // which isn't known until flush().
1071 // 0,
1072 // code.len,
1073 // &decl_state.?,
1074 // );
1075 // }
1076 return wasm.finishUpdateDecl(decl_index, code);
10651077}
10661078
10671079// Generate code for the Decl, storing it in memory to be later written to
......@@ -1084,17 +1096,14 @@ pub fn updateDecl(wasm: *Wasm, mod: *Module, decl_index: Module.Decl.Index) !voi
10841096 return;
10851097 }
10861098
1087 const atom = &decl.link.wasm;
1088 try atom.ensureInitialized(wasm);
1089 const gop = try wasm.decls.getOrPut(wasm.base.allocator, decl_index);
1090 if (gop.found_existing) {
1091 atom.clear();
1092 } else gop.value_ptr.* = {};
1099 const atom_index = try wasm.getOrCreateAtomForDecl(decl_index);
1100 const atom = wasm.getAtomPtr(atom_index);
1101 atom.clear();
10931102
10941103 if (decl.isExtern()) {
10951104 const variable = decl.getVariable().?;
10961105 const name = mem.sliceTo(decl.name, 0);
1097 return wasm.addOrUpdateImport(name, decl.link.wasm.sym_index, variable.lib_name, null);
1106 return wasm.addOrUpdateImport(name, atom.sym_index, variable.lib_name, null);
10981107 }
10991108 const val = if (decl.val.castTag(.variable)) |payload| payload.data.init else decl.val;
11001109
......@@ -1107,7 +1116,7 @@ pub fn updateDecl(wasm: *Wasm, mod: *Module, decl_index: Module.Decl.Index) !voi
11071116 .{ .ty = decl.ty, .val = val },
11081117 &code_writer,
11091118 .none,
1110 .{ .parent_atom_index = decl.link.wasm.sym_index },
1119 .{ .parent_atom_index = atom.sym_index },
11111120 );
11121121
11131122 const code = switch (res) {
......@@ -1119,26 +1128,29 @@ pub fn updateDecl(wasm: *Wasm, mod: *Module, decl_index: Module.Decl.Index) !voi
11191128 },
11201129 };
11211130
1122 return wasm.finishUpdateDecl(decl, code);
1131 return wasm.finishUpdateDecl(decl_index, code);
11231132}
11241133
1125pub fn updateDeclLineNumber(wasm: *Wasm, mod: *Module, decl: *const Module.Decl) !void {
1134pub fn updateDeclLineNumber(wasm: *Wasm, mod: *Module, decl_index: Module.Decl.Index) !void {
11261135 if (wasm.llvm_object) |_| return;
11271136 if (wasm.dwarf) |*dw| {
11281137 const tracy = trace(@src());
11291138 defer tracy.end();
11301139
1140 const decl = mod.declPtr(decl_index);
11311141 const decl_name = try decl.getFullyQualifiedName(mod);
11321142 defer wasm.base.allocator.free(decl_name);
11331143
11341144 log.debug("updateDeclLineNumber {s}{*}", .{ decl_name, decl });
1135 try dw.updateDeclLineNumber(decl);
1145 try dw.updateDeclLineNumber(mod, decl_index);
11361146 }
11371147}
11381148
1139fn finishUpdateDecl(wasm: *Wasm, decl: *Module.Decl, code: []const u8) !void {
1149fn finishUpdateDecl(wasm: *Wasm, decl_index: Module.Decl.Index, code: []const u8) !void {
11401150 const mod = wasm.base.options.module.?;
1141 const atom: *Atom = &decl.link.wasm;
1151 const decl = mod.declPtr(decl_index);
1152 const atom_index = wasm.decls.get(decl_index).?;
1153 const atom = wasm.getAtomPtr(atom_index);
11421154 const symbol = &wasm.symbols.items[atom.sym_index];
11431155 const full_name = try decl.getFullyQualifiedName(mod);
11441156 defer wasm.base.allocator.free(full_name);
......@@ -1204,48 +1216,51 @@ pub fn lowerUnnamedConst(wasm: *Wasm, tv: TypedValue, decl_index: Module.Decl.In
12041216 const decl = mod.declPtr(decl_index);
12051217
12061218 // Create and initialize a new local symbol and atom
1207 const local_index = decl.link.wasm.locals.items.len;
1219 const atom_index = try wasm.createAtom();
1220 const parent_atom_index = try wasm.getOrCreateAtomForDecl(decl_index);
1221 const parent_atom = wasm.getAtomPtr(parent_atom_index);
1222 const local_index = parent_atom.locals.items.len;
1223 try parent_atom.locals.append(wasm.base.allocator, atom_index);
12081224 const fqdn = try decl.getFullyQualifiedName(mod);
12091225 defer wasm.base.allocator.free(fqdn);
12101226 const name = try std.fmt.allocPrintZ(wasm.base.allocator, "__unnamed_{s}_{d}", .{ fqdn, local_index });
12111227 defer wasm.base.allocator.free(name);
1212
1213 const atom = try decl.link.wasm.locals.addOne(wasm.base.allocator);
1214 atom.* = Atom.empty;
1215 try atom.ensureInitialized(wasm);
1216 atom.alignment = tv.ty.abiAlignment(wasm.base.options.target);
1217 wasm.symbols.items[atom.sym_index] = .{
1218 .name = try wasm.string_table.put(wasm.base.allocator, name),
1219 .flags = @enumToInt(Symbol.Flag.WASM_SYM_BINDING_LOCAL),
1220 .tag = .data,
1221 .index = undefined,
1222 };
1223
1224 try wasm.resolved_symbols.putNoClobber(wasm.base.allocator, atom.symbolLoc(), {});
1225
12261228 var value_bytes = std.ArrayList(u8).init(wasm.base.allocator);
12271229 defer value_bytes.deinit();
12281230
1229 const result = try codegen.generateSymbol(
1230 &wasm.base,
1231 decl.srcLoc(),
1232 tv,
1233 &value_bytes,
1234 .none,
1235 .{
1236 .parent_atom_index = atom.sym_index,
1237 .addend = null,
1238 },
1239 );
1240 const code = switch (result) {
1241 .ok => value_bytes.items,
1242 .fail => |em| {
1243 decl.analysis = .codegen_failure;
1244 try mod.failed_decls.put(mod.gpa, decl_index, em);
1245 return error.AnalysisFail;
1246 },
1231 const code = code: {
1232 const atom = wasm.getAtomPtr(atom_index);
1233 atom.alignment = tv.ty.abiAlignment(wasm.base.options.target);
1234 wasm.symbols.items[atom.sym_index] = .{
1235 .name = try wasm.string_table.put(wasm.base.allocator, name),
1236 .flags = @enumToInt(Symbol.Flag.WASM_SYM_BINDING_LOCAL),
1237 .tag = .data,
1238 .index = undefined,
1239 };
1240 try wasm.resolved_symbols.putNoClobber(wasm.base.allocator, atom.symbolLoc(), {});
1241
1242 const result = try codegen.generateSymbol(
1243 &wasm.base,
1244 decl.srcLoc(),
1245 tv,
1246 &value_bytes,
1247 .none,
1248 .{
1249 .parent_atom_index = atom.sym_index,
1250 .addend = null,
1251 },
1252 );
1253 break :code switch (result) {
1254 .ok => 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 },
1260 };
12471261 };
12481262
1263 const atom = wasm.getAtomPtr(atom_index);
12491264 atom.size = @intCast(u32, code.len);
12501265 try atom.code.appendSlice(wasm.base.allocator, code);
12511266 return atom.sym_index;
......@@ -1293,10 +1308,13 @@ pub fn getDeclVAddr(
12931308) !u64 {
12941309 const mod = wasm.base.options.module.?;
12951310 const decl = mod.declPtr(decl_index);
1296 try decl.link.wasm.ensureInitialized(wasm);
1297 const target_symbol_index = decl.link.wasm.sym_index;
1311
1312 const target_atom_index = try wasm.getOrCreateAtomForDecl(decl_index);
1313 const target_symbol_index = wasm.getAtom(target_atom_index).sym_index;
1314
12981315 assert(reloc_info.parent_atom_index != 0);
1299 const atom = wasm.symbol_atom.get(.{ .file = null, .index = reloc_info.parent_atom_index }).?;
1316 const atom_index = wasm.symbol_atom.get(.{ .file = null, .index = reloc_info.parent_atom_index }).?;
1317 const atom = wasm.getAtomPtr(atom_index);
13001318 const is_wasm32 = wasm.base.options.target.cpu.arch == .wasm32;
13011319 if (decl.ty.zigTypeTag() == .Fn) {
13021320 assert(reloc_info.addend == 0); // addend not allowed for function relocations
......@@ -1324,9 +1342,10 @@ pub fn getDeclVAddr(
13241342 return target_symbol_index;
13251343}
13261344
1327pub fn deleteExport(wasm: *Wasm, exp: Export) void {
1345pub fn deleteDeclExport(wasm: *Wasm, decl_index: Module.Decl.Index) void {
13281346 if (wasm.llvm_object) |_| return;
1329 const sym_index = exp.sym_index orelse return;
1347 const atom_index = wasm.decls.get(decl_index) orelse return;
1348 const sym_index = wasm.getAtom(atom_index).sym_index;
13301349 const loc: SymbolLoc = .{ .file = null, .index = sym_index };
13311350 const symbol = loc.getSymbol(wasm);
13321351 const symbol_name = wasm.string_table.get(symbol.name);
......@@ -1352,7 +1371,8 @@ pub fn updateDeclExports(
13521371 }
13531372
13541373 const decl = mod.declPtr(decl_index);
1355 if (decl.link.wasm.getSymbolIndex() == null) return; // unititialized
1374 const atom_index = try wasm.getOrCreateAtomForDecl(decl_index);
1375 const atom = wasm.getAtom(atom_index);
13561376
13571377 for (exports) |exp| {
13581378 if (exp.options.section) |section| {
......@@ -1367,7 +1387,7 @@ pub fn updateDeclExports(
13671387
13681388 const export_name = try wasm.string_table.put(wasm.base.allocator, exp.options.name);
13691389 if (wasm.globals.getPtr(export_name)) |existing_loc| {
1370 if (existing_loc.index == decl.link.wasm.sym_index) continue;
1390 if (existing_loc.index == atom.sym_index) continue;
13711391 const existing_sym: Symbol = existing_loc.getSymbol(wasm).*;
13721392
13731393 const exp_is_weak = exp.options.linkage == .Internal or exp.options.linkage == .Weak;
......@@ -1388,15 +1408,16 @@ pub fn updateDeclExports(
13881408 } else if (exp_is_weak) {
13891409 continue; // to-be-exported symbol is weak, so we keep the existing symbol
13901410 } else {
1391 existing_loc.index = decl.link.wasm.sym_index;
1411 // TODO: Revisit this, why was this needed?
1412 existing_loc.index = atom.sym_index;
13921413 existing_loc.file = null;
1393 exp.link.wasm.sym_index = existing_loc.index;
1414 // exp.link.wasm.sym_index = existing_loc.index;
13941415 }
13951416 }
13961417
1397 const exported_decl = mod.declPtr(exp.exported_decl);
1398 const sym_index = exported_decl.link.wasm.sym_index;
1399 const sym_loc = exported_decl.link.wasm.symbolLoc();
1418 const exported_atom_index = try wasm.getOrCreateAtomForDecl(exp.exported_decl);
1419 const exported_atom = wasm.getAtom(exported_atom_index);
1420 const sym_loc = exported_atom.symbolLoc();
14001421 const symbol = sym_loc.getSymbol(wasm);
14011422 switch (exp.options.linkage) {
14021423 .Internal => {
......@@ -1432,7 +1453,6 @@ pub fn updateDeclExports(
14321453 // if the symbol was previously undefined, remove it as an import
14331454 _ = wasm.imports.remove(sym_loc);
14341455 _ = wasm.undefs.swapRemove(exp.options.name);
1435 exp.link.wasm.sym_index = sym_index;
14361456 }
14371457}
14381458
......@@ -1442,11 +1462,13 @@ pub fn freeDecl(wasm: *Wasm, decl_index: Module.Decl.Index) void {
14421462 }
14431463 const mod = wasm.base.options.module.?;
14441464 const decl = mod.declPtr(decl_index);
1445 const atom = &decl.link.wasm;
1465 const atom_index = wasm.decls.get(decl_index).?;
1466 const atom = wasm.getAtomPtr(atom_index);
14461467 wasm.symbols_free_list.append(wasm.base.allocator, atom.sym_index) catch {};
14471468 _ = wasm.decls.remove(decl_index);
14481469 wasm.symbols.items[atom.sym_index].tag = .dead;
1449 for (atom.locals.items) |local_atom| {
1470 for (atom.locals.items) |local_atom_index| {
1471 const local_atom = wasm.getAtom(local_atom_index);
14501472 const local_symbol = &wasm.symbols.items[local_atom.sym_index];
14511473 local_symbol.tag = .dead; // also for any local symbol
14521474 wasm.symbols_free_list.append(wasm.base.allocator, local_atom.sym_index) catch {};
......@@ -1460,12 +1482,20 @@ pub fn freeDecl(wasm: *Wasm, decl_index: Module.Decl.Index) void {
14601482 _ = wasm.resolved_symbols.swapRemove(atom.symbolLoc());
14611483 _ = wasm.symbol_atom.remove(atom.symbolLoc());
14621484
1463 if (wasm.dwarf) |*dwarf| {
1464 dwarf.freeDecl(decl);
1465 dwarf.freeAtom(&atom.dbg_info_atom);
1466 }
1485 // if (wasm.dwarf) |*dwarf| {
1486 // dwarf.freeDecl(decl_index);
1487 // }
14671488
1468 atom.deinit(wasm.base.allocator);
1489 if (atom.next) |next_atom_index| {
1490 const next_atom = wasm.getAtomPtr(next_atom_index);
1491 next_atom.prev = atom.prev;
1492 atom.next = null;
1493 }
1494 if (atom.prev) |prev_index| {
1495 const prev_atom = wasm.getAtomPtr(prev_index);
1496 prev_atom.next = atom.next;
1497 atom.prev = null;
1498 }
14691499}
14701500
14711501/// Appends a new entry to the indirect function table
......@@ -1572,7 +1602,7 @@ const Kind = union(enum) {
15721602 initialized,
15731603 synthetic,
15741604 },
1575 function: FnData,
1605 function: void,
15761606
15771607 /// Returns the segment name the data kind represents.
15781608 /// Asserts `kind` has its active tag set to `data`.
......@@ -1587,15 +1617,17 @@ const Kind = union(enum) {
15871617};
15881618
15891619/// Parses an Atom and inserts its metadata into the corresponding sections.
1590fn parseAtom(wasm: *Wasm, atom: *Atom, kind: Kind) !void {
1620fn parseAtom(wasm: *Wasm, atom_index: Atom.Index, kind: Kind) !void {
1621 const atom = wasm.getAtomPtr(atom_index);
15911622 const symbol = (SymbolLoc{ .file = null, .index = atom.sym_index }).getSymbol(wasm);
15921623 const final_index: u32 = switch (kind) {
1593 .function => |fn_data| result: {
1624 .function => result: {
15941625 const index = @intCast(u32, wasm.functions.count() + wasm.imported_functions_count);
1626 const type_index = wasm.atom_types.get(atom_index).?;
15951627 try wasm.functions.putNoClobber(
15961628 wasm.base.allocator,
15971629 .{ .file = null, .index = index },
1598 .{ .type_index = fn_data.type_index },
1630 .{ .type_index = type_index },
15991631 );
16001632 symbol.tag = .function;
16011633 symbol.index = index;
......@@ -1662,18 +1694,20 @@ fn parseAtom(wasm: *Wasm, atom: *Atom, kind: Kind) !void {
16621694 const segment: *Segment = &wasm.segments.items[final_index];
16631695 segment.alignment = std.math.max(segment.alignment, atom.alignment);
16641696
1665 try wasm.appendAtomAtIndex(final_index, atom);
1697 try wasm.appendAtomAtIndex(final_index, atom_index);
16661698}
16671699
16681700/// From a given index, append the given `Atom` at the back of the linked list.
16691701/// Simply inserts it into the map of atoms when it doesn't exist yet.
1670pub fn appendAtomAtIndex(wasm: *Wasm, index: u32, atom: *Atom) !void {
1671 if (wasm.atoms.getPtr(index)) |last| {
1672 last.*.next = atom;
1673 atom.prev = last.*;
1674 last.* = atom;
1702pub fn appendAtomAtIndex(wasm: *Wasm, index: u32, atom_index: Atom.Index) !void {
1703 const atom = wasm.getAtomPtr(atom_index);
1704 if (wasm.atoms.getPtr(index)) |last_index_ptr| {
1705 const last = wasm.getAtomPtr(last_index_ptr.*);
1706 last.*.next = atom_index;
1707 atom.prev = last_index_ptr.*;
1708 last_index_ptr.* = atom_index;
16751709 } else {
1676 try wasm.atoms.putNoClobber(wasm.base.allocator, index, atom);
1710 try wasm.atoms.putNoClobber(wasm.base.allocator, index, atom_index);
16771711 }
16781712}
16791713
......@@ -1683,16 +1717,17 @@ fn allocateDebugAtoms(wasm: *Wasm) !void {
16831717 if (wasm.dwarf == null) return;
16841718
16851719 const allocAtom = struct {
1686 fn f(bin: *Wasm, maybe_index: *?u32, atom: *Atom) !void {
1720 fn f(bin: *Wasm, maybe_index: *?u32, atom_index: Atom.Index) !void {
16871721 const index = maybe_index.* orelse idx: {
16881722 const index = @intCast(u32, bin.segments.items.len);
16891723 try bin.appendDummySegment();
16901724 maybe_index.* = index;
16911725 break :idx index;
16921726 };
1727 const atom = bin.getAtomPtr(atom_index);
16931728 atom.size = @intCast(u32, atom.code.items.len);
16941729 bin.symbols.items[atom.sym_index].index = index;
1695 try bin.appendAtomAtIndex(index, atom);
1730 try bin.appendAtomAtIndex(index, atom_index);
16961731 }
16971732 }.f;
16981733
......@@ -1714,15 +1749,16 @@ fn allocateAtoms(wasm: *Wasm) !void {
17141749 var it = wasm.atoms.iterator();
17151750 while (it.next()) |entry| {
17161751 const segment = &wasm.segments.items[entry.key_ptr.*];
1717 var atom: *Atom = entry.value_ptr.*.getFirst();
1752 var atom_index = entry.value_ptr.*;
17181753 var offset: u32 = 0;
17191754 while (true) {
1755 const atom = wasm.getAtomPtr(atom_index);
17201756 const symbol_loc = atom.symbolLoc();
17211757 if (wasm.code_section_index) |index| {
17221758 if (index == entry.key_ptr.*) {
17231759 if (!wasm.resolved_symbols.contains(symbol_loc)) {
17241760 // only allocate resolved function body's.
1725 atom = atom.next orelse break;
1761 atom_index = atom.prev orelse break;
17261762 continue;
17271763 }
17281764 }
......@@ -1736,8 +1772,7 @@ fn allocateAtoms(wasm: *Wasm) !void {
17361772 atom.size,
17371773 });
17381774 offset += atom.size;
1739 try wasm.symbol_atom.put(wasm.base.allocator, symbol_loc, atom); // Update atom pointers
1740 atom = atom.next orelse break;
1775 atom_index = atom.prev orelse break;
17411776 }
17421777 segment.size = std.mem.alignForwardGeneric(u32, offset, segment.alignment);
17431778 }
......@@ -1871,8 +1906,8 @@ fn initializeCallCtorsFunction(wasm: *Wasm) !void {
18711906 symbol.index = func_index;
18721907
18731908 // create the atom that will be output into the final binary
1874 const atom = try wasm.base.allocator.create(Atom);
1875 errdefer wasm.base.allocator.destroy(atom);
1909 const atom_index = @intCast(Atom.Index, wasm.managed_atoms.items.len);
1910 const atom = try wasm.managed_atoms.addOne(wasm.base.allocator);
18761911 atom.* = .{
18771912 .size = @intCast(u32, function_body.items.len),
18781913 .offset = 0,
......@@ -1882,15 +1917,14 @@ fn initializeCallCtorsFunction(wasm: *Wasm) !void {
18821917 .next = null,
18831918 .prev = null,
18841919 .code = function_body.moveToUnmanaged(),
1885 .dbg_info_atom = undefined,
18861920 };
1887 try wasm.managed_atoms.append(wasm.base.allocator, atom);
1888 try wasm.appendAtomAtIndex(wasm.code_section_index.?, atom);
1889 try wasm.symbol_atom.putNoClobber(wasm.base.allocator, loc, atom);
1921 try wasm.appendAtomAtIndex(wasm.code_section_index.?, atom_index);
1922 try wasm.symbol_atom.putNoClobber(wasm.base.allocator, loc, atom_index);
18901923
18911924 // `allocateAtoms` has already been called, set the atom's offset manually.
18921925 // This is fine to do manually as we insert the atom at the very end.
1893 atom.offset = atom.prev.?.offset + atom.prev.?.size;
1926 const prev_atom = wasm.getAtom(atom.prev.?);
1927 atom.offset = prev_atom.offset + prev_atom.size;
18941928}
18951929
18961930fn setupImports(wasm: *Wasm) !void {
......@@ -2093,7 +2127,8 @@ fn setupExports(wasm: *Wasm) !void {
20932127 break :blk try wasm.string_table.put(wasm.base.allocator, sym_name);
20942128 };
20952129 const exp: types.Export = if (symbol.tag == .data) exp: {
2096 const atom = wasm.symbol_atom.get(sym_loc).?;
2130 const atom_index = wasm.symbol_atom.get(sym_loc).?;
2131 const atom = wasm.getAtom(atom_index);
20972132 const va = atom.getVA(wasm, symbol);
20982133 const global_index = @intCast(u32, wasm.imported_globals_count + wasm.wasm_globals.items.len);
20992134 try wasm.wasm_globals.append(wasm.base.allocator, .{
......@@ -2198,7 +2233,8 @@ fn setupMemory(wasm: *Wasm) !void {
21982233 const segment_index = wasm.data_segments.get(".synthetic").?;
21992234 const segment = &wasm.segments.items[segment_index];
22002235 segment.offset = 0; // for simplicity we store the entire VA into atom's offset.
2201 const atom = wasm.symbol_atom.get(loc).?;
2236 const atom_index = wasm.symbol_atom.get(loc).?;
2237 const atom = wasm.getAtomPtr(atom_index);
22022238 atom.offset = @intCast(u32, mem.alignForwardGeneric(u64, memory_ptr, heap_alignment));
22032239 }
22042240
......@@ -2231,7 +2267,8 @@ fn setupMemory(wasm: *Wasm) !void {
22312267 const segment_index = wasm.data_segments.get(".synthetic").?;
22322268 const segment = &wasm.segments.items[segment_index];
22332269 segment.offset = 0;
2234 const atom = wasm.symbol_atom.get(loc).?;
2270 const atom_index = wasm.symbol_atom.get(loc).?;
2271 const atom = wasm.getAtomPtr(atom_index);
22352272 atom.offset = @intCast(u32, memory_ptr);
22362273 }
22372274
......@@ -2357,15 +2394,14 @@ pub fn getErrorTableSymbol(wasm: *Wasm) !u32 {
23572394 // and then return said symbol's index. The final table will be populated
23582395 // during `flush` when we know all possible error names.
23592396
2360 // As sym_index '0' is reserved, we use it for our stack pointer symbol
2361 const symbol_index = wasm.symbols_free_list.popOrNull() orelse blk: {
2362 const index = @intCast(u32, wasm.symbols.items.len);
2363 _ = try wasm.symbols.addOne(wasm.base.allocator);
2364 break :blk index;
2365 };
2397 const atom_index = try wasm.createAtom();
2398 const atom = wasm.getAtomPtr(atom_index);
2399 const slice_ty = Type.initTag(.const_slice_u8_sentinel_0);
2400 atom.alignment = slice_ty.abiAlignment(wasm.base.options.target);
2401 const sym_index = atom.sym_index;
23662402
23672403 const sym_name = try wasm.string_table.put(wasm.base.allocator, "__zig_err_name_table");
2368 const symbol = &wasm.symbols.items[symbol_index];
2404 const symbol = &wasm.symbols.items[sym_index];
23692405 symbol.* = .{
23702406 .name = sym_name,
23712407 .tag = .data,
......@@ -2374,20 +2410,11 @@ pub fn getErrorTableSymbol(wasm: *Wasm) !u32 {
23742410 };
23752411 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
23762412
2377 const slice_ty = Type.initTag(.const_slice_u8_sentinel_0);
2413 try wasm.resolved_symbols.put(wasm.base.allocator, atom.symbolLoc(), {});
23782414
2379 const atom = try wasm.base.allocator.create(Atom);
2380 atom.* = Atom.empty;
2381 atom.sym_index = symbol_index;
2382 atom.alignment = slice_ty.abiAlignment(wasm.base.options.target);
2383 try wasm.managed_atoms.append(wasm.base.allocator, atom);
2384 const loc = atom.symbolLoc();
2385 try wasm.resolved_symbols.put(wasm.base.allocator, loc, {});
2386 try wasm.symbol_atom.put(wasm.base.allocator, loc, atom);
2387
2388 log.debug("Error name table was created with symbol index: ({d})", .{symbol_index});
2389 wasm.error_table_symbol = symbol_index;
2390 return symbol_index;
2415 log.debug("Error name table was created with symbol index: ({d})", .{sym_index});
2416 wasm.error_table_symbol = sym_index;
2417 return sym_index;
23912418}
23922419
23932420/// Populates the error name table, when `error_table_symbol` is not null.
......@@ -2396,22 +2423,17 @@ pub fn getErrorTableSymbol(wasm: *Wasm) !u32 {
23962423/// The table is what is being pointed to within the runtime bodies that are generated.
23972424fn populateErrorNameTable(wasm: *Wasm) !void {
23982425 const symbol_index = wasm.error_table_symbol orelse return;
2399 const atom: *Atom = wasm.symbol_atom.get(.{ .file = null, .index = symbol_index }).?;
2426 const atom_index = wasm.symbol_atom.get(.{ .file = null, .index = symbol_index }).?;
2427 const atom = wasm.getAtomPtr(atom_index);
2428
24002429 // Rather than creating a symbol for each individual error name,
24012430 // we create a symbol for the entire region of error names. We then calculate
24022431 // the pointers into the list using addends which are appended to the relocation.
2403 const names_atom = try wasm.base.allocator.create(Atom);
2404 names_atom.* = Atom.empty;
2405 try wasm.managed_atoms.append(wasm.base.allocator, names_atom);
2406 const names_symbol_index = wasm.symbols_free_list.popOrNull() orelse blk: {
2407 const index = @intCast(u32, wasm.symbols.items.len);
2408 _ = try wasm.symbols.addOne(wasm.base.allocator);
2409 break :blk index;
2410 };
2411 names_atom.sym_index = names_symbol_index;
2432 const names_atom_index = try wasm.createAtom();
2433 const names_atom = wasm.getAtomPtr(names_atom_index);
24122434 names_atom.alignment = 1;
24132435 const sym_name = try wasm.string_table.put(wasm.base.allocator, "__zig_err_names");
2414 const names_symbol = &wasm.symbols.items[names_symbol_index];
2436 const names_symbol = &wasm.symbols.items[names_atom.sym_index];
24152437 names_symbol.* = .{
24162438 .name = sym_name,
24172439 .tag = .data,
......@@ -2435,7 +2457,7 @@ fn populateErrorNameTable(wasm: *Wasm) !void {
24352457 try atom.code.writer(wasm.base.allocator).writeIntLittle(u32, len - 1);
24362458 // create relocation to the error name
24372459 try atom.relocs.append(wasm.base.allocator, .{
2438 .index = names_symbol_index,
2460 .index = names_atom.sym_index,
24392461 .relocation_type = .R_WASM_MEMORY_ADDR_I32,
24402462 .offset = offset,
24412463 .addend = @intCast(i32, addend),
......@@ -2454,61 +2476,53 @@ fn populateErrorNameTable(wasm: *Wasm) !void {
24542476
24552477 const name_loc = names_atom.symbolLoc();
24562478 try wasm.resolved_symbols.put(wasm.base.allocator, name_loc, {});
2457 try wasm.symbol_atom.put(wasm.base.allocator, name_loc, names_atom);
2479 try wasm.symbol_atom.put(wasm.base.allocator, name_loc, names_atom_index);
24582480
24592481 // link the atoms with the rest of the binary so they can be allocated
24602482 // and relocations will be performed.
2461 try wasm.parseAtom(atom, .{ .data = .read_only });
2462 try wasm.parseAtom(names_atom, .{ .data = .read_only });
2483 try wasm.parseAtom(atom_index, .{ .data = .read_only });
2484 try wasm.parseAtom(names_atom_index, .{ .data = .read_only });
24632485}
24642486
24652487/// From a given index variable, creates a new debug section.
24662488/// This initializes the index, appends a new segment,
24672489/// and finally, creates a managed `Atom`.
2468pub fn createDebugSectionForIndex(wasm: *Wasm, index: *?u32, name: []const u8) !*Atom {
2490pub fn createDebugSectionForIndex(wasm: *Wasm, index: *?u32, name: []const u8) !Atom.Index {
24692491 const new_index = @intCast(u32, wasm.segments.items.len);
24702492 index.* = new_index;
24712493 try wasm.appendDummySegment();
24722494
2473 const sym_index = wasm.symbols_free_list.popOrNull() orelse idx: {
2474 const tmp_index = @intCast(u32, wasm.symbols.items.len);
2475 _ = try wasm.symbols.addOne(wasm.base.allocator);
2476 break :idx tmp_index;
2477 };
2478 wasm.symbols.items[sym_index] = .{
2495 const atom_index = try wasm.createAtom();
2496 const atom = wasm.getAtomPtr(atom_index);
2497 wasm.symbols.items[atom.sym_index] = .{
24792498 .tag = .section,
24802499 .name = try wasm.string_table.put(wasm.base.allocator, name),
24812500 .index = 0,
24822501 .flags = @enumToInt(Symbol.Flag.WASM_SYM_BINDING_LOCAL),
24832502 };
24842503
2485 const atom = try wasm.base.allocator.create(Atom);
2486 atom.* = Atom.empty;
24872504 atom.alignment = 1; // debug sections are always 1-byte-aligned
2488 atom.sym_index = sym_index;
2489 try wasm.managed_atoms.append(wasm.base.allocator, atom);
2490 try wasm.symbol_atom.put(wasm.base.allocator, atom.symbolLoc(), atom);
2491 return atom;
2505 return atom_index;
24922506}
24932507
24942508fn resetState(wasm: *Wasm) void {
24952509 for (wasm.segment_info.values()) |segment_info| {
24962510 wasm.base.allocator.free(segment_info.name);
24972511 }
2498 if (wasm.base.options.module) |mod| {
2499 var decl_it = wasm.decls.keyIterator();
2500 while (decl_it.next()) |decl_index_ptr| {
2501 const decl = mod.declPtr(decl_index_ptr.*);
2502 const atom = &decl.link.wasm;
2503 atom.next = null;
2504 atom.prev = null;
2505
2506 for (atom.locals.items) |*local_atom| {
2507 local_atom.next = null;
2508 local_atom.prev = null;
2509 }
2512
2513 var atom_it = wasm.decls.valueIterator();
2514 while (atom_it.next()) |atom_index| {
2515 const atom = wasm.getAtomPtr(atom_index.*);
2516 atom.next = null;
2517 atom.prev = null;
2518
2519 for (atom.locals.items) |local_atom_index| {
2520 const local_atom = wasm.getAtomPtr(local_atom_index);
2521 local_atom.next = null;
2522 local_atom.prev = null;
25102523 }
25112524 }
2525
25122526 wasm.functions.clearRetainingCapacity();
25132527 wasm.exports.clearRetainingCapacity();
25142528 wasm.segments.clearRetainingCapacity();
......@@ -2805,28 +2819,29 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
28052819 try wasm.setupStart();
28062820 try wasm.setupImports();
28072821 if (wasm.base.options.module) |mod| {
2808 var decl_it = wasm.decls.keyIterator();
2809 while (decl_it.next()) |decl_index_ptr| {
2810 const decl = mod.declPtr(decl_index_ptr.*);
2822 var decl_it = wasm.decls.iterator();
2823 while (decl_it.next()) |entry| {
2824 const decl = mod.declPtr(entry.key_ptr.*);
28112825 if (decl.isExtern()) continue;
2812 const atom = &decl.*.link.wasm;
2826 const atom_index = entry.value_ptr.*;
28132827 if (decl.ty.zigTypeTag() == .Fn) {
2814 try wasm.parseAtom(atom, .{ .function = decl.fn_link.wasm });
2828 try wasm.parseAtom(atom_index, .function);
28152829 } else if (decl.getVariable()) |variable| {
28162830 if (!variable.is_mutable) {
2817 try wasm.parseAtom(atom, .{ .data = .read_only });
2831 try wasm.parseAtom(atom_index, .{ .data = .read_only });
28182832 } else if (variable.init.isUndefDeep()) {
2819 try wasm.parseAtom(atom, .{ .data = .uninitialized });
2833 try wasm.parseAtom(atom_index, .{ .data = .uninitialized });
28202834 } else {
2821 try wasm.parseAtom(atom, .{ .data = .initialized });
2835 try wasm.parseAtom(atom_index, .{ .data = .initialized });
28222836 }
28232837 } else {
2824 try wasm.parseAtom(atom, .{ .data = .read_only });
2838 try wasm.parseAtom(atom_index, .{ .data = .read_only });
28252839 }
28262840
28272841 // also parse atoms for a decl's locals
2828 for (atom.locals.items) |*local_atom| {
2829 try wasm.parseAtom(local_atom, .{ .data = .read_only });
2842 const atom = wasm.getAtomPtr(atom_index);
2843 for (atom.locals.items) |local_atom_index| {
2844 try wasm.parseAtom(local_atom_index, .{ .data = .read_only });
28302845 }
28312846 }
28322847
......@@ -3071,20 +3086,22 @@ fn writeToFile(
30713086 var code_section_size: u32 = 0;
30723087 if (wasm.code_section_index) |code_index| {
30733088 const header_offset = try reserveVecSectionHeader(&binary_bytes);
3074 var atom: *Atom = wasm.atoms.get(code_index).?.getFirst();
3089 var atom_index = wasm.atoms.get(code_index).?;
30753090
30763091 // The code section must be sorted in line with the function order.
30773092 var sorted_atoms = try std.ArrayList(*Atom).initCapacity(wasm.base.allocator, wasm.functions.count());
30783093 defer sorted_atoms.deinit();
30793094
30803095 while (true) {
3096 var atom = wasm.getAtomPtr(atom_index);
30813097 if (wasm.resolved_symbols.contains(atom.symbolLoc())) {
30823098 if (!is_obj) {
30833099 atom.resolveRelocs(wasm);
30843100 }
30853101 sorted_atoms.appendAssumeCapacity(atom);
30863102 }
3087 atom = atom.next orelse break;
3103 // atom = if (atom.prev) |prev| wasm.getAtomPtr(prev) else break;
3104 atom_index = atom.prev orelse break;
30883105 }
30893106
30903107 const atom_sort_fn = struct {
......@@ -3124,11 +3141,11 @@ fn writeToFile(
31243141 // do not output 'bss' section unless we import memory and therefore
31253142 // want to guarantee the data is zero initialized
31263143 if (!import_memory and std.mem.eql(u8, entry.key_ptr.*, ".bss")) continue;
3127 const atom_index = entry.value_ptr.*;
3128 const segment = wasm.segments.items[atom_index];
3144 const segment_index = entry.value_ptr.*;
3145 const segment = wasm.segments.items[segment_index];
31293146 if (segment.size == 0) continue; // do not emit empty segments
31303147 segment_count += 1;
3131 var atom: *Atom = wasm.atoms.getPtr(atom_index).?.*.getFirst();
3148 var atom_index = wasm.atoms.get(segment_index).?;
31323149
31333150 // flag and index to memory section (currently, there can only be 1 memory section in wasm)
31343151 try leb.writeULEB128(binary_writer, @as(u32, 0));
......@@ -3139,6 +3156,7 @@ fn writeToFile(
31393156 // fill in the offset table and the data segments
31403157 var current_offset: u32 = 0;
31413158 while (true) {
3159 const atom = wasm.getAtomPtr(atom_index);
31423160 if (!is_obj) {
31433161 atom.resolveRelocs(wasm);
31443162 }
......@@ -3154,8 +3172,8 @@ fn writeToFile(
31543172 try binary_writer.writeAll(atom.code.items);
31553173
31563174 current_offset += atom.size;
3157 if (atom.next) |next| {
3158 atom = next;
3175 if (atom.prev) |prev| {
3176 atom_index = prev;
31593177 } else {
31603178 // also pad with zeroes when last atom to ensure
31613179 // segments are aligned.
......@@ -3197,15 +3215,15 @@ fn writeToFile(
31973215 }
31983216
31993217 if (!wasm.base.options.strip) {
3200 if (wasm.dwarf) |*dwarf| {
3201 const mod = wasm.base.options.module.?;
3202 try dwarf.writeDbgAbbrev();
3203 // for debug info and ranges, the address is always 0,
3204 // as locations are always offsets relative to 'code' section.
3205 try dwarf.writeDbgInfoHeader(mod, 0, code_section_size);
3206 try dwarf.writeDbgAranges(0, code_section_size);
3207 try dwarf.writeDbgLineHeader();
3208 }
3218 // if (wasm.dwarf) |*dwarf| {
3219 // const mod = wasm.base.options.module.?;
3220 // try dwarf.writeDbgAbbrev();
3221 // // for debug info and ranges, the address is always 0,
3222 // // as locations are always offsets relative to 'code' section.
3223 // try dwarf.writeDbgInfoHeader(mod, 0, code_section_size);
3224 // try dwarf.writeDbgAranges(0, code_section_size);
3225 // try dwarf.writeDbgLineHeader();
3226 // }
32093227
32103228 var debug_bytes = std.ArrayList(u8).init(wasm.base.allocator);
32113229 defer debug_bytes.deinit();
......@@ -3228,11 +3246,11 @@ fn writeToFile(
32283246
32293247 for (debug_sections) |item| {
32303248 if (item.index) |index| {
3231 var atom = wasm.atoms.get(index).?.getFirst();
3249 var atom = wasm.getAtomPtr(wasm.atoms.get(index).?);
32323250 while (true) {
32333251 atom.resolveRelocs(wasm);
32343252 try debug_bytes.appendSlice(atom.code.items);
3235 atom = atom.next orelse break;
3253 atom = if (atom.prev) |prev| wasm.getAtomPtr(prev) else break;
32363254 }
32373255 try emitDebugSection(&binary_bytes, debug_bytes.items, item.name);
32383256 debug_bytes.clearRetainingCapacity();
......@@ -3964,7 +3982,8 @@ fn emitSymbolTable(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), symbol_table:
39643982
39653983 if (symbol.isDefined()) {
39663984 try leb.writeULEB128(writer, symbol.index);
3967 const atom = wasm.symbol_atom.get(sym_loc).?;
3985 const atom_index = wasm.symbol_atom.get(sym_loc).?;
3986 const atom = wasm.getAtom(atom_index);
39683987 try leb.writeULEB128(writer, @as(u32, atom.offset));
39693988 try leb.writeULEB128(writer, @as(u32, atom.size));
39703989 }
......@@ -4042,7 +4061,7 @@ fn emitCodeRelocations(
40424061 const reloc_start = binary_bytes.items.len;
40434062
40444063 var count: u32 = 0;
4045 var atom: *Atom = wasm.atoms.get(code_index).?.getFirst();
4064 var atom: *Atom = wasm.getAtomPtr(wasm.atoms.get(code_index).?);
40464065 // for each atom, we calculate the uleb size and append that
40474066 var size_offset: u32 = 5; // account for code section size leb128
40484067 while (true) {
......@@ -4060,7 +4079,7 @@ fn emitCodeRelocations(
40604079 }
40614080 log.debug("Emit relocation: {}", .{relocation});
40624081 }
4063 atom = atom.next orelse break;
4082 atom = if (atom.prev) |prev| wasm.getAtomPtr(prev) else break;
40644083 }
40654084 if (count == 0) return;
40664085 var buf: [5]u8 = undefined;
......@@ -4091,7 +4110,7 @@ fn emitDataRelocations(
40914110 // for each atom, we calculate the uleb size and append that
40924111 var size_offset: u32 = 5; // account for code section size leb128
40934112 for (wasm.data_segments.values()) |segment_index| {
4094 var atom: *Atom = wasm.atoms.get(segment_index).?.getFirst();
4113 var atom: *Atom = wasm.getAtomPtr(wasm.atoms.get(segment_index).?);
40954114 while (true) {
40964115 size_offset += getULEB128Size(atom.size);
40974116 for (atom.relocs.items) |relocation| {
......@@ -4110,7 +4129,7 @@ fn emitDataRelocations(
41104129 }
41114130 log.debug("Emit relocation: {}", .{relocation});
41124131 }
4113 atom = atom.next orelse break;
4132 atom = if (atom.prev) |prev| wasm.getAtomPtr(prev) else break;
41144133 }
41154134 }
41164135 if (count == 0) return;
......@@ -4149,3 +4168,13 @@ pub fn putOrGetFuncType(wasm: *Wasm, func_type: std.wasm.Type) !u32 {
41494168 });
41504169 return index;
41514170}
4171
4172/// For the given `decl_index`, stores the corresponding type representing the function signature.
4173/// Asserts declaration has an associated `Atom`.
4174/// Returns the index into the list of types.
4175pub fn storeDeclType(wasm: *Wasm, decl_index: Module.Decl.Index, func_type: std.wasm.Type) !u32 {
4176 const atom_index = wasm.decls.get(decl_index).?;
4177 const index = try wasm.putOrGetFuncType(func_type);
4178 try wasm.atom_types.put(wasm.base.allocator, atom_index, index);
4179 return index;
4180}
src/link/Wasm/Atom.zig+19-28
......@@ -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,24 +79,11 @@ 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
98pub fn ensureInitialized(atom: *Atom, wasm_bin: *Wasm) !void {
99 if (atom.getSymbolIndex() != null) return; // already initialized
100 atom.sym_index = try wasm_bin.allocateSymbol();
101 try wasm_bin.symbol_atom.putNoClobber(wasm_bin.base.allocator, atom.symbolLoc(), atom);
102}
103
10487pub fn getSymbolIndex(atom: Atom) ?u32 {
10588 if (atom.sym_index == 0) return null;
10689 return atom.sym_index;
......@@ -203,20 +186,28 @@ fn relocationValue(atom: Atom, relocation: types.Relocation, wasm_bin: *const Wa
203186 if (symbol.isUndefined()) {
204187 return 0;
205188 }
206 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);
207196 const va = @intCast(i32, target_atom.getVA(wasm_bin, symbol));
208197 return @intCast(u32, va + relocation.addend);
209198 },
210199 .R_WASM_EVENT_INDEX_LEB => return symbol.index,
211200 .R_WASM_SECTION_OFFSET_I32 => {
212 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);
213203 const rel_value = @intCast(i32, target_atom.offset) + relocation.addend;
214204 return @intCast(u32, rel_value);
215205 },
216206 .R_WASM_FUNCTION_OFFSET_I32 => {
217 const target_atom = wasm_bin.symbol_atom.get(target_loc) orelse {
207 const target_atom_index = wasm_bin.symbol_atom.get(target_loc) orelse {
218208 return @bitCast(u32, @as(i32, -1));
219209 };
210 const target_atom = wasm_bin.getAtom(target_atom_index);
220211 const offset: u32 = 11 + Wasm.getULEB128Size(target_atom.size); // Header (11 bytes fixed-size) + body size (leb-encoded)
221212 const rel_value = @intCast(i32, target_atom.offset + offset) + relocation.addend;
222213 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+15-12
......@@ -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];
......@@ -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-585
......@@ -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,536 +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 => 4,
7055
7056 .aarch64_32,
7057 .amdgcn,
7058 .amdil64,
7059 .bpfel,
7060 .bpfeb,
7061 .hexagon,
7062 .hsail64,
7063 .loongarch64,
7064 .m68k,
7065 .mips,
7066 .mipsel,
7067 .sparc,
7068 .sparcel,
7069 .sparc64,
7070 .lanai,
7071 .le64,
7072 .nvptx,
7073 .nvptx64,
7074 .r600,
7075 .s390x,
7076 .spir64,
7077 .spirv64,
7078 .renderscript64,
7079 => 8,
7080
7081 .aarch64,
7082 .aarch64_be,
7083 .mips64,
7084 .mips64el,
7085 .powerpc,
7086 .powerpcle,
7087 .powerpc64,
7088 .powerpc64le,
7089 .riscv32,
7090 .riscv64,
7091 .x86_64,
7092 .wasm32,
7093 .wasm64,
7094 => 16,
7095 },
7096 );
7097 }
7098
7099 pub fn preferredAlignment(self: CType, target: Target) u16 {
7100
7101 // Overrides for unusual alignments
7102 switch (target.cpu.arch) {
7103 .arm, .armeb, .thumb, .thumbeb => switch (target.os.tag) {
7104 .netbsd => switch (target.abi) {
7105 .gnueabi,
7106 .gnueabihf,
7107 .eabi,
7108 .eabihf,
7109 .android,
7110 .musleabi,
7111 .musleabihf,
7112 => {},
7113
7114 else => switch (self) {
7115 .longdouble => return 4,
7116 else => {},
7117 },
7118 },
7119 .ios, .tvos, .watchos => switch (self) {
7120 .longdouble => return 4,
7121 else => {},
7122 },
7123 else => {},
7124 },
7125 .arc => switch (self) {
7126 .longdouble => return 4,
7127 else => {},
7128 },
7129 .avr => switch (self) {
7130 .int, .uint, .long, .ulong, .float, .longdouble => return 1,
7131 .short, .ushort => return 2,
7132 .double => return 4,
7133 .longlong, .ulonglong => return 8,
7134 },
7135 .x86 => switch (target.os.tag) {
7136 .windows, .uefi => switch (self) {
7137 .longdouble => switch (target.abi) {
7138 .gnu, .gnuilp32, .cygnus => return 4,
7139 else => return 8,
7140 },
7141 else => {},
7142 },
7143 else => switch (self) {
7144 .longdouble => return 4,
7145 else => {},
7146 },
7147 },
7148 else => {},
7149 }
7150
7151 // Next-power-of-two-aligned, up to a maximum.
7152 return @min(
7153 std.math.ceilPowerOfTwoAssert(u16, (self.sizeInBits(target) + 7) / 8),
7154 switch (target.cpu.arch) {
7155 .msp430 => @as(u16, 2),
7156
7157 .csky,
7158 .xcore,
7159 .dxil,
7160 .loongarch32,
7161 .tce,
7162 .tcele,
7163 .le32,
7164 .amdil,
7165 .hsail,
7166 .spir,
7167 .spirv32,
7168 .kalimba,
7169 .shave,
7170 .renderscript32,
7171 .ve,
7172 .spu_2,
7173 => 4,
7174
7175 .arc,
7176 .arm,
7177 .armeb,
7178 .avr,
7179 .thumb,
7180 .thumbeb,
7181 .aarch64_32,
7182 .amdgcn,
7183 .amdil64,
7184 .bpfel,
7185 .bpfeb,
7186 .hexagon,
7187 .hsail64,
7188 .x86,
7189 .loongarch64,
7190 .m68k,
7191 .mips,
7192 .mipsel,
7193 .sparc,
7194 .sparcel,
7195 .sparc64,
7196 .lanai,
7197 .le64,
7198 .nvptx,
7199 .nvptx64,
7200 .r600,
7201 .s390x,
7202 .spir64,
7203 .spirv64,
7204 .renderscript64,
7205 => 8,
7206
7207 .aarch64,
7208 .aarch64_be,
7209 .mips64,
7210 .mips64el,
7211 .powerpc,
7212 .powerpcle,
7213 .powerpc64,
7214 .powerpc64le,
7215 .riscv32,
7216 .riscv64,
7217 .x86_64,
7218 .wasm32,
7219 .wasm64,
7220 => 16,
7221 },
7222 );
7223 }
7224};
stage1/zig1.wasm
Binary files a/stage1/zig1.wasm and b/stage1/zig1.wasm differ
test/behavior/basic.zig+18
......@@ -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/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/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/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/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.zig+1
......@@ -102,4 +102,5 @@ pub fn addCases(cases: *tests.StandaloneContext) void {
102102 cases.addBuildFile("test/standalone/issue_13030/build.zig", .{ .build_modes = true });
103103 cases.addBuildFile("test/standalone/emit_asm_and_bin/build.zig", .{});
104104 cases.addBuildFile("test/standalone/issue_12588/build.zig", .{});
105 cases.addBuildFile("test/standalone/embed_generated_file/build.zig", .{});
105106}
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/embed_generated_file/bootloader.zig created+1
......@@ -0,0 +1 @@
1pub export fn _start() void {}
test/standalone/embed_generated_file/build.zig created+28
......@@ -0,0 +1,28 @@
1const std = @import("std");
2
3pub fn build(b: *std.Build) void {
4 const target = b.standardTargetOptions(.{});
5 const optimize = b.standardOptimizeOption(.{});
6
7 const bootloader = b.addExecutable(.{
8 .name = "bootloader",
9 .root_source_file = .{ .path = "bootloader.zig" },
10 .target = .{
11 .cpu_arch = .x86,
12 .os_tag = .freestanding,
13 },
14 .optimize = .ReleaseSmall,
15 });
16
17 const exe = b.addTest(.{
18 .root_source_file = .{ .path = "main.zig" },
19 .target = target,
20 .optimize = optimize,
21 });
22 exe.addAnonymousModule("bootloader.elf", .{
23 .source_file = bootloader.getOutputSource(),
24 });
25
26 const test_step = b.step("test", "Test the program");
27 test_step.dependOn(&exe.step);
28}
test/standalone/embed_generated_file/main.zig created+8
......@@ -0,0 +1,8 @@
1const std = @import("std");
2const blah = @embedFile("bootloader.elf");
3
4test {
5 comptime {
6 std.debug.assert(std.mem.eql(u8, blah[1..][0..3], "ELF"));
7 }
8}
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.addAnonymousModule("my_pkg", .{ .source_file = .{ .path = "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}
tools/generate_linux_syscalls.zig+25
......@@ -167,6 +167,31 @@ pub fn main() !void {
167167
168168 try writer.writeAll("};\n\n");
169169 }
170 {
171 try writer.writeAll(
172 \\pub const Mips64 = enum(usize) {
173 \\ pub const Linux = 5000;
174 \\
175 \\
176 );
177
178 const table = try linux_dir.readFile("arch/mips/kernel/syscalls/syscall_n64.tbl", buf);
179 var lines = mem.tokenize(u8, table, "\n");
180 while (lines.next()) |line| {
181 if (line[0] == '#') continue;
182
183 var fields = mem.tokenize(u8, line, " \t");
184 const number = fields.next() orelse return error.Incomplete;
185 // abi is always n64
186 _ = fields.next() orelse return error.Incomplete;
187 const name = fields.next() orelse return error.Incomplete;
188 const fixed_name = if (stdlib_renames.get(name)) |fixed| fixed else name;
189
190 try writer.print(" {s} = Linux + {s},\n", .{ zig.fmtId(fixed_name), number });
191 }
192
193 try writer.writeAll("};\n\n");
194 }
170195 {
171196 try writer.writeAll("pub const PowerPC = enum(usize) {\n");
172197
tools/update_cpu_features.zig+1-1
......@@ -1306,7 +1306,7 @@ fn usageAndExit(file: fs.File, arg0: []const u8, code: u8) noreturn {
13061306 \\
13071307 \\Updates lib/std/target/<target>.zig from llvm/lib/Target/<Target>/<Target>.td .
13081308 \\
1309 \\On a less beefy system, or when debugging, compile with --single-threaded.
1309 \\On a less beefy system, or when debugging, compile with -fsingle-threaded.
13101310 \\
13111311 , .{arg0}) catch std.process.exit(1);
13121312 std.process.exit(code);