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

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


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

.github/workflows/ci.yaml+1
...@@ -19,6 +19,7 @@ jobs:...@@ -19,6 +19,7 @@ jobs:
19 - name: Build and Test19 - name: Build and Test
20 run: sh ci/x86_64-linux-debug.sh20 run: sh ci/x86_64-linux-debug.sh
21 x86_64-linux-release:21 x86_64-linux-release:
22 timeout-minutes: 420
22 runs-on: [self-hosted, Linux, x86_64]23 runs-on: [self-hosted, Linux, x86_64]
23 steps:24 steps:
24 - name: Checkout25 - name: Checkout
CMakeLists.txt+10-37
...@@ -513,7 +513,7 @@ set(ZIG_STAGE2_SOURCES...@@ -513,7 +513,7 @@ set(ZIG_STAGE2_SOURCES
513 "${CMAKE_SOURCE_DIR}/lib/std/zig/Ast.zig"513 "${CMAKE_SOURCE_DIR}/lib/std/zig/Ast.zig"
514 "${CMAKE_SOURCE_DIR}/lib/std/zig/CrossTarget.zig"514 "${CMAKE_SOURCE_DIR}/lib/std/zig/CrossTarget.zig"
515 "${CMAKE_SOURCE_DIR}/lib/std/zig/c_builtins.zig"515 "${CMAKE_SOURCE_DIR}/lib/std/zig/c_builtins.zig"
516 "${CMAKE_SOURCE_DIR}/lib/std/zig/parse.zig"516 "${CMAKE_SOURCE_DIR}/lib/std/zig/Parse.zig"
517 "${CMAKE_SOURCE_DIR}/lib/std/zig/render.zig"517 "${CMAKE_SOURCE_DIR}/lib/std/zig/render.zig"
518 "${CMAKE_SOURCE_DIR}/lib/std/zig/string_literal.zig"518 "${CMAKE_SOURCE_DIR}/lib/std/zig/string_literal.zig"
519 "${CMAKE_SOURCE_DIR}/lib/std/zig/system.zig"519 "${CMAKE_SOURCE_DIR}/lib/std/zig/system.zig"
...@@ -654,46 +654,19 @@ include_directories(...@@ -654,46 +654,19 @@ include_directories(
654 "${CMAKE_SOURCE_DIR}/src"654 "${CMAKE_SOURCE_DIR}/src"
655)655)
656656
657# These have to go before the -Wno- flags
658if(MSVC)657if(MSVC)
659 set(EXE_CXX_FLAGS "/std:c++17")658 set(EXE_CXX_FLAGS "/std:c++17")
660else(MSVC)659 set(EXE_LDFLAGS "/STACK:16777216")
661 set(EXE_CXX_FLAGS "-std=c++17")660 if(NOT "${CMAKE_BUILD_TYPE}" STREQUAL "Release" AND NOT "${CMAKE_BUILD_TYPE}" STREQUAL "MinSizeRel")
662endif(MSVC)661 set(EXE_LDFLAGS "${EXE_LDFLAGS} /debug:fastlink")
663662 endif()
664if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
665 if(MSVC)
666 set(EXE_CXX_FLAGS "${EXE_CXX_FLAGS} /w")
667 else()
668 set(EXE_CXX_FLAGS "${EXE_CXX_FLAGS} -Werror -Wall")
669 # fallthrough support was added in GCC 7.0
670 if(NOT CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 7.0)
671 set(EXE_CXX_FLAGS "${EXE_CXX_FLAGS} -Werror=implicit-fallthrough")
672 endif()
673 # GCC 9.2 and older are unable to detect valid variable initialization in some cases
674 if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS_EQUAL 9.2)
675 set(EXE_CXX_FLAGS "${EXE_CXX_FLAGS} -Wno-maybe-uninitialized")
676 endif()
677 endif()
678endif()
679
680if(MSVC)
681 set(EXE_CXX_FLAGS "${EXE_CXX_FLAGS}")
682else()663else()
683 set(EXE_CXX_FLAGS "${EXE_CXX_FLAGS} -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -D_GNU_SOURCE -fvisibility-inlines-hidden -fno-exceptions -fno-rtti -Werror=type-limits -Wno-missing-braces -Wno-comment")664 set(EXE_CXX_FLAGS "-std=c++17 -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -D_GNU_SOURCE -fvisibility-inlines-hidden -fno-exceptions -fno-rtti -Werror=type-limits -Wno-missing-braces -Wno-comment")
684 if(MINGW)665 set(EXE_LDFLAGS " ")
685 set(EXE_CXX_FLAGS "${EXE_CXX_FLAGS} -Wno-format")666 if(MINGW)
686 endif()667 set(EXE_CXX_FLAGS "${EXE_CXX_FLAGS} -Wno-format")
687endif()
688
689set(EXE_LDFLAGS " ")
690if(MSVC)
691 set(EXE_LDFLAGS "${EXE_LDFLAGS} /STACK:16777216")
692 if(NOT "${CMAKE_BUILD_TYPE}" STREQUAL "Release" AND NOT "${CMAKE_BUILD_TYPE}" STREQUAL "MinSizeRel")
693 set(EXE_LDFLAGS "${EXE_LDFLAGS} /debug:fastlink")
694 endif()
695elseif(MINGW)
696 set(EXE_LDFLAGS "${EXE_LDFLAGS} -Wl,--stack,16777216")668 set(EXE_LDFLAGS "${EXE_LDFLAGS} -Wl,--stack,16777216")
669 endif()
697endif()670endif()
698671
699if(ZIG_STATIC)672if(ZIG_STATIC)
build.zig+55-204
...@@ -1,19 +1,18 @@...@@ -1,19 +1,18 @@
1const std = @import("std");1const std = @import("std");
2const builtin = std.builtin;2const builtin = std.builtin;
3const Builder = std.build.Builder;
4const tests = @import("test/tests.zig");3const tests = @import("test/tests.zig");
5const BufMap = std.BufMap;4const BufMap = std.BufMap;
6const mem = std.mem;5const mem = std.mem;
7const ArrayList = std.ArrayList;6const ArrayList = std.ArrayList;
8const io = std.io;7const io = std.io;
9const fs = std.fs;8const fs = std.fs;
10const InstallDirectoryOptions = std.build.InstallDirectoryOptions;9const InstallDirectoryOptions = std.Build.InstallDirectoryOptions;
11const assert = std.debug.assert;10const assert = std.debug.assert;
1211
13const zig_version = std.builtin.Version{ .major = 0, .minor = 11, .patch = 0 };12const zig_version = std.builtin.Version{ .major = 0, .minor = 11, .patch = 0 };
14const stack_size = 32 * 1024 * 1024;13const stack_size = 32 * 1024 * 1024;
1514
16pub fn build(b: *Builder) !void {15pub fn build(b: *std.Build) !void {
17 const release = b.option(bool, "release", "Build in release mode") orelse false;16 const release = b.option(bool, "release", "Build in release mode") orelse false;
18 const only_c = b.option(bool, "only-c", "Translate the Zig compiler to C code, with only the C backend enabled") orelse false;17 const only_c = b.option(bool, "only-c", "Translate the Zig compiler to C code, with only the C backend enabled") orelse false;
19 const target = t: {18 const target = t: {
...@@ -23,7 +22,7 @@ pub fn build(b: *Builder) !void {...@@ -23,7 +22,7 @@ pub fn build(b: *Builder) !void {
23 }22 }
24 break :t b.standardTargetOptions(.{ .default_target = default_target });23 break :t b.standardTargetOptions(.{ .default_target = default_target });
25 };24 };
26 const mode: std.builtin.Mode = if (release) switch (target.getCpuArch()) {25 const optimize: std.builtin.OptimizeMode = if (release) switch (target.getCpuArch()) {
27 .wasm32 => .ReleaseSmall,26 .wasm32 => .ReleaseSmall,
28 else => .ReleaseFast,27 else => .ReleaseFast,
29 } else .Debug;28 } else .Debug;
...@@ -33,7 +32,12 @@ pub fn build(b: *Builder) !void {...@@ -33,7 +32,12 @@ pub fn build(b: *Builder) !void {
3332
34 const test_step = b.step("test", "Run all the tests");33 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 });
37 docgen_exe.single_threaded = single_threaded;41 docgen_exe.single_threaded = single_threaded;
3842
39 const rel_zig_exe = try fs.path.relative(b.allocator, b.build_root, b.zig_exe);43 const rel_zig_exe = try fs.path.relative(b.allocator, b.build_root, b.zig_exe);
...@@ -53,10 +57,12 @@ pub fn build(b: *Builder) !void {...@@ -53,10 +57,12 @@ pub fn build(b: *Builder) !void {
53 const docs_step = b.step("docs", "Build documentation");57 const docs_step = b.step("docs", "Build documentation");
54 docs_step.dependOn(&docgen_cmd.step);58 docs_step.dependOn(&docgen_cmd.step);
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 });
57 test_cases.main_pkg_path = ".";64 test_cases.main_pkg_path = ".";
58 test_cases.stack_size = stack_size;65 test_cases.stack_size = stack_size;
59 test_cases.setBuildMode(mode);
60 test_cases.single_threaded = single_threaded;66 test_cases.single_threaded = single_threaded;
6167
62 const fmt_build_zig = b.addFmt(&[_][]const u8{"build.zig"});68 const fmt_build_zig = b.addFmt(&[_][]const u8{"build.zig"});
...@@ -154,17 +160,15 @@ pub fn build(b: *Builder) !void {...@@ -154,17 +160,15 @@ pub fn build(b: *Builder) !void {
154160
155 const mem_leak_frames: u32 = b.option(u32, "mem-leak-frames", "How many stack frames to print when a memory leak occurs. Tests get 2x this amount.") orelse blk: {161 const mem_leak_frames: u32 = b.option(u32, "mem-leak-frames", "How many stack frames to print when a memory leak occurs. Tests get 2x this amount.") orelse blk: {
156 if (strip == true) break :blk @as(u32, 0);162 if (strip == true) break :blk @as(u32, 0);
157 if (mode != .Debug) break :blk 0;163 if (optimize != .Debug) break :blk 0;
158 break :blk 4;164 break :blk 4;
159 };165 };
160166
161 const exe = addCompilerStep(b);167 const exe = addCompilerStep(b, optimize, target);
162 exe.strip = strip;168 exe.strip = strip;
163 exe.sanitize_thread = sanitize_thread;169 exe.sanitize_thread = sanitize_thread;
164 exe.build_id = b.option(bool, "build-id", "Include a build id note") orelse false;170 exe.build_id = b.option(bool, "build-id", "Include a build id note") orelse false;
165 exe.install();171 exe.install();
166 exe.setBuildMode(mode);
167 exe.setTarget(target);
168172
169 const compile_step = b.step("compile", "Build the self-hosted compiler");173 const compile_step = b.step("compile", "Build the self-hosted compiler");
170 compile_step.dependOn(&exe.step);174 compile_step.dependOn(&exe.step);
...@@ -201,7 +205,7 @@ pub fn build(b: *Builder) !void {...@@ -201,7 +205,7 @@ pub fn build(b: *Builder) !void {
201 test_cases.linkLibC();205 test_cases.linkLibC();
202 }206 }
203207
204 const is_debug = mode == .Debug;208 const is_debug = optimize == .Debug;
205 const enable_logging = b.option(bool, "log", "Enable debug logging with --debug-log") orelse is_debug;209 const enable_logging = b.option(bool, "log", "Enable debug logging with --debug-log") orelse is_debug;
206 const enable_link_snapshots = b.option(bool, "link-snapshot", "Whether to enable linker state snapshots") orelse false;210 const enable_link_snapshots = b.option(bool, "link-snapshot", "Whether to enable linker state snapshots") orelse false;
207211
...@@ -367,25 +371,25 @@ pub fn build(b: *Builder) !void {...@@ -367,25 +371,25 @@ pub fn build(b: *Builder) !void {
367 test_step.dependOn(test_cases_step);371 test_step.dependOn(test_cases_step);
368 }372 }
369373
370 var chosen_modes: [4]builtin.Mode = undefined;374 var chosen_opt_modes_buf: [4]builtin.Mode = undefined;
371 var chosen_mode_index: usize = 0;375 var chosen_mode_index: usize = 0;
372 if (!skip_debug) {376 if (!skip_debug) {
373 chosen_modes[chosen_mode_index] = builtin.Mode.Debug;377 chosen_opt_modes_buf[chosen_mode_index] = builtin.Mode.Debug;
374 chosen_mode_index += 1;378 chosen_mode_index += 1;
375 }379 }
376 if (!skip_release_safe) {380 if (!skip_release_safe) {
377 chosen_modes[chosen_mode_index] = builtin.Mode.ReleaseSafe;381 chosen_opt_modes_buf[chosen_mode_index] = builtin.Mode.ReleaseSafe;
378 chosen_mode_index += 1;382 chosen_mode_index += 1;
379 }383 }
380 if (!skip_release_fast) {384 if (!skip_release_fast) {
381 chosen_modes[chosen_mode_index] = builtin.Mode.ReleaseFast;385 chosen_opt_modes_buf[chosen_mode_index] = builtin.Mode.ReleaseFast;
382 chosen_mode_index += 1;386 chosen_mode_index += 1;
383 }387 }
384 if (!skip_release_small) {388 if (!skip_release_small) {
385 chosen_modes[chosen_mode_index] = builtin.Mode.ReleaseSmall;389 chosen_opt_modes_buf[chosen_mode_index] = builtin.Mode.ReleaseSmall;
386 chosen_mode_index += 1;390 chosen_mode_index += 1;
387 }391 }
388 const modes = chosen_modes[0..chosen_mode_index];392 const optimization_modes = chosen_opt_modes_buf[0..chosen_mode_index];
389393
390 // run stage1 `zig fmt` on this build.zig file just to make sure it works394 // run stage1 `zig fmt` on this build.zig file just to make sure it works
391 test_step.dependOn(&fmt_build_zig.step);395 test_step.dependOn(&fmt_build_zig.step);
...@@ -398,7 +402,7 @@ pub fn build(b: *Builder) !void {...@@ -398,7 +402,7 @@ pub fn build(b: *Builder) !void {
398 "test/behavior.zig",402 "test/behavior.zig",
399 "behavior",403 "behavior",
400 "Run the behavior tests",404 "Run the behavior tests",
401 modes,405 optimization_modes,
402 skip_single_threaded,406 skip_single_threaded,
403 skip_non_native,407 skip_non_native,
404 skip_libc,408 skip_libc,
...@@ -412,7 +416,7 @@ pub fn build(b: *Builder) !void {...@@ -412,7 +416,7 @@ pub fn build(b: *Builder) !void {
412 "lib/compiler_rt.zig",416 "lib/compiler_rt.zig",
413 "compiler-rt",417 "compiler-rt",
414 "Run the compiler_rt tests",418 "Run the compiler_rt tests",
415 modes,419 optimization_modes,
416 true, // skip_single_threaded420 true, // skip_single_threaded
417 skip_non_native,421 skip_non_native,
418 true, // skip_libc422 true, // skip_libc
...@@ -426,7 +430,7 @@ pub fn build(b: *Builder) !void {...@@ -426,7 +430,7 @@ pub fn build(b: *Builder) !void {
426 "lib/c.zig",430 "lib/c.zig",
427 "universal-libc",431 "universal-libc",
428 "Run the universal libc tests",432 "Run the universal libc tests",
429 modes,433 optimization_modes,
430 true, // skip_single_threaded434 true, // skip_single_threaded
431 skip_non_native,435 skip_non_native,
432 true, // skip_libc436 true, // skip_libc
...@@ -434,11 +438,11 @@ pub fn build(b: *Builder) !void {...@@ -434,11 +438,11 @@ pub fn build(b: *Builder) !void {
434 skip_stage2_tests or true, // TODO get these all passing438 skip_stage2_tests or true, // TODO get these all passing
435 ));439 ));
436440
437 test_step.dependOn(tests.addCompareOutputTests(b, test_filter, modes));441 test_step.dependOn(tests.addCompareOutputTests(b, test_filter, optimization_modes));
438 test_step.dependOn(tests.addStandaloneTests(442 test_step.dependOn(tests.addStandaloneTests(
439 b,443 b,
440 test_filter,444 test_filter,
441 modes,445 optimization_modes,
442 skip_non_native,446 skip_non_native,
443 enable_macos_sdk,447 enable_macos_sdk,
444 target,448 target,
...@@ -451,10 +455,10 @@ pub fn build(b: *Builder) !void {...@@ -451,10 +455,10 @@ pub fn build(b: *Builder) !void {
451 enable_symlinks_windows,455 enable_symlinks_windows,
452 ));456 ));
453 test_step.dependOn(tests.addCAbiTests(b, skip_non_native, skip_release));457 test_step.dependOn(tests.addCAbiTests(b, skip_non_native, skip_release));
454 test_step.dependOn(tests.addLinkTests(b, test_filter, modes, enable_macos_sdk, skip_stage2_tests, enable_symlinks_windows));458 test_step.dependOn(tests.addLinkTests(b, test_filter, optimization_modes, enable_macos_sdk, skip_stage2_tests, enable_symlinks_windows));
455 test_step.dependOn(tests.addStackTraceTests(b, test_filter, modes));459 test_step.dependOn(tests.addStackTraceTests(b, test_filter, optimization_modes));
456 test_step.dependOn(tests.addCliTests(b, test_filter, modes));460 test_step.dependOn(tests.addCliTests(b, test_filter, optimization_modes));
457 test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, modes));461 test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, optimization_modes));
458 test_step.dependOn(tests.addTranslateCTests(b, test_filter));462 test_step.dependOn(tests.addTranslateCTests(b, test_filter));
459 if (!skip_run_translated_c) {463 if (!skip_run_translated_c) {
460 test_step.dependOn(tests.addRunTranslatedCTests(b, test_filter, target));464 test_step.dependOn(tests.addRunTranslatedCTests(b, test_filter, target));
...@@ -468,7 +472,7 @@ pub fn build(b: *Builder) !void {...@@ -468,7 +472,7 @@ pub fn build(b: *Builder) !void {
468 "lib/std/std.zig",472 "lib/std/std.zig",
469 "std",473 "std",
470 "Run the standard library tests",474 "Run the standard library tests",
471 modes,475 optimization_modes,
472 skip_single_threaded,476 skip_single_threaded,
473 skip_non_native,477 skip_non_native,
474 skip_libc,478 skip_libc,
...@@ -479,7 +483,7 @@ pub fn build(b: *Builder) !void {...@@ -479,7 +483,7 @@ pub fn build(b: *Builder) !void {
479 try addWasiUpdateStep(b, version);483 try addWasiUpdateStep(b, version);
480}484}
481485
482fn addWasiUpdateStep(b: *Builder, version: [:0]const u8) !void {486fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void {
483 const semver = try std.SemanticVersion.parse(version);487 const semver = try std.SemanticVersion.parse(version);
484488
485 var target: std.zig.CrossTarget = .{489 var target: std.zig.CrossTarget = .{
...@@ -488,9 +492,7 @@ fn addWasiUpdateStep(b: *Builder, version: [:0]const u8) !void {...@@ -488,9 +492,7 @@ fn addWasiUpdateStep(b: *Builder, version: [:0]const u8) !void {
488 };492 };
489 target.cpu_features_add.addFeature(@enumToInt(std.Target.wasm.Feature.bulk_memory));493 target.cpu_features_add.addFeature(@enumToInt(std.Target.wasm.Feature.bulk_memory));
490494
491 const exe = addCompilerStep(b);495 const exe = addCompilerStep(b, .ReleaseSmall, target);
492 exe.setBuildMode(.ReleaseSmall);
493 exe.setTarget(target);
494496
495 const exe_options = b.addOptions();497 const exe_options = b.addOptions();
496 exe.addOptions("build_options", exe_options);498 exe.addOptions("build_options", exe_options);
...@@ -517,8 +519,17 @@ fn addWasiUpdateStep(b: *Builder, version: [:0]const u8) !void {...@@ -517,8 +519,17 @@ fn addWasiUpdateStep(b: *Builder, version: [:0]const u8) !void {
517 update_zig1_step.dependOn(&run_opt.step);519 update_zig1_step.dependOn(&run_opt.step);
518}520}
519521
520fn addCompilerStep(b: *Builder) *std.build.LibExeObjStep {522fn addCompilerStep(
521 const exe = b.addExecutable("zig", "src/main.zig");523 b: *std.Build,
524 optimize: std.builtin.OptimizeMode,
525 target: std.zig.CrossTarget,
526) *std.Build.CompileStep {
527 const exe = b.addExecutable(.{
528 .name = "zig",
529 .root_source_file = .{ .path = "src/main.zig" },
530 .target = target,
531 .optimize = optimize,
532 });
522 exe.stack_size = stack_size;533 exe.stack_size = stack_size;
523 return exe;534 return exe;
524}535}
...@@ -538,9 +549,9 @@ const exe_cflags = [_][]const u8{...@@ -538,9 +549,9 @@ const exe_cflags = [_][]const u8{
538};549};
539550
540fn addCmakeCfgOptionsToExe(551fn addCmakeCfgOptionsToExe(
541 b: *Builder,552 b: *std.Build,
542 cfg: CMakeConfig,553 cfg: CMakeConfig,
543 exe: *std.build.LibExeObjStep,554 exe: *std.Build.CompileStep,
544 use_zig_libcxx: bool,555 use_zig_libcxx: bool,
545) !void {556) !void {
546 if (exe.target.isDarwin()) {557 if (exe.target.isDarwin()) {
...@@ -619,7 +630,7 @@ fn addCmakeCfgOptionsToExe(...@@ -619,7 +630,7 @@ fn addCmakeCfgOptionsToExe(
619 }630 }
620}631}
621632
622fn addStaticLlvmOptionsToExe(exe: *std.build.LibExeObjStep) !void {633fn addStaticLlvmOptionsToExe(exe: *std.Build.CompileStep) !void {
623 // Adds the Zig C++ sources which both stage1 and stage2 need.634 // Adds the Zig C++ sources which both stage1 and stage2 need.
624 //635 //
625 // We need this because otherwise zig_clang_cc1_main.cpp ends up pulling636 // We need this because otherwise zig_clang_cc1_main.cpp ends up pulling
...@@ -656,9 +667,9 @@ fn addStaticLlvmOptionsToExe(exe: *std.build.LibExeObjStep) !void {...@@ -656,9 +667,9 @@ fn addStaticLlvmOptionsToExe(exe: *std.build.LibExeObjStep) !void {
656}667}
657668
658fn addCxxKnownPath(669fn addCxxKnownPath(
659 b: *Builder,670 b: *std.Build,
660 ctx: CMakeConfig,671 ctx: CMakeConfig,
661 exe: *std.build.LibExeObjStep,672 exe: *std.Build.CompileStep,
662 objname: []const u8,673 objname: []const u8,
663 errtxt: ?[]const u8,674 errtxt: ?[]const u8,
664 need_cpp_includes: bool,675 need_cpp_includes: bool,
...@@ -691,7 +702,7 @@ fn addCxxKnownPath(...@@ -691,7 +702,7 @@ fn addCxxKnownPath(
691 }702 }
692}703}
693704
694fn addCMakeLibraryList(exe: *std.build.LibExeObjStep, list: []const u8) void {705fn addCMakeLibraryList(exe: *std.Build.CompileStep, list: []const u8) void {
695 var it = mem.tokenize(u8, list, ";");706 var it = mem.tokenize(u8, list, ";");
696 while (it.next()) |lib| {707 while (it.next()) |lib| {
697 if (mem.startsWith(u8, lib, "-l")) {708 if (mem.startsWith(u8, lib, "-l")) {
...@@ -705,7 +716,7 @@ fn addCMakeLibraryList(exe: *std.build.LibExeObjStep, list: []const u8) void {...@@ -705,7 +716,7 @@ fn addCMakeLibraryList(exe: *std.build.LibExeObjStep, list: []const u8) void {
705}716}
706717
707const CMakeConfig = struct {718const CMakeConfig = struct {
708 llvm_linkage: std.build.LibExeObjStep.Linkage,719 llvm_linkage: std.Build.CompileStep.Linkage,
709 cmake_binary_dir: []const u8,720 cmake_binary_dir: []const u8,
710 cmake_prefix_path: []const u8,721 cmake_prefix_path: []const u8,
711 cmake_static_library_prefix: []const u8,722 cmake_static_library_prefix: []const u8,
...@@ -722,7 +733,7 @@ const CMakeConfig = struct {...@@ -722,7 +733,7 @@ const CMakeConfig = struct {
722733
723const max_config_h_bytes = 1 * 1024 * 1024;734const max_config_h_bytes = 1 * 1024 * 1024;
724735
725fn findConfigH(b: *Builder, config_h_path_option: ?[]const u8) ?[]const u8 {736fn findConfigH(b: *std.Build, config_h_path_option: ?[]const u8) ?[]const u8 {
726 if (config_h_path_option) |path| {737 if (config_h_path_option) |path| {
727 var config_h_or_err = fs.cwd().openFile(path, .{});738 var config_h_or_err = fs.cwd().openFile(path, .{});
728 if (config_h_or_err) |*file| {739 if (config_h_or_err) |*file| {
...@@ -768,7 +779,7 @@ fn findConfigH(b: *Builder, config_h_path_option: ?[]const u8) ?[]const u8 {...@@ -768,7 +779,7 @@ fn findConfigH(b: *Builder, config_h_path_option: ?[]const u8) ?[]const u8 {
768 } else unreachable; // TODO should not need `else unreachable`.779 } else unreachable; // TODO should not need `else unreachable`.
769}780}
770781
771fn parseConfigH(b: *Builder, config_h_text: []const u8) ?CMakeConfig {782fn parseConfigH(b: *std.Build, config_h_text: []const u8) ?CMakeConfig {
772 var ctx: CMakeConfig = .{783 var ctx: CMakeConfig = .{
773 .llvm_linkage = undefined,784 .llvm_linkage = undefined,
774 .cmake_binary_dir = undefined,785 .cmake_binary_dir = undefined,
...@@ -857,7 +868,7 @@ fn parseConfigH(b: *Builder, config_h_text: []const u8) ?CMakeConfig {...@@ -857,7 +868,7 @@ fn parseConfigH(b: *Builder, config_h_text: []const u8) ?CMakeConfig {
857 return ctx;868 return ctx;
858}869}
859870
860fn toNativePathSep(b: *Builder, s: []const u8) []u8 {871fn toNativePathSep(b: *std.Build, s: []const u8) []u8 {
861 const duplicated = b.allocator.dupe(u8, s) catch unreachable;872 const duplicated = b.allocator.dupe(u8, s) catch unreachable;
862 for (duplicated) |*byte| switch (byte.*) {873 for (duplicated) |*byte| switch (byte.*) {
863 '/' => byte.* = fs.path.sep,874 '/' => byte.* = fs.path.sep,
...@@ -866,166 +877,6 @@ fn toNativePathSep(b: *Builder, s: []const u8) []u8 {...@@ -866,166 +877,6 @@ fn toNativePathSep(b: *Builder, s: []const u8) []u8 {
866 return duplicated;877 return duplicated;
867}878}
868879
869const softfloat_sources = [_][]const u8{
870 "deps/SoftFloat-3e/source/8086/f128M_isSignalingNaN.c",
871 "deps/SoftFloat-3e/source/8086/extF80M_isSignalingNaN.c",
872 "deps/SoftFloat-3e/source/8086/s_commonNaNToF128M.c",
873 "deps/SoftFloat-3e/source/8086/s_commonNaNToExtF80M.c",
874 "deps/SoftFloat-3e/source/8086/s_commonNaNToF16UI.c",
875 "deps/SoftFloat-3e/source/8086/s_commonNaNToF32UI.c",
876 "deps/SoftFloat-3e/source/8086/s_commonNaNToF64UI.c",
877 "deps/SoftFloat-3e/source/8086/s_f128MToCommonNaN.c",
878 "deps/SoftFloat-3e/source/8086/s_extF80MToCommonNaN.c",
879 "deps/SoftFloat-3e/source/8086/s_f16UIToCommonNaN.c",
880 "deps/SoftFloat-3e/source/8086/s_f32UIToCommonNaN.c",
881 "deps/SoftFloat-3e/source/8086/s_f64UIToCommonNaN.c",
882 "deps/SoftFloat-3e/source/8086/s_propagateNaNF128M.c",
883 "deps/SoftFloat-3e/source/8086/s_propagateNaNExtF80M.c",
884 "deps/SoftFloat-3e/source/8086/s_propagateNaNF16UI.c",
885 "deps/SoftFloat-3e/source/8086/softfloat_raiseFlags.c",
886 "deps/SoftFloat-3e/source/f128M_add.c",
887 "deps/SoftFloat-3e/source/f128M_div.c",
888 "deps/SoftFloat-3e/source/f128M_eq.c",
889 "deps/SoftFloat-3e/source/f128M_eq_signaling.c",
890 "deps/SoftFloat-3e/source/f128M_le.c",
891 "deps/SoftFloat-3e/source/f128M_le_quiet.c",
892 "deps/SoftFloat-3e/source/f128M_lt.c",
893 "deps/SoftFloat-3e/source/f128M_lt_quiet.c",
894 "deps/SoftFloat-3e/source/f128M_mul.c",
895 "deps/SoftFloat-3e/source/f128M_mulAdd.c",
896 "deps/SoftFloat-3e/source/f128M_rem.c",
897 "deps/SoftFloat-3e/source/f128M_roundToInt.c",
898 "deps/SoftFloat-3e/source/f128M_sqrt.c",
899 "deps/SoftFloat-3e/source/f128M_sub.c",
900 "deps/SoftFloat-3e/source/f128M_to_f16.c",
901 "deps/SoftFloat-3e/source/f128M_to_f32.c",
902 "deps/SoftFloat-3e/source/f128M_to_f64.c",
903 "deps/SoftFloat-3e/source/f128M_to_extF80M.c",
904 "deps/SoftFloat-3e/source/f128M_to_i32.c",
905 "deps/SoftFloat-3e/source/f128M_to_i32_r_minMag.c",
906 "deps/SoftFloat-3e/source/f128M_to_i64.c",
907 "deps/SoftFloat-3e/source/f128M_to_i64_r_minMag.c",
908 "deps/SoftFloat-3e/source/f128M_to_ui32.c",
909 "deps/SoftFloat-3e/source/f128M_to_ui32_r_minMag.c",
910 "deps/SoftFloat-3e/source/f128M_to_ui64.c",
911 "deps/SoftFloat-3e/source/f128M_to_ui64_r_minMag.c",
912 "deps/SoftFloat-3e/source/extF80M_add.c",
913 "deps/SoftFloat-3e/source/extF80M_div.c",
914 "deps/SoftFloat-3e/source/extF80M_eq.c",
915 "deps/SoftFloat-3e/source/extF80M_le.c",
916 "deps/SoftFloat-3e/source/extF80M_lt.c",
917 "deps/SoftFloat-3e/source/extF80M_mul.c",
918 "deps/SoftFloat-3e/source/extF80M_rem.c",
919 "deps/SoftFloat-3e/source/extF80M_roundToInt.c",
920 "deps/SoftFloat-3e/source/extF80M_sqrt.c",
921 "deps/SoftFloat-3e/source/extF80M_sub.c",
922 "deps/SoftFloat-3e/source/extF80M_to_f16.c",
923 "deps/SoftFloat-3e/source/extF80M_to_f32.c",
924 "deps/SoftFloat-3e/source/extF80M_to_f64.c",
925 "deps/SoftFloat-3e/source/extF80M_to_f128M.c",
926 "deps/SoftFloat-3e/source/f16_add.c",
927 "deps/SoftFloat-3e/source/f16_div.c",
928 "deps/SoftFloat-3e/source/f16_eq.c",
929 "deps/SoftFloat-3e/source/f16_isSignalingNaN.c",
930 "deps/SoftFloat-3e/source/f16_lt.c",
931 "deps/SoftFloat-3e/source/f16_mul.c",
932 "deps/SoftFloat-3e/source/f16_mulAdd.c",
933 "deps/SoftFloat-3e/source/f16_rem.c",
934 "deps/SoftFloat-3e/source/f16_roundToInt.c",
935 "deps/SoftFloat-3e/source/f16_sqrt.c",
936 "deps/SoftFloat-3e/source/f16_sub.c",
937 "deps/SoftFloat-3e/source/f16_to_extF80M.c",
938 "deps/SoftFloat-3e/source/f16_to_f128M.c",
939 "deps/SoftFloat-3e/source/f16_to_f64.c",
940 "deps/SoftFloat-3e/source/f32_to_extF80M.c",
941 "deps/SoftFloat-3e/source/f32_to_f128M.c",
942 "deps/SoftFloat-3e/source/f64_to_extF80M.c",
943 "deps/SoftFloat-3e/source/f64_to_f128M.c",
944 "deps/SoftFloat-3e/source/f64_to_f16.c",
945 "deps/SoftFloat-3e/source/i32_to_f128M.c",
946 "deps/SoftFloat-3e/source/s_add256M.c",
947 "deps/SoftFloat-3e/source/s_addCarryM.c",
948 "deps/SoftFloat-3e/source/s_addComplCarryM.c",
949 "deps/SoftFloat-3e/source/s_addF128M.c",
950 "deps/SoftFloat-3e/source/s_addExtF80M.c",
951 "deps/SoftFloat-3e/source/s_addM.c",
952 "deps/SoftFloat-3e/source/s_addMagsF16.c",
953 "deps/SoftFloat-3e/source/s_addMagsF32.c",
954 "deps/SoftFloat-3e/source/s_addMagsF64.c",
955 "deps/SoftFloat-3e/source/s_approxRecip32_1.c",
956 "deps/SoftFloat-3e/source/s_approxRecipSqrt32_1.c",
957 "deps/SoftFloat-3e/source/s_approxRecipSqrt_1Ks.c",
958 "deps/SoftFloat-3e/source/s_approxRecip_1Ks.c",
959 "deps/SoftFloat-3e/source/s_compare128M.c",
960 "deps/SoftFloat-3e/source/s_compare96M.c",
961 "deps/SoftFloat-3e/source/s_compareNonnormExtF80M.c",
962 "deps/SoftFloat-3e/source/s_countLeadingZeros16.c",
963 "deps/SoftFloat-3e/source/s_countLeadingZeros32.c",
964 "deps/SoftFloat-3e/source/s_countLeadingZeros64.c",
965 "deps/SoftFloat-3e/source/s_countLeadingZeros8.c",
966 "deps/SoftFloat-3e/source/s_eq128.c",
967 "deps/SoftFloat-3e/source/s_invalidF128M.c",
968 "deps/SoftFloat-3e/source/s_invalidExtF80M.c",
969 "deps/SoftFloat-3e/source/s_isNaNF128M.c",
970 "deps/SoftFloat-3e/source/s_le128.c",
971 "deps/SoftFloat-3e/source/s_lt128.c",
972 "deps/SoftFloat-3e/source/s_mul128MTo256M.c",
973 "deps/SoftFloat-3e/source/s_mul64To128M.c",
974 "deps/SoftFloat-3e/source/s_mulAddF128M.c",
975 "deps/SoftFloat-3e/source/s_mulAddF16.c",
976 "deps/SoftFloat-3e/source/s_mulAddF32.c",
977 "deps/SoftFloat-3e/source/s_mulAddF64.c",
978 "deps/SoftFloat-3e/source/s_negXM.c",
979 "deps/SoftFloat-3e/source/s_normExtF80SigM.c",
980 "deps/SoftFloat-3e/source/s_normRoundPackMToF128M.c",
981 "deps/SoftFloat-3e/source/s_normRoundPackMToExtF80M.c",
982 "deps/SoftFloat-3e/source/s_normRoundPackToF16.c",
983 "deps/SoftFloat-3e/source/s_normRoundPackToF32.c",
984 "deps/SoftFloat-3e/source/s_normRoundPackToF64.c",
985 "deps/SoftFloat-3e/source/s_normSubnormalF128SigM.c",
986 "deps/SoftFloat-3e/source/s_normSubnormalF16Sig.c",
987 "deps/SoftFloat-3e/source/s_normSubnormalF32Sig.c",
988 "deps/SoftFloat-3e/source/s_normSubnormalF64Sig.c",
989 "deps/SoftFloat-3e/source/s_remStepMBy32.c",
990 "deps/SoftFloat-3e/source/s_roundMToI64.c",
991 "deps/SoftFloat-3e/source/s_roundMToUI64.c",
992 "deps/SoftFloat-3e/source/s_roundPackMToExtF80M.c",
993 "deps/SoftFloat-3e/source/s_roundPackMToF128M.c",
994 "deps/SoftFloat-3e/source/s_roundPackToF16.c",
995 "deps/SoftFloat-3e/source/s_roundPackToF32.c",
996 "deps/SoftFloat-3e/source/s_roundPackToF64.c",
997 "deps/SoftFloat-3e/source/s_roundToI32.c",
998 "deps/SoftFloat-3e/source/s_roundToI64.c",
999 "deps/SoftFloat-3e/source/s_roundToUI32.c",
1000 "deps/SoftFloat-3e/source/s_roundToUI64.c",
1001 "deps/SoftFloat-3e/source/s_shiftLeftM.c",
1002 "deps/SoftFloat-3e/source/s_shiftNormSigF128M.c",
1003 "deps/SoftFloat-3e/source/s_shiftRightJam256M.c",
1004 "deps/SoftFloat-3e/source/s_shiftRightJam32.c",
1005 "deps/SoftFloat-3e/source/s_shiftRightJam64.c",
1006 "deps/SoftFloat-3e/source/s_shiftRightJamM.c",
1007 "deps/SoftFloat-3e/source/s_shiftRightM.c",
1008 "deps/SoftFloat-3e/source/s_shortShiftLeft64To96M.c",
1009 "deps/SoftFloat-3e/source/s_shortShiftLeftM.c",
1010 "deps/SoftFloat-3e/source/s_shortShiftRightExtendM.c",
1011 "deps/SoftFloat-3e/source/s_shortShiftRightJam64.c",
1012 "deps/SoftFloat-3e/source/s_shortShiftRightJamM.c",
1013 "deps/SoftFloat-3e/source/s_shortShiftRightM.c",
1014 "deps/SoftFloat-3e/source/s_sub1XM.c",
1015 "deps/SoftFloat-3e/source/s_sub256M.c",
1016 "deps/SoftFloat-3e/source/s_subM.c",
1017 "deps/SoftFloat-3e/source/s_subMagsF16.c",
1018 "deps/SoftFloat-3e/source/s_subMagsF32.c",
1019 "deps/SoftFloat-3e/source/s_subMagsF64.c",
1020 "deps/SoftFloat-3e/source/s_tryPropagateNaNF128M.c",
1021 "deps/SoftFloat-3e/source/s_tryPropagateNaNExtF80M.c",
1022 "deps/SoftFloat-3e/source/softfloat_state.c",
1023 "deps/SoftFloat-3e/source/ui32_to_f128M.c",
1024 "deps/SoftFloat-3e/source/ui64_to_f128M.c",
1025 "deps/SoftFloat-3e/source/ui32_to_extF80M.c",
1026 "deps/SoftFloat-3e/source/ui64_to_extF80M.c",
1027};
1028
1029const zig_cpp_sources = [_][]const u8{880const zig_cpp_sources = [_][]const u8{
1030 // These are planned to stay even when we are self-hosted.881 // These are planned to stay even when we are self-hosted.
1031 "src/zig_llvm.cpp",882 "src/zig_llvm.cpp",
ci/x86_64-windows-debug.ps1+3-2
...@@ -76,7 +76,8 @@ Write-Output "Build x86_64-windows-msvc behavior tests using the C backend..."...@@ -76,7 +76,8 @@ Write-Output "Build x86_64-windows-msvc behavior tests using the C backend..."
76 -ofmt=c `76 -ofmt=c `
77 -femit-bin="test-x86_64-windows-msvc.c" `77 -femit-bin="test-x86_64-windows-msvc.c" `
78 --test-no-exec `78 --test-no-exec `
79 -target x86_64-windows-msvc79 -target x86_64-windows-msvc `
80 -lc
80CheckLastExitCode81CheckLastExitCode
8182
82& "stage3-debug\bin\zig.exe" build-obj `83& "stage3-debug\bin\zig.exe" build-obj `
...@@ -99,7 +100,7 @@ Enter-VsDevShell -VsInstallPath "C:\Program Files\Microsoft Visual Studio\2022\E...@@ -99,7 +100,7 @@ Enter-VsDevShell -VsInstallPath "C:\Program Files\Microsoft Visual Studio\2022\E
99CheckLastExitCode100CheckLastExitCode
100101
101Write-Output "Build and run behavior tests with msvc..."102Write-Output "Build and run behavior tests with msvc..."
102& cl.exe -I..\lib test-x86_64-windows-msvc.c compiler_rt-x86_64-windows-msvc.c /W3 /Z7 -link -nologo -debug -subsystem:console -entry:wWinMainCRTStartup kernel32.lib ntdll.lib vcruntime.lib libucrt.lib103& cl.exe -I..\lib test-x86_64-windows-msvc.c compiler_rt-x86_64-windows-msvc.c /W3 /Z7 -link -nologo -debug -subsystem:console kernel32.lib ntdll.lib libcmt.lib
103CheckLastExitCode104CheckLastExitCode
104105
105& .\test-x86_64-windows-msvc.exe106& .\test-x86_64-windows-msvc.exe
ci/x86_64-windows-release.ps1+3-2
...@@ -76,7 +76,8 @@ Write-Output "Build x86_64-windows-msvc behavior tests using the C backend..."...@@ -76,7 +76,8 @@ Write-Output "Build x86_64-windows-msvc behavior tests using the C backend..."
76 -ofmt=c `76 -ofmt=c `
77 -femit-bin="test-x86_64-windows-msvc.c" `77 -femit-bin="test-x86_64-windows-msvc.c" `
78 --test-no-exec `78 --test-no-exec `
79 -target x86_64-windows-msvc79 -target x86_64-windows-msvc `
80 -lc
80CheckLastExitCode81CheckLastExitCode
8182
82& "stage3-release\bin\zig.exe" build-obj `83& "stage3-release\bin\zig.exe" build-obj `
...@@ -99,7 +100,7 @@ Enter-VsDevShell -VsInstallPath "C:\Program Files\Microsoft Visual Studio\2022\E...@@ -99,7 +100,7 @@ Enter-VsDevShell -VsInstallPath "C:\Program Files\Microsoft Visual Studio\2022\E
99CheckLastExitCode100CheckLastExitCode
100101
101Write-Output "Build and run behavior tests with msvc..."102Write-Output "Build and run behavior tests with msvc..."
102& cl.exe -I..\lib test-x86_64-windows-msvc.c compiler_rt-x86_64-windows-msvc.c /W3 /Z7 -link -nologo -debug -subsystem:console -entry:wWinMainCRTStartup kernel32.lib ntdll.lib vcruntime.lib libucrt.lib103& cl.exe -I..\lib test-x86_64-windows-msvc.c compiler_rt-x86_64-windows-msvc.c /W3 /Z7 -link -nologo -debug -subsystem:console kernel32.lib ntdll.lib libcmt.lib
103CheckLastExitCode104CheckLastExitCode
104105
105& .\test-x86_64-windows-msvc.exe106& .\test-x86_64-windows-msvc.exe
doc/langref.html.in+93-58
...@@ -871,6 +871,13 @@ pub fn main() void {...@@ -871,6 +871,13 @@ pub fn main() void {
871 However, it is possible to embed non-UTF-8 bytes into a string literal using <code>\xNN</code> notation.871 However, it is possible to embed non-UTF-8 bytes into a string literal using <code>\xNN</code> notation.
872 </p>872 </p>
873 <p>873 <p>
874 Indexing into a string containing non-ASCII bytes will return individual bytes, whether valid
875 UTF-8 or not.
876 The {#link|Zig Standard Library#} provides routines for checking the validity of UTF-8 encoded
877 strings, accessing their code points and other encoding/decoding related tasks in
878 {#syntax#}std.unicode{#endsyntax#}.
879 </p>
880 <p>
874 Unicode code point literals have type {#syntax#}comptime_int{#endsyntax#}, the same as881 Unicode code point literals have type {#syntax#}comptime_int{#endsyntax#}, the same as
875 {#link|Integer Literals#}. All {#link|Escape Sequences#} are valid in both string literals882 {#link|Integer Literals#}. All {#link|Escape Sequences#} are valid in both string literals
876 and Unicode code point literals.883 and Unicode code point literals.
...@@ -894,9 +901,12 @@ pub fn main() void {...@@ -894,9 +901,12 @@ pub fn main() void {
894 print("{}\n", .{'e' == '\x65'}); // true901 print("{}\n", .{'e' == '\x65'}); // true
895 print("{d}\n", .{'\u{1f4a9}'}); // 128169902 print("{d}\n", .{'\u{1f4a9}'}); // 128169
896 print("{d}\n", .{'💯'}); // 128175903 print("{d}\n", .{'💯'}); // 128175
897 print("{}\n", .{mem.eql(u8, "hello", "h\x65llo")}); // true
898 print("0x{x}\n", .{"\xff"[0]}); // non-UTF-8 strings are possible with \xNN notation.
899 print("{u}\n", .{'âš¡'});904 print("{u}\n", .{'âš¡'});
905 print("{}\n", .{mem.eql(u8, "hello", "h\x65llo")}); // true
906 print("{}\n", .{mem.eql(u8, "💯", "\xf0\x9f\x92\xaf")}); // also true
907 const invalid_utf8 = "\xff\xfe"; // non-UTF-8 strings are possible with \xNN notation.
908 print("0x{x}\n", .{invalid_utf8[1]}); // indexing them returns individual bytes...
909 print("0x{x}\n", .{"💯"[1]}); // ...as does indexing part-way through non-ASCII characters
900}910}
901 {#code_end#}911 {#code_end#}
902 {#see_also|Arrays|Source Encoding#}912 {#see_also|Arrays|Source Encoding#}
...@@ -8799,6 +8809,15 @@ pub const PrefetchOptions = struct {...@@ -8799,6 +8809,15 @@ pub const PrefetchOptions = struct {
8799 {#link|Optional Pointers#} are allowed. Casting an optional pointer which is {#link|null#}8809 {#link|Optional Pointers#} are allowed. Casting an optional pointer which is {#link|null#}
8800 to a non-optional pointer invokes safety-checked {#link|Undefined Behavior#}.8810 to a non-optional pointer invokes safety-checked {#link|Undefined Behavior#}.
8801 </p>8811 </p>
8812 <p>
8813 {#syntax#}@ptrCast{#endsyntax#} cannot be used for:
8814 </p>
8815 <ul>
8816 <li>Removing {#syntax#}const{#endsyntax#} or {#syntax#}volatile{#endsyntax#} qualifier, use {#link|@qualCast#}.</li>
8817 <li>Changing pointer address space, use {#link|@addrSpaceCast#}.</li>
8818 <li>Increasing pointer alignment, use {#link|@alignCast#}.</li>
8819 <li>Casting a non-slice pointer to a slice, use slicing syntax {#syntax#}ptr[start..end]{#endsyntax#}.</li>
8820 </ul>
8802 {#header_close#}8821 {#header_close#}
88038822
8804 {#header_open|@ptrToInt#}8823 {#header_open|@ptrToInt#}
...@@ -8811,6 +8830,13 @@ pub const PrefetchOptions = struct {...@@ -8811,6 +8830,13 @@ pub const PrefetchOptions = struct {
88118830
8812 {#header_close#}8831 {#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
8814 {#header_open|@rem#}8840 {#header_open|@rem#}
8815 <pre>{#syntax#}@rem(numerator: T, denominator: T) T{#endsyntax#}</pre>8841 <pre>{#syntax#}@rem(numerator: T, denominator: T) T{#endsyntax#}</pre>
8816 <p>8842 <p>
...@@ -9180,8 +9206,7 @@ fn doTheTest() !void {...@@ -9180,8 +9206,7 @@ fn doTheTest() !void {
9180 when available.9206 when available.
9181 </p>9207 </p>
9182 <p>9208 <p>
9183 Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that9209 Supports {#link|Floats#} and {#link|Vectors#} of floats.
9184 <a href="https://github.com/ziglang/zig/issues/4026">some float operations are not yet implemented for all float types</a>.
9185 </p>9210 </p>
9186 {#header_close#}9211 {#header_close#}
9187 {#header_open|@sin#}9212 {#header_open|@sin#}
...@@ -9191,8 +9216,7 @@ fn doTheTest() !void {...@@ -9191,8 +9216,7 @@ fn doTheTest() !void {
9191 when available.9216 when available.
9192 </p>9217 </p>
9193 <p>9218 <p>
9194 Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that9219 Supports {#link|Floats#} and {#link|Vectors#} of floats.
9195 <a href="https://github.com/ziglang/zig/issues/4026">some float operations are not yet implemented for all float types</a>.
9196 </p>9220 </p>
9197 {#header_close#}9221 {#header_close#}
91989222
...@@ -9203,8 +9227,7 @@ fn doTheTest() !void {...@@ -9203,8 +9227,7 @@ fn doTheTest() !void {
9203 when available.9227 when available.
9204 </p>9228 </p>
9205 <p>9229 <p>
9206 Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that9230 Supports {#link|Floats#} and {#link|Vectors#} of floats.
9207 <a href="https://github.com/ziglang/zig/issues/4026">some float operations are not yet implemented for all float types</a>.
9208 </p>9231 </p>
9209 {#header_close#}9232 {#header_close#}
92109233
...@@ -9215,8 +9238,7 @@ fn doTheTest() !void {...@@ -9215,8 +9238,7 @@ fn doTheTest() !void {
9215 Uses a dedicated hardware instruction when available.9238 Uses a dedicated hardware instruction when available.
9216 </p>9239 </p>
9217 <p>9240 <p>
9218 Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that9241 Supports {#link|Floats#} and {#link|Vectors#} of floats.
9219 <a href="https://github.com/ziglang/zig/issues/4026">some float operations are not yet implemented for all float types</a>.
9220 </p>9242 </p>
9221 {#header_close#}9243 {#header_close#}
92229244
...@@ -9227,8 +9249,7 @@ fn doTheTest() !void {...@@ -9227,8 +9249,7 @@ fn doTheTest() !void {
9227 when available.9249 when available.
9228 </p>9250 </p>
9229 <p>9251 <p>
9230 Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that9252 Supports {#link|Floats#} and {#link|Vectors#} of floats.
9231 <a href="https://github.com/ziglang/zig/issues/4026">some float operations are not yet implemented for all float types</a>.
9232 </p>9253 </p>
9233 {#header_close#}9254 {#header_close#}
9234 {#header_open|@exp2#}9255 {#header_open|@exp2#}
...@@ -9238,8 +9259,7 @@ fn doTheTest() !void {...@@ -9238,8 +9259,7 @@ fn doTheTest() !void {
9238 when available.9259 when available.
9239 </p>9260 </p>
9240 <p>9261 <p>
9241 Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that9262 Supports {#link|Floats#} and {#link|Vectors#} of floats.
9242 <a href="https://github.com/ziglang/zig/issues/4026">some float operations are not yet implemented for all float types</a>.
9243 </p>9263 </p>
9244 {#header_close#}9264 {#header_close#}
9245 {#header_open|@log#}9265 {#header_open|@log#}
...@@ -9249,8 +9269,7 @@ fn doTheTest() !void {...@@ -9249,8 +9269,7 @@ fn doTheTest() !void {
9249 when available.9269 when available.
9250 </p>9270 </p>
9251 <p>9271 <p>
9252 Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that9272 Supports {#link|Floats#} and {#link|Vectors#} of floats.
9253 <a href="https://github.com/ziglang/zig/issues/4026">some float operations are not yet implemented for all float types</a>.
9254 </p>9273 </p>
9255 {#header_close#}9274 {#header_close#}
9256 {#header_open|@log2#}9275 {#header_open|@log2#}
...@@ -9260,8 +9279,7 @@ fn doTheTest() !void {...@@ -9260,8 +9279,7 @@ fn doTheTest() !void {
9260 when available.9279 when available.
9261 </p>9280 </p>
9262 <p>9281 <p>
9263 Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that9282 Supports {#link|Floats#} and {#link|Vectors#} of floats.
9264 <a href="https://github.com/ziglang/zig/issues/4026">some float operations are not yet implemented for all float types</a>.
9265 </p>9283 </p>
9266 {#header_close#}9284 {#header_close#}
9267 {#header_open|@log10#}9285 {#header_open|@log10#}
...@@ -9271,8 +9289,7 @@ fn doTheTest() !void {...@@ -9271,8 +9289,7 @@ fn doTheTest() !void {
9271 when available.9289 when available.
9272 </p>9290 </p>
9273 <p>9291 <p>
9274 Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that9292 Supports {#link|Floats#} and {#link|Vectors#} of floats.
9275 <a href="https://github.com/ziglang/zig/issues/4026">some float operations are not yet implemented for all float types</a>.
9276 </p>9293 </p>
9277 {#header_close#}9294 {#header_close#}
9278 {#header_open|@fabs#}9295 {#header_open|@fabs#}
...@@ -9282,8 +9299,7 @@ fn doTheTest() !void {...@@ -9282,8 +9299,7 @@ fn doTheTest() !void {
9282 when available.9299 when available.
9283 </p>9300 </p>
9284 <p>9301 <p>
9285 Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that9302 Supports {#link|Floats#} and {#link|Vectors#} of floats.
9286 <a href="https://github.com/ziglang/zig/issues/4026">some float operations are not yet implemented for all float types</a>.
9287 </p>9303 </p>
9288 {#header_close#}9304 {#header_close#}
9289 {#header_open|@floor#}9305 {#header_open|@floor#}
...@@ -9293,8 +9309,7 @@ fn doTheTest() !void {...@@ -9293,8 +9309,7 @@ fn doTheTest() !void {
9293 Uses a dedicated hardware instruction when available.9309 Uses a dedicated hardware instruction when available.
9294 </p>9310 </p>
9295 <p>9311 <p>
9296 Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that9312 Supports {#link|Floats#} and {#link|Vectors#} of floats.
9297 <a href="https://github.com/ziglang/zig/issues/4026">some float operations are not yet implemented for all float types</a>.
9298 </p>9313 </p>
9299 {#header_close#}9314 {#header_close#}
9300 {#header_open|@ceil#}9315 {#header_open|@ceil#}
...@@ -9304,8 +9319,7 @@ fn doTheTest() !void {...@@ -9304,8 +9319,7 @@ fn doTheTest() !void {
9304 Uses a dedicated hardware instruction when available.9319 Uses a dedicated hardware instruction when available.
9305 </p>9320 </p>
9306 <p>9321 <p>
9307 Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that9322 Supports {#link|Floats#} and {#link|Vectors#} of floats.
9308 <a href="https://github.com/ziglang/zig/issues/4026">some float operations are not yet implemented for all float types</a>.
9309 </p>9323 </p>
9310 {#header_close#}9324 {#header_close#}
9311 {#header_open|@trunc#}9325 {#header_open|@trunc#}
...@@ -9315,8 +9329,7 @@ fn doTheTest() !void {...@@ -9315,8 +9329,7 @@ fn doTheTest() !void {
9315 Uses a dedicated hardware instruction when available.9329 Uses a dedicated hardware instruction when available.
9316 </p>9330 </p>
9317 <p>9331 <p>
9318 Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that9332 Supports {#link|Floats#} and {#link|Vectors#} of floats.
9319 <a href="https://github.com/ziglang/zig/issues/4026">some float operations are not yet implemented for all float types</a>.
9320 </p>9333 </p>
9321 {#header_close#}9334 {#header_close#}
9322 {#header_open|@round#}9335 {#header_open|@round#}
...@@ -9326,8 +9339,7 @@ fn doTheTest() !void {...@@ -9326,8 +9339,7 @@ fn doTheTest() !void {
9326 when available.9339 when available.
9327 </p>9340 </p>
9328 <p>9341 <p>
9329 Supports {#link|Floats#} and {#link|Vectors#} of floats, with the caveat that9342 Supports {#link|Floats#} and {#link|Vectors#} of floats.
9330 <a href="https://github.com/ziglang/zig/issues/4026">some float operations are not yet implemented for all float types</a>.
9331 </p>9343 </p>
9332 {#header_close#}9344 {#header_close#}
93339345
...@@ -9528,11 +9540,15 @@ fn foo(comptime T: type, ptr: *T) T {...@@ -9528,11 +9540,15 @@ fn foo(comptime T: type, ptr: *T) T {
9528 To add standard build options to a <code class="file">build.zig</code> file:9540 To add standard build options to a <code class="file">build.zig</code> file:
9529 </p>9541 </p>
9530 {#code_begin|syntax|build#}9542 {#code_begin|syntax|build#}
9531const Builder = @import("std").build.Builder;9543const std = @import("std");
95329544
9533pub fn build(b: *Builder) void {9545pub fn build(b: *std.Build) void {
9534 const exe = b.addExecutable("example", "example.zig");9546 const optimize = b.standardOptimizeOption(.{});
9535 exe.setBuildMode(b.standardReleaseOptions());9547 const exe = b.addExecutable(.{
9548 .name = "example",
9549 .root_source_file = .{ .path = "example.zig" },
9550 .optimize = optimize,
9551 });
9536 b.default_step.dependOn(&exe.step);9552 b.default_step.dependOn(&exe.step);
9537}9553}
9538 {#code_end#}9554 {#code_end#}
...@@ -10547,22 +10563,26 @@ const separator = if (builtin.os.tag == .windows) '\\' else '/';...@@ -10547,22 +10563,26 @@ const separator = if (builtin.os.tag == .windows) '\\' else '/';
10547 <p>This <code class="file">build.zig</code> file is automatically generated10563 <p>This <code class="file">build.zig</code> file is automatically generated
10548 by <kbd>zig init-exe</kbd>.</p>10564 by <kbd>zig init-exe</kbd>.</p>
10549 {#code_begin|syntax|build_executable#}10565 {#code_begin|syntax|build_executable#}
10550const Builder = @import("std").build.Builder;10566const std = @import("std");
1055110567
10552pub fn build(b: *Builder) void {10568pub fn build(b: *std.Build) void {
10553 // Standard target options allows the person running `zig build` to choose10569 // Standard target options allows the person running `zig build` to choose
10554 // what target to build for. Here we do not override the defaults, which10570 // what target to build for. Here we do not override the defaults, which
10555 // means any target is allowed, and the default is native. Other options10571 // means any target is allowed, and the default is native. Other options
10556 // for restricting supported target set are available.10572 // for restricting supported target set are available.
10557 const target = b.standardTargetOptions(.{});10573 const target = b.standardTargetOptions(.{});
1055810574
10559 // Standard release options allow the person running `zig build` to select10575 // Standard optimization options allow the person running `zig build` to select
10560 // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall.10576 // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not
10561 const mode = b.standardReleaseOptions();10577 // set a preferred release mode, allowing the user to decide how to optimize.
10578 const optimize = b.standardOptimizeOption(.{});
1056210579
10563 const exe = b.addExecutable("example", "src/main.zig");10580 const exe = b.addExecutable(.{
10564 exe.setTarget(target);10581 .name = "example",
10565 exe.setBuildMode(mode);10582 .root_source_file = .{ .path = "src/main.zig" },
10583 .target = target,
10584 .optimize = optimize,
10585 });
10566 exe.install();10586 exe.install();
1056710587
10568 const run_cmd = exe.run();10588 const run_cmd = exe.run();
...@@ -10581,16 +10601,21 @@ pub fn build(b: *Builder) void {...@@ -10581,16 +10601,21 @@ pub fn build(b: *Builder) void {
10581 <p>This <code class="file">build.zig</code> file is automatically generated10601 <p>This <code class="file">build.zig</code> file is automatically generated
10582 by <kbd>zig init-lib</kbd>.</p>10602 by <kbd>zig init-lib</kbd>.</p>
10583 {#code_begin|syntax|build_library#}10603 {#code_begin|syntax|build_library#}
10584const Builder = @import("std").build.Builder;10604const std = @import("std");
1058510605
10586pub fn build(b: *Builder) void {10606pub fn build(b: *std.Build) void {
10587 const mode = b.standardReleaseOptions();10607 const optimize = b.standardOptimizeOption(.{});
10588 const lib = b.addStaticLibrary("example", "src/main.zig");10608 const lib = b.addStaticLibrary(.{
10589 lib.setBuildMode(mode);10609 .name = "example",
10610 .root_source_file = .{ .path = "src/main.zig" },
10611 .optimize = optimize,
10612 });
10590 lib.install();10613 lib.install();
1059110614
10592 var main_tests = b.addTest("src/main.zig");10615 const main_tests = b.addTest(.{
10593 main_tests.setBuildMode(mode);10616 .root_source_file = .{ .path = "src/main.zig" },
10617 .optimize = optimize,
10618 });
1059410619
10595 const test_step = b.step("test", "Run library tests");10620 const test_step = b.step("test", "Run library tests");
10596 test_step.dependOn(&main_tests.step);10621 test_step.dependOn(&main_tests.step);
...@@ -10949,12 +10974,17 @@ int main(int argc, char **argv) {...@@ -10949,12 +10974,17 @@ int main(int argc, char **argv) {
10949}10974}
10950 {#end_syntax_block#}10975 {#end_syntax_block#}
10951 {#code_begin|syntax|build_c#}10976 {#code_begin|syntax|build_c#}
10952const Builder = @import("std").build.Builder;10977const std = @import("std");
10953
10954pub fn build(b: *Builder) void {
10955 const lib = b.addSharedLibrary("mathtest", "mathtest.zig", b.version(1, 0, 0));
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 });
10958 exe.addCSourceFile("test.c", &[_][]const u8{"-std=c99"});10988 exe.addCSourceFile("test.c", &[_][]const u8{"-std=c99"});
10959 exe.linkLibrary(lib);10989 exe.linkLibrary(lib);
10960 exe.linkSystemLibrary("c");10990 exe.linkSystemLibrary("c");
...@@ -11011,12 +11041,17 @@ int main(int argc, char **argv) {...@@ -11011,12 +11041,17 @@ int main(int argc, char **argv) {
11011}11041}
11012 {#end_syntax_block#}11042 {#end_syntax_block#}
11013 {#code_begin|syntax|build_object#}11043 {#code_begin|syntax|build_object#}
11014const Builder = @import("std").build.Builder;11044const std = @import("std");
1101511045
11016pub fn build(b: *Builder) void {11046pub fn build(b: *std.Build) void {
11017 const obj = b.addObject("base64", "base64.zig");11047 const obj = b.addObject(.{
11048 .name = "base64",
11049 .root_source_file = .{ .path = "base64.zig" },
11050 });
1101811051
11019 const exe = b.addExecutable("test", null);11052 const exe = b.addExecutable(.{
11053 .name = "test",
11054 });
11020 exe.addCSourceFile("test.c", &[_][]const u8{"-std=c99"});11055 exe.addCSourceFile("test.c", &[_][]const u8{"-std=c99"});
11021 exe.addObject(obj);11056 exe.addObject(obj);
11022 exe.linkSystemLibrary("c");11057 exe.linkSystemLibrary("c");
lib/build_runner.zig+7-5
...@@ -3,7 +3,6 @@ const std = @import("std");...@@ -3,7 +3,6 @@ const std = @import("std");
3const builtin = @import("builtin");3const builtin = @import("builtin");
4const io = std.io;4const io = std.io;
5const fmt = std.fmt;5const fmt = std.fmt;
6const Builder = std.build.Builder;
7const mem = std.mem;6const mem = std.mem;
8const process = std.process;7const process = std.process;
9const ArrayList = std.ArrayList;8const ArrayList = std.ArrayList;
...@@ -42,12 +41,15 @@ pub fn main() !void {...@@ -42,12 +41,15 @@ pub fn main() !void {
42 return error.InvalidArgs;41 return error.InvalidArgs;
43 };42 };
4443
45 const builder = try Builder.create(44 const host = try std.zig.system.NativeTargetInfo.detect(.{});
45
46 const builder = try std.Build.create(
46 allocator,47 allocator,
47 zig_exe,48 zig_exe,
48 build_root,49 build_root,
49 cache_root,50 cache_root,
50 global_cache_root,51 global_cache_root,
52 host,
51 );53 );
52 defer builder.destroy();54 defer builder.destroy();
5355
...@@ -58,7 +60,7 @@ pub fn main() !void {...@@ -58,7 +60,7 @@ pub fn main() !void {
58 const stdout_stream = io.getStdOut().writer();60 const stdout_stream = io.getStdOut().writer();
5961
60 var install_prefix: ?[]const u8 = null;62 var install_prefix: ?[]const u8 = null;
61 var dir_list = Builder.DirList{};63 var dir_list = std.Build.DirList{};
6264
63 // before arg parsing, check for the NO_COLOR environment variable65 // before arg parsing, check for the NO_COLOR environment variable
64 // if it exists, default the color setting to .off66 // if it exists, default the color setting to .off
...@@ -230,7 +232,7 @@ pub fn main() !void {...@@ -230,7 +232,7 @@ pub fn main() !void {
230 };232 };
231}233}
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 {
234 // run the build script to collect the options236 // run the build script to collect the options
235 if (!already_ran_build) {237 if (!already_ran_build) {
236 builder.resolveInstallPrefix(null, .{});238 builder.resolveInstallPrefix(null, .{});
...@@ -330,7 +332,7 @@ fn usage(builder: *Builder, already_ran_build: bool, out_stream: anytype) !void...@@ -330,7 +332,7 @@ fn usage(builder: *Builder, already_ran_build: bool, out_stream: anytype) !void
330 );332 );
331}333}
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 {
334 usage(builder, already_ran_build, out_stream) catch {};336 usage(builder, already_ran_build, out_stream) catch {};
335 process.exit(1);337 process.exit(1);
336}338}
lib/compiler_rt/README.md+534-471
...@@ -27,482 +27,545 @@ then statically linked and therefore is a transparent dependency for the...@@ -27,482 +27,545 @@ then statically linked and therefore is a transparent dependency for the
27programmer.27programmer.
28For details see `../compiler_rt.zig`.28For 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
43Bugs should be solved by trying to duplicate the bug upstream, if possible.30Bugs should be solved by trying to duplicate the bug upstream, if possible.
44 * If the bug exists upstream, get it fixed upstream and port the fix downstream to Zig.31 * If the bug exists upstream, get it fixed upstream and port the fix downstream to Zig.
45 * If the bug only exists in Zig, use the corresponding C code and debug32 * If the bug only exists in Zig, use the corresponding C code and debug
46 both implementations side by side to figure out what is wrong.33 both implementations side by side to figure out what is wrong.
4734
48## Integer library routines35Routines with status are given below. Sources were besides
4936"The Art of Computer Programming" by Donald E. Knuth, "HackersDelight" by Henry S. Warren,
50#### Integer Bit operations37"Bit Twiddling Hacks" collected by Sean Eron Anderson, "Berkeley SoftFloat" by John R. Hauser,
5138LLVM "compiler-rt" as it was MIT-licensed, "musl libc" and thoughts + work of contributors.
52- dev HackersDelight __clzsi2 // count leading zeros39
53- dev HackersDelight __clzdi2 // count leading zeros40The compiler-rt routines have not yet been audited.
54- dev HackersDelight __clzti2 // count leading zeros41See https://github.com/ziglang/zig/issues/1504.
55- dev HackersDelight __ctzsi2 // count trailing zeros42
56- dev HackersDelight __ctzdi2 // count trailing zeros43From left to right the columns mean 1. if the routine is implemented (✗ or ✓),
57- dev HackersDelight __ctzti2 // count trailing zeros442. the name, 3. input (`a`), 4. input (`b`), 5. return value,
58- dev __ctzsi2 __ffssi2 // find least significant 1 bit456. an explanation of the functionality, .. to repeat the comment from the
59- dev __ctzsi2 __ffsdi2 // find least significant 1 bit46column a row above and/or additional return values.
60- dev __ctzsi2 __ffsti2 // find least significant 1 bit47Some routines have more extensive comments supplemented with a reference text.
61- dev BitTwiddlingHacks __paritysi2 // bit parity48
62- dev BitTwiddlingHacks __paritydi2 // bit parity49Integer and Float Operations
63- dev BitTwiddlingHacks __parityti2 // bit parity50
64- dev TAOCP __popcountsi2 // bit population51| Done | Name | a | b | Out | Comment |
65- dev TAOCP __popcountdi2 // bit population52| ------ | ------------- | ---- | ---- | ---- | ------------------------------ |
66- dev TAOCP __popcountti2 // bit population53| | | | | | **Integer Bit Operations** |
67- dev other __bswapsi2 // a byteswapped54| ✓ | __clzsi2 | u32 | ∅ | i32 | count leading zeroes |
68- dev other __bswapdi2 // a byteswapped55| ✓ | __clzdi2 | u64 | ∅ | i32 | count leading zeroes |
69- dev other __bswapti2 // a byteswapped56| ✓ | __clzti2 | u128 | ∅ | i32 | count trailing zeros |
7057| ✓ | __ctzsi2 | u32 | ∅ | i32 | count trailing zeros |
71#### Integer Comparison58| ✓ | __ctzdi2 | u64 | ∅ | i32 | count trailing zeros |
7259| ✓ | __ctzti2 | u128 | ∅ | i32 | count leading zeroes |
73- port llvm __cmpsi2 // a,b: i32, (a<b)-> 0, (a==b) -> 1, (a>b) -> 260| ✓ | __ffssi2 | u32 | ∅ | i32 | count leading zeroes |
74- port llvm __cmpdi2 // a,b: i6461| ✓ | __ffsdi2 | u64 | ∅ | i32 | count leading zeroes |
75- port llvm __cmpti2 // a,b: i12862| ✓ | __ffsti2 | u128 | ∅ | i32 | count leading zeroes |
76- port llvm __ucmpsi2 // a,b: u32, (a<b)-> 0, (a==b) -> 1, (a>b) -> 263| ✓ | __paritysi2 | u32 | ∅ | i32 | find least significant 1 bit |
77- port llvm __ucmpdi2 // a,b: u6464| ✓ | __paritydi2 | u64 | ∅ | i32 | find least significant 1 bit |
78- port llvm __ucmpti2 // a,b: u12865| ✓ | __parityti2 | u128 | ∅ | i32 | find least significant 1 bit |
7966| ✓ | __popcountsi2 | u32 | ∅ | i32 | bit population |
80#### Integer Arithmetic67| ✓ | __popcountdi2 | u64 | ∅ | i32 | bit population |
8168| ✓ | __popcountti2 | u128 | ∅ | i32 | bit population |
82- none none __ashlsi3 // a,b: i32, a << b unused in llvm, TODO (e.g. used by rl78)69| ✓ | __bswapsi2 | u32 | ∅ | i32 | bit parity |
83- port llvm __ashldi3 // a,b: u6470| ✓ | __bswapdi2 | u64 | ∅ | i32 | bit parity |
84- port llvm __ashlti3 // a,b: u12871| ✓ | __bswapti2 | u128 | ∅ | i32 | bit parity |
85- none none __ashrsi3 // a,b: i32, a >> b arithmetic (sign fill) TODO (e.g. used by rl78)72| | | | | | **Integer Comparison** |
86- port llvm __ashrdi3 // ..73| ✓ | __cmpsi2 | i32 | i32 | i32 | `(a<b) -> 0, (a==b) -> 1, (a>b) -> 2` |
87- port llvm __ashrti3 //74| ✓ | __cmpdi2 | i64 | i64 | i32 | .. |
88- none none __lshrsi3 // a,b: i32, a >> b logical (zero fill) TODO (e.g. used by rl78)75| ✓ | __cmpti2 | i128 | i128 | i32 | .. |
89- port llvm __lshrdi3 //76| ✓ | __ucmpsi2 | i32 | i32 | i32 | `(a<b) -> 0, (a==b) -> 1, (a>b) -> 2` |
90- port llvm __lshrti3 //77| ✓ | __ucmpdi2 | i64 | i64 | i32 | .. |
91- port llvm __negdi2 // a: i32, -a, symbol-level compatibility with libgcc78| ✓ | __ucmpti2 | i128 | i128 | i32 | .. |
92- port llvm __negti2 // unnecessary: unused in backends79| | | | | | **Integer Arithmetic** |
93- port llvm __mulsi3 // a,b: i32, a * b80| ✗ | __ashlsi3 | i32 | i32 | i32 | `a << b` [^unused_rl78] |
94- port llvm __muldi3 //81| ✓ | __ashldi3 | i64 | i32 | i64 | .. |
95- port llvm __multi3 //82| ✓ | __ashlti3 | i128 | i32 | i128 | .. |
96- port llvm __divsi3 // a,b: i32, a / b83| ✓ | __aeabi_llsl | i32 | i32 | i32 | .. ARM |
97- port llvm __divdi3 //84| ✗ | __ashrsi3 | i32 | i32 | i32 | `a >> b` arithmetic (sign fill) [^unused_rl78] |
98- port llvm __divti3 //85| ✓ | __ashrdi3 | i64 | i32 | i64 | .. |
99- port llvm __udivsi3 // a,b: u32, a / b86| ✓ | __ashrti3 | i128 | i32 | i128 | .. |
100- port llvm __udivdi3 //87| ✓ | __aeabi_lasr | i64 | i32 | i64 | .. ARM |
101- port llvm __udivti3 //88| ✗ | __lshrsi3 | i32 | i32 | i32 | `a >> b` logical (zero fill) [^unused_rl78] |
102- port llvm __modsi3 // a,b: i32, a % b89| ✓ | __lshrdi3 | i64 | i32 | i64 | .. |
103- port llvm __moddi3 //90| ✓ | __lshrti3 | i128 | i32 | i128 | .. |
104- port llvm __modti3 //91| ✓ | __aeabi_llsr | i64 | i32 | i64 | .. ARM |
105- port llvm __umodsi3 // a,b: u32, a % b92| ✓ | __negsi2 | i32 | i32 | i32 | `-a` [^libgcc_compat] |
106- port llvm __umoddi3 //93| ✓ | __negdi2 | i64 | i64 | i64 | .. |
107- port llvm __umodti3 //94| ✓ | __negti2 | i128 | i128 | i128 | .. |
108- port llvm __udivmoddi4 // a,b: u32, a / b, rem.* = a % b unsigned95| ✓ | __mulsi3 | i32 | i32 | i32 | `a * b` |
109- port llvm __udivmodti4 //96| ✓ | __muldi3 | i64 | i64 | i64 | .. |
110- port llvm __udivmodsi4 //97| ✓ | __multi3 | i128 | i128 | i128 | .. |
111- port llvm __divmodsi4 // a,b: i32, a / b, rem.* = a % b signed, ARM98| ✓ | __divsi3 | i32 | i32 | i32 | `a / b` |
112- port llvm __divmoddi4 //99| ✓ | __divdi3 | i64 | i64 | i64 | .. |
113100| ✓ | __divti3 | i128 | i128 | i128 | .. |
114#### Integer Arithmetic with trapping overflow101| ✓ | __aeabi_idiv | i32 | i32 | i32 | .. ARM |
115102| ✓ | __udivsi3 | u32 | u32 | u32 | `a / b` |
116- dev BitTwiddlingHacks __absvsi2 // abs(a)103| ✓ | __udivdi3 | u64 | u64 | u64 | .. |
117- dev BitTwiddlingHacks __absvdi2 // abs(a)104| ✓ | __udivti3 | u128 | u128 | u128 | .. |
118- dev BitTwiddlingHacks __absvti2 // abs(a)105| ✓ | __aeabi_uidiv | i32 | i32 | i32 | .. ARM |
119- port llvm __negvsi2 // -a symbol-level compatibility: libgcc106| ✓ | __modsi3 | i32 | i32 | i32 | `a % b` |
120- port llvm __negvdi2 // -a unnecessary: unused in backends107| ✓ | __moddi3 | i64 | i64 | i64 | .. |
121- port llvm __negvti2 // -a108| ✓ | __modti3 | i128 | i128 | i128 | .. |
122- TODO upstreaming __addvsi3..__mulvti3 after testing panics works109| ✓ | __umodsi3 | u32 | u32 | u32 | `a % b` |
123- dev HackersDelight __addvsi3 // a + b110| ✓ | __umoddi3 | u64 | u64 | u64 | .. |
124- dev HackersDelight __addvdi3 //111| ✓ | __umodti3 | u128 | u128 | u128 | .. |
125- dev HackersDelight __addvti3 //112| ✓ | __udivmodsi4 | u32 | u32 | u32 | `a / b, rem.* = a % b` |
126- dev HackersDelight __subvsi3 // a - b113| ✓ | __udivmoddi4 | u64 | u64 | u64 | .. |
127- dev HackersDelight __subvdi3 //114| ✓ | __udivmodti4 | u128 | u128 | u128 | .. |
128- dev HackersDelight __subvti3 //115| ✓ | __divmodsi4 | i32 | i32 | i32 | `a / b, rem.* = a % b` |
129- dev HackersDelight __mulvsi3 // a * b116| ✓ | __divmoddi4 | i64 | i64 | i64 | .. |
130- dev HackersDelight __mulvdi3 //117| ✗ | __divmodti4 | i128 | i128 | i128 | .. [^libgcc_compat] |
131- dev HackersDelight __mulvti3 //118| | | | | | **Integer Arithmetic with Trapping Overflow**|
132119| ✓ | __absvsi2 | i32 | i32 | i32 | abs(a) |
133#### Integer Arithmetic which returns if overflow (would be faster without pointer)120| ✓ | __absvdi2 | i64 | i64 | i64 | .. |
134121| ✓ | __absvti2 | i128 | i128 | i128 | .. |
135- dev HackersDelight __addosi4 // a + b, overflow->ov.*=1 else 0122| ✓ | __negvsi2 | i32 | i32 | i32 | `-a` [^libgcc_compat] |
136- dev HackersDelight __addodi4 // (completeness + performance, llvm does not use them)123| ✓ | __negvdi2 | i64 | i64 | i64 | .. |
137- dev HackersDelight __addoti4 //124| ✓ | __negvti2 | i128 | i128 | i128 | .. |
138- dev HackersDelight __subosi4 // a - b, overflow->ov.*=1 else 0125| ✗ | __addvsi3 | i32 | i32 | i32 | `a + b` |
139- dev HackersDelight __subodi4 // (completeness + performance, llvm does not use them)126| ✗ | __addvdi3 | i64 | i64 | i64 | .. |
140- dev HackersDelight __suboti4 //127| ✗ | __addvti3 | i128 | i128 | i128 | .. |
141- dev HackersDelight __mulosi4 // a * b, overflow->ov.*=1 else 0128| ✗ | __subvsi3 | i32 | i32 | i32 | `a - b` |
142- dev HackersDelight __mulodi4 // (required by llvm)129| ✗ | __subvdi3 | i64 | i64 | i64 | .. |
143- dev HackersDelight __muloti4 //130| ✗ | __subvti3 | i128 | i128 | i128 | .. |
144131| ✗ | __mulvsi3 | i32 | i32 | i32 | `a * b` |
145## Float library routines132| ✗ | __mulvdi3 | i64 | i64 | i64 | .. |
146133| ✗ | __mulvti3 | i128 | i128 | i128 | .. |
147TODO: review source of implementation134| | | | | | **Integer Arithmetic which Return on Overflow** [^noptr_faster] |
148135| ✓ | __addosi4 | i32 | i32 | i32 | `a + b`, overflow->ov.*=1 else 0 [^perf_addition] |
149#### Float Conversion136| ✓ | __addodi4 | i64 | i64 | i64 | .. |
150137| ✓ | __addoti4 | i128 | i128 | i128 | .. |
151- dev other __extendsfdf2 // a: f32 -> f64, TODO: missing tests138| ✓ | __subosi4 | i32 | i32 | i32 | `a - b`, overflow->ov.*=1 else 0 [^perf_addition] |
152- dev other __extendsftf2 // a: f32 -> f128139| ✓ | __subodi4 | i64 | i64 | i64 | .. |
153- dev llvm __extendsfxf2 // a: f32 -> f80, TODO: missing tests140| ✓ | __suboti4 | i128 | i128 | i128 | .. |
154- dev other __extenddftf2 // a: f64 -> f128141| ✓ | __mulosi4 | i32 | i32 | i32 | `a * b`, overflow->ov.*=1 else 0 |
155- dev llvm __extenddfxf2 // a: f64 -> f80142| ✓ | __mulodi4 | i64 | i64 | i64 | .. |
156- dev other __truncdfsf2 // a: f64 -> f32, rounding towards zero143| ✓ | __muloti4 | i128 | i128 | i128 | .. |
157- dev other __trunctfdf2 // a: f128-> f64144| | | | | | **Float Conversion** |
158- dev other __trunctfsf2 // a: f128-> f32145| ✓ | __extendsfdf2 | f32 | ∅ | f64 | .. |
159- dev llvm __truncxfsf2 // a: f80 -> f32, TODO: missing tests146| ✓ | __extendsftf2 | f32 | ∅ | f128 | .. |
160- dev llvm __truncxfdf2 // a: f80 -> f64, TODO: missing tests147| ✓ | __extendsfxf2 | f32 | ∅ | f80 | .. |
161148| ✓ | __extenddftf2 | f64 | ∅ | f128 | .. |
162- dev unclear __fixsfsi // a: f32 -> i32, rounding towards zero149| ✓ | __extenddfxf2 | f64 | ∅ | f80 | .. |
163- dev unclear __fixdfsi // a: f64 -> i32150| ✓ | __truncsfhf2 | f32 | ∅ | f16 | rounding towards zero |
164- dev unclear __fixtfsi // a: f128-> i32151| ✓ | __truncdfhf2 | f64 | ∅ | f16 | .. |
165- dev unclear __fixxfsi // a: f80 -> i32, TODO: missing tests152| ✓ | __truncdfsf2 | f64 | ∅ | f32 | .. |
166- dev unclear __fixsfdi // a: f32 -> i64, rounding towards zero153| ✓ | __trunctfhf2 | f128 | ∅ | f16 | .. |
167- dev unclear __fixdfdi // ..154| ✓ | __trunctfsf2 | f128 | ∅ | f32 | .. |
168- dev unclear __fixtfdi //155| ✓ | __trunctfdf2 | f128 | ∅ | f64 | .. |
169- dev unclear __fixxfdi // TODO: missing tests156| ✓ | __trunctfxf2 | f128 | ∅ | f80 | .. |
170- dev unclear __fixsfti // a: f32 -> i128, rounding towards zero157| ✓ | __truncxfhf2 | f80 | ∅ | f16 | .. |
171- dev unclear __fixdfti // ..158| ✓ | __truncxfsf2 | f80 | ∅ | f32 | .. |
172- dev unclear __fixtfdi //159| ✓ | __truncxfdf2 | f80 | ∅ | f64 | .. |
173- dev unclear __fixxfti // TODO: missing tests160| ✓ | __aeabi_f2h | f32 | ∅ | f16 | .. ARM |
174161| ✓ | __gnu_f2h_ieee | f32 | ∅ | f16 | ..GNU naming convention |
175- dev unclear __fixunssfsi // a: f32 -> u32, rounding towards zero. negative values become 0.162| ✓ | __aeabi_d2h | f64 | ∅ | f16 | .. ARM |
176- dev unclear __fixunsdfsi // ..163| ✓ | __aeabi_d2f | f64 | ∅ | f32 | .. ARM |
177- dev unclear __fixunstfsi //164| ✓ | __trunckfsf2 | f128 | ∅ | f32 | .. PPC |
178- dev unclear __fixunsxfsi // TODO: missing tests165| ✓ | _Qp_qtos |*f128 | ∅ | f32 | .. SPARC |
179- dev unclear __fixunssfdi // a: f32 -> u64, rounding towards zero. negative values become 0.166| ✓ | __trunckfdf2 | f128 | ∅ | f64 | .. PPC |
180- dev unclear __fixunsdfdi //167| ✓ | _Qp_qtod |*f128 | ∅ | f64 | .. SPARC |
181- dev unclear __fixunstfdi //168| ✓ | __fixhfsi | f16 | ∅ | i32 | rounding towards zero |
182- dev unclear __fixunsxfdi // TODO: missing tests169| ✓ | __fixsfsi | f32 | ∅ | i32 | .. |
183- dev unclear __fixunssfti // a: f32 -> u128, rounding towards zero. negative values become 0.170| ✓ | __fixdfsi | f64 | ∅ | i32 | .. |
184- dev unclear __fixunsdfti //171| ✓ | __fixtfsi | f128 | ∅ | i32 | .. |
185- dev unclear __fixunstfdi //172| ✓ | __fixxfsi | f80 | ∅ | i32 | .. |
186- dev unclear __fixunsxfti // TODO: some more tests needed for base coverage173| ✓ | __fixhfdi | f16 | ∅ | i64 | .. |
187174| ✓ | __fixsfdi | f32 | ∅ | i64 | .. |
188- dev unclear __floatsisf // a: i32 -> f32175| ✓ | __fixdfdi | f64 | ∅ | i64 | .. |
189- dev unclear __floatsidf // a: i32 -> f64, TODO: missing tests176| ✓ | __fixtfdi | f128 | ∅ | i64 | .. |
190- dev unclear __floatsitf // ..177| ✓ | __fixxfdi | f80 | ∅ | i64 | .. |
191- dev unclear __floatsixf // TODO: missing tests178| ✓ | __fixhfti | f16 | ∅ | i128 | .. |
192- dev unclear __floatdisf // a: i64 -> f32179| ✓ | __fixsfti | f32 | ∅ | i128 | .. |
193- dev unclear __floatdidf //180| ✓ | __fixdfti | f64 | ∅ | i128 | .. |
194- dev unclear __floatditf //181| ✓ | __fixtfti | f128 | ∅ | i128 | .. |
195- dev unclear __floatdixf // TODO: missing tests182| ✓ | __fixxfti | f80 | ∅ | i128 | .. |
196- dev unclear __floattisf // a: i128-> f32183| ✓ | __fixunshfsi | f16 | ∅ | u32 | rounding towards zero. negative values become 0. |
197- dev unclear __floattidf //184| ✓ | __fixunssfsi | f32 | ∅ | u32 | .. |
198- dev unclear __floattitf //185| ✓ | __fixunsdfsi | f64 | ∅ | u32 | .. |
199- dev unclear __floattixf // TODO: missing tests186| ✓ | __fixunstfsi | f128 | ∅ | u32 | .. |
200187| ✓ | __fixunsxfsi | f80 | ∅ | u32 | .. |
201- dev unclear __floatunsisf // a: u32 -> f32188| ✓ | __fixunshfdi | f16 | ∅ | u64 | .. |
202- dev unclear __floatunsidf // TODO: missing tests189| ✓ | __fixunssfdi | f32 | ∅ | u64 | .. |
203- dev unclear __floatunsitf //190| ✓ | __fixunsdfdi | f64 | ∅ | u64 | .. |
204- dev unclear __floatunsixf // TODO: missing tests191| ✓ | __fixunstfdi | f128 | ∅ | u64 | .. |
205- dev unclear __floatundisf // a: u64 -> f32192| ✓ | __fixunsxfdi | f80 | ∅ | u64 | .. |
206- dev unclear __floatundidf //193| ✓ | __fixunshfti | f16 | ∅ | u128 | .. |
207- dev unclear __floatunditf //194| ✓ | __fixunssfti | f32 | ∅ | u128 | .. |
208- dev unclear __floatundixf // TODO: missing tests195| ✓ | __fixunsdfti | f64 | ∅ | u128 | .. |
209- dev unclear __floatuntisf // a: u128-> f32196| ✓ | __fixunstfti | f128 | ∅ | u128 | .. |
210- dev unclear __floatuntidf //197| ✓ | __fixunsxfti | f80 | ∅ | u128 | .. |
211- dev unclear __floatuntitf //198| ✓ | __floatsihf | i32 | ∅ | f16 | int_to_float conversions |
212- dev unclear __floatuntixf // TODO: missing tests199| ✓ | __floatsisf | i32 | ∅ | f32 | .. |
213200| ✓ | __floatsidf | i32 | ∅ | f64 | .. |
214#### Float Comparison201| ✓ | __floatsitf | i32 | ∅ | f128 | .. |
215202| ✓ | __floatsixf | i32 | ∅ | f80 | .. |
216- dev other __cmpsf2 // a,b:f32, (a<b)->-1,(a==b)->0,(a>b)->1,Nan->1203| ✓ | __floatdisf | i64 | ∅ | f32 | .. |
217- dev other __cmpdf2 // exported from __lesf2, __ledf2, __letf2 (below)204| ✓ | __floatdidf | i64 | ∅ | f64 | .. |
218- dev other __cmptf2 // But: if NaN is a possibility, use another routine.205| ✓ | __floatditf | i64 | ∅ | f128 | .. |
219- dev other __unordsf2 // a,b:f32, (a==+-NaN or b==+-NaN) -> !=0, else -> 0206| ✓ | __floatdixf | i64 | ∅ | f80 | .. |
220- dev other __unorddf2 // __only reliable for (input!=NaN)__207| ✓ | __floattihf | i128 | ∅ | f16 | .. |
221- dev other __unordtf2 // TODO: missing tests208| ✓ | __floattisf | i128 | ∅ | f32 | .. |
222- dev other __eqsf2 // (a!=NaN) and (b!=Nan) and (a==b) -> output=0209| ✓ | __floattidf | i128 | ∅ | f64 | .. |
223- dev other __eqdf2 //210| ✓ | __floattitf | i128 | ∅ | f128 | .. |
224- dev other __eqtf2 //211| ✓ | __floattixf | i128 | ∅ | f80 | .. |
225- dev other __nesf2 // (a==NaN) or (b==Nan) or (a!=b) -> output!=0212| ✓ | __floatunsihf | u32 | ∅ | f16 | uint_to_float conversions |
226- dev other __nedf2 //213| ✓ | __floatunsisf | u32 | ∅ | f32 | .. |
227- dev other __netf2 // __eqtf2 and __netf2 have same return value -> tested with __eqsf2214| ✓ | __floatunsidf | u32 | ∅ | f64 | .. |
228- dev other __gesf2 // (a!=Nan) and (b!=Nan) and (a>=b) -> output>=0215| ✓ | __floatunsitf | u32 | ∅ | f128 | .. |
229- dev other __gedf2 //216| ✓ | __floatunsixf | u32 | ∅ | f80 | .. |
230- dev other __getf2 // TODO: missing tests217| ✓ | __floatundihf | u64 | ∅ | f16 | .. |
231- dev other __ltsf2 // (a!=Nan) and (b!=Nan) and (a<b) -> output<0218| ✓ | __floatundisf | u64 | ∅ | f32 | .. |
232- dev other __ltdf2 //219| ✓ | __floatundidf | u64 | ∅ | f64 | .. |
233- dev other __lttf2 // TODO: missing tests220| ✓ | __floatunditf | u64 | ∅ | f128 | .. |
234- dev other __lesf2 // (a!=Nan) and (b!=Nan) and (a<=b) -> output<=0221| ✓ | __floatundixf | u64 | ∅ | f80 | .. |
235- dev other __ledf2 //222| ✓ | __floatuntihf | u128 | ∅ | f16 | .. |
236- dev other __letf2 // TODO: missing tests223| ✓ | __floatuntisf | u128 | ∅ | f32 | .. |
237- dev other __gtsf2 // (a!=Nan) and (b!=Nan) and (a>b) -> output>0224| ✓ | __floatuntidf | u128 | ∅ | f64 | .. |
238- dev other __gtdf2 //225| ✓ | __floatuntitf | u128 | ∅ | f128 | .. |
239- dev other __gttf2 // TODO: missing tests226| ✓ | __floatuntixf | u128 | ∅ | f80 | .. |
240227| | | | | | **Float Comparison** |
241#### Float Arithmetic228| ✓ | __cmphf2 | f16 | f16 | i32 | `(a<b)->-1, (a==b)->0, (a>b)->1, Nan->1` |
242229| ✓ | __cmpsf2 | f32 | f32 | i32 | exported from __lesf2, __ledf2, __letf2 (below) |
243- dev unclear __addsf3 // a + b f32, TODO: missing tests230| ✓ | __cmpdf2 | f64 | f64 | i32 | But: if NaN is a possibility, use another routine. |
244- dev unclear __adddf3 // a + b f64, TODO: missing tests231| ✓ | __cmptf2 | f128 | f128 | i32 | .. |
245- dev unclear __addtf3 // a + b f128232| ✓ | __cmpxf2 | f80 | f80 | i32 | .. |
246- dev unclear __addxf3 // a + b f80233| ✓ | _Qp_cmp |*f128 |*f128 | i32 | .. SPARC |
247- dev unclear __aeabi_fadd // a + b f64 ARM: AAPCS234| ✓ | __unordhf2 | f16 | f16 | i32 | `(a==+-NaN or b==+-NaN) -> !=0, else -> 0` |
248- dev unclear __aeabi_dadd // a + b f64 ARM: AAPCS235| ✓ | __unordsf2 | f32 | f32 | i32 | .. |
249- dev unclear __subsf3 // a - b, TODO: missing tests236| ✓ | __unorddf2 | f64 | f64 | i32 | Note: only reliable for (input!=NaN) |
250- dev unclear __subdf3 // a - b, TODO: missing tests237| ✓ | __unordtf2 | f128 | f128 | i32 | .. |
251- dev unclear __subtf3 // a - b238| ✓ | __unordxf2 | f80 | f80 | i32 | .. |
252- dev unclear __subxf3 // a - b f80, TODO: missing tests239| ✓ | __aeabi_fcmpun | f32 | f32 | i32 | .. ARM |
253- dev unclear __aeabi_fsub // a - b f64 ARM: AAPCS240| ✓ | __aeabi_dcmpun | f32 | f32 | i32 | .. ARM |
254- dev unclear __aeabi_dsub // a - b f64 ARM: AAPCS241| ✓ | __unordkf2 | f128 | f128 | i32 | .. PPC |
255- dev unclear __mulsf3 // a * b, TODO: missing tests242| ✓ | __eqhf2 | f16 | f16 | i32 | `(a!=NaN) and (b!=Nan) and (a==b) -> output=0` |
256- dev unclear __muldf3 // a * b, TODO: missing tests243| ✓ | __eqsf2 | f32 | f32 | i32 | .. |
257- dev unclear __multf3 // a * b244| ✓ | __eqdf2 | f64 | f64 | i32 | .. |
258- dev unclear __mulxf3 // a * b245| ✓ | __eqtf2 | f128 | f128 | i32 | .. |
259- dev unclear __divsf3 // a / b, TODO: review tests246| ✓ | __eqxf2 | f80 | f80 | i32 | .. |
260- dev unclear __divdf3 // a / b, TODO: review tests247| ✓ | __aeabi_fcmpeq | f32 | f32 | i32 | .. ARM |
261- dev unclear __divtf3 // a / b248| ✓ | __aeabi_dcmpeq | f32 | f32 | i32 | .. ARM |
262- dev unclear __divxf3 // a / b249| ✓ | __eqkf2 | f128 | f128 | i32 | .. PPC |
263- dev unclear __negsf2 // -a symbol-level compatibility: libgcc uses this for the rl78250| ✓ | _Qp_feq |*f128 |*f128 | bool | .. SPARC |
264- dev unclear __negdf2 // -a unnecessary: can be lowered directly to a xor251| ✓ | __nehf2 | f16 | f16 | i32 | `(a==NaN) or (b==Nan) or (a!=b) -> output!=0` |
265- dev unclear __negtf2 // -a, TODO: missing tests252| ✓ | __nesf2 | f32 | f32 | i32 | Note: __eqXf2 and __neXf2 have same return value |
266- dev unclear __negxf2 // -a, TODO: missing tests253| ✓ | __nedf2 | f64 | f64 | i32 | .. |
267254| ✓ | __netf2 | f128 | f128 | i32 | .. |
268#### Floating point raised to integer power255| ✓ | __nexf2 | f80 | f80 | i32 | .. |
269- dev unclear __powisf2 // a ^ b, TODO256| ✓ | __nekf2 | f128 | f128 | i32 | .. PPC |
270- dev unclear __powidf2 //257| ✓ | _Qp_fne |*f128 |*f128 | bool | .. SPARC |
271- dev unclear __powitf2 //258| ✓ | __gehf2 | f16 | f16 | i32 | `(a!=Nan) and (b!=Nan) and (a>=b) -> output>=0` |
272- dev unclear __powixf2 //259| ✓ | __gesf2 | f32 | f32 | i32 | .. |
273- dev unclear __mulsc3 // (a+ib) * (c+id)260| ✓ | __gedf2 | f64 | f64 | i32 | .. |
274- dev unclear __muldc3 //261| ✓ | __getf2 | f128 | f128 | i32 | .. |
275- dev unclear __multc3 //262| ✓ | __gexf2 | f80 | f80 | i32 | .. |
276- dev unclear __mulxc3 //263| ✓ | __gekf2 | f128 | f128 | i32 | .. PPC |
277- dev unclear __divsc3 // (a+ib) * / (c+id)264| ✓ | _Qp_fge |*f128 |*f128 | bool | .. SPARC |
278- dev unclear __divdc3 //265| ✓ | __lthf2 | f16 | f16 | i32 | `(a!=Nan) and (b!=Nan) and (a<b) -> output<0` |
279- dev unclear __divtc3 //266| ✓ | __ltsf2 | f32 | f32 | i32 | .. |
280- dev unclear __divxc3 //267| ✓ | __ltdf2 | f64 | f64 | i32 | .. |
281268| ✓ | __lttf2 | f128 | f128 | i32 | .. |
282## Decimal float library routines269| ✓ | __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
284BID means Binary Integer Decimal encoding, DPD means Densely Packed Decimal encoding.355BID means Binary Integer Decimal encoding, DPD means Densely Packed Decimal encoding.
285BID should be only chosen for binary data, DPD for decimal data (ASCII, Unicode etc).356BID 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 representable357For example the number 0.2 is not accurately representable in binary data.
287in binary like the number 0.2.358
288359| Done | Name | a | b | Out | Comment |
289All routines are TODO.360| ------ | ------------- | --------- | --------- | --------- | ---------------------------- |
290361| | | | | | **Decimal Float Conversion** |
291#### Decimal float Conversion362| ✗ | __dpd_extendsddd2 | dec32 | ∅ | dec64 | conversion |
292363| ✗ | __bid_extendsddd2 | dec32 | ∅ | dec64 | .. |
293- __dpd_extendsddd2 // dec32->dec64364| ✗ | __dpd_extendsdtd2 | dec32 | ∅ | dec128| .. |
294- __bid_extendsddd2 // dec32->dec64365| ✗ | __bid_extendsdtd2 | dec32 | ∅ | dec128| .. |
295- __dpd_extendsdtd2 // dec32->dec128366| ✗ | __dpd_extendddtd2 | dec64 | ∅ | dec128| .. |
296- __bid_extendsdtd2 // dec32->dec128367| ✗ | __bid_extendddtd2 | dec64 | ∅ | dec128| .. |
297- __dpd_extendddtd2 // dec64->dec128368| ✗ | __dpd_truncddsd2 | dec64 | ∅ | dec32 | .. |
298- __bid_extendddtd2 // dec64->dec128369| ✗ | __bid_truncddsd2 | dec64 | ∅ | dec32 | .. |
299- __dpd_truncddsd2 // dec64->dec32370| ✗ | __dpd_trunctdsd2 | dec128 | ∅ | dec32 | .. |
300- __bid_truncddsd2 // dec64->dec32371| ✗ | __bid_trunctdsd2 | dec128 | ∅ | dec32 | .. |
301- __dpd_trunctdsd2 // dec128->dec32372| ✗ | __dpd_trunctddd2 | dec128 | ∅ | dec64 | .. |
302- __bid_trunctdsd2 // dec128->dec32373| ✗ | __bid_trunctddd2 | dec128 | ∅ | dec64 | .. |
303- __dpd_trunctddd2 // dec128->dec64374| ✗ | __dpd_extendsfdd | float | ∅ | dec64 | .. |
304- __bid_trunctddd2 // dec128->dec64375| ✗ | __bid_extendsfdd | float | ∅ | dec64 | .. |
305376| ✗ | __dpd_extendsftd | float | ∅ | dec128| .. |
306- __dpd_extendsfdd // float->dec64377| ✗ | __bid_extendsftd | float | ∅ | dec128| .. |
307- __bid_extendsfdd // float->dec64378| ✗ | __dpd_extenddftd | double | ∅ | dec128| .. |
308- __dpd_extendsftd // float->dec128379| ✗ | __bid_extenddftd | double | ∅ | dec128| .. |
309- __bid_extendsftd // float->dec128380| ✗ | __dpd_extendxftd |long double | ∅ | dec128| .. |
310- __dpd_extenddftd // double->dec128381| ✗ | __bid_extendxftd |long double | ∅ | dec128| .. |
311- __bid_extenddftd // double->dec128382| ✗ | __dpd_truncdfsd | double | ∅ | dec32 | .. |
312- __dpd_extendxftd // long double->dec128383| ✗ | __bid_truncdfsd | double | ∅ | dec32 | .. |
313- __bid_extendxftd // long double->dec128384| ✗ | __dpd_truncxfsd |long double | ∅ | dec32 | .. |
314- __dpd_truncdfsd // double->dec32385| ✗ | __bid_truncxfsd |long double | ∅ | dec32 | .. |
315- __bid_truncdfsd // double->dec32386| ✗ | __dpd_trunctfsd |long double | ∅ | dec32 | .. |
316- __dpd_truncxfsd // long double->dec32387| ✗ | __bid_trunctfsd |long double | ∅ | dec32 | .. |
317- __bid_truncxfsd // long double->dec32388| ✗ | __dpd_truncxfdd |long double | ∅ | dec64 | .. |
318- __dpd_trunctfsd // long double->dec32389| ✗ | __bid_truncxfdd |long double | ∅ | dec64 | .. |
319- __bid_trunctfsd // long double->dec32390| ✗ | __dpd_trunctfdd |long double | ∅ | dec64 | .. |
320- __dpd_truncxfdd // long double->dec64391| ✗ | __bid_trunctfdd |long double | ∅ | dec64 | .. |
321- __bid_truncxfdd // long double->dec64392| ✗ | __dpd_truncddsf | dec64 | ∅ | float | .. |
322- __dpd_trunctfdd // long double->dec64393| ✗ | __bid_truncddsf | dec64 | ∅ | float | .. |
323- __bid_trunctfdd // long double->dec64394| ✗ | __dpd_trunctdsf | dec128 | ∅ | float | .. |
324395| ✗ | __bid_trunctdsf | dec128 | ∅ | float | .. |
325- __dpd_truncddsf // dec64->float396| ✗ | __dpd_extendsddf | dec32 | ∅ | double| .. |
326- __bid_truncddsf // dec64->float397| ✗ | __bid_extendsddf | dec32 | ∅ | double| .. |
327- __dpd_trunctdsf // dec128->float398| ✗ | __dpd_trunctddf | dec128 | ∅ | double| .. |
328- __bid_trunctdsf // dec128->float399| ✗ | __bid_trunctddf | dec128 | ∅ | double| .. |
329- __dpd_extendsddf // dec32->double400| ✗ | __dpd_extendsdxf | dec32 | ∅ |long double| .. |
330- __bid_extendsddf // dec32->double401| ✗ | __bid_extendsdxf | dec32 | ∅ |long double| .. |
331- __dpd_trunctddf // dec128->double402| ✗ | __dpd_extendddxf | dec64 | ∅ |long double| .. |
332- __bid_trunctddf // dec128->double403| ✗ | __bid_extendddxf | dec64 | ∅ |long double| .. |
333- __dpd_extendsdxf // dec32->long double404| ✗ | __dpd_trunctdxf | dec128 | ∅ |long double| .. |
334- __bid_extendsdxf // dec32->long double405| ✗ | __bid_trunctdxf | dec128 | ∅ |long double| .. |
335- __dpd_extendddxf // dec64->long double406| ✗ | __dpd_extendsdtf | dec32 | ∅ |long double| .. |
336- __bid_extendddxf // dec64->long double407| ✗ | __bid_extendsdtf | dec32 | ∅ |long double| .. |
337- __dpd_trunctdxf // dec128->long double408| ✗ | __dpd_extendddtf | dec64 | ∅ |long double| .. |
338- __bid_trunctdxf // dec128->long double409| ✗ | __bid_extendddtf | dec64 | ∅ |long double| .. |
339- __dpd_extendsdtf // dec32->long double410| ✗ | __dpd_extendsfsd | float | ∅ | dec32 | same size conversions |
340- __bid_extendsdtf // dec32->long double411| ✗ | __bid_extendsfsd | float | ∅ | dec32 | .. |
341- __dpd_extendddtf // dec64->long double412| ✗ | __dpd_extenddfdd | double | ∅ | dec64 | .. |
342- __bid_extendddtf // dec64->long double413| ✗ | __bid_extenddfdd | double | ∅ | dec64 | .. |
343414| ✗ | __dpd_extendtftd |long double | ∅ | dec128| .. |
344Same size conversion:415| ✗ | __bid_extendtftd |long double | ∅ | dec128| .. |
345- __dpd_extendsfsd // float->dec32416| ✗ | __dpd_truncsdsf | dec32 | ∅ | float | .. |
346- __bid_extendsfsd // float->dec32417| ✗ | __bid_truncsdsf | dec32 | ∅ | float | .. |
347- __dpd_extenddfdd // double->dec64418| ✗ | __dpd_truncdddf | dec64 | ∅ | float | conversion |
348- __bid_extenddfdd // double->dec64419| ✗ | __bid_truncdddf | dec64 | ∅ | float | .. |
349- __dpd_extendtftd //long double->dec128420| ✗ | __dpd_trunctdtf | dec128 | ∅ |long double| .. |
350- __bid_extendtftd //long double->dec128421| ✗ | __bid_trunctdtf | dec128 | ∅ |long double| .. |
351- __dpd_truncsdsf // dec32->float422| ✗ | __dpd_fixsdsi | dec32 | ∅ | int | .. |
352- __bid_truncsdsf // dec32->float423| ✗ | __bid_fixsdsi | dec32 | ∅ | int | .. |
353- __dpd_truncdddf // dec64->float424| ✗ | __dpd_fixddsi | dec64 | ∅ | int | .. |
354- __bid_truncdddf // dec64->float425| ✗ | __bid_fixddsi | dec64 | ∅ | int | .. |
355- __dpd_trunctdtf // dec128->long double426| ✗ | __dpd_fixtdsi | dec128 | ∅ | int | .. |
356- __bid_trunctdtf // dec128->long double427| ✗ | __bid_fixtdsi | dec128 | ∅ | int | .. |
357428| ✗ | __dpd_fixsddi | dec32 | ∅ | long | .. |
358- __dpd_fixsdsi // dec32->int429| ✗ | __bid_fixsddi | dec32 | ∅ | long | .. |
359- __bid_fixsdsi // dec32->int430| ✗ | __dpd_fixdddi | dec64 | ∅ | long | .. |
360- __dpd_fixddsi // dec64->int431| ✗ | __bid_fixdddi | dec64 | ∅ | long | .. |
361- __bid_fixddsi // dec64->int432| ✗ | __dpd_fixtddi | dec128 | ∅ | long | .. |
362- __dpd_fixtdsi // dec128->int433| ✗ | __bid_fixtddi | dec128 | ∅ | long | .. |
363- __bid_fixtdsi // dec128->int434| ✗ | __dpd_fixunssdsi | dec32 | ∅ |unsigned int | .. All negative values become zero. |
364435| ✗ | __bid_fixunssdsi | dec32 | ∅ |unsigned int | .. |
365- __dpd_fixsddi // dec32->long436| ✗ | __dpd_fixunsddsi | dec64 | ∅ |unsigned int | .. |
366- __bid_fixsddi // dec32->long437| ✗ | __bid_fixunsddsi | dec64 | ∅ |unsigned int | .. |
367- __dpd_fixdddi // dec64->long438| ✗ | __dpd_fixunstdsi | dec128 | ∅ |unsigned int | .. |
368- __bid_fixdddi // dec64->long439| ✗ | __bid_fixunstdsi | dec128 | ∅ |unsigned int | .. |
369- __dpd_fixtddi // dec128->long440| ✗ | __dpd_fixunssddi | dec32 | ∅ |unsigned long| .. |
370- __bid_fixtddi // dec128->long441| ✗ | __bid_fixunssddi | dec32 | ∅ |unsigned long| .. |
371442| ✗ | __dpd_fixunsdddi | dec64 | ∅ |unsigned long| .. |
372- __dpd_fixunssdsi // dec32->unsigned int, All negative values become zero.443| ✗ | __bid_fixunsdddi | dec64 | ∅ |unsigned long| .. |
373- __bid_fixunssdsi // dec32->unsigned int444| ✗ | __dpd_fixunstddi | dec128 | ∅ |unsigned long| .. |
374- __dpd_fixunsddsi // dec64->unsigned int445| ✗ | __bid_fixunstddi | dec128 | ∅ |unsigned long| .. |
375- __bid_fixunsddsi // dec64->unsigned int446| ✗ | __dpd_floatsisd | int | ∅ | dec32 | .. |
376- __dpd_fixunstdsi // dec128->unsigned int447| ✗ | __bid_floatsisd | int | ∅ | dec32 | .. |
377- __bid_fixunstdsi // dec128->unsigned int448| ✗ | __dpd_floatsidd | int | ∅ | dec64 | .. |
378449| ✗ | __bid_floatsidd | int | ∅ | dec64 | .. |
379- __dpd_fixunssddi // dec32->unsigned long, All negative values become zero.450| ✗ | __dpd_floatsitd | int | ∅ | dec128 | .. |
380- __bid_fixunssddi // dec32->unsigned long451| ✗ | __bid_floatsitd | int | ∅ | dec128 | .. |
381- __dpd_fixunsdddi // dec64->unsigned long452| ✗ | __dpd_floatdisd | long | ∅ | dec32 | .. |
382- __bid_fixunsdddi // dec64->unsigned long453| ✗ | __bid_floatdisd | long | ∅ | dec32 | .. |
383- __dpd_fixunstddi // dec128->unsigned long454| ✗ | __dpd_floatdidd | long | ∅ | dec64 | .. |
384- __bid_fixunstddi // dec128->unsigned long455| ✗ | __bid_floatdidd | long | ∅ | dec64 | .. |
385456| ✗ | __dpd_floatditd | long | ∅ | dec128 | .. |
386- __dpd_floatsisd // int->dec32457| ✗ | __bid_floatditd | long | ∅ | dec128 | .. |
387- __bid_floatsisd // int->dec32458| ✗ | __dpd_floatunssisd | unsigned int| ∅ | dec32 | .. |
388- __dpd_floatsidd // int->dec64459| ✗ | __bid_floatunssisd | unsigned int| ∅ | dec32 | .. |
389- __bid_floatsidd // int->dec64460| ✗ | __dpd_floatunssidd | unsigned int| ∅ | dec64 | .. |
390- __dpd_floatsitd // int->dec128461| ✗ | __bid_floatunssidd | unsigned int| ∅ | dec64 | .. |
391- __bid_floatsitd // int->dec128462| ✗ | __dpd_floatunssitd | unsigned int| ∅ | dec128 | .. |
392463| ✗ | __bid_floatunssitd | unsigned int| ∅ | dec128 | .. |
393- __dpd_floatdisd // long->dec32464| ✗ | __dpd_floatunsdisd |unsigned long| ∅ | dec32 | .. |
394- __bid_floatdisd // long->dec32465| ✗ | __bid_floatunsdisd |unsigned long| ∅ | dec32 | .. |
395- __dpd_floatdidd // long->dec64466| ✗ | __dpd_floatunsdidd |unsigned long| ∅ | dec64 | .. |
396- __bid_floatdidd // long->dec64467| ✗ | __bid_floatunsdidd |unsigned long| ∅ | dec64 | .. |
397- __dpd_floatditd // long->dec128468| ✗ | __dpd_floatunsditd |unsigned long| ∅ | dec128 | .. |
398- __bid_floatditd // long->dec128469| ✗ | __bid_floatunsditd |unsigned long| ∅ | dec128 | .. |
399470| | | | | | **Decimal Float Comparison** |
400- __dpd_floatunssisd // unsigned int->dec32471| ✗ | __dpd_unordsd2 | dec32 | dec32 | c_int | `a +-NaN or a +-NaN -> 1(nonzero), else -> 0` |
401- __bid_floatunssisd // unsigned int->dec32472| ✗ | __bid_unordsd2 | dec32 | dec32 | c_int | .. |
402- __dpd_floatunssidd // unsigned int->dec64473| ✗ | __dpd_unorddd2 | dec64 | dec64 | c_int | .. |
403- __bid_floatunssidd // unsigned int->dec64474| ✗ | __bid_unorddd2 | dec64 | dec64 | c_int | .. |
404- __dpd_floatunssitd // unsigned int->dec128475| ✗ | __dpd_unordtd2 | dec128 | dec128 | c_int | .. |
405- __bid_floatunssitd // unsigned int->dec128476| ✗ | __bid_unordtd2 | dec128 | dec128 | c_int | .. |
406477| ✗ | __dpd_eqsd2 | dec32 | dec32 | c_int |`a!=+-NaN and b!=+-Nan and a==b -> 0, else -> 1(nonzero)`|
407- __dpd_floatunsdisd // unsigned long->dec32478| ✗ | __bid_eqsd2 | dec32 | dec32 | c_int | .. |
408- __bid_floatunsdisd // unsigned long->dec32479| ✗ | __dpd_eqdd2 | dec64 | dec64 | c_int | .. |
409- __dpd_floatunsdidd // unsigned long->dec64480| ✗ | __bid_eqdd2 | dec64 | dec64 | c_int | .. |
410- __bid_floatunsdidd // unsigned long->dec64481| ✗ | __dpd_eqtd2 | dec128 | dec128 | c_int | .. |
411- __dpd_floatunsditd // unsigned long->dec128482| ✗ | __bid_eqtd2 | dec128 | dec128 | c_int | .. |
412- __bid_floatunsditd // unsigned long->dec128483| ✗ | __dpd_nesd2 | dec32 | dec32 | c_int | `a==+-NaN or b==+-NaN or a!=b -> 1(nonzero), else -> 0` |
413484| ✗ | __bid_nesd2 | dec32 | dec32 | c_int | .. |
414#### Decimal float Comparison485| ✗ | __dpd_nedd2 | dec64 | dec64 | c_int | .. |
415486| ✗ | __bid_nedd2 | dec64 | dec64 | c_int | .. |
416All decimal float comparison routines return c_int.487| ✗ | __dpd_netd2 | dec128 | dec128 | c_int | .. |
417488| ✗ | __bid_netd2 | dec128 | dec128 | c_int | .. |
418- __dpd_unordsd2 // a,b: dec32, a +-NaN or a +-NaN -> 1(nonzero), else -> 0489| ✗ | __dpd_gesd2 | dec32 | dec32 | c_int | `a!=+-NaN and b!=+-NaN and a>=b -> >=0, else -> <0` |
419- __bid_unordsd2 // a,b: dec32490| ✗ | __bid_gesd2 | dec32 | dec32 | c_int | .. |
420- __dpd_unorddd2 // a,b: dec64491| ✗ | __dpd_gedd2 | dec64 | dec64 | c_int | .. |
421- __bid_unorddd2 // a,b: dec64492| ✗ | __bid_gedd2 | dec64 | dec64 | c_int | .. |
422- __dpd_unordtd2 // a,b: dec128493| ✗ | __dpd_getd2 | dec128 | dec128 | c_int | .. |
423- __bid_unordtd2 // a,b: dec128494| ✗ | __bid_getd2 | dec128 | dec128 | c_int | .. |
424495| ✗ | __dpd_ltsd2 | dec32 | dec32 | c_int | `a!=+-NaN and b!=+-NaN and a<b -> <0, else -> >=0` |
425- __dpd_eqsd2 // a,b: dec32, a!=+-NaN and b!=+-Nan and a==b -> 0, else -> 1(nonzero)496| ✗ | __bid_ltsd2 | dec32 | dec32 | c_int | .. |
426- __bid_eqsd2 // a,b: dec32497| ✗ | __dpd_ltdd2 | dec64 | dec64 | c_int | .. |
427- __dpd_eqdd2 // a,b: dec64498| ✗ | __bid_ltdd2 | dec64 | dec64 | c_int | .. |
428- __bid_eqdd2 // a,b: dec64499| ✗ | __dpd_lttd2 | dec128 | dec128 | c_int | .. |
429- __dpd_eqtd2 // a,b: dec128500| ✗ | __bid_lttd2 | dec128 | dec128 | c_int | .. |
430- __bid_eqtd2 // a,b: dec128501| ✗ | __dpd_lesd2 | dec32 | dec32 | c_int | `a!=+-NaN and b!=+-NaN and a<=b -> <=0, else -> >=0` |
431502| ✗ | __bid_lesd2 | dec32 | dec32 | c_int | .. |
432- __dpd_nesd2 // a,b: dec32, a==+-NaN or b==+-NaN or a!=b -> 1(nonzero), else -> 0503| ✗ | __dpd_ledd2 | dec64 | dec64 | c_int | .. |
433- __bid_nesd2 // a,b: dec32504| ✗ | __bid_ledd2 | dec64 | dec64 | c_int | .. |
434- __dpd_nedd2 // a,b: dec64505| ✗ | __dpd_letd2 | dec128 | dec128 | c_int | .. |
435- __bid_nedd2 // a,b: dec64506| ✗ | __bid_letd2 | dec128 | dec128 | c_int | .. |
436- __dpd_netd2 // a,b: dec128507| ✗ | __dpd_gtsd2 | dec32 | dec32 | c_int | `a!=+-NaN and b!=+-NaN and a>b -> >0, else -> <=0` |
437- __bid_netd2 // a,b: dec128508| ✗ | __bid_gtsd2 | dec32 | dec32 | c_int | .. |
438509| ✗ | __dpd_gtdd2 | dec64 | dec64 | c_int | .. |
439- __dpd_gesd2 // a,b: dec32, a!=+-NaN and b!=+-NaN and a>=b -> >=0, else -> <0510| ✗ | __bid_gtdd2 | dec64 | dec64 | c_int | .. |
440- __bid_gesd2 // a,b: dec32511| ✗ | __dpd_gttd2 | dec128 | dec128 | c_int | .. |
441- __dpd_gedd2 // a,b: dec64512| ✗ | __bid_gttd2 | dec128 | dec128 | c_int | .. |
442- __bid_gedd2 // a,b: dec64513| | | | | | **Decimal Float Arithmetic**[^options] |
443- __dpd_getd2 // a,b: dec128514| ✗ | __dpd_addsd3 | dec32 | dec32 | dec32 |`a + b`|
444- __bid_getd2 // a,b: dec128515| ✗ | __bid_addsd3 | dec32 | dec32 | dec32 | .. |
445516| ✗ | __dpd_adddd3 | dec64 | dec64 | dec64 | .. |
446- __dpd_ltsd2 // a,b: dec32, a!=+-NaN and b!=+-NaN and a<b -> <0, else -> >=0517| ✗ | __bid_adddd3 | dec64 | dec64 | dec64 | .. |
447- __bid_ltsd2 // a,b: dec32518| ✗ | __dpd_addtd3 | dec128 | dec128 | dec128 | .. |
448- __dpd_ltdd2 // a,b: dec64519| ✗ | __bid_addtd3 | dec128 | dec128 | dec128 | .. |
449- __bid_ltdd2 // a,b: dec64520| ✗ | __dpd_subsd3 | dec32 | dec32 | dec32 |`a - b`|
450- __dpd_lttd2 // a,b: dec128521| ✗ | __bid_subsd3 | dec32 | dec32 | dec32 | .. |
451- __bid_lttd2 // a,b: dec128522| ✗ | __dpd_subdd3 | dec64 | dec64 | dec64 | .. |
452523| ✗ | __bid_subdd3 | dec64 | dec64 | dec64 | .. |
453- __dpd_lesd2 // a,b: dec32, a!=+-NaN and b!=+-NaN and a<=b -> <=0, else -> >=0524| ✗ | __dpd_subtd3 | dec128 | dec128 | dec128 | .. |
454- __bid_lesd2 // a,b: dec32525| ✗ | __bid_subtd3 | dec128 | dec128 | dec128 | .. |
455- __dpd_ledd2 // a,b: dec64526| ✗ | __dpd_mulsd3 | dec32 | dec32 | dec32 |`a * b`|
456- __bid_ledd2 // a,b: dec64527| ✗ | __bid_mulsd3 | dec32 | dec32 | dec32 | .. |
457- __dpd_letd2 // a,b: dec128528| ✗ | __dpd_muldd3 | dec64 | dec64 | dec64 | .. |
458- __bid_letd2 // a,b: dec128529| ✗ | __bid_muldd3 | dec64 | dec64 | dec64 | .. |
459530| ✗ | __dpd_multd3 | dec128 | dec128 | dec128 | .. |
460- __dpd_gtsd2 // a,b: dec32, a!=+-NaN and b!=+-NaN and a>b -> >0, else -> <=0531| ✗ | __bid_multd3 | dec128 | dec128 | dec128 | .. |
461- __bid_gtsd2 // a,b: dec32532| ✗ | __dpd_divsd3 | dec32 | dec32 | dec32 |`a / b`|
462- __dpd_gtdd2 // a,b: dec64533| ✗ | __bid_divsd3 | dec32 | dec32 | dec32 | .. |
463- __bid_gtdd2 // a,b: dec64534| ✗ | __dpd_divdd3 | dec64 | dec64 | dec64 | .. |
464- __dpd_gttd2 // a,b: dec128535| ✗ | __bid_divdd3 | dec64 | dec64 | dec64 | .. |
465- __bid_gttd2 // a,b: dec128536| ✗ | __dpd_divtd3 | dec128 | dec128 | dec128 | .. |
466537| ✗ | __bid_divtd3 | dec128 | dec128 | dec128 | .. |
467#### Decimal float Arithmetic538| ✗ | __dpd_negsd2 | dec32 | dec32 | dec32 | `-a` |
468539| ✗ | __bid_negsd2 | dec32 | dec32 | dec32 | .. |
469These numbers include options with routines for +-0 and +-Nan.540| ✗ | __dpd_negdd2 | dec64 | dec64 | dec64 | .. |
470541| ✗ | __bid_negdd2 | dec64 | dec64 | dec64 | .. |
471- __dpd_addsd3 // a,b: dec32 -> dec32, a + b542| ✗ | __dpd_negtd2 | dec128 | dec128 | dec128 | .. |
472- __bid_addsd3 // a,b: dec32 -> dec32543| ✗ | __bid_negtd2 | dec128 | dec128 | dec128 | .. |
473- __dpd_adddd3 // a,b: dec64 -> dec64544
474- __bid_adddd3 // a,b: dec64 -> dec64545[^options]: These numbers include options with routines for +-0 and +-Nan.
475- __dpd_addtd3 // a,b: dec128-> dec128546
476- __bid_addtd3 // a,b: dec128-> dec128547Fixed-point fractional library routines
477- __dpd_subsd3 // a,b: dec32, a - b548
478- __bid_subsd3 // a,b: dec32 -> dec32549TODO brief explanation + implementation
479- __dpd_subdd3 // a,b: dec64 ..550
480- __bid_subdd3 // a,b: dec64551| Done | Name | a | b | Out | Comment |
481- __dpd_subtd3 // a,b: dec128552| ------ | ------------- | --------- | --------- | --------- | -------------------------- |
482- __bid_subtd3 // a,b: dec128553| | | | | | **Fixed-Point Fractional** |
483- __dpd_mulsd3 // a,b: dec32, a * b554
484- __bid_mulsd3 // a,b: dec32 -> dec32555Further content:
485- __dpd_muldd3 // a,b: dec64 ..556- aarch64 outline atomics
486- __bid_muldd3 // a,b: dec64557- atomics
487- __dpd_multd3 // a,b: dec128558- msvc things like _alldiv, _aulldiv, _allrem
488- __bid_multd3 // a,b: dec128559- clear cache
489- __dpd_divsd3 // a,b: dec32, a / b560- tls emulation
490- __bid_divsd3 // a,b: dec32 -> dec32561- math routines (cos, sin, tan, ceil, floor, exp, exp2, fabs, log, log10, log2, sincos, sqrt)
491- __dpd_divdd3 // a,b: dec64 ..562- bcmp
492- __bid_divdd3 // a,b: dec64563- ieee float routines (fma, fmax, fmin, fmod, fabs, float rounding, )
493- __dpd_divtd3 // a,b: dec128564- arm routines (memory routines + memclr [setting to 0], divmod routines and stubs for unwind_cpp)
494- __bid_divtd3 // a,b: dec128565- memory routines (memcmp, memcpy, memset, memmove)
495- __dpd_negsd2 // a,b: dec32, -a566- objective-c __isPlatformVersionAtLeast check
496- __bid_negsd2 // a,b: dec32 -> dec32567- stack probe routines
497- __dpd_negdd2 // a,b: dec64 ..568
498- __bid_negdd2 // a,b: dec64569Future work
499- __dpd_negtd2 // a,b: dec128570
500- __bid_negtd2 // a,b: dec128571Arbitrary length integer library routines
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"
lib/compiler_rt/atomics.zig+104
...@@ -192,6 +192,10 @@ fn __atomic_load_8(src: *u64, model: i32) callconv(.C) u64 {...@@ -192,6 +192,10 @@ fn __atomic_load_8(src: *u64, model: i32) callconv(.C) u64 {
192 return atomic_load_N(u64, src, model);192 return atomic_load_N(u64, src, model);
193}193}
194194
195fn __atomic_load_16(src: *u128, model: i32) callconv(.C) u128 {
196 return atomic_load_N(u128, src, model);
197}
198
195inline fn atomic_store_N(comptime T: type, dst: *T, value: T, model: i32) void {199inline fn atomic_store_N(comptime T: type, dst: *T, value: T, model: i32) void {
196 _ = model;200 _ = model;
197 if (@sizeOf(T) > largest_atomic_size) {201 if (@sizeOf(T) > largest_atomic_size) {
...@@ -219,6 +223,10 @@ fn __atomic_store_8(dst: *u64, value: u64, model: i32) callconv(.C) void {...@@ -219,6 +223,10 @@ fn __atomic_store_8(dst: *u64, value: u64, model: i32) callconv(.C) void {
219 return atomic_store_N(u64, dst, value, model);223 return atomic_store_N(u64, dst, value, model);
220}224}
221225
226fn __atomic_store_16(dst: *u128, value: u128, model: i32) callconv(.C) void {
227 return atomic_store_N(u128, dst, value, model);
228}
229
222fn wideUpdate(comptime T: type, ptr: *T, val: T, update: anytype) T {230fn wideUpdate(comptime T: type, ptr: *T, val: T, update: anytype) T {
223 const WideAtomic = std.meta.Int(.unsigned, smallest_atomic_fetch_exch_size * 8);231 const WideAtomic = std.meta.Int(.unsigned, smallest_atomic_fetch_exch_size * 8);
224232
...@@ -282,6 +290,10 @@ fn __atomic_exchange_8(ptr: *u64, val: u64, model: i32) callconv(.C) u64 {...@@ -282,6 +290,10 @@ fn __atomic_exchange_8(ptr: *u64, val: u64, model: i32) callconv(.C) u64 {
282 return atomic_exchange_N(u64, ptr, val, model);290 return atomic_exchange_N(u64, ptr, val, model);
283}291}
284292
293fn __atomic_exchange_16(ptr: *u128, val: u128, model: i32) callconv(.C) u128 {
294 return atomic_exchange_N(u128, ptr, val, model);
295}
296
285inline fn atomic_compare_exchange_N(297inline fn atomic_compare_exchange_N(
286 comptime T: type,298 comptime T: type,
287 ptr: *T,299 ptr: *T,
...@@ -327,6 +339,10 @@ fn __atomic_compare_exchange_8(ptr: *u64, expected: *u64, desired: u64, success:...@@ -327,6 +339,10 @@ fn __atomic_compare_exchange_8(ptr: *u64, expected: *u64, desired: u64, success:
327 return atomic_compare_exchange_N(u64, ptr, expected, desired, success, failure);339 return atomic_compare_exchange_N(u64, ptr, expected, desired, success, failure);
328}340}
329341
342fn __atomic_compare_exchange_16(ptr: *u128, expected: *u128, desired: u128, success: i32, failure: i32) callconv(.C) i32 {
343 return atomic_compare_exchange_N(u128, ptr, expected, desired, success, failure);
344}
345
330inline fn fetch_op_N(comptime T: type, comptime op: std.builtin.AtomicRmwOp, ptr: *T, val: T, model: i32) T {346inline fn fetch_op_N(comptime T: type, comptime op: std.builtin.AtomicRmwOp, ptr: *T, val: T, model: i32) T {
331 _ = model;347 _ = model;
332 const Updater = struct {348 const Updater = struct {
...@@ -338,6 +354,8 @@ inline fn fetch_op_N(comptime T: type, comptime op: std.builtin.AtomicRmwOp, ptr...@@ -338,6 +354,8 @@ inline fn fetch_op_N(comptime T: type, comptime op: std.builtin.AtomicRmwOp, ptr
338 .Nand => ~(old & new),354 .Nand => ~(old & new),
339 .Or => old | new,355 .Or => old | new,
340 .Xor => old ^ new,356 .Xor => old ^ new,
357 .Max => @max(old, new),
358 .Min => @min(old, new),
341 else => @compileError("unsupported atomic op"),359 else => @compileError("unsupported atomic op"),
342 };360 };
343 }361 }
...@@ -374,6 +392,10 @@ fn __atomic_fetch_add_8(ptr: *u64, val: u64, model: i32) callconv(.C) u64 {...@@ -374,6 +392,10 @@ fn __atomic_fetch_add_8(ptr: *u64, val: u64, model: i32) callconv(.C) u64 {
374 return fetch_op_N(u64, .Add, ptr, val, model);392 return fetch_op_N(u64, .Add, ptr, val, model);
375}393}
376394
395fn __atomic_fetch_add_16(ptr: *u128, val: u128, model: i32) callconv(.C) u128 {
396 return fetch_op_N(u128, .Add, ptr, val, model);
397}
398
377fn __atomic_fetch_sub_1(ptr: *u8, val: u8, model: i32) callconv(.C) u8 {399fn __atomic_fetch_sub_1(ptr: *u8, val: u8, model: i32) callconv(.C) u8 {
378 return fetch_op_N(u8, .Sub, ptr, val, model);400 return fetch_op_N(u8, .Sub, ptr, val, model);
379}401}
...@@ -390,6 +412,10 @@ fn __atomic_fetch_sub_8(ptr: *u64, val: u64, model: i32) callconv(.C) u64 {...@@ -390,6 +412,10 @@ fn __atomic_fetch_sub_8(ptr: *u64, val: u64, model: i32) callconv(.C) u64 {
390 return fetch_op_N(u64, .Sub, ptr, val, model);412 return fetch_op_N(u64, .Sub, ptr, val, model);
391}413}
392414
415fn __atomic_fetch_sub_16(ptr: *u128, val: u128, model: i32) callconv(.C) u128 {
416 return fetch_op_N(u128, .Sub, ptr, val, model);
417}
418
393fn __atomic_fetch_and_1(ptr: *u8, val: u8, model: i32) callconv(.C) u8 {419fn __atomic_fetch_and_1(ptr: *u8, val: u8, model: i32) callconv(.C) u8 {
394 return fetch_op_N(u8, .And, ptr, val, model);420 return fetch_op_N(u8, .And, ptr, val, model);
395}421}
...@@ -406,6 +432,10 @@ fn __atomic_fetch_and_8(ptr: *u64, val: u64, model: i32) callconv(.C) u64 {...@@ -406,6 +432,10 @@ fn __atomic_fetch_and_8(ptr: *u64, val: u64, model: i32) callconv(.C) u64 {
406 return fetch_op_N(u64, .And, ptr, val, model);432 return fetch_op_N(u64, .And, ptr, val, model);
407}433}
408434
435fn __atomic_fetch_and_16(ptr: *u128, val: u128, model: i32) callconv(.C) u128 {
436 return fetch_op_N(u128, .And, ptr, val, model);
437}
438
409fn __atomic_fetch_or_1(ptr: *u8, val: u8, model: i32) callconv(.C) u8 {439fn __atomic_fetch_or_1(ptr: *u8, val: u8, model: i32) callconv(.C) u8 {
410 return fetch_op_N(u8, .Or, ptr, val, model);440 return fetch_op_N(u8, .Or, ptr, val, model);
411}441}
...@@ -422,6 +452,10 @@ fn __atomic_fetch_or_8(ptr: *u64, val: u64, model: i32) callconv(.C) u64 {...@@ -422,6 +452,10 @@ fn __atomic_fetch_or_8(ptr: *u64, val: u64, model: i32) callconv(.C) u64 {
422 return fetch_op_N(u64, .Or, ptr, val, model);452 return fetch_op_N(u64, .Or, ptr, val, model);
423}453}
424454
455fn __atomic_fetch_or_16(ptr: *u128, val: u128, model: i32) callconv(.C) u128 {
456 return fetch_op_N(u128, .Or, ptr, val, model);
457}
458
425fn __atomic_fetch_xor_1(ptr: *u8, val: u8, model: i32) callconv(.C) u8 {459fn __atomic_fetch_xor_1(ptr: *u8, val: u8, model: i32) callconv(.C) u8 {
426 return fetch_op_N(u8, .Xor, ptr, val, model);460 return fetch_op_N(u8, .Xor, ptr, val, model);
427}461}
...@@ -438,6 +472,10 @@ fn __atomic_fetch_xor_8(ptr: *u64, val: u64, model: i32) callconv(.C) u64 {...@@ -438,6 +472,10 @@ fn __atomic_fetch_xor_8(ptr: *u64, val: u64, model: i32) callconv(.C) u64 {
438 return fetch_op_N(u64, .Xor, ptr, val, model);472 return fetch_op_N(u64, .Xor, ptr, val, model);
439}473}
440474
475fn __atomic_fetch_xor_16(ptr: *u128, val: u128, model: i32) callconv(.C) u128 {
476 return fetch_op_N(u128, .Xor, ptr, val, model);
477}
478
441fn __atomic_fetch_nand_1(ptr: *u8, val: u8, model: i32) callconv(.C) u8 {479fn __atomic_fetch_nand_1(ptr: *u8, val: u8, model: i32) callconv(.C) u8 {
442 return fetch_op_N(u8, .Nand, ptr, val, model);480 return fetch_op_N(u8, .Nand, ptr, val, model);
443}481}
...@@ -454,6 +492,50 @@ fn __atomic_fetch_nand_8(ptr: *u64, val: u64, model: i32) callconv(.C) u64 {...@@ -454,6 +492,50 @@ fn __atomic_fetch_nand_8(ptr: *u64, val: u64, model: i32) callconv(.C) u64 {
454 return fetch_op_N(u64, .Nand, ptr, val, model);492 return fetch_op_N(u64, .Nand, ptr, val, model);
455}493}
456494
495fn __atomic_fetch_nand_16(ptr: *u128, val: u128, model: i32) callconv(.C) u128 {
496 return fetch_op_N(u128, .Nand, ptr, val, model);
497}
498
499fn __atomic_fetch_umax_1(ptr: *u8, val: u8, model: i32) callconv(.C) u8 {
500 return fetch_op_N(u8, .Max, ptr, val, model);
501}
502
503fn __atomic_fetch_umax_2(ptr: *u16, val: u16, model: i32) callconv(.C) u16 {
504 return fetch_op_N(u16, .Max, ptr, val, model);
505}
506
507fn __atomic_fetch_umax_4(ptr: *u32, val: u32, model: i32) callconv(.C) u32 {
508 return fetch_op_N(u32, .Max, ptr, val, model);
509}
510
511fn __atomic_fetch_umax_8(ptr: *u64, val: u64, model: i32) callconv(.C) u64 {
512 return fetch_op_N(u64, .Max, ptr, val, model);
513}
514
515fn __atomic_fetch_umax_16(ptr: *u128, val: u128, model: i32) callconv(.C) u128 {
516 return fetch_op_N(u128, .Max, ptr, val, model);
517}
518
519fn __atomic_fetch_umin_1(ptr: *u8, val: u8, model: i32) callconv(.C) u8 {
520 return fetch_op_N(u8, .Min, ptr, val, model);
521}
522
523fn __atomic_fetch_umin_2(ptr: *u16, val: u16, model: i32) callconv(.C) u16 {
524 return fetch_op_N(u16, .Min, ptr, val, model);
525}
526
527fn __atomic_fetch_umin_4(ptr: *u32, val: u32, model: i32) callconv(.C) u32 {
528 return fetch_op_N(u32, .Min, ptr, val, model);
529}
530
531fn __atomic_fetch_umin_8(ptr: *u64, val: u64, model: i32) callconv(.C) u64 {
532 return fetch_op_N(u64, .Min, ptr, val, model);
533}
534
535fn __atomic_fetch_umin_16(ptr: *u128, val: u128, model: i32) callconv(.C) u128 {
536 return fetch_op_N(u128, .Min, ptr, val, model);
537}
538
457comptime {539comptime {
458 if (supports_atomic_ops and builtin.object_format != .c) {540 if (supports_atomic_ops and builtin.object_format != .c) {
459 @export(__atomic_load, .{ .name = "__atomic_load", .linkage = linkage, .visibility = visibility });541 @export(__atomic_load, .{ .name = "__atomic_load", .linkage = linkage, .visibility = visibility });
...@@ -465,50 +547,72 @@ comptime {...@@ -465,50 +547,72 @@ comptime {
465 @export(__atomic_fetch_add_2, .{ .name = "__atomic_fetch_add_2", .linkage = linkage, .visibility = visibility });547 @export(__atomic_fetch_add_2, .{ .name = "__atomic_fetch_add_2", .linkage = linkage, .visibility = visibility });
466 @export(__atomic_fetch_add_4, .{ .name = "__atomic_fetch_add_4", .linkage = linkage, .visibility = visibility });548 @export(__atomic_fetch_add_4, .{ .name = "__atomic_fetch_add_4", .linkage = linkage, .visibility = visibility });
467 @export(__atomic_fetch_add_8, .{ .name = "__atomic_fetch_add_8", .linkage = linkage, .visibility = visibility });549 @export(__atomic_fetch_add_8, .{ .name = "__atomic_fetch_add_8", .linkage = linkage, .visibility = visibility });
550 @export(__atomic_fetch_add_16, .{ .name = "__atomic_fetch_add_16", .linkage = linkage, .visibility = visibility });
468551
469 @export(__atomic_fetch_sub_1, .{ .name = "__atomic_fetch_sub_1", .linkage = linkage, .visibility = visibility });552 @export(__atomic_fetch_sub_1, .{ .name = "__atomic_fetch_sub_1", .linkage = linkage, .visibility = visibility });
470 @export(__atomic_fetch_sub_2, .{ .name = "__atomic_fetch_sub_2", .linkage = linkage, .visibility = visibility });553 @export(__atomic_fetch_sub_2, .{ .name = "__atomic_fetch_sub_2", .linkage = linkage, .visibility = visibility });
471 @export(__atomic_fetch_sub_4, .{ .name = "__atomic_fetch_sub_4", .linkage = linkage, .visibility = visibility });554 @export(__atomic_fetch_sub_4, .{ .name = "__atomic_fetch_sub_4", .linkage = linkage, .visibility = visibility });
472 @export(__atomic_fetch_sub_8, .{ .name = "__atomic_fetch_sub_8", .linkage = linkage, .visibility = visibility });555 @export(__atomic_fetch_sub_8, .{ .name = "__atomic_fetch_sub_8", .linkage = linkage, .visibility = visibility });
556 @export(__atomic_fetch_sub_16, .{ .name = "__atomic_fetch_sub_16", .linkage = linkage, .visibility = visibility });
473557
474 @export(__atomic_fetch_and_1, .{ .name = "__atomic_fetch_and_1", .linkage = linkage, .visibility = visibility });558 @export(__atomic_fetch_and_1, .{ .name = "__atomic_fetch_and_1", .linkage = linkage, .visibility = visibility });
475 @export(__atomic_fetch_and_2, .{ .name = "__atomic_fetch_and_2", .linkage = linkage, .visibility = visibility });559 @export(__atomic_fetch_and_2, .{ .name = "__atomic_fetch_and_2", .linkage = linkage, .visibility = visibility });
476 @export(__atomic_fetch_and_4, .{ .name = "__atomic_fetch_and_4", .linkage = linkage, .visibility = visibility });560 @export(__atomic_fetch_and_4, .{ .name = "__atomic_fetch_and_4", .linkage = linkage, .visibility = visibility });
477 @export(__atomic_fetch_and_8, .{ .name = "__atomic_fetch_and_8", .linkage = linkage, .visibility = visibility });561 @export(__atomic_fetch_and_8, .{ .name = "__atomic_fetch_and_8", .linkage = linkage, .visibility = visibility });
562 @export(__atomic_fetch_and_16, .{ .name = "__atomic_fetch_and_16", .linkage = linkage, .visibility = visibility });
478563
479 @export(__atomic_fetch_or_1, .{ .name = "__atomic_fetch_or_1", .linkage = linkage, .visibility = visibility });564 @export(__atomic_fetch_or_1, .{ .name = "__atomic_fetch_or_1", .linkage = linkage, .visibility = visibility });
480 @export(__atomic_fetch_or_2, .{ .name = "__atomic_fetch_or_2", .linkage = linkage, .visibility = visibility });565 @export(__atomic_fetch_or_2, .{ .name = "__atomic_fetch_or_2", .linkage = linkage, .visibility = visibility });
481 @export(__atomic_fetch_or_4, .{ .name = "__atomic_fetch_or_4", .linkage = linkage, .visibility = visibility });566 @export(__atomic_fetch_or_4, .{ .name = "__atomic_fetch_or_4", .linkage = linkage, .visibility = visibility });
482 @export(__atomic_fetch_or_8, .{ .name = "__atomic_fetch_or_8", .linkage = linkage, .visibility = visibility });567 @export(__atomic_fetch_or_8, .{ .name = "__atomic_fetch_or_8", .linkage = linkage, .visibility = visibility });
568 @export(__atomic_fetch_or_16, .{ .name = "__atomic_fetch_or_16", .linkage = linkage, .visibility = visibility });
483569
484 @export(__atomic_fetch_xor_1, .{ .name = "__atomic_fetch_xor_1", .linkage = linkage, .visibility = visibility });570 @export(__atomic_fetch_xor_1, .{ .name = "__atomic_fetch_xor_1", .linkage = linkage, .visibility = visibility });
485 @export(__atomic_fetch_xor_2, .{ .name = "__atomic_fetch_xor_2", .linkage = linkage, .visibility = visibility });571 @export(__atomic_fetch_xor_2, .{ .name = "__atomic_fetch_xor_2", .linkage = linkage, .visibility = visibility });
486 @export(__atomic_fetch_xor_4, .{ .name = "__atomic_fetch_xor_4", .linkage = linkage, .visibility = visibility });572 @export(__atomic_fetch_xor_4, .{ .name = "__atomic_fetch_xor_4", .linkage = linkage, .visibility = visibility });
487 @export(__atomic_fetch_xor_8, .{ .name = "__atomic_fetch_xor_8", .linkage = linkage, .visibility = visibility });573 @export(__atomic_fetch_xor_8, .{ .name = "__atomic_fetch_xor_8", .linkage = linkage, .visibility = visibility });
574 @export(__atomic_fetch_xor_16, .{ .name = "__atomic_fetch_xor_16", .linkage = linkage, .visibility = visibility });
488575
489 @export(__atomic_fetch_nand_1, .{ .name = "__atomic_fetch_nand_1", .linkage = linkage, .visibility = visibility });576 @export(__atomic_fetch_nand_1, .{ .name = "__atomic_fetch_nand_1", .linkage = linkage, .visibility = visibility });
490 @export(__atomic_fetch_nand_2, .{ .name = "__atomic_fetch_nand_2", .linkage = linkage, .visibility = visibility });577 @export(__atomic_fetch_nand_2, .{ .name = "__atomic_fetch_nand_2", .linkage = linkage, .visibility = visibility });
491 @export(__atomic_fetch_nand_4, .{ .name = "__atomic_fetch_nand_4", .linkage = linkage, .visibility = visibility });578 @export(__atomic_fetch_nand_4, .{ .name = "__atomic_fetch_nand_4", .linkage = linkage, .visibility = visibility });
492 @export(__atomic_fetch_nand_8, .{ .name = "__atomic_fetch_nand_8", .linkage = linkage, .visibility = visibility });579 @export(__atomic_fetch_nand_8, .{ .name = "__atomic_fetch_nand_8", .linkage = linkage, .visibility = visibility });
580 @export(__atomic_fetch_nand_16, .{ .name = "__atomic_fetch_nand_16", .linkage = linkage, .visibility = visibility });
581
582 @export(__atomic_fetch_umax_1, .{ .name = "__atomic_fetch_umax_1", .linkage = linkage, .visibility = visibility });
583 @export(__atomic_fetch_umax_2, .{ .name = "__atomic_fetch_umax_2", .linkage = linkage, .visibility = visibility });
584 @export(__atomic_fetch_umax_4, .{ .name = "__atomic_fetch_umax_4", .linkage = linkage, .visibility = visibility });
585 @export(__atomic_fetch_umax_8, .{ .name = "__atomic_fetch_umax_8", .linkage = linkage, .visibility = visibility });
586 @export(__atomic_fetch_umax_16, .{ .name = "__atomic_fetch_umax_16", .linkage = linkage, .visibility = visibility });
587
588 @export(__atomic_fetch_umin_1, .{ .name = "__atomic_fetch_umin_1", .linkage = linkage, .visibility = visibility });
589 @export(__atomic_fetch_umin_2, .{ .name = "__atomic_fetch_umin_2", .linkage = linkage, .visibility = visibility });
590 @export(__atomic_fetch_umin_4, .{ .name = "__atomic_fetch_umin_4", .linkage = linkage, .visibility = visibility });
591 @export(__atomic_fetch_umin_8, .{ .name = "__atomic_fetch_umin_8", .linkage = linkage, .visibility = visibility });
592 @export(__atomic_fetch_umin_16, .{ .name = "__atomic_fetch_umin_16", .linkage = linkage, .visibility = visibility });
493593
494 @export(__atomic_load_1, .{ .name = "__atomic_load_1", .linkage = linkage, .visibility = visibility });594 @export(__atomic_load_1, .{ .name = "__atomic_load_1", .linkage = linkage, .visibility = visibility });
495 @export(__atomic_load_2, .{ .name = "__atomic_load_2", .linkage = linkage, .visibility = visibility });595 @export(__atomic_load_2, .{ .name = "__atomic_load_2", .linkage = linkage, .visibility = visibility });
496 @export(__atomic_load_4, .{ .name = "__atomic_load_4", .linkage = linkage, .visibility = visibility });596 @export(__atomic_load_4, .{ .name = "__atomic_load_4", .linkage = linkage, .visibility = visibility });
497 @export(__atomic_load_8, .{ .name = "__atomic_load_8", .linkage = linkage, .visibility = visibility });597 @export(__atomic_load_8, .{ .name = "__atomic_load_8", .linkage = linkage, .visibility = visibility });
598 @export(__atomic_load_16, .{ .name = "__atomic_load_16", .linkage = linkage, .visibility = visibility });
498599
499 @export(__atomic_store_1, .{ .name = "__atomic_store_1", .linkage = linkage, .visibility = visibility });600 @export(__atomic_store_1, .{ .name = "__atomic_store_1", .linkage = linkage, .visibility = visibility });
500 @export(__atomic_store_2, .{ .name = "__atomic_store_2", .linkage = linkage, .visibility = visibility });601 @export(__atomic_store_2, .{ .name = "__atomic_store_2", .linkage = linkage, .visibility = visibility });
501 @export(__atomic_store_4, .{ .name = "__atomic_store_4", .linkage = linkage, .visibility = visibility });602 @export(__atomic_store_4, .{ .name = "__atomic_store_4", .linkage = linkage, .visibility = visibility });
502 @export(__atomic_store_8, .{ .name = "__atomic_store_8", .linkage = linkage, .visibility = visibility });603 @export(__atomic_store_8, .{ .name = "__atomic_store_8", .linkage = linkage, .visibility = visibility });
604 @export(__atomic_store_16, .{ .name = "__atomic_store_16", .linkage = linkage, .visibility = visibility });
503605
504 @export(__atomic_exchange_1, .{ .name = "__atomic_exchange_1", .linkage = linkage, .visibility = visibility });606 @export(__atomic_exchange_1, .{ .name = "__atomic_exchange_1", .linkage = linkage, .visibility = visibility });
505 @export(__atomic_exchange_2, .{ .name = "__atomic_exchange_2", .linkage = linkage, .visibility = visibility });607 @export(__atomic_exchange_2, .{ .name = "__atomic_exchange_2", .linkage = linkage, .visibility = visibility });
506 @export(__atomic_exchange_4, .{ .name = "__atomic_exchange_4", .linkage = linkage, .visibility = visibility });608 @export(__atomic_exchange_4, .{ .name = "__atomic_exchange_4", .linkage = linkage, .visibility = visibility });
507 @export(__atomic_exchange_8, .{ .name = "__atomic_exchange_8", .linkage = linkage, .visibility = visibility });609 @export(__atomic_exchange_8, .{ .name = "__atomic_exchange_8", .linkage = linkage, .visibility = visibility });
610 @export(__atomic_exchange_16, .{ .name = "__atomic_exchange_16", .linkage = linkage, .visibility = visibility });
508611
509 @export(__atomic_compare_exchange_1, .{ .name = "__atomic_compare_exchange_1", .linkage = linkage, .visibility = visibility });612 @export(__atomic_compare_exchange_1, .{ .name = "__atomic_compare_exchange_1", .linkage = linkage, .visibility = visibility });
510 @export(__atomic_compare_exchange_2, .{ .name = "__atomic_compare_exchange_2", .linkage = linkage, .visibility = visibility });613 @export(__atomic_compare_exchange_2, .{ .name = "__atomic_compare_exchange_2", .linkage = linkage, .visibility = visibility });
511 @export(__atomic_compare_exchange_4, .{ .name = "__atomic_compare_exchange_4", .linkage = linkage, .visibility = visibility });614 @export(__atomic_compare_exchange_4, .{ .name = "__atomic_compare_exchange_4", .linkage = linkage, .visibility = visibility });
512 @export(__atomic_compare_exchange_8, .{ .name = "__atomic_compare_exchange_8", .linkage = linkage, .visibility = visibility });615 @export(__atomic_compare_exchange_8, .{ .name = "__atomic_compare_exchange_8", .linkage = linkage, .visibility = visibility });
616 @export(__atomic_compare_exchange_16, .{ .name = "__atomic_compare_exchange_16", .linkage = linkage, .visibility = visibility });
513 }617 }
514}618}
lib/docs/main.js+4-1
...@@ -1354,6 +1354,10 @@ const NAV_MODES = {...@@ -1354,6 +1354,10 @@ const NAV_MODES = {
1354 payloadHtml += "ptrCast";1354 payloadHtml += "ptrCast";
1355 break;1355 break;
1356 }1356 }
1357 case "qual_cast": {
1358 payloadHtml += "qualCast";
1359 break;
1360 }
1357 case "truncate": {1361 case "truncate": {
1358 payloadHtml += "truncate";1362 payloadHtml += "truncate";
1359 break;1363 break;
...@@ -3158,7 +3162,6 @@ const NAV_MODES = {...@@ -3158,7 +3162,6 @@ const NAV_MODES = {
3158 canonTypeDecls = new Array(zigAnalysis.types.length);3162 canonTypeDecls = new Array(zigAnalysis.types.length);
31593163
3160 for (let pkgI = 0; pkgI < zigAnalysis.packages.length; pkgI += 1) {3164 for (let pkgI = 0; pkgI < zigAnalysis.packages.length; pkgI += 1) {
3161 if (pkgI === zigAnalysis.rootPkg && rootIsStd) continue;
3162 let pkg = zigAnalysis.packages[pkgI];3165 let pkg = zigAnalysis.packages[pkgI];
3163 let pkgNames = canonPkgPaths[pkgI];3166 let pkgNames = canonPkgPaths[pkgI];
3164 if (pkgNames === undefined) continue;3167 if (pkgNames === undefined) continue;
lib/init-exe/build.zig+43-10
...@@ -1,34 +1,67 @@...@@ -1,34 +1,67 @@
1const std = @import("std");1const 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 {
4 // Standard target options allows the person running `zig build` to choose7 // Standard target options allows the person running `zig build` to choose
5 // what target to build for. Here we do not override the defaults, which8 // what target to build for. Here we do not override the defaults, which
6 // means any target is allowed, and the default is native. Other options9 // means any target is allowed, and the default is native. Other options
7 // for restricting supported target set are available.10 // for restricting supported target set are available.
8 const target = b.standardTargetOptions(.{});11 const target = b.standardTargetOptions(.{});
912
10 // Standard release options allow the person running `zig build` to select13 // Standard optimization options allow the person running `zig build` to select
11 // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall.14 // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not
12 const mode = b.standardReleaseOptions();15 // set a preferred release mode, allowing the user to decide how to optimize.
16 const optimize = b.standardOptimizeOption(.{});
1317
14 const exe = b.addExecutable("$", "src/main.zig");18 const exe = b.addExecutable(.{
15 exe.setTarget(target);19 .name = "$",
16 exe.setBuildMode(mode);20 // In this case the main source file is merely a path, however, in more
21 // complicated build scripts, this could be a generated file.
22 .root_source_file = .{ .path = "src/main.zig" },
23 .target = target,
24 .optimize = optimize,
25 });
26
27 // This declares intent for the executable to be installed into the
28 // standard location when the user invokes the "install" step (the default
29 // step when running `zig build`).
17 exe.install();30 exe.install();
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.
19 const run_cmd = exe.run();35 const run_cmd = exe.run();
36
37 // By making the run step depend on the install step, it will be run from the
38 // installation directory rather than directly from within the cache directory.
39 // This is not necessary, however, if the application depends on other installed
40 // files, this ensures they will be present and in the expected location.
20 run_cmd.step.dependOn(b.getInstallStep());41 run_cmd.step.dependOn(b.getInstallStep());
42
43 // This allows the user to pass arguments to the application in the build
44 // command itself, like this: `zig build run -- arg1 arg2 etc`
21 if (b.args) |args| {45 if (b.args) |args| {
22 run_cmd.addArgs(args);46 run_cmd.addArgs(args);
23 }47 }
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".
25 const run_step = b.step("run", "Run the app");52 const run_step = b.step("run", "Run the app");
26 run_step.dependOn(&run_cmd.step);53 run_step.dependOn(&run_cmd.step);
2754
28 const exe_tests = b.addTest("src/main.zig");55 // Creates a step for unit testing.
29 exe_tests.setTarget(target);56 const exe_tests = b.addTest(.{
30 exe_tests.setBuildMode(mode);57 .root_source_file = .{ .path = "src/main.zig" },
58 .target = target,
59 .optimize = optimize,
60 });
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.
32 const test_step = b.step("test", "Run unit tests");65 const test_step = b.step("test", "Run unit tests");
33 test_step.dependOn(&exe_tests.step);66 test_step.dependOn(&exe_tests.step);
34}67}
lib/init-lib/build.zig+35-8
...@@ -1,17 +1,44 @@...@@ -1,17 +1,44 @@
1const std = @import("std");1const 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 // Standard release options allow the person running `zig build` to select4// declaratively construct a build graph that will be executed by an external
5 // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall.5// runner.
6 const mode = b.standardReleaseOptions();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");13 // Standard optimization options allow the person running `zig build` to select
9 lib.setBuildMode(mode);14 // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not
15 // set a preferred release mode, allowing the user to decide how to optimize.
16 const optimize = b.standardOptimizeOption(.{});
17
18 const lib = b.addStaticLibrary(.{
19 .name = "$",
20 // In this case the main source file is merely a path, however, in more
21 // complicated build scripts, this could be a generated file.
22 .root_source_file = .{ .path = "src/main.zig" },
23 .target = target,
24 .optimize = optimize,
25 });
26
27 // This declares intent for the library to be installed into the standard
28 // location when the user invokes the "install" step (the default step when
29 // running `zig build`).
10 lib.install();30 lib.install();
1131
12 const main_tests = b.addTest("src/main.zig");32 // Creates a step for unit testing.
13 main_tests.setBuildMode(mode);33 const main_tests = b.addTest(.{
34 .root_source_file = .{ .path = "src/main.zig" },
35 .target = target,
36 .optimize = optimize,
37 });
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".
15 const test_step = b.step("test", "Run library tests");42 const test_step = b.step("test", "Run library tests");
16 test_step.dependOn(&main_tests.step);43 test_step.dependOn(&main_tests.step);
17}44}
lib/libc/mingw/misc/strtoimax.c+1-4
...@@ -31,10 +31,7 @@...@@ -31,10 +31,7 @@
31#define valid(n, b) ((n) >= 0 && (n) < (b))31#define valid(n, b) ((n) >= 0 && (n) < (b))
3232
33intmax_t33intmax_t
34strtoimax(nptr, endptr, base)34strtoimax(const char * __restrict__ nptr, char ** __restrict__ endptr, int base)
35 register const char * __restrict__ nptr;
36 char ** __restrict__ endptr;
37 register int base;
38 {35 {
39 register uintmax_t accum; /* accumulates converted value */36 register uintmax_t accum; /* accumulates converted value */
40 register int n; /* numeral from digit character */37 register int n; /* numeral from digit character */
lib/libc/mingw/misc/strtoumax.c+1-4
...@@ -31,10 +31,7 @@...@@ -31,10 +31,7 @@
31#define valid(n, b) ((n) >= 0 && (n) < (b))31#define valid(n, b) ((n) >= 0 && (n) < (b))
3232
33uintmax_t33uintmax_t
34strtoumax(nptr, endptr, base)34strtoumax(const char * __restrict__ nptr, char ** __restrict__ endptr, int base)
35 register const char * __restrict__ nptr;
36 char ** __restrict__ endptr;
37 register int base;
38 {35 {
39 register uintmax_t accum; /* accumulates converted value */36 register uintmax_t accum; /* accumulates converted value */
40 register uintmax_t next; /* for computing next value of accum */37 register uintmax_t next; /* for computing next value of accum */
lib/libc/mingw/misc/wcstoimax.c+1-4
...@@ -33,10 +33,7 @@...@@ -33,10 +33,7 @@
33#define valid(n, b) ((n) >= 0 && (n) < (b))33#define valid(n, b) ((n) >= 0 && (n) < (b))
3434
35intmax_t35intmax_t
36wcstoimax(nptr, endptr, base)36wcstoimax(const wchar_t * __restrict__ nptr, wchar_t ** __restrict__ endptr, int base)
37 register const wchar_t * __restrict__ nptr;
38 wchar_t ** __restrict__ endptr;
39 register int base;
40 {37 {
41 register uintmax_t accum; /* accumulates converted value */38 register uintmax_t accum; /* accumulates converted value */
42 register int n; /* numeral from digit character */39 register int n; /* numeral from digit character */
lib/libc/mingw/misc/wcstoumax.c+1-4
...@@ -33,10 +33,7 @@...@@ -33,10 +33,7 @@
33#define valid(n, b) ((n) >= 0 && (n) < (b))33#define valid(n, b) ((n) >= 0 && (n) < (b))
3434
35uintmax_t35uintmax_t
36wcstoumax(nptr, endptr, base)36wcstoumax(const wchar_t * __restrict__ nptr, wchar_t ** __restrict__ endptr, int base)
37 register const wchar_t * __restrict__ nptr;
38 wchar_t ** __restrict__ endptr;
39 register int base;
40 {37 {
41 register uintmax_t accum; /* accumulates converted value */38 register uintmax_t accum; /* accumulates converted value */
42 register uintmax_t next; /* for computing next value of accum */39 register uintmax_t next; /* for computing next value of accum */
lib/std/Build.zig created+1780
...@@ -0,0 +1,1780 @@
1const std = @import("std.zig");
2const builtin = @import("builtin");
3const io = std.io;
4const fs = std.fs;
5const mem = std.mem;
6const debug = std.debug;
7const panic = std.debug.panic;
8const assert = debug.assert;
9const log = std.log;
10const ArrayList = std.ArrayList;
11const StringHashMap = std.StringHashMap;
12const Allocator = mem.Allocator;
13const process = std.process;
14const EnvMap = std.process.EnvMap;
15const fmt_lib = std.fmt;
16const File = std.fs.File;
17const CrossTarget = std.zig.CrossTarget;
18const NativeTargetInfo = std.zig.system.NativeTargetInfo;
19const Sha256 = std.crypto.hash.sha2.Sha256;
20const Build = @This();
21
22/// deprecated: use `CompileStep`.
23pub const LibExeObjStep = CompileStep;
24/// deprecated: use `Build`.
25pub const Builder = Build;
26/// deprecated: use `InstallDirStep.Options`
27pub const InstallDirectoryOptions = InstallDirStep.Options;
28
29pub const Step = @import("Build/Step.zig");
30pub const CheckFileStep = @import("Build/CheckFileStep.zig");
31pub const CheckObjectStep = @import("Build/CheckObjectStep.zig");
32pub const ConfigHeaderStep = @import("Build/ConfigHeaderStep.zig");
33pub const EmulatableRunStep = @import("Build/EmulatableRunStep.zig");
34pub const FmtStep = @import("Build/FmtStep.zig");
35pub const InstallArtifactStep = @import("Build/InstallArtifactStep.zig");
36pub const InstallDirStep = @import("Build/InstallDirStep.zig");
37pub const InstallFileStep = @import("Build/InstallFileStep.zig");
38pub const InstallRawStep = @import("Build/InstallRawStep.zig");
39pub const CompileStep = @import("Build/CompileStep.zig");
40pub const LogStep = @import("Build/LogStep.zig");
41pub const OptionsStep = @import("Build/OptionsStep.zig");
42pub const RemoveDirStep = @import("Build/RemoveDirStep.zig");
43pub const RunStep = @import("Build/RunStep.zig");
44pub const TranslateCStep = @import("Build/TranslateCStep.zig");
45pub const WriteFileStep = @import("Build/WriteFileStep.zig");
46
47install_tls: TopLevelStep,
48uninstall_tls: TopLevelStep,
49allocator: Allocator,
50user_input_options: UserInputOptionsMap,
51available_options_map: AvailableOptionsMap,
52available_options_list: ArrayList(AvailableOption),
53verbose: bool,
54verbose_link: bool,
55verbose_cc: bool,
56verbose_air: bool,
57verbose_llvm_ir: bool,
58verbose_cimport: bool,
59verbose_llvm_cpu_features: bool,
60/// The purpose of executing the command is for a human to read compile errors from the terminal
61prominent_compile_errors: bool,
62color: enum { auto, on, off } = .auto,
63reference_trace: ?u32 = null,
64invalid_user_input: bool,
65zig_exe: []const u8,
66default_step: *Step,
67env_map: *EnvMap,
68top_level_steps: ArrayList(*TopLevelStep),
69install_prefix: []const u8,
70dest_dir: ?[]const u8,
71lib_dir: []const u8,
72exe_dir: []const u8,
73h_dir: []const u8,
74install_path: []const u8,
75sysroot: ?[]const u8 = null,
76search_prefixes: ArrayList([]const u8),
77libc_file: ?[]const u8 = null,
78installed_files: ArrayList(InstalledFile),
79/// Path to the directory containing build.zig.
80build_root: []const u8,
81cache_root: []const u8,
82global_cache_root: []const u8,
83/// zig lib dir
84override_lib_dir: ?[]const u8,
85vcpkg_root: VcpkgRoot = .unattempted,
86pkg_config_pkg_list: ?(PkgConfigError![]const PkgConfigPkg) = null,
87args: ?[][]const u8 = null,
88debug_log_scopes: []const []const u8 = &.{},
89debug_compile_errors: bool = false,
90
91/// Experimental. Use system Darling installation to run cross compiled macOS build artifacts.
92enable_darling: bool = false,
93/// Use system QEMU installation to run cross compiled foreign architecture build artifacts.
94enable_qemu: bool = false,
95/// Darwin. Use Rosetta to run x86_64 macOS build artifacts on arm64 macOS.
96enable_rosetta: bool = false,
97/// Use system Wasmtime installation to run cross compiled wasm/wasi build artifacts.
98enable_wasmtime: bool = false,
99/// Use system Wine installation to run cross compiled Windows build artifacts.
100enable_wine: bool = false,
101/// After following the steps in https://github.com/ziglang/zig/wiki/Updating-libc#glibc,
102/// this will be the directory $glibc-build-dir/install/glibcs
103/// Given the example of the aarch64 target, this is the directory
104/// that contains the path `aarch64-linux-gnu/lib/ld-linux-aarch64.so.1`.
105glibc_runtimes_dir: ?[]const u8 = null,
106
107/// Information about the native target. Computed before build() is invoked.
108host: NativeTargetInfo,
109
110dep_prefix: []const u8 = "",
111
112pub const ExecError = error{
113 ReadFailure,
114 ExitCodeFailure,
115 ProcessTerminated,
116 ExecNotSupported,
117} || std.ChildProcess.SpawnError;
118
119pub const PkgConfigError = error{
120 PkgConfigCrashed,
121 PkgConfigFailed,
122 PkgConfigNotInstalled,
123 PkgConfigInvalidOutput,
124};
125
126pub const PkgConfigPkg = struct {
127 name: []const u8,
128 desc: []const u8,
129};
130
131pub const CStd = enum {
132 C89,
133 C99,
134 C11,
135};
136
137const UserInputOptionsMap = StringHashMap(UserInputOption);
138const AvailableOptionsMap = StringHashMap(AvailableOption);
139
140const AvailableOption = struct {
141 name: []const u8,
142 type_id: TypeId,
143 description: []const u8,
144 /// If the `type_id` is `enum` this provides the list of enum options
145 enum_options: ?[]const []const u8,
146};
147
148const UserInputOption = struct {
149 name: []const u8,
150 value: UserValue,
151 used: bool,
152};
153
154const UserValue = union(enum) {
155 flag: void,
156 scalar: []const u8,
157 list: ArrayList([]const u8),
158 map: StringHashMap(*const UserValue),
159};
160
161const TypeId = enum {
162 bool,
163 int,
164 float,
165 @"enum",
166 string,
167 list,
168};
169
170const TopLevelStep = struct {
171 pub const base_id = .top_level;
172
173 step: Step,
174 description: []const u8,
175};
176
177pub const DirList = struct {
178 lib_dir: ?[]const u8 = null,
179 exe_dir: ?[]const u8 = null,
180 include_dir: ?[]const u8 = null,
181};
182
183pub fn create(
184 allocator: Allocator,
185 zig_exe: []const u8,
186 build_root: []const u8,
187 cache_root: []const u8,
188 global_cache_root: []const u8,
189 host: NativeTargetInfo,
190) !*Build {
191 const env_map = try allocator.create(EnvMap);
192 env_map.* = try process.getEnvMap(allocator);
193
194 const self = try allocator.create(Build);
195 self.* = Build{
196 .zig_exe = zig_exe,
197 .build_root = build_root,
198 .cache_root = try fs.path.relative(allocator, build_root, cache_root),
199 .global_cache_root = global_cache_root,
200 .verbose = false,
201 .verbose_link = false,
202 .verbose_cc = false,
203 .verbose_air = false,
204 .verbose_llvm_ir = false,
205 .verbose_cimport = false,
206 .verbose_llvm_cpu_features = false,
207 .prominent_compile_errors = false,
208 .invalid_user_input = false,
209 .allocator = allocator,
210 .user_input_options = UserInputOptionsMap.init(allocator),
211 .available_options_map = AvailableOptionsMap.init(allocator),
212 .available_options_list = ArrayList(AvailableOption).init(allocator),
213 .top_level_steps = ArrayList(*TopLevelStep).init(allocator),
214 .default_step = undefined,
215 .env_map = env_map,
216 .search_prefixes = ArrayList([]const u8).init(allocator),
217 .install_prefix = undefined,
218 .lib_dir = undefined,
219 .exe_dir = undefined,
220 .h_dir = undefined,
221 .dest_dir = env_map.get("DESTDIR"),
222 .installed_files = ArrayList(InstalledFile).init(allocator),
223 .install_tls = TopLevelStep{
224 .step = Step.initNoOp(.top_level, "install", allocator),
225 .description = "Copy build artifacts to prefix path",
226 },
227 .uninstall_tls = TopLevelStep{
228 .step = Step.init(.top_level, "uninstall", allocator, makeUninstall),
229 .description = "Remove build artifacts from prefix path",
230 },
231 .override_lib_dir = null,
232 .install_path = undefined,
233 .args = null,
234 .host = host,
235 };
236 try self.top_level_steps.append(&self.install_tls);
237 try self.top_level_steps.append(&self.uninstall_tls);
238 self.default_step = &self.install_tls.step;
239 return self;
240}
241
242fn createChild(
243 parent: *Build,
244 dep_name: []const u8,
245 build_root: []const u8,
246 args: anytype,
247) !*Build {
248 const child = try createChildOnly(parent, dep_name, build_root);
249 try applyArgs(child, args);
250 return child;
251}
252
253fn createChildOnly(parent: *Build, dep_name: []const u8, build_root: []const u8) !*Build {
254 const allocator = parent.allocator;
255 const child = try allocator.create(Build);
256 child.* = .{
257 .allocator = allocator,
258 .install_tls = .{
259 .step = Step.initNoOp(.top_level, "install", allocator),
260 .description = "Copy build artifacts to prefix path",
261 },
262 .uninstall_tls = .{
263 .step = Step.init(.top_level, "uninstall", allocator, makeUninstall),
264 .description = "Remove build artifacts from prefix path",
265 },
266 .user_input_options = UserInputOptionsMap.init(allocator),
267 .available_options_map = AvailableOptionsMap.init(allocator),
268 .available_options_list = ArrayList(AvailableOption).init(allocator),
269 .verbose = parent.verbose,
270 .verbose_link = parent.verbose_link,
271 .verbose_cc = parent.verbose_cc,
272 .verbose_air = parent.verbose_air,
273 .verbose_llvm_ir = parent.verbose_llvm_ir,
274 .verbose_cimport = parent.verbose_cimport,
275 .verbose_llvm_cpu_features = parent.verbose_llvm_cpu_features,
276 .prominent_compile_errors = parent.prominent_compile_errors,
277 .color = parent.color,
278 .reference_trace = parent.reference_trace,
279 .invalid_user_input = false,
280 .zig_exe = parent.zig_exe,
281 .default_step = undefined,
282 .env_map = parent.env_map,
283 .top_level_steps = ArrayList(*TopLevelStep).init(allocator),
284 .install_prefix = undefined,
285 .dest_dir = parent.dest_dir,
286 .lib_dir = parent.lib_dir,
287 .exe_dir = parent.exe_dir,
288 .h_dir = parent.h_dir,
289 .install_path = parent.install_path,
290 .sysroot = parent.sysroot,
291 .search_prefixes = ArrayList([]const u8).init(allocator),
292 .libc_file = parent.libc_file,
293 .installed_files = ArrayList(InstalledFile).init(allocator),
294 .build_root = build_root,
295 .cache_root = parent.cache_root,
296 .global_cache_root = parent.global_cache_root,
297 .override_lib_dir = parent.override_lib_dir,
298 .debug_log_scopes = parent.debug_log_scopes,
299 .debug_compile_errors = parent.debug_compile_errors,
300 .enable_darling = parent.enable_darling,
301 .enable_qemu = parent.enable_qemu,
302 .enable_rosetta = parent.enable_rosetta,
303 .enable_wasmtime = parent.enable_wasmtime,
304 .enable_wine = parent.enable_wine,
305 .glibc_runtimes_dir = parent.glibc_runtimes_dir,
306 .host = parent.host,
307 .dep_prefix = parent.fmt("{s}{s}.", .{ parent.dep_prefix, dep_name }),
308 };
309 try child.top_level_steps.append(&child.install_tls);
310 try child.top_level_steps.append(&child.uninstall_tls);
311 child.default_step = &child.install_tls.step;
312 return child;
313}
314
315fn applyArgs(b: *Build, args: anytype) !void {
316 inline for (@typeInfo(@TypeOf(args)).Struct.fields) |field| {
317 const v = @field(args, field.name);
318 const T = @TypeOf(v);
319 switch (T) {
320 CrossTarget => {
321 try b.user_input_options.put(field.name, .{
322 .name = field.name,
323 .value = .{ .scalar = try v.zigTriple(b.allocator) },
324 .used = false,
325 });
326 try b.user_input_options.put("cpu", .{
327 .name = "cpu",
328 .value = .{ .scalar = try serializeCpu(b.allocator, v.getCpu()) },
329 .used = false,
330 });
331 },
332 []const u8 => {
333 try b.user_input_options.put(field.name, .{
334 .name = field.name,
335 .value = .{ .scalar = v },
336 .used = false,
337 });
338 },
339 else => switch (@typeInfo(T)) {
340 .Bool => {
341 try b.user_input_options.put(field.name, .{
342 .name = field.name,
343 .value = .{ .scalar = if (v) "true" else "false" },
344 .used = false,
345 });
346 },
347 .Enum => {
348 try b.user_input_options.put(field.name, .{
349 .name = field.name,
350 .value = .{ .scalar = @tagName(v) },
351 .used = false,
352 });
353 },
354 .Int => {
355 try b.user_input_options.put(field.name, .{
356 .name = field.name,
357 .value = .{ .scalar = try std.fmt.allocPrint(b.allocator, "{d}", .{v}) },
358 .used = false,
359 });
360 },
361 else => @compileError("option '" ++ field.name ++ "' has unsupported type: " ++ @typeName(T)),
362 },
363 }
364 }
365 const Hasher = std.crypto.auth.siphash.SipHash128(1, 3);
366 // Random bytes to make unique. Refresh this with new random bytes when
367 // implementation is modified in a non-backwards-compatible way.
368 var hash = Hasher.init("ZaEsvQ5ClaA2IdH9");
369 hash.update(b.dep_prefix);
370 // TODO additionally update the hash with `args`.
371
372 var digest: [16]u8 = undefined;
373 hash.final(&digest);
374 var hash_basename: [digest.len * 2]u8 = undefined;
375 _ = std.fmt.bufPrint(&hash_basename, "{s}", .{std.fmt.fmtSliceHexLower(&digest)}) catch
376 unreachable;
377
378 const install_prefix = b.pathJoin(&.{ b.cache_root, "i", &hash_basename });
379 b.resolveInstallPrefix(install_prefix, .{});
380}
381
382pub fn destroy(self: *Build) void {
383 self.env_map.deinit();
384 self.top_level_steps.deinit();
385 self.allocator.destroy(self);
386}
387
388/// This function is intended to be called by lib/build_runner.zig, not a build.zig file.
389pub fn resolveInstallPrefix(self: *Build, install_prefix: ?[]const u8, dir_list: DirList) void {
390 if (self.dest_dir) |dest_dir| {
391 self.install_prefix = install_prefix orelse "/usr";
392 self.install_path = self.pathJoin(&.{ dest_dir, self.install_prefix });
393 } else {
394 self.install_prefix = install_prefix orelse
395 (self.pathJoin(&.{ self.build_root, "zig-out" }));
396 self.install_path = self.install_prefix;
397 }
398
399 var lib_list = [_][]const u8{ self.install_path, "lib" };
400 var exe_list = [_][]const u8{ self.install_path, "bin" };
401 var h_list = [_][]const u8{ self.install_path, "include" };
402
403 if (dir_list.lib_dir) |dir| {
404 if (std.fs.path.isAbsolute(dir)) lib_list[0] = self.dest_dir orelse "";
405 lib_list[1] = dir;
406 }
407
408 if (dir_list.exe_dir) |dir| {
409 if (std.fs.path.isAbsolute(dir)) exe_list[0] = self.dest_dir orelse "";
410 exe_list[1] = dir;
411 }
412
413 if (dir_list.include_dir) |dir| {
414 if (std.fs.path.isAbsolute(dir)) h_list[0] = self.dest_dir orelse "";
415 h_list[1] = dir;
416 }
417
418 self.lib_dir = self.pathJoin(&lib_list);
419 self.exe_dir = self.pathJoin(&exe_list);
420 self.h_dir = self.pathJoin(&h_list);
421}
422
423pub fn addOptions(self: *Build) *OptionsStep {
424 return OptionsStep.create(self);
425}
426
427pub const ExecutableOptions = struct {
428 name: []const u8,
429 root_source_file: ?FileSource = null,
430 version: ?std.builtin.Version = null,
431 target: CrossTarget = .{},
432 optimize: std.builtin.Mode = .Debug,
433 linkage: ?CompileStep.Linkage = null,
434};
435
436pub fn addExecutable(b: *Build, options: ExecutableOptions) *CompileStep {
437 return CompileStep.create(b, .{
438 .name = options.name,
439 .root_source_file = options.root_source_file,
440 .version = options.version,
441 .target = options.target,
442 .optimize = options.optimize,
443 .kind = .exe,
444 .linkage = options.linkage,
445 });
446}
447
448pub const ObjectOptions = struct {
449 name: []const u8,
450 root_source_file: ?FileSource = null,
451 target: CrossTarget,
452 optimize: std.builtin.Mode,
453};
454
455pub fn addObject(b: *Build, options: ObjectOptions) *CompileStep {
456 return CompileStep.create(b, .{
457 .name = options.name,
458 .root_source_file = options.root_source_file,
459 .target = options.target,
460 .optimize = options.optimize,
461 .kind = .obj,
462 });
463}
464
465pub const SharedLibraryOptions = struct {
466 name: []const u8,
467 root_source_file: ?FileSource = null,
468 version: ?std.builtin.Version = null,
469 target: CrossTarget,
470 optimize: std.builtin.Mode,
471};
472
473pub fn addSharedLibrary(b: *Build, options: SharedLibraryOptions) *CompileStep {
474 return CompileStep.create(b, .{
475 .name = options.name,
476 .root_source_file = options.root_source_file,
477 .kind = .lib,
478 .linkage = .dynamic,
479 .version = options.version,
480 .target = options.target,
481 .optimize = options.optimize,
482 });
483}
484
485pub const StaticLibraryOptions = struct {
486 name: []const u8,
487 root_source_file: ?FileSource = null,
488 target: CrossTarget,
489 optimize: std.builtin.Mode,
490 version: ?std.builtin.Version = null,
491};
492
493pub fn addStaticLibrary(b: *Build, options: StaticLibraryOptions) *CompileStep {
494 return CompileStep.create(b, .{
495 .name = options.name,
496 .root_source_file = options.root_source_file,
497 .kind = .lib,
498 .linkage = .static,
499 .version = options.version,
500 .target = options.target,
501 .optimize = options.optimize,
502 });
503}
504
505pub const TestOptions = struct {
506 name: []const u8 = "test",
507 kind: CompileStep.Kind = .@"test",
508 root_source_file: FileSource,
509 target: CrossTarget = .{},
510 optimize: std.builtin.Mode = .Debug,
511 version: ?std.builtin.Version = null,
512};
513
514pub fn addTest(b: *Build, options: TestOptions) *CompileStep {
515 return CompileStep.create(b, .{
516 .name = options.name,
517 .kind = options.kind,
518 .root_source_file = options.root_source_file,
519 .target = options.target,
520 .optimize = options.optimize,
521 });
522}
523
524pub const AssemblyOptions = struct {
525 name: []const u8,
526 source_file: FileSource,
527 target: CrossTarget,
528 optimize: std.builtin.Mode,
529};
530
531pub fn addAssembly(b: *Build, options: AssemblyOptions) *CompileStep {
532 const obj_step = CompileStep.create(b, .{
533 .name = options.name,
534 .root_source_file = null,
535 .target = options.target,
536 .optimize = options.optimize,
537 });
538 obj_step.addAssemblyFileSource(options.source_file.dupe(b));
539 return obj_step;
540}
541
542/// Initializes a RunStep with argv, which must at least have the path to the
543/// executable. More command line arguments can be added with `addArg`,
544/// `addArgs`, and `addArtifactArg`.
545/// Be careful using this function, as it introduces a system dependency.
546/// To run an executable built with zig build, see `CompileStep.run`.
547pub fn addSystemCommand(self: *Build, argv: []const []const u8) *RunStep {
548 assert(argv.len >= 1);
549 const run_step = RunStep.create(self, self.fmt("run {s}", .{argv[0]}));
550 run_step.addArgs(argv);
551 return run_step;
552}
553
554pub fn addConfigHeader(
555 b: *Build,
556 source: FileSource,
557 style: ConfigHeaderStep.Style,
558 values: anytype,
559) *ConfigHeaderStep {
560 const config_header_step = ConfigHeaderStep.create(b, source, style);
561 config_header_step.addValues(values);
562 return config_header_step;
563}
564
565/// Allocator.dupe without the need to handle out of memory.
566pub fn dupe(self: *Build, bytes: []const u8) []u8 {
567 return self.allocator.dupe(u8, bytes) catch @panic("OOM");
568}
569
570/// Duplicates an array of strings without the need to handle out of memory.
571pub fn dupeStrings(self: *Build, strings: []const []const u8) [][]u8 {
572 const array = self.allocator.alloc([]u8, strings.len) catch @panic("OOM");
573 for (strings) |s, i| {
574 array[i] = self.dupe(s);
575 }
576 return array;
577}
578
579/// Duplicates a path and converts all slashes to the OS's canonical path separator.
580pub fn dupePath(self: *Build, bytes: []const u8) []u8 {
581 const the_copy = self.dupe(bytes);
582 for (the_copy) |*byte| {
583 switch (byte.*) {
584 '/', '\\' => byte.* = fs.path.sep,
585 else => {},
586 }
587 }
588 return the_copy;
589}
590
591/// Duplicates a package recursively.
592pub fn dupePkg(self: *Build, package: Pkg) Pkg {
593 var the_copy = Pkg{
594 .name = self.dupe(package.name),
595 .source = package.source.dupe(self),
596 };
597
598 if (package.dependencies) |dependencies| {
599 const new_dependencies = self.allocator.alloc(Pkg, dependencies.len) catch @panic("OOM");
600 the_copy.dependencies = new_dependencies;
601
602 for (dependencies) |dep_package, i| {
603 new_dependencies[i] = self.dupePkg(dep_package);
604 }
605 }
606 return the_copy;
607}
608
609pub fn addWriteFile(self: *Build, file_path: []const u8, data: []const u8) *WriteFileStep {
610 const write_file_step = self.addWriteFiles();
611 write_file_step.add(file_path, data);
612 return write_file_step;
613}
614
615pub fn addWriteFiles(self: *Build) *WriteFileStep {
616 const write_file_step = self.allocator.create(WriteFileStep) catch @panic("OOM");
617 write_file_step.* = WriteFileStep.init(self);
618 return write_file_step;
619}
620
621pub fn addLog(self: *Build, comptime format: []const u8, args: anytype) *LogStep {
622 const data = self.fmt(format, args);
623 const log_step = self.allocator.create(LogStep) catch @panic("OOM");
624 log_step.* = LogStep.init(self, data);
625 return log_step;
626}
627
628pub fn addRemoveDirTree(self: *Build, dir_path: []const u8) *RemoveDirStep {
629 const remove_dir_step = self.allocator.create(RemoveDirStep) catch @panic("OOM");
630 remove_dir_step.* = RemoveDirStep.init(self, dir_path);
631 return remove_dir_step;
632}
633
634pub fn addFmt(self: *Build, paths: []const []const u8) *FmtStep {
635 return FmtStep.create(self, paths);
636}
637
638pub fn addTranslateC(self: *Build, options: TranslateCStep.Options) *TranslateCStep {
639 return TranslateCStep.create(self, options);
640}
641
642pub fn make(self: *Build, step_names: []const []const u8) !void {
643 try self.makePath(self.cache_root);
644
645 var wanted_steps = ArrayList(*Step).init(self.allocator);
646 defer wanted_steps.deinit();
647
648 if (step_names.len == 0) {
649 try wanted_steps.append(self.default_step);
650 } else {
651 for (step_names) |step_name| {
652 const s = try self.getTopLevelStepByName(step_name);
653 try wanted_steps.append(s);
654 }
655 }
656
657 for (wanted_steps.items) |s| {
658 try self.makeOneStep(s);
659 }
660}
661
662pub fn getInstallStep(self: *Build) *Step {
663 return &self.install_tls.step;
664}
665
666pub fn getUninstallStep(self: *Build) *Step {
667 return &self.uninstall_tls.step;
668}
669
670fn makeUninstall(uninstall_step: *Step) anyerror!void {
671 const uninstall_tls = @fieldParentPtr(TopLevelStep, "step", uninstall_step);
672 const self = @fieldParentPtr(Build, "uninstall_tls", uninstall_tls);
673
674 for (self.installed_files.items) |installed_file| {
675 const full_path = self.getInstallPath(installed_file.dir, installed_file.path);
676 if (self.verbose) {
677 log.info("rm {s}", .{full_path});
678 }
679 fs.cwd().deleteTree(full_path) catch {};
680 }
681
682 // TODO remove empty directories
683}
684
685fn makeOneStep(self: *Build, s: *Step) anyerror!void {
686 if (s.loop_flag) {
687 log.err("Dependency loop detected:\n {s}", .{s.name});
688 return error.DependencyLoopDetected;
689 }
690 s.loop_flag = true;
691
692 for (s.dependencies.items) |dep| {
693 self.makeOneStep(dep) catch |err| {
694 if (err == error.DependencyLoopDetected) {
695 log.err(" {s}", .{s.name});
696 }
697 return err;
698 };
699 }
700
701 s.loop_flag = false;
702
703 try s.make();
704}
705
706fn getTopLevelStepByName(self: *Build, name: []const u8) !*Step {
707 for (self.top_level_steps.items) |top_level_step| {
708 if (mem.eql(u8, top_level_step.step.name, name)) {
709 return &top_level_step.step;
710 }
711 }
712 log.err("Cannot run step '{s}' because it does not exist", .{name});
713 return error.InvalidStepName;
714}
715
716pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_raw: []const u8) ?T {
717 const name = self.dupe(name_raw);
718 const description = self.dupe(description_raw);
719 const type_id = comptime typeToEnum(T);
720 const enum_options = if (type_id == .@"enum") blk: {
721 const fields = comptime std.meta.fields(T);
722 var options = ArrayList([]const u8).initCapacity(self.allocator, fields.len) catch @panic("OOM");
723
724 inline for (fields) |field| {
725 options.appendAssumeCapacity(field.name);
726 }
727
728 break :blk options.toOwnedSlice() catch @panic("OOM");
729 } else null;
730 const available_option = AvailableOption{
731 .name = name,
732 .type_id = type_id,
733 .description = description,
734 .enum_options = enum_options,
735 };
736 if ((self.available_options_map.fetchPut(name, available_option) catch @panic("OOM")) != null) {
737 panic("Option '{s}' declared twice", .{name});
738 }
739 self.available_options_list.append(available_option) catch @panic("OOM");
740
741 const option_ptr = self.user_input_options.getPtr(name) orelse return null;
742 option_ptr.used = true;
743 switch (type_id) {
744 .bool => switch (option_ptr.value) {
745 .flag => return true,
746 .scalar => |s| {
747 if (mem.eql(u8, s, "true")) {
748 return true;
749 } else if (mem.eql(u8, s, "false")) {
750 return false;
751 } else {
752 log.err("Expected -D{s} to be a boolean, but received '{s}'\n", .{ name, s });
753 self.markInvalidUserInput();
754 return null;
755 }
756 },
757 .list, .map => {
758 log.err("Expected -D{s} to be a boolean, but received a {s}.\n", .{
759 name, @tagName(option_ptr.value),
760 });
761 self.markInvalidUserInput();
762 return null;
763 },
764 },
765 .int => switch (option_ptr.value) {
766 .flag, .list, .map => {
767 log.err("Expected -D{s} to be an integer, but received a {s}.\n", .{
768 name, @tagName(option_ptr.value),
769 });
770 self.markInvalidUserInput();
771 return null;
772 },
773 .scalar => |s| {
774 const n = std.fmt.parseInt(T, s, 10) catch |err| switch (err) {
775 error.Overflow => {
776 log.err("-D{s} value {s} cannot fit into type {s}.\n", .{ name, s, @typeName(T) });
777 self.markInvalidUserInput();
778 return null;
779 },
780 else => {
781 log.err("Expected -D{s} to be an integer of type {s}.\n", .{ name, @typeName(T) });
782 self.markInvalidUserInput();
783 return null;
784 },
785 };
786 return n;
787 },
788 },
789 .float => switch (option_ptr.value) {
790 .flag, .map, .list => {
791 log.err("Expected -D{s} to be a float, but received a {s}.\n", .{
792 name, @tagName(option_ptr.value),
793 });
794 self.markInvalidUserInput();
795 return null;
796 },
797 .scalar => |s| {
798 const n = std.fmt.parseFloat(T, s) catch {
799 log.err("Expected -D{s} to be a float of type {s}.\n", .{ name, @typeName(T) });
800 self.markInvalidUserInput();
801 return null;
802 };
803 return n;
804 },
805 },
806 .@"enum" => switch (option_ptr.value) {
807 .flag, .map, .list => {
808 log.err("Expected -D{s} to be an enum, but received a {s}.\n", .{
809 name, @tagName(option_ptr.value),
810 });
811 self.markInvalidUserInput();
812 return null;
813 },
814 .scalar => |s| {
815 if (std.meta.stringToEnum(T, s)) |enum_lit| {
816 return enum_lit;
817 } else {
818 log.err("Expected -D{s} to be of type {s}.\n", .{ name, @typeName(T) });
819 self.markInvalidUserInput();
820 return null;
821 }
822 },
823 },
824 .string => switch (option_ptr.value) {
825 .flag, .list, .map => {
826 log.err("Expected -D{s} to be a string, but received a {s}.\n", .{
827 name, @tagName(option_ptr.value),
828 });
829 self.markInvalidUserInput();
830 return null;
831 },
832 .scalar => |s| return s,
833 },
834 .list => switch (option_ptr.value) {
835 .flag, .map => {
836 log.err("Expected -D{s} to be a list, but received a {s}.\n", .{
837 name, @tagName(option_ptr.value),
838 });
839 self.markInvalidUserInput();
840 return null;
841 },
842 .scalar => |s| {
843 return self.allocator.dupe([]const u8, &[_][]const u8{s}) catch @panic("OOM");
844 },
845 .list => |lst| return lst.items,
846 },
847 }
848}
849
850pub fn step(self: *Build, name: []const u8, description: []const u8) *Step {
851 const step_info = self.allocator.create(TopLevelStep) catch @panic("OOM");
852 step_info.* = TopLevelStep{
853 .step = Step.initNoOp(.top_level, name, self.allocator),
854 .description = self.dupe(description),
855 };
856 self.top_level_steps.append(step_info) catch @panic("OOM");
857 return &step_info.step;
858}
859
860pub const StandardOptimizeOptionOptions = struct {
861 preferred_optimize_mode: ?std.builtin.Mode = null,
862};
863
864pub fn standardOptimizeOption(self: *Build, options: StandardOptimizeOptionOptions) std.builtin.Mode {
865 if (options.preferred_optimize_mode) |mode| {
866 if (self.option(bool, "release", "optimize for end users") orelse false) {
867 return mode;
868 } else {
869 return .Debug;
870 }
871 } else {
872 return self.option(
873 std.builtin.Mode,
874 "optimize",
875 "prioritize performance, safety, or binary size (-O flag)",
876 ) orelse .Debug;
877 }
878}
879
880pub const StandardTargetOptionsArgs = struct {
881 whitelist: ?[]const CrossTarget = null,
882
883 default_target: CrossTarget = CrossTarget{},
884};
885
886/// Exposes standard `zig build` options for choosing a target.
887pub fn standardTargetOptions(self: *Build, args: StandardTargetOptionsArgs) CrossTarget {
888 const maybe_triple = self.option(
889 []const u8,
890 "target",
891 "The CPU architecture, OS, and ABI to build for",
892 );
893 const mcpu = self.option([]const u8, "cpu", "Target CPU features to add or subtract");
894
895 if (maybe_triple == null and mcpu == null) {
896 return args.default_target;
897 }
898
899 const triple = maybe_triple orelse "native";
900
901 var diags: CrossTarget.ParseOptions.Diagnostics = .{};
902 const selected_target = CrossTarget.parse(.{
903 .arch_os_abi = triple,
904 .cpu_features = mcpu,
905 .diagnostics = &diags,
906 }) catch |err| switch (err) {
907 error.UnknownCpuModel => {
908 log.err("Unknown CPU: '{s}'\nAvailable CPUs for architecture '{s}':", .{
909 diags.cpu_name.?,
910 @tagName(diags.arch.?),
911 });
912 for (diags.arch.?.allCpuModels()) |cpu| {
913 log.err(" {s}", .{cpu.name});
914 }
915 self.markInvalidUserInput();
916 return args.default_target;
917 },
918 error.UnknownCpuFeature => {
919 log.err(
920 \\Unknown CPU feature: '{s}'
921 \\Available CPU features for architecture '{s}':
922 \\
923 , .{
924 diags.unknown_feature_name.?,
925 @tagName(diags.arch.?),
926 });
927 for (diags.arch.?.allFeaturesList()) |feature| {
928 log.err(" {s}: {s}", .{ feature.name, feature.description });
929 }
930 self.markInvalidUserInput();
931 return args.default_target;
932 },
933 error.UnknownOperatingSystem => {
934 log.err(
935 \\Unknown OS: '{s}'
936 \\Available operating systems:
937 \\
938 , .{diags.os_name.?});
939 inline for (std.meta.fields(std.Target.Os.Tag)) |field| {
940 log.err(" {s}", .{field.name});
941 }
942 self.markInvalidUserInput();
943 return args.default_target;
944 },
945 else => |e| {
946 log.err("Unable to parse target '{s}': {s}\n", .{ triple, @errorName(e) });
947 self.markInvalidUserInput();
948 return args.default_target;
949 },
950 };
951
952 const selected_canonicalized_triple = selected_target.zigTriple(self.allocator) catch @panic("OOM");
953
954 if (args.whitelist) |list| whitelist_check: {
955 // Make sure it's a match of one of the list.
956 var mismatch_triple = true;
957 var mismatch_cpu_features = true;
958 var whitelist_item = CrossTarget{};
959 for (list) |t| {
960 mismatch_cpu_features = true;
961 mismatch_triple = true;
962
963 const t_triple = t.zigTriple(self.allocator) catch @panic("OOM");
964 if (mem.eql(u8, t_triple, selected_canonicalized_triple)) {
965 mismatch_triple = false;
966 whitelist_item = t;
967 if (t.getCpuFeatures().isSuperSetOf(selected_target.getCpuFeatures())) {
968 mismatch_cpu_features = false;
969 break :whitelist_check;
970 } else {
971 break;
972 }
973 }
974 }
975 if (mismatch_triple) {
976 log.err("Chosen target '{s}' does not match one of the supported targets:", .{
977 selected_canonicalized_triple,
978 });
979 for (list) |t| {
980 const t_triple = t.zigTriple(self.allocator) catch @panic("OOM");
981 log.err(" {s}", .{t_triple});
982 }
983 } else {
984 assert(mismatch_cpu_features);
985 const whitelist_cpu = whitelist_item.getCpu();
986 const selected_cpu = selected_target.getCpu();
987 log.err("Chosen CPU model '{s}' does not match one of the supported targets:", .{
988 selected_cpu.model.name,
989 });
990 log.err(" Supported feature Set: ", .{});
991 const all_features = whitelist_cpu.arch.allFeaturesList();
992 var populated_cpu_features = whitelist_cpu.model.features;
993 populated_cpu_features.populateDependencies(all_features);
994 for (all_features) |feature, i_usize| {
995 const i = @intCast(std.Target.Cpu.Feature.Set.Index, i_usize);
996 const in_cpu_set = populated_cpu_features.isEnabled(i);
997 if (in_cpu_set) {
998 log.err("{s} ", .{feature.name});
999 }
1000 }
1001 log.err(" Remove: ", .{});
1002 for (all_features) |feature, i_usize| {
1003 const i = @intCast(std.Target.Cpu.Feature.Set.Index, i_usize);
1004 const in_cpu_set = populated_cpu_features.isEnabled(i);
1005 const in_actual_set = selected_cpu.features.isEnabled(i);
1006 if (in_actual_set and !in_cpu_set) {
1007 log.err("{s} ", .{feature.name});
1008 }
1009 }
1010 }
1011 self.markInvalidUserInput();
1012 return args.default_target;
1013 }
1014
1015 return selected_target;
1016}
1017
1018pub fn addUserInputOption(self: *Build, name_raw: []const u8, value_raw: []const u8) !bool {
1019 const name = self.dupe(name_raw);
1020 const value = self.dupe(value_raw);
1021 const gop = try self.user_input_options.getOrPut(name);
1022 if (!gop.found_existing) {
1023 gop.value_ptr.* = UserInputOption{
1024 .name = name,
1025 .value = .{ .scalar = value },
1026 .used = false,
1027 };
1028 return false;
1029 }
1030
1031 // option already exists
1032 switch (gop.value_ptr.value) {
1033 .scalar => |s| {
1034 // turn it into a list
1035 var list = ArrayList([]const u8).init(self.allocator);
1036 try list.append(s);
1037 try list.append(value);
1038 try self.user_input_options.put(name, .{
1039 .name = name,
1040 .value = .{ .list = list },
1041 .used = false,
1042 });
1043 },
1044 .list => |*list| {
1045 // append to the list
1046 try list.append(value);
1047 try self.user_input_options.put(name, .{
1048 .name = name,
1049 .value = .{ .list = list.* },
1050 .used = false,
1051 });
1052 },
1053 .flag => {
1054 log.warn("Option '-D{s}={s}' conflicts with flag '-D{s}'.", .{ name, value, name });
1055 return true;
1056 },
1057 .map => |*map| {
1058 _ = map;
1059 log.warn("TODO maps as command line arguments is not implemented yet.", .{});
1060 return true;
1061 },
1062 }
1063 return false;
1064}
1065
1066pub fn addUserInputFlag(self: *Build, name_raw: []const u8) !bool {
1067 const name = self.dupe(name_raw);
1068 const gop = try self.user_input_options.getOrPut(name);
1069 if (!gop.found_existing) {
1070 gop.value_ptr.* = .{
1071 .name = name,
1072 .value = .{ .flag = {} },
1073 .used = false,
1074 };
1075 return false;
1076 }
1077
1078 // option already exists
1079 switch (gop.value_ptr.value) {
1080 .scalar => |s| {
1081 log.err("Flag '-D{s}' conflicts with option '-D{s}={s}'.", .{ name, name, s });
1082 return true;
1083 },
1084 .list, .map => {
1085 log.err("Flag '-D{s}' conflicts with multiple options of the same name.", .{name});
1086 return true;
1087 },
1088 .flag => {},
1089 }
1090 return false;
1091}
1092
1093fn typeToEnum(comptime T: type) TypeId {
1094 return switch (@typeInfo(T)) {
1095 .Int => .int,
1096 .Float => .float,
1097 .Bool => .bool,
1098 .Enum => .@"enum",
1099 else => switch (T) {
1100 []const u8 => .string,
1101 []const []const u8 => .list,
1102 else => @compileError("Unsupported type: " ++ @typeName(T)),
1103 },
1104 };
1105}
1106
1107fn markInvalidUserInput(self: *Build) void {
1108 self.invalid_user_input = true;
1109}
1110
1111pub fn validateUserInputDidItFail(self: *Build) bool {
1112 // make sure all args are used
1113 var it = self.user_input_options.iterator();
1114 while (it.next()) |entry| {
1115 if (!entry.value_ptr.used) {
1116 log.err("Invalid option: -D{s}", .{entry.key_ptr.*});
1117 self.markInvalidUserInput();
1118 }
1119 }
1120
1121 return self.invalid_user_input;
1122}
1123
1124pub fn spawnChild(self: *Build, argv: []const []const u8) !void {
1125 return self.spawnChildEnvMap(null, self.env_map, argv);
1126}
1127
1128fn printCmd(cwd: ?[]const u8, argv: []const []const u8) void {
1129 if (cwd) |yes_cwd| std.debug.print("cd {s} && ", .{yes_cwd});
1130 for (argv) |arg| {
1131 std.debug.print("{s} ", .{arg});
1132 }
1133 std.debug.print("\n", .{});
1134}
1135
1136pub fn spawnChildEnvMap(self: *Build, cwd: ?[]const u8, env_map: *const EnvMap, argv: []const []const u8) !void {
1137 if (self.verbose) {
1138 printCmd(cwd, argv);
1139 }
1140
1141 if (!std.process.can_spawn)
1142 return error.ExecNotSupported;
1143
1144 var child = std.ChildProcess.init(argv, self.allocator);
1145 child.cwd = cwd;
1146 child.env_map = env_map;
1147
1148 const term = child.spawnAndWait() catch |err| {
1149 log.err("Unable to spawn {s}: {s}", .{ argv[0], @errorName(err) });
1150 return err;
1151 };
1152
1153 switch (term) {
1154 .Exited => |code| {
1155 if (code != 0) {
1156 log.err("The following command exited with error code {}:", .{code});
1157 printCmd(cwd, argv);
1158 return error.UncleanExit;
1159 }
1160 },
1161 else => {
1162 log.err("The following command terminated unexpectedly:", .{});
1163 printCmd(cwd, argv);
1164
1165 return error.UncleanExit;
1166 },
1167 }
1168}
1169
1170pub fn makePath(self: *Build, path: []const u8) !void {
1171 fs.cwd().makePath(self.pathFromRoot(path)) catch |err| {
1172 log.err("Unable to create path {s}: {s}", .{ path, @errorName(err) });
1173 return err;
1174 };
1175}
1176
1177pub fn installArtifact(self: *Build, artifact: *CompileStep) void {
1178 self.getInstallStep().dependOn(&self.addInstallArtifact(artifact).step);
1179}
1180
1181pub fn addInstallArtifact(self: *Build, artifact: *CompileStep) *InstallArtifactStep {
1182 return InstallArtifactStep.create(self, artifact);
1183}
1184
1185///`dest_rel_path` is relative to prefix path
1186pub fn installFile(self: *Build, src_path: []const u8, dest_rel_path: []const u8) void {
1187 self.getInstallStep().dependOn(&self.addInstallFileWithDir(.{ .path = src_path }, .prefix, dest_rel_path).step);
1188}
1189
1190pub fn installDirectory(self: *Build, options: InstallDirectoryOptions) void {
1191 self.getInstallStep().dependOn(&self.addInstallDirectory(options).step);
1192}
1193
1194///`dest_rel_path` is relative to bin path
1195pub fn installBinFile(self: *Build, src_path: []const u8, dest_rel_path: []const u8) void {
1196 self.getInstallStep().dependOn(&self.addInstallFileWithDir(.{ .path = src_path }, .bin, dest_rel_path).step);
1197}
1198
1199///`dest_rel_path` is relative to lib path
1200pub fn installLibFile(self: *Build, src_path: []const u8, dest_rel_path: []const u8) void {
1201 self.getInstallStep().dependOn(&self.addInstallFileWithDir(.{ .path = src_path }, .lib, dest_rel_path).step);
1202}
1203
1204/// Output format (BIN vs Intel HEX) determined by filename
1205pub fn installRaw(self: *Build, artifact: *CompileStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep {
1206 const raw = self.addInstallRaw(artifact, dest_filename, options);
1207 self.getInstallStep().dependOn(&raw.step);
1208 return raw;
1209}
1210
1211///`dest_rel_path` is relative to install prefix path
1212pub fn addInstallFile(self: *Build, source: FileSource, dest_rel_path: []const u8) *InstallFileStep {
1213 return self.addInstallFileWithDir(source.dupe(self), .prefix, dest_rel_path);
1214}
1215
1216///`dest_rel_path` is relative to bin path
1217pub fn addInstallBinFile(self: *Build, source: FileSource, dest_rel_path: []const u8) *InstallFileStep {
1218 return self.addInstallFileWithDir(source.dupe(self), .bin, dest_rel_path);
1219}
1220
1221///`dest_rel_path` is relative to lib path
1222pub fn addInstallLibFile(self: *Build, source: FileSource, dest_rel_path: []const u8) *InstallFileStep {
1223 return self.addInstallFileWithDir(source.dupe(self), .lib, dest_rel_path);
1224}
1225
1226pub fn addInstallHeaderFile(b: *Build, src_path: []const u8, dest_rel_path: []const u8) *InstallFileStep {
1227 return b.addInstallFileWithDir(.{ .path = src_path }, .header, dest_rel_path);
1228}
1229
1230pub fn addInstallRaw(self: *Build, artifact: *CompileStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep {
1231 return InstallRawStep.create(self, artifact, dest_filename, options);
1232}
1233
1234pub fn addInstallFileWithDir(
1235 self: *Build,
1236 source: FileSource,
1237 install_dir: InstallDir,
1238 dest_rel_path: []const u8,
1239) *InstallFileStep {
1240 if (dest_rel_path.len == 0) {
1241 panic("dest_rel_path must be non-empty", .{});
1242 }
1243 const install_step = self.allocator.create(InstallFileStep) catch @panic("OOM");
1244 install_step.* = InstallFileStep.init(self, source.dupe(self), install_dir, dest_rel_path);
1245 return install_step;
1246}
1247
1248pub fn addInstallDirectory(self: *Build, options: InstallDirectoryOptions) *InstallDirStep {
1249 const install_step = self.allocator.create(InstallDirStep) catch @panic("OOM");
1250 install_step.* = InstallDirStep.init(self, options);
1251 return install_step;
1252}
1253
1254pub fn pushInstalledFile(self: *Build, dir: InstallDir, dest_rel_path: []const u8) void {
1255 const file = InstalledFile{
1256 .dir = dir,
1257 .path = dest_rel_path,
1258 };
1259 self.installed_files.append(file.dupe(self)) catch @panic("OOM");
1260}
1261
1262pub fn updateFile(self: *Build, source_path: []const u8, dest_path: []const u8) !void {
1263 if (self.verbose) {
1264 log.info("cp {s} {s} ", .{ source_path, dest_path });
1265 }
1266 const cwd = fs.cwd();
1267 const prev_status = try fs.Dir.updateFile(cwd, source_path, cwd, dest_path, .{});
1268 if (self.verbose) switch (prev_status) {
1269 .stale => log.info("# installed", .{}),
1270 .fresh => log.info("# up-to-date", .{}),
1271 };
1272}
1273
1274pub fn truncateFile(self: *Build, dest_path: []const u8) !void {
1275 if (self.verbose) {
1276 log.info("truncate {s}", .{dest_path});
1277 }
1278 const cwd = fs.cwd();
1279 var src_file = cwd.createFile(dest_path, .{}) catch |err| switch (err) {
1280 error.FileNotFound => blk: {
1281 if (fs.path.dirname(dest_path)) |dirname| {
1282 try cwd.makePath(dirname);
1283 }
1284 break :blk try cwd.createFile(dest_path, .{});
1285 },
1286 else => |e| return e,
1287 };
1288 src_file.close();
1289}
1290
1291pub fn pathFromRoot(self: *Build, rel_path: []const u8) []u8 {
1292 return fs.path.resolve(self.allocator, &[_][]const u8{ self.build_root, rel_path }) catch @panic("OOM");
1293}
1294
1295pub fn pathJoin(self: *Build, paths: []const []const u8) []u8 {
1296 return fs.path.join(self.allocator, paths) catch @panic("OOM");
1297}
1298
1299pub fn fmt(self: *Build, comptime format: []const u8, args: anytype) []u8 {
1300 return fmt_lib.allocPrint(self.allocator, format, args) catch @panic("OOM");
1301}
1302
1303pub fn findProgram(self: *Build, names: []const []const u8, paths: []const []const u8) ![]const u8 {
1304 // TODO report error for ambiguous situations
1305 const exe_extension = @as(CrossTarget, .{}).exeFileExt();
1306 for (self.search_prefixes.items) |search_prefix| {
1307 for (names) |name| {
1308 if (fs.path.isAbsolute(name)) {
1309 return name;
1310 }
1311 const full_path = self.pathJoin(&.{
1312 search_prefix,
1313 "bin",
1314 self.fmt("{s}{s}", .{ name, exe_extension }),
1315 });
1316 return fs.realpathAlloc(self.allocator, full_path) catch continue;
1317 }
1318 }
1319 if (self.env_map.get("PATH")) |PATH| {
1320 for (names) |name| {
1321 if (fs.path.isAbsolute(name)) {
1322 return name;
1323 }
1324 var it = mem.tokenize(u8, PATH, &[_]u8{fs.path.delimiter});
1325 while (it.next()) |path| {
1326 const full_path = self.pathJoin(&.{
1327 path,
1328 self.fmt("{s}{s}", .{ name, exe_extension }),
1329 });
1330 return fs.realpathAlloc(self.allocator, full_path) catch continue;
1331 }
1332 }
1333 }
1334 for (names) |name| {
1335 if (fs.path.isAbsolute(name)) {
1336 return name;
1337 }
1338 for (paths) |path| {
1339 const full_path = self.pathJoin(&.{
1340 path,
1341 self.fmt("{s}{s}", .{ name, exe_extension }),
1342 });
1343 return fs.realpathAlloc(self.allocator, full_path) catch continue;
1344 }
1345 }
1346 return error.FileNotFound;
1347}
1348
1349pub fn execAllowFail(
1350 self: *Build,
1351 argv: []const []const u8,
1352 out_code: *u8,
1353 stderr_behavior: std.ChildProcess.StdIo,
1354) ExecError![]u8 {
1355 assert(argv.len != 0);
1356
1357 if (!std.process.can_spawn)
1358 return error.ExecNotSupported;
1359
1360 const max_output_size = 400 * 1024;
1361 var child = std.ChildProcess.init(argv, self.allocator);
1362 child.stdin_behavior = .Ignore;
1363 child.stdout_behavior = .Pipe;
1364 child.stderr_behavior = stderr_behavior;
1365 child.env_map = self.env_map;
1366
1367 try child.spawn();
1368
1369 const stdout = child.stdout.?.reader().readAllAlloc(self.allocator, max_output_size) catch {
1370 return error.ReadFailure;
1371 };
1372 errdefer self.allocator.free(stdout);
1373
1374 const term = try child.wait();
1375 switch (term) {
1376 .Exited => |code| {
1377 if (code != 0) {
1378 out_code.* = @truncate(u8, code);
1379 return error.ExitCodeFailure;
1380 }
1381 return stdout;
1382 },
1383 .Signal, .Stopped, .Unknown => |code| {
1384 out_code.* = @truncate(u8, code);
1385 return error.ProcessTerminated;
1386 },
1387 }
1388}
1389
1390pub fn execFromStep(self: *Build, argv: []const []const u8, src_step: ?*Step) ![]u8 {
1391 assert(argv.len != 0);
1392
1393 if (self.verbose) {
1394 printCmd(null, argv);
1395 }
1396
1397 if (!std.process.can_spawn) {
1398 if (src_step) |s| log.err("{s}...", .{s.name});
1399 log.err("Unable to spawn the following command: cannot spawn child process", .{});
1400 printCmd(null, argv);
1401 std.os.abort();
1402 }
1403
1404 var code: u8 = undefined;
1405 return self.execAllowFail(argv, &code, .Inherit) catch |err| switch (err) {
1406 error.ExecNotSupported => {
1407 if (src_step) |s| log.err("{s}...", .{s.name});
1408 log.err("Unable to spawn the following command: cannot spawn child process", .{});
1409 printCmd(null, argv);
1410 std.os.abort();
1411 },
1412 error.FileNotFound => {
1413 if (src_step) |s| log.err("{s}...", .{s.name});
1414 log.err("Unable to spawn the following command: file not found", .{});
1415 printCmd(null, argv);
1416 std.os.exit(@truncate(u8, code));
1417 },
1418 error.ExitCodeFailure => {
1419 if (src_step) |s| log.err("{s}...", .{s.name});
1420 if (self.prominent_compile_errors) {
1421 log.err("The step exited with error code {d}", .{code});
1422 } else {
1423 log.err("The following command exited with error code {d}:", .{code});
1424 printCmd(null, argv);
1425 }
1426
1427 std.os.exit(@truncate(u8, code));
1428 },
1429 error.ProcessTerminated => {
1430 if (src_step) |s| log.err("{s}...", .{s.name});
1431 log.err("The following command terminated unexpectedly:", .{});
1432 printCmd(null, argv);
1433 std.os.exit(@truncate(u8, code));
1434 },
1435 else => |e| return e,
1436 };
1437}
1438
1439pub fn exec(self: *Build, argv: []const []const u8) ![]u8 {
1440 return self.execFromStep(argv, null);
1441}
1442
1443pub fn addSearchPrefix(self: *Build, search_prefix: []const u8) void {
1444 self.search_prefixes.append(self.dupePath(search_prefix)) catch @panic("OOM");
1445}
1446
1447pub fn getInstallPath(self: *Build, dir: InstallDir, dest_rel_path: []const u8) []const u8 {
1448 assert(!fs.path.isAbsolute(dest_rel_path)); // Install paths must be relative to the prefix
1449 const base_dir = switch (dir) {
1450 .prefix => self.install_path,
1451 .bin => self.exe_dir,
1452 .lib => self.lib_dir,
1453 .header => self.h_dir,
1454 .custom => |path| self.pathJoin(&.{ self.install_path, path }),
1455 };
1456 return fs.path.resolve(
1457 self.allocator,
1458 &[_][]const u8{ base_dir, dest_rel_path },
1459 ) catch @panic("OOM");
1460}
1461
1462pub const Dependency = struct {
1463 builder: *Build,
1464
1465 pub fn artifact(d: *Dependency, name: []const u8) *CompileStep {
1466 var found: ?*CompileStep = null;
1467 for (d.builder.install_tls.step.dependencies.items) |dep_step| {
1468 const inst = dep_step.cast(InstallArtifactStep) orelse continue;
1469 if (mem.eql(u8, inst.artifact.name, name)) {
1470 if (found != null) panic("artifact name '{s}' is ambiguous", .{name});
1471 found = inst.artifact;
1472 }
1473 }
1474 return found orelse {
1475 for (d.builder.install_tls.step.dependencies.items) |dep_step| {
1476 const inst = dep_step.cast(InstallArtifactStep) orelse continue;
1477 log.info("available artifact: '{s}'", .{inst.artifact.name});
1478 }
1479 panic("unable to find artifact '{s}'", .{name});
1480 };
1481 }
1482};
1483
1484pub fn dependency(b: *Build, name: []const u8, args: anytype) *Dependency {
1485 const build_runner = @import("root");
1486 const deps = build_runner.dependencies;
1487
1488 inline for (@typeInfo(deps.imports).Struct.decls) |decl| {
1489 if (mem.startsWith(u8, decl.name, b.dep_prefix) and
1490 mem.endsWith(u8, decl.name, name) and
1491 decl.name.len == b.dep_prefix.len + name.len)
1492 {
1493 const build_zig = @field(deps.imports, decl.name);
1494 const build_root = @field(deps.build_root, decl.name);
1495 return dependencyInner(b, name, build_root, build_zig, args);
1496 }
1497 }
1498
1499 const full_path = b.pathFromRoot("build.zig.zon");
1500 std.debug.print("no dependency named '{s}' in '{s}'. All packages used in build.zig must be declared in this file.\n", .{ name, full_path });
1501 std.process.exit(1);
1502}
1503
1504fn dependencyInner(
1505 b: *Build,
1506 name: []const u8,
1507 build_root: []const u8,
1508 comptime build_zig: type,
1509 args: anytype,
1510) *Dependency {
1511 const sub_builder = b.createChild(name, build_root, args) catch @panic("unhandled error");
1512 sub_builder.runBuild(build_zig) catch @panic("unhandled error");
1513
1514 if (sub_builder.validateUserInputDidItFail()) {
1515 std.debug.dumpCurrentStackTrace(@returnAddress());
1516 }
1517
1518 const dep = b.allocator.create(Dependency) catch @panic("OOM");
1519 dep.* = .{ .builder = sub_builder };
1520 return dep;
1521}
1522
1523pub fn runBuild(b: *Build, build_zig: anytype) anyerror!void {
1524 switch (@typeInfo(@typeInfo(@TypeOf(build_zig.build)).Fn.return_type.?)) {
1525 .Void => build_zig.build(b),
1526 .ErrorUnion => try build_zig.build(b),
1527 else => @compileError("expected return type of build to be 'void' or '!void'"),
1528 }
1529}
1530
1531test "builder.findProgram compiles" {
1532 if (builtin.os.tag == .wasi) return error.SkipZigTest;
1533
1534 var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
1535 defer arena.deinit();
1536
1537 const host = try NativeTargetInfo.detect(.{});
1538
1539 const builder = try Build.create(
1540 arena.allocator(),
1541 "zig",
1542 "zig-cache",
1543 "zig-cache",
1544 "zig-cache",
1545 host,
1546 );
1547 defer builder.destroy();
1548 _ = builder.findProgram(&[_][]const u8{}, &[_][]const u8{}) catch null;
1549}
1550
1551pub const Pkg = struct {
1552 name: []const u8,
1553 source: FileSource,
1554 dependencies: ?[]const Pkg = null,
1555};
1556
1557/// A file that is generated by a build step.
1558/// This struct is an interface that is meant to be used with `@fieldParentPtr` to implement the actual path logic.
1559pub const GeneratedFile = struct {
1560 /// The step that generates the file
1561 step: *Step,
1562
1563 /// The path to the generated file. Must be either absolute or relative to the build root.
1564 /// This value must be set in the `fn make()` of the `step` and must not be `null` afterwards.
1565 path: ?[]const u8 = null,
1566
1567 pub fn getPath(self: GeneratedFile) []const u8 {
1568 return self.path orelse std.debug.panic(
1569 "getPath() was called on a GeneratedFile that wasn't build yet. Is there a missing Step dependency on step '{s}'?",
1570 .{self.step.name},
1571 );
1572 }
1573};
1574
1575/// A file source is a reference to an existing or future file.
1576///
1577pub const FileSource = union(enum) {
1578 /// A plain file path, relative to build root or absolute.
1579 path: []const u8,
1580
1581 /// A file that is generated by an interface. Those files usually are
1582 /// not available until built by a build step.
1583 generated: *const GeneratedFile,
1584
1585 /// Returns a new file source that will have a relative path to the build root guaranteed.
1586 /// This should be preferred over setting `.path` directly as it documents that the files are in the project directory.
1587 pub fn relative(path: []const u8) FileSource {
1588 std.debug.assert(!std.fs.path.isAbsolute(path));
1589 return FileSource{ .path = path };
1590 }
1591
1592 /// Returns a string that can be shown to represent the file source.
1593 /// Either returns the path or `"generated"`.
1594 pub fn getDisplayName(self: FileSource) []const u8 {
1595 return switch (self) {
1596 .path => self.path,
1597 .generated => "generated",
1598 };
1599 }
1600
1601 /// Adds dependencies this file source implies to the given step.
1602 pub fn addStepDependencies(self: FileSource, other_step: *Step) void {
1603 switch (self) {
1604 .path => {},
1605 .generated => |gen| other_step.dependOn(gen.step),
1606 }
1607 }
1608
1609 /// Should only be called during make(), returns a path relative to the build root or absolute.
1610 pub fn getPath(self: FileSource, builder: *Build) []const u8 {
1611 const path = switch (self) {
1612 .path => |p| builder.pathFromRoot(p),
1613 .generated => |gen| gen.getPath(),
1614 };
1615 return path;
1616 }
1617
1618 /// Duplicates the file source for a given builder.
1619 pub fn dupe(self: FileSource, b: *Build) FileSource {
1620 return switch (self) {
1621 .path => |p| .{ .path = b.dupePath(p) },
1622 .generated => |gen| .{ .generated = gen },
1623 };
1624 }
1625};
1626
1627/// Allocates a new string for assigning a value to a named macro.
1628/// If the value is omitted, it is set to 1.
1629/// `name` and `value` need not live longer than the function call.
1630pub fn constructCMacro(allocator: Allocator, name: []const u8, value: ?[]const u8) []const u8 {
1631 var macro = allocator.alloc(
1632 u8,
1633 name.len + if (value) |value_slice| value_slice.len + 1 else 0,
1634 ) catch |err| if (err == error.OutOfMemory) @panic("Out of memory") else unreachable;
1635 mem.copy(u8, macro, name);
1636 if (value) |value_slice| {
1637 macro[name.len] = '=';
1638 mem.copy(u8, macro[name.len + 1 ..], value_slice);
1639 }
1640 return macro;
1641}
1642
1643pub const VcpkgRoot = union(VcpkgRootStatus) {
1644 unattempted: void,
1645 not_found: void,
1646 found: []const u8,
1647};
1648
1649pub const VcpkgRootStatus = enum {
1650 unattempted,
1651 not_found,
1652 found,
1653};
1654
1655pub const InstallDir = union(enum) {
1656 prefix: void,
1657 lib: void,
1658 bin: void,
1659 header: void,
1660 /// A path relative to the prefix
1661 custom: []const u8,
1662
1663 /// Duplicates the install directory including the path if set to custom.
1664 pub fn dupe(self: InstallDir, builder: *Build) InstallDir {
1665 if (self == .custom) {
1666 // Written with this temporary to avoid RLS problems
1667 const duped_path = builder.dupe(self.custom);
1668 return .{ .custom = duped_path };
1669 } else {
1670 return self;
1671 }
1672 }
1673};
1674
1675pub const InstalledFile = struct {
1676 dir: InstallDir,
1677 path: []const u8,
1678
1679 /// Duplicates the installed file path and directory.
1680 pub fn dupe(self: InstalledFile, builder: *Build) InstalledFile {
1681 return .{
1682 .dir = self.dir.dupe(builder),
1683 .path = builder.dupe(self.path),
1684 };
1685 }
1686};
1687
1688pub fn serializeCpu(allocator: Allocator, cpu: std.Target.Cpu) ![]const u8 {
1689 // TODO this logic can disappear if cpu model + features becomes part of the target triple
1690 const all_features = cpu.arch.allFeaturesList();
1691 var populated_cpu_features = cpu.model.features;
1692 populated_cpu_features.populateDependencies(all_features);
1693
1694 if (populated_cpu_features.eql(cpu.features)) {
1695 // The CPU name alone is sufficient.
1696 return cpu.model.name;
1697 } else {
1698 var mcpu_buffer = ArrayList(u8).init(allocator);
1699 try mcpu_buffer.appendSlice(cpu.model.name);
1700
1701 for (all_features) |feature, i_usize| {
1702 const i = @intCast(std.Target.Cpu.Feature.Set.Index, i_usize);
1703 const in_cpu_set = populated_cpu_features.isEnabled(i);
1704 const in_actual_set = cpu.features.isEnabled(i);
1705 if (in_cpu_set and !in_actual_set) {
1706 try mcpu_buffer.writer().print("-{s}", .{feature.name});
1707 } else if (!in_cpu_set and in_actual_set) {
1708 try mcpu_buffer.writer().print("+{s}", .{feature.name});
1709 }
1710 }
1711
1712 return try mcpu_buffer.toOwnedSlice();
1713 }
1714}
1715
1716test "dupePkg()" {
1717 if (builtin.os.tag == .wasi) return error.SkipZigTest;
1718
1719 var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
1720 defer arena.deinit();
1721
1722 const host = try NativeTargetInfo.detect(.{});
1723
1724 var builder = try Build.create(
1725 arena.allocator(),
1726 "test",
1727 "test",
1728 "test",
1729 "test",
1730 host,
1731 );
1732 defer builder.destroy();
1733
1734 var pkg_dep = Pkg{
1735 .name = "pkg_dep",
1736 .source = .{ .path = "/not/a/pkg_dep.zig" },
1737 };
1738 var pkg_top = Pkg{
1739 .name = "pkg_top",
1740 .source = .{ .path = "/not/a/pkg_top.zig" },
1741 .dependencies = &[_]Pkg{pkg_dep},
1742 };
1743 const duped = builder.dupePkg(pkg_top);
1744
1745 const original_deps = pkg_top.dependencies.?;
1746 const dupe_deps = duped.dependencies.?;
1747
1748 // probably the same top level package details
1749 try std.testing.expectEqualStrings(pkg_top.name, duped.name);
1750
1751 // probably the same dependencies
1752 try std.testing.expectEqual(original_deps.len, dupe_deps.len);
1753 try std.testing.expectEqual(original_deps[0].name, pkg_dep.name);
1754
1755 // could segfault otherwise if pointers in duplicated package's fields are
1756 // the same as those in stack allocated package's fields
1757 try std.testing.expect(dupe_deps.ptr != original_deps.ptr);
1758 try std.testing.expect(duped.name.ptr != pkg_top.name.ptr);
1759 try std.testing.expect(duped.source.path.ptr != pkg_top.source.path.ptr);
1760 try std.testing.expect(dupe_deps[0].name.ptr != pkg_dep.name.ptr);
1761 try std.testing.expect(dupe_deps[0].source.path.ptr != pkg_dep.source.path.ptr);
1762}
1763
1764test {
1765 _ = CheckFileStep;
1766 _ = CheckObjectStep;
1767 _ = EmulatableRunStep;
1768 _ = FmtStep;
1769 _ = InstallArtifactStep;
1770 _ = InstallDirStep;
1771 _ = InstallFileStep;
1772 _ = InstallRawStep;
1773 _ = CompileStep;
1774 _ = LogStep;
1775 _ = OptionsStep;
1776 _ = RemoveDirStep;
1777 _ = RunStep;
1778 _ = TranslateCStep;
1779 _ = WriteFileStep;
1780}
lib/std/Build/CheckFileStep.zig created+51
...@@ -0,0 +1,51 @@
1const std = @import("../std.zig");
2const Step = std.Build.Step;
3const fs = std.fs;
4const mem = std.mem;
5
6const CheckFileStep = @This();
7
8pub const base_id = .check_file;
9
10step: Step,
11builder: *std.Build,
12expected_matches: []const []const u8,
13source: std.Build.FileSource,
14max_bytes: usize = 20 * 1024 * 1024,
15
16pub fn create(
17 builder: *std.Build,
18 source: std.Build.FileSource,
19 expected_matches: []const []const u8,
20) *CheckFileStep {
21 const self = builder.allocator.create(CheckFileStep) catch @panic("OOM");
22 self.* = CheckFileStep{
23 .builder = builder,
24 .step = Step.init(.check_file, "CheckFile", builder.allocator, make),
25 .source = source.dupe(builder),
26 .expected_matches = builder.dupeStrings(expected_matches),
27 };
28 self.source.addStepDependencies(&self.step);
29 return self;
30}
31
32fn make(step: *Step) !void {
33 const self = @fieldParentPtr(CheckFileStep, "step", step);
34
35 const src_path = self.source.getPath(self.builder);
36 const contents = try fs.cwd().readFileAlloc(self.builder.allocator, src_path, self.max_bytes);
37
38 for (self.expected_matches) |expected_match| {
39 if (mem.indexOf(u8, contents, expected_match) == null) {
40 std.debug.print(
41 \\
42 \\========= Expected to find: ===================
43 \\{s}
44 \\========= But file does not contain it: =======
45 \\{s}
46 \\
47 , .{ expected_match, contents });
48 return error.TestFailed;
49 }
50 }
51}
lib/std/Build/CheckObjectStep.zig created+1024
...@@ -0,0 +1,1024 @@
1const std = @import("../std.zig");
2const assert = std.debug.assert;
3const fs = std.fs;
4const macho = std.macho;
5const math = std.math;
6const mem = std.mem;
7const testing = std.testing;
8
9const CheckObjectStep = @This();
10
11const Allocator = mem.Allocator;
12const Step = std.Build.Step;
13const EmulatableRunStep = std.Build.EmulatableRunStep;
14
15pub const base_id = .check_object;
16
17step: Step,
18builder: *std.Build,
19source: std.Build.FileSource,
20max_bytes: usize = 20 * 1024 * 1024,
21checks: std.ArrayList(Check),
22dump_symtab: bool = false,
23obj_format: std.Target.ObjectFormat,
24
25pub fn create(builder: *std.Build, source: std.Build.FileSource, obj_format: std.Target.ObjectFormat) *CheckObjectStep {
26 const gpa = builder.allocator;
27 const self = gpa.create(CheckObjectStep) catch @panic("OOM");
28 self.* = .{
29 .builder = builder,
30 .step = Step.init(.check_file, "CheckObject", gpa, make),
31 .source = source.dupe(builder),
32 .checks = std.ArrayList(Check).init(gpa),
33 .obj_format = obj_format,
34 };
35 self.source.addStepDependencies(&self.step);
36 return self;
37}
38
39/// Runs and (optionally) compares the output of a binary.
40/// Asserts `self` was generated from an executable step.
41pub fn runAndCompare(self: *CheckObjectStep) *EmulatableRunStep {
42 const dependencies_len = self.step.dependencies.items.len;
43 assert(dependencies_len > 0);
44 const exe_step = self.step.dependencies.items[dependencies_len - 1];
45 const exe = exe_step.cast(std.Build.CompileStep).?;
46 const emulatable_step = EmulatableRunStep.create(self.builder, "EmulatableRun", exe);
47 emulatable_step.step.dependOn(&self.step);
48 return emulatable_step;
49}
50
51/// There two types of actions currently suported:
52/// * `.match` - is the main building block of standard matchers with optional eat-all token `{*}`
53/// and extractors by name such as `{n_value}`. Please note this action is very simplistic in nature
54/// i.e., it won't really handle edge cases/nontrivial examples. But given that we do want to use
55/// it mainly to test the output of our object format parser-dumpers when testing the linkers, etc.
56/// it should be plenty useful in its current form.
57/// * `.compute_cmp` - can be used to perform an operation on the extracted global variables
58/// using the MatchAction. It currently only supports an addition. The operation is required
59/// to be specified in Reverse Polish Notation to ease in operator-precedence parsing (well,
60/// to avoid any parsing really).
61/// For example, if the two extracted values were saved as `vmaddr` and `entryoff` respectively
62/// they could then be added with this simple program `vmaddr entryoff +`.
63const Action = struct {
64 tag: enum { match, not_present, compute_cmp },
65 phrase: []const u8,
66 expected: ?ComputeCompareExpected = null,
67
68 /// Will return true if the `phrase` was found in the `haystack`.
69 /// Some examples include:
70 ///
71 /// LC 0 => will match in its entirety
72 /// vmaddr {vmaddr} => will match `vmaddr` and then extract the following value as u64
73 /// and save under `vmaddr` global name (see `global_vars` param)
74 /// name {*}libobjc{*}.dylib => will match `name` followed by a token which contains `libobjc` and `.dylib`
75 /// in that order with other letters in between
76 fn match(act: Action, haystack: []const u8, global_vars: anytype) !bool {
77 assert(act.tag == .match or act.tag == .not_present);
78
79 var candidate_var: ?struct { name: []const u8, value: u64 } = null;
80 var hay_it = mem.tokenize(u8, mem.trim(u8, haystack, " "), " ");
81 var needle_it = mem.tokenize(u8, mem.trim(u8, act.phrase, " "), " ");
82
83 while (needle_it.next()) |needle_tok| {
84 const hay_tok = hay_it.next() orelse return false;
85
86 if (mem.indexOf(u8, needle_tok, "{*}")) |index| {
87 // We have fuzzy matchers within the search pattern, so we match substrings.
88 var start = index;
89 var n_tok = needle_tok;
90 var h_tok = hay_tok;
91 while (true) {
92 n_tok = n_tok[start + 3 ..];
93 const inner = if (mem.indexOf(u8, n_tok, "{*}")) |sub_end|
94 n_tok[0..sub_end]
95 else
96 n_tok;
97 if (mem.indexOf(u8, h_tok, inner) == null) return false;
98 start = mem.indexOf(u8, n_tok, "{*}") orelse break;
99 }
100 } else if (mem.startsWith(u8, needle_tok, "{")) {
101 const closing_brace = mem.indexOf(u8, needle_tok, "}") orelse return error.MissingClosingBrace;
102 if (closing_brace != needle_tok.len - 1) return error.ClosingBraceNotLast;
103
104 const name = needle_tok[1..closing_brace];
105 if (name.len == 0) return error.MissingBraceValue;
106 const value = try std.fmt.parseInt(u64, hay_tok, 16);
107 candidate_var = .{
108 .name = name,
109 .value = value,
110 };
111 } else {
112 if (!mem.eql(u8, hay_tok, needle_tok)) return false;
113 }
114 }
115
116 if (candidate_var) |v| {
117 try global_vars.putNoClobber(v.name, v.value);
118 }
119
120 return true;
121 }
122
123 /// Will return true if the `phrase` is correctly parsed into an RPN program and
124 /// its reduced, computed value compares using `op` with the expected value, either
125 /// a literal or another extracted variable.
126 fn computeCmp(act: Action, gpa: Allocator, global_vars: anytype) !bool {
127 var op_stack = std.ArrayList(enum { add, sub, mod, mul }).init(gpa);
128 var values = std.ArrayList(u64).init(gpa);
129
130 var it = mem.tokenize(u8, act.phrase, " ");
131 while (it.next()) |next| {
132 if (mem.eql(u8, next, "+")) {
133 try op_stack.append(.add);
134 } else if (mem.eql(u8, next, "-")) {
135 try op_stack.append(.sub);
136 } else if (mem.eql(u8, next, "%")) {
137 try op_stack.append(.mod);
138 } else if (mem.eql(u8, next, "*")) {
139 try op_stack.append(.mul);
140 } else {
141 const val = std.fmt.parseInt(u64, next, 0) catch blk: {
142 break :blk global_vars.get(next) orelse {
143 std.debug.print(
144 \\
145 \\========= Variable was not extracted: ===========
146 \\{s}
147 \\
148 , .{next});
149 return error.UnknownVariable;
150 };
151 };
152 try values.append(val);
153 }
154 }
155
156 var op_i: usize = 1;
157 var reduced: u64 = values.items[0];
158 for (op_stack.items) |op| {
159 const other = values.items[op_i];
160 switch (op) {
161 .add => {
162 reduced += other;
163 },
164 .sub => {
165 reduced -= other;
166 },
167 .mod => {
168 reduced %= other;
169 },
170 .mul => {
171 reduced *= other;
172 },
173 }
174 op_i += 1;
175 }
176
177 const exp_value = switch (act.expected.?.value) {
178 .variable => |name| global_vars.get(name) orelse {
179 std.debug.print(
180 \\
181 \\========= Variable was not extracted: ===========
182 \\{s}
183 \\
184 , .{name});
185 return error.UnknownVariable;
186 },
187 .literal => |x| x,
188 };
189 return math.compare(reduced, act.expected.?.op, exp_value);
190 }
191};
192
193const ComputeCompareExpected = struct {
194 op: math.CompareOperator,
195 value: union(enum) {
196 variable: []const u8,
197 literal: u64,
198 },
199
200 pub fn format(
201 value: @This(),
202 comptime fmt: []const u8,
203 options: std.fmt.FormatOptions,
204 writer: anytype,
205 ) !void {
206 if (fmt.len != 0) std.fmt.invalidFmtError(fmt, value);
207 _ = options;
208 try writer.print("{s} ", .{@tagName(value.op)});
209 switch (value.value) {
210 .variable => |name| try writer.writeAll(name),
211 .literal => |x| try writer.print("{x}", .{x}),
212 }
213 }
214};
215
216const Check = struct {
217 builder: *std.Build,
218 actions: std.ArrayList(Action),
219
220 fn create(b: *std.Build) Check {
221 return .{
222 .builder = b,
223 .actions = std.ArrayList(Action).init(b.allocator),
224 };
225 }
226
227 fn match(self: *Check, phrase: []const u8) void {
228 self.actions.append(.{
229 .tag = .match,
230 .phrase = self.builder.dupe(phrase),
231 }) catch @panic("OOM");
232 }
233
234 fn notPresent(self: *Check, phrase: []const u8) void {
235 self.actions.append(.{
236 .tag = .not_present,
237 .phrase = self.builder.dupe(phrase),
238 }) catch @panic("OOM");
239 }
240
241 fn computeCmp(self: *Check, phrase: []const u8, expected: ComputeCompareExpected) void {
242 self.actions.append(.{
243 .tag = .compute_cmp,
244 .phrase = self.builder.dupe(phrase),
245 .expected = expected,
246 }) catch @panic("OOM");
247 }
248};
249
250/// Creates a new sequence of actions with `phrase` as the first anchor searched phrase.
251pub fn checkStart(self: *CheckObjectStep, phrase: []const u8) void {
252 var new_check = Check.create(self.builder);
253 new_check.match(phrase);
254 self.checks.append(new_check) catch @panic("OOM");
255}
256
257/// Adds another searched phrase to the latest created Check with `CheckObjectStep.checkStart(...)`.
258/// Asserts at least one check already exists.
259pub fn checkNext(self: *CheckObjectStep, phrase: []const u8) void {
260 assert(self.checks.items.len > 0);
261 const last = &self.checks.items[self.checks.items.len - 1];
262 last.match(phrase);
263}
264
265/// Adds another searched phrase to the latest created Check with `CheckObjectStep.checkStart(...)`
266/// however ensures there is no matching phrase in the output.
267/// Asserts at least one check already exists.
268pub fn checkNotPresent(self: *CheckObjectStep, phrase: []const u8) void {
269 assert(self.checks.items.len > 0);
270 const last = &self.checks.items[self.checks.items.len - 1];
271 last.notPresent(phrase);
272}
273
274/// Creates a new check checking specifically symbol table parsed and dumped from the object
275/// file.
276/// Issuing this check will force parsing and dumping of the symbol table.
277pub fn checkInSymtab(self: *CheckObjectStep) void {
278 self.dump_symtab = true;
279 const symtab_label = switch (self.obj_format) {
280 .macho => MachODumper.symtab_label,
281 else => @panic("TODO other parsers"),
282 };
283 self.checkStart(symtab_label);
284}
285
286/// Creates a new standalone, singular check which allows running simple binary operations
287/// on the extracted variables. It will then compare the reduced program with the value of
288/// the expected variable.
289pub fn checkComputeCompare(
290 self: *CheckObjectStep,
291 program: []const u8,
292 expected: ComputeCompareExpected,
293) void {
294 var new_check = Check.create(self.builder);
295 new_check.computeCmp(program, expected);
296 self.checks.append(new_check) catch @panic("OOM");
297}
298
299fn make(step: *Step) !void {
300 const self = @fieldParentPtr(CheckObjectStep, "step", step);
301
302 const gpa = self.builder.allocator;
303 const src_path = self.source.getPath(self.builder);
304 const contents = try fs.cwd().readFileAllocOptions(
305 gpa,
306 src_path,
307 self.max_bytes,
308 null,
309 @alignOf(u64),
310 null,
311 );
312
313 const output = switch (self.obj_format) {
314 .macho => try MachODumper.parseAndDump(contents, .{
315 .gpa = gpa,
316 .dump_symtab = self.dump_symtab,
317 }),
318 .elf => @panic("TODO elf parser"),
319 .coff => @panic("TODO coff parser"),
320 .wasm => try WasmDumper.parseAndDump(contents, .{
321 .gpa = gpa,
322 .dump_symtab = self.dump_symtab,
323 }),
324 else => unreachable,
325 };
326
327 var vars = std.StringHashMap(u64).init(gpa);
328
329 for (self.checks.items) |chk| {
330 var it = mem.tokenize(u8, output, "\r\n");
331 for (chk.actions.items) |act| {
332 switch (act.tag) {
333 .match => {
334 while (it.next()) |line| {
335 if (try act.match(line, &vars)) break;
336 } else {
337 std.debug.print(
338 \\
339 \\========= Expected to find: ==========================
340 \\{s}
341 \\========= But parsed file does not contain it: =======
342 \\{s}
343 \\
344 , .{ act.phrase, output });
345 return error.TestFailed;
346 }
347 },
348 .not_present => {
349 while (it.next()) |line| {
350 if (try act.match(line, &vars)) {
351 std.debug.print(
352 \\
353 \\========= Expected not to find: ===================
354 \\{s}
355 \\========= But parsed file does contain it: ========
356 \\{s}
357 \\
358 , .{ act.phrase, output });
359 return error.TestFailed;
360 }
361 }
362 },
363 .compute_cmp => {
364 const res = act.computeCmp(gpa, vars) catch |err| switch (err) {
365 error.UnknownVariable => {
366 std.debug.print(
367 \\========= From parsed file: =====================
368 \\{s}
369 \\
370 , .{output});
371 return error.TestFailed;
372 },
373 else => |e| return e,
374 };
375 if (!res) {
376 std.debug.print(
377 \\
378 \\========= Comparison failed for action: ===========
379 \\{s} {}
380 \\========= From parsed file: =======================
381 \\{s}
382 \\
383 , .{ act.phrase, act.expected.?, output });
384 return error.TestFailed;
385 }
386 },
387 }
388 }
389 }
390}
391
392const Opts = struct {
393 gpa: ?Allocator = null,
394 dump_symtab: bool = false,
395};
396
397const MachODumper = struct {
398 const LoadCommandIterator = macho.LoadCommandIterator;
399 const symtab_label = "symtab";
400
401 fn parseAndDump(bytes: []align(@alignOf(u64)) const u8, opts: Opts) ![]const u8 {
402 const gpa = opts.gpa orelse unreachable; // MachO dumper requires an allocator
403 var stream = std.io.fixedBufferStream(bytes);
404 const reader = stream.reader();
405
406 const hdr = try reader.readStruct(macho.mach_header_64);
407 if (hdr.magic != macho.MH_MAGIC_64) {
408 return error.InvalidMagicNumber;
409 }
410
411 var output = std.ArrayList(u8).init(gpa);
412 const writer = output.writer();
413
414 var symtab: []const macho.nlist_64 = undefined;
415 var strtab: []const u8 = undefined;
416 var sections = std.ArrayList(macho.section_64).init(gpa);
417 var imports = std.ArrayList([]const u8).init(gpa);
418
419 var it = LoadCommandIterator{
420 .ncmds = hdr.ncmds,
421 .buffer = bytes[@sizeOf(macho.mach_header_64)..][0..hdr.sizeofcmds],
422 };
423 var i: usize = 0;
424 while (it.next()) |cmd| {
425 switch (cmd.cmd()) {
426 .SEGMENT_64 => {
427 const seg = cmd.cast(macho.segment_command_64).?;
428 try sections.ensureUnusedCapacity(seg.nsects);
429 for (cmd.getSections()) |sect| {
430 sections.appendAssumeCapacity(sect);
431 }
432 },
433 .SYMTAB => if (opts.dump_symtab) {
434 const lc = cmd.cast(macho.symtab_command).?;
435 symtab = @ptrCast(
436 [*]const macho.nlist_64,
437 @alignCast(@alignOf(macho.nlist_64), &bytes[lc.symoff]),
438 )[0..lc.nsyms];
439 strtab = bytes[lc.stroff..][0..lc.strsize];
440 },
441 .LOAD_DYLIB,
442 .LOAD_WEAK_DYLIB,
443 .REEXPORT_DYLIB,
444 => {
445 try imports.append(cmd.getDylibPathName());
446 },
447 else => {},
448 }
449
450 try dumpLoadCommand(cmd, i, writer);
451 try writer.writeByte('\n');
452
453 i += 1;
454 }
455
456 if (opts.dump_symtab) {
457 try writer.print("{s}\n", .{symtab_label});
458 for (symtab) |sym| {
459 if (sym.stab()) continue;
460 const sym_name = mem.sliceTo(@ptrCast([*:0]const u8, strtab.ptr + sym.n_strx), 0);
461 if (sym.sect()) {
462 const sect = sections.items[sym.n_sect - 1];
463 try writer.print("{x} ({s},{s})", .{
464 sym.n_value,
465 sect.segName(),
466 sect.sectName(),
467 });
468 if (sym.ext()) {
469 try writer.writeAll(" external");
470 }
471 try writer.print(" {s}\n", .{sym_name});
472 } else if (sym.undf()) {
473 const ordinal = @divTrunc(@bitCast(i16, sym.n_desc), macho.N_SYMBOL_RESOLVER);
474 const import_name = blk: {
475 if (ordinal <= 0) {
476 if (ordinal == macho.BIND_SPECIAL_DYLIB_SELF)
477 break :blk "self import";
478 if (ordinal == macho.BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE)
479 break :blk "main executable";
480 if (ordinal == macho.BIND_SPECIAL_DYLIB_FLAT_LOOKUP)
481 break :blk "flat lookup";
482 unreachable;
483 }
484 const full_path = imports.items[@bitCast(u16, ordinal) - 1];
485 const basename = fs.path.basename(full_path);
486 assert(basename.len > 0);
487 const ext = mem.lastIndexOfScalar(u8, basename, '.') orelse basename.len;
488 break :blk basename[0..ext];
489 };
490 try writer.writeAll("(undefined)");
491 if (sym.weakRef()) {
492 try writer.writeAll(" weak");
493 }
494 if (sym.ext()) {
495 try writer.writeAll(" external");
496 }
497 try writer.print(" {s} (from {s})\n", .{
498 sym_name,
499 import_name,
500 });
501 } else unreachable;
502 }
503 }
504
505 return output.toOwnedSlice();
506 }
507
508 fn dumpLoadCommand(lc: macho.LoadCommandIterator.LoadCommand, index: usize, writer: anytype) !void {
509 // print header first
510 try writer.print(
511 \\LC {d}
512 \\cmd {s}
513 \\cmdsize {d}
514 , .{ index, @tagName(lc.cmd()), lc.cmdsize() });
515
516 switch (lc.cmd()) {
517 .SEGMENT_64 => {
518 const seg = lc.cast(macho.segment_command_64).?;
519 try writer.writeByte('\n');
520 try writer.print(
521 \\segname {s}
522 \\vmaddr {x}
523 \\vmsize {x}
524 \\fileoff {x}
525 \\filesz {x}
526 , .{
527 seg.segName(),
528 seg.vmaddr,
529 seg.vmsize,
530 seg.fileoff,
531 seg.filesize,
532 });
533
534 for (lc.getSections()) |sect| {
535 try writer.writeByte('\n');
536 try writer.print(
537 \\sectname {s}
538 \\addr {x}
539 \\size {x}
540 \\offset {x}
541 \\align {x}
542 , .{
543 sect.sectName(),
544 sect.addr,
545 sect.size,
546 sect.offset,
547 sect.@"align",
548 });
549 }
550 },
551
552 .ID_DYLIB,
553 .LOAD_DYLIB,
554 .LOAD_WEAK_DYLIB,
555 .REEXPORT_DYLIB,
556 => {
557 const dylib = lc.cast(macho.dylib_command).?;
558 try writer.writeByte('\n');
559 try writer.print(
560 \\name {s}
561 \\timestamp {d}
562 \\current version {x}
563 \\compatibility version {x}
564 , .{
565 lc.getDylibPathName(),
566 dylib.dylib.timestamp,
567 dylib.dylib.current_version,
568 dylib.dylib.compatibility_version,
569 });
570 },
571
572 .MAIN => {
573 const main = lc.cast(macho.entry_point_command).?;
574 try writer.writeByte('\n');
575 try writer.print(
576 \\entryoff {x}
577 \\stacksize {x}
578 , .{ main.entryoff, main.stacksize });
579 },
580
581 .RPATH => {
582 try writer.writeByte('\n');
583 try writer.print(
584 \\path {s}
585 , .{
586 lc.getRpathPathName(),
587 });
588 },
589
590 .UUID => {
591 const uuid = lc.cast(macho.uuid_command).?;
592 try writer.writeByte('\n');
593 try writer.print("uuid {x}", .{std.fmt.fmtSliceHexLower(&uuid.uuid)});
594 },
595
596 .DATA_IN_CODE,
597 .FUNCTION_STARTS,
598 .CODE_SIGNATURE,
599 => {
600 const llc = lc.cast(macho.linkedit_data_command).?;
601 try writer.writeByte('\n');
602 try writer.print(
603 \\dataoff {x}
604 \\datasize {x}
605 , .{ llc.dataoff, llc.datasize });
606 },
607
608 .DYLD_INFO_ONLY => {
609 const dlc = lc.cast(macho.dyld_info_command).?;
610 try writer.writeByte('\n');
611 try writer.print(
612 \\rebaseoff {x}
613 \\rebasesize {x}
614 \\bindoff {x}
615 \\bindsize {x}
616 \\weakbindoff {x}
617 \\weakbindsize {x}
618 \\lazybindoff {x}
619 \\lazybindsize {x}
620 \\exportoff {x}
621 \\exportsize {x}
622 , .{
623 dlc.rebase_off,
624 dlc.rebase_size,
625 dlc.bind_off,
626 dlc.bind_size,
627 dlc.weak_bind_off,
628 dlc.weak_bind_size,
629 dlc.lazy_bind_off,
630 dlc.lazy_bind_size,
631 dlc.export_off,
632 dlc.export_size,
633 });
634 },
635
636 .SYMTAB => {
637 const slc = lc.cast(macho.symtab_command).?;
638 try writer.writeByte('\n');
639 try writer.print(
640 \\symoff {x}
641 \\nsyms {x}
642 \\stroff {x}
643 \\strsize {x}
644 , .{
645 slc.symoff,
646 slc.nsyms,
647 slc.stroff,
648 slc.strsize,
649 });
650 },
651
652 .DYSYMTAB => {
653 const dlc = lc.cast(macho.dysymtab_command).?;
654 try writer.writeByte('\n');
655 try writer.print(
656 \\ilocalsym {x}
657 \\nlocalsym {x}
658 \\iextdefsym {x}
659 \\nextdefsym {x}
660 \\iundefsym {x}
661 \\nundefsym {x}
662 \\indirectsymoff {x}
663 \\nindirectsyms {x}
664 , .{
665 dlc.ilocalsym,
666 dlc.nlocalsym,
667 dlc.iextdefsym,
668 dlc.nextdefsym,
669 dlc.iundefsym,
670 dlc.nundefsym,
671 dlc.indirectsymoff,
672 dlc.nindirectsyms,
673 });
674 },
675
676 else => {},
677 }
678 }
679};
680
681const WasmDumper = struct {
682 const symtab_label = "symbols";
683
684 fn parseAndDump(bytes: []const u8, opts: Opts) ![]const u8 {
685 const gpa = opts.gpa orelse unreachable; // Wasm dumper requires an allocator
686 if (opts.dump_symtab) {
687 @panic("TODO: Implement symbol table parsing and dumping");
688 }
689
690 var fbs = std.io.fixedBufferStream(bytes);
691 const reader = fbs.reader();
692
693 const buf = try reader.readBytesNoEof(8);
694 if (!mem.eql(u8, buf[0..4], &std.wasm.magic)) {
695 return error.InvalidMagicByte;
696 }
697 if (!mem.eql(u8, buf[4..], &std.wasm.version)) {
698 return error.UnsupportedWasmVersion;
699 }
700
701 var output = std.ArrayList(u8).init(gpa);
702 errdefer output.deinit();
703 const writer = output.writer();
704
705 while (reader.readByte()) |current_byte| {
706 const section = std.meta.intToEnum(std.wasm.Section, current_byte) catch |err| {
707 std.debug.print("Found invalid section id '{d}'\n", .{current_byte});
708 return err;
709 };
710
711 const section_length = try std.leb.readULEB128(u32, reader);
712 try parseAndDumpSection(section, bytes[fbs.pos..][0..section_length], writer);
713 fbs.pos += section_length;
714 } else |_| {} // reached end of stream
715
716 return output.toOwnedSlice();
717 }
718
719 fn parseAndDumpSection(section: std.wasm.Section, data: []const u8, writer: anytype) !void {
720 var fbs = std.io.fixedBufferStream(data);
721 const reader = fbs.reader();
722
723 try writer.print(
724 \\Section {s}
725 \\size {d}
726 , .{ @tagName(section), data.len });
727
728 switch (section) {
729 .type,
730 .import,
731 .function,
732 .table,
733 .memory,
734 .global,
735 .@"export",
736 .element,
737 .code,
738 .data,
739 => {
740 const entries = try std.leb.readULEB128(u32, reader);
741 try writer.print("\nentries {d}\n", .{entries});
742 try dumpSection(section, data[fbs.pos..], entries, writer);
743 },
744 .custom => {
745 const name_length = try std.leb.readULEB128(u32, reader);
746 const name = data[fbs.pos..][0..name_length];
747 fbs.pos += name_length;
748 try writer.print("\nname {s}\n", .{name});
749
750 if (mem.eql(u8, name, "name")) {
751 try parseDumpNames(reader, writer, data);
752 } else if (mem.eql(u8, name, "producers")) {
753 try parseDumpProducers(reader, writer, data);
754 } else if (mem.eql(u8, name, "target_features")) {
755 try parseDumpFeatures(reader, writer, data);
756 }
757 // TODO: Implement parsing and dumping other custom sections (such as relocations)
758 },
759 .start => {
760 const start = try std.leb.readULEB128(u32, reader);
761 try writer.print("\nstart {d}\n", .{start});
762 },
763 else => {}, // skip unknown sections
764 }
765 }
766
767 fn dumpSection(section: std.wasm.Section, data: []const u8, entries: u32, writer: anytype) !void {
768 var fbs = std.io.fixedBufferStream(data);
769 const reader = fbs.reader();
770
771 switch (section) {
772 .type => {
773 var i: u32 = 0;
774 while (i < entries) : (i += 1) {
775 const func_type = try reader.readByte();
776 if (func_type != std.wasm.function_type) {
777 std.debug.print("Expected function type, found byte '{d}'\n", .{func_type});
778 return error.UnexpectedByte;
779 }
780 const params = try std.leb.readULEB128(u32, reader);
781 try writer.print("params {d}\n", .{params});
782 var index: u32 = 0;
783 while (index < params) : (index += 1) {
784 try parseDumpType(std.wasm.Valtype, reader, writer);
785 } else index = 0;
786 const returns = try std.leb.readULEB128(u32, reader);
787 try writer.print("returns {d}\n", .{returns});
788 while (index < returns) : (index += 1) {
789 try parseDumpType(std.wasm.Valtype, reader, writer);
790 }
791 }
792 },
793 .import => {
794 var i: u32 = 0;
795 while (i < entries) : (i += 1) {
796 const module_name_len = try std.leb.readULEB128(u32, reader);
797 const module_name = data[fbs.pos..][0..module_name_len];
798 fbs.pos += module_name_len;
799 const name_len = try std.leb.readULEB128(u32, reader);
800 const name = data[fbs.pos..][0..name_len];
801 fbs.pos += name_len;
802
803 const kind = std.meta.intToEnum(std.wasm.ExternalKind, try reader.readByte()) catch |err| {
804 std.debug.print("Invalid import kind\n", .{});
805 return err;
806 };
807
808 try writer.print(
809 \\module {s}
810 \\name {s}
811 \\kind {s}
812 , .{ module_name, name, @tagName(kind) });
813 try writer.writeByte('\n');
814 switch (kind) {
815 .function => {
816 try writer.print("index {d}\n", .{try std.leb.readULEB128(u32, reader)});
817 },
818 .memory => {
819 try parseDumpLimits(reader, writer);
820 },
821 .global => {
822 try parseDumpType(std.wasm.Valtype, reader, writer);
823 try writer.print("mutable {}\n", .{0x01 == try std.leb.readULEB128(u32, reader)});
824 },
825 .table => {
826 try parseDumpType(std.wasm.RefType, reader, writer);
827 try parseDumpLimits(reader, writer);
828 },
829 }
830 }
831 },
832 .function => {
833 var i: u32 = 0;
834 while (i < entries) : (i += 1) {
835 try writer.print("index {d}\n", .{try std.leb.readULEB128(u32, reader)});
836 }
837 },
838 .table => {
839 var i: u32 = 0;
840 while (i < entries) : (i += 1) {
841 try parseDumpType(std.wasm.RefType, reader, writer);
842 try parseDumpLimits(reader, writer);
843 }
844 },
845 .memory => {
846 var i: u32 = 0;
847 while (i < entries) : (i += 1) {
848 try parseDumpLimits(reader, writer);
849 }
850 },
851 .global => {
852 var i: u32 = 0;
853 while (i < entries) : (i += 1) {
854 try parseDumpType(std.wasm.Valtype, reader, writer);
855 try writer.print("mutable {}\n", .{0x01 == try std.leb.readULEB128(u1, reader)});
856 try parseDumpInit(reader, writer);
857 }
858 },
859 .@"export" => {
860 var i: u32 = 0;
861 while (i < entries) : (i += 1) {
862 const name_len = try std.leb.readULEB128(u32, reader);
863 const name = data[fbs.pos..][0..name_len];
864 fbs.pos += name_len;
865 const kind_byte = try std.leb.readULEB128(u8, reader);
866 const kind = std.meta.intToEnum(std.wasm.ExternalKind, kind_byte) catch |err| {
867 std.debug.print("invalid export kind value '{d}'\n", .{kind_byte});
868 return err;
869 };
870 const index = try std.leb.readULEB128(u32, reader);
871 try writer.print(
872 \\name {s}
873 \\kind {s}
874 \\index {d}
875 , .{ name, @tagName(kind), index });
876 try writer.writeByte('\n');
877 }
878 },
879 .element => {
880 var i: u32 = 0;
881 while (i < entries) : (i += 1) {
882 try writer.print("table index {d}\n", .{try std.leb.readULEB128(u32, reader)});
883 try parseDumpInit(reader, writer);
884
885 const function_indexes = try std.leb.readULEB128(u32, reader);
886 var function_index: u32 = 0;
887 try writer.print("indexes {d}\n", .{function_indexes});
888 while (function_index < function_indexes) : (function_index += 1) {
889 try writer.print("index {d}\n", .{try std.leb.readULEB128(u32, reader)});
890 }
891 }
892 },
893 .code => {}, // code section is considered opaque to linker
894 .data => {
895 var i: u32 = 0;
896 while (i < entries) : (i += 1) {
897 const index = try std.leb.readULEB128(u32, reader);
898 try writer.print("memory index 0x{x}\n", .{index});
899 try parseDumpInit(reader, writer);
900 const size = try std.leb.readULEB128(u32, reader);
901 try writer.print("size {d}\n", .{size});
902 try reader.skipBytes(size, .{}); // we do not care about the content of the segments
903 }
904 },
905 else => unreachable,
906 }
907 }
908
909 fn parseDumpType(comptime WasmType: type, reader: anytype, writer: anytype) !void {
910 const type_byte = try reader.readByte();
911 const valtype = std.meta.intToEnum(WasmType, type_byte) catch |err| {
912 std.debug.print("Invalid wasm type value '{d}'\n", .{type_byte});
913 return err;
914 };
915 try writer.print("type {s}\n", .{@tagName(valtype)});
916 }
917
918 fn parseDumpLimits(reader: anytype, writer: anytype) !void {
919 const flags = try std.leb.readULEB128(u8, reader);
920 const min = try std.leb.readULEB128(u32, reader);
921
922 try writer.print("min {x}\n", .{min});
923 if (flags != 0) {
924 try writer.print("max {x}\n", .{try std.leb.readULEB128(u32, reader)});
925 }
926 }
927
928 fn parseDumpInit(reader: anytype, writer: anytype) !void {
929 const byte = try std.leb.readULEB128(u8, reader);
930 const opcode = std.meta.intToEnum(std.wasm.Opcode, byte) catch |err| {
931 std.debug.print("invalid wasm opcode '{d}'\n", .{byte});
932 return err;
933 };
934 switch (opcode) {
935 .i32_const => try writer.print("i32.const {x}\n", .{try std.leb.readILEB128(i32, reader)}),
936 .i64_const => try writer.print("i64.const {x}\n", .{try std.leb.readILEB128(i64, reader)}),
937 .f32_const => try writer.print("f32.const {x}\n", .{@bitCast(f32, try reader.readIntLittle(u32))}),
938 .f64_const => try writer.print("f64.const {x}\n", .{@bitCast(f64, try reader.readIntLittle(u64))}),
939 .global_get => try writer.print("global.get {x}\n", .{try std.leb.readULEB128(u32, reader)}),
940 else => unreachable,
941 }
942 const end_opcode = try std.leb.readULEB128(u8, reader);
943 if (end_opcode != std.wasm.opcode(.end)) {
944 std.debug.print("expected 'end' opcode in init expression\n", .{});
945 return error.MissingEndOpcode;
946 }
947 }
948
949 fn parseDumpNames(reader: anytype, writer: anytype, data: []const u8) !void {
950 while (reader.context.pos < data.len) {
951 try parseDumpType(std.wasm.NameSubsection, reader, writer);
952 const size = try std.leb.readULEB128(u32, reader);
953 const entries = try std.leb.readULEB128(u32, reader);
954 try writer.print(
955 \\size {d}
956 \\names {d}
957 , .{ size, entries });
958 try writer.writeByte('\n');
959 var i: u32 = 0;
960 while (i < entries) : (i += 1) {
961 const index = try std.leb.readULEB128(u32, reader);
962 const name_len = try std.leb.readULEB128(u32, reader);
963 const pos = reader.context.pos;
964 const name = data[pos..][0..name_len];
965 reader.context.pos += name_len;
966
967 try writer.print(
968 \\index {d}
969 \\name {s}
970 , .{ index, name });
971 try writer.writeByte('\n');
972 }
973 }
974 }
975
976 fn parseDumpProducers(reader: anytype, writer: anytype, data: []const u8) !void {
977 const field_count = try std.leb.readULEB128(u32, reader);
978 try writer.print("fields {d}\n", .{field_count});
979 var current_field: u32 = 0;
980 while (current_field < field_count) : (current_field += 1) {
981 const field_name_length = try std.leb.readULEB128(u32, reader);
982 const field_name = data[reader.context.pos..][0..field_name_length];
983 reader.context.pos += field_name_length;
984
985 const value_count = try std.leb.readULEB128(u32, reader);
986 try writer.print(
987 \\field_name {s}
988 \\values {d}
989 , .{ field_name, value_count });
990 try writer.writeByte('\n');
991 var current_value: u32 = 0;
992 while (current_value < value_count) : (current_value += 1) {
993 const value_length = try std.leb.readULEB128(u32, reader);
994 const value = data[reader.context.pos..][0..value_length];
995 reader.context.pos += value_length;
996
997 const version_length = try std.leb.readULEB128(u32, reader);
998 const version = data[reader.context.pos..][0..version_length];
999 reader.context.pos += version_length;
1000
1001 try writer.print(
1002 \\value_name {s}
1003 \\version {s}
1004 , .{ value, version });
1005 try writer.writeByte('\n');
1006 }
1007 }
1008 }
1009
1010 fn parseDumpFeatures(reader: anytype, writer: anytype, data: []const u8) !void {
1011 const feature_count = try std.leb.readULEB128(u32, reader);
1012 try writer.print("features {d}\n", .{feature_count});
1013
1014 var index: u32 = 0;
1015 while (index < feature_count) : (index += 1) {
1016 const prefix_byte = try std.leb.readULEB128(u8, reader);
1017 const name_length = try std.leb.readULEB128(u32, reader);
1018 const feature_name = data[reader.context.pos..][0..name_length];
1019 reader.context.pos += name_length;
1020
1021 try writer.print("{c} {s}\n", .{ prefix_byte, feature_name });
1022 }
1023 }
1024};
lib/std/Build/CompileStep.zig created+2053
...@@ -0,0 +1,2053 @@
1const builtin = @import("builtin");
2const std = @import("../std.zig");
3const mem = std.mem;
4const log = std.log;
5const fs = std.fs;
6const assert = std.debug.assert;
7const panic = std.debug.panic;
8const ArrayList = std.ArrayList;
9const StringHashMap = std.StringHashMap;
10const Sha256 = std.crypto.hash.sha2.Sha256;
11const Allocator = mem.Allocator;
12const Step = std.Build.Step;
13const CrossTarget = std.zig.CrossTarget;
14const NativeTargetInfo = std.zig.system.NativeTargetInfo;
15const FileSource = std.Build.FileSource;
16const PkgConfigPkg = std.Build.PkgConfigPkg;
17const PkgConfigError = std.Build.PkgConfigError;
18const ExecError = std.Build.ExecError;
19const Pkg = std.Build.Pkg;
20const VcpkgRoot = std.Build.VcpkgRoot;
21const InstallDir = std.Build.InstallDir;
22const InstallArtifactStep = std.Build.InstallArtifactStep;
23const GeneratedFile = std.Build.GeneratedFile;
24const InstallRawStep = std.Build.InstallRawStep;
25const EmulatableRunStep = std.Build.EmulatableRunStep;
26const CheckObjectStep = std.Build.CheckObjectStep;
27const RunStep = std.Build.RunStep;
28const OptionsStep = std.Build.OptionsStep;
29const ConfigHeaderStep = std.Build.ConfigHeaderStep;
30const CompileStep = @This();
31
32pub const base_id: Step.Id = .compile;
33
34step: Step,
35builder: *std.Build,
36name: []const u8,
37target: CrossTarget,
38target_info: NativeTargetInfo,
39optimize: std.builtin.Mode,
40linker_script: ?FileSource = null,
41version_script: ?[]const u8 = null,
42out_filename: []const u8,
43linkage: ?Linkage = null,
44version: ?std.builtin.Version,
45kind: Kind,
46major_only_filename: ?[]const u8,
47name_only_filename: ?[]const u8,
48strip: ?bool,
49unwind_tables: ?bool,
50// keep in sync with src/link.zig:CompressDebugSections
51compress_debug_sections: enum { none, zlib } = .none,
52lib_paths: ArrayList([]const u8),
53rpaths: ArrayList([]const u8),
54framework_dirs: ArrayList([]const u8),
55frameworks: StringHashMap(FrameworkLinkInfo),
56verbose_link: bool,
57verbose_cc: bool,
58emit_analysis: EmitOption = .default,
59emit_asm: EmitOption = .default,
60emit_bin: EmitOption = .default,
61emit_docs: EmitOption = .default,
62emit_implib: EmitOption = .default,
63emit_llvm_bc: EmitOption = .default,
64emit_llvm_ir: EmitOption = .default,
65// Lots of things depend on emit_h having a consistent path,
66// so it is not an EmitOption for now.
67emit_h: bool = false,
68bundle_compiler_rt: ?bool = null,
69single_threaded: ?bool = null,
70stack_protector: ?bool = null,
71disable_stack_probing: bool,
72disable_sanitize_c: bool,
73sanitize_thread: bool,
74rdynamic: bool,
75import_memory: bool = false,
76/// For WebAssembly targets, this will allow for undefined symbols to
77/// be imported from the host environment.
78import_symbols: bool = false,
79import_table: bool = false,
80export_table: bool = false,
81initial_memory: ?u64 = null,
82max_memory: ?u64 = null,
83shared_memory: bool = false,
84global_base: ?u64 = null,
85c_std: std.Build.CStd,
86override_lib_dir: ?[]const u8,
87main_pkg_path: ?[]const u8,
88exec_cmd_args: ?[]const ?[]const u8,
89name_prefix: []const u8,
90filter: ?[]const u8,
91test_evented_io: bool = false,
92test_runner: ?[]const u8,
93code_model: std.builtin.CodeModel = .default,
94wasi_exec_model: ?std.builtin.WasiExecModel = null,
95/// Symbols to be exported when compiling to wasm
96export_symbol_names: []const []const u8 = &.{},
97
98root_src: ?FileSource,
99out_h_filename: []const u8,
100out_lib_filename: []const u8,
101out_pdb_filename: []const u8,
102packages: ArrayList(Pkg),
103
104object_src: []const u8,
105
106link_objects: ArrayList(LinkObject),
107include_dirs: ArrayList(IncludeDir),
108c_macros: ArrayList([]const u8),
109installed_headers: ArrayList(*Step),
110output_dir: ?[]const u8,
111is_linking_libc: bool = false,
112is_linking_libcpp: bool = false,
113vcpkg_bin_path: ?[]const u8 = null,
114
115/// This may be set in order to override the default install directory
116override_dest_dir: ?InstallDir,
117installed_path: ?[]const u8,
118install_step: ?*InstallArtifactStep,
119
120/// Base address for an executable image.
121image_base: ?u64 = null,
122
123libc_file: ?FileSource = null,
124
125valgrind_support: ?bool = null,
126each_lib_rpath: ?bool = null,
127/// On ELF targets, this will emit a link section called ".note.gnu.build-id"
128/// which can be used to coordinate a stripped binary with its debug symbols.
129/// As an example, the bloaty project refuses to work unless its inputs have
130/// build ids, in order to prevent accidental mismatches.
131/// The default is to not include this section because it slows down linking.
132build_id: ?bool = null,
133
134/// Create a .eh_frame_hdr section and a PT_GNU_EH_FRAME segment in the ELF
135/// file.
136link_eh_frame_hdr: bool = false,
137link_emit_relocs: bool = false,
138
139/// Place every function in its own section so that unused ones may be
140/// safely garbage-collected during the linking phase.
141link_function_sections: bool = false,
142
143/// Remove functions and data that are unreachable by the entry point or
144/// exported symbols.
145link_gc_sections: ?bool = null,
146
147linker_allow_shlib_undefined: ?bool = null,
148
149/// Permit read-only relocations in read-only segments. Disallowed by default.
150link_z_notext: bool = false,
151
152/// Force all relocations to be read-only after processing.
153link_z_relro: bool = true,
154
155/// Allow relocations to be lazily processed after load.
156link_z_lazy: bool = false,
157
158/// Common page size
159link_z_common_page_size: ?u64 = null,
160
161/// Maximum page size
162link_z_max_page_size: ?u64 = null,
163
164/// (Darwin) Install name for the dylib
165install_name: ?[]const u8 = null,
166
167/// (Darwin) Path to entitlements file
168entitlements: ?[]const u8 = null,
169
170/// (Darwin) Size of the pagezero segment.
171pagezero_size: ?u64 = null,
172
173/// (Darwin) Search strategy for searching system libraries. Either `paths_first` or `dylibs_first`.
174/// The former lowers to `-search_paths_first` linker option, while the latter to `-search_dylibs_first`
175/// option.
176/// By default, if no option is specified, the linker assumes `paths_first` as the default
177/// search strategy.
178search_strategy: ?enum { paths_first, dylibs_first } = null,
179
180/// (Darwin) Set size of the padding between the end of load commands
181/// and start of `__TEXT,__text` section.
182headerpad_size: ?u32 = null,
183
184/// (Darwin) Automatically Set size of the padding between the end of load commands
185/// and start of `__TEXT,__text` section to a value fitting all paths expanded to MAXPATHLEN.
186headerpad_max_install_names: bool = false,
187
188/// (Darwin) Remove dylibs that are unreachable by the entry point or exported symbols.
189dead_strip_dylibs: bool = false,
190
191/// Position Independent Code
192force_pic: ?bool = null,
193
194/// Position Independent Executable
195pie: ?bool = null,
196
197red_zone: ?bool = null,
198
199omit_frame_pointer: ?bool = null,
200dll_export_fns: ?bool = null,
201
202subsystem: ?std.Target.SubSystem = null,
203
204entry_symbol_name: ?[]const u8 = null,
205
206/// Overrides the default stack size
207stack_size: ?u64 = null,
208
209want_lto: ?bool = null,
210use_llvm: ?bool = null,
211use_lld: ?bool = null,
212
213output_path_source: GeneratedFile,
214output_lib_path_source: GeneratedFile,
215output_h_path_source: GeneratedFile,
216output_pdb_path_source: GeneratedFile,
217
218pub const CSourceFiles = struct {
219 files: []const []const u8,
220 flags: []const []const u8,
221};
222
223pub const CSourceFile = struct {
224 source: FileSource,
225 args: []const []const u8,
226
227 pub fn dupe(self: CSourceFile, b: *std.Build) CSourceFile {
228 return .{
229 .source = self.source.dupe(b),
230 .args = b.dupeStrings(self.args),
231 };
232 }
233};
234
235pub const LinkObject = union(enum) {
236 static_path: FileSource,
237 other_step: *CompileStep,
238 system_lib: SystemLib,
239 assembly_file: FileSource,
240 c_source_file: *CSourceFile,
241 c_source_files: *CSourceFiles,
242};
243
244pub const SystemLib = struct {
245 name: []const u8,
246 needed: bool,
247 weak: bool,
248 use_pkg_config: enum {
249 /// Don't use pkg-config, just pass -lfoo where foo is name.
250 no,
251 /// Try to get information on how to link the library from pkg-config.
252 /// If that fails, fall back to passing -lfoo where foo is name.
253 yes,
254 /// Try to get information on how to link the library from pkg-config.
255 /// If that fails, error out.
256 force,
257 },
258};
259
260const FrameworkLinkInfo = struct {
261 needed: bool = false,
262 weak: bool = false,
263};
264
265pub const IncludeDir = union(enum) {
266 raw_path: []const u8,
267 raw_path_system: []const u8,
268 other_step: *CompileStep,
269 config_header_step: *ConfigHeaderStep,
270};
271
272pub const Options = struct {
273 name: []const u8,
274 root_source_file: ?FileSource = null,
275 target: CrossTarget,
276 optimize: std.builtin.Mode,
277 kind: Kind,
278 linkage: ?Linkage = null,
279 version: ?std.builtin.Version = null,
280};
281
282pub const Kind = enum {
283 exe,
284 lib,
285 obj,
286 @"test",
287 test_exe,
288};
289
290pub const Linkage = enum { dynamic, static };
291
292pub const EmitOption = union(enum) {
293 default: void,
294 no_emit: void,
295 emit: void,
296 emit_to: []const u8,
297
298 fn getArg(self: @This(), b: *std.Build, arg_name: []const u8) ?[]const u8 {
299 return switch (self) {
300 .no_emit => b.fmt("-fno-{s}", .{arg_name}),
301 .default => null,
302 .emit => b.fmt("-f{s}", .{arg_name}),
303 .emit_to => |path| b.fmt("-f{s}={s}", .{ arg_name, path }),
304 };
305 }
306};
307
308pub fn create(builder: *std.Build, options: Options) *CompileStep {
309 const name = builder.dupe(options.name);
310 const root_src: ?FileSource = if (options.root_source_file) |rsrc| rsrc.dupe(builder) else null;
311 if (mem.indexOf(u8, name, "/") != null or mem.indexOf(u8, name, "\\") != null) {
312 panic("invalid name: '{s}'. It looks like a file path, but it is supposed to be the library or application name.", .{name});
313 }
314
315 const self = builder.allocator.create(CompileStep) catch @panic("OOM");
316 self.* = CompileStep{
317 .strip = null,
318 .unwind_tables = null,
319 .builder = builder,
320 .verbose_link = false,
321 .verbose_cc = false,
322 .optimize = options.optimize,
323 .target = options.target,
324 .linkage = options.linkage,
325 .kind = options.kind,
326 .root_src = root_src,
327 .name = name,
328 .frameworks = StringHashMap(FrameworkLinkInfo).init(builder.allocator),
329 .step = Step.init(base_id, name, builder.allocator, make),
330 .version = options.version,
331 .out_filename = undefined,
332 .out_h_filename = builder.fmt("{s}.h", .{name}),
333 .out_lib_filename = undefined,
334 .out_pdb_filename = builder.fmt("{s}.pdb", .{name}),
335 .major_only_filename = null,
336 .name_only_filename = null,
337 .packages = ArrayList(Pkg).init(builder.allocator),
338 .include_dirs = ArrayList(IncludeDir).init(builder.allocator),
339 .link_objects = ArrayList(LinkObject).init(builder.allocator),
340 .c_macros = ArrayList([]const u8).init(builder.allocator),
341 .lib_paths = ArrayList([]const u8).init(builder.allocator),
342 .rpaths = ArrayList([]const u8).init(builder.allocator),
343 .framework_dirs = ArrayList([]const u8).init(builder.allocator),
344 .installed_headers = ArrayList(*Step).init(builder.allocator),
345 .object_src = undefined,
346 .c_std = std.Build.CStd.C99,
347 .override_lib_dir = null,
348 .main_pkg_path = null,
349 .exec_cmd_args = null,
350 .name_prefix = "",
351 .filter = null,
352 .test_runner = null,
353 .disable_stack_probing = false,
354 .disable_sanitize_c = false,
355 .sanitize_thread = false,
356 .rdynamic = false,
357 .output_dir = null,
358 .override_dest_dir = null,
359 .installed_path = null,
360 .install_step = null,
361
362 .output_path_source = GeneratedFile{ .step = &self.step },
363 .output_lib_path_source = GeneratedFile{ .step = &self.step },
364 .output_h_path_source = GeneratedFile{ .step = &self.step },
365 .output_pdb_path_source = GeneratedFile{ .step = &self.step },
366
367 .target_info = NativeTargetInfo.detect(self.target) catch @panic("unhandled error"),
368 };
369 self.computeOutFileNames();
370 if (root_src) |rs| rs.addStepDependencies(&self.step);
371 return self;
372}
373
374fn computeOutFileNames(self: *CompileStep) void {
375 const target = self.target_info.target;
376
377 self.out_filename = std.zig.binNameAlloc(self.builder.allocator, .{
378 .root_name = self.name,
379 .target = target,
380 .output_mode = switch (self.kind) {
381 .lib => .Lib,
382 .obj => .Obj,
383 .exe, .@"test", .test_exe => .Exe,
384 },
385 .link_mode = if (self.linkage) |some| @as(std.builtin.LinkMode, switch (some) {
386 .dynamic => .Dynamic,
387 .static => .Static,
388 }) else null,
389 .version = self.version,
390 }) catch @panic("OOM");
391
392 if (self.kind == .lib) {
393 if (self.linkage != null and self.linkage.? == .static) {
394 self.out_lib_filename = self.out_filename;
395 } else if (self.version) |version| {
396 if (target.isDarwin()) {
397 self.major_only_filename = self.builder.fmt("lib{s}.{d}.dylib", .{
398 self.name,
399 version.major,
400 });
401 self.name_only_filename = self.builder.fmt("lib{s}.dylib", .{self.name});
402 self.out_lib_filename = self.out_filename;
403 } else if (target.os.tag == .windows) {
404 self.out_lib_filename = self.builder.fmt("{s}.lib", .{self.name});
405 } else {
406 self.major_only_filename = self.builder.fmt("lib{s}.so.{d}", .{ self.name, version.major });
407 self.name_only_filename = self.builder.fmt("lib{s}.so", .{self.name});
408 self.out_lib_filename = self.out_filename;
409 }
410 } else {
411 if (target.isDarwin()) {
412 self.out_lib_filename = self.out_filename;
413 } else if (target.os.tag == .windows) {
414 self.out_lib_filename = self.builder.fmt("{s}.lib", .{self.name});
415 } else {
416 self.out_lib_filename = self.out_filename;
417 }
418 }
419 if (self.output_dir != null) {
420 self.output_lib_path_source.path = self.builder.pathJoin(
421 &.{ self.output_dir.?, self.out_lib_filename },
422 );
423 }
424 }
425}
426
427pub fn setOutputDir(self: *CompileStep, dir: []const u8) void {
428 self.output_dir = self.builder.dupePath(dir);
429}
430
431pub fn install(self: *CompileStep) void {
432 self.builder.installArtifact(self);
433}
434
435pub fn installRaw(self: *CompileStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep {
436 return self.builder.installRaw(self, dest_filename, options);
437}
438
439pub fn installHeader(a: *CompileStep, src_path: []const u8, dest_rel_path: []const u8) void {
440 const install_file = a.builder.addInstallHeaderFile(src_path, dest_rel_path);
441 a.builder.getInstallStep().dependOn(&install_file.step);
442 a.installed_headers.append(&install_file.step) catch @panic("OOM");
443}
444
445pub fn installHeadersDirectory(
446 a: *CompileStep,
447 src_dir_path: []const u8,
448 dest_rel_path: []const u8,
449) void {
450 return installHeadersDirectoryOptions(a, .{
451 .source_dir = src_dir_path,
452 .install_dir = .header,
453 .install_subdir = dest_rel_path,
454 });
455}
456
457pub fn installHeadersDirectoryOptions(
458 a: *CompileStep,
459 options: std.Build.InstallDirStep.Options,
460) void {
461 const install_dir = a.builder.addInstallDirectory(options);
462 a.builder.getInstallStep().dependOn(&install_dir.step);
463 a.installed_headers.append(&install_dir.step) catch @panic("OOM");
464}
465
466pub fn installLibraryHeaders(a: *CompileStep, l: *CompileStep) void {
467 assert(l.kind == .lib);
468 const install_step = a.builder.getInstallStep();
469 // Copy each element from installed_headers, modifying the builder
470 // to be the new parent's builder.
471 for (l.installed_headers.items) |step| {
472 const step_copy = switch (step.id) {
473 inline .install_file, .install_dir => |id| blk: {
474 const T = id.Type();
475 const ptr = a.builder.allocator.create(T) catch @panic("OOM");
476 ptr.* = step.cast(T).?.*;
477 ptr.override_source_builder = ptr.builder;
478 ptr.builder = a.builder;
479 break :blk &ptr.step;
480 },
481 else => unreachable,
482 };
483 a.installed_headers.append(step_copy) catch @panic("OOM");
484 install_step.dependOn(step_copy);
485 }
486 a.installed_headers.appendSlice(l.installed_headers.items) catch @panic("OOM");
487}
488
489/// Creates a `RunStep` with an executable built with `addExecutable`.
490/// Add command line arguments with `addArg`.
491pub fn run(exe: *CompileStep) *RunStep {
492 assert(exe.kind == .exe or exe.kind == .test_exe);
493
494 // It doesn't have to be native. We catch that if you actually try to run it.
495 // Consider that this is declarative; the run step may not be run unless a user
496 // option is supplied.
497 const run_step = RunStep.create(exe.builder, exe.builder.fmt("run {s}", .{exe.step.name}));
498 run_step.addArtifactArg(exe);
499
500 if (exe.kind == .test_exe) {
501 run_step.addArg(exe.builder.zig_exe);
502 }
503
504 if (exe.vcpkg_bin_path) |path| {
505 run_step.addPathDir(path);
506 }
507
508 return run_step;
509}
510
511/// Creates an `EmulatableRunStep` with an executable built with `addExecutable`.
512/// Allows running foreign binaries through emulation platforms such as Qemu or Rosetta.
513/// When a binary cannot be ran through emulation or the option is disabled, a warning
514/// will be printed and the binary will *NOT* be ran.
515pub fn runEmulatable(exe: *CompileStep) *EmulatableRunStep {
516 assert(exe.kind == .exe or exe.kind == .test_exe);
517
518 const run_step = EmulatableRunStep.create(exe.builder, exe.builder.fmt("run {s}", .{exe.step.name}), exe);
519 if (exe.vcpkg_bin_path) |path| {
520 RunStep.addPathDirInternal(&run_step.step, exe.builder, path);
521 }
522 return run_step;
523}
524
525pub fn checkObject(self: *CompileStep, obj_format: std.Target.ObjectFormat) *CheckObjectStep {
526 return CheckObjectStep.create(self.builder, self.getOutputSource(), obj_format);
527}
528
529pub fn setLinkerScriptPath(self: *CompileStep, source: FileSource) void {
530 self.linker_script = source.dupe(self.builder);
531 source.addStepDependencies(&self.step);
532}
533
534pub fn linkFramework(self: *CompileStep, framework_name: []const u8) void {
535 self.frameworks.put(self.builder.dupe(framework_name), .{}) catch @panic("OOM");
536}
537
538pub fn linkFrameworkNeeded(self: *CompileStep, framework_name: []const u8) void {
539 self.frameworks.put(self.builder.dupe(framework_name), .{
540 .needed = true,
541 }) catch @panic("OOM");
542}
543
544pub fn linkFrameworkWeak(self: *CompileStep, framework_name: []const u8) void {
545 self.frameworks.put(self.builder.dupe(framework_name), .{
546 .weak = true,
547 }) catch @panic("OOM");
548}
549
550/// Returns whether the library, executable, or object depends on a particular system library.
551pub fn dependsOnSystemLibrary(self: CompileStep, name: []const u8) bool {
552 if (isLibCLibrary(name)) {
553 return self.is_linking_libc;
554 }
555 if (isLibCppLibrary(name)) {
556 return self.is_linking_libcpp;
557 }
558 for (self.link_objects.items) |link_object| {
559 switch (link_object) {
560 .system_lib => |lib| if (mem.eql(u8, lib.name, name)) return true,
561 else => continue,
562 }
563 }
564 return false;
565}
566
567pub fn linkLibrary(self: *CompileStep, lib: *CompileStep) void {
568 assert(lib.kind == .lib);
569 self.linkLibraryOrObject(lib);
570}
571
572pub fn isDynamicLibrary(self: *CompileStep) bool {
573 return self.kind == .lib and self.linkage == Linkage.dynamic;
574}
575
576pub fn isStaticLibrary(self: *CompileStep) bool {
577 return self.kind == .lib and self.linkage != Linkage.dynamic;
578}
579
580pub fn producesPdbFile(self: *CompileStep) bool {
581 if (!self.target.isWindows() and !self.target.isUefi()) return false;
582 if (self.target.getObjectFormat() == .c) return false;
583 if (self.strip == true) return false;
584 return self.isDynamicLibrary() or self.kind == .exe or self.kind == .test_exe;
585}
586
587pub fn linkLibC(self: *CompileStep) void {
588 self.is_linking_libc = true;
589}
590
591pub fn linkLibCpp(self: *CompileStep) void {
592 self.is_linking_libcpp = true;
593}
594
595/// If the value is omitted, it is set to 1.
596/// `name` and `value` need not live longer than the function call.
597pub fn defineCMacro(self: *CompileStep, name: []const u8, value: ?[]const u8) void {
598 const macro = std.Build.constructCMacro(self.builder.allocator, name, value);
599 self.c_macros.append(macro) catch @panic("OOM");
600}
601
602/// name_and_value looks like [name]=[value]. If the value is omitted, it is set to 1.
603pub fn defineCMacroRaw(self: *CompileStep, name_and_value: []const u8) void {
604 self.c_macros.append(self.builder.dupe(name_and_value)) catch @panic("OOM");
605}
606
607/// This one has no integration with anything, it just puts -lname on the command line.
608/// Prefer to use `linkSystemLibrary` instead.
609pub fn linkSystemLibraryName(self: *CompileStep, name: []const u8) void {
610 self.link_objects.append(.{
611 .system_lib = .{
612 .name = self.builder.dupe(name),
613 .needed = false,
614 .weak = false,
615 .use_pkg_config = .no,
616 },
617 }) catch @panic("OOM");
618}
619
620/// This one has no integration with anything, it just puts -needed-lname on the command line.
621/// Prefer to use `linkSystemLibraryNeeded` instead.
622pub fn linkSystemLibraryNeededName(self: *CompileStep, name: []const u8) void {
623 self.link_objects.append(.{
624 .system_lib = .{
625 .name = self.builder.dupe(name),
626 .needed = true,
627 .weak = false,
628 .use_pkg_config = .no,
629 },
630 }) catch @panic("OOM");
631}
632
633/// Darwin-only. This one has no integration with anything, it just puts -weak-lname on the
634/// command line. Prefer to use `linkSystemLibraryWeak` instead.
635pub fn linkSystemLibraryWeakName(self: *CompileStep, name: []const u8) void {
636 self.link_objects.append(.{
637 .system_lib = .{
638 .name = self.builder.dupe(name),
639 .needed = false,
640 .weak = true,
641 .use_pkg_config = .no,
642 },
643 }) catch @panic("OOM");
644}
645
646/// This links against a system library, exclusively using pkg-config to find the library.
647/// Prefer to use `linkSystemLibrary` instead.
648pub fn linkSystemLibraryPkgConfigOnly(self: *CompileStep, lib_name: []const u8) void {
649 self.link_objects.append(.{
650 .system_lib = .{
651 .name = self.builder.dupe(lib_name),
652 .needed = false,
653 .weak = false,
654 .use_pkg_config = .force,
655 },
656 }) catch @panic("OOM");
657}
658
659/// This links against a system library, exclusively using pkg-config to find the library.
660/// Prefer to use `linkSystemLibraryNeeded` instead.
661pub fn linkSystemLibraryNeededPkgConfigOnly(self: *CompileStep, lib_name: []const u8) void {
662 self.link_objects.append(.{
663 .system_lib = .{
664 .name = self.builder.dupe(lib_name),
665 .needed = true,
666 .weak = false,
667 .use_pkg_config = .force,
668 },
669 }) catch @panic("OOM");
670}
671
672/// Run pkg-config for the given library name and parse the output, returning the arguments
673/// that should be passed to zig to link the given library.
674pub fn runPkgConfig(self: *CompileStep, lib_name: []const u8) ![]const []const u8 {
675 const pkg_name = match: {
676 // First we have to map the library name to pkg config name. Unfortunately,
677 // there are several examples where this is not straightforward:
678 // -lSDL2 -> pkg-config sdl2
679 // -lgdk-3 -> pkg-config gdk-3.0
680 // -latk-1.0 -> pkg-config atk
681 const pkgs = try getPkgConfigList(self.builder);
682
683 // Exact match means instant winner.
684 for (pkgs) |pkg| {
685 if (mem.eql(u8, pkg.name, lib_name)) {
686 break :match pkg.name;
687 }
688 }
689
690 // Next we'll try ignoring case.
691 for (pkgs) |pkg| {
692 if (std.ascii.eqlIgnoreCase(pkg.name, lib_name)) {
693 break :match pkg.name;
694 }
695 }
696
697 // Now try appending ".0".
698 for (pkgs) |pkg| {
699 if (std.ascii.indexOfIgnoreCase(pkg.name, lib_name)) |pos| {
700 if (pos != 0) continue;
701 if (mem.eql(u8, pkg.name[lib_name.len..], ".0")) {
702 break :match pkg.name;
703 }
704 }
705 }
706
707 // Trimming "-1.0".
708 if (mem.endsWith(u8, lib_name, "-1.0")) {
709 const trimmed_lib_name = lib_name[0 .. lib_name.len - "-1.0".len];
710 for (pkgs) |pkg| {
711 if (std.ascii.eqlIgnoreCase(pkg.name, trimmed_lib_name)) {
712 break :match pkg.name;
713 }
714 }
715 }
716
717 return error.PackageNotFound;
718 };
719
720 var code: u8 = undefined;
721 const stdout = if (self.builder.execAllowFail(&[_][]const u8{
722 "pkg-config",
723 pkg_name,
724 "--cflags",
725 "--libs",
726 }, &code, .Ignore)) |stdout| stdout else |err| switch (err) {
727 error.ProcessTerminated => return error.PkgConfigCrashed,
728 error.ExecNotSupported => return error.PkgConfigFailed,
729 error.ExitCodeFailure => return error.PkgConfigFailed,
730 error.FileNotFound => return error.PkgConfigNotInstalled,
731 error.ChildExecFailed => return error.PkgConfigFailed,
732 else => return err,
733 };
734
735 var zig_args = ArrayList([]const u8).init(self.builder.allocator);
736 defer zig_args.deinit();
737
738 var it = mem.tokenize(u8, stdout, " \r\n\t");
739 while (it.next()) |tok| {
740 if (mem.eql(u8, tok, "-I")) {
741 const dir = it.next() orelse return error.PkgConfigInvalidOutput;
742 try zig_args.appendSlice(&[_][]const u8{ "-I", dir });
743 } else if (mem.startsWith(u8, tok, "-I")) {
744 try zig_args.append(tok);
745 } else if (mem.eql(u8, tok, "-L")) {
746 const dir = it.next() orelse return error.PkgConfigInvalidOutput;
747 try zig_args.appendSlice(&[_][]const u8{ "-L", dir });
748 } else if (mem.startsWith(u8, tok, "-L")) {
749 try zig_args.append(tok);
750 } else if (mem.eql(u8, tok, "-l")) {
751 const lib = it.next() orelse return error.PkgConfigInvalidOutput;
752 try zig_args.appendSlice(&[_][]const u8{ "-l", lib });
753 } else if (mem.startsWith(u8, tok, "-l")) {
754 try zig_args.append(tok);
755 } else if (mem.eql(u8, tok, "-D")) {
756 const macro = it.next() orelse return error.PkgConfigInvalidOutput;
757 try zig_args.appendSlice(&[_][]const u8{ "-D", macro });
758 } else if (mem.startsWith(u8, tok, "-D")) {
759 try zig_args.append(tok);
760 } else if (self.builder.verbose) {
761 log.warn("Ignoring pkg-config flag '{s}'", .{tok});
762 }
763 }
764
765 return zig_args.toOwnedSlice();
766}
767
768pub fn linkSystemLibrary(self: *CompileStep, name: []const u8) void {
769 self.linkSystemLibraryInner(name, .{});
770}
771
772pub fn linkSystemLibraryNeeded(self: *CompileStep, name: []const u8) void {
773 self.linkSystemLibraryInner(name, .{ .needed = true });
774}
775
776pub fn linkSystemLibraryWeak(self: *CompileStep, name: []const u8) void {
777 self.linkSystemLibraryInner(name, .{ .weak = true });
778}
779
780fn linkSystemLibraryInner(self: *CompileStep, name: []const u8, opts: struct {
781 needed: bool = false,
782 weak: bool = false,
783}) void {
784 if (isLibCLibrary(name)) {
785 self.linkLibC();
786 return;
787 }
788 if (isLibCppLibrary(name)) {
789 self.linkLibCpp();
790 return;
791 }
792
793 self.link_objects.append(.{
794 .system_lib = .{
795 .name = self.builder.dupe(name),
796 .needed = opts.needed,
797 .weak = opts.weak,
798 .use_pkg_config = .yes,
799 },
800 }) catch @panic("OOM");
801}
802
803pub fn setNamePrefix(self: *CompileStep, text: []const u8) void {
804 assert(self.kind == .@"test" or self.kind == .test_exe);
805 self.name_prefix = self.builder.dupe(text);
806}
807
808pub fn setFilter(self: *CompileStep, text: ?[]const u8) void {
809 assert(self.kind == .@"test" or self.kind == .test_exe);
810 self.filter = if (text) |t| self.builder.dupe(t) else null;
811}
812
813pub fn setTestRunner(self: *CompileStep, path: ?[]const u8) void {
814 assert(self.kind == .@"test" or self.kind == .test_exe);
815 self.test_runner = if (path) |p| self.builder.dupePath(p) else null;
816}
817
818/// Handy when you have many C/C++ source files and want them all to have the same flags.
819pub fn addCSourceFiles(self: *CompileStep, files: []const []const u8, flags: []const []const u8) void {
820 const c_source_files = self.builder.allocator.create(CSourceFiles) catch @panic("OOM");
821
822 const files_copy = self.builder.dupeStrings(files);
823 const flags_copy = self.builder.dupeStrings(flags);
824
825 c_source_files.* = .{
826 .files = files_copy,
827 .flags = flags_copy,
828 };
829 self.link_objects.append(.{ .c_source_files = c_source_files }) catch @panic("OOM");
830}
831
832pub fn addCSourceFile(self: *CompileStep, file: []const u8, flags: []const []const u8) void {
833 self.addCSourceFileSource(.{
834 .args = flags,
835 .source = .{ .path = file },
836 });
837}
838
839pub fn addCSourceFileSource(self: *CompileStep, source: CSourceFile) void {
840 const c_source_file = self.builder.allocator.create(CSourceFile) catch @panic("OOM");
841 c_source_file.* = source.dupe(self.builder);
842 self.link_objects.append(.{ .c_source_file = c_source_file }) catch @panic("OOM");
843 source.source.addStepDependencies(&self.step);
844}
845
846pub fn setVerboseLink(self: *CompileStep, value: bool) void {
847 self.verbose_link = value;
848}
849
850pub fn setVerboseCC(self: *CompileStep, value: bool) void {
851 self.verbose_cc = value;
852}
853
854pub fn overrideZigLibDir(self: *CompileStep, dir_path: []const u8) void {
855 self.override_lib_dir = self.builder.dupePath(dir_path);
856}
857
858pub fn setMainPkgPath(self: *CompileStep, dir_path: []const u8) void {
859 self.main_pkg_path = self.builder.dupePath(dir_path);
860}
861
862pub fn setLibCFile(self: *CompileStep, libc_file: ?FileSource) void {
863 self.libc_file = if (libc_file) |f| f.dupe(self.builder) else null;
864}
865
866/// Returns the generated executable, library or object file.
867/// To run an executable built with zig build, use `run`, or create an install step and invoke it.
868pub fn getOutputSource(self: *CompileStep) FileSource {
869 return FileSource{ .generated = &self.output_path_source };
870}
871
872/// Returns the generated import library. This function can only be called for libraries.
873pub fn getOutputLibSource(self: *CompileStep) FileSource {
874 assert(self.kind == .lib);
875 return FileSource{ .generated = &self.output_lib_path_source };
876}
877
878/// Returns the generated header file.
879/// This function can only be called for libraries or object files which have `emit_h` set.
880pub fn getOutputHSource(self: *CompileStep) FileSource {
881 assert(self.kind != .exe and self.kind != .test_exe and self.kind != .@"test");
882 assert(self.emit_h);
883 return FileSource{ .generated = &self.output_h_path_source };
884}
885
886/// Returns the generated PDB file. This function can only be called for Windows and UEFI.
887pub fn getOutputPdbSource(self: *CompileStep) FileSource {
888 // TODO: Is this right? Isn't PDB for *any* PE/COFF file?
889 assert(self.target.isWindows() or self.target.isUefi());
890 return FileSource{ .generated = &self.output_pdb_path_source };
891}
892
893pub fn addAssemblyFile(self: *CompileStep, path: []const u8) void {
894 self.link_objects.append(.{
895 .assembly_file = .{ .path = self.builder.dupe(path) },
896 }) catch @panic("OOM");
897}
898
899pub fn addAssemblyFileSource(self: *CompileStep, source: FileSource) void {
900 const source_duped = source.dupe(self.builder);
901 self.link_objects.append(.{ .assembly_file = source_duped }) catch @panic("OOM");
902 source_duped.addStepDependencies(&self.step);
903}
904
905pub fn addObjectFile(self: *CompileStep, source_file: []const u8) void {
906 self.addObjectFileSource(.{ .path = source_file });
907}
908
909pub fn addObjectFileSource(self: *CompileStep, source: FileSource) void {
910 self.link_objects.append(.{ .static_path = source.dupe(self.builder) }) catch @panic("OOM");
911 source.addStepDependencies(&self.step);
912}
913
914pub fn addObject(self: *CompileStep, obj: *CompileStep) void {
915 assert(obj.kind == .obj);
916 self.linkLibraryOrObject(obj);
917}
918
919pub const addSystemIncludeDir = @compileError("deprecated; use addSystemIncludePath");
920pub const addIncludeDir = @compileError("deprecated; use addIncludePath");
921pub const addLibPath = @compileError("deprecated, use addLibraryPath");
922pub const addFrameworkDir = @compileError("deprecated, use addFrameworkPath");
923
924pub fn addSystemIncludePath(self: *CompileStep, path: []const u8) void {
925 self.include_dirs.append(IncludeDir{ .raw_path_system = self.builder.dupe(path) }) catch @panic("OOM");
926}
927
928pub fn addIncludePath(self: *CompileStep, path: []const u8) void {
929 self.include_dirs.append(IncludeDir{ .raw_path = self.builder.dupe(path) }) catch @panic("OOM");
930}
931
932pub fn addConfigHeader(self: *CompileStep, config_header: *ConfigHeaderStep) void {
933 self.step.dependOn(&config_header.step);
934 self.include_dirs.append(.{ .config_header_step = config_header }) catch @panic("OOM");
935}
936
937pub fn addLibraryPath(self: *CompileStep, path: []const u8) void {
938 self.lib_paths.append(self.builder.dupe(path)) catch @panic("OOM");
939}
940
941pub fn addRPath(self: *CompileStep, path: []const u8) void {
942 self.rpaths.append(self.builder.dupe(path)) catch @panic("OOM");
943}
944
945pub fn addFrameworkPath(self: *CompileStep, dir_path: []const u8) void {
946 self.framework_dirs.append(self.builder.dupe(dir_path)) catch @panic("OOM");
947}
948
949pub fn addPackage(self: *CompileStep, package: Pkg) void {
950 self.packages.append(self.builder.dupePkg(package)) catch @panic("OOM");
951 self.addRecursiveBuildDeps(package);
952}
953
954pub fn addOptions(self: *CompileStep, package_name: []const u8, options: *OptionsStep) void {
955 self.addPackage(options.getPackage(package_name));
956}
957
958fn addRecursiveBuildDeps(self: *CompileStep, package: Pkg) void {
959 package.source.addStepDependencies(&self.step);
960 if (package.dependencies) |deps| {
961 for (deps) |dep| {
962 self.addRecursiveBuildDeps(dep);
963 }
964 }
965}
966
967pub fn addPackagePath(self: *CompileStep, name: []const u8, pkg_index_path: []const u8) void {
968 self.addPackage(Pkg{
969 .name = self.builder.dupe(name),
970 .source = .{ .path = self.builder.dupe(pkg_index_path) },
971 });
972}
973
974/// If Vcpkg was found on the system, it will be added to include and lib
975/// paths for the specified target.
976pub fn addVcpkgPaths(self: *CompileStep, linkage: CompileStep.Linkage) !void {
977 // Ideally in the Unattempted case we would call the function recursively
978 // after findVcpkgRoot and have only one switch statement, but the compiler
979 // cannot resolve the error set.
980 switch (self.builder.vcpkg_root) {
981 .unattempted => {
982 self.builder.vcpkg_root = if (try findVcpkgRoot(self.builder.allocator)) |root|
983 VcpkgRoot{ .found = root }
984 else
985 .not_found;
986 },
987 .not_found => return error.VcpkgNotFound,
988 .found => {},
989 }
990
991 switch (self.builder.vcpkg_root) {
992 .unattempted => unreachable,
993 .not_found => return error.VcpkgNotFound,
994 .found => |root| {
995 const allocator = self.builder.allocator;
996 const triplet = try self.target.vcpkgTriplet(allocator, if (linkage == .static) .Static else .Dynamic);
997 defer self.builder.allocator.free(triplet);
998
999 const include_path = self.builder.pathJoin(&.{ root, "installed", triplet, "include" });
1000 errdefer allocator.free(include_path);
1001 try self.include_dirs.append(IncludeDir{ .raw_path = include_path });
1002
1003 const lib_path = self.builder.pathJoin(&.{ root, "installed", triplet, "lib" });
1004 try self.lib_paths.append(lib_path);
1005
1006 self.vcpkg_bin_path = self.builder.pathJoin(&.{ root, "installed", triplet, "bin" });
1007 },
1008 }
1009}
1010
1011pub fn setExecCmd(self: *CompileStep, args: []const ?[]const u8) void {
1012 assert(self.kind == .@"test");
1013 const duped_args = self.builder.allocator.alloc(?[]u8, args.len) catch @panic("OOM");
1014 for (args) |arg, i| {
1015 duped_args[i] = if (arg) |a| self.builder.dupe(a) else null;
1016 }
1017 self.exec_cmd_args = duped_args;
1018}
1019
1020fn linkLibraryOrObject(self: *CompileStep, other: *CompileStep) void {
1021 self.step.dependOn(&other.step);
1022 self.link_objects.append(.{ .other_step = other }) catch @panic("OOM");
1023 self.include_dirs.append(.{ .other_step = other }) catch @panic("OOM");
1024}
1025
1026fn makePackageCmd(self: *CompileStep, pkg: Pkg, zig_args: *ArrayList([]const u8)) error{OutOfMemory}!void {
1027 const builder = self.builder;
1028
1029 try zig_args.append("--pkg-begin");
1030 try zig_args.append(pkg.name);
1031 try zig_args.append(builder.pathFromRoot(pkg.source.getPath(self.builder)));
1032
1033 if (pkg.dependencies) |dependencies| {
1034 for (dependencies) |sub_pkg| {
1035 try self.makePackageCmd(sub_pkg, zig_args);
1036 }
1037 }
1038
1039 try zig_args.append("--pkg-end");
1040}
1041
1042fn make(step: *Step) !void {
1043 const self = @fieldParentPtr(CompileStep, "step", step);
1044 const builder = self.builder;
1045
1046 if (self.root_src == null and self.link_objects.items.len == 0) {
1047 log.err("{s}: linker needs 1 or more objects to link", .{self.step.name});
1048 return error.NeedAnObject;
1049 }
1050
1051 var zig_args = ArrayList([]const u8).init(builder.allocator);
1052 defer zig_args.deinit();
1053
1054 try zig_args.append(builder.zig_exe);
1055
1056 const cmd = switch (self.kind) {
1057 .lib => "build-lib",
1058 .exe => "build-exe",
1059 .obj => "build-obj",
1060 .@"test" => "test",
1061 .test_exe => "test",
1062 };
1063 try zig_args.append(cmd);
1064
1065 if (builder.color != .auto) {
1066 try zig_args.append("--color");
1067 try zig_args.append(@tagName(builder.color));
1068 }
1069
1070 if (builder.reference_trace) |some| {
1071 try zig_args.append(try std.fmt.allocPrint(builder.allocator, "-freference-trace={d}", .{some}));
1072 }
1073
1074 try addFlag(&zig_args, "LLVM", self.use_llvm);
1075 try addFlag(&zig_args, "LLD", self.use_lld);
1076
1077 if (self.target.ofmt) |ofmt| {
1078 try zig_args.append(try std.fmt.allocPrint(builder.allocator, "-ofmt={s}", .{@tagName(ofmt)}));
1079 }
1080
1081 if (self.entry_symbol_name) |entry| {
1082 try zig_args.append("--entry");
1083 try zig_args.append(entry);
1084 }
1085
1086 if (self.stack_size) |stack_size| {
1087 try zig_args.append("--stack");
1088 try zig_args.append(try std.fmt.allocPrint(builder.allocator, "{}", .{stack_size}));
1089 }
1090
1091 if (self.root_src) |root_src| try zig_args.append(root_src.getPath(builder));
1092
1093 // We will add link objects from transitive dependencies, but we want to keep
1094 // all link objects in the same order provided.
1095 // This array is used to keep self.link_objects immutable.
1096 var transitive_deps: TransitiveDeps = .{
1097 .link_objects = ArrayList(LinkObject).init(builder.allocator),
1098 .seen_system_libs = StringHashMap(void).init(builder.allocator),
1099 .seen_steps = std.AutoHashMap(*const Step, void).init(builder.allocator),
1100 .is_linking_libcpp = self.is_linking_libcpp,
1101 .is_linking_libc = self.is_linking_libc,
1102 .frameworks = &self.frameworks,
1103 };
1104
1105 try transitive_deps.seen_steps.put(&self.step, {});
1106 try transitive_deps.add(self.link_objects.items);
1107
1108 var prev_has_extra_flags = false;
1109
1110 for (transitive_deps.link_objects.items) |link_object| {
1111 switch (link_object) {
1112 .static_path => |static_path| try zig_args.append(static_path.getPath(builder)),
1113
1114 .other_step => |other| switch (other.kind) {
1115 .exe => @panic("Cannot link with an executable build artifact"),
1116 .test_exe => @panic("Cannot link with an executable build artifact"),
1117 .@"test" => @panic("Cannot link with a test"),
1118 .obj => {
1119 try zig_args.append(other.getOutputSource().getPath(builder));
1120 },
1121 .lib => l: {
1122 if (self.isStaticLibrary() and other.isStaticLibrary()) {
1123 // Avoid putting a static library inside a static library.
1124 break :l;
1125 }
1126
1127 const full_path_lib = other.getOutputLibSource().getPath(builder);
1128 try zig_args.append(full_path_lib);
1129
1130 if (other.linkage == Linkage.dynamic and !self.target.isWindows()) {
1131 if (fs.path.dirname(full_path_lib)) |dirname| {
1132 try zig_args.append("-rpath");
1133 try zig_args.append(dirname);
1134 }
1135 }
1136 },
1137 },
1138
1139 .system_lib => |system_lib| {
1140 const prefix: []const u8 = prefix: {
1141 if (system_lib.needed) break :prefix "-needed-l";
1142 if (system_lib.weak) {
1143 if (self.target.isDarwin()) break :prefix "-weak-l";
1144 log.warn("Weak library import used for a non-darwin target, this will be converted to normally library import `-lname`", .{});
1145 }
1146 break :prefix "-l";
1147 };
1148 switch (system_lib.use_pkg_config) {
1149 .no => try zig_args.append(builder.fmt("{s}{s}", .{ prefix, system_lib.name })),
1150 .yes, .force => {
1151 if (self.runPkgConfig(system_lib.name)) |args| {
1152 try zig_args.appendSlice(args);
1153 } else |err| switch (err) {
1154 error.PkgConfigInvalidOutput,
1155 error.PkgConfigCrashed,
1156 error.PkgConfigFailed,
1157 error.PkgConfigNotInstalled,
1158 error.PackageNotFound,
1159 => switch (system_lib.use_pkg_config) {
1160 .yes => {
1161 // pkg-config failed, so fall back to linking the library
1162 // by name directly.
1163 try zig_args.append(builder.fmt("{s}{s}", .{
1164 prefix,
1165 system_lib.name,
1166 }));
1167 },
1168 .force => {
1169 panic("pkg-config failed for library {s}", .{system_lib.name});
1170 },
1171 .no => unreachable,
1172 },
1173
1174 else => |e| return e,
1175 }
1176 },
1177 }
1178 },
1179
1180 .assembly_file => |asm_file| {
1181 if (prev_has_extra_flags) {
1182 try zig_args.append("-extra-cflags");
1183 try zig_args.append("--");
1184 prev_has_extra_flags = false;
1185 }
1186 try zig_args.append(asm_file.getPath(builder));
1187 },
1188
1189 .c_source_file => |c_source_file| {
1190 if (c_source_file.args.len == 0) {
1191 if (prev_has_extra_flags) {
1192 try zig_args.append("-cflags");
1193 try zig_args.append("--");
1194 prev_has_extra_flags = false;
1195 }
1196 } else {
1197 try zig_args.append("-cflags");
1198 for (c_source_file.args) |arg| {
1199 try zig_args.append(arg);
1200 }
1201 try zig_args.append("--");
1202 }
1203 try zig_args.append(c_source_file.source.getPath(builder));
1204 },
1205
1206 .c_source_files => |c_source_files| {
1207 if (c_source_files.flags.len == 0) {
1208 if (prev_has_extra_flags) {
1209 try zig_args.append("-cflags");
1210 try zig_args.append("--");
1211 prev_has_extra_flags = false;
1212 }
1213 } else {
1214 try zig_args.append("-cflags");
1215 for (c_source_files.flags) |flag| {
1216 try zig_args.append(flag);
1217 }
1218 try zig_args.append("--");
1219 }
1220 for (c_source_files.files) |file| {
1221 try zig_args.append(builder.pathFromRoot(file));
1222 }
1223 },
1224 }
1225 }
1226
1227 if (transitive_deps.is_linking_libcpp) {
1228 try zig_args.append("-lc++");
1229 }
1230
1231 if (transitive_deps.is_linking_libc) {
1232 try zig_args.append("-lc");
1233 }
1234
1235 if (self.image_base) |image_base| {
1236 try zig_args.append("--image-base");
1237 try zig_args.append(builder.fmt("0x{x}", .{image_base}));
1238 }
1239
1240 if (self.filter) |filter| {
1241 try zig_args.append("--test-filter");
1242 try zig_args.append(filter);
1243 }
1244
1245 if (self.test_evented_io) {
1246 try zig_args.append("--test-evented-io");
1247 }
1248
1249 if (self.name_prefix.len != 0) {
1250 try zig_args.append("--test-name-prefix");
1251 try zig_args.append(self.name_prefix);
1252 }
1253
1254 if (self.test_runner) |test_runner| {
1255 try zig_args.append("--test-runner");
1256 try zig_args.append(builder.pathFromRoot(test_runner));
1257 }
1258
1259 for (builder.debug_log_scopes) |log_scope| {
1260 try zig_args.append("--debug-log");
1261 try zig_args.append(log_scope);
1262 }
1263
1264 if (builder.debug_compile_errors) {
1265 try zig_args.append("--debug-compile-errors");
1266 }
1267
1268 if (builder.verbose_cimport) try zig_args.append("--verbose-cimport");
1269 if (builder.verbose_air) try zig_args.append("--verbose-air");
1270 if (builder.verbose_llvm_ir) try zig_args.append("--verbose-llvm-ir");
1271 if (builder.verbose_link or self.verbose_link) try zig_args.append("--verbose-link");
1272 if (builder.verbose_cc or self.verbose_cc) try zig_args.append("--verbose-cc");
1273 if (builder.verbose_llvm_cpu_features) try zig_args.append("--verbose-llvm-cpu-features");
1274
1275 if (self.emit_analysis.getArg(builder, "emit-analysis")) |arg| try zig_args.append(arg);
1276 if (self.emit_asm.getArg(builder, "emit-asm")) |arg| try zig_args.append(arg);
1277 if (self.emit_bin.getArg(builder, "emit-bin")) |arg| try zig_args.append(arg);
1278 if (self.emit_docs.getArg(builder, "emit-docs")) |arg| try zig_args.append(arg);
1279 if (self.emit_implib.getArg(builder, "emit-implib")) |arg| try zig_args.append(arg);
1280 if (self.emit_llvm_bc.getArg(builder, "emit-llvm-bc")) |arg| try zig_args.append(arg);
1281 if (self.emit_llvm_ir.getArg(builder, "emit-llvm-ir")) |arg| try zig_args.append(arg);
1282
1283 if (self.emit_h) try zig_args.append("-femit-h");
1284
1285 try addFlag(&zig_args, "strip", self.strip);
1286 try addFlag(&zig_args, "unwind-tables", self.unwind_tables);
1287
1288 switch (self.compress_debug_sections) {
1289 .none => {},
1290 .zlib => try zig_args.append("--compress-debug-sections=zlib"),
1291 }
1292
1293 if (self.link_eh_frame_hdr) {
1294 try zig_args.append("--eh-frame-hdr");
1295 }
1296 if (self.link_emit_relocs) {
1297 try zig_args.append("--emit-relocs");
1298 }
1299 if (self.link_function_sections) {
1300 try zig_args.append("-ffunction-sections");
1301 }
1302 if (self.link_gc_sections) |x| {
1303 try zig_args.append(if (x) "--gc-sections" else "--no-gc-sections");
1304 }
1305 if (self.linker_allow_shlib_undefined) |x| {
1306 try zig_args.append(if (x) "-fallow-shlib-undefined" else "-fno-allow-shlib-undefined");
1307 }
1308 if (self.link_z_notext) {
1309 try zig_args.append("-z");
1310 try zig_args.append("notext");
1311 }
1312 if (!self.link_z_relro) {
1313 try zig_args.append("-z");
1314 try zig_args.append("norelro");
1315 }
1316 if (self.link_z_lazy) {
1317 try zig_args.append("-z");
1318 try zig_args.append("lazy");
1319 }
1320 if (self.link_z_common_page_size) |size| {
1321 try zig_args.append("-z");
1322 try zig_args.append(builder.fmt("common-page-size={d}", .{size}));
1323 }
1324 if (self.link_z_max_page_size) |size| {
1325 try zig_args.append("-z");
1326 try zig_args.append(builder.fmt("max-page-size={d}", .{size}));
1327 }
1328
1329 if (self.libc_file) |libc_file| {
1330 try zig_args.append("--libc");
1331 try zig_args.append(libc_file.getPath(builder));
1332 } else if (builder.libc_file) |libc_file| {
1333 try zig_args.append("--libc");
1334 try zig_args.append(libc_file);
1335 }
1336
1337 switch (self.optimize) {
1338 .Debug => {}, // Skip since it's the default.
1339 else => try zig_args.append(builder.fmt("-O{s}", .{@tagName(self.optimize)})),
1340 }
1341
1342 try zig_args.append("--cache-dir");
1343 try zig_args.append(builder.pathFromRoot(builder.cache_root));
1344
1345 try zig_args.append("--global-cache-dir");
1346 try zig_args.append(builder.pathFromRoot(builder.global_cache_root));
1347
1348 try zig_args.append("--name");
1349 try zig_args.append(self.name);
1350
1351 if (self.linkage) |some| switch (some) {
1352 .dynamic => try zig_args.append("-dynamic"),
1353 .static => try zig_args.append("-static"),
1354 };
1355 if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic) {
1356 if (self.version) |version| {
1357 try zig_args.append("--version");
1358 try zig_args.append(builder.fmt("{}", .{version}));
1359 }
1360
1361 if (self.target.isDarwin()) {
1362 const install_name = self.install_name orelse builder.fmt("@rpath/{s}{s}{s}", .{
1363 self.target.libPrefix(),
1364 self.name,
1365 self.target.dynamicLibSuffix(),
1366 });
1367 try zig_args.append("-install_name");
1368 try zig_args.append(install_name);
1369 }
1370 }
1371
1372 if (self.entitlements) |entitlements| {
1373 try zig_args.appendSlice(&[_][]const u8{ "--entitlements", entitlements });
1374 }
1375 if (self.pagezero_size) |pagezero_size| {
1376 const size = try std.fmt.allocPrint(builder.allocator, "{x}", .{pagezero_size});
1377 try zig_args.appendSlice(&[_][]const u8{ "-pagezero_size", size });
1378 }
1379 if (self.search_strategy) |strat| switch (strat) {
1380 .paths_first => try zig_args.append("-search_paths_first"),
1381 .dylibs_first => try zig_args.append("-search_dylibs_first"),
1382 };
1383 if (self.headerpad_size) |headerpad_size| {
1384 const size = try std.fmt.allocPrint(builder.allocator, "{x}", .{headerpad_size});
1385 try zig_args.appendSlice(&[_][]const u8{ "-headerpad", size });
1386 }
1387 if (self.headerpad_max_install_names) {
1388 try zig_args.append("-headerpad_max_install_names");
1389 }
1390 if (self.dead_strip_dylibs) {
1391 try zig_args.append("-dead_strip_dylibs");
1392 }
1393
1394 try addFlag(&zig_args, "compiler-rt", self.bundle_compiler_rt);
1395 try addFlag(&zig_args, "single-threaded", self.single_threaded);
1396 if (self.disable_stack_probing) {
1397 try zig_args.append("-fno-stack-check");
1398 }
1399 try addFlag(&zig_args, "stack-protector", self.stack_protector);
1400 if (self.red_zone) |red_zone| {
1401 if (red_zone) {
1402 try zig_args.append("-mred-zone");
1403 } else {
1404 try zig_args.append("-mno-red-zone");
1405 }
1406 }
1407 try addFlag(&zig_args, "omit-frame-pointer", self.omit_frame_pointer);
1408 try addFlag(&zig_args, "dll-export-fns", self.dll_export_fns);
1409
1410 if (self.disable_sanitize_c) {
1411 try zig_args.append("-fno-sanitize-c");
1412 }
1413 if (self.sanitize_thread) {
1414 try zig_args.append("-fsanitize-thread");
1415 }
1416 if (self.rdynamic) {
1417 try zig_args.append("-rdynamic");
1418 }
1419 if (self.import_memory) {
1420 try zig_args.append("--import-memory");
1421 }
1422 if (self.import_symbols) {
1423 try zig_args.append("--import-symbols");
1424 }
1425 if (self.import_table) {
1426 try zig_args.append("--import-table");
1427 }
1428 if (self.export_table) {
1429 try zig_args.append("--export-table");
1430 }
1431 if (self.initial_memory) |initial_memory| {
1432 try zig_args.append(builder.fmt("--initial-memory={d}", .{initial_memory}));
1433 }
1434 if (self.max_memory) |max_memory| {
1435 try zig_args.append(builder.fmt("--max-memory={d}", .{max_memory}));
1436 }
1437 if (self.shared_memory) {
1438 try zig_args.append("--shared-memory");
1439 }
1440 if (self.global_base) |global_base| {
1441 try zig_args.append(builder.fmt("--global-base={d}", .{global_base}));
1442 }
1443
1444 if (self.code_model != .default) {
1445 try zig_args.append("-mcmodel");
1446 try zig_args.append(@tagName(self.code_model));
1447 }
1448 if (self.wasi_exec_model) |model| {
1449 try zig_args.append(builder.fmt("-mexec-model={s}", .{@tagName(model)}));
1450 }
1451 for (self.export_symbol_names) |symbol_name| {
1452 try zig_args.append(builder.fmt("--export={s}", .{symbol_name}));
1453 }
1454
1455 if (!self.target.isNative()) {
1456 try zig_args.appendSlice(&.{
1457 "-target", try self.target.zigTriple(builder.allocator),
1458 "-mcpu", try std.Build.serializeCpu(builder.allocator, self.target.getCpu()),
1459 });
1460
1461 if (self.target.dynamic_linker.get()) |dynamic_linker| {
1462 try zig_args.append("--dynamic-linker");
1463 try zig_args.append(dynamic_linker);
1464 }
1465 }
1466
1467 if (self.linker_script) |linker_script| {
1468 try zig_args.append("--script");
1469 try zig_args.append(linker_script.getPath(builder));
1470 }
1471
1472 if (self.version_script) |version_script| {
1473 try zig_args.append("--version-script");
1474 try zig_args.append(builder.pathFromRoot(version_script));
1475 }
1476
1477 if (self.kind == .@"test") {
1478 if (self.exec_cmd_args) |exec_cmd_args| {
1479 for (exec_cmd_args) |cmd_arg| {
1480 if (cmd_arg) |arg| {
1481 try zig_args.append("--test-cmd");
1482 try zig_args.append(arg);
1483 } else {
1484 try zig_args.append("--test-cmd-bin");
1485 }
1486 }
1487 } else {
1488 const need_cross_glibc = self.target.isGnuLibC() and transitive_deps.is_linking_libc;
1489
1490 switch (builder.host.getExternalExecutor(self.target_info, .{
1491 .qemu_fixes_dl = need_cross_glibc and builder.glibc_runtimes_dir != null,
1492 .link_libc = transitive_deps.is_linking_libc,
1493 })) {
1494 .native => {},
1495 .bad_dl, .bad_os_or_cpu => {
1496 try zig_args.append("--test-no-exec");
1497 },
1498 .rosetta => if (builder.enable_rosetta) {
1499 try zig_args.append("--test-cmd-bin");
1500 } else {
1501 try zig_args.append("--test-no-exec");
1502 },
1503 .qemu => |bin_name| ok: {
1504 if (builder.enable_qemu) qemu: {
1505 const glibc_dir_arg = if (need_cross_glibc)
1506 builder.glibc_runtimes_dir orelse break :qemu
1507 else
1508 null;
1509 try zig_args.append("--test-cmd");
1510 try zig_args.append(bin_name);
1511 if (glibc_dir_arg) |dir| {
1512 // TODO look into making this a call to `linuxTriple`. This
1513 // needs the directory to be called "i686" rather than
1514 // "x86" which is why we do it manually here.
1515 const fmt_str = "{s}" ++ fs.path.sep_str ++ "{s}-{s}-{s}";
1516 const cpu_arch = self.target.getCpuArch();
1517 const os_tag = self.target.getOsTag();
1518 const abi = self.target.getAbi();
1519 const cpu_arch_name: []const u8 = if (cpu_arch == .x86)
1520 "i686"
1521 else
1522 @tagName(cpu_arch);
1523 const full_dir = try std.fmt.allocPrint(builder.allocator, fmt_str, .{
1524 dir, cpu_arch_name, @tagName(os_tag), @tagName(abi),
1525 });
1526
1527 try zig_args.append("--test-cmd");
1528 try zig_args.append("-L");
1529 try zig_args.append("--test-cmd");
1530 try zig_args.append(full_dir);
1531 }
1532 try zig_args.append("--test-cmd-bin");
1533 break :ok;
1534 }
1535 try zig_args.append("--test-no-exec");
1536 },
1537 .wine => |bin_name| if (builder.enable_wine) {
1538 try zig_args.append("--test-cmd");
1539 try zig_args.append(bin_name);
1540 try zig_args.append("--test-cmd-bin");
1541 } else {
1542 try zig_args.append("--test-no-exec");
1543 },
1544 .wasmtime => |bin_name| if (builder.enable_wasmtime) {
1545 try zig_args.append("--test-cmd");
1546 try zig_args.append(bin_name);
1547 try zig_args.append("--test-cmd");
1548 try zig_args.append("--dir=.");
1549 try zig_args.append("--test-cmd-bin");
1550 } else {
1551 try zig_args.append("--test-no-exec");
1552 },
1553 .darling => |bin_name| if (builder.enable_darling) {
1554 try zig_args.append("--test-cmd");
1555 try zig_args.append(bin_name);
1556 try zig_args.append("--test-cmd-bin");
1557 } else {
1558 try zig_args.append("--test-no-exec");
1559 },
1560 }
1561 }
1562 } else if (self.kind == .test_exe) {
1563 try zig_args.append("--test-no-exec");
1564 }
1565
1566 for (self.packages.items) |pkg| {
1567 try self.makePackageCmd(pkg, &zig_args);
1568 }
1569
1570 for (self.include_dirs.items) |include_dir| {
1571 switch (include_dir) {
1572 .raw_path => |include_path| {
1573 try zig_args.append("-I");
1574 try zig_args.append(builder.pathFromRoot(include_path));
1575 },
1576 .raw_path_system => |include_path| {
1577 if (builder.sysroot != null) {
1578 try zig_args.append("-iwithsysroot");
1579 } else {
1580 try zig_args.append("-isystem");
1581 }
1582
1583 const resolved_include_path = builder.pathFromRoot(include_path);
1584
1585 const common_include_path = if (builtin.os.tag == .windows and builder.sysroot != null and fs.path.isAbsolute(resolved_include_path)) blk: {
1586 // We need to check for disk designator and strip it out from dir path so
1587 // that zig/clang can concat resolved_include_path with sysroot.
1588 const disk_designator = fs.path.diskDesignatorWindows(resolved_include_path);
1589
1590 if (mem.indexOf(u8, resolved_include_path, disk_designator)) |where| {
1591 break :blk resolved_include_path[where + disk_designator.len ..];
1592 }
1593
1594 break :blk resolved_include_path;
1595 } else resolved_include_path;
1596
1597 try zig_args.append(common_include_path);
1598 },
1599 .other_step => |other| {
1600 if (other.emit_h) {
1601 const h_path = other.getOutputHSource().getPath(builder);
1602 try zig_args.append("-isystem");
1603 try zig_args.append(fs.path.dirname(h_path).?);
1604 }
1605 if (other.installed_headers.items.len > 0) {
1606 for (other.installed_headers.items) |install_step| {
1607 try install_step.make();
1608 }
1609 try zig_args.append("-I");
1610 try zig_args.append(builder.pathJoin(&.{
1611 other.builder.install_prefix, "include",
1612 }));
1613 }
1614 },
1615 .config_header_step => |config_header| {
1616 try zig_args.append("-I");
1617 try zig_args.append(config_header.output_dir);
1618 },
1619 }
1620 }
1621
1622 for (self.lib_paths.items) |lib_path| {
1623 try zig_args.append("-L");
1624 try zig_args.append(lib_path);
1625 }
1626
1627 for (self.rpaths.items) |rpath| {
1628 try zig_args.append("-rpath");
1629 try zig_args.append(rpath);
1630 }
1631
1632 for (self.c_macros.items) |c_macro| {
1633 try zig_args.append("-D");
1634 try zig_args.append(c_macro);
1635 }
1636
1637 if (self.target.isDarwin()) {
1638 for (self.framework_dirs.items) |dir| {
1639 if (builder.sysroot != null) {
1640 try zig_args.append("-iframeworkwithsysroot");
1641 } else {
1642 try zig_args.append("-iframework");
1643 }
1644 try zig_args.append(dir);
1645 try zig_args.append("-F");
1646 try zig_args.append(dir);
1647 }
1648
1649 var it = self.frameworks.iterator();
1650 while (it.next()) |entry| {
1651 const name = entry.key_ptr.*;
1652 const info = entry.value_ptr.*;
1653 if (info.needed) {
1654 try zig_args.append("-needed_framework");
1655 } else if (info.weak) {
1656 try zig_args.append("-weak_framework");
1657 } else {
1658 try zig_args.append("-framework");
1659 }
1660 try zig_args.append(name);
1661 }
1662 } else {
1663 if (self.framework_dirs.items.len > 0) {
1664 log.info("Framework directories have been added for a non-darwin target, this will have no affect on the build", .{});
1665 }
1666
1667 if (self.frameworks.count() > 0) {
1668 log.info("Frameworks have been added for a non-darwin target, this will have no affect on the build", .{});
1669 }
1670 }
1671
1672 if (builder.sysroot) |sysroot| {
1673 try zig_args.appendSlice(&[_][]const u8{ "--sysroot", sysroot });
1674 }
1675
1676 for (builder.search_prefixes.items) |search_prefix| {
1677 try zig_args.append("-L");
1678 try zig_args.append(builder.pathJoin(&.{
1679 search_prefix, "lib",
1680 }));
1681 try zig_args.append("-I");
1682 try zig_args.append(builder.pathJoin(&.{
1683 search_prefix, "include",
1684 }));
1685 }
1686
1687 try addFlag(&zig_args, "valgrind", self.valgrind_support);
1688 try addFlag(&zig_args, "each-lib-rpath", self.each_lib_rpath);
1689 try addFlag(&zig_args, "build-id", self.build_id);
1690
1691 if (self.override_lib_dir) |dir| {
1692 try zig_args.append("--zig-lib-dir");
1693 try zig_args.append(builder.pathFromRoot(dir));
1694 } else if (builder.override_lib_dir) |dir| {
1695 try zig_args.append("--zig-lib-dir");
1696 try zig_args.append(builder.pathFromRoot(dir));
1697 }
1698
1699 if (self.main_pkg_path) |dir| {
1700 try zig_args.append("--main-pkg-path");
1701 try zig_args.append(builder.pathFromRoot(dir));
1702 }
1703
1704 try addFlag(&zig_args, "PIC", self.force_pic);
1705 try addFlag(&zig_args, "PIE", self.pie);
1706 try addFlag(&zig_args, "lto", self.want_lto);
1707
1708 if (self.subsystem) |subsystem| {
1709 try zig_args.append("--subsystem");
1710 try zig_args.append(switch (subsystem) {
1711 .Console => "console",
1712 .Windows => "windows",
1713 .Posix => "posix",
1714 .Native => "native",
1715 .EfiApplication => "efi_application",
1716 .EfiBootServiceDriver => "efi_boot_service_driver",
1717 .EfiRom => "efi_rom",
1718 .EfiRuntimeDriver => "efi_runtime_driver",
1719 });
1720 }
1721
1722 try zig_args.append("--enable-cache");
1723
1724 // Windows has an argument length limit of 32,766 characters, macOS 262,144 and Linux
1725 // 2,097,152. If our args exceed 30 KiB, we instead write them to a "response file" and
1726 // pass that to zig, e.g. via 'zig build-lib @args.rsp'
1727 // See @file syntax here: https://gcc.gnu.org/onlinedocs/gcc/Overall-Options.html
1728 var args_length: usize = 0;
1729 for (zig_args.items) |arg| {
1730 args_length += arg.len + 1; // +1 to account for null terminator
1731 }
1732 if (args_length >= 30 * 1024) {
1733 const args_dir = try fs.path.join(
1734 builder.allocator,
1735 &[_][]const u8{ builder.pathFromRoot("zig-cache"), "args" },
1736 );
1737 try std.fs.cwd().makePath(args_dir);
1738
1739 var args_arena = std.heap.ArenaAllocator.init(builder.allocator);
1740 defer args_arena.deinit();
1741
1742 const args_to_escape = zig_args.items[2..];
1743 var escaped_args = try ArrayList([]const u8).initCapacity(args_arena.allocator(), args_to_escape.len);
1744
1745 arg_blk: for (args_to_escape) |arg| {
1746 for (arg) |c, arg_idx| {
1747 if (c == '\\' or c == '"') {
1748 // Slow path for arguments that need to be escaped. We'll need to allocate and copy
1749 var escaped = try ArrayList(u8).initCapacity(args_arena.allocator(), arg.len + 1);
1750 const writer = escaped.writer();
1751 try writer.writeAll(arg[0..arg_idx]);
1752 for (arg[arg_idx..]) |to_escape| {
1753 if (to_escape == '\\' or to_escape == '"') try writer.writeByte('\\');
1754 try writer.writeByte(to_escape);
1755 }
1756 escaped_args.appendAssumeCapacity(escaped.items);
1757 continue :arg_blk;
1758 }
1759 }
1760 escaped_args.appendAssumeCapacity(arg); // no escaping needed so just use original argument
1761 }
1762
1763 // Write the args to zig-cache/args/<SHA256 hash of args> to avoid conflicts with
1764 // other zig build commands running in parallel.
1765 const partially_quoted = try std.mem.join(builder.allocator, "\" \"", escaped_args.items);
1766 const args = try std.mem.concat(builder.allocator, u8, &[_][]const u8{ "\"", partially_quoted, "\"" });
1767
1768 var args_hash: [Sha256.digest_length]u8 = undefined;
1769 Sha256.hash(args, &args_hash, .{});
1770 var args_hex_hash: [Sha256.digest_length * 2]u8 = undefined;
1771 _ = try std.fmt.bufPrint(
1772 &args_hex_hash,
1773 "{s}",
1774 .{std.fmt.fmtSliceHexLower(&args_hash)},
1775 );
1776
1777 const args_file = try fs.path.join(builder.allocator, &[_][]const u8{ args_dir, args_hex_hash[0..] });
1778 try std.fs.cwd().writeFile(args_file, args);
1779
1780 zig_args.shrinkRetainingCapacity(2);
1781 try zig_args.append(try std.mem.concat(builder.allocator, u8, &[_][]const u8{ "@", args_file }));
1782 }
1783
1784 const output_dir_nl = try builder.execFromStep(zig_args.items, &self.step);
1785 const build_output_dir = mem.trimRight(u8, output_dir_nl, "\r\n");
1786
1787 if (self.output_dir) |output_dir| {
1788 var src_dir = try std.fs.cwd().openIterableDir(build_output_dir, .{});
1789 defer src_dir.close();
1790
1791 // Create the output directory if it doesn't exist.
1792 try std.fs.cwd().makePath(output_dir);
1793
1794 var dest_dir = try std.fs.cwd().openDir(output_dir, .{});
1795 defer dest_dir.close();
1796
1797 var it = src_dir.iterate();
1798 while (try it.next()) |entry| {
1799 // The compiler can put these files into the same directory, but we don't
1800 // want to copy them over.
1801 if (mem.eql(u8, entry.name, "llvm-ar.id") or
1802 mem.eql(u8, entry.name, "libs.txt") or
1803 mem.eql(u8, entry.name, "builtin.zig") or
1804 mem.eql(u8, entry.name, "zld.id") or
1805 mem.eql(u8, entry.name, "lld.id")) continue;
1806
1807 _ = try src_dir.dir.updateFile(entry.name, dest_dir, entry.name, .{});
1808 }
1809 } else {
1810 self.output_dir = build_output_dir;
1811 }
1812
1813 // This will ensure all output filenames will now have the output_dir available!
1814 self.computeOutFileNames();
1815
1816 // Update generated files
1817 if (self.output_dir != null) {
1818 self.output_path_source.path = builder.pathJoin(
1819 &.{ self.output_dir.?, self.out_filename },
1820 );
1821
1822 if (self.emit_h) {
1823 self.output_h_path_source.path = builder.pathJoin(
1824 &.{ self.output_dir.?, self.out_h_filename },
1825 );
1826 }
1827
1828 if (self.target.isWindows() or self.target.isUefi()) {
1829 self.output_pdb_path_source.path = builder.pathJoin(
1830 &.{ self.output_dir.?, self.out_pdb_filename },
1831 );
1832 }
1833 }
1834
1835 if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic and self.version != null and self.target.wantSharedLibSymLinks()) {
1836 try doAtomicSymLinks(builder.allocator, self.getOutputSource().getPath(builder), self.major_only_filename.?, self.name_only_filename.?);
1837 }
1838}
1839
1840fn isLibCLibrary(name: []const u8) bool {
1841 const libc_libraries = [_][]const u8{ "c", "m", "dl", "rt", "pthread" };
1842 for (libc_libraries) |libc_lib_name| {
1843 if (mem.eql(u8, name, libc_lib_name))
1844 return true;
1845 }
1846 return false;
1847}
1848
1849fn isLibCppLibrary(name: []const u8) bool {
1850 const libcpp_libraries = [_][]const u8{ "c++", "stdc++" };
1851 for (libcpp_libraries) |libcpp_lib_name| {
1852 if (mem.eql(u8, name, libcpp_lib_name))
1853 return true;
1854 }
1855 return false;
1856}
1857
1858/// Returned slice must be freed by the caller.
1859fn findVcpkgRoot(allocator: Allocator) !?[]const u8 {
1860 const appdata_path = try fs.getAppDataDir(allocator, "vcpkg");
1861 defer allocator.free(appdata_path);
1862
1863 const path_file = try fs.path.join(allocator, &[_][]const u8{ appdata_path, "vcpkg.path.txt" });
1864 defer allocator.free(path_file);
1865
1866 const file = fs.cwd().openFile(path_file, .{}) catch return null;
1867 defer file.close();
1868
1869 const size = @intCast(usize, try file.getEndPos());
1870 const vcpkg_path = try allocator.alloc(u8, size);
1871 const size_read = try file.read(vcpkg_path);
1872 std.debug.assert(size == size_read);
1873
1874 return vcpkg_path;
1875}
1876
1877pub fn doAtomicSymLinks(
1878 allocator: Allocator,
1879 output_path: []const u8,
1880 filename_major_only: []const u8,
1881 filename_name_only: []const u8,
1882) !void {
1883 const out_dir = fs.path.dirname(output_path) orelse ".";
1884 const out_basename = fs.path.basename(output_path);
1885 // sym link for libfoo.so.1 to libfoo.so.1.2.3
1886 const major_only_path = try fs.path.join(
1887 allocator,
1888 &[_][]const u8{ out_dir, filename_major_only },
1889 );
1890 fs.atomicSymLink(allocator, out_basename, major_only_path) catch |err| {
1891 log.err("Unable to symlink {s} -> {s}", .{ major_only_path, out_basename });
1892 return err;
1893 };
1894 // sym link for libfoo.so to libfoo.so.1
1895 const name_only_path = try fs.path.join(
1896 allocator,
1897 &[_][]const u8{ out_dir, filename_name_only },
1898 );
1899 fs.atomicSymLink(allocator, filename_major_only, name_only_path) catch |err| {
1900 log.err("Unable to symlink {s} -> {s}", .{ name_only_path, filename_major_only });
1901 return err;
1902 };
1903}
1904
1905fn execPkgConfigList(self: *std.Build, out_code: *u8) (PkgConfigError || ExecError)![]const PkgConfigPkg {
1906 const stdout = try self.execAllowFail(&[_][]const u8{ "pkg-config", "--list-all" }, out_code, .Ignore);
1907 var list = ArrayList(PkgConfigPkg).init(self.allocator);
1908 errdefer list.deinit();
1909 var line_it = mem.tokenize(u8, stdout, "\r\n");
1910 while (line_it.next()) |line| {
1911 if (mem.trim(u8, line, " \t").len == 0) continue;
1912 var tok_it = mem.tokenize(u8, line, " \t");
1913 try list.append(PkgConfigPkg{
1914 .name = tok_it.next() orelse return error.PkgConfigInvalidOutput,
1915 .desc = tok_it.rest(),
1916 });
1917 }
1918 return list.toOwnedSlice();
1919}
1920
1921fn getPkgConfigList(self: *std.Build) ![]const PkgConfigPkg {
1922 if (self.pkg_config_pkg_list) |res| {
1923 return res;
1924 }
1925 var code: u8 = undefined;
1926 if (execPkgConfigList(self, &code)) |list| {
1927 self.pkg_config_pkg_list = list;
1928 return list;
1929 } else |err| {
1930 const result = switch (err) {
1931 error.ProcessTerminated => error.PkgConfigCrashed,
1932 error.ExecNotSupported => error.PkgConfigFailed,
1933 error.ExitCodeFailure => error.PkgConfigFailed,
1934 error.FileNotFound => error.PkgConfigNotInstalled,
1935 error.InvalidName => error.PkgConfigNotInstalled,
1936 error.PkgConfigInvalidOutput => error.PkgConfigInvalidOutput,
1937 error.ChildExecFailed => error.PkgConfigFailed,
1938 else => return err,
1939 };
1940 self.pkg_config_pkg_list = result;
1941 return result;
1942 }
1943}
1944
1945test "addPackage" {
1946 if (builtin.os.tag == .wasi) return error.SkipZigTest;
1947
1948 var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
1949 defer arena.deinit();
1950
1951 const host = try NativeTargetInfo.detect(.{});
1952
1953 var builder = try std.Build.create(
1954 arena.allocator(),
1955 "test",
1956 "test",
1957 "test",
1958 "test",
1959 host,
1960 );
1961 defer builder.destroy();
1962
1963 const pkg_dep = Pkg{
1964 .name = "pkg_dep",
1965 .source = .{ .path = "/not/a/pkg_dep.zig" },
1966 };
1967 const pkg_top = Pkg{
1968 .name = "pkg_dep",
1969 .source = .{ .path = "/not/a/pkg_top.zig" },
1970 .dependencies = &[_]Pkg{pkg_dep},
1971 };
1972
1973 var exe = builder.addExecutable(.{
1974 .name = "not_an_executable",
1975 .root_source_file = .{ .path = "/not/an/executable.zig" },
1976 });
1977 exe.addPackage(pkg_top);
1978
1979 try std.testing.expectEqual(@as(usize, 1), exe.packages.items.len);
1980
1981 const dupe = exe.packages.items[0];
1982 try std.testing.expectEqualStrings(pkg_top.name, dupe.name);
1983}
1984
1985fn addFlag(args: *ArrayList([]const u8), comptime name: []const u8, opt: ?bool) !void {
1986 const cond = opt orelse return;
1987 try args.ensureUnusedCapacity(1);
1988 if (cond) {
1989 args.appendAssumeCapacity("-f" ++ name);
1990 } else {
1991 args.appendAssumeCapacity("-fno-" ++ name);
1992 }
1993}
1994
1995const TransitiveDeps = struct {
1996 link_objects: ArrayList(LinkObject),
1997 seen_system_libs: StringHashMap(void),
1998 seen_steps: std.AutoHashMap(*const Step, void),
1999 is_linking_libcpp: bool,
2000 is_linking_libc: bool,
2001 frameworks: *StringHashMap(FrameworkLinkInfo),
2002
2003 fn add(td: *TransitiveDeps, link_objects: []const LinkObject) !void {
2004 try td.link_objects.ensureUnusedCapacity(link_objects.len);
2005
2006 for (link_objects) |link_object| {
2007 try td.link_objects.append(link_object);
2008 switch (link_object) {
2009 .other_step => |other| try addInner(td, other, other.isDynamicLibrary()),
2010 else => {},
2011 }
2012 }
2013 }
2014
2015 fn addInner(td: *TransitiveDeps, other: *CompileStep, dyn: bool) !void {
2016 // Inherit dependency on libc and libc++
2017 td.is_linking_libcpp = td.is_linking_libcpp or other.is_linking_libcpp;
2018 td.is_linking_libc = td.is_linking_libc or other.is_linking_libc;
2019
2020 // Inherit dependencies on darwin frameworks
2021 if (!dyn) {
2022 var it = other.frameworks.iterator();
2023 while (it.next()) |framework| {
2024 try td.frameworks.put(framework.key_ptr.*, framework.value_ptr.*);
2025 }
2026 }
2027
2028 // Inherit dependencies on system libraries and static libraries.
2029 for (other.link_objects.items) |other_link_object| {
2030 switch (other_link_object) {
2031 .system_lib => |system_lib| {
2032 if ((try td.seen_system_libs.fetchPut(system_lib.name, {})) != null)
2033 continue;
2034
2035 if (dyn)
2036 continue;
2037
2038 try td.link_objects.append(other_link_object);
2039 },
2040 .other_step => |inner_other| {
2041 if ((try td.seen_steps.fetchPut(&inner_other.step, {})) != null)
2042 continue;
2043
2044 if (!dyn)
2045 try td.link_objects.append(other_link_object);
2046
2047 try addInner(td, inner_other, dyn or inner_other.isDynamicLibrary());
2048 },
2049 else => continue,
2050 }
2051 }
2052 }
2053};
lib/std/Build/ConfigHeaderStep.zig created+299
...@@ -0,0 +1,299 @@
1const std = @import("../std.zig");
2const ConfigHeaderStep = @This();
3const Step = std.Build.Step;
4
5pub const base_id: Step.Id = .config_header;
6
7pub const Style = enum {
8 /// The configure format supported by autotools. It uses `#undef foo` to
9 /// mark lines that can be substituted with different values.
10 autoconf,
11 /// The configure format supported by CMake. It uses `@@FOO@@` and
12 /// `#cmakedefine` for template substitution.
13 cmake,
14};
15
16pub const Value = union(enum) {
17 undef,
18 defined,
19 boolean: bool,
20 int: i64,
21 ident: []const u8,
22 string: []const u8,
23};
24
25step: Step,
26builder: *std.Build,
27source: std.Build.FileSource,
28style: Style,
29values: std.StringHashMap(Value),
30max_bytes: usize = 2 * 1024 * 1024,
31output_dir: []const u8,
32output_basename: []const u8,
33
34pub fn create(builder: *std.Build, source: std.Build.FileSource, style: Style) *ConfigHeaderStep {
35 const self = builder.allocator.create(ConfigHeaderStep) catch @panic("OOM");
36 const name = builder.fmt("configure header {s}", .{source.getDisplayName()});
37 self.* = .{
38 .builder = builder,
39 .step = Step.init(base_id, name, builder.allocator, make),
40 .source = source,
41 .style = style,
42 .values = std.StringHashMap(Value).init(builder.allocator),
43 .output_dir = undefined,
44 .output_basename = "config.h",
45 };
46 switch (source) {
47 .path => |p| {
48 const basename = std.fs.path.basename(p);
49 if (std.mem.endsWith(u8, basename, ".h.in")) {
50 self.output_basename = basename[0 .. basename.len - 3];
51 }
52 },
53 else => {},
54 }
55 return self;
56}
57
58pub fn addValues(self: *ConfigHeaderStep, values: anytype) void {
59 return addValuesInner(self, values) catch @panic("OOM");
60}
61
62fn addValuesInner(self: *ConfigHeaderStep, values: anytype) !void {
63 inline for (@typeInfo(@TypeOf(values)).Struct.fields) |field| {
64 try putValue(self, field.name, field.type, @field(values, field.name));
65 }
66}
67
68fn putValue(self: *ConfigHeaderStep, field_name: []const u8, comptime T: type, v: T) !void {
69 switch (@typeInfo(T)) {
70 .Null => {
71 try self.values.put(field_name, .undef);
72 },
73 .Void => {
74 try self.values.put(field_name, .defined);
75 },
76 .Bool => {
77 try self.values.put(field_name, .{ .boolean = v });
78 },
79 .Int => {
80 try self.values.put(field_name, .{ .int = v });
81 },
82 .ComptimeInt => {
83 try self.values.put(field_name, .{ .int = v });
84 },
85 .EnumLiteral => {
86 try self.values.put(field_name, .{ .ident = @tagName(v) });
87 },
88 .Optional => {
89 if (v) |x| {
90 return putValue(self, field_name, @TypeOf(x), x);
91 } else {
92 try self.values.put(field_name, .undef);
93 }
94 },
95 .Pointer => |ptr| {
96 switch (@typeInfo(ptr.child)) {
97 .Array => |array| {
98 if (ptr.size == .One and array.child == u8) {
99 try self.values.put(field_name, .{ .string = v });
100 return;
101 }
102 },
103 else => {},
104 }
105
106 @compileError("unsupported ConfigHeaderStep value type: " ++ @typeName(T));
107 },
108 else => @compileError("unsupported ConfigHeaderStep value type: " ++ @typeName(T)),
109 }
110}
111
112fn make(step: *Step) !void {
113 const self = @fieldParentPtr(ConfigHeaderStep, "step", step);
114 const gpa = self.builder.allocator;
115 const src_path = self.source.getPath(self.builder);
116 const contents = try std.fs.cwd().readFileAlloc(gpa, src_path, self.max_bytes);
117
118 // The cache is used here not really as a way to speed things up - because writing
119 // the data to a file would probably be very fast - but as a way to find a canonical
120 // location to put build artifacts.
121
122 // If, for example, a hard-coded path was used as the location to put ConfigHeaderStep
123 // files, then two ConfigHeaderStep executing in parallel might clobber each other.
124
125 // TODO port the cache system from the compiler to zig std lib. Until then
126 // we construct the path directly, and no "cache hit" detection happens;
127 // the files are always written.
128 // Note there is very similar code over in WriteFileStep
129 const Hasher = std.crypto.auth.siphash.SipHash128(1, 3);
130 // Random bytes to make ConfigHeaderStep unique. Refresh this with new
131 // random bytes when ConfigHeaderStep implementation is modified in a
132 // non-backwards-compatible way.
133 var hash = Hasher.init("X1pQzdDt91Zlh7Eh");
134 hash.update(self.source.getDisplayName());
135 hash.update(contents);
136
137 var digest: [16]u8 = undefined;
138 hash.final(&digest);
139 var hash_basename: [digest.len * 2]u8 = undefined;
140 _ = std.fmt.bufPrint(
141 &hash_basename,
142 "{s}",
143 .{std.fmt.fmtSliceHexLower(&digest)},
144 ) catch unreachable;
145
146 self.output_dir = try std.fs.path.join(gpa, &[_][]const u8{
147 self.builder.cache_root, "o", &hash_basename,
148 });
149 var dir = std.fs.cwd().makeOpenPath(self.output_dir, .{}) catch |err| {
150 std.debug.print("unable to make path {s}: {s}\n", .{ self.output_dir, @errorName(err) });
151 return err;
152 };
153 defer dir.close();
154
155 var values_copy = try self.values.clone();
156 defer values_copy.deinit();
157
158 var output = std.ArrayList(u8).init(gpa);
159 defer output.deinit();
160 try output.ensureTotalCapacity(contents.len);
161
162 try output.appendSlice("/* This file was generated by ConfigHeaderStep using the Zig Build System. */\n");
163
164 switch (self.style) {
165 .autoconf => try render_autoconf(contents, &output, &values_copy, src_path),
166 .cmake => try render_cmake(contents, &output, &values_copy, src_path),
167 }
168
169 try dir.writeFile(self.output_basename, output.items);
170}
171
172fn render_autoconf(
173 contents: []const u8,
174 output: *std.ArrayList(u8),
175 values_copy: *std.StringHashMap(Value),
176 src_path: []const u8,
177) !void {
178 var any_errors = false;
179 var line_index: u32 = 0;
180 var line_it = std.mem.split(u8, contents, "\n");
181 while (line_it.next()) |line| : (line_index += 1) {
182 if (!std.mem.startsWith(u8, line, "#")) {
183 try output.appendSlice(line);
184 try output.appendSlice("\n");
185 continue;
186 }
187 var it = std.mem.tokenize(u8, line[1..], " \t\r");
188 const undef = it.next().?;
189 if (!std.mem.eql(u8, undef, "undef")) {
190 try output.appendSlice(line);
191 try output.appendSlice("\n");
192 continue;
193 }
194 const name = it.rest();
195 const kv = values_copy.fetchRemove(name) orelse {
196 std.debug.print("{s}:{d}: error: unspecified config header value: '{s}'\n", .{
197 src_path, line_index + 1, name,
198 });
199 any_errors = true;
200 continue;
201 };
202 try renderValue(output, name, kv.value);
203 }
204
205 {
206 var it = values_copy.iterator();
207 while (it.next()) |entry| {
208 const name = entry.key_ptr.*;
209 std.debug.print("{s}: error: config header value unused: '{s}'\n", .{ src_path, name });
210 }
211 }
212
213 if (any_errors) {
214 return error.HeaderConfigFailed;
215 }
216}
217
218fn render_cmake(
219 contents: []const u8,
220 output: *std.ArrayList(u8),
221 values_copy: *std.StringHashMap(Value),
222 src_path: []const u8,
223) !void {
224 var any_errors = false;
225 var line_index: u32 = 0;
226 var line_it = std.mem.split(u8, contents, "\n");
227 while (line_it.next()) |line| : (line_index += 1) {
228 if (!std.mem.startsWith(u8, line, "#")) {
229 try output.appendSlice(line);
230 try output.appendSlice("\n");
231 continue;
232 }
233 var it = std.mem.tokenize(u8, line[1..], " \t\r");
234 const cmakedefine = it.next().?;
235 if (!std.mem.eql(u8, cmakedefine, "cmakedefine")) {
236 try output.appendSlice(line);
237 try output.appendSlice("\n");
238 continue;
239 }
240 const name = it.next() orelse {
241 std.debug.print("{s}:{d}: error: missing define name\n", .{
242 src_path, line_index + 1,
243 });
244 any_errors = true;
245 continue;
246 };
247 const kv = values_copy.fetchRemove(name) orelse {
248 std.debug.print("{s}:{d}: error: unspecified config header value: '{s}'\n", .{
249 src_path, line_index + 1, name,
250 });
251 any_errors = true;
252 continue;
253 };
254 try renderValue(output, name, kv.value);
255 }
256
257 {
258 var it = values_copy.iterator();
259 while (it.next()) |entry| {
260 const name = entry.key_ptr.*;
261 std.debug.print("{s}: error: config header value unused: '{s}'\n", .{ src_path, name });
262 }
263 }
264
265 if (any_errors) {
266 return error.HeaderConfigFailed;
267 }
268}
269
270fn renderValue(output: *std.ArrayList(u8), name: []const u8, value: Value) !void {
271 switch (value) {
272 .undef => {
273 try output.appendSlice("/* #undef ");
274 try output.appendSlice(name);
275 try output.appendSlice(" */\n");
276 },
277 .defined => {
278 try output.appendSlice("#define ");
279 try output.appendSlice(name);
280 try output.appendSlice("\n");
281 },
282 .boolean => |b| {
283 try output.appendSlice("#define ");
284 try output.appendSlice(name);
285 try output.appendSlice(" ");
286 try output.appendSlice(if (b) "true\n" else "false\n");
287 },
288 .int => |i| {
289 try output.writer().print("#define {s} {d}\n", .{ name, i });
290 },
291 .ident => |ident| {
292 try output.writer().print("#define {s} {s}\n", .{ name, ident });
293 },
294 .string => |string| {
295 // TODO: use C-specific escaping instead of zig string literals
296 try output.writer().print("#define {s} \"{}\"\n", .{ name, std.zig.fmtEscapes(string) });
297 },
298 }
299}
lib/std/Build/EmulatableRunStep.zig created+213
...@@ -0,0 +1,213 @@
1//! Unlike `RunStep` this step will provide emulation, when enabled, to run foreign binaries.
2//! When a binary is foreign, but emulation for the target is disabled, the specified binary
3//! will not be run and therefore also not validated against its output.
4//! This step can be useful when wishing to run a built binary on multiple platforms,
5//! without having to verify if it's possible to be ran against.
6
7const std = @import("../std.zig");
8const Step = std.Build.Step;
9const CompileStep = std.Build.CompileStep;
10const RunStep = std.Build.RunStep;
11
12const fs = std.fs;
13const process = std.process;
14const EnvMap = process.EnvMap;
15
16const EmulatableRunStep = @This();
17
18pub const base_id = .emulatable_run;
19
20const max_stdout_size = 1 * 1024 * 1024; // 1 MiB
21
22step: Step,
23builder: *std.Build,
24
25/// The artifact (executable) to be run by this step
26exe: *CompileStep,
27
28/// Set this to `null` to ignore the exit code for the purpose of determining a successful execution
29expected_exit_code: ?u8 = 0,
30
31/// Override this field to modify the environment
32env_map: ?*EnvMap,
33
34/// Set this to modify the current working directory
35cwd: ?[]const u8,
36
37stdout_action: RunStep.StdIoAction = .inherit,
38stderr_action: RunStep.StdIoAction = .inherit,
39
40/// When set to true, hides the warning of skipping a foreign binary which cannot be run on the host
41/// or through emulation.
42hide_foreign_binaries_warning: bool,
43
44/// Creates a step that will execute the given artifact. This step will allow running the
45/// binary through emulation when any of the emulation options such as `enable_rosetta` are set to true.
46/// When set to false, and the binary is foreign, running the executable is skipped.
47/// Asserts given artifact is an executable.
48pub fn create(builder: *std.Build, name: []const u8, artifact: *CompileStep) *EmulatableRunStep {
49 std.debug.assert(artifact.kind == .exe or artifact.kind == .test_exe);
50 const self = builder.allocator.create(EmulatableRunStep) catch @panic("OOM");
51
52 const option_name = "hide-foreign-warnings";
53 const hide_warnings = if (builder.available_options_map.get(option_name) == null) warn: {
54 break :warn builder.option(bool, option_name, "Hide the warning when a foreign binary which is incompatible is skipped") orelse false;
55 } else false;
56
57 self.* = .{
58 .builder = builder,
59 .step = Step.init(.emulatable_run, name, builder.allocator, make),
60 .exe = artifact,
61 .env_map = null,
62 .cwd = null,
63 .hide_foreign_binaries_warning = hide_warnings,
64 };
65 self.step.dependOn(&artifact.step);
66
67 return self;
68}
69
70fn make(step: *Step) !void {
71 const self = @fieldParentPtr(EmulatableRunStep, "step", step);
72 const host_info = self.builder.host;
73
74 var argv_list = std.ArrayList([]const u8).init(self.builder.allocator);
75 defer argv_list.deinit();
76
77 const need_cross_glibc = self.exe.target.isGnuLibC() and self.exe.is_linking_libc;
78 switch (host_info.getExternalExecutor(self.exe.target_info, .{
79 .qemu_fixes_dl = need_cross_glibc and self.builder.glibc_runtimes_dir != null,
80 .link_libc = self.exe.is_linking_libc,
81 })) {
82 .native => {},
83 .rosetta => if (!self.builder.enable_rosetta) return warnAboutForeignBinaries(self),
84 .wine => |bin_name| if (self.builder.enable_wine) {
85 try argv_list.append(bin_name);
86 } else return,
87 .qemu => |bin_name| if (self.builder.enable_qemu) {
88 const glibc_dir_arg = if (need_cross_glibc)
89 self.builder.glibc_runtimes_dir orelse return
90 else
91 null;
92 try argv_list.append(bin_name);
93 if (glibc_dir_arg) |dir| {
94 // TODO look into making this a call to `linuxTriple`. This
95 // needs the directory to be called "i686" rather than
96 // "x86" which is why we do it manually here.
97 const fmt_str = "{s}" ++ fs.path.sep_str ++ "{s}-{s}-{s}";
98 const cpu_arch = self.exe.target.getCpuArch();
99 const os_tag = self.exe.target.getOsTag();
100 const abi = self.exe.target.getAbi();
101 const cpu_arch_name: []const u8 = if (cpu_arch == .x86)
102 "i686"
103 else
104 @tagName(cpu_arch);
105 const full_dir = try std.fmt.allocPrint(self.builder.allocator, fmt_str, .{
106 dir, cpu_arch_name, @tagName(os_tag), @tagName(abi),
107 });
108
109 try argv_list.append("-L");
110 try argv_list.append(full_dir);
111 }
112 } else return warnAboutForeignBinaries(self),
113 .darling => |bin_name| if (self.builder.enable_darling) {
114 try argv_list.append(bin_name);
115 } else return warnAboutForeignBinaries(self),
116 .wasmtime => |bin_name| if (self.builder.enable_wasmtime) {
117 try argv_list.append(bin_name);
118 try argv_list.append("--dir=.");
119 } else return warnAboutForeignBinaries(self),
120 else => return warnAboutForeignBinaries(self),
121 }
122
123 if (self.exe.target.isWindows()) {
124 // On Windows we don't have rpaths so we have to add .dll search paths to PATH
125 RunStep.addPathForDynLibsInternal(&self.step, self.builder, self.exe);
126 }
127
128 const executable_path = self.exe.installed_path orelse self.exe.getOutputSource().getPath(self.builder);
129 try argv_list.append(executable_path);
130
131 try RunStep.runCommand(
132 argv_list.items,
133 self.builder,
134 self.expected_exit_code,
135 self.stdout_action,
136 self.stderr_action,
137 .Inherit,
138 self.env_map,
139 self.cwd,
140 false,
141 );
142}
143
144pub fn expectStdErrEqual(self: *EmulatableRunStep, bytes: []const u8) void {
145 self.stderr_action = .{ .expect_exact = self.builder.dupe(bytes) };
146}
147
148pub fn expectStdOutEqual(self: *EmulatableRunStep, bytes: []const u8) void {
149 self.stdout_action = .{ .expect_exact = self.builder.dupe(bytes) };
150}
151
152fn warnAboutForeignBinaries(step: *EmulatableRunStep) void {
153 if (step.hide_foreign_binaries_warning) return;
154 const builder = step.builder;
155 const artifact = step.exe;
156
157 const host_name = builder.host.target.zigTriple(builder.allocator) catch @panic("unhandled error");
158 const foreign_name = artifact.target.zigTriple(builder.allocator) catch @panic("unhandled error");
159 const target_info = std.zig.system.NativeTargetInfo.detect(artifact.target) catch @panic("unhandled error");
160 const need_cross_glibc = artifact.target.isGnuLibC() and artifact.is_linking_libc;
161 switch (builder.host.getExternalExecutor(target_info, .{
162 .qemu_fixes_dl = need_cross_glibc and builder.glibc_runtimes_dir != null,
163 .link_libc = artifact.is_linking_libc,
164 })) {
165 .native => unreachable,
166 .bad_dl => |foreign_dl| {
167 const host_dl = builder.host.dynamic_linker.get() orelse "(none)";
168 std.debug.print("the host system does not appear to be capable of executing binaries from the target because the host dynamic linker is '{s}', while the target dynamic linker is '{s}'. Consider setting the dynamic linker as '{s}'.\n", .{
169 host_dl, foreign_dl, host_dl,
170 });
171 },
172 .bad_os_or_cpu => {
173 std.debug.print("the host system ({s}) does not appear to be capable of executing binaries from the target ({s}).\n", .{
174 host_name, foreign_name,
175 });
176 },
177 .darling => if (!builder.enable_darling) {
178 std.debug.print(
179 "the host system ({s}) does not appear to be capable of executing binaries " ++
180 "from the target ({s}). Consider enabling darling.\n",
181 .{ host_name, foreign_name },
182 );
183 },
184 .rosetta => if (!builder.enable_rosetta) {
185 std.debug.print(
186 "the host system ({s}) does not appear to be capable of executing binaries " ++
187 "from the target ({s}). Consider enabling rosetta.\n",
188 .{ host_name, foreign_name },
189 );
190 },
191 .wine => if (!builder.enable_wine) {
192 std.debug.print(
193 "the host system ({s}) does not appear to be capable of executing binaries " ++
194 "from the target ({s}). Consider enabling wine.\n",
195 .{ host_name, foreign_name },
196 );
197 },
198 .qemu => if (!builder.enable_qemu) {
199 std.debug.print(
200 "the host system ({s}) does not appear to be capable of executing binaries " ++
201 "from the target ({s}). Consider enabling qemu.\n",
202 .{ host_name, foreign_name },
203 );
204 },
205 .wasmtime => {
206 std.debug.print(
207 "the host system ({s}) does not appear to be capable of executing binaries " ++
208 "from the target ({s}). Consider enabling wasmtime.\n",
209 .{ host_name, foreign_name },
210 );
211 },
212 }
213}
lib/std/Build/FmtStep.zig created+32
...@@ -0,0 +1,32 @@
1const std = @import("../std.zig");
2const Step = std.Build.Step;
3const FmtStep = @This();
4
5pub const base_id = .fmt;
6
7step: Step,
8builder: *std.Build,
9argv: [][]const u8,
10
11pub fn create(builder: *std.Build, paths: []const []const u8) *FmtStep {
12 const self = builder.allocator.create(FmtStep) catch @panic("OOM");
13 const name = "zig fmt";
14 self.* = FmtStep{
15 .step = Step.init(.fmt, name, builder.allocator, make),
16 .builder = builder,
17 .argv = builder.allocator.alloc([]u8, paths.len + 2) catch @panic("OOM"),
18 };
19
20 self.argv[0] = builder.zig_exe;
21 self.argv[1] = "fmt";
22 for (paths) |path, i| {
23 self.argv[2 + i] = builder.pathFromRoot(path);
24 }
25 return self;
26}
27
28fn make(step: *Step) !void {
29 const self = @fieldParentPtr(FmtStep, "step", step);
30
31 return self.builder.spawnChild(self.argv);
32}
lib/std/Build/InstallArtifactStep.zig created+85
...@@ -0,0 +1,85 @@
1const std = @import("../std.zig");
2const Step = std.Build.Step;
3const CompileStep = std.Build.CompileStep;
4const InstallDir = std.Build.InstallDir;
5const InstallArtifactStep = @This();
6
7pub const base_id = .install_artifact;
8
9step: Step,
10builder: *std.Build,
11artifact: *CompileStep,
12dest_dir: InstallDir,
13pdb_dir: ?InstallDir,
14h_dir: ?InstallDir,
15
16pub fn create(builder: *std.Build, artifact: *CompileStep) *InstallArtifactStep {
17 if (artifact.install_step) |s| return s;
18
19 const self = builder.allocator.create(InstallArtifactStep) catch @panic("OOM");
20 self.* = InstallArtifactStep{
21 .builder = builder,
22 .step = Step.init(.install_artifact, builder.fmt("install {s}", .{artifact.step.name}), builder.allocator, make),
23 .artifact = artifact,
24 .dest_dir = artifact.override_dest_dir orelse switch (artifact.kind) {
25 .obj => @panic("Cannot install a .obj build artifact."),
26 .@"test" => @panic("Cannot install a .test build artifact, use .test_exe instead."),
27 .exe, .test_exe => InstallDir{ .bin = {} },
28 .lib => InstallDir{ .lib = {} },
29 },
30 .pdb_dir = if (artifact.producesPdbFile()) blk: {
31 if (artifact.kind == .exe or artifact.kind == .test_exe) {
32 break :blk InstallDir{ .bin = {} };
33 } else {
34 break :blk InstallDir{ .lib = {} };
35 }
36 } else null,
37 .h_dir = if (artifact.kind == .lib and artifact.emit_h) .header else null,
38 };
39 self.step.dependOn(&artifact.step);
40 artifact.install_step = self;
41
42 builder.pushInstalledFile(self.dest_dir, artifact.out_filename);
43 if (self.artifact.isDynamicLibrary()) {
44 if (artifact.major_only_filename) |name| {
45 builder.pushInstalledFile(.lib, name);
46 }
47 if (artifact.name_only_filename) |name| {
48 builder.pushInstalledFile(.lib, name);
49 }
50 if (self.artifact.target.isWindows()) {
51 builder.pushInstalledFile(.lib, artifact.out_lib_filename);
52 }
53 }
54 if (self.pdb_dir) |pdb_dir| {
55 builder.pushInstalledFile(pdb_dir, artifact.out_pdb_filename);
56 }
57 if (self.h_dir) |h_dir| {
58 builder.pushInstalledFile(h_dir, artifact.out_h_filename);
59 }
60 return self;
61}
62
63fn make(step: *Step) !void {
64 const self = @fieldParentPtr(InstallArtifactStep, "step", step);
65 const builder = self.builder;
66
67 const full_dest_path = builder.getInstallPath(self.dest_dir, self.artifact.out_filename);
68 try builder.updateFile(self.artifact.getOutputSource().getPath(builder), full_dest_path);
69 if (self.artifact.isDynamicLibrary() and self.artifact.version != null and self.artifact.target.wantSharedLibSymLinks()) {
70 try CompileStep.doAtomicSymLinks(builder.allocator, full_dest_path, self.artifact.major_only_filename.?, self.artifact.name_only_filename.?);
71 }
72 if (self.artifact.isDynamicLibrary() and self.artifact.target.isWindows() and self.artifact.emit_implib != .no_emit) {
73 const full_implib_path = builder.getInstallPath(self.dest_dir, self.artifact.out_lib_filename);
74 try builder.updateFile(self.artifact.getOutputLibSource().getPath(builder), full_implib_path);
75 }
76 if (self.pdb_dir) |pdb_dir| {
77 const full_pdb_path = builder.getInstallPath(pdb_dir, self.artifact.out_pdb_filename);
78 try builder.updateFile(self.artifact.getOutputPdbSource().getPath(builder), full_pdb_path);
79 }
80 if (self.h_dir) |h_dir| {
81 const full_h_path = builder.getInstallPath(h_dir, self.artifact.out_h_filename);
82 try builder.updateFile(self.artifact.getOutputHSource().getPath(builder), full_h_path);
83 }
84 self.artifact.installed_path = full_dest_path;
85}
lib/std/Build/InstallDirStep.zig created+93
...@@ -0,0 +1,93 @@
1const std = @import("../std.zig");
2const mem = std.mem;
3const fs = std.fs;
4const Step = std.Build.Step;
5const InstallDir = std.Build.InstallDir;
6const InstallDirStep = @This();
7const log = std.log;
8
9step: Step,
10builder: *std.Build,
11options: Options,
12/// This is used by the build system when a file being installed comes from one
13/// package but is being installed by another.
14override_source_builder: ?*std.Build = null,
15
16pub const base_id = .install_dir;
17
18pub const Options = struct {
19 source_dir: []const u8,
20 install_dir: InstallDir,
21 install_subdir: []const u8,
22 /// File paths which end in any of these suffixes will be excluded
23 /// from being installed.
24 exclude_extensions: []const []const u8 = &.{},
25 /// File paths which end in any of these suffixes will result in
26 /// empty files being installed. This is mainly intended for large
27 /// test.zig files in order to prevent needless installation bloat.
28 /// However if the files were not present at all, then
29 /// `@import("test.zig")` would be a compile error.
30 blank_extensions: []const []const u8 = &.{},
31
32 fn dupe(self: Options, b: *std.Build) Options {
33 return .{
34 .source_dir = b.dupe(self.source_dir),
35 .install_dir = self.install_dir.dupe(b),
36 .install_subdir = b.dupe(self.install_subdir),
37 .exclude_extensions = b.dupeStrings(self.exclude_extensions),
38 .blank_extensions = b.dupeStrings(self.blank_extensions),
39 };
40 }
41};
42
43pub fn init(
44 builder: *std.Build,
45 options: Options,
46) InstallDirStep {
47 builder.pushInstalledFile(options.install_dir, options.install_subdir);
48 return InstallDirStep{
49 .builder = builder,
50 .step = Step.init(.install_dir, builder.fmt("install {s}/", .{options.source_dir}), builder.allocator, make),
51 .options = options.dupe(builder),
52 };
53}
54
55fn make(step: *Step) !void {
56 const self = @fieldParentPtr(InstallDirStep, "step", step);
57 const dest_prefix = self.builder.getInstallPath(self.options.install_dir, self.options.install_subdir);
58 const src_builder = self.override_source_builder orelse self.builder;
59 const full_src_dir = src_builder.pathFromRoot(self.options.source_dir);
60 var src_dir = std.fs.cwd().openIterableDir(full_src_dir, .{}) catch |err| {
61 log.err("InstallDirStep: unable to open source directory '{s}': {s}", .{
62 full_src_dir, @errorName(err),
63 });
64 return error.StepFailed;
65 };
66 defer src_dir.close();
67 var it = try src_dir.walk(self.builder.allocator);
68 next_entry: while (try it.next()) |entry| {
69 for (self.options.exclude_extensions) |ext| {
70 if (mem.endsWith(u8, entry.path, ext)) {
71 continue :next_entry;
72 }
73 }
74
75 const full_path = self.builder.pathJoin(&.{ full_src_dir, entry.path });
76 const dest_path = self.builder.pathJoin(&.{ dest_prefix, entry.path });
77
78 switch (entry.kind) {
79 .Directory => try fs.cwd().makePath(dest_path),
80 .File => {
81 for (self.options.blank_extensions) |ext| {
82 if (mem.endsWith(u8, entry.path, ext)) {
83 try self.builder.truncateFile(dest_path);
84 continue :next_entry;
85 }
86 }
87
88 try self.builder.updateFile(full_path, dest_path);
89 },
90 else => continue,
91 }
92 }
93}
lib/std/Build/InstallFileStep.zig created+40
...@@ -0,0 +1,40 @@
1const std = @import("../std.zig");
2const Step = std.Build.Step;
3const FileSource = std.Build.FileSource;
4const InstallDir = std.Build.InstallDir;
5const InstallFileStep = @This();
6
7pub const base_id = .install_file;
8
9step: Step,
10builder: *std.Build,
11source: FileSource,
12dir: InstallDir,
13dest_rel_path: []const u8,
14/// This is used by the build system when a file being installed comes from one
15/// package but is being installed by another.
16override_source_builder: ?*std.Build = null,
17
18pub fn init(
19 builder: *std.Build,
20 source: FileSource,
21 dir: InstallDir,
22 dest_rel_path: []const u8,
23) InstallFileStep {
24 builder.pushInstalledFile(dir, dest_rel_path);
25 return InstallFileStep{
26 .builder = builder,
27 .step = Step.init(.install_file, builder.fmt("install {s} to {s}", .{ source.getDisplayName(), dest_rel_path }), builder.allocator, make),
28 .source = source.dupe(builder),
29 .dir = dir.dupe(builder),
30 .dest_rel_path = builder.dupePath(dest_rel_path),
31 };
32}
33
34fn make(step: *Step) !void {
35 const self = @fieldParentPtr(InstallFileStep, "step", step);
36 const src_builder = self.override_source_builder orelse self.builder;
37 const full_src_path = self.source.getPath(src_builder);
38 const full_dest_path = self.builder.getInstallPath(self.dir, self.dest_rel_path);
39 try self.builder.updateFile(full_src_path, full_dest_path);
40}
lib/std/Build/InstallRawStep.zig created+110
...@@ -0,0 +1,110 @@
1//! TODO: Rename this to ObjCopyStep now that it invokes the `zig objcopy`
2//! subcommand rather than containing an implementation directly.
3
4const std = @import("std");
5const InstallRawStep = @This();
6
7const Allocator = std.mem.Allocator;
8const ArenaAllocator = std.heap.ArenaAllocator;
9const ArrayListUnmanaged = std.ArrayListUnmanaged;
10const File = std.fs.File;
11const InstallDir = std.Build.InstallDir;
12const CompileStep = std.Build.CompileStep;
13const Step = std.Build.Step;
14const elf = std.elf;
15const fs = std.fs;
16const io = std.io;
17const sort = std.sort;
18
19pub const base_id = .install_raw;
20
21pub const RawFormat = enum {
22 bin,
23 hex,
24};
25
26step: Step,
27builder: *std.Build,
28artifact: *CompileStep,
29dest_dir: InstallDir,
30dest_filename: []const u8,
31options: CreateOptions,
32output_file: std.Build.GeneratedFile,
33
34pub const CreateOptions = struct {
35 format: ?RawFormat = null,
36 dest_dir: ?InstallDir = null,
37 only_section: ?[]const u8 = null,
38 pad_to: ?u64 = null,
39};
40
41pub fn create(
42 builder: *std.Build,
43 artifact: *CompileStep,
44 dest_filename: []const u8,
45 options: CreateOptions,
46) *InstallRawStep {
47 const self = builder.allocator.create(InstallRawStep) catch @panic("OOM");
48 self.* = InstallRawStep{
49 .step = Step.init(.install_raw, builder.fmt("install raw binary {s}", .{artifact.step.name}), builder.allocator, make),
50 .builder = builder,
51 .artifact = artifact,
52 .dest_dir = if (options.dest_dir) |d| d else switch (artifact.kind) {
53 .obj => unreachable,
54 .@"test" => unreachable,
55 .exe, .test_exe => .bin,
56 .lib => unreachable,
57 },
58 .dest_filename = dest_filename,
59 .options = options,
60 .output_file = std.Build.GeneratedFile{ .step = &self.step },
61 };
62 self.step.dependOn(&artifact.step);
63
64 builder.pushInstalledFile(self.dest_dir, dest_filename);
65 return self;
66}
67
68pub fn getOutputSource(self: *const InstallRawStep) std.Build.FileSource {
69 return std.Build.FileSource{ .generated = &self.output_file };
70}
71
72fn make(step: *Step) !void {
73 const self = @fieldParentPtr(InstallRawStep, "step", step);
74 const b = self.builder;
75
76 if (self.artifact.target.getObjectFormat() != .elf) {
77 std.debug.print("InstallRawStep only works with ELF format.\n", .{});
78 return error.InvalidObjectFormat;
79 }
80
81 const full_src_path = self.artifact.getOutputSource().getPath(b);
82 const full_dest_path = b.getInstallPath(self.dest_dir, self.dest_filename);
83 self.output_file.path = full_dest_path;
84
85 try fs.cwd().makePath(b.getInstallPath(self.dest_dir, ""));
86
87 var argv_list = std.ArrayList([]const u8).init(b.allocator);
88 try argv_list.appendSlice(&.{ b.zig_exe, "objcopy" });
89
90 if (self.options.only_section) |only_section| {
91 try argv_list.appendSlice(&.{ "-j", only_section });
92 }
93 if (self.options.pad_to) |pad_to| {
94 try argv_list.appendSlice(&.{
95 "--pad-to",
96 b.fmt("{d}", .{pad_to}),
97 });
98 }
99 if (self.options.format) |format| switch (format) {
100 .bin => try argv_list.appendSlice(&.{ "-O", "binary" }),
101 .hex => try argv_list.appendSlice(&.{ "-O", "hex" }),
102 };
103
104 try argv_list.appendSlice(&.{ full_src_path, full_dest_path });
105 _ = try self.builder.execFromStep(argv_list.items, &self.step);
106}
107
108test {
109 std.testing.refAllDecls(InstallRawStep);
110}
lib/std/Build/LogStep.zig created+23
...@@ -0,0 +1,23 @@
1const std = @import("../std.zig");
2const log = std.log;
3const Step = std.Build.Step;
4const LogStep = @This();
5
6pub const base_id = .log;
7
8step: Step,
9builder: *std.Build,
10data: []const u8,
11
12pub fn init(builder: *std.Build, data: []const u8) LogStep {
13 return LogStep{
14 .builder = builder,
15 .step = Step.init(.log, builder.fmt("log {s}", .{data}), builder.allocator, make),
16 .data = builder.dupe(data),
17 };
18}
19
20fn make(step: *Step) anyerror!void {
21 const self = @fieldParentPtr(LogStep, "step", step);
22 log.info("{s}", .{self.data});
23}
lib/std/Build/OptionsStep.zig created+371
...@@ -0,0 +1,371 @@
1const std = @import("../std.zig");
2const builtin = @import("builtin");
3const fs = std.fs;
4const Step = std.Build.Step;
5const GeneratedFile = std.Build.GeneratedFile;
6const CompileStep = std.Build.CompileStep;
7const FileSource = std.Build.FileSource;
8
9const OptionsStep = @This();
10
11pub const base_id = .options;
12
13step: Step,
14generated_file: GeneratedFile,
15builder: *std.Build,
16
17contents: std.ArrayList(u8),
18artifact_args: std.ArrayList(OptionArtifactArg),
19file_source_args: std.ArrayList(OptionFileSourceArg),
20
21pub fn create(builder: *std.Build) *OptionsStep {
22 const self = builder.allocator.create(OptionsStep) catch @panic("OOM");
23 self.* = .{
24 .builder = builder,
25 .step = Step.init(.options, "options", builder.allocator, make),
26 .generated_file = undefined,
27 .contents = std.ArrayList(u8).init(builder.allocator),
28 .artifact_args = std.ArrayList(OptionArtifactArg).init(builder.allocator),
29 .file_source_args = std.ArrayList(OptionFileSourceArg).init(builder.allocator),
30 };
31 self.generated_file = .{ .step = &self.step };
32
33 return self;
34}
35
36pub fn addOption(self: *OptionsStep, comptime T: type, name: []const u8, value: T) void {
37 return addOptionFallible(self, T, name, value) catch @panic("unhandled error");
38}
39
40fn addOptionFallible(self: *OptionsStep, comptime T: type, name: []const u8, value: T) !void {
41 const out = self.contents.writer();
42 switch (T) {
43 []const []const u8 => {
44 try out.print("pub const {}: []const []const u8 = &[_][]const u8{{\n", .{std.zig.fmtId(name)});
45 for (value) |slice| {
46 try out.print(" \"{}\",\n", .{std.zig.fmtEscapes(slice)});
47 }
48 try out.writeAll("};\n");
49 return;
50 },
51 [:0]const u8 => {
52 try out.print("pub const {}: [:0]const u8 = \"{}\";\n", .{ std.zig.fmtId(name), std.zig.fmtEscapes(value) });
53 return;
54 },
55 []const u8 => {
56 try out.print("pub const {}: []const u8 = \"{}\";\n", .{ std.zig.fmtId(name), std.zig.fmtEscapes(value) });
57 return;
58 },
59 ?[:0]const u8 => {
60 try out.print("pub const {}: ?[:0]const u8 = ", .{std.zig.fmtId(name)});
61 if (value) |payload| {
62 try out.print("\"{}\";\n", .{std.zig.fmtEscapes(payload)});
63 } else {
64 try out.writeAll("null;\n");
65 }
66 return;
67 },
68 ?[]const u8 => {
69 try out.print("pub const {}: ?[]const u8 = ", .{std.zig.fmtId(name)});
70 if (value) |payload| {
71 try out.print("\"{}\";\n", .{std.zig.fmtEscapes(payload)});
72 } else {
73 try out.writeAll("null;\n");
74 }
75 return;
76 },
77 std.builtin.Version => {
78 try out.print(
79 \\pub const {}: @import("std").builtin.Version = .{{
80 \\ .major = {d},
81 \\ .minor = {d},
82 \\ .patch = {d},
83 \\}};
84 \\
85 , .{
86 std.zig.fmtId(name),
87
88 value.major,
89 value.minor,
90 value.patch,
91 });
92 return;
93 },
94 std.SemanticVersion => {
95 try out.print(
96 \\pub const {}: @import("std").SemanticVersion = .{{
97 \\ .major = {d},
98 \\ .minor = {d},
99 \\ .patch = {d},
100 \\
101 , .{
102 std.zig.fmtId(name),
103
104 value.major,
105 value.minor,
106 value.patch,
107 });
108 if (value.pre) |some| {
109 try out.print(" .pre = \"{}\",\n", .{std.zig.fmtEscapes(some)});
110 }
111 if (value.build) |some| {
112 try out.print(" .build = \"{}\",\n", .{std.zig.fmtEscapes(some)});
113 }
114 try out.writeAll("};\n");
115 return;
116 },
117 else => {},
118 }
119 switch (@typeInfo(T)) {
120 .Enum => |enum_info| {
121 try out.print("pub const {} = enum {{\n", .{std.zig.fmtId(@typeName(T))});
122 inline for (enum_info.fields) |field| {
123 try out.print(" {},\n", .{std.zig.fmtId(field.name)});
124 }
125 try out.writeAll("};\n");
126 try out.print("pub const {}: {s} = {s}.{s};\n", .{
127 std.zig.fmtId(name),
128 std.zig.fmtId(@typeName(T)),
129 std.zig.fmtId(@typeName(T)),
130 std.zig.fmtId(@tagName(value)),
131 });
132 return;
133 },
134 else => {},
135 }
136 try out.print("pub const {}: {s} = ", .{ std.zig.fmtId(name), @typeName(T) });
137 try printLiteral(out, value, 0);
138 try out.writeAll(";\n");
139}
140
141// TODO: non-recursive?
142fn printLiteral(out: anytype, val: anytype, indent: u8) !void {
143 const T = @TypeOf(val);
144 switch (@typeInfo(T)) {
145 .Array => {
146 try out.print("{s} {{\n", .{@typeName(T)});
147 for (val) |item| {
148 try out.writeByteNTimes(' ', indent + 4);
149 try printLiteral(out, item, indent + 4);
150 try out.writeAll(",\n");
151 }
152 try out.writeByteNTimes(' ', indent);
153 try out.writeAll("}");
154 },
155 .Pointer => |p| {
156 if (p.size != .Slice) {
157 @compileError("Non-slice pointers are not yet supported in build options");
158 }
159 try out.print("&[_]{s} {{\n", .{@typeName(p.child)});
160 for (val) |item| {
161 try out.writeByteNTimes(' ', indent + 4);
162 try printLiteral(out, item, indent + 4);
163 try out.writeAll(",\n");
164 }
165 try out.writeByteNTimes(' ', indent);
166 try out.writeAll("}");
167 },
168 .Optional => {
169 if (val) |inner| {
170 return printLiteral(out, inner, indent);
171 } else {
172 return out.writeAll("null");
173 }
174 },
175 .Void,
176 .Bool,
177 .Int,
178 .ComptimeInt,
179 .Float,
180 .Null,
181 => try out.print("{any}", .{val}),
182 else => @compileError(std.fmt.comptimePrint("`{s}` are not yet supported as build options", .{@tagName(@typeInfo(T))})),
183 }
184}
185
186/// The value is the path in the cache dir.
187/// Adds a dependency automatically.
188pub fn addOptionFileSource(
189 self: *OptionsStep,
190 name: []const u8,
191 source: FileSource,
192) void {
193 self.file_source_args.append(.{
194 .name = name,
195 .source = source.dupe(self.builder),
196 }) catch @panic("OOM");
197 source.addStepDependencies(&self.step);
198}
199
200/// The value is the path in the cache dir.
201/// Adds a dependency automatically.
202pub fn addOptionArtifact(self: *OptionsStep, name: []const u8, artifact: *CompileStep) void {
203 self.artifact_args.append(.{ .name = self.builder.dupe(name), .artifact = artifact }) catch @panic("OOM");
204 self.step.dependOn(&artifact.step);
205}
206
207pub fn getPackage(self: *OptionsStep, package_name: []const u8) std.Build.Pkg {
208 return .{ .name = package_name, .source = self.getSource() };
209}
210
211pub fn getSource(self: *OptionsStep) FileSource {
212 return .{ .generated = &self.generated_file };
213}
214
215fn make(step: *Step) !void {
216 const self = @fieldParentPtr(OptionsStep, "step", step);
217
218 for (self.artifact_args.items) |item| {
219 self.addOption(
220 []const u8,
221 item.name,
222 self.builder.pathFromRoot(item.artifact.getOutputSource().getPath(self.builder)),
223 );
224 }
225
226 for (self.file_source_args.items) |item| {
227 self.addOption(
228 []const u8,
229 item.name,
230 item.source.getPath(self.builder),
231 );
232 }
233
234 const options_directory = self.builder.pathFromRoot(
235 try fs.path.join(
236 self.builder.allocator,
237 &[_][]const u8{ self.builder.cache_root, "options" },
238 ),
239 );
240
241 try fs.cwd().makePath(options_directory);
242
243 const options_file = try fs.path.join(
244 self.builder.allocator,
245 &[_][]const u8{ options_directory, &self.hashContentsToFileName() },
246 );
247
248 try fs.cwd().writeFile(options_file, self.contents.items);
249
250 self.generated_file.path = options_file;
251}
252
253fn hashContentsToFileName(self: *OptionsStep) [64]u8 {
254 // This implementation is copied from `WriteFileStep.make`
255
256 var hash = std.crypto.hash.blake2.Blake2b384.init(.{});
257
258 // Random bytes to make OptionsStep unique. Refresh this with
259 // new random bytes when OptionsStep implementation is modified
260 // in a non-backwards-compatible way.
261 hash.update("yL0Ya4KkmcCjBlP8");
262 hash.update(self.contents.items);
263
264 var digest: [48]u8 = undefined;
265 hash.final(&digest);
266 var hash_basename: [64]u8 = undefined;
267 _ = fs.base64_encoder.encode(&hash_basename, &digest);
268 return hash_basename;
269}
270
271const OptionArtifactArg = struct {
272 name: []const u8,
273 artifact: *CompileStep,
274};
275
276const OptionFileSourceArg = struct {
277 name: []const u8,
278 source: FileSource,
279};
280
281test "OptionsStep" {
282 if (builtin.os.tag == .wasi) return error.SkipZigTest;
283
284 var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
285 defer arena.deinit();
286
287 const host = try std.zig.system.NativeTargetInfo.detect(.{});
288
289 var builder = try std.Build.create(
290 arena.allocator(),
291 "test",
292 "test",
293 "test",
294 "test",
295 host,
296 );
297 defer builder.destroy();
298
299 const options = builder.addOptions();
300
301 // TODO this regressed at some point
302 //const KeywordEnum = enum {
303 // @"0.8.1",
304 //};
305
306 const nested_array = [2][2]u16{
307 [2]u16{ 300, 200 },
308 [2]u16{ 300, 200 },
309 };
310 const nested_slice: []const []const u16 = &[_][]const u16{ &nested_array[0], &nested_array[1] };
311
312 options.addOption(usize, "option1", 1);
313 options.addOption(?usize, "option2", null);
314 options.addOption(?usize, "option3", 3);
315 options.addOption(comptime_int, "option4", 4);
316 options.addOption([]const u8, "string", "zigisthebest");
317 options.addOption(?[]const u8, "optional_string", null);
318 options.addOption([2][2]u16, "nested_array", nested_array);
319 options.addOption([]const []const u16, "nested_slice", nested_slice);
320 //options.addOption(KeywordEnum, "keyword_enum", .@"0.8.1");
321 options.addOption(std.builtin.Version, "version", try std.builtin.Version.parse("0.1.2"));
322 options.addOption(std.SemanticVersion, "semantic_version", try std.SemanticVersion.parse("0.1.2-foo+bar"));
323
324 try std.testing.expectEqualStrings(
325 \\pub const option1: usize = 1;
326 \\pub const option2: ?usize = null;
327 \\pub const option3: ?usize = 3;
328 \\pub const option4: comptime_int = 4;
329 \\pub const string: []const u8 = "zigisthebest";
330 \\pub const optional_string: ?[]const u8 = null;
331 \\pub const nested_array: [2][2]u16 = [2][2]u16 {
332 \\ [2]u16 {
333 \\ 300,
334 \\ 200,
335 \\ },
336 \\ [2]u16 {
337 \\ 300,
338 \\ 200,
339 \\ },
340 \\};
341 \\pub const nested_slice: []const []const u16 = &[_][]const u16 {
342 \\ &[_]u16 {
343 \\ 300,
344 \\ 200,
345 \\ },
346 \\ &[_]u16 {
347 \\ 300,
348 \\ 200,
349 \\ },
350 \\};
351 //\\pub const KeywordEnum = enum {
352 //\\ @"0.8.1",
353 //\\};
354 //\\pub const keyword_enum: KeywordEnum = KeywordEnum.@"0.8.1";
355 \\pub const version: @import("std").builtin.Version = .{
356 \\ .major = 0,
357 \\ .minor = 1,
358 \\ .patch = 2,
359 \\};
360 \\pub const semantic_version: @import("std").SemanticVersion = .{
361 \\ .major = 0,
362 \\ .minor = 1,
363 \\ .patch = 2,
364 \\ .pre = "foo",
365 \\ .build = "bar",
366 \\};
367 \\
368 , options.contents.items);
369
370 _ = try std.zig.Ast.parse(arena.allocator(), try options.contents.toOwnedSliceSentinel(0), .zig);
371}
lib/std/Build/RemoveDirStep.zig created+29
...@@ -0,0 +1,29 @@
1const std = @import("../std.zig");
2const log = std.log;
3const fs = std.fs;
4const Step = std.Build.Step;
5const RemoveDirStep = @This();
6
7pub const base_id = .remove_dir;
8
9step: Step,
10builder: *std.Build,
11dir_path: []const u8,
12
13pub fn init(builder: *std.Build, dir_path: []const u8) RemoveDirStep {
14 return RemoveDirStep{
15 .builder = builder,
16 .step = Step.init(.remove_dir, builder.fmt("RemoveDir {s}", .{dir_path}), builder.allocator, make),
17 .dir_path = builder.dupePath(dir_path),
18 };
19}
20
21fn make(step: *Step) !void {
22 const self = @fieldParentPtr(RemoveDirStep, "step", step);
23
24 const full_path = self.builder.pathFromRoot(self.dir_path);
25 fs.cwd().deleteTree(full_path) catch |err| {
26 log.err("Unable to remove {s}: {s}", .{ full_path, @errorName(err) });
27 return err;
28 };
29}
lib/std/Build/RunStep.zig created+376
...@@ -0,0 +1,376 @@
1const std = @import("../std.zig");
2const builtin = @import("builtin");
3const Step = std.Build.Step;
4const CompileStep = std.Build.CompileStep;
5const WriteFileStep = std.Build.WriteFileStep;
6const fs = std.fs;
7const mem = std.mem;
8const process = std.process;
9const ArrayList = std.ArrayList;
10const EnvMap = process.EnvMap;
11const Allocator = mem.Allocator;
12const ExecError = std.Build.ExecError;
13
14const max_stdout_size = 1 * 1024 * 1024; // 1 MiB
15
16const RunStep = @This();
17
18pub const base_id: Step.Id = .run;
19
20step: Step,
21builder: *std.Build,
22
23/// See also addArg and addArgs to modifying this directly
24argv: ArrayList(Arg),
25
26/// Set this to modify the current working directory
27cwd: ?[]const u8,
28
29/// Override this field to modify the environment, or use setEnvironmentVariable
30env_map: ?*EnvMap,
31
32stdout_action: StdIoAction = .inherit,
33stderr_action: StdIoAction = .inherit,
34
35stdin_behavior: std.ChildProcess.StdIo = .Inherit,
36
37/// Set this to `null` to ignore the exit code for the purpose of determining a successful execution
38expected_exit_code: ?u8 = 0,
39
40/// Print the command before running it
41print: bool,
42
43pub const StdIoAction = union(enum) {
44 inherit,
45 ignore,
46 expect_exact: []const u8,
47 expect_matches: []const []const u8,
48};
49
50pub const Arg = union(enum) {
51 artifact: *CompileStep,
52 file_source: std.Build.FileSource,
53 bytes: []u8,
54};
55
56pub fn create(builder: *std.Build, name: []const u8) *RunStep {
57 const self = builder.allocator.create(RunStep) catch @panic("OOM");
58 self.* = RunStep{
59 .builder = builder,
60 .step = Step.init(base_id, name, builder.allocator, make),
61 .argv = ArrayList(Arg).init(builder.allocator),
62 .cwd = null,
63 .env_map = null,
64 .print = builder.verbose,
65 };
66 return self;
67}
68
69pub fn addArtifactArg(self: *RunStep, artifact: *CompileStep) void {
70 self.argv.append(Arg{ .artifact = artifact }) catch @panic("OOM");
71 self.step.dependOn(&artifact.step);
72}
73
74pub fn addFileSourceArg(self: *RunStep, file_source: std.Build.FileSource) void {
75 self.argv.append(Arg{
76 .file_source = file_source.dupe(self.builder),
77 }) catch @panic("OOM");
78 file_source.addStepDependencies(&self.step);
79}
80
81pub fn addArg(self: *RunStep, arg: []const u8) void {
82 self.argv.append(Arg{ .bytes = self.builder.dupe(arg) }) catch @panic("OOM");
83}
84
85pub fn addArgs(self: *RunStep, args: []const []const u8) void {
86 for (args) |arg| {
87 self.addArg(arg);
88 }
89}
90
91pub fn clearEnvironment(self: *RunStep) void {
92 const new_env_map = self.builder.allocator.create(EnvMap) catch @panic("OOM");
93 new_env_map.* = EnvMap.init(self.builder.allocator);
94 self.env_map = new_env_map;
95}
96
97pub fn addPathDir(self: *RunStep, search_path: []const u8) void {
98 addPathDirInternal(&self.step, self.builder, search_path);
99}
100
101/// For internal use only, users of `RunStep` should use `addPathDir` directly.
102pub fn addPathDirInternal(step: *Step, builder: *std.Build, search_path: []const u8) void {
103 const env_map = getEnvMapInternal(step, builder.allocator);
104
105 const key = "PATH";
106 var prev_path = env_map.get(key);
107
108 if (prev_path) |pp| {
109 const new_path = builder.fmt("{s}" ++ [1]u8{fs.path.delimiter} ++ "{s}", .{ pp, search_path });
110 env_map.put(key, new_path) catch @panic("OOM");
111 } else {
112 env_map.put(key, builder.dupePath(search_path)) catch @panic("OOM");
113 }
114}
115
116pub fn getEnvMap(self: *RunStep) *EnvMap {
117 return getEnvMapInternal(&self.step, self.builder.allocator);
118}
119
120fn getEnvMapInternal(step: *Step, allocator: Allocator) *EnvMap {
121 const maybe_env_map = switch (step.id) {
122 .run => step.cast(RunStep).?.env_map,
123 .emulatable_run => step.cast(std.Build.EmulatableRunStep).?.env_map,
124 else => unreachable,
125 };
126 return maybe_env_map orelse {
127 const env_map = allocator.create(EnvMap) catch @panic("OOM");
128 env_map.* = process.getEnvMap(allocator) catch @panic("unhandled error");
129 switch (step.id) {
130 .run => step.cast(RunStep).?.env_map = env_map,
131 .emulatable_run => step.cast(RunStep).?.env_map = env_map,
132 else => unreachable,
133 }
134 return env_map;
135 };
136}
137
138pub fn setEnvironmentVariable(self: *RunStep, key: []const u8, value: []const u8) void {
139 const env_map = self.getEnvMap();
140 env_map.put(
141 self.builder.dupe(key),
142 self.builder.dupe(value),
143 ) catch @panic("unhandled error");
144}
145
146pub fn expectStdErrEqual(self: *RunStep, bytes: []const u8) void {
147 self.stderr_action = .{ .expect_exact = self.builder.dupe(bytes) };
148}
149
150pub fn expectStdOutEqual(self: *RunStep, bytes: []const u8) void {
151 self.stdout_action = .{ .expect_exact = self.builder.dupe(bytes) };
152}
153
154fn stdIoActionToBehavior(action: StdIoAction) std.ChildProcess.StdIo {
155 return switch (action) {
156 .ignore => .Ignore,
157 .inherit => .Inherit,
158 .expect_exact, .expect_matches => .Pipe,
159 };
160}
161
162fn make(step: *Step) !void {
163 const self = @fieldParentPtr(RunStep, "step", step);
164
165 var argv_list = ArrayList([]const u8).init(self.builder.allocator);
166 for (self.argv.items) |arg| {
167 switch (arg) {
168 .bytes => |bytes| try argv_list.append(bytes),
169 .file_source => |file| try argv_list.append(file.getPath(self.builder)),
170 .artifact => |artifact| {
171 if (artifact.target.isWindows()) {
172 // On Windows we don't have rpaths so we have to add .dll search paths to PATH
173 self.addPathForDynLibs(artifact);
174 }
175 const executable_path = artifact.installed_path orelse artifact.getOutputSource().getPath(self.builder);
176 try argv_list.append(executable_path);
177 },
178 }
179 }
180
181 try runCommand(
182 argv_list.items,
183 self.builder,
184 self.expected_exit_code,
185 self.stdout_action,
186 self.stderr_action,
187 self.stdin_behavior,
188 self.env_map,
189 self.cwd,
190 self.print,
191 );
192}
193
194pub fn runCommand(
195 argv: []const []const u8,
196 builder: *std.Build,
197 expected_exit_code: ?u8,
198 stdout_action: StdIoAction,
199 stderr_action: StdIoAction,
200 stdin_behavior: std.ChildProcess.StdIo,
201 env_map: ?*EnvMap,
202 maybe_cwd: ?[]const u8,
203 print: bool,
204) !void {
205 const cwd = if (maybe_cwd) |cwd| builder.pathFromRoot(cwd) else builder.build_root;
206
207 if (!std.process.can_spawn) {
208 const cmd = try std.mem.join(builder.allocator, " ", argv);
209 std.debug.print("the following command cannot be executed ({s} does not support spawning a child process):\n{s}", .{ @tagName(builtin.os.tag), cmd });
210 builder.allocator.free(cmd);
211 return ExecError.ExecNotSupported;
212 }
213
214 var child = std.ChildProcess.init(argv, builder.allocator);
215 child.cwd = cwd;
216 child.env_map = env_map orelse builder.env_map;
217
218 child.stdin_behavior = stdin_behavior;
219 child.stdout_behavior = stdIoActionToBehavior(stdout_action);
220 child.stderr_behavior = stdIoActionToBehavior(stderr_action);
221
222 if (print)
223 printCmd(cwd, argv);
224
225 child.spawn() catch |err| {
226 std.debug.print("Unable to spawn {s}: {s}\n", .{ argv[0], @errorName(err) });
227 return err;
228 };
229
230 // TODO need to poll to read these streams to prevent a deadlock (or rely on evented I/O).
231
232 var stdout: ?[]const u8 = null;
233 defer if (stdout) |s| builder.allocator.free(s);
234
235 switch (stdout_action) {
236 .expect_exact, .expect_matches => {
237 stdout = try child.stdout.?.reader().readAllAlloc(builder.allocator, max_stdout_size);
238 },
239 .inherit, .ignore => {},
240 }
241
242 var stderr: ?[]const u8 = null;
243 defer if (stderr) |s| builder.allocator.free(s);
244
245 switch (stderr_action) {
246 .expect_exact, .expect_matches => {
247 stderr = try child.stderr.?.reader().readAllAlloc(builder.allocator, max_stdout_size);
248 },
249 .inherit, .ignore => {},
250 }
251
252 const term = child.wait() catch |err| {
253 std.debug.print("Unable to spawn {s}: {s}\n", .{ argv[0], @errorName(err) });
254 return err;
255 };
256
257 switch (term) {
258 .Exited => |code| blk: {
259 const expected_code = expected_exit_code orelse break :blk;
260
261 if (code != expected_code) {
262 if (builder.prominent_compile_errors) {
263 std.debug.print("Run step exited with error code {} (expected {})\n", .{
264 code,
265 expected_code,
266 });
267 } else {
268 std.debug.print("The following command exited with error code {} (expected {}):\n", .{
269 code,
270 expected_code,
271 });
272 printCmd(cwd, argv);
273 }
274
275 return error.UnexpectedExitCode;
276 }
277 },
278 else => {
279 std.debug.print("The following command terminated unexpectedly:\n", .{});
280 printCmd(cwd, argv);
281 return error.UncleanExit;
282 },
283 }
284
285 switch (stderr_action) {
286 .inherit, .ignore => {},
287 .expect_exact => |expected_bytes| {
288 if (!mem.eql(u8, expected_bytes, stderr.?)) {
289 std.debug.print(
290 \\
291 \\========= Expected this stderr: =========
292 \\{s}
293 \\========= But found: ====================
294 \\{s}
295 \\
296 , .{ expected_bytes, stderr.? });
297 printCmd(cwd, argv);
298 return error.TestFailed;
299 }
300 },
301 .expect_matches => |matches| for (matches) |match| {
302 if (mem.indexOf(u8, stderr.?, match) == null) {
303 std.debug.print(
304 \\
305 \\========= Expected to find in stderr: =========
306 \\{s}
307 \\========= But stderr does not contain it: =====
308 \\{s}
309 \\
310 , .{ match, stderr.? });
311 printCmd(cwd, argv);
312 return error.TestFailed;
313 }
314 },
315 }
316
317 switch (stdout_action) {
318 .inherit, .ignore => {},
319 .expect_exact => |expected_bytes| {
320 if (!mem.eql(u8, expected_bytes, stdout.?)) {
321 std.debug.print(
322 \\
323 \\========= Expected this stdout: =========
324 \\{s}
325 \\========= But found: ====================
326 \\{s}
327 \\
328 , .{ expected_bytes, stdout.? });
329 printCmd(cwd, argv);
330 return error.TestFailed;
331 }
332 },
333 .expect_matches => |matches| for (matches) |match| {
334 if (mem.indexOf(u8, stdout.?, match) == null) {
335 std.debug.print(
336 \\
337 \\========= Expected to find in stdout: =========
338 \\{s}
339 \\========= But stdout does not contain it: =====
340 \\{s}
341 \\
342 , .{ match, stdout.? });
343 printCmd(cwd, argv);
344 return error.TestFailed;
345 }
346 },
347 }
348}
349
350fn printCmd(cwd: ?[]const u8, argv: []const []const u8) void {
351 if (cwd) |yes_cwd| std.debug.print("cd {s} && ", .{yes_cwd});
352 for (argv) |arg| {
353 std.debug.print("{s} ", .{arg});
354 }
355 std.debug.print("\n", .{});
356}
357
358fn addPathForDynLibs(self: *RunStep, artifact: *CompileStep) void {
359 addPathForDynLibsInternal(&self.step, self.builder, artifact);
360}
361
362/// This should only be used for internal usage, this is called automatically
363/// for the user.
364pub fn addPathForDynLibsInternal(step: *Step, builder: *std.Build, artifact: *CompileStep) void {
365 for (artifact.link_objects.items) |link_object| {
366 switch (link_object) {
367 .other_step => |other| {
368 if (other.target.isWindows() and other.isDynamicLibrary()) {
369 addPathDirInternal(step, builder, fs.path.dirname(other.getOutputSource().getPath(builder)).?);
370 addPathForDynLibsInternal(step, builder, other);
371 }
372 },
373 else => {},
374 }
375 }
376}
lib/std/Build/Step.zig created+97
...@@ -0,0 +1,97 @@
1id: Id,
2name: []const u8,
3makeFn: *const fn (self: *Step) anyerror!void,
4dependencies: std.ArrayList(*Step),
5loop_flag: bool,
6done_flag: bool,
7
8pub const Id = enum {
9 top_level,
10 compile,
11 install_artifact,
12 install_file,
13 install_dir,
14 log,
15 remove_dir,
16 fmt,
17 translate_c,
18 write_file,
19 run,
20 emulatable_run,
21 check_file,
22 check_object,
23 config_header,
24 install_raw,
25 options,
26 custom,
27
28 pub fn Type(comptime id: Id) type {
29 return switch (id) {
30 .top_level => Build.TopLevelStep,
31 .compile => Build.CompileStep,
32 .install_artifact => Build.InstallArtifactStep,
33 .install_file => Build.InstallFileStep,
34 .install_dir => Build.InstallDirStep,
35 .log => Build.LogStep,
36 .remove_dir => Build.RemoveDirStep,
37 .fmt => Build.FmtStep,
38 .translate_c => Build.TranslateCStep,
39 .write_file => Build.WriteFileStep,
40 .run => Build.RunStep,
41 .emulatable_run => Build.EmulatableRunStep,
42 .check_file => Build.CheckFileStep,
43 .check_object => Build.CheckObjectStep,
44 .config_header => Build.ConfigHeaderStep,
45 .install_raw => Build.InstallRawStep,
46 .options => Build.OptionsStep,
47 .custom => @compileError("no type available for custom step"),
48 };
49 }
50};
51
52pub fn init(
53 id: Id,
54 name: []const u8,
55 allocator: Allocator,
56 makeFn: *const fn (self: *Step) anyerror!void,
57) Step {
58 return Step{
59 .id = id,
60 .name = allocator.dupe(u8, name) catch @panic("OOM"),
61 .makeFn = makeFn,
62 .dependencies = std.ArrayList(*Step).init(allocator),
63 .loop_flag = false,
64 .done_flag = false,
65 };
66}
67
68pub fn initNoOp(id: Id, name: []const u8, allocator: Allocator) Step {
69 return init(id, name, allocator, makeNoOp);
70}
71
72pub fn make(self: *Step) !void {
73 if (self.done_flag) return;
74
75 try self.makeFn(self);
76 self.done_flag = true;
77}
78
79pub fn dependOn(self: *Step, other: *Step) void {
80 self.dependencies.append(other) catch @panic("OOM");
81}
82
83fn makeNoOp(self: *Step) anyerror!void {
84 _ = self;
85}
86
87pub fn cast(step: *Step, comptime T: type) ?*T {
88 if (step.id == T.base_id) {
89 return @fieldParentPtr(T, "step", step);
90 }
91 return null;
92}
93
94const Step = @This();
95const std = @import("../std.zig");
96const Build = std.Build;
97const Allocator = std.mem.Allocator;
lib/std/Build/TranslateCStep.zig created+136
...@@ -0,0 +1,136 @@
1const std = @import("../std.zig");
2const Step = std.Build.Step;
3const CompileStep = std.Build.CompileStep;
4const CheckFileStep = std.Build.CheckFileStep;
5const fs = std.fs;
6const mem = std.mem;
7const CrossTarget = std.zig.CrossTarget;
8
9const TranslateCStep = @This();
10
11pub const base_id = .translate_c;
12
13step: Step,
14builder: *std.Build,
15source: std.Build.FileSource,
16include_dirs: std.ArrayList([]const u8),
17c_macros: std.ArrayList([]const u8),
18output_dir: ?[]const u8,
19out_basename: []const u8,
20target: CrossTarget,
21optimize: std.builtin.OptimizeMode,
22output_file: std.Build.GeneratedFile,
23
24pub const Options = struct {
25 source_file: std.Build.FileSource,
26 target: CrossTarget,
27 optimize: std.builtin.OptimizeMode,
28};
29
30pub fn create(builder: *std.Build, options: Options) *TranslateCStep {
31 const self = builder.allocator.create(TranslateCStep) catch @panic("OOM");
32 const source = options.source_file.dupe(builder);
33 self.* = TranslateCStep{
34 .step = Step.init(.translate_c, "translate-c", builder.allocator, make),
35 .builder = builder,
36 .source = source,
37 .include_dirs = std.ArrayList([]const u8).init(builder.allocator),
38 .c_macros = std.ArrayList([]const u8).init(builder.allocator),
39 .output_dir = null,
40 .out_basename = undefined,
41 .target = options.target,
42 .optimize = options.optimize,
43 .output_file = std.Build.GeneratedFile{ .step = &self.step },
44 };
45 source.addStepDependencies(&self.step);
46 return self;
47}
48
49pub const AddExecutableOptions = struct {
50 name: ?[]const u8 = null,
51 version: ?std.builtin.Version = null,
52 target: ?CrossTarget = null,
53 optimize: ?std.builtin.Mode = null,
54 linkage: ?CompileStep.Linkage = null,
55};
56
57/// Creates a step to build an executable from the translated source.
58pub fn addExecutable(self: *TranslateCStep, options: AddExecutableOptions) *CompileStep {
59 return self.builder.addExecutable(.{
60 .root_source_file = .{ .generated = &self.output_file },
61 .name = options.name orelse "translated_c",
62 .version = options.version,
63 .target = options.target orelse self.target,
64 .optimize = options.optimize orelse self.optimize,
65 .linkage = options.linkage,
66 });
67}
68
69pub fn addIncludeDir(self: *TranslateCStep, include_dir: []const u8) void {
70 self.include_dirs.append(self.builder.dupePath(include_dir)) catch @panic("OOM");
71}
72
73pub fn addCheckFile(self: *TranslateCStep, expected_matches: []const []const u8) *CheckFileStep {
74 return CheckFileStep.create(self.builder, .{ .generated = &self.output_file }, self.builder.dupeStrings(expected_matches));
75}
76
77/// If the value is omitted, it is set to 1.
78/// `name` and `value` need not live longer than the function call.
79pub fn defineCMacro(self: *TranslateCStep, name: []const u8, value: ?[]const u8) void {
80 const macro = std.Build.constructCMacro(self.builder.allocator, name, value);
81 self.c_macros.append(macro) catch @panic("OOM");
82}
83
84/// name_and_value looks like [name]=[value]. If the value is omitted, it is set to 1.
85pub fn defineCMacroRaw(self: *TranslateCStep, name_and_value: []const u8) void {
86 self.c_macros.append(self.builder.dupe(name_and_value)) catch @panic("OOM");
87}
88
89fn make(step: *Step) !void {
90 const self = @fieldParentPtr(TranslateCStep, "step", step);
91
92 var argv_list = std.ArrayList([]const u8).init(self.builder.allocator);
93 try argv_list.append(self.builder.zig_exe);
94 try argv_list.append("translate-c");
95 try argv_list.append("-lc");
96
97 try argv_list.append("--enable-cache");
98
99 if (!self.target.isNative()) {
100 try argv_list.append("-target");
101 try argv_list.append(try self.target.zigTriple(self.builder.allocator));
102 }
103
104 switch (self.optimize) {
105 .Debug => {}, // Skip since it's the default.
106 else => try argv_list.append(self.builder.fmt("-O{s}", .{@tagName(self.optimize)})),
107 }
108
109 for (self.include_dirs.items) |include_dir| {
110 try argv_list.append("-I");
111 try argv_list.append(include_dir);
112 }
113
114 for (self.c_macros.items) |c_macro| {
115 try argv_list.append("-D");
116 try argv_list.append(c_macro);
117 }
118
119 try argv_list.append(self.source.getPath(self.builder));
120
121 const output_path_nl = try self.builder.execFromStep(argv_list.items, &self.step);
122 const output_path = mem.trimRight(u8, output_path_nl, "\r\n");
123
124 self.out_basename = fs.path.basename(output_path);
125 if (self.output_dir) |output_dir| {
126 const full_dest = try fs.path.join(self.builder.allocator, &[_][]const u8{ output_dir, self.out_basename });
127 try self.builder.updateFile(output_path, full_dest);
128 } else {
129 self.output_dir = fs.path.dirname(output_path).?;
130 }
131
132 self.output_file.path = try fs.path.join(
133 self.builder.allocator,
134 &[_][]const u8{ self.output_dir.?, self.out_basename },
135 );
136}
lib/std/Build/WriteFileStep.zig created+115
...@@ -0,0 +1,115 @@
1const std = @import("../std.zig");
2const Step = std.Build.Step;
3const fs = std.fs;
4const ArrayList = std.ArrayList;
5
6const WriteFileStep = @This();
7
8pub const base_id = .write_file;
9
10step: Step,
11builder: *std.Build,
12output_dir: []const u8,
13files: std.TailQueue(File),
14
15pub const File = struct {
16 source: std.Build.GeneratedFile,
17 basename: []const u8,
18 bytes: []const u8,
19};
20
21pub fn init(builder: *std.Build) WriteFileStep {
22 return WriteFileStep{
23 .builder = builder,
24 .step = Step.init(.write_file, "writefile", builder.allocator, make),
25 .files = .{},
26 .output_dir = undefined,
27 };
28}
29
30pub fn add(self: *WriteFileStep, basename: []const u8, bytes: []const u8) void {
31 const node = self.builder.allocator.create(std.TailQueue(File).Node) catch @panic("unhandled error");
32 node.* = .{
33 .data = .{
34 .source = std.Build.GeneratedFile{ .step = &self.step },
35 .basename = self.builder.dupePath(basename),
36 .bytes = self.builder.dupe(bytes),
37 },
38 };
39
40 self.files.append(node);
41}
42
43/// Gets a file source for the given basename. If the file does not exist, returns `null`.
44pub fn getFileSource(step: *WriteFileStep, basename: []const u8) ?std.Build.FileSource {
45 var it = step.files.first;
46 while (it) |node| : (it = node.next) {
47 if (std.mem.eql(u8, node.data.basename, basename))
48 return std.Build.FileSource{ .generated = &node.data.source };
49 }
50 return null;
51}
52
53fn make(step: *Step) !void {
54 const self = @fieldParentPtr(WriteFileStep, "step", step);
55
56 // The cache is used here not really as a way to speed things up - because writing
57 // the data to a file would probably be very fast - but as a way to find a canonical
58 // location to put build artifacts.
59
60 // If, for example, a hard-coded path was used as the location to put WriteFileStep
61 // files, then two WriteFileSteps executing in parallel might clobber each other.
62
63 // TODO port the cache system from the compiler to zig std lib. Until then
64 // we directly construct the path, and no "cache hit" detection happens;
65 // the files are always written.
66 // Note there is similar code over in ConfigHeaderStep.
67 const Hasher = std.crypto.auth.siphash.SipHash128(1, 3);
68 // Random bytes to make WriteFileStep unique. Refresh this with
69 // new random bytes when WriteFileStep implementation is modified
70 // in a non-backwards-compatible way.
71 var hash = Hasher.init("eagVR1dYXoE7ARDP");
72
73 {
74 var it = self.files.first;
75 while (it) |node| : (it = node.next) {
76 hash.update(node.data.basename);
77 hash.update(node.data.bytes);
78 hash.update("|");
79 }
80 }
81 var digest: [16]u8 = undefined;
82 hash.final(&digest);
83 var hash_basename: [digest.len * 2]u8 = undefined;
84 _ = std.fmt.bufPrint(
85 &hash_basename,
86 "{s}",
87 .{std.fmt.fmtSliceHexLower(&digest)},
88 ) catch unreachable;
89
90 self.output_dir = try fs.path.join(self.builder.allocator, &[_][]const u8{
91 self.builder.cache_root, "o", &hash_basename,
92 });
93 var dir = fs.cwd().makeOpenPath(self.output_dir, .{}) catch |err| {
94 std.debug.print("unable to make path {s}: {s}\n", .{ self.output_dir, @errorName(err) });
95 return err;
96 };
97 defer dir.close();
98 {
99 var it = self.files.first;
100 while (it) |node| : (it = node.next) {
101 dir.writeFile(node.data.basename, node.data.bytes) catch |err| {
102 std.debug.print("unable to write {s} into {s}: {s}\n", .{
103 node.data.basename,
104 self.output_dir,
105 @errorName(err),
106 });
107 return err;
108 };
109 node.data.source.path = try fs.path.join(
110 self.builder.allocator,
111 &[_][]const u8{ self.output_dir, node.data.basename },
112 );
113 }
114 }
115}
lib/std/Thread.zig+1-1
...@@ -166,7 +166,7 @@ pub const GetNameError = error{...@@ -166,7 +166,7 @@ pub const GetNameError = error{
166166
167pub fn getName(self: Thread, buffer_ptr: *[max_name_len:0]u8) GetNameError!?[]const u8 {167pub fn getName(self: Thread, buffer_ptr: *[max_name_len:0]u8) GetNameError!?[]const u8 {
168 buffer_ptr[max_name_len] = 0;168 buffer_ptr[max_name_len] = 0;
169 var buffer = std.mem.span(buffer_ptr);169 var buffer: [:0]u8 = buffer_ptr;
170170
171 switch (target.os.tag) {171 switch (target.os.tag) {
172 .linux => if (use_pthreads and is_gnu) {172 .linux => if (use_pthreads and is_gnu) {
lib/std/array_hash_map.zig+2-1
...@@ -1145,7 +1145,8 @@ pub fn ArrayHashMapUnmanaged(...@@ -1145,7 +1145,8 @@ pub fn ArrayHashMapUnmanaged(
1145 }1145 }
11461146
1147 /// Create a copy of the hash map which can be modified separately.1147 /// Create a copy of the hash map which can be modified separately.
1148 /// The copy uses the same context and allocator as this instance.1148 /// The copy uses the same context as this instance, but is allocated
1149 /// with the provided allocator.
1149 pub fn clone(self: Self, allocator: Allocator) !Self {1150 pub fn clone(self: Self, allocator: Allocator) !Self {
1150 if (@sizeOf(ByIndexContext) != 0)1151 if (@sizeOf(ByIndexContext) != 0)
1151 @compileError("Cannot infer context " ++ @typeName(Context) ++ ", call cloneContext instead.");1152 @compileError("Cannot infer context " ++ @typeName(Context) ++ ", call cloneContext instead.");
lib/std/bounded_array.zig+5-1
...@@ -29,7 +29,11 @@ pub fn BoundedArray(comptime T: type, comptime buffer_capacity: usize) type {...@@ -29,7 +29,11 @@ pub fn BoundedArray(comptime T: type, comptime buffer_capacity: usize) type {
29 }29 }
3030
31 /// View the internal array as a slice whose size was previously set.31 /// View the internal array as a slice whose size was previously set.
32 pub fn slice(self: anytype) mem.Span(@TypeOf(&self.buffer)) {32 pub fn slice(self: anytype) switch (@TypeOf(&self.buffer)) {
33 *[buffer_capacity]T => []T,
34 *const [buffer_capacity]T => []const T,
35 else => unreachable,
36 } {
33 return self.buffer[0..self.len];37 return self.buffer[0..self.len];
34 }38 }
3539
lib/std/build.zig deleted-1781
...@@ -1,1781 +0,0 @@
1const std = @import("std.zig");
2const builtin = @import("builtin");
3const io = std.io;
4const fs = std.fs;
5const mem = std.mem;
6const debug = std.debug;
7const panic = std.debug.panic;
8const assert = debug.assert;
9const log = std.log;
10const ArrayList = std.ArrayList;
11const StringHashMap = std.StringHashMap;
12const Allocator = mem.Allocator;
13const process = std.process;
14const EnvMap = std.process.EnvMap;
15const fmt_lib = std.fmt;
16const File = std.fs.File;
17const CrossTarget = std.zig.CrossTarget;
18const NativeTargetInfo = std.zig.system.NativeTargetInfo;
19const Sha256 = std.crypto.hash.sha2.Sha256;
20const ThisModule = @This();
21
22pub const CheckFileStep = @import("build/CheckFileStep.zig");
23pub const CheckObjectStep = @import("build/CheckObjectStep.zig");
24pub const ConfigHeaderStep = @import("build/ConfigHeaderStep.zig");
25pub const EmulatableRunStep = @import("build/EmulatableRunStep.zig");
26pub const FmtStep = @import("build/FmtStep.zig");
27pub const InstallArtifactStep = @import("build/InstallArtifactStep.zig");
28pub const InstallDirStep = @import("build/InstallDirStep.zig");
29pub const InstallFileStep = @import("build/InstallFileStep.zig");
30pub const InstallRawStep = @import("build/InstallRawStep.zig");
31pub const LibExeObjStep = @import("build/LibExeObjStep.zig");
32pub const LogStep = @import("build/LogStep.zig");
33pub const OptionsStep = @import("build/OptionsStep.zig");
34pub const RemoveDirStep = @import("build/RemoveDirStep.zig");
35pub const RunStep = @import("build/RunStep.zig");
36pub const TranslateCStep = @import("build/TranslateCStep.zig");
37pub const WriteFileStep = @import("build/WriteFileStep.zig");
38
39pub const Builder = struct {
40 install_tls: TopLevelStep,
41 uninstall_tls: TopLevelStep,
42 allocator: Allocator,
43 user_input_options: UserInputOptionsMap,
44 available_options_map: AvailableOptionsMap,
45 available_options_list: ArrayList(AvailableOption),
46 verbose: bool,
47 verbose_link: bool,
48 verbose_cc: bool,
49 verbose_air: bool,
50 verbose_llvm_ir: bool,
51 verbose_cimport: bool,
52 verbose_llvm_cpu_features: bool,
53 /// The purpose of executing the command is for a human to read compile errors from the terminal
54 prominent_compile_errors: bool,
55 color: enum { auto, on, off } = .auto,
56 reference_trace: ?u32 = null,
57 invalid_user_input: bool,
58 zig_exe: []const u8,
59 default_step: *Step,
60 env_map: *EnvMap,
61 top_level_steps: ArrayList(*TopLevelStep),
62 install_prefix: []const u8,
63 dest_dir: ?[]const u8,
64 lib_dir: []const u8,
65 exe_dir: []const u8,
66 h_dir: []const u8,
67 install_path: []const u8,
68 sysroot: ?[]const u8 = null,
69 search_prefixes: ArrayList([]const u8),
70 libc_file: ?[]const u8 = null,
71 installed_files: ArrayList(InstalledFile),
72 /// Path to the directory containing build.zig.
73 build_root: []const u8,
74 cache_root: []const u8,
75 global_cache_root: []const u8,
76 release_mode: ?std.builtin.Mode,
77 is_release: bool,
78 /// zig lib dir
79 override_lib_dir: ?[]const u8,
80 vcpkg_root: VcpkgRoot = .unattempted,
81 pkg_config_pkg_list: ?(PkgConfigError![]const PkgConfigPkg) = null,
82 args: ?[][]const u8 = null,
83 debug_log_scopes: []const []const u8 = &.{},
84 debug_compile_errors: bool = false,
85
86 /// Experimental. Use system Darling installation to run cross compiled macOS build artifacts.
87 enable_darling: bool = false,
88 /// Use system QEMU installation to run cross compiled foreign architecture build artifacts.
89 enable_qemu: bool = false,
90 /// Darwin. Use Rosetta to run x86_64 macOS build artifacts on arm64 macOS.
91 enable_rosetta: bool = false,
92 /// Use system Wasmtime installation to run cross compiled wasm/wasi build artifacts.
93 enable_wasmtime: bool = false,
94 /// Use system Wine installation to run cross compiled Windows build artifacts.
95 enable_wine: bool = false,
96 /// After following the steps in https://github.com/ziglang/zig/wiki/Updating-libc#glibc,
97 /// this will be the directory $glibc-build-dir/install/glibcs
98 /// Given the example of the aarch64 target, this is the directory
99 /// that contains the path `aarch64-linux-gnu/lib/ld-linux-aarch64.so.1`.
100 glibc_runtimes_dir: ?[]const u8 = null,
101
102 /// Information about the native target. Computed before build() is invoked.
103 host: NativeTargetInfo,
104
105 dep_prefix: []const u8 = "",
106
107 pub const ExecError = error{
108 ReadFailure,
109 ExitCodeFailure,
110 ProcessTerminated,
111 ExecNotSupported,
112 } || std.ChildProcess.SpawnError;
113
114 pub const PkgConfigError = error{
115 PkgConfigCrashed,
116 PkgConfigFailed,
117 PkgConfigNotInstalled,
118 PkgConfigInvalidOutput,
119 };
120
121 pub const PkgConfigPkg = struct {
122 name: []const u8,
123 desc: []const u8,
124 };
125
126 pub const CStd = enum {
127 C89,
128 C99,
129 C11,
130 };
131
132 const UserInputOptionsMap = StringHashMap(UserInputOption);
133 const AvailableOptionsMap = StringHashMap(AvailableOption);
134
135 const AvailableOption = struct {
136 name: []const u8,
137 type_id: TypeId,
138 description: []const u8,
139 /// If the `type_id` is `enum` this provides the list of enum options
140 enum_options: ?[]const []const u8,
141 };
142
143 const UserInputOption = struct {
144 name: []const u8,
145 value: UserValue,
146 used: bool,
147 };
148
149 const UserValue = union(enum) {
150 flag: void,
151 scalar: []const u8,
152 list: ArrayList([]const u8),
153 };
154
155 const TypeId = enum {
156 bool,
157 int,
158 float,
159 @"enum",
160 string,
161 list,
162 };
163
164 const TopLevelStep = struct {
165 pub const base_id = .top_level;
166
167 step: Step,
168 description: []const u8,
169 };
170
171 pub const DirList = struct {
172 lib_dir: ?[]const u8 = null,
173 exe_dir: ?[]const u8 = null,
174 include_dir: ?[]const u8 = null,
175 };
176
177 pub fn create(
178 allocator: Allocator,
179 zig_exe: []const u8,
180 build_root: []const u8,
181 cache_root: []const u8,
182 global_cache_root: []const u8,
183 ) !*Builder {
184 const env_map = try allocator.create(EnvMap);
185 env_map.* = try process.getEnvMap(allocator);
186
187 const host = try NativeTargetInfo.detect(.{});
188
189 const self = try allocator.create(Builder);
190 self.* = Builder{
191 .zig_exe = zig_exe,
192 .build_root = build_root,
193 .cache_root = try fs.path.relative(allocator, build_root, cache_root),
194 .global_cache_root = global_cache_root,
195 .verbose = false,
196 .verbose_link = false,
197 .verbose_cc = false,
198 .verbose_air = false,
199 .verbose_llvm_ir = false,
200 .verbose_cimport = false,
201 .verbose_llvm_cpu_features = false,
202 .prominent_compile_errors = false,
203 .invalid_user_input = false,
204 .allocator = allocator,
205 .user_input_options = UserInputOptionsMap.init(allocator),
206 .available_options_map = AvailableOptionsMap.init(allocator),
207 .available_options_list = ArrayList(AvailableOption).init(allocator),
208 .top_level_steps = ArrayList(*TopLevelStep).init(allocator),
209 .default_step = undefined,
210 .env_map = env_map,
211 .search_prefixes = ArrayList([]const u8).init(allocator),
212 .install_prefix = undefined,
213 .lib_dir = undefined,
214 .exe_dir = undefined,
215 .h_dir = undefined,
216 .dest_dir = env_map.get("DESTDIR"),
217 .installed_files = ArrayList(InstalledFile).init(allocator),
218 .install_tls = TopLevelStep{
219 .step = Step.initNoOp(.top_level, "install", allocator),
220 .description = "Copy build artifacts to prefix path",
221 },
222 .uninstall_tls = TopLevelStep{
223 .step = Step.init(.top_level, "uninstall", allocator, makeUninstall),
224 .description = "Remove build artifacts from prefix path",
225 },
226 .release_mode = null,
227 .is_release = false,
228 .override_lib_dir = null,
229 .install_path = undefined,
230 .args = null,
231 .host = host,
232 };
233 try self.top_level_steps.append(&self.install_tls);
234 try self.top_level_steps.append(&self.uninstall_tls);
235 self.default_step = &self.install_tls.step;
236 return self;
237 }
238
239 fn createChild(
240 parent: *Builder,
241 dep_name: []const u8,
242 build_root: []const u8,
243 args: anytype,
244 ) !*Builder {
245 const child = try createChildOnly(parent, dep_name, build_root);
246 try applyArgs(child, args);
247 return child;
248 }
249
250 fn createChildOnly(parent: *Builder, dep_name: []const u8, build_root: []const u8) !*Builder {
251 const allocator = parent.allocator;
252 const child = try allocator.create(Builder);
253 child.* = .{
254 .allocator = allocator,
255 .install_tls = .{
256 .step = Step.initNoOp(.top_level, "install", allocator),
257 .description = "Copy build artifacts to prefix path",
258 },
259 .uninstall_tls = .{
260 .step = Step.init(.top_level, "uninstall", allocator, makeUninstall),
261 .description = "Remove build artifacts from prefix path",
262 },
263 .user_input_options = UserInputOptionsMap.init(allocator),
264 .available_options_map = AvailableOptionsMap.init(allocator),
265 .available_options_list = ArrayList(AvailableOption).init(allocator),
266 .verbose = parent.verbose,
267 .verbose_link = parent.verbose_link,
268 .verbose_cc = parent.verbose_cc,
269 .verbose_air = parent.verbose_air,
270 .verbose_llvm_ir = parent.verbose_llvm_ir,
271 .verbose_cimport = parent.verbose_cimport,
272 .verbose_llvm_cpu_features = parent.verbose_llvm_cpu_features,
273 .prominent_compile_errors = parent.prominent_compile_errors,
274 .color = parent.color,
275 .reference_trace = parent.reference_trace,
276 .invalid_user_input = false,
277 .zig_exe = parent.zig_exe,
278 .default_step = undefined,
279 .env_map = parent.env_map,
280 .top_level_steps = ArrayList(*TopLevelStep).init(allocator),
281 .install_prefix = undefined,
282 .dest_dir = parent.dest_dir,
283 .lib_dir = parent.lib_dir,
284 .exe_dir = parent.exe_dir,
285 .h_dir = parent.h_dir,
286 .install_path = parent.install_path,
287 .sysroot = parent.sysroot,
288 .search_prefixes = ArrayList([]const u8).init(allocator),
289 .libc_file = parent.libc_file,
290 .installed_files = ArrayList(InstalledFile).init(allocator),
291 .build_root = build_root,
292 .cache_root = parent.cache_root,
293 .global_cache_root = parent.global_cache_root,
294 .release_mode = parent.release_mode,
295 .is_release = parent.is_release,
296 .override_lib_dir = parent.override_lib_dir,
297 .debug_log_scopes = parent.debug_log_scopes,
298 .debug_compile_errors = parent.debug_compile_errors,
299 .enable_darling = parent.enable_darling,
300 .enable_qemu = parent.enable_qemu,
301 .enable_rosetta = parent.enable_rosetta,
302 .enable_wasmtime = parent.enable_wasmtime,
303 .enable_wine = parent.enable_wine,
304 .glibc_runtimes_dir = parent.glibc_runtimes_dir,
305 .host = parent.host,
306 .dep_prefix = parent.fmt("{s}{s}.", .{ parent.dep_prefix, dep_name }),
307 };
308 try child.top_level_steps.append(&child.install_tls);
309 try child.top_level_steps.append(&child.uninstall_tls);
310 child.default_step = &child.install_tls.step;
311 return child;
312 }
313
314 fn applyArgs(b: *Builder, args: anytype) !void {
315 // TODO this function is the way that a build.zig file communicates
316 // options to its dependencies. It is the programmatic way to give
317 // command line arguments to a build.zig script.
318 _ = args;
319 const Hasher = std.crypto.auth.siphash.SipHash128(1, 3);
320 // Random bytes to make unique. Refresh this with new random bytes when
321 // implementation is modified in a non-backwards-compatible way.
322 var hash = Hasher.init("ZaEsvQ5ClaA2IdH9");
323 hash.update(b.dep_prefix);
324 // TODO additionally update the hash with `args`.
325
326 var digest: [16]u8 = undefined;
327 hash.final(&digest);
328 var hash_basename: [digest.len * 2]u8 = undefined;
329 _ = std.fmt.bufPrint(&hash_basename, "{s}", .{std.fmt.fmtSliceHexLower(&digest)}) catch
330 unreachable;
331
332 const install_prefix = b.pathJoin(&.{ b.cache_root, "i", &hash_basename });
333 b.resolveInstallPrefix(install_prefix, .{});
334 }
335
336 pub fn destroy(self: *Builder) void {
337 self.env_map.deinit();
338 self.top_level_steps.deinit();
339 self.allocator.destroy(self);
340 }
341
342 /// This function is intended to be called by lib/build_runner.zig, not a build.zig file.
343 pub fn resolveInstallPrefix(self: *Builder, install_prefix: ?[]const u8, dir_list: DirList) void {
344 if (self.dest_dir) |dest_dir| {
345 self.install_prefix = install_prefix orelse "/usr";
346 self.install_path = self.pathJoin(&.{ dest_dir, self.install_prefix });
347 } else {
348 self.install_prefix = install_prefix orelse
349 (self.pathJoin(&.{ self.build_root, "zig-out" }));
350 self.install_path = self.install_prefix;
351 }
352
353 var lib_list = [_][]const u8{ self.install_path, "lib" };
354 var exe_list = [_][]const u8{ self.install_path, "bin" };
355 var h_list = [_][]const u8{ self.install_path, "include" };
356
357 if (dir_list.lib_dir) |dir| {
358 if (std.fs.path.isAbsolute(dir)) lib_list[0] = self.dest_dir orelse "";
359 lib_list[1] = dir;
360 }
361
362 if (dir_list.exe_dir) |dir| {
363 if (std.fs.path.isAbsolute(dir)) exe_list[0] = self.dest_dir orelse "";
364 exe_list[1] = dir;
365 }
366
367 if (dir_list.include_dir) |dir| {
368 if (std.fs.path.isAbsolute(dir)) h_list[0] = self.dest_dir orelse "";
369 h_list[1] = dir;
370 }
371
372 self.lib_dir = self.pathJoin(&lib_list);
373 self.exe_dir = self.pathJoin(&exe_list);
374 self.h_dir = self.pathJoin(&h_list);
375 }
376
377 fn convertOptionalPathToFileSource(path: ?[]const u8) ?FileSource {
378 return if (path) |p|
379 FileSource{ .path = p }
380 else
381 null;
382 }
383
384 pub fn addExecutable(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep {
385 return addExecutableSource(self, name, convertOptionalPathToFileSource(root_src));
386 }
387
388 pub fn addExecutableSource(builder: *Builder, name: []const u8, root_src: ?FileSource) *LibExeObjStep {
389 return LibExeObjStep.createExecutable(builder, name, root_src);
390 }
391
392 pub fn addOptions(self: *Builder) *OptionsStep {
393 return OptionsStep.create(self);
394 }
395
396 pub fn addObject(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep {
397 return addObjectSource(self, name, convertOptionalPathToFileSource(root_src));
398 }
399
400 pub fn addObjectSource(builder: *Builder, name: []const u8, root_src: ?FileSource) *LibExeObjStep {
401 return LibExeObjStep.createObject(builder, name, root_src);
402 }
403
404 pub fn addSharedLibrary(
405 self: *Builder,
406 name: []const u8,
407 root_src: ?[]const u8,
408 kind: LibExeObjStep.SharedLibKind,
409 ) *LibExeObjStep {
410 return addSharedLibrarySource(self, name, convertOptionalPathToFileSource(root_src), kind);
411 }
412
413 pub fn addSharedLibrarySource(
414 self: *Builder,
415 name: []const u8,
416 root_src: ?FileSource,
417 kind: LibExeObjStep.SharedLibKind,
418 ) *LibExeObjStep {
419 return LibExeObjStep.createSharedLibrary(self, name, root_src, kind);
420 }
421
422 pub fn addStaticLibrary(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep {
423 return addStaticLibrarySource(self, name, convertOptionalPathToFileSource(root_src));
424 }
425
426 pub fn addStaticLibrarySource(self: *Builder, name: []const u8, root_src: ?FileSource) *LibExeObjStep {
427 return LibExeObjStep.createStaticLibrary(self, name, root_src);
428 }
429
430 pub fn addTest(self: *Builder, root_src: []const u8) *LibExeObjStep {
431 return LibExeObjStep.createTest(self, "test", .{ .path = root_src });
432 }
433
434 pub fn addTestSource(self: *Builder, root_src: FileSource) *LibExeObjStep {
435 return LibExeObjStep.createTest(self, "test", root_src.dupe(self));
436 }
437
438 pub fn addTestExe(self: *Builder, name: []const u8, root_src: []const u8) *LibExeObjStep {
439 return LibExeObjStep.createTestExe(self, name, .{ .path = root_src });
440 }
441
442 pub fn addTestExeSource(self: *Builder, name: []const u8, root_src: FileSource) *LibExeObjStep {
443 return LibExeObjStep.createTestExe(self, name, root_src.dupe(self));
444 }
445
446 pub fn addAssemble(self: *Builder, name: []const u8, src: []const u8) *LibExeObjStep {
447 return addAssembleSource(self, name, .{ .path = src });
448 }
449
450 pub fn addAssembleSource(self: *Builder, name: []const u8, src: FileSource) *LibExeObjStep {
451 const obj_step = LibExeObjStep.createObject(self, name, null);
452 obj_step.addAssemblyFileSource(src.dupe(self));
453 return obj_step;
454 }
455
456 /// Initializes a RunStep with argv, which must at least have the path to the
457 /// executable. More command line arguments can be added with `addArg`,
458 /// `addArgs`, and `addArtifactArg`.
459 /// Be careful using this function, as it introduces a system dependency.
460 /// To run an executable built with zig build, see `LibExeObjStep.run`.
461 pub fn addSystemCommand(self: *Builder, argv: []const []const u8) *RunStep {
462 assert(argv.len >= 1);
463 const run_step = RunStep.create(self, self.fmt("run {s}", .{argv[0]}));
464 run_step.addArgs(argv);
465 return run_step;
466 }
467
468 pub fn addConfigHeader(
469 b: *Builder,
470 source: FileSource,
471 style: ConfigHeaderStep.Style,
472 values: anytype,
473 ) *ConfigHeaderStep {
474 const config_header_step = ConfigHeaderStep.create(b, source, style);
475 config_header_step.addValues(values);
476 return config_header_step;
477 }
478
479 /// Allocator.dupe without the need to handle out of memory.
480 pub fn dupe(self: *Builder, bytes: []const u8) []u8 {
481 return self.allocator.dupe(u8, bytes) catch unreachable;
482 }
483
484 /// Duplicates an array of strings without the need to handle out of memory.
485 pub fn dupeStrings(self: *Builder, strings: []const []const u8) [][]u8 {
486 const array = self.allocator.alloc([]u8, strings.len) catch unreachable;
487 for (strings) |s, i| {
488 array[i] = self.dupe(s);
489 }
490 return array;
491 }
492
493 /// Duplicates a path and converts all slashes to the OS's canonical path separator.
494 pub fn dupePath(self: *Builder, bytes: []const u8) []u8 {
495 const the_copy = self.dupe(bytes);
496 for (the_copy) |*byte| {
497 switch (byte.*) {
498 '/', '\\' => byte.* = fs.path.sep,
499 else => {},
500 }
501 }
502 return the_copy;
503 }
504
505 /// Duplicates a package recursively.
506 pub fn dupePkg(self: *Builder, package: Pkg) Pkg {
507 var the_copy = Pkg{
508 .name = self.dupe(package.name),
509 .source = package.source.dupe(self),
510 };
511
512 if (package.dependencies) |dependencies| {
513 const new_dependencies = self.allocator.alloc(Pkg, dependencies.len) catch unreachable;
514 the_copy.dependencies = new_dependencies;
515
516 for (dependencies) |dep_package, i| {
517 new_dependencies[i] = self.dupePkg(dep_package);
518 }
519 }
520 return the_copy;
521 }
522
523 pub fn addWriteFile(self: *Builder, file_path: []const u8, data: []const u8) *WriteFileStep {
524 const write_file_step = self.addWriteFiles();
525 write_file_step.add(file_path, data);
526 return write_file_step;
527 }
528
529 pub fn addWriteFiles(self: *Builder) *WriteFileStep {
530 const write_file_step = self.allocator.create(WriteFileStep) catch unreachable;
531 write_file_step.* = WriteFileStep.init(self);
532 return write_file_step;
533 }
534
535 pub fn addLog(self: *Builder, comptime format: []const u8, args: anytype) *LogStep {
536 const data = self.fmt(format, args);
537 const log_step = self.allocator.create(LogStep) catch unreachable;
538 log_step.* = LogStep.init(self, data);
539 return log_step;
540 }
541
542 pub fn addRemoveDirTree(self: *Builder, dir_path: []const u8) *RemoveDirStep {
543 const remove_dir_step = self.allocator.create(RemoveDirStep) catch unreachable;
544 remove_dir_step.* = RemoveDirStep.init(self, dir_path);
545 return remove_dir_step;
546 }
547
548 pub fn addFmt(self: *Builder, paths: []const []const u8) *FmtStep {
549 return FmtStep.create(self, paths);
550 }
551
552 pub fn addTranslateC(self: *Builder, source: FileSource) *TranslateCStep {
553 return TranslateCStep.create(self, source.dupe(self));
554 }
555
556 pub fn version(self: *const Builder, major: u32, minor: u32, patch: u32) LibExeObjStep.SharedLibKind {
557 _ = self;
558 return .{
559 .versioned = .{
560 .major = major,
561 .minor = minor,
562 .patch = patch,
563 },
564 };
565 }
566
567 pub fn make(self: *Builder, step_names: []const []const u8) !void {
568 try self.makePath(self.cache_root);
569
570 var wanted_steps = ArrayList(*Step).init(self.allocator);
571 defer wanted_steps.deinit();
572
573 if (step_names.len == 0) {
574 try wanted_steps.append(self.default_step);
575 } else {
576 for (step_names) |step_name| {
577 const s = try self.getTopLevelStepByName(step_name);
578 try wanted_steps.append(s);
579 }
580 }
581
582 for (wanted_steps.items) |s| {
583 try self.makeOneStep(s);
584 }
585 }
586
587 pub fn getInstallStep(self: *Builder) *Step {
588 return &self.install_tls.step;
589 }
590
591 pub fn getUninstallStep(self: *Builder) *Step {
592 return &self.uninstall_tls.step;
593 }
594
595 fn makeUninstall(uninstall_step: *Step) anyerror!void {
596 const uninstall_tls = @fieldParentPtr(TopLevelStep, "step", uninstall_step);
597 const self = @fieldParentPtr(Builder, "uninstall_tls", uninstall_tls);
598
599 for (self.installed_files.items) |installed_file| {
600 const full_path = self.getInstallPath(installed_file.dir, installed_file.path);
601 if (self.verbose) {
602 log.info("rm {s}", .{full_path});
603 }
604 fs.cwd().deleteTree(full_path) catch {};
605 }
606
607 // TODO remove empty directories
608 }
609
610 fn makeOneStep(self: *Builder, s: *Step) anyerror!void {
611 if (s.loop_flag) {
612 log.err("Dependency loop detected:\n {s}", .{s.name});
613 return error.DependencyLoopDetected;
614 }
615 s.loop_flag = true;
616
617 for (s.dependencies.items) |dep| {
618 self.makeOneStep(dep) catch |err| {
619 if (err == error.DependencyLoopDetected) {
620 log.err(" {s}", .{s.name});
621 }
622 return err;
623 };
624 }
625
626 s.loop_flag = false;
627
628 try s.make();
629 }
630
631 fn getTopLevelStepByName(self: *Builder, name: []const u8) !*Step {
632 for (self.top_level_steps.items) |top_level_step| {
633 if (mem.eql(u8, top_level_step.step.name, name)) {
634 return &top_level_step.step;
635 }
636 }
637 log.err("Cannot run step '{s}' because it does not exist", .{name});
638 return error.InvalidStepName;
639 }
640
641 pub fn option(self: *Builder, comptime T: type, name_raw: []const u8, description_raw: []const u8) ?T {
642 const name = self.dupe(name_raw);
643 const description = self.dupe(description_raw);
644 const type_id = comptime typeToEnum(T);
645 const enum_options = if (type_id == .@"enum") blk: {
646 const fields = comptime std.meta.fields(T);
647 var options = ArrayList([]const u8).initCapacity(self.allocator, fields.len) catch unreachable;
648
649 inline for (fields) |field| {
650 options.appendAssumeCapacity(field.name);
651 }
652
653 break :blk options.toOwnedSlice() catch unreachable;
654 } else null;
655 const available_option = AvailableOption{
656 .name = name,
657 .type_id = type_id,
658 .description = description,
659 .enum_options = enum_options,
660 };
661 if ((self.available_options_map.fetchPut(name, available_option) catch unreachable) != null) {
662 panic("Option '{s}' declared twice", .{name});
663 }
664 self.available_options_list.append(available_option) catch unreachable;
665
666 const option_ptr = self.user_input_options.getPtr(name) orelse return null;
667 option_ptr.used = true;
668 switch (type_id) {
669 .bool => switch (option_ptr.value) {
670 .flag => return true,
671 .scalar => |s| {
672 if (mem.eql(u8, s, "true")) {
673 return true;
674 } else if (mem.eql(u8, s, "false")) {
675 return false;
676 } else {
677 log.err("Expected -D{s} to be a boolean, but received '{s}'\n", .{ name, s });
678 self.markInvalidUserInput();
679 return null;
680 }
681 },
682 .list => {
683 log.err("Expected -D{s} to be a boolean, but received a list.\n", .{name});
684 self.markInvalidUserInput();
685 return null;
686 },
687 },
688 .int => switch (option_ptr.value) {
689 .flag => {
690 log.err("Expected -D{s} to be an integer, but received a boolean.\n", .{name});
691 self.markInvalidUserInput();
692 return null;
693 },
694 .scalar => |s| {
695 const n = std.fmt.parseInt(T, s, 10) catch |err| switch (err) {
696 error.Overflow => {
697 log.err("-D{s} value {s} cannot fit into type {s}.\n", .{ name, s, @typeName(T) });
698 self.markInvalidUserInput();
699 return null;
700 },
701 else => {
702 log.err("Expected -D{s} to be an integer of type {s}.\n", .{ name, @typeName(T) });
703 self.markInvalidUserInput();
704 return null;
705 },
706 };
707 return n;
708 },
709 .list => {
710 log.err("Expected -D{s} to be an integer, but received a list.\n", .{name});
711 self.markInvalidUserInput();
712 return null;
713 },
714 },
715 .float => switch (option_ptr.value) {
716 .flag => {
717 log.err("Expected -D{s} to be a float, but received a boolean.\n", .{name});
718 self.markInvalidUserInput();
719 return null;
720 },
721 .scalar => |s| {
722 const n = std.fmt.parseFloat(T, s) catch {
723 log.err("Expected -D{s} to be a float of type {s}.\n", .{ name, @typeName(T) });
724 self.markInvalidUserInput();
725 return null;
726 };
727 return n;
728 },
729 .list => {
730 log.err("Expected -D{s} to be a float, but received a list.\n", .{name});
731 self.markInvalidUserInput();
732 return null;
733 },
734 },
735 .@"enum" => switch (option_ptr.value) {
736 .flag => {
737 log.err("Expected -D{s} to be a string, but received a boolean.\n", .{name});
738 self.markInvalidUserInput();
739 return null;
740 },
741 .scalar => |s| {
742 if (std.meta.stringToEnum(T, s)) |enum_lit| {
743 return enum_lit;
744 } else {
745 log.err("Expected -D{s} to be of type {s}.\n", .{ name, @typeName(T) });
746 self.markInvalidUserInput();
747 return null;
748 }
749 },
750 .list => {
751 log.err("Expected -D{s} to be a string, but received a list.\n", .{name});
752 self.markInvalidUserInput();
753 return null;
754 },
755 },
756 .string => switch (option_ptr.value) {
757 .flag => {
758 log.err("Expected -D{s} to be a string, but received a boolean.\n", .{name});
759 self.markInvalidUserInput();
760 return null;
761 },
762 .list => {
763 log.err("Expected -D{s} to be a string, but received a list.\n", .{name});
764 self.markInvalidUserInput();
765 return null;
766 },
767 .scalar => |s| return s,
768 },
769 .list => switch (option_ptr.value) {
770 .flag => {
771 log.err("Expected -D{s} to be a list, but received a boolean.\n", .{name});
772 self.markInvalidUserInput();
773 return null;
774 },
775 .scalar => |s| {
776 return self.allocator.dupe([]const u8, &[_][]const u8{s}) catch unreachable;
777 },
778 .list => |lst| return lst.items,
779 },
780 }
781 }
782
783 pub fn step(self: *Builder, name: []const u8, description: []const u8) *Step {
784 const step_info = self.allocator.create(TopLevelStep) catch unreachable;
785 step_info.* = TopLevelStep{
786 .step = Step.initNoOp(.top_level, name, self.allocator),
787 .description = self.dupe(description),
788 };
789 self.top_level_steps.append(step_info) catch unreachable;
790 return &step_info.step;
791 }
792
793 /// This provides the -Drelease option to the build user and does not give them the choice.
794 pub fn setPreferredReleaseMode(self: *Builder, mode: std.builtin.Mode) void {
795 if (self.release_mode != null) {
796 @panic("setPreferredReleaseMode must be called before standardReleaseOptions and may not be called twice");
797 }
798 const description = self.fmt("Create a release build ({s})", .{@tagName(mode)});
799 self.is_release = self.option(bool, "release", description) orelse false;
800 self.release_mode = if (self.is_release) mode else std.builtin.Mode.Debug;
801 }
802
803 /// If you call this without first calling `setPreferredReleaseMode` then it gives the build user
804 /// the choice of what kind of release.
805 pub fn standardReleaseOptions(self: *Builder) std.builtin.Mode {
806 if (self.release_mode) |mode| return mode;
807
808 const release_safe = self.option(bool, "release-safe", "Optimizations on and safety on") orelse false;
809 const release_fast = self.option(bool, "release-fast", "Optimizations on and safety off") orelse false;
810 const release_small = self.option(bool, "release-small", "Size optimizations on and safety off") orelse false;
811
812 const mode = if (release_safe and !release_fast and !release_small)
813 std.builtin.Mode.ReleaseSafe
814 else if (release_fast and !release_safe and !release_small)
815 std.builtin.Mode.ReleaseFast
816 else if (release_small and !release_fast and !release_safe)
817 std.builtin.Mode.ReleaseSmall
818 else if (!release_fast and !release_safe and !release_small)
819 std.builtin.Mode.Debug
820 else x: {
821 log.err("Multiple release modes (of -Drelease-safe, -Drelease-fast and -Drelease-small)\n", .{});
822 self.markInvalidUserInput();
823 break :x std.builtin.Mode.Debug;
824 };
825 self.is_release = mode != .Debug;
826 self.release_mode = mode;
827 return mode;
828 }
829
830 pub const StandardTargetOptionsArgs = struct {
831 whitelist: ?[]const CrossTarget = null,
832
833 default_target: CrossTarget = CrossTarget{},
834 };
835
836 /// Exposes standard `zig build` options for choosing a target.
837 pub fn standardTargetOptions(self: *Builder, args: StandardTargetOptionsArgs) CrossTarget {
838 const maybe_triple = self.option(
839 []const u8,
840 "target",
841 "The CPU architecture, OS, and ABI to build for",
842 );
843 const mcpu = self.option([]const u8, "cpu", "Target CPU features to add or subtract");
844
845 if (maybe_triple == null and mcpu == null) {
846 return args.default_target;
847 }
848
849 const triple = maybe_triple orelse "native";
850
851 var diags: CrossTarget.ParseOptions.Diagnostics = .{};
852 const selected_target = CrossTarget.parse(.{
853 .arch_os_abi = triple,
854 .cpu_features = mcpu,
855 .diagnostics = &diags,
856 }) catch |err| switch (err) {
857 error.UnknownCpuModel => {
858 log.err("Unknown CPU: '{s}'\nAvailable CPUs for architecture '{s}':", .{
859 diags.cpu_name.?,
860 @tagName(diags.arch.?),
861 });
862 for (diags.arch.?.allCpuModels()) |cpu| {
863 log.err(" {s}", .{cpu.name});
864 }
865 self.markInvalidUserInput();
866 return args.default_target;
867 },
868 error.UnknownCpuFeature => {
869 log.err(
870 \\Unknown CPU feature: '{s}'
871 \\Available CPU features for architecture '{s}':
872 \\
873 , .{
874 diags.unknown_feature_name.?,
875 @tagName(diags.arch.?),
876 });
877 for (diags.arch.?.allFeaturesList()) |feature| {
878 log.err(" {s}: {s}", .{ feature.name, feature.description });
879 }
880 self.markInvalidUserInput();
881 return args.default_target;
882 },
883 error.UnknownOperatingSystem => {
884 log.err(
885 \\Unknown OS: '{s}'
886 \\Available operating systems:
887 \\
888 , .{diags.os_name.?});
889 inline for (std.meta.fields(std.Target.Os.Tag)) |field| {
890 log.err(" {s}", .{field.name});
891 }
892 self.markInvalidUserInput();
893 return args.default_target;
894 },
895 else => |e| {
896 log.err("Unable to parse target '{s}': {s}\n", .{ triple, @errorName(e) });
897 self.markInvalidUserInput();
898 return args.default_target;
899 },
900 };
901
902 const selected_canonicalized_triple = selected_target.zigTriple(self.allocator) catch unreachable;
903
904 if (args.whitelist) |list| whitelist_check: {
905 // Make sure it's a match of one of the list.
906 var mismatch_triple = true;
907 var mismatch_cpu_features = true;
908 var whitelist_item = CrossTarget{};
909 for (list) |t| {
910 mismatch_cpu_features = true;
911 mismatch_triple = true;
912
913 const t_triple = t.zigTriple(self.allocator) catch unreachable;
914 if (mem.eql(u8, t_triple, selected_canonicalized_triple)) {
915 mismatch_triple = false;
916 whitelist_item = t;
917 if (t.getCpuFeatures().isSuperSetOf(selected_target.getCpuFeatures())) {
918 mismatch_cpu_features = false;
919 break :whitelist_check;
920 } else {
921 break;
922 }
923 }
924 }
925 if (mismatch_triple) {
926 log.err("Chosen target '{s}' does not match one of the supported targets:", .{
927 selected_canonicalized_triple,
928 });
929 for (list) |t| {
930 const t_triple = t.zigTriple(self.allocator) catch unreachable;
931 log.err(" {s}", .{t_triple});
932 }
933 } else {
934 assert(mismatch_cpu_features);
935 const whitelist_cpu = whitelist_item.getCpu();
936 const selected_cpu = selected_target.getCpu();
937 log.err("Chosen CPU model '{s}' does not match one of the supported targets:", .{
938 selected_cpu.model.name,
939 });
940 log.err(" Supported feature Set: ", .{});
941 const all_features = whitelist_cpu.arch.allFeaturesList();
942 var populated_cpu_features = whitelist_cpu.model.features;
943 populated_cpu_features.populateDependencies(all_features);
944 for (all_features) |feature, i_usize| {
945 const i = @intCast(std.Target.Cpu.Feature.Set.Index, i_usize);
946 const in_cpu_set = populated_cpu_features.isEnabled(i);
947 if (in_cpu_set) {
948 log.err("{s} ", .{feature.name});
949 }
950 }
951 log.err(" Remove: ", .{});
952 for (all_features) |feature, i_usize| {
953 const i = @intCast(std.Target.Cpu.Feature.Set.Index, i_usize);
954 const in_cpu_set = populated_cpu_features.isEnabled(i);
955 const in_actual_set = selected_cpu.features.isEnabled(i);
956 if (in_actual_set and !in_cpu_set) {
957 log.err("{s} ", .{feature.name});
958 }
959 }
960 }
961 self.markInvalidUserInput();
962 return args.default_target;
963 }
964
965 return selected_target;
966 }
967
968 pub fn addUserInputOption(self: *Builder, name_raw: []const u8, value_raw: []const u8) !bool {
969 const name = self.dupe(name_raw);
970 const value = self.dupe(value_raw);
971 const gop = try self.user_input_options.getOrPut(name);
972 if (!gop.found_existing) {
973 gop.value_ptr.* = UserInputOption{
974 .name = name,
975 .value = .{ .scalar = value },
976 .used = false,
977 };
978 return false;
979 }
980
981 // option already exists
982 switch (gop.value_ptr.value) {
983 .scalar => |s| {
984 // turn it into a list
985 var list = ArrayList([]const u8).init(self.allocator);
986 list.append(s) catch unreachable;
987 list.append(value) catch unreachable;
988 self.user_input_options.put(name, .{
989 .name = name,
990 .value = .{ .list = list },
991 .used = false,
992 }) catch unreachable;
993 },
994 .list => |*list| {
995 // append to the list
996 list.append(value) catch unreachable;
997 self.user_input_options.put(name, .{
998 .name = name,
999 .value = .{ .list = list.* },
1000 .used = false,
1001 }) catch unreachable;
1002 },
1003 .flag => {
1004 log.warn("Option '-D{s}={s}' conflicts with flag '-D{s}'.", .{ name, value, name });
1005 return true;
1006 },
1007 }
1008 return false;
1009 }
1010
1011 pub fn addUserInputFlag(self: *Builder, name_raw: []const u8) !bool {
1012 const name = self.dupe(name_raw);
1013 const gop = try self.user_input_options.getOrPut(name);
1014 if (!gop.found_existing) {
1015 gop.value_ptr.* = .{
1016 .name = name,
1017 .value = .{ .flag = {} },
1018 .used = false,
1019 };
1020 return false;
1021 }
1022
1023 // option already exists
1024 switch (gop.value_ptr.value) {
1025 .scalar => |s| {
1026 log.err("Flag '-D{s}' conflicts with option '-D{s}={s}'.", .{ name, name, s });
1027 return true;
1028 },
1029 .list => {
1030 log.err("Flag '-D{s}' conflicts with multiple options of the same name.", .{name});
1031 return true;
1032 },
1033 .flag => {},
1034 }
1035 return false;
1036 }
1037
1038 fn typeToEnum(comptime T: type) TypeId {
1039 return switch (@typeInfo(T)) {
1040 .Int => .int,
1041 .Float => .float,
1042 .Bool => .bool,
1043 .Enum => .@"enum",
1044 else => switch (T) {
1045 []const u8 => .string,
1046 []const []const u8 => .list,
1047 else => @compileError("Unsupported type: " ++ @typeName(T)),
1048 },
1049 };
1050 }
1051
1052 fn markInvalidUserInput(self: *Builder) void {
1053 self.invalid_user_input = true;
1054 }
1055
1056 pub fn validateUserInputDidItFail(self: *Builder) bool {
1057 // make sure all args are used
1058 var it = self.user_input_options.iterator();
1059 while (it.next()) |entry| {
1060 if (!entry.value_ptr.used) {
1061 log.err("Invalid option: -D{s}\n", .{entry.key_ptr.*});
1062 self.markInvalidUserInput();
1063 }
1064 }
1065
1066 return self.invalid_user_input;
1067 }
1068
1069 pub fn spawnChild(self: *Builder, argv: []const []const u8) !void {
1070 return self.spawnChildEnvMap(null, self.env_map, argv);
1071 }
1072
1073 fn printCmd(cwd: ?[]const u8, argv: []const []const u8) void {
1074 if (cwd) |yes_cwd| std.debug.print("cd {s} && ", .{yes_cwd});
1075 for (argv) |arg| {
1076 std.debug.print("{s} ", .{arg});
1077 }
1078 std.debug.print("\n", .{});
1079 }
1080
1081 pub fn spawnChildEnvMap(self: *Builder, cwd: ?[]const u8, env_map: *const EnvMap, argv: []const []const u8) !void {
1082 if (self.verbose) {
1083 printCmd(cwd, argv);
1084 }
1085
1086 if (!std.process.can_spawn)
1087 return error.ExecNotSupported;
1088
1089 var child = std.ChildProcess.init(argv, self.allocator);
1090 child.cwd = cwd;
1091 child.env_map = env_map;
1092
1093 const term = child.spawnAndWait() catch |err| {
1094 log.err("Unable to spawn {s}: {s}", .{ argv[0], @errorName(err) });
1095 return err;
1096 };
1097
1098 switch (term) {
1099 .Exited => |code| {
1100 if (code != 0) {
1101 log.err("The following command exited with error code {}:", .{code});
1102 printCmd(cwd, argv);
1103 return error.UncleanExit;
1104 }
1105 },
1106 else => {
1107 log.err("The following command terminated unexpectedly:", .{});
1108 printCmd(cwd, argv);
1109
1110 return error.UncleanExit;
1111 },
1112 }
1113 }
1114
1115 pub fn makePath(self: *Builder, path: []const u8) !void {
1116 fs.cwd().makePath(self.pathFromRoot(path)) catch |err| {
1117 log.err("Unable to create path {s}: {s}", .{ path, @errorName(err) });
1118 return err;
1119 };
1120 }
1121
1122 pub fn installArtifact(self: *Builder, artifact: *LibExeObjStep) void {
1123 self.getInstallStep().dependOn(&self.addInstallArtifact(artifact).step);
1124 }
1125
1126 pub fn addInstallArtifact(self: *Builder, artifact: *LibExeObjStep) *InstallArtifactStep {
1127 return InstallArtifactStep.create(self, artifact);
1128 }
1129
1130 ///`dest_rel_path` is relative to prefix path
1131 pub fn installFile(self: *Builder, src_path: []const u8, dest_rel_path: []const u8) void {
1132 self.getInstallStep().dependOn(&self.addInstallFileWithDir(.{ .path = src_path }, .prefix, dest_rel_path).step);
1133 }
1134
1135 pub fn installDirectory(self: *Builder, options: InstallDirectoryOptions) void {
1136 self.getInstallStep().dependOn(&self.addInstallDirectory(options).step);
1137 }
1138
1139 ///`dest_rel_path` is relative to bin path
1140 pub fn installBinFile(self: *Builder, src_path: []const u8, dest_rel_path: []const u8) void {
1141 self.getInstallStep().dependOn(&self.addInstallFileWithDir(.{ .path = src_path }, .bin, dest_rel_path).step);
1142 }
1143
1144 ///`dest_rel_path` is relative to lib path
1145 pub fn installLibFile(self: *Builder, src_path: []const u8, dest_rel_path: []const u8) void {
1146 self.getInstallStep().dependOn(&self.addInstallFileWithDir(.{ .path = src_path }, .lib, dest_rel_path).step);
1147 }
1148
1149 /// Output format (BIN vs Intel HEX) determined by filename
1150 pub fn installRaw(self: *Builder, artifact: *LibExeObjStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep {
1151 const raw = self.addInstallRaw(artifact, dest_filename, options);
1152 self.getInstallStep().dependOn(&raw.step);
1153 return raw;
1154 }
1155
1156 ///`dest_rel_path` is relative to install prefix path
1157 pub fn addInstallFile(self: *Builder, source: FileSource, dest_rel_path: []const u8) *InstallFileStep {
1158 return self.addInstallFileWithDir(source.dupe(self), .prefix, dest_rel_path);
1159 }
1160
1161 ///`dest_rel_path` is relative to bin path
1162 pub fn addInstallBinFile(self: *Builder, source: FileSource, dest_rel_path: []const u8) *InstallFileStep {
1163 return self.addInstallFileWithDir(source.dupe(self), .bin, dest_rel_path);
1164 }
1165
1166 ///`dest_rel_path` is relative to lib path
1167 pub fn addInstallLibFile(self: *Builder, source: FileSource, dest_rel_path: []const u8) *InstallFileStep {
1168 return self.addInstallFileWithDir(source.dupe(self), .lib, dest_rel_path);
1169 }
1170
1171 pub fn addInstallHeaderFile(b: *Builder, src_path: []const u8, dest_rel_path: []const u8) *InstallFileStep {
1172 return b.addInstallFileWithDir(.{ .path = src_path }, .header, dest_rel_path);
1173 }
1174
1175 pub fn addInstallRaw(self: *Builder, artifact: *LibExeObjStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep {
1176 return InstallRawStep.create(self, artifact, dest_filename, options);
1177 }
1178
1179 pub fn addInstallFileWithDir(
1180 self: *Builder,
1181 source: FileSource,
1182 install_dir: InstallDir,
1183 dest_rel_path: []const u8,
1184 ) *InstallFileStep {
1185 if (dest_rel_path.len == 0) {
1186 panic("dest_rel_path must be non-empty", .{});
1187 }
1188 const install_step = self.allocator.create(InstallFileStep) catch unreachable;
1189 install_step.* = InstallFileStep.init(self, source.dupe(self), install_dir, dest_rel_path);
1190 return install_step;
1191 }
1192
1193 pub fn addInstallDirectory(self: *Builder, options: InstallDirectoryOptions) *InstallDirStep {
1194 const install_step = self.allocator.create(InstallDirStep) catch unreachable;
1195 install_step.* = InstallDirStep.init(self, options);
1196 return install_step;
1197 }
1198
1199 pub fn pushInstalledFile(self: *Builder, dir: InstallDir, dest_rel_path: []const u8) void {
1200 const file = InstalledFile{
1201 .dir = dir,
1202 .path = dest_rel_path,
1203 };
1204 self.installed_files.append(file.dupe(self)) catch unreachable;
1205 }
1206
1207 pub fn updateFile(self: *Builder, source_path: []const u8, dest_path: []const u8) !void {
1208 if (self.verbose) {
1209 log.info("cp {s} {s} ", .{ source_path, dest_path });
1210 }
1211 const cwd = fs.cwd();
1212 const prev_status = try fs.Dir.updateFile(cwd, source_path, cwd, dest_path, .{});
1213 if (self.verbose) switch (prev_status) {
1214 .stale => log.info("# installed", .{}),
1215 .fresh => log.info("# up-to-date", .{}),
1216 };
1217 }
1218
1219 pub fn truncateFile(self: *Builder, dest_path: []const u8) !void {
1220 if (self.verbose) {
1221 log.info("truncate {s}", .{dest_path});
1222 }
1223 const cwd = fs.cwd();
1224 var src_file = cwd.createFile(dest_path, .{}) catch |err| switch (err) {
1225 error.FileNotFound => blk: {
1226 if (fs.path.dirname(dest_path)) |dirname| {
1227 try cwd.makePath(dirname);
1228 }
1229 break :blk try cwd.createFile(dest_path, .{});
1230 },
1231 else => |e| return e,
1232 };
1233 src_file.close();
1234 }
1235
1236 pub fn pathFromRoot(self: *Builder, rel_path: []const u8) []u8 {
1237 return fs.path.resolve(self.allocator, &[_][]const u8{ self.build_root, rel_path }) catch unreachable;
1238 }
1239
1240 /// Shorthand for `std.fs.path.join(builder.allocator, paths) catch unreachable`
1241 pub fn pathJoin(self: *Builder, paths: []const []const u8) []u8 {
1242 return fs.path.join(self.allocator, paths) catch unreachable;
1243 }
1244
1245 pub fn fmt(self: *Builder, comptime format: []const u8, args: anytype) []u8 {
1246 return fmt_lib.allocPrint(self.allocator, format, args) catch unreachable;
1247 }
1248
1249 pub fn findProgram(self: *Builder, names: []const []const u8, paths: []const []const u8) ![]const u8 {
1250 // TODO report error for ambiguous situations
1251 const exe_extension = @as(CrossTarget, .{}).exeFileExt();
1252 for (self.search_prefixes.items) |search_prefix| {
1253 for (names) |name| {
1254 if (fs.path.isAbsolute(name)) {
1255 return name;
1256 }
1257 const full_path = self.pathJoin(&.{
1258 search_prefix,
1259 "bin",
1260 self.fmt("{s}{s}", .{ name, exe_extension }),
1261 });
1262 return fs.realpathAlloc(self.allocator, full_path) catch continue;
1263 }
1264 }
1265 if (self.env_map.get("PATH")) |PATH| {
1266 for (names) |name| {
1267 if (fs.path.isAbsolute(name)) {
1268 return name;
1269 }
1270 var it = mem.tokenize(u8, PATH, &[_]u8{fs.path.delimiter});
1271 while (it.next()) |path| {
1272 const full_path = self.pathJoin(&.{
1273 path,
1274 self.fmt("{s}{s}", .{ name, exe_extension }),
1275 });
1276 return fs.realpathAlloc(self.allocator, full_path) catch continue;
1277 }
1278 }
1279 }
1280 for (names) |name| {
1281 if (fs.path.isAbsolute(name)) {
1282 return name;
1283 }
1284 for (paths) |path| {
1285 const full_path = self.pathJoin(&.{
1286 path,
1287 self.fmt("{s}{s}", .{ name, exe_extension }),
1288 });
1289 return fs.realpathAlloc(self.allocator, full_path) catch continue;
1290 }
1291 }
1292 return error.FileNotFound;
1293 }
1294
1295 pub fn execAllowFail(
1296 self: *Builder,
1297 argv: []const []const u8,
1298 out_code: *u8,
1299 stderr_behavior: std.ChildProcess.StdIo,
1300 ) ExecError![]u8 {
1301 assert(argv.len != 0);
1302
1303 if (!std.process.can_spawn)
1304 return error.ExecNotSupported;
1305
1306 const max_output_size = 400 * 1024;
1307 var child = std.ChildProcess.init(argv, self.allocator);
1308 child.stdin_behavior = .Ignore;
1309 child.stdout_behavior = .Pipe;
1310 child.stderr_behavior = stderr_behavior;
1311 child.env_map = self.env_map;
1312
1313 try child.spawn();
1314
1315 const stdout = child.stdout.?.reader().readAllAlloc(self.allocator, max_output_size) catch {
1316 return error.ReadFailure;
1317 };
1318 errdefer self.allocator.free(stdout);
1319
1320 const term = try child.wait();
1321 switch (term) {
1322 .Exited => |code| {
1323 if (code != 0) {
1324 out_code.* = @truncate(u8, code);
1325 return error.ExitCodeFailure;
1326 }
1327 return stdout;
1328 },
1329 .Signal, .Stopped, .Unknown => |code| {
1330 out_code.* = @truncate(u8, code);
1331 return error.ProcessTerminated;
1332 },
1333 }
1334 }
1335
1336 pub fn execFromStep(self: *Builder, argv: []const []const u8, src_step: ?*Step) ![]u8 {
1337 assert(argv.len != 0);
1338
1339 if (self.verbose) {
1340 printCmd(null, argv);
1341 }
1342
1343 if (!std.process.can_spawn) {
1344 if (src_step) |s| log.err("{s}...", .{s.name});
1345 log.err("Unable to spawn the following command: cannot spawn child process", .{});
1346 printCmd(null, argv);
1347 std.os.abort();
1348 }
1349
1350 var code: u8 = undefined;
1351 return self.execAllowFail(argv, &code, .Inherit) catch |err| switch (err) {
1352 error.ExecNotSupported => {
1353 if (src_step) |s| log.err("{s}...", .{s.name});
1354 log.err("Unable to spawn the following command: cannot spawn child process", .{});
1355 printCmd(null, argv);
1356 std.os.abort();
1357 },
1358 error.FileNotFound => {
1359 if (src_step) |s| log.err("{s}...", .{s.name});
1360 log.err("Unable to spawn the following command: file not found", .{});
1361 printCmd(null, argv);
1362 std.os.exit(@truncate(u8, code));
1363 },
1364 error.ExitCodeFailure => {
1365 if (src_step) |s| log.err("{s}...", .{s.name});
1366 if (self.prominent_compile_errors) {
1367 log.err("The step exited with error code {d}", .{code});
1368 } else {
1369 log.err("The following command exited with error code {d}:", .{code});
1370 printCmd(null, argv);
1371 }
1372
1373 std.os.exit(@truncate(u8, code));
1374 },
1375 error.ProcessTerminated => {
1376 if (src_step) |s| log.err("{s}...", .{s.name});
1377 log.err("The following command terminated unexpectedly:", .{});
1378 printCmd(null, argv);
1379 std.os.exit(@truncate(u8, code));
1380 },
1381 else => |e| return e,
1382 };
1383 }
1384
1385 pub fn exec(self: *Builder, argv: []const []const u8) ![]u8 {
1386 return self.execFromStep(argv, null);
1387 }
1388
1389 pub fn addSearchPrefix(self: *Builder, search_prefix: []const u8) void {
1390 self.search_prefixes.append(self.dupePath(search_prefix)) catch unreachable;
1391 }
1392
1393 pub fn getInstallPath(self: *Builder, dir: InstallDir, dest_rel_path: []const u8) []const u8 {
1394 assert(!fs.path.isAbsolute(dest_rel_path)); // Install paths must be relative to the prefix
1395 const base_dir = switch (dir) {
1396 .prefix => self.install_path,
1397 .bin => self.exe_dir,
1398 .lib => self.lib_dir,
1399 .header => self.h_dir,
1400 .custom => |path| self.pathJoin(&.{ self.install_path, path }),
1401 };
1402 return fs.path.resolve(
1403 self.allocator,
1404 &[_][]const u8{ base_dir, dest_rel_path },
1405 ) catch unreachable;
1406 }
1407
1408 pub const Dependency = struct {
1409 builder: *Builder,
1410
1411 pub fn artifact(d: *Dependency, name: []const u8) *LibExeObjStep {
1412 var found: ?*LibExeObjStep = null;
1413 for (d.builder.install_tls.step.dependencies.items) |dep_step| {
1414 const inst = dep_step.cast(InstallArtifactStep) orelse continue;
1415 if (mem.eql(u8, inst.artifact.name, name)) {
1416 if (found != null) panic("artifact name '{s}' is ambiguous", .{name});
1417 found = inst.artifact;
1418 }
1419 }
1420 return found orelse {
1421 for (d.builder.install_tls.step.dependencies.items) |dep_step| {
1422 const inst = dep_step.cast(InstallArtifactStep) orelse continue;
1423 log.info("available artifact: '{s}'", .{inst.artifact.name});
1424 }
1425 panic("unable to find artifact '{s}'", .{name});
1426 };
1427 }
1428 };
1429
1430 pub fn dependency(b: *Builder, name: []const u8, args: anytype) *Dependency {
1431 const build_runner = @import("root");
1432 const deps = build_runner.dependencies;
1433
1434 inline for (@typeInfo(deps.imports).Struct.decls) |decl| {
1435 if (mem.startsWith(u8, decl.name, b.dep_prefix) and
1436 mem.endsWith(u8, decl.name, name) and
1437 decl.name.len == b.dep_prefix.len + name.len)
1438 {
1439 const build_zig = @field(deps.imports, decl.name);
1440 const build_root = @field(deps.build_root, decl.name);
1441 return dependencyInner(b, name, build_root, build_zig, args);
1442 }
1443 }
1444
1445 const full_path = b.pathFromRoot("build.zig.ini");
1446 std.debug.print("no dependency named '{s}' in '{s}'\n", .{ name, full_path });
1447 std.process.exit(1);
1448 }
1449
1450 fn dependencyInner(
1451 b: *Builder,
1452 name: []const u8,
1453 build_root: []const u8,
1454 comptime build_zig: type,
1455 args: anytype,
1456 ) *Dependency {
1457 const sub_builder = b.createChild(name, build_root, args) catch unreachable;
1458 sub_builder.runBuild(build_zig) catch unreachable;
1459 const dep = b.allocator.create(Dependency) catch unreachable;
1460 dep.* = .{ .builder = sub_builder };
1461 return dep;
1462 }
1463
1464 pub fn runBuild(b: *Builder, build_zig: anytype) anyerror!void {
1465 switch (@typeInfo(@typeInfo(@TypeOf(build_zig.build)).Fn.return_type.?)) {
1466 .Void => build_zig.build(b),
1467 .ErrorUnion => try build_zig.build(b),
1468 else => @compileError("expected return type of build to be 'void' or '!void'"),
1469 }
1470 }
1471};
1472
1473test "builder.findProgram compiles" {
1474 if (builtin.os.tag == .wasi) return error.SkipZigTest;
1475
1476 var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
1477 defer arena.deinit();
1478
1479 const builder = try Builder.create(
1480 arena.allocator(),
1481 "zig",
1482 "zig-cache",
1483 "zig-cache",
1484 "zig-cache",
1485 );
1486 defer builder.destroy();
1487 _ = builder.findProgram(&[_][]const u8{}, &[_][]const u8{}) catch null;
1488}
1489
1490pub const Pkg = struct {
1491 name: []const u8,
1492 source: FileSource,
1493 dependencies: ?[]const Pkg = null,
1494};
1495
1496/// A file that is generated by a build step.
1497/// This struct is an interface that is meant to be used with `@fieldParentPtr` to implement the actual path logic.
1498pub const GeneratedFile = struct {
1499 /// The step that generates the file
1500 step: *Step,
1501
1502 /// The path to the generated file. Must be either absolute or relative to the build root.
1503 /// This value must be set in the `fn make()` of the `step` and must not be `null` afterwards.
1504 path: ?[]const u8 = null,
1505
1506 pub fn getPath(self: GeneratedFile) []const u8 {
1507 return self.path orelse std.debug.panic(
1508 "getPath() was called on a GeneratedFile that wasn't build yet. Is there a missing Step dependency on step '{s}'?",
1509 .{self.step.name},
1510 );
1511 }
1512};
1513
1514/// A file source is a reference to an existing or future file.
1515///
1516pub const FileSource = union(enum) {
1517 /// A plain file path, relative to build root or absolute.
1518 path: []const u8,
1519
1520 /// A file that is generated by an interface. Those files usually are
1521 /// not available until built by a build step.
1522 generated: *const GeneratedFile,
1523
1524 /// Returns a new file source that will have a relative path to the build root guaranteed.
1525 /// This should be preferred over setting `.path` directly as it documents that the files are in the project directory.
1526 pub fn relative(path: []const u8) FileSource {
1527 std.debug.assert(!std.fs.path.isAbsolute(path));
1528 return FileSource{ .path = path };
1529 }
1530
1531 /// Returns a string that can be shown to represent the file source.
1532 /// Either returns the path or `"generated"`.
1533 pub fn getDisplayName(self: FileSource) []const u8 {
1534 return switch (self) {
1535 .path => self.path,
1536 .generated => "generated",
1537 };
1538 }
1539
1540 /// Adds dependencies this file source implies to the given step.
1541 pub fn addStepDependencies(self: FileSource, step: *Step) void {
1542 switch (self) {
1543 .path => {},
1544 .generated => |gen| step.dependOn(gen.step),
1545 }
1546 }
1547
1548 /// Should only be called during make(), returns a path relative to the build root or absolute.
1549 pub fn getPath(self: FileSource, builder: *Builder) []const u8 {
1550 const path = switch (self) {
1551 .path => |p| builder.pathFromRoot(p),
1552 .generated => |gen| gen.getPath(),
1553 };
1554 return path;
1555 }
1556
1557 /// Duplicates the file source for a given builder.
1558 pub fn dupe(self: FileSource, b: *Builder) FileSource {
1559 return switch (self) {
1560 .path => |p| .{ .path = b.dupePath(p) },
1561 .generated => |gen| .{ .generated = gen },
1562 };
1563 }
1564};
1565
1566/// Allocates a new string for assigning a value to a named macro.
1567/// If the value is omitted, it is set to 1.
1568/// `name` and `value` need not live longer than the function call.
1569pub fn constructCMacro(allocator: Allocator, name: []const u8, value: ?[]const u8) []const u8 {
1570 var macro = allocator.alloc(
1571 u8,
1572 name.len + if (value) |value_slice| value_slice.len + 1 else 0,
1573 ) catch |err| if (err == error.OutOfMemory) @panic("Out of memory") else unreachable;
1574 mem.copy(u8, macro, name);
1575 if (value) |value_slice| {
1576 macro[name.len] = '=';
1577 mem.copy(u8, macro[name.len + 1 ..], value_slice);
1578 }
1579 return macro;
1580}
1581
1582/// deprecated: use `InstallDirStep.Options`
1583pub const InstallDirectoryOptions = InstallDirStep.Options;
1584
1585pub const Step = struct {
1586 id: Id,
1587 name: []const u8,
1588 makeFn: MakeFn,
1589 dependencies: ArrayList(*Step),
1590 loop_flag: bool,
1591 done_flag: bool,
1592
1593 const MakeFn = *const fn (self: *Step) anyerror!void;
1594
1595 pub const Id = enum {
1596 top_level,
1597 lib_exe_obj,
1598 install_artifact,
1599 install_file,
1600 install_dir,
1601 log,
1602 remove_dir,
1603 fmt,
1604 translate_c,
1605 write_file,
1606 run,
1607 emulatable_run,
1608 check_file,
1609 check_object,
1610 config_header,
1611 install_raw,
1612 options,
1613 custom,
1614
1615 pub fn Type(comptime id: Id) type {
1616 return switch (id) {
1617 .top_level => Builder.TopLevelStep,
1618 .lib_exe_obj => LibExeObjStep,
1619 .install_artifact => InstallArtifactStep,
1620 .install_file => InstallFileStep,
1621 .install_dir => InstallDirStep,
1622 .log => LogStep,
1623 .remove_dir => RemoveDirStep,
1624 .fmt => FmtStep,
1625 .translate_c => TranslateCStep,
1626 .write_file => WriteFileStep,
1627 .run => RunStep,
1628 .emulatable_run => EmulatableRunStep,
1629 .check_file => CheckFileStep,
1630 .check_object => CheckObjectStep,
1631 .config_header => ConfigHeaderStep,
1632 .install_raw => InstallRawStep,
1633 .options => OptionsStep,
1634 .custom => @compileError("no type available for custom step"),
1635 };
1636 }
1637 };
1638
1639 pub fn init(id: Id, name: []const u8, allocator: Allocator, makeFn: MakeFn) Step {
1640 return Step{
1641 .id = id,
1642 .name = allocator.dupe(u8, name) catch unreachable,
1643 .makeFn = makeFn,
1644 .dependencies = ArrayList(*Step).init(allocator),
1645 .loop_flag = false,
1646 .done_flag = false,
1647 };
1648 }
1649 pub fn initNoOp(id: Id, name: []const u8, allocator: Allocator) Step {
1650 return init(id, name, allocator, makeNoOp);
1651 }
1652
1653 pub fn make(self: *Step) !void {
1654 if (self.done_flag) return;
1655
1656 try self.makeFn(self);
1657 self.done_flag = true;
1658 }
1659
1660 pub fn dependOn(self: *Step, other: *Step) void {
1661 self.dependencies.append(other) catch unreachable;
1662 }
1663
1664 fn makeNoOp(self: *Step) anyerror!void {
1665 _ = self;
1666 }
1667
1668 pub fn cast(step: *Step, comptime T: type) ?*T {
1669 if (step.id == T.base_id) {
1670 return @fieldParentPtr(T, "step", step);
1671 }
1672 return null;
1673 }
1674};
1675
1676pub const VcpkgRoot = union(VcpkgRootStatus) {
1677 unattempted: void,
1678 not_found: void,
1679 found: []const u8,
1680};
1681
1682pub const VcpkgRootStatus = enum {
1683 unattempted,
1684 not_found,
1685 found,
1686};
1687
1688pub const InstallDir = union(enum) {
1689 prefix: void,
1690 lib: void,
1691 bin: void,
1692 header: void,
1693 /// A path relative to the prefix
1694 custom: []const u8,
1695
1696 /// Duplicates the install directory including the path if set to custom.
1697 pub fn dupe(self: InstallDir, builder: *Builder) InstallDir {
1698 if (self == .custom) {
1699 // Written with this temporary to avoid RLS problems
1700 const duped_path = builder.dupe(self.custom);
1701 return .{ .custom = duped_path };
1702 } else {
1703 return self;
1704 }
1705 }
1706};
1707
1708pub const InstalledFile = struct {
1709 dir: InstallDir,
1710 path: []const u8,
1711
1712 /// Duplicates the installed file path and directory.
1713 pub fn dupe(self: InstalledFile, builder: *Builder) InstalledFile {
1714 return .{
1715 .dir = self.dir.dupe(builder),
1716 .path = builder.dupe(self.path),
1717 };
1718 }
1719};
1720
1721test "dupePkg()" {
1722 if (builtin.os.tag == .wasi) return error.SkipZigTest;
1723
1724 var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
1725 defer arena.deinit();
1726 var builder = try Builder.create(
1727 arena.allocator(),
1728 "test",
1729 "test",
1730 "test",
1731 "test",
1732 );
1733 defer builder.destroy();
1734
1735 var pkg_dep = Pkg{
1736 .name = "pkg_dep",
1737 .source = .{ .path = "/not/a/pkg_dep.zig" },
1738 };
1739 var pkg_top = Pkg{
1740 .name = "pkg_top",
1741 .source = .{ .path = "/not/a/pkg_top.zig" },
1742 .dependencies = &[_]Pkg{pkg_dep},
1743 };
1744 const dupe = builder.dupePkg(pkg_top);
1745
1746 const original_deps = pkg_top.dependencies.?;
1747 const dupe_deps = dupe.dependencies.?;
1748
1749 // probably the same top level package details
1750 try std.testing.expectEqualStrings(pkg_top.name, dupe.name);
1751
1752 // probably the same dependencies
1753 try std.testing.expectEqual(original_deps.len, dupe_deps.len);
1754 try std.testing.expectEqual(original_deps[0].name, pkg_dep.name);
1755
1756 // could segfault otherwise if pointers in duplicated package's fields are
1757 // the same as those in stack allocated package's fields
1758 try std.testing.expect(dupe_deps.ptr != original_deps.ptr);
1759 try std.testing.expect(dupe.name.ptr != pkg_top.name.ptr);
1760 try std.testing.expect(dupe.source.path.ptr != pkg_top.source.path.ptr);
1761 try std.testing.expect(dupe_deps[0].name.ptr != pkg_dep.name.ptr);
1762 try std.testing.expect(dupe_deps[0].source.path.ptr != pkg_dep.source.path.ptr);
1763}
1764
1765test {
1766 _ = CheckFileStep;
1767 _ = CheckObjectStep;
1768 _ = EmulatableRunStep;
1769 _ = FmtStep;
1770 _ = InstallArtifactStep;
1771 _ = InstallDirStep;
1772 _ = InstallFileStep;
1773 _ = InstallRawStep;
1774 _ = LibExeObjStep;
1775 _ = LogStep;
1776 _ = OptionsStep;
1777 _ = RemoveDirStep;
1778 _ = RunStep;
1779 _ = TranslateCStep;
1780 _ = WriteFileStep;
1781}
lib/std/build/CheckFileStep.zig deleted-53
...@@ -1,53 +0,0 @@
1const std = @import("../std.zig");
2const build = std.build;
3const Step = build.Step;
4const Builder = build.Builder;
5const fs = std.fs;
6const mem = std.mem;
7
8const CheckFileStep = @This();
9
10pub const base_id = .check_file;
11
12step: Step,
13builder: *Builder,
14expected_matches: []const []const u8,
15source: build.FileSource,
16max_bytes: usize = 20 * 1024 * 1024,
17
18pub fn create(
19 builder: *Builder,
20 source: build.FileSource,
21 expected_matches: []const []const u8,
22) *CheckFileStep {
23 const self = builder.allocator.create(CheckFileStep) catch unreachable;
24 self.* = CheckFileStep{
25 .builder = builder,
26 .step = Step.init(.check_file, "CheckFile", builder.allocator, make),
27 .source = source.dupe(builder),
28 .expected_matches = builder.dupeStrings(expected_matches),
29 };
30 self.source.addStepDependencies(&self.step);
31 return self;
32}
33
34fn make(step: *Step) !void {
35 const self = @fieldParentPtr(CheckFileStep, "step", step);
36
37 const src_path = self.source.getPath(self.builder);
38 const contents = try fs.cwd().readFileAlloc(self.builder.allocator, src_path, self.max_bytes);
39
40 for (self.expected_matches) |expected_match| {
41 if (mem.indexOf(u8, contents, expected_match) == null) {
42 std.debug.print(
43 \\
44 \\========= Expected to find: ===================
45 \\{s}
46 \\========= But file does not contain it: =======
47 \\{s}
48 \\
49 , .{ expected_match, contents });
50 return error.TestFailed;
51 }
52 }
53}
lib/std/build/CheckObjectStep.zig deleted-1026
...@@ -1,1026 +0,0 @@
1const std = @import("../std.zig");
2const assert = std.debug.assert;
3const build = std.build;
4const fs = std.fs;
5const macho = std.macho;
6const math = std.math;
7const mem = std.mem;
8const testing = std.testing;
9
10const CheckObjectStep = @This();
11
12const Allocator = mem.Allocator;
13const Builder = build.Builder;
14const Step = build.Step;
15const EmulatableRunStep = build.EmulatableRunStep;
16
17pub const base_id = .check_object;
18
19step: Step,
20builder: *Builder,
21source: build.FileSource,
22max_bytes: usize = 20 * 1024 * 1024,
23checks: std.ArrayList(Check),
24dump_symtab: bool = false,
25obj_format: std.Target.ObjectFormat,
26
27pub fn create(builder: *Builder, source: build.FileSource, obj_format: std.Target.ObjectFormat) *CheckObjectStep {
28 const gpa = builder.allocator;
29 const self = gpa.create(CheckObjectStep) catch unreachable;
30 self.* = .{
31 .builder = builder,
32 .step = Step.init(.check_file, "CheckObject", gpa, make),
33 .source = source.dupe(builder),
34 .checks = std.ArrayList(Check).init(gpa),
35 .obj_format = obj_format,
36 };
37 self.source.addStepDependencies(&self.step);
38 return self;
39}
40
41/// Runs and (optionally) compares the output of a binary.
42/// Asserts `self` was generated from an executable step.
43pub fn runAndCompare(self: *CheckObjectStep) *EmulatableRunStep {
44 const dependencies_len = self.step.dependencies.items.len;
45 assert(dependencies_len > 0);
46 const exe_step = self.step.dependencies.items[dependencies_len - 1];
47 const exe = exe_step.cast(std.build.LibExeObjStep).?;
48 const emulatable_step = EmulatableRunStep.create(self.builder, "EmulatableRun", exe);
49 emulatable_step.step.dependOn(&self.step);
50 return emulatable_step;
51}
52
53/// There two types of actions currently suported:
54/// * `.match` - is the main building block of standard matchers with optional eat-all token `{*}`
55/// and extractors by name such as `{n_value}`. Please note this action is very simplistic in nature
56/// i.e., it won't really handle edge cases/nontrivial examples. But given that we do want to use
57/// it mainly to test the output of our object format parser-dumpers when testing the linkers, etc.
58/// it should be plenty useful in its current form.
59/// * `.compute_cmp` - can be used to perform an operation on the extracted global variables
60/// using the MatchAction. It currently only supports an addition. The operation is required
61/// to be specified in Reverse Polish Notation to ease in operator-precedence parsing (well,
62/// to avoid any parsing really).
63/// For example, if the two extracted values were saved as `vmaddr` and `entryoff` respectively
64/// they could then be added with this simple program `vmaddr entryoff +`.
65const Action = struct {
66 tag: enum { match, not_present, compute_cmp },
67 phrase: []const u8,
68 expected: ?ComputeCompareExpected = null,
69
70 /// Will return true if the `phrase` was found in the `haystack`.
71 /// Some examples include:
72 ///
73 /// LC 0 => will match in its entirety
74 /// vmaddr {vmaddr} => will match `vmaddr` and then extract the following value as u64
75 /// and save under `vmaddr` global name (see `global_vars` param)
76 /// name {*}libobjc{*}.dylib => will match `name` followed by a token which contains `libobjc` and `.dylib`
77 /// in that order with other letters in between
78 fn match(act: Action, haystack: []const u8, global_vars: anytype) !bool {
79 assert(act.tag == .match or act.tag == .not_present);
80
81 var candidate_var: ?struct { name: []const u8, value: u64 } = null;
82 var hay_it = mem.tokenize(u8, mem.trim(u8, haystack, " "), " ");
83 var needle_it = mem.tokenize(u8, mem.trim(u8, act.phrase, " "), " ");
84
85 while (needle_it.next()) |needle_tok| {
86 const hay_tok = hay_it.next() orelse return false;
87
88 if (mem.indexOf(u8, needle_tok, "{*}")) |index| {
89 // We have fuzzy matchers within the search pattern, so we match substrings.
90 var start = index;
91 var n_tok = needle_tok;
92 var h_tok = hay_tok;
93 while (true) {
94 n_tok = n_tok[start + 3 ..];
95 const inner = if (mem.indexOf(u8, n_tok, "{*}")) |sub_end|
96 n_tok[0..sub_end]
97 else
98 n_tok;
99 if (mem.indexOf(u8, h_tok, inner) == null) return false;
100 start = mem.indexOf(u8, n_tok, "{*}") orelse break;
101 }
102 } else if (mem.startsWith(u8, needle_tok, "{")) {
103 const closing_brace = mem.indexOf(u8, needle_tok, "}") orelse return error.MissingClosingBrace;
104 if (closing_brace != needle_tok.len - 1) return error.ClosingBraceNotLast;
105
106 const name = needle_tok[1..closing_brace];
107 if (name.len == 0) return error.MissingBraceValue;
108 const value = try std.fmt.parseInt(u64, hay_tok, 16);
109 candidate_var = .{
110 .name = name,
111 .value = value,
112 };
113 } else {
114 if (!mem.eql(u8, hay_tok, needle_tok)) return false;
115 }
116 }
117
118 if (candidate_var) |v| {
119 try global_vars.putNoClobber(v.name, v.value);
120 }
121
122 return true;
123 }
124
125 /// Will return true if the `phrase` is correctly parsed into an RPN program and
126 /// its reduced, computed value compares using `op` with the expected value, either
127 /// a literal or another extracted variable.
128 fn computeCmp(act: Action, gpa: Allocator, global_vars: anytype) !bool {
129 var op_stack = std.ArrayList(enum { add, sub, mod, mul }).init(gpa);
130 var values = std.ArrayList(u64).init(gpa);
131
132 var it = mem.tokenize(u8, act.phrase, " ");
133 while (it.next()) |next| {
134 if (mem.eql(u8, next, "+")) {
135 try op_stack.append(.add);
136 } else if (mem.eql(u8, next, "-")) {
137 try op_stack.append(.sub);
138 } else if (mem.eql(u8, next, "%")) {
139 try op_stack.append(.mod);
140 } else if (mem.eql(u8, next, "*")) {
141 try op_stack.append(.mul);
142 } else {
143 const val = std.fmt.parseInt(u64, next, 0) catch blk: {
144 break :blk global_vars.get(next) orelse {
145 std.debug.print(
146 \\
147 \\========= Variable was not extracted: ===========
148 \\{s}
149 \\
150 , .{next});
151 return error.UnknownVariable;
152 };
153 };
154 try values.append(val);
155 }
156 }
157
158 var op_i: usize = 1;
159 var reduced: u64 = values.items[0];
160 for (op_stack.items) |op| {
161 const other = values.items[op_i];
162 switch (op) {
163 .add => {
164 reduced += other;
165 },
166 .sub => {
167 reduced -= other;
168 },
169 .mod => {
170 reduced %= other;
171 },
172 .mul => {
173 reduced *= other;
174 },
175 }
176 op_i += 1;
177 }
178
179 const exp_value = switch (act.expected.?.value) {
180 .variable => |name| global_vars.get(name) orelse {
181 std.debug.print(
182 \\
183 \\========= Variable was not extracted: ===========
184 \\{s}
185 \\
186 , .{name});
187 return error.UnknownVariable;
188 },
189 .literal => |x| x,
190 };
191 return math.compare(reduced, act.expected.?.op, exp_value);
192 }
193};
194
195const ComputeCompareExpected = struct {
196 op: math.CompareOperator,
197 value: union(enum) {
198 variable: []const u8,
199 literal: u64,
200 },
201
202 pub fn format(
203 value: @This(),
204 comptime fmt: []const u8,
205 options: std.fmt.FormatOptions,
206 writer: anytype,
207 ) !void {
208 if (fmt.len != 0) std.fmt.invalidFmtError(fmt, value);
209 _ = options;
210 try writer.print("{s} ", .{@tagName(value.op)});
211 switch (value.value) {
212 .variable => |name| try writer.writeAll(name),
213 .literal => |x| try writer.print("{x}", .{x}),
214 }
215 }
216};
217
218const Check = struct {
219 builder: *Builder,
220 actions: std.ArrayList(Action),
221
222 fn create(b: *Builder) Check {
223 return .{
224 .builder = b,
225 .actions = std.ArrayList(Action).init(b.allocator),
226 };
227 }
228
229 fn match(self: *Check, phrase: []const u8) void {
230 self.actions.append(.{
231 .tag = .match,
232 .phrase = self.builder.dupe(phrase),
233 }) catch unreachable;
234 }
235
236 fn notPresent(self: *Check, phrase: []const u8) void {
237 self.actions.append(.{
238 .tag = .not_present,
239 .phrase = self.builder.dupe(phrase),
240 }) catch unreachable;
241 }
242
243 fn computeCmp(self: *Check, phrase: []const u8, expected: ComputeCompareExpected) void {
244 self.actions.append(.{
245 .tag = .compute_cmp,
246 .phrase = self.builder.dupe(phrase),
247 .expected = expected,
248 }) catch unreachable;
249 }
250};
251
252/// Creates a new sequence of actions with `phrase` as the first anchor searched phrase.
253pub fn checkStart(self: *CheckObjectStep, phrase: []const u8) void {
254 var new_check = Check.create(self.builder);
255 new_check.match(phrase);
256 self.checks.append(new_check) catch unreachable;
257}
258
259/// Adds another searched phrase to the latest created Check with `CheckObjectStep.checkStart(...)`.
260/// Asserts at least one check already exists.
261pub fn checkNext(self: *CheckObjectStep, phrase: []const u8) void {
262 assert(self.checks.items.len > 0);
263 const last = &self.checks.items[self.checks.items.len - 1];
264 last.match(phrase);
265}
266
267/// Adds another searched phrase to the latest created Check with `CheckObjectStep.checkStart(...)`
268/// however ensures there is no matching phrase in the output.
269/// Asserts at least one check already exists.
270pub fn checkNotPresent(self: *CheckObjectStep, phrase: []const u8) void {
271 assert(self.checks.items.len > 0);
272 const last = &self.checks.items[self.checks.items.len - 1];
273 last.notPresent(phrase);
274}
275
276/// Creates a new check checking specifically symbol table parsed and dumped from the object
277/// file.
278/// Issuing this check will force parsing and dumping of the symbol table.
279pub fn checkInSymtab(self: *CheckObjectStep) void {
280 self.dump_symtab = true;
281 const symtab_label = switch (self.obj_format) {
282 .macho => MachODumper.symtab_label,
283 else => @panic("TODO other parsers"),
284 };
285 self.checkStart(symtab_label);
286}
287
288/// Creates a new standalone, singular check which allows running simple binary operations
289/// on the extracted variables. It will then compare the reduced program with the value of
290/// the expected variable.
291pub fn checkComputeCompare(
292 self: *CheckObjectStep,
293 program: []const u8,
294 expected: ComputeCompareExpected,
295) void {
296 var new_check = Check.create(self.builder);
297 new_check.computeCmp(program, expected);
298 self.checks.append(new_check) catch unreachable;
299}
300
301fn make(step: *Step) !void {
302 const self = @fieldParentPtr(CheckObjectStep, "step", step);
303
304 const gpa = self.builder.allocator;
305 const src_path = self.source.getPath(self.builder);
306 const contents = try fs.cwd().readFileAllocOptions(
307 gpa,
308 src_path,
309 self.max_bytes,
310 null,
311 @alignOf(u64),
312 null,
313 );
314
315 const output = switch (self.obj_format) {
316 .macho => try MachODumper.parseAndDump(contents, .{
317 .gpa = gpa,
318 .dump_symtab = self.dump_symtab,
319 }),
320 .elf => @panic("TODO elf parser"),
321 .coff => @panic("TODO coff parser"),
322 .wasm => try WasmDumper.parseAndDump(contents, .{
323 .gpa = gpa,
324 .dump_symtab = self.dump_symtab,
325 }),
326 else => unreachable,
327 };
328
329 var vars = std.StringHashMap(u64).init(gpa);
330
331 for (self.checks.items) |chk| {
332 var it = mem.tokenize(u8, output, "\r\n");
333 for (chk.actions.items) |act| {
334 switch (act.tag) {
335 .match => {
336 while (it.next()) |line| {
337 if (try act.match(line, &vars)) break;
338 } else {
339 std.debug.print(
340 \\
341 \\========= Expected to find: ==========================
342 \\{s}
343 \\========= But parsed file does not contain it: =======
344 \\{s}
345 \\
346 , .{ act.phrase, output });
347 return error.TestFailed;
348 }
349 },
350 .not_present => {
351 while (it.next()) |line| {
352 if (try act.match(line, &vars)) {
353 std.debug.print(
354 \\
355 \\========= Expected not to find: ===================
356 \\{s}
357 \\========= But parsed file does contain it: ========
358 \\{s}
359 \\
360 , .{ act.phrase, output });
361 return error.TestFailed;
362 }
363 }
364 },
365 .compute_cmp => {
366 const res = act.computeCmp(gpa, vars) catch |err| switch (err) {
367 error.UnknownVariable => {
368 std.debug.print(
369 \\========= From parsed file: =====================
370 \\{s}
371 \\
372 , .{output});
373 return error.TestFailed;
374 },
375 else => |e| return e,
376 };
377 if (!res) {
378 std.debug.print(
379 \\
380 \\========= Comparison failed for action: ===========
381 \\{s} {}
382 \\========= From parsed file: =======================
383 \\{s}
384 \\
385 , .{ act.phrase, act.expected.?, output });
386 return error.TestFailed;
387 }
388 },
389 }
390 }
391 }
392}
393
394const Opts = struct {
395 gpa: ?Allocator = null,
396 dump_symtab: bool = false,
397};
398
399const MachODumper = struct {
400 const LoadCommandIterator = macho.LoadCommandIterator;
401 const symtab_label = "symtab";
402
403 fn parseAndDump(bytes: []align(@alignOf(u64)) const u8, opts: Opts) ![]const u8 {
404 const gpa = opts.gpa orelse unreachable; // MachO dumper requires an allocator
405 var stream = std.io.fixedBufferStream(bytes);
406 const reader = stream.reader();
407
408 const hdr = try reader.readStruct(macho.mach_header_64);
409 if (hdr.magic != macho.MH_MAGIC_64) {
410 return error.InvalidMagicNumber;
411 }
412
413 var output = std.ArrayList(u8).init(gpa);
414 const writer = output.writer();
415
416 var symtab: []const macho.nlist_64 = undefined;
417 var strtab: []const u8 = undefined;
418 var sections = std.ArrayList(macho.section_64).init(gpa);
419 var imports = std.ArrayList([]const u8).init(gpa);
420
421 var it = LoadCommandIterator{
422 .ncmds = hdr.ncmds,
423 .buffer = bytes[@sizeOf(macho.mach_header_64)..][0..hdr.sizeofcmds],
424 };
425 var i: usize = 0;
426 while (it.next()) |cmd| {
427 switch (cmd.cmd()) {
428 .SEGMENT_64 => {
429 const seg = cmd.cast(macho.segment_command_64).?;
430 try sections.ensureUnusedCapacity(seg.nsects);
431 for (cmd.getSections()) |sect| {
432 sections.appendAssumeCapacity(sect);
433 }
434 },
435 .SYMTAB => if (opts.dump_symtab) {
436 const lc = cmd.cast(macho.symtab_command).?;
437 symtab = @ptrCast(
438 [*]const macho.nlist_64,
439 @alignCast(@alignOf(macho.nlist_64), &bytes[lc.symoff]),
440 )[0..lc.nsyms];
441 strtab = bytes[lc.stroff..][0..lc.strsize];
442 },
443 .LOAD_DYLIB,
444 .LOAD_WEAK_DYLIB,
445 .REEXPORT_DYLIB,
446 => {
447 try imports.append(cmd.getDylibPathName());
448 },
449 else => {},
450 }
451
452 try dumpLoadCommand(cmd, i, writer);
453 try writer.writeByte('\n');
454
455 i += 1;
456 }
457
458 if (opts.dump_symtab) {
459 try writer.print("{s}\n", .{symtab_label});
460 for (symtab) |sym| {
461 if (sym.stab()) continue;
462 const sym_name = mem.sliceTo(@ptrCast([*:0]const u8, strtab.ptr + sym.n_strx), 0);
463 if (sym.sect()) {
464 const sect = sections.items[sym.n_sect - 1];
465 try writer.print("{x} ({s},{s})", .{
466 sym.n_value,
467 sect.segName(),
468 sect.sectName(),
469 });
470 if (sym.ext()) {
471 try writer.writeAll(" external");
472 }
473 try writer.print(" {s}\n", .{sym_name});
474 } else if (sym.undf()) {
475 const ordinal = @divTrunc(@bitCast(i16, sym.n_desc), macho.N_SYMBOL_RESOLVER);
476 const import_name = blk: {
477 if (ordinal <= 0) {
478 if (ordinal == macho.BIND_SPECIAL_DYLIB_SELF)
479 break :blk "self import";
480 if (ordinal == macho.BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE)
481 break :blk "main executable";
482 if (ordinal == macho.BIND_SPECIAL_DYLIB_FLAT_LOOKUP)
483 break :blk "flat lookup";
484 unreachable;
485 }
486 const full_path = imports.items[@bitCast(u16, ordinal) - 1];
487 const basename = fs.path.basename(full_path);
488 assert(basename.len > 0);
489 const ext = mem.lastIndexOfScalar(u8, basename, '.') orelse basename.len;
490 break :blk basename[0..ext];
491 };
492 try writer.writeAll("(undefined)");
493 if (sym.weakRef()) {
494 try writer.writeAll(" weak");
495 }
496 if (sym.ext()) {
497 try writer.writeAll(" external");
498 }
499 try writer.print(" {s} (from {s})\n", .{
500 sym_name,
501 import_name,
502 });
503 } else unreachable;
504 }
505 }
506
507 return output.toOwnedSlice();
508 }
509
510 fn dumpLoadCommand(lc: macho.LoadCommandIterator.LoadCommand, index: usize, writer: anytype) !void {
511 // print header first
512 try writer.print(
513 \\LC {d}
514 \\cmd {s}
515 \\cmdsize {d}
516 , .{ index, @tagName(lc.cmd()), lc.cmdsize() });
517
518 switch (lc.cmd()) {
519 .SEGMENT_64 => {
520 const seg = lc.cast(macho.segment_command_64).?;
521 try writer.writeByte('\n');
522 try writer.print(
523 \\segname {s}
524 \\vmaddr {x}
525 \\vmsize {x}
526 \\fileoff {x}
527 \\filesz {x}
528 , .{
529 seg.segName(),
530 seg.vmaddr,
531 seg.vmsize,
532 seg.fileoff,
533 seg.filesize,
534 });
535
536 for (lc.getSections()) |sect| {
537 try writer.writeByte('\n');
538 try writer.print(
539 \\sectname {s}
540 \\addr {x}
541 \\size {x}
542 \\offset {x}
543 \\align {x}
544 , .{
545 sect.sectName(),
546 sect.addr,
547 sect.size,
548 sect.offset,
549 sect.@"align",
550 });
551 }
552 },
553
554 .ID_DYLIB,
555 .LOAD_DYLIB,
556 .LOAD_WEAK_DYLIB,
557 .REEXPORT_DYLIB,
558 => {
559 const dylib = lc.cast(macho.dylib_command).?;
560 try writer.writeByte('\n');
561 try writer.print(
562 \\name {s}
563 \\timestamp {d}
564 \\current version {x}
565 \\compatibility version {x}
566 , .{
567 lc.getDylibPathName(),
568 dylib.dylib.timestamp,
569 dylib.dylib.current_version,
570 dylib.dylib.compatibility_version,
571 });
572 },
573
574 .MAIN => {
575 const main = lc.cast(macho.entry_point_command).?;
576 try writer.writeByte('\n');
577 try writer.print(
578 \\entryoff {x}
579 \\stacksize {x}
580 , .{ main.entryoff, main.stacksize });
581 },
582
583 .RPATH => {
584 try writer.writeByte('\n');
585 try writer.print(
586 \\path {s}
587 , .{
588 lc.getRpathPathName(),
589 });
590 },
591
592 .UUID => {
593 const uuid = lc.cast(macho.uuid_command).?;
594 try writer.writeByte('\n');
595 try writer.print("uuid {x}", .{std.fmt.fmtSliceHexLower(&uuid.uuid)});
596 },
597
598 .DATA_IN_CODE,
599 .FUNCTION_STARTS,
600 .CODE_SIGNATURE,
601 => {
602 const llc = lc.cast(macho.linkedit_data_command).?;
603 try writer.writeByte('\n');
604 try writer.print(
605 \\dataoff {x}
606 \\datasize {x}
607 , .{ llc.dataoff, llc.datasize });
608 },
609
610 .DYLD_INFO_ONLY => {
611 const dlc = lc.cast(macho.dyld_info_command).?;
612 try writer.writeByte('\n');
613 try writer.print(
614 \\rebaseoff {x}
615 \\rebasesize {x}
616 \\bindoff {x}
617 \\bindsize {x}
618 \\weakbindoff {x}
619 \\weakbindsize {x}
620 \\lazybindoff {x}
621 \\lazybindsize {x}
622 \\exportoff {x}
623 \\exportsize {x}
624 , .{
625 dlc.rebase_off,
626 dlc.rebase_size,
627 dlc.bind_off,
628 dlc.bind_size,
629 dlc.weak_bind_off,
630 dlc.weak_bind_size,
631 dlc.lazy_bind_off,
632 dlc.lazy_bind_size,
633 dlc.export_off,
634 dlc.export_size,
635 });
636 },
637
638 .SYMTAB => {
639 const slc = lc.cast(macho.symtab_command).?;
640 try writer.writeByte('\n');
641 try writer.print(
642 \\symoff {x}
643 \\nsyms {x}
644 \\stroff {x}
645 \\strsize {x}
646 , .{
647 slc.symoff,
648 slc.nsyms,
649 slc.stroff,
650 slc.strsize,
651 });
652 },
653
654 .DYSYMTAB => {
655 const dlc = lc.cast(macho.dysymtab_command).?;
656 try writer.writeByte('\n');
657 try writer.print(
658 \\ilocalsym {x}
659 \\nlocalsym {x}
660 \\iextdefsym {x}
661 \\nextdefsym {x}
662 \\iundefsym {x}
663 \\nundefsym {x}
664 \\indirectsymoff {x}
665 \\nindirectsyms {x}
666 , .{
667 dlc.ilocalsym,
668 dlc.nlocalsym,
669 dlc.iextdefsym,
670 dlc.nextdefsym,
671 dlc.iundefsym,
672 dlc.nundefsym,
673 dlc.indirectsymoff,
674 dlc.nindirectsyms,
675 });
676 },
677
678 else => {},
679 }
680 }
681};
682
683const WasmDumper = struct {
684 const symtab_label = "symbols";
685
686 fn parseAndDump(bytes: []const u8, opts: Opts) ![]const u8 {
687 const gpa = opts.gpa orelse unreachable; // Wasm dumper requires an allocator
688 if (opts.dump_symtab) {
689 @panic("TODO: Implement symbol table parsing and dumping");
690 }
691
692 var fbs = std.io.fixedBufferStream(bytes);
693 const reader = fbs.reader();
694
695 const buf = try reader.readBytesNoEof(8);
696 if (!mem.eql(u8, buf[0..4], &std.wasm.magic)) {
697 return error.InvalidMagicByte;
698 }
699 if (!mem.eql(u8, buf[4..], &std.wasm.version)) {
700 return error.UnsupportedWasmVersion;
701 }
702
703 var output = std.ArrayList(u8).init(gpa);
704 errdefer output.deinit();
705 const writer = output.writer();
706
707 while (reader.readByte()) |current_byte| {
708 const section = std.meta.intToEnum(std.wasm.Section, current_byte) catch |err| {
709 std.debug.print("Found invalid section id '{d}'\n", .{current_byte});
710 return err;
711 };
712
713 const section_length = try std.leb.readULEB128(u32, reader);
714 try parseAndDumpSection(section, bytes[fbs.pos..][0..section_length], writer);
715 fbs.pos += section_length;
716 } else |_| {} // reached end of stream
717
718 return output.toOwnedSlice();
719 }
720
721 fn parseAndDumpSection(section: std.wasm.Section, data: []const u8, writer: anytype) !void {
722 var fbs = std.io.fixedBufferStream(data);
723 const reader = fbs.reader();
724
725 try writer.print(
726 \\Section {s}
727 \\size {d}
728 , .{ @tagName(section), data.len });
729
730 switch (section) {
731 .type,
732 .import,
733 .function,
734 .table,
735 .memory,
736 .global,
737 .@"export",
738 .element,
739 .code,
740 .data,
741 => {
742 const entries = try std.leb.readULEB128(u32, reader);
743 try writer.print("\nentries {d}\n", .{entries});
744 try dumpSection(section, data[fbs.pos..], entries, writer);
745 },
746 .custom => {
747 const name_length = try std.leb.readULEB128(u32, reader);
748 const name = data[fbs.pos..][0..name_length];
749 fbs.pos += name_length;
750 try writer.print("\nname {s}\n", .{name});
751
752 if (mem.eql(u8, name, "name")) {
753 try parseDumpNames(reader, writer, data);
754 } else if (mem.eql(u8, name, "producers")) {
755 try parseDumpProducers(reader, writer, data);
756 } else if (mem.eql(u8, name, "target_features")) {
757 try parseDumpFeatures(reader, writer, data);
758 }
759 // TODO: Implement parsing and dumping other custom sections (such as relocations)
760 },
761 .start => {
762 const start = try std.leb.readULEB128(u32, reader);
763 try writer.print("\nstart {d}\n", .{start});
764 },
765 else => {}, // skip unknown sections
766 }
767 }
768
769 fn dumpSection(section: std.wasm.Section, data: []const u8, entries: u32, writer: anytype) !void {
770 var fbs = std.io.fixedBufferStream(data);
771 const reader = fbs.reader();
772
773 switch (section) {
774 .type => {
775 var i: u32 = 0;
776 while (i < entries) : (i += 1) {
777 const func_type = try reader.readByte();
778 if (func_type != std.wasm.function_type) {
779 std.debug.print("Expected function type, found byte '{d}'\n", .{func_type});
780 return error.UnexpectedByte;
781 }
782 const params = try std.leb.readULEB128(u32, reader);
783 try writer.print("params {d}\n", .{params});
784 var index: u32 = 0;
785 while (index < params) : (index += 1) {
786 try parseDumpType(std.wasm.Valtype, reader, writer);
787 } else index = 0;
788 const returns = try std.leb.readULEB128(u32, reader);
789 try writer.print("returns {d}\n", .{returns});
790 while (index < returns) : (index += 1) {
791 try parseDumpType(std.wasm.Valtype, reader, writer);
792 }
793 }
794 },
795 .import => {
796 var i: u32 = 0;
797 while (i < entries) : (i += 1) {
798 const module_name_len = try std.leb.readULEB128(u32, reader);
799 const module_name = data[fbs.pos..][0..module_name_len];
800 fbs.pos += module_name_len;
801 const name_len = try std.leb.readULEB128(u32, reader);
802 const name = data[fbs.pos..][0..name_len];
803 fbs.pos += name_len;
804
805 const kind = std.meta.intToEnum(std.wasm.ExternalKind, try reader.readByte()) catch |err| {
806 std.debug.print("Invalid import kind\n", .{});
807 return err;
808 };
809
810 try writer.print(
811 \\module {s}
812 \\name {s}
813 \\kind {s}
814 , .{ module_name, name, @tagName(kind) });
815 try writer.writeByte('\n');
816 switch (kind) {
817 .function => {
818 try writer.print("index {d}\n", .{try std.leb.readULEB128(u32, reader)});
819 },
820 .memory => {
821 try parseDumpLimits(reader, writer);
822 },
823 .global => {
824 try parseDumpType(std.wasm.Valtype, reader, writer);
825 try writer.print("mutable {}\n", .{0x01 == try std.leb.readULEB128(u32, reader)});
826 },
827 .table => {
828 try parseDumpType(std.wasm.RefType, reader, writer);
829 try parseDumpLimits(reader, writer);
830 },
831 }
832 }
833 },
834 .function => {
835 var i: u32 = 0;
836 while (i < entries) : (i += 1) {
837 try writer.print("index {d}\n", .{try std.leb.readULEB128(u32, reader)});
838 }
839 },
840 .table => {
841 var i: u32 = 0;
842 while (i < entries) : (i += 1) {
843 try parseDumpType(std.wasm.RefType, reader, writer);
844 try parseDumpLimits(reader, writer);
845 }
846 },
847 .memory => {
848 var i: u32 = 0;
849 while (i < entries) : (i += 1) {
850 try parseDumpLimits(reader, writer);
851 }
852 },
853 .global => {
854 var i: u32 = 0;
855 while (i < entries) : (i += 1) {
856 try parseDumpType(std.wasm.Valtype, reader, writer);
857 try writer.print("mutable {}\n", .{0x01 == try std.leb.readULEB128(u1, reader)});
858 try parseDumpInit(reader, writer);
859 }
860 },
861 .@"export" => {
862 var i: u32 = 0;
863 while (i < entries) : (i += 1) {
864 const name_len = try std.leb.readULEB128(u32, reader);
865 const name = data[fbs.pos..][0..name_len];
866 fbs.pos += name_len;
867 const kind_byte = try std.leb.readULEB128(u8, reader);
868 const kind = std.meta.intToEnum(std.wasm.ExternalKind, kind_byte) catch |err| {
869 std.debug.print("invalid export kind value '{d}'\n", .{kind_byte});
870 return err;
871 };
872 const index = try std.leb.readULEB128(u32, reader);
873 try writer.print(
874 \\name {s}
875 \\kind {s}
876 \\index {d}
877 , .{ name, @tagName(kind), index });
878 try writer.writeByte('\n');
879 }
880 },
881 .element => {
882 var i: u32 = 0;
883 while (i < entries) : (i += 1) {
884 try writer.print("table index {d}\n", .{try std.leb.readULEB128(u32, reader)});
885 try parseDumpInit(reader, writer);
886
887 const function_indexes = try std.leb.readULEB128(u32, reader);
888 var function_index: u32 = 0;
889 try writer.print("indexes {d}\n", .{function_indexes});
890 while (function_index < function_indexes) : (function_index += 1) {
891 try writer.print("index {d}\n", .{try std.leb.readULEB128(u32, reader)});
892 }
893 }
894 },
895 .code => {}, // code section is considered opaque to linker
896 .data => {
897 var i: u32 = 0;
898 while (i < entries) : (i += 1) {
899 const index = try std.leb.readULEB128(u32, reader);
900 try writer.print("memory index 0x{x}\n", .{index});
901 try parseDumpInit(reader, writer);
902 const size = try std.leb.readULEB128(u32, reader);
903 try writer.print("size {d}\n", .{size});
904 try reader.skipBytes(size, .{}); // we do not care about the content of the segments
905 }
906 },
907 else => unreachable,
908 }
909 }
910
911 fn parseDumpType(comptime WasmType: type, reader: anytype, writer: anytype) !void {
912 const type_byte = try reader.readByte();
913 const valtype = std.meta.intToEnum(WasmType, type_byte) catch |err| {
914 std.debug.print("Invalid wasm type value '{d}'\n", .{type_byte});
915 return err;
916 };
917 try writer.print("type {s}\n", .{@tagName(valtype)});
918 }
919
920 fn parseDumpLimits(reader: anytype, writer: anytype) !void {
921 const flags = try std.leb.readULEB128(u8, reader);
922 const min = try std.leb.readULEB128(u32, reader);
923
924 try writer.print("min {x}\n", .{min});
925 if (flags != 0) {
926 try writer.print("max {x}\n", .{try std.leb.readULEB128(u32, reader)});
927 }
928 }
929
930 fn parseDumpInit(reader: anytype, writer: anytype) !void {
931 const byte = try std.leb.readULEB128(u8, reader);
932 const opcode = std.meta.intToEnum(std.wasm.Opcode, byte) catch |err| {
933 std.debug.print("invalid wasm opcode '{d}'\n", .{byte});
934 return err;
935 };
936 switch (opcode) {
937 .i32_const => try writer.print("i32.const {x}\n", .{try std.leb.readILEB128(i32, reader)}),
938 .i64_const => try writer.print("i64.const {x}\n", .{try std.leb.readILEB128(i64, reader)}),
939 .f32_const => try writer.print("f32.const {x}\n", .{@bitCast(f32, try reader.readIntLittle(u32))}),
940 .f64_const => try writer.print("f64.const {x}\n", .{@bitCast(f64, try reader.readIntLittle(u64))}),
941 .global_get => try writer.print("global.get {x}\n", .{try std.leb.readULEB128(u32, reader)}),
942 else => unreachable,
943 }
944 const end_opcode = try std.leb.readULEB128(u8, reader);
945 if (end_opcode != std.wasm.opcode(.end)) {
946 std.debug.print("expected 'end' opcode in init expression\n", .{});
947 return error.MissingEndOpcode;
948 }
949 }
950
951 fn parseDumpNames(reader: anytype, writer: anytype, data: []const u8) !void {
952 while (reader.context.pos < data.len) {
953 try parseDumpType(std.wasm.NameSubsection, reader, writer);
954 const size = try std.leb.readULEB128(u32, reader);
955 const entries = try std.leb.readULEB128(u32, reader);
956 try writer.print(
957 \\size {d}
958 \\names {d}
959 , .{ size, entries });
960 try writer.writeByte('\n');
961 var i: u32 = 0;
962 while (i < entries) : (i += 1) {
963 const index = try std.leb.readULEB128(u32, reader);
964 const name_len = try std.leb.readULEB128(u32, reader);
965 const pos = reader.context.pos;
966 const name = data[pos..][0..name_len];
967 reader.context.pos += name_len;
968
969 try writer.print(
970 \\index {d}
971 \\name {s}
972 , .{ index, name });
973 try writer.writeByte('\n');
974 }
975 }
976 }
977
978 fn parseDumpProducers(reader: anytype, writer: anytype, data: []const u8) !void {
979 const field_count = try std.leb.readULEB128(u32, reader);
980 try writer.print("fields {d}\n", .{field_count});
981 var current_field: u32 = 0;
982 while (current_field < field_count) : (current_field += 1) {
983 const field_name_length = try std.leb.readULEB128(u32, reader);
984 const field_name = data[reader.context.pos..][0..field_name_length];
985 reader.context.pos += field_name_length;
986
987 const value_count = try std.leb.readULEB128(u32, reader);
988 try writer.print(
989 \\field_name {s}
990 \\values {d}
991 , .{ field_name, value_count });
992 try writer.writeByte('\n');
993 var current_value: u32 = 0;
994 while (current_value < value_count) : (current_value += 1) {
995 const value_length = try std.leb.readULEB128(u32, reader);
996 const value = data[reader.context.pos..][0..value_length];
997 reader.context.pos += value_length;
998
999 const version_length = try std.leb.readULEB128(u32, reader);
1000 const version = data[reader.context.pos..][0..version_length];
1001 reader.context.pos += version_length;
1002
1003 try writer.print(
1004 \\value_name {s}
1005 \\version {s}
1006 , .{ value, version });
1007 try writer.writeByte('\n');
1008 }
1009 }
1010 }
1011
1012 fn parseDumpFeatures(reader: anytype, writer: anytype, data: []const u8) !void {
1013 const feature_count = try std.leb.readULEB128(u32, reader);
1014 try writer.print("features {d}\n", .{feature_count});
1015
1016 var index: u32 = 0;
1017 while (index < feature_count) : (index += 1) {
1018 const prefix_byte = try std.leb.readULEB128(u8, reader);
1019 const name_length = try std.leb.readULEB128(u32, reader);
1020 const feature_name = data[reader.context.pos..][0..name_length];
1021 reader.context.pos += name_length;
1022
1023 try writer.print("{c} {s}\n", .{ prefix_byte, feature_name });
1024 }
1025 }
1026};
lib/std/build/ConfigHeaderStep.zig deleted-288
...@@ -1,288 +0,0 @@
1const std = @import("../std.zig");
2const ConfigHeaderStep = @This();
3const Step = std.build.Step;
4const Builder = std.build.Builder;
5
6pub const base_id: Step.Id = .config_header;
7
8pub const Style = enum {
9 /// The configure format supported by autotools. It uses `#undef foo` to
10 /// mark lines that can be substituted with different values.
11 autoconf,
12 /// The configure format supported by CMake. It uses `@@FOO@@` and
13 /// `#cmakedefine` for template substitution.
14 cmake,
15};
16
17pub const Value = union(enum) {
18 undef,
19 defined,
20 boolean: bool,
21 int: i64,
22 ident: []const u8,
23 string: []const u8,
24};
25
26step: Step,
27builder: *Builder,
28source: std.build.FileSource,
29style: Style,
30values: std.StringHashMap(Value),
31max_bytes: usize = 2 * 1024 * 1024,
32output_dir: []const u8,
33output_basename: []const u8,
34
35pub fn create(builder: *Builder, source: std.build.FileSource, style: Style) *ConfigHeaderStep {
36 const self = builder.allocator.create(ConfigHeaderStep) catch @panic("OOM");
37 const name = builder.fmt("configure header {s}", .{source.getDisplayName()});
38 self.* = .{
39 .builder = builder,
40 .step = Step.init(base_id, name, builder.allocator, make),
41 .source = source,
42 .style = style,
43 .values = std.StringHashMap(Value).init(builder.allocator),
44 .output_dir = undefined,
45 .output_basename = "config.h",
46 };
47 switch (source) {
48 .path => |p| {
49 const basename = std.fs.path.basename(p);
50 if (std.mem.endsWith(u8, basename, ".h.in")) {
51 self.output_basename = basename[0 .. basename.len - 3];
52 }
53 },
54 else => {},
55 }
56 return self;
57}
58
59pub fn addValues(self: *ConfigHeaderStep, values: anytype) void {
60 return addValuesInner(self, values) catch @panic("OOM");
61}
62
63fn addValuesInner(self: *ConfigHeaderStep, values: anytype) !void {
64 inline for (@typeInfo(@TypeOf(values)).Struct.fields) |field| {
65 switch (@typeInfo(field.type)) {
66 .Null => {
67 try self.values.put(field.name, .undef);
68 },
69 .Void => {
70 try self.values.put(field.name, .defined);
71 },
72 .Bool => {
73 try self.values.put(field.name, .{ .boolean = @field(values, field.name) });
74 },
75 .ComptimeInt => {
76 try self.values.put(field.name, .{ .int = @field(values, field.name) });
77 },
78 .EnumLiteral => {
79 try self.values.put(field.name, .{ .ident = @tagName(@field(values, field.name)) });
80 },
81 .Pointer => |ptr| {
82 switch (@typeInfo(ptr.child)) {
83 .Array => |array| {
84 if (ptr.size == .One and array.child == u8) {
85 try self.values.put(field.name, .{ .string = @field(values, field.name) });
86 continue;
87 }
88 },
89 else => {},
90 }
91
92 @compileError("unsupported ConfigHeaderStep value type: " ++
93 @typeName(field.type));
94 },
95 else => @compileError("unsupported ConfigHeaderStep value type: " ++
96 @typeName(field.type)),
97 }
98 }
99}
100
101fn make(step: *Step) !void {
102 const self = @fieldParentPtr(ConfigHeaderStep, "step", step);
103 const gpa = self.builder.allocator;
104 const src_path = self.source.getPath(self.builder);
105 const contents = try std.fs.cwd().readFileAlloc(gpa, src_path, self.max_bytes);
106
107 // The cache is used here not really as a way to speed things up - because writing
108 // the data to a file would probably be very fast - but as a way to find a canonical
109 // location to put build artifacts.
110
111 // If, for example, a hard-coded path was used as the location to put ConfigHeaderStep
112 // files, then two ConfigHeaderStep executing in parallel might clobber each other.
113
114 // TODO port the cache system from the compiler to zig std lib. Until then
115 // we construct the path directly, and no "cache hit" detection happens;
116 // the files are always written.
117 // Note there is very similar code over in WriteFileStep
118 const Hasher = std.crypto.auth.siphash.SipHash128(1, 3);
119 // Random bytes to make ConfigHeaderStep unique. Refresh this with new
120 // random bytes when ConfigHeaderStep implementation is modified in a
121 // non-backwards-compatible way.
122 var hash = Hasher.init("X1pQzdDt91Zlh7Eh");
123 hash.update(self.source.getDisplayName());
124 hash.update(contents);
125
126 var digest: [16]u8 = undefined;
127 hash.final(&digest);
128 var hash_basename: [digest.len * 2]u8 = undefined;
129 _ = std.fmt.bufPrint(
130 &hash_basename,
131 "{s}",
132 .{std.fmt.fmtSliceHexLower(&digest)},
133 ) catch unreachable;
134
135 self.output_dir = try std.fs.path.join(gpa, &[_][]const u8{
136 self.builder.cache_root, "o", &hash_basename,
137 });
138 var dir = std.fs.cwd().makeOpenPath(self.output_dir, .{}) catch |err| {
139 std.debug.print("unable to make path {s}: {s}\n", .{ self.output_dir, @errorName(err) });
140 return err;
141 };
142 defer dir.close();
143
144 var values_copy = try self.values.clone();
145 defer values_copy.deinit();
146
147 var output = std.ArrayList(u8).init(gpa);
148 defer output.deinit();
149 try output.ensureTotalCapacity(contents.len);
150
151 try output.appendSlice("/* This file was generated by ConfigHeaderStep using the Zig Build System. */\n");
152
153 switch (self.style) {
154 .autoconf => try render_autoconf(contents, &output, &values_copy, src_path),
155 .cmake => try render_cmake(contents, &output, &values_copy, src_path),
156 }
157
158 try dir.writeFile(self.output_basename, output.items);
159}
160
161fn render_autoconf(
162 contents: []const u8,
163 output: *std.ArrayList(u8),
164 values_copy: *std.StringHashMap(Value),
165 src_path: []const u8,
166) !void {
167 var any_errors = false;
168 var line_index: u32 = 0;
169 var line_it = std.mem.split(u8, contents, "\n");
170 while (line_it.next()) |line| : (line_index += 1) {
171 if (!std.mem.startsWith(u8, line, "#")) {
172 try output.appendSlice(line);
173 try output.appendSlice("\n");
174 continue;
175 }
176 var it = std.mem.tokenize(u8, line[1..], " \t\r");
177 const undef = it.next().?;
178 if (!std.mem.eql(u8, undef, "undef")) {
179 try output.appendSlice(line);
180 try output.appendSlice("\n");
181 continue;
182 }
183 const name = it.rest();
184 const kv = values_copy.fetchRemove(name) orelse {
185 std.debug.print("{s}:{d}: error: unspecified config header value: '{s}'\n", .{
186 src_path, line_index + 1, name,
187 });
188 any_errors = true;
189 continue;
190 };
191 try renderValue(output, name, kv.value);
192 }
193
194 {
195 var it = values_copy.iterator();
196 while (it.next()) |entry| {
197 const name = entry.key_ptr.*;
198 std.debug.print("{s}: error: config header value unused: '{s}'\n", .{ src_path, name });
199 }
200 }
201
202 if (any_errors) {
203 return error.HeaderConfigFailed;
204 }
205}
206
207fn render_cmake(
208 contents: []const u8,
209 output: *std.ArrayList(u8),
210 values_copy: *std.StringHashMap(Value),
211 src_path: []const u8,
212) !void {
213 var any_errors = false;
214 var line_index: u32 = 0;
215 var line_it = std.mem.split(u8, contents, "\n");
216 while (line_it.next()) |line| : (line_index += 1) {
217 if (!std.mem.startsWith(u8, line, "#")) {
218 try output.appendSlice(line);
219 try output.appendSlice("\n");
220 continue;
221 }
222 var it = std.mem.tokenize(u8, line[1..], " \t\r");
223 const cmakedefine = it.next().?;
224 if (!std.mem.eql(u8, cmakedefine, "cmakedefine")) {
225 try output.appendSlice(line);
226 try output.appendSlice("\n");
227 continue;
228 }
229 const name = it.next() orelse {
230 std.debug.print("{s}:{d}: error: missing define name\n", .{
231 src_path, line_index + 1,
232 });
233 any_errors = true;
234 continue;
235 };
236 const kv = values_copy.fetchRemove(name) orelse {
237 std.debug.print("{s}:{d}: error: unspecified config header value: '{s}'\n", .{
238 src_path, line_index + 1, name,
239 });
240 any_errors = true;
241 continue;
242 };
243 try renderValue(output, name, kv.value);
244 }
245
246 {
247 var it = values_copy.iterator();
248 while (it.next()) |entry| {
249 const name = entry.key_ptr.*;
250 std.debug.print("{s}: error: config header value unused: '{s}'\n", .{ src_path, name });
251 }
252 }
253
254 if (any_errors) {
255 return error.HeaderConfigFailed;
256 }
257}
258
259fn renderValue(output: *std.ArrayList(u8), name: []const u8, value: Value) !void {
260 switch (value) {
261 .undef => {
262 try output.appendSlice("/* #undef ");
263 try output.appendSlice(name);
264 try output.appendSlice(" */\n");
265 },
266 .defined => {
267 try output.appendSlice("#define ");
268 try output.appendSlice(name);
269 try output.appendSlice("\n");
270 },
271 .boolean => |b| {
272 try output.appendSlice("#define ");
273 try output.appendSlice(name);
274 try output.appendSlice(" ");
275 try output.appendSlice(if (b) "true\n" else "false\n");
276 },
277 .int => |i| {
278 try output.writer().print("#define {s} {d}\n", .{ name, i });
279 },
280 .ident => |ident| {
281 try output.writer().print("#define {s} {s}\n", .{ name, ident });
282 },
283 .string => |string| {
284 // TODO: use C-specific escaping instead of zig string literals
285 try output.writer().print("#define {s} \"{}\"\n", .{ name, std.zig.fmtEscapes(string) });
286 },
287 }
288}
lib/std/build/EmulatableRunStep.zig deleted-215
...@@ -1,215 +0,0 @@
1//! Unlike `RunStep` this step will provide emulation, when enabled, to run foreign binaries.
2//! When a binary is foreign, but emulation for the target is disabled, the specified binary
3//! will not be run and therefore also not validated against its output.
4//! This step can be useful when wishing to run a built binary on multiple platforms,
5//! without having to verify if it's possible to be ran against.
6
7const std = @import("../std.zig");
8const build = std.build;
9const Step = std.build.Step;
10const Builder = std.build.Builder;
11const LibExeObjStep = std.build.LibExeObjStep;
12const RunStep = std.build.RunStep;
13
14const fs = std.fs;
15const process = std.process;
16const EnvMap = process.EnvMap;
17
18const EmulatableRunStep = @This();
19
20pub const base_id = .emulatable_run;
21
22const max_stdout_size = 1 * 1024 * 1024; // 1 MiB
23
24step: Step,
25builder: *Builder,
26
27/// The artifact (executable) to be run by this step
28exe: *LibExeObjStep,
29
30/// Set this to `null` to ignore the exit code for the purpose of determining a successful execution
31expected_exit_code: ?u8 = 0,
32
33/// Override this field to modify the environment
34env_map: ?*EnvMap,
35
36/// Set this to modify the current working directory
37cwd: ?[]const u8,
38
39stdout_action: RunStep.StdIoAction = .inherit,
40stderr_action: RunStep.StdIoAction = .inherit,
41
42/// When set to true, hides the warning of skipping a foreign binary which cannot be run on the host
43/// or through emulation.
44hide_foreign_binaries_warning: bool,
45
46/// Creates a step that will execute the given artifact. This step will allow running the
47/// binary through emulation when any of the emulation options such as `enable_rosetta` are set to true.
48/// When set to false, and the binary is foreign, running the executable is skipped.
49/// Asserts given artifact is an executable.
50pub fn create(builder: *Builder, name: []const u8, artifact: *LibExeObjStep) *EmulatableRunStep {
51 std.debug.assert(artifact.kind == .exe or artifact.kind == .test_exe);
52 const self = builder.allocator.create(EmulatableRunStep) catch unreachable;
53
54 const option_name = "hide-foreign-warnings";
55 const hide_warnings = if (builder.available_options_map.get(option_name) == null) warn: {
56 break :warn builder.option(bool, option_name, "Hide the warning when a foreign binary which is incompatible is skipped") orelse false;
57 } else false;
58
59 self.* = .{
60 .builder = builder,
61 .step = Step.init(.emulatable_run, name, builder.allocator, make),
62 .exe = artifact,
63 .env_map = null,
64 .cwd = null,
65 .hide_foreign_binaries_warning = hide_warnings,
66 };
67 self.step.dependOn(&artifact.step);
68
69 return self;
70}
71
72fn make(step: *Step) !void {
73 const self = @fieldParentPtr(EmulatableRunStep, "step", step);
74 const host_info = self.builder.host;
75
76 var argv_list = std.ArrayList([]const u8).init(self.builder.allocator);
77 defer argv_list.deinit();
78
79 const need_cross_glibc = self.exe.target.isGnuLibC() and self.exe.is_linking_libc;
80 switch (host_info.getExternalExecutor(self.exe.target_info, .{
81 .qemu_fixes_dl = need_cross_glibc and self.builder.glibc_runtimes_dir != null,
82 .link_libc = self.exe.is_linking_libc,
83 })) {
84 .native => {},
85 .rosetta => if (!self.builder.enable_rosetta) return warnAboutForeignBinaries(self),
86 .wine => |bin_name| if (self.builder.enable_wine) {
87 try argv_list.append(bin_name);
88 } else return,
89 .qemu => |bin_name| if (self.builder.enable_qemu) {
90 const glibc_dir_arg = if (need_cross_glibc)
91 self.builder.glibc_runtimes_dir orelse return
92 else
93 null;
94 try argv_list.append(bin_name);
95 if (glibc_dir_arg) |dir| {
96 // TODO look into making this a call to `linuxTriple`. This
97 // needs the directory to be called "i686" rather than
98 // "x86" which is why we do it manually here.
99 const fmt_str = "{s}" ++ fs.path.sep_str ++ "{s}-{s}-{s}";
100 const cpu_arch = self.exe.target.getCpuArch();
101 const os_tag = self.exe.target.getOsTag();
102 const abi = self.exe.target.getAbi();
103 const cpu_arch_name: []const u8 = if (cpu_arch == .x86)
104 "i686"
105 else
106 @tagName(cpu_arch);
107 const full_dir = try std.fmt.allocPrint(self.builder.allocator, fmt_str, .{
108 dir, cpu_arch_name, @tagName(os_tag), @tagName(abi),
109 });
110
111 try argv_list.append("-L");
112 try argv_list.append(full_dir);
113 }
114 } else return warnAboutForeignBinaries(self),
115 .darling => |bin_name| if (self.builder.enable_darling) {
116 try argv_list.append(bin_name);
117 } else return warnAboutForeignBinaries(self),
118 .wasmtime => |bin_name| if (self.builder.enable_wasmtime) {
119 try argv_list.append(bin_name);
120 try argv_list.append("--dir=.");
121 } else return warnAboutForeignBinaries(self),
122 else => return warnAboutForeignBinaries(self),
123 }
124
125 if (self.exe.target.isWindows()) {
126 // On Windows we don't have rpaths so we have to add .dll search paths to PATH
127 RunStep.addPathForDynLibsInternal(&self.step, self.builder, self.exe);
128 }
129
130 const executable_path = self.exe.installed_path orelse self.exe.getOutputSource().getPath(self.builder);
131 try argv_list.append(executable_path);
132
133 try RunStep.runCommand(
134 argv_list.items,
135 self.builder,
136 self.expected_exit_code,
137 self.stdout_action,
138 self.stderr_action,
139 .Inherit,
140 self.env_map,
141 self.cwd,
142 false,
143 );
144}
145
146pub fn expectStdErrEqual(self: *EmulatableRunStep, bytes: []const u8) void {
147 self.stderr_action = .{ .expect_exact = self.builder.dupe(bytes) };
148}
149
150pub fn expectStdOutEqual(self: *EmulatableRunStep, bytes: []const u8) void {
151 self.stdout_action = .{ .expect_exact = self.builder.dupe(bytes) };
152}
153
154fn warnAboutForeignBinaries(step: *EmulatableRunStep) void {
155 if (step.hide_foreign_binaries_warning) return;
156 const builder = step.builder;
157 const artifact = step.exe;
158
159 const host_name = builder.host.target.zigTriple(builder.allocator) catch unreachable;
160 const foreign_name = artifact.target.zigTriple(builder.allocator) catch unreachable;
161 const target_info = std.zig.system.NativeTargetInfo.detect(artifact.target) catch unreachable;
162 const need_cross_glibc = artifact.target.isGnuLibC() and artifact.is_linking_libc;
163 switch (builder.host.getExternalExecutor(target_info, .{
164 .qemu_fixes_dl = need_cross_glibc and builder.glibc_runtimes_dir != null,
165 .link_libc = artifact.is_linking_libc,
166 })) {
167 .native => unreachable,
168 .bad_dl => |foreign_dl| {
169 const host_dl = builder.host.dynamic_linker.get() orelse "(none)";
170 std.debug.print("the host system does not appear to be capable of executing binaries from the target because the host dynamic linker is '{s}', while the target dynamic linker is '{s}'. Consider setting the dynamic linker as '{s}'.\n", .{
171 host_dl, foreign_dl, host_dl,
172 });
173 },
174 .bad_os_or_cpu => {
175 std.debug.print("the host system ({s}) does not appear to be capable of executing binaries from the target ({s}).\n", .{
176 host_name, foreign_name,
177 });
178 },
179 .darling => if (!builder.enable_darling) {
180 std.debug.print(
181 "the host system ({s}) does not appear to be capable of executing binaries " ++
182 "from the target ({s}). Consider enabling darling.\n",
183 .{ host_name, foreign_name },
184 );
185 },
186 .rosetta => if (!builder.enable_rosetta) {
187 std.debug.print(
188 "the host system ({s}) does not appear to be capable of executing binaries " ++
189 "from the target ({s}). Consider enabling rosetta.\n",
190 .{ host_name, foreign_name },
191 );
192 },
193 .wine => if (!builder.enable_wine) {
194 std.debug.print(
195 "the host system ({s}) does not appear to be capable of executing binaries " ++
196 "from the target ({s}). Consider enabling wine.\n",
197 .{ host_name, foreign_name },
198 );
199 },
200 .qemu => if (!builder.enable_qemu) {
201 std.debug.print(
202 "the host system ({s}) does not appear to be capable of executing binaries " ++
203 "from the target ({s}). Consider enabling qemu.\n",
204 .{ host_name, foreign_name },
205 );
206 },
207 .wasmtime => {
208 std.debug.print(
209 "the host system ({s}) does not appear to be capable of executing binaries " ++
210 "from the target ({s}). Consider enabling wasmtime.\n",
211 .{ host_name, foreign_name },
212 );
213 },
214 }
215}
lib/std/build/FmtStep.zig deleted-37
...@@ -1,37 +0,0 @@
1const std = @import("../std.zig");
2const build = @import("../build.zig");
3const Step = build.Step;
4const Builder = build.Builder;
5const BufMap = std.BufMap;
6const mem = std.mem;
7
8const FmtStep = @This();
9
10pub const base_id = .fmt;
11
12step: Step,
13builder: *Builder,
14argv: [][]const u8,
15
16pub fn create(builder: *Builder, paths: []const []const u8) *FmtStep {
17 const self = builder.allocator.create(FmtStep) catch unreachable;
18 const name = "zig fmt";
19 self.* = FmtStep{
20 .step = Step.init(.fmt, name, builder.allocator, make),
21 .builder = builder,
22 .argv = builder.allocator.alloc([]u8, paths.len + 2) catch unreachable,
23 };
24
25 self.argv[0] = builder.zig_exe;
26 self.argv[1] = "fmt";
27 for (paths) |path, i| {
28 self.argv[2 + i] = builder.pathFromRoot(path);
29 }
30 return self;
31}
32
33fn make(step: *Step) !void {
34 const self = @fieldParentPtr(FmtStep, "step", step);
35
36 return self.builder.spawnChild(self.argv);
37}
lib/std/build/InstallArtifactStep.zig deleted-88
...@@ -1,88 +0,0 @@
1const std = @import("../std.zig");
2const build = @import("../build.zig");
3const Step = build.Step;
4const Builder = build.Builder;
5const LibExeObjStep = std.build.LibExeObjStep;
6const InstallDir = std.build.InstallDir;
7
8pub const base_id = .install_artifact;
9
10step: Step,
11builder: *Builder,
12artifact: *LibExeObjStep,
13dest_dir: InstallDir,
14pdb_dir: ?InstallDir,
15h_dir: ?InstallDir,
16
17const Self = @This();
18
19pub fn create(builder: *Builder, artifact: *LibExeObjStep) *Self {
20 if (artifact.install_step) |s| return s;
21
22 const self = builder.allocator.create(Self) catch unreachable;
23 self.* = Self{
24 .builder = builder,
25 .step = Step.init(.install_artifact, builder.fmt("install {s}", .{artifact.step.name}), builder.allocator, make),
26 .artifact = artifact,
27 .dest_dir = artifact.override_dest_dir orelse switch (artifact.kind) {
28 .obj => @panic("Cannot install a .obj build artifact."),
29 .@"test" => @panic("Cannot install a test build artifact, use addTestExe instead."),
30 .exe, .test_exe => InstallDir{ .bin = {} },
31 .lib => InstallDir{ .lib = {} },
32 },
33 .pdb_dir = if (artifact.producesPdbFile()) blk: {
34 if (artifact.kind == .exe or artifact.kind == .test_exe) {
35 break :blk InstallDir{ .bin = {} };
36 } else {
37 break :blk InstallDir{ .lib = {} };
38 }
39 } else null,
40 .h_dir = if (artifact.kind == .lib and artifact.emit_h) .header else null,
41 };
42 self.step.dependOn(&artifact.step);
43 artifact.install_step = self;
44
45 builder.pushInstalledFile(self.dest_dir, artifact.out_filename);
46 if (self.artifact.isDynamicLibrary()) {
47 if (artifact.major_only_filename) |name| {
48 builder.pushInstalledFile(.lib, name);
49 }
50 if (artifact.name_only_filename) |name| {
51 builder.pushInstalledFile(.lib, name);
52 }
53 if (self.artifact.target.isWindows()) {
54 builder.pushInstalledFile(.lib, artifact.out_lib_filename);
55 }
56 }
57 if (self.pdb_dir) |pdb_dir| {
58 builder.pushInstalledFile(pdb_dir, artifact.out_pdb_filename);
59 }
60 if (self.h_dir) |h_dir| {
61 builder.pushInstalledFile(h_dir, artifact.out_h_filename);
62 }
63 return self;
64}
65
66fn make(step: *Step) !void {
67 const self = @fieldParentPtr(Self, "step", step);
68 const builder = self.builder;
69
70 const full_dest_path = builder.getInstallPath(self.dest_dir, self.artifact.out_filename);
71 try builder.updateFile(self.artifact.getOutputSource().getPath(builder), full_dest_path);
72 if (self.artifact.isDynamicLibrary() and self.artifact.version != null and self.artifact.target.wantSharedLibSymLinks()) {
73 try LibExeObjStep.doAtomicSymLinks(builder.allocator, full_dest_path, self.artifact.major_only_filename.?, self.artifact.name_only_filename.?);
74 }
75 if (self.artifact.isDynamicLibrary() and self.artifact.target.isWindows() and self.artifact.emit_implib != .no_emit) {
76 const full_implib_path = builder.getInstallPath(self.dest_dir, self.artifact.out_lib_filename);
77 try builder.updateFile(self.artifact.getOutputLibSource().getPath(builder), full_implib_path);
78 }
79 if (self.pdb_dir) |pdb_dir| {
80 const full_pdb_path = builder.getInstallPath(pdb_dir, self.artifact.out_pdb_filename);
81 try builder.updateFile(self.artifact.getOutputPdbSource().getPath(builder), full_pdb_path);
82 }
83 if (self.h_dir) |h_dir| {
84 const full_h_path = builder.getInstallPath(h_dir, self.artifact.out_h_filename);
85 try builder.updateFile(self.artifact.getOutputHSource().getPath(builder), full_h_path);
86 }
87 self.artifact.installed_path = full_dest_path;
88}
lib/std/build/InstallDirStep.zig deleted-95
...@@ -1,95 +0,0 @@
1const std = @import("../std.zig");
2const mem = std.mem;
3const fs = std.fs;
4const build = @import("../build.zig");
5const Step = build.Step;
6const Builder = build.Builder;
7const InstallDir = std.build.InstallDir;
8const InstallDirStep = @This();
9const log = std.log;
10
11step: Step,
12builder: *Builder,
13options: Options,
14/// This is used by the build system when a file being installed comes from one
15/// package but is being installed by another.
16override_source_builder: ?*Builder = null,
17
18pub const base_id = .install_dir;
19
20pub const Options = struct {
21 source_dir: []const u8,
22 install_dir: InstallDir,
23 install_subdir: []const u8,
24 /// File paths which end in any of these suffixes will be excluded
25 /// from being installed.
26 exclude_extensions: []const []const u8 = &.{},
27 /// File paths which end in any of these suffixes will result in
28 /// empty files being installed. This is mainly intended for large
29 /// test.zig files in order to prevent needless installation bloat.
30 /// However if the files were not present at all, then
31 /// `@import("test.zig")` would be a compile error.
32 blank_extensions: []const []const u8 = &.{},
33
34 fn dupe(self: Options, b: *Builder) Options {
35 return .{
36 .source_dir = b.dupe(self.source_dir),
37 .install_dir = self.install_dir.dupe(b),
38 .install_subdir = b.dupe(self.install_subdir),
39 .exclude_extensions = b.dupeStrings(self.exclude_extensions),
40 .blank_extensions = b.dupeStrings(self.blank_extensions),
41 };
42 }
43};
44
45pub fn init(
46 builder: *Builder,
47 options: Options,
48) InstallDirStep {
49 builder.pushInstalledFile(options.install_dir, options.install_subdir);
50 return InstallDirStep{
51 .builder = builder,
52 .step = Step.init(.install_dir, builder.fmt("install {s}/", .{options.source_dir}), builder.allocator, make),
53 .options = options.dupe(builder),
54 };
55}
56
57fn make(step: *Step) !void {
58 const self = @fieldParentPtr(InstallDirStep, "step", step);
59 const dest_prefix = self.builder.getInstallPath(self.options.install_dir, self.options.install_subdir);
60 const src_builder = self.override_source_builder orelse self.builder;
61 const full_src_dir = src_builder.pathFromRoot(self.options.source_dir);
62 var src_dir = std.fs.cwd().openIterableDir(full_src_dir, .{}) catch |err| {
63 log.err("InstallDirStep: unable to open source directory '{s}': {s}", .{
64 full_src_dir, @errorName(err),
65 });
66 return error.StepFailed;
67 };
68 defer src_dir.close();
69 var it = try src_dir.walk(self.builder.allocator);
70 next_entry: while (try it.next()) |entry| {
71 for (self.options.exclude_extensions) |ext| {
72 if (mem.endsWith(u8, entry.path, ext)) {
73 continue :next_entry;
74 }
75 }
76
77 const full_path = self.builder.pathJoin(&.{ full_src_dir, entry.path });
78 const dest_path = self.builder.pathJoin(&.{ dest_prefix, entry.path });
79
80 switch (entry.kind) {
81 .Directory => try fs.cwd().makePath(dest_path),
82 .File => {
83 for (self.options.blank_extensions) |ext| {
84 if (mem.endsWith(u8, entry.path, ext)) {
85 try self.builder.truncateFile(dest_path);
86 continue :next_entry;
87 }
88 }
89
90 try self.builder.updateFile(full_path, dest_path);
91 },
92 else => continue,
93 }
94 }
95}
lib/std/build/InstallFileStep.zig deleted-42
...@@ -1,42 +0,0 @@
1const std = @import("../std.zig");
2const build = @import("../build.zig");
3const Step = build.Step;
4const Builder = build.Builder;
5const FileSource = std.build.FileSource;
6const InstallDir = std.build.InstallDir;
7const InstallFileStep = @This();
8
9pub const base_id = .install_file;
10
11step: Step,
12builder: *Builder,
13source: FileSource,
14dir: InstallDir,
15dest_rel_path: []const u8,
16/// This is used by the build system when a file being installed comes from one
17/// package but is being installed by another.
18override_source_builder: ?*Builder = null,
19
20pub fn init(
21 builder: *Builder,
22 source: FileSource,
23 dir: InstallDir,
24 dest_rel_path: []const u8,
25) InstallFileStep {
26 builder.pushInstalledFile(dir, dest_rel_path);
27 return InstallFileStep{
28 .builder = builder,
29 .step = Step.init(.install_file, builder.fmt("install {s} to {s}", .{ source.getDisplayName(), dest_rel_path }), builder.allocator, make),
30 .source = source.dupe(builder),
31 .dir = dir.dupe(builder),
32 .dest_rel_path = builder.dupePath(dest_rel_path),
33 };
34}
35
36fn make(step: *Step) !void {
37 const self = @fieldParentPtr(InstallFileStep, "step", step);
38 const src_builder = self.override_source_builder orelse self.builder;
39 const full_src_path = self.source.getPath(src_builder);
40 const full_dest_path = self.builder.getInstallPath(self.dir, self.dest_rel_path);
41 try self.builder.updateFile(full_src_path, full_dest_path);
42}
lib/std/build/InstallRawStep.zig deleted-106
...@@ -1,106 +0,0 @@
1//! TODO: Rename this to ObjCopyStep now that it invokes the `zig objcopy`
2//! subcommand rather than containing an implementation directly.
3
4const std = @import("std");
5const InstallRawStep = @This();
6
7const Allocator = std.mem.Allocator;
8const ArenaAllocator = std.heap.ArenaAllocator;
9const ArrayListUnmanaged = std.ArrayListUnmanaged;
10const Builder = std.build.Builder;
11const File = std.fs.File;
12const InstallDir = std.build.InstallDir;
13const LibExeObjStep = std.build.LibExeObjStep;
14const Step = std.build.Step;
15const elf = std.elf;
16const fs = std.fs;
17const io = std.io;
18const sort = std.sort;
19
20pub const base_id = .install_raw;
21
22pub const RawFormat = enum {
23 bin,
24 hex,
25};
26
27step: Step,
28builder: *Builder,
29artifact: *LibExeObjStep,
30dest_dir: InstallDir,
31dest_filename: []const u8,
32options: CreateOptions,
33output_file: std.build.GeneratedFile,
34
35pub const CreateOptions = struct {
36 format: ?RawFormat = null,
37 dest_dir: ?InstallDir = null,
38 only_section: ?[]const u8 = null,
39 pad_to: ?u64 = null,
40};
41
42pub fn create(builder: *Builder, artifact: *LibExeObjStep, dest_filename: []const u8, options: CreateOptions) *InstallRawStep {
43 const self = builder.allocator.create(InstallRawStep) catch unreachable;
44 self.* = InstallRawStep{
45 .step = Step.init(.install_raw, builder.fmt("install raw binary {s}", .{artifact.step.name}), builder.allocator, make),
46 .builder = builder,
47 .artifact = artifact,
48 .dest_dir = if (options.dest_dir) |d| d else switch (artifact.kind) {
49 .obj => unreachable,
50 .@"test" => unreachable,
51 .exe, .test_exe => .bin,
52 .lib => unreachable,
53 },
54 .dest_filename = dest_filename,
55 .options = options,
56 .output_file = std.build.GeneratedFile{ .step = &self.step },
57 };
58 self.step.dependOn(&artifact.step);
59
60 builder.pushInstalledFile(self.dest_dir, dest_filename);
61 return self;
62}
63
64pub fn getOutputSource(self: *const InstallRawStep) std.build.FileSource {
65 return std.build.FileSource{ .generated = &self.output_file };
66}
67
68fn make(step: *Step) !void {
69 const self = @fieldParentPtr(InstallRawStep, "step", step);
70 const b = self.builder;
71
72 if (self.artifact.target.getObjectFormat() != .elf) {
73 std.debug.print("InstallRawStep only works with ELF format.\n", .{});
74 return error.InvalidObjectFormat;
75 }
76
77 const full_src_path = self.artifact.getOutputSource().getPath(b);
78 const full_dest_path = b.getInstallPath(self.dest_dir, self.dest_filename);
79 self.output_file.path = full_dest_path;
80
81 fs.cwd().makePath(b.getInstallPath(self.dest_dir, "")) catch unreachable;
82
83 var argv_list = std.ArrayList([]const u8).init(b.allocator);
84 try argv_list.appendSlice(&.{ b.zig_exe, "objcopy" });
85
86 if (self.options.only_section) |only_section| {
87 try argv_list.appendSlice(&.{ "-j", only_section });
88 }
89 if (self.options.pad_to) |pad_to| {
90 try argv_list.appendSlice(&.{
91 "--pad-to",
92 b.fmt("{d}", .{pad_to}),
93 });
94 }
95 if (self.options.format) |format| switch (format) {
96 .bin => try argv_list.appendSlice(&.{ "-O", "binary" }),
97 .hex => try argv_list.appendSlice(&.{ "-O", "hex" }),
98 };
99
100 try argv_list.appendSlice(&.{ full_src_path, full_dest_path });
101 _ = try self.builder.execFromStep(argv_list.items, &self.step);
102}
103
104test {
105 std.testing.refAllDecls(InstallRawStep);
106}
lib/std/build/LibExeObjStep.zig deleted-2110
...@@ -1,2110 +0,0 @@
1const builtin = @import("builtin");
2const std = @import("../std.zig");
3const mem = std.mem;
4const log = std.log;
5const fs = std.fs;
6const assert = std.debug.assert;
7const panic = std.debug.panic;
8const ArrayList = std.ArrayList;
9const StringHashMap = std.StringHashMap;
10const Sha256 = std.crypto.hash.sha2.Sha256;
11const Allocator = mem.Allocator;
12const build = @import("../build.zig");
13const Step = build.Step;
14const Builder = build.Builder;
15const CrossTarget = std.zig.CrossTarget;
16const NativeTargetInfo = std.zig.system.NativeTargetInfo;
17const FileSource = std.build.FileSource;
18const PkgConfigPkg = Builder.PkgConfigPkg;
19const PkgConfigError = Builder.PkgConfigError;
20const ExecError = Builder.ExecError;
21const Pkg = std.build.Pkg;
22const VcpkgRoot = std.build.VcpkgRoot;
23const InstallDir = std.build.InstallDir;
24const InstallArtifactStep = std.build.InstallArtifactStep;
25const GeneratedFile = std.build.GeneratedFile;
26const InstallRawStep = std.build.InstallRawStep;
27const EmulatableRunStep = std.build.EmulatableRunStep;
28const CheckObjectStep = std.build.CheckObjectStep;
29const RunStep = std.build.RunStep;
30const OptionsStep = std.build.OptionsStep;
31const ConfigHeaderStep = std.build.ConfigHeaderStep;
32const LibExeObjStep = @This();
33
34pub const base_id = .lib_exe_obj;
35
36step: Step,
37builder: *Builder,
38name: []const u8,
39target: CrossTarget = CrossTarget{},
40target_info: NativeTargetInfo,
41linker_script: ?FileSource = null,
42version_script: ?[]const u8 = null,
43out_filename: []const u8,
44linkage: ?Linkage = null,
45version: ?std.builtin.Version,
46build_mode: std.builtin.Mode,
47kind: Kind,
48major_only_filename: ?[]const u8,
49name_only_filename: ?[]const u8,
50strip: ?bool,
51unwind_tables: ?bool,
52// keep in sync with src/link.zig:CompressDebugSections
53compress_debug_sections: enum { none, zlib } = .none,
54lib_paths: ArrayList([]const u8),
55rpaths: ArrayList([]const u8),
56framework_dirs: ArrayList([]const u8),
57frameworks: StringHashMap(FrameworkLinkInfo),
58verbose_link: bool,
59verbose_cc: bool,
60emit_analysis: EmitOption = .default,
61emit_asm: EmitOption = .default,
62emit_bin: EmitOption = .default,
63emit_docs: EmitOption = .default,
64emit_implib: EmitOption = .default,
65emit_llvm_bc: EmitOption = .default,
66emit_llvm_ir: EmitOption = .default,
67// Lots of things depend on emit_h having a consistent path,
68// so it is not an EmitOption for now.
69emit_h: bool = false,
70bundle_compiler_rt: ?bool = null,
71single_threaded: ?bool = null,
72stack_protector: ?bool = null,
73disable_stack_probing: bool,
74disable_sanitize_c: bool,
75sanitize_thread: bool,
76rdynamic: bool,
77import_memory: bool = false,
78/// For WebAssembly targets, this will allow for undefined symbols to
79/// be imported from the host environment.
80import_symbols: bool = false,
81import_table: bool = false,
82export_table: bool = false,
83initial_memory: ?u64 = null,
84max_memory: ?u64 = null,
85shared_memory: bool = false,
86global_base: ?u64 = null,
87c_std: Builder.CStd,
88override_lib_dir: ?[]const u8,
89main_pkg_path: ?[]const u8,
90exec_cmd_args: ?[]const ?[]const u8,
91name_prefix: []const u8,
92filter: ?[]const u8,
93test_evented_io: bool = false,
94test_runner: ?[]const u8,
95code_model: std.builtin.CodeModel = .default,
96wasi_exec_model: ?std.builtin.WasiExecModel = null,
97/// Symbols to be exported when compiling to wasm
98export_symbol_names: []const []const u8 = &.{},
99
100root_src: ?FileSource,
101out_h_filename: []const u8,
102out_lib_filename: []const u8,
103out_pdb_filename: []const u8,
104packages: ArrayList(Pkg),
105
106object_src: []const u8,
107
108link_objects: ArrayList(LinkObject),
109include_dirs: ArrayList(IncludeDir),
110c_macros: ArrayList([]const u8),
111installed_headers: ArrayList(*std.build.Step),
112output_dir: ?[]const u8,
113is_linking_libc: bool = false,
114is_linking_libcpp: bool = false,
115vcpkg_bin_path: ?[]const u8 = null,
116
117/// This may be set in order to override the default install directory
118override_dest_dir: ?InstallDir,
119installed_path: ?[]const u8,
120install_step: ?*InstallArtifactStep,
121
122/// Base address for an executable image.
123image_base: ?u64 = null,
124
125libc_file: ?FileSource = null,
126
127valgrind_support: ?bool = null,
128each_lib_rpath: ?bool = null,
129/// On ELF targets, this will emit a link section called ".note.gnu.build-id"
130/// which can be used to coordinate a stripped binary with its debug symbols.
131/// As an example, the bloaty project refuses to work unless its inputs have
132/// build ids, in order to prevent accidental mismatches.
133/// The default is to not include this section because it slows down linking.
134build_id: ?bool = null,
135
136/// Create a .eh_frame_hdr section and a PT_GNU_EH_FRAME segment in the ELF
137/// file.
138link_eh_frame_hdr: bool = false,
139link_emit_relocs: bool = false,
140
141/// Place every function in its own section so that unused ones may be
142/// safely garbage-collected during the linking phase.
143link_function_sections: bool = false,
144
145/// Remove functions and data that are unreachable by the entry point or
146/// exported symbols.
147link_gc_sections: ?bool = null,
148
149linker_allow_shlib_undefined: ?bool = null,
150
151/// Permit read-only relocations in read-only segments. Disallowed by default.
152link_z_notext: bool = false,
153
154/// Force all relocations to be read-only after processing.
155link_z_relro: bool = true,
156
157/// Allow relocations to be lazily processed after load.
158link_z_lazy: bool = false,
159
160/// Common page size
161link_z_common_page_size: ?u64 = null,
162
163/// Maximum page size
164link_z_max_page_size: ?u64 = null,
165
166/// (Darwin) Install name for the dylib
167install_name: ?[]const u8 = null,
168
169/// (Darwin) Path to entitlements file
170entitlements: ?[]const u8 = null,
171
172/// (Darwin) Size of the pagezero segment.
173pagezero_size: ?u64 = null,
174
175/// (Darwin) Search strategy for searching system libraries. Either `paths_first` or `dylibs_first`.
176/// The former lowers to `-search_paths_first` linker option, while the latter to `-search_dylibs_first`
177/// option.
178/// By default, if no option is specified, the linker assumes `paths_first` as the default
179/// search strategy.
180search_strategy: ?enum { paths_first, dylibs_first } = null,
181
182/// (Darwin) Set size of the padding between the end of load commands
183/// and start of `__TEXT,__text` section.
184headerpad_size: ?u32 = null,
185
186/// (Darwin) Automatically Set size of the padding between the end of load commands
187/// and start of `__TEXT,__text` section to a value fitting all paths expanded to MAXPATHLEN.
188headerpad_max_install_names: bool = false,
189
190/// (Darwin) Remove dylibs that are unreachable by the entry point or exported symbols.
191dead_strip_dylibs: bool = false,
192
193/// Position Independent Code
194force_pic: ?bool = null,
195
196/// Position Independent Executable
197pie: ?bool = null,
198
199red_zone: ?bool = null,
200
201omit_frame_pointer: ?bool = null,
202dll_export_fns: ?bool = null,
203
204subsystem: ?std.Target.SubSystem = null,
205
206entry_symbol_name: ?[]const u8 = null,
207
208/// Overrides the default stack size
209stack_size: ?u64 = null,
210
211want_lto: ?bool = null,
212use_llvm: ?bool = null,
213use_lld: ?bool = null,
214
215output_path_source: GeneratedFile,
216output_lib_path_source: GeneratedFile,
217output_h_path_source: GeneratedFile,
218output_pdb_path_source: GeneratedFile,
219
220pub const CSourceFiles = struct {
221 files: []const []const u8,
222 flags: []const []const u8,
223};
224
225pub const CSourceFile = struct {
226 source: FileSource,
227 args: []const []const u8,
228
229 pub fn dupe(self: CSourceFile, b: *Builder) CSourceFile {
230 return .{
231 .source = self.source.dupe(b),
232 .args = b.dupeStrings(self.args),
233 };
234 }
235};
236
237pub const LinkObject = union(enum) {
238 static_path: FileSource,
239 other_step: *LibExeObjStep,
240 system_lib: SystemLib,
241 assembly_file: FileSource,
242 c_source_file: *CSourceFile,
243 c_source_files: *CSourceFiles,
244};
245
246pub const SystemLib = struct {
247 name: []const u8,
248 needed: bool,
249 weak: bool,
250 use_pkg_config: enum {
251 /// Don't use pkg-config, just pass -lfoo where foo is name.
252 no,
253 /// Try to get information on how to link the library from pkg-config.
254 /// If that fails, fall back to passing -lfoo where foo is name.
255 yes,
256 /// Try to get information on how to link the library from pkg-config.
257 /// If that fails, error out.
258 force,
259 },
260};
261
262const FrameworkLinkInfo = struct {
263 needed: bool = false,
264 weak: bool = false,
265};
266
267pub const IncludeDir = union(enum) {
268 raw_path: []const u8,
269 raw_path_system: []const u8,
270 other_step: *LibExeObjStep,
271 config_header_step: *ConfigHeaderStep,
272};
273
274pub const Kind = enum {
275 exe,
276 lib,
277 obj,
278 @"test",
279 test_exe,
280};
281
282pub const SharedLibKind = union(enum) {
283 versioned: std.builtin.Version,
284 unversioned: void,
285};
286
287pub const Linkage = enum { dynamic, static };
288
289pub const EmitOption = union(enum) {
290 default: void,
291 no_emit: void,
292 emit: void,
293 emit_to: []const u8,
294
295 fn getArg(self: @This(), b: *Builder, arg_name: []const u8) ?[]const u8 {
296 return switch (self) {
297 .no_emit => b.fmt("-fno-{s}", .{arg_name}),
298 .default => null,
299 .emit => b.fmt("-f{s}", .{arg_name}),
300 .emit_to => |path| b.fmt("-f{s}={s}", .{ arg_name, path }),
301 };
302 }
303};
304
305pub fn createSharedLibrary(builder: *Builder, name: []const u8, root_src: ?FileSource, kind: SharedLibKind) *LibExeObjStep {
306 return initExtraArgs(builder, name, root_src, .lib, .dynamic, switch (kind) {
307 .versioned => |ver| ver,
308 .unversioned => null,
309 });
310}
311
312pub fn createStaticLibrary(builder: *Builder, name: []const u8, root_src: ?FileSource) *LibExeObjStep {
313 return initExtraArgs(builder, name, root_src, .lib, .static, null);
314}
315
316pub fn createObject(builder: *Builder, name: []const u8, root_src: ?FileSource) *LibExeObjStep {
317 return initExtraArgs(builder, name, root_src, .obj, null, null);
318}
319
320pub fn createExecutable(builder: *Builder, name: []const u8, root_src: ?FileSource) *LibExeObjStep {
321 return initExtraArgs(builder, name, root_src, .exe, null, null);
322}
323
324pub fn createTest(builder: *Builder, name: []const u8, root_src: FileSource) *LibExeObjStep {
325 return initExtraArgs(builder, name, root_src, .@"test", null, null);
326}
327
328pub fn createTestExe(builder: *Builder, name: []const u8, root_src: FileSource) *LibExeObjStep {
329 return initExtraArgs(builder, name, root_src, .test_exe, null, null);
330}
331
332fn initExtraArgs(
333 builder: *Builder,
334 name_raw: []const u8,
335 root_src_raw: ?FileSource,
336 kind: Kind,
337 linkage: ?Linkage,
338 ver: ?std.builtin.Version,
339) *LibExeObjStep {
340 const name = builder.dupe(name_raw);
341 const root_src: ?FileSource = if (root_src_raw) |rsrc| rsrc.dupe(builder) else null;
342 if (mem.indexOf(u8, name, "/") != null or mem.indexOf(u8, name, "\\") != null) {
343 panic("invalid name: '{s}'. It looks like a file path, but it is supposed to be the library or application name.", .{name});
344 }
345
346 const self = builder.allocator.create(LibExeObjStep) catch unreachable;
347 self.* = LibExeObjStep{
348 .strip = null,
349 .unwind_tables = null,
350 .builder = builder,
351 .verbose_link = false,
352 .verbose_cc = false,
353 .build_mode = std.builtin.Mode.Debug,
354 .linkage = linkage,
355 .kind = kind,
356 .root_src = root_src,
357 .name = name,
358 .frameworks = StringHashMap(FrameworkLinkInfo).init(builder.allocator),
359 .step = Step.init(base_id, name, builder.allocator, make),
360 .version = ver,
361 .out_filename = undefined,
362 .out_h_filename = builder.fmt("{s}.h", .{name}),
363 .out_lib_filename = undefined,
364 .out_pdb_filename = builder.fmt("{s}.pdb", .{name}),
365 .major_only_filename = null,
366 .name_only_filename = null,
367 .packages = ArrayList(Pkg).init(builder.allocator),
368 .include_dirs = ArrayList(IncludeDir).init(builder.allocator),
369 .link_objects = ArrayList(LinkObject).init(builder.allocator),
370 .c_macros = ArrayList([]const u8).init(builder.allocator),
371 .lib_paths = ArrayList([]const u8).init(builder.allocator),
372 .rpaths = ArrayList([]const u8).init(builder.allocator),
373 .framework_dirs = ArrayList([]const u8).init(builder.allocator),
374 .installed_headers = ArrayList(*std.build.Step).init(builder.allocator),
375 .object_src = undefined,
376 .c_std = Builder.CStd.C99,
377 .override_lib_dir = null,
378 .main_pkg_path = null,
379 .exec_cmd_args = null,
380 .name_prefix = "",
381 .filter = null,
382 .test_runner = null,
383 .disable_stack_probing = false,
384 .disable_sanitize_c = false,
385 .sanitize_thread = false,
386 .rdynamic = false,
387 .output_dir = null,
388 .override_dest_dir = null,
389 .installed_path = null,
390 .install_step = null,
391
392 .output_path_source = GeneratedFile{ .step = &self.step },
393 .output_lib_path_source = GeneratedFile{ .step = &self.step },
394 .output_h_path_source = GeneratedFile{ .step = &self.step },
395 .output_pdb_path_source = GeneratedFile{ .step = &self.step },
396
397 .target_info = undefined, // populated in computeOutFileNames
398 };
399 self.computeOutFileNames();
400 if (root_src) |rs| rs.addStepDependencies(&self.step);
401 return self;
402}
403
404fn computeOutFileNames(self: *LibExeObjStep) void {
405 self.target_info = NativeTargetInfo.detect(self.target) catch
406 unreachable;
407
408 const target = self.target_info.target;
409
410 self.out_filename = std.zig.binNameAlloc(self.builder.allocator, .{
411 .root_name = self.name,
412 .target = target,
413 .output_mode = switch (self.kind) {
414 .lib => .Lib,
415 .obj => .Obj,
416 .exe, .@"test", .test_exe => .Exe,
417 },
418 .link_mode = if (self.linkage) |some| @as(std.builtin.LinkMode, switch (some) {
419 .dynamic => .Dynamic,
420 .static => .Static,
421 }) else null,
422 .version = self.version,
423 }) catch unreachable;
424
425 if (self.kind == .lib) {
426 if (self.linkage != null and self.linkage.? == .static) {
427 self.out_lib_filename = self.out_filename;
428 } else if (self.version) |version| {
429 if (target.isDarwin()) {
430 self.major_only_filename = self.builder.fmt("lib{s}.{d}.dylib", .{
431 self.name,
432 version.major,
433 });
434 self.name_only_filename = self.builder.fmt("lib{s}.dylib", .{self.name});
435 self.out_lib_filename = self.out_filename;
436 } else if (target.os.tag == .windows) {
437 self.out_lib_filename = self.builder.fmt("{s}.lib", .{self.name});
438 } else {
439 self.major_only_filename = self.builder.fmt("lib{s}.so.{d}", .{ self.name, version.major });
440 self.name_only_filename = self.builder.fmt("lib{s}.so", .{self.name});
441 self.out_lib_filename = self.out_filename;
442 }
443 } else {
444 if (target.isDarwin()) {
445 self.out_lib_filename = self.out_filename;
446 } else if (target.os.tag == .windows) {
447 self.out_lib_filename = self.builder.fmt("{s}.lib", .{self.name});
448 } else {
449 self.out_lib_filename = self.out_filename;
450 }
451 }
452 if (self.output_dir != null) {
453 self.output_lib_path_source.path = self.builder.pathJoin(
454 &.{ self.output_dir.?, self.out_lib_filename },
455 );
456 }
457 }
458}
459
460pub fn setTarget(self: *LibExeObjStep, target: CrossTarget) void {
461 self.target = target;
462 self.computeOutFileNames();
463}
464
465pub fn setOutputDir(self: *LibExeObjStep, dir: []const u8) void {
466 self.output_dir = self.builder.dupePath(dir);
467}
468
469pub fn install(self: *LibExeObjStep) void {
470 self.builder.installArtifact(self);
471}
472
473pub fn installRaw(self: *LibExeObjStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep {
474 return self.builder.installRaw(self, dest_filename, options);
475}
476
477pub fn installHeader(a: *LibExeObjStep, src_path: []const u8, dest_rel_path: []const u8) void {
478 const install_file = a.builder.addInstallHeaderFile(src_path, dest_rel_path);
479 a.builder.getInstallStep().dependOn(&install_file.step);
480 a.installed_headers.append(&install_file.step) catch unreachable;
481}
482
483pub fn installHeadersDirectory(
484 a: *LibExeObjStep,
485 src_dir_path: []const u8,
486 dest_rel_path: []const u8,
487) void {
488 return installHeadersDirectoryOptions(a, .{
489 .source_dir = src_dir_path,
490 .install_dir = .header,
491 .install_subdir = dest_rel_path,
492 });
493}
494
495pub fn installHeadersDirectoryOptions(
496 a: *LibExeObjStep,
497 options: std.build.InstallDirStep.Options,
498) void {
499 const install_dir = a.builder.addInstallDirectory(options);
500 a.builder.getInstallStep().dependOn(&install_dir.step);
501 a.installed_headers.append(&install_dir.step) catch unreachable;
502}
503
504pub fn installLibraryHeaders(a: *LibExeObjStep, l: *LibExeObjStep) void {
505 assert(l.kind == .lib);
506 const install_step = a.builder.getInstallStep();
507 // Copy each element from installed_headers, modifying the builder
508 // to be the new parent's builder.
509 for (l.installed_headers.items) |step| {
510 const step_copy = switch (step.id) {
511 inline .install_file, .install_dir => |id| blk: {
512 const T = id.Type();
513 const ptr = a.builder.allocator.create(T) catch unreachable;
514 ptr.* = step.cast(T).?.*;
515 ptr.override_source_builder = ptr.builder;
516 ptr.builder = a.builder;
517 break :blk &ptr.step;
518 },
519 else => unreachable,
520 };
521 a.installed_headers.append(step_copy) catch unreachable;
522 install_step.dependOn(step_copy);
523 }
524 a.installed_headers.appendSlice(l.installed_headers.items) catch unreachable;
525}
526
527/// Creates a `RunStep` with an executable built with `addExecutable`.
528/// Add command line arguments with `addArg`.
529pub fn run(exe: *LibExeObjStep) *RunStep {
530 assert(exe.kind == .exe or exe.kind == .test_exe);
531
532 // It doesn't have to be native. We catch that if you actually try to run it.
533 // Consider that this is declarative; the run step may not be run unless a user
534 // option is supplied.
535 const run_step = RunStep.create(exe.builder, exe.builder.fmt("run {s}", .{exe.step.name}));
536 run_step.addArtifactArg(exe);
537
538 if (exe.kind == .test_exe) {
539 run_step.addArg(exe.builder.zig_exe);
540 }
541
542 if (exe.vcpkg_bin_path) |path| {
543 run_step.addPathDir(path);
544 }
545
546 return run_step;
547}
548
549/// Creates an `EmulatableRunStep` with an executable built with `addExecutable`.
550/// Allows running foreign binaries through emulation platforms such as Qemu or Rosetta.
551/// When a binary cannot be ran through emulation or the option is disabled, a warning
552/// will be printed and the binary will *NOT* be ran.
553pub fn runEmulatable(exe: *LibExeObjStep) *EmulatableRunStep {
554 assert(exe.kind == .exe or exe.kind == .test_exe);
555
556 const run_step = EmulatableRunStep.create(exe.builder, exe.builder.fmt("run {s}", .{exe.step.name}), exe);
557 if (exe.vcpkg_bin_path) |path| {
558 RunStep.addPathDirInternal(&run_step.step, exe.builder, path);
559 }
560 return run_step;
561}
562
563pub fn checkObject(self: *LibExeObjStep, obj_format: std.Target.ObjectFormat) *CheckObjectStep {
564 return CheckObjectStep.create(self.builder, self.getOutputSource(), obj_format);
565}
566
567pub fn setLinkerScriptPath(self: *LibExeObjStep, source: FileSource) void {
568 self.linker_script = source.dupe(self.builder);
569 source.addStepDependencies(&self.step);
570}
571
572pub fn linkFramework(self: *LibExeObjStep, framework_name: []const u8) void {
573 self.frameworks.put(self.builder.dupe(framework_name), .{}) catch unreachable;
574}
575
576pub fn linkFrameworkNeeded(self: *LibExeObjStep, framework_name: []const u8) void {
577 self.frameworks.put(self.builder.dupe(framework_name), .{
578 .needed = true,
579 }) catch unreachable;
580}
581
582pub fn linkFrameworkWeak(self: *LibExeObjStep, framework_name: []const u8) void {
583 self.frameworks.put(self.builder.dupe(framework_name), .{
584 .weak = true,
585 }) catch unreachable;
586}
587
588/// Returns whether the library, executable, or object depends on a particular system library.
589pub fn dependsOnSystemLibrary(self: LibExeObjStep, name: []const u8) bool {
590 if (isLibCLibrary(name)) {
591 return self.is_linking_libc;
592 }
593 if (isLibCppLibrary(name)) {
594 return self.is_linking_libcpp;
595 }
596 for (self.link_objects.items) |link_object| {
597 switch (link_object) {
598 .system_lib => |lib| if (mem.eql(u8, lib.name, name)) return true,
599 else => continue,
600 }
601 }
602 return false;
603}
604
605pub fn linkLibrary(self: *LibExeObjStep, lib: *LibExeObjStep) void {
606 assert(lib.kind == .lib);
607 self.linkLibraryOrObject(lib);
608}
609
610pub fn isDynamicLibrary(self: *LibExeObjStep) bool {
611 return self.kind == .lib and self.linkage == Linkage.dynamic;
612}
613
614pub fn isStaticLibrary(self: *LibExeObjStep) bool {
615 return self.kind == .lib and self.linkage != Linkage.dynamic;
616}
617
618pub fn producesPdbFile(self: *LibExeObjStep) bool {
619 if (!self.target.isWindows() and !self.target.isUefi()) return false;
620 if (self.strip == true) return false;
621 return self.isDynamicLibrary() or self.kind == .exe or self.kind == .test_exe;
622}
623
624pub fn linkLibC(self: *LibExeObjStep) void {
625 self.is_linking_libc = true;
626}
627
628pub fn linkLibCpp(self: *LibExeObjStep) void {
629 self.is_linking_libcpp = true;
630}
631
632/// If the value is omitted, it is set to 1.
633/// `name` and `value` need not live longer than the function call.
634pub fn defineCMacro(self: *LibExeObjStep, name: []const u8, value: ?[]const u8) void {
635 const macro = std.build.constructCMacro(self.builder.allocator, name, value);
636 self.c_macros.append(macro) catch unreachable;
637}
638
639/// name_and_value looks like [name]=[value]. If the value is omitted, it is set to 1.
640pub fn defineCMacroRaw(self: *LibExeObjStep, name_and_value: []const u8) void {
641 self.c_macros.append(self.builder.dupe(name_and_value)) catch unreachable;
642}
643
644/// This one has no integration with anything, it just puts -lname on the command line.
645/// Prefer to use `linkSystemLibrary` instead.
646pub fn linkSystemLibraryName(self: *LibExeObjStep, name: []const u8) void {
647 self.link_objects.append(.{
648 .system_lib = .{
649 .name = self.builder.dupe(name),
650 .needed = false,
651 .weak = false,
652 .use_pkg_config = .no,
653 },
654 }) catch unreachable;
655}
656
657/// This one has no integration with anything, it just puts -needed-lname on the command line.
658/// Prefer to use `linkSystemLibraryNeeded` instead.
659pub fn linkSystemLibraryNeededName(self: *LibExeObjStep, name: []const u8) void {
660 self.link_objects.append(.{
661 .system_lib = .{
662 .name = self.builder.dupe(name),
663 .needed = true,
664 .weak = false,
665 .use_pkg_config = .no,
666 },
667 }) catch unreachable;
668}
669
670/// Darwin-only. This one has no integration with anything, it just puts -weak-lname on the
671/// command line. Prefer to use `linkSystemLibraryWeak` instead.
672pub fn linkSystemLibraryWeakName(self: *LibExeObjStep, name: []const u8) void {
673 self.link_objects.append(.{
674 .system_lib = .{
675 .name = self.builder.dupe(name),
676 .needed = false,
677 .weak = true,
678 .use_pkg_config = .no,
679 },
680 }) catch unreachable;
681}
682
683/// This links against a system library, exclusively using pkg-config to find the library.
684/// Prefer to use `linkSystemLibrary` instead.
685pub fn linkSystemLibraryPkgConfigOnly(self: *LibExeObjStep, lib_name: []const u8) void {
686 self.link_objects.append(.{
687 .system_lib = .{
688 .name = self.builder.dupe(lib_name),
689 .needed = false,
690 .weak = false,
691 .use_pkg_config = .force,
692 },
693 }) catch unreachable;
694}
695
696/// This links against a system library, exclusively using pkg-config to find the library.
697/// Prefer to use `linkSystemLibraryNeeded` instead.
698pub fn linkSystemLibraryNeededPkgConfigOnly(self: *LibExeObjStep, lib_name: []const u8) void {
699 self.link_objects.append(.{
700 .system_lib = .{
701 .name = self.builder.dupe(lib_name),
702 .needed = true,
703 .weak = false,
704 .use_pkg_config = .force,
705 },
706 }) catch unreachable;
707}
708
709/// Run pkg-config for the given library name and parse the output, returning the arguments
710/// that should be passed to zig to link the given library.
711pub fn runPkgConfig(self: *LibExeObjStep, lib_name: []const u8) ![]const []const u8 {
712 const pkg_name = match: {
713 // First we have to map the library name to pkg config name. Unfortunately,
714 // there are several examples where this is not straightforward:
715 // -lSDL2 -> pkg-config sdl2
716 // -lgdk-3 -> pkg-config gdk-3.0
717 // -latk-1.0 -> pkg-config atk
718 const pkgs = try getPkgConfigList(self.builder);
719
720 // Exact match means instant winner.
721 for (pkgs) |pkg| {
722 if (mem.eql(u8, pkg.name, lib_name)) {
723 break :match pkg.name;
724 }
725 }
726
727 // Next we'll try ignoring case.
728 for (pkgs) |pkg| {
729 if (std.ascii.eqlIgnoreCase(pkg.name, lib_name)) {
730 break :match pkg.name;
731 }
732 }
733
734 // Now try appending ".0".
735 for (pkgs) |pkg| {
736 if (std.ascii.indexOfIgnoreCase(pkg.name, lib_name)) |pos| {
737 if (pos != 0) continue;
738 if (mem.eql(u8, pkg.name[lib_name.len..], ".0")) {
739 break :match pkg.name;
740 }
741 }
742 }
743
744 // Trimming "-1.0".
745 if (mem.endsWith(u8, lib_name, "-1.0")) {
746 const trimmed_lib_name = lib_name[0 .. lib_name.len - "-1.0".len];
747 for (pkgs) |pkg| {
748 if (std.ascii.eqlIgnoreCase(pkg.name, trimmed_lib_name)) {
749 break :match pkg.name;
750 }
751 }
752 }
753
754 return error.PackageNotFound;
755 };
756
757 var code: u8 = undefined;
758 const stdout = if (self.builder.execAllowFail(&[_][]const u8{
759 "pkg-config",
760 pkg_name,
761 "--cflags",
762 "--libs",
763 }, &code, .Ignore)) |stdout| stdout else |err| switch (err) {
764 error.ProcessTerminated => return error.PkgConfigCrashed,
765 error.ExecNotSupported => return error.PkgConfigFailed,
766 error.ExitCodeFailure => return error.PkgConfigFailed,
767 error.FileNotFound => return error.PkgConfigNotInstalled,
768 error.ChildExecFailed => return error.PkgConfigFailed,
769 else => return err,
770 };
771
772 var zig_args = ArrayList([]const u8).init(self.builder.allocator);
773 defer zig_args.deinit();
774
775 var it = mem.tokenize(u8, stdout, " \r\n\t");
776 while (it.next()) |tok| {
777 if (mem.eql(u8, tok, "-I")) {
778 const dir = it.next() orelse return error.PkgConfigInvalidOutput;
779 try zig_args.appendSlice(&[_][]const u8{ "-I", dir });
780 } else if (mem.startsWith(u8, tok, "-I")) {
781 try zig_args.append(tok);
782 } else if (mem.eql(u8, tok, "-L")) {
783 const dir = it.next() orelse return error.PkgConfigInvalidOutput;
784 try zig_args.appendSlice(&[_][]const u8{ "-L", dir });
785 } else if (mem.startsWith(u8, tok, "-L")) {
786 try zig_args.append(tok);
787 } else if (mem.eql(u8, tok, "-l")) {
788 const lib = it.next() orelse return error.PkgConfigInvalidOutput;
789 try zig_args.appendSlice(&[_][]const u8{ "-l", lib });
790 } else if (mem.startsWith(u8, tok, "-l")) {
791 try zig_args.append(tok);
792 } else if (mem.eql(u8, tok, "-D")) {
793 const macro = it.next() orelse return error.PkgConfigInvalidOutput;
794 try zig_args.appendSlice(&[_][]const u8{ "-D", macro });
795 } else if (mem.startsWith(u8, tok, "-D")) {
796 try zig_args.append(tok);
797 } else if (self.builder.verbose) {
798 log.warn("Ignoring pkg-config flag '{s}'", .{tok});
799 }
800 }
801
802 return zig_args.toOwnedSlice();
803}
804
805pub fn linkSystemLibrary(self: *LibExeObjStep, name: []const u8) void {
806 self.linkSystemLibraryInner(name, .{});
807}
808
809pub fn linkSystemLibraryNeeded(self: *LibExeObjStep, name: []const u8) void {
810 self.linkSystemLibraryInner(name, .{ .needed = true });
811}
812
813pub fn linkSystemLibraryWeak(self: *LibExeObjStep, name: []const u8) void {
814 self.linkSystemLibraryInner(name, .{ .weak = true });
815}
816
817fn linkSystemLibraryInner(self: *LibExeObjStep, name: []const u8, opts: struct {
818 needed: bool = false,
819 weak: bool = false,
820}) void {
821 if (isLibCLibrary(name)) {
822 self.linkLibC();
823 return;
824 }
825 if (isLibCppLibrary(name)) {
826 self.linkLibCpp();
827 return;
828 }
829
830 self.link_objects.append(.{
831 .system_lib = .{
832 .name = self.builder.dupe(name),
833 .needed = opts.needed,
834 .weak = opts.weak,
835 .use_pkg_config = .yes,
836 },
837 }) catch unreachable;
838}
839
840pub fn setNamePrefix(self: *LibExeObjStep, text: []const u8) void {
841 assert(self.kind == .@"test" or self.kind == .test_exe);
842 self.name_prefix = self.builder.dupe(text);
843}
844
845pub fn setFilter(self: *LibExeObjStep, text: ?[]const u8) void {
846 assert(self.kind == .@"test" or self.kind == .test_exe);
847 self.filter = if (text) |t| self.builder.dupe(t) else null;
848}
849
850pub fn setTestRunner(self: *LibExeObjStep, path: ?[]const u8) void {
851 assert(self.kind == .@"test" or self.kind == .test_exe);
852 self.test_runner = if (path) |p| self.builder.dupePath(p) else null;
853}
854
855/// Handy when you have many C/C++ source files and want them all to have the same flags.
856pub fn addCSourceFiles(self: *LibExeObjStep, files: []const []const u8, flags: []const []const u8) void {
857 const c_source_files = self.builder.allocator.create(CSourceFiles) catch unreachable;
858
859 const files_copy = self.builder.dupeStrings(files);
860 const flags_copy = self.builder.dupeStrings(flags);
861
862 c_source_files.* = .{
863 .files = files_copy,
864 .flags = flags_copy,
865 };
866 self.link_objects.append(.{ .c_source_files = c_source_files }) catch unreachable;
867}
868
869pub fn addCSourceFile(self: *LibExeObjStep, file: []const u8, flags: []const []const u8) void {
870 self.addCSourceFileSource(.{
871 .args = flags,
872 .source = .{ .path = file },
873 });
874}
875
876pub fn addCSourceFileSource(self: *LibExeObjStep, source: CSourceFile) void {
877 const c_source_file = self.builder.allocator.create(CSourceFile) catch unreachable;
878 c_source_file.* = source.dupe(self.builder);
879 self.link_objects.append(.{ .c_source_file = c_source_file }) catch unreachable;
880 source.source.addStepDependencies(&self.step);
881}
882
883pub fn setVerboseLink(self: *LibExeObjStep, value: bool) void {
884 self.verbose_link = value;
885}
886
887pub fn setVerboseCC(self: *LibExeObjStep, value: bool) void {
888 self.verbose_cc = value;
889}
890
891pub fn setBuildMode(self: *LibExeObjStep, mode: std.builtin.Mode) void {
892 self.build_mode = mode;
893}
894
895pub fn overrideZigLibDir(self: *LibExeObjStep, dir_path: []const u8) void {
896 self.override_lib_dir = self.builder.dupePath(dir_path);
897}
898
899pub fn setMainPkgPath(self: *LibExeObjStep, dir_path: []const u8) void {
900 self.main_pkg_path = self.builder.dupePath(dir_path);
901}
902
903pub fn setLibCFile(self: *LibExeObjStep, libc_file: ?FileSource) void {
904 self.libc_file = if (libc_file) |f| f.dupe(self.builder) else null;
905}
906
907/// Returns the generated executable, library or object file.
908/// To run an executable built with zig build, use `run`, or create an install step and invoke it.
909pub fn getOutputSource(self: *LibExeObjStep) FileSource {
910 return FileSource{ .generated = &self.output_path_source };
911}
912
913/// Returns the generated import library. This function can only be called for libraries.
914pub fn getOutputLibSource(self: *LibExeObjStep) FileSource {
915 assert(self.kind == .lib);
916 return FileSource{ .generated = &self.output_lib_path_source };
917}
918
919/// Returns the generated header file.
920/// This function can only be called for libraries or object files which have `emit_h` set.
921pub fn getOutputHSource(self: *LibExeObjStep) FileSource {
922 assert(self.kind != .exe and self.kind != .test_exe and self.kind != .@"test");
923 assert(self.emit_h);
924 return FileSource{ .generated = &self.output_h_path_source };
925}
926
927/// Returns the generated PDB file. This function can only be called for Windows and UEFI.
928pub fn getOutputPdbSource(self: *LibExeObjStep) FileSource {
929 // TODO: Is this right? Isn't PDB for *any* PE/COFF file?
930 assert(self.target.isWindows() or self.target.isUefi());
931 return FileSource{ .generated = &self.output_pdb_path_source };
932}
933
934pub fn addAssemblyFile(self: *LibExeObjStep, path: []const u8) void {
935 self.link_objects.append(.{
936 .assembly_file = .{ .path = self.builder.dupe(path) },
937 }) catch unreachable;
938}
939
940pub fn addAssemblyFileSource(self: *LibExeObjStep, source: FileSource) void {
941 const source_duped = source.dupe(self.builder);
942 self.link_objects.append(.{ .assembly_file = source_duped }) catch unreachable;
943 source_duped.addStepDependencies(&self.step);
944}
945
946pub fn addObjectFile(self: *LibExeObjStep, source_file: []const u8) void {
947 self.addObjectFileSource(.{ .path = source_file });
948}
949
950pub fn addObjectFileSource(self: *LibExeObjStep, source: FileSource) void {
951 self.link_objects.append(.{ .static_path = source.dupe(self.builder) }) catch unreachable;
952 source.addStepDependencies(&self.step);
953}
954
955pub fn addObject(self: *LibExeObjStep, obj: *LibExeObjStep) void {
956 assert(obj.kind == .obj);
957 self.linkLibraryOrObject(obj);
958}
959
960pub const addSystemIncludeDir = @compileError("deprecated; use addSystemIncludePath");
961pub const addIncludeDir = @compileError("deprecated; use addIncludePath");
962pub const addLibPath = @compileError("deprecated, use addLibraryPath");
963pub const addFrameworkDir = @compileError("deprecated, use addFrameworkPath");
964
965pub fn addSystemIncludePath(self: *LibExeObjStep, path: []const u8) void {
966 self.include_dirs.append(IncludeDir{ .raw_path_system = self.builder.dupe(path) }) catch unreachable;
967}
968
969pub fn addIncludePath(self: *LibExeObjStep, path: []const u8) void {
970 self.include_dirs.append(IncludeDir{ .raw_path = self.builder.dupe(path) }) catch unreachable;
971}
972
973pub fn addConfigHeader(self: *LibExeObjStep, config_header: *ConfigHeaderStep) void {
974 self.step.dependOn(&config_header.step);
975 self.include_dirs.append(.{ .config_header_step = config_header }) catch @panic("OOM");
976}
977
978pub fn addLibraryPath(self: *LibExeObjStep, path: []const u8) void {
979 self.lib_paths.append(self.builder.dupe(path)) catch unreachable;
980}
981
982pub fn addRPath(self: *LibExeObjStep, path: []const u8) void {
983 self.rpaths.append(self.builder.dupe(path)) catch unreachable;
984}
985
986pub fn addFrameworkPath(self: *LibExeObjStep, dir_path: []const u8) void {
987 self.framework_dirs.append(self.builder.dupe(dir_path)) catch unreachable;
988}
989
990pub fn addPackage(self: *LibExeObjStep, package: Pkg) void {
991 self.packages.append(self.builder.dupePkg(package)) catch unreachable;
992 self.addRecursiveBuildDeps(package);
993}
994
995pub fn addOptions(self: *LibExeObjStep, package_name: []const u8, options: *OptionsStep) void {
996 self.addPackage(options.getPackage(package_name));
997}
998
999fn addRecursiveBuildDeps(self: *LibExeObjStep, package: Pkg) void {
1000 package.source.addStepDependencies(&self.step);
1001 if (package.dependencies) |deps| {
1002 for (deps) |dep| {
1003 self.addRecursiveBuildDeps(dep);
1004 }
1005 }
1006}
1007
1008pub fn addPackagePath(self: *LibExeObjStep, name: []const u8, pkg_index_path: []const u8) void {
1009 self.addPackage(Pkg{
1010 .name = self.builder.dupe(name),
1011 .source = .{ .path = self.builder.dupe(pkg_index_path) },
1012 });
1013}
1014
1015/// If Vcpkg was found on the system, it will be added to include and lib
1016/// paths for the specified target.
1017pub fn addVcpkgPaths(self: *LibExeObjStep, linkage: LibExeObjStep.Linkage) !void {
1018 // Ideally in the Unattempted case we would call the function recursively
1019 // after findVcpkgRoot and have only one switch statement, but the compiler
1020 // cannot resolve the error set.
1021 switch (self.builder.vcpkg_root) {
1022 .unattempted => {
1023 self.builder.vcpkg_root = if (try findVcpkgRoot(self.builder.allocator)) |root|
1024 VcpkgRoot{ .found = root }
1025 else
1026 .not_found;
1027 },
1028 .not_found => return error.VcpkgNotFound,
1029 .found => {},
1030 }
1031
1032 switch (self.builder.vcpkg_root) {
1033 .unattempted => unreachable,
1034 .not_found => return error.VcpkgNotFound,
1035 .found => |root| {
1036 const allocator = self.builder.allocator;
1037 const triplet = try self.target.vcpkgTriplet(allocator, if (linkage == .static) .Static else .Dynamic);
1038 defer self.builder.allocator.free(triplet);
1039
1040 const include_path = self.builder.pathJoin(&.{ root, "installed", triplet, "include" });
1041 errdefer allocator.free(include_path);
1042 try self.include_dirs.append(IncludeDir{ .raw_path = include_path });
1043
1044 const lib_path = self.builder.pathJoin(&.{ root, "installed", triplet, "lib" });
1045 try self.lib_paths.append(lib_path);
1046
1047 self.vcpkg_bin_path = self.builder.pathJoin(&.{ root, "installed", triplet, "bin" });
1048 },
1049 }
1050}
1051
1052pub fn setExecCmd(self: *LibExeObjStep, args: []const ?[]const u8) void {
1053 assert(self.kind == .@"test");
1054 const duped_args = self.builder.allocator.alloc(?[]u8, args.len) catch unreachable;
1055 for (args) |arg, i| {
1056 duped_args[i] = if (arg) |a| self.builder.dupe(a) else null;
1057 }
1058 self.exec_cmd_args = duped_args;
1059}
1060
1061fn linkLibraryOrObject(self: *LibExeObjStep, other: *LibExeObjStep) void {
1062 self.step.dependOn(&other.step);
1063 self.link_objects.append(.{ .other_step = other }) catch unreachable;
1064 self.include_dirs.append(.{ .other_step = other }) catch unreachable;
1065}
1066
1067fn makePackageCmd(self: *LibExeObjStep, pkg: Pkg, zig_args: *ArrayList([]const u8)) error{OutOfMemory}!void {
1068 const builder = self.builder;
1069
1070 try zig_args.append("--pkg-begin");
1071 try zig_args.append(pkg.name);
1072 try zig_args.append(builder.pathFromRoot(pkg.source.getPath(self.builder)));
1073
1074 if (pkg.dependencies) |dependencies| {
1075 for (dependencies) |sub_pkg| {
1076 try self.makePackageCmd(sub_pkg, zig_args);
1077 }
1078 }
1079
1080 try zig_args.append("--pkg-end");
1081}
1082
1083fn make(step: *Step) !void {
1084 const self = @fieldParentPtr(LibExeObjStep, "step", step);
1085 const builder = self.builder;
1086
1087 if (self.root_src == null and self.link_objects.items.len == 0) {
1088 log.err("{s}: linker needs 1 or more objects to link", .{self.step.name});
1089 return error.NeedAnObject;
1090 }
1091
1092 var zig_args = ArrayList([]const u8).init(builder.allocator);
1093 defer zig_args.deinit();
1094
1095 zig_args.append(builder.zig_exe) catch unreachable;
1096
1097 const cmd = switch (self.kind) {
1098 .lib => "build-lib",
1099 .exe => "build-exe",
1100 .obj => "build-obj",
1101 .@"test" => "test",
1102 .test_exe => "test",
1103 };
1104 zig_args.append(cmd) catch unreachable;
1105
1106 if (builder.color != .auto) {
1107 try zig_args.append("--color");
1108 try zig_args.append(@tagName(builder.color));
1109 }
1110
1111 if (builder.reference_trace) |some| {
1112 try zig_args.append(try std.fmt.allocPrint(builder.allocator, "-freference-trace={d}", .{some}));
1113 }
1114
1115 try addFlag(&zig_args, "LLVM", self.use_llvm);
1116 try addFlag(&zig_args, "LLD", self.use_lld);
1117
1118 if (self.target.ofmt) |ofmt| {
1119 try zig_args.append(try std.fmt.allocPrint(builder.allocator, "-ofmt={s}", .{@tagName(ofmt)}));
1120 }
1121
1122 if (self.entry_symbol_name) |entry| {
1123 try zig_args.append("--entry");
1124 try zig_args.append(entry);
1125 }
1126
1127 if (self.stack_size) |stack_size| {
1128 try zig_args.append("--stack");
1129 try zig_args.append(try std.fmt.allocPrint(builder.allocator, "{}", .{stack_size}));
1130 }
1131
1132 if (self.root_src) |root_src| try zig_args.append(root_src.getPath(builder));
1133
1134 // We will add link objects from transitive dependencies, but we want to keep
1135 // all link objects in the same order provided.
1136 // This array is used to keep self.link_objects immutable.
1137 var transitive_deps: TransitiveDeps = .{
1138 .link_objects = ArrayList(LinkObject).init(builder.allocator),
1139 .seen_system_libs = StringHashMap(void).init(builder.allocator),
1140 .seen_steps = std.AutoHashMap(*const Step, void).init(builder.allocator),
1141 .is_linking_libcpp = self.is_linking_libcpp,
1142 .is_linking_libc = self.is_linking_libc,
1143 .frameworks = &self.frameworks,
1144 };
1145
1146 try transitive_deps.seen_steps.put(&self.step, {});
1147 try transitive_deps.add(self.link_objects.items);
1148
1149 var prev_has_extra_flags = false;
1150
1151 for (transitive_deps.link_objects.items) |link_object| {
1152 switch (link_object) {
1153 .static_path => |static_path| try zig_args.append(static_path.getPath(builder)),
1154
1155 .other_step => |other| switch (other.kind) {
1156 .exe => @panic("Cannot link with an executable build artifact"),
1157 .test_exe => @panic("Cannot link with an executable build artifact"),
1158 .@"test" => @panic("Cannot link with a test"),
1159 .obj => {
1160 try zig_args.append(other.getOutputSource().getPath(builder));
1161 },
1162 .lib => l: {
1163 if (self.isStaticLibrary() and other.isStaticLibrary()) {
1164 // Avoid putting a static library inside a static library.
1165 break :l;
1166 }
1167
1168 const full_path_lib = other.getOutputLibSource().getPath(builder);
1169 try zig_args.append(full_path_lib);
1170
1171 if (other.linkage == Linkage.dynamic and !self.target.isWindows()) {
1172 if (fs.path.dirname(full_path_lib)) |dirname| {
1173 try zig_args.append("-rpath");
1174 try zig_args.append(dirname);
1175 }
1176 }
1177 },
1178 },
1179
1180 .system_lib => |system_lib| {
1181 const prefix: []const u8 = prefix: {
1182 if (system_lib.needed) break :prefix "-needed-l";
1183 if (system_lib.weak) {
1184 if (self.target.isDarwin()) break :prefix "-weak-l";
1185 log.warn("Weak library import used for a non-darwin target, this will be converted to normally library import `-lname`", .{});
1186 }
1187 break :prefix "-l";
1188 };
1189 switch (system_lib.use_pkg_config) {
1190 .no => try zig_args.append(builder.fmt("{s}{s}", .{ prefix, system_lib.name })),
1191 .yes, .force => {
1192 if (self.runPkgConfig(system_lib.name)) |args| {
1193 try zig_args.appendSlice(args);
1194 } else |err| switch (err) {
1195 error.PkgConfigInvalidOutput,
1196 error.PkgConfigCrashed,
1197 error.PkgConfigFailed,
1198 error.PkgConfigNotInstalled,
1199 error.PackageNotFound,
1200 => switch (system_lib.use_pkg_config) {
1201 .yes => {
1202 // pkg-config failed, so fall back to linking the library
1203 // by name directly.
1204 try zig_args.append(builder.fmt("{s}{s}", .{
1205 prefix,
1206 system_lib.name,
1207 }));
1208 },
1209 .force => {
1210 panic("pkg-config failed for library {s}", .{system_lib.name});
1211 },
1212 .no => unreachable,
1213 },
1214
1215 else => |e| return e,
1216 }
1217 },
1218 }
1219 },
1220
1221 .assembly_file => |asm_file| {
1222 if (prev_has_extra_flags) {
1223 try zig_args.append("-extra-cflags");
1224 try zig_args.append("--");
1225 prev_has_extra_flags = false;
1226 }
1227 try zig_args.append(asm_file.getPath(builder));
1228 },
1229
1230 .c_source_file => |c_source_file| {
1231 if (c_source_file.args.len == 0) {
1232 if (prev_has_extra_flags) {
1233 try zig_args.append("-cflags");
1234 try zig_args.append("--");
1235 prev_has_extra_flags = false;
1236 }
1237 } else {
1238 try zig_args.append("-cflags");
1239 for (c_source_file.args) |arg| {
1240 try zig_args.append(arg);
1241 }
1242 try zig_args.append("--");
1243 }
1244 try zig_args.append(c_source_file.source.getPath(builder));
1245 },
1246
1247 .c_source_files => |c_source_files| {
1248 if (c_source_files.flags.len == 0) {
1249 if (prev_has_extra_flags) {
1250 try zig_args.append("-cflags");
1251 try zig_args.append("--");
1252 prev_has_extra_flags = false;
1253 }
1254 } else {
1255 try zig_args.append("-cflags");
1256 for (c_source_files.flags) |flag| {
1257 try zig_args.append(flag);
1258 }
1259 try zig_args.append("--");
1260 }
1261 for (c_source_files.files) |file| {
1262 try zig_args.append(builder.pathFromRoot(file));
1263 }
1264 },
1265 }
1266 }
1267
1268 if (transitive_deps.is_linking_libcpp) {
1269 try zig_args.append("-lc++");
1270 }
1271
1272 if (transitive_deps.is_linking_libc) {
1273 try zig_args.append("-lc");
1274 }
1275
1276 if (self.image_base) |image_base| {
1277 try zig_args.append("--image-base");
1278 try zig_args.append(builder.fmt("0x{x}", .{image_base}));
1279 }
1280
1281 if (self.filter) |filter| {
1282 try zig_args.append("--test-filter");
1283 try zig_args.append(filter);
1284 }
1285
1286 if (self.test_evented_io) {
1287 try zig_args.append("--test-evented-io");
1288 }
1289
1290 if (self.name_prefix.len != 0) {
1291 try zig_args.append("--test-name-prefix");
1292 try zig_args.append(self.name_prefix);
1293 }
1294
1295 if (self.test_runner) |test_runner| {
1296 try zig_args.append("--test-runner");
1297 try zig_args.append(builder.pathFromRoot(test_runner));
1298 }
1299
1300 for (builder.debug_log_scopes) |log_scope| {
1301 try zig_args.append("--debug-log");
1302 try zig_args.append(log_scope);
1303 }
1304
1305 if (builder.debug_compile_errors) {
1306 try zig_args.append("--debug-compile-errors");
1307 }
1308
1309 if (builder.verbose_cimport) zig_args.append("--verbose-cimport") catch unreachable;
1310 if (builder.verbose_air) zig_args.append("--verbose-air") catch unreachable;
1311 if (builder.verbose_llvm_ir) zig_args.append("--verbose-llvm-ir") catch unreachable;
1312 if (builder.verbose_link or self.verbose_link) zig_args.append("--verbose-link") catch unreachable;
1313 if (builder.verbose_cc or self.verbose_cc) zig_args.append("--verbose-cc") catch unreachable;
1314 if (builder.verbose_llvm_cpu_features) zig_args.append("--verbose-llvm-cpu-features") catch unreachable;
1315
1316 if (self.emit_analysis.getArg(builder, "emit-analysis")) |arg| try zig_args.append(arg);
1317 if (self.emit_asm.getArg(builder, "emit-asm")) |arg| try zig_args.append(arg);
1318 if (self.emit_bin.getArg(builder, "emit-bin")) |arg| try zig_args.append(arg);
1319 if (self.emit_docs.getArg(builder, "emit-docs")) |arg| try zig_args.append(arg);
1320 if (self.emit_implib.getArg(builder, "emit-implib")) |arg| try zig_args.append(arg);
1321 if (self.emit_llvm_bc.getArg(builder, "emit-llvm-bc")) |arg| try zig_args.append(arg);
1322 if (self.emit_llvm_ir.getArg(builder, "emit-llvm-ir")) |arg| try zig_args.append(arg);
1323
1324 if (self.emit_h) try zig_args.append("-femit-h");
1325
1326 try addFlag(&zig_args, "strip", self.strip);
1327 try addFlag(&zig_args, "unwind-tables", self.unwind_tables);
1328
1329 switch (self.compress_debug_sections) {
1330 .none => {},
1331 .zlib => try zig_args.append("--compress-debug-sections=zlib"),
1332 }
1333
1334 if (self.link_eh_frame_hdr) {
1335 try zig_args.append("--eh-frame-hdr");
1336 }
1337 if (self.link_emit_relocs) {
1338 try zig_args.append("--emit-relocs");
1339 }
1340 if (self.link_function_sections) {
1341 try zig_args.append("-ffunction-sections");
1342 }
1343 if (self.link_gc_sections) |x| {
1344 try zig_args.append(if (x) "--gc-sections" else "--no-gc-sections");
1345 }
1346 if (self.linker_allow_shlib_undefined) |x| {
1347 try zig_args.append(if (x) "-fallow-shlib-undefined" else "-fno-allow-shlib-undefined");
1348 }
1349 if (self.link_z_notext) {
1350 try zig_args.append("-z");
1351 try zig_args.append("notext");
1352 }
1353 if (!self.link_z_relro) {
1354 try zig_args.append("-z");
1355 try zig_args.append("norelro");
1356 }
1357 if (self.link_z_lazy) {
1358 try zig_args.append("-z");
1359 try zig_args.append("lazy");
1360 }
1361 if (self.link_z_common_page_size) |size| {
1362 try zig_args.append("-z");
1363 try zig_args.append(builder.fmt("common-page-size={d}", .{size}));
1364 }
1365 if (self.link_z_max_page_size) |size| {
1366 try zig_args.append("-z");
1367 try zig_args.append(builder.fmt("max-page-size={d}", .{size}));
1368 }
1369
1370 if (self.libc_file) |libc_file| {
1371 try zig_args.append("--libc");
1372 try zig_args.append(libc_file.getPath(builder));
1373 } else if (builder.libc_file) |libc_file| {
1374 try zig_args.append("--libc");
1375 try zig_args.append(libc_file);
1376 }
1377
1378 switch (self.build_mode) {
1379 .Debug => {}, // Skip since it's the default.
1380 else => zig_args.append(builder.fmt("-O{s}", .{@tagName(self.build_mode)})) catch unreachable,
1381 }
1382
1383 try zig_args.append("--cache-dir");
1384 try zig_args.append(builder.pathFromRoot(builder.cache_root));
1385
1386 try zig_args.append("--global-cache-dir");
1387 try zig_args.append(builder.pathFromRoot(builder.global_cache_root));
1388
1389 zig_args.append("--name") catch unreachable;
1390 zig_args.append(self.name) catch unreachable;
1391
1392 if (self.linkage) |some| switch (some) {
1393 .dynamic => try zig_args.append("-dynamic"),
1394 .static => try zig_args.append("-static"),
1395 };
1396 if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic) {
1397 if (self.version) |version| {
1398 zig_args.append("--version") catch unreachable;
1399 zig_args.append(builder.fmt("{}", .{version})) catch unreachable;
1400 }
1401
1402 if (self.target.isDarwin()) {
1403 const install_name = self.install_name orelse builder.fmt("@rpath/{s}{s}{s}", .{
1404 self.target.libPrefix(),
1405 self.name,
1406 self.target.dynamicLibSuffix(),
1407 });
1408 try zig_args.append("-install_name");
1409 try zig_args.append(install_name);
1410 }
1411 }
1412
1413 if (self.entitlements) |entitlements| {
1414 try zig_args.appendSlice(&[_][]const u8{ "--entitlements", entitlements });
1415 }
1416 if (self.pagezero_size) |pagezero_size| {
1417 const size = try std.fmt.allocPrint(builder.allocator, "{x}", .{pagezero_size});
1418 try zig_args.appendSlice(&[_][]const u8{ "-pagezero_size", size });
1419 }
1420 if (self.search_strategy) |strat| switch (strat) {
1421 .paths_first => try zig_args.append("-search_paths_first"),
1422 .dylibs_first => try zig_args.append("-search_dylibs_first"),
1423 };
1424 if (self.headerpad_size) |headerpad_size| {
1425 const size = try std.fmt.allocPrint(builder.allocator, "{x}", .{headerpad_size});
1426 try zig_args.appendSlice(&[_][]const u8{ "-headerpad", size });
1427 }
1428 if (self.headerpad_max_install_names) {
1429 try zig_args.append("-headerpad_max_install_names");
1430 }
1431 if (self.dead_strip_dylibs) {
1432 try zig_args.append("-dead_strip_dylibs");
1433 }
1434
1435 try addFlag(&zig_args, "compiler-rt", self.bundle_compiler_rt);
1436 try addFlag(&zig_args, "single-threaded", self.single_threaded);
1437 if (self.disable_stack_probing) {
1438 try zig_args.append("-fno-stack-check");
1439 }
1440 try addFlag(&zig_args, "stack-protector", self.stack_protector);
1441 if (self.red_zone) |red_zone| {
1442 if (red_zone) {
1443 try zig_args.append("-mred-zone");
1444 } else {
1445 try zig_args.append("-mno-red-zone");
1446 }
1447 }
1448 try addFlag(&zig_args, "omit-frame-pointer", self.omit_frame_pointer);
1449 try addFlag(&zig_args, "dll-export-fns", self.dll_export_fns);
1450
1451 if (self.disable_sanitize_c) {
1452 try zig_args.append("-fno-sanitize-c");
1453 }
1454 if (self.sanitize_thread) {
1455 try zig_args.append("-fsanitize-thread");
1456 }
1457 if (self.rdynamic) {
1458 try zig_args.append("-rdynamic");
1459 }
1460 if (self.import_memory) {
1461 try zig_args.append("--import-memory");
1462 }
1463 if (self.import_symbols) {
1464 try zig_args.append("--import-symbols");
1465 }
1466 if (self.import_table) {
1467 try zig_args.append("--import-table");
1468 }
1469 if (self.export_table) {
1470 try zig_args.append("--export-table");
1471 }
1472 if (self.initial_memory) |initial_memory| {
1473 try zig_args.append(builder.fmt("--initial-memory={d}", .{initial_memory}));
1474 }
1475 if (self.max_memory) |max_memory| {
1476 try zig_args.append(builder.fmt("--max-memory={d}", .{max_memory}));
1477 }
1478 if (self.shared_memory) {
1479 try zig_args.append("--shared-memory");
1480 }
1481 if (self.global_base) |global_base| {
1482 try zig_args.append(builder.fmt("--global-base={d}", .{global_base}));
1483 }
1484
1485 if (self.code_model != .default) {
1486 try zig_args.append("-mcmodel");
1487 try zig_args.append(@tagName(self.code_model));
1488 }
1489 if (self.wasi_exec_model) |model| {
1490 try zig_args.append(builder.fmt("-mexec-model={s}", .{@tagName(model)}));
1491 }
1492 for (self.export_symbol_names) |symbol_name| {
1493 try zig_args.append(builder.fmt("--export={s}", .{symbol_name}));
1494 }
1495
1496 if (!self.target.isNative()) {
1497 try zig_args.append("-target");
1498 try zig_args.append(try self.target.zigTriple(builder.allocator));
1499
1500 // TODO this logic can disappear if cpu model + features becomes part of the target triple
1501 const cross = self.target.toTarget();
1502 const all_features = cross.cpu.arch.allFeaturesList();
1503 var populated_cpu_features = cross.cpu.model.features;
1504 populated_cpu_features.populateDependencies(all_features);
1505
1506 if (populated_cpu_features.eql(cross.cpu.features)) {
1507 // The CPU name alone is sufficient.
1508 try zig_args.append("-mcpu");
1509 try zig_args.append(cross.cpu.model.name);
1510 } else {
1511 var mcpu_buffer = ArrayList(u8).init(builder.allocator);
1512
1513 try mcpu_buffer.writer().print("-mcpu={s}", .{cross.cpu.model.name});
1514
1515 for (all_features) |feature, i_usize| {
1516 const i = @intCast(std.Target.Cpu.Feature.Set.Index, i_usize);
1517 const in_cpu_set = populated_cpu_features.isEnabled(i);
1518 const in_actual_set = cross.cpu.features.isEnabled(i);
1519 if (in_cpu_set and !in_actual_set) {
1520 try mcpu_buffer.writer().print("-{s}", .{feature.name});
1521 } else if (!in_cpu_set and in_actual_set) {
1522 try mcpu_buffer.writer().print("+{s}", .{feature.name});
1523 }
1524 }
1525
1526 try zig_args.append(try mcpu_buffer.toOwnedSlice());
1527 }
1528
1529 if (self.target.dynamic_linker.get()) |dynamic_linker| {
1530 try zig_args.append("--dynamic-linker");
1531 try zig_args.append(dynamic_linker);
1532 }
1533 }
1534
1535 if (self.linker_script) |linker_script| {
1536 try zig_args.append("--script");
1537 try zig_args.append(linker_script.getPath(builder));
1538 }
1539
1540 if (self.version_script) |version_script| {
1541 try zig_args.append("--version-script");
1542 try zig_args.append(builder.pathFromRoot(version_script));
1543 }
1544
1545 if (self.kind == .@"test") {
1546 if (self.exec_cmd_args) |exec_cmd_args| {
1547 for (exec_cmd_args) |cmd_arg| {
1548 if (cmd_arg) |arg| {
1549 try zig_args.append("--test-cmd");
1550 try zig_args.append(arg);
1551 } else {
1552 try zig_args.append("--test-cmd-bin");
1553 }
1554 }
1555 } else {
1556 const need_cross_glibc = self.target.isGnuLibC() and transitive_deps.is_linking_libc;
1557
1558 switch (builder.host.getExternalExecutor(self.target_info, .{
1559 .qemu_fixes_dl = need_cross_glibc and builder.glibc_runtimes_dir != null,
1560 .link_libc = transitive_deps.is_linking_libc,
1561 })) {
1562 .native => {},
1563 .bad_dl, .bad_os_or_cpu => {
1564 try zig_args.append("--test-no-exec");
1565 },
1566 .rosetta => if (builder.enable_rosetta) {
1567 try zig_args.append("--test-cmd-bin");
1568 } else {
1569 try zig_args.append("--test-no-exec");
1570 },
1571 .qemu => |bin_name| ok: {
1572 if (builder.enable_qemu) qemu: {
1573 const glibc_dir_arg = if (need_cross_glibc)
1574 builder.glibc_runtimes_dir orelse break :qemu
1575 else
1576 null;
1577 try zig_args.append("--test-cmd");
1578 try zig_args.append(bin_name);
1579 if (glibc_dir_arg) |dir| {
1580 // TODO look into making this a call to `linuxTriple`. This
1581 // needs the directory to be called "i686" rather than
1582 // "x86" which is why we do it manually here.
1583 const fmt_str = "{s}" ++ fs.path.sep_str ++ "{s}-{s}-{s}";
1584 const cpu_arch = self.target.getCpuArch();
1585 const os_tag = self.target.getOsTag();
1586 const abi = self.target.getAbi();
1587 const cpu_arch_name: []const u8 = if (cpu_arch == .x86)
1588 "i686"
1589 else
1590 @tagName(cpu_arch);
1591 const full_dir = try std.fmt.allocPrint(builder.allocator, fmt_str, .{
1592 dir, cpu_arch_name, @tagName(os_tag), @tagName(abi),
1593 });
1594
1595 try zig_args.append("--test-cmd");
1596 try zig_args.append("-L");
1597 try zig_args.append("--test-cmd");
1598 try zig_args.append(full_dir);
1599 }
1600 try zig_args.append("--test-cmd-bin");
1601 break :ok;
1602 }
1603 try zig_args.append("--test-no-exec");
1604 },
1605 .wine => |bin_name| if (builder.enable_wine) {
1606 try zig_args.append("--test-cmd");
1607 try zig_args.append(bin_name);
1608 try zig_args.append("--test-cmd-bin");
1609 } else {
1610 try zig_args.append("--test-no-exec");
1611 },
1612 .wasmtime => |bin_name| if (builder.enable_wasmtime) {
1613 try zig_args.append("--test-cmd");
1614 try zig_args.append(bin_name);
1615 try zig_args.append("--test-cmd");
1616 try zig_args.append("--dir=.");
1617 try zig_args.append("--test-cmd-bin");
1618 } else {
1619 try zig_args.append("--test-no-exec");
1620 },
1621 .darling => |bin_name| if (builder.enable_darling) {
1622 try zig_args.append("--test-cmd");
1623 try zig_args.append(bin_name);
1624 try zig_args.append("--test-cmd-bin");
1625 } else {
1626 try zig_args.append("--test-no-exec");
1627 },
1628 }
1629 }
1630 } else if (self.kind == .test_exe) {
1631 try zig_args.append("--test-no-exec");
1632 }
1633
1634 for (self.packages.items) |pkg| {
1635 try self.makePackageCmd(pkg, &zig_args);
1636 }
1637
1638 for (self.include_dirs.items) |include_dir| {
1639 switch (include_dir) {
1640 .raw_path => |include_path| {
1641 try zig_args.append("-I");
1642 try zig_args.append(builder.pathFromRoot(include_path));
1643 },
1644 .raw_path_system => |include_path| {
1645 if (builder.sysroot != null) {
1646 try zig_args.append("-iwithsysroot");
1647 } else {
1648 try zig_args.append("-isystem");
1649 }
1650
1651 const resolved_include_path = builder.pathFromRoot(include_path);
1652
1653 const common_include_path = if (builtin.os.tag == .windows and builder.sysroot != null and fs.path.isAbsolute(resolved_include_path)) blk: {
1654 // We need to check for disk designator and strip it out from dir path so
1655 // that zig/clang can concat resolved_include_path with sysroot.
1656 const disk_designator = fs.path.diskDesignatorWindows(resolved_include_path);
1657
1658 if (mem.indexOf(u8, resolved_include_path, disk_designator)) |where| {
1659 break :blk resolved_include_path[where + disk_designator.len ..];
1660 }
1661
1662 break :blk resolved_include_path;
1663 } else resolved_include_path;
1664
1665 try zig_args.append(common_include_path);
1666 },
1667 .other_step => |other| {
1668 if (other.emit_h) {
1669 const h_path = other.getOutputHSource().getPath(builder);
1670 try zig_args.append("-isystem");
1671 try zig_args.append(fs.path.dirname(h_path).?);
1672 }
1673 if (other.installed_headers.items.len > 0) {
1674 for (other.installed_headers.items) |install_step| {
1675 try install_step.make();
1676 }
1677 try zig_args.append("-I");
1678 try zig_args.append(builder.pathJoin(&.{
1679 other.builder.install_prefix, "include",
1680 }));
1681 }
1682 },
1683 .config_header_step => |config_header| {
1684 try zig_args.append("-I");
1685 try zig_args.append(config_header.output_dir);
1686 },
1687 }
1688 }
1689
1690 for (self.lib_paths.items) |lib_path| {
1691 try zig_args.append("-L");
1692 try zig_args.append(lib_path);
1693 }
1694
1695 for (self.rpaths.items) |rpath| {
1696 try zig_args.append("-rpath");
1697 try zig_args.append(rpath);
1698 }
1699
1700 for (self.c_macros.items) |c_macro| {
1701 try zig_args.append("-D");
1702 try zig_args.append(c_macro);
1703 }
1704
1705 if (self.target.isDarwin()) {
1706 for (self.framework_dirs.items) |dir| {
1707 if (builder.sysroot != null) {
1708 try zig_args.append("-iframeworkwithsysroot");
1709 } else {
1710 try zig_args.append("-iframework");
1711 }
1712 try zig_args.append(dir);
1713 try zig_args.append("-F");
1714 try zig_args.append(dir);
1715 }
1716
1717 var it = self.frameworks.iterator();
1718 while (it.next()) |entry| {
1719 const name = entry.key_ptr.*;
1720 const info = entry.value_ptr.*;
1721 if (info.needed) {
1722 zig_args.append("-needed_framework") catch unreachable;
1723 } else if (info.weak) {
1724 zig_args.append("-weak_framework") catch unreachable;
1725 } else {
1726 zig_args.append("-framework") catch unreachable;
1727 }
1728 zig_args.append(name) catch unreachable;
1729 }
1730 } else {
1731 if (self.framework_dirs.items.len > 0) {
1732 log.info("Framework directories have been added for a non-darwin target, this will have no affect on the build", .{});
1733 }
1734
1735 if (self.frameworks.count() > 0) {
1736 log.info("Frameworks have been added for a non-darwin target, this will have no affect on the build", .{});
1737 }
1738 }
1739
1740 if (builder.sysroot) |sysroot| {
1741 try zig_args.appendSlice(&[_][]const u8{ "--sysroot", sysroot });
1742 }
1743
1744 for (builder.search_prefixes.items) |search_prefix| {
1745 try zig_args.append("-L");
1746 try zig_args.append(builder.pathJoin(&.{
1747 search_prefix, "lib",
1748 }));
1749 try zig_args.append("-I");
1750 try zig_args.append(builder.pathJoin(&.{
1751 search_prefix, "include",
1752 }));
1753 }
1754
1755 try addFlag(&zig_args, "valgrind", self.valgrind_support);
1756 try addFlag(&zig_args, "each-lib-rpath", self.each_lib_rpath);
1757 try addFlag(&zig_args, "build-id", self.build_id);
1758
1759 if (self.override_lib_dir) |dir| {
1760 try zig_args.append("--zig-lib-dir");
1761 try zig_args.append(builder.pathFromRoot(dir));
1762 } else if (builder.override_lib_dir) |dir| {
1763 try zig_args.append("--zig-lib-dir");
1764 try zig_args.append(builder.pathFromRoot(dir));
1765 }
1766
1767 if (self.main_pkg_path) |dir| {
1768 try zig_args.append("--main-pkg-path");
1769 try zig_args.append(builder.pathFromRoot(dir));
1770 }
1771
1772 try addFlag(&zig_args, "PIC", self.force_pic);
1773 try addFlag(&zig_args, "PIE", self.pie);
1774 try addFlag(&zig_args, "lto", self.want_lto);
1775
1776 if (self.subsystem) |subsystem| {
1777 try zig_args.append("--subsystem");
1778 try zig_args.append(switch (subsystem) {
1779 .Console => "console",
1780 .Windows => "windows",
1781 .Posix => "posix",
1782 .Native => "native",
1783 .EfiApplication => "efi_application",
1784 .EfiBootServiceDriver => "efi_boot_service_driver",
1785 .EfiRom => "efi_rom",
1786 .EfiRuntimeDriver => "efi_runtime_driver",
1787 });
1788 }
1789
1790 try zig_args.append("--enable-cache");
1791
1792 // Windows has an argument length limit of 32,766 characters, macOS 262,144 and Linux
1793 // 2,097,152. If our args exceed 30 KiB, we instead write them to a "response file" and
1794 // pass that to zig, e.g. via 'zig build-lib @args.rsp'
1795 // See @file syntax here: https://gcc.gnu.org/onlinedocs/gcc/Overall-Options.html
1796 var args_length: usize = 0;
1797 for (zig_args.items) |arg| {
1798 args_length += arg.len + 1; // +1 to account for null terminator
1799 }
1800 if (args_length >= 30 * 1024) {
1801 const args_dir = try fs.path.join(
1802 builder.allocator,
1803 &[_][]const u8{ builder.pathFromRoot("zig-cache"), "args" },
1804 );
1805 try std.fs.cwd().makePath(args_dir);
1806
1807 var args_arena = std.heap.ArenaAllocator.init(builder.allocator);
1808 defer args_arena.deinit();
1809
1810 const args_to_escape = zig_args.items[2..];
1811 var escaped_args = try ArrayList([]const u8).initCapacity(args_arena.allocator(), args_to_escape.len);
1812
1813 arg_blk: for (args_to_escape) |arg| {
1814 for (arg) |c, arg_idx| {
1815 if (c == '\\' or c == '"') {
1816 // Slow path for arguments that need to be escaped. We'll need to allocate and copy
1817 var escaped = try ArrayList(u8).initCapacity(args_arena.allocator(), arg.len + 1);
1818 const writer = escaped.writer();
1819 writer.writeAll(arg[0..arg_idx]) catch unreachable;
1820 for (arg[arg_idx..]) |to_escape| {
1821 if (to_escape == '\\' or to_escape == '"') try writer.writeByte('\\');
1822 try writer.writeByte(to_escape);
1823 }
1824 escaped_args.appendAssumeCapacity(escaped.items);
1825 continue :arg_blk;
1826 }
1827 }
1828 escaped_args.appendAssumeCapacity(arg); // no escaping needed so just use original argument
1829 }
1830
1831 // Write the args to zig-cache/args/<SHA256 hash of args> to avoid conflicts with
1832 // other zig build commands running in parallel.
1833 const partially_quoted = try std.mem.join(builder.allocator, "\" \"", escaped_args.items);
1834 const args = try std.mem.concat(builder.allocator, u8, &[_][]const u8{ "\"", partially_quoted, "\"" });
1835
1836 var args_hash: [Sha256.digest_length]u8 = undefined;
1837 Sha256.hash(args, &args_hash, .{});
1838 var args_hex_hash: [Sha256.digest_length * 2]u8 = undefined;
1839 _ = try std.fmt.bufPrint(
1840 &args_hex_hash,
1841 "{s}",
1842 .{std.fmt.fmtSliceHexLower(&args_hash)},
1843 );
1844
1845 const args_file = try fs.path.join(builder.allocator, &[_][]const u8{ args_dir, args_hex_hash[0..] });
1846 try std.fs.cwd().writeFile(args_file, args);
1847
1848 zig_args.shrinkRetainingCapacity(2);
1849 try zig_args.append(try std.mem.concat(builder.allocator, u8, &[_][]const u8{ "@", args_file }));
1850 }
1851
1852 const output_dir_nl = try builder.execFromStep(zig_args.items, &self.step);
1853 const build_output_dir = mem.trimRight(u8, output_dir_nl, "\r\n");
1854
1855 if (self.output_dir) |output_dir| {
1856 var src_dir = try std.fs.cwd().openIterableDir(build_output_dir, .{});
1857 defer src_dir.close();
1858
1859 // Create the output directory if it doesn't exist.
1860 try std.fs.cwd().makePath(output_dir);
1861
1862 var dest_dir = try std.fs.cwd().openDir(output_dir, .{});
1863 defer dest_dir.close();
1864
1865 var it = src_dir.iterate();
1866 while (try it.next()) |entry| {
1867 // The compiler can put these files into the same directory, but we don't
1868 // want to copy them over.
1869 if (mem.eql(u8, entry.name, "llvm-ar.id") or
1870 mem.eql(u8, entry.name, "libs.txt") or
1871 mem.eql(u8, entry.name, "builtin.zig") or
1872 mem.eql(u8, entry.name, "zld.id") or
1873 mem.eql(u8, entry.name, "lld.id")) continue;
1874
1875 _ = try src_dir.dir.updateFile(entry.name, dest_dir, entry.name, .{});
1876 }
1877 } else {
1878 self.output_dir = build_output_dir;
1879 }
1880
1881 // This will ensure all output filenames will now have the output_dir available!
1882 self.computeOutFileNames();
1883
1884 // Update generated files
1885 if (self.output_dir != null) {
1886 self.output_path_source.path = builder.pathJoin(
1887 &.{ self.output_dir.?, self.out_filename },
1888 );
1889
1890 if (self.emit_h) {
1891 self.output_h_path_source.path = builder.pathJoin(
1892 &.{ self.output_dir.?, self.out_h_filename },
1893 );
1894 }
1895
1896 if (self.target.isWindows() or self.target.isUefi()) {
1897 self.output_pdb_path_source.path = builder.pathJoin(
1898 &.{ self.output_dir.?, self.out_pdb_filename },
1899 );
1900 }
1901 }
1902
1903 if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic and self.version != null and self.target.wantSharedLibSymLinks()) {
1904 try doAtomicSymLinks(builder.allocator, self.getOutputSource().getPath(builder), self.major_only_filename.?, self.name_only_filename.?);
1905 }
1906}
1907
1908fn isLibCLibrary(name: []const u8) bool {
1909 const libc_libraries = [_][]const u8{ "c", "m", "dl", "rt", "pthread" };
1910 for (libc_libraries) |libc_lib_name| {
1911 if (mem.eql(u8, name, libc_lib_name))
1912 return true;
1913 }
1914 return false;
1915}
1916
1917fn isLibCppLibrary(name: []const u8) bool {
1918 const libcpp_libraries = [_][]const u8{ "c++", "stdc++" };
1919 for (libcpp_libraries) |libcpp_lib_name| {
1920 if (mem.eql(u8, name, libcpp_lib_name))
1921 return true;
1922 }
1923 return false;
1924}
1925
1926/// Returned slice must be freed by the caller.
1927fn findVcpkgRoot(allocator: Allocator) !?[]const u8 {
1928 const appdata_path = try fs.getAppDataDir(allocator, "vcpkg");
1929 defer allocator.free(appdata_path);
1930
1931 const path_file = try fs.path.join(allocator, &[_][]const u8{ appdata_path, "vcpkg.path.txt" });
1932 defer allocator.free(path_file);
1933
1934 const file = fs.cwd().openFile(path_file, .{}) catch return null;
1935 defer file.close();
1936
1937 const size = @intCast(usize, try file.getEndPos());
1938 const vcpkg_path = try allocator.alloc(u8, size);
1939 const size_read = try file.read(vcpkg_path);
1940 std.debug.assert(size == size_read);
1941
1942 return vcpkg_path;
1943}
1944
1945pub fn doAtomicSymLinks(allocator: Allocator, output_path: []const u8, filename_major_only: []const u8, filename_name_only: []const u8) !void {
1946 const out_dir = fs.path.dirname(output_path) orelse ".";
1947 const out_basename = fs.path.basename(output_path);
1948 // sym link for libfoo.so.1 to libfoo.so.1.2.3
1949 const major_only_path = fs.path.join(
1950 allocator,
1951 &[_][]const u8{ out_dir, filename_major_only },
1952 ) catch unreachable;
1953 fs.atomicSymLink(allocator, out_basename, major_only_path) catch |err| {
1954 log.err("Unable to symlink {s} -> {s}", .{ major_only_path, out_basename });
1955 return err;
1956 };
1957 // sym link for libfoo.so to libfoo.so.1
1958 const name_only_path = fs.path.join(
1959 allocator,
1960 &[_][]const u8{ out_dir, filename_name_only },
1961 ) catch unreachable;
1962 fs.atomicSymLink(allocator, filename_major_only, name_only_path) catch |err| {
1963 log.err("Unable to symlink {s} -> {s}", .{ name_only_path, filename_major_only });
1964 return err;
1965 };
1966}
1967
1968fn execPkgConfigList(self: *Builder, out_code: *u8) (PkgConfigError || ExecError)![]const PkgConfigPkg {
1969 const stdout = try self.execAllowFail(&[_][]const u8{ "pkg-config", "--list-all" }, out_code, .Ignore);
1970 var list = ArrayList(PkgConfigPkg).init(self.allocator);
1971 errdefer list.deinit();
1972 var line_it = mem.tokenize(u8, stdout, "\r\n");
1973 while (line_it.next()) |line| {
1974 if (mem.trim(u8, line, " \t").len == 0) continue;
1975 var tok_it = mem.tokenize(u8, line, " \t");
1976 try list.append(PkgConfigPkg{
1977 .name = tok_it.next() orelse return error.PkgConfigInvalidOutput,
1978 .desc = tok_it.rest(),
1979 });
1980 }
1981 return list.toOwnedSlice();
1982}
1983
1984fn getPkgConfigList(self: *Builder) ![]const PkgConfigPkg {
1985 if (self.pkg_config_pkg_list) |res| {
1986 return res;
1987 }
1988 var code: u8 = undefined;
1989 if (execPkgConfigList(self, &code)) |list| {
1990 self.pkg_config_pkg_list = list;
1991 return list;
1992 } else |err| {
1993 const result = switch (err) {
1994 error.ProcessTerminated => error.PkgConfigCrashed,
1995 error.ExecNotSupported => error.PkgConfigFailed,
1996 error.ExitCodeFailure => error.PkgConfigFailed,
1997 error.FileNotFound => error.PkgConfigNotInstalled,
1998 error.InvalidName => error.PkgConfigNotInstalled,
1999 error.PkgConfigInvalidOutput => error.PkgConfigInvalidOutput,
2000 error.ChildExecFailed => error.PkgConfigFailed,
2001 else => return err,
2002 };
2003 self.pkg_config_pkg_list = result;
2004 return result;
2005 }
2006}
2007
2008test "addPackage" {
2009 if (builtin.os.tag == .wasi) return error.SkipZigTest;
2010
2011 var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
2012 defer arena.deinit();
2013
2014 var builder = try Builder.create(
2015 arena.allocator(),
2016 "test",
2017 "test",
2018 "test",
2019 "test",
2020 );
2021 defer builder.destroy();
2022
2023 const pkg_dep = Pkg{
2024 .name = "pkg_dep",
2025 .source = .{ .path = "/not/a/pkg_dep.zig" },
2026 };
2027 const pkg_top = Pkg{
2028 .name = "pkg_dep",
2029 .source = .{ .path = "/not/a/pkg_top.zig" },
2030 .dependencies = &[_]Pkg{pkg_dep},
2031 };
2032
2033 var exe = builder.addExecutable("not_an_executable", "/not/an/executable.zig");
2034 exe.addPackage(pkg_top);
2035
2036 try std.testing.expectEqual(@as(usize, 1), exe.packages.items.len);
2037
2038 const dupe = exe.packages.items[0];
2039 try std.testing.expectEqualStrings(pkg_top.name, dupe.name);
2040}
2041
2042fn addFlag(args: *ArrayList([]const u8), comptime name: []const u8, opt: ?bool) !void {
2043 const cond = opt orelse return;
2044 try args.ensureUnusedCapacity(1);
2045 if (cond) {
2046 args.appendAssumeCapacity("-f" ++ name);
2047 } else {
2048 args.appendAssumeCapacity("-fno-" ++ name);
2049 }
2050}
2051
2052const TransitiveDeps = struct {
2053 link_objects: ArrayList(LinkObject),
2054 seen_system_libs: StringHashMap(void),
2055 seen_steps: std.AutoHashMap(*const Step, void),
2056 is_linking_libcpp: bool,
2057 is_linking_libc: bool,
2058 frameworks: *StringHashMap(FrameworkLinkInfo),
2059
2060 fn add(td: *TransitiveDeps, link_objects: []const LinkObject) !void {
2061 try td.link_objects.ensureUnusedCapacity(link_objects.len);
2062
2063 for (link_objects) |link_object| {
2064 try td.link_objects.append(link_object);
2065 switch (link_object) {
2066 .other_step => |other| try addInner(td, other, other.isDynamicLibrary()),
2067 else => {},
2068 }
2069 }
2070 }
2071
2072 fn addInner(td: *TransitiveDeps, other: *LibExeObjStep, dyn: bool) !void {
2073 // Inherit dependency on libc and libc++
2074 td.is_linking_libcpp = td.is_linking_libcpp or other.is_linking_libcpp;
2075 td.is_linking_libc = td.is_linking_libc or other.is_linking_libc;
2076
2077 // Inherit dependencies on darwin frameworks
2078 if (!dyn) {
2079 var it = other.frameworks.iterator();
2080 while (it.next()) |framework| {
2081 try td.frameworks.put(framework.key_ptr.*, framework.value_ptr.*);
2082 }
2083 }
2084
2085 // Inherit dependencies on system libraries and static libraries.
2086 for (other.link_objects.items) |other_link_object| {
2087 switch (other_link_object) {
2088 .system_lib => |system_lib| {
2089 if ((try td.seen_system_libs.fetchPut(system_lib.name, {})) != null)
2090 continue;
2091
2092 if (dyn)
2093 continue;
2094
2095 try td.link_objects.append(other_link_object);
2096 },
2097 .other_step => |inner_other| {
2098 if ((try td.seen_steps.fetchPut(&inner_other.step, {})) != null)
2099 continue;
2100
2101 if (!dyn)
2102 try td.link_objects.append(other_link_object);
2103
2104 try addInner(td, inner_other, dyn or inner_other.isDynamicLibrary());
2105 },
2106 else => continue,
2107 }
2108 }
2109 }
2110};
lib/std/build/LogStep.zig deleted-25
...@@ -1,25 +0,0 @@
1const std = @import("../std.zig");
2const log = std.log;
3const build = @import("../build.zig");
4const Step = build.Step;
5const Builder = build.Builder;
6const LogStep = @This();
7
8pub const base_id = .log;
9
10step: Step,
11builder: *Builder,
12data: []const u8,
13
14pub fn init(builder: *Builder, data: []const u8) LogStep {
15 return LogStep{
16 .builder = builder,
17 .step = Step.init(.log, builder.fmt("log {s}", .{data}), builder.allocator, make),
18 .data = builder.dupe(data),
19 };
20}
21
22fn make(step: *Step) anyerror!void {
23 const self = @fieldParentPtr(LogStep, "step", step);
24 log.info("{s}", .{self.data});
25}
lib/std/build/OptionsStep.zig deleted-365
...@@ -1,365 +0,0 @@
1const std = @import("../std.zig");
2const builtin = @import("builtin");
3const build = std.build;
4const fs = std.fs;
5const Step = build.Step;
6const Builder = build.Builder;
7const GeneratedFile = build.GeneratedFile;
8const LibExeObjStep = build.LibExeObjStep;
9const FileSource = build.FileSource;
10
11const OptionsStep = @This();
12
13pub const base_id = .options;
14
15step: Step,
16generated_file: GeneratedFile,
17builder: *Builder,
18
19contents: std.ArrayList(u8),
20artifact_args: std.ArrayList(OptionArtifactArg),
21file_source_args: std.ArrayList(OptionFileSourceArg),
22
23pub fn create(builder: *Builder) *OptionsStep {
24 const self = builder.allocator.create(OptionsStep) catch unreachable;
25 self.* = .{
26 .builder = builder,
27 .step = Step.init(.options, "options", builder.allocator, make),
28 .generated_file = undefined,
29 .contents = std.ArrayList(u8).init(builder.allocator),
30 .artifact_args = std.ArrayList(OptionArtifactArg).init(builder.allocator),
31 .file_source_args = std.ArrayList(OptionFileSourceArg).init(builder.allocator),
32 };
33 self.generated_file = .{ .step = &self.step };
34
35 return self;
36}
37
38pub fn addOption(self: *OptionsStep, comptime T: type, name: []const u8, value: T) void {
39 const out = self.contents.writer();
40 switch (T) {
41 []const []const u8 => {
42 out.print("pub const {}: []const []const u8 = &[_][]const u8{{\n", .{std.zig.fmtId(name)}) catch unreachable;
43 for (value) |slice| {
44 out.print(" \"{}\",\n", .{std.zig.fmtEscapes(slice)}) catch unreachable;
45 }
46 out.writeAll("};\n") catch unreachable;
47 return;
48 },
49 [:0]const u8 => {
50 out.print("pub const {}: [:0]const u8 = \"{}\";\n", .{ std.zig.fmtId(name), std.zig.fmtEscapes(value) }) catch unreachable;
51 return;
52 },
53 []const u8 => {
54 out.print("pub const {}: []const u8 = \"{}\";\n", .{ std.zig.fmtId(name), std.zig.fmtEscapes(value) }) catch unreachable;
55 return;
56 },
57 ?[:0]const u8 => {
58 out.print("pub const {}: ?[:0]const u8 = ", .{std.zig.fmtId(name)}) catch unreachable;
59 if (value) |payload| {
60 out.print("\"{}\";\n", .{std.zig.fmtEscapes(payload)}) catch unreachable;
61 } else {
62 out.writeAll("null;\n") catch unreachable;
63 }
64 return;
65 },
66 ?[]const u8 => {
67 out.print("pub const {}: ?[]const u8 = ", .{std.zig.fmtId(name)}) catch unreachable;
68 if (value) |payload| {
69 out.print("\"{}\";\n", .{std.zig.fmtEscapes(payload)}) catch unreachable;
70 } else {
71 out.writeAll("null;\n") catch unreachable;
72 }
73 return;
74 },
75 std.builtin.Version => {
76 out.print(
77 \\pub const {}: @import("std").builtin.Version = .{{
78 \\ .major = {d},
79 \\ .minor = {d},
80 \\ .patch = {d},
81 \\}};
82 \\
83 , .{
84 std.zig.fmtId(name),
85
86 value.major,
87 value.minor,
88 value.patch,
89 }) catch unreachable;
90 return;
91 },
92 std.SemanticVersion => {
93 out.print(
94 \\pub const {}: @import("std").SemanticVersion = .{{
95 \\ .major = {d},
96 \\ .minor = {d},
97 \\ .patch = {d},
98 \\
99 , .{
100 std.zig.fmtId(name),
101
102 value.major,
103 value.minor,
104 value.patch,
105 }) catch unreachable;
106 if (value.pre) |some| {
107 out.print(" .pre = \"{}\",\n", .{std.zig.fmtEscapes(some)}) catch unreachable;
108 }
109 if (value.build) |some| {
110 out.print(" .build = \"{}\",\n", .{std.zig.fmtEscapes(some)}) catch unreachable;
111 }
112 out.writeAll("};\n") catch unreachable;
113 return;
114 },
115 else => {},
116 }
117 switch (@typeInfo(T)) {
118 .Enum => |enum_info| {
119 out.print("pub const {} = enum {{\n", .{std.zig.fmtId(@typeName(T))}) catch unreachable;
120 inline for (enum_info.fields) |field| {
121 out.print(" {},\n", .{std.zig.fmtId(field.name)}) catch unreachable;
122 }
123 out.writeAll("};\n") catch unreachable;
124 out.print("pub const {}: {s} = {s}.{s};\n", .{
125 std.zig.fmtId(name),
126 std.zig.fmtId(@typeName(T)),
127 std.zig.fmtId(@typeName(T)),
128 std.zig.fmtId(@tagName(value)),
129 }) catch unreachable;
130 return;
131 },
132 else => {},
133 }
134 out.print("pub const {}: {s} = ", .{ std.zig.fmtId(name), @typeName(T) }) catch unreachable;
135 printLiteral(out, value, 0) catch unreachable;
136 out.writeAll(";\n") catch unreachable;
137}
138
139// TODO: non-recursive?
140fn printLiteral(out: anytype, val: anytype, indent: u8) !void {
141 const T = @TypeOf(val);
142 switch (@typeInfo(T)) {
143 .Array => {
144 try out.print("{s} {{\n", .{@typeName(T)});
145 for (val) |item| {
146 try out.writeByteNTimes(' ', indent + 4);
147 try printLiteral(out, item, indent + 4);
148 try out.writeAll(",\n");
149 }
150 try out.writeByteNTimes(' ', indent);
151 try out.writeAll("}");
152 },
153 .Pointer => |p| {
154 if (p.size != .Slice) {
155 @compileError("Non-slice pointers are not yet supported in build options");
156 }
157 try out.print("&[_]{s} {{\n", .{@typeName(p.child)});
158 for (val) |item| {
159 try out.writeByteNTimes(' ', indent + 4);
160 try printLiteral(out, item, indent + 4);
161 try out.writeAll(",\n");
162 }
163 try out.writeByteNTimes(' ', indent);
164 try out.writeAll("}");
165 },
166 .Optional => {
167 if (val) |inner| {
168 return printLiteral(out, inner, indent);
169 } else {
170 return out.writeAll("null");
171 }
172 },
173 .Void,
174 .Bool,
175 .Int,
176 .ComptimeInt,
177 .Float,
178 .Null,
179 => try out.print("{any}", .{val}),
180 else => @compileError(std.fmt.comptimePrint("`{s}` are not yet supported as build options", .{@tagName(@typeInfo(T))})),
181 }
182}
183
184/// The value is the path in the cache dir.
185/// Adds a dependency automatically.
186pub fn addOptionFileSource(
187 self: *OptionsStep,
188 name: []const u8,
189 source: FileSource,
190) void {
191 self.file_source_args.append(.{
192 .name = name,
193 .source = source.dupe(self.builder),
194 }) catch unreachable;
195 source.addStepDependencies(&self.step);
196}
197
198/// The value is the path in the cache dir.
199/// Adds a dependency automatically.
200pub fn addOptionArtifact(self: *OptionsStep, name: []const u8, artifact: *LibExeObjStep) void {
201 self.artifact_args.append(.{ .name = self.builder.dupe(name), .artifact = artifact }) catch unreachable;
202 self.step.dependOn(&artifact.step);
203}
204
205pub fn getPackage(self: *OptionsStep, package_name: []const u8) build.Pkg {
206 return .{ .name = package_name, .source = self.getSource() };
207}
208
209pub fn getSource(self: *OptionsStep) FileSource {
210 return .{ .generated = &self.generated_file };
211}
212
213fn make(step: *Step) !void {
214 const self = @fieldParentPtr(OptionsStep, "step", step);
215
216 for (self.artifact_args.items) |item| {
217 self.addOption(
218 []const u8,
219 item.name,
220 self.builder.pathFromRoot(item.artifact.getOutputSource().getPath(self.builder)),
221 );
222 }
223
224 for (self.file_source_args.items) |item| {
225 self.addOption(
226 []const u8,
227 item.name,
228 item.source.getPath(self.builder),
229 );
230 }
231
232 const options_directory = self.builder.pathFromRoot(
233 try fs.path.join(
234 self.builder.allocator,
235 &[_][]const u8{ self.builder.cache_root, "options" },
236 ),
237 );
238
239 try fs.cwd().makePath(options_directory);
240
241 const options_file = try fs.path.join(
242 self.builder.allocator,
243 &[_][]const u8{ options_directory, &self.hashContentsToFileName() },
244 );
245
246 try fs.cwd().writeFile(options_file, self.contents.items);
247
248 self.generated_file.path = options_file;
249}
250
251fn hashContentsToFileName(self: *OptionsStep) [64]u8 {
252 // This implementation is copied from `WriteFileStep.make`
253
254 var hash = std.crypto.hash.blake2.Blake2b384.init(.{});
255
256 // Random bytes to make OptionsStep unique. Refresh this with
257 // new random bytes when OptionsStep implementation is modified
258 // in a non-backwards-compatible way.
259 hash.update("yL0Ya4KkmcCjBlP8");
260 hash.update(self.contents.items);
261
262 var digest: [48]u8 = undefined;
263 hash.final(&digest);
264 var hash_basename: [64]u8 = undefined;
265 _ = fs.base64_encoder.encode(&hash_basename, &digest);
266 return hash_basename;
267}
268
269const OptionArtifactArg = struct {
270 name: []const u8,
271 artifact: *LibExeObjStep,
272};
273
274const OptionFileSourceArg = struct {
275 name: []const u8,
276 source: FileSource,
277};
278
279test "OptionsStep" {
280 if (builtin.os.tag == .wasi) return error.SkipZigTest;
281
282 var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
283 defer arena.deinit();
284 var builder = try Builder.create(
285 arena.allocator(),
286 "test",
287 "test",
288 "test",
289 "test",
290 );
291 defer builder.destroy();
292
293 const options = builder.addOptions();
294
295 // TODO this regressed at some point
296 //const KeywordEnum = enum {
297 // @"0.8.1",
298 //};
299
300 const nested_array = [2][2]u16{
301 [2]u16{ 300, 200 },
302 [2]u16{ 300, 200 },
303 };
304 const nested_slice: []const []const u16 = &[_][]const u16{ &nested_array[0], &nested_array[1] };
305
306 options.addOption(usize, "option1", 1);
307 options.addOption(?usize, "option2", null);
308 options.addOption(?usize, "option3", 3);
309 options.addOption(comptime_int, "option4", 4);
310 options.addOption([]const u8, "string", "zigisthebest");
311 options.addOption(?[]const u8, "optional_string", null);
312 options.addOption([2][2]u16, "nested_array", nested_array);
313 options.addOption([]const []const u16, "nested_slice", nested_slice);
314 //options.addOption(KeywordEnum, "keyword_enum", .@"0.8.1");
315 options.addOption(std.builtin.Version, "version", try std.builtin.Version.parse("0.1.2"));
316 options.addOption(std.SemanticVersion, "semantic_version", try std.SemanticVersion.parse("0.1.2-foo+bar"));
317
318 try std.testing.expectEqualStrings(
319 \\pub const option1: usize = 1;
320 \\pub const option2: ?usize = null;
321 \\pub const option3: ?usize = 3;
322 \\pub const option4: comptime_int = 4;
323 \\pub const string: []const u8 = "zigisthebest";
324 \\pub const optional_string: ?[]const u8 = null;
325 \\pub const nested_array: [2][2]u16 = [2][2]u16 {
326 \\ [2]u16 {
327 \\ 300,
328 \\ 200,
329 \\ },
330 \\ [2]u16 {
331 \\ 300,
332 \\ 200,
333 \\ },
334 \\};
335 \\pub const nested_slice: []const []const u16 = &[_][]const u16 {
336 \\ &[_]u16 {
337 \\ 300,
338 \\ 200,
339 \\ },
340 \\ &[_]u16 {
341 \\ 300,
342 \\ 200,
343 \\ },
344 \\};
345 //\\pub const KeywordEnum = enum {
346 //\\ @"0.8.1",
347 //\\};
348 //\\pub const keyword_enum: KeywordEnum = KeywordEnum.@"0.8.1";
349 \\pub const version: @import("std").builtin.Version = .{
350 \\ .major = 0,
351 \\ .minor = 1,
352 \\ .patch = 2,
353 \\};
354 \\pub const semantic_version: @import("std").SemanticVersion = .{
355 \\ .major = 0,
356 \\ .minor = 1,
357 \\ .patch = 2,
358 \\ .pre = "foo",
359 \\ .build = "bar",
360 \\};
361 \\
362 , options.contents.items);
363
364 _ = try std.zig.parse(arena.allocator(), try options.contents.toOwnedSliceSentinel(0));
365}
lib/std/build/RemoveDirStep.zig deleted-31
...@@ -1,31 +0,0 @@
1const std = @import("../std.zig");
2const log = std.log;
3const fs = std.fs;
4const build = @import("../build.zig");
5const Step = build.Step;
6const Builder = build.Builder;
7const RemoveDirStep = @This();
8
9pub const base_id = .remove_dir;
10
11step: Step,
12builder: *Builder,
13dir_path: []const u8,
14
15pub fn init(builder: *Builder, dir_path: []const u8) RemoveDirStep {
16 return RemoveDirStep{
17 .builder = builder,
18 .step = Step.init(.remove_dir, builder.fmt("RemoveDir {s}", .{dir_path}), builder.allocator, make),
19 .dir_path = builder.dupePath(dir_path),
20 };
21}
22
23fn make(step: *Step) !void {
24 const self = @fieldParentPtr(RemoveDirStep, "step", step);
25
26 const full_path = self.builder.pathFromRoot(self.dir_path);
27 fs.cwd().deleteTree(full_path) catch |err| {
28 log.err("Unable to remove {s}: {s}", .{ full_path, @errorName(err) });
29 return err;
30 };
31}
lib/std/build/RunStep.zig deleted-378
...@@ -1,378 +0,0 @@
1const std = @import("../std.zig");
2const builtin = @import("builtin");
3const build = std.build;
4const Step = build.Step;
5const Builder = build.Builder;
6const LibExeObjStep = build.LibExeObjStep;
7const WriteFileStep = build.WriteFileStep;
8const fs = std.fs;
9const mem = std.mem;
10const process = std.process;
11const ArrayList = std.ArrayList;
12const EnvMap = process.EnvMap;
13const Allocator = mem.Allocator;
14const ExecError = build.Builder.ExecError;
15
16const max_stdout_size = 1 * 1024 * 1024; // 1 MiB
17
18const RunStep = @This();
19
20pub const base_id: Step.Id = .run;
21
22step: Step,
23builder: *Builder,
24
25/// See also addArg and addArgs to modifying this directly
26argv: ArrayList(Arg),
27
28/// Set this to modify the current working directory
29cwd: ?[]const u8,
30
31/// Override this field to modify the environment, or use setEnvironmentVariable
32env_map: ?*EnvMap,
33
34stdout_action: StdIoAction = .inherit,
35stderr_action: StdIoAction = .inherit,
36
37stdin_behavior: std.ChildProcess.StdIo = .Inherit,
38
39/// Set this to `null` to ignore the exit code for the purpose of determining a successful execution
40expected_exit_code: ?u8 = 0,
41
42/// Print the command before running it
43print: bool,
44
45pub const StdIoAction = union(enum) {
46 inherit,
47 ignore,
48 expect_exact: []const u8,
49 expect_matches: []const []const u8,
50};
51
52pub const Arg = union(enum) {
53 artifact: *LibExeObjStep,
54 file_source: build.FileSource,
55 bytes: []u8,
56};
57
58pub fn create(builder: *Builder, name: []const u8) *RunStep {
59 const self = builder.allocator.create(RunStep) catch unreachable;
60 self.* = RunStep{
61 .builder = builder,
62 .step = Step.init(base_id, name, builder.allocator, make),
63 .argv = ArrayList(Arg).init(builder.allocator),
64 .cwd = null,
65 .env_map = null,
66 .print = builder.verbose,
67 };
68 return self;
69}
70
71pub fn addArtifactArg(self: *RunStep, artifact: *LibExeObjStep) void {
72 self.argv.append(Arg{ .artifact = artifact }) catch unreachable;
73 self.step.dependOn(&artifact.step);
74}
75
76pub fn addFileSourceArg(self: *RunStep, file_source: build.FileSource) void {
77 self.argv.append(Arg{
78 .file_source = file_source.dupe(self.builder),
79 }) catch unreachable;
80 file_source.addStepDependencies(&self.step);
81}
82
83pub fn addArg(self: *RunStep, arg: []const u8) void {
84 self.argv.append(Arg{ .bytes = self.builder.dupe(arg) }) catch unreachable;
85}
86
87pub fn addArgs(self: *RunStep, args: []const []const u8) void {
88 for (args) |arg| {
89 self.addArg(arg);
90 }
91}
92
93pub fn clearEnvironment(self: *RunStep) void {
94 const new_env_map = self.builder.allocator.create(EnvMap) catch unreachable;
95 new_env_map.* = EnvMap.init(self.builder.allocator);
96 self.env_map = new_env_map;
97}
98
99pub fn addPathDir(self: *RunStep, search_path: []const u8) void {
100 addPathDirInternal(&self.step, self.builder, search_path);
101}
102
103/// For internal use only, users of `RunStep` should use `addPathDir` directly.
104pub fn addPathDirInternal(step: *Step, builder: *Builder, search_path: []const u8) void {
105 const env_map = getEnvMapInternal(step, builder.allocator);
106
107 const key = "PATH";
108 var prev_path = env_map.get(key);
109
110 if (prev_path) |pp| {
111 const new_path = builder.fmt("{s}" ++ [1]u8{fs.path.delimiter} ++ "{s}", .{ pp, search_path });
112 env_map.put(key, new_path) catch unreachable;
113 } else {
114 env_map.put(key, builder.dupePath(search_path)) catch unreachable;
115 }
116}
117
118pub fn getEnvMap(self: *RunStep) *EnvMap {
119 return getEnvMapInternal(&self.step, self.builder.allocator);
120}
121
122fn getEnvMapInternal(step: *Step, allocator: Allocator) *EnvMap {
123 const maybe_env_map = switch (step.id) {
124 .run => step.cast(RunStep).?.env_map,
125 .emulatable_run => step.cast(build.EmulatableRunStep).?.env_map,
126 else => unreachable,
127 };
128 return maybe_env_map orelse {
129 const env_map = allocator.create(EnvMap) catch unreachable;
130 env_map.* = process.getEnvMap(allocator) catch unreachable;
131 switch (step.id) {
132 .run => step.cast(RunStep).?.env_map = env_map,
133 .emulatable_run => step.cast(RunStep).?.env_map = env_map,
134 else => unreachable,
135 }
136 return env_map;
137 };
138}
139
140pub fn setEnvironmentVariable(self: *RunStep, key: []const u8, value: []const u8) void {
141 const env_map = self.getEnvMap();
142 env_map.put(
143 self.builder.dupe(key),
144 self.builder.dupe(value),
145 ) catch unreachable;
146}
147
148pub fn expectStdErrEqual(self: *RunStep, bytes: []const u8) void {
149 self.stderr_action = .{ .expect_exact = self.builder.dupe(bytes) };
150}
151
152pub fn expectStdOutEqual(self: *RunStep, bytes: []const u8) void {
153 self.stdout_action = .{ .expect_exact = self.builder.dupe(bytes) };
154}
155
156fn stdIoActionToBehavior(action: StdIoAction) std.ChildProcess.StdIo {
157 return switch (action) {
158 .ignore => .Ignore,
159 .inherit => .Inherit,
160 .expect_exact, .expect_matches => .Pipe,
161 };
162}
163
164fn make(step: *Step) !void {
165 const self = @fieldParentPtr(RunStep, "step", step);
166
167 var argv_list = ArrayList([]const u8).init(self.builder.allocator);
168 for (self.argv.items) |arg| {
169 switch (arg) {
170 .bytes => |bytes| try argv_list.append(bytes),
171 .file_source => |file| try argv_list.append(file.getPath(self.builder)),
172 .artifact => |artifact| {
173 if (artifact.target.isWindows()) {
174 // On Windows we don't have rpaths so we have to add .dll search paths to PATH
175 self.addPathForDynLibs(artifact);
176 }
177 const executable_path = artifact.installed_path orelse artifact.getOutputSource().getPath(self.builder);
178 try argv_list.append(executable_path);
179 },
180 }
181 }
182
183 try runCommand(
184 argv_list.items,
185 self.builder,
186 self.expected_exit_code,
187 self.stdout_action,
188 self.stderr_action,
189 self.stdin_behavior,
190 self.env_map,
191 self.cwd,
192 self.print,
193 );
194}
195
196pub fn runCommand(
197 argv: []const []const u8,
198 builder: *Builder,
199 expected_exit_code: ?u8,
200 stdout_action: StdIoAction,
201 stderr_action: StdIoAction,
202 stdin_behavior: std.ChildProcess.StdIo,
203 env_map: ?*EnvMap,
204 maybe_cwd: ?[]const u8,
205 print: bool,
206) !void {
207 const cwd = if (maybe_cwd) |cwd| builder.pathFromRoot(cwd) else builder.build_root;
208
209 if (!std.process.can_spawn) {
210 const cmd = try std.mem.join(builder.allocator, " ", argv);
211 std.debug.print("the following command cannot be executed ({s} does not support spawning a child process):\n{s}", .{ @tagName(builtin.os.tag), cmd });
212 builder.allocator.free(cmd);
213 return ExecError.ExecNotSupported;
214 }
215
216 var child = std.ChildProcess.init(argv, builder.allocator);
217 child.cwd = cwd;
218 child.env_map = env_map orelse builder.env_map;
219
220 child.stdin_behavior = stdin_behavior;
221 child.stdout_behavior = stdIoActionToBehavior(stdout_action);
222 child.stderr_behavior = stdIoActionToBehavior(stderr_action);
223
224 if (print)
225 printCmd(cwd, argv);
226
227 child.spawn() catch |err| {
228 std.debug.print("Unable to spawn {s}: {s}\n", .{ argv[0], @errorName(err) });
229 return err;
230 };
231
232 // TODO need to poll to read these streams to prevent a deadlock (or rely on evented I/O).
233
234 var stdout: ?[]const u8 = null;
235 defer if (stdout) |s| builder.allocator.free(s);
236
237 switch (stdout_action) {
238 .expect_exact, .expect_matches => {
239 stdout = child.stdout.?.reader().readAllAlloc(builder.allocator, max_stdout_size) catch unreachable;
240 },
241 .inherit, .ignore => {},
242 }
243
244 var stderr: ?[]const u8 = null;
245 defer if (stderr) |s| builder.allocator.free(s);
246
247 switch (stderr_action) {
248 .expect_exact, .expect_matches => {
249 stderr = child.stderr.?.reader().readAllAlloc(builder.allocator, max_stdout_size) catch unreachable;
250 },
251 .inherit, .ignore => {},
252 }
253
254 const term = child.wait() catch |err| {
255 std.debug.print("Unable to spawn {s}: {s}\n", .{ argv[0], @errorName(err) });
256 return err;
257 };
258
259 switch (term) {
260 .Exited => |code| blk: {
261 const expected_code = expected_exit_code orelse break :blk;
262
263 if (code != expected_code) {
264 if (builder.prominent_compile_errors) {
265 std.debug.print("Run step exited with error code {} (expected {})\n", .{
266 code,
267 expected_code,
268 });
269 } else {
270 std.debug.print("The following command exited with error code {} (expected {}):\n", .{
271 code,
272 expected_code,
273 });
274 printCmd(cwd, argv);
275 }
276
277 return error.UnexpectedExitCode;
278 }
279 },
280 else => {
281 std.debug.print("The following command terminated unexpectedly:\n", .{});
282 printCmd(cwd, argv);
283 return error.UncleanExit;
284 },
285 }
286
287 switch (stderr_action) {
288 .inherit, .ignore => {},
289 .expect_exact => |expected_bytes| {
290 if (!mem.eql(u8, expected_bytes, stderr.?)) {
291 std.debug.print(
292 \\
293 \\========= Expected this stderr: =========
294 \\{s}
295 \\========= But found: ====================
296 \\{s}
297 \\
298 , .{ expected_bytes, stderr.? });
299 printCmd(cwd, argv);
300 return error.TestFailed;
301 }
302 },
303 .expect_matches => |matches| for (matches) |match| {
304 if (mem.indexOf(u8, stderr.?, match) == null) {
305 std.debug.print(
306 \\
307 \\========= Expected to find in stderr: =========
308 \\{s}
309 \\========= But stderr does not contain it: =====
310 \\{s}
311 \\
312 , .{ match, stderr.? });
313 printCmd(cwd, argv);
314 return error.TestFailed;
315 }
316 },
317 }
318
319 switch (stdout_action) {
320 .inherit, .ignore => {},
321 .expect_exact => |expected_bytes| {
322 if (!mem.eql(u8, expected_bytes, stdout.?)) {
323 std.debug.print(
324 \\
325 \\========= Expected this stdout: =========
326 \\{s}
327 \\========= But found: ====================
328 \\{s}
329 \\
330 , .{ expected_bytes, stdout.? });
331 printCmd(cwd, argv);
332 return error.TestFailed;
333 }
334 },
335 .expect_matches => |matches| for (matches) |match| {
336 if (mem.indexOf(u8, stdout.?, match) == null) {
337 std.debug.print(
338 \\
339 \\========= Expected to find in stdout: =========
340 \\{s}
341 \\========= But stdout does not contain it: =====
342 \\{s}
343 \\
344 , .{ match, stdout.? });
345 printCmd(cwd, argv);
346 return error.TestFailed;
347 }
348 },
349 }
350}
351
352fn printCmd(cwd: ?[]const u8, argv: []const []const u8) void {
353 if (cwd) |yes_cwd| std.debug.print("cd {s} && ", .{yes_cwd});
354 for (argv) |arg| {
355 std.debug.print("{s} ", .{arg});
356 }
357 std.debug.print("\n", .{});
358}
359
360fn addPathForDynLibs(self: *RunStep, artifact: *LibExeObjStep) void {
361 addPathForDynLibsInternal(&self.step, self.builder, artifact);
362}
363
364/// This should only be used for internal usage, this is called automatically
365/// for the user.
366pub fn addPathForDynLibsInternal(step: *Step, builder: *Builder, artifact: *LibExeObjStep) void {
367 for (artifact.link_objects.items) |link_object| {
368 switch (link_object) {
369 .other_step => |other| {
370 if (other.target.isWindows() and other.isDynamicLibrary()) {
371 addPathDirInternal(step, builder, fs.path.dirname(other.getOutputSource().getPath(builder)).?);
372 addPathForDynLibsInternal(step, builder, other);
373 }
374 },
375 else => {},
376 }
377 }
378}
lib/std/build/TranslateCStep.zig deleted-112
...@@ -1,112 +0,0 @@
1const std = @import("../std.zig");
2const build = std.build;
3const Step = build.Step;
4const Builder = build.Builder;
5const LibExeObjStep = build.LibExeObjStep;
6const CheckFileStep = build.CheckFileStep;
7const fs = std.fs;
8const mem = std.mem;
9const CrossTarget = std.zig.CrossTarget;
10
11const TranslateCStep = @This();
12
13pub const base_id = .translate_c;
14
15step: Step,
16builder: *Builder,
17source: build.FileSource,
18include_dirs: std.ArrayList([]const u8),
19c_macros: std.ArrayList([]const u8),
20output_dir: ?[]const u8,
21out_basename: []const u8,
22target: CrossTarget = CrossTarget{},
23output_file: build.GeneratedFile,
24
25pub fn create(builder: *Builder, source: build.FileSource) *TranslateCStep {
26 const self = builder.allocator.create(TranslateCStep) catch unreachable;
27 self.* = TranslateCStep{
28 .step = Step.init(.translate_c, "translate-c", builder.allocator, make),
29 .builder = builder,
30 .source = source,
31 .include_dirs = std.ArrayList([]const u8).init(builder.allocator),
32 .c_macros = std.ArrayList([]const u8).init(builder.allocator),
33 .output_dir = null,
34 .out_basename = undefined,
35 .output_file = build.GeneratedFile{ .step = &self.step },
36 };
37 source.addStepDependencies(&self.step);
38 return self;
39}
40
41pub fn setTarget(self: *TranslateCStep, target: CrossTarget) void {
42 self.target = target;
43}
44
45/// Creates a step to build an executable from the translated source.
46pub fn addExecutable(self: *TranslateCStep) *LibExeObjStep {
47 return self.builder.addExecutableSource("translated_c", build.FileSource{ .generated = &self.output_file });
48}
49
50pub fn addIncludeDir(self: *TranslateCStep, include_dir: []const u8) void {
51 self.include_dirs.append(self.builder.dupePath(include_dir)) catch unreachable;
52}
53
54pub fn addCheckFile(self: *TranslateCStep, expected_matches: []const []const u8) *CheckFileStep {
55 return CheckFileStep.create(self.builder, .{ .generated = &self.output_file }, self.builder.dupeStrings(expected_matches));
56}
57
58/// If the value is omitted, it is set to 1.
59/// `name` and `value` need not live longer than the function call.
60pub fn defineCMacro(self: *TranslateCStep, name: []const u8, value: ?[]const u8) void {
61 const macro = build.constructCMacro(self.builder.allocator, name, value);
62 self.c_macros.append(macro) catch unreachable;
63}
64
65/// name_and_value looks like [name]=[value]. If the value is omitted, it is set to 1.
66pub fn defineCMacroRaw(self: *TranslateCStep, name_and_value: []const u8) void {
67 self.c_macros.append(self.builder.dupe(name_and_value)) catch unreachable;
68}
69
70fn make(step: *Step) !void {
71 const self = @fieldParentPtr(TranslateCStep, "step", step);
72
73 var argv_list = std.ArrayList([]const u8).init(self.builder.allocator);
74 try argv_list.append(self.builder.zig_exe);
75 try argv_list.append("translate-c");
76 try argv_list.append("-lc");
77
78 try argv_list.append("--enable-cache");
79
80 if (!self.target.isNative()) {
81 try argv_list.append("-target");
82 try argv_list.append(try self.target.zigTriple(self.builder.allocator));
83 }
84
85 for (self.include_dirs.items) |include_dir| {
86 try argv_list.append("-I");
87 try argv_list.append(include_dir);
88 }
89
90 for (self.c_macros.items) |c_macro| {
91 try argv_list.append("-D");
92 try argv_list.append(c_macro);
93 }
94
95 try argv_list.append(self.source.getPath(self.builder));
96
97 const output_path_nl = try self.builder.execFromStep(argv_list.items, &self.step);
98 const output_path = mem.trimRight(u8, output_path_nl, "\r\n");
99
100 self.out_basename = fs.path.basename(output_path);
101 if (self.output_dir) |output_dir| {
102 const full_dest = try fs.path.join(self.builder.allocator, &[_][]const u8{ output_dir, self.out_basename });
103 try self.builder.updateFile(output_path, full_dest);
104 } else {
105 self.output_dir = fs.path.dirname(output_path).?;
106 }
107
108 self.output_file.path = fs.path.join(
109 self.builder.allocator,
110 &[_][]const u8{ self.output_dir.?, self.out_basename },
111 ) catch unreachable;
112}
lib/std/build/WriteFileStep.zig deleted-117
...@@ -1,117 +0,0 @@
1const std = @import("../std.zig");
2const build = @import("../build.zig");
3const Step = build.Step;
4const Builder = build.Builder;
5const fs = std.fs;
6const ArrayList = std.ArrayList;
7
8const WriteFileStep = @This();
9
10pub const base_id = .write_file;
11
12step: Step,
13builder: *Builder,
14output_dir: []const u8,
15files: std.TailQueue(File),
16
17pub const File = struct {
18 source: build.GeneratedFile,
19 basename: []const u8,
20 bytes: []const u8,
21};
22
23pub fn init(builder: *Builder) WriteFileStep {
24 return WriteFileStep{
25 .builder = builder,
26 .step = Step.init(.write_file, "writefile", builder.allocator, make),
27 .files = .{},
28 .output_dir = undefined,
29 };
30}
31
32pub fn add(self: *WriteFileStep, basename: []const u8, bytes: []const u8) void {
33 const node = self.builder.allocator.create(std.TailQueue(File).Node) catch unreachable;
34 node.* = .{
35 .data = .{
36 .source = build.GeneratedFile{ .step = &self.step },
37 .basename = self.builder.dupePath(basename),
38 .bytes = self.builder.dupe(bytes),
39 },
40 };
41
42 self.files.append(node);
43}
44
45/// Gets a file source for the given basename. If the file does not exist, returns `null`.
46pub fn getFileSource(step: *WriteFileStep, basename: []const u8) ?build.FileSource {
47 var it = step.files.first;
48 while (it) |node| : (it = node.next) {
49 if (std.mem.eql(u8, node.data.basename, basename))
50 return build.FileSource{ .generated = &node.data.source };
51 }
52 return null;
53}
54
55fn make(step: *Step) !void {
56 const self = @fieldParentPtr(WriteFileStep, "step", step);
57
58 // The cache is used here not really as a way to speed things up - because writing
59 // the data to a file would probably be very fast - but as a way to find a canonical
60 // location to put build artifacts.
61
62 // If, for example, a hard-coded path was used as the location to put WriteFileStep
63 // files, then two WriteFileSteps executing in parallel might clobber each other.
64
65 // TODO port the cache system from the compiler to zig std lib. Until then
66 // we directly construct the path, and no "cache hit" detection happens;
67 // the files are always written.
68 // Note there is similar code over in ConfigHeaderStep.
69 const Hasher = std.crypto.auth.siphash.SipHash128(1, 3);
70 // Random bytes to make WriteFileStep unique. Refresh this with
71 // new random bytes when WriteFileStep implementation is modified
72 // in a non-backwards-compatible way.
73 var hash = Hasher.init("eagVR1dYXoE7ARDP");
74
75 {
76 var it = self.files.first;
77 while (it) |node| : (it = node.next) {
78 hash.update(node.data.basename);
79 hash.update(node.data.bytes);
80 hash.update("|");
81 }
82 }
83 var digest: [16]u8 = undefined;
84 hash.final(&digest);
85 var hash_basename: [digest.len * 2]u8 = undefined;
86 _ = std.fmt.bufPrint(
87 &hash_basename,
88 "{s}",
89 .{std.fmt.fmtSliceHexLower(&digest)},
90 ) catch unreachable;
91
92 self.output_dir = try fs.path.join(self.builder.allocator, &[_][]const u8{
93 self.builder.cache_root, "o", &hash_basename,
94 });
95 var dir = fs.cwd().makeOpenPath(self.output_dir, .{}) catch |err| {
96 std.debug.print("unable to make path {s}: {s}\n", .{ self.output_dir, @errorName(err) });
97 return err;
98 };
99 defer dir.close();
100 {
101 var it = self.files.first;
102 while (it) |node| : (it = node.next) {
103 dir.writeFile(node.data.basename, node.data.bytes) catch |err| {
104 std.debug.print("unable to write {s} into {s}: {s}\n", .{
105 node.data.basename,
106 self.output_dir,
107 @errorName(err),
108 });
109 return err;
110 };
111 node.data.source.path = fs.path.join(
112 self.builder.allocator,
113 &[_][]const u8{ self.output_dir, node.data.basename },
114 ) catch unreachable;
115 }
116 }
117}
lib/std/builtin.zig+4-1
...@@ -131,13 +131,16 @@ pub const CodeModel = enum {...@@ -131,13 +131,16 @@ pub const CodeModel = enum {
131131
132/// This data structure is used by the Zig language code generation and132/// This data structure is used by the Zig language code generation and
133/// therefore must be kept in sync with the compiler implementation.133/// therefore must be kept in sync with the compiler implementation.
134pub const Mode = enum {134pub const OptimizeMode = enum {
135 Debug,135 Debug,
136 ReleaseSafe,136 ReleaseSafe,
137 ReleaseFast,137 ReleaseFast,
138 ReleaseSmall,138 ReleaseSmall,
139};139};
140140
141/// Deprecated; use OptimizeMode.
142pub const Mode = OptimizeMode;
143
141/// This data structure is used by the Zig language code generation and144/// This data structure is used by the Zig language code generation and
142/// therefore must be kept in sync with the compiler implementation.145/// therefore must be kept in sync with the compiler implementation.
143pub const CallingConvention = enum {146pub const CallingConvention = enum {
lib/std/c.zig+2-1
...@@ -90,6 +90,8 @@ pub usingnamespace switch (builtin.os.tag) {...@@ -90,6 +90,8 @@ pub usingnamespace switch (builtin.os.tag) {
90 pub extern "c" fn stat(noalias path: [*:0]const u8, noalias buf: *c.Stat) c_int;90 pub extern "c" fn stat(noalias path: [*:0]const u8, noalias buf: *c.Stat) c_int;
9191
92 pub extern "c" fn alarm(seconds: c_uint) c_uint;92 pub extern "c" fn alarm(seconds: c_uint) c_uint;
93
94 pub extern "c" fn msync(addr: *align(page_size) const anyopaque, len: usize, flags: c_int) c_int;
93 },95 },
94};96};
9597
...@@ -145,7 +147,6 @@ pub extern "c" fn write(fd: c.fd_t, buf: [*]const u8, nbyte: usize) isize;...@@ -145,7 +147,6 @@ pub extern "c" fn write(fd: c.fd_t, buf: [*]const u8, nbyte: usize) isize;
145pub extern "c" fn pwrite(fd: c.fd_t, buf: [*]const u8, nbyte: usize, offset: c.off_t) isize;147pub extern "c" fn pwrite(fd: c.fd_t, buf: [*]const u8, nbyte: usize, offset: c.off_t) isize;
146pub extern "c" fn mmap(addr: ?*align(page_size) anyopaque, len: usize, prot: c_uint, flags: c_uint, fd: c.fd_t, offset: c.off_t) *anyopaque;148pub extern "c" fn mmap(addr: ?*align(page_size) anyopaque, len: usize, prot: c_uint, flags: c_uint, fd: c.fd_t, offset: c.off_t) *anyopaque;
147pub extern "c" fn munmap(addr: *align(page_size) const anyopaque, len: usize) c_int;149pub extern "c" fn munmap(addr: *align(page_size) const anyopaque, len: usize) c_int;
148pub extern "c" fn msync(addr: *align(page_size) const anyopaque, len: usize, flags: c_int) c_int;
149pub extern "c" fn mprotect(addr: *align(page_size) anyopaque, len: usize, prot: c_uint) c_int;150pub extern "c" fn mprotect(addr: *align(page_size) anyopaque, len: usize, prot: c_uint) c_int;
150pub extern "c" fn link(oldpath: [*:0]const u8, newpath: [*:0]const u8, flags: c_int) c_int;151pub extern "c" fn link(oldpath: [*:0]const u8, newpath: [*:0]const u8, flags: c_int) c_int;
151pub extern "c" fn linkat(oldfd: c.fd_t, oldpath: [*:0]const u8, newfd: c.fd_t, newpath: [*:0]const u8, flags: c_int) c_int;152pub extern "c" fn linkat(oldfd: c.fd_t, oldpath: [*:0]const u8, newfd: c.fd_t, newpath: [*:0]const u8, flags: c_int) c_int;
lib/std/c/netbsd.zig+3
...@@ -59,6 +59,9 @@ pub const sched_yield = __libc_thr_yield;...@@ -59,6 +59,9 @@ pub const sched_yield = __libc_thr_yield;
5959
60pub extern "c" fn posix_memalign(memptr: *?*anyopaque, alignment: usize, size: usize) c_int;60pub extern "c" fn posix_memalign(memptr: *?*anyopaque, alignment: usize, size: usize) c_int;
6161
62pub extern "c" fn __msync13(addr: *align(std.mem.page_size) const anyopaque, len: usize, flags: c_int) c_int;
63pub const msync = __msync13;
64
62pub const pthread_mutex_t = extern struct {65pub const pthread_mutex_t = extern struct {
63 magic: u32 = 0x33330003,66 magic: u32 = 0x33330003,
64 errorcheck: padded_pthread_spin_t = 0,67 errorcheck: padded_pthread_spin_t = 0,
lib/std/child_process.zig+2-2
...@@ -1164,7 +1164,7 @@ fn windowsCreateProcessPathExt(...@@ -1164,7 +1164,7 @@ fn windowsCreateProcessPathExt(
1164 var app_name_unicode_string = windows.UNICODE_STRING{1164 var app_name_unicode_string = windows.UNICODE_STRING{
1165 .Length = app_name_len_bytes,1165 .Length = app_name_len_bytes,
1166 .MaximumLength = app_name_len_bytes,1166 .MaximumLength = app_name_len_bytes,
1167 .Buffer = @intToPtr([*]u16, @ptrToInt(app_name_wildcard.ptr)),1167 .Buffer = @qualCast([*:0]u16, app_name_wildcard.ptr),
1168 };1168 };
1169 const rc = windows.ntdll.NtQueryDirectoryFile(1169 const rc = windows.ntdll.NtQueryDirectoryFile(
1170 dir.fd,1170 dir.fd,
...@@ -1261,7 +1261,7 @@ fn windowsCreateProcessPathExt(...@@ -1261,7 +1261,7 @@ fn windowsCreateProcessPathExt(
1261 var app_name_unicode_string = windows.UNICODE_STRING{1261 var app_name_unicode_string = windows.UNICODE_STRING{
1262 .Length = app_name_len_bytes,1262 .Length = app_name_len_bytes,
1263 .MaximumLength = app_name_len_bytes,1263 .MaximumLength = app_name_len_bytes,
1264 .Buffer = @intToPtr([*]u16, @ptrToInt(app_name_appended.ptr)),1264 .Buffer = @qualCast([*:0]u16, app_name_appended.ptr),
1265 };1265 };
12661266
1267 // Re-use the directory handle but this time we call with the appended app name1267 // Re-use the directory handle but this time we call with the appended app name
lib/std/cstr.zig-1
...@@ -28,7 +28,6 @@ test "cstr fns" {...@@ -28,7 +28,6 @@ test "cstr fns" {
2828
29fn testCStrFnsImpl() !void {29fn testCStrFnsImpl() !void {
30 try testing.expect(cmp("aoeu", "aoez") == -1);30 try testing.expect(cmp("aoeu", "aoez") == -1);
31 try testing.expect(mem.len("123456789") == 9);
32}31}
3332
34/// Returns a mutable, null-terminated slice with the same length as `slice`.33/// Returns a mutable, null-terminated slice with the same length as `slice`.
lib/std/debug.zig+5
...@@ -2060,6 +2060,11 @@ pub fn dumpStackPointerAddr(prefix: []const u8) void {...@@ -2060,6 +2060,11 @@ pub fn dumpStackPointerAddr(prefix: []const u8) void {
2060test "manage resources correctly" {2060test "manage resources correctly" {
2061 if (builtin.os.tag == .wasi) return error.SkipZigTest;2061 if (builtin.os.tag == .wasi) return error.SkipZigTest;
20622062
2063 if (builtin.os.tag == .windows and builtin.cpu.arch == .x86_64) {
2064 // https://github.com/ziglang/zig/issues/13963
2065 return error.SkipZigTest;
2066 }
2067
2063 const writer = std.io.null_writer;2068 const writer = std.io.null_writer;
2064 var di = try openSelfDebugInfo(testing.allocator);2069 var di = try openSelfDebugInfo(testing.allocator);
2065 defer di.deinit();2070 defer di.deinit();
lib/std/fmt.zig+6-5
...@@ -1,11 +1,12 @@...@@ -1,11 +1,12 @@
1const std = @import("std.zig");1const std = @import("std.zig");
2const builtin = @import("builtin");
3
2const io = std.io;4const io = std.io;
3const math = std.math;5const math = std.math;
4const assert = std.debug.assert;6const assert = std.debug.assert;
5const mem = std.mem;7const mem = std.mem;
6const unicode = std.unicode;8const unicode = std.unicode;
7const meta = std.meta;9const meta = std.meta;
8const builtin = @import("builtin");
9const errol = @import("fmt/errol.zig");10const errol = @import("fmt/errol.zig");
10const lossyCast = std.math.lossyCast;11const lossyCast = std.math.lossyCast;
11const expectFmt = std.testing.expectFmt;12const expectFmt = std.testing.expectFmt;
...@@ -190,7 +191,7 @@ pub fn format(...@@ -190,7 +191,7 @@ pub fn format(
190 .precision = precision,191 .precision = precision,
191 },192 },
192 writer,193 writer,
193 default_max_depth,194 std.options.fmt_max_depth,
194 );195 );
195 }196 }
196197
...@@ -2140,15 +2141,15 @@ test "buffer" {...@@ -2140,15 +2141,15 @@ test "buffer" {
2140 {2141 {
2141 var buf1: [32]u8 = undefined;2142 var buf1: [32]u8 = undefined;
2142 var fbs = std.io.fixedBufferStream(&buf1);2143 var fbs = std.io.fixedBufferStream(&buf1);
2143 try formatType(1234, "", FormatOptions{}, fbs.writer(), default_max_depth);2144 try formatType(1234, "", FormatOptions{}, fbs.writer(), std.options.fmt_max_depth);
2144 try std.testing.expect(mem.eql(u8, fbs.getWritten(), "1234"));2145 try std.testing.expect(mem.eql(u8, fbs.getWritten(), "1234"));
21452146
2146 fbs.reset();2147 fbs.reset();
2147 try formatType('a', "c", FormatOptions{}, fbs.writer(), default_max_depth);2148 try formatType('a', "c", FormatOptions{}, fbs.writer(), std.options.fmt_max_depth);
2148 try std.testing.expect(mem.eql(u8, fbs.getWritten(), "a"));2149 try std.testing.expect(mem.eql(u8, fbs.getWritten(), "a"));
21492150
2150 fbs.reset();2151 fbs.reset();
2151 try formatType(0b1100, "b", FormatOptions{}, fbs.writer(), default_max_depth);2152 try formatType(0b1100, "b", FormatOptions{}, fbs.writer(), std.options.fmt_max_depth);
2152 try std.testing.expect(mem.eql(u8, fbs.getWritten(), "1100"));2153 try std.testing.expect(mem.eql(u8, fbs.getWritten(), "1100"));
2153 }2154 }
2154}2155}
lib/std/fs.zig+2-2
...@@ -834,7 +834,7 @@ pub const IterableDir = struct {...@@ -834,7 +834,7 @@ pub const IterableDir = struct {
834 self.end_index = self.index; // Force fd_readdir in the next loop.834 self.end_index = self.index; // Force fd_readdir in the next loop.
835 continue :start_over;835 continue :start_over;
836 }836 }
837 const name = mem.span(self.buf[name_index .. name_index + entry.d_namlen]);837 const name = self.buf[name_index .. name_index + entry.d_namlen];
838838
839 const next_index = name_index + entry.d_namlen;839 const next_index = name_index + entry.d_namlen;
840 self.index = next_index;840 self.index = next_index;
...@@ -1763,7 +1763,7 @@ pub const Dir = struct {...@@ -1763,7 +1763,7 @@ pub const Dir = struct {
1763 var nt_name = w.UNICODE_STRING{1763 var nt_name = w.UNICODE_STRING{
1764 .Length = path_len_bytes,1764 .Length = path_len_bytes,
1765 .MaximumLength = path_len_bytes,1765 .MaximumLength = path_len_bytes,
1766 .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w)),1766 .Buffer = @qualCast([*:0]u16, sub_path_w),
1767 };1767 };
1768 var attr = w.OBJECT_ATTRIBUTES{1768 var attr = w.OBJECT_ATTRIBUTES{
1769 .Length = @sizeOf(w.OBJECT_ATTRIBUTES),1769 .Length = @sizeOf(w.OBJECT_ATTRIBUTES),
lib/std/fs/file.zig+2-1
...@@ -179,7 +179,7 @@ pub const File = struct {...@@ -179,7 +179,7 @@ pub const File = struct {
179 lock_nonblocking: bool = false,179 lock_nonblocking: bool = false,
180180
181 /// For POSIX systems this is the file system mode the file will181 /// For POSIX systems this is the file system mode the file will
182 /// be created with.182 /// be created with. On other systems this is always 0.
183 mode: Mode = default_mode,183 mode: Mode = default_mode,
184184
185 /// Setting this to `.blocking` prevents `O.NONBLOCK` from being passed even185 /// Setting this to `.blocking` prevents `O.NONBLOCK` from being passed even
...@@ -307,6 +307,7 @@ pub const File = struct {...@@ -307,6 +307,7 @@ pub const File = struct {
307 /// is unique to each filesystem.307 /// is unique to each filesystem.
308 inode: INode,308 inode: INode,
309 size: u64,309 size: u64,
310 /// This is available on POSIX systems and is always 0 otherwise.
310 mode: Mode,311 mode: Mode,
311 kind: Kind,312 kind: Kind,
312313
lib/std/io/fixed_buffer_stream.zig+19-6
...@@ -113,14 +113,27 @@ pub fn FixedBufferStream(comptime Buffer: type) type {...@@ -113,14 +113,27 @@ pub fn FixedBufferStream(comptime Buffer: type) type {
113 };113 };
114}114}
115115
116pub fn fixedBufferStream(buffer: anytype) FixedBufferStream(NonSentinelSpan(@TypeOf(buffer))) {116pub fn fixedBufferStream(buffer: anytype) FixedBufferStream(Slice(@TypeOf(buffer))) {
117 return .{ .buffer = mem.span(buffer), .pos = 0 };117 return .{ .buffer = buffer, .pos = 0 };
118}118}
119119
120fn NonSentinelSpan(comptime T: type) type {120fn Slice(comptime T: type) type {
121 var ptr_info = @typeInfo(mem.Span(T)).Pointer;121 switch (@typeInfo(T)) {
122 ptr_info.sentinel = null;122 .Pointer => |ptr_info| {
123 return @Type(.{ .Pointer = ptr_info });123 var new_ptr_info = ptr_info;
124 switch (ptr_info.size) {
125 .Slice => {},
126 .One => switch (@typeInfo(ptr_info.child)) {
127 .Array => |info| new_ptr_info.child = info.child,
128 else => @compileError("invalid type given to fixedBufferStream"),
129 },
130 else => @compileError("invalid type given to fixedBufferStream"),
131 }
132 new_ptr_info.size = .Slice;
133 return @Type(.{ .Pointer = new_ptr_info });
134 },
135 else => @compileError("invalid type given to fixedBufferStream"),
136 }
124}137}
125138
126test "FixedBufferStream output" {139test "FixedBufferStream output" {
lib/std/json.zig+2-1
...@@ -1384,7 +1384,7 @@ fn ParseInternalErrorImpl(comptime T: type, comptime inferred_types: []const typ...@@ -1384,7 +1384,7 @@ fn ParseInternalErrorImpl(comptime T: type, comptime inferred_types: []const typ
1384 return errors;1384 return errors;
1385 },1385 },
1386 .Array => |arrayInfo| {1386 .Array => |arrayInfo| {
1387 return error{ UnexpectedEndOfJson, UnexpectedToken } || TokenStream.Error ||1387 return error{ UnexpectedEndOfJson, UnexpectedToken, LengthMismatch } || TokenStream.Error ||
1388 UnescapeValidStringError ||1388 UnescapeValidStringError ||
1389 ParseInternalErrorImpl(arrayInfo.child, inferred_types ++ [_]type{T});1389 ParseInternalErrorImpl(arrayInfo.child, inferred_types ++ [_]type{T});
1390 },1390 },
...@@ -1625,6 +1625,7 @@ fn parseInternal(...@@ -1625,6 +1625,7 @@ fn parseInternal(
1625 if (arrayInfo.child != u8) return error.UnexpectedToken;1625 if (arrayInfo.child != u8) return error.UnexpectedToken;
1626 var r: T = undefined;1626 var r: T = undefined;
1627 const source_slice = stringToken.slice(tokens.slice, tokens.i - 1);1627 const source_slice = stringToken.slice(tokens.slice, tokens.i - 1);
1628 if (r.len != stringToken.decodedLength()) return error.LengthMismatch;
1628 switch (stringToken.escapes) {1629 switch (stringToken.escapes) {
1629 .None => mem.copy(u8, &r, source_slice),1630 .None => mem.copy(u8, &r, source_slice),
1630 .Some => try unescapeValidString(&r, source_slice),1631 .Some => try unescapeValidString(&r, source_slice),
lib/std/json/test.zig+6
...@@ -2238,6 +2238,12 @@ test "parse into struct with no fields" {...@@ -2238,6 +2238,12 @@ test "parse into struct with no fields" {
2238 try testing.expectEqual(T{}, try parse(T, &ts, ParseOptions{}));2238 try testing.expectEqual(T{}, try parse(T, &ts, ParseOptions{}));
2239}2239}
22402240
2241test "parse into struct where destination and source lengths mismatch" {
2242 const T = struct { a: [2]u8 };
2243 var ts = TokenStream.init("{\"a\": \"bbb\"}");
2244 try testing.expectError(error.LengthMismatch, parse(T, &ts, ParseOptions{}));
2245}
2246
2241test "parse into struct with misc fields" {2247test "parse into struct with misc fields" {
2242 @setEvalBranchQuota(10000);2248 @setEvalBranchQuota(10000);
2243 const options = ParseOptions{ .allocator = testing.allocator };2249 const options = ParseOptions{ .allocator = testing.allocator };
lib/std/mem.zig+24-78
...@@ -636,12 +636,9 @@ test "indexOfDiff" {...@@ -636,12 +636,9 @@ test "indexOfDiff" {
636 try testing.expectEqual(indexOfDiff(u8, "xne", "one"), 0);636 try testing.expectEqual(indexOfDiff(u8, "xne", "one"), 0);
637}637}
638638
639/// Takes a pointer to an array, a sentinel-terminated pointer, or a slice, and639/// Takes a sentinel-terminated pointer and returns a slice preserving pointer attributes.
640/// returns a slice. If there is a sentinel on the input type, there will be a640/// `[*c]` pointers are assumed to be 0-terminated and assumed to not be allowzero.
641/// sentinel on the output type. The constness of the output type matches641fn Span(comptime T: type) type {
642/// the constness of the input type. `[*c]` pointers are assumed to be 0-terminated,
643/// and assumed to not allow null.
644pub fn Span(comptime T: type) type {
645 switch (@typeInfo(T)) {642 switch (@typeInfo(T)) {
646 .Optional => |optional_info| {643 .Optional => |optional_info| {
647 return ?Span(optional_info.child);644 return ?Span(optional_info.child);
...@@ -649,39 +646,22 @@ pub fn Span(comptime T: type) type {...@@ -649,39 +646,22 @@ pub fn Span(comptime T: type) type {
649 .Pointer => |ptr_info| {646 .Pointer => |ptr_info| {
650 var new_ptr_info = ptr_info;647 var new_ptr_info = ptr_info;
651 switch (ptr_info.size) {648 switch (ptr_info.size) {
652 .One => switch (@typeInfo(ptr_info.child)) {
653 .Array => |info| {
654 new_ptr_info.child = info.child;
655 new_ptr_info.sentinel = info.sentinel;
656 },
657 else => @compileError("invalid type given to std.mem.Span"),
658 },
659 .C => {649 .C => {
660 new_ptr_info.sentinel = &@as(ptr_info.child, 0);650 new_ptr_info.sentinel = &@as(ptr_info.child, 0);
661 new_ptr_info.is_allowzero = false;651 new_ptr_info.is_allowzero = false;
662 },652 },
663 .Many, .Slice => {},653 .Many => if (ptr_info.sentinel == null) @compileError("invalid type given to std.mem.span: " ++ @typeName(T)),
654 .One, .Slice => @compileError("invalid type given to std.mem.span: " ++ @typeName(T)),
664 }655 }
665 new_ptr_info.size = .Slice;656 new_ptr_info.size = .Slice;
666 return @Type(.{ .Pointer = new_ptr_info });657 return @Type(.{ .Pointer = new_ptr_info });
667 },658 },
668 else => @compileError("invalid type given to std.mem.Span"),659 else => {},
669 }660 }
661 @compileError("invalid type given to std.mem.span: " ++ @typeName(T));
670}662}
671663
672test "Span" {664test "Span" {
673 try testing.expect(Span(*[5]u16) == []u16);
674 try testing.expect(Span(?*[5]u16) == ?[]u16);
675 try testing.expect(Span(*const [5]u16) == []const u16);
676 try testing.expect(Span(?*const [5]u16) == ?[]const u16);
677 try testing.expect(Span([]u16) == []u16);
678 try testing.expect(Span(?[]u16) == ?[]u16);
679 try testing.expect(Span([]const u8) == []const u8);
680 try testing.expect(Span(?[]const u8) == ?[]const u8);
681 try testing.expect(Span([:1]u16) == [:1]u16);
682 try testing.expect(Span(?[:1]u16) == ?[:1]u16);
683 try testing.expect(Span([:1]const u8) == [:1]const u8);
684 try testing.expect(Span(?[:1]const u8) == ?[:1]const u8);
685 try testing.expect(Span([*:1]u16) == [:1]u16);665 try testing.expect(Span([*:1]u16) == [:1]u16);
686 try testing.expect(Span(?[*:1]u16) == ?[:1]u16);666 try testing.expect(Span(?[*:1]u16) == ?[:1]u16);
687 try testing.expect(Span([*:1]const u8) == [:1]const u8);667 try testing.expect(Span([*:1]const u8) == [:1]const u8);
...@@ -692,13 +672,10 @@ test "Span" {...@@ -692,13 +672,10 @@ test "Span" {
692 try testing.expect(Span(?[*c]const u8) == ?[:0]const u8);672 try testing.expect(Span(?[*c]const u8) == ?[:0]const u8);
693}673}
694674
695/// Takes a pointer to an array, a sentinel-terminated pointer, or a slice, and675/// Takes a sentinel-terminated pointer and returns a slice, iterating over the
696/// returns a slice. If there is a sentinel on the input type, there will be a676/// memory to find the sentinel and determine the length.
697/// sentinel on the output type. The constness of the output type matches677/// Ponter attributes such as const are preserved.
698/// the constness of the input type.678/// `[*c]` pointers are assumed to be non-null and 0-terminated.
699///
700/// When there is both a sentinel and an array length or slice length, the
701/// length value is used instead of the sentinel.
702pub fn span(ptr: anytype) Span(@TypeOf(ptr)) {679pub fn span(ptr: anytype) Span(@TypeOf(ptr)) {
703 if (@typeInfo(@TypeOf(ptr)) == .Optional) {680 if (@typeInfo(@TypeOf(ptr)) == .Optional) {
704 if (ptr) |non_null| {681 if (ptr) |non_null| {
...@@ -722,7 +699,6 @@ test "span" {...@@ -722,7 +699,6 @@ test "span" {
722 var array: [5]u16 = [_]u16{ 1, 2, 3, 4, 5 };699 var array: [5]u16 = [_]u16{ 1, 2, 3, 4, 5 };
723 const ptr = @as([*:3]u16, array[0..2 :3]);700 const ptr = @as([*:3]u16, array[0..2 :3]);
724 try testing.expect(eql(u16, span(ptr), &[_]u16{ 1, 2 }));701 try testing.expect(eql(u16, span(ptr), &[_]u16{ 1, 2 }));
725 try testing.expect(eql(u16, span(&array), &[_]u16{ 1, 2, 3, 4, 5 }));
726 try testing.expectEqual(@as(?[:0]u16, null), span(@as(?[*:0]u16, null)));702 try testing.expectEqual(@as(?[:0]u16, null), span(@as(?[*:0]u16, null)));
727}703}
728704
...@@ -919,22 +895,15 @@ test "lenSliceTo" {...@@ -919,22 +895,15 @@ test "lenSliceTo" {
919 }895 }
920}896}
921897
922/// Takes a pointer to an array, an array, a vector, a sentinel-terminated pointer,898/// Takes a sentinel-terminated pointer and iterates over the memory to find the
923/// a slice or a tuple, and returns the length.899/// sentinel and determine the length.
924/// In the case of a sentinel-terminated array, it uses the array length.900/// `[*c]` pointers are assumed to be non-null and 0-terminated.
925/// For C pointers it assumes it is a pointer-to-many with a 0 sentinel.
926pub fn len(value: anytype) usize {901pub fn len(value: anytype) usize {
927 return switch (@typeInfo(@TypeOf(value))) {902 switch (@typeInfo(@TypeOf(value))) {
928 .Array => |info| info.len,
929 .Vector => |info| info.len,
930 .Pointer => |info| switch (info.size) {903 .Pointer => |info| switch (info.size) {
931 .One => switch (@typeInfo(info.child)) {
932 .Array => value.len,
933 else => @compileError("invalid type given to std.mem.len"),
934 },
935 .Many => {904 .Many => {
936 const sentinel_ptr = info.sentinel orelse905 const sentinel_ptr = info.sentinel orelse
937 @compileError("length of pointer with no sentinel");906 @compileError("invalid type given to std.mem.len: " ++ @typeName(@TypeOf(value)));
938 const sentinel = @ptrCast(*align(1) const info.child, sentinel_ptr).*;907 const sentinel = @ptrCast(*align(1) const info.child, sentinel_ptr).*;
939 return indexOfSentinel(info.child, sentinel, value);908 return indexOfSentinel(info.child, sentinel, value);
940 },909 },
...@@ -942,41 +911,18 @@ pub fn len(value: anytype) usize {...@@ -942,41 +911,18 @@ pub fn len(value: anytype) usize {
942 assert(value != null);911 assert(value != null);
943 return indexOfSentinel(info.child, 0, value);912 return indexOfSentinel(info.child, 0, value);
944 },913 },
945 .Slice => value.len,914 else => @compileError("invalid type given to std.mem.len: " ++ @typeName(@TypeOf(value))),
946 },915 },
947 .Struct => |info| if (info.is_tuple) {916 else => @compileError("invalid type given to std.mem.len: " ++ @typeName(@TypeOf(value))),
948 return info.fields.len;917 }
949 } else @compileError("invalid type given to std.mem.len"),
950 else => @compileError("invalid type given to std.mem.len"),
951 };
952}918}
953919
954test "len" {920test "len" {
955 try testing.expect(len("aoeu") == 4);921 var array: [5]u16 = [_]u16{ 1, 2, 0, 4, 5 };
956922 const ptr = @as([*:4]u16, array[0..3 :4]);
957 {923 try testing.expect(len(ptr) == 3);
958 var array: [5]u16 = [_]u16{ 1, 2, 3, 4, 5 };924 const c_ptr = @as([*c]u16, ptr);
959 try testing.expect(len(&array) == 5);925 try testing.expect(len(c_ptr) == 2);
960 try testing.expect(len(array[0..3]) == 3);
961 array[2] = 0;
962 const ptr = @as([*:0]u16, array[0..2 :0]);
963 try testing.expect(len(ptr) == 2);
964 }
965 {
966 var array: [5:0]u16 = [_:0]u16{ 1, 2, 3, 4, 5 };
967 try testing.expect(len(&array) == 5);
968 array[2] = 0;
969 try testing.expect(len(&array) == 5);
970 }
971 {
972 const vector: meta.Vector(2, u32) = [2]u32{ 1, 2 };
973 try testing.expect(len(vector) == 2);
974 }
975 {
976 const tuple = .{ 1, 2 };
977 try testing.expect(len(tuple) == 2);
978 try testing.expect(tuple[0] == 1);
979 }
980}926}
981927
982pub fn indexOfSentinel(comptime Elem: type, comptime sentinel: Elem, ptr: [*:sentinel]const Elem) usize {928pub fn indexOfSentinel(comptime Elem: type, comptime sentinel: Elem, ptr: [*:sentinel]const Elem) usize {
lib/std/os.zig+5-6
...@@ -550,7 +550,6 @@ pub fn abort() noreturn {...@@ -550,7 +550,6 @@ pub fn abort() noreturn {
550 exit(0); // TODO choose appropriate exit code550 exit(0); // TODO choose appropriate exit code
551 }551 }
552 if (builtin.os.tag == .wasi) {552 if (builtin.os.tag == .wasi) {
553 @breakpoint();
554 exit(1);553 exit(1);
555 }554 }
556 if (builtin.os.tag == .cuda) {555 if (builtin.os.tag == .cuda) {
...@@ -4514,7 +4513,7 @@ pub fn faccessatW(dirfd: fd_t, sub_path_w: [*:0]const u16, mode: u32, flags: u32...@@ -4514,7 +4513,7 @@ pub fn faccessatW(dirfd: fd_t, sub_path_w: [*:0]const u16, mode: u32, flags: u32
4514 var nt_name = windows.UNICODE_STRING{4513 var nt_name = windows.UNICODE_STRING{
4515 .Length = path_len_bytes,4514 .Length = path_len_bytes,
4516 .MaximumLength = path_len_bytes,4515 .MaximumLength = path_len_bytes,
4517 .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w)),4516 .Buffer = @qualCast([*:0]u16, sub_path_w),
4518 };4517 };
4519 var attr = windows.OBJECT_ATTRIBUTES{4518 var attr = windows.OBJECT_ATTRIBUTES{
4520 .Length = @sizeOf(windows.OBJECT_ATTRIBUTES),4519 .Length = @sizeOf(windows.OBJECT_ATTRIBUTES),
...@@ -6029,7 +6028,7 @@ pub fn sendfile(...@@ -6029,7 +6028,7 @@ pub fn sendfile(
6029 .BADF => unreachable, // Always a race condition.6028 .BADF => unreachable, // Always a race condition.
6030 .FAULT => unreachable, // Segmentation fault.6029 .FAULT => unreachable, // Segmentation fault.
6031 .OVERFLOW => unreachable, // We avoid passing too large of a `count`.6030 .OVERFLOW => unreachable, // We avoid passing too large of a `count`.
6032 .NOTCONN => unreachable, // `out_fd` is an unconnected socket.6031 .NOTCONN => return error.BrokenPipe, // `out_fd` is an unconnected socket
60336032
6034 .INVAL, .NOSYS => {6033 .INVAL, .NOSYS => {
6035 // EINVAL could be any of the following situations:6034 // EINVAL could be any of the following situations:
...@@ -6097,7 +6096,7 @@ pub fn sendfile(...@@ -6097,7 +6096,7 @@ pub fn sendfile(
60976096
6098 .BADF => unreachable, // Always a race condition.6097 .BADF => unreachable, // Always a race condition.
6099 .FAULT => unreachable, // Segmentation fault.6098 .FAULT => unreachable, // Segmentation fault.
6100 .NOTCONN => unreachable, // `out_fd` is an unconnected socket.6099 .NOTCONN => return error.BrokenPipe, // `out_fd` is an unconnected socket
61016100
6102 .INVAL, .OPNOTSUPP, .NOTSOCK, .NOSYS => {6101 .INVAL, .OPNOTSUPP, .NOTSOCK, .NOSYS => {
6103 // EINVAL could be any of the following situations:6102 // EINVAL could be any of the following situations:
...@@ -6179,7 +6178,7 @@ pub fn sendfile(...@@ -6179,7 +6178,7 @@ pub fn sendfile(
6179 .BADF => unreachable, // Always a race condition.6178 .BADF => unreachable, // Always a race condition.
6180 .FAULT => unreachable, // Segmentation fault.6179 .FAULT => unreachable, // Segmentation fault.
6181 .INVAL => unreachable,6180 .INVAL => unreachable,
6182 .NOTCONN => unreachable, // `out_fd` is an unconnected socket.6181 .NOTCONN => return error.BrokenPipe, // `out_fd` is an unconnected socket
61836182
6184 .OPNOTSUPP, .NOTSOCK, .NOSYS => break :sf,6183 .OPNOTSUPP, .NOTSOCK, .NOSYS => break :sf,
61856184
...@@ -6473,7 +6472,7 @@ pub fn recvfrom(...@@ -6473,7 +6472,7 @@ pub fn recvfrom(
6473 .BADF => unreachable, // always a race condition6472 .BADF => unreachable, // always a race condition
6474 .FAULT => unreachable,6473 .FAULT => unreachable,
6475 .INVAL => unreachable,6474 .INVAL => unreachable,
6476 .NOTCONN => unreachable,6475 .NOTCONN => return error.SocketNotConnected,
6477 .NOTSOCK => unreachable,6476 .NOTSOCK => unreachable,
6478 .INTR => continue,6477 .INTR => continue,
6479 .AGAIN => return error.WouldBlock,6478 .AGAIN => return error.WouldBlock,
lib/std/os/uefi/pool_allocator.zig+1-1
...@@ -22,7 +22,7 @@ const UefiPoolAllocator = struct {...@@ -22,7 +22,7 @@ const UefiPoolAllocator = struct {
2222
23 assert(len > 0);23 assert(len > 0);
2424
25 const ptr_align = 1 << log2_ptr_align;25 const ptr_align = @as(usize, 1) << @intCast(Allocator.Log2Align, log2_ptr_align);
2626
27 const metadata_len = mem.alignForward(@sizeOf(usize), ptr_align);27 const metadata_len = mem.alignForward(@sizeOf(usize), ptr_align);
2828
lib/std/os/windows.zig+7-7
...@@ -85,7 +85,7 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN...@@ -85,7 +85,7 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN
85 var nt_name = UNICODE_STRING{85 var nt_name = UNICODE_STRING{
86 .Length = path_len_bytes,86 .Length = path_len_bytes,
87 .MaximumLength = path_len_bytes,87 .MaximumLength = path_len_bytes,
88 .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w.ptr)),88 .Buffer = @qualCast([*]u16, sub_path_w.ptr),
89 };89 };
90 var attr = OBJECT_ATTRIBUTES{90 var attr = OBJECT_ATTRIBUTES{
91 .Length = @sizeOf(OBJECT_ATTRIBUTES),91 .Length = @sizeOf(OBJECT_ATTRIBUTES),
...@@ -634,7 +634,7 @@ pub fn SetCurrentDirectory(path_name: []const u16) SetCurrentDirectoryError!void...@@ -634,7 +634,7 @@ pub fn SetCurrentDirectory(path_name: []const u16) SetCurrentDirectoryError!void
634 var nt_name = UNICODE_STRING{634 var nt_name = UNICODE_STRING{
635 .Length = path_len_bytes,635 .Length = path_len_bytes,
636 .MaximumLength = path_len_bytes,636 .MaximumLength = path_len_bytes,
637 .Buffer = @intToPtr([*]u16, @ptrToInt(path_name.ptr)),637 .Buffer = @qualCast([*]u16, path_name.ptr),
638 };638 };
639639
640 const rc = ntdll.RtlSetCurrentDirectory_U(&nt_name);640 const rc = ntdll.RtlSetCurrentDirectory_U(&nt_name);
...@@ -766,7 +766,7 @@ pub fn ReadLink(dir: ?HANDLE, sub_path_w: []const u16, out_buffer: []u8) ReadLin...@@ -766,7 +766,7 @@ pub fn ReadLink(dir: ?HANDLE, sub_path_w: []const u16, out_buffer: []u8) ReadLin
766 var nt_name = UNICODE_STRING{766 var nt_name = UNICODE_STRING{
767 .Length = path_len_bytes,767 .Length = path_len_bytes,
768 .MaximumLength = path_len_bytes,768 .MaximumLength = path_len_bytes,
769 .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w.ptr)),769 .Buffer = @qualCast([*]u16, sub_path_w.ptr),
770 };770 };
771 var attr = OBJECT_ATTRIBUTES{771 var attr = OBJECT_ATTRIBUTES{
772 .Length = @sizeOf(OBJECT_ATTRIBUTES),772 .Length = @sizeOf(OBJECT_ATTRIBUTES),
...@@ -876,7 +876,7 @@ pub fn DeleteFile(sub_path_w: []const u16, options: DeleteFileOptions) DeleteFil...@@ -876,7 +876,7 @@ pub fn DeleteFile(sub_path_w: []const u16, options: DeleteFileOptions) DeleteFil
876 .Length = path_len_bytes,876 .Length = path_len_bytes,
877 .MaximumLength = path_len_bytes,877 .MaximumLength = path_len_bytes,
878 // The Windows API makes this mutable, but it will not mutate here.878 // The Windows API makes this mutable, but it will not mutate here.
879 .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w.ptr)),879 .Buffer = @qualCast([*]u16, sub_path_w.ptr),
880 };880 };
881881
882 if (sub_path_w[0] == '.' and sub_path_w[1] == 0) {882 if (sub_path_w[0] == '.' and sub_path_w[1] == 0) {
...@@ -1414,7 +1414,7 @@ pub fn sendmsg(...@@ -1414,7 +1414,7 @@ pub fn sendmsg(
1414}1414}
14151415
1416pub 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 {1416pub fn sendto(s: ws2_32.SOCKET, buf: [*]const u8, len: usize, flags: u32, to: ?*const ws2_32.sockaddr, to_len: ws2_32.socklen_t) i32 {
1417 var buffer = ws2_32.WSABUF{ .len = @truncate(u31, len), .buf = @intToPtr([*]u8, @ptrToInt(buf)) };1417 var buffer = ws2_32.WSABUF{ .len = @truncate(u31, len), .buf = @qualCast([*]u8, buf) };
1418 var bytes_send: DWORD = undefined;1418 var bytes_send: DWORD = undefined;
1419 if (ws2_32.WSASendTo(s, @ptrCast([*]ws2_32.WSABUF, &buffer), 1, &bytes_send, flags, to, @intCast(i32, to_len), null, null) == ws2_32.SOCKET_ERROR) {1419 if (ws2_32.WSASendTo(s, @ptrCast([*]ws2_32.WSABUF, &buffer), 1, &bytes_send, flags, to, @intCast(i32, to_len), null, null) == ws2_32.SOCKET_ERROR) {
1420 return ws2_32.SOCKET_ERROR;1420 return ws2_32.SOCKET_ERROR;
...@@ -1876,13 +1876,13 @@ pub fn eqlIgnoreCaseWTF16(a: []const u16, b: []const u16) bool {...@@ -1876,13 +1876,13 @@ pub fn eqlIgnoreCaseWTF16(a: []const u16, b: []const u16) bool {
1876 const a_string = UNICODE_STRING{1876 const a_string = UNICODE_STRING{
1877 .Length = a_bytes,1877 .Length = a_bytes,
1878 .MaximumLength = a_bytes,1878 .MaximumLength = a_bytes,
1879 .Buffer = @intToPtr([*]u16, @ptrToInt(a.ptr)),1879 .Buffer = @qualCast([*]u16, a.ptr),
1880 };1880 };
1881 const b_bytes = @intCast(u16, b.len * 2);1881 const b_bytes = @intCast(u16, b.len * 2);
1882 const b_string = UNICODE_STRING{1882 const b_string = UNICODE_STRING{
1883 .Length = b_bytes,1883 .Length = b_bytes,
1884 .MaximumLength = b_bytes,1884 .MaximumLength = b_bytes,
1885 .Buffer = @intToPtr([*]u16, @ptrToInt(b.ptr)),1885 .Buffer = @qualCast([*]u16, b.ptr),
1886 };1886 };
1887 return ntdll.RtlEqualUnicodeString(&a_string, &b_string, TRUE) == TRUE;1887 return ntdll.RtlEqualUnicodeString(&a_string, &b_string, TRUE) == TRUE;
1888}1888}
lib/std/start_windows_tls.zig+5-3
...@@ -7,12 +7,14 @@ export var _tls_end: u8 linksection(".tls$ZZZ") = 0;...@@ -7,12 +7,14 @@ export var _tls_end: u8 linksection(".tls$ZZZ") = 0;
7export var __xl_a: std.os.windows.PIMAGE_TLS_CALLBACK linksection(".CRT$XLA") = null;7export var __xl_a: std.os.windows.PIMAGE_TLS_CALLBACK linksection(".CRT$XLA") = null;
8export var __xl_z: std.os.windows.PIMAGE_TLS_CALLBACK linksection(".CRT$XLZ") = null;8export var __xl_z: std.os.windows.PIMAGE_TLS_CALLBACK linksection(".CRT$XLZ") = null;
99
10const tls_array: u32 = 0x2c;
11comptime {10comptime {
12 if (builtin.target.cpu.arch == .x86) {11 if (builtin.target.cpu.arch == .x86 and builtin.zig_backend != .stage2_c) {
13 // The __tls_array is the offset of the ThreadLocalStoragePointer field12 // The __tls_array is the offset of the ThreadLocalStoragePointer field
14 // in the TEB block whose base address held in the %fs segment.13 // in the TEB block whose base address held in the %fs segment.
15 @export(tls_array, .{ .name = "_tls_array" });14 asm (
15 \\ .global __tls_array
16 \\ __tls_array = 0x2C
17 );
16 }18 }
17}19}
1820
lib/std/std.zig+9-1
...@@ -9,6 +9,7 @@ pub const AutoArrayHashMapUnmanaged = array_hash_map.AutoArrayHashMapUnmanaged;...@@ -9,6 +9,7 @@ pub const AutoArrayHashMapUnmanaged = array_hash_map.AutoArrayHashMapUnmanaged;
9pub const AutoHashMap = hash_map.AutoHashMap;9pub const AutoHashMap = hash_map.AutoHashMap;
10pub const AutoHashMapUnmanaged = hash_map.AutoHashMapUnmanaged;10pub const AutoHashMapUnmanaged = hash_map.AutoHashMapUnmanaged;
11pub const BoundedArray = @import("bounded_array.zig").BoundedArray;11pub const BoundedArray = @import("bounded_array.zig").BoundedArray;
12pub const Build = @import("Build.zig");
12pub const BufMap = @import("buf_map.zig").BufMap;13pub const BufMap = @import("buf_map.zig").BufMap;
13pub const BufSet = @import("buf_set.zig").BufSet;14pub const BufSet = @import("buf_set.zig").BufSet;
14pub const ChildProcess = @import("child_process.zig").ChildProcess;15pub const ChildProcess = @import("child_process.zig").ChildProcess;
...@@ -49,7 +50,6 @@ pub const array_hash_map = @import("array_hash_map.zig");...@@ -49,7 +50,6 @@ pub const array_hash_map = @import("array_hash_map.zig");
49pub const atomic = @import("atomic.zig");50pub const atomic = @import("atomic.zig");
50pub const base64 = @import("base64.zig");51pub const base64 = @import("base64.zig");
51pub const bit_set = @import("bit_set.zig");52pub const bit_set = @import("bit_set.zig");
52pub const build = @import("build.zig");
53pub const builtin = @import("builtin.zig");53pub const builtin = @import("builtin.zig");
54pub const c = @import("c.zig");54pub const c = @import("c.zig");
55pub const coff = @import("coff.zig");55pub const coff = @import("coff.zig");
...@@ -96,6 +96,9 @@ pub const wasm = @import("wasm.zig");...@@ -96,6 +96,9 @@ pub const wasm = @import("wasm.zig");
96pub const zig = @import("zig.zig");96pub const zig = @import("zig.zig");
97pub const start = @import("start.zig");97pub const start = @import("start.zig");
9898
99/// deprecated: use `Build`.
100pub const build = Build;
101
99const root = @import("root");102const root = @import("root");
100const options_override = if (@hasDecl(root, "std_options")) root.std_options else struct {};103const options_override = if (@hasDecl(root, "std_options")) root.std_options else struct {};
101104
...@@ -150,6 +153,11 @@ pub const options = struct {...@@ -150,6 +153,11 @@ pub const options = struct {
150 else153 else
151 log.defaultLog;154 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
153 pub const cryptoRandomSeed: fn (buffer: []u8) void = if (@hasDecl(options_override, "cryptoRandomSeed"))161 pub const cryptoRandomSeed: fn (buffer: []u8) void = if (@hasDecl(options_override, "cryptoRandomSeed"))
154 options_override.cryptoRandomSeed162 options_override.cryptoRandomSeed
155 else163 else
lib/std/tar.zig+23
...@@ -1,6 +1,18 @@...@@ -1,6 +1,18 @@
1pub const Options = struct {1pub const Options = struct {
2 /// Number of directory levels to skip when extracting files.2 /// Number of directory levels to skip when extracting files.
3 strip_components: u32 = 0,3 strip_components: u32 = 0,
4 /// How to handle the "mode" property of files from within the tar file.
5 mode_mode: ModeMode = .executable_bit_only,
6
7 const ModeMode = enum {
8 /// The mode from the tar file is completely ignored. Files are created
9 /// with the default mode when creating files.
10 ignore,
11 /// The mode from the tar file is inspected for the owner executable bit
12 /// only. This bit is copied to the group and other executable bits.
13 /// Other bits of the mode are left as the default when creating files.
14 executable_bit_only,
15 };
4};16};
517
6pub const Header = struct {18pub const Header = struct {
...@@ -72,6 +84,17 @@ pub const Header = struct {...@@ -72,6 +84,17 @@ pub const Header = struct {
72};84};
7385
74pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: Options) !void {86pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: Options) !void {
87 switch (options.mode_mode) {
88 .ignore => {},
89 .executable_bit_only => {
90 // This code does not look at the mode bits yet. To implement this feature,
91 // the implementation must be adjusted to look at the mode, and check the
92 // user executable bit, then call fchmod on newly created files when
93 // the executable bit is supposed to be set.
94 // It also needs to properly deal with ACLs on Windows.
95 @panic("TODO: unimplemented: tar ModeMode.executable_bit_only");
96 },
97 }
75 var file_name_buffer: [255]u8 = undefined;98 var file_name_buffer: [255]u8 = undefined;
76 var buffer: [512 * 8]u8 = undefined;99 var buffer: [512 * 8]u8 = undefined;
77 var start: usize = 0;100 var start: usize = 0;
lib/std/target.zig+556-4
...@@ -702,9 +702,6 @@ pub const Target = struct {...@@ -702,9 +702,6 @@ pub const Target = struct {
702 pub const ShiftInt = std.math.Log2Int(usize);702 pub const ShiftInt = std.math.Log2Int(usize);
703703
704 pub const empty = Set{ .ints = [1]usize{0} ** usize_count };704 pub const empty = Set{ .ints = [1]usize{0} ** usize_count };
705 pub fn empty_workaround() Set {
706 return Set{ .ints = [1]usize{0} ** usize_count };
707 }
708705
709 pub fn isEmpty(set: Set) bool {706 pub fn isEmpty(set: Set) bool {
710 return for (set.ints) |x| {707 return for (set.ints) |x| {
...@@ -787,7 +784,7 @@ pub const Target = struct {...@@ -787,7 +784,7 @@ pub const Target = struct {
787 return struct {784 return struct {
788 /// Populates only the feature bits specified.785 /// Populates only the feature bits specified.
789 pub fn featureSet(features: []const F) Set {786 pub fn featureSet(features: []const F) Set {
790 var x = Set.empty_workaround(); // TODO remove empty_workaround787 var x = Set.empty;
791 for (features) |feature| {788 for (features) |feature| {
792 x.addFeature(@enumToInt(feature));789 x.addFeature(@enumToInt(feature));
793 }790 }
...@@ -1907,6 +1904,561 @@ pub const Target = struct {...@@ -1907,6 +1904,561 @@ pub const Target = struct {
1907 => 16,1904 => 16,
1908 };1905 };
1909 }1906 }
1907
1908 pub const CType = enum {
1909 short,
1910 ushort,
1911 int,
1912 uint,
1913 long,
1914 ulong,
1915 longlong,
1916 ulonglong,
1917 float,
1918 double,
1919 longdouble,
1920 };
1921
1922 pub fn c_type_byte_size(t: Target, c_type: CType) u16 {
1923 return switch (c_type) {
1924 .short,
1925 .ushort,
1926 .int,
1927 .uint,
1928 .long,
1929 .ulong,
1930 .longlong,
1931 .ulonglong,
1932 => @divExact(c_type_bit_size(t, c_type), 8),
1933
1934 .float => 4,
1935 .double => 8,
1936
1937 .longdouble => switch (c_type_bit_size(t, c_type)) {
1938 16 => 2,
1939 32 => 4,
1940 64 => 8,
1941 80 => @intCast(u16, mem.alignForward(10, c_type_alignment(t, .longdouble))),
1942 128 => 16,
1943 else => unreachable,
1944 },
1945 };
1946 }
1947
1948 pub fn c_type_bit_size(target: Target, c_type: CType) u16 {
1949 switch (target.os.tag) {
1950 .freestanding, .other => switch (target.cpu.arch) {
1951 .msp430 => switch (c_type) {
1952 .short, .ushort, .int, .uint => return 16,
1953 .float, .long, .ulong => return 32,
1954 .longlong, .ulonglong, .double, .longdouble => return 64,
1955 },
1956 .avr => switch (c_type) {
1957 .short, .ushort, .int, .uint => return 16,
1958 .long, .ulong, .float, .double, .longdouble => return 32,
1959 .longlong, .ulonglong => return 64,
1960 },
1961 .tce, .tcele => switch (c_type) {
1962 .short, .ushort => return 16,
1963 .int, .uint, .long, .ulong, .longlong, .ulonglong => return 32,
1964 .float, .double, .longdouble => return 32,
1965 },
1966 .mips64, .mips64el => switch (c_type) {
1967 .short, .ushort => return 16,
1968 .int, .uint, .float => return 32,
1969 .long, .ulong => return if (target.abi != .gnuabin32) 64 else 32,
1970 .longlong, .ulonglong, .double => return 64,
1971 .longdouble => return 128,
1972 },
1973 .x86_64 => switch (c_type) {
1974 .short, .ushort => return 16,
1975 .int, .uint, .float => return 32,
1976 .long, .ulong => switch (target.abi) {
1977 .gnux32, .muslx32 => return 32,
1978 else => return 64,
1979 },
1980 .longlong, .ulonglong, .double => return 64,
1981 .longdouble => return 80,
1982 },
1983 else => switch (c_type) {
1984 .short, .ushort => return 16,
1985 .int, .uint, .float => return 32,
1986 .long, .ulong => return target.cpu.arch.ptrBitWidth(),
1987 .longlong, .ulonglong, .double => return 64,
1988 .longdouble => switch (target.cpu.arch) {
1989 .x86 => switch (target.abi) {
1990 .android => return 64,
1991 else => return 80,
1992 },
1993
1994 .powerpc,
1995 .powerpcle,
1996 .powerpc64,
1997 .powerpc64le,
1998 => switch (target.abi) {
1999 .musl,
2000 .musleabi,
2001 .musleabihf,
2002 .muslx32,
2003 => return 64,
2004 else => return 128,
2005 },
2006
2007 .riscv32,
2008 .riscv64,
2009 .aarch64,
2010 .aarch64_be,
2011 .aarch64_32,
2012 .s390x,
2013 .sparc,
2014 .sparc64,
2015 .sparcel,
2016 .wasm32,
2017 .wasm64,
2018 => return 128,
2019
2020 else => return 64,
2021 },
2022 },
2023 },
2024
2025 .linux,
2026 .freebsd,
2027 .netbsd,
2028 .dragonfly,
2029 .openbsd,
2030 .wasi,
2031 .emscripten,
2032 .plan9,
2033 .solaris,
2034 .haiku,
2035 .ananas,
2036 .fuchsia,
2037 .minix,
2038 => switch (target.cpu.arch) {
2039 .msp430 => switch (c_type) {
2040 .short, .ushort, .int, .uint => return 16,
2041 .long, .ulong, .float => return 32,
2042 .longlong, .ulonglong, .double, .longdouble => return 64,
2043 },
2044 .avr => switch (c_type) {
2045 .short, .ushort, .int, .uint => return 16,
2046 .long, .ulong, .float, .double, .longdouble => return 32,
2047 .longlong, .ulonglong => return 64,
2048 },
2049 .tce, .tcele => switch (c_type) {
2050 .short, .ushort => return 16,
2051 .int, .uint, .long, .ulong, .longlong, .ulonglong => return 32,
2052 .float, .double, .longdouble => return 32,
2053 },
2054 .mips64, .mips64el => switch (c_type) {
2055 .short, .ushort => return 16,
2056 .int, .uint, .float => return 32,
2057 .long, .ulong => return if (target.abi != .gnuabin32) 64 else 32,
2058 .longlong, .ulonglong, .double => return 64,
2059 .longdouble => if (target.os.tag == .freebsd) return 64 else return 128,
2060 },
2061 .x86_64 => switch (c_type) {
2062 .short, .ushort => return 16,
2063 .int, .uint, .float => return 32,
2064 .long, .ulong => switch (target.abi) {
2065 .gnux32, .muslx32 => return 32,
2066 else => return 64,
2067 },
2068 .longlong, .ulonglong, .double => return 64,
2069 .longdouble => return 80,
2070 },
2071 else => switch (c_type) {
2072 .short, .ushort => return 16,
2073 .int, .uint, .float => return 32,
2074 .long, .ulong => return target.cpu.arch.ptrBitWidth(),
2075 .longlong, .ulonglong, .double => return 64,
2076 .longdouble => switch (target.cpu.arch) {
2077 .x86 => switch (target.abi) {
2078 .android => return 64,
2079 else => return 80,
2080 },
2081
2082 .powerpc,
2083 .powerpcle,
2084 => switch (target.abi) {
2085 .musl,
2086 .musleabi,
2087 .musleabihf,
2088 .muslx32,
2089 => return 64,
2090 else => switch (target.os.tag) {
2091 .freebsd, .netbsd, .openbsd => return 64,
2092 else => return 128,
2093 },
2094 },
2095
2096 .powerpc64,
2097 .powerpc64le,
2098 => switch (target.abi) {
2099 .musl,
2100 .musleabi,
2101 .musleabihf,
2102 .muslx32,
2103 => return 64,
2104 else => switch (target.os.tag) {
2105 .freebsd, .openbsd => return 64,
2106 else => return 128,
2107 },
2108 },
2109
2110 .riscv32,
2111 .riscv64,
2112 .aarch64,
2113 .aarch64_be,
2114 .aarch64_32,
2115 .s390x,
2116 .mips64,
2117 .mips64el,
2118 .sparc,
2119 .sparc64,
2120 .sparcel,
2121 .wasm32,
2122 .wasm64,
2123 => return 128,
2124
2125 else => return 64,
2126 },
2127 },
2128 },
2129
2130 .windows, .uefi => switch (target.cpu.arch) {
2131 .x86 => switch (c_type) {
2132 .short, .ushort => return 16,
2133 .int, .uint, .float => return 32,
2134 .long, .ulong => return 32,
2135 .longlong, .ulonglong, .double => return 64,
2136 .longdouble => switch (target.abi) {
2137 .gnu, .gnuilp32, .cygnus => return 80,
2138 else => return 64,
2139 },
2140 },
2141 .x86_64 => switch (c_type) {
2142 .short, .ushort => return 16,
2143 .int, .uint, .float => return 32,
2144 .long, .ulong => switch (target.abi) {
2145 .cygnus => return 64,
2146 else => return 32,
2147 },
2148 .longlong, .ulonglong, .double => return 64,
2149 .longdouble => switch (target.abi) {
2150 .gnu, .gnuilp32, .cygnus => return 80,
2151 else => return 64,
2152 },
2153 },
2154 else => switch (c_type) {
2155 .short, .ushort => return 16,
2156 .int, .uint, .float => return 32,
2157 .long, .ulong => return 32,
2158 .longlong, .ulonglong, .double => return 64,
2159 .longdouble => return 64,
2160 },
2161 },
2162
2163 .macos, .ios, .tvos, .watchos => switch (c_type) {
2164 .short, .ushort => return 16,
2165 .int, .uint, .float => return 32,
2166 .long, .ulong => switch (target.cpu.arch) {
2167 .x86, .arm, .aarch64_32 => return 32,
2168 .x86_64 => switch (target.abi) {
2169 .gnux32, .muslx32 => return 32,
2170 else => return 64,
2171 },
2172 else => return 64,
2173 },
2174 .longlong, .ulonglong, .double => return 64,
2175 .longdouble => switch (target.cpu.arch) {
2176 .x86 => switch (target.abi) {
2177 .android => return 64,
2178 else => return 80,
2179 },
2180 .x86_64 => return 80,
2181 else => return 64,
2182 },
2183 },
2184
2185 .nvcl, .cuda => switch (c_type) {
2186 .short, .ushort => return 16,
2187 .int, .uint, .float => return 32,
2188 .long, .ulong => switch (target.cpu.arch) {
2189 .nvptx => return 32,
2190 .nvptx64 => return 64,
2191 else => return 64,
2192 },
2193 .longlong, .ulonglong, .double => return 64,
2194 .longdouble => return 64,
2195 },
2196
2197 .amdhsa, .amdpal => switch (c_type) {
2198 .short, .ushort => return 16,
2199 .int, .uint, .float => return 32,
2200 .long, .ulong, .longlong, .ulonglong, .double => return 64,
2201 .longdouble => return 128,
2202 },
2203
2204 .cloudabi,
2205 .kfreebsd,
2206 .lv2,
2207 .zos,
2208 .rtems,
2209 .nacl,
2210 .aix,
2211 .ps4,
2212 .ps5,
2213 .elfiamcu,
2214 .mesa3d,
2215 .contiki,
2216 .hermit,
2217 .hurd,
2218 .opencl,
2219 .glsl450,
2220 .vulkan,
2221 .driverkit,
2222 .shadermodel,
2223 => @panic("TODO specify the C integer and float type sizes for this OS"),
2224 }
2225 }
2226
2227 pub fn c_type_alignment(target: Target, c_type: CType) u16 {
2228 // Overrides for unusual alignments
2229 switch (target.cpu.arch) {
2230 .avr => switch (c_type) {
2231 .short, .ushort => return 2,
2232 else => return 1,
2233 },
2234 .x86 => switch (target.os.tag) {
2235 .windows, .uefi => switch (c_type) {
2236 .longlong, .ulonglong, .double => return 8,
2237 .longdouble => switch (target.abi) {
2238 .gnu, .gnuilp32, .cygnus => return 4,
2239 else => return 8,
2240 },
2241 else => {},
2242 },
2243 else => {},
2244 },
2245 else => {},
2246 }
2247
2248 // Next-power-of-two-aligned, up to a maximum.
2249 return @min(
2250 std.math.ceilPowerOfTwoAssert(u16, (c_type_bit_size(target, c_type) + 7) / 8),
2251 switch (target.cpu.arch) {
2252 .arm, .armeb, .thumb, .thumbeb => switch (target.os.tag) {
2253 .netbsd => switch (target.abi) {
2254 .gnueabi,
2255 .gnueabihf,
2256 .eabi,
2257 .eabihf,
2258 .android,
2259 .musleabi,
2260 .musleabihf,
2261 => 8,
2262
2263 else => @as(u16, 4),
2264 },
2265 .ios, .tvos, .watchos => 4,
2266 else => 8,
2267 },
2268
2269 .msp430,
2270 .avr,
2271 => 2,
2272
2273 .arc,
2274 .csky,
2275 .x86,
2276 .xcore,
2277 .dxil,
2278 .loongarch32,
2279 .tce,
2280 .tcele,
2281 .le32,
2282 .amdil,
2283 .hsail,
2284 .spir,
2285 .spirv32,
2286 .kalimba,
2287 .shave,
2288 .renderscript32,
2289 .ve,
2290 .spu_2,
2291 .xtensa,
2292 => 4,
2293
2294 .aarch64_32,
2295 .amdgcn,
2296 .amdil64,
2297 .bpfel,
2298 .bpfeb,
2299 .hexagon,
2300 .hsail64,
2301 .loongarch64,
2302 .m68k,
2303 .mips,
2304 .mipsel,
2305 .sparc,
2306 .sparcel,
2307 .sparc64,
2308 .lanai,
2309 .le64,
2310 .nvptx,
2311 .nvptx64,
2312 .r600,
2313 .s390x,
2314 .spir64,
2315 .spirv64,
2316 .renderscript64,
2317 => 8,
2318
2319 .aarch64,
2320 .aarch64_be,
2321 .mips64,
2322 .mips64el,
2323 .powerpc,
2324 .powerpcle,
2325 .powerpc64,
2326 .powerpc64le,
2327 .riscv32,
2328 .riscv64,
2329 .x86_64,
2330 .wasm32,
2331 .wasm64,
2332 => 16,
2333 },
2334 );
2335 }
2336
2337 pub fn c_type_preferred_alignment(target: Target, c_type: CType) u16 {
2338 // Overrides for unusual alignments
2339 switch (target.cpu.arch) {
2340 .arm, .armeb, .thumb, .thumbeb => switch (target.os.tag) {
2341 .netbsd => switch (target.abi) {
2342 .gnueabi,
2343 .gnueabihf,
2344 .eabi,
2345 .eabihf,
2346 .android,
2347 .musleabi,
2348 .musleabihf,
2349 => {},
2350
2351 else => switch (c_type) {
2352 .longdouble => return 4,
2353 else => {},
2354 },
2355 },
2356 .ios, .tvos, .watchos => switch (c_type) {
2357 .longdouble => return 4,
2358 else => {},
2359 },
2360 else => {},
2361 },
2362 .arc => switch (c_type) {
2363 .longdouble => return 4,
2364 else => {},
2365 },
2366 .avr => switch (c_type) {
2367 .int, .uint, .long, .ulong, .float, .longdouble => return 1,
2368 .short, .ushort => return 2,
2369 .double => return 4,
2370 .longlong, .ulonglong => return 8,
2371 },
2372 .x86 => switch (target.os.tag) {
2373 .windows, .uefi => switch (c_type) {
2374 .longdouble => switch (target.abi) {
2375 .gnu, .gnuilp32, .cygnus => return 4,
2376 else => return 8,
2377 },
2378 else => {},
2379 },
2380 else => switch (c_type) {
2381 .longdouble => return 4,
2382 else => {},
2383 },
2384 },
2385 else => {},
2386 }
2387
2388 // Next-power-of-two-aligned, up to a maximum.
2389 return @min(
2390 std.math.ceilPowerOfTwoAssert(u16, (c_type_bit_size(target, c_type) + 7) / 8),
2391 switch (target.cpu.arch) {
2392 .msp430 => @as(u16, 2),
2393
2394 .csky,
2395 .xcore,
2396 .dxil,
2397 .loongarch32,
2398 .tce,
2399 .tcele,
2400 .le32,
2401 .amdil,
2402 .hsail,
2403 .spir,
2404 .spirv32,
2405 .kalimba,
2406 .shave,
2407 .renderscript32,
2408 .ve,
2409 .spu_2,
2410 .xtensa,
2411 => 4,
2412
2413 .arc,
2414 .arm,
2415 .armeb,
2416 .avr,
2417 .thumb,
2418 .thumbeb,
2419 .aarch64_32,
2420 .amdgcn,
2421 .amdil64,
2422 .bpfel,
2423 .bpfeb,
2424 .hexagon,
2425 .hsail64,
2426 .x86,
2427 .loongarch64,
2428 .m68k,
2429 .mips,
2430 .mipsel,
2431 .sparc,
2432 .sparcel,
2433 .sparc64,
2434 .lanai,
2435 .le64,
2436 .nvptx,
2437 .nvptx64,
2438 .r600,
2439 .s390x,
2440 .spir64,
2441 .spirv64,
2442 .renderscript64,
2443 => 8,
2444
2445 .aarch64,
2446 .aarch64_be,
2447 .mips64,
2448 .mips64el,
2449 .powerpc,
2450 .powerpcle,
2451 .powerpc64,
2452 .powerpc64le,
2453 .riscv32,
2454 .riscv64,
2455 .x86_64,
2456 .wasm32,
2457 .wasm64,
2458 => 16,
2459 },
2460 );
2461 }
1910};2462};
19112463
1912test {2464test {
lib/std/testing.zig+246
...@@ -670,6 +670,252 @@ pub fn expectStringEndsWith(actual: []const u8, expected_ends_with: []const u8)...@@ -670,6 +670,252 @@ pub fn expectStringEndsWith(actual: []const u8, expected_ends_with: []const u8)
670 return error.TestExpectedEndsWith;670 return error.TestExpectedEndsWith;
671}671}
672672
673/// This function is intended to be used only in tests. When the two values are not
674/// deeply equal, prints diagnostics to stderr to show exactly how they are not equal,
675/// then returns a test failure error.
676/// `actual` is casted to the type of `expected`.
677///
678/// Deeply equal is defined as follows:
679/// Primitive types are deeply equal if they are equal using `==` operator.
680/// Struct values are deeply equal if their corresponding fields are deeply equal.
681/// Container types(like Array/Slice/Vector) deeply equal when their corresponding elements are deeply equal.
682/// Pointer values are deeply equal if values they point to are deeply equal.
683///
684/// Note: Self-referential structs are not supported (e.g. things like std.SinglyLinkedList)
685pub fn expectEqualDeep(expected: anytype, actual: @TypeOf(expected)) !void {
686 switch (@typeInfo(@TypeOf(actual))) {
687 .NoReturn,
688 .Opaque,
689 .Frame,
690 .AnyFrame,
691 => @compileError("value of type " ++ @typeName(@TypeOf(actual)) ++ " encountered"),
692
693 .Undefined,
694 .Null,
695 .Void,
696 => return,
697
698 .Type => {
699 if (actual != expected) {
700 std.debug.print("expected type {s}, found type {s}\n", .{ @typeName(expected), @typeName(actual) });
701 return error.TestExpectedEqual;
702 }
703 },
704
705 .Bool,
706 .Int,
707 .Float,
708 .ComptimeFloat,
709 .ComptimeInt,
710 .EnumLiteral,
711 .Enum,
712 .Fn,
713 .ErrorSet,
714 => {
715 if (actual != expected) {
716 std.debug.print("expected {}, found {}\n", .{ expected, actual });
717 return error.TestExpectedEqual;
718 }
719 },
720
721 .Pointer => |pointer| {
722 switch (pointer.size) {
723 // We have no idea what is behind those pointers, so the best we can do is `==` check.
724 .C, .Many => {
725 if (actual != expected) {
726 std.debug.print("expected {*}, found {*}\n", .{ expected, actual });
727 return error.TestExpectedEqual;
728 }
729 },
730 .One => {
731 // Length of those pointers are runtime value, so the best we can do is `==` check.
732 switch (@typeInfo(pointer.child)) {
733 .Fn, .Opaque => {
734 if (actual != expected) {
735 std.debug.print("expected {*}, found {*}\n", .{ expected, actual });
736 return error.TestExpectedEqual;
737 }
738 },
739 else => try expectEqualDeep(expected.*, actual.*),
740 }
741 },
742 .Slice => {
743 if (expected.len != actual.len) {
744 std.debug.print("Slice len not the same, expected {d}, found {d}\n", .{ expected.len, actual.len });
745 return error.TestExpectedEqual;
746 }
747 var i: usize = 0;
748 while (i < expected.len) : (i += 1) {
749 expectEqualDeep(expected[i], actual[i]) catch |e| {
750 std.debug.print("index {d} incorrect. expected {any}, found {any}\n", .{
751 i, expected[i], actual[i],
752 });
753 return e;
754 };
755 }
756 },
757 }
758 },
759
760 .Array => |_| {
761 if (expected.len != actual.len) {
762 std.debug.print("Array len not the same, expected {d}, found {d}\n", .{ expected.len, actual.len });
763 return error.TestExpectedEqual;
764 }
765 var i: usize = 0;
766 while (i < expected.len) : (i += 1) {
767 expectEqualDeep(expected[i], actual[i]) catch |e| {
768 std.debug.print("index {d} incorrect. expected {any}, found {any}\n", .{
769 i, expected[i], actual[i],
770 });
771 return e;
772 };
773 }
774 },
775
776 .Vector => |info| {
777 if (info.len != @typeInfo(@TypeOf(actual)).Vector.len) {
778 std.debug.print("Vector len not the same, expected {d}, found {d}\n", .{ info.len, @typeInfo(@TypeOf(actual)).Vector.len });
779 return error.TestExpectedEqual;
780 }
781 var i: usize = 0;
782 while (i < info.len) : (i += 1) {
783 expectEqualDeep(expected[i], actual[i]) catch |e| {
784 std.debug.print("index {d} incorrect. expected {any}, found {any}\n", .{
785 i, expected[i], actual[i],
786 });
787 return e;
788 };
789 }
790 },
791
792 .Struct => |structType| {
793 inline for (structType.fields) |field| {
794 expectEqualDeep(@field(expected, field.name), @field(actual, field.name)) catch |e| {
795 std.debug.print("Field {s} incorrect. expected {any}, found {any}\n", .{ field.name, @field(expected, field.name), @field(actual, field.name) });
796 return e;
797 };
798 }
799 },
800
801 .Union => |union_info| {
802 if (union_info.tag_type == null) {
803 @compileError("Unable to compare untagged union values");
804 }
805
806 const Tag = std.meta.Tag(@TypeOf(expected));
807
808 const expectedTag = @as(Tag, expected);
809 const actualTag = @as(Tag, actual);
810
811 try expectEqual(expectedTag, actualTag);
812
813 // we only reach this loop if the tags are equal
814 switch (expected) {
815 inline else => |val, tag| {
816 try expectEqualDeep(val, @field(actual, @tagName(tag)));
817 },
818 }
819 },
820
821 .Optional => {
822 if (expected) |expected_payload| {
823 if (actual) |actual_payload| {
824 try expectEqualDeep(expected_payload, actual_payload);
825 } else {
826 std.debug.print("expected {any}, found null\n", .{expected_payload});
827 return error.TestExpectedEqual;
828 }
829 } else {
830 if (actual) |actual_payload| {
831 std.debug.print("expected null, found {any}\n", .{actual_payload});
832 return error.TestExpectedEqual;
833 }
834 }
835 },
836
837 .ErrorUnion => {
838 if (expected) |expected_payload| {
839 if (actual) |actual_payload| {
840 try expectEqualDeep(expected_payload, actual_payload);
841 } else |actual_err| {
842 std.debug.print("expected {any}, found {any}\n", .{ expected_payload, actual_err });
843 return error.TestExpectedEqual;
844 }
845 } else |expected_err| {
846 if (actual) |actual_payload| {
847 std.debug.print("expected {any}, found {any}\n", .{ expected_err, actual_payload });
848 return error.TestExpectedEqual;
849 } else |actual_err| {
850 try expectEqualDeep(expected_err, actual_err);
851 }
852 }
853 },
854 }
855}
856
857test "expectEqualDeep primitive type" {
858 try expectEqualDeep(1, 1);
859 try expectEqualDeep(true, true);
860 try expectEqualDeep(1.5, 1.5);
861 try expectEqualDeep(u8, u8);
862 try expectEqualDeep(error.Bad, error.Bad);
863
864 // optional
865 {
866 const foo: ?u32 = 1;
867 const bar: ?u32 = 1;
868 try expectEqualDeep(foo, bar);
869 try expectEqualDeep(?u32, ?u32);
870 }
871 // function type
872 {
873 const fnType = struct {
874 fn foo() void {
875 unreachable;
876 }
877 }.foo;
878 try expectEqualDeep(fnType, fnType);
879 }
880}
881
882test "expectEqualDeep pointer" {
883 const a = 1;
884 const b = 1;
885 try expectEqualDeep(&a, &b);
886}
887
888test "expectEqualDeep composite type" {
889 try expectEqualDeep("abc", "abc");
890 const s1: []const u8 = "abc";
891 const s2 = "abcd";
892 const s3: []const u8 = s2[0..3];
893 try expectEqualDeep(s1, s3);
894
895 const TestStruct = struct { s: []const u8 };
896 try expectEqualDeep(TestStruct{ .s = "abc" }, TestStruct{ .s = "abc" });
897 try expectEqualDeep([_][]const u8{ "a", "b", "c" }, [_][]const u8{ "a", "b", "c" });
898
899 // vector
900 try expectEqualDeep(@splat(4, @as(u32, 4)), @splat(4, @as(u32, 4)));
901
902 // nested array
903 {
904 const a = [2][2]f32{
905 [_]f32{ 1.0, 0.0 },
906 [_]f32{ 0.0, 1.0 },
907 };
908
909 const b = [2][2]f32{
910 [_]f32{ 1.0, 0.0 },
911 [_]f32{ 0.0, 1.0 },
912 };
913
914 try expectEqualDeep(a, b);
915 try expectEqualDeep(&a, &b);
916 }
917}
918
673fn printIndicatorLine(source: []const u8, indicator_index: usize) void {919fn printIndicatorLine(source: []const u8, indicator_index: usize) void {
674 const line_begin_index = if (std.mem.lastIndexOfScalar(u8, source[0..indicator_index], '\n')) |line_begin|920 const line_begin_index = if (std.mem.lastIndexOfScalar(u8, source[0..indicator_index], '\n')) |line_begin|
675 line_begin + 1921 line_begin + 1
lib/std/zig.zig-1
...@@ -8,7 +8,6 @@ pub const Tokenizer = tokenizer.Tokenizer;...@@ -8,7 +8,6 @@ pub const Tokenizer = tokenizer.Tokenizer;
8pub const fmtId = fmt.fmtId;8pub const fmtId = fmt.fmtId;
9pub const fmtEscapes = fmt.fmtEscapes;9pub const fmtEscapes = fmt.fmtEscapes;
10pub const isValidId = fmt.isValidId;10pub const isValidId = fmt.isValidId;
11pub const parse = @import("zig/parse.zig").parse;
12pub const string_literal = @import("zig/string_literal.zig");11pub const string_literal = @import("zig/string_literal.zig");
13pub const number_literal = @import("zig/number_literal.zig");12pub const number_literal = @import("zig/number_literal.zig");
14pub const primitives = @import("zig/primitives.zig");13pub const primitives = @import("zig/primitives.zig");
lib/std/zig/Ast.zig+73-9
...@@ -1,4 +1,8 @@...@@ -1,4 +1,8 @@
1//! Abstract Syntax Tree for Zig source code.1//! Abstract Syntax Tree for Zig source code.
2//! For Zig syntax, the root node is at nodes[0] and contains the list of
3//! sub-nodes.
4//! For Zon syntax, the root node is at nodes[0] and contains lhs as the node
5//! index of the main expression.
26
3/// Reference to externally-owned data.7/// Reference to externally-owned data.
4source: [:0]const u8,8source: [:0]const u8,
...@@ -11,13 +15,6 @@ extra_data: []Node.Index,...@@ -11,13 +15,6 @@ extra_data: []Node.Index,
1115
12errors: []const Error,16errors: []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
21pub const TokenIndex = u32;18pub const TokenIndex = u32;
22pub const ByteOffset = u32;19pub const ByteOffset = u32;
2320
...@@ -34,7 +31,7 @@ pub const Location = struct {...@@ -34,7 +31,7 @@ pub const Location = struct {
34 line_end: usize,31 line_end: usize,
35};32};
3633
37pub fn deinit(tree: *Ast, gpa: mem.Allocator) void {34pub fn deinit(tree: *Ast, gpa: Allocator) void {
38 tree.tokens.deinit(gpa);35 tree.tokens.deinit(gpa);
39 tree.nodes.deinit(gpa);36 tree.nodes.deinit(gpa);
40 gpa.free(tree.extra_data);37 gpa.free(tree.extra_data);
...@@ -48,11 +45,69 @@ pub const RenderError = error{...@@ -48,11 +45,69 @@ pub const RenderError = error{
48 OutOfMemory,45 OutOfMemory,
49};46};
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
51/// `gpa` is used for allocating the resulting formatted source code, as well as106/// `gpa` is used for allocating the resulting formatted source code, as well as
52/// for allocating extra stack memory if needed, because this function utilizes recursion.107/// for allocating extra stack memory if needed, because this function utilizes recursion.
53/// Note: that's not actually true yet, see https://github.com/ziglang/zig/issues/1006.108/// Note: that's not actually true yet, see https://github.com/ziglang/zig/issues/1006.
54/// Caller owns the returned slice of bytes, allocated with `gpa`.109/// Caller owns the returned slice of bytes, allocated with `gpa`.
55pub fn render(tree: Ast, gpa: mem.Allocator) RenderError![]u8 {110pub fn render(tree: Ast, gpa: Allocator) RenderError![]u8 {
56 var buffer = std.ArrayList(u8).init(gpa);111 var buffer = std.ArrayList(u8).init(gpa);
57 defer buffer.deinit();112 defer buffer.deinit();
58113
...@@ -3347,3 +3402,12 @@ pub const Node = struct {...@@ -3347,3 +3402,12 @@ pub const Node = struct {
3347 rparen: TokenIndex,3402 rparen: TokenIndex,
3348 };3403 };
3349};3404};
3405
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 {...@@ -75,7 +75,7 @@ fn castPtr(comptime DestType: type, target: anytype) DestType {
75 const source = ptrInfo(@TypeOf(target));75 const source = ptrInfo(@TypeOf(target));
7676
77 if (source.is_const and !dest.is_const or source.is_volatile and !dest.is_volatile)77 if (source.is_const and !dest.is_const or source.is_volatile and !dest.is_volatile)
78 return @intToPtr(DestType, @ptrToInt(target))78 return @qualCast(DestType, target)
79 else if (@typeInfo(dest.child) == .Opaque)79 else if (@typeInfo(dest.child) == .Opaque)
80 // dest.alignment would error out80 // dest.alignment would error out
81 return @ptrCast(DestType, target)81 return @ptrCast(DestType, target)
lib/std/zig/parse.zig deleted-3852
...@@ -1,3852 +0,0 @@
1const std = @import("../std.zig");
2const assert = std.debug.assert;
3const Allocator = std.mem.Allocator;
4const Ast = std.zig.Ast;
5const Node = Ast.Node;
6const AstError = Ast.Error;
7const TokenIndex = Ast.TokenIndex;
8const Token = std.zig.Token;
9
10pub const Error = error{ParseError} || Allocator.Error;
11
12/// Result should be freed with tree.deinit() when there are
13/// no more references to any of the tokens or nodes.
14pub fn parse(gpa: Allocator, source: [:0]const u8) Allocator.Error!Ast {
15 var tokens = Ast.TokenList{};
16 defer tokens.deinit(gpa);
17
18 // Empirically, the zig std lib has an 8:1 ratio of source bytes to token count.
19 const estimated_token_count = source.len / 8;
20 try tokens.ensureTotalCapacity(gpa, estimated_token_count);
21
22 var tokenizer = std.zig.Tokenizer.init(source);
23 while (true) {
24 const token = tokenizer.next();
25 try tokens.append(gpa, .{
26 .tag = token.tag,
27 .start = @intCast(u32, token.loc.start),
28 });
29 if (token.tag == .eof) break;
30 }
31
32 var parser: Parser = .{
33 .source = source,
34 .gpa = gpa,
35 .token_tags = tokens.items(.tag),
36 .token_starts = tokens.items(.start),
37 .errors = .{},
38 .nodes = .{},
39 .extra_data = .{},
40 .scratch = .{},
41 .tok_i = 0,
42 };
43 defer parser.errors.deinit(gpa);
44 defer parser.nodes.deinit(gpa);
45 defer parser.extra_data.deinit(gpa);
46 defer parser.scratch.deinit(gpa);
47
48 // Empirically, Zig source code has a 2:1 ratio of tokens to AST nodes.
49 // Make sure at least 1 so we can use appendAssumeCapacity on the root node below.
50 const estimated_node_count = (tokens.len + 2) / 2;
51 try parser.nodes.ensureTotalCapacity(gpa, estimated_node_count);
52
53 try parser.parseRoot();
54
55 // TODO experiment with compacting the MultiArrayList slices here
56 return Ast{
57 .source = source,
58 .tokens = tokens.toOwnedSlice(),
59 .nodes = parser.nodes.toOwnedSlice(),
60 .extra_data = try parser.extra_data.toOwnedSlice(gpa),
61 .errors = try parser.errors.toOwnedSlice(gpa),
62 };
63}
64
65const null_node: Node.Index = 0;
66
67/// Represents in-progress parsing, will be converted to an Ast after completion.
68const Parser = struct {
69 gpa: Allocator,
70 source: []const u8,
71 token_tags: []const Token.Tag,
72 token_starts: []const Ast.ByteOffset,
73 tok_i: TokenIndex,
74 errors: std.ArrayListUnmanaged(AstError),
75 nodes: Ast.NodeList,
76 extra_data: std.ArrayListUnmanaged(Node.Index),
77 scratch: std.ArrayListUnmanaged(Node.Index),
78
79 const SmallSpan = union(enum) {
80 zero_or_one: Node.Index,
81 multi: Node.SubRange,
82 };
83
84 const Members = struct {
85 len: usize,
86 lhs: Node.Index,
87 rhs: Node.Index,
88 trailing: bool,
89
90 fn toSpan(self: Members, p: *Parser) !Node.SubRange {
91 if (self.len <= 2) {
92 const nodes = [2]Node.Index{ self.lhs, self.rhs };
93 return p.listToSpan(nodes[0..self.len]);
94 } else {
95 return Node.SubRange{ .start = self.lhs, .end = self.rhs };
96 }
97 }
98 };
99
100 fn listToSpan(p: *Parser, list: []const Node.Index) !Node.SubRange {
101 try p.extra_data.appendSlice(p.gpa, list);
102 return Node.SubRange{
103 .start = @intCast(Node.Index, p.extra_data.items.len - list.len),
104 .end = @intCast(Node.Index, p.extra_data.items.len),
105 };
106 }
107
108 fn addNode(p: *Parser, elem: Ast.NodeList.Elem) Allocator.Error!Node.Index {
109 const result = @intCast(Node.Index, p.nodes.len);
110 try p.nodes.append(p.gpa, elem);
111 return result;
112 }
113
114 fn setNode(p: *Parser, i: usize, elem: Ast.NodeList.Elem) Node.Index {
115 p.nodes.set(i, elem);
116 return @intCast(Node.Index, i);
117 }
118
119 fn reserveNode(p: *Parser, tag: Ast.Node.Tag) !usize {
120 try p.nodes.resize(p.gpa, p.nodes.len + 1);
121 p.nodes.items(.tag)[p.nodes.len - 1] = tag;
122 return p.nodes.len - 1;
123 }
124
125 fn unreserveNode(p: *Parser, node_index: usize) void {
126 if (p.nodes.len == node_index) {
127 p.nodes.resize(p.gpa, p.nodes.len - 1) catch unreachable;
128 } else {
129 // There is zombie node left in the tree, let's make it as inoffensive as possible
130 // (sadly there's no no-op node)
131 p.nodes.items(.tag)[node_index] = .unreachable_literal;
132 p.nodes.items(.main_token)[node_index] = p.tok_i;
133 }
134 }
135
136 fn addExtra(p: *Parser, extra: anytype) Allocator.Error!Node.Index {
137 const fields = std.meta.fields(@TypeOf(extra));
138 try p.extra_data.ensureUnusedCapacity(p.gpa, fields.len);
139 const result = @intCast(u32, p.extra_data.items.len);
140 inline for (fields) |field| {
141 comptime assert(field.type == Node.Index);
142 p.extra_data.appendAssumeCapacity(@field(extra, field.name));
143 }
144 return result;
145 }
146
147 fn warnExpected(p: *Parser, expected_token: Token.Tag) error{OutOfMemory}!void {
148 @setCold(true);
149 try p.warnMsg(.{
150 .tag = .expected_token,
151 .token = p.tok_i,
152 .extra = .{ .expected_tag = expected_token },
153 });
154 }
155
156 fn warn(p: *Parser, error_tag: AstError.Tag) error{OutOfMemory}!void {
157 @setCold(true);
158 try p.warnMsg(.{ .tag = error_tag, .token = p.tok_i });
159 }
160
161 fn warnMsg(p: *Parser, msg: Ast.Error) error{OutOfMemory}!void {
162 @setCold(true);
163 switch (msg.tag) {
164 .expected_semi_after_decl,
165 .expected_semi_after_stmt,
166 .expected_comma_after_field,
167 .expected_comma_after_arg,
168 .expected_comma_after_param,
169 .expected_comma_after_initializer,
170 .expected_comma_after_switch_prong,
171 .expected_semi_or_else,
172 .expected_semi_or_lbrace,
173 .expected_token,
174 .expected_block,
175 .expected_block_or_assignment,
176 .expected_block_or_expr,
177 .expected_block_or_field,
178 .expected_expr,
179 .expected_expr_or_assignment,
180 .expected_fn,
181 .expected_inlinable,
182 .expected_labelable,
183 .expected_param_list,
184 .expected_prefix_expr,
185 .expected_primary_type_expr,
186 .expected_pub_item,
187 .expected_return_type,
188 .expected_suffix_op,
189 .expected_type_expr,
190 .expected_var_decl,
191 .expected_var_decl_or_fn,
192 .expected_loop_payload,
193 .expected_container,
194 => if (msg.token != 0 and !p.tokensOnSameLine(msg.token - 1, msg.token)) {
195 var copy = msg;
196 copy.token_is_prev = true;
197 copy.token -= 1;
198 return p.errors.append(p.gpa, copy);
199 },
200 else => {},
201 }
202 try p.errors.append(p.gpa, msg);
203 }
204
205 fn fail(p: *Parser, tag: Ast.Error.Tag) error{ ParseError, OutOfMemory } {
206 @setCold(true);
207 return p.failMsg(.{ .tag = tag, .token = p.tok_i });
208 }
209
210 fn failExpected(p: *Parser, expected_token: Token.Tag) error{ ParseError, OutOfMemory } {
211 @setCold(true);
212 return p.failMsg(.{
213 .tag = .expected_token,
214 .token = p.tok_i,
215 .extra = .{ .expected_tag = expected_token },
216 });
217 }
218
219 fn failMsg(p: *Parser, msg: Ast.Error) error{ ParseError, OutOfMemory } {
220 @setCold(true);
221 try p.warnMsg(msg);
222 return error.ParseError;
223 }
224
225 /// Root <- skip container_doc_comment? ContainerMembers eof
226 fn parseRoot(p: *Parser) !void {
227 // Root node must be index 0.
228 p.nodes.appendAssumeCapacity(.{
229 .tag = .root,
230 .main_token = 0,
231 .data = undefined,
232 });
233 const root_members = try p.parseContainerMembers();
234 const root_decls = try root_members.toSpan(p);
235 if (p.token_tags[p.tok_i] != .eof) {
236 try p.warnExpected(.eof);
237 }
238 p.nodes.items(.data)[0] = .{
239 .lhs = root_decls.start,
240 .rhs = root_decls.end,
241 };
242 }
243
244 /// ContainerMembers <- ContainerDeclarations (ContainerField COMMA)* (ContainerField / ContainerDeclarations)
245 ///
246 /// ContainerDeclarations
247 /// <- TestDecl ContainerDeclarations
248 /// / ComptimeDecl ContainerDeclarations
249 /// / doc_comment? KEYWORD_pub? Decl ContainerDeclarations
250 /// /
251 ///
252 /// ComptimeDecl <- KEYWORD_comptime Block
253 fn parseContainerMembers(p: *Parser) !Members {
254 const scratch_top = p.scratch.items.len;
255 defer p.scratch.shrinkRetainingCapacity(scratch_top);
256
257 var field_state: union(enum) {
258 /// No fields have been seen.
259 none,
260 /// Currently parsing fields.
261 seen,
262 /// Saw fields and then a declaration after them.
263 /// Payload is first token of previous declaration.
264 end: Node.Index,
265 /// There was a declaration between fields, don't report more errors.
266 err,
267 } = .none;
268
269 var last_field: TokenIndex = undefined;
270
271 // Skip container doc comments.
272 while (p.eatToken(.container_doc_comment)) |_| {}
273
274 var trailing = false;
275 while (true) {
276 const doc_comment = try p.eatDocComments();
277
278 switch (p.token_tags[p.tok_i]) {
279 .keyword_test => {
280 if (doc_comment) |some| {
281 try p.warnMsg(.{ .tag = .test_doc_comment, .token = some });
282 }
283 const test_decl_node = try p.expectTestDeclRecoverable();
284 if (test_decl_node != 0) {
285 if (field_state == .seen) {
286 field_state = .{ .end = test_decl_node };
287 }
288 try p.scratch.append(p.gpa, test_decl_node);
289 }
290 trailing = false;
291 },
292 .keyword_comptime => switch (p.token_tags[p.tok_i + 1]) {
293 .l_brace => {
294 if (doc_comment) |some| {
295 try p.warnMsg(.{ .tag = .comptime_doc_comment, .token = some });
296 }
297 const comptime_token = p.nextToken();
298 const block = p.parseBlock() catch |err| switch (err) {
299 error.OutOfMemory => return error.OutOfMemory,
300 error.ParseError => blk: {
301 p.findNextContainerMember();
302 break :blk null_node;
303 },
304 };
305 if (block != 0) {
306 const comptime_node = try p.addNode(.{
307 .tag = .@"comptime",
308 .main_token = comptime_token,
309 .data = .{
310 .lhs = block,
311 .rhs = undefined,
312 },
313 });
314 if (field_state == .seen) {
315 field_state = .{ .end = comptime_node };
316 }
317 try p.scratch.append(p.gpa, comptime_node);
318 }
319 trailing = false;
320 },
321 else => {
322 const identifier = p.tok_i;
323 defer last_field = identifier;
324 const container_field = p.expectContainerField() catch |err| switch (err) {
325 error.OutOfMemory => return error.OutOfMemory,
326 error.ParseError => {
327 p.findNextContainerMember();
328 continue;
329 },
330 };
331 switch (field_state) {
332 .none => field_state = .seen,
333 .err, .seen => {},
334 .end => |node| {
335 try p.warnMsg(.{
336 .tag = .decl_between_fields,
337 .token = p.nodes.items(.main_token)[node],
338 });
339 try p.warnMsg(.{
340 .tag = .previous_field,
341 .is_note = true,
342 .token = last_field,
343 });
344 try p.warnMsg(.{
345 .tag = .next_field,
346 .is_note = true,
347 .token = identifier,
348 });
349 // Continue parsing; error will be reported later.
350 field_state = .err;
351 },
352 }
353 try p.scratch.append(p.gpa, container_field);
354 switch (p.token_tags[p.tok_i]) {
355 .comma => {
356 p.tok_i += 1;
357 trailing = true;
358 continue;
359 },
360 .r_brace, .eof => {
361 trailing = false;
362 break;
363 },
364 else => {},
365 }
366 // There is not allowed to be a decl after a field with no comma.
367 // Report error but recover parser.
368 try p.warn(.expected_comma_after_field);
369 p.findNextContainerMember();
370 },
371 },
372 .keyword_pub => {
373 p.tok_i += 1;
374 const top_level_decl = try p.expectTopLevelDeclRecoverable();
375 if (top_level_decl != 0) {
376 if (field_state == .seen) {
377 field_state = .{ .end = top_level_decl };
378 }
379 try p.scratch.append(p.gpa, top_level_decl);
380 }
381 trailing = p.token_tags[p.tok_i - 1] == .semicolon;
382 },
383 .keyword_usingnamespace => {
384 const node = try p.expectUsingNamespaceRecoverable();
385 if (node != 0) {
386 if (field_state == .seen) {
387 field_state = .{ .end = node };
388 }
389 try p.scratch.append(p.gpa, node);
390 }
391 trailing = p.token_tags[p.tok_i - 1] == .semicolon;
392 },
393 .keyword_const,
394 .keyword_var,
395 .keyword_threadlocal,
396 .keyword_export,
397 .keyword_extern,
398 .keyword_inline,
399 .keyword_noinline,
400 .keyword_fn,
401 => {
402 const top_level_decl = try p.expectTopLevelDeclRecoverable();
403 if (top_level_decl != 0) {
404 if (field_state == .seen) {
405 field_state = .{ .end = top_level_decl };
406 }
407 try p.scratch.append(p.gpa, top_level_decl);
408 }
409 trailing = p.token_tags[p.tok_i - 1] == .semicolon;
410 },
411 .eof, .r_brace => {
412 if (doc_comment) |tok| {
413 try p.warnMsg(.{
414 .tag = .unattached_doc_comment,
415 .token = tok,
416 });
417 }
418 break;
419 },
420 else => {
421 const c_container = p.parseCStyleContainer() catch |err| switch (err) {
422 error.OutOfMemory => return error.OutOfMemory,
423 error.ParseError => false,
424 };
425 if (c_container) continue;
426
427 const identifier = p.tok_i;
428 defer last_field = identifier;
429 const container_field = p.expectContainerField() catch |err| switch (err) {
430 error.OutOfMemory => return error.OutOfMemory,
431 error.ParseError => {
432 p.findNextContainerMember();
433 continue;
434 },
435 };
436 switch (field_state) {
437 .none => field_state = .seen,
438 .err, .seen => {},
439 .end => |node| {
440 try p.warnMsg(.{
441 .tag = .decl_between_fields,
442 .token = p.nodes.items(.main_token)[node],
443 });
444 try p.warnMsg(.{
445 .tag = .previous_field,
446 .is_note = true,
447 .token = last_field,
448 });
449 try p.warnMsg(.{
450 .tag = .next_field,
451 .is_note = true,
452 .token = identifier,
453 });
454 // Continue parsing; error will be reported later.
455 field_state = .err;
456 },
457 }
458 try p.scratch.append(p.gpa, container_field);
459 switch (p.token_tags[p.tok_i]) {
460 .comma => {
461 p.tok_i += 1;
462 trailing = true;
463 continue;
464 },
465 .r_brace, .eof => {
466 trailing = false;
467 break;
468 },
469 else => {},
470 }
471 // There is not allowed to be a decl after a field with no comma.
472 // Report error but recover parser.
473 try p.warn(.expected_comma_after_field);
474 if (p.token_tags[p.tok_i] == .semicolon and p.token_tags[identifier] == .identifier) {
475 try p.warnMsg(.{
476 .tag = .var_const_decl,
477 .is_note = true,
478 .token = identifier,
479 });
480 }
481 p.findNextContainerMember();
482 continue;
483 },
484 }
485 }
486
487 const items = p.scratch.items[scratch_top..];
488 switch (items.len) {
489 0 => return Members{
490 .len = 0,
491 .lhs = 0,
492 .rhs = 0,
493 .trailing = trailing,
494 },
495 1 => return Members{
496 .len = 1,
497 .lhs = items[0],
498 .rhs = 0,
499 .trailing = trailing,
500 },
501 2 => return Members{
502 .len = 2,
503 .lhs = items[0],
504 .rhs = items[1],
505 .trailing = trailing,
506 },
507 else => {
508 const span = try p.listToSpan(items);
509 return Members{
510 .len = items.len,
511 .lhs = span.start,
512 .rhs = span.end,
513 .trailing = trailing,
514 };
515 },
516 }
517 }
518
519 /// Attempts to find next container member by searching for certain tokens
520 fn findNextContainerMember(p: *Parser) void {
521 var level: u32 = 0;
522 while (true) {
523 const tok = p.nextToken();
524 switch (p.token_tags[tok]) {
525 // Any of these can start a new top level declaration.
526 .keyword_test,
527 .keyword_comptime,
528 .keyword_pub,
529 .keyword_export,
530 .keyword_extern,
531 .keyword_inline,
532 .keyword_noinline,
533 .keyword_usingnamespace,
534 .keyword_threadlocal,
535 .keyword_const,
536 .keyword_var,
537 .keyword_fn,
538 => {
539 if (level == 0) {
540 p.tok_i -= 1;
541 return;
542 }
543 },
544 .identifier => {
545 if (p.token_tags[tok + 1] == .comma and level == 0) {
546 p.tok_i -= 1;
547 return;
548 }
549 },
550 .comma, .semicolon => {
551 // this decl was likely meant to end here
552 if (level == 0) {
553 return;
554 }
555 },
556 .l_paren, .l_bracket, .l_brace => level += 1,
557 .r_paren, .r_bracket => {
558 if (level != 0) level -= 1;
559 },
560 .r_brace => {
561 if (level == 0) {
562 // end of container, exit
563 p.tok_i -= 1;
564 return;
565 }
566 level -= 1;
567 },
568 .eof => {
569 p.tok_i -= 1;
570 return;
571 },
572 else => {},
573 }
574 }
575 }
576
577 /// Attempts to find the next statement by searching for a semicolon
578 fn findNextStmt(p: *Parser) void {
579 var level: u32 = 0;
580 while (true) {
581 const tok = p.nextToken();
582 switch (p.token_tags[tok]) {
583 .l_brace => level += 1,
584 .r_brace => {
585 if (level == 0) {
586 p.tok_i -= 1;
587 return;
588 }
589 level -= 1;
590 },
591 .semicolon => {
592 if (level == 0) {
593 return;
594 }
595 },
596 .eof => {
597 p.tok_i -= 1;
598 return;
599 },
600 else => {},
601 }
602 }
603 }
604
605 /// TestDecl <- KEYWORD_test (STRINGLITERALSINGLE / IDENTIFIER)? Block
606 fn expectTestDecl(p: *Parser) !Node.Index {
607 const test_token = p.assertToken(.keyword_test);
608 const name_token = switch (p.token_tags[p.nextToken()]) {
609 .string_literal, .identifier => p.tok_i - 1,
610 else => blk: {
611 p.tok_i -= 1;
612 break :blk null;
613 },
614 };
615 const block_node = try p.parseBlock();
616 if (block_node == 0) return p.fail(.expected_block);
617 return p.addNode(.{
618 .tag = .test_decl,
619 .main_token = test_token,
620 .data = .{
621 .lhs = name_token orelse 0,
622 .rhs = block_node,
623 },
624 });
625 }
626
627 fn expectTestDeclRecoverable(p: *Parser) error{OutOfMemory}!Node.Index {
628 return p.expectTestDecl() catch |err| switch (err) {
629 error.OutOfMemory => return error.OutOfMemory,
630 error.ParseError => {
631 p.findNextContainerMember();
632 return null_node;
633 },
634 };
635 }
636
637 /// Decl
638 /// <- (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE? / (KEYWORD_inline / KEYWORD_noinline))? FnProto (SEMICOLON / Block)
639 /// / (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE?)? KEYWORD_threadlocal? VarDecl
640 /// / KEYWORD_usingnamespace Expr SEMICOLON
641 fn expectTopLevelDecl(p: *Parser) !Node.Index {
642 const extern_export_inline_token = p.nextToken();
643 var is_extern: bool = false;
644 var expect_fn: bool = false;
645 var expect_var_or_fn: bool = false;
646 switch (p.token_tags[extern_export_inline_token]) {
647 .keyword_extern => {
648 _ = p.eatToken(.string_literal);
649 is_extern = true;
650 expect_var_or_fn = true;
651 },
652 .keyword_export => expect_var_or_fn = true,
653 .keyword_inline, .keyword_noinline => expect_fn = true,
654 else => p.tok_i -= 1,
655 }
656 const fn_proto = try p.parseFnProto();
657 if (fn_proto != 0) {
658 switch (p.token_tags[p.tok_i]) {
659 .semicolon => {
660 p.tok_i += 1;
661 return fn_proto;
662 },
663 .l_brace => {
664 if (is_extern) {
665 try p.warnMsg(.{ .tag = .extern_fn_body, .token = extern_export_inline_token });
666 return null_node;
667 }
668 const fn_decl_index = try p.reserveNode(.fn_decl);
669 errdefer p.unreserveNode(fn_decl_index);
670
671 const body_block = try p.parseBlock();
672 assert(body_block != 0);
673 return p.setNode(fn_decl_index, .{
674 .tag = .fn_decl,
675 .main_token = p.nodes.items(.main_token)[fn_proto],
676 .data = .{
677 .lhs = fn_proto,
678 .rhs = body_block,
679 },
680 });
681 },
682 else => {
683 // Since parseBlock only return error.ParseError on
684 // a missing '}' we can assume this function was
685 // supposed to end here.
686 try p.warn(.expected_semi_or_lbrace);
687 return null_node;
688 },
689 }
690 }
691 if (expect_fn) {
692 try p.warn(.expected_fn);
693 return error.ParseError;
694 }
695
696 const thread_local_token = p.eatToken(.keyword_threadlocal);
697 const var_decl = try p.parseVarDecl();
698 if (var_decl != 0) {
699 try p.expectSemicolon(.expected_semi_after_decl, false);
700 return var_decl;
701 }
702 if (thread_local_token != null) {
703 return p.fail(.expected_var_decl);
704 }
705 if (expect_var_or_fn) {
706 return p.fail(.expected_var_decl_or_fn);
707 }
708 if (p.token_tags[p.tok_i] != .keyword_usingnamespace) {
709 return p.fail(.expected_pub_item);
710 }
711 return p.expectUsingNamespace();
712 }
713
714 fn expectTopLevelDeclRecoverable(p: *Parser) error{OutOfMemory}!Node.Index {
715 return p.expectTopLevelDecl() catch |err| switch (err) {
716 error.OutOfMemory => return error.OutOfMemory,
717 error.ParseError => {
718 p.findNextContainerMember();
719 return null_node;
720 },
721 };
722 }
723
724 fn expectUsingNamespace(p: *Parser) !Node.Index {
725 const usingnamespace_token = p.assertToken(.keyword_usingnamespace);
726 const expr = try p.expectExpr();
727 try p.expectSemicolon(.expected_semi_after_decl, false);
728 return p.addNode(.{
729 .tag = .@"usingnamespace",
730 .main_token = usingnamespace_token,
731 .data = .{
732 .lhs = expr,
733 .rhs = undefined,
734 },
735 });
736 }
737
738 fn expectUsingNamespaceRecoverable(p: *Parser) error{OutOfMemory}!Node.Index {
739 return p.expectUsingNamespace() catch |err| switch (err) {
740 error.OutOfMemory => return error.OutOfMemory,
741 error.ParseError => {
742 p.findNextContainerMember();
743 return null_node;
744 },
745 };
746 }
747
748 /// FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? AddrSpace? LinkSection? CallConv? EXCLAMATIONMARK? TypeExpr
749 fn parseFnProto(p: *Parser) !Node.Index {
750 const fn_token = p.eatToken(.keyword_fn) orelse return null_node;
751
752 // We want the fn proto node to be before its children in the array.
753 const fn_proto_index = try p.reserveNode(.fn_proto);
754 errdefer p.unreserveNode(fn_proto_index);
755
756 _ = p.eatToken(.identifier);
757 const params = try p.parseParamDeclList();
758 const align_expr = try p.parseByteAlign();
759 const addrspace_expr = try p.parseAddrSpace();
760 const section_expr = try p.parseLinkSection();
761 const callconv_expr = try p.parseCallconv();
762 _ = p.eatToken(.bang);
763
764 const return_type_expr = try p.parseTypeExpr();
765 if (return_type_expr == 0) {
766 // most likely the user forgot to specify the return type.
767 // Mark return type as invalid and try to continue.
768 try p.warn(.expected_return_type);
769 }
770
771 if (align_expr == 0 and section_expr == 0 and callconv_expr == 0 and addrspace_expr == 0) {
772 switch (params) {
773 .zero_or_one => |param| return p.setNode(fn_proto_index, .{
774 .tag = .fn_proto_simple,
775 .main_token = fn_token,
776 .data = .{
777 .lhs = param,
778 .rhs = return_type_expr,
779 },
780 }),
781 .multi => |span| {
782 return p.setNode(fn_proto_index, .{
783 .tag = .fn_proto_multi,
784 .main_token = fn_token,
785 .data = .{
786 .lhs = try p.addExtra(Node.SubRange{
787 .start = span.start,
788 .end = span.end,
789 }),
790 .rhs = return_type_expr,
791 },
792 });
793 },
794 }
795 }
796 switch (params) {
797 .zero_or_one => |param| return p.setNode(fn_proto_index, .{
798 .tag = .fn_proto_one,
799 .main_token = fn_token,
800 .data = .{
801 .lhs = try p.addExtra(Node.FnProtoOne{
802 .param = param,
803 .align_expr = align_expr,
804 .addrspace_expr = addrspace_expr,
805 .section_expr = section_expr,
806 .callconv_expr = callconv_expr,
807 }),
808 .rhs = return_type_expr,
809 },
810 }),
811 .multi => |span| {
812 return p.setNode(fn_proto_index, .{
813 .tag = .fn_proto,
814 .main_token = fn_token,
815 .data = .{
816 .lhs = try p.addExtra(Node.FnProto{
817 .params_start = span.start,
818 .params_end = span.end,
819 .align_expr = align_expr,
820 .addrspace_expr = addrspace_expr,
821 .section_expr = section_expr,
822 .callconv_expr = callconv_expr,
823 }),
824 .rhs = return_type_expr,
825 },
826 });
827 },
828 }
829 }
830
831 /// VarDecl <- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? AddrSpace? LinkSection? (EQUAL Expr)? SEMICOLON
832 fn parseVarDecl(p: *Parser) !Node.Index {
833 const mut_token = p.eatToken(.keyword_const) orelse
834 p.eatToken(.keyword_var) orelse
835 return null_node;
836
837 _ = try p.expectToken(.identifier);
838 const type_node: Node.Index = if (p.eatToken(.colon) == null) 0 else try p.expectTypeExpr();
839 const align_node = try p.parseByteAlign();
840 const addrspace_node = try p.parseAddrSpace();
841 const section_node = try p.parseLinkSection();
842 const init_node: Node.Index = switch (p.token_tags[p.tok_i]) {
843 .equal_equal => blk: {
844 try p.warn(.wrong_equal_var_decl);
845 p.tok_i += 1;
846 break :blk try p.expectExpr();
847 },
848 .equal => blk: {
849 p.tok_i += 1;
850 break :blk try p.expectExpr();
851 },
852 else => 0,
853 };
854 if (section_node == 0 and addrspace_node == 0) {
855 if (align_node == 0) {
856 return p.addNode(.{
857 .tag = .simple_var_decl,
858 .main_token = mut_token,
859 .data = .{
860 .lhs = type_node,
861 .rhs = init_node,
862 },
863 });
864 } else if (type_node == 0) {
865 return p.addNode(.{
866 .tag = .aligned_var_decl,
867 .main_token = mut_token,
868 .data = .{
869 .lhs = align_node,
870 .rhs = init_node,
871 },
872 });
873 } else {
874 return p.addNode(.{
875 .tag = .local_var_decl,
876 .main_token = mut_token,
877 .data = .{
878 .lhs = try p.addExtra(Node.LocalVarDecl{
879 .type_node = type_node,
880 .align_node = align_node,
881 }),
882 .rhs = init_node,
883 },
884 });
885 }
886 } else {
887 return p.addNode(.{
888 .tag = .global_var_decl,
889 .main_token = mut_token,
890 .data = .{
891 .lhs = try p.addExtra(Node.GlobalVarDecl{
892 .type_node = type_node,
893 .align_node = align_node,
894 .addrspace_node = addrspace_node,
895 .section_node = section_node,
896 }),
897 .rhs = init_node,
898 },
899 });
900 }
901 }
902
903 /// ContainerField
904 /// <- doc_comment? KEYWORD_comptime? IDENTIFIER (COLON TypeExpr)? ByteAlign? (EQUAL Expr)?
905 /// / doc_comment? KEYWORD_comptime? (IDENTIFIER COLON)? !KEYWORD_fn TypeExpr ByteAlign? (EQUAL Expr)?
906 fn expectContainerField(p: *Parser) !Node.Index {
907 var main_token = p.tok_i;
908 _ = p.eatToken(.keyword_comptime);
909 const tuple_like = p.token_tags[p.tok_i] != .identifier or p.token_tags[p.tok_i + 1] != .colon;
910 if (!tuple_like) {
911 main_token = p.assertToken(.identifier);
912 }
913
914 var align_expr: Node.Index = 0;
915 var type_expr: Node.Index = 0;
916 if (p.eatToken(.colon) != null or tuple_like) {
917 type_expr = try p.expectTypeExpr();
918 align_expr = try p.parseByteAlign();
919 }
920
921 const value_expr: Node.Index = if (p.eatToken(.equal) == null) 0 else try p.expectExpr();
922
923 if (align_expr == 0) {
924 return p.addNode(.{
925 .tag = .container_field_init,
926 .main_token = main_token,
927 .data = .{
928 .lhs = type_expr,
929 .rhs = value_expr,
930 },
931 });
932 } else if (value_expr == 0) {
933 return p.addNode(.{
934 .tag = .container_field_align,
935 .main_token = main_token,
936 .data = .{
937 .lhs = type_expr,
938 .rhs = align_expr,
939 },
940 });
941 } else {
942 return p.addNode(.{
943 .tag = .container_field,
944 .main_token = main_token,
945 .data = .{
946 .lhs = type_expr,
947 .rhs = try p.addExtra(Node.ContainerField{
948 .value_expr = value_expr,
949 .align_expr = align_expr,
950 }),
951 },
952 });
953 }
954 }
955
956 /// Statement
957 /// <- KEYWORD_comptime? VarDecl
958 /// / KEYWORD_comptime BlockExprStatement
959 /// / KEYWORD_nosuspend BlockExprStatement
960 /// / KEYWORD_suspend BlockExprStatement
961 /// / KEYWORD_defer BlockExprStatement
962 /// / KEYWORD_errdefer Payload? BlockExprStatement
963 /// / IfStatement
964 /// / LabeledStatement
965 /// / SwitchExpr
966 /// / AssignExpr SEMICOLON
967 fn parseStatement(p: *Parser, allow_defer_var: bool) Error!Node.Index {
968 const comptime_token = p.eatToken(.keyword_comptime);
969
970 if (allow_defer_var) {
971 const var_decl = try p.parseVarDecl();
972 if (var_decl != 0) {
973 try p.expectSemicolon(.expected_semi_after_decl, true);
974 return var_decl;
975 }
976 }
977
978 if (comptime_token) |token| {
979 return p.addNode(.{
980 .tag = .@"comptime",
981 .main_token = token,
982 .data = .{
983 .lhs = try p.expectBlockExprStatement(),
984 .rhs = undefined,
985 },
986 });
987 }
988
989 switch (p.token_tags[p.tok_i]) {
990 .keyword_nosuspend => {
991 return p.addNode(.{
992 .tag = .@"nosuspend",
993 .main_token = p.nextToken(),
994 .data = .{
995 .lhs = try p.expectBlockExprStatement(),
996 .rhs = undefined,
997 },
998 });
999 },
1000 .keyword_suspend => {
1001 const token = p.nextToken();
1002 const block_expr = try p.expectBlockExprStatement();
1003 return p.addNode(.{
1004 .tag = .@"suspend",
1005 .main_token = token,
1006 .data = .{
1007 .lhs = block_expr,
1008 .rhs = undefined,
1009 },
1010 });
1011 },
1012 .keyword_defer => if (allow_defer_var) return p.addNode(.{
1013 .tag = .@"defer",
1014 .main_token = p.nextToken(),
1015 .data = .{
1016 .lhs = undefined,
1017 .rhs = try p.expectBlockExprStatement(),
1018 },
1019 }),
1020 .keyword_errdefer => if (allow_defer_var) return p.addNode(.{
1021 .tag = .@"errdefer",
1022 .main_token = p.nextToken(),
1023 .data = .{
1024 .lhs = try p.parsePayload(),
1025 .rhs = try p.expectBlockExprStatement(),
1026 },
1027 }),
1028 .keyword_switch => return p.expectSwitchExpr(),
1029 .keyword_if => return p.expectIfStatement(),
1030 .keyword_enum, .keyword_struct, .keyword_union => {
1031 const identifier = p.tok_i + 1;
1032 if (try p.parseCStyleContainer()) {
1033 // Return something so that `expectStatement` is happy.
1034 return p.addNode(.{
1035 .tag = .identifier,
1036 .main_token = identifier,
1037 .data = .{
1038 .lhs = undefined,
1039 .rhs = undefined,
1040 },
1041 });
1042 }
1043 },
1044 else => {},
1045 }
1046
1047 const labeled_statement = try p.parseLabeledStatement();
1048 if (labeled_statement != 0) return labeled_statement;
1049
1050 const assign_expr = try p.parseAssignExpr();
1051 if (assign_expr != 0) {
1052 try p.expectSemicolon(.expected_semi_after_stmt, true);
1053 return assign_expr;
1054 }
1055
1056 return null_node;
1057 }
1058
1059 fn expectStatement(p: *Parser, allow_defer_var: bool) !Node.Index {
1060 const statement = try p.parseStatement(allow_defer_var);
1061 if (statement == 0) {
1062 return p.fail(.expected_statement);
1063 }
1064 return statement;
1065 }
1066
1067 /// If a parse error occurs, reports an error, but then finds the next statement
1068 /// and returns that one instead. If a parse error occurs but there is no following
1069 /// statement, returns 0.
1070 fn expectStatementRecoverable(p: *Parser) Error!Node.Index {
1071 while (true) {
1072 return p.expectStatement(true) catch |err| switch (err) {
1073 error.OutOfMemory => return error.OutOfMemory,
1074 error.ParseError => {
1075 p.findNextStmt(); // Try to skip to the next statement.
1076 switch (p.token_tags[p.tok_i]) {
1077 .r_brace => return null_node,
1078 .eof => return error.ParseError,
1079 else => continue,
1080 }
1081 },
1082 };
1083 }
1084 }
1085
1086 /// IfStatement
1087 /// <- IfPrefix BlockExpr ( KEYWORD_else Payload? Statement )?
1088 /// / IfPrefix AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement )
1089 fn expectIfStatement(p: *Parser) !Node.Index {
1090 const if_token = p.assertToken(.keyword_if);
1091 _ = try p.expectToken(.l_paren);
1092 const condition = try p.expectExpr();
1093 _ = try p.expectToken(.r_paren);
1094 _ = try p.parsePtrPayload();
1095
1096 // TODO propose to change the syntax so that semicolons are always required
1097 // inside if statements, even if there is an `else`.
1098 var else_required = false;
1099 const then_expr = blk: {
1100 const block_expr = try p.parseBlockExpr();
1101 if (block_expr != 0) break :blk block_expr;
1102 const assign_expr = try p.parseAssignExpr();
1103 if (assign_expr == 0) {
1104 return p.fail(.expected_block_or_assignment);
1105 }
1106 if (p.eatToken(.semicolon)) |_| {
1107 return p.addNode(.{
1108 .tag = .if_simple,
1109 .main_token = if_token,
1110 .data = .{
1111 .lhs = condition,
1112 .rhs = assign_expr,
1113 },
1114 });
1115 }
1116 else_required = true;
1117 break :blk assign_expr;
1118 };
1119 _ = p.eatToken(.keyword_else) orelse {
1120 if (else_required) {
1121 try p.warn(.expected_semi_or_else);
1122 }
1123 return p.addNode(.{
1124 .tag = .if_simple,
1125 .main_token = if_token,
1126 .data = .{
1127 .lhs = condition,
1128 .rhs = then_expr,
1129 },
1130 });
1131 };
1132 _ = try p.parsePayload();
1133 const else_expr = try p.expectStatement(false);
1134 return p.addNode(.{
1135 .tag = .@"if",
1136 .main_token = if_token,
1137 .data = .{
1138 .lhs = condition,
1139 .rhs = try p.addExtra(Node.If{
1140 .then_expr = then_expr,
1141 .else_expr = else_expr,
1142 }),
1143 },
1144 });
1145 }
1146
1147 /// LabeledStatement <- BlockLabel? (Block / LoopStatement)
1148 fn parseLabeledStatement(p: *Parser) !Node.Index {
1149 const label_token = p.parseBlockLabel();
1150 const block = try p.parseBlock();
1151 if (block != 0) return block;
1152
1153 const loop_stmt = try p.parseLoopStatement();
1154 if (loop_stmt != 0) return loop_stmt;
1155
1156 if (label_token != 0) {
1157 const after_colon = p.tok_i;
1158 const node = try p.parseTypeExpr();
1159 if (node != 0) {
1160 const a = try p.parseByteAlign();
1161 const b = try p.parseAddrSpace();
1162 const c = try p.parseLinkSection();
1163 const d = if (p.eatToken(.equal) == null) 0 else try p.expectExpr();
1164 if (a != 0 or b != 0 or c != 0 or d != 0) {
1165 return p.failMsg(.{ .tag = .expected_var_const, .token = label_token });
1166 }
1167 }
1168 return p.failMsg(.{ .tag = .expected_labelable, .token = after_colon });
1169 }
1170
1171 return null_node;
1172 }
1173
1174 /// LoopStatement <- KEYWORD_inline? (ForStatement / WhileStatement)
1175 fn parseLoopStatement(p: *Parser) !Node.Index {
1176 const inline_token = p.eatToken(.keyword_inline);
1177
1178 const for_statement = try p.parseForStatement();
1179 if (for_statement != 0) return for_statement;
1180
1181 const while_statement = try p.parseWhileStatement();
1182 if (while_statement != 0) return while_statement;
1183
1184 if (inline_token == null) return null_node;
1185
1186 // If we've seen "inline", there should have been a "for" or "while"
1187 return p.fail(.expected_inlinable);
1188 }
1189
1190 /// ForPrefix <- KEYWORD_for LPAREN Expr RPAREN PtrIndexPayload
1191 ///
1192 /// ForStatement
1193 /// <- ForPrefix BlockExpr ( KEYWORD_else Statement )?
1194 /// / ForPrefix AssignExpr ( SEMICOLON / KEYWORD_else Statement )
1195 fn parseForStatement(p: *Parser) !Node.Index {
1196 const for_token = p.eatToken(.keyword_for) orelse return null_node;
1197 _ = try p.expectToken(.l_paren);
1198 const array_expr = try p.expectExpr();
1199 _ = try p.expectToken(.r_paren);
1200 const found_payload = try p.parsePtrIndexPayload();
1201 if (found_payload == 0) try p.warn(.expected_loop_payload);
1202
1203 // TODO propose to change the syntax so that semicolons are always required
1204 // inside while statements, even if there is an `else`.
1205 var else_required = false;
1206 const then_expr = blk: {
1207 const block_expr = try p.parseBlockExpr();
1208 if (block_expr != 0) break :blk block_expr;
1209 const assign_expr = try p.parseAssignExpr();
1210 if (assign_expr == 0) {
1211 return p.fail(.expected_block_or_assignment);
1212 }
1213 if (p.eatToken(.semicolon)) |_| {
1214 return p.addNode(.{
1215 .tag = .for_simple,
1216 .main_token = for_token,
1217 .data = .{
1218 .lhs = array_expr,
1219 .rhs = assign_expr,
1220 },
1221 });
1222 }
1223 else_required = true;
1224 break :blk assign_expr;
1225 };
1226 _ = p.eatToken(.keyword_else) orelse {
1227 if (else_required) {
1228 try p.warn(.expected_semi_or_else);
1229 }
1230 return p.addNode(.{
1231 .tag = .for_simple,
1232 .main_token = for_token,
1233 .data = .{
1234 .lhs = array_expr,
1235 .rhs = then_expr,
1236 },
1237 });
1238 };
1239 return p.addNode(.{
1240 .tag = .@"for",
1241 .main_token = for_token,
1242 .data = .{
1243 .lhs = array_expr,
1244 .rhs = try p.addExtra(Node.If{
1245 .then_expr = then_expr,
1246 .else_expr = try p.expectStatement(false),
1247 }),
1248 },
1249 });
1250 }
1251
1252 /// WhilePrefix <- KEYWORD_while LPAREN Expr RPAREN PtrPayload? WhileContinueExpr?
1253 ///
1254 /// WhileStatement
1255 /// <- WhilePrefix BlockExpr ( KEYWORD_else Payload? Statement )?
1256 /// / WhilePrefix AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement )
1257 fn parseWhileStatement(p: *Parser) !Node.Index {
1258 const while_token = p.eatToken(.keyword_while) orelse return null_node;
1259 _ = try p.expectToken(.l_paren);
1260 const condition = try p.expectExpr();
1261 _ = try p.expectToken(.r_paren);
1262 _ = try p.parsePtrPayload();
1263 const cont_expr = try p.parseWhileContinueExpr();
1264
1265 // TODO propose to change the syntax so that semicolons are always required
1266 // inside while statements, even if there is an `else`.
1267 var else_required = false;
1268 const then_expr = blk: {
1269 const block_expr = try p.parseBlockExpr();
1270 if (block_expr != 0) break :blk block_expr;
1271 const assign_expr = try p.parseAssignExpr();
1272 if (assign_expr == 0) {
1273 return p.fail(.expected_block_or_assignment);
1274 }
1275 if (p.eatToken(.semicolon)) |_| {
1276 if (cont_expr == 0) {
1277 return p.addNode(.{
1278 .tag = .while_simple,
1279 .main_token = while_token,
1280 .data = .{
1281 .lhs = condition,
1282 .rhs = assign_expr,
1283 },
1284 });
1285 } else {
1286 return p.addNode(.{
1287 .tag = .while_cont,
1288 .main_token = while_token,
1289 .data = .{
1290 .lhs = condition,
1291 .rhs = try p.addExtra(Node.WhileCont{
1292 .cont_expr = cont_expr,
1293 .then_expr = assign_expr,
1294 }),
1295 },
1296 });
1297 }
1298 }
1299 else_required = true;
1300 break :blk assign_expr;
1301 };
1302 _ = p.eatToken(.keyword_else) orelse {
1303 if (else_required) {
1304 try p.warn(.expected_semi_or_else);
1305 }
1306 if (cont_expr == 0) {
1307 return p.addNode(.{
1308 .tag = .while_simple,
1309 .main_token = while_token,
1310 .data = .{
1311 .lhs = condition,
1312 .rhs = then_expr,
1313 },
1314 });
1315 } else {
1316 return p.addNode(.{
1317 .tag = .while_cont,
1318 .main_token = while_token,
1319 .data = .{
1320 .lhs = condition,
1321 .rhs = try p.addExtra(Node.WhileCont{
1322 .cont_expr = cont_expr,
1323 .then_expr = then_expr,
1324 }),
1325 },
1326 });
1327 }
1328 };
1329 _ = try p.parsePayload();
1330 const else_expr = try p.expectStatement(false);
1331 return p.addNode(.{
1332 .tag = .@"while",
1333 .main_token = while_token,
1334 .data = .{
1335 .lhs = condition,
1336 .rhs = try p.addExtra(Node.While{
1337 .cont_expr = cont_expr,
1338 .then_expr = then_expr,
1339 .else_expr = else_expr,
1340 }),
1341 },
1342 });
1343 }
1344
1345 /// BlockExprStatement
1346 /// <- BlockExpr
1347 /// / AssignExpr SEMICOLON
1348 fn parseBlockExprStatement(p: *Parser) !Node.Index {
1349 const block_expr = try p.parseBlockExpr();
1350 if (block_expr != 0) {
1351 return block_expr;
1352 }
1353 const assign_expr = try p.parseAssignExpr();
1354 if (assign_expr != 0) {
1355 try p.expectSemicolon(.expected_semi_after_stmt, true);
1356 return assign_expr;
1357 }
1358 return null_node;
1359 }
1360
1361 fn expectBlockExprStatement(p: *Parser) !Node.Index {
1362 const node = try p.parseBlockExprStatement();
1363 if (node == 0) {
1364 return p.fail(.expected_block_or_expr);
1365 }
1366 return node;
1367 }
1368
1369 /// BlockExpr <- BlockLabel? Block
1370 fn parseBlockExpr(p: *Parser) Error!Node.Index {
1371 switch (p.token_tags[p.tok_i]) {
1372 .identifier => {
1373 if (p.token_tags[p.tok_i + 1] == .colon and
1374 p.token_tags[p.tok_i + 2] == .l_brace)
1375 {
1376 p.tok_i += 2;
1377 return p.parseBlock();
1378 } else {
1379 return null_node;
1380 }
1381 },
1382 .l_brace => return p.parseBlock(),
1383 else => return null_node,
1384 }
1385 }
1386
1387 /// AssignExpr <- Expr (AssignOp Expr)?
1388 ///
1389 /// AssignOp
1390 /// <- ASTERISKEQUAL
1391 /// / ASTERISKPIPEEQUAL
1392 /// / SLASHEQUAL
1393 /// / PERCENTEQUAL
1394 /// / PLUSEQUAL
1395 /// / PLUSPIPEEQUAL
1396 /// / MINUSEQUAL
1397 /// / MINUSPIPEEQUAL
1398 /// / LARROW2EQUAL
1399 /// / LARROW2PIPEEQUAL
1400 /// / RARROW2EQUAL
1401 /// / AMPERSANDEQUAL
1402 /// / CARETEQUAL
1403 /// / PIPEEQUAL
1404 /// / ASTERISKPERCENTEQUAL
1405 /// / PLUSPERCENTEQUAL
1406 /// / MINUSPERCENTEQUAL
1407 /// / EQUAL
1408 fn parseAssignExpr(p: *Parser) !Node.Index {
1409 const expr = try p.parseExpr();
1410 if (expr == 0) return null_node;
1411
1412 const tag: Node.Tag = switch (p.token_tags[p.tok_i]) {
1413 .asterisk_equal => .assign_mul,
1414 .slash_equal => .assign_div,
1415 .percent_equal => .assign_mod,
1416 .plus_equal => .assign_add,
1417 .minus_equal => .assign_sub,
1418 .angle_bracket_angle_bracket_left_equal => .assign_shl,
1419 .angle_bracket_angle_bracket_left_pipe_equal => .assign_shl_sat,
1420 .angle_bracket_angle_bracket_right_equal => .assign_shr,
1421 .ampersand_equal => .assign_bit_and,
1422 .caret_equal => .assign_bit_xor,
1423 .pipe_equal => .assign_bit_or,
1424 .asterisk_percent_equal => .assign_mul_wrap,
1425 .plus_percent_equal => .assign_add_wrap,
1426 .minus_percent_equal => .assign_sub_wrap,
1427 .asterisk_pipe_equal => .assign_mul_sat,
1428 .plus_pipe_equal => .assign_add_sat,
1429 .minus_pipe_equal => .assign_sub_sat,
1430 .equal => .assign,
1431 else => return expr,
1432 };
1433 return p.addNode(.{
1434 .tag = tag,
1435 .main_token = p.nextToken(),
1436 .data = .{
1437 .lhs = expr,
1438 .rhs = try p.expectExpr(),
1439 },
1440 });
1441 }
1442
1443 fn expectAssignExpr(p: *Parser) !Node.Index {
1444 const expr = try p.parseAssignExpr();
1445 if (expr == 0) {
1446 return p.fail(.expected_expr_or_assignment);
1447 }
1448 return expr;
1449 }
1450
1451 fn parseExpr(p: *Parser) Error!Node.Index {
1452 return p.parseExprPrecedence(0);
1453 }
1454
1455 fn expectExpr(p: *Parser) Error!Node.Index {
1456 const node = try p.parseExpr();
1457 if (node == 0) {
1458 return p.fail(.expected_expr);
1459 } else {
1460 return node;
1461 }
1462 }
1463
1464 const Assoc = enum {
1465 left,
1466 none,
1467 };
1468
1469 const OperInfo = struct {
1470 prec: i8,
1471 tag: Node.Tag,
1472 assoc: Assoc = Assoc.left,
1473 };
1474
1475 // A table of binary operator information. Higher precedence numbers are
1476 // stickier. All operators at the same precedence level should have the same
1477 // associativity.
1478 const operTable = std.enums.directEnumArrayDefault(Token.Tag, OperInfo, .{ .prec = -1, .tag = Node.Tag.root }, 0, .{
1479 .keyword_or = .{ .prec = 10, .tag = .bool_or },
1480
1481 .keyword_and = .{ .prec = 20, .tag = .bool_and },
1482
1483 .equal_equal = .{ .prec = 30, .tag = .equal_equal, .assoc = Assoc.none },
1484 .bang_equal = .{ .prec = 30, .tag = .bang_equal, .assoc = Assoc.none },
1485 .angle_bracket_left = .{ .prec = 30, .tag = .less_than, .assoc = Assoc.none },
1486 .angle_bracket_right = .{ .prec = 30, .tag = .greater_than, .assoc = Assoc.none },
1487 .angle_bracket_left_equal = .{ .prec = 30, .tag = .less_or_equal, .assoc = Assoc.none },
1488 .angle_bracket_right_equal = .{ .prec = 30, .tag = .greater_or_equal, .assoc = Assoc.none },
1489
1490 .ampersand = .{ .prec = 40, .tag = .bit_and },
1491 .caret = .{ .prec = 40, .tag = .bit_xor },
1492 .pipe = .{ .prec = 40, .tag = .bit_or },
1493 .keyword_orelse = .{ .prec = 40, .tag = .@"orelse" },
1494 .keyword_catch = .{ .prec = 40, .tag = .@"catch" },
1495
1496 .angle_bracket_angle_bracket_left = .{ .prec = 50, .tag = .shl },
1497 .angle_bracket_angle_bracket_left_pipe = .{ .prec = 50, .tag = .shl_sat },
1498 .angle_bracket_angle_bracket_right = .{ .prec = 50, .tag = .shr },
1499
1500 .plus = .{ .prec = 60, .tag = .add },
1501 .minus = .{ .prec = 60, .tag = .sub },
1502 .plus_plus = .{ .prec = 60, .tag = .array_cat },
1503 .plus_percent = .{ .prec = 60, .tag = .add_wrap },
1504 .minus_percent = .{ .prec = 60, .tag = .sub_wrap },
1505 .plus_pipe = .{ .prec = 60, .tag = .add_sat },
1506 .minus_pipe = .{ .prec = 60, .tag = .sub_sat },
1507
1508 .pipe_pipe = .{ .prec = 70, .tag = .merge_error_sets },
1509 .asterisk = .{ .prec = 70, .tag = .mul },
1510 .slash = .{ .prec = 70, .tag = .div },
1511 .percent = .{ .prec = 70, .tag = .mod },
1512 .asterisk_asterisk = .{ .prec = 70, .tag = .array_mult },
1513 .asterisk_percent = .{ .prec = 70, .tag = .mul_wrap },
1514 .asterisk_pipe = .{ .prec = 70, .tag = .mul_sat },
1515 });
1516
1517 fn parseExprPrecedence(p: *Parser, min_prec: i32) Error!Node.Index {
1518 assert(min_prec >= 0);
1519 var node = try p.parsePrefixExpr();
1520 if (node == 0) {
1521 return null_node;
1522 }
1523
1524 var banned_prec: i8 = -1;
1525
1526 while (true) {
1527 const tok_tag = p.token_tags[p.tok_i];
1528 const info = operTable[@intCast(usize, @enumToInt(tok_tag))];
1529 if (info.prec < min_prec) {
1530 break;
1531 }
1532 if (info.prec == banned_prec) {
1533 return p.fail(.chained_comparison_operators);
1534 }
1535
1536 const oper_token = p.nextToken();
1537 // Special-case handling for "catch"
1538 if (tok_tag == .keyword_catch) {
1539 _ = try p.parsePayload();
1540 }
1541 const rhs = try p.parseExprPrecedence(info.prec + 1);
1542 if (rhs == 0) {
1543 try p.warn(.expected_expr);
1544 return node;
1545 }
1546
1547 {
1548 const tok_len = tok_tag.lexeme().?.len;
1549 const char_before = p.source[p.token_starts[oper_token] - 1];
1550 const char_after = p.source[p.token_starts[oper_token] + tok_len];
1551 if (tok_tag == .ampersand and char_after == '&') {
1552 // without types we don't know if '&&' was intended as 'bitwise_and address_of', or a c-style logical_and
1553 // The best the parser can do is recommend changing it to 'and' or ' & &'
1554 try p.warnMsg(.{ .tag = .invalid_ampersand_ampersand, .token = oper_token });
1555 } else if (std.ascii.isWhitespace(char_before) != std.ascii.isWhitespace(char_after)) {
1556 try p.warnMsg(.{ .tag = .mismatched_binary_op_whitespace, .token = oper_token });
1557 }
1558 }
1559
1560 node = try p.addNode(.{
1561 .tag = info.tag,
1562 .main_token = oper_token,
1563 .data = .{
1564 .lhs = node,
1565 .rhs = rhs,
1566 },
1567 });
1568
1569 if (info.assoc == Assoc.none) {
1570 banned_prec = info.prec;
1571 }
1572 }
1573
1574 return node;
1575 }
1576
1577 /// PrefixExpr <- PrefixOp* PrimaryExpr
1578 ///
1579 /// PrefixOp
1580 /// <- EXCLAMATIONMARK
1581 /// / MINUS
1582 /// / TILDE
1583 /// / MINUSPERCENT
1584 /// / AMPERSAND
1585 /// / KEYWORD_try
1586 /// / KEYWORD_await
1587 fn parsePrefixExpr(p: *Parser) Error!Node.Index {
1588 const tag: Node.Tag = switch (p.token_tags[p.tok_i]) {
1589 .bang => .bool_not,
1590 .minus => .negation,
1591 .tilde => .bit_not,
1592 .minus_percent => .negation_wrap,
1593 .ampersand => .address_of,
1594 .keyword_try => .@"try",
1595 .keyword_await => .@"await",
1596 else => return p.parsePrimaryExpr(),
1597 };
1598 return p.addNode(.{
1599 .tag = tag,
1600 .main_token = p.nextToken(),
1601 .data = .{
1602 .lhs = try p.expectPrefixExpr(),
1603 .rhs = undefined,
1604 },
1605 });
1606 }
1607
1608 fn expectPrefixExpr(p: *Parser) Error!Node.Index {
1609 const node = try p.parsePrefixExpr();
1610 if (node == 0) {
1611 return p.fail(.expected_prefix_expr);
1612 }
1613 return node;
1614 }
1615
1616 /// TypeExpr <- PrefixTypeOp* ErrorUnionExpr
1617 ///
1618 /// PrefixTypeOp
1619 /// <- QUESTIONMARK
1620 /// / KEYWORD_anyframe MINUSRARROW
1621 /// / SliceTypeStart (ByteAlign / AddrSpace / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)*
1622 /// / PtrTypeStart (AddrSpace / KEYWORD_align LPAREN Expr (COLON Expr COLON Expr)? RPAREN / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)*
1623 /// / ArrayTypeStart
1624 ///
1625 /// SliceTypeStart <- LBRACKET (COLON Expr)? RBRACKET
1626 ///
1627 /// PtrTypeStart
1628 /// <- ASTERISK
1629 /// / ASTERISK2
1630 /// / LBRACKET ASTERISK (LETTERC / COLON Expr)? RBRACKET
1631 ///
1632 /// ArrayTypeStart <- LBRACKET Expr (COLON Expr)? RBRACKET
1633 fn parseTypeExpr(p: *Parser) Error!Node.Index {
1634 switch (p.token_tags[p.tok_i]) {
1635 .question_mark => return p.addNode(.{
1636 .tag = .optional_type,
1637 .main_token = p.nextToken(),
1638 .data = .{
1639 .lhs = try p.expectTypeExpr(),
1640 .rhs = undefined,
1641 },
1642 }),
1643 .keyword_anyframe => switch (p.token_tags[p.tok_i + 1]) {
1644 .arrow => return p.addNode(.{
1645 .tag = .anyframe_type,
1646 .main_token = p.nextToken(),
1647 .data = .{
1648 .lhs = p.nextToken(),
1649 .rhs = try p.expectTypeExpr(),
1650 },
1651 }),
1652 else => return p.parseErrorUnionExpr(),
1653 },
1654 .asterisk => {
1655 const asterisk = p.nextToken();
1656 const mods = try p.parsePtrModifiers();
1657 const elem_type = try p.expectTypeExpr();
1658 if (mods.bit_range_start != 0) {
1659 return p.addNode(.{
1660 .tag = .ptr_type_bit_range,
1661 .main_token = asterisk,
1662 .data = .{
1663 .lhs = try p.addExtra(Node.PtrTypeBitRange{
1664 .sentinel = 0,
1665 .align_node = mods.align_node,
1666 .addrspace_node = mods.addrspace_node,
1667 .bit_range_start = mods.bit_range_start,
1668 .bit_range_end = mods.bit_range_end,
1669 }),
1670 .rhs = elem_type,
1671 },
1672 });
1673 } else if (mods.addrspace_node != 0) {
1674 return p.addNode(.{
1675 .tag = .ptr_type,
1676 .main_token = asterisk,
1677 .data = .{
1678 .lhs = try p.addExtra(Node.PtrType{
1679 .sentinel = 0,
1680 .align_node = mods.align_node,
1681 .addrspace_node = mods.addrspace_node,
1682 }),
1683 .rhs = elem_type,
1684 },
1685 });
1686 } else {
1687 return p.addNode(.{
1688 .tag = .ptr_type_aligned,
1689 .main_token = asterisk,
1690 .data = .{
1691 .lhs = mods.align_node,
1692 .rhs = elem_type,
1693 },
1694 });
1695 }
1696 },
1697 .asterisk_asterisk => {
1698 const asterisk = p.nextToken();
1699 const mods = try p.parsePtrModifiers();
1700 const elem_type = try p.expectTypeExpr();
1701 const inner: Node.Index = inner: {
1702 if (mods.bit_range_start != 0) {
1703 break :inner try p.addNode(.{
1704 .tag = .ptr_type_bit_range,
1705 .main_token = asterisk,
1706 .data = .{
1707 .lhs = try p.addExtra(Node.PtrTypeBitRange{
1708 .sentinel = 0,
1709 .align_node = mods.align_node,
1710 .addrspace_node = mods.addrspace_node,
1711 .bit_range_start = mods.bit_range_start,
1712 .bit_range_end = mods.bit_range_end,
1713 }),
1714 .rhs = elem_type,
1715 },
1716 });
1717 } else if (mods.addrspace_node != 0) {
1718 break :inner try p.addNode(.{
1719 .tag = .ptr_type,
1720 .main_token = asterisk,
1721 .data = .{
1722 .lhs = try p.addExtra(Node.PtrType{
1723 .sentinel = 0,
1724 .align_node = mods.align_node,
1725 .addrspace_node = mods.addrspace_node,
1726 }),
1727 .rhs = elem_type,
1728 },
1729 });
1730 } else {
1731 break :inner try p.addNode(.{
1732 .tag = .ptr_type_aligned,
1733 .main_token = asterisk,
1734 .data = .{
1735 .lhs = mods.align_node,
1736 .rhs = elem_type,
1737 },
1738 });
1739 }
1740 };
1741 return p.addNode(.{
1742 .tag = .ptr_type_aligned,
1743 .main_token = asterisk,
1744 .data = .{
1745 .lhs = 0,
1746 .rhs = inner,
1747 },
1748 });
1749 },
1750 .l_bracket => switch (p.token_tags[p.tok_i + 1]) {
1751 .asterisk => {
1752 _ = p.nextToken();
1753 const asterisk = p.nextToken();
1754 var sentinel: Node.Index = 0;
1755 if (p.eatToken(.identifier)) |ident| {
1756 const ident_slice = p.source[p.token_starts[ident]..p.token_starts[ident + 1]];
1757 if (!std.mem.eql(u8, std.mem.trimRight(u8, ident_slice, &std.ascii.whitespace), "c")) {
1758 p.tok_i -= 1;
1759 }
1760 } else if (p.eatToken(.colon)) |_| {
1761 sentinel = try p.expectExpr();
1762 }
1763 _ = try p.expectToken(.r_bracket);
1764 const mods = try p.parsePtrModifiers();
1765 const elem_type = try p.expectTypeExpr();
1766 if (mods.bit_range_start == 0) {
1767 if (sentinel == 0 and mods.addrspace_node == 0) {
1768 return p.addNode(.{
1769 .tag = .ptr_type_aligned,
1770 .main_token = asterisk,
1771 .data = .{
1772 .lhs = mods.align_node,
1773 .rhs = elem_type,
1774 },
1775 });
1776 } else if (mods.align_node == 0 and mods.addrspace_node == 0) {
1777 return p.addNode(.{
1778 .tag = .ptr_type_sentinel,
1779 .main_token = asterisk,
1780 .data = .{
1781 .lhs = sentinel,
1782 .rhs = elem_type,
1783 },
1784 });
1785 } else {
1786 return p.addNode(.{
1787 .tag = .ptr_type,
1788 .main_token = asterisk,
1789 .data = .{
1790 .lhs = try p.addExtra(Node.PtrType{
1791 .sentinel = sentinel,
1792 .align_node = mods.align_node,
1793 .addrspace_node = mods.addrspace_node,
1794 }),
1795 .rhs = elem_type,
1796 },
1797 });
1798 }
1799 } else {
1800 return p.addNode(.{
1801 .tag = .ptr_type_bit_range,
1802 .main_token = asterisk,
1803 .data = .{
1804 .lhs = try p.addExtra(Node.PtrTypeBitRange{
1805 .sentinel = sentinel,
1806 .align_node = mods.align_node,
1807 .addrspace_node = mods.addrspace_node,
1808 .bit_range_start = mods.bit_range_start,
1809 .bit_range_end = mods.bit_range_end,
1810 }),
1811 .rhs = elem_type,
1812 },
1813 });
1814 }
1815 },
1816 else => {
1817 const lbracket = p.nextToken();
1818 const len_expr = try p.parseExpr();
1819 const sentinel: Node.Index = if (p.eatToken(.colon)) |_|
1820 try p.expectExpr()
1821 else
1822 0;
1823 _ = try p.expectToken(.r_bracket);
1824 if (len_expr == 0) {
1825 const mods = try p.parsePtrModifiers();
1826 const elem_type = try p.expectTypeExpr();
1827 if (mods.bit_range_start != 0) {
1828 try p.warnMsg(.{
1829 .tag = .invalid_bit_range,
1830 .token = p.nodes.items(.main_token)[mods.bit_range_start],
1831 });
1832 }
1833 if (sentinel == 0 and mods.addrspace_node == 0) {
1834 return p.addNode(.{
1835 .tag = .ptr_type_aligned,
1836 .main_token = lbracket,
1837 .data = .{
1838 .lhs = mods.align_node,
1839 .rhs = elem_type,
1840 },
1841 });
1842 } else if (mods.align_node == 0 and mods.addrspace_node == 0) {
1843 return p.addNode(.{
1844 .tag = .ptr_type_sentinel,
1845 .main_token = lbracket,
1846 .data = .{
1847 .lhs = sentinel,
1848 .rhs = elem_type,
1849 },
1850 });
1851 } else {
1852 return p.addNode(.{
1853 .tag = .ptr_type,
1854 .main_token = lbracket,
1855 .data = .{
1856 .lhs = try p.addExtra(Node.PtrType{
1857 .sentinel = sentinel,
1858 .align_node = mods.align_node,
1859 .addrspace_node = mods.addrspace_node,
1860 }),
1861 .rhs = elem_type,
1862 },
1863 });
1864 }
1865 } else {
1866 switch (p.token_tags[p.tok_i]) {
1867 .keyword_align,
1868 .keyword_const,
1869 .keyword_volatile,
1870 .keyword_allowzero,
1871 .keyword_addrspace,
1872 => return p.fail(.ptr_mod_on_array_child_type),
1873 else => {},
1874 }
1875 const elem_type = try p.expectTypeExpr();
1876 if (sentinel == 0) {
1877 return p.addNode(.{
1878 .tag = .array_type,
1879 .main_token = lbracket,
1880 .data = .{
1881 .lhs = len_expr,
1882 .rhs = elem_type,
1883 },
1884 });
1885 } else {
1886 return p.addNode(.{
1887 .tag = .array_type_sentinel,
1888 .main_token = lbracket,
1889 .data = .{
1890 .lhs = len_expr,
1891 .rhs = try p.addExtra(.{
1892 .elem_type = elem_type,
1893 .sentinel = sentinel,
1894 }),
1895 },
1896 });
1897 }
1898 }
1899 },
1900 },
1901 else => return p.parseErrorUnionExpr(),
1902 }
1903 }
1904
1905 fn expectTypeExpr(p: *Parser) Error!Node.Index {
1906 const node = try p.parseTypeExpr();
1907 if (node == 0) {
1908 return p.fail(.expected_type_expr);
1909 }
1910 return node;
1911 }
1912
1913 /// PrimaryExpr
1914 /// <- AsmExpr
1915 /// / IfExpr
1916 /// / KEYWORD_break BreakLabel? Expr?
1917 /// / KEYWORD_comptime Expr
1918 /// / KEYWORD_nosuspend Expr
1919 /// / KEYWORD_continue BreakLabel?
1920 /// / KEYWORD_resume Expr
1921 /// / KEYWORD_return Expr?
1922 /// / BlockLabel? LoopExpr
1923 /// / Block
1924 /// / CurlySuffixExpr
1925 fn parsePrimaryExpr(p: *Parser) !Node.Index {
1926 switch (p.token_tags[p.tok_i]) {
1927 .keyword_asm => return p.expectAsmExpr(),
1928 .keyword_if => return p.parseIfExpr(),
1929 .keyword_break => {
1930 p.tok_i += 1;
1931 return p.addNode(.{
1932 .tag = .@"break",
1933 .main_token = p.tok_i - 1,
1934 .data = .{
1935 .lhs = try p.parseBreakLabel(),
1936 .rhs = try p.parseExpr(),
1937 },
1938 });
1939 },
1940 .keyword_continue => {
1941 p.tok_i += 1;
1942 return p.addNode(.{
1943 .tag = .@"continue",
1944 .main_token = p.tok_i - 1,
1945 .data = .{
1946 .lhs = try p.parseBreakLabel(),
1947 .rhs = undefined,
1948 },
1949 });
1950 },
1951 .keyword_comptime => {
1952 p.tok_i += 1;
1953 return p.addNode(.{
1954 .tag = .@"comptime",
1955 .main_token = p.tok_i - 1,
1956 .data = .{
1957 .lhs = try p.expectExpr(),
1958 .rhs = undefined,
1959 },
1960 });
1961 },
1962 .keyword_nosuspend => {
1963 p.tok_i += 1;
1964 return p.addNode(.{
1965 .tag = .@"nosuspend",
1966 .main_token = p.tok_i - 1,
1967 .data = .{
1968 .lhs = try p.expectExpr(),
1969 .rhs = undefined,
1970 },
1971 });
1972 },
1973 .keyword_resume => {
1974 p.tok_i += 1;
1975 return p.addNode(.{
1976 .tag = .@"resume",
1977 .main_token = p.tok_i - 1,
1978 .data = .{
1979 .lhs = try p.expectExpr(),
1980 .rhs = undefined,
1981 },
1982 });
1983 },
1984 .keyword_return => {
1985 p.tok_i += 1;
1986 return p.addNode(.{
1987 .tag = .@"return",
1988 .main_token = p.tok_i - 1,
1989 .data = .{
1990 .lhs = try p.parseExpr(),
1991 .rhs = undefined,
1992 },
1993 });
1994 },
1995 .identifier => {
1996 if (p.token_tags[p.tok_i + 1] == .colon) {
1997 switch (p.token_tags[p.tok_i + 2]) {
1998 .keyword_inline => {
1999 p.tok_i += 3;
2000 switch (p.token_tags[p.tok_i]) {
2001 .keyword_for => return p.parseForExpr(),
2002 .keyword_while => return p.parseWhileExpr(),
2003 else => return p.fail(.expected_inlinable),
2004 }
2005 },
2006 .keyword_for => {
2007 p.tok_i += 2;
2008 return p.parseForExpr();
2009 },
2010 .keyword_while => {
2011 p.tok_i += 2;
2012 return p.parseWhileExpr();
2013 },
2014 .l_brace => {
2015 p.tok_i += 2;
2016 return p.parseBlock();
2017 },
2018 else => return p.parseCurlySuffixExpr(),
2019 }
2020 } else {
2021 return p.parseCurlySuffixExpr();
2022 }
2023 },
2024 .keyword_inline => {
2025 p.tok_i += 1;
2026 switch (p.token_tags[p.tok_i]) {
2027 .keyword_for => return p.parseForExpr(),
2028 .keyword_while => return p.parseWhileExpr(),
2029 else => return p.fail(.expected_inlinable),
2030 }
2031 },
2032 .keyword_for => return p.parseForExpr(),
2033 .keyword_while => return p.parseWhileExpr(),
2034 .l_brace => return p.parseBlock(),
2035 else => return p.parseCurlySuffixExpr(),
2036 }
2037 }
2038
2039 /// IfExpr <- IfPrefix Expr (KEYWORD_else Payload? Expr)?
2040 fn parseIfExpr(p: *Parser) !Node.Index {
2041 return p.parseIf(expectExpr);
2042 }
2043
2044 /// Block <- LBRACE Statement* RBRACE
2045 fn parseBlock(p: *Parser) !Node.Index {
2046 const lbrace = p.eatToken(.l_brace) orelse return null_node;
2047 const scratch_top = p.scratch.items.len;
2048 defer p.scratch.shrinkRetainingCapacity(scratch_top);
2049 while (true) {
2050 if (p.token_tags[p.tok_i] == .r_brace) break;
2051 const statement = try p.expectStatementRecoverable();
2052 if (statement == 0) break;
2053 try p.scratch.append(p.gpa, statement);
2054 }
2055 _ = try p.expectToken(.r_brace);
2056 const semicolon = (p.token_tags[p.tok_i - 2] == .semicolon);
2057 const statements = p.scratch.items[scratch_top..];
2058 switch (statements.len) {
2059 0 => return p.addNode(.{
2060 .tag = .block_two,
2061 .main_token = lbrace,
2062 .data = .{
2063 .lhs = 0,
2064 .rhs = 0,
2065 },
2066 }),
2067 1 => return p.addNode(.{
2068 .tag = if (semicolon) .block_two_semicolon else .block_two,
2069 .main_token = lbrace,
2070 .data = .{
2071 .lhs = statements[0],
2072 .rhs = 0,
2073 },
2074 }),
2075 2 => return p.addNode(.{
2076 .tag = if (semicolon) .block_two_semicolon else .block_two,
2077 .main_token = lbrace,
2078 .data = .{
2079 .lhs = statements[0],
2080 .rhs = statements[1],
2081 },
2082 }),
2083 else => {
2084 const span = try p.listToSpan(statements);
2085 return p.addNode(.{
2086 .tag = if (semicolon) .block_semicolon else .block,
2087 .main_token = lbrace,
2088 .data = .{
2089 .lhs = span.start,
2090 .rhs = span.end,
2091 },
2092 });
2093 },
2094 }
2095 }
2096
2097 /// ForPrefix <- KEYWORD_for LPAREN Expr RPAREN PtrIndexPayload
2098 ///
2099 /// ForExpr <- ForPrefix Expr (KEYWORD_else Expr)?
2100 fn parseForExpr(p: *Parser) !Node.Index {
2101 const for_token = p.eatToken(.keyword_for) orelse return null_node;
2102 _ = try p.expectToken(.l_paren);
2103 const array_expr = try p.expectExpr();
2104 _ = try p.expectToken(.r_paren);
2105 const found_payload = try p.parsePtrIndexPayload();
2106 if (found_payload == 0) try p.warn(.expected_loop_payload);
2107
2108 const then_expr = try p.expectExpr();
2109 _ = p.eatToken(.keyword_else) orelse {
2110 return p.addNode(.{
2111 .tag = .for_simple,
2112 .main_token = for_token,
2113 .data = .{
2114 .lhs = array_expr,
2115 .rhs = then_expr,
2116 },
2117 });
2118 };
2119 const else_expr = try p.expectExpr();
2120 return p.addNode(.{
2121 .tag = .@"for",
2122 .main_token = for_token,
2123 .data = .{
2124 .lhs = array_expr,
2125 .rhs = try p.addExtra(Node.If{
2126 .then_expr = then_expr,
2127 .else_expr = else_expr,
2128 }),
2129 },
2130 });
2131 }
2132
2133 /// WhilePrefix <- KEYWORD_while LPAREN Expr RPAREN PtrPayload? WhileContinueExpr?
2134 ///
2135 /// WhileExpr <- WhilePrefix Expr (KEYWORD_else Payload? Expr)?
2136 fn parseWhileExpr(p: *Parser) !Node.Index {
2137 const while_token = p.eatToken(.keyword_while) orelse return null_node;
2138 _ = try p.expectToken(.l_paren);
2139 const condition = try p.expectExpr();
2140 _ = try p.expectToken(.r_paren);
2141 _ = try p.parsePtrPayload();
2142 const cont_expr = try p.parseWhileContinueExpr();
2143
2144 const then_expr = try p.expectExpr();
2145 _ = p.eatToken(.keyword_else) orelse {
2146 if (cont_expr == 0) {
2147 return p.addNode(.{
2148 .tag = .while_simple,
2149 .main_token = while_token,
2150 .data = .{
2151 .lhs = condition,
2152 .rhs = then_expr,
2153 },
2154 });
2155 } else {
2156 return p.addNode(.{
2157 .tag = .while_cont,
2158 .main_token = while_token,
2159 .data = .{
2160 .lhs = condition,
2161 .rhs = try p.addExtra(Node.WhileCont{
2162 .cont_expr = cont_expr,
2163 .then_expr = then_expr,
2164 }),
2165 },
2166 });
2167 }
2168 };
2169 _ = try p.parsePayload();
2170 const else_expr = try p.expectExpr();
2171 return p.addNode(.{
2172 .tag = .@"while",
2173 .main_token = while_token,
2174 .data = .{
2175 .lhs = condition,
2176 .rhs = try p.addExtra(Node.While{
2177 .cont_expr = cont_expr,
2178 .then_expr = then_expr,
2179 .else_expr = else_expr,
2180 }),
2181 },
2182 });
2183 }
2184
2185 /// CurlySuffixExpr <- TypeExpr InitList?
2186 ///
2187 /// InitList
2188 /// <- LBRACE FieldInit (COMMA FieldInit)* COMMA? RBRACE
2189 /// / LBRACE Expr (COMMA Expr)* COMMA? RBRACE
2190 /// / LBRACE RBRACE
2191 fn parseCurlySuffixExpr(p: *Parser) !Node.Index {
2192 const lhs = try p.parseTypeExpr();
2193 if (lhs == 0) return null_node;
2194 const lbrace = p.eatToken(.l_brace) orelse return lhs;
2195
2196 // If there are 0 or 1 items, we can use ArrayInitOne/StructInitOne;
2197 // otherwise we use the full ArrayInit/StructInit.
2198
2199 const scratch_top = p.scratch.items.len;
2200 defer p.scratch.shrinkRetainingCapacity(scratch_top);
2201 const field_init = try p.parseFieldInit();
2202 if (field_init != 0) {
2203 try p.scratch.append(p.gpa, field_init);
2204 while (true) {
2205 switch (p.token_tags[p.tok_i]) {
2206 .comma => p.tok_i += 1,
2207 .r_brace => {
2208 p.tok_i += 1;
2209 break;
2210 },
2211 .colon, .r_paren, .r_bracket => return p.failExpected(.r_brace),
2212 // Likely just a missing comma; give error but continue parsing.
2213 else => try p.warn(.expected_comma_after_initializer),
2214 }
2215 if (p.eatToken(.r_brace)) |_| break;
2216 const next = try p.expectFieldInit();
2217 try p.scratch.append(p.gpa, next);
2218 }
2219 const comma = (p.token_tags[p.tok_i - 2] == .comma);
2220 const inits = p.scratch.items[scratch_top..];
2221 switch (inits.len) {
2222 0 => unreachable,
2223 1 => return p.addNode(.{
2224 .tag = if (comma) .struct_init_one_comma else .struct_init_one,
2225 .main_token = lbrace,
2226 .data = .{
2227 .lhs = lhs,
2228 .rhs = inits[0],
2229 },
2230 }),
2231 else => return p.addNode(.{
2232 .tag = if (comma) .struct_init_comma else .struct_init,
2233 .main_token = lbrace,
2234 .data = .{
2235 .lhs = lhs,
2236 .rhs = try p.addExtra(try p.listToSpan(inits)),
2237 },
2238 }),
2239 }
2240 }
2241
2242 while (true) {
2243 if (p.eatToken(.r_brace)) |_| break;
2244 const elem_init = try p.expectExpr();
2245 try p.scratch.append(p.gpa, elem_init);
2246 switch (p.token_tags[p.tok_i]) {
2247 .comma => p.tok_i += 1,
2248 .r_brace => {
2249 p.tok_i += 1;
2250 break;
2251 },
2252 .colon, .r_paren, .r_bracket => return p.failExpected(.r_brace),
2253 // Likely just a missing comma; give error but continue parsing.
2254 else => try p.warn(.expected_comma_after_initializer),
2255 }
2256 }
2257 const comma = (p.token_tags[p.tok_i - 2] == .comma);
2258 const inits = p.scratch.items[scratch_top..];
2259 switch (inits.len) {
2260 0 => return p.addNode(.{
2261 .tag = .struct_init_one,
2262 .main_token = lbrace,
2263 .data = .{
2264 .lhs = lhs,
2265 .rhs = 0,
2266 },
2267 }),
2268 1 => return p.addNode(.{
2269 .tag = if (comma) .array_init_one_comma else .array_init_one,
2270 .main_token = lbrace,
2271 .data = .{
2272 .lhs = lhs,
2273 .rhs = inits[0],
2274 },
2275 }),
2276 else => return p.addNode(.{
2277 .tag = if (comma) .array_init_comma else .array_init,
2278 .main_token = lbrace,
2279 .data = .{
2280 .lhs = lhs,
2281 .rhs = try p.addExtra(try p.listToSpan(inits)),
2282 },
2283 }),
2284 }
2285 }
2286
2287 /// ErrorUnionExpr <- SuffixExpr (EXCLAMATIONMARK TypeExpr)?
2288 fn parseErrorUnionExpr(p: *Parser) !Node.Index {
2289 const suffix_expr = try p.parseSuffixExpr();
2290 if (suffix_expr == 0) return null_node;
2291 const bang = p.eatToken(.bang) orelse return suffix_expr;
2292 return p.addNode(.{
2293 .tag = .error_union,
2294 .main_token = bang,
2295 .data = .{
2296 .lhs = suffix_expr,
2297 .rhs = try p.expectTypeExpr(),
2298 },
2299 });
2300 }
2301
2302 /// SuffixExpr
2303 /// <- KEYWORD_async PrimaryTypeExpr SuffixOp* FnCallArguments
2304 /// / PrimaryTypeExpr (SuffixOp / FnCallArguments)*
2305 ///
2306 /// FnCallArguments <- LPAREN ExprList RPAREN
2307 ///
2308 /// ExprList <- (Expr COMMA)* Expr?
2309 fn parseSuffixExpr(p: *Parser) !Node.Index {
2310 if (p.eatToken(.keyword_async)) |_| {
2311 var res = try p.expectPrimaryTypeExpr();
2312 while (true) {
2313 const node = try p.parseSuffixOp(res);
2314 if (node == 0) break;
2315 res = node;
2316 }
2317 const lparen = p.eatToken(.l_paren) orelse {
2318 try p.warn(.expected_param_list);
2319 return res;
2320 };
2321 const scratch_top = p.scratch.items.len;
2322 defer p.scratch.shrinkRetainingCapacity(scratch_top);
2323 while (true) {
2324 if (p.eatToken(.r_paren)) |_| break;
2325 const param = try p.expectExpr();
2326 try p.scratch.append(p.gpa, param);
2327 switch (p.token_tags[p.tok_i]) {
2328 .comma => p.tok_i += 1,
2329 .r_paren => {
2330 p.tok_i += 1;
2331 break;
2332 },
2333 .colon, .r_brace, .r_bracket => return p.failExpected(.r_paren),
2334 // Likely just a missing comma; give error but continue parsing.
2335 else => try p.warn(.expected_comma_after_arg),
2336 }
2337 }
2338 const comma = (p.token_tags[p.tok_i - 2] == .comma);
2339 const params = p.scratch.items[scratch_top..];
2340 switch (params.len) {
2341 0 => return p.addNode(.{
2342 .tag = if (comma) .async_call_one_comma else .async_call_one,
2343 .main_token = lparen,
2344 .data = .{
2345 .lhs = res,
2346 .rhs = 0,
2347 },
2348 }),
2349 1 => return p.addNode(.{
2350 .tag = if (comma) .async_call_one_comma else .async_call_one,
2351 .main_token = lparen,
2352 .data = .{
2353 .lhs = res,
2354 .rhs = params[0],
2355 },
2356 }),
2357 else => return p.addNode(.{
2358 .tag = if (comma) .async_call_comma else .async_call,
2359 .main_token = lparen,
2360 .data = .{
2361 .lhs = res,
2362 .rhs = try p.addExtra(try p.listToSpan(params)),
2363 },
2364 }),
2365 }
2366 }
2367
2368 var res = try p.parsePrimaryTypeExpr();
2369 if (res == 0) return res;
2370 while (true) {
2371 const suffix_op = try p.parseSuffixOp(res);
2372 if (suffix_op != 0) {
2373 res = suffix_op;
2374 continue;
2375 }
2376 const lparen = p.eatToken(.l_paren) orelse return res;
2377 const scratch_top = p.scratch.items.len;
2378 defer p.scratch.shrinkRetainingCapacity(scratch_top);
2379 while (true) {
2380 if (p.eatToken(.r_paren)) |_| break;
2381 const param = try p.expectExpr();
2382 try p.scratch.append(p.gpa, param);
2383 switch (p.token_tags[p.tok_i]) {
2384 .comma => p.tok_i += 1,
2385 .r_paren => {
2386 p.tok_i += 1;
2387 break;
2388 },
2389 .colon, .r_brace, .r_bracket => return p.failExpected(.r_paren),
2390 // Likely just a missing comma; give error but continue parsing.
2391 else => try p.warn(.expected_comma_after_arg),
2392 }
2393 }
2394 const comma = (p.token_tags[p.tok_i - 2] == .comma);
2395 const params = p.scratch.items[scratch_top..];
2396 res = switch (params.len) {
2397 0 => try p.addNode(.{
2398 .tag = if (comma) .call_one_comma else .call_one,
2399 .main_token = lparen,
2400 .data = .{
2401 .lhs = res,
2402 .rhs = 0,
2403 },
2404 }),
2405 1 => try p.addNode(.{
2406 .tag = if (comma) .call_one_comma else .call_one,
2407 .main_token = lparen,
2408 .data = .{
2409 .lhs = res,
2410 .rhs = params[0],
2411 },
2412 }),
2413 else => try p.addNode(.{
2414 .tag = if (comma) .call_comma else .call,
2415 .main_token = lparen,
2416 .data = .{
2417 .lhs = res,
2418 .rhs = try p.addExtra(try p.listToSpan(params)),
2419 },
2420 }),
2421 };
2422 }
2423 }
2424
2425 /// PrimaryTypeExpr
2426 /// <- BUILTINIDENTIFIER FnCallArguments
2427 /// / CHAR_LITERAL
2428 /// / ContainerDecl
2429 /// / DOT IDENTIFIER
2430 /// / DOT InitList
2431 /// / ErrorSetDecl
2432 /// / FLOAT
2433 /// / FnProto
2434 /// / GroupedExpr
2435 /// / LabeledTypeExpr
2436 /// / IDENTIFIER
2437 /// / IfTypeExpr
2438 /// / INTEGER
2439 /// / KEYWORD_comptime TypeExpr
2440 /// / KEYWORD_error DOT IDENTIFIER
2441 /// / KEYWORD_anyframe
2442 /// / KEYWORD_unreachable
2443 /// / STRINGLITERAL
2444 /// / SwitchExpr
2445 ///
2446 /// ContainerDecl <- (KEYWORD_extern / KEYWORD_packed)? ContainerDeclAuto
2447 ///
2448 /// ContainerDeclAuto <- ContainerDeclType LBRACE container_doc_comment? ContainerMembers RBRACE
2449 ///
2450 /// InitList
2451 /// <- LBRACE FieldInit (COMMA FieldInit)* COMMA? RBRACE
2452 /// / LBRACE Expr (COMMA Expr)* COMMA? RBRACE
2453 /// / LBRACE RBRACE
2454 ///
2455 /// ErrorSetDecl <- KEYWORD_error LBRACE IdentifierList RBRACE
2456 ///
2457 /// GroupedExpr <- LPAREN Expr RPAREN
2458 ///
2459 /// IfTypeExpr <- IfPrefix TypeExpr (KEYWORD_else Payload? TypeExpr)?
2460 ///
2461 /// LabeledTypeExpr
2462 /// <- BlockLabel Block
2463 /// / BlockLabel? LoopTypeExpr
2464 ///
2465 /// LoopTypeExpr <- KEYWORD_inline? (ForTypeExpr / WhileTypeExpr)
2466 fn parsePrimaryTypeExpr(p: *Parser) !Node.Index {
2467 switch (p.token_tags[p.tok_i]) {
2468 .char_literal => return p.addNode(.{
2469 .tag = .char_literal,
2470 .main_token = p.nextToken(),
2471 .data = .{
2472 .lhs = undefined,
2473 .rhs = undefined,
2474 },
2475 }),
2476 .number_literal => return p.addNode(.{
2477 .tag = .number_literal,
2478 .main_token = p.nextToken(),
2479 .data = .{
2480 .lhs = undefined,
2481 .rhs = undefined,
2482 },
2483 }),
2484 .keyword_unreachable => return p.addNode(.{
2485 .tag = .unreachable_literal,
2486 .main_token = p.nextToken(),
2487 .data = .{
2488 .lhs = undefined,
2489 .rhs = undefined,
2490 },
2491 }),
2492 .keyword_anyframe => return p.addNode(.{
2493 .tag = .anyframe_literal,
2494 .main_token = p.nextToken(),
2495 .data = .{
2496 .lhs = undefined,
2497 .rhs = undefined,
2498 },
2499 }),
2500 .string_literal => {
2501 const main_token = p.nextToken();
2502 return p.addNode(.{
2503 .tag = .string_literal,
2504 .main_token = main_token,
2505 .data = .{
2506 .lhs = undefined,
2507 .rhs = undefined,
2508 },
2509 });
2510 },
2511
2512 .builtin => return p.parseBuiltinCall(),
2513 .keyword_fn => return p.parseFnProto(),
2514 .keyword_if => return p.parseIf(expectTypeExpr),
2515 .keyword_switch => return p.expectSwitchExpr(),
2516
2517 .keyword_extern,
2518 .keyword_packed,
2519 => {
2520 p.tok_i += 1;
2521 return p.parseContainerDeclAuto();
2522 },
2523
2524 .keyword_struct,
2525 .keyword_opaque,
2526 .keyword_enum,
2527 .keyword_union,
2528 => return p.parseContainerDeclAuto(),
2529
2530 .keyword_comptime => return p.addNode(.{
2531 .tag = .@"comptime",
2532 .main_token = p.nextToken(),
2533 .data = .{
2534 .lhs = try p.expectTypeExpr(),
2535 .rhs = undefined,
2536 },
2537 }),
2538 .multiline_string_literal_line => {
2539 const first_line = p.nextToken();
2540 while (p.token_tags[p.tok_i] == .multiline_string_literal_line) {
2541 p.tok_i += 1;
2542 }
2543 return p.addNode(.{
2544 .tag = .multiline_string_literal,
2545 .main_token = first_line,
2546 .data = .{
2547 .lhs = first_line,
2548 .rhs = p.tok_i - 1,
2549 },
2550 });
2551 },
2552 .identifier => switch (p.token_tags[p.tok_i + 1]) {
2553 .colon => switch (p.token_tags[p.tok_i + 2]) {
2554 .keyword_inline => {
2555 p.tok_i += 3;
2556 switch (p.token_tags[p.tok_i]) {
2557 .keyword_for => return p.parseForTypeExpr(),
2558 .keyword_while => return p.parseWhileTypeExpr(),
2559 else => return p.fail(.expected_inlinable),
2560 }
2561 },
2562 .keyword_for => {
2563 p.tok_i += 2;
2564 return p.parseForTypeExpr();
2565 },
2566 .keyword_while => {
2567 p.tok_i += 2;
2568 return p.parseWhileTypeExpr();
2569 },
2570 .l_brace => {
2571 p.tok_i += 2;
2572 return p.parseBlock();
2573 },
2574 else => return p.addNode(.{
2575 .tag = .identifier,
2576 .main_token = p.nextToken(),
2577 .data = .{
2578 .lhs = undefined,
2579 .rhs = undefined,
2580 },
2581 }),
2582 },
2583 else => return p.addNode(.{
2584 .tag = .identifier,
2585 .main_token = p.nextToken(),
2586 .data = .{
2587 .lhs = undefined,
2588 .rhs = undefined,
2589 },
2590 }),
2591 },
2592 .keyword_inline => {
2593 p.tok_i += 1;
2594 switch (p.token_tags[p.tok_i]) {
2595 .keyword_for => return p.parseForTypeExpr(),
2596 .keyword_while => return p.parseWhileTypeExpr(),
2597 else => return p.fail(.expected_inlinable),
2598 }
2599 },
2600 .keyword_for => return p.parseForTypeExpr(),
2601 .keyword_while => return p.parseWhileTypeExpr(),
2602 .period => switch (p.token_tags[p.tok_i + 1]) {
2603 .identifier => return p.addNode(.{
2604 .tag = .enum_literal,
2605 .data = .{
2606 .lhs = p.nextToken(), // dot
2607 .rhs = undefined,
2608 },
2609 .main_token = p.nextToken(), // identifier
2610 }),
2611 .l_brace => {
2612 const lbrace = p.tok_i + 1;
2613 p.tok_i = lbrace + 1;
2614
2615 // If there are 0, 1, or 2 items, we can use ArrayInitDotTwo/StructInitDotTwo;
2616 // otherwise we use the full ArrayInitDot/StructInitDot.
2617
2618 const scratch_top = p.scratch.items.len;
2619 defer p.scratch.shrinkRetainingCapacity(scratch_top);
2620 const field_init = try p.parseFieldInit();
2621 if (field_init != 0) {
2622 try p.scratch.append(p.gpa, field_init);
2623 while (true) {
2624 switch (p.token_tags[p.tok_i]) {
2625 .comma => p.tok_i += 1,
2626 .r_brace => {
2627 p.tok_i += 1;
2628 break;
2629 },
2630 .colon, .r_paren, .r_bracket => return p.failExpected(.r_brace),
2631 // Likely just a missing comma; give error but continue parsing.
2632 else => try p.warn(.expected_comma_after_initializer),
2633 }
2634 if (p.eatToken(.r_brace)) |_| break;
2635 const next = try p.expectFieldInit();
2636 try p.scratch.append(p.gpa, next);
2637 }
2638 const comma = (p.token_tags[p.tok_i - 2] == .comma);
2639 const inits = p.scratch.items[scratch_top..];
2640 switch (inits.len) {
2641 0 => unreachable,
2642 1 => return p.addNode(.{
2643 .tag = if (comma) .struct_init_dot_two_comma else .struct_init_dot_two,
2644 .main_token = lbrace,
2645 .data = .{
2646 .lhs = inits[0],
2647 .rhs = 0,
2648 },
2649 }),
2650 2 => return p.addNode(.{
2651 .tag = if (comma) .struct_init_dot_two_comma else .struct_init_dot_two,
2652 .main_token = lbrace,
2653 .data = .{
2654 .lhs = inits[0],
2655 .rhs = inits[1],
2656 },
2657 }),
2658 else => {
2659 const span = try p.listToSpan(inits);
2660 return p.addNode(.{
2661 .tag = if (comma) .struct_init_dot_comma else .struct_init_dot,
2662 .main_token = lbrace,
2663 .data = .{
2664 .lhs = span.start,
2665 .rhs = span.end,
2666 },
2667 });
2668 },
2669 }
2670 }
2671
2672 while (true) {
2673 if (p.eatToken(.r_brace)) |_| break;
2674 const elem_init = try p.expectExpr();
2675 try p.scratch.append(p.gpa, elem_init);
2676 switch (p.token_tags[p.tok_i]) {
2677 .comma => p.tok_i += 1,
2678 .r_brace => {
2679 p.tok_i += 1;
2680 break;
2681 },
2682 .colon, .r_paren, .r_bracket => return p.failExpected(.r_brace),
2683 // Likely just a missing comma; give error but continue parsing.
2684 else => try p.warn(.expected_comma_after_initializer),
2685 }
2686 }
2687 const comma = (p.token_tags[p.tok_i - 2] == .comma);
2688 const inits = p.scratch.items[scratch_top..];
2689 switch (inits.len) {
2690 0 => return p.addNode(.{
2691 .tag = .struct_init_dot_two,
2692 .main_token = lbrace,
2693 .data = .{
2694 .lhs = 0,
2695 .rhs = 0,
2696 },
2697 }),
2698 1 => return p.addNode(.{
2699 .tag = if (comma) .array_init_dot_two_comma else .array_init_dot_two,
2700 .main_token = lbrace,
2701 .data = .{
2702 .lhs = inits[0],
2703 .rhs = 0,
2704 },
2705 }),
2706 2 => return p.addNode(.{
2707 .tag = if (comma) .array_init_dot_two_comma else .array_init_dot_two,
2708 .main_token = lbrace,
2709 .data = .{
2710 .lhs = inits[0],
2711 .rhs = inits[1],
2712 },
2713 }),
2714 else => {
2715 const span = try p.listToSpan(inits);
2716 return p.addNode(.{
2717 .tag = if (comma) .array_init_dot_comma else .array_init_dot,
2718 .main_token = lbrace,
2719 .data = .{
2720 .lhs = span.start,
2721 .rhs = span.end,
2722 },
2723 });
2724 },
2725 }
2726 },
2727 else => return null_node,
2728 },
2729 .keyword_error => switch (p.token_tags[p.tok_i + 1]) {
2730 .l_brace => {
2731 const error_token = p.tok_i;
2732 p.tok_i += 2;
2733 while (true) {
2734 if (p.eatToken(.r_brace)) |_| break;
2735 _ = try p.eatDocComments();
2736 _ = try p.expectToken(.identifier);
2737 switch (p.token_tags[p.tok_i]) {
2738 .comma => p.tok_i += 1,
2739 .r_brace => {
2740 p.tok_i += 1;
2741 break;
2742 },
2743 .colon, .r_paren, .r_bracket => return p.failExpected(.r_brace),
2744 // Likely just a missing comma; give error but continue parsing.
2745 else => try p.warn(.expected_comma_after_field),
2746 }
2747 }
2748 return p.addNode(.{
2749 .tag = .error_set_decl,
2750 .main_token = error_token,
2751 .data = .{
2752 .lhs = undefined,
2753 .rhs = p.tok_i - 1, // rbrace
2754 },
2755 });
2756 },
2757 else => {
2758 const main_token = p.nextToken();
2759 const period = p.eatToken(.period);
2760 if (period == null) try p.warnExpected(.period);
2761 const identifier = p.eatToken(.identifier);
2762 if (identifier == null) try p.warnExpected(.identifier);
2763 return p.addNode(.{
2764 .tag = .error_value,
2765 .main_token = main_token,
2766 .data = .{
2767 .lhs = period orelse 0,
2768 .rhs = identifier orelse 0,
2769 },
2770 });
2771 },
2772 },
2773 .l_paren => return p.addNode(.{
2774 .tag = .grouped_expression,
2775 .main_token = p.nextToken(),
2776 .data = .{
2777 .lhs = try p.expectExpr(),
2778 .rhs = try p.expectToken(.r_paren),
2779 },
2780 }),
2781 else => return null_node,
2782 }
2783 }
2784
2785 fn expectPrimaryTypeExpr(p: *Parser) !Node.Index {
2786 const node = try p.parsePrimaryTypeExpr();
2787 if (node == 0) {
2788 return p.fail(.expected_primary_type_expr);
2789 }
2790 return node;
2791 }
2792
2793 /// ForPrefix <- KEYWORD_for LPAREN Expr RPAREN PtrIndexPayload
2794 ///
2795 /// ForTypeExpr <- ForPrefix TypeExpr (KEYWORD_else TypeExpr)?
2796 fn parseForTypeExpr(p: *Parser) !Node.Index {
2797 const for_token = p.eatToken(.keyword_for) orelse return null_node;
2798 _ = try p.expectToken(.l_paren);
2799 const array_expr = try p.expectExpr();
2800 _ = try p.expectToken(.r_paren);
2801 const found_payload = try p.parsePtrIndexPayload();
2802 if (found_payload == 0) try p.warn(.expected_loop_payload);
2803
2804 const then_expr = try p.expectTypeExpr();
2805 _ = p.eatToken(.keyword_else) orelse {
2806 return p.addNode(.{
2807 .tag = .for_simple,
2808 .main_token = for_token,
2809 .data = .{
2810 .lhs = array_expr,
2811 .rhs = then_expr,
2812 },
2813 });
2814 };
2815 const else_expr = try p.expectTypeExpr();
2816 return p.addNode(.{
2817 .tag = .@"for",
2818 .main_token = for_token,
2819 .data = .{
2820 .lhs = array_expr,
2821 .rhs = try p.addExtra(Node.If{
2822 .then_expr = then_expr,
2823 .else_expr = else_expr,
2824 }),
2825 },
2826 });
2827 }
2828
2829 /// WhilePrefix <- KEYWORD_while LPAREN Expr RPAREN PtrPayload? WhileContinueExpr?
2830 ///
2831 /// WhileTypeExpr <- WhilePrefix TypeExpr (KEYWORD_else Payload? TypeExpr)?
2832 fn parseWhileTypeExpr(p: *Parser) !Node.Index {
2833 const while_token = p.eatToken(.keyword_while) orelse return null_node;
2834 _ = try p.expectToken(.l_paren);
2835 const condition = try p.expectExpr();
2836 _ = try p.expectToken(.r_paren);
2837 _ = try p.parsePtrPayload();
2838 const cont_expr = try p.parseWhileContinueExpr();
2839
2840 const then_expr = try p.expectTypeExpr();
2841 _ = p.eatToken(.keyword_else) orelse {
2842 if (cont_expr == 0) {
2843 return p.addNode(.{
2844 .tag = .while_simple,
2845 .main_token = while_token,
2846 .data = .{
2847 .lhs = condition,
2848 .rhs = then_expr,
2849 },
2850 });
2851 } else {
2852 return p.addNode(.{
2853 .tag = .while_cont,
2854 .main_token = while_token,
2855 .data = .{
2856 .lhs = condition,
2857 .rhs = try p.addExtra(Node.WhileCont{
2858 .cont_expr = cont_expr,
2859 .then_expr = then_expr,
2860 }),
2861 },
2862 });
2863 }
2864 };
2865 _ = try p.parsePayload();
2866 const else_expr = try p.expectTypeExpr();
2867 return p.addNode(.{
2868 .tag = .@"while",
2869 .main_token = while_token,
2870 .data = .{
2871 .lhs = condition,
2872 .rhs = try p.addExtra(Node.While{
2873 .cont_expr = cont_expr,
2874 .then_expr = then_expr,
2875 .else_expr = else_expr,
2876 }),
2877 },
2878 });
2879 }
2880
2881 /// SwitchExpr <- KEYWORD_switch LPAREN Expr RPAREN LBRACE SwitchProngList RBRACE
2882 fn expectSwitchExpr(p: *Parser) !Node.Index {
2883 const switch_token = p.assertToken(.keyword_switch);
2884 _ = try p.expectToken(.l_paren);
2885 const expr_node = try p.expectExpr();
2886 _ = try p.expectToken(.r_paren);
2887 _ = try p.expectToken(.l_brace);
2888 const cases = try p.parseSwitchProngList();
2889 const trailing_comma = p.token_tags[p.tok_i - 1] == .comma;
2890 _ = try p.expectToken(.r_brace);
2891
2892 return p.addNode(.{
2893 .tag = if (trailing_comma) .switch_comma else .@"switch",
2894 .main_token = switch_token,
2895 .data = .{
2896 .lhs = expr_node,
2897 .rhs = try p.addExtra(Node.SubRange{
2898 .start = cases.start,
2899 .end = cases.end,
2900 }),
2901 },
2902 });
2903 }
2904
2905 /// AsmExpr <- KEYWORD_asm KEYWORD_volatile? LPAREN Expr AsmOutput? RPAREN
2906 ///
2907 /// AsmOutput <- COLON AsmOutputList AsmInput?
2908 ///
2909 /// AsmInput <- COLON AsmInputList AsmClobbers?
2910 ///
2911 /// AsmClobbers <- COLON StringList
2912 ///
2913 /// StringList <- (STRINGLITERAL COMMA)* STRINGLITERAL?
2914 ///
2915 /// AsmOutputList <- (AsmOutputItem COMMA)* AsmOutputItem?
2916 ///
2917 /// AsmInputList <- (AsmInputItem COMMA)* AsmInputItem?
2918 fn expectAsmExpr(p: *Parser) !Node.Index {
2919 const asm_token = p.assertToken(.keyword_asm);
2920 _ = p.eatToken(.keyword_volatile);
2921 _ = try p.expectToken(.l_paren);
2922 const template = try p.expectExpr();
2923
2924 if (p.eatToken(.r_paren)) |rparen| {
2925 return p.addNode(.{
2926 .tag = .asm_simple,
2927 .main_token = asm_token,
2928 .data = .{
2929 .lhs = template,
2930 .rhs = rparen,
2931 },
2932 });
2933 }
2934
2935 _ = try p.expectToken(.colon);
2936
2937 const scratch_top = p.scratch.items.len;
2938 defer p.scratch.shrinkRetainingCapacity(scratch_top);
2939
2940 while (true) {
2941 const output_item = try p.parseAsmOutputItem();
2942 if (output_item == 0) break;
2943 try p.scratch.append(p.gpa, output_item);
2944 switch (p.token_tags[p.tok_i]) {
2945 .comma => p.tok_i += 1,
2946 // All possible delimiters.
2947 .colon, .r_paren, .r_brace, .r_bracket => break,
2948 // Likely just a missing comma; give error but continue parsing.
2949 else => try p.warnExpected(.comma),
2950 }
2951 }
2952 if (p.eatToken(.colon)) |_| {
2953 while (true) {
2954 const input_item = try p.parseAsmInputItem();
2955 if (input_item == 0) break;
2956 try p.scratch.append(p.gpa, input_item);
2957 switch (p.token_tags[p.tok_i]) {
2958 .comma => p.tok_i += 1,
2959 // All possible delimiters.
2960 .colon, .r_paren, .r_brace, .r_bracket => break,
2961 // Likely just a missing comma; give error but continue parsing.
2962 else => try p.warnExpected(.comma),
2963 }
2964 }
2965 if (p.eatToken(.colon)) |_| {
2966 while (p.eatToken(.string_literal)) |_| {
2967 switch (p.token_tags[p.tok_i]) {
2968 .comma => p.tok_i += 1,
2969 .colon, .r_paren, .r_brace, .r_bracket => break,
2970 // Likely just a missing comma; give error but continue parsing.
2971 else => try p.warnExpected(.comma),
2972 }
2973 }
2974 }
2975 }
2976 const rparen = try p.expectToken(.r_paren);
2977 const span = try p.listToSpan(p.scratch.items[scratch_top..]);
2978 return p.addNode(.{
2979 .tag = .@"asm",
2980 .main_token = asm_token,
2981 .data = .{
2982 .lhs = template,
2983 .rhs = try p.addExtra(Node.Asm{
2984 .items_start = span.start,
2985 .items_end = span.end,
2986 .rparen = rparen,
2987 }),
2988 },
2989 });
2990 }
2991
2992 /// AsmOutputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERAL LPAREN (MINUSRARROW TypeExpr / IDENTIFIER) RPAREN
2993 fn parseAsmOutputItem(p: *Parser) !Node.Index {
2994 _ = p.eatToken(.l_bracket) orelse return null_node;
2995 const identifier = try p.expectToken(.identifier);
2996 _ = try p.expectToken(.r_bracket);
2997 _ = try p.expectToken(.string_literal);
2998 _ = try p.expectToken(.l_paren);
2999 const type_expr: Node.Index = blk: {
3000 if (p.eatToken(.arrow)) |_| {
3001 break :blk try p.expectTypeExpr();
3002 } else {
3003 _ = try p.expectToken(.identifier);
3004 break :blk null_node;
3005 }
3006 };
3007 const rparen = try p.expectToken(.r_paren);
3008 return p.addNode(.{
3009 .tag = .asm_output,
3010 .main_token = identifier,
3011 .data = .{
3012 .lhs = type_expr,
3013 .rhs = rparen,
3014 },
3015 });
3016 }
3017
3018 /// AsmInputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERAL LPAREN Expr RPAREN
3019 fn parseAsmInputItem(p: *Parser) !Node.Index {
3020 _ = p.eatToken(.l_bracket) orelse return null_node;
3021 const identifier = try p.expectToken(.identifier);
3022 _ = try p.expectToken(.r_bracket);
3023 _ = try p.expectToken(.string_literal);
3024 _ = try p.expectToken(.l_paren);
3025 const expr = try p.expectExpr();
3026 const rparen = try p.expectToken(.r_paren);
3027 return p.addNode(.{
3028 .tag = .asm_input,
3029 .main_token = identifier,
3030 .data = .{
3031 .lhs = expr,
3032 .rhs = rparen,
3033 },
3034 });
3035 }
3036
3037 /// BreakLabel <- COLON IDENTIFIER
3038 fn parseBreakLabel(p: *Parser) !TokenIndex {
3039 _ = p.eatToken(.colon) orelse return @as(TokenIndex, 0);
3040 return p.expectToken(.identifier);
3041 }
3042
3043 /// BlockLabel <- IDENTIFIER COLON
3044 fn parseBlockLabel(p: *Parser) TokenIndex {
3045 if (p.token_tags[p.tok_i] == .identifier and
3046 p.token_tags[p.tok_i + 1] == .colon)
3047 {
3048 const identifier = p.tok_i;
3049 p.tok_i += 2;
3050 return identifier;
3051 }
3052 return null_node;
3053 }
3054
3055 /// FieldInit <- DOT IDENTIFIER EQUAL Expr
3056 fn parseFieldInit(p: *Parser) !Node.Index {
3057 if (p.token_tags[p.tok_i + 0] == .period and
3058 p.token_tags[p.tok_i + 1] == .identifier and
3059 p.token_tags[p.tok_i + 2] == .equal)
3060 {
3061 p.tok_i += 3;
3062 return p.expectExpr();
3063 } else {
3064 return null_node;
3065 }
3066 }
3067
3068 fn expectFieldInit(p: *Parser) !Node.Index {
3069 if (p.token_tags[p.tok_i] != .period or
3070 p.token_tags[p.tok_i + 1] != .identifier or
3071 p.token_tags[p.tok_i + 2] != .equal)
3072 return p.fail(.expected_initializer);
3073
3074 p.tok_i += 3;
3075 return p.expectExpr();
3076 }
3077
3078 /// WhileContinueExpr <- COLON LPAREN AssignExpr RPAREN
3079 fn parseWhileContinueExpr(p: *Parser) !Node.Index {
3080 _ = p.eatToken(.colon) orelse {
3081 if (p.token_tags[p.tok_i] == .l_paren and
3082 p.tokensOnSameLine(p.tok_i - 1, p.tok_i))
3083 return p.fail(.expected_continue_expr);
3084 return null_node;
3085 };
3086 _ = try p.expectToken(.l_paren);
3087 const node = try p.parseAssignExpr();
3088 if (node == 0) return p.fail(.expected_expr_or_assignment);
3089 _ = try p.expectToken(.r_paren);
3090 return node;
3091 }
3092
3093 /// LinkSection <- KEYWORD_linksection LPAREN Expr RPAREN
3094 fn parseLinkSection(p: *Parser) !Node.Index {
3095 _ = p.eatToken(.keyword_linksection) orelse return null_node;
3096 _ = try p.expectToken(.l_paren);
3097 const expr_node = try p.expectExpr();
3098 _ = try p.expectToken(.r_paren);
3099 return expr_node;
3100 }
3101
3102 /// CallConv <- KEYWORD_callconv LPAREN Expr RPAREN
3103 fn parseCallconv(p: *Parser) !Node.Index {
3104 _ = p.eatToken(.keyword_callconv) orelse return null_node;
3105 _ = try p.expectToken(.l_paren);
3106 const expr_node = try p.expectExpr();
3107 _ = try p.expectToken(.r_paren);
3108 return expr_node;
3109 }
3110
3111 /// AddrSpace <- KEYWORD_addrspace LPAREN Expr RPAREN
3112 fn parseAddrSpace(p: *Parser) !Node.Index {
3113 _ = p.eatToken(.keyword_addrspace) orelse return null_node;
3114 _ = try p.expectToken(.l_paren);
3115 const expr_node = try p.expectExpr();
3116 _ = try p.expectToken(.r_paren);
3117 return expr_node;
3118 }
3119
3120 /// This function can return null nodes and then still return nodes afterwards,
3121 /// such as in the case of anytype and `...`. Caller must look for rparen to find
3122 /// out when there are no more param decls left.
3123 ///
3124 /// ParamDecl
3125 /// <- doc_comment? (KEYWORD_noalias / KEYWORD_comptime)? (IDENTIFIER COLON)? ParamType
3126 /// / DOT3
3127 ///
3128 /// ParamType
3129 /// <- KEYWORD_anytype
3130 /// / TypeExpr
3131 fn expectParamDecl(p: *Parser) !Node.Index {
3132 _ = try p.eatDocComments();
3133 switch (p.token_tags[p.tok_i]) {
3134 .keyword_noalias, .keyword_comptime => p.tok_i += 1,
3135 .ellipsis3 => {
3136 p.tok_i += 1;
3137 return null_node;
3138 },
3139 else => {},
3140 }
3141 if (p.token_tags[p.tok_i] == .identifier and
3142 p.token_tags[p.tok_i + 1] == .colon)
3143 {
3144 p.tok_i += 2;
3145 }
3146 switch (p.token_tags[p.tok_i]) {
3147 .keyword_anytype => {
3148 p.tok_i += 1;
3149 return null_node;
3150 },
3151 else => return p.expectTypeExpr(),
3152 }
3153 }
3154
3155 /// Payload <- PIPE IDENTIFIER PIPE
3156 fn parsePayload(p: *Parser) !TokenIndex {
3157 _ = p.eatToken(.pipe) orelse return @as(TokenIndex, 0);
3158 const identifier = try p.expectToken(.identifier);
3159 _ = try p.expectToken(.pipe);
3160 return identifier;
3161 }
3162
3163 /// PtrPayload <- PIPE ASTERISK? IDENTIFIER PIPE
3164 fn parsePtrPayload(p: *Parser) !TokenIndex {
3165 _ = p.eatToken(.pipe) orelse return @as(TokenIndex, 0);
3166 _ = p.eatToken(.asterisk);
3167 const identifier = try p.expectToken(.identifier);
3168 _ = try p.expectToken(.pipe);
3169 return identifier;
3170 }
3171
3172 /// Returns the first identifier token, if any.
3173 ///
3174 /// PtrIndexPayload <- PIPE ASTERISK? IDENTIFIER (COMMA IDENTIFIER)? PIPE
3175 fn parsePtrIndexPayload(p: *Parser) !TokenIndex {
3176 _ = p.eatToken(.pipe) orelse return @as(TokenIndex, 0);
3177 _ = p.eatToken(.asterisk);
3178 const identifier = try p.expectToken(.identifier);
3179 if (p.eatToken(.comma) != null) {
3180 _ = try p.expectToken(.identifier);
3181 }
3182 _ = try p.expectToken(.pipe);
3183 return identifier;
3184 }
3185
3186 /// SwitchProng <- KEYWORD_inline? SwitchCase EQUALRARROW PtrIndexPayload? AssignExpr
3187 ///
3188 /// SwitchCase
3189 /// <- SwitchItem (COMMA SwitchItem)* COMMA?
3190 /// / KEYWORD_else
3191 fn parseSwitchProng(p: *Parser) !Node.Index {
3192 const scratch_top = p.scratch.items.len;
3193 defer p.scratch.shrinkRetainingCapacity(scratch_top);
3194
3195 const is_inline = p.eatToken(.keyword_inline) != null;
3196
3197 if (p.eatToken(.keyword_else) == null) {
3198 while (true) {
3199 const item = try p.parseSwitchItem();
3200 if (item == 0) break;
3201 try p.scratch.append(p.gpa, item);
3202 if (p.eatToken(.comma) == null) break;
3203 }
3204 if (scratch_top == p.scratch.items.len) {
3205 if (is_inline) p.tok_i -= 1;
3206 return null_node;
3207 }
3208 }
3209 const arrow_token = try p.expectToken(.equal_angle_bracket_right);
3210 _ = try p.parsePtrIndexPayload();
3211
3212 const items = p.scratch.items[scratch_top..];
3213 switch (items.len) {
3214 0 => return p.addNode(.{
3215 .tag = if (is_inline) .switch_case_inline_one else .switch_case_one,
3216 .main_token = arrow_token,
3217 .data = .{
3218 .lhs = 0,
3219 .rhs = try p.expectAssignExpr(),
3220 },
3221 }),
3222 1 => return p.addNode(.{
3223 .tag = if (is_inline) .switch_case_inline_one else .switch_case_one,
3224 .main_token = arrow_token,
3225 .data = .{
3226 .lhs = items[0],
3227 .rhs = try p.expectAssignExpr(),
3228 },
3229 }),
3230 else => return p.addNode(.{
3231 .tag = if (is_inline) .switch_case_inline else .switch_case,
3232 .main_token = arrow_token,
3233 .data = .{
3234 .lhs = try p.addExtra(try p.listToSpan(items)),
3235 .rhs = try p.expectAssignExpr(),
3236 },
3237 }),
3238 }
3239 }
3240
3241 /// SwitchItem <- Expr (DOT3 Expr)?
3242 fn parseSwitchItem(p: *Parser) !Node.Index {
3243 const expr = try p.parseExpr();
3244 if (expr == 0) return null_node;
3245
3246 if (p.eatToken(.ellipsis3)) |token| {
3247 return p.addNode(.{
3248 .tag = .switch_range,
3249 .main_token = token,
3250 .data = .{
3251 .lhs = expr,
3252 .rhs = try p.expectExpr(),
3253 },
3254 });
3255 }
3256 return expr;
3257 }
3258
3259 const PtrModifiers = struct {
3260 align_node: Node.Index,
3261 addrspace_node: Node.Index,
3262 bit_range_start: Node.Index,
3263 bit_range_end: Node.Index,
3264 };
3265
3266 fn parsePtrModifiers(p: *Parser) !PtrModifiers {
3267 var result: PtrModifiers = .{
3268 .align_node = 0,
3269 .addrspace_node = 0,
3270 .bit_range_start = 0,
3271 .bit_range_end = 0,
3272 };
3273 var saw_const = false;
3274 var saw_volatile = false;
3275 var saw_allowzero = false;
3276 var saw_addrspace = false;
3277 while (true) {
3278 switch (p.token_tags[p.tok_i]) {
3279 .keyword_align => {
3280 if (result.align_node != 0) {
3281 try p.warn(.extra_align_qualifier);
3282 }
3283 p.tok_i += 1;
3284 _ = try p.expectToken(.l_paren);
3285 result.align_node = try p.expectExpr();
3286
3287 if (p.eatToken(.colon)) |_| {
3288 result.bit_range_start = try p.expectExpr();
3289 _ = try p.expectToken(.colon);
3290 result.bit_range_end = try p.expectExpr();
3291 }
3292
3293 _ = try p.expectToken(.r_paren);
3294 },
3295 .keyword_const => {
3296 if (saw_const) {
3297 try p.warn(.extra_const_qualifier);
3298 }
3299 p.tok_i += 1;
3300 saw_const = true;
3301 },
3302 .keyword_volatile => {
3303 if (saw_volatile) {
3304 try p.warn(.extra_volatile_qualifier);
3305 }
3306 p.tok_i += 1;
3307 saw_volatile = true;
3308 },
3309 .keyword_allowzero => {
3310 if (saw_allowzero) {
3311 try p.warn(.extra_allowzero_qualifier);
3312 }
3313 p.tok_i += 1;
3314 saw_allowzero = true;
3315 },
3316 .keyword_addrspace => {
3317 if (saw_addrspace) {
3318 try p.warn(.extra_addrspace_qualifier);
3319 }
3320 result.addrspace_node = try p.parseAddrSpace();
3321 },
3322 else => return result,
3323 }
3324 }
3325 }
3326
3327 /// SuffixOp
3328 /// <- LBRACKET Expr (DOT2 (Expr? (COLON Expr)?)?)? RBRACKET
3329 /// / DOT IDENTIFIER
3330 /// / DOTASTERISK
3331 /// / DOTQUESTIONMARK
3332 fn parseSuffixOp(p: *Parser, lhs: Node.Index) !Node.Index {
3333 switch (p.token_tags[p.tok_i]) {
3334 .l_bracket => {
3335 const lbracket = p.nextToken();
3336 const index_expr = try p.expectExpr();
3337
3338 if (p.eatToken(.ellipsis2)) |_| {
3339 const end_expr = try p.parseExpr();
3340 if (p.eatToken(.colon)) |_| {
3341 const sentinel = try p.expectExpr();
3342 _ = try p.expectToken(.r_bracket);
3343 return p.addNode(.{
3344 .tag = .slice_sentinel,
3345 .main_token = lbracket,
3346 .data = .{
3347 .lhs = lhs,
3348 .rhs = try p.addExtra(Node.SliceSentinel{
3349 .start = index_expr,
3350 .end = end_expr,
3351 .sentinel = sentinel,
3352 }),
3353 },
3354 });
3355 }
3356 _ = try p.expectToken(.r_bracket);
3357 if (end_expr == 0) {
3358 return p.addNode(.{
3359 .tag = .slice_open,
3360 .main_token = lbracket,
3361 .data = .{
3362 .lhs = lhs,
3363 .rhs = index_expr,
3364 },
3365 });
3366 }
3367 return p.addNode(.{
3368 .tag = .slice,
3369 .main_token = lbracket,
3370 .data = .{
3371 .lhs = lhs,
3372 .rhs = try p.addExtra(Node.Slice{
3373 .start = index_expr,
3374 .end = end_expr,
3375 }),
3376 },
3377 });
3378 }
3379 _ = try p.expectToken(.r_bracket);
3380 return p.addNode(.{
3381 .tag = .array_access,
3382 .main_token = lbracket,
3383 .data = .{
3384 .lhs = lhs,
3385 .rhs = index_expr,
3386 },
3387 });
3388 },
3389 .period_asterisk => return p.addNode(.{
3390 .tag = .deref,
3391 .main_token = p.nextToken(),
3392 .data = .{
3393 .lhs = lhs,
3394 .rhs = undefined,
3395 },
3396 }),
3397 .invalid_periodasterisks => {
3398 try p.warn(.asterisk_after_ptr_deref);
3399 return p.addNode(.{
3400 .tag = .deref,
3401 .main_token = p.nextToken(),
3402 .data = .{
3403 .lhs = lhs,
3404 .rhs = undefined,
3405 },
3406 });
3407 },
3408 .period => switch (p.token_tags[p.tok_i + 1]) {
3409 .identifier => return p.addNode(.{
3410 .tag = .field_access,
3411 .main_token = p.nextToken(),
3412 .data = .{
3413 .lhs = lhs,
3414 .rhs = p.nextToken(),
3415 },
3416 }),
3417 .question_mark => return p.addNode(.{
3418 .tag = .unwrap_optional,
3419 .main_token = p.nextToken(),
3420 .data = .{
3421 .lhs = lhs,
3422 .rhs = p.nextToken(),
3423 },
3424 }),
3425 .l_brace => {
3426 // this a misplaced `.{`, handle the error somewhere else
3427 return null_node;
3428 },
3429 else => {
3430 p.tok_i += 1;
3431 try p.warn(.expected_suffix_op);
3432 return null_node;
3433 },
3434 },
3435 else => return null_node,
3436 }
3437 }
3438
3439 /// Caller must have already verified the first token.
3440 ///
3441 /// ContainerDeclAuto <- ContainerDeclType LBRACE container_doc_comment? ContainerMembers RBRACE
3442 ///
3443 /// ContainerDeclType
3444 /// <- KEYWORD_struct (LPAREN Expr RPAREN)?
3445 /// / KEYWORD_opaque
3446 /// / KEYWORD_enum (LPAREN Expr RPAREN)?
3447 /// / KEYWORD_union (LPAREN (KEYWORD_enum (LPAREN Expr RPAREN)? / Expr) RPAREN)?
3448 fn parseContainerDeclAuto(p: *Parser) !Node.Index {
3449 const main_token = p.nextToken();
3450 const arg_expr = switch (p.token_tags[main_token]) {
3451 .keyword_opaque => null_node,
3452 .keyword_struct, .keyword_enum => blk: {
3453 if (p.eatToken(.l_paren)) |_| {
3454 const expr = try p.expectExpr();
3455 _ = try p.expectToken(.r_paren);
3456 break :blk expr;
3457 } else {
3458 break :blk null_node;
3459 }
3460 },
3461 .keyword_union => blk: {
3462 if (p.eatToken(.l_paren)) |_| {
3463 if (p.eatToken(.keyword_enum)) |_| {
3464 if (p.eatToken(.l_paren)) |_| {
3465 const enum_tag_expr = try p.expectExpr();
3466 _ = try p.expectToken(.r_paren);
3467 _ = try p.expectToken(.r_paren);
3468
3469 _ = try p.expectToken(.l_brace);
3470 const members = try p.parseContainerMembers();
3471 const members_span = try members.toSpan(p);
3472 _ = try p.expectToken(.r_brace);
3473 return p.addNode(.{
3474 .tag = switch (members.trailing) {
3475 true => .tagged_union_enum_tag_trailing,
3476 false => .tagged_union_enum_tag,
3477 },
3478 .main_token = main_token,
3479 .data = .{
3480 .lhs = enum_tag_expr,
3481 .rhs = try p.addExtra(members_span),
3482 },
3483 });
3484 } else {
3485 _ = try p.expectToken(.r_paren);
3486
3487 _ = try p.expectToken(.l_brace);
3488 const members = try p.parseContainerMembers();
3489 _ = try p.expectToken(.r_brace);
3490 if (members.len <= 2) {
3491 return p.addNode(.{
3492 .tag = switch (members.trailing) {
3493 true => .tagged_union_two_trailing,
3494 false => .tagged_union_two,
3495 },
3496 .main_token = main_token,
3497 .data = .{
3498 .lhs = members.lhs,
3499 .rhs = members.rhs,
3500 },
3501 });
3502 } else {
3503 const span = try members.toSpan(p);
3504 return p.addNode(.{
3505 .tag = switch (members.trailing) {
3506 true => .tagged_union_trailing,
3507 false => .tagged_union,
3508 },
3509 .main_token = main_token,
3510 .data = .{
3511 .lhs = span.start,
3512 .rhs = span.end,
3513 },
3514 });
3515 }
3516 }
3517 } else {
3518 const expr = try p.expectExpr();
3519 _ = try p.expectToken(.r_paren);
3520 break :blk expr;
3521 }
3522 } else {
3523 break :blk null_node;
3524 }
3525 },
3526 else => {
3527 p.tok_i -= 1;
3528 return p.fail(.expected_container);
3529 },
3530 };
3531 _ = try p.expectToken(.l_brace);
3532 const members = try p.parseContainerMembers();
3533 _ = try p.expectToken(.r_brace);
3534 if (arg_expr == 0) {
3535 if (members.len <= 2) {
3536 return p.addNode(.{
3537 .tag = switch (members.trailing) {
3538 true => .container_decl_two_trailing,
3539 false => .container_decl_two,
3540 },
3541 .main_token = main_token,
3542 .data = .{
3543 .lhs = members.lhs,
3544 .rhs = members.rhs,
3545 },
3546 });
3547 } else {
3548 const span = try members.toSpan(p);
3549 return p.addNode(.{
3550 .tag = switch (members.trailing) {
3551 true => .container_decl_trailing,
3552 false => .container_decl,
3553 },
3554 .main_token = main_token,
3555 .data = .{
3556 .lhs = span.start,
3557 .rhs = span.end,
3558 },
3559 });
3560 }
3561 } else {
3562 const span = try members.toSpan(p);
3563 return p.addNode(.{
3564 .tag = switch (members.trailing) {
3565 true => .container_decl_arg_trailing,
3566 false => .container_decl_arg,
3567 },
3568 .main_token = main_token,
3569 .data = .{
3570 .lhs = arg_expr,
3571 .rhs = try p.addExtra(Node.SubRange{
3572 .start = span.start,
3573 .end = span.end,
3574 }),
3575 },
3576 });
3577 }
3578 }
3579
3580 /// Give a helpful error message for those transitioning from
3581 /// C's 'struct Foo {};' to Zig's 'const Foo = struct {};'.
3582 fn parseCStyleContainer(p: *Parser) Error!bool {
3583 const main_token = p.tok_i;
3584 switch (p.token_tags[p.tok_i]) {
3585 .keyword_enum, .keyword_union, .keyword_struct => {},
3586 else => return false,
3587 }
3588 const identifier = p.tok_i + 1;
3589 if (p.token_tags[identifier] != .identifier) return false;
3590 p.tok_i += 2;
3591
3592 try p.warnMsg(.{
3593 .tag = .c_style_container,
3594 .token = identifier,
3595 .extra = .{ .expected_tag = p.token_tags[main_token] },
3596 });
3597 try p.warnMsg(.{
3598 .tag = .zig_style_container,
3599 .is_note = true,
3600 .token = identifier,
3601 .extra = .{ .expected_tag = p.token_tags[main_token] },
3602 });
3603
3604 _ = try p.expectToken(.l_brace);
3605 _ = try p.parseContainerMembers();
3606 _ = try p.expectToken(.r_brace);
3607 try p.expectSemicolon(.expected_semi_after_decl, true);
3608 return true;
3609 }
3610
3611 /// Holds temporary data until we are ready to construct the full ContainerDecl AST node.
3612 ///
3613 /// ByteAlign <- KEYWORD_align LPAREN Expr RPAREN
3614 fn parseByteAlign(p: *Parser) !Node.Index {
3615 _ = p.eatToken(.keyword_align) orelse return null_node;
3616 _ = try p.expectToken(.l_paren);
3617 const expr = try p.expectExpr();
3618 _ = try p.expectToken(.r_paren);
3619 return expr;
3620 }
3621
3622 /// SwitchProngList <- (SwitchProng COMMA)* SwitchProng?
3623 fn parseSwitchProngList(p: *Parser) !Node.SubRange {
3624 const scratch_top = p.scratch.items.len;
3625 defer p.scratch.shrinkRetainingCapacity(scratch_top);
3626
3627 while (true) {
3628 const item = try parseSwitchProng(p);
3629 if (item == 0) break;
3630
3631 try p.scratch.append(p.gpa, item);
3632
3633 switch (p.token_tags[p.tok_i]) {
3634 .comma => p.tok_i += 1,
3635 // All possible delimiters.
3636 .colon, .r_paren, .r_brace, .r_bracket => break,
3637 // Likely just a missing comma; give error but continue parsing.
3638 else => try p.warn(.expected_comma_after_switch_prong),
3639 }
3640 }
3641 return p.listToSpan(p.scratch.items[scratch_top..]);
3642 }
3643
3644 /// ParamDeclList <- (ParamDecl COMMA)* ParamDecl?
3645 fn parseParamDeclList(p: *Parser) !SmallSpan {
3646 _ = try p.expectToken(.l_paren);
3647 const scratch_top = p.scratch.items.len;
3648 defer p.scratch.shrinkRetainingCapacity(scratch_top);
3649 var varargs: union(enum) { none, seen, nonfinal: TokenIndex } = .none;
3650 while (true) {
3651 if (p.eatToken(.r_paren)) |_| break;
3652 if (varargs == .seen) varargs = .{ .nonfinal = p.tok_i };
3653 const param = try p.expectParamDecl();
3654 if (param != 0) {
3655 try p.scratch.append(p.gpa, param);
3656 } else if (p.token_tags[p.tok_i - 1] == .ellipsis3) {
3657 if (varargs == .none) varargs = .seen;
3658 }
3659 switch (p.token_tags[p.tok_i]) {
3660 .comma => p.tok_i += 1,
3661 .r_paren => {
3662 p.tok_i += 1;
3663 break;
3664 },
3665 .colon, .r_brace, .r_bracket => return p.failExpected(.r_paren),
3666 // Likely just a missing comma; give error but continue parsing.
3667 else => try p.warn(.expected_comma_after_param),
3668 }
3669 }
3670 if (varargs == .nonfinal) {
3671 try p.warnMsg(.{ .tag = .varargs_nonfinal, .token = varargs.nonfinal });
3672 }
3673 const params = p.scratch.items[scratch_top..];
3674 return switch (params.len) {
3675 0 => SmallSpan{ .zero_or_one = 0 },
3676 1 => SmallSpan{ .zero_or_one = params[0] },
3677 else => SmallSpan{ .multi = try p.listToSpan(params) },
3678 };
3679 }
3680
3681 /// FnCallArguments <- LPAREN ExprList RPAREN
3682 ///
3683 /// ExprList <- (Expr COMMA)* Expr?
3684 fn parseBuiltinCall(p: *Parser) !Node.Index {
3685 const builtin_token = p.assertToken(.builtin);
3686 if (p.token_tags[p.nextToken()] != .l_paren) {
3687 p.tok_i -= 1;
3688 try p.warn(.expected_param_list);
3689 // Pretend this was an identifier so we can continue parsing.
3690 return p.addNode(.{
3691 .tag = .identifier,
3692 .main_token = builtin_token,
3693 .data = .{
3694 .lhs = undefined,
3695 .rhs = undefined,
3696 },
3697 });
3698 }
3699 const scratch_top = p.scratch.items.len;
3700 defer p.scratch.shrinkRetainingCapacity(scratch_top);
3701 while (true) {
3702 if (p.eatToken(.r_paren)) |_| break;
3703 const param = try p.expectExpr();
3704 try p.scratch.append(p.gpa, param);
3705 switch (p.token_tags[p.tok_i]) {
3706 .comma => p.tok_i += 1,
3707 .r_paren => {
3708 p.tok_i += 1;
3709 break;
3710 },
3711 // Likely just a missing comma; give error but continue parsing.
3712 else => try p.warn(.expected_comma_after_arg),
3713 }
3714 }
3715 const comma = (p.token_tags[p.tok_i - 2] == .comma);
3716 const params = p.scratch.items[scratch_top..];
3717 switch (params.len) {
3718 0 => return p.addNode(.{
3719 .tag = .builtin_call_two,
3720 .main_token = builtin_token,
3721 .data = .{
3722 .lhs = 0,
3723 .rhs = 0,
3724 },
3725 }),
3726 1 => return p.addNode(.{
3727 .tag = if (comma) .builtin_call_two_comma else .builtin_call_two,
3728 .main_token = builtin_token,
3729 .data = .{
3730 .lhs = params[0],
3731 .rhs = 0,
3732 },
3733 }),
3734 2 => return p.addNode(.{
3735 .tag = if (comma) .builtin_call_two_comma else .builtin_call_two,
3736 .main_token = builtin_token,
3737 .data = .{
3738 .lhs = params[0],
3739 .rhs = params[1],
3740 },
3741 }),
3742 else => {
3743 const span = try p.listToSpan(params);
3744 return p.addNode(.{
3745 .tag = if (comma) .builtin_call_comma else .builtin_call,
3746 .main_token = builtin_token,
3747 .data = .{
3748 .lhs = span.start,
3749 .rhs = span.end,
3750 },
3751 });
3752 },
3753 }
3754 }
3755
3756 /// IfPrefix <- KEYWORD_if LPAREN Expr RPAREN PtrPayload?
3757 fn parseIf(p: *Parser, comptime bodyParseFn: fn (p: *Parser) Error!Node.Index) !Node.Index {
3758 const if_token = p.eatToken(.keyword_if) orelse return null_node;
3759 _ = try p.expectToken(.l_paren);
3760 const condition = try p.expectExpr();
3761 _ = try p.expectToken(.r_paren);
3762 _ = try p.parsePtrPayload();
3763
3764 const then_expr = try bodyParseFn(p);
3765 assert(then_expr != 0);
3766
3767 _ = p.eatToken(.keyword_else) orelse return p.addNode(.{
3768 .tag = .if_simple,
3769 .main_token = if_token,
3770 .data = .{
3771 .lhs = condition,
3772 .rhs = then_expr,
3773 },
3774 });
3775 _ = try p.parsePayload();
3776 const else_expr = try bodyParseFn(p);
3777 assert(then_expr != 0);
3778
3779 return p.addNode(.{
3780 .tag = .@"if",
3781 .main_token = if_token,
3782 .data = .{
3783 .lhs = condition,
3784 .rhs = try p.addExtra(Node.If{
3785 .then_expr = then_expr,
3786 .else_expr = else_expr,
3787 }),
3788 },
3789 });
3790 }
3791
3792 /// Skips over doc comment tokens. Returns the first one, if any.
3793 fn eatDocComments(p: *Parser) !?TokenIndex {
3794 if (p.eatToken(.doc_comment)) |tok| {
3795 var first_line = tok;
3796 if (tok > 0 and tokensOnSameLine(p, tok - 1, tok)) {
3797 try p.warnMsg(.{
3798 .tag = .same_line_doc_comment,
3799 .token = tok,
3800 });
3801 first_line = p.eatToken(.doc_comment) orelse return null;
3802 }
3803 while (p.eatToken(.doc_comment)) |_| {}
3804 return first_line;
3805 }
3806 return null;
3807 }
3808
3809 fn tokensOnSameLine(p: *Parser, token1: TokenIndex, token2: TokenIndex) bool {
3810 return std.mem.indexOfScalar(u8, p.source[p.token_starts[token1]..p.token_starts[token2]], '\n') == null;
3811 }
3812
3813 fn eatToken(p: *Parser, tag: Token.Tag) ?TokenIndex {
3814 return if (p.token_tags[p.tok_i] == tag) p.nextToken() else null;
3815 }
3816
3817 fn assertToken(p: *Parser, tag: Token.Tag) TokenIndex {
3818 const token = p.nextToken();
3819 assert(p.token_tags[token] == tag);
3820 return token;
3821 }
3822
3823 fn expectToken(p: *Parser, tag: Token.Tag) Error!TokenIndex {
3824 if (p.token_tags[p.tok_i] != tag) {
3825 return p.failMsg(.{
3826 .tag = .expected_token,
3827 .token = p.tok_i,
3828 .extra = .{ .expected_tag = tag },
3829 });
3830 }
3831 return p.nextToken();
3832 }
3833
3834 fn expectSemicolon(p: *Parser, error_tag: AstError.Tag, recoverable: bool) Error!void {
3835 if (p.token_tags[p.tok_i] == .semicolon) {
3836 _ = p.nextToken();
3837 return;
3838 }
3839 try p.warn(error_tag);
3840 if (!recoverable) return error.ParseError;
3841 }
3842
3843 fn nextToken(p: *Parser) TokenIndex {
3844 const result = p.tok_i;
3845 p.tok_i += 1;
3846 return result;
3847 }
3848};
3849
3850test {
3851 _ = @import("parser_test.zig");
3852}
lib/std/zig/parser_test.zig+11-2
...@@ -186,6 +186,15 @@ test "zig fmt: file ends in comment" {...@@ -186,6 +186,15 @@ test "zig fmt: file ends in comment" {
186 );186 );
187}187}
188188
189test "zig fmt: file ends in multi line comment" {
190 try testTransform(
191 \\ \\foobar
192 ,
193 \\\\foobar
194 \\
195 );
196}
197
189test "zig fmt: file ends in comment after var decl" {198test "zig fmt: file ends in comment after var decl" {
190 try testTransform(199 try testTransform(
191 \\const x = 42;200 \\const x = 42;
...@@ -6064,7 +6073,7 @@ var fixed_buffer_mem: [100 * 1024]u8 = undefined;...@@ -6064,7 +6073,7 @@ var fixed_buffer_mem: [100 * 1024]u8 = undefined;
6064fn testParse(source: [:0]const u8, allocator: mem.Allocator, anything_changed: *bool) ![]u8 {6073fn testParse(source: [:0]const u8, allocator: mem.Allocator, anything_changed: *bool) ![]u8 {
6065 const stderr = io.getStdErr().writer();6074 const stderr = io.getStdErr().writer();
60666075
6067 var tree = try std.zig.parse(allocator, source);6076 var tree = try std.zig.Ast.parse(allocator, source, .zig);
6068 defer tree.deinit(allocator);6077 defer tree.deinit(allocator);
60696078
6070 for (tree.errors) |parse_error| {6079 for (tree.errors) |parse_error| {
...@@ -6115,7 +6124,7 @@ fn testCanonical(source: [:0]const u8) !void {...@@ -6115,7 +6124,7 @@ fn testCanonical(source: [:0]const u8) !void {
6115const Error = std.zig.Ast.Error.Tag;6124const Error = std.zig.Ast.Error.Tag;
61166125
6117fn testError(source: [:0]const u8, expected_errors: []const Error) !void {6126fn testError(source: [:0]const u8, expected_errors: []const Error) !void {
6118 var tree = try std.zig.parse(std.testing.allocator, source);6127 var tree = try std.zig.Ast.parse(std.testing.allocator, source, .zig);
6119 defer tree.deinit(std.testing.allocator);6128 defer tree.deinit(std.testing.allocator);
61206129
6121 std.testing.expectEqual(expected_errors.len, tree.errors.len) catch |err| {6130 std.testing.expectEqual(expected_errors.len, tree.errors.len) catch |err| {
lib/std/zig/perf_test.zig+1-2
...@@ -1,7 +1,6 @@...@@ -1,7 +1,6 @@
1const std = @import("std");1const std = @import("std");
2const mem = std.mem;2const mem = std.mem;
3const Tokenizer = std.zig.Tokenizer;3const Tokenizer = std.zig.Tokenizer;
4const Parser = std.zig.Parser;
5const io = std.io;4const io = std.io;
6const fmtIntSizeBin = std.fmt.fmtIntSizeBin;5const fmtIntSizeBin = std.fmt.fmtIntSizeBin;
76
...@@ -34,6 +33,6 @@ pub fn main() !void {...@@ -34,6 +33,6 @@ pub fn main() !void {
34fn testOnce() usize {33fn testOnce() usize {
35 var fixed_buf_alloc = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]);34 var fixed_buf_alloc = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
36 var allocator = fixed_buf_alloc.allocator();35 var allocator = fixed_buf_alloc.allocator();
37 _ = std.zig.parse(allocator, source) catch @panic("parse failure");36 _ = std.zig.Ast.parse(allocator, source, .zig) catch @panic("parse failure");
38 return fixed_buf_alloc.end_index;37 return fixed_buf_alloc.end_index;
39}38}
lib/std/zig/render.zig+1-2
...@@ -2759,8 +2759,7 @@ fn tokenSliceForRender(tree: Ast, token_index: Ast.TokenIndex) []const u8 {...@@ -2759,8 +2759,7 @@ fn tokenSliceForRender(tree: Ast, token_index: Ast.TokenIndex) []const u8 {
2759 var ret = tree.tokenSlice(token_index);2759 var ret = tree.tokenSlice(token_index);
2760 switch (tree.tokens.items(.tag)[token_index]) {2760 switch (tree.tokens.items(.tag)[token_index]) {
2761 .multiline_string_literal_line => {2761 .multiline_string_literal_line => {
2762 assert(ret[ret.len - 1] == '\n');2762 if (ret[ret.len - 1] == '\n') ret.len -= 1;
2763 ret.len -= 1;
2764 },2763 },
2765 .container_doc_comment, .doc_comment => {2764 .container_doc_comment, .doc_comment => {
2766 ret = mem.trimRight(u8, ret, &std.ascii.whitespace);2765 ret = mem.trimRight(u8, ret, &std.ascii.whitespace);
lib/test_runner.zig+1-1
...@@ -11,7 +11,7 @@ var log_err_count: usize = 0;...@@ -11,7 +11,7 @@ var log_err_count: usize = 0;
1111
12pub fn main() void {12pub fn main() void {
13 if (builtin.zig_backend != .stage1 and13 if (builtin.zig_backend != .stage1 and
14 (builtin.zig_backend != .stage2_llvm or builtin.cpu.arch == .wasm32) and14 builtin.zig_backend != .stage2_llvm and
15 builtin.zig_backend != .stage2_c)15 builtin.zig_backend != .stage2_c)
16 {16 {
17 return main2() catch @panic("test failure");17 return main2() catch @panic("test failure");
lib/zig.h+24
...@@ -93,6 +93,14 @@ typedef char bool;...@@ -93,6 +93,14 @@ typedef char bool;
93#define zig_align zig_align_unavailable93#define zig_align zig_align_unavailable
94#endif94#endif
9595
96#if zig_has_attribute(aligned)
97#define zig_under_align(alignment) __attribute__((aligned(alignment)))
98#elif _MSC_VER
99#define zig_under_align(alignment) zig_align(alignment)
100#else
101#define zig_align zig_align_unavailable
102#endif
103
96#if zig_has_attribute(aligned)104#if zig_has_attribute(aligned)
97#define zig_align_fn(alignment) __attribute__((aligned(alignment)))105#define zig_align_fn(alignment) __attribute__((aligned(alignment)))
98#elif _MSC_VER106#elif _MSC_VER
...@@ -101,6 +109,22 @@ typedef char bool;...@@ -101,6 +109,22 @@ typedef char bool;
101#define zig_align_fn zig_align_fn_unavailable109#define zig_align_fn zig_align_fn_unavailable
102#endif110#endif
103111
112#if zig_has_attribute(packed)
113#define zig_packed(definition) __attribute__((packed)) definition
114#elif _MSC_VER
115#define zig_packed(definition) __pragma(pack(1)) definition __pragma(pack())
116#else
117#define zig_packed(definition) zig_packed_unavailable
118#endif
119
120#if zig_has_attribute(section)
121#define zig_linksection(name, def, ...) def __attribute__((section(name)))
122#elif _MSC_VER
123#define zig_linksection(name, def, ...) __pragma(section(name, __VA_ARGS__)) __declspec(allocate(name)) def
124#else
125#define zig_linksection(name, def, ...) zig_linksection_unavailable
126#endif
127
104#if zig_has_builtin(unreachable) || defined(zig_gnuc)128#if zig_has_builtin(unreachable) || defined(zig_gnuc)
105#define zig_unreachable() __builtin_unreachable()129#define zig_unreachable() __builtin_unreachable()
106#else130#else
src/AstGen.zig+37-2
...@@ -2530,6 +2530,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As...@@ -2530,6 +2530,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
2530 .bit_size_of,2530 .bit_size_of,
2531 .typeof_log2_int_type,2531 .typeof_log2_int_type,
2532 .ptr_to_int,2532 .ptr_to_int,
2533 .qual_cast,
2533 .align_of,2534 .align_of,
2534 .bool_to_int,2535 .bool_to_int,
2535 .embed_file,2536 .embed_file,
...@@ -4278,7 +4279,34 @@ fn testDecl(...@@ -4278,7 +4279,34 @@ fn testDecl(
4278 var num_namespaces_out: u32 = 0;4279 var num_namespaces_out: u32 = 0;
4279 var capturing_namespace: ?*Scope.Namespace = null;4280 var capturing_namespace: ?*Scope.Namespace = null;
4280 while (true) switch (s.tag) {4281 while (true) switch (s.tag) {
4281 .local_val, .local_ptr => unreachable, // a test cannot be in a local scope4282 .local_val => {
4283 const local_val = s.cast(Scope.LocalVal).?;
4284 if (local_val.name == name_str_index) {
4285 local_val.used = test_name_token;
4286 return astgen.failTokNotes(test_name_token, "cannot test a {s}", .{
4287 @tagName(local_val.id_cat),
4288 }, &[_]u32{
4289 try astgen.errNoteTok(local_val.token_src, "{s} declared here", .{
4290 @tagName(local_val.id_cat),
4291 }),
4292 });
4293 }
4294 s = local_val.parent;
4295 },
4296 .local_ptr => {
4297 const local_ptr = s.cast(Scope.LocalPtr).?;
4298 if (local_ptr.name == name_str_index) {
4299 local_ptr.used = test_name_token;
4300 return astgen.failTokNotes(test_name_token, "cannot test a {s}", .{
4301 @tagName(local_ptr.id_cat),
4302 }, &[_]u32{
4303 try astgen.errNoteTok(local_ptr.token_src, "{s} declared here", .{
4304 @tagName(local_ptr.id_cat),
4305 }),
4306 });
4307 }
4308 s = local_ptr.parent;
4309 },
4282 .gen_zir => s = s.cast(GenZir).?.parent,4310 .gen_zir => s = s.cast(GenZir).?.parent,
4283 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,4311 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
4284 .namespace, .enum_namespace => {4312 .namespace, .enum_namespace => {
...@@ -8010,6 +8038,7 @@ fn builtinCall(...@@ -8010,6 +8038,7 @@ fn builtinCall(
8010 .float_cast => return typeCast(gz, scope, ri, node, params[0], params[1], .float_cast),8038 .float_cast => return typeCast(gz, scope, ri, node, params[0], params[1], .float_cast),
8011 .int_cast => return typeCast(gz, scope, ri, node, params[0], params[1], .int_cast),8039 .int_cast => return typeCast(gz, scope, ri, node, params[0], params[1], .int_cast),
8012 .ptr_cast => return typeCast(gz, scope, ri, node, params[0], params[1], .ptr_cast),8040 .ptr_cast => return typeCast(gz, scope, ri, node, params[0], params[1], .ptr_cast),
8041 .qual_cast => return typeCast(gz, scope, ri, node, params[0], params[1], .qual_cast),
8013 .truncate => return typeCast(gz, scope, ri, node, params[0], params[1], .truncate),8042 .truncate => return typeCast(gz, scope, ri, node, params[0], params[1], .truncate),
8014 // zig fmt: on8043 // zig fmt: on
80158044
...@@ -8692,6 +8721,7 @@ fn callExpr(...@@ -8692,6 +8721,7 @@ fn callExpr(
8692 defer arg_block.unstack();8721 defer arg_block.unstack();
86938722
8694 // `call_inst` is reused to provide the param type.8723 // `call_inst` is reused to provide the param type.
8724 arg_block.rl_ty_inst = call_inst;
8695 const arg_ref = try expr(&arg_block, &arg_block.base, .{ .rl = .{ .coerced_ty = call_inst }, .ctx = .fn_arg }, param_node);8725 const arg_ref = try expr(&arg_block, &arg_block.base, .{ .rl = .{ .coerced_ty = call_inst }, .ctx = .fn_arg }, param_node);
8696 _ = try arg_block.addBreak(.break_inline, call_index, arg_ref);8726 _ = try arg_block.addBreak(.break_inline, call_index, arg_ref);
86978727
...@@ -10840,7 +10870,12 @@ const GenZir = struct {...@@ -10840,7 +10870,12 @@ const GenZir = struct {
10840 // we emit ZIR for the block break instructions to have the result values,10870 // we emit ZIR for the block break instructions to have the result values,
10841 // and then rvalue() on that to pass the value to the result location.10871 // and then rvalue() on that to pass the value to the result location.
10842 switch (parent_ri.rl) {10872 switch (parent_ri.rl) {
10843 .ty, .coerced_ty => |ty_inst| {10873 .coerced_ty => |ty_inst| {
10874 // Type coercion needs to happend before breaks.
10875 gz.rl_ty_inst = ty_inst;
10876 gz.break_result_info = .{ .rl = .{ .ty = ty_inst } };
10877 },
10878 .ty => |ty_inst| {
10844 gz.rl_ty_inst = ty_inst;10879 gz.rl_ty_inst = ty_inst;
10845 gz.break_result_info = parent_ri;10880 gz.break_result_info = parent_ri;
10846 },10881 },
src/Autodoc.zig+6-11
...@@ -1400,6 +1400,7 @@ fn walkInstruction(...@@ -1400,6 +1400,7 @@ fn walkInstruction(
1400 .float_cast,1400 .float_cast,
1401 .int_cast,1401 .int_cast,
1402 .ptr_cast,1402 .ptr_cast,
1403 .qual_cast,
1403 .truncate,1404 .truncate,
1404 .align_cast,1405 .align_cast,
1405 .has_decl,1406 .has_decl,
...@@ -2200,17 +2201,10 @@ fn walkInstruction(...@@ -2200,17 +2201,10 @@ fn walkInstruction(
2200 false,2201 false,
2201 );2202 );
22022203
2203 _ = operand;2204 return DocData.WalkResult{
22042205 .typeRef = operand.expr,
2205 // WIP2206 .expr = .{ .@"struct" = &.{} },
22062207 };
2207 printWithContext(
2208 file,
2209 inst_index,
2210 "TODO: implement `{s}` for walkInstruction\n\n",
2211 .{@tagName(tags[inst_index])},
2212 );
2213 return self.cteTodo(@tagName(tags[inst_index]));
2214 },2208 },
2215 .struct_init_anon => {2209 .struct_init_anon => {
2216 const pl_node = data[inst_index].pl_node;2210 const pl_node = data[inst_index].pl_node;
...@@ -2537,6 +2531,7 @@ fn walkInstruction(...@@ -2537,6 +2531,7 @@ fn walkInstruction(
2537 const var_init_ref = @intToEnum(Ref, file.zir.extra[extra_index]);2531 const var_init_ref = @intToEnum(Ref, file.zir.extra[extra_index]);
2538 const var_init = try self.walkRef(file, parent_scope, parent_src, var_init_ref, need_type);2532 const var_init = try self.walkRef(file, parent_scope, parent_src, var_init_ref, need_type);
2539 value.expr = var_init.expr;2533 value.expr = var_init.expr;
2534 value.typeRef = var_init.typeRef;
2540 }2535 }
25412536
2542 return value;2537 return value;
src/BuiltinFn.zig+8
...@@ -75,6 +75,7 @@ pub const Tag = enum {...@@ -75,6 +75,7 @@ pub const Tag = enum {
75 prefetch,75 prefetch,
76 ptr_cast,76 ptr_cast,
77 ptr_to_int,77 ptr_to_int,
78 qual_cast,
78 rem,79 rem,
79 return_address,80 return_address,
80 select,81 select,
...@@ -674,6 +675,13 @@ pub const list = list: {...@@ -674,6 +675,13 @@ pub const list = list: {
674 .param_count = 1,675 .param_count = 1,
675 },676 },
676 },677 },
678 .{
679 "@qualCast",
680 .{
681 .tag = .qual_cast,
682 .param_count = 2,
683 },
684 },
677 .{685 .{
678 "@rem",686 "@rem",
679 .{687 .{
src/Compilation.zig+2-2
...@@ -385,7 +385,7 @@ pub const AllErrors = struct {...@@ -385,7 +385,7 @@ pub const AllErrors = struct {
385 count: u32 = 1,385 count: u32 = 1,
386 /// Does not include the trailing newline.386 /// Does not include the trailing newline.
387 source_line: ?[]const u8,387 source_line: ?[]const u8,
388 notes: []Message = &.{},388 notes: []const Message = &.{},
389 reference_trace: []Message = &.{},389 reference_trace: []Message = &.{},
390390
391 /// Splits the error message up into lines to properly indent them391 /// Splits the error message up into lines to properly indent them
...@@ -3299,7 +3299,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void {...@@ -3299,7 +3299,7 @@ fn processOneJob(comp: *Compilation, job: Job) !void {
3299 const gpa = comp.gpa;3299 const gpa = comp.gpa;
3300 const module = comp.bin_file.options.module.?;3300 const module = comp.bin_file.options.module.?;
3301 const decl = module.declPtr(decl_index);3301 const decl = module.declPtr(decl_index);
3302 comp.bin_file.updateDeclLineNumber(module, decl) catch |err| {3302 comp.bin_file.updateDeclLineNumber(module, decl_index) catch |err| {
3303 try module.failed_decls.ensureUnusedCapacity(gpa, 1);3303 try module.failed_decls.ensureUnusedCapacity(gpa, 1);
3304 module.failed_decls.putAssumeCapacityNoClobber(decl_index, try Module.ErrorMsg.create(3304 module.failed_decls.putAssumeCapacityNoClobber(decl_index, try Module.ErrorMsg.create(
3305 gpa,3305 gpa,
src/Manifest.zig created+499
...@@ -0,0 +1,499 @@
1pub const basename = "build.zig.zon";
2pub const Hash = std.crypto.hash.sha2.Sha256;
3
4pub const Dependency = struct {
5 url: []const u8,
6 url_tok: Ast.TokenIndex,
7 hash: ?[]const u8,
8 hash_tok: Ast.TokenIndex,
9};
10
11pub const ErrorMessage = struct {
12 msg: []const u8,
13 tok: Ast.TokenIndex,
14 off: u32,
15};
16
17pub const MultihashFunction = enum(u16) {
18 identity = 0x00,
19 sha1 = 0x11,
20 @"sha2-256" = 0x12,
21 @"sha2-512" = 0x13,
22 @"sha3-512" = 0x14,
23 @"sha3-384" = 0x15,
24 @"sha3-256" = 0x16,
25 @"sha3-224" = 0x17,
26 @"sha2-384" = 0x20,
27 @"sha2-256-trunc254-padded" = 0x1012,
28 @"sha2-224" = 0x1013,
29 @"sha2-512-224" = 0x1014,
30 @"sha2-512-256" = 0x1015,
31 @"blake2b-256" = 0xb220,
32 _,
33};
34
35pub const multihash_function: MultihashFunction = switch (Hash) {
36 std.crypto.hash.sha2.Sha256 => .@"sha2-256",
37 else => @compileError("unreachable"),
38};
39comptime {
40 // We avoid unnecessary uleb128 code in hexDigest by asserting here the
41 // values are small enough to be contained in the one-byte encoding.
42 assert(@enumToInt(multihash_function) < 127);
43 assert(Hash.digest_length < 127);
44}
45pub const multihash_len = 1 + 1 + Hash.digest_length;
46
47name: []const u8,
48version: std.SemanticVersion,
49dependencies: std.StringArrayHashMapUnmanaged(Dependency),
50
51errors: []ErrorMessage,
52arena_state: std.heap.ArenaAllocator.State,
53
54pub const Error = Allocator.Error;
55
56pub fn parse(gpa: Allocator, ast: std.zig.Ast) Error!Manifest {
57 const node_tags = ast.nodes.items(.tag);
58 const node_datas = ast.nodes.items(.data);
59 assert(node_tags[0] == .root);
60 const main_node_index = node_datas[0].lhs;
61
62 var arena_instance = std.heap.ArenaAllocator.init(gpa);
63 errdefer arena_instance.deinit();
64
65 var p: Parse = .{
66 .gpa = gpa,
67 .ast = ast,
68 .arena = arena_instance.allocator(),
69 .errors = .{},
70
71 .name = undefined,
72 .version = undefined,
73 .dependencies = .{},
74 .buf = .{},
75 };
76 defer p.buf.deinit(gpa);
77 defer p.errors.deinit(gpa);
78 defer p.dependencies.deinit(gpa);
79
80 p.parseRoot(main_node_index) catch |err| switch (err) {
81 error.ParseFailure => assert(p.errors.items.len > 0),
82 else => |e| return e,
83 };
84
85 return .{
86 .name = p.name,
87 .version = p.version,
88 .dependencies = try p.dependencies.clone(p.arena),
89 .errors = try p.arena.dupe(ErrorMessage, p.errors.items),
90 .arena_state = arena_instance.state,
91 };
92}
93
94pub fn deinit(man: *Manifest, gpa: Allocator) void {
95 man.arena_state.promote(gpa).deinit();
96 man.* = undefined;
97}
98
99const hex_charset = "0123456789abcdef";
100
101pub fn hex64(x: u64) [16]u8 {
102 var result: [16]u8 = undefined;
103 var i: usize = 0;
104 while (i < 8) : (i += 1) {
105 const byte = @truncate(u8, x >> @intCast(u6, 8 * i));
106 result[i * 2 + 0] = hex_charset[byte >> 4];
107 result[i * 2 + 1] = hex_charset[byte & 15];
108 }
109 return result;
110}
111
112test hex64 {
113 const s = "[" ++ hex64(0x12345678_abcdef00) ++ "]";
114 try std.testing.expectEqualStrings("[00efcdab78563412]", s);
115}
116
117pub fn hexDigest(digest: [Hash.digest_length]u8) [multihash_len * 2]u8 {
118 var result: [multihash_len * 2]u8 = undefined;
119
120 result[0] = hex_charset[@enumToInt(multihash_function) >> 4];
121 result[1] = hex_charset[@enumToInt(multihash_function) & 15];
122
123 result[2] = hex_charset[Hash.digest_length >> 4];
124 result[3] = hex_charset[Hash.digest_length & 15];
125
126 for (digest) |byte, i| {
127 result[4 + i * 2] = hex_charset[byte >> 4];
128 result[5 + i * 2] = hex_charset[byte & 15];
129 }
130 return result;
131}
132
133const Parse = struct {
134 gpa: Allocator,
135 ast: std.zig.Ast,
136 arena: Allocator,
137 buf: std.ArrayListUnmanaged(u8),
138 errors: std.ArrayListUnmanaged(ErrorMessage),
139
140 name: []const u8,
141 version: std.SemanticVersion,
142 dependencies: std.StringArrayHashMapUnmanaged(Dependency),
143
144 const InnerError = error{ ParseFailure, OutOfMemory };
145
146 fn parseRoot(p: *Parse, node: Ast.Node.Index) !void {
147 const ast = p.ast;
148 const main_tokens = ast.nodes.items(.main_token);
149 const main_token = main_tokens[node];
150
151 var buf: [2]Ast.Node.Index = undefined;
152 const struct_init = ast.fullStructInit(&buf, node) orelse {
153 return fail(p, main_token, "expected top level expression to be a struct", .{});
154 };
155
156 var have_name = false;
157 var have_version = false;
158
159 for (struct_init.ast.fields) |field_init| {
160 const name_token = ast.firstToken(field_init) - 2;
161 const field_name = try identifierTokenString(p, name_token);
162 // We could get fancy with reflection and comptime logic here but doing
163 // things manually provides an opportunity to do any additional verification
164 // that is desirable on a per-field basis.
165 if (mem.eql(u8, field_name, "dependencies")) {
166 try parseDependencies(p, field_init);
167 } else if (mem.eql(u8, field_name, "name")) {
168 p.name = try parseString(p, field_init);
169 have_name = true;
170 } else if (mem.eql(u8, field_name, "version")) {
171 const version_text = try parseString(p, field_init);
172 p.version = std.SemanticVersion.parse(version_text) catch |err| v: {
173 try appendError(p, main_tokens[field_init], "unable to parse semantic version: {s}", .{@errorName(err)});
174 break :v undefined;
175 };
176 have_version = true;
177 } else {
178 // Ignore unknown fields so that we can add fields in future zig
179 // versions without breaking older zig versions.
180 }
181 }
182
183 if (!have_name) {
184 try appendError(p, main_token, "missing top-level 'name' field", .{});
185 }
186
187 if (!have_version) {
188 try appendError(p, main_token, "missing top-level 'version' field", .{});
189 }
190 }
191
192 fn parseDependencies(p: *Parse, node: Ast.Node.Index) !void {
193 const ast = p.ast;
194 const main_tokens = ast.nodes.items(.main_token);
195
196 var buf: [2]Ast.Node.Index = undefined;
197 const struct_init = ast.fullStructInit(&buf, node) orelse {
198 const tok = main_tokens[node];
199 return fail(p, tok, "expected dependencies expression to be a struct", .{});
200 };
201
202 for (struct_init.ast.fields) |field_init| {
203 const name_token = ast.firstToken(field_init) - 2;
204 const dep_name = try identifierTokenString(p, name_token);
205 const dep = try parseDependency(p, field_init);
206 try p.dependencies.put(p.gpa, dep_name, dep);
207 }
208 }
209
210 fn parseDependency(p: *Parse, node: Ast.Node.Index) !Dependency {
211 const ast = p.ast;
212 const main_tokens = ast.nodes.items(.main_token);
213
214 var buf: [2]Ast.Node.Index = undefined;
215 const struct_init = ast.fullStructInit(&buf, node) orelse {
216 const tok = main_tokens[node];
217 return fail(p, tok, "expected dependency expression to be a struct", .{});
218 };
219
220 var dep: Dependency = .{
221 .url = undefined,
222 .url_tok = undefined,
223 .hash = null,
224 .hash_tok = undefined,
225 };
226 var have_url = false;
227
228 for (struct_init.ast.fields) |field_init| {
229 const name_token = ast.firstToken(field_init) - 2;
230 const field_name = try identifierTokenString(p, name_token);
231 // We could get fancy with reflection and comptime logic here but doing
232 // things manually provides an opportunity to do any additional verification
233 // that is desirable on a per-field basis.
234 if (mem.eql(u8, field_name, "url")) {
235 dep.url = parseString(p, field_init) catch |err| switch (err) {
236 error.ParseFailure => continue,
237 else => |e| return e,
238 };
239 dep.url_tok = main_tokens[field_init];
240 have_url = true;
241 } else if (mem.eql(u8, field_name, "hash")) {
242 dep.hash = parseHash(p, field_init) catch |err| switch (err) {
243 error.ParseFailure => continue,
244 else => |e| return e,
245 };
246 dep.hash_tok = main_tokens[field_init];
247 } else {
248 // Ignore unknown fields so that we can add fields in future zig
249 // versions without breaking older zig versions.
250 }
251 }
252
253 if (!have_url) {
254 try appendError(p, main_tokens[node], "dependency is missing 'url' field", .{});
255 }
256
257 return dep;
258 }
259
260 fn parseString(p: *Parse, node: Ast.Node.Index) ![]const u8 {
261 const ast = p.ast;
262 const node_tags = ast.nodes.items(.tag);
263 const main_tokens = ast.nodes.items(.main_token);
264 if (node_tags[node] != .string_literal) {
265 return fail(p, main_tokens[node], "expected string literal", .{});
266 }
267 const str_lit_token = main_tokens[node];
268 const token_bytes = ast.tokenSlice(str_lit_token);
269 p.buf.clearRetainingCapacity();
270 try parseStrLit(p, str_lit_token, &p.buf, token_bytes, 0);
271 const duped = try p.arena.dupe(u8, p.buf.items);
272 return duped;
273 }
274
275 fn parseHash(p: *Parse, node: Ast.Node.Index) ![]const u8 {
276 const ast = p.ast;
277 const main_tokens = ast.nodes.items(.main_token);
278 const tok = main_tokens[node];
279 const h = try parseString(p, node);
280
281 if (h.len >= 2) {
282 const their_multihash_func = std.fmt.parseInt(u8, h[0..2], 16) catch |err| {
283 return fail(p, tok, "invalid multihash value: unable to parse hash function: {s}", .{
284 @errorName(err),
285 });
286 };
287 if (@intToEnum(MultihashFunction, their_multihash_func) != multihash_function) {
288 return fail(p, tok, "unsupported hash function: only sha2-256 is supported", .{});
289 }
290 }
291
292 const hex_multihash_len = 2 * Manifest.multihash_len;
293 if (h.len != hex_multihash_len) {
294 return fail(p, tok, "wrong hash size. expected: {d}, found: {d}", .{
295 hex_multihash_len, h.len,
296 });
297 }
298
299 return h;
300 }
301
302 /// TODO: try to DRY this with AstGen.identifierTokenString
303 fn identifierTokenString(p: *Parse, token: Ast.TokenIndex) InnerError![]const u8 {
304 const ast = p.ast;
305 const token_tags = ast.tokens.items(.tag);
306 assert(token_tags[token] == .identifier);
307 const ident_name = ast.tokenSlice(token);
308 if (!mem.startsWith(u8, ident_name, "@")) {
309 return ident_name;
310 }
311 p.buf.clearRetainingCapacity();
312 try parseStrLit(p, token, &p.buf, ident_name, 1);
313 const duped = try p.arena.dupe(u8, p.buf.items);
314 return duped;
315 }
316
317 /// TODO: try to DRY this with AstGen.parseStrLit
318 fn parseStrLit(
319 p: *Parse,
320 token: Ast.TokenIndex,
321 buf: *std.ArrayListUnmanaged(u8),
322 bytes: []const u8,
323 offset: u32,
324 ) InnerError!void {
325 const raw_string = bytes[offset..];
326 var buf_managed = buf.toManaged(p.gpa);
327 const result = std.zig.string_literal.parseWrite(buf_managed.writer(), raw_string);
328 buf.* = buf_managed.moveToUnmanaged();
329 switch (try result) {
330 .success => {},
331 .failure => |err| try p.appendStrLitError(err, token, bytes, offset),
332 }
333 }
334
335 /// TODO: try to DRY this with AstGen.failWithStrLitError
336 fn appendStrLitError(
337 p: *Parse,
338 err: std.zig.string_literal.Error,
339 token: Ast.TokenIndex,
340 bytes: []const u8,
341 offset: u32,
342 ) Allocator.Error!void {
343 const raw_string = bytes[offset..];
344 switch (err) {
345 .invalid_escape_character => |bad_index| {
346 try p.appendErrorOff(
347 token,
348 offset + @intCast(u32, bad_index),
349 "invalid escape character: '{c}'",
350 .{raw_string[bad_index]},
351 );
352 },
353 .expected_hex_digit => |bad_index| {
354 try p.appendErrorOff(
355 token,
356 offset + @intCast(u32, bad_index),
357 "expected hex digit, found '{c}'",
358 .{raw_string[bad_index]},
359 );
360 },
361 .empty_unicode_escape_sequence => |bad_index| {
362 try p.appendErrorOff(
363 token,
364 offset + @intCast(u32, bad_index),
365 "empty unicode escape sequence",
366 .{},
367 );
368 },
369 .expected_hex_digit_or_rbrace => |bad_index| {
370 try p.appendErrorOff(
371 token,
372 offset + @intCast(u32, bad_index),
373 "expected hex digit or '}}', found '{c}'",
374 .{raw_string[bad_index]},
375 );
376 },
377 .invalid_unicode_codepoint => |bad_index| {
378 try p.appendErrorOff(
379 token,
380 offset + @intCast(u32, bad_index),
381 "unicode escape does not correspond to a valid codepoint",
382 .{},
383 );
384 },
385 .expected_lbrace => |bad_index| {
386 try p.appendErrorOff(
387 token,
388 offset + @intCast(u32, bad_index),
389 "expected '{{', found '{c}",
390 .{raw_string[bad_index]},
391 );
392 },
393 .expected_rbrace => |bad_index| {
394 try p.appendErrorOff(
395 token,
396 offset + @intCast(u32, bad_index),
397 "expected '}}', found '{c}",
398 .{raw_string[bad_index]},
399 );
400 },
401 .expected_single_quote => |bad_index| {
402 try p.appendErrorOff(
403 token,
404 offset + @intCast(u32, bad_index),
405 "expected single quote ('), found '{c}",
406 .{raw_string[bad_index]},
407 );
408 },
409 .invalid_character => |bad_index| {
410 try p.appendErrorOff(
411 token,
412 offset + @intCast(u32, bad_index),
413 "invalid byte in string or character literal: '{c}'",
414 .{raw_string[bad_index]},
415 );
416 },
417 }
418 }
419
420 fn fail(
421 p: *Parse,
422 tok: Ast.TokenIndex,
423 comptime fmt: []const u8,
424 args: anytype,
425 ) InnerError {
426 try appendError(p, tok, fmt, args);
427 return error.ParseFailure;
428 }
429
430 fn appendError(p: *Parse, tok: Ast.TokenIndex, comptime fmt: []const u8, args: anytype) !void {
431 return appendErrorOff(p, tok, 0, fmt, args);
432 }
433
434 fn appendErrorOff(
435 p: *Parse,
436 tok: Ast.TokenIndex,
437 byte_offset: u32,
438 comptime fmt: []const u8,
439 args: anytype,
440 ) Allocator.Error!void {
441 try p.errors.append(p.gpa, .{
442 .msg = try std.fmt.allocPrint(p.arena, fmt, args),
443 .tok = tok,
444 .off = byte_offset,
445 });
446 }
447};
448
449const Manifest = @This();
450const std = @import("std");
451const mem = std.mem;
452const Allocator = std.mem.Allocator;
453const assert = std.debug.assert;
454const Ast = std.zig.Ast;
455const testing = std.testing;
456
457test "basic" {
458 const gpa = testing.allocator;
459
460 const example =
461 \\.{
462 \\ .name = "foo",
463 \\ .version = "3.2.1",
464 \\ .dependencies = .{
465 \\ .bar = .{
466 \\ .url = "https://example.com/baz.tar.gz",
467 \\ .hash = "1220f1b680b6065fcfc94fe777f22e73bcb7e2767e5f4d99d4255fe76ded69c7a35f",
468 \\ },
469 \\ },
470 \\}
471 ;
472
473 var ast = try std.zig.Ast.parse(gpa, example, .zon);
474 defer ast.deinit(gpa);
475
476 try testing.expect(ast.errors.len == 0);
477
478 var manifest = try Manifest.parse(gpa, ast);
479 defer manifest.deinit(gpa);
480
481 try testing.expectEqualStrings("foo", manifest.name);
482
483 try testing.expectEqual(@as(std.SemanticVersion, .{
484 .major = 3,
485 .minor = 2,
486 .patch = 1,
487 }), manifest.version);
488
489 try testing.expect(manifest.dependencies.count() == 1);
490 try testing.expectEqualStrings("bar", manifest.dependencies.keys()[0]);
491 try testing.expectEqualStrings(
492 "https://example.com/baz.tar.gz",
493 manifest.dependencies.values()[0].url,
494 );
495 try testing.expectEqualStrings(
496 "1220f1b680b6065fcfc94fe777f22e73bcb7e2767e5f4d99d4255fe76ded69c7a35f",
497 manifest.dependencies.values()[0].hash orelse return error.TestFailed,
498 );
499}
src/Module.zig+17-94
...@@ -328,8 +328,6 @@ pub const ErrorInt = u32;...@@ -328,8 +328,6 @@ pub const ErrorInt = u32;
328pub const Export = struct {328pub const Export = struct {
329 options: std.builtin.ExportOptions,329 options: std.builtin.ExportOptions,
330 src: LazySrcLoc,330 src: LazySrcLoc,
331 /// Represents the position of the export, if any, in the output file.
332 link: link.File.Export,
333 /// The Decl that performs the export. Note that this is *not* the Decl being exported.331 /// The Decl that performs the export. Note that this is *not* the Decl being exported.
334 owner_decl: Decl.Index,332 owner_decl: Decl.Index,
335 /// The Decl containing the export statement. Inline function calls333 /// The Decl containing the export statement. Inline function calls
...@@ -533,16 +531,8 @@ pub const Decl = struct {...@@ -533,16 +531,8 @@ pub const Decl = struct {
533 /// What kind of a declaration is this.531 /// What kind of a declaration is this.
534 kind: Kind,532 kind: Kind,
535533
536 /// Represents the position of the code in the output file.534 /// TODO remove this once Wasm backend catches up
537 /// This is populated regardless of semantic analysis and code generation.535 fn_link: ?link.File.Wasm.FnData = null,
538 link: link.File.LinkBlock,
539
540 /// Represents the function in the linked output file, if the `Decl` is a function.
541 /// This is stored here and not in `Fn` because `Decl` survives across updates but
542 /// `Fn` does not.
543 /// TODO Look into making `Fn` a longer lived structure and moving this field there
544 /// to save on memory usage.
545 fn_link: link.File.LinkFn,
546536
547 /// The shallow set of other decls whose typed_value could possibly change if this Decl's537 /// The shallow set of other decls whose typed_value could possibly change if this Decl's
548 /// typed_value is modified.538 /// typed_value is modified.
...@@ -2067,7 +2057,7 @@ pub const File = struct {...@@ -2067,7 +2057,7 @@ pub const File = struct {
2067 if (file.tree_loaded) return &file.tree;2057 if (file.tree_loaded) return &file.tree;
20682058
2069 const source = try file.getSource(gpa);2059 const source = try file.getSource(gpa);
2070 file.tree = try std.zig.parse(gpa, source.bytes);2060 file.tree = try Ast.parse(gpa, source.bytes, .zig);
2071 file.tree_loaded = true;2061 file.tree_loaded = true;
2072 return &file.tree;2062 return &file.tree;
2073 }2063 }
...@@ -3672,7 +3662,7 @@ pub fn astGenFile(mod: *Module, file: *File) !void {...@@ -3672,7 +3662,7 @@ pub fn astGenFile(mod: *Module, file: *File) !void {
3672 file.source = source;3662 file.source = source;
3673 file.source_loaded = true;3663 file.source_loaded = true;
36743664
3675 file.tree = try std.zig.parse(gpa, source);3665 file.tree = try Ast.parse(gpa, source, .zig);
3676 defer if (!file.tree_loaded) file.tree.deinit(gpa);3666 defer if (!file.tree_loaded) file.tree.deinit(gpa);
36773667
3678 if (file.tree.errors.len != 0) {3668 if (file.tree.errors.len != 0) {
...@@ -3987,7 +3977,7 @@ pub fn populateBuiltinFile(mod: *Module) !void {...@@ -3987,7 +3977,7 @@ pub fn populateBuiltinFile(mod: *Module) !void {
3987 else => |e| return e,3977 else => |e| return e,
3988 }3978 }
39893979
3990 file.tree = try std.zig.parse(gpa, file.source);3980 file.tree = try Ast.parse(gpa, file.source, .zig);
3991 file.tree_loaded = true;3981 file.tree_loaded = true;
3992 assert(file.tree.errors.len == 0); // builtin.zig must parse3982 assert(file.tree.errors.len == 0); // builtin.zig must parse
39933983
...@@ -4098,7 +4088,7 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl_index: Decl.Index) SemaError!void {...@@ -4098,7 +4088,7 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl_index: Decl.Index) SemaError!void {
40984088
4099 // The exports this Decl performs will be re-discovered, so we remove them here4089 // The exports this Decl performs will be re-discovered, so we remove them here
4100 // prior to re-analysis.4090 // prior to re-analysis.
4101 mod.deleteDeclExports(decl_index);4091 try mod.deleteDeclExports(decl_index);
41024092
4103 // Similarly, `@setAlignStack` invocations will be re-discovered.4093 // Similarly, `@setAlignStack` invocations will be re-discovered.
4104 if (decl.getFunction()) |func| {4094 if (decl.getFunction()) |func| {
...@@ -4585,7 +4575,6 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {...@@ -4585,7 +4575,6 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {
4585 // We don't fully codegen the decl until later, but we do need to reserve a global4575 // We don't fully codegen the decl until later, but we do need to reserve a global
4586 // offset table index for it. This allows us to codegen decls out of dependency4576 // offset table index for it. This allows us to codegen decls out of dependency
4587 // order, increasing how many computations can be done in parallel.4577 // order, increasing how many computations can be done in parallel.
4588 try mod.comp.bin_file.allocateDeclIndexes(decl_index);
4589 try mod.comp.work_queue.writeItem(.{ .codegen_func = func });4578 try mod.comp.work_queue.writeItem(.{ .codegen_func = func });
4590 if (type_changed and mod.emit_h != null) {4579 if (type_changed and mod.emit_h != null) {
4591 try mod.comp.work_queue.writeItem(.{ .emit_h_decl = decl_index });4580 try mod.comp.work_queue.writeItem(.{ .emit_h_decl = decl_index });
...@@ -4697,7 +4686,6 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {...@@ -4697,7 +4686,6 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {
4697 // codegen backend wants full access to the Decl Type.4686 // codegen backend wants full access to the Decl Type.
4698 try sema.resolveTypeFully(decl.ty);4687 try sema.resolveTypeFully(decl.ty);
46994688
4700 try mod.comp.bin_file.allocateDeclIndexes(decl_index);
4701 try mod.comp.work_queue.writeItem(.{ .codegen_decl = decl_index });4689 try mod.comp.work_queue.writeItem(.{ .codegen_decl = decl_index });
47024690
4703 if (type_changed and mod.emit_h != null) {4691 if (type_changed and mod.emit_h != null) {
...@@ -5185,20 +5173,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) Allocator.Err...@@ -5185,20 +5173,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) Allocator.Err
5185 decl.zir_decl_index = @intCast(u32, decl_sub_index);5173 decl.zir_decl_index = @intCast(u32, decl_sub_index);
5186 if (decl.getFunction()) |_| {5174 if (decl.getFunction()) |_| {
5187 switch (comp.bin_file.tag) {5175 switch (comp.bin_file.tag) {
5188 .coff => {5176 .coff, .elf, .macho, .plan9 => {
5189 // TODO Implement for COFF
5190 },
5191 .elf => if (decl.fn_link.elf.len != 0) {
5192 // TODO Look into detecting when this would be unnecessary by storing enough state
5193 // in `Decl` to notice that the line number did not change.
5194 comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl_index });
5195 },
5196 .macho => if (decl.fn_link.macho.len != 0) {
5197 // TODO Look into detecting when this would be unnecessary by storing enough state
5198 // in `Decl` to notice that the line number did not change.
5199 comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl_index });
5200 },
5201 .plan9 => {
5202 // TODO Look into detecting when this would be unnecessary by storing enough state5177 // TODO Look into detecting when this would be unnecessary by storing enough state
5203 // in `Decl` to notice that the line number did not change.5178 // in `Decl` to notice that the line number did not change.
5204 comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl_index });5179 comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl_index });
...@@ -5267,33 +5242,15 @@ pub fn clearDecl(...@@ -5267,33 +5242,15 @@ pub fn clearDecl(
5267 assert(emit_h.decl_table.swapRemove(decl_index));5242 assert(emit_h.decl_table.swapRemove(decl_index));
5268 }5243 }
5269 _ = mod.compile_log_decls.swapRemove(decl_index);5244 _ = mod.compile_log_decls.swapRemove(decl_index);
5270 mod.deleteDeclExports(decl_index);5245 try mod.deleteDeclExports(decl_index);
52715246
5272 if (decl.has_tv) {5247 if (decl.has_tv) {
5273 if (decl.ty.isFnOrHasRuntimeBits()) {5248 if (decl.ty.isFnOrHasRuntimeBits()) {
5274 mod.comp.bin_file.freeDecl(decl_index);5249 mod.comp.bin_file.freeDecl(decl_index);
52755250
5276 // TODO instead of a union, put this memory trailing Decl objects,
5277 // and allow it to be variably sized.
5278 decl.link = switch (mod.comp.bin_file.tag) {
5279 .coff => .{ .coff = link.File.Coff.Atom.empty },
5280 .elf => .{ .elf = link.File.Elf.TextBlock.empty },
5281 .macho => .{ .macho = link.File.MachO.Atom.empty },
5282 .plan9 => .{ .plan9 = link.File.Plan9.DeclBlock.empty },
5283 .c => .{ .c = {} },
5284 .wasm => .{ .wasm = link.File.Wasm.DeclBlock.empty },
5285 .spirv => .{ .spirv = {} },
5286 .nvptx => .{ .nvptx = {} },
5287 };
5288 decl.fn_link = switch (mod.comp.bin_file.tag) {5251 decl.fn_link = switch (mod.comp.bin_file.tag) {
5289 .coff => .{ .coff = {} },5252 .wasm => link.File.Wasm.FnData.empty,
5290 .elf => .{ .elf = link.File.Dwarf.SrcFn.empty },5253 else => null,
5291 .macho => .{ .macho = link.File.Dwarf.SrcFn.empty },
5292 .plan9 => .{ .plan9 = {} },
5293 .c => .{ .c = {} },
5294 .wasm => .{ .wasm = link.File.Wasm.FnData.empty },
5295 .spirv => .{ .spirv = .{} },
5296 .nvptx => .{ .nvptx = {} },
5297 };5254 };
5298 }5255 }
5299 if (decl.getInnerNamespace()) |namespace| {5256 if (decl.getInnerNamespace()) |namespace| {
...@@ -5315,23 +5272,6 @@ pub fn deleteUnusedDecl(mod: *Module, decl_index: Decl.Index) void {...@@ -5315,23 +5272,6 @@ pub fn deleteUnusedDecl(mod: *Module, decl_index: Decl.Index) void {
5315 const decl = mod.declPtr(decl_index);5272 const decl = mod.declPtr(decl_index);
5316 log.debug("deleteUnusedDecl {d} ({s})", .{ decl_index, decl.name });5273 log.debug("deleteUnusedDecl {d} ({s})", .{ decl_index, decl.name });
53175274
5318 // TODO: remove `allocateDeclIndexes` and make the API that the linker backends
5319 // are required to notice the first time `updateDecl` happens and keep track
5320 // of it themselves. However they can rely on getting a `freeDecl` call if any
5321 // `updateDecl` or `updateFunc` calls happen. This will allow us to avoid any call
5322 // into the linker backend here, since the linker backend will never have been told
5323 // about the Decl in the first place.
5324 // Until then, we did call `allocateDeclIndexes` on this anonymous Decl and so we
5325 // must call `freeDecl` in the linker backend now.
5326 switch (mod.comp.bin_file.tag) {
5327 .c => {}, // this linker backend has already migrated to the new API
5328 else => if (decl.has_tv) {
5329 if (decl.ty.isFnOrHasRuntimeBits()) {
5330 mod.comp.bin_file.freeDecl(decl_index);
5331 }
5332 },
5333 }
5334
5335 assert(!mod.declIsRoot(decl_index));5275 assert(!mod.declIsRoot(decl_index));
5336 assert(decl.src_namespace.anon_decls.swapRemove(decl_index));5276 assert(decl.src_namespace.anon_decls.swapRemove(decl_index));
53375277
...@@ -5377,7 +5317,7 @@ pub fn abortAnonDecl(mod: *Module, decl_index: Decl.Index) void {...@@ -5377,7 +5317,7 @@ pub fn abortAnonDecl(mod: *Module, decl_index: Decl.Index) void {
53775317
5378/// Delete all the Export objects that are caused by this Decl. Re-analysis of5318/// Delete all the Export objects that are caused by this Decl. Re-analysis of
5379/// this Decl will cause them to be re-created (or not).5319/// this Decl will cause them to be re-created (or not).
5380fn deleteDeclExports(mod: *Module, decl_index: Decl.Index) void {5320fn deleteDeclExports(mod: *Module, decl_index: Decl.Index) Allocator.Error!void {
5381 var export_owners = (mod.export_owners.fetchSwapRemove(decl_index) orelse return).value;5321 var export_owners = (mod.export_owners.fetchSwapRemove(decl_index) orelse return).value;
53825322
5383 for (export_owners.items) |exp| {5323 for (export_owners.items) |exp| {
...@@ -5400,16 +5340,16 @@ fn deleteDeclExports(mod: *Module, decl_index: Decl.Index) void {...@@ -5400,16 +5340,16 @@ fn deleteDeclExports(mod: *Module, decl_index: Decl.Index) void {
5400 }5340 }
5401 }5341 }
5402 if (mod.comp.bin_file.cast(link.File.Elf)) |elf| {5342 if (mod.comp.bin_file.cast(link.File.Elf)) |elf| {
5403 elf.deleteExport(exp.link.elf);5343 elf.deleteDeclExport(decl_index, exp.options.name);
5404 }5344 }
5405 if (mod.comp.bin_file.cast(link.File.MachO)) |macho| {5345 if (mod.comp.bin_file.cast(link.File.MachO)) |macho| {
5406 macho.deleteExport(exp.link.macho);5346 try macho.deleteDeclExport(decl_index, exp.options.name);
5407 }5347 }
5408 if (mod.comp.bin_file.cast(link.File.Wasm)) |wasm| {5348 if (mod.comp.bin_file.cast(link.File.Wasm)) |wasm| {
5409 wasm.deleteExport(exp.link.wasm);5349 wasm.deleteDeclExport(decl_index);
5410 }5350 }
5411 if (mod.comp.bin_file.cast(link.File.Coff)) |coff| {5351 if (mod.comp.bin_file.cast(link.File.Coff)) |coff| {
5412 coff.deleteExport(exp.link.coff);5352 coff.deleteDeclExport(decl_index, exp.options.name);
5413 }5353 }
5414 if (mod.failed_exports.fetchSwapRemove(exp)) |failed_kv| {5354 if (mod.failed_exports.fetchSwapRemove(exp)) |failed_kv| {
5415 failed_kv.value.destroy(mod.gpa);5355 failed_kv.value.destroy(mod.gpa);
...@@ -5712,25 +5652,9 @@ pub fn allocateNewDecl(...@@ -5712,25 +5652,9 @@ pub fn allocateNewDecl(
5712 .deletion_flag = false,5652 .deletion_flag = false,
5713 .zir_decl_index = 0,5653 .zir_decl_index = 0,
5714 .src_scope = src_scope,5654 .src_scope = src_scope,
5715 .link = switch (mod.comp.bin_file.tag) {
5716 .coff => .{ .coff = link.File.Coff.Atom.empty },
5717 .elf => .{ .elf = link.File.Elf.TextBlock.empty },
5718 .macho => .{ .macho = link.File.MachO.Atom.empty },
5719 .plan9 => .{ .plan9 = link.File.Plan9.DeclBlock.empty },
5720 .c => .{ .c = {} },
5721 .wasm => .{ .wasm = link.File.Wasm.DeclBlock.empty },
5722 .spirv => .{ .spirv = {} },
5723 .nvptx => .{ .nvptx = {} },
5724 },
5725 .fn_link = switch (mod.comp.bin_file.tag) {5655 .fn_link = switch (mod.comp.bin_file.tag) {
5726 .coff => .{ .coff = {} },5656 .wasm => link.File.Wasm.FnData.empty,
5727 .elf => .{ .elf = link.File.Dwarf.SrcFn.empty },5657 else => null,
5728 .macho => .{ .macho = link.File.Dwarf.SrcFn.empty },
5729 .plan9 => .{ .plan9 = {} },
5730 .c => .{ .c = {} },
5731 .wasm => .{ .wasm = link.File.Wasm.FnData.empty },
5732 .spirv => .{ .spirv = .{} },
5733 .nvptx => .{ .nvptx = {} },
5734 },5658 },
5735 .generation = 0,5659 .generation = 0,
5736 .is_pub = false,5660 .is_pub = false,
...@@ -5816,7 +5740,6 @@ pub fn initNewAnonDecl(...@@ -5816,7 +5740,6 @@ pub fn initNewAnonDecl(
5816 // the Decl will be garbage collected by the `codegen_decl` task instead of sent5740 // the Decl will be garbage collected by the `codegen_decl` task instead of sent
5817 // to the linker.5741 // to the linker.
5818 if (typed_value.ty.isFnOrHasRuntimeBits()) {5742 if (typed_value.ty.isFnOrHasRuntimeBits()) {
5819 try mod.comp.bin_file.allocateDeclIndexes(new_decl_index);
5820 try mod.comp.anon_work_queue.writeItem(.{ .codegen_decl = new_decl_index });5743 try mod.comp.anon_work_queue.writeItem(.{ .codegen_decl = new_decl_index });
5821 }5744 }
5822}5745}
src/Package.zig+157-152
...@@ -1,12 +1,13 @@...@@ -1,12 +1,13 @@
1const Package = @This();1const Package = @This();
22
3const builtin = @import("builtin");
3const std = @import("std");4const std = @import("std");
4const fs = std.fs;5const fs = std.fs;
5const mem = std.mem;6const mem = std.mem;
6const Allocator = mem.Allocator;7const Allocator = mem.Allocator;
7const assert = std.debug.assert;8const assert = std.debug.assert;
8const Hash = std.crypto.hash.sha2.Sha256;
9const log = std.log.scoped(.package);9const log = std.log.scoped(.package);
10const main = @import("main.zig");
1011
11const Compilation = @import("Compilation.zig");12const Compilation = @import("Compilation.zig");
12const Module = @import("Module.zig");13const Module = @import("Module.zig");
...@@ -14,6 +15,7 @@ const ThreadPool = @import("ThreadPool.zig");...@@ -14,6 +15,7 @@ const ThreadPool = @import("ThreadPool.zig");
14const WaitGroup = @import("WaitGroup.zig");15const WaitGroup = @import("WaitGroup.zig");
15const Cache = @import("Cache.zig");16const Cache = @import("Cache.zig");
16const build_options = @import("build_options");17const build_options = @import("build_options");
18const Manifest = @import("Manifest.zig");
1719
18pub const Table = std.StringHashMapUnmanaged(*Package);20pub const Table = std.StringHashMapUnmanaged(*Package);
1921
...@@ -140,10 +142,10 @@ pub fn addAndAdopt(parent: *Package, gpa: Allocator, child: *Package) !void {...@@ -140,10 +142,10 @@ pub fn addAndAdopt(parent: *Package, gpa: Allocator, child: *Package) !void {
140}142}
141143
142pub const build_zig_basename = "build.zig";144pub const build_zig_basename = "build.zig";
143pub const ini_basename = build_zig_basename ++ ".ini";
144145
145pub fn fetchAndAddDependencies(146pub fn fetchAndAddDependencies(
146 pkg: *Package,147 pkg: *Package,
148 arena: Allocator,
147 thread_pool: *ThreadPool,149 thread_pool: *ThreadPool,
148 http_client: *std.http.Client,150 http_client: *std.http.Client,
149 directory: Compilation.Directory,151 directory: Compilation.Directory,
...@@ -152,89 +154,77 @@ pub fn fetchAndAddDependencies(...@@ -152,89 +154,77 @@ pub fn fetchAndAddDependencies(
152 dependencies_source: *std.ArrayList(u8),154 dependencies_source: *std.ArrayList(u8),
153 build_roots_source: *std.ArrayList(u8),155 build_roots_source: *std.ArrayList(u8),
154 name_prefix: []const u8,156 name_prefix: []const u8,
157 color: main.Color,
155) !void {158) !void {
156 const max_bytes = 10 * 1024 * 1024;159 const max_bytes = 10 * 1024 * 1024;
157 const gpa = thread_pool.allocator;160 const gpa = thread_pool.allocator;
158 const build_zig_ini = directory.handle.readFileAlloc(gpa, ini_basename, max_bytes) catch |err| switch (err) {161 const build_zig_zon_bytes = directory.handle.readFileAllocOptions(
162 arena,
163 Manifest.basename,
164 max_bytes,
165 null,
166 1,
167 0,
168 ) catch |err| switch (err) {
159 error.FileNotFound => {169 error.FileNotFound => {
160 // Handle the same as no dependencies.170 // Handle the same as no dependencies.
161 return;171 return;
162 },172 },
163 else => |e| return e,173 else => |e| return e,
164 };174 };
165 defer gpa.free(build_zig_ini);
166175
167 const ini: std.Ini = .{ .bytes = build_zig_ini };176 var ast = try std.zig.Ast.parse(gpa, build_zig_zon_bytes, .zon);
168 var any_error = false;177 defer ast.deinit(gpa);
169 var it = ini.iterateSection("\n[dependency]\n");
170 while (it.next()) |dep| {
171 var line_it = mem.split(u8, dep, "\n");
172 var opt_name: ?[]const u8 = null;
173 var opt_url: ?[]const u8 = null;
174 var expected_hash: ?[]const u8 = null;
175 while (line_it.next()) |kv| {
176 const eq_pos = mem.indexOfScalar(u8, kv, '=') orelse continue;
177 const key = kv[0..eq_pos];
178 const value = kv[eq_pos + 1 ..];
179 if (mem.eql(u8, key, "name")) {
180 opt_name = value;
181 } else if (mem.eql(u8, key, "url")) {
182 opt_url = value;
183 } else if (mem.eql(u8, key, "hash")) {
184 expected_hash = value;
185 } else {
186 const loc = std.zig.findLineColumn(ini.bytes, @ptrToInt(key.ptr) - @ptrToInt(ini.bytes.ptr));
187 std.log.warn("{s}/{s}:{d}:{d} unrecognized key: '{s}'", .{
188 directory.path orelse ".",
189 "build.zig.ini",
190 loc.line,
191 loc.column,
192 key,
193 });
194 }
195 }
196178
197 const name = opt_name orelse {179 if (ast.errors.len > 0) {
198 const loc = std.zig.findLineColumn(ini.bytes, @ptrToInt(dep.ptr) - @ptrToInt(ini.bytes.ptr));180 const file_path = try directory.join(arena, &.{Manifest.basename});
199 std.log.err("{s}/{s}:{d}:{d} missing key: 'name'", .{181 try main.printErrsMsgToStdErr(gpa, arena, ast, file_path, color);
200 directory.path orelse ".",182 return error.PackageFetchFailed;
201 "build.zig.ini",183 }
202 loc.line,
203 loc.column,
204 });
205 any_error = true;
206 continue;
207 };
208184
209 const url = opt_url orelse {185 var manifest = try Manifest.parse(gpa, ast);
210 const loc = std.zig.findLineColumn(ini.bytes, @ptrToInt(dep.ptr) - @ptrToInt(ini.bytes.ptr));186 defer manifest.deinit(gpa);
211 std.log.err("{s}/{s}:{d}:{d} missing key: 'name'", .{187
212 directory.path orelse ".",188 if (manifest.errors.len > 0) {
213 "build.zig.ini",189 const ttyconf: std.debug.TTY.Config = switch (color) {
214 loc.line,190 .auto => std.debug.detectTTYConfig(std.io.getStdErr()),
215 loc.column,191 .on => .escape_codes,
216 });192 .off => .no_color,
217 any_error = true;
218 continue;
219 };193 };
194 const file_path = try directory.join(arena, &.{Manifest.basename});
195 for (manifest.errors) |msg| {
196 Report.renderErrorMessage(ast, file_path, ttyconf, msg, &.{});
197 }
198 return error.PackageFetchFailed;
199 }
220200
221 const sub_prefix = try std.fmt.allocPrint(gpa, "{s}{s}.", .{ name_prefix, name });201 const report: Report = .{
222 defer gpa.free(sub_prefix);202 .ast = &ast,
203 .directory = directory,
204 .color = color,
205 .arena = arena,
206 };
207
208 var any_error = false;
209 const deps_list = manifest.dependencies.values();
210 for (manifest.dependencies.keys()) |name, i| {
211 const dep = deps_list[i];
212
213 const sub_prefix = try std.fmt.allocPrint(arena, "{s}{s}.", .{ name_prefix, name });
223 const fqn = sub_prefix[0 .. sub_prefix.len - 1];214 const fqn = sub_prefix[0 .. sub_prefix.len - 1];
224215
225 const sub_pkg = try fetchAndUnpack(216 const sub_pkg = try fetchAndUnpack(
226 thread_pool,217 thread_pool,
227 http_client,218 http_client,
228 global_cache_directory,219 global_cache_directory,
229 url,220 dep,
230 expected_hash,221 report,
231 ini,
232 directory,
233 build_roots_source,222 build_roots_source,
234 fqn,223 fqn,
235 );224 );
236225
237 try pkg.fetchAndAddDependencies(226 try pkg.fetchAndAddDependencies(
227 arena,
238 thread_pool,228 thread_pool,
239 http_client,229 http_client,
240 sub_pkg.root_src_directory,230 sub_pkg.root_src_directory,
...@@ -243,6 +233,7 @@ pub fn fetchAndAddDependencies(...@@ -243,6 +233,7 @@ pub fn fetchAndAddDependencies(
243 dependencies_source,233 dependencies_source,
244 build_roots_source,234 build_roots_source,
245 sub_prefix,235 sub_prefix,
236 color,
246 );237 );
247238
248 try addAndAdopt(pkg, gpa, sub_pkg);239 try addAndAdopt(pkg, gpa, sub_pkg);
...@@ -252,7 +243,7 @@ pub fn fetchAndAddDependencies(...@@ -252,7 +243,7 @@ pub fn fetchAndAddDependencies(
252 });243 });
253 }244 }
254245
255 if (any_error) return error.InvalidBuildZigIniFile;246 if (any_error) return error.InvalidBuildManifestFile;
256}247}
257248
258pub fn createFilePkg(249pub fn createFilePkg(
...@@ -263,7 +254,7 @@ pub fn createFilePkg(...@@ -263,7 +254,7 @@ pub fn createFilePkg(
263 contents: []const u8,254 contents: []const u8,
264) !*Package {255) !*Package {
265 const rand_int = std.crypto.random.int(u64);256 const rand_int = std.crypto.random.int(u64);
266 const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ hex64(rand_int);257 const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ Manifest.hex64(rand_int);
267 {258 {
268 var tmp_dir = try cache_directory.handle.makeOpenPath(tmp_dir_sub_path, .{});259 var tmp_dir = try cache_directory.handle.makeOpenPath(tmp_dir_sub_path, .{});
269 defer tmp_dir.close();260 defer tmp_dir.close();
...@@ -281,14 +272,73 @@ pub fn createFilePkg(...@@ -281,14 +272,73 @@ pub fn createFilePkg(
281 return createWithDir(gpa, name, cache_directory, o_dir_sub_path, basename);272 return createWithDir(gpa, name, cache_directory, o_dir_sub_path, basename);
282}273}
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
284fn fetchAndUnpack(336fn fetchAndUnpack(
285 thread_pool: *ThreadPool,337 thread_pool: *ThreadPool,
286 http_client: *std.http.Client,338 http_client: *std.http.Client,
287 global_cache_directory: Compilation.Directory,339 global_cache_directory: Compilation.Directory,
288 url: []const u8,340 dep: Manifest.Dependency,
289 expected_hash: ?[]const u8,341 report: Report,
290 ini: std.Ini,
291 comp_directory: Compilation.Directory,
292 build_roots_source: *std.ArrayList(u8),342 build_roots_source: *std.ArrayList(u8),
293 fqn: []const u8,343 fqn: []const u8,
294) !*Package {344) !*Package {
...@@ -297,17 +347,9 @@ fn fetchAndUnpack(...@@ -297,17 +347,9 @@ fn fetchAndUnpack(
297347
298 // Check if the expected_hash is already present in the global package348 // Check if the expected_hash is already present in the global package
299 // cache, and thereby avoid both fetching and unpacking.349 // cache, and thereby avoid both fetching and unpacking.
300 if (expected_hash) |h| cached: {350 if (dep.hash) |h| cached: {
301 if (h.len != 2 * Hash.digest_length) {351 const hex_multihash_len = 2 * Manifest.multihash_len;
302 return reportError(352 const hex_digest = h[0..hex_multihash_len];
303 ini,
304 comp_directory,
305 h.ptr,
306 "wrong hash size. expected: {d}, found: {d}",
307 .{ Hash.digest_length, h.len },
308 );
309 }
310 const hex_digest = h[0 .. 2 * Hash.digest_length];
311 const pkg_dir_sub_path = "p" ++ s ++ hex_digest;353 const pkg_dir_sub_path = "p" ++ s ++ hex_digest;
312 var pkg_dir = global_cache_directory.handle.openDir(pkg_dir_sub_path, .{}) catch |err| switch (err) {354 var pkg_dir = global_cache_directory.handle.openDir(pkg_dir_sub_path, .{}) catch |err| switch (err) {
313 error.FileNotFound => break :cached,355 error.FileNotFound => break :cached,
...@@ -344,10 +386,10 @@ fn fetchAndUnpack(...@@ -344,10 +386,10 @@ fn fetchAndUnpack(
344 return ptr;386 return ptr;
345 }387 }
346388
347 const uri = try std.Uri.parse(url);389 const uri = try std.Uri.parse(dep.url);
348390
349 const rand_int = std.crypto.random.int(u64);391 const rand_int = std.crypto.random.int(u64);
350 const tmp_dir_sub_path = "tmp" ++ s ++ hex64(rand_int);392 const tmp_dir_sub_path = "tmp" ++ s ++ Manifest.hex64(rand_int);
351393
352 const actual_hash = a: {394 const actual_hash = a: {
353 var tmp_directory: Compilation.Directory = d: {395 var tmp_directory: Compilation.Directory = d: {
...@@ -376,13 +418,9 @@ fn fetchAndUnpack(...@@ -376,13 +418,9 @@ fn fetchAndUnpack(
376 // by default, so the same logic applies for buffering the reader as for gzip.418 // by default, so the same logic applies for buffering the reader as for gzip.
377 try unpackTarball(gpa, &req, tmp_directory.handle, std.compress.xz);419 try unpackTarball(gpa, &req, tmp_directory.handle, std.compress.xz);
378 } else {420 } else {
379 return reportError(421 return report.fail(dep.url_tok, "unknown file extension for path '{s}'", .{
380 ini,422 uri.path,
381 comp_directory,423 });
382 uri.path.ptr,
383 "unknown file extension for path '{s}'",
384 .{uri.path},
385 );
386 }424 }
387425
388 // TODO: delete files not included in the package prior to computing the package hash.426 // TODO: delete files not included in the package prior to computing the package hash.
...@@ -393,28 +431,21 @@ fn fetchAndUnpack(...@@ -393,28 +431,21 @@ fn fetchAndUnpack(
393 break :a try computePackageHash(thread_pool, .{ .dir = tmp_directory.handle });431 break :a try computePackageHash(thread_pool, .{ .dir = tmp_directory.handle });
394 };432 };
395433
396 const pkg_dir_sub_path = "p" ++ s ++ hexDigest(actual_hash);434 const pkg_dir_sub_path = "p" ++ s ++ Manifest.hexDigest(actual_hash);
397 try renameTmpIntoCache(global_cache_directory.handle, tmp_dir_sub_path, pkg_dir_sub_path);435 try renameTmpIntoCache(global_cache_directory.handle, tmp_dir_sub_path, pkg_dir_sub_path);
398436
399 if (expected_hash) |h| {437 const actual_hex = Manifest.hexDigest(actual_hash);
400 const actual_hex = hexDigest(actual_hash);438 if (dep.hash) |h| {
401 if (!mem.eql(u8, h, &actual_hex)) {439 if (!mem.eql(u8, h, &actual_hex)) {
402 return reportError(440 return report.fail(dep.hash_tok, "hash mismatch: expected: {s}, found: {s}", .{
403 ini,441 h, actual_hex,
404 comp_directory,442 });
405 h.ptr,
406 "hash mismatch: expected: {s}, found: {s}",
407 .{ h, actual_hex },
408 );
409 }443 }
410 } else {444 } else {
411 return reportError(445 const notes: [1]Compilation.AllErrors.Message = .{.{ .plain = .{
412 ini,446 .msg = try std.fmt.allocPrint(report.arena, "expected .hash = \"{s}\",", .{&actual_hex}),
413 comp_directory,447 } }};
414 url.ptr,448 return report.failWithNotes(&notes, dep.url_tok, "url field is missing corresponding hash field", .{});
415 "url field is missing corresponding hash field: hash={s}",
416 .{std.fmt.fmtSliceHexLower(&actual_hash)},
417 );
418 }449 }
419450
420 const build_root = try global_cache_directory.join(gpa, &.{pkg_dir_sub_path});451 const build_root = try global_cache_directory.join(gpa, &.{pkg_dir_sub_path});
...@@ -440,35 +471,21 @@ fn unpackTarball(...@@ -440,35 +471,21 @@ fn unpackTarball(
440471
441 try std.tar.pipeToFileSystem(out_dir, decompress.reader(), .{472 try std.tar.pipeToFileSystem(out_dir, decompress.reader(), .{
442 .strip_components = 1,473 .strip_components = 1,
474 // TODO: we would like to set this to executable_bit_only, but two
475 // things need to happen before that:
476 // 1. the tar implementation needs to support it
477 // 2. the hashing algorithm here needs to support detecting the is_executable
478 // bit on Windows from the ACLs (see the isExecutable function).
479 .mode_mode = .ignore,
443 });480 });
444}481}
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
466const HashedFile = struct {483const HashedFile = struct {
467 path: []const u8,484 path: []const u8,
468 hash: [Hash.digest_length]u8,485 hash: [Manifest.Hash.digest_length]u8,
469 failure: Error!void,486 failure: Error!void,
470487
471 const Error = fs.File.OpenError || fs.File.ReadError;488 const Error = fs.File.OpenError || fs.File.ReadError || fs.File.StatError;
472489
473 fn lessThan(context: void, lhs: *const HashedFile, rhs: *const HashedFile) bool {490 fn lessThan(context: void, lhs: *const HashedFile, rhs: *const HashedFile) bool {
474 _ = context;491 _ = context;
...@@ -479,7 +496,7 @@ const HashedFile = struct {...@@ -479,7 +496,7 @@ const HashedFile = struct {
479fn computePackageHash(496fn computePackageHash(
480 thread_pool: *ThreadPool,497 thread_pool: *ThreadPool,
481 pkg_dir: fs.IterableDir,498 pkg_dir: fs.IterableDir,
482) ![Hash.digest_length]u8 {499) ![Manifest.Hash.digest_length]u8 {
483 const gpa = thread_pool.allocator;500 const gpa = thread_pool.allocator;
484501
485 // We'll use an arena allocator for the path name strings since they all502 // We'll use an arena allocator for the path name strings since they all
...@@ -522,7 +539,7 @@ fn computePackageHash(...@@ -522,7 +539,7 @@ fn computePackageHash(
522539
523 std.sort.sort(*HashedFile, all_files.items, {}, HashedFile.lessThan);540 std.sort.sort(*HashedFile, all_files.items, {}, HashedFile.lessThan);
524541
525 var hasher = Hash.init(.{});542 var hasher = Manifest.Hash.init(.{});
526 var any_failures = false;543 var any_failures = false;
527 for (all_files.items) |hashed_file| {544 for (all_files.items) |hashed_file| {
528 hashed_file.failure catch |err| {545 hashed_file.failure catch |err| {
...@@ -543,7 +560,9 @@ fn workerHashFile(dir: fs.Dir, hashed_file: *HashedFile, wg: *WaitGroup) void {...@@ -543,7 +560,9 @@ fn workerHashFile(dir: fs.Dir, hashed_file: *HashedFile, wg: *WaitGroup) void {
543fn hashFileFallible(dir: fs.Dir, hashed_file: *HashedFile) HashedFile.Error!void {560fn hashFileFallible(dir: fs.Dir, hashed_file: *HashedFile) HashedFile.Error!void {
544 var buf: [8000]u8 = undefined;561 var buf: [8000]u8 = undefined;
545 var file = try dir.openFile(hashed_file.path, .{});562 var file = try dir.openFile(hashed_file.path, .{});
546 var hasher = Hash.init(.{});563 var hasher = Manifest.Hash.init(.{});
564 hasher.update(hashed_file.path);
565 hasher.update(&.{ 0, @boolToInt(try isExecutable(file)) });
547 while (true) {566 while (true) {
548 const bytes_read = try file.read(&buf);567 const bytes_read = try file.read(&buf);
549 if (bytes_read == 0) break;568 if (bytes_read == 0) break;
...@@ -552,31 +571,17 @@ fn hashFileFallible(dir: fs.Dir, hashed_file: *HashedFile) HashedFile.Error!void...@@ -552,31 +571,17 @@ fn hashFileFallible(dir: fs.Dir, hashed_file: *HashedFile) HashedFile.Error!void
552 hasher.final(&hashed_file.hash);571 hasher.final(&hashed_file.hash);
553}572}
554573
555const hex_charset = "0123456789abcdef";574fn isExecutable(file: fs.File) !bool {
556575 if (builtin.os.tag == .windows) {
557fn hex64(x: u64) [16]u8 {576 // TODO check the ACL on Windows.
558 var result: [16]u8 = undefined;577 // Until this is implemented, this could be a false negative on
559 var i: usize = 0;578 // Windows, which is why we do not yet set executable_bit_only above
560 while (i < 8) : (i += 1) {579 // when unpacking the tarball.
561 const byte = @truncate(u8, x >> @intCast(u6, 8 * i));580 return false;
562 result[i * 2 + 0] = hex_charset[byte >> 4];581 } else {
563 result[i * 2 + 1] = hex_charset[byte & 15];582 const stat = try file.stat();
564 }583 return (stat.mode & std.os.S.IXUSR) != 0;
565 return result;
566}
567
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];
578 }584 }
579 return result;
580}585}
581586
582fn renameTmpIntoCache(587fn renameTmpIntoCache(
src/Sema.zig+99-22
...@@ -1015,6 +1015,7 @@ fn analyzeBodyInner(...@@ -1015,6 +1015,7 @@ fn analyzeBodyInner(
1015 .float_cast => try sema.zirFloatCast(block, inst),1015 .float_cast => try sema.zirFloatCast(block, inst),
1016 .int_cast => try sema.zirIntCast(block, inst),1016 .int_cast => try sema.zirIntCast(block, inst),
1017 .ptr_cast => try sema.zirPtrCast(block, inst),1017 .ptr_cast => try sema.zirPtrCast(block, inst),
1018 .qual_cast => try sema.zirQualCast(block, inst),
1018 .truncate => try sema.zirTruncate(block, inst),1019 .truncate => try sema.zirTruncate(block, inst),
1019 .align_cast => try sema.zirAlignCast(block, inst),1020 .align_cast => try sema.zirAlignCast(block, inst),
1020 .has_decl => try sema.zirHasDecl(block, inst),1021 .has_decl => try sema.zirHasDecl(block, inst),
...@@ -3294,7 +3295,7 @@ fn ensureResultUsed(...@@ -3294,7 +3295,7 @@ fn ensureResultUsed(
3294 const msg = msg: {3295 const msg = msg: {
3295 const msg = try sema.errMsg(block, src, "error is ignored", .{});3296 const msg = try sema.errMsg(block, src, "error is ignored", .{});
3296 errdefer msg.destroy(sema.gpa);3297 errdefer msg.destroy(sema.gpa);
3297 try sema.errNote(block, src, msg, "consider using `try`, `catch`, or `if`", .{});3298 try sema.errNote(block, src, msg, "consider using 'try', 'catch', or 'if'", .{});
3298 break :msg msg;3299 break :msg msg;
3299 };3300 };
3300 return sema.failWithOwnedErrorMsg(msg);3301 return sema.failWithOwnedErrorMsg(msg);
...@@ -3325,7 +3326,7 @@ fn zirEnsureResultNonError(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com...@@ -3325,7 +3326,7 @@ fn zirEnsureResultNonError(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
3325 const msg = msg: {3326 const msg = msg: {
3326 const msg = try sema.errMsg(block, src, "error is discarded", .{});3327 const msg = try sema.errMsg(block, src, "error is discarded", .{});
3327 errdefer msg.destroy(sema.gpa);3328 errdefer msg.destroy(sema.gpa);
3328 try sema.errNote(block, src, msg, "consider using `try`, `catch`, or `if`", .{});3329 try sema.errNote(block, src, msg, "consider using 'try', 'catch', or 'if'", .{});
3329 break :msg msg;3330 break :msg msg;
3330 };3331 };
3331 return sema.failWithOwnedErrorMsg(msg);3332 return sema.failWithOwnedErrorMsg(msg);
...@@ -5564,16 +5565,6 @@ pub fn analyzeExport(...@@ -5564,16 +5565,6 @@ pub fn analyzeExport(
5564 .visibility = borrowed_options.visibility,5565 .visibility = borrowed_options.visibility,
5565 },5566 },
5566 .src = src,5567 .src = src,
5567 .link = switch (mod.comp.bin_file.tag) {
5568 .coff => .{ .coff = .{} },
5569 .elf => .{ .elf = .{} },
5570 .macho => .{ .macho = .{} },
5571 .plan9 => .{ .plan9 = null },
5572 .c => .{ .c = {} },
5573 .wasm => .{ .wasm = .{} },
5574 .spirv => .{ .spirv = {} },
5575 .nvptx => .{ .nvptx = {} },
5576 },
5577 .owner_decl = sema.owner_decl_index,5568 .owner_decl = sema.owner_decl_index,
5578 .src_decl = block.src_decl,5569 .src_decl = block.src_decl,
5579 .exported_decl = exported_decl_index,5570 .exported_decl = exported_decl_index,
...@@ -6446,7 +6437,12 @@ fn analyzeCall(...@@ -6446,7 +6437,12 @@ fn analyzeCall(
6446 .extern_fn => return sema.fail(block, call_src, "{s} call of extern function", .{6437 .extern_fn => return sema.fail(block, call_src, "{s} call of extern function", .{
6447 @as([]const u8, if (is_comptime_call) "comptime" else "inline"),6438 @as([]const u8, if (is_comptime_call) "comptime" else "inline"),
6448 }),6439 }),
6449 else => unreachable,6440 else => {
6441 assert(callee_ty.isPtrAtRuntime());
6442 return sema.fail(block, call_src, "{s} call of function pointer", .{
6443 @as([]const u8, if (is_comptime_call) "comptime" else "inline"),
6444 });
6445 },
6450 };6446 };
6451 if (func_ty_info.is_var_args) {6447 if (func_ty_info.is_var_args) {
6452 return sema.fail(block, call_src, "{s} call of variadic function", .{6448 return sema.fail(block, call_src, "{s} call of variadic function", .{
...@@ -6879,6 +6875,8 @@ fn analyzeInlineCallArg(...@@ -6879,6 +6875,8 @@ fn analyzeInlineCallArg(
6879 if (err == error.AnalysisFail and param_block.comptime_reason != null) try param_block.comptime_reason.?.explain(sema, sema.err);6875 if (err == error.AnalysisFail and param_block.comptime_reason != null) try param_block.comptime_reason.?.explain(sema, sema.err);
6880 return err;6876 return err;
6881 };6877 };
6878 } else if (!is_comptime_call and zir_tags[inst] == .param_comptime) {
6879 _ = try sema.resolveConstMaybeUndefVal(arg_block, arg_src, uncasted_arg, "parameter is comptime");
6882 }6880 }
6883 const casted_arg = sema.coerceExtra(arg_block, param_ty, uncasted_arg, arg_src, .{ .param_src = .{6881 const casted_arg = sema.coerceExtra(arg_block, param_ty, uncasted_arg, arg_src, .{ .param_src = .{
6884 .func_inst = func_inst,6882 .func_inst = func_inst,
...@@ -6952,6 +6950,9 @@ fn analyzeInlineCallArg(...@@ -6952,6 +6950,9 @@ fn analyzeInlineCallArg(
6952 .val = arg_val,6950 .val = arg_val,
6953 };6951 };
6954 } else {6952 } else {
6953 if (zir_tags[inst] == .param_anytype_comptime) {
6954 _ = try sema.resolveConstMaybeUndefVal(arg_block, arg_src, uncasted_arg, "parameter is comptime");
6955 }
6955 sema.inst_map.putAssumeCapacityNoClobber(inst, uncasted_arg);6956 sema.inst_map.putAssumeCapacityNoClobber(inst, uncasted_arg);
6956 }6957 }
69576958
...@@ -7510,7 +7511,6 @@ fn resolveGenericInstantiationType(...@@ -7510,7 +7511,6 @@ fn resolveGenericInstantiationType(
7510 // Queue up a `codegen_func` work item for the new Fn. The `comptime_args` field7511 // Queue up a `codegen_func` work item for the new Fn. The `comptime_args` field
7511 // will be populated, ensuring it will have `analyzeBody` called with the ZIR7512 // will be populated, ensuring it will have `analyzeBody` called with the ZIR
7512 // parameters mapped appropriately.7513 // parameters mapped appropriately.
7513 try mod.comp.bin_file.allocateDeclIndexes(new_decl_index);
7514 try mod.comp.work_queue.writeItem(.{ .codegen_func = new_func });7514 try mod.comp.work_queue.writeItem(.{ .codegen_func = new_func });
7515 return new_func;7515 return new_func;
7516}7516}
...@@ -8473,7 +8473,7 @@ fn handleExternLibName(...@@ -8473,7 +8473,7 @@ fn handleExternLibName(
8473 return sema.fail(8473 return sema.fail(
8474 block,8474 block,
8475 src_loc,8475 src_loc,
8476 "dependency on dynamic library '{s}' requires enabling Position Independent Code. Fixed by `-l{s}` or `-fPIC`.",8476 "dependency on dynamic library '{s}' requires enabling Position Independent Code. Fixed by '-l{s}' or '-fPIC'.",
8477 .{ lib_name, lib_name },8477 .{ lib_name, lib_name },
8478 );8478 );
8479 }8479 }
...@@ -9010,7 +9010,18 @@ fn zirParam(...@@ -9010,7 +9010,18 @@ fn zirParam(
9010 if (is_comptime and sema.preallocated_new_func != null) {9010 if (is_comptime and sema.preallocated_new_func != null) {
9011 // We have a comptime value for this parameter so it should be elided from the9011 // We have a comptime value for this parameter so it should be elided from the
9012 // function type of the function instruction in this block.9012 // function type of the function instruction in this block.
9013 const coerced_arg = try sema.coerce(block, param_ty, arg, src);9013 const coerced_arg = sema.coerce(block, param_ty, arg, .unneeded) catch |err| switch (err) {
9014 error.NeededSourceLocation => {
9015 // We are instantiating a generic function and a comptime arg
9016 // cannot be coerced to the param type, but since we don't
9017 // have the callee source location return `GenericPoison`
9018 // so that the instantiation is failed and the coercion
9019 // is handled by comptime call logic instead.
9020 assert(sema.is_generic_instantiation);
9021 return error.GenericPoison;
9022 },
9023 else => return err,
9024 };
9014 sema.inst_map.putAssumeCapacity(inst, coerced_arg);9025 sema.inst_map.putAssumeCapacity(inst, coerced_arg);
9015 return;9026 return;
9016 }9027 }
...@@ -19525,13 +19536,34 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air...@@ -19525,13 +19536,34 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
19525 const operand_info = operand_ty.ptrInfo().data;19536 const operand_info = operand_ty.ptrInfo().data;
19526 const dest_info = dest_ty.ptrInfo().data;19537 const dest_info = dest_ty.ptrInfo().data;
19527 if (!operand_info.mutable and dest_info.mutable) {19538 if (!operand_info.mutable and dest_info.mutable) {
19528 return sema.fail(block, src, "cast discards const qualifier", .{});19539 const msg = msg: {
19540 const msg = try sema.errMsg(block, src, "cast discards const qualifier", .{});
19541 errdefer msg.destroy(sema.gpa);
19542
19543 try sema.errNote(block, src, msg, "consider using '@qualCast'", .{});
19544 break :msg msg;
19545 };
19546 return sema.failWithOwnedErrorMsg(msg);
19529 }19547 }
19530 if (operand_info.@"volatile" and !dest_info.@"volatile") {19548 if (operand_info.@"volatile" and !dest_info.@"volatile") {
19531 return sema.fail(block, src, "cast discards volatile qualifier", .{});19549 const msg = msg: {
19550 const msg = try sema.errMsg(block, src, "cast discards volatile qualifier", .{});
19551 errdefer msg.destroy(sema.gpa);
19552
19553 try sema.errNote(block, src, msg, "consider using '@qualCast'", .{});
19554 break :msg msg;
19555 };
19556 return sema.failWithOwnedErrorMsg(msg);
19532 }19557 }
19533 if (operand_info.@"addrspace" != dest_info.@"addrspace") {19558 if (operand_info.@"addrspace" != dest_info.@"addrspace") {
19534 return sema.fail(block, src, "cast changes pointer address space", .{});19559 const msg = msg: {
19560 const msg = try sema.errMsg(block, src, "cast changes pointer address space", .{});
19561 errdefer msg.destroy(sema.gpa);
19562
19563 try sema.errNote(block, src, msg, "consider using '@addrSpaceCast'", .{});
19564 break :msg msg;
19565 };
19566 return sema.failWithOwnedErrorMsg(msg);
19535 }19567 }
1953619568
19537 const dest_is_slice = dest_ty.isSlice();19569 const dest_is_slice = dest_ty.isSlice();
...@@ -19586,6 +19618,8 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air...@@ -19586,6 +19618,8 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
19586 try sema.errNote(block, dest_ty_src, msg, "'{}' has alignment '{d}'", .{19618 try sema.errNote(block, dest_ty_src, msg, "'{}' has alignment '{d}'", .{
19587 dest_ty.fmt(sema.mod), dest_align,19619 dest_ty.fmt(sema.mod), dest_align,
19588 });19620 });
19621
19622 try sema.errNote(block, src, msg, "consider using '@alignCast'", .{});
19589 break :msg msg;19623 break :msg msg;
19590 };19624 };
19591 return sema.failWithOwnedErrorMsg(msg);19625 return sema.failWithOwnedErrorMsg(msg);
...@@ -19621,6 +19655,49 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air...@@ -19621,6 +19655,49 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
19621 return block.addBitCast(aligned_dest_ty, ptr);19655 return block.addBitCast(aligned_dest_ty, ptr);
19622}19656}
1962319657
19658fn zirQualCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
19659 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
19660 const src = inst_data.src();
19661 const dest_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
19662 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
19663 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
19664 const dest_ty = try sema.resolveType(block, dest_ty_src, extra.lhs);
19665 const operand = try sema.resolveInst(extra.rhs);
19666 const operand_ty = sema.typeOf(operand);
19667
19668 try sema.checkPtrType(block, dest_ty_src, dest_ty);
19669 try sema.checkPtrOperand(block, operand_src, operand_ty);
19670
19671 var operand_payload = operand_ty.ptrInfo();
19672 var dest_info = dest_ty.ptrInfo();
19673
19674 operand_payload.data.mutable = dest_info.data.mutable;
19675 operand_payload.data.@"volatile" = dest_info.data.@"volatile";
19676
19677 const altered_operand_ty = Type.initPayload(&operand_payload.base);
19678 if (!altered_operand_ty.eql(dest_ty, sema.mod)) {
19679 const msg = msg: {
19680 const msg = try sema.errMsg(block, src, "'@qualCast' can only modify 'const' and 'volatile' qualifiers", .{});
19681 errdefer msg.destroy(sema.gpa);
19682
19683 dest_info.data.mutable = !operand_ty.isConstPtr();
19684 dest_info.data.@"volatile" = operand_ty.isVolatilePtr();
19685 const altered_dest_ty = Type.initPayload(&dest_info.base);
19686 try sema.errNote(block, src, msg, "expected type '{}'", .{altered_dest_ty.fmt(sema.mod)});
19687 try sema.errNote(block, src, msg, "got type '{}'", .{operand_ty.fmt(sema.mod)});
19688 break :msg msg;
19689 };
19690 return sema.failWithOwnedErrorMsg(msg);
19691 }
19692
19693 if (try sema.resolveMaybeUndefVal(operand)) |operand_val| {
19694 return sema.addConstant(dest_ty, operand_val);
19695 }
19696
19697 try sema.requireRuntimeBlock(block, src, null);
19698 return block.addBitCast(dest_ty, operand);
19699}
19700
19624fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {19701fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
19625 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;19702 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
19626 const src = inst_data.src();19703 const src = inst_data.src();
...@@ -25137,7 +25214,7 @@ fn coerceExtra(...@@ -25137,7 +25214,7 @@ fn coerceExtra(
25137 (try sema.coerceInMemoryAllowed(block, inst_ty.errorUnionPayload(), dest_ty, false, target, dest_ty_src, inst_src)) == .ok)25214 (try sema.coerceInMemoryAllowed(block, inst_ty.errorUnionPayload(), dest_ty, false, target, dest_ty_src, inst_src)) == .ok)
25138 {25215 {
25139 try sema.errNote(block, inst_src, msg, "cannot convert error union to payload type", .{});25216 try sema.errNote(block, inst_src, msg, "cannot convert error union to payload type", .{});
25140 try sema.errNote(block, inst_src, msg, "consider using `try`, `catch`, or `if`", .{});25217 try sema.errNote(block, inst_src, msg, "consider using 'try', 'catch', or 'if'", .{});
25141 }25218 }
2514225219
25143 // ?T to T25220 // ?T to T
...@@ -25146,7 +25223,7 @@ fn coerceExtra(...@@ -25146,7 +25223,7 @@ fn coerceExtra(
25146 (try sema.coerceInMemoryAllowed(block, inst_ty.optionalChild(&buf), dest_ty, false, target, dest_ty_src, inst_src)) == .ok)25223 (try sema.coerceInMemoryAllowed(block, inst_ty.optionalChild(&buf), dest_ty, false, target, dest_ty_src, inst_src)) == .ok)
25147 {25224 {
25148 try sema.errNote(block, inst_src, msg, "cannot convert optional to payload type", .{});25225 try sema.errNote(block, inst_src, msg, "cannot convert optional to payload type", .{});
25149 try sema.errNote(block, inst_src, msg, "consider using `.?`, `orelse`, or `if`", .{});25226 try sema.errNote(block, inst_src, msg, "consider using '.?', 'orelse', or 'if'", .{});
25150 }25227 }
2515125228
25152 try in_memory_result.report(sema, block, inst_src, msg);25229 try in_memory_result.report(sema, block, inst_src, msg);
...@@ -26072,7 +26149,7 @@ fn coerceVarArgParam(...@@ -26072,7 +26149,7 @@ fn coerceVarArgParam(
26072 .Array => return sema.fail(block, inst_src, "arrays must be passed by reference to variadic function", .{}),26149 .Array => return sema.fail(block, inst_src, "arrays must be passed by reference to variadic function", .{}),
26073 .Float => float: {26150 .Float => float: {
26074 const target = sema.mod.getTarget();26151 const target = sema.mod.getTarget();
26075 const double_bits = @import("type.zig").CType.sizeInBits(.double, target);26152 const double_bits = target.c_type_bit_size(.double);
26076 const inst_bits = uncasted_ty.floatBits(sema.mod.getTarget());26153 const inst_bits = uncasted_ty.floatBits(sema.mod.getTarget());
26077 if (inst_bits >= double_bits) break :float inst;26154 if (inst_bits >= double_bits) break :float inst;
26078 switch (double_bits) {26155 switch (double_bits) {
src/TypedValue.zig+4-1
...@@ -176,7 +176,9 @@ pub fn print(...@@ -176,7 +176,9 @@ pub fn print(
176176
177 var i: u32 = 0;177 var i: u32 = 0;
178 while (i < max_len) : (i += 1) {178 while (i < max_len) : (i += 1) {
179 buf[i] = std.math.cast(u8, val.fieldValue(ty, i).toUnsignedInt(target)) orelse break :str;179 const elem = val.fieldValue(ty, i);
180 if (elem.isUndef()) break :str;
181 buf[i] = std.math.cast(u8, elem.toUnsignedInt(target)) orelse break :str;
180 }182 }
181183
182 const truncated = if (len > max_string_len) " (truncated)" else "";184 const truncated = if (len > max_string_len) " (truncated)" else "";
...@@ -390,6 +392,7 @@ pub fn print(...@@ -390,6 +392,7 @@ pub fn print(
390 while (i < max_len) : (i += 1) {392 while (i < max_len) : (i += 1) {
391 var elem_buf: Value.ElemValueBuffer = undefined;393 var elem_buf: Value.ElemValueBuffer = undefined;
392 const elem_val = payload.ptr.elemValueBuffer(mod, i, &elem_buf);394 const elem_val = payload.ptr.elemValueBuffer(mod, i, &elem_buf);
395 if (elem_val.isUndef()) break :str;
393 buf[i] = std.math.cast(u8, elem_val.toUnsignedInt(target)) orelse break :str;396 buf[i] = std.math.cast(u8, elem_val.toUnsignedInt(target)) orelse break :str;
394 }397 }
395398
src/Zir.zig+6
...@@ -857,6 +857,9 @@ pub const Inst = struct {...@@ -857,6 +857,9 @@ pub const Inst = struct {
857 /// Implements the `@ptrCast` builtin.857 /// Implements the `@ptrCast` builtin.
858 /// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand.858 /// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand.
859 ptr_cast,859 ptr_cast,
860 /// Implements the `@qualCast` builtin.
861 /// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand.
862 qual_cast,
860 /// Implements the `@truncate` builtin.863 /// Implements the `@truncate` builtin.
861 /// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand.864 /// Uses `pl_node` with payload `Bin`. `lhs` is dest type, `rhs` is operand.
862 truncate,865 truncate,
...@@ -1195,6 +1198,7 @@ pub const Inst = struct {...@@ -1195,6 +1198,7 @@ pub const Inst = struct {
1195 .float_cast,1198 .float_cast,
1196 .int_cast,1199 .int_cast,
1197 .ptr_cast,1200 .ptr_cast,
1201 .qual_cast,
1198 .truncate,1202 .truncate,
1199 .align_cast,1203 .align_cast,
1200 .has_field,1204 .has_field,
...@@ -1484,6 +1488,7 @@ pub const Inst = struct {...@@ -1484,6 +1488,7 @@ pub const Inst = struct {
1484 .float_cast,1488 .float_cast,
1485 .int_cast,1489 .int_cast,
1486 .ptr_cast,1490 .ptr_cast,
1491 .qual_cast,
1487 .truncate,1492 .truncate,
1488 .align_cast,1493 .align_cast,
1489 .has_field,1494 .has_field,
...@@ -1755,6 +1760,7 @@ pub const Inst = struct {...@@ -1755,6 +1760,7 @@ pub const Inst = struct {
1755 .float_cast = .pl_node,1760 .float_cast = .pl_node,
1756 .int_cast = .pl_node,1761 .int_cast = .pl_node,
1757 .ptr_cast = .pl_node,1762 .ptr_cast = .pl_node,
1763 .qual_cast = .pl_node,
1758 .truncate = .pl_node,1764 .truncate = .pl_node,
1759 .align_cast = .pl_node,1765 .align_cast = .pl_node,
1760 .typeof_builtin = .pl_node,1766 .typeof_builtin = .pl_node,
src/arch/aarch64/CodeGen.zig+143-175
...@@ -24,7 +24,7 @@ const log = std.log.scoped(.codegen);...@@ -24,7 +24,7 @@ const log = std.log.scoped(.codegen);
24const build_options = @import("build_options");24const build_options = @import("build_options");
2525
26const GenerateSymbolError = codegen.GenerateSymbolError;26const GenerateSymbolError = codegen.GenerateSymbolError;
27const FnResult = codegen.FnResult;27const Result = codegen.Result;
28const DebugInfoOutput = codegen.DebugInfoOutput;28const DebugInfoOutput = codegen.DebugInfoOutput;
2929
30const bits = @import("bits.zig");30const bits = @import("bits.zig");
...@@ -181,6 +181,7 @@ const DbgInfoReloc = struct {...@@ -181,6 +181,7 @@ const DbgInfoReloc = struct {
181 else => unreachable,181 else => unreachable,
182 }182 }
183 }183 }
184
184 fn genArgDbgInfo(reloc: DbgInfoReloc, function: Self) error{OutOfMemory}!void {185 fn genArgDbgInfo(reloc: DbgInfoReloc, function: Self) error{OutOfMemory}!void {
185 switch (function.debug_output) {186 switch (function.debug_output) {
186 .dwarf => |dw| {187 .dwarf => |dw| {
...@@ -202,13 +203,7 @@ const DbgInfoReloc = struct {...@@ -202,13 +203,7 @@ const DbgInfoReloc = struct {
202 else => unreachable, // not a possible argument203 else => unreachable, // not a possible argument
203204
204 };205 };
205 try dw.genArgDbgInfo(206 try dw.genArgDbgInfo(reloc.name, reloc.ty, function.mod_fn.owner_decl, loc);
206 reloc.name,
207 reloc.ty,
208 function.bin_file.tag,
209 function.mod_fn.owner_decl,
210 loc,
211 );
212 },207 },
213 .plan9 => {},208 .plan9 => {},
214 .none => {},209 .none => {},
...@@ -254,14 +249,7 @@ const DbgInfoReloc = struct {...@@ -254,14 +249,7 @@ const DbgInfoReloc = struct {
254 break :blk .nop;249 break :blk .nop;
255 },250 },
256 };251 };
257 try dw.genVarDbgInfo(252 try dw.genVarDbgInfo(reloc.name, reloc.ty, function.mod_fn.owner_decl, is_ptr, loc);
258 reloc.name,
259 reloc.ty,
260 function.bin_file.tag,
261 function.mod_fn.owner_decl,
262 is_ptr,
263 loc,
264 );
265 },253 },
266 .plan9 => {},254 .plan9 => {},
267 .none => {},255 .none => {},
...@@ -349,7 +337,7 @@ pub fn generate(...@@ -349,7 +337,7 @@ pub fn generate(
349 liveness: Liveness,337 liveness: Liveness,
350 code: *std.ArrayList(u8),338 code: *std.ArrayList(u8),
351 debug_output: DebugInfoOutput,339 debug_output: DebugInfoOutput,
352) GenerateSymbolError!FnResult {340) GenerateSymbolError!Result {
353 if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) {341 if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) {
354 @panic("Attempted to compile for architecture that was disabled by build configuration");342 @panic("Attempted to compile for architecture that was disabled by build configuration");
355 }343 }
...@@ -392,8 +380,8 @@ pub fn generate(...@@ -392,8 +380,8 @@ pub fn generate(
392 defer function.dbg_info_relocs.deinit(bin_file.allocator);380 defer function.dbg_info_relocs.deinit(bin_file.allocator);
393381
394 var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) {382 var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) {
395 error.CodegenFail => return FnResult{ .fail = function.err_msg.? },383 error.CodegenFail => return Result{ .fail = function.err_msg.? },
396 error.OutOfRegisters => return FnResult{384 error.OutOfRegisters => return Result{
397 .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),385 .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
398 },386 },
399 else => |e| return e,387 else => |e| return e,
...@@ -406,8 +394,8 @@ pub fn generate(...@@ -406,8 +394,8 @@ pub fn generate(
406 function.max_end_stack = call_info.stack_byte_count;394 function.max_end_stack = call_info.stack_byte_count;
407395
408 function.gen() catch |err| switch (err) {396 function.gen() catch |err| switch (err) {
409 error.CodegenFail => return FnResult{ .fail = function.err_msg.? },397 error.CodegenFail => return Result{ .fail = function.err_msg.? },
410 error.OutOfRegisters => return FnResult{398 error.OutOfRegisters => return Result{
411 .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),399 .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
412 },400 },
413 else => |e| return e,401 else => |e| return e,
...@@ -439,14 +427,14 @@ pub fn generate(...@@ -439,14 +427,14 @@ pub fn generate(
439 defer emit.deinit();427 defer emit.deinit();
440428
441 emit.emitMir() catch |err| switch (err) {429 emit.emitMir() catch |err| switch (err) {
442 error.EmitFail => return FnResult{ .fail = emit.err_msg.? },430 error.EmitFail => return Result{ .fail = emit.err_msg.? },
443 else => |e| return e,431 else => |e| return e,
444 };432 };
445433
446 if (function.err_msg) |em| {434 if (function.err_msg) |em| {
447 return FnResult{ .fail = em };435 return Result{ .fail = em };
448 } else {436 } else {
449 return FnResult{ .appended = {} };437 return Result.ok;
450 }438 }
451}439}
452440
...@@ -527,6 +515,28 @@ fn gen(self: *Self) !void {...@@ -527,6 +515,28 @@ fn gen(self: *Self) !void {
527 self.ret_mcv = MCValue{ .stack_offset = stack_offset };515 self.ret_mcv = MCValue{ .stack_offset = stack_offset };
528 }516 }
529517
518 for (self.args) |*arg, arg_index| {
519 // Copy register arguments to the stack
520 switch (arg.*) {
521 .register => |reg| {
522 // The first AIR instructions of the main body are guaranteed
523 // to be the functions arguments
524 const inst = self.air.getMainBody()[arg_index];
525 assert(self.air.instructions.items(.tag)[inst] == .arg);
526
527 const ty = self.air.typeOfIndex(inst);
528
529 const abi_size = @intCast(u32, ty.abiSize(self.target.*));
530 const abi_align = ty.abiAlignment(self.target.*);
531 const stack_offset = try self.allocMem(abi_size, abi_align, inst);
532 try self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
533
534 arg.* = MCValue{ .stack_offset = stack_offset };
535 },
536 else => {},
537 }
538 }
539
530 _ = try self.addInst(.{540 _ = try self.addInst(.{
531 .tag = .dbg_prologue_end,541 .tag = .dbg_prologue_end,
532 .data = .{ .nop = {} },542 .data = .{ .nop = {} },
...@@ -3996,11 +4006,17 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type...@@ -3996,11 +4006,17 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
3996 .direct => .load_memory_ptr_direct,4006 .direct => .load_memory_ptr_direct,
3997 .import => unreachable,4007 .import => unreachable,
3998 };4008 };
3999 const mod = self.bin_file.options.module.?;
4000 const owner_decl = mod.declPtr(self.mod_fn.owner_decl);
4001 const atom_index = switch (self.bin_file.tag) {4009 const atom_index = switch (self.bin_file.tag) {
4002 .macho => owner_decl.link.macho.sym_index,4010 .macho => blk: {
4003 .coff => owner_decl.link.coff.sym_index,4011 const macho_file = self.bin_file.cast(link.File.MachO).?;
4012 const atom = try macho_file.getOrCreateAtomForDecl(self.mod_fn.owner_decl);
4013 break :blk macho_file.getAtom(atom).getSymbolIndex().?;
4014 },
4015 .coff => blk: {
4016 const coff_file = self.bin_file.cast(link.File.Coff).?;
4017 const atom = try coff_file.getOrCreateAtomForDecl(self.mod_fn.owner_decl);
4018 break :blk coff_file.getAtom(atom).getSymbolIndex().?;
4019 },
4004 else => unreachable, // unsupported target format4020 else => unreachable, // unsupported target format
4005 };4021 };
4006 _ = try self.addInst(.{4022 _ = try self.addInst(.{
...@@ -4163,45 +4179,19 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {...@@ -4163,45 +4179,19 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
4163 self.arg_index += 1;4179 self.arg_index += 1;
41644180
4165 const ty = self.air.typeOfIndex(inst);4181 const ty = self.air.typeOfIndex(inst);
4166 const result = self.args[arg_index];4182 const tag = self.air.instructions.items(.tag)[inst];
4167 const src_index = self.air.instructions.items(.data)[inst].arg.src_index;4183 const src_index = self.air.instructions.items(.data)[inst].arg.src_index;
4168 const name = self.mod_fn.getParamName(self.bin_file.options.module.?, src_index);4184 const name = self.mod_fn.getParamName(self.bin_file.options.module.?, src_index);
41694185
4170 const mcv = switch (result) {
4171 // Copy registers to the stack
4172 .register => |reg| blk: {
4173 const mod = self.bin_file.options.module.?;
4174 const abi_size = math.cast(u32, ty.abiSize(self.target.*)) orelse {
4175 return self.fail("type '{}' too big to fit into stack frame", .{ty.fmt(mod)});
4176 };
4177 const abi_align = ty.abiAlignment(self.target.*);
4178 const stack_offset = try self.allocMem(abi_size, abi_align, inst);
4179 try self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
4180
4181 break :blk MCValue{ .stack_offset = stack_offset };
4182 },
4183 else => result,
4184 };
4185
4186 const tag = self.air.instructions.items(.tag)[inst];
4187 try self.dbg_info_relocs.append(self.gpa, .{4186 try self.dbg_info_relocs.append(self.gpa, .{
4188 .tag = tag,4187 .tag = tag,
4189 .ty = ty,4188 .ty = ty,
4190 .name = name,4189 .name = name,
4191 .mcv = result,4190 .mcv = self.args[arg_index],
4192 });4191 });
41934192
4194 if (self.liveness.isUnused(inst))4193 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else self.args[arg_index];
4195 return self.finishAirBookkeeping();4194 return self.finishAir(inst, result, .{ .none, .none, .none });
4196
4197 switch (mcv) {
4198 .register => |reg| {
4199 self.register_manager.getRegAssumeFree(reg, inst);
4200 },
4201 else => {},
4202 }
4203
4204 return self.finishAir(inst, mcv, .{ .none, .none, .none });
4205}4195}
42064196
4207fn airBreakpoint(self: *Self) !void {4197fn airBreakpoint(self: *Self) !void {
...@@ -4302,90 +4292,71 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier...@@ -4302,90 +4292,71 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
4302 // on linking.4292 // on linking.
4303 const mod = self.bin_file.options.module.?;4293 const mod = self.bin_file.options.module.?;
4304 if (self.air.value(callee)) |func_value| {4294 if (self.air.value(callee)) |func_value| {
4305 if (self.bin_file.cast(link.File.Elf)) |elf_file| {4295 if (func_value.castTag(.function)) |func_payload| {
4306 if (func_value.castTag(.function)) |func_payload| {4296 const func = func_payload.data;
4307 const func = func_payload.data;
4308 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
4309 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
4310 const fn_owner_decl = mod.declPtr(func.owner_decl);
4311 const got_addr = blk: {
4312 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
4313 break :blk @intCast(u32, got.p_vaddr + fn_owner_decl.link.elf.offset_table_index * ptr_bytes);
4314 };
43154297
4298 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
4299 const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl);
4300 const atom = elf_file.getAtom(atom_index);
4301 const got_addr = @intCast(u32, atom.getOffsetTableAddress(elf_file));
4316 try self.genSetReg(Type.initTag(.usize), .x30, .{ .memory = got_addr });4302 try self.genSetReg(Type.initTag(.usize), .x30, .{ .memory = got_addr });
43174303 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
4318 _ = try self.addInst(.{4304 const atom = try macho_file.getOrCreateAtomForDecl(func.owner_decl);
4319 .tag = .blr,4305 const sym_index = macho_file.getAtom(atom).getSymbolIndex().?;
4320 .data = .{ .reg = .x30 },4306 try self.genSetReg(Type.initTag(.u64), .x30, .{
4307 .linker_load = .{
4308 .type = .got,
4309 .sym_index = sym_index,
4310 },
4321 });4311 });
4322 } else if (func_value.castTag(.extern_fn)) |_| {4312 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
4323 return self.fail("TODO implement calling extern functions", .{});4313 const atom = try coff_file.getOrCreateAtomForDecl(func.owner_decl);
4324 } else {4314 const sym_index = coff_file.getAtom(atom).getSymbolIndex().?;
4325 return self.fail("TODO implement calling bitcasted functions", .{});
4326 }
4327 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
4328 if (func_value.castTag(.function)) |func_payload| {
4329 const func = func_payload.data;
4330 const fn_owner_decl = mod.declPtr(func.owner_decl);
4331 try self.genSetReg(Type.initTag(.u64), .x30, .{4315 try self.genSetReg(Type.initTag(.u64), .x30, .{
4332 .linker_load = .{4316 .linker_load = .{
4333 .type = .got,4317 .type = .got,
4334 .sym_index = fn_owner_decl.link.macho.sym_index,4318 .sym_index = sym_index,
4335 },4319 },
4336 });4320 });
4337 // blr x304321 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
4338 _ = try self.addInst(.{4322 const decl_block_index = try p9.seeDecl(func.owner_decl);
4339 .tag = .blr,4323 const decl_block = p9.getDeclBlock(decl_block_index);
4340 .data = .{ .reg = .x30 },4324 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
4325 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
4326 const got_addr = p9.bases.data;
4327 const got_index = decl_block.got_index.?;
4328 const fn_got_addr = got_addr + got_index * ptr_bytes;
4329 try self.genSetReg(Type.initTag(.usize), .x30, .{ .memory = fn_got_addr });
4330 } else unreachable;
4331
4332 _ = try self.addInst(.{
4333 .tag = .blr,
4334 .data = .{ .reg = .x30 },
4335 });
4336 } else if (func_value.castTag(.extern_fn)) |func_payload| {
4337 const extern_fn = func_payload.data;
4338 const decl_name = mod.declPtr(extern_fn.owner_decl).name;
4339 if (extern_fn.lib_name) |lib_name| {
4340 log.debug("TODO enforce that '{s}' is expected in '{s}' library", .{
4341 decl_name,
4342 lib_name,
4341 });4343 });
4342 } else if (func_value.castTag(.extern_fn)) |func_payload| {4344 }
4343 const extern_fn = func_payload.data;
4344 const decl_name = mod.declPtr(extern_fn.owner_decl).name;
4345 if (extern_fn.lib_name) |lib_name| {
4346 log.debug("TODO enforce that '{s}' is expected in '{s}' library", .{
4347 decl_name,
4348 lib_name,
4349 });
4350 }
4351 const sym_index = try macho_file.getGlobalSymbol(mem.sliceTo(decl_name, 0));
43524345
4346 if (self.bin_file.cast(link.File.MachO)) |macho_file| {
4347 const sym_index = try macho_file.getGlobalSymbol(mem.sliceTo(decl_name, 0));
4348 const atom = try macho_file.getOrCreateAtomForDecl(self.mod_fn.owner_decl);
4349 const atom_index = macho_file.getAtom(atom).getSymbolIndex().?;
4353 _ = try self.addInst(.{4350 _ = try self.addInst(.{
4354 .tag = .call_extern,4351 .tag = .call_extern,
4355 .data = .{4352 .data = .{
4356 .relocation = .{4353 .relocation = .{
4357 .atom_index = mod.declPtr(self.mod_fn.owner_decl).link.macho.sym_index,4354 .atom_index = atom_index,
4358 .sym_index = sym_index,4355 .sym_index = sym_index,
4359 },4356 },
4360 },4357 },
4361 });4358 });
4362 } else {4359 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
4363 return self.fail("TODO implement calling bitcasted functions", .{});
4364 }
4365 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
4366 if (func_value.castTag(.function)) |func_payload| {
4367 const func = func_payload.data;
4368 const fn_owner_decl = mod.declPtr(func.owner_decl);
4369 try self.genSetReg(Type.initTag(.u64), .x30, .{
4370 .linker_load = .{
4371 .type = .got,
4372 .sym_index = fn_owner_decl.link.coff.sym_index,
4373 },
4374 });
4375 // blr x30
4376 _ = try self.addInst(.{
4377 .tag = .blr,
4378 .data = .{ .reg = .x30 },
4379 });
4380 } else if (func_value.castTag(.extern_fn)) |func_payload| {
4381 const extern_fn = func_payload.data;
4382 const decl_name = mod.declPtr(extern_fn.owner_decl).name;
4383 if (extern_fn.lib_name) |lib_name| {
4384 log.debug("TODO enforce that '{s}' is expected in '{s}' library", .{
4385 decl_name,
4386 lib_name,
4387 });
4388 }
4389 const sym_index = try coff_file.getGlobalSymbol(mem.sliceTo(decl_name, 0));4360 const sym_index = try coff_file.getGlobalSymbol(mem.sliceTo(decl_name, 0));
4390 try self.genSetReg(Type.initTag(.u64), .x30, .{4361 try self.genSetReg(Type.initTag(.u64), .x30, .{
4391 .linker_load = .{4362 .linker_load = .{
...@@ -4393,35 +4364,16 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier...@@ -4393,35 +4364,16 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
4393 .sym_index = sym_index,4364 .sym_index = sym_index,
4394 },4365 },
4395 });4366 });
4396 // blr x30
4397 _ = try self.addInst(.{4367 _ = try self.addInst(.{
4398 .tag = .blr,4368 .tag = .blr,
4399 .data = .{ .reg = .x30 },4369 .data = .{ .reg = .x30 },
4400 });4370 });
4401 } else {4371 } else {
4402 return self.fail("TODO implement calling bitcasted functions", .{});
4403 }
4404 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
4405 if (func_value.castTag(.function)) |func_payload| {
4406 try p9.seeDecl(func_payload.data.owner_decl);
4407 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
4408 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
4409 const got_addr = p9.bases.data;
4410 const got_index = mod.declPtr(func_payload.data.owner_decl).link.plan9.got_index.?;
4411 const fn_got_addr = got_addr + got_index * ptr_bytes;
4412
4413 try self.genSetReg(Type.initTag(.usize), .x30, .{ .memory = fn_got_addr });
4414
4415 _ = try self.addInst(.{
4416 .tag = .blr,
4417 .data = .{ .reg = .x30 },
4418 });
4419 } else if (func_value.castTag(.extern_fn)) |_| {
4420 return self.fail("TODO implement calling extern functions", .{});4372 return self.fail("TODO implement calling extern functions", .{});
4421 } else {
4422 return self.fail("TODO implement calling bitcasted functions", .{});
4423 }4373 }
4424 } else unreachable;4374 } else {
4375 return self.fail("TODO implement calling bitcasted functions", .{});
4376 }
4425 } else {4377 } else {
4426 assert(ty.zigTypeTag() == .Pointer);4378 assert(ty.zigTypeTag() == .Pointer);
4427 const mcv = try self.resolveInst(callee);4379 const mcv = try self.resolveInst(callee);
...@@ -5534,11 +5486,17 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro...@@ -5534,11 +5486,17 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
5534 .direct => .load_memory_ptr_direct,5486 .direct => .load_memory_ptr_direct,
5535 .import => unreachable,5487 .import => unreachable,
5536 };5488 };
5537 const mod = self.bin_file.options.module.?;
5538 const owner_decl = mod.declPtr(self.mod_fn.owner_decl);
5539 const atom_index = switch (self.bin_file.tag) {5489 const atom_index = switch (self.bin_file.tag) {
5540 .macho => owner_decl.link.macho.sym_index,5490 .macho => blk: {
5541 .coff => owner_decl.link.coff.sym_index,5491 const macho_file = self.bin_file.cast(link.File.MachO).?;
5492 const atom = try macho_file.getOrCreateAtomForDecl(self.mod_fn.owner_decl);
5493 break :blk macho_file.getAtom(atom).getSymbolIndex().?;
5494 },
5495 .coff => blk: {
5496 const coff_file = self.bin_file.cast(link.File.Coff).?;
5497 const atom = try coff_file.getOrCreateAtomForDecl(self.mod_fn.owner_decl);
5498 break :blk coff_file.getAtom(atom).getSymbolIndex().?;
5499 },
5542 else => unreachable, // unsupported target format5500 else => unreachable, // unsupported target format
5543 };5501 };
5544 _ = try self.addInst(.{5502 _ = try self.addInst(.{
...@@ -5648,11 +5606,17 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void...@@ -5648,11 +5606,17 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
5648 .direct => .load_memory_direct,5606 .direct => .load_memory_direct,
5649 .import => .load_memory_import,5607 .import => .load_memory_import,
5650 };5608 };
5651 const mod = self.bin_file.options.module.?;
5652 const owner_decl = mod.declPtr(self.mod_fn.owner_decl);
5653 const atom_index = switch (self.bin_file.tag) {5609 const atom_index = switch (self.bin_file.tag) {
5654 .macho => owner_decl.link.macho.sym_index,5610 .macho => blk: {
5655 .coff => owner_decl.link.coff.sym_index,5611 const macho_file = self.bin_file.cast(link.File.MachO).?;
5612 const atom = try macho_file.getOrCreateAtomForDecl(self.mod_fn.owner_decl);
5613 break :blk macho_file.getAtom(atom).getSymbolIndex().?;
5614 },
5615 .coff => blk: {
5616 const coff_file = self.bin_file.cast(link.File.Coff).?;
5617 const atom = try coff_file.getOrCreateAtomForDecl(self.mod_fn.owner_decl);
5618 break :blk coff_file.getAtom(atom).getSymbolIndex().?;
5619 },
5656 else => unreachable, // unsupported target format5620 else => unreachable, // unsupported target format
5657 };5621 };
5658 _ = try self.addInst(.{5622 _ = try self.addInst(.{
...@@ -5842,11 +5806,17 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I...@@ -5842,11 +5806,17 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I
5842 .direct => .load_memory_ptr_direct,5806 .direct => .load_memory_ptr_direct,
5843 .import => unreachable,5807 .import => unreachable,
5844 };5808 };
5845 const mod = self.bin_file.options.module.?;
5846 const owner_decl = mod.declPtr(self.mod_fn.owner_decl);
5847 const atom_index = switch (self.bin_file.tag) {5809 const atom_index = switch (self.bin_file.tag) {
5848 .macho => owner_decl.link.macho.sym_index,5810 .macho => blk: {
5849 .coff => owner_decl.link.coff.sym_index,5811 const macho_file = self.bin_file.cast(link.File.MachO).?;
5812 const atom = try macho_file.getOrCreateAtomForDecl(self.mod_fn.owner_decl);
5813 break :blk macho_file.getAtom(atom).getSymbolIndex().?;
5814 },
5815 .coff => blk: {
5816 const coff_file = self.bin_file.cast(link.File.Coff).?;
5817 const atom = try coff_file.getOrCreateAtomForDecl(self.mod_fn.owner_decl);
5818 break :blk coff_file.getAtom(atom).getSymbolIndex().?;
5819 },
5850 else => unreachable, // unsupported target format5820 else => unreachable, // unsupported target format
5851 };5821 };
5852 _ = try self.addInst(.{5822 _ = try self.addInst(.{
...@@ -6165,28 +6135,27 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne...@@ -6165,28 +6135,27 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne
6165 mod.markDeclAlive(decl);6135 mod.markDeclAlive(decl);
61666136
6167 if (self.bin_file.cast(link.File.Elf)) |elf_file| {6137 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
6168 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];6138 const atom_index = try elf_file.getOrCreateAtomForDecl(decl_index);
6169 const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes;6139 const atom = elf_file.getAtom(atom_index);
6170 return MCValue{ .memory = got_addr };6140 return MCValue{ .memory = atom.getOffsetTableAddress(elf_file) };
6171 } else if (self.bin_file.cast(link.File.MachO)) |_| {6141 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
6172 // Because MachO is PIE-always-on, we defer memory address resolution until6142 const atom = try macho_file.getOrCreateAtomForDecl(decl_index);
6173 // the linker has enough info to perform relocations.6143 const sym_index = macho_file.getAtom(atom).getSymbolIndex().?;
6174 assert(decl.link.macho.sym_index != 0);
6175 return MCValue{ .linker_load = .{6144 return MCValue{ .linker_load = .{
6176 .type = .got,6145 .type = .got,
6177 .sym_index = decl.link.macho.sym_index,6146 .sym_index = sym_index,
6178 } };6147 } };
6179 } else if (self.bin_file.cast(link.File.Coff)) |_| {6148 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
6180 // Because COFF is PIE-always-on, we defer memory address resolution until6149 const atom_index = try coff_file.getOrCreateAtomForDecl(decl_index);
6181 // the linker has enough info to perform relocations.6150 const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?;
6182 assert(decl.link.coff.sym_index != 0);
6183 return MCValue{ .linker_load = .{6151 return MCValue{ .linker_load = .{
6184 .type = .got,6152 .type = .got,
6185 .sym_index = decl.link.coff.sym_index,6153 .sym_index = sym_index,
6186 } };6154 } };
6187 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {6155 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
6188 try p9.seeDecl(decl_index);6156 const decl_block_index = try p9.seeDecl(decl_index);
6189 const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes;6157 const decl_block = p9.getDeclBlock(decl_block_index);
6158 const got_addr = p9.bases.data + decl_block.got_index.? * ptr_bytes;
6190 return MCValue{ .memory = got_addr };6159 return MCValue{ .memory = got_addr };
6191 } else {6160 } else {
6192 return self.fail("TODO codegen non-ELF const Decl pointer", .{});6161 return self.fail("TODO codegen non-ELF const Decl pointer", .{});
...@@ -6199,8 +6168,7 @@ fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue {...@@ -6199,8 +6168,7 @@ fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue {
6199 return self.fail("lowering unnamed constant failed: {s}", .{@errorName(err)});6168 return self.fail("lowering unnamed constant failed: {s}", .{@errorName(err)});
6200 };6169 };
6201 if (self.bin_file.cast(link.File.Elf)) |elf_file| {6170 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
6202 const vaddr = elf_file.local_symbols.items[local_sym_index].st_value;6171 return MCValue{ .memory = elf_file.getSymbol(local_sym_index).st_value };
6203 return MCValue{ .memory = vaddr };
6204 } else if (self.bin_file.cast(link.File.MachO)) |_| {6172 } else if (self.bin_file.cast(link.File.MachO)) |_| {
6205 return MCValue{ .linker_load = .{6173 return MCValue{ .linker_load = .{
6206 .type = .direct,6174 .type = .direct,
src/arch/aarch64/Emit.zig+8-8
...@@ -670,9 +670,9 @@ fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -670,9 +670,9 @@ fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) !void {
670670
671 if (emit.bin_file.cast(link.File.MachO)) |macho_file| {671 if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
672 // Add relocation to the decl.672 // Add relocation to the decl.
673 const atom = macho_file.getAtomForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?;673 const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?;
674 const target = macho_file.getGlobalByIndex(relocation.sym_index);674 const target = macho_file.getGlobalByIndex(relocation.sym_index);
675 try atom.addRelocation(macho_file, .{675 try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{
676 .type = @enumToInt(std.macho.reloc_type_arm64.ARM64_RELOC_BRANCH26),676 .type = @enumToInt(std.macho.reloc_type_arm64.ARM64_RELOC_BRANCH26),
677 .target = target,677 .target = target,
678 .offset = offset,678 .offset = offset,
...@@ -883,10 +883,10 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -883,10 +883,10 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void {
883 }883 }
884884
885 if (emit.bin_file.cast(link.File.MachO)) |macho_file| {885 if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
886 const atom = macho_file.getAtomForSymbol(.{ .sym_index = data.atom_index, .file = null }).?;886 const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = data.atom_index, .file = null }).?;
887 // TODO this causes segfault in stage1887 // TODO this causes segfault in stage1
888 // try atom.addRelocations(macho_file, 2, .{888 // try atom.addRelocations(macho_file, 2, .{
889 try atom.addRelocation(macho_file, .{889 try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{
890 .target = .{ .sym_index = data.sym_index, .file = null },890 .target = .{ .sym_index = data.sym_index, .file = null },
891 .offset = offset,891 .offset = offset,
892 .addend = 0,892 .addend = 0,
...@@ -902,7 +902,7 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -902,7 +902,7 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void {
902 else => unreachable,902 else => unreachable,
903 },903 },
904 });904 });
905 try atom.addRelocation(macho_file, .{905 try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{
906 .target = .{ .sym_index = data.sym_index, .file = null },906 .target = .{ .sym_index = data.sym_index, .file = null },
907 .offset = offset + 4,907 .offset = offset + 4,
908 .addend = 0,908 .addend = 0,
...@@ -919,7 +919,7 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -919,7 +919,7 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void {
919 },919 },
920 });920 });
921 } else if (emit.bin_file.cast(link.File.Coff)) |coff_file| {921 } else if (emit.bin_file.cast(link.File.Coff)) |coff_file| {
922 const atom = coff_file.getAtomForSymbol(.{ .sym_index = data.atom_index, .file = null }).?;922 const atom_index = coff_file.getAtomIndexForSymbol(.{ .sym_index = data.atom_index, .file = null }).?;
923 const target = switch (tag) {923 const target = switch (tag) {
924 .load_memory_got,924 .load_memory_got,
925 .load_memory_ptr_got,925 .load_memory_ptr_got,
...@@ -929,7 +929,7 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -929,7 +929,7 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void {
929 .load_memory_import => coff_file.getGlobalByIndex(data.sym_index),929 .load_memory_import => coff_file.getGlobalByIndex(data.sym_index),
930 else => unreachable,930 else => unreachable,
931 };931 };
932 try atom.addRelocation(coff_file, .{932 try link.File.Coff.Atom.addRelocation(coff_file, atom_index, .{
933 .target = target,933 .target = target,
934 .offset = offset,934 .offset = offset,
935 .addend = 0,935 .addend = 0,
...@@ -946,7 +946,7 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -946,7 +946,7 @@ fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void {
946 else => unreachable,946 else => unreachable,
947 },947 },
948 });948 });
949 try atom.addRelocation(coff_file, .{949 try link.File.Coff.Atom.addRelocation(coff_file, atom_index, .{
950 .target = target,950 .target = target,
951 .offset = offset + 4,951 .offset = offset + 4,
952 .addend = 0,952 .addend = 0,
src/arch/arm/CodeGen.zig+65-81
...@@ -23,7 +23,7 @@ const leb128 = std.leb;...@@ -23,7 +23,7 @@ const leb128 = std.leb;
23const log = std.log.scoped(.codegen);23const log = std.log.scoped(.codegen);
24const build_options = @import("build_options");24const build_options = @import("build_options");
2525
26const FnResult = codegen.FnResult;26const Result = codegen.Result;
27const GenerateSymbolError = codegen.GenerateSymbolError;27const GenerateSymbolError = codegen.GenerateSymbolError;
28const DebugInfoOutput = codegen.DebugInfoOutput;28const DebugInfoOutput = codegen.DebugInfoOutput;
2929
...@@ -282,13 +282,7 @@ const DbgInfoReloc = struct {...@@ -282,13 +282,7 @@ const DbgInfoReloc = struct {
282 else => unreachable, // not a possible argument282 else => unreachable, // not a possible argument
283 };283 };
284284
285 try dw.genArgDbgInfo(285 try dw.genArgDbgInfo(reloc.name, reloc.ty, function.mod_fn.owner_decl, loc);
286 reloc.name,
287 reloc.ty,
288 function.bin_file.tag,
289 function.mod_fn.owner_decl,
290 loc,
291 );
292 },286 },
293 .plan9 => {},287 .plan9 => {},
294 .none => {},288 .none => {},
...@@ -331,14 +325,7 @@ const DbgInfoReloc = struct {...@@ -331,14 +325,7 @@ const DbgInfoReloc = struct {
331 break :blk .nop;325 break :blk .nop;
332 },326 },
333 };327 };
334 try dw.genVarDbgInfo(328 try dw.genVarDbgInfo(reloc.name, reloc.ty, function.mod_fn.owner_decl, is_ptr, loc);
335 reloc.name,
336 reloc.ty,
337 function.bin_file.tag,
338 function.mod_fn.owner_decl,
339 is_ptr,
340 loc,
341 );
342 },329 },
343 .plan9 => {},330 .plan9 => {},
344 .none => {},331 .none => {},
...@@ -356,7 +343,7 @@ pub fn generate(...@@ -356,7 +343,7 @@ pub fn generate(
356 liveness: Liveness,343 liveness: Liveness,
357 code: *std.ArrayList(u8),344 code: *std.ArrayList(u8),
358 debug_output: DebugInfoOutput,345 debug_output: DebugInfoOutput,
359) GenerateSymbolError!FnResult {346) GenerateSymbolError!Result {
360 if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) {347 if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) {
361 @panic("Attempted to compile for architecture that was disabled by build configuration");348 @panic("Attempted to compile for architecture that was disabled by build configuration");
362 }349 }
...@@ -399,8 +386,8 @@ pub fn generate(...@@ -399,8 +386,8 @@ pub fn generate(
399 defer function.dbg_info_relocs.deinit(bin_file.allocator);386 defer function.dbg_info_relocs.deinit(bin_file.allocator);
400387
401 var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) {388 var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) {
402 error.CodegenFail => return FnResult{ .fail = function.err_msg.? },389 error.CodegenFail => return Result{ .fail = function.err_msg.? },
403 error.OutOfRegisters => return FnResult{390 error.OutOfRegisters => return Result{
404 .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),391 .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
405 },392 },
406 else => |e| return e,393 else => |e| return e,
...@@ -413,8 +400,8 @@ pub fn generate(...@@ -413,8 +400,8 @@ pub fn generate(
413 function.max_end_stack = call_info.stack_byte_count;400 function.max_end_stack = call_info.stack_byte_count;
414401
415 function.gen() catch |err| switch (err) {402 function.gen() catch |err| switch (err) {
416 error.CodegenFail => return FnResult{ .fail = function.err_msg.? },403 error.CodegenFail => return Result{ .fail = function.err_msg.? },
417 error.OutOfRegisters => return FnResult{404 error.OutOfRegisters => return Result{
418 .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),405 .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
419 },406 },
420 else => |e| return e,407 else => |e| return e,
...@@ -446,14 +433,14 @@ pub fn generate(...@@ -446,14 +433,14 @@ pub fn generate(
446 defer emit.deinit();433 defer emit.deinit();
447434
448 emit.emitMir() catch |err| switch (err) {435 emit.emitMir() catch |err| switch (err) {
449 error.EmitFail => return FnResult{ .fail = emit.err_msg.? },436 error.EmitFail => return Result{ .fail = emit.err_msg.? },
450 else => |e| return e,437 else => |e| return e,
451 };438 };
452439
453 if (function.err_msg) |em| {440 if (function.err_msg) |em| {
454 return FnResult{ .fail = em };441 return Result{ .fail = em };
455 } else {442 } else {
456 return FnResult{ .appended = {} };443 return Result.ok;
457 }444 }
458}445}
459446
...@@ -4253,59 +4240,56 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier...@@ -4253,59 +4240,56 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
42534240
4254 // Due to incremental compilation, how function calls are generated depends4241 // Due to incremental compilation, how function calls are generated depends
4255 // on linking.4242 // on linking.
4256 switch (self.bin_file.tag) {4243 if (self.air.value(callee)) |func_value| {
4257 .elf => {4244 if (func_value.castTag(.function)) |func_payload| {
4258 if (self.air.value(callee)) |func_value| {4245 const func = func_payload.data;
4259 if (func_value.castTag(.function)) |func_payload| {4246
4260 const func = func_payload.data;4247 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
4261 const ptr_bits = self.target.cpu.arch.ptrBitWidth();4248 const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl);
4262 const ptr_bytes: u64 = @divExact(ptr_bits, 8);4249 const atom = elf_file.getAtom(atom_index);
4263 const mod = self.bin_file.options.module.?;4250 const got_addr = @intCast(u32, atom.getOffsetTableAddress(elf_file));
4264 const fn_owner_decl = mod.declPtr(func.owner_decl);4251 try self.genSetReg(Type.initTag(.usize), .lr, .{ .memory = got_addr });
4265 const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: {4252 } else if (self.bin_file.cast(link.File.MachO)) |_| {
4266 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];4253 unreachable; // unsupported architecture for MachO
4267 break :blk @intCast(u32, got.p_vaddr + fn_owner_decl.link.elf.offset_table_index * ptr_bytes);
4268 } else unreachable;
4269 try self.genSetReg(Type.initTag(.usize), .lr, .{ .memory = got_addr });
4270 } else if (func_value.castTag(.extern_fn)) |_| {
4271 return self.fail("TODO implement calling extern functions", .{});
4272 } else {
4273 return self.fail("TODO implement calling bitcasted functions", .{});
4274 }
4275 } else {4254 } else {
4276 assert(ty.zigTypeTag() == .Pointer);4255 return self.fail("TODO implement call on {s} for {s}", .{
4277 const mcv = try self.resolveInst(callee);4256 @tagName(self.bin_file.tag),
42784257 @tagName(self.target.cpu.arch),
4279 try self.genSetReg(Type.initTag(.usize), .lr, mcv);
4280 }
4281
4282 // TODO: add Instruction.supportedOn
4283 // function for ARM
4284 if (Target.arm.featureSetHas(self.target.cpu.features, .has_v5t)) {
4285 _ = try self.addInst(.{
4286 .tag = .blx,
4287 .data = .{ .reg = .lr },
4288 });4258 });
4289 } else {
4290 return self.fail("TODO fix blx emulation for ARM <v5", .{});
4291 // _ = try self.addInst(.{
4292 // .tag = .mov,
4293 // .data = .{ .rr_op = .{
4294 // .rd = .lr,
4295 // .rn = .r0,
4296 // .op = Instruction.Operand.reg(.pc, Instruction.Operand.Shift.none),
4297 // } },
4298 // });
4299 // _ = try self.addInst(.{
4300 // .tag = .bx,
4301 // .data = .{ .reg = .lr },
4302 // });
4303 }4259 }
4304 },4260 } else if (func_value.castTag(.extern_fn)) |_| {
4305 .macho => unreachable, // unsupported architecture for MachO4261 return self.fail("TODO implement calling extern functions", .{});
4306 .coff => return self.fail("TODO implement call in COFF for {}", .{self.target.cpu.arch}),4262 } else {
4307 .plan9 => return self.fail("TODO implement call on plan9 for {}", .{self.target.cpu.arch}),4263 return self.fail("TODO implement calling bitcasted functions", .{});
4308 else => unreachable,4264 }
4265 } else {
4266 assert(ty.zigTypeTag() == .Pointer);
4267 const mcv = try self.resolveInst(callee);
4268
4269 try self.genSetReg(Type.initTag(.usize), .lr, mcv);
4270 }
4271
4272 // TODO: add Instruction.supportedOn
4273 // function for ARM
4274 if (Target.arm.featureSetHas(self.target.cpu.features, .has_v5t)) {
4275 _ = try self.addInst(.{
4276 .tag = .blx,
4277 .data = .{ .reg = .lr },
4278 });
4279 } else {
4280 return self.fail("TODO fix blx emulation for ARM <v5", .{});
4281 // _ = try self.addInst(.{
4282 // .tag = .mov,
4283 // .data = .{ .rr_op = .{
4284 // .rd = .lr,
4285 // .rn = .r0,
4286 // .op = Instruction.Operand.reg(.pc, Instruction.Operand.Shift.none),
4287 // } },
4288 // });
4289 // _ = try self.addInst(.{
4290 // .tag = .bx,
4291 // .data = .{ .reg = .lr },
4292 // });
4309 }4293 }
43104294
4311 const result: MCValue = result: {4295 const result: MCValue = result: {
...@@ -6086,16 +6070,17 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne...@@ -6086,16 +6070,17 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne
6086 mod.markDeclAlive(decl);6070 mod.markDeclAlive(decl);
60876071
6088 if (self.bin_file.cast(link.File.Elf)) |elf_file| {6072 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
6089 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];6073 const atom_index = try elf_file.getOrCreateAtomForDecl(decl_index);
6090 const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes;6074 const atom = elf_file.getAtom(atom_index);
6091 return MCValue{ .memory = got_addr };6075 return MCValue{ .memory = atom.getOffsetTableAddress(elf_file) };
6092 } else if (self.bin_file.cast(link.File.MachO)) |_| {6076 } else if (self.bin_file.cast(link.File.MachO)) |_| {
6093 unreachable; // unsupported architecture for MachO6077 unreachable; // unsupported architecture for MachO
6094 } else if (self.bin_file.cast(link.File.Coff)) |_| {6078 } else if (self.bin_file.cast(link.File.Coff)) |_| {
6095 return self.fail("TODO codegen COFF const Decl pointer", .{});6079 return self.fail("TODO codegen COFF const Decl pointer", .{});
6096 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {6080 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
6097 try p9.seeDecl(decl_index);6081 const decl_block_index = try p9.seeDecl(decl_index);
6098 const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes;6082 const decl_block = p9.getDeclBlock(decl_block_index);
6083 const got_addr = p9.bases.data + decl_block.got_index.? * ptr_bytes;
6099 return MCValue{ .memory = got_addr };6084 return MCValue{ .memory = got_addr };
6100 } else {6085 } else {
6101 return self.fail("TODO codegen non-ELF const Decl pointer", .{});6086 return self.fail("TODO codegen non-ELF const Decl pointer", .{});
...@@ -6109,8 +6094,7 @@ fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue {...@@ -6109,8 +6094,7 @@ fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue {
6109 return self.fail("lowering unnamed constant failed: {s}", .{@errorName(err)});6094 return self.fail("lowering unnamed constant failed: {s}", .{@errorName(err)});
6110 };6095 };
6111 if (self.bin_file.cast(link.File.Elf)) |elf_file| {6096 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
6112 const vaddr = elf_file.local_symbols.items[local_sym_index].st_value;6097 return MCValue{ .memory = elf_file.getSymbol(local_sym_index).st_value };
6113 return MCValue{ .memory = vaddr };
6114 } else if (self.bin_file.cast(link.File.MachO)) |_| {6098 } else if (self.bin_file.cast(link.File.MachO)) |_| {
6115 unreachable;6099 unreachable;
6116 } else if (self.bin_file.cast(link.File.Coff)) |_| {6100 } else if (self.bin_file.cast(link.File.Coff)) |_| {
src/arch/riscv64/CodeGen.zig+22-34
...@@ -22,7 +22,7 @@ const leb128 = std.leb;...@@ -22,7 +22,7 @@ const leb128 = std.leb;
22const log = std.log.scoped(.codegen);22const log = std.log.scoped(.codegen);
23const build_options = @import("build_options");23const build_options = @import("build_options");
2424
25const FnResult = @import("../../codegen.zig").FnResult;25const Result = @import("../../codegen.zig").Result;
26const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError;26const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError;
27const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;27const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;
2828
...@@ -225,7 +225,7 @@ pub fn generate(...@@ -225,7 +225,7 @@ pub fn generate(
225 liveness: Liveness,225 liveness: Liveness,
226 code: *std.ArrayList(u8),226 code: *std.ArrayList(u8),
227 debug_output: DebugInfoOutput,227 debug_output: DebugInfoOutput,
228) GenerateSymbolError!FnResult {228) GenerateSymbolError!Result {
229 if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) {229 if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) {
230 @panic("Attempted to compile for architecture that was disabled by build configuration");230 @panic("Attempted to compile for architecture that was disabled by build configuration");
231 }231 }
...@@ -268,8 +268,8 @@ pub fn generate(...@@ -268,8 +268,8 @@ pub fn generate(
268 defer function.exitlude_jump_relocs.deinit(bin_file.allocator);268 defer function.exitlude_jump_relocs.deinit(bin_file.allocator);
269269
270 var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) {270 var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) {
271 error.CodegenFail => return FnResult{ .fail = function.err_msg.? },271 error.CodegenFail => return Result{ .fail = function.err_msg.? },
272 error.OutOfRegisters => return FnResult{272 error.OutOfRegisters => return Result{
273 .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),273 .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
274 },274 },
275 else => |e| return e,275 else => |e| return e,
...@@ -282,8 +282,8 @@ pub fn generate(...@@ -282,8 +282,8 @@ pub fn generate(
282 function.max_end_stack = call_info.stack_byte_count;282 function.max_end_stack = call_info.stack_byte_count;
283283
284 function.gen() catch |err| switch (err) {284 function.gen() catch |err| switch (err) {
285 error.CodegenFail => return FnResult{ .fail = function.err_msg.? },285 error.CodegenFail => return Result{ .fail = function.err_msg.? },
286 error.OutOfRegisters => return FnResult{286 error.OutOfRegisters => return Result{
287 .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),287 .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
288 },288 },
289 else => |e| return e,289 else => |e| return e,
...@@ -309,14 +309,14 @@ pub fn generate(...@@ -309,14 +309,14 @@ pub fn generate(
309 defer emit.deinit();309 defer emit.deinit();
310310
311 emit.emitMir() catch |err| switch (err) {311 emit.emitMir() catch |err| switch (err) {
312 error.EmitFail => return FnResult{ .fail = emit.err_msg.? },312 error.EmitFail => return Result{ .fail = emit.err_msg.? },
313 else => |e| return e,313 else => |e| return e,
314 };314 };
315315
316 if (function.err_msg) |em| {316 if (function.err_msg) |em| {
317 return FnResult{ .fail = em };317 return Result{ .fail = em };
318 } else {318 } else {
319 return FnResult{ .appended = {} };319 return Result.ok;
320 }320 }
321}321}
322322
...@@ -1615,13 +1615,9 @@ fn genArgDbgInfo(self: Self, inst: Air.Inst.Index, mcv: MCValue) !void {...@@ -1615,13 +1615,9 @@ fn genArgDbgInfo(self: Self, inst: Air.Inst.Index, mcv: MCValue) !void {
16151615
1616 switch (self.debug_output) {1616 switch (self.debug_output) {
1617 .dwarf => |dw| switch (mcv) {1617 .dwarf => |dw| switch (mcv) {
1618 .register => |reg| try dw.genArgDbgInfo(1618 .register => |reg| try dw.genArgDbgInfo(name, ty, self.mod_fn.owner_decl, .{
1619 name,1619 .register = reg.dwarfLocOp(),
1620 ty,1620 }),
1621 self.bin_file.tag,
1622 self.mod_fn.owner_decl,
1623 .{ .register = reg.dwarfLocOp() },
1624 ),
1625 .stack_offset => {},1621 .stack_offset => {},
1626 else => {},1622 else => {},
1627 },1623 },
...@@ -1721,16 +1717,9 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier...@@ -1721,16 +1717,9 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
1721 if (self.air.value(callee)) |func_value| {1717 if (self.air.value(callee)) |func_value| {
1722 if (func_value.castTag(.function)) |func_payload| {1718 if (func_value.castTag(.function)) |func_payload| {
1723 const func = func_payload.data;1719 const func = func_payload.data;
17241720 const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl);
1725 const ptr_bits = self.target.cpu.arch.ptrBitWidth();1721 const atom = elf_file.getAtom(atom_index);
1726 const ptr_bytes: u64 = @divExact(ptr_bits, 8);1722 const got_addr = @intCast(u32, atom.getOffsetTableAddress(elf_file));
1727 const mod = self.bin_file.options.module.?;
1728 const fn_owner_decl = mod.declPtr(func.owner_decl);
1729 const got_addr = blk: {
1730 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
1731 break :blk @intCast(u32, got.p_vaddr + fn_owner_decl.link.elf.offset_table_index * ptr_bytes);
1732 };
1733
1734 try self.genSetReg(Type.initTag(.usize), .ra, .{ .memory = got_addr });1723 try self.genSetReg(Type.initTag(.usize), .ra, .{ .memory = got_addr });
1735 _ = try self.addInst(.{1724 _ = try self.addInst(.{
1736 .tag = .jalr,1725 .tag = .jalr,
...@@ -2557,18 +2546,17 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne...@@ -2557,18 +2546,17 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne
2557 const decl = mod.declPtr(decl_index);2546 const decl = mod.declPtr(decl_index);
2558 mod.markDeclAlive(decl);2547 mod.markDeclAlive(decl);
2559 if (self.bin_file.cast(link.File.Elf)) |elf_file| {2548 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
2560 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];2549 const atom_index = try elf_file.getOrCreateAtomForDecl(decl_index);
2561 const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes;2550 const atom = elf_file.getAtom(atom_index);
2562 return MCValue{ .memory = got_addr };2551 return MCValue{ .memory = atom.getOffsetTableAddress(elf_file) };
2563 } else if (self.bin_file.cast(link.File.MachO)) |_| {2552 } else if (self.bin_file.cast(link.File.MachO)) |_| {
2564 // TODO I'm hacking my way through here by repurposing .memory for storing2553 unreachable;
2565 // index to the GOT target symbol index.
2566 return MCValue{ .memory = decl.link.macho.sym_index };
2567 } else if (self.bin_file.cast(link.File.Coff)) |_| {2554 } else if (self.bin_file.cast(link.File.Coff)) |_| {
2568 return self.fail("TODO codegen COFF const Decl pointer", .{});2555 return self.fail("TODO codegen COFF const Decl pointer", .{});
2569 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {2556 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
2570 try p9.seeDecl(decl_index);2557 const decl_block_index = try p9.seeDecl(decl_index);
2571 const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes;2558 const decl_block = p9.getDeclBlock(decl_block_index);
2559 const got_addr = p9.bases.data + decl_block.got_index.? * ptr_bytes;
2572 return MCValue{ .memory = got_addr };2560 return MCValue{ .memory = got_addr };
2573 } else {2561 } else {
2574 return self.fail("TODO codegen non-ELF const Decl pointer", .{});2562 return self.fail("TODO codegen non-ELF const Decl pointer", .{});
src/arch/sparc64/CodeGen.zig+18-27
...@@ -20,7 +20,7 @@ const Emit = @import("Emit.zig");...@@ -20,7 +20,7 @@ const Emit = @import("Emit.zig");
20const Liveness = @import("../../Liveness.zig");20const Liveness = @import("../../Liveness.zig");
21const Type = @import("../../type.zig").Type;21const Type = @import("../../type.zig").Type;
22const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError;22const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError;
23const FnResult = @import("../../codegen.zig").FnResult;23const Result = @import("../../codegen.zig").Result;
24const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;24const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;
2525
26const build_options = @import("build_options");26const build_options = @import("build_options");
...@@ -265,7 +265,7 @@ pub fn generate(...@@ -265,7 +265,7 @@ pub fn generate(
265 liveness: Liveness,265 liveness: Liveness,
266 code: *std.ArrayList(u8),266 code: *std.ArrayList(u8),
267 debug_output: DebugInfoOutput,267 debug_output: DebugInfoOutput,
268) GenerateSymbolError!FnResult {268) GenerateSymbolError!Result {
269 if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) {269 if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) {
270 @panic("Attempted to compile for architecture that was disabled by build configuration");270 @panic("Attempted to compile for architecture that was disabled by build configuration");
271 }271 }
...@@ -310,8 +310,8 @@ pub fn generate(...@@ -310,8 +310,8 @@ pub fn generate(
310 defer function.exitlude_jump_relocs.deinit(bin_file.allocator);310 defer function.exitlude_jump_relocs.deinit(bin_file.allocator);
311311
312 var call_info = function.resolveCallingConventionValues(fn_type, .callee) catch |err| switch (err) {312 var call_info = function.resolveCallingConventionValues(fn_type, .callee) catch |err| switch (err) {
313 error.CodegenFail => return FnResult{ .fail = function.err_msg.? },313 error.CodegenFail => return Result{ .fail = function.err_msg.? },
314 error.OutOfRegisters => return FnResult{314 error.OutOfRegisters => return Result{
315 .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),315 .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
316 },316 },
317 else => |e| return e,317 else => |e| return e,
...@@ -324,8 +324,8 @@ pub fn generate(...@@ -324,8 +324,8 @@ pub fn generate(
324 function.max_end_stack = call_info.stack_byte_count;324 function.max_end_stack = call_info.stack_byte_count;
325325
326 function.gen() catch |err| switch (err) {326 function.gen() catch |err| switch (err) {
327 error.CodegenFail => return FnResult{ .fail = function.err_msg.? },327 error.CodegenFail => return Result{ .fail = function.err_msg.? },
328 error.OutOfRegisters => return FnResult{328 error.OutOfRegisters => return Result{
329 .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),329 .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
330 },330 },
331 else => |e| return e,331 else => |e| return e,
...@@ -351,14 +351,14 @@ pub fn generate(...@@ -351,14 +351,14 @@ pub fn generate(
351 defer emit.deinit();351 defer emit.deinit();
352352
353 emit.emitMir() catch |err| switch (err) {353 emit.emitMir() catch |err| switch (err) {
354 error.EmitFail => return FnResult{ .fail = emit.err_msg.? },354 error.EmitFail => return Result{ .fail = emit.err_msg.? },
355 else => |e| return e,355 else => |e| return e,
356 };356 };
357357
358 if (function.err_msg) |em| {358 if (function.err_msg) |em| {
359 return FnResult{ .fail = em };359 return Result{ .fail = em };
360 } else {360 } else {
361 return FnResult{ .appended = {} };361 return Result.ok;
362 }362 }
363}363}
364364
...@@ -1216,12 +1216,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier...@@ -1216,12 +1216,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
1216 if (self.bin_file.tag == link.File.Elf.base_tag) {1216 if (self.bin_file.tag == link.File.Elf.base_tag) {
1217 if (func_value.castTag(.function)) |func_payload| {1217 if (func_value.castTag(.function)) |func_payload| {
1218 const func = func_payload.data;1218 const func = func_payload.data;
1219 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
1220 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
1221 const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: {1219 const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: {
1222 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];1220 const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl);
1223 const mod = self.bin_file.options.module.?;1221 const atom = elf_file.getAtom(atom_index);
1224 break :blk @intCast(u32, got.p_vaddr + mod.declPtr(func.owner_decl).link.elf.offset_table_index * ptr_bytes);1222 break :blk @intCast(u32, atom.getOffsetTableAddress(elf_file));
1225 } else unreachable;1223 } else unreachable;
12261224
1227 try self.genSetReg(Type.initTag(.usize), .o7, .{ .memory = got_addr });1225 try self.genSetReg(Type.initTag(.usize), .o7, .{ .memory = got_addr });
...@@ -3414,13 +3412,9 @@ fn genArgDbgInfo(self: Self, inst: Air.Inst.Index, mcv: MCValue) !void {...@@ -3414,13 +3412,9 @@ fn genArgDbgInfo(self: Self, inst: Air.Inst.Index, mcv: MCValue) !void {
34143412
3415 switch (self.debug_output) {3413 switch (self.debug_output) {
3416 .dwarf => |dw| switch (mcv) {3414 .dwarf => |dw| switch (mcv) {
3417 .register => |reg| try dw.genArgDbgInfo(3415 .register => |reg| try dw.genArgDbgInfo(name, ty, self.mod_fn.owner_decl, .{
3418 name,3416 .register = reg.dwarfLocOp(),
3419 ty,3417 }),
3420 self.bin_file.tag,
3421 self.mod_fn.owner_decl,
3422 .{ .register = reg.dwarfLocOp() },
3423 ),
3424 else => {},3418 else => {},
3425 },3419 },
3426 else => {},3420 else => {},
...@@ -4193,9 +4187,6 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo...@@ -4193,9 +4187,6 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
4193}4187}
41944188
4195fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) InnerError!MCValue {4189fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) InnerError!MCValue {
4196 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
4197 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
4198
4199 // TODO this feels clunky. Perhaps we should check for it in `genTypedValue`?4190 // TODO this feels clunky. Perhaps we should check for it in `genTypedValue`?
4200 if (tv.ty.zigTypeTag() == .Pointer) blk: {4191 if (tv.ty.zigTypeTag() == .Pointer) blk: {
4201 if (tv.ty.castPtrToFn()) |_| break :blk;4192 if (tv.ty.castPtrToFn()) |_| break :blk;
...@@ -4209,9 +4200,9 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne...@@ -4209,9 +4200,9 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne
42094200
4210 mod.markDeclAlive(decl);4201 mod.markDeclAlive(decl);
4211 if (self.bin_file.cast(link.File.Elf)) |elf_file| {4202 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
4212 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];4203 const atom_index = try elf_file.getOrCreateAtomForDecl(decl_index);
4213 const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes;4204 const atom = elf_file.getAtom(atom_index);
4214 return MCValue{ .memory = got_addr };4205 return MCValue{ .memory = atom.getOffsetTableAddress(elf_file) };
4215 } else {4206 } else {
4216 return self.fail("TODO codegen non-ELF const Decl pointer", .{});4207 return self.fail("TODO codegen non-ELF const Decl pointer", .{});
4217 }4208 }
src/arch/wasm/CodeGen.zig+36-28
...@@ -627,13 +627,6 @@ test "Wasm - buildOpcode" {...@@ -627,13 +627,6 @@ test "Wasm - buildOpcode" {
627 try testing.expectEqual(@as(wasm.Opcode, .f64_reinterpret_i64), f64_reinterpret_i64);627 try testing.expectEqual(@as(wasm.Opcode, .f64_reinterpret_i64), f64_reinterpret_i64);
628}628}
629629
630pub const Result = union(enum) {
631 /// The codegen bytes have been appended to `Context.code`
632 appended: void,
633 /// The data is managed externally and are part of the `Result`
634 externally_managed: []const u8,
635};
636
637/// Hashmap to store generated `WValue` for each `Air.Inst.Ref`630/// Hashmap to store generated `WValue` for each `Air.Inst.Ref`
638pub const ValueTable = std.AutoArrayHashMapUnmanaged(Air.Inst.Ref, WValue);631pub const ValueTable = std.AutoArrayHashMapUnmanaged(Air.Inst.Ref, WValue);
639632
...@@ -1171,7 +1164,7 @@ pub fn generate(...@@ -1171,7 +1164,7 @@ pub fn generate(
1171 liveness: Liveness,1164 liveness: Liveness,
1172 code: *std.ArrayList(u8),1165 code: *std.ArrayList(u8),
1173 debug_output: codegen.DebugInfoOutput,1166 debug_output: codegen.DebugInfoOutput,
1174) codegen.GenerateSymbolError!codegen.FnResult {1167) codegen.GenerateSymbolError!codegen.Result {
1175 _ = src_loc;1168 _ = src_loc;
1176 var code_gen: CodeGen = .{1169 var code_gen: CodeGen = .{
1177 .gpa = bin_file.allocator,1170 .gpa = bin_file.allocator,
...@@ -1190,18 +1183,18 @@ pub fn generate(...@@ -1190,18 +1183,18 @@ pub fn generate(
1190 defer code_gen.deinit();1183 defer code_gen.deinit();
11911184
1192 genFunc(&code_gen) catch |err| switch (err) {1185 genFunc(&code_gen) catch |err| switch (err) {
1193 error.CodegenFail => return codegen.FnResult{ .fail = code_gen.err_msg },1186 error.CodegenFail => return codegen.Result{ .fail = code_gen.err_msg },
1194 else => |e| return e,1187 else => |e| return e,
1195 };1188 };
11961189
1197 return codegen.FnResult{ .appended = {} };1190 return codegen.Result.ok;
1198}1191}
11991192
1200fn genFunc(func: *CodeGen) InnerError!void {1193fn genFunc(func: *CodeGen) InnerError!void {
1201 const fn_info = func.decl.ty.fnInfo();1194 const fn_info = func.decl.ty.fnInfo();
1202 var func_type = try genFunctype(func.gpa, fn_info.cc, fn_info.param_types, fn_info.return_type, func.target);1195 var func_type = try genFunctype(func.gpa, fn_info.cc, fn_info.param_types, fn_info.return_type, func.target);
1203 defer func_type.deinit(func.gpa);1196 defer func_type.deinit(func.gpa);
1204 func.decl.fn_link.wasm.type_index = try func.bin_file.putOrGetFuncType(func_type);1197 func.decl.fn_link.?.type_index = try func.bin_file.putOrGetFuncType(func_type);
12051198
1206 var cc_result = try func.resolveCallingConventionValues(func.decl.ty);1199 var cc_result = try func.resolveCallingConventionValues(func.decl.ty);
1207 defer cc_result.deinit(func.gpa);1200 defer cc_result.deinit(func.gpa);
...@@ -1276,10 +1269,10 @@ fn genFunc(func: *CodeGen) InnerError!void {...@@ -1276,10 +1269,10 @@ fn genFunc(func: *CodeGen) InnerError!void {
12761269
1277 var emit: Emit = .{1270 var emit: Emit = .{
1278 .mir = mir,1271 .mir = mir,
1279 .bin_file = &func.bin_file.base,1272 .bin_file = func.bin_file,
1280 .code = func.code,1273 .code = func.code,
1281 .locals = func.locals.items,1274 .locals = func.locals.items,
1282 .decl = func.decl,1275 .decl_index = func.decl_index,
1283 .dbg_output = func.debug_output,1276 .dbg_output = func.debug_output,
1284 .prev_di_line = 0,1277 .prev_di_line = 0,
1285 .prev_di_column = 0,1278 .prev_di_column = 0,
...@@ -1713,9 +1706,11 @@ fn isByRef(ty: Type, target: std.Target) bool {...@@ -1713,9 +1706,11 @@ fn isByRef(ty: Type, target: std.Target) bool {
1713 return true;1706 return true;
1714 },1707 },
1715 .Optional => {1708 .Optional => {
1716 if (ty.optionalReprIsPayload()) return false;1709 if (ty.isPtrLikeOptional()) return false;
1717 var buf: Type.Payload.ElemType = undefined;1710 var buf: Type.Payload.ElemType = undefined;
1718 return ty.optionalChild(&buf).hasRuntimeBitsIgnoreComptime();1711 const pl_type = ty.optionalChild(&buf);
1712 if (pl_type.zigTypeTag() == .ErrorSet) return false;
1713 return pl_type.hasRuntimeBitsIgnoreComptime();
1719 },1714 },
1720 .Pointer => {1715 .Pointer => {
1721 // Slices act like struct and will be passed by reference1716 // Slices act like struct and will be passed by reference
...@@ -2122,27 +2117,31 @@ fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModif...@@ -2122,27 +2117,31 @@ fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModif
2122 const fn_info = fn_ty.fnInfo();2117 const fn_info = fn_ty.fnInfo();
2123 const first_param_sret = firstParamSRet(fn_info.cc, fn_info.return_type, func.target);2118 const first_param_sret = firstParamSRet(fn_info.cc, fn_info.return_type, func.target);
21242119
2125 const callee: ?*Decl = blk: {2120 const callee: ?Decl.Index = blk: {
2126 const func_val = func.air.value(pl_op.operand) orelse break :blk null;2121 const func_val = func.air.value(pl_op.operand) orelse break :blk null;
2127 const module = func.bin_file.base.options.module.?;2122 const module = func.bin_file.base.options.module.?;
21282123
2129 if (func_val.castTag(.function)) |function| {2124 if (func_val.castTag(.function)) |function| {
2130 break :blk module.declPtr(function.data.owner_decl);2125 _ = try func.bin_file.getOrCreateAtomForDecl(function.data.owner_decl);
2126 break :blk function.data.owner_decl;
2131 } else if (func_val.castTag(.extern_fn)) |extern_fn| {2127 } else if (func_val.castTag(.extern_fn)) |extern_fn| {
2132 const ext_decl = module.declPtr(extern_fn.data.owner_decl);2128 const ext_decl = module.declPtr(extern_fn.data.owner_decl);
2133 const ext_info = ext_decl.ty.fnInfo();2129 const ext_info = ext_decl.ty.fnInfo();
2134 var func_type = try genFunctype(func.gpa, ext_info.cc, ext_info.param_types, ext_info.return_type, func.target);2130 var func_type = try genFunctype(func.gpa, ext_info.cc, ext_info.param_types, ext_info.return_type, func.target);
2135 defer func_type.deinit(func.gpa);2131 defer func_type.deinit(func.gpa);
2136 ext_decl.fn_link.wasm.type_index = try func.bin_file.putOrGetFuncType(func_type);2132 const atom_index = try func.bin_file.getOrCreateAtomForDecl(extern_fn.data.owner_decl);
2133 const atom = func.bin_file.getAtomPtr(atom_index);
2134 ext_decl.fn_link.?.type_index = try func.bin_file.putOrGetFuncType(func_type);
2137 try func.bin_file.addOrUpdateImport(2135 try func.bin_file.addOrUpdateImport(
2138 mem.sliceTo(ext_decl.name, 0),2136 mem.sliceTo(ext_decl.name, 0),
2139 ext_decl.link.wasm.sym_index,2137 atom.getSymbolIndex().?,
2140 ext_decl.getExternFn().?.lib_name,2138 ext_decl.getExternFn().?.lib_name,
2141 ext_decl.fn_link.wasm.type_index,2139 ext_decl.fn_link.?.type_index,
2142 );2140 );
2143 break :blk ext_decl;2141 break :blk extern_fn.data.owner_decl;
2144 } else if (func_val.castTag(.decl_ref)) |decl_ref| {2142 } else if (func_val.castTag(.decl_ref)) |decl_ref| {
2145 break :blk module.declPtr(decl_ref.data);2143 _ = try func.bin_file.getOrCreateAtomForDecl(decl_ref.data);
2144 break :blk decl_ref.data;
2146 }2145 }
2147 return func.fail("Expected a function, but instead found type '{}'", .{func_val.tag()});2146 return func.fail("Expected a function, but instead found type '{}'", .{func_val.tag()});
2148 };2147 };
...@@ -2163,7 +2162,8 @@ fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModif...@@ -2163,7 +2162,8 @@ fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModif
2163 }2162 }
21642163
2165 if (callee) |direct| {2164 if (callee) |direct| {
2166 try func.addLabel(.call, direct.link.wasm.sym_index);2165 const atom_index = func.bin_file.decls.get(direct).?;
2166 try func.addLabel(.call, func.bin_file.getAtom(atom_index).sym_index);
2167 } else {2167 } else {
2168 // in this case we call a function pointer2168 // in this case we call a function pointer
2169 // so load its value onto the stack2169 // so load its value onto the stack
...@@ -2476,7 +2476,7 @@ fn airArg(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -2476,7 +2476,7 @@ fn airArg(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
2476 .dwarf => |dwarf| {2476 .dwarf => |dwarf| {
2477 const src_index = func.air.instructions.items(.data)[inst].arg.src_index;2477 const src_index = func.air.instructions.items(.data)[inst].arg.src_index;
2478 const name = func.mod_fn.getParamName(func.bin_file.base.options.module.?, src_index);2478 const name = func.mod_fn.getParamName(func.bin_file.base.options.module.?, src_index);
2479 try dwarf.genArgDbgInfo(name, arg_ty, .wasm, func.mod_fn.owner_decl, .{2479 try dwarf.genArgDbgInfo(name, arg_ty, func.mod_fn.owner_decl, .{
2480 .wasm_local = arg.local.value,2480 .wasm_local = arg.local.value,
2481 });2481 });
2482 },2482 },
...@@ -2759,8 +2759,10 @@ fn lowerDeclRefValue(func: *CodeGen, tv: TypedValue, decl_index: Module.Decl.Ind...@@ -2759,8 +2759,10 @@ fn lowerDeclRefValue(func: *CodeGen, tv: TypedValue, decl_index: Module.Decl.Ind
2759 }2759 }
27602760
2761 module.markDeclAlive(decl);2761 module.markDeclAlive(decl);
2762 const atom_index = try func.bin_file.getOrCreateAtomForDecl(decl_index);
2763 const atom = func.bin_file.getAtom(atom_index);
27622764
2763 const target_sym_index = decl.link.wasm.sym_index;2765 const target_sym_index = atom.sym_index;
2764 if (decl.ty.zigTypeTag() == .Fn) {2766 if (decl.ty.zigTypeTag() == .Fn) {
2765 try func.bin_file.addTableFunction(target_sym_index);2767 try func.bin_file.addTableFunction(target_sym_index);
2766 return WValue{ .function_index = target_sym_index };2768 return WValue{ .function_index = target_sym_index };
...@@ -3869,14 +3871,20 @@ fn airIsNull(func: *CodeGen, inst: Air.Inst.Index, opcode: wasm.Opcode, op_kind:...@@ -3869,14 +3871,20 @@ fn airIsNull(func: *CodeGen, inst: Air.Inst.Index, opcode: wasm.Opcode, op_kind:
3869/// NOTE: Leaves the result on the stack3871/// NOTE: Leaves the result on the stack
3870fn isNull(func: *CodeGen, operand: WValue, optional_ty: Type, opcode: wasm.Opcode) InnerError!WValue {3872fn isNull(func: *CodeGen, operand: WValue, optional_ty: Type, opcode: wasm.Opcode) InnerError!WValue {
3871 try func.emitWValue(operand);3873 try func.emitWValue(operand);
3874 var buf: Type.Payload.ElemType = undefined;
3875 const payload_ty = optional_ty.optionalChild(&buf);
3872 if (!optional_ty.optionalReprIsPayload()) {3876 if (!optional_ty.optionalReprIsPayload()) {
3873 var buf: Type.Payload.ElemType = undefined;
3874 const payload_ty = optional_ty.optionalChild(&buf);
3875 // When payload is zero-bits, we can treat operand as a value, rather than3877 // When payload is zero-bits, we can treat operand as a value, rather than
3876 // a pointer to the stack value3878 // a pointer to the stack value
3877 if (payload_ty.hasRuntimeBitsIgnoreComptime()) {3879 if (payload_ty.hasRuntimeBitsIgnoreComptime()) {
3878 try func.addMemArg(.i32_load8_u, .{ .offset = operand.offset(), .alignment = 1 });3880 try func.addMemArg(.i32_load8_u, .{ .offset = operand.offset(), .alignment = 1 });
3879 }3881 }
3882 } else if (payload_ty.isSlice()) {
3883 switch (func.arch()) {
3884 .wasm32 => try func.addMemArg(.i32_load, .{ .offset = operand.offset(), .alignment = 4 }),
3885 .wasm64 => try func.addMemArg(.i64_load, .{ .offset = operand.offset(), .alignment = 8 }),
3886 else => unreachable,
3887 }
3880 }3888 }
38813889
3882 // Compare the null value with '0'3890 // Compare the null value with '0'
...@@ -5539,7 +5547,7 @@ fn airDbgVar(func: *CodeGen, inst: Air.Inst.Index, is_ptr: bool) !void {...@@ -5539,7 +5547,7 @@ fn airDbgVar(func: *CodeGen, inst: Air.Inst.Index, is_ptr: bool) !void {
5539 break :blk .nop;5547 break :blk .nop;
5540 },5548 },
5541 };5549 };
5542 try func.debug_output.dwarf.genVarDbgInfo(name, ty, .wasm, func.mod_fn.owner_decl, is_ptr, loc);5550 try func.debug_output.dwarf.genVarDbgInfo(name, ty, func.mod_fn.owner_decl, is_ptr, loc);
55435551
5544 func.finishAir(inst, .none, &.{});5552 func.finishAir(inst, .none, &.{});
5545}5553}
src/arch/wasm/Emit.zig+18-11
...@@ -11,8 +11,8 @@ const leb128 = std.leb;...@@ -11,8 +11,8 @@ const leb128 = std.leb;
1111
12/// Contains our list of instructions12/// Contains our list of instructions
13mir: Mir,13mir: Mir,
14/// Reference to the file handler14/// Reference to the Wasm module linker
15bin_file: *link.File,15bin_file: *link.File.Wasm,
16/// Possible error message. When set, the value is allocated and16/// Possible error message. When set, the value is allocated and
17/// must be freed manually.17/// must be freed manually.
18error_msg: ?*Module.ErrorMsg = null,18error_msg: ?*Module.ErrorMsg = null,
...@@ -21,7 +21,7 @@ code: *std.ArrayList(u8),...@@ -21,7 +21,7 @@ code: *std.ArrayList(u8),
21/// List of allocated locals.21/// List of allocated locals.
22locals: []const u8,22locals: []const u8,
23/// The declaration that code is being generated for.23/// The declaration that code is being generated for.
24decl: *Module.Decl,24decl_index: Module.Decl.Index,
2525
26// Debug information26// Debug information
27/// Holds the debug information for this emission27/// Holds the debug information for this emission
...@@ -252,8 +252,8 @@ fn offset(self: Emit) u32 {...@@ -252,8 +252,8 @@ fn offset(self: Emit) u32 {
252fn fail(emit: *Emit, comptime format: []const u8, args: anytype) InnerError {252fn fail(emit: *Emit, comptime format: []const u8, args: anytype) InnerError {
253 @setCold(true);253 @setCold(true);
254 std.debug.assert(emit.error_msg == null);254 std.debug.assert(emit.error_msg == null);
255 // TODO: Determine the source location.255 const mod = emit.bin_file.base.options.module.?;
256 emit.error_msg = try Module.ErrorMsg.create(emit.bin_file.allocator, emit.decl.srcLoc(), format, args);256 emit.error_msg = try Module.ErrorMsg.create(emit.bin_file.base.allocator, mod.declPtr(emit.decl_index).srcLoc(), format, args);
257 return error.EmitFail;257 return error.EmitFail;
258}258}
259259
...@@ -304,8 +304,9 @@ fn emitGlobal(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) !void {...@@ -304,8 +304,9 @@ fn emitGlobal(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) !void {
304 const global_offset = emit.offset();304 const global_offset = emit.offset();
305 try emit.code.appendSlice(&buf);305 try emit.code.appendSlice(&buf);
306306
307 // globals can have index 0 as it represents the stack pointer307 const atom_index = emit.bin_file.decls.get(emit.decl_index).?;
308 try emit.decl.link.wasm.relocs.append(emit.bin_file.allocator, .{308 const atom = emit.bin_file.getAtomPtr(atom_index);
309 try atom.relocs.append(emit.bin_file.base.allocator, .{
309 .index = label,310 .index = label,
310 .offset = global_offset,311 .offset = global_offset,
311 .relocation_type = .R_WASM_GLOBAL_INDEX_LEB,312 .relocation_type = .R_WASM_GLOBAL_INDEX_LEB,
...@@ -361,7 +362,9 @@ fn emitCall(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -361,7 +362,9 @@ fn emitCall(emit: *Emit, inst: Mir.Inst.Index) !void {
361 try emit.code.appendSlice(&buf);362 try emit.code.appendSlice(&buf);
362363
363 if (label != 0) {364 if (label != 0) {
364 try emit.decl.link.wasm.relocs.append(emit.bin_file.allocator, .{365 const atom_index = emit.bin_file.decls.get(emit.decl_index).?;
366 const atom = emit.bin_file.getAtomPtr(atom_index);
367 try atom.relocs.append(emit.bin_file.base.allocator, .{
365 .offset = call_offset,368 .offset = call_offset,
366 .index = label,369 .index = label,
367 .relocation_type = .R_WASM_FUNCTION_INDEX_LEB,370 .relocation_type = .R_WASM_FUNCTION_INDEX_LEB,
...@@ -387,7 +390,9 @@ fn emitFunctionIndex(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -387,7 +390,9 @@ fn emitFunctionIndex(emit: *Emit, inst: Mir.Inst.Index) !void {
387 try emit.code.appendSlice(&buf);390 try emit.code.appendSlice(&buf);
388391
389 if (symbol_index != 0) {392 if (symbol_index != 0) {
390 try emit.decl.link.wasm.relocs.append(emit.bin_file.allocator, .{393 const atom_index = emit.bin_file.decls.get(emit.decl_index).?;
394 const atom = emit.bin_file.getAtomPtr(atom_index);
395 try atom.relocs.append(emit.bin_file.base.allocator, .{
391 .offset = index_offset,396 .offset = index_offset,
392 .index = symbol_index,397 .index = symbol_index,
393 .relocation_type = .R_WASM_TABLE_INDEX_SLEB,398 .relocation_type = .R_WASM_TABLE_INDEX_SLEB,
...@@ -399,7 +404,7 @@ fn emitMemAddress(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -399,7 +404,7 @@ fn emitMemAddress(emit: *Emit, inst: Mir.Inst.Index) !void {
399 const extra_index = emit.mir.instructions.items(.data)[inst].payload;404 const extra_index = emit.mir.instructions.items(.data)[inst].payload;
400 const mem = emit.mir.extraData(Mir.Memory, extra_index).data;405 const mem = emit.mir.extraData(Mir.Memory, extra_index).data;
401 const mem_offset = emit.offset() + 1;406 const mem_offset = emit.offset() + 1;
402 const is_wasm32 = emit.bin_file.options.target.cpu.arch == .wasm32;407 const is_wasm32 = emit.bin_file.base.options.target.cpu.arch == .wasm32;
403 if (is_wasm32) {408 if (is_wasm32) {
404 try emit.code.append(std.wasm.opcode(.i32_const));409 try emit.code.append(std.wasm.opcode(.i32_const));
405 var buf: [5]u8 = undefined;410 var buf: [5]u8 = undefined;
...@@ -413,7 +418,9 @@ fn emitMemAddress(emit: *Emit, inst: Mir.Inst.Index) !void {...@@ -413,7 +418,9 @@ fn emitMemAddress(emit: *Emit, inst: Mir.Inst.Index) !void {
413 }418 }
414419
415 if (mem.pointer != 0) {420 if (mem.pointer != 0) {
416 try emit.decl.link.wasm.relocs.append(emit.bin_file.allocator, .{421 const atom_index = emit.bin_file.decls.get(emit.decl_index).?;
422 const atom = emit.bin_file.getAtomPtr(atom_index);
423 try atom.relocs.append(emit.bin_file.base.allocator, .{
417 .offset = mem_offset,424 .offset = mem_offset,
418 .index = mem.pointer,425 .index = mem.pointer,
419 .relocation_type = if (is_wasm32) .R_WASM_MEMORY_ADDR_LEB else .R_WASM_MEMORY_ADDR_LEB64,426 .relocation_type = if (is_wasm32) .R_WASM_MEMORY_ADDR_LEB else .R_WASM_MEMORY_ADDR_LEB64,
src/arch/x86_64/CodeGen.zig+98-156
...@@ -16,7 +16,7 @@ const Compilation = @import("../../Compilation.zig");...@@ -16,7 +16,7 @@ const Compilation = @import("../../Compilation.zig");
16const DebugInfoOutput = codegen.DebugInfoOutput;16const DebugInfoOutput = codegen.DebugInfoOutput;
17const DW = std.dwarf;17const DW = std.dwarf;
18const ErrorMsg = Module.ErrorMsg;18const ErrorMsg = Module.ErrorMsg;
19const FnResult = codegen.FnResult;19const Result = codegen.Result;
20const GenerateSymbolError = codegen.GenerateSymbolError;20const GenerateSymbolError = codegen.GenerateSymbolError;
21const Emit = @import("Emit.zig");21const Emit = @import("Emit.zig");
22const Liveness = @import("../../Liveness.zig");22const Liveness = @import("../../Liveness.zig");
...@@ -257,7 +257,7 @@ pub fn generate(...@@ -257,7 +257,7 @@ pub fn generate(
257 liveness: Liveness,257 liveness: Liveness,
258 code: *std.ArrayList(u8),258 code: *std.ArrayList(u8),
259 debug_output: DebugInfoOutput,259 debug_output: DebugInfoOutput,
260) GenerateSymbolError!FnResult {260) GenerateSymbolError!Result {
261 if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) {261 if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) {
262 @panic("Attempted to compile for architecture that was disabled by build configuration");262 @panic("Attempted to compile for architecture that was disabled by build configuration");
263 }263 }
...@@ -305,8 +305,8 @@ pub fn generate(...@@ -305,8 +305,8 @@ pub fn generate(
305 defer if (builtin.mode == .Debug) function.mir_to_air_map.deinit();305 defer if (builtin.mode == .Debug) function.mir_to_air_map.deinit();
306306
307 var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) {307 var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) {
308 error.CodegenFail => return FnResult{ .fail = function.err_msg.? },308 error.CodegenFail => return Result{ .fail = function.err_msg.? },
309 error.OutOfRegisters => return FnResult{309 error.OutOfRegisters => return Result{
310 .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),310 .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
311 },311 },
312 else => |e| return e,312 else => |e| return e,
...@@ -319,8 +319,8 @@ pub fn generate(...@@ -319,8 +319,8 @@ pub fn generate(
319 function.max_end_stack = call_info.stack_byte_count;319 function.max_end_stack = call_info.stack_byte_count;
320320
321 function.gen() catch |err| switch (err) {321 function.gen() catch |err| switch (err) {
322 error.CodegenFail => return FnResult{ .fail = function.err_msg.? },322 error.CodegenFail => return Result{ .fail = function.err_msg.? },
323 error.OutOfRegisters => return FnResult{323 error.OutOfRegisters => return Result{
324 .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),324 .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}),
325 },325 },
326 else => |e| return e,326 else => |e| return e,
...@@ -345,14 +345,14 @@ pub fn generate(...@@ -345,14 +345,14 @@ pub fn generate(
345 };345 };
346 defer emit.deinit();346 defer emit.deinit();
347 emit.lowerMir() catch |err| switch (err) {347 emit.lowerMir() catch |err| switch (err) {
348 error.EmitFail => return FnResult{ .fail = emit.err_msg.? },348 error.EmitFail => return Result{ .fail = emit.err_msg.? },
349 else => |e| return e,349 else => |e| return e,
350 };350 };
351351
352 if (function.err_msg) |em| {352 if (function.err_msg) |em| {
353 return FnResult{ .fail = em };353 return Result{ .fail = em };
354 } else {354 } else {
355 return FnResult{ .appended = {} };355 return Result.ok;
356 }356 }
357}357}
358358
...@@ -2668,12 +2668,13 @@ fn loadMemPtrIntoRegister(self: *Self, reg: Register, ptr_ty: Type, ptr: MCValue...@@ -2668,12 +2668,13 @@ fn loadMemPtrIntoRegister(self: *Self, reg: Register, ptr_ty: Type, ptr: MCValue
2668 switch (ptr) {2668 switch (ptr) {
2669 .linker_load => |load_struct| {2669 .linker_load => |load_struct| {
2670 const abi_size = @intCast(u32, ptr_ty.abiSize(self.target.*));2670 const abi_size = @intCast(u32, ptr_ty.abiSize(self.target.*));
2671 const mod = self.bin_file.options.module.?;2671 const atom_index = if (self.bin_file.cast(link.File.MachO)) |macho_file| blk: {
2672 const fn_owner_decl = mod.declPtr(self.mod_fn.owner_decl);2672 const atom = try macho_file.getOrCreateAtomForDecl(self.mod_fn.owner_decl);
2673 const atom_index = if (self.bin_file.tag == link.File.MachO.base_tag)2673 break :blk macho_file.getAtom(atom).getSymbolIndex().?;
2674 fn_owner_decl.link.macho.sym_index2674 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| blk: {
2675 else2675 const atom = try coff_file.getOrCreateAtomForDecl(self.mod_fn.owner_decl);
2676 fn_owner_decl.link.coff.sym_index;2676 break :blk coff_file.getAtom(atom).getSymbolIndex().?;
2677 } else unreachable;
2677 const flags: u2 = switch (load_struct.type) {2678 const flags: u2 = switch (load_struct.type) {
2678 .got => 0b00,2679 .got => 0b00,
2679 .direct => 0b01,2680 .direct => 0b01,
...@@ -3835,7 +3836,7 @@ fn genArgDbgInfo(self: Self, ty: Type, name: [:0]const u8, mcv: MCValue) !void {...@@ -3835,7 +3836,7 @@ fn genArgDbgInfo(self: Self, ty: Type, name: [:0]const u8, mcv: MCValue) !void {
3835 },3836 },
3836 else => unreachable, // not a valid function parameter3837 else => unreachable, // not a valid function parameter
3837 };3838 };
3838 try dw.genArgDbgInfo(name, ty, self.bin_file.tag, self.mod_fn.owner_decl, loc);3839 try dw.genArgDbgInfo(name, ty, self.mod_fn.owner_decl, loc);
3839 },3840 },
3840 .plan9 => {},3841 .plan9 => {},
3841 .none => {},3842 .none => {},
...@@ -3875,7 +3876,7 @@ fn genVarDbgInfo(...@@ -3875,7 +3876,7 @@ fn genVarDbgInfo(
3875 break :blk .nop;3876 break :blk .nop;
3876 },3877 },
3877 };3878 };
3878 try dw.genVarDbgInfo(name, ty, self.bin_file.tag, self.mod_fn.owner_decl, is_ptr, loc);3879 try dw.genVarDbgInfo(name, ty, self.mod_fn.owner_decl, is_ptr, loc);
3879 },3880 },
3880 .plan9 => {},3881 .plan9 => {},
3881 .none => {},3882 .none => {},
...@@ -3992,49 +3993,26 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier...@@ -3992,49 +3993,26 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
3992 // Due to incremental compilation, how function calls are generated depends3993 // Due to incremental compilation, how function calls are generated depends
3993 // on linking.3994 // on linking.
3994 const mod = self.bin_file.options.module.?;3995 const mod = self.bin_file.options.module.?;
3995 if (self.bin_file.cast(link.File.Elf)) |elf_file| {3996 if (self.air.value(callee)) |func_value| {
3996 if (self.air.value(callee)) |func_value| {3997 if (func_value.castTag(.function)) |func_payload| {
3997 if (func_value.castTag(.function)) |func_payload| {3998 const func = func_payload.data;
3998 const func = func_payload.data;3999
3999 const ptr_bits = self.target.cpu.arch.ptrBitWidth();4000 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
4000 const ptr_bytes: u64 = @divExact(ptr_bits, 8);4001 const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl);
4001 const fn_owner_decl = mod.declPtr(func.owner_decl);4002 const atom = elf_file.getAtom(atom_index);
4002 const got_addr = blk: {4003 const got_addr = @intCast(u32, atom.getOffsetTableAddress(elf_file));
4003 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
4004 break :blk @intCast(u32, got.p_vaddr + fn_owner_decl.link.elf.offset_table_index * ptr_bytes);
4005 };
4006 _ = try self.addInst(.{4004 _ = try self.addInst(.{
4007 .tag = .call,4005 .tag = .call,
4008 .ops = Mir.Inst.Ops.encode(.{ .flags = 0b01 }),4006 .ops = Mir.Inst.Ops.encode(.{ .flags = 0b01 }),
4009 .data = .{ .imm = @truncate(u32, got_addr) },4007 .data = .{ .imm = got_addr },
4010 });4008 });
4011 } else if (func_value.castTag(.extern_fn)) |_| {4009 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
4012 return self.fail("TODO implement calling extern functions", .{});4010 const atom_index = try coff_file.getOrCreateAtomForDecl(func.owner_decl);
4013 } else {4011 const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?;
4014 return self.fail("TODO implement calling bitcasted functions", .{});
4015 }
4016 } else {
4017 assert(ty.zigTypeTag() == .Pointer);
4018 const mcv = try self.resolveInst(callee);
4019 try self.genSetReg(Type.initTag(.usize), .rax, mcv);
4020 _ = try self.addInst(.{
4021 .tag = .call,
4022 .ops = Mir.Inst.Ops.encode(.{
4023 .reg1 = .rax,
4024 .flags = 0b01,
4025 }),
4026 .data = undefined,
4027 });
4028 }
4029 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
4030 if (self.air.value(callee)) |func_value| {
4031 if (func_value.castTag(.function)) |func_payload| {
4032 const func = func_payload.data;
4033 const fn_owner_decl = mod.declPtr(func.owner_decl);
4034 try self.genSetReg(Type.initTag(.usize), .rax, .{4012 try self.genSetReg(Type.initTag(.usize), .rax, .{
4035 .linker_load = .{4013 .linker_load = .{
4036 .type = .got,4014 .type = .got,
4037 .sym_index = fn_owner_decl.link.coff.sym_index,4015 .sym_index = sym_index,
4038 },4016 },
4039 });4017 });
4040 _ = try self.addInst(.{4018 _ = try self.addInst(.{
...@@ -4045,19 +4023,12 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier...@@ -4045,19 +4023,12 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
4045 }),4023 }),
4046 .data = undefined,4024 .data = undefined,
4047 });4025 });
4048 } else if (func_value.castTag(.extern_fn)) |func_payload| {4026 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
4049 const extern_fn = func_payload.data;4027 const atom_index = try macho_file.getOrCreateAtomForDecl(func.owner_decl);
4050 const decl_name = mod.declPtr(extern_fn.owner_decl).name;4028 const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?;
4051 if (extern_fn.lib_name) |lib_name| {
4052 log.debug("TODO enforce that '{s}' is expected in '{s}' library", .{
4053 decl_name,
4054 lib_name,
4055 });
4056 }
4057 const sym_index = try coff_file.getGlobalSymbol(mem.sliceTo(decl_name, 0));
4058 try self.genSetReg(Type.initTag(.usize), .rax, .{4029 try self.genSetReg(Type.initTag(.usize), .rax, .{
4059 .linker_load = .{4030 .linker_load = .{
4060 .type = .import,4031 .type = .got,
4061 .sym_index = sym_index,4032 .sym_index = sym_index,
4062 },4033 },
4063 });4034 });
...@@ -4069,35 +4040,38 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier...@@ -4069,35 +4040,38 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
4069 }),4040 }),
4070 .data = undefined,4041 .data = undefined,
4071 });4042 });
4072 } else {4043 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
4073 return self.fail("TODO implement calling bitcasted functions", .{});4044 const decl_block_index = try p9.seeDecl(func.owner_decl);
4045 const decl_block = p9.getDeclBlock(decl_block_index);
4046 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
4047 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
4048 const got_addr = p9.bases.data;
4049 const got_index = decl_block.got_index.?;
4050 const fn_got_addr = got_addr + got_index * ptr_bytes;
4051 _ = try self.addInst(.{
4052 .tag = .call,
4053 .ops = Mir.Inst.Ops.encode(.{ .flags = 0b01 }),
4054 .data = .{ .imm = @intCast(u32, fn_got_addr) },
4055 });
4056 } else unreachable;
4057 } else if (func_value.castTag(.extern_fn)) |func_payload| {
4058 const extern_fn = func_payload.data;
4059 const decl_name = mod.declPtr(extern_fn.owner_decl).name;
4060 if (extern_fn.lib_name) |lib_name| {
4061 log.debug("TODO enforce that '{s}' is expected in '{s}' library", .{
4062 decl_name,
4063 lib_name,
4064 });
4074 }4065 }
4075 } else {4066
4076 assert(ty.zigTypeTag() == .Pointer);4067 if (self.bin_file.cast(link.File.Coff)) |coff_file| {
4077 const mcv = try self.resolveInst(callee);4068 const sym_index = try coff_file.getGlobalSymbol(mem.sliceTo(decl_name, 0));
4078 try self.genSetReg(Type.initTag(.usize), .rax, mcv);
4079 _ = try self.addInst(.{
4080 .tag = .call,
4081 .ops = Mir.Inst.Ops.encode(.{
4082 .reg1 = .rax,
4083 .flags = 0b01,
4084 }),
4085 .data = undefined,
4086 });
4087 }
4088 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
4089 if (self.air.value(callee)) |func_value| {
4090 if (func_value.castTag(.function)) |func_payload| {
4091 const func = func_payload.data;
4092 const fn_owner_decl = mod.declPtr(func.owner_decl);
4093 const sym_index = fn_owner_decl.link.macho.sym_index;
4094 try self.genSetReg(Type.initTag(.usize), .rax, .{4069 try self.genSetReg(Type.initTag(.usize), .rax, .{
4095 .linker_load = .{4070 .linker_load = .{
4096 .type = .got,4071 .type = .import,
4097 .sym_index = sym_index,4072 .sym_index = sym_index,
4098 },4073 },
4099 });4074 });
4100 // callq *%rax
4101 _ = try self.addInst(.{4075 _ = try self.addInst(.{
4102 .tag = .call,4076 .tag = .call,
4103 .ops = Mir.Inst.Ops.encode(.{4077 .ops = Mir.Inst.Ops.encode(.{
...@@ -4106,71 +4080,37 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier...@@ -4106,71 +4080,37 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
4106 }),4080 }),
4107 .data = undefined,4081 .data = undefined,
4108 });4082 });
4109 } else if (func_value.castTag(.extern_fn)) |func_payload| {4083 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
4110 const extern_fn = func_payload.data;
4111 const decl_name = mod.declPtr(extern_fn.owner_decl).name;
4112 if (extern_fn.lib_name) |lib_name| {
4113 log.debug("TODO enforce that '{s}' is expected in '{s}' library", .{
4114 decl_name,
4115 lib_name,
4116 });
4117 }
4118 const sym_index = try macho_file.getGlobalSymbol(mem.sliceTo(decl_name, 0));4084 const sym_index = try macho_file.getGlobalSymbol(mem.sliceTo(decl_name, 0));
4085 const atom = try macho_file.getOrCreateAtomForDecl(self.mod_fn.owner_decl);
4086 const atom_index = macho_file.getAtom(atom).getSymbolIndex().?;
4119 _ = try self.addInst(.{4087 _ = try self.addInst(.{
4120 .tag = .call_extern,4088 .tag = .call_extern,
4121 .ops = undefined,4089 .ops = undefined,
4122 .data = .{4090 .data = .{ .relocation = .{
4123 .relocation = .{4091 .atom_index = atom_index,
4124 .atom_index = mod.declPtr(self.mod_fn.owner_decl).link.macho.sym_index,4092 .sym_index = sym_index,
4125 .sym_index = sym_index,4093 } },
4126 },
4127 },
4128 });4094 });
4129 } else {4095 } else {
4130 return self.fail("TODO implement calling bitcasted functions", .{});4096 return self.fail("TODO implement calling extern functions", .{});
4131 }4097 }
4132 } else {4098 } else {
4133 assert(ty.zigTypeTag() == .Pointer);4099 return self.fail("TODO implement calling bitcasted functions", .{});
4134 const mcv = try self.resolveInst(callee);
4135 try self.genSetReg(Type.initTag(.usize), .rax, mcv);
4136 _ = try self.addInst(.{
4137 .tag = .call,
4138 .ops = Mir.Inst.Ops.encode(.{
4139 .reg1 = .rax,
4140 .flags = 0b01,
4141 }),
4142 .data = undefined,
4143 });
4144 }4100 }
4145 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {4101 } else {
4146 if (self.air.value(callee)) |func_value| {4102 assert(ty.zigTypeTag() == .Pointer);
4147 if (func_value.castTag(.function)) |func_payload| {4103 const mcv = try self.resolveInst(callee);
4148 try p9.seeDecl(func_payload.data.owner_decl);4104 try self.genSetReg(Type.initTag(.usize), .rax, mcv);
4149 const ptr_bits = self.target.cpu.arch.ptrBitWidth();4105 _ = try self.addInst(.{
4150 const ptr_bytes: u64 = @divExact(ptr_bits, 8);4106 .tag = .call,
4151 const got_addr = p9.bases.data;4107 .ops = Mir.Inst.Ops.encode(.{
4152 const got_index = mod.declPtr(func_payload.data.owner_decl).link.plan9.got_index.?;4108 .reg1 = .rax,
4153 const fn_got_addr = got_addr + got_index * ptr_bytes;4109 .flags = 0b01,
4154 _ = try self.addInst(.{4110 }),
4155 .tag = .call,4111 .data = undefined,
4156 .ops = Mir.Inst.Ops.encode(.{ .flags = 0b01 }),4112 });
4157 .data = .{ .imm = @intCast(u32, fn_got_addr) },4113 }
4158 });
4159 } else return self.fail("TODO implement calling extern fn on plan9", .{});
4160 } else {
4161 assert(ty.zigTypeTag() == .Pointer);
4162 const mcv = try self.resolveInst(callee);
4163 try self.genSetReg(Type.initTag(.usize), .rax, mcv);
4164 _ = try self.addInst(.{
4165 .tag = .call,
4166 .ops = Mir.Inst.Ops.encode(.{
4167 .reg1 = .rax,
4168 .flags = 0b01,
4169 }),
4170 .data = undefined,
4171 });
4172 }
4173 } else unreachable;
41744114
4175 if (info.stack_byte_count > 0) {4115 if (info.stack_byte_count > 0) {
4176 // Readjust the stack4116 // Readjust the stack
...@@ -6781,24 +6721,27 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne...@@ -6781,24 +6721,27 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne
6781 module.markDeclAlive(decl);6721 module.markDeclAlive(decl);
67826722
6783 if (self.bin_file.cast(link.File.Elf)) |elf_file| {6723 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
6784 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];6724 const atom_index = try elf_file.getOrCreateAtomForDecl(decl_index);
6785 const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes;6725 const atom = elf_file.getAtom(atom_index);
6786 return MCValue{ .memory = got_addr };6726 return MCValue{ .memory = atom.getOffsetTableAddress(elf_file) };
6787 } else if (self.bin_file.cast(link.File.MachO)) |_| {6727 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
6788 assert(decl.link.macho.sym_index != 0);6728 const atom_index = try macho_file.getOrCreateAtomForDecl(decl_index);
6729 const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?;
6789 return MCValue{ .linker_load = .{6730 return MCValue{ .linker_load = .{
6790 .type = .got,6731 .type = .got,
6791 .sym_index = decl.link.macho.sym_index,6732 .sym_index = sym_index,
6792 } };6733 } };
6793 } else if (self.bin_file.cast(link.File.Coff)) |_| {6734 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
6794 assert(decl.link.coff.sym_index != 0);6735 const atom_index = try coff_file.getOrCreateAtomForDecl(decl_index);
6736 const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?;
6795 return MCValue{ .linker_load = .{6737 return MCValue{ .linker_load = .{
6796 .type = .got,6738 .type = .got,
6797 .sym_index = decl.link.coff.sym_index,6739 .sym_index = sym_index,
6798 } };6740 } };
6799 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {6741 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
6800 try p9.seeDecl(decl_index);6742 const decl_block_index = try p9.seeDecl(decl_index);
6801 const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes;6743 const decl_block = p9.getDeclBlock(decl_block_index);
6744 const got_addr = p9.bases.data + decl_block.got_index.? * ptr_bytes;
6802 return MCValue{ .memory = got_addr };6745 return MCValue{ .memory = got_addr };
6803 } else {6746 } else {
6804 return self.fail("TODO codegen non-ELF const Decl pointer", .{});6747 return self.fail("TODO codegen non-ELF const Decl pointer", .{});
...@@ -6811,8 +6754,7 @@ fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue {...@@ -6811,8 +6754,7 @@ fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue {
6811 return self.fail("lowering unnamed constant failed: {s}", .{@errorName(err)});6754 return self.fail("lowering unnamed constant failed: {s}", .{@errorName(err)});
6812 };6755 };
6813 if (self.bin_file.cast(link.File.Elf)) |elf_file| {6756 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
6814 const vaddr = elf_file.local_symbols.items[local_sym_index].st_value;6757 return MCValue{ .memory = elf_file.getSymbol(local_sym_index).st_value };
6815 return MCValue{ .memory = vaddr };
6816 } else if (self.bin_file.cast(link.File.MachO)) |_| {6758 } else if (self.bin_file.cast(link.File.MachO)) |_| {
6817 return MCValue{ .linker_load = .{6759 return MCValue{ .linker_load = .{
6818 .type = .direct,6760 .type = .direct,
src/arch/x86_64/Emit.zig+8-8
...@@ -1001,8 +1001,8 @@ fn mirLeaPic(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {...@@ -1001,8 +1001,8 @@ fn mirLeaPic(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
1001 0b01 => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_SIGNED),1001 0b01 => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_SIGNED),
1002 else => unreachable,1002 else => unreachable,
1003 };1003 };
1004 const atom = macho_file.getAtomForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?;1004 const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?;
1005 try atom.addRelocation(macho_file, .{1005 try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{
1006 .type = reloc_type,1006 .type = reloc_type,
1007 .target = .{ .sym_index = relocation.sym_index, .file = null },1007 .target = .{ .sym_index = relocation.sym_index, .file = null },
1008 .offset = @intCast(u32, end_offset - 4),1008 .offset = @intCast(u32, end_offset - 4),
...@@ -1011,8 +1011,8 @@ fn mirLeaPic(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {...@@ -1011,8 +1011,8 @@ fn mirLeaPic(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
1011 .length = 2,1011 .length = 2,
1012 });1012 });
1013 } else if (emit.bin_file.cast(link.File.Coff)) |coff_file| {1013 } else if (emit.bin_file.cast(link.File.Coff)) |coff_file| {
1014 const atom = coff_file.getAtomForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?;1014 const atom_index = coff_file.getAtomIndexForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?;
1015 try atom.addRelocation(coff_file, .{1015 try link.File.Coff.Atom.addRelocation(coff_file, atom_index, .{
1016 .type = switch (ops.flags) {1016 .type = switch (ops.flags) {
1017 0b00 => .got,1017 0b00 => .got,
1018 0b01 => .direct,1018 0b01 => .direct,
...@@ -1140,9 +1140,9 @@ fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {...@@ -1140,9 +1140,9 @@ fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
11401140
1141 if (emit.bin_file.cast(link.File.MachO)) |macho_file| {1141 if (emit.bin_file.cast(link.File.MachO)) |macho_file| {
1142 // Add relocation to the decl.1142 // Add relocation to the decl.
1143 const atom = macho_file.getAtomForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?;1143 const atom_index = macho_file.getAtomIndexForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?;
1144 const target = macho_file.getGlobalByIndex(relocation.sym_index);1144 const target = macho_file.getGlobalByIndex(relocation.sym_index);
1145 try atom.addRelocation(macho_file, .{1145 try link.File.MachO.Atom.addRelocation(macho_file, atom_index, .{
1146 .type = @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_BRANCH),1146 .type = @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_BRANCH),
1147 .target = target,1147 .target = target,
1148 .offset = offset,1148 .offset = offset,
...@@ -1152,9 +1152,9 @@ fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {...@@ -1152,9 +1152,9 @@ fn mirCallExtern(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
1152 });1152 });
1153 } else if (emit.bin_file.cast(link.File.Coff)) |coff_file| {1153 } else if (emit.bin_file.cast(link.File.Coff)) |coff_file| {
1154 // Add relocation to the decl.1154 // Add relocation to the decl.
1155 const atom = coff_file.getAtomForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?;1155 const atom_index = coff_file.getAtomIndexForSymbol(.{ .sym_index = relocation.atom_index, .file = null }).?;
1156 const target = coff_file.getGlobalByIndex(relocation.sym_index);1156 const target = coff_file.getGlobalByIndex(relocation.sym_index);
1157 try atom.addRelocation(coff_file, .{1157 try link.File.Coff.Atom.addRelocation(coff_file, atom_index, .{
1158 .type = .direct,1158 .type = .direct,
1159 .target = target,1159 .target = target,
1160 .offset = offset,1160 .offset = offset,
src/codegen.zig+58-131
...@@ -21,16 +21,11 @@ const TypedValue = @import("TypedValue.zig");...@@ -21,16 +21,11 @@ const TypedValue = @import("TypedValue.zig");
21const Value = @import("value.zig").Value;21const Value = @import("value.zig").Value;
22const Zir = @import("Zir.zig");22const Zir = @import("Zir.zig");
2323
24pub const FnResult = union(enum) {
25 /// The `code` parameter passed to `generateSymbol` has the value appended.
26 appended: void,
27 fail: *ErrorMsg,
28};
29pub const Result = union(enum) {24pub const Result = union(enum) {
30 /// The `code` parameter passed to `generateSymbol` has the value appended.25 /// The `code` parameter passed to `generateSymbol` has the value ok.
31 appended: void,26 ok: void,
32 /// The value is available externally, `code` is unused.27
33 externally_managed: []const u8,28 /// There was a codegen error.
34 fail: *ErrorMsg,29 fail: *ErrorMsg,
35};30};
3631
...@@ -89,7 +84,7 @@ pub fn generateFunction(...@@ -89,7 +84,7 @@ pub fn generateFunction(
89 liveness: Liveness,84 liveness: Liveness,
90 code: *std.ArrayList(u8),85 code: *std.ArrayList(u8),
91 debug_output: DebugInfoOutput,86 debug_output: DebugInfoOutput,
92) GenerateSymbolError!FnResult {87) GenerateSymbolError!Result {
93 switch (bin_file.options.target.cpu.arch) {88 switch (bin_file.options.target.cpu.arch) {
94 .arm,89 .arm,
95 .armeb,90 .armeb,
...@@ -145,7 +140,7 @@ pub fn generateSymbol(...@@ -145,7 +140,7 @@ pub fn generateSymbol(
145 if (typed_value.val.isUndefDeep()) {140 if (typed_value.val.isUndefDeep()) {
146 const abi_size = math.cast(usize, typed_value.ty.abiSize(target)) orelse return error.Overflow;141 const abi_size = math.cast(usize, typed_value.ty.abiSize(target)) orelse return error.Overflow;
147 try code.appendNTimes(0xaa, abi_size);142 try code.appendNTimes(0xaa, abi_size);
148 return Result{ .appended = {} };143 return Result.ok;
149 }144 }
150145
151 switch (typed_value.ty.zigTypeTag()) {146 switch (typed_value.ty.zigTypeTag()) {
...@@ -176,7 +171,7 @@ pub fn generateSymbol(...@@ -176,7 +171,7 @@ pub fn generateSymbol(
176 128 => writeFloat(f128, typed_value.val.toFloat(f128), target, endian, try code.addManyAsArray(16)),171 128 => writeFloat(f128, typed_value.val.toFloat(f128), target, endian, try code.addManyAsArray(16)),
177 else => unreachable,172 else => unreachable,
178 }173 }
179 return Result{ .appended = {} };174 return Result.ok;
180 },175 },
181 .Array => switch (typed_value.val.tag()) {176 .Array => switch (typed_value.val.tag()) {
182 .bytes => {177 .bytes => {
...@@ -185,7 +180,7 @@ pub fn generateSymbol(...@@ -185,7 +180,7 @@ pub fn generateSymbol(
185 // The bytes payload already includes the sentinel, if any180 // The bytes payload already includes the sentinel, if any
186 try code.ensureUnusedCapacity(len);181 try code.ensureUnusedCapacity(len);
187 code.appendSliceAssumeCapacity(bytes[0..len]);182 code.appendSliceAssumeCapacity(bytes[0..len]);
188 return Result{ .appended = {} };183 return Result.ok;
189 },184 },
190 .str_lit => {185 .str_lit => {
191 const str_lit = typed_value.val.castTag(.str_lit).?.data;186 const str_lit = typed_value.val.castTag(.str_lit).?.data;
...@@ -197,7 +192,7 @@ pub fn generateSymbol(...@@ -197,7 +192,7 @@ pub fn generateSymbol(
197 const byte = @intCast(u8, sent_val.toUnsignedInt(target));192 const byte = @intCast(u8, sent_val.toUnsignedInt(target));
198 code.appendAssumeCapacity(byte);193 code.appendAssumeCapacity(byte);
199 }194 }
200 return Result{ .appended = {} };195 return Result.ok;
201 },196 },
202 .aggregate => {197 .aggregate => {
203 const elem_vals = typed_value.val.castTag(.aggregate).?.data;198 const elem_vals = typed_value.val.castTag(.aggregate).?.data;
...@@ -208,14 +203,11 @@ pub fn generateSymbol(...@@ -208,14 +203,11 @@ pub fn generateSymbol(
208 .ty = elem_ty,203 .ty = elem_ty,
209 .val = elem_val,204 .val = elem_val,
210 }, code, debug_output, reloc_info)) {205 }, code, debug_output, reloc_info)) {
211 .appended => {},206 .ok => {},
212 .externally_managed => |slice| {
213 code.appendSliceAssumeCapacity(slice);
214 },
215 .fail => |em| return Result{ .fail = em },207 .fail => |em| return Result{ .fail = em },
216 }208 }
217 }209 }
218 return Result{ .appended = {} };210 return Result.ok;
219 },211 },
220 .repeated => {212 .repeated => {
221 const array = typed_value.val.castTag(.repeated).?.data;213 const array = typed_value.val.castTag(.repeated).?.data;
...@@ -229,10 +221,7 @@ pub fn generateSymbol(...@@ -229,10 +221,7 @@ pub fn generateSymbol(
229 .ty = elem_ty,221 .ty = elem_ty,
230 .val = array,222 .val = array,
231 }, code, debug_output, reloc_info)) {223 }, code, debug_output, reloc_info)) {
232 .appended => {},224 .ok => {},
233 .externally_managed => |slice| {
234 code.appendSliceAssumeCapacity(slice);
235 },
236 .fail => |em| return Result{ .fail = em },225 .fail => |em| return Result{ .fail = em },
237 }226 }
238 }227 }
...@@ -242,15 +231,12 @@ pub fn generateSymbol(...@@ -242,15 +231,12 @@ pub fn generateSymbol(
242 .ty = elem_ty,231 .ty = elem_ty,
243 .val = sentinel_val,232 .val = sentinel_val,
244 }, code, debug_output, reloc_info)) {233 }, code, debug_output, reloc_info)) {
245 .appended => {},234 .ok => {},
246 .externally_managed => |slice| {
247 code.appendSliceAssumeCapacity(slice);
248 },
249 .fail => |em| return Result{ .fail = em },235 .fail => |em| return Result{ .fail = em },
250 }236 }
251 }237 }
252238
253 return Result{ .appended = {} };239 return Result.ok;
254 },240 },
255 .empty_array_sentinel => {241 .empty_array_sentinel => {
256 const elem_ty = typed_value.ty.childType();242 const elem_ty = typed_value.ty.childType();
...@@ -259,13 +245,10 @@ pub fn generateSymbol(...@@ -259,13 +245,10 @@ pub fn generateSymbol(
259 .ty = elem_ty,245 .ty = elem_ty,
260 .val = sentinel_val,246 .val = sentinel_val,
261 }, code, debug_output, reloc_info)) {247 }, code, debug_output, reloc_info)) {
262 .appended => {},248 .ok => {},
263 .externally_managed => |slice| {
264 code.appendSliceAssumeCapacity(slice);
265 },
266 .fail => |em| return Result{ .fail = em },249 .fail => |em| return Result{ .fail = em },
267 }250 }
268 return Result{ .appended = {} };251 return Result.ok;
269 },252 },
270 else => return Result{253 else => return Result{
271 .fail = try ErrorMsg.create(254 .fail = try ErrorMsg.create(
...@@ -289,7 +272,7 @@ pub fn generateSymbol(...@@ -289,7 +272,7 @@ pub fn generateSymbol(
289 },272 },
290 else => unreachable,273 else => unreachable,
291 }274 }
292 return Result{ .appended = {} };275 return Result.ok;
293 },276 },
294 .variable => {277 .variable => {
295 const decl = typed_value.val.castTag(.variable).?.data.owner_decl;278 const decl = typed_value.val.castTag(.variable).?.data.owner_decl;
...@@ -309,10 +292,7 @@ pub fn generateSymbol(...@@ -309,10 +292,7 @@ pub fn generateSymbol(
309 .ty = slice_ptr_field_type,292 .ty = slice_ptr_field_type,
310 .val = slice.ptr,293 .val = slice.ptr,
311 }, code, debug_output, reloc_info)) {294 }, code, debug_output, reloc_info)) {
312 .appended => {},295 .ok => {},
313 .externally_managed => |external_slice| {
314 code.appendSliceAssumeCapacity(external_slice);
315 },
316 .fail => |em| return Result{ .fail = em },296 .fail => |em| return Result{ .fail = em },
317 }297 }
318298
...@@ -321,14 +301,11 @@ pub fn generateSymbol(...@@ -321,14 +301,11 @@ pub fn generateSymbol(
321 .ty = Type.initTag(.usize),301 .ty = Type.initTag(.usize),
322 .val = slice.len,302 .val = slice.len,
323 }, code, debug_output, reloc_info)) {303 }, code, debug_output, reloc_info)) {
324 .appended => {},304 .ok => {},
325 .externally_managed => |external_slice| {
326 code.appendSliceAssumeCapacity(external_slice);
327 },
328 .fail => |em| return Result{ .fail = em },305 .fail => |em| return Result{ .fail = em },
329 }306 }
330307
331 return Result{ .appended = {} };308 return Result.ok;
332 },309 },
333 .field_ptr => {310 .field_ptr => {
334 const field_ptr = typed_value.val.castTag(.field_ptr).?.data;311 const field_ptr = typed_value.val.castTag(.field_ptr).?.data;
...@@ -375,13 +352,10 @@ pub fn generateSymbol(...@@ -375,13 +352,10 @@ pub fn generateSymbol(
375 .ty = typed_value.ty,352 .ty = typed_value.ty,
376 .val = container_ptr,353 .val = container_ptr,
377 }, code, debug_output, reloc_info)) {354 }, code, debug_output, reloc_info)) {
378 .appended => {},355 .ok => {},
379 .externally_managed => |external_slice| {
380 code.appendSliceAssumeCapacity(external_slice);
381 },
382 .fail => |em| return Result{ .fail = em },356 .fail => |em| return Result{ .fail = em },
383 }357 }
384 return Result{ .appended = {} };358 return Result.ok;
385 },359 },
386 else => return Result{360 else => return Result{
387 .fail = try ErrorMsg.create(361 .fail = try ErrorMsg.create(
...@@ -434,7 +408,7 @@ pub fn generateSymbol(...@@ -434,7 +408,7 @@ pub fn generateSymbol(
434 .signed => @bitCast(u8, @intCast(i8, typed_value.val.toSignedInt(target))),408 .signed => @bitCast(u8, @intCast(i8, typed_value.val.toSignedInt(target))),
435 };409 };
436 try code.append(x);410 try code.append(x);
437 return Result{ .appended = {} };411 return Result.ok;
438 }412 }
439 if (info.bits > 64) {413 if (info.bits > 64) {
440 var bigint_buffer: Value.BigIntSpace = undefined;414 var bigint_buffer: Value.BigIntSpace = undefined;
...@@ -443,7 +417,7 @@ pub fn generateSymbol(...@@ -443,7 +417,7 @@ pub fn generateSymbol(
443 const start = code.items.len;417 const start = code.items.len;
444 try code.resize(start + abi_size);418 try code.resize(start + abi_size);
445 bigint.writeTwosComplement(code.items[start..][0..abi_size], endian);419 bigint.writeTwosComplement(code.items[start..][0..abi_size], endian);
446 return Result{ .appended = {} };420 return Result.ok;
447 }421 }
448 switch (info.signedness) {422 switch (info.signedness) {
449 .unsigned => {423 .unsigned => {
...@@ -471,7 +445,7 @@ pub fn generateSymbol(...@@ -471,7 +445,7 @@ pub fn generateSymbol(
471 }445 }
472 },446 },
473 }447 }
474 return Result{ .appended = {} };448 return Result.ok;
475 },449 },
476 .Enum => {450 .Enum => {
477 var int_buffer: Value.Payload.U64 = undefined;451 var int_buffer: Value.Payload.U64 = undefined;
...@@ -481,7 +455,7 @@ pub fn generateSymbol(...@@ -481,7 +455,7 @@ pub fn generateSymbol(
481 if (info.bits <= 8) {455 if (info.bits <= 8) {
482 const x = @intCast(u8, int_val.toUnsignedInt(target));456 const x = @intCast(u8, int_val.toUnsignedInt(target));
483 try code.append(x);457 try code.append(x);
484 return Result{ .appended = {} };458 return Result.ok;
485 }459 }
486 if (info.bits > 64) {460 if (info.bits > 64) {
487 return Result{461 return Result{
...@@ -519,12 +493,12 @@ pub fn generateSymbol(...@@ -519,12 +493,12 @@ pub fn generateSymbol(
519 }493 }
520 },494 },
521 }495 }
522 return Result{ .appended = {} };496 return Result.ok;
523 },497 },
524 .Bool => {498 .Bool => {
525 const x: u8 = @boolToInt(typed_value.val.toBool());499 const x: u8 = @boolToInt(typed_value.val.toBool());
526 try code.append(x);500 try code.append(x);
527 return Result{ .appended = {} };501 return Result.ok;
528 },502 },
529 .Struct => {503 .Struct => {
530 if (typed_value.ty.containerLayout() == .Packed) {504 if (typed_value.ty.containerLayout() == .Packed) {
...@@ -549,12 +523,7 @@ pub fn generateSymbol(...@@ -549,12 +523,7 @@ pub fn generateSymbol(
549 .ty = field_ty,523 .ty = field_ty,
550 .val = field_val,524 .val = field_val,
551 }, &tmp_list, debug_output, reloc_info)) {525 }, &tmp_list, debug_output, reloc_info)) {
552 .appended => {526 .ok => mem.copy(u8, code.items[current_pos..], tmp_list.items),
553 mem.copy(u8, code.items[current_pos..], tmp_list.items);
554 },
555 .externally_managed => |external_slice| {
556 mem.copy(u8, code.items[current_pos..], external_slice);
557 },
558 .fail => |em| return Result{ .fail = em },527 .fail => |em| return Result{ .fail = em },
559 }528 }
560 } else {529 } else {
...@@ -563,7 +532,7 @@ pub fn generateSymbol(...@@ -563,7 +532,7 @@ pub fn generateSymbol(
563 bits += @intCast(u16, field_ty.bitSize(target));532 bits += @intCast(u16, field_ty.bitSize(target));
564 }533 }
565534
566 return Result{ .appended = {} };535 return Result.ok;
567 }536 }
568537
569 const struct_begin = code.items.len;538 const struct_begin = code.items.len;
...@@ -576,10 +545,7 @@ pub fn generateSymbol(...@@ -576,10 +545,7 @@ pub fn generateSymbol(
576 .ty = field_ty,545 .ty = field_ty,
577 .val = field_val,546 .val = field_val,
578 }, code, debug_output, reloc_info)) {547 }, code, debug_output, reloc_info)) {
579 .appended => {},548 .ok => {},
580 .externally_managed => |external_slice| {
581 code.appendSliceAssumeCapacity(external_slice);
582 },
583 .fail => |em| return Result{ .fail = em },549 .fail => |em| return Result{ .fail = em },
584 }550 }
585 const unpadded_field_end = code.items.len - struct_begin;551 const unpadded_field_end = code.items.len - struct_begin;
...@@ -593,7 +559,7 @@ pub fn generateSymbol(...@@ -593,7 +559,7 @@ pub fn generateSymbol(
593 }559 }
594 }560 }
595561
596 return Result{ .appended = {} };562 return Result.ok;
597 },563 },
598 .Union => {564 .Union => {
599 const union_obj = typed_value.val.castTag(.@"union").?.data;565 const union_obj = typed_value.val.castTag(.@"union").?.data;
...@@ -612,10 +578,7 @@ pub fn generateSymbol(...@@ -612,10 +578,7 @@ pub fn generateSymbol(
612 .ty = typed_value.ty.unionTagType().?,578 .ty = typed_value.ty.unionTagType().?,
613 .val = union_obj.tag,579 .val = union_obj.tag,
614 }, code, debug_output, reloc_info)) {580 }, code, debug_output, reloc_info)) {
615 .appended => {},581 .ok => {},
616 .externally_managed => |external_slice| {
617 code.appendSliceAssumeCapacity(external_slice);
618 },
619 .fail => |em| return Result{ .fail = em },582 .fail => |em| return Result{ .fail = em },
620 }583 }
621 }584 }
...@@ -632,10 +595,7 @@ pub fn generateSymbol(...@@ -632,10 +595,7 @@ pub fn generateSymbol(
632 .ty = field_ty,595 .ty = field_ty,
633 .val = union_obj.val,596 .val = union_obj.val,
634 }, code, debug_output, reloc_info)) {597 }, code, debug_output, reloc_info)) {
635 .appended => {},598 .ok => {},
636 .externally_managed => |external_slice| {
637 code.appendSliceAssumeCapacity(external_slice);
638 },
639 .fail => |em| return Result{ .fail = em },599 .fail => |em| return Result{ .fail = em },
640 }600 }
641601
...@@ -650,15 +610,12 @@ pub fn generateSymbol(...@@ -650,15 +610,12 @@ pub fn generateSymbol(
650 .ty = union_ty.tag_ty,610 .ty = union_ty.tag_ty,
651 .val = union_obj.tag,611 .val = union_obj.tag,
652 }, code, debug_output, reloc_info)) {612 }, code, debug_output, reloc_info)) {
653 .appended => {},613 .ok => {},
654 .externally_managed => |external_slice| {
655 code.appendSliceAssumeCapacity(external_slice);
656 },
657 .fail => |em| return Result{ .fail = em },614 .fail => |em| return Result{ .fail = em },
658 }615 }
659 }616 }
660617
661 return Result{ .appended = {} };618 return Result.ok;
662 },619 },
663 .Optional => {620 .Optional => {
664 var opt_buf: Type.Payload.ElemType = undefined;621 var opt_buf: Type.Payload.ElemType = undefined;
...@@ -669,7 +626,7 @@ pub fn generateSymbol(...@@ -669,7 +626,7 @@ pub fn generateSymbol(
669626
670 if (!payload_type.hasRuntimeBits()) {627 if (!payload_type.hasRuntimeBits()) {
671 try code.writer().writeByteNTimes(@boolToInt(is_pl), abi_size);628 try code.writer().writeByteNTimes(@boolToInt(is_pl), abi_size);
672 return Result{ .appended = {} };629 return Result.ok;
673 }630 }
674631
675 if (typed_value.ty.optionalReprIsPayload()) {632 if (typed_value.ty.optionalReprIsPayload()) {
...@@ -678,10 +635,7 @@ pub fn generateSymbol(...@@ -678,10 +635,7 @@ pub fn generateSymbol(
678 .ty = payload_type,635 .ty = payload_type,
679 .val = payload.data,636 .val = payload.data,
680 }, code, debug_output, reloc_info)) {637 }, code, debug_output, reloc_info)) {
681 .appended => {},638 .ok => {},
682 .externally_managed => |external_slice| {
683 code.appendSliceAssumeCapacity(external_slice);
684 },
685 .fail => |em| return Result{ .fail = em },639 .fail => |em| return Result{ .fail = em },
686 }640 }
687 } else if (!typed_value.val.isNull()) {641 } else if (!typed_value.val.isNull()) {
...@@ -689,17 +643,14 @@ pub fn generateSymbol(...@@ -689,17 +643,14 @@ pub fn generateSymbol(
689 .ty = payload_type,643 .ty = payload_type,
690 .val = typed_value.val,644 .val = typed_value.val,
691 }, code, debug_output, reloc_info)) {645 }, code, debug_output, reloc_info)) {
692 .appended => {},646 .ok => {},
693 .externally_managed => |external_slice| {
694 code.appendSliceAssumeCapacity(external_slice);
695 },
696 .fail => |em| return Result{ .fail = em },647 .fail => |em| return Result{ .fail = em },
697 }648 }
698 } else {649 } else {
699 try code.writer().writeByteNTimes(0, abi_size);650 try code.writer().writeByteNTimes(0, abi_size);
700 }651 }
701652
702 return Result{ .appended = {} };653 return Result.ok;
703 }654 }
704655
705 const value = if (typed_value.val.castTag(.opt_payload)) |payload| payload.data else Value.initTag(.undef);656 const value = if (typed_value.val.castTag(.opt_payload)) |payload| payload.data else Value.initTag(.undef);
...@@ -708,14 +659,11 @@ pub fn generateSymbol(...@@ -708,14 +659,11 @@ pub fn generateSymbol(
708 .ty = payload_type,659 .ty = payload_type,
709 .val = value,660 .val = value,
710 }, code, debug_output, reloc_info)) {661 }, code, debug_output, reloc_info)) {
711 .appended => {},662 .ok => {},
712 .externally_managed => |external_slice| {
713 code.appendSliceAssumeCapacity(external_slice);
714 },
715 .fail => |em| return Result{ .fail = em },663 .fail => |em| return Result{ .fail = em },
716 }664 }
717665
718 return Result{ .appended = {} };666 return Result.ok;
719 },667 },
720 .ErrorUnion => {668 .ErrorUnion => {
721 const error_ty = typed_value.ty.errorUnionSet();669 const error_ty = typed_value.ty.errorUnionSet();
...@@ -740,10 +688,7 @@ pub fn generateSymbol(...@@ -740,10 +688,7 @@ pub fn generateSymbol(
740 .ty = error_ty,688 .ty = error_ty,
741 .val = if (is_payload) Value.initTag(.zero) else typed_value.val,689 .val = if (is_payload) Value.initTag(.zero) else typed_value.val,
742 }, code, debug_output, reloc_info)) {690 }, code, debug_output, reloc_info)) {
743 .appended => {},691 .ok => {},
744 .externally_managed => |external_slice| {
745 code.appendSliceAssumeCapacity(external_slice);
746 },
747 .fail => |em| return Result{ .fail = em },692 .fail => |em| return Result{ .fail = em },
748 }693 }
749 }694 }
...@@ -756,10 +701,7 @@ pub fn generateSymbol(...@@ -756,10 +701,7 @@ pub fn generateSymbol(
756 .ty = payload_ty,701 .ty = payload_ty,
757 .val = payload_val,702 .val = payload_val,
758 }, code, debug_output, reloc_info)) {703 }, code, debug_output, reloc_info)) {
759 .appended => {},704 .ok => {},
760 .externally_managed => |external_slice| {
761 code.appendSliceAssumeCapacity(external_slice);
762 },
763 .fail => |em| return Result{ .fail = em },705 .fail => |em| return Result{ .fail = em },
764 }706 }
765 const unpadded_end = code.items.len - begin;707 const unpadded_end = code.items.len - begin;
...@@ -778,10 +720,7 @@ pub fn generateSymbol(...@@ -778,10 +720,7 @@ pub fn generateSymbol(
778 .ty = error_ty,720 .ty = error_ty,
779 .val = if (is_payload) Value.initTag(.zero) else typed_value.val,721 .val = if (is_payload) Value.initTag(.zero) else typed_value.val,
780 }, code, debug_output, reloc_info)) {722 }, code, debug_output, reloc_info)) {
781 .appended => {},723 .ok => {},
782 .externally_managed => |external_slice| {
783 code.appendSliceAssumeCapacity(external_slice);
784 },
785 .fail => |em| return Result{ .fail = em },724 .fail => |em| return Result{ .fail = em },
786 }725 }
787 const unpadded_end = code.items.len - begin;726 const unpadded_end = code.items.len - begin;
...@@ -793,7 +732,7 @@ pub fn generateSymbol(...@@ -793,7 +732,7 @@ pub fn generateSymbol(
793 }732 }
794 }733 }
795734
796 return Result{ .appended = {} };735 return Result.ok;
797 },736 },
798 .ErrorSet => {737 .ErrorSet => {
799 switch (typed_value.val.tag()) {738 switch (typed_value.val.tag()) {
...@@ -806,7 +745,7 @@ pub fn generateSymbol(...@@ -806,7 +745,7 @@ pub fn generateSymbol(
806 try code.writer().writeByteNTimes(0, @intCast(usize, Type.anyerror.abiSize(target)));745 try code.writer().writeByteNTimes(0, @intCast(usize, Type.anyerror.abiSize(target)));
807 },746 },
808 }747 }
809 return Result{ .appended = {} };748 return Result.ok;
810 },749 },
811 .Vector => switch (typed_value.val.tag()) {750 .Vector => switch (typed_value.val.tag()) {
812 .bytes => {751 .bytes => {
...@@ -814,7 +753,7 @@ pub fn generateSymbol(...@@ -814,7 +753,7 @@ pub fn generateSymbol(
814 const len = @intCast(usize, typed_value.ty.arrayLen());753 const len = @intCast(usize, typed_value.ty.arrayLen());
815 try code.ensureUnusedCapacity(len);754 try code.ensureUnusedCapacity(len);
816 code.appendSliceAssumeCapacity(bytes[0..len]);755 code.appendSliceAssumeCapacity(bytes[0..len]);
817 return Result{ .appended = {} };756 return Result.ok;
818 },757 },
819 .aggregate => {758 .aggregate => {
820 const elem_vals = typed_value.val.castTag(.aggregate).?.data;759 const elem_vals = typed_value.val.castTag(.aggregate).?.data;
...@@ -825,14 +764,11 @@ pub fn generateSymbol(...@@ -825,14 +764,11 @@ pub fn generateSymbol(
825 .ty = elem_ty,764 .ty = elem_ty,
826 .val = elem_val,765 .val = elem_val,
827 }, code, debug_output, reloc_info)) {766 }, code, debug_output, reloc_info)) {
828 .appended => {},767 .ok => {},
829 .externally_managed => |slice| {
830 code.appendSliceAssumeCapacity(slice);
831 },
832 .fail => |em| return Result{ .fail = em },768 .fail => |em| return Result{ .fail = em },
833 }769 }
834 }770 }
835 return Result{ .appended = {} };771 return Result.ok;
836 },772 },
837 .repeated => {773 .repeated => {
838 const array = typed_value.val.castTag(.repeated).?.data;774 const array = typed_value.val.castTag(.repeated).?.data;
...@@ -845,14 +781,11 @@ pub fn generateSymbol(...@@ -845,14 +781,11 @@ pub fn generateSymbol(
845 .ty = elem_ty,781 .ty = elem_ty,
846 .val = array,782 .val = array,
847 }, code, debug_output, reloc_info)) {783 }, code, debug_output, reloc_info)) {
848 .appended => {},784 .ok => {},
849 .externally_managed => |slice| {
850 code.appendSliceAssumeCapacity(slice);
851 },
852 .fail => |em| return Result{ .fail = em },785 .fail => |em| return Result{ .fail = em },
853 }786 }
854 }787 }
855 return Result{ .appended = {} };788 return Result.ok;
856 },789 },
857 .str_lit => {790 .str_lit => {
858 const str_lit = typed_value.val.castTag(.str_lit).?.data;791 const str_lit = typed_value.val.castTag(.str_lit).?.data;
...@@ -860,7 +793,7 @@ pub fn generateSymbol(...@@ -860,7 +793,7 @@ pub fn generateSymbol(
860 const bytes = mod.string_literal_bytes.items[str_lit.index..][0..str_lit.len];793 const bytes = mod.string_literal_bytes.items[str_lit.index..][0..str_lit.len];
861 try code.ensureUnusedCapacity(str_lit.len);794 try code.ensureUnusedCapacity(str_lit.len);
862 code.appendSliceAssumeCapacity(bytes);795 code.appendSliceAssumeCapacity(bytes);
863 return Result{ .appended = {} };796 return Result.ok;
864 },797 },
865 else => unreachable,798 else => unreachable,
866 },799 },
...@@ -901,10 +834,7 @@ fn lowerDeclRef(...@@ -901,10 +834,7 @@ fn lowerDeclRef(
901 .ty = slice_ptr_field_type,834 .ty = slice_ptr_field_type,
902 .val = typed_value.val,835 .val = typed_value.val,
903 }, code, debug_output, reloc_info)) {836 }, code, debug_output, reloc_info)) {
904 .appended => {},837 .ok => {},
905 .externally_managed => |external_slice| {
906 code.appendSliceAssumeCapacity(external_slice);
907 },
908 .fail => |em| return Result{ .fail = em },838 .fail => |em| return Result{ .fail = em },
909 }839 }
910840
...@@ -917,14 +847,11 @@ fn lowerDeclRef(...@@ -917,14 +847,11 @@ fn lowerDeclRef(
917 .ty = Type.usize,847 .ty = Type.usize,
918 .val = Value.initPayload(&slice_len.base),848 .val = Value.initPayload(&slice_len.base),
919 }, code, debug_output, reloc_info)) {849 }, code, debug_output, reloc_info)) {
920 .appended => {},850 .ok => {},
921 .externally_managed => |external_slice| {
922 code.appendSliceAssumeCapacity(external_slice);
923 },
924 .fail => |em| return Result{ .fail = em },851 .fail => |em| return Result{ .fail = em },
925 }852 }
926853
927 return Result{ .appended = {} };854 return Result.ok;
928 }855 }
929856
930 const ptr_width = target.cpu.arch.ptrBitWidth();857 const ptr_width = target.cpu.arch.ptrBitWidth();
...@@ -932,7 +859,7 @@ fn lowerDeclRef(...@@ -932,7 +859,7 @@ fn lowerDeclRef(
932 const is_fn_body = decl.ty.zigTypeTag() == .Fn;859 const is_fn_body = decl.ty.zigTypeTag() == .Fn;
933 if (!is_fn_body and !decl.ty.hasRuntimeBits()) {860 if (!is_fn_body and !decl.ty.hasRuntimeBits()) {
934 try code.writer().writeByteNTimes(0xaa, @divExact(ptr_width, 8));861 try code.writer().writeByteNTimes(0xaa, @divExact(ptr_width, 8));
935 return Result{ .appended = {} };862 return Result.ok;
936 }863 }
937864
938 module.markDeclAlive(decl);865 module.markDeclAlive(decl);
...@@ -950,7 +877,7 @@ fn lowerDeclRef(...@@ -950,7 +877,7 @@ fn lowerDeclRef(
950 else => unreachable,877 else => unreachable,
951 }878 }
952879
953 return Result{ .appended = {} };880 return Result.ok;
954}881}
955882
956pub fn errUnionPayloadOffset(payload_ty: Type, target: std.Target) u64 {883pub fn errUnionPayloadOffset(payload_ty: Type, target: std.Target) u64 {
src/codegen/c.zig+48-22
...@@ -16,7 +16,6 @@ const trace = @import("../tracy.zig").trace;...@@ -16,7 +16,6 @@ const trace = @import("../tracy.zig").trace;
16const LazySrcLoc = Module.LazySrcLoc;16const LazySrcLoc = Module.LazySrcLoc;
17const Air = @import("../Air.zig");17const Air = @import("../Air.zig");
18const Liveness = @import("../Liveness.zig");18const Liveness = @import("../Liveness.zig");
19const CType = @import("../type.zig").CType;
2019
21const target_util = @import("../target.zig");20const target_util = @import("../target.zig");
22const libcFloatPrefix = target_util.libcFloatPrefix;21const libcFloatPrefix = target_util.libcFloatPrefix;
...@@ -1663,6 +1662,22 @@ pub const DeclGen = struct {...@@ -1663,6 +1662,22 @@ pub const DeclGen = struct {
1663 defer buffer.deinit();1662 defer buffer.deinit();
16641663
1665 try buffer.appendSlice("struct ");1664 try buffer.appendSlice("struct ");
1665
1666 var needs_pack_attr = false;
1667 {
1668 var it = t.structFields().iterator();
1669 while (it.next()) |field| {
1670 const field_ty = field.value_ptr.ty;
1671 if (!field_ty.hasRuntimeBits()) continue;
1672 const alignment = field.value_ptr.abi_align;
1673 if (alignment != 0 and alignment < field_ty.abiAlignment(dg.module.getTarget())) {
1674 needs_pack_attr = true;
1675 try buffer.appendSlice("zig_packed(");
1676 break;
1677 }
1678 }
1679 }
1680
1666 try buffer.appendSlice(name);1681 try buffer.appendSlice(name);
1667 try buffer.appendSlice(" {\n");1682 try buffer.appendSlice(" {\n");
1668 {1683 {
...@@ -1672,7 +1687,7 @@ pub const DeclGen = struct {...@@ -1672,7 +1687,7 @@ pub const DeclGen = struct {
1672 const field_ty = field.value_ptr.ty;1687 const field_ty = field.value_ptr.ty;
1673 if (!field_ty.hasRuntimeBits()) continue;1688 if (!field_ty.hasRuntimeBits()) continue;
16741689
1675 const alignment = field.value_ptr.abi_align;1690 const alignment = field.value_ptr.alignment(dg.module.getTarget(), t.containerLayout());
1676 const field_name = CValue{ .identifier = field.key_ptr.* };1691 const field_name = CValue{ .identifier = field.key_ptr.* };
1677 try buffer.append(' ');1692 try buffer.append(' ');
1678 try dg.renderTypeAndName(buffer.writer(), field_ty, field_name, .Mut, alignment, .Complete);1693 try dg.renderTypeAndName(buffer.writer(), field_ty, field_name, .Mut, alignment, .Complete);
...@@ -1682,7 +1697,7 @@ pub const DeclGen = struct {...@@ -1682,7 +1697,7 @@ pub const DeclGen = struct {
1682 }1697 }
1683 if (empty) try buffer.appendSlice(" char empty_struct;\n");1698 if (empty) try buffer.appendSlice(" char empty_struct;\n");
1684 }1699 }
1685 try buffer.appendSlice("};\n");1700 if (needs_pack_attr) try buffer.appendSlice("});\n") else try buffer.appendSlice("};\n");
16861701
1687 const rendered = try buffer.toOwnedSlice();1702 const rendered = try buffer.toOwnedSlice();
1688 errdefer dg.typedefs.allocator.free(rendered);1703 errdefer dg.typedefs.allocator.free(rendered);
...@@ -2367,8 +2382,13 @@ pub const DeclGen = struct {...@@ -2367,8 +2382,13 @@ pub const DeclGen = struct {
2367 depth += 1;2382 depth += 1;
2368 }2383 }
23692384
2370 if (alignment != 0 and alignment > ty.abiAlignment(target)) {2385 if (alignment != 0) {
2371 try w.print("zig_align({}) ", .{alignment});2386 const abi_alignment = ty.abiAlignment(target);
2387 if (alignment < abi_alignment) {
2388 try w.print("zig_under_align({}) ", .{alignment});
2389 } else if (alignment > abi_alignment) {
2390 try w.print("zig_align({}) ", .{alignment});
2391 }
2372 }2392 }
2373 try dg.renderType(w, render_ty, kind);2393 try dg.renderType(w, render_ty, kind);
23742394
...@@ -2860,27 +2880,30 @@ pub fn genDecl(o: *Object) !void {...@@ -2860,27 +2880,30 @@ pub fn genDecl(o: *Object) !void {
2860 const w = o.writer();2880 const w = o.writer();
2861 if (!is_global) try w.writeAll("static ");2881 if (!is_global) try w.writeAll("static ");
2862 if (variable.is_threadlocal) try w.writeAll("zig_threadlocal ");2882 if (variable.is_threadlocal) try w.writeAll("zig_threadlocal ");
2883 if (o.dg.decl.@"linksection") |section| try w.print("zig_linksection(\"{s}\", ", .{section});
2863 try o.dg.renderTypeAndName(w, o.dg.decl.ty, decl_c_value, .Mut, o.dg.decl.@"align", .Complete);2884 try o.dg.renderTypeAndName(w, o.dg.decl.ty, decl_c_value, .Mut, o.dg.decl.@"align", .Complete);
2885 if (o.dg.decl.@"linksection" != null) try w.writeAll(", read, write)");
2864 try w.writeAll(" = ");2886 try w.writeAll(" = ");
2865 try o.dg.renderValue(w, tv.ty, variable.init, .StaticInitializer);2887 try o.dg.renderValue(w, tv.ty, variable.init, .StaticInitializer);
2866 try w.writeByte(';');2888 try w.writeByte(';');
2867 try o.indent_writer.insertNewline();2889 try o.indent_writer.insertNewline();
2868 } else {2890 } else {
2891 const is_global = o.dg.module.decl_exports.contains(o.dg.decl_index);
2892 const fwd_decl_writer = o.dg.fwd_decl.writer();
2869 const decl_c_value: CValue = .{ .decl = o.dg.decl_index };2893 const decl_c_value: CValue = .{ .decl = o.dg.decl_index };
28702894
2871 const fwd_decl_writer = o.dg.fwd_decl.writer();2895 try fwd_decl_writer.writeAll(if (is_global) "zig_extern " else "static ");
2872 try fwd_decl_writer.writeAll("static ");2896 try o.dg.renderTypeAndName(fwd_decl_writer, tv.ty, decl_c_value, .Const, o.dg.decl.@"align", .Complete);
2873 try o.dg.renderTypeAndName(fwd_decl_writer, tv.ty, decl_c_value, .Mut, o.dg.decl.@"align", .Complete);
2874 try fwd_decl_writer.writeAll(";\n");2897 try fwd_decl_writer.writeAll(";\n");
28752898
2876 const writer = o.writer();2899 const w = o.writer();
2877 try writer.writeAll("static ");2900 if (!is_global) try w.writeAll("static ");
2878 // TODO ask the Decl if it is const2901 if (o.dg.decl.@"linksection") |section| try w.print("zig_linksection(\"{s}\", ", .{section});
2879 // https://github.com/ziglang/zig/issues/75822902 try o.dg.renderTypeAndName(w, tv.ty, decl_c_value, .Const, o.dg.decl.@"align", .Complete);
2880 try o.dg.renderTypeAndName(writer, tv.ty, decl_c_value, .Mut, o.dg.decl.@"align", .Complete);2903 if (o.dg.decl.@"linksection" != null) try w.writeAll(", read)");
2881 try writer.writeAll(" = ");2904 try w.writeAll(" = ");
2882 try o.dg.renderValue(writer, tv.ty, tv.val, .StaticInitializer);2905 try o.dg.renderValue(w, tv.ty, tv.val, .StaticInitializer);
2883 try writer.writeAll(";\n");2906 try w.writeAll(";\n");
2884 }2907 }
2885}2908}
28862909
...@@ -3726,16 +3749,15 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -3726,16 +3749,15 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue {
37263749
3727 const ptr_val = try f.resolveInst(bin_op.lhs);3750 const ptr_val = try f.resolveInst(bin_op.lhs);
3728 const src_ty = f.air.typeOf(bin_op.rhs);3751 const src_ty = f.air.typeOf(bin_op.rhs);
3729 const src_val = try f.resolveInst(bin_op.rhs);
3730
3731 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
37323752
3733 // TODO Sema should emit a different instruction when the store should3753 // TODO Sema should emit a different instruction when the store should
3734 // possibly do the safety 0xaa bytes for undefined.3754 // possibly do the safety 0xaa bytes for undefined.
3735 const src_val_is_undefined =3755 const src_val_is_undefined =
3736 if (f.air.value(bin_op.rhs)) |v| v.isUndefDeep() else false;3756 if (f.air.value(bin_op.rhs)) |v| v.isUndefDeep() else false;
3737 if (src_val_is_undefined)3757 if (src_val_is_undefined) {
3758 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
3738 return try storeUndefined(f, ptr_info.pointee_type, ptr_val);3759 return try storeUndefined(f, ptr_info.pointee_type, ptr_val);
3760 }
37393761
3740 const target = f.object.dg.module.getTarget();3762 const target = f.object.dg.module.getTarget();
3741 const is_aligned = ptr_info.@"align" == 0 or3763 const is_aligned = ptr_info.@"align" == 0 or
...@@ -3744,6 +3766,9 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -3744,6 +3766,9 @@ fn airStore(f: *Function, inst: Air.Inst.Index) !CValue {
3744 const need_memcpy = !is_aligned or is_array;3766 const need_memcpy = !is_aligned or is_array;
3745 const writer = f.object.writer();3767 const writer = f.object.writer();
37463768
3769 const src_val = try f.resolveInst(bin_op.rhs);
3770 try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs });
3771
3747 if (need_memcpy) {3772 if (need_memcpy) {
3748 // For this memcpy to safely work we need the rhs to have the same3773 // For this memcpy to safely work we need the rhs to have the same
3749 // underlying type as the lhs (i.e. they must both be arrays of the same underlying type).3774 // underlying type as the lhs (i.e. they must both be arrays of the same underlying type).
...@@ -4344,8 +4369,9 @@ fn airDbgInline(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -4344,8 +4369,9 @@ fn airDbgInline(f: *Function, inst: Air.Inst.Index) !CValue {
4344fn airDbgVar(f: *Function, inst: Air.Inst.Index) !CValue {4369fn airDbgVar(f: *Function, inst: Air.Inst.Index) !CValue {
4345 const pl_op = f.air.instructions.items(.data)[inst].pl_op;4370 const pl_op = f.air.instructions.items(.data)[inst].pl_op;
4346 const name = f.air.nullTerminatedString(pl_op.payload);4371 const name = f.air.nullTerminatedString(pl_op.payload);
4347 const operand = try f.resolveInst(pl_op.operand);4372 const operand_is_undef = if (f.air.value(pl_op.operand)) |v| v.isUndefDeep() else false;
4348 _ = operand;4373 if (!operand_is_undef) _ = try f.resolveInst(pl_op.operand);
4374
4349 try reap(f, inst, &.{pl_op.operand});4375 try reap(f, inst, &.{pl_op.operand});
4350 const writer = f.object.writer();4376 const writer = f.object.writer();
4351 try writer.print("/* var:{s} */\n", .{name});4377 try writer.print("/* var:{s} */\n", .{name});
src/codegen/llvm.zig+2-3
...@@ -19,7 +19,6 @@ const Liveness = @import("../Liveness.zig");...@@ -19,7 +19,6 @@ const Liveness = @import("../Liveness.zig");
19const Value = @import("../value.zig").Value;19const Value = @import("../value.zig").Value;
20const Type = @import("../type.zig").Type;20const Type = @import("../type.zig").Type;
21const LazySrcLoc = Module.LazySrcLoc;21const LazySrcLoc = Module.LazySrcLoc;
22const CType = @import("../type.zig").CType;
23const x86_64_abi = @import("../arch/x86_64/abi.zig");22const x86_64_abi = @import("../arch/x86_64/abi.zig");
24const wasm_c_abi = @import("../arch/wasm/abi.zig");23const wasm_c_abi = @import("../arch/wasm/abi.zig");
25const aarch64_c_abi = @import("../arch/aarch64/abi.zig");24const aarch64_c_abi = @import("../arch/aarch64/abi.zig");
...@@ -11057,8 +11056,8 @@ fn backendSupportsF128(target: std.Target) bool {...@@ -11057,8 +11056,8 @@ fn backendSupportsF128(target: std.Target) bool {
11057fn intrinsicsAllowed(scalar_ty: Type, target: std.Target) bool {11056fn intrinsicsAllowed(scalar_ty: Type, target: std.Target) bool {
11058 return switch (scalar_ty.tag()) {11057 return switch (scalar_ty.tag()) {
11059 .f16 => backendSupportsF16(target),11058 .f16 => backendSupportsF16(target),
11060 .f80 => (CType.longdouble.sizeInBits(target) == 80) and backendSupportsF80(target),11059 .f80 => (target.c_type_bit_size(.longdouble) == 80) and backendSupportsF80(target),
11061 .f128 => (CType.longdouble.sizeInBits(target) == 128) and backendSupportsF128(target),11060 .f128 => (target.c_type_bit_size(.longdouble) == 128) and backendSupportsF128(target),
11062 else => true,11061 else => true,
11063 };11062 };
11064}11063}
src/codegen/spirv.zig+19-11
...@@ -49,7 +49,7 @@ pub const DeclGen = struct {...@@ -49,7 +49,7 @@ pub const DeclGen = struct {
49 spv: *SpvModule,49 spv: *SpvModule,
5050
51 /// The decl we are currently generating code for.51 /// The decl we are currently generating code for.
52 decl: *Decl,52 decl_index: Decl.Index,
5353
54 /// The intermediate code of the declaration we are currently generating. Note: If54 /// The intermediate code of the declaration we are currently generating. Note: If
55 /// the declaration is not a function, this value will be undefined!55 /// the declaration is not a function, this value will be undefined!
...@@ -59,6 +59,8 @@ pub const DeclGen = struct {...@@ -59,6 +59,8 @@ pub const DeclGen = struct {
59 /// Note: If the declaration is not a function, this value will be undefined!59 /// Note: If the declaration is not a function, this value will be undefined!
60 liveness: Liveness,60 liveness: Liveness,
6161
62 ids: *const std.AutoHashMap(Decl.Index, IdResult),
63
62 /// An array of function argument result-ids. Each index corresponds with the64 /// An array of function argument result-ids. Each index corresponds with the
63 /// function argument of the same index.65 /// function argument of the same index.
64 args: std.ArrayListUnmanaged(IdRef) = .{},66 args: std.ArrayListUnmanaged(IdRef) = .{},
...@@ -133,14 +135,20 @@ pub const DeclGen = struct {...@@ -133,14 +135,20 @@ pub const DeclGen = struct {
133135
134 /// Initialize the common resources of a DeclGen. Some fields are left uninitialized,136 /// Initialize the common resources of a DeclGen. Some fields are left uninitialized,
135 /// only set when `gen` is called.137 /// only set when `gen` is called.
136 pub fn init(allocator: Allocator, module: *Module, spv: *SpvModule) DeclGen {138 pub fn init(
139 allocator: Allocator,
140 module: *Module,
141 spv: *SpvModule,
142 ids: *const std.AutoHashMap(Decl.Index, IdResult),
143 ) DeclGen {
137 return .{144 return .{
138 .gpa = allocator,145 .gpa = allocator,
139 .module = module,146 .module = module,
140 .spv = spv,147 .spv = spv,
141 .decl = undefined,148 .decl_index = undefined,
142 .air = undefined,149 .air = undefined,
143 .liveness = undefined,150 .liveness = undefined,
151 .ids = ids,
144 .next_arg_index = undefined,152 .next_arg_index = undefined,
145 .current_block_label_id = undefined,153 .current_block_label_id = undefined,
146 .error_msg = undefined,154 .error_msg = undefined,
...@@ -150,9 +158,9 @@ pub const DeclGen = struct {...@@ -150,9 +158,9 @@ pub const DeclGen = struct {
150 /// Generate the code for `decl`. If a reportable error occurred during code generation,158 /// Generate the code for `decl`. If a reportable error occurred during code generation,
151 /// a message is returned by this function. Callee owns the memory. If this function159 /// a message is returned by this function. Callee owns the memory. If this function
152 /// returns such a reportable error, it is valid to be called again for a different decl.160 /// returns such a reportable error, it is valid to be called again for a different decl.
153 pub fn gen(self: *DeclGen, decl: *Decl, air: Air, liveness: Liveness) !?*Module.ErrorMsg {161 pub fn gen(self: *DeclGen, decl_index: Decl.Index, air: Air, liveness: Liveness) !?*Module.ErrorMsg {
154 // Reset internal resources, we don't want to re-allocate these.162 // Reset internal resources, we don't want to re-allocate these.
155 self.decl = decl;163 self.decl_index = decl_index;
156 self.air = air;164 self.air = air;
157 self.liveness = liveness;165 self.liveness = liveness;
158 self.args.items.len = 0;166 self.args.items.len = 0;
...@@ -194,7 +202,7 @@ pub const DeclGen = struct {...@@ -194,7 +202,7 @@ pub const DeclGen = struct {
194 pub fn fail(self: *DeclGen, comptime format: []const u8, args: anytype) Error {202 pub fn fail(self: *DeclGen, comptime format: []const u8, args: anytype) Error {
195 @setCold(true);203 @setCold(true);
196 const src = LazySrcLoc.nodeOffset(0);204 const src = LazySrcLoc.nodeOffset(0);
197 const src_loc = src.toSrcLoc(self.decl);205 const src_loc = src.toSrcLoc(self.module.declPtr(self.decl_index));
198 assert(self.error_msg == null);206 assert(self.error_msg == null);
199 self.error_msg = try Module.ErrorMsg.create(self.module.gpa, src_loc, format, args);207 self.error_msg = try Module.ErrorMsg.create(self.module.gpa, src_loc, format, args);
200 return error.CodegenFail;208 return error.CodegenFail;
...@@ -332,7 +340,7 @@ pub const DeclGen = struct {...@@ -332,7 +340,7 @@ pub const DeclGen = struct {
332 };340 };
333 const decl = self.module.declPtr(fn_decl_index);341 const decl = self.module.declPtr(fn_decl_index);
334 self.module.markDeclAlive(decl);342 self.module.markDeclAlive(decl);
335 return decl.fn_link.spirv.id.toRef();343 return self.ids.get(fn_decl_index).?.toRef();
336 }344 }
337345
338 const target = self.getTarget();346 const target = self.getTarget();
...@@ -553,8 +561,8 @@ pub const DeclGen = struct {...@@ -553,8 +561,8 @@ pub const DeclGen = struct {
553 }561 }
554562
555 fn genDecl(self: *DeclGen) !void {563 fn genDecl(self: *DeclGen) !void {
556 const decl = self.decl;564 const result_id = self.ids.get(self.decl_index).?;
557 const result_id = decl.fn_link.spirv.id;565 const decl = self.module.declPtr(self.decl_index);
558566
559 if (decl.val.castTag(.function)) |_| {567 if (decl.val.castTag(.function)) |_| {
560 assert(decl.ty.zigTypeTag() == .Fn);568 assert(decl.ty.zigTypeTag() == .Fn);
...@@ -945,7 +953,7 @@ pub const DeclGen = struct {...@@ -945,7 +953,7 @@ pub const DeclGen = struct {
945953
946 fn airDbgStmt(self: *DeclGen, inst: Air.Inst.Index) !void {954 fn airDbgStmt(self: *DeclGen, inst: Air.Inst.Index) !void {
947 const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt;955 const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt;
948 const src_fname_id = try self.spv.resolveSourceFileName(self.decl);956 const src_fname_id = try self.spv.resolveSourceFileName(self.module.declPtr(self.decl_index));
949 try self.func.body.emit(self.spv.gpa, .OpLine, .{957 try self.func.body.emit(self.spv.gpa, .OpLine, .{
950 .file = src_fname_id,958 .file = src_fname_id,
951 .line = dbg_stmt.line,959 .line = dbg_stmt.line,
...@@ -1106,7 +1114,7 @@ pub const DeclGen = struct {...@@ -1106,7 +1114,7 @@ pub const DeclGen = struct {
1106 assert(as.errors.items.len != 0);1114 assert(as.errors.items.len != 0);
1107 assert(self.error_msg == null);1115 assert(self.error_msg == null);
1108 const loc = LazySrcLoc.nodeOffset(0);1116 const loc = LazySrcLoc.nodeOffset(0);
1109 const src_loc = loc.toSrcLoc(self.decl);1117 const src_loc = loc.toSrcLoc(self.module.declPtr(self.decl_index));
1110 self.error_msg = try Module.ErrorMsg.create(self.module.gpa, src_loc, "failed to assemble SPIR-V inline assembly", .{});1118 self.error_msg = try Module.ErrorMsg.create(self.module.gpa, src_loc, "failed to assemble SPIR-V inline assembly", .{});
1111 const notes = try self.module.gpa.alloc(Module.ErrorMsg, as.errors.items.len);1119 const notes = try self.module.gpa.alloc(Module.ErrorMsg, as.errors.items.len);
11121120
src/link.zig+14-69
...@@ -261,39 +261,6 @@ pub const File = struct {...@@ -261,39 +261,6 @@ pub const File = struct {
261 /// of this linking operation.261 /// of this linking operation.
262 lock: ?Cache.Lock = null,262 lock: ?Cache.Lock = null,
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
297 /// Attempts incremental linking, if the file already exists. If264 /// Attempts incremental linking, if the file already exists. If
298 /// incremental linking fails, falls back to truncating the file and265 /// incremental linking fails, falls back to truncating the file and
299 /// rewriting it. A malicious file is detected as incremental link failure266 /// rewriting it. A malicious file is detected as incremental link failure
...@@ -533,8 +500,7 @@ pub const File = struct {...@@ -533,8 +500,7 @@ pub const File = struct {
533 }500 }
534 }501 }
535502
536 /// May be called before or after updateDeclExports but must be called503 /// May be called before or after updateDeclExports for any given Decl.
537 /// after allocateDeclIndexes for any given Decl.
538 pub fn updateDecl(base: *File, module: *Module, decl_index: Module.Decl.Index) UpdateDeclError!void {504 pub fn updateDecl(base: *File, module: *Module, decl_index: Module.Decl.Index) UpdateDeclError!void {
539 const decl = module.declPtr(decl_index);505 const decl = module.declPtr(decl_index);
540 log.debug("updateDecl {*} ({s}), type={}", .{ decl, decl.name, decl.ty.fmtDebug() });506 log.debug("updateDecl {*} ({s}), type={}", .{ decl, decl.name, decl.ty.fmtDebug() });
...@@ -557,8 +523,7 @@ pub const File = struct {...@@ -557,8 +523,7 @@ pub const File = struct {
557 }523 }
558 }524 }
559525
560 /// May be called before or after updateDeclExports but must be called526 /// May be called before or after updateDeclExports for any given Decl.
561 /// after allocateDeclIndexes for any given Decl.
562 pub fn updateFunc(base: *File, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) UpdateDeclError!void {527 pub fn updateFunc(base: *File, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) UpdateDeclError!void {
563 const owner_decl = module.declPtr(func.owner_decl);528 const owner_decl = module.declPtr(func.owner_decl);
564 log.debug("updateFunc {*} ({s}), type={}", .{529 log.debug("updateFunc {*} ({s}), type={}", .{
...@@ -582,48 +547,27 @@ pub const File = struct {...@@ -582,48 +547,27 @@ pub const File = struct {
582 }547 }
583 }548 }
584549
585 pub fn updateDeclLineNumber(base: *File, module: *Module, decl: *Module.Decl) UpdateDeclError!void {550 pub fn updateDeclLineNumber(base: *File, module: *Module, decl_index: Module.Decl.Index) UpdateDeclError!void {
551 const decl = module.declPtr(decl_index);
586 log.debug("updateDeclLineNumber {*} ({s}), line={}", .{552 log.debug("updateDeclLineNumber {*} ({s}), line={}", .{
587 decl, decl.name, decl.src_line + 1,553 decl, decl.name, decl.src_line + 1,
588 });554 });
589 assert(decl.has_tv);555 assert(decl.has_tv);
590 if (build_options.only_c) {556 if (build_options.only_c) {
591 assert(base.tag == .c);557 assert(base.tag == .c);
592 return @fieldParentPtr(C, "base", base).updateDeclLineNumber(module, decl);558 return @fieldParentPtr(C, "base", base).updateDeclLineNumber(module, decl_index);
593 }559 }
594 switch (base.tag) {560 switch (base.tag) {
595 .coff => return @fieldParentPtr(Coff, "base", base).updateDeclLineNumber(module, decl),561 .coff => return @fieldParentPtr(Coff, "base", base).updateDeclLineNumber(module, decl_index),
596 .elf => return @fieldParentPtr(Elf, "base", base).updateDeclLineNumber(module, decl),562 .elf => return @fieldParentPtr(Elf, "base", base).updateDeclLineNumber(module, decl_index),
597 .macho => return @fieldParentPtr(MachO, "base", base).updateDeclLineNumber(module, decl),563 .macho => return @fieldParentPtr(MachO, "base", base).updateDeclLineNumber(module, decl_index),
598 .c => return @fieldParentPtr(C, "base", base).updateDeclLineNumber(module, decl),564 .c => return @fieldParentPtr(C, "base", base).updateDeclLineNumber(module, decl_index),
599 .wasm => return @fieldParentPtr(Wasm, "base", base).updateDeclLineNumber(module, decl),565 .wasm => return @fieldParentPtr(Wasm, "base", base).updateDeclLineNumber(module, decl_index),
600 .plan9 => return @fieldParentPtr(Plan9, "base", base).updateDeclLineNumber(module, decl),566 .plan9 => return @fieldParentPtr(Plan9, "base", base).updateDeclLineNumber(module, decl_index),
601 .spirv, .nvptx => {},567 .spirv, .nvptx => {},
602 }568 }
603 }569 }
604570
605 /// Must be called before any call to updateDecl or updateDeclExports for
606 /// any given Decl.
607 /// TODO we're transitioning to deleting this function and instead having
608 /// each linker backend notice the first time updateDecl or updateFunc is called, or
609 /// a callee referenced from AIR.
610 pub fn allocateDeclIndexes(base: *File, decl_index: Module.Decl.Index) error{OutOfMemory}!void {
611 const decl = base.options.module.?.declPtr(decl_index);
612 log.debug("allocateDeclIndexes {*} ({s})", .{ decl, decl.name });
613 if (build_options.only_c) {
614 assert(base.tag == .c);
615 return;
616 }
617 switch (base.tag) {
618 .coff => return @fieldParentPtr(Coff, "base", base).allocateDeclIndexes(decl_index),
619 .elf => return @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl_index),
620 .macho => return @fieldParentPtr(MachO, "base", base).allocateDeclIndexes(decl_index),
621 .wasm => return @fieldParentPtr(Wasm, "base", base).allocateDeclIndexes(decl_index),
622 .plan9 => return @fieldParentPtr(Plan9, "base", base).allocateDeclIndexes(decl_index),
623 .c, .spirv, .nvptx => {},
624 }
625 }
626
627 pub fn releaseLock(self: *File) void {571 pub fn releaseLock(self: *File) void {
628 if (self.lock) |*lock| {572 if (self.lock) |*lock| {
629 lock.release();573 lock.release();
...@@ -874,8 +818,7 @@ pub const File = struct {...@@ -874,8 +818,7 @@ pub const File = struct {
874 AnalysisFail,818 AnalysisFail,
875 };819 };
876820
877 /// May be called before or after updateDecl, but must be called after821 /// May be called before or after updateDecl for any given Decl.
878 /// allocateDeclIndexes for any given Decl.
879 pub fn updateDeclExports(822 pub fn updateDeclExports(
880 base: *File,823 base: *File,
881 module: *Module,824 module: *Module,
...@@ -911,6 +854,8 @@ pub const File = struct {...@@ -911,6 +854,8 @@ pub const File = struct {
911 /// The linker is passed information about the containing atom, `parent_atom_index`, and offset within it's854 /// The linker is passed information about the containing atom, `parent_atom_index`, and offset within it's
912 /// memory buffer, `offset`, so that it can make a note of potential relocation sites, should the855 /// memory buffer, `offset`, so that it can make a note of potential relocation sites, should the
913 /// `Decl`'s address was not yet resolved, or the containing atom gets moved in virtual memory.856 /// `Decl`'s address was not yet resolved, or the containing atom gets moved in virtual memory.
857 /// May be called before or after updateFunc/updateDecl therefore it is up to the linker to allocate
858 /// the block/atom.
914 pub fn getDeclVAddr(base: *File, decl_index: Module.Decl.Index, reloc_info: RelocInfo) !u64 {859 pub fn getDeclVAddr(base: *File, decl_index: Module.Decl.Index, reloc_info: RelocInfo) !u64 {
915 if (build_options.only_c) unreachable;860 if (build_options.only_c) unreachable;
916 switch (base.tag) {861 switch (base.tag) {
src/link/C.zig+2-2
...@@ -219,12 +219,12 @@ pub fn updateDecl(self: *C, module: *Module, decl_index: Module.Decl.Index) !voi...@@ -219,12 +219,12 @@ pub fn updateDecl(self: *C, module: *Module, decl_index: Module.Decl.Index) !voi
219 code.shrinkAndFree(module.gpa, code.items.len);219 code.shrinkAndFree(module.gpa, code.items.len);
220}220}
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 {
223 // The C backend does not have the ability to fix line numbers without re-generating223 // The C backend does not have the ability to fix line numbers without re-generating
224 // the entire Decl.224 // the entire Decl.
225 _ = self;225 _ = self;
226 _ = module;226 _ = module;
227 _ = decl;227 _ = decl_index;
228}228}
229229
230pub fn flush(self: *C, comp: *Compilation, prog_node: *std.Progress.Node) !void {230pub fn flush(self: *C, comp: *Compilation, prog_node: *std.Progress.Node) !void {
src/link/Coff.zig+301-242
...@@ -79,13 +79,13 @@ entry_addr: ?u32 = null,...@@ -79,13 +79,13 @@ entry_addr: ?u32 = null,
79/// We store them here so that we can properly dispose of any allocated79/// We store them here so that we can properly dispose of any allocated
80/// memory within the atom in the incremental linker.80/// memory within the atom in the incremental linker.
81/// TODO consolidate this.81/// TODO consolidate this.
82decls: std.AutoHashMapUnmanaged(Module.Decl.Index, ?u16) = .{},82decls: std.AutoHashMapUnmanaged(Module.Decl.Index, DeclMetadata) = .{},
8383
84/// List of atoms that are either synthetic or map directly to the Zig source program.84/// List of atoms that are either synthetic or map directly to the Zig source program.
85managed_atoms: std.ArrayListUnmanaged(*Atom) = .{},85atoms: std.ArrayListUnmanaged(Atom) = .{},
8686
87/// Table of atoms indexed by the symbol index.87/// 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
90/// Table of unnamed constants associated with a parent `Decl`.90/// Table of unnamed constants associated with a parent `Decl`.
91/// We store them here so that we can free the constants whenever the `Decl`91/// We store them here so that we can free the constants whenever the `Decl`
...@@ -124,9 +124,9 @@ const Entry = struct {...@@ -124,9 +124,9 @@ const Entry = struct {
124 sym_index: u32,124 sym_index: u32,
125};125};
126126
127const RelocTable = std.AutoHashMapUnmanaged(*Atom, std.ArrayListUnmanaged(Relocation));127const RelocTable = std.AutoHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(Relocation));
128const BaseRelocationTable = std.AutoHashMapUnmanaged(*Atom, std.ArrayListUnmanaged(u32));128const BaseRelocationTable = std.AutoHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(u32));
129const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(*Atom));129const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(Atom.Index));
130130
131const default_file_alignment: u16 = 0x200;131const default_file_alignment: u16 = 0x200;
132const default_size_of_stack_reserve: u32 = 0x1000000;132const default_size_of_stack_reserve: u32 = 0x1000000;
...@@ -137,7 +137,7 @@ const default_size_of_heap_commit: u32 = 0x1000;...@@ -137,7 +137,7 @@ const default_size_of_heap_commit: u32 = 0x1000;
137const Section = struct {137const Section = struct {
138 header: coff.SectionHeader,138 header: coff.SectionHeader,
139139
140 last_atom: ?*Atom = null,140 last_atom_index: ?Atom.Index = null,
141141
142 /// A list of atoms that have surplus capacity. This list can have false142 /// A list of atoms that have surplus capacity. This list can have false
143 /// positives, as functions grow and shrink over time, only sometimes being added143 /// positives, as functions grow and shrink over time, only sometimes being added
...@@ -154,7 +154,34 @@ const Section = struct {...@@ -154,7 +154,34 @@ const Section = struct {
154 /// overcapacity can be negative. A simple way to have negative overcapacity is to154 /// overcapacity can be negative. A simple way to have negative overcapacity is to
155 /// allocate a fresh atom, which will have ideal capacity, and then grow it155 /// allocate a fresh atom, which will have ideal capacity, and then grow it
156 /// by 1 byte. It will then have -1 overcapacity.156 /// by 1 byte. It will then have -1 overcapacity.
157 free_list: std.ArrayListUnmanaged(*Atom) = .{},157 free_list: std.ArrayListUnmanaged(Atom.Index) = .{},
158};
159
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 }
158};185};
159186
160pub const PtrWidth = enum {187pub const PtrWidth = enum {
...@@ -168,11 +195,6 @@ pub const PtrWidth = enum {...@@ -168,11 +195,6 @@ pub const PtrWidth = enum {
168 };195 };
169 }196 }
170};197};
171pub const SrcFn = void;
172
173pub const Export = struct {
174 sym_index: ?u32 = null,
175};
176198
177pub const SymbolWithLoc = struct {199pub const SymbolWithLoc = struct {
178 // Index into the respective symbol table.200 // Index into the respective symbol table.
...@@ -271,11 +293,7 @@ pub fn deinit(self: *Coff) void {...@@ -271,11 +293,7 @@ pub fn deinit(self: *Coff) void {
271 }293 }
272 self.sections.deinit(gpa);294 self.sections.deinit(gpa);
273295
274 for (self.managed_atoms.items) |atom| {296 self.atoms.deinit(gpa);
275 gpa.destroy(atom);
276 }
277 self.managed_atoms.deinit(gpa);
278
279 self.locals.deinit(gpa);297 self.locals.deinit(gpa);
280 self.globals.deinit(gpa);298 self.globals.deinit(gpa);
281299
...@@ -297,7 +315,15 @@ pub fn deinit(self: *Coff) void {...@@ -297,7 +315,15 @@ pub fn deinit(self: *Coff) void {
297 self.imports.deinit(gpa);315 self.imports.deinit(gpa);
298 self.imports_free_list.deinit(gpa);316 self.imports_free_list.deinit(gpa);
299 self.imports_table.deinit(gpa);317 self.imports_table.deinit(gpa);
300 self.decls.deinit(gpa);318
319 {
320 var it = self.decls.iterator();
321 while (it.next()) |entry| {
322 entry.value_ptr.exports.deinit(gpa);
323 }
324 self.decls.deinit(gpa);
325 }
326
301 self.atom_by_index_table.deinit(gpa);327 self.atom_by_index_table.deinit(gpa);
302328
303 {329 {
...@@ -461,17 +487,18 @@ fn growSectionVM(self: *Coff, sect_id: u32, needed_size: u32) !void {...@@ -461,17 +487,18 @@ fn growSectionVM(self: *Coff, sect_id: u32, needed_size: u32) !void {
461 // TODO: enforce order by increasing VM addresses in self.sections container.487 // TODO: enforce order by increasing VM addresses in self.sections container.
462 // This is required by the loader anyhow as far as I can tell.488 // This is required by the loader anyhow as far as I can tell.
463 for (self.sections.items(.header)[sect_id + 1 ..]) |*next_header, next_sect_id| {489 for (self.sections.items(.header)[sect_id + 1 ..]) |*next_header, next_sect_id| {
464 const maybe_last_atom = &self.sections.items(.last_atom)[sect_id + 1 + next_sect_id];490 const maybe_last_atom_index = self.sections.items(.last_atom_index)[sect_id + 1 + next_sect_id];
465 next_header.virtual_address += diff;491 next_header.virtual_address += diff;
466492
467 if (maybe_last_atom.*) |last_atom| {493 if (maybe_last_atom_index) |last_atom_index| {
468 var atom = last_atom;494 var atom_index = last_atom_index;
469 while (true) {495 while (true) {
496 const atom = self.getAtom(atom_index);
470 const sym = atom.getSymbolPtr(self);497 const sym = atom.getSymbolPtr(self);
471 sym.value += diff;498 sym.value += diff;
472499
473 if (atom.prev) |prev| {500 if (atom.prev_index) |prev_index| {
474 atom = prev;501 atom_index = prev_index;
475 } else break;502 } else break;
476 }503 }
477 }504 }
...@@ -480,24 +507,15 @@ fn growSectionVM(self: *Coff, sect_id: u32, needed_size: u32) !void {...@@ -480,24 +507,15 @@ fn growSectionVM(self: *Coff, sect_id: u32, needed_size: u32) !void {
480 header.virtual_size = increased_size;507 header.virtual_size = increased_size;
481}508}
482509
483pub fn allocateDeclIndexes(self: *Coff, decl_index: Module.Decl.Index) !void {510fn allocateAtom(self: *Coff, atom_index: Atom.Index, new_atom_size: u32, alignment: u32) !u32 {
484 if (self.llvm_object) |_| return;
485 const decl = self.base.options.module.?.declPtr(decl_index);
486 if (decl.link.coff.sym_index != 0) return;
487 decl.link.coff.sym_index = try self.allocateSymbol();
488 const gpa = self.base.allocator;
489 try self.atom_by_index_table.putNoClobber(gpa, decl.link.coff.sym_index, &decl.link.coff);
490 try self.decls.putNoClobber(gpa, decl_index, null);
491}
492
493fn allocateAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u32 {
494 const tracy = trace(@src());511 const tracy = trace(@src());
495 defer tracy.end();512 defer tracy.end();
496513
514 const atom = self.getAtom(atom_index);
497 const sect_id = @enumToInt(atom.getSymbol(self).section_number) - 1;515 const sect_id = @enumToInt(atom.getSymbol(self).section_number) - 1;
498 const header = &self.sections.items(.header)[sect_id];516 const header = &self.sections.items(.header)[sect_id];
499 const free_list = &self.sections.items(.free_list)[sect_id];517 const free_list = &self.sections.items(.free_list)[sect_id];
500 const maybe_last_atom = &self.sections.items(.last_atom)[sect_id];518 const maybe_last_atom_index = &self.sections.items(.last_atom_index)[sect_id];
501 const new_atom_ideal_capacity = if (header.isCode()) padToIdeal(new_atom_size) else new_atom_size;519 const new_atom_ideal_capacity = if (header.isCode()) padToIdeal(new_atom_size) else new_atom_size;
502520
503 // We use these to indicate our intention to update metadata, placing the new atom,521 // We use these to indicate our intention to update metadata, placing the new atom,
...@@ -505,7 +523,7 @@ fn allocateAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u...@@ -505,7 +523,7 @@ fn allocateAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u
505 // It would be simpler to do it inside the for loop below, but that would cause a523 // It would be simpler to do it inside the for loop below, but that would cause a
506 // problem if an error was returned later in the function. So this action524 // problem if an error was returned later in the function. So this action
507 // is actually carried out at the end of the function, when errors are no longer possible.525 // is actually carried out at the end of the function, when errors are no longer possible.
508 var atom_placement: ?*Atom = null;526 var atom_placement: ?Atom.Index = null;
509 var free_list_removal: ?usize = null;527 var free_list_removal: ?usize = null;
510528
511 // First we look for an appropriately sized free list node.529 // First we look for an appropriately sized free list node.
...@@ -513,7 +531,8 @@ fn allocateAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u...@@ -513,7 +531,8 @@ fn allocateAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u
513 var vaddr = blk: {531 var vaddr = blk: {
514 var i: usize = 0;532 var i: usize = 0;
515 while (i < free_list.items.len) {533 while (i < free_list.items.len) {
516 const big_atom = free_list.items[i];534 const big_atom_index = free_list.items[i];
535 const big_atom = self.getAtom(big_atom_index);
517 // We now have a pointer to a live atom that has too much capacity.536 // We now have a pointer to a live atom that has too much capacity.
518 // Is it enough that we could fit this new atom?537 // Is it enough that we could fit this new atom?
519 const sym = big_atom.getSymbol(self);538 const sym = big_atom.getSymbol(self);
...@@ -541,34 +560,43 @@ fn allocateAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u...@@ -541,34 +560,43 @@ fn allocateAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u
541 const keep_free_list_node = remaining_capacity >= min_text_capacity;560 const keep_free_list_node = remaining_capacity >= min_text_capacity;
542561
543 // Set up the metadata to be updated, after errors are no longer possible.562 // Set up the metadata to be updated, after errors are no longer possible.
544 atom_placement = big_atom;563 atom_placement = big_atom_index;
545 if (!keep_free_list_node) {564 if (!keep_free_list_node) {
546 free_list_removal = i;565 free_list_removal = i;
547 }566 }
548 break :blk new_start_vaddr;567 break :blk new_start_vaddr;
549 } else if (maybe_last_atom.*) |last| {568 } else if (maybe_last_atom_index.*) |last_index| {
569 const last = self.getAtom(last_index);
550 const last_symbol = last.getSymbol(self);570 const last_symbol = last.getSymbol(self);
551 const ideal_capacity = if (header.isCode()) padToIdeal(last.size) else last.size;571 const ideal_capacity = if (header.isCode()) padToIdeal(last.size) else last.size;
552 const ideal_capacity_end_vaddr = last_symbol.value + ideal_capacity;572 const ideal_capacity_end_vaddr = last_symbol.value + ideal_capacity;
553 const new_start_vaddr = mem.alignForwardGeneric(u32, ideal_capacity_end_vaddr, alignment);573 const new_start_vaddr = mem.alignForwardGeneric(u32, ideal_capacity_end_vaddr, alignment);
554 atom_placement = last;574 atom_placement = last_index;
555 break :blk new_start_vaddr;575 break :blk new_start_vaddr;
556 } else {576 } else {
557 break :blk mem.alignForwardGeneric(u32, header.virtual_address, alignment);577 break :blk mem.alignForwardGeneric(u32, header.virtual_address, alignment);
558 }578 }
559 };579 };
560580
561 const expand_section = atom_placement == null or atom_placement.?.next == null;581 const expand_section = if (atom_placement) |placement_index|
582 self.getAtom(placement_index).next_index == null
583 else
584 true;
562 if (expand_section) {585 if (expand_section) {
563 const sect_capacity = self.allocatedSize(header.pointer_to_raw_data);586 const sect_capacity = self.allocatedSize(header.pointer_to_raw_data);
564 const needed_size: u32 = (vaddr + new_atom_size) - header.virtual_address;587 const needed_size: u32 = (vaddr + new_atom_size) - header.virtual_address;
565 if (needed_size > sect_capacity) {588 if (needed_size > sect_capacity) {
566 const new_offset = self.findFreeSpace(needed_size, default_file_alignment);589 const new_offset = self.findFreeSpace(needed_size, default_file_alignment);
567 const current_size = if (maybe_last_atom.*) |last_atom| blk: {590 const current_size = if (maybe_last_atom_index.*) |last_atom_index| blk: {
591 const last_atom = self.getAtom(last_atom_index);
568 const sym = last_atom.getSymbol(self);592 const sym = last_atom.getSymbol(self);
569 break :blk (sym.value + last_atom.size) - header.virtual_address;593 break :blk (sym.value + last_atom.size) - header.virtual_address;
570 } else 0;594 } else 0;
571 log.debug("moving {s} from 0x{x} to 0x{x}", .{ self.getSectionName(header), header.pointer_to_raw_data, new_offset });595 log.debug("moving {s} from 0x{x} to 0x{x}", .{
596 self.getSectionName(header),
597 header.pointer_to_raw_data,
598 new_offset,
599 });
572 const amt = try self.base.file.?.copyRangeAll(600 const amt = try self.base.file.?.copyRangeAll(
573 header.pointer_to_raw_data,601 header.pointer_to_raw_data,
574 self.base.file.?,602 self.base.file.?,
...@@ -587,26 +615,34 @@ fn allocateAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u...@@ -587,26 +615,34 @@ fn allocateAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u
587615
588 header.virtual_size = @max(header.virtual_size, needed_size);616 header.virtual_size = @max(header.virtual_size, needed_size);
589 header.size_of_raw_data = needed_size;617 header.size_of_raw_data = needed_size;
590 maybe_last_atom.* = atom;618 maybe_last_atom_index.* = atom_index;
591 }619 }
592620
593 atom.size = new_atom_size;621 {
594 atom.alignment = alignment;622 const atom_ptr = self.getAtomPtr(atom_index);
623 atom_ptr.size = new_atom_size;
624 atom_ptr.alignment = alignment;
625 }
595626
596 if (atom.prev) |prev| {627 if (atom.prev_index) |prev_index| {
597 prev.next = atom.next;628 const prev = self.getAtomPtr(prev_index);
629 prev.next_index = atom.next_index;
598 }630 }
599 if (atom.next) |next| {631 if (atom.next_index) |next_index| {
600 next.prev = atom.prev;632 const next = self.getAtomPtr(next_index);
633 next.prev_index = atom.prev_index;
601 }634 }
602635
603 if (atom_placement) |big_atom| {636 if (atom_placement) |big_atom_index| {
604 atom.prev = big_atom;637 const big_atom = self.getAtomPtr(big_atom_index);
605 atom.next = big_atom.next;638 const atom_ptr = self.getAtomPtr(atom_index);
606 big_atom.next = atom;639 atom_ptr.prev_index = big_atom_index;
640 atom_ptr.next_index = big_atom.next_index;
641 big_atom.next_index = atom_index;
607 } else {642 } else {
608 atom.prev = null;643 const atom_ptr = self.getAtomPtr(atom_index);
609 atom.next = null;644 atom_ptr.prev_index = null;
645 atom_ptr.next_index = null;
610 }646 }
611 if (free_list_removal) |i| {647 if (free_list_removal) |i| {
612 _ = free_list.swapRemove(i);648 _ = free_list.swapRemove(i);
...@@ -615,7 +651,7 @@ fn allocateAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u...@@ -615,7 +651,7 @@ fn allocateAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u
615 return vaddr;651 return vaddr;
616}652}
617653
618fn allocateSymbol(self: *Coff) !u32 {654pub fn allocateSymbol(self: *Coff) !u32 {
619 const gpa = self.base.allocator;655 const gpa = self.base.allocator;
620 try self.locals.ensureUnusedCapacity(gpa, 1);656 try self.locals.ensureUnusedCapacity(gpa, 1);
621657
...@@ -711,25 +747,37 @@ pub fn allocateImportEntry(self: *Coff, target: SymbolWithLoc) !u32 {...@@ -711,25 +747,37 @@ pub fn allocateImportEntry(self: *Coff, target: SymbolWithLoc) !u32 {
711 return index;747 return index;
712}748}
713749
714fn createGotAtom(self: *Coff, target: SymbolWithLoc) !*Atom {750pub fn createAtom(self: *Coff) !Atom.Index {
715 const gpa = self.base.allocator;751 const gpa = self.base.allocator;
716 const atom = try gpa.create(Atom);752 const atom_index = @intCast(Atom.Index, self.atoms.items.len);
717 errdefer gpa.destroy(atom);753 const atom = try self.atoms.addOne(gpa);
718 atom.* = Atom.empty;754 const sym_index = try self.allocateSymbol();
719 atom.sym_index = try self.allocateSymbol();755 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom_index);
756 atom.* = .{
757 .sym_index = sym_index,
758 .file = null,
759 .size = 0,
760 .alignment = 0,
761 .prev_index = null,
762 .next_index = null,
763 };
764 log.debug("creating ATOM(%{d}) at index {d}", .{ sym_index, atom_index });
765 return atom_index;
766}
767
768fn createGotAtom(self: *Coff, target: SymbolWithLoc) !Atom.Index {
769 const atom_index = try self.createAtom();
770 const atom = self.getAtomPtr(atom_index);
720 atom.size = @sizeOf(u64);771 atom.size = @sizeOf(u64);
721 atom.alignment = @alignOf(u64);772 atom.alignment = @alignOf(u64);
722773
723 try self.managed_atoms.append(gpa, atom);
724 try self.atom_by_index_table.putNoClobber(gpa, atom.sym_index, atom);
725
726 const sym = atom.getSymbolPtr(self);774 const sym = atom.getSymbolPtr(self);
727 sym.section_number = @intToEnum(coff.SectionNumber, self.got_section_index.? + 1);775 sym.section_number = @intToEnum(coff.SectionNumber, self.got_section_index.? + 1);
728 sym.value = try self.allocateAtom(atom, atom.size, atom.alignment);776 sym.value = try self.allocateAtom(atom_index, atom.size, atom.alignment);
729777
730 log.debug("allocated GOT atom at 0x{x}", .{sym.value});778 log.debug("allocated GOT atom at 0x{x}", .{sym.value});
731779
732 try atom.addRelocation(self, .{780 try Atom.addRelocation(self, atom_index, .{
733 .type = .direct,781 .type = .direct,
734 .target = target,782 .target = target,
735 .offset = 0,783 .offset = 0,
...@@ -743,67 +791,67 @@ fn createGotAtom(self: *Coff, target: SymbolWithLoc) !*Atom {...@@ -743,67 +791,67 @@ fn createGotAtom(self: *Coff, target: SymbolWithLoc) !*Atom {
743 .UNDEFINED => @panic("TODO generate a binding for undefined GOT target"),791 .UNDEFINED => @panic("TODO generate a binding for undefined GOT target"),
744 .ABSOLUTE => {},792 .ABSOLUTE => {},
745 .DEBUG => unreachable, // not possible793 .DEBUG => unreachable, // not possible
746 else => try atom.addBaseRelocation(self, 0),794 else => try Atom.addBaseRelocation(self, atom_index, 0),
747 }795 }
748796
749 return atom;797 return atom_index;
750}798}
751799
752fn createImportAtom(self: *Coff) !*Atom {800fn createImportAtom(self: *Coff) !Atom.Index {
753 const gpa = self.base.allocator;801 const atom_index = try self.createAtom();
754 const atom = try gpa.create(Atom);802 const atom = self.getAtomPtr(atom_index);
755 errdefer gpa.destroy(atom);
756 atom.* = Atom.empty;
757 atom.sym_index = try self.allocateSymbol();
758 atom.size = @sizeOf(u64);803 atom.size = @sizeOf(u64);
759 atom.alignment = @alignOf(u64);804 atom.alignment = @alignOf(u64);
760805
761 try self.managed_atoms.append(gpa, atom);
762 try self.atom_by_index_table.putNoClobber(gpa, atom.sym_index, atom);
763
764 const sym = atom.getSymbolPtr(self);806 const sym = atom.getSymbolPtr(self);
765 sym.section_number = @intToEnum(coff.SectionNumber, self.idata_section_index.? + 1);807 sym.section_number = @intToEnum(coff.SectionNumber, self.idata_section_index.? + 1);
766 sym.value = try self.allocateAtom(atom, atom.size, atom.alignment);808 sym.value = try self.allocateAtom(atom_index, atom.size, atom.alignment);
767809
768 log.debug("allocated import atom at 0x{x}", .{sym.value});810 log.debug("allocated import atom at 0x{x}", .{sym.value});
769811
770 return atom;812 return atom_index;
771}813}
772814
773fn growAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u32 {815fn growAtom(self: *Coff, atom_index: Atom.Index, new_atom_size: u32, alignment: u32) !u32 {
816 const atom = self.getAtom(atom_index);
774 const sym = atom.getSymbol(self);817 const sym = atom.getSymbol(self);
775 const align_ok = mem.alignBackwardGeneric(u32, sym.value, alignment) == sym.value;818 const align_ok = mem.alignBackwardGeneric(u32, sym.value, alignment) == sym.value;
776 const need_realloc = !align_ok or new_atom_size > atom.capacity(self);819 const need_realloc = !align_ok or new_atom_size > atom.capacity(self);
777 if (!need_realloc) return sym.value;820 if (!need_realloc) return sym.value;
778 return self.allocateAtom(atom, new_atom_size, alignment);821 return self.allocateAtom(atom_index, new_atom_size, alignment);
779}822}
780823
781fn shrinkAtom(self: *Coff, atom: *Atom, new_block_size: u32) void {824fn shrinkAtom(self: *Coff, atom_index: Atom.Index, new_block_size: u32) void {
782 _ = self;825 _ = self;
783 _ = atom;826 _ = atom_index;
784 _ = new_block_size;827 _ = new_block_size;
785 // TODO check the new capacity, and if it crosses the size threshold into a big enough828 // TODO check the new capacity, and if it crosses the size threshold into a big enough
786 // capacity, insert a free list node for it.829 // capacity, insert a free list node for it.
787}830}
788831
789fn writeAtom(self: *Coff, atom: *Atom, code: []const u8) !void {832fn writeAtom(self: *Coff, atom_index: Atom.Index, code: []const u8) !void {
833 const atom = self.getAtom(atom_index);
790 const sym = atom.getSymbol(self);834 const sym = atom.getSymbol(self);
791 const section = self.sections.get(@enumToInt(sym.section_number) - 1);835 const section = self.sections.get(@enumToInt(sym.section_number) - 1);
792 const file_offset = section.header.pointer_to_raw_data + sym.value - section.header.virtual_address;836 const file_offset = section.header.pointer_to_raw_data + sym.value - section.header.virtual_address;
793 log.debug("writing atom for symbol {s} at file offset 0x{x} to 0x{x}", .{ atom.getName(self), file_offset, file_offset + code.len });837 log.debug("writing atom for symbol {s} at file offset 0x{x} to 0x{x}", .{
838 atom.getName(self),
839 file_offset,
840 file_offset + code.len,
841 });
794 try self.base.file.?.pwriteAll(code, file_offset);842 try self.base.file.?.pwriteAll(code, file_offset);
795 try self.resolveRelocs(atom);843 try self.resolveRelocs(atom_index);
796}844}
797845
798fn writePtrWidthAtom(self: *Coff, atom: *Atom) !void {846fn writePtrWidthAtom(self: *Coff, atom_index: Atom.Index) !void {
799 switch (self.ptr_width) {847 switch (self.ptr_width) {
800 .p32 => {848 .p32 => {
801 var buffer: [@sizeOf(u32)]u8 = [_]u8{0} ** @sizeOf(u32);849 var buffer: [@sizeOf(u32)]u8 = [_]u8{0} ** @sizeOf(u32);
802 try self.writeAtom(atom, &buffer);850 try self.writeAtom(atom_index, &buffer);
803 },851 },
804 .p64 => {852 .p64 => {
805 var buffer: [@sizeOf(u64)]u8 = [_]u8{0} ** @sizeOf(u64);853 var buffer: [@sizeOf(u64)]u8 = [_]u8{0} ** @sizeOf(u64);
806 try self.writeAtom(atom, &buffer);854 try self.writeAtom(atom_index, &buffer);
807 },855 },
808 }856 }
809}857}
...@@ -823,7 +871,8 @@ fn markRelocsDirtyByAddress(self: *Coff, addr: u32) void {...@@ -823,7 +871,8 @@ fn markRelocsDirtyByAddress(self: *Coff, addr: u32) void {
823 var it = self.relocs.valueIterator();871 var it = self.relocs.valueIterator();
824 while (it.next()) |relocs| {872 while (it.next()) |relocs| {
825 for (relocs.items) |*reloc| {873 for (relocs.items) |*reloc| {
826 const target_atom = reloc.getTargetAtom(self) orelse continue;874 const target_atom_index = reloc.getTargetAtomIndex(self) orelse continue;
875 const target_atom = self.getAtom(target_atom_index);
827 const target_sym = target_atom.getSymbol(self);876 const target_sym = target_atom.getSymbol(self);
828 if (target_sym.value < addr) continue;877 if (target_sym.value < addr) continue;
829 reloc.dirty = true;878 reloc.dirty = true;
...@@ -831,23 +880,26 @@ fn markRelocsDirtyByAddress(self: *Coff, addr: u32) void {...@@ -831,23 +880,26 @@ fn markRelocsDirtyByAddress(self: *Coff, addr: u32) void {
831 }880 }
832}881}
833882
834fn resolveRelocs(self: *Coff, atom: *Atom) !void {883fn resolveRelocs(self: *Coff, atom_index: Atom.Index) !void {
835 const relocs = self.relocs.get(atom) orelse return;884 const relocs = self.relocs.get(atom_index) orelse return;
836885
837 log.debug("relocating '{s}'", .{atom.getName(self)});886 log.debug("relocating '{s}'", .{self.getAtom(atom_index).getName(self)});
838887
839 for (relocs.items) |*reloc| {888 for (relocs.items) |*reloc| {
840 if (!reloc.dirty) continue;889 if (!reloc.dirty) continue;
841 try reloc.resolve(atom, self);890 try reloc.resolve(atom_index, self);
842 }891 }
843}892}
844893
845fn freeAtom(self: *Coff, atom: *Atom) void {894fn freeAtom(self: *Coff, atom_index: Atom.Index) void {
846 log.debug("freeAtom {*}", .{atom});895 log.debug("freeAtom {d}", .{atom_index});
896
897 const gpa = self.base.allocator;
847898
848 // Remove any relocs and base relocs associated with this Atom899 // Remove any relocs and base relocs associated with this Atom
849 self.freeRelocationsForAtom(atom);900 Atom.freeRelocations(self, atom_index);
850901
902 const atom = self.getAtom(atom_index);
851 const sym = atom.getSymbol(self);903 const sym = atom.getSymbol(self);
852 const sect_id = @enumToInt(sym.section_number) - 1;904 const sect_id = @enumToInt(sym.section_number) - 1;
853 const free_list = &self.sections.items(.free_list)[sect_id];905 const free_list = &self.sections.items(.free_list)[sect_id];
...@@ -856,46 +908,69 @@ fn freeAtom(self: *Coff, atom: *Atom) void {...@@ -856,46 +908,69 @@ fn freeAtom(self: *Coff, atom: *Atom) void {
856 var i: usize = 0;908 var i: usize = 0;
857 // TODO turn free_list into a hash map909 // TODO turn free_list into a hash map
858 while (i < free_list.items.len) {910 while (i < free_list.items.len) {
859 if (free_list.items[i] == atom) {911 if (free_list.items[i] == atom_index) {
860 _ = free_list.swapRemove(i);912 _ = free_list.swapRemove(i);
861 continue;913 continue;
862 }914 }
863 if (free_list.items[i] == atom.prev) {915 if (free_list.items[i] == atom.prev_index) {
864 already_have_free_list_node = true;916 already_have_free_list_node = true;
865 }917 }
866 i += 1;918 i += 1;
867 }919 }
868 }920 }
869921
870 const maybe_last_atom = &self.sections.items(.last_atom)[sect_id];922 const maybe_last_atom_index = &self.sections.items(.last_atom_index)[sect_id];
871 if (maybe_last_atom.*) |last_atom| {923 if (maybe_last_atom_index.*) |last_atom_index| {
872 if (last_atom == atom) {924 if (last_atom_index == atom_index) {
873 if (atom.prev) |prev| {925 if (atom.prev_index) |prev_index| {
874 // TODO shrink the section size here926 // TODO shrink the section size here
875 maybe_last_atom.* = prev;927 maybe_last_atom_index.* = prev_index;
876 } else {928 } else {
877 maybe_last_atom.* = null;929 maybe_last_atom_index.* = null;
878 }930 }
879 }931 }
880 }932 }
881933
882 if (atom.prev) |prev| {934 if (atom.prev_index) |prev_index| {
883 prev.next = atom.next;935 const prev = self.getAtomPtr(prev_index);
936 prev.next_index = atom.next_index;
884937
885 if (!already_have_free_list_node and prev.freeListEligible(self)) {938 if (!already_have_free_list_node and prev.*.freeListEligible(self)) {
886 // The free list is heuristics, it doesn't have to be perfect, so we can939 // The free list is heuristics, it doesn't have to be perfect, so we can
887 // ignore the OOM here.940 // ignore the OOM here.
888 free_list.append(self.base.allocator, prev) catch {};941 free_list.append(gpa, prev_index) catch {};
889 }942 }
890 } else {943 } else {
891 atom.prev = null;944 self.getAtomPtr(atom_index).prev_index = null;
892 }945 }
893946
894 if (atom.next) |next| {947 if (atom.next_index) |next_index| {
895 next.prev = atom.prev;948 self.getAtomPtr(next_index).prev_index = atom.prev_index;
896 } else {949 } else {
897 atom.next = null;950 self.getAtomPtr(atom_index).next_index = null;
951 }
952
953 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
954 const sym_index = atom.getSymbolIndex().?;
955 self.locals_free_list.append(gpa, sym_index) catch {};
956
957 // Try freeing GOT atom if this decl had one
958 const got_target = SymbolWithLoc{ .sym_index = sym_index, .file = null };
959 if (self.got_entries_table.get(got_target)) |got_index| {
960 self.got_entries_free_list.append(gpa, @intCast(u32, got_index)) catch {};
961 self.got_entries.items[got_index] = .{
962 .target = .{ .sym_index = 0, .file = null },
963 .sym_index = 0,
964 };
965 _ = self.got_entries_table.remove(got_target);
966
967 log.debug(" adding GOT index {d} to free list (target local@{d})", .{ got_index, sym_index });
898 }968 }
969
970 self.locals.items[sym_index].section_number = .UNDEFINED;
971 _ = self.atom_by_index_table.remove(sym_index);
972 log.debug(" adding local symbol index {d} to free list", .{sym_index});
973 self.getAtomPtr(atom_index).sym_index = 0;
899}974}
900975
901pub fn updateFunc(self: *Coff, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void {976pub fn updateFunc(self: *Coff, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void {
...@@ -912,8 +987,10 @@ pub fn updateFunc(self: *Coff, module: *Module, func: *Module.Fn, air: Air, live...@@ -912,8 +987,10 @@ pub fn updateFunc(self: *Coff, module: *Module, func: *Module.Fn, air: Air, live
912987
913 const decl_index = func.owner_decl;988 const decl_index = func.owner_decl;
914 const decl = module.declPtr(decl_index);989 const decl = module.declPtr(decl_index);
990
991 const atom_index = try self.getOrCreateAtomForDecl(decl_index);
915 self.freeUnnamedConsts(decl_index);992 self.freeUnnamedConsts(decl_index);
916 self.freeRelocationsForAtom(&decl.link.coff);993 Atom.freeRelocations(self, atom_index);
917994
918 var code_buffer = std.ArrayList(u8).init(self.base.allocator);995 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
919 defer code_buffer.deinit();996 defer code_buffer.deinit();
...@@ -928,7 +1005,7 @@ pub fn updateFunc(self: *Coff, module: *Module, func: *Module.Fn, air: Air, live...@@ -928,7 +1005,7 @@ pub fn updateFunc(self: *Coff, module: *Module, func: *Module.Fn, air: Air, live
928 .none,1005 .none,
929 );1006 );
930 const code = switch (res) {1007 const code = switch (res) {
931 .appended => code_buffer.items,1008 .ok => code_buffer.items,
932 .fail => |em| {1009 .fail => |em| {
933 decl.analysis = .codegen_failure;1010 decl.analysis = .codegen_failure;
934 try module.failed_decls.put(module.gpa, decl_index, em);1011 try module.failed_decls.put(module.gpa, decl_index, em);
...@@ -957,12 +1034,8 @@ pub fn lowerUnnamedConst(self: *Coff, tv: TypedValue, decl_index: Module.Decl.In...@@ -957,12 +1034,8 @@ pub fn lowerUnnamedConst(self: *Coff, tv: TypedValue, decl_index: Module.Decl.In
957 }1034 }
958 const unnamed_consts = gop.value_ptr;1035 const unnamed_consts = gop.value_ptr;
9591036
960 const atom = try gpa.create(Atom);1037 const atom_index = try self.createAtom();
961 errdefer gpa.destroy(atom);
962 atom.* = Atom.empty;
9631038
964 atom.sym_index = try self.allocateSymbol();
965 const sym = atom.getSymbolPtr(self);
966 const sym_name = blk: {1039 const sym_name = blk: {
967 const decl_name = try decl.getFullyQualifiedName(mod);1040 const decl_name = try decl.getFullyQualifiedName(mod);
968 defer gpa.free(decl_name);1041 defer gpa.free(decl_name);
...@@ -971,18 +1044,18 @@ pub fn lowerUnnamedConst(self: *Coff, tv: TypedValue, decl_index: Module.Decl.In...@@ -971,18 +1044,18 @@ pub fn lowerUnnamedConst(self: *Coff, tv: TypedValue, decl_index: Module.Decl.In
971 break :blk try std.fmt.allocPrint(gpa, "__unnamed_{s}_{d}", .{ decl_name, index });1044 break :blk try std.fmt.allocPrint(gpa, "__unnamed_{s}_{d}", .{ decl_name, index });
972 };1045 };
973 defer gpa.free(sym_name);1046 defer gpa.free(sym_name);
974 try self.setSymbolName(sym, sym_name);1047 {
975 sym.section_number = @intToEnum(coff.SectionNumber, self.rdata_section_index.? + 1);1048 const atom = self.getAtom(atom_index);
9761049 const sym = atom.getSymbolPtr(self);
977 try self.managed_atoms.append(gpa, atom);1050 try self.setSymbolName(sym, sym_name);
978 try self.atom_by_index_table.putNoClobber(gpa, atom.sym_index, atom);1051 sym.section_number = @intToEnum(coff.SectionNumber, self.rdata_section_index.? + 1);
1052 }
9791053
980 const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), tv, &code_buffer, .none, .{1054 const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), tv, &code_buffer, .none, .{
981 .parent_atom_index = atom.sym_index,1055 .parent_atom_index = self.getAtom(atom_index).getSymbolIndex().?,
982 });1056 });
983 const code = switch (res) {1057 const code = switch (res) {
984 .externally_managed => |x| x,1058 .ok => code_buffer.items,
985 .appended => code_buffer.items,
986 .fail => |em| {1059 .fail => |em| {
987 decl.analysis = .codegen_failure;1060 decl.analysis = .codegen_failure;
988 try mod.failed_decls.put(mod.gpa, decl_index, em);1061 try mod.failed_decls.put(mod.gpa, decl_index, em);
...@@ -992,19 +1065,20 @@ pub fn lowerUnnamedConst(self: *Coff, tv: TypedValue, decl_index: Module.Decl.In...@@ -992,19 +1065,20 @@ pub fn lowerUnnamedConst(self: *Coff, tv: TypedValue, decl_index: Module.Decl.In
992 };1065 };
9931066
994 const required_alignment = tv.ty.abiAlignment(self.base.options.target);1067 const required_alignment = tv.ty.abiAlignment(self.base.options.target);
1068 const atom = self.getAtomPtr(atom_index);
995 atom.alignment = required_alignment;1069 atom.alignment = required_alignment;
996 atom.size = @intCast(u32, code.len);1070 atom.size = @intCast(u32, code.len);
997 sym.value = try self.allocateAtom(atom, atom.size, atom.alignment);1071 atom.getSymbolPtr(self).value = try self.allocateAtom(atom_index, atom.size, atom.alignment);
998 errdefer self.freeAtom(atom);1072 errdefer self.freeAtom(atom_index);
9991073
1000 try unnamed_consts.append(gpa, atom);1074 try unnamed_consts.append(gpa, atom_index);
10011075
1002 log.debug("allocated atom for {s} at 0x{x}", .{ sym_name, sym.value });1076 log.debug("allocated atom for {s} at 0x{x}", .{ sym_name, atom.getSymbol(self).value });
1003 log.debug(" (required alignment 0x{x})", .{required_alignment});1077 log.debug(" (required alignment 0x{x})", .{required_alignment});
10041078
1005 try self.writeAtom(atom, code);1079 try self.writeAtom(atom_index, code);
10061080
1007 return atom.sym_index;1081 return atom.getSymbolIndex().?;
1008}1082}
10091083
1010pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) !void {1084pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) !void {
...@@ -1029,7 +1103,9 @@ pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) !...@@ -1029,7 +1103,9 @@ pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) !
1029 }1103 }
1030 }1104 }
10311105
1032 self.freeRelocationsForAtom(&decl.link.coff);1106 const atom_index = try self.getOrCreateAtomForDecl(decl_index);
1107 Atom.freeRelocations(self, atom_index);
1108 const atom = self.getAtom(atom_index);
10331109
1034 var code_buffer = std.ArrayList(u8).init(self.base.allocator);1110 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
1035 defer code_buffer.deinit();1111 defer code_buffer.deinit();
...@@ -1039,11 +1115,10 @@ pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) !...@@ -1039,11 +1115,10 @@ pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) !
1039 .ty = decl.ty,1115 .ty = decl.ty,
1040 .val = decl_val,1116 .val = decl_val,
1041 }, &code_buffer, .none, .{1117 }, &code_buffer, .none, .{
1042 .parent_atom_index = decl.link.coff.sym_index,1118 .parent_atom_index = atom.getSymbolIndex().?,
1043 });1119 });
1044 const code = switch (res) {1120 const code = switch (res) {
1045 .externally_managed => |x| x,1121 .ok => code_buffer.items,
1046 .appended => code_buffer.items,
1047 .fail => |em| {1122 .fail => |em| {
1048 decl.analysis = .codegen_failure;1123 decl.analysis = .codegen_failure;
1049 try module.failed_decls.put(module.gpa, decl_index, em);1124 try module.failed_decls.put(module.gpa, decl_index, em);
...@@ -1058,7 +1133,20 @@ pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) !...@@ -1058,7 +1133,20 @@ pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) !
1058 return self.updateDeclExports(module, decl_index, module.getDeclExports(decl_index));1133 return self.updateDeclExports(module, decl_index, module.getDeclExports(decl_index));
1059}1134}
10601135
1061fn getDeclOutputSection(self: *Coff, decl: *Module.Decl) u16 {1136pub fn getOrCreateAtomForDecl(self: *Coff, decl_index: Module.Decl.Index) !Atom.Index {
1137 const gop = try self.decls.getOrPut(self.base.allocator, decl_index);
1138 if (!gop.found_existing) {
1139 gop.value_ptr.* = .{
1140 .atom = try self.createAtom(),
1141 .section = self.getDeclOutputSection(decl_index),
1142 .exports = .{},
1143 };
1144 }
1145 return gop.value_ptr.atom;
1146}
1147
1148fn getDeclOutputSection(self: *Coff, decl_index: Module.Decl.Index) u16 {
1149 const decl = self.base.options.module.?.declPtr(decl_index);
1062 const ty = decl.ty;1150 const ty = decl.ty;
1063 const zig_ty = ty.zigTypeTag();1151 const zig_ty = ty.zigTypeTag();
1064 const val = decl.val;1152 const val = decl.val;
...@@ -1093,15 +1181,12 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []const u8,...@@ -1093,15 +1181,12 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []const u8,
1093 log.debug("updateDeclCode {s}{*}", .{ decl_name, decl });1181 log.debug("updateDeclCode {s}{*}", .{ decl_name, decl });
1094 const required_alignment = decl.getAlignment(self.base.options.target);1182 const required_alignment = decl.getAlignment(self.base.options.target);
10951183
1096 const decl_ptr = self.decls.getPtr(decl_index).?;1184 const decl_metadata = self.decls.get(decl_index).?;
1097 if (decl_ptr.* == null) {1185 const atom_index = decl_metadata.atom;
1098 decl_ptr.* = self.getDeclOutputSection(decl);1186 const atom = self.getAtom(atom_index);
1099 }1187 const sect_index = decl_metadata.section;
1100 const sect_index = decl_ptr.*.?;
1101
1102 const code_len = @intCast(u32, code.len);1188 const code_len = @intCast(u32, code.len);
1103 const atom = &decl.link.coff;1189
1104 assert(atom.sym_index != 0); // Caller forgot to allocateDeclIndexes()
1105 if (atom.size != 0) {1190 if (atom.size != 0) {
1106 const sym = atom.getSymbolPtr(self);1191 const sym = atom.getSymbolPtr(self);
1107 try self.setSymbolName(sym, decl_name);1192 try self.setSymbolName(sym, decl_name);
...@@ -1111,62 +1196,51 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []const u8,...@@ -1111,62 +1196,51 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []const u8,
1111 const capacity = atom.capacity(self);1196 const capacity = atom.capacity(self);
1112 const need_realloc = code.len > capacity or !mem.isAlignedGeneric(u64, sym.value, required_alignment);1197 const need_realloc = code.len > capacity or !mem.isAlignedGeneric(u64, sym.value, required_alignment);
1113 if (need_realloc) {1198 if (need_realloc) {
1114 const vaddr = try self.growAtom(atom, code_len, required_alignment);1199 const vaddr = try self.growAtom(atom_index, code_len, required_alignment);
1115 log.debug("growing {s} from 0x{x} to 0x{x}", .{ decl_name, sym.value, vaddr });1200 log.debug("growing {s} from 0x{x} to 0x{x}", .{ decl_name, sym.value, vaddr });
1116 log.debug(" (required alignment 0x{x}", .{required_alignment});1201 log.debug(" (required alignment 0x{x}", .{required_alignment});
11171202
1118 if (vaddr != sym.value) {1203 if (vaddr != sym.value) {
1119 sym.value = vaddr;1204 sym.value = vaddr;
1120 log.debug(" (updating GOT entry)", .{});1205 log.debug(" (updating GOT entry)", .{});
1121 const got_target = SymbolWithLoc{ .sym_index = atom.sym_index, .file = null };1206 const got_target = SymbolWithLoc{ .sym_index = atom.getSymbolIndex().?, .file = null };
1122 const got_atom = self.getGotAtomForSymbol(got_target).?;1207 const got_atom_index = self.getGotAtomIndexForSymbol(got_target).?;
1123 self.markRelocsDirtyByTarget(got_target);1208 self.markRelocsDirtyByTarget(got_target);
1124 try self.writePtrWidthAtom(got_atom);1209 try self.writePtrWidthAtom(got_atom_index);
1125 }1210 }
1126 } else if (code_len < atom.size) {1211 } else if (code_len < atom.size) {
1127 self.shrinkAtom(atom, code_len);1212 self.shrinkAtom(atom_index, code_len);
1128 }1213 }
1129 atom.size = code_len;1214 self.getAtomPtr(atom_index).size = code_len;
1130 } else {1215 } else {
1131 const sym = atom.getSymbolPtr(self);1216 const sym = atom.getSymbolPtr(self);
1132 try self.setSymbolName(sym, decl_name);1217 try self.setSymbolName(sym, decl_name);
1133 sym.section_number = @intToEnum(coff.SectionNumber, sect_index + 1);1218 sym.section_number = @intToEnum(coff.SectionNumber, sect_index + 1);
1134 sym.type = .{ .complex_type = complex_type, .base_type = .NULL };1219 sym.type = .{ .complex_type = complex_type, .base_type = .NULL };
11351220
1136 const vaddr = try self.allocateAtom(atom, code_len, required_alignment);1221 const vaddr = try self.allocateAtom(atom_index, code_len, required_alignment);
1137 errdefer self.freeAtom(atom);1222 errdefer self.freeAtom(atom_index);
1138 log.debug("allocated atom for {s} at 0x{x}", .{ decl_name, vaddr });1223 log.debug("allocated atom for {s} at 0x{x}", .{ decl_name, vaddr });
1139 atom.size = code_len;1224 self.getAtomPtr(atom_index).size = code_len;
1140 sym.value = vaddr;1225 sym.value = vaddr;
11411226
1142 const got_target = SymbolWithLoc{ .sym_index = atom.sym_index, .file = null };1227 const got_target = SymbolWithLoc{ .sym_index = atom.getSymbolIndex().?, .file = null };
1143 const got_index = try self.allocateGotEntry(got_target);1228 const got_index = try self.allocateGotEntry(got_target);
1144 const got_atom = try self.createGotAtom(got_target);1229 const got_atom_index = try self.createGotAtom(got_target);
1145 self.got_entries.items[got_index].sym_index = got_atom.sym_index;1230 const got_atom = self.getAtom(got_atom_index);
1146 try self.writePtrWidthAtom(got_atom);1231 self.got_entries.items[got_index].sym_index = got_atom.getSymbolIndex().?;
1232 try self.writePtrWidthAtom(got_atom_index);
1147 }1233 }
11481234
1149 self.markRelocsDirtyByTarget(atom.getSymbolWithLoc());1235 self.markRelocsDirtyByTarget(atom.getSymbolWithLoc());
1150 try self.writeAtom(atom, code);1236 try self.writeAtom(atom_index, code);
1151}
1152
1153fn freeRelocationsForAtom(self: *Coff, atom: *Atom) void {
1154 var removed_relocs = self.relocs.fetchRemove(atom);
1155 if (removed_relocs) |*relocs| relocs.value.deinit(self.base.allocator);
1156 var removed_base_relocs = self.base_relocs.fetchRemove(atom);
1157 if (removed_base_relocs) |*base_relocs| base_relocs.value.deinit(self.base.allocator);
1158}1237}
11591238
1160fn freeUnnamedConsts(self: *Coff, decl_index: Module.Decl.Index) void {1239fn freeUnnamedConsts(self: *Coff, decl_index: Module.Decl.Index) void {
1161 const gpa = self.base.allocator;1240 const gpa = self.base.allocator;
1162 const unnamed_consts = self.unnamed_const_atoms.getPtr(decl_index) orelse return;1241 const unnamed_consts = self.unnamed_const_atoms.getPtr(decl_index) orelse return;
1163 for (unnamed_consts.items) |atom| {1242 for (unnamed_consts.items) |atom_index| {
1164 self.freeAtom(atom);1243 self.freeAtom(atom_index);
1165 self.locals_free_list.append(gpa, atom.sym_index) catch {};
1166 self.locals.items[atom.sym_index].section_number = .UNDEFINED;
1167 _ = self.atom_by_index_table.remove(atom.sym_index);
1168 log.debug(" adding local symbol index {d} to free list", .{atom.sym_index});
1169 atom.sym_index = 0;
1170 }1244 }
1171 unnamed_consts.clearAndFree(gpa);1245 unnamed_consts.clearAndFree(gpa);
1172}1246}
...@@ -1181,35 +1255,11 @@ pub fn freeDecl(self: *Coff, decl_index: Module.Decl.Index) void {...@@ -1181,35 +1255,11 @@ pub fn freeDecl(self: *Coff, decl_index: Module.Decl.Index) void {
11811255
1182 log.debug("freeDecl {*}", .{decl});1256 log.debug("freeDecl {*}", .{decl});
11831257
1184 const kv = self.decls.fetchRemove(decl_index);1258 if (self.decls.fetchRemove(decl_index)) |const_kv| {
1185 if (kv.?.value) |_| {1259 var kv = const_kv;
1186 self.freeAtom(&decl.link.coff);1260 self.freeAtom(kv.value.atom);
1187 self.freeUnnamedConsts(decl_index);1261 self.freeUnnamedConsts(decl_index);
1188 }1262 kv.value.exports.deinit(self.base.allocator);
1189
1190 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
1191 const gpa = self.base.allocator;
1192 const sym_index = decl.link.coff.sym_index;
1193 if (sym_index != 0) {
1194 self.locals_free_list.append(gpa, sym_index) catch {};
1195
1196 // Try freeing GOT atom if this decl had one
1197 const got_target = SymbolWithLoc{ .sym_index = sym_index, .file = null };
1198 if (self.got_entries_table.get(got_target)) |got_index| {
1199 self.got_entries_free_list.append(gpa, @intCast(u32, got_index)) catch {};
1200 self.got_entries.items[got_index] = .{
1201 .target = .{ .sym_index = 0, .file = null },
1202 .sym_index = 0,
1203 };
1204 _ = self.got_entries_table.remove(got_target);
1205
1206 log.debug(" adding GOT index {d} to free list (target local@{d})", .{ got_index, sym_index });
1207 }
1208
1209 self.locals.items[sym_index].section_number = .UNDEFINED;
1210 _ = self.atom_by_index_table.remove(sym_index);
1211 log.debug(" adding local symbol index {d} to free list", .{sym_index});
1212 decl.link.coff.sym_index = 0;
1213 }1263 }
1214}1264}
12151265
...@@ -1262,9 +1312,10 @@ pub fn updateDeclExports(...@@ -1262,9 +1312,10 @@ pub fn updateDeclExports(
1262 const gpa = self.base.allocator;1312 const gpa = self.base.allocator;
12631313
1264 const decl = module.declPtr(decl_index);1314 const decl = module.declPtr(decl_index);
1265 const atom = &decl.link.coff;1315 const atom_index = try self.getOrCreateAtomForDecl(decl_index);
1266 if (atom.sym_index == 0) return;1316 const atom = self.getAtom(atom_index);
1267 const decl_sym = atom.getSymbol(self);1317 const decl_sym = atom.getSymbol(self);
1318 const decl_metadata = self.decls.getPtr(decl_index).?;
12681319
1269 for (exports) |exp| {1320 for (exports) |exp| {
1270 log.debug("adding new export '{s}'", .{exp.options.name});1321 log.debug("adding new export '{s}'", .{exp.options.name});
...@@ -1299,9 +1350,9 @@ pub fn updateDeclExports(...@@ -1299,9 +1350,9 @@ pub fn updateDeclExports(
1299 continue;1350 continue;
1300 }1351 }
13011352
1302 const sym_index = exp.link.coff.sym_index orelse blk: {1353 const sym_index = decl_metadata.getExport(self, exp.options.name) orelse blk: {
1303 const sym_index = try self.allocateSymbol();1354 const sym_index = try self.allocateSymbol();
1304 exp.link.coff.sym_index = sym_index;1355 try decl_metadata.exports.append(gpa, sym_index);
1305 break :blk sym_index;1356 break :blk sym_index;
1306 };1357 };
1307 const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = null };1358 const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = null };
...@@ -1324,16 +1375,15 @@ pub fn updateDeclExports(...@@ -1324,16 +1375,15 @@ pub fn updateDeclExports(
1324 }1375 }
1325}1376}
13261377
1327pub fn deleteExport(self: *Coff, exp: Export) void {1378pub fn deleteDeclExport(self: *Coff, decl_index: Module.Decl.Index, name: []const u8) void {
1328 if (self.llvm_object) |_| return;1379 if (self.llvm_object) |_| return;
1329 const sym_index = exp.sym_index orelse return;1380 const metadata = self.decls.getPtr(decl_index) orelse return;
1381 const sym_index = metadata.getExportPtr(self, name) orelse return;
13301382
1331 const gpa = self.base.allocator;1383 const gpa = self.base.allocator;
13321384 const sym_loc = SymbolWithLoc{ .sym_index = sym_index.*, .file = null };
1333 const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = null };
1334 const sym = self.getSymbolPtr(sym_loc);1385 const sym = self.getSymbolPtr(sym_loc);
1335 const sym_name = self.getSymbolName(sym_loc);1386 log.debug("deleting export '{s}'", .{name});
1336 log.debug("deleting export '{s}'", .{sym_name});
1337 assert(sym.storage_class == .EXTERNAL and sym.section_number != .UNDEFINED);1387 assert(sym.storage_class == .EXTERNAL and sym.section_number != .UNDEFINED);
1338 sym.* = .{1388 sym.* = .{
1339 .name = [_]u8{0} ** 8,1389 .name = [_]u8{0} ** 8,
...@@ -1343,9 +1393,9 @@ pub fn deleteExport(self: *Coff, exp: Export) void {...@@ -1343,9 +1393,9 @@ pub fn deleteExport(self: *Coff, exp: Export) void {
1343 .storage_class = .NULL,1393 .storage_class = .NULL,
1344 .number_of_aux_symbols = 0,1394 .number_of_aux_symbols = 0,
1345 };1395 };
1346 self.locals_free_list.append(gpa, sym_index) catch {};1396 self.locals_free_list.append(gpa, sym_index.*) catch {};
13471397
1348 if (self.resolver.fetchRemove(sym_name)) |entry| {1398 if (self.resolver.fetchRemove(name)) |entry| {
1349 defer gpa.free(entry.key);1399 defer gpa.free(entry.key);
1350 self.globals_free_list.append(gpa, entry.value) catch {};1400 self.globals_free_list.append(gpa, entry.value) catch {};
1351 self.globals.items[entry.value] = .{1401 self.globals.items[entry.value] = .{
...@@ -1353,6 +1403,8 @@ pub fn deleteExport(self: *Coff, exp: Export) void {...@@ -1353,6 +1403,8 @@ pub fn deleteExport(self: *Coff, exp: Export) void {
1353 .file = null,1403 .file = null,
1354 };1404 };
1355 }1405 }
1406
1407 sym_index.* = 0;
1356}1408}
13571409
1358fn resolveGlobalSymbol(self: *Coff, current: SymbolWithLoc) !void {1410fn resolveGlobalSymbol(self: *Coff, current: SymbolWithLoc) !void {
...@@ -1417,9 +1469,10 @@ pub fn flushModule(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod...@@ -1417,9 +1469,10 @@ pub fn flushModule(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
1417 if (self.imports_table.contains(global)) continue;1469 if (self.imports_table.contains(global)) continue;
14181470
1419 const import_index = try self.allocateImportEntry(global);1471 const import_index = try self.allocateImportEntry(global);
1420 const import_atom = try self.createImportAtom();1472 const import_atom_index = try self.createImportAtom();
1421 self.imports.items[import_index].sym_index = import_atom.sym_index;1473 const import_atom = self.getAtom(import_atom_index);
1422 try self.writePtrWidthAtom(import_atom);1474 self.imports.items[import_index].sym_index = import_atom.getSymbolIndex().?;
1475 try self.writePtrWidthAtom(import_atom_index);
1423 }1476 }
14241477
1425 if (build_options.enable_logging) {1478 if (build_options.enable_logging) {
...@@ -1453,20 +1506,14 @@ pub fn flushModule(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod...@@ -1453,20 +1506,14 @@ pub fn flushModule(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
1453 }1506 }
1454}1507}
14551508
1456pub fn getDeclVAddr(1509pub fn getDeclVAddr(self: *Coff, decl_index: Module.Decl.Index, reloc_info: link.File.RelocInfo) !u64 {
1457 self: *Coff,
1458 decl_index: Module.Decl.Index,
1459 reloc_info: link.File.RelocInfo,
1460) !u64 {
1461 const mod = self.base.options.module.?;
1462 const decl = mod.declPtr(decl_index);
1463
1464 assert(self.llvm_object == null);1510 assert(self.llvm_object == null);
1465 assert(decl.link.coff.sym_index != 0);
14661511
1467 const atom = self.getAtomForSymbol(.{ .sym_index = reloc_info.parent_atom_index, .file = null }).?;1512 const this_atom_index = try self.getOrCreateAtomForDecl(decl_index);
1468 const target = SymbolWithLoc{ .sym_index = decl.link.coff.sym_index, .file = null };1513 const sym_index = self.getAtom(this_atom_index).getSymbolIndex().?;
1469 try atom.addRelocation(self, .{1514 const atom_index = self.getAtomIndexForSymbol(.{ .sym_index = reloc_info.parent_atom_index, .file = null }).?;
1515 const target = SymbolWithLoc{ .sym_index = sym_index, .file = null };
1516 try Atom.addRelocation(self, atom_index, .{
1470 .type = .direct,1517 .type = .direct,
1471 .target = target,1518 .target = target,
1472 .offset = @intCast(u32, reloc_info.offset),1519 .offset = @intCast(u32, reloc_info.offset),
...@@ -1474,7 +1521,7 @@ pub fn getDeclVAddr(...@@ -1474,7 +1521,7 @@ pub fn getDeclVAddr(
1474 .pcrel = false,1521 .pcrel = false,
1475 .length = 3,1522 .length = 3,
1476 });1523 });
1477 try atom.addBaseRelocation(self, @intCast(u32, reloc_info.offset));1524 try Atom.addBaseRelocation(self, atom_index, @intCast(u32, reloc_info.offset));
14781525
1479 return 0;1526 return 0;
1480}1527}
...@@ -1501,10 +1548,10 @@ pub fn getGlobalSymbol(self: *Coff, name: []const u8) !u32 {...@@ -1501,10 +1548,10 @@ pub fn getGlobalSymbol(self: *Coff, name: []const u8) !u32 {
1501 return global_index;1548 return global_index;
1502}1549}
15031550
1504pub fn updateDeclLineNumber(self: *Coff, module: *Module, decl: *Module.Decl) !void {1551pub fn updateDeclLineNumber(self: *Coff, module: *Module, decl_index: Module.Decl.Index) !void {
1505 _ = self;1552 _ = self;
1506 _ = module;1553 _ = module;
1507 _ = decl;1554 _ = decl_index;
1508 log.debug("TODO implement updateDeclLineNumber", .{});1555 log.debug("TODO implement updateDeclLineNumber", .{});
1509}1556}
15101557
...@@ -1525,7 +1572,8 @@ fn writeBaseRelocations(self: *Coff) !void {...@@ -1525,7 +1572,8 @@ fn writeBaseRelocations(self: *Coff) !void {
15251572
1526 var it = self.base_relocs.iterator();1573 var it = self.base_relocs.iterator();
1527 while (it.next()) |entry| {1574 while (it.next()) |entry| {
1528 const atom = entry.key_ptr.*;1575 const atom_index = entry.key_ptr.*;
1576 const atom = self.getAtom(atom_index);
1529 const offsets = entry.value_ptr.*;1577 const offsets = entry.value_ptr.*;
15301578
1531 for (offsets.items) |offset| {1579 for (offsets.items) |offset| {
...@@ -1609,7 +1657,8 @@ fn writeImportTable(self: *Coff) !void {...@@ -1609,7 +1657,8 @@ fn writeImportTable(self: *Coff) !void {
1609 const gpa = self.base.allocator;1657 const gpa = self.base.allocator;
16101658
1611 const section = self.sections.get(self.idata_section_index.?);1659 const section = self.sections.get(self.idata_section_index.?);
1612 const last_atom = section.last_atom orelse return;1660 const last_atom_index = section.last_atom_index orelse return;
1661 const last_atom = self.getAtom(last_atom_index);
16131662
1614 const iat_rva = section.header.virtual_address;1663 const iat_rva = section.header.virtual_address;
1615 const iat_size = last_atom.getSymbol(self).value + last_atom.size * 2 - iat_rva; // account for sentinel zero pointer1664 const iat_size = last_atom.getSymbol(self).value + last_atom.size * 2 - iat_rva; // account for sentinel zero pointer
...@@ -2047,27 +2096,37 @@ pub fn getOrPutGlobalPtr(self: *Coff, name: []const u8) !GetOrPutGlobalPtrResult...@@ -2047,27 +2096,37 @@ pub fn getOrPutGlobalPtr(self: *Coff, name: []const u8) !GetOrPutGlobalPtrResult
2047 return GetOrPutGlobalPtrResult{ .found_existing = false, .value_ptr = ptr };2096 return GetOrPutGlobalPtrResult{ .found_existing = false, .value_ptr = ptr };
2048}2097}
20492098
2099pub fn getAtom(self: *const Coff, atom_index: Atom.Index) Atom {
2100 assert(atom_index < self.atoms.items.len);
2101 return self.atoms.items[atom_index];
2102}
2103
2104pub fn getAtomPtr(self: *Coff, atom_index: Atom.Index) *Atom {
2105 assert(atom_index < self.atoms.items.len);
2106 return &self.atoms.items[atom_index];
2107}
2108
2050/// Returns atom if there is an atom referenced by the symbol described by `sym_loc` descriptor.2109/// Returns atom if there is an atom referenced by the symbol described by `sym_loc` descriptor.
2051/// Returns null on failure.2110/// Returns null on failure.
2052pub fn getAtomForSymbol(self: *Coff, sym_loc: SymbolWithLoc) ?*Atom {2111pub fn getAtomIndexForSymbol(self: *const Coff, sym_loc: SymbolWithLoc) ?Atom.Index {
2053 assert(sym_loc.file == null); // TODO linking with object files2112 assert(sym_loc.file == null); // TODO linking with object files
2054 return self.atom_by_index_table.get(sym_loc.sym_index);2113 return self.atom_by_index_table.get(sym_loc.sym_index);
2055}2114}
20562115
2057/// Returns GOT atom that references `sym_loc` if one exists.2116/// Returns GOT atom that references `sym_loc` if one exists.
2058/// Returns null otherwise.2117/// Returns null otherwise.
2059pub fn getGotAtomForSymbol(self: *Coff, sym_loc: SymbolWithLoc) ?*Atom {2118pub fn getGotAtomIndexForSymbol(self: *const Coff, sym_loc: SymbolWithLoc) ?Atom.Index {
2060 const got_index = self.got_entries_table.get(sym_loc) orelse return null;2119 const got_index = self.got_entries_table.get(sym_loc) orelse return null;
2061 const got_entry = self.got_entries.items[got_index];2120 const got_entry = self.got_entries.items[got_index];
2062 return self.getAtomForSymbol(.{ .sym_index = got_entry.sym_index, .file = null });2121 return self.getAtomIndexForSymbol(.{ .sym_index = got_entry.sym_index, .file = null });
2063}2122}
20642123
2065/// Returns import atom that references `sym_loc` if one exists.2124/// Returns import atom that references `sym_loc` if one exists.
2066/// Returns null otherwise.2125/// Returns null otherwise.
2067pub fn getImportAtomForSymbol(self: *Coff, sym_loc: SymbolWithLoc) ?*Atom {2126pub fn getImportAtomIndexForSymbol(self: *const Coff, sym_loc: SymbolWithLoc) ?Atom.Index {
2068 const imports_index = self.imports_table.get(sym_loc) orelse return null;2127 const imports_index = self.imports_table.get(sym_loc) orelse return null;
2069 const imports_entry = self.imports.items[imports_index];2128 const imports_entry = self.imports.items[imports_index];
2070 return self.getAtomForSymbol(.{ .sym_index = imports_entry.sym_index, .file = null });2129 return self.getAtomIndexForSymbol(.{ .sym_index = imports_entry.sym_index, .file = null });
2071}2130}
20722131
2073fn setSectionName(self: *Coff, header: *coff.SectionHeader, name: []const u8) !void {2132fn setSectionName(self: *Coff, header: *coff.SectionHeader, name: []const u8) !void {
src/link/Coff/Atom.zig+37-22
...@@ -27,42 +27,44 @@ alignment: u32,...@@ -27,42 +27,44 @@ alignment: u32,
2727
28/// Points to the previous and next neighbors, based on the `text_offset`.28/// Points to the previous and next neighbors, based on the `text_offset`.
29/// This can be used to find, for example, the capacity of this `Atom`.29/// This can be used to find, for example, the capacity of this `Atom`.
30prev: ?*Atom,30prev_index: ?Index,
31next: ?*Atom,31next_index: ?Index,
3232
33pub const empty = Atom{33pub const Index = u32;
34 .sym_index = 0,34
35 .file = null,35pub fn getSymbolIndex(self: Atom) ?u32 {
36 .size = 0,36 if (self.sym_index == 0) return null;
37 .alignment = 0,37 return self.sym_index;
38 .prev = null,38}
39 .next = null,
40};
4139
42/// Returns symbol referencing this atom.40/// Returns symbol referencing this atom.
43pub fn getSymbol(self: Atom, coff_file: *const Coff) *const coff.Symbol {41pub fn getSymbol(self: Atom, coff_file: *const Coff) *const coff.Symbol {
42 const sym_index = self.getSymbolIndex().?;
44 return coff_file.getSymbol(.{43 return coff_file.getSymbol(.{
45 .sym_index = self.sym_index,44 .sym_index = sym_index,
46 .file = self.file,45 .file = self.file,
47 });46 });
48}47}
4948
50/// Returns pointer-to-symbol referencing this atom.49/// Returns pointer-to-symbol referencing this atom.
51pub fn getSymbolPtr(self: Atom, coff_file: *Coff) *coff.Symbol {50pub fn getSymbolPtr(self: Atom, coff_file: *Coff) *coff.Symbol {
51 const sym_index = self.getSymbolIndex().?;
52 return coff_file.getSymbolPtr(.{52 return coff_file.getSymbolPtr(.{
53 .sym_index = self.sym_index,53 .sym_index = sym_index,
54 .file = self.file,54 .file = self.file,
55 });55 });
56}56}
5757
58pub fn getSymbolWithLoc(self: Atom) SymbolWithLoc {58pub fn getSymbolWithLoc(self: Atom) SymbolWithLoc {
59 return .{ .sym_index = self.sym_index, .file = self.file };59 const sym_index = self.getSymbolIndex().?;
60 return .{ .sym_index = sym_index, .file = self.file };
60}61}
6162
62/// Returns the name of this atom.63/// Returns the name of this atom.
63pub fn getName(self: Atom, coff_file: *const Coff) []const u8 {64pub fn getName(self: Atom, coff_file: *const Coff) []const u8 {
65 const sym_index = self.getSymbolIndex().?;
64 return coff_file.getSymbolName(.{66 return coff_file.getSymbolName(.{
65 .sym_index = self.sym_index,67 .sym_index = sym_index,
66 .file = self.file,68 .file = self.file,
67 });69 });
68}70}
...@@ -70,7 +72,8 @@ pub fn getName(self: Atom, coff_file: *const Coff) []const u8 {...@@ -70,7 +72,8 @@ pub fn getName(self: Atom, coff_file: *const Coff) []const u8 {
70/// Returns how much room there is to grow in virtual address space.72/// Returns how much room there is to grow in virtual address space.
71pub fn capacity(self: Atom, coff_file: *const Coff) u32 {73pub fn capacity(self: Atom, coff_file: *const Coff) u32 {
72 const self_sym = self.getSymbol(coff_file);74 const self_sym = self.getSymbol(coff_file);
73 if (self.next) |next| {75 if (self.next_index) |next_index| {
76 const next = coff_file.getAtom(next_index);
74 const next_sym = next.getSymbol(coff_file);77 const next_sym = next.getSymbol(coff_file);
75 return next_sym.value - self_sym.value;78 return next_sym.value - self_sym.value;
76 } else {79 } else {
...@@ -82,7 +85,8 @@ pub fn capacity(self: Atom, coff_file: *const Coff) u32 {...@@ -82,7 +85,8 @@ pub fn capacity(self: Atom, coff_file: *const Coff) u32 {
8285
83pub fn freeListEligible(self: Atom, coff_file: *const Coff) bool {86pub fn freeListEligible(self: Atom, coff_file: *const Coff) bool {
84 // No need to keep a free list node for the last atom.87 // No need to keep a free list node for the last atom.
85 const next = self.next orelse return false;88 const next_index = self.next_index orelse return false;
89 const next = coff_file.getAtom(next_index);
86 const self_sym = self.getSymbol(coff_file);90 const self_sym = self.getSymbol(coff_file);
87 const next_sym = next.getSymbol(coff_file);91 const next_sym = next.getSymbol(coff_file);
88 const cap = next_sym.value - self_sym.value;92 const cap = next_sym.value - self_sym.value;
...@@ -92,22 +96,33 @@ pub fn freeListEligible(self: Atom, coff_file: *const Coff) bool {...@@ -92,22 +96,33 @@ pub fn freeListEligible(self: Atom, coff_file: *const Coff) bool {
92 return surplus >= Coff.min_text_capacity;96 return surplus >= Coff.min_text_capacity;
93}97}
9498
95pub fn addRelocation(self: *Atom, coff_file: *Coff, reloc: Relocation) !void {99pub fn addRelocation(coff_file: *Coff, atom_index: Index, reloc: Relocation) !void {
96 const gpa = coff_file.base.allocator;100 const gpa = coff_file.base.allocator;
97 log.debug(" (adding reloc of type {s} to target %{d})", .{ @tagName(reloc.type), reloc.target.sym_index });101 log.debug(" (adding reloc of type {s} to target %{d})", .{ @tagName(reloc.type), reloc.target.sym_index });
98 const gop = try coff_file.relocs.getOrPut(gpa, self);102 const gop = try coff_file.relocs.getOrPut(gpa, atom_index);
99 if (!gop.found_existing) {103 if (!gop.found_existing) {
100 gop.value_ptr.* = .{};104 gop.value_ptr.* = .{};
101 }105 }
102 try gop.value_ptr.append(gpa, reloc);106 try gop.value_ptr.append(gpa, reloc);
103}107}
104108
105pub fn addBaseRelocation(self: *Atom, coff_file: *Coff, offset: u32) !void {109pub fn addBaseRelocation(coff_file: *Coff, atom_index: Index, offset: u32) !void {
106 const gpa = coff_file.base.allocator;110 const gpa = coff_file.base.allocator;
107 log.debug(" (adding base relocation at offset 0x{x} in %{d})", .{ offset, self.sym_index });111 log.debug(" (adding base relocation at offset 0x{x} in %{d})", .{
108 const gop = try coff_file.base_relocs.getOrPut(gpa, self);112 offset,
113 coff_file.getAtom(atom_index).getSymbolIndex().?,
114 });
115 const gop = try coff_file.base_relocs.getOrPut(gpa, atom_index);
109 if (!gop.found_existing) {116 if (!gop.found_existing) {
110 gop.value_ptr.* = .{};117 gop.value_ptr.* = .{};
111 }118 }
112 try gop.value_ptr.append(gpa, offset);119 try gop.value_ptr.append(gpa, offset);
113}120}
121
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,...@@ -46,33 +46,35 @@ length: u2,
46dirty: bool = true,46dirty: bool = true,
4747
48/// Returns an Atom which is the target node of this relocation edge (if any).48/// Returns an Atom which is the target node of this relocation edge (if any).
49pub fn getTargetAtom(self: Relocation, coff_file: *Coff) ?*Atom {49pub fn getTargetAtomIndex(self: Relocation, coff_file: *const Coff) ?Atom.Index {
50 switch (self.type) {50 switch (self.type) {
51 .got,51 .got,
52 .got_page,52 .got_page,
53 .got_pageoff,53 .got_pageoff,
54 => return coff_file.getGotAtomForSymbol(self.target),54 => return coff_file.getGotAtomIndexForSymbol(self.target),
5555
56 .direct,56 .direct,
57 .page,57 .page,
58 .pageoff,58 .pageoff,
59 => return coff_file.getAtomForSymbol(self.target),59 => return coff_file.getAtomIndexForSymbol(self.target),
6060
61 .import,61 .import,
62 .import_page,62 .import_page,
63 .import_pageoff,63 .import_pageoff,
64 => return coff_file.getImportAtomForSymbol(self.target),64 => return coff_file.getImportAtomIndexForSymbol(self.target),
65 }65 }
66}66}
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);
69 const source_sym = atom.getSymbol(coff_file);70 const source_sym = atom.getSymbol(coff_file);
70 const source_section = coff_file.sections.get(@enumToInt(source_sym.section_number) - 1).header;71 const source_section = coff_file.sections.get(@enumToInt(source_sym.section_number) - 1).header;
71 const source_vaddr = source_sym.value + self.offset;72 const source_vaddr = source_sym.value + self.offset;
7273
73 const file_offset = source_section.pointer_to_raw_data + source_sym.value - source_section.virtual_address;74 const file_offset = source_section.pointer_to_raw_data + source_sym.value - source_section.virtual_address;
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);
76 const target_vaddr = target_atom.getSymbol(coff_file).value;78 const target_vaddr = target_atom.getSymbol(coff_file).value;
77 const target_vaddr_with_addend = target_vaddr + self.addend;79 const target_vaddr_with_addend = target_vaddr + self.addend;
7880
...@@ -107,7 +109,7 @@ const Context = struct {...@@ -107,7 +109,7 @@ const Context = struct {
107 image_base: u64,109 image_base: u64,
108};110};
109111
110fn resolveAarch64(self: *Relocation, ctx: Context, coff_file: *Coff) !void {112fn resolveAarch64(self: Relocation, ctx: Context, coff_file: *Coff) !void {
111 var buffer: [@sizeOf(u64)]u8 = undefined;113 var buffer: [@sizeOf(u64)]u8 = undefined;
112 switch (self.length) {114 switch (self.length) {
113 2 => {115 2 => {
...@@ -197,7 +199,7 @@ fn resolveAarch64(self: *Relocation, ctx: Context, coff_file: *Coff) !void {...@@ -197,7 +199,7 @@ fn resolveAarch64(self: *Relocation, ctx: Context, coff_file: *Coff) !void {
197 }199 }
198}200}
199201
200fn resolveX86(self: *Relocation, ctx: Context, coff_file: *Coff) !void {202fn resolveX86(self: Relocation, ctx: Context, coff_file: *Coff) !void {
201 switch (self.type) {203 switch (self.type) {
202 .got_page => unreachable,204 .got_page => unreachable,
203 .got_pageoff => unreachable,205 .got_pageoff => unreachable,
src/link/Dwarf.zig+320-259
...@@ -18,31 +18,36 @@ const LinkBlock = File.LinkBlock;...@@ -18,31 +18,36 @@ const LinkBlock = File.LinkBlock;
18const LinkFn = File.LinkFn;18const LinkFn = File.LinkFn;
19const LinkerLoad = @import("../codegen.zig").LinkerLoad;19const LinkerLoad = @import("../codegen.zig").LinkerLoad;
20const Module = @import("../Module.zig");20const Module = @import("../Module.zig");
21const Value = @import("../value.zig").Value;21const StringTable = @import("strtab.zig").StringTable;
22const Type = @import("../type.zig").Type;22const Type = @import("../type.zig").Type;
23const Value = @import("../value.zig").Value;
2324
24allocator: Allocator,25allocator: Allocator,
25bin_file: *File,26bin_file: *File,
26ptr_width: PtrWidth,27ptr_width: PtrWidth,
27target: std.Target,28target: std.Target,
2829
29/// A list of `File.LinkFn` whose Line Number Programs have surplus capacity.30/// A list of `Atom`s whose Line Number Programs have surplus capacity.
30/// This is the same concept as `text_block_free_list`; see those doc comments.31/// This is the same concept as `Section.free_list` in Elf; see those doc comments.
31dbg_line_fn_free_list: std.AutoHashMapUnmanaged(*SrcFn, void) = .{},32src_fn_free_list: std.AutoHashMapUnmanaged(Atom.Index, void) = .{},
32dbg_line_fn_first: ?*SrcFn = null,33src_fn_first_index: ?Atom.Index = null,
33dbg_line_fn_last: ?*SrcFn = null,34src_fn_last_index: ?Atom.Index = null,
35src_fns: std.ArrayListUnmanaged(Atom) = .{},
36src_fn_decls: AtomTable = .{},
3437
35/// A list of `Atom`s whose corresponding .debug_info tags have surplus capacity.38/// A list of `Atom`s whose corresponding .debug_info tags have surplus capacity.
36/// This is the same concept as `text_block_free_list`; see those doc comments.39/// This is the same concept as `text_block_free_list`; see those doc comments.
37atom_free_list: std.AutoHashMapUnmanaged(*Atom, void) = .{},40di_atom_free_list: std.AutoHashMapUnmanaged(Atom.Index, void) = .{},
38atom_first: ?*Atom = null,41di_atom_first_index: ?Atom.Index = null,
39atom_last: ?*Atom = null,42di_atom_last_index: ?Atom.Index = null,
43di_atoms: std.ArrayListUnmanaged(Atom) = .{},
44di_atom_decls: AtomTable = .{},
4045
41abbrev_table_offset: ?u64 = null,46abbrev_table_offset: ?u64 = null,
4247
43/// TODO replace with InternPool48/// TODO replace with InternPool
44/// Table of debug symbol names.49/// Table of debug symbol names.
45strtab: std.ArrayListUnmanaged(u8) = .{},50strtab: StringTable(.strtab) = .{},
4651
47/// Quick lookup array of all defined source files referenced by at least one Decl.52/// Quick lookup array of all defined source files referenced by at least one Decl.
48/// They will end up in the DWARF debug_line header as two lists:53/// They will end up in the DWARF debug_line header as two lists:
...@@ -50,22 +55,23 @@ strtab: std.ArrayListUnmanaged(u8) = .{},...@@ -50,22 +55,23 @@ strtab: std.ArrayListUnmanaged(u8) = .{},
50/// * []file_names55/// * []file_names
51di_files: std.AutoArrayHashMapUnmanaged(*const Module.File, void) = .{},56di_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
58global_abbrev_relocs: std.ArrayListUnmanaged(AbbrevRelocation) = .{},58global_abbrev_relocs: std.ArrayListUnmanaged(AbbrevRelocation) = .{},
5959
60pub const Atom = struct {60const AtomTable = std.AutoHashMapUnmanaged(Module.Decl.Index, Atom.Index);
61 /// Previous/next linked list pointers.61
62 /// This is the linked list node for this Decl's corresponding .debug_info tag.62const Atom = struct {
63 prev: ?*Atom,63 /// Offset into .debug_info pointing to the tag for this Decl, or
64 next: ?*Atom,64 /// offset from the beginning of the Debug Line Program header that contains this function.
65 /// Offset into .debug_info pointing to the tag for this Decl.
66 off: u32,65 off: u32,
67 /// Size of the .debug_info tag for this Decl, not including padding.66 /// Size of the .debug_info tag for this Decl, not including padding, or
67 /// size of the line number program component belonging to this function, not
68 /// including padding.
68 len: u32,69 len: u32,
70
71 prev_index: ?Index,
72 next_index: ?Index,
73
74 pub const Index = u32;
69};75};
7076
71/// Represents state of the analysed Decl.77/// Represents state of the analysed Decl.
...@@ -75,6 +81,7 @@ pub const Atom = struct {...@@ -75,6 +81,7 @@ pub const Atom = struct {
75pub const DeclState = struct {81pub const DeclState = struct {
76 gpa: Allocator,82 gpa: Allocator,
77 mod: *Module,83 mod: *Module,
84 di_atom_decls: *const AtomTable,
78 dbg_line: std.ArrayList(u8),85 dbg_line: std.ArrayList(u8),
79 dbg_info: std.ArrayList(u8),86 dbg_info: std.ArrayList(u8),
80 abbrev_type_arena: std.heap.ArenaAllocator,87 abbrev_type_arena: std.heap.ArenaAllocator,
...@@ -88,10 +95,11 @@ pub const DeclState = struct {...@@ -88,10 +95,11 @@ pub const DeclState = struct {
88 abbrev_relocs: std.ArrayListUnmanaged(AbbrevRelocation) = .{},95 abbrev_relocs: std.ArrayListUnmanaged(AbbrevRelocation) = .{},
89 exprloc_relocs: std.ArrayListUnmanaged(ExprlocRelocation) = .{},96 exprloc_relocs: std.ArrayListUnmanaged(ExprlocRelocation) = .{},
9097
91 fn init(gpa: Allocator, mod: *Module) DeclState {98 fn init(gpa: Allocator, mod: *Module, di_atom_decls: *const AtomTable) DeclState {
92 return .{99 return .{
93 .gpa = gpa,100 .gpa = gpa,
94 .mod = mod,101 .mod = mod,
102 .di_atom_decls = di_atom_decls,
95 .dbg_line = std.ArrayList(u8).init(gpa),103 .dbg_line = std.ArrayList(u8).init(gpa),
96 .dbg_info = std.ArrayList(u8).init(gpa),104 .dbg_info = std.ArrayList(u8).init(gpa),
97 .abbrev_type_arena = std.heap.ArenaAllocator.init(gpa),105 .abbrev_type_arena = std.heap.ArenaAllocator.init(gpa),
...@@ -119,11 +127,11 @@ pub const DeclState = struct {...@@ -119,11 +127,11 @@ pub const DeclState = struct {
119127
120 /// Adds local type relocation of the form: @offset => @this + addend128 /// Adds local type relocation of the form: @offset => @this + addend
121 /// @this signifies the offset within the .debug_abbrev section of the containing atom.129 /// @this signifies the offset within the .debug_abbrev section of the containing atom.
122 fn addTypeRelocLocal(self: *DeclState, atom: *const Atom, offset: u32, addend: u32) !void {130 fn addTypeRelocLocal(self: *DeclState, atom_index: Atom.Index, offset: u32, addend: u32) !void {
123 log.debug("{x}: @this + {x}", .{ offset, addend });131 log.debug("{x}: @this + {x}", .{ offset, addend });
124 try self.abbrev_relocs.append(self.gpa, .{132 try self.abbrev_relocs.append(self.gpa, .{
125 .target = null,133 .target = null,
126 .atom = atom,134 .atom_index = atom_index,
127 .offset = offset,135 .offset = offset,
128 .addend = addend,136 .addend = addend,
129 });137 });
...@@ -132,13 +140,13 @@ pub const DeclState = struct {...@@ -132,13 +140,13 @@ pub const DeclState = struct {
132 /// Adds global type relocation of the form: @offset => @symbol + 0140 /// Adds global type relocation of the form: @offset => @symbol + 0
133 /// @symbol signifies a type abbreviation posititioned somewhere in the .debug_abbrev section141 /// @symbol signifies a type abbreviation posititioned somewhere in the .debug_abbrev section
134 /// which we use as our target of the relocation.142 /// which we use as our target of the relocation.
135 fn addTypeRelocGlobal(self: *DeclState, atom: *const Atom, ty: Type, offset: u32) !void {143 fn addTypeRelocGlobal(self: *DeclState, atom_index: Atom.Index, ty: Type, offset: u32) !void {
136 const resolv = self.abbrev_resolver.getContext(ty, .{144 const resolv = self.abbrev_resolver.getContext(ty, .{
137 .mod = self.mod,145 .mod = self.mod,
138 }) orelse blk: {146 }) orelse blk: {
139 const sym_index = @intCast(u32, self.abbrev_table.items.len);147 const sym_index = @intCast(u32, self.abbrev_table.items.len);
140 try self.abbrev_table.append(self.gpa, .{148 try self.abbrev_table.append(self.gpa, .{
141 .atom = atom,149 .atom_index = atom_index,
142 .type = ty,150 .type = ty,
143 .offset = undefined,151 .offset = undefined,
144 });152 });
...@@ -153,7 +161,7 @@ pub const DeclState = struct {...@@ -153,7 +161,7 @@ pub const DeclState = struct {
153 log.debug("{x}: %{d} + 0", .{ offset, resolv });161 log.debug("{x}: %{d} + 0", .{ offset, resolv });
154 try self.abbrev_relocs.append(self.gpa, .{162 try self.abbrev_relocs.append(self.gpa, .{
155 .target = resolv,163 .target = resolv,
156 .atom = atom,164 .atom_index = atom_index,
157 .offset = offset,165 .offset = offset,
158 .addend = 0,166 .addend = 0,
159 });167 });
...@@ -162,7 +170,7 @@ pub const DeclState = struct {...@@ -162,7 +170,7 @@ pub const DeclState = struct {
162 fn addDbgInfoType(170 fn addDbgInfoType(
163 self: *DeclState,171 self: *DeclState,
164 module: *Module,172 module: *Module,
165 atom: *Atom,173 atom_index: Atom.Index,
166 ty: Type,174 ty: Type,
167 ) error{OutOfMemory}!void {175 ) error{OutOfMemory}!void {
168 const arena = self.abbrev_type_arena.allocator();176 const arena = self.abbrev_type_arena.allocator();
...@@ -227,7 +235,7 @@ pub const DeclState = struct {...@@ -227,7 +235,7 @@ pub const DeclState = struct {
227 // DW.AT.type, DW.FORM.ref4235 // DW.AT.type, DW.FORM.ref4
228 var index = dbg_info_buffer.items.len;236 var index = dbg_info_buffer.items.len;
229 try dbg_info_buffer.resize(index + 4);237 try dbg_info_buffer.resize(index + 4);
230 try self.addTypeRelocGlobal(atom, Type.bool, @intCast(u32, index));238 try self.addTypeRelocGlobal(atom_index, Type.bool, @intCast(u32, index));
231 // DW.AT.data_member_location, DW.FORM.sdata239 // DW.AT.data_member_location, DW.FORM.sdata
232 try dbg_info_buffer.ensureUnusedCapacity(6);240 try dbg_info_buffer.ensureUnusedCapacity(6);
233 dbg_info_buffer.appendAssumeCapacity(0);241 dbg_info_buffer.appendAssumeCapacity(0);
...@@ -239,7 +247,7 @@ pub const DeclState = struct {...@@ -239,7 +247,7 @@ pub const DeclState = struct {
239 // DW.AT.type, DW.FORM.ref4247 // DW.AT.type, DW.FORM.ref4
240 index = dbg_info_buffer.items.len;248 index = dbg_info_buffer.items.len;
241 try dbg_info_buffer.resize(index + 4);249 try dbg_info_buffer.resize(index + 4);
242 try self.addTypeRelocGlobal(atom, payload_ty, @intCast(u32, index));250 try self.addTypeRelocGlobal(atom_index, payload_ty, @intCast(u32, index));
243 // DW.AT.data_member_location, DW.FORM.sdata251 // DW.AT.data_member_location, DW.FORM.sdata
244 const offset = abi_size - payload_ty.abiSize(target);252 const offset = abi_size - payload_ty.abiSize(target);
245 try leb128.writeULEB128(dbg_info_buffer.writer(), offset);253 try leb128.writeULEB128(dbg_info_buffer.writer(), offset);
...@@ -270,7 +278,7 @@ pub const DeclState = struct {...@@ -270,7 +278,7 @@ pub const DeclState = struct {
270 try dbg_info_buffer.resize(index + 4);278 try dbg_info_buffer.resize(index + 4);
271 var buf = try arena.create(Type.SlicePtrFieldTypeBuffer);279 var buf = try arena.create(Type.SlicePtrFieldTypeBuffer);
272 const ptr_ty = ty.slicePtrFieldType(buf);280 const ptr_ty = ty.slicePtrFieldType(buf);
273 try self.addTypeRelocGlobal(atom, ptr_ty, @intCast(u32, index));281 try self.addTypeRelocGlobal(atom_index, ptr_ty, @intCast(u32, index));
274 // DW.AT.data_member_location, DW.FORM.sdata282 // DW.AT.data_member_location, DW.FORM.sdata
275 try dbg_info_buffer.ensureUnusedCapacity(6);283 try dbg_info_buffer.ensureUnusedCapacity(6);
276 dbg_info_buffer.appendAssumeCapacity(0);284 dbg_info_buffer.appendAssumeCapacity(0);
...@@ -282,7 +290,7 @@ pub const DeclState = struct {...@@ -282,7 +290,7 @@ pub const DeclState = struct {
282 // DW.AT.type, DW.FORM.ref4290 // DW.AT.type, DW.FORM.ref4
283 index = dbg_info_buffer.items.len;291 index = dbg_info_buffer.items.len;
284 try dbg_info_buffer.resize(index + 4);292 try dbg_info_buffer.resize(index + 4);
285 try self.addTypeRelocGlobal(atom, Type.usize, @intCast(u32, index));293 try self.addTypeRelocGlobal(atom_index, Type.usize, @intCast(u32, index));
286 // DW.AT.data_member_location, DW.FORM.sdata294 // DW.AT.data_member_location, DW.FORM.sdata
287 try dbg_info_buffer.ensureUnusedCapacity(2);295 try dbg_info_buffer.ensureUnusedCapacity(2);
288 dbg_info_buffer.appendAssumeCapacity(ptr_bytes);296 dbg_info_buffer.appendAssumeCapacity(ptr_bytes);
...@@ -294,7 +302,7 @@ pub const DeclState = struct {...@@ -294,7 +302,7 @@ pub const DeclState = struct {
294 // DW.AT.type, DW.FORM.ref4302 // DW.AT.type, DW.FORM.ref4
295 const index = dbg_info_buffer.items.len;303 const index = dbg_info_buffer.items.len;
296 try dbg_info_buffer.resize(index + 4);304 try dbg_info_buffer.resize(index + 4);
297 try self.addTypeRelocGlobal(atom, ty.childType(), @intCast(u32, index));305 try self.addTypeRelocGlobal(atom_index, ty.childType(), @intCast(u32, index));
298 }306 }
299 },307 },
300 .Array => {308 .Array => {
...@@ -305,13 +313,13 @@ pub const DeclState = struct {...@@ -305,13 +313,13 @@ pub const DeclState = struct {
305 // DW.AT.type, DW.FORM.ref4313 // DW.AT.type, DW.FORM.ref4
306 var index = dbg_info_buffer.items.len;314 var index = dbg_info_buffer.items.len;
307 try dbg_info_buffer.resize(index + 4);315 try dbg_info_buffer.resize(index + 4);
308 try self.addTypeRelocGlobal(atom, ty.childType(), @intCast(u32, index));316 try self.addTypeRelocGlobal(atom_index, ty.childType(), @intCast(u32, index));
309 // DW.AT.subrange_type317 // DW.AT.subrange_type
310 try dbg_info_buffer.append(@enumToInt(AbbrevKind.array_dim));318 try dbg_info_buffer.append(@enumToInt(AbbrevKind.array_dim));
311 // DW.AT.type, DW.FORM.ref4319 // DW.AT.type, DW.FORM.ref4
312 index = dbg_info_buffer.items.len;320 index = dbg_info_buffer.items.len;
313 try dbg_info_buffer.resize(index + 4);321 try dbg_info_buffer.resize(index + 4);
314 try self.addTypeRelocGlobal(atom, Type.usize, @intCast(u32, index));322 try self.addTypeRelocGlobal(atom_index, Type.usize, @intCast(u32, index));
315 // DW.AT.count, DW.FORM.udata323 // DW.AT.count, DW.FORM.udata
316 const len = ty.arrayLenIncludingSentinel();324 const len = ty.arrayLenIncludingSentinel();
317 try leb128.writeULEB128(dbg_info_buffer.writer(), len);325 try leb128.writeULEB128(dbg_info_buffer.writer(), len);
...@@ -339,7 +347,7 @@ pub const DeclState = struct {...@@ -339,7 +347,7 @@ pub const DeclState = struct {
339 // DW.AT.type, DW.FORM.ref4347 // DW.AT.type, DW.FORM.ref4
340 var index = dbg_info_buffer.items.len;348 var index = dbg_info_buffer.items.len;
341 try dbg_info_buffer.resize(index + 4);349 try dbg_info_buffer.resize(index + 4);
342 try self.addTypeRelocGlobal(atom, field, @intCast(u32, index));350 try self.addTypeRelocGlobal(atom_index, field, @intCast(u32, index));
343 // DW.AT.data_member_location, DW.FORM.sdata351 // DW.AT.data_member_location, DW.FORM.sdata
344 const field_off = ty.structFieldOffset(field_index, target);352 const field_off = ty.structFieldOffset(field_index, target);
345 try leb128.writeULEB128(dbg_info_buffer.writer(), field_off);353 try leb128.writeULEB128(dbg_info_buffer.writer(), field_off);
...@@ -371,7 +379,7 @@ pub const DeclState = struct {...@@ -371,7 +379,7 @@ pub const DeclState = struct {
371 // DW.AT.type, DW.FORM.ref4379 // DW.AT.type, DW.FORM.ref4
372 var index = dbg_info_buffer.items.len;380 var index = dbg_info_buffer.items.len;
373 try dbg_info_buffer.resize(index + 4);381 try dbg_info_buffer.resize(index + 4);
374 try self.addTypeRelocGlobal(atom, field.ty, @intCast(u32, index));382 try self.addTypeRelocGlobal(atom_index, field.ty, @intCast(u32, index));
375 // DW.AT.data_member_location, DW.FORM.sdata383 // DW.AT.data_member_location, DW.FORM.sdata
376 const field_off = ty.structFieldOffset(field_index, target);384 const field_off = ty.structFieldOffset(field_index, target);
377 try leb128.writeULEB128(dbg_info_buffer.writer(), field_off);385 try leb128.writeULEB128(dbg_info_buffer.writer(), field_off);
...@@ -454,7 +462,7 @@ pub const DeclState = struct {...@@ -454,7 +462,7 @@ pub const DeclState = struct {
454 // DW.AT.type, DW.FORM.ref4462 // DW.AT.type, DW.FORM.ref4
455 const inner_union_index = dbg_info_buffer.items.len;463 const inner_union_index = dbg_info_buffer.items.len;
456 try dbg_info_buffer.resize(inner_union_index + 4);464 try dbg_info_buffer.resize(inner_union_index + 4);
457 try self.addTypeRelocLocal(atom, @intCast(u32, inner_union_index), 5);465 try self.addTypeRelocLocal(atom_index, @intCast(u32, inner_union_index), 5);
458 // DW.AT.data_member_location, DW.FORM.sdata466 // DW.AT.data_member_location, DW.FORM.sdata
459 try leb128.writeULEB128(dbg_info_buffer.writer(), payload_offset);467 try leb128.writeULEB128(dbg_info_buffer.writer(), payload_offset);
460 }468 }
...@@ -481,7 +489,7 @@ pub const DeclState = struct {...@@ -481,7 +489,7 @@ pub const DeclState = struct {
481 // DW.AT.type, DW.FORM.ref4489 // DW.AT.type, DW.FORM.ref4
482 const index = dbg_info_buffer.items.len;490 const index = dbg_info_buffer.items.len;
483 try dbg_info_buffer.resize(index + 4);491 try dbg_info_buffer.resize(index + 4);
484 try self.addTypeRelocGlobal(atom, field.ty, @intCast(u32, index));492 try self.addTypeRelocGlobal(atom_index, field.ty, @intCast(u32, index));
485 // DW.AT.data_member_location, DW.FORM.sdata493 // DW.AT.data_member_location, DW.FORM.sdata
486 try dbg_info_buffer.append(0);494 try dbg_info_buffer.append(0);
487 }495 }
...@@ -498,7 +506,7 @@ pub const DeclState = struct {...@@ -498,7 +506,7 @@ pub const DeclState = struct {
498 // DW.AT.type, DW.FORM.ref4506 // DW.AT.type, DW.FORM.ref4
499 const index = dbg_info_buffer.items.len;507 const index = dbg_info_buffer.items.len;
500 try dbg_info_buffer.resize(index + 4);508 try dbg_info_buffer.resize(index + 4);
501 try self.addTypeRelocGlobal(atom, union_obj.tag_ty, @intCast(u32, index));509 try self.addTypeRelocGlobal(atom_index, union_obj.tag_ty, @intCast(u32, index));
502 // DW.AT.data_member_location, DW.FORM.sdata510 // DW.AT.data_member_location, DW.FORM.sdata
503 try leb128.writeULEB128(dbg_info_buffer.writer(), tag_offset);511 try leb128.writeULEB128(dbg_info_buffer.writer(), tag_offset);
504512
...@@ -541,7 +549,7 @@ pub const DeclState = struct {...@@ -541,7 +549,7 @@ pub const DeclState = struct {
541 // DW.AT.type, DW.FORM.ref4549 // DW.AT.type, DW.FORM.ref4
542 var index = dbg_info_buffer.items.len;550 var index = dbg_info_buffer.items.len;
543 try dbg_info_buffer.resize(index + 4);551 try dbg_info_buffer.resize(index + 4);
544 try self.addTypeRelocGlobal(atom, payload_ty, @intCast(u32, index));552 try self.addTypeRelocGlobal(atom_index, payload_ty, @intCast(u32, index));
545 // DW.AT.data_member_location, DW.FORM.sdata553 // DW.AT.data_member_location, DW.FORM.sdata
546 try leb128.writeULEB128(dbg_info_buffer.writer(), payload_off);554 try leb128.writeULEB128(dbg_info_buffer.writer(), payload_off);
547555
...@@ -554,7 +562,7 @@ pub const DeclState = struct {...@@ -554,7 +562,7 @@ pub const DeclState = struct {
554 // DW.AT.type, DW.FORM.ref4562 // DW.AT.type, DW.FORM.ref4
555 index = dbg_info_buffer.items.len;563 index = dbg_info_buffer.items.len;
556 try dbg_info_buffer.resize(index + 4);564 try dbg_info_buffer.resize(index + 4);
557 try self.addTypeRelocGlobal(atom, error_ty, @intCast(u32, index));565 try self.addTypeRelocGlobal(atom_index, error_ty, @intCast(u32, index));
558 // DW.AT.data_member_location, DW.FORM.sdata566 // DW.AT.data_member_location, DW.FORM.sdata
559 try leb128.writeULEB128(dbg_info_buffer.writer(), error_off);567 try leb128.writeULEB128(dbg_info_buffer.writer(), error_off);
560568
...@@ -587,12 +595,11 @@ pub const DeclState = struct {...@@ -587,12 +595,11 @@ pub const DeclState = struct {
587 self: *DeclState,595 self: *DeclState,
588 name: [:0]const u8,596 name: [:0]const u8,
589 ty: Type,597 ty: Type,
590 tag: File.Tag,
591 owner_decl: Module.Decl.Index,598 owner_decl: Module.Decl.Index,
592 loc: DbgInfoLoc,599 loc: DbgInfoLoc,
593 ) error{OutOfMemory}!void {600 ) error{OutOfMemory}!void {
594 const dbg_info = &self.dbg_info;601 const dbg_info = &self.dbg_info;
595 const atom = getDbgInfoAtom(tag, self.mod, owner_decl);602 const atom_index = self.di_atom_decls.get(owner_decl).?;
596 const name_with_null = name.ptr[0 .. name.len + 1];603 const name_with_null = name.ptr[0 .. name.len + 1];
597604
598 switch (loc) {605 switch (loc) {
...@@ -637,7 +644,7 @@ pub const DeclState = struct {...@@ -637,7 +644,7 @@ pub const DeclState = struct {
637 try dbg_info.ensureUnusedCapacity(5 + name_with_null.len);644 try dbg_info.ensureUnusedCapacity(5 + name_with_null.len);
638 const index = dbg_info.items.len;645 const index = dbg_info.items.len;
639 try dbg_info.resize(index + 4); // dw.at.type, dw.form.ref4646 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.ref4647 try self.addTypeRelocGlobal(atom_index, ty, @intCast(u32, index)); // DW.AT.type, DW.FORM.ref4
641 dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string648 dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string
642 }649 }
643650
...@@ -645,13 +652,12 @@ pub const DeclState = struct {...@@ -645,13 +652,12 @@ pub const DeclState = struct {
645 self: *DeclState,652 self: *DeclState,
646 name: [:0]const u8,653 name: [:0]const u8,
647 ty: Type,654 ty: Type,
648 tag: File.Tag,
649 owner_decl: Module.Decl.Index,655 owner_decl: Module.Decl.Index,
650 is_ptr: bool,656 is_ptr: bool,
651 loc: DbgInfoLoc,657 loc: DbgInfoLoc,
652 ) error{OutOfMemory}!void {658 ) error{OutOfMemory}!void {
653 const dbg_info = &self.dbg_info;659 const dbg_info = &self.dbg_info;
654 const atom = getDbgInfoAtom(tag, self.mod, owner_decl);660 const atom_index = self.di_atom_decls.get(owner_decl).?;
655 const name_with_null = name.ptr[0 .. name.len + 1];661 const name_with_null = name.ptr[0 .. name.len + 1];
656 try dbg_info.append(@enumToInt(AbbrevKind.variable));662 try dbg_info.append(@enumToInt(AbbrevKind.variable));
657 const target = self.mod.getTarget();663 const target = self.mod.getTarget();
...@@ -781,7 +787,7 @@ pub const DeclState = struct {...@@ -781,7 +787,7 @@ pub const DeclState = struct {
781 try dbg_info.ensureUnusedCapacity(5 + name_with_null.len);787 try dbg_info.ensureUnusedCapacity(5 + name_with_null.len);
782 const index = dbg_info.items.len;788 const index = dbg_info.items.len;
783 try dbg_info.resize(index + 4); // dw.at.type, dw.form.ref4789 try dbg_info.resize(index + 4); // dw.at.type, dw.form.ref4
784 try self.addTypeRelocGlobal(atom, child_ty, @intCast(u32, index));790 try self.addTypeRelocGlobal(atom_index, child_ty, @intCast(u32, index));
785 dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string791 dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string
786 }792 }
787793
...@@ -814,7 +820,7 @@ pub const DeclState = struct {...@@ -814,7 +820,7 @@ pub const DeclState = struct {
814};820};
815821
816pub const AbbrevEntry = struct {822pub const AbbrevEntry = struct {
817 atom: *const Atom,823 atom_index: Atom.Index,
818 type: Type,824 type: Type,
819 offset: u32,825 offset: u32,
820};826};
...@@ -823,7 +829,7 @@ pub const AbbrevRelocation = struct {...@@ -823,7 +829,7 @@ pub const AbbrevRelocation = struct {
823 /// If target is null, we deal with a local relocation that is based on simple offset + addend829 /// If target is null, we deal with a local relocation that is based on simple offset + addend
824 /// only.830 /// only.
825 target: ?u32,831 target: ?u32,
826 atom: *const Atom,832 atom_index: Atom.Index,
827 offset: u32,833 offset: u32,
828 addend: u32,834 addend: u32,
829};835};
...@@ -840,26 +846,6 @@ pub const ExprlocRelocation = struct {...@@ -840,26 +846,6 @@ pub const ExprlocRelocation = struct {
840 offset: u32,846 offset: u32,
841};847};
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
863pub const PtrWidth = enum { p32, p64 };849pub const PtrWidth = enum { p32, p64 };
864850
865pub const AbbrevKind = enum(u8) {851pub const AbbrevKind = enum(u8) {
...@@ -909,16 +895,18 @@ pub fn init(allocator: Allocator, bin_file: *File, target: std.Target) Dwarf {...@@ -909,16 +895,18 @@ pub fn init(allocator: Allocator, bin_file: *File, target: std.Target) Dwarf {
909895
910pub fn deinit(self: *Dwarf) void {896pub fn deinit(self: *Dwarf) void {
911 const gpa = self.allocator;897 const gpa = self.allocator;
912 self.dbg_line_fn_free_list.deinit(gpa);898
913 self.atom_free_list.deinit(gpa);899 self.src_fn_free_list.deinit(gpa);
900 self.src_fns.deinit(gpa);
901 self.src_fn_decls.deinit(gpa);
902
903 self.di_atom_free_list.deinit(gpa);
904 self.di_atoms.deinit(gpa);
905 self.di_atom_decls.deinit(gpa);
906
914 self.strtab.deinit(gpa);907 self.strtab.deinit(gpa);
915 self.di_files.deinit(gpa);908 self.di_files.deinit(gpa);
916 self.global_abbrev_relocs.deinit(gpa);909 self.global_abbrev_relocs.deinit(gpa);
917
918 for (self.managed_atoms.items) |atom| {
919 gpa.destroy(atom);
920 }
921 self.managed_atoms.deinit(gpa);
922}910}
923911
924/// Initializes Decl's state and its matching output buffers.912/// Initializes Decl's state and its matching output buffers.
...@@ -934,15 +922,19 @@ pub fn initDeclState(self: *Dwarf, mod: *Module, decl_index: Module.Decl.Index)...@@ -934,15 +922,19 @@ pub fn initDeclState(self: *Dwarf, mod: *Module, decl_index: Module.Decl.Index)
934 log.debug("initDeclState {s}{*}", .{ decl_name, decl });922 log.debug("initDeclState {s}{*}", .{ decl_name, decl });
935923
936 const gpa = self.allocator;924 const gpa = self.allocator;
937 var decl_state = DeclState.init(gpa, mod);925 var decl_state = DeclState.init(gpa, mod, &self.di_atom_decls);
938 errdefer decl_state.deinit();926 errdefer decl_state.deinit();
939 const dbg_line_buffer = &decl_state.dbg_line;927 const dbg_line_buffer = &decl_state.dbg_line;
940 const dbg_info_buffer = &decl_state.dbg_info;928 const dbg_info_buffer = &decl_state.dbg_info;
941929
930 const di_atom_index = try self.getOrCreateAtomForDecl(.di_atom, decl_index);
931
942 assert(decl.has_tv);932 assert(decl.has_tv);
943933
944 switch (decl.ty.zigTypeTag()) {934 switch (decl.ty.zigTypeTag()) {
945 .Fn => {935 .Fn => {
936 _ = try self.getOrCreateAtomForDecl(.src_fn, decl_index);
937
946 // For functions we need to add a prologue to the debug line program.938 // For functions we need to add a prologue to the debug line program.
947 try dbg_line_buffer.ensureTotalCapacity(26);939 try dbg_line_buffer.ensureTotalCapacity(26);
948940
...@@ -1002,8 +994,7 @@ pub fn initDeclState(self: *Dwarf, mod: *Module, decl_index: Module.Decl.Index)...@@ -1002,8 +994,7 @@ pub fn initDeclState(self: *Dwarf, mod: *Module, decl_index: Module.Decl.Index)
1002 dbg_info_buffer.items.len += 4; // DW.AT.high_pc, DW.FORM.data4994 dbg_info_buffer.items.len += 4; // DW.AT.high_pc, DW.FORM.data4
1003 //995 //
1004 if (fn_ret_has_bits) {996 if (fn_ret_has_bits) {
1005 const atom = getDbgInfoAtom(self.bin_file.tag, mod, decl_index);997 try decl_state.addTypeRelocGlobal(di_atom_index, fn_ret_type, @intCast(u32, dbg_info_buffer.items.len));
1006 try decl_state.addTypeRelocGlobal(atom, fn_ret_type, @intCast(u32, dbg_info_buffer.items.len));
1007 dbg_info_buffer.items.len += 4; // DW.AT.type, DW.FORM.ref4998 dbg_info_buffer.items.len += 4; // DW.AT.type, DW.FORM.ref4
1008 }999 }
10091000
...@@ -1075,31 +1066,28 @@ pub fn commitDeclState(...@@ -1075,31 +1066,28 @@ pub fn commitDeclState(
1075 // This logic is nearly identical to the logic below in `updateDeclDebugInfo` for1066 // This logic is nearly identical to the logic below in `updateDeclDebugInfo` for
1076 // `TextBlock` and the .debug_info. If you are editing this logic, you1067 // `TextBlock` and the .debug_info. If you are editing this logic, you
1077 // probably need to edit that logic too.1068 // probably need to edit that logic too.
1078 const src_fn = switch (self.bin_file.tag) {1069 const src_fn_index = self.src_fn_decls.get(decl_index).?;
1079 .elf => &decl.fn_link.elf,1070 const src_fn = self.getAtomPtr(.src_fn, src_fn_index);
1080 .macho => &decl.fn_link.macho,
1081 .wasm => &decl.fn_link.wasm.src_fn,
1082 else => unreachable, // TODO
1083 };
1084 src_fn.len = @intCast(u32, dbg_line_buffer.items.len);1071 src_fn.len = @intCast(u32, dbg_line_buffer.items.len);
10851072
1086 if (self.dbg_line_fn_last) |last| blk: {1073 if (self.src_fn_last_index) |last_index| blk: {
1087 if (src_fn == last) break :blk;1074 if (src_fn_index == last_index) break :blk;
1088 if (src_fn.next) |next| {1075 if (src_fn.next_index) |next_index| {
1076 const next = self.getAtomPtr(.src_fn, next_index);
1089 // Update existing function - non-last item.1077 // Update existing function - non-last item.
1090 if (src_fn.off + src_fn.len + min_nop_size > next.off) {1078 if (src_fn.off + src_fn.len + min_nop_size > next.off) {
1091 // It grew too big, so we move it to a new location.1079 // It grew too big, so we move it to a new location.
1092 if (src_fn.prev) |prev| {1080 if (src_fn.prev_index) |prev_index| {
1093 self.dbg_line_fn_free_list.put(gpa, prev, {}) catch {};1081 self.src_fn_free_list.put(gpa, prev_index, {}) catch {};
1094 prev.next = src_fn.next;1082 self.getAtomPtr(.src_fn, prev_index).next_index = src_fn.next_index;
1095 }1083 }
1096 next.prev = src_fn.prev;1084 next.prev_index = src_fn.prev_index;
1097 src_fn.next = null;1085 src_fn.next_index = null;
1098 // Populate where it used to be with NOPs.1086 // Populate where it used to be with NOPs.
1099 switch (self.bin_file.tag) {1087 switch (self.bin_file.tag) {
1100 .elf => {1088 .elf => {
1101 const elf_file = self.bin_file.cast(File.Elf).?;1089 const elf_file = self.bin_file.cast(File.Elf).?;
1102 const debug_line_sect = &elf_file.sections.items[elf_file.debug_line_section_index.?];1090 const debug_line_sect = &elf_file.sections.items(.shdr)[elf_file.debug_line_section_index.?];
1103 const file_pos = debug_line_sect.sh_offset + src_fn.off;1091 const file_pos = debug_line_sect.sh_offset + src_fn.off;
1104 try pwriteDbgLineNops(elf_file.base.file.?, file_pos, 0, &[0]u8{}, src_fn.len);1092 try pwriteDbgLineNops(elf_file.base.file.?, file_pos, 0, &[0]u8{}, src_fn.len);
1105 },1093 },
...@@ -1111,39 +1099,48 @@ pub fn commitDeclState(...@@ -1111,39 +1099,48 @@ pub fn commitDeclState(
1111 },1099 },
1112 .wasm => {1100 .wasm => {
1113 const wasm_file = self.bin_file.cast(File.Wasm).?;1101 const wasm_file = self.bin_file.cast(File.Wasm).?;
1114 const debug_line = wasm_file.debug_line_atom.?.code;1102 const debug_line = wasm_file.getAtomPtr(wasm_file.debug_line_atom.?).code;
1115 writeDbgLineNopsBuffered(debug_line.items, src_fn.off, 0, &.{}, src_fn.len);1103 writeDbgLineNopsBuffered(debug_line.items, src_fn.off, 0, &.{}, src_fn.len);
1116 },1104 },
1117 else => unreachable,1105 else => unreachable,
1118 }1106 }
1119 // TODO Look at the free list before appending at the end.1107 // TODO Look at the free list before appending at the end.
1120 src_fn.prev = last;1108 src_fn.prev_index = last_index;
1121 last.next = src_fn;1109 const last = self.getAtomPtr(.src_fn, last_index);
1122 self.dbg_line_fn_last = src_fn;1110 last.next_index = src_fn_index;
1111 self.src_fn_last_index = src_fn_index;
11231112
1124 src_fn.off = last.off + padToIdeal(last.len);1113 src_fn.off = last.off + padToIdeal(last.len);
1125 }1114 }
1126 } else if (src_fn.prev == null) {1115 } else if (src_fn.prev_index == null) {
1127 // Append new function.1116 // Append new function.
1128 // TODO Look at the free list before appending at the end.1117 // TODO Look at the free list before appending at the end.
1129 src_fn.prev = last;1118 src_fn.prev_index = last_index;
1130 last.next = src_fn;1119 const last = self.getAtomPtr(.src_fn, last_index);
1131 self.dbg_line_fn_last = src_fn;1120 last.next_index = src_fn_index;
1121 self.src_fn_last_index = src_fn_index;
11321122
1133 src_fn.off = last.off + padToIdeal(last.len);1123 src_fn.off = last.off + padToIdeal(last.len);
1134 }1124 }
1135 } else {1125 } else {
1136 // This is the first function of the Line Number Program.1126 // This is the first function of the Line Number Program.
1137 self.dbg_line_fn_first = src_fn;1127 self.src_fn_first_index = src_fn_index;
1138 self.dbg_line_fn_last = src_fn;1128 self.src_fn_last_index = src_fn_index;
11391129
1140 src_fn.off = padToIdeal(self.dbgLineNeededHeaderBytes(&[0][]u8{}, &[0][]u8{}));1130 src_fn.off = padToIdeal(self.dbgLineNeededHeaderBytes(&[0][]u8{}, &[0][]u8{}));
1141 }1131 }
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);
1144 const needed_size = last_src_fn.off + last_src_fn.len;1135 const needed_size = last_src_fn.off + last_src_fn.len;
1145 const prev_padding_size: u32 = if (src_fn.prev) |prev| src_fn.off - (prev.off + prev.len) else 0;1136 const prev_padding_size: u32 = if (src_fn.prev_index) |prev_index| blk: {
1146 const next_padding_size: u32 = if (src_fn.next) |next| next.off - (src_fn.off + src_fn.len) else 0;1137 const prev = self.getAtom(.src_fn, prev_index);
1138 break :blk src_fn.off - (prev.off + prev.len);
1139 } else 0;
1140 const next_padding_size: u32 = if (src_fn.next_index) |next_index| blk: {
1141 const next = self.getAtom(.src_fn, next_index);
1142 break :blk next.off - (src_fn.off + src_fn.len);
1143 } else 0;
11471144
1148 // We only have support for one compilation unit so far, so the offsets are directly1145 // We only have support for one compilation unit so far, so the offsets are directly
1149 // from the .debug_line section.1146 // from the .debug_line section.
...@@ -1152,7 +1149,7 @@ pub fn commitDeclState(...@@ -1152,7 +1149,7 @@ pub fn commitDeclState(
1152 const elf_file = self.bin_file.cast(File.Elf).?;1149 const elf_file = self.bin_file.cast(File.Elf).?;
1153 const shdr_index = elf_file.debug_line_section_index.?;1150 const shdr_index = elf_file.debug_line_section_index.?;
1154 try elf_file.growNonAllocSection(shdr_index, needed_size, 1, true);1151 try elf_file.growNonAllocSection(shdr_index, needed_size, 1, true);
1155 const debug_line_sect = elf_file.sections.items[shdr_index];1152 const debug_line_sect = elf_file.sections.items(.shdr)[shdr_index];
1156 const file_pos = debug_line_sect.sh_offset + src_fn.off;1153 const file_pos = debug_line_sect.sh_offset + src_fn.off;
1157 try pwriteDbgLineNops(1154 try pwriteDbgLineNops(
1158 elf_file.base.file.?,1155 elf_file.base.file.?,
...@@ -1180,7 +1177,7 @@ pub fn commitDeclState(...@@ -1180,7 +1177,7 @@ pub fn commitDeclState(
11801177
1181 .wasm => {1178 .wasm => {
1182 const wasm_file = self.bin_file.cast(File.Wasm).?;1179 const wasm_file = self.bin_file.cast(File.Wasm).?;
1183 const atom = wasm_file.debug_line_atom.?;1180 const atom = wasm_file.getAtomPtr(wasm_file.debug_line_atom.?);
1184 const debug_line = &atom.code;1181 const debug_line = &atom.code;
1185 const segment_size = debug_line.items.len;1182 const segment_size = debug_line.items.len;
1186 if (needed_size != segment_size) {1183 if (needed_size != segment_size) {
...@@ -1212,7 +1209,7 @@ pub fn commitDeclState(...@@ -1212,7 +1209,7 @@ pub fn commitDeclState(
1212 if (dbg_info_buffer.items.len == 0)1209 if (dbg_info_buffer.items.len == 0)
1213 return;1210 return;
12141211
1215 const atom = getDbgInfoAtom(self.bin_file.tag, module, decl_index);1212 const di_atom_index = self.di_atom_decls.get(decl_index).?;
1216 if (decl_state.abbrev_table.items.len > 0) {1213 if (decl_state.abbrev_table.items.len > 0) {
1217 // Now we emit the .debug_info types of the Decl. These will count towards the size of1214 // Now we emit the .debug_info types of the Decl. These will count towards the size of
1218 // the buffer, so we have to do it before computing the offset, and we can't perform the actual1215 // the buffer, so we have to do it before computing the offset, and we can't perform the actual
...@@ -1234,12 +1231,12 @@ pub fn commitDeclState(...@@ -1234,12 +1231,12 @@ pub fn commitDeclState(
1234 if (deferred) continue;1231 if (deferred) continue;
12351232
1236 symbol.offset = @intCast(u32, dbg_info_buffer.items.len);1233 symbol.offset = @intCast(u32, dbg_info_buffer.items.len);
1237 try decl_state.addDbgInfoType(module, atom, ty);1234 try decl_state.addDbgInfoType(module, di_atom_index, ty);
1238 }1235 }
1239 }1236 }
12401237
1241 log.debug("updateDeclDebugInfoAllocation for '{s}'", .{decl.name});1238 log.debug("updateDeclDebugInfoAllocation for '{s}'", .{decl.name});
1242 try self.updateDeclDebugInfoAllocation(atom, @intCast(u32, dbg_info_buffer.items.len));1239 try self.updateDeclDebugInfoAllocation(di_atom_index, @intCast(u32, dbg_info_buffer.items.len));
12431240
1244 while (decl_state.abbrev_relocs.popOrNull()) |reloc| {1241 while (decl_state.abbrev_relocs.popOrNull()) |reloc| {
1245 if (reloc.target) |target| {1242 if (reloc.target) |target| {
...@@ -1260,11 +1257,12 @@ pub fn commitDeclState(...@@ -1260,11 +1257,12 @@ pub fn commitDeclState(
1260 try self.global_abbrev_relocs.append(gpa, .{1257 try self.global_abbrev_relocs.append(gpa, .{
1261 .target = null,1258 .target = null,
1262 .offset = reloc.offset,1259 .offset = reloc.offset,
1263 .atom = reloc.atom,1260 .atom_index = reloc.atom_index,
1264 .addend = reloc.addend,1261 .addend = reloc.addend,
1265 });1262 });
1266 } else {1263 } else {
1267 const value = symbol.atom.off + symbol.offset + reloc.addend;1264 const atom = self.getAtom(.di_atom, symbol.atom_index);
1265 const value = atom.off + symbol.offset + reloc.addend;
1268 log.debug("{x}: [() => {x}] (%{d}, '{}')", .{ reloc.offset, value, target, ty.fmtDebug() });1266 log.debug("{x}: [() => {x}] (%{d}, '{}')", .{ reloc.offset, value, target, ty.fmtDebug() });
1269 mem.writeInt(1267 mem.writeInt(
1270 u32,1268 u32,
...@@ -1274,10 +1272,11 @@ pub fn commitDeclState(...@@ -1274,10 +1272,11 @@ pub fn commitDeclState(
1274 );1272 );
1275 }1273 }
1276 } else {1274 } else {
1275 const atom = self.getAtom(.di_atom, reloc.atom_index);
1277 mem.writeInt(1276 mem.writeInt(
1278 u32,1277 u32,
1279 dbg_info_buffer.items[reloc.offset..][0..@sizeOf(u32)],1278 dbg_info_buffer.items[reloc.offset..][0..@sizeOf(u32)],
1280 reloc.atom.off + reloc.offset + reloc.addend,1279 atom.off + reloc.offset + reloc.addend,
1281 target_endian,1280 target_endian,
1282 );1281 );
1283 }1282 }
...@@ -1293,7 +1292,7 @@ pub fn commitDeclState(...@@ -1293,7 +1292,7 @@ pub fn commitDeclState(
1293 .got_load => .got_load,1292 .got_load => .got_load,
1294 },1293 },
1295 .target = reloc.target,1294 .target = reloc.target,
1296 .offset = reloc.offset + atom.off,1295 .offset = reloc.offset + self.getAtom(.di_atom, di_atom_index).off,
1297 .addend = 0,1296 .addend = 0,
1298 .prev_vaddr = 0,1297 .prev_vaddr = 0,
1299 });1298 });
...@@ -1303,10 +1302,10 @@ pub fn commitDeclState(...@@ -1303,10 +1302,10 @@ pub fn commitDeclState(
1303 }1302 }
13041303
1305 log.debug("writeDeclDebugInfo for '{s}", .{decl.name});1304 log.debug("writeDeclDebugInfo for '{s}", .{decl.name});
1306 try self.writeDeclDebugInfo(atom, dbg_info_buffer.items);1305 try self.writeDeclDebugInfo(di_atom_index, dbg_info_buffer.items);
1307}1306}
13081307
1309fn updateDeclDebugInfoAllocation(self: *Dwarf, atom: *Atom, len: u32) !void {1308fn updateDeclDebugInfoAllocation(self: *Dwarf, atom_index: Atom.Index, len: u32) !void {
1310 const tracy = trace(@src());1309 const tracy = trace(@src());
1311 defer tracy.end();1310 defer tracy.end();
13121311
...@@ -1315,24 +1314,26 @@ fn updateDeclDebugInfoAllocation(self: *Dwarf, atom: *Atom, len: u32) !void {...@@ -1315,24 +1314,26 @@ fn updateDeclDebugInfoAllocation(self: *Dwarf, atom: *Atom, len: u32) !void {
1315 // probably need to edit that logic too.1314 // probably need to edit that logic too.
1316 const gpa = self.allocator;1315 const gpa = self.allocator;
13171316
1317 const atom = self.getAtomPtr(.di_atom, atom_index);
1318 atom.len = len;1318 atom.len = len;
1319 if (self.atom_last) |last| blk: {1319 if (self.di_atom_last_index) |last_index| blk: {
1320 if (atom == last) break :blk;1320 if (atom_index == last_index) break :blk;
1321 if (atom.next) |next| {1321 if (atom.next_index) |next_index| {
1322 const next = self.getAtomPtr(.di_atom, next_index);
1322 // Update existing Decl - non-last item.1323 // Update existing Decl - non-last item.
1323 if (atom.off + atom.len + min_nop_size > next.off) {1324 if (atom.off + atom.len + min_nop_size > next.off) {
1324 // It grew too big, so we move it to a new location.1325 // It grew too big, so we move it to a new location.
1325 if (atom.prev) |prev| {1326 if (atom.prev_index) |prev_index| {
1326 self.atom_free_list.put(gpa, prev, {}) catch {};1327 self.di_atom_free_list.put(gpa, prev_index, {}) catch {};
1327 prev.next = atom.next;1328 self.getAtomPtr(.di_atom, prev_index).next_index = atom.next_index;
1328 }1329 }
1329 next.prev = atom.prev;1330 next.prev_index = atom.prev_index;
1330 atom.next = null;1331 atom.next_index = null;
1331 // Populate where it used to be with NOPs.1332 // Populate where it used to be with NOPs.
1332 switch (self.bin_file.tag) {1333 switch (self.bin_file.tag) {
1333 .elf => {1334 .elf => {
1334 const elf_file = self.bin_file.cast(File.Elf).?;1335 const elf_file = self.bin_file.cast(File.Elf).?;
1335 const debug_info_sect = &elf_file.sections.items[elf_file.debug_info_section_index.?];1336 const debug_info_sect = &elf_file.sections.items(.shdr)[elf_file.debug_info_section_index.?];
1336 const file_pos = debug_info_sect.sh_offset + atom.off;1337 const file_pos = debug_info_sect.sh_offset + atom.off;
1337 try pwriteDbgInfoNops(elf_file.base.file.?, file_pos, 0, &[0]u8{}, atom.len, false);1338 try pwriteDbgInfoNops(elf_file.base.file.?, file_pos, 0, &[0]u8{}, atom.len, false);
1338 },1339 },
...@@ -1344,37 +1345,40 @@ fn updateDeclDebugInfoAllocation(self: *Dwarf, atom: *Atom, len: u32) !void {...@@ -1344,37 +1345,40 @@ fn updateDeclDebugInfoAllocation(self: *Dwarf, atom: *Atom, len: u32) !void {
1344 },1345 },
1345 .wasm => {1346 .wasm => {
1346 const wasm_file = self.bin_file.cast(File.Wasm).?;1347 const wasm_file = self.bin_file.cast(File.Wasm).?;
1347 const debug_info = &wasm_file.debug_info_atom.?.code;1348 const debug_info_index = wasm_file.debug_info_atom.?;
1349 const debug_info = &wasm_file.getAtomPtr(debug_info_index).code;
1348 try writeDbgInfoNopsToArrayList(gpa, debug_info, atom.off, 0, &.{0}, atom.len, false);1350 try writeDbgInfoNopsToArrayList(gpa, debug_info, atom.off, 0, &.{0}, atom.len, false);
1349 },1351 },
1350 else => unreachable,1352 else => unreachable,
1351 }1353 }
1352 // TODO Look at the free list before appending at the end.1354 // TODO Look at the free list before appending at the end.
1353 atom.prev = last;1355 atom.prev_index = last_index;
1354 last.next = atom;1356 const last = self.getAtomPtr(.di_atom, last_index);
1355 self.atom_last = atom;1357 last.next_index = atom_index;
1358 self.di_atom_last_index = atom_index;
13561359
1357 atom.off = last.off + padToIdeal(last.len);1360 atom.off = last.off + padToIdeal(last.len);
1358 }1361 }
1359 } else if (atom.prev == null) {1362 } else if (atom.prev_index == null) {
1360 // Append new Decl.1363 // Append new Decl.
1361 // TODO Look at the free list before appending at the end.1364 // TODO Look at the free list before appending at the end.
1362 atom.prev = last;1365 atom.prev_index = last_index;
1363 last.next = atom;1366 const last = self.getAtomPtr(.di_atom, last_index);
1364 self.atom_last = atom;1367 last.next_index = atom_index;
1368 self.di_atom_last_index = atom_index;
13651369
1366 atom.off = last.off + padToIdeal(last.len);1370 atom.off = last.off + padToIdeal(last.len);
1367 }1371 }
1368 } else {1372 } else {
1369 // This is the first Decl of the .debug_info1373 // This is the first Decl of the .debug_info
1370 self.atom_first = atom;1374 self.di_atom_first_index = atom_index;
1371 self.atom_last = atom;1375 self.di_atom_last_index = atom_index;
13721376
1373 atom.off = @intCast(u32, padToIdeal(self.dbgInfoHeaderBytes()));1377 atom.off = @intCast(u32, padToIdeal(self.dbgInfoHeaderBytes()));
1374 }1378 }
1375}1379}
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 {
1378 const tracy = trace(@src());1382 const tracy = trace(@src());
1379 defer tracy.end();1383 defer tracy.end();
13801384
...@@ -1383,14 +1387,22 @@ fn writeDeclDebugInfo(self: *Dwarf, atom: *Atom, dbg_info_buf: []const u8) !void...@@ -1383,14 +1387,22 @@ fn writeDeclDebugInfo(self: *Dwarf, atom: *Atom, dbg_info_buf: []const u8) !void
1383 // probably need to edit that logic too.1387 // probably need to edit that logic too.
1384 const gpa = self.allocator;1388 const gpa = self.allocator;
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);
1387 // +1 for a trailing zero to end the children of the decl tag.1393 // +1 for a trailing zero to end the children of the decl tag.
1388 const needed_size = last_decl.off + last_decl.len + 1;1394 const needed_size = last_decl.off + last_decl.len + 1;
1389 const prev_padding_size: u32 = if (atom.prev) |prev| atom.off - (prev.off + prev.len) else 0;1395 const prev_padding_size: u32 = if (atom.prev_index) |prev_index| blk: {
1390 const next_padding_size: u32 = if (atom.next) |next| next.off - (atom.off + atom.len) else 0;1396 const prev = self.getAtom(.di_atom, prev_index);
1397 break :blk atom.off - (prev.off + prev.len);
1398 } else 0;
1399 const next_padding_size: u32 = if (atom.next_index) |next_index| blk: {
1400 const next = self.getAtom(.di_atom, next_index);
1401 break :blk next.off - (atom.off + atom.len);
1402 } else 0;
13911403
1392 // To end the children of the decl tag.1404 // To end the children of the decl tag.
1393 const trailing_zero = atom.next == null;1405 const trailing_zero = atom.next_index == null;
13941406
1395 // We only have support for one compilation unit so far, so the offsets are directly1407 // We only have support for one compilation unit so far, so the offsets are directly
1396 // from the .debug_info section.1408 // from the .debug_info section.
...@@ -1399,7 +1411,7 @@ fn writeDeclDebugInfo(self: *Dwarf, atom: *Atom, dbg_info_buf: []const u8) !void...@@ -1399,7 +1411,7 @@ fn writeDeclDebugInfo(self: *Dwarf, atom: *Atom, dbg_info_buf: []const u8) !void
1399 const elf_file = self.bin_file.cast(File.Elf).?;1411 const elf_file = self.bin_file.cast(File.Elf).?;
1400 const shdr_index = elf_file.debug_info_section_index.?;1412 const shdr_index = elf_file.debug_info_section_index.?;
1401 try elf_file.growNonAllocSection(shdr_index, needed_size, 1, true);1413 try elf_file.growNonAllocSection(shdr_index, needed_size, 1, true);
1402 const debug_info_sect = elf_file.sections.items[shdr_index];1414 const debug_info_sect = elf_file.sections.items(.shdr)[shdr_index];
1403 const file_pos = debug_info_sect.sh_offset + atom.off;1415 const file_pos = debug_info_sect.sh_offset + atom.off;
1404 try pwriteDbgInfoNops(1416 try pwriteDbgInfoNops(
1405 elf_file.base.file.?,1417 elf_file.base.file.?,
...@@ -1430,7 +1442,7 @@ fn writeDeclDebugInfo(self: *Dwarf, atom: *Atom, dbg_info_buf: []const u8) !void...@@ -1430,7 +1442,7 @@ fn writeDeclDebugInfo(self: *Dwarf, atom: *Atom, dbg_info_buf: []const u8) !void
1430 .wasm => {1442 .wasm => {
1431 const wasm_file = self.bin_file.cast(File.Wasm).?;1443 const wasm_file = self.bin_file.cast(File.Wasm).?;
1432 const info_atom = wasm_file.debug_info_atom.?;1444 const info_atom = wasm_file.debug_info_atom.?;
1433 const debug_info = &info_atom.code;1445 const debug_info = &wasm_file.getAtomPtr(info_atom).code;
1434 const segment_size = debug_info.items.len;1446 const segment_size = debug_info.items.len;
1435 if (needed_size != segment_size) {1447 if (needed_size != segment_size) {
1436 log.debug(" needed size does not equal allocated size: {d}", .{needed_size});1448 log.debug(" needed size does not equal allocated size: {d}", .{needed_size});
...@@ -1458,10 +1470,15 @@ fn writeDeclDebugInfo(self: *Dwarf, atom: *Atom, dbg_info_buf: []const u8) !void...@@ -1458,10 +1470,15 @@ fn writeDeclDebugInfo(self: *Dwarf, atom: *Atom, dbg_info_buf: []const u8) !void
1458 }1470 }
1459}1471}
14601472
1461pub fn updateDeclLineNumber(self: *Dwarf, decl: *const Module.Decl) !void {1473pub fn updateDeclLineNumber(self: *Dwarf, module: *Module, decl_index: Module.Decl.Index) !void {
1462 const tracy = trace(@src());1474 const tracy = trace(@src());
1463 defer tracy.end();1475 defer tracy.end();
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);
1465 const func = decl.val.castTag(.function).?.data;1482 const func = decl.val.castTag(.function).?.data;
1466 log.debug("decl.src_line={d}, func.lbrace_line={d}, func.rbrace_line={d}", .{1483 log.debug("decl.src_line={d}, func.lbrace_line={d}, func.rbrace_line={d}", .{
1467 decl.src_line,1484 decl.src_line,
...@@ -1475,79 +1492,81 @@ pub fn updateDeclLineNumber(self: *Dwarf, decl: *const Module.Decl) !void {...@@ -1475,79 +1492,81 @@ pub fn updateDeclLineNumber(self: *Dwarf, decl: *const Module.Decl) !void {
1475 switch (self.bin_file.tag) {1492 switch (self.bin_file.tag) {
1476 .elf => {1493 .elf => {
1477 const elf_file = self.bin_file.cast(File.Elf).?;1494 const elf_file = self.bin_file.cast(File.Elf).?;
1478 const shdr = elf_file.sections.items[elf_file.debug_line_section_index.?];1495 const shdr = elf_file.sections.items(.shdr)[elf_file.debug_line_section_index.?];
1479 const file_pos = shdr.sh_offset + decl.fn_link.elf.off + self.getRelocDbgLineOff();1496 const file_pos = shdr.sh_offset + atom.off + self.getRelocDbgLineOff();
1480 try elf_file.base.file.?.pwriteAll(&data, file_pos);1497 try elf_file.base.file.?.pwriteAll(&data, file_pos);
1481 },1498 },
1482 .macho => {1499 .macho => {
1483 const d_sym = self.bin_file.cast(File.MachO).?.getDebugSymbols().?;1500 const d_sym = self.bin_file.cast(File.MachO).?.getDebugSymbols().?;
1484 const sect = d_sym.getSection(d_sym.debug_line_section_index.?);1501 const sect = d_sym.getSection(d_sym.debug_line_section_index.?);
1485 const file_pos = sect.offset + decl.fn_link.macho.off + self.getRelocDbgLineOff();1502 const file_pos = sect.offset + atom.off + self.getRelocDbgLineOff();
1486 try d_sym.file.pwriteAll(&data, file_pos);1503 try d_sym.file.pwriteAll(&data, file_pos);
1487 },1504 },
1488 .wasm => {1505 .wasm => {
1489 const wasm_file = self.bin_file.cast(File.Wasm).?;1506 const wasm_file = self.bin_file.cast(File.Wasm).?;
1490 const offset = decl.fn_link.wasm.src_fn.off + self.getRelocDbgLineOff();1507 const offset = atom.off + self.getRelocDbgLineOff();
1491 const atom = wasm_file.debug_line_atom.?;1508 const line_atom_index = wasm_file.debug_line_atom.?;
1492 mem.copy(u8, atom.code.items[offset..], &data);1509 mem.copy(u8, wasm_file.getAtomPtr(line_atom_index).code.items[offset..], &data);
1493 },1510 },
1494 else => unreachable,1511 else => unreachable,
1495 }1512 }
1496}1513}
14971514
1498pub fn freeAtom(self: *Dwarf, atom: *Atom) void {1515pub fn freeDecl(self: *Dwarf, decl_index: Module.Decl.Index) void {
1499 if (self.atom_first == atom) {1516 const gpa = self.allocator;
1500 self.atom_first = atom.next;
1501 }
1502 if (self.atom_last == atom) {
1503 // TODO shrink the .debug_info section size here
1504 self.atom_last = atom.prev;
1505 }
1506
1507 if (atom.prev) |prev| {
1508 prev.next = atom.next;
15091517
1510 // TODO the free list logic like we do for text blocks above1518 // Free SrcFn atom
1511 } else {1519 if (self.src_fn_decls.fetchRemove(decl_index)) |kv| {
1512 atom.prev = null;1520 const src_fn_index = kv.value;
1521 const src_fn = self.getAtom(.src_fn, src_fn_index);
1522 _ = self.src_fn_free_list.remove(src_fn_index);
1523
1524 if (src_fn.prev_index) |prev_index| {
1525 self.src_fn_free_list.put(gpa, prev_index, {}) catch {};
1526 const prev = self.getAtomPtr(.src_fn, prev_index);
1527 prev.next_index = src_fn.next_index;
1528 if (src_fn.next_index) |next_index| {
1529 self.getAtomPtr(.src_fn, next_index).prev_index = prev_index;
1530 } else {
1531 self.src_fn_last_index = prev_index;
1532 }
1533 } else if (src_fn.next_index) |next_index| {
1534 self.src_fn_first_index = next_index;
1535 self.getAtomPtr(.src_fn, next_index).prev_index = null;
1536 }
1537 if (self.src_fn_first_index == src_fn_index) {
1538 self.src_fn_first_index = src_fn.next_index;
1539 }
1540 if (self.src_fn_last_index == src_fn_index) {
1541 self.src_fn_last_index = src_fn.prev_index;
1542 }
1513 }1543 }
15141544
1515 if (atom.next) |next| {1545 // Free DI atom
1516 next.prev = atom.prev;1546 if (self.di_atom_decls.fetchRemove(decl_index)) |kv| {
1517 } else {1547 const di_atom_index = kv.value;
1518 atom.next = null;1548 const di_atom = self.getAtomPtr(.di_atom, di_atom_index);
1519 }
1520}
15211549
1522pub fn freeDecl(self: *Dwarf, decl: *Module.Decl) void {1550 if (self.di_atom_first_index == di_atom_index) {
1523 // TODO make this logic match freeTextBlock. Maybe abstract the logic out since the same thing1551 self.di_atom_first_index = di_atom.next_index;
1524 // is desired for both.1552 }
1525 const gpa = self.allocator;1553 if (self.di_atom_last_index == di_atom_index) {
1526 const fn_link = switch (self.bin_file.tag) {1554 // TODO shrink the .debug_info section size here
1527 .elf => &decl.fn_link.elf,1555 self.di_atom_last_index = di_atom.prev_index;
1528 .macho => &decl.fn_link.macho,1556 }
1529 .wasm => &decl.fn_link.wasm.src_fn,
1530 else => unreachable,
1531 };
1532 _ = self.dbg_line_fn_free_list.remove(fn_link);
15331557
1534 if (fn_link.prev) |prev| {1558 if (di_atom.prev_index) |prev_index| {
1535 self.dbg_line_fn_free_list.put(gpa, prev, {}) catch {};1559 self.getAtomPtr(.di_atom, prev_index).next_index = di_atom.next_index;
1536 prev.next = fn_link.next;1560 // TODO the free list logic like we do for SrcFn above
1537 if (fn_link.next) |next| {
1538 next.prev = prev;
1539 } else {1561 } else {
1540 self.dbg_line_fn_last = prev;1562 di_atom.prev_index = null;
1563 }
1564
1565 if (di_atom.next_index) |next_index| {
1566 self.getAtomPtr(.di_atom, next_index).prev_index = di_atom.prev_index;
1567 } else {
1568 di_atom.next_index = null;
1541 }1569 }
1542 } else if (fn_link.next) |next| {
1543 self.dbg_line_fn_first = next;
1544 next.prev = null;
1545 }
1546 if (self.dbg_line_fn_first == fn_link) {
1547 self.dbg_line_fn_first = fn_link.next;
1548 }
1549 if (self.dbg_line_fn_last == fn_link) {
1550 self.dbg_line_fn_last = fn_link.prev;
1551 }1570 }
1552}1571}
15531572
...@@ -1690,7 +1709,7 @@ pub fn writeDbgAbbrev(self: *Dwarf) !void {...@@ -1690,7 +1709,7 @@ pub fn writeDbgAbbrev(self: *Dwarf) !void {
1690 const elf_file = self.bin_file.cast(File.Elf).?;1709 const elf_file = self.bin_file.cast(File.Elf).?;
1691 const shdr_index = elf_file.debug_abbrev_section_index.?;1710 const shdr_index = elf_file.debug_abbrev_section_index.?;
1692 try elf_file.growNonAllocSection(shdr_index, needed_size, 1, false);1711 try elf_file.growNonAllocSection(shdr_index, needed_size, 1, false);
1693 const debug_abbrev_sect = elf_file.sections.items[shdr_index];1712 const debug_abbrev_sect = elf_file.sections.items(.shdr)[shdr_index];
1694 const file_pos = debug_abbrev_sect.sh_offset + abbrev_offset;1713 const file_pos = debug_abbrev_sect.sh_offset + abbrev_offset;
1695 try elf_file.base.file.?.pwriteAll(&abbrev_buf, file_pos);1714 try elf_file.base.file.?.pwriteAll(&abbrev_buf, file_pos);
1696 },1715 },
...@@ -1704,7 +1723,7 @@ pub fn writeDbgAbbrev(self: *Dwarf) !void {...@@ -1704,7 +1723,7 @@ pub fn writeDbgAbbrev(self: *Dwarf) !void {
1704 },1723 },
1705 .wasm => {1724 .wasm => {
1706 const wasm_file = self.bin_file.cast(File.Wasm).?;1725 const wasm_file = self.bin_file.cast(File.Wasm).?;
1707 const debug_abbrev = &wasm_file.debug_abbrev_atom.?.code;1726 const debug_abbrev = &wasm_file.getAtomPtr(wasm_file.debug_abbrev_atom.?).code;
1708 try debug_abbrev.resize(wasm_file.base.allocator, needed_size);1727 try debug_abbrev.resize(wasm_file.base.allocator, needed_size);
1709 mem.copy(u8, debug_abbrev.items, &abbrev_buf);1728 mem.copy(u8, debug_abbrev.items, &abbrev_buf);
1710 },1729 },
...@@ -1770,11 +1789,11 @@ pub fn writeDbgInfoHeader(self: *Dwarf, module: *Module, low_pc: u64, high_pc: u...@@ -1770,11 +1789,11 @@ pub fn writeDbgInfoHeader(self: *Dwarf, module: *Module, low_pc: u64, high_pc: u
1770 },1789 },
1771 }1790 }
1772 // Write the form for the compile unit, which must match the abbrev table above.1791 // Write the form for the compile unit, which must match the abbrev table above.
1773 const name_strp = try self.makeString(module.root_pkg.root_src_path);1792 const name_strp = try self.strtab.insert(self.allocator, module.root_pkg.root_src_path);
1774 var compile_unit_dir_buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;1793 var compile_unit_dir_buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;
1775 const compile_unit_dir = resolveCompilationDir(module, &compile_unit_dir_buffer);1794 const compile_unit_dir = resolveCompilationDir(module, &compile_unit_dir_buffer);
1776 const comp_dir_strp = try self.makeString(compile_unit_dir);1795 const comp_dir_strp = try self.strtab.insert(self.allocator, compile_unit_dir);
1777 const producer_strp = try self.makeString(link.producer_string);1796 const producer_strp = try self.strtab.insert(self.allocator, link.producer_string);
17781797
1779 di_buf.appendAssumeCapacity(@enumToInt(AbbrevKind.compile_unit));1798 di_buf.appendAssumeCapacity(@enumToInt(AbbrevKind.compile_unit));
1780 if (self.bin_file.tag == .macho) {1799 if (self.bin_file.tag == .macho) {
...@@ -1805,7 +1824,7 @@ pub fn writeDbgInfoHeader(self: *Dwarf, module: *Module, low_pc: u64, high_pc: u...@@ -1805,7 +1824,7 @@ pub fn writeDbgInfoHeader(self: *Dwarf, module: *Module, low_pc: u64, high_pc: u
1805 switch (self.bin_file.tag) {1824 switch (self.bin_file.tag) {
1806 .elf => {1825 .elf => {
1807 const elf_file = self.bin_file.cast(File.Elf).?;1826 const elf_file = self.bin_file.cast(File.Elf).?;
1808 const debug_info_sect = elf_file.sections.items[elf_file.debug_info_section_index.?];1827 const debug_info_sect = elf_file.sections.items(.shdr)[elf_file.debug_info_section_index.?];
1809 const file_pos = debug_info_sect.sh_offset;1828 const file_pos = debug_info_sect.sh_offset;
1810 try pwriteDbgInfoNops(elf_file.base.file.?, file_pos, 0, di_buf.items, jmp_amt, false);1829 try pwriteDbgInfoNops(elf_file.base.file.?, file_pos, 0, di_buf.items, jmp_amt, false);
1811 },1830 },
...@@ -1817,7 +1836,7 @@ pub fn writeDbgInfoHeader(self: *Dwarf, module: *Module, low_pc: u64, high_pc: u...@@ -1817,7 +1836,7 @@ pub fn writeDbgInfoHeader(self: *Dwarf, module: *Module, low_pc: u64, high_pc: u
1817 },1836 },
1818 .wasm => {1837 .wasm => {
1819 const wasm_file = self.bin_file.cast(File.Wasm).?;1838 const wasm_file = self.bin_file.cast(File.Wasm).?;
1820 const debug_info = &wasm_file.debug_info_atom.?.code;1839 const debug_info = &wasm_file.getAtomPtr(wasm_file.debug_info_atom.?).code;
1821 try writeDbgInfoNopsToArrayList(self.allocator, debug_info, 0, 0, di_buf.items, jmp_amt, false);1840 try writeDbgInfoNopsToArrayList(self.allocator, debug_info, 0, 0, di_buf.items, jmp_amt, false);
1822 },1841 },
1823 else => unreachable,1842 else => unreachable,
...@@ -2124,7 +2143,7 @@ pub fn writeDbgAranges(self: *Dwarf, addr: u64, size: u64) !void {...@@ -2124,7 +2143,7 @@ pub fn writeDbgAranges(self: *Dwarf, addr: u64, size: u64) !void {
2124 const elf_file = self.bin_file.cast(File.Elf).?;2143 const elf_file = self.bin_file.cast(File.Elf).?;
2125 const shdr_index = elf_file.debug_aranges_section_index.?;2144 const shdr_index = elf_file.debug_aranges_section_index.?;
2126 try elf_file.growNonAllocSection(shdr_index, needed_size, 16, false);2145 try elf_file.growNonAllocSection(shdr_index, needed_size, 16, false);
2127 const debug_aranges_sect = elf_file.sections.items[shdr_index];2146 const debug_aranges_sect = elf_file.sections.items(.shdr)[shdr_index];
2128 const file_pos = debug_aranges_sect.sh_offset;2147 const file_pos = debug_aranges_sect.sh_offset;
2129 try elf_file.base.file.?.pwriteAll(di_buf.items, file_pos);2148 try elf_file.base.file.?.pwriteAll(di_buf.items, file_pos);
2130 },2149 },
...@@ -2138,7 +2157,7 @@ pub fn writeDbgAranges(self: *Dwarf, addr: u64, size: u64) !void {...@@ -2138,7 +2157,7 @@ pub fn writeDbgAranges(self: *Dwarf, addr: u64, size: u64) !void {
2138 },2157 },
2139 .wasm => {2158 .wasm => {
2140 const wasm_file = self.bin_file.cast(File.Wasm).?;2159 const wasm_file = self.bin_file.cast(File.Wasm).?;
2141 const debug_ranges = &wasm_file.debug_ranges_atom.?.code;2160 const debug_ranges = &wasm_file.getAtomPtr(wasm_file.debug_ranges_atom.?).code;
2142 try debug_ranges.resize(wasm_file.base.allocator, needed_size);2161 try debug_ranges.resize(wasm_file.base.allocator, needed_size);
2143 mem.copy(u8, debug_ranges.items, di_buf.items);2162 mem.copy(u8, debug_ranges.items, di_buf.items);
2144 },2163 },
...@@ -2275,19 +2294,23 @@ pub fn writeDbgLineHeader(self: *Dwarf) !void {...@@ -2275,19 +2294,23 @@ pub fn writeDbgLineHeader(self: *Dwarf) !void {
2275 const needed_with_padding = padToIdeal(needed_bytes);2294 const needed_with_padding = padToIdeal(needed_bytes);
2276 const delta = needed_with_padding - dbg_line_prg_off;2295 const delta = needed_with_padding - dbg_line_prg_off;
22772296
2278 var src_fn = self.dbg_line_fn_first.?;2297 const first_fn_index = self.src_fn_first_index.?;
2279 const last_fn = self.dbg_line_fn_last.?;2298 const first_fn = self.getAtom(.src_fn, first_fn_index);
2299 const last_fn_index = self.src_fn_last_index.?;
2300 const last_fn = self.getAtom(.src_fn, last_fn_index);
2301
2302 var src_fn_index = first_fn_index;
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);
2282 defer gpa.free(buffer);2305 defer gpa.free(buffer);
22832306
2284 switch (self.bin_file.tag) {2307 switch (self.bin_file.tag) {
2285 .elf => {2308 .elf => {
2286 const elf_file = self.bin_file.cast(File.Elf).?;2309 const elf_file = self.bin_file.cast(File.Elf).?;
2287 const shdr_index = elf_file.debug_line_section_index.?;2310 const shdr_index = elf_file.debug_line_section_index.?;
2288 const needed_size = elf_file.sections.items[shdr_index].sh_size + delta;2311 const needed_size = elf_file.sections.items(.shdr)[shdr_index].sh_size + delta;
2289 try elf_file.growNonAllocSection(shdr_index, needed_size, 1, true);2312 try elf_file.growNonAllocSection(shdr_index, needed_size, 1, true);
2290 const file_pos = elf_file.sections.items[shdr_index].sh_offset + src_fn.off;2313 const file_pos = elf_file.sections.items(.shdr)[shdr_index].sh_offset + first_fn.off;
22912314
2292 const amt = try elf_file.base.file.?.preadAll(buffer, file_pos);2315 const amt = try elf_file.base.file.?.preadAll(buffer, file_pos);
2293 if (amt != buffer.len) return error.InputOutput;2316 if (amt != buffer.len) return error.InputOutput;
...@@ -2299,7 +2322,7 @@ pub fn writeDbgLineHeader(self: *Dwarf) !void {...@@ -2299,7 +2322,7 @@ pub fn writeDbgLineHeader(self: *Dwarf) !void {
2299 const sect_index = d_sym.debug_line_section_index.?;2322 const sect_index = d_sym.debug_line_section_index.?;
2300 const needed_size = @intCast(u32, d_sym.getSection(sect_index).size + delta);2323 const needed_size = @intCast(u32, d_sym.getSection(sect_index).size + delta);
2301 try d_sym.growSection(sect_index, needed_size, true);2324 try d_sym.growSection(sect_index, needed_size, true);
2302 const file_pos = d_sym.getSection(sect_index).offset + src_fn.off;2325 const file_pos = d_sym.getSection(sect_index).offset + first_fn.off;
23032326
2304 const amt = try d_sym.file.preadAll(buffer, file_pos);2327 const amt = try d_sym.file.preadAll(buffer, file_pos);
2305 if (amt != buffer.len) return error.InputOutput;2328 if (amt != buffer.len) return error.InputOutput;
...@@ -2308,19 +2331,20 @@ pub fn writeDbgLineHeader(self: *Dwarf) !void {...@@ -2308,19 +2331,20 @@ pub fn writeDbgLineHeader(self: *Dwarf) !void {
2308 },2331 },
2309 .wasm => {2332 .wasm => {
2310 const wasm_file = self.bin_file.cast(File.Wasm).?;2333 const wasm_file = self.bin_file.cast(File.Wasm).?;
2311 const debug_line = &wasm_file.debug_line_atom.?.code;2334 const debug_line = &wasm_file.getAtomPtr(wasm_file.debug_line_atom.?).code;
2312 mem.copy(u8, buffer, debug_line.items[src_fn.off..]);2335 mem.copy(u8, buffer, debug_line.items[first_fn.off..]);
2313 try debug_line.resize(self.allocator, debug_line.items.len + delta);2336 try debug_line.resize(self.allocator, debug_line.items.len + delta);
2314 mem.copy(u8, debug_line.items[src_fn.off + delta ..], buffer);2337 mem.copy(u8, debug_line.items[first_fn.off + delta ..], buffer);
2315 },2338 },
2316 else => unreachable,2339 else => unreachable,
2317 }2340 }
23182341
2319 while (true) {2342 while (true) {
2343 const src_fn = self.getAtomPtr(.src_fn, src_fn_index);
2320 src_fn.off += delta;2344 src_fn.off += delta;
23212345
2322 if (src_fn.next) |next| {2346 if (src_fn.next_index) |next_index| {
2323 src_fn = next;2347 src_fn_index = next_index;
2324 } else break;2348 } else break;
2325 }2349 }
2326 }2350 }
...@@ -2346,7 +2370,7 @@ pub fn writeDbgLineHeader(self: *Dwarf) !void {...@@ -2346,7 +2370,7 @@ pub fn writeDbgLineHeader(self: *Dwarf) !void {
2346 switch (self.bin_file.tag) {2370 switch (self.bin_file.tag) {
2347 .elf => {2371 .elf => {
2348 const elf_file = self.bin_file.cast(File.Elf).?;2372 const elf_file = self.bin_file.cast(File.Elf).?;
2349 const debug_line_sect = elf_file.sections.items[elf_file.debug_line_section_index.?];2373 const debug_line_sect = elf_file.sections.items(.shdr)[elf_file.debug_line_section_index.?];
2350 const file_pos = debug_line_sect.sh_offset;2374 const file_pos = debug_line_sect.sh_offset;
2351 try pwriteDbgLineNops(elf_file.base.file.?, file_pos, 0, di_buf.items, jmp_amt);2375 try pwriteDbgLineNops(elf_file.base.file.?, file_pos, 0, di_buf.items, jmp_amt);
2352 },2376 },
...@@ -2358,7 +2382,7 @@ pub fn writeDbgLineHeader(self: *Dwarf) !void {...@@ -2358,7 +2382,7 @@ pub fn writeDbgLineHeader(self: *Dwarf) !void {
2358 },2382 },
2359 .wasm => {2383 .wasm => {
2360 const wasm_file = self.bin_file.cast(File.Wasm).?;2384 const wasm_file = self.bin_file.cast(File.Wasm).?;
2361 const debug_line = wasm_file.debug_line_atom.?.code;2385 const debug_line = &wasm_file.getAtomPtr(wasm_file.debug_line_atom.?).code;
2362 writeDbgLineNopsBuffered(debug_line.items, 0, 0, di_buf.items, jmp_amt);2386 writeDbgLineNopsBuffered(debug_line.items, 0, 0, di_buf.items, jmp_amt);
2363 },2387 },
2364 else => unreachable,2388 else => unreachable,
...@@ -2366,22 +2390,26 @@ pub fn writeDbgLineHeader(self: *Dwarf) !void {...@@ -2366,22 +2390,26 @@ pub fn writeDbgLineHeader(self: *Dwarf) !void {
2366}2390}
23672391
2368fn getDebugInfoOff(self: Dwarf) ?u32 {2392fn getDebugInfoOff(self: Dwarf) ?u32 {
2369 const first = self.atom_first orelse return null;2393 const first_index = self.di_atom_first_index orelse return null;
2394 const first = self.getAtom(.di_atom, first_index);
2370 return first.off;2395 return first.off;
2371}2396}
23722397
2373fn getDebugInfoEnd(self: Dwarf) ?u32 {2398fn getDebugInfoEnd(self: Dwarf) ?u32 {
2374 const last = self.atom_last orelse return null;2399 const last_index = self.di_atom_last_index orelse return null;
2400 const last = self.getAtom(.di_atom, last_index);
2375 return last.off + last.len;2401 return last.off + last.len;
2376}2402}
23772403
2378fn getDebugLineProgramOff(self: Dwarf) ?u32 {2404fn getDebugLineProgramOff(self: Dwarf) ?u32 {
2379 const first = self.dbg_line_fn_first orelse return null;2405 const first_index = self.src_fn_first_index orelse return null;
2406 const first = self.getAtom(.src_fn, first_index);
2380 return first.off;2407 return first.off;
2381}2408}
23822409
2383fn getDebugLineProgramEnd(self: Dwarf) ?u32 {2410fn getDebugLineProgramEnd(self: Dwarf) ?u32 {
2384 const last = self.dbg_line_fn_last orelse return null;2411 const last_index = self.src_fn_last_index orelse return null;
2412 const last = self.getAtom(.src_fn, last_index);
2385 return last.off + last.len;2413 return last.off + last.len;
2386}2414}
23872415
...@@ -2435,15 +2463,6 @@ fn getRelocDbgInfoSubprogramHighPC(self: Dwarf) u32 {...@@ -2435,15 +2463,6 @@ fn getRelocDbgInfoSubprogramHighPC(self: Dwarf) u32 {
2435 return dbg_info_low_pc_reloc_index + self.ptrWidthBytes();2463 return dbg_info_low_pc_reloc_index + self.ptrWidthBytes();
2436}2464}
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
2447fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) {2466fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) {
2448 return actual_size +| (actual_size / ideal_factor);2467 return actual_size +| (actual_size / ideal_factor);
2449}2468}
...@@ -2465,29 +2484,20 @@ pub fn flushModule(self: *Dwarf, module: *Module) !void {...@@ -2465,29 +2484,20 @@ pub fn flushModule(self: *Dwarf, module: *Module) !void {
2465 }2484 }
2466 error_set.names = names;2485 error_set.names = names;
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
2477 var dbg_info_buffer = std.ArrayList(u8).init(arena);2487 var dbg_info_buffer = std.ArrayList(u8).init(arena);
2478 try addDbgInfoErrorSet(arena, module, error_ty, self.target, &dbg_info_buffer);2488 try addDbgInfoErrorSet(arena, module, error_ty, self.target, &dbg_info_buffer);
24792489
2480 try self.managed_atoms.append(gpa, atom);2490 const di_atom_index = try self.createAtom(.di_atom);
2481 log.debug("updateDeclDebugInfoAllocation in flushModule", .{});2491 log.debug("updateDeclDebugInfoAllocation in flushModule", .{});
2482 try self.updateDeclDebugInfoAllocation(atom, @intCast(u32, dbg_info_buffer.items.len));2492 try self.updateDeclDebugInfoAllocation(di_atom_index, @intCast(u32, dbg_info_buffer.items.len));
2483 log.debug("writeDeclDebugInfo in flushModule", .{});2493 log.debug("writeDeclDebugInfo in flushModule", .{});
2484 try self.writeDeclDebugInfo(atom, dbg_info_buffer.items);2494 try self.writeDeclDebugInfo(di_atom_index, dbg_info_buffer.items);
24852495
2486 const file_pos = blk: {2496 const file_pos = blk: {
2487 switch (self.bin_file.tag) {2497 switch (self.bin_file.tag) {
2488 .elf => {2498 .elf => {
2489 const elf_file = self.bin_file.cast(File.Elf).?;2499 const elf_file = self.bin_file.cast(File.Elf).?;
2490 const debug_info_sect = &elf_file.sections.items[elf_file.debug_info_section_index.?];2500 const debug_info_sect = &elf_file.sections.items(.shdr)[elf_file.debug_info_section_index.?];
2491 break :blk debug_info_sect.sh_offset;2501 break :blk debug_info_sect.sh_offset;
2492 },2502 },
2493 .macho => {2503 .macho => {
...@@ -2502,22 +2512,23 @@ pub fn flushModule(self: *Dwarf, module: *Module) !void {...@@ -2502,22 +2512,23 @@ pub fn flushModule(self: *Dwarf, module: *Module) !void {
2502 };2512 };
25032513
2504 var buf: [@sizeOf(u32)]u8 = undefined;2514 var buf: [@sizeOf(u32)]u8 = undefined;
2505 mem.writeInt(u32, &buf, atom.off, self.target.cpu.arch.endian());2515 mem.writeInt(u32, &buf, self.getAtom(.di_atom, di_atom_index).off, self.target.cpu.arch.endian());
25062516
2507 while (self.global_abbrev_relocs.popOrNull()) |reloc| {2517 while (self.global_abbrev_relocs.popOrNull()) |reloc| {
2518 const atom = self.getAtom(.di_atom, reloc.atom_index);
2508 switch (self.bin_file.tag) {2519 switch (self.bin_file.tag) {
2509 .elf => {2520 .elf => {
2510 const elf_file = self.bin_file.cast(File.Elf).?;2521 const elf_file = self.bin_file.cast(File.Elf).?;
2511 try elf_file.base.file.?.pwriteAll(&buf, file_pos + reloc.atom.off + reloc.offset);2522 try elf_file.base.file.?.pwriteAll(&buf, file_pos + atom.off + reloc.offset);
2512 },2523 },
2513 .macho => {2524 .macho => {
2514 const d_sym = self.bin_file.cast(File.MachO).?.getDebugSymbols().?;2525 const d_sym = self.bin_file.cast(File.MachO).?.getDebugSymbols().?;
2515 try d_sym.file.pwriteAll(&buf, file_pos + reloc.atom.off + reloc.offset);2526 try d_sym.file.pwriteAll(&buf, file_pos + atom.off + reloc.offset);
2516 },2527 },
2517 .wasm => {2528 .wasm => {
2518 const wasm_file = self.bin_file.cast(File.Wasm).?;2529 const wasm_file = self.bin_file.cast(File.Wasm).?;
2519 const debug_info = wasm_file.debug_info_atom.?.code;2530 const debug_info = wasm_file.getAtomPtr(wasm_file.debug_info_atom.?).code;
2520 mem.copy(u8, debug_info.items[reloc.atom.off + reloc.offset ..], &buf);2531 mem.copy(u8, debug_info.items[atom.off + reloc.offset ..], &buf);
2521 },2532 },
2522 else => unreachable,2533 else => unreachable,
2523 }2534 }
...@@ -2635,12 +2646,62 @@ fn addDbgInfoErrorSet(...@@ -2635,12 +2646,62 @@ fn addDbgInfoErrorSet(
2635 try dbg_info_buffer.append(0);2646 try dbg_info_buffer.append(0);
2636}2647}
26372648
2638fn getDbgInfoAtom(tag: File.Tag, mod: *Module, decl_index: Module.Decl.Index) *Atom {2649const Kind = enum { src_fn, di_atom };
2639 const decl = mod.declPtr(decl_index);2650
2640 return switch (tag) {2651fn createAtom(self: *Dwarf, comptime kind: Kind) !Atom.Index {
2641 .elf => &decl.link.elf.dbg_info_atom,2652 const index = blk: {
2642 .macho => &decl.link.macho.dbg_info_atom,2653 switch (kind) {
2643 .wasm => &decl.link.wasm.dbg_info_atom,2654 .src_fn => {
2644 else => unreachable,2655 const index = @intCast(Atom.Index, self.src_fns.items.len);
2656 _ = try self.src_fns.addOne(self.allocator);
2657 break :blk index;
2658 },
2659 .di_atom => {
2660 const index = @intCast(Atom.Index, self.di_atoms.items.len);
2661 _ = try self.di_atoms.addOne(self.allocator);
2662 break :blk index;
2663 },
2664 }
2665 };
2666 const atom = self.getAtomPtr(kind, index);
2667 atom.* = .{
2668 .off = 0,
2669 .len = 0,
2670 .prev_index = null,
2671 .next_index = null,
2672 };
2673 return index;
2674}
2675
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],
2645 };2706 };
2646}2707}
src/link/Elf.zig+665-633
...@@ -1,40 +1,89 @@...@@ -1,40 +1,89 @@
1const Elf = @This();1const Elf = @This();
22
3const std = @import("std");3const std = @import("std");
4const build_options = @import("build_options");
4const builtin = @import("builtin");5const builtin = @import("builtin");
5const math = std.math;
6const mem = std.mem;
7const assert = std.debug.assert;6const assert = std.debug.assert;
8const Allocator = std.mem.Allocator;
9const fs = std.fs;
10const elf = std.elf;7const elf = std.elf;
8const fs = std.fs;
11const log = std.log.scoped(.link);9const log = std.log.scoped(.link);
10const math = std.math;
11const mem = std.mem;
1212
13const Module = @import("../Module.zig");
14const Compilation = @import("../Compilation.zig");
15const Dwarf = @import("Dwarf.zig");
16const codegen = @import("../codegen.zig");13const codegen = @import("../codegen.zig");
17const lldMain = @import("../main.zig").lldMain;
18const trace = @import("../tracy.zig").trace;
19const Package = @import("../Package.zig");
20const Value = @import("../value.zig").Value;
21const Type = @import("../type.zig").Type;
22const TypedValue = @import("../TypedValue.zig");
23const link = @import("../link.zig");
24const File = link.File;
25const build_options = @import("build_options");
26const target_util = @import("../target.zig");
27const glibc = @import("../glibc.zig");14const glibc = @import("../glibc.zig");
15const link = @import("../link.zig");
16const lldMain = @import("../main.zig").lldMain;
28const musl = @import("../musl.zig");17const musl = @import("../musl.zig");
29const Cache = @import("../Cache.zig");18const target_util = @import("../target.zig");
19const trace = @import("../tracy.zig").trace;
20
30const Air = @import("../Air.zig");21const 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;
31const Liveness = @import("../Liveness.zig");28const Liveness = @import("../Liveness.zig");
32const LlvmObject = @import("../codegen/llvm.zig").Object;29const LlvmObject = @import("../codegen/llvm.zig").Object;
30const Module = @import("../Module.zig");
31const Package = @import("../Package.zig");
32const StringTable = @import("strtab.zig").StringTable;
33const Type = @import("../type.zig").Type;
34const TypedValue = @import("../TypedValue.zig");
35const Value = @import("../value.zig").Value;
3336
34const default_entry_addr = 0x8000000;37const default_entry_addr = 0x8000000;
3538
36pub const base_tag: File.Tag = .elf;39pub const base_tag: File.Tag = .elf;
3740
41const Section = struct {
42 shdr: elf.Elf64_Shdr,
43 phdr_index: u16,
44
45 /// Index of the last allocated atom in this section.
46 last_atom_index: ?Atom.Index = null,
47
48 /// A list of atoms that have surplus capacity. This list can have false
49 /// positives, as functions grow and shrink over time, only sometimes being added
50 /// or removed from the freelist.
51 ///
52 /// An atom has surplus capacity when its overcapacity value is greater than
53 /// padToIdeal(minimum_atom_size). That is, when it has so
54 /// much extra capacity, that we could fit a small new symbol in it, itself with
55 /// ideal_capacity or more.
56 ///
57 /// Ideal capacity is defined by size + (size / ideal_factor)
58 ///
59 /// Overcapacity is measured by actual_capacity - ideal_capacity. Note that
60 /// overcapacity can be negative. A simple way to have negative overcapacity is to
61 /// allocate a fresh text block, which will have ideal capacity, and then grow it
62 /// by 1 byte. It will then have -1 overcapacity.
63 free_list: std.ArrayListUnmanaged(Atom.Index) = .{},
64};
65
66const DeclMetadata = struct {
67 atom: Atom.Index,
68 shdr: u16,
69 /// A list of all exports aliases of this Decl.
70 exports: std.ArrayListUnmanaged(u32) = .{},
71
72 fn getExport(m: DeclMetadata, elf_file: *const Elf, name: []const u8) ?u32 {
73 for (m.exports.items) |exp| {
74 if (mem.eql(u8, name, elf_file.getGlobalName(exp))) return exp;
75 }
76 return null;
77 }
78
79 fn getExportPtr(m: *DeclMetadata, elf_file: *Elf, name: []const u8) ?*u32 {
80 for (m.exports.items) |*exp| {
81 if (mem.eql(u8, name, elf_file.getGlobalName(exp.*))) return exp;
82 }
83 return null;
84 }
85};
86
38base: File,87base: File,
39dwarf: ?Dwarf = null,88dwarf: ?Dwarf = null,
4089
...@@ -45,12 +94,12 @@ llvm_object: ?*LlvmObject = null,...@@ -45,12 +94,12 @@ llvm_object: ?*LlvmObject = null,
4594
46/// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.95/// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
47/// Same order as in the file.96/// Same order as in the file.
48sections: std.ArrayListUnmanaged(elf.Elf64_Shdr) = std.ArrayListUnmanaged(elf.Elf64_Shdr){},97sections: std.MultiArrayList(Section) = .{},
49shdr_table_offset: ?u64 = null,98shdr_table_offset: ?u64 = null,
5099
51/// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.100/// Stored in native-endian format, depending on target endianness needs to be bswapped on read/write.
52/// Same order as in the file.101/// Same order as in the file.
53program_headers: std.ArrayListUnmanaged(elf.Elf64_Phdr) = std.ArrayListUnmanaged(elf.Elf64_Phdr){},102program_headers: std.ArrayListUnmanaged(elf.Elf64_Phdr) = .{},
54phdr_table_offset: ?u64 = null,103phdr_table_offset: ?u64 = null,
55/// The index into the program headers of a PT_LOAD program header with Read and Execute flags104/// The index into the program headers of a PT_LOAD program header with Read and Execute flags
56phdr_load_re_index: ?u16 = null,105phdr_load_re_index: ?u16 = null,
...@@ -62,12 +111,10 @@ phdr_load_ro_index: ?u16 = null,...@@ -62,12 +111,10 @@ phdr_load_ro_index: ?u16 = null,
62/// The index into the program headers of a PT_LOAD program header with Write flag111/// The index into the program headers of a PT_LOAD program header with Write flag
63phdr_load_rw_index: ?u16 = null,112phdr_load_rw_index: ?u16 = null,
64113
65phdr_shdr_table: std.AutoHashMapUnmanaged(u16, u16) = .{},
66
67entry_addr: ?u64 = null,114entry_addr: ?u64 = null,
68page_size: u32,115page_size: u32,
69116
70shstrtab: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8){},117shstrtab: StringTable(.strtab) = .{},
71shstrtab_index: ?u16 = null,118shstrtab_index: ?u16 = null,
72119
73symtab_section_index: ?u16 = null,120symtab_section_index: ?u16 = null,
...@@ -110,39 +157,14 @@ debug_line_header_dirty: bool = false,...@@ -110,39 +157,14 @@ debug_line_header_dirty: bool = false,
110157
111error_flags: File.ErrorFlags = File.ErrorFlags{},158error_flags: File.ErrorFlags = File.ErrorFlags{},
112159
113/// Pointer to the last allocated atom160/// Table of tracked Decls.
114atoms: std.AutoHashMapUnmanaged(u16, *TextBlock) = .{},161decls: std.AutoHashMapUnmanaged(Module.Decl.Index, DeclMetadata) = .{},
115
116/// A list of text blocks that have surplus capacity. This list can have false
117/// positives, as functions grow and shrink over time, only sometimes being added
118/// or removed from the freelist.
119///
120/// A text block has surplus capacity when its overcapacity value is greater than
121/// padToIdeal(minimum_text_block_size). That is, when it has so
122/// much extra capacity, that we could fit a small new symbol in it, itself with
123/// ideal_capacity or more.
124///
125/// Ideal capacity is defined by size + (size / ideal_factor)
126///
127/// Overcapacity is measured by actual_capacity - ideal_capacity. Note that
128/// overcapacity can be negative. A simple way to have negative overcapacity is to
129/// allocate a fresh text block, which will have ideal capacity, and then grow it
130/// by 1 byte. It will then have -1 overcapacity.
131atom_free_lists: std.AutoHashMapUnmanaged(u16, std.ArrayListUnmanaged(*TextBlock)) = .{},
132
133/// Table of Decls that are currently alive.
134/// We store them here so that we can properly dispose of any allocated
135/// memory within the atom in the incremental linker.
136/// TODO consolidate this.
137decls: std.AutoHashMapUnmanaged(Module.Decl.Index, ?u16) = .{},
138162
139/// List of atoms that are owned directly by the linker.163/// List of atoms that are owned directly by the linker.
140/// Currently these are only atoms that are the result of linking164atoms: std.ArrayListUnmanaged(Atom) = .{},
141/// object files. Atoms which take part in incremental linking are165
142/// at present owned by Module.Decl.166/// Table of atoms indexed by the symbol index.
143/// TODO consolidate this.167atom_by_index_table: std.AutoHashMapUnmanaged(u32, Atom.Index) = .{},
144managed_atoms: std.ArrayListUnmanaged(*TextBlock) = .{},
145atom_by_index_table: std.AutoHashMapUnmanaged(u32, *TextBlock) = .{},
146168
147/// Table of unnamed constants associated with a parent `Decl`.169/// Table of unnamed constants associated with a parent `Decl`.
148/// We store them here so that we can free the constants whenever the `Decl`170/// We store them here so that we can free the constants whenever the `Decl`
...@@ -170,15 +192,8 @@ unnamed_const_atoms: UnnamedConstTable = .{},...@@ -170,15 +192,8 @@ unnamed_const_atoms: UnnamedConstTable = .{},
170/// this will be a table indexed by index into the list of Atoms.192/// this will be a table indexed by index into the list of Atoms.
171relocs: RelocTable = .{},193relocs: RelocTable = .{},
172194
173const Reloc = struct {195const RelocTable = std.AutoHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(Atom.Reloc));
174 target: u32,196const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(Atom.Index));
175 offset: u64,
176 addend: u32,
177 prev_vaddr: u64,
178};
179
180const RelocTable = std.AutoHashMapUnmanaged(*TextBlock, std.ArrayListUnmanaged(Reloc));
181const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(*TextBlock));
182197
183/// When allocating, the ideal_capacity is calculated by198/// When allocating, the ideal_capacity is calculated by
184/// actual_capacity + (actual_capacity / ideal_factor)199/// actual_capacity + (actual_capacity / ideal_factor)
...@@ -187,67 +202,11 @@ const ideal_factor = 3;...@@ -187,67 +202,11 @@ const ideal_factor = 3;
187/// In order for a slice of bytes to be considered eligible to keep metadata pointing at202/// In order for a slice of bytes to be considered eligible to keep metadata pointing at
188/// it as a possible place to put new symbols, it must have enough room for this many bytes203/// it as a possible place to put new symbols, it must have enough room for this many bytes
189/// (plus extra for reserved capacity).204/// (plus extra for reserved capacity).
190const minimum_text_block_size = 64;205const minimum_atom_size = 64;
191const min_text_capacity = padToIdeal(minimum_text_block_size);206pub const min_text_capacity = padToIdeal(minimum_atom_size);
192207
193pub const PtrWidth = enum { p32, p64 };208pub const PtrWidth = enum { p32, p64 };
194209
195pub const TextBlock = struct {
196 /// Each decl always gets a local symbol with the fully qualified name.
197 /// The vaddr and size are found here directly.
198 /// The file offset is found by computing the vaddr offset from the section vaddr
199 /// the symbol references, and adding that to the file offset of the section.
200 /// If this field is 0, it means the codegen size = 0 and there is no symbol or
201 /// offset table entry.
202 local_sym_index: u32,
203 /// This field is undefined for symbols with size = 0.
204 offset_table_index: u32,
205 /// Points to the previous and next neighbors, based on the `text_offset`.
206 /// This can be used to find, for example, the capacity of this `TextBlock`.
207 prev: ?*TextBlock,
208 next: ?*TextBlock,
209
210 dbg_info_atom: Dwarf.Atom,
211
212 pub const empty = TextBlock{
213 .local_sym_index = 0,
214 .offset_table_index = undefined,
215 .prev = null,
216 .next = null,
217 .dbg_info_atom = undefined,
218 };
219
220 /// Returns how much room there is to grow in virtual address space.
221 /// File offset relocation happens transparently, so it is not included in
222 /// this calculation.
223 fn capacity(self: TextBlock, elf_file: Elf) u64 {
224 const self_sym = elf_file.local_symbols.items[self.local_sym_index];
225 if (self.next) |next| {
226 const next_sym = elf_file.local_symbols.items[next.local_sym_index];
227 return next_sym.st_value - self_sym.st_value;
228 } else {
229 // We are the last block. The capacity is limited only by virtual address space.
230 return std.math.maxInt(u32) - self_sym.st_value;
231 }
232 }
233
234 fn freeListEligible(self: TextBlock, elf_file: Elf) bool {
235 // No need to keep a free list node for the last block.
236 const next = self.next orelse return false;
237 const self_sym = elf_file.local_symbols.items[self.local_sym_index];
238 const next_sym = elf_file.local_symbols.items[next.local_sym_index];
239 const cap = next_sym.st_value - self_sym.st_value;
240 const ideal_cap = padToIdeal(self_sym.st_size);
241 if (cap <= ideal_cap) return false;
242 const surplus = cap - ideal_cap;
243 return surplus >= min_text_capacity;
244 }
245};
246
247pub const Export = struct {
248 sym_index: ?u32 = null,
249};
250
251pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Options) !*Elf {210pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Options) !*Elf {
252 assert(options.target.ofmt == .elf);211 assert(options.target.ofmt == .elf);
253212
...@@ -279,16 +238,19 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option...@@ -279,16 +238,19 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
279238
280 // There must always be a null section in index 0239 // There must always be a null section in index 0
281 try self.sections.append(allocator, .{240 try self.sections.append(allocator, .{
282 .sh_name = 0,241 .shdr = .{
283 .sh_type = elf.SHT_NULL,242 .sh_name = 0,
284 .sh_flags = 0,243 .sh_type = elf.SHT_NULL,
285 .sh_addr = 0,244 .sh_flags = 0,
286 .sh_offset = 0,245 .sh_addr = 0,
287 .sh_size = 0,246 .sh_offset = 0,
288 .sh_link = 0,247 .sh_size = 0,
289 .sh_info = 0,248 .sh_link = 0,
290 .sh_addralign = 0,249 .sh_info = 0,
291 .sh_entsize = 0,250 .sh_addralign = 0,
251 .sh_entsize = 0,
252 },
253 .phdr_index = undefined,
292 });254 });
293255
294 try self.populateMissingMetadata();256 try self.populateMissingMetadata();
...@@ -335,74 +297,67 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Elf {...@@ -335,74 +297,67 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Elf {
335}297}
336298
337pub fn deinit(self: *Elf) void {299pub fn deinit(self: *Elf) void {
300 const gpa = self.base.allocator;
301
338 if (build_options.have_llvm) {302 if (build_options.have_llvm) {
339 if (self.llvm_object) |llvm_object| llvm_object.destroy(self.base.allocator);303 if (self.llvm_object) |llvm_object| llvm_object.destroy(gpa);
340 }304 }
341305
342 self.sections.deinit(self.base.allocator);306 for (self.sections.items(.free_list)) |*free_list| {
343 self.program_headers.deinit(self.base.allocator);307 free_list.deinit(gpa);
344 self.shstrtab.deinit(self.base.allocator);308 }
345 self.local_symbols.deinit(self.base.allocator);309 self.sections.deinit(gpa);
346 self.global_symbols.deinit(self.base.allocator);310
347 self.global_symbol_free_list.deinit(self.base.allocator);311 self.program_headers.deinit(gpa);
348 self.local_symbol_free_list.deinit(self.base.allocator);312 self.shstrtab.deinit(gpa);
349 self.offset_table_free_list.deinit(self.base.allocator);313 self.local_symbols.deinit(gpa);
350 self.offset_table.deinit(self.base.allocator);314 self.global_symbols.deinit(gpa);
351 self.phdr_shdr_table.deinit(self.base.allocator);315 self.global_symbol_free_list.deinit(gpa);
352 self.decls.deinit(self.base.allocator);316 self.local_symbol_free_list.deinit(gpa);
353317 self.offset_table_free_list.deinit(gpa);
354 self.atoms.deinit(self.base.allocator);318 self.offset_table.deinit(gpa);
319
355 {320 {
356 var it = self.atom_free_lists.valueIterator();321 var it = self.decls.iterator();
357 while (it.next()) |free_list| {322 while (it.next()) |entry| {
358 free_list.deinit(self.base.allocator);323 entry.value_ptr.exports.deinit(gpa);
359 }324 }
360 self.atom_free_lists.deinit(self.base.allocator);325 self.decls.deinit(gpa);
361 }326 }
362327
363 for (self.managed_atoms.items) |atom| {328 self.atoms.deinit(gpa);
364 self.base.allocator.destroy(atom);329 self.atom_by_index_table.deinit(gpa);
365 }
366 self.managed_atoms.deinit(self.base.allocator);
367330
368 {331 {
369 var it = self.unnamed_const_atoms.valueIterator();332 var it = self.unnamed_const_atoms.valueIterator();
370 while (it.next()) |atoms| {333 while (it.next()) |atoms| {
371 atoms.deinit(self.base.allocator);334 atoms.deinit(gpa);
372 }335 }
373 self.unnamed_const_atoms.deinit(self.base.allocator);336 self.unnamed_const_atoms.deinit(gpa);
374 }337 }
375338
376 {339 {
377 var it = self.relocs.valueIterator();340 var it = self.relocs.valueIterator();
378 while (it.next()) |relocs| {341 while (it.next()) |relocs| {
379 relocs.deinit(self.base.allocator);342 relocs.deinit(gpa);
380 }343 }
381 self.relocs.deinit(self.base.allocator);344 self.relocs.deinit(gpa);
382 }345 }
383346
384 self.atom_by_index_table.deinit(self.base.allocator);
385
386 if (self.dwarf) |*dw| {347 if (self.dwarf) |*dw| {
387 dw.deinit();348 dw.deinit();
388 }349 }
389}350}
390351
391pub fn getDeclVAddr(self: *Elf, decl_index: Module.Decl.Index, reloc_info: File.RelocInfo) !u64 {352pub fn getDeclVAddr(self: *Elf, decl_index: Module.Decl.Index, reloc_info: File.RelocInfo) !u64 {
392 const mod = self.base.options.module.?;
393 const decl = mod.declPtr(decl_index);
394
395 assert(self.llvm_object == null);353 assert(self.llvm_object == null);
396 assert(decl.link.elf.local_sym_index != 0);
397354
398 const target = decl.link.elf.local_sym_index;355 const this_atom_index = try self.getOrCreateAtomForDecl(decl_index);
399 const vaddr = self.local_symbols.items[target].st_value;356 const this_atom = self.getAtom(this_atom_index);
400 const atom = self.atom_by_index_table.get(reloc_info.parent_atom_index).?;357 const target = this_atom.getSymbolIndex().?;
401 const gop = try self.relocs.getOrPut(self.base.allocator, atom);358 const vaddr = this_atom.getSymbol(self).st_value;
402 if (!gop.found_existing) {359 const atom_index = self.getAtomIndexForSymbol(reloc_info.parent_atom_index).?;
403 gop.value_ptr.* = .{};360 try Atom.addRelocation(self, atom_index, .{
404 }
405 try gop.value_ptr.append(self.base.allocator, .{
406 .target = target,361 .target = target,
407 .offset = reloc_info.offset,362 .offset = reloc_info.offset,
408 .addend = reloc_info.addend,363 .addend = reloc_info.addend,
...@@ -423,7 +378,7 @@ fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 {...@@ -423,7 +378,7 @@ fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 {
423378
424 if (self.shdr_table_offset) |off| {379 if (self.shdr_table_offset) |off| {
425 const shdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Shdr) else @sizeOf(elf.Elf64_Shdr);380 const shdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Shdr) else @sizeOf(elf.Elf64_Shdr);
426 const tight_size = self.sections.items.len * shdr_size;381 const tight_size = self.sections.slice().len * shdr_size;
427 const increased_size = padToIdeal(tight_size);382 const increased_size = padToIdeal(tight_size);
428 const test_end = off + increased_size;383 const test_end = off + increased_size;
429 if (end > off and start < test_end) {384 if (end > off and start < test_end) {
...@@ -433,7 +388,7 @@ fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 {...@@ -433,7 +388,7 @@ fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 {
433388
434 if (self.phdr_table_offset) |off| {389 if (self.phdr_table_offset) |off| {
435 const phdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Phdr) else @sizeOf(elf.Elf64_Phdr);390 const phdr_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Phdr) else @sizeOf(elf.Elf64_Phdr);
436 const tight_size = self.sections.items.len * phdr_size;391 const tight_size = self.sections.slice().len * phdr_size;
437 const increased_size = padToIdeal(tight_size);392 const increased_size = padToIdeal(tight_size);
438 const test_end = off + increased_size;393 const test_end = off + increased_size;
439 if (end > off and start < test_end) {394 if (end > off and start < test_end) {
...@@ -441,7 +396,7 @@ fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 {...@@ -441,7 +396,7 @@ fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 {
441 }396 }
442 }397 }
443398
444 for (self.sections.items) |section| {399 for (self.sections.items(.shdr)) |section| {
445 const increased_size = padToIdeal(section.sh_size);400 const increased_size = padToIdeal(section.sh_size);
446 const test_end = section.sh_offset + increased_size;401 const test_end = section.sh_offset + increased_size;
447 if (end > section.sh_offset and start < test_end) {402 if (end > section.sh_offset and start < test_end) {
...@@ -468,7 +423,7 @@ pub fn allocatedSize(self: *Elf, start: u64) u64 {...@@ -468,7 +423,7 @@ pub fn allocatedSize(self: *Elf, start: u64) u64 {
468 if (self.phdr_table_offset) |off| {423 if (self.phdr_table_offset) |off| {
469 if (off > start and off < min_pos) min_pos = off;424 if (off > start and off < min_pos) min_pos = off;
470 }425 }
471 for (self.sections.items) |section| {426 for (self.sections.items(.shdr)) |section| {
472 if (section.sh_offset <= start) continue;427 if (section.sh_offset <= start) continue;
473 if (section.sh_offset < min_pos) min_pos = section.sh_offset;428 if (section.sh_offset < min_pos) min_pos = section.sh_offset;
474 }429 }
...@@ -487,31 +442,10 @@ pub fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u32) u64 {...@@ -487,31 +442,10 @@ pub fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u32) u64 {
487 return start;442 return start;
488}443}
489444
490/// TODO Improve this to use a table.
491fn makeString(self: *Elf, bytes: []const u8) !u32 {
492 try self.shstrtab.ensureUnusedCapacity(self.base.allocator, bytes.len + 1);
493 const result = self.shstrtab.items.len;
494 self.shstrtab.appendSliceAssumeCapacity(bytes);
495 self.shstrtab.appendAssumeCapacity(0);
496 return @intCast(u32, result);
497}
498
499fn getString(self: Elf, str_off: u32) []const u8 {
500 assert(str_off < self.shstrtab.items.len);
501 return mem.sliceTo(@ptrCast([*:0]const u8, self.shstrtab.items.ptr + str_off), 0);
502}
503
504fn updateString(self: *Elf, old_str_off: u32, new_name: []const u8) !u32 {
505 const existing_name = self.getString(old_str_off);
506 if (mem.eql(u8, existing_name, new_name)) {
507 return old_str_off;
508 }
509 return self.makeString(new_name);
510}
511
512pub fn populateMissingMetadata(self: *Elf) !void {445pub fn populateMissingMetadata(self: *Elf) !void {
513 assert(self.llvm_object == null);446 assert(self.llvm_object == null);
514447
448 const gpa = self.base.allocator;
515 const small_ptr = switch (self.ptr_width) {449 const small_ptr = switch (self.ptr_width) {
516 .p32 => true,450 .p32 => true,
517 .p64 => false,451 .p64 => false,
...@@ -525,7 +459,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {...@@ -525,7 +459,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {
525 const off = self.findFreeSpace(file_size, p_align);459 const off = self.findFreeSpace(file_size, p_align);
526 log.debug("found PT_LOAD RE free space 0x{x} to 0x{x}", .{ off, off + file_size });460 log.debug("found PT_LOAD RE free space 0x{x} to 0x{x}", .{ off, off + file_size });
527 const entry_addr: u64 = self.entry_addr orelse if (self.base.options.target.cpu.arch == .spu_2) @as(u64, 0) else default_entry_addr;461 const entry_addr: u64 = self.entry_addr orelse if (self.base.options.target.cpu.arch == .spu_2) @as(u64, 0) else default_entry_addr;
528 try self.program_headers.append(self.base.allocator, .{462 try self.program_headers.append(gpa, .{
529 .p_type = elf.PT_LOAD,463 .p_type = elf.PT_LOAD,
530 .p_offset = off,464 .p_offset = off,
531 .p_filesz = file_size,465 .p_filesz = file_size,
...@@ -535,7 +469,6 @@ pub fn populateMissingMetadata(self: *Elf) !void {...@@ -535,7 +469,6 @@ pub fn populateMissingMetadata(self: *Elf) !void {
535 .p_align = p_align,469 .p_align = p_align,
536 .p_flags = elf.PF_X | elf.PF_R,470 .p_flags = elf.PF_X | elf.PF_R,
537 });471 });
538 try self.atom_free_lists.putNoClobber(self.base.allocator, self.phdr_load_re_index.?, .{});
539 self.entry_addr = null;472 self.entry_addr = null;
540 self.phdr_table_dirty = true;473 self.phdr_table_dirty = true;
541 }474 }
...@@ -552,7 +485,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {...@@ -552,7 +485,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {
552 // we'll need to re-use that function anyway, in case the GOT grows and overlaps something485 // we'll need to re-use that function anyway, in case the GOT grows and overlaps something
553 // else in virtual memory.486 // else in virtual memory.
554 const got_addr: u32 = if (self.base.options.target.cpu.arch.ptrBitWidth() >= 32) 0x4000000 else 0x8000;487 const got_addr: u32 = if (self.base.options.target.cpu.arch.ptrBitWidth() >= 32) 0x4000000 else 0x8000;
555 try self.program_headers.append(self.base.allocator, .{488 try self.program_headers.append(gpa, .{
556 .p_type = elf.PT_LOAD,489 .p_type = elf.PT_LOAD,
557 .p_offset = off,490 .p_offset = off,
558 .p_filesz = file_size,491 .p_filesz = file_size,
...@@ -575,7 +508,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {...@@ -575,7 +508,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {
575 log.debug("found PT_LOAD RO free space 0x{x} to 0x{x}", .{ off, off + file_size });508 log.debug("found PT_LOAD RO free space 0x{x} to 0x{x}", .{ off, off + file_size });
576 // TODO Same as for GOT509 // TODO Same as for GOT
577 const rodata_addr: u32 = if (self.base.options.target.cpu.arch.ptrBitWidth() >= 32) 0xc000000 else 0xa000;510 const rodata_addr: u32 = if (self.base.options.target.cpu.arch.ptrBitWidth() >= 32) 0xc000000 else 0xa000;
578 try self.program_headers.append(self.base.allocator, .{511 try self.program_headers.append(gpa, .{
579 .p_type = elf.PT_LOAD,512 .p_type = elf.PT_LOAD,
580 .p_offset = off,513 .p_offset = off,
581 .p_filesz = file_size,514 .p_filesz = file_size,
...@@ -585,7 +518,6 @@ pub fn populateMissingMetadata(self: *Elf) !void {...@@ -585,7 +518,6 @@ pub fn populateMissingMetadata(self: *Elf) !void {
585 .p_align = p_align,518 .p_align = p_align,
586 .p_flags = elf.PF_R,519 .p_flags = elf.PF_R,
587 });520 });
588 try self.atom_free_lists.putNoClobber(self.base.allocator, self.phdr_load_ro_index.?, .{});
589 self.phdr_table_dirty = true;521 self.phdr_table_dirty = true;
590 }522 }
591523
...@@ -599,7 +531,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {...@@ -599,7 +531,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {
599 log.debug("found PT_LOAD RW free space 0x{x} to 0x{x}", .{ off, off + file_size });531 log.debug("found PT_LOAD RW free space 0x{x} to 0x{x}", .{ off, off + file_size });
600 // TODO Same as for GOT532 // TODO Same as for GOT
601 const rwdata_addr: u32 = if (self.base.options.target.cpu.arch.ptrBitWidth() >= 32) 0x10000000 else 0xc000;533 const rwdata_addr: u32 = if (self.base.options.target.cpu.arch.ptrBitWidth() >= 32) 0x10000000 else 0xc000;
602 try self.program_headers.append(self.base.allocator, .{534 try self.program_headers.append(gpa, .{
603 .p_type = elf.PT_LOAD,535 .p_type = elf.PT_LOAD,
604 .p_offset = off,536 .p_offset = off,
605 .p_filesz = file_size,537 .p_filesz = file_size,
...@@ -609,148 +541,145 @@ pub fn populateMissingMetadata(self: *Elf) !void {...@@ -609,148 +541,145 @@ pub fn populateMissingMetadata(self: *Elf) !void {
609 .p_align = p_align,541 .p_align = p_align,
610 .p_flags = elf.PF_R | elf.PF_W,542 .p_flags = elf.PF_R | elf.PF_W,
611 });543 });
612 try self.atom_free_lists.putNoClobber(self.base.allocator, self.phdr_load_rw_index.?, .{});
613 self.phdr_table_dirty = true;544 self.phdr_table_dirty = true;
614 }545 }
615546
616 if (self.shstrtab_index == null) {547 if (self.shstrtab_index == null) {
617 self.shstrtab_index = @intCast(u16, self.sections.items.len);548 self.shstrtab_index = @intCast(u16, self.sections.slice().len);
618 assert(self.shstrtab.items.len == 0);549 assert(self.shstrtab.buffer.items.len == 0);
619 try self.shstrtab.append(self.base.allocator, 0); // need a 0 at position 0550 try self.shstrtab.buffer.append(gpa, 0); // need a 0 at position 0
620 const off = self.findFreeSpace(self.shstrtab.items.len, 1);551 const off = self.findFreeSpace(self.shstrtab.buffer.items.len, 1);
621 log.debug("found shstrtab free space 0x{x} to 0x{x}", .{ off, off + self.shstrtab.items.len });552 log.debug("found shstrtab free space 0x{x} to 0x{x}", .{ off, off + self.shstrtab.buffer.items.len });
622 try self.sections.append(self.base.allocator, .{553 try self.sections.append(gpa, .{
623 .sh_name = try self.makeString(".shstrtab"),554 .shdr = .{
624 .sh_type = elf.SHT_STRTAB,555 .sh_name = try self.shstrtab.insert(gpa, ".shstrtab"),
625 .sh_flags = 0,556 .sh_type = elf.SHT_STRTAB,
626 .sh_addr = 0,557 .sh_flags = 0,
627 .sh_offset = off,558 .sh_addr = 0,
628 .sh_size = self.shstrtab.items.len,559 .sh_offset = off,
629 .sh_link = 0,560 .sh_size = self.shstrtab.buffer.items.len,
630 .sh_info = 0,561 .sh_link = 0,
631 .sh_addralign = 1,562 .sh_info = 0,
632 .sh_entsize = 0,563 .sh_addralign = 1,
564 .sh_entsize = 0,
565 },
566 .phdr_index = undefined,
633 });567 });
634 self.shstrtab_dirty = true;568 self.shstrtab_dirty = true;
635 self.shdr_table_dirty = true;569 self.shdr_table_dirty = true;
636 }570 }
637571
638 if (self.text_section_index == null) {572 if (self.text_section_index == null) {
639 self.text_section_index = @intCast(u16, self.sections.items.len);573 self.text_section_index = @intCast(u16, self.sections.slice().len);
640 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];574 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
641575
642 try self.sections.append(self.base.allocator, .{576 try self.sections.append(gpa, .{
643 .sh_name = try self.makeString(".text"),577 .shdr = .{
644 .sh_type = elf.SHT_PROGBITS,578 .sh_name = try self.shstrtab.insert(gpa, ".text"),
645 .sh_flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR,579 .sh_type = elf.SHT_PROGBITS,
646 .sh_addr = phdr.p_vaddr,580 .sh_flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR,
647 .sh_offset = phdr.p_offset,581 .sh_addr = phdr.p_vaddr,
648 .sh_size = phdr.p_filesz,582 .sh_offset = phdr.p_offset,
649 .sh_link = 0,583 .sh_size = phdr.p_filesz,
650 .sh_info = 0,584 .sh_link = 0,
651 .sh_addralign = 1,585 .sh_info = 0,
652 .sh_entsize = 0,586 .sh_addralign = 1,
587 .sh_entsize = 0,
588 },
589 .phdr_index = self.phdr_load_re_index.?,
653 });590 });
654 try self.phdr_shdr_table.putNoClobber(
655 self.base.allocator,
656 self.phdr_load_re_index.?,
657 self.text_section_index.?,
658 );
659 self.shdr_table_dirty = true;591 self.shdr_table_dirty = true;
660 }592 }
661593
662 if (self.got_section_index == null) {594 if (self.got_section_index == null) {
663 self.got_section_index = @intCast(u16, self.sections.items.len);595 self.got_section_index = @intCast(u16, self.sections.slice().len);
664 const phdr = &self.program_headers.items[self.phdr_got_index.?];596 const phdr = &self.program_headers.items[self.phdr_got_index.?];
665597
666 try self.sections.append(self.base.allocator, .{598 try self.sections.append(gpa, .{
667 .sh_name = try self.makeString(".got"),599 .shdr = .{
668 .sh_type = elf.SHT_PROGBITS,600 .sh_name = try self.shstrtab.insert(gpa, ".got"),
669 .sh_flags = elf.SHF_ALLOC,601 .sh_type = elf.SHT_PROGBITS,
670 .sh_addr = phdr.p_vaddr,602 .sh_flags = elf.SHF_ALLOC,
671 .sh_offset = phdr.p_offset,603 .sh_addr = phdr.p_vaddr,
672 .sh_size = phdr.p_filesz,604 .sh_offset = phdr.p_offset,
673 .sh_link = 0,605 .sh_size = phdr.p_filesz,
674 .sh_info = 0,606 .sh_link = 0,
675 .sh_addralign = @as(u16, ptr_size),607 .sh_info = 0,
676 .sh_entsize = 0,608 .sh_addralign = @as(u16, ptr_size),
609 .sh_entsize = 0,
610 },
611 .phdr_index = self.phdr_got_index.?,
677 });612 });
678 try self.phdr_shdr_table.putNoClobber(
679 self.base.allocator,
680 self.phdr_got_index.?,
681 self.got_section_index.?,
682 );
683 self.shdr_table_dirty = true;613 self.shdr_table_dirty = true;
684 }614 }
685615
686 if (self.rodata_section_index == null) {616 if (self.rodata_section_index == null) {
687 self.rodata_section_index = @intCast(u16, self.sections.items.len);617 self.rodata_section_index = @intCast(u16, self.sections.slice().len);
688 const phdr = &self.program_headers.items[self.phdr_load_ro_index.?];618 const phdr = &self.program_headers.items[self.phdr_load_ro_index.?];
689619
690 try self.sections.append(self.base.allocator, .{620 try self.sections.append(gpa, .{
691 .sh_name = try self.makeString(".rodata"),621 .shdr = .{
692 .sh_type = elf.SHT_PROGBITS,622 .sh_name = try self.shstrtab.insert(gpa, ".rodata"),
693 .sh_flags = elf.SHF_ALLOC,623 .sh_type = elf.SHT_PROGBITS,
694 .sh_addr = phdr.p_vaddr,624 .sh_flags = elf.SHF_ALLOC,
695 .sh_offset = phdr.p_offset,625 .sh_addr = phdr.p_vaddr,
696 .sh_size = phdr.p_filesz,626 .sh_offset = phdr.p_offset,
697 .sh_link = 0,627 .sh_size = phdr.p_filesz,
698 .sh_info = 0,628 .sh_link = 0,
699 .sh_addralign = 1,629 .sh_info = 0,
700 .sh_entsize = 0,630 .sh_addralign = 1,
631 .sh_entsize = 0,
632 },
633 .phdr_index = self.phdr_load_ro_index.?,
701 });634 });
702 try self.phdr_shdr_table.putNoClobber(
703 self.base.allocator,
704 self.phdr_load_ro_index.?,
705 self.rodata_section_index.?,
706 );
707 self.shdr_table_dirty = true;635 self.shdr_table_dirty = true;
708 }636 }
709637
710 if (self.data_section_index == null) {638 if (self.data_section_index == null) {
711 self.data_section_index = @intCast(u16, self.sections.items.len);639 self.data_section_index = @intCast(u16, self.sections.slice().len);
712 const phdr = &self.program_headers.items[self.phdr_load_rw_index.?];640 const phdr = &self.program_headers.items[self.phdr_load_rw_index.?];
713641
714 try self.sections.append(self.base.allocator, .{642 try self.sections.append(gpa, .{
715 .sh_name = try self.makeString(".data"),643 .shdr = .{
716 .sh_type = elf.SHT_PROGBITS,644 .sh_name = try self.shstrtab.insert(gpa, ".data"),
717 .sh_flags = elf.SHF_WRITE | elf.SHF_ALLOC,645 .sh_type = elf.SHT_PROGBITS,
718 .sh_addr = phdr.p_vaddr,646 .sh_flags = elf.SHF_WRITE | elf.SHF_ALLOC,
719 .sh_offset = phdr.p_offset,647 .sh_addr = phdr.p_vaddr,
720 .sh_size = phdr.p_filesz,648 .sh_offset = phdr.p_offset,
721 .sh_link = 0,649 .sh_size = phdr.p_filesz,
722 .sh_info = 0,650 .sh_link = 0,
723 .sh_addralign = @as(u16, ptr_size),651 .sh_info = 0,
724 .sh_entsize = 0,652 .sh_addralign = @as(u16, ptr_size),
653 .sh_entsize = 0,
654 },
655 .phdr_index = self.phdr_load_rw_index.?,
725 });656 });
726 try self.phdr_shdr_table.putNoClobber(
727 self.base.allocator,
728 self.phdr_load_rw_index.?,
729 self.data_section_index.?,
730 );
731 self.shdr_table_dirty = true;657 self.shdr_table_dirty = true;
732 }658 }
733659
734 if (self.symtab_section_index == null) {660 if (self.symtab_section_index == null) {
735 self.symtab_section_index = @intCast(u16, self.sections.items.len);661 self.symtab_section_index = @intCast(u16, self.sections.slice().len);
736 const min_align: u16 = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym);662 const min_align: u16 = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym);
737 const each_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Sym) else @sizeOf(elf.Elf64_Sym);663 const each_size: u64 = if (small_ptr) @sizeOf(elf.Elf32_Sym) else @sizeOf(elf.Elf64_Sym);
738 const file_size = self.base.options.symbol_count_hint * each_size;664 const file_size = self.base.options.symbol_count_hint * each_size;
739 const off = self.findFreeSpace(file_size, min_align);665 const off = self.findFreeSpace(file_size, min_align);
740 log.debug("found symtab free space 0x{x} to 0x{x}", .{ off, off + file_size });666 log.debug("found symtab free space 0x{x} to 0x{x}", .{ off, off + file_size });
741667
742 try self.sections.append(self.base.allocator, .{668 try self.sections.append(gpa, .{
743 .sh_name = try self.makeString(".symtab"),669 .shdr = .{
744 .sh_type = elf.SHT_SYMTAB,670 .sh_name = try self.shstrtab.insert(gpa, ".symtab"),
745 .sh_flags = 0,671 .sh_type = elf.SHT_SYMTAB,
746 .sh_addr = 0,672 .sh_flags = 0,
747 .sh_offset = off,673 .sh_addr = 0,
748 .sh_size = file_size,674 .sh_offset = off,
749 // The section header index of the associated string table.675 .sh_size = file_size,
750 .sh_link = self.shstrtab_index.?,676 // The section header index of the associated string table.
751 .sh_info = @intCast(u32, self.local_symbols.items.len),677 .sh_link = self.shstrtab_index.?,
752 .sh_addralign = min_align,678 .sh_info = @intCast(u32, self.local_symbols.items.len),
753 .sh_entsize = each_size,679 .sh_addralign = min_align,
680 .sh_entsize = each_size,
681 },
682 .phdr_index = undefined,
754 });683 });
755 self.shdr_table_dirty = true;684 self.shdr_table_dirty = true;
756 try self.writeSymbol(0);685 try self.writeSymbol(0);
...@@ -758,27 +687,30 @@ pub fn populateMissingMetadata(self: *Elf) !void {...@@ -758,27 +687,30 @@ pub fn populateMissingMetadata(self: *Elf) !void {
758687
759 if (self.dwarf) |*dw| {688 if (self.dwarf) |*dw| {
760 if (self.debug_str_section_index == null) {689 if (self.debug_str_section_index == null) {
761 self.debug_str_section_index = @intCast(u16, self.sections.items.len);690 self.debug_str_section_index = @intCast(u16, self.sections.slice().len);
762 assert(dw.strtab.items.len == 0);691 assert(dw.strtab.buffer.items.len == 0);
763 try dw.strtab.append(self.base.allocator, 0);692 try dw.strtab.buffer.append(gpa, 0);
764 try self.sections.append(self.base.allocator, .{693 try self.sections.append(gpa, .{
765 .sh_name = try self.makeString(".debug_str"),694 .shdr = .{
766 .sh_type = elf.SHT_PROGBITS,695 .sh_name = try self.shstrtab.insert(gpa, ".debug_str"),
767 .sh_flags = elf.SHF_MERGE | elf.SHF_STRINGS,696 .sh_type = elf.SHT_PROGBITS,
768 .sh_addr = 0,697 .sh_flags = elf.SHF_MERGE | elf.SHF_STRINGS,
769 .sh_offset = 0,698 .sh_addr = 0,
770 .sh_size = 0,699 .sh_offset = 0,
771 .sh_link = 0,700 .sh_size = 0,
772 .sh_info = 0,701 .sh_link = 0,
773 .sh_addralign = 1,702 .sh_info = 0,
774 .sh_entsize = 1,703 .sh_addralign = 1,
704 .sh_entsize = 1,
705 },
706 .phdr_index = undefined,
775 });707 });
776 self.debug_strtab_dirty = true;708 self.debug_strtab_dirty = true;
777 self.shdr_table_dirty = true;709 self.shdr_table_dirty = true;
778 }710 }
779711
780 if (self.debug_info_section_index == null) {712 if (self.debug_info_section_index == null) {
781 self.debug_info_section_index = @intCast(u16, self.sections.items.len);713 self.debug_info_section_index = @intCast(u16, self.sections.slice().len);
782714
783 const file_size_hint = 200;715 const file_size_hint = 200;
784 const p_align = 1;716 const p_align = 1;
...@@ -787,24 +719,27 @@ pub fn populateMissingMetadata(self: *Elf) !void {...@@ -787,24 +719,27 @@ pub fn populateMissingMetadata(self: *Elf) !void {
787 off,719 off,
788 off + file_size_hint,720 off + file_size_hint,
789 });721 });
790 try self.sections.append(self.base.allocator, .{722 try self.sections.append(gpa, .{
791 .sh_name = try self.makeString(".debug_info"),723 .shdr = .{
792 .sh_type = elf.SHT_PROGBITS,724 .sh_name = try self.shstrtab.insert(gpa, ".debug_info"),
793 .sh_flags = 0,725 .sh_type = elf.SHT_PROGBITS,
794 .sh_addr = 0,726 .sh_flags = 0,
795 .sh_offset = off,727 .sh_addr = 0,
796 .sh_size = file_size_hint,728 .sh_offset = off,
797 .sh_link = 0,729 .sh_size = file_size_hint,
798 .sh_info = 0,730 .sh_link = 0,
799 .sh_addralign = p_align,731 .sh_info = 0,
800 .sh_entsize = 0,732 .sh_addralign = p_align,
733 .sh_entsize = 0,
734 },
735 .phdr_index = undefined,
801 });736 });
802 self.shdr_table_dirty = true;737 self.shdr_table_dirty = true;
803 self.debug_info_header_dirty = true;738 self.debug_info_header_dirty = true;
804 }739 }
805740
806 if (self.debug_abbrev_section_index == null) {741 if (self.debug_abbrev_section_index == null) {
807 self.debug_abbrev_section_index = @intCast(u16, self.sections.items.len);742 self.debug_abbrev_section_index = @intCast(u16, self.sections.slice().len);
808743
809 const file_size_hint = 128;744 const file_size_hint = 128;
810 const p_align = 1;745 const p_align = 1;
...@@ -813,24 +748,27 @@ pub fn populateMissingMetadata(self: *Elf) !void {...@@ -813,24 +748,27 @@ pub fn populateMissingMetadata(self: *Elf) !void {
813 off,748 off,
814 off + file_size_hint,749 off + file_size_hint,
815 });750 });
816 try self.sections.append(self.base.allocator, .{751 try self.sections.append(gpa, .{
817 .sh_name = try self.makeString(".debug_abbrev"),752 .shdr = .{
818 .sh_type = elf.SHT_PROGBITS,753 .sh_name = try self.shstrtab.insert(gpa, ".debug_abbrev"),
819 .sh_flags = 0,754 .sh_type = elf.SHT_PROGBITS,
820 .sh_addr = 0,755 .sh_flags = 0,
821 .sh_offset = off,756 .sh_addr = 0,
822 .sh_size = file_size_hint,757 .sh_offset = off,
823 .sh_link = 0,758 .sh_size = file_size_hint,
824 .sh_info = 0,759 .sh_link = 0,
825 .sh_addralign = p_align,760 .sh_info = 0,
826 .sh_entsize = 0,761 .sh_addralign = p_align,
762 .sh_entsize = 0,
763 },
764 .phdr_index = undefined,
827 });765 });
828 self.shdr_table_dirty = true;766 self.shdr_table_dirty = true;
829 self.debug_abbrev_section_dirty = true;767 self.debug_abbrev_section_dirty = true;
830 }768 }
831769
832 if (self.debug_aranges_section_index == null) {770 if (self.debug_aranges_section_index == null) {
833 self.debug_aranges_section_index = @intCast(u16, self.sections.items.len);771 self.debug_aranges_section_index = @intCast(u16, self.sections.slice().len);
834772
835 const file_size_hint = 160;773 const file_size_hint = 160;
836 const p_align = 16;774 const p_align = 16;
...@@ -839,24 +777,27 @@ pub fn populateMissingMetadata(self: *Elf) !void {...@@ -839,24 +777,27 @@ pub fn populateMissingMetadata(self: *Elf) !void {
839 off,777 off,
840 off + file_size_hint,778 off + file_size_hint,
841 });779 });
842 try self.sections.append(self.base.allocator, .{780 try self.sections.append(gpa, .{
843 .sh_name = try self.makeString(".debug_aranges"),781 .shdr = .{
844 .sh_type = elf.SHT_PROGBITS,782 .sh_name = try self.shstrtab.insert(gpa, ".debug_aranges"),
845 .sh_flags = 0,783 .sh_type = elf.SHT_PROGBITS,
846 .sh_addr = 0,784 .sh_flags = 0,
847 .sh_offset = off,785 .sh_addr = 0,
848 .sh_size = file_size_hint,786 .sh_offset = off,
849 .sh_link = 0,787 .sh_size = file_size_hint,
850 .sh_info = 0,788 .sh_link = 0,
851 .sh_addralign = p_align,789 .sh_info = 0,
852 .sh_entsize = 0,790 .sh_addralign = p_align,
791 .sh_entsize = 0,
792 },
793 .phdr_index = undefined,
853 });794 });
854 self.shdr_table_dirty = true;795 self.shdr_table_dirty = true;
855 self.debug_aranges_section_dirty = true;796 self.debug_aranges_section_dirty = true;
856 }797 }
857798
858 if (self.debug_line_section_index == null) {799 if (self.debug_line_section_index == null) {
859 self.debug_line_section_index = @intCast(u16, self.sections.items.len);800 self.debug_line_section_index = @intCast(u16, self.sections.slice().len);
860801
861 const file_size_hint = 250;802 const file_size_hint = 250;
862 const p_align = 1;803 const p_align = 1;
...@@ -865,17 +806,20 @@ pub fn populateMissingMetadata(self: *Elf) !void {...@@ -865,17 +806,20 @@ pub fn populateMissingMetadata(self: *Elf) !void {
865 off,806 off,
866 off + file_size_hint,807 off + file_size_hint,
867 });808 });
868 try self.sections.append(self.base.allocator, .{809 try self.sections.append(gpa, .{
869 .sh_name = try self.makeString(".debug_line"),810 .shdr = .{
870 .sh_type = elf.SHT_PROGBITS,811 .sh_name = try self.shstrtab.insert(gpa, ".debug_line"),
871 .sh_flags = 0,812 .sh_type = elf.SHT_PROGBITS,
872 .sh_addr = 0,813 .sh_flags = 0,
873 .sh_offset = off,814 .sh_addr = 0,
874 .sh_size = file_size_hint,815 .sh_offset = off,
875 .sh_link = 0,816 .sh_size = file_size_hint,
876 .sh_info = 0,817 .sh_link = 0,
877 .sh_addralign = p_align,818 .sh_info = 0,
878 .sh_entsize = 0,819 .sh_addralign = p_align,
820 .sh_entsize = 0,
821 },
822 .phdr_index = undefined,
879 });823 });
880 self.shdr_table_dirty = true;824 self.shdr_table_dirty = true;
881 self.debug_line_header_dirty = true;825 self.debug_line_header_dirty = true;
...@@ -891,7 +835,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {...@@ -891,7 +835,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {
891 .p64 => @alignOf(elf.Elf64_Shdr),835 .p64 => @alignOf(elf.Elf64_Shdr),
892 };836 };
893 if (self.shdr_table_offset == null) {837 if (self.shdr_table_offset == null) {
894 self.shdr_table_offset = self.findFreeSpace(self.sections.items.len * shsize, shalign);838 self.shdr_table_offset = self.findFreeSpace(self.sections.slice().len * shsize, shalign);
895 self.shdr_table_dirty = true;839 self.shdr_table_dirty = true;
896 }840 }
897841
...@@ -922,7 +866,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {...@@ -922,7 +866,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {
922 // offset + it's filesize.866 // offset + it's filesize.
923 var max_file_offset: u64 = 0;867 var max_file_offset: u64 = 0;
924868
925 for (self.sections.items) |shdr| {869 for (self.sections.items(.shdr)) |shdr| {
926 if (shdr.sh_offset + shdr.sh_size > max_file_offset) {870 if (shdr.sh_offset + shdr.sh_size > max_file_offset) {
927 max_file_offset = shdr.sh_offset + shdr.sh_size;871 max_file_offset = shdr.sh_offset + shdr.sh_size;
928 }872 }
...@@ -932,24 +876,27 @@ pub fn populateMissingMetadata(self: *Elf) !void {...@@ -932,24 +876,27 @@ pub fn populateMissingMetadata(self: *Elf) !void {
932 }876 }
933}877}
934878
935fn growAllocSection(self: *Elf, shdr_index: u16, phdr_index: u16, needed_size: u64) !void {879fn growAllocSection(self: *Elf, shdr_index: u16, needed_size: u64) !void {
936 // TODO Also detect virtual address collisions.880 // TODO Also detect virtual address collisions.
937 const shdr = &self.sections.items[shdr_index];881 const shdr = &self.sections.items(.shdr)[shdr_index];
882 const phdr_index = self.sections.items(.phdr_index)[shdr_index];
938 const phdr = &self.program_headers.items[phdr_index];883 const phdr = &self.program_headers.items[phdr_index];
884 const maybe_last_atom_index = self.sections.items(.last_atom_index)[shdr_index];
939885
940 if (needed_size > self.allocatedSize(shdr.sh_offset)) {886 if (needed_size > self.allocatedSize(shdr.sh_offset)) {
941 // Must move the entire section.887 // Must move the entire section.
942 const new_offset = self.findFreeSpace(needed_size, self.page_size);888 const new_offset = self.findFreeSpace(needed_size, self.page_size);
943 const existing_size = if (self.atoms.get(phdr_index)) |last| blk: {889 const existing_size = if (maybe_last_atom_index) |last_atom_index| blk: {
944 const sym = self.local_symbols.items[last.local_sym_index];890 const last = self.getAtom(last_atom_index);
891 const sym = last.getSymbol(self);
945 break :blk (sym.st_value + sym.st_size) - phdr.p_vaddr;892 break :blk (sym.st_value + sym.st_size) - phdr.p_vaddr;
946 } else if (shdr_index == self.got_section_index.?) blk: {893 } else if (shdr_index == self.got_section_index.?) blk: {
947 break :blk shdr.sh_size;894 break :blk shdr.sh_size;
948 } else 0;895 } else 0;
949 shdr.sh_size = 0;896 shdr.sh_size = 0;
950897
951 log.debug("new '{s}' file offset 0x{x} to 0x{x}", .{898 log.debug("new '{?s}' file offset 0x{x} to 0x{x}", .{
952 self.getString(shdr.sh_name),899 self.shstrtab.get(shdr.sh_name),
953 new_offset,900 new_offset,
954 new_offset + existing_size,901 new_offset + existing_size,
955 });902 });
...@@ -975,7 +922,7 @@ pub fn growNonAllocSection(...@@ -975,7 +922,7 @@ pub fn growNonAllocSection(
975 min_alignment: u32,922 min_alignment: u32,
976 requires_file_copy: bool,923 requires_file_copy: bool,
977) !void {924) !void {
978 const shdr = &self.sections.items[shdr_index];925 const shdr = &self.sections.items(.shdr)[shdr_index];
979926
980 if (needed_size > self.allocatedSize(shdr.sh_offset)) {927 if (needed_size > self.allocatedSize(shdr.sh_offset)) {
981 const existing_size = if (self.symtab_section_index.? == shdr_index) blk: {928 const existing_size = if (self.symtab_section_index.? == shdr_index) blk: {
...@@ -988,7 +935,7 @@ pub fn growNonAllocSection(...@@ -988,7 +935,7 @@ pub fn growNonAllocSection(
988 shdr.sh_size = 0;935 shdr.sh_size = 0;
989 // Move all the symbols to a new file location.936 // Move all the symbols to a new file location.
990 const new_offset = self.findFreeSpace(needed_size, min_alignment);937 const new_offset = self.findFreeSpace(needed_size, min_alignment);
991 log.debug("moving '{s}' from 0x{x} to 0x{x}", .{ self.getString(shdr.sh_name), shdr.sh_offset, new_offset });938 log.debug("moving '{?s}' from 0x{x} to 0x{x}", .{ self.shstrtab.get(shdr.sh_name), shdr.sh_offset, new_offset });
992939
993 if (requires_file_copy) {940 if (requires_file_copy) {
994 const amt = try self.base.file.?.copyRangeAll(941 const amt = try self.base.file.?.copyRangeAll(
...@@ -1059,6 +1006,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node...@@ -1059,6 +1006,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
1059 }1006 }
1060 }1007 }
10611008
1009 const gpa = self.base.allocator;
1062 var sub_prog_node = prog_node.start("ELF Flush", 0);1010 var sub_prog_node = prog_node.start("ELF Flush", 0);
1063 sub_prog_node.activate();1011 sub_prog_node.activate();
1064 defer sub_prog_node.end();1012 defer sub_prog_node.end();
...@@ -1077,12 +1025,13 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node...@@ -1077,12 +1025,13 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
1077 {1025 {
1078 var it = self.relocs.iterator();1026 var it = self.relocs.iterator();
1079 while (it.next()) |entry| {1027 while (it.next()) |entry| {
1080 const atom = entry.key_ptr.*;1028 const atom_index = entry.key_ptr.*;
1081 const relocs = entry.value_ptr.*;1029 const relocs = entry.value_ptr.*;
1082 const source_sym = self.local_symbols.items[atom.local_sym_index];1030 const atom = self.getAtom(atom_index);
1083 const source_shdr = self.sections.items[source_sym.st_shndx];1031 const source_sym = atom.getSymbol(self);
1032 const source_shdr = self.sections.items(.shdr)[source_sym.st_shndx];
10841033
1085 log.debug("relocating '{s}'", .{self.getString(source_sym.st_name)});1034 log.debug("relocating '{?s}'", .{self.shstrtab.get(source_sym.st_name)});
10861035
1087 for (relocs.items) |*reloc| {1036 for (relocs.items) |*reloc| {
1088 const target_sym = self.local_symbols.items[reloc.target];1037 const target_sym = self.local_symbols.items[reloc.target];
...@@ -1093,10 +1042,10 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node...@@ -1093,10 +1042,10 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
1093 const section_offset = (source_sym.st_value + reloc.offset) - source_shdr.sh_addr;1042 const section_offset = (source_sym.st_value + reloc.offset) - source_shdr.sh_addr;
1094 const file_offset = source_shdr.sh_offset + section_offset;1043 const file_offset = source_shdr.sh_offset + section_offset;
10951044
1096 log.debug(" ({x}: [() => 0x{x}] ({s}))", .{1045 log.debug(" ({x}: [() => 0x{x}] ({?s}))", .{
1097 reloc.offset,1046 reloc.offset,
1098 target_vaddr,1047 target_vaddr,
1099 self.getString(target_sym.st_name),1048 self.shstrtab.get(target_sym.st_name),
1100 });1049 });
11011050
1102 switch (self.ptr_width) {1051 switch (self.ptr_width) {
...@@ -1174,8 +1123,8 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node...@@ -1174,8 +1123,8 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
11741123
1175 switch (self.ptr_width) {1124 switch (self.ptr_width) {
1176 .p32 => {1125 .p32 => {
1177 const buf = try self.base.allocator.alloc(elf.Elf32_Phdr, self.program_headers.items.len);1126 const buf = try gpa.alloc(elf.Elf32_Phdr, self.program_headers.items.len);
1178 defer self.base.allocator.free(buf);1127 defer gpa.free(buf);
11791128
1180 for (buf) |*phdr, i| {1129 for (buf) |*phdr, i| {
1181 phdr.* = progHeaderTo32(self.program_headers.items[i]);1130 phdr.* = progHeaderTo32(self.program_headers.items[i]);
...@@ -1186,8 +1135,8 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node...@@ -1186,8 +1135,8 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
1186 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?);1135 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.phdr_table_offset.?);
1187 },1136 },
1188 .p64 => {1137 .p64 => {
1189 const buf = try self.base.allocator.alloc(elf.Elf64_Phdr, self.program_headers.items.len);1138 const buf = try gpa.alloc(elf.Elf64_Phdr, self.program_headers.items.len);
1190 defer self.base.allocator.free(buf);1139 defer gpa.free(buf);
11911140
1192 for (buf) |*phdr, i| {1141 for (buf) |*phdr, i| {
1193 phdr.* = self.program_headers.items[i];1142 phdr.* = self.program_headers.items[i];
...@@ -1203,20 +1152,20 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node...@@ -1203,20 +1152,20 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
12031152
1204 {1153 {
1205 const shdr_index = self.shstrtab_index.?;1154 const shdr_index = self.shstrtab_index.?;
1206 if (self.shstrtab_dirty or self.shstrtab.items.len != self.sections.items[shdr_index].sh_size) {1155 if (self.shstrtab_dirty or self.shstrtab.buffer.items.len != self.sections.items(.shdr)[shdr_index].sh_size) {
1207 try self.growNonAllocSection(shdr_index, self.shstrtab.items.len, 1, false);1156 try self.growNonAllocSection(shdr_index, self.shstrtab.buffer.items.len, 1, false);
1208 const shstrtab_sect = self.sections.items[shdr_index];1157 const shstrtab_sect = self.sections.items(.shdr)[shdr_index];
1209 try self.base.file.?.pwriteAll(self.shstrtab.items, shstrtab_sect.sh_offset);1158 try self.base.file.?.pwriteAll(self.shstrtab.buffer.items, shstrtab_sect.sh_offset);
1210 self.shstrtab_dirty = false;1159 self.shstrtab_dirty = false;
1211 }1160 }
1212 }1161 }
12131162
1214 if (self.dwarf) |dwarf| {1163 if (self.dwarf) |dwarf| {
1215 const shdr_index = self.debug_str_section_index.?;1164 const shdr_index = self.debug_str_section_index.?;
1216 if (self.debug_strtab_dirty or dwarf.strtab.items.len != self.sections.items[shdr_index].sh_size) {1165 if (self.debug_strtab_dirty or dwarf.strtab.buffer.items.len != self.sections.items(.shdr)[shdr_index].sh_size) {
1217 try self.growNonAllocSection(shdr_index, dwarf.strtab.items.len, 1, false);1166 try self.growNonAllocSection(shdr_index, dwarf.strtab.buffer.items.len, 1, false);
1218 const debug_strtab_sect = self.sections.items[shdr_index];1167 const debug_strtab_sect = self.sections.items(.shdr)[shdr_index];
1219 try self.base.file.?.pwriteAll(dwarf.strtab.items, debug_strtab_sect.sh_offset);1168 try self.base.file.?.pwriteAll(dwarf.strtab.buffer.items, debug_strtab_sect.sh_offset);
1220 self.debug_strtab_dirty = false;1169 self.debug_strtab_dirty = false;
1221 }1170 }
1222 }1171 }
...@@ -1231,7 +1180,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node...@@ -1231,7 +1180,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
1231 .p64 => @alignOf(elf.Elf64_Shdr),1180 .p64 => @alignOf(elf.Elf64_Shdr),
1232 };1181 };
1233 const allocated_size = self.allocatedSize(self.shdr_table_offset.?);1182 const allocated_size = self.allocatedSize(self.shdr_table_offset.?);
1234 const needed_size = self.sections.items.len * shsize;1183 const needed_size = self.sections.slice().len * shsize;
12351184
1236 if (needed_size > allocated_size) {1185 if (needed_size > allocated_size) {
1237 self.shdr_table_offset = null; // free the space1186 self.shdr_table_offset = null; // free the space
...@@ -1240,12 +1189,13 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node...@@ -1240,12 +1189,13 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
12401189
1241 switch (self.ptr_width) {1190 switch (self.ptr_width) {
1242 .p32 => {1191 .p32 => {
1243 const buf = try self.base.allocator.alloc(elf.Elf32_Shdr, self.sections.items.len);1192 const slice = self.sections.slice();
1244 defer self.base.allocator.free(buf);1193 const buf = try gpa.alloc(elf.Elf32_Shdr, slice.len);
1194 defer gpa.free(buf);
12451195
1246 for (buf) |*shdr, i| {1196 for (buf) |*shdr, i| {
1247 shdr.* = sectHeaderTo32(self.sections.items[i]);1197 shdr.* = sectHeaderTo32(slice.items(.shdr)[i]);
1248 log.debug("writing section {s}: {}", .{ self.getString(shdr.sh_name), shdr.* });1198 log.debug("writing section {?s}: {}", .{ self.shstrtab.get(shdr.sh_name), shdr.* });
1249 if (foreign_endian) {1199 if (foreign_endian) {
1250 mem.byteSwapAllFields(elf.Elf32_Shdr, shdr);1200 mem.byteSwapAllFields(elf.Elf32_Shdr, shdr);
1251 }1201 }
...@@ -1253,12 +1203,13 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node...@@ -1253,12 +1203,13 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
1253 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);1203 try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), self.shdr_table_offset.?);
1254 },1204 },
1255 .p64 => {1205 .p64 => {
1256 const buf = try self.base.allocator.alloc(elf.Elf64_Shdr, self.sections.items.len);1206 const slice = self.sections.slice();
1257 defer self.base.allocator.free(buf);1207 const buf = try gpa.alloc(elf.Elf64_Shdr, slice.len);
1208 defer gpa.free(buf);
12581209
1259 for (buf) |*shdr, i| {1210 for (buf) |*shdr, i| {
1260 shdr.* = self.sections.items[i];1211 shdr.* = slice.items(.shdr)[i];
1261 log.debug("writing section {s}: {}", .{ self.getString(shdr.sh_name), shdr.* });1212 log.debug("writing section {?s}: {}", .{ self.shstrtab.get(shdr.sh_name), shdr.* });
1262 if (foreign_endian) {1213 if (foreign_endian) {
1263 mem.byteSwapAllFields(elf.Elf64_Shdr, shdr);1214 mem.byteSwapAllFields(elf.Elf64_Shdr, shdr);
1264 }1215 }
...@@ -2069,7 +2020,7 @@ fn writeElfHeader(self: *Elf) !void {...@@ -2069,7 +2020,7 @@ fn writeElfHeader(self: *Elf) !void {
2069 mem.writeInt(u16, hdr_buf[index..][0..2], e_shentsize, endian);2020 mem.writeInt(u16, hdr_buf[index..][0..2], e_shentsize, endian);
2070 index += 2;2021 index += 2;
20712022
2072 const e_shnum = @intCast(u16, self.sections.items.len);2023 const e_shnum = @intCast(u16, self.sections.slice().len);
2073 mem.writeInt(u16, hdr_buf[index..][0..2], e_shnum, endian);2024 mem.writeInt(u16, hdr_buf[index..][0..2], e_shnum, endian);
2074 index += 2;2025 index += 2;
20752026
...@@ -2081,113 +2032,145 @@ fn writeElfHeader(self: *Elf) !void {...@@ -2081,113 +2032,145 @@ fn writeElfHeader(self: *Elf) !void {
2081 try self.base.file.?.pwriteAll(hdr_buf[0..index], 0);2032 try self.base.file.?.pwriteAll(hdr_buf[0..index], 0);
2082}2033}
20832034
2084fn freeTextBlock(self: *Elf, text_block: *TextBlock, phdr_index: u16) void {2035fn freeAtom(self: *Elf, atom_index: Atom.Index) void {
2085 const local_sym = self.local_symbols.items[text_block.local_sym_index];2036 const atom = self.getAtom(atom_index);
2086 const name_str_index = local_sym.st_name;2037 log.debug("freeAtom {d} ({s})", .{ atom_index, atom.getName(self) });
2087 const name = self.getString(name_str_index);
2088 log.debug("freeTextBlock {*} ({s})", .{ text_block, name });
20892038
2090 const free_list = self.atom_free_lists.getPtr(phdr_index).?;2039 Atom.freeRelocations(self, atom_index);
2040
2041 const gpa = self.base.allocator;
2042 const shndx = atom.getSymbol(self).st_shndx;
2043 const free_list = &self.sections.items(.free_list)[shndx];
2091 var already_have_free_list_node = false;2044 var already_have_free_list_node = false;
2092 {2045 {
2093 var i: usize = 0;2046 var i: usize = 0;
2094 // TODO turn free_list into a hash map2047 // TODO turn free_list into a hash map
2095 while (i < free_list.items.len) {2048 while (i < free_list.items.len) {
2096 if (free_list.items[i] == text_block) {2049 if (free_list.items[i] == atom_index) {
2097 _ = free_list.swapRemove(i);2050 _ = free_list.swapRemove(i);
2098 continue;2051 continue;
2099 }2052 }
2100 if (free_list.items[i] == text_block.prev) {2053 if (free_list.items[i] == atom.prev_index) {
2101 already_have_free_list_node = true;2054 already_have_free_list_node = true;
2102 }2055 }
2103 i += 1;2056 i += 1;
2104 }2057 }
2105 }2058 }
21062059
2107 if (self.atoms.getPtr(phdr_index)) |last_block| {2060 const maybe_last_atom_index = &self.sections.items(.last_atom_index)[shndx];
2108 if (last_block.* == text_block) {2061 if (maybe_last_atom_index.*) |last_atom_index| {
2109 if (text_block.prev) |prev| {2062 if (last_atom_index == atom_index) {
2063 if (atom.prev_index) |prev_index| {
2110 // TODO shrink the section size here2064 // TODO shrink the section size here
2111 last_block.* = prev;2065 maybe_last_atom_index.* = prev_index;
2112 } else {2066 } else {
2113 _ = self.atoms.fetchRemove(phdr_index);2067 maybe_last_atom_index.* = null;
2114 }2068 }
2115 }2069 }
2116 }2070 }
21172071
2118 if (text_block.prev) |prev| {2072 if (atom.prev_index) |prev_index| {
2119 prev.next = text_block.next;2073 const prev = self.getAtomPtr(prev_index);
2074 prev.next_index = atom.next_index;
21202075
2121 if (!already_have_free_list_node and prev.freeListEligible(self.*)) {2076 if (!already_have_free_list_node and prev.*.freeListEligible(self)) {
2122 // The free list is heuristics, it doesn't have to be perfect, so we can2077 // The free list is heuristics, it doesn't have to be perfect, so we can
2123 // ignore the OOM here.2078 // ignore the OOM here.
2124 free_list.append(self.base.allocator, prev) catch {};2079 free_list.append(gpa, prev_index) catch {};
2125 }2080 }
2126 } else {2081 } else {
2127 text_block.prev = null;2082 self.getAtomPtr(atom_index).prev_index = null;
2128 }2083 }
21292084
2130 if (text_block.next) |next| {2085 if (atom.next_index) |next_index| {
2131 next.prev = text_block.prev;2086 self.getAtomPtr(next_index).prev_index = atom.prev_index;
2132 } else {2087 } else {
2133 text_block.next = null;2088 self.getAtomPtr(atom_index).next_index = null;
2134 }2089 }
21352090
2136 if (self.dwarf) |*dw| {2091 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
2137 dw.freeAtom(&text_block.dbg_info_atom);2092 const local_sym_index = atom.getSymbolIndex().?;
2138 }2093
2094 self.local_symbol_free_list.append(gpa, local_sym_index) catch {};
2095 self.local_symbols.items[local_sym_index].st_info = 0;
2096 self.local_symbols.items[local_sym_index].st_shndx = 0;
2097 _ = self.atom_by_index_table.remove(local_sym_index);
2098 self.getAtomPtr(atom_index).local_sym_index = 0;
2099
2100 self.offset_table_free_list.append(self.base.allocator, atom.offset_table_index) catch {};
2139}2101}
21402102
2141fn shrinkTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, phdr_index: u16) void {2103fn shrinkAtom(self: *Elf, atom_index: Atom.Index, new_block_size: u64) void {
2142 _ = self;2104 _ = self;
2143 _ = text_block;2105 _ = atom_index;
2144 _ = new_block_size;2106 _ = new_block_size;
2145 _ = phdr_index;
2146}2107}
21472108
2148fn growTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, alignment: u64, phdr_index: u16) !u64 {2109fn growAtom(self: *Elf, atom_index: Atom.Index, new_block_size: u64, alignment: u64) !u64 {
2149 const sym = self.local_symbols.items[text_block.local_sym_index];2110 const atom = self.getAtom(atom_index);
2111 const sym = atom.getSymbol(self);
2150 const align_ok = mem.alignBackwardGeneric(u64, sym.st_value, alignment) == sym.st_value;2112 const align_ok = mem.alignBackwardGeneric(u64, sym.st_value, alignment) == sym.st_value;
2151 const need_realloc = !align_ok or new_block_size > text_block.capacity(self.*);2113 const need_realloc = !align_ok or new_block_size > atom.capacity(self);
2152 if (!need_realloc) return sym.st_value;2114 if (!need_realloc) return sym.st_value;
2153 return self.allocateTextBlock(text_block, new_block_size, alignment, phdr_index);2115 return self.allocateAtom(atom_index, new_block_size, alignment);
2154}2116}
21552117
2156fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, alignment: u64, phdr_index: u16) !u64 {2118pub fn createAtom(self: *Elf) !Atom.Index {
2157 const shdr_index = self.phdr_shdr_table.get(phdr_index).?;2119 const gpa = self.base.allocator;
2120 const atom_index = @intCast(Atom.Index, self.atoms.items.len);
2121 const atom = try self.atoms.addOne(gpa);
2122 const local_sym_index = try self.allocateLocalSymbol();
2123 const offset_table_index = try self.allocateGotOffset();
2124 try self.atom_by_index_table.putNoClobber(gpa, local_sym_index, atom_index);
2125 atom.* = .{
2126 .local_sym_index = local_sym_index,
2127 .offset_table_index = offset_table_index,
2128 .prev_index = null,
2129 .next_index = null,
2130 };
2131 log.debug("creating ATOM(%{d}) at index {d}", .{ local_sym_index, atom_index });
2132 return atom_index;
2133}
2134
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];
2158 const phdr = &self.program_headers.items[phdr_index];2139 const phdr = &self.program_headers.items[phdr_index];
2159 const shdr = &self.sections.items[shdr_index];2140 const shdr = &self.sections.items(.shdr)[sym.st_shndx];
2160 const new_block_ideal_capacity = padToIdeal(new_block_size);2141 const free_list = &self.sections.items(.free_list)[sym.st_shndx];
2142 const maybe_last_atom_index = &self.sections.items(.last_atom_index)[sym.st_shndx];
2143 const new_atom_ideal_capacity = padToIdeal(new_block_size);
21612144
2162 // We use these to indicate our intention to update metadata, placing the new block,2145 // We use these to indicate our intention to update metadata, placing the new atom,
2163 // and possibly removing a free list node.2146 // and possibly removing a free list node.
2164 // It would be simpler to do it inside the for loop below, but that would cause a2147 // It would be simpler to do it inside the for loop below, but that would cause a
2165 // problem if an error was returned later in the function. So this action2148 // problem if an error was returned later in the function. So this action
2166 // is actually carried out at the end of the function, when errors are no longer possible.2149 // is actually carried out at the end of the function, when errors are no longer possible.
2167 var block_placement: ?*TextBlock = null;2150 var atom_placement: ?Atom.Index = null;
2168 var free_list_removal: ?usize = null;2151 var free_list_removal: ?usize = null;
2169 var free_list = self.atom_free_lists.get(phdr_index).?;
21702152
2171 // First we look for an appropriately sized free list node.2153 // First we look for an appropriately sized free list node.
2172 // The list is unordered. We'll just take the first thing that works.2154 // The list is unordered. We'll just take the first thing that works.
2173 const vaddr = blk: {2155 const vaddr = blk: {
2174 var i: usize = 0;2156 var i: usize = 0;
2175 while (i < free_list.items.len) {2157 while (i < free_list.items.len) {
2176 const big_block = free_list.items[i];2158 const big_atom_index = free_list.items[i];
2177 // We now have a pointer to a live text block that has too much capacity.2159 const big_atom = self.getAtom(big_atom_index);
2178 // Is it enough that we could fit this new text block?2160 // We now have a pointer to a live atom that has too much capacity.
2179 const sym = self.local_symbols.items[big_block.local_sym_index];2161 // Is it enough that we could fit this new atom?
2180 const capacity = big_block.capacity(self.*);2162 const big_atom_sym = big_atom.getSymbol(self);
2163 const capacity = big_atom.capacity(self);
2181 const ideal_capacity = padToIdeal(capacity);2164 const ideal_capacity = padToIdeal(capacity);
2182 const ideal_capacity_end_vaddr = std.math.add(u64, sym.st_value, ideal_capacity) catch ideal_capacity;2165 const ideal_capacity_end_vaddr = std.math.add(u64, big_atom_sym.st_value, ideal_capacity) catch ideal_capacity;
2183 const capacity_end_vaddr = sym.st_value + capacity;2166 const capacity_end_vaddr = big_atom_sym.st_value + capacity;
2184 const new_start_vaddr_unaligned = capacity_end_vaddr - new_block_ideal_capacity;2167 const new_start_vaddr_unaligned = capacity_end_vaddr - new_atom_ideal_capacity;
2185 const new_start_vaddr = mem.alignBackwardGeneric(u64, new_start_vaddr_unaligned, alignment);2168 const new_start_vaddr = mem.alignBackwardGeneric(u64, new_start_vaddr_unaligned, alignment);
2186 if (new_start_vaddr < ideal_capacity_end_vaddr) {2169 if (new_start_vaddr < ideal_capacity_end_vaddr) {
2187 // Additional bookkeeping here to notice if this free list node2170 // Additional bookkeeping here to notice if this free list node
2188 // should be deleted because the block that it points to has grown to take up2171 // should be deleted because the block that it points to has grown to take up
2189 // more of the extra capacity.2172 // more of the extra capacity.
2190 if (!big_block.freeListEligible(self.*)) {2173 if (!big_atom.freeListEligible(self)) {
2191 _ = free_list.swapRemove(i);2174 _ = free_list.swapRemove(i);
2192 } else {2175 } else {
2193 i += 1;2176 i += 1;
...@@ -2201,29 +2184,33 @@ fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, al...@@ -2201,29 +2184,33 @@ fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, al
2201 const keep_free_list_node = remaining_capacity >= min_text_capacity;2184 const keep_free_list_node = remaining_capacity >= min_text_capacity;
22022185
2203 // Set up the metadata to be updated, after errors are no longer possible.2186 // Set up the metadata to be updated, after errors are no longer possible.
2204 block_placement = big_block;2187 atom_placement = big_atom_index;
2205 if (!keep_free_list_node) {2188 if (!keep_free_list_node) {
2206 free_list_removal = i;2189 free_list_removal = i;
2207 }2190 }
2208 break :blk new_start_vaddr;2191 break :blk new_start_vaddr;
2209 } else if (self.atoms.get(phdr_index)) |last| {2192 } else if (maybe_last_atom_index.*) |last_index| {
2210 const sym = self.local_symbols.items[last.local_sym_index];2193 const last = self.getAtom(last_index);
2211 const ideal_capacity = padToIdeal(sym.st_size);2194 const last_sym = last.getSymbol(self);
2212 const ideal_capacity_end_vaddr = sym.st_value + ideal_capacity;2195 const ideal_capacity = padToIdeal(last_sym.st_size);
2196 const ideal_capacity_end_vaddr = last_sym.st_value + ideal_capacity;
2213 const new_start_vaddr = mem.alignForwardGeneric(u64, ideal_capacity_end_vaddr, alignment);2197 const new_start_vaddr = mem.alignForwardGeneric(u64, ideal_capacity_end_vaddr, alignment);
2214 // Set up the metadata to be updated, after errors are no longer possible.2198 // Set up the metadata to be updated, after errors are no longer possible.
2215 block_placement = last;2199 atom_placement = last_index;
2216 break :blk new_start_vaddr;2200 break :blk new_start_vaddr;
2217 } else {2201 } else {
2218 break :blk phdr.p_vaddr;2202 break :blk phdr.p_vaddr;
2219 }2203 }
2220 };2204 };
22212205
2222 const expand_text_section = block_placement == null or block_placement.?.next == null;2206 const expand_section = if (atom_placement) |placement_index|
2223 if (expand_text_section) {2207 self.getAtom(placement_index).next_index == null
2208 else
2209 true;
2210 if (expand_section) {
2224 const needed_size = (vaddr + new_block_size) - phdr.p_vaddr;2211 const needed_size = (vaddr + new_block_size) - phdr.p_vaddr;
2225 try self.growAllocSection(shdr_index, phdr_index, needed_size);2212 try self.growAllocSection(sym.st_shndx, needed_size);
2226 _ = try self.atoms.put(self.base.allocator, phdr_index, text_block);2213 maybe_last_atom_index.* = atom_index;
22272214
2228 if (self.dwarf) |_| {2215 if (self.dwarf) |_| {
2229 // The .debug_info section has `low_pc` and `high_pc` values which is the virtual address2216 // The .debug_info section has `low_pc` and `high_pc` values which is the virtual address
...@@ -2238,23 +2225,28 @@ fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, al...@@ -2238,23 +2225,28 @@ fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, al
2238 }2225 }
2239 shdr.sh_addralign = math.max(shdr.sh_addralign, alignment);2226 shdr.sh_addralign = math.max(shdr.sh_addralign, alignment);
22402227
2241 // This function can also reallocate a text block.2228 // This function can also reallocate an atom.
2242 // In this case we need to "unplug" it from its previous location before2229 // In this case we need to "unplug" it from its previous location before
2243 // plugging it in to its new location.2230 // plugging it in to its new location.
2244 if (text_block.prev) |prev| {2231 if (atom.prev_index) |prev_index| {
2245 prev.next = text_block.next;2232 const prev = self.getAtomPtr(prev_index);
2233 prev.next_index = atom.next_index;
2246 }2234 }
2247 if (text_block.next) |next| {2235 if (atom.next_index) |next_index| {
2248 next.prev = text_block.prev;2236 const next = self.getAtomPtr(next_index);
2237 next.prev_index = atom.prev_index;
2249 }2238 }
22502239
2251 if (block_placement) |big_block| {2240 if (atom_placement) |big_atom_index| {
2252 text_block.prev = big_block;2241 const big_atom = self.getAtomPtr(big_atom_index);
2253 text_block.next = big_block.next;2242 const atom_ptr = self.getAtomPtr(atom_index);
2254 big_block.next = text_block;2243 atom_ptr.prev_index = big_atom_index;
2244 atom_ptr.next_index = big_atom.next_index;
2245 big_atom.next_index = atom_index;
2255 } else {2246 } else {
2256 text_block.prev = null;2247 const atom_ptr = self.getAtomPtr(atom_index);
2257 text_block.next = null;2248 atom_ptr.prev_index = null;
2249 atom_ptr.next_index = null;
2258 }2250 }
2259 if (free_list_removal) |i| {2251 if (free_list_removal) |i| {
2260 _ = free_list.swapRemove(i);2252 _ = free_list.swapRemove(i);
...@@ -2262,7 +2254,7 @@ fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, al...@@ -2262,7 +2254,7 @@ fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, al
2262 return vaddr;2254 return vaddr;
2263}2255}
22642256
2265fn allocateLocalSymbol(self: *Elf) !u32 {2257pub fn allocateLocalSymbol(self: *Elf) !u32 {
2266 try self.local_symbols.ensureUnusedCapacity(self.base.allocator, 1);2258 try self.local_symbols.ensureUnusedCapacity(self.base.allocator, 1);
22672259
2268 const index = blk: {2260 const index = blk: {
...@@ -2289,40 +2281,30 @@ fn allocateLocalSymbol(self: *Elf) !u32 {...@@ -2289,40 +2281,30 @@ fn allocateLocalSymbol(self: *Elf) !u32 {
2289 return index;2281 return index;
2290}2282}
22912283
2292pub fn allocateDeclIndexes(self: *Elf, decl_index: Module.Decl.Index) !void {2284pub fn allocateGotOffset(self: *Elf) !u32 {
2293 if (self.llvm_object) |_| return;
2294
2295 const mod = self.base.options.module.?;
2296 const decl = mod.declPtr(decl_index);
2297 if (decl.link.elf.local_sym_index != 0) return;
2298
2299 try self.offset_table.ensureUnusedCapacity(self.base.allocator, 1);2285 try self.offset_table.ensureUnusedCapacity(self.base.allocator, 1);
2300 try self.decls.putNoClobber(self.base.allocator, decl_index, null);
23012286
2302 const decl_name = try decl.getFullyQualifiedName(mod);2287 const index = blk: {
2303 defer self.base.allocator.free(decl_name);2288 if (self.offset_table_free_list.popOrNull()) |index| {
23042289 log.debug(" (reusing GOT offset at index {d})", .{index});
2305 log.debug("allocating symbol indexes for {s}", .{decl_name});2290 break :blk index;
2306 decl.link.elf.local_sym_index = try self.allocateLocalSymbol();2291 } else {
2307 try self.atom_by_index_table.putNoClobber(self.base.allocator, decl.link.elf.local_sym_index, &decl.link.elf);2292 log.debug(" (allocating GOT offset at index {d})", .{self.offset_table.items.len});
2293 const index = @intCast(u32, self.offset_table.items.len);
2294 _ = self.offset_table.addOneAssumeCapacity();
2295 self.offset_table_count_dirty = true;
2296 break :blk index;
2297 }
2298 };
23082299
2309 if (self.offset_table_free_list.popOrNull()) |i| {2300 self.offset_table.items[index] = 0;
2310 decl.link.elf.offset_table_index = i;2301 return index;
2311 } else {
2312 decl.link.elf.offset_table_index = @intCast(u32, self.offset_table.items.len);
2313 _ = self.offset_table.addOneAssumeCapacity();
2314 self.offset_table_count_dirty = true;
2315 }
2316 self.offset_table.items[decl.link.elf.offset_table_index] = 0;
2317}2302}
23182303
2319fn freeUnnamedConsts(self: *Elf, decl_index: Module.Decl.Index) void {2304fn freeUnnamedConsts(self: *Elf, decl_index: Module.Decl.Index) void {
2320 const unnamed_consts = self.unnamed_const_atoms.getPtr(decl_index) orelse return;2305 const unnamed_consts = self.unnamed_const_atoms.getPtr(decl_index) orelse return;
2321 for (unnamed_consts.items) |atom| {2306 for (unnamed_consts.items) |atom| {
2322 self.freeTextBlock(atom, self.phdr_load_ro_index.?);2307 self.freeAtom(atom);
2323 self.local_symbol_free_list.append(self.base.allocator, atom.local_sym_index) catch {};
2324 self.local_symbols.items[atom.local_sym_index].st_info = 0;
2325 _ = self.atom_by_index_table.remove(atom.local_sym_index);
2326 }2308 }
2327 unnamed_consts.clearAndFree(self.base.allocator);2309 unnamed_consts.clearAndFree(self.base.allocator);
2328}2310}
...@@ -2335,52 +2317,59 @@ pub fn freeDecl(self: *Elf, decl_index: Module.Decl.Index) void {...@@ -2335,52 +2317,59 @@ pub fn freeDecl(self: *Elf, decl_index: Module.Decl.Index) void {
2335 const mod = self.base.options.module.?;2317 const mod = self.base.options.module.?;
2336 const decl = mod.declPtr(decl_index);2318 const decl = mod.declPtr(decl_index);
23372319
2338 const kv = self.decls.fetchRemove(decl_index);2320 log.debug("freeDecl {*}", .{decl});
2339 if (kv.?.value) |index| {2321
2340 self.freeTextBlock(&decl.link.elf, index);2322 if (self.decls.fetchRemove(decl_index)) |const_kv| {
2323 var kv = const_kv;
2324 self.freeAtom(kv.value.atom);
2341 self.freeUnnamedConsts(decl_index);2325 self.freeUnnamedConsts(decl_index);
2326 kv.value.exports.deinit(self.base.allocator);
2342 }2327 }
23432328
2344 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.2329 if (self.dwarf) |*dw| {
2345 if (decl.link.elf.local_sym_index != 0) {2330 dw.freeDecl(decl_index);
2346 self.local_symbol_free_list.append(self.base.allocator, decl.link.elf.local_sym_index) catch {};
2347 self.local_symbols.items[decl.link.elf.local_sym_index].st_info = 0;
2348 _ = self.atom_by_index_table.remove(decl.link.elf.local_sym_index);
2349 decl.link.elf.local_sym_index = 0;
2350
2351 self.offset_table_free_list.append(self.base.allocator, decl.link.elf.offset_table_index) catch {};
2352 }2331 }
2332}
23532333
2354 if (self.dwarf) |*dw| {2334pub fn getOrCreateAtomForDecl(self: *Elf, decl_index: Module.Decl.Index) !Atom.Index {
2355 dw.freeDecl(decl);2335 const gop = try self.decls.getOrPut(self.base.allocator, decl_index);
2336 if (!gop.found_existing) {
2337 gop.value_ptr.* = .{
2338 .atom = try self.createAtom(),
2339 .shdr = self.getDeclShdrIndex(decl_index),
2340 .exports = .{},
2341 };
2356 }2342 }
2343 return gop.value_ptr.atom;
2357}2344}
23582345
2359fn getDeclPhdrIndex(self: *Elf, decl: *Module.Decl) !u16 {2346fn getDeclShdrIndex(self: *Elf, decl_index: Module.Decl.Index) u16 {
2347 const decl = self.base.options.module.?.declPtr(decl_index);
2360 const ty = decl.ty;2348 const ty = decl.ty;
2361 const zig_ty = ty.zigTypeTag();2349 const zig_ty = ty.zigTypeTag();
2362 const val = decl.val;2350 const val = decl.val;
2363 const phdr_index: u16 = blk: {2351 const shdr_index: u16 = blk: {
2364 if (val.isUndefDeep()) {2352 if (val.isUndefDeep()) {
2365 // TODO in release-fast and release-small, we should put undef in .bss2353 // TODO in release-fast and release-small, we should put undef in .bss
2366 break :blk self.phdr_load_rw_index.?;2354 break :blk self.data_section_index.?;
2367 }2355 }
23682356
2369 switch (zig_ty) {2357 switch (zig_ty) {
2370 // TODO: what if this is a function pointer?2358 // TODO: what if this is a function pointer?
2371 .Fn => break :blk self.phdr_load_re_index.?,2359 .Fn => break :blk self.text_section_index.?,
2372 else => {2360 else => {
2373 if (val.castTag(.variable)) |_| {2361 if (val.castTag(.variable)) |_| {
2374 break :blk self.phdr_load_rw_index.?;2362 break :blk self.data_section_index.?;
2375 }2363 }
2376 break :blk self.phdr_load_ro_index.?;2364 break :blk self.rodata_section_index.?;
2377 },2365 },
2378 }2366 }
2379 };2367 };
2380 return phdr_index;2368 return shdr_index;
2381}2369}
23822370
2383fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, stt_bits: u8) !*elf.Elf64_Sym {2371fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, stt_bits: u8) !*elf.Elf64_Sym {
2372 const gpa = self.base.allocator;
2384 const mod = self.base.options.module.?;2373 const mod = self.base.options.module.?;
2385 const decl = mod.declPtr(decl_index);2374 const decl = mod.declPtr(decl_index);
23862375
...@@ -2390,61 +2379,65 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s...@@ -2390,61 +2379,65 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s
2390 log.debug("updateDeclCode {s}{*}", .{ decl_name, decl });2379 log.debug("updateDeclCode {s}{*}", .{ decl_name, decl });
2391 const required_alignment = decl.getAlignment(self.base.options.target);2380 const required_alignment = decl.getAlignment(self.base.options.target);
23922381
2393 const decl_ptr = self.decls.getPtr(decl_index).?;2382 const decl_metadata = self.decls.get(decl_index).?;
2394 if (decl_ptr.* == null) {2383 const atom_index = decl_metadata.atom;
2395 decl_ptr.* = try self.getDeclPhdrIndex(decl);2384 const atom = self.getAtom(atom_index);
2396 }
2397 const phdr_index = decl_ptr.*.?;
2398 const shdr_index = self.phdr_shdr_table.get(phdr_index).?;
23992385
2400 assert(decl.link.elf.local_sym_index != 0); // Caller forgot to allocateDeclIndexes()2386 const shdr_index = decl_metadata.shdr;
2401 const local_sym = &self.local_symbols.items[decl.link.elf.local_sym_index];2387 if (atom.getSymbol(self).st_size != 0) {
2402 if (local_sym.st_size != 0) {2388 const local_sym = atom.getSymbolPtr(self);
2403 const capacity = decl.link.elf.capacity(self.*);2389 local_sym.st_name = try self.shstrtab.insert(gpa, decl_name);
2390 local_sym.st_info = (elf.STB_LOCAL << 4) | stt_bits;
2391 local_sym.st_other = 0;
2392 local_sym.st_shndx = shdr_index;
2393
2394 const capacity = atom.capacity(self);
2404 const need_realloc = code.len > capacity or2395 const need_realloc = code.len > capacity or
2405 !mem.isAlignedGeneric(u64, local_sym.st_value, required_alignment);2396 !mem.isAlignedGeneric(u64, local_sym.st_value, required_alignment);
2397
2406 if (need_realloc) {2398 if (need_realloc) {
2407 const vaddr = try self.growTextBlock(&decl.link.elf, code.len, required_alignment, phdr_index);2399 const vaddr = try self.growAtom(atom_index, code.len, required_alignment);
2408 log.debug("growing {s} from 0x{x} to 0x{x}", .{ decl_name, local_sym.st_value, vaddr });2400 log.debug("growing {s} from 0x{x} to 0x{x}", .{ decl_name, local_sym.st_value, vaddr });
2409 if (vaddr != local_sym.st_value) {2401 if (vaddr != local_sym.st_value) {
2410 local_sym.st_value = vaddr;2402 local_sym.st_value = vaddr;
24112403
2412 log.debug(" (writing new offset table entry)", .{});2404 log.debug(" (writing new offset table entry)", .{});
2413 self.offset_table.items[decl.link.elf.offset_table_index] = vaddr;2405 self.offset_table.items[atom.offset_table_index] = vaddr;
2414 try self.writeOffsetTableEntry(decl.link.elf.offset_table_index);2406 try self.writeOffsetTableEntry(atom.offset_table_index);
2415 }2407 }
2416 } else if (code.len < local_sym.st_size) {2408 } else if (code.len < local_sym.st_size) {
2417 self.shrinkTextBlock(&decl.link.elf, code.len, phdr_index);2409 self.shrinkAtom(atom_index, code.len);
2418 }2410 }
2419 local_sym.st_size = code.len;2411 local_sym.st_size = code.len;
2420 local_sym.st_name = try self.updateString(local_sym.st_name, decl_name);2412
2421 local_sym.st_info = (elf.STB_LOCAL << 4) | stt_bits;
2422 local_sym.st_other = 0;
2423 local_sym.st_shndx = shdr_index;
2424 // TODO this write could be avoided if no fields of the symbol were changed.2413 // TODO this write could be avoided if no fields of the symbol were changed.
2425 try self.writeSymbol(decl.link.elf.local_sym_index);2414 try self.writeSymbol(atom.getSymbolIndex().?);
2426 } else {2415 } else {
2427 const name_str_index = try self.makeString(decl_name);2416 const local_sym = atom.getSymbolPtr(self);
2428 const vaddr = try self.allocateTextBlock(&decl.link.elf, code.len, required_alignment, phdr_index);
2429 errdefer self.freeTextBlock(&decl.link.elf, phdr_index);
2430 log.debug("allocated text block for {s} at 0x{x}", .{ decl_name, vaddr });
2431
2432 local_sym.* = .{2417 local_sym.* = .{
2433 .st_name = name_str_index,2418 .st_name = try self.shstrtab.insert(gpa, decl_name),
2434 .st_info = (elf.STB_LOCAL << 4) | stt_bits,2419 .st_info = (elf.STB_LOCAL << 4) | stt_bits,
2435 .st_other = 0,2420 .st_other = 0,
2436 .st_shndx = shdr_index,2421 .st_shndx = shdr_index,
2437 .st_value = vaddr,2422 .st_value = 0,
2438 .st_size = code.len,2423 .st_size = 0,
2439 };2424 };
2440 self.offset_table.items[decl.link.elf.offset_table_index] = vaddr;2425 const vaddr = try self.allocateAtom(atom_index, code.len, required_alignment);
2426 errdefer self.freeAtom(atom_index);
2427 log.debug("allocated text block for {s} at 0x{x}", .{ decl_name, vaddr });
2428
2429 self.offset_table.items[atom.offset_table_index] = vaddr;
2430 local_sym.st_value = vaddr;
2431 local_sym.st_size = code.len;
24412432
2442 try self.writeSymbol(decl.link.elf.local_sym_index);2433 try self.writeSymbol(atom.getSymbolIndex().?);
2443 try self.writeOffsetTableEntry(decl.link.elf.offset_table_index);2434 try self.writeOffsetTableEntry(atom.offset_table_index);
2444 }2435 }
24452436
2437 const local_sym = atom.getSymbolPtr(self);
2438 const phdr_index = self.sections.items(.phdr_index)[shdr_index];
2446 const section_offset = local_sym.st_value - self.program_headers.items[phdr_index].p_vaddr;2439 const section_offset = local_sym.st_value - self.program_headers.items[phdr_index].p_vaddr;
2447 const file_offset = self.sections.items[shdr_index].sh_offset + section_offset;2440 const file_offset = self.sections.items(.shdr)[shdr_index].sh_offset + section_offset;
2448 try self.base.file.?.pwriteAll(code, file_offset);2441 try self.base.file.?.pwriteAll(code, file_offset);
24492442
2450 return local_sym;2443 return local_sym;
...@@ -2461,12 +2454,15 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven...@@ -2461,12 +2454,15 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven
2461 const tracy = trace(@src());2454 const tracy = trace(@src());
2462 defer tracy.end();2455 defer tracy.end();
24632456
2464 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
2465 defer code_buffer.deinit();
2466
2467 const decl_index = func.owner_decl;2457 const decl_index = func.owner_decl;
2468 const decl = module.declPtr(decl_index);2458 const decl = module.declPtr(decl_index);
2459
2460 const atom_index = try self.getOrCreateAtomForDecl(decl_index);
2469 self.freeUnnamedConsts(decl_index);2461 self.freeUnnamedConsts(decl_index);
2462 Atom.freeRelocations(self, atom_index);
2463
2464 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
2465 defer code_buffer.deinit();
24702466
2471 var decl_state: ?Dwarf.DeclState = if (self.dwarf) |*dw| try dw.initDeclState(module, decl_index) else null;2467 var decl_state: ?Dwarf.DeclState = if (self.dwarf) |*dw| try dw.initDeclState(module, decl_index) else null;
2472 defer if (decl_state) |*ds| ds.deinit();2468 defer if (decl_state) |*ds| ds.deinit();
...@@ -2479,7 +2475,7 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven...@@ -2479,7 +2475,7 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven
2479 try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .none);2475 try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .none);
24802476
2481 const code = switch (res) {2477 const code = switch (res) {
2482 .appended => code_buffer.items,2478 .ok => code_buffer.items,
2483 .fail => |em| {2479 .fail => |em| {
2484 decl.analysis = .codegen_failure;2480 decl.analysis = .codegen_failure;
2485 try module.failed_decls.put(module.gpa, decl_index, em);2481 try module.failed_decls.put(module.gpa, decl_index, em);
...@@ -2525,7 +2521,9 @@ pub fn updateDecl(self: *Elf, module: *Module, decl_index: Module.Decl.Index) !v...@@ -2525,7 +2521,9 @@ pub fn updateDecl(self: *Elf, module: *Module, decl_index: Module.Decl.Index) !v
2525 }2521 }
2526 }2522 }
25272523
2528 assert(!self.unnamed_const_atoms.contains(decl_index));2524 const atom_index = try self.getOrCreateAtomForDecl(decl_index);
2525 Atom.freeRelocations(self, atom_index);
2526 const atom = self.getAtom(atom_index);
25292527
2530 var code_buffer = std.ArrayList(u8).init(self.base.allocator);2528 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
2531 defer code_buffer.deinit();2529 defer code_buffer.deinit();
...@@ -2542,19 +2540,18 @@ pub fn updateDecl(self: *Elf, module: *Module, decl_index: Module.Decl.Index) !v...@@ -2542,19 +2540,18 @@ pub fn updateDecl(self: *Elf, module: *Module, decl_index: Module.Decl.Index) !v
2542 }, &code_buffer, .{2540 }, &code_buffer, .{
2543 .dwarf = ds,2541 .dwarf = ds,
2544 }, .{2542 }, .{
2545 .parent_atom_index = decl.link.elf.local_sym_index,2543 .parent_atom_index = atom.getSymbolIndex().?,
2546 })2544 })
2547 else2545 else
2548 try codegen.generateSymbol(&self.base, decl.srcLoc(), .{2546 try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
2549 .ty = decl.ty,2547 .ty = decl.ty,
2550 .val = decl_val,2548 .val = decl_val,
2551 }, &code_buffer, .none, .{2549 }, &code_buffer, .none, .{
2552 .parent_atom_index = decl.link.elf.local_sym_index,2550 .parent_atom_index = atom.getSymbolIndex().?,
2553 });2551 });
25542552
2555 const code = switch (res) {2553 const code = switch (res) {
2556 .externally_managed => |x| x,2554 .ok => code_buffer.items,
2557 .appended => code_buffer.items,
2558 .fail => |em| {2555 .fail => |em| {
2559 decl.analysis = .codegen_failure;2556 decl.analysis = .codegen_failure;
2560 try module.failed_decls.put(module.gpa, decl_index, em);2557 try module.failed_decls.put(module.gpa, decl_index, em);
...@@ -2579,47 +2576,38 @@ pub fn updateDecl(self: *Elf, module: *Module, decl_index: Module.Decl.Index) !v...@@ -2579,47 +2576,38 @@ pub fn updateDecl(self: *Elf, module: *Module, decl_index: Module.Decl.Index) !v
2579}2576}
25802577
2581pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module.Decl.Index) !u32 {2578pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module.Decl.Index) !u32 {
2582 var code_buffer = std.ArrayList(u8).init(self.base.allocator);2579 const gpa = self.base.allocator;
2580
2581 var code_buffer = std.ArrayList(u8).init(gpa);
2583 defer code_buffer.deinit();2582 defer code_buffer.deinit();
25842583
2585 const mod = self.base.options.module.?;2584 const mod = self.base.options.module.?;
2586 const decl = mod.declPtr(decl_index);2585 const gop = try self.unnamed_const_atoms.getOrPut(gpa, decl_index);
2587
2588 const gop = try self.unnamed_const_atoms.getOrPut(self.base.allocator, decl_index);
2589 if (!gop.found_existing) {2586 if (!gop.found_existing) {
2590 gop.value_ptr.* = .{};2587 gop.value_ptr.* = .{};
2591 }2588 }
2592 const unnamed_consts = gop.value_ptr;2589 const unnamed_consts = gop.value_ptr;
25932590
2594 const atom = try self.base.allocator.create(TextBlock);2591 const decl = mod.declPtr(decl_index);
2595 errdefer self.base.allocator.destroy(atom);
2596 atom.* = TextBlock.empty;
2597 try self.managed_atoms.append(self.base.allocator, atom);
2598
2599 const name_str_index = blk: {2592 const name_str_index = blk: {
2600 const decl_name = try decl.getFullyQualifiedName(mod);2593 const decl_name = try decl.getFullyQualifiedName(mod);
2601 defer self.base.allocator.free(decl_name);2594 defer gpa.free(decl_name);
2602
2603 const index = unnamed_consts.items.len;2595 const index = unnamed_consts.items.len;
2604 const name = try std.fmt.allocPrint(self.base.allocator, "__unnamed_{s}_{d}", .{ decl_name, index });2596 const name = try std.fmt.allocPrint(gpa, "__unnamed_{s}_{d}", .{ decl_name, index });
2605 defer self.base.allocator.free(name);2597 defer gpa.free(name);
26062598 break :blk try self.shstrtab.insert(gpa, name);
2607 break :blk try self.makeString(name);
2608 };2599 };
2609 const name = self.getString(name_str_index);2600 const name = self.shstrtab.get(name_str_index).?;
26102601
2611 log.debug("allocating symbol indexes for {s}", .{name});2602 const atom_index = try self.createAtom();
2612 atom.local_sym_index = try self.allocateLocalSymbol();
2613 try self.atom_by_index_table.putNoClobber(self.base.allocator, atom.local_sym_index, atom);
26142603
2615 const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), typed_value, &code_buffer, .{2604 const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), typed_value, &code_buffer, .{
2616 .none = {},2605 .none = {},
2617 }, .{2606 }, .{
2618 .parent_atom_index = atom.local_sym_index,2607 .parent_atom_index = self.getAtom(atom_index).getSymbolIndex().?,
2619 });2608 });
2620 const code = switch (res) {2609 const code = switch (res) {
2621 .externally_managed => |x| x,2610 .ok => code_buffer.items,
2622 .appended => code_buffer.items,
2623 .fail => |em| {2611 .fail => |em| {
2624 decl.analysis = .codegen_failure;2612 decl.analysis = .codegen_failure;
2625 try mod.failed_decls.put(mod.gpa, decl_index, em);2613 try mod.failed_decls.put(mod.gpa, decl_index, em);
...@@ -2629,31 +2617,27 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module...@@ -2629,31 +2617,27 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module
2629 };2617 };
26302618
2631 const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);2619 const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);
2632 const phdr_index = self.phdr_load_ro_index.?;2620 const shdr_index = self.rodata_section_index.?;
2633 const shdr_index = self.phdr_shdr_table.get(phdr_index).?;2621 const phdr_index = self.sections.items(.phdr_index)[shdr_index];
2634 const vaddr = try self.allocateTextBlock(atom, code.len, required_alignment, phdr_index);2622 const local_sym = self.getAtom(atom_index).getSymbolPtr(self);
2635 errdefer self.freeTextBlock(atom, phdr_index);2623 local_sym.st_name = name_str_index;
26362624 local_sym.st_info = (elf.STB_LOCAL << 4) | elf.STT_OBJECT;
2637 log.debug("allocated text block for {s} at 0x{x}", .{ name, vaddr });2625 local_sym.st_other = 0;
26382626 local_sym.st_shndx = shdr_index;
2639 const local_sym = &self.local_symbols.items[atom.local_sym_index];2627 local_sym.st_size = code.len;
2640 local_sym.* = .{2628 local_sym.st_value = try self.allocateAtom(atom_index, code.len, required_alignment);
2641 .st_name = name_str_index,2629 errdefer self.freeAtom(atom_index);
2642 .st_info = (elf.STB_LOCAL << 4) | elf.STT_OBJECT,2630
2643 .st_other = 0,2631 log.debug("allocated text block for {s} at 0x{x}", .{ name, local_sym.st_value });
2644 .st_shndx = shdr_index,2632
2645 .st_value = vaddr,2633 try self.writeSymbol(self.getAtom(atom_index).getSymbolIndex().?);
2646 .st_size = code.len,2634 try unnamed_consts.append(gpa, atom_index);
2647 };
2648
2649 try self.writeSymbol(atom.local_sym_index);
2650 try unnamed_consts.append(self.base.allocator, atom);
26512635
2652 const section_offset = local_sym.st_value - self.program_headers.items[phdr_index].p_vaddr;2636 const section_offset = local_sym.st_value - self.program_headers.items[phdr_index].p_vaddr;
2653 const file_offset = self.sections.items[shdr_index].sh_offset + section_offset;2637 const file_offset = self.sections.items(.shdr)[shdr_index].sh_offset + section_offset;
2654 try self.base.file.?.pwriteAll(code, file_offset);2638 try self.base.file.?.pwriteAll(code, file_offset);
26552639
2656 return atom.local_sym_index;2640 return self.getAtom(atom_index).getSymbolIndex().?;
2657}2641}
26582642
2659pub fn updateDeclExports(2643pub fn updateDeclExports(
...@@ -2672,17 +2656,16 @@ pub fn updateDeclExports(...@@ -2672,17 +2656,16 @@ pub fn updateDeclExports(
2672 const tracy = trace(@src());2656 const tracy = trace(@src());
2673 defer tracy.end();2657 defer tracy.end();
26742658
2675 try self.global_symbols.ensureUnusedCapacity(self.base.allocator, exports.len);2659 const gpa = self.base.allocator;
2660
2676 const decl = module.declPtr(decl_index);2661 const decl = module.declPtr(decl_index);
2677 if (decl.link.elf.local_sym_index == 0) return;2662 const atom_index = try self.getOrCreateAtomForDecl(decl_index);
2678 const decl_sym = self.local_symbols.items[decl.link.elf.local_sym_index];2663 const atom = self.getAtom(atom_index);
2664 const decl_sym = atom.getSymbol(self);
2665 const decl_metadata = self.decls.getPtr(decl_index).?;
2666 const shdr_index = decl_metadata.shdr;
26792667
2680 const decl_ptr = self.decls.getPtr(decl_index).?;2668 try self.global_symbols.ensureUnusedCapacity(gpa, exports.len);
2681 if (decl_ptr.* == null) {
2682 decl_ptr.* = try self.getDeclPhdrIndex(decl);
2683 }
2684 const phdr_index = decl_ptr.*.?;
2685 const shdr_index = self.phdr_shdr_table.get(phdr_index).?;
26862669
2687 for (exports) |exp| {2670 for (exports) |exp| {
2688 if (exp.options.section) |section_name| {2671 if (exp.options.section) |section_name| {
...@@ -2715,10 +2698,10 @@ pub fn updateDeclExports(...@@ -2715,10 +2698,10 @@ pub fn updateDeclExports(
2715 },2698 },
2716 };2699 };
2717 const stt_bits: u8 = @truncate(u4, decl_sym.st_info);2700 const stt_bits: u8 = @truncate(u4, decl_sym.st_info);
2718 if (exp.link.elf.sym_index) |i| {2701 if (decl_metadata.getExport(self, exp.options.name)) |i| {
2719 const sym = &self.global_symbols.items[i];2702 const sym = &self.global_symbols.items[i];
2720 sym.* = .{2703 sym.* = .{
2721 .st_name = try self.updateString(sym.st_name, exp.options.name),2704 .st_name = try self.shstrtab.insert(gpa, exp.options.name),
2722 .st_info = (stb_bits << 4) | stt_bits,2705 .st_info = (stb_bits << 4) | stt_bits,
2723 .st_other = 0,2706 .st_other = 0,
2724 .st_shndx = shdr_index,2707 .st_shndx = shdr_index,
...@@ -2726,30 +2709,29 @@ pub fn updateDeclExports(...@@ -2726,30 +2709,29 @@ pub fn updateDeclExports(
2726 .st_size = decl_sym.st_size,2709 .st_size = decl_sym.st_size,
2727 };2710 };
2728 } else {2711 } else {
2729 const name = try self.makeString(exp.options.name);
2730 const i = if (self.global_symbol_free_list.popOrNull()) |i| i else blk: {2712 const i = if (self.global_symbol_free_list.popOrNull()) |i| i else blk: {
2731 _ = self.global_symbols.addOneAssumeCapacity();2713 _ = self.global_symbols.addOneAssumeCapacity();
2732 break :blk self.global_symbols.items.len - 1;2714 break :blk self.global_symbols.items.len - 1;
2733 };2715 };
2716 try decl_metadata.exports.append(gpa, @intCast(u32, i));
2734 self.global_symbols.items[i] = .{2717 self.global_symbols.items[i] = .{
2735 .st_name = name,2718 .st_name = try self.shstrtab.insert(gpa, exp.options.name),
2736 .st_info = (stb_bits << 4) | stt_bits,2719 .st_info = (stb_bits << 4) | stt_bits,
2737 .st_other = 0,2720 .st_other = 0,
2738 .st_shndx = shdr_index,2721 .st_shndx = shdr_index,
2739 .st_value = decl_sym.st_value,2722 .st_value = decl_sym.st_value,
2740 .st_size = decl_sym.st_size,2723 .st_size = decl_sym.st_size,
2741 };2724 };
2742
2743 exp.link.elf.sym_index = @intCast(u32, i);
2744 }2725 }
2745 }2726 }
2746}2727}
27472728
2748/// Must be called only after a successful call to `updateDecl`.2729/// Must be called only after a successful call to `updateDecl`.
2749pub fn updateDeclLineNumber(self: *Elf, mod: *Module, decl: *const Module.Decl) !void {2730pub fn updateDeclLineNumber(self: *Elf, mod: *Module, decl_index: Module.Decl.Index) !void {
2750 const tracy = trace(@src());2731 const tracy = trace(@src());
2751 defer tracy.end();2732 defer tracy.end();
27522733
2734 const decl = mod.declPtr(decl_index);
2753 const decl_name = try decl.getFullyQualifiedName(mod);2735 const decl_name = try decl.getFullyQualifiedName(mod);
2754 defer self.base.allocator.free(decl_name);2736 defer self.base.allocator.free(decl_name);
27552737
...@@ -2757,16 +2739,18 @@ pub fn updateDeclLineNumber(self: *Elf, mod: *Module, decl: *const Module.Decl)...@@ -2757,16 +2739,18 @@ pub fn updateDeclLineNumber(self: *Elf, mod: *Module, decl: *const Module.Decl)
27572739
2758 if (self.llvm_object) |_| return;2740 if (self.llvm_object) |_| return;
2759 if (self.dwarf) |*dw| {2741 if (self.dwarf) |*dw| {
2760 try dw.updateDeclLineNumber(decl);2742 try dw.updateDeclLineNumber(mod, decl_index);
2761 }2743 }
2762}2744}
27632745
2764pub fn deleteExport(self: *Elf, exp: Export) void {2746pub fn deleteDeclExport(self: *Elf, decl_index: Module.Decl.Index, name: []const u8) void {
2765 if (self.llvm_object) |_| return;2747 if (self.llvm_object) |_| return;
27662748 const metadata = self.decls.getPtr(decl_index) orelse return;
2767 const sym_index = exp.sym_index orelse return;2749 const sym_index = metadata.getExportPtr(self, name) orelse return;
2768 self.global_symbol_free_list.append(self.base.allocator, sym_index) catch {};2750 log.debug("deleting export '{s}'", .{name});
2769 self.global_symbols.items[sym_index].st_info = 0;2751 self.global_symbol_free_list.append(self.base.allocator, sym_index.*) catch {};
2752 self.global_symbols.items[sym_index.*].st_info = 0;
2753 sym_index.* = 0;
2770}2754}
27712755
2772fn writeProgHeader(self: *Elf, index: usize) !void {2756fn writeProgHeader(self: *Elf, index: usize) !void {
...@@ -2795,7 +2779,7 @@ fn writeSectHeader(self: *Elf, index: usize) !void {...@@ -2795,7 +2779,7 @@ fn writeSectHeader(self: *Elf, index: usize) !void {
2795 switch (self.ptr_width) {2779 switch (self.ptr_width) {
2796 .p32 => {2780 .p32 => {
2797 var shdr: [1]elf.Elf32_Shdr = undefined;2781 var shdr: [1]elf.Elf32_Shdr = undefined;
2798 shdr[0] = sectHeaderTo32(self.sections.items[index]);2782 shdr[0] = sectHeaderTo32(self.sections.items(.shdr)[index]);
2799 if (foreign_endian) {2783 if (foreign_endian) {
2800 mem.byteSwapAllFields(elf.Elf32_Shdr, &shdr[0]);2784 mem.byteSwapAllFields(elf.Elf32_Shdr, &shdr[0]);
2801 }2785 }
...@@ -2803,7 +2787,7 @@ fn writeSectHeader(self: *Elf, index: usize) !void {...@@ -2803,7 +2787,7 @@ fn writeSectHeader(self: *Elf, index: usize) !void {
2803 return self.base.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);2787 return self.base.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);
2804 },2788 },
2805 .p64 => {2789 .p64 => {
2806 var shdr = [1]elf.Elf64_Shdr{self.sections.items[index]};2790 var shdr = [1]elf.Elf64_Shdr{self.sections.items(.shdr)[index]};
2807 if (foreign_endian) {2791 if (foreign_endian) {
2808 mem.byteSwapAllFields(elf.Elf64_Shdr, &shdr[0]);2792 mem.byteSwapAllFields(elf.Elf64_Shdr, &shdr[0]);
2809 }2793 }
...@@ -2817,11 +2801,11 @@ fn writeOffsetTableEntry(self: *Elf, index: usize) !void {...@@ -2817,11 +2801,11 @@ fn writeOffsetTableEntry(self: *Elf, index: usize) !void {
2817 const entry_size: u16 = self.archPtrWidthBytes();2801 const entry_size: u16 = self.archPtrWidthBytes();
2818 if (self.offset_table_count_dirty) {2802 if (self.offset_table_count_dirty) {
2819 const needed_size = self.offset_table.items.len * entry_size;2803 const needed_size = self.offset_table.items.len * entry_size;
2820 try self.growAllocSection(self.got_section_index.?, self.phdr_got_index.?, needed_size);2804 try self.growAllocSection(self.got_section_index.?, needed_size);
2821 self.offset_table_count_dirty = false;2805 self.offset_table_count_dirty = false;
2822 }2806 }
2823 const endian = self.base.options.target.cpu.arch.endian();2807 const endian = self.base.options.target.cpu.arch.endian();
2824 const shdr = &self.sections.items[self.got_section_index.?];2808 const shdr = &self.sections.items(.shdr)[self.got_section_index.?];
2825 const off = shdr.sh_offset + @as(u64, entry_size) * index;2809 const off = shdr.sh_offset + @as(u64, entry_size) * index;
2826 switch (entry_size) {2810 switch (entry_size) {
2827 2 => {2811 2 => {
...@@ -2847,7 +2831,7 @@ fn writeSymbol(self: *Elf, index: usize) !void {...@@ -2847,7 +2831,7 @@ fn writeSymbol(self: *Elf, index: usize) !void {
2847 const tracy = trace(@src());2831 const tracy = trace(@src());
2848 defer tracy.end();2832 defer tracy.end();
28492833
2850 const syms_sect = &self.sections.items[self.symtab_section_index.?];2834 const syms_sect = &self.sections.items(.shdr)[self.symtab_section_index.?];
2851 // Make sure we are not pointlessly writing symbol data that will have to get relocated2835 // Make sure we are not pointlessly writing symbol data that will have to get relocated
2852 // due to running out of space.2836 // due to running out of space.
2853 if (self.local_symbols.items.len != syms_sect.sh_info) {2837 if (self.local_symbols.items.len != syms_sect.sh_info) {
...@@ -2869,7 +2853,7 @@ fn writeSymbol(self: *Elf, index: usize) !void {...@@ -2869,7 +2853,7 @@ fn writeSymbol(self: *Elf, index: usize) !void {
2869 .p64 => syms_sect.sh_offset + @sizeOf(elf.Elf64_Sym) * index,2853 .p64 => syms_sect.sh_offset + @sizeOf(elf.Elf64_Sym) * index,
2870 };2854 };
2871 const local = self.local_symbols.items[index];2855 const local = self.local_symbols.items[index];
2872 log.debug("writing symbol {d}, '{s}' at 0x{x}", .{ index, self.getString(local.st_name), off });2856 log.debug("writing symbol {d}, '{?s}' at 0x{x}", .{ index, self.shstrtab.get(local.st_name), off });
2873 log.debug(" ({})", .{local});2857 log.debug(" ({})", .{local});
2874 switch (self.ptr_width) {2858 switch (self.ptr_width) {
2875 .p32 => {2859 .p32 => {
...@@ -2899,7 +2883,7 @@ fn writeSymbol(self: *Elf, index: usize) !void {...@@ -2899,7 +2883,7 @@ fn writeSymbol(self: *Elf, index: usize) !void {
2899}2883}
29002884
2901fn writeAllGlobalSymbols(self: *Elf) !void {2885fn writeAllGlobalSymbols(self: *Elf) !void {
2902 const syms_sect = &self.sections.items[self.symtab_section_index.?];2886 const syms_sect = &self.sections.items(.shdr)[self.symtab_section_index.?];
2903 const sym_size: u64 = switch (self.ptr_width) {2887 const sym_size: u64 = switch (self.ptr_width) {
2904 .p32 => @sizeOf(elf.Elf32_Sym),2888 .p32 => @sizeOf(elf.Elf32_Sym),
2905 .p64 => @sizeOf(elf.Elf64_Sym),2889 .p64 => @sizeOf(elf.Elf64_Sym),
...@@ -3042,7 +3026,7 @@ fn getLDMOption(target: std.Target) ?[]const u8 {...@@ -3042,7 +3026,7 @@ fn getLDMOption(target: std.Target) ?[]const u8 {
3042 }3026 }
3043}3027}
30443028
3045fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) {3029pub fn padToIdeal(actual_size: anytype) @TypeOf(actual_size) {
3046 return actual_size +| (actual_size / ideal_factor);3030 return actual_size +| (actual_size / ideal_factor);
3047}3031}
30483032
...@@ -3249,10 +3233,58 @@ const CsuObjects = struct {...@@ -3249,10 +3233,58 @@ const CsuObjects = struct {
3249fn logSymtab(self: Elf) void {3233fn logSymtab(self: Elf) void {
3250 log.debug("locals:", .{});3234 log.debug("locals:", .{});
3251 for (self.local_symbols.items) |sym, id| {3235 for (self.local_symbols.items) |sym, id| {
3252 log.debug(" {d}: {s}: @{x} in {d}", .{ id, self.getString(sym.st_name), sym.st_value, sym.st_shndx });3236 log.debug(" {d}: {?s}: @{x} in {d}", .{ id, self.shstrtab.get(sym.st_name), sym.st_value, sym.st_shndx });
3253 }3237 }
3254 log.debug("globals:", .{});3238 log.debug("globals:", .{});
3255 for (self.global_symbols.items) |sym, id| {3239 for (self.global_symbols.items) |sym, id| {
3256 log.debug(" {d}: {s}: @{x} in {d}", .{ id, self.getString(sym.st_name), sym.st_value, sym.st_shndx });3240 log.debug(" {d}: {?s}: @{x} in {d}", .{ id, self.shstrtab.get(sym.st_name), sym.st_value, sym.st_shndx });
3257 }3241 }
3258}3242}
3243
3244pub fn getProgramHeader(self: *const Elf, shdr_index: u16) elf.Elf64_Phdr {
3245 const index = self.sections.items(.phdr_index)[shdr_index];
3246 return self.program_headers.items[index];
3247}
3248
3249pub fn getProgramHeaderPtr(self: *Elf, shdr_index: u16) *elf.Elf64_Phdr {
3250 const index = self.sections.items(.phdr_index)[shdr_index];
3251 return &self.program_headers.items[index];
3252}
3253
3254/// Returns pointer-to-symbol described at sym_index.
3255pub fn getSymbolPtr(self: *Elf, sym_index: u32) *elf.Elf64_Sym {
3256 return &self.local_symbols.items[sym_index];
3257}
3258
3259/// Returns symbol at sym_index.
3260pub fn getSymbol(self: *const Elf, sym_index: u32) elf.Elf64_Sym {
3261 return self.local_symbols.items[sym_index];
3262}
3263
3264/// Returns name of the symbol at sym_index.
3265pub fn getSymbolName(self: *const Elf, sym_index: u32) []const u8 {
3266 const sym = self.local_symbols.items[sym_index];
3267 return self.shstrtab.get(sym.st_name).?;
3268}
3269
3270/// Returns name of the global symbol at index.
3271pub fn getGlobalName(self: *const Elf, index: u32) []const u8 {
3272 const sym = self.global_symbols.items[index];
3273 return self.shstrtab.get(sym.st_name).?;
3274}
3275
3276pub fn getAtom(self: *const Elf, atom_index: Atom.Index) Atom {
3277 assert(atom_index < self.atoms.items.len);
3278 return self.atoms.items[atom_index];
3279}
3280
3281pub fn getAtomPtr(self: *Elf, atom_index: Atom.Index) *Atom {
3282 assert(atom_index < self.atoms.items.len);
3283 return &self.atoms.items[atom_index];
3284}
3285
3286/// Returns atom if there is an atom referenced by the symbol.
3287/// Returns null on failure.
3288pub fn getAtomIndexForSymbol(self: *Elf, sym_index: u32) ?Atom.Index {
3289 return self.atom_by_index_table.get(sym_index);
3290}
src/link/Elf/Atom.zig created+100
...@@ -0,0 +1,100 @@
1const Atom = @This();
2
3const std = @import("std");
4const assert = std.debug.assert;
5const elf = std.elf;
6
7const Elf = @import("../Elf.zig");
8
9/// Each decl always gets a local symbol with the fully qualified name.
10/// The vaddr and size are found here directly.
11/// The file offset is found by computing the vaddr offset from the section vaddr
12/// the symbol references, and adding that to the file offset of the section.
13/// If this field is 0, it means the codegen size = 0 and there is no symbol or
14/// offset table entry.
15local_sym_index: u32,
16
17/// This field is undefined for symbols with size = 0.
18offset_table_index: u32,
19
20/// Points to the previous and next neighbors, based on the `text_offset`.
21/// This can be used to find, for example, the capacity of this `TextBlock`.
22prev_index: ?Index,
23next_index: ?Index,
24
25pub const Index = u32;
26
27pub const Reloc = struct {
28 target: u32,
29 offset: u64,
30 addend: u32,
31 prev_vaddr: u64,
32};
33
34pub fn getSymbolIndex(self: Atom) ?u32 {
35 if (self.local_sym_index == 0) return null;
36 return self.local_sym_index;
37}
38
39pub fn getSymbol(self: Atom, elf_file: *const Elf) elf.Elf64_Sym {
40 return elf_file.getSymbol(self.getSymbolIndex().?);
41}
42
43pub fn getSymbolPtr(self: Atom, elf_file: *Elf) *elf.Elf64_Sym {
44 return elf_file.getSymbolPtr(self.getSymbolIndex().?);
45}
46
47pub fn getName(self: Atom, elf_file: *const Elf) []const u8 {
48 return elf_file.getSymbolName(self.getSymbolIndex().?);
49}
50
51pub fn getOffsetTableAddress(self: Atom, elf_file: *Elf) u64 {
52 assert(self.getSymbolIndex() != null);
53 const target = elf_file.base.options.target;
54 const ptr_bits = target.cpu.arch.ptrBitWidth();
55 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
56 const got = elf_file.program_headers.items[elf_file.phdr_got_index.?];
57 return got.p_vaddr + self.offset_table_index * ptr_bytes;
58}
59
60/// Returns how much room there is to grow in virtual address space.
61/// File offset relocation happens transparently, so it is not included in
62/// this calculation.
63pub fn capacity(self: Atom, elf_file: *const Elf) u64 {
64 const self_sym = self.getSymbol(elf_file);
65 if (self.next_index) |next_index| {
66 const next = elf_file.getAtom(next_index);
67 const next_sym = next.getSymbol(elf_file);
68 return next_sym.st_value - self_sym.st_value;
69 } else {
70 // We are the last block. The capacity is limited only by virtual address space.
71 return std.math.maxInt(u32) - self_sym.st_value;
72 }
73}
74
75pub fn freeListEligible(self: Atom, elf_file: *const Elf) bool {
76 // No need to keep a free list node for the last block.
77 const next_index = self.next_index orelse return false;
78 const next = elf_file.getAtom(next_index);
79 const self_sym = self.getSymbol(elf_file);
80 const next_sym = next.getSymbol(elf_file);
81 const cap = next_sym.st_value - self_sym.st_value;
82 const ideal_cap = Elf.padToIdeal(self_sym.st_size);
83 if (cap <= ideal_cap) return false;
84 const surplus = cap - ideal_cap;
85 return surplus >= Elf.min_text_capacity;
86}
87
88pub fn addRelocation(elf_file: *Elf, atom_index: Index, reloc: Reloc) !void {
89 const gpa = elf_file.base.allocator;
90 const gop = try elf_file.relocs.getOrPut(gpa, atom_index);
91 if (!gop.found_existing) {
92 gop.value_ptr.* = .{};
93 }
94 try gop.value_ptr.append(gpa, reloc);
95}
96
97pub fn freeRelocations(elf_file: *Elf, atom_index: Index) void {
98 var removed_relocs = elf_file.relocs.fetchRemove(atom_index);
99 if (removed_relocs) |*relocs| relocs.value.deinit(elf_file.base.allocator);
100}
src/link/MachO.zig+385-380
...@@ -66,7 +66,7 @@ const Section = struct {...@@ -66,7 +66,7 @@ const Section = struct {
6666
67 // TODO is null here necessary, or can we do away with tracking via section67 // TODO is null here necessary, or can we do away with tracking via section
68 // size in incremental context?68 // size in incremental context?
69 last_atom: ?*Atom = null,69 last_atom_index: ?Atom.Index = null,
7070
71 /// A list of atoms that have surplus capacity. This list can have false71 /// A list of atoms that have surplus capacity. This list can have false
72 /// positives, as functions grow and shrink over time, only sometimes being added72 /// positives, as functions grow and shrink over time, only sometimes being added
...@@ -83,7 +83,7 @@ const Section = struct {...@@ -83,7 +83,7 @@ const Section = struct {
83 /// overcapacity can be negative. A simple way to have negative overcapacity is to83 /// overcapacity can be negative. A simple way to have negative overcapacity is to
84 /// allocate a fresh atom, which will have ideal capacity, and then grow it84 /// allocate a fresh atom, which will have ideal capacity, and then grow it
85 /// by 1 byte. It will then have -1 overcapacity.85 /// by 1 byte. It will then have -1 overcapacity.
86 free_list: std.ArrayListUnmanaged(*Atom) = .{},86 free_list: std.ArrayListUnmanaged(Atom.Index) = .{},
87};87};
8888
89base: File,89base: File,
...@@ -140,8 +140,8 @@ locals_free_list: std.ArrayListUnmanaged(u32) = .{},...@@ -140,8 +140,8 @@ locals_free_list: std.ArrayListUnmanaged(u32) = .{},
140globals_free_list: std.ArrayListUnmanaged(u32) = .{},140globals_free_list: std.ArrayListUnmanaged(u32) = .{},
141141
142dyld_stub_binder_index: ?u32 = null,142dyld_stub_binder_index: ?u32 = null,
143dyld_private_atom: ?*Atom = null,143dyld_private_atom_index: ?Atom.Index = null,
144stub_helper_preamble_atom: ?*Atom = null,144stub_helper_preamble_atom_index: ?Atom.Index = null,
145145
146strtab: StringTable(.strtab) = .{},146strtab: StringTable(.strtab) = .{},
147147
...@@ -164,10 +164,10 @@ segment_table_dirty: bool = false,...@@ -164,10 +164,10 @@ segment_table_dirty: bool = false,
164cold_start: bool = true,164cold_start: bool = true,
165165
166/// List of atoms that are either synthetic or map directly to the Zig source program.166/// List of atoms that are either synthetic or map directly to the Zig source program.
167managed_atoms: std.ArrayListUnmanaged(*Atom) = .{},167atoms: std.ArrayListUnmanaged(Atom) = .{},
168168
169/// Table of atoms indexed by the symbol index.169/// 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
172/// Table of unnamed constants associated with a parent `Decl`.172/// Table of unnamed constants associated with a parent `Decl`.
173/// We store them here so that we can free the constants whenever the `Decl`173/// We store them here so that we can free the constants whenever the `Decl`
...@@ -210,11 +210,36 @@ bindings: BindingTable = .{},...@@ -210,11 +210,36 @@ bindings: BindingTable = .{},
210/// this will be a table indexed by index into the list of Atoms.210/// this will be a table indexed by index into the list of Atoms.
211lazy_bindings: BindingTable = .{},211lazy_bindings: BindingTable = .{},
212212
213/// Table of Decls that are currently alive.213/// Table of tracked Decls.
214/// We store them here so that we can properly dispose of any allocated214decls: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, DeclMetadata) = .{},
215/// memory within the atom in the incremental linker.215
216/// TODO consolidate this.216const DeclMetadata = struct {
217decls: std.AutoArrayHashMapUnmanaged(Module.Decl.Index, ?u8) = .{},217 atom: Atom.Index,
218 section: u8,
219 /// A list of all exports aliases of this Decl.
220 /// TODO do we actually need this at all?
221 exports: std.ArrayListUnmanaged(u32) = .{},
222
223 fn getExport(m: DeclMetadata, macho_file: *const MachO, name: []const u8) ?u32 {
224 for (m.exports.items) |exp| {
225 if (mem.eql(u8, name, macho_file.getSymbolName(.{
226 .sym_index = exp,
227 .file = null,
228 }))) return exp;
229 }
230 return null;
231 }
232
233 fn getExportPtr(m: *DeclMetadata, macho_file: *MachO, name: []const u8) ?*u32 {
234 for (m.exports.items) |*exp| {
235 if (mem.eql(u8, name, macho_file.getSymbolName(.{
236 .sym_index = exp.*,
237 .file = null,
238 }))) return exp;
239 }
240 return null;
241 }
242};
218243
219const Entry = struct {244const Entry = struct {
220 target: SymbolWithLoc,245 target: SymbolWithLoc,
...@@ -229,8 +254,8 @@ const Entry = struct {...@@ -229,8 +254,8 @@ const Entry = struct {
229 return macho_file.getSymbolPtr(.{ .sym_index = entry.sym_index, .file = null });254 return macho_file.getSymbolPtr(.{ .sym_index = entry.sym_index, .file = null });
230 }255 }
231256
232 pub fn getAtom(entry: Entry, macho_file: *MachO) ?*Atom {257 pub fn getAtomIndex(entry: Entry, macho_file: *MachO) ?Atom.Index {
233 return macho_file.getAtomForSymbol(.{ .sym_index = entry.sym_index, .file = null });258 return macho_file.getAtomIndexForSymbol(.{ .sym_index = entry.sym_index, .file = null });
234 }259 }
235260
236 pub fn getName(entry: Entry, macho_file: *MachO) []const u8 {261 pub fn getName(entry: Entry, macho_file: *MachO) []const u8 {
...@@ -238,10 +263,10 @@ const Entry = struct {...@@ -238,10 +263,10 @@ const Entry = struct {
238 }263 }
239};264};
240265
241const BindingTable = std.AutoArrayHashMapUnmanaged(*Atom, std.ArrayListUnmanaged(Atom.Binding));266const BindingTable = std.AutoArrayHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(Atom.Binding));
242const UnnamedConstTable = std.AutoArrayHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(*Atom));267const UnnamedConstTable = std.AutoArrayHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(Atom.Index));
243const RebaseTable = std.AutoArrayHashMapUnmanaged(*Atom, std.ArrayListUnmanaged(u32));268const RebaseTable = std.AutoArrayHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(u32));
244const RelocationTable = std.AutoArrayHashMapUnmanaged(*Atom, std.ArrayListUnmanaged(Relocation));269const RelocationTable = std.AutoArrayHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(Relocation));
245270
246const PendingUpdate = union(enum) {271const PendingUpdate = union(enum) {
247 resolve_undef: u32,272 resolve_undef: u32,
...@@ -286,10 +311,6 @@ pub const default_pagezero_vmsize: u64 = 0x100000000;...@@ -286,10 +311,6 @@ pub const default_pagezero_vmsize: u64 = 0x100000000;
286/// potential future extensions.311/// potential future extensions.
287pub const default_headerpad_size: u32 = 0x1000;312pub const default_headerpad_size: u32 = 0x1000;
288313
289pub const Export = struct {
290 sym_index: ?u32 = null,
291};
292
293pub fn openPath(allocator: Allocator, options: link.Options) !*MachO {314pub fn openPath(allocator: Allocator, options: link.Options) !*MachO {
294 assert(options.target.ofmt == .macho);315 assert(options.target.ofmt == .macho);
295316
...@@ -547,8 +568,8 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No...@@ -547,8 +568,8 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No
547568
548 try self.allocateSpecialSymbols();569 try self.allocateSpecialSymbols();
549570
550 for (self.relocs.keys()) |atom| {571 for (self.relocs.keys()) |atom_index| {
551 try atom.resolveRelocations(self);572 try Atom.resolveRelocations(self, atom_index);
552 }573 }
553574
554 if (build_options.enable_logging) {575 if (build_options.enable_logging) {
...@@ -999,18 +1020,19 @@ pub fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs:...@@ -999,18 +1020,19 @@ pub fn parseDependentLibs(self: *MachO, syslibroot: ?[]const u8, dependent_libs:
999 }1020 }
1000}1021}
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);
1003 const sym = atom.getSymbol(self);1025 const sym = atom.getSymbol(self);
1004 const section = self.sections.get(sym.n_sect - 1);1026 const section = self.sections.get(sym.n_sect - 1);
1005 const file_offset = section.header.offset + sym.n_value - section.header.addr;1027 const file_offset = section.header.offset + sym.n_value - section.header.addr;
1006 log.debug("writing atom for symbol {s} at file offset 0x{x}", .{ atom.getName(self), file_offset });1028 log.debug("writing atom for symbol {s} at file offset 0x{x}", .{ atom.getName(self), file_offset });
1007 try self.base.file.?.pwriteAll(code, file_offset);1029 try self.base.file.?.pwriteAll(code, file_offset);
1008 try atom.resolveRelocations(self);1030 try Atom.resolveRelocations(self, atom_index);
1009}1031}
10101032
1011fn writePtrWidthAtom(self: *MachO, atom: *Atom) !void {1033fn writePtrWidthAtom(self: *MachO, atom_index: Atom.Index) !void {
1012 var buffer: [@sizeOf(u64)]u8 = [_]u8{0} ** @sizeOf(u64);1034 var buffer: [@sizeOf(u64)]u8 = [_]u8{0} ** @sizeOf(u64);
1013 try self.writeAtom(atom, &buffer);1035 try self.writeAtom(atom_index, &buffer);
1014}1036}
10151037
1016fn markRelocsDirtyByTarget(self: *MachO, target: SymbolWithLoc) void {1038fn markRelocsDirtyByTarget(self: *MachO, target: SymbolWithLoc) void {
...@@ -1026,7 +1048,8 @@ fn markRelocsDirtyByTarget(self: *MachO, target: SymbolWithLoc) void {...@@ -1026,7 +1048,8 @@ fn markRelocsDirtyByTarget(self: *MachO, target: SymbolWithLoc) void {
1026fn markRelocsDirtyByAddress(self: *MachO, addr: u64) void {1048fn markRelocsDirtyByAddress(self: *MachO, addr: u64) void {
1027 for (self.relocs.values()) |*relocs| {1049 for (self.relocs.values()) |*relocs| {
1028 for (relocs.items) |*reloc| {1050 for (relocs.items) |*reloc| {
1029 const target_atom = reloc.getTargetAtom(self) orelse continue;1051 const target_atom_index = reloc.getTargetAtomIndex(self) orelse continue;
1052 const target_atom = self.getAtom(target_atom_index);
1030 const target_sym = target_atom.getSymbol(self);1053 const target_sym = target_atom.getSymbol(self);
1031 if (target_sym.n_value < addr) continue;1054 if (target_sym.n_value < addr) continue;
1032 reloc.dirty = true;1055 reloc.dirty = true;
...@@ -1053,31 +1076,38 @@ pub fn allocateSpecialSymbols(self: *MachO) !void {...@@ -1053,31 +1076,38 @@ pub fn allocateSpecialSymbols(self: *MachO) !void {
1053 }1076 }
1054}1077}
10551078
1056pub fn createGotAtom(self: *MachO, target: SymbolWithLoc) !*Atom {1079pub fn createAtom(self: *MachO) !Atom.Index {
1057 const gpa = self.base.allocator;1080 const gpa = self.base.allocator;
10581081 const atom_index = @intCast(Atom.Index, self.atoms.items.len);
1082 const atom = try self.atoms.addOne(gpa);
1059 const sym_index = try self.allocateSymbol();1083 const sym_index = try self.allocateSymbol();
1060 const atom = blk: {1084 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom_index);
1061 const atom = try gpa.create(Atom);1085 atom.* = .{
1062 atom.* = Atom.empty;1086 .sym_index = sym_index,
1063 atom.sym_index = sym_index;1087 .file = null,
1064 atom.size = @sizeOf(u64);1088 .size = 0,
1065 atom.alignment = @alignOf(u64);1089 .alignment = 0,
1066 break :blk atom;1090 .prev_index = null,
1091 .next_index = null,
1067 };1092 };
1068 errdefer gpa.destroy(atom);1093 log.debug("creating ATOM(%{d}) at index {d}", .{ sym_index, atom_index });
1094 return atom_index;
1095}
10691096
1070 try self.managed_atoms.append(gpa, atom);1097pub fn createGotAtom(self: *MachO, target: SymbolWithLoc) !Atom.Index {
1071 try self.atom_by_index_table.putNoClobber(gpa, atom.sym_index, atom);1098 const atom_index = try self.createAtom();
1099 const atom = self.getAtomPtr(atom_index);
1100 atom.size = @sizeOf(u64);
1101 atom.alignment = @alignOf(u64);
10721102
1073 const sym = atom.getSymbolPtr(self);1103 const sym = atom.getSymbolPtr(self);
1074 sym.n_type = macho.N_SECT;1104 sym.n_type = macho.N_SECT;
1075 sym.n_sect = self.got_section_index.? + 1;1105 sym.n_sect = self.got_section_index.? + 1;
1076 sym.n_value = try self.allocateAtom(atom, atom.size, @alignOf(u64));1106 sym.n_value = try self.allocateAtom(atom_index, atom.size, @alignOf(u64));
10771107
1078 log.debug("allocated GOT atom at 0x{x}", .{sym.n_value});1108 log.debug("allocated GOT atom at 0x{x}", .{sym.n_value});
10791109
1080 try atom.addRelocation(self, .{1110 try Atom.addRelocation(self, atom_index, .{
1081 .type = switch (self.base.options.target.cpu.arch) {1111 .type = switch (self.base.options.target.cpu.arch) {
1082 .aarch64 => @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),1112 .aarch64 => @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),
1083 .x86_64 => @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED),1113 .x86_64 => @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED),
...@@ -1092,50 +1122,39 @@ pub fn createGotAtom(self: *MachO, target: SymbolWithLoc) !*Atom {...@@ -1092,50 +1122,39 @@ pub fn createGotAtom(self: *MachO, target: SymbolWithLoc) !*Atom {
10921122
1093 const target_sym = self.getSymbol(target);1123 const target_sym = self.getSymbol(target);
1094 if (target_sym.undf()) {1124 if (target_sym.undf()) {
1095 try atom.addBinding(self, .{1125 try Atom.addBinding(self, atom_index, .{
1096 .target = self.getGlobal(self.getSymbolName(target)).?,1126 .target = self.getGlobal(self.getSymbolName(target)).?,
1097 .offset = 0,1127 .offset = 0,
1098 });1128 });
1099 } else {1129 } else {
1100 try atom.addRebase(self, 0);1130 try Atom.addRebase(self, atom_index, 0);
1101 }1131 }
11021132
1103 return atom;1133 return atom_index;
1104}1134}
11051135
1106pub fn createDyldPrivateAtom(self: *MachO) !void {1136pub fn createDyldPrivateAtom(self: *MachO) !void {
1107 if (self.dyld_stub_binder_index == null) return;1137 if (self.dyld_stub_binder_index == null) return;
1108 if (self.dyld_private_atom != null) return;1138 if (self.dyld_private_atom_index != null) return;
1109
1110 const gpa = self.base.allocator;
11111139
1112 const sym_index = try self.allocateSymbol();1140 const atom_index = try self.createAtom();
1113 const atom = blk: {1141 const atom = self.getAtomPtr(atom_index);
1114 const atom = try gpa.create(Atom);1142 atom.size = @sizeOf(u64);
1115 atom.* = Atom.empty;1143 atom.alignment = @alignOf(u64);
1116 atom.sym_index = sym_index;
1117 atom.size = @sizeOf(u64);
1118 atom.alignment = @alignOf(u64);
1119 break :blk atom;
1120 };
1121 errdefer gpa.destroy(atom);
11221144
1123 const sym = atom.getSymbolPtr(self);1145 const sym = atom.getSymbolPtr(self);
1124 sym.n_type = macho.N_SECT;1146 sym.n_type = macho.N_SECT;
1125 sym.n_sect = self.data_section_index.? + 1;1147 sym.n_sect = self.data_section_index.? + 1;
1126 self.dyld_private_atom = atom;1148 self.dyld_private_atom_index = atom_index;
11271149
1128 try self.managed_atoms.append(gpa, atom);1150 sym.n_value = try self.allocateAtom(atom_index, atom.size, @alignOf(u64));
1129 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
1130
1131 sym.n_value = try self.allocateAtom(atom, atom.size, @alignOf(u64));
1132 log.debug("allocated dyld_private atom at 0x{x}", .{sym.n_value});1151 log.debug("allocated dyld_private atom at 0x{x}", .{sym.n_value});
1133 try self.writePtrWidthAtom(atom);1152 try self.writePtrWidthAtom(atom_index);
1134}1153}
11351154
1136pub fn createStubHelperPreambleAtom(self: *MachO) !void {1155pub fn createStubHelperPreambleAtom(self: *MachO) !void {
1137 if (self.dyld_stub_binder_index == null) return;1156 if (self.dyld_stub_binder_index == null) return;
1138 if (self.stub_helper_preamble_atom != null) return;1157 if (self.stub_helper_preamble_atom_index != null) return;
11391158
1140 const gpa = self.base.allocator;1159 const gpa = self.base.allocator;
1141 const arch = self.base.options.target.cpu.arch;1160 const arch = self.base.options.target.cpu.arch;
...@@ -1144,26 +1163,23 @@ pub fn createStubHelperPreambleAtom(self: *MachO) !void {...@@ -1144,26 +1163,23 @@ pub fn createStubHelperPreambleAtom(self: *MachO) !void {
1144 .aarch64 => 6 * @sizeOf(u32),1163 .aarch64 => 6 * @sizeOf(u32),
1145 else => unreachable,1164 else => unreachable,
1146 };1165 };
1147 const sym_index = try self.allocateSymbol();1166 const atom_index = try self.createAtom();
1148 const atom = blk: {1167 const atom = self.getAtomPtr(atom_index);
1149 const atom = try gpa.create(Atom);1168 atom.size = size;
1150 atom.* = Atom.empty;1169 atom.alignment = switch (arch) {
1151 atom.sym_index = sym_index;1170 .x86_64 => 1,
1152 atom.size = size;1171 .aarch64 => @alignOf(u32),
1153 atom.alignment = switch (arch) {1172 else => unreachable,
1154 .x86_64 => 1,
1155 .aarch64 => @alignOf(u32),
1156 else => unreachable,
1157 };
1158 break :blk atom;
1159 };1173 };
1160 errdefer gpa.destroy(atom);
11611174
1162 const sym = atom.getSymbolPtr(self);1175 const sym = atom.getSymbolPtr(self);
1163 sym.n_type = macho.N_SECT;1176 sym.n_type = macho.N_SECT;
1164 sym.n_sect = self.stub_helper_section_index.? + 1;1177 sym.n_sect = self.stub_helper_section_index.? + 1;
11651178
1166 const dyld_private_sym_index = self.dyld_private_atom.?.sym_index;1179 const dyld_private_sym_index = if (self.dyld_private_atom_index) |dyld_index|
1180 self.getAtom(dyld_index).getSymbolIndex().?
1181 else
1182 unreachable;
11671183
1168 const code = try gpa.alloc(u8, size);1184 const code = try gpa.alloc(u8, size);
1169 defer gpa.free(code);1185 defer gpa.free(code);
...@@ -1182,7 +1198,7 @@ pub fn createStubHelperPreambleAtom(self: *MachO) !void {...@@ -1182,7 +1198,7 @@ pub fn createStubHelperPreambleAtom(self: *MachO) !void {
1182 code[9] = 0xff;1198 code[9] = 0xff;
1183 code[10] = 0x25;1199 code[10] = 0x25;
11841200
1185 try atom.addRelocations(self, 2, .{ .{1201 try Atom.addRelocations(self, atom_index, 2, .{ .{
1186 .type = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_SIGNED),1202 .type = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_SIGNED),
1187 .target = .{ .sym_index = dyld_private_sym_index, .file = null },1203 .target = .{ .sym_index = dyld_private_sym_index, .file = null },
1188 .offset = 3,1204 .offset = 3,
...@@ -1222,7 +1238,7 @@ pub fn createStubHelperPreambleAtom(self: *MachO) !void {...@@ -1222,7 +1238,7 @@ pub fn createStubHelperPreambleAtom(self: *MachO) !void {
1222 // br x161238 // br x16
1223 mem.writeIntLittle(u32, code[20..][0..4], aarch64.Instruction.br(.x16).toU32());1239 mem.writeIntLittle(u32, code[20..][0..4], aarch64.Instruction.br(.x16).toU32());
12241240
1225 try atom.addRelocations(self, 4, .{ .{1241 try Atom.addRelocations(self, atom_index, 4, .{ .{
1226 .type = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGE21),1242 .type = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGE21),
1227 .target = .{ .sym_index = dyld_private_sym_index, .file = null },1243 .target = .{ .sym_index = dyld_private_sym_index, .file = null },
1228 .offset = 0,1244 .offset = 0,
...@@ -1255,17 +1271,14 @@ pub fn createStubHelperPreambleAtom(self: *MachO) !void {...@@ -1255,17 +1271,14 @@ pub fn createStubHelperPreambleAtom(self: *MachO) !void {
12551271
1256 else => unreachable,1272 else => unreachable,
1257 }1273 }
1258 self.stub_helper_preamble_atom = atom;1274 self.stub_helper_preamble_atom_index = atom_index;
1259
1260 try self.managed_atoms.append(gpa, atom);
1261 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
12621275
1263 sym.n_value = try self.allocateAtom(atom, size, atom.alignment);1276 sym.n_value = try self.allocateAtom(atom_index, size, atom.alignment);
1264 log.debug("allocated stub preamble atom at 0x{x}", .{sym.n_value});1277 log.debug("allocated stub preamble atom at 0x{x}", .{sym.n_value});
1265 try self.writeAtom(atom, code);1278 try self.writeAtom(atom_index, code);
1266}1279}
12671280
1268pub fn createStubHelperAtom(self: *MachO) !*Atom {1281pub fn createStubHelperAtom(self: *MachO) !Atom.Index {
1269 const gpa = self.base.allocator;1282 const gpa = self.base.allocator;
1270 const arch = self.base.options.target.cpu.arch;1283 const arch = self.base.options.target.cpu.arch;
1271 const size: u4 = switch (arch) {1284 const size: u4 = switch (arch) {
...@@ -1273,20 +1286,14 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {...@@ -1273,20 +1286,14 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
1273 .aarch64 => 3 * @sizeOf(u32),1286 .aarch64 => 3 * @sizeOf(u32),
1274 else => unreachable,1287 else => unreachable,
1275 };1288 };
1276 const sym_index = try self.allocateSymbol();1289 const atom_index = try self.createAtom();
1277 const atom = blk: {1290 const atom = self.getAtomPtr(atom_index);
1278 const atom = try gpa.create(Atom);1291 atom.size = size;
1279 atom.* = Atom.empty;1292 atom.alignment = switch (arch) {
1280 atom.sym_index = sym_index;1293 .x86_64 => 1,
1281 atom.size = size;1294 .aarch64 => @alignOf(u32),
1282 atom.alignment = switch (arch) {1295 else => unreachable,
1283 .x86_64 => 1,
1284 .aarch64 => @alignOf(u32),
1285 else => unreachable,
1286 };
1287 break :blk atom;
1288 };1296 };
1289 errdefer gpa.destroy(atom);
12901297
1291 const sym = atom.getSymbolPtr(self);1298 const sym = atom.getSymbolPtr(self);
1292 sym.n_type = macho.N_SECT;1299 sym.n_type = macho.N_SECT;
...@@ -1296,6 +1303,11 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {...@@ -1296,6 +1303,11 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
1296 defer gpa.free(code);1303 defer gpa.free(code);
1297 mem.set(u8, code, 0);1304 mem.set(u8, code, 0);
12981305
1306 const stub_helper_preamble_atom_sym_index = if (self.stub_helper_preamble_atom_index) |stub_index|
1307 self.getAtom(stub_index).getSymbolIndex().?
1308 else
1309 unreachable;
1310
1299 switch (arch) {1311 switch (arch) {
1300 .x86_64 => {1312 .x86_64 => {
1301 // pushq1313 // pushq
...@@ -1304,9 +1316,9 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {...@@ -1304,9 +1316,9 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
1304 // jmpq1316 // jmpq
1305 code[5] = 0xe9;1317 code[5] = 0xe9;
13061318
1307 try atom.addRelocation(self, .{1319 try Atom.addRelocation(self, atom_index, .{
1308 .type = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_BRANCH),1320 .type = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_BRANCH),
1309 .target = .{ .sym_index = self.stub_helper_preamble_atom.?.sym_index, .file = null },1321 .target = .{ .sym_index = stub_helper_preamble_atom_sym_index, .file = null },
1310 .offset = 6,1322 .offset = 6,
1311 .addend = 0,1323 .addend = 0,
1312 .pcrel = true,1324 .pcrel = true,
...@@ -1327,9 +1339,9 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {...@@ -1327,9 +1339,9 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
1327 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.b(0).toU32());1339 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.b(0).toU32());
1328 // Next 4 bytes 8..12 are just a placeholder populated in `populateLazyBindOffsetsInStubHelper`.1340 // Next 4 bytes 8..12 are just a placeholder populated in `populateLazyBindOffsetsInStubHelper`.
13291341
1330 try atom.addRelocation(self, .{1342 try Atom.addRelocation(self, atom_index, .{
1331 .type = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_BRANCH26),1343 .type = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_BRANCH26),
1332 .target = .{ .sym_index = self.stub_helper_preamble_atom.?.sym_index, .file = null },1344 .target = .{ .sym_index = stub_helper_preamble_atom_sym_index, .file = null },
1333 .offset = 4,1345 .offset = 4,
1334 .addend = 0,1346 .addend = 0,
1335 .pcrel = true,1347 .pcrel = true,
...@@ -1339,34 +1351,24 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {...@@ -1339,34 +1351,24 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
1339 else => unreachable,1351 else => unreachable,
1340 }1352 }
13411353
1342 try self.managed_atoms.append(gpa, atom);1354 sym.n_value = try self.allocateAtom(atom_index, size, atom.alignment);
1343 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
1344
1345 sym.n_value = try self.allocateAtom(atom, size, atom.alignment);
1346 log.debug("allocated stub helper atom at 0x{x}", .{sym.n_value});1355 log.debug("allocated stub helper atom at 0x{x}", .{sym.n_value});
1347 try self.writeAtom(atom, code);1356 try self.writeAtom(atom_index, code);
13481357
1349 return atom;1358 return atom_index;
1350}1359}
13511360
1352pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, target: SymbolWithLoc) !*Atom {1361pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, target: SymbolWithLoc) !Atom.Index {
1353 const gpa = self.base.allocator;1362 const atom_index = try self.createAtom();
1354 const sym_index = try self.allocateSymbol();1363 const atom = self.getAtomPtr(atom_index);
1355 const atom = blk: {1364 atom.size = @sizeOf(u64);
1356 const atom = try gpa.create(Atom);1365 atom.alignment = @alignOf(u64);
1357 atom.* = Atom.empty;
1358 atom.sym_index = sym_index;
1359 atom.size = @sizeOf(u64);
1360 atom.alignment = @alignOf(u64);
1361 break :blk atom;
1362 };
1363 errdefer gpa.destroy(atom);
13641366
1365 const sym = atom.getSymbolPtr(self);1367 const sym = atom.getSymbolPtr(self);
1366 sym.n_type = macho.N_SECT;1368 sym.n_type = macho.N_SECT;
1367 sym.n_sect = self.la_symbol_ptr_section_index.? + 1;1369 sym.n_sect = self.la_symbol_ptr_section_index.? + 1;
13681370
1369 try atom.addRelocation(self, .{1371 try Atom.addRelocation(self, atom_index, .{
1370 .type = switch (self.base.options.target.cpu.arch) {1372 .type = switch (self.base.options.target.cpu.arch) {
1371 .aarch64 => @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),1373 .aarch64 => @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),
1372 .x86_64 => @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED),1374 .x86_64 => @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED),
...@@ -1378,23 +1380,20 @@ pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, target: SymbolWi...@@ -1378,23 +1380,20 @@ pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, target: SymbolWi
1378 .pcrel = false,1380 .pcrel = false,
1379 .length = 3,1381 .length = 3,
1380 });1382 });
1381 try atom.addRebase(self, 0);1383 try Atom.addRebase(self, atom_index, 0);
1382 try atom.addLazyBinding(self, .{1384 try Atom.addLazyBinding(self, atom_index, .{
1383 .target = self.getGlobal(self.getSymbolName(target)).?,1385 .target = self.getGlobal(self.getSymbolName(target)).?,
1384 .offset = 0,1386 .offset = 0,
1385 });1387 });
13861388
1387 try self.managed_atoms.append(gpa, atom);1389 sym.n_value = try self.allocateAtom(atom_index, atom.size, @alignOf(u64));
1388 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
1389
1390 sym.n_value = try self.allocateAtom(atom, atom.size, @alignOf(u64));
1391 log.debug("allocated lazy pointer atom at 0x{x} ({s})", .{ sym.n_value, self.getSymbolName(target) });1390 log.debug("allocated lazy pointer atom at 0x{x} ({s})", .{ sym.n_value, self.getSymbolName(target) });
1392 try self.writePtrWidthAtom(atom);1391 try self.writePtrWidthAtom(atom_index);
13931392
1394 return atom;1393 return atom_index;
1395}1394}
13961395
1397pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {1396pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !Atom.Index {
1398 const gpa = self.base.allocator;1397 const gpa = self.base.allocator;
1399 const arch = self.base.options.target.cpu.arch;1398 const arch = self.base.options.target.cpu.arch;
1400 const size: u4 = switch (arch) {1399 const size: u4 = switch (arch) {
...@@ -1402,21 +1401,15 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {...@@ -1402,21 +1401,15 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
1402 .aarch64 => 3 * @sizeOf(u32),1401 .aarch64 => 3 * @sizeOf(u32),
1403 else => unreachable, // unhandled architecture type1402 else => unreachable, // unhandled architecture type
1404 };1403 };
1405 const sym_index = try self.allocateSymbol();1404 const atom_index = try self.createAtom();
1406 const atom = blk: {1405 const atom = self.getAtomPtr(atom_index);
1407 const atom = try gpa.create(Atom);1406 atom.size = size;
1408 atom.* = Atom.empty;1407 atom.alignment = switch (arch) {
1409 atom.sym_index = sym_index;1408 .x86_64 => 1,
1410 atom.size = size;1409 .aarch64 => @alignOf(u32),
1411 atom.alignment = switch (arch) {1410 else => unreachable, // unhandled architecture type
1412 .x86_64 => 1,
1413 .aarch64 => @alignOf(u32),
1414 else => unreachable, // unhandled architecture type
14151411
1416 };
1417 break :blk atom;
1418 };1412 };
1419 errdefer gpa.destroy(atom);
14201413
1421 const sym = atom.getSymbolPtr(self);1414 const sym = atom.getSymbolPtr(self);
1422 sym.n_type = macho.N_SECT;1415 sym.n_type = macho.N_SECT;
...@@ -1432,7 +1425,7 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {...@@ -1432,7 +1425,7 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
1432 code[0] = 0xff;1425 code[0] = 0xff;
1433 code[1] = 0x25;1426 code[1] = 0x25;
14341427
1435 try atom.addRelocation(self, .{1428 try Atom.addRelocation(self, atom_index, .{
1436 .type = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_BRANCH),1429 .type = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_BRANCH),
1437 .target = .{ .sym_index = laptr_sym_index, .file = null },1430 .target = .{ .sym_index = laptr_sym_index, .file = null },
1438 .offset = 2,1431 .offset = 2,
...@@ -1453,7 +1446,7 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {...@@ -1453,7 +1446,7 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
1453 // br x161446 // br x16
1454 mem.writeIntLittle(u32, code[8..12], aarch64.Instruction.br(.x16).toU32());1447 mem.writeIntLittle(u32, code[8..12], aarch64.Instruction.br(.x16).toU32());
14551448
1456 try atom.addRelocations(self, 2, .{1449 try Atom.addRelocations(self, atom_index, 2, .{
1457 .{1450 .{
1458 .type = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGE21),1451 .type = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGE21),
1459 .target = .{ .sym_index = laptr_sym_index, .file = null },1452 .target = .{ .sym_index = laptr_sym_index, .file = null },
...@@ -1475,14 +1468,11 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {...@@ -1475,14 +1468,11 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
1475 else => unreachable,1468 else => unreachable,
1476 }1469 }
14771470
1478 try self.managed_atoms.append(gpa, atom);1471 sym.n_value = try self.allocateAtom(atom_index, size, atom.alignment);
1479 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom);
1480
1481 sym.n_value = try self.allocateAtom(atom, size, atom.alignment);
1482 log.debug("allocated stub atom at 0x{x}", .{sym.n_value});1472 log.debug("allocated stub atom at 0x{x}", .{sym.n_value});
1483 try self.writeAtom(atom, code);1473 try self.writeAtom(atom_index, code);
14841474
1485 return atom;1475 return atom_index;
1486}1476}
14871477
1488pub fn createMhExecuteHeaderSymbol(self: *MachO) !void {1478pub fn createMhExecuteHeaderSymbol(self: *MachO) !void {
...@@ -1616,10 +1606,13 @@ pub fn resolveSymbolsInDylibs(self: *MachO) !void {...@@ -1616,10 +1606,13 @@ pub fn resolveSymbolsInDylibs(self: *MachO) !void {
1616 if (self.stubs_table.contains(global)) break :blk;1606 if (self.stubs_table.contains(global)) break :blk;
16171607
1618 const stub_index = try self.allocateStubEntry(global);1608 const stub_index = try self.allocateStubEntry(global);
1619 const stub_helper_atom = try self.createStubHelperAtom();1609 const stub_helper_atom_index = try self.createStubHelperAtom();
1620 const laptr_atom = try self.createLazyPointerAtom(stub_helper_atom.sym_index, global);1610 const stub_helper_atom = self.getAtom(stub_helper_atom_index);
1621 const stub_atom = try self.createStubAtom(laptr_atom.sym_index);1611 const laptr_atom_index = try self.createLazyPointerAtom(stub_helper_atom.getSymbolIndex().?, global);
1622 self.stubs.items[stub_index].sym_index = stub_atom.sym_index;1612 const laptr_atom = self.getAtom(laptr_atom_index);
1613 const stub_atom_index = try self.createStubAtom(laptr_atom.getSymbolIndex().?);
1614 const stub_atom = self.getAtom(stub_atom_index);
1615 self.stubs.items[stub_index].sym_index = stub_atom.getSymbolIndex().?;
1623 self.markRelocsDirtyByTarget(global);1616 self.markRelocsDirtyByTarget(global);
1624 }1617 }
16251618
...@@ -1716,10 +1709,11 @@ pub fn resolveDyldStubBinder(self: *MachO) !void {...@@ -1716,10 +1709,11 @@ pub fn resolveDyldStubBinder(self: *MachO) !void {
17161709
1717 // Add dyld_stub_binder as the final GOT entry.1710 // Add dyld_stub_binder as the final GOT entry.
1718 const got_index = try self.allocateGotEntry(global);1711 const got_index = try self.allocateGotEntry(global);
1719 const got_atom = try self.createGotAtom(global);1712 const got_atom_index = try self.createGotAtom(global);
1720 self.got_entries.items[got_index].sym_index = got_atom.sym_index;1713 const got_atom = self.getAtom(got_atom_index);
1714 self.got_entries.items[got_index].sym_index = got_atom.getSymbolIndex().?;
17211715
1722 try self.writePtrWidthAtom(got_atom);1716 try self.writePtrWidthAtom(got_atom_index);
1723}1717}
17241718
1725pub fn deinit(self: *MachO) void {1719pub fn deinit(self: *MachO) void {
...@@ -1769,12 +1763,12 @@ pub fn deinit(self: *MachO) void {...@@ -1769,12 +1763,12 @@ pub fn deinit(self: *MachO) void {
1769 }1763 }
1770 self.sections.deinit(gpa);1764 self.sections.deinit(gpa);
17711765
1772 for (self.managed_atoms.items) |atom| {1766 self.atoms.deinit(gpa);
1773 gpa.destroy(atom);
1774 }
1775 self.managed_atoms.deinit(gpa);
17761767
1777 if (self.base.options.module) |_| {1768 if (self.base.options.module) |_| {
1769 for (self.decls.values()) |*m| {
1770 m.exports.deinit(gpa);
1771 }
1778 self.decls.deinit(gpa);1772 self.decls.deinit(gpa);
1779 } else {1773 } else {
1780 assert(self.decls.count() == 0);1774 assert(self.decls.count() == 0);
...@@ -1808,12 +1802,14 @@ pub fn deinit(self: *MachO) void {...@@ -1808,12 +1802,14 @@ pub fn deinit(self: *MachO) void {
1808 self.lazy_bindings.deinit(gpa);1802 self.lazy_bindings.deinit(gpa);
1809}1803}
18101804
1811fn freeAtom(self: *MachO, atom: *Atom) void {1805fn freeAtom(self: *MachO, atom_index: Atom.Index) void {
1812 log.debug("freeAtom {*}", .{atom});1806 const gpa = self.base.allocator;
1807 log.debug("freeAtom {d}", .{atom_index});
18131808
1814 // Remove any relocs and base relocs associated with this Atom1809 // Remove any relocs and base relocs associated with this Atom
1815 self.freeRelocationsForAtom(atom);1810 Atom.freeRelocations(self, atom_index);
18161811
1812 const atom = self.getAtom(atom_index);
1817 const sect_id = atom.getSymbol(self).n_sect - 1;1813 const sect_id = atom.getSymbol(self).n_sect - 1;
1818 const free_list = &self.sections.items(.free_list)[sect_id];1814 const free_list = &self.sections.items(.free_list)[sect_id];
1819 var already_have_free_list_node = false;1815 var already_have_free_list_node = false;
...@@ -1821,69 +1817,94 @@ fn freeAtom(self: *MachO, atom: *Atom) void {...@@ -1821,69 +1817,94 @@ fn freeAtom(self: *MachO, atom: *Atom) void {
1821 var i: usize = 0;1817 var i: usize = 0;
1822 // TODO turn free_list into a hash map1818 // TODO turn free_list into a hash map
1823 while (i < free_list.items.len) {1819 while (i < free_list.items.len) {
1824 if (free_list.items[i] == atom) {1820 if (free_list.items[i] == atom_index) {
1825 _ = free_list.swapRemove(i);1821 _ = free_list.swapRemove(i);
1826 continue;1822 continue;
1827 }1823 }
1828 if (free_list.items[i] == atom.prev) {1824 if (free_list.items[i] == atom.prev_index) {
1829 already_have_free_list_node = true;1825 already_have_free_list_node = true;
1830 }1826 }
1831 i += 1;1827 i += 1;
1832 }1828 }
1833 }1829 }
18341830
1835 const maybe_last_atom = &self.sections.items(.last_atom)[sect_id];1831 const maybe_last_atom_index = &self.sections.items(.last_atom_index)[sect_id];
1836 if (maybe_last_atom.*) |last_atom| {1832 if (maybe_last_atom_index.*) |last_atom_index| {
1837 if (last_atom == atom) {1833 if (last_atom_index == atom_index) {
1838 if (atom.prev) |prev| {1834 if (atom.prev_index) |prev_index| {
1839 // TODO shrink the section size here1835 // TODO shrink the section size here
1840 maybe_last_atom.* = prev;1836 maybe_last_atom_index.* = prev_index;
1841 } else {1837 } else {
1842 maybe_last_atom.* = null;1838 maybe_last_atom_index.* = null;
1843 }1839 }
1844 }1840 }
1845 }1841 }
18461842
1847 if (atom.prev) |prev| {1843 if (atom.prev_index) |prev_index| {
1848 prev.next = atom.next;1844 const prev = self.getAtomPtr(prev_index);
1845 prev.next_index = atom.next_index;
18491846
1850 if (!already_have_free_list_node and prev.freeListEligible(self)) {1847 if (!already_have_free_list_node and prev.*.freeListEligible(self)) {
1851 // The free list is heuristics, it doesn't have to be perfect, so we can ignore1848 // The free list is heuristics, it doesn't have to be perfect, so we can ignore
1852 // the OOM here.1849 // the OOM here.
1853 free_list.append(self.base.allocator, prev) catch {};1850 free_list.append(gpa, prev_index) catch {};
1854 }1851 }
1855 } else {1852 } else {
1856 atom.prev = null;1853 self.getAtomPtr(atom_index).prev_index = null;
1857 }1854 }
18581855
1859 if (atom.next) |next| {1856 if (atom.next_index) |next_index| {
1860 next.prev = atom.prev;1857 self.getAtomPtr(next_index).prev_index = atom.prev_index;
1861 } else {1858 } else {
1862 atom.next = null;1859 self.getAtomPtr(atom_index).next_index = null;
1863 }1860 }
18641861
1865 if (self.d_sym) |*d_sym| {1862 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
1866 d_sym.dwarf.freeAtom(&atom.dbg_info_atom);1863 const sym_index = atom.getSymbolIndex().?;
1864
1865 self.locals_free_list.append(gpa, sym_index) catch {};
1866
1867 // Try freeing GOT atom if this decl had one
1868 const got_target = SymbolWithLoc{ .sym_index = sym_index, .file = null };
1869 if (self.got_entries_table.get(got_target)) |got_index| {
1870 self.got_entries_free_list.append(gpa, @intCast(u32, got_index)) catch {};
1871 self.got_entries.items[got_index] = .{
1872 .target = .{ .sym_index = 0, .file = null },
1873 .sym_index = 0,
1874 };
1875 _ = self.got_entries_table.remove(got_target);
1876
1877 if (self.d_sym) |*d_sym| {
1878 d_sym.swapRemoveRelocs(sym_index);
1879 }
1880
1881 log.debug(" adding GOT index {d} to free list (target local@{d})", .{ got_index, sym_index });
1867 }1882 }
1883
1884 self.locals.items[sym_index].n_type = 0;
1885 _ = self.atom_by_index_table.remove(sym_index);
1886 log.debug(" adding local symbol index {d} to free list", .{sym_index});
1887 self.getAtomPtr(atom_index).sym_index = 0;
1868}1888}
18691889
1870fn shrinkAtom(self: *MachO, atom: *Atom, new_block_size: u64) void {1890fn shrinkAtom(self: *MachO, atom_index: Atom.Index, new_block_size: u64) void {
1871 _ = self;1891 _ = self;
1872 _ = atom;1892 _ = atom_index;
1873 _ = new_block_size;1893 _ = new_block_size;
1874 // TODO check the new capacity, and if it crosses the size threshold into a big enough1894 // TODO check the new capacity, and if it crosses the size threshold into a big enough
1875 // capacity, insert a free list node for it.1895 // capacity, insert a free list node for it.
1876}1896}
18771897
1878fn growAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) !u64 {1898fn growAtom(self: *MachO, atom_index: Atom.Index, new_atom_size: u64, alignment: u64) !u64 {
1899 const atom = self.getAtom(atom_index);
1879 const sym = atom.getSymbol(self);1900 const sym = atom.getSymbol(self);
1880 const align_ok = mem.alignBackwardGeneric(u64, sym.n_value, alignment) == sym.n_value;1901 const align_ok = mem.alignBackwardGeneric(u64, sym.n_value, alignment) == sym.n_value;
1881 const need_realloc = !align_ok or new_atom_size > atom.capacity(self);1902 const need_realloc = !align_ok or new_atom_size > atom.capacity(self);
1882 if (!need_realloc) return sym.n_value;1903 if (!need_realloc) return sym.n_value;
1883 return self.allocateAtom(atom, new_atom_size, alignment);1904 return self.allocateAtom(atom_index, new_atom_size, alignment);
1884}1905}
18851906
1886fn allocateSymbol(self: *MachO) !u32 {1907pub fn allocateSymbol(self: *MachO) !u32 {
1887 try self.locals.ensureUnusedCapacity(self.base.allocator, 1);1908 try self.locals.ensureUnusedCapacity(self.base.allocator, 1);
18881909
1889 const index = blk: {1910 const index = blk: {
...@@ -1975,16 +1996,6 @@ pub fn allocateStubEntry(self: *MachO, target: SymbolWithLoc) !u32 {...@@ -1975,16 +1996,6 @@ pub fn allocateStubEntry(self: *MachO, target: SymbolWithLoc) !u32 {
1975 return index;1996 return index;
1976}1997}
19771998
1978pub fn allocateDeclIndexes(self: *MachO, decl_index: Module.Decl.Index) !void {
1979 if (self.llvm_object) |_| return;
1980 const decl = self.base.options.module.?.declPtr(decl_index);
1981 if (decl.link.macho.sym_index != 0) return;
1982
1983 decl.link.macho.sym_index = try self.allocateSymbol();
1984 try self.atom_by_index_table.putNoClobber(self.base.allocator, decl.link.macho.sym_index, &decl.link.macho);
1985 try self.decls.putNoClobber(self.base.allocator, decl_index, null);
1986}
1987
1988pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void {1999pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void {
1989 if (build_options.skip_non_native and builtin.object_format != .macho) {2000 if (build_options.skip_non_native and builtin.object_format != .macho) {
1990 @panic("Attempted to compile for object format that was disabled by build configuration");2001 @panic("Attempted to compile for object format that was disabled by build configuration");
...@@ -1997,8 +2008,12 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv...@@ -1997,8 +2008,12 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv
19972008
1998 const decl_index = func.owner_decl;2009 const decl_index = func.owner_decl;
1999 const decl = module.declPtr(decl_index);2010 const decl = module.declPtr(decl_index);
2011
2012 const atom_index = try self.getOrCreateAtomForDecl(decl_index);
2000 self.freeUnnamedConsts(decl_index);2013 self.freeUnnamedConsts(decl_index);
2001 self.freeRelocationsForAtom(&decl.link.macho);2014 Atom.freeRelocations(self, atom_index);
2015
2016 const atom = self.getAtom(atom_index);
20022017
2003 var code_buffer = std.ArrayList(u8).init(self.base.allocator);2018 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
2004 defer code_buffer.deinit();2019 defer code_buffer.deinit();
...@@ -2017,7 +2032,7 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv...@@ -2017,7 +2032,7 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv
2017 try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .none);2032 try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .none);
20182033
2019 const code = switch (res) {2034 const code = switch (res) {
2020 .appended => code_buffer.items,2035 .ok => code_buffer.items,
2021 .fail => |em| {2036 .fail => |em| {
2022 decl.analysis = .codegen_failure;2037 decl.analysis = .codegen_failure;
2023 try module.failed_decls.put(module.gpa, decl_index, em);2038 try module.failed_decls.put(module.gpa, decl_index, em);
...@@ -2028,13 +2043,7 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv...@@ -2028,13 +2043,7 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv
2028 const addr = try self.updateDeclCode(decl_index, code);2043 const addr = try self.updateDeclCode(decl_index, code);
20292044
2030 if (decl_state) |*ds| {2045 if (decl_state) |*ds| {
2031 try self.d_sym.?.dwarf.commitDeclState(2046 try self.d_sym.?.dwarf.commitDeclState(module, decl_index, addr, atom.size, ds);
2032 module,
2033 decl_index,
2034 addr,
2035 decl.link.macho.size,
2036 ds,
2037 );
2038 }2047 }
20392048
2040 // Since we updated the vaddr and the size, each corresponding export symbol also2049 // Since we updated the vaddr and the size, each corresponding export symbol also
...@@ -2069,21 +2078,13 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu...@@ -2069,21 +2078,13 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu
20692078
2070 log.debug("allocating symbol indexes for {?s}", .{name});2079 log.debug("allocating symbol indexes for {?s}", .{name});
20712080
2072 const atom = try gpa.create(Atom);2081 const atom_index = try self.createAtom();
2073 errdefer gpa.destroy(atom);
2074 atom.* = Atom.empty;
2075
2076 atom.sym_index = try self.allocateSymbol();
2077
2078 try self.managed_atoms.append(gpa, atom);
2079 try self.atom_by_index_table.putNoClobber(gpa, atom.sym_index, atom);
20802082
2081 const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), typed_value, &code_buffer, .none, .{2083 const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), typed_value, &code_buffer, .none, .{
2082 .parent_atom_index = atom.sym_index,2084 .parent_atom_index = self.getAtom(atom_index).getSymbolIndex().?,
2083 });2085 });
2084 const code = switch (res) {2086 const code = switch (res) {
2085 .externally_managed => |x| x,2087 .ok => code_buffer.items,
2086 .appended => code_buffer.items,
2087 .fail => |em| {2088 .fail => |em| {
2088 decl.analysis = .codegen_failure;2089 decl.analysis = .codegen_failure;
2089 try module.failed_decls.put(module.gpa, decl_index, em);2090 try module.failed_decls.put(module.gpa, decl_index, em);
...@@ -2093,26 +2094,27 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu...@@ -2093,26 +2094,27 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu
2093 };2094 };
20942095
2095 const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);2096 const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);
2097 const atom = self.getAtomPtr(atom_index);
2096 atom.size = code.len;2098 atom.size = code.len;
2097 atom.alignment = required_alignment;2099 atom.alignment = required_alignment;
2098 // TODO: work out logic for disambiguating functions from function pointers2100 // TODO: work out logic for disambiguating functions from function pointers
2099 // const sect_id = self.getDeclOutputSection(decl);2101 // const sect_id = self.getDeclOutputSection(decl_index);
2100 const sect_id = self.data_const_section_index.?;2102 const sect_id = self.data_const_section_index.?;
2101 const symbol = atom.getSymbolPtr(self);2103 const symbol = atom.getSymbolPtr(self);
2102 symbol.n_strx = name_str_index;2104 symbol.n_strx = name_str_index;
2103 symbol.n_type = macho.N_SECT;2105 symbol.n_type = macho.N_SECT;
2104 symbol.n_sect = sect_id + 1;2106 symbol.n_sect = sect_id + 1;
2105 symbol.n_value = try self.allocateAtom(atom, code.len, required_alignment);2107 symbol.n_value = try self.allocateAtom(atom_index, code.len, required_alignment);
2106 errdefer self.freeAtom(atom);2108 errdefer self.freeAtom(atom_index);
21072109
2108 try unnamed_consts.append(gpa, atom);2110 try unnamed_consts.append(gpa, atom_index);
21092111
2110 log.debug("allocated atom for {?s} at 0x{x}", .{ name, symbol.n_value });2112 log.debug("allocated atom for {?s} at 0x{x}", .{ name, symbol.n_value });
2111 log.debug(" (required alignment 0x{x})", .{required_alignment});2113 log.debug(" (required alignment 0x{x})", .{required_alignment});
21122114
2113 try self.writeAtom(atom, code);2115 try self.writeAtom(atom_index, code);
21142116
2115 return atom.sym_index;2117 return atom.getSymbolIndex().?;
2116}2118}
21172119
2118pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index) !void {2120pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index) !void {
...@@ -2137,7 +2139,9 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index)...@@ -2137,7 +2139,9 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index)
2137 }2139 }
2138 }2140 }
21392141
2140 self.freeRelocationsForAtom(&decl.link.macho);2142 const atom_index = try self.getOrCreateAtomForDecl(decl_index);
2143 Atom.freeRelocations(self, atom_index);
2144 const atom = self.getAtom(atom_index);
21412145
2142 var code_buffer = std.ArrayList(u8).init(self.base.allocator);2146 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
2143 defer code_buffer.deinit();2147 defer code_buffer.deinit();
...@@ -2156,19 +2160,18 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index)...@@ -2156,19 +2160,18 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index)
2156 }, &code_buffer, .{2160 }, &code_buffer, .{
2157 .dwarf = ds,2161 .dwarf = ds,
2158 }, .{2162 }, .{
2159 .parent_atom_index = decl.link.macho.sym_index,2163 .parent_atom_index = atom.getSymbolIndex().?,
2160 })2164 })
2161 else2165 else
2162 try codegen.generateSymbol(&self.base, decl.srcLoc(), .{2166 try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
2163 .ty = decl.ty,2167 .ty = decl.ty,
2164 .val = decl_val,2168 .val = decl_val,
2165 }, &code_buffer, .none, .{2169 }, &code_buffer, .none, .{
2166 .parent_atom_index = decl.link.macho.sym_index,2170 .parent_atom_index = atom.getSymbolIndex().?,
2167 });2171 });
21682172
2169 const code = switch (res) {2173 const code = switch (res) {
2170 .externally_managed => |x| x,2174 .ok => code_buffer.items,
2171 .appended => code_buffer.items,
2172 .fail => |em| {2175 .fail => |em| {
2173 decl.analysis = .codegen_failure;2176 decl.analysis = .codegen_failure;
2174 try module.failed_decls.put(module.gpa, decl_index, em);2177 try module.failed_decls.put(module.gpa, decl_index, em);
...@@ -2178,13 +2181,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index)...@@ -2178,13 +2181,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index)
2178 const addr = try self.updateDeclCode(decl_index, code);2181 const addr = try self.updateDeclCode(decl_index, code);
21792182
2180 if (decl_state) |*ds| {2183 if (decl_state) |*ds| {
2181 try self.d_sym.?.dwarf.commitDeclState(2184 try self.d_sym.?.dwarf.commitDeclState(module, decl_index, addr, atom.size, ds);
2182 module,
2183 decl_index,
2184 addr,
2185 decl.link.macho.size,
2186 ds,
2187 );
2188 }2185 }
21892186
2190 // Since we updated the vaddr and the size, each corresponding export symbol also2187 // Since we updated the vaddr and the size, each corresponding export symbol also
...@@ -2192,7 +2189,20 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index)...@@ -2192,7 +2189,20 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index)
2192 try self.updateDeclExports(module, decl_index, module.getDeclExports(decl_index));2189 try self.updateDeclExports(module, decl_index, module.getDeclExports(decl_index));
2193}2190}
21942191
2195fn getDeclOutputSection(self: *MachO, decl: *Module.Decl) u8 {2192pub fn getOrCreateAtomForDecl(self: *MachO, decl_index: Module.Decl.Index) !Atom.Index {
2193 const gop = try self.decls.getOrPut(self.base.allocator, decl_index);
2194 if (!gop.found_existing) {
2195 gop.value_ptr.* = .{
2196 .atom = try self.createAtom(),
2197 .section = self.getDeclOutputSection(decl_index),
2198 .exports = .{},
2199 };
2200 }
2201 return gop.value_ptr.atom;
2202}
2203
2204fn getDeclOutputSection(self: *MachO, decl_index: Module.Decl.Index) u8 {
2205 const decl = self.base.options.module.?.declPtr(decl_index);
2196 const ty = decl.ty;2206 const ty = decl.ty;
2197 const val = decl.val;2207 const val = decl.val;
2198 const zig_ty = ty.zigTypeTag();2208 const zig_ty = ty.zigTypeTag();
...@@ -2339,17 +2349,15 @@ fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []const u8)...@@ -2339,17 +2349,15 @@ fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []const u8)
2339 const decl = mod.declPtr(decl_index);2349 const decl = mod.declPtr(decl_index);
23402350
2341 const required_alignment = decl.getAlignment(self.base.options.target);2351 const required_alignment = decl.getAlignment(self.base.options.target);
2342 assert(decl.link.macho.sym_index != 0); // Caller forgot to call allocateDeclIndexes()
23432352
2344 const sym_name = try decl.getFullyQualifiedName(mod);2353 const sym_name = try decl.getFullyQualifiedName(mod);
2345 defer self.base.allocator.free(sym_name);2354 defer self.base.allocator.free(sym_name);
23462355
2347 const atom = &decl.link.macho;2356 const decl_metadata = self.decls.get(decl_index).?;
2348 const decl_ptr = self.decls.getPtr(decl_index).?;2357 const atom_index = decl_metadata.atom;
2349 if (decl_ptr.* == null) {2358 const atom = self.getAtom(atom_index);
2350 decl_ptr.* = self.getDeclOutputSection(decl);2359 const sym_index = atom.getSymbolIndex().?;
2351 }2360 const sect_id = decl_metadata.section;
2352 const sect_id = decl_ptr.*.?;
2353 const code_len = code.len;2361 const code_len = code.len;
23542362
2355 if (atom.size != 0) {2363 if (atom.size != 0) {
...@@ -2359,31 +2367,31 @@ fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []const u8)...@@ -2359,31 +2367,31 @@ fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []const u8)
2359 sym.n_sect = sect_id + 1;2367 sym.n_sect = sect_id + 1;
2360 sym.n_desc = 0;2368 sym.n_desc = 0;
23612369
2362 const capacity = decl.link.macho.capacity(self);2370 const capacity = atom.capacity(self);
2363 const need_realloc = code_len > capacity or !mem.isAlignedGeneric(u64, sym.n_value, required_alignment);2371 const need_realloc = code_len > capacity or !mem.isAlignedGeneric(u64, sym.n_value, required_alignment);
23642372
2365 if (need_realloc) {2373 if (need_realloc) {
2366 const vaddr = try self.growAtom(atom, code_len, required_alignment);2374 const vaddr = try self.growAtom(atom_index, code_len, required_alignment);
2367 log.debug("growing {s} and moving from 0x{x} to 0x{x}", .{ sym_name, sym.n_value, vaddr });2375 log.debug("growing {s} and moving from 0x{x} to 0x{x}", .{ sym_name, sym.n_value, vaddr });
2368 log.debug(" (required alignment 0x{x})", .{required_alignment});2376 log.debug(" (required alignment 0x{x})", .{required_alignment});
23692377
2370 if (vaddr != sym.n_value) {2378 if (vaddr != sym.n_value) {
2371 sym.n_value = vaddr;2379 sym.n_value = vaddr;
2372 log.debug(" (updating GOT entry)", .{});2380 log.debug(" (updating GOT entry)", .{});
2373 const got_target = SymbolWithLoc{ .sym_index = atom.sym_index, .file = null };2381 const got_target = SymbolWithLoc{ .sym_index = sym_index, .file = null };
2374 const got_atom = self.getGotAtomForSymbol(got_target).?;2382 const got_atom_index = self.getGotAtomIndexForSymbol(got_target).?;
2375 self.markRelocsDirtyByTarget(got_target);2383 self.markRelocsDirtyByTarget(got_target);
2376 try self.writePtrWidthAtom(got_atom);2384 try self.writePtrWidthAtom(got_atom_index);
2377 }2385 }
2378 } else if (code_len < atom.size) {2386 } else if (code_len < atom.size) {
2379 self.shrinkAtom(atom, code_len);2387 self.shrinkAtom(atom_index, code_len);
2380 } else if (atom.next == null) {2388 } else if (atom.next_index == null) {
2381 const header = &self.sections.items(.header)[sect_id];2389 const header = &self.sections.items(.header)[sect_id];
2382 const segment = self.getSegment(sect_id);2390 const segment = self.getSegment(sect_id);
2383 const needed_size = (sym.n_value + code_len) - segment.vmaddr;2391 const needed_size = (sym.n_value + code_len) - segment.vmaddr;
2384 header.size = needed_size;2392 header.size = needed_size;
2385 }2393 }
2386 atom.size = code_len;2394 self.getAtomPtr(atom_index).size = code_len;
2387 } else {2395 } else {
2388 const name_str_index = try self.strtab.insert(gpa, sym_name);2396 const name_str_index = try self.strtab.insert(gpa, sym_name);
2389 const sym = atom.getSymbolPtr(self);2397 const sym = atom.getSymbolPtr(self);
...@@ -2392,32 +2400,32 @@ fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []const u8)...@@ -2392,32 +2400,32 @@ fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []const u8)
2392 sym.n_sect = sect_id + 1;2400 sym.n_sect = sect_id + 1;
2393 sym.n_desc = 0;2401 sym.n_desc = 0;
23942402
2395 const vaddr = try self.allocateAtom(atom, code_len, required_alignment);2403 const vaddr = try self.allocateAtom(atom_index, code_len, required_alignment);
2396 errdefer self.freeAtom(atom);2404 errdefer self.freeAtom(atom_index);
23972405
2398 log.debug("allocated atom for {s} at 0x{x}", .{ sym_name, vaddr });2406 log.debug("allocated atom for {s} at 0x{x}", .{ sym_name, vaddr });
2399 log.debug(" (required alignment 0x{x})", .{required_alignment});2407 log.debug(" (required alignment 0x{x})", .{required_alignment});
24002408
2401 atom.size = code_len;2409 self.getAtomPtr(atom_index).size = code_len;
2402 sym.n_value = vaddr;2410 sym.n_value = vaddr;
24032411
2404 const got_target = SymbolWithLoc{ .sym_index = atom.sym_index, .file = null };2412 const got_target = SymbolWithLoc{ .sym_index = sym_index, .file = null };
2405 const got_index = try self.allocateGotEntry(got_target);2413 const got_index = try self.allocateGotEntry(got_target);
2406 const got_atom = try self.createGotAtom(got_target);2414 const got_atom_index = try self.createGotAtom(got_target);
2407 self.got_entries.items[got_index].sym_index = got_atom.sym_index;2415 const got_atom = self.getAtom(got_atom_index);
2408 try self.writePtrWidthAtom(got_atom);2416 self.got_entries.items[got_index].sym_index = got_atom.getSymbolIndex().?;
2417 try self.writePtrWidthAtom(got_atom_index);
2409 }2418 }
24102419
2411 self.markRelocsDirtyByTarget(atom.getSymbolWithLoc());2420 self.markRelocsDirtyByTarget(atom.getSymbolWithLoc());
2412 try self.writeAtom(atom, code);2421 try self.writeAtom(atom_index, code);
24132422
2414 return atom.getSymbol(self).n_value;2423 return atom.getSymbol(self).n_value;
2415}2424}
24162425
2417pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl: *const Module.Decl) !void {2426pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl_index: Module.Decl.Index) !void {
2418 _ = module;
2419 if (self.d_sym) |*d_sym| {2427 if (self.d_sym) |*d_sym| {
2420 try d_sym.dwarf.updateDeclLineNumber(decl);2428 try d_sym.dwarf.updateDeclLineNumber(module, decl_index);
2421 }2429 }
2422}2430}
24232431
...@@ -2434,14 +2442,17 @@ pub fn updateDeclExports(...@@ -2434,14 +2442,17 @@ pub fn updateDeclExports(
2434 if (self.llvm_object) |llvm_object|2442 if (self.llvm_object) |llvm_object|
2435 return llvm_object.updateDeclExports(module, decl_index, exports);2443 return llvm_object.updateDeclExports(module, decl_index, exports);
2436 }2444 }
2445
2437 const tracy = trace(@src());2446 const tracy = trace(@src());
2438 defer tracy.end();2447 defer tracy.end();
24392448
2440 const gpa = self.base.allocator;2449 const gpa = self.base.allocator;
24412450
2442 const decl = module.declPtr(decl_index);2451 const decl = module.declPtr(decl_index);
2443 if (decl.link.macho.sym_index == 0) return;2452 const atom_index = try self.getOrCreateAtomForDecl(decl_index);
2444 const decl_sym = decl.link.macho.getSymbol(self);2453 const atom = self.getAtom(atom_index);
2454 const decl_sym = atom.getSymbol(self);
2455 const decl_metadata = self.decls.getPtr(decl_index).?;
24452456
2446 for (exports) |exp| {2457 for (exports) |exp| {
2447 const exp_name = try std.fmt.allocPrint(gpa, "_{s}", .{exp.options.name});2458 const exp_name = try std.fmt.allocPrint(gpa, "_{s}", .{exp.options.name});
...@@ -2479,9 +2490,9 @@ pub fn updateDeclExports(...@@ -2479,9 +2490,9 @@ pub fn updateDeclExports(
2479 continue;2490 continue;
2480 }2491 }
24812492
2482 const sym_index = exp.link.macho.sym_index orelse blk: {2493 const sym_index = decl_metadata.getExport(self, exp_name) orelse blk: {
2483 const sym_index = try self.allocateSymbol();2494 const sym_index = try self.allocateSymbol();
2484 exp.link.macho.sym_index = sym_index;2495 try decl_metadata.exports.append(gpa, sym_index);
2485 break :blk sym_index;2496 break :blk sym_index;
2486 };2497 };
2487 const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = null };2498 const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = null };
...@@ -2529,16 +2540,18 @@ pub fn updateDeclExports(...@@ -2529,16 +2540,18 @@ pub fn updateDeclExports(
2529 }2540 }
2530}2541}
25312542
2532pub fn deleteExport(self: *MachO, exp: Export) void {2543pub fn deleteDeclExport(self: *MachO, decl_index: Module.Decl.Index, name: []const u8) Allocator.Error!void {
2533 if (self.llvm_object) |_| return;2544 if (self.llvm_object) |_| return;
2534 const sym_index = exp.sym_index orelse return;2545 const metadata = self.decls.getPtr(decl_index) orelse return;
25352546
2536 const gpa = self.base.allocator;2547 const gpa = self.base.allocator;
2548 const exp_name = try std.fmt.allocPrint(gpa, "_{s}", .{name});
2549 defer gpa.free(exp_name);
2550 const sym_index = metadata.getExportPtr(self, exp_name) orelse return;
25372551
2538 const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = null };2552 const sym_loc = SymbolWithLoc{ .sym_index = sym_index.*, .file = null };
2539 const sym = self.getSymbolPtr(sym_loc);2553 const sym = self.getSymbolPtr(sym_loc);
2540 const sym_name = self.getSymbolName(sym_loc);2554 log.debug("deleting export '{s}'", .{exp_name});
2541 log.debug("deleting export '{s}'", .{sym_name});
2542 assert(sym.sect() and sym.ext());2555 assert(sym.sect() and sym.ext());
2543 sym.* = .{2556 sym.* = .{
2544 .n_strx = 0,2557 .n_strx = 0,
...@@ -2547,9 +2560,9 @@ pub fn deleteExport(self: *MachO, exp: Export) void {...@@ -2547,9 +2560,9 @@ pub fn deleteExport(self: *MachO, exp: Export) void {
2547 .n_desc = 0,2560 .n_desc = 0,
2548 .n_value = 0,2561 .n_value = 0,
2549 };2562 };
2550 self.locals_free_list.append(gpa, sym_index) catch {};2563 self.locals_free_list.append(gpa, sym_index.*) catch {};
25512564
2552 if (self.resolver.fetchRemove(sym_name)) |entry| {2565 if (self.resolver.fetchRemove(exp_name)) |entry| {
2553 defer gpa.free(entry.key);2566 defer gpa.free(entry.key);
2554 self.globals_free_list.append(gpa, entry.value) catch {};2567 self.globals_free_list.append(gpa, entry.value) catch {};
2555 self.globals.items[entry.value] = .{2568 self.globals.items[entry.value] = .{
...@@ -2557,17 +2570,8 @@ pub fn deleteExport(self: *MachO, exp: Export) void {...@@ -2557,17 +2570,8 @@ pub fn deleteExport(self: *MachO, exp: Export) void {
2557 .file = null,2570 .file = null,
2558 };2571 };
2559 }2572 }
2560}
25612573
2562fn freeRelocationsForAtom(self: *MachO, atom: *Atom) void {2574 sym_index.* = 0;
2563 var removed_relocs = self.relocs.fetchOrderedRemove(atom);
2564 if (removed_relocs) |*relocs| relocs.value.deinit(self.base.allocator);
2565 var removed_rebases = self.rebases.fetchOrderedRemove(atom);
2566 if (removed_rebases) |*rebases| rebases.value.deinit(self.base.allocator);
2567 var removed_bindings = self.bindings.fetchOrderedRemove(atom);
2568 if (removed_bindings) |*bindings| bindings.value.deinit(self.base.allocator);
2569 var removed_lazy_bindings = self.lazy_bindings.fetchOrderedRemove(atom);
2570 if (removed_lazy_bindings) |*lazy_bindings| lazy_bindings.value.deinit(self.base.allocator);
2571}2575}
25722576
2573fn freeUnnamedConsts(self: *MachO, decl_index: Module.Decl.Index) void {2577fn freeUnnamedConsts(self: *MachO, decl_index: Module.Decl.Index) void {
...@@ -2575,11 +2579,6 @@ fn freeUnnamedConsts(self: *MachO, decl_index: Module.Decl.Index) void {...@@ -2575,11 +2579,6 @@ fn freeUnnamedConsts(self: *MachO, decl_index: Module.Decl.Index) void {
2575 const unnamed_consts = self.unnamed_const_atoms.getPtr(decl_index) orelse return;2579 const unnamed_consts = self.unnamed_const_atoms.getPtr(decl_index) orelse return;
2576 for (unnamed_consts.items) |atom| {2580 for (unnamed_consts.items) |atom| {
2577 self.freeAtom(atom);2581 self.freeAtom(atom);
2578 self.locals_free_list.append(gpa, atom.sym_index) catch {};
2579 self.locals.items[atom.sym_index].n_type = 0;
2580 _ = self.atom_by_index_table.remove(atom.sym_index);
2581 log.debug(" adding local symbol index {d} to free list", .{atom.sym_index});
2582 atom.sym_index = 0;
2583 }2582 }
2584 unnamed_consts.clearAndFree(gpa);2583 unnamed_consts.clearAndFree(gpa);
2585}2584}
...@@ -2593,67 +2592,37 @@ pub fn freeDecl(self: *MachO, decl_index: Module.Decl.Index) void {...@@ -2593,67 +2592,37 @@ pub fn freeDecl(self: *MachO, decl_index: Module.Decl.Index) void {
25932592
2594 log.debug("freeDecl {*}", .{decl});2593 log.debug("freeDecl {*}", .{decl});
25952594
2596 const kv = self.decls.fetchSwapRemove(decl_index);2595 if (self.decls.fetchSwapRemove(decl_index)) |const_kv| {
2597 if (kv.?.value) |_| {2596 var kv = const_kv;
2598 self.freeAtom(&decl.link.macho);2597 self.freeAtom(kv.value.atom);
2599 self.freeUnnamedConsts(decl_index);2598 self.freeUnnamedConsts(decl_index);
2600 }2599 kv.value.exports.deinit(self.base.allocator);
2601
2602 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
2603 const gpa = self.base.allocator;
2604 const sym_index = decl.link.macho.sym_index;
2605 if (sym_index != 0) {
2606 self.locals_free_list.append(gpa, sym_index) catch {};
2607
2608 // Try freeing GOT atom if this decl had one
2609 const got_target = SymbolWithLoc{ .sym_index = sym_index, .file = null };
2610 if (self.got_entries_table.get(got_target)) |got_index| {
2611 self.got_entries_free_list.append(gpa, @intCast(u32, got_index)) catch {};
2612 self.got_entries.items[got_index] = .{
2613 .target = .{ .sym_index = 0, .file = null },
2614 .sym_index = 0,
2615 };
2616 _ = self.got_entries_table.remove(got_target);
2617
2618 if (self.d_sym) |*d_sym| {
2619 d_sym.swapRemoveRelocs(sym_index);
2620 }
2621
2622 log.debug(" adding GOT index {d} to free list (target local@{d})", .{ got_index, sym_index });
2623 }
2624
2625 self.locals.items[sym_index].n_type = 0;
2626 _ = self.atom_by_index_table.remove(sym_index);
2627 log.debug(" adding local symbol index {d} to free list", .{sym_index});
2628 decl.link.macho.sym_index = 0;
2629 }2600 }
26302601
2631 if (self.d_sym) |*d_sym| {2602 if (self.d_sym) |*d_sym| {
2632 d_sym.dwarf.freeDecl(decl);2603 d_sym.dwarf.freeDecl(decl_index);
2633 }2604 }
2634}2605}
26352606
2636pub fn getDeclVAddr(self: *MachO, decl_index: Module.Decl.Index, reloc_info: File.RelocInfo) !u64 {2607pub fn getDeclVAddr(self: *MachO, decl_index: Module.Decl.Index, reloc_info: File.RelocInfo) !u64 {
2637 const mod = self.base.options.module.?;
2638 const decl = mod.declPtr(decl_index);
2639
2640 assert(self.llvm_object == null);2608 assert(self.llvm_object == null);
2641 assert(decl.link.macho.sym_index != 0);
26422609
2643 const atom = self.getAtomForSymbol(.{ .sym_index = reloc_info.parent_atom_index, .file = null }).?;2610 const this_atom_index = try self.getOrCreateAtomForDecl(decl_index);
2644 try atom.addRelocation(self, .{2611 const sym_index = self.getAtom(this_atom_index).getSymbolIndex().?;
2612 const atom_index = self.getAtomIndexForSymbol(.{ .sym_index = reloc_info.parent_atom_index, .file = null }).?;
2613 try Atom.addRelocation(self, atom_index, .{
2645 .type = switch (self.base.options.target.cpu.arch) {2614 .type = switch (self.base.options.target.cpu.arch) {
2646 .aarch64 => @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),2615 .aarch64 => @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),
2647 .x86_64 => @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED),2616 .x86_64 => @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_UNSIGNED),
2648 else => unreachable,2617 else => unreachable,
2649 },2618 },
2650 .target = .{ .sym_index = decl.link.macho.sym_index, .file = null },2619 .target = .{ .sym_index = sym_index, .file = null },
2651 .offset = @intCast(u32, reloc_info.offset),2620 .offset = @intCast(u32, reloc_info.offset),
2652 .addend = reloc_info.addend,2621 .addend = reloc_info.addend,
2653 .pcrel = false,2622 .pcrel = false,
2654 .length = 3,2623 .length = 3,
2655 });2624 });
2656 try atom.addRebase(self, @intCast(u32, reloc_info.offset));2625 try Atom.addRebase(self, atom_index, @intCast(u32, reloc_info.offset));
26572626
2658 return 0;2627 return 0;
2659}2628}
...@@ -2885,34 +2854,36 @@ fn moveSectionInVirtualMemory(self: *MachO, sect_id: u8, needed_size: u64) !void...@@ -2885,34 +2854,36 @@ fn moveSectionInVirtualMemory(self: *MachO, sect_id: u8, needed_size: u64) !void
2885 // TODO: enforce order by increasing VM addresses in self.sections container.2854 // TODO: enforce order by increasing VM addresses in self.sections container.
2886 for (self.sections.items(.header)[sect_id + 1 ..]) |*next_header, next_sect_id| {2855 for (self.sections.items(.header)[sect_id + 1 ..]) |*next_header, next_sect_id| {
2887 const index = @intCast(u8, sect_id + 1 + next_sect_id);2856 const index = @intCast(u8, sect_id + 1 + next_sect_id);
2888 const maybe_last_atom = &self.sections.items(.last_atom)[index];
2889 const next_segment = self.getSegmentPtr(index);2857 const next_segment = self.getSegmentPtr(index);
2890 next_header.addr += diff;2858 next_header.addr += diff;
2891 next_segment.vmaddr += diff;2859 next_segment.vmaddr += diff;
28922860
2893 if (maybe_last_atom.*) |last_atom| {2861 const maybe_last_atom_index = &self.sections.items(.last_atom_index)[index];
2894 var atom = last_atom;2862 if (maybe_last_atom_index.*) |last_atom_index| {
2863 var atom_index = last_atom_index;
2895 while (true) {2864 while (true) {
2865 const atom = self.getAtom(atom_index);
2896 const sym = atom.getSymbolPtr(self);2866 const sym = atom.getSymbolPtr(self);
2897 sym.n_value += diff;2867 sym.n_value += diff;
28982868
2899 if (atom.prev) |prev| {2869 if (atom.prev_index) |prev_index| {
2900 atom = prev;2870 atom_index = prev_index;
2901 } else break;2871 } else break;
2902 }2872 }
2903 }2873 }
2904 }2874 }
2905}2875}
29062876
2907fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) !u64 {2877fn allocateAtom(self: *MachO, atom_index: Atom.Index, new_atom_size: u64, alignment: u64) !u64 {
2908 const tracy = trace(@src());2878 const tracy = trace(@src());
2909 defer tracy.end();2879 defer tracy.end();
29102880
2881 const atom = self.getAtom(atom_index);
2911 const sect_id = atom.getSymbol(self).n_sect - 1;2882 const sect_id = atom.getSymbol(self).n_sect - 1;
2912 const segment = self.getSegmentPtr(sect_id);2883 const segment = self.getSegmentPtr(sect_id);
2913 const header = &self.sections.items(.header)[sect_id];2884 const header = &self.sections.items(.header)[sect_id];
2914 const free_list = &self.sections.items(.free_list)[sect_id];2885 const free_list = &self.sections.items(.free_list)[sect_id];
2915 const maybe_last_atom = &self.sections.items(.last_atom)[sect_id];2886 const maybe_last_atom_index = &self.sections.items(.last_atom_index)[sect_id];
2916 const requires_padding = blk: {2887 const requires_padding = blk: {
2917 if (!header.isCode()) break :blk false;2888 if (!header.isCode()) break :blk false;
2918 if (header.isSymbolStubs()) break :blk false;2889 if (header.isSymbolStubs()) break :blk false;
...@@ -2926,7 +2897,7 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) !...@@ -2926,7 +2897,7 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) !
2926 // It would be simpler to do it inside the for loop below, but that would cause a2897 // It would be simpler to do it inside the for loop below, but that would cause a
2927 // problem if an error was returned later in the function. So this action2898 // problem if an error was returned later in the function. So this action
2928 // is actually carried out at the end of the function, when errors are no longer possible.2899 // is actually carried out at the end of the function, when errors are no longer possible.
2929 var atom_placement: ?*Atom = null;2900 var atom_placement: ?Atom.Index = null;
2930 var free_list_removal: ?usize = null;2901 var free_list_removal: ?usize = null;
29312902
2932 // First we look for an appropriately sized free list node.2903 // First we look for an appropriately sized free list node.
...@@ -2934,7 +2905,8 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) !...@@ -2934,7 +2905,8 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) !
2934 var vaddr = blk: {2905 var vaddr = blk: {
2935 var i: usize = 0;2906 var i: usize = 0;
2936 while (i < free_list.items.len) {2907 while (i < free_list.items.len) {
2937 const big_atom = free_list.items[i];2908 const big_atom_index = free_list.items[i];
2909 const big_atom = self.getAtom(big_atom_index);
2938 // We now have a pointer to a live atom that has too much capacity.2910 // We now have a pointer to a live atom that has too much capacity.
2939 // Is it enough that we could fit this new atom?2911 // Is it enough that we could fit this new atom?
2940 const sym = big_atom.getSymbol(self);2912 const sym = big_atom.getSymbol(self);
...@@ -2962,30 +2934,35 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) !...@@ -2962,30 +2934,35 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) !
2962 const keep_free_list_node = remaining_capacity >= min_text_capacity;2934 const keep_free_list_node = remaining_capacity >= min_text_capacity;
29632935
2964 // Set up the metadata to be updated, after errors are no longer possible.2936 // Set up the metadata to be updated, after errors are no longer possible.
2965 atom_placement = big_atom;2937 atom_placement = big_atom_index;
2966 if (!keep_free_list_node) {2938 if (!keep_free_list_node) {
2967 free_list_removal = i;2939 free_list_removal = i;
2968 }2940 }
2969 break :blk new_start_vaddr;2941 break :blk new_start_vaddr;
2970 } else if (maybe_last_atom.*) |last| {2942 } else if (maybe_last_atom_index.*) |last_index| {
2943 const last = self.getAtom(last_index);
2971 const last_symbol = last.getSymbol(self);2944 const last_symbol = last.getSymbol(self);
2972 const ideal_capacity = if (requires_padding) padToIdeal(last.size) else last.size;2945 const ideal_capacity = if (requires_padding) padToIdeal(last.size) else last.size;
2973 const ideal_capacity_end_vaddr = last_symbol.n_value + ideal_capacity;2946 const ideal_capacity_end_vaddr = last_symbol.n_value + ideal_capacity;
2974 const new_start_vaddr = mem.alignForwardGeneric(u64, ideal_capacity_end_vaddr, alignment);2947 const new_start_vaddr = mem.alignForwardGeneric(u64, ideal_capacity_end_vaddr, alignment);
2975 atom_placement = last;2948 atom_placement = last_index;
2976 break :blk new_start_vaddr;2949 break :blk new_start_vaddr;
2977 } else {2950 } else {
2978 break :blk mem.alignForwardGeneric(u64, segment.vmaddr, alignment);2951 break :blk mem.alignForwardGeneric(u64, segment.vmaddr, alignment);
2979 }2952 }
2980 };2953 };
29812954
2982 const expand_section = atom_placement == null or atom_placement.?.next == null;2955 const expand_section = if (atom_placement) |placement_index|
2956 self.getAtom(placement_index).next_index == null
2957 else
2958 true;
2983 if (expand_section) {2959 if (expand_section) {
2984 const sect_capacity = self.allocatedSize(header.offset);2960 const sect_capacity = self.allocatedSize(header.offset);
2985 const needed_size = (vaddr + new_atom_size) - segment.vmaddr;2961 const needed_size = (vaddr + new_atom_size) - segment.vmaddr;
2986 if (needed_size > sect_capacity) {2962 if (needed_size > sect_capacity) {
2987 const new_offset = self.findFreeSpace(needed_size, self.page_size);2963 const new_offset = self.findFreeSpace(needed_size, self.page_size);
2988 const current_size = if (maybe_last_atom.*) |last_atom| blk: {2964 const current_size = if (maybe_last_atom_index.*) |last_atom_index| blk: {
2965 const last_atom = self.getAtom(last_atom_index);
2989 const sym = last_atom.getSymbol(self);2966 const sym = last_atom.getSymbol(self);
2990 break :blk (sym.n_value + last_atom.size) - segment.vmaddr;2967 break :blk (sym.n_value + last_atom.size) - segment.vmaddr;
2991 } else 0;2968 } else 0;
...@@ -3017,7 +2994,7 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) !...@@ -3017,7 +2994,7 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) !
3017 header.size = needed_size;2994 header.size = needed_size;
3018 segment.filesize = mem.alignForwardGeneric(u64, needed_size, self.page_size);2995 segment.filesize = mem.alignForwardGeneric(u64, needed_size, self.page_size);
3019 segment.vmsize = mem.alignForwardGeneric(u64, needed_size, self.page_size);2996 segment.vmsize = mem.alignForwardGeneric(u64, needed_size, self.page_size);
3020 maybe_last_atom.* = atom;2997 maybe_last_atom_index.* = atom_index;
30212998
3022 self.segment_table_dirty = true;2999 self.segment_table_dirty = true;
3023 }3000 }
...@@ -3026,21 +3003,31 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) !...@@ -3026,21 +3003,31 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) !
3026 if (header.@"align" < align_pow) {3003 if (header.@"align" < align_pow) {
3027 header.@"align" = align_pow;3004 header.@"align" = align_pow;
3028 }3005 }
3006 {
3007 const atom_ptr = self.getAtomPtr(atom_index);
3008 atom_ptr.size = new_atom_size;
3009 atom_ptr.alignment = @intCast(u32, alignment);
3010 }
30293011
3030 if (atom.prev) |prev| {3012 if (atom.prev_index) |prev_index| {
3031 prev.next = atom.next;3013 const prev = self.getAtomPtr(prev_index);
3014 prev.next_index = atom.next_index;
3032 }3015 }
3033 if (atom.next) |next| {3016 if (atom.next_index) |next_index| {
3034 next.prev = atom.prev;3017 const next = self.getAtomPtr(next_index);
3018 next.prev_index = atom.prev_index;
3035 }3019 }
30363020
3037 if (atom_placement) |big_atom| {3021 if (atom_placement) |big_atom_index| {
3038 atom.prev = big_atom;3022 const big_atom = self.getAtomPtr(big_atom_index);
3039 atom.next = big_atom.next;3023 const atom_ptr = self.getAtomPtr(atom_index);
3040 big_atom.next = atom;3024 atom_ptr.prev_index = big_atom_index;
3025 atom_ptr.next_index = big_atom.next_index;
3026 big_atom.next_index = atom_index;
3041 } else {3027 } else {
3042 atom.prev = null;3028 const atom_ptr = self.getAtomPtr(atom_index);
3043 atom.next = null;3029 atom_ptr.prev_index = null;
3030 atom_ptr.next_index = null;
3044 }3031 }
3045 if (free_list_removal) |i| {3032 if (free_list_removal) |i| {
3046 _ = free_list.swapRemove(i);3033 _ = free_list.swapRemove(i);
...@@ -3180,8 +3167,9 @@ fn collectRebaseData(self: *MachO, rebase: *Rebase) !void {...@@ -3180,8 +3167,9 @@ fn collectRebaseData(self: *MachO, rebase: *Rebase) !void {
3180 const gpa = self.base.allocator;3167 const gpa = self.base.allocator;
3181 const slice = self.sections.slice();3168 const slice = self.sections.slice();
31823169
3183 for (self.rebases.keys()) |atom, i| {3170 for (self.rebases.keys()) |atom_index, i| {
3184 log.debug(" ATOM(%{d}, '{s}')", .{ atom.sym_index, atom.getName(self) });3171 const atom = self.getAtom(atom_index);
3172 log.debug(" ATOM(%{?d}, '{s}')", .{ atom.getSymbolIndex(), atom.getName(self) });
31853173
3186 const sym = atom.getSymbol(self);3174 const sym = atom.getSymbol(self);
3187 const segment_index = slice.items(.segment_index)[sym.n_sect - 1];3175 const segment_index = slice.items(.segment_index)[sym.n_sect - 1];
...@@ -3209,8 +3197,9 @@ fn collectBindData(self: *MachO, bind: anytype, raw_bindings: anytype) !void {...@@ -3209,8 +3197,9 @@ fn collectBindData(self: *MachO, bind: anytype, raw_bindings: anytype) !void {
3209 const gpa = self.base.allocator;3197 const gpa = self.base.allocator;
3210 const slice = self.sections.slice();3198 const slice = self.sections.slice();
32113199
3212 for (raw_bindings.keys()) |atom, i| {3200 for (raw_bindings.keys()) |atom_index, i| {
3213 log.debug(" ATOM(%{d}, '{s}')", .{ atom.sym_index, atom.getName(self) });3201 const atom = self.getAtom(atom_index);
3202 log.debug(" ATOM(%{?d}, '{s}')", .{ atom.getSymbolIndex(), atom.getName(self) });
32143203
3215 const sym = atom.getSymbol(self);3204 const sym = atom.getSymbol(self);
3216 const segment_index = slice.items(.segment_index)[sym.n_sect - 1];3205 const segment_index = slice.items(.segment_index)[sym.n_sect - 1];
...@@ -3384,7 +3373,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, lazy_bind: LazyBind) !void...@@ -3384,7 +3373,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, lazy_bind: LazyBind) !void
3384 if (lazy_bind.size() == 0) return;3373 if (lazy_bind.size() == 0) return;
33853374
3386 const stub_helper_section_index = self.stub_helper_section_index.?;3375 const stub_helper_section_index = self.stub_helper_section_index.?;
3387 assert(self.stub_helper_preamble_atom != null);3376 assert(self.stub_helper_preamble_atom_index != null);
33883377
3389 const section = self.sections.get(stub_helper_section_index);3378 const section = self.sections.get(stub_helper_section_index);
33903379
...@@ -3394,10 +3383,11 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, lazy_bind: LazyBind) !void...@@ -3394,10 +3383,11 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, lazy_bind: LazyBind) !void
3394 else => unreachable,3383 else => unreachable,
3395 };3384 };
3396 const header = section.header;3385 const header = section.header;
3397 var atom = section.last_atom.?;3386 var atom_index = section.last_atom_index.?;
33983387
3399 var index: usize = lazy_bind.offsets.items.len;3388 var index: usize = lazy_bind.offsets.items.len;
3400 while (index > 0) : (index -= 1) {3389 while (index > 0) : (index -= 1) {
3390 const atom = self.getAtom(atom_index);
3401 const sym = atom.getSymbol(self);3391 const sym = atom.getSymbol(self);
3402 const file_offset = header.offset + sym.n_value - header.addr + stub_offset;3392 const file_offset = header.offset + sym.n_value - header.addr + stub_offset;
3403 const bind_offset = lazy_bind.offsets.items[index - 1];3393 const bind_offset = lazy_bind.offsets.items[index - 1];
...@@ -3410,7 +3400,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, lazy_bind: LazyBind) !void...@@ -3410,7 +3400,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, lazy_bind: LazyBind) !void
34103400
3411 try self.base.file.?.pwriteAll(mem.asBytes(&bind_offset), file_offset);3401 try self.base.file.?.pwriteAll(mem.asBytes(&bind_offset), file_offset);
34123402
3413 atom = atom.prev.?;3403 atom_index = atom.prev_index.?;
3414 }3404 }
3415}3405}
34163406
...@@ -3853,25 +3843,35 @@ pub fn getOrPutGlobalPtr(self: *MachO, name: []const u8) !GetOrPutGlobalPtrResul...@@ -3853,25 +3843,35 @@ pub fn getOrPutGlobalPtr(self: *MachO, name: []const u8) !GetOrPutGlobalPtrResul
3853 return GetOrPutGlobalPtrResult{ .found_existing = false, .value_ptr = ptr };3843 return GetOrPutGlobalPtrResult{ .found_existing = false, .value_ptr = ptr };
3854}3844}
38553845
3846pub fn getAtom(self: *MachO, atom_index: Atom.Index) Atom {
3847 assert(atom_index < self.atoms.items.len);
3848 return self.atoms.items[atom_index];
3849}
3850
3851pub fn getAtomPtr(self: *MachO, atom_index: Atom.Index) *Atom {
3852 assert(atom_index < self.atoms.items.len);
3853 return &self.atoms.items[atom_index];
3854}
3855
3856/// Returns atom if there is an atom referenced by the symbol described by `sym_with_loc` descriptor.3856/// Returns atom if there is an atom referenced by the symbol described by `sym_with_loc` descriptor.
3857/// Returns null on failure.3857/// Returns null on failure.
3858pub fn getAtomForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?*Atom {3858pub fn getAtomIndexForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?Atom.Index {
3859 assert(sym_with_loc.file == null);3859 assert(sym_with_loc.file == null);
3860 return self.atom_by_index_table.get(sym_with_loc.sym_index);3860 return self.atom_by_index_table.get(sym_with_loc.sym_index);
3861}3861}
38623862
3863/// Returns GOT atom that references `sym_with_loc` if one exists.3863/// Returns GOT atom that references `sym_with_loc` if one exists.
3864/// Returns null otherwise.3864/// Returns null otherwise.
3865pub fn getGotAtomForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?*Atom {3865pub fn getGotAtomIndexForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?Atom.Index {
3866 const got_index = self.got_entries_table.get(sym_with_loc) orelse return null;3866 const got_index = self.got_entries_table.get(sym_with_loc) orelse return null;
3867 return self.got_entries.items[got_index].getAtom(self);3867 return self.got_entries.items[got_index].getAtomIndex(self);
3868}3868}
38693869
3870/// Returns stubs atom that references `sym_with_loc` if one exists.3870/// Returns stubs atom that references `sym_with_loc` if one exists.
3871/// Returns null otherwise.3871/// Returns null otherwise.
3872pub fn getStubsAtomForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?*Atom {3872pub fn getStubsAtomIndexForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?Atom.Index {
3873 const stubs_index = self.stubs_table.get(sym_with_loc) orelse return null;3873 const stubs_index = self.stubs_table.get(sym_with_loc) orelse return null;
3874 return self.stubs.items[stubs_index].getAtom(self);3874 return self.stubs.items[stubs_index].getAtomIndex(self);
3875}3875}
38763876
3877/// Returns symbol location corresponding to the set entrypoint.3877/// Returns symbol location corresponding to the set entrypoint.
...@@ -4257,30 +4257,35 @@ pub fn logAtoms(self: *MachO) void {...@@ -4257,30 +4257,35 @@ pub fn logAtoms(self: *MachO) void {
4257 log.debug("atoms:", .{});4257 log.debug("atoms:", .{});
42584258
4259 const slice = self.sections.slice();4259 const slice = self.sections.slice();
4260 for (slice.items(.last_atom)) |last, i| {4260 for (slice.items(.last_atom_index)) |last_atom_index, i| {
4261 var atom = last orelse continue;4261 var atom_index = last_atom_index orelse continue;
4262 const header = slice.items(.header)[i];4262 const header = slice.items(.header)[i];
42634263
4264 while (atom.prev) |prev| {4264 while (true) {
4265 atom = prev;4265 const atom = self.getAtom(atom_index);
4266 if (atom.prev_index) |prev_index| {
4267 atom_index = prev_index;
4268 } else break;
4266 }4269 }
42674270
4268 log.debug("{s},{s}", .{ header.segName(), header.sectName() });4271 log.debug("{s},{s}", .{ header.segName(), header.sectName() });
42694272
4270 while (true) {4273 while (true) {
4271 self.logAtom(atom);4274 self.logAtom(atom_index);
4272 if (atom.next) |next| {4275 const atom = self.getAtom(atom_index);
4273 atom = next;4276 if (atom.next_index) |next_index| {
4277 atom_index = next_index;
4274 } else break;4278 } else break;
4275 }4279 }
4276 }4280 }
4277}4281}
42784282
4279pub fn logAtom(self: *MachO, atom: *const Atom) void {4283pub fn logAtom(self: *MachO, atom_index: Atom.Index) void {
4284 const atom = self.getAtom(atom_index);
4280 const sym = atom.getSymbol(self);4285 const sym = atom.getSymbol(self);
4281 const sym_name = atom.getName(self);4286 const sym_name = atom.getName(self);
4282 log.debug(" ATOM(%{d}, '{s}') @ {x} (sizeof({x}), alignof({x})) in object({?d}) in sect({d})", .{4287 log.debug(" ATOM(%{?d}, '{s}') @ {x} (sizeof({x}), alignof({x})) in object({?d}) in sect({d})", .{
4283 atom.sym_index,4288 atom.getSymbolIndex(),
4284 sym_name,4289 sym_name,
4285 sym.n_value,4290 sym.n_value,
4286 atom.size,4291 atom.size,
src/link/MachO/Atom.zig+54-38
...@@ -13,7 +13,6 @@ const trace = @import("../../tracy.zig").trace;...@@ -13,7 +13,6 @@ const trace = @import("../../tracy.zig").trace;
1313
14const Allocator = mem.Allocator;14const Allocator = mem.Allocator;
15const Arch = std.Target.Cpu.Arch;15const Arch = std.Target.Cpu.Arch;
16const Dwarf = @import("../Dwarf.zig");
17const MachO = @import("../MachO.zig");16const MachO = @import("../MachO.zig");
18const Relocation = @import("Relocation.zig");17const Relocation = @import("Relocation.zig");
19const SymbolWithLoc = MachO.SymbolWithLoc;18const SymbolWithLoc = MachO.SymbolWithLoc;
...@@ -39,10 +38,11 @@ size: u64,...@@ -39,10 +38,11 @@ size: u64,
39alignment: u32,38alignment: u32,
4039
41/// Points to the previous and next neighbours40/// Points to the previous and next neighbours
42next: ?*Atom,41/// TODO use the same trick as with symbols: reserve index 0 as null atom
43prev: ?*Atom,42next_index: ?Index,
43prev_index: ?Index,
4444
45dbg_info_atom: Dwarf.Atom,45pub const Index = u32;
4646
47pub const Binding = struct {47pub const Binding = struct {
48 target: SymbolWithLoc,48 target: SymbolWithLoc,
...@@ -54,15 +54,10 @@ pub const SymbolAtOffset = struct {...@@ -54,15 +54,10 @@ pub const SymbolAtOffset = struct {
54 offset: u64,54 offset: u64,
55};55};
5656
57pub const empty = Atom{57pub fn getSymbolIndex(self: Atom) ?u32 {
58 .sym_index = 0,58 if (self.sym_index == 0) return null;
59 .file = null,59 return self.sym_index;
60 .size = 0,60}
61 .alignment = 0,
62 .prev = null,
63 .next = null,
64 .dbg_info_atom = undefined,
65};
6661
67/// Returns symbol referencing this atom.62/// Returns symbol referencing this atom.
68pub fn getSymbol(self: Atom, macho_file: *MachO) macho.nlist_64 {63pub fn getSymbol(self: Atom, macho_file: *MachO) macho.nlist_64 {
...@@ -71,20 +66,23 @@ pub fn getSymbol(self: Atom, macho_file: *MachO) macho.nlist_64 {...@@ -71,20 +66,23 @@ pub fn getSymbol(self: Atom, macho_file: *MachO) macho.nlist_64 {
7166
72/// Returns pointer-to-symbol referencing this atom.67/// Returns pointer-to-symbol referencing this atom.
73pub fn getSymbolPtr(self: Atom, macho_file: *MachO) *macho.nlist_64 {68pub fn getSymbolPtr(self: Atom, macho_file: *MachO) *macho.nlist_64 {
69 const sym_index = self.getSymbolIndex().?;
74 return macho_file.getSymbolPtr(.{70 return macho_file.getSymbolPtr(.{
75 .sym_index = self.sym_index,71 .sym_index = sym_index,
76 .file = self.file,72 .file = self.file,
77 });73 });
78}74}
7975
80pub fn getSymbolWithLoc(self: Atom) SymbolWithLoc {76pub fn getSymbolWithLoc(self: Atom) SymbolWithLoc {
81 return .{ .sym_index = self.sym_index, .file = self.file };77 const sym_index = self.getSymbolIndex().?;
78 return .{ .sym_index = sym_index, .file = self.file };
82}79}
8380
84/// Returns the name of this atom.81/// Returns the name of this atom.
85pub fn getName(self: Atom, macho_file: *MachO) []const u8 {82pub fn getName(self: Atom, macho_file: *MachO) []const u8 {
83 const sym_index = self.getSymbolIndex().?;
86 return macho_file.getSymbolName(.{84 return macho_file.getSymbolName(.{
87 .sym_index = self.sym_index,85 .sym_index = sym_index,
88 .file = self.file,86 .file = self.file,
89 });87 });
90}88}
...@@ -94,7 +92,8 @@ pub fn getName(self: Atom, macho_file: *MachO) []const u8 {...@@ -94,7 +92,8 @@ pub fn getName(self: Atom, macho_file: *MachO) []const u8 {
94/// this calculation.92/// this calculation.
95pub fn capacity(self: Atom, macho_file: *MachO) u64 {93pub fn capacity(self: Atom, macho_file: *MachO) u64 {
96 const self_sym = self.getSymbol(macho_file);94 const self_sym = self.getSymbol(macho_file);
97 if (self.next) |next| {95 if (self.next_index) |next_index| {
96 const next = macho_file.getAtom(next_index);
98 const next_sym = next.getSymbol(macho_file);97 const next_sym = next.getSymbol(macho_file);
99 return next_sym.n_value - self_sym.n_value;98 return next_sym.n_value - self_sym.n_value;
100 } else {99 } else {
...@@ -106,7 +105,8 @@ pub fn capacity(self: Atom, macho_file: *MachO) u64 {...@@ -106,7 +105,8 @@ pub fn capacity(self: Atom, macho_file: *MachO) u64 {
106105
107pub fn freeListEligible(self: Atom, macho_file: *MachO) bool {106pub fn freeListEligible(self: Atom, macho_file: *MachO) bool {
108 // No need to keep a free list node for the last atom.107 // No need to keep a free list node for the last atom.
109 const next = self.next orelse return false;108 const next_index = self.next_index orelse return false;
109 const next = macho_file.getAtom(next_index);
110 const self_sym = self.getSymbol(macho_file);110 const self_sym = self.getSymbol(macho_file);
111 const next_sym = next.getSymbol(macho_file);111 const next_sym = next.getSymbol(macho_file);
112 const cap = next_sym.n_value - self_sym.n_value;112 const cap = next_sym.n_value - self_sym.n_value;
...@@ -116,19 +116,19 @@ pub fn freeListEligible(self: Atom, macho_file: *MachO) bool {...@@ -116,19 +116,19 @@ pub fn freeListEligible(self: Atom, macho_file: *MachO) bool {
116 return surplus >= MachO.min_text_capacity;116 return surplus >= MachO.min_text_capacity;
117}117}
118118
119pub fn addRelocation(self: *Atom, macho_file: *MachO, reloc: Relocation) !void {119pub fn addRelocation(macho_file: *MachO, atom_index: Index, reloc: Relocation) !void {
120 return self.addRelocations(macho_file, 1, .{reloc});120 return addRelocations(macho_file, atom_index, 1, .{reloc});
121}121}
122122
123pub fn addRelocations(123pub fn addRelocations(
124 self: *Atom,
125 macho_file: *MachO,124 macho_file: *MachO,
125 atom_index: Index,
126 comptime count: comptime_int,126 comptime count: comptime_int,
127 relocs: [count]Relocation,127 relocs: [count]Relocation,
128) !void {128) !void {
129 const gpa = macho_file.base.allocator;129 const gpa = macho_file.base.allocator;
130 const target = macho_file.base.options.target;130 const target = macho_file.base.options.target;
131 const gop = try macho_file.relocs.getOrPut(gpa, self);131 const gop = try macho_file.relocs.getOrPut(gpa, atom_index);
132 if (!gop.found_existing) {132 if (!gop.found_existing) {
133 gop.value_ptr.* = .{};133 gop.value_ptr.* = .{};
134 }134 }
...@@ -142,56 +142,72 @@ pub fn addRelocations(...@@ -142,56 +142,72 @@ pub fn addRelocations(
142 }142 }
143}143}
144144
145pub fn addRebase(self: *Atom, macho_file: *MachO, offset: u32) !void {145pub fn addRebase(macho_file: *MachO, atom_index: Index, offset: u32) !void {
146 const gpa = macho_file.base.allocator;146 const gpa = macho_file.base.allocator;
147 log.debug(" (adding rebase at offset 0x{x} in %{d})", .{ offset, self.sym_index });147 const atom = macho_file.getAtom(atom_index);
148 const gop = try macho_file.rebases.getOrPut(gpa, self);148 log.debug(" (adding rebase at offset 0x{x} in %{?d})", .{ offset, atom.getSymbolIndex() });
149 const gop = try macho_file.rebases.getOrPut(gpa, atom_index);
149 if (!gop.found_existing) {150 if (!gop.found_existing) {
150 gop.value_ptr.* = .{};151 gop.value_ptr.* = .{};
151 }152 }
152 try gop.value_ptr.append(gpa, offset);153 try gop.value_ptr.append(gpa, offset);
153}154}
154155
155pub fn addBinding(self: *Atom, macho_file: *MachO, binding: Binding) !void {156pub fn addBinding(macho_file: *MachO, atom_index: Index, binding: Binding) !void {
156 const gpa = macho_file.base.allocator;157 const gpa = macho_file.base.allocator;
157 log.debug(" (adding binding to symbol {s} at offset 0x{x} in %{d})", .{158 const atom = macho_file.getAtom(atom_index);
159 log.debug(" (adding binding to symbol {s} at offset 0x{x} in %{?d})", .{
158 macho_file.getSymbolName(binding.target),160 macho_file.getSymbolName(binding.target),
159 binding.offset,161 binding.offset,
160 self.sym_index,162 atom.getSymbolIndex(),
161 });163 });
162 const gop = try macho_file.bindings.getOrPut(gpa, self);164 const gop = try macho_file.bindings.getOrPut(gpa, atom_index);
163 if (!gop.found_existing) {165 if (!gop.found_existing) {
164 gop.value_ptr.* = .{};166 gop.value_ptr.* = .{};
165 }167 }
166 try gop.value_ptr.append(gpa, binding);168 try gop.value_ptr.append(gpa, binding);
167}169}
168170
169pub fn addLazyBinding(self: *Atom, macho_file: *MachO, binding: Binding) !void {171pub fn addLazyBinding(macho_file: *MachO, atom_index: Index, binding: Binding) !void {
170 const gpa = macho_file.base.allocator;172 const gpa = macho_file.base.allocator;
171 log.debug(" (adding lazy binding to symbol {s} at offset 0x{x} in %{d})", .{173 const atom = macho_file.getAtom(atom_index);
174 log.debug(" (adding lazy binding to symbol {s} at offset 0x{x} in %{?d})", .{
172 macho_file.getSymbolName(binding.target),175 macho_file.getSymbolName(binding.target),
173 binding.offset,176 binding.offset,
174 self.sym_index,177 atom.getSymbolIndex(),
175 });178 });
176 const gop = try macho_file.lazy_bindings.getOrPut(gpa, self);179 const gop = try macho_file.lazy_bindings.getOrPut(gpa, atom_index);
177 if (!gop.found_existing) {180 if (!gop.found_existing) {
178 gop.value_ptr.* = .{};181 gop.value_ptr.* = .{};
179 }182 }
180 try gop.value_ptr.append(gpa, binding);183 try gop.value_ptr.append(gpa, binding);
181}184}
182185
183pub fn resolveRelocations(self: *Atom, macho_file: *MachO) !void {186pub fn resolveRelocations(macho_file: *MachO, atom_index: Index) !void {
184 const relocs = macho_file.relocs.get(self) orelse return;187 const atom = macho_file.getAtom(atom_index);
185 const source_sym = self.getSymbol(macho_file);188 const relocs = macho_file.relocs.get(atom_index) orelse return;
189 const source_sym = atom.getSymbol(macho_file);
186 const source_section = macho_file.sections.get(source_sym.n_sect - 1).header;190 const source_section = macho_file.sections.get(source_sym.n_sect - 1).header;
187 const file_offset = source_section.offset + source_sym.n_value - source_section.addr;191 const file_offset = source_section.offset + source_sym.n_value - source_section.addr;
188192
189 log.debug("relocating '{s}'", .{self.getName(macho_file)});193 log.debug("relocating '{s}'", .{atom.getName(macho_file)});
190194
191 for (relocs.items) |*reloc| {195 for (relocs.items) |*reloc| {
192 if (!reloc.dirty) continue;196 if (!reloc.dirty) continue;
193197
194 try reloc.resolve(self, macho_file, file_offset);198 try reloc.resolve(macho_file, atom_index, file_offset);
195 reloc.dirty = false;199 reloc.dirty = false;
196 }200 }
197}201}
202
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 {...@@ -82,11 +82,11 @@ pub fn populateMissingMetadata(self: *DebugSymbols) !void {
82 }82 }
8383
84 if (self.debug_str_section_index == null) {84 if (self.debug_str_section_index == null) {
85 assert(self.dwarf.strtab.items.len == 0);85 assert(self.dwarf.strtab.buffer.items.len == 0);
86 try self.dwarf.strtab.append(self.allocator, 0);86 try self.dwarf.strtab.buffer.append(self.allocator, 0);
87 self.debug_str_section_index = try self.allocateSection(87 self.debug_str_section_index = try self.allocateSection(
88 "__debug_str",88 "__debug_str",
89 @intCast(u32, self.dwarf.strtab.items.len),89 @intCast(u32, self.dwarf.strtab.buffer.items.len),
90 0,90 0,
91 );91 );
92 self.debug_string_table_dirty = true;92 self.debug_string_table_dirty = true;
...@@ -291,10 +291,10 @@ pub fn flushModule(self: *DebugSymbols, macho_file: *MachO) !void {...@@ -291,10 +291,10 @@ pub fn flushModule(self: *DebugSymbols, macho_file: *MachO) !void {
291291
292 {292 {
293 const sect_index = self.debug_str_section_index.?;293 const sect_index = self.debug_str_section_index.?;
294 if (self.debug_string_table_dirty or self.dwarf.strtab.items.len != self.getSection(sect_index).size) {294 if (self.debug_string_table_dirty or self.dwarf.strtab.buffer.items.len != self.getSection(sect_index).size) {
295 const needed_size = @intCast(u32, self.dwarf.strtab.items.len);295 const needed_size = @intCast(u32, self.dwarf.strtab.buffer.items.len);
296 try self.growSection(sect_index, needed_size, false);296 try self.growSection(sect_index, needed_size, false);
297 try self.file.pwriteAll(self.dwarf.strtab.items, self.getSection(sect_index).offset);297 try self.file.pwriteAll(self.dwarf.strtab.buffer.items, self.getSection(sect_index).offset);
298 self.debug_string_table_dirty = false;298 self.debug_string_table_dirty = false;
299 }299 }
300 }300 }
src/link/MachO/Relocation.zig+9-7
...@@ -29,33 +29,35 @@ pub fn fmtType(self: Relocation, target: std.Target) []const u8 {...@@ -29,33 +29,35 @@ pub fn fmtType(self: Relocation, target: std.Target) []const u8 {
29 }29 }
30}30}
3131
32pub fn getTargetAtom(self: Relocation, macho_file: *MachO) ?*Atom {32pub fn getTargetAtomIndex(self: Relocation, macho_file: *MachO) ?Atom.Index {
33 switch (macho_file.base.options.target.cpu.arch) {33 switch (macho_file.base.options.target.cpu.arch) {
34 .aarch64 => switch (@intToEnum(macho.reloc_type_arm64, self.type)) {34 .aarch64 => switch (@intToEnum(macho.reloc_type_arm64, self.type)) {
35 .ARM64_RELOC_GOT_LOAD_PAGE21,35 .ARM64_RELOC_GOT_LOAD_PAGE21,
36 .ARM64_RELOC_GOT_LOAD_PAGEOFF12,36 .ARM64_RELOC_GOT_LOAD_PAGEOFF12,
37 .ARM64_RELOC_POINTER_TO_GOT,37 .ARM64_RELOC_POINTER_TO_GOT,
38 => return macho_file.getGotAtomForSymbol(self.target),38 => return macho_file.getGotAtomIndexForSymbol(self.target),
39 else => {},39 else => {},
40 },40 },
41 .x86_64 => switch (@intToEnum(macho.reloc_type_x86_64, self.type)) {41 .x86_64 => switch (@intToEnum(macho.reloc_type_x86_64, self.type)) {
42 .X86_64_RELOC_GOT,42 .X86_64_RELOC_GOT,
43 .X86_64_RELOC_GOT_LOAD,43 .X86_64_RELOC_GOT_LOAD,
44 => return macho_file.getGotAtomForSymbol(self.target),44 => return macho_file.getGotAtomIndexForSymbol(self.target),
45 else => {},45 else => {},
46 },46 },
47 else => unreachable,47 else => unreachable,
48 }48 }
49 if (macho_file.getStubsAtomForSymbol(self.target)) |stubs_atom| return stubs_atom;49 if (macho_file.getStubsAtomIndexForSymbol(self.target)) |stubs_atom| return stubs_atom;
50 return macho_file.getAtomForSymbol(self.target);50 return macho_file.getAtomIndexForSymbol(self.target);
51}51}
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 {
54 const arch = macho_file.base.options.target.cpu.arch;54 const arch = macho_file.base.options.target.cpu.arch;
55 const atom = macho_file.getAtom(atom_index);
55 const source_sym = atom.getSymbol(macho_file);56 const source_sym = atom.getSymbol(macho_file);
56 const source_addr = source_sym.n_value + self.offset;57 const source_addr = source_sym.n_value + self.offset;
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);
59 const target_addr = @intCast(i64, target_atom.getSymbol(macho_file).n_value) + self.addend;61 const target_addr = @intCast(i64, target_atom.getSymbol(macho_file).n_value) + self.addend;
6062
61 log.debug(" ({x}: [() => 0x{x} ({s})) ({s})", .{63 log.debug(" ({x}: [() => 0x{x} ({s})) ({s})", .{
src/link/MachO/load_commands.zig+1-1
...@@ -12,7 +12,7 @@ pub const default_dyld_path: [*:0]const u8 = "/usr/lib/dyld";...@@ -12,7 +12,7 @@ pub const default_dyld_path: [*:0]const u8 = "/usr/lib/dyld";
1212
13fn calcInstallNameLen(cmd_size: u64, name: []const u8, assume_max_path_len: bool) u64 {13fn calcInstallNameLen(cmd_size: u64, name: []const u8, assume_max_path_len: bool) u64 {
14 const darwin_path_max = 1024;14 const darwin_path_max = 1024;
15 const name_len = if (assume_max_path_len) darwin_path_max else std.mem.len(name) + 1;15 const name_len = if (assume_max_path_len) darwin_path_max else name.len + 1;
16 return mem.alignForwardGeneric(u64, cmd_size + name_len, @alignOf(u64));16 return mem.alignForwardGeneric(u64, cmd_size + name_len, @alignOf(u64));
17}17}
1818
src/link/MachO/zld.zig+7-4
...@@ -3596,7 +3596,8 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr...@@ -3596,7 +3596,8 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
3596 man.hash.addOptionalBytes(options.sysroot);3596 man.hash.addOptionalBytes(options.sysroot);
3597 try man.addOptionalFile(options.entitlements);3597 try man.addOptionalFile(options.entitlements);
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.
3600 _ = try man.hit();3601 _ = try man.hit();
3601 digest = man.final();3602 digest = man.final();
36023603
...@@ -4177,9 +4178,11 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr...@@ -4177,9 +4178,11 @@ pub fn linkWithZld(macho_file: *MachO, comp: *Compilation, prog_node: *std.Progr
4177 log.debug("failed to save linking hash digest file: {s}", .{@errorName(err)});4178 log.debug("failed to save linking hash digest file: {s}", .{@errorName(err)});
4178 };4179 };
4179 // Again failure here only means an unnecessary cache miss.4180 // Again failure here only means an unnecessary cache miss.
4180 man.writeManifest() catch |err| {4181 if (man.have_exclusive_lock) {
4181 log.debug("failed to write cache manifest when linking: {s}", .{@errorName(err)});4182 man.writeManifest() catch |err| {
4182 };4183 log.debug("failed to write cache manifest when linking: {s}", .{@errorName(err)});
4184 };
4185 }
4183 // We hang on to this lock so that the output file path can be used without4186 // We hang on to this lock so that the output file path can be used without
4184 // other processes clobbering it.4187 // other processes clobbering it.
4185 macho_file.base.lock = man.toOwnedLock();4188 macho_file.base.lock = man.toOwnedLock();
src/link/Plan9.zig+154-95
...@@ -21,14 +21,7 @@ const Allocator = std.mem.Allocator;...@@ -21,14 +21,7 @@ const Allocator = std.mem.Allocator;
21const log = std.log.scoped(.link);21const log = std.log.scoped(.link);
22const assert = std.debug.assert;22const assert = std.debug.assert;
2323
24const FnDeclOutput = struct {24pub const base_tag = .plan9;
25 /// this code is modified when relocated so it is mutable
26 code: []u8,
27 /// this might have to be modified in the linker, so thats why its mutable
28 lineinfo: []u8,
29 start_line: u32,
30 end_line: u32,
31};
3225
33base: link.File,26base: link.File,
34sixtyfour_bit: bool,27sixtyfour_bit: bool,
...@@ -101,6 +94,9 @@ got_index_free_list: std.ArrayListUnmanaged(usize) = .{},...@@ -101,6 +94,9 @@ got_index_free_list: std.ArrayListUnmanaged(usize) = .{},
10194
102syms_index_free_list: std.ArrayListUnmanaged(usize) = .{},95syms_index_free_list: std.ArrayListUnmanaged(usize) = .{},
10396
97decl_blocks: std.ArrayListUnmanaged(DeclBlock) = .{},
98decls: std.AutoHashMapUnmanaged(Module.Decl.Index, DeclMetadata) = .{},
99
104const Reloc = struct {100const Reloc = struct {
105 target: Module.Decl.Index,101 target: Module.Decl.Index,
106 offset: u64,102 offset: u64,
...@@ -115,6 +111,42 @@ const Bases = struct {...@@ -115,6 +111,42 @@ const Bases = struct {
115111
116const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(struct { info: DeclBlock, code: []const u8 }));112const 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
118fn getAddr(self: Plan9, addr: u64, t: aout.Sym.Type) u64 {150fn getAddr(self: Plan9, addr: u64, t: aout.Sym.Type) u64 {
119 return addr + switch (t) {151 return addr + switch (t) {
120 .T, .t, .l, .L => self.bases.text,152 .T, .t, .l, .L => self.bases.text,
...@@ -127,22 +159,6 @@ fn getSymAddr(self: Plan9, s: aout.Sym) u64 {...@@ -127,22 +159,6 @@ fn getSymAddr(self: Plan9, s: aout.Sym) u64 {
127 return self.getAddr(s.value, s.type);159 return self.getAddr(s.value, s.type);
128}160}
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
146pub fn defaultBaseAddrs(arch: std.Target.Cpu.Arch) Bases {162pub fn defaultBaseAddrs(arch: std.Target.Cpu.Arch) Bases {
147 return switch (arch) {163 return switch (arch) {
148 .x86_64 => .{164 .x86_64 => .{
...@@ -164,8 +180,6 @@ pub fn defaultBaseAddrs(arch: std.Target.Cpu.Arch) Bases {...@@ -164,8 +180,6 @@ pub fn defaultBaseAddrs(arch: std.Target.Cpu.Arch) Bases {
164 };180 };
165}181}
166182
167pub const PtrWidth = enum { p32, p64 };
168
169pub fn createEmpty(gpa: Allocator, options: link.Options) !*Plan9 {183pub fn createEmpty(gpa: Allocator, options: link.Options) !*Plan9 {
170 if (options.use_llvm)184 if (options.use_llvm)
171 return error.LLVMBackendDoesNotSupportPlan9;185 return error.LLVMBackendDoesNotSupportPlan9;
...@@ -271,7 +285,7 @@ pub fn updateFunc(self: *Plan9, module: *Module, func: *Module.Fn, air: Air, liv...@@ -271,7 +285,7 @@ pub fn updateFunc(self: *Plan9, module: *Module, func: *Module.Fn, air: Air, liv
271 const decl = module.declPtr(decl_index);285 const decl = module.declPtr(decl_index);
272 self.freeUnnamedConsts(decl_index);286 self.freeUnnamedConsts(decl_index);
273287
274 try self.seeDecl(decl_index);288 _ = try self.seeDecl(decl_index);
275 log.debug("codegen decl {*} ({s})", .{ decl, decl.name });289 log.debug("codegen decl {*} ({s})", .{ decl, decl.name });
276290
277 var code_buffer = std.ArrayList(u8).init(self.base.allocator);291 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
...@@ -299,7 +313,7 @@ pub fn updateFunc(self: *Plan9, module: *Module, func: *Module.Fn, air: Air, liv...@@ -299,7 +313,7 @@ pub fn updateFunc(self: *Plan9, module: *Module, func: *Module.Fn, air: Air, liv
299 },313 },
300 );314 );
301 const code = switch (res) {315 const code = switch (res) {
302 .appended => try code_buffer.toOwnedSlice(),316 .ok => try code_buffer.toOwnedSlice(),
303 .fail => |em| {317 .fail => |em| {
304 decl.analysis = .codegen_failure;318 decl.analysis = .codegen_failure;
305 try module.failed_decls.put(module.gpa, decl_index, em);319 try module.failed_decls.put(module.gpa, decl_index, em);
...@@ -313,11 +327,11 @@ pub fn updateFunc(self: *Plan9, module: *Module, func: *Module.Fn, air: Air, liv...@@ -313,11 +327,11 @@ pub fn updateFunc(self: *Plan9, module: *Module, func: *Module.Fn, air: Air, liv
313 .end_line = end_line,327 .end_line = end_line,
314 };328 };
315 try self.putFn(decl_index, out);329 try self.putFn(decl_index, out);
316 return self.updateFinish(decl);330 return self.updateFinish(decl_index);
317}331}
318332
319pub fn lowerUnnamedConst(self: *Plan9, tv: TypedValue, decl_index: Module.Decl.Index) !u32 {333pub fn lowerUnnamedConst(self: *Plan9, tv: TypedValue, decl_index: Module.Decl.Index) !u32 {
320 try self.seeDecl(decl_index);334 _ = try self.seeDecl(decl_index);
321 var code_buffer = std.ArrayList(u8).init(self.base.allocator);335 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
322 defer code_buffer.deinit();336 defer code_buffer.deinit();
323337
...@@ -358,8 +372,7 @@ pub fn lowerUnnamedConst(self: *Plan9, tv: TypedValue, decl_index: Module.Decl.I...@@ -358,8 +372,7 @@ pub fn lowerUnnamedConst(self: *Plan9, tv: TypedValue, decl_index: Module.Decl.I
358 .parent_atom_index = @enumToInt(decl_index),372 .parent_atom_index = @enumToInt(decl_index),
359 });373 });
360 const code = switch (res) {374 const code = switch (res) {
361 .externally_managed => |x| x,375 .ok => code_buffer.items,
362 .appended => code_buffer.items,
363 .fail => |em| {376 .fail => |em| {
364 decl.analysis = .codegen_failure;377 decl.analysis = .codegen_failure;
365 try mod.failed_decls.put(mod.gpa, decl_index, em);378 try mod.failed_decls.put(mod.gpa, decl_index, em);
...@@ -388,7 +401,7 @@ pub fn updateDecl(self: *Plan9, module: *Module, decl_index: Module.Decl.Index)...@@ -388,7 +401,7 @@ pub fn updateDecl(self: *Plan9, module: *Module, decl_index: Module.Decl.Index)
388 }401 }
389 }402 }
390403
391 try self.seeDecl(decl_index);404 _ = try self.seeDecl(decl_index);
392405
393 log.debug("codegen decl {*} ({s}) ({d})", .{ decl, decl.name, decl_index });406 log.debug("codegen decl {*} ({s}) ({d})", .{ decl, decl.name, decl_index });
394407
...@@ -403,8 +416,7 @@ pub fn updateDecl(self: *Plan9, module: *Module, decl_index: Module.Decl.Index)...@@ -403,8 +416,7 @@ pub fn updateDecl(self: *Plan9, module: *Module, decl_index: Module.Decl.Index)
403 .parent_atom_index = @enumToInt(decl_index),416 .parent_atom_index = @enumToInt(decl_index),
404 });417 });
405 const code = switch (res) {418 const code = switch (res) {
406 .externally_managed => |x| x,419 .ok => code_buffer.items,
407 .appended => code_buffer.items,
408 .fail => |em| {420 .fail => |em| {
409 decl.analysis = .codegen_failure;421 decl.analysis = .codegen_failure;
410 try module.failed_decls.put(module.gpa, decl_index, em);422 try module.failed_decls.put(module.gpa, decl_index, em);
...@@ -416,28 +428,31 @@ pub fn updateDecl(self: *Plan9, module: *Module, decl_index: Module.Decl.Index)...@@ -416,28 +428,31 @@ pub fn updateDecl(self: *Plan9, module: *Module, decl_index: Module.Decl.Index)
416 if (self.data_decl_table.fetchPutAssumeCapacity(decl_index, duped_code)) |old_entry| {428 if (self.data_decl_table.fetchPutAssumeCapacity(decl_index, duped_code)) |old_entry| {
417 self.base.allocator.free(old_entry.value);429 self.base.allocator.free(old_entry.value);
418 }430 }
419 return self.updateFinish(decl);431 return self.updateFinish(decl_index);
420}432}
421/// called at the end of update{Decl,Func}433/// called at the end of update{Decl,Func}
422fn updateFinish(self: *Plan9, decl: *Module.Decl) !void {434fn updateFinish(self: *Plan9, decl_index: Module.Decl.Index) !void {
435 const decl = self.base.options.module.?.declPtr(decl_index);
423 const is_fn = (decl.ty.zigTypeTag() == .Fn);436 const is_fn = (decl.ty.zigTypeTag() == .Fn);
424 log.debug("update the symbol table and got for decl {*} ({s})", .{ decl, decl.name });437 log.debug("update the symbol table and got for decl {*} ({s})", .{ decl, decl.name });
425 const sym_t: aout.Sym.Type = if (is_fn) .t else .d;438 const sym_t: aout.Sym.Type = if (is_fn) .t else .d;
439
440 const decl_block = self.getDeclBlockPtr(self.decls.get(decl_index).?.index);
426 // write the internal linker metadata441 // write the internal linker metadata
427 decl.link.plan9.type = sym_t;442 decl_block.type = sym_t;
428 // write the symbol443 // write the symbol
429 // we already have the got index because that got allocated in allocateDeclIndexes444 // we already have the got index
430 const sym: aout.Sym = .{445 const sym: aout.Sym = .{
431 .value = undefined, // the value of stuff gets filled in in flushModule446 .value = undefined, // the value of stuff gets filled in in flushModule
432 .type = decl.link.plan9.type,447 .type = decl_block.type,
433 .name = mem.span(decl.name),448 .name = mem.span(decl.name),
434 };449 };
435450
436 if (decl.link.plan9.sym_index) |s| {451 if (decl_block.sym_index) |s| {
437 self.syms.items[s] = sym;452 self.syms.items[s] = sym;
438 } else {453 } else {
439 const s = try self.allocateSymbolIndex();454 const s = try self.allocateSymbolIndex();
440 decl.link.plan9.sym_index = s;455 decl_block.sym_index = s;
441 self.syms.items[s] = sym;456 self.syms.items[s] = sym;
442 }457 }
443}458}
...@@ -552,6 +567,7 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No...@@ -552,6 +567,7 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No
552 while (it.next()) |entry| {567 while (it.next()) |entry| {
553 const decl_index = entry.key_ptr.*;568 const decl_index = entry.key_ptr.*;
554 const decl = mod.declPtr(decl_index);569 const decl = mod.declPtr(decl_index);
570 const decl_block = self.getDeclBlockPtr(self.decls.get(decl_index).?.index);
555 const out = entry.value_ptr.*;571 const out = entry.value_ptr.*;
556 log.debug("write text decl {*} ({s}), lines {d} to {d}", .{ decl, decl.name, out.start_line + 1, out.end_line });572 log.debug("write text decl {*} ({s}), lines {d} to {d}", .{ decl, decl.name, out.start_line + 1, out.end_line });
557 {573 {
...@@ -570,16 +586,16 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No...@@ -570,16 +586,16 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No
570 iovecs_i += 1;586 iovecs_i += 1;
571 const off = self.getAddr(text_i, .t);587 const off = self.getAddr(text_i, .t);
572 text_i += out.code.len;588 text_i += out.code.len;
573 decl.link.plan9.offset = off;589 decl_block.offset = off;
574 if (!self.sixtyfour_bit) {590 if (!self.sixtyfour_bit) {
575 mem.writeIntNative(u32, got_table[decl.link.plan9.got_index.? * 4 ..][0..4], @intCast(u32, off));591 mem.writeIntNative(u32, got_table[decl_block.got_index.? * 4 ..][0..4], @intCast(u32, off));
576 mem.writeInt(u32, got_table[decl.link.plan9.got_index.? * 4 ..][0..4], @intCast(u32, off), self.base.options.target.cpu.arch.endian());592 mem.writeInt(u32, got_table[decl_block.got_index.? * 4 ..][0..4], @intCast(u32, off), self.base.options.target.cpu.arch.endian());
577 } else {593 } else {
578 mem.writeInt(u64, got_table[decl.link.plan9.got_index.? * 8 ..][0..8], off, self.base.options.target.cpu.arch.endian());594 mem.writeInt(u64, got_table[decl_block.got_index.? * 8 ..][0..8], off, self.base.options.target.cpu.arch.endian());
579 }595 }
580 self.syms.items[decl.link.plan9.sym_index.?].value = off;596 self.syms.items[decl_block.sym_index.?].value = off;
581 if (mod.decl_exports.get(decl_index)) |exports| {597 if (mod.decl_exports.get(decl_index)) |exports| {
582 try self.addDeclExports(mod, decl, exports.items);598 try self.addDeclExports(mod, decl_index, exports.items);
583 }599 }
584 }600 }
585 }601 }
...@@ -600,6 +616,7 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No...@@ -600,6 +616,7 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No
600 while (it.next()) |entry| {616 while (it.next()) |entry| {
601 const decl_index = entry.key_ptr.*;617 const decl_index = entry.key_ptr.*;
602 const decl = mod.declPtr(decl_index);618 const decl = mod.declPtr(decl_index);
619 const decl_block = self.getDeclBlockPtr(self.decls.get(decl_index).?.index);
603 const code = entry.value_ptr.*;620 const code = entry.value_ptr.*;
604 log.debug("write data decl {*} ({s})", .{ decl, decl.name });621 log.debug("write data decl {*} ({s})", .{ decl, decl.name });
605622
...@@ -608,15 +625,15 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No...@@ -608,15 +625,15 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No
608 iovecs_i += 1;625 iovecs_i += 1;
609 const off = self.getAddr(data_i, .d);626 const off = self.getAddr(data_i, .d);
610 data_i += code.len;627 data_i += code.len;
611 decl.link.plan9.offset = off;628 decl_block.offset = off;
612 if (!self.sixtyfour_bit) {629 if (!self.sixtyfour_bit) {
613 mem.writeInt(u32, got_table[decl.link.plan9.got_index.? * 4 ..][0..4], @intCast(u32, off), self.base.options.target.cpu.arch.endian());630 mem.writeInt(u32, got_table[decl_block.got_index.? * 4 ..][0..4], @intCast(u32, off), self.base.options.target.cpu.arch.endian());
614 } else {631 } else {
615 mem.writeInt(u64, got_table[decl.link.plan9.got_index.? * 8 ..][0..8], off, self.base.options.target.cpu.arch.endian());632 mem.writeInt(u64, got_table[decl_block.got_index.? * 8 ..][0..8], off, self.base.options.target.cpu.arch.endian());
616 }633 }
617 self.syms.items[decl.link.plan9.sym_index.?].value = off;634 self.syms.items[decl_block.sym_index.?].value = off;
618 if (mod.decl_exports.get(decl_index)) |exports| {635 if (mod.decl_exports.get(decl_index)) |exports| {
619 try self.addDeclExports(mod, decl, exports.items);636 try self.addDeclExports(mod, decl_index, exports.items);
620 }637 }
621 }638 }
622 // write the unnamed constants after the other data decls639 // write the unnamed constants after the other data decls
...@@ -678,7 +695,8 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No...@@ -678,7 +695,8 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No
678 for (kv.value_ptr.items) |reloc| {695 for (kv.value_ptr.items) |reloc| {
679 const target_decl_index = reloc.target;696 const target_decl_index = reloc.target;
680 const target_decl = mod.declPtr(target_decl_index);697 const target_decl = mod.declPtr(target_decl_index);
681 const target_decl_offset = target_decl.link.plan9.offset.?;698 const target_decl_block = self.getDeclBlock(self.decls.get(target_decl_index).?.index);
699 const target_decl_offset = target_decl_block.offset.?;
682700
683 const offset = reloc.offset;701 const offset = reloc.offset;
684 const addend = reloc.addend;702 const addend = reloc.addend;
...@@ -711,35 +729,43 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No...@@ -711,35 +729,43 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No
711fn addDeclExports(729fn addDeclExports(
712 self: *Plan9,730 self: *Plan9,
713 module: *Module,731 module: *Module,
714 decl: *Module.Decl,732 decl_index: Module.Decl.Index,
715 exports: []const *Module.Export,733 exports: []const *Module.Export,
716) !void {734) !void {
735 const metadata = self.decls.getPtr(decl_index).?;
736 const decl_block = self.getDeclBlock(metadata.index);
737
717 for (exports) |exp| {738 for (exports) |exp| {
718 // plan9 does not support custom sections739 // plan9 does not support custom sections
719 if (exp.options.section) |section_name| {740 if (exp.options.section) |section_name| {
720 if (!mem.eql(u8, section_name, ".text") or !mem.eql(u8, section_name, ".data")) {741 if (!mem.eql(u8, section_name, ".text") or !mem.eql(u8, section_name, ".data")) {
721 try module.failed_exports.put(module.gpa, exp, try Module.ErrorMsg.create(self.base.allocator, decl.srcLoc(), "plan9 does not support extra sections", .{}));742 try module.failed_exports.put(module.gpa, exp, try Module.ErrorMsg.create(
743 self.base.allocator,
744 module.declPtr(decl_index).srcLoc(),
745 "plan9 does not support extra sections",
746 .{},
747 ));
722 break;748 break;
723 }749 }
724 }750 }
725 const sym = .{751 const sym = .{
726 .value = decl.link.plan9.offset.?,752 .value = decl_block.offset.?,
727 .type = decl.link.plan9.type.toGlobal(),753 .type = decl_block.type.toGlobal(),
728 .name = exp.options.name,754 .name = exp.options.name,
729 };755 };
730756
731 if (exp.link.plan9) |i| {757 if (metadata.getExport(self, exp.options.name)) |i| {
732 self.syms.items[i] = sym;758 self.syms.items[i] = sym;
733 } else {759 } else {
734 try self.syms.append(self.base.allocator, sym);760 try self.syms.append(self.base.allocator, sym);
735 exp.link.plan9 = self.syms.items.len - 1;761 try metadata.exports.append(self.base.allocator, self.syms.items.len - 1);
736 }762 }
737 }763 }
738}764}
739765
740pub fn freeDecl(self: *Plan9, decl_index: Module.Decl.Index) void {766pub fn freeDecl(self: *Plan9, decl_index: Module.Decl.Index) void {
741 // TODO audit the lifetimes of decls table entries. It's possible to get767 // TODO audit the lifetimes of decls table entries. It's possible to get
742 // allocateDeclIndexes and then freeDecl without any updateDecl in between.768 // freeDecl without any updateDecl in between.
743 // However that is planned to change, see the TODO comment in Module.zig769 // However that is planned to change, see the TODO comment in Module.zig
744 // in the deleteUnusedDecl function.770 // in the deleteUnusedDecl function.
745 const mod = self.base.options.module.?;771 const mod = self.base.options.module.?;
...@@ -762,13 +788,18 @@ pub fn freeDecl(self: *Plan9, decl_index: Module.Decl.Index) void {...@@ -762,13 +788,18 @@ pub fn freeDecl(self: *Plan9, decl_index: Module.Decl.Index) void {
762 self.base.allocator.free(removed_entry.value);788 self.base.allocator.free(removed_entry.value);
763 }789 }
764 }790 }
765 if (decl.link.plan9.got_index) |i| {791 if (self.decls.fetchRemove(decl_index)) |const_kv| {
766 // TODO: if this catch {} is triggered, an assertion in flushModule will be triggered, because got_index_free_list will have the wrong length792 var kv = const_kv;
767 self.got_index_free_list.append(self.base.allocator, i) catch {};793 const decl_block = self.getDeclBlock(kv.value.index);
768 }794 if (decl_block.got_index) |i| {
769 if (decl.link.plan9.sym_index) |i| {795 // TODO: if this catch {} is triggered, an assertion in flushModule will be triggered, because got_index_free_list will have the wrong length
770 self.syms_index_free_list.append(self.base.allocator, i) catch {};796 self.got_index_free_list.append(self.base.allocator, i) catch {};
771 self.syms.items[i] = aout.Sym.undefined_symbol;797 }
798 if (decl_block.sym_index) |i| {
799 self.syms_index_free_list.append(self.base.allocator, i) catch {};
800 self.syms.items[i] = aout.Sym.undefined_symbol;
801 }
802 kv.value.exports.deinit(self.base.allocator);
772 }803 }
773 self.freeUnnamedConsts(decl_index);804 self.freeUnnamedConsts(decl_index);
774 {805 {
...@@ -788,12 +819,30 @@ fn freeUnnamedConsts(self: *Plan9, decl_index: Module.Decl.Index) void {...@@ -788,12 +819,30 @@ fn freeUnnamedConsts(self: *Plan9, decl_index: Module.Decl.Index) void {
788 unnamed_consts.clearAndFree(self.base.allocator);819 unnamed_consts.clearAndFree(self.base.allocator);
789}820}
790821
791pub fn seeDecl(self: *Plan9, decl_index: Module.Decl.Index) !void {822fn createDeclBlock(self: *Plan9) !DeclBlock.Index {
792 const mod = self.base.options.module.?;823 const gpa = self.base.allocator;
793 const decl = mod.declPtr(decl_index);824 const index = @intCast(DeclBlock.Index, self.decl_blocks.items.len);
794 if (decl.link.plan9.got_index == null) {825 const decl_block = try self.decl_blocks.addOne(gpa);
795 decl.link.plan9.got_index = self.allocateGotIndex();826 decl_block.* = .{
827 .type = .t,
828 .offset = null,
829 .sym_index = null,
830 .got_index = null,
831 };
832 return index;
833}
834
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 };
796 }844 }
845 return gop.value_ptr.index;
797}846}
798847
799pub fn updateDeclExports(848pub fn updateDeclExports(
...@@ -802,7 +851,7 @@ pub fn updateDeclExports(...@@ -802,7 +851,7 @@ pub fn updateDeclExports(
802 decl_index: Module.Decl.Index,851 decl_index: Module.Decl.Index,
803 exports: []const *Module.Export,852 exports: []const *Module.Export,
804) !void {853) !void {
805 try self.seeDecl(decl_index);854 _ = try self.seeDecl(decl_index);
806 // we do all the things in flush855 // we do all the things in flush
807 _ = module;856 _ = module;
808 _ = exports;857 _ = exports;
...@@ -844,10 +893,17 @@ pub fn deinit(self: *Plan9) void {...@@ -844,10 +893,17 @@ pub fn deinit(self: *Plan9) void {
844 self.syms_index_free_list.deinit(gpa);893 self.syms_index_free_list.deinit(gpa);
845 self.file_segments.deinit(gpa);894 self.file_segments.deinit(gpa);
846 self.path_arena.deinit();895 self.path_arena.deinit();
896 self.decl_blocks.deinit(gpa);
897
898 {
899 var it = self.decls.iterator();
900 while (it.next()) |entry| {
901 entry.value_ptr.exports.deinit(gpa);
902 }
903 self.decls.deinit(gpa);
904 }
847}905}
848906
849pub const Export = ?usize;
850pub const base_tag = .plan9;
851pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Options) !*Plan9 {907pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Options) !*Plan9 {
852 if (options.use_llvm)908 if (options.use_llvm)
853 return error.LLVMBackendDoesNotSupportPlan9;909 return error.LLVMBackendDoesNotSupportPlan9;
...@@ -913,20 +969,19 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void {...@@ -913,20 +969,19 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void {
913 }969 }
914 }970 }
915971
916 const mod = self.base.options.module.?;
917
918 // write the data symbols972 // write the data symbols
919 {973 {
920 var it = self.data_decl_table.iterator();974 var it = self.data_decl_table.iterator();
921 while (it.next()) |entry| {975 while (it.next()) |entry| {
922 const decl_index = entry.key_ptr.*;976 const decl_index = entry.key_ptr.*;
923 const decl = mod.declPtr(decl_index);977 const decl_metadata = self.decls.get(decl_index).?;
924 const sym = self.syms.items[decl.link.plan9.sym_index.?];978 const decl_block = self.getDeclBlock(decl_metadata.index);
979 const sym = self.syms.items[decl_block.sym_index.?];
925 try self.writeSym(writer, sym);980 try self.writeSym(writer, sym);
926 if (self.base.options.module.?.decl_exports.get(decl_index)) |exports| {981 if (self.base.options.module.?.decl_exports.get(decl_index)) |exports| {
927 for (exports.items) |e| {982 for (exports.items) |e| if (decl_metadata.getExport(self, e.options.name)) |exp_i| {
928 try self.writeSym(writer, self.syms.items[e.link.plan9.?]);983 try self.writeSym(writer, self.syms.items[exp_i]);
929 }984 };
930 }985 }
931 }986 }
932 }987 }
...@@ -945,32 +1000,28 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void {...@@ -945,32 +1000,28 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void {
945 var submap_it = symidx_and_submap.functions.iterator();1000 var submap_it = symidx_and_submap.functions.iterator();
946 while (submap_it.next()) |entry| {1001 while (submap_it.next()) |entry| {
947 const decl_index = entry.key_ptr.*;1002 const decl_index = entry.key_ptr.*;
948 const decl = mod.declPtr(decl_index);1003 const decl_metadata = self.decls.get(decl_index).?;
949 const sym = self.syms.items[decl.link.plan9.sym_index.?];1004 const decl_block = self.getDeclBlock(decl_metadata.index);
1005 const sym = self.syms.items[decl_block.sym_index.?];
950 try self.writeSym(writer, sym);1006 try self.writeSym(writer, sym);
951 if (self.base.options.module.?.decl_exports.get(decl_index)) |exports| {1007 if (self.base.options.module.?.decl_exports.get(decl_index)) |exports| {
952 for (exports.items) |e| {1008 for (exports.items) |e| if (decl_metadata.getExport(self, e.options.name)) |exp_i| {
953 const s = self.syms.items[e.link.plan9.?];1009 const s = self.syms.items[exp_i];
954 if (mem.eql(u8, s.name, "_start"))1010 if (mem.eql(u8, s.name, "_start"))
955 self.entry_val = s.value;1011 self.entry_val = s.value;
956 try self.writeSym(writer, s);1012 try self.writeSym(writer, s);
957 }1013 };
958 }1014 }
959 }1015 }
960 }1016 }
961 }1017 }
962}1018}
9631019
964/// this will be removed, moved to updateFinish
965pub fn allocateDeclIndexes(self: *Plan9, decl_index: Module.Decl.Index) !void {
966 _ = self;
967 _ = decl_index;
968}
969/// Must be called only after a successful call to `updateDecl`.1020/// Must be called only after a successful call to `updateDecl`.
970pub fn updateDeclLineNumber(self: *Plan9, mod: *Module, decl: *const Module.Decl) !void {1021pub fn updateDeclLineNumber(self: *Plan9, mod: *Module, decl_index: Module.Decl.Index) !void {
971 _ = self;1022 _ = self;
972 _ = mod;1023 _ = mod;
973 _ = decl;1024 _ = decl_index;
974}1025}
9751026
976pub fn getDeclVAddr(1027pub fn getDeclVAddr(
...@@ -1011,3 +1062,11 @@ pub fn getDeclVAddr(...@@ -1011,3 +1062,11 @@ pub fn getDeclVAddr(
1011 });1062 });
1012 return undefined;1063 return undefined;
1013}1064}
1065
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");...@@ -42,13 +42,6 @@ const SpvModule = @import("../codegen/spirv/Module.zig");
42const spec = @import("../codegen/spirv/spec.zig");42const spec = @import("../codegen/spirv/spec.zig");
43const IdResult = spec.IdResult;43const 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
52base: link.File,45base: link.File,
5346
54/// This linker backend does not try to incrementally link output SPIR-V code.47/// This linker backend does not try to incrementally link output SPIR-V code.
...@@ -209,16 +202,19 @@ pub fn flushModule(self: *SpirV, comp: *Compilation, prog_node: *std.Progress.No...@@ -209,16 +202,19 @@ pub fn flushModule(self: *SpirV, comp: *Compilation, prog_node: *std.Progress.No
209 // so that we can access them before processing them.202 // so that we can access them before processing them.
210 // TODO: We're allocating an ID unconditionally now, are there203 // TODO: We're allocating an ID unconditionally now, are there
211 // declarations which don't generate a result?204 // declarations which don't generate a result?
212 // TODO: fn_link is used here, but thats probably not the right field. It will work anyway though.205 var ids = std.AutoHashMap(Module.Decl.Index, IdResult).init(self.base.allocator);
206 defer ids.deinit();
207 try ids.ensureTotalCapacity(@intCast(u32, self.decl_table.count()));
208
213 for (self.decl_table.keys()) |decl_index| {209 for (self.decl_table.keys()) |decl_index| {
214 const decl = module.declPtr(decl_index);210 const decl = module.declPtr(decl_index);
215 if (decl.has_tv) {211 if (decl.has_tv) {
216 decl.fn_link.spirv.id = spv.allocId();212 ids.putAssumeCapacityNoClobber(decl_index, spv.allocId());
217 }213 }
218 }214 }
219215
220 // Now, actually generate the code for all declarations.216 // Now, actually generate the code for all declarations.
221 var decl_gen = codegen.DeclGen.init(self.base.allocator, module, &spv);217 var decl_gen = codegen.DeclGen.init(self.base.allocator, module, &spv, &ids);
222 defer decl_gen.deinit();218 defer decl_gen.deinit();
223219
224 var it = self.decl_table.iterator();220 var it = self.decl_table.iterator();
...@@ -231,7 +227,7 @@ pub fn flushModule(self: *SpirV, comp: *Compilation, prog_node: *std.Progress.No...@@ -231,7 +227,7 @@ pub fn flushModule(self: *SpirV, comp: *Compilation, prog_node: *std.Progress.No
231 const liveness = entry.value_ptr.liveness;227 const liveness = entry.value_ptr.liveness;
232228
233 // Note, if `decl` is not a function, air/liveness may be undefined.229 // Note, if `decl` is not a function, air/liveness may be undefined.
234 if (try decl_gen.gen(decl, air, liveness)) |msg| {230 if (try decl_gen.gen(decl_index, air, liveness)) |msg| {
235 try module.failed_decls.put(module.gpa, decl_index, msg);231 try module.failed_decls.put(module.gpa, decl_index, msg);
236 return; // TODO: Attempt to generate more decls?232 return; // TODO: Attempt to generate more decls?
237 }233 }
src/link/Wasm.zig+292-281
...@@ -9,7 +9,7 @@ const fs = std.fs;...@@ -9,7 +9,7 @@ const fs = std.fs;
9const leb = std.leb;9const leb = std.leb;
10const log = std.log.scoped(.link);10const log = std.log.scoped(.link);
1111
12const Atom = @import("Wasm/Atom.zig");12pub const Atom = @import("Wasm/Atom.zig");
13const Dwarf = @import("Dwarf.zig");13const Dwarf = @import("Dwarf.zig");
14const Module = @import("../Module.zig");14const Module = @import("../Module.zig");
15const Compilation = @import("../Compilation.zig");15const Compilation = @import("../Compilation.zig");
...@@ -31,10 +31,7 @@ const Object = @import("Wasm/Object.zig");...@@ -31,10 +31,7 @@ const Object = @import("Wasm/Object.zig");
31const Archive = @import("Wasm/Archive.zig");31const Archive = @import("Wasm/Archive.zig");
32const types = @import("Wasm/types.zig");32const types = @import("Wasm/types.zig");
3333
34pub const base_tag = link.File.Tag.wasm;34pub const base_tag: link.File.Tag = .wasm;
35
36/// deprecated: Use `@import("Wasm/Atom.zig");`
37pub const DeclBlock = Atom;
3835
39base: link.File,36base: link.File,
40/// Output name of the file37/// Output name of the file
...@@ -47,18 +44,16 @@ llvm_object: ?*LlvmObject = null,...@@ -47,18 +44,16 @@ llvm_object: ?*LlvmObject = null,
47/// TODO: Allow setting this through a flag?44/// TODO: Allow setting this through a flag?
48host_name: []const u8 = "env",45host_name: []const u8 = "env",
49/// List of all `Decl` that are currently alive.46/// List of all `Decl` that are currently alive.
50/// This is ment for bookkeeping so we can safely cleanup all codegen memory47/// Each index maps to the corresponding `Atom.Index`.
51/// when calling `deinit`48decls: std.AutoHashMapUnmanaged(Module.Decl.Index, Atom.Index) = .{},
52decls: std.AutoHashMapUnmanaged(Module.Decl.Index, void) = .{},
53/// List of all symbols generated by Zig code.49/// List of all symbols generated by Zig code.
54symbols: std.ArrayListUnmanaged(Symbol) = .{},50symbols: std.ArrayListUnmanaged(Symbol) = .{},
55/// List of symbol indexes which are free to be used.51/// List of symbol indexes which are free to be used.
56symbols_free_list: std.ArrayListUnmanaged(u32) = .{},52symbols_free_list: std.ArrayListUnmanaged(u32) = .{},
57/// Maps atoms to their segment index53/// Maps atoms to their segment index
58atoms: std.AutoHashMapUnmanaged(u32, *Atom) = .{},54atoms: std.AutoHashMapUnmanaged(u32, Atom.Index) = .{},
59/// Atoms managed and created by the linker. This contains atoms55/// List of all atoms.
60/// from object files, and not Atoms generated by a Decl.56managed_atoms: std.ArrayListUnmanaged(Atom) = .{},
61managed_atoms: std.ArrayListUnmanaged(*Atom) = .{},
62/// Represents the index into `segments` where the 'code' section57/// Represents the index into `segments` where the 'code' section
63/// lives.58/// lives.
64code_section_index: ?u32 = null,59code_section_index: ?u32 = null,
...@@ -148,7 +143,7 @@ undefs: std.StringArrayHashMapUnmanaged(SymbolLoc) = .{},...@@ -148,7 +143,7 @@ undefs: std.StringArrayHashMapUnmanaged(SymbolLoc) = .{},
148/// Maps a symbol's location to an atom. This can be used to find meta143/// Maps a symbol's location to an atom. This can be used to find meta
149/// data of a symbol, such as its size, or its offset to perform a relocation.144/// data of a symbol, such as its size, or its offset to perform a relocation.
150/// Undefined (and synthetic) symbols do not have an Atom and therefore cannot be mapped.145/// Undefined (and synthetic) symbols do not have an Atom and therefore cannot be mapped.
151symbol_atom: std.AutoHashMapUnmanaged(SymbolLoc, *Atom) = .{},146symbol_atom: std.AutoHashMapUnmanaged(SymbolLoc, Atom.Index) = .{},
152/// Maps a symbol's location to its export name, which may differ from the decl's name147/// Maps a symbol's location to its export name, which may differ from the decl's name
153/// which does the exporting.148/// which does the exporting.
154/// Note: The value represents the offset into the string table, rather than the actual string.149/// Note: The value represents the offset into the string table, rather than the actual string.
...@@ -165,14 +160,14 @@ error_table_symbol: ?u32 = null,...@@ -165,14 +160,14 @@ error_table_symbol: ?u32 = null,
165// unit contains Zig code. The lifetime of these atoms are extended160// unit contains Zig code. The lifetime of these atoms are extended
166// until the end of the compiler's lifetime. Meaning they're not freed161// until the end of the compiler's lifetime. Meaning they're not freed
167// during `flush()` in incremental-mode.162// during `flush()` in incremental-mode.
168debug_info_atom: ?*Atom = null,163debug_info_atom: ?Atom.Index = null,
169debug_line_atom: ?*Atom = null,164debug_line_atom: ?Atom.Index = null,
170debug_loc_atom: ?*Atom = null,165debug_loc_atom: ?Atom.Index = null,
171debug_ranges_atom: ?*Atom = null,166debug_ranges_atom: ?Atom.Index = null,
172debug_abbrev_atom: ?*Atom = null,167debug_abbrev_atom: ?Atom.Index = null,
173debug_str_atom: ?*Atom = null,168debug_str_atom: ?Atom.Index = null,
174debug_pubnames_atom: ?*Atom = null,169debug_pubnames_atom: ?Atom.Index = null,
175debug_pubtypes_atom: ?*Atom = null,170debug_pubtypes_atom: ?Atom.Index = null,
176171
177pub const Segment = struct {172pub const Segment = struct {
178 alignment: u32,173 alignment: u32,
...@@ -183,13 +178,9 @@ pub const Segment = struct {...@@ -183,13 +178,9 @@ pub const Segment = struct {
183pub const FnData = struct {178pub const FnData = struct {
184 /// Reference to the wasm type that represents this function.179 /// Reference to the wasm type that represents this function.
185 type_index: u32,180 type_index: u32,
186 /// Contains debug information related to this function.
187 /// For Wasm, the offset is relative to the code-section.
188 src_fn: Dwarf.SrcFn,
189181
190 pub const empty: FnData = .{182 pub const empty: FnData = .{
191 .type_index = undefined,183 .type_index = undefined,
192 .src_fn = Dwarf.SrcFn.empty,
193 };184 };
194};185};
195186
...@@ -434,10 +425,10 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option...@@ -434,10 +425,10 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
434 // at the end during `initializeCallCtorsFunction`.425 // at the end during `initializeCallCtorsFunction`.
435 }426 }
436427
437 if (!options.strip and options.module != null) {428 // if (!options.strip and options.module != null) {
438 wasm_bin.dwarf = Dwarf.init(allocator, &wasm_bin.base, options.target);429 // wasm_bin.dwarf = Dwarf.init(allocator, &wasm_bin.base, options.target);
439 try wasm_bin.initDebugSections();430 // try wasm_bin.initDebugSections();
440 }431 // }
441432
442 return wasm_bin;433 return wasm_bin;
443}434}
...@@ -478,6 +469,7 @@ fn createSyntheticSymbol(wasm: *Wasm, name: []const u8, tag: Symbol.Tag) !Symbol...@@ -478,6 +469,7 @@ fn createSyntheticSymbol(wasm: *Wasm, name: []const u8, tag: Symbol.Tag) !Symbol
478 try wasm.globals.put(wasm.base.allocator, name_offset, loc);469 try wasm.globals.put(wasm.base.allocator, name_offset, loc);
479 return loc;470 return loc;
480}471}
472
481/// Initializes symbols and atoms for the debug sections473/// Initializes symbols and atoms for the debug sections
482/// Initialization is only done when compiling Zig code.474/// Initialization is only done when compiling Zig code.
483/// When Zig is invoked as a linker instead, the atoms475/// When Zig is invoked as a linker instead, the atoms
...@@ -520,6 +512,36 @@ fn parseObjectFile(wasm: *Wasm, path: []const u8) !bool {...@@ -520,6 +512,36 @@ fn parseObjectFile(wasm: *Wasm, path: []const u8) !bool {
520 return true;512 return true;
521}513}
522514
515/// For a given `Module.Decl.Index` returns its corresponding `Atom.Index`.
516/// When the index was not found, a new `Atom` will be created, and its index will be returned.
517/// The newly created Atom is empty with default fields as specified by `Atom.empty`.
518pub fn getOrCreateAtomForDecl(wasm: *Wasm, decl_index: Module.Decl.Index) !Atom.Index {
519 const gop = try wasm.decls.getOrPut(wasm.base.allocator, decl_index);
520 if (!gop.found_existing) {
521 gop.value_ptr.* = try wasm.createAtom();
522 }
523 return gop.value_ptr.*;
524}
525
526/// Creates a new empty `Atom` and returns its `Atom.Index`
527fn createAtom(wasm: *Wasm) !Atom.Index {
528 const index = @intCast(Atom.Index, wasm.managed_atoms.items.len);
529 const atom = try wasm.managed_atoms.addOne(wasm.base.allocator);
530 atom.* = Atom.empty;
531 atom.sym_index = try wasm.allocateSymbol();
532 try wasm.symbol_atom.putNoClobber(wasm.base.allocator, .{ .file = null, .index = atom.sym_index }, index);
533
534 return index;
535}
536
537pub inline fn getAtom(wasm: *const Wasm, index: Atom.Index) Atom {
538 return wasm.managed_atoms.items[index];
539}
540
541pub inline fn getAtomPtr(wasm: *Wasm, index: Atom.Index) *Atom {
542 return &wasm.managed_atoms.items[index];
543}
544
523/// Parses an archive file and will then parse each object file545/// Parses an archive file and will then parse each object file
524/// that was found in the archive file.546/// that was found in the archive file.
525/// Returns false when the file is not an archive file.547/// Returns false when the file is not an archive file.
...@@ -861,15 +883,16 @@ fn resolveLazySymbols(wasm: *Wasm) !void {...@@ -861,15 +883,16 @@ fn resolveLazySymbols(wasm: *Wasm) !void {
861 try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc);883 try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc);
862 _ = wasm.resolved_symbols.swapRemove(loc); // we don't want to emit this symbol, only use it for relocations.884 _ = wasm.resolved_symbols.swapRemove(loc); // we don't want to emit this symbol, only use it for relocations.
863885
864 const atom = try wasm.base.allocator.create(Atom);886 // TODO: Can we use `createAtom` here while also re-using the symbol
865 errdefer wasm.base.allocator.destroy(atom);887 // from `createSyntheticSymbol`.
866 try wasm.managed_atoms.append(wasm.base.allocator, atom);888 const atom_index = @intCast(Atom.Index, wasm.managed_atoms.items.len);
889 const atom = try wasm.managed_atoms.addOne(wasm.base.allocator);
867 atom.* = Atom.empty;890 atom.* = Atom.empty;
868 atom.sym_index = loc.index;891 atom.sym_index = loc.index;
869 atom.alignment = 1;892 atom.alignment = 1;
870893
871 try wasm.parseAtom(atom, .{ .data = .synthetic });894 try wasm.parseAtom(atom_index, .{ .data = .synthetic });
872 try wasm.symbol_atom.putNoClobber(wasm.base.allocator, loc, atom);895 try wasm.symbol_atom.putNoClobber(wasm.base.allocator, loc, atom_index);
873 }896 }
874897
875 if (wasm.undefs.fetchSwapRemove("__heap_end")) |kv| {898 if (wasm.undefs.fetchSwapRemove("__heap_end")) |kv| {
...@@ -877,15 +900,14 @@ fn resolveLazySymbols(wasm: *Wasm) !void {...@@ -877,15 +900,14 @@ fn resolveLazySymbols(wasm: *Wasm) !void {
877 try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc);900 try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc);
878 _ = wasm.resolved_symbols.swapRemove(loc);901 _ = wasm.resolved_symbols.swapRemove(loc);
879902
880 const atom = try wasm.base.allocator.create(Atom);903 const atom_index = @intCast(Atom.Index, wasm.managed_atoms.items.len);
881 errdefer wasm.base.allocator.destroy(atom);904 const atom = try wasm.managed_atoms.addOne(wasm.base.allocator);
882 try wasm.managed_atoms.append(wasm.base.allocator, atom);
883 atom.* = Atom.empty;905 atom.* = Atom.empty;
884 atom.sym_index = loc.index;906 atom.sym_index = loc.index;
885 atom.alignment = 1;907 atom.alignment = 1;
886908
887 try wasm.parseAtom(atom, .{ .data = .synthetic });909 try wasm.parseAtom(atom_index, .{ .data = .synthetic });
888 try wasm.symbol_atom.putNoClobber(wasm.base.allocator, loc, atom);910 try wasm.symbol_atom.putNoClobber(wasm.base.allocator, loc, atom_index);
889 }911 }
890}912}
891913
...@@ -924,16 +946,6 @@ pub fn deinit(wasm: *Wasm) void {...@@ -924,16 +946,6 @@ pub fn deinit(wasm: *Wasm) void {
924 if (wasm.llvm_object) |llvm_object| llvm_object.destroy(gpa);946 if (wasm.llvm_object) |llvm_object| llvm_object.destroy(gpa);
925 }947 }
926948
927 if (wasm.base.options.module) |mod| {
928 var decl_it = wasm.decls.keyIterator();
929 while (decl_it.next()) |decl_index_ptr| {
930 const decl = mod.declPtr(decl_index_ptr.*);
931 decl.link.wasm.deinit(gpa);
932 }
933 } else {
934 assert(wasm.decls.count() == 0);
935 }
936
937 for (wasm.func_types.items) |*func_type| {949 for (wasm.func_types.items) |*func_type| {
938 func_type.deinit(gpa);950 func_type.deinit(gpa);
939 }951 }
...@@ -958,9 +970,8 @@ pub fn deinit(wasm: *Wasm) void {...@@ -958,9 +970,8 @@ pub fn deinit(wasm: *Wasm) void {
958 wasm.symbol_atom.deinit(gpa);970 wasm.symbol_atom.deinit(gpa);
959 wasm.export_names.deinit(gpa);971 wasm.export_names.deinit(gpa);
960 wasm.atoms.deinit(gpa);972 wasm.atoms.deinit(gpa);
961 for (wasm.managed_atoms.items) |managed_atom| {973 for (wasm.managed_atoms.items) |*managed_atom| {
962 managed_atom.deinit(gpa);974 managed_atom.deinit(wasm);
963 gpa.destroy(managed_atom);
964 }975 }
965 wasm.managed_atoms.deinit(gpa);976 wasm.managed_atoms.deinit(gpa);
966 wasm.segments.deinit(gpa);977 wasm.segments.deinit(gpa);
...@@ -986,31 +997,23 @@ pub fn deinit(wasm: *Wasm) void {...@@ -986,31 +997,23 @@ pub fn deinit(wasm: *Wasm) void {
986 }997 }
987}998}
988999
989pub fn allocateDeclIndexes(wasm: *Wasm, decl_index: Module.Decl.Index) !void {1000/// Allocates a new symbol and returns its index.
990 if (wasm.llvm_object) |_| return;1001/// Will re-use slots when a symbol was freed at an earlier stage.
991 const decl = wasm.base.options.module.?.declPtr(decl_index);1002pub fn allocateSymbol(wasm: *Wasm) !u32 {
992 if (decl.link.wasm.sym_index != 0) return;
993
994 try wasm.symbols.ensureUnusedCapacity(wasm.base.allocator, 1);1003 try wasm.symbols.ensureUnusedCapacity(wasm.base.allocator, 1);
995 try wasm.decls.putNoClobber(wasm.base.allocator, decl_index, {});
996
997 const atom = &decl.link.wasm;
998
999 var symbol: Symbol = .{1004 var symbol: Symbol = .{
1000 .name = undefined, // will be set after updateDecl1005 .name = undefined, // will be set after updateDecl
1001 .flags = @enumToInt(Symbol.Flag.WASM_SYM_BINDING_LOCAL),1006 .flags = @enumToInt(Symbol.Flag.WASM_SYM_BINDING_LOCAL),
1002 .tag = undefined, // will be set after updateDecl1007 .tag = undefined, // will be set after updateDecl
1003 .index = undefined, // will be set after updateDecl1008 .index = undefined, // will be set after updateDecl
1004 };1009 };
1005
1006 if (wasm.symbols_free_list.popOrNull()) |index| {1010 if (wasm.symbols_free_list.popOrNull()) |index| {
1007 atom.sym_index = index;
1008 wasm.symbols.items[index] = symbol;1011 wasm.symbols.items[index] = symbol;
1009 } else {1012 return index;
1010 atom.sym_index = @intCast(u32, wasm.symbols.items.len);
1011 wasm.symbols.appendAssumeCapacity(symbol);
1012 }1013 }
1013 try wasm.symbol_atom.putNoClobber(wasm.base.allocator, atom.symbolLoc(), atom);1014 const index = @intCast(u32, wasm.symbols.items.len);
1015 wasm.symbols.appendAssumeCapacity(symbol);
1016 return index;
1014}1017}
10151018
1016pub fn updateFunc(wasm: *Wasm, mod: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void {1019pub fn updateFunc(wasm: *Wasm, mod: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void {
...@@ -1026,15 +1029,24 @@ pub fn updateFunc(wasm: *Wasm, mod: *Module, func: *Module.Fn, air: Air, livenes...@@ -1026,15 +1029,24 @@ pub fn updateFunc(wasm: *Wasm, mod: *Module, func: *Module.Fn, air: Air, livenes
10261029
1027 const decl_index = func.owner_decl;1030 const decl_index = func.owner_decl;
1028 const decl = mod.declPtr(decl_index);1031 const decl = mod.declPtr(decl_index);
1029 assert(decl.link.wasm.sym_index != 0); // Must call allocateDeclIndexes()1032 const atom_index = try wasm.getOrCreateAtomForDecl(decl_index);
1033 const atom = wasm.getAtomPtr(atom_index);
1034 atom.clear();
10301035
1031 decl.link.wasm.clear();1036 // var decl_state: ?Dwarf.DeclState = if (wasm.dwarf) |*dwarf| try dwarf.initDeclState(mod, decl_index) else null;
10321037 // defer if (decl_state) |*ds| ds.deinit();
1033 var decl_state: ?Dwarf.DeclState = if (wasm.dwarf) |*dwarf| try dwarf.initDeclState(mod, decl_index) else null;
1034 defer if (decl_state) |*ds| ds.deinit();
10351038
1036 var code_writer = std.ArrayList(u8).init(wasm.base.allocator);1039 var code_writer = std.ArrayList(u8).init(wasm.base.allocator);
1037 defer code_writer.deinit();1040 defer code_writer.deinit();
1041 // const result = try codegen.generateFunction(
1042 // &wasm.base,
1043 // decl.srcLoc(),
1044 // func,
1045 // air,
1046 // liveness,
1047 // &code_writer,
1048 // if (decl_state) |*ds| .{ .dwarf = ds } else .none,
1049 // );
1038 const result = try codegen.generateFunction(1050 const result = try codegen.generateFunction(
1039 &wasm.base,1051 &wasm.base,
1040 decl.srcLoc(),1052 decl.srcLoc(),
...@@ -1042,11 +1054,11 @@ pub fn updateFunc(wasm: *Wasm, mod: *Module, func: *Module.Fn, air: Air, livenes...@@ -1042,11 +1054,11 @@ pub fn updateFunc(wasm: *Wasm, mod: *Module, func: *Module.Fn, air: Air, livenes
1042 air,1054 air,
1043 liveness,1055 liveness,
1044 &code_writer,1056 &code_writer,
1045 if (decl_state) |*ds| .{ .dwarf = ds } else .none,1057 .none,
1046 );1058 );
10471059
1048 const code = switch (result) {1060 const code = switch (result) {
1049 .appended => code_writer.items,1061 .ok => code_writer.items,
1050 .fail => |em| {1062 .fail => |em| {
1051 decl.analysis = .codegen_failure;1063 decl.analysis = .codegen_failure;
1052 try mod.failed_decls.put(mod.gpa, decl_index, em);1064 try mod.failed_decls.put(mod.gpa, decl_index, em);
...@@ -1054,19 +1066,19 @@ pub fn updateFunc(wasm: *Wasm, mod: *Module, func: *Module.Fn, air: Air, livenes...@@ -1054,19 +1066,19 @@ pub fn updateFunc(wasm: *Wasm, mod: *Module, func: *Module.Fn, air: Air, livenes
1054 },1066 },
1055 };1067 };
10561068
1057 if (wasm.dwarf) |*dwarf| {1069 // if (wasm.dwarf) |*dwarf| {
1058 try dwarf.commitDeclState(1070 // try dwarf.commitDeclState(
1059 mod,1071 // mod,
1060 decl_index,1072 // decl_index,
1061 // Actual value will be written after relocation.1073 // // Actual value will be written after relocation.
1062 // For Wasm, this is the offset relative to the code section1074 // // For Wasm, this is the offset relative to the code section
1063 // which isn't known until flush().1075 // // which isn't known until flush().
1064 0,1076 // 0,
1065 code.len,1077 // code.len,
1066 &decl_state.?,1078 // &decl_state.?,
1067 );1079 // );
1068 }1080 // }
1069 return wasm.finishUpdateDecl(decl, code);1081 return wasm.finishUpdateDecl(decl_index, code);
1070}1082}
10711083
1072// Generate code for the Decl, storing it in memory to be later written to1084// Generate code for the Decl, storing it in memory to be later written to
...@@ -1083,20 +1095,20 @@ pub fn updateDecl(wasm: *Wasm, mod: *Module, decl_index: Module.Decl.Index) !voi...@@ -1083,20 +1095,20 @@ pub fn updateDecl(wasm: *Wasm, mod: *Module, decl_index: Module.Decl.Index) !voi
1083 defer tracy.end();1095 defer tracy.end();
10841096
1085 const decl = mod.declPtr(decl_index);1097 const decl = mod.declPtr(decl_index);
1086 assert(decl.link.wasm.sym_index != 0); // Must call allocateDeclIndexes()
1087
1088 decl.link.wasm.clear();
1089
1090 if (decl.val.castTag(.function)) |_| {1098 if (decl.val.castTag(.function)) |_| {
1091 return;1099 return;
1092 } else if (decl.val.castTag(.extern_fn)) |_| {1100 } else if (decl.val.castTag(.extern_fn)) |_| {
1093 return;1101 return;
1094 }1102 }
10951103
1104 const atom_index = try wasm.getOrCreateAtomForDecl(decl_index);
1105 const atom = wasm.getAtomPtr(atom_index);
1106 atom.clear();
1107
1096 if (decl.isExtern()) {1108 if (decl.isExtern()) {
1097 const variable = decl.getVariable().?;1109 const variable = decl.getVariable().?;
1098 const name = mem.sliceTo(decl.name, 0);1110 const name = mem.sliceTo(decl.name, 0);
1099 return wasm.addOrUpdateImport(name, decl.link.wasm.sym_index, variable.lib_name, null);1111 return wasm.addOrUpdateImport(name, atom.sym_index, variable.lib_name, null);
1100 }1112 }
1101 const val = if (decl.val.castTag(.variable)) |payload| payload.data.init else decl.val;1113 const val = if (decl.val.castTag(.variable)) |payload| payload.data.init else decl.val;
11021114
...@@ -1109,12 +1121,11 @@ pub fn updateDecl(wasm: *Wasm, mod: *Module, decl_index: Module.Decl.Index) !voi...@@ -1109,12 +1121,11 @@ pub fn updateDecl(wasm: *Wasm, mod: *Module, decl_index: Module.Decl.Index) !voi
1109 .{ .ty = decl.ty, .val = val },1121 .{ .ty = decl.ty, .val = val },
1110 &code_writer,1122 &code_writer,
1111 .none,1123 .none,
1112 .{ .parent_atom_index = decl.link.wasm.sym_index },1124 .{ .parent_atom_index = atom.sym_index },
1113 );1125 );
11141126
1115 const code = switch (res) {1127 const code = switch (res) {
1116 .externally_managed => |x| x,1128 .ok => code_writer.items,
1117 .appended => code_writer.items,
1118 .fail => |em| {1129 .fail => |em| {
1119 decl.analysis = .codegen_failure;1130 decl.analysis = .codegen_failure;
1120 try mod.failed_decls.put(mod.gpa, decl_index, em);1131 try mod.failed_decls.put(mod.gpa, decl_index, em);
...@@ -1122,26 +1133,29 @@ pub fn updateDecl(wasm: *Wasm, mod: *Module, decl_index: Module.Decl.Index) !voi...@@ -1122,26 +1133,29 @@ pub fn updateDecl(wasm: *Wasm, mod: *Module, decl_index: Module.Decl.Index) !voi
1122 },1133 },
1123 };1134 };
11241135
1125 return wasm.finishUpdateDecl(decl, code);1136 return wasm.finishUpdateDecl(decl_index, code);
1126}1137}
11271138
1128pub fn updateDeclLineNumber(wasm: *Wasm, mod: *Module, decl: *const Module.Decl) !void {1139pub fn updateDeclLineNumber(wasm: *Wasm, mod: *Module, decl_index: Module.Decl.Index) !void {
1129 if (wasm.llvm_object) |_| return;1140 if (wasm.llvm_object) |_| return;
1130 if (wasm.dwarf) |*dw| {1141 if (wasm.dwarf) |*dw| {
1131 const tracy = trace(@src());1142 const tracy = trace(@src());
1132 defer tracy.end();1143 defer tracy.end();
11331144
1145 const decl = mod.declPtr(decl_index);
1134 const decl_name = try decl.getFullyQualifiedName(mod);1146 const decl_name = try decl.getFullyQualifiedName(mod);
1135 defer wasm.base.allocator.free(decl_name);1147 defer wasm.base.allocator.free(decl_name);
11361148
1137 log.debug("updateDeclLineNumber {s}{*}", .{ decl_name, decl });1149 log.debug("updateDeclLineNumber {s}{*}", .{ decl_name, decl });
1138 try dw.updateDeclLineNumber(decl);1150 try dw.updateDeclLineNumber(mod, decl_index);
1139 }1151 }
1140}1152}
11411153
1142fn finishUpdateDecl(wasm: *Wasm, decl: *Module.Decl, code: []const u8) !void {1154fn finishUpdateDecl(wasm: *Wasm, decl_index: Module.Decl.Index, code: []const u8) !void {
1143 const mod = wasm.base.options.module.?;1155 const mod = wasm.base.options.module.?;
1144 const atom: *Atom = &decl.link.wasm;1156 const decl = mod.declPtr(decl_index);
1157 const atom_index = wasm.decls.get(decl_index).?;
1158 const atom = wasm.getAtomPtr(atom_index);
1145 const symbol = &wasm.symbols.items[atom.sym_index];1159 const symbol = &wasm.symbols.items[atom.sym_index];
1146 const full_name = try decl.getFullyQualifiedName(mod);1160 const full_name = try decl.getFullyQualifiedName(mod);
1147 defer wasm.base.allocator.free(full_name);1161 defer wasm.base.allocator.free(full_name);
...@@ -1149,8 +1163,8 @@ fn finishUpdateDecl(wasm: *Wasm, decl: *Module.Decl, code: []const u8) !void {...@@ -1149,8 +1163,8 @@ fn finishUpdateDecl(wasm: *Wasm, decl: *Module.Decl, code: []const u8) !void {
1149 try atom.code.appendSlice(wasm.base.allocator, code);1163 try atom.code.appendSlice(wasm.base.allocator, code);
1150 try wasm.resolved_symbols.put(wasm.base.allocator, atom.symbolLoc(), {});1164 try wasm.resolved_symbols.put(wasm.base.allocator, atom.symbolLoc(), {});
11511165
1152 if (code.len == 0) return;
1153 atom.size = @intCast(u32, code.len);1166 atom.size = @intCast(u32, code.len);
1167 if (code.len == 0) return;
1154 atom.alignment = decl.ty.abiAlignment(wasm.base.options.target);1168 atom.alignment = decl.ty.abiAlignment(wasm.base.options.target);
1155}1169}
11561170
...@@ -1207,58 +1221,51 @@ pub fn lowerUnnamedConst(wasm: *Wasm, tv: TypedValue, decl_index: Module.Decl.In...@@ -1207,58 +1221,51 @@ pub fn lowerUnnamedConst(wasm: *Wasm, tv: TypedValue, decl_index: Module.Decl.In
1207 const decl = mod.declPtr(decl_index);1221 const decl = mod.declPtr(decl_index);
12081222
1209 // Create and initialize a new local symbol and atom1223 // Create and initialize a new local symbol and atom
1210 const local_index = decl.link.wasm.locals.items.len;1224 const atom_index = try wasm.createAtom();
1225 const parent_atom_index = try wasm.getOrCreateAtomForDecl(decl_index);
1226 const parent_atom = wasm.getAtomPtr(parent_atom_index);
1227 const local_index = parent_atom.locals.items.len;
1228 try parent_atom.locals.append(wasm.base.allocator, atom_index);
1211 const fqdn = try decl.getFullyQualifiedName(mod);1229 const fqdn = try decl.getFullyQualifiedName(mod);
1212 defer wasm.base.allocator.free(fqdn);1230 defer wasm.base.allocator.free(fqdn);
1213 const name = try std.fmt.allocPrintZ(wasm.base.allocator, "__unnamed_{s}_{d}", .{ fqdn, local_index });1231 const name = try std.fmt.allocPrintZ(wasm.base.allocator, "__unnamed_{s}_{d}", .{ fqdn, local_index });
1214 defer wasm.base.allocator.free(name);1232 defer wasm.base.allocator.free(name);
1215 var symbol: Symbol = .{
1216 .name = try wasm.string_table.put(wasm.base.allocator, name),
1217 .flags = 0,
1218 .tag = .data,
1219 .index = undefined,
1220 };
1221 symbol.setFlag(.WASM_SYM_BINDING_LOCAL);
1222
1223 const atom = try decl.link.wasm.locals.addOne(wasm.base.allocator);
1224 atom.* = Atom.empty;
1225 atom.alignment = tv.ty.abiAlignment(wasm.base.options.target);
1226 try wasm.symbols.ensureUnusedCapacity(wasm.base.allocator, 1);
1227
1228 if (wasm.symbols_free_list.popOrNull()) |index| {
1229 atom.sym_index = index;
1230 wasm.symbols.items[index] = symbol;
1231 } else {
1232 atom.sym_index = @intCast(u32, wasm.symbols.items.len);
1233 wasm.symbols.appendAssumeCapacity(symbol);
1234 }
1235 try wasm.resolved_symbols.putNoClobber(wasm.base.allocator, atom.symbolLoc(), {});
1236 try wasm.symbol_atom.putNoClobber(wasm.base.allocator, atom.symbolLoc(), atom);
1237
1238 var value_bytes = std.ArrayList(u8).init(wasm.base.allocator);1233 var value_bytes = std.ArrayList(u8).init(wasm.base.allocator);
1239 defer value_bytes.deinit();1234 defer value_bytes.deinit();
12401235
1241 const result = try codegen.generateSymbol(1236 const code = code: {
1242 &wasm.base,1237 const atom = wasm.getAtomPtr(atom_index);
1243 decl.srcLoc(),1238 atom.alignment = tv.ty.abiAlignment(wasm.base.options.target);
1244 tv,1239 wasm.symbols.items[atom.sym_index] = .{
1245 &value_bytes,1240 .name = try wasm.string_table.put(wasm.base.allocator, name),
1246 .none,1241 .flags = @enumToInt(Symbol.Flag.WASM_SYM_BINDING_LOCAL),
1247 .{1242 .tag = .data,
1248 .parent_atom_index = atom.sym_index,1243 .index = undefined,
1249 .addend = null,1244 };
1250 },1245 try wasm.resolved_symbols.putNoClobber(wasm.base.allocator, atom.symbolLoc(), {});
1251 );1246
1252 const code = switch (result) {1247 const result = try codegen.generateSymbol(
1253 .externally_managed => |x| x,1248 &wasm.base,
1254 .appended => value_bytes.items,1249 decl.srcLoc(),
1255 .fail => |em| {1250 tv,
1256 decl.analysis = .codegen_failure;1251 &value_bytes,
1257 try mod.failed_decls.put(mod.gpa, decl_index, em);1252 .none,
1258 return error.AnalysisFail;1253 .{
1259 },1254 .parent_atom_index = atom.sym_index,
1255 .addend = null,
1256 },
1257 );
1258 break :code switch (result) {
1259 .ok => value_bytes.items,
1260 .fail => |em| {
1261 decl.analysis = .codegen_failure;
1262 try mod.failed_decls.put(mod.gpa, decl_index, em);
1263 return error.AnalysisFail;
1264 },
1265 };
1260 };1266 };
12611267
1268 const atom = wasm.getAtomPtr(atom_index);
1262 atom.size = @intCast(u32, code.len);1269 atom.size = @intCast(u32, code.len);
1263 try atom.code.appendSlice(wasm.base.allocator, code);1270 try atom.code.appendSlice(wasm.base.allocator, code);
1264 return atom.sym_index;1271 return atom.sym_index;
...@@ -1306,10 +1313,13 @@ pub fn getDeclVAddr(...@@ -1306,10 +1313,13 @@ pub fn getDeclVAddr(
1306) !u64 {1313) !u64 {
1307 const mod = wasm.base.options.module.?;1314 const mod = wasm.base.options.module.?;
1308 const decl = mod.declPtr(decl_index);1315 const decl = mod.declPtr(decl_index);
1309 const target_symbol_index = decl.link.wasm.sym_index;1316
1310 assert(target_symbol_index != 0);1317 const target_atom_index = try wasm.getOrCreateAtomForDecl(decl_index);
1318 const target_symbol_index = wasm.getAtom(target_atom_index).sym_index;
1319
1311 assert(reloc_info.parent_atom_index != 0);1320 assert(reloc_info.parent_atom_index != 0);
1312 const atom = wasm.symbol_atom.get(.{ .file = null, .index = reloc_info.parent_atom_index }).?;1321 const atom_index = wasm.symbol_atom.get(.{ .file = null, .index = reloc_info.parent_atom_index }).?;
1322 const atom = wasm.getAtomPtr(atom_index);
1313 const is_wasm32 = wasm.base.options.target.cpu.arch == .wasm32;1323 const is_wasm32 = wasm.base.options.target.cpu.arch == .wasm32;
1314 if (decl.ty.zigTypeTag() == .Fn) {1324 if (decl.ty.zigTypeTag() == .Fn) {
1315 assert(reloc_info.addend == 0); // addend not allowed for function relocations1325 assert(reloc_info.addend == 0); // addend not allowed for function relocations
...@@ -1337,9 +1347,10 @@ pub fn getDeclVAddr(...@@ -1337,9 +1347,10 @@ pub fn getDeclVAddr(
1337 return target_symbol_index;1347 return target_symbol_index;
1338}1348}
13391349
1340pub fn deleteExport(wasm: *Wasm, exp: Export) void {1350pub fn deleteDeclExport(wasm: *Wasm, decl_index: Module.Decl.Index) void {
1341 if (wasm.llvm_object) |_| return;1351 if (wasm.llvm_object) |_| return;
1342 const sym_index = exp.sym_index orelse return;1352 const atom_index = wasm.decls.get(decl_index) orelse return;
1353 const sym_index = wasm.getAtom(atom_index).sym_index;
1343 const loc: SymbolLoc = .{ .file = null, .index = sym_index };1354 const loc: SymbolLoc = .{ .file = null, .index = sym_index };
1344 const symbol = loc.getSymbol(wasm);1355 const symbol = loc.getSymbol(wasm);
1345 const symbol_name = wasm.string_table.get(symbol.name);1356 const symbol_name = wasm.string_table.get(symbol.name);
...@@ -1365,6 +1376,8 @@ pub fn updateDeclExports(...@@ -1365,6 +1376,8 @@ pub fn updateDeclExports(
1365 }1376 }
13661377
1367 const decl = mod.declPtr(decl_index);1378 const decl = mod.declPtr(decl_index);
1379 const atom_index = try wasm.getOrCreateAtomForDecl(decl_index);
1380 const atom = wasm.getAtom(atom_index);
13681381
1369 for (exports) |exp| {1382 for (exports) |exp| {
1370 if (exp.options.section) |section| {1383 if (exp.options.section) |section| {
...@@ -1379,7 +1392,7 @@ pub fn updateDeclExports(...@@ -1379,7 +1392,7 @@ pub fn updateDeclExports(
13791392
1380 const export_name = try wasm.string_table.put(wasm.base.allocator, exp.options.name);1393 const export_name = try wasm.string_table.put(wasm.base.allocator, exp.options.name);
1381 if (wasm.globals.getPtr(export_name)) |existing_loc| {1394 if (wasm.globals.getPtr(export_name)) |existing_loc| {
1382 if (existing_loc.index == decl.link.wasm.sym_index) continue;1395 if (existing_loc.index == atom.sym_index) continue;
1383 const existing_sym: Symbol = existing_loc.getSymbol(wasm).*;1396 const existing_sym: Symbol = existing_loc.getSymbol(wasm).*;
13841397
1385 const exp_is_weak = exp.options.linkage == .Internal or exp.options.linkage == .Weak;1398 const exp_is_weak = exp.options.linkage == .Internal or exp.options.linkage == .Weak;
...@@ -1400,15 +1413,16 @@ pub fn updateDeclExports(...@@ -1400,15 +1413,16 @@ pub fn updateDeclExports(
1400 } else if (exp_is_weak) {1413 } else if (exp_is_weak) {
1401 continue; // to-be-exported symbol is weak, so we keep the existing symbol1414 continue; // to-be-exported symbol is weak, so we keep the existing symbol
1402 } else {1415 } else {
1403 existing_loc.index = decl.link.wasm.sym_index;1416 // TODO: Revisit this, why was this needed?
1417 existing_loc.index = atom.sym_index;
1404 existing_loc.file = null;1418 existing_loc.file = null;
1405 exp.link.wasm.sym_index = existing_loc.index;1419 // exp.link.wasm.sym_index = existing_loc.index;
1406 }1420 }
1407 }1421 }
14081422
1409 const exported_decl = mod.declPtr(exp.exported_decl);1423 const exported_atom_index = try wasm.getOrCreateAtomForDecl(exp.exported_decl);
1410 const sym_index = exported_decl.link.wasm.sym_index;1424 const exported_atom = wasm.getAtom(exported_atom_index);
1411 const sym_loc = exported_decl.link.wasm.symbolLoc();1425 const sym_loc = exported_atom.symbolLoc();
1412 const symbol = sym_loc.getSymbol(wasm);1426 const symbol = sym_loc.getSymbol(wasm);
1413 switch (exp.options.linkage) {1427 switch (exp.options.linkage) {
1414 .Internal => {1428 .Internal => {
...@@ -1444,7 +1458,6 @@ pub fn updateDeclExports(...@@ -1444,7 +1458,6 @@ pub fn updateDeclExports(
1444 // if the symbol was previously undefined, remove it as an import1458 // if the symbol was previously undefined, remove it as an import
1445 _ = wasm.imports.remove(sym_loc);1459 _ = wasm.imports.remove(sym_loc);
1446 _ = wasm.undefs.swapRemove(exp.options.name);1460 _ = wasm.undefs.swapRemove(exp.options.name);
1447 exp.link.wasm.sym_index = sym_index;
1448 }1461 }
1449}1462}
14501463
...@@ -1454,11 +1467,13 @@ pub fn freeDecl(wasm: *Wasm, decl_index: Module.Decl.Index) void {...@@ -1454,11 +1467,13 @@ pub fn freeDecl(wasm: *Wasm, decl_index: Module.Decl.Index) void {
1454 }1467 }
1455 const mod = wasm.base.options.module.?;1468 const mod = wasm.base.options.module.?;
1456 const decl = mod.declPtr(decl_index);1469 const decl = mod.declPtr(decl_index);
1457 const atom = &decl.link.wasm;1470 const atom_index = wasm.decls.get(decl_index).?;
1471 const atom = wasm.getAtomPtr(atom_index);
1458 wasm.symbols_free_list.append(wasm.base.allocator, atom.sym_index) catch {};1472 wasm.symbols_free_list.append(wasm.base.allocator, atom.sym_index) catch {};
1459 _ = wasm.decls.remove(decl_index);1473 _ = wasm.decls.remove(decl_index);
1460 wasm.symbols.items[atom.sym_index].tag = .dead;1474 wasm.symbols.items[atom.sym_index].tag = .dead;
1461 for (atom.locals.items) |local_atom| {1475 for (atom.locals.items) |local_atom_index| {
1476 const local_atom = wasm.getAtom(local_atom_index);
1462 const local_symbol = &wasm.symbols.items[local_atom.sym_index];1477 const local_symbol = &wasm.symbols.items[local_atom.sym_index];
1463 local_symbol.tag = .dead; // also for any local symbol1478 local_symbol.tag = .dead; // also for any local symbol
1464 wasm.symbols_free_list.append(wasm.base.allocator, local_atom.sym_index) catch {};1479 wasm.symbols_free_list.append(wasm.base.allocator, local_atom.sym_index) catch {};
...@@ -1472,12 +1487,20 @@ pub fn freeDecl(wasm: *Wasm, decl_index: Module.Decl.Index) void {...@@ -1472,12 +1487,20 @@ pub fn freeDecl(wasm: *Wasm, decl_index: Module.Decl.Index) void {
1472 _ = wasm.resolved_symbols.swapRemove(atom.symbolLoc());1487 _ = wasm.resolved_symbols.swapRemove(atom.symbolLoc());
1473 _ = wasm.symbol_atom.remove(atom.symbolLoc());1488 _ = wasm.symbol_atom.remove(atom.symbolLoc());
14741489
1475 if (wasm.dwarf) |*dwarf| {1490 // if (wasm.dwarf) |*dwarf| {
1476 dwarf.freeDecl(decl);1491 // dwarf.freeDecl(decl_index);
1477 dwarf.freeAtom(&atom.dbg_info_atom);1492 // }
1478 }
14791493
1480 atom.deinit(wasm.base.allocator);1494 if (atom.next) |next_atom_index| {
1495 const next_atom = wasm.getAtomPtr(next_atom_index);
1496 next_atom.prev = atom.prev;
1497 atom.next = null;
1498 }
1499 if (atom.prev) |prev_index| {
1500 const prev_atom = wasm.getAtomPtr(prev_index);
1501 prev_atom.next = atom.next;
1502 atom.prev = null;
1503 }
1481}1504}
14821505
1483/// Appends a new entry to the indirect function table1506/// Appends a new entry to the indirect function table
...@@ -1599,7 +1622,8 @@ const Kind = union(enum) {...@@ -1599,7 +1622,8 @@ const Kind = union(enum) {
1599};1622};
16001623
1601/// Parses an Atom and inserts its metadata into the corresponding sections.1624/// Parses an Atom and inserts its metadata into the corresponding sections.
1602fn parseAtom(wasm: *Wasm, atom: *Atom, kind: Kind) !void {1625fn parseAtom(wasm: *Wasm, atom_index: Atom.Index, kind: Kind) !void {
1626 const atom = wasm.getAtomPtr(atom_index);
1603 const symbol = (SymbolLoc{ .file = null, .index = atom.sym_index }).getSymbol(wasm);1627 const symbol = (SymbolLoc{ .file = null, .index = atom.sym_index }).getSymbol(wasm);
1604 const final_index: u32 = switch (kind) {1628 const final_index: u32 = switch (kind) {
1605 .function => |fn_data| result: {1629 .function => |fn_data| result: {
...@@ -1674,18 +1698,20 @@ fn parseAtom(wasm: *Wasm, atom: *Atom, kind: Kind) !void {...@@ -1674,18 +1698,20 @@ fn parseAtom(wasm: *Wasm, atom: *Atom, kind: Kind) !void {
1674 const segment: *Segment = &wasm.segments.items[final_index];1698 const segment: *Segment = &wasm.segments.items[final_index];
1675 segment.alignment = std.math.max(segment.alignment, atom.alignment);1699 segment.alignment = std.math.max(segment.alignment, atom.alignment);
16761700
1677 try wasm.appendAtomAtIndex(final_index, atom);1701 try wasm.appendAtomAtIndex(final_index, atom_index);
1678}1702}
16791703
1680/// From a given index, append the given `Atom` at the back of the linked list.1704/// From a given index, append the given `Atom` at the back of the linked list.
1681/// Simply inserts it into the map of atoms when it doesn't exist yet.1705/// Simply inserts it into the map of atoms when it doesn't exist yet.
1682pub fn appendAtomAtIndex(wasm: *Wasm, index: u32, atom: *Atom) !void {1706pub fn appendAtomAtIndex(wasm: *Wasm, index: u32, atom_index: Atom.Index) !void {
1683 if (wasm.atoms.getPtr(index)) |last| {1707 const atom = wasm.getAtomPtr(atom_index);
1684 last.*.next = atom;1708 if (wasm.atoms.getPtr(index)) |last_index_ptr| {
1685 atom.prev = last.*;1709 const last = wasm.getAtomPtr(last_index_ptr.*);
1686 last.* = atom;1710 last.*.next = atom_index;
1711 atom.prev = last_index_ptr.*;
1712 last_index_ptr.* = atom_index;
1687 } else {1713 } else {
1688 try wasm.atoms.putNoClobber(wasm.base.allocator, index, atom);1714 try wasm.atoms.putNoClobber(wasm.base.allocator, index, atom_index);
1689 }1715 }
1690}1716}
16911717
...@@ -1695,16 +1721,17 @@ fn allocateDebugAtoms(wasm: *Wasm) !void {...@@ -1695,16 +1721,17 @@ fn allocateDebugAtoms(wasm: *Wasm) !void {
1695 if (wasm.dwarf == null) return;1721 if (wasm.dwarf == null) return;
16961722
1697 const allocAtom = struct {1723 const allocAtom = struct {
1698 fn f(bin: *Wasm, maybe_index: *?u32, atom: *Atom) !void {1724 fn f(bin: *Wasm, maybe_index: *?u32, atom_index: Atom.Index) !void {
1699 const index = maybe_index.* orelse idx: {1725 const index = maybe_index.* orelse idx: {
1700 const index = @intCast(u32, bin.segments.items.len);1726 const index = @intCast(u32, bin.segments.items.len);
1701 try bin.appendDummySegment();1727 try bin.appendDummySegment();
1702 maybe_index.* = index;1728 maybe_index.* = index;
1703 break :idx index;1729 break :idx index;
1704 };1730 };
1731 const atom = bin.getAtomPtr(atom_index);
1705 atom.size = @intCast(u32, atom.code.items.len);1732 atom.size = @intCast(u32, atom.code.items.len);
1706 bin.symbols.items[atom.sym_index].index = index;1733 bin.symbols.items[atom.sym_index].index = index;
1707 try bin.appendAtomAtIndex(index, atom);1734 try bin.appendAtomAtIndex(index, atom_index);
1708 }1735 }
1709 }.f;1736 }.f;
17101737
...@@ -1726,15 +1753,16 @@ fn allocateAtoms(wasm: *Wasm) !void {...@@ -1726,15 +1753,16 @@ fn allocateAtoms(wasm: *Wasm) !void {
1726 var it = wasm.atoms.iterator();1753 var it = wasm.atoms.iterator();
1727 while (it.next()) |entry| {1754 while (it.next()) |entry| {
1728 const segment = &wasm.segments.items[entry.key_ptr.*];1755 const segment = &wasm.segments.items[entry.key_ptr.*];
1729 var atom: *Atom = entry.value_ptr.*.getFirst();1756 var atom_index = entry.value_ptr.*;
1730 var offset: u32 = 0;1757 var offset: u32 = 0;
1731 while (true) {1758 while (true) {
1759 const atom = wasm.getAtomPtr(atom_index);
1732 const symbol_loc = atom.symbolLoc();1760 const symbol_loc = atom.symbolLoc();
1733 if (wasm.code_section_index) |index| {1761 if (wasm.code_section_index) |index| {
1734 if (index == entry.key_ptr.*) {1762 if (index == entry.key_ptr.*) {
1735 if (!wasm.resolved_symbols.contains(symbol_loc)) {1763 if (!wasm.resolved_symbols.contains(symbol_loc)) {
1736 // only allocate resolved function body's.1764 // only allocate resolved function body's.
1737 atom = atom.next orelse break;1765 atom_index = atom.prev orelse break;
1738 continue;1766 continue;
1739 }1767 }
1740 }1768 }
...@@ -1748,8 +1776,7 @@ fn allocateAtoms(wasm: *Wasm) !void {...@@ -1748,8 +1776,7 @@ fn allocateAtoms(wasm: *Wasm) !void {
1748 atom.size,1776 atom.size,
1749 });1777 });
1750 offset += atom.size;1778 offset += atom.size;
1751 try wasm.symbol_atom.put(wasm.base.allocator, symbol_loc, atom); // Update atom pointers1779 atom_index = atom.prev orelse break;
1752 atom = atom.next orelse break;
1753 }1780 }
1754 segment.size = std.mem.alignForwardGeneric(u32, offset, segment.alignment);1781 segment.size = std.mem.alignForwardGeneric(u32, offset, segment.alignment);
1755 }1782 }
...@@ -1883,8 +1910,8 @@ fn initializeCallCtorsFunction(wasm: *Wasm) !void {...@@ -1883,8 +1910,8 @@ fn initializeCallCtorsFunction(wasm: *Wasm) !void {
1883 symbol.index = func_index;1910 symbol.index = func_index;
18841911
1885 // create the atom that will be output into the final binary1912 // create the atom that will be output into the final binary
1886 const atom = try wasm.base.allocator.create(Atom);1913 const atom_index = @intCast(Atom.Index, wasm.managed_atoms.items.len);
1887 errdefer wasm.base.allocator.destroy(atom);1914 const atom = try wasm.managed_atoms.addOne(wasm.base.allocator);
1888 atom.* = .{1915 atom.* = .{
1889 .size = @intCast(u32, function_body.items.len),1916 .size = @intCast(u32, function_body.items.len),
1890 .offset = 0,1917 .offset = 0,
...@@ -1894,15 +1921,14 @@ fn initializeCallCtorsFunction(wasm: *Wasm) !void {...@@ -1894,15 +1921,14 @@ fn initializeCallCtorsFunction(wasm: *Wasm) !void {
1894 .next = null,1921 .next = null,
1895 .prev = null,1922 .prev = null,
1896 .code = function_body.moveToUnmanaged(),1923 .code = function_body.moveToUnmanaged(),
1897 .dbg_info_atom = undefined,
1898 };1924 };
1899 try wasm.managed_atoms.append(wasm.base.allocator, atom);1925 try wasm.appendAtomAtIndex(wasm.code_section_index.?, atom_index);
1900 try wasm.appendAtomAtIndex(wasm.code_section_index.?, atom);1926 try wasm.symbol_atom.putNoClobber(wasm.base.allocator, loc, atom_index);
1901 try wasm.symbol_atom.putNoClobber(wasm.base.allocator, loc, atom);
19021927
1903 // `allocateAtoms` has already been called, set the atom's offset manually.1928 // `allocateAtoms` has already been called, set the atom's offset manually.
1904 // This is fine to do manually as we insert the atom at the very end.1929 // This is fine to do manually as we insert the atom at the very end.
1905 atom.offset = atom.prev.?.offset + atom.prev.?.size;1930 const prev_atom = wasm.getAtom(atom.prev.?);
1931 atom.offset = prev_atom.offset + prev_atom.size;
1906}1932}
19071933
1908fn setupImports(wasm: *Wasm) !void {1934fn setupImports(wasm: *Wasm) !void {
...@@ -2105,7 +2131,8 @@ fn setupExports(wasm: *Wasm) !void {...@@ -2105,7 +2131,8 @@ fn setupExports(wasm: *Wasm) !void {
2105 break :blk try wasm.string_table.put(wasm.base.allocator, sym_name);2131 break :blk try wasm.string_table.put(wasm.base.allocator, sym_name);
2106 };2132 };
2107 const exp: types.Export = if (symbol.tag == .data) exp: {2133 const exp: types.Export = if (symbol.tag == .data) exp: {
2108 const atom = wasm.symbol_atom.get(sym_loc).?;2134 const atom_index = wasm.symbol_atom.get(sym_loc).?;
2135 const atom = wasm.getAtom(atom_index);
2109 const va = atom.getVA(wasm, symbol);2136 const va = atom.getVA(wasm, symbol);
2110 const global_index = @intCast(u32, wasm.imported_globals_count + wasm.wasm_globals.items.len);2137 const global_index = @intCast(u32, wasm.imported_globals_count + wasm.wasm_globals.items.len);
2111 try wasm.wasm_globals.append(wasm.base.allocator, .{2138 try wasm.wasm_globals.append(wasm.base.allocator, .{
...@@ -2210,7 +2237,8 @@ fn setupMemory(wasm: *Wasm) !void {...@@ -2210,7 +2237,8 @@ fn setupMemory(wasm: *Wasm) !void {
2210 const segment_index = wasm.data_segments.get(".synthetic").?;2237 const segment_index = wasm.data_segments.get(".synthetic").?;
2211 const segment = &wasm.segments.items[segment_index];2238 const segment = &wasm.segments.items[segment_index];
2212 segment.offset = 0; // for simplicity we store the entire VA into atom's offset.2239 segment.offset = 0; // for simplicity we store the entire VA into atom's offset.
2213 const atom = wasm.symbol_atom.get(loc).?;2240 const atom_index = wasm.symbol_atom.get(loc).?;
2241 const atom = wasm.getAtomPtr(atom_index);
2214 atom.offset = @intCast(u32, mem.alignForwardGeneric(u64, memory_ptr, heap_alignment));2242 atom.offset = @intCast(u32, mem.alignForwardGeneric(u64, memory_ptr, heap_alignment));
2215 }2243 }
22162244
...@@ -2243,7 +2271,8 @@ fn setupMemory(wasm: *Wasm) !void {...@@ -2243,7 +2271,8 @@ fn setupMemory(wasm: *Wasm) !void {
2243 const segment_index = wasm.data_segments.get(".synthetic").?;2271 const segment_index = wasm.data_segments.get(".synthetic").?;
2244 const segment = &wasm.segments.items[segment_index];2272 const segment = &wasm.segments.items[segment_index];
2245 segment.offset = 0;2273 segment.offset = 0;
2246 const atom = wasm.symbol_atom.get(loc).?;2274 const atom_index = wasm.symbol_atom.get(loc).?;
2275 const atom = wasm.getAtomPtr(atom_index);
2247 atom.offset = @intCast(u32, memory_ptr);2276 atom.offset = @intCast(u32, memory_ptr);
2248 }2277 }
22492278
...@@ -2369,15 +2398,14 @@ pub fn getErrorTableSymbol(wasm: *Wasm) !u32 {...@@ -2369,15 +2398,14 @@ pub fn getErrorTableSymbol(wasm: *Wasm) !u32 {
2369 // and then return said symbol's index. The final table will be populated2398 // and then return said symbol's index. The final table will be populated
2370 // during `flush` when we know all possible error names.2399 // during `flush` when we know all possible error names.
23712400
2372 // As sym_index '0' is reserved, we use it for our stack pointer symbol2401 const atom_index = try wasm.createAtom();
2373 const symbol_index = wasm.symbols_free_list.popOrNull() orelse blk: {2402 const atom = wasm.getAtomPtr(atom_index);
2374 const index = @intCast(u32, wasm.symbols.items.len);2403 const slice_ty = Type.initTag(.const_slice_u8_sentinel_0);
2375 _ = try wasm.symbols.addOne(wasm.base.allocator);2404 atom.alignment = slice_ty.abiAlignment(wasm.base.options.target);
2376 break :blk index;2405 const sym_index = atom.sym_index;
2377 };
23782406
2379 const sym_name = try wasm.string_table.put(wasm.base.allocator, "__zig_err_name_table");2407 const sym_name = try wasm.string_table.put(wasm.base.allocator, "__zig_err_name_table");
2380 const symbol = &wasm.symbols.items[symbol_index];2408 const symbol = &wasm.symbols.items[sym_index];
2381 symbol.* = .{2409 symbol.* = .{
2382 .name = sym_name,2410 .name = sym_name,
2383 .tag = .data,2411 .tag = .data,
...@@ -2386,20 +2414,11 @@ pub fn getErrorTableSymbol(wasm: *Wasm) !u32 {...@@ -2386,20 +2414,11 @@ pub fn getErrorTableSymbol(wasm: *Wasm) !u32 {
2386 };2414 };
2387 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);2415 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
23882416
2389 const slice_ty = Type.initTag(.const_slice_u8_sentinel_0);2417 try wasm.resolved_symbols.put(wasm.base.allocator, atom.symbolLoc(), {});
23902418
2391 const atom = try wasm.base.allocator.create(Atom);2419 log.debug("Error name table was created with symbol index: ({d})", .{sym_index});
2392 atom.* = Atom.empty;2420 wasm.error_table_symbol = sym_index;
2393 atom.sym_index = symbol_index;2421 return sym_index;
2394 atom.alignment = slice_ty.abiAlignment(wasm.base.options.target);
2395 try wasm.managed_atoms.append(wasm.base.allocator, atom);
2396 const loc = atom.symbolLoc();
2397 try wasm.resolved_symbols.put(wasm.base.allocator, loc, {});
2398 try wasm.symbol_atom.put(wasm.base.allocator, loc, atom);
2399
2400 log.debug("Error name table was created with symbol index: ({d})", .{symbol_index});
2401 wasm.error_table_symbol = symbol_index;
2402 return symbol_index;
2403}2422}
24042423
2405/// Populates the error name table, when `error_table_symbol` is not null.2424/// Populates the error name table, when `error_table_symbol` is not null.
...@@ -2408,22 +2427,17 @@ pub fn getErrorTableSymbol(wasm: *Wasm) !u32 {...@@ -2408,22 +2427,17 @@ pub fn getErrorTableSymbol(wasm: *Wasm) !u32 {
2408/// The table is what is being pointed to within the runtime bodies that are generated.2427/// The table is what is being pointed to within the runtime bodies that are generated.
2409fn populateErrorNameTable(wasm: *Wasm) !void {2428fn populateErrorNameTable(wasm: *Wasm) !void {
2410 const symbol_index = wasm.error_table_symbol orelse return;2429 const symbol_index = wasm.error_table_symbol orelse return;
2411 const atom: *Atom = wasm.symbol_atom.get(.{ .file = null, .index = symbol_index }).?;2430 const atom_index = wasm.symbol_atom.get(.{ .file = null, .index = symbol_index }).?;
2431 const atom = wasm.getAtomPtr(atom_index);
2432
2412 // Rather than creating a symbol for each individual error name,2433 // Rather than creating a symbol for each individual error name,
2413 // we create a symbol for the entire region of error names. We then calculate2434 // we create a symbol for the entire region of error names. We then calculate
2414 // the pointers into the list using addends which are appended to the relocation.2435 // the pointers into the list using addends which are appended to the relocation.
2415 const names_atom = try wasm.base.allocator.create(Atom);2436 const names_atom_index = try wasm.createAtom();
2416 names_atom.* = Atom.empty;2437 const names_atom = wasm.getAtomPtr(names_atom_index);
2417 try wasm.managed_atoms.append(wasm.base.allocator, names_atom);
2418 const names_symbol_index = wasm.symbols_free_list.popOrNull() orelse blk: {
2419 const index = @intCast(u32, wasm.symbols.items.len);
2420 _ = try wasm.symbols.addOne(wasm.base.allocator);
2421 break :blk index;
2422 };
2423 names_atom.sym_index = names_symbol_index;
2424 names_atom.alignment = 1;2438 names_atom.alignment = 1;
2425 const sym_name = try wasm.string_table.put(wasm.base.allocator, "__zig_err_names");2439 const sym_name = try wasm.string_table.put(wasm.base.allocator, "__zig_err_names");
2426 const names_symbol = &wasm.symbols.items[names_symbol_index];2440 const names_symbol = &wasm.symbols.items[names_atom.sym_index];
2427 names_symbol.* = .{2441 names_symbol.* = .{
2428 .name = sym_name,2442 .name = sym_name,
2429 .tag = .data,2443 .tag = .data,
...@@ -2447,7 +2461,7 @@ fn populateErrorNameTable(wasm: *Wasm) !void {...@@ -2447,7 +2461,7 @@ fn populateErrorNameTable(wasm: *Wasm) !void {
2447 try atom.code.writer(wasm.base.allocator).writeIntLittle(u32, len - 1);2461 try atom.code.writer(wasm.base.allocator).writeIntLittle(u32, len - 1);
2448 // create relocation to the error name2462 // create relocation to the error name
2449 try atom.relocs.append(wasm.base.allocator, .{2463 try atom.relocs.append(wasm.base.allocator, .{
2450 .index = names_symbol_index,2464 .index = names_atom.sym_index,
2451 .relocation_type = .R_WASM_MEMORY_ADDR_I32,2465 .relocation_type = .R_WASM_MEMORY_ADDR_I32,
2452 .offset = offset,2466 .offset = offset,
2453 .addend = @intCast(i32, addend),2467 .addend = @intCast(i32, addend),
...@@ -2466,61 +2480,53 @@ fn populateErrorNameTable(wasm: *Wasm) !void {...@@ -2466,61 +2480,53 @@ fn populateErrorNameTable(wasm: *Wasm) !void {
24662480
2467 const name_loc = names_atom.symbolLoc();2481 const name_loc = names_atom.symbolLoc();
2468 try wasm.resolved_symbols.put(wasm.base.allocator, name_loc, {});2482 try wasm.resolved_symbols.put(wasm.base.allocator, name_loc, {});
2469 try wasm.symbol_atom.put(wasm.base.allocator, name_loc, names_atom);2483 try wasm.symbol_atom.put(wasm.base.allocator, name_loc, names_atom_index);
24702484
2471 // link the atoms with the rest of the binary so they can be allocated2485 // link the atoms with the rest of the binary so they can be allocated
2472 // and relocations will be performed.2486 // and relocations will be performed.
2473 try wasm.parseAtom(atom, .{ .data = .read_only });2487 try wasm.parseAtom(atom_index, .{ .data = .read_only });
2474 try wasm.parseAtom(names_atom, .{ .data = .read_only });2488 try wasm.parseAtom(names_atom_index, .{ .data = .read_only });
2475}2489}
24762490
2477/// From a given index variable, creates a new debug section.2491/// From a given index variable, creates a new debug section.
2478/// This initializes the index, appends a new segment,2492/// This initializes the index, appends a new segment,
2479/// and finally, creates a managed `Atom`.2493/// and finally, creates a managed `Atom`.
2480pub fn createDebugSectionForIndex(wasm: *Wasm, index: *?u32, name: []const u8) !*Atom {2494pub fn createDebugSectionForIndex(wasm: *Wasm, index: *?u32, name: []const u8) !Atom.Index {
2481 const new_index = @intCast(u32, wasm.segments.items.len);2495 const new_index = @intCast(u32, wasm.segments.items.len);
2482 index.* = new_index;2496 index.* = new_index;
2483 try wasm.appendDummySegment();2497 try wasm.appendDummySegment();
24842498
2485 const sym_index = wasm.symbols_free_list.popOrNull() orelse idx: {2499 const atom_index = try wasm.createAtom();
2486 const tmp_index = @intCast(u32, wasm.symbols.items.len);2500 const atom = wasm.getAtomPtr(atom_index);
2487 _ = try wasm.symbols.addOne(wasm.base.allocator);2501 wasm.symbols.items[atom.sym_index] = .{
2488 break :idx tmp_index;
2489 };
2490 wasm.symbols.items[sym_index] = .{
2491 .tag = .section,2502 .tag = .section,
2492 .name = try wasm.string_table.put(wasm.base.allocator, name),2503 .name = try wasm.string_table.put(wasm.base.allocator, name),
2493 .index = 0,2504 .index = 0,
2494 .flags = @enumToInt(Symbol.Flag.WASM_SYM_BINDING_LOCAL),2505 .flags = @enumToInt(Symbol.Flag.WASM_SYM_BINDING_LOCAL),
2495 };2506 };
24962507
2497 const atom = try wasm.base.allocator.create(Atom);
2498 atom.* = Atom.empty;
2499 atom.alignment = 1; // debug sections are always 1-byte-aligned2508 atom.alignment = 1; // debug sections are always 1-byte-aligned
2500 atom.sym_index = sym_index;2509 return atom_index;
2501 try wasm.managed_atoms.append(wasm.base.allocator, atom);
2502 try wasm.symbol_atom.put(wasm.base.allocator, atom.symbolLoc(), atom);
2503 return atom;
2504}2510}
25052511
2506fn resetState(wasm: *Wasm) void {2512fn resetState(wasm: *Wasm) void {
2507 for (wasm.segment_info.values()) |segment_info| {2513 for (wasm.segment_info.values()) |segment_info| {
2508 wasm.base.allocator.free(segment_info.name);2514 wasm.base.allocator.free(segment_info.name);
2509 }2515 }
2510 if (wasm.base.options.module) |mod| {2516
2511 var decl_it = wasm.decls.keyIterator();2517 var atom_it = wasm.decls.valueIterator();
2512 while (decl_it.next()) |decl_index_ptr| {2518 while (atom_it.next()) |atom_index| {
2513 const decl = mod.declPtr(decl_index_ptr.*);2519 const atom = wasm.getAtomPtr(atom_index.*);
2514 const atom = &decl.link.wasm;2520 atom.next = null;
2515 atom.next = null;2521 atom.prev = null;
2516 atom.prev = null;2522
25172523 for (atom.locals.items) |local_atom_index| {
2518 for (atom.locals.items) |*local_atom| {2524 const local_atom = wasm.getAtomPtr(local_atom_index);
2519 local_atom.next = null;2525 local_atom.next = null;
2520 local_atom.prev = null;2526 local_atom.prev = null;
2521 }
2522 }2527 }
2523 }2528 }
2529
2524 wasm.functions.clearRetainingCapacity();2530 wasm.functions.clearRetainingCapacity();
2525 wasm.exports.clearRetainingCapacity();2531 wasm.exports.clearRetainingCapacity();
2526 wasm.segments.clearRetainingCapacity();2532 wasm.segments.clearRetainingCapacity();
...@@ -2817,28 +2823,29 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod...@@ -2817,28 +2823,29 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
2817 try wasm.setupStart();2823 try wasm.setupStart();
2818 try wasm.setupImports();2824 try wasm.setupImports();
2819 if (wasm.base.options.module) |mod| {2825 if (wasm.base.options.module) |mod| {
2820 var decl_it = wasm.decls.keyIterator();2826 var decl_it = wasm.decls.iterator();
2821 while (decl_it.next()) |decl_index_ptr| {2827 while (decl_it.next()) |entry| {
2822 const decl = mod.declPtr(decl_index_ptr.*);2828 const decl = mod.declPtr(entry.key_ptr.*);
2823 if (decl.isExtern()) continue;2829 if (decl.isExtern()) continue;
2824 const atom = &decl.*.link.wasm;2830 const atom_index = entry.value_ptr.*;
2825 if (decl.ty.zigTypeTag() == .Fn) {2831 if (decl.ty.zigTypeTag() == .Fn) {
2826 try wasm.parseAtom(atom, .{ .function = decl.fn_link.wasm });2832 try wasm.parseAtom(atom_index, .{ .function = decl.fn_link.? });
2827 } else if (decl.getVariable()) |variable| {2833 } else if (decl.getVariable()) |variable| {
2828 if (!variable.is_mutable) {2834 if (!variable.is_mutable) {
2829 try wasm.parseAtom(atom, .{ .data = .read_only });2835 try wasm.parseAtom(atom_index, .{ .data = .read_only });
2830 } else if (variable.init.isUndefDeep()) {2836 } else if (variable.init.isUndefDeep()) {
2831 try wasm.parseAtom(atom, .{ .data = .uninitialized });2837 try wasm.parseAtom(atom_index, .{ .data = .uninitialized });
2832 } else {2838 } else {
2833 try wasm.parseAtom(atom, .{ .data = .initialized });2839 try wasm.parseAtom(atom_index, .{ .data = .initialized });
2834 }2840 }
2835 } else {2841 } else {
2836 try wasm.parseAtom(atom, .{ .data = .read_only });2842 try wasm.parseAtom(atom_index, .{ .data = .read_only });
2837 }2843 }
28382844
2839 // also parse atoms for a decl's locals2845 // also parse atoms for a decl's locals
2840 for (atom.locals.items) |*local_atom| {2846 const atom = wasm.getAtomPtr(atom_index);
2841 try wasm.parseAtom(local_atom, .{ .data = .read_only });2847 for (atom.locals.items) |local_atom_index| {
2848 try wasm.parseAtom(local_atom_index, .{ .data = .read_only });
2842 }2849 }
2843 }2850 }
28442851
...@@ -3083,20 +3090,22 @@ fn writeToFile(...@@ -3083,20 +3090,22 @@ fn writeToFile(
3083 var code_section_size: u32 = 0;3090 var code_section_size: u32 = 0;
3084 if (wasm.code_section_index) |code_index| {3091 if (wasm.code_section_index) |code_index| {
3085 const header_offset = try reserveVecSectionHeader(&binary_bytes);3092 const header_offset = try reserveVecSectionHeader(&binary_bytes);
3086 var atom: *Atom = wasm.atoms.get(code_index).?.getFirst();3093 var atom_index = wasm.atoms.get(code_index).?;
30873094
3088 // The code section must be sorted in line with the function order.3095 // The code section must be sorted in line with the function order.
3089 var sorted_atoms = try std.ArrayList(*Atom).initCapacity(wasm.base.allocator, wasm.functions.count());3096 var sorted_atoms = try std.ArrayList(*Atom).initCapacity(wasm.base.allocator, wasm.functions.count());
3090 defer sorted_atoms.deinit();3097 defer sorted_atoms.deinit();
30913098
3092 while (true) {3099 while (true) {
3100 var atom = wasm.getAtomPtr(atom_index);
3093 if (wasm.resolved_symbols.contains(atom.symbolLoc())) {3101 if (wasm.resolved_symbols.contains(atom.symbolLoc())) {
3094 if (!is_obj) {3102 if (!is_obj) {
3095 atom.resolveRelocs(wasm);3103 atom.resolveRelocs(wasm);
3096 }3104 }
3097 sorted_atoms.appendAssumeCapacity(atom);3105 sorted_atoms.appendAssumeCapacity(atom);
3098 }3106 }
3099 atom = atom.next orelse break;3107 // atom = if (atom.prev) |prev| wasm.getAtomPtr(prev) else break;
3108 atom_index = atom.prev orelse break;
3100 }3109 }
31013110
3102 const atom_sort_fn = struct {3111 const atom_sort_fn = struct {
...@@ -3136,11 +3145,11 @@ fn writeToFile(...@@ -3136,11 +3145,11 @@ fn writeToFile(
3136 // do not output 'bss' section unless we import memory and therefore3145 // do not output 'bss' section unless we import memory and therefore
3137 // want to guarantee the data is zero initialized3146 // want to guarantee the data is zero initialized
3138 if (!import_memory and std.mem.eql(u8, entry.key_ptr.*, ".bss")) continue;3147 if (!import_memory and std.mem.eql(u8, entry.key_ptr.*, ".bss")) continue;
3139 const atom_index = entry.value_ptr.*;3148 const segment_index = entry.value_ptr.*;
3140 const segment = wasm.segments.items[atom_index];3149 const segment = wasm.segments.items[segment_index];
3141 if (segment.size == 0) continue; // do not emit empty segments3150 if (segment.size == 0) continue; // do not emit empty segments
3142 segment_count += 1;3151 segment_count += 1;
3143 var atom: *Atom = wasm.atoms.getPtr(atom_index).?.*.getFirst();3152 var atom_index = wasm.atoms.get(segment_index).?;
31443153
3145 // flag and index to memory section (currently, there can only be 1 memory section in wasm)3154 // flag and index to memory section (currently, there can only be 1 memory section in wasm)
3146 try leb.writeULEB128(binary_writer, @as(u32, 0));3155 try leb.writeULEB128(binary_writer, @as(u32, 0));
...@@ -3151,6 +3160,7 @@ fn writeToFile(...@@ -3151,6 +3160,7 @@ fn writeToFile(
3151 // fill in the offset table and the data segments3160 // fill in the offset table and the data segments
3152 var current_offset: u32 = 0;3161 var current_offset: u32 = 0;
3153 while (true) {3162 while (true) {
3163 const atom = wasm.getAtomPtr(atom_index);
3154 if (!is_obj) {3164 if (!is_obj) {
3155 atom.resolveRelocs(wasm);3165 atom.resolveRelocs(wasm);
3156 }3166 }
...@@ -3166,8 +3176,8 @@ fn writeToFile(...@@ -3166,8 +3176,8 @@ fn writeToFile(
3166 try binary_writer.writeAll(atom.code.items);3176 try binary_writer.writeAll(atom.code.items);
31673177
3168 current_offset += atom.size;3178 current_offset += atom.size;
3169 if (atom.next) |next| {3179 if (atom.prev) |prev| {
3170 atom = next;3180 atom_index = prev;
3171 } else {3181 } else {
3172 // also pad with zeroes when last atom to ensure3182 // also pad with zeroes when last atom to ensure
3173 // segments are aligned.3183 // segments are aligned.
...@@ -3209,15 +3219,15 @@ fn writeToFile(...@@ -3209,15 +3219,15 @@ fn writeToFile(
3209 }3219 }
32103220
3211 if (!wasm.base.options.strip) {3221 if (!wasm.base.options.strip) {
3212 if (wasm.dwarf) |*dwarf| {3222 // if (wasm.dwarf) |*dwarf| {
3213 const mod = wasm.base.options.module.?;3223 // const mod = wasm.base.options.module.?;
3214 try dwarf.writeDbgAbbrev();3224 // try dwarf.writeDbgAbbrev();
3215 // for debug info and ranges, the address is always 0,3225 // // for debug info and ranges, the address is always 0,
3216 // as locations are always offsets relative to 'code' section.3226 // // as locations are always offsets relative to 'code' section.
3217 try dwarf.writeDbgInfoHeader(mod, 0, code_section_size);3227 // try dwarf.writeDbgInfoHeader(mod, 0, code_section_size);
3218 try dwarf.writeDbgAranges(0, code_section_size);3228 // try dwarf.writeDbgAranges(0, code_section_size);
3219 try dwarf.writeDbgLineHeader();3229 // try dwarf.writeDbgLineHeader();
3220 }3230 // }
32213231
3222 var debug_bytes = std.ArrayList(u8).init(wasm.base.allocator);3232 var debug_bytes = std.ArrayList(u8).init(wasm.base.allocator);
3223 defer debug_bytes.deinit();3233 defer debug_bytes.deinit();
...@@ -3240,11 +3250,11 @@ fn writeToFile(...@@ -3240,11 +3250,11 @@ fn writeToFile(
32403250
3241 for (debug_sections) |item| {3251 for (debug_sections) |item| {
3242 if (item.index) |index| {3252 if (item.index) |index| {
3243 var atom = wasm.atoms.get(index).?.getFirst();3253 var atom = wasm.getAtomPtr(wasm.atoms.get(index).?);
3244 while (true) {3254 while (true) {
3245 atom.resolveRelocs(wasm);3255 atom.resolveRelocs(wasm);
3246 try debug_bytes.appendSlice(atom.code.items);3256 try debug_bytes.appendSlice(atom.code.items);
3247 atom = atom.next orelse break;3257 atom = if (atom.prev) |prev| wasm.getAtomPtr(prev) else break;
3248 }3258 }
3249 try emitDebugSection(&binary_bytes, debug_bytes.items, item.name);3259 try emitDebugSection(&binary_bytes, debug_bytes.items, item.name);
3250 debug_bytes.clearRetainingCapacity();3260 debug_bytes.clearRetainingCapacity();
...@@ -3976,7 +3986,8 @@ fn emitSymbolTable(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), symbol_table:...@@ -3976,7 +3986,8 @@ fn emitSymbolTable(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), symbol_table:
39763986
3977 if (symbol.isDefined()) {3987 if (symbol.isDefined()) {
3978 try leb.writeULEB128(writer, symbol.index);3988 try leb.writeULEB128(writer, symbol.index);
3979 const atom = wasm.symbol_atom.get(sym_loc).?;3989 const atom_index = wasm.symbol_atom.get(sym_loc).?;
3990 const atom = wasm.getAtom(atom_index);
3980 try leb.writeULEB128(writer, @as(u32, atom.offset));3991 try leb.writeULEB128(writer, @as(u32, atom.offset));
3981 try leb.writeULEB128(writer, @as(u32, atom.size));3992 try leb.writeULEB128(writer, @as(u32, atom.size));
3982 }3993 }
...@@ -4054,7 +4065,7 @@ fn emitCodeRelocations(...@@ -4054,7 +4065,7 @@ fn emitCodeRelocations(
4054 const reloc_start = binary_bytes.items.len;4065 const reloc_start = binary_bytes.items.len;
40554066
4056 var count: u32 = 0;4067 var count: u32 = 0;
4057 var atom: *Atom = wasm.atoms.get(code_index).?.getFirst();4068 var atom: *Atom = wasm.getAtomPtr(wasm.atoms.get(code_index).?);
4058 // for each atom, we calculate the uleb size and append that4069 // for each atom, we calculate the uleb size and append that
4059 var size_offset: u32 = 5; // account for code section size leb1284070 var size_offset: u32 = 5; // account for code section size leb128
4060 while (true) {4071 while (true) {
...@@ -4072,7 +4083,7 @@ fn emitCodeRelocations(...@@ -4072,7 +4083,7 @@ fn emitCodeRelocations(
4072 }4083 }
4073 log.debug("Emit relocation: {}", .{relocation});4084 log.debug("Emit relocation: {}", .{relocation});
4074 }4085 }
4075 atom = atom.next orelse break;4086 atom = if (atom.prev) |prev| wasm.getAtomPtr(prev) else break;
4076 }4087 }
4077 if (count == 0) return;4088 if (count == 0) return;
4078 var buf: [5]u8 = undefined;4089 var buf: [5]u8 = undefined;
...@@ -4103,7 +4114,7 @@ fn emitDataRelocations(...@@ -4103,7 +4114,7 @@ fn emitDataRelocations(
4103 // for each atom, we calculate the uleb size and append that4114 // for each atom, we calculate the uleb size and append that
4104 var size_offset: u32 = 5; // account for code section size leb1284115 var size_offset: u32 = 5; // account for code section size leb128
4105 for (wasm.data_segments.values()) |segment_index| {4116 for (wasm.data_segments.values()) |segment_index| {
4106 var atom: *Atom = wasm.atoms.get(segment_index).?.getFirst();4117 var atom: *Atom = wasm.getAtomPtr(wasm.atoms.get(segment_index).?);
4107 while (true) {4118 while (true) {
4108 size_offset += getULEB128Size(atom.size);4119 size_offset += getULEB128Size(atom.size);
4109 for (atom.relocs.items) |relocation| {4120 for (atom.relocs.items) |relocation| {
...@@ -4122,7 +4133,7 @@ fn emitDataRelocations(...@@ -4122,7 +4133,7 @@ fn emitDataRelocations(
4122 }4133 }
4123 log.debug("Emit relocation: {}", .{relocation});4134 log.debug("Emit relocation: {}", .{relocation});
4124 }4135 }
4125 atom = atom.next orelse break;4136 atom = if (atom.prev) |prev| wasm.getAtomPtr(prev) else break;
4126 }4137 }
4127 }4138 }
4128 if (count == 0) return;4139 if (count == 0) return;
src/link/Wasm/Atom.zig+24-22
...@@ -4,7 +4,6 @@ const std = @import("std");...@@ -4,7 +4,6 @@ const std = @import("std");
4const types = @import("types.zig");4const types = @import("types.zig");
5const Wasm = @import("../Wasm.zig");5const Wasm = @import("../Wasm.zig");
6const Symbol = @import("Symbol.zig");6const Symbol = @import("Symbol.zig");
7const Dwarf = @import("../Dwarf.zig");
87
9const leb = std.leb;8const leb = std.leb;
10const log = std.log.scoped(.link);9const log = std.log.scoped(.link);
...@@ -30,17 +29,17 @@ file: ?u16,...@@ -30,17 +29,17 @@ file: ?u16,
3029
31/// Next atom in relation to this atom.30/// Next atom in relation to this atom.
32/// When null, this atom is the last atom31/// When null, this atom is the last atom
33next: ?*Atom,32next: ?Atom.Index,
34/// Previous atom in relation to this atom.33/// Previous atom in relation to this atom.
35/// is null when this atom is the first in its order34/// is null when this atom is the first in its order
36prev: ?*Atom,35prev: ?Atom.Index,
3736
38/// Contains atoms local to a decl, all managed by this `Atom`.37/// Contains atoms local to a decl, all managed by this `Atom`.
39/// When the parent atom is being freed, it will also do so for all local atoms.38/// When the parent atom is being freed, it will also do so for all local atoms.
40locals: std.ArrayListUnmanaged(Atom) = .{},39locals: std.ArrayListUnmanaged(Atom.Index) = .{},
4140
42/// Represents the debug Atom that holds all debug information of this Atom.41/// Alias to an unsigned 32-bit integer
43dbg_info_atom: Dwarf.Atom,42pub const Index = u32;
4443
45/// Represents a default empty wasm `Atom`44/// Represents a default empty wasm `Atom`
46pub const empty: Atom = .{45pub const empty: Atom = .{
...@@ -51,18 +50,15 @@ pub const empty: Atom = .{...@@ -51,18 +50,15 @@ pub const empty: Atom = .{
51 .prev = null,50 .prev = null,
52 .size = 0,51 .size = 0,
53 .sym_index = 0,52 .sym_index = 0,
54 .dbg_info_atom = undefined,
55};53};
5654
57/// Frees all resources owned by this `Atom`.55/// 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;
59 atom.relocs.deinit(gpa);58 atom.relocs.deinit(gpa);
60 atom.code.deinit(gpa);59 atom.code.deinit(gpa);
61
62 for (atom.locals.items) |*local| {
63 local.deinit(gpa);
64 }
65 atom.locals.deinit(gpa);60 atom.locals.deinit(gpa);
61 atom.* = undefined;
66}62}
6763
68/// Sets the length of relocations and code to '0',64/// Sets the length of relocations and code to '0',
...@@ -83,18 +79,16 @@ pub fn format(atom: Atom, comptime fmt: []const u8, options: std.fmt.FormatOptio...@@ -83,18 +79,16 @@ pub fn format(atom: Atom, comptime fmt: []const u8, options: std.fmt.FormatOptio
83 });79 });
84}80}
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
93/// Returns the location of the symbol that represents this `Atom`82/// Returns the location of the symbol that represents this `Atom`
94pub fn symbolLoc(atom: Atom) Wasm.SymbolLoc {83pub fn symbolLoc(atom: Atom) Wasm.SymbolLoc {
95 return .{ .file = atom.file, .index = atom.sym_index };84 return .{ .file = atom.file, .index = atom.sym_index };
96}85}
9786
87pub fn getSymbolIndex(atom: Atom) ?u32 {
88 if (atom.sym_index == 0) return null;
89 return atom.sym_index;
90}
91
98/// Returns the virtual address of the `Atom`. This is the address starting92/// Returns the virtual address of the `Atom`. This is the address starting
99/// from the first entry within a section.93/// from the first entry within a section.
100pub fn getVA(atom: Atom, wasm: *const Wasm, symbol: *const Symbol) u32 {94pub fn getVA(atom: Atom, wasm: *const Wasm, symbol: *const Symbol) u32 {
...@@ -192,20 +186,28 @@ fn relocationValue(atom: Atom, relocation: types.Relocation, wasm_bin: *const Wa...@@ -192,20 +186,28 @@ fn relocationValue(atom: Atom, relocation: types.Relocation, wasm_bin: *const Wa
192 if (symbol.isUndefined()) {186 if (symbol.isUndefined()) {
193 return 0;187 return 0;
194 }188 }
195 const target_atom = wasm_bin.symbol_atom.get(target_loc).?;189 const target_atom_index = wasm_bin.symbol_atom.get(target_loc) orelse {
190 // this can only occur during incremental-compilation when a relocation
191 // still points to a freed decl. It is fine to emit the value 0 here
192 // as no actual code will point towards it.
193 return 0;
194 };
195 const target_atom = wasm_bin.getAtom(target_atom_index);
196 const va = @intCast(i32, target_atom.getVA(wasm_bin, symbol));196 const va = @intCast(i32, target_atom.getVA(wasm_bin, symbol));
197 return @intCast(u32, va + relocation.addend);197 return @intCast(u32, va + relocation.addend);
198 },198 },
199 .R_WASM_EVENT_INDEX_LEB => return symbol.index,199 .R_WASM_EVENT_INDEX_LEB => return symbol.index,
200 .R_WASM_SECTION_OFFSET_I32 => {200 .R_WASM_SECTION_OFFSET_I32 => {
201 const target_atom = wasm_bin.symbol_atom.get(target_loc).?;201 const target_atom_index = wasm_bin.symbol_atom.get(target_loc).?;
202 const target_atom = wasm_bin.getAtom(target_atom_index);
202 const rel_value = @intCast(i32, target_atom.offset) + relocation.addend;203 const rel_value = @intCast(i32, target_atom.offset) + relocation.addend;
203 return @intCast(u32, rel_value);204 return @intCast(u32, rel_value);
204 },205 },
205 .R_WASM_FUNCTION_OFFSET_I32 => {206 .R_WASM_FUNCTION_OFFSET_I32 => {
206 const target_atom = wasm_bin.symbol_atom.get(target_loc) orelse {207 const target_atom_index = wasm_bin.symbol_atom.get(target_loc) orelse {
207 return @bitCast(u32, @as(i32, -1));208 return @bitCast(u32, @as(i32, -1));
208 };209 };
210 const target_atom = wasm_bin.getAtom(target_atom_index);
209 const offset: u32 = 11 + Wasm.getULEB128Size(target_atom.size); // Header (11 bytes fixed-size) + body size (leb-encoded)211 const offset: u32 = 11 + Wasm.getULEB128Size(target_atom.size); // Header (11 bytes fixed-size) + body size (leb-encoded)
210 const rel_value = @intCast(i32, target_atom.offset + offset) + relocation.addend;212 const rel_value = @intCast(i32, target_atom.offset + offset) + relocation.addend;
211 return @intCast(u32, rel_value);213 return @intCast(u32, rel_value);
src/link/Wasm/Object.zig+5-10
...@@ -901,14 +901,9 @@ pub fn parseIntoAtoms(object: *Object, gpa: Allocator, object_index: u16, wasm_b...@@ -901,14 +901,9 @@ pub fn parseIntoAtoms(object: *Object, gpa: Allocator, object_index: u16, wasm_b
901 continue; // found unknown section, so skip parsing into atom as we do not know how to handle it.901 continue; // found unknown section, so skip parsing into atom as we do not know how to handle it.
902 };902 };
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);
905 atom.* = Atom.empty;906 atom.* = Atom.empty;
906 errdefer {
907 atom.deinit(gpa);
908 gpa.destroy(atom);
909 }
910
911 try wasm_bin.managed_atoms.append(gpa, atom);
912 atom.file = object_index;907 atom.file = object_index;
913 atom.size = relocatable_data.size;908 atom.size = relocatable_data.size;
914 atom.alignment = relocatable_data.getAlignment(object);909 atom.alignment = relocatable_data.getAlignment(object);
...@@ -938,12 +933,12 @@ pub fn parseIntoAtoms(object: *Object, gpa: Allocator, object_index: u16, wasm_b...@@ -938,12 +933,12 @@ pub fn parseIntoAtoms(object: *Object, gpa: Allocator, object_index: u16, wasm_b
938 .index = relocatable_data.getIndex(),933 .index = relocatable_data.getIndex(),
939 })) |symbols| {934 })) |symbols| {
940 atom.sym_index = symbols.pop();935 atom.sym_index = symbols.pop();
941 try wasm_bin.symbol_atom.putNoClobber(gpa, atom.symbolLoc(), atom);936 try wasm_bin.symbol_atom.putNoClobber(gpa, atom.symbolLoc(), atom_index);
942937
943 // symbols referencing the same atom will be added as alias938 // symbols referencing the same atom will be added as alias
944 // or as 'parent' when they are global.939 // or as 'parent' when they are global.
945 while (symbols.popOrNull()) |idx| {940 while (symbols.popOrNull()) |idx| {
946 try wasm_bin.symbol_atom.putNoClobber(gpa, .{ .file = atom.file, .index = idx }, atom);941 try wasm_bin.symbol_atom.putNoClobber(gpa, .{ .file = atom.file, .index = idx }, atom_index);
947 const alias_symbol = object.symtable[idx];942 const alias_symbol = object.symtable[idx];
948 if (alias_symbol.isGlobal()) {943 if (alias_symbol.isGlobal()) {
949 atom.sym_index = idx;944 atom.sym_index = idx;
...@@ -956,7 +951,7 @@ pub fn parseIntoAtoms(object: *Object, gpa: Allocator, object_index: u16, wasm_b...@@ -956,7 +951,7 @@ pub fn parseIntoAtoms(object: *Object, gpa: Allocator, object_index: u16, wasm_b
956 segment.alignment = std.math.max(segment.alignment, atom.alignment);951 segment.alignment = std.math.max(segment.alignment, atom.alignment);
957 }952 }
958953
959 try wasm_bin.appendAtomAtIndex(final_index, atom);954 try wasm_bin.appendAtomAtIndex(final_index, atom_index);
960 log.debug("Parsed into atom: '{s}' at segment index {d}", .{ object.string_table.get(object.symtable[atom.sym_index].name), final_index });955 log.debug("Parsed into atom: '{s}' at segment index {d}", .{ object.string_table.get(object.symtable[atom.sym_index].name), final_index });
961 }956 }
962}957}
src/main.zig+19-16
...@@ -893,7 +893,7 @@ fn buildOutputType(...@@ -893,7 +893,7 @@ fn buildOutputType(
893 i: usize = 0,893 i: usize = 0,
894 fn next(it: *@This()) ?[]const u8 {894 fn next(it: *@This()) ?[]const u8 {
895 if (it.i >= it.args.len) {895 if (it.i >= it.args.len) {
896 if (it.resp_file) |*resp| return if (resp.next()) |sentinel| std.mem.span(sentinel) else null;896 if (it.resp_file) |*resp| return resp.next();
897 return null;897 return null;
898 }898 }
899 defer it.i += 1;899 defer it.i += 1;
...@@ -901,7 +901,7 @@ fn buildOutputType(...@@ -901,7 +901,7 @@ fn buildOutputType(
901 }901 }
902 fn nextOrFatal(it: *@This()) []const u8 {902 fn nextOrFatal(it: *@This()) []const u8 {
903 if (it.i >= it.args.len) {903 if (it.i >= it.args.len) {
904 if (it.resp_file) |*resp| if (resp.next()) |sentinel| return std.mem.span(sentinel);904 if (it.resp_file) |*resp| if (resp.next()) |ret| return ret;
905 fatal("expected parameter after {s}", .{it.args[it.i - 1]});905 fatal("expected parameter after {s}", .{it.args[it.i - 1]});
906 }906 }
907 defer it.i += 1;907 defer it.i += 1;
...@@ -3915,6 +3915,7 @@ pub const usage_build =...@@ -3915,6 +3915,7 @@ pub const usage_build =
3915;3915;
39163916
3917pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {3917pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
3918 var color: Color = .auto;
3918 var prominent_compile_errors: bool = false;3919 var prominent_compile_errors: bool = false;
39193920
3920 // We want to release all the locks before executing the child process, so we make a nice3921 // We want to release all the locks before executing the child process, so we make a nice
...@@ -4117,6 +4118,7 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi...@@ -4117,6 +4118,7 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
4117 // Here we borrow main package's table and will replace it with a fresh4118 // Here we borrow main package's table and will replace it with a fresh
4118 // one after this process completes.4119 // one after this process completes.
4119 main_pkg.fetchAndAddDependencies(4120 main_pkg.fetchAndAddDependencies(
4121 arena,
4120 &thread_pool,4122 &thread_pool,
4121 &http_client,4123 &http_client,
4122 build_directory,4124 build_directory,
...@@ -4125,6 +4127,7 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi...@@ -4125,6 +4127,7 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
4125 &dependencies_source,4127 &dependencies_source,
4126 &build_roots_source,4128 &build_roots_source,
4127 "",4129 "",
4130 color,
4128 ) catch |err| switch (err) {4131 ) catch |err| switch (err) {
4129 error.PackageFetchFailed => process.exit(1),4132 error.PackageFetchFailed => process.exit(1),
4130 else => |e| return e,4133 else => |e| return e,
...@@ -4361,12 +4364,12 @@ pub fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void...@@ -4361,12 +4364,12 @@ pub fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void
4361 };4364 };
4362 defer gpa.free(source_code);4365 defer gpa.free(source_code);
43634366
4364 var tree = std.zig.parse(gpa, source_code) catch |err| {4367 var tree = Ast.parse(gpa, source_code, .zig) catch |err| {
4365 fatal("error parsing stdin: {}", .{err});4368 fatal("error parsing stdin: {}", .{err});
4366 };4369 };
4367 defer tree.deinit(gpa);4370 defer tree.deinit(gpa);
43684371
4369 try printErrsMsgToStdErr(gpa, arena, tree.errors, tree, "<stdin>", color);4372 try printErrsMsgToStdErr(gpa, arena, tree, "<stdin>", color);
4370 var has_ast_error = false;4373 var has_ast_error = false;
4371 if (check_ast_flag) {4374 if (check_ast_flag) {
4372 const Module = @import("Module.zig");4375 const Module = @import("Module.zig");
...@@ -4566,10 +4569,10 @@ fn fmtPathFile(...@@ -4566,10 +4569,10 @@ fn fmtPathFile(
4566 // Add to set after no longer possible to get error.IsDir.4569 // Add to set after no longer possible to get error.IsDir.
4567 if (try fmt.seen.fetchPut(stat.inode, {})) |_| return;4570 if (try fmt.seen.fetchPut(stat.inode, {})) |_| return;
45684571
4569 var tree = try std.zig.parse(fmt.gpa, source_code);4572 var tree = try Ast.parse(fmt.gpa, source_code, .zig);
4570 defer tree.deinit(fmt.gpa);4573 defer tree.deinit(fmt.gpa);
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);
4573 if (tree.errors.len != 0) {4576 if (tree.errors.len != 0) {
4574 fmt.any_error = true;4577 fmt.any_error = true;
4575 return;4578 return;
...@@ -4649,14 +4652,14 @@ fn fmtPathFile(...@@ -4649,14 +4652,14 @@ fn fmtPathFile(
4649 }4652 }
4650}4653}
46514654
4652fn printErrsMsgToStdErr(4655pub fn printErrsMsgToStdErr(
4653 gpa: mem.Allocator,4656 gpa: mem.Allocator,
4654 arena: mem.Allocator,4657 arena: mem.Allocator,
4655 parse_errors: []const Ast.Error,
4656 tree: Ast,4658 tree: Ast,
4657 path: []const u8,4659 path: []const u8,
4658 color: Color,4660 color: Color,
4659) !void {4661) !void {
4662 const parse_errors: []const Ast.Error = tree.errors;
4660 var i: usize = 0;4663 var i: usize = 0;
4661 while (i < parse_errors.len) : (i += 1) {4664 while (i < parse_errors.len) : (i += 1) {
4662 const parse_error = parse_errors[i];4665 const parse_error = parse_errors[i];
...@@ -4973,7 +4976,7 @@ pub const ClangArgIterator = struct {...@@ -4973,7 +4976,7 @@ pub const ClangArgIterator = struct {
4973 // rather than an argument to a parameter.4976 // rather than an argument to a parameter.
4974 // We adjust the len below when necessary.4977 // We adjust the len below when necessary.
4975 self.other_args = (self.argv.ptr + self.next_index)[0..1];4978 self.other_args = (self.argv.ptr + self.next_index)[0..1];
4976 var arg = mem.span(self.argv[self.next_index]);4979 var arg = self.argv[self.next_index];
4977 self.incrementArgIndex();4980 self.incrementArgIndex();
49784981
4979 if (mem.startsWith(u8, arg, "@")) {4982 if (mem.startsWith(u8, arg, "@")) {
...@@ -5017,7 +5020,7 @@ pub const ClangArgIterator = struct {...@@ -5017,7 +5020,7 @@ pub const ClangArgIterator = struct {
50175020
5018 self.has_next = true;5021 self.has_next = true;
5019 self.other_args = (self.argv.ptr + self.next_index)[0..1]; // We adjust len below when necessary.5022 self.other_args = (self.argv.ptr + self.next_index)[0..1]; // We adjust len below when necessary.
5020 arg = mem.span(self.argv[self.next_index]);5023 arg = self.argv[self.next_index];
5021 self.incrementArgIndex();5024 self.incrementArgIndex();
5022 }5025 }
50235026
...@@ -5312,11 +5315,11 @@ pub fn cmdAstCheck(...@@ -5312,11 +5315,11 @@ pub fn cmdAstCheck(
5312 file.pkg = try Package.create(gpa, "root", null, file.sub_file_path);5315 file.pkg = try Package.create(gpa, "root", null, file.sub_file_path);
5313 defer file.pkg.destroy(gpa);5316 defer file.pkg.destroy(gpa);
53145317
5315 file.tree = try std.zig.parse(gpa, file.source);5318 file.tree = try Ast.parse(gpa, file.source, .zig);
5316 file.tree_loaded = true;5319 file.tree_loaded = true;
5317 defer file.tree.deinit(gpa);5320 defer file.tree.deinit(gpa);
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);
5320 if (file.tree.errors.len != 0) {5323 if (file.tree.errors.len != 0) {
5321 process.exit(1);5324 process.exit(1);
5322 }5325 }
...@@ -5438,11 +5441,11 @@ pub fn cmdChangelist(...@@ -5438,11 +5441,11 @@ pub fn cmdChangelist(
5438 file.source = source;5441 file.source = source;
5439 file.source_loaded = true;5442 file.source_loaded = true;
54405443
5441 file.tree = try std.zig.parse(gpa, file.source);5444 file.tree = try Ast.parse(gpa, file.source, .zig);
5442 file.tree_loaded = true;5445 file.tree_loaded = true;
5443 defer file.tree.deinit(gpa);5446 defer file.tree.deinit(gpa);
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);
5446 if (file.tree.errors.len != 0) {5449 if (file.tree.errors.len != 0) {
5447 process.exit(1);5450 process.exit(1);
5448 }5451 }
...@@ -5476,10 +5479,10 @@ pub fn cmdChangelist(...@@ -5476,10 +5479,10 @@ pub fn cmdChangelist(
5476 if (new_amt != new_stat.size)5479 if (new_amt != new_stat.size)
5477 return error.UnexpectedEndOfFile;5480 return error.UnexpectedEndOfFile;
54785481
5479 var new_tree = try std.zig.parse(gpa, new_source);5482 var new_tree = try Ast.parse(gpa, new_source, .zig);
5480 defer new_tree.deinit(gpa);5483 defer new_tree.deinit(gpa);
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);
5483 if (new_tree.errors.len != 0) {5486 if (new_tree.errors.len != 0) {
5484 process.exit(1);5487 process.exit(1);
5485 }5488 }
src/mingw.zig+1
...@@ -106,6 +106,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {...@@ -106,6 +106,7 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
106 .msvcrt_os_lib => {106 .msvcrt_os_lib => {
107 const extra_flags = try arena.dupe([]const u8, &[_][]const u8{107 const extra_flags = try arena.dupe([]const u8, &[_][]const u8{
108 "-DHAVE_CONFIG_H",108 "-DHAVE_CONFIG_H",
109 "-D__LIBMSVCRT__",
109 "-D__LIBMSVCRT_OS__",110 "-D__LIBMSVCRT_OS__",
110111
111 "-I",112 "-I",
src/print_zir.zig+1
...@@ -332,6 +332,7 @@ const Writer = struct {...@@ -332,6 +332,7 @@ const Writer = struct {
332 .float_cast,332 .float_cast,
333 .int_cast,333 .int_cast,
334 .ptr_cast,334 .ptr_cast,
335 .qual_cast,
335 .truncate,336 .truncate,
336 .align_cast,337 .align_cast,
337 .div_exact,338 .div_exact,
src/translate_c.zig+4-1
...@@ -4519,7 +4519,10 @@ fn transCreateNodeAssign(...@@ -4519,7 +4519,10 @@ fn transCreateNodeAssign(
4519 defer block_scope.deinit();4519 defer block_scope.deinit();
45204520
4521 const tmp = try block_scope.makeMangledName(c, "tmp");4521 const tmp = try block_scope.makeMangledName(c, "tmp");
4522 const rhs_node = try transExpr(c, &block_scope.base, rhs, .used);4522 var rhs_node = try transExpr(c, &block_scope.base, rhs, .used);
4523 if (!exprIsBooleanType(lhs) and isBoolRes(rhs_node)) {
4524 rhs_node = try Tag.bool_to_int.create(c.arena, rhs_node);
4525 }
4523 const tmp_decl = try Tag.var_simple.create(c.arena, .{ .name = tmp, .init = rhs_node });4526 const tmp_decl = try Tag.var_simple.create(c.arena, .{ .name = tmp, .init = rhs_node });
4524 try block_scope.statements.append(tmp_decl);4527 try block_scope.statements.append(tmp_decl);
45254528
src/type.zig+45-586
...@@ -2937,24 +2937,24 @@ pub const Type = extern union {...@@ -2937,24 +2937,24 @@ pub const Type = extern union {
2937 .anyframe_T,2937 .anyframe_T,
2938 => return AbiAlignmentAdvanced{ .scalar = @divExact(target.cpu.arch.ptrBitWidth(), 8) },2938 => return AbiAlignmentAdvanced{ .scalar = @divExact(target.cpu.arch.ptrBitWidth(), 8) },
29392939
2940 .c_short => return AbiAlignmentAdvanced{ .scalar = CType.short.alignment(target) },2940 .c_short => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.short) },
2941 .c_ushort => return AbiAlignmentAdvanced{ .scalar = CType.ushort.alignment(target) },2941 .c_ushort => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.ushort) },
2942 .c_int => return AbiAlignmentAdvanced{ .scalar = CType.int.alignment(target) },2942 .c_int => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.int) },
2943 .c_uint => return AbiAlignmentAdvanced{ .scalar = CType.uint.alignment(target) },2943 .c_uint => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.uint) },
2944 .c_long => return AbiAlignmentAdvanced{ .scalar = CType.long.alignment(target) },2944 .c_long => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.long) },
2945 .c_ulong => return AbiAlignmentAdvanced{ .scalar = CType.ulong.alignment(target) },2945 .c_ulong => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.ulong) },
2946 .c_longlong => return AbiAlignmentAdvanced{ .scalar = CType.longlong.alignment(target) },2946 .c_longlong => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.longlong) },
2947 .c_ulonglong => return AbiAlignmentAdvanced{ .scalar = CType.ulonglong.alignment(target) },2947 .c_ulonglong => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.ulonglong) },
2948 .c_longdouble => return AbiAlignmentAdvanced{ .scalar = CType.longdouble.alignment(target) },2948 .c_longdouble => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.longdouble) },
29492949
2950 .f16 => return AbiAlignmentAdvanced{ .scalar = 2 },2950 .f16 => return AbiAlignmentAdvanced{ .scalar = 2 },
2951 .f32 => return AbiAlignmentAdvanced{ .scalar = CType.float.alignment(target) },2951 .f32 => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.float) },
2952 .f64 => switch (CType.double.sizeInBits(target)) {2952 .f64 => switch (target.c_type_bit_size(.double)) {
2953 64 => return AbiAlignmentAdvanced{ .scalar = CType.double.alignment(target) },2953 64 => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.double) },
2954 else => return AbiAlignmentAdvanced{ .scalar = 8 },2954 else => return AbiAlignmentAdvanced{ .scalar = 8 },
2955 },2955 },
2956 .f80 => switch (CType.longdouble.sizeInBits(target)) {2956 .f80 => switch (target.c_type_bit_size(.longdouble)) {
2957 80 => return AbiAlignmentAdvanced{ .scalar = CType.longdouble.alignment(target) },2957 80 => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.longdouble) },
2958 else => {2958 else => {
2959 var payload: Payload.Bits = .{2959 var payload: Payload.Bits = .{
2960 .base = .{ .tag = .int_unsigned },2960 .base = .{ .tag = .int_unsigned },
...@@ -2964,8 +2964,8 @@ pub const Type = extern union {...@@ -2964,8 +2964,8 @@ pub const Type = extern union {
2964 return AbiAlignmentAdvanced{ .scalar = abiAlignment(u80_ty, target) };2964 return AbiAlignmentAdvanced{ .scalar = abiAlignment(u80_ty, target) };
2965 },2965 },
2966 },2966 },
2967 .f128 => switch (CType.longdouble.sizeInBits(target)) {2967 .f128 => switch (target.c_type_bit_size(.longdouble)) {
2968 128 => return AbiAlignmentAdvanced{ .scalar = CType.longdouble.alignment(target) },2968 128 => return AbiAlignmentAdvanced{ .scalar = target.c_type_alignment(.longdouble) },
2969 else => return AbiAlignmentAdvanced{ .scalar = 16 },2969 else => return AbiAlignmentAdvanced{ .scalar = 16 },
2970 },2970 },
29712971
...@@ -3434,21 +3434,22 @@ pub const Type = extern union {...@@ -3434,21 +3434,22 @@ pub const Type = extern union {
3434 else => return AbiSizeAdvanced{ .scalar = @divExact(target.cpu.arch.ptrBitWidth(), 8) },3434 else => return AbiSizeAdvanced{ .scalar = @divExact(target.cpu.arch.ptrBitWidth(), 8) },
3435 },3435 },
34363436
3437 .c_short => return AbiSizeAdvanced{ .scalar = @divExact(CType.short.sizeInBits(target), 8) },3437 .c_short => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.short) },
3438 .c_ushort => return AbiSizeAdvanced{ .scalar = @divExact(CType.ushort.sizeInBits(target), 8) },3438 .c_ushort => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.ushort) },
3439 .c_int => return AbiSizeAdvanced{ .scalar = @divExact(CType.int.sizeInBits(target), 8) },3439 .c_int => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.int) },
3440 .c_uint => return AbiSizeAdvanced{ .scalar = @divExact(CType.uint.sizeInBits(target), 8) },3440 .c_uint => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.uint) },
3441 .c_long => return AbiSizeAdvanced{ .scalar = @divExact(CType.long.sizeInBits(target), 8) },3441 .c_long => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.long) },
3442 .c_ulong => return AbiSizeAdvanced{ .scalar = @divExact(CType.ulong.sizeInBits(target), 8) },3442 .c_ulong => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.ulong) },
3443 .c_longlong => return AbiSizeAdvanced{ .scalar = @divExact(CType.longlong.sizeInBits(target), 8) },3443 .c_longlong => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.longlong) },
3444 .c_ulonglong => return AbiSizeAdvanced{ .scalar = @divExact(CType.ulonglong.sizeInBits(target), 8) },3444 .c_ulonglong => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.ulonglong) },
3445 .c_longdouble => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.longdouble) },
34453446
3446 .f16 => return AbiSizeAdvanced{ .scalar = 2 },3447 .f16 => return AbiSizeAdvanced{ .scalar = 2 },
3447 .f32 => return AbiSizeAdvanced{ .scalar = 4 },3448 .f32 => return AbiSizeAdvanced{ .scalar = 4 },
3448 .f64 => return AbiSizeAdvanced{ .scalar = 8 },3449 .f64 => return AbiSizeAdvanced{ .scalar = 8 },
3449 .f128 => return AbiSizeAdvanced{ .scalar = 16 },3450 .f128 => return AbiSizeAdvanced{ .scalar = 16 },
3450 .f80 => switch (CType.longdouble.sizeInBits(target)) {3451 .f80 => switch (target.c_type_bit_size(.longdouble)) {
3451 80 => return AbiSizeAdvanced{ .scalar = std.mem.alignForward(10, CType.longdouble.alignment(target)) },3452 80 => return AbiSizeAdvanced{ .scalar = target.c_type_byte_size(.longdouble) },
3452 else => {3453 else => {
3453 var payload: Payload.Bits = .{3454 var payload: Payload.Bits = .{
3454 .base = .{ .tag = .int_unsigned },3455 .base = .{ .tag = .int_unsigned },
...@@ -3458,14 +3459,6 @@ pub const Type = extern union {...@@ -3458,14 +3459,6 @@ pub const Type = extern union {
3458 return AbiSizeAdvanced{ .scalar = abiSize(u80_ty, target) };3459 return AbiSizeAdvanced{ .scalar = abiSize(u80_ty, target) };
3459 },3460 },
3460 },3461 },
3461 .c_longdouble => switch (CType.longdouble.sizeInBits(target)) {
3462 16 => return AbiSizeAdvanced{ .scalar = abiSize(Type.f16, target) },
3463 32 => return AbiSizeAdvanced{ .scalar = abiSize(Type.f32, target) },
3464 64 => return AbiSizeAdvanced{ .scalar = abiSize(Type.f64, target) },
3465 80 => return AbiSizeAdvanced{ .scalar = abiSize(Type.f80, target) },
3466 128 => return AbiSizeAdvanced{ .scalar = abiSize(Type.f128, target) },
3467 else => unreachable,
3468 },
34693462
3470 // TODO revisit this when we have the concept of the error tag type3463 // TODO revisit this when we have the concept of the error tag type
3471 .anyerror_void_error_union,3464 .anyerror_void_error_union,
...@@ -3748,15 +3741,15 @@ pub const Type = extern union {...@@ -3748,15 +3741,15 @@ pub const Type = extern union {
3748 .manyptr_const_u8_sentinel_0,3741 .manyptr_const_u8_sentinel_0,
3749 => return target.cpu.arch.ptrBitWidth(),3742 => return target.cpu.arch.ptrBitWidth(),
37503743
3751 .c_short => return CType.short.sizeInBits(target),3744 .c_short => return target.c_type_bit_size(.short),
3752 .c_ushort => return CType.ushort.sizeInBits(target),3745 .c_ushort => return target.c_type_bit_size(.ushort),
3753 .c_int => return CType.int.sizeInBits(target),3746 .c_int => return target.c_type_bit_size(.int),
3754 .c_uint => return CType.uint.sizeInBits(target),3747 .c_uint => return target.c_type_bit_size(.uint),
3755 .c_long => return CType.long.sizeInBits(target),3748 .c_long => return target.c_type_bit_size(.long),
3756 .c_ulong => return CType.ulong.sizeInBits(target),3749 .c_ulong => return target.c_type_bit_size(.ulong),
3757 .c_longlong => return CType.longlong.sizeInBits(target),3750 .c_longlong => return target.c_type_bit_size(.longlong),
3758 .c_ulonglong => return CType.ulonglong.sizeInBits(target),3751 .c_ulonglong => return target.c_type_bit_size(.ulonglong),
3759 .c_longdouble => return CType.longdouble.sizeInBits(target),3752 .c_longdouble => return target.c_type_bit_size(.longdouble),
37603753
3761 .error_set,3754 .error_set,
3762 .error_set_single,3755 .error_set_single,
...@@ -4631,14 +4624,14 @@ pub const Type = extern union {...@@ -4631,14 +4624,14 @@ pub const Type = extern union {
4631 .i128 => return .{ .signedness = .signed, .bits = 128 },4624 .i128 => return .{ .signedness = .signed, .bits = 128 },
4632 .usize => return .{ .signedness = .unsigned, .bits = target.cpu.arch.ptrBitWidth() },4625 .usize => return .{ .signedness = .unsigned, .bits = target.cpu.arch.ptrBitWidth() },
4633 .isize => return .{ .signedness = .signed, .bits = target.cpu.arch.ptrBitWidth() },4626 .isize => return .{ .signedness = .signed, .bits = target.cpu.arch.ptrBitWidth() },
4634 .c_short => return .{ .signedness = .signed, .bits = CType.short.sizeInBits(target) },4627 .c_short => return .{ .signedness = .signed, .bits = target.c_type_bit_size(.short) },
4635 .c_ushort => return .{ .signedness = .unsigned, .bits = CType.ushort.sizeInBits(target) },4628 .c_ushort => return .{ .signedness = .unsigned, .bits = target.c_type_bit_size(.ushort) },
4636 .c_int => return .{ .signedness = .signed, .bits = CType.int.sizeInBits(target) },4629 .c_int => return .{ .signedness = .signed, .bits = target.c_type_bit_size(.int) },
4637 .c_uint => return .{ .signedness = .unsigned, .bits = CType.uint.sizeInBits(target) },4630 .c_uint => return .{ .signedness = .unsigned, .bits = target.c_type_bit_size(.uint) },
4638 .c_long => return .{ .signedness = .signed, .bits = CType.long.sizeInBits(target) },4631 .c_long => return .{ .signedness = .signed, .bits = target.c_type_bit_size(.long) },
4639 .c_ulong => return .{ .signedness = .unsigned, .bits = CType.ulong.sizeInBits(target) },4632 .c_ulong => return .{ .signedness = .unsigned, .bits = target.c_type_bit_size(.ulong) },
4640 .c_longlong => return .{ .signedness = .signed, .bits = CType.longlong.sizeInBits(target) },4633 .c_longlong => return .{ .signedness = .signed, .bits = target.c_type_bit_size(.longlong) },
4641 .c_ulonglong => return .{ .signedness = .unsigned, .bits = CType.ulonglong.sizeInBits(target) },4634 .c_ulonglong => return .{ .signedness = .unsigned, .bits = target.c_type_bit_size(.ulonglong) },
46424635
4643 .enum_full, .enum_nonexhaustive => ty = ty.cast(Payload.EnumFull).?.data.tag_ty,4636 .enum_full, .enum_nonexhaustive => ty = ty.cast(Payload.EnumFull).?.data.tag_ty,
4644 .enum_numbered => ty = ty.castTag(.enum_numbered).?.data.tag_ty,4637 .enum_numbered => ty = ty.castTag(.enum_numbered).?.data.tag_ty,
...@@ -4724,7 +4717,7 @@ pub const Type = extern union {...@@ -4724,7 +4717,7 @@ pub const Type = extern union {
4724 .f64 => 64,4717 .f64 => 64,
4725 .f80 => 80,4718 .f80 => 80,
4726 .f128, .comptime_float => 128,4719 .f128, .comptime_float => 128,
4727 .c_longdouble => CType.longdouble.sizeInBits(target),4720 .c_longdouble => target.c_type_bit_size(.longdouble),
47284721
4729 else => unreachable,4722 else => unreachable,
4730 };4723 };
...@@ -6689,537 +6682,3 @@ pub const Type = extern union {...@@ -6689,537 +6682,3 @@ pub const Type = extern union {
6689 /// to packed struct layout to find out all the places in the codebase you need to edit!6682 /// to packed struct layout to find out all the places in the codebase you need to edit!
6690 pub const packed_struct_layout_version = 2;6683 pub const packed_struct_layout_version = 2;
6691};6684};
6692
6693pub const CType = enum {
6694 short,
6695 ushort,
6696 int,
6697 uint,
6698 long,
6699 ulong,
6700 longlong,
6701 ulonglong,
6702 longdouble,
6703
6704 // We don't have a `c_float`/`c_double` type in Zig, but these
6705 // are useful for querying target-correct alignment and checking
6706 // whether C's double is f64 or f32
6707 float,
6708 double,
6709
6710 pub fn sizeInBits(self: CType, target: Target) u16 {
6711 switch (target.os.tag) {
6712 .freestanding, .other => switch (target.cpu.arch) {
6713 .msp430 => switch (self) {
6714 .short, .ushort, .int, .uint => return 16,
6715 .float, .long, .ulong => return 32,
6716 .longlong, .ulonglong, .double, .longdouble => return 64,
6717 },
6718 .avr => switch (self) {
6719 .short, .ushort, .int, .uint => return 16,
6720 .long, .ulong, .float, .double, .longdouble => return 32,
6721 .longlong, .ulonglong => return 64,
6722 },
6723 .tce, .tcele => switch (self) {
6724 .short, .ushort => return 16,
6725 .int, .uint, .long, .ulong, .longlong, .ulonglong => return 32,
6726 .float, .double, .longdouble => return 32,
6727 },
6728 .mips64, .mips64el => switch (self) {
6729 .short, .ushort => return 16,
6730 .int, .uint, .float => return 32,
6731 .long, .ulong => return if (target.abi != .gnuabin32) 64 else 32,
6732 .longlong, .ulonglong, .double => return 64,
6733 .longdouble => return 128,
6734 },
6735 .x86_64 => switch (self) {
6736 .short, .ushort => return 16,
6737 .int, .uint, .float => return 32,
6738 .long, .ulong => switch (target.abi) {
6739 .gnux32, .muslx32 => return 32,
6740 else => return 64,
6741 },
6742 .longlong, .ulonglong, .double => return 64,
6743 .longdouble => return 80,
6744 },
6745 else => switch (self) {
6746 .short, .ushort => return 16,
6747 .int, .uint, .float => return 32,
6748 .long, .ulong => return target.cpu.arch.ptrBitWidth(),
6749 .longlong, .ulonglong, .double => return 64,
6750 .longdouble => switch (target.cpu.arch) {
6751 .x86 => switch (target.abi) {
6752 .android => return 64,
6753 else => return 80,
6754 },
6755
6756 .powerpc,
6757 .powerpcle,
6758 .powerpc64,
6759 .powerpc64le,
6760 => switch (target.abi) {
6761 .musl,
6762 .musleabi,
6763 .musleabihf,
6764 .muslx32,
6765 => return 64,
6766 else => return 128,
6767 },
6768
6769 .riscv32,
6770 .riscv64,
6771 .aarch64,
6772 .aarch64_be,
6773 .aarch64_32,
6774 .s390x,
6775 .sparc,
6776 .sparc64,
6777 .sparcel,
6778 .wasm32,
6779 .wasm64,
6780 => return 128,
6781
6782 else => return 64,
6783 },
6784 },
6785 },
6786
6787 .linux,
6788 .freebsd,
6789 .netbsd,
6790 .dragonfly,
6791 .openbsd,
6792 .wasi,
6793 .emscripten,
6794 .plan9,
6795 .solaris,
6796 .haiku,
6797 .ananas,
6798 .fuchsia,
6799 .minix,
6800 => switch (target.cpu.arch) {
6801 .msp430 => switch (self) {
6802 .short, .ushort, .int, .uint => return 16,
6803 .long, .ulong, .float => return 32,
6804 .longlong, .ulonglong, .double, .longdouble => return 64,
6805 },
6806 .avr => switch (self) {
6807 .short, .ushort, .int, .uint => return 16,
6808 .long, .ulong, .float, .double, .longdouble => return 32,
6809 .longlong, .ulonglong => return 64,
6810 },
6811 .tce, .tcele => switch (self) {
6812 .short, .ushort => return 16,
6813 .int, .uint, .long, .ulong, .longlong, .ulonglong => return 32,
6814 .float, .double, .longdouble => return 32,
6815 },
6816 .mips64, .mips64el => switch (self) {
6817 .short, .ushort => return 16,
6818 .int, .uint, .float => return 32,
6819 .long, .ulong => return if (target.abi != .gnuabin32) 64 else 32,
6820 .longlong, .ulonglong, .double => return 64,
6821 .longdouble => if (target.os.tag == .freebsd) return 64 else return 128,
6822 },
6823 .x86_64 => switch (self) {
6824 .short, .ushort => return 16,
6825 .int, .uint, .float => return 32,
6826 .long, .ulong => switch (target.abi) {
6827 .gnux32, .muslx32 => return 32,
6828 else => return 64,
6829 },
6830 .longlong, .ulonglong, .double => return 64,
6831 .longdouble => return 80,
6832 },
6833 else => switch (self) {
6834 .short, .ushort => return 16,
6835 .int, .uint, .float => return 32,
6836 .long, .ulong => return target.cpu.arch.ptrBitWidth(),
6837 .longlong, .ulonglong, .double => return 64,
6838 .longdouble => switch (target.cpu.arch) {
6839 .x86 => switch (target.abi) {
6840 .android => return 64,
6841 else => return 80,
6842 },
6843
6844 .powerpc,
6845 .powerpcle,
6846 => switch (target.abi) {
6847 .musl,
6848 .musleabi,
6849 .musleabihf,
6850 .muslx32,
6851 => return 64,
6852 else => switch (target.os.tag) {
6853 .freebsd, .netbsd, .openbsd => return 64,
6854 else => return 128,
6855 },
6856 },
6857
6858 .powerpc64,
6859 .powerpc64le,
6860 => switch (target.abi) {
6861 .musl,
6862 .musleabi,
6863 .musleabihf,
6864 .muslx32,
6865 => return 64,
6866 else => switch (target.os.tag) {
6867 .freebsd, .openbsd => return 64,
6868 else => return 128,
6869 },
6870 },
6871
6872 .riscv32,
6873 .riscv64,
6874 .aarch64,
6875 .aarch64_be,
6876 .aarch64_32,
6877 .s390x,
6878 .mips64,
6879 .mips64el,
6880 .sparc,
6881 .sparc64,
6882 .sparcel,
6883 .wasm32,
6884 .wasm64,
6885 => return 128,
6886
6887 else => return 64,
6888 },
6889 },
6890 },
6891
6892 .windows, .uefi => switch (target.cpu.arch) {
6893 .x86 => switch (self) {
6894 .short, .ushort => return 16,
6895 .int, .uint, .float => return 32,
6896 .long, .ulong => return 32,
6897 .longlong, .ulonglong, .double => return 64,
6898 .longdouble => switch (target.abi) {
6899 .gnu, .gnuilp32, .cygnus => return 80,
6900 else => return 64,
6901 },
6902 },
6903 .x86_64 => switch (self) {
6904 .short, .ushort => return 16,
6905 .int, .uint, .float => return 32,
6906 .long, .ulong => switch (target.abi) {
6907 .cygnus => return 64,
6908 else => return 32,
6909 },
6910 .longlong, .ulonglong, .double => return 64,
6911 .longdouble => switch (target.abi) {
6912 .gnu, .gnuilp32, .cygnus => return 80,
6913 else => return 64,
6914 },
6915 },
6916 else => switch (self) {
6917 .short, .ushort => return 16,
6918 .int, .uint, .float => return 32,
6919 .long, .ulong => return 32,
6920 .longlong, .ulonglong, .double => return 64,
6921 .longdouble => return 64,
6922 },
6923 },
6924
6925 .macos, .ios, .tvos, .watchos => switch (self) {
6926 .short, .ushort => return 16,
6927 .int, .uint, .float => return 32,
6928 .long, .ulong => switch (target.cpu.arch) {
6929 .x86, .arm, .aarch64_32 => return 32,
6930 .x86_64 => switch (target.abi) {
6931 .gnux32, .muslx32 => return 32,
6932 else => return 64,
6933 },
6934 else => return 64,
6935 },
6936 .longlong, .ulonglong, .double => return 64,
6937 .longdouble => switch (target.cpu.arch) {
6938 .x86 => switch (target.abi) {
6939 .android => return 64,
6940 else => return 80,
6941 },
6942 .x86_64 => return 80,
6943 else => return 64,
6944 },
6945 },
6946
6947 .nvcl, .cuda => switch (self) {
6948 .short, .ushort => return 16,
6949 .int, .uint, .float => return 32,
6950 .long, .ulong => switch (target.cpu.arch) {
6951 .nvptx => return 32,
6952 .nvptx64 => return 64,
6953 else => return 64,
6954 },
6955 .longlong, .ulonglong, .double => return 64,
6956 .longdouble => return 64,
6957 },
6958
6959 .amdhsa, .amdpal => switch (self) {
6960 .short, .ushort => return 16,
6961 .int, .uint, .float => return 32,
6962 .long, .ulong, .longlong, .ulonglong, .double => return 64,
6963 .longdouble => return 128,
6964 },
6965
6966 .cloudabi,
6967 .kfreebsd,
6968 .lv2,
6969 .zos,
6970 .rtems,
6971 .nacl,
6972 .aix,
6973 .ps4,
6974 .ps5,
6975 .elfiamcu,
6976 .mesa3d,
6977 .contiki,
6978 .hermit,
6979 .hurd,
6980 .opencl,
6981 .glsl450,
6982 .vulkan,
6983 .driverkit,
6984 .shadermodel,
6985 => @panic("TODO specify the C integer and float type sizes for this OS"),
6986 }
6987 }
6988
6989 pub fn alignment(self: CType, target: Target) u16 {
6990
6991 // Overrides for unusual alignments
6992 switch (target.cpu.arch) {
6993 .avr => switch (self) {
6994 .short, .ushort => return 2,
6995 else => return 1,
6996 },
6997 .x86 => switch (target.os.tag) {
6998 .windows, .uefi => switch (self) {
6999 .longlong, .ulonglong, .double => return 8,
7000 .longdouble => switch (target.abi) {
7001 .gnu, .gnuilp32, .cygnus => return 4,
7002 else => return 8,
7003 },
7004 else => {},
7005 },
7006 else => {},
7007 },
7008 else => {},
7009 }
7010
7011 // Next-power-of-two-aligned, up to a maximum.
7012 return @min(
7013 std.math.ceilPowerOfTwoAssert(u16, (self.sizeInBits(target) + 7) / 8),
7014 switch (target.cpu.arch) {
7015 .arm, .armeb, .thumb, .thumbeb => switch (target.os.tag) {
7016 .netbsd => switch (target.abi) {
7017 .gnueabi,
7018 .gnueabihf,
7019 .eabi,
7020 .eabihf,
7021 .android,
7022 .musleabi,
7023 .musleabihf,
7024 => 8,
7025
7026 else => @as(u16, 4),
7027 },
7028 .ios, .tvos, .watchos => 4,
7029 else => 8,
7030 },
7031
7032 .msp430,
7033 .avr,
7034 => 2,
7035
7036 .arc,
7037 .csky,
7038 .x86,
7039 .xcore,
7040 .dxil,
7041 .loongarch32,
7042 .tce,
7043 .tcele,
7044 .le32,
7045 .amdil,
7046 .hsail,
7047 .spir,
7048 .spirv32,
7049 .kalimba,
7050 .shave,
7051 .renderscript32,
7052 .ve,
7053 .spu_2,
7054 .xtensa,
7055 => 4,
7056
7057 .aarch64_32,
7058 .amdgcn,
7059 .amdil64,
7060 .bpfel,
7061 .bpfeb,
7062 .hexagon,
7063 .hsail64,
7064 .loongarch64,
7065 .m68k,
7066 .mips,
7067 .mipsel,
7068 .sparc,
7069 .sparcel,
7070 .sparc64,
7071 .lanai,
7072 .le64,
7073 .nvptx,
7074 .nvptx64,
7075 .r600,
7076 .s390x,
7077 .spir64,
7078 .spirv64,
7079 .renderscript64,
7080 => 8,
7081
7082 .aarch64,
7083 .aarch64_be,
7084 .mips64,
7085 .mips64el,
7086 .powerpc,
7087 .powerpcle,
7088 .powerpc64,
7089 .powerpc64le,
7090 .riscv32,
7091 .riscv64,
7092 .x86_64,
7093 .wasm32,
7094 .wasm64,
7095 => 16,
7096 },
7097 );
7098 }
7099
7100 pub fn preferredAlignment(self: CType, target: Target) u16 {
7101
7102 // Overrides for unusual alignments
7103 switch (target.cpu.arch) {
7104 .arm, .armeb, .thumb, .thumbeb => switch (target.os.tag) {
7105 .netbsd => switch (target.abi) {
7106 .gnueabi,
7107 .gnueabihf,
7108 .eabi,
7109 .eabihf,
7110 .android,
7111 .musleabi,
7112 .musleabihf,
7113 => {},
7114
7115 else => switch (self) {
7116 .longdouble => return 4,
7117 else => {},
7118 },
7119 },
7120 .ios, .tvos, .watchos => switch (self) {
7121 .longdouble => return 4,
7122 else => {},
7123 },
7124 else => {},
7125 },
7126 .arc => switch (self) {
7127 .longdouble => return 4,
7128 else => {},
7129 },
7130 .avr => switch (self) {
7131 .int, .uint, .long, .ulong, .float, .longdouble => return 1,
7132 .short, .ushort => return 2,
7133 .double => return 4,
7134 .longlong, .ulonglong => return 8,
7135 },
7136 .x86 => switch (target.os.tag) {
7137 .windows, .uefi => switch (self) {
7138 .longdouble => switch (target.abi) {
7139 .gnu, .gnuilp32, .cygnus => return 4,
7140 else => return 8,
7141 },
7142 else => {},
7143 },
7144 else => switch (self) {
7145 .longdouble => return 4,
7146 else => {},
7147 },
7148 },
7149 else => {},
7150 }
7151
7152 // Next-power-of-two-aligned, up to a maximum.
7153 return @min(
7154 std.math.ceilPowerOfTwoAssert(u16, (self.sizeInBits(target) + 7) / 8),
7155 switch (target.cpu.arch) {
7156 .msp430 => @as(u16, 2),
7157
7158 .csky,
7159 .xcore,
7160 .dxil,
7161 .loongarch32,
7162 .tce,
7163 .tcele,
7164 .le32,
7165 .amdil,
7166 .hsail,
7167 .spir,
7168 .spirv32,
7169 .kalimba,
7170 .shave,
7171 .renderscript32,
7172 .ve,
7173 .spu_2,
7174 => 4,
7175
7176 .arc,
7177 .arm,
7178 .armeb,
7179 .avr,
7180 .thumb,
7181 .thumbeb,
7182 .aarch64_32,
7183 .amdgcn,
7184 .amdil64,
7185 .bpfel,
7186 .bpfeb,
7187 .hexagon,
7188 .hsail64,
7189 .x86,
7190 .loongarch64,
7191 .m68k,
7192 .mips,
7193 .mipsel,
7194 .sparc,
7195 .sparcel,
7196 .sparc64,
7197 .lanai,
7198 .le64,
7199 .nvptx,
7200 .nvptx64,
7201 .r600,
7202 .s390x,
7203 .spir64,
7204 .spirv64,
7205 .renderscript64,
7206 => 8,
7207
7208 .aarch64,
7209 .aarch64_be,
7210 .mips64,
7211 .mips64el,
7212 .powerpc,
7213 .powerpcle,
7214 .powerpc64,
7215 .powerpc64le,
7216 .riscv32,
7217 .riscv64,
7218 .x86_64,
7219 .wasm32,
7220 .wasm64,
7221 => 16,
7222 },
7223 );
7224 }
7225};
stage1/zig1.wasm
Binary files a/stage1/zig1.wasm and b/stage1/zig1.wasm differ
test/behavior/basic.zig+19-1
...@@ -703,7 +703,7 @@ test "string concatenation" {...@@ -703,7 +703,7 @@ test "string concatenation" {
703 comptime try expect(@TypeOf(a) == *const [12:0]u8);703 comptime try expect(@TypeOf(a) == *const [12:0]u8);
704 comptime try expect(@TypeOf(b) == *const [12:0]u8);704 comptime try expect(@TypeOf(b) == *const [12:0]u8);
705705
706 const len = mem.len(b);706 const len = b.len;
707 const len_with_null = len + 1;707 const len_with_null = len + 1;
708 {708 {
709 var i: u32 = 0;709 var i: u32 = 0;
...@@ -1125,3 +1125,21 @@ test "returning an opaque type from a function" {...@@ -1125,3 +1125,21 @@ test "returning an opaque type from a function" {
1125 };1125 };
1126 try expect(S.foo(123).b == 123);1126 try expect(S.foo(123).b == 123);
1127}1127}
1128
1129test "orelse coercion as function argument" {
1130 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1131 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1132
1133 const Loc = struct { start: i32 = -1 };
1134 const Container = struct {
1135 a: ?Loc = null,
1136 fn init(a: Loc) @This() {
1137 return .{
1138 .a = a,
1139 };
1140 }
1141 };
1142 var optional: ?Loc = .{};
1143 var foo = Container.init(optional orelse .{});
1144 try expect(foo.a.?.start == -1);
1145}
test/behavior/cast.zig-3
...@@ -1179,7 +1179,6 @@ fn peerTypeEmptyArrayAndSlice(a: bool, slice: []const u8) []const u8 {...@@ -1179,7 +1179,6 @@ fn peerTypeEmptyArrayAndSlice(a: bool, slice: []const u8) []const u8 {
1179test "implicitly cast from [N]T to ?[]const T" {1179test "implicitly cast from [N]T to ?[]const T" {
1180 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;1180 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
1181 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO1181 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1182 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1183 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO1182 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
11841183
1185 try expect(mem.eql(u8, castToOptionalSlice().?, "hi"));1184 try expect(mem.eql(u8, castToOptionalSlice().?, "hi"));
...@@ -1264,7 +1263,6 @@ test "cast from array reference to fn: runtime fn ptr" {...@@ -1264,7 +1263,6 @@ test "cast from array reference to fn: runtime fn ptr" {
1264test "*const [N]null u8 to ?[]const u8" {1263test "*const [N]null u8 to ?[]const u8" {
1265 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;1264 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
1266 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO1265 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1267 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1268 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO1266 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
12691267
1270 const S = struct {1268 const S = struct {
...@@ -1413,7 +1411,6 @@ test "cast i8 fn call peers to i32 result" {...@@ -1413,7 +1411,6 @@ test "cast i8 fn call peers to i32 result" {
1413test "cast compatible optional types" {1411test "cast compatible optional types" {
1414 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO1412 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1415 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO1413 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1416 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1417 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO1414 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
14181415
1419 var a: ?[:0]const u8 = null;1416 var a: ?[:0]const u8 = null;
test/behavior/error.zig+15
...@@ -896,3 +896,18 @@ test "optional error union return type" {...@@ -896,3 +896,18 @@ test "optional error union return type" {
896 };896 };
897 try expect(1234 == try S.foo().?);897 try expect(1234 == try S.foo().?);
898}898}
899
900test "optional error set return type" {
901 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
902 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
903
904 const E = error{ A, B };
905 const S = struct {
906 fn foo(return_null: bool) ?E {
907 return if (return_null) null else E.A;
908 }
909 };
910
911 try expect(null == S.foo(true));
912 try expect(E.A == S.foo(false).?);
913}
test/behavior/math.zig-3
...@@ -1332,7 +1332,6 @@ test "float remainder division using @rem" {...@@ -1332,7 +1332,6 @@ test "float remainder division using @rem" {
1332 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO1332 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1333 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO1333 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1334 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO1334 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1335 if (builtin.zig_backend == .stage2_llvm and builtin.os.tag == .windows) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/12602
13361335
1337 comptime try frem(f16);1336 comptime try frem(f16);
1338 comptime try frem(f32);1337 comptime try frem(f32);
...@@ -1375,7 +1374,6 @@ test "float modulo division using @mod" {...@@ -1375,7 +1374,6 @@ test "float modulo division using @mod" {
1375 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO1374 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1376 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO1375 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1377 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO1376 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1378 if (builtin.zig_backend == .stage2_llvm and builtin.os.tag == .windows) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/12602
13791377
1380 comptime try fmod(f16);1378 comptime try fmod(f16);
1381 comptime try fmod(f32);1379 comptime try fmod(f32);
...@@ -1438,7 +1436,6 @@ test "@round f80" {...@@ -1438,7 +1436,6 @@ test "@round f80" {
1438 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO1436 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1439 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO1437 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1440 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO1438 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1441 if (builtin.zig_backend == .stage2_llvm and builtin.os.tag == .windows) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/12602
14421439
1443 try testRound(f80, 12.0);1440 try testRound(f80, 12.0);
1444 comptime try testRound(f80, 12.0);1441 comptime try testRound(f80, 12.0);
test/behavior/muladd.zig-2
...@@ -50,7 +50,6 @@ test "@mulAdd f80" {...@@ -50,7 +50,6 @@ test "@mulAdd f80" {
50 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO50 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
51 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO51 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
52 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO52 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
53 if (builtin.zig_backend == .stage2_llvm and builtin.os.tag == .windows) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/12602
5453
55 comptime try testMulAdd80();54 comptime try testMulAdd80();
56 try testMulAdd80();55 try testMulAdd80();
...@@ -178,7 +177,6 @@ test "vector f80" {...@@ -178,7 +177,6 @@ test "vector f80" {
178 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO177 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
179 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO178 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
180 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO179 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
181 if (builtin.zig_backend == .stage2_llvm and builtin.os.tag == .windows) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/12602
182180
183 comptime try vector80();181 comptime try vector80();
184 try vector80();182 try vector80();
test/behavior/optional.zig-2
...@@ -439,7 +439,6 @@ test "Optional slice size is optimized" {...@@ -439,7 +439,6 @@ test "Optional slice size is optimized" {
439 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;439 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
440 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;440 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
441 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;441 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
442 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
443 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO442 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
444443
445 try expect(@sizeOf(?[]u8) == @sizeOf([]u8));444 try expect(@sizeOf(?[]u8) == @sizeOf([]u8));
...@@ -479,7 +478,6 @@ test "cast slice to const slice nested in error union and optional" {...@@ -479,7 +478,6 @@ test "cast slice to const slice nested in error union and optional" {
479 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;478 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
480 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;479 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
481 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;480 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
482 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
483481
484 const S = struct {482 const S = struct {
485 fn inner() !?[]u8 {483 fn inner() !?[]u8 {
test/behavior/sizeof_and_typeof.zig+9
...@@ -292,3 +292,12 @@ test "@sizeOf optional of previously unresolved union" {...@@ -292,3 +292,12 @@ test "@sizeOf optional of previously unresolved union" {
292 const Node = union { a: usize };292 const Node = union { a: usize };
293 try expect(@sizeOf(?Node) == @sizeOf(Node) + @alignOf(Node));293 try expect(@sizeOf(?Node) == @sizeOf(Node) + @alignOf(Node));
294}294}
295
296test "@offsetOf zero-bit field" {
297 const S = packed struct {
298 a: u32,
299 b: u0,
300 c: u32,
301 };
302 try expect(@offsetOf(S, "b") == @offsetOf(S, "c"));
303}
test/behavior/threadlocal.zig+12-6
...@@ -7,8 +7,10 @@ test "thread local variable" {...@@ -7,8 +7,10 @@ test "thread local variable" {
7 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO7 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
8 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO8 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
9 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO9 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
10 if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch != .x86_64) return error.SkipZigTest; // TODO10 if (builtin.zig_backend == .stage2_llvm) switch (builtin.cpu.arch) {
11 if (builtin.zig_backend == .stage2_c and builtin.os.tag == .windows) return error.SkipZigTest; // TODO11 .x86_64, .x86 => {},
12 else => return error.SkipZigTest,
13 }; // TODO
12 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO14 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1315
14 const S = struct {16 const S = struct {
...@@ -23,8 +25,10 @@ test "pointer to thread local array" {...@@ -23,8 +25,10 @@ test "pointer to thread local array" {
23 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO25 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
24 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO26 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
25 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO27 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
26 if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch != .x86_64) return error.SkipZigTest; // TODO28 if (builtin.zig_backend == .stage2_llvm) switch (builtin.cpu.arch) {
27 if (builtin.zig_backend == .stage2_c and builtin.os.tag == .windows) return error.SkipZigTest; // TODO29 .x86_64, .x86 => {},
30 else => return error.SkipZigTest,
31 }; // TODO
28 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO32 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
2933
30 const s = "Hello world";34 const s = "Hello world";
...@@ -39,8 +43,10 @@ test "reference a global threadlocal variable" {...@@ -39,8 +43,10 @@ test "reference a global threadlocal variable" {
39 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO43 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
40 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO44 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
41 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO45 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
42 if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch != .x86_64) return error.SkipZigTest; // TODO46 if (builtin.zig_backend == .stage2_llvm) switch (builtin.cpu.arch) {
43 if (builtin.zig_backend == .stage2_c and builtin.os.tag == .windows) return error.SkipZigTest; // TODO47 .x86_64, .x86 => {},
48 else => return error.SkipZigTest,
49 }; // TODO
44 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO50 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
4551
46 _ = nrfx_uart_rx(&g_uart0);52 _ = nrfx_uart_rx(&g_uart0);
test/cases/compile_errors/assigning_to_struct_or_union_fields_that_are_not_optionals_with_a_function_that_returns_an_optional.zig+1-1
...@@ -20,4 +20,4 @@ export fn entry() void {...@@ -20,4 +20,4 @@ export fn entry() void {
20//20//
21// :11:27: error: expected type 'u8', found '?u8'21// :11:27: error: expected type 'u8', found '?u8'
22// :11:27: note: cannot convert optional to payload type22// :11:27: note: cannot convert optional to payload type
23// :11:27: note: consider using `.?`, `orelse`, or `if`23// :11:27: note: consider using '.?', 'orelse', or 'if'
test/cases/compile_errors/comptime_arg_to_generic_fn_callee_error.zig created+21
...@@ -0,0 +1,21 @@
1const std = @import("std");
2const MyStruct = struct {
3 a: i32,
4 b: i32,
5
6 pub fn getA(self: *List) i32 {
7 return self.items(.c);
8 }
9};
10const List = std.MultiArrayList(MyStruct);
11pub export fn entry() void {
12 var list = List{};
13 _ = MyStruct.getA(&list);
14}
15
16// error
17// backend=stage2
18// target=native
19//
20// :7:28: error: no field named 'c' in enum 'meta.FieldEnum(tmp.MyStruct)'
21// :?:?: note: enum declared here
test/cases/compile_errors/comptime_call_of_function_pointer.zig created+10
...@@ -0,0 +1,10 @@
1export fn entry() void {
2 const fn_ptr = @intToPtr(*align(1) fn () void, 0xffd2);
3 comptime fn_ptr();
4}
5
6// error
7// backend=stage2
8// target=native
9//
10// :3:20: error: comptime call of function pointer
test/cases/compile_errors/discarding_error_value.zig+1-1
...@@ -10,4 +10,4 @@ fn foo() !void {...@@ -10,4 +10,4 @@ fn foo() !void {
10// target=native10// target=native
11//11//
12// :2:12: error: error is discarded12// :2:12: error: error is discarded
13// :2:12: note: consider using `try`, `catch`, or `if`13// :2:12: note: consider using 'try', 'catch', or 'if'
test/cases/compile_errors/helpful_return_type_error_message.zig+2-2
...@@ -26,7 +26,7 @@ export fn quux() u32 {...@@ -26,7 +26,7 @@ export fn quux() u32 {
26// :11:15: error: expected type 'u32', found '@typeInfo(@typeInfo(@TypeOf(tmp.bar)).Fn.return_type.?).ErrorUnion.error_set!u32'26// :11:15: error: expected type 'u32', found '@typeInfo(@typeInfo(@TypeOf(tmp.bar)).Fn.return_type.?).ErrorUnion.error_set!u32'
27// :10:17: note: function cannot return an error27// :10:17: note: function cannot return an error
28// :11:15: note: cannot convert error union to payload type28// :11:15: note: cannot convert error union to payload type
29// :11:15: note: consider using `try`, `catch`, or `if`29// :11:15: note: consider using 'try', 'catch', or 'if'
30// :15:14: error: expected type 'u32', found '@typeInfo(@typeInfo(@TypeOf(tmp.bar)).Fn.return_type.?).ErrorUnion.error_set!u32'30// :15:14: error: expected type 'u32', found '@typeInfo(@typeInfo(@TypeOf(tmp.bar)).Fn.return_type.?).ErrorUnion.error_set!u32'
31// :15:14: note: cannot convert error union to payload type31// :15:14: note: cannot convert error union to payload type
32// :15:14: note: consider using `try`, `catch`, or `if`32// :15:14: note: consider using 'try', 'catch', or 'if'
test/cases/compile_errors/ignored_deferred_function_call.zig+1-1
...@@ -8,4 +8,4 @@ fn bar() anyerror!i32 { return 0; }...@@ -8,4 +8,4 @@ fn bar() anyerror!i32 { return 0; }
8// target=native8// target=native
9//9//
10// :2:14: error: error is ignored10// :2:14: error: error is ignored
11// :2:14: note: consider using `try`, `catch`, or `if`11// :2:14: note: consider using 'try', 'catch', or 'if'
test/cases/compile_errors/ignored_expression_in_while_continuation.zig+3-3
...@@ -18,8 +18,8 @@ fn bad() anyerror!void {...@@ -18,8 +18,8 @@ fn bad() anyerror!void {
18// target=native18// target=native
19//19//
20// :2:24: error: error is ignored20// :2:24: error: error is ignored
21// :2:24: note: consider using `try`, `catch`, or `if`21// :2:24: note: consider using 'try', 'catch', or 'if'
22// :6:25: error: error is ignored22// :6:25: error: error is ignored
23// :6:25: note: consider using `try`, `catch`, or `if`23// :6:25: note: consider using 'try', 'catch', or 'if'
24// :10:25: error: error is ignored24// :10:25: error: error is ignored
25// :10:25: note: consider using `try`, `catch`, or `if`25// :10:25: note: consider using 'try', 'catch', or 'if'
test/cases/compile_errors/increase_pointer_alignment_in_ptrCast.zig+1
...@@ -11,3 +11,4 @@ export fn entry() u32 {...@@ -11,3 +11,4 @@ export fn entry() u32 {
11// :3:17: error: cast increases pointer alignment11// :3:17: error: cast increases pointer alignment
12// :3:32: note: '*u8' has alignment '1'12// :3:32: note: '*u8' has alignment '1'
13// :3:26: note: '*u32' has alignment '4'13// :3:26: note: '*u32' has alignment '4'
14// :3:17: note: consider using '@alignCast'
test/cases/compile_errors/inline_call_runtime_value_to_comptime_param.zig created+17
...@@ -0,0 +1,17 @@
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 @@...@@ -1,6 +1,6 @@
1const builtin = @import("std").builtin;1const builtin = @import("std").builtin;
2export fn entry() void {2export fn entry() void {
3 const foo = builtin.Mode.x86;3 const foo = builtin.OptimizeMode.x86;
4 _ = foo;4 _ = foo;
5}5}
66
...@@ -8,5 +8,5 @@ export fn entry() void {...@@ -8,5 +8,5 @@ export fn entry() void {
8// backend=stage28// backend=stage2
9// target=native9// target=native
10//10//
11// :3:30: error: enum 'builtin.Mode' has no member named 'x86'11// :3:38: error: enum 'builtin.OptimizeMode' has no member named 'x86'
12// :?:18: note: enum declared here12// :?: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 {...@@ -10,5 +10,5 @@ export fn foo() void {
10//10//
11// :4:9: error: expected type '*anyopaque', found '?*anyopaque'11// :4:9: error: expected type '*anyopaque', found '?*anyopaque'
12// :4:9: note: cannot convert optional to payload type12// :4:9: note: cannot convert optional to payload type
13// :4:9: note: consider using `.?`, `orelse`, or `if`13// :4:9: note: consider using '.?', 'orelse', or 'if'
14// :4:9: note: '?*anyopaque' could have null values which are illegal in type '*anyopaque'14// :4:9: note: '?*anyopaque' could have null values which are illegal in type '*anyopaque'
test/cases/compile_errors/ptrCast_discards_const_qualifier.zig+1
...@@ -9,3 +9,4 @@ export fn entry() void {...@@ -9,3 +9,4 @@ export fn entry() void {
9// target=native9// target=native
10//10//
11// :3:15: error: cast discards const qualifier11// :3:15: error: cast discards const qualifier
12// :3:15: note: consider using '@qualCast'
test/cases/compile_errors/regression_test_2980_base_type_u32_is_not_type_checked_properly_when_assigning_a_value_within_a_struct.zig+1-1
...@@ -20,4 +20,4 @@ export fn entry() void {...@@ -20,4 +20,4 @@ export fn entry() void {
20//20//
21// :12:25: error: expected type 'u32', found '@typeInfo(@typeInfo(@TypeOf(tmp.get_uval)).Fn.return_type.?).ErrorUnion.error_set!u32'21// :12:25: error: expected type 'u32', found '@typeInfo(@typeInfo(@TypeOf(tmp.get_uval)).Fn.return_type.?).ErrorUnion.error_set!u32'
22// :12:25: note: cannot convert error union to payload type22// :12:25: note: cannot convert error union to payload type
23// :12:25: note: consider using `try`, `catch`, or `if`23// :12:25: note: consider using 'try', 'catch', or 'if'
test/cases/compile_errors/result_location_incompatibility_mismatching_handle_is_ptr.zig+1-1
...@@ -17,4 +17,4 @@ pub const Container = struct {...@@ -17,4 +17,4 @@ pub const Container = struct {
17//17//
18// :3:36: error: expected type 'i32', found '?i32'18// :3:36: error: expected type 'i32', found '?i32'
19// :3:36: note: cannot convert optional to payload type19// :3:36: note: cannot convert optional to payload type
20// :3:36: note: consider using `.?`, `orelse`, or `if`20// :3:36: note: consider using '.?', 'orelse', or 'if'
test/cases/compile_errors/result_location_incompatibility_mismatching_handle_is_ptr_generic_call.zig+1-1
...@@ -17,4 +17,4 @@ pub const Container = struct {...@@ -17,4 +17,4 @@ pub const Container = struct {
17//17//
18// :3:36: error: expected type 'i32', found '?i32'18// :3:36: error: expected type 'i32', found '?i32'
19// :3:36: note: cannot convert optional to payload type19// :3:36: note: cannot convert optional to payload type
20// :3:36: note: consider using `.?`, `orelse`, or `if`20// :3:36: note: consider using '.?', 'orelse', or 'if'
test/link/bss/build.zig+8-5
...@@ -1,12 +1,15 @@...@@ -1,12 +1,15 @@
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 mode = b.standardReleaseOptions();4 const optimize = b.standardOptimizeOption(.{});
5 const test_step = b.step("test", "Test");5 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 });
8 b.default_step.dependOn(&exe.step);12 b.default_step.dependOn(&exe.step);
9 exe.setBuildMode(mode);
1013
11 const run = exe.run();14 const run = exe.run();
12 run.expectStdOutEqual("0, 1, 0\n");15 run.expectStdOutEqual("0, 1, 0\n");
test/link/common_symbols/build.zig+12-7
...@@ -1,14 +1,19 @@...@@ -1,14 +1,19 @@
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 mode = b.standardReleaseOptions();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 });
7 lib_a.addCSourceFiles(&.{ "c.c", "a.c", "b.c" }, &.{"-fcommon"});11 lib_a.addCSourceFiles(&.{ "c.c", "a.c", "b.c" }, &.{"-fcommon"});
8 lib_a.setBuildMode(mode);
912
10 const test_exe = b.addTest("main.zig");13 const test_exe = b.addTest(.{
11 test_exe.setBuildMode(mode);14 .root_source_file = .{ .path = "main.zig" },
15 .optimize = optimize,
16 });
12 test_exe.linkLibrary(lib_a);17 test_exe.linkLibrary(lib_a);
1318
14 const test_step = b.step("test", "Test it");19 const test_step = b.step("test", "Test it");
test/link/common_symbols_alignment/build.zig+14-7
...@@ -1,14 +1,21 @@...@@ -1,14 +1,21 @@
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 mode = b.standardReleaseOptions();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 });
7 lib_a.addCSourceFiles(&.{"a.c"}, &.{"-fcommon"});12 lib_a.addCSourceFiles(&.{"a.c"}, &.{"-fcommon"});
8 lib_a.setBuildMode(mode);
913
10 const test_exe = b.addTest("main.zig");14 const test_exe = b.addTest(.{
11 test_exe.setBuildMode(mode);15 .root_source_file = .{ .path = "main.zig" },
16 .optimize = optimize,
17 .target = target,
18 });
12 test_exe.linkLibrary(lib_a);19 test_exe.linkLibrary(lib_a);
1320
14 const test_step = b.step("test", "Test it");21 const test_step = b.step("test", "Test it");
test/link/interdependent_static_c_libs/build.zig+19-9
...@@ -1,20 +1,30 @@...@@ -1,20 +1,30 @@
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 mode = b.standardReleaseOptions();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 });
7 lib_a.addCSourceFile("a.c", &[_][]const u8{});12 lib_a.addCSourceFile("a.c", &[_][]const u8{});
8 lib_a.setBuildMode(mode);
9 lib_a.addIncludePath(".");13 lib_a.addIncludePath(".");
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 });
12 lib_b.addCSourceFile("b.c", &[_][]const u8{});20 lib_b.addCSourceFile("b.c", &[_][]const u8{});
13 lib_b.setBuildMode(mode);
14 lib_b.addIncludePath(".");21 lib_b.addIncludePath(".");
1522
16 const test_exe = b.addTest("main.zig");23 const test_exe = b.addTest(.{
17 test_exe.setBuildMode(mode);24 .root_source_file = .{ .path = "main.zig" },
25 .optimize = optimize,
26 .target = target,
27 });
18 test_exe.linkLibrary(lib_a);28 test_exe.linkLibrary(lib_a);
19 test_exe.linkLibrary(lib_b);29 test_exe.linkLibrary(lib_b);
20 test_exe.addIncludePath(".");30 test_exe.addIncludePath(".");
test/link/macho/bugs/13056/build.zig+6-5
...@@ -1,8 +1,7 @@...@@ -1,8 +1,7 @@
1const std = @import("std");1const std = @import("std");
2const Builder = std.build.Builder;
32
4pub fn build(b: *Builder) void {3pub fn build(b: *std.Build) void {
5 const mode = b.standardReleaseOptions();4 const optimize = b.standardOptimizeOption(.{});
65
7 const target: std.zig.CrossTarget = .{ .os_tag = .macos };6 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
8 const target_info = std.zig.system.NativeTargetInfo.detect(target) catch unreachable;7 const target_info = std.zig.system.NativeTargetInfo.detect(target) catch unreachable;
...@@ -11,7 +10,10 @@ pub fn build(b: *Builder) void {...@@ -11,7 +10,10 @@ pub fn build(b: *Builder) void {
1110
12 const test_step = b.step("test", "Test the program");11 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 });
15 b.default_step.dependOn(&exe.step);17 b.default_step.dependOn(&exe.step);
16 exe.addIncludePath(std.fs.path.join(b.allocator, &.{ sdk.path, "/usr/include" }) catch unreachable);18 exe.addIncludePath(std.fs.path.join(b.allocator, &.{ sdk.path, "/usr/include" }) catch unreachable);
17 exe.addIncludePath(std.fs.path.join(b.allocator, &.{ sdk.path, "/usr/include/c++/v1" }) catch unreachable);19 exe.addIncludePath(std.fs.path.join(b.allocator, &.{ sdk.path, "/usr/include/c++/v1" }) catch unreachable);
...@@ -20,7 +22,6 @@ pub fn build(b: *Builder) void {...@@ -20,7 +22,6 @@ pub fn build(b: *Builder) void {
20 "-nostdinc++",22 "-nostdinc++",
21 });23 });
22 exe.addObjectFile(std.fs.path.join(b.allocator, &.{ sdk.path, "/usr/lib/libc++.tbd" }) catch unreachable);24 exe.addObjectFile(std.fs.path.join(b.allocator, &.{ sdk.path, "/usr/lib/libc++.tbd" }) catch unreachable);
23 exe.setBuildMode(mode);
2425
25 const run_cmd = exe.run();26 const run_cmd = exe.run();
26 run_cmd.expectStdErrEqual("x: 5\n");27 run_cmd.expectStdErrEqual("x: 5\n");
test/link/macho/bugs/13457/build.zig+8-7
...@@ -1,16 +1,17 @@...@@ -1,16 +1,17 @@
1const std = @import("std");1const 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 {
6 const mode = b.standardReleaseOptions();4 const optimize = b.standardOptimizeOption(.{});
7 const target: std.zig.CrossTarget = .{ .os_tag = .macos };5 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
86
9 const test_step = b.step("test", "Test the program");7 const test_step = b.step("test", "Test the program");
108
11 const exe = b.addExecutable("test", "main.zig");9 const exe = b.addExecutable(.{
12 exe.setBuildMode(mode);10 .name = "test",
13 exe.setTarget(target);11 .root_source_file = .{ .path = "main.zig" },
12 .optimize = optimize,
13 .target = target,
14 });
1415
15 const run = exe.runEmulatable();16 const run = exe.runEmulatable();
16 test_step.dependOn(&run.step);17 test_step.dependOn(&run.step);
test/link/macho/dead_strip/build.zig+14-10
...@@ -1,9 +1,7 @@...@@ -1,9 +1,7 @@
1const std = @import("std");1const 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 {
6 const mode = b.standardReleaseOptions();4 const optimize = b.standardOptimizeOption(.{});
7 const target: std.zig.CrossTarget = .{ .os_tag = .macos };5 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
86
9 const test_step = b.step("test", "Test the program");7 const test_step = b.step("test", "Test the program");
...@@ -11,7 +9,7 @@ pub fn build(b: *Builder) void {...@@ -11,7 +9,7 @@ pub fn build(b: *Builder) void {
119
12 {10 {
13 // Without -dead_strip, we expect `iAmUnused` symbol present11 // Without -dead_strip, we expect `iAmUnused` symbol present
14 const exe = createScenario(b, mode, target);12 const exe = createScenario(b, optimize, target);
1513
16 const check = exe.checkObject(.macho);14 const check = exe.checkObject(.macho);
17 check.checkInSymtab();15 check.checkInSymtab();
...@@ -24,7 +22,7 @@ pub fn build(b: *Builder) void {...@@ -24,7 +22,7 @@ pub fn build(b: *Builder) void {
2422
25 {23 {
26 // With -dead_strip, no `iAmUnused` symbol should be present24 // With -dead_strip, no `iAmUnused` symbol should be present
27 const exe = createScenario(b, mode, target);25 const exe = createScenario(b, optimize, target);
28 exe.link_gc_sections = true;26 exe.link_gc_sections = true;
2927
30 const check = exe.checkObject(.macho);28 const check = exe.checkObject(.macho);
...@@ -37,11 +35,17 @@ pub fn build(b: *Builder) void {...@@ -37,11 +35,17 @@ pub fn build(b: *Builder) void {
37 }35 }
38}36}
3937
40fn createScenario(b: *Builder, mode: std.builtin.Mode, target: std.zig.CrossTarget) *LibExeObjectStep {38fn createScenario(
41 const exe = b.addExecutable("test", null);39 b: *std.Build,
40 optimize: std.builtin.OptimizeMode,
41 target: std.zig.CrossTarget,
42) *std.Build.CompileStep {
43 const exe = b.addExecutable(.{
44 .name = "test",
45 .optimize = optimize,
46 .target = target,
47 });
42 exe.addCSourceFile("main.c", &[0][]const u8{});48 exe.addCSourceFile("main.c", &[0][]const u8{});
43 exe.setBuildMode(mode);
44 exe.setTarget(target);
45 exe.linkLibC();49 exe.linkLibC();
46 return exe;50 return exe;
47}51}
test/link/macho/dead_strip_dylibs/build.zig+9-9
...@@ -1,16 +1,14 @@...@@ -1,16 +1,14 @@
1const std = @import("std");1const 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 {
6 const mode = b.standardReleaseOptions();4 const optimize = b.standardOptimizeOption(.{});
75
8 const test_step = b.step("test", "Test the program");6 const test_step = b.step("test", "Test the program");
9 test_step.dependOn(b.getInstallStep());7 test_step.dependOn(b.getInstallStep());
108
11 {9 {
12 // Without -dead_strip_dylibs we expect `-la` to include liba.dylib in the final executable10 // 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
15 const check = exe.checkObject(.macho);13 const check = exe.checkObject(.macho);
16 check.checkStart("cmd LOAD_DYLIB");14 check.checkStart("cmd LOAD_DYLIB");
...@@ -27,7 +25,7 @@ pub fn build(b: *Builder) void {...@@ -27,7 +25,7 @@ pub fn build(b: *Builder) void {
2725
28 {26 {
29 // With -dead_strip_dylibs, we should include liba.dylib as it's unreachable27 // With -dead_strip_dylibs, we should include liba.dylib as it's unreachable
30 const exe = createScenario(b, mode);28 const exe = createScenario(b, optimize);
31 exe.dead_strip_dylibs = true;29 exe.dead_strip_dylibs = true;
3230
33 const run_cmd = exe.run();31 const run_cmd = exe.run();
...@@ -36,10 +34,12 @@ pub fn build(b: *Builder) void {...@@ -36,10 +34,12 @@ pub fn build(b: *Builder) void {
36 }34 }
37}35}
3836
39fn createScenario(b: *Builder, mode: std.builtin.Mode) *LibExeObjectStep {37fn createScenario(b: *std.Build, optimize: std.builtin.OptimizeMode) *std.Build.CompileStep {
40 const exe = b.addExecutable("test", null);38 const exe = b.addExecutable(.{
39 .name = "test",
40 .optimize = optimize,
41 });
41 exe.addCSourceFile("main.c", &[0][]const u8{});42 exe.addCSourceFile("main.c", &[0][]const u8{});
42 exe.setBuildMode(mode);
43 exe.linkLibC();43 exe.linkLibC();
44 exe.linkFramework("Cocoa");44 exe.linkFramework("Cocoa");
45 return exe;45 return exe;
test/link/macho/dylib/build.zig+13-9
...@@ -1,16 +1,18 @@...@@ -1,16 +1,18 @@
1const std = @import("std");1const std = @import("std");
2const Builder = std.build.Builder;
32
4pub fn build(b: *Builder) void {3pub fn build(b: *std.Build) void {
5 const mode = b.standardReleaseOptions();4 const optimize = b.standardOptimizeOption(.{});
6 const target: std.zig.CrossTarget = .{ .os_tag = .macos };5 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
76
8 const test_step = b.step("test", "Test");7 const test_step = b.step("test", "Test");
9 test_step.dependOn(b.getInstallStep());8 test_step.dependOn(b.getInstallStep());
109
11 const dylib = b.addSharedLibrary("a", null, b.version(1, 0, 0));10 const dylib = b.addSharedLibrary(.{
12 dylib.setBuildMode(mode);11 .name = "a",
13 dylib.setTarget(target);12 .version = .{ .major = 1, .minor = 0 },
13 .optimize = optimize,
14 .target = target,
15 });
14 dylib.addCSourceFile("a.c", &.{});16 dylib.addCSourceFile("a.c", &.{});
15 dylib.linkLibC();17 dylib.linkLibC();
16 dylib.install();18 dylib.install();
...@@ -24,9 +26,11 @@ pub fn build(b: *Builder) void {...@@ -24,9 +26,11 @@ pub fn build(b: *Builder) void {
2426
25 test_step.dependOn(&check_dylib.step);27 test_step.dependOn(&check_dylib.step);
2628
27 const exe = b.addExecutable("main", null);29 const exe = b.addExecutable(.{
28 exe.setTarget(target);30 .name = "main",
29 exe.setBuildMode(mode);31 .optimize = optimize,
32 .target = target,
33 });
30 exe.addCSourceFile("main.c", &.{});34 exe.addCSourceFile("main.c", &.{});
31 exe.linkSystemLibrary("a");35 exe.linkSystemLibrary("a");
32 exe.linkLibC();36 exe.linkLibC();
test/link/macho/empty/build.zig+8-7
...@@ -1,21 +1,22 @@...@@ -1,21 +1,22 @@
1const std = @import("std");1const std = @import("std");
2const Builder = std.build.Builder;
32
4pub fn build(b: *Builder) void {3pub fn build(b: *std.Build) void {
5 const mode = b.standardReleaseOptions();4 const optimize = b.standardOptimizeOption(.{});
6 const target: std.zig.CrossTarget = .{ .os_tag = .macos };5 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
76
8 const test_step = b.step("test", "Test the program");7 const test_step = b.step("test", "Test the program");
9 test_step.dependOn(b.getInstallStep());8 test_step.dependOn(b.getInstallStep());
109
11 const exe = b.addExecutable("test", null);10 const exe = b.addExecutable(.{
11 .name = "test",
12 .optimize = optimize,
13 .target = target,
14 });
12 exe.addCSourceFile("main.c", &[0][]const u8{});15 exe.addCSourceFile("main.c", &[0][]const u8{});
13 exe.addCSourceFile("empty.c", &[0][]const u8{});16 exe.addCSourceFile("empty.c", &[0][]const u8{});
14 exe.setBuildMode(mode);
15 exe.setTarget(target);
16 exe.linkLibC();17 exe.linkLibC();
1718
18 const run_cmd = std.build.EmulatableRunStep.create(b, "run", exe);19 const run_cmd = std.Build.EmulatableRunStep.create(b, "run", exe);
19 run_cmd.expectStdOutEqual("Hello!\n");20 run_cmd.expectStdOutEqual("Hello!\n");
20 test_step.dependOn(&run_cmd.step);21 test_step.dependOn(&run_cmd.step);
21}22}
test/link/macho/entry/build.zig+7-6
...@@ -1,15 +1,16 @@...@@ -1,15 +1,16 @@
1const std = @import("std");1const std = @import("std");
2const Builder = std.build.Builder;
32
4pub fn build(b: *Builder) void {3pub fn build(b: *std.Build) void {
5 const mode = b.standardReleaseOptions();4 const optimize = b.standardOptimizeOption(.{});
65
7 const test_step = b.step("test", "Test");6 const test_step = b.step("test", "Test");
8 test_step.dependOn(b.getInstallStep());7 test_step.dependOn(b.getInstallStep());
98
10 const exe = b.addExecutable("main", null);9 const exe = b.addExecutable(.{
11 exe.setTarget(.{ .os_tag = .macos });10 .name = "main",
12 exe.setBuildMode(mode);11 .optimize = optimize,
12 .target = .{ .os_tag = .macos },
13 });
13 exe.addCSourceFile("main.c", &.{});14 exe.addCSourceFile("main.c", &.{});
14 exe.linkLibC();15 exe.linkLibC();
15 exe.entry_symbol_name = "_non_main";16 exe.entry_symbol_name = "_non_main";
test/link/macho/headerpad/build.zig+11-11
...@@ -1,17 +1,15 @@...@@ -1,17 +1,15 @@
1const std = @import("std");1const std = @import("std");
2const builtin = @import("builtin");2const builtin = @import("builtin");
3const Builder = std.build.Builder;
4const LibExeObjectStep = std.build.LibExeObjStep;
53
6pub fn build(b: *Builder) void {4pub fn build(b: *std.Build) void {
7 const mode = b.standardReleaseOptions();5 const optimize = b.standardOptimizeOption(.{});
86
9 const test_step = b.step("test", "Test");7 const test_step = b.step("test", "Test");
10 test_step.dependOn(b.getInstallStep());8 test_step.dependOn(b.getInstallStep());
119
12 {10 {
13 // Test -headerpad_max_install_names11 // Test -headerpad_max_install_names
14 const exe = simpleExe(b, mode);12 const exe = simpleExe(b, optimize);
15 exe.headerpad_max_install_names = true;13 exe.headerpad_max_install_names = true;
1614
17 const check = exe.checkObject(.macho);15 const check = exe.checkObject(.macho);
...@@ -36,7 +34,7 @@ pub fn build(b: *Builder) void {...@@ -36,7 +34,7 @@ pub fn build(b: *Builder) void {
3634
37 {35 {
38 // Test -headerpad36 // Test -headerpad
39 const exe = simpleExe(b, mode);37 const exe = simpleExe(b, optimize);
40 exe.headerpad_size = 0x10000;38 exe.headerpad_size = 0x10000;
4139
42 const check = exe.checkObject(.macho);40 const check = exe.checkObject(.macho);
...@@ -52,7 +50,7 @@ pub fn build(b: *Builder) void {...@@ -52,7 +50,7 @@ pub fn build(b: *Builder) void {
5250
53 {51 {
54 // Test both flags with -headerpad overriding -headerpad_max_install_names52 // Test both flags with -headerpad overriding -headerpad_max_install_names
55 const exe = simpleExe(b, mode);53 const exe = simpleExe(b, optimize);
56 exe.headerpad_max_install_names = true;54 exe.headerpad_max_install_names = true;
57 exe.headerpad_size = 0x10000;55 exe.headerpad_size = 0x10000;
5856
...@@ -69,7 +67,7 @@ pub fn build(b: *Builder) void {...@@ -69,7 +67,7 @@ pub fn build(b: *Builder) void {
6967
70 {68 {
71 // Test both flags with -headerpad_max_install_names overriding -headerpad69 // Test both flags with -headerpad_max_install_names overriding -headerpad
72 const exe = simpleExe(b, mode);70 const exe = simpleExe(b, optimize);
73 exe.headerpad_size = 0x1000;71 exe.headerpad_size = 0x1000;
74 exe.headerpad_max_install_names = true;72 exe.headerpad_max_install_names = true;
7573
...@@ -94,9 +92,11 @@ pub fn build(b: *Builder) void {...@@ -94,9 +92,11 @@ pub fn build(b: *Builder) void {
94 }92 }
95}93}
9694
97fn simpleExe(b: *Builder, mode: std.builtin.Mode) *LibExeObjectStep {95fn simpleExe(b: *std.Build, optimize: std.builtin.OptimizeMode) *std.Build.CompileStep {
98 const exe = b.addExecutable("main", null);96 const exe = b.addExecutable(.{
99 exe.setBuildMode(mode);97 .name = "main",
98 .optimize = optimize,
99 });
100 exe.addCSourceFile("main.c", &.{});100 exe.addCSourceFile("main.c", &.{});
101 exe.linkLibC();101 exe.linkLibC();
102 exe.linkFramework("CoreFoundation");102 exe.linkFramework("CoreFoundation");
test/link/macho/linksection/build.zig+9-6
...@@ -1,15 +1,18 @@...@@ -1,15 +1,18 @@
1const std = @import("std");1const std = @import("std");
22
3pub fn build(b: *std.build.Builder) void {3pub fn build(b: *std.Build) void {
4 const mode = b.standardReleaseOptions();4 const optimize = b.standardOptimizeOption(.{});
5 const target = std.zig.CrossTarget{ .os_tag = .macos };5 const target = std.zig.CrossTarget{ .os_tag = .macos };
66
7 const test_step = b.step("test", "Test");7 const test_step = b.step("test", "Test");
8 test_step.dependOn(b.getInstallStep());8 test_step.dependOn(b.getInstallStep());
99
10 const obj = b.addObject("test", "main.zig");10 const obj = b.addObject(.{
11 obj.setBuildMode(mode);11 .name = "test",
12 obj.setTarget(target);12 .root_source_file = .{ .path = "main.zig" },
13 .optimize = optimize,
14 .target = target,
15 });
1316
14 const check = obj.checkObject(.macho);17 const check = obj.checkObject(.macho);
1518
...@@ -19,7 +22,7 @@ pub fn build(b: *std.build.Builder) void {...@@ -19,7 +22,7 @@ pub fn build(b: *std.build.Builder) void {
19 check.checkInSymtab();22 check.checkInSymtab();
20 check.checkNext("{*} (__TEXT,__TestFn) external _testFn");23 check.checkNext("{*} (__TEXT,__TestFn) external _testFn");
2124
22 if (mode == .Debug) {25 if (optimize == .Debug) {
23 check.checkInSymtab();26 check.checkInSymtab();
24 check.checkNext("{*} (__TEXT,__TestGenFnA) _main.testGenericFn__anon_{*}");27 check.checkNext("{*} (__TEXT,__TestGenFnA) _main.testGenericFn__anon_{*}");
25 }28 }
test/link/macho/needed_framework/build.zig+6-6
...@@ -1,18 +1,18 @@...@@ -1,18 +1,18 @@
1const std = @import("std");1const 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 {
6 const mode = b.standardReleaseOptions();4 const optimize = b.standardOptimizeOption(.{});
75
8 const test_step = b.step("test", "Test the program");6 const test_step = b.step("test", "Test the program");
9 test_step.dependOn(b.getInstallStep());7 test_step.dependOn(b.getInstallStep());
108
11 // -dead_strip_dylibs9 // -dead_strip_dylibs
12 // -needed_framework Cocoa10 // -needed_framework Cocoa
13 const exe = b.addExecutable("test", null);11 const exe = b.addExecutable(.{
12 .name = "test",
13 .optimize = optimize,
14 });
14 exe.addCSourceFile("main.c", &[0][]const u8{});15 exe.addCSourceFile("main.c", &[0][]const u8{});
15 exe.setBuildMode(mode);
16 exe.linkLibC();16 exe.linkLibC();
17 exe.linkFrameworkNeeded("Cocoa");17 exe.linkFrameworkNeeded("Cocoa");
18 exe.dead_strip_dylibs = true;18 exe.dead_strip_dylibs = true;
test/link/macho/needed_library/build.zig+13-10
...@@ -1,27 +1,30 @@...@@ -1,27 +1,30 @@
1const std = @import("std");1const 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 {
6 const mode = b.standardReleaseOptions();4 const optimize = b.standardOptimizeOption(.{});
7 const target: std.zig.CrossTarget = .{ .os_tag = .macos };5 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
86
9 const test_step = b.step("test", "Test the program");7 const test_step = b.step("test", "Test the program");
10 test_step.dependOn(b.getInstallStep());8 test_step.dependOn(b.getInstallStep());
119
12 const dylib = b.addSharedLibrary("a", null, b.version(1, 0, 0));10 const dylib = b.addSharedLibrary(.{
13 dylib.setTarget(target);11 .name = "a",
14 dylib.setBuildMode(mode);12 .version = .{ .major = 1, .minor = 0 },
13 .optimize = optimize,
14 .target = target,
15 });
15 dylib.addCSourceFile("a.c", &.{});16 dylib.addCSourceFile("a.c", &.{});
16 dylib.linkLibC();17 dylib.linkLibC();
17 dylib.install();18 dylib.install();
1819
19 // -dead_strip_dylibs20 // -dead_strip_dylibs
20 // -needed-la21 // -needed-la
21 const exe = b.addExecutable("test", null);22 const exe = b.addExecutable(.{
23 .name = "test",
24 .optimize = optimize,
25 .target = target,
26 });
22 exe.addCSourceFile("main.c", &[0][]const u8{});27 exe.addCSourceFile("main.c", &[0][]const u8{});
23 exe.setBuildMode(mode);
24 exe.setTarget(target);
25 exe.linkLibC();28 exe.linkLibC();
26 exe.linkSystemLibraryNeeded("a");29 exe.linkSystemLibraryNeeded("a");
27 exe.addLibraryPath(b.pathFromRoot("zig-out/lib"));30 exe.addLibraryPath(b.pathFromRoot("zig-out/lib"));
test/link/macho/objc/build.zig+7-6
...@@ -1,21 +1,22 @@...@@ -1,21 +1,22 @@
1const std = @import("std");1const std = @import("std");
2const Builder = std.build.Builder;
32
4pub fn build(b: *Builder) void {3pub fn build(b: *std.Build) void {
5 const mode = b.standardReleaseOptions();4 const optimize = b.standardOptimizeOption(.{});
65
7 const test_step = b.step("test", "Test the program");6 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 });
10 exe.addIncludePath(".");12 exe.addIncludePath(".");
11 exe.addCSourceFile("Foo.m", &[0][]const u8{});13 exe.addCSourceFile("Foo.m", &[0][]const u8{});
12 exe.addCSourceFile("test.m", &[0][]const u8{});14 exe.addCSourceFile("test.m", &[0][]const u8{});
13 exe.setBuildMode(mode);
14 exe.linkLibC();15 exe.linkLibC();
15 // TODO when we figure out how to ship framework stubs for cross-compilation,16 // TODO when we figure out how to ship framework stubs for cross-compilation,
16 // populate paths to the sysroot here.17 // populate paths to the sysroot here.
17 exe.linkFramework("Foundation");18 exe.linkFramework("Foundation");
1819
19 const run_cmd = std.build.EmulatableRunStep.create(b, "run", exe);20 const run_cmd = std.Build.EmulatableRunStep.create(b, "run", exe);
20 test_step.dependOn(&run_cmd.step);21 test_step.dependOn(&run_cmd.step);
21}22}
test/link/macho/objcpp/build.zig+6-5
...@@ -1,17 +1,18 @@...@@ -1,17 +1,18 @@
1const std = @import("std");1const std = @import("std");
2const Builder = std.build.Builder;
32
4pub fn build(b: *Builder) void {3pub fn build(b: *std.Build) void {
5 const mode = b.standardReleaseOptions();4 const optimize = b.standardOptimizeOption(.{});
65
7 const test_step = b.step("test", "Test the program");6 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 });
10 b.default_step.dependOn(&exe.step);12 b.default_step.dependOn(&exe.step);
11 exe.addIncludePath(".");13 exe.addIncludePath(".");
12 exe.addCSourceFile("Foo.mm", &[0][]const u8{});14 exe.addCSourceFile("Foo.mm", &[0][]const u8{});
13 exe.addCSourceFile("test.mm", &[0][]const u8{});15 exe.addCSourceFile("test.mm", &[0][]const u8{});
14 exe.setBuildMode(mode);
15 exe.linkLibCpp();16 exe.linkLibCpp();
16 // TODO when we figure out how to ship framework stubs for cross-compilation,17 // TODO when we figure out how to ship framework stubs for cross-compilation,
17 // populate paths to the sysroot here.18 // populate paths to the sysroot here.
test/link/macho/pagezero/build.zig+12-9
...@@ -1,17 +1,18 @@...@@ -1,17 +1,18 @@
1const std = @import("std");1const std = @import("std");
2const Builder = std.build.Builder;
32
4pub fn build(b: *Builder) void {3pub fn build(b: *std.Build) void {
5 const mode = b.standardReleaseOptions();4 const optimize = b.standardOptimizeOption(.{});
6 const target: std.zig.CrossTarget = .{ .os_tag = .macos };5 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
76
8 const test_step = b.step("test", "Test");7 const test_step = b.step("test", "Test");
9 test_step.dependOn(b.getInstallStep());8 test_step.dependOn(b.getInstallStep());
109
11 {10 {
12 const exe = b.addExecutable("pagezero", null);11 const exe = b.addExecutable(.{
13 exe.setTarget(target);12 .name = "pagezero",
14 exe.setBuildMode(mode);13 .optimize = optimize,
14 .target = target,
15 });
15 exe.addCSourceFile("main.c", &.{});16 exe.addCSourceFile("main.c", &.{});
16 exe.linkLibC();17 exe.linkLibC();
17 exe.pagezero_size = 0x4000;18 exe.pagezero_size = 0x4000;
...@@ -29,9 +30,11 @@ pub fn build(b: *Builder) void {...@@ -29,9 +30,11 @@ pub fn build(b: *Builder) void {
29 }30 }
3031
31 {32 {
32 const exe = b.addExecutable("no_pagezero", null);33 const exe = b.addExecutable(.{
33 exe.setTarget(target);34 .name = "no_pagezero",
34 exe.setBuildMode(mode);35 .optimize = optimize,
36 .target = target,
37 });
35 exe.addCSourceFile("main.c", &.{});38 exe.addCSourceFile("main.c", &.{});
36 exe.linkLibC();39 exe.linkLibC();
37 exe.pagezero_size = 0;40 exe.pagezero_size = 0;
test/link/macho/search_strategy/build.zig+28-19
...@@ -1,9 +1,7 @@...@@ -1,9 +1,7 @@
1const std = @import("std");1const 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 {
6 const mode = b.standardReleaseOptions();4 const optimize = b.standardOptimizeOption(.{});
7 const target: std.zig.CrossTarget = .{ .os_tag = .macos };5 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
86
9 const test_step = b.step("test", "Test");7 const test_step = b.step("test", "Test");
...@@ -11,7 +9,7 @@ pub fn build(b: *Builder) void {...@@ -11,7 +9,7 @@ pub fn build(b: *Builder) void {
119
12 {10 {
13 // -search_dylibs_first11 // -search_dylibs_first
14 const exe = createScenario(b, mode, target);12 const exe = createScenario(b, optimize, target);
15 exe.search_strategy = .dylibs_first;13 exe.search_strategy = .dylibs_first;
1614
17 const check = exe.checkObject(.macho);15 const check = exe.checkObject(.macho);
...@@ -26,40 +24,51 @@ pub fn build(b: *Builder) void {...@@ -26,40 +24,51 @@ pub fn build(b: *Builder) void {
2624
27 {25 {
28 // -search_paths_first26 // -search_paths_first
29 const exe = createScenario(b, mode, target);27 const exe = createScenario(b, optimize, target);
30 exe.search_strategy = .paths_first;28 exe.search_strategy = .paths_first;
3129
32 const run = std.build.EmulatableRunStep.create(b, "run", exe);30 const run = std.Build.EmulatableRunStep.create(b, "run", exe);
33 run.cwd = b.pathFromRoot(".");31 run.cwd = b.pathFromRoot(".");
34 run.expectStdOutEqual("Hello world");32 run.expectStdOutEqual("Hello world");
35 test_step.dependOn(&run.step);33 test_step.dependOn(&run.step);
36 }34 }
37}35}
3836
39fn createScenario(b: *Builder, mode: std.builtin.Mode, target: std.zig.CrossTarget) *LibExeObjectStep {37fn createScenario(
40 const static = b.addStaticLibrary("a", null);38 b: *std.Build,
41 static.setTarget(target);39 optimize: std.builtin.OptimizeMode,
42 static.setBuildMode(mode);40 target: std.zig.CrossTarget,
41) *std.Build.CompileStep {
42 const static = b.addStaticLibrary(.{
43 .name = "a",
44 .optimize = optimize,
45 .target = target,
46 });
43 static.addCSourceFile("a.c", &.{});47 static.addCSourceFile("a.c", &.{});
44 static.linkLibC();48 static.linkLibC();
45 static.override_dest_dir = std.build.InstallDir{49 static.override_dest_dir = std.Build.InstallDir{
46 .custom = "static",50 .custom = "static",
47 };51 };
48 static.install();52 static.install();
4953
50 const dylib = b.addSharedLibrary("a", null, b.version(1, 0, 0));54 const dylib = b.addSharedLibrary(.{
51 dylib.setTarget(target);55 .name = "a",
52 dylib.setBuildMode(mode);56 .version = .{ .major = 1, .minor = 0 },
57 .optimize = optimize,
58 .target = target,
59 });
53 dylib.addCSourceFile("a.c", &.{});60 dylib.addCSourceFile("a.c", &.{});
54 dylib.linkLibC();61 dylib.linkLibC();
55 dylib.override_dest_dir = std.build.InstallDir{62 dylib.override_dest_dir = std.Build.InstallDir{
56 .custom = "dynamic",63 .custom = "dynamic",
57 };64 };
58 dylib.install();65 dylib.install();
5966
60 const exe = b.addExecutable("main", null);67 const exe = b.addExecutable(.{
61 exe.setTarget(target);68 .name = "main",
62 exe.setBuildMode(mode);69 .optimize = optimize,
70 .target = target,
71 });
63 exe.addCSourceFile("main.c", &.{});72 exe.addCSourceFile("main.c", &.{});
64 exe.linkSystemLibraryName("a");73 exe.linkSystemLibraryName("a");
65 exe.linkLibC();74 exe.linkLibC();
test/link/macho/stack_size/build.zig+7-6
...@@ -1,16 +1,17 @@...@@ -1,16 +1,17 @@
1const std = @import("std");1const std = @import("std");
2const Builder = std.build.Builder;
32
4pub fn build(b: *Builder) void {3pub fn build(b: *std.Build) void {
5 const mode = b.standardReleaseOptions();4 const optimize = b.standardOptimizeOption(.{});
6 const target: std.zig.CrossTarget = .{ .os_tag = .macos };5 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
76
8 const test_step = b.step("test", "Test");7 const test_step = b.step("test", "Test");
9 test_step.dependOn(b.getInstallStep());8 test_step.dependOn(b.getInstallStep());
109
11 const exe = b.addExecutable("main", null);10 const exe = b.addExecutable(.{
12 exe.setTarget(target);11 .name = "main",
13 exe.setBuildMode(mode);12 .optimize = optimize,
13 .target = target,
14 });
14 exe.addCSourceFile("main.c", &.{});15 exe.addCSourceFile("main.c", &.{});
15 exe.linkLibC();16 exe.linkLibC();
16 exe.stack_size = 0x100000000;17 exe.stack_size = 0x100000000;
test/link/macho/strict_validation/build.zig+8-7
...@@ -1,18 +1,19 @@...@@ -1,18 +1,19 @@
1const std = @import("std");1const std = @import("std");
2const builtin = @import("builtin");2const builtin = @import("builtin");
3const Builder = std.build.Builder;
4const LibExeObjectStep = std.build.LibExeObjStep;
53
6pub fn build(b: *Builder) void {4pub fn build(b: *std.Build) void {
7 const mode = b.standardReleaseOptions();5 const optimize = b.standardOptimizeOption(.{});
8 const target: std.zig.CrossTarget = .{ .os_tag = .macos };6 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
97
10 const test_step = b.step("test", "Test");8 const test_step = b.step("test", "Test");
11 test_step.dependOn(b.getInstallStep());9 test_step.dependOn(b.getInstallStep());
1210
13 const exe = b.addExecutable("main", "main.zig");11 const exe = b.addExecutable(.{
14 exe.setBuildMode(mode);12 .name = "main",
15 exe.setTarget(target);13 .root_source_file = .{ .path = "main.zig" },
14 .optimize = optimize,
15 .target = target,
16 });
16 exe.linkLibC();17 exe.linkLibC();
1718
18 const check_exe = exe.checkObject(.macho);19 const check_exe = exe.checkObject(.macho);
test/link/macho/tls/build.zig+13-9
...@@ -1,19 +1,23 @@...@@ -1,19 +1,23 @@
1const std = @import("std");1const std = @import("std");
2const Builder = std.build.Builder;
32
4pub fn build(b: *Builder) void {3pub fn build(b: *std.Build) void {
5 const mode = b.standardReleaseOptions();4 const optimize = b.standardOptimizeOption(.{});
6 const target: std.zig.CrossTarget = .{ .os_tag = .macos };5 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
76
8 const lib = b.addSharedLibrary("a", null, b.version(1, 0, 0));7 const lib = b.addSharedLibrary(.{
9 lib.setBuildMode(mode);8 .name = "a",
10 lib.setTarget(target);9 .version = .{ .major = 1, .minor = 0 },
10 .optimize = optimize,
11 .target = target,
12 });
11 lib.addCSourceFile("a.c", &.{});13 lib.addCSourceFile("a.c", &.{});
12 lib.linkLibC();14 lib.linkLibC();
1315
14 const test_exe = b.addTest("main.zig");16 const test_exe = b.addTest(.{
15 test_exe.setBuildMode(mode);17 .root_source_file = .{ .path = "main.zig" },
16 test_exe.setTarget(target);18 .optimize = optimize,
19 .target = target,
20 });
17 test_exe.linkLibrary(lib);21 test_exe.linkLibrary(lib);
18 test_exe.linkLibC();22 test_exe.linkLibC();
1923
test/link/macho/unwind_info/build.zig+18-14
...@@ -1,26 +1,24 @@...@@ -1,26 +1,24 @@
1const std = @import("std");1const std = @import("std");
2const builtin = @import("builtin");2const builtin = @import("builtin");
3const Builder = std.build.Builder;
4const LibExeObjectStep = std.build.LibExeObjStep;
53
6pub fn build(b: *Builder) void {4pub fn build(b: *std.Build) void {
7 const mode = b.standardReleaseOptions();5 const optimize = b.standardOptimizeOption(.{});
8 const target: std.zig.CrossTarget = .{ .os_tag = .macos };6 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
97
10 const test_step = b.step("test", "Test the program");8 const test_step = b.step("test", "Test the program");
119
12 testUnwindInfo(b, test_step, mode, target, false);10 testUnwindInfo(b, test_step, optimize, target, false);
13 testUnwindInfo(b, test_step, mode, target, true);11 testUnwindInfo(b, test_step, optimize, target, true);
14}12}
1513
16fn testUnwindInfo(14fn testUnwindInfo(
17 b: *Builder,15 b: *std.Build,
18 test_step: *std.build.Step,16 test_step: *std.Build.Step,
19 mode: std.builtin.Mode,17 optimize: std.builtin.OptimizeMode,
20 target: std.zig.CrossTarget,18 target: std.zig.CrossTarget,
21 dead_strip: bool,19 dead_strip: bool,
22) void {20) void {
23 const exe = createScenario(b, mode, target);21 const exe = createScenario(b, optimize, target);
24 exe.link_gc_sections = dead_strip;22 exe.link_gc_sections = dead_strip;
2523
26 const check = exe.checkObject(.macho);24 const check = exe.checkObject(.macho);
...@@ -52,8 +50,16 @@ fn testUnwindInfo(...@@ -52,8 +50,16 @@ fn testUnwindInfo(
52 test_step.dependOn(&run_cmd.step);50 test_step.dependOn(&run_cmd.step);
53}51}
5452
55fn createScenario(b: *Builder, mode: std.builtin.Mode, target: std.zig.CrossTarget) *LibExeObjectStep {53fn createScenario(
56 const exe = b.addExecutable("test", null);54 b: *std.Build,
55 optimize: std.builtin.OptimizeMode,
56 target: std.zig.CrossTarget,
57) *std.Build.CompileStep {
58 const exe = b.addExecutable(.{
59 .name = "test",
60 .optimize = optimize,
61 .target = target,
62 });
57 b.default_step.dependOn(&exe.step);63 b.default_step.dependOn(&exe.step);
58 exe.addIncludePath(".");64 exe.addIncludePath(".");
59 exe.addCSourceFiles(&[_][]const u8{65 exe.addCSourceFiles(&[_][]const u8{
...@@ -61,8 +67,6 @@ fn createScenario(b: *Builder, mode: std.builtin.Mode, target: std.zig.CrossTarg...@@ -61,8 +67,6 @@ fn createScenario(b: *Builder, mode: std.builtin.Mode, target: std.zig.CrossTarg
61 "simple_string.cpp",67 "simple_string.cpp",
62 "simple_string_owner.cpp",68 "simple_string_owner.cpp",
63 }, &[0][]const u8{});69 }, &[0][]const u8{});
64 exe.setBuildMode(mode);
65 exe.setTarget(target);
66 exe.linkLibCpp();70 exe.linkLibCpp();
67 return exe;71 return exe;
68}72}
test/link/macho/uuid/build.zig+17-12
...@@ -1,8 +1,6 @@...@@ -1,8 +1,6 @@
1const std = @import("std");1const 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 {
6 const test_step = b.step("test", "Test");4 const test_step = b.step("test", "Test");
7 test_step.dependOn(b.getInstallStep());5 test_step.dependOn(b.getInstallStep());
86
...@@ -27,23 +25,23 @@ pub fn build(b: *Builder) void {...@@ -27,23 +25,23 @@ pub fn build(b: *Builder) void {
27}25}
2826
29fn testUuid(27fn testUuid(
30 b: *Builder,28 b: *std.Build,
31 test_step: *std.build.Step,29 test_step: *std.Build.Step,
32 mode: std.builtin.Mode,30 optimize: std.builtin.OptimizeMode,
33 target: std.zig.CrossTarget,31 target: std.zig.CrossTarget,
34 comptime exp: []const u8,32 comptime exp: []const u8,
35) void {33) void {
36 // The calculated UUID value is independent of debug info and so it should34 // The calculated UUID value is independent of debug info and so it should
37 // stay the same across builds.35 // stay the same across builds.
38 {36 {
39 const dylib = simpleDylib(b, mode, target);37 const dylib = simpleDylib(b, optimize, target);
40 const check_dylib = dylib.checkObject(.macho);38 const check_dylib = dylib.checkObject(.macho);
41 check_dylib.checkStart("cmd UUID");39 check_dylib.checkStart("cmd UUID");
42 check_dylib.checkNext("uuid " ++ exp);40 check_dylib.checkNext("uuid " ++ exp);
43 test_step.dependOn(&check_dylib.step);41 test_step.dependOn(&check_dylib.step);
44 }42 }
45 {43 {
46 const dylib = simpleDylib(b, mode, target);44 const dylib = simpleDylib(b, optimize, target);
47 dylib.strip = true;45 dylib.strip = true;
48 const check_dylib = dylib.checkObject(.macho);46 const check_dylib = dylib.checkObject(.macho);
49 check_dylib.checkStart("cmd UUID");47 check_dylib.checkStart("cmd UUID");
...@@ -52,10 +50,17 @@ fn testUuid(...@@ -52,10 +50,17 @@ fn testUuid(
52 }50 }
53}51}
5452
55fn simpleDylib(b: *Builder, mode: std.builtin.Mode, target: std.zig.CrossTarget) *LibExeObjectStep {53fn simpleDylib(
56 const dylib = b.addSharedLibrary("test", null, b.version(1, 0, 0));54 b: *std.Build,
57 dylib.setTarget(target);55 optimize: std.builtin.OptimizeMode,
58 dylib.setBuildMode(mode);56 target: std.zig.CrossTarget,
57) *std.Build.CompileStep {
58 const dylib = b.addSharedLibrary(.{
59 .name = "test",
60 .version = .{ .major = 1, .minor = 0 },
61 .optimize = optimize,
62 .target = target,
63 });
59 dylib.addCSourceFile("test.c", &.{});64 dylib.addCSourceFile("test.c", &.{});
60 dylib.linkLibC();65 dylib.linkLibC();
61 return dylib;66 return dylib;
test/link/macho/weak_framework/build.zig+6-6
...@@ -1,16 +1,16 @@...@@ -1,16 +1,16 @@
1const std = @import("std");1const 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 {
6 const mode = b.standardReleaseOptions();4 const optimize = b.standardOptimizeOption(.{});
75
8 const test_step = b.step("test", "Test the program");6 const test_step = b.step("test", "Test the program");
9 test_step.dependOn(b.getInstallStep());7 test_step.dependOn(b.getInstallStep());
108
11 const exe = b.addExecutable("test", null);9 const exe = b.addExecutable(.{
10 .name = "test",
11 .optimize = optimize,
12 });
12 exe.addCSourceFile("main.c", &[0][]const u8{});13 exe.addCSourceFile("main.c", &[0][]const u8{});
13 exe.setBuildMode(mode);
14 exe.linkLibC();14 exe.linkLibC();
15 exe.linkFrameworkWeak("Cocoa");15 exe.linkFrameworkWeak("Cocoa");
1616
test/link/macho/weak_library/build.zig+13-10
...@@ -1,25 +1,28 @@...@@ -1,25 +1,28 @@
1const std = @import("std");1const 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 {
6 const mode = b.standardReleaseOptions();4 const optimize = b.standardOptimizeOption(.{});
7 const target: std.zig.CrossTarget = .{ .os_tag = .macos };5 const target: std.zig.CrossTarget = .{ .os_tag = .macos };
86
9 const test_step = b.step("test", "Test the program");7 const test_step = b.step("test", "Test the program");
10 test_step.dependOn(b.getInstallStep());8 test_step.dependOn(b.getInstallStep());
119
12 const dylib = b.addSharedLibrary("a", null, b.version(1, 0, 0));10 const dylib = b.addSharedLibrary(.{
13 dylib.setTarget(target);11 .name = "a",
14 dylib.setBuildMode(mode);12 .version = .{ .major = 1, .minor = 0, .patch = 0 },
13 .target = target,
14 .optimize = optimize,
15 });
15 dylib.addCSourceFile("a.c", &.{});16 dylib.addCSourceFile("a.c", &.{});
16 dylib.linkLibC();17 dylib.linkLibC();
17 dylib.install();18 dylib.install();
1819
19 const exe = b.addExecutable("test", null);20 const exe = b.addExecutable(.{
21 .name = "test",
22 .target = target,
23 .optimize = optimize,
24 });
20 exe.addCSourceFile("main.c", &[0][]const u8{});25 exe.addCSourceFile("main.c", &[0][]const u8{});
21 exe.setTarget(target);
22 exe.setBuildMode(mode);
23 exe.linkLibC();26 exe.linkLibC();
24 exe.linkSystemLibraryWeak("a");27 exe.linkSystemLibraryWeak("a");
25 exe.addLibraryPath(b.pathFromRoot("zig-out/lib"));28 exe.addLibraryPath(b.pathFromRoot("zig-out/lib"));
test/link/static_lib_as_system_lib/build.zig+13-7
...@@ -1,17 +1,23 @@...@@ -1,17 +1,23 @@
1const std = @import("std");1const std = @import("std");
2const Builder = std.build.Builder;
32
4pub fn build(b: *Builder) void {3pub fn build(b: *std.Build) void {
5 const mode = b.standardReleaseOptions();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 });
8 lib_a.addCSourceFile("a.c", &[_][]const u8{});12 lib_a.addCSourceFile("a.c", &[_][]const u8{});
9 lib_a.setBuildMode(mode);
10 lib_a.addIncludePath(".");13 lib_a.addIncludePath(".");
11 lib_a.install();14 lib_a.install();
1215
13 const test_exe = b.addTest("main.zig");16 const test_exe = b.addTest(.{
14 test_exe.setBuildMode(mode);17 .root_source_file = .{ .path = "main.zig" },
18 .optimize = optimize,
19 .target = target,
20 });
15 test_exe.linkSystemLibrary("a"); // force linking liba.a as -la21 test_exe.linkSystemLibrary("a"); // force linking liba.a as -la
16 test_exe.addSystemIncludePath(".");22 test_exe.addSystemIncludePath(".");
17 const search_path = std.fs.path.join(b.allocator, &[_][]const u8{ b.install_path, "lib" }) catch unreachable;23 const search_path = std.fs.path.join(b.allocator, &[_][]const u8{ b.install_path, "lib" }) catch unreachable;
test/link/wasm/archive/build.zig+7-7
...@@ -1,17 +1,17 @@...@@ -1,17 +1,17 @@
1const std = @import("std");1const 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 {
7 const test_step = b.step("test", "Test");4 const test_step = b.step("test", "Test");
8 test_step.dependOn(b.getInstallStep());5 test_step.dependOn(b.getInstallStep());
96
10 // The code in question will pull-in compiler-rt,7 // The code in question will pull-in compiler-rt,
11 // and therefore link with its archive file.8 // and therefore link with its archive file.
12 const lib = b.addSharedLibrary("main", "main.zig", .unversioned);9 const lib = b.addSharedLibrary(.{
13 lib.setBuildMode(mode);10 .name = "main",
14 lib.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });11 .root_source_file = .{ .path = "main.zig" },
12 .optimize = b.standardOptimizeOption(.{}),
13 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
14 });
15 lib.use_llvm = false;15 lib.use_llvm = false;
16 lib.use_lld = false;16 lib.use_lld = false;
17 lib.strip = false;17 lib.strip = false;
test/link/wasm/basic-features/build.zig+12-8
...@@ -1,14 +1,18 @@...@@ -1,14 +1,18 @@
1const std = @import("std");1const std = @import("std");
22
3pub fn build(b: *std.build.Builder) void {3pub fn build(b: *std.Build) void {
4 const mode = b.standardReleaseOptions();
5
6 // Library with explicitly set cpu features4 // Library with explicitly set cpu features
7 const lib = b.addSharedLibrary("lib", "main.zig", .unversioned);5 const lib = b.addSharedLibrary(.{
8 lib.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });6 .name = "lib",
9 lib.target.cpu_model = .{ .explicit = &std.Target.wasm.cpu.mvp };7 .root_source_file = .{ .path = "main.zig" },
10 lib.target.cpu_features_add.addFeature(0); // index 0 == atomics (see std.Target.wasm.Features)8 .optimize = b.standardOptimizeOption(.{}),
11 lib.setBuildMode(mode);9 .target = .{
10 .cpu_arch = .wasm32,
11 .cpu_model = .{ .explicit = &std.Target.wasm.cpu.mvp },
12 .cpu_features_add = std.Target.wasm.featureSet(&.{.atomics}),
13 .os_tag = .freestanding,
14 },
15 });
12 lib.use_llvm = false;16 lib.use_llvm = false;
13 lib.use_lld = false;17 lib.use_lld = false;
1418
test/link/wasm/bss/build.zig+7-7
...@@ -1,15 +1,15 @@...@@ -1,15 +1,15 @@
1const std = @import("std");1const 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 {
7 const test_step = b.step("test", "Test");4 const test_step = b.step("test", "Test");
8 test_step.dependOn(b.getInstallStep());5 test_step.dependOn(b.getInstallStep());
96
10 const lib = b.addSharedLibrary("lib", "lib.zig", .unversioned);7 const lib = b.addSharedLibrary(.{
11 lib.setBuildMode(mode);8 .name = "lib",
12 lib.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });9 .root_source_file = .{ .path = "lib.zig" },
10 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
11 .optimize = b.standardOptimizeOption(.{}),
12 });
13 lib.use_llvm = false;13 lib.use_llvm = false;
14 lib.use_lld = false;14 lib.use_lld = false;
15 lib.strip = false;15 lib.strip = false;
test/link/wasm/export-data/build.zig+9-7
...@@ -1,13 +1,15 @@...@@ -1,13 +1,15 @@
1const std = @import("std");1const std = @import("std");
2const Builder = std.build.Builder;
32
4pub fn build(b: *Builder) void {3pub fn build(b: *std.Build) void {
5 const test_step = b.step("test", "Test");4 const test_step = b.step("test", "Test");
6 test_step.dependOn(b.getInstallStep());5 test_step.dependOn(b.getInstallStep());
76
8 const lib = b.addSharedLibrary("lib", "lib.zig", .unversioned);7 const lib = b.addSharedLibrary(.{
9 lib.setBuildMode(.ReleaseSafe); // to make the output deterministic in address positions8 .name = "lib",
10 lib.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });9 .root_source_file = .{ .path = "lib.zig" },
10 .optimize = .ReleaseSafe, // to make the output deterministic in address positions
11 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
12 });
11 lib.use_lld = false;13 lib.use_lld = false;
12 lib.export_symbol_names = &.{ "foo", "bar" };14 lib.export_symbol_names = &.{ "foo", "bar" };
13 lib.global_base = 0; // put data section at address 0 to make data symbols easier to parse15 lib.global_base = 0; // put data section at address 0 to make data symbols easier to parse
...@@ -23,8 +25,8 @@ pub fn build(b: *Builder) void {...@@ -23,8 +25,8 @@ pub fn build(b: *Builder) void {
23 check_lib.checkNext("type i32");25 check_lib.checkNext("type i32");
24 check_lib.checkNext("mutable false");26 check_lib.checkNext("mutable false");
25 check_lib.checkNext("i32.const {bar_address}");27 check_lib.checkNext("i32.const {bar_address}");
26 check_lib.checkComputeCompare("foo_address", .{ .op = .eq, .value = .{ .literal = 0 } });28 check_lib.checkComputeCompare("foo_address", .{ .op = .eq, .value = .{ .literal = 4 } });
27 check_lib.checkComputeCompare("bar_address", .{ .op = .eq, .value = .{ .literal = 4 } });29 check_lib.checkComputeCompare("bar_address", .{ .op = .eq, .value = .{ .literal = 0 } });
2830
29 check_lib.checkStart("Section export");31 check_lib.checkStart("Section export");
30 check_lib.checkNext("entries 3");32 check_lib.checkNext("entries 3");
test/link/wasm/export/build.zig+21-12
...@@ -1,24 +1,33 @@...@@ -1,24 +1,33 @@
1const std = @import("std");1const std = @import("std");
22
3pub fn build(b: *std.build.Builder) void {3pub fn build(b: *std.Build) void {
4 const mode = b.standardReleaseOptions();4 const optimize = b.standardOptimizeOption(.{});
55
6 const no_export = b.addSharedLibrary("no-export", "main.zig", .unversioned);6 const no_export = b.addSharedLibrary(.{
7 no_export.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });7 .name = "no-export",
8 no_export.setBuildMode(mode);8 .root_source_file = .{ .path = "main.zig" },
9 .optimize = optimize,
10 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
11 });
9 no_export.use_llvm = false;12 no_export.use_llvm = false;
10 no_export.use_lld = false;13 no_export.use_lld = false;
1114
12 const dynamic_export = b.addSharedLibrary("dynamic", "main.zig", .unversioned);15 const dynamic_export = b.addSharedLibrary(.{
13 dynamic_export.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });16 .name = "dynamic",
14 dynamic_export.setBuildMode(mode);17 .root_source_file = .{ .path = "main.zig" },
18 .optimize = optimize,
19 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
20 });
15 dynamic_export.rdynamic = true;21 dynamic_export.rdynamic = true;
16 dynamic_export.use_llvm = false;22 dynamic_export.use_llvm = false;
17 dynamic_export.use_lld = false;23 dynamic_export.use_lld = false;
1824
19 const force_export = b.addSharedLibrary("force", "main.zig", .unversioned);25 const force_export = b.addSharedLibrary(.{
20 force_export.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });26 .name = "force",
21 force_export.setBuildMode(mode);27 .root_source_file = .{ .path = "main.zig" },
28 .optimize = optimize,
29 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
30 });
22 force_export.export_symbol_names = &.{"foo"};31 force_export.export_symbol_names = &.{"foo"};
23 force_export.use_llvm = false;32 force_export.use_llvm = false;
24 force_export.use_lld = false;33 force_export.use_lld = false;
test/link/wasm/extern-mangle/build.zig+7-7
...@@ -1,15 +1,15 @@...@@ -1,15 +1,15 @@
1const std = @import("std");1const 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 {
7 const test_step = b.step("test", "Test");4 const test_step = b.step("test", "Test");
8 test_step.dependOn(b.getInstallStep());5 test_step.dependOn(b.getInstallStep());
96
10 const lib = b.addSharedLibrary("lib", "lib.zig", .unversioned);7 const lib = b.addSharedLibrary(.{
11 lib.setBuildMode(mode);8 .name = "lib",
12 lib.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });9 .root_source_file = .{ .path = "lib.zig" },
10 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
11 .optimize = b.standardOptimizeOption(.{}),
12 });
13 lib.import_symbols = true; // import `a` and `b`13 lib.import_symbols = true; // import `a` and `b`
14 lib.rdynamic = true; // export `foo`14 lib.rdynamic = true; // export `foo`
15 lib.install();15 lib.install();
test/link/wasm/extern/build.zig+7-5
...@@ -1,10 +1,12 @@...@@ -1,10 +1,12 @@
1const std = @import("std");1const std = @import("std");
22
3pub fn build(b: *std.build.Builder) void {3pub fn build(b: *std.Build) void {
4 const mode = b.standardReleaseOptions();4 const exe = b.addExecutable(.{
5 const exe = b.addExecutable("extern", "main.zig");5 .name = "extern",
6 exe.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .wasi });6 .root_source_file = .{ .path = "main.zig" },
7 exe.setBuildMode(mode);7 .optimize = b.standardOptimizeOption(.{}),
8 .target = .{ .cpu_arch = .wasm32, .os_tag = .wasi },
9 });
8 exe.addCSourceFile("foo.c", &.{});10 exe.addCSourceFile("foo.c", &.{});
9 exe.use_llvm = false;11 exe.use_llvm = false;
10 exe.use_lld = false;12 exe.use_lld = false;
test/link/wasm/function-table/build.zig+20-12
...@@ -1,29 +1,37 @@...@@ -1,29 +1,37 @@
1const std = @import("std");1const std = @import("std");
2const Builder = std.build.Builder;
32
4pub fn build(b: *Builder) void {3pub fn build(b: *std.Build) void {
5 const mode = b.standardReleaseOptions();4 const optimize = b.standardOptimizeOption(.{});
65
7 const test_step = b.step("test", "Test");6 const test_step = b.step("test", "Test");
8 test_step.dependOn(b.getInstallStep());7 test_step.dependOn(b.getInstallStep());
98
10 const import_table = b.addSharedLibrary("lib", "lib.zig", .unversioned);9 const import_table = b.addSharedLibrary(.{
11 import_table.setBuildMode(mode);10 .name = "lib",
12 import_table.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });11 .root_source_file = .{ .path = "lib.zig" },
12 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
13 .optimize = optimize,
14 });
13 import_table.use_llvm = false;15 import_table.use_llvm = false;
14 import_table.use_lld = false;16 import_table.use_lld = false;
15 import_table.import_table = true;17 import_table.import_table = true;
1618
17 const export_table = b.addSharedLibrary("lib", "lib.zig", .unversioned);19 const export_table = b.addSharedLibrary(.{
18 export_table.setBuildMode(mode);20 .name = "lib",
19 export_table.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });21 .root_source_file = .{ .path = "lib.zig" },
22 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
23 .optimize = optimize,
24 });
20 export_table.use_llvm = false;25 export_table.use_llvm = false;
21 export_table.use_lld = false;26 export_table.use_lld = false;
22 export_table.export_table = true;27 export_table.export_table = true;
2328
24 const regular_table = b.addSharedLibrary("lib", "lib.zig", .unversioned);29 const regular_table = b.addSharedLibrary(.{
25 regular_table.setBuildMode(mode);30 .name = "lib",
26 regular_table.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });31 .root_source_file = .{ .path = "lib.zig" },
32 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
33 .optimize = optimize,
34 });
27 regular_table.use_llvm = false;35 regular_table.use_llvm = false;
28 regular_table.use_lld = false;36 regular_table.use_lld = false;
2937
test/link/wasm/infer-features/build.zig+21-10
...@@ -1,21 +1,32 @@...@@ -1,21 +1,32 @@
1const std = @import("std");1const std = @import("std");
22
3pub fn build(b: *std.build.Builder) void {3pub fn build(b: *std.Build) void {
4 const mode = b.standardReleaseOptions();4 const optimize = b.standardOptimizeOption(.{});
55
6 // Wasm Object file which we will use to infer the features from6 // Wasm Object file which we will use to infer the features from
7 const c_obj = b.addObject("c_obj", null);7 const c_obj = b.addObject(.{
8 c_obj.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });8 .name = "c_obj",
9 c_obj.target.cpu_model = .{ .explicit = &std.Target.wasm.cpu.bleeding_edge };9 .optimize = optimize,
10 .target = .{
11 .cpu_arch = .wasm32,
12 .cpu_model = .{ .explicit = &std.Target.wasm.cpu.bleeding_edge },
13 .os_tag = .freestanding,
14 },
15 });
10 c_obj.addCSourceFile("foo.c", &.{});16 c_obj.addCSourceFile("foo.c", &.{});
11 c_obj.setBuildMode(mode);
1217
13 // Wasm library that doesn't have any features specified. This will18 // Wasm library that doesn't have any features specified. This will
14 // infer its featureset from other linked object files.19 // infer its featureset from other linked object files.
15 const lib = b.addSharedLibrary("lib", "main.zig", .unversioned);20 const lib = b.addSharedLibrary(.{
16 lib.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });21 .name = "lib",
17 lib.target.cpu_model = .{ .explicit = &std.Target.wasm.cpu.mvp };22 .root_source_file = .{ .path = "main.zig" },
18 lib.setBuildMode(mode);23 .optimize = optimize,
24 .target = .{
25 .cpu_arch = .wasm32,
26 .cpu_model = .{ .explicit = &std.Target.wasm.cpu.mvp },
27 .os_tag = .freestanding,
28 },
29 });
19 lib.use_llvm = false;30 lib.use_llvm = false;
20 lib.use_lld = false;31 lib.use_lld = false;
21 lib.addObject(c_obj);32 lib.addObject(c_obj);
test/link/wasm/producers/build.zig+7-7
...@@ -1,16 +1,16 @@...@@ -1,16 +1,16 @@
1const std = @import("std");1const std = @import("std");
2const builtin = @import("builtin");2const 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 {
8 const test_step = b.step("test", "Test");5 const test_step = b.step("test", "Test");
9 test_step.dependOn(b.getInstallStep());6 test_step.dependOn(b.getInstallStep());
107
11 const lib = b.addSharedLibrary("lib", "lib.zig", .unversioned);8 const lib = b.addSharedLibrary(.{
12 lib.setBuildMode(mode);9 .name = "lib",
13 lib.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });10 .root_source_file = .{ .path = "lib.zig" },
11 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
12 .optimize = b.standardOptimizeOption(.{}),
13 });
14 lib.use_llvm = false;14 lib.use_llvm = false;
15 lib.use_lld = false;15 lib.use_lld = false;
16 lib.strip = false;16 lib.strip = false;
test/link/wasm/segments/build.zig+7-7
...@@ -1,15 +1,15 @@...@@ -1,15 +1,15 @@
1const std = @import("std");1const 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 {
7 const test_step = b.step("test", "Test");4 const test_step = b.step("test", "Test");
8 test_step.dependOn(b.getInstallStep());5 test_step.dependOn(b.getInstallStep());
96
10 const lib = b.addSharedLibrary("lib", "lib.zig", .unversioned);7 const lib = b.addSharedLibrary(.{
11 lib.setBuildMode(mode);8 .name = "lib",
12 lib.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });9 .root_source_file = .{ .path = "lib.zig" },
10 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
11 .optimize = b.standardOptimizeOption(.{}),
12 });
13 lib.use_llvm = false;13 lib.use_llvm = false;
14 lib.use_lld = false;14 lib.use_lld = false;
15 lib.strip = false;15 lib.strip = false;
test/link/wasm/stack_pointer/build.zig+7-7
...@@ -1,15 +1,15 @@...@@ -1,15 +1,15 @@
1const std = @import("std");1const 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 {
7 const test_step = b.step("test", "Test");4 const test_step = b.step("test", "Test");
8 test_step.dependOn(b.getInstallStep());5 test_step.dependOn(b.getInstallStep());
96
10 const lib = b.addSharedLibrary("lib", "lib.zig", .unversioned);7 const lib = b.addSharedLibrary(.{
11 lib.setBuildMode(mode);8 .name = "lib",
12 lib.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });9 .root_source_file = .{ .path = "lib.zig" },
10 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
11 .optimize = b.standardOptimizeOption(.{}),
12 });
13 lib.use_llvm = false;13 lib.use_llvm = false;
14 lib.use_lld = false;14 lib.use_lld = false;
15 lib.strip = false;15 lib.strip = false;
test/link/wasm/type/build.zig+7-7
...@@ -1,15 +1,15 @@...@@ -1,15 +1,15 @@
1const std = @import("std");1const 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 {
7 const test_step = b.step("test", "Test");4 const test_step = b.step("test", "Test");
8 test_step.dependOn(b.getInstallStep());5 test_step.dependOn(b.getInstallStep());
96
10 const lib = b.addSharedLibrary("lib", "lib.zig", .unversioned);7 const lib = b.addSharedLibrary(.{
11 lib.setBuildMode(mode);8 .name = "lib",
12 lib.setTarget(.{ .cpu_arch = .wasm32, .os_tag = .freestanding });9 .root_source_file = .{ .path = "lib.zig" },
10 .target = .{ .cpu_arch = .wasm32, .os_tag = .freestanding },
11 .optimize = b.standardOptimizeOption(.{}),
12 });
13 lib.use_llvm = false;13 lib.use_llvm = false;
14 lib.use_lld = false;14 lib.use_lld = false;
15 lib.strip = false;15 lib.strip = false;
test/src/compare_output.zig+25-11
...@@ -1,19 +1,18 @@...@@ -1,19 +1,18 @@
1// This is the implementation of the test harness.1// This is the implementation of the test harness.
2// For the actual test cases, see test/compare_output.zig.2// For the actual test cases, see test/compare_output.zig.
3const std = @import("std");3const std = @import("std");
4const build = std.build;
5const ArrayList = std.ArrayList;4const ArrayList = std.ArrayList;
6const fmt = std.fmt;5const fmt = std.fmt;
7const mem = std.mem;6const mem = std.mem;
8const fs = std.fs;7const fs = std.fs;
9const Mode = std.builtin.Mode;8const OptimizeMode = std.builtin.OptimizeMode;
109
11pub const CompareOutputContext = struct {10pub const CompareOutputContext = struct {
12 b: *build.Builder,11 b: *std.Build,
13 step: *build.Step,12 step: *std.Build.Step,
14 test_index: usize,13 test_index: usize,
15 test_filter: ?[]const u8,14 test_filter: ?[]const u8,
16 modes: []const Mode,15 optimize_modes: []const OptimizeMode,
1716
18 const Special = enum {17 const Special = enum {
19 None,18 None,
...@@ -102,7 +101,11 @@ pub const CompareOutputContext = struct {...@@ -102,7 +101,11 @@ pub const CompareOutputContext = struct {
102 if (mem.indexOf(u8, annotated_case_name, filter) == null) return;101 if (mem.indexOf(u8, annotated_case_name, filter) == null) return;
103 }102 }
104103
105 const exe = b.addExecutable("test", null);104 const exe = b.addExecutable(.{
105 .name = "test",
106 .target = .{},
107 .optimize = .Debug,
108 });
106 exe.addAssemblyFileSource(write_src.getFileSource(case.sources.items[0].filename).?);109 exe.addAssemblyFileSource(write_src.getFileSource(case.sources.items[0].filename).?);
107110
108 const run = exe.run();111 const run = exe.run();
...@@ -113,19 +116,23 @@ pub const CompareOutputContext = struct {...@@ -113,19 +116,23 @@ pub const CompareOutputContext = struct {
113 self.step.dependOn(&run.step);116 self.step.dependOn(&run.step);
114 },117 },
115 Special.None => {118 Special.None => {
116 for (self.modes) |mode| {119 for (self.optimize_modes) |optimize| {
117 const annotated_case_name = fmt.allocPrint(self.b.allocator, "{s} {s} ({s})", .{120 const annotated_case_name = fmt.allocPrint(self.b.allocator, "{s} {s} ({s})", .{
118 "compare-output",121 "compare-output",
119 case.name,122 case.name,
120 @tagName(mode),123 @tagName(optimize),
121 }) catch unreachable;124 }) catch unreachable;
122 if (self.test_filter) |filter| {125 if (self.test_filter) |filter| {
123 if (mem.indexOf(u8, annotated_case_name, filter) == null) continue;126 if (mem.indexOf(u8, annotated_case_name, filter) == null) continue;
124 }127 }
125128
126 const basename = case.sources.items[0].filename;129 const basename = case.sources.items[0].filename;
127 const exe = b.addExecutableSource("test", write_src.getFileSource(basename).?);130 const exe = b.addExecutable(.{
128 exe.setBuildMode(mode);131 .name = "test",
132 .root_source_file = write_src.getFileSource(basename).?,
133 .optimize = optimize,
134 .target = .{},
135 });
129 if (case.link_libc) {136 if (case.link_libc) {
130 exe.linkSystemLibrary("c");137 exe.linkSystemLibrary("c");
131 }138 }
...@@ -139,13 +146,20 @@ pub const CompareOutputContext = struct {...@@ -139,13 +146,20 @@ pub const CompareOutputContext = struct {
139 }146 }
140 },147 },
141 Special.RuntimeSafety => {148 Special.RuntimeSafety => {
149 // TODO iterate over self.optimize_modes and test this in both
150 // debug and release safe mode
142 const annotated_case_name = fmt.allocPrint(self.b.allocator, "safety {s}", .{case.name}) catch unreachable;151 const annotated_case_name = fmt.allocPrint(self.b.allocator, "safety {s}", .{case.name}) catch unreachable;
143 if (self.test_filter) |filter| {152 if (self.test_filter) |filter| {
144 if (mem.indexOf(u8, annotated_case_name, filter) == null) return;153 if (mem.indexOf(u8, annotated_case_name, filter) == null) return;
145 }154 }
146155
147 const basename = case.sources.items[0].filename;156 const basename = case.sources.items[0].filename;
148 const exe = b.addExecutableSource("test", write_src.getFileSource(basename).?);157 const exe = b.addExecutable(.{
158 .name = "test",
159 .root_source_file = write_src.getFileSource(basename).?,
160 .target = .{},
161 .optimize = .Debug,
162 });
149 if (case.link_libc) {163 if (case.link_libc) {
150 exe.linkSystemLibrary("c");164 exe.linkSystemLibrary("c");
151 }165 }
test/src/run_translated_c.zig+8-6
...@@ -1,15 +1,14 @@...@@ -1,15 +1,14 @@
1// This is the implementation of the test harness for running translated1// This is the implementation of the test harness for running translated
2// C code. For the actual test cases, see test/run_translated_c.zig.2// C code. For the actual test cases, see test/run_translated_c.zig.
3const std = @import("std");3const std = @import("std");
4const build = std.build;
5const ArrayList = std.ArrayList;4const ArrayList = std.ArrayList;
6const fmt = std.fmt;5const fmt = std.fmt;
7const mem = std.mem;6const mem = std.mem;
8const fs = std.fs;7const fs = std.fs;
98
10pub const RunTranslatedCContext = struct {9pub const RunTranslatedCContext = struct {
11 b: *build.Builder,10 b: *std.Build,
12 step: *build.Step,11 step: *std.Build.Step,
13 test_index: usize,12 test_index: usize,
14 test_filter: ?[]const u8,13 test_filter: ?[]const u8,
15 target: std.zig.CrossTarget,14 target: std.zig.CrossTarget,
...@@ -85,11 +84,14 @@ pub const RunTranslatedCContext = struct {...@@ -85,11 +84,14 @@ pub const RunTranslatedCContext = struct {
85 for (case.sources.items) |src_file| {84 for (case.sources.items) |src_file| {
86 write_src.add(src_file.filename, src_file.source);85 write_src.add(src_file.filename, src_file.source);
87 }86 }
88 const translate_c = b.addTranslateC(write_src.getFileSource(case.sources.items[0].filename).?);87 const translate_c = b.addTranslateC(.{
88 .source_file = write_src.getFileSource(case.sources.items[0].filename).?,
89 .target = .{},
90 .optimize = .Debug,
91 });
8992
90 translate_c.step.name = b.fmt("{s} translate-c", .{annotated_case_name});93 translate_c.step.name = b.fmt("{s} translate-c", .{annotated_case_name});
91 const exe = translate_c.addExecutable();94 const exe = translate_c.addExecutable(.{});
92 exe.setTarget(self.target);
93 exe.step.name = b.fmt("{s} build-exe", .{annotated_case_name});95 exe.step.name = b.fmt("{s} build-exe", .{annotated_case_name});
94 exe.linkLibC();96 exe.linkLibC();
95 const run = exe.run();97 const run = exe.run();
test/src/translate_c.zig+7-5
...@@ -1,7 +1,6 @@...@@ -1,7 +1,6 @@
1// This is the implementation of the test harness.1// This is the implementation of the test harness.
2// For the actual test cases, see test/translate_c.zig.2// For the actual test cases, see test/translate_c.zig.
3const std = @import("std");3const std = @import("std");
4const build = std.build;
5const ArrayList = std.ArrayList;4const ArrayList = std.ArrayList;
6const fmt = std.fmt;5const fmt = std.fmt;
7const mem = std.mem;6const mem = std.mem;
...@@ -9,8 +8,8 @@ const fs = std.fs;...@@ -9,8 +8,8 @@ const fs = std.fs;
9const CrossTarget = std.zig.CrossTarget;8const CrossTarget = std.zig.CrossTarget;
109
11pub const TranslateCContext = struct {10pub const TranslateCContext = struct {
12 b: *build.Builder,11 b: *std.Build,
13 step: *build.Step,12 step: *std.Build.Step,
14 test_index: usize,13 test_index: usize,
15 test_filter: ?[]const u8,14 test_filter: ?[]const u8,
1615
...@@ -108,10 +107,13 @@ pub const TranslateCContext = struct {...@@ -108,10 +107,13 @@ pub const TranslateCContext = struct {
108 write_src.add(src_file.filename, src_file.source);107 write_src.add(src_file.filename, src_file.source);
109 }108 }
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
113 translate_c.step.name = annotated_case_name;116 translate_c.step.name = annotated_case_name;
114 translate_c.setTarget(case.target);
115117
116 const check_file = translate_c.addCheckFile(case.expected_lines.items);118 const check_file = translate_c.addCheckFile(case.expected_lines.items);
117119
test/standalone/brace_expansion/build.zig+6-4
...@@ -1,8 +1,10 @@...@@ -1,8 +1,10 @@
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 main = b.addTest("main.zig");4 const main = b.addTest(.{
5 main.setBuildMode(b.standardReleaseOptions());5 .root_source_file = .{ .path = "main.zig" },
6 .optimize = b.standardOptimizeOption(.{}),
7 });
68
7 const test_step = b.step("test", "Test it");9 const test_step = b.step("test", "Test it");
8 test_step.dependOn(&main.step);10 test_step.dependOn(&main.step);
test/standalone/c_compiler/build.zig+13-10
...@@ -1,9 +1,8 @@...@@ -1,9 +1,8 @@
1const std = @import("std");1const std = @import("std");
2const builtin = @import("builtin");2const builtin = @import("builtin");
3const Builder = std.build.Builder;
4const CrossTarget = std.zig.CrossTarget;3const CrossTarget = std.zig.CrossTarget;
54
6// TODO integrate this with the std.build executor API5// TODO integrate this with the std.Build executor API
7fn isRunnableTarget(t: CrossTarget) bool {6fn isRunnableTarget(t: CrossTarget) bool {
8 if (t.isNative()) return true;7 if (t.isNative()) return true;
98
...@@ -11,24 +10,28 @@ fn isRunnableTarget(t: CrossTarget) bool {...@@ -11,24 +10,28 @@ fn isRunnableTarget(t: CrossTarget) bool {
11 t.getCpuArch() == builtin.cpu.arch);10 t.getCpuArch() == builtin.cpu.arch);
12}11}
1312
14pub fn build(b: *Builder) void {13pub fn build(b: *std.Build) void {
15 const mode = b.standardReleaseOptions();14 const optimize = b.standardOptimizeOption(.{});
16 const target = b.standardTargetOptions(.{});15 const target = b.standardTargetOptions(.{});
1716
18 const test_step = b.step("test", "Test the program");17 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 });
21 b.default_step.dependOn(&exe_c.step);24 b.default_step.dependOn(&exe_c.step);
22 exe_c.addCSourceFile("test.c", &[0][]const u8{});25 exe_c.addCSourceFile("test.c", &[0][]const u8{});
23 exe_c.setBuildMode(mode);
24 exe_c.setTarget(target);
25 exe_c.linkLibC();26 exe_c.linkLibC();
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 });
28 b.default_step.dependOn(&exe_cpp.step);33 b.default_step.dependOn(&exe_cpp.step);
29 exe_cpp.addCSourceFile("test.cpp", &[0][]const u8{});34 exe_cpp.addCSourceFile("test.cpp", &[0][]const u8{});
30 exe_cpp.setBuildMode(mode);
31 exe_cpp.setTarget(target);
32 exe_cpp.linkLibCpp();35 exe_cpp.linkLibCpp();
3336
34 switch (target.getOsTag()) {37 switch (target.getOsTag()) {
test/standalone/emit_asm_and_bin/build.zig+6-4
...@@ -1,8 +1,10 @@...@@ -1,8 +1,10 @@
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 main = b.addTest("main.zig");4 const main = b.addTest(.{
5 main.setBuildMode(b.standardReleaseOptions());5 .root_source_file = .{ .path = "main.zig" },
6 .optimize = b.standardOptimizeOption(.{}),
7 });
6 main.emit_asm = .{ .emit_to = b.pathFromRoot("main.s") };8 main.emit_asm = .{ .emit_to = b.pathFromRoot("main.s") };
7 main.emit_bin = .{ .emit_to = b.pathFromRoot("main") };9 main.emit_bin = .{ .emit_to = b.pathFromRoot("main") };
810
test/standalone/empty_env/build.zig+7-4
...@@ -1,8 +1,11 @@...@@ -1,8 +1,11 @@
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 main = b.addExecutable("main", "main.zig");4 const main = b.addExecutable(.{
5 main.setBuildMode(b.standardReleaseOptions());5 .name = "main",
6 .root_source_file = .{ .path = "main.zig" },
7 .optimize = b.standardOptimizeOption(.{}),
8 });
69
7 const run = main.run();10 const run = main.run();
8 run.clearEnvironment();11 run.clearEnvironment();
test/standalone/global_linkage/build.zig+19-9
...@@ -1,16 +1,26 @@...@@ -1,16 +1,26 @@
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 mode = b.standardReleaseOptions();4 const optimize = b.standardOptimizeOption(.{});
55
6 const obj1 = b.addStaticLibrary("obj1", "obj1.zig");6 const obj1 = b.addStaticLibrary(.{
7 obj1.setBuildMode(mode);7 .name = "obj1",
8 .root_source_file = .{ .path = "obj1.zig" },
9 .optimize = optimize,
10 .target = .{},
11 });
812
9 const obj2 = b.addStaticLibrary("obj2", "obj2.zig");13 const obj2 = b.addStaticLibrary(.{
10 obj2.setBuildMode(mode);14 .name = "obj2",
15 .root_source_file = .{ .path = "obj2.zig" },
16 .optimize = optimize,
17 .target = .{},
18 });
1119
12 const main = b.addTest("main.zig");20 const main = b.addTest(.{
13 main.setBuildMode(mode);21 .root_source_file = .{ .path = "main.zig" },
22 .optimize = optimize,
23 });
14 main.linkLibrary(obj1);24 main.linkLibrary(obj1);
15 main.linkLibrary(obj2);25 main.linkLibrary(obj2);
1626
test/standalone/install_raw_hex/build.zig+9-6
...@@ -1,8 +1,8 @@...@@ -1,8 +1,8 @@
1const builtin = @import("builtin");1const builtin = @import("builtin");
2const std = @import("std");2const 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 {
6 const target = .{6 const target = .{
7 .cpu_arch = .thumb,7 .cpu_arch = .thumb,
8 .cpu_model = .{ .explicit = &std.Target.arm.cpu.cortex_m4 },8 .cpu_model = .{ .explicit = &std.Target.arm.cpu.cortex_m4 },
...@@ -10,11 +10,14 @@ pub fn build(b: *std.build.Builder) void {...@@ -10,11 +10,14 @@ pub fn build(b: *std.build.Builder) void {
10 .abi = .gnueabihf,10 .abi = .gnueabihf,
11 };11 };
1212
13 const mode = b.standardReleaseOptions();13 const optimize = b.standardOptimizeOption(.{});
1414
15 const elf = b.addExecutable("zig-nrf52-blink.elf", "main.zig");15 const elf = b.addExecutable(.{
16 elf.setTarget(target);16 .name = "zig-nrf52-blink.elf",
17 elf.setBuildMode(mode);17 .root_source_file = .{ .path = "main.zig" },
18 .target = target,
19 .optimize = optimize,
20 });
1821
19 const test_step = b.step("test", "Test the program");22 const test_step = b.step("test", "Test the program");
20 b.default_step.dependOn(test_step);23 b.default_step.dependOn(test_step);
test/standalone/issue_11595/build.zig+9-7
...@@ -1,9 +1,8 @@...@@ -1,9 +1,8 @@
1const std = @import("std");1const std = @import("std");
2const builtin = @import("builtin");2const builtin = @import("builtin");
3const Builder = std.build.Builder;
4const CrossTarget = std.zig.CrossTarget;3const CrossTarget = std.zig.CrossTarget;
54
6// TODO integrate this with the std.build executor API5// TODO integrate this with the std.Build executor API
7fn isRunnableTarget(t: CrossTarget) bool {6fn isRunnableTarget(t: CrossTarget) bool {
8 if (t.isNative()) return true;7 if (t.isNative()) return true;
98
...@@ -11,12 +10,16 @@ fn isRunnableTarget(t: CrossTarget) bool {...@@ -11,12 +10,16 @@ fn isRunnableTarget(t: CrossTarget) bool {
11 t.getCpuArch() == builtin.cpu.arch);10 t.getCpuArch() == builtin.cpu.arch);
12}11}
1312
14pub fn build(b: *Builder) void {13pub fn build(b: *std.Build) void {
15 const mode = b.standardReleaseOptions();14 const optimize = b.standardOptimizeOption(.{});
16 const target = b.standardTargetOptions(.{});15 const target = b.standardTargetOptions(.{});
1716
18 const exe = b.addExecutable("zigtest", "main.zig");17 const exe = b.addExecutable(.{
19 exe.setBuildMode(mode);18 .name = "zigtest",
19 .root_source_file = .{ .path = "main.zig" },
20 .target = target,
21 .optimize = optimize,
22 });
20 exe.install();23 exe.install();
2124
22 const c_sources = [_][]const u8{25 const c_sources = [_][]const u8{
...@@ -39,7 +42,6 @@ pub fn build(b: *Builder) void {...@@ -39,7 +42,6 @@ pub fn build(b: *Builder) void {
39 exe.defineCMacro("QUX", "\"Q\" \"UX\"");42 exe.defineCMacro("QUX", "\"Q\" \"UX\"");
40 exe.defineCMacro("QUUX", "\"QU\\\"UX\"");43 exe.defineCMacro("QUUX", "\"QU\\\"UX\"");
4144
42 exe.setTarget(target);
43 b.default_step.dependOn(&exe.step);45 b.default_step.dependOn(&exe.step);
4446
45 const test_step = b.step("test", "Test the program");47 const test_step = b.step("test", "Test the program");
test/standalone/issue_12588/build.zig+8-6
...@@ -1,13 +1,15 @@...@@ -1,13 +1,15 @@
1const std = @import("std");1const std = @import("std");
2const Builder = std.build.Builder;
32
4pub fn build(b: *Builder) void {3pub fn build(b: *std.Build) void {
5 const mode = b.standardReleaseOptions();4 const optimize = b.standardOptimizeOption(.{});
6 const target = b.standardTargetOptions(.{});5 const target = b.standardTargetOptions(.{});
76
8 const obj = b.addObject("main", "main.zig");7 const obj = b.addObject(.{
9 obj.setBuildMode(mode);8 .name = "main",
10 obj.setTarget(target);9 .root_source_file = .{ .path = "main.zig" },
10 .optimize = optimize,
11 .target = target,
12 });
11 obj.emit_llvm_ir = .{ .emit_to = b.pathFromRoot("main.ll") };13 obj.emit_llvm_ir = .{ .emit_to = b.pathFromRoot("main.ll") };
12 obj.emit_llvm_bc = .{ .emit_to = b.pathFromRoot("main.bc") };14 obj.emit_llvm_bc = .{ .emit_to = b.pathFromRoot("main.bc") };
13 obj.emit_bin = .no_emit;15 obj.emit_bin = .no_emit;
test/standalone/issue_12706/build.zig+9-7
...@@ -1,9 +1,8 @@...@@ -1,9 +1,8 @@
1const std = @import("std");1const std = @import("std");
2const builtin = @import("builtin");2const builtin = @import("builtin");
3const Builder = std.build.Builder;
4const CrossTarget = std.zig.CrossTarget;3const CrossTarget = std.zig.CrossTarget;
54
6// TODO integrate this with the std.build executor API5// TODO integrate this with the std.Build executor API
7fn isRunnableTarget(t: CrossTarget) bool {6fn isRunnableTarget(t: CrossTarget) bool {
8 if (t.isNative()) return true;7 if (t.isNative()) return true;
98
...@@ -11,12 +10,16 @@ fn isRunnableTarget(t: CrossTarget) bool {...@@ -11,12 +10,16 @@ fn isRunnableTarget(t: CrossTarget) bool {
11 t.getCpuArch() == builtin.cpu.arch);10 t.getCpuArch() == builtin.cpu.arch);
12}11}
1312
14pub fn build(b: *Builder) void {13pub fn build(b: *std.Build) void {
15 const mode = b.standardReleaseOptions();14 const optimize = b.standardOptimizeOption(.{});
16 const target = b.standardTargetOptions(.{});15 const target = b.standardTargetOptions(.{});
1716
18 const exe = b.addExecutable("main", "main.zig");17 const exe = b.addExecutable(.{
19 exe.setBuildMode(mode);18 .name = "main",
19 .root_source_file = .{ .path = "main.zig" },
20 .optimize = optimize,
21 .target = target,
22 });
20 exe.install();23 exe.install();
2124
22 const c_sources = [_][]const u8{25 const c_sources = [_][]const u8{
...@@ -26,7 +29,6 @@ pub fn build(b: *Builder) void {...@@ -26,7 +29,6 @@ pub fn build(b: *Builder) void {
26 exe.addCSourceFiles(&c_sources, &.{});29 exe.addCSourceFiles(&c_sources, &.{});
27 exe.linkLibC();30 exe.linkLibC();
2831
29 exe.setTarget(target);
30 b.default_step.dependOn(&exe.step);32 b.default_step.dependOn(&exe.step);
3133
32 const test_step = b.step("test", "Test the program");34 const test_step = b.step("test", "Test the program");
test/standalone/issue_13030/build.zig+8-7
...@@ -1,16 +1,17 @@...@@ -1,16 +1,17 @@
1const std = @import("std");1const std = @import("std");
2const builtin = @import("builtin");2const builtin = @import("builtin");
3const Builder = std.build.Builder;
4const CrossTarget = std.zig.CrossTarget;3const CrossTarget = std.zig.CrossTarget;
54
6pub fn build(b: *Builder) void {5pub fn build(b: *std.Build) void {
7 const mode = b.standardReleaseOptions();6 const optimize = b.standardOptimizeOption(.{});
8 const target = b.standardTargetOptions(.{});7 const target = b.standardTargetOptions(.{});
98
10 const obj = b.addObject("main", "main.zig");9 const obj = b.addObject(.{
11 obj.setBuildMode(mode);10 .name = "main",
1211 .root_source_file = .{ .path = "main.zig" },
13 obj.setTarget(target);12 .optimize = optimize,
13 .target = target,
14 });
14 b.default_step.dependOn(&obj.step);15 b.default_step.dependOn(&obj.step);
1516
16 const test_step = b.step("test", "Test the program");17 const test_step = b.step("test", "Test the program");
test/standalone/issue_339/build.zig+8-3
...@@ -1,7 +1,12 @@...@@ -1,7 +1,12 @@
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 obj = b.addObject("test", "test.zig");4 const obj = b.addObject(.{
5 .name = "test",
6 .root_source_file = .{ .path = "test.zig" },
7 .target = b.standardTargetOptions(.{}),
8 .optimize = b.standardOptimizeOption(.{}),
9 });
510
6 const test_step = b.step("test", "Test the program");11 const test_step = b.step("test", "Test the program");
7 test_step.dependOn(&obj.step);12 test_step.dependOn(&obj.step);
test/standalone/issue_5825/build.zig+14-9
...@@ -1,22 +1,27 @@...@@ -1,22 +1,27 @@
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 target = .{4 const target = .{
5 .cpu_arch = .x86_64,5 .cpu_arch = .x86_64,
6 .os_tag = .windows,6 .os_tag = .windows,
7 .abi = .msvc,7 .abi = .msvc,
8 };8 };
9 const mode = b.standardReleaseOptions();9 const optimize = b.standardOptimizeOption(.{});
10 const obj = b.addObject("issue_5825", "main.zig");10 const obj = b.addObject(.{
11 obj.setTarget(target);11 .name = "issue_5825",
12 obj.setBuildMode(mode);12 .root_source_file = .{ .path = "main.zig" },
13 .optimize = optimize,
14 .target = target,
15 });
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 });
15 exe.subsystem = .Console;22 exe.subsystem = .Console;
16 exe.linkSystemLibrary("kernel32");23 exe.linkSystemLibrary("kernel32");
17 exe.linkSystemLibrary("ntdll");24 exe.linkSystemLibrary("ntdll");
18 exe.setTarget(target);
19 exe.setBuildMode(mode);
20 exe.addObject(obj);25 exe.addObject(obj);
2126
22 const test_step = b.step("test", "Test the program");27 const test_step = b.step("test", "Test the program");
test/standalone/issue_7030/build.zig+9-6
...@@ -1,10 +1,13 @@...@@ -1,10 +1,13 @@
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 exe = b.addExecutable("issue_7030", "main.zig");4 const exe = b.addExecutable(.{
5 exe.setTarget(.{5 .name = "issue_7030",
6 .cpu_arch = .wasm32,6 .root_source_file = .{ .path = "main.zig" },
7 .os_tag = .freestanding,7 .target = .{
8 .cpu_arch = .wasm32,
9 .os_tag = .freestanding,
10 },
8 });11 });
9 exe.install();12 exe.install();
10 b.default_step.dependOn(&exe.step);13 b.default_step.dependOn(&exe.step);
test/standalone/issue_794/build.zig+5-3
...@@ -1,7 +1,9 @@...@@ -1,7 +1,9 @@
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 test_artifact = b.addTest("main.zig");4 const test_artifact = b.addTest(.{
5 .root_source_file = .{ .path = "main.zig" },
6 });
5 test_artifact.addIncludePath("a_directory");7 test_artifact.addIncludePath("a_directory");
68
7 b.default_step.dependOn(&test_artifact.step);9 b.default_step.dependOn(&test_artifact.step);
test/standalone/issue_8550/build.zig+8-5
...@@ -1,6 +1,6 @@...@@ -1,6 +1,6 @@
1const std = @import("std");1const std = @import("std");
22
3pub fn build(b: *std.build.Builder) !void {3pub fn build(b: *std.Build) !void {
4 const target = std.zig.CrossTarget{4 const target = std.zig.CrossTarget{
5 .os_tag = .freestanding,5 .os_tag = .freestanding,
6 .cpu_arch = .arm,6 .cpu_arch = .arm,
...@@ -8,12 +8,15 @@ pub fn build(b: *std.build.Builder) !void {...@@ -8,12 +8,15 @@ pub fn build(b: *std.build.Builder) !void {
8 .explicit = &std.Target.arm.cpu.arm1176jz_s,8 .explicit = &std.Target.arm.cpu.arm1176jz_s,
9 },9 },
10 };10 };
11 const mode = b.standardReleaseOptions();11 const optimize = b.standardOptimizeOption(.{});
12 const kernel = b.addExecutable("kernel", "./main.zig");12 const kernel = b.addExecutable(.{
13 .name = "kernel",
14 .root_source_file = .{ .path = "./main.zig" },
15 .optimize = optimize,
16 .target = target,
17 });
13 kernel.addObjectFile("./boot.S");18 kernel.addObjectFile("./boot.S");
14 kernel.setLinkerScriptPath(.{ .path = "./linker.ld" });19 kernel.setLinkerScriptPath(.{ .path = "./linker.ld" });
15 kernel.setBuildMode(mode);
16 kernel.setTarget(target);
17 kernel.install();20 kernel.install();
1821
19 const test_step = b.step("test", "Test it");22 const test_step = b.step("test", "Test it");
test/standalone/issue_9812/build.zig+6-4
...@@ -1,9 +1,11 @@...@@ -1,9 +1,11 @@
1const std = @import("std");1const std = @import("std");
22
3pub fn build(b: *std.build.Builder) !void {3pub fn build(b: *std.Build) !void {
4 const mode = b.standardReleaseOptions();4 const optimize = b.standardOptimizeOption(.{});
5 const zip_add = b.addTest("main.zig");5 const zip_add = b.addTest(.{
6 zip_add.setBuildMode(mode);6 .root_source_file = .{ .path = "main.zig" },
7 .optimize = optimize,
8 });
7 zip_add.addCSourceFile("vendor/kuba-zip/zip.c", &[_][]const u8{9 zip_add.addCSourceFile("vendor/kuba-zip/zip.c", &[_][]const u8{
8 "-std=c99",10 "-std=c99",
9 "-fno-sanitize=undefined",11 "-fno-sanitize=undefined",
test/standalone/load_dynamic_library/build.zig+17-7
...@@ -1,13 +1,23 @@...@@ -1,13 +1,23 @@
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 opts = b.standardReleaseOptions();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 const lib = b.addSharedLibrary(.{
7 lib.setBuildMode(opts);8 .name = "add",
9 .root_source_file = .{ .path = "add.zig" },
10 .version = .{ .major = 1, .minor = 0 },
11 .optimize = optimize,
12 .target = target,
13 });
814
9 const main = b.addExecutable("main", "main.zig");15 const main = b.addExecutable(.{
10 main.setBuildMode(opts);16 .name = "main",
17 .root_source_file = .{ .path = "main.zig" },
18 .optimize = optimize,
19 .target = target,
20 });
1121
12 const run = main.run();22 const run = main.run();
13 run.addArtifactArg(lib);23 run.addArtifactArg(lib);
test/standalone/main_pkg_path/build.zig+5-3
...@@ -1,7 +1,9 @@...@@ -1,7 +1,9 @@
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 test_exe = b.addTest("a/test.zig");4 const test_exe = b.addTest(.{
5 .root_source_file = .{ .path = "a/test.zig" },
6 });
5 test_exe.setMainPkgPath(".");7 test_exe.setMainPkgPath(".");
68
7 const test_step = b.step("test", "Test the program");9 const test_step = b.step("test", "Test the program");
test/standalone/mix_c_files/build.zig+9-7
...@@ -1,9 +1,8 @@...@@ -1,9 +1,8 @@
1const std = @import("std");1const std = @import("std");
2const builtin = @import("builtin");2const builtin = @import("builtin");
3const Builder = std.build.Builder;
4const CrossTarget = std.zig.CrossTarget;3const CrossTarget = std.zig.CrossTarget;
54
6// TODO integrate this with the std.build executor API5// TODO integrate this with the std.Build executor API
7fn isRunnableTarget(t: CrossTarget) bool {6fn isRunnableTarget(t: CrossTarget) bool {
8 if (t.isNative()) return true;7 if (t.isNative()) return true;
98
...@@ -11,15 +10,18 @@ fn isRunnableTarget(t: CrossTarget) bool {...@@ -11,15 +10,18 @@ fn isRunnableTarget(t: CrossTarget) bool {
11 t.getCpuArch() == builtin.cpu.arch);10 t.getCpuArch() == builtin.cpu.arch);
12}11}
1312
14pub fn build(b: *Builder) void {13pub fn build(b: *std.Build) void {
15 const mode = b.standardReleaseOptions();14 const optimize = b.standardOptimizeOption(.{});
16 const target = b.standardTargetOptions(.{});15 const target = b.standardTargetOptions(.{});
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 });
19 exe.addCSourceFile("test.c", &[_][]const u8{"-std=c11"});23 exe.addCSourceFile("test.c", &[_][]const u8{"-std=c11"});
20 exe.setBuildMode(mode);
21 exe.linkLibC();24 exe.linkLibC();
22 exe.setTarget(target);
23 b.default_step.dependOn(&exe.step);25 b.default_step.dependOn(&exe.step);
2426
25 const test_step = b.step("test", "Test the program");27 const test_step = b.step("test", "Test the program");
test/standalone/mix_o_files/build.zig+14-4
...@@ -1,9 +1,19 @@...@@ -1,9 +1,19 @@
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 obj = b.addObject("base64", "base64.zig");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 });
7 exe.addCSourceFile("test.c", &[_][]const u8{"-std=c99"});17 exe.addCSourceFile("test.c", &[_][]const u8{"-std=c99"});
8 exe.addObject(obj);18 exe.addObject(obj);
9 exe.linkSystemLibrary("c");19 exe.linkSystemLibrary("c");
test/standalone/options/build.zig+7-5
...@@ -1,12 +1,14 @@...@@ -1,12 +1,14 @@
1const std = @import("std");1const std = @import("std");
22
3pub fn build(b: *std.build.Builder) void {3pub fn build(b: *std.Build) void {
4 const target = b.standardTargetOptions(.{});4 const target = b.standardTargetOptions(.{});
5 const mode = b.standardReleaseOptions();5 const optimize = b.standardOptimizeOption(.{});
66
7 const main = b.addTest("src/main.zig");7 const main = b.addTest(.{
8 main.setTarget(target);8 .root_source_file = .{ .path = "src/main.zig" },
9 main.setBuildMode(mode);9 .target = target,
10 .optimize = optimize,
11 });
1012
11 const options = b.addOptions();13 const options = b.addOptions();
12 main.addOptions("build_options", options);14 main.addOptions("build_options", options);
test/standalone/pie/build.zig+6-4
...@@ -1,8 +1,10 @@...@@ -1,8 +1,10 @@
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 main = b.addTest("main.zig");4 const main = b.addTest(.{
5 main.setBuildMode(b.standardReleaseOptions());5 .root_source_file = .{ .path = "main.zig" },
6 .optimize = b.standardOptimizeOption(.{}),
7 });
6 main.pie = true;8 main.pie = true;
79
8 const test_step = b.step("test", "Test the program");10 const test_step = b.step("test", "Test the program");
test/standalone/pkg_import/build.zig+9-8
...@@ -1,13 +1,14 @@...@@ -1,13 +1,14 @@
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 exe = b.addExecutable("test", "test.zig");4 const optimize = b.standardOptimizeOption(.{});
5 exe.addPackagePath("my_pkg", "pkg.zig");
65
7 // This is duplicated to test that you are allowed to call6 const exe = b.addExecutable(.{
8 // b.standardReleaseOptions() twice.7 .name = "test",
9 exe.setBuildMode(b.standardReleaseOptions());8 .root_source_file = .{ .path = "test.zig" },
10 exe.setBuildMode(b.standardReleaseOptions());9 .optimize = optimize,
10 });
11 exe.addPackagePath("my_pkg", "pkg.zig");
1112
12 const run = exe.run();13 const run = exe.run();
1314
test/standalone/shared_library/build.zig+15-6
...@@ -1,12 +1,21 @@...@@ -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(.{});
4 const target = b.standardTargetOptions(.{});5 const target = b.standardTargetOptions(.{});
5 const lib = b.addSharedLibrary("mathtest", "mathtest.zig", b.version(1, 0, 0));6 const lib = b.addSharedLibrary(.{
6 lib.setTarget(target);7 .name = "mathtest",
8 .root_source_file = .{ .path = "mathtest.zig" },
9 .version = .{ .major = 1, .minor = 0 },
10 .target = target,
11 .optimize = optimize,
12 });
713
8 const exe = b.addExecutable("test", null);14 const exe = b.addExecutable(.{
9 exe.setTarget(target);15 .name = "test",
16 .target = target,
17 .optimize = optimize,
18 });
10 exe.addCSourceFile("test.c", &[_][]const u8{"-std=c99"});19 exe.addCSourceFile("test.c", &[_][]const u8{"-std=c99"});
11 exe.linkLibrary(lib);20 exe.linkLibrary(lib);
12 exe.linkSystemLibrary("c");21 exe.linkSystemLibrary("c");
test/standalone/static_c_lib/build.zig+12-7
...@@ -1,15 +1,20 @@...@@ -1,15 +1,20 @@
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 mode = b.standardReleaseOptions();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 });
7 foo.addCSourceFile("foo.c", &[_][]const u8{});11 foo.addCSourceFile("foo.c", &[_][]const u8{});
8 foo.setBuildMode(mode);
9 foo.addIncludePath(".");12 foo.addIncludePath(".");
1013
11 const test_exe = b.addTest("foo.zig");14 const test_exe = b.addTest(.{
12 test_exe.setBuildMode(mode);15 .root_source_file = .{ .path = "foo.zig" },
16 .optimize = optimize,
17 });
13 test_exe.linkLibrary(foo);18 test_exe.linkLibrary(foo);
14 test_exe.addIncludePath(".");19 test_exe.addIncludePath(".");
1520
test/standalone/test_runner_path/build.zig+6-3
...@@ -1,7 +1,10 @@...@@ -1,7 +1,10 @@
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 test_exe = b.addTestExe("test", "test.zig");4 const test_exe = b.addTest(.{
5 .root_source_file = .{ .path = "test.zig" },
6 .kind = .test_exe,
7 });
5 test_exe.test_runner = "test_runner.zig";8 test_exe.test_runner = "test_runner.zig";
69
7 const test_run = test_exe.run();10 const test_run = test_exe.run();
test/standalone/use_alias/build.zig+6-4
...@@ -1,8 +1,10 @@...@@ -1,8 +1,10 @@
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 main = b.addTest("main.zig");4 const main = b.addTest(.{
5 main.setBuildMode(b.standardReleaseOptions());5 .root_source_file = .{ .path = "main.zig" },
6 .optimize = b.standardOptimizeOption(.{}),
7 });
6 main.addIncludePath(".");8 main.addIncludePath(".");
79
8 const test_step = b.step("test", "Test it");10 const test_step = b.step("test", "Test it");
test/standalone/windows_spawn/build.zig+14-7
...@@ -1,13 +1,20 @@...@@ -1,13 +1,20 @@
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 mode = b.standardReleaseOptions();4 const optimize = b.standardOptimizeOption(.{});
55
6 const hello = b.addExecutable("hello", "hello.zig");6 const hello = b.addExecutable(.{
7 hello.setBuildMode(mode);7 .name = "hello",
8 .root_source_file = .{ .path = "hello.zig" },
9 .optimize = optimize,
10 });
11
12 const main = b.addExecutable(.{
13 .name = "main",
14 .root_source_file = .{ .path = "main.zig" },
15 .optimize = optimize,
16 });
817
9 const main = b.addExecutable("main", "main.zig");
10 main.setBuildMode(mode);
11 const run = main.run();18 const run = main.run();
12 run.addArtifactArg(hello);19 run.addArtifactArg(hello);
1320
test/tests.zig+117-100
...@@ -1,17 +1,17 @@...@@ -1,17 +1,17 @@
1const std = @import("std");1const std = @import("std");
2const builtin = @import("builtin");2const builtin = @import("builtin");
3const debug = std.debug;3const debug = std.debug;
4const build = std.build;
5const CrossTarget = std.zig.CrossTarget;4const CrossTarget = std.zig.CrossTarget;
6const io = std.io;5const io = std.io;
7const fs = std.fs;6const fs = std.fs;
8const mem = std.mem;7const mem = std.mem;
9const fmt = std.fmt;8const fmt = std.fmt;
10const ArrayList = std.ArrayList;9const ArrayList = std.ArrayList;
11const Mode = std.builtin.Mode;10const OptimizeMode = std.builtin.OptimizeMode;
12const LibExeObjStep = build.LibExeObjStep;11const CompileStep = std.Build.CompileStep;
13const Allocator = mem.Allocator;12const Allocator = mem.Allocator;
14const ExecError = build.Builder.ExecError;13const ExecError = std.Build.ExecError;
14const Step = std.Build.Step;
1515
16// Cases16// Cases
17const compare_output = @import("compare_output.zig");17const compare_output = @import("compare_output.zig");
...@@ -30,7 +30,7 @@ pub const CompareOutputContext = @import("src/compare_output.zig").CompareOutput...@@ -30,7 +30,7 @@ pub const CompareOutputContext = @import("src/compare_output.zig").CompareOutput
3030
31const TestTarget = struct {31const TestTarget = struct {
32 target: CrossTarget = @as(CrossTarget, .{}),32 target: CrossTarget = @as(CrossTarget, .{}),
33 mode: std.builtin.Mode = .Debug,33 optimize_mode: std.builtin.OptimizeMode = .Debug,
34 link_libc: bool = false,34 link_libc: bool = false,
35 single_threaded: bool = false,35 single_threaded: bool = false,
36 disable_native: bool = false,36 disable_native: bool = false,
...@@ -423,38 +423,38 @@ const test_targets = blk: {...@@ -423,38 +423,38 @@ const test_targets = blk: {
423423
424 // Do the release tests last because they take a long time424 // Do the release tests last because they take a long time
425 .{425 .{
426 .mode = .ReleaseFast,426 .optimize_mode = .ReleaseFast,
427 },427 },
428 .{428 .{
429 .link_libc = true,429 .link_libc = true,
430 .mode = .ReleaseFast,430 .optimize_mode = .ReleaseFast,
431 },431 },
432 .{432 .{
433 .mode = .ReleaseFast,433 .optimize_mode = .ReleaseFast,
434 .single_threaded = true,434 .single_threaded = true,
435 },435 },
436436
437 .{437 .{
438 .mode = .ReleaseSafe,438 .optimize_mode = .ReleaseSafe,
439 },439 },
440 .{440 .{
441 .link_libc = true,441 .link_libc = true,
442 .mode = .ReleaseSafe,442 .optimize_mode = .ReleaseSafe,
443 },443 },
444 .{444 .{
445 .mode = .ReleaseSafe,445 .optimize_mode = .ReleaseSafe,
446 .single_threaded = true,446 .single_threaded = true,
447 },447 },
448448
449 .{449 .{
450 .mode = .ReleaseSmall,450 .optimize_mode = .ReleaseSmall,
451 },451 },
452 .{452 .{
453 .link_libc = true,453 .link_libc = true,
454 .mode = .ReleaseSmall,454 .optimize_mode = .ReleaseSmall,
455 },455 },
456 .{456 .{
457 .mode = .ReleaseSmall,457 .optimize_mode = .ReleaseSmall,
458 .single_threaded = true,458 .single_threaded = true,
459 },459 },
460 };460 };
...@@ -462,14 +462,14 @@ const test_targets = blk: {...@@ -462,14 +462,14 @@ const test_targets = blk: {
462462
463const max_stdout_size = 1 * 1024 * 1024; // 1 MB463const 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 {
466 const cases = b.allocator.create(CompareOutputContext) catch unreachable;466 const cases = b.allocator.create(CompareOutputContext) catch unreachable;
467 cases.* = CompareOutputContext{467 cases.* = CompareOutputContext{
468 .b = b,468 .b = b,
469 .step = b.step("test-compare-output", "Run the compare output tests"),469 .step = b.step("test-compare-output", "Run the compare output tests"),
470 .test_index = 0,470 .test_index = 0,
471 .test_filter = test_filter,471 .test_filter = test_filter,
472 .modes = modes,472 .optimize_modes = optimize_modes,
473 };473 };
474474
475 compare_output.addCases(cases);475 compare_output.addCases(cases);
...@@ -477,14 +477,14 @@ pub fn addCompareOutputTests(b: *build.Builder, test_filter: ?[]const u8, modes:...@@ -477,14 +477,14 @@ pub fn addCompareOutputTests(b: *build.Builder, test_filter: ?[]const u8, modes:
477 return cases.step;477 return cases.step;
478}478}
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 {
481 const cases = b.allocator.create(StackTracesContext) catch unreachable;481 const cases = b.allocator.create(StackTracesContext) catch unreachable;
482 cases.* = StackTracesContext{482 cases.* = StackTracesContext{
483 .b = b,483 .b = b,
484 .step = b.step("test-stack-traces", "Run the stack trace tests"),484 .step = b.step("test-stack-traces", "Run the stack trace tests"),
485 .test_index = 0,485 .test_index = 0,
486 .test_filter = test_filter,486 .test_filter = test_filter,
487 .modes = modes,487 .optimize_modes = optimize_modes,
488 };488 };
489489
490 stack_traces.addCases(cases);490 stack_traces.addCases(cases);
...@@ -493,9 +493,9 @@ pub fn addStackTraceTests(b: *build.Builder, test_filter: ?[]const u8, modes: []...@@ -493,9 +493,9 @@ pub fn addStackTraceTests(b: *build.Builder, test_filter: ?[]const u8, modes: []
493}493}
494494
495pub fn addStandaloneTests(495pub fn addStandaloneTests(
496 b: *build.Builder,496 b: *std.Build,
497 test_filter: ?[]const u8,497 test_filter: ?[]const u8,
498 modes: []const Mode,498 optimize_modes: []const OptimizeMode,
499 skip_non_native: bool,499 skip_non_native: bool,
500 enable_macos_sdk: bool,500 enable_macos_sdk: bool,
501 target: std.zig.CrossTarget,501 target: std.zig.CrossTarget,
...@@ -506,14 +506,14 @@ pub fn addStandaloneTests(...@@ -506,14 +506,14 @@ pub fn addStandaloneTests(
506 enable_wasmtime: bool,506 enable_wasmtime: bool,
507 enable_wine: bool,507 enable_wine: bool,
508 enable_symlinks_windows: bool,508 enable_symlinks_windows: bool,
509) *build.Step {509) *Step {
510 const cases = b.allocator.create(StandaloneContext) catch unreachable;510 const cases = b.allocator.create(StandaloneContext) catch unreachable;
511 cases.* = StandaloneContext{511 cases.* = StandaloneContext{
512 .b = b,512 .b = b,
513 .step = b.step("test-standalone", "Run the standalone tests"),513 .step = b.step("test-standalone", "Run the standalone tests"),
514 .test_index = 0,514 .test_index = 0,
515 .test_filter = test_filter,515 .test_filter = test_filter,
516 .modes = modes,516 .optimize_modes = optimize_modes,
517 .skip_non_native = skip_non_native,517 .skip_non_native = skip_non_native,
518 .enable_macos_sdk = enable_macos_sdk,518 .enable_macos_sdk = enable_macos_sdk,
519 .target = target,519 .target = target,
...@@ -532,20 +532,20 @@ pub fn addStandaloneTests(...@@ -532,20 +532,20 @@ pub fn addStandaloneTests(
532}532}
533533
534pub fn addLinkTests(534pub fn addLinkTests(
535 b: *build.Builder,535 b: *std.Build,
536 test_filter: ?[]const u8,536 test_filter: ?[]const u8,
537 modes: []const Mode,537 optimize_modes: []const OptimizeMode,
538 enable_macos_sdk: bool,538 enable_macos_sdk: bool,
539 omit_stage2: bool,539 omit_stage2: bool,
540 enable_symlinks_windows: bool,540 enable_symlinks_windows: bool,
541) *build.Step {541) *Step {
542 const cases = b.allocator.create(StandaloneContext) catch unreachable;542 const cases = b.allocator.create(StandaloneContext) catch unreachable;
543 cases.* = StandaloneContext{543 cases.* = StandaloneContext{
544 .b = b,544 .b = b,
545 .step = b.step("test-link", "Run the linker tests"),545 .step = b.step("test-link", "Run the linker tests"),
546 .test_index = 0,546 .test_index = 0,
547 .test_filter = test_filter,547 .test_filter = test_filter,
548 .modes = modes,548 .optimize_modes = optimize_modes,
549 .skip_non_native = true,549 .skip_non_native = true,
550 .enable_macos_sdk = enable_macos_sdk,550 .enable_macos_sdk = enable_macos_sdk,
551 .target = .{},551 .target = .{},
...@@ -556,12 +556,17 @@ pub fn addLinkTests(...@@ -556,12 +556,17 @@ pub fn addLinkTests(
556 return cases.step;556 return cases.step;
557}557}
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 {
560 _ = test_filter;560 _ = test_filter;
561 _ = modes;561 _ = optimize_modes;
562 const step = b.step("test-cli", "Test the command line interface");562 const step = b.step("test-cli", "Test the command line interface");
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 });
565 const run_cmd = exe.run();570 const run_cmd = exe.run();
566 run_cmd.addArgs(&[_][]const u8{571 run_cmd.addArgs(&[_][]const u8{
567 fs.realpathAlloc(b.allocator, b.zig_exe) catch unreachable,572 fs.realpathAlloc(b.allocator, b.zig_exe) catch unreachable,
...@@ -572,14 +577,14 @@ pub fn addCliTests(b: *build.Builder, test_filter: ?[]const u8, modes: []const M...@@ -572,14 +577,14 @@ pub fn addCliTests(b: *build.Builder, test_filter: ?[]const u8, modes: []const M
572 return step;577 return step;
573}578}
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 {
576 const cases = b.allocator.create(CompareOutputContext) catch unreachable;581 const cases = b.allocator.create(CompareOutputContext) catch unreachable;
577 cases.* = CompareOutputContext{582 cases.* = CompareOutputContext{
578 .b = b,583 .b = b,
579 .step = b.step("test-asm-link", "Run the assemble and link tests"),584 .step = b.step("test-asm-link", "Run the assemble and link tests"),
580 .test_index = 0,585 .test_index = 0,
581 .test_filter = test_filter,586 .test_filter = test_filter,
582 .modes = modes,587 .optimize_modes = optimize_modes,
583 };588 };
584589
585 assemble_and_link.addCases(cases);590 assemble_and_link.addCases(cases);
...@@ -587,7 +592,7 @@ pub fn addAssembleAndLinkTests(b: *build.Builder, test_filter: ?[]const u8, mode...@@ -587,7 +592,7 @@ pub fn addAssembleAndLinkTests(b: *build.Builder, test_filter: ?[]const u8, mode
587 return cases.step;592 return cases.step;
588}593}
589594
590pub fn addTranslateCTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step {595pub fn addTranslateCTests(b: *std.Build, test_filter: ?[]const u8) *Step {
591 const cases = b.allocator.create(TranslateCContext) catch unreachable;596 const cases = b.allocator.create(TranslateCContext) catch unreachable;
592 cases.* = TranslateCContext{597 cases.* = TranslateCContext{
593 .b = b,598 .b = b,
...@@ -602,10 +607,10 @@ pub fn addTranslateCTests(b: *build.Builder, test_filter: ?[]const u8) *build.St...@@ -602,10 +607,10 @@ pub fn addTranslateCTests(b: *build.Builder, test_filter: ?[]const u8) *build.St
602}607}
603608
604pub fn addRunTranslatedCTests(609pub fn addRunTranslatedCTests(
605 b: *build.Builder,610 b: *std.Build,
606 test_filter: ?[]const u8,611 test_filter: ?[]const u8,
607 target: std.zig.CrossTarget,612 target: std.zig.CrossTarget,
608) *build.Step {613) *Step {
609 const cases = b.allocator.create(RunTranslatedCContext) catch unreachable;614 const cases = b.allocator.create(RunTranslatedCContext) catch unreachable;
610 cases.* = .{615 cases.* = .{
611 .b = b,616 .b = b,
...@@ -620,7 +625,7 @@ pub fn addRunTranslatedCTests(...@@ -620,7 +625,7 @@ pub fn addRunTranslatedCTests(
620 return cases.step;625 return cases.step;
621}626}
622627
623pub fn addGenHTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step {628pub fn addGenHTests(b: *std.Build, test_filter: ?[]const u8) *Step {
624 const cases = b.allocator.create(GenHContext) catch unreachable;629 const cases = b.allocator.create(GenHContext) catch unreachable;
625 cases.* = GenHContext{630 cases.* = GenHContext{
626 .b = b,631 .b = b,
...@@ -635,18 +640,18 @@ pub fn addGenHTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step {...@@ -635,18 +640,18 @@ pub fn addGenHTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step {
635}640}
636641
637pub fn addPkgTests(642pub fn addPkgTests(
638 b: *build.Builder,643 b: *std.Build,
639 test_filter: ?[]const u8,644 test_filter: ?[]const u8,
640 root_src: []const u8,645 root_src: []const u8,
641 name: []const u8,646 name: []const u8,
642 desc: []const u8,647 desc: []const u8,
643 modes: []const Mode,648 optimize_modes: []const OptimizeMode,
644 skip_single_threaded: bool,649 skip_single_threaded: bool,
645 skip_non_native: bool,650 skip_non_native: bool,
646 skip_libc: bool,651 skip_libc: bool,
647 skip_stage1: bool,652 skip_stage1: bool,
648 skip_stage2: bool,653 skip_stage2: bool,
649) *build.Step {654) *Step {
650 const step = b.step(b.fmt("test-{s}", .{name}), desc);655 const step = b.step(b.fmt("test-{s}", .{name}), desc);
651656
652 for (test_targets) |test_target| {657 for (test_targets) |test_target| {
...@@ -677,8 +682,8 @@ pub fn addPkgTests(...@@ -677,8 +682,8 @@ pub fn addPkgTests(
677 else => if (skip_stage2) continue,682 else => if (skip_stage2) continue,
678 };683 };
679684
680 const want_this_mode = for (modes) |m| {685 const want_this_mode = for (optimize_modes) |m| {
681 if (m == test_target.mode) break true;686 if (m == test_target.optimize_mode) break true;
682 } else false;687 } else false;
683 if (!want_this_mode) continue;688 if (!want_this_mode) continue;
684689
...@@ -691,21 +696,23 @@ pub fn addPkgTests(...@@ -691,21 +696,23 @@ pub fn addPkgTests(
691696
692 const triple_prefix = test_target.target.zigTriple(b.allocator) catch unreachable;697 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 });
695 const single_threaded_txt = if (test_target.single_threaded) "single" else "multi";704 const single_threaded_txt = if (test_target.single_threaded) "single" else "multi";
696 const backend_txt = if (test_target.backend) |backend| @tagName(backend) else "default";705 const backend_txt = if (test_target.backend) |backend| @tagName(backend) else "default";
697 these_tests.setNamePrefix(b.fmt("{s}-{s}-{s}-{s}-{s}-{s} ", .{706 these_tests.setNamePrefix(b.fmt("{s}-{s}-{s}-{s}-{s}-{s} ", .{
698 name,707 name,
699 triple_prefix,708 triple_prefix,
700 @tagName(test_target.mode),709 @tagName(test_target.optimize_mode),
701 libc_prefix,710 libc_prefix,
702 single_threaded_txt,711 single_threaded_txt,
703 backend_txt,712 backend_txt,
704 }));713 }));
705 these_tests.single_threaded = test_target.single_threaded;714 these_tests.single_threaded = test_target.single_threaded;
706 these_tests.setFilter(test_filter);715 these_tests.setFilter(test_filter);
707 these_tests.setBuildMode(test_target.mode);
708 these_tests.setTarget(test_target.target);
709 if (test_target.link_libc) {716 if (test_target.link_libc) {
710 these_tests.linkSystemLibrary("c");717 these_tests.linkSystemLibrary("c");
711 }718 }
...@@ -735,13 +742,13 @@ pub fn addPkgTests(...@@ -735,13 +742,13 @@ pub fn addPkgTests(
735}742}
736743
737pub const StackTracesContext = struct {744pub const StackTracesContext = struct {
738 b: *build.Builder,745 b: *std.Build,
739 step: *build.Step,746 step: *Step,
740 test_index: usize,747 test_index: usize,
741 test_filter: ?[]const u8,748 test_filter: ?[]const u8,
742 modes: []const Mode,749 optimize_modes: []const OptimizeMode,
743750
744 const Expect = [@typeInfo(Mode).Enum.fields.len][]const u8;751 const Expect = [@typeInfo(OptimizeMode).Enum.fields.len][]const u8;
745752
746 pub fn addCase(self: *StackTracesContext, config: anytype) void {753 pub fn addCase(self: *StackTracesContext, config: anytype) void {
747 if (@hasField(@TypeOf(config), "exclude")) {754 if (@hasField(@TypeOf(config), "exclude")) {
...@@ -755,26 +762,26 @@ pub const StackTracesContext = struct {...@@ -755,26 +762,26 @@ pub const StackTracesContext = struct {
755 const exclude_os: []const std.Target.Os.Tag = &config.exclude_os;762 const exclude_os: []const std.Target.Os.Tag = &config.exclude_os;
756 for (exclude_os) |os| if (os == builtin.os.tag) return;763 for (exclude_os) |os| if (os == builtin.os.tag) return;
757 }764 }
758 for (self.modes) |mode| {765 for (self.optimize_modes) |optimize_mode| {
759 switch (mode) {766 switch (optimize_mode) {
760 .Debug => {767 .Debug => {
761 if (@hasField(@TypeOf(config), "Debug")) {768 if (@hasField(@TypeOf(config), "Debug")) {
762 self.addExpect(config.name, config.source, mode, config.Debug);769 self.addExpect(config.name, config.source, optimize_mode, config.Debug);
763 }770 }
764 },771 },
765 .ReleaseSafe => {772 .ReleaseSafe => {
766 if (@hasField(@TypeOf(config), "ReleaseSafe")) {773 if (@hasField(@TypeOf(config), "ReleaseSafe")) {
767 self.addExpect(config.name, config.source, mode, config.ReleaseSafe);774 self.addExpect(config.name, config.source, optimize_mode, config.ReleaseSafe);
768 }775 }
769 },776 },
770 .ReleaseFast => {777 .ReleaseFast => {
771 if (@hasField(@TypeOf(config), "ReleaseFast")) {778 if (@hasField(@TypeOf(config), "ReleaseFast")) {
772 self.addExpect(config.name, config.source, mode, config.ReleaseFast);779 self.addExpect(config.name, config.source, optimize_mode, config.ReleaseFast);
773 }780 }
774 },781 },
775 .ReleaseSmall => {782 .ReleaseSmall => {
776 if (@hasField(@TypeOf(config), "ReleaseSmall")) {783 if (@hasField(@TypeOf(config), "ReleaseSmall")) {
777 self.addExpect(config.name, config.source, mode, config.ReleaseSmall);784 self.addExpect(config.name, config.source, optimize_mode, config.ReleaseSmall);
778 }785 }
779 },786 },
780 }787 }
...@@ -785,7 +792,7 @@ pub const StackTracesContext = struct {...@@ -785,7 +792,7 @@ pub const StackTracesContext = struct {
785 self: *StackTracesContext,792 self: *StackTracesContext,
786 name: []const u8,793 name: []const u8,
787 source: []const u8,794 source: []const u8,
788 mode: Mode,795 optimize_mode: OptimizeMode,
789 mode_config: anytype,796 mode_config: anytype,
790 ) void {797 ) void {
791 if (@hasField(@TypeOf(mode_config), "exclude")) {798 if (@hasField(@TypeOf(mode_config), "exclude")) {
...@@ -803,7 +810,7 @@ pub const StackTracesContext = struct {...@@ -803,7 +810,7 @@ pub const StackTracesContext = struct {
803 const annotated_case_name = fmt.allocPrint(self.b.allocator, "{s} {s} ({s})", .{810 const annotated_case_name = fmt.allocPrint(self.b.allocator, "{s} {s} ({s})", .{
804 "stack-trace",811 "stack-trace",
805 name,812 name,
806 @tagName(mode),813 @tagName(optimize_mode),
807 }) catch unreachable;814 }) catch unreachable;
808 if (self.test_filter) |filter| {815 if (self.test_filter) |filter| {
809 if (mem.indexOf(u8, annotated_case_name, filter) == null) return;816 if (mem.indexOf(u8, annotated_case_name, filter) == null) return;
...@@ -812,14 +819,18 @@ pub const StackTracesContext = struct {...@@ -812,14 +819,18 @@ pub const StackTracesContext = struct {
812 const b = self.b;819 const b = self.b;
813 const src_basename = "source.zig";820 const src_basename = "source.zig";
814 const write_src = b.addWriteFile(src_basename, source);821 const write_src = b.addWriteFile(src_basename, source);
815 const exe = b.addExecutableSource("test", write_src.getFileSource(src_basename).?);822 const exe = b.addExecutable(.{
816 exe.setBuildMode(mode);823 .name = "test",
824 .root_source_file = write_src.getFileSource(src_basename).?,
825 .optimize = optimize_mode,
826 .target = .{},
827 });
817828
818 const run_and_compare = RunAndCompareStep.create(829 const run_and_compare = RunAndCompareStep.create(
819 self,830 self,
820 exe,831 exe,
821 annotated_case_name,832 annotated_case_name,
822 mode,833 optimize_mode,
823 mode_config.expect,834 mode_config.expect,
824 );835 );
825836
...@@ -829,29 +840,29 @@ pub const StackTracesContext = struct {...@@ -829,29 +840,29 @@ pub const StackTracesContext = struct {
829 const RunAndCompareStep = struct {840 const RunAndCompareStep = struct {
830 pub const base_id = .custom;841 pub const base_id = .custom;
831842
832 step: build.Step,843 step: Step,
833 context: *StackTracesContext,844 context: *StackTracesContext,
834 exe: *LibExeObjStep,845 exe: *CompileStep,
835 name: []const u8,846 name: []const u8,
836 mode: Mode,847 optimize_mode: OptimizeMode,
837 expect_output: []const u8,848 expect_output: []const u8,
838 test_index: usize,849 test_index: usize,
839850
840 pub fn create(851 pub fn create(
841 context: *StackTracesContext,852 context: *StackTracesContext,
842 exe: *LibExeObjStep,853 exe: *CompileStep,
843 name: []const u8,854 name: []const u8,
844 mode: Mode,855 optimize_mode: OptimizeMode,
845 expect_output: []const u8,856 expect_output: []const u8,
846 ) *RunAndCompareStep {857 ) *RunAndCompareStep {
847 const allocator = context.b.allocator;858 const allocator = context.b.allocator;
848 const ptr = allocator.create(RunAndCompareStep) catch unreachable;859 const ptr = allocator.create(RunAndCompareStep) catch unreachable;
849 ptr.* = RunAndCompareStep{860 ptr.* = RunAndCompareStep{
850 .step = build.Step.init(.custom, "StackTraceCompareOutputStep", allocator, make),861 .step = Step.init(.custom, "StackTraceCompareOutputStep", allocator, make),
851 .context = context,862 .context = context,
852 .exe = exe,863 .exe = exe,
853 .name = name,864 .name = name,
854 .mode = mode,865 .optimize_mode = optimize_mode,
855 .expect_output = expect_output,866 .expect_output = expect_output,
856 .test_index = context.test_index,867 .test_index = context.test_index,
857 };868 };
...@@ -860,7 +871,7 @@ pub const StackTracesContext = struct {...@@ -860,7 +871,7 @@ pub const StackTracesContext = struct {
860 return ptr;871 return ptr;
861 }872 }
862873
863 fn make(step: *build.Step) !void {874 fn make(step: *Step) !void {
864 const self = @fieldParentPtr(RunAndCompareStep, "step", step);875 const self = @fieldParentPtr(RunAndCompareStep, "step", step);
865 const b = self.context.b;876 const b = self.context.b;
866877
...@@ -932,7 +943,7 @@ pub const StackTracesContext = struct {...@@ -932,7 +943,7 @@ pub const StackTracesContext = struct {
932 // process result943 // process result
933 // - keep only basename of source file path944 // - keep only basename of source file path
934 // - replace address with symbolic string945 // - replace address with symbolic string
935 // - replace function name with symbolic string when mode != .Debug946 // - replace function name with symbolic string when optimize_mode != .Debug
936 // - skip empty lines947 // - skip empty lines
937 const got: []const u8 = got_result: {948 const got: []const u8 = got_result: {
938 var buf = ArrayList(u8).init(b.allocator);949 var buf = ArrayList(u8).init(b.allocator);
...@@ -968,7 +979,7 @@ pub const StackTracesContext = struct {...@@ -968,7 +979,7 @@ pub const StackTracesContext = struct {
968 // emit substituted line979 // emit substituted line
969 try buf.appendSlice(line[pos + 1 .. marks[2] + delims[2].len]);980 try buf.appendSlice(line[pos + 1 .. marks[2] + delims[2].len]);
970 try buf.appendSlice(" [address]");981 try buf.appendSlice(" [address]");
971 if (self.mode == .Debug) {982 if (self.optimize_mode == .Debug) {
972 // On certain platforms (windows) or possibly depending on how we choose to link main983 // On certain platforms (windows) or possibly depending on how we choose to link main
973 // the object file extension may be present so we simply strip any extension.984 // the object file extension may be present so we simply strip any extension.
974 if (mem.indexOfScalar(u8, line[marks[4]..marks[5]], '.')) |idot| {985 if (mem.indexOfScalar(u8, line[marks[4]..marks[5]], '.')) |idot| {
...@@ -1003,11 +1014,11 @@ pub const StackTracesContext = struct {...@@ -1003,11 +1014,11 @@ pub const StackTracesContext = struct {
1003};1014};
10041015
1005pub const StandaloneContext = struct {1016pub const StandaloneContext = struct {
1006 b: *build.Builder,1017 b: *std.Build,
1007 step: *build.Step,1018 step: *Step,
1008 test_index: usize,1019 test_index: usize,
1009 test_filter: ?[]const u8,1020 test_filter: ?[]const u8,
1010 modes: []const Mode,1021 optimize_modes: []const OptimizeMode,
1011 skip_non_native: bool,1022 skip_non_native: bool,
1012 enable_macos_sdk: bool,1023 enable_macos_sdk: bool,
1013 target: std.zig.CrossTarget,1024 target: std.zig.CrossTarget,
...@@ -1087,13 +1098,13 @@ pub const StandaloneContext = struct {...@@ -1087,13 +1098,13 @@ pub const StandaloneContext = struct {
1087 }1098 }
1088 }1099 }
10891100
1090 const modes = if (features.build_modes) self.modes else &[1]Mode{.Debug};1101 const optimize_modes = if (features.build_modes) self.optimize_modes else &[1]OptimizeMode{.Debug};
1091 for (modes) |mode| {1102 for (optimize_modes) |optimize_mode| {
1092 const arg = switch (mode) {1103 const arg = switch (optimize_mode) {
1093 .Debug => "",1104 .Debug => "",
1094 .ReleaseFast => "-Drelease-fast",1105 .ReleaseFast => "-Doptimize=ReleaseFast",
1095 .ReleaseSafe => "-Drelease-safe",1106 .ReleaseSafe => "-Doptimize=ReleaseSafe",
1096 .ReleaseSmall => "-Drelease-small",1107 .ReleaseSmall => "-Doptimize=ReleaseSmall",
1097 };1108 };
1098 const zig_args_base_len = zig_args.items.len;1109 const zig_args_base_len = zig_args.items.len;
1099 if (arg.len > 0)1110 if (arg.len > 0)
...@@ -1101,7 +1112,7 @@ pub const StandaloneContext = struct {...@@ -1101,7 +1112,7 @@ pub const StandaloneContext = struct {
1101 defer zig_args.resize(zig_args_base_len) catch unreachable;1112 defer zig_args.resize(zig_args_base_len) catch unreachable;
11021113
1103 const run_cmd = b.addSystemCommand(zig_args.items);1114 const run_cmd = b.addSystemCommand(zig_args.items);
1104 const log_step = b.addLog("PASS {s} ({s})", .{ annotated_case_name, @tagName(mode) });1115 const log_step = b.addLog("PASS {s} ({s})", .{ annotated_case_name, @tagName(optimize_mode) });
1105 log_step.step.dependOn(&run_cmd.step);1116 log_step.step.dependOn(&run_cmd.step);
11061117
1107 self.step.dependOn(&log_step.step);1118 self.step.dependOn(&log_step.step);
...@@ -1111,17 +1122,21 @@ pub const StandaloneContext = struct {...@@ -1111,17 +1122,21 @@ pub const StandaloneContext = struct {
1111 pub fn addAllArgs(self: *StandaloneContext, root_src: []const u8, link_libc: bool) void {1122 pub fn addAllArgs(self: *StandaloneContext, root_src: []const u8, link_libc: bool) void {
1112 const b = self.b;1123 const b = self.b;
11131124
1114 for (self.modes) |mode| {1125 for (self.optimize_modes) |optimize| {
1115 const annotated_case_name = fmt.allocPrint(self.b.allocator, "build {s} ({s})", .{1126 const annotated_case_name = fmt.allocPrint(self.b.allocator, "build {s} ({s})", .{
1116 root_src,1127 root_src,
1117 @tagName(mode),1128 @tagName(optimize),
1118 }) catch unreachable;1129 }) catch unreachable;
1119 if (self.test_filter) |filter| {1130 if (self.test_filter) |filter| {
1120 if (mem.indexOf(u8, annotated_case_name, filter) == null) continue;1131 if (mem.indexOf(u8, annotated_case_name, filter) == null) continue;
1121 }1132 }
11221133
1123 const exe = b.addExecutable("test", root_src);1134 const exe = b.addExecutable(.{
1124 exe.setBuildMode(mode);1135 .name = "test",
1136 .root_source_file = .{ .path = root_src },
1137 .optimize = optimize,
1138 .target = .{},
1139 });
1125 if (link_libc) {1140 if (link_libc) {
1126 exe.linkSystemLibrary("c");1141 exe.linkSystemLibrary("c");
1127 }1142 }
...@@ -1135,8 +1150,8 @@ pub const StandaloneContext = struct {...@@ -1135,8 +1150,8 @@ pub const StandaloneContext = struct {
1135};1150};
11361151
1137pub const GenHContext = struct {1152pub const GenHContext = struct {
1138 b: *build.Builder,1153 b: *std.Build,
1139 step: *build.Step,1154 step: *Step,
1140 test_index: usize,1155 test_index: usize,
1141 test_filter: ?[]const u8,1156 test_filter: ?[]const u8,
11421157
...@@ -1163,23 +1178,23 @@ pub const GenHContext = struct {...@@ -1163,23 +1178,23 @@ pub const GenHContext = struct {
1163 };1178 };
11641179
1165 const GenHCmpOutputStep = struct {1180 const GenHCmpOutputStep = struct {
1166 step: build.Step,1181 step: Step,
1167 context: *GenHContext,1182 context: *GenHContext,
1168 obj: *LibExeObjStep,1183 obj: *CompileStep,
1169 name: []const u8,1184 name: []const u8,
1170 test_index: usize,1185 test_index: usize,
1171 case: *const TestCase,1186 case: *const TestCase,
11721187
1173 pub fn create(1188 pub fn create(
1174 context: *GenHContext,1189 context: *GenHContext,
1175 obj: *LibExeObjStep,1190 obj: *CompileStep,
1176 name: []const u8,1191 name: []const u8,
1177 case: *const TestCase,1192 case: *const TestCase,
1178 ) *GenHCmpOutputStep {1193 ) *GenHCmpOutputStep {
1179 const allocator = context.b.allocator;1194 const allocator = context.b.allocator;
1180 const ptr = allocator.create(GenHCmpOutputStep) catch unreachable;1195 const ptr = allocator.create(GenHCmpOutputStep) catch unreachable;
1181 ptr.* = GenHCmpOutputStep{1196 ptr.* = GenHCmpOutputStep{
1182 .step = build.Step.init(.Custom, "ParseCCmpOutput", allocator, make),1197 .step = Step.init(.Custom, "ParseCCmpOutput", allocator, make),
1183 .context = context,1198 .context = context,
1184 .obj = obj,1199 .obj = obj,
1185 .name = name,1200 .name = name,
...@@ -1191,7 +1206,7 @@ pub const GenHContext = struct {...@@ -1191,7 +1206,7 @@ pub const GenHContext = struct {
1191 return ptr;1206 return ptr;
1192 }1207 }
11931208
1194 fn make(step: *build.Step) !void {1209 fn make(step: *Step) !void {
1195 const self = @fieldParentPtr(GenHCmpOutputStep, "step", step);1210 const self = @fieldParentPtr(GenHCmpOutputStep, "step", step);
1196 const b = self.context.b;1211 const b = self.context.b;
11971212
...@@ -1247,8 +1262,8 @@ pub const GenHContext = struct {...@@ -1247,8 +1262,8 @@ pub const GenHContext = struct {
1247 pub fn addCase(self: *GenHContext, case: *const TestCase) void {1262 pub fn addCase(self: *GenHContext, case: *const TestCase) void {
1248 const b = self.b;1263 const b = self.b;
12491264
1250 const mode = std.builtin.Mode.Debug;1265 const optimize_mode = std.builtin.OptimizeMode.Debug;
1251 const annotated_case_name = fmt.allocPrint(self.b.allocator, "gen-h {s} ({s})", .{ case.name, @tagName(mode) }) catch unreachable;1266 const annotated_case_name = fmt.allocPrint(self.b.allocator, "gen-h {s} ({s})", .{ case.name, @tagName(optimize_mode) }) catch unreachable;
1252 if (self.test_filter) |filter| {1267 if (self.test_filter) |filter| {
1253 if (mem.indexOf(u8, annotated_case_name, filter) == null) return;1268 if (mem.indexOf(u8, annotated_case_name, filter) == null) return;
1254 }1269 }
...@@ -1259,7 +1274,7 @@ pub const GenHContext = struct {...@@ -1259,7 +1274,7 @@ pub const GenHContext = struct {
1259 }1274 }
12601275
1261 const obj = b.addObjectFromWriteFileStep("test", write_src, case.sources.items[0].filename);1276 const obj = b.addObjectFromWriteFileStep("test", write_src, case.sources.items[0].filename);
1262 obj.setBuildMode(mode);1277 obj.setBuildMode(optimize_mode);
12631278
1264 const cmp_h = GenHCmpOutputStep.create(self, obj, annotated_case_name, case);1279 const cmp_h = GenHCmpOutputStep.create(self, obj, annotated_case_name, case);
12651280
...@@ -1333,17 +1348,20 @@ const c_abi_targets = [_]CrossTarget{...@@ -1333,17 +1348,20 @@ const c_abi_targets = [_]CrossTarget{
1333 },1348 },
1334};1349};
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 {
1337 const step = b.step("test-c-abi", "Run the C ABI tests");1352 const step = b.step("test-c-abi", "Run the C ABI tests");
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| {
1342 if (skip_non_native and !c_abi_target.isNative())1357 if (skip_non_native and !c_abi_target.isNative())
1343 continue;1358 continue;
13441359
1345 const test_step = b.addTest("test/c_abi/main.zig");1360 const test_step = b.addTest(.{
1346 test_step.setTarget(c_abi_target);1361 .root_source_file = .{ .path = "test/c_abi/main.zig" },
1362 .optimize = optimize_mode,
1363 .target = c_abi_target,
1364 });
1347 if (c_abi_target.abi != null and c_abi_target.abi.?.isMusl()) {1365 if (c_abi_target.abi != null and c_abi_target.abi.?.isMusl()) {
1348 // TODO NativeTargetInfo insists on dynamically linking musl1366 // TODO NativeTargetInfo insists on dynamically linking musl
1349 // for some reason?1367 // for some reason?
...@@ -1351,7 +1369,6 @@ pub fn addCAbiTests(b: *build.Builder, skip_non_native: bool, skip_release: bool...@@ -1351,7 +1369,6 @@ pub fn addCAbiTests(b: *build.Builder, skip_non_native: bool, skip_release: bool
1351 }1369 }
1352 test_step.linkLibC();1370 test_step.linkLibC();
1353 test_step.addCSourceFile("test/c_abi/cfuncs.c", &.{"-std=c99"});1371 test_step.addCSourceFile("test/c_abi/cfuncs.c", &.{"-std=c99"});
1354 test_step.setBuildMode(mode);
13551372
1356 if (c_abi_target.isWindows() and (c_abi_target.getCpuArch() == .x86 or builtin.target.os.tag == .linux)) {1373 if (c_abi_target.isWindows() and (c_abi_target.getCpuArch() == .x86 or builtin.target.os.tag == .linux)) {
1357 // LTO currently incorrectly strips stdcall name-mangled functions1374 // LTO currently incorrectly strips stdcall name-mangled functions
...@@ -1363,7 +1380,7 @@ pub fn addCAbiTests(b: *build.Builder, skip_non_native: bool, skip_release: bool...@@ -1363,7 +1380,7 @@ pub fn addCAbiTests(b: *build.Builder, skip_non_native: bool, skip_release: bool
1363 test_step.setNamePrefix(b.fmt("{s}-{s}-{s} ", .{1380 test_step.setNamePrefix(b.fmt("{s}-{s}-{s} ", .{
1364 "test-c-abi",1381 "test-c-abi",
1365 triple_prefix,1382 triple_prefix,
1366 @tagName(mode),1383 @tagName(optimize_mode),
1367 }));1384 }));
13681385
1369 step.dependOn(&test_step.step);1386 step.dependOn(&test_step.step);
test/translate_c.zig+16
...@@ -3900,4 +3900,20 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -3900,4 +3900,20 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
3900 \\pub const ZERO = @as(c_int, 0);3900 \\pub const ZERO = @as(c_int, 0);
3901 \\pub const WORLD = @as(c_int, 0o0000123);3901 \\pub const WORLD = @as(c_int, 0o0000123);
3902 });3902 });
3903
3904 cases.add("Assign expression from bool to int",
3905 \\void foo(void) {
3906 \\ int a;
3907 \\ if (a = 1 > 0) {}
3908 \\}
3909 , &[_][]const u8{
3910 \\pub export fn foo() void {
3911 \\ var a: c_int = undefined;
3912 \\ if ((blk: {
3913 \\ const tmp = @boolToInt(@as(c_int, 1) > @as(c_int, 0));
3914 \\ a = tmp;
3915 \\ break :blk tmp;
3916 \\ }) != 0) {}
3917 \\}
3918 });
3903}3919}