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

Merge branch 'master' into lzma


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

CMakeLists.txt+1-1
...@@ -513,7 +513,7 @@ set(ZIG_STAGE2_SOURCES...@@ -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"
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"});
...@@ -151,17 +157,15 @@ pub fn build(b: *Builder) !void {...@@ -151,17 +157,15 @@ pub fn build(b: *Builder) !void {
151157
152 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: {158 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: {
153 if (strip == true) break :blk @as(u32, 0);159 if (strip == true) break :blk @as(u32, 0);
154 if (mode != .Debug) break :blk 0;160 if (optimize != .Debug) break :blk 0;
155 break :blk 4;161 break :blk 4;
156 };162 };
157163
158 const exe = addCompilerStep(b);164 const exe = addCompilerStep(b, optimize, target);
159 exe.strip = strip;165 exe.strip = strip;
160 exe.sanitize_thread = sanitize_thread;166 exe.sanitize_thread = sanitize_thread;
161 exe.build_id = b.option(bool, "build-id", "Include a build id note") orelse false;167 exe.build_id = b.option(bool, "build-id", "Include a build id note") orelse false;
162 exe.install();168 exe.install();
163 exe.setBuildMode(mode);
164 exe.setTarget(target);
165169
166 const compile_step = b.step("compile", "Build the self-hosted compiler");170 const compile_step = b.step("compile", "Build the self-hosted compiler");
167 compile_step.dependOn(&exe.step);171 compile_step.dependOn(&exe.step);
...@@ -197,7 +201,7 @@ pub fn build(b: *Builder) !void {...@@ -197,7 +201,7 @@ pub fn build(b: *Builder) !void {
197 test_cases.linkLibC();201 test_cases.linkLibC();
198 }202 }
199203
200 const is_debug = mode == .Debug;204 const is_debug = optimize == .Debug;
201 const enable_logging = b.option(bool, "log", "Enable debug logging with --debug-log") orelse is_debug;205 const enable_logging = b.option(bool, "log", "Enable debug logging with --debug-log") orelse is_debug;
202 const enable_link_snapshots = b.option(bool, "link-snapshot", "Whether to enable linker state snapshots") orelse false;206 const enable_link_snapshots = b.option(bool, "link-snapshot", "Whether to enable linker state snapshots") orelse false;
203207
...@@ -362,25 +366,25 @@ pub fn build(b: *Builder) !void {...@@ -362,25 +366,25 @@ pub fn build(b: *Builder) !void {
362 test_step.dependOn(test_cases_step);366 test_step.dependOn(test_cases_step);
363 }367 }
364368
365 var chosen_modes: [4]builtin.Mode = undefined;369 var chosen_opt_modes_buf: [4]builtin.Mode = undefined;
366 var chosen_mode_index: usize = 0;370 var chosen_mode_index: usize = 0;
367 if (!skip_debug) {371 if (!skip_debug) {
368 chosen_modes[chosen_mode_index] = builtin.Mode.Debug;372 chosen_opt_modes_buf[chosen_mode_index] = builtin.Mode.Debug;
369 chosen_mode_index += 1;373 chosen_mode_index += 1;
370 }374 }
371 if (!skip_release_safe) {375 if (!skip_release_safe) {
372 chosen_modes[chosen_mode_index] = builtin.Mode.ReleaseSafe;376 chosen_opt_modes_buf[chosen_mode_index] = builtin.Mode.ReleaseSafe;
373 chosen_mode_index += 1;377 chosen_mode_index += 1;
374 }378 }
375 if (!skip_release_fast) {379 if (!skip_release_fast) {
376 chosen_modes[chosen_mode_index] = builtin.Mode.ReleaseFast;380 chosen_opt_modes_buf[chosen_mode_index] = builtin.Mode.ReleaseFast;
377 chosen_mode_index += 1;381 chosen_mode_index += 1;
378 }382 }
379 if (!skip_release_small) {383 if (!skip_release_small) {
380 chosen_modes[chosen_mode_index] = builtin.Mode.ReleaseSmall;384 chosen_opt_modes_buf[chosen_mode_index] = builtin.Mode.ReleaseSmall;
381 chosen_mode_index += 1;385 chosen_mode_index += 1;
382 }386 }
383 const modes = chosen_modes[0..chosen_mode_index];387 const optimization_modes = chosen_opt_modes_buf[0..chosen_mode_index];
384388
385 // run stage1 `zig fmt` on this build.zig file just to make sure it works389 // run stage1 `zig fmt` on this build.zig file just to make sure it works
386 test_step.dependOn(&fmt_build_zig.step);390 test_step.dependOn(&fmt_build_zig.step);
...@@ -393,7 +397,7 @@ pub fn build(b: *Builder) !void {...@@ -393,7 +397,7 @@ pub fn build(b: *Builder) !void {
393 "test/behavior.zig",397 "test/behavior.zig",
394 "behavior",398 "behavior",
395 "Run the behavior tests",399 "Run the behavior tests",
396 modes,400 optimization_modes,
397 skip_single_threaded,401 skip_single_threaded,
398 skip_non_native,402 skip_non_native,
399 skip_libc,403 skip_libc,
...@@ -407,7 +411,7 @@ pub fn build(b: *Builder) !void {...@@ -407,7 +411,7 @@ pub fn build(b: *Builder) !void {
407 "lib/compiler_rt.zig",411 "lib/compiler_rt.zig",
408 "compiler-rt",412 "compiler-rt",
409 "Run the compiler_rt tests",413 "Run the compiler_rt tests",
410 modes,414 optimization_modes,
411 true, // skip_single_threaded415 true, // skip_single_threaded
412 skip_non_native,416 skip_non_native,
413 true, // skip_libc417 true, // skip_libc
...@@ -421,7 +425,7 @@ pub fn build(b: *Builder) !void {...@@ -421,7 +425,7 @@ pub fn build(b: *Builder) !void {
421 "lib/c.zig",425 "lib/c.zig",
422 "universal-libc",426 "universal-libc",
423 "Run the universal libc tests",427 "Run the universal libc tests",
424 modes,428 optimization_modes,
425 true, // skip_single_threaded429 true, // skip_single_threaded
426 skip_non_native,430 skip_non_native,
427 true, // skip_libc431 true, // skip_libc
...@@ -429,11 +433,11 @@ pub fn build(b: *Builder) !void {...@@ -429,11 +433,11 @@ pub fn build(b: *Builder) !void {
429 skip_stage2_tests or true, // TODO get these all passing433 skip_stage2_tests or true, // TODO get these all passing
430 ));434 ));
431435
432 test_step.dependOn(tests.addCompareOutputTests(b, test_filter, modes));436 test_step.dependOn(tests.addCompareOutputTests(b, test_filter, optimization_modes));
433 test_step.dependOn(tests.addStandaloneTests(437 test_step.dependOn(tests.addStandaloneTests(
434 b,438 b,
435 test_filter,439 test_filter,
436 modes,440 optimization_modes,
437 skip_non_native,441 skip_non_native,
438 enable_macos_sdk,442 enable_macos_sdk,
439 target,443 target,
...@@ -446,10 +450,10 @@ pub fn build(b: *Builder) !void {...@@ -446,10 +450,10 @@ pub fn build(b: *Builder) !void {
446 enable_symlinks_windows,450 enable_symlinks_windows,
447 ));451 ));
448 test_step.dependOn(tests.addCAbiTests(b, skip_non_native, skip_release));452 test_step.dependOn(tests.addCAbiTests(b, skip_non_native, skip_release));
449 test_step.dependOn(tests.addLinkTests(b, test_filter, modes, enable_macos_sdk, skip_stage2_tests, enable_symlinks_windows));453 test_step.dependOn(tests.addLinkTests(b, test_filter, optimization_modes, enable_macos_sdk, skip_stage2_tests, enable_symlinks_windows));
450 test_step.dependOn(tests.addStackTraceTests(b, test_filter, modes));454 test_step.dependOn(tests.addStackTraceTests(b, test_filter, optimization_modes));
451 test_step.dependOn(tests.addCliTests(b, test_filter, modes));455 test_step.dependOn(tests.addCliTests(b, test_filter, optimization_modes));
452 test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, modes));456 test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, optimization_modes));
453 test_step.dependOn(tests.addTranslateCTests(b, test_filter));457 test_step.dependOn(tests.addTranslateCTests(b, test_filter));
454 if (!skip_run_translated_c) {458 if (!skip_run_translated_c) {
455 test_step.dependOn(tests.addRunTranslatedCTests(b, test_filter, target));459 test_step.dependOn(tests.addRunTranslatedCTests(b, test_filter, target));
...@@ -463,7 +467,7 @@ pub fn build(b: *Builder) !void {...@@ -463,7 +467,7 @@ pub fn build(b: *Builder) !void {
463 "lib/std/std.zig",467 "lib/std/std.zig",
464 "std",468 "std",
465 "Run the standard library tests",469 "Run the standard library tests",
466 modes,470 optimization_modes,
467 skip_single_threaded,471 skip_single_threaded,
468 skip_non_native,472 skip_non_native,
469 skip_libc,473 skip_libc,
...@@ -474,7 +478,7 @@ pub fn build(b: *Builder) !void {...@@ -474,7 +478,7 @@ pub fn build(b: *Builder) !void {
474 try addWasiUpdateStep(b, version);478 try addWasiUpdateStep(b, version);
475}479}
476480
477fn addWasiUpdateStep(b: *Builder, version: [:0]const u8) !void {481fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void {
478 const semver = try std.SemanticVersion.parse(version);482 const semver = try std.SemanticVersion.parse(version);
479483
480 var target: std.zig.CrossTarget = .{484 var target: std.zig.CrossTarget = .{
...@@ -483,9 +487,7 @@ fn addWasiUpdateStep(b: *Builder, version: [:0]const u8) !void {...@@ -483,9 +487,7 @@ fn addWasiUpdateStep(b: *Builder, version: [:0]const u8) !void {
483 };487 };
484 target.cpu_features_add.addFeature(@enumToInt(std.Target.wasm.Feature.bulk_memory));488 target.cpu_features_add.addFeature(@enumToInt(std.Target.wasm.Feature.bulk_memory));
485489
486 const exe = addCompilerStep(b);490 const exe = addCompilerStep(b, .ReleaseSmall, target);
487 exe.setBuildMode(.ReleaseSmall);
488 exe.setTarget(target);
489491
490 const exe_options = b.addOptions();492 const exe_options = b.addOptions();
491 exe.addOptions("build_options", exe_options);493 exe.addOptions("build_options", exe_options);
...@@ -512,8 +514,17 @@ fn addWasiUpdateStep(b: *Builder, version: [:0]const u8) !void {...@@ -512,8 +514,17 @@ fn addWasiUpdateStep(b: *Builder, version: [:0]const u8) !void {
512 update_zig1_step.dependOn(&run_opt.step);514 update_zig1_step.dependOn(&run_opt.step);
513}515}
514516
515fn addCompilerStep(b: *Builder) *std.build.LibExeObjStep {517fn addCompilerStep(
516 const exe = b.addExecutable("zig", "src/main.zig");518 b: *std.Build,
519 optimize: std.builtin.OptimizeMode,
520 target: std.zig.CrossTarget,
521) *std.Build.CompileStep {
522 const exe = b.addExecutable(.{
523 .name = "zig",
524 .root_source_file = .{ .path = "src/main.zig" },
525 .target = target,
526 .optimize = optimize,
527 });
517 exe.stack_size = stack_size;528 exe.stack_size = stack_size;
518 return exe;529 return exe;
519}530}
...@@ -533,9 +544,9 @@ const exe_cflags = [_][]const u8{...@@ -533,9 +544,9 @@ const exe_cflags = [_][]const u8{
533};544};
534545
535fn addCmakeCfgOptionsToExe(546fn addCmakeCfgOptionsToExe(
536 b: *Builder,547 b: *std.Build,
537 cfg: CMakeConfig,548 cfg: CMakeConfig,
538 exe: *std.build.LibExeObjStep,549 exe: *std.Build.CompileStep,
539 use_zig_libcxx: bool,550 use_zig_libcxx: bool,
540) !void {551) !void {
541 if (exe.target.isDarwin()) {552 if (exe.target.isDarwin()) {
...@@ -614,7 +625,7 @@ fn addCmakeCfgOptionsToExe(...@@ -614,7 +625,7 @@ fn addCmakeCfgOptionsToExe(
614 }625 }
615}626}
616627
617fn addStaticLlvmOptionsToExe(exe: *std.build.LibExeObjStep) !void {628fn addStaticLlvmOptionsToExe(exe: *std.Build.CompileStep) !void {
618 // Adds the Zig C++ sources which both stage1 and stage2 need.629 // Adds the Zig C++ sources which both stage1 and stage2 need.
619 //630 //
620 // We need this because otherwise zig_clang_cc1_main.cpp ends up pulling631 // We need this because otherwise zig_clang_cc1_main.cpp ends up pulling
...@@ -651,9 +662,9 @@ fn addStaticLlvmOptionsToExe(exe: *std.build.LibExeObjStep) !void {...@@ -651,9 +662,9 @@ fn addStaticLlvmOptionsToExe(exe: *std.build.LibExeObjStep) !void {
651}662}
652663
653fn addCxxKnownPath(664fn addCxxKnownPath(
654 b: *Builder,665 b: *std.Build,
655 ctx: CMakeConfig,666 ctx: CMakeConfig,
656 exe: *std.build.LibExeObjStep,667 exe: *std.Build.CompileStep,
657 objname: []const u8,668 objname: []const u8,
658 errtxt: ?[]const u8,669 errtxt: ?[]const u8,
659 need_cpp_includes: bool,670 need_cpp_includes: bool,
...@@ -686,7 +697,7 @@ fn addCxxKnownPath(...@@ -686,7 +697,7 @@ fn addCxxKnownPath(
686 }697 }
687}698}
688699
689fn addCMakeLibraryList(exe: *std.build.LibExeObjStep, list: []const u8) void {700fn addCMakeLibraryList(exe: *std.Build.CompileStep, list: []const u8) void {
690 var it = mem.tokenize(u8, list, ";");701 var it = mem.tokenize(u8, list, ";");
691 while (it.next()) |lib| {702 while (it.next()) |lib| {
692 if (mem.startsWith(u8, lib, "-l")) {703 if (mem.startsWith(u8, lib, "-l")) {
...@@ -700,7 +711,7 @@ fn addCMakeLibraryList(exe: *std.build.LibExeObjStep, list: []const u8) void {...@@ -700,7 +711,7 @@ fn addCMakeLibraryList(exe: *std.build.LibExeObjStep, list: []const u8) void {
700}711}
701712
702const CMakeConfig = struct {713const CMakeConfig = struct {
703 llvm_linkage: std.build.LibExeObjStep.Linkage,714 llvm_linkage: std.Build.CompileStep.Linkage,
704 cmake_binary_dir: []const u8,715 cmake_binary_dir: []const u8,
705 cmake_prefix_path: []const u8,716 cmake_prefix_path: []const u8,
706 cmake_static_library_prefix: []const u8,717 cmake_static_library_prefix: []const u8,
...@@ -717,7 +728,7 @@ const CMakeConfig = struct {...@@ -717,7 +728,7 @@ const CMakeConfig = struct {
717728
718const max_config_h_bytes = 1 * 1024 * 1024;729const max_config_h_bytes = 1 * 1024 * 1024;
719730
720fn findConfigH(b: *Builder, config_h_path_option: ?[]const u8) ?[]const u8 {731fn findConfigH(b: *std.Build, config_h_path_option: ?[]const u8) ?[]const u8 {
721 if (config_h_path_option) |path| {732 if (config_h_path_option) |path| {
722 var config_h_or_err = fs.cwd().openFile(path, .{});733 var config_h_or_err = fs.cwd().openFile(path, .{});
723 if (config_h_or_err) |*file| {734 if (config_h_or_err) |*file| {
...@@ -763,7 +774,7 @@ fn findConfigH(b: *Builder, config_h_path_option: ?[]const u8) ?[]const u8 {...@@ -763,7 +774,7 @@ fn findConfigH(b: *Builder, config_h_path_option: ?[]const u8) ?[]const u8 {
763 } else unreachable; // TODO should not need `else unreachable`.774 } else unreachable; // TODO should not need `else unreachable`.
764}775}
765776
766fn parseConfigH(b: *Builder, config_h_text: []const u8) ?CMakeConfig {777fn parseConfigH(b: *std.Build, config_h_text: []const u8) ?CMakeConfig {
767 var ctx: CMakeConfig = .{778 var ctx: CMakeConfig = .{
768 .llvm_linkage = undefined,779 .llvm_linkage = undefined,
769 .cmake_binary_dir = undefined,780 .cmake_binary_dir = undefined,
...@@ -852,7 +863,7 @@ fn parseConfigH(b: *Builder, config_h_text: []const u8) ?CMakeConfig {...@@ -852,7 +863,7 @@ fn parseConfigH(b: *Builder, config_h_text: []const u8) ?CMakeConfig {
852 return ctx;863 return ctx;
853}864}
854865
855fn toNativePathSep(b: *Builder, s: []const u8) []u8 {866fn toNativePathSep(b: *std.Build, s: []const u8) []u8 {
856 const duplicated = b.allocator.dupe(u8, s) catch unreachable;867 const duplicated = b.allocator.dupe(u8, s) catch unreachable;
857 for (duplicated) |*byte| switch (byte.*) {868 for (duplicated) |*byte| switch (byte.*) {
858 '/' => byte.* = fs.path.sep,869 '/' => byte.* = fs.path.sep,
...@@ -861,166 +872,6 @@ fn toNativePathSep(b: *Builder, s: []const u8) []u8 {...@@ -861,166 +872,6 @@ fn toNativePathSep(b: *Builder, s: []const u8) []u8 {
861 return duplicated;872 return duplicated;
862}873}
863874
864const softfloat_sources = [_][]const u8{
865 "deps/SoftFloat-3e/source/8086/f128M_isSignalingNaN.c",
866 "deps/SoftFloat-3e/source/8086/extF80M_isSignalingNaN.c",
867 "deps/SoftFloat-3e/source/8086/s_commonNaNToF128M.c",
868 "deps/SoftFloat-3e/source/8086/s_commonNaNToExtF80M.c",
869 "deps/SoftFloat-3e/source/8086/s_commonNaNToF16UI.c",
870 "deps/SoftFloat-3e/source/8086/s_commonNaNToF32UI.c",
871 "deps/SoftFloat-3e/source/8086/s_commonNaNToF64UI.c",
872 "deps/SoftFloat-3e/source/8086/s_f128MToCommonNaN.c",
873 "deps/SoftFloat-3e/source/8086/s_extF80MToCommonNaN.c",
874 "deps/SoftFloat-3e/source/8086/s_f16UIToCommonNaN.c",
875 "deps/SoftFloat-3e/source/8086/s_f32UIToCommonNaN.c",
876 "deps/SoftFloat-3e/source/8086/s_f64UIToCommonNaN.c",
877 "deps/SoftFloat-3e/source/8086/s_propagateNaNF128M.c",
878 "deps/SoftFloat-3e/source/8086/s_propagateNaNExtF80M.c",
879 "deps/SoftFloat-3e/source/8086/s_propagateNaNF16UI.c",
880 "deps/SoftFloat-3e/source/8086/softfloat_raiseFlags.c",
881 "deps/SoftFloat-3e/source/f128M_add.c",
882 "deps/SoftFloat-3e/source/f128M_div.c",
883 "deps/SoftFloat-3e/source/f128M_eq.c",
884 "deps/SoftFloat-3e/source/f128M_eq_signaling.c",
885 "deps/SoftFloat-3e/source/f128M_le.c",
886 "deps/SoftFloat-3e/source/f128M_le_quiet.c",
887 "deps/SoftFloat-3e/source/f128M_lt.c",
888 "deps/SoftFloat-3e/source/f128M_lt_quiet.c",
889 "deps/SoftFloat-3e/source/f128M_mul.c",
890 "deps/SoftFloat-3e/source/f128M_mulAdd.c",
891 "deps/SoftFloat-3e/source/f128M_rem.c",
892 "deps/SoftFloat-3e/source/f128M_roundToInt.c",
893 "deps/SoftFloat-3e/source/f128M_sqrt.c",
894 "deps/SoftFloat-3e/source/f128M_sub.c",
895 "deps/SoftFloat-3e/source/f128M_to_f16.c",
896 "deps/SoftFloat-3e/source/f128M_to_f32.c",
897 "deps/SoftFloat-3e/source/f128M_to_f64.c",
898 "deps/SoftFloat-3e/source/f128M_to_extF80M.c",
899 "deps/SoftFloat-3e/source/f128M_to_i32.c",
900 "deps/SoftFloat-3e/source/f128M_to_i32_r_minMag.c",
901 "deps/SoftFloat-3e/source/f128M_to_i64.c",
902 "deps/SoftFloat-3e/source/f128M_to_i64_r_minMag.c",
903 "deps/SoftFloat-3e/source/f128M_to_ui32.c",
904 "deps/SoftFloat-3e/source/f128M_to_ui32_r_minMag.c",
905 "deps/SoftFloat-3e/source/f128M_to_ui64.c",
906 "deps/SoftFloat-3e/source/f128M_to_ui64_r_minMag.c",
907 "deps/SoftFloat-3e/source/extF80M_add.c",
908 "deps/SoftFloat-3e/source/extF80M_div.c",
909 "deps/SoftFloat-3e/source/extF80M_eq.c",
910 "deps/SoftFloat-3e/source/extF80M_le.c",
911 "deps/SoftFloat-3e/source/extF80M_lt.c",
912 "deps/SoftFloat-3e/source/extF80M_mul.c",
913 "deps/SoftFloat-3e/source/extF80M_rem.c",
914 "deps/SoftFloat-3e/source/extF80M_roundToInt.c",
915 "deps/SoftFloat-3e/source/extF80M_sqrt.c",
916 "deps/SoftFloat-3e/source/extF80M_sub.c",
917 "deps/SoftFloat-3e/source/extF80M_to_f16.c",
918 "deps/SoftFloat-3e/source/extF80M_to_f32.c",
919 "deps/SoftFloat-3e/source/extF80M_to_f64.c",
920 "deps/SoftFloat-3e/source/extF80M_to_f128M.c",
921 "deps/SoftFloat-3e/source/f16_add.c",
922 "deps/SoftFloat-3e/source/f16_div.c",
923 "deps/SoftFloat-3e/source/f16_eq.c",
924 "deps/SoftFloat-3e/source/f16_isSignalingNaN.c",
925 "deps/SoftFloat-3e/source/f16_lt.c",
926 "deps/SoftFloat-3e/source/f16_mul.c",
927 "deps/SoftFloat-3e/source/f16_mulAdd.c",
928 "deps/SoftFloat-3e/source/f16_rem.c",
929 "deps/SoftFloat-3e/source/f16_roundToInt.c",
930 "deps/SoftFloat-3e/source/f16_sqrt.c",
931 "deps/SoftFloat-3e/source/f16_sub.c",
932 "deps/SoftFloat-3e/source/f16_to_extF80M.c",
933 "deps/SoftFloat-3e/source/f16_to_f128M.c",
934 "deps/SoftFloat-3e/source/f16_to_f64.c",
935 "deps/SoftFloat-3e/source/f32_to_extF80M.c",
936 "deps/SoftFloat-3e/source/f32_to_f128M.c",
937 "deps/SoftFloat-3e/source/f64_to_extF80M.c",
938 "deps/SoftFloat-3e/source/f64_to_f128M.c",
939 "deps/SoftFloat-3e/source/f64_to_f16.c",
940 "deps/SoftFloat-3e/source/i32_to_f128M.c",
941 "deps/SoftFloat-3e/source/s_add256M.c",
942 "deps/SoftFloat-3e/source/s_addCarryM.c",
943 "deps/SoftFloat-3e/source/s_addComplCarryM.c",
944 "deps/SoftFloat-3e/source/s_addF128M.c",
945 "deps/SoftFloat-3e/source/s_addExtF80M.c",
946 "deps/SoftFloat-3e/source/s_addM.c",
947 "deps/SoftFloat-3e/source/s_addMagsF16.c",
948 "deps/SoftFloat-3e/source/s_addMagsF32.c",
949 "deps/SoftFloat-3e/source/s_addMagsF64.c",
950 "deps/SoftFloat-3e/source/s_approxRecip32_1.c",
951 "deps/SoftFloat-3e/source/s_approxRecipSqrt32_1.c",
952 "deps/SoftFloat-3e/source/s_approxRecipSqrt_1Ks.c",
953 "deps/SoftFloat-3e/source/s_approxRecip_1Ks.c",
954 "deps/SoftFloat-3e/source/s_compare128M.c",
955 "deps/SoftFloat-3e/source/s_compare96M.c",
956 "deps/SoftFloat-3e/source/s_compareNonnormExtF80M.c",
957 "deps/SoftFloat-3e/source/s_countLeadingZeros16.c",
958 "deps/SoftFloat-3e/source/s_countLeadingZeros32.c",
959 "deps/SoftFloat-3e/source/s_countLeadingZeros64.c",
960 "deps/SoftFloat-3e/source/s_countLeadingZeros8.c",
961 "deps/SoftFloat-3e/source/s_eq128.c",
962 "deps/SoftFloat-3e/source/s_invalidF128M.c",
963 "deps/SoftFloat-3e/source/s_invalidExtF80M.c",
964 "deps/SoftFloat-3e/source/s_isNaNF128M.c",
965 "deps/SoftFloat-3e/source/s_le128.c",
966 "deps/SoftFloat-3e/source/s_lt128.c",
967 "deps/SoftFloat-3e/source/s_mul128MTo256M.c",
968 "deps/SoftFloat-3e/source/s_mul64To128M.c",
969 "deps/SoftFloat-3e/source/s_mulAddF128M.c",
970 "deps/SoftFloat-3e/source/s_mulAddF16.c",
971 "deps/SoftFloat-3e/source/s_mulAddF32.c",
972 "deps/SoftFloat-3e/source/s_mulAddF64.c",
973 "deps/SoftFloat-3e/source/s_negXM.c",
974 "deps/SoftFloat-3e/source/s_normExtF80SigM.c",
975 "deps/SoftFloat-3e/source/s_normRoundPackMToF128M.c",
976 "deps/SoftFloat-3e/source/s_normRoundPackMToExtF80M.c",
977 "deps/SoftFloat-3e/source/s_normRoundPackToF16.c",
978 "deps/SoftFloat-3e/source/s_normRoundPackToF32.c",
979 "deps/SoftFloat-3e/source/s_normRoundPackToF64.c",
980 "deps/SoftFloat-3e/source/s_normSubnormalF128SigM.c",
981 "deps/SoftFloat-3e/source/s_normSubnormalF16Sig.c",
982 "deps/SoftFloat-3e/source/s_normSubnormalF32Sig.c",
983 "deps/SoftFloat-3e/source/s_normSubnormalF64Sig.c",
984 "deps/SoftFloat-3e/source/s_remStepMBy32.c",
985 "deps/SoftFloat-3e/source/s_roundMToI64.c",
986 "deps/SoftFloat-3e/source/s_roundMToUI64.c",
987 "deps/SoftFloat-3e/source/s_roundPackMToExtF80M.c",
988 "deps/SoftFloat-3e/source/s_roundPackMToF128M.c",
989 "deps/SoftFloat-3e/source/s_roundPackToF16.c",
990 "deps/SoftFloat-3e/source/s_roundPackToF32.c",
991 "deps/SoftFloat-3e/source/s_roundPackToF64.c",
992 "deps/SoftFloat-3e/source/s_roundToI32.c",
993 "deps/SoftFloat-3e/source/s_roundToI64.c",
994 "deps/SoftFloat-3e/source/s_roundToUI32.c",
995 "deps/SoftFloat-3e/source/s_roundToUI64.c",
996 "deps/SoftFloat-3e/source/s_shiftLeftM.c",
997 "deps/SoftFloat-3e/source/s_shiftNormSigF128M.c",
998 "deps/SoftFloat-3e/source/s_shiftRightJam256M.c",
999 "deps/SoftFloat-3e/source/s_shiftRightJam32.c",
1000 "deps/SoftFloat-3e/source/s_shiftRightJam64.c",
1001 "deps/SoftFloat-3e/source/s_shiftRightJamM.c",
1002 "deps/SoftFloat-3e/source/s_shiftRightM.c",
1003 "deps/SoftFloat-3e/source/s_shortShiftLeft64To96M.c",
1004 "deps/SoftFloat-3e/source/s_shortShiftLeftM.c",
1005 "deps/SoftFloat-3e/source/s_shortShiftRightExtendM.c",
1006 "deps/SoftFloat-3e/source/s_shortShiftRightJam64.c",
1007 "deps/SoftFloat-3e/source/s_shortShiftRightJamM.c",
1008 "deps/SoftFloat-3e/source/s_shortShiftRightM.c",
1009 "deps/SoftFloat-3e/source/s_sub1XM.c",
1010 "deps/SoftFloat-3e/source/s_sub256M.c",
1011 "deps/SoftFloat-3e/source/s_subM.c",
1012 "deps/SoftFloat-3e/source/s_subMagsF16.c",
1013 "deps/SoftFloat-3e/source/s_subMagsF32.c",
1014 "deps/SoftFloat-3e/source/s_subMagsF64.c",
1015 "deps/SoftFloat-3e/source/s_tryPropagateNaNF128M.c",
1016 "deps/SoftFloat-3e/source/s_tryPropagateNaNExtF80M.c",
1017 "deps/SoftFloat-3e/source/softfloat_state.c",
1018 "deps/SoftFloat-3e/source/ui32_to_f128M.c",
1019 "deps/SoftFloat-3e/source/ui64_to_f128M.c",
1020 "deps/SoftFloat-3e/source/ui32_to_extF80M.c",
1021 "deps/SoftFloat-3e/source/ui64_to_extF80M.c",
1022};
1023
1024const zig_cpp_sources = [_][]const u8{875const zig_cpp_sources = [_][]const u8{
1025 // These are planned to stay even when we are self-hosted.876 // These are planned to stay even when we are self-hosted.
1026 "src/zig_llvm.cpp",877 "src/zig_llvm.cpp",
doc/langref.html.in+94-59
...@@ -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#}
...@@ -9588,7 +9604,7 @@ pub fn build(b: *Builder) void {...@@ -9588,7 +9604,7 @@ pub fn build(b: *Builder) void {
9588 {#header_close#}9604 {#header_close#}
95899605
9590 {#header_open|Single Threaded Builds#}9606 {#header_open|Single Threaded Builds#}
9591 <p>Zig has a compile option <kbd>--single-threaded</kbd> which has the following effects:</p>9607 <p>Zig has a compile option <kbd>-fsingle-threaded</kbd> which has the following effects:</p>
9592 <ul>9608 <ul>
9593 <li>All {#link|Thread Local Variables#} are treated as regular {#link|Container Level Variables#}.</li>9609 <li>All {#link|Thread Local Variables#} are treated as regular {#link|Container Level Variables#}.</li>
9594 <li>The overhead of {#link|Async Functions#} becomes equivalent to function call overhead.</li>9610 <li>The overhead of {#link|Async Functions#} becomes equivalent to function call overhead.</li>
...@@ -10547,22 +10563,26 @@ const separator = if (builtin.os.tag == .windows) '\\' else '/';...@@ -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/c.zig+1-1
...@@ -354,7 +354,7 @@ fn clone() callconv(.Naked) void {...@@ -354,7 +354,7 @@ fn clone() callconv(.Naked) void {
354 \\ ecall354 \\ ecall
355 );355 );
356 },356 },
357 .mips, .mipsel => {357 .mips, .mipsel, .mips64, .mips64el => {
358 // __clone(func, stack, flags, arg, ptid, tls, ctid)358 // __clone(func, stack, flags, arg, ptid, tls, ctid)
359 // 3, 4, 5, 6, 7, 8, 9359 // 3, 4, 5, 6, 7, 8, 9
360360
lib/compiler_rt/README.md+534-471
...@@ -27,482 +27,545 @@ then statically linked and therefore is a transparent dependency for the...@@ -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 zeros |
68- dev other __bswapdi2 // a byteswapped55| ✓ | __clzdi2 | u64 | ∅ | i32 | count leading zeros |
69- dev other __bswapti2 // a byteswapped56| ✓ | __clzti2 | u128 | ∅ | i32 | count leading zeros |
7057| ✓ | __ctzsi2 | u32 | ∅ | i32 | count trailing zeros |
71#### Integer Comparison58| ✓ | __ctzdi2 | u64 | ∅ | i32 | count trailing zeros |
7259| ✓ | __ctzti2 | u128 | ∅ | i32 | count trailing zeros |
73- port llvm __cmpsi2 // a,b: i32, (a<b)-> 0, (a==b) -> 1, (a>b) -> 260| ✓ | __ffssi2 | u32 | ∅ | i32 | find least significant 1 bit |
74- port llvm __cmpdi2 // a,b: i6461| ✓ | __ffsdi2 | u64 | ∅ | i32 | find least significant 1 bit |
75- port llvm __cmpti2 // a,b: i12862| ✓ | __ffsti2 | u128 | ∅ | i32 | find least significant 1 bit |
76- port llvm __ucmpsi2 // a,b: u32, (a<b)-> 0, (a==b) -> 1, (a>b) -> 263| ✓ | __paritysi2 | u32 | ∅ | i32 | bit parity |
77- port llvm __ucmpdi2 // a,b: u6464| ✓ | __paritydi2 | u64 | ∅ | i32 | bit parity |
78- port llvm __ucmpti2 // a,b: u12865| ✓ | __parityti2 | u128 | ∅ | i32 | bit parity |
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 | byte swap |
83- port llvm __ashldi3 // a,b: u6470| ✓ | __bswapdi2 | u64 | ∅ | i32 | byte swap |
84- port llvm __ashlti3 // a,b: u12871| ✓ | __bswapti2 | u128 | ∅ | i32 | byte swap |
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 | u32 | u32 | i32 | `(a<b) -> 0, (a==b) -> 1, (a>b) -> 2` |
90- port llvm __lshrti3 //77| ✓ | __ucmpdi2 | u64 | u64 | i32 | .. |
91- port llvm __negdi2 // a: i32, -a, symbol-level compatibility with libgcc78| ✓ | __ucmpti2 | u128 | u128 | 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 | float to int, 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 | float to uint, 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 |
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 |
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/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+1774
...@@ -0,0 +1,1774 @@
1const std = @import("std.zig");
2const builtin = @import("builtin");
3const io = std.io;
4const fs = std.fs;
5const mem = std.mem;
6const debug = std.debug;
7const panic = std.debug.panic;
8const assert = debug.assert;
9const log = std.log;
10const ArrayList = std.ArrayList;
11const StringHashMap = std.StringHashMap;
12const Allocator = mem.Allocator;
13const process = std.process;
14const EnvMap = std.process.EnvMap;
15const fmt_lib = std.fmt;
16const File = std.fs.File;
17const CrossTarget = std.zig.CrossTarget;
18const NativeTargetInfo = std.zig.system.NativeTargetInfo;
19const Sha256 = std.crypto.hash.sha2.Sha256;
20const Build = @This();
21
22/// deprecated: use `CompileStep`.
23pub const LibExeObjStep = CompileStep;
24/// deprecated: use `Build`.
25pub const Builder = Build;
26/// deprecated: use `InstallDirStep.Options`
27pub const InstallDirectoryOptions = InstallDirStep.Options;
28
29pub const Step = @import("Build/Step.zig");
30pub const CheckFileStep = @import("Build/CheckFileStep.zig");
31pub const CheckObjectStep = @import("Build/CheckObjectStep.zig");
32pub const ConfigHeaderStep = @import("Build/ConfigHeaderStep.zig");
33pub const EmulatableRunStep = @import("Build/EmulatableRunStep.zig");
34pub const FmtStep = @import("Build/FmtStep.zig");
35pub const InstallArtifactStep = @import("Build/InstallArtifactStep.zig");
36pub const InstallDirStep = @import("Build/InstallDirStep.zig");
37pub const InstallFileStep = @import("Build/InstallFileStep.zig");
38pub const InstallRawStep = @import("Build/InstallRawStep.zig");
39pub const CompileStep = @import("Build/CompileStep.zig");
40pub const LogStep = @import("Build/LogStep.zig");
41pub const OptionsStep = @import("Build/OptionsStep.zig");
42pub const RemoveDirStep = @import("Build/RemoveDirStep.zig");
43pub const RunStep = @import("Build/RunStep.zig");
44pub const TranslateCStep = @import("Build/TranslateCStep.zig");
45pub const WriteFileStep = @import("Build/WriteFileStep.zig");
46
47install_tls: TopLevelStep,
48uninstall_tls: TopLevelStep,
49allocator: Allocator,
50user_input_options: UserInputOptionsMap,
51available_options_map: AvailableOptionsMap,
52available_options_list: ArrayList(AvailableOption),
53verbose: bool,
54verbose_link: bool,
55verbose_cc: bool,
56verbose_air: bool,
57verbose_llvm_ir: bool,
58verbose_cimport: bool,
59verbose_llvm_cpu_features: bool,
60/// The purpose of executing the command is for a human to read compile errors from the terminal
61prominent_compile_errors: bool,
62color: enum { auto, on, off } = .auto,
63reference_trace: ?u32 = null,
64invalid_user_input: bool,
65zig_exe: []const u8,
66default_step: *Step,
67env_map: *EnvMap,
68top_level_steps: ArrayList(*TopLevelStep),
69install_prefix: []const u8,
70dest_dir: ?[]const u8,
71lib_dir: []const u8,
72exe_dir: []const u8,
73h_dir: []const u8,
74install_path: []const u8,
75sysroot: ?[]const u8 = null,
76search_prefixes: ArrayList([]const u8),
77libc_file: ?[]const u8 = null,
78installed_files: ArrayList(InstalledFile),
79/// Path to the directory containing build.zig.
80build_root: []const u8,
81cache_root: []const u8,
82global_cache_root: []const u8,
83/// zig lib dir
84override_lib_dir: ?[]const u8,
85vcpkg_root: VcpkgRoot = .unattempted,
86pkg_config_pkg_list: ?(PkgConfigError![]const PkgConfigPkg) = null,
87args: ?[][]const u8 = null,
88debug_log_scopes: []const []const u8 = &.{},
89debug_compile_errors: bool = false,
90
91/// Experimental. Use system Darling installation to run cross compiled macOS build artifacts.
92enable_darling: bool = false,
93/// Use system QEMU installation to run cross compiled foreign architecture build artifacts.
94enable_qemu: bool = false,
95/// Darwin. Use Rosetta to run x86_64 macOS build artifacts on arm64 macOS.
96enable_rosetta: bool = false,
97/// Use system Wasmtime installation to run cross compiled wasm/wasi build artifacts.
98enable_wasmtime: bool = false,
99/// Use system Wine installation to run cross compiled Windows build artifacts.
100enable_wine: bool = false,
101/// After following the steps in https://github.com/ziglang/zig/wiki/Updating-libc#glibc,
102/// this will be the directory $glibc-build-dir/install/glibcs
103/// Given the example of the aarch64 target, this is the directory
104/// that contains the path `aarch64-linux-gnu/lib/ld-linux-aarch64.so.1`.
105glibc_runtimes_dir: ?[]const u8 = null,
106
107/// Information about the native target. Computed before build() is invoked.
108host: NativeTargetInfo,
109
110dep_prefix: []const u8 = "",
111
112modules: std.StringArrayHashMap(*Module),
113
114pub const ExecError = error{
115 ReadFailure,
116 ExitCodeFailure,
117 ProcessTerminated,
118 ExecNotSupported,
119} || std.ChildProcess.SpawnError;
120
121pub const PkgConfigError = error{
122 PkgConfigCrashed,
123 PkgConfigFailed,
124 PkgConfigNotInstalled,
125 PkgConfigInvalidOutput,
126};
127
128pub const PkgConfigPkg = struct {
129 name: []const u8,
130 desc: []const u8,
131};
132
133pub const CStd = enum {
134 C89,
135 C99,
136 C11,
137};
138
139const UserInputOptionsMap = StringHashMap(UserInputOption);
140const AvailableOptionsMap = StringHashMap(AvailableOption);
141
142const AvailableOption = struct {
143 name: []const u8,
144 type_id: TypeId,
145 description: []const u8,
146 /// If the `type_id` is `enum` this provides the list of enum options
147 enum_options: ?[]const []const u8,
148};
149
150const UserInputOption = struct {
151 name: []const u8,
152 value: UserValue,
153 used: bool,
154};
155
156const UserValue = union(enum) {
157 flag: void,
158 scalar: []const u8,
159 list: ArrayList([]const u8),
160 map: StringHashMap(*const UserValue),
161};
162
163const TypeId = enum {
164 bool,
165 int,
166 float,
167 @"enum",
168 string,
169 list,
170};
171
172const TopLevelStep = struct {
173 pub const base_id = .top_level;
174
175 step: Step,
176 description: []const u8,
177};
178
179pub const DirList = struct {
180 lib_dir: ?[]const u8 = null,
181 exe_dir: ?[]const u8 = null,
182 include_dir: ?[]const u8 = null,
183};
184
185pub fn create(
186 allocator: Allocator,
187 zig_exe: []const u8,
188 build_root: []const u8,
189 cache_root: []const u8,
190 global_cache_root: []const u8,
191 host: NativeTargetInfo,
192) !*Build {
193 const env_map = try allocator.create(EnvMap);
194 env_map.* = try process.getEnvMap(allocator);
195
196 const self = try allocator.create(Build);
197 self.* = Build{
198 .zig_exe = zig_exe,
199 .build_root = build_root,
200 .cache_root = try fs.path.relative(allocator, build_root, cache_root),
201 .global_cache_root = global_cache_root,
202 .verbose = false,
203 .verbose_link = false,
204 .verbose_cc = false,
205 .verbose_air = false,
206 .verbose_llvm_ir = false,
207 .verbose_cimport = false,
208 .verbose_llvm_cpu_features = false,
209 .prominent_compile_errors = false,
210 .invalid_user_input = false,
211 .allocator = allocator,
212 .user_input_options = UserInputOptionsMap.init(allocator),
213 .available_options_map = AvailableOptionsMap.init(allocator),
214 .available_options_list = ArrayList(AvailableOption).init(allocator),
215 .top_level_steps = ArrayList(*TopLevelStep).init(allocator),
216 .default_step = undefined,
217 .env_map = env_map,
218 .search_prefixes = ArrayList([]const u8).init(allocator),
219 .install_prefix = undefined,
220 .lib_dir = undefined,
221 .exe_dir = undefined,
222 .h_dir = undefined,
223 .dest_dir = env_map.get("DESTDIR"),
224 .installed_files = ArrayList(InstalledFile).init(allocator),
225 .install_tls = TopLevelStep{
226 .step = Step.initNoOp(.top_level, "install", allocator),
227 .description = "Copy build artifacts to prefix path",
228 },
229 .uninstall_tls = TopLevelStep{
230 .step = Step.init(.top_level, "uninstall", allocator, makeUninstall),
231 .description = "Remove build artifacts from prefix path",
232 },
233 .override_lib_dir = null,
234 .install_path = undefined,
235 .args = null,
236 .host = host,
237 .modules = std.StringArrayHashMap(*Module).init(allocator),
238 };
239 try self.top_level_steps.append(&self.install_tls);
240 try self.top_level_steps.append(&self.uninstall_tls);
241 self.default_step = &self.install_tls.step;
242 return self;
243}
244
245fn createChild(
246 parent: *Build,
247 dep_name: []const u8,
248 build_root: []const u8,
249 args: anytype,
250) !*Build {
251 const child = try createChildOnly(parent, dep_name, build_root);
252 try applyArgs(child, args);
253 return child;
254}
255
256fn createChildOnly(parent: *Build, dep_name: []const u8, build_root: []const u8) !*Build {
257 const allocator = parent.allocator;
258 const child = try allocator.create(Build);
259 child.* = .{
260 .allocator = allocator,
261 .install_tls = .{
262 .step = Step.initNoOp(.top_level, "install", allocator),
263 .description = "Copy build artifacts to prefix path",
264 },
265 .uninstall_tls = .{
266 .step = Step.init(.top_level, "uninstall", allocator, makeUninstall),
267 .description = "Remove build artifacts from prefix path",
268 },
269 .user_input_options = UserInputOptionsMap.init(allocator),
270 .available_options_map = AvailableOptionsMap.init(allocator),
271 .available_options_list = ArrayList(AvailableOption).init(allocator),
272 .verbose = parent.verbose,
273 .verbose_link = parent.verbose_link,
274 .verbose_cc = parent.verbose_cc,
275 .verbose_air = parent.verbose_air,
276 .verbose_llvm_ir = parent.verbose_llvm_ir,
277 .verbose_cimport = parent.verbose_cimport,
278 .verbose_llvm_cpu_features = parent.verbose_llvm_cpu_features,
279 .prominent_compile_errors = parent.prominent_compile_errors,
280 .color = parent.color,
281 .reference_trace = parent.reference_trace,
282 .invalid_user_input = false,
283 .zig_exe = parent.zig_exe,
284 .default_step = undefined,
285 .env_map = parent.env_map,
286 .top_level_steps = ArrayList(*TopLevelStep).init(allocator),
287 .install_prefix = undefined,
288 .dest_dir = parent.dest_dir,
289 .lib_dir = parent.lib_dir,
290 .exe_dir = parent.exe_dir,
291 .h_dir = parent.h_dir,
292 .install_path = parent.install_path,
293 .sysroot = parent.sysroot,
294 .search_prefixes = ArrayList([]const u8).init(allocator),
295 .libc_file = parent.libc_file,
296 .installed_files = ArrayList(InstalledFile).init(allocator),
297 .build_root = build_root,
298 .cache_root = parent.cache_root,
299 .global_cache_root = parent.global_cache_root,
300 .override_lib_dir = parent.override_lib_dir,
301 .debug_log_scopes = parent.debug_log_scopes,
302 .debug_compile_errors = parent.debug_compile_errors,
303 .enable_darling = parent.enable_darling,
304 .enable_qemu = parent.enable_qemu,
305 .enable_rosetta = parent.enable_rosetta,
306 .enable_wasmtime = parent.enable_wasmtime,
307 .enable_wine = parent.enable_wine,
308 .glibc_runtimes_dir = parent.glibc_runtimes_dir,
309 .host = parent.host,
310 .dep_prefix = parent.fmt("{s}{s}.", .{ parent.dep_prefix, dep_name }),
311 .modules = std.StringArrayHashMap(*Module).init(allocator),
312 };
313 try child.top_level_steps.append(&child.install_tls);
314 try child.top_level_steps.append(&child.uninstall_tls);
315 child.default_step = &child.install_tls.step;
316 return child;
317}
318
319fn applyArgs(b: *Build, args: anytype) !void {
320 inline for (@typeInfo(@TypeOf(args)).Struct.fields) |field| {
321 const v = @field(args, field.name);
322 const T = @TypeOf(v);
323 switch (T) {
324 CrossTarget => {
325 try b.user_input_options.put(field.name, .{
326 .name = field.name,
327 .value = .{ .scalar = try v.zigTriple(b.allocator) },
328 .used = false,
329 });
330 try b.user_input_options.put("cpu", .{
331 .name = "cpu",
332 .value = .{ .scalar = try serializeCpu(b.allocator, v.getCpu()) },
333 .used = false,
334 });
335 },
336 []const u8 => {
337 try b.user_input_options.put(field.name, .{
338 .name = field.name,
339 .value = .{ .scalar = v },
340 .used = false,
341 });
342 },
343 else => switch (@typeInfo(T)) {
344 .Bool => {
345 try b.user_input_options.put(field.name, .{
346 .name = field.name,
347 .value = .{ .scalar = if (v) "true" else "false" },
348 .used = false,
349 });
350 },
351 .Enum => {
352 try b.user_input_options.put(field.name, .{
353 .name = field.name,
354 .value = .{ .scalar = @tagName(v) },
355 .used = false,
356 });
357 },
358 .Int => {
359 try b.user_input_options.put(field.name, .{
360 .name = field.name,
361 .value = .{ .scalar = try std.fmt.allocPrint(b.allocator, "{d}", .{v}) },
362 .used = false,
363 });
364 },
365 else => @compileError("option '" ++ field.name ++ "' has unsupported type: " ++ @typeName(T)),
366 },
367 }
368 }
369 const Hasher = std.crypto.auth.siphash.SipHash128(1, 3);
370 // Random bytes to make unique. Refresh this with new random bytes when
371 // implementation is modified in a non-backwards-compatible way.
372 var hash = Hasher.init("ZaEsvQ5ClaA2IdH9");
373 hash.update(b.dep_prefix);
374 // TODO additionally update the hash with `args`.
375
376 var digest: [16]u8 = undefined;
377 hash.final(&digest);
378 var hash_basename: [digest.len * 2]u8 = undefined;
379 _ = std.fmt.bufPrint(&hash_basename, "{s}", .{std.fmt.fmtSliceHexLower(&digest)}) catch
380 unreachable;
381
382 const install_prefix = b.pathJoin(&.{ b.cache_root, "i", &hash_basename });
383 b.resolveInstallPrefix(install_prefix, .{});
384}
385
386pub fn destroy(self: *Build) void {
387 self.env_map.deinit();
388 self.top_level_steps.deinit();
389 self.allocator.destroy(self);
390}
391
392/// This function is intended to be called by lib/build_runner.zig, not a build.zig file.
393pub fn resolveInstallPrefix(self: *Build, install_prefix: ?[]const u8, dir_list: DirList) void {
394 if (self.dest_dir) |dest_dir| {
395 self.install_prefix = install_prefix orelse "/usr";
396 self.install_path = self.pathJoin(&.{ dest_dir, self.install_prefix });
397 } else {
398 self.install_prefix = install_prefix orelse
399 (self.pathJoin(&.{ self.build_root, "zig-out" }));
400 self.install_path = self.install_prefix;
401 }
402
403 var lib_list = [_][]const u8{ self.install_path, "lib" };
404 var exe_list = [_][]const u8{ self.install_path, "bin" };
405 var h_list = [_][]const u8{ self.install_path, "include" };
406
407 if (dir_list.lib_dir) |dir| {
408 if (std.fs.path.isAbsolute(dir)) lib_list[0] = self.dest_dir orelse "";
409 lib_list[1] = dir;
410 }
411
412 if (dir_list.exe_dir) |dir| {
413 if (std.fs.path.isAbsolute(dir)) exe_list[0] = self.dest_dir orelse "";
414 exe_list[1] = dir;
415 }
416
417 if (dir_list.include_dir) |dir| {
418 if (std.fs.path.isAbsolute(dir)) h_list[0] = self.dest_dir orelse "";
419 h_list[1] = dir;
420 }
421
422 self.lib_dir = self.pathJoin(&lib_list);
423 self.exe_dir = self.pathJoin(&exe_list);
424 self.h_dir = self.pathJoin(&h_list);
425}
426
427pub fn addOptions(self: *Build) *OptionsStep {
428 return OptionsStep.create(self);
429}
430
431pub const ExecutableOptions = struct {
432 name: []const u8,
433 root_source_file: ?FileSource = null,
434 version: ?std.builtin.Version = null,
435 target: CrossTarget = .{},
436 optimize: std.builtin.Mode = .Debug,
437 linkage: ?CompileStep.Linkage = null,
438};
439
440pub fn addExecutable(b: *Build, options: ExecutableOptions) *CompileStep {
441 return CompileStep.create(b, .{
442 .name = options.name,
443 .root_source_file = options.root_source_file,
444 .version = options.version,
445 .target = options.target,
446 .optimize = options.optimize,
447 .kind = .exe,
448 .linkage = options.linkage,
449 });
450}
451
452pub const ObjectOptions = struct {
453 name: []const u8,
454 root_source_file: ?FileSource = null,
455 target: CrossTarget,
456 optimize: std.builtin.Mode,
457};
458
459pub fn addObject(b: *Build, options: ObjectOptions) *CompileStep {
460 return CompileStep.create(b, .{
461 .name = options.name,
462 .root_source_file = options.root_source_file,
463 .target = options.target,
464 .optimize = options.optimize,
465 .kind = .obj,
466 });
467}
468
469pub const SharedLibraryOptions = struct {
470 name: []const u8,
471 root_source_file: ?FileSource = null,
472 version: ?std.builtin.Version = null,
473 target: CrossTarget,
474 optimize: std.builtin.Mode,
475};
476
477pub fn addSharedLibrary(b: *Build, options: SharedLibraryOptions) *CompileStep {
478 return CompileStep.create(b, .{
479 .name = options.name,
480 .root_source_file = options.root_source_file,
481 .kind = .lib,
482 .linkage = .dynamic,
483 .version = options.version,
484 .target = options.target,
485 .optimize = options.optimize,
486 });
487}
488
489pub const StaticLibraryOptions = struct {
490 name: []const u8,
491 root_source_file: ?FileSource = null,
492 target: CrossTarget,
493 optimize: std.builtin.Mode,
494 version: ?std.builtin.Version = null,
495};
496
497pub fn addStaticLibrary(b: *Build, options: StaticLibraryOptions) *CompileStep {
498 return CompileStep.create(b, .{
499 .name = options.name,
500 .root_source_file = options.root_source_file,
501 .kind = .lib,
502 .linkage = .static,
503 .version = options.version,
504 .target = options.target,
505 .optimize = options.optimize,
506 });
507}
508
509pub const TestOptions = struct {
510 name: []const u8 = "test",
511 kind: CompileStep.Kind = .@"test",
512 root_source_file: FileSource,
513 target: CrossTarget = .{},
514 optimize: std.builtin.Mode = .Debug,
515 version: ?std.builtin.Version = null,
516};
517
518pub fn addTest(b: *Build, options: TestOptions) *CompileStep {
519 return CompileStep.create(b, .{
520 .name = options.name,
521 .kind = options.kind,
522 .root_source_file = options.root_source_file,
523 .target = options.target,
524 .optimize = options.optimize,
525 });
526}
527
528pub const AssemblyOptions = struct {
529 name: []const u8,
530 source_file: FileSource,
531 target: CrossTarget,
532 optimize: std.builtin.Mode,
533};
534
535pub fn addAssembly(b: *Build, options: AssemblyOptions) *CompileStep {
536 const obj_step = CompileStep.create(b, .{
537 .name = options.name,
538 .root_source_file = null,
539 .target = options.target,
540 .optimize = options.optimize,
541 });
542 obj_step.addAssemblyFileSource(options.source_file.dupe(b));
543 return obj_step;
544}
545
546pub const AddModuleOptions = struct {
547 name: []const u8,
548 source_file: FileSource,
549 dependencies: []const ModuleDependency = &.{},
550};
551
552pub fn addModule(b: *Build, options: AddModuleOptions) void {
553 b.modules.put(b.dupe(options.name), b.createModule(.{
554 .source_file = options.source_file,
555 .dependencies = options.dependencies,
556 })) catch @panic("OOM");
557}
558
559pub const ModuleDependency = struct {
560 name: []const u8,
561 module: *Module,
562};
563
564pub const CreateModuleOptions = struct {
565 source_file: FileSource,
566 dependencies: []const ModuleDependency = &.{},
567};
568
569/// Prefer to use `addModule` which will make the module available to other
570/// packages which depend on this package.
571pub fn createModule(b: *Build, options: CreateModuleOptions) *Module {
572 const module = b.allocator.create(Module) catch @panic("OOM");
573 module.* = .{
574 .builder = b,
575 .source_file = options.source_file,
576 .dependencies = moduleDependenciesToArrayHashMap(b.allocator, options.dependencies),
577 };
578 return module;
579}
580
581fn moduleDependenciesToArrayHashMap(arena: Allocator, deps: []const ModuleDependency) std.StringArrayHashMap(*Module) {
582 var result = std.StringArrayHashMap(*Module).init(arena);
583 for (deps) |dep| {
584 result.put(dep.name, dep.module) catch @panic("OOM");
585 }
586 return result;
587}
588
589/// Initializes a RunStep with argv, which must at least have the path to the
590/// executable. More command line arguments can be added with `addArg`,
591/// `addArgs`, and `addArtifactArg`.
592/// Be careful using this function, as it introduces a system dependency.
593/// To run an executable built with zig build, see `CompileStep.run`.
594pub fn addSystemCommand(self: *Build, argv: []const []const u8) *RunStep {
595 assert(argv.len >= 1);
596 const run_step = RunStep.create(self, self.fmt("run {s}", .{argv[0]}));
597 run_step.addArgs(argv);
598 return run_step;
599}
600
601/// Using the `values` provided, produces a C header file, possibly based on a
602/// template input file (e.g. config.h.in).
603/// When an input template file is provided, this function will fail the build
604/// when an option not found in the input file is provided in `values`, and
605/// when an option found in the input file is missing from `values`.
606pub fn addConfigHeader(
607 b: *Build,
608 options: ConfigHeaderStep.Options,
609 values: anytype,
610) *ConfigHeaderStep {
611 const config_header_step = ConfigHeaderStep.create(b, options);
612 config_header_step.addValues(values);
613 return config_header_step;
614}
615
616/// Allocator.dupe without the need to handle out of memory.
617pub fn dupe(self: *Build, bytes: []const u8) []u8 {
618 return self.allocator.dupe(u8, bytes) catch @panic("OOM");
619}
620
621/// Duplicates an array of strings without the need to handle out of memory.
622pub fn dupeStrings(self: *Build, strings: []const []const u8) [][]u8 {
623 const array = self.allocator.alloc([]u8, strings.len) catch @panic("OOM");
624 for (strings) |s, i| {
625 array[i] = self.dupe(s);
626 }
627 return array;
628}
629
630/// Duplicates a path and converts all slashes to the OS's canonical path separator.
631pub fn dupePath(self: *Build, bytes: []const u8) []u8 {
632 const the_copy = self.dupe(bytes);
633 for (the_copy) |*byte| {
634 switch (byte.*) {
635 '/', '\\' => byte.* = fs.path.sep,
636 else => {},
637 }
638 }
639 return the_copy;
640}
641
642pub fn addWriteFile(self: *Build, file_path: []const u8, data: []const u8) *WriteFileStep {
643 const write_file_step = self.addWriteFiles();
644 write_file_step.add(file_path, data);
645 return write_file_step;
646}
647
648pub fn addWriteFiles(self: *Build) *WriteFileStep {
649 const write_file_step = self.allocator.create(WriteFileStep) catch @panic("OOM");
650 write_file_step.* = WriteFileStep.init(self);
651 return write_file_step;
652}
653
654pub fn addLog(self: *Build, comptime format: []const u8, args: anytype) *LogStep {
655 const data = self.fmt(format, args);
656 const log_step = self.allocator.create(LogStep) catch @panic("OOM");
657 log_step.* = LogStep.init(self, data);
658 return log_step;
659}
660
661pub fn addRemoveDirTree(self: *Build, dir_path: []const u8) *RemoveDirStep {
662 const remove_dir_step = self.allocator.create(RemoveDirStep) catch @panic("OOM");
663 remove_dir_step.* = RemoveDirStep.init(self, dir_path);
664 return remove_dir_step;
665}
666
667pub fn addFmt(self: *Build, paths: []const []const u8) *FmtStep {
668 return FmtStep.create(self, paths);
669}
670
671pub fn addTranslateC(self: *Build, options: TranslateCStep.Options) *TranslateCStep {
672 return TranslateCStep.create(self, options);
673}
674
675pub fn make(self: *Build, step_names: []const []const u8) !void {
676 try self.makePath(self.cache_root);
677
678 var wanted_steps = ArrayList(*Step).init(self.allocator);
679 defer wanted_steps.deinit();
680
681 if (step_names.len == 0) {
682 try wanted_steps.append(self.default_step);
683 } else {
684 for (step_names) |step_name| {
685 const s = try self.getTopLevelStepByName(step_name);
686 try wanted_steps.append(s);
687 }
688 }
689
690 for (wanted_steps.items) |s| {
691 try self.makeOneStep(s);
692 }
693}
694
695pub fn getInstallStep(self: *Build) *Step {
696 return &self.install_tls.step;
697}
698
699pub fn getUninstallStep(self: *Build) *Step {
700 return &self.uninstall_tls.step;
701}
702
703fn makeUninstall(uninstall_step: *Step) anyerror!void {
704 const uninstall_tls = @fieldParentPtr(TopLevelStep, "step", uninstall_step);
705 const self = @fieldParentPtr(Build, "uninstall_tls", uninstall_tls);
706
707 for (self.installed_files.items) |installed_file| {
708 const full_path = self.getInstallPath(installed_file.dir, installed_file.path);
709 if (self.verbose) {
710 log.info("rm {s}", .{full_path});
711 }
712 fs.cwd().deleteTree(full_path) catch {};
713 }
714
715 // TODO remove empty directories
716}
717
718fn makeOneStep(self: *Build, s: *Step) anyerror!void {
719 if (s.loop_flag) {
720 log.err("Dependency loop detected:\n {s}", .{s.name});
721 return error.DependencyLoopDetected;
722 }
723 s.loop_flag = true;
724
725 for (s.dependencies.items) |dep| {
726 self.makeOneStep(dep) catch |err| {
727 if (err == error.DependencyLoopDetected) {
728 log.err(" {s}", .{s.name});
729 }
730 return err;
731 };
732 }
733
734 s.loop_flag = false;
735
736 try s.make();
737}
738
739fn getTopLevelStepByName(self: *Build, name: []const u8) !*Step {
740 for (self.top_level_steps.items) |top_level_step| {
741 if (mem.eql(u8, top_level_step.step.name, name)) {
742 return &top_level_step.step;
743 }
744 }
745 log.err("Cannot run step '{s}' because it does not exist", .{name});
746 return error.InvalidStepName;
747}
748
749pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_raw: []const u8) ?T {
750 const name = self.dupe(name_raw);
751 const description = self.dupe(description_raw);
752 const type_id = comptime typeToEnum(T);
753 const enum_options = if (type_id == .@"enum") blk: {
754 const fields = comptime std.meta.fields(T);
755 var options = ArrayList([]const u8).initCapacity(self.allocator, fields.len) catch @panic("OOM");
756
757 inline for (fields) |field| {
758 options.appendAssumeCapacity(field.name);
759 }
760
761 break :blk options.toOwnedSlice() catch @panic("OOM");
762 } else null;
763 const available_option = AvailableOption{
764 .name = name,
765 .type_id = type_id,
766 .description = description,
767 .enum_options = enum_options,
768 };
769 if ((self.available_options_map.fetchPut(name, available_option) catch @panic("OOM")) != null) {
770 panic("Option '{s}' declared twice", .{name});
771 }
772 self.available_options_list.append(available_option) catch @panic("OOM");
773
774 const option_ptr = self.user_input_options.getPtr(name) orelse return null;
775 option_ptr.used = true;
776 switch (type_id) {
777 .bool => switch (option_ptr.value) {
778 .flag => return true,
779 .scalar => |s| {
780 if (mem.eql(u8, s, "true")) {
781 return true;
782 } else if (mem.eql(u8, s, "false")) {
783 return false;
784 } else {
785 log.err("Expected -D{s} to be a boolean, but received '{s}'\n", .{ name, s });
786 self.markInvalidUserInput();
787 return null;
788 }
789 },
790 .list, .map => {
791 log.err("Expected -D{s} to be a boolean, but received a {s}.\n", .{
792 name, @tagName(option_ptr.value),
793 });
794 self.markInvalidUserInput();
795 return null;
796 },
797 },
798 .int => switch (option_ptr.value) {
799 .flag, .list, .map => {
800 log.err("Expected -D{s} to be an integer, but received a {s}.\n", .{
801 name, @tagName(option_ptr.value),
802 });
803 self.markInvalidUserInput();
804 return null;
805 },
806 .scalar => |s| {
807 const n = std.fmt.parseInt(T, s, 10) catch |err| switch (err) {
808 error.Overflow => {
809 log.err("-D{s} value {s} cannot fit into type {s}.\n", .{ name, s, @typeName(T) });
810 self.markInvalidUserInput();
811 return null;
812 },
813 else => {
814 log.err("Expected -D{s} to be an integer of type {s}.\n", .{ name, @typeName(T) });
815 self.markInvalidUserInput();
816 return null;
817 },
818 };
819 return n;
820 },
821 },
822 .float => switch (option_ptr.value) {
823 .flag, .map, .list => {
824 log.err("Expected -D{s} to be a float, but received a {s}.\n", .{
825 name, @tagName(option_ptr.value),
826 });
827 self.markInvalidUserInput();
828 return null;
829 },
830 .scalar => |s| {
831 const n = std.fmt.parseFloat(T, s) catch {
832 log.err("Expected -D{s} to be a float of type {s}.\n", .{ name, @typeName(T) });
833 self.markInvalidUserInput();
834 return null;
835 };
836 return n;
837 },
838 },
839 .@"enum" => switch (option_ptr.value) {
840 .flag, .map, .list => {
841 log.err("Expected -D{s} to be an enum, but received a {s}.\n", .{
842 name, @tagName(option_ptr.value),
843 });
844 self.markInvalidUserInput();
845 return null;
846 },
847 .scalar => |s| {
848 if (std.meta.stringToEnum(T, s)) |enum_lit| {
849 return enum_lit;
850 } else {
851 log.err("Expected -D{s} to be of type {s}.\n", .{ name, @typeName(T) });
852 self.markInvalidUserInput();
853 return null;
854 }
855 },
856 },
857 .string => switch (option_ptr.value) {
858 .flag, .list, .map => {
859 log.err("Expected -D{s} to be a string, but received a {s}.\n", .{
860 name, @tagName(option_ptr.value),
861 });
862 self.markInvalidUserInput();
863 return null;
864 },
865 .scalar => |s| return s,
866 },
867 .list => switch (option_ptr.value) {
868 .flag, .map => {
869 log.err("Expected -D{s} to be a list, but received a {s}.\n", .{
870 name, @tagName(option_ptr.value),
871 });
872 self.markInvalidUserInput();
873 return null;
874 },
875 .scalar => |s| {
876 return self.allocator.dupe([]const u8, &[_][]const u8{s}) catch @panic("OOM");
877 },
878 .list => |lst| return lst.items,
879 },
880 }
881}
882
883pub fn step(self: *Build, name: []const u8, description: []const u8) *Step {
884 const step_info = self.allocator.create(TopLevelStep) catch @panic("OOM");
885 step_info.* = TopLevelStep{
886 .step = Step.initNoOp(.top_level, name, self.allocator),
887 .description = self.dupe(description),
888 };
889 self.top_level_steps.append(step_info) catch @panic("OOM");
890 return &step_info.step;
891}
892
893pub const StandardOptimizeOptionOptions = struct {
894 preferred_optimize_mode: ?std.builtin.Mode = null,
895};
896
897pub fn standardOptimizeOption(self: *Build, options: StandardOptimizeOptionOptions) std.builtin.Mode {
898 if (options.preferred_optimize_mode) |mode| {
899 if (self.option(bool, "release", "optimize for end users") orelse false) {
900 return mode;
901 } else {
902 return .Debug;
903 }
904 } else {
905 return self.option(
906 std.builtin.Mode,
907 "optimize",
908 "prioritize performance, safety, or binary size (-O flag)",
909 ) orelse .Debug;
910 }
911}
912
913pub const StandardTargetOptionsArgs = struct {
914 whitelist: ?[]const CrossTarget = null,
915
916 default_target: CrossTarget = CrossTarget{},
917};
918
919/// Exposes standard `zig build` options for choosing a target.
920pub fn standardTargetOptions(self: *Build, args: StandardTargetOptionsArgs) CrossTarget {
921 const maybe_triple = self.option(
922 []const u8,
923 "target",
924 "The CPU architecture, OS, and ABI to build for",
925 );
926 const mcpu = self.option([]const u8, "cpu", "Target CPU features to add or subtract");
927
928 if (maybe_triple == null and mcpu == null) {
929 return args.default_target;
930 }
931
932 const triple = maybe_triple orelse "native";
933
934 var diags: CrossTarget.ParseOptions.Diagnostics = .{};
935 const selected_target = CrossTarget.parse(.{
936 .arch_os_abi = triple,
937 .cpu_features = mcpu,
938 .diagnostics = &diags,
939 }) catch |err| switch (err) {
940 error.UnknownCpuModel => {
941 log.err("Unknown CPU: '{s}'\nAvailable CPUs for architecture '{s}':", .{
942 diags.cpu_name.?,
943 @tagName(diags.arch.?),
944 });
945 for (diags.arch.?.allCpuModels()) |cpu| {
946 log.err(" {s}", .{cpu.name});
947 }
948 self.markInvalidUserInput();
949 return args.default_target;
950 },
951 error.UnknownCpuFeature => {
952 log.err(
953 \\Unknown CPU feature: '{s}'
954 \\Available CPU features for architecture '{s}':
955 \\
956 , .{
957 diags.unknown_feature_name.?,
958 @tagName(diags.arch.?),
959 });
960 for (diags.arch.?.allFeaturesList()) |feature| {
961 log.err(" {s}: {s}", .{ feature.name, feature.description });
962 }
963 self.markInvalidUserInput();
964 return args.default_target;
965 },
966 error.UnknownOperatingSystem => {
967 log.err(
968 \\Unknown OS: '{s}'
969 \\Available operating systems:
970 \\
971 , .{diags.os_name.?});
972 inline for (std.meta.fields(std.Target.Os.Tag)) |field| {
973 log.err(" {s}", .{field.name});
974 }
975 self.markInvalidUserInput();
976 return args.default_target;
977 },
978 else => |e| {
979 log.err("Unable to parse target '{s}': {s}\n", .{ triple, @errorName(e) });
980 self.markInvalidUserInput();
981 return args.default_target;
982 },
983 };
984
985 const selected_canonicalized_triple = selected_target.zigTriple(self.allocator) catch @panic("OOM");
986
987 if (args.whitelist) |list| whitelist_check: {
988 // Make sure it's a match of one of the list.
989 var mismatch_triple = true;
990 var mismatch_cpu_features = true;
991 var whitelist_item = CrossTarget{};
992 for (list) |t| {
993 mismatch_cpu_features = true;
994 mismatch_triple = true;
995
996 const t_triple = t.zigTriple(self.allocator) catch @panic("OOM");
997 if (mem.eql(u8, t_triple, selected_canonicalized_triple)) {
998 mismatch_triple = false;
999 whitelist_item = t;
1000 if (t.getCpuFeatures().isSuperSetOf(selected_target.getCpuFeatures())) {
1001 mismatch_cpu_features = false;
1002 break :whitelist_check;
1003 } else {
1004 break;
1005 }
1006 }
1007 }
1008 if (mismatch_triple) {
1009 log.err("Chosen target '{s}' does not match one of the supported targets:", .{
1010 selected_canonicalized_triple,
1011 });
1012 for (list) |t| {
1013 const t_triple = t.zigTriple(self.allocator) catch @panic("OOM");
1014 log.err(" {s}", .{t_triple});
1015 }
1016 } else {
1017 assert(mismatch_cpu_features);
1018 const whitelist_cpu = whitelist_item.getCpu();
1019 const selected_cpu = selected_target.getCpu();
1020 log.err("Chosen CPU model '{s}' does not match one of the supported targets:", .{
1021 selected_cpu.model.name,
1022 });
1023 log.err(" Supported feature Set: ", .{});
1024 const all_features = whitelist_cpu.arch.allFeaturesList();
1025 var populated_cpu_features = whitelist_cpu.model.features;
1026 populated_cpu_features.populateDependencies(all_features);
1027 for (all_features) |feature, i_usize| {
1028 const i = @intCast(std.Target.Cpu.Feature.Set.Index, i_usize);
1029 const in_cpu_set = populated_cpu_features.isEnabled(i);
1030 if (in_cpu_set) {
1031 log.err("{s} ", .{feature.name});
1032 }
1033 }
1034 log.err(" Remove: ", .{});
1035 for (all_features) |feature, i_usize| {
1036 const i = @intCast(std.Target.Cpu.Feature.Set.Index, i_usize);
1037 const in_cpu_set = populated_cpu_features.isEnabled(i);
1038 const in_actual_set = selected_cpu.features.isEnabled(i);
1039 if (in_actual_set and !in_cpu_set) {
1040 log.err("{s} ", .{feature.name});
1041 }
1042 }
1043 }
1044 self.markInvalidUserInput();
1045 return args.default_target;
1046 }
1047
1048 return selected_target;
1049}
1050
1051pub fn addUserInputOption(self: *Build, name_raw: []const u8, value_raw: []const u8) !bool {
1052 const name = self.dupe(name_raw);
1053 const value = self.dupe(value_raw);
1054 const gop = try self.user_input_options.getOrPut(name);
1055 if (!gop.found_existing) {
1056 gop.value_ptr.* = UserInputOption{
1057 .name = name,
1058 .value = .{ .scalar = value },
1059 .used = false,
1060 };
1061 return false;
1062 }
1063
1064 // option already exists
1065 switch (gop.value_ptr.value) {
1066 .scalar => |s| {
1067 // turn it into a list
1068 var list = ArrayList([]const u8).init(self.allocator);
1069 try list.append(s);
1070 try list.append(value);
1071 try self.user_input_options.put(name, .{
1072 .name = name,
1073 .value = .{ .list = list },
1074 .used = false,
1075 });
1076 },
1077 .list => |*list| {
1078 // append to the list
1079 try list.append(value);
1080 try self.user_input_options.put(name, .{
1081 .name = name,
1082 .value = .{ .list = list.* },
1083 .used = false,
1084 });
1085 },
1086 .flag => {
1087 log.warn("Option '-D{s}={s}' conflicts with flag '-D{s}'.", .{ name, value, name });
1088 return true;
1089 },
1090 .map => |*map| {
1091 _ = map;
1092 log.warn("TODO maps as command line arguments is not implemented yet.", .{});
1093 return true;
1094 },
1095 }
1096 return false;
1097}
1098
1099pub fn addUserInputFlag(self: *Build, name_raw: []const u8) !bool {
1100 const name = self.dupe(name_raw);
1101 const gop = try self.user_input_options.getOrPut(name);
1102 if (!gop.found_existing) {
1103 gop.value_ptr.* = .{
1104 .name = name,
1105 .value = .{ .flag = {} },
1106 .used = false,
1107 };
1108 return false;
1109 }
1110
1111 // option already exists
1112 switch (gop.value_ptr.value) {
1113 .scalar => |s| {
1114 log.err("Flag '-D{s}' conflicts with option '-D{s}={s}'.", .{ name, name, s });
1115 return true;
1116 },
1117 .list, .map => {
1118 log.err("Flag '-D{s}' conflicts with multiple options of the same name.", .{name});
1119 return true;
1120 },
1121 .flag => {},
1122 }
1123 return false;
1124}
1125
1126fn typeToEnum(comptime T: type) TypeId {
1127 return switch (@typeInfo(T)) {
1128 .Int => .int,
1129 .Float => .float,
1130 .Bool => .bool,
1131 .Enum => .@"enum",
1132 else => switch (T) {
1133 []const u8 => .string,
1134 []const []const u8 => .list,
1135 else => @compileError("Unsupported type: " ++ @typeName(T)),
1136 },
1137 };
1138}
1139
1140fn markInvalidUserInput(self: *Build) void {
1141 self.invalid_user_input = true;
1142}
1143
1144pub fn validateUserInputDidItFail(self: *Build) bool {
1145 // make sure all args are used
1146 var it = self.user_input_options.iterator();
1147 while (it.next()) |entry| {
1148 if (!entry.value_ptr.used) {
1149 log.err("Invalid option: -D{s}", .{entry.key_ptr.*});
1150 self.markInvalidUserInput();
1151 }
1152 }
1153
1154 return self.invalid_user_input;
1155}
1156
1157pub fn spawnChild(self: *Build, argv: []const []const u8) !void {
1158 return self.spawnChildEnvMap(null, self.env_map, argv);
1159}
1160
1161fn printCmd(cwd: ?[]const u8, argv: []const []const u8) void {
1162 if (cwd) |yes_cwd| std.debug.print("cd {s} && ", .{yes_cwd});
1163 for (argv) |arg| {
1164 std.debug.print("{s} ", .{arg});
1165 }
1166 std.debug.print("\n", .{});
1167}
1168
1169pub fn spawnChildEnvMap(self: *Build, cwd: ?[]const u8, env_map: *const EnvMap, argv: []const []const u8) !void {
1170 if (self.verbose) {
1171 printCmd(cwd, argv);
1172 }
1173
1174 if (!std.process.can_spawn)
1175 return error.ExecNotSupported;
1176
1177 var child = std.ChildProcess.init(argv, self.allocator);
1178 child.cwd = cwd;
1179 child.env_map = env_map;
1180
1181 const term = child.spawnAndWait() catch |err| {
1182 log.err("Unable to spawn {s}: {s}", .{ argv[0], @errorName(err) });
1183 return err;
1184 };
1185
1186 switch (term) {
1187 .Exited => |code| {
1188 if (code != 0) {
1189 log.err("The following command exited with error code {}:", .{code});
1190 printCmd(cwd, argv);
1191 return error.UncleanExit;
1192 }
1193 },
1194 else => {
1195 log.err("The following command terminated unexpectedly:", .{});
1196 printCmd(cwd, argv);
1197
1198 return error.UncleanExit;
1199 },
1200 }
1201}
1202
1203pub fn makePath(self: *Build, path: []const u8) !void {
1204 fs.cwd().makePath(self.pathFromRoot(path)) catch |err| {
1205 log.err("Unable to create path {s}: {s}", .{ path, @errorName(err) });
1206 return err;
1207 };
1208}
1209
1210pub fn installArtifact(self: *Build, artifact: *CompileStep) void {
1211 self.getInstallStep().dependOn(&self.addInstallArtifact(artifact).step);
1212}
1213
1214pub fn addInstallArtifact(self: *Build, artifact: *CompileStep) *InstallArtifactStep {
1215 return InstallArtifactStep.create(self, artifact);
1216}
1217
1218///`dest_rel_path` is relative to prefix path
1219pub fn installFile(self: *Build, src_path: []const u8, dest_rel_path: []const u8) void {
1220 self.getInstallStep().dependOn(&self.addInstallFileWithDir(.{ .path = src_path }, .prefix, dest_rel_path).step);
1221}
1222
1223pub fn installDirectory(self: *Build, options: InstallDirectoryOptions) void {
1224 self.getInstallStep().dependOn(&self.addInstallDirectory(options).step);
1225}
1226
1227///`dest_rel_path` is relative to bin path
1228pub fn installBinFile(self: *Build, src_path: []const u8, dest_rel_path: []const u8) void {
1229 self.getInstallStep().dependOn(&self.addInstallFileWithDir(.{ .path = src_path }, .bin, dest_rel_path).step);
1230}
1231
1232///`dest_rel_path` is relative to lib path
1233pub fn installLibFile(self: *Build, src_path: []const u8, dest_rel_path: []const u8) void {
1234 self.getInstallStep().dependOn(&self.addInstallFileWithDir(.{ .path = src_path }, .lib, dest_rel_path).step);
1235}
1236
1237/// Output format (BIN vs Intel HEX) determined by filename
1238pub fn installRaw(self: *Build, artifact: *CompileStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep {
1239 const raw = self.addInstallRaw(artifact, dest_filename, options);
1240 self.getInstallStep().dependOn(&raw.step);
1241 return raw;
1242}
1243
1244///`dest_rel_path` is relative to install prefix path
1245pub fn addInstallFile(self: *Build, source: FileSource, dest_rel_path: []const u8) *InstallFileStep {
1246 return self.addInstallFileWithDir(source.dupe(self), .prefix, dest_rel_path);
1247}
1248
1249///`dest_rel_path` is relative to bin path
1250pub fn addInstallBinFile(self: *Build, source: FileSource, dest_rel_path: []const u8) *InstallFileStep {
1251 return self.addInstallFileWithDir(source.dupe(self), .bin, dest_rel_path);
1252}
1253
1254///`dest_rel_path` is relative to lib path
1255pub fn addInstallLibFile(self: *Build, source: FileSource, dest_rel_path: []const u8) *InstallFileStep {
1256 return self.addInstallFileWithDir(source.dupe(self), .lib, dest_rel_path);
1257}
1258
1259pub fn addInstallHeaderFile(b: *Build, src_path: []const u8, dest_rel_path: []const u8) *InstallFileStep {
1260 return b.addInstallFileWithDir(.{ .path = src_path }, .header, dest_rel_path);
1261}
1262
1263pub fn addInstallRaw(self: *Build, artifact: *CompileStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep {
1264 return InstallRawStep.create(self, artifact, dest_filename, options);
1265}
1266
1267pub fn addInstallFileWithDir(
1268 self: *Build,
1269 source: FileSource,
1270 install_dir: InstallDir,
1271 dest_rel_path: []const u8,
1272) *InstallFileStep {
1273 if (dest_rel_path.len == 0) {
1274 panic("dest_rel_path must be non-empty", .{});
1275 }
1276 const install_step = self.allocator.create(InstallFileStep) catch @panic("OOM");
1277 install_step.* = InstallFileStep.init(self, source.dupe(self), install_dir, dest_rel_path);
1278 return install_step;
1279}
1280
1281pub fn addInstallDirectory(self: *Build, options: InstallDirectoryOptions) *InstallDirStep {
1282 const install_step = self.allocator.create(InstallDirStep) catch @panic("OOM");
1283 install_step.* = InstallDirStep.init(self, options);
1284 return install_step;
1285}
1286
1287pub fn pushInstalledFile(self: *Build, dir: InstallDir, dest_rel_path: []const u8) void {
1288 const file = InstalledFile{
1289 .dir = dir,
1290 .path = dest_rel_path,
1291 };
1292 self.installed_files.append(file.dupe(self)) catch @panic("OOM");
1293}
1294
1295pub fn updateFile(self: *Build, source_path: []const u8, dest_path: []const u8) !void {
1296 if (self.verbose) {
1297 log.info("cp {s} {s} ", .{ source_path, dest_path });
1298 }
1299 const cwd = fs.cwd();
1300 const prev_status = try fs.Dir.updateFile(cwd, source_path, cwd, dest_path, .{});
1301 if (self.verbose) switch (prev_status) {
1302 .stale => log.info("# installed", .{}),
1303 .fresh => log.info("# up-to-date", .{}),
1304 };
1305}
1306
1307pub fn truncateFile(self: *Build, dest_path: []const u8) !void {
1308 if (self.verbose) {
1309 log.info("truncate {s}", .{dest_path});
1310 }
1311 const cwd = fs.cwd();
1312 var src_file = cwd.createFile(dest_path, .{}) catch |err| switch (err) {
1313 error.FileNotFound => blk: {
1314 if (fs.path.dirname(dest_path)) |dirname| {
1315 try cwd.makePath(dirname);
1316 }
1317 break :blk try cwd.createFile(dest_path, .{});
1318 },
1319 else => |e| return e,
1320 };
1321 src_file.close();
1322}
1323
1324pub fn pathFromRoot(self: *Build, rel_path: []const u8) []u8 {
1325 return fs.path.resolve(self.allocator, &[_][]const u8{ self.build_root, rel_path }) catch @panic("OOM");
1326}
1327
1328pub fn pathJoin(self: *Build, paths: []const []const u8) []u8 {
1329 return fs.path.join(self.allocator, paths) catch @panic("OOM");
1330}
1331
1332pub fn fmt(self: *Build, comptime format: []const u8, args: anytype) []u8 {
1333 return fmt_lib.allocPrint(self.allocator, format, args) catch @panic("OOM");
1334}
1335
1336pub fn findProgram(self: *Build, names: []const []const u8, paths: []const []const u8) ![]const u8 {
1337 // TODO report error for ambiguous situations
1338 const exe_extension = @as(CrossTarget, .{}).exeFileExt();
1339 for (self.search_prefixes.items) |search_prefix| {
1340 for (names) |name| {
1341 if (fs.path.isAbsolute(name)) {
1342 return name;
1343 }
1344 const full_path = self.pathJoin(&.{
1345 search_prefix,
1346 "bin",
1347 self.fmt("{s}{s}", .{ name, exe_extension }),
1348 });
1349 return fs.realpathAlloc(self.allocator, full_path) catch continue;
1350 }
1351 }
1352 if (self.env_map.get("PATH")) |PATH| {
1353 for (names) |name| {
1354 if (fs.path.isAbsolute(name)) {
1355 return name;
1356 }
1357 var it = mem.tokenize(u8, PATH, &[_]u8{fs.path.delimiter});
1358 while (it.next()) |path| {
1359 const full_path = self.pathJoin(&.{
1360 path,
1361 self.fmt("{s}{s}", .{ name, exe_extension }),
1362 });
1363 return fs.realpathAlloc(self.allocator, full_path) catch continue;
1364 }
1365 }
1366 }
1367 for (names) |name| {
1368 if (fs.path.isAbsolute(name)) {
1369 return name;
1370 }
1371 for (paths) |path| {
1372 const full_path = self.pathJoin(&.{
1373 path,
1374 self.fmt("{s}{s}", .{ name, exe_extension }),
1375 });
1376 return fs.realpathAlloc(self.allocator, full_path) catch continue;
1377 }
1378 }
1379 return error.FileNotFound;
1380}
1381
1382pub fn execAllowFail(
1383 self: *Build,
1384 argv: []const []const u8,
1385 out_code: *u8,
1386 stderr_behavior: std.ChildProcess.StdIo,
1387) ExecError![]u8 {
1388 assert(argv.len != 0);
1389
1390 if (!std.process.can_spawn)
1391 return error.ExecNotSupported;
1392
1393 const max_output_size = 400 * 1024;
1394 var child = std.ChildProcess.init(argv, self.allocator);
1395 child.stdin_behavior = .Ignore;
1396 child.stdout_behavior = .Pipe;
1397 child.stderr_behavior = stderr_behavior;
1398 child.env_map = self.env_map;
1399
1400 try child.spawn();
1401
1402 const stdout = child.stdout.?.reader().readAllAlloc(self.allocator, max_output_size) catch {
1403 return error.ReadFailure;
1404 };
1405 errdefer self.allocator.free(stdout);
1406
1407 const term = try child.wait();
1408 switch (term) {
1409 .Exited => |code| {
1410 if (code != 0) {
1411 out_code.* = @truncate(u8, code);
1412 return error.ExitCodeFailure;
1413 }
1414 return stdout;
1415 },
1416 .Signal, .Stopped, .Unknown => |code| {
1417 out_code.* = @truncate(u8, code);
1418 return error.ProcessTerminated;
1419 },
1420 }
1421}
1422
1423pub fn execFromStep(self: *Build, argv: []const []const u8, src_step: ?*Step) ![]u8 {
1424 assert(argv.len != 0);
1425
1426 if (self.verbose) {
1427 printCmd(null, argv);
1428 }
1429
1430 if (!std.process.can_spawn) {
1431 if (src_step) |s| log.err("{s}...", .{s.name});
1432 log.err("Unable to spawn the following command: cannot spawn child process", .{});
1433 printCmd(null, argv);
1434 std.os.abort();
1435 }
1436
1437 var code: u8 = undefined;
1438 return self.execAllowFail(argv, &code, .Inherit) catch |err| switch (err) {
1439 error.ExecNotSupported => {
1440 if (src_step) |s| log.err("{s}...", .{s.name});
1441 log.err("Unable to spawn the following command: cannot spawn child process", .{});
1442 printCmd(null, argv);
1443 std.os.abort();
1444 },
1445 error.FileNotFound => {
1446 if (src_step) |s| log.err("{s}...", .{s.name});
1447 log.err("Unable to spawn the following command: file not found", .{});
1448 printCmd(null, argv);
1449 std.os.exit(@truncate(u8, code));
1450 },
1451 error.ExitCodeFailure => {
1452 if (src_step) |s| log.err("{s}...", .{s.name});
1453 if (self.prominent_compile_errors) {
1454 log.err("The step exited with error code {d}", .{code});
1455 } else {
1456 log.err("The following command exited with error code {d}:", .{code});
1457 printCmd(null, argv);
1458 }
1459
1460 std.os.exit(@truncate(u8, code));
1461 },
1462 error.ProcessTerminated => {
1463 if (src_step) |s| log.err("{s}...", .{s.name});
1464 log.err("The following command terminated unexpectedly:", .{});
1465 printCmd(null, argv);
1466 std.os.exit(@truncate(u8, code));
1467 },
1468 else => |e| return e,
1469 };
1470}
1471
1472pub fn exec(self: *Build, argv: []const []const u8) ![]u8 {
1473 return self.execFromStep(argv, null);
1474}
1475
1476pub fn addSearchPrefix(self: *Build, search_prefix: []const u8) void {
1477 self.search_prefixes.append(self.dupePath(search_prefix)) catch @panic("OOM");
1478}
1479
1480pub fn getInstallPath(self: *Build, dir: InstallDir, dest_rel_path: []const u8) []const u8 {
1481 assert(!fs.path.isAbsolute(dest_rel_path)); // Install paths must be relative to the prefix
1482 const base_dir = switch (dir) {
1483 .prefix => self.install_path,
1484 .bin => self.exe_dir,
1485 .lib => self.lib_dir,
1486 .header => self.h_dir,
1487 .custom => |path| self.pathJoin(&.{ self.install_path, path }),
1488 };
1489 return fs.path.resolve(
1490 self.allocator,
1491 &[_][]const u8{ base_dir, dest_rel_path },
1492 ) catch @panic("OOM");
1493}
1494
1495pub const Dependency = struct {
1496 builder: *Build,
1497
1498 pub fn artifact(d: *Dependency, name: []const u8) *CompileStep {
1499 var found: ?*CompileStep = null;
1500 for (d.builder.install_tls.step.dependencies.items) |dep_step| {
1501 const inst = dep_step.cast(InstallArtifactStep) orelse continue;
1502 if (mem.eql(u8, inst.artifact.name, name)) {
1503 if (found != null) panic("artifact name '{s}' is ambiguous", .{name});
1504 found = inst.artifact;
1505 }
1506 }
1507 return found orelse {
1508 for (d.builder.install_tls.step.dependencies.items) |dep_step| {
1509 const inst = dep_step.cast(InstallArtifactStep) orelse continue;
1510 log.info("available artifact: '{s}'", .{inst.artifact.name});
1511 }
1512 panic("unable to find artifact '{s}'", .{name});
1513 };
1514 }
1515
1516 pub fn module(d: *Dependency, name: []const u8) *Module {
1517 return d.builder.modules.get(name) orelse {
1518 panic("unable to find module '{s}'", .{name});
1519 };
1520 }
1521};
1522
1523pub fn dependency(b: *Build, name: []const u8, args: anytype) *Dependency {
1524 const build_runner = @import("root");
1525 const deps = build_runner.dependencies;
1526
1527 inline for (@typeInfo(deps.imports).Struct.decls) |decl| {
1528 if (mem.startsWith(u8, decl.name, b.dep_prefix) and
1529 mem.endsWith(u8, decl.name, name) and
1530 decl.name.len == b.dep_prefix.len + name.len)
1531 {
1532 const build_zig = @field(deps.imports, decl.name);
1533 const build_root = @field(deps.build_root, decl.name);
1534 return dependencyInner(b, name, build_root, build_zig, args);
1535 }
1536 }
1537
1538 const full_path = b.pathFromRoot("build.zig.zon");
1539 std.debug.print("no dependency named '{s}' in '{s}'. All packages used in build.zig must be declared in this file.\n", .{ name, full_path });
1540 std.process.exit(1);
1541}
1542
1543fn dependencyInner(
1544 b: *Build,
1545 name: []const u8,
1546 build_root: []const u8,
1547 comptime build_zig: type,
1548 args: anytype,
1549) *Dependency {
1550 const sub_builder = b.createChild(name, build_root, args) catch @panic("unhandled error");
1551 sub_builder.runBuild(build_zig) catch @panic("unhandled error");
1552
1553 if (sub_builder.validateUserInputDidItFail()) {
1554 std.debug.dumpCurrentStackTrace(@returnAddress());
1555 }
1556
1557 const dep = b.allocator.create(Dependency) catch @panic("OOM");
1558 dep.* = .{ .builder = sub_builder };
1559 return dep;
1560}
1561
1562pub fn runBuild(b: *Build, build_zig: anytype) anyerror!void {
1563 switch (@typeInfo(@typeInfo(@TypeOf(build_zig.build)).Fn.return_type.?)) {
1564 .Void => build_zig.build(b),
1565 .ErrorUnion => try build_zig.build(b),
1566 else => @compileError("expected return type of build to be 'void' or '!void'"),
1567 }
1568}
1569
1570test "builder.findProgram compiles" {
1571 if (builtin.os.tag == .wasi) return error.SkipZigTest;
1572
1573 var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
1574 defer arena.deinit();
1575
1576 const host = try NativeTargetInfo.detect(.{});
1577
1578 const builder = try Build.create(
1579 arena.allocator(),
1580 "zig",
1581 "zig-cache",
1582 "zig-cache",
1583 "zig-cache",
1584 host,
1585 );
1586 defer builder.destroy();
1587 _ = builder.findProgram(&[_][]const u8{}, &[_][]const u8{}) catch null;
1588}
1589
1590pub const Module = struct {
1591 builder: *Build,
1592 /// This could either be a generated file, in which case the module
1593 /// contains exactly one file, or it could be a path to the root source
1594 /// file of directory of files which constitute the module.
1595 source_file: FileSource,
1596 dependencies: std.StringArrayHashMap(*Module),
1597};
1598
1599/// A file that is generated by a build step.
1600/// This struct is an interface that is meant to be used with `@fieldParentPtr` to implement the actual path logic.
1601pub const GeneratedFile = struct {
1602 /// The step that generates the file
1603 step: *Step,
1604
1605 /// The path to the generated file. Must be either absolute or relative to the build root.
1606 /// This value must be set in the `fn make()` of the `step` and must not be `null` afterwards.
1607 path: ?[]const u8 = null,
1608
1609 pub fn getPath(self: GeneratedFile) []const u8 {
1610 return self.path orelse std.debug.panic(
1611 "getPath() was called on a GeneratedFile that wasn't build yet. Is there a missing Step dependency on step '{s}'?",
1612 .{self.step.name},
1613 );
1614 }
1615};
1616
1617/// A file source is a reference to an existing or future file.
1618///
1619pub const FileSource = union(enum) {
1620 /// A plain file path, relative to build root or absolute.
1621 path: []const u8,
1622
1623 /// A file that is generated by an interface. Those files usually are
1624 /// not available until built by a build step.
1625 generated: *const GeneratedFile,
1626
1627 /// Returns a new file source that will have a relative path to the build root guaranteed.
1628 /// This should be preferred over setting `.path` directly as it documents that the files are in the project directory.
1629 pub fn relative(path: []const u8) FileSource {
1630 std.debug.assert(!std.fs.path.isAbsolute(path));
1631 return FileSource{ .path = path };
1632 }
1633
1634 /// Returns a string that can be shown to represent the file source.
1635 /// Either returns the path or `"generated"`.
1636 pub fn getDisplayName(self: FileSource) []const u8 {
1637 return switch (self) {
1638 .path => self.path,
1639 .generated => "generated",
1640 };
1641 }
1642
1643 /// Adds dependencies this file source implies to the given step.
1644 pub fn addStepDependencies(self: FileSource, other_step: *Step) void {
1645 switch (self) {
1646 .path => {},
1647 .generated => |gen| other_step.dependOn(gen.step),
1648 }
1649 }
1650
1651 /// Should only be called during make(), returns a path relative to the build root or absolute.
1652 pub fn getPath(self: FileSource, builder: *Build) []const u8 {
1653 const path = switch (self) {
1654 .path => |p| builder.pathFromRoot(p),
1655 .generated => |gen| gen.getPath(),
1656 };
1657 return path;
1658 }
1659
1660 /// Duplicates the file source for a given builder.
1661 pub fn dupe(self: FileSource, b: *Build) FileSource {
1662 return switch (self) {
1663 .path => |p| .{ .path = b.dupePath(p) },
1664 .generated => |gen| .{ .generated = gen },
1665 };
1666 }
1667};
1668
1669/// Allocates a new string for assigning a value to a named macro.
1670/// If the value is omitted, it is set to 1.
1671/// `name` and `value` need not live longer than the function call.
1672pub fn constructCMacro(allocator: Allocator, name: []const u8, value: ?[]const u8) []const u8 {
1673 var macro = allocator.alloc(
1674 u8,
1675 name.len + if (value) |value_slice| value_slice.len + 1 else 0,
1676 ) catch |err| if (err == error.OutOfMemory) @panic("Out of memory") else unreachable;
1677 mem.copy(u8, macro, name);
1678 if (value) |value_slice| {
1679 macro[name.len] = '=';
1680 mem.copy(u8, macro[name.len + 1 ..], value_slice);
1681 }
1682 return macro;
1683}
1684
1685pub const VcpkgRoot = union(VcpkgRootStatus) {
1686 unattempted: void,
1687 not_found: void,
1688 found: []const u8,
1689};
1690
1691pub const VcpkgRootStatus = enum {
1692 unattempted,
1693 not_found,
1694 found,
1695};
1696
1697pub const InstallDir = union(enum) {
1698 prefix: void,
1699 lib: void,
1700 bin: void,
1701 header: void,
1702 /// A path relative to the prefix
1703 custom: []const u8,
1704
1705 /// Duplicates the install directory including the path if set to custom.
1706 pub fn dupe(self: InstallDir, builder: *Build) InstallDir {
1707 if (self == .custom) {
1708 // Written with this temporary to avoid RLS problems
1709 const duped_path = builder.dupe(self.custom);
1710 return .{ .custom = duped_path };
1711 } else {
1712 return self;
1713 }
1714 }
1715};
1716
1717pub const InstalledFile = struct {
1718 dir: InstallDir,
1719 path: []const u8,
1720
1721 /// Duplicates the installed file path and directory.
1722 pub fn dupe(self: InstalledFile, builder: *Build) InstalledFile {
1723 return .{
1724 .dir = self.dir.dupe(builder),
1725 .path = builder.dupe(self.path),
1726 };
1727 }
1728};
1729
1730pub fn serializeCpu(allocator: Allocator, cpu: std.Target.Cpu) ![]const u8 {
1731 // TODO this logic can disappear if cpu model + features becomes part of the target triple
1732 const all_features = cpu.arch.allFeaturesList();
1733 var populated_cpu_features = cpu.model.features;
1734 populated_cpu_features.populateDependencies(all_features);
1735
1736 if (populated_cpu_features.eql(cpu.features)) {
1737 // The CPU name alone is sufficient.
1738 return cpu.model.name;
1739 } else {
1740 var mcpu_buffer = ArrayList(u8).init(allocator);
1741 try mcpu_buffer.appendSlice(cpu.model.name);
1742
1743 for (all_features) |feature, i_usize| {
1744 const i = @intCast(std.Target.Cpu.Feature.Set.Index, i_usize);
1745 const in_cpu_set = populated_cpu_features.isEnabled(i);
1746 const in_actual_set = cpu.features.isEnabled(i);
1747 if (in_cpu_set and !in_actual_set) {
1748 try mcpu_buffer.writer().print("-{s}", .{feature.name});
1749 } else if (!in_cpu_set and in_actual_set) {
1750 try mcpu_buffer.writer().print("+{s}", .{feature.name});
1751 }
1752 }
1753
1754 return try mcpu_buffer.toOwnedSlice();
1755 }
1756}
1757
1758test {
1759 _ = CheckFileStep;
1760 _ = CheckObjectStep;
1761 _ = EmulatableRunStep;
1762 _ = FmtStep;
1763 _ = InstallArtifactStep;
1764 _ = InstallDirStep;
1765 _ = InstallFileStep;
1766 _ = InstallRawStep;
1767 _ = CompileStep;
1768 _ = LogStep;
1769 _ = OptionsStep;
1770 _ = RemoveDirStep;
1771 _ = RunStep;
1772 _ = TranslateCStep;
1773 _ = WriteFileStep;
1774}
lib/std/Build/CheckFileStep.zig created+51
...@@ -0,0 +1,51 @@
1const std = @import("../std.zig");
2const Step = std.Build.Step;
3const fs = std.fs;
4const mem = std.mem;
5
6const CheckFileStep = @This();
7
8pub const base_id = .check_file;
9
10step: Step,
11builder: *std.Build,
12expected_matches: []const []const u8,
13source: std.Build.FileSource,
14max_bytes: usize = 20 * 1024 * 1024,
15
16pub fn create(
17 builder: *std.Build,
18 source: std.Build.FileSource,
19 expected_matches: []const []const u8,
20) *CheckFileStep {
21 const self = builder.allocator.create(CheckFileStep) catch @panic("OOM");
22 self.* = CheckFileStep{
23 .builder = builder,
24 .step = Step.init(.check_file, "CheckFile", builder.allocator, make),
25 .source = source.dupe(builder),
26 .expected_matches = builder.dupeStrings(expected_matches),
27 };
28 self.source.addStepDependencies(&self.step);
29 return self;
30}
31
32fn make(step: *Step) !void {
33 const self = @fieldParentPtr(CheckFileStep, "step", step);
34
35 const src_path = self.source.getPath(self.builder);
36 const contents = try fs.cwd().readFileAlloc(self.builder.allocator, src_path, self.max_bytes);
37
38 for (self.expected_matches) |expected_match| {
39 if (mem.indexOf(u8, contents, expected_match) == null) {
40 std.debug.print(
41 \\
42 \\========= Expected to find: ===================
43 \\{s}
44 \\========= But file does not contain it: =======
45 \\{s}
46 \\
47 , .{ expected_match, contents });
48 return error.TestFailed;
49 }
50 }
51}
lib/std/Build/CheckObjectStep.zig created+1024
...@@ -0,0 +1,1024 @@
1const std = @import("../std.zig");
2const assert = std.debug.assert;
3const fs = std.fs;
4const macho = std.macho;
5const math = std.math;
6const mem = std.mem;
7const testing = std.testing;
8
9const CheckObjectStep = @This();
10
11const Allocator = mem.Allocator;
12const Step = std.Build.Step;
13const EmulatableRunStep = std.Build.EmulatableRunStep;
14
15pub const base_id = .check_object;
16
17step: Step,
18builder: *std.Build,
19source: std.Build.FileSource,
20max_bytes: usize = 20 * 1024 * 1024,
21checks: std.ArrayList(Check),
22dump_symtab: bool = false,
23obj_format: std.Target.ObjectFormat,
24
25pub fn create(builder: *std.Build, source: std.Build.FileSource, obj_format: std.Target.ObjectFormat) *CheckObjectStep {
26 const gpa = builder.allocator;
27 const self = gpa.create(CheckObjectStep) catch @panic("OOM");
28 self.* = .{
29 .builder = builder,
30 .step = Step.init(.check_file, "CheckObject", gpa, make),
31 .source = source.dupe(builder),
32 .checks = std.ArrayList(Check).init(gpa),
33 .obj_format = obj_format,
34 };
35 self.source.addStepDependencies(&self.step);
36 return self;
37}
38
39/// Runs and (optionally) compares the output of a binary.
40/// Asserts `self` was generated from an executable step.
41pub fn runAndCompare(self: *CheckObjectStep) *EmulatableRunStep {
42 const dependencies_len = self.step.dependencies.items.len;
43 assert(dependencies_len > 0);
44 const exe_step = self.step.dependencies.items[dependencies_len - 1];
45 const exe = exe_step.cast(std.Build.CompileStep).?;
46 const emulatable_step = EmulatableRunStep.create(self.builder, "EmulatableRun", exe);
47 emulatable_step.step.dependOn(&self.step);
48 return emulatable_step;
49}
50
51/// There two types of actions currently suported:
52/// * `.match` - is the main building block of standard matchers with optional eat-all token `{*}`
53/// and extractors by name such as `{n_value}`. Please note this action is very simplistic in nature
54/// i.e., it won't really handle edge cases/nontrivial examples. But given that we do want to use
55/// it mainly to test the output of our object format parser-dumpers when testing the linkers, etc.
56/// it should be plenty useful in its current form.
57/// * `.compute_cmp` - can be used to perform an operation on the extracted global variables
58/// using the MatchAction. It currently only supports an addition. The operation is required
59/// to be specified in Reverse Polish Notation to ease in operator-precedence parsing (well,
60/// to avoid any parsing really).
61/// For example, if the two extracted values were saved as `vmaddr` and `entryoff` respectively
62/// they could then be added with this simple program `vmaddr entryoff +`.
63const Action = struct {
64 tag: enum { match, not_present, compute_cmp },
65 phrase: []const u8,
66 expected: ?ComputeCompareExpected = null,
67
68 /// Will return true if the `phrase` was found in the `haystack`.
69 /// Some examples include:
70 ///
71 /// LC 0 => will match in its entirety
72 /// vmaddr {vmaddr} => will match `vmaddr` and then extract the following value as u64
73 /// and save under `vmaddr` global name (see `global_vars` param)
74 /// name {*}libobjc{*}.dylib => will match `name` followed by a token which contains `libobjc` and `.dylib`
75 /// in that order with other letters in between
76 fn match(act: Action, haystack: []const u8, global_vars: anytype) !bool {
77 assert(act.tag == .match or act.tag == .not_present);
78
79 var candidate_var: ?struct { name: []const u8, value: u64 } = null;
80 var hay_it = mem.tokenize(u8, mem.trim(u8, haystack, " "), " ");
81 var needle_it = mem.tokenize(u8, mem.trim(u8, act.phrase, " "), " ");
82
83 while (needle_it.next()) |needle_tok| {
84 const hay_tok = hay_it.next() orelse return false;
85
86 if (mem.indexOf(u8, needle_tok, "{*}")) |index| {
87 // We have fuzzy matchers within the search pattern, so we match substrings.
88 var start = index;
89 var n_tok = needle_tok;
90 var h_tok = hay_tok;
91 while (true) {
92 n_tok = n_tok[start + 3 ..];
93 const inner = if (mem.indexOf(u8, n_tok, "{*}")) |sub_end|
94 n_tok[0..sub_end]
95 else
96 n_tok;
97 if (mem.indexOf(u8, h_tok, inner) == null) return false;
98 start = mem.indexOf(u8, n_tok, "{*}") orelse break;
99 }
100 } else if (mem.startsWith(u8, needle_tok, "{")) {
101 const closing_brace = mem.indexOf(u8, needle_tok, "}") orelse return error.MissingClosingBrace;
102 if (closing_brace != needle_tok.len - 1) return error.ClosingBraceNotLast;
103
104 const name = needle_tok[1..closing_brace];
105 if (name.len == 0) return error.MissingBraceValue;
106 const value = try std.fmt.parseInt(u64, hay_tok, 16);
107 candidate_var = .{
108 .name = name,
109 .value = value,
110 };
111 } else {
112 if (!mem.eql(u8, hay_tok, needle_tok)) return false;
113 }
114 }
115
116 if (candidate_var) |v| {
117 try global_vars.putNoClobber(v.name, v.value);
118 }
119
120 return true;
121 }
122
123 /// Will return true if the `phrase` is correctly parsed into an RPN program and
124 /// its reduced, computed value compares using `op` with the expected value, either
125 /// a literal or another extracted variable.
126 fn computeCmp(act: Action, gpa: Allocator, global_vars: anytype) !bool {
127 var op_stack = std.ArrayList(enum { add, sub, mod, mul }).init(gpa);
128 var values = std.ArrayList(u64).init(gpa);
129
130 var it = mem.tokenize(u8, act.phrase, " ");
131 while (it.next()) |next| {
132 if (mem.eql(u8, next, "+")) {
133 try op_stack.append(.add);
134 } else if (mem.eql(u8, next, "-")) {
135 try op_stack.append(.sub);
136 } else if (mem.eql(u8, next, "%")) {
137 try op_stack.append(.mod);
138 } else if (mem.eql(u8, next, "*")) {
139 try op_stack.append(.mul);
140 } else {
141 const val = std.fmt.parseInt(u64, next, 0) catch blk: {
142 break :blk global_vars.get(next) orelse {
143 std.debug.print(
144 \\
145 \\========= Variable was not extracted: ===========
146 \\{s}
147 \\
148 , .{next});
149 return error.UnknownVariable;
150 };
151 };
152 try values.append(val);
153 }
154 }
155
156 var op_i: usize = 1;
157 var reduced: u64 = values.items[0];
158 for (op_stack.items) |op| {
159 const other = values.items[op_i];
160 switch (op) {
161 .add => {
162 reduced += other;
163 },
164 .sub => {
165 reduced -= other;
166 },
167 .mod => {
168 reduced %= other;
169 },
170 .mul => {
171 reduced *= other;
172 },
173 }
174 op_i += 1;
175 }
176
177 const exp_value = switch (act.expected.?.value) {
178 .variable => |name| global_vars.get(name) orelse {
179 std.debug.print(
180 \\
181 \\========= Variable was not extracted: ===========
182 \\{s}
183 \\
184 , .{name});
185 return error.UnknownVariable;
186 },
187 .literal => |x| x,
188 };
189 return math.compare(reduced, act.expected.?.op, exp_value);
190 }
191};
192
193const ComputeCompareExpected = struct {
194 op: math.CompareOperator,
195 value: union(enum) {
196 variable: []const u8,
197 literal: u64,
198 },
199
200 pub fn format(
201 value: @This(),
202 comptime fmt: []const u8,
203 options: std.fmt.FormatOptions,
204 writer: anytype,
205 ) !void {
206 if (fmt.len != 0) std.fmt.invalidFmtError(fmt, value);
207 _ = options;
208 try writer.print("{s} ", .{@tagName(value.op)});
209 switch (value.value) {
210 .variable => |name| try writer.writeAll(name),
211 .literal => |x| try writer.print("{x}", .{x}),
212 }
213 }
214};
215
216const Check = struct {
217 builder: *std.Build,
218 actions: std.ArrayList(Action),
219
220 fn create(b: *std.Build) Check {
221 return .{
222 .builder = b,
223 .actions = std.ArrayList(Action).init(b.allocator),
224 };
225 }
226
227 fn match(self: *Check, phrase: []const u8) void {
228 self.actions.append(.{
229 .tag = .match,
230 .phrase = self.builder.dupe(phrase),
231 }) catch @panic("OOM");
232 }
233
234 fn notPresent(self: *Check, phrase: []const u8) void {
235 self.actions.append(.{
236 .tag = .not_present,
237 .phrase = self.builder.dupe(phrase),
238 }) catch @panic("OOM");
239 }
240
241 fn computeCmp(self: *Check, phrase: []const u8, expected: ComputeCompareExpected) void {
242 self.actions.append(.{
243 .tag = .compute_cmp,
244 .phrase = self.builder.dupe(phrase),
245 .expected = expected,
246 }) catch @panic("OOM");
247 }
248};
249
250/// Creates a new sequence of actions with `phrase` as the first anchor searched phrase.
251pub fn checkStart(self: *CheckObjectStep, phrase: []const u8) void {
252 var new_check = Check.create(self.builder);
253 new_check.match(phrase);
254 self.checks.append(new_check) catch @panic("OOM");
255}
256
257/// Adds another searched phrase to the latest created Check with `CheckObjectStep.checkStart(...)`.
258/// Asserts at least one check already exists.
259pub fn checkNext(self: *CheckObjectStep, phrase: []const u8) void {
260 assert(self.checks.items.len > 0);
261 const last = &self.checks.items[self.checks.items.len - 1];
262 last.match(phrase);
263}
264
265/// Adds another searched phrase to the latest created Check with `CheckObjectStep.checkStart(...)`
266/// however ensures there is no matching phrase in the output.
267/// Asserts at least one check already exists.
268pub fn checkNotPresent(self: *CheckObjectStep, phrase: []const u8) void {
269 assert(self.checks.items.len > 0);
270 const last = &self.checks.items[self.checks.items.len - 1];
271 last.notPresent(phrase);
272}
273
274/// Creates a new check checking specifically symbol table parsed and dumped from the object
275/// file.
276/// Issuing this check will force parsing and dumping of the symbol table.
277pub fn checkInSymtab(self: *CheckObjectStep) void {
278 self.dump_symtab = true;
279 const symtab_label = switch (self.obj_format) {
280 .macho => MachODumper.symtab_label,
281 else => @panic("TODO other parsers"),
282 };
283 self.checkStart(symtab_label);
284}
285
286/// Creates a new standalone, singular check which allows running simple binary operations
287/// on the extracted variables. It will then compare the reduced program with the value of
288/// the expected variable.
289pub fn checkComputeCompare(
290 self: *CheckObjectStep,
291 program: []const u8,
292 expected: ComputeCompareExpected,
293) void {
294 var new_check = Check.create(self.builder);
295 new_check.computeCmp(program, expected);
296 self.checks.append(new_check) catch @panic("OOM");
297}
298
299fn make(step: *Step) !void {
300 const self = @fieldParentPtr(CheckObjectStep, "step", step);
301
302 const gpa = self.builder.allocator;
303 const src_path = self.source.getPath(self.builder);
304 const contents = try fs.cwd().readFileAllocOptions(
305 gpa,
306 src_path,
307 self.max_bytes,
308 null,
309 @alignOf(u64),
310 null,
311 );
312
313 const output = switch (self.obj_format) {
314 .macho => try MachODumper.parseAndDump(contents, .{
315 .gpa = gpa,
316 .dump_symtab = self.dump_symtab,
317 }),
318 .elf => @panic("TODO elf parser"),
319 .coff => @panic("TODO coff parser"),
320 .wasm => try WasmDumper.parseAndDump(contents, .{
321 .gpa = gpa,
322 .dump_symtab = self.dump_symtab,
323 }),
324 else => unreachable,
325 };
326
327 var vars = std.StringHashMap(u64).init(gpa);
328
329 for (self.checks.items) |chk| {
330 var it = mem.tokenize(u8, output, "\r\n");
331 for (chk.actions.items) |act| {
332 switch (act.tag) {
333 .match => {
334 while (it.next()) |line| {
335 if (try act.match(line, &vars)) break;
336 } else {
337 std.debug.print(
338 \\
339 \\========= Expected to find: ==========================
340 \\{s}
341 \\========= But parsed file does not contain it: =======
342 \\{s}
343 \\
344 , .{ act.phrase, output });
345 return error.TestFailed;
346 }
347 },
348 .not_present => {
349 while (it.next()) |line| {
350 if (try act.match(line, &vars)) {
351 std.debug.print(
352 \\
353 \\========= Expected not to find: ===================
354 \\{s}
355 \\========= But parsed file does contain it: ========
356 \\{s}
357 \\
358 , .{ act.phrase, output });
359 return error.TestFailed;
360 }
361 }
362 },
363 .compute_cmp => {
364 const res = act.computeCmp(gpa, vars) catch |err| switch (err) {
365 error.UnknownVariable => {
366 std.debug.print(
367 \\========= From parsed file: =====================
368 \\{s}
369 \\
370 , .{output});
371 return error.TestFailed;
372 },
373 else => |e| return e,
374 };
375 if (!res) {
376 std.debug.print(
377 \\
378 \\========= Comparison failed for action: ===========
379 \\{s} {}
380 \\========= From parsed file: =======================
381 \\{s}
382 \\
383 , .{ act.phrase, act.expected.?, output });
384 return error.TestFailed;
385 }
386 },
387 }
388 }
389 }
390}
391
392const Opts = struct {
393 gpa: ?Allocator = null,
394 dump_symtab: bool = false,
395};
396
397const MachODumper = struct {
398 const LoadCommandIterator = macho.LoadCommandIterator;
399 const symtab_label = "symtab";
400
401 fn parseAndDump(bytes: []align(@alignOf(u64)) const u8, opts: Opts) ![]const u8 {
402 const gpa = opts.gpa orelse unreachable; // MachO dumper requires an allocator
403 var stream = std.io.fixedBufferStream(bytes);
404 const reader = stream.reader();
405
406 const hdr = try reader.readStruct(macho.mach_header_64);
407 if (hdr.magic != macho.MH_MAGIC_64) {
408 return error.InvalidMagicNumber;
409 }
410
411 var output = std.ArrayList(u8).init(gpa);
412 const writer = output.writer();
413
414 var symtab: []const macho.nlist_64 = undefined;
415 var strtab: []const u8 = undefined;
416 var sections = std.ArrayList(macho.section_64).init(gpa);
417 var imports = std.ArrayList([]const u8).init(gpa);
418
419 var it = LoadCommandIterator{
420 .ncmds = hdr.ncmds,
421 .buffer = bytes[@sizeOf(macho.mach_header_64)..][0..hdr.sizeofcmds],
422 };
423 var i: usize = 0;
424 while (it.next()) |cmd| {
425 switch (cmd.cmd()) {
426 .SEGMENT_64 => {
427 const seg = cmd.cast(macho.segment_command_64).?;
428 try sections.ensureUnusedCapacity(seg.nsects);
429 for (cmd.getSections()) |sect| {
430 sections.appendAssumeCapacity(sect);
431 }
432 },
433 .SYMTAB => if (opts.dump_symtab) {
434 const lc = cmd.cast(macho.symtab_command).?;
435 symtab = @ptrCast(
436 [*]const macho.nlist_64,
437 @alignCast(@alignOf(macho.nlist_64), &bytes[lc.symoff]),
438 )[0..lc.nsyms];
439 strtab = bytes[lc.stroff..][0..lc.strsize];
440 },
441 .LOAD_DYLIB,
442 .LOAD_WEAK_DYLIB,
443 .REEXPORT_DYLIB,
444 => {
445 try imports.append(cmd.getDylibPathName());
446 },
447 else => {},
448 }
449
450 try dumpLoadCommand(cmd, i, writer);
451 try writer.writeByte('\n');
452
453 i += 1;
454 }
455
456 if (opts.dump_symtab) {
457 try writer.print("{s}\n", .{symtab_label});
458 for (symtab) |sym| {
459 if (sym.stab()) continue;
460 const sym_name = mem.sliceTo(@ptrCast([*:0]const u8, strtab.ptr + sym.n_strx), 0);
461 if (sym.sect()) {
462 const sect = sections.items[sym.n_sect - 1];
463 try writer.print("{x} ({s},{s})", .{
464 sym.n_value,
465 sect.segName(),
466 sect.sectName(),
467 });
468 if (sym.ext()) {
469 try writer.writeAll(" external");
470 }
471 try writer.print(" {s}\n", .{sym_name});
472 } else if (sym.undf()) {
473 const ordinal = @divTrunc(@bitCast(i16, sym.n_desc), macho.N_SYMBOL_RESOLVER);
474 const import_name = blk: {
475 if (ordinal <= 0) {
476 if (ordinal == macho.BIND_SPECIAL_DYLIB_SELF)
477 break :blk "self import";
478 if (ordinal == macho.BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE)
479 break :blk "main executable";
480 if (ordinal == macho.BIND_SPECIAL_DYLIB_FLAT_LOOKUP)
481 break :blk "flat lookup";
482 unreachable;
483 }
484 const full_path = imports.items[@bitCast(u16, ordinal) - 1];
485 const basename = fs.path.basename(full_path);
486 assert(basename.len > 0);
487 const ext = mem.lastIndexOfScalar(u8, basename, '.') orelse basename.len;
488 break :blk basename[0..ext];
489 };
490 try writer.writeAll("(undefined)");
491 if (sym.weakRef()) {
492 try writer.writeAll(" weak");
493 }
494 if (sym.ext()) {
495 try writer.writeAll(" external");
496 }
497 try writer.print(" {s} (from {s})\n", .{
498 sym_name,
499 import_name,
500 });
501 } else unreachable;
502 }
503 }
504
505 return output.toOwnedSlice();
506 }
507
508 fn dumpLoadCommand(lc: macho.LoadCommandIterator.LoadCommand, index: usize, writer: anytype) !void {
509 // print header first
510 try writer.print(
511 \\LC {d}
512 \\cmd {s}
513 \\cmdsize {d}
514 , .{ index, @tagName(lc.cmd()), lc.cmdsize() });
515
516 switch (lc.cmd()) {
517 .SEGMENT_64 => {
518 const seg = lc.cast(macho.segment_command_64).?;
519 try writer.writeByte('\n');
520 try writer.print(
521 \\segname {s}
522 \\vmaddr {x}
523 \\vmsize {x}
524 \\fileoff {x}
525 \\filesz {x}
526 , .{
527 seg.segName(),
528 seg.vmaddr,
529 seg.vmsize,
530 seg.fileoff,
531 seg.filesize,
532 });
533
534 for (lc.getSections()) |sect| {
535 try writer.writeByte('\n');
536 try writer.print(
537 \\sectname {s}
538 \\addr {x}
539 \\size {x}
540 \\offset {x}
541 \\align {x}
542 , .{
543 sect.sectName(),
544 sect.addr,
545 sect.size,
546 sect.offset,
547 sect.@"align",
548 });
549 }
550 },
551
552 .ID_DYLIB,
553 .LOAD_DYLIB,
554 .LOAD_WEAK_DYLIB,
555 .REEXPORT_DYLIB,
556 => {
557 const dylib = lc.cast(macho.dylib_command).?;
558 try writer.writeByte('\n');
559 try writer.print(
560 \\name {s}
561 \\timestamp {d}
562 \\current version {x}
563 \\compatibility version {x}
564 , .{
565 lc.getDylibPathName(),
566 dylib.dylib.timestamp,
567 dylib.dylib.current_version,
568 dylib.dylib.compatibility_version,
569 });
570 },
571
572 .MAIN => {
573 const main = lc.cast(macho.entry_point_command).?;
574 try writer.writeByte('\n');
575 try writer.print(
576 \\entryoff {x}
577 \\stacksize {x}
578 , .{ main.entryoff, main.stacksize });
579 },
580
581 .RPATH => {
582 try writer.writeByte('\n');
583 try writer.print(
584 \\path {s}
585 , .{
586 lc.getRpathPathName(),
587 });
588 },
589
590 .UUID => {
591 const uuid = lc.cast(macho.uuid_command).?;
592 try writer.writeByte('\n');
593 try writer.print("uuid {x}", .{std.fmt.fmtSliceHexLower(&uuid.uuid)});
594 },
595
596 .DATA_IN_CODE,
597 .FUNCTION_STARTS,
598 .CODE_SIGNATURE,
599 => {
600 const llc = lc.cast(macho.linkedit_data_command).?;
601 try writer.writeByte('\n');
602 try writer.print(
603 \\dataoff {x}
604 \\datasize {x}
605 , .{ llc.dataoff, llc.datasize });
606 },
607
608 .DYLD_INFO_ONLY => {
609 const dlc = lc.cast(macho.dyld_info_command).?;
610 try writer.writeByte('\n');
611 try writer.print(
612 \\rebaseoff {x}
613 \\rebasesize {x}
614 \\bindoff {x}
615 \\bindsize {x}
616 \\weakbindoff {x}
617 \\weakbindsize {x}
618 \\lazybindoff {x}
619 \\lazybindsize {x}
620 \\exportoff {x}
621 \\exportsize {x}
622 , .{
623 dlc.rebase_off,
624 dlc.rebase_size,
625 dlc.bind_off,
626 dlc.bind_size,
627 dlc.weak_bind_off,
628 dlc.weak_bind_size,
629 dlc.lazy_bind_off,
630 dlc.lazy_bind_size,
631 dlc.export_off,
632 dlc.export_size,
633 });
634 },
635
636 .SYMTAB => {
637 const slc = lc.cast(macho.symtab_command).?;
638 try writer.writeByte('\n');
639 try writer.print(
640 \\symoff {x}
641 \\nsyms {x}
642 \\stroff {x}
643 \\strsize {x}
644 , .{
645 slc.symoff,
646 slc.nsyms,
647 slc.stroff,
648 slc.strsize,
649 });
650 },
651
652 .DYSYMTAB => {
653 const dlc = lc.cast(macho.dysymtab_command).?;
654 try writer.writeByte('\n');
655 try writer.print(
656 \\ilocalsym {x}
657 \\nlocalsym {x}
658 \\iextdefsym {x}
659 \\nextdefsym {x}
660 \\iundefsym {x}
661 \\nundefsym {x}
662 \\indirectsymoff {x}
663 \\nindirectsyms {x}
664 , .{
665 dlc.ilocalsym,
666 dlc.nlocalsym,
667 dlc.iextdefsym,
668 dlc.nextdefsym,
669 dlc.iundefsym,
670 dlc.nundefsym,
671 dlc.indirectsymoff,
672 dlc.nindirectsyms,
673 });
674 },
675
676 else => {},
677 }
678 }
679};
680
681const WasmDumper = struct {
682 const symtab_label = "symbols";
683
684 fn parseAndDump(bytes: []const u8, opts: Opts) ![]const u8 {
685 const gpa = opts.gpa orelse unreachable; // Wasm dumper requires an allocator
686 if (opts.dump_symtab) {
687 @panic("TODO: Implement symbol table parsing and dumping");
688 }
689
690 var fbs = std.io.fixedBufferStream(bytes);
691 const reader = fbs.reader();
692
693 const buf = try reader.readBytesNoEof(8);
694 if (!mem.eql(u8, buf[0..4], &std.wasm.magic)) {
695 return error.InvalidMagicByte;
696 }
697 if (!mem.eql(u8, buf[4..], &std.wasm.version)) {
698 return error.UnsupportedWasmVersion;
699 }
700
701 var output = std.ArrayList(u8).init(gpa);
702 errdefer output.deinit();
703 const writer = output.writer();
704
705 while (reader.readByte()) |current_byte| {
706 const section = std.meta.intToEnum(std.wasm.Section, current_byte) catch |err| {
707 std.debug.print("Found invalid section id '{d}'\n", .{current_byte});
708 return err;
709 };
710
711 const section_length = try std.leb.readULEB128(u32, reader);
712 try parseAndDumpSection(section, bytes[fbs.pos..][0..section_length], writer);
713 fbs.pos += section_length;
714 } else |_| {} // reached end of stream
715
716 return output.toOwnedSlice();
717 }
718
719 fn parseAndDumpSection(section: std.wasm.Section, data: []const u8, writer: anytype) !void {
720 var fbs = std.io.fixedBufferStream(data);
721 const reader = fbs.reader();
722
723 try writer.print(
724 \\Section {s}
725 \\size {d}
726 , .{ @tagName(section), data.len });
727
728 switch (section) {
729 .type,
730 .import,
731 .function,
732 .table,
733 .memory,
734 .global,
735 .@"export",
736 .element,
737 .code,
738 .data,
739 => {
740 const entries = try std.leb.readULEB128(u32, reader);
741 try writer.print("\nentries {d}\n", .{entries});
742 try dumpSection(section, data[fbs.pos..], entries, writer);
743 },
744 .custom => {
745 const name_length = try std.leb.readULEB128(u32, reader);
746 const name = data[fbs.pos..][0..name_length];
747 fbs.pos += name_length;
748 try writer.print("\nname {s}\n", .{name});
749
750 if (mem.eql(u8, name, "name")) {
751 try parseDumpNames(reader, writer, data);
752 } else if (mem.eql(u8, name, "producers")) {
753 try parseDumpProducers(reader, writer, data);
754 } else if (mem.eql(u8, name, "target_features")) {
755 try parseDumpFeatures(reader, writer, data);
756 }
757 // TODO: Implement parsing and dumping other custom sections (such as relocations)
758 },
759 .start => {
760 const start = try std.leb.readULEB128(u32, reader);
761 try writer.print("\nstart {d}\n", .{start});
762 },
763 else => {}, // skip unknown sections
764 }
765 }
766
767 fn dumpSection(section: std.wasm.Section, data: []const u8, entries: u32, writer: anytype) !void {
768 var fbs = std.io.fixedBufferStream(data);
769 const reader = fbs.reader();
770
771 switch (section) {
772 .type => {
773 var i: u32 = 0;
774 while (i < entries) : (i += 1) {
775 const func_type = try reader.readByte();
776 if (func_type != std.wasm.function_type) {
777 std.debug.print("Expected function type, found byte '{d}'\n", .{func_type});
778 return error.UnexpectedByte;
779 }
780 const params = try std.leb.readULEB128(u32, reader);
781 try writer.print("params {d}\n", .{params});
782 var index: u32 = 0;
783 while (index < params) : (index += 1) {
784 try parseDumpType(std.wasm.Valtype, reader, writer);
785 } else index = 0;
786 const returns = try std.leb.readULEB128(u32, reader);
787 try writer.print("returns {d}\n", .{returns});
788 while (index < returns) : (index += 1) {
789 try parseDumpType(std.wasm.Valtype, reader, writer);
790 }
791 }
792 },
793 .import => {
794 var i: u32 = 0;
795 while (i < entries) : (i += 1) {
796 const module_name_len = try std.leb.readULEB128(u32, reader);
797 const module_name = data[fbs.pos..][0..module_name_len];
798 fbs.pos += module_name_len;
799 const name_len = try std.leb.readULEB128(u32, reader);
800 const name = data[fbs.pos..][0..name_len];
801 fbs.pos += name_len;
802
803 const kind = std.meta.intToEnum(std.wasm.ExternalKind, try reader.readByte()) catch |err| {
804 std.debug.print("Invalid import kind\n", .{});
805 return err;
806 };
807
808 try writer.print(
809 \\module {s}
810 \\name {s}
811 \\kind {s}
812 , .{ module_name, name, @tagName(kind) });
813 try writer.writeByte('\n');
814 switch (kind) {
815 .function => {
816 try writer.print("index {d}\n", .{try std.leb.readULEB128(u32, reader)});
817 },
818 .memory => {
819 try parseDumpLimits(reader, writer);
820 },
821 .global => {
822 try parseDumpType(std.wasm.Valtype, reader, writer);
823 try writer.print("mutable {}\n", .{0x01 == try std.leb.readULEB128(u32, reader)});
824 },
825 .table => {
826 try parseDumpType(std.wasm.RefType, reader, writer);
827 try parseDumpLimits(reader, writer);
828 },
829 }
830 }
831 },
832 .function => {
833 var i: u32 = 0;
834 while (i < entries) : (i += 1) {
835 try writer.print("index {d}\n", .{try std.leb.readULEB128(u32, reader)});
836 }
837 },
838 .table => {
839 var i: u32 = 0;
840 while (i < entries) : (i += 1) {
841 try parseDumpType(std.wasm.RefType, reader, writer);
842 try parseDumpLimits(reader, writer);
843 }
844 },
845 .memory => {
846 var i: u32 = 0;
847 while (i < entries) : (i += 1) {
848 try parseDumpLimits(reader, writer);
849 }
850 },
851 .global => {
852 var i: u32 = 0;
853 while (i < entries) : (i += 1) {
854 try parseDumpType(std.wasm.Valtype, reader, writer);
855 try writer.print("mutable {}\n", .{0x01 == try std.leb.readULEB128(u1, reader)});
856 try parseDumpInit(reader, writer);
857 }
858 },
859 .@"export" => {
860 var i: u32 = 0;
861 while (i < entries) : (i += 1) {
862 const name_len = try std.leb.readULEB128(u32, reader);
863 const name = data[fbs.pos..][0..name_len];
864 fbs.pos += name_len;
865 const kind_byte = try std.leb.readULEB128(u8, reader);
866 const kind = std.meta.intToEnum(std.wasm.ExternalKind, kind_byte) catch |err| {
867 std.debug.print("invalid export kind value '{d}'\n", .{kind_byte});
868 return err;
869 };
870 const index = try std.leb.readULEB128(u32, reader);
871 try writer.print(
872 \\name {s}
873 \\kind {s}
874 \\index {d}
875 , .{ name, @tagName(kind), index });
876 try writer.writeByte('\n');
877 }
878 },
879 .element => {
880 var i: u32 = 0;
881 while (i < entries) : (i += 1) {
882 try writer.print("table index {d}\n", .{try std.leb.readULEB128(u32, reader)});
883 try parseDumpInit(reader, writer);
884
885 const function_indexes = try std.leb.readULEB128(u32, reader);
886 var function_index: u32 = 0;
887 try writer.print("indexes {d}\n", .{function_indexes});
888 while (function_index < function_indexes) : (function_index += 1) {
889 try writer.print("index {d}\n", .{try std.leb.readULEB128(u32, reader)});
890 }
891 }
892 },
893 .code => {}, // code section is considered opaque to linker
894 .data => {
895 var i: u32 = 0;
896 while (i < entries) : (i += 1) {
897 const index = try std.leb.readULEB128(u32, reader);
898 try writer.print("memory index 0x{x}\n", .{index});
899 try parseDumpInit(reader, writer);
900 const size = try std.leb.readULEB128(u32, reader);
901 try writer.print("size {d}\n", .{size});
902 try reader.skipBytes(size, .{}); // we do not care about the content of the segments
903 }
904 },
905 else => unreachable,
906 }
907 }
908
909 fn parseDumpType(comptime WasmType: type, reader: anytype, writer: anytype) !void {
910 const type_byte = try reader.readByte();
911 const valtype = std.meta.intToEnum(WasmType, type_byte) catch |err| {
912 std.debug.print("Invalid wasm type value '{d}'\n", .{type_byte});
913 return err;
914 };
915 try writer.print("type {s}\n", .{@tagName(valtype)});
916 }
917
918 fn parseDumpLimits(reader: anytype, writer: anytype) !void {
919 const flags = try std.leb.readULEB128(u8, reader);
920 const min = try std.leb.readULEB128(u32, reader);
921
922 try writer.print("min {x}\n", .{min});
923 if (flags != 0) {
924 try writer.print("max {x}\n", .{try std.leb.readULEB128(u32, reader)});
925 }
926 }
927
928 fn parseDumpInit(reader: anytype, writer: anytype) !void {
929 const byte = try std.leb.readULEB128(u8, reader);
930 const opcode = std.meta.intToEnum(std.wasm.Opcode, byte) catch |err| {
931 std.debug.print("invalid wasm opcode '{d}'\n", .{byte});
932 return err;
933 };
934 switch (opcode) {
935 .i32_const => try writer.print("i32.const {x}\n", .{try std.leb.readILEB128(i32, reader)}),
936 .i64_const => try writer.print("i64.const {x}\n", .{try std.leb.readILEB128(i64, reader)}),
937 .f32_const => try writer.print("f32.const {x}\n", .{@bitCast(f32, try reader.readIntLittle(u32))}),
938 .f64_const => try writer.print("f64.const {x}\n", .{@bitCast(f64, try reader.readIntLittle(u64))}),
939 .global_get => try writer.print("global.get {x}\n", .{try std.leb.readULEB128(u32, reader)}),
940 else => unreachable,
941 }
942 const end_opcode = try std.leb.readULEB128(u8, reader);
943 if (end_opcode != std.wasm.opcode(.end)) {
944 std.debug.print("expected 'end' opcode in init expression\n", .{});
945 return error.MissingEndOpcode;
946 }
947 }
948
949 fn parseDumpNames(reader: anytype, writer: anytype, data: []const u8) !void {
950 while (reader.context.pos < data.len) {
951 try parseDumpType(std.wasm.NameSubsection, reader, writer);
952 const size = try std.leb.readULEB128(u32, reader);
953 const entries = try std.leb.readULEB128(u32, reader);
954 try writer.print(
955 \\size {d}
956 \\names {d}
957 , .{ size, entries });
958 try writer.writeByte('\n');
959 var i: u32 = 0;
960 while (i < entries) : (i += 1) {
961 const index = try std.leb.readULEB128(u32, reader);
962 const name_len = try std.leb.readULEB128(u32, reader);
963 const pos = reader.context.pos;
964 const name = data[pos..][0..name_len];
965 reader.context.pos += name_len;
966
967 try writer.print(
968 \\index {d}
969 \\name {s}
970 , .{ index, name });
971 try writer.writeByte('\n');
972 }
973 }
974 }
975
976 fn parseDumpProducers(reader: anytype, writer: anytype, data: []const u8) !void {
977 const field_count = try std.leb.readULEB128(u32, reader);
978 try writer.print("fields {d}\n", .{field_count});
979 var current_field: u32 = 0;
980 while (current_field < field_count) : (current_field += 1) {
981 const field_name_length = try std.leb.readULEB128(u32, reader);
982 const field_name = data[reader.context.pos..][0..field_name_length];
983 reader.context.pos += field_name_length;
984
985 const value_count = try std.leb.readULEB128(u32, reader);
986 try writer.print(
987 \\field_name {s}
988 \\values {d}
989 , .{ field_name, value_count });
990 try writer.writeByte('\n');
991 var current_value: u32 = 0;
992 while (current_value < value_count) : (current_value += 1) {
993 const value_length = try std.leb.readULEB128(u32, reader);
994 const value = data[reader.context.pos..][0..value_length];
995 reader.context.pos += value_length;
996
997 const version_length = try std.leb.readULEB128(u32, reader);
998 const version = data[reader.context.pos..][0..version_length];
999 reader.context.pos += version_length;
1000
1001 try writer.print(
1002 \\value_name {s}
1003 \\version {s}
1004 , .{ value, version });
1005 try writer.writeByte('\n');
1006 }
1007 }
1008 }
1009
1010 fn parseDumpFeatures(reader: anytype, writer: anytype, data: []const u8) !void {
1011 const feature_count = try std.leb.readULEB128(u32, reader);
1012 try writer.print("features {d}\n", .{feature_count});
1013
1014 var index: u32 = 0;
1015 while (index < feature_count) : (index += 1) {
1016 const prefix_byte = try std.leb.readULEB128(u8, reader);
1017 const name_length = try std.leb.readULEB128(u32, reader);
1018 const feature_name = data[reader.context.pos..][0..name_length];
1019 reader.context.pos += name_length;
1020
1021 try writer.print("{c} {s}\n", .{ prefix_byte, feature_name });
1022 }
1023 }
1024};
lib/std/Build/CompileStep.zig created+2043
...@@ -0,0 +1,2043 @@
1const builtin = @import("builtin");
2const std = @import("../std.zig");
3const mem = std.mem;
4const log = std.log;
5const fs = std.fs;
6const assert = std.debug.assert;
7const panic = std.debug.panic;
8const ArrayList = std.ArrayList;
9const StringHashMap = std.StringHashMap;
10const Sha256 = std.crypto.hash.sha2.Sha256;
11const Allocator = mem.Allocator;
12const Step = std.Build.Step;
13const CrossTarget = std.zig.CrossTarget;
14const NativeTargetInfo = std.zig.system.NativeTargetInfo;
15const FileSource = std.Build.FileSource;
16const PkgConfigPkg = std.Build.PkgConfigPkg;
17const PkgConfigError = std.Build.PkgConfigError;
18const ExecError = std.Build.ExecError;
19const Module = std.Build.Module;
20const VcpkgRoot = std.Build.VcpkgRoot;
21const InstallDir = std.Build.InstallDir;
22const InstallArtifactStep = std.Build.InstallArtifactStep;
23const GeneratedFile = std.Build.GeneratedFile;
24const InstallRawStep = std.Build.InstallRawStep;
25const EmulatableRunStep = std.Build.EmulatableRunStep;
26const CheckObjectStep = std.Build.CheckObjectStep;
27const RunStep = std.Build.RunStep;
28const OptionsStep = std.Build.OptionsStep;
29const ConfigHeaderStep = std.Build.ConfigHeaderStep;
30const CompileStep = @This();
31
32pub const base_id: Step.Id = .compile;
33
34step: Step,
35builder: *std.Build,
36name: []const u8,
37target: CrossTarget,
38target_info: NativeTargetInfo,
39optimize: std.builtin.Mode,
40linker_script: ?FileSource = null,
41version_script: ?[]const u8 = null,
42out_filename: []const u8,
43linkage: ?Linkage = null,
44version: ?std.builtin.Version,
45kind: Kind,
46major_only_filename: ?[]const u8,
47name_only_filename: ?[]const u8,
48strip: ?bool,
49unwind_tables: ?bool,
50// keep in sync with src/link.zig:CompressDebugSections
51compress_debug_sections: enum { none, zlib } = .none,
52lib_paths: ArrayList([]const u8),
53rpaths: ArrayList([]const u8),
54framework_dirs: ArrayList([]const u8),
55frameworks: StringHashMap(FrameworkLinkInfo),
56verbose_link: bool,
57verbose_cc: bool,
58emit_analysis: EmitOption = .default,
59emit_asm: EmitOption = .default,
60emit_bin: EmitOption = .default,
61emit_docs: EmitOption = .default,
62emit_implib: EmitOption = .default,
63emit_llvm_bc: EmitOption = .default,
64emit_llvm_ir: EmitOption = .default,
65// Lots of things depend on emit_h having a consistent path,
66// so it is not an EmitOption for now.
67emit_h: bool = false,
68bundle_compiler_rt: ?bool = null,
69single_threaded: ?bool = null,
70stack_protector: ?bool = null,
71disable_stack_probing: bool,
72disable_sanitize_c: bool,
73sanitize_thread: bool,
74rdynamic: bool,
75import_memory: bool = false,
76/// For WebAssembly targets, this will allow for undefined symbols to
77/// be imported from the host environment.
78import_symbols: bool = false,
79import_table: bool = false,
80export_table: bool = false,
81initial_memory: ?u64 = null,
82max_memory: ?u64 = null,
83shared_memory: bool = false,
84global_base: ?u64 = null,
85c_std: std.Build.CStd,
86override_lib_dir: ?[]const u8,
87main_pkg_path: ?[]const u8,
88exec_cmd_args: ?[]const ?[]const u8,
89name_prefix: []const u8,
90filter: ?[]const u8,
91test_evented_io: bool = false,
92test_runner: ?[]const u8,
93code_model: std.builtin.CodeModel = .default,
94wasi_exec_model: ?std.builtin.WasiExecModel = null,
95/// Symbols to be exported when compiling to wasm
96export_symbol_names: []const []const u8 = &.{},
97
98root_src: ?FileSource,
99out_h_filename: []const u8,
100out_lib_filename: []const u8,
101out_pdb_filename: []const u8,
102modules: std.StringArrayHashMap(*Module),
103
104object_src: []const u8,
105
106link_objects: ArrayList(LinkObject),
107include_dirs: ArrayList(IncludeDir),
108c_macros: ArrayList([]const u8),
109installed_headers: ArrayList(*Step),
110output_dir: ?[]const u8,
111is_linking_libc: bool = false,
112is_linking_libcpp: bool = false,
113vcpkg_bin_path: ?[]const u8 = null,
114
115/// This may be set in order to override the default install directory
116override_dest_dir: ?InstallDir,
117installed_path: ?[]const u8,
118install_step: ?*InstallArtifactStep,
119
120/// Base address for an executable image.
121image_base: ?u64 = null,
122
123libc_file: ?FileSource = null,
124
125valgrind_support: ?bool = null,
126each_lib_rpath: ?bool = null,
127/// On ELF targets, this will emit a link section called ".note.gnu.build-id"
128/// which can be used to coordinate a stripped binary with its debug symbols.
129/// As an example, the bloaty project refuses to work unless its inputs have
130/// build ids, in order to prevent accidental mismatches.
131/// The default is to not include this section because it slows down linking.
132build_id: ?bool = null,
133
134/// Create a .eh_frame_hdr section and a PT_GNU_EH_FRAME segment in the ELF
135/// file.
136link_eh_frame_hdr: bool = false,
137link_emit_relocs: bool = false,
138
139/// Place every function in its own section so that unused ones may be
140/// safely garbage-collected during the linking phase.
141link_function_sections: bool = false,
142
143/// Remove functions and data that are unreachable by the entry point or
144/// exported symbols.
145link_gc_sections: ?bool = null,
146
147linker_allow_shlib_undefined: ?bool = null,
148
149/// Permit read-only relocations in read-only segments. Disallowed by default.
150link_z_notext: bool = false,
151
152/// Force all relocations to be read-only after processing.
153link_z_relro: bool = true,
154
155/// Allow relocations to be lazily processed after load.
156link_z_lazy: bool = false,
157
158/// Common page size
159link_z_common_page_size: ?u64 = null,
160
161/// Maximum page size
162link_z_max_page_size: ?u64 = null,
163
164/// (Darwin) Install name for the dylib
165install_name: ?[]const u8 = null,
166
167/// (Darwin) Path to entitlements file
168entitlements: ?[]const u8 = null,
169
170/// (Darwin) Size of the pagezero segment.
171pagezero_size: ?u64 = null,
172
173/// (Darwin) Search strategy for searching system libraries. Either `paths_first` or `dylibs_first`.
174/// The former lowers to `-search_paths_first` linker option, while the latter to `-search_dylibs_first`
175/// option.
176/// By default, if no option is specified, the linker assumes `paths_first` as the default
177/// search strategy.
178search_strategy: ?enum { paths_first, dylibs_first } = null,
179
180/// (Darwin) Set size of the padding between the end of load commands
181/// and start of `__TEXT,__text` section.
182headerpad_size: ?u32 = null,
183
184/// (Darwin) Automatically Set size of the padding between the end of load commands
185/// and start of `__TEXT,__text` section to a value fitting all paths expanded to MAXPATHLEN.
186headerpad_max_install_names: bool = false,
187
188/// (Darwin) Remove dylibs that are unreachable by the entry point or exported symbols.
189dead_strip_dylibs: bool = false,
190
191/// Position Independent Code
192force_pic: ?bool = null,
193
194/// Position Independent Executable
195pie: ?bool = null,
196
197red_zone: ?bool = null,
198
199omit_frame_pointer: ?bool = null,
200dll_export_fns: ?bool = null,
201
202subsystem: ?std.Target.SubSystem = null,
203
204entry_symbol_name: ?[]const u8 = null,
205
206/// Overrides the default stack size
207stack_size: ?u64 = null,
208
209want_lto: ?bool = null,
210use_llvm: ?bool = null,
211use_lld: ?bool = null,
212
213output_path_source: GeneratedFile,
214output_lib_path_source: GeneratedFile,
215output_h_path_source: GeneratedFile,
216output_pdb_path_source: GeneratedFile,
217
218pub const CSourceFiles = struct {
219 files: []const []const u8,
220 flags: []const []const u8,
221};
222
223pub const CSourceFile = struct {
224 source: FileSource,
225 args: []const []const u8,
226
227 pub fn dupe(self: CSourceFile, b: *std.Build) CSourceFile {
228 return .{
229 .source = self.source.dupe(b),
230 .args = b.dupeStrings(self.args),
231 };
232 }
233};
234
235pub const LinkObject = union(enum) {
236 static_path: FileSource,
237 other_step: *CompileStep,
238 system_lib: SystemLib,
239 assembly_file: FileSource,
240 c_source_file: *CSourceFile,
241 c_source_files: *CSourceFiles,
242};
243
244pub const SystemLib = struct {
245 name: []const u8,
246 needed: bool,
247 weak: bool,
248 use_pkg_config: enum {
249 /// Don't use pkg-config, just pass -lfoo where foo is name.
250 no,
251 /// Try to get information on how to link the library from pkg-config.
252 /// If that fails, fall back to passing -lfoo where foo is name.
253 yes,
254 /// Try to get information on how to link the library from pkg-config.
255 /// If that fails, error out.
256 force,
257 },
258};
259
260const FrameworkLinkInfo = struct {
261 needed: bool = false,
262 weak: bool = false,
263};
264
265pub const IncludeDir = union(enum) {
266 raw_path: []const u8,
267 raw_path_system: []const u8,
268 other_step: *CompileStep,
269 config_header_step: *ConfigHeaderStep,
270};
271
272pub const Options = struct {
273 name: []const u8,
274 root_source_file: ?FileSource = null,
275 target: CrossTarget,
276 optimize: std.builtin.Mode,
277 kind: Kind,
278 linkage: ?Linkage = null,
279 version: ?std.builtin.Version = null,
280};
281
282pub const Kind = enum {
283 exe,
284 lib,
285 obj,
286 @"test",
287 test_exe,
288};
289
290pub const Linkage = enum { dynamic, static };
291
292pub const EmitOption = union(enum) {
293 default: void,
294 no_emit: void,
295 emit: void,
296 emit_to: []const u8,
297
298 fn getArg(self: @This(), b: *std.Build, arg_name: []const u8) ?[]const u8 {
299 return switch (self) {
300 .no_emit => b.fmt("-fno-{s}", .{arg_name}),
301 .default => null,
302 .emit => b.fmt("-f{s}", .{arg_name}),
303 .emit_to => |path| b.fmt("-f{s}={s}", .{ arg_name, path }),
304 };
305 }
306};
307
308pub fn create(builder: *std.Build, options: Options) *CompileStep {
309 const name = builder.dupe(options.name);
310 const root_src: ?FileSource = if (options.root_source_file) |rsrc| rsrc.dupe(builder) else null;
311 if (mem.indexOf(u8, name, "/") != null or mem.indexOf(u8, name, "\\") != null) {
312 panic("invalid name: '{s}'. It looks like a file path, but it is supposed to be the library or application name.", .{name});
313 }
314
315 const self = builder.allocator.create(CompileStep) catch @panic("OOM");
316 self.* = CompileStep{
317 .strip = null,
318 .unwind_tables = null,
319 .builder = builder,
320 .verbose_link = false,
321 .verbose_cc = false,
322 .optimize = options.optimize,
323 .target = options.target,
324 .linkage = options.linkage,
325 .kind = options.kind,
326 .root_src = root_src,
327 .name = name,
328 .frameworks = StringHashMap(FrameworkLinkInfo).init(builder.allocator),
329 .step = Step.init(base_id, name, builder.allocator, make),
330 .version = options.version,
331 .out_filename = undefined,
332 .out_h_filename = builder.fmt("{s}.h", .{name}),
333 .out_lib_filename = undefined,
334 .out_pdb_filename = builder.fmt("{s}.pdb", .{name}),
335 .major_only_filename = null,
336 .name_only_filename = null,
337 .modules = std.StringArrayHashMap(*Module).init(builder.allocator),
338 .include_dirs = ArrayList(IncludeDir).init(builder.allocator),
339 .link_objects = ArrayList(LinkObject).init(builder.allocator),
340 .c_macros = ArrayList([]const u8).init(builder.allocator),
341 .lib_paths = ArrayList([]const u8).init(builder.allocator),
342 .rpaths = ArrayList([]const u8).init(builder.allocator),
343 .framework_dirs = ArrayList([]const u8).init(builder.allocator),
344 .installed_headers = ArrayList(*Step).init(builder.allocator),
345 .object_src = undefined,
346 .c_std = std.Build.CStd.C99,
347 .override_lib_dir = null,
348 .main_pkg_path = null,
349 .exec_cmd_args = null,
350 .name_prefix = "",
351 .filter = null,
352 .test_runner = null,
353 .disable_stack_probing = false,
354 .disable_sanitize_c = false,
355 .sanitize_thread = false,
356 .rdynamic = false,
357 .output_dir = null,
358 .override_dest_dir = null,
359 .installed_path = null,
360 .install_step = null,
361
362 .output_path_source = GeneratedFile{ .step = &self.step },
363 .output_lib_path_source = GeneratedFile{ .step = &self.step },
364 .output_h_path_source = GeneratedFile{ .step = &self.step },
365 .output_pdb_path_source = GeneratedFile{ .step = &self.step },
366
367 .target_info = NativeTargetInfo.detect(self.target) catch @panic("unhandled error"),
368 };
369 self.computeOutFileNames();
370 if (root_src) |rs| rs.addStepDependencies(&self.step);
371 return self;
372}
373
374fn computeOutFileNames(self: *CompileStep) void {
375 const target = self.target_info.target;
376
377 self.out_filename = std.zig.binNameAlloc(self.builder.allocator, .{
378 .root_name = self.name,
379 .target = target,
380 .output_mode = switch (self.kind) {
381 .lib => .Lib,
382 .obj => .Obj,
383 .exe, .@"test", .test_exe => .Exe,
384 },
385 .link_mode = if (self.linkage) |some| @as(std.builtin.LinkMode, switch (some) {
386 .dynamic => .Dynamic,
387 .static => .Static,
388 }) else null,
389 .version = self.version,
390 }) catch @panic("OOM");
391
392 if (self.kind == .lib) {
393 if (self.linkage != null and self.linkage.? == .static) {
394 self.out_lib_filename = self.out_filename;
395 } else if (self.version) |version| {
396 if (target.isDarwin()) {
397 self.major_only_filename = self.builder.fmt("lib{s}.{d}.dylib", .{
398 self.name,
399 version.major,
400 });
401 self.name_only_filename = self.builder.fmt("lib{s}.dylib", .{self.name});
402 self.out_lib_filename = self.out_filename;
403 } else if (target.os.tag == .windows) {
404 self.out_lib_filename = self.builder.fmt("{s}.lib", .{self.name});
405 } else {
406 self.major_only_filename = self.builder.fmt("lib{s}.so.{d}", .{ self.name, version.major });
407 self.name_only_filename = self.builder.fmt("lib{s}.so", .{self.name});
408 self.out_lib_filename = self.out_filename;
409 }
410 } else {
411 if (target.isDarwin()) {
412 self.out_lib_filename = self.out_filename;
413 } else if (target.os.tag == .windows) {
414 self.out_lib_filename = self.builder.fmt("{s}.lib", .{self.name});
415 } else {
416 self.out_lib_filename = self.out_filename;
417 }
418 }
419 if (self.output_dir != null) {
420 self.output_lib_path_source.path = self.builder.pathJoin(
421 &.{ self.output_dir.?, self.out_lib_filename },
422 );
423 }
424 }
425}
426
427pub fn setOutputDir(self: *CompileStep, dir: []const u8) void {
428 self.output_dir = self.builder.dupePath(dir);
429}
430
431pub fn install(self: *CompileStep) void {
432 self.builder.installArtifact(self);
433}
434
435pub fn installRaw(self: *CompileStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep {
436 return self.builder.installRaw(self, dest_filename, options);
437}
438
439pub fn installHeader(a: *CompileStep, src_path: []const u8, dest_rel_path: []const u8) void {
440 const install_file = a.builder.addInstallHeaderFile(src_path, dest_rel_path);
441 a.builder.getInstallStep().dependOn(&install_file.step);
442 a.installed_headers.append(&install_file.step) catch @panic("OOM");
443}
444
445pub const InstallConfigHeaderOptions = struct {
446 install_dir: InstallDir = .header,
447 dest_rel_path: ?[]const u8 = null,
448};
449
450pub fn installConfigHeader(
451 cs: *CompileStep,
452 config_header: *ConfigHeaderStep,
453 options: InstallConfigHeaderOptions,
454) void {
455 const dest_rel_path = options.dest_rel_path orelse config_header.include_path;
456 const install_file = cs.builder.addInstallFileWithDir(
457 .{ .generated = &config_header.output_file },
458 options.install_dir,
459 dest_rel_path,
460 );
461 cs.builder.getInstallStep().dependOn(&install_file.step);
462 cs.installed_headers.append(&install_file.step) catch @panic("OOM");
463}
464
465pub fn installHeadersDirectory(
466 a: *CompileStep,
467 src_dir_path: []const u8,
468 dest_rel_path: []const u8,
469) void {
470 return installHeadersDirectoryOptions(a, .{
471 .source_dir = src_dir_path,
472 .install_dir = .header,
473 .install_subdir = dest_rel_path,
474 });
475}
476
477pub fn installHeadersDirectoryOptions(
478 a: *CompileStep,
479 options: std.Build.InstallDirStep.Options,
480) void {
481 const install_dir = a.builder.addInstallDirectory(options);
482 a.builder.getInstallStep().dependOn(&install_dir.step);
483 a.installed_headers.append(&install_dir.step) catch @panic("OOM");
484}
485
486pub fn installLibraryHeaders(a: *CompileStep, l: *CompileStep) void {
487 assert(l.kind == .lib);
488 const install_step = a.builder.getInstallStep();
489 // Copy each element from installed_headers, modifying the builder
490 // to be the new parent's builder.
491 for (l.installed_headers.items) |step| {
492 const step_copy = switch (step.id) {
493 inline .install_file, .install_dir => |id| blk: {
494 const T = id.Type();
495 const ptr = a.builder.allocator.create(T) catch @panic("OOM");
496 ptr.* = step.cast(T).?.*;
497 ptr.override_source_builder = ptr.builder;
498 ptr.builder = a.builder;
499 break :blk &ptr.step;
500 },
501 else => unreachable,
502 };
503 a.installed_headers.append(step_copy) catch @panic("OOM");
504 install_step.dependOn(step_copy);
505 }
506 a.installed_headers.appendSlice(l.installed_headers.items) catch @panic("OOM");
507}
508
509/// Creates a `RunStep` with an executable built with `addExecutable`.
510/// Add command line arguments with `addArg`.
511pub fn run(exe: *CompileStep) *RunStep {
512 assert(exe.kind == .exe or exe.kind == .test_exe);
513
514 // It doesn't have to be native. We catch that if you actually try to run it.
515 // Consider that this is declarative; the run step may not be run unless a user
516 // option is supplied.
517 const run_step = RunStep.create(exe.builder, exe.builder.fmt("run {s}", .{exe.step.name}));
518 run_step.addArtifactArg(exe);
519
520 if (exe.kind == .test_exe) {
521 run_step.addArg(exe.builder.zig_exe);
522 }
523
524 if (exe.vcpkg_bin_path) |path| {
525 run_step.addPathDir(path);
526 }
527
528 return run_step;
529}
530
531/// Creates an `EmulatableRunStep` with an executable built with `addExecutable`.
532/// Allows running foreign binaries through emulation platforms such as Qemu or Rosetta.
533/// When a binary cannot be ran through emulation or the option is disabled, a warning
534/// will be printed and the binary will *NOT* be ran.
535pub fn runEmulatable(exe: *CompileStep) *EmulatableRunStep {
536 assert(exe.kind == .exe or exe.kind == .test_exe);
537
538 const run_step = EmulatableRunStep.create(exe.builder, exe.builder.fmt("run {s}", .{exe.step.name}), exe);
539 if (exe.vcpkg_bin_path) |path| {
540 RunStep.addPathDirInternal(&run_step.step, exe.builder, path);
541 }
542 return run_step;
543}
544
545pub fn checkObject(self: *CompileStep, obj_format: std.Target.ObjectFormat) *CheckObjectStep {
546 return CheckObjectStep.create(self.builder, self.getOutputSource(), obj_format);
547}
548
549pub fn setLinkerScriptPath(self: *CompileStep, source: FileSource) void {
550 self.linker_script = source.dupe(self.builder);
551 source.addStepDependencies(&self.step);
552}
553
554pub fn linkFramework(self: *CompileStep, framework_name: []const u8) void {
555 self.frameworks.put(self.builder.dupe(framework_name), .{}) catch @panic("OOM");
556}
557
558pub fn linkFrameworkNeeded(self: *CompileStep, framework_name: []const u8) void {
559 self.frameworks.put(self.builder.dupe(framework_name), .{
560 .needed = true,
561 }) catch @panic("OOM");
562}
563
564pub fn linkFrameworkWeak(self: *CompileStep, framework_name: []const u8) void {
565 self.frameworks.put(self.builder.dupe(framework_name), .{
566 .weak = true,
567 }) catch @panic("OOM");
568}
569
570/// Returns whether the library, executable, or object depends on a particular system library.
571pub fn dependsOnSystemLibrary(self: CompileStep, name: []const u8) bool {
572 if (isLibCLibrary(name)) {
573 return self.is_linking_libc;
574 }
575 if (isLibCppLibrary(name)) {
576 return self.is_linking_libcpp;
577 }
578 for (self.link_objects.items) |link_object| {
579 switch (link_object) {
580 .system_lib => |lib| if (mem.eql(u8, lib.name, name)) return true,
581 else => continue,
582 }
583 }
584 return false;
585}
586
587pub fn linkLibrary(self: *CompileStep, lib: *CompileStep) void {
588 assert(lib.kind == .lib);
589 self.linkLibraryOrObject(lib);
590}
591
592pub fn isDynamicLibrary(self: *CompileStep) bool {
593 return self.kind == .lib and self.linkage == Linkage.dynamic;
594}
595
596pub fn isStaticLibrary(self: *CompileStep) bool {
597 return self.kind == .lib and self.linkage != Linkage.dynamic;
598}
599
600pub fn producesPdbFile(self: *CompileStep) bool {
601 if (!self.target.isWindows() and !self.target.isUefi()) return false;
602 if (self.target.getObjectFormat() == .c) return false;
603 if (self.strip == true) return false;
604 return self.isDynamicLibrary() or self.kind == .exe or self.kind == .test_exe;
605}
606
607pub fn linkLibC(self: *CompileStep) void {
608 self.is_linking_libc = true;
609}
610
611pub fn linkLibCpp(self: *CompileStep) void {
612 self.is_linking_libcpp = true;
613}
614
615/// If the value is omitted, it is set to 1.
616/// `name` and `value` need not live longer than the function call.
617pub fn defineCMacro(self: *CompileStep, name: []const u8, value: ?[]const u8) void {
618 const macro = std.Build.constructCMacro(self.builder.allocator, name, value);
619 self.c_macros.append(macro) catch @panic("OOM");
620}
621
622/// name_and_value looks like [name]=[value]. If the value is omitted, it is set to 1.
623pub fn defineCMacroRaw(self: *CompileStep, name_and_value: []const u8) void {
624 self.c_macros.append(self.builder.dupe(name_and_value)) catch @panic("OOM");
625}
626
627/// This one has no integration with anything, it just puts -lname on the command line.
628/// Prefer to use `linkSystemLibrary` instead.
629pub fn linkSystemLibraryName(self: *CompileStep, name: []const u8) void {
630 self.link_objects.append(.{
631 .system_lib = .{
632 .name = self.builder.dupe(name),
633 .needed = false,
634 .weak = false,
635 .use_pkg_config = .no,
636 },
637 }) catch @panic("OOM");
638}
639
640/// This one has no integration with anything, it just puts -needed-lname on the command line.
641/// Prefer to use `linkSystemLibraryNeeded` instead.
642pub fn linkSystemLibraryNeededName(self: *CompileStep, name: []const u8) void {
643 self.link_objects.append(.{
644 .system_lib = .{
645 .name = self.builder.dupe(name),
646 .needed = true,
647 .weak = false,
648 .use_pkg_config = .no,
649 },
650 }) catch @panic("OOM");
651}
652
653/// Darwin-only. This one has no integration with anything, it just puts -weak-lname on the
654/// command line. Prefer to use `linkSystemLibraryWeak` instead.
655pub fn linkSystemLibraryWeakName(self: *CompileStep, name: []const u8) void {
656 self.link_objects.append(.{
657 .system_lib = .{
658 .name = self.builder.dupe(name),
659 .needed = false,
660 .weak = true,
661 .use_pkg_config = .no,
662 },
663 }) catch @panic("OOM");
664}
665
666/// This links against a system library, exclusively using pkg-config to find the library.
667/// Prefer to use `linkSystemLibrary` instead.
668pub fn linkSystemLibraryPkgConfigOnly(self: *CompileStep, lib_name: []const u8) void {
669 self.link_objects.append(.{
670 .system_lib = .{
671 .name = self.builder.dupe(lib_name),
672 .needed = false,
673 .weak = false,
674 .use_pkg_config = .force,
675 },
676 }) catch @panic("OOM");
677}
678
679/// This links against a system library, exclusively using pkg-config to find the library.
680/// Prefer to use `linkSystemLibraryNeeded` instead.
681pub fn linkSystemLibraryNeededPkgConfigOnly(self: *CompileStep, lib_name: []const u8) void {
682 self.link_objects.append(.{
683 .system_lib = .{
684 .name = self.builder.dupe(lib_name),
685 .needed = true,
686 .weak = false,
687 .use_pkg_config = .force,
688 },
689 }) catch @panic("OOM");
690}
691
692/// Run pkg-config for the given library name and parse the output, returning the arguments
693/// that should be passed to zig to link the given library.
694pub fn runPkgConfig(self: *CompileStep, lib_name: []const u8) ![]const []const u8 {
695 const pkg_name = match: {
696 // First we have to map the library name to pkg config name. Unfortunately,
697 // there are several examples where this is not straightforward:
698 // -lSDL2 -> pkg-config sdl2
699 // -lgdk-3 -> pkg-config gdk-3.0
700 // -latk-1.0 -> pkg-config atk
701 const pkgs = try getPkgConfigList(self.builder);
702
703 // Exact match means instant winner.
704 for (pkgs) |pkg| {
705 if (mem.eql(u8, pkg.name, lib_name)) {
706 break :match pkg.name;
707 }
708 }
709
710 // Next we'll try ignoring case.
711 for (pkgs) |pkg| {
712 if (std.ascii.eqlIgnoreCase(pkg.name, lib_name)) {
713 break :match pkg.name;
714 }
715 }
716
717 // Now try appending ".0".
718 for (pkgs) |pkg| {
719 if (std.ascii.indexOfIgnoreCase(pkg.name, lib_name)) |pos| {
720 if (pos != 0) continue;
721 if (mem.eql(u8, pkg.name[lib_name.len..], ".0")) {
722 break :match pkg.name;
723 }
724 }
725 }
726
727 // Trimming "-1.0".
728 if (mem.endsWith(u8, lib_name, "-1.0")) {
729 const trimmed_lib_name = lib_name[0 .. lib_name.len - "-1.0".len];
730 for (pkgs) |pkg| {
731 if (std.ascii.eqlIgnoreCase(pkg.name, trimmed_lib_name)) {
732 break :match pkg.name;
733 }
734 }
735 }
736
737 return error.PackageNotFound;
738 };
739
740 var code: u8 = undefined;
741 const stdout = if (self.builder.execAllowFail(&[_][]const u8{
742 "pkg-config",
743 pkg_name,
744 "--cflags",
745 "--libs",
746 }, &code, .Ignore)) |stdout| stdout else |err| switch (err) {
747 error.ProcessTerminated => return error.PkgConfigCrashed,
748 error.ExecNotSupported => return error.PkgConfigFailed,
749 error.ExitCodeFailure => return error.PkgConfigFailed,
750 error.FileNotFound => return error.PkgConfigNotInstalled,
751 error.ChildExecFailed => return error.PkgConfigFailed,
752 else => return err,
753 };
754
755 var zig_args = ArrayList([]const u8).init(self.builder.allocator);
756 defer zig_args.deinit();
757
758 var it = mem.tokenize(u8, stdout, " \r\n\t");
759 while (it.next()) |tok| {
760 if (mem.eql(u8, tok, "-I")) {
761 const dir = it.next() orelse return error.PkgConfigInvalidOutput;
762 try zig_args.appendSlice(&[_][]const u8{ "-I", dir });
763 } else if (mem.startsWith(u8, tok, "-I")) {
764 try zig_args.append(tok);
765 } else if (mem.eql(u8, tok, "-L")) {
766 const dir = it.next() orelse return error.PkgConfigInvalidOutput;
767 try zig_args.appendSlice(&[_][]const u8{ "-L", dir });
768 } else if (mem.startsWith(u8, tok, "-L")) {
769 try zig_args.append(tok);
770 } else if (mem.eql(u8, tok, "-l")) {
771 const lib = it.next() orelse return error.PkgConfigInvalidOutput;
772 try zig_args.appendSlice(&[_][]const u8{ "-l", lib });
773 } else if (mem.startsWith(u8, tok, "-l")) {
774 try zig_args.append(tok);
775 } else if (mem.eql(u8, tok, "-D")) {
776 const macro = it.next() orelse return error.PkgConfigInvalidOutput;
777 try zig_args.appendSlice(&[_][]const u8{ "-D", macro });
778 } else if (mem.startsWith(u8, tok, "-D")) {
779 try zig_args.append(tok);
780 } else if (self.builder.verbose) {
781 log.warn("Ignoring pkg-config flag '{s}'", .{tok});
782 }
783 }
784
785 return zig_args.toOwnedSlice();
786}
787
788pub fn linkSystemLibrary(self: *CompileStep, name: []const u8) void {
789 self.linkSystemLibraryInner(name, .{});
790}
791
792pub fn linkSystemLibraryNeeded(self: *CompileStep, name: []const u8) void {
793 self.linkSystemLibraryInner(name, .{ .needed = true });
794}
795
796pub fn linkSystemLibraryWeak(self: *CompileStep, name: []const u8) void {
797 self.linkSystemLibraryInner(name, .{ .weak = true });
798}
799
800fn linkSystemLibraryInner(self: *CompileStep, name: []const u8, opts: struct {
801 needed: bool = false,
802 weak: bool = false,
803}) void {
804 if (isLibCLibrary(name)) {
805 self.linkLibC();
806 return;
807 }
808 if (isLibCppLibrary(name)) {
809 self.linkLibCpp();
810 return;
811 }
812
813 self.link_objects.append(.{
814 .system_lib = .{
815 .name = self.builder.dupe(name),
816 .needed = opts.needed,
817 .weak = opts.weak,
818 .use_pkg_config = .yes,
819 },
820 }) catch @panic("OOM");
821}
822
823pub fn setNamePrefix(self: *CompileStep, text: []const u8) void {
824 assert(self.kind == .@"test" or self.kind == .test_exe);
825 self.name_prefix = self.builder.dupe(text);
826}
827
828pub fn setFilter(self: *CompileStep, text: ?[]const u8) void {
829 assert(self.kind == .@"test" or self.kind == .test_exe);
830 self.filter = if (text) |t| self.builder.dupe(t) else null;
831}
832
833pub fn setTestRunner(self: *CompileStep, path: ?[]const u8) void {
834 assert(self.kind == .@"test" or self.kind == .test_exe);
835 self.test_runner = if (path) |p| self.builder.dupePath(p) else null;
836}
837
838/// Handy when you have many C/C++ source files and want them all to have the same flags.
839pub fn addCSourceFiles(self: *CompileStep, files: []const []const u8, flags: []const []const u8) void {
840 const c_source_files = self.builder.allocator.create(CSourceFiles) catch @panic("OOM");
841
842 const files_copy = self.builder.dupeStrings(files);
843 const flags_copy = self.builder.dupeStrings(flags);
844
845 c_source_files.* = .{
846 .files = files_copy,
847 .flags = flags_copy,
848 };
849 self.link_objects.append(.{ .c_source_files = c_source_files }) catch @panic("OOM");
850}
851
852pub fn addCSourceFile(self: *CompileStep, file: []const u8, flags: []const []const u8) void {
853 self.addCSourceFileSource(.{
854 .args = flags,
855 .source = .{ .path = file },
856 });
857}
858
859pub fn addCSourceFileSource(self: *CompileStep, source: CSourceFile) void {
860 const c_source_file = self.builder.allocator.create(CSourceFile) catch @panic("OOM");
861 c_source_file.* = source.dupe(self.builder);
862 self.link_objects.append(.{ .c_source_file = c_source_file }) catch @panic("OOM");
863 source.source.addStepDependencies(&self.step);
864}
865
866pub fn setVerboseLink(self: *CompileStep, value: bool) void {
867 self.verbose_link = value;
868}
869
870pub fn setVerboseCC(self: *CompileStep, value: bool) void {
871 self.verbose_cc = value;
872}
873
874pub fn overrideZigLibDir(self: *CompileStep, dir_path: []const u8) void {
875 self.override_lib_dir = self.builder.dupePath(dir_path);
876}
877
878pub fn setMainPkgPath(self: *CompileStep, dir_path: []const u8) void {
879 self.main_pkg_path = self.builder.dupePath(dir_path);
880}
881
882pub fn setLibCFile(self: *CompileStep, libc_file: ?FileSource) void {
883 self.libc_file = if (libc_file) |f| f.dupe(self.builder) else null;
884}
885
886/// Returns the generated executable, library or object file.
887/// To run an executable built with zig build, use `run`, or create an install step and invoke it.
888pub fn getOutputSource(self: *CompileStep) FileSource {
889 return FileSource{ .generated = &self.output_path_source };
890}
891
892/// Returns the generated import library. This function can only be called for libraries.
893pub fn getOutputLibSource(self: *CompileStep) FileSource {
894 assert(self.kind == .lib);
895 return FileSource{ .generated = &self.output_lib_path_source };
896}
897
898/// Returns the generated header file.
899/// This function can only be called for libraries or object files which have `emit_h` set.
900pub fn getOutputHSource(self: *CompileStep) FileSource {
901 assert(self.kind != .exe and self.kind != .test_exe and self.kind != .@"test");
902 assert(self.emit_h);
903 return FileSource{ .generated = &self.output_h_path_source };
904}
905
906/// Returns the generated PDB file. This function can only be called for Windows and UEFI.
907pub fn getOutputPdbSource(self: *CompileStep) FileSource {
908 // TODO: Is this right? Isn't PDB for *any* PE/COFF file?
909 assert(self.target.isWindows() or self.target.isUefi());
910 return FileSource{ .generated = &self.output_pdb_path_source };
911}
912
913pub fn addAssemblyFile(self: *CompileStep, path: []const u8) void {
914 self.link_objects.append(.{
915 .assembly_file = .{ .path = self.builder.dupe(path) },
916 }) catch @panic("OOM");
917}
918
919pub fn addAssemblyFileSource(self: *CompileStep, source: FileSource) void {
920 const source_duped = source.dupe(self.builder);
921 self.link_objects.append(.{ .assembly_file = source_duped }) catch @panic("OOM");
922 source_duped.addStepDependencies(&self.step);
923}
924
925pub fn addObjectFile(self: *CompileStep, source_file: []const u8) void {
926 self.addObjectFileSource(.{ .path = source_file });
927}
928
929pub fn addObjectFileSource(self: *CompileStep, source: FileSource) void {
930 self.link_objects.append(.{ .static_path = source.dupe(self.builder) }) catch @panic("OOM");
931 source.addStepDependencies(&self.step);
932}
933
934pub fn addObject(self: *CompileStep, obj: *CompileStep) void {
935 assert(obj.kind == .obj);
936 self.linkLibraryOrObject(obj);
937}
938
939pub const addSystemIncludeDir = @compileError("deprecated; use addSystemIncludePath");
940pub const addIncludeDir = @compileError("deprecated; use addIncludePath");
941pub const addLibPath = @compileError("deprecated, use addLibraryPath");
942pub const addFrameworkDir = @compileError("deprecated, use addFrameworkPath");
943
944pub fn addSystemIncludePath(self: *CompileStep, path: []const u8) void {
945 self.include_dirs.append(IncludeDir{ .raw_path_system = self.builder.dupe(path) }) catch @panic("OOM");
946}
947
948pub fn addIncludePath(self: *CompileStep, path: []const u8) void {
949 self.include_dirs.append(IncludeDir{ .raw_path = self.builder.dupe(path) }) catch @panic("OOM");
950}
951
952pub fn addConfigHeader(self: *CompileStep, config_header: *ConfigHeaderStep) void {
953 self.step.dependOn(&config_header.step);
954 self.include_dirs.append(.{ .config_header_step = config_header }) catch @panic("OOM");
955}
956
957pub fn addLibraryPath(self: *CompileStep, path: []const u8) void {
958 self.lib_paths.append(self.builder.dupe(path)) catch @panic("OOM");
959}
960
961pub fn addRPath(self: *CompileStep, path: []const u8) void {
962 self.rpaths.append(self.builder.dupe(path)) catch @panic("OOM");
963}
964
965pub fn addFrameworkPath(self: *CompileStep, dir_path: []const u8) void {
966 self.framework_dirs.append(self.builder.dupe(dir_path)) catch @panic("OOM");
967}
968
969/// Adds a module to be used with `@import` and exposing it in the current
970/// package's module table using `name`.
971pub fn addModule(cs: *CompileStep, name: []const u8, module: *Module) void {
972 cs.modules.put(cs.builder.dupe(name), module) catch @panic("OOM");
973 cs.addRecursiveBuildDeps(module);
974}
975
976/// Adds a module to be used with `@import` without exposing it in the current
977/// package's module table.
978pub fn addAnonymousModule(cs: *CompileStep, name: []const u8, options: std.Build.CreateModuleOptions) void {
979 const module = cs.builder.createModule(options);
980 return addModule(cs, name, module);
981}
982
983pub fn addOptions(cs: *CompileStep, module_name: []const u8, options: *OptionsStep) void {
984 addModule(cs, module_name, options.createModule());
985}
986
987fn addRecursiveBuildDeps(cs: *CompileStep, module: *Module) void {
988 module.source_file.addStepDependencies(&cs.step);
989 for (module.dependencies.values()) |dep| {
990 cs.addRecursiveBuildDeps(dep);
991 }
992}
993
994/// If Vcpkg was found on the system, it will be added to include and lib
995/// paths for the specified target.
996pub fn addVcpkgPaths(self: *CompileStep, linkage: CompileStep.Linkage) !void {
997 // Ideally in the Unattempted case we would call the function recursively
998 // after findVcpkgRoot and have only one switch statement, but the compiler
999 // cannot resolve the error set.
1000 switch (self.builder.vcpkg_root) {
1001 .unattempted => {
1002 self.builder.vcpkg_root = if (try findVcpkgRoot(self.builder.allocator)) |root|
1003 VcpkgRoot{ .found = root }
1004 else
1005 .not_found;
1006 },
1007 .not_found => return error.VcpkgNotFound,
1008 .found => {},
1009 }
1010
1011 switch (self.builder.vcpkg_root) {
1012 .unattempted => unreachable,
1013 .not_found => return error.VcpkgNotFound,
1014 .found => |root| {
1015 const allocator = self.builder.allocator;
1016 const triplet = try self.target.vcpkgTriplet(allocator, if (linkage == .static) .Static else .Dynamic);
1017 defer self.builder.allocator.free(triplet);
1018
1019 const include_path = self.builder.pathJoin(&.{ root, "installed", triplet, "include" });
1020 errdefer allocator.free(include_path);
1021 try self.include_dirs.append(IncludeDir{ .raw_path = include_path });
1022
1023 const lib_path = self.builder.pathJoin(&.{ root, "installed", triplet, "lib" });
1024 try self.lib_paths.append(lib_path);
1025
1026 self.vcpkg_bin_path = self.builder.pathJoin(&.{ root, "installed", triplet, "bin" });
1027 },
1028 }
1029}
1030
1031pub fn setExecCmd(self: *CompileStep, args: []const ?[]const u8) void {
1032 assert(self.kind == .@"test");
1033 const duped_args = self.builder.allocator.alloc(?[]u8, args.len) catch @panic("OOM");
1034 for (args) |arg, i| {
1035 duped_args[i] = if (arg) |a| self.builder.dupe(a) else null;
1036 }
1037 self.exec_cmd_args = duped_args;
1038}
1039
1040fn linkLibraryOrObject(self: *CompileStep, other: *CompileStep) void {
1041 self.step.dependOn(&other.step);
1042 self.link_objects.append(.{ .other_step = other }) catch @panic("OOM");
1043 self.include_dirs.append(.{ .other_step = other }) catch @panic("OOM");
1044}
1045
1046fn appendModuleArgs(
1047 cs: *CompileStep,
1048 zig_args: *ArrayList([]const u8),
1049 name: []const u8,
1050 module: *Module,
1051) error{OutOfMemory}!void {
1052 try zig_args.append("--pkg-begin");
1053 try zig_args.append(name);
1054 try zig_args.append(module.builder.pathFromRoot(module.source_file.getPath(module.builder)));
1055
1056 {
1057 const keys = module.dependencies.keys();
1058 for (module.dependencies.values()) |sub_module, i| {
1059 const sub_name = keys[i];
1060 try cs.appendModuleArgs(zig_args, sub_name, sub_module);
1061 }
1062 }
1063
1064 try zig_args.append("--pkg-end");
1065}
1066
1067fn make(step: *Step) !void {
1068 const self = @fieldParentPtr(CompileStep, "step", step);
1069 const builder = self.builder;
1070
1071 if (self.root_src == null and self.link_objects.items.len == 0) {
1072 log.err("{s}: linker needs 1 or more objects to link", .{self.step.name});
1073 return error.NeedAnObject;
1074 }
1075
1076 var zig_args = ArrayList([]const u8).init(builder.allocator);
1077 defer zig_args.deinit();
1078
1079 try zig_args.append(builder.zig_exe);
1080
1081 const cmd = switch (self.kind) {
1082 .lib => "build-lib",
1083 .exe => "build-exe",
1084 .obj => "build-obj",
1085 .@"test" => "test",
1086 .test_exe => "test",
1087 };
1088 try zig_args.append(cmd);
1089
1090 if (builder.color != .auto) {
1091 try zig_args.append("--color");
1092 try zig_args.append(@tagName(builder.color));
1093 }
1094
1095 if (builder.reference_trace) |some| {
1096 try zig_args.append(try std.fmt.allocPrint(builder.allocator, "-freference-trace={d}", .{some}));
1097 }
1098
1099 try addFlag(&zig_args, "LLVM", self.use_llvm);
1100 try addFlag(&zig_args, "LLD", self.use_lld);
1101
1102 if (self.target.ofmt) |ofmt| {
1103 try zig_args.append(try std.fmt.allocPrint(builder.allocator, "-ofmt={s}", .{@tagName(ofmt)}));
1104 }
1105
1106 if (self.entry_symbol_name) |entry| {
1107 try zig_args.append("--entry");
1108 try zig_args.append(entry);
1109 }
1110
1111 if (self.stack_size) |stack_size| {
1112 try zig_args.append("--stack");
1113 try zig_args.append(try std.fmt.allocPrint(builder.allocator, "{}", .{stack_size}));
1114 }
1115
1116 if (self.root_src) |root_src| try zig_args.append(root_src.getPath(builder));
1117
1118 // We will add link objects from transitive dependencies, but we want to keep
1119 // all link objects in the same order provided.
1120 // This array is used to keep self.link_objects immutable.
1121 var transitive_deps: TransitiveDeps = .{
1122 .link_objects = ArrayList(LinkObject).init(builder.allocator),
1123 .seen_system_libs = StringHashMap(void).init(builder.allocator),
1124 .seen_steps = std.AutoHashMap(*const Step, void).init(builder.allocator),
1125 .is_linking_libcpp = self.is_linking_libcpp,
1126 .is_linking_libc = self.is_linking_libc,
1127 .frameworks = &self.frameworks,
1128 };
1129
1130 try transitive_deps.seen_steps.put(&self.step, {});
1131 try transitive_deps.add(self.link_objects.items);
1132
1133 var prev_has_extra_flags = false;
1134
1135 for (transitive_deps.link_objects.items) |link_object| {
1136 switch (link_object) {
1137 .static_path => |static_path| try zig_args.append(static_path.getPath(builder)),
1138
1139 .other_step => |other| switch (other.kind) {
1140 .exe => @panic("Cannot link with an executable build artifact"),
1141 .test_exe => @panic("Cannot link with an executable build artifact"),
1142 .@"test" => @panic("Cannot link with a test"),
1143 .obj => {
1144 try zig_args.append(other.getOutputSource().getPath(builder));
1145 },
1146 .lib => l: {
1147 if (self.isStaticLibrary() and other.isStaticLibrary()) {
1148 // Avoid putting a static library inside a static library.
1149 break :l;
1150 }
1151
1152 const full_path_lib = other.getOutputLibSource().getPath(builder);
1153 try zig_args.append(full_path_lib);
1154
1155 if (other.linkage == Linkage.dynamic and !self.target.isWindows()) {
1156 if (fs.path.dirname(full_path_lib)) |dirname| {
1157 try zig_args.append("-rpath");
1158 try zig_args.append(dirname);
1159 }
1160 }
1161 },
1162 },
1163
1164 .system_lib => |system_lib| {
1165 const prefix: []const u8 = prefix: {
1166 if (system_lib.needed) break :prefix "-needed-l";
1167 if (system_lib.weak) {
1168 if (self.target.isDarwin()) break :prefix "-weak-l";
1169 log.warn("Weak library import used for a non-darwin target, this will be converted to normally library import `-lname`", .{});
1170 }
1171 break :prefix "-l";
1172 };
1173 switch (system_lib.use_pkg_config) {
1174 .no => try zig_args.append(builder.fmt("{s}{s}", .{ prefix, system_lib.name })),
1175 .yes, .force => {
1176 if (self.runPkgConfig(system_lib.name)) |args| {
1177 try zig_args.appendSlice(args);
1178 } else |err| switch (err) {
1179 error.PkgConfigInvalidOutput,
1180 error.PkgConfigCrashed,
1181 error.PkgConfigFailed,
1182 error.PkgConfigNotInstalled,
1183 error.PackageNotFound,
1184 => switch (system_lib.use_pkg_config) {
1185 .yes => {
1186 // pkg-config failed, so fall back to linking the library
1187 // by name directly.
1188 try zig_args.append(builder.fmt("{s}{s}", .{
1189 prefix,
1190 system_lib.name,
1191 }));
1192 },
1193 .force => {
1194 panic("pkg-config failed for library {s}", .{system_lib.name});
1195 },
1196 .no => unreachable,
1197 },
1198
1199 else => |e| return e,
1200 }
1201 },
1202 }
1203 },
1204
1205 .assembly_file => |asm_file| {
1206 if (prev_has_extra_flags) {
1207 try zig_args.append("-extra-cflags");
1208 try zig_args.append("--");
1209 prev_has_extra_flags = false;
1210 }
1211 try zig_args.append(asm_file.getPath(builder));
1212 },
1213
1214 .c_source_file => |c_source_file| {
1215 if (c_source_file.args.len == 0) {
1216 if (prev_has_extra_flags) {
1217 try zig_args.append("-cflags");
1218 try zig_args.append("--");
1219 prev_has_extra_flags = false;
1220 }
1221 } else {
1222 try zig_args.append("-cflags");
1223 for (c_source_file.args) |arg| {
1224 try zig_args.append(arg);
1225 }
1226 try zig_args.append("--");
1227 }
1228 try zig_args.append(c_source_file.source.getPath(builder));
1229 },
1230
1231 .c_source_files => |c_source_files| {
1232 if (c_source_files.flags.len == 0) {
1233 if (prev_has_extra_flags) {
1234 try zig_args.append("-cflags");
1235 try zig_args.append("--");
1236 prev_has_extra_flags = false;
1237 }
1238 } else {
1239 try zig_args.append("-cflags");
1240 for (c_source_files.flags) |flag| {
1241 try zig_args.append(flag);
1242 }
1243 try zig_args.append("--");
1244 }
1245 for (c_source_files.files) |file| {
1246 try zig_args.append(builder.pathFromRoot(file));
1247 }
1248 },
1249 }
1250 }
1251
1252 if (transitive_deps.is_linking_libcpp) {
1253 try zig_args.append("-lc++");
1254 }
1255
1256 if (transitive_deps.is_linking_libc) {
1257 try zig_args.append("-lc");
1258 }
1259
1260 if (self.image_base) |image_base| {
1261 try zig_args.append("--image-base");
1262 try zig_args.append(builder.fmt("0x{x}", .{image_base}));
1263 }
1264
1265 if (self.filter) |filter| {
1266 try zig_args.append("--test-filter");
1267 try zig_args.append(filter);
1268 }
1269
1270 if (self.test_evented_io) {
1271 try zig_args.append("--test-evented-io");
1272 }
1273
1274 if (self.name_prefix.len != 0) {
1275 try zig_args.append("--test-name-prefix");
1276 try zig_args.append(self.name_prefix);
1277 }
1278
1279 if (self.test_runner) |test_runner| {
1280 try zig_args.append("--test-runner");
1281 try zig_args.append(builder.pathFromRoot(test_runner));
1282 }
1283
1284 for (builder.debug_log_scopes) |log_scope| {
1285 try zig_args.append("--debug-log");
1286 try zig_args.append(log_scope);
1287 }
1288
1289 if (builder.debug_compile_errors) {
1290 try zig_args.append("--debug-compile-errors");
1291 }
1292
1293 if (builder.verbose_cimport) try zig_args.append("--verbose-cimport");
1294 if (builder.verbose_air) try zig_args.append("--verbose-air");
1295 if (builder.verbose_llvm_ir) try zig_args.append("--verbose-llvm-ir");
1296 if (builder.verbose_link or self.verbose_link) try zig_args.append("--verbose-link");
1297 if (builder.verbose_cc or self.verbose_cc) try zig_args.append("--verbose-cc");
1298 if (builder.verbose_llvm_cpu_features) try zig_args.append("--verbose-llvm-cpu-features");
1299
1300 if (self.emit_analysis.getArg(builder, "emit-analysis")) |arg| try zig_args.append(arg);
1301 if (self.emit_asm.getArg(builder, "emit-asm")) |arg| try zig_args.append(arg);
1302 if (self.emit_bin.getArg(builder, "emit-bin")) |arg| try zig_args.append(arg);
1303 if (self.emit_docs.getArg(builder, "emit-docs")) |arg| try zig_args.append(arg);
1304 if (self.emit_implib.getArg(builder, "emit-implib")) |arg| try zig_args.append(arg);
1305 if (self.emit_llvm_bc.getArg(builder, "emit-llvm-bc")) |arg| try zig_args.append(arg);
1306 if (self.emit_llvm_ir.getArg(builder, "emit-llvm-ir")) |arg| try zig_args.append(arg);
1307
1308 if (self.emit_h) try zig_args.append("-femit-h");
1309
1310 try addFlag(&zig_args, "strip", self.strip);
1311 try addFlag(&zig_args, "unwind-tables", self.unwind_tables);
1312
1313 switch (self.compress_debug_sections) {
1314 .none => {},
1315 .zlib => try zig_args.append("--compress-debug-sections=zlib"),
1316 }
1317
1318 if (self.link_eh_frame_hdr) {
1319 try zig_args.append("--eh-frame-hdr");
1320 }
1321 if (self.link_emit_relocs) {
1322 try zig_args.append("--emit-relocs");
1323 }
1324 if (self.link_function_sections) {
1325 try zig_args.append("-ffunction-sections");
1326 }
1327 if (self.link_gc_sections) |x| {
1328 try zig_args.append(if (x) "--gc-sections" else "--no-gc-sections");
1329 }
1330 if (self.linker_allow_shlib_undefined) |x| {
1331 try zig_args.append(if (x) "-fallow-shlib-undefined" else "-fno-allow-shlib-undefined");
1332 }
1333 if (self.link_z_notext) {
1334 try zig_args.append("-z");
1335 try zig_args.append("notext");
1336 }
1337 if (!self.link_z_relro) {
1338 try zig_args.append("-z");
1339 try zig_args.append("norelro");
1340 }
1341 if (self.link_z_lazy) {
1342 try zig_args.append("-z");
1343 try zig_args.append("lazy");
1344 }
1345 if (self.link_z_common_page_size) |size| {
1346 try zig_args.append("-z");
1347 try zig_args.append(builder.fmt("common-page-size={d}", .{size}));
1348 }
1349 if (self.link_z_max_page_size) |size| {
1350 try zig_args.append("-z");
1351 try zig_args.append(builder.fmt("max-page-size={d}", .{size}));
1352 }
1353
1354 if (self.libc_file) |libc_file| {
1355 try zig_args.append("--libc");
1356 try zig_args.append(libc_file.getPath(builder));
1357 } else if (builder.libc_file) |libc_file| {
1358 try zig_args.append("--libc");
1359 try zig_args.append(libc_file);
1360 }
1361
1362 switch (self.optimize) {
1363 .Debug => {}, // Skip since it's the default.
1364 else => try zig_args.append(builder.fmt("-O{s}", .{@tagName(self.optimize)})),
1365 }
1366
1367 try zig_args.append("--cache-dir");
1368 try zig_args.append(builder.pathFromRoot(builder.cache_root));
1369
1370 try zig_args.append("--global-cache-dir");
1371 try zig_args.append(builder.pathFromRoot(builder.global_cache_root));
1372
1373 try zig_args.append("--name");
1374 try zig_args.append(self.name);
1375
1376 if (self.linkage) |some| switch (some) {
1377 .dynamic => try zig_args.append("-dynamic"),
1378 .static => try zig_args.append("-static"),
1379 };
1380 if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic) {
1381 if (self.version) |version| {
1382 try zig_args.append("--version");
1383 try zig_args.append(builder.fmt("{}", .{version}));
1384 }
1385
1386 if (self.target.isDarwin()) {
1387 const install_name = self.install_name orelse builder.fmt("@rpath/{s}{s}{s}", .{
1388 self.target.libPrefix(),
1389 self.name,
1390 self.target.dynamicLibSuffix(),
1391 });
1392 try zig_args.append("-install_name");
1393 try zig_args.append(install_name);
1394 }
1395 }
1396
1397 if (self.entitlements) |entitlements| {
1398 try zig_args.appendSlice(&[_][]const u8{ "--entitlements", entitlements });
1399 }
1400 if (self.pagezero_size) |pagezero_size| {
1401 const size = try std.fmt.allocPrint(builder.allocator, "{x}", .{pagezero_size});
1402 try zig_args.appendSlice(&[_][]const u8{ "-pagezero_size", size });
1403 }
1404 if (self.search_strategy) |strat| switch (strat) {
1405 .paths_first => try zig_args.append("-search_paths_first"),
1406 .dylibs_first => try zig_args.append("-search_dylibs_first"),
1407 };
1408 if (self.headerpad_size) |headerpad_size| {
1409 const size = try std.fmt.allocPrint(builder.allocator, "{x}", .{headerpad_size});
1410 try zig_args.appendSlice(&[_][]const u8{ "-headerpad", size });
1411 }
1412 if (self.headerpad_max_install_names) {
1413 try zig_args.append("-headerpad_max_install_names");
1414 }
1415 if (self.dead_strip_dylibs) {
1416 try zig_args.append("-dead_strip_dylibs");
1417 }
1418
1419 try addFlag(&zig_args, "compiler-rt", self.bundle_compiler_rt);
1420 try addFlag(&zig_args, "single-threaded", self.single_threaded);
1421 if (self.disable_stack_probing) {
1422 try zig_args.append("-fno-stack-check");
1423 }
1424 try addFlag(&zig_args, "stack-protector", self.stack_protector);
1425 if (self.red_zone) |red_zone| {
1426 if (red_zone) {
1427 try zig_args.append("-mred-zone");
1428 } else {
1429 try zig_args.append("-mno-red-zone");
1430 }
1431 }
1432 try addFlag(&zig_args, "omit-frame-pointer", self.omit_frame_pointer);
1433 try addFlag(&zig_args, "dll-export-fns", self.dll_export_fns);
1434
1435 if (self.disable_sanitize_c) {
1436 try zig_args.append("-fno-sanitize-c");
1437 }
1438 if (self.sanitize_thread) {
1439 try zig_args.append("-fsanitize-thread");
1440 }
1441 if (self.rdynamic) {
1442 try zig_args.append("-rdynamic");
1443 }
1444 if (self.import_memory) {
1445 try zig_args.append("--import-memory");
1446 }
1447 if (self.import_symbols) {
1448 try zig_args.append("--import-symbols");
1449 }
1450 if (self.import_table) {
1451 try zig_args.append("--import-table");
1452 }
1453 if (self.export_table) {
1454 try zig_args.append("--export-table");
1455 }
1456 if (self.initial_memory) |initial_memory| {
1457 try zig_args.append(builder.fmt("--initial-memory={d}", .{initial_memory}));
1458 }
1459 if (self.max_memory) |max_memory| {
1460 try zig_args.append(builder.fmt("--max-memory={d}", .{max_memory}));
1461 }
1462 if (self.shared_memory) {
1463 try zig_args.append("--shared-memory");
1464 }
1465 if (self.global_base) |global_base| {
1466 try zig_args.append(builder.fmt("--global-base={d}", .{global_base}));
1467 }
1468
1469 if (self.code_model != .default) {
1470 try zig_args.append("-mcmodel");
1471 try zig_args.append(@tagName(self.code_model));
1472 }
1473 if (self.wasi_exec_model) |model| {
1474 try zig_args.append(builder.fmt("-mexec-model={s}", .{@tagName(model)}));
1475 }
1476 for (self.export_symbol_names) |symbol_name| {
1477 try zig_args.append(builder.fmt("--export={s}", .{symbol_name}));
1478 }
1479
1480 if (!self.target.isNative()) {
1481 try zig_args.appendSlice(&.{
1482 "-target", try self.target.zigTriple(builder.allocator),
1483 "-mcpu", try std.Build.serializeCpu(builder.allocator, self.target.getCpu()),
1484 });
1485
1486 if (self.target.dynamic_linker.get()) |dynamic_linker| {
1487 try zig_args.append("--dynamic-linker");
1488 try zig_args.append(dynamic_linker);
1489 }
1490 }
1491
1492 if (self.linker_script) |linker_script| {
1493 try zig_args.append("--script");
1494 try zig_args.append(linker_script.getPath(builder));
1495 }
1496
1497 if (self.version_script) |version_script| {
1498 try zig_args.append("--version-script");
1499 try zig_args.append(builder.pathFromRoot(version_script));
1500 }
1501
1502 if (self.kind == .@"test") {
1503 if (self.exec_cmd_args) |exec_cmd_args| {
1504 for (exec_cmd_args) |cmd_arg| {
1505 if (cmd_arg) |arg| {
1506 try zig_args.append("--test-cmd");
1507 try zig_args.append(arg);
1508 } else {
1509 try zig_args.append("--test-cmd-bin");
1510 }
1511 }
1512 } else {
1513 const need_cross_glibc = self.target.isGnuLibC() and transitive_deps.is_linking_libc;
1514
1515 switch (builder.host.getExternalExecutor(self.target_info, .{
1516 .qemu_fixes_dl = need_cross_glibc and builder.glibc_runtimes_dir != null,
1517 .link_libc = transitive_deps.is_linking_libc,
1518 })) {
1519 .native => {},
1520 .bad_dl, .bad_os_or_cpu => {
1521 try zig_args.append("--test-no-exec");
1522 },
1523 .rosetta => if (builder.enable_rosetta) {
1524 try zig_args.append("--test-cmd-bin");
1525 } else {
1526 try zig_args.append("--test-no-exec");
1527 },
1528 .qemu => |bin_name| ok: {
1529 if (builder.enable_qemu) qemu: {
1530 const glibc_dir_arg = if (need_cross_glibc)
1531 builder.glibc_runtimes_dir orelse break :qemu
1532 else
1533 null;
1534 try zig_args.append("--test-cmd");
1535 try zig_args.append(bin_name);
1536 if (glibc_dir_arg) |dir| {
1537 // TODO look into making this a call to `linuxTriple`. This
1538 // needs the directory to be called "i686" rather than
1539 // "x86" which is why we do it manually here.
1540 const fmt_str = "{s}" ++ fs.path.sep_str ++ "{s}-{s}-{s}";
1541 const cpu_arch = self.target.getCpuArch();
1542 const os_tag = self.target.getOsTag();
1543 const abi = self.target.getAbi();
1544 const cpu_arch_name: []const u8 = if (cpu_arch == .x86)
1545 "i686"
1546 else
1547 @tagName(cpu_arch);
1548 const full_dir = try std.fmt.allocPrint(builder.allocator, fmt_str, .{
1549 dir, cpu_arch_name, @tagName(os_tag), @tagName(abi),
1550 });
1551
1552 try zig_args.append("--test-cmd");
1553 try zig_args.append("-L");
1554 try zig_args.append("--test-cmd");
1555 try zig_args.append(full_dir);
1556 }
1557 try zig_args.append("--test-cmd-bin");
1558 break :ok;
1559 }
1560 try zig_args.append("--test-no-exec");
1561 },
1562 .wine => |bin_name| if (builder.enable_wine) {
1563 try zig_args.append("--test-cmd");
1564 try zig_args.append(bin_name);
1565 try zig_args.append("--test-cmd-bin");
1566 } else {
1567 try zig_args.append("--test-no-exec");
1568 },
1569 .wasmtime => |bin_name| if (builder.enable_wasmtime) {
1570 try zig_args.append("--test-cmd");
1571 try zig_args.append(bin_name);
1572 try zig_args.append("--test-cmd");
1573 try zig_args.append("--dir=.");
1574 try zig_args.append("--test-cmd-bin");
1575 } else {
1576 try zig_args.append("--test-no-exec");
1577 },
1578 .darling => |bin_name| if (builder.enable_darling) {
1579 try zig_args.append("--test-cmd");
1580 try zig_args.append(bin_name);
1581 try zig_args.append("--test-cmd-bin");
1582 } else {
1583 try zig_args.append("--test-no-exec");
1584 },
1585 }
1586 }
1587 } else if (self.kind == .test_exe) {
1588 try zig_args.append("--test-no-exec");
1589 }
1590
1591 {
1592 const keys = self.modules.keys();
1593 for (self.modules.values()) |module, i| {
1594 const name = keys[i];
1595 try self.appendModuleArgs(&zig_args, name, module);
1596 }
1597 }
1598
1599 for (self.include_dirs.items) |include_dir| {
1600 switch (include_dir) {
1601 .raw_path => |include_path| {
1602 try zig_args.append("-I");
1603 try zig_args.append(builder.pathFromRoot(include_path));
1604 },
1605 .raw_path_system => |include_path| {
1606 if (builder.sysroot != null) {
1607 try zig_args.append("-iwithsysroot");
1608 } else {
1609 try zig_args.append("-isystem");
1610 }
1611
1612 const resolved_include_path = builder.pathFromRoot(include_path);
1613
1614 const common_include_path = if (builtin.os.tag == .windows and builder.sysroot != null and fs.path.isAbsolute(resolved_include_path)) blk: {
1615 // We need to check for disk designator and strip it out from dir path so
1616 // that zig/clang can concat resolved_include_path with sysroot.
1617 const disk_designator = fs.path.diskDesignatorWindows(resolved_include_path);
1618
1619 if (mem.indexOf(u8, resolved_include_path, disk_designator)) |where| {
1620 break :blk resolved_include_path[where + disk_designator.len ..];
1621 }
1622
1623 break :blk resolved_include_path;
1624 } else resolved_include_path;
1625
1626 try zig_args.append(common_include_path);
1627 },
1628 .other_step => |other| {
1629 if (other.emit_h) {
1630 const h_path = other.getOutputHSource().getPath(builder);
1631 try zig_args.append("-isystem");
1632 try zig_args.append(fs.path.dirname(h_path).?);
1633 }
1634 if (other.installed_headers.items.len > 0) {
1635 for (other.installed_headers.items) |install_step| {
1636 try install_step.make();
1637 }
1638 try zig_args.append("-I");
1639 try zig_args.append(builder.pathJoin(&.{
1640 other.builder.install_prefix, "include",
1641 }));
1642 }
1643 },
1644 .config_header_step => |config_header| {
1645 const full_file_path = config_header.output_file.path.?;
1646 const header_dir_path = full_file_path[0 .. full_file_path.len - config_header.include_path.len];
1647 try zig_args.appendSlice(&.{ "-I", header_dir_path });
1648 },
1649 }
1650 }
1651
1652 for (self.lib_paths.items) |lib_path| {
1653 try zig_args.append("-L");
1654 try zig_args.append(lib_path);
1655 }
1656
1657 for (self.rpaths.items) |rpath| {
1658 try zig_args.append("-rpath");
1659 try zig_args.append(rpath);
1660 }
1661
1662 for (self.c_macros.items) |c_macro| {
1663 try zig_args.append("-D");
1664 try zig_args.append(c_macro);
1665 }
1666
1667 if (self.target.isDarwin()) {
1668 for (self.framework_dirs.items) |dir| {
1669 if (builder.sysroot != null) {
1670 try zig_args.append("-iframeworkwithsysroot");
1671 } else {
1672 try zig_args.append("-iframework");
1673 }
1674 try zig_args.append(dir);
1675 try zig_args.append("-F");
1676 try zig_args.append(dir);
1677 }
1678
1679 var it = self.frameworks.iterator();
1680 while (it.next()) |entry| {
1681 const name = entry.key_ptr.*;
1682 const info = entry.value_ptr.*;
1683 if (info.needed) {
1684 try zig_args.append("-needed_framework");
1685 } else if (info.weak) {
1686 try zig_args.append("-weak_framework");
1687 } else {
1688 try zig_args.append("-framework");
1689 }
1690 try zig_args.append(name);
1691 }
1692 } else {
1693 if (self.framework_dirs.items.len > 0) {
1694 log.info("Framework directories have been added for a non-darwin target, this will have no affect on the build", .{});
1695 }
1696
1697 if (self.frameworks.count() > 0) {
1698 log.info("Frameworks have been added for a non-darwin target, this will have no affect on the build", .{});
1699 }
1700 }
1701
1702 if (builder.sysroot) |sysroot| {
1703 try zig_args.appendSlice(&[_][]const u8{ "--sysroot", sysroot });
1704 }
1705
1706 for (builder.search_prefixes.items) |search_prefix| {
1707 try zig_args.append("-L");
1708 try zig_args.append(builder.pathJoin(&.{
1709 search_prefix, "lib",
1710 }));
1711 try zig_args.append("-I");
1712 try zig_args.append(builder.pathJoin(&.{
1713 search_prefix, "include",
1714 }));
1715 }
1716
1717 try addFlag(&zig_args, "valgrind", self.valgrind_support);
1718 try addFlag(&zig_args, "each-lib-rpath", self.each_lib_rpath);
1719 try addFlag(&zig_args, "build-id", self.build_id);
1720
1721 if (self.override_lib_dir) |dir| {
1722 try zig_args.append("--zig-lib-dir");
1723 try zig_args.append(builder.pathFromRoot(dir));
1724 } else if (builder.override_lib_dir) |dir| {
1725 try zig_args.append("--zig-lib-dir");
1726 try zig_args.append(builder.pathFromRoot(dir));
1727 }
1728
1729 if (self.main_pkg_path) |dir| {
1730 try zig_args.append("--main-pkg-path");
1731 try zig_args.append(builder.pathFromRoot(dir));
1732 }
1733
1734 try addFlag(&zig_args, "PIC", self.force_pic);
1735 try addFlag(&zig_args, "PIE", self.pie);
1736 try addFlag(&zig_args, "lto", self.want_lto);
1737
1738 if (self.subsystem) |subsystem| {
1739 try zig_args.append("--subsystem");
1740 try zig_args.append(switch (subsystem) {
1741 .Console => "console",
1742 .Windows => "windows",
1743 .Posix => "posix",
1744 .Native => "native",
1745 .EfiApplication => "efi_application",
1746 .EfiBootServiceDriver => "efi_boot_service_driver",
1747 .EfiRom => "efi_rom",
1748 .EfiRuntimeDriver => "efi_runtime_driver",
1749 });
1750 }
1751
1752 try zig_args.append("--enable-cache");
1753
1754 // Windows has an argument length limit of 32,766 characters, macOS 262,144 and Linux
1755 // 2,097,152. If our args exceed 30 KiB, we instead write them to a "response file" and
1756 // pass that to zig, e.g. via 'zig build-lib @args.rsp'
1757 // See @file syntax here: https://gcc.gnu.org/onlinedocs/gcc/Overall-Options.html
1758 var args_length: usize = 0;
1759 for (zig_args.items) |arg| {
1760 args_length += arg.len + 1; // +1 to account for null terminator
1761 }
1762 if (args_length >= 30 * 1024) {
1763 const args_dir = try fs.path.join(
1764 builder.allocator,
1765 &[_][]const u8{ builder.pathFromRoot("zig-cache"), "args" },
1766 );
1767 try std.fs.cwd().makePath(args_dir);
1768
1769 var args_arena = std.heap.ArenaAllocator.init(builder.allocator);
1770 defer args_arena.deinit();
1771
1772 const args_to_escape = zig_args.items[2..];
1773 var escaped_args = try ArrayList([]const u8).initCapacity(args_arena.allocator(), args_to_escape.len);
1774
1775 arg_blk: for (args_to_escape) |arg| {
1776 for (arg) |c, arg_idx| {
1777 if (c == '\\' or c == '"') {
1778 // Slow path for arguments that need to be escaped. We'll need to allocate and copy
1779 var escaped = try ArrayList(u8).initCapacity(args_arena.allocator(), arg.len + 1);
1780 const writer = escaped.writer();
1781 try writer.writeAll(arg[0..arg_idx]);
1782 for (arg[arg_idx..]) |to_escape| {
1783 if (to_escape == '\\' or to_escape == '"') try writer.writeByte('\\');
1784 try writer.writeByte(to_escape);
1785 }
1786 escaped_args.appendAssumeCapacity(escaped.items);
1787 continue :arg_blk;
1788 }
1789 }
1790 escaped_args.appendAssumeCapacity(arg); // no escaping needed so just use original argument
1791 }
1792
1793 // Write the args to zig-cache/args/<SHA256 hash of args> to avoid conflicts with
1794 // other zig build commands running in parallel.
1795 const partially_quoted = try std.mem.join(builder.allocator, "\" \"", escaped_args.items);
1796 const args = try std.mem.concat(builder.allocator, u8, &[_][]const u8{ "\"", partially_quoted, "\"" });
1797
1798 var args_hash: [Sha256.digest_length]u8 = undefined;
1799 Sha256.hash(args, &args_hash, .{});
1800 var args_hex_hash: [Sha256.digest_length * 2]u8 = undefined;
1801 _ = try std.fmt.bufPrint(
1802 &args_hex_hash,
1803 "{s}",
1804 .{std.fmt.fmtSliceHexLower(&args_hash)},
1805 );
1806
1807 const args_file = try fs.path.join(builder.allocator, &[_][]const u8{ args_dir, args_hex_hash[0..] });
1808 try std.fs.cwd().writeFile(args_file, args);
1809
1810 zig_args.shrinkRetainingCapacity(2);
1811 try zig_args.append(try std.mem.concat(builder.allocator, u8, &[_][]const u8{ "@", args_file }));
1812 }
1813
1814 const output_dir_nl = try builder.execFromStep(zig_args.items, &self.step);
1815 const build_output_dir = mem.trimRight(u8, output_dir_nl, "\r\n");
1816
1817 if (self.output_dir) |output_dir| {
1818 var src_dir = try std.fs.cwd().openIterableDir(build_output_dir, .{});
1819 defer src_dir.close();
1820
1821 // Create the output directory if it doesn't exist.
1822 try std.fs.cwd().makePath(output_dir);
1823
1824 var dest_dir = try std.fs.cwd().openDir(output_dir, .{});
1825 defer dest_dir.close();
1826
1827 var it = src_dir.iterate();
1828 while (try it.next()) |entry| {
1829 // The compiler can put these files into the same directory, but we don't
1830 // want to copy them over.
1831 if (mem.eql(u8, entry.name, "llvm-ar.id") or
1832 mem.eql(u8, entry.name, "libs.txt") or
1833 mem.eql(u8, entry.name, "builtin.zig") or
1834 mem.eql(u8, entry.name, "zld.id") or
1835 mem.eql(u8, entry.name, "lld.id")) continue;
1836
1837 _ = try src_dir.dir.updateFile(entry.name, dest_dir, entry.name, .{});
1838 }
1839 } else {
1840 self.output_dir = build_output_dir;
1841 }
1842
1843 // This will ensure all output filenames will now have the output_dir available!
1844 self.computeOutFileNames();
1845
1846 // Update generated files
1847 if (self.output_dir != null) {
1848 self.output_path_source.path = builder.pathJoin(
1849 &.{ self.output_dir.?, self.out_filename },
1850 );
1851
1852 if (self.emit_h) {
1853 self.output_h_path_source.path = builder.pathJoin(
1854 &.{ self.output_dir.?, self.out_h_filename },
1855 );
1856 }
1857
1858 if (self.target.isWindows() or self.target.isUefi()) {
1859 self.output_pdb_path_source.path = builder.pathJoin(
1860 &.{ self.output_dir.?, self.out_pdb_filename },
1861 );
1862 }
1863 }
1864
1865 if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic and self.version != null and self.target.wantSharedLibSymLinks()) {
1866 try doAtomicSymLinks(builder.allocator, self.getOutputSource().getPath(builder), self.major_only_filename.?, self.name_only_filename.?);
1867 }
1868}
1869
1870fn isLibCLibrary(name: []const u8) bool {
1871 const libc_libraries = [_][]const u8{ "c", "m", "dl", "rt", "pthread" };
1872 for (libc_libraries) |libc_lib_name| {
1873 if (mem.eql(u8, name, libc_lib_name))
1874 return true;
1875 }
1876 return false;
1877}
1878
1879fn isLibCppLibrary(name: []const u8) bool {
1880 const libcpp_libraries = [_][]const u8{ "c++", "stdc++" };
1881 for (libcpp_libraries) |libcpp_lib_name| {
1882 if (mem.eql(u8, name, libcpp_lib_name))
1883 return true;
1884 }
1885 return false;
1886}
1887
1888/// Returned slice must be freed by the caller.
1889fn findVcpkgRoot(allocator: Allocator) !?[]const u8 {
1890 const appdata_path = try fs.getAppDataDir(allocator, "vcpkg");
1891 defer allocator.free(appdata_path);
1892
1893 const path_file = try fs.path.join(allocator, &[_][]const u8{ appdata_path, "vcpkg.path.txt" });
1894 defer allocator.free(path_file);
1895
1896 const file = fs.cwd().openFile(path_file, .{}) catch return null;
1897 defer file.close();
1898
1899 const size = @intCast(usize, try file.getEndPos());
1900 const vcpkg_path = try allocator.alloc(u8, size);
1901 const size_read = try file.read(vcpkg_path);
1902 std.debug.assert(size == size_read);
1903
1904 return vcpkg_path;
1905}
1906
1907pub fn doAtomicSymLinks(
1908 allocator: Allocator,
1909 output_path: []const u8,
1910 filename_major_only: []const u8,
1911 filename_name_only: []const u8,
1912) !void {
1913 const out_dir = fs.path.dirname(output_path) orelse ".";
1914 const out_basename = fs.path.basename(output_path);
1915 // sym link for libfoo.so.1 to libfoo.so.1.2.3
1916 const major_only_path = try fs.path.join(
1917 allocator,
1918 &[_][]const u8{ out_dir, filename_major_only },
1919 );
1920 fs.atomicSymLink(allocator, out_basename, major_only_path) catch |err| {
1921 log.err("Unable to symlink {s} -> {s}", .{ major_only_path, out_basename });
1922 return err;
1923 };
1924 // sym link for libfoo.so to libfoo.so.1
1925 const name_only_path = try fs.path.join(
1926 allocator,
1927 &[_][]const u8{ out_dir, filename_name_only },
1928 );
1929 fs.atomicSymLink(allocator, filename_major_only, name_only_path) catch |err| {
1930 log.err("Unable to symlink {s} -> {s}", .{ name_only_path, filename_major_only });
1931 return err;
1932 };
1933}
1934
1935fn execPkgConfigList(self: *std.Build, out_code: *u8) (PkgConfigError || ExecError)![]const PkgConfigPkg {
1936 const stdout = try self.execAllowFail(&[_][]const u8{ "pkg-config", "--list-all" }, out_code, .Ignore);
1937 var list = ArrayList(PkgConfigPkg).init(self.allocator);
1938 errdefer list.deinit();
1939 var line_it = mem.tokenize(u8, stdout, "\r\n");
1940 while (line_it.next()) |line| {
1941 if (mem.trim(u8, line, " \t").len == 0) continue;
1942 var tok_it = mem.tokenize(u8, line, " \t");
1943 try list.append(PkgConfigPkg{
1944 .name = tok_it.next() orelse return error.PkgConfigInvalidOutput,
1945 .desc = tok_it.rest(),
1946 });
1947 }
1948 return list.toOwnedSlice();
1949}
1950
1951fn getPkgConfigList(self: *std.Build) ![]const PkgConfigPkg {
1952 if (self.pkg_config_pkg_list) |res| {
1953 return res;
1954 }
1955 var code: u8 = undefined;
1956 if (execPkgConfigList(self, &code)) |list| {
1957 self.pkg_config_pkg_list = list;
1958 return list;
1959 } else |err| {
1960 const result = switch (err) {
1961 error.ProcessTerminated => error.PkgConfigCrashed,
1962 error.ExecNotSupported => error.PkgConfigFailed,
1963 error.ExitCodeFailure => error.PkgConfigFailed,
1964 error.FileNotFound => error.PkgConfigNotInstalled,
1965 error.InvalidName => error.PkgConfigNotInstalled,
1966 error.PkgConfigInvalidOutput => error.PkgConfigInvalidOutput,
1967 error.ChildExecFailed => error.PkgConfigFailed,
1968 else => return err,
1969 };
1970 self.pkg_config_pkg_list = result;
1971 return result;
1972 }
1973}
1974
1975fn addFlag(args: *ArrayList([]const u8), comptime name: []const u8, opt: ?bool) !void {
1976 const cond = opt orelse return;
1977 try args.ensureUnusedCapacity(1);
1978 if (cond) {
1979 args.appendAssumeCapacity("-f" ++ name);
1980 } else {
1981 args.appendAssumeCapacity("-fno-" ++ name);
1982 }
1983}
1984
1985const TransitiveDeps = struct {
1986 link_objects: ArrayList(LinkObject),
1987 seen_system_libs: StringHashMap(void),
1988 seen_steps: std.AutoHashMap(*const Step, void),
1989 is_linking_libcpp: bool,
1990 is_linking_libc: bool,
1991 frameworks: *StringHashMap(FrameworkLinkInfo),
1992
1993 fn add(td: *TransitiveDeps, link_objects: []const LinkObject) !void {
1994 try td.link_objects.ensureUnusedCapacity(link_objects.len);
1995
1996 for (link_objects) |link_object| {
1997 try td.link_objects.append(link_object);
1998 switch (link_object) {
1999 .other_step => |other| try addInner(td, other, other.isDynamicLibrary()),
2000 else => {},
2001 }
2002 }
2003 }
2004
2005 fn addInner(td: *TransitiveDeps, other: *CompileStep, dyn: bool) !void {
2006 // Inherit dependency on libc and libc++
2007 td.is_linking_libcpp = td.is_linking_libcpp or other.is_linking_libcpp;
2008 td.is_linking_libc = td.is_linking_libc or other.is_linking_libc;
2009
2010 // Inherit dependencies on darwin frameworks
2011 if (!dyn) {
2012 var it = other.frameworks.iterator();
2013 while (it.next()) |framework| {
2014 try td.frameworks.put(framework.key_ptr.*, framework.value_ptr.*);
2015 }
2016 }
2017
2018 // Inherit dependencies on system libraries and static libraries.
2019 for (other.link_objects.items) |other_link_object| {
2020 switch (other_link_object) {
2021 .system_lib => |system_lib| {
2022 if ((try td.seen_system_libs.fetchPut(system_lib.name, {})) != null)
2023 continue;
2024
2025 if (dyn)
2026 continue;
2027
2028 try td.link_objects.append(other_link_object);
2029 },
2030 .other_step => |inner_other| {
2031 if ((try td.seen_steps.fetchPut(&inner_other.step, {})) != null)
2032 continue;
2033
2034 if (!dyn)
2035 try td.link_objects.append(other_link_object);
2036
2037 try addInner(td, inner_other, dyn or inner_other.isDynamicLibrary());
2038 },
2039 else => continue,
2040 }
2041 }
2042 }
2043};
lib/std/Build/ConfigHeaderStep.zig created+372
...@@ -0,0 +1,372 @@
1const std = @import("../std.zig");
2const ConfigHeaderStep = @This();
3const Step = std.Build.Step;
4
5pub const base_id: Step.Id = .config_header;
6
7pub const Style = union(enum) {
8 /// The configure format supported by autotools. It uses `#undef foo` to
9 /// mark lines that can be substituted with different values.
10 autoconf: std.Build.FileSource,
11 /// The configure format supported by CMake. It uses `@@FOO@@` and
12 /// `#cmakedefine` for template substitution.
13 cmake: std.Build.FileSource,
14 /// Instead of starting with an input file, start with nothing.
15 blank,
16
17 pub fn getFileSource(style: Style) ?std.Build.FileSource {
18 switch (style) {
19 .autoconf, .cmake => |s| return s,
20 .blank => return null,
21 }
22 }
23};
24
25pub const Value = union(enum) {
26 undef,
27 defined,
28 boolean: bool,
29 int: i64,
30 ident: []const u8,
31 string: []const u8,
32};
33
34step: Step,
35builder: *std.Build,
36values: std.StringArrayHashMap(Value),
37output_file: std.Build.GeneratedFile,
38
39style: Style,
40max_bytes: usize,
41include_path: []const u8,
42
43pub const Options = struct {
44 style: Style = .blank,
45 max_bytes: usize = 2 * 1024 * 1024,
46 include_path: ?[]const u8 = null,
47};
48
49pub fn create(builder: *std.Build, options: Options) *ConfigHeaderStep {
50 const self = builder.allocator.create(ConfigHeaderStep) catch @panic("OOM");
51 const name = if (options.style.getFileSource()) |s|
52 builder.fmt("configure {s} header {s}", .{ @tagName(options.style), s.getDisplayName() })
53 else
54 builder.fmt("configure {s} header", .{@tagName(options.style)});
55 self.* = .{
56 .builder = builder,
57 .step = Step.init(base_id, name, builder.allocator, make),
58 .style = options.style,
59 .values = std.StringArrayHashMap(Value).init(builder.allocator),
60
61 .max_bytes = options.max_bytes,
62 .include_path = "config.h",
63 .output_file = .{ .step = &self.step },
64 };
65
66 if (options.style.getFileSource()) |s| switch (s) {
67 .path => |p| {
68 const basename = std.fs.path.basename(p);
69 if (std.mem.endsWith(u8, basename, ".h.in")) {
70 self.include_path = basename[0 .. basename.len - 3];
71 }
72 },
73 else => {},
74 };
75
76 if (options.include_path) |include_path| {
77 self.include_path = include_path;
78 }
79
80 return self;
81}
82
83pub fn addValues(self: *ConfigHeaderStep, values: anytype) void {
84 return addValuesInner(self, values) catch @panic("OOM");
85}
86
87fn addValuesInner(self: *ConfigHeaderStep, values: anytype) !void {
88 inline for (@typeInfo(@TypeOf(values)).Struct.fields) |field| {
89 try putValue(self, field.name, field.type, @field(values, field.name));
90 }
91}
92
93fn putValue(self: *ConfigHeaderStep, field_name: []const u8, comptime T: type, v: T) !void {
94 switch (@typeInfo(T)) {
95 .Null => {
96 try self.values.put(field_name, .undef);
97 },
98 .Void => {
99 try self.values.put(field_name, .defined);
100 },
101 .Bool => {
102 try self.values.put(field_name, .{ .boolean = v });
103 },
104 .Int => {
105 try self.values.put(field_name, .{ .int = v });
106 },
107 .ComptimeInt => {
108 try self.values.put(field_name, .{ .int = v });
109 },
110 .EnumLiteral => {
111 try self.values.put(field_name, .{ .ident = @tagName(v) });
112 },
113 .Optional => {
114 if (v) |x| {
115 return putValue(self, field_name, @TypeOf(x), x);
116 } else {
117 try self.values.put(field_name, .undef);
118 }
119 },
120 .Pointer => |ptr| {
121 switch (@typeInfo(ptr.child)) {
122 .Array => |array| {
123 if (ptr.size == .One and array.child == u8) {
124 try self.values.put(field_name, .{ .string = v });
125 return;
126 }
127 },
128 else => {},
129 }
130
131 @compileError("unsupported ConfigHeaderStep value type: " ++ @typeName(T));
132 },
133 else => @compileError("unsupported ConfigHeaderStep value type: " ++ @typeName(T)),
134 }
135}
136
137fn make(step: *Step) !void {
138 const self = @fieldParentPtr(ConfigHeaderStep, "step", step);
139 const gpa = self.builder.allocator;
140
141 // The cache is used here not really as a way to speed things up - because writing
142 // the data to a file would probably be very fast - but as a way to find a canonical
143 // location to put build artifacts.
144
145 // If, for example, a hard-coded path was used as the location to put ConfigHeaderStep
146 // files, then two ConfigHeaderStep executing in parallel might clobber each other.
147
148 // TODO port the cache system from the compiler to zig std lib. Until then
149 // we construct the path directly, and no "cache hit" detection happens;
150 // the files are always written.
151 // Note there is very similar code over in WriteFileStep
152 const Hasher = std.crypto.auth.siphash.SipHash128(1, 3);
153 // Random bytes to make ConfigHeaderStep unique. Refresh this with new
154 // random bytes when ConfigHeaderStep implementation is modified in a
155 // non-backwards-compatible way.
156 var hash = Hasher.init("PGuDTpidxyMqnkGM");
157
158 var output = std.ArrayList(u8).init(gpa);
159 defer output.deinit();
160
161 try output.appendSlice("/* This file was generated by ConfigHeaderStep using the Zig Build System. */\n");
162
163 switch (self.style) {
164 .autoconf => |file_source| {
165 const src_path = file_source.getPath(self.builder);
166 const contents = try std.fs.cwd().readFileAlloc(gpa, src_path, self.max_bytes);
167 try render_autoconf(contents, &output, self.values, src_path);
168 },
169 .cmake => |file_source| {
170 const src_path = file_source.getPath(self.builder);
171 const contents = try std.fs.cwd().readFileAlloc(gpa, src_path, self.max_bytes);
172 try render_cmake(contents, &output, self.values, src_path);
173 },
174 .blank => {
175 try render_blank(&output, self.values, self.include_path);
176 },
177 }
178
179 hash.update(output.items);
180
181 var digest: [16]u8 = undefined;
182 hash.final(&digest);
183 var hash_basename: [digest.len * 2]u8 = undefined;
184 _ = std.fmt.bufPrint(
185 &hash_basename,
186 "{s}",
187 .{std.fmt.fmtSliceHexLower(&digest)},
188 ) catch unreachable;
189
190 const output_dir = try std.fs.path.join(gpa, &[_][]const u8{
191 self.builder.cache_root, "o", &hash_basename,
192 });
193
194 // If output_path has directory parts, deal with them. Example:
195 // output_dir is zig-cache/o/HASH
196 // output_path is libavutil/avconfig.h
197 // We want to open directory zig-cache/o/HASH/libavutil/
198 // but keep output_dir as zig-cache/o/HASH for -I include
199 const sub_dir_path = if (std.fs.path.dirname(self.include_path)) |d|
200 try std.fs.path.join(gpa, &.{ output_dir, d })
201 else
202 output_dir;
203
204 var dir = std.fs.cwd().makeOpenPath(sub_dir_path, .{}) catch |err| {
205 std.debug.print("unable to make path {s}: {s}\n", .{ output_dir, @errorName(err) });
206 return err;
207 };
208 defer dir.close();
209
210 try dir.writeFile(std.fs.path.basename(self.include_path), output.items);
211
212 self.output_file.path = try std.fs.path.join(self.builder.allocator, &.{
213 output_dir, self.include_path,
214 });
215}
216
217fn render_autoconf(
218 contents: []const u8,
219 output: *std.ArrayList(u8),
220 values: std.StringArrayHashMap(Value),
221 src_path: []const u8,
222) !void {
223 var values_copy = try values.clone();
224 defer values_copy.deinit();
225
226 var any_errors = false;
227 var line_index: u32 = 0;
228 var line_it = std.mem.split(u8, contents, "\n");
229 while (line_it.next()) |line| : (line_index += 1) {
230 if (!std.mem.startsWith(u8, line, "#")) {
231 try output.appendSlice(line);
232 try output.appendSlice("\n");
233 continue;
234 }
235 var it = std.mem.tokenize(u8, line[1..], " \t\r");
236 const undef = it.next().?;
237 if (!std.mem.eql(u8, undef, "undef")) {
238 try output.appendSlice(line);
239 try output.appendSlice("\n");
240 continue;
241 }
242 const name = it.rest();
243 const kv = values_copy.fetchSwapRemove(name) orelse {
244 std.debug.print("{s}:{d}: error: unspecified config header value: '{s}'\n", .{
245 src_path, line_index + 1, name,
246 });
247 any_errors = true;
248 continue;
249 };
250 try renderValue(output, name, kv.value);
251 }
252
253 for (values_copy.keys()) |name| {
254 std.debug.print("{s}: error: config header value unused: '{s}'\n", .{ src_path, name });
255 }
256
257 if (any_errors) {
258 return error.HeaderConfigFailed;
259 }
260}
261
262fn render_cmake(
263 contents: []const u8,
264 output: *std.ArrayList(u8),
265 values: std.StringArrayHashMap(Value),
266 src_path: []const u8,
267) !void {
268 var values_copy = try values.clone();
269 defer values_copy.deinit();
270
271 var any_errors = false;
272 var line_index: u32 = 0;
273 var line_it = std.mem.split(u8, contents, "\n");
274 while (line_it.next()) |line| : (line_index += 1) {
275 if (!std.mem.startsWith(u8, line, "#")) {
276 try output.appendSlice(line);
277 try output.appendSlice("\n");
278 continue;
279 }
280 var it = std.mem.tokenize(u8, line[1..], " \t\r");
281 const cmakedefine = it.next().?;
282 if (!std.mem.eql(u8, cmakedefine, "cmakedefine")) {
283 try output.appendSlice(line);
284 try output.appendSlice("\n");
285 continue;
286 }
287 const name = it.next() orelse {
288 std.debug.print("{s}:{d}: error: missing define name\n", .{
289 src_path, line_index + 1,
290 });
291 any_errors = true;
292 continue;
293 };
294 const kv = values_copy.fetchSwapRemove(name) orelse {
295 std.debug.print("{s}:{d}: error: unspecified config header value: '{s}'\n", .{
296 src_path, line_index + 1, name,
297 });
298 any_errors = true;
299 continue;
300 };
301 try renderValue(output, name, kv.value);
302 }
303
304 for (values_copy.keys()) |name| {
305 std.debug.print("{s}: error: config header value unused: '{s}'\n", .{ src_path, name });
306 }
307
308 if (any_errors) {
309 return error.HeaderConfigFailed;
310 }
311}
312
313fn render_blank(
314 output: *std.ArrayList(u8),
315 defines: std.StringArrayHashMap(Value),
316 include_path: []const u8,
317) !void {
318 const include_guard_name = try output.allocator.dupe(u8, include_path);
319 for (include_guard_name) |*byte| {
320 switch (byte.*) {
321 'a'...'z' => byte.* = byte.* - 'a' + 'A',
322 'A'...'Z', '0'...'9' => continue,
323 else => byte.* = '_',
324 }
325 }
326
327 try output.appendSlice("#ifndef ");
328 try output.appendSlice(include_guard_name);
329 try output.appendSlice("\n#define ");
330 try output.appendSlice(include_guard_name);
331 try output.appendSlice("\n");
332
333 const values = defines.values();
334 for (defines.keys()) |name, i| {
335 try renderValue(output, name, values[i]);
336 }
337
338 try output.appendSlice("#endif /* ");
339 try output.appendSlice(include_guard_name);
340 try output.appendSlice(" */\n");
341}
342
343fn renderValue(output: *std.ArrayList(u8), name: []const u8, value: Value) !void {
344 switch (value) {
345 .undef => {
346 try output.appendSlice("/* #undef ");
347 try output.appendSlice(name);
348 try output.appendSlice(" */\n");
349 },
350 .defined => {
351 try output.appendSlice("#define ");
352 try output.appendSlice(name);
353 try output.appendSlice("\n");
354 },
355 .boolean => |b| {
356 try output.appendSlice("#define ");
357 try output.appendSlice(name);
358 try output.appendSlice(" ");
359 try output.appendSlice(if (b) "true\n" else "false\n");
360 },
361 .int => |i| {
362 try output.writer().print("#define {s} {d}\n", .{ name, i });
363 },
364 .ident => |ident| {
365 try output.writer().print("#define {s} {s}\n", .{ name, ident });
366 },
367 .string => |string| {
368 // TODO: use C-specific escaping instead of zig string literals
369 try output.writer().print("#define {s} \"{}\"\n", .{ name, std.zig.fmtEscapes(string) });
370 },
371 }
372}
lib/std/Build/EmulatableRunStep.zig created+213
...@@ -0,0 +1,213 @@
1//! Unlike `RunStep` this step will provide emulation, when enabled, to run foreign binaries.
2//! When a binary is foreign, but emulation for the target is disabled, the specified binary
3//! will not be run and therefore also not validated against its output.
4//! This step can be useful when wishing to run a built binary on multiple platforms,
5//! without having to verify if it's possible to be ran against.
6
7const std = @import("../std.zig");
8const Step = std.Build.Step;
9const CompileStep = std.Build.CompileStep;
10const RunStep = std.Build.RunStep;
11
12const fs = std.fs;
13const process = std.process;
14const EnvMap = process.EnvMap;
15
16const EmulatableRunStep = @This();
17
18pub const base_id = .emulatable_run;
19
20const max_stdout_size = 1 * 1024 * 1024; // 1 MiB
21
22step: Step,
23builder: *std.Build,
24
25/// The artifact (executable) to be run by this step
26exe: *CompileStep,
27
28/// Set this to `null` to ignore the exit code for the purpose of determining a successful execution
29expected_exit_code: ?u8 = 0,
30
31/// Override this field to modify the environment
32env_map: ?*EnvMap,
33
34/// Set this to modify the current working directory
35cwd: ?[]const u8,
36
37stdout_action: RunStep.StdIoAction = .inherit,
38stderr_action: RunStep.StdIoAction = .inherit,
39
40/// When set to true, hides the warning of skipping a foreign binary which cannot be run on the host
41/// or through emulation.
42hide_foreign_binaries_warning: bool,
43
44/// Creates a step that will execute the given artifact. This step will allow running the
45/// binary through emulation when any of the emulation options such as `enable_rosetta` are set to true.
46/// When set to false, and the binary is foreign, running the executable is skipped.
47/// Asserts given artifact is an executable.
48pub fn create(builder: *std.Build, name: []const u8, artifact: *CompileStep) *EmulatableRunStep {
49 std.debug.assert(artifact.kind == .exe or artifact.kind == .test_exe);
50 const self = builder.allocator.create(EmulatableRunStep) catch @panic("OOM");
51
52 const option_name = "hide-foreign-warnings";
53 const hide_warnings = if (builder.available_options_map.get(option_name) == null) warn: {
54 break :warn builder.option(bool, option_name, "Hide the warning when a foreign binary which is incompatible is skipped") orelse false;
55 } else false;
56
57 self.* = .{
58 .builder = builder,
59 .step = Step.init(.emulatable_run, name, builder.allocator, make),
60 .exe = artifact,
61 .env_map = null,
62 .cwd = null,
63 .hide_foreign_binaries_warning = hide_warnings,
64 };
65 self.step.dependOn(&artifact.step);
66
67 return self;
68}
69
70fn make(step: *Step) !void {
71 const self = @fieldParentPtr(EmulatableRunStep, "step", step);
72 const host_info = self.builder.host;
73
74 var argv_list = std.ArrayList([]const u8).init(self.builder.allocator);
75 defer argv_list.deinit();
76
77 const need_cross_glibc = self.exe.target.isGnuLibC() and self.exe.is_linking_libc;
78 switch (host_info.getExternalExecutor(self.exe.target_info, .{
79 .qemu_fixes_dl = need_cross_glibc and self.builder.glibc_runtimes_dir != null,
80 .link_libc = self.exe.is_linking_libc,
81 })) {
82 .native => {},
83 .rosetta => if (!self.builder.enable_rosetta) return warnAboutForeignBinaries(self),
84 .wine => |bin_name| if (self.builder.enable_wine) {
85 try argv_list.append(bin_name);
86 } else return,
87 .qemu => |bin_name| if (self.builder.enable_qemu) {
88 const glibc_dir_arg = if (need_cross_glibc)
89 self.builder.glibc_runtimes_dir orelse return
90 else
91 null;
92 try argv_list.append(bin_name);
93 if (glibc_dir_arg) |dir| {
94 // TODO look into making this a call to `linuxTriple`. This
95 // needs the directory to be called "i686" rather than
96 // "x86" which is why we do it manually here.
97 const fmt_str = "{s}" ++ fs.path.sep_str ++ "{s}-{s}-{s}";
98 const cpu_arch = self.exe.target.getCpuArch();
99 const os_tag = self.exe.target.getOsTag();
100 const abi = self.exe.target.getAbi();
101 const cpu_arch_name: []const u8 = if (cpu_arch == .x86)
102 "i686"
103 else
104 @tagName(cpu_arch);
105 const full_dir = try std.fmt.allocPrint(self.builder.allocator, fmt_str, .{
106 dir, cpu_arch_name, @tagName(os_tag), @tagName(abi),
107 });
108
109 try argv_list.append("-L");
110 try argv_list.append(full_dir);
111 }
112 } else return warnAboutForeignBinaries(self),
113 .darling => |bin_name| if (self.builder.enable_darling) {
114 try argv_list.append(bin_name);
115 } else return warnAboutForeignBinaries(self),
116 .wasmtime => |bin_name| if (self.builder.enable_wasmtime) {
117 try argv_list.append(bin_name);
118 try argv_list.append("--dir=.");
119 } else return warnAboutForeignBinaries(self),
120 else => return warnAboutForeignBinaries(self),
121 }
122
123 if (self.exe.target.isWindows()) {
124 // On Windows we don't have rpaths so we have to add .dll search paths to PATH
125 RunStep.addPathForDynLibsInternal(&self.step, self.builder, self.exe);
126 }
127
128 const executable_path = self.exe.installed_path orelse self.exe.getOutputSource().getPath(self.builder);
129 try argv_list.append(executable_path);
130
131 try RunStep.runCommand(
132 argv_list.items,
133 self.builder,
134 self.expected_exit_code,
135 self.stdout_action,
136 self.stderr_action,
137 .Inherit,
138 self.env_map,
139 self.cwd,
140 false,
141 );
142}
143
144pub fn expectStdErrEqual(self: *EmulatableRunStep, bytes: []const u8) void {
145 self.stderr_action = .{ .expect_exact = self.builder.dupe(bytes) };
146}
147
148pub fn expectStdOutEqual(self: *EmulatableRunStep, bytes: []const u8) void {
149 self.stdout_action = .{ .expect_exact = self.builder.dupe(bytes) };
150}
151
152fn warnAboutForeignBinaries(step: *EmulatableRunStep) void {
153 if (step.hide_foreign_binaries_warning) return;
154 const builder = step.builder;
155 const artifact = step.exe;
156
157 const host_name = builder.host.target.zigTriple(builder.allocator) catch @panic("unhandled error");
158 const foreign_name = artifact.target.zigTriple(builder.allocator) catch @panic("unhandled error");
159 const target_info = std.zig.system.NativeTargetInfo.detect(artifact.target) catch @panic("unhandled error");
160 const need_cross_glibc = artifact.target.isGnuLibC() and artifact.is_linking_libc;
161 switch (builder.host.getExternalExecutor(target_info, .{
162 .qemu_fixes_dl = need_cross_glibc and builder.glibc_runtimes_dir != null,
163 .link_libc = artifact.is_linking_libc,
164 })) {
165 .native => unreachable,
166 .bad_dl => |foreign_dl| {
167 const host_dl = builder.host.dynamic_linker.get() orelse "(none)";
168 std.debug.print("the host system does not appear to be capable of executing binaries from the target because the host dynamic linker is '{s}', while the target dynamic linker is '{s}'. Consider setting the dynamic linker as '{s}'.\n", .{
169 host_dl, foreign_dl, host_dl,
170 });
171 },
172 .bad_os_or_cpu => {
173 std.debug.print("the host system ({s}) does not appear to be capable of executing binaries from the target ({s}).\n", .{
174 host_name, foreign_name,
175 });
176 },
177 .darling => if (!builder.enable_darling) {
178 std.debug.print(
179 "the host system ({s}) does not appear to be capable of executing binaries " ++
180 "from the target ({s}). Consider enabling darling.\n",
181 .{ host_name, foreign_name },
182 );
183 },
184 .rosetta => if (!builder.enable_rosetta) {
185 std.debug.print(
186 "the host system ({s}) does not appear to be capable of executing binaries " ++
187 "from the target ({s}). Consider enabling rosetta.\n",
188 .{ host_name, foreign_name },
189 );
190 },
191 .wine => if (!builder.enable_wine) {
192 std.debug.print(
193 "the host system ({s}) does not appear to be capable of executing binaries " ++
194 "from the target ({s}). Consider enabling wine.\n",
195 .{ host_name, foreign_name },
196 );
197 },
198 .qemu => if (!builder.enable_qemu) {
199 std.debug.print(
200 "the host system ({s}) does not appear to be capable of executing binaries " ++
201 "from the target ({s}). Consider enabling qemu.\n",
202 .{ host_name, foreign_name },
203 );
204 },
205 .wasmtime => {
206 std.debug.print(
207 "the host system ({s}) does not appear to be capable of executing binaries " ++
208 "from the target ({s}). Consider enabling wasmtime.\n",
209 .{ host_name, foreign_name },
210 );
211 },
212 }
213}
lib/std/Build/FmtStep.zig created+32
...@@ -0,0 +1,32 @@
1const std = @import("../std.zig");
2const Step = std.Build.Step;
3const FmtStep = @This();
4
5pub const base_id = .fmt;
6
7step: Step,
8builder: *std.Build,
9argv: [][]const u8,
10
11pub fn create(builder: *std.Build, paths: []const []const u8) *FmtStep {
12 const self = builder.allocator.create(FmtStep) catch @panic("OOM");
13 const name = "zig fmt";
14 self.* = FmtStep{
15 .step = Step.init(.fmt, name, builder.allocator, make),
16 .builder = builder,
17 .argv = builder.allocator.alloc([]u8, paths.len + 2) catch @panic("OOM"),
18 };
19
20 self.argv[0] = builder.zig_exe;
21 self.argv[1] = "fmt";
22 for (paths) |path, i| {
23 self.argv[2 + i] = builder.pathFromRoot(path);
24 }
25 return self;
26}
27
28fn make(step: *Step) !void {
29 const self = @fieldParentPtr(FmtStep, "step", step);
30
31 return self.builder.spawnChild(self.argv);
32}
lib/std/Build/InstallArtifactStep.zig created+85
...@@ -0,0 +1,85 @@
1const std = @import("../std.zig");
2const Step = std.Build.Step;
3const CompileStep = std.Build.CompileStep;
4const InstallDir = std.Build.InstallDir;
5const InstallArtifactStep = @This();
6
7pub const base_id = .install_artifact;
8
9step: Step,
10builder: *std.Build,
11artifact: *CompileStep,
12dest_dir: InstallDir,
13pdb_dir: ?InstallDir,
14h_dir: ?InstallDir,
15
16pub fn create(builder: *std.Build, artifact: *CompileStep) *InstallArtifactStep {
17 if (artifact.install_step) |s| return s;
18
19 const self = builder.allocator.create(InstallArtifactStep) catch @panic("OOM");
20 self.* = InstallArtifactStep{
21 .builder = builder,
22 .step = Step.init(.install_artifact, builder.fmt("install {s}", .{artifact.step.name}), builder.allocator, make),
23 .artifact = artifact,
24 .dest_dir = artifact.override_dest_dir orelse switch (artifact.kind) {
25 .obj => @panic("Cannot install a .obj build artifact."),
26 .@"test" => @panic("Cannot install a .test build artifact, use .test_exe instead."),
27 .exe, .test_exe => InstallDir{ .bin = {} },
28 .lib => InstallDir{ .lib = {} },
29 },
30 .pdb_dir = if (artifact.producesPdbFile()) blk: {
31 if (artifact.kind == .exe or artifact.kind == .test_exe) {
32 break :blk InstallDir{ .bin = {} };
33 } else {
34 break :blk InstallDir{ .lib = {} };
35 }
36 } else null,
37 .h_dir = if (artifact.kind == .lib and artifact.emit_h) .header else null,
38 };
39 self.step.dependOn(&artifact.step);
40 artifact.install_step = self;
41
42 builder.pushInstalledFile(self.dest_dir, artifact.out_filename);
43 if (self.artifact.isDynamicLibrary()) {
44 if (artifact.major_only_filename) |name| {
45 builder.pushInstalledFile(.lib, name);
46 }
47 if (artifact.name_only_filename) |name| {
48 builder.pushInstalledFile(.lib, name);
49 }
50 if (self.artifact.target.isWindows()) {
51 builder.pushInstalledFile(.lib, artifact.out_lib_filename);
52 }
53 }
54 if (self.pdb_dir) |pdb_dir| {
55 builder.pushInstalledFile(pdb_dir, artifact.out_pdb_filename);
56 }
57 if (self.h_dir) |h_dir| {
58 builder.pushInstalledFile(h_dir, artifact.out_h_filename);
59 }
60 return self;
61}
62
63fn make(step: *Step) !void {
64 const self = @fieldParentPtr(InstallArtifactStep, "step", step);
65 const builder = self.builder;
66
67 const full_dest_path = builder.getInstallPath(self.dest_dir, self.artifact.out_filename);
68 try builder.updateFile(self.artifact.getOutputSource().getPath(builder), full_dest_path);
69 if (self.artifact.isDynamicLibrary() and self.artifact.version != null and self.artifact.target.wantSharedLibSymLinks()) {
70 try CompileStep.doAtomicSymLinks(builder.allocator, full_dest_path, self.artifact.major_only_filename.?, self.artifact.name_only_filename.?);
71 }
72 if (self.artifact.isDynamicLibrary() and self.artifact.target.isWindows() and self.artifact.emit_implib != .no_emit) {
73 const full_implib_path = builder.getInstallPath(self.dest_dir, self.artifact.out_lib_filename);
74 try builder.updateFile(self.artifact.getOutputLibSource().getPath(builder), full_implib_path);
75 }
76 if (self.pdb_dir) |pdb_dir| {
77 const full_pdb_path = builder.getInstallPath(pdb_dir, self.artifact.out_pdb_filename);
78 try builder.updateFile(self.artifact.getOutputPdbSource().getPath(builder), full_pdb_path);
79 }
80 if (self.h_dir) |h_dir| {
81 const full_h_path = builder.getInstallPath(h_dir, self.artifact.out_h_filename);
82 try builder.updateFile(self.artifact.getOutputHSource().getPath(builder), full_h_path);
83 }
84 self.artifact.installed_path = full_dest_path;
85}
lib/std/Build/InstallDirStep.zig created+93
...@@ -0,0 +1,93 @@
1const std = @import("../std.zig");
2const mem = std.mem;
3const fs = std.fs;
4const Step = std.Build.Step;
5const InstallDir = std.Build.InstallDir;
6const InstallDirStep = @This();
7const log = std.log;
8
9step: Step,
10builder: *std.Build,
11options: Options,
12/// This is used by the build system when a file being installed comes from one
13/// package but is being installed by another.
14override_source_builder: ?*std.Build = null,
15
16pub const base_id = .install_dir;
17
18pub const Options = struct {
19 source_dir: []const u8,
20 install_dir: InstallDir,
21 install_subdir: []const u8,
22 /// File paths which end in any of these suffixes will be excluded
23 /// from being installed.
24 exclude_extensions: []const []const u8 = &.{},
25 /// File paths which end in any of these suffixes will result in
26 /// empty files being installed. This is mainly intended for large
27 /// test.zig files in order to prevent needless installation bloat.
28 /// However if the files were not present at all, then
29 /// `@import("test.zig")` would be a compile error.
30 blank_extensions: []const []const u8 = &.{},
31
32 fn dupe(self: Options, b: *std.Build) Options {
33 return .{
34 .source_dir = b.dupe(self.source_dir),
35 .install_dir = self.install_dir.dupe(b),
36 .install_subdir = b.dupe(self.install_subdir),
37 .exclude_extensions = b.dupeStrings(self.exclude_extensions),
38 .blank_extensions = b.dupeStrings(self.blank_extensions),
39 };
40 }
41};
42
43pub fn init(
44 builder: *std.Build,
45 options: Options,
46) InstallDirStep {
47 builder.pushInstalledFile(options.install_dir, options.install_subdir);
48 return InstallDirStep{
49 .builder = builder,
50 .step = Step.init(.install_dir, builder.fmt("install {s}/", .{options.source_dir}), builder.allocator, make),
51 .options = options.dupe(builder),
52 };
53}
54
55fn make(step: *Step) !void {
56 const self = @fieldParentPtr(InstallDirStep, "step", step);
57 const dest_prefix = self.builder.getInstallPath(self.options.install_dir, self.options.install_subdir);
58 const src_builder = self.override_source_builder orelse self.builder;
59 const full_src_dir = src_builder.pathFromRoot(self.options.source_dir);
60 var src_dir = std.fs.cwd().openIterableDir(full_src_dir, .{}) catch |err| {
61 log.err("InstallDirStep: unable to open source directory '{s}': {s}", .{
62 full_src_dir, @errorName(err),
63 });
64 return error.StepFailed;
65 };
66 defer src_dir.close();
67 var it = try src_dir.walk(self.builder.allocator);
68 next_entry: while (try it.next()) |entry| {
69 for (self.options.exclude_extensions) |ext| {
70 if (mem.endsWith(u8, entry.path, ext)) {
71 continue :next_entry;
72 }
73 }
74
75 const full_path = self.builder.pathJoin(&.{ full_src_dir, entry.path });
76 const dest_path = self.builder.pathJoin(&.{ dest_prefix, entry.path });
77
78 switch (entry.kind) {
79 .Directory => try fs.cwd().makePath(dest_path),
80 .File => {
81 for (self.options.blank_extensions) |ext| {
82 if (mem.endsWith(u8, entry.path, ext)) {
83 try self.builder.truncateFile(dest_path);
84 continue :next_entry;
85 }
86 }
87
88 try self.builder.updateFile(full_path, dest_path);
89 },
90 else => continue,
91 }
92 }
93}
lib/std/Build/InstallFileStep.zig created+40
...@@ -0,0 +1,40 @@
1const std = @import("../std.zig");
2const Step = std.Build.Step;
3const FileSource = std.Build.FileSource;
4const InstallDir = std.Build.InstallDir;
5const InstallFileStep = @This();
6
7pub const base_id = .install_file;
8
9step: Step,
10builder: *std.Build,
11source: FileSource,
12dir: InstallDir,
13dest_rel_path: []const u8,
14/// This is used by the build system when a file being installed comes from one
15/// package but is being installed by another.
16override_source_builder: ?*std.Build = null,
17
18pub fn init(
19 builder: *std.Build,
20 source: FileSource,
21 dir: InstallDir,
22 dest_rel_path: []const u8,
23) InstallFileStep {
24 builder.pushInstalledFile(dir, dest_rel_path);
25 return InstallFileStep{
26 .builder = builder,
27 .step = Step.init(.install_file, builder.fmt("install {s} to {s}", .{ source.getDisplayName(), dest_rel_path }), builder.allocator, make),
28 .source = source.dupe(builder),
29 .dir = dir.dupe(builder),
30 .dest_rel_path = builder.dupePath(dest_rel_path),
31 };
32}
33
34fn make(step: *Step) !void {
35 const self = @fieldParentPtr(InstallFileStep, "step", step);
36 const src_builder = self.override_source_builder orelse self.builder;
37 const full_src_path = self.source.getPath(src_builder);
38 const full_dest_path = self.builder.getInstallPath(self.dir, self.dest_rel_path);
39 try self.builder.updateFile(full_src_path, full_dest_path);
40}
lib/std/Build/InstallRawStep.zig created+110
...@@ -0,0 +1,110 @@
1//! TODO: Rename this to ObjCopyStep now that it invokes the `zig objcopy`
2//! subcommand rather than containing an implementation directly.
3
4const std = @import("std");
5const InstallRawStep = @This();
6
7const Allocator = std.mem.Allocator;
8const ArenaAllocator = std.heap.ArenaAllocator;
9const ArrayListUnmanaged = std.ArrayListUnmanaged;
10const File = std.fs.File;
11const InstallDir = std.Build.InstallDir;
12const CompileStep = std.Build.CompileStep;
13const Step = std.Build.Step;
14const elf = std.elf;
15const fs = std.fs;
16const io = std.io;
17const sort = std.sort;
18
19pub const base_id = .install_raw;
20
21pub const RawFormat = enum {
22 bin,
23 hex,
24};
25
26step: Step,
27builder: *std.Build,
28artifact: *CompileStep,
29dest_dir: InstallDir,
30dest_filename: []const u8,
31options: CreateOptions,
32output_file: std.Build.GeneratedFile,
33
34pub const CreateOptions = struct {
35 format: ?RawFormat = null,
36 dest_dir: ?InstallDir = null,
37 only_section: ?[]const u8 = null,
38 pad_to: ?u64 = null,
39};
40
41pub fn create(
42 builder: *std.Build,
43 artifact: *CompileStep,
44 dest_filename: []const u8,
45 options: CreateOptions,
46) *InstallRawStep {
47 const self = builder.allocator.create(InstallRawStep) catch @panic("OOM");
48 self.* = InstallRawStep{
49 .step = Step.init(.install_raw, builder.fmt("install raw binary {s}", .{artifact.step.name}), builder.allocator, make),
50 .builder = builder,
51 .artifact = artifact,
52 .dest_dir = if (options.dest_dir) |d| d else switch (artifact.kind) {
53 .obj => unreachable,
54 .@"test" => unreachable,
55 .exe, .test_exe => .bin,
56 .lib => unreachable,
57 },
58 .dest_filename = dest_filename,
59 .options = options,
60 .output_file = std.Build.GeneratedFile{ .step = &self.step },
61 };
62 self.step.dependOn(&artifact.step);
63
64 builder.pushInstalledFile(self.dest_dir, dest_filename);
65 return self;
66}
67
68pub fn getOutputSource(self: *const InstallRawStep) std.Build.FileSource {
69 return std.Build.FileSource{ .generated = &self.output_file };
70}
71
72fn make(step: *Step) !void {
73 const self = @fieldParentPtr(InstallRawStep, "step", step);
74 const b = self.builder;
75
76 if (self.artifact.target.getObjectFormat() != .elf) {
77 std.debug.print("InstallRawStep only works with ELF format.\n", .{});
78 return error.InvalidObjectFormat;
79 }
80
81 const full_src_path = self.artifact.getOutputSource().getPath(b);
82 const full_dest_path = b.getInstallPath(self.dest_dir, self.dest_filename);
83 self.output_file.path = full_dest_path;
84
85 try fs.cwd().makePath(b.getInstallPath(self.dest_dir, ""));
86
87 var argv_list = std.ArrayList([]const u8).init(b.allocator);
88 try argv_list.appendSlice(&.{ b.zig_exe, "objcopy" });
89
90 if (self.options.only_section) |only_section| {
91 try argv_list.appendSlice(&.{ "-j", only_section });
92 }
93 if (self.options.pad_to) |pad_to| {
94 try argv_list.appendSlice(&.{
95 "--pad-to",
96 b.fmt("{d}", .{pad_to}),
97 });
98 }
99 if (self.options.format) |format| switch (format) {
100 .bin => try argv_list.appendSlice(&.{ "-O", "binary" }),
101 .hex => try argv_list.appendSlice(&.{ "-O", "hex" }),
102 };
103
104 try argv_list.appendSlice(&.{ full_src_path, full_dest_path });
105 _ = try self.builder.execFromStep(argv_list.items, &self.step);
106}
107
108test {
109 std.testing.refAllDecls(InstallRawStep);
110}
lib/std/Build/LogStep.zig created+23
...@@ -0,0 +1,23 @@
1const std = @import("../std.zig");
2const log = std.log;
3const Step = std.Build.Step;
4const LogStep = @This();
5
6pub const base_id = .log;
7
8step: Step,
9builder: *std.Build,
10data: []const u8,
11
12pub fn init(builder: *std.Build, data: []const u8) LogStep {
13 return LogStep{
14 .builder = builder,
15 .step = Step.init(.log, builder.fmt("log {s}", .{data}), builder.allocator, make),
16 .data = builder.dupe(data),
17 };
18}
19
20fn make(step: *Step) anyerror!void {
21 const self = @fieldParentPtr(LogStep, "step", step);
22 log.info("{s}", .{self.data});
23}
lib/std/Build/OptionsStep.zig created+374
...@@ -0,0 +1,374 @@
1const std = @import("../std.zig");
2const builtin = @import("builtin");
3const fs = std.fs;
4const Step = std.Build.Step;
5const GeneratedFile = std.Build.GeneratedFile;
6const CompileStep = std.Build.CompileStep;
7const FileSource = std.Build.FileSource;
8
9const OptionsStep = @This();
10
11pub const base_id = .options;
12
13step: Step,
14generated_file: GeneratedFile,
15builder: *std.Build,
16
17contents: std.ArrayList(u8),
18artifact_args: std.ArrayList(OptionArtifactArg),
19file_source_args: std.ArrayList(OptionFileSourceArg),
20
21pub fn create(builder: *std.Build) *OptionsStep {
22 const self = builder.allocator.create(OptionsStep) catch @panic("OOM");
23 self.* = .{
24 .builder = builder,
25 .step = Step.init(.options, "options", builder.allocator, make),
26 .generated_file = undefined,
27 .contents = std.ArrayList(u8).init(builder.allocator),
28 .artifact_args = std.ArrayList(OptionArtifactArg).init(builder.allocator),
29 .file_source_args = std.ArrayList(OptionFileSourceArg).init(builder.allocator),
30 };
31 self.generated_file = .{ .step = &self.step };
32
33 return self;
34}
35
36pub fn addOption(self: *OptionsStep, comptime T: type, name: []const u8, value: T) void {
37 return addOptionFallible(self, T, name, value) catch @panic("unhandled error");
38}
39
40fn addOptionFallible(self: *OptionsStep, comptime T: type, name: []const u8, value: T) !void {
41 const out = self.contents.writer();
42 switch (T) {
43 []const []const u8 => {
44 try out.print("pub const {}: []const []const u8 = &[_][]const u8{{\n", .{std.zig.fmtId(name)});
45 for (value) |slice| {
46 try out.print(" \"{}\",\n", .{std.zig.fmtEscapes(slice)});
47 }
48 try out.writeAll("};\n");
49 return;
50 },
51 [:0]const u8 => {
52 try out.print("pub const {}: [:0]const u8 = \"{}\";\n", .{ std.zig.fmtId(name), std.zig.fmtEscapes(value) });
53 return;
54 },
55 []const u8 => {
56 try out.print("pub const {}: []const u8 = \"{}\";\n", .{ std.zig.fmtId(name), std.zig.fmtEscapes(value) });
57 return;
58 },
59 ?[:0]const u8 => {
60 try out.print("pub const {}: ?[:0]const u8 = ", .{std.zig.fmtId(name)});
61 if (value) |payload| {
62 try out.print("\"{}\";\n", .{std.zig.fmtEscapes(payload)});
63 } else {
64 try out.writeAll("null;\n");
65 }
66 return;
67 },
68 ?[]const u8 => {
69 try out.print("pub const {}: ?[]const u8 = ", .{std.zig.fmtId(name)});
70 if (value) |payload| {
71 try out.print("\"{}\";\n", .{std.zig.fmtEscapes(payload)});
72 } else {
73 try out.writeAll("null;\n");
74 }
75 return;
76 },
77 std.builtin.Version => {
78 try out.print(
79 \\pub const {}: @import("std").builtin.Version = .{{
80 \\ .major = {d},
81 \\ .minor = {d},
82 \\ .patch = {d},
83 \\}};
84 \\
85 , .{
86 std.zig.fmtId(name),
87
88 value.major,
89 value.minor,
90 value.patch,
91 });
92 return;
93 },
94 std.SemanticVersion => {
95 try out.print(
96 \\pub const {}: @import("std").SemanticVersion = .{{
97 \\ .major = {d},
98 \\ .minor = {d},
99 \\ .patch = {d},
100 \\
101 , .{
102 std.zig.fmtId(name),
103
104 value.major,
105 value.minor,
106 value.patch,
107 });
108 if (value.pre) |some| {
109 try out.print(" .pre = \"{}\",\n", .{std.zig.fmtEscapes(some)});
110 }
111 if (value.build) |some| {
112 try out.print(" .build = \"{}\",\n", .{std.zig.fmtEscapes(some)});
113 }
114 try out.writeAll("};\n");
115 return;
116 },
117 else => {},
118 }
119 switch (@typeInfo(T)) {
120 .Enum => |enum_info| {
121 try out.print("pub const {} = enum {{\n", .{std.zig.fmtId(@typeName(T))});
122 inline for (enum_info.fields) |field| {
123 try out.print(" {},\n", .{std.zig.fmtId(field.name)});
124 }
125 try out.writeAll("};\n");
126 try out.print("pub const {}: {s} = {s}.{s};\n", .{
127 std.zig.fmtId(name),
128 std.zig.fmtId(@typeName(T)),
129 std.zig.fmtId(@typeName(T)),
130 std.zig.fmtId(@tagName(value)),
131 });
132 return;
133 },
134 else => {},
135 }
136 try out.print("pub const {}: {s} = ", .{ std.zig.fmtId(name), @typeName(T) });
137 try printLiteral(out, value, 0);
138 try out.writeAll(";\n");
139}
140
141// TODO: non-recursive?
142fn printLiteral(out: anytype, val: anytype, indent: u8) !void {
143 const T = @TypeOf(val);
144 switch (@typeInfo(T)) {
145 .Array => {
146 try out.print("{s} {{\n", .{@typeName(T)});
147 for (val) |item| {
148 try out.writeByteNTimes(' ', indent + 4);
149 try printLiteral(out, item, indent + 4);
150 try out.writeAll(",\n");
151 }
152 try out.writeByteNTimes(' ', indent);
153 try out.writeAll("}");
154 },
155 .Pointer => |p| {
156 if (p.size != .Slice) {
157 @compileError("Non-slice pointers are not yet supported in build options");
158 }
159 try out.print("&[_]{s} {{\n", .{@typeName(p.child)});
160 for (val) |item| {
161 try out.writeByteNTimes(' ', indent + 4);
162 try printLiteral(out, item, indent + 4);
163 try out.writeAll(",\n");
164 }
165 try out.writeByteNTimes(' ', indent);
166 try out.writeAll("}");
167 },
168 .Optional => {
169 if (val) |inner| {
170 return printLiteral(out, inner, indent);
171 } else {
172 return out.writeAll("null");
173 }
174 },
175 .Void,
176 .Bool,
177 .Int,
178 .ComptimeInt,
179 .Float,
180 .Null,
181 => try out.print("{any}", .{val}),
182 else => @compileError(std.fmt.comptimePrint("`{s}` are not yet supported as build options", .{@tagName(@typeInfo(T))})),
183 }
184}
185
186/// The value is the path in the cache dir.
187/// Adds a dependency automatically.
188pub fn addOptionFileSource(
189 self: *OptionsStep,
190 name: []const u8,
191 source: FileSource,
192) void {
193 self.file_source_args.append(.{
194 .name = name,
195 .source = source.dupe(self.builder),
196 }) catch @panic("OOM");
197 source.addStepDependencies(&self.step);
198}
199
200/// The value is the path in the cache dir.
201/// Adds a dependency automatically.
202pub fn addOptionArtifact(self: *OptionsStep, name: []const u8, artifact: *CompileStep) void {
203 self.artifact_args.append(.{ .name = self.builder.dupe(name), .artifact = artifact }) catch @panic("OOM");
204 self.step.dependOn(&artifact.step);
205}
206
207pub fn createModule(self: *OptionsStep) *std.Build.Module {
208 return self.builder.createModule(.{
209 .source_file = self.getSource(),
210 .dependencies = &.{},
211 });
212}
213
214pub fn getSource(self: *OptionsStep) FileSource {
215 return .{ .generated = &self.generated_file };
216}
217
218fn make(step: *Step) !void {
219 const self = @fieldParentPtr(OptionsStep, "step", step);
220
221 for (self.artifact_args.items) |item| {
222 self.addOption(
223 []const u8,
224 item.name,
225 self.builder.pathFromRoot(item.artifact.getOutputSource().getPath(self.builder)),
226 );
227 }
228
229 for (self.file_source_args.items) |item| {
230 self.addOption(
231 []const u8,
232 item.name,
233 item.source.getPath(self.builder),
234 );
235 }
236
237 const options_directory = self.builder.pathFromRoot(
238 try fs.path.join(
239 self.builder.allocator,
240 &[_][]const u8{ self.builder.cache_root, "options" },
241 ),
242 );
243
244 try fs.cwd().makePath(options_directory);
245
246 const options_file = try fs.path.join(
247 self.builder.allocator,
248 &[_][]const u8{ options_directory, &self.hashContentsToFileName() },
249 );
250
251 try fs.cwd().writeFile(options_file, self.contents.items);
252
253 self.generated_file.path = options_file;
254}
255
256fn hashContentsToFileName(self: *OptionsStep) [64]u8 {
257 // This implementation is copied from `WriteFileStep.make`
258
259 var hash = std.crypto.hash.blake2.Blake2b384.init(.{});
260
261 // Random bytes to make OptionsStep unique. Refresh this with
262 // new random bytes when OptionsStep implementation is modified
263 // in a non-backwards-compatible way.
264 hash.update("yL0Ya4KkmcCjBlP8");
265 hash.update(self.contents.items);
266
267 var digest: [48]u8 = undefined;
268 hash.final(&digest);
269 var hash_basename: [64]u8 = undefined;
270 _ = fs.base64_encoder.encode(&hash_basename, &digest);
271 return hash_basename;
272}
273
274const OptionArtifactArg = struct {
275 name: []const u8,
276 artifact: *CompileStep,
277};
278
279const OptionFileSourceArg = struct {
280 name: []const u8,
281 source: FileSource,
282};
283
284test "OptionsStep" {
285 if (builtin.os.tag == .wasi) return error.SkipZigTest;
286
287 var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
288 defer arena.deinit();
289
290 const host = try std.zig.system.NativeTargetInfo.detect(.{});
291
292 var builder = try std.Build.create(
293 arena.allocator(),
294 "test",
295 "test",
296 "test",
297 "test",
298 host,
299 );
300 defer builder.destroy();
301
302 const options = builder.addOptions();
303
304 // TODO this regressed at some point
305 //const KeywordEnum = enum {
306 // @"0.8.1",
307 //};
308
309 const nested_array = [2][2]u16{
310 [2]u16{ 300, 200 },
311 [2]u16{ 300, 200 },
312 };
313 const nested_slice: []const []const u16 = &[_][]const u16{ &nested_array[0], &nested_array[1] };
314
315 options.addOption(usize, "option1", 1);
316 options.addOption(?usize, "option2", null);
317 options.addOption(?usize, "option3", 3);
318 options.addOption(comptime_int, "option4", 4);
319 options.addOption([]const u8, "string", "zigisthebest");
320 options.addOption(?[]const u8, "optional_string", null);
321 options.addOption([2][2]u16, "nested_array", nested_array);
322 options.addOption([]const []const u16, "nested_slice", nested_slice);
323 //options.addOption(KeywordEnum, "keyword_enum", .@"0.8.1");
324 options.addOption(std.builtin.Version, "version", try std.builtin.Version.parse("0.1.2"));
325 options.addOption(std.SemanticVersion, "semantic_version", try std.SemanticVersion.parse("0.1.2-foo+bar"));
326
327 try std.testing.expectEqualStrings(
328 \\pub const option1: usize = 1;
329 \\pub const option2: ?usize = null;
330 \\pub const option3: ?usize = 3;
331 \\pub const option4: comptime_int = 4;
332 \\pub const string: []const u8 = "zigisthebest";
333 \\pub const optional_string: ?[]const u8 = null;
334 \\pub const nested_array: [2][2]u16 = [2][2]u16 {
335 \\ [2]u16 {
336 \\ 300,
337 \\ 200,
338 \\ },
339 \\ [2]u16 {
340 \\ 300,
341 \\ 200,
342 \\ },
343 \\};
344 \\pub const nested_slice: []const []const u16 = &[_][]const u16 {
345 \\ &[_]u16 {
346 \\ 300,
347 \\ 200,
348 \\ },
349 \\ &[_]u16 {
350 \\ 300,
351 \\ 200,
352 \\ },
353 \\};
354 //\\pub const KeywordEnum = enum {
355 //\\ @"0.8.1",
356 //\\};
357 //\\pub const keyword_enum: KeywordEnum = KeywordEnum.@"0.8.1";
358 \\pub const version: @import("std").builtin.Version = .{
359 \\ .major = 0,
360 \\ .minor = 1,
361 \\ .patch = 2,
362 \\};
363 \\pub const semantic_version: @import("std").SemanticVersion = .{
364 \\ .major = 0,
365 \\ .minor = 1,
366 \\ .patch = 2,
367 \\ .pre = "foo",
368 \\ .build = "bar",
369 \\};
370 \\
371 , options.contents.items);
372
373 _ = try std.zig.Ast.parse(arena.allocator(), try options.contents.toOwnedSliceSentinel(0), .zig);
374}
lib/std/Build/RemoveDirStep.zig created+29
...@@ -0,0 +1,29 @@
1const std = @import("../std.zig");
2const log = std.log;
3const fs = std.fs;
4const Step = std.Build.Step;
5const RemoveDirStep = @This();
6
7pub const base_id = .remove_dir;
8
9step: Step,
10builder: *std.Build,
11dir_path: []const u8,
12
13pub fn init(builder: *std.Build, dir_path: []const u8) RemoveDirStep {
14 return RemoveDirStep{
15 .builder = builder,
16 .step = Step.init(.remove_dir, builder.fmt("RemoveDir {s}", .{dir_path}), builder.allocator, make),
17 .dir_path = builder.dupePath(dir_path),
18 };
19}
20
21fn make(step: *Step) !void {
22 const self = @fieldParentPtr(RemoveDirStep, "step", step);
23
24 const full_path = self.builder.pathFromRoot(self.dir_path);
25 fs.cwd().deleteTree(full_path) catch |err| {
26 log.err("Unable to remove {s}: {s}", .{ full_path, @errorName(err) });
27 return err;
28 };
29}
lib/std/Build/RunStep.zig created+376
...@@ -0,0 +1,376 @@
1const std = @import("../std.zig");
2const builtin = @import("builtin");
3const Step = std.Build.Step;
4const CompileStep = std.Build.CompileStep;
5const WriteFileStep = std.Build.WriteFileStep;
6const fs = std.fs;
7const mem = std.mem;
8const process = std.process;
9const ArrayList = std.ArrayList;
10const EnvMap = process.EnvMap;
11const Allocator = mem.Allocator;
12const ExecError = std.Build.ExecError;
13
14const max_stdout_size = 1 * 1024 * 1024; // 1 MiB
15
16const RunStep = @This();
17
18pub const base_id: Step.Id = .run;
19
20step: Step,
21builder: *std.Build,
22
23/// See also addArg and addArgs to modifying this directly
24argv: ArrayList(Arg),
25
26/// Set this to modify the current working directory
27cwd: ?[]const u8,
28
29/// Override this field to modify the environment, or use setEnvironmentVariable
30env_map: ?*EnvMap,
31
32stdout_action: StdIoAction = .inherit,
33stderr_action: StdIoAction = .inherit,
34
35stdin_behavior: std.ChildProcess.StdIo = .Inherit,
36
37/// Set this to `null` to ignore the exit code for the purpose of determining a successful execution
38expected_exit_code: ?u8 = 0,
39
40/// Print the command before running it
41print: bool,
42
43pub const StdIoAction = union(enum) {
44 inherit,
45 ignore,
46 expect_exact: []const u8,
47 expect_matches: []const []const u8,
48};
49
50pub const Arg = union(enum) {
51 artifact: *CompileStep,
52 file_source: std.Build.FileSource,
53 bytes: []u8,
54};
55
56pub fn create(builder: *std.Build, name: []const u8) *RunStep {
57 const self = builder.allocator.create(RunStep) catch @panic("OOM");
58 self.* = RunStep{
59 .builder = builder,
60 .step = Step.init(base_id, name, builder.allocator, make),
61 .argv = ArrayList(Arg).init(builder.allocator),
62 .cwd = null,
63 .env_map = null,
64 .print = builder.verbose,
65 };
66 return self;
67}
68
69pub fn addArtifactArg(self: *RunStep, artifact: *CompileStep) void {
70 self.argv.append(Arg{ .artifact = artifact }) catch @panic("OOM");
71 self.step.dependOn(&artifact.step);
72}
73
74pub fn addFileSourceArg(self: *RunStep, file_source: std.Build.FileSource) void {
75 self.argv.append(Arg{
76 .file_source = file_source.dupe(self.builder),
77 }) catch @panic("OOM");
78 file_source.addStepDependencies(&self.step);
79}
80
81pub fn addArg(self: *RunStep, arg: []const u8) void {
82 self.argv.append(Arg{ .bytes = self.builder.dupe(arg) }) catch @panic("OOM");
83}
84
85pub fn addArgs(self: *RunStep, args: []const []const u8) void {
86 for (args) |arg| {
87 self.addArg(arg);
88 }
89}
90
91pub fn clearEnvironment(self: *RunStep) void {
92 const new_env_map = self.builder.allocator.create(EnvMap) catch @panic("OOM");
93 new_env_map.* = EnvMap.init(self.builder.allocator);
94 self.env_map = new_env_map;
95}
96
97pub fn addPathDir(self: *RunStep, search_path: []const u8) void {
98 addPathDirInternal(&self.step, self.builder, search_path);
99}
100
101/// For internal use only, users of `RunStep` should use `addPathDir` directly.
102pub fn addPathDirInternal(step: *Step, builder: *std.Build, search_path: []const u8) void {
103 const env_map = getEnvMapInternal(step, builder.allocator);
104
105 const key = "PATH";
106 var prev_path = env_map.get(key);
107
108 if (prev_path) |pp| {
109 const new_path = builder.fmt("{s}" ++ [1]u8{fs.path.delimiter} ++ "{s}", .{ pp, search_path });
110 env_map.put(key, new_path) catch @panic("OOM");
111 } else {
112 env_map.put(key, builder.dupePath(search_path)) catch @panic("OOM");
113 }
114}
115
116pub fn getEnvMap(self: *RunStep) *EnvMap {
117 return getEnvMapInternal(&self.step, self.builder.allocator);
118}
119
120fn getEnvMapInternal(step: *Step, allocator: Allocator) *EnvMap {
121 const maybe_env_map = switch (step.id) {
122 .run => step.cast(RunStep).?.env_map,
123 .emulatable_run => step.cast(std.Build.EmulatableRunStep).?.env_map,
124 else => unreachable,
125 };
126 return maybe_env_map orelse {
127 const env_map = allocator.create(EnvMap) catch @panic("OOM");
128 env_map.* = process.getEnvMap(allocator) catch @panic("unhandled error");
129 switch (step.id) {
130 .run => step.cast(RunStep).?.env_map = env_map,
131 .emulatable_run => step.cast(RunStep).?.env_map = env_map,
132 else => unreachable,
133 }
134 return env_map;
135 };
136}
137
138pub fn setEnvironmentVariable(self: *RunStep, key: []const u8, value: []const u8) void {
139 const env_map = self.getEnvMap();
140 env_map.put(
141 self.builder.dupe(key),
142 self.builder.dupe(value),
143 ) catch @panic("unhandled error");
144}
145
146pub fn expectStdErrEqual(self: *RunStep, bytes: []const u8) void {
147 self.stderr_action = .{ .expect_exact = self.builder.dupe(bytes) };
148}
149
150pub fn expectStdOutEqual(self: *RunStep, bytes: []const u8) void {
151 self.stdout_action = .{ .expect_exact = self.builder.dupe(bytes) };
152}
153
154fn stdIoActionToBehavior(action: StdIoAction) std.ChildProcess.StdIo {
155 return switch (action) {
156 .ignore => .Ignore,
157 .inherit => .Inherit,
158 .expect_exact, .expect_matches => .Pipe,
159 };
160}
161
162fn make(step: *Step) !void {
163 const self = @fieldParentPtr(RunStep, "step", step);
164
165 var argv_list = ArrayList([]const u8).init(self.builder.allocator);
166 for (self.argv.items) |arg| {
167 switch (arg) {
168 .bytes => |bytes| try argv_list.append(bytes),
169 .file_source => |file| try argv_list.append(file.getPath(self.builder)),
170 .artifact => |artifact| {
171 if (artifact.target.isWindows()) {
172 // On Windows we don't have rpaths so we have to add .dll search paths to PATH
173 self.addPathForDynLibs(artifact);
174 }
175 const executable_path = artifact.installed_path orelse artifact.getOutputSource().getPath(self.builder);
176 try argv_list.append(executable_path);
177 },
178 }
179 }
180
181 try runCommand(
182 argv_list.items,
183 self.builder,
184 self.expected_exit_code,
185 self.stdout_action,
186 self.stderr_action,
187 self.stdin_behavior,
188 self.env_map,
189 self.cwd,
190 self.print,
191 );
192}
193
194pub fn runCommand(
195 argv: []const []const u8,
196 builder: *std.Build,
197 expected_exit_code: ?u8,
198 stdout_action: StdIoAction,
199 stderr_action: StdIoAction,
200 stdin_behavior: std.ChildProcess.StdIo,
201 env_map: ?*EnvMap,
202 maybe_cwd: ?[]const u8,
203 print: bool,
204) !void {
205 const cwd = if (maybe_cwd) |cwd| builder.pathFromRoot(cwd) else builder.build_root;
206
207 if (!std.process.can_spawn) {
208 const cmd = try std.mem.join(builder.allocator, " ", argv);
209 std.debug.print("the following command cannot be executed ({s} does not support spawning a child process):\n{s}", .{ @tagName(builtin.os.tag), cmd });
210 builder.allocator.free(cmd);
211 return ExecError.ExecNotSupported;
212 }
213
214 var child = std.ChildProcess.init(argv, builder.allocator);
215 child.cwd = cwd;
216 child.env_map = env_map orelse builder.env_map;
217
218 child.stdin_behavior = stdin_behavior;
219 child.stdout_behavior = stdIoActionToBehavior(stdout_action);
220 child.stderr_behavior = stdIoActionToBehavior(stderr_action);
221
222 if (print)
223 printCmd(cwd, argv);
224
225 child.spawn() catch |err| {
226 std.debug.print("Unable to spawn {s}: {s}\n", .{ argv[0], @errorName(err) });
227 return err;
228 };
229
230 // TODO need to poll to read these streams to prevent a deadlock (or rely on evented I/O).
231
232 var stdout: ?[]const u8 = null;
233 defer if (stdout) |s| builder.allocator.free(s);
234
235 switch (stdout_action) {
236 .expect_exact, .expect_matches => {
237 stdout = try child.stdout.?.reader().readAllAlloc(builder.allocator, max_stdout_size);
238 },
239 .inherit, .ignore => {},
240 }
241
242 var stderr: ?[]const u8 = null;
243 defer if (stderr) |s| builder.allocator.free(s);
244
245 switch (stderr_action) {
246 .expect_exact, .expect_matches => {
247 stderr = try child.stderr.?.reader().readAllAlloc(builder.allocator, max_stdout_size);
248 },
249 .inherit, .ignore => {},
250 }
251
252 const term = child.wait() catch |err| {
253 std.debug.print("Unable to spawn {s}: {s}\n", .{ argv[0], @errorName(err) });
254 return err;
255 };
256
257 switch (term) {
258 .Exited => |code| blk: {
259 const expected_code = expected_exit_code orelse break :blk;
260
261 if (code != expected_code) {
262 if (builder.prominent_compile_errors) {
263 std.debug.print("Run step exited with error code {} (expected {})\n", .{
264 code,
265 expected_code,
266 });
267 } else {
268 std.debug.print("The following command exited with error code {} (expected {}):\n", .{
269 code,
270 expected_code,
271 });
272 printCmd(cwd, argv);
273 }
274
275 return error.UnexpectedExitCode;
276 }
277 },
278 else => {
279 std.debug.print("The following command terminated unexpectedly:\n", .{});
280 printCmd(cwd, argv);
281 return error.UncleanExit;
282 },
283 }
284
285 switch (stderr_action) {
286 .inherit, .ignore => {},
287 .expect_exact => |expected_bytes| {
288 if (!mem.eql(u8, expected_bytes, stderr.?)) {
289 std.debug.print(
290 \\
291 \\========= Expected this stderr: =========
292 \\{s}
293 \\========= But found: ====================
294 \\{s}
295 \\
296 , .{ expected_bytes, stderr.? });
297 printCmd(cwd, argv);
298 return error.TestFailed;
299 }
300 },
301 .expect_matches => |matches| for (matches) |match| {
302 if (mem.indexOf(u8, stderr.?, match) == null) {
303 std.debug.print(
304 \\
305 \\========= Expected to find in stderr: =========
306 \\{s}
307 \\========= But stderr does not contain it: =====
308 \\{s}
309 \\
310 , .{ match, stderr.? });
311 printCmd(cwd, argv);
312 return error.TestFailed;
313 }
314 },
315 }
316
317 switch (stdout_action) {
318 .inherit, .ignore => {},
319 .expect_exact => |expected_bytes| {
320 if (!mem.eql(u8, expected_bytes, stdout.?)) {
321 std.debug.print(
322 \\
323 \\========= Expected this stdout: =========
324 \\{s}
325 \\========= But found: ====================
326 \\{s}
327 \\
328 , .{ expected_bytes, stdout.? });
329 printCmd(cwd, argv);
330 return error.TestFailed;
331 }
332 },
333 .expect_matches => |matches| for (matches) |match| {
334 if (mem.indexOf(u8, stdout.?, match) == null) {
335 std.debug.print(
336 \\
337 \\========= Expected to find in stdout: =========
338 \\{s}
339 \\========= But stdout does not contain it: =====
340 \\{s}
341 \\
342 , .{ match, stdout.? });
343 printCmd(cwd, argv);
344 return error.TestFailed;
345 }
346 },
347 }
348}
349
350fn printCmd(cwd: ?[]const u8, argv: []const []const u8) void {
351 if (cwd) |yes_cwd| std.debug.print("cd {s} && ", .{yes_cwd});
352 for (argv) |arg| {
353 std.debug.print("{s} ", .{arg});
354 }
355 std.debug.print("\n", .{});
356}
357
358fn addPathForDynLibs(self: *RunStep, artifact: *CompileStep) void {
359 addPathForDynLibsInternal(&self.step, self.builder, artifact);
360}
361
362/// This should only be used for internal usage, this is called automatically
363/// for the user.
364pub fn addPathForDynLibsInternal(step: *Step, builder: *std.Build, artifact: *CompileStep) void {
365 for (artifact.link_objects.items) |link_object| {
366 switch (link_object) {
367 .other_step => |other| {
368 if (other.target.isWindows() and other.isDynamicLibrary()) {
369 addPathDirInternal(step, builder, fs.path.dirname(other.getOutputSource().getPath(builder)).?);
370 addPathForDynLibsInternal(step, builder, other);
371 }
372 },
373 else => {},
374 }
375 }
376}
lib/std/Build/Step.zig created+97
...@@ -0,0 +1,97 @@
1id: Id,
2name: []const u8,
3makeFn: *const fn (self: *Step) anyerror!void,
4dependencies: std.ArrayList(*Step),
5loop_flag: bool,
6done_flag: bool,
7
8pub const Id = enum {
9 top_level,
10 compile,
11 install_artifact,
12 install_file,
13 install_dir,
14 log,
15 remove_dir,
16 fmt,
17 translate_c,
18 write_file,
19 run,
20 emulatable_run,
21 check_file,
22 check_object,
23 config_header,
24 install_raw,
25 options,
26 custom,
27
28 pub fn Type(comptime id: Id) type {
29 return switch (id) {
30 .top_level => Build.TopLevelStep,
31 .compile => Build.CompileStep,
32 .install_artifact => Build.InstallArtifactStep,
33 .install_file => Build.InstallFileStep,
34 .install_dir => Build.InstallDirStep,
35 .log => Build.LogStep,
36 .remove_dir => Build.RemoveDirStep,
37 .fmt => Build.FmtStep,
38 .translate_c => Build.TranslateCStep,
39 .write_file => Build.WriteFileStep,
40 .run => Build.RunStep,
41 .emulatable_run => Build.EmulatableRunStep,
42 .check_file => Build.CheckFileStep,
43 .check_object => Build.CheckObjectStep,
44 .config_header => Build.ConfigHeaderStep,
45 .install_raw => Build.InstallRawStep,
46 .options => Build.OptionsStep,
47 .custom => @compileError("no type available for custom step"),
48 };
49 }
50};
51
52pub fn init(
53 id: Id,
54 name: []const u8,
55 allocator: Allocator,
56 makeFn: *const fn (self: *Step) anyerror!void,
57) Step {
58 return Step{
59 .id = id,
60 .name = allocator.dupe(u8, name) catch @panic("OOM"),
61 .makeFn = makeFn,
62 .dependencies = std.ArrayList(*Step).init(allocator),
63 .loop_flag = false,
64 .done_flag = false,
65 };
66}
67
68pub fn initNoOp(id: Id, name: []const u8, allocator: Allocator) Step {
69 return init(id, name, allocator, makeNoOp);
70}
71
72pub fn make(self: *Step) !void {
73 if (self.done_flag) return;
74
75 try self.makeFn(self);
76 self.done_flag = true;
77}
78
79pub fn dependOn(self: *Step, other: *Step) void {
80 self.dependencies.append(other) catch @panic("OOM");
81}
82
83fn makeNoOp(self: *Step) anyerror!void {
84 _ = self;
85}
86
87pub fn cast(step: *Step, comptime T: type) ?*T {
88 if (step.id == T.base_id) {
89 return @fieldParentPtr(T, "step", step);
90 }
91 return null;
92}
93
94const Step = @This();
95const std = @import("../std.zig");
96const Build = std.Build;
97const Allocator = std.mem.Allocator;
lib/std/Build/TranslateCStep.zig created+129
...@@ -0,0 +1,129 @@
1const std = @import("../std.zig");
2const Step = std.Build.Step;
3const CompileStep = std.Build.CompileStep;
4const CheckFileStep = std.Build.CheckFileStep;
5const fs = std.fs;
6const mem = std.mem;
7const CrossTarget = std.zig.CrossTarget;
8
9const TranslateCStep = @This();
10
11pub const base_id = .translate_c;
12
13step: Step,
14builder: *std.Build,
15source: std.Build.FileSource,
16include_dirs: std.ArrayList([]const u8),
17c_macros: std.ArrayList([]const u8),
18out_basename: []const u8,
19target: CrossTarget,
20optimize: std.builtin.OptimizeMode,
21output_file: std.Build.GeneratedFile,
22
23pub const Options = struct {
24 source_file: std.Build.FileSource,
25 target: CrossTarget,
26 optimize: std.builtin.OptimizeMode,
27};
28
29pub fn create(builder: *std.Build, options: Options) *TranslateCStep {
30 const self = builder.allocator.create(TranslateCStep) catch @panic("OOM");
31 const source = options.source_file.dupe(builder);
32 self.* = TranslateCStep{
33 .step = Step.init(.translate_c, "translate-c", builder.allocator, make),
34 .builder = builder,
35 .source = source,
36 .include_dirs = std.ArrayList([]const u8).init(builder.allocator),
37 .c_macros = std.ArrayList([]const u8).init(builder.allocator),
38 .out_basename = undefined,
39 .target = options.target,
40 .optimize = options.optimize,
41 .output_file = std.Build.GeneratedFile{ .step = &self.step },
42 };
43 source.addStepDependencies(&self.step);
44 return self;
45}
46
47pub const AddExecutableOptions = struct {
48 name: ?[]const u8 = null,
49 version: ?std.builtin.Version = null,
50 target: ?CrossTarget = null,
51 optimize: ?std.builtin.Mode = null,
52 linkage: ?CompileStep.Linkage = null,
53};
54
55/// Creates a step to build an executable from the translated source.
56pub fn addExecutable(self: *TranslateCStep, options: AddExecutableOptions) *CompileStep {
57 return self.builder.addExecutable(.{
58 .root_source_file = .{ .generated = &self.output_file },
59 .name = options.name orelse "translated_c",
60 .version = options.version,
61 .target = options.target orelse self.target,
62 .optimize = options.optimize orelse self.optimize,
63 .linkage = options.linkage,
64 });
65}
66
67pub fn addIncludeDir(self: *TranslateCStep, include_dir: []const u8) void {
68 self.include_dirs.append(self.builder.dupePath(include_dir)) catch @panic("OOM");
69}
70
71pub fn addCheckFile(self: *TranslateCStep, expected_matches: []const []const u8) *CheckFileStep {
72 return CheckFileStep.create(self.builder, .{ .generated = &self.output_file }, self.builder.dupeStrings(expected_matches));
73}
74
75/// If the value is omitted, it is set to 1.
76/// `name` and `value` need not live longer than the function call.
77pub fn defineCMacro(self: *TranslateCStep, name: []const u8, value: ?[]const u8) void {
78 const macro = std.Build.constructCMacro(self.builder.allocator, name, value);
79 self.c_macros.append(macro) catch @panic("OOM");
80}
81
82/// name_and_value looks like [name]=[value]. If the value is omitted, it is set to 1.
83pub fn defineCMacroRaw(self: *TranslateCStep, name_and_value: []const u8) void {
84 self.c_macros.append(self.builder.dupe(name_and_value)) catch @panic("OOM");
85}
86
87fn make(step: *Step) !void {
88 const self = @fieldParentPtr(TranslateCStep, "step", step);
89
90 var argv_list = std.ArrayList([]const u8).init(self.builder.allocator);
91 try argv_list.append(self.builder.zig_exe);
92 try argv_list.append("translate-c");
93 try argv_list.append("-lc");
94
95 try argv_list.append("--enable-cache");
96
97 if (!self.target.isNative()) {
98 try argv_list.append("-target");
99 try argv_list.append(try self.target.zigTriple(self.builder.allocator));
100 }
101
102 switch (self.optimize) {
103 .Debug => {}, // Skip since it's the default.
104 else => try argv_list.append(self.builder.fmt("-O{s}", .{@tagName(self.optimize)})),
105 }
106
107 for (self.include_dirs.items) |include_dir| {
108 try argv_list.append("-I");
109 try argv_list.append(include_dir);
110 }
111
112 for (self.c_macros.items) |c_macro| {
113 try argv_list.append("-D");
114 try argv_list.append(c_macro);
115 }
116
117 try argv_list.append(self.source.getPath(self.builder));
118
119 const output_path_nl = try self.builder.execFromStep(argv_list.items, &self.step);
120 const output_path = mem.trimRight(u8, output_path_nl, "\r\n");
121
122 self.out_basename = fs.path.basename(output_path);
123 const output_dir = fs.path.dirname(output_path).?;
124
125 self.output_file.path = try fs.path.join(
126 self.builder.allocator,
127 &[_][]const u8{ output_dir, self.out_basename },
128 );
129}
lib/std/Build/WriteFileStep.zig created+113
...@@ -0,0 +1,113 @@
1const std = @import("../std.zig");
2const Step = std.Build.Step;
3const fs = std.fs;
4const ArrayList = std.ArrayList;
5
6const WriteFileStep = @This();
7
8pub const base_id = .write_file;
9
10step: Step,
11builder: *std.Build,
12files: std.TailQueue(File),
13
14pub const File = struct {
15 source: std.Build.GeneratedFile,
16 basename: []const u8,
17 bytes: []const u8,
18};
19
20pub fn init(builder: *std.Build) WriteFileStep {
21 return WriteFileStep{
22 .builder = builder,
23 .step = Step.init(.write_file, "writefile", builder.allocator, make),
24 .files = .{},
25 };
26}
27
28pub fn add(self: *WriteFileStep, basename: []const u8, bytes: []const u8) void {
29 const node = self.builder.allocator.create(std.TailQueue(File).Node) catch @panic("unhandled error");
30 node.* = .{
31 .data = .{
32 .source = std.Build.GeneratedFile{ .step = &self.step },
33 .basename = self.builder.dupePath(basename),
34 .bytes = self.builder.dupe(bytes),
35 },
36 };
37
38 self.files.append(node);
39}
40
41/// Gets a file source for the given basename. If the file does not exist, returns `null`.
42pub fn getFileSource(step: *WriteFileStep, basename: []const u8) ?std.Build.FileSource {
43 var it = step.files.first;
44 while (it) |node| : (it = node.next) {
45 if (std.mem.eql(u8, node.data.basename, basename))
46 return std.Build.FileSource{ .generated = &node.data.source };
47 }
48 return null;
49}
50
51fn make(step: *Step) !void {
52 const self = @fieldParentPtr(WriteFileStep, "step", step);
53
54 // The cache is used here not really as a way to speed things up - because writing
55 // the data to a file would probably be very fast - but as a way to find a canonical
56 // location to put build artifacts.
57
58 // If, for example, a hard-coded path was used as the location to put WriteFileStep
59 // files, then two WriteFileSteps executing in parallel might clobber each other.
60
61 // TODO port the cache system from the compiler to zig std lib. Until then
62 // we directly construct the path, and no "cache hit" detection happens;
63 // the files are always written.
64 // Note there is similar code over in ConfigHeaderStep.
65 const Hasher = std.crypto.auth.siphash.SipHash128(1, 3);
66 // Random bytes to make WriteFileStep unique. Refresh this with
67 // new random bytes when WriteFileStep implementation is modified
68 // in a non-backwards-compatible way.
69 var hash = Hasher.init("eagVR1dYXoE7ARDP");
70
71 {
72 var it = self.files.first;
73 while (it) |node| : (it = node.next) {
74 hash.update(node.data.basename);
75 hash.update(node.data.bytes);
76 hash.update("|");
77 }
78 }
79 var digest: [16]u8 = undefined;
80 hash.final(&digest);
81 var hash_basename: [digest.len * 2]u8 = undefined;
82 _ = std.fmt.bufPrint(
83 &hash_basename,
84 "{s}",
85 .{std.fmt.fmtSliceHexLower(&digest)},
86 ) catch unreachable;
87
88 const output_dir = try fs.path.join(self.builder.allocator, &[_][]const u8{
89 self.builder.cache_root, "o", &hash_basename,
90 });
91 var dir = fs.cwd().makeOpenPath(output_dir, .{}) catch |err| {
92 std.debug.print("unable to make path {s}: {s}\n", .{ output_dir, @errorName(err) });
93 return err;
94 };
95 defer dir.close();
96 {
97 var it = self.files.first;
98 while (it) |node| : (it = node.next) {
99 dir.writeFile(node.data.basename, node.data.bytes) catch |err| {
100 std.debug.print("unable to write {s} into {s}: {s}\n", .{
101 node.data.basename,
102 output_dir,
103 @errorName(err),
104 });
105 return err;
106 };
107 node.data.source.path = try fs.path.join(
108 self.builder.allocator,
109 &[_][]const u8{ output_dir, node.data.basename },
110 );
111 }
112 }
113}
lib/std/array_hash_map.zig+2-1
...@@ -1145,7 +1145,8 @@ pub fn ArrayHashMapUnmanaged(...@@ -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/array_list.zig+28-4
...@@ -482,14 +482,14 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {...@@ -482,14 +482,14 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
482482
483 /// Return the last element from the list.483 /// Return the last element from the list.
484 /// Asserts the list has at least one item.484 /// Asserts the list has at least one item.
485 pub fn getLast(self: *Self) T {485 pub fn getLast(self: Self) T {
486 const val = self.items[self.items.len - 1];486 const val = self.items[self.items.len - 1];
487 return val;487 return val;
488 }488 }
489489
490 /// Return the last element from the list, or490 /// Return the last element from the list, or
491 /// return `null` if list is empty.491 /// return `null` if list is empty.
492 pub fn getLastOrNull(self: *Self) ?T {492 pub fn getLastOrNull(self: Self) ?T {
493 if (self.items.len == 0) return null;493 if (self.items.len == 0) return null;
494 return self.getLast();494 return self.getLast();
495 }495 }
...@@ -961,14 +961,14 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ...@@ -961,14 +961,14 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
961961
962 /// Return the last element from the list.962 /// Return the last element from the list.
963 /// Asserts the list has at least one item.963 /// Asserts the list has at least one item.
964 pub fn getLast(self: *Self) T {964 pub fn getLast(self: Self) T {
965 const val = self.items[self.items.len - 1];965 const val = self.items[self.items.len - 1];
966 return val;966 return val;
967 }967 }
968968
969 /// Return the last element from the list, or969 /// Return the last element from the list, or
970 /// return `null` if list is empty.970 /// return `null` if list is empty.
971 pub fn getLastOrNull(self: *Self) ?T {971 pub fn getLastOrNull(self: Self) ?T {
972 if (self.items.len == 0) return null;972 if (self.items.len == 0) return null;
973 return self.getLast();973 return self.getLast();
974 }974 }
...@@ -1719,3 +1719,27 @@ test "std.ArrayList(?u32).popOrNull()" {...@@ -1719,3 +1719,27 @@ test "std.ArrayList(?u32).popOrNull()" {
1719 try testing.expect(list.popOrNull().? == null);1719 try testing.expect(list.popOrNull().? == null);
1720 try testing.expect(list.popOrNull() == null);1720 try testing.expect(list.popOrNull() == null);
1721}1721}
1722
1723test "std.ArrayList(u32).getLast()" {
1724 const a = testing.allocator;
1725
1726 var list = ArrayList(u32).init(a);
1727 defer list.deinit();
1728
1729 try list.append(2);
1730 const const_list = list;
1731 try testing.expectEqual(const_list.getLast(), 2);
1732}
1733
1734test "std.ArrayList(u32).getLastOrNull()" {
1735 const a = testing.allocator;
1736
1737 var list = ArrayList(u32).init(a);
1738 defer list.deinit();
1739
1740 try testing.expectEqual(list.getLastOrNull(), null);
1741
1742 try list.append(2);
1743 const const_list = list;
1744 try testing.expectEqual(const_list.getLastOrNull().?, 2);
1745}
lib/std/build.zig deleted-1781
...@@ -1,1781 +0,0 @@
1const std = @import("std.zig");
2const builtin = @import("builtin");
3const io = std.io;
4const fs = std.fs;
5const mem = std.mem;
6const debug = std.debug;
7const panic = std.debug.panic;
8const assert = debug.assert;
9const log = std.log;
10const ArrayList = std.ArrayList;
11const StringHashMap = std.StringHashMap;
12const Allocator = mem.Allocator;
13const process = std.process;
14const EnvMap = std.process.EnvMap;
15const fmt_lib = std.fmt;
16const File = std.fs.File;
17const CrossTarget = std.zig.CrossTarget;
18const NativeTargetInfo = std.zig.system.NativeTargetInfo;
19const Sha256 = std.crypto.hash.sha2.Sha256;
20const ThisModule = @This();
21
22pub const CheckFileStep = @import("build/CheckFileStep.zig");
23pub const CheckObjectStep = @import("build/CheckObjectStep.zig");
24pub const ConfigHeaderStep = @import("build/ConfigHeaderStep.zig");
25pub const EmulatableRunStep = @import("build/EmulatableRunStep.zig");
26pub const FmtStep = @import("build/FmtStep.zig");
27pub const InstallArtifactStep = @import("build/InstallArtifactStep.zig");
28pub const InstallDirStep = @import("build/InstallDirStep.zig");
29pub const InstallFileStep = @import("build/InstallFileStep.zig");
30pub const InstallRawStep = @import("build/InstallRawStep.zig");
31pub const LibExeObjStep = @import("build/LibExeObjStep.zig");
32pub const LogStep = @import("build/LogStep.zig");
33pub const OptionsStep = @import("build/OptionsStep.zig");
34pub const RemoveDirStep = @import("build/RemoveDirStep.zig");
35pub const RunStep = @import("build/RunStep.zig");
36pub const TranslateCStep = @import("build/TranslateCStep.zig");
37pub const WriteFileStep = @import("build/WriteFileStep.zig");
38
39pub const Builder = struct {
40 install_tls: TopLevelStep,
41 uninstall_tls: TopLevelStep,
42 allocator: Allocator,
43 user_input_options: UserInputOptionsMap,
44 available_options_map: AvailableOptionsMap,
45 available_options_list: ArrayList(AvailableOption),
46 verbose: bool,
47 verbose_link: bool,
48 verbose_cc: bool,
49 verbose_air: bool,
50 verbose_llvm_ir: bool,
51 verbose_cimport: bool,
52 verbose_llvm_cpu_features: bool,
53 /// The purpose of executing the command is for a human to read compile errors from the terminal
54 prominent_compile_errors: bool,
55 color: enum { auto, on, off } = .auto,
56 reference_trace: ?u32 = null,
57 invalid_user_input: bool,
58 zig_exe: []const u8,
59 default_step: *Step,
60 env_map: *EnvMap,
61 top_level_steps: ArrayList(*TopLevelStep),
62 install_prefix: []const u8,
63 dest_dir: ?[]const u8,
64 lib_dir: []const u8,
65 exe_dir: []const u8,
66 h_dir: []const u8,
67 install_path: []const u8,
68 sysroot: ?[]const u8 = null,
69 search_prefixes: ArrayList([]const u8),
70 libc_file: ?[]const u8 = null,
71 installed_files: ArrayList(InstalledFile),
72 /// Path to the directory containing build.zig.
73 build_root: []const u8,
74 cache_root: []const u8,
75 global_cache_root: []const u8,
76 release_mode: ?std.builtin.Mode,
77 is_release: bool,
78 /// zig lib dir
79 override_lib_dir: ?[]const u8,
80 vcpkg_root: VcpkgRoot = .unattempted,
81 pkg_config_pkg_list: ?(PkgConfigError![]const PkgConfigPkg) = null,
82 args: ?[][]const u8 = null,
83 debug_log_scopes: []const []const u8 = &.{},
84 debug_compile_errors: bool = false,
85
86 /// Experimental. Use system Darling installation to run cross compiled macOS build artifacts.
87 enable_darling: bool = false,
88 /// Use system QEMU installation to run cross compiled foreign architecture build artifacts.
89 enable_qemu: bool = false,
90 /// Darwin. Use Rosetta to run x86_64 macOS build artifacts on arm64 macOS.
91 enable_rosetta: bool = false,
92 /// Use system Wasmtime installation to run cross compiled wasm/wasi build artifacts.
93 enable_wasmtime: bool = false,
94 /// Use system Wine installation to run cross compiled Windows build artifacts.
95 enable_wine: bool = false,
96 /// After following the steps in https://github.com/ziglang/zig/wiki/Updating-libc#glibc,
97 /// this will be the directory $glibc-build-dir/install/glibcs
98 /// Given the example of the aarch64 target, this is the directory
99 /// that contains the path `aarch64-linux-gnu/lib/ld-linux-aarch64.so.1`.
100 glibc_runtimes_dir: ?[]const u8 = null,
101
102 /// Information about the native target. Computed before build() is invoked.
103 host: NativeTargetInfo,
104
105 dep_prefix: []const u8 = "",
106
107 pub const ExecError = error{
108 ReadFailure,
109 ExitCodeFailure,
110 ProcessTerminated,
111 ExecNotSupported,
112 } || std.ChildProcess.SpawnError;
113
114 pub const PkgConfigError = error{
115 PkgConfigCrashed,
116 PkgConfigFailed,
117 PkgConfigNotInstalled,
118 PkgConfigInvalidOutput,
119 };
120
121 pub const PkgConfigPkg = struct {
122 name: []const u8,
123 desc: []const u8,
124 };
125
126 pub const CStd = enum {
127 C89,
128 C99,
129 C11,
130 };
131
132 const UserInputOptionsMap = StringHashMap(UserInputOption);
133 const AvailableOptionsMap = StringHashMap(AvailableOption);
134
135 const AvailableOption = struct {
136 name: []const u8,
137 type_id: TypeId,
138 description: []const u8,
139 /// If the `type_id` is `enum` this provides the list of enum options
140 enum_options: ?[]const []const u8,
141 };
142
143 const UserInputOption = struct {
144 name: []const u8,
145 value: UserValue,
146 used: bool,
147 };
148
149 const UserValue = union(enum) {
150 flag: void,
151 scalar: []const u8,
152 list: ArrayList([]const u8),
153 };
154
155 const TypeId = enum {
156 bool,
157 int,
158 float,
159 @"enum",
160 string,
161 list,
162 };
163
164 const TopLevelStep = struct {
165 pub const base_id = .top_level;
166
167 step: Step,
168 description: []const u8,
169 };
170
171 pub const DirList = struct {
172 lib_dir: ?[]const u8 = null,
173 exe_dir: ?[]const u8 = null,
174 include_dir: ?[]const u8 = null,
175 };
176
177 pub fn create(
178 allocator: Allocator,
179 zig_exe: []const u8,
180 build_root: []const u8,
181 cache_root: []const u8,
182 global_cache_root: []const u8,
183 ) !*Builder {
184 const env_map = try allocator.create(EnvMap);
185 env_map.* = try process.getEnvMap(allocator);
186
187 const host = try NativeTargetInfo.detect(.{});
188
189 const self = try allocator.create(Builder);
190 self.* = Builder{
191 .zig_exe = zig_exe,
192 .build_root = build_root,
193 .cache_root = try fs.path.relative(allocator, build_root, cache_root),
194 .global_cache_root = global_cache_root,
195 .verbose = false,
196 .verbose_link = false,
197 .verbose_cc = false,
198 .verbose_air = false,
199 .verbose_llvm_ir = false,
200 .verbose_cimport = false,
201 .verbose_llvm_cpu_features = false,
202 .prominent_compile_errors = false,
203 .invalid_user_input = false,
204 .allocator = allocator,
205 .user_input_options = UserInputOptionsMap.init(allocator),
206 .available_options_map = AvailableOptionsMap.init(allocator),
207 .available_options_list = ArrayList(AvailableOption).init(allocator),
208 .top_level_steps = ArrayList(*TopLevelStep).init(allocator),
209 .default_step = undefined,
210 .env_map = env_map,
211 .search_prefixes = ArrayList([]const u8).init(allocator),
212 .install_prefix = undefined,
213 .lib_dir = undefined,
214 .exe_dir = undefined,
215 .h_dir = undefined,
216 .dest_dir = env_map.get("DESTDIR"),
217 .installed_files = ArrayList(InstalledFile).init(allocator),
218 .install_tls = TopLevelStep{
219 .step = Step.initNoOp(.top_level, "install", allocator),
220 .description = "Copy build artifacts to prefix path",
221 },
222 .uninstall_tls = TopLevelStep{
223 .step = Step.init(.top_level, "uninstall", allocator, makeUninstall),
224 .description = "Remove build artifacts from prefix path",
225 },
226 .release_mode = null,
227 .is_release = false,
228 .override_lib_dir = null,
229 .install_path = undefined,
230 .args = null,
231 .host = host,
232 };
233 try self.top_level_steps.append(&self.install_tls);
234 try self.top_level_steps.append(&self.uninstall_tls);
235 self.default_step = &self.install_tls.step;
236 return self;
237 }
238
239 fn createChild(
240 parent: *Builder,
241 dep_name: []const u8,
242 build_root: []const u8,
243 args: anytype,
244 ) !*Builder {
245 const child = try createChildOnly(parent, dep_name, build_root);
246 try applyArgs(child, args);
247 return child;
248 }
249
250 fn createChildOnly(parent: *Builder, dep_name: []const u8, build_root: []const u8) !*Builder {
251 const allocator = parent.allocator;
252 const child = try allocator.create(Builder);
253 child.* = .{
254 .allocator = allocator,
255 .install_tls = .{
256 .step = Step.initNoOp(.top_level, "install", allocator),
257 .description = "Copy build artifacts to prefix path",
258 },
259 .uninstall_tls = .{
260 .step = Step.init(.top_level, "uninstall", allocator, makeUninstall),
261 .description = "Remove build artifacts from prefix path",
262 },
263 .user_input_options = UserInputOptionsMap.init(allocator),
264 .available_options_map = AvailableOptionsMap.init(allocator),
265 .available_options_list = ArrayList(AvailableOption).init(allocator),
266 .verbose = parent.verbose,
267 .verbose_link = parent.verbose_link,
268 .verbose_cc = parent.verbose_cc,
269 .verbose_air = parent.verbose_air,
270 .verbose_llvm_ir = parent.verbose_llvm_ir,
271 .verbose_cimport = parent.verbose_cimport,
272 .verbose_llvm_cpu_features = parent.verbose_llvm_cpu_features,
273 .prominent_compile_errors = parent.prominent_compile_errors,
274 .color = parent.color,
275 .reference_trace = parent.reference_trace,
276 .invalid_user_input = false,
277 .zig_exe = parent.zig_exe,
278 .default_step = undefined,
279 .env_map = parent.env_map,
280 .top_level_steps = ArrayList(*TopLevelStep).init(allocator),
281 .install_prefix = undefined,
282 .dest_dir = parent.dest_dir,
283 .lib_dir = parent.lib_dir,
284 .exe_dir = parent.exe_dir,
285 .h_dir = parent.h_dir,
286 .install_path = parent.install_path,
287 .sysroot = parent.sysroot,
288 .search_prefixes = ArrayList([]const u8).init(allocator),
289 .libc_file = parent.libc_file,
290 .installed_files = ArrayList(InstalledFile).init(allocator),
291 .build_root = build_root,
292 .cache_root = parent.cache_root,
293 .global_cache_root = parent.global_cache_root,
294 .release_mode = parent.release_mode,
295 .is_release = parent.is_release,
296 .override_lib_dir = parent.override_lib_dir,
297 .debug_log_scopes = parent.debug_log_scopes,
298 .debug_compile_errors = parent.debug_compile_errors,
299 .enable_darling = parent.enable_darling,
300 .enable_qemu = parent.enable_qemu,
301 .enable_rosetta = parent.enable_rosetta,
302 .enable_wasmtime = parent.enable_wasmtime,
303 .enable_wine = parent.enable_wine,
304 .glibc_runtimes_dir = parent.glibc_runtimes_dir,
305 .host = parent.host,
306 .dep_prefix = parent.fmt("{s}{s}.", .{ parent.dep_prefix, dep_name }),
307 };
308 try child.top_level_steps.append(&child.install_tls);
309 try child.top_level_steps.append(&child.uninstall_tls);
310 child.default_step = &child.install_tls.step;
311 return child;
312 }
313
314 fn applyArgs(b: *Builder, args: anytype) !void {
315 // TODO this function is the way that a build.zig file communicates
316 // options to its dependencies. It is the programmatic way to give
317 // command line arguments to a build.zig script.
318 _ = args;
319 const Hasher = std.crypto.auth.siphash.SipHash128(1, 3);
320 // Random bytes to make unique. Refresh this with new random bytes when
321 // implementation is modified in a non-backwards-compatible way.
322 var hash = Hasher.init("ZaEsvQ5ClaA2IdH9");
323 hash.update(b.dep_prefix);
324 // TODO additionally update the hash with `args`.
325
326 var digest: [16]u8 = undefined;
327 hash.final(&digest);
328 var hash_basename: [digest.len * 2]u8 = undefined;
329 _ = std.fmt.bufPrint(&hash_basename, "{s}", .{std.fmt.fmtSliceHexLower(&digest)}) catch
330 unreachable;
331
332 const install_prefix = b.pathJoin(&.{ b.cache_root, "i", &hash_basename });
333 b.resolveInstallPrefix(install_prefix, .{});
334 }
335
336 pub fn destroy(self: *Builder) void {
337 self.env_map.deinit();
338 self.top_level_steps.deinit();
339 self.allocator.destroy(self);
340 }
341
342 /// This function is intended to be called by lib/build_runner.zig, not a build.zig file.
343 pub fn resolveInstallPrefix(self: *Builder, install_prefix: ?[]const u8, dir_list: DirList) void {
344 if (self.dest_dir) |dest_dir| {
345 self.install_prefix = install_prefix orelse "/usr";
346 self.install_path = self.pathJoin(&.{ dest_dir, self.install_prefix });
347 } else {
348 self.install_prefix = install_prefix orelse
349 (self.pathJoin(&.{ self.build_root, "zig-out" }));
350 self.install_path = self.install_prefix;
351 }
352
353 var lib_list = [_][]const u8{ self.install_path, "lib" };
354 var exe_list = [_][]const u8{ self.install_path, "bin" };
355 var h_list = [_][]const u8{ self.install_path, "include" };
356
357 if (dir_list.lib_dir) |dir| {
358 if (std.fs.path.isAbsolute(dir)) lib_list[0] = self.dest_dir orelse "";
359 lib_list[1] = dir;
360 }
361
362 if (dir_list.exe_dir) |dir| {
363 if (std.fs.path.isAbsolute(dir)) exe_list[0] = self.dest_dir orelse "";
364 exe_list[1] = dir;
365 }
366
367 if (dir_list.include_dir) |dir| {
368 if (std.fs.path.isAbsolute(dir)) h_list[0] = self.dest_dir orelse "";
369 h_list[1] = dir;
370 }
371
372 self.lib_dir = self.pathJoin(&lib_list);
373 self.exe_dir = self.pathJoin(&exe_list);
374 self.h_dir = self.pathJoin(&h_list);
375 }
376
377 fn convertOptionalPathToFileSource(path: ?[]const u8) ?FileSource {
378 return if (path) |p|
379 FileSource{ .path = p }
380 else
381 null;
382 }
383
384 pub fn addExecutable(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep {
385 return addExecutableSource(self, name, convertOptionalPathToFileSource(root_src));
386 }
387
388 pub fn addExecutableSource(builder: *Builder, name: []const u8, root_src: ?FileSource) *LibExeObjStep {
389 return LibExeObjStep.createExecutable(builder, name, root_src);
390 }
391
392 pub fn addOptions(self: *Builder) *OptionsStep {
393 return OptionsStep.create(self);
394 }
395
396 pub fn addObject(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep {
397 return addObjectSource(self, name, convertOptionalPathToFileSource(root_src));
398 }
399
400 pub fn addObjectSource(builder: *Builder, name: []const u8, root_src: ?FileSource) *LibExeObjStep {
401 return LibExeObjStep.createObject(builder, name, root_src);
402 }
403
404 pub fn addSharedLibrary(
405 self: *Builder,
406 name: []const u8,
407 root_src: ?[]const u8,
408 kind: LibExeObjStep.SharedLibKind,
409 ) *LibExeObjStep {
410 return addSharedLibrarySource(self, name, convertOptionalPathToFileSource(root_src), kind);
411 }
412
413 pub fn addSharedLibrarySource(
414 self: *Builder,
415 name: []const u8,
416 root_src: ?FileSource,
417 kind: LibExeObjStep.SharedLibKind,
418 ) *LibExeObjStep {
419 return LibExeObjStep.createSharedLibrary(self, name, root_src, kind);
420 }
421
422 pub fn addStaticLibrary(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep {
423 return addStaticLibrarySource(self, name, convertOptionalPathToFileSource(root_src));
424 }
425
426 pub fn addStaticLibrarySource(self: *Builder, name: []const u8, root_src: ?FileSource) *LibExeObjStep {
427 return LibExeObjStep.createStaticLibrary(self, name, root_src);
428 }
429
430 pub fn addTest(self: *Builder, root_src: []const u8) *LibExeObjStep {
431 return LibExeObjStep.createTest(self, "test", .{ .path = root_src });
432 }
433
434 pub fn addTestSource(self: *Builder, root_src: FileSource) *LibExeObjStep {
435 return LibExeObjStep.createTest(self, "test", root_src.dupe(self));
436 }
437
438 pub fn addTestExe(self: *Builder, name: []const u8, root_src: []const u8) *LibExeObjStep {
439 return LibExeObjStep.createTestExe(self, name, .{ .path = root_src });
440 }
441
442 pub fn addTestExeSource(self: *Builder, name: []const u8, root_src: FileSource) *LibExeObjStep {
443 return LibExeObjStep.createTestExe(self, name, root_src.dupe(self));
444 }
445
446 pub fn addAssemble(self: *Builder, name: []const u8, src: []const u8) *LibExeObjStep {
447 return addAssembleSource(self, name, .{ .path = src });
448 }
449
450 pub fn addAssembleSource(self: *Builder, name: []const u8, src: FileSource) *LibExeObjStep {
451 const obj_step = LibExeObjStep.createObject(self, name, null);
452 obj_step.addAssemblyFileSource(src.dupe(self));
453 return obj_step;
454 }
455
456 /// Initializes a RunStep with argv, which must at least have the path to the
457 /// executable. More command line arguments can be added with `addArg`,
458 /// `addArgs`, and `addArtifactArg`.
459 /// Be careful using this function, as it introduces a system dependency.
460 /// To run an executable built with zig build, see `LibExeObjStep.run`.
461 pub fn addSystemCommand(self: *Builder, argv: []const []const u8) *RunStep {
462 assert(argv.len >= 1);
463 const run_step = RunStep.create(self, self.fmt("run {s}", .{argv[0]}));
464 run_step.addArgs(argv);
465 return run_step;
466 }
467
468 pub fn addConfigHeader(
469 b: *Builder,
470 source: FileSource,
471 style: ConfigHeaderStep.Style,
472 values: anytype,
473 ) *ConfigHeaderStep {
474 const config_header_step = ConfigHeaderStep.create(b, source, style);
475 config_header_step.addValues(values);
476 return config_header_step;
477 }
478
479 /// Allocator.dupe without the need to handle out of memory.
480 pub fn dupe(self: *Builder, bytes: []const u8) []u8 {
481 return self.allocator.dupe(u8, bytes) catch unreachable;
482 }
483
484 /// Duplicates an array of strings without the need to handle out of memory.
485 pub fn dupeStrings(self: *Builder, strings: []const []const u8) [][]u8 {
486 const array = self.allocator.alloc([]u8, strings.len) catch unreachable;
487 for (strings) |s, i| {
488 array[i] = self.dupe(s);
489 }
490 return array;
491 }
492
493 /// Duplicates a path and converts all slashes to the OS's canonical path separator.
494 pub fn dupePath(self: *Builder, bytes: []const u8) []u8 {
495 const the_copy = self.dupe(bytes);
496 for (the_copy) |*byte| {
497 switch (byte.*) {
498 '/', '\\' => byte.* = fs.path.sep,
499 else => {},
500 }
501 }
502 return the_copy;
503 }
504
505 /// Duplicates a package recursively.
506 pub fn dupePkg(self: *Builder, package: Pkg) Pkg {
507 var the_copy = Pkg{
508 .name = self.dupe(package.name),
509 .source = package.source.dupe(self),
510 };
511
512 if (package.dependencies) |dependencies| {
513 const new_dependencies = self.allocator.alloc(Pkg, dependencies.len) catch unreachable;
514 the_copy.dependencies = new_dependencies;
515
516 for (dependencies) |dep_package, i| {
517 new_dependencies[i] = self.dupePkg(dep_package);
518 }
519 }
520 return the_copy;
521 }
522
523 pub fn addWriteFile(self: *Builder, file_path: []const u8, data: []const u8) *WriteFileStep {
524 const write_file_step = self.addWriteFiles();
525 write_file_step.add(file_path, data);
526 return write_file_step;
527 }
528
529 pub fn addWriteFiles(self: *Builder) *WriteFileStep {
530 const write_file_step = self.allocator.create(WriteFileStep) catch unreachable;
531 write_file_step.* = WriteFileStep.init(self);
532 return write_file_step;
533 }
534
535 pub fn addLog(self: *Builder, comptime format: []const u8, args: anytype) *LogStep {
536 const data = self.fmt(format, args);
537 const log_step = self.allocator.create(LogStep) catch unreachable;
538 log_step.* = LogStep.init(self, data);
539 return log_step;
540 }
541
542 pub fn addRemoveDirTree(self: *Builder, dir_path: []const u8) *RemoveDirStep {
543 const remove_dir_step = self.allocator.create(RemoveDirStep) catch unreachable;
544 remove_dir_step.* = RemoveDirStep.init(self, dir_path);
545 return remove_dir_step;
546 }
547
548 pub fn addFmt(self: *Builder, paths: []const []const u8) *FmtStep {
549 return FmtStep.create(self, paths);
550 }
551
552 pub fn addTranslateC(self: *Builder, source: FileSource) *TranslateCStep {
553 return TranslateCStep.create(self, source.dupe(self));
554 }
555
556 pub fn version(self: *const Builder, major: u32, minor: u32, patch: u32) LibExeObjStep.SharedLibKind {
557 _ = self;
558 return .{
559 .versioned = .{
560 .major = major,
561 .minor = minor,
562 .patch = patch,
563 },
564 };
565 }
566
567 pub fn make(self: *Builder, step_names: []const []const u8) !void {
568 try self.makePath(self.cache_root);
569
570 var wanted_steps = ArrayList(*Step).init(self.allocator);
571 defer wanted_steps.deinit();
572
573 if (step_names.len == 0) {
574 try wanted_steps.append(self.default_step);
575 } else {
576 for (step_names) |step_name| {
577 const s = try self.getTopLevelStepByName(step_name);
578 try wanted_steps.append(s);
579 }
580 }
581
582 for (wanted_steps.items) |s| {
583 try self.makeOneStep(s);
584 }
585 }
586
587 pub fn getInstallStep(self: *Builder) *Step {
588 return &self.install_tls.step;
589 }
590
591 pub fn getUninstallStep(self: *Builder) *Step {
592 return &self.uninstall_tls.step;
593 }
594
595 fn makeUninstall(uninstall_step: *Step) anyerror!void {
596 const uninstall_tls = @fieldParentPtr(TopLevelStep, "step", uninstall_step);
597 const self = @fieldParentPtr(Builder, "uninstall_tls", uninstall_tls);
598
599 for (self.installed_files.items) |installed_file| {
600 const full_path = self.getInstallPath(installed_file.dir, installed_file.path);
601 if (self.verbose) {
602 log.info("rm {s}", .{full_path});
603 }
604 fs.cwd().deleteTree(full_path) catch {};
605 }
606
607 // TODO remove empty directories
608 }
609
610 fn makeOneStep(self: *Builder, s: *Step) anyerror!void {
611 if (s.loop_flag) {
612 log.err("Dependency loop detected:\n {s}", .{s.name});
613 return error.DependencyLoopDetected;
614 }
615 s.loop_flag = true;
616
617 for (s.dependencies.items) |dep| {
618 self.makeOneStep(dep) catch |err| {
619 if (err == error.DependencyLoopDetected) {
620 log.err(" {s}", .{s.name});
621 }
622 return err;
623 };
624 }
625
626 s.loop_flag = false;
627
628 try s.make();
629 }
630
631 fn getTopLevelStepByName(self: *Builder, name: []const u8) !*Step {
632 for (self.top_level_steps.items) |top_level_step| {
633 if (mem.eql(u8, top_level_step.step.name, name)) {
634 return &top_level_step.step;
635 }
636 }
637 log.err("Cannot run step '{s}' because it does not exist", .{name});
638 return error.InvalidStepName;
639 }
640
641 pub fn option(self: *Builder, comptime T: type, name_raw: []const u8, description_raw: []const u8) ?T {
642 const name = self.dupe(name_raw);
643 const description = self.dupe(description_raw);
644 const type_id = comptime typeToEnum(T);
645 const enum_options = if (type_id == .@"enum") blk: {
646 const fields = comptime std.meta.fields(T);
647 var options = ArrayList([]const u8).initCapacity(self.allocator, fields.len) catch unreachable;
648
649 inline for (fields) |field| {
650 options.appendAssumeCapacity(field.name);
651 }
652
653 break :blk options.toOwnedSlice() catch unreachable;
654 } else null;
655 const available_option = AvailableOption{
656 .name = name,
657 .type_id = type_id,
658 .description = description,
659 .enum_options = enum_options,
660 };
661 if ((self.available_options_map.fetchPut(name, available_option) catch unreachable) != null) {
662 panic("Option '{s}' declared twice", .{name});
663 }
664 self.available_options_list.append(available_option) catch unreachable;
665
666 const option_ptr = self.user_input_options.getPtr(name) orelse return null;
667 option_ptr.used = true;
668 switch (type_id) {
669 .bool => switch (option_ptr.value) {
670 .flag => return true,
671 .scalar => |s| {
672 if (mem.eql(u8, s, "true")) {
673 return true;
674 } else if (mem.eql(u8, s, "false")) {
675 return false;
676 } else {
677 log.err("Expected -D{s} to be a boolean, but received '{s}'\n", .{ name, s });
678 self.markInvalidUserInput();
679 return null;
680 }
681 },
682 .list => {
683 log.err("Expected -D{s} to be a boolean, but received a list.\n", .{name});
684 self.markInvalidUserInput();
685 return null;
686 },
687 },
688 .int => switch (option_ptr.value) {
689 .flag => {
690 log.err("Expected -D{s} to be an integer, but received a boolean.\n", .{name});
691 self.markInvalidUserInput();
692 return null;
693 },
694 .scalar => |s| {
695 const n = std.fmt.parseInt(T, s, 10) catch |err| switch (err) {
696 error.Overflow => {
697 log.err("-D{s} value {s} cannot fit into type {s}.\n", .{ name, s, @typeName(T) });
698 self.markInvalidUserInput();
699 return null;
700 },
701 else => {
702 log.err("Expected -D{s} to be an integer of type {s}.\n", .{ name, @typeName(T) });
703 self.markInvalidUserInput();
704 return null;
705 },
706 };
707 return n;
708 },
709 .list => {
710 log.err("Expected -D{s} to be an integer, but received a list.\n", .{name});
711 self.markInvalidUserInput();
712 return null;
713 },
714 },
715 .float => switch (option_ptr.value) {
716 .flag => {
717 log.err("Expected -D{s} to be a float, but received a boolean.\n", .{name});
718 self.markInvalidUserInput();
719 return null;
720 },
721 .scalar => |s| {
722 const n = std.fmt.parseFloat(T, s) catch {
723 log.err("Expected -D{s} to be a float of type {s}.\n", .{ name, @typeName(T) });
724 self.markInvalidUserInput();
725 return null;
726 };
727 return n;
728 },
729 .list => {
730 log.err("Expected -D{s} to be a float, but received a list.\n", .{name});
731 self.markInvalidUserInput();
732 return null;
733 },
734 },
735 .@"enum" => switch (option_ptr.value) {
736 .flag => {
737 log.err("Expected -D{s} to be a string, but received a boolean.\n", .{name});
738 self.markInvalidUserInput();
739 return null;
740 },
741 .scalar => |s| {
742 if (std.meta.stringToEnum(T, s)) |enum_lit| {
743 return enum_lit;
744 } else {
745 log.err("Expected -D{s} to be of type {s}.\n", .{ name, @typeName(T) });
746 self.markInvalidUserInput();
747 return null;
748 }
749 },
750 .list => {
751 log.err("Expected -D{s} to be a string, but received a list.\n", .{name});
752 self.markInvalidUserInput();
753 return null;
754 },
755 },
756 .string => switch (option_ptr.value) {
757 .flag => {
758 log.err("Expected -D{s} to be a string, but received a boolean.\n", .{name});
759 self.markInvalidUserInput();
760 return null;
761 },
762 .list => {
763 log.err("Expected -D{s} to be a string, but received a list.\n", .{name});
764 self.markInvalidUserInput();
765 return null;
766 },
767 .scalar => |s| return s,
768 },
769 .list => switch (option_ptr.value) {
770 .flag => {
771 log.err("Expected -D{s} to be a list, but received a boolean.\n", .{name});
772 self.markInvalidUserInput();
773 return null;
774 },
775 .scalar => |s| {
776 return self.allocator.dupe([]const u8, &[_][]const u8{s}) catch unreachable;
777 },
778 .list => |lst| return lst.items,
779 },
780 }
781 }
782
783 pub fn step(self: *Builder, name: []const u8, description: []const u8) *Step {
784 const step_info = self.allocator.create(TopLevelStep) catch unreachable;
785 step_info.* = TopLevelStep{
786 .step = Step.initNoOp(.top_level, name, self.allocator),
787 .description = self.dupe(description),
788 };
789 self.top_level_steps.append(step_info) catch unreachable;
790 return &step_info.step;
791 }
792
793 /// This provides the -Drelease option to the build user and does not give them the choice.
794 pub fn setPreferredReleaseMode(self: *Builder, mode: std.builtin.Mode) void {
795 if (self.release_mode != null) {
796 @panic("setPreferredReleaseMode must be called before standardReleaseOptions and may not be called twice");
797 }
798 const description = self.fmt("Create a release build ({s})", .{@tagName(mode)});
799 self.is_release = self.option(bool, "release", description) orelse false;
800 self.release_mode = if (self.is_release) mode else std.builtin.Mode.Debug;
801 }
802
803 /// If you call this without first calling `setPreferredReleaseMode` then it gives the build user
804 /// the choice of what kind of release.
805 pub fn standardReleaseOptions(self: *Builder) std.builtin.Mode {
806 if (self.release_mode) |mode| return mode;
807
808 const release_safe = self.option(bool, "release-safe", "Optimizations on and safety on") orelse false;
809 const release_fast = self.option(bool, "release-fast", "Optimizations on and safety off") orelse false;
810 const release_small = self.option(bool, "release-small", "Size optimizations on and safety off") orelse false;
811
812 const mode = if (release_safe and !release_fast and !release_small)
813 std.builtin.Mode.ReleaseSafe
814 else if (release_fast and !release_safe and !release_small)
815 std.builtin.Mode.ReleaseFast
816 else if (release_small and !release_fast and !release_safe)
817 std.builtin.Mode.ReleaseSmall
818 else if (!release_fast and !release_safe and !release_small)
819 std.builtin.Mode.Debug
820 else x: {
821 log.err("Multiple release modes (of -Drelease-safe, -Drelease-fast and -Drelease-small)\n", .{});
822 self.markInvalidUserInput();
823 break :x std.builtin.Mode.Debug;
824 };
825 self.is_release = mode != .Debug;
826 self.release_mode = mode;
827 return mode;
828 }
829
830 pub const StandardTargetOptionsArgs = struct {
831 whitelist: ?[]const CrossTarget = null,
832
833 default_target: CrossTarget = CrossTarget{},
834 };
835
836 /// Exposes standard `zig build` options for choosing a target.
837 pub fn standardTargetOptions(self: *Builder, args: StandardTargetOptionsArgs) CrossTarget {
838 const maybe_triple = self.option(
839 []const u8,
840 "target",
841 "The CPU architecture, OS, and ABI to build for",
842 );
843 const mcpu = self.option([]const u8, "cpu", "Target CPU features to add or subtract");
844
845 if (maybe_triple == null and mcpu == null) {
846 return args.default_target;
847 }
848
849 const triple = maybe_triple orelse "native";
850
851 var diags: CrossTarget.ParseOptions.Diagnostics = .{};
852 const selected_target = CrossTarget.parse(.{
853 .arch_os_abi = triple,
854 .cpu_features = mcpu,
855 .diagnostics = &diags,
856 }) catch |err| switch (err) {
857 error.UnknownCpuModel => {
858 log.err("Unknown CPU: '{s}'\nAvailable CPUs for architecture '{s}':", .{
859 diags.cpu_name.?,
860 @tagName(diags.arch.?),
861 });
862 for (diags.arch.?.allCpuModels()) |cpu| {
863 log.err(" {s}", .{cpu.name});
864 }
865 self.markInvalidUserInput();
866 return args.default_target;
867 },
868 error.UnknownCpuFeature => {
869 log.err(
870 \\Unknown CPU feature: '{s}'
871 \\Available CPU features for architecture '{s}':
872 \\
873 , .{
874 diags.unknown_feature_name.?,
875 @tagName(diags.arch.?),
876 });
877 for (diags.arch.?.allFeaturesList()) |feature| {
878 log.err(" {s}: {s}", .{ feature.name, feature.description });
879 }
880 self.markInvalidUserInput();
881 return args.default_target;
882 },
883 error.UnknownOperatingSystem => {
884 log.err(
885 \\Unknown OS: '{s}'
886 \\Available operating systems:
887 \\
888 , .{diags.os_name.?});
889 inline for (std.meta.fields(std.Target.Os.Tag)) |field| {
890 log.err(" {s}", .{field.name});
891 }
892 self.markInvalidUserInput();
893 return args.default_target;
894 },
895 else => |e| {
896 log.err("Unable to parse target '{s}': {s}\n", .{ triple, @errorName(e) });
897 self.markInvalidUserInput();
898 return args.default_target;
899 },
900 };
901
902 const selected_canonicalized_triple = selected_target.zigTriple(self.allocator) catch unreachable;
903
904 if (args.whitelist) |list| whitelist_check: {
905 // Make sure it's a match of one of the list.
906 var mismatch_triple = true;
907 var mismatch_cpu_features = true;
908 var whitelist_item = CrossTarget{};
909 for (list) |t| {
910 mismatch_cpu_features = true;
911 mismatch_triple = true;
912
913 const t_triple = t.zigTriple(self.allocator) catch unreachable;
914 if (mem.eql(u8, t_triple, selected_canonicalized_triple)) {
915 mismatch_triple = false;
916 whitelist_item = t;
917 if (t.getCpuFeatures().isSuperSetOf(selected_target.getCpuFeatures())) {
918 mismatch_cpu_features = false;
919 break :whitelist_check;
920 } else {
921 break;
922 }
923 }
924 }
925 if (mismatch_triple) {
926 log.err("Chosen target '{s}' does not match one of the supported targets:", .{
927 selected_canonicalized_triple,
928 });
929 for (list) |t| {
930 const t_triple = t.zigTriple(self.allocator) catch unreachable;
931 log.err(" {s}", .{t_triple});
932 }
933 } else {
934 assert(mismatch_cpu_features);
935 const whitelist_cpu = whitelist_item.getCpu();
936 const selected_cpu = selected_target.getCpu();
937 log.err("Chosen CPU model '{s}' does not match one of the supported targets:", .{
938 selected_cpu.model.name,
939 });
940 log.err(" Supported feature Set: ", .{});
941 const all_features = whitelist_cpu.arch.allFeaturesList();
942 var populated_cpu_features = whitelist_cpu.model.features;
943 populated_cpu_features.populateDependencies(all_features);
944 for (all_features) |feature, i_usize| {
945 const i = @intCast(std.Target.Cpu.Feature.Set.Index, i_usize);
946 const in_cpu_set = populated_cpu_features.isEnabled(i);
947 if (in_cpu_set) {
948 log.err("{s} ", .{feature.name});
949 }
950 }
951 log.err(" Remove: ", .{});
952 for (all_features) |feature, i_usize| {
953 const i = @intCast(std.Target.Cpu.Feature.Set.Index, i_usize);
954 const in_cpu_set = populated_cpu_features.isEnabled(i);
955 const in_actual_set = selected_cpu.features.isEnabled(i);
956 if (in_actual_set and !in_cpu_set) {
957 log.err("{s} ", .{feature.name});
958 }
959 }
960 }
961 self.markInvalidUserInput();
962 return args.default_target;
963 }
964
965 return selected_target;
966 }
967
968 pub fn addUserInputOption(self: *Builder, name_raw: []const u8, value_raw: []const u8) !bool {
969 const name = self.dupe(name_raw);
970 const value = self.dupe(value_raw);
971 const gop = try self.user_input_options.getOrPut(name);
972 if (!gop.found_existing) {
973 gop.value_ptr.* = UserInputOption{
974 .name = name,
975 .value = .{ .scalar = value },
976 .used = false,
977 };
978 return false;
979 }
980
981 // option already exists
982 switch (gop.value_ptr.value) {
983 .scalar => |s| {
984 // turn it into a list
985 var list = ArrayList([]const u8).init(self.allocator);
986 list.append(s) catch unreachable;
987 list.append(value) catch unreachable;
988 self.user_input_options.put(name, .{
989 .name = name,
990 .value = .{ .list = list },
991 .used = false,
992 }) catch unreachable;
993 },
994 .list => |*list| {
995 // append to the list
996 list.append(value) catch unreachable;
997 self.user_input_options.put(name, .{
998 .name = name,
999 .value = .{ .list = list.* },
1000 .used = false,
1001 }) catch unreachable;
1002 },
1003 .flag => {
1004 log.warn("Option '-D{s}={s}' conflicts with flag '-D{s}'.", .{ name, value, name });
1005 return true;
1006 },
1007 }
1008 return false;
1009 }
1010
1011 pub fn addUserInputFlag(self: *Builder, name_raw: []const u8) !bool {
1012 const name = self.dupe(name_raw);
1013 const gop = try self.user_input_options.getOrPut(name);
1014 if (!gop.found_existing) {
1015 gop.value_ptr.* = .{
1016 .name = name,
1017 .value = .{ .flag = {} },
1018 .used = false,
1019 };
1020 return false;
1021 }
1022
1023 // option already exists
1024 switch (gop.value_ptr.value) {
1025 .scalar => |s| {
1026 log.err("Flag '-D{s}' conflicts with option '-D{s}={s}'.", .{ name, name, s });
1027 return true;
1028 },
1029 .list => {
1030 log.err("Flag '-D{s}' conflicts with multiple options of the same name.", .{name});
1031 return true;
1032 },
1033 .flag => {},
1034 }
1035 return false;
1036 }
1037
1038 fn typeToEnum(comptime T: type) TypeId {
1039 return switch (@typeInfo(T)) {
1040 .Int => .int,
1041 .Float => .float,
1042 .Bool => .bool,
1043 .Enum => .@"enum",
1044 else => switch (T) {
1045 []const u8 => .string,
1046 []const []const u8 => .list,
1047 else => @compileError("Unsupported type: " ++ @typeName(T)),
1048 },
1049 };
1050 }
1051
1052 fn markInvalidUserInput(self: *Builder) void {
1053 self.invalid_user_input = true;
1054 }
1055
1056 pub fn validateUserInputDidItFail(self: *Builder) bool {
1057 // make sure all args are used
1058 var it = self.user_input_options.iterator();
1059 while (it.next()) |entry| {
1060 if (!entry.value_ptr.used) {
1061 log.err("Invalid option: -D{s}\n", .{entry.key_ptr.*});
1062 self.markInvalidUserInput();
1063 }
1064 }
1065
1066 return self.invalid_user_input;
1067 }
1068
1069 pub fn spawnChild(self: *Builder, argv: []const []const u8) !void {
1070 return self.spawnChildEnvMap(null, self.env_map, argv);
1071 }
1072
1073 fn printCmd(cwd: ?[]const u8, argv: []const []const u8) void {
1074 if (cwd) |yes_cwd| std.debug.print("cd {s} && ", .{yes_cwd});
1075 for (argv) |arg| {
1076 std.debug.print("{s} ", .{arg});
1077 }
1078 std.debug.print("\n", .{});
1079 }
1080
1081 pub fn spawnChildEnvMap(self: *Builder, cwd: ?[]const u8, env_map: *const EnvMap, argv: []const []const u8) !void {
1082 if (self.verbose) {
1083 printCmd(cwd, argv);
1084 }
1085
1086 if (!std.process.can_spawn)
1087 return error.ExecNotSupported;
1088
1089 var child = std.ChildProcess.init(argv, self.allocator);
1090 child.cwd = cwd;
1091 child.env_map = env_map;
1092
1093 const term = child.spawnAndWait() catch |err| {
1094 log.err("Unable to spawn {s}: {s}", .{ argv[0], @errorName(err) });
1095 return err;
1096 };
1097
1098 switch (term) {
1099 .Exited => |code| {
1100 if (code != 0) {
1101 log.err("The following command exited with error code {}:", .{code});
1102 printCmd(cwd, argv);
1103 return error.UncleanExit;
1104 }
1105 },
1106 else => {
1107 log.err("The following command terminated unexpectedly:", .{});
1108 printCmd(cwd, argv);
1109
1110 return error.UncleanExit;
1111 },
1112 }
1113 }
1114
1115 pub fn makePath(self: *Builder, path: []const u8) !void {
1116 fs.cwd().makePath(self.pathFromRoot(path)) catch |err| {
1117 log.err("Unable to create path {s}: {s}", .{ path, @errorName(err) });
1118 return err;
1119 };
1120 }
1121
1122 pub fn installArtifact(self: *Builder, artifact: *LibExeObjStep) void {
1123 self.getInstallStep().dependOn(&self.addInstallArtifact(artifact).step);
1124 }
1125
1126 pub fn addInstallArtifact(self: *Builder, artifact: *LibExeObjStep) *InstallArtifactStep {
1127 return InstallArtifactStep.create(self, artifact);
1128 }
1129
1130 ///`dest_rel_path` is relative to prefix path
1131 pub fn installFile(self: *Builder, src_path: []const u8, dest_rel_path: []const u8) void {
1132 self.getInstallStep().dependOn(&self.addInstallFileWithDir(.{ .path = src_path }, .prefix, dest_rel_path).step);
1133 }
1134
1135 pub fn installDirectory(self: *Builder, options: InstallDirectoryOptions) void {
1136 self.getInstallStep().dependOn(&self.addInstallDirectory(options).step);
1137 }
1138
1139 ///`dest_rel_path` is relative to bin path
1140 pub fn installBinFile(self: *Builder, src_path: []const u8, dest_rel_path: []const u8) void {
1141 self.getInstallStep().dependOn(&self.addInstallFileWithDir(.{ .path = src_path }, .bin, dest_rel_path).step);
1142 }
1143
1144 ///`dest_rel_path` is relative to lib path
1145 pub fn installLibFile(self: *Builder, src_path: []const u8, dest_rel_path: []const u8) void {
1146 self.getInstallStep().dependOn(&self.addInstallFileWithDir(.{ .path = src_path }, .lib, dest_rel_path).step);
1147 }
1148
1149 /// Output format (BIN vs Intel HEX) determined by filename
1150 pub fn installRaw(self: *Builder, artifact: *LibExeObjStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep {
1151 const raw = self.addInstallRaw(artifact, dest_filename, options);
1152 self.getInstallStep().dependOn(&raw.step);
1153 return raw;
1154 }
1155
1156 ///`dest_rel_path` is relative to install prefix path
1157 pub fn addInstallFile(self: *Builder, source: FileSource, dest_rel_path: []const u8) *InstallFileStep {
1158 return self.addInstallFileWithDir(source.dupe(self), .prefix, dest_rel_path);
1159 }
1160
1161 ///`dest_rel_path` is relative to bin path
1162 pub fn addInstallBinFile(self: *Builder, source: FileSource, dest_rel_path: []const u8) *InstallFileStep {
1163 return self.addInstallFileWithDir(source.dupe(self), .bin, dest_rel_path);
1164 }
1165
1166 ///`dest_rel_path` is relative to lib path
1167 pub fn addInstallLibFile(self: *Builder, source: FileSource, dest_rel_path: []const u8) *InstallFileStep {
1168 return self.addInstallFileWithDir(source.dupe(self), .lib, dest_rel_path);
1169 }
1170
1171 pub fn addInstallHeaderFile(b: *Builder, src_path: []const u8, dest_rel_path: []const u8) *InstallFileStep {
1172 return b.addInstallFileWithDir(.{ .path = src_path }, .header, dest_rel_path);
1173 }
1174
1175 pub fn addInstallRaw(self: *Builder, artifact: *LibExeObjStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep {
1176 return InstallRawStep.create(self, artifact, dest_filename, options);
1177 }
1178
1179 pub fn addInstallFileWithDir(
1180 self: *Builder,
1181 source: FileSource,
1182 install_dir: InstallDir,
1183 dest_rel_path: []const u8,
1184 ) *InstallFileStep {
1185 if (dest_rel_path.len == 0) {
1186 panic("dest_rel_path must be non-empty", .{});
1187 }
1188 const install_step = self.allocator.create(InstallFileStep) catch unreachable;
1189 install_step.* = InstallFileStep.init(self, source.dupe(self), install_dir, dest_rel_path);
1190 return install_step;
1191 }
1192
1193 pub fn addInstallDirectory(self: *Builder, options: InstallDirectoryOptions) *InstallDirStep {
1194 const install_step = self.allocator.create(InstallDirStep) catch unreachable;
1195 install_step.* = InstallDirStep.init(self, options);
1196 return install_step;
1197 }
1198
1199 pub fn pushInstalledFile(self: *Builder, dir: InstallDir, dest_rel_path: []const u8) void {
1200 const file = InstalledFile{
1201 .dir = dir,
1202 .path = dest_rel_path,
1203 };
1204 self.installed_files.append(file.dupe(self)) catch unreachable;
1205 }
1206
1207 pub fn updateFile(self: *Builder, source_path: []const u8, dest_path: []const u8) !void {
1208 if (self.verbose) {
1209 log.info("cp {s} {s} ", .{ source_path, dest_path });
1210 }
1211 const cwd = fs.cwd();
1212 const prev_status = try fs.Dir.updateFile(cwd, source_path, cwd, dest_path, .{});
1213 if (self.verbose) switch (prev_status) {
1214 .stale => log.info("# installed", .{}),
1215 .fresh => log.info("# up-to-date", .{}),
1216 };
1217 }
1218
1219 pub fn truncateFile(self: *Builder, dest_path: []const u8) !void {
1220 if (self.verbose) {
1221 log.info("truncate {s}", .{dest_path});
1222 }
1223 const cwd = fs.cwd();
1224 var src_file = cwd.createFile(dest_path, .{}) catch |err| switch (err) {
1225 error.FileNotFound => blk: {
1226 if (fs.path.dirname(dest_path)) |dirname| {
1227 try cwd.makePath(dirname);
1228 }
1229 break :blk try cwd.createFile(dest_path, .{});
1230 },
1231 else => |e| return e,
1232 };
1233 src_file.close();
1234 }
1235
1236 pub fn pathFromRoot(self: *Builder, rel_path: []const u8) []u8 {
1237 return fs.path.resolve(self.allocator, &[_][]const u8{ self.build_root, rel_path }) catch unreachable;
1238 }
1239
1240 /// Shorthand for `std.fs.path.join(builder.allocator, paths) catch unreachable`
1241 pub fn pathJoin(self: *Builder, paths: []const []const u8) []u8 {
1242 return fs.path.join(self.allocator, paths) catch unreachable;
1243 }
1244
1245 pub fn fmt(self: *Builder, comptime format: []const u8, args: anytype) []u8 {
1246 return fmt_lib.allocPrint(self.allocator, format, args) catch unreachable;
1247 }
1248
1249 pub fn findProgram(self: *Builder, names: []const []const u8, paths: []const []const u8) ![]const u8 {
1250 // TODO report error for ambiguous situations
1251 const exe_extension = @as(CrossTarget, .{}).exeFileExt();
1252 for (self.search_prefixes.items) |search_prefix| {
1253 for (names) |name| {
1254 if (fs.path.isAbsolute(name)) {
1255 return name;
1256 }
1257 const full_path = self.pathJoin(&.{
1258 search_prefix,
1259 "bin",
1260 self.fmt("{s}{s}", .{ name, exe_extension }),
1261 });
1262 return fs.realpathAlloc(self.allocator, full_path) catch continue;
1263 }
1264 }
1265 if (self.env_map.get("PATH")) |PATH| {
1266 for (names) |name| {
1267 if (fs.path.isAbsolute(name)) {
1268 return name;
1269 }
1270 var it = mem.tokenize(u8, PATH, &[_]u8{fs.path.delimiter});
1271 while (it.next()) |path| {
1272 const full_path = self.pathJoin(&.{
1273 path,
1274 self.fmt("{s}{s}", .{ name, exe_extension }),
1275 });
1276 return fs.realpathAlloc(self.allocator, full_path) catch continue;
1277 }
1278 }
1279 }
1280 for (names) |name| {
1281 if (fs.path.isAbsolute(name)) {
1282 return name;
1283 }
1284 for (paths) |path| {
1285 const full_path = self.pathJoin(&.{
1286 path,
1287 self.fmt("{s}{s}", .{ name, exe_extension }),
1288 });
1289 return fs.realpathAlloc(self.allocator, full_path) catch continue;
1290 }
1291 }
1292 return error.FileNotFound;
1293 }
1294
1295 pub fn execAllowFail(
1296 self: *Builder,
1297 argv: []const []const u8,
1298 out_code: *u8,
1299 stderr_behavior: std.ChildProcess.StdIo,
1300 ) ExecError![]u8 {
1301 assert(argv.len != 0);
1302
1303 if (!std.process.can_spawn)
1304 return error.ExecNotSupported;
1305
1306 const max_output_size = 400 * 1024;
1307 var child = std.ChildProcess.init(argv, self.allocator);
1308 child.stdin_behavior = .Ignore;
1309 child.stdout_behavior = .Pipe;
1310 child.stderr_behavior = stderr_behavior;
1311 child.env_map = self.env_map;
1312
1313 try child.spawn();
1314
1315 const stdout = child.stdout.?.reader().readAllAlloc(self.allocator, max_output_size) catch {
1316 return error.ReadFailure;
1317 };
1318 errdefer self.allocator.free(stdout);
1319
1320 const term = try child.wait();
1321 switch (term) {
1322 .Exited => |code| {
1323 if (code != 0) {
1324 out_code.* = @truncate(u8, code);
1325 return error.ExitCodeFailure;
1326 }
1327 return stdout;
1328 },
1329 .Signal, .Stopped, .Unknown => |code| {
1330 out_code.* = @truncate(u8, code);
1331 return error.ProcessTerminated;
1332 },
1333 }
1334 }
1335
1336 pub fn execFromStep(self: *Builder, argv: []const []const u8, src_step: ?*Step) ![]u8 {
1337 assert(argv.len != 0);
1338
1339 if (self.verbose) {
1340 printCmd(null, argv);
1341 }
1342
1343 if (!std.process.can_spawn) {
1344 if (src_step) |s| log.err("{s}...", .{s.name});
1345 log.err("Unable to spawn the following command: cannot spawn child process", .{});
1346 printCmd(null, argv);
1347 std.os.abort();
1348 }
1349
1350 var code: u8 = undefined;
1351 return self.execAllowFail(argv, &code, .Inherit) catch |err| switch (err) {
1352 error.ExecNotSupported => {
1353 if (src_step) |s| log.err("{s}...", .{s.name});
1354 log.err("Unable to spawn the following command: cannot spawn child process", .{});
1355 printCmd(null, argv);
1356 std.os.abort();
1357 },
1358 error.FileNotFound => {
1359 if (src_step) |s| log.err("{s}...", .{s.name});
1360 log.err("Unable to spawn the following command: file not found", .{});
1361 printCmd(null, argv);
1362 std.os.exit(@truncate(u8, code));
1363 },
1364 error.ExitCodeFailure => {
1365 if (src_step) |s| log.err("{s}...", .{s.name});
1366 if (self.prominent_compile_errors) {
1367 log.err("The step exited with error code {d}", .{code});
1368 } else {
1369 log.err("The following command exited with error code {d}:", .{code});
1370 printCmd(null, argv);
1371 }
1372
1373 std.os.exit(@truncate(u8, code));
1374 },
1375 error.ProcessTerminated => {
1376 if (src_step) |s| log.err("{s}...", .{s.name});
1377 log.err("The following command terminated unexpectedly:", .{});
1378 printCmd(null, argv);
1379 std.os.exit(@truncate(u8, code));
1380 },
1381 else => |e| return e,
1382 };
1383 }
1384
1385 pub fn exec(self: *Builder, argv: []const []const u8) ![]u8 {
1386 return self.execFromStep(argv, null);
1387 }
1388
1389 pub fn addSearchPrefix(self: *Builder, search_prefix: []const u8) void {
1390 self.search_prefixes.append(self.dupePath(search_prefix)) catch unreachable;
1391 }
1392
1393 pub fn getInstallPath(self: *Builder, dir: InstallDir, dest_rel_path: []const u8) []const u8 {
1394 assert(!fs.path.isAbsolute(dest_rel_path)); // Install paths must be relative to the prefix
1395 const base_dir = switch (dir) {
1396 .prefix => self.install_path,
1397 .bin => self.exe_dir,
1398 .lib => self.lib_dir,
1399 .header => self.h_dir,
1400 .custom => |path| self.pathJoin(&.{ self.install_path, path }),
1401 };
1402 return fs.path.resolve(
1403 self.allocator,
1404 &[_][]const u8{ base_dir, dest_rel_path },
1405 ) catch unreachable;
1406 }
1407
1408 pub const Dependency = struct {
1409 builder: *Builder,
1410
1411 pub fn artifact(d: *Dependency, name: []const u8) *LibExeObjStep {
1412 var found: ?*LibExeObjStep = null;
1413 for (d.builder.install_tls.step.dependencies.items) |dep_step| {
1414 const inst = dep_step.cast(InstallArtifactStep) orelse continue;
1415 if (mem.eql(u8, inst.artifact.name, name)) {
1416 if (found != null) panic("artifact name '{s}' is ambiguous", .{name});
1417 found = inst.artifact;
1418 }
1419 }
1420 return found orelse {
1421 for (d.builder.install_tls.step.dependencies.items) |dep_step| {
1422 const inst = dep_step.cast(InstallArtifactStep) orelse continue;
1423 log.info("available artifact: '{s}'", .{inst.artifact.name});
1424 }
1425 panic("unable to find artifact '{s}'", .{name});
1426 };
1427 }
1428 };
1429
1430 pub fn dependency(b: *Builder, name: []const u8, args: anytype) *Dependency {
1431 const build_runner = @import("root");
1432 const deps = build_runner.dependencies;
1433
1434 inline for (@typeInfo(deps.imports).Struct.decls) |decl| {
1435 if (mem.startsWith(u8, decl.name, b.dep_prefix) and
1436 mem.endsWith(u8, decl.name, name) and
1437 decl.name.len == b.dep_prefix.len + name.len)
1438 {
1439 const build_zig = @field(deps.imports, decl.name);
1440 const build_root = @field(deps.build_root, decl.name);
1441 return dependencyInner(b, name, build_root, build_zig, args);
1442 }
1443 }
1444
1445 const full_path = b.pathFromRoot("build.zig.ini");
1446 std.debug.print("no dependency named '{s}' in '{s}'\n", .{ name, full_path });
1447 std.process.exit(1);
1448 }
1449
1450 fn dependencyInner(
1451 b: *Builder,
1452 name: []const u8,
1453 build_root: []const u8,
1454 comptime build_zig: type,
1455 args: anytype,
1456 ) *Dependency {
1457 const sub_builder = b.createChild(name, build_root, args) catch unreachable;
1458 sub_builder.runBuild(build_zig) catch unreachable;
1459 const dep = b.allocator.create(Dependency) catch unreachable;
1460 dep.* = .{ .builder = sub_builder };
1461 return dep;
1462 }
1463
1464 pub fn runBuild(b: *Builder, build_zig: anytype) anyerror!void {
1465 switch (@typeInfo(@typeInfo(@TypeOf(build_zig.build)).Fn.return_type.?)) {
1466 .Void => build_zig.build(b),
1467 .ErrorUnion => try build_zig.build(b),
1468 else => @compileError("expected return type of build to be 'void' or '!void'"),
1469 }
1470 }
1471};
1472
1473test "builder.findProgram compiles" {
1474 if (builtin.os.tag == .wasi) return error.SkipZigTest;
1475
1476 var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
1477 defer arena.deinit();
1478
1479 const builder = try Builder.create(
1480 arena.allocator(),
1481 "zig",
1482 "zig-cache",
1483 "zig-cache",
1484 "zig-cache",
1485 );
1486 defer builder.destroy();
1487 _ = builder.findProgram(&[_][]const u8{}, &[_][]const u8{}) catch null;
1488}
1489
1490pub const Pkg = struct {
1491 name: []const u8,
1492 source: FileSource,
1493 dependencies: ?[]const Pkg = null,
1494};
1495
1496/// A file that is generated by a build step.
1497/// This struct is an interface that is meant to be used with `@fieldParentPtr` to implement the actual path logic.
1498pub const GeneratedFile = struct {
1499 /// The step that generates the file
1500 step: *Step,
1501
1502 /// The path to the generated file. Must be either absolute or relative to the build root.
1503 /// This value must be set in the `fn make()` of the `step` and must not be `null` afterwards.
1504 path: ?[]const u8 = null,
1505
1506 pub fn getPath(self: GeneratedFile) []const u8 {
1507 return self.path orelse std.debug.panic(
1508 "getPath() was called on a GeneratedFile that wasn't build yet. Is there a missing Step dependency on step '{s}'?",
1509 .{self.step.name},
1510 );
1511 }
1512};
1513
1514/// A file source is a reference to an existing or future file.
1515///
1516pub const FileSource = union(enum) {
1517 /// A plain file path, relative to build root or absolute.
1518 path: []const u8,
1519
1520 /// A file that is generated by an interface. Those files usually are
1521 /// not available until built by a build step.
1522 generated: *const GeneratedFile,
1523
1524 /// Returns a new file source that will have a relative path to the build root guaranteed.
1525 /// This should be preferred over setting `.path` directly as it documents that the files are in the project directory.
1526 pub fn relative(path: []const u8) FileSource {
1527 std.debug.assert(!std.fs.path.isAbsolute(path));
1528 return FileSource{ .path = path };
1529 }
1530
1531 /// Returns a string that can be shown to represent the file source.
1532 /// Either returns the path or `"generated"`.
1533 pub fn getDisplayName(self: FileSource) []const u8 {
1534 return switch (self) {
1535 .path => self.path,
1536 .generated => "generated",
1537 };
1538 }
1539
1540 /// Adds dependencies this file source implies to the given step.
1541 pub fn addStepDependencies(self: FileSource, step: *Step) void {
1542 switch (self) {
1543 .path => {},
1544 .generated => |gen| step.dependOn(gen.step),
1545 }
1546 }
1547
1548 /// Should only be called during make(), returns a path relative to the build root or absolute.
1549 pub fn getPath(self: FileSource, builder: *Builder) []const u8 {
1550 const path = switch (self) {
1551 .path => |p| builder.pathFromRoot(p),
1552 .generated => |gen| gen.getPath(),
1553 };
1554 return path;
1555 }
1556
1557 /// Duplicates the file source for a given builder.
1558 pub fn dupe(self: FileSource, b: *Builder) FileSource {
1559 return switch (self) {
1560 .path => |p| .{ .path = b.dupePath(p) },
1561 .generated => |gen| .{ .generated = gen },
1562 };
1563 }
1564};
1565
1566/// Allocates a new string for assigning a value to a named macro.
1567/// If the value is omitted, it is set to 1.
1568/// `name` and `value` need not live longer than the function call.
1569pub fn constructCMacro(allocator: Allocator, name: []const u8, value: ?[]const u8) []const u8 {
1570 var macro = allocator.alloc(
1571 u8,
1572 name.len + if (value) |value_slice| value_slice.len + 1 else 0,
1573 ) catch |err| if (err == error.OutOfMemory) @panic("Out of memory") else unreachable;
1574 mem.copy(u8, macro, name);
1575 if (value) |value_slice| {
1576 macro[name.len] = '=';
1577 mem.copy(u8, macro[name.len + 1 ..], value_slice);
1578 }
1579 return macro;
1580}
1581
1582/// deprecated: use `InstallDirStep.Options`
1583pub const InstallDirectoryOptions = InstallDirStep.Options;
1584
1585pub const Step = struct {
1586 id: Id,
1587 name: []const u8,
1588 makeFn: MakeFn,
1589 dependencies: ArrayList(*Step),
1590 loop_flag: bool,
1591 done_flag: bool,
1592
1593 const MakeFn = *const fn (self: *Step) anyerror!void;
1594
1595 pub const Id = enum {
1596 top_level,
1597 lib_exe_obj,
1598 install_artifact,
1599 install_file,
1600 install_dir,
1601 log,
1602 remove_dir,
1603 fmt,
1604 translate_c,
1605 write_file,
1606 run,
1607 emulatable_run,
1608 check_file,
1609 check_object,
1610 config_header,
1611 install_raw,
1612 options,
1613 custom,
1614
1615 pub fn Type(comptime id: Id) type {
1616 return switch (id) {
1617 .top_level => Builder.TopLevelStep,
1618 .lib_exe_obj => LibExeObjStep,
1619 .install_artifact => InstallArtifactStep,
1620 .install_file => InstallFileStep,
1621 .install_dir => InstallDirStep,
1622 .log => LogStep,
1623 .remove_dir => RemoveDirStep,
1624 .fmt => FmtStep,
1625 .translate_c => TranslateCStep,
1626 .write_file => WriteFileStep,
1627 .run => RunStep,
1628 .emulatable_run => EmulatableRunStep,
1629 .check_file => CheckFileStep,
1630 .check_object => CheckObjectStep,
1631 .config_header => ConfigHeaderStep,
1632 .install_raw => InstallRawStep,
1633 .options => OptionsStep,
1634 .custom => @compileError("no type available for custom step"),
1635 };
1636 }
1637 };
1638
1639 pub fn init(id: Id, name: []const u8, allocator: Allocator, makeFn: MakeFn) Step {
1640 return Step{
1641 .id = id,
1642 .name = allocator.dupe(u8, name) catch unreachable,
1643 .makeFn = makeFn,
1644 .dependencies = ArrayList(*Step).init(allocator),
1645 .loop_flag = false,
1646 .done_flag = false,
1647 };
1648 }
1649 pub fn initNoOp(id: Id, name: []const u8, allocator: Allocator) Step {
1650 return init(id, name, allocator, makeNoOp);
1651 }
1652
1653 pub fn make(self: *Step) !void {
1654 if (self.done_flag) return;
1655
1656 try self.makeFn(self);
1657 self.done_flag = true;
1658 }
1659
1660 pub fn dependOn(self: *Step, other: *Step) void {
1661 self.dependencies.append(other) catch unreachable;
1662 }
1663
1664 fn makeNoOp(self: *Step) anyerror!void {
1665 _ = self;
1666 }
1667
1668 pub fn cast(step: *Step, comptime T: type) ?*T {
1669 if (step.id == T.base_id) {
1670 return @fieldParentPtr(T, "step", step);
1671 }
1672 return null;
1673 }
1674};
1675
1676pub const VcpkgRoot = union(VcpkgRootStatus) {
1677 unattempted: void,
1678 not_found: void,
1679 found: []const u8,
1680};
1681
1682pub const VcpkgRootStatus = enum {
1683 unattempted,
1684 not_found,
1685 found,
1686};
1687
1688pub const InstallDir = union(enum) {
1689 prefix: void,
1690 lib: void,
1691 bin: void,
1692 header: void,
1693 /// A path relative to the prefix
1694 custom: []const u8,
1695
1696 /// Duplicates the install directory including the path if set to custom.
1697 pub fn dupe(self: InstallDir, builder: *Builder) InstallDir {
1698 if (self == .custom) {
1699 // Written with this temporary to avoid RLS problems
1700 const duped_path = builder.dupe(self.custom);
1701 return .{ .custom = duped_path };
1702 } else {
1703 return self;
1704 }
1705 }
1706};
1707
1708pub const InstalledFile = struct {
1709 dir: InstallDir,
1710 path: []const u8,
1711
1712 /// Duplicates the installed file path and directory.
1713 pub fn dupe(self: InstalledFile, builder: *Builder) InstalledFile {
1714 return .{
1715 .dir = self.dir.dupe(builder),
1716 .path = builder.dupe(self.path),
1717 };
1718 }
1719};
1720
1721test "dupePkg()" {
1722 if (builtin.os.tag == .wasi) return error.SkipZigTest;
1723
1724 var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
1725 defer arena.deinit();
1726 var builder = try Builder.create(
1727 arena.allocator(),
1728 "test",
1729 "test",
1730 "test",
1731 "test",
1732 );
1733 defer builder.destroy();
1734
1735 var pkg_dep = Pkg{
1736 .name = "pkg_dep",
1737 .source = .{ .path = "/not/a/pkg_dep.zig" },
1738 };
1739 var pkg_top = Pkg{
1740 .name = "pkg_top",
1741 .source = .{ .path = "/not/a/pkg_top.zig" },
1742 .dependencies = &[_]Pkg{pkg_dep},
1743 };
1744 const dupe = builder.dupePkg(pkg_top);
1745
1746 const original_deps = pkg_top.dependencies.?;
1747 const dupe_deps = dupe.dependencies.?;
1748
1749 // probably the same top level package details
1750 try std.testing.expectEqualStrings(pkg_top.name, dupe.name);
1751
1752 // probably the same dependencies
1753 try std.testing.expectEqual(original_deps.len, dupe_deps.len);
1754 try std.testing.expectEqual(original_deps[0].name, pkg_dep.name);
1755
1756 // could segfault otherwise if pointers in duplicated package's fields are
1757 // the same as those in stack allocated package's fields
1758 try std.testing.expect(dupe_deps.ptr != original_deps.ptr);
1759 try std.testing.expect(dupe.name.ptr != pkg_top.name.ptr);
1760 try std.testing.expect(dupe.source.path.ptr != pkg_top.source.path.ptr);
1761 try std.testing.expect(dupe_deps[0].name.ptr != pkg_dep.name.ptr);
1762 try std.testing.expect(dupe_deps[0].source.path.ptr != pkg_dep.source.path.ptr);
1763}
1764
1765test {
1766 _ = CheckFileStep;
1767 _ = CheckObjectStep;
1768 _ = EmulatableRunStep;
1769 _ = FmtStep;
1770 _ = InstallArtifactStep;
1771 _ = InstallDirStep;
1772 _ = InstallFileStep;
1773 _ = InstallRawStep;
1774 _ = LibExeObjStep;
1775 _ = LogStep;
1776 _ = OptionsStep;
1777 _ = RemoveDirStep;
1778 _ = RunStep;
1779 _ = TranslateCStep;
1780 _ = WriteFileStep;
1781}
lib/std/build/CheckFileStep.zig deleted-53
...@@ -1,53 +0,0 @@
1const std = @import("../std.zig");
2const build = std.build;
3const Step = build.Step;
4const Builder = build.Builder;
5const fs = std.fs;
6const mem = std.mem;
7
8const CheckFileStep = @This();
9
10pub const base_id = .check_file;
11
12step: Step,
13builder: *Builder,
14expected_matches: []const []const u8,
15source: build.FileSource,
16max_bytes: usize = 20 * 1024 * 1024,
17
18pub fn create(
19 builder: *Builder,
20 source: build.FileSource,
21 expected_matches: []const []const u8,
22) *CheckFileStep {
23 const self = builder.allocator.create(CheckFileStep) catch unreachable;
24 self.* = CheckFileStep{
25 .builder = builder,
26 .step = Step.init(.check_file, "CheckFile", builder.allocator, make),
27 .source = source.dupe(builder),
28 .expected_matches = builder.dupeStrings(expected_matches),
29 };
30 self.source.addStepDependencies(&self.step);
31 return self;
32}
33
34fn make(step: *Step) !void {
35 const self = @fieldParentPtr(CheckFileStep, "step", step);
36
37 const src_path = self.source.getPath(self.builder);
38 const contents = try fs.cwd().readFileAlloc(self.builder.allocator, src_path, self.max_bytes);
39
40 for (self.expected_matches) |expected_match| {
41 if (mem.indexOf(u8, contents, expected_match) == null) {
42 std.debug.print(
43 \\
44 \\========= Expected to find: ===================
45 \\{s}
46 \\========= But file does not contain it: =======
47 \\{s}
48 \\
49 , .{ expected_match, contents });
50 return error.TestFailed;
51 }
52 }
53}
lib/std/build/CheckObjectStep.zig deleted-1026
...@@ -1,1026 +0,0 @@
1const std = @import("../std.zig");
2const assert = std.debug.assert;
3const build = std.build;
4const fs = std.fs;
5const macho = std.macho;
6const math = std.math;
7const mem = std.mem;
8const testing = std.testing;
9
10const CheckObjectStep = @This();
11
12const Allocator = mem.Allocator;
13const Builder = build.Builder;
14const Step = build.Step;
15const EmulatableRunStep = build.EmulatableRunStep;
16
17pub const base_id = .check_object;
18
19step: Step,
20builder: *Builder,
21source: build.FileSource,
22max_bytes: usize = 20 * 1024 * 1024,
23checks: std.ArrayList(Check),
24dump_symtab: bool = false,
25obj_format: std.Target.ObjectFormat,
26
27pub fn create(builder: *Builder, source: build.FileSource, obj_format: std.Target.ObjectFormat) *CheckObjectStep {
28 const gpa = builder.allocator;
29 const self = gpa.create(CheckObjectStep) catch unreachable;
30 self.* = .{
31 .builder = builder,
32 .step = Step.init(.check_file, "CheckObject", gpa, make),
33 .source = source.dupe(builder),
34 .checks = std.ArrayList(Check).init(gpa),
35 .obj_format = obj_format,
36 };
37 self.source.addStepDependencies(&self.step);
38 return self;
39}
40
41/// Runs and (optionally) compares the output of a binary.
42/// Asserts `self` was generated from an executable step.
43pub fn runAndCompare(self: *CheckObjectStep) *EmulatableRunStep {
44 const dependencies_len = self.step.dependencies.items.len;
45 assert(dependencies_len > 0);
46 const exe_step = self.step.dependencies.items[dependencies_len - 1];
47 const exe = exe_step.cast(std.build.LibExeObjStep).?;
48 const emulatable_step = EmulatableRunStep.create(self.builder, "EmulatableRun", exe);
49 emulatable_step.step.dependOn(&self.step);
50 return emulatable_step;
51}
52
53/// There two types of actions currently suported:
54/// * `.match` - is the main building block of standard matchers with optional eat-all token `{*}`
55/// and extractors by name such as `{n_value}`. Please note this action is very simplistic in nature
56/// i.e., it won't really handle edge cases/nontrivial examples. But given that we do want to use
57/// it mainly to test the output of our object format parser-dumpers when testing the linkers, etc.
58/// it should be plenty useful in its current form.
59/// * `.compute_cmp` - can be used to perform an operation on the extracted global variables
60/// using the MatchAction. It currently only supports an addition. The operation is required
61/// to be specified in Reverse Polish Notation to ease in operator-precedence parsing (well,
62/// to avoid any parsing really).
63/// For example, if the two extracted values were saved as `vmaddr` and `entryoff` respectively
64/// they could then be added with this simple program `vmaddr entryoff +`.
65const Action = struct {
66 tag: enum { match, not_present, compute_cmp },
67 phrase: []const u8,
68 expected: ?ComputeCompareExpected = null,
69
70 /// Will return true if the `phrase` was found in the `haystack`.
71 /// Some examples include:
72 ///
73 /// LC 0 => will match in its entirety
74 /// vmaddr {vmaddr} => will match `vmaddr` and then extract the following value as u64
75 /// and save under `vmaddr` global name (see `global_vars` param)
76 /// name {*}libobjc{*}.dylib => will match `name` followed by a token which contains `libobjc` and `.dylib`
77 /// in that order with other letters in between
78 fn match(act: Action, haystack: []const u8, global_vars: anytype) !bool {
79 assert(act.tag == .match or act.tag == .not_present);
80
81 var candidate_var: ?struct { name: []const u8, value: u64 } = null;
82 var hay_it = mem.tokenize(u8, mem.trim(u8, haystack, " "), " ");
83 var needle_it = mem.tokenize(u8, mem.trim(u8, act.phrase, " "), " ");
84
85 while (needle_it.next()) |needle_tok| {
86 const hay_tok = hay_it.next() orelse return false;
87
88 if (mem.indexOf(u8, needle_tok, "{*}")) |index| {
89 // We have fuzzy matchers within the search pattern, so we match substrings.
90 var start = index;
91 var n_tok = needle_tok;
92 var h_tok = hay_tok;
93 while (true) {
94 n_tok = n_tok[start + 3 ..];
95 const inner = if (mem.indexOf(u8, n_tok, "{*}")) |sub_end|
96 n_tok[0..sub_end]
97 else
98 n_tok;
99 if (mem.indexOf(u8, h_tok, inner) == null) return false;
100 start = mem.indexOf(u8, n_tok, "{*}") orelse break;
101 }
102 } else if (mem.startsWith(u8, needle_tok, "{")) {
103 const closing_brace = mem.indexOf(u8, needle_tok, "}") orelse return error.MissingClosingBrace;
104 if (closing_brace != needle_tok.len - 1) return error.ClosingBraceNotLast;
105
106 const name = needle_tok[1..closing_brace];
107 if (name.len == 0) return error.MissingBraceValue;
108 const value = try std.fmt.parseInt(u64, hay_tok, 16);
109 candidate_var = .{
110 .name = name,
111 .value = value,
112 };
113 } else {
114 if (!mem.eql(u8, hay_tok, needle_tok)) return false;
115 }
116 }
117
118 if (candidate_var) |v| {
119 try global_vars.putNoClobber(v.name, v.value);
120 }
121
122 return true;
123 }
124
125 /// Will return true if the `phrase` is correctly parsed into an RPN program and
126 /// its reduced, computed value compares using `op` with the expected value, either
127 /// a literal or another extracted variable.
128 fn computeCmp(act: Action, gpa: Allocator, global_vars: anytype) !bool {
129 var op_stack = std.ArrayList(enum { add, sub, mod, mul }).init(gpa);
130 var values = std.ArrayList(u64).init(gpa);
131
132 var it = mem.tokenize(u8, act.phrase, " ");
133 while (it.next()) |next| {
134 if (mem.eql(u8, next, "+")) {
135 try op_stack.append(.add);
136 } else if (mem.eql(u8, next, "-")) {
137 try op_stack.append(.sub);
138 } else if (mem.eql(u8, next, "%")) {
139 try op_stack.append(.mod);
140 } else if (mem.eql(u8, next, "*")) {
141 try op_stack.append(.mul);
142 } else {
143 const val = std.fmt.parseInt(u64, next, 0) catch blk: {
144 break :blk global_vars.get(next) orelse {
145 std.debug.print(
146 \\
147 \\========= Variable was not extracted: ===========
148 \\{s}
149 \\
150 , .{next});
151 return error.UnknownVariable;
152 };
153 };
154 try values.append(val);
155 }
156 }
157
158 var op_i: usize = 1;
159 var reduced: u64 = values.items[0];
160 for (op_stack.items) |op| {
161 const other = values.items[op_i];
162 switch (op) {
163 .add => {
164 reduced += other;
165 },
166 .sub => {
167 reduced -= other;
168 },
169 .mod => {
170 reduced %= other;
171 },
172 .mul => {
173 reduced *= other;
174 },
175 }
176 op_i += 1;
177 }
178
179 const exp_value = switch (act.expected.?.value) {
180 .variable => |name| global_vars.get(name) orelse {
181 std.debug.print(
182 \\
183 \\========= Variable was not extracted: ===========
184 \\{s}
185 \\
186 , .{name});
187 return error.UnknownVariable;
188 },
189 .literal => |x| x,
190 };
191 return math.compare(reduced, act.expected.?.op, exp_value);
192 }
193};
194
195const ComputeCompareExpected = struct {
196 op: math.CompareOperator,
197 value: union(enum) {
198 variable: []const u8,
199 literal: u64,
200 },
201
202 pub fn format(
203 value: @This(),
204 comptime fmt: []const u8,
205 options: std.fmt.FormatOptions,
206 writer: anytype,
207 ) !void {
208 if (fmt.len != 0) std.fmt.invalidFmtError(fmt, value);
209 _ = options;
210 try writer.print("{s} ", .{@tagName(value.op)});
211 switch (value.value) {
212 .variable => |name| try writer.writeAll(name),
213 .literal => |x| try writer.print("{x}", .{x}),
214 }
215 }
216};
217
218const Check = struct {
219 builder: *Builder,
220 actions: std.ArrayList(Action),
221
222 fn create(b: *Builder) Check {
223 return .{
224 .builder = b,
225 .actions = std.ArrayList(Action).init(b.allocator),
226 };
227 }
228
229 fn match(self: *Check, phrase: []const u8) void {
230 self.actions.append(.{
231 .tag = .match,
232 .phrase = self.builder.dupe(phrase),
233 }) catch unreachable;
234 }
235
236 fn notPresent(self: *Check, phrase: []const u8) void {
237 self.actions.append(.{
238 .tag = .not_present,
239 .phrase = self.builder.dupe(phrase),
240 }) catch unreachable;
241 }
242
243 fn computeCmp(self: *Check, phrase: []const u8, expected: ComputeCompareExpected) void {
244 self.actions.append(.{
245 .tag = .compute_cmp,
246 .phrase = self.builder.dupe(phrase),
247 .expected = expected,
248 }) catch unreachable;
249 }
250};
251
252/// Creates a new sequence of actions with `phrase` as the first anchor searched phrase.
253pub fn checkStart(self: *CheckObjectStep, phrase: []const u8) void {
254 var new_check = Check.create(self.builder);
255 new_check.match(phrase);
256 self.checks.append(new_check) catch unreachable;
257}
258
259/// Adds another searched phrase to the latest created Check with `CheckObjectStep.checkStart(...)`.
260/// Asserts at least one check already exists.
261pub fn checkNext(self: *CheckObjectStep, phrase: []const u8) void {
262 assert(self.checks.items.len > 0);
263 const last = &self.checks.items[self.checks.items.len - 1];
264 last.match(phrase);
265}
266
267/// Adds another searched phrase to the latest created Check with `CheckObjectStep.checkStart(...)`
268/// however ensures there is no matching phrase in the output.
269/// Asserts at least one check already exists.
270pub fn checkNotPresent(self: *CheckObjectStep, phrase: []const u8) void {
271 assert(self.checks.items.len > 0);
272 const last = &self.checks.items[self.checks.items.len - 1];
273 last.notPresent(phrase);
274}
275
276/// Creates a new check checking specifically symbol table parsed and dumped from the object
277/// file.
278/// Issuing this check will force parsing and dumping of the symbol table.
279pub fn checkInSymtab(self: *CheckObjectStep) void {
280 self.dump_symtab = true;
281 const symtab_label = switch (self.obj_format) {
282 .macho => MachODumper.symtab_label,
283 else => @panic("TODO other parsers"),
284 };
285 self.checkStart(symtab_label);
286}
287
288/// Creates a new standalone, singular check which allows running simple binary operations
289/// on the extracted variables. It will then compare the reduced program with the value of
290/// the expected variable.
291pub fn checkComputeCompare(
292 self: *CheckObjectStep,
293 program: []const u8,
294 expected: ComputeCompareExpected,
295) void {
296 var new_check = Check.create(self.builder);
297 new_check.computeCmp(program, expected);
298 self.checks.append(new_check) catch unreachable;
299}
300
301fn make(step: *Step) !void {
302 const self = @fieldParentPtr(CheckObjectStep, "step", step);
303
304 const gpa = self.builder.allocator;
305 const src_path = self.source.getPath(self.builder);
306 const contents = try fs.cwd().readFileAllocOptions(
307 gpa,
308 src_path,
309 self.max_bytes,
310 null,
311 @alignOf(u64),
312 null,
313 );
314
315 const output = switch (self.obj_format) {
316 .macho => try MachODumper.parseAndDump(contents, .{
317 .gpa = gpa,
318 .dump_symtab = self.dump_symtab,
319 }),
320 .elf => @panic("TODO elf parser"),
321 .coff => @panic("TODO coff parser"),
322 .wasm => try WasmDumper.parseAndDump(contents, .{
323 .gpa = gpa,
324 .dump_symtab = self.dump_symtab,
325 }),
326 else => unreachable,
327 };
328
329 var vars = std.StringHashMap(u64).init(gpa);
330
331 for (self.checks.items) |chk| {
332 var it = mem.tokenize(u8, output, "\r\n");
333 for (chk.actions.items) |act| {
334 switch (act.tag) {
335 .match => {
336 while (it.next()) |line| {
337 if (try act.match(line, &vars)) break;
338 } else {
339 std.debug.print(
340 \\
341 \\========= Expected to find: ==========================
342 \\{s}
343 \\========= But parsed file does not contain it: =======
344 \\{s}
345 \\
346 , .{ act.phrase, output });
347 return error.TestFailed;
348 }
349 },
350 .not_present => {
351 while (it.next()) |line| {
352 if (try act.match(line, &vars)) {
353 std.debug.print(
354 \\
355 \\========= Expected not to find: ===================
356 \\{s}
357 \\========= But parsed file does contain it: ========
358 \\{s}
359 \\
360 , .{ act.phrase, output });
361 return error.TestFailed;
362 }
363 }
364 },
365 .compute_cmp => {
366 const res = act.computeCmp(gpa, vars) catch |err| switch (err) {
367 error.UnknownVariable => {
368 std.debug.print(
369 \\========= From parsed file: =====================
370 \\{s}
371 \\
372 , .{output});
373 return error.TestFailed;
374 },
375 else => |e| return e,
376 };
377 if (!res) {
378 std.debug.print(
379 \\
380 \\========= Comparison failed for action: ===========
381 \\{s} {}
382 \\========= From parsed file: =======================
383 \\{s}
384 \\
385 , .{ act.phrase, act.expected.?, output });
386 return error.TestFailed;
387 }
388 },
389 }
390 }
391 }
392}
393
394const Opts = struct {
395 gpa: ?Allocator = null,
396 dump_symtab: bool = false,
397};
398
399const MachODumper = struct {
400 const LoadCommandIterator = macho.LoadCommandIterator;
401 const symtab_label = "symtab";
402
403 fn parseAndDump(bytes: []align(@alignOf(u64)) const u8, opts: Opts) ![]const u8 {
404 const gpa = opts.gpa orelse unreachable; // MachO dumper requires an allocator
405 var stream = std.io.fixedBufferStream(bytes);
406 const reader = stream.reader();
407
408 const hdr = try reader.readStruct(macho.mach_header_64);
409 if (hdr.magic != macho.MH_MAGIC_64) {
410 return error.InvalidMagicNumber;
411 }
412
413 var output = std.ArrayList(u8).init(gpa);
414 const writer = output.writer();
415
416 var symtab: []const macho.nlist_64 = undefined;
417 var strtab: []const u8 = undefined;
418 var sections = std.ArrayList(macho.section_64).init(gpa);
419 var imports = std.ArrayList([]const u8).init(gpa);
420
421 var it = LoadCommandIterator{
422 .ncmds = hdr.ncmds,
423 .buffer = bytes[@sizeOf(macho.mach_header_64)..][0..hdr.sizeofcmds],
424 };
425 var i: usize = 0;
426 while (it.next()) |cmd| {
427 switch (cmd.cmd()) {
428 .SEGMENT_64 => {
429 const seg = cmd.cast(macho.segment_command_64).?;
430 try sections.ensureUnusedCapacity(seg.nsects);
431 for (cmd.getSections()) |sect| {
432 sections.appendAssumeCapacity(sect);
433 }
434 },
435 .SYMTAB => if (opts.dump_symtab) {
436 const lc = cmd.cast(macho.symtab_command).?;
437 symtab = @ptrCast(
438 [*]const macho.nlist_64,
439 @alignCast(@alignOf(macho.nlist_64), &bytes[lc.symoff]),
440 )[0..lc.nsyms];
441 strtab = bytes[lc.stroff..][0..lc.strsize];
442 },
443 .LOAD_DYLIB,
444 .LOAD_WEAK_DYLIB,
445 .REEXPORT_DYLIB,
446 => {
447 try imports.append(cmd.getDylibPathName());
448 },
449 else => {},
450 }
451
452 try dumpLoadCommand(cmd, i, writer);
453 try writer.writeByte('\n');
454
455 i += 1;
456 }
457
458 if (opts.dump_symtab) {
459 try writer.print("{s}\n", .{symtab_label});
460 for (symtab) |sym| {
461 if (sym.stab()) continue;
462 const sym_name = mem.sliceTo(@ptrCast([*:0]const u8, strtab.ptr + sym.n_strx), 0);
463 if (sym.sect()) {
464 const sect = sections.items[sym.n_sect - 1];
465 try writer.print("{x} ({s},{s})", .{
466 sym.n_value,
467 sect.segName(),
468 sect.sectName(),
469 });
470 if (sym.ext()) {
471 try writer.writeAll(" external");
472 }
473 try writer.print(" {s}\n", .{sym_name});
474 } else if (sym.undf()) {
475 const ordinal = @divTrunc(@bitCast(i16, sym.n_desc), macho.N_SYMBOL_RESOLVER);
476 const import_name = blk: {
477 if (ordinal <= 0) {
478 if (ordinal == macho.BIND_SPECIAL_DYLIB_SELF)
479 break :blk "self import";
480 if (ordinal == macho.BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE)
481 break :blk "main executable";
482 if (ordinal == macho.BIND_SPECIAL_DYLIB_FLAT_LOOKUP)
483 break :blk "flat lookup";
484 unreachable;
485 }
486 const full_path = imports.items[@bitCast(u16, ordinal) - 1];
487 const basename = fs.path.basename(full_path);
488 assert(basename.len > 0);
489 const ext = mem.lastIndexOfScalar(u8, basename, '.') orelse basename.len;
490 break :blk basename[0..ext];
491 };
492 try writer.writeAll("(undefined)");
493 if (sym.weakRef()) {
494 try writer.writeAll(" weak");
495 }
496 if (sym.ext()) {
497 try writer.writeAll(" external");
498 }
499 try writer.print(" {s} (from {s})\n", .{
500 sym_name,
501 import_name,
502 });
503 } else unreachable;
504 }
505 }
506
507 return output.toOwnedSlice();
508 }
509
510 fn dumpLoadCommand(lc: macho.LoadCommandIterator.LoadCommand, index: usize, writer: anytype) !void {
511 // print header first
512 try writer.print(
513 \\LC {d}
514 \\cmd {s}
515 \\cmdsize {d}
516 , .{ index, @tagName(lc.cmd()), lc.cmdsize() });
517
518 switch (lc.cmd()) {
519 .SEGMENT_64 => {
520 const seg = lc.cast(macho.segment_command_64).?;
521 try writer.writeByte('\n');
522 try writer.print(
523 \\segname {s}
524 \\vmaddr {x}
525 \\vmsize {x}
526 \\fileoff {x}
527 \\filesz {x}
528 , .{
529 seg.segName(),
530 seg.vmaddr,
531 seg.vmsize,
532 seg.fileoff,
533 seg.filesize,
534 });
535
536 for (lc.getSections()) |sect| {
537 try writer.writeByte('\n');
538 try writer.print(
539 \\sectname {s}
540 \\addr {x}
541 \\size {x}
542 \\offset {x}
543 \\align {x}
544 , .{
545 sect.sectName(),
546 sect.addr,
547 sect.size,
548 sect.offset,
549 sect.@"align",
550 });
551 }
552 },
553
554 .ID_DYLIB,
555 .LOAD_DYLIB,
556 .LOAD_WEAK_DYLIB,
557 .REEXPORT_DYLIB,
558 => {
559 const dylib = lc.cast(macho.dylib_command).?;
560 try writer.writeByte('\n');
561 try writer.print(
562 \\name {s}
563 \\timestamp {d}
564 \\current version {x}
565 \\compatibility version {x}
566 , .{
567 lc.getDylibPathName(),
568 dylib.dylib.timestamp,
569 dylib.dylib.current_version,
570 dylib.dylib.compatibility_version,
571 });
572 },
573
574 .MAIN => {
575 const main = lc.cast(macho.entry_point_command).?;
576 try writer.writeByte('\n');
577 try writer.print(
578 \\entryoff {x}
579 \\stacksize {x}
580 , .{ main.entryoff, main.stacksize });
581 },
582
583 .RPATH => {
584 try writer.writeByte('\n');
585 try writer.print(
586 \\path {s}
587 , .{
588 lc.getRpathPathName(),
589 });
590 },
591
592 .UUID => {
593 const uuid = lc.cast(macho.uuid_command).?;
594 try writer.writeByte('\n');
595 try writer.print("uuid {x}", .{std.fmt.fmtSliceHexLower(&uuid.uuid)});
596 },
597
598 .DATA_IN_CODE,
599 .FUNCTION_STARTS,
600 .CODE_SIGNATURE,
601 => {
602 const llc = lc.cast(macho.linkedit_data_command).?;
603 try writer.writeByte('\n');
604 try writer.print(
605 \\dataoff {x}
606 \\datasize {x}
607 , .{ llc.dataoff, llc.datasize });
608 },
609
610 .DYLD_INFO_ONLY => {
611 const dlc = lc.cast(macho.dyld_info_command).?;
612 try writer.writeByte('\n');
613 try writer.print(
614 \\rebaseoff {x}
615 \\rebasesize {x}
616 \\bindoff {x}
617 \\bindsize {x}
618 \\weakbindoff {x}
619 \\weakbindsize {x}
620 \\lazybindoff {x}
621 \\lazybindsize {x}
622 \\exportoff {x}
623 \\exportsize {x}
624 , .{
625 dlc.rebase_off,
626 dlc.rebase_size,
627 dlc.bind_off,
628 dlc.bind_size,
629 dlc.weak_bind_off,
630 dlc.weak_bind_size,
631 dlc.lazy_bind_off,
632 dlc.lazy_bind_size,
633 dlc.export_off,
634 dlc.export_size,
635 });
636 },
637
638 .SYMTAB => {
639 const slc = lc.cast(macho.symtab_command).?;
640 try writer.writeByte('\n');
641 try writer.print(
642 \\symoff {x}
643 \\nsyms {x}
644 \\stroff {x}
645 \\strsize {x}
646 , .{
647 slc.symoff,
648 slc.nsyms,
649 slc.stroff,
650 slc.strsize,
651 });
652 },
653
654 .DYSYMTAB => {
655 const dlc = lc.cast(macho.dysymtab_command).?;
656 try writer.writeByte('\n');
657 try writer.print(
658 \\ilocalsym {x}
659 \\nlocalsym {x}
660 \\iextdefsym {x}
661 \\nextdefsym {x}
662 \\iundefsym {x}
663 \\nundefsym {x}
664 \\indirectsymoff {x}
665 \\nindirectsyms {x}
666 , .{
667 dlc.ilocalsym,
668 dlc.nlocalsym,
669 dlc.iextdefsym,
670 dlc.nextdefsym,
671 dlc.iundefsym,
672 dlc.nundefsym,
673 dlc.indirectsymoff,
674 dlc.nindirectsyms,
675 });
676 },
677
678 else => {},
679 }
680 }
681};
682
683const WasmDumper = struct {
684 const symtab_label = "symbols";
685
686 fn parseAndDump(bytes: []const u8, opts: Opts) ![]const u8 {
687 const gpa = opts.gpa orelse unreachable; // Wasm dumper requires an allocator
688 if (opts.dump_symtab) {
689 @panic("TODO: Implement symbol table parsing and dumping");
690 }
691
692 var fbs = std.io.fixedBufferStream(bytes);
693 const reader = fbs.reader();
694
695 const buf = try reader.readBytesNoEof(8);
696 if (!mem.eql(u8, buf[0..4], &std.wasm.magic)) {
697 return error.InvalidMagicByte;
698 }
699 if (!mem.eql(u8, buf[4..], &std.wasm.version)) {
700 return error.UnsupportedWasmVersion;
701 }
702
703 var output = std.ArrayList(u8).init(gpa);
704 errdefer output.deinit();
705 const writer = output.writer();
706
707 while (reader.readByte()) |current_byte| {
708 const section = std.meta.intToEnum(std.wasm.Section, current_byte) catch |err| {
709 std.debug.print("Found invalid section id '{d}'\n", .{current_byte});
710 return err;
711 };
712
713 const section_length = try std.leb.readULEB128(u32, reader);
714 try parseAndDumpSection(section, bytes[fbs.pos..][0..section_length], writer);
715 fbs.pos += section_length;
716 } else |_| {} // reached end of stream
717
718 return output.toOwnedSlice();
719 }
720
721 fn parseAndDumpSection(section: std.wasm.Section, data: []const u8, writer: anytype) !void {
722 var fbs = std.io.fixedBufferStream(data);
723 const reader = fbs.reader();
724
725 try writer.print(
726 \\Section {s}
727 \\size {d}
728 , .{ @tagName(section), data.len });
729
730 switch (section) {
731 .type,
732 .import,
733 .function,
734 .table,
735 .memory,
736 .global,
737 .@"export",
738 .element,
739 .code,
740 .data,
741 => {
742 const entries = try std.leb.readULEB128(u32, reader);
743 try writer.print("\nentries {d}\n", .{entries});
744 try dumpSection(section, data[fbs.pos..], entries, writer);
745 },
746 .custom => {
747 const name_length = try std.leb.readULEB128(u32, reader);
748 const name = data[fbs.pos..][0..name_length];
749 fbs.pos += name_length;
750 try writer.print("\nname {s}\n", .{name});
751
752 if (mem.eql(u8, name, "name")) {
753 try parseDumpNames(reader, writer, data);
754 } else if (mem.eql(u8, name, "producers")) {
755 try parseDumpProducers(reader, writer, data);
756 } else if (mem.eql(u8, name, "target_features")) {
757 try parseDumpFeatures(reader, writer, data);
758 }
759 // TODO: Implement parsing and dumping other custom sections (such as relocations)
760 },
761 .start => {
762 const start = try std.leb.readULEB128(u32, reader);
763 try writer.print("\nstart {d}\n", .{start});
764 },
765 else => {}, // skip unknown sections
766 }
767 }
768
769 fn dumpSection(section: std.wasm.Section, data: []const u8, entries: u32, writer: anytype) !void {
770 var fbs = std.io.fixedBufferStream(data);
771 const reader = fbs.reader();
772
773 switch (section) {
774 .type => {
775 var i: u32 = 0;
776 while (i < entries) : (i += 1) {
777 const func_type = try reader.readByte();
778 if (func_type != std.wasm.function_type) {
779 std.debug.print("Expected function type, found byte '{d}'\n", .{func_type});
780 return error.UnexpectedByte;
781 }
782 const params = try std.leb.readULEB128(u32, reader);
783 try writer.print("params {d}\n", .{params});
784 var index: u32 = 0;
785 while (index < params) : (index += 1) {
786 try parseDumpType(std.wasm.Valtype, reader, writer);
787 } else index = 0;
788 const returns = try std.leb.readULEB128(u32, reader);
789 try writer.print("returns {d}\n", .{returns});
790 while (index < returns) : (index += 1) {
791 try parseDumpType(std.wasm.Valtype, reader, writer);
792 }
793 }
794 },
795 .import => {
796 var i: u32 = 0;
797 while (i < entries) : (i += 1) {
798 const module_name_len = try std.leb.readULEB128(u32, reader);
799 const module_name = data[fbs.pos..][0..module_name_len];
800 fbs.pos += module_name_len;
801 const name_len = try std.leb.readULEB128(u32, reader);
802 const name = data[fbs.pos..][0..name_len];
803 fbs.pos += name_len;
804
805 const kind = std.meta.intToEnum(std.wasm.ExternalKind, try reader.readByte()) catch |err| {
806 std.debug.print("Invalid import kind\n", .{});
807 return err;
808 };
809
810 try writer.print(
811 \\module {s}
812 \\name {s}
813 \\kind {s}
814 , .{ module_name, name, @tagName(kind) });
815 try writer.writeByte('\n');
816 switch (kind) {
817 .function => {
818 try writer.print("index {d}\n", .{try std.leb.readULEB128(u32, reader)});
819 },
820 .memory => {
821 try parseDumpLimits(reader, writer);
822 },
823 .global => {
824 try parseDumpType(std.wasm.Valtype, reader, writer);
825 try writer.print("mutable {}\n", .{0x01 == try std.leb.readULEB128(u32, reader)});
826 },
827 .table => {
828 try parseDumpType(std.wasm.RefType, reader, writer);
829 try parseDumpLimits(reader, writer);
830 },
831 }
832 }
833 },
834 .function => {
835 var i: u32 = 0;
836 while (i < entries) : (i += 1) {
837 try writer.print("index {d}\n", .{try std.leb.readULEB128(u32, reader)});
838 }
839 },
840 .table => {
841 var i: u32 = 0;
842 while (i < entries) : (i += 1) {
843 try parseDumpType(std.wasm.RefType, reader, writer);
844 try parseDumpLimits(reader, writer);
845 }
846 },
847 .memory => {
848 var i: u32 = 0;
849 while (i < entries) : (i += 1) {
850 try parseDumpLimits(reader, writer);
851 }
852 },
853 .global => {
854 var i: u32 = 0;
855 while (i < entries) : (i += 1) {
856 try parseDumpType(std.wasm.Valtype, reader, writer);
857 try writer.print("mutable {}\n", .{0x01 == try std.leb.readULEB128(u1, reader)});
858 try parseDumpInit(reader, writer);
859 }
860 },
861 .@"export" => {
862 var i: u32 = 0;
863 while (i < entries) : (i += 1) {
864 const name_len = try std.leb.readULEB128(u32, reader);
865 const name = data[fbs.pos..][0..name_len];
866 fbs.pos += name_len;
867 const kind_byte = try std.leb.readULEB128(u8, reader);
868 const kind = std.meta.intToEnum(std.wasm.ExternalKind, kind_byte) catch |err| {
869 std.debug.print("invalid export kind value '{d}'\n", .{kind_byte});
870 return err;
871 };
872 const index = try std.leb.readULEB128(u32, reader);
873 try writer.print(
874 \\name {s}
875 \\kind {s}
876 \\index {d}
877 , .{ name, @tagName(kind), index });
878 try writer.writeByte('\n');
879 }
880 },
881 .element => {
882 var i: u32 = 0;
883 while (i < entries) : (i += 1) {
884 try writer.print("table index {d}\n", .{try std.leb.readULEB128(u32, reader)});
885 try parseDumpInit(reader, writer);
886
887 const function_indexes = try std.leb.readULEB128(u32, reader);
888 var function_index: u32 = 0;
889 try writer.print("indexes {d}\n", .{function_indexes});
890 while (function_index < function_indexes) : (function_index += 1) {
891 try writer.print("index {d}\n", .{try std.leb.readULEB128(u32, reader)});
892 }
893 }
894 },
895 .code => {}, // code section is considered opaque to linker
896 .data => {
897 var i: u32 = 0;
898 while (i < entries) : (i += 1) {
899 const index = try std.leb.readULEB128(u32, reader);
900 try writer.print("memory index 0x{x}\n", .{index});
901 try parseDumpInit(reader, writer);
902 const size = try std.leb.readULEB128(u32, reader);
903 try writer.print("size {d}\n", .{size});
904 try reader.skipBytes(size, .{}); // we do not care about the content of the segments
905 }
906 },
907 else => unreachable,
908 }
909 }
910
911 fn parseDumpType(comptime WasmType: type, reader: anytype, writer: anytype) !void {
912 const type_byte = try reader.readByte();
913 const valtype = std.meta.intToEnum(WasmType, type_byte) catch |err| {
914 std.debug.print("Invalid wasm type value '{d}'\n", .{type_byte});
915 return err;
916 };
917 try writer.print("type {s}\n", .{@tagName(valtype)});
918 }
919
920 fn parseDumpLimits(reader: anytype, writer: anytype) !void {
921 const flags = try std.leb.readULEB128(u8, reader);
922 const min = try std.leb.readULEB128(u32, reader);
923
924 try writer.print("min {x}\n", .{min});
925 if (flags != 0) {
926 try writer.print("max {x}\n", .{try std.leb.readULEB128(u32, reader)});
927 }
928 }
929
930 fn parseDumpInit(reader: anytype, writer: anytype) !void {
931 const byte = try std.leb.readULEB128(u8, reader);
932 const opcode = std.meta.intToEnum(std.wasm.Opcode, byte) catch |err| {
933 std.debug.print("invalid wasm opcode '{d}'\n", .{byte});
934 return err;
935 };
936 switch (opcode) {
937 .i32_const => try writer.print("i32.const {x}\n", .{try std.leb.readILEB128(i32, reader)}),
938 .i64_const => try writer.print("i64.const {x}\n", .{try std.leb.readILEB128(i64, reader)}),
939 .f32_const => try writer.print("f32.const {x}\n", .{@bitCast(f32, try reader.readIntLittle(u32))}),
940 .f64_const => try writer.print("f64.const {x}\n", .{@bitCast(f64, try reader.readIntLittle(u64))}),
941 .global_get => try writer.print("global.get {x}\n", .{try std.leb.readULEB128(u32, reader)}),
942 else => unreachable,
943 }
944 const end_opcode = try std.leb.readULEB128(u8, reader);
945 if (end_opcode != std.wasm.opcode(.end)) {
946 std.debug.print("expected 'end' opcode in init expression\n", .{});
947 return error.MissingEndOpcode;
948 }
949 }
950
951 fn parseDumpNames(reader: anytype, writer: anytype, data: []const u8) !void {
952 while (reader.context.pos < data.len) {
953 try parseDumpType(std.wasm.NameSubsection, reader, writer);
954 const size = try std.leb.readULEB128(u32, reader);
955 const entries = try std.leb.readULEB128(u32, reader);
956 try writer.print(
957 \\size {d}
958 \\names {d}
959 , .{ size, entries });
960 try writer.writeByte('\n');
961 var i: u32 = 0;
962 while (i < entries) : (i += 1) {
963 const index = try std.leb.readULEB128(u32, reader);
964 const name_len = try std.leb.readULEB128(u32, reader);
965 const pos = reader.context.pos;
966 const name = data[pos..][0..name_len];
967 reader.context.pos += name_len;
968
969 try writer.print(
970 \\index {d}
971 \\name {s}
972 , .{ index, name });
973 try writer.writeByte('\n');
974 }
975 }
976 }
977
978 fn parseDumpProducers(reader: anytype, writer: anytype, data: []const u8) !void {
979 const field_count = try std.leb.readULEB128(u32, reader);
980 try writer.print("fields {d}\n", .{field_count});
981 var current_field: u32 = 0;
982 while (current_field < field_count) : (current_field += 1) {
983 const field_name_length = try std.leb.readULEB128(u32, reader);
984 const field_name = data[reader.context.pos..][0..field_name_length];
985 reader.context.pos += field_name_length;
986
987 const value_count = try std.leb.readULEB128(u32, reader);
988 try writer.print(
989 \\field_name {s}
990 \\values {d}
991 , .{ field_name, value_count });
992 try writer.writeByte('\n');
993 var current_value: u32 = 0;
994 while (current_value < value_count) : (current_value += 1) {
995 const value_length = try std.leb.readULEB128(u32, reader);
996 const value = data[reader.context.pos..][0..value_length];
997 reader.context.pos += value_length;
998
999 const version_length = try std.leb.readULEB128(u32, reader);
1000 const version = data[reader.context.pos..][0..version_length];
1001 reader.context.pos += version_length;
1002
1003 try writer.print(
1004 \\value_name {s}
1005 \\version {s}
1006 , .{ value, version });
1007 try writer.writeByte('\n');
1008 }
1009 }
1010 }
1011
1012 fn parseDumpFeatures(reader: anytype, writer: anytype, data: []const u8) !void {
1013 const feature_count = try std.leb.readULEB128(u32, reader);
1014 try writer.print("features {d}\n", .{feature_count});
1015
1016 var index: u32 = 0;
1017 while (index < feature_count) : (index += 1) {
1018 const prefix_byte = try std.leb.readULEB128(u8, reader);
1019 const name_length = try std.leb.readULEB128(u32, reader);
1020 const feature_name = data[reader.context.pos..][0..name_length];
1021 reader.context.pos += name_length;
1022
1023 try writer.print("{c} {s}\n", .{ prefix_byte, feature_name });
1024 }
1025 }
1026};
lib/std/build/ConfigHeaderStep.zig deleted-288
...@@ -1,288 +0,0 @@
1const std = @import("../std.zig");
2const ConfigHeaderStep = @This();
3const Step = std.build.Step;
4const Builder = std.build.Builder;
5
6pub const base_id: Step.Id = .config_header;
7
8pub const Style = enum {
9 /// The configure format supported by autotools. It uses `#undef foo` to
10 /// mark lines that can be substituted with different values.
11 autoconf,
12 /// The configure format supported by CMake. It uses `@@FOO@@` and
13 /// `#cmakedefine` for template substitution.
14 cmake,
15};
16
17pub const Value = union(enum) {
18 undef,
19 defined,
20 boolean: bool,
21 int: i64,
22 ident: []const u8,
23 string: []const u8,
24};
25
26step: Step,
27builder: *Builder,
28source: std.build.FileSource,
29style: Style,
30values: std.StringHashMap(Value),
31max_bytes: usize = 2 * 1024 * 1024,
32output_dir: []const u8,
33output_basename: []const u8,
34
35pub fn create(builder: *Builder, source: std.build.FileSource, style: Style) *ConfigHeaderStep {
36 const self = builder.allocator.create(ConfigHeaderStep) catch @panic("OOM");
37 const name = builder.fmt("configure header {s}", .{source.getDisplayName()});
38 self.* = .{
39 .builder = builder,
40 .step = Step.init(base_id, name, builder.allocator, make),
41 .source = source,
42 .style = style,
43 .values = std.StringHashMap(Value).init(builder.allocator),
44 .output_dir = undefined,
45 .output_basename = "config.h",
46 };
47 switch (source) {
48 .path => |p| {
49 const basename = std.fs.path.basename(p);
50 if (std.mem.endsWith(u8, basename, ".h.in")) {
51 self.output_basename = basename[0 .. basename.len - 3];
52 }
53 },
54 else => {},
55 }
56 return self;
57}
58
59pub fn addValues(self: *ConfigHeaderStep, values: anytype) void {
60 return addValuesInner(self, values) catch @panic("OOM");
61}
62
63fn addValuesInner(self: *ConfigHeaderStep, values: anytype) !void {
64 inline for (@typeInfo(@TypeOf(values)).Struct.fields) |field| {
65 switch (@typeInfo(field.type)) {
66 .Null => {
67 try self.values.put(field.name, .undef);
68 },
69 .Void => {
70 try self.values.put(field.name, .defined);
71 },
72 .Bool => {
73 try self.values.put(field.name, .{ .boolean = @field(values, field.name) });
74 },
75 .ComptimeInt => {
76 try self.values.put(field.name, .{ .int = @field(values, field.name) });
77 },
78 .EnumLiteral => {
79 try self.values.put(field.name, .{ .ident = @tagName(@field(values, field.name)) });
80 },
81 .Pointer => |ptr| {
82 switch (@typeInfo(ptr.child)) {
83 .Array => |array| {
84 if (ptr.size == .One and array.child == u8) {
85 try self.values.put(field.name, .{ .string = @field(values, field.name) });
86 continue;
87 }
88 },
89 else => {},
90 }
91
92 @compileError("unsupported ConfigHeaderStep value type: " ++
93 @typeName(field.type));
94 },
95 else => @compileError("unsupported ConfigHeaderStep value type: " ++
96 @typeName(field.type)),
97 }
98 }
99}
100
101fn make(step: *Step) !void {
102 const self = @fieldParentPtr(ConfigHeaderStep, "step", step);
103 const gpa = self.builder.allocator;
104 const src_path = self.source.getPath(self.builder);
105 const contents = try std.fs.cwd().readFileAlloc(gpa, src_path, self.max_bytes);
106
107 // The cache is used here not really as a way to speed things up - because writing
108 // the data to a file would probably be very fast - but as a way to find a canonical
109 // location to put build artifacts.
110
111 // If, for example, a hard-coded path was used as the location to put ConfigHeaderStep
112 // files, then two ConfigHeaderStep executing in parallel might clobber each other.
113
114 // TODO port the cache system from the compiler to zig std lib. Until then
115 // we construct the path directly, and no "cache hit" detection happens;
116 // the files are always written.
117 // Note there is very similar code over in WriteFileStep
118 const Hasher = std.crypto.auth.siphash.SipHash128(1, 3);
119 // Random bytes to make ConfigHeaderStep unique. Refresh this with new
120 // random bytes when ConfigHeaderStep implementation is modified in a
121 // non-backwards-compatible way.
122 var hash = Hasher.init("X1pQzdDt91Zlh7Eh");
123 hash.update(self.source.getDisplayName());
124 hash.update(contents);
125
126 var digest: [16]u8 = undefined;
127 hash.final(&digest);
128 var hash_basename: [digest.len * 2]u8 = undefined;
129 _ = std.fmt.bufPrint(
130 &hash_basename,
131 "{s}",
132 .{std.fmt.fmtSliceHexLower(&digest)},
133 ) catch unreachable;
134
135 self.output_dir = try std.fs.path.join(gpa, &[_][]const u8{
136 self.builder.cache_root, "o", &hash_basename,
137 });
138 var dir = std.fs.cwd().makeOpenPath(self.output_dir, .{}) catch |err| {
139 std.debug.print("unable to make path {s}: {s}\n", .{ self.output_dir, @errorName(err) });
140 return err;
141 };
142 defer dir.close();
143
144 var values_copy = try self.values.clone();
145 defer values_copy.deinit();
146
147 var output = std.ArrayList(u8).init(gpa);
148 defer output.deinit();
149 try output.ensureTotalCapacity(contents.len);
150
151 try output.appendSlice("/* This file was generated by ConfigHeaderStep using the Zig Build System. */\n");
152
153 switch (self.style) {
154 .autoconf => try render_autoconf(contents, &output, &values_copy, src_path),
155 .cmake => try render_cmake(contents, &output, &values_copy, src_path),
156 }
157
158 try dir.writeFile(self.output_basename, output.items);
159}
160
161fn render_autoconf(
162 contents: []const u8,
163 output: *std.ArrayList(u8),
164 values_copy: *std.StringHashMap(Value),
165 src_path: []const u8,
166) !void {
167 var any_errors = false;
168 var line_index: u32 = 0;
169 var line_it = std.mem.split(u8, contents, "\n");
170 while (line_it.next()) |line| : (line_index += 1) {
171 if (!std.mem.startsWith(u8, line, "#")) {
172 try output.appendSlice(line);
173 try output.appendSlice("\n");
174 continue;
175 }
176 var it = std.mem.tokenize(u8, line[1..], " \t\r");
177 const undef = it.next().?;
178 if (!std.mem.eql(u8, undef, "undef")) {
179 try output.appendSlice(line);
180 try output.appendSlice("\n");
181 continue;
182 }
183 const name = it.rest();
184 const kv = values_copy.fetchRemove(name) orelse {
185 std.debug.print("{s}:{d}: error: unspecified config header value: '{s}'\n", .{
186 src_path, line_index + 1, name,
187 });
188 any_errors = true;
189 continue;
190 };
191 try renderValue(output, name, kv.value);
192 }
193
194 {
195 var it = values_copy.iterator();
196 while (it.next()) |entry| {
197 const name = entry.key_ptr.*;
198 std.debug.print("{s}: error: config header value unused: '{s}'\n", .{ src_path, name });
199 }
200 }
201
202 if (any_errors) {
203 return error.HeaderConfigFailed;
204 }
205}
206
207fn render_cmake(
208 contents: []const u8,
209 output: *std.ArrayList(u8),
210 values_copy: *std.StringHashMap(Value),
211 src_path: []const u8,
212) !void {
213 var any_errors = false;
214 var line_index: u32 = 0;
215 var line_it = std.mem.split(u8, contents, "\n");
216 while (line_it.next()) |line| : (line_index += 1) {
217 if (!std.mem.startsWith(u8, line, "#")) {
218 try output.appendSlice(line);
219 try output.appendSlice("\n");
220 continue;
221 }
222 var it = std.mem.tokenize(u8, line[1..], " \t\r");
223 const cmakedefine = it.next().?;
224 if (!std.mem.eql(u8, cmakedefine, "cmakedefine")) {
225 try output.appendSlice(line);
226 try output.appendSlice("\n");
227 continue;
228 }
229 const name = it.next() orelse {
230 std.debug.print("{s}:{d}: error: missing define name\n", .{
231 src_path, line_index + 1,
232 });
233 any_errors = true;
234 continue;
235 };
236 const kv = values_copy.fetchRemove(name) orelse {
237 std.debug.print("{s}:{d}: error: unspecified config header value: '{s}'\n", .{
238 src_path, line_index + 1, name,
239 });
240 any_errors = true;
241 continue;
242 };
243 try renderValue(output, name, kv.value);
244 }
245
246 {
247 var it = values_copy.iterator();
248 while (it.next()) |entry| {
249 const name = entry.key_ptr.*;
250 std.debug.print("{s}: error: config header value unused: '{s}'\n", .{ src_path, name });
251 }
252 }
253
254 if (any_errors) {
255 return error.HeaderConfigFailed;
256 }
257}
258
259fn renderValue(output: *std.ArrayList(u8), name: []const u8, value: Value) !void {
260 switch (value) {
261 .undef => {
262 try output.appendSlice("/* #undef ");
263 try output.appendSlice(name);
264 try output.appendSlice(" */\n");
265 },
266 .defined => {
267 try output.appendSlice("#define ");
268 try output.appendSlice(name);
269 try output.appendSlice("\n");
270 },
271 .boolean => |b| {
272 try output.appendSlice("#define ");
273 try output.appendSlice(name);
274 try output.appendSlice(" ");
275 try output.appendSlice(if (b) "true\n" else "false\n");
276 },
277 .int => |i| {
278 try output.writer().print("#define {s} {d}\n", .{ name, i });
279 },
280 .ident => |ident| {
281 try output.writer().print("#define {s} {s}\n", .{ name, ident });
282 },
283 .string => |string| {
284 // TODO: use C-specific escaping instead of zig string literals
285 try output.writer().print("#define {s} \"{}\"\n", .{ name, std.zig.fmtEscapes(string) });
286 },
287 }
288}
lib/std/build/EmulatableRunStep.zig deleted-215
...@@ -1,215 +0,0 @@
1//! Unlike `RunStep` this step will provide emulation, when enabled, to run foreign binaries.
2//! When a binary is foreign, but emulation for the target is disabled, the specified binary
3//! will not be run and therefore also not validated against its output.
4//! This step can be useful when wishing to run a built binary on multiple platforms,
5//! without having to verify if it's possible to be ran against.
6
7const std = @import("../std.zig");
8const build = std.build;
9const Step = std.build.Step;
10const Builder = std.build.Builder;
11const LibExeObjStep = std.build.LibExeObjStep;
12const RunStep = std.build.RunStep;
13
14const fs = std.fs;
15const process = std.process;
16const EnvMap = process.EnvMap;
17
18const EmulatableRunStep = @This();
19
20pub const base_id = .emulatable_run;
21
22const max_stdout_size = 1 * 1024 * 1024; // 1 MiB
23
24step: Step,
25builder: *Builder,
26
27/// The artifact (executable) to be run by this step
28exe: *LibExeObjStep,
29
30/// Set this to `null` to ignore the exit code for the purpose of determining a successful execution
31expected_exit_code: ?u8 = 0,
32
33/// Override this field to modify the environment
34env_map: ?*EnvMap,
35
36/// Set this to modify the current working directory
37cwd: ?[]const u8,
38
39stdout_action: RunStep.StdIoAction = .inherit,
40stderr_action: RunStep.StdIoAction = .inherit,
41
42/// When set to true, hides the warning of skipping a foreign binary which cannot be run on the host
43/// or through emulation.
44hide_foreign_binaries_warning: bool,
45
46/// Creates a step that will execute the given artifact. This step will allow running the
47/// binary through emulation when any of the emulation options such as `enable_rosetta` are set to true.
48/// When set to false, and the binary is foreign, running the executable is skipped.
49/// Asserts given artifact is an executable.
50pub fn create(builder: *Builder, name: []const u8, artifact: *LibExeObjStep) *EmulatableRunStep {
51 std.debug.assert(artifact.kind == .exe or artifact.kind == .test_exe);
52 const self = builder.allocator.create(EmulatableRunStep) catch unreachable;
53
54 const option_name = "hide-foreign-warnings";
55 const hide_warnings = if (builder.available_options_map.get(option_name) == null) warn: {
56 break :warn builder.option(bool, option_name, "Hide the warning when a foreign binary which is incompatible is skipped") orelse false;
57 } else false;
58
59 self.* = .{
60 .builder = builder,
61 .step = Step.init(.emulatable_run, name, builder.allocator, make),
62 .exe = artifact,
63 .env_map = null,
64 .cwd = null,
65 .hide_foreign_binaries_warning = hide_warnings,
66 };
67 self.step.dependOn(&artifact.step);
68
69 return self;
70}
71
72fn make(step: *Step) !void {
73 const self = @fieldParentPtr(EmulatableRunStep, "step", step);
74 const host_info = self.builder.host;
75
76 var argv_list = std.ArrayList([]const u8).init(self.builder.allocator);
77 defer argv_list.deinit();
78
79 const need_cross_glibc = self.exe.target.isGnuLibC() and self.exe.is_linking_libc;
80 switch (host_info.getExternalExecutor(self.exe.target_info, .{
81 .qemu_fixes_dl = need_cross_glibc and self.builder.glibc_runtimes_dir != null,
82 .link_libc = self.exe.is_linking_libc,
83 })) {
84 .native => {},
85 .rosetta => if (!self.builder.enable_rosetta) return warnAboutForeignBinaries(self),
86 .wine => |bin_name| if (self.builder.enable_wine) {
87 try argv_list.append(bin_name);
88 } else return,
89 .qemu => |bin_name| if (self.builder.enable_qemu) {
90 const glibc_dir_arg = if (need_cross_glibc)
91 self.builder.glibc_runtimes_dir orelse return
92 else
93 null;
94 try argv_list.append(bin_name);
95 if (glibc_dir_arg) |dir| {
96 // TODO look into making this a call to `linuxTriple`. This
97 // needs the directory to be called "i686" rather than
98 // "x86" which is why we do it manually here.
99 const fmt_str = "{s}" ++ fs.path.sep_str ++ "{s}-{s}-{s}";
100 const cpu_arch = self.exe.target.getCpuArch();
101 const os_tag = self.exe.target.getOsTag();
102 const abi = self.exe.target.getAbi();
103 const cpu_arch_name: []const u8 = if (cpu_arch == .x86)
104 "i686"
105 else
106 @tagName(cpu_arch);
107 const full_dir = try std.fmt.allocPrint(self.builder.allocator, fmt_str, .{
108 dir, cpu_arch_name, @tagName(os_tag), @tagName(abi),
109 });
110
111 try argv_list.append("-L");
112 try argv_list.append(full_dir);
113 }
114 } else return warnAboutForeignBinaries(self),
115 .darling => |bin_name| if (self.builder.enable_darling) {
116 try argv_list.append(bin_name);
117 } else return warnAboutForeignBinaries(self),
118 .wasmtime => |bin_name| if (self.builder.enable_wasmtime) {
119 try argv_list.append(bin_name);
120 try argv_list.append("--dir=.");
121 } else return warnAboutForeignBinaries(self),
122 else => return warnAboutForeignBinaries(self),
123 }
124
125 if (self.exe.target.isWindows()) {
126 // On Windows we don't have rpaths so we have to add .dll search paths to PATH
127 RunStep.addPathForDynLibsInternal(&self.step, self.builder, self.exe);
128 }
129
130 const executable_path = self.exe.installed_path orelse self.exe.getOutputSource().getPath(self.builder);
131 try argv_list.append(executable_path);
132
133 try RunStep.runCommand(
134 argv_list.items,
135 self.builder,
136 self.expected_exit_code,
137 self.stdout_action,
138 self.stderr_action,
139 .Inherit,
140 self.env_map,
141 self.cwd,
142 false,
143 );
144}
145
146pub fn expectStdErrEqual(self: *EmulatableRunStep, bytes: []const u8) void {
147 self.stderr_action = .{ .expect_exact = self.builder.dupe(bytes) };
148}
149
150pub fn expectStdOutEqual(self: *EmulatableRunStep, bytes: []const u8) void {
151 self.stdout_action = .{ .expect_exact = self.builder.dupe(bytes) };
152}
153
154fn warnAboutForeignBinaries(step: *EmulatableRunStep) void {
155 if (step.hide_foreign_binaries_warning) return;
156 const builder = step.builder;
157 const artifact = step.exe;
158
159 const host_name = builder.host.target.zigTriple(builder.allocator) catch unreachable;
160 const foreign_name = artifact.target.zigTriple(builder.allocator) catch unreachable;
161 const target_info = std.zig.system.NativeTargetInfo.detect(artifact.target) catch unreachable;
162 const need_cross_glibc = artifact.target.isGnuLibC() and artifact.is_linking_libc;
163 switch (builder.host.getExternalExecutor(target_info, .{
164 .qemu_fixes_dl = need_cross_glibc and builder.glibc_runtimes_dir != null,
165 .link_libc = artifact.is_linking_libc,
166 })) {
167 .native => unreachable,
168 .bad_dl => |foreign_dl| {
169 const host_dl = builder.host.dynamic_linker.get() orelse "(none)";
170 std.debug.print("the host system does not appear to be capable of executing binaries from the target because the host dynamic linker is '{s}', while the target dynamic linker is '{s}'. Consider setting the dynamic linker as '{s}'.\n", .{
171 host_dl, foreign_dl, host_dl,
172 });
173 },
174 .bad_os_or_cpu => {
175 std.debug.print("the host system ({s}) does not appear to be capable of executing binaries from the target ({s}).\n", .{
176 host_name, foreign_name,
177 });
178 },
179 .darling => if (!builder.enable_darling) {
180 std.debug.print(
181 "the host system ({s}) does not appear to be capable of executing binaries " ++
182 "from the target ({s}). Consider enabling darling.\n",
183 .{ host_name, foreign_name },
184 );
185 },
186 .rosetta => if (!builder.enable_rosetta) {
187 std.debug.print(
188 "the host system ({s}) does not appear to be capable of executing binaries " ++
189 "from the target ({s}). Consider enabling rosetta.\n",
190 .{ host_name, foreign_name },
191 );
192 },
193 .wine => if (!builder.enable_wine) {
194 std.debug.print(
195 "the host system ({s}) does not appear to be capable of executing binaries " ++
196 "from the target ({s}). Consider enabling wine.\n",
197 .{ host_name, foreign_name },
198 );
199 },
200 .qemu => if (!builder.enable_qemu) {
201 std.debug.print(
202 "the host system ({s}) does not appear to be capable of executing binaries " ++
203 "from the target ({s}). Consider enabling qemu.\n",
204 .{ host_name, foreign_name },
205 );
206 },
207 .wasmtime => {
208 std.debug.print(
209 "the host system ({s}) does not appear to be capable of executing binaries " ++
210 "from the target ({s}). Consider enabling wasmtime.\n",
211 .{ host_name, foreign_name },
212 );
213 },
214 }
215}
lib/std/build/FmtStep.zig deleted-37
...@@ -1,37 +0,0 @@
1const std = @import("../std.zig");
2const build = @import("../build.zig");
3const Step = build.Step;
4const Builder = build.Builder;
5const BufMap = std.BufMap;
6const mem = std.mem;
7
8const FmtStep = @This();
9
10pub const base_id = .fmt;
11
12step: Step,
13builder: *Builder,
14argv: [][]const u8,
15
16pub fn create(builder: *Builder, paths: []const []const u8) *FmtStep {
17 const self = builder.allocator.create(FmtStep) catch unreachable;
18 const name = "zig fmt";
19 self.* = FmtStep{
20 .step = Step.init(.fmt, name, builder.allocator, make),
21 .builder = builder,
22 .argv = builder.allocator.alloc([]u8, paths.len + 2) catch unreachable,
23 };
24
25 self.argv[0] = builder.zig_exe;
26 self.argv[1] = "fmt";
27 for (paths) |path, i| {
28 self.argv[2 + i] = builder.pathFromRoot(path);
29 }
30 return self;
31}
32
33fn make(step: *Step) !void {
34 const self = @fieldParentPtr(FmtStep, "step", step);
35
36 return self.builder.spawnChild(self.argv);
37}
lib/std/build/InstallArtifactStep.zig deleted-88
...@@ -1,88 +0,0 @@
1const std = @import("../std.zig");
2const build = @import("../build.zig");
3const Step = build.Step;
4const Builder = build.Builder;
5const LibExeObjStep = std.build.LibExeObjStep;
6const InstallDir = std.build.InstallDir;
7
8pub const base_id = .install_artifact;
9
10step: Step,
11builder: *Builder,
12artifact: *LibExeObjStep,
13dest_dir: InstallDir,
14pdb_dir: ?InstallDir,
15h_dir: ?InstallDir,
16
17const Self = @This();
18
19pub fn create(builder: *Builder, artifact: *LibExeObjStep) *Self {
20 if (artifact.install_step) |s| return s;
21
22 const self = builder.allocator.create(Self) catch unreachable;
23 self.* = Self{
24 .builder = builder,
25 .step = Step.init(.install_artifact, builder.fmt("install {s}", .{artifact.step.name}), builder.allocator, make),
26 .artifact = artifact,
27 .dest_dir = artifact.override_dest_dir orelse switch (artifact.kind) {
28 .obj => @panic("Cannot install a .obj build artifact."),
29 .@"test" => @panic("Cannot install a test build artifact, use addTestExe instead."),
30 .exe, .test_exe => InstallDir{ .bin = {} },
31 .lib => InstallDir{ .lib = {} },
32 },
33 .pdb_dir = if (artifact.producesPdbFile()) blk: {
34 if (artifact.kind == .exe or artifact.kind == .test_exe) {
35 break :blk InstallDir{ .bin = {} };
36 } else {
37 break :blk InstallDir{ .lib = {} };
38 }
39 } else null,
40 .h_dir = if (artifact.kind == .lib and artifact.emit_h) .header else null,
41 };
42 self.step.dependOn(&artifact.step);
43 artifact.install_step = self;
44
45 builder.pushInstalledFile(self.dest_dir, artifact.out_filename);
46 if (self.artifact.isDynamicLibrary()) {
47 if (artifact.major_only_filename) |name| {
48 builder.pushInstalledFile(.lib, name);
49 }
50 if (artifact.name_only_filename) |name| {
51 builder.pushInstalledFile(.lib, name);
52 }
53 if (self.artifact.target.isWindows()) {
54 builder.pushInstalledFile(.lib, artifact.out_lib_filename);
55 }
56 }
57 if (self.pdb_dir) |pdb_dir| {
58 builder.pushInstalledFile(pdb_dir, artifact.out_pdb_filename);
59 }
60 if (self.h_dir) |h_dir| {
61 builder.pushInstalledFile(h_dir, artifact.out_h_filename);
62 }
63 return self;
64}
65
66fn make(step: *Step) !void {
67 const self = @fieldParentPtr(Self, "step", step);
68 const builder = self.builder;
69
70 const full_dest_path = builder.getInstallPath(self.dest_dir, self.artifact.out_filename);
71 try builder.updateFile(self.artifact.getOutputSource().getPath(builder), full_dest_path);
72 if (self.artifact.isDynamicLibrary() and self.artifact.version != null and self.artifact.target.wantSharedLibSymLinks()) {
73 try LibExeObjStep.doAtomicSymLinks(builder.allocator, full_dest_path, self.artifact.major_only_filename.?, self.artifact.name_only_filename.?);
74 }
75 if (self.artifact.isDynamicLibrary() and self.artifact.target.isWindows() and self.artifact.emit_implib != .no_emit) {
76 const full_implib_path = builder.getInstallPath(self.dest_dir, self.artifact.out_lib_filename);
77 try builder.updateFile(self.artifact.getOutputLibSource().getPath(builder), full_implib_path);
78 }
79 if (self.pdb_dir) |pdb_dir| {
80 const full_pdb_path = builder.getInstallPath(pdb_dir, self.artifact.out_pdb_filename);
81 try builder.updateFile(self.artifact.getOutputPdbSource().getPath(builder), full_pdb_path);
82 }
83 if (self.h_dir) |h_dir| {
84 const full_h_path = builder.getInstallPath(h_dir, self.artifact.out_h_filename);
85 try builder.updateFile(self.artifact.getOutputHSource().getPath(builder), full_h_path);
86 }
87 self.artifact.installed_path = full_dest_path;
88}
lib/std/build/InstallDirStep.zig deleted-95
...@@ -1,95 +0,0 @@
1const std = @import("../std.zig");
2const mem = std.mem;
3const fs = std.fs;
4const build = @import("../build.zig");
5const Step = build.Step;
6const Builder = build.Builder;
7const InstallDir = std.build.InstallDir;
8const InstallDirStep = @This();
9const log = std.log;
10
11step: Step,
12builder: *Builder,
13options: Options,
14/// This is used by the build system when a file being installed comes from one
15/// package but is being installed by another.
16override_source_builder: ?*Builder = null,
17
18pub const base_id = .install_dir;
19
20pub const Options = struct {
21 source_dir: []const u8,
22 install_dir: InstallDir,
23 install_subdir: []const u8,
24 /// File paths which end in any of these suffixes will be excluded
25 /// from being installed.
26 exclude_extensions: []const []const u8 = &.{},
27 /// File paths which end in any of these suffixes will result in
28 /// empty files being installed. This is mainly intended for large
29 /// test.zig files in order to prevent needless installation bloat.
30 /// However if the files were not present at all, then
31 /// `@import("test.zig")` would be a compile error.
32 blank_extensions: []const []const u8 = &.{},
33
34 fn dupe(self: Options, b: *Builder) Options {
35 return .{
36 .source_dir = b.dupe(self.source_dir),
37 .install_dir = self.install_dir.dupe(b),
38 .install_subdir = b.dupe(self.install_subdir),
39 .exclude_extensions = b.dupeStrings(self.exclude_extensions),
40 .blank_extensions = b.dupeStrings(self.blank_extensions),
41 };
42 }
43};
44
45pub fn init(
46 builder: *Builder,
47 options: Options,
48) InstallDirStep {
49 builder.pushInstalledFile(options.install_dir, options.install_subdir);
50 return InstallDirStep{
51 .builder = builder,
52 .step = Step.init(.install_dir, builder.fmt("install {s}/", .{options.source_dir}), builder.allocator, make),
53 .options = options.dupe(builder),
54 };
55}
56
57fn make(step: *Step) !void {
58 const self = @fieldParentPtr(InstallDirStep, "step", step);
59 const dest_prefix = self.builder.getInstallPath(self.options.install_dir, self.options.install_subdir);
60 const src_builder = self.override_source_builder orelse self.builder;
61 const full_src_dir = src_builder.pathFromRoot(self.options.source_dir);
62 var src_dir = std.fs.cwd().openIterableDir(full_src_dir, .{}) catch |err| {
63 log.err("InstallDirStep: unable to open source directory '{s}': {s}", .{
64 full_src_dir, @errorName(err),
65 });
66 return error.StepFailed;
67 };
68 defer src_dir.close();
69 var it = try src_dir.walk(self.builder.allocator);
70 next_entry: while (try it.next()) |entry| {
71 for (self.options.exclude_extensions) |ext| {
72 if (mem.endsWith(u8, entry.path, ext)) {
73 continue :next_entry;
74 }
75 }
76
77 const full_path = self.builder.pathJoin(&.{ full_src_dir, entry.path });
78 const dest_path = self.builder.pathJoin(&.{ dest_prefix, entry.path });
79
80 switch (entry.kind) {
81 .Directory => try fs.cwd().makePath(dest_path),
82 .File => {
83 for (self.options.blank_extensions) |ext| {
84 if (mem.endsWith(u8, entry.path, ext)) {
85 try self.builder.truncateFile(dest_path);
86 continue :next_entry;
87 }
88 }
89
90 try self.builder.updateFile(full_path, dest_path);
91 },
92 else => continue,
93 }
94 }
95}
lib/std/build/InstallFileStep.zig deleted-42
...@@ -1,42 +0,0 @@
1const std = @import("../std.zig");
2const build = @import("../build.zig");
3const Step = build.Step;
4const Builder = build.Builder;
5const FileSource = std.build.FileSource;
6const InstallDir = std.build.InstallDir;
7const InstallFileStep = @This();
8
9pub const base_id = .install_file;
10
11step: Step,
12builder: *Builder,
13source: FileSource,
14dir: InstallDir,
15dest_rel_path: []const u8,
16/// This is used by the build system when a file being installed comes from one
17/// package but is being installed by another.
18override_source_builder: ?*Builder = null,
19
20pub fn init(
21 builder: *Builder,
22 source: FileSource,
23 dir: InstallDir,
24 dest_rel_path: []const u8,
25) InstallFileStep {
26 builder.pushInstalledFile(dir, dest_rel_path);
27 return InstallFileStep{
28 .builder = builder,
29 .step = Step.init(.install_file, builder.fmt("install {s} to {s}", .{ source.getDisplayName(), dest_rel_path }), builder.allocator, make),
30 .source = source.dupe(builder),
31 .dir = dir.dupe(builder),
32 .dest_rel_path = builder.dupePath(dest_rel_path),
33 };
34}
35
36fn make(step: *Step) !void {
37 const self = @fieldParentPtr(InstallFileStep, "step", step);
38 const src_builder = self.override_source_builder orelse self.builder;
39 const full_src_path = self.source.getPath(src_builder);
40 const full_dest_path = self.builder.getInstallPath(self.dir, self.dest_rel_path);
41 try self.builder.updateFile(full_src_path, full_dest_path);
42}
lib/std/build/InstallRawStep.zig deleted-106
...@@ -1,106 +0,0 @@
1//! TODO: Rename this to ObjCopyStep now that it invokes the `zig objcopy`
2//! subcommand rather than containing an implementation directly.
3
4const std = @import("std");
5const InstallRawStep = @This();
6
7const Allocator = std.mem.Allocator;
8const ArenaAllocator = std.heap.ArenaAllocator;
9const ArrayListUnmanaged = std.ArrayListUnmanaged;
10const Builder = std.build.Builder;
11const File = std.fs.File;
12const InstallDir = std.build.InstallDir;
13const LibExeObjStep = std.build.LibExeObjStep;
14const Step = std.build.Step;
15const elf = std.elf;
16const fs = std.fs;
17const io = std.io;
18const sort = std.sort;
19
20pub const base_id = .install_raw;
21
22pub const RawFormat = enum {
23 bin,
24 hex,
25};
26
27step: Step,
28builder: *Builder,
29artifact: *LibExeObjStep,
30dest_dir: InstallDir,
31dest_filename: []const u8,
32options: CreateOptions,
33output_file: std.build.GeneratedFile,
34
35pub const CreateOptions = struct {
36 format: ?RawFormat = null,
37 dest_dir: ?InstallDir = null,
38 only_section: ?[]const u8 = null,
39 pad_to: ?u64 = null,
40};
41
42pub fn create(builder: *Builder, artifact: *LibExeObjStep, dest_filename: []const u8, options: CreateOptions) *InstallRawStep {
43 const self = builder.allocator.create(InstallRawStep) catch unreachable;
44 self.* = InstallRawStep{
45 .step = Step.init(.install_raw, builder.fmt("install raw binary {s}", .{artifact.step.name}), builder.allocator, make),
46 .builder = builder,
47 .artifact = artifact,
48 .dest_dir = if (options.dest_dir) |d| d else switch (artifact.kind) {
49 .obj => unreachable,
50 .@"test" => unreachable,
51 .exe, .test_exe => .bin,
52 .lib => unreachable,
53 },
54 .dest_filename = dest_filename,
55 .options = options,
56 .output_file = std.build.GeneratedFile{ .step = &self.step },
57 };
58 self.step.dependOn(&artifact.step);
59
60 builder.pushInstalledFile(self.dest_dir, dest_filename);
61 return self;
62}
63
64pub fn getOutputSource(self: *const InstallRawStep) std.build.FileSource {
65 return std.build.FileSource{ .generated = &self.output_file };
66}
67
68fn make(step: *Step) !void {
69 const self = @fieldParentPtr(InstallRawStep, "step", step);
70 const b = self.builder;
71
72 if (self.artifact.target.getObjectFormat() != .elf) {
73 std.debug.print("InstallRawStep only works with ELF format.\n", .{});
74 return error.InvalidObjectFormat;
75 }
76
77 const full_src_path = self.artifact.getOutputSource().getPath(b);
78 const full_dest_path = b.getInstallPath(self.dest_dir, self.dest_filename);
79 self.output_file.path = full_dest_path;
80
81 fs.cwd().makePath(b.getInstallPath(self.dest_dir, "")) catch unreachable;
82
83 var argv_list = std.ArrayList([]const u8).init(b.allocator);
84 try argv_list.appendSlice(&.{ b.zig_exe, "objcopy" });
85
86 if (self.options.only_section) |only_section| {
87 try argv_list.appendSlice(&.{ "-j", only_section });
88 }
89 if (self.options.pad_to) |pad_to| {
90 try argv_list.appendSlice(&.{
91 "--pad-to",
92 b.fmt("{d}", .{pad_to}),
93 });
94 }
95 if (self.options.format) |format| switch (format) {
96 .bin => try argv_list.appendSlice(&.{ "-O", "binary" }),
97 .hex => try argv_list.appendSlice(&.{ "-O", "hex" }),
98 };
99
100 try argv_list.appendSlice(&.{ full_src_path, full_dest_path });
101 _ = try self.builder.execFromStep(argv_list.items, &self.step);
102}
103
104test {
105 std.testing.refAllDecls(InstallRawStep);
106}
lib/std/build/LibExeObjStep.zig deleted-2111
...@@ -1,2111 +0,0 @@
1const builtin = @import("builtin");
2const std = @import("../std.zig");
3const mem = std.mem;
4const log = std.log;
5const fs = std.fs;
6const assert = std.debug.assert;
7const panic = std.debug.panic;
8const ArrayList = std.ArrayList;
9const StringHashMap = std.StringHashMap;
10const Sha256 = std.crypto.hash.sha2.Sha256;
11const Allocator = mem.Allocator;
12const build = @import("../build.zig");
13const Step = build.Step;
14const Builder = build.Builder;
15const CrossTarget = std.zig.CrossTarget;
16const NativeTargetInfo = std.zig.system.NativeTargetInfo;
17const FileSource = std.build.FileSource;
18const PkgConfigPkg = Builder.PkgConfigPkg;
19const PkgConfigError = Builder.PkgConfigError;
20const ExecError = Builder.ExecError;
21const Pkg = std.build.Pkg;
22const VcpkgRoot = std.build.VcpkgRoot;
23const InstallDir = std.build.InstallDir;
24const InstallArtifactStep = std.build.InstallArtifactStep;
25const GeneratedFile = std.build.GeneratedFile;
26const InstallRawStep = std.build.InstallRawStep;
27const EmulatableRunStep = std.build.EmulatableRunStep;
28const CheckObjectStep = std.build.CheckObjectStep;
29const RunStep = std.build.RunStep;
30const OptionsStep = std.build.OptionsStep;
31const ConfigHeaderStep = std.build.ConfigHeaderStep;
32const LibExeObjStep = @This();
33
34pub const base_id = .lib_exe_obj;
35
36step: Step,
37builder: *Builder,
38name: []const u8,
39target: CrossTarget = CrossTarget{},
40target_info: NativeTargetInfo,
41linker_script: ?FileSource = null,
42version_script: ?[]const u8 = null,
43out_filename: []const u8,
44linkage: ?Linkage = null,
45version: ?std.builtin.Version,
46build_mode: std.builtin.Mode,
47kind: Kind,
48major_only_filename: ?[]const u8,
49name_only_filename: ?[]const u8,
50strip: ?bool,
51unwind_tables: ?bool,
52// keep in sync with src/link.zig:CompressDebugSections
53compress_debug_sections: enum { none, zlib } = .none,
54lib_paths: ArrayList([]const u8),
55rpaths: ArrayList([]const u8),
56framework_dirs: ArrayList([]const u8),
57frameworks: StringHashMap(FrameworkLinkInfo),
58verbose_link: bool,
59verbose_cc: bool,
60emit_analysis: EmitOption = .default,
61emit_asm: EmitOption = .default,
62emit_bin: EmitOption = .default,
63emit_docs: EmitOption = .default,
64emit_implib: EmitOption = .default,
65emit_llvm_bc: EmitOption = .default,
66emit_llvm_ir: EmitOption = .default,
67// Lots of things depend on emit_h having a consistent path,
68// so it is not an EmitOption for now.
69emit_h: bool = false,
70bundle_compiler_rt: ?bool = null,
71single_threaded: ?bool = null,
72stack_protector: ?bool = null,
73disable_stack_probing: bool,
74disable_sanitize_c: bool,
75sanitize_thread: bool,
76rdynamic: bool,
77import_memory: bool = false,
78/// For WebAssembly targets, this will allow for undefined symbols to
79/// be imported from the host environment.
80import_symbols: bool = false,
81import_table: bool = false,
82export_table: bool = false,
83initial_memory: ?u64 = null,
84max_memory: ?u64 = null,
85shared_memory: bool = false,
86global_base: ?u64 = null,
87c_std: Builder.CStd,
88override_lib_dir: ?[]const u8,
89main_pkg_path: ?[]const u8,
90exec_cmd_args: ?[]const ?[]const u8,
91name_prefix: []const u8,
92filter: ?[]const u8,
93test_evented_io: bool = false,
94test_runner: ?[]const u8,
95code_model: std.builtin.CodeModel = .default,
96wasi_exec_model: ?std.builtin.WasiExecModel = null,
97/// Symbols to be exported when compiling to wasm
98export_symbol_names: []const []const u8 = &.{},
99
100root_src: ?FileSource,
101out_h_filename: []const u8,
102out_lib_filename: []const u8,
103out_pdb_filename: []const u8,
104packages: ArrayList(Pkg),
105
106object_src: []const u8,
107
108link_objects: ArrayList(LinkObject),
109include_dirs: ArrayList(IncludeDir),
110c_macros: ArrayList([]const u8),
111installed_headers: ArrayList(*std.build.Step),
112output_dir: ?[]const u8,
113is_linking_libc: bool = false,
114is_linking_libcpp: bool = false,
115vcpkg_bin_path: ?[]const u8 = null,
116
117/// This may be set in order to override the default install directory
118override_dest_dir: ?InstallDir,
119installed_path: ?[]const u8,
120install_step: ?*InstallArtifactStep,
121
122/// Base address for an executable image.
123image_base: ?u64 = null,
124
125libc_file: ?FileSource = null,
126
127valgrind_support: ?bool = null,
128each_lib_rpath: ?bool = null,
129/// On ELF targets, this will emit a link section called ".note.gnu.build-id"
130/// which can be used to coordinate a stripped binary with its debug symbols.
131/// As an example, the bloaty project refuses to work unless its inputs have
132/// build ids, in order to prevent accidental mismatches.
133/// The default is to not include this section because it slows down linking.
134build_id: ?bool = null,
135
136/// Create a .eh_frame_hdr section and a PT_GNU_EH_FRAME segment in the ELF
137/// file.
138link_eh_frame_hdr: bool = false,
139link_emit_relocs: bool = false,
140
141/// Place every function in its own section so that unused ones may be
142/// safely garbage-collected during the linking phase.
143link_function_sections: bool = false,
144
145/// Remove functions and data that are unreachable by the entry point or
146/// exported symbols.
147link_gc_sections: ?bool = null,
148
149linker_allow_shlib_undefined: ?bool = null,
150
151/// Permit read-only relocations in read-only segments. Disallowed by default.
152link_z_notext: bool = false,
153
154/// Force all relocations to be read-only after processing.
155link_z_relro: bool = true,
156
157/// Allow relocations to be lazily processed after load.
158link_z_lazy: bool = false,
159
160/// Common page size
161link_z_common_page_size: ?u64 = null,
162
163/// Maximum page size
164link_z_max_page_size: ?u64 = null,
165
166/// (Darwin) Install name for the dylib
167install_name: ?[]const u8 = null,
168
169/// (Darwin) Path to entitlements file
170entitlements: ?[]const u8 = null,
171
172/// (Darwin) Size of the pagezero segment.
173pagezero_size: ?u64 = null,
174
175/// (Darwin) Search strategy for searching system libraries. Either `paths_first` or `dylibs_first`.
176/// The former lowers to `-search_paths_first` linker option, while the latter to `-search_dylibs_first`
177/// option.
178/// By default, if no option is specified, the linker assumes `paths_first` as the default
179/// search strategy.
180search_strategy: ?enum { paths_first, dylibs_first } = null,
181
182/// (Darwin) Set size of the padding between the end of load commands
183/// and start of `__TEXT,__text` section.
184headerpad_size: ?u32 = null,
185
186/// (Darwin) Automatically Set size of the padding between the end of load commands
187/// and start of `__TEXT,__text` section to a value fitting all paths expanded to MAXPATHLEN.
188headerpad_max_install_names: bool = false,
189
190/// (Darwin) Remove dylibs that are unreachable by the entry point or exported symbols.
191dead_strip_dylibs: bool = false,
192
193/// Position Independent Code
194force_pic: ?bool = null,
195
196/// Position Independent Executable
197pie: ?bool = null,
198
199red_zone: ?bool = null,
200
201omit_frame_pointer: ?bool = null,
202dll_export_fns: ?bool = null,
203
204subsystem: ?std.Target.SubSystem = null,
205
206entry_symbol_name: ?[]const u8 = null,
207
208/// Overrides the default stack size
209stack_size: ?u64 = null,
210
211want_lto: ?bool = null,
212use_llvm: ?bool = null,
213use_lld: ?bool = null,
214
215output_path_source: GeneratedFile,
216output_lib_path_source: GeneratedFile,
217output_h_path_source: GeneratedFile,
218output_pdb_path_source: GeneratedFile,
219
220pub const CSourceFiles = struct {
221 files: []const []const u8,
222 flags: []const []const u8,
223};
224
225pub const CSourceFile = struct {
226 source: FileSource,
227 args: []const []const u8,
228
229 pub fn dupe(self: CSourceFile, b: *Builder) CSourceFile {
230 return .{
231 .source = self.source.dupe(b),
232 .args = b.dupeStrings(self.args),
233 };
234 }
235};
236
237pub const LinkObject = union(enum) {
238 static_path: FileSource,
239 other_step: *LibExeObjStep,
240 system_lib: SystemLib,
241 assembly_file: FileSource,
242 c_source_file: *CSourceFile,
243 c_source_files: *CSourceFiles,
244};
245
246pub const SystemLib = struct {
247 name: []const u8,
248 needed: bool,
249 weak: bool,
250 use_pkg_config: enum {
251 /// Don't use pkg-config, just pass -lfoo where foo is name.
252 no,
253 /// Try to get information on how to link the library from pkg-config.
254 /// If that fails, fall back to passing -lfoo where foo is name.
255 yes,
256 /// Try to get information on how to link the library from pkg-config.
257 /// If that fails, error out.
258 force,
259 },
260};
261
262const FrameworkLinkInfo = struct {
263 needed: bool = false,
264 weak: bool = false,
265};
266
267pub const IncludeDir = union(enum) {
268 raw_path: []const u8,
269 raw_path_system: []const u8,
270 other_step: *LibExeObjStep,
271 config_header_step: *ConfigHeaderStep,
272};
273
274pub const Kind = enum {
275 exe,
276 lib,
277 obj,
278 @"test",
279 test_exe,
280};
281
282pub const SharedLibKind = union(enum) {
283 versioned: std.builtin.Version,
284 unversioned: void,
285};
286
287pub const Linkage = enum { dynamic, static };
288
289pub const EmitOption = union(enum) {
290 default: void,
291 no_emit: void,
292 emit: void,
293 emit_to: []const u8,
294
295 fn getArg(self: @This(), b: *Builder, arg_name: []const u8) ?[]const u8 {
296 return switch (self) {
297 .no_emit => b.fmt("-fno-{s}", .{arg_name}),
298 .default => null,
299 .emit => b.fmt("-f{s}", .{arg_name}),
300 .emit_to => |path| b.fmt("-f{s}={s}", .{ arg_name, path }),
301 };
302 }
303};
304
305pub fn createSharedLibrary(builder: *Builder, name: []const u8, root_src: ?FileSource, kind: SharedLibKind) *LibExeObjStep {
306 return initExtraArgs(builder, name, root_src, .lib, .dynamic, switch (kind) {
307 .versioned => |ver| ver,
308 .unversioned => null,
309 });
310}
311
312pub fn createStaticLibrary(builder: *Builder, name: []const u8, root_src: ?FileSource) *LibExeObjStep {
313 return initExtraArgs(builder, name, root_src, .lib, .static, null);
314}
315
316pub fn createObject(builder: *Builder, name: []const u8, root_src: ?FileSource) *LibExeObjStep {
317 return initExtraArgs(builder, name, root_src, .obj, null, null);
318}
319
320pub fn createExecutable(builder: *Builder, name: []const u8, root_src: ?FileSource) *LibExeObjStep {
321 return initExtraArgs(builder, name, root_src, .exe, null, null);
322}
323
324pub fn createTest(builder: *Builder, name: []const u8, root_src: FileSource) *LibExeObjStep {
325 return initExtraArgs(builder, name, root_src, .@"test", null, null);
326}
327
328pub fn createTestExe(builder: *Builder, name: []const u8, root_src: FileSource) *LibExeObjStep {
329 return initExtraArgs(builder, name, root_src, .test_exe, null, null);
330}
331
332fn initExtraArgs(
333 builder: *Builder,
334 name_raw: []const u8,
335 root_src_raw: ?FileSource,
336 kind: Kind,
337 linkage: ?Linkage,
338 ver: ?std.builtin.Version,
339) *LibExeObjStep {
340 const name = builder.dupe(name_raw);
341 const root_src: ?FileSource = if (root_src_raw) |rsrc| rsrc.dupe(builder) else null;
342 if (mem.indexOf(u8, name, "/") != null or mem.indexOf(u8, name, "\\") != null) {
343 panic("invalid name: '{s}'. It looks like a file path, but it is supposed to be the library or application name.", .{name});
344 }
345
346 const self = builder.allocator.create(LibExeObjStep) catch unreachable;
347 self.* = LibExeObjStep{
348 .strip = null,
349 .unwind_tables = null,
350 .builder = builder,
351 .verbose_link = false,
352 .verbose_cc = false,
353 .build_mode = std.builtin.Mode.Debug,
354 .linkage = linkage,
355 .kind = kind,
356 .root_src = root_src,
357 .name = name,
358 .frameworks = StringHashMap(FrameworkLinkInfo).init(builder.allocator),
359 .step = Step.init(base_id, name, builder.allocator, make),
360 .version = ver,
361 .out_filename = undefined,
362 .out_h_filename = builder.fmt("{s}.h", .{name}),
363 .out_lib_filename = undefined,
364 .out_pdb_filename = builder.fmt("{s}.pdb", .{name}),
365 .major_only_filename = null,
366 .name_only_filename = null,
367 .packages = ArrayList(Pkg).init(builder.allocator),
368 .include_dirs = ArrayList(IncludeDir).init(builder.allocator),
369 .link_objects = ArrayList(LinkObject).init(builder.allocator),
370 .c_macros = ArrayList([]const u8).init(builder.allocator),
371 .lib_paths = ArrayList([]const u8).init(builder.allocator),
372 .rpaths = ArrayList([]const u8).init(builder.allocator),
373 .framework_dirs = ArrayList([]const u8).init(builder.allocator),
374 .installed_headers = ArrayList(*std.build.Step).init(builder.allocator),
375 .object_src = undefined,
376 .c_std = Builder.CStd.C99,
377 .override_lib_dir = null,
378 .main_pkg_path = null,
379 .exec_cmd_args = null,
380 .name_prefix = "",
381 .filter = null,
382 .test_runner = null,
383 .disable_stack_probing = false,
384 .disable_sanitize_c = false,
385 .sanitize_thread = false,
386 .rdynamic = false,
387 .output_dir = null,
388 .override_dest_dir = null,
389 .installed_path = null,
390 .install_step = null,
391
392 .output_path_source = GeneratedFile{ .step = &self.step },
393 .output_lib_path_source = GeneratedFile{ .step = &self.step },
394 .output_h_path_source = GeneratedFile{ .step = &self.step },
395 .output_pdb_path_source = GeneratedFile{ .step = &self.step },
396
397 .target_info = undefined, // populated in computeOutFileNames
398 };
399 self.computeOutFileNames();
400 if (root_src) |rs| rs.addStepDependencies(&self.step);
401 return self;
402}
403
404fn computeOutFileNames(self: *LibExeObjStep) void {
405 self.target_info = NativeTargetInfo.detect(self.target) catch
406 unreachable;
407
408 const target = self.target_info.target;
409
410 self.out_filename = std.zig.binNameAlloc(self.builder.allocator, .{
411 .root_name = self.name,
412 .target = target,
413 .output_mode = switch (self.kind) {
414 .lib => .Lib,
415 .obj => .Obj,
416 .exe, .@"test", .test_exe => .Exe,
417 },
418 .link_mode = if (self.linkage) |some| @as(std.builtin.LinkMode, switch (some) {
419 .dynamic => .Dynamic,
420 .static => .Static,
421 }) else null,
422 .version = self.version,
423 }) catch unreachable;
424
425 if (self.kind == .lib) {
426 if (self.linkage != null and self.linkage.? == .static) {
427 self.out_lib_filename = self.out_filename;
428 } else if (self.version) |version| {
429 if (target.isDarwin()) {
430 self.major_only_filename = self.builder.fmt("lib{s}.{d}.dylib", .{
431 self.name,
432 version.major,
433 });
434 self.name_only_filename = self.builder.fmt("lib{s}.dylib", .{self.name});
435 self.out_lib_filename = self.out_filename;
436 } else if (target.os.tag == .windows) {
437 self.out_lib_filename = self.builder.fmt("{s}.lib", .{self.name});
438 } else {
439 self.major_only_filename = self.builder.fmt("lib{s}.so.{d}", .{ self.name, version.major });
440 self.name_only_filename = self.builder.fmt("lib{s}.so", .{self.name});
441 self.out_lib_filename = self.out_filename;
442 }
443 } else {
444 if (target.isDarwin()) {
445 self.out_lib_filename = self.out_filename;
446 } else if (target.os.tag == .windows) {
447 self.out_lib_filename = self.builder.fmt("{s}.lib", .{self.name});
448 } else {
449 self.out_lib_filename = self.out_filename;
450 }
451 }
452 if (self.output_dir != null) {
453 self.output_lib_path_source.path = self.builder.pathJoin(
454 &.{ self.output_dir.?, self.out_lib_filename },
455 );
456 }
457 }
458}
459
460pub fn setTarget(self: *LibExeObjStep, target: CrossTarget) void {
461 self.target = target;
462 self.computeOutFileNames();
463}
464
465pub fn setOutputDir(self: *LibExeObjStep, dir: []const u8) void {
466 self.output_dir = self.builder.dupePath(dir);
467}
468
469pub fn install(self: *LibExeObjStep) void {
470 self.builder.installArtifact(self);
471}
472
473pub fn installRaw(self: *LibExeObjStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep {
474 return self.builder.installRaw(self, dest_filename, options);
475}
476
477pub fn installHeader(a: *LibExeObjStep, src_path: []const u8, dest_rel_path: []const u8) void {
478 const install_file = a.builder.addInstallHeaderFile(src_path, dest_rel_path);
479 a.builder.getInstallStep().dependOn(&install_file.step);
480 a.installed_headers.append(&install_file.step) catch unreachable;
481}
482
483pub fn installHeadersDirectory(
484 a: *LibExeObjStep,
485 src_dir_path: []const u8,
486 dest_rel_path: []const u8,
487) void {
488 return installHeadersDirectoryOptions(a, .{
489 .source_dir = src_dir_path,
490 .install_dir = .header,
491 .install_subdir = dest_rel_path,
492 });
493}
494
495pub fn installHeadersDirectoryOptions(
496 a: *LibExeObjStep,
497 options: std.build.InstallDirStep.Options,
498) void {
499 const install_dir = a.builder.addInstallDirectory(options);
500 a.builder.getInstallStep().dependOn(&install_dir.step);
501 a.installed_headers.append(&install_dir.step) catch unreachable;
502}
503
504pub fn installLibraryHeaders(a: *LibExeObjStep, l: *LibExeObjStep) void {
505 assert(l.kind == .lib);
506 const install_step = a.builder.getInstallStep();
507 // Copy each element from installed_headers, modifying the builder
508 // to be the new parent's builder.
509 for (l.installed_headers.items) |step| {
510 const step_copy = switch (step.id) {
511 inline .install_file, .install_dir => |id| blk: {
512 const T = id.Type();
513 const ptr = a.builder.allocator.create(T) catch unreachable;
514 ptr.* = step.cast(T).?.*;
515 ptr.override_source_builder = ptr.builder;
516 ptr.builder = a.builder;
517 break :blk &ptr.step;
518 },
519 else => unreachable,
520 };
521 a.installed_headers.append(step_copy) catch unreachable;
522 install_step.dependOn(step_copy);
523 }
524 a.installed_headers.appendSlice(l.installed_headers.items) catch unreachable;
525}
526
527/// Creates a `RunStep` with an executable built with `addExecutable`.
528/// Add command line arguments with `addArg`.
529pub fn run(exe: *LibExeObjStep) *RunStep {
530 assert(exe.kind == .exe or exe.kind == .test_exe);
531
532 // It doesn't have to be native. We catch that if you actually try to run it.
533 // Consider that this is declarative; the run step may not be run unless a user
534 // option is supplied.
535 const run_step = RunStep.create(exe.builder, exe.builder.fmt("run {s}", .{exe.step.name}));
536 run_step.addArtifactArg(exe);
537
538 if (exe.kind == .test_exe) {
539 run_step.addArg(exe.builder.zig_exe);
540 }
541
542 if (exe.vcpkg_bin_path) |path| {
543 run_step.addPathDir(path);
544 }
545
546 return run_step;
547}
548
549/// Creates an `EmulatableRunStep` with an executable built with `addExecutable`.
550/// Allows running foreign binaries through emulation platforms such as Qemu or Rosetta.
551/// When a binary cannot be ran through emulation or the option is disabled, a warning
552/// will be printed and the binary will *NOT* be ran.
553pub fn runEmulatable(exe: *LibExeObjStep) *EmulatableRunStep {
554 assert(exe.kind == .exe or exe.kind == .test_exe);
555
556 const run_step = EmulatableRunStep.create(exe.builder, exe.builder.fmt("run {s}", .{exe.step.name}), exe);
557 if (exe.vcpkg_bin_path) |path| {
558 RunStep.addPathDirInternal(&run_step.step, exe.builder, path);
559 }
560 return run_step;
561}
562
563pub fn checkObject(self: *LibExeObjStep, obj_format: std.Target.ObjectFormat) *CheckObjectStep {
564 return CheckObjectStep.create(self.builder, self.getOutputSource(), obj_format);
565}
566
567pub fn setLinkerScriptPath(self: *LibExeObjStep, source: FileSource) void {
568 self.linker_script = source.dupe(self.builder);
569 source.addStepDependencies(&self.step);
570}
571
572pub fn linkFramework(self: *LibExeObjStep, framework_name: []const u8) void {
573 self.frameworks.put(self.builder.dupe(framework_name), .{}) catch unreachable;
574}
575
576pub fn linkFrameworkNeeded(self: *LibExeObjStep, framework_name: []const u8) void {
577 self.frameworks.put(self.builder.dupe(framework_name), .{
578 .needed = true,
579 }) catch unreachable;
580}
581
582pub fn linkFrameworkWeak(self: *LibExeObjStep, framework_name: []const u8) void {
583 self.frameworks.put(self.builder.dupe(framework_name), .{
584 .weak = true,
585 }) catch unreachable;
586}
587
588/// Returns whether the library, executable, or object depends on a particular system library.
589pub fn dependsOnSystemLibrary(self: LibExeObjStep, name: []const u8) bool {
590 if (isLibCLibrary(name)) {
591 return self.is_linking_libc;
592 }
593 if (isLibCppLibrary(name)) {
594 return self.is_linking_libcpp;
595 }
596 for (self.link_objects.items) |link_object| {
597 switch (link_object) {
598 .system_lib => |lib| if (mem.eql(u8, lib.name, name)) return true,
599 else => continue,
600 }
601 }
602 return false;
603}
604
605pub fn linkLibrary(self: *LibExeObjStep, lib: *LibExeObjStep) void {
606 assert(lib.kind == .lib);
607 self.linkLibraryOrObject(lib);
608}
609
610pub fn isDynamicLibrary(self: *LibExeObjStep) bool {
611 return self.kind == .lib and self.linkage == Linkage.dynamic;
612}
613
614pub fn isStaticLibrary(self: *LibExeObjStep) bool {
615 return self.kind == .lib and self.linkage != Linkage.dynamic;
616}
617
618pub fn producesPdbFile(self: *LibExeObjStep) bool {
619 if (!self.target.isWindows() and !self.target.isUefi()) return false;
620 if (self.target.getObjectFormat() == .c) return false;
621 if (self.strip == true) return false;
622 return self.isDynamicLibrary() or self.kind == .exe or self.kind == .test_exe;
623}
624
625pub fn linkLibC(self: *LibExeObjStep) void {
626 self.is_linking_libc = true;
627}
628
629pub fn linkLibCpp(self: *LibExeObjStep) void {
630 self.is_linking_libcpp = true;
631}
632
633/// If the value is omitted, it is set to 1.
634/// `name` and `value` need not live longer than the function call.
635pub fn defineCMacro(self: *LibExeObjStep, name: []const u8, value: ?[]const u8) void {
636 const macro = std.build.constructCMacro(self.builder.allocator, name, value);
637 self.c_macros.append(macro) catch unreachable;
638}
639
640/// name_and_value looks like [name]=[value]. If the value is omitted, it is set to 1.
641pub fn defineCMacroRaw(self: *LibExeObjStep, name_and_value: []const u8) void {
642 self.c_macros.append(self.builder.dupe(name_and_value)) catch unreachable;
643}
644
645/// This one has no integration with anything, it just puts -lname on the command line.
646/// Prefer to use `linkSystemLibrary` instead.
647pub fn linkSystemLibraryName(self: *LibExeObjStep, name: []const u8) void {
648 self.link_objects.append(.{
649 .system_lib = .{
650 .name = self.builder.dupe(name),
651 .needed = false,
652 .weak = false,
653 .use_pkg_config = .no,
654 },
655 }) catch unreachable;
656}
657
658/// This one has no integration with anything, it just puts -needed-lname on the command line.
659/// Prefer to use `linkSystemLibraryNeeded` instead.
660pub fn linkSystemLibraryNeededName(self: *LibExeObjStep, name: []const u8) void {
661 self.link_objects.append(.{
662 .system_lib = .{
663 .name = self.builder.dupe(name),
664 .needed = true,
665 .weak = false,
666 .use_pkg_config = .no,
667 },
668 }) catch unreachable;
669}
670
671/// Darwin-only. This one has no integration with anything, it just puts -weak-lname on the
672/// command line. Prefer to use `linkSystemLibraryWeak` instead.
673pub fn linkSystemLibraryWeakName(self: *LibExeObjStep, name: []const u8) void {
674 self.link_objects.append(.{
675 .system_lib = .{
676 .name = self.builder.dupe(name),
677 .needed = false,
678 .weak = true,
679 .use_pkg_config = .no,
680 },
681 }) catch unreachable;
682}
683
684/// This links against a system library, exclusively using pkg-config to find the library.
685/// Prefer to use `linkSystemLibrary` instead.
686pub fn linkSystemLibraryPkgConfigOnly(self: *LibExeObjStep, lib_name: []const u8) void {
687 self.link_objects.append(.{
688 .system_lib = .{
689 .name = self.builder.dupe(lib_name),
690 .needed = false,
691 .weak = false,
692 .use_pkg_config = .force,
693 },
694 }) catch unreachable;
695}
696
697/// This links against a system library, exclusively using pkg-config to find the library.
698/// Prefer to use `linkSystemLibraryNeeded` instead.
699pub fn linkSystemLibraryNeededPkgConfigOnly(self: *LibExeObjStep, lib_name: []const u8) void {
700 self.link_objects.append(.{
701 .system_lib = .{
702 .name = self.builder.dupe(lib_name),
703 .needed = true,
704 .weak = false,
705 .use_pkg_config = .force,
706 },
707 }) catch unreachable;
708}
709
710/// Run pkg-config for the given library name and parse the output, returning the arguments
711/// that should be passed to zig to link the given library.
712pub fn runPkgConfig(self: *LibExeObjStep, lib_name: []const u8) ![]const []const u8 {
713 const pkg_name = match: {
714 // First we have to map the library name to pkg config name. Unfortunately,
715 // there are several examples where this is not straightforward:
716 // -lSDL2 -> pkg-config sdl2
717 // -lgdk-3 -> pkg-config gdk-3.0
718 // -latk-1.0 -> pkg-config atk
719 const pkgs = try getPkgConfigList(self.builder);
720
721 // Exact match means instant winner.
722 for (pkgs) |pkg| {
723 if (mem.eql(u8, pkg.name, lib_name)) {
724 break :match pkg.name;
725 }
726 }
727
728 // Next we'll try ignoring case.
729 for (pkgs) |pkg| {
730 if (std.ascii.eqlIgnoreCase(pkg.name, lib_name)) {
731 break :match pkg.name;
732 }
733 }
734
735 // Now try appending ".0".
736 for (pkgs) |pkg| {
737 if (std.ascii.indexOfIgnoreCase(pkg.name, lib_name)) |pos| {
738 if (pos != 0) continue;
739 if (mem.eql(u8, pkg.name[lib_name.len..], ".0")) {
740 break :match pkg.name;
741 }
742 }
743 }
744
745 // Trimming "-1.0".
746 if (mem.endsWith(u8, lib_name, "-1.0")) {
747 const trimmed_lib_name = lib_name[0 .. lib_name.len - "-1.0".len];
748 for (pkgs) |pkg| {
749 if (std.ascii.eqlIgnoreCase(pkg.name, trimmed_lib_name)) {
750 break :match pkg.name;
751 }
752 }
753 }
754
755 return error.PackageNotFound;
756 };
757
758 var code: u8 = undefined;
759 const stdout = if (self.builder.execAllowFail(&[_][]const u8{
760 "pkg-config",
761 pkg_name,
762 "--cflags",
763 "--libs",
764 }, &code, .Ignore)) |stdout| stdout else |err| switch (err) {
765 error.ProcessTerminated => return error.PkgConfigCrashed,
766 error.ExecNotSupported => return error.PkgConfigFailed,
767 error.ExitCodeFailure => return error.PkgConfigFailed,
768 error.FileNotFound => return error.PkgConfigNotInstalled,
769 error.ChildExecFailed => return error.PkgConfigFailed,
770 else => return err,
771 };
772
773 var zig_args = ArrayList([]const u8).init(self.builder.allocator);
774 defer zig_args.deinit();
775
776 var it = mem.tokenize(u8, stdout, " \r\n\t");
777 while (it.next()) |tok| {
778 if (mem.eql(u8, tok, "-I")) {
779 const dir = it.next() orelse return error.PkgConfigInvalidOutput;
780 try zig_args.appendSlice(&[_][]const u8{ "-I", dir });
781 } else if (mem.startsWith(u8, tok, "-I")) {
782 try zig_args.append(tok);
783 } else if (mem.eql(u8, tok, "-L")) {
784 const dir = it.next() orelse return error.PkgConfigInvalidOutput;
785 try zig_args.appendSlice(&[_][]const u8{ "-L", dir });
786 } else if (mem.startsWith(u8, tok, "-L")) {
787 try zig_args.append(tok);
788 } else if (mem.eql(u8, tok, "-l")) {
789 const lib = it.next() orelse return error.PkgConfigInvalidOutput;
790 try zig_args.appendSlice(&[_][]const u8{ "-l", lib });
791 } else if (mem.startsWith(u8, tok, "-l")) {
792 try zig_args.append(tok);
793 } else if (mem.eql(u8, tok, "-D")) {
794 const macro = it.next() orelse return error.PkgConfigInvalidOutput;
795 try zig_args.appendSlice(&[_][]const u8{ "-D", macro });
796 } else if (mem.startsWith(u8, tok, "-D")) {
797 try zig_args.append(tok);
798 } else if (self.builder.verbose) {
799 log.warn("Ignoring pkg-config flag '{s}'", .{tok});
800 }
801 }
802
803 return zig_args.toOwnedSlice();
804}
805
806pub fn linkSystemLibrary(self: *LibExeObjStep, name: []const u8) void {
807 self.linkSystemLibraryInner(name, .{});
808}
809
810pub fn linkSystemLibraryNeeded(self: *LibExeObjStep, name: []const u8) void {
811 self.linkSystemLibraryInner(name, .{ .needed = true });
812}
813
814pub fn linkSystemLibraryWeak(self: *LibExeObjStep, name: []const u8) void {
815 self.linkSystemLibraryInner(name, .{ .weak = true });
816}
817
818fn linkSystemLibraryInner(self: *LibExeObjStep, name: []const u8, opts: struct {
819 needed: bool = false,
820 weak: bool = false,
821}) void {
822 if (isLibCLibrary(name)) {
823 self.linkLibC();
824 return;
825 }
826 if (isLibCppLibrary(name)) {
827 self.linkLibCpp();
828 return;
829 }
830
831 self.link_objects.append(.{
832 .system_lib = .{
833 .name = self.builder.dupe(name),
834 .needed = opts.needed,
835 .weak = opts.weak,
836 .use_pkg_config = .yes,
837 },
838 }) catch unreachable;
839}
840
841pub fn setNamePrefix(self: *LibExeObjStep, text: []const u8) void {
842 assert(self.kind == .@"test" or self.kind == .test_exe);
843 self.name_prefix = self.builder.dupe(text);
844}
845
846pub fn setFilter(self: *LibExeObjStep, text: ?[]const u8) void {
847 assert(self.kind == .@"test" or self.kind == .test_exe);
848 self.filter = if (text) |t| self.builder.dupe(t) else null;
849}
850
851pub fn setTestRunner(self: *LibExeObjStep, path: ?[]const u8) void {
852 assert(self.kind == .@"test" or self.kind == .test_exe);
853 self.test_runner = if (path) |p| self.builder.dupePath(p) else null;
854}
855
856/// Handy when you have many C/C++ source files and want them all to have the same flags.
857pub fn addCSourceFiles(self: *LibExeObjStep, files: []const []const u8, flags: []const []const u8) void {
858 const c_source_files = self.builder.allocator.create(CSourceFiles) catch unreachable;
859
860 const files_copy = self.builder.dupeStrings(files);
861 const flags_copy = self.builder.dupeStrings(flags);
862
863 c_source_files.* = .{
864 .files = files_copy,
865 .flags = flags_copy,
866 };
867 self.link_objects.append(.{ .c_source_files = c_source_files }) catch unreachable;
868}
869
870pub fn addCSourceFile(self: *LibExeObjStep, file: []const u8, flags: []const []const u8) void {
871 self.addCSourceFileSource(.{
872 .args = flags,
873 .source = .{ .path = file },
874 });
875}
876
877pub fn addCSourceFileSource(self: *LibExeObjStep, source: CSourceFile) void {
878 const c_source_file = self.builder.allocator.create(CSourceFile) catch unreachable;
879 c_source_file.* = source.dupe(self.builder);
880 self.link_objects.append(.{ .c_source_file = c_source_file }) catch unreachable;
881 source.source.addStepDependencies(&self.step);
882}
883
884pub fn setVerboseLink(self: *LibExeObjStep, value: bool) void {
885 self.verbose_link = value;
886}
887
888pub fn setVerboseCC(self: *LibExeObjStep, value: bool) void {
889 self.verbose_cc = value;
890}
891
892pub fn setBuildMode(self: *LibExeObjStep, mode: std.builtin.Mode) void {
893 self.build_mode = mode;
894}
895
896pub fn overrideZigLibDir(self: *LibExeObjStep, dir_path: []const u8) void {
897 self.override_lib_dir = self.builder.dupePath(dir_path);
898}
899
900pub fn setMainPkgPath(self: *LibExeObjStep, dir_path: []const u8) void {
901 self.main_pkg_path = self.builder.dupePath(dir_path);
902}
903
904pub fn setLibCFile(self: *LibExeObjStep, libc_file: ?FileSource) void {
905 self.libc_file = if (libc_file) |f| f.dupe(self.builder) else null;
906}
907
908/// Returns the generated executable, library or object file.
909/// To run an executable built with zig build, use `run`, or create an install step and invoke it.
910pub fn getOutputSource(self: *LibExeObjStep) FileSource {
911 return FileSource{ .generated = &self.output_path_source };
912}
913
914/// Returns the generated import library. This function can only be called for libraries.
915pub fn getOutputLibSource(self: *LibExeObjStep) FileSource {
916 assert(self.kind == .lib);
917 return FileSource{ .generated = &self.output_lib_path_source };
918}
919
920/// Returns the generated header file.
921/// This function can only be called for libraries or object files which have `emit_h` set.
922pub fn getOutputHSource(self: *LibExeObjStep) FileSource {
923 assert(self.kind != .exe and self.kind != .test_exe and self.kind != .@"test");
924 assert(self.emit_h);
925 return FileSource{ .generated = &self.output_h_path_source };
926}
927
928/// Returns the generated PDB file. This function can only be called for Windows and UEFI.
929pub fn getOutputPdbSource(self: *LibExeObjStep) FileSource {
930 // TODO: Is this right? Isn't PDB for *any* PE/COFF file?
931 assert(self.target.isWindows() or self.target.isUefi());
932 return FileSource{ .generated = &self.output_pdb_path_source };
933}
934
935pub fn addAssemblyFile(self: *LibExeObjStep, path: []const u8) void {
936 self.link_objects.append(.{
937 .assembly_file = .{ .path = self.builder.dupe(path) },
938 }) catch unreachable;
939}
940
941pub fn addAssemblyFileSource(self: *LibExeObjStep, source: FileSource) void {
942 const source_duped = source.dupe(self.builder);
943 self.link_objects.append(.{ .assembly_file = source_duped }) catch unreachable;
944 source_duped.addStepDependencies(&self.step);
945}
946
947pub fn addObjectFile(self: *LibExeObjStep, source_file: []const u8) void {
948 self.addObjectFileSource(.{ .path = source_file });
949}
950
951pub fn addObjectFileSource(self: *LibExeObjStep, source: FileSource) void {
952 self.link_objects.append(.{ .static_path = source.dupe(self.builder) }) catch unreachable;
953 source.addStepDependencies(&self.step);
954}
955
956pub fn addObject(self: *LibExeObjStep, obj: *LibExeObjStep) void {
957 assert(obj.kind == .obj);
958 self.linkLibraryOrObject(obj);
959}
960
961pub const addSystemIncludeDir = @compileError("deprecated; use addSystemIncludePath");
962pub const addIncludeDir = @compileError("deprecated; use addIncludePath");
963pub const addLibPath = @compileError("deprecated, use addLibraryPath");
964pub const addFrameworkDir = @compileError("deprecated, use addFrameworkPath");
965
966pub fn addSystemIncludePath(self: *LibExeObjStep, path: []const u8) void {
967 self.include_dirs.append(IncludeDir{ .raw_path_system = self.builder.dupe(path) }) catch unreachable;
968}
969
970pub fn addIncludePath(self: *LibExeObjStep, path: []const u8) void {
971 self.include_dirs.append(IncludeDir{ .raw_path = self.builder.dupe(path) }) catch unreachable;
972}
973
974pub fn addConfigHeader(self: *LibExeObjStep, config_header: *ConfigHeaderStep) void {
975 self.step.dependOn(&config_header.step);
976 self.include_dirs.append(.{ .config_header_step = config_header }) catch @panic("OOM");
977}
978
979pub fn addLibraryPath(self: *LibExeObjStep, path: []const u8) void {
980 self.lib_paths.append(self.builder.dupe(path)) catch unreachable;
981}
982
983pub fn addRPath(self: *LibExeObjStep, path: []const u8) void {
984 self.rpaths.append(self.builder.dupe(path)) catch unreachable;
985}
986
987pub fn addFrameworkPath(self: *LibExeObjStep, dir_path: []const u8) void {
988 self.framework_dirs.append(self.builder.dupe(dir_path)) catch unreachable;
989}
990
991pub fn addPackage(self: *LibExeObjStep, package: Pkg) void {
992 self.packages.append(self.builder.dupePkg(package)) catch unreachable;
993 self.addRecursiveBuildDeps(package);
994}
995
996pub fn addOptions(self: *LibExeObjStep, package_name: []const u8, options: *OptionsStep) void {
997 self.addPackage(options.getPackage(package_name));
998}
999
1000fn addRecursiveBuildDeps(self: *LibExeObjStep, package: Pkg) void {
1001 package.source.addStepDependencies(&self.step);
1002 if (package.dependencies) |deps| {
1003 for (deps) |dep| {
1004 self.addRecursiveBuildDeps(dep);
1005 }
1006 }
1007}
1008
1009pub fn addPackagePath(self: *LibExeObjStep, name: []const u8, pkg_index_path: []const u8) void {
1010 self.addPackage(Pkg{
1011 .name = self.builder.dupe(name),
1012 .source = .{ .path = self.builder.dupe(pkg_index_path) },
1013 });
1014}
1015
1016/// If Vcpkg was found on the system, it will be added to include and lib
1017/// paths for the specified target.
1018pub fn addVcpkgPaths(self: *LibExeObjStep, linkage: LibExeObjStep.Linkage) !void {
1019 // Ideally in the Unattempted case we would call the function recursively
1020 // after findVcpkgRoot and have only one switch statement, but the compiler
1021 // cannot resolve the error set.
1022 switch (self.builder.vcpkg_root) {
1023 .unattempted => {
1024 self.builder.vcpkg_root = if (try findVcpkgRoot(self.builder.allocator)) |root|
1025 VcpkgRoot{ .found = root }
1026 else
1027 .not_found;
1028 },
1029 .not_found => return error.VcpkgNotFound,
1030 .found => {},
1031 }
1032
1033 switch (self.builder.vcpkg_root) {
1034 .unattempted => unreachable,
1035 .not_found => return error.VcpkgNotFound,
1036 .found => |root| {
1037 const allocator = self.builder.allocator;
1038 const triplet = try self.target.vcpkgTriplet(allocator, if (linkage == .static) .Static else .Dynamic);
1039 defer self.builder.allocator.free(triplet);
1040
1041 const include_path = self.builder.pathJoin(&.{ root, "installed", triplet, "include" });
1042 errdefer allocator.free(include_path);
1043 try self.include_dirs.append(IncludeDir{ .raw_path = include_path });
1044
1045 const lib_path = self.builder.pathJoin(&.{ root, "installed", triplet, "lib" });
1046 try self.lib_paths.append(lib_path);
1047
1048 self.vcpkg_bin_path = self.builder.pathJoin(&.{ root, "installed", triplet, "bin" });
1049 },
1050 }
1051}
1052
1053pub fn setExecCmd(self: *LibExeObjStep, args: []const ?[]const u8) void {
1054 assert(self.kind == .@"test");
1055 const duped_args = self.builder.allocator.alloc(?[]u8, args.len) catch unreachable;
1056 for (args) |arg, i| {
1057 duped_args[i] = if (arg) |a| self.builder.dupe(a) else null;
1058 }
1059 self.exec_cmd_args = duped_args;
1060}
1061
1062fn linkLibraryOrObject(self: *LibExeObjStep, other: *LibExeObjStep) void {
1063 self.step.dependOn(&other.step);
1064 self.link_objects.append(.{ .other_step = other }) catch unreachable;
1065 self.include_dirs.append(.{ .other_step = other }) catch unreachable;
1066}
1067
1068fn makePackageCmd(self: *LibExeObjStep, pkg: Pkg, zig_args: *ArrayList([]const u8)) error{OutOfMemory}!void {
1069 const builder = self.builder;
1070
1071 try zig_args.append("--pkg-begin");
1072 try zig_args.append(pkg.name);
1073 try zig_args.append(builder.pathFromRoot(pkg.source.getPath(self.builder)));
1074
1075 if (pkg.dependencies) |dependencies| {
1076 for (dependencies) |sub_pkg| {
1077 try self.makePackageCmd(sub_pkg, zig_args);
1078 }
1079 }
1080
1081 try zig_args.append("--pkg-end");
1082}
1083
1084fn make(step: *Step) !void {
1085 const self = @fieldParentPtr(LibExeObjStep, "step", step);
1086 const builder = self.builder;
1087
1088 if (self.root_src == null and self.link_objects.items.len == 0) {
1089 log.err("{s}: linker needs 1 or more objects to link", .{self.step.name});
1090 return error.NeedAnObject;
1091 }
1092
1093 var zig_args = ArrayList([]const u8).init(builder.allocator);
1094 defer zig_args.deinit();
1095
1096 zig_args.append(builder.zig_exe) catch unreachable;
1097
1098 const cmd = switch (self.kind) {
1099 .lib => "build-lib",
1100 .exe => "build-exe",
1101 .obj => "build-obj",
1102 .@"test" => "test",
1103 .test_exe => "test",
1104 };
1105 zig_args.append(cmd) catch unreachable;
1106
1107 if (builder.color != .auto) {
1108 try zig_args.append("--color");
1109 try zig_args.append(@tagName(builder.color));
1110 }
1111
1112 if (builder.reference_trace) |some| {
1113 try zig_args.append(try std.fmt.allocPrint(builder.allocator, "-freference-trace={d}", .{some}));
1114 }
1115
1116 try addFlag(&zig_args, "LLVM", self.use_llvm);
1117 try addFlag(&zig_args, "LLD", self.use_lld);
1118
1119 if (self.target.ofmt) |ofmt| {
1120 try zig_args.append(try std.fmt.allocPrint(builder.allocator, "-ofmt={s}", .{@tagName(ofmt)}));
1121 }
1122
1123 if (self.entry_symbol_name) |entry| {
1124 try zig_args.append("--entry");
1125 try zig_args.append(entry);
1126 }
1127
1128 if (self.stack_size) |stack_size| {
1129 try zig_args.append("--stack");
1130 try zig_args.append(try std.fmt.allocPrint(builder.allocator, "{}", .{stack_size}));
1131 }
1132
1133 if (self.root_src) |root_src| try zig_args.append(root_src.getPath(builder));
1134
1135 // We will add link objects from transitive dependencies, but we want to keep
1136 // all link objects in the same order provided.
1137 // This array is used to keep self.link_objects immutable.
1138 var transitive_deps: TransitiveDeps = .{
1139 .link_objects = ArrayList(LinkObject).init(builder.allocator),
1140 .seen_system_libs = StringHashMap(void).init(builder.allocator),
1141 .seen_steps = std.AutoHashMap(*const Step, void).init(builder.allocator),
1142 .is_linking_libcpp = self.is_linking_libcpp,
1143 .is_linking_libc = self.is_linking_libc,
1144 .frameworks = &self.frameworks,
1145 };
1146
1147 try transitive_deps.seen_steps.put(&self.step, {});
1148 try transitive_deps.add(self.link_objects.items);
1149
1150 var prev_has_extra_flags = false;
1151
1152 for (transitive_deps.link_objects.items) |link_object| {
1153 switch (link_object) {
1154 .static_path => |static_path| try zig_args.append(static_path.getPath(builder)),
1155
1156 .other_step => |other| switch (other.kind) {
1157 .exe => @panic("Cannot link with an executable build artifact"),
1158 .test_exe => @panic("Cannot link with an executable build artifact"),
1159 .@"test" => @panic("Cannot link with a test"),
1160 .obj => {
1161 try zig_args.append(other.getOutputSource().getPath(builder));
1162 },
1163 .lib => l: {
1164 if (self.isStaticLibrary() and other.isStaticLibrary()) {
1165 // Avoid putting a static library inside a static library.
1166 break :l;
1167 }
1168
1169 const full_path_lib = other.getOutputLibSource().getPath(builder);
1170 try zig_args.append(full_path_lib);
1171
1172 if (other.linkage == Linkage.dynamic and !self.target.isWindows()) {
1173 if (fs.path.dirname(full_path_lib)) |dirname| {
1174 try zig_args.append("-rpath");
1175 try zig_args.append(dirname);
1176 }
1177 }
1178 },
1179 },
1180
1181 .system_lib => |system_lib| {
1182 const prefix: []const u8 = prefix: {
1183 if (system_lib.needed) break :prefix "-needed-l";
1184 if (system_lib.weak) {
1185 if (self.target.isDarwin()) break :prefix "-weak-l";
1186 log.warn("Weak library import used for a non-darwin target, this will be converted to normally library import `-lname`", .{});
1187 }
1188 break :prefix "-l";
1189 };
1190 switch (system_lib.use_pkg_config) {
1191 .no => try zig_args.append(builder.fmt("{s}{s}", .{ prefix, system_lib.name })),
1192 .yes, .force => {
1193 if (self.runPkgConfig(system_lib.name)) |args| {
1194 try zig_args.appendSlice(args);
1195 } else |err| switch (err) {
1196 error.PkgConfigInvalidOutput,
1197 error.PkgConfigCrashed,
1198 error.PkgConfigFailed,
1199 error.PkgConfigNotInstalled,
1200 error.PackageNotFound,
1201 => switch (system_lib.use_pkg_config) {
1202 .yes => {
1203 // pkg-config failed, so fall back to linking the library
1204 // by name directly.
1205 try zig_args.append(builder.fmt("{s}{s}", .{
1206 prefix,
1207 system_lib.name,
1208 }));
1209 },
1210 .force => {
1211 panic("pkg-config failed for library {s}", .{system_lib.name});
1212 },
1213 .no => unreachable,
1214 },
1215
1216 else => |e| return e,
1217 }
1218 },
1219 }
1220 },
1221
1222 .assembly_file => |asm_file| {
1223 if (prev_has_extra_flags) {
1224 try zig_args.append("-extra-cflags");
1225 try zig_args.append("--");
1226 prev_has_extra_flags = false;
1227 }
1228 try zig_args.append(asm_file.getPath(builder));
1229 },
1230
1231 .c_source_file => |c_source_file| {
1232 if (c_source_file.args.len == 0) {
1233 if (prev_has_extra_flags) {
1234 try zig_args.append("-cflags");
1235 try zig_args.append("--");
1236 prev_has_extra_flags = false;
1237 }
1238 } else {
1239 try zig_args.append("-cflags");
1240 for (c_source_file.args) |arg| {
1241 try zig_args.append(arg);
1242 }
1243 try zig_args.append("--");
1244 }
1245 try zig_args.append(c_source_file.source.getPath(builder));
1246 },
1247
1248 .c_source_files => |c_source_files| {
1249 if (c_source_files.flags.len == 0) {
1250 if (prev_has_extra_flags) {
1251 try zig_args.append("-cflags");
1252 try zig_args.append("--");
1253 prev_has_extra_flags = false;
1254 }
1255 } else {
1256 try zig_args.append("-cflags");
1257 for (c_source_files.flags) |flag| {
1258 try zig_args.append(flag);
1259 }
1260 try zig_args.append("--");
1261 }
1262 for (c_source_files.files) |file| {
1263 try zig_args.append(builder.pathFromRoot(file));
1264 }
1265 },
1266 }
1267 }
1268
1269 if (transitive_deps.is_linking_libcpp) {
1270 try zig_args.append("-lc++");
1271 }
1272
1273 if (transitive_deps.is_linking_libc) {
1274 try zig_args.append("-lc");
1275 }
1276
1277 if (self.image_base) |image_base| {
1278 try zig_args.append("--image-base");
1279 try zig_args.append(builder.fmt("0x{x}", .{image_base}));
1280 }
1281
1282 if (self.filter) |filter| {
1283 try zig_args.append("--test-filter");
1284 try zig_args.append(filter);
1285 }
1286
1287 if (self.test_evented_io) {
1288 try zig_args.append("--test-evented-io");
1289 }
1290
1291 if (self.name_prefix.len != 0) {
1292 try zig_args.append("--test-name-prefix");
1293 try zig_args.append(self.name_prefix);
1294 }
1295
1296 if (self.test_runner) |test_runner| {
1297 try zig_args.append("--test-runner");
1298 try zig_args.append(builder.pathFromRoot(test_runner));
1299 }
1300
1301 for (builder.debug_log_scopes) |log_scope| {
1302 try zig_args.append("--debug-log");
1303 try zig_args.append(log_scope);
1304 }
1305
1306 if (builder.debug_compile_errors) {
1307 try zig_args.append("--debug-compile-errors");
1308 }
1309
1310 if (builder.verbose_cimport) zig_args.append("--verbose-cimport") catch unreachable;
1311 if (builder.verbose_air) zig_args.append("--verbose-air") catch unreachable;
1312 if (builder.verbose_llvm_ir) zig_args.append("--verbose-llvm-ir") catch unreachable;
1313 if (builder.verbose_link or self.verbose_link) zig_args.append("--verbose-link") catch unreachable;
1314 if (builder.verbose_cc or self.verbose_cc) zig_args.append("--verbose-cc") catch unreachable;
1315 if (builder.verbose_llvm_cpu_features) zig_args.append("--verbose-llvm-cpu-features") catch unreachable;
1316
1317 if (self.emit_analysis.getArg(builder, "emit-analysis")) |arg| try zig_args.append(arg);
1318 if (self.emit_asm.getArg(builder, "emit-asm")) |arg| try zig_args.append(arg);
1319 if (self.emit_bin.getArg(builder, "emit-bin")) |arg| try zig_args.append(arg);
1320 if (self.emit_docs.getArg(builder, "emit-docs")) |arg| try zig_args.append(arg);
1321 if (self.emit_implib.getArg(builder, "emit-implib")) |arg| try zig_args.append(arg);
1322 if (self.emit_llvm_bc.getArg(builder, "emit-llvm-bc")) |arg| try zig_args.append(arg);
1323 if (self.emit_llvm_ir.getArg(builder, "emit-llvm-ir")) |arg| try zig_args.append(arg);
1324
1325 if (self.emit_h) try zig_args.append("-femit-h");
1326
1327 try addFlag(&zig_args, "strip", self.strip);
1328 try addFlag(&zig_args, "unwind-tables", self.unwind_tables);
1329
1330 switch (self.compress_debug_sections) {
1331 .none => {},
1332 .zlib => try zig_args.append("--compress-debug-sections=zlib"),
1333 }
1334
1335 if (self.link_eh_frame_hdr) {
1336 try zig_args.append("--eh-frame-hdr");
1337 }
1338 if (self.link_emit_relocs) {
1339 try zig_args.append("--emit-relocs");
1340 }
1341 if (self.link_function_sections) {
1342 try zig_args.append("-ffunction-sections");
1343 }
1344 if (self.link_gc_sections) |x| {
1345 try zig_args.append(if (x) "--gc-sections" else "--no-gc-sections");
1346 }
1347 if (self.linker_allow_shlib_undefined) |x| {
1348 try zig_args.append(if (x) "-fallow-shlib-undefined" else "-fno-allow-shlib-undefined");
1349 }
1350 if (self.link_z_notext) {
1351 try zig_args.append("-z");
1352 try zig_args.append("notext");
1353 }
1354 if (!self.link_z_relro) {
1355 try zig_args.append("-z");
1356 try zig_args.append("norelro");
1357 }
1358 if (self.link_z_lazy) {
1359 try zig_args.append("-z");
1360 try zig_args.append("lazy");
1361 }
1362 if (self.link_z_common_page_size) |size| {
1363 try zig_args.append("-z");
1364 try zig_args.append(builder.fmt("common-page-size={d}", .{size}));
1365 }
1366 if (self.link_z_max_page_size) |size| {
1367 try zig_args.append("-z");
1368 try zig_args.append(builder.fmt("max-page-size={d}", .{size}));
1369 }
1370
1371 if (self.libc_file) |libc_file| {
1372 try zig_args.append("--libc");
1373 try zig_args.append(libc_file.getPath(builder));
1374 } else if (builder.libc_file) |libc_file| {
1375 try zig_args.append("--libc");
1376 try zig_args.append(libc_file);
1377 }
1378
1379 switch (self.build_mode) {
1380 .Debug => {}, // Skip since it's the default.
1381 else => zig_args.append(builder.fmt("-O{s}", .{@tagName(self.build_mode)})) catch unreachable,
1382 }
1383
1384 try zig_args.append("--cache-dir");
1385 try zig_args.append(builder.pathFromRoot(builder.cache_root));
1386
1387 try zig_args.append("--global-cache-dir");
1388 try zig_args.append(builder.pathFromRoot(builder.global_cache_root));
1389
1390 zig_args.append("--name") catch unreachable;
1391 zig_args.append(self.name) catch unreachable;
1392
1393 if (self.linkage) |some| switch (some) {
1394 .dynamic => try zig_args.append("-dynamic"),
1395 .static => try zig_args.append("-static"),
1396 };
1397 if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic) {
1398 if (self.version) |version| {
1399 zig_args.append("--version") catch unreachable;
1400 zig_args.append(builder.fmt("{}", .{version})) catch unreachable;
1401 }
1402
1403 if (self.target.isDarwin()) {
1404 const install_name = self.install_name orelse builder.fmt("@rpath/{s}{s}{s}", .{
1405 self.target.libPrefix(),
1406 self.name,
1407 self.target.dynamicLibSuffix(),
1408 });
1409 try zig_args.append("-install_name");
1410 try zig_args.append(install_name);
1411 }
1412 }
1413
1414 if (self.entitlements) |entitlements| {
1415 try zig_args.appendSlice(&[_][]const u8{ "--entitlements", entitlements });
1416 }
1417 if (self.pagezero_size) |pagezero_size| {
1418 const size = try std.fmt.allocPrint(builder.allocator, "{x}", .{pagezero_size});
1419 try zig_args.appendSlice(&[_][]const u8{ "-pagezero_size", size });
1420 }
1421 if (self.search_strategy) |strat| switch (strat) {
1422 .paths_first => try zig_args.append("-search_paths_first"),
1423 .dylibs_first => try zig_args.append("-search_dylibs_first"),
1424 };
1425 if (self.headerpad_size) |headerpad_size| {
1426 const size = try std.fmt.allocPrint(builder.allocator, "{x}", .{headerpad_size});
1427 try zig_args.appendSlice(&[_][]const u8{ "-headerpad", size });
1428 }
1429 if (self.headerpad_max_install_names) {
1430 try zig_args.append("-headerpad_max_install_names");
1431 }
1432 if (self.dead_strip_dylibs) {
1433 try zig_args.append("-dead_strip_dylibs");
1434 }
1435
1436 try addFlag(&zig_args, "compiler-rt", self.bundle_compiler_rt);
1437 try addFlag(&zig_args, "single-threaded", self.single_threaded);
1438 if (self.disable_stack_probing) {
1439 try zig_args.append("-fno-stack-check");
1440 }
1441 try addFlag(&zig_args, "stack-protector", self.stack_protector);
1442 if (self.red_zone) |red_zone| {
1443 if (red_zone) {
1444 try zig_args.append("-mred-zone");
1445 } else {
1446 try zig_args.append("-mno-red-zone");
1447 }
1448 }
1449 try addFlag(&zig_args, "omit-frame-pointer", self.omit_frame_pointer);
1450 try addFlag(&zig_args, "dll-export-fns", self.dll_export_fns);
1451
1452 if (self.disable_sanitize_c) {
1453 try zig_args.append("-fno-sanitize-c");
1454 }
1455 if (self.sanitize_thread) {
1456 try zig_args.append("-fsanitize-thread");
1457 }
1458 if (self.rdynamic) {
1459 try zig_args.append("-rdynamic");
1460 }
1461 if (self.import_memory) {
1462 try zig_args.append("--import-memory");
1463 }
1464 if (self.import_symbols) {
1465 try zig_args.append("--import-symbols");
1466 }
1467 if (self.import_table) {
1468 try zig_args.append("--import-table");
1469 }
1470 if (self.export_table) {
1471 try zig_args.append("--export-table");
1472 }
1473 if (self.initial_memory) |initial_memory| {
1474 try zig_args.append(builder.fmt("--initial-memory={d}", .{initial_memory}));
1475 }
1476 if (self.max_memory) |max_memory| {
1477 try zig_args.append(builder.fmt("--max-memory={d}", .{max_memory}));
1478 }
1479 if (self.shared_memory) {
1480 try zig_args.append("--shared-memory");
1481 }
1482 if (self.global_base) |global_base| {
1483 try zig_args.append(builder.fmt("--global-base={d}", .{global_base}));
1484 }
1485
1486 if (self.code_model != .default) {
1487 try zig_args.append("-mcmodel");
1488 try zig_args.append(@tagName(self.code_model));
1489 }
1490 if (self.wasi_exec_model) |model| {
1491 try zig_args.append(builder.fmt("-mexec-model={s}", .{@tagName(model)}));
1492 }
1493 for (self.export_symbol_names) |symbol_name| {
1494 try zig_args.append(builder.fmt("--export={s}", .{symbol_name}));
1495 }
1496
1497 if (!self.target.isNative()) {
1498 try zig_args.append("-target");
1499 try zig_args.append(try self.target.zigTriple(builder.allocator));
1500
1501 // TODO this logic can disappear if cpu model + features becomes part of the target triple
1502 const cross = self.target.toTarget();
1503 const all_features = cross.cpu.arch.allFeaturesList();
1504 var populated_cpu_features = cross.cpu.model.features;
1505 populated_cpu_features.populateDependencies(all_features);
1506
1507 if (populated_cpu_features.eql(cross.cpu.features)) {
1508 // The CPU name alone is sufficient.
1509 try zig_args.append("-mcpu");
1510 try zig_args.append(cross.cpu.model.name);
1511 } else {
1512 var mcpu_buffer = ArrayList(u8).init(builder.allocator);
1513
1514 try mcpu_buffer.writer().print("-mcpu={s}", .{cross.cpu.model.name});
1515
1516 for (all_features) |feature, i_usize| {
1517 const i = @intCast(std.Target.Cpu.Feature.Set.Index, i_usize);
1518 const in_cpu_set = populated_cpu_features.isEnabled(i);
1519 const in_actual_set = cross.cpu.features.isEnabled(i);
1520 if (in_cpu_set and !in_actual_set) {
1521 try mcpu_buffer.writer().print("-{s}", .{feature.name});
1522 } else if (!in_cpu_set and in_actual_set) {
1523 try mcpu_buffer.writer().print("+{s}", .{feature.name});
1524 }
1525 }
1526
1527 try zig_args.append(try mcpu_buffer.toOwnedSlice());
1528 }
1529
1530 if (self.target.dynamic_linker.get()) |dynamic_linker| {
1531 try zig_args.append("--dynamic-linker");
1532 try zig_args.append(dynamic_linker);
1533 }
1534 }
1535
1536 if (self.linker_script) |linker_script| {
1537 try zig_args.append("--script");
1538 try zig_args.append(linker_script.getPath(builder));
1539 }
1540
1541 if (self.version_script) |version_script| {
1542 try zig_args.append("--version-script");
1543 try zig_args.append(builder.pathFromRoot(version_script));
1544 }
1545
1546 if (self.kind == .@"test") {
1547 if (self.exec_cmd_args) |exec_cmd_args| {
1548 for (exec_cmd_args) |cmd_arg| {
1549 if (cmd_arg) |arg| {
1550 try zig_args.append("--test-cmd");
1551 try zig_args.append(arg);
1552 } else {
1553 try zig_args.append("--test-cmd-bin");
1554 }
1555 }
1556 } else {
1557 const need_cross_glibc = self.target.isGnuLibC() and transitive_deps.is_linking_libc;
1558
1559 switch (builder.host.getExternalExecutor(self.target_info, .{
1560 .qemu_fixes_dl = need_cross_glibc and builder.glibc_runtimes_dir != null,
1561 .link_libc = transitive_deps.is_linking_libc,
1562 })) {
1563 .native => {},
1564 .bad_dl, .bad_os_or_cpu => {
1565 try zig_args.append("--test-no-exec");
1566 },
1567 .rosetta => if (builder.enable_rosetta) {
1568 try zig_args.append("--test-cmd-bin");
1569 } else {
1570 try zig_args.append("--test-no-exec");
1571 },
1572 .qemu => |bin_name| ok: {
1573 if (builder.enable_qemu) qemu: {
1574 const glibc_dir_arg = if (need_cross_glibc)
1575 builder.glibc_runtimes_dir orelse break :qemu
1576 else
1577 null;
1578 try zig_args.append("--test-cmd");
1579 try zig_args.append(bin_name);
1580 if (glibc_dir_arg) |dir| {
1581 // TODO look into making this a call to `linuxTriple`. This
1582 // needs the directory to be called "i686" rather than
1583 // "x86" which is why we do it manually here.
1584 const fmt_str = "{s}" ++ fs.path.sep_str ++ "{s}-{s}-{s}";
1585 const cpu_arch = self.target.getCpuArch();
1586 const os_tag = self.target.getOsTag();
1587 const abi = self.target.getAbi();
1588 const cpu_arch_name: []const u8 = if (cpu_arch == .x86)
1589 "i686"
1590 else
1591 @tagName(cpu_arch);
1592 const full_dir = try std.fmt.allocPrint(builder.allocator, fmt_str, .{
1593 dir, cpu_arch_name, @tagName(os_tag), @tagName(abi),
1594 });
1595
1596 try zig_args.append("--test-cmd");
1597 try zig_args.append("-L");
1598 try zig_args.append("--test-cmd");
1599 try zig_args.append(full_dir);
1600 }
1601 try zig_args.append("--test-cmd-bin");
1602 break :ok;
1603 }
1604 try zig_args.append("--test-no-exec");
1605 },
1606 .wine => |bin_name| if (builder.enable_wine) {
1607 try zig_args.append("--test-cmd");
1608 try zig_args.append(bin_name);
1609 try zig_args.append("--test-cmd-bin");
1610 } else {
1611 try zig_args.append("--test-no-exec");
1612 },
1613 .wasmtime => |bin_name| if (builder.enable_wasmtime) {
1614 try zig_args.append("--test-cmd");
1615 try zig_args.append(bin_name);
1616 try zig_args.append("--test-cmd");
1617 try zig_args.append("--dir=.");
1618 try zig_args.append("--test-cmd-bin");
1619 } else {
1620 try zig_args.append("--test-no-exec");
1621 },
1622 .darling => |bin_name| if (builder.enable_darling) {
1623 try zig_args.append("--test-cmd");
1624 try zig_args.append(bin_name);
1625 try zig_args.append("--test-cmd-bin");
1626 } else {
1627 try zig_args.append("--test-no-exec");
1628 },
1629 }
1630 }
1631 } else if (self.kind == .test_exe) {
1632 try zig_args.append("--test-no-exec");
1633 }
1634
1635 for (self.packages.items) |pkg| {
1636 try self.makePackageCmd(pkg, &zig_args);
1637 }
1638
1639 for (self.include_dirs.items) |include_dir| {
1640 switch (include_dir) {
1641 .raw_path => |include_path| {
1642 try zig_args.append("-I");
1643 try zig_args.append(builder.pathFromRoot(include_path));
1644 },
1645 .raw_path_system => |include_path| {
1646 if (builder.sysroot != null) {
1647 try zig_args.append("-iwithsysroot");
1648 } else {
1649 try zig_args.append("-isystem");
1650 }
1651
1652 const resolved_include_path = builder.pathFromRoot(include_path);
1653
1654 const common_include_path = if (builtin.os.tag == .windows and builder.sysroot != null and fs.path.isAbsolute(resolved_include_path)) blk: {
1655 // We need to check for disk designator and strip it out from dir path so
1656 // that zig/clang can concat resolved_include_path with sysroot.
1657 const disk_designator = fs.path.diskDesignatorWindows(resolved_include_path);
1658
1659 if (mem.indexOf(u8, resolved_include_path, disk_designator)) |where| {
1660 break :blk resolved_include_path[where + disk_designator.len ..];
1661 }
1662
1663 break :blk resolved_include_path;
1664 } else resolved_include_path;
1665
1666 try zig_args.append(common_include_path);
1667 },
1668 .other_step => |other| {
1669 if (other.emit_h) {
1670 const h_path = other.getOutputHSource().getPath(builder);
1671 try zig_args.append("-isystem");
1672 try zig_args.append(fs.path.dirname(h_path).?);
1673 }
1674 if (other.installed_headers.items.len > 0) {
1675 for (other.installed_headers.items) |install_step| {
1676 try install_step.make();
1677 }
1678 try zig_args.append("-I");
1679 try zig_args.append(builder.pathJoin(&.{
1680 other.builder.install_prefix, "include",
1681 }));
1682 }
1683 },
1684 .config_header_step => |config_header| {
1685 try zig_args.append("-I");
1686 try zig_args.append(config_header.output_dir);
1687 },
1688 }
1689 }
1690
1691 for (self.lib_paths.items) |lib_path| {
1692 try zig_args.append("-L");
1693 try zig_args.append(lib_path);
1694 }
1695
1696 for (self.rpaths.items) |rpath| {
1697 try zig_args.append("-rpath");
1698 try zig_args.append(rpath);
1699 }
1700
1701 for (self.c_macros.items) |c_macro| {
1702 try zig_args.append("-D");
1703 try zig_args.append(c_macro);
1704 }
1705
1706 if (self.target.isDarwin()) {
1707 for (self.framework_dirs.items) |dir| {
1708 if (builder.sysroot != null) {
1709 try zig_args.append("-iframeworkwithsysroot");
1710 } else {
1711 try zig_args.append("-iframework");
1712 }
1713 try zig_args.append(dir);
1714 try zig_args.append("-F");
1715 try zig_args.append(dir);
1716 }
1717
1718 var it = self.frameworks.iterator();
1719 while (it.next()) |entry| {
1720 const name = entry.key_ptr.*;
1721 const info = entry.value_ptr.*;
1722 if (info.needed) {
1723 zig_args.append("-needed_framework") catch unreachable;
1724 } else if (info.weak) {
1725 zig_args.append("-weak_framework") catch unreachable;
1726 } else {
1727 zig_args.append("-framework") catch unreachable;
1728 }
1729 zig_args.append(name) catch unreachable;
1730 }
1731 } else {
1732 if (self.framework_dirs.items.len > 0) {
1733 log.info("Framework directories have been added for a non-darwin target, this will have no affect on the build", .{});
1734 }
1735
1736 if (self.frameworks.count() > 0) {
1737 log.info("Frameworks have been added for a non-darwin target, this will have no affect on the build", .{});
1738 }
1739 }
1740
1741 if (builder.sysroot) |sysroot| {
1742 try zig_args.appendSlice(&[_][]const u8{ "--sysroot", sysroot });
1743 }
1744
1745 for (builder.search_prefixes.items) |search_prefix| {
1746 try zig_args.append("-L");
1747 try zig_args.append(builder.pathJoin(&.{
1748 search_prefix, "lib",
1749 }));
1750 try zig_args.append("-I");
1751 try zig_args.append(builder.pathJoin(&.{
1752 search_prefix, "include",
1753 }));
1754 }
1755
1756 try addFlag(&zig_args, "valgrind", self.valgrind_support);
1757 try addFlag(&zig_args, "each-lib-rpath", self.each_lib_rpath);
1758 try addFlag(&zig_args, "build-id", self.build_id);
1759
1760 if (self.override_lib_dir) |dir| {
1761 try zig_args.append("--zig-lib-dir");
1762 try zig_args.append(builder.pathFromRoot(dir));
1763 } else if (builder.override_lib_dir) |dir| {
1764 try zig_args.append("--zig-lib-dir");
1765 try zig_args.append(builder.pathFromRoot(dir));
1766 }
1767
1768 if (self.main_pkg_path) |dir| {
1769 try zig_args.append("--main-pkg-path");
1770 try zig_args.append(builder.pathFromRoot(dir));
1771 }
1772
1773 try addFlag(&zig_args, "PIC", self.force_pic);
1774 try addFlag(&zig_args, "PIE", self.pie);
1775 try addFlag(&zig_args, "lto", self.want_lto);
1776
1777 if (self.subsystem) |subsystem| {
1778 try zig_args.append("--subsystem");
1779 try zig_args.append(switch (subsystem) {
1780 .Console => "console",
1781 .Windows => "windows",
1782 .Posix => "posix",
1783 .Native => "native",
1784 .EfiApplication => "efi_application",
1785 .EfiBootServiceDriver => "efi_boot_service_driver",
1786 .EfiRom => "efi_rom",
1787 .EfiRuntimeDriver => "efi_runtime_driver",
1788 });
1789 }
1790
1791 try zig_args.append("--enable-cache");
1792
1793 // Windows has an argument length limit of 32,766 characters, macOS 262,144 and Linux
1794 // 2,097,152. If our args exceed 30 KiB, we instead write them to a "response file" and
1795 // pass that to zig, e.g. via 'zig build-lib @args.rsp'
1796 // See @file syntax here: https://gcc.gnu.org/onlinedocs/gcc/Overall-Options.html
1797 var args_length: usize = 0;
1798 for (zig_args.items) |arg| {
1799 args_length += arg.len + 1; // +1 to account for null terminator
1800 }
1801 if (args_length >= 30 * 1024) {
1802 const args_dir = try fs.path.join(
1803 builder.allocator,
1804 &[_][]const u8{ builder.pathFromRoot("zig-cache"), "args" },
1805 );
1806 try std.fs.cwd().makePath(args_dir);
1807
1808 var args_arena = std.heap.ArenaAllocator.init(builder.allocator);
1809 defer args_arena.deinit();
1810
1811 const args_to_escape = zig_args.items[2..];
1812 var escaped_args = try ArrayList([]const u8).initCapacity(args_arena.allocator(), args_to_escape.len);
1813
1814 arg_blk: for (args_to_escape) |arg| {
1815 for (arg) |c, arg_idx| {
1816 if (c == '\\' or c == '"') {
1817 // Slow path for arguments that need to be escaped. We'll need to allocate and copy
1818 var escaped = try ArrayList(u8).initCapacity(args_arena.allocator(), arg.len + 1);
1819 const writer = escaped.writer();
1820 writer.writeAll(arg[0..arg_idx]) catch unreachable;
1821 for (arg[arg_idx..]) |to_escape| {
1822 if (to_escape == '\\' or to_escape == '"') try writer.writeByte('\\');
1823 try writer.writeByte(to_escape);
1824 }
1825 escaped_args.appendAssumeCapacity(escaped.items);
1826 continue :arg_blk;
1827 }
1828 }
1829 escaped_args.appendAssumeCapacity(arg); // no escaping needed so just use original argument
1830 }
1831
1832 // Write the args to zig-cache/args/<SHA256 hash of args> to avoid conflicts with
1833 // other zig build commands running in parallel.
1834 const partially_quoted = try std.mem.join(builder.allocator, "\" \"", escaped_args.items);
1835 const args = try std.mem.concat(builder.allocator, u8, &[_][]const u8{ "\"", partially_quoted, "\"" });
1836
1837 var args_hash: [Sha256.digest_length]u8 = undefined;
1838 Sha256.hash(args, &args_hash, .{});
1839 var args_hex_hash: [Sha256.digest_length * 2]u8 = undefined;
1840 _ = try std.fmt.bufPrint(
1841 &args_hex_hash,
1842 "{s}",
1843 .{std.fmt.fmtSliceHexLower(&args_hash)},
1844 );
1845
1846 const args_file = try fs.path.join(builder.allocator, &[_][]const u8{ args_dir, args_hex_hash[0..] });
1847 try std.fs.cwd().writeFile(args_file, args);
1848
1849 zig_args.shrinkRetainingCapacity(2);
1850 try zig_args.append(try std.mem.concat(builder.allocator, u8, &[_][]const u8{ "@", args_file }));
1851 }
1852
1853 const output_dir_nl = try builder.execFromStep(zig_args.items, &self.step);
1854 const build_output_dir = mem.trimRight(u8, output_dir_nl, "\r\n");
1855
1856 if (self.output_dir) |output_dir| {
1857 var src_dir = try std.fs.cwd().openIterableDir(build_output_dir, .{});
1858 defer src_dir.close();
1859
1860 // Create the output directory if it doesn't exist.
1861 try std.fs.cwd().makePath(output_dir);
1862
1863 var dest_dir = try std.fs.cwd().openDir(output_dir, .{});
1864 defer dest_dir.close();
1865
1866 var it = src_dir.iterate();
1867 while (try it.next()) |entry| {
1868 // The compiler can put these files into the same directory, but we don't
1869 // want to copy them over.
1870 if (mem.eql(u8, entry.name, "llvm-ar.id") or
1871 mem.eql(u8, entry.name, "libs.txt") or
1872 mem.eql(u8, entry.name, "builtin.zig") or
1873 mem.eql(u8, entry.name, "zld.id") or
1874 mem.eql(u8, entry.name, "lld.id")) continue;
1875
1876 _ = try src_dir.dir.updateFile(entry.name, dest_dir, entry.name, .{});
1877 }
1878 } else {
1879 self.output_dir = build_output_dir;
1880 }
1881
1882 // This will ensure all output filenames will now have the output_dir available!
1883 self.computeOutFileNames();
1884
1885 // Update generated files
1886 if (self.output_dir != null) {
1887 self.output_path_source.path = builder.pathJoin(
1888 &.{ self.output_dir.?, self.out_filename },
1889 );
1890
1891 if (self.emit_h) {
1892 self.output_h_path_source.path = builder.pathJoin(
1893 &.{ self.output_dir.?, self.out_h_filename },
1894 );
1895 }
1896
1897 if (self.target.isWindows() or self.target.isUefi()) {
1898 self.output_pdb_path_source.path = builder.pathJoin(
1899 &.{ self.output_dir.?, self.out_pdb_filename },
1900 );
1901 }
1902 }
1903
1904 if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic and self.version != null and self.target.wantSharedLibSymLinks()) {
1905 try doAtomicSymLinks(builder.allocator, self.getOutputSource().getPath(builder), self.major_only_filename.?, self.name_only_filename.?);
1906 }
1907}
1908
1909fn isLibCLibrary(name: []const u8) bool {
1910 const libc_libraries = [_][]const u8{ "c", "m", "dl", "rt", "pthread" };
1911 for (libc_libraries) |libc_lib_name| {
1912 if (mem.eql(u8, name, libc_lib_name))
1913 return true;
1914 }
1915 return false;
1916}
1917
1918fn isLibCppLibrary(name: []const u8) bool {
1919 const libcpp_libraries = [_][]const u8{ "c++", "stdc++" };
1920 for (libcpp_libraries) |libcpp_lib_name| {
1921 if (mem.eql(u8, name, libcpp_lib_name))
1922 return true;
1923 }
1924 return false;
1925}
1926
1927/// Returned slice must be freed by the caller.
1928fn findVcpkgRoot(allocator: Allocator) !?[]const u8 {
1929 const appdata_path = try fs.getAppDataDir(allocator, "vcpkg");
1930 defer allocator.free(appdata_path);
1931
1932 const path_file = try fs.path.join(allocator, &[_][]const u8{ appdata_path, "vcpkg.path.txt" });
1933 defer allocator.free(path_file);
1934
1935 const file = fs.cwd().openFile(path_file, .{}) catch return null;
1936 defer file.close();
1937
1938 const size = @intCast(usize, try file.getEndPos());
1939 const vcpkg_path = try allocator.alloc(u8, size);
1940 const size_read = try file.read(vcpkg_path);
1941 std.debug.assert(size == size_read);
1942
1943 return vcpkg_path;
1944}
1945
1946pub fn doAtomicSymLinks(allocator: Allocator, output_path: []const u8, filename_major_only: []const u8, filename_name_only: []const u8) !void {
1947 const out_dir = fs.path.dirname(output_path) orelse ".";
1948 const out_basename = fs.path.basename(output_path);
1949 // sym link for libfoo.so.1 to libfoo.so.1.2.3
1950 const major_only_path = fs.path.join(
1951 allocator,
1952 &[_][]const u8{ out_dir, filename_major_only },
1953 ) catch unreachable;
1954 fs.atomicSymLink(allocator, out_basename, major_only_path) catch |err| {
1955 log.err("Unable to symlink {s} -> {s}", .{ major_only_path, out_basename });
1956 return err;
1957 };
1958 // sym link for libfoo.so to libfoo.so.1
1959 const name_only_path = fs.path.join(
1960 allocator,
1961 &[_][]const u8{ out_dir, filename_name_only },
1962 ) catch unreachable;
1963 fs.atomicSymLink(allocator, filename_major_only, name_only_path) catch |err| {
1964 log.err("Unable to symlink {s} -> {s}", .{ name_only_path, filename_major_only });
1965 return err;
1966 };
1967}
1968
1969fn execPkgConfigList(self: *Builder, out_code: *u8) (PkgConfigError || ExecError)![]const PkgConfigPkg {
1970 const stdout = try self.execAllowFail(&[_][]const u8{ "pkg-config", "--list-all" }, out_code, .Ignore);
1971 var list = ArrayList(PkgConfigPkg).init(self.allocator);
1972 errdefer list.deinit();
1973 var line_it = mem.tokenize(u8, stdout, "\r\n");
1974 while (line_it.next()) |line| {
1975 if (mem.trim(u8, line, " \t").len == 0) continue;
1976 var tok_it = mem.tokenize(u8, line, " \t");
1977 try list.append(PkgConfigPkg{
1978 .name = tok_it.next() orelse return error.PkgConfigInvalidOutput,
1979 .desc = tok_it.rest(),
1980 });
1981 }
1982 return list.toOwnedSlice();
1983}
1984
1985fn getPkgConfigList(self: *Builder) ![]const PkgConfigPkg {
1986 if (self.pkg_config_pkg_list) |res| {
1987 return res;
1988 }
1989 var code: u8 = undefined;
1990 if (execPkgConfigList(self, &code)) |list| {
1991 self.pkg_config_pkg_list = list;
1992 return list;
1993 } else |err| {
1994 const result = switch (err) {
1995 error.ProcessTerminated => error.PkgConfigCrashed,
1996 error.ExecNotSupported => error.PkgConfigFailed,
1997 error.ExitCodeFailure => error.PkgConfigFailed,
1998 error.FileNotFound => error.PkgConfigNotInstalled,
1999 error.InvalidName => error.PkgConfigNotInstalled,
2000 error.PkgConfigInvalidOutput => error.PkgConfigInvalidOutput,
2001 error.ChildExecFailed => error.PkgConfigFailed,
2002 else => return err,
2003 };
2004 self.pkg_config_pkg_list = result;
2005 return result;
2006 }
2007}
2008
2009test "addPackage" {
2010 if (builtin.os.tag == .wasi) return error.SkipZigTest;
2011
2012 var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
2013 defer arena.deinit();
2014
2015 var builder = try Builder.create(
2016 arena.allocator(),
2017 "test",
2018 "test",
2019 "test",
2020 "test",
2021 );
2022 defer builder.destroy();
2023
2024 const pkg_dep = Pkg{
2025 .name = "pkg_dep",
2026 .source = .{ .path = "/not/a/pkg_dep.zig" },
2027 };
2028 const pkg_top = Pkg{
2029 .name = "pkg_dep",
2030 .source = .{ .path = "/not/a/pkg_top.zig" },
2031 .dependencies = &[_]Pkg{pkg_dep},
2032 };
2033
2034 var exe = builder.addExecutable("not_an_executable", "/not/an/executable.zig");
2035 exe.addPackage(pkg_top);
2036
2037 try std.testing.expectEqual(@as(usize, 1), exe.packages.items.len);
2038
2039 const dupe = exe.packages.items[0];
2040 try std.testing.expectEqualStrings(pkg_top.name, dupe.name);
2041}
2042
2043fn addFlag(args: *ArrayList([]const u8), comptime name: []const u8, opt: ?bool) !void {
2044 const cond = opt orelse return;
2045 try args.ensureUnusedCapacity(1);
2046 if (cond) {
2047 args.appendAssumeCapacity("-f" ++ name);
2048 } else {
2049 args.appendAssumeCapacity("-fno-" ++ name);
2050 }
2051}
2052
2053const TransitiveDeps = struct {
2054 link_objects: ArrayList(LinkObject),
2055 seen_system_libs: StringHashMap(void),
2056 seen_steps: std.AutoHashMap(*const Step, void),
2057 is_linking_libcpp: bool,
2058 is_linking_libc: bool,
2059 frameworks: *StringHashMap(FrameworkLinkInfo),
2060
2061 fn add(td: *TransitiveDeps, link_objects: []const LinkObject) !void {
2062 try td.link_objects.ensureUnusedCapacity(link_objects.len);
2063
2064 for (link_objects) |link_object| {
2065 try td.link_objects.append(link_object);
2066 switch (link_object) {
2067 .other_step => |other| try addInner(td, other, other.isDynamicLibrary()),
2068 else => {},
2069 }
2070 }
2071 }
2072
2073 fn addInner(td: *TransitiveDeps, other: *LibExeObjStep, dyn: bool) !void {
2074 // Inherit dependency on libc and libc++
2075 td.is_linking_libcpp = td.is_linking_libcpp or other.is_linking_libcpp;
2076 td.is_linking_libc = td.is_linking_libc or other.is_linking_libc;
2077
2078 // Inherit dependencies on darwin frameworks
2079 if (!dyn) {
2080 var it = other.frameworks.iterator();
2081 while (it.next()) |framework| {
2082 try td.frameworks.put(framework.key_ptr.*, framework.value_ptr.*);
2083 }
2084 }
2085
2086 // Inherit dependencies on system libraries and static libraries.
2087 for (other.link_objects.items) |other_link_object| {
2088 switch (other_link_object) {
2089 .system_lib => |system_lib| {
2090 if ((try td.seen_system_libs.fetchPut(system_lib.name, {})) != null)
2091 continue;
2092
2093 if (dyn)
2094 continue;
2095
2096 try td.link_objects.append(other_link_object);
2097 },
2098 .other_step => |inner_other| {
2099 if ((try td.seen_steps.fetchPut(&inner_other.step, {})) != null)
2100 continue;
2101
2102 if (!dyn)
2103 try td.link_objects.append(other_link_object);
2104
2105 try addInner(td, inner_other, dyn or inner_other.isDynamicLibrary());
2106 },
2107 else => continue,
2108 }
2109 }
2110 }
2111};
lib/std/build/LogStep.zig deleted-25
...@@ -1,25 +0,0 @@
1const std = @import("../std.zig");
2const log = std.log;
3const build = @import("../build.zig");
4const Step = build.Step;
5const Builder = build.Builder;
6const LogStep = @This();
7
8pub const base_id = .log;
9
10step: Step,
11builder: *Builder,
12data: []const u8,
13
14pub fn init(builder: *Builder, data: []const u8) LogStep {
15 return LogStep{
16 .builder = builder,
17 .step = Step.init(.log, builder.fmt("log {s}", .{data}), builder.allocator, make),
18 .data = builder.dupe(data),
19 };
20}
21
22fn make(step: *Step) anyerror!void {
23 const self = @fieldParentPtr(LogStep, "step", step);
24 log.info("{s}", .{self.data});
25}
lib/std/build/OptionsStep.zig deleted-365
...@@ -1,365 +0,0 @@
1const std = @import("../std.zig");
2const builtin = @import("builtin");
3const build = std.build;
4const fs = std.fs;
5const Step = build.Step;
6const Builder = build.Builder;
7const GeneratedFile = build.GeneratedFile;
8const LibExeObjStep = build.LibExeObjStep;
9const FileSource = build.FileSource;
10
11const OptionsStep = @This();
12
13pub const base_id = .options;
14
15step: Step,
16generated_file: GeneratedFile,
17builder: *Builder,
18
19contents: std.ArrayList(u8),
20artifact_args: std.ArrayList(OptionArtifactArg),
21file_source_args: std.ArrayList(OptionFileSourceArg),
22
23pub fn create(builder: *Builder) *OptionsStep {
24 const self = builder.allocator.create(OptionsStep) catch unreachable;
25 self.* = .{
26 .builder = builder,
27 .step = Step.init(.options, "options", builder.allocator, make),
28 .generated_file = undefined,
29 .contents = std.ArrayList(u8).init(builder.allocator),
30 .artifact_args = std.ArrayList(OptionArtifactArg).init(builder.allocator),
31 .file_source_args = std.ArrayList(OptionFileSourceArg).init(builder.allocator),
32 };
33 self.generated_file = .{ .step = &self.step };
34
35 return self;
36}
37
38pub fn addOption(self: *OptionsStep, comptime T: type, name: []const u8, value: T) void {
39 const out = self.contents.writer();
40 switch (T) {
41 []const []const u8 => {
42 out.print("pub const {}: []const []const u8 = &[_][]const u8{{\n", .{std.zig.fmtId(name)}) catch unreachable;
43 for (value) |slice| {
44 out.print(" \"{}\",\n", .{std.zig.fmtEscapes(slice)}) catch unreachable;
45 }
46 out.writeAll("};\n") catch unreachable;
47 return;
48 },
49 [:0]const u8 => {
50 out.print("pub const {}: [:0]const u8 = \"{}\";\n", .{ std.zig.fmtId(name), std.zig.fmtEscapes(value) }) catch unreachable;
51 return;
52 },
53 []const u8 => {
54 out.print("pub const {}: []const u8 = \"{}\";\n", .{ std.zig.fmtId(name), std.zig.fmtEscapes(value) }) catch unreachable;
55 return;
56 },
57 ?[:0]const u8 => {
58 out.print("pub const {}: ?[:0]const u8 = ", .{std.zig.fmtId(name)}) catch unreachable;
59 if (value) |payload| {
60 out.print("\"{}\";\n", .{std.zig.fmtEscapes(payload)}) catch unreachable;
61 } else {
62 out.writeAll("null;\n") catch unreachable;
63 }
64 return;
65 },
66 ?[]const u8 => {
67 out.print("pub const {}: ?[]const u8 = ", .{std.zig.fmtId(name)}) catch unreachable;
68 if (value) |payload| {
69 out.print("\"{}\";\n", .{std.zig.fmtEscapes(payload)}) catch unreachable;
70 } else {
71 out.writeAll("null;\n") catch unreachable;
72 }
73 return;
74 },
75 std.builtin.Version => {
76 out.print(
77 \\pub const {}: @import("std").builtin.Version = .{{
78 \\ .major = {d},
79 \\ .minor = {d},
80 \\ .patch = {d},
81 \\}};
82 \\
83 , .{
84 std.zig.fmtId(name),
85
86 value.major,
87 value.minor,
88 value.patch,
89 }) catch unreachable;
90 return;
91 },
92 std.SemanticVersion => {
93 out.print(
94 \\pub const {}: @import("std").SemanticVersion = .{{
95 \\ .major = {d},
96 \\ .minor = {d},
97 \\ .patch = {d},
98 \\
99 , .{
100 std.zig.fmtId(name),
101
102 value.major,
103 value.minor,
104 value.patch,
105 }) catch unreachable;
106 if (value.pre) |some| {
107 out.print(" .pre = \"{}\",\n", .{std.zig.fmtEscapes(some)}) catch unreachable;
108 }
109 if (value.build) |some| {
110 out.print(" .build = \"{}\",\n", .{std.zig.fmtEscapes(some)}) catch unreachable;
111 }
112 out.writeAll("};\n") catch unreachable;
113 return;
114 },
115 else => {},
116 }
117 switch (@typeInfo(T)) {
118 .Enum => |enum_info| {
119 out.print("pub const {} = enum {{\n", .{std.zig.fmtId(@typeName(T))}) catch unreachable;
120 inline for (enum_info.fields) |field| {
121 out.print(" {},\n", .{std.zig.fmtId(field.name)}) catch unreachable;
122 }
123 out.writeAll("};\n") catch unreachable;
124 out.print("pub const {}: {s} = {s}.{s};\n", .{
125 std.zig.fmtId(name),
126 std.zig.fmtId(@typeName(T)),
127 std.zig.fmtId(@typeName(T)),
128 std.zig.fmtId(@tagName(value)),
129 }) catch unreachable;
130 return;
131 },
132 else => {},
133 }
134 out.print("pub const {}: {s} = ", .{ std.zig.fmtId(name), @typeName(T) }) catch unreachable;
135 printLiteral(out, value, 0) catch unreachable;
136 out.writeAll(";\n") catch unreachable;
137}
138
139// TODO: non-recursive?
140fn printLiteral(out: anytype, val: anytype, indent: u8) !void {
141 const T = @TypeOf(val);
142 switch (@typeInfo(T)) {
143 .Array => {
144 try out.print("{s} {{\n", .{@typeName(T)});
145 for (val) |item| {
146 try out.writeByteNTimes(' ', indent + 4);
147 try printLiteral(out, item, indent + 4);
148 try out.writeAll(",\n");
149 }
150 try out.writeByteNTimes(' ', indent);
151 try out.writeAll("}");
152 },
153 .Pointer => |p| {
154 if (p.size != .Slice) {
155 @compileError("Non-slice pointers are not yet supported in build options");
156 }
157 try out.print("&[_]{s} {{\n", .{@typeName(p.child)});
158 for (val) |item| {
159 try out.writeByteNTimes(' ', indent + 4);
160 try printLiteral(out, item, indent + 4);
161 try out.writeAll(",\n");
162 }
163 try out.writeByteNTimes(' ', indent);
164 try out.writeAll("}");
165 },
166 .Optional => {
167 if (val) |inner| {
168 return printLiteral(out, inner, indent);
169 } else {
170 return out.writeAll("null");
171 }
172 },
173 .Void,
174 .Bool,
175 .Int,
176 .ComptimeInt,
177 .Float,
178 .Null,
179 => try out.print("{any}", .{val}),
180 else => @compileError(std.fmt.comptimePrint("`{s}` are not yet supported as build options", .{@tagName(@typeInfo(T))})),
181 }
182}
183
184/// The value is the path in the cache dir.
185/// Adds a dependency automatically.
186pub fn addOptionFileSource(
187 self: *OptionsStep,
188 name: []const u8,
189 source: FileSource,
190) void {
191 self.file_source_args.append(.{
192 .name = name,
193 .source = source.dupe(self.builder),
194 }) catch unreachable;
195 source.addStepDependencies(&self.step);
196}
197
198/// The value is the path in the cache dir.
199/// Adds a dependency automatically.
200pub fn addOptionArtifact(self: *OptionsStep, name: []const u8, artifact: *LibExeObjStep) void {
201 self.artifact_args.append(.{ .name = self.builder.dupe(name), .artifact = artifact }) catch unreachable;
202 self.step.dependOn(&artifact.step);
203}
204
205pub fn getPackage(self: *OptionsStep, package_name: []const u8) build.Pkg {
206 return .{ .name = package_name, .source = self.getSource() };
207}
208
209pub fn getSource(self: *OptionsStep) FileSource {
210 return .{ .generated = &self.generated_file };
211}
212
213fn make(step: *Step) !void {
214 const self = @fieldParentPtr(OptionsStep, "step", step);
215
216 for (self.artifact_args.items) |item| {
217 self.addOption(
218 []const u8,
219 item.name,
220 self.builder.pathFromRoot(item.artifact.getOutputSource().getPath(self.builder)),
221 );
222 }
223
224 for (self.file_source_args.items) |item| {
225 self.addOption(
226 []const u8,
227 item.name,
228 item.source.getPath(self.builder),
229 );
230 }
231
232 const options_directory = self.builder.pathFromRoot(
233 try fs.path.join(
234 self.builder.allocator,
235 &[_][]const u8{ self.builder.cache_root, "options" },
236 ),
237 );
238
239 try fs.cwd().makePath(options_directory);
240
241 const options_file = try fs.path.join(
242 self.builder.allocator,
243 &[_][]const u8{ options_directory, &self.hashContentsToFileName() },
244 );
245
246 try fs.cwd().writeFile(options_file, self.contents.items);
247
248 self.generated_file.path = options_file;
249}
250
251fn hashContentsToFileName(self: *OptionsStep) [64]u8 {
252 // This implementation is copied from `WriteFileStep.make`
253
254 var hash = std.crypto.hash.blake2.Blake2b384.init(.{});
255
256 // Random bytes to make OptionsStep unique. Refresh this with
257 // new random bytes when OptionsStep implementation is modified
258 // in a non-backwards-compatible way.
259 hash.update("yL0Ya4KkmcCjBlP8");
260 hash.update(self.contents.items);
261
262 var digest: [48]u8 = undefined;
263 hash.final(&digest);
264 var hash_basename: [64]u8 = undefined;
265 _ = fs.base64_encoder.encode(&hash_basename, &digest);
266 return hash_basename;
267}
268
269const OptionArtifactArg = struct {
270 name: []const u8,
271 artifact: *LibExeObjStep,
272};
273
274const OptionFileSourceArg = struct {
275 name: []const u8,
276 source: FileSource,
277};
278
279test "OptionsStep" {
280 if (builtin.os.tag == .wasi) return error.SkipZigTest;
281
282 var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
283 defer arena.deinit();
284 var builder = try Builder.create(
285 arena.allocator(),
286 "test",
287 "test",
288 "test",
289 "test",
290 );
291 defer builder.destroy();
292
293 const options = builder.addOptions();
294
295 // TODO this regressed at some point
296 //const KeywordEnum = enum {
297 // @"0.8.1",
298 //};
299
300 const nested_array = [2][2]u16{
301 [2]u16{ 300, 200 },
302 [2]u16{ 300, 200 },
303 };
304 const nested_slice: []const []const u16 = &[_][]const u16{ &nested_array[0], &nested_array[1] };
305
306 options.addOption(usize, "option1", 1);
307 options.addOption(?usize, "option2", null);
308 options.addOption(?usize, "option3", 3);
309 options.addOption(comptime_int, "option4", 4);
310 options.addOption([]const u8, "string", "zigisthebest");
311 options.addOption(?[]const u8, "optional_string", null);
312 options.addOption([2][2]u16, "nested_array", nested_array);
313 options.addOption([]const []const u16, "nested_slice", nested_slice);
314 //options.addOption(KeywordEnum, "keyword_enum", .@"0.8.1");
315 options.addOption(std.builtin.Version, "version", try std.builtin.Version.parse("0.1.2"));
316 options.addOption(std.SemanticVersion, "semantic_version", try std.SemanticVersion.parse("0.1.2-foo+bar"));
317
318 try std.testing.expectEqualStrings(
319 \\pub const option1: usize = 1;
320 \\pub const option2: ?usize = null;
321 \\pub const option3: ?usize = 3;
322 \\pub const option4: comptime_int = 4;
323 \\pub const string: []const u8 = "zigisthebest";
324 \\pub const optional_string: ?[]const u8 = null;
325 \\pub const nested_array: [2][2]u16 = [2][2]u16 {
326 \\ [2]u16 {
327 \\ 300,
328 \\ 200,
329 \\ },
330 \\ [2]u16 {
331 \\ 300,
332 \\ 200,
333 \\ },
334 \\};
335 \\pub const nested_slice: []const []const u16 = &[_][]const u16 {
336 \\ &[_]u16 {
337 \\ 300,
338 \\ 200,
339 \\ },
340 \\ &[_]u16 {
341 \\ 300,
342 \\ 200,
343 \\ },
344 \\};
345 //\\pub const KeywordEnum = enum {
346 //\\ @"0.8.1",
347 //\\};
348 //\\pub const keyword_enum: KeywordEnum = KeywordEnum.@"0.8.1";
349 \\pub const version: @import("std").builtin.Version = .{
350 \\ .major = 0,
351 \\ .minor = 1,
352 \\ .patch = 2,
353 \\};
354 \\pub const semantic_version: @import("std").SemanticVersion = .{
355 \\ .major = 0,
356 \\ .minor = 1,
357 \\ .patch = 2,
358 \\ .pre = "foo",
359 \\ .build = "bar",
360 \\};
361 \\
362 , options.contents.items);
363
364 _ = try std.zig.parse(arena.allocator(), try options.contents.toOwnedSliceSentinel(0));
365}
lib/std/build/RemoveDirStep.zig deleted-31
...@@ -1,31 +0,0 @@
1const std = @import("../std.zig");
2const log = std.log;
3const fs = std.fs;
4const build = @import("../build.zig");
5const Step = build.Step;
6const Builder = build.Builder;
7const RemoveDirStep = @This();
8
9pub const base_id = .remove_dir;
10
11step: Step,
12builder: *Builder,
13dir_path: []const u8,
14
15pub fn init(builder: *Builder, dir_path: []const u8) RemoveDirStep {
16 return RemoveDirStep{
17 .builder = builder,
18 .step = Step.init(.remove_dir, builder.fmt("RemoveDir {s}", .{dir_path}), builder.allocator, make),
19 .dir_path = builder.dupePath(dir_path),
20 };
21}
22
23fn make(step: *Step) !void {
24 const self = @fieldParentPtr(RemoveDirStep, "step", step);
25
26 const full_path = self.builder.pathFromRoot(self.dir_path);
27 fs.cwd().deleteTree(full_path) catch |err| {
28 log.err("Unable to remove {s}: {s}", .{ full_path, @errorName(err) });
29 return err;
30 };
31}
lib/std/build/RunStep.zig deleted-378
...@@ -1,378 +0,0 @@
1const std = @import("../std.zig");
2const builtin = @import("builtin");
3const build = std.build;
4const Step = build.Step;
5const Builder = build.Builder;
6const LibExeObjStep = build.LibExeObjStep;
7const WriteFileStep = build.WriteFileStep;
8const fs = std.fs;
9const mem = std.mem;
10const process = std.process;
11const ArrayList = std.ArrayList;
12const EnvMap = process.EnvMap;
13const Allocator = mem.Allocator;
14const ExecError = build.Builder.ExecError;
15
16const max_stdout_size = 1 * 1024 * 1024; // 1 MiB
17
18const RunStep = @This();
19
20pub const base_id: Step.Id = .run;
21
22step: Step,
23builder: *Builder,
24
25/// See also addArg and addArgs to modifying this directly
26argv: ArrayList(Arg),
27
28/// Set this to modify the current working directory
29cwd: ?[]const u8,
30
31/// Override this field to modify the environment, or use setEnvironmentVariable
32env_map: ?*EnvMap,
33
34stdout_action: StdIoAction = .inherit,
35stderr_action: StdIoAction = .inherit,
36
37stdin_behavior: std.ChildProcess.StdIo = .Inherit,
38
39/// Set this to `null` to ignore the exit code for the purpose of determining a successful execution
40expected_exit_code: ?u8 = 0,
41
42/// Print the command before running it
43print: bool,
44
45pub const StdIoAction = union(enum) {
46 inherit,
47 ignore,
48 expect_exact: []const u8,
49 expect_matches: []const []const u8,
50};
51
52pub const Arg = union(enum) {
53 artifact: *LibExeObjStep,
54 file_source: build.FileSource,
55 bytes: []u8,
56};
57
58pub fn create(builder: *Builder, name: []const u8) *RunStep {
59 const self = builder.allocator.create(RunStep) catch unreachable;
60 self.* = RunStep{
61 .builder = builder,
62 .step = Step.init(base_id, name, builder.allocator, make),
63 .argv = ArrayList(Arg).init(builder.allocator),
64 .cwd = null,
65 .env_map = null,
66 .print = builder.verbose,
67 };
68 return self;
69}
70
71pub fn addArtifactArg(self: *RunStep, artifact: *LibExeObjStep) void {
72 self.argv.append(Arg{ .artifact = artifact }) catch unreachable;
73 self.step.dependOn(&artifact.step);
74}
75
76pub fn addFileSourceArg(self: *RunStep, file_source: build.FileSource) void {
77 self.argv.append(Arg{
78 .file_source = file_source.dupe(self.builder),
79 }) catch unreachable;
80 file_source.addStepDependencies(&self.step);
81}
82
83pub fn addArg(self: *RunStep, arg: []const u8) void {
84 self.argv.append(Arg{ .bytes = self.builder.dupe(arg) }) catch unreachable;
85}
86
87pub fn addArgs(self: *RunStep, args: []const []const u8) void {
88 for (args) |arg| {
89 self.addArg(arg);
90 }
91}
92
93pub fn clearEnvironment(self: *RunStep) void {
94 const new_env_map = self.builder.allocator.create(EnvMap) catch unreachable;
95 new_env_map.* = EnvMap.init(self.builder.allocator);
96 self.env_map = new_env_map;
97}
98
99pub fn addPathDir(self: *RunStep, search_path: []const u8) void {
100 addPathDirInternal(&self.step, self.builder, search_path);
101}
102
103/// For internal use only, users of `RunStep` should use `addPathDir` directly.
104pub fn addPathDirInternal(step: *Step, builder: *Builder, search_path: []const u8) void {
105 const env_map = getEnvMapInternal(step, builder.allocator);
106
107 const key = "PATH";
108 var prev_path = env_map.get(key);
109
110 if (prev_path) |pp| {
111 const new_path = builder.fmt("{s}" ++ [1]u8{fs.path.delimiter} ++ "{s}", .{ pp, search_path });
112 env_map.put(key, new_path) catch unreachable;
113 } else {
114 env_map.put(key, builder.dupePath(search_path)) catch unreachable;
115 }
116}
117
118pub fn getEnvMap(self: *RunStep) *EnvMap {
119 return getEnvMapInternal(&self.step, self.builder.allocator);
120}
121
122fn getEnvMapInternal(step: *Step, allocator: Allocator) *EnvMap {
123 const maybe_env_map = switch (step.id) {
124 .run => step.cast(RunStep).?.env_map,
125 .emulatable_run => step.cast(build.EmulatableRunStep).?.env_map,
126 else => unreachable,
127 };
128 return maybe_env_map orelse {
129 const env_map = allocator.create(EnvMap) catch unreachable;
130 env_map.* = process.getEnvMap(allocator) catch unreachable;
131 switch (step.id) {
132 .run => step.cast(RunStep).?.env_map = env_map,
133 .emulatable_run => step.cast(RunStep).?.env_map = env_map,
134 else => unreachable,
135 }
136 return env_map;
137 };
138}
139
140pub fn setEnvironmentVariable(self: *RunStep, key: []const u8, value: []const u8) void {
141 const env_map = self.getEnvMap();
142 env_map.put(
143 self.builder.dupe(key),
144 self.builder.dupe(value),
145 ) catch unreachable;
146}
147
148pub fn expectStdErrEqual(self: *RunStep, bytes: []const u8) void {
149 self.stderr_action = .{ .expect_exact = self.builder.dupe(bytes) };
150}
151
152pub fn expectStdOutEqual(self: *RunStep, bytes: []const u8) void {
153 self.stdout_action = .{ .expect_exact = self.builder.dupe(bytes) };
154}
155
156fn stdIoActionToBehavior(action: StdIoAction) std.ChildProcess.StdIo {
157 return switch (action) {
158 .ignore => .Ignore,
159 .inherit => .Inherit,
160 .expect_exact, .expect_matches => .Pipe,
161 };
162}
163
164fn make(step: *Step) !void {
165 const self = @fieldParentPtr(RunStep, "step", step);
166
167 var argv_list = ArrayList([]const u8).init(self.builder.allocator);
168 for (self.argv.items) |arg| {
169 switch (arg) {
170 .bytes => |bytes| try argv_list.append(bytes),
171 .file_source => |file| try argv_list.append(file.getPath(self.builder)),
172 .artifact => |artifact| {
173 if (artifact.target.isWindows()) {
174 // On Windows we don't have rpaths so we have to add .dll search paths to PATH
175 self.addPathForDynLibs(artifact);
176 }
177 const executable_path = artifact.installed_path orelse artifact.getOutputSource().getPath(self.builder);
178 try argv_list.append(executable_path);
179 },
180 }
181 }
182
183 try runCommand(
184 argv_list.items,
185 self.builder,
186 self.expected_exit_code,
187 self.stdout_action,
188 self.stderr_action,
189 self.stdin_behavior,
190 self.env_map,
191 self.cwd,
192 self.print,
193 );
194}
195
196pub fn runCommand(
197 argv: []const []const u8,
198 builder: *Builder,
199 expected_exit_code: ?u8,
200 stdout_action: StdIoAction,
201 stderr_action: StdIoAction,
202 stdin_behavior: std.ChildProcess.StdIo,
203 env_map: ?*EnvMap,
204 maybe_cwd: ?[]const u8,
205 print: bool,
206) !void {
207 const cwd = if (maybe_cwd) |cwd| builder.pathFromRoot(cwd) else builder.build_root;
208
209 if (!std.process.can_spawn) {
210 const cmd = try std.mem.join(builder.allocator, " ", argv);
211 std.debug.print("the following command cannot be executed ({s} does not support spawning a child process):\n{s}", .{ @tagName(builtin.os.tag), cmd });
212 builder.allocator.free(cmd);
213 return ExecError.ExecNotSupported;
214 }
215
216 var child = std.ChildProcess.init(argv, builder.allocator);
217 child.cwd = cwd;
218 child.env_map = env_map orelse builder.env_map;
219
220 child.stdin_behavior = stdin_behavior;
221 child.stdout_behavior = stdIoActionToBehavior(stdout_action);
222 child.stderr_behavior = stdIoActionToBehavior(stderr_action);
223
224 if (print)
225 printCmd(cwd, argv);
226
227 child.spawn() catch |err| {
228 std.debug.print("Unable to spawn {s}: {s}\n", .{ argv[0], @errorName(err) });
229 return err;
230 };
231
232 // TODO need to poll to read these streams to prevent a deadlock (or rely on evented I/O).
233
234 var stdout: ?[]const u8 = null;
235 defer if (stdout) |s| builder.allocator.free(s);
236
237 switch (stdout_action) {
238 .expect_exact, .expect_matches => {
239 stdout = child.stdout.?.reader().readAllAlloc(builder.allocator, max_stdout_size) catch unreachable;
240 },
241 .inherit, .ignore => {},
242 }
243
244 var stderr: ?[]const u8 = null;
245 defer if (stderr) |s| builder.allocator.free(s);
246
247 switch (stderr_action) {
248 .expect_exact, .expect_matches => {
249 stderr = child.stderr.?.reader().readAllAlloc(builder.allocator, max_stdout_size) catch unreachable;
250 },
251 .inherit, .ignore => {},
252 }
253
254 const term = child.wait() catch |err| {
255 std.debug.print("Unable to spawn {s}: {s}\n", .{ argv[0], @errorName(err) });
256 return err;
257 };
258
259 switch (term) {
260 .Exited => |code| blk: {
261 const expected_code = expected_exit_code orelse break :blk;
262
263 if (code != expected_code) {
264 if (builder.prominent_compile_errors) {
265 std.debug.print("Run step exited with error code {} (expected {})\n", .{
266 code,
267 expected_code,
268 });
269 } else {
270 std.debug.print("The following command exited with error code {} (expected {}):\n", .{
271 code,
272 expected_code,
273 });
274 printCmd(cwd, argv);
275 }
276
277 return error.UnexpectedExitCode;
278 }
279 },
280 else => {
281 std.debug.print("The following command terminated unexpectedly:\n", .{});
282 printCmd(cwd, argv);
283 return error.UncleanExit;
284 },
285 }
286
287 switch (stderr_action) {
288 .inherit, .ignore => {},
289 .expect_exact => |expected_bytes| {
290 if (!mem.eql(u8, expected_bytes, stderr.?)) {
291 std.debug.print(
292 \\
293 \\========= Expected this stderr: =========
294 \\{s}
295 \\========= But found: ====================
296 \\{s}
297 \\
298 , .{ expected_bytes, stderr.? });
299 printCmd(cwd, argv);
300 return error.TestFailed;
301 }
302 },
303 .expect_matches => |matches| for (matches) |match| {
304 if (mem.indexOf(u8, stderr.?, match) == null) {
305 std.debug.print(
306 \\
307 \\========= Expected to find in stderr: =========
308 \\{s}
309 \\========= But stderr does not contain it: =====
310 \\{s}
311 \\
312 , .{ match, stderr.? });
313 printCmd(cwd, argv);
314 return error.TestFailed;
315 }
316 },
317 }
318
319 switch (stdout_action) {
320 .inherit, .ignore => {},
321 .expect_exact => |expected_bytes| {
322 if (!mem.eql(u8, expected_bytes, stdout.?)) {
323 std.debug.print(
324 \\
325 \\========= Expected this stdout: =========
326 \\{s}
327 \\========= But found: ====================
328 \\{s}
329 \\
330 , .{ expected_bytes, stdout.? });
331 printCmd(cwd, argv);
332 return error.TestFailed;
333 }
334 },
335 .expect_matches => |matches| for (matches) |match| {
336 if (mem.indexOf(u8, stdout.?, match) == null) {
337 std.debug.print(
338 \\
339 \\========= Expected to find in stdout: =========
340 \\{s}
341 \\========= But stdout does not contain it: =====
342 \\{s}
343 \\
344 , .{ match, stdout.? });
345 printCmd(cwd, argv);
346 return error.TestFailed;
347 }
348 },
349 }
350}
351
352fn printCmd(cwd: ?[]const u8, argv: []const []const u8) void {
353 if (cwd) |yes_cwd| std.debug.print("cd {s} && ", .{yes_cwd});
354 for (argv) |arg| {
355 std.debug.print("{s} ", .{arg});
356 }
357 std.debug.print("\n", .{});
358}
359
360fn addPathForDynLibs(self: *RunStep, artifact: *LibExeObjStep) void {
361 addPathForDynLibsInternal(&self.step, self.builder, artifact);
362}
363
364/// This should only be used for internal usage, this is called automatically
365/// for the user.
366pub fn addPathForDynLibsInternal(step: *Step, builder: *Builder, artifact: *LibExeObjStep) void {
367 for (artifact.link_objects.items) |link_object| {
368 switch (link_object) {
369 .other_step => |other| {
370 if (other.target.isWindows() and other.isDynamicLibrary()) {
371 addPathDirInternal(step, builder, fs.path.dirname(other.getOutputSource().getPath(builder)).?);
372 addPathForDynLibsInternal(step, builder, other);
373 }
374 },
375 else => {},
376 }
377 }
378}
lib/std/build/TranslateCStep.zig deleted-112
...@@ -1,112 +0,0 @@
1const std = @import("../std.zig");
2const build = std.build;
3const Step = build.Step;
4const Builder = build.Builder;
5const LibExeObjStep = build.LibExeObjStep;
6const CheckFileStep = build.CheckFileStep;
7const fs = std.fs;
8const mem = std.mem;
9const CrossTarget = std.zig.CrossTarget;
10
11const TranslateCStep = @This();
12
13pub const base_id = .translate_c;
14
15step: Step,
16builder: *Builder,
17source: build.FileSource,
18include_dirs: std.ArrayList([]const u8),
19c_macros: std.ArrayList([]const u8),
20output_dir: ?[]const u8,
21out_basename: []const u8,
22target: CrossTarget = CrossTarget{},
23output_file: build.GeneratedFile,
24
25pub fn create(builder: *Builder, source: build.FileSource) *TranslateCStep {
26 const self = builder.allocator.create(TranslateCStep) catch unreachable;
27 self.* = TranslateCStep{
28 .step = Step.init(.translate_c, "translate-c", builder.allocator, make),
29 .builder = builder,
30 .source = source,
31 .include_dirs = std.ArrayList([]const u8).init(builder.allocator),
32 .c_macros = std.ArrayList([]const u8).init(builder.allocator),
33 .output_dir = null,
34 .out_basename = undefined,
35 .output_file = build.GeneratedFile{ .step = &self.step },
36 };
37 source.addStepDependencies(&self.step);
38 return self;
39}
40
41pub fn setTarget(self: *TranslateCStep, target: CrossTarget) void {
42 self.target = target;
43}
44
45/// Creates a step to build an executable from the translated source.
46pub fn addExecutable(self: *TranslateCStep) *LibExeObjStep {
47 return self.builder.addExecutableSource("translated_c", build.FileSource{ .generated = &self.output_file });
48}
49
50pub fn addIncludeDir(self: *TranslateCStep, include_dir: []const u8) void {
51 self.include_dirs.append(self.builder.dupePath(include_dir)) catch unreachable;
52}
53
54pub fn addCheckFile(self: *TranslateCStep, expected_matches: []const []const u8) *CheckFileStep {
55 return CheckFileStep.create(self.builder, .{ .generated = &self.output_file }, self.builder.dupeStrings(expected_matches));
56}
57
58/// If the value is omitted, it is set to 1.
59/// `name` and `value` need not live longer than the function call.
60pub fn defineCMacro(self: *TranslateCStep, name: []const u8, value: ?[]const u8) void {
61 const macro = build.constructCMacro(self.builder.allocator, name, value);
62 self.c_macros.append(macro) catch unreachable;
63}
64
65/// name_and_value looks like [name]=[value]. If the value is omitted, it is set to 1.
66pub fn defineCMacroRaw(self: *TranslateCStep, name_and_value: []const u8) void {
67 self.c_macros.append(self.builder.dupe(name_and_value)) catch unreachable;
68}
69
70fn make(step: *Step) !void {
71 const self = @fieldParentPtr(TranslateCStep, "step", step);
72
73 var argv_list = std.ArrayList([]const u8).init(self.builder.allocator);
74 try argv_list.append(self.builder.zig_exe);
75 try argv_list.append("translate-c");
76 try argv_list.append("-lc");
77
78 try argv_list.append("--enable-cache");
79
80 if (!self.target.isNative()) {
81 try argv_list.append("-target");
82 try argv_list.append(try self.target.zigTriple(self.builder.allocator));
83 }
84
85 for (self.include_dirs.items) |include_dir| {
86 try argv_list.append("-I");
87 try argv_list.append(include_dir);
88 }
89
90 for (self.c_macros.items) |c_macro| {
91 try argv_list.append("-D");
92 try argv_list.append(c_macro);
93 }
94
95 try argv_list.append(self.source.getPath(self.builder));
96
97 const output_path_nl = try self.builder.execFromStep(argv_list.items, &self.step);
98 const output_path = mem.trimRight(u8, output_path_nl, "\r\n");
99
100 self.out_basename = fs.path.basename(output_path);
101 if (self.output_dir) |output_dir| {
102 const full_dest = try fs.path.join(self.builder.allocator, &[_][]const u8{ output_dir, self.out_basename });
103 try self.builder.updateFile(output_path, full_dest);
104 } else {
105 self.output_dir = fs.path.dirname(output_path).?;
106 }
107
108 self.output_file.path = fs.path.join(
109 self.builder.allocator,
110 &[_][]const u8{ self.output_dir.?, self.out_basename },
111 ) catch unreachable;
112}
lib/std/build/WriteFileStep.zig deleted-117
...@@ -1,117 +0,0 @@
1const std = @import("../std.zig");
2const build = @import("../build.zig");
3const Step = build.Step;
4const Builder = build.Builder;
5const fs = std.fs;
6const ArrayList = std.ArrayList;
7
8const WriteFileStep = @This();
9
10pub const base_id = .write_file;
11
12step: Step,
13builder: *Builder,
14output_dir: []const u8,
15files: std.TailQueue(File),
16
17pub const File = struct {
18 source: build.GeneratedFile,
19 basename: []const u8,
20 bytes: []const u8,
21};
22
23pub fn init(builder: *Builder) WriteFileStep {
24 return WriteFileStep{
25 .builder = builder,
26 .step = Step.init(.write_file, "writefile", builder.allocator, make),
27 .files = .{},
28 .output_dir = undefined,
29 };
30}
31
32pub fn add(self: *WriteFileStep, basename: []const u8, bytes: []const u8) void {
33 const node = self.builder.allocator.create(std.TailQueue(File).Node) catch unreachable;
34 node.* = .{
35 .data = .{
36 .source = build.GeneratedFile{ .step = &self.step },
37 .basename = self.builder.dupePath(basename),
38 .bytes = self.builder.dupe(bytes),
39 },
40 };
41
42 self.files.append(node);
43}
44
45/// Gets a file source for the given basename. If the file does not exist, returns `null`.
46pub fn getFileSource(step: *WriteFileStep, basename: []const u8) ?build.FileSource {
47 var it = step.files.first;
48 while (it) |node| : (it = node.next) {
49 if (std.mem.eql(u8, node.data.basename, basename))
50 return build.FileSource{ .generated = &node.data.source };
51 }
52 return null;
53}
54
55fn make(step: *Step) !void {
56 const self = @fieldParentPtr(WriteFileStep, "step", step);
57
58 // The cache is used here not really as a way to speed things up - because writing
59 // the data to a file would probably be very fast - but as a way to find a canonical
60 // location to put build artifacts.
61
62 // If, for example, a hard-coded path was used as the location to put WriteFileStep
63 // files, then two WriteFileSteps executing in parallel might clobber each other.
64
65 // TODO port the cache system from the compiler to zig std lib. Until then
66 // we directly construct the path, and no "cache hit" detection happens;
67 // the files are always written.
68 // Note there is similar code over in ConfigHeaderStep.
69 const Hasher = std.crypto.auth.siphash.SipHash128(1, 3);
70 // Random bytes to make WriteFileStep unique. Refresh this with
71 // new random bytes when WriteFileStep implementation is modified
72 // in a non-backwards-compatible way.
73 var hash = Hasher.init("eagVR1dYXoE7ARDP");
74
75 {
76 var it = self.files.first;
77 while (it) |node| : (it = node.next) {
78 hash.update(node.data.basename);
79 hash.update(node.data.bytes);
80 hash.update("|");
81 }
82 }
83 var digest: [16]u8 = undefined;
84 hash.final(&digest);
85 var hash_basename: [digest.len * 2]u8 = undefined;
86 _ = std.fmt.bufPrint(
87 &hash_basename,
88 "{s}",
89 .{std.fmt.fmtSliceHexLower(&digest)},
90 ) catch unreachable;
91
92 self.output_dir = try fs.path.join(self.builder.allocator, &[_][]const u8{
93 self.builder.cache_root, "o", &hash_basename,
94 });
95 var dir = fs.cwd().makeOpenPath(self.output_dir, .{}) catch |err| {
96 std.debug.print("unable to make path {s}: {s}\n", .{ self.output_dir, @errorName(err) });
97 return err;
98 };
99 defer dir.close();
100 {
101 var it = self.files.first;
102 while (it) |node| : (it = node.next) {
103 dir.writeFile(node.data.basename, node.data.bytes) catch |err| {
104 std.debug.print("unable to write {s} into {s}: {s}\n", .{
105 node.data.basename,
106 self.output_dir,
107 @errorName(err),
108 });
109 return err;
110 };
111 node.data.source.path = fs.path.join(
112 self.builder.allocator,
113 &[_][]const u8{ self.output_dir, node.data.basename },
114 ) catch unreachable;
115 }
116 }
117}
lib/std/builtin.zig+4-1
...@@ -131,13 +131,16 @@ pub const CodeModel = enum {...@@ -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/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/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+1-1
...@@ -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/math/big.zig-1
...@@ -13,7 +13,6 @@ pub const Log2Limb = std.math.Log2Int(Limb);...@@ -13,7 +13,6 @@ pub const Log2Limb = std.math.Log2Int(Limb);
1313
14comptime {14comptime {
15 assert(std.math.floorPowerOfTwo(usize, limb_info.bits) == limb_info.bits);15 assert(std.math.floorPowerOfTwo(usize, limb_info.bits) == limb_info.bits);
16 assert(limb_info.bits <= 64); // u128 set is unsupported
17 assert(limb_info.signedness == .unsigned);16 assert(limb_info.signedness == .unsigned);
18}17}
1918
lib/std/math/big/int.zig+3-8
...@@ -30,7 +30,7 @@ pub fn calcLimbLen(scalar: anytype) usize {...@@ -30,7 +30,7 @@ pub fn calcLimbLen(scalar: anytype) usize {
30 }30 }
3131
32 const w_value = std.math.absCast(scalar);32 const w_value = std.math.absCast(scalar);
33 return @divFloor(@intCast(Limb, math.log2(w_value)), limb_bits) + 1;33 return @intCast(usize, @divFloor(@intCast(Limb, math.log2(w_value)), limb_bits) + 1);
34}34}
3535
36pub fn calcToStringLimbsBufferLen(a_len: usize, base: u8) usize {36pub fn calcToStringLimbsBufferLen(a_len: usize, base: u8) usize {
...@@ -238,10 +238,7 @@ pub const Mutable = struct {...@@ -238,10 +238,7 @@ pub const Mutable = struct {
238 var i: usize = 0;238 var i: usize = 0;
239 while (true) : (i += 1) {239 while (true) : (i += 1) {
240 self.limbs[i] = @truncate(Limb, w_value);240 self.limbs[i] = @truncate(Limb, w_value);
241241 w_value >>= limb_bits;
242 // TODO: shift == 64 at compile-time fails. Fails on u128 limbs.
243 w_value >>= limb_bits / 2;
244 w_value >>= limb_bits / 2;
245242
246 if (w_value == 0) break;243 if (w_value == 0) break;
247 }244 }
...@@ -258,9 +255,7 @@ pub const Mutable = struct {...@@ -258,9 +255,7 @@ pub const Mutable = struct {
258 comptime var i = 0;255 comptime var i = 0;
259 inline while (true) : (i += 1) {256 inline while (true) : (i += 1) {
260 self.limbs[i] = w_value & mask;257 self.limbs[i] = w_value & mask;
261258 w_value >>= limb_bits;
262 w_value >>= limb_bits / 2;
263 w_value >>= limb_bits / 2;
264259
265 if (w_value == 0) break;260 if (w_value == 0) break;
266 }261 }
lib/std/meta.zig+1-1
...@@ -332,7 +332,7 @@ pub fn Sentinel(comptime T: type, comptime sentinel_val: Elem(T)) type {...@@ -332,7 +332,7 @@ pub fn Sentinel(comptime T: type, comptime sentinel_val: Elem(T)) type {
332 @compileError("Unable to derive a sentinel pointer type from " ++ @typeName(T));332 @compileError("Unable to derive a sentinel pointer type from " ++ @typeName(T));
333}333}
334334
335const assumeSentinel = @compileError("This function has been removed, consider using std.mem.sliceTo() or if needed a @ptrCast()");335pub const assumeSentinel = @compileError("This function has been removed, consider using std.mem.sliceTo() or if needed a @ptrCast()");
336336
337pub fn containerLayout(comptime T: type) Type.ContainerLayout {337pub fn containerLayout(comptime T: type) Type.ContainerLayout {
338 return switch (@typeInfo(T)) {338 return switch (@typeInfo(T)) {
lib/std/os.zig+1-1
...@@ -4513,7 +4513,7 @@ pub fn faccessatW(dirfd: fd_t, sub_path_w: [*:0]const u16, mode: u32, flags: u32...@@ -4513,7 +4513,7 @@ pub fn faccessatW(dirfd: fd_t, sub_path_w: [*:0]const u16, mode: u32, flags: u32
4513 var nt_name = windows.UNICODE_STRING{4513 var nt_name = windows.UNICODE_STRING{
4514 .Length = path_len_bytes,4514 .Length = path_len_bytes,
4515 .MaximumLength = path_len_bytes,4515 .MaximumLength = path_len_bytes,
4516 .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w)),4516 .Buffer = @qualCast([*:0]u16, sub_path_w),
4517 };4517 };
4518 var attr = windows.OBJECT_ATTRIBUTES{4518 var attr = windows.OBJECT_ATTRIBUTES{
4519 .Length = @sizeOf(windows.OBJECT_ATTRIBUTES),4519 .Length = @sizeOf(windows.OBJECT_ATTRIBUTES),
lib/std/os/linux.zig+2
...@@ -40,6 +40,7 @@ const arch_bits = switch (native_arch) {...@@ -40,6 +40,7 @@ const arch_bits = switch (native_arch) {
40 .riscv64 => @import("linux/riscv64.zig"),40 .riscv64 => @import("linux/riscv64.zig"),
41 .sparc64 => @import("linux/sparc64.zig"),41 .sparc64 => @import("linux/sparc64.zig"),
42 .mips, .mipsel => @import("linux/mips.zig"),42 .mips, .mipsel => @import("linux/mips.zig"),
43 .mips64, .mips64el => @import("linux/mips64.zig"),
43 .powerpc => @import("linux/powerpc.zig"),44 .powerpc => @import("linux/powerpc.zig"),
44 .powerpc64, .powerpc64le => @import("linux/powerpc64.zig"),45 .powerpc64, .powerpc64le => @import("linux/powerpc64.zig"),
45 else => struct {},46 else => struct {},
...@@ -101,6 +102,7 @@ pub const SYS = switch (@import("builtin").cpu.arch) {...@@ -101,6 +102,7 @@ pub const SYS = switch (@import("builtin").cpu.arch) {
101 .riscv64 => syscalls.RiscV64,102 .riscv64 => syscalls.RiscV64,
102 .sparc64 => syscalls.Sparc64,103 .sparc64 => syscalls.Sparc64,
103 .mips, .mipsel => syscalls.Mips,104 .mips, .mipsel => syscalls.Mips,
105 .mips64, .mips64el => syscalls.Mips64,
104 .powerpc => syscalls.PowerPC,106 .powerpc => syscalls.PowerPC,
105 .powerpc64, .powerpc64le => syscalls.PowerPC64,107 .powerpc64, .powerpc64le => syscalls.PowerPC64,
106 else => @compileError("The Zig Standard Library is missing syscall definitions for the target CPU architecture"),108 else => @compileError("The Zig Standard Library is missing syscall definitions for the target CPU architecture"),
lib/std/os/linux/mips64.zig created+413
...@@ -0,0 +1,413 @@
1const std = @import("../../std.zig");
2const maxInt = std.math.maxInt;
3const linux = std.os.linux;
4const SYS = linux.SYS;
5const socklen_t = linux.socklen_t;
6const iovec = std.os.iovec;
7const iovec_const = std.os.iovec_const;
8const uid_t = linux.uid_t;
9const gid_t = linux.gid_t;
10const pid_t = linux.pid_t;
11const sockaddr = linux.sockaddr;
12const timespec = linux.timespec;
13
14pub fn syscall0(number: SYS) usize {
15 return asm volatile (
16 \\ syscall
17 \\ blez $7, 1f
18 \\ dsubu $2, $0, $2
19 \\ 1:
20 : [ret] "={$2}" (-> usize),
21 : [number] "{$2}" (@enumToInt(number)),
22 : "$1", "$3", "$4", "$5", "$6", "$7", "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24", "$25", "hi", "lo", "memory"
23 );
24}
25
26pub fn syscall_pipe(fd: *[2]i32) usize {
27 return asm volatile (
28 \\ .set noat
29 \\ .set noreorder
30 \\ syscall
31 \\ blez $7, 1f
32 \\ nop
33 \\ b 2f
34 \\ subu $2, $0, $2
35 \\ 1:
36 \\ sw $2, 0($4)
37 \\ sw $3, 4($4)
38 \\ 2:
39 : [ret] "={$2}" (-> usize),
40 : [number] "{$2}" (@enumToInt(SYS.pipe)),
41 [fd] "{$4}" (fd),
42 : "$1", "$3", "$5", "$6", "$7", "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24", "$25", "hi", "lo", "memory"
43 );
44}
45
46pub fn syscall1(number: SYS, arg1: usize) usize {
47 return asm volatile (
48 \\ syscall
49 \\ blez $7, 1f
50 \\ dsubu $2, $0, $2
51 \\ 1:
52 : [ret] "={$2}" (-> usize),
53 : [number] "{$2}" (@enumToInt(number)),
54 [arg1] "{$4}" (arg1),
55 : "$1", "$3", "$5", "$6", "$7", "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24", "$25", "hi", "lo", "memory"
56 );
57}
58
59pub fn syscall2(number: SYS, arg1: usize, arg2: usize) usize {
60 return asm volatile (
61 \\ syscall
62 \\ blez $7, 1f
63 \\ dsubu $2, $0, $2
64 \\ 1:
65 : [ret] "={$2}" (-> usize),
66 : [number] "{$2}" (@enumToInt(number)),
67 [arg1] "{$4}" (arg1),
68 [arg2] "{$5}" (arg2),
69 : "$1", "$3", "$6", "$7", "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24", "$25", "hi", "lo", "memory"
70 );
71}
72
73pub fn syscall3(number: SYS, arg1: usize, arg2: usize, arg3: usize) usize {
74 return asm volatile (
75 \\ syscall
76 \\ blez $7, 1f
77 \\ dsubu $2, $0, $2
78 \\ 1:
79 : [ret] "={$2}" (-> usize),
80 : [number] "{$2}" (@enumToInt(number)),
81 [arg1] "{$4}" (arg1),
82 [arg2] "{$5}" (arg2),
83 [arg3] "{$6}" (arg3),
84 : "$1", "$3", "$7", "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24", "$25", "hi", "lo", "memory"
85 );
86}
87
88pub fn syscall4(number: SYS, arg1: usize, arg2: usize, arg3: usize, arg4: usize) usize {
89 return asm volatile (
90 \\ syscall
91 \\ blez $7, 1f
92 \\ dsubu $2, $0, $2
93 \\ 1:
94 : [ret] "={$2}" (-> usize),
95 : [number] "{$2}" (@enumToInt(number)),
96 [arg1] "{$4}" (arg1),
97 [arg2] "{$5}" (arg2),
98 [arg3] "{$6}" (arg3),
99 [arg4] "{$7}" (arg4),
100 : "$1", "$3", "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24", "$25", "hi", "lo", "memory"
101 );
102}
103
104pub fn syscall5(number: SYS, arg1: usize, arg2: usize, arg3: usize, arg4: usize, arg5: usize) usize {
105 return asm volatile (
106 \\ syscall
107 \\ blez $7, 1f
108 \\ dsubu $2, $0, $2
109 \\ 1:
110 : [ret] "={$2}" (-> usize),
111 : [number] "{$2}" (@enumToInt(number)),
112 [arg1] "{$4}" (arg1),
113 [arg2] "{$5}" (arg2),
114 [arg3] "{$6}" (arg3),
115 [arg4] "{$7}" (arg4),
116 [arg5] "{$8}" (arg5),
117 : "$1", "$3", "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24", "$25", "hi", "lo", "memory"
118 );
119}
120
121// NOTE: The o32 calling convention requires the callee to reserve 16 bytes for
122// the first four arguments even though they're passed in $a0-$a3.
123
124pub fn syscall6(
125 number: SYS,
126 arg1: usize,
127 arg2: usize,
128 arg3: usize,
129 arg4: usize,
130 arg5: usize,
131 arg6: usize,
132) usize {
133 return asm volatile (
134 \\ syscall
135 \\ blez $7, 1f
136 \\ dsubu $2, $0, $2
137 \\ 1:
138 : [ret] "={$2}" (-> usize),
139 : [number] "{$2}" (@enumToInt(number)),
140 [arg1] "{$4}" (arg1),
141 [arg2] "{$5}" (arg2),
142 [arg3] "{$6}" (arg3),
143 [arg4] "{$7}" (arg4),
144 [arg5] "{$8}" (arg5),
145 [arg6] "{$9}" (arg6),
146 : "$1", "$3", "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24", "$25", "hi", "lo", "memory"
147 );
148}
149
150pub fn syscall7(
151 number: SYS,
152 arg1: usize,
153 arg2: usize,
154 arg3: usize,
155 arg4: usize,
156 arg5: usize,
157 arg6: usize,
158 arg7: usize,
159) usize {
160 return asm volatile (
161 \\ syscall
162 \\ blez $7, 1f
163 \\ dsubu $2, $0, $2
164 \\ 1:
165 : [ret] "={$2}" (-> usize),
166 : [number] "{$2}" (@enumToInt(number)),
167 [arg1] "{$4}" (arg1),
168 [arg2] "{$5}" (arg2),
169 [arg3] "{$6}" (arg3),
170 [arg4] "{$7}" (arg4),
171 [arg5] "{$8}" (arg5),
172 [arg6] "{$9}" (arg6),
173 [arg7] "{$10}" (arg7),
174 : "$1", "$3", "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24", "$25", "hi", "lo", "memory"
175 );
176}
177
178const CloneFn = *const fn (arg: usize) callconv(.C) u8;
179
180/// This matches the libc clone function.
181pub extern fn clone(func: CloneFn, stack: usize, flags: u32, arg: usize, ptid: *i32, tls: usize, ctid: *i32) usize;
182
183pub fn restore() callconv(.Naked) void {
184 return asm volatile ("syscall"
185 :
186 : [number] "{$2}" (@enumToInt(SYS.rt_sigreturn)),
187 : "$1", "$3", "$4", "$5", "$6", "$7", "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24", "$25", "hi", "lo", "memory"
188 );
189}
190
191pub fn restore_rt() callconv(.Naked) void {
192 return asm volatile ("syscall"
193 :
194 : [number] "{$2}" (@enumToInt(SYS.rt_sigreturn)),
195 : "$1", "$3", "$4", "$5", "$6", "$7", "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24", "$25", "hi", "lo", "memory"
196 );
197}
198
199pub const O = struct {
200 pub const CREAT = 0o0400;
201 pub const EXCL = 0o02000;
202 pub const NOCTTY = 0o04000;
203 pub const TRUNC = 0o01000;
204 pub const APPEND = 0o0010;
205 pub const NONBLOCK = 0o0200;
206 pub const DSYNC = 0o0020;
207 pub const SYNC = 0o040020;
208 pub const RSYNC = 0o040020;
209 pub const DIRECTORY = 0o0200000;
210 pub const NOFOLLOW = 0o0400000;
211 pub const CLOEXEC = 0o02000000;
212
213 pub const ASYNC = 0o010000;
214 pub const DIRECT = 0o0100000;
215 pub const LARGEFILE = 0o020000;
216 pub const NOATIME = 0o01000000;
217 pub const PATH = 0o010000000;
218 pub const TMPFILE = 0o020200000;
219 pub const NDELAY = NONBLOCK;
220};
221
222pub const F = struct {
223 pub const DUPFD = 0;
224 pub const GETFD = 1;
225 pub const SETFD = 2;
226 pub const GETFL = 3;
227 pub const SETFL = 4;
228
229 pub const SETOWN = 24;
230 pub const GETOWN = 23;
231 pub const SETSIG = 10;
232 pub const GETSIG = 11;
233
234 pub const GETLK = 33;
235 pub const SETLK = 34;
236 pub const SETLKW = 35;
237
238 pub const RDLCK = 0;
239 pub const WRLCK = 1;
240 pub const UNLCK = 2;
241
242 pub const SETOWN_EX = 15;
243 pub const GETOWN_EX = 16;
244
245 pub const GETOWNER_UIDS = 17;
246};
247
248pub const LOCK = struct {
249 pub const SH = 1;
250 pub const EX = 2;
251 pub const UN = 8;
252 pub const NB = 4;
253};
254
255pub const MMAP2_UNIT = 4096;
256
257pub const MAP = struct {
258 pub const NORESERVE = 0x0400;
259 pub const GROWSDOWN = 0x1000;
260 pub const DENYWRITE = 0x2000;
261 pub const EXECUTABLE = 0x4000;
262 pub const LOCKED = 0x8000;
263 pub const @"32BIT" = 0x40;
264};
265
266pub const VDSO = struct {
267 pub const CGT_SYM = "__kernel_clock_gettime";
268 pub const CGT_VER = "LINUX_2.6.39";
269};
270
271pub const Flock = extern struct {
272 type: i16,
273 whence: i16,
274 __pad0: [4]u8,
275 start: off_t,
276 len: off_t,
277 pid: pid_t,
278 __unused: [4]u8,
279};
280
281pub const msghdr = extern struct {
282 name: ?*sockaddr,
283 namelen: socklen_t,
284 iov: [*]iovec,
285 iovlen: i32,
286 control: ?*anyopaque,
287 controllen: socklen_t,
288 flags: i32,
289};
290
291pub const msghdr_const = extern struct {
292 name: ?*const sockaddr,
293 namelen: socklen_t,
294 iov: [*]const iovec_const,
295 iovlen: i32,
296 control: ?*const anyopaque,
297 controllen: socklen_t,
298 flags: i32,
299};
300
301pub const blksize_t = i32;
302pub const nlink_t = u32;
303pub const time_t = i32;
304pub const mode_t = u32;
305pub const off_t = i64;
306pub const ino_t = u64;
307pub const dev_t = u64;
308pub const blkcnt_t = i64;
309
310// The `stat` definition used by the Linux kernel.
311pub const Stat = extern struct {
312 dev: u32,
313 __pad0: [3]u32, // Reserved for st_dev expansion
314 ino: ino_t,
315 mode: mode_t,
316 nlink: nlink_t,
317 uid: uid_t,
318 gid: gid_t,
319 rdev: u32,
320 __pad1: [3]u32,
321 size: off_t,
322 atim: timespec,
323 mtim: timespec,
324 ctim: timespec,
325 blksize: blksize_t,
326 __pad3: u32,
327 blocks: blkcnt_t,
328 __pad4: [14]usize,
329
330 pub fn atime(self: @This()) timespec {
331 return self.atim;
332 }
333
334 pub fn mtime(self: @This()) timespec {
335 return self.mtim;
336 }
337
338 pub fn ctime(self: @This()) timespec {
339 return self.ctim;
340 }
341};
342
343pub const timeval = extern struct {
344 tv_sec: isize,
345 tv_usec: isize,
346};
347
348pub const timezone = extern struct {
349 tz_minuteswest: i32,
350 tz_dsttime: i32,
351};
352
353pub const Elf_Symndx = u32;
354
355pub const rlimit_resource = enum(c_int) {
356 /// Per-process CPU limit, in seconds.
357 CPU,
358
359 /// Largest file that can be created, in bytes.
360 FSIZE,
361
362 /// Maximum size of data segment, in bytes.
363 DATA,
364
365 /// Maximum size of stack segment, in bytes.
366 STACK,
367
368 /// Largest core file that can be created, in bytes.
369 CORE,
370
371 /// Number of open files.
372 NOFILE,
373
374 /// Address space limit.
375 AS,
376
377 /// Largest resident set size, in bytes.
378 /// This affects swapping; processes that are exceeding their
379 /// resident set size will be more likely to have physical memory
380 /// taken from them.
381 RSS,
382
383 /// Number of processes.
384 NPROC,
385
386 /// Locked-in-memory address space.
387 MEMLOCK,
388
389 /// Maximum number of file locks.
390 LOCKS,
391
392 /// Maximum number of pending signals.
393 SIGPENDING,
394
395 /// Maximum bytes in POSIX message queues.
396 MSGQUEUE,
397
398 /// Maximum nice priority allowed to raise to.
399 /// Nice levels 19 .. -20 correspond to 0 .. 39
400 /// values of this resource limit.
401 NICE,
402
403 /// Maximum realtime priority allowed for non-priviledged
404 /// processes.
405 RTPRIO,
406
407 /// Maximum CPU time in µs that a process scheduled under a real-time
408 /// scheduling policy may consume without making a blocking system
409 /// call before being forcibly descheduled.
410 RTTIME,
411
412 _,
413};
lib/std/os/linux/syscalls.zig+359
...@@ -2032,6 +2032,365 @@ pub const Mips = enum(usize) {...@@ -2032,6 +2032,365 @@ pub const Mips = enum(usize) {
2032 set_mempolicy_home_node = Linux + 450,2032 set_mempolicy_home_node = Linux + 450,
2033};2033};
20342034
2035pub const Mips64 = enum(usize) {
2036 pub const Linux = 5000;
2037
2038 read = Linux + 0,
2039 write = Linux + 1,
2040 open = Linux + 2,
2041 close = Linux + 3,
2042 stat = Linux + 4,
2043 fstat = Linux + 5,
2044 lstat = Linux + 6,
2045 poll = Linux + 7,
2046 lseek = Linux + 8,
2047 mmap = Linux + 9,
2048 mprotect = Linux + 10,
2049 munmap = Linux + 11,
2050 brk = Linux + 12,
2051 rt_sigaction = Linux + 13,
2052 rt_sigprocmask = Linux + 14,
2053 ioctl = Linux + 15,
2054 pread64 = Linux + 16,
2055 pwrite64 = Linux + 17,
2056 readv = Linux + 18,
2057 writev = Linux + 19,
2058 access = Linux + 20,
2059 pipe = Linux + 21,
2060 _newselect = Linux + 22,
2061 sched_yield = Linux + 23,
2062 mremap = Linux + 24,
2063 msync = Linux + 25,
2064 mincore = Linux + 26,
2065 madvise = Linux + 27,
2066 shmget = Linux + 28,
2067 shmat = Linux + 29,
2068 shmctl = Linux + 30,
2069 dup = Linux + 31,
2070 dup2 = Linux + 32,
2071 pause = Linux + 33,
2072 nanosleep = Linux + 34,
2073 getitimer = Linux + 35,
2074 setitimer = Linux + 36,
2075 alarm = Linux + 37,
2076 getpid = Linux + 38,
2077 sendfile = Linux + 39,
2078 socket = Linux + 40,
2079 connect = Linux + 41,
2080 accept = Linux + 42,
2081 sendto = Linux + 43,
2082 recvfrom = Linux + 44,
2083 sendmsg = Linux + 45,
2084 recvmsg = Linux + 46,
2085 shutdown = Linux + 47,
2086 bind = Linux + 48,
2087 listen = Linux + 49,
2088 getsockname = Linux + 50,
2089 getpeername = Linux + 51,
2090 socketpair = Linux + 52,
2091 setsockopt = Linux + 53,
2092 getsockopt = Linux + 54,
2093 clone = Linux + 55,
2094 fork = Linux + 56,
2095 execve = Linux + 57,
2096 exit = Linux + 58,
2097 wait4 = Linux + 59,
2098 kill = Linux + 60,
2099 uname = Linux + 61,
2100 semget = Linux + 62,
2101 semop = Linux + 63,
2102 semctl = Linux + 64,
2103 shmdt = Linux + 65,
2104 msgget = Linux + 66,
2105 msgsnd = Linux + 67,
2106 msgrcv = Linux + 68,
2107 msgctl = Linux + 69,
2108 fcntl = Linux + 70,
2109 flock = Linux + 71,
2110 fsync = Linux + 72,
2111 fdatasync = Linux + 73,
2112 truncate = Linux + 74,
2113 ftruncate = Linux + 75,
2114 getdents = Linux + 76,
2115 getcwd = Linux + 77,
2116 chdir = Linux + 78,
2117 fchdir = Linux + 79,
2118 rename = Linux + 80,
2119 mkdir = Linux + 81,
2120 rmdir = Linux + 82,
2121 creat = Linux + 83,
2122 link = Linux + 84,
2123 unlink = Linux + 85,
2124 symlink = Linux + 86,
2125 readlink = Linux + 87,
2126 chmod = Linux + 88,
2127 fchmod = Linux + 89,
2128 chown = Linux + 90,
2129 fchown = Linux + 91,
2130 lchown = Linux + 92,
2131 umask = Linux + 93,
2132 gettimeofday = Linux + 94,
2133 getrlimit = Linux + 95,
2134 getrusage = Linux + 96,
2135 sysinfo = Linux + 97,
2136 times = Linux + 98,
2137 ptrace = Linux + 99,
2138 getuid = Linux + 100,
2139 syslog = Linux + 101,
2140 getgid = Linux + 102,
2141 setuid = Linux + 103,
2142 setgid = Linux + 104,
2143 geteuid = Linux + 105,
2144 getegid = Linux + 106,
2145 setpgid = Linux + 107,
2146 getppid = Linux + 108,
2147 getpgrp = Linux + 109,
2148 setsid = Linux + 110,
2149 setreuid = Linux + 111,
2150 setregid = Linux + 112,
2151 getgroups = Linux + 113,
2152 setgroups = Linux + 114,
2153 setresuid = Linux + 115,
2154 getresuid = Linux + 116,
2155 setresgid = Linux + 117,
2156 getresgid = Linux + 118,
2157 getpgid = Linux + 119,
2158 setfsuid = Linux + 120,
2159 setfsgid = Linux + 121,
2160 getsid = Linux + 122,
2161 capget = Linux + 123,
2162 capset = Linux + 124,
2163 rt_sigpending = Linux + 125,
2164 rt_sigtimedwait = Linux + 126,
2165 rt_sigqueueinfo = Linux + 127,
2166 rt_sigsuspend = Linux + 128,
2167 sigaltstack = Linux + 129,
2168 utime = Linux + 130,
2169 mknod = Linux + 131,
2170 personality = Linux + 132,
2171 ustat = Linux + 133,
2172 statfs = Linux + 134,
2173 fstatfs = Linux + 135,
2174 sysfs = Linux + 136,
2175 getpriority = Linux + 137,
2176 setpriority = Linux + 138,
2177 sched_setparam = Linux + 139,
2178 sched_getparam = Linux + 140,
2179 sched_setscheduler = Linux + 141,
2180 sched_getscheduler = Linux + 142,
2181 sched_get_priority_max = Linux + 143,
2182 sched_get_priority_min = Linux + 144,
2183 sched_rr_get_interval = Linux + 145,
2184 mlock = Linux + 146,
2185 munlock = Linux + 147,
2186 mlockall = Linux + 148,
2187 munlockall = Linux + 149,
2188 vhangup = Linux + 150,
2189 pivot_root = Linux + 151,
2190 _sysctl = Linux + 152,
2191 prctl = Linux + 153,
2192 adjtimex = Linux + 154,
2193 setrlimit = Linux + 155,
2194 chroot = Linux + 156,
2195 sync = Linux + 157,
2196 acct = Linux + 158,
2197 settimeofday = Linux + 159,
2198 mount = Linux + 160,
2199 umount2 = Linux + 161,
2200 swapon = Linux + 162,
2201 swapoff = Linux + 163,
2202 reboot = Linux + 164,
2203 sethostname = Linux + 165,
2204 setdomainname = Linux + 166,
2205 create_module = Linux + 167,
2206 init_module = Linux + 168,
2207 delete_module = Linux + 169,
2208 get_kernel_syms = Linux + 170,
2209 query_module = Linux + 171,
2210 quotactl = Linux + 172,
2211 nfsservctl = Linux + 173,
2212 getpmsg = Linux + 174,
2213 putpmsg = Linux + 175,
2214 afs_syscall = Linux + 176,
2215 reserved177 = Linux + 177,
2216 gettid = Linux + 178,
2217 readahead = Linux + 179,
2218 setxattr = Linux + 180,
2219 lsetxattr = Linux + 181,
2220 fsetxattr = Linux + 182,
2221 getxattr = Linux + 183,
2222 lgetxattr = Linux + 184,
2223 fgetxattr = Linux + 185,
2224 listxattr = Linux + 186,
2225 llistxattr = Linux + 187,
2226 flistxattr = Linux + 188,
2227 removexattr = Linux + 189,
2228 lremovexattr = Linux + 190,
2229 fremovexattr = Linux + 191,
2230 tkill = Linux + 192,
2231 reserved193 = Linux + 193,
2232 futex = Linux + 194,
2233 sched_setaffinity = Linux + 195,
2234 sched_getaffinity = Linux + 196,
2235 cacheflush = Linux + 197,
2236 cachectl = Linux + 198,
2237 sysmips = Linux + 199,
2238 io_setup = Linux + 200,
2239 io_destroy = Linux + 201,
2240 io_getevents = Linux + 202,
2241 io_submit = Linux + 203,
2242 io_cancel = Linux + 204,
2243 exit_group = Linux + 205,
2244 lookup_dcookie = Linux + 206,
2245 epoll_create = Linux + 207,
2246 epoll_ctl = Linux + 208,
2247 epoll_wait = Linux + 209,
2248 remap_file_pages = Linux + 210,
2249 rt_sigreturn = Linux + 211,
2250 set_tid_address = Linux + 212,
2251 restart_syscall = Linux + 213,
2252 semtimedop = Linux + 214,
2253 fadvise64 = Linux + 215,
2254 timer_create = Linux + 216,
2255 timer_settime = Linux + 217,
2256 timer_gettime = Linux + 218,
2257 timer_getoverrun = Linux + 219,
2258 timer_delete = Linux + 220,
2259 clock_settime = Linux + 221,
2260 clock_gettime = Linux + 222,
2261 clock_getres = Linux + 223,
2262 clock_nanosleep = Linux + 224,
2263 tgkill = Linux + 225,
2264 utimes = Linux + 226,
2265 mbind = Linux + 227,
2266 get_mempolicy = Linux + 228,
2267 set_mempolicy = Linux + 229,
2268 mq_open = Linux + 230,
2269 mq_unlink = Linux + 231,
2270 mq_timedsend = Linux + 232,
2271 mq_timedreceive = Linux + 233,
2272 mq_notify = Linux + 234,
2273 mq_getsetattr = Linux + 235,
2274 vserver = Linux + 236,
2275 waitid = Linux + 237,
2276 add_key = Linux + 239,
2277 request_key = Linux + 240,
2278 keyctl = Linux + 241,
2279 set_thread_area = Linux + 242,
2280 inotify_init = Linux + 243,
2281 inotify_add_watch = Linux + 244,
2282 inotify_rm_watch = Linux + 245,
2283 migrate_pages = Linux + 246,
2284 openat = Linux + 247,
2285 mkdirat = Linux + 248,
2286 mknodat = Linux + 249,
2287 fchownat = Linux + 250,
2288 futimesat = Linux + 251,
2289 fstatat64 = Linux + 252,
2290 unlinkat = Linux + 253,
2291 renameat = Linux + 254,
2292 linkat = Linux + 255,
2293 symlinkat = Linux + 256,
2294 readlinkat = Linux + 257,
2295 fchmodat = Linux + 258,
2296 faccessat = Linux + 259,
2297 pselect6 = Linux + 260,
2298 ppoll = Linux + 261,
2299 unshare = Linux + 262,
2300 splice = Linux + 263,
2301 sync_file_range = Linux + 264,
2302 tee = Linux + 265,
2303 vmsplice = Linux + 266,
2304 move_pages = Linux + 267,
2305 set_robust_list = Linux + 268,
2306 get_robust_list = Linux + 269,
2307 kexec_load = Linux + 270,
2308 getcpu = Linux + 271,
2309 epoll_pwait = Linux + 272,
2310 ioprio_set = Linux + 273,
2311 ioprio_get = Linux + 274,
2312 utimensat = Linux + 275,
2313 signalfd = Linux + 276,
2314 timerfd = Linux + 277,
2315 eventfd = Linux + 278,
2316 fallocate = Linux + 279,
2317 timerfd_create = Linux + 280,
2318 timerfd_gettime = Linux + 281,
2319 timerfd_settime = Linux + 282,
2320 signalfd4 = Linux + 283,
2321 eventfd2 = Linux + 284,
2322 epoll_create1 = Linux + 285,
2323 dup3 = Linux + 286,
2324 pipe2 = Linux + 287,
2325 inotify_init1 = Linux + 288,
2326 preadv = Linux + 289,
2327 pwritev = Linux + 290,
2328 rt_tgsigqueueinfo = Linux + 291,
2329 perf_event_open = Linux + 292,
2330 accept4 = Linux + 293,
2331 recvmmsg = Linux + 294,
2332 fanotify_init = Linux + 295,
2333 fanotify_mark = Linux + 296,
2334 prlimit64 = Linux + 297,
2335 name_to_handle_at = Linux + 298,
2336 open_by_handle_at = Linux + 299,
2337 clock_adjtime = Linux + 300,
2338 syncfs = Linux + 301,
2339 sendmmsg = Linux + 302,
2340 setns = Linux + 303,
2341 process_vm_readv = Linux + 304,
2342 process_vm_writev = Linux + 305,
2343 kcmp = Linux + 306,
2344 finit_module = Linux + 307,
2345 getdents64 = Linux + 308,
2346 sched_setattr = Linux + 309,
2347 sched_getattr = Linux + 310,
2348 renameat2 = Linux + 311,
2349 seccomp = Linux + 312,
2350 getrandom = Linux + 313,
2351 memfd_create = Linux + 314,
2352 bpf = Linux + 315,
2353 execveat = Linux + 316,
2354 userfaultfd = Linux + 317,
2355 membarrier = Linux + 318,
2356 mlock2 = Linux + 319,
2357 copy_file_range = Linux + 320,
2358 preadv2 = Linux + 321,
2359 pwritev2 = Linux + 322,
2360 pkey_mprotect = Linux + 323,
2361 pkey_alloc = Linux + 324,
2362 pkey_free = Linux + 325,
2363 statx = Linux + 326,
2364 rseq = Linux + 327,
2365 io_pgetevents = Linux + 328,
2366 pidfd_send_signal = Linux + 424,
2367 io_uring_setup = Linux + 425,
2368 io_uring_enter = Linux + 426,
2369 io_uring_register = Linux + 427,
2370 open_tree = Linux + 428,
2371 move_mount = Linux + 429,
2372 fsopen = Linux + 430,
2373 fsconfig = Linux + 431,
2374 fsmount = Linux + 432,
2375 fspick = Linux + 433,
2376 pidfd_open = Linux + 434,
2377 clone3 = Linux + 435,
2378 close_range = Linux + 436,
2379 openat2 = Linux + 437,
2380 pidfd_getfd = Linux + 438,
2381 faccessat2 = Linux + 439,
2382 process_madvise = Linux + 440,
2383 epoll_pwait2 = Linux + 441,
2384 mount_setattr = Linux + 442,
2385 quotactl_fd = Linux + 443,
2386 landlock_create_ruleset = Linux + 444,
2387 landlock_add_rule = Linux + 445,
2388 landlock_restrict_self = Linux + 446,
2389 process_mrelease = Linux + 448,
2390 futex_waitv = Linux + 449,
2391 set_mempolicy_home_node = Linux + 450,
2392};
2393
2035pub const PowerPC = enum(usize) {2394pub const PowerPC = enum(usize) {
2036 restart_syscall = 0,2395 restart_syscall = 0,
2037 exit = 1,2396 exit = 1,
lib/std/os/linux/tls.zig+5-5
...@@ -48,7 +48,7 @@ const TLSVariant = enum {...@@ -48,7 +48,7 @@ const TLSVariant = enum {
48};48};
4949
50const tls_variant = switch (native_arch) {50const tls_variant = switch (native_arch) {
51 .arm, .armeb, .thumb, .aarch64, .aarch64_be, .riscv32, .riscv64, .mips, .mipsel, .powerpc, .powerpc64, .powerpc64le => TLSVariant.VariantI,51 .arm, .armeb, .thumb, .aarch64, .aarch64_be, .riscv32, .riscv64, .mips, .mipsel, .mips64, .mips64el, .powerpc, .powerpc64, .powerpc64le => TLSVariant.VariantI,
52 .x86_64, .x86, .sparc64 => TLSVariant.VariantII,52 .x86_64, .x86, .sparc64 => TLSVariant.VariantII,
53 else => @compileError("undefined tls_variant for this architecture"),53 else => @compileError("undefined tls_variant for this architecture"),
54};54};
...@@ -64,7 +64,7 @@ const tls_tcb_size = switch (native_arch) {...@@ -64,7 +64,7 @@ const tls_tcb_size = switch (native_arch) {
6464
65// Controls if the TP points to the end of the TCB instead of its beginning65// Controls if the TP points to the end of the TCB instead of its beginning
66const tls_tp_points_past_tcb = switch (native_arch) {66const tls_tp_points_past_tcb = switch (native_arch) {
67 .riscv32, .riscv64, .mips, .mipsel, .powerpc, .powerpc64, .powerpc64le => true,67 .riscv32, .riscv64, .mips, .mipsel, .mips64, .mips64el, .powerpc, .powerpc64, .powerpc64le => true,
68 else => false,68 else => false,
69};69};
7070
...@@ -72,12 +72,12 @@ const tls_tp_points_past_tcb = switch (native_arch) {...@@ -72,12 +72,12 @@ const tls_tp_points_past_tcb = switch (native_arch) {
72// make the generated code more efficient72// make the generated code more efficient
7373
74const tls_tp_offset = switch (native_arch) {74const tls_tp_offset = switch (native_arch) {
75 .mips, .mipsel, .powerpc, .powerpc64, .powerpc64le => 0x7000,75 .mips, .mipsel, .mips64, .mips64el, .powerpc, .powerpc64, .powerpc64le => 0x7000,
76 else => 0,76 else => 0,
77};77};
7878
79const tls_dtv_offset = switch (native_arch) {79const tls_dtv_offset = switch (native_arch) {
80 .mips, .mipsel, .powerpc, .powerpc64, .powerpc64le => 0x8000,80 .mips, .mipsel, .mips64, .mips64el, .powerpc, .powerpc64, .powerpc64le => 0x8000,
81 .riscv32, .riscv64 => 0x800,81 .riscv32, .riscv64 => 0x800,
82 else => 0,82 else => 0,
83};83};
...@@ -156,7 +156,7 @@ pub fn setThreadPointer(addr: usize) void {...@@ -156,7 +156,7 @@ pub fn setThreadPointer(addr: usize) void {
156 : [addr] "r" (addr),156 : [addr] "r" (addr),
157 );157 );
158 },158 },
159 .mips, .mipsel => {159 .mips, .mipsel, .mips64, .mips64el => {
160 const rc = std.os.linux.syscall1(.set_thread_area, addr);160 const rc = std.os.linux.syscall1(.set_thread_area, addr);
161 assert(rc == 0);161 assert(rc == 0);
162 },162 },
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.zig+1-1
...@@ -327,7 +327,7 @@ fn _start() callconv(.Naked) noreturn {...@@ -327,7 +327,7 @@ fn _start() callconv(.Naked) noreturn {
327 : [argc] "={sp}" (-> [*]usize),327 : [argc] "={sp}" (-> [*]usize),
328 );328 );
329 },329 },
330 .mips, .mipsel => {330 .mips, .mipsel, .mips64, .mips64el => {
331 // The lr is already zeroed on entry, as specified by the ABI.331 // The lr is already zeroed on entry, as specified by the ABI.
332 argc_argv_ptr = asm volatile (332 argc_argv_ptr = asm volatile (
333 \\ move $fp, $0333 \\ move $fp, $0
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+553
...@@ -1880,6 +1880,559 @@ pub const Target = struct {...@@ -1880,6 +1880,559 @@ pub const Target = struct {
1880 => 16,1880 => 16,
1881 };1881 };
1882 }1882 }
1883
1884 pub const CType = enum {
1885 short,
1886 ushort,
1887 int,
1888 uint,
1889 long,
1890 ulong,
1891 longlong,
1892 ulonglong,
1893 float,
1894 double,
1895 longdouble,
1896 };
1897
1898 pub fn c_type_byte_size(t: Target, c_type: CType) u16 {
1899 return switch (c_type) {
1900 .short,
1901 .ushort,
1902 .int,
1903 .uint,
1904 .long,
1905 .ulong,
1906 .longlong,
1907 .ulonglong,
1908 => @divExact(c_type_bit_size(t, c_type), 8),
1909
1910 .float => 4,
1911 .double => 8,
1912
1913 .longdouble => switch (c_type_bit_size(t, c_type)) {
1914 16 => 2,
1915 32 => 4,
1916 64 => 8,
1917 80 => @intCast(u16, mem.alignForward(10, c_type_alignment(t, .longdouble))),
1918 128 => 16,
1919 else => unreachable,
1920 },
1921 };
1922 }
1923
1924 pub fn c_type_bit_size(target: Target, c_type: CType) u16 {
1925 switch (target.os.tag) {
1926 .freestanding, .other => switch (target.cpu.arch) {
1927 .msp430 => switch (c_type) {
1928 .short, .ushort, .int, .uint => return 16,
1929 .float, .long, .ulong => return 32,
1930 .longlong, .ulonglong, .double, .longdouble => return 64,
1931 },
1932 .avr => switch (c_type) {
1933 .short, .ushort, .int, .uint => return 16,
1934 .long, .ulong, .float, .double, .longdouble => return 32,
1935 .longlong, .ulonglong => return 64,
1936 },
1937 .tce, .tcele => switch (c_type) {
1938 .short, .ushort => return 16,
1939 .int, .uint, .long, .ulong, .longlong, .ulonglong => return 32,
1940 .float, .double, .longdouble => return 32,
1941 },
1942 .mips64, .mips64el => switch (c_type) {
1943 .short, .ushort => return 16,
1944 .int, .uint, .float => return 32,
1945 .long, .ulong => return if (target.abi != .gnuabin32) 64 else 32,
1946 .longlong, .ulonglong, .double => return 64,
1947 .longdouble => return 128,
1948 },
1949 .x86_64 => switch (c_type) {
1950 .short, .ushort => return 16,
1951 .int, .uint, .float => return 32,
1952 .long, .ulong => switch (target.abi) {
1953 .gnux32, .muslx32 => return 32,
1954 else => return 64,
1955 },
1956 .longlong, .ulonglong, .double => return 64,
1957 .longdouble => return 80,
1958 },
1959 else => switch (c_type) {
1960 .short, .ushort => return 16,
1961 .int, .uint, .float => return 32,
1962 .long, .ulong => return target.cpu.arch.ptrBitWidth(),
1963 .longlong, .ulonglong, .double => return 64,
1964 .longdouble => switch (target.cpu.arch) {
1965 .x86 => switch (target.abi) {
1966 .android => return 64,
1967 else => return 80,
1968 },
1969
1970 .powerpc,
1971 .powerpcle,
1972 .powerpc64,
1973 .powerpc64le,
1974 => switch (target.abi) {
1975 .musl,
1976 .musleabi,
1977 .musleabihf,
1978 .muslx32,
1979 => return 64,
1980 else => return 128,
1981 },
1982
1983 .riscv32,
1984 .riscv64,
1985 .aarch64,
1986 .aarch64_be,
1987 .aarch64_32,
1988 .s390x,
1989 .sparc,
1990 .sparc64,
1991 .sparcel,
1992 .wasm32,
1993 .wasm64,
1994 => return 128,
1995
1996 else => return 64,
1997 },
1998 },
1999 },
2000
2001 .linux,
2002 .freebsd,
2003 .netbsd,
2004 .dragonfly,
2005 .openbsd,
2006 .wasi,
2007 .emscripten,
2008 .plan9,
2009 .solaris,
2010 .haiku,
2011 .ananas,
2012 .fuchsia,
2013 .minix,
2014 => switch (target.cpu.arch) {
2015 .msp430 => switch (c_type) {
2016 .short, .ushort, .int, .uint => return 16,
2017 .long, .ulong, .float => return 32,
2018 .longlong, .ulonglong, .double, .longdouble => return 64,
2019 },
2020 .avr => switch (c_type) {
2021 .short, .ushort, .int, .uint => return 16,
2022 .long, .ulong, .float, .double, .longdouble => return 32,
2023 .longlong, .ulonglong => return 64,
2024 },
2025 .tce, .tcele => switch (c_type) {
2026 .short, .ushort => return 16,
2027 .int, .uint, .long, .ulong, .longlong, .ulonglong => return 32,
2028 .float, .double, .longdouble => return 32,
2029 },
2030 .mips64, .mips64el => switch (c_type) {
2031 .short, .ushort => return 16,
2032 .int, .uint, .float => return 32,
2033 .long, .ulong => return if (target.abi != .gnuabin32) 64 else 32,
2034 .longlong, .ulonglong, .double => return 64,
2035 .longdouble => if (target.os.tag == .freebsd) return 64 else return 128,
2036 },
2037 .x86_64 => switch (c_type) {
2038 .short, .ushort => return 16,
2039 .int, .uint, .float => return 32,
2040 .long, .ulong => switch (target.abi) {
2041 .gnux32, .muslx32 => return 32,
2042 else => return 64,
2043 },
2044 .longlong, .ulonglong, .double => return 64,
2045 .longdouble => return 80,
2046 },
2047 else => switch (c_type) {
2048 .short, .ushort => return 16,
2049 .int, .uint, .float => return 32,
2050 .long, .ulong => return target.cpu.arch.ptrBitWidth(),
2051 .longlong, .ulonglong, .double => return 64,
2052 .longdouble => switch (target.cpu.arch) {
2053 .x86 => switch (target.abi) {
2054 .android => return 64,
2055 else => return 80,
2056 },
2057
2058 .powerpc,
2059 .powerpcle,
2060 => switch (target.abi) {
2061 .musl,
2062 .musleabi,
2063 .musleabihf,
2064 .muslx32,
2065 => return 64,
2066 else => switch (target.os.tag) {
2067 .freebsd, .netbsd, .openbsd => return 64,
2068 else => return 128,
2069 },
2070 },
2071
2072 .powerpc64,
2073 .powerpc64le,
2074 => switch (target.abi) {
2075 .musl,
2076 .musleabi,
2077 .musleabihf,
2078 .muslx32,
2079 => return 64,
2080 else => switch (target.os.tag) {
2081 .freebsd, .openbsd => return 64,
2082 else => return 128,
2083 },
2084 },
2085
2086 .riscv32,
2087 .riscv64,
2088 .aarch64,
2089 .aarch64_be,
2090 .aarch64_32,
2091 .s390x,
2092 .mips64,
2093 .mips64el,
2094 .sparc,
2095 .sparc64,
2096 .sparcel,
2097 .wasm32,
2098 .wasm64,
2099 => return 128,
2100
2101 else => return 64,
2102 },
2103 },
2104 },
2105
2106 .windows, .uefi => switch (target.cpu.arch) {
2107 .x86 => switch (c_type) {
2108 .short, .ushort => return 16,
2109 .int, .uint, .float => return 32,
2110 .long, .ulong => return 32,
2111 .longlong, .ulonglong, .double => return 64,
2112 .longdouble => switch (target.abi) {
2113 .gnu, .gnuilp32, .cygnus => return 80,
2114 else => return 64,
2115 },
2116 },
2117 .x86_64 => switch (c_type) {
2118 .short, .ushort => return 16,
2119 .int, .uint, .float => return 32,
2120 .long, .ulong => switch (target.abi) {
2121 .cygnus => return 64,
2122 else => return 32,
2123 },
2124 .longlong, .ulonglong, .double => return 64,
2125 .longdouble => switch (target.abi) {
2126 .gnu, .gnuilp32, .cygnus => return 80,
2127 else => return 64,
2128 },
2129 },
2130 else => switch (c_type) {
2131 .short, .ushort => return 16,
2132 .int, .uint, .float => return 32,
2133 .long, .ulong => return 32,
2134 .longlong, .ulonglong, .double => return 64,
2135 .longdouble => return 64,
2136 },
2137 },
2138
2139 .macos, .ios, .tvos, .watchos => switch (c_type) {
2140 .short, .ushort => return 16,
2141 .int, .uint, .float => return 32,
2142 .long, .ulong => switch (target.cpu.arch) {
2143 .x86, .arm, .aarch64_32 => return 32,
2144 .x86_64 => switch (target.abi) {
2145 .gnux32, .muslx32 => return 32,
2146 else => return 64,
2147 },
2148 else => return 64,
2149 },
2150 .longlong, .ulonglong, .double => return 64,
2151 .longdouble => switch (target.cpu.arch) {
2152 .x86 => switch (target.abi) {
2153 .android => return 64,
2154 else => return 80,
2155 },
2156 .x86_64 => return 80,
2157 else => return 64,
2158 },
2159 },
2160
2161 .nvcl, .cuda => switch (c_type) {
2162 .short, .ushort => return 16,
2163 .int, .uint, .float => return 32,
2164 .long, .ulong => switch (target.cpu.arch) {
2165 .nvptx => return 32,
2166 .nvptx64 => return 64,
2167 else => return 64,
2168 },
2169 .longlong, .ulonglong, .double => return 64,
2170 .longdouble => return 64,
2171 },
2172
2173 .amdhsa, .amdpal => switch (c_type) {
2174 .short, .ushort => return 16,
2175 .int, .uint, .float => return 32,
2176 .long, .ulong, .longlong, .ulonglong, .double => return 64,
2177 .longdouble => return 128,
2178 },
2179
2180 .cloudabi,
2181 .kfreebsd,
2182 .lv2,
2183 .zos,
2184 .rtems,
2185 .nacl,
2186 .aix,
2187 .ps4,
2188 .ps5,
2189 .elfiamcu,
2190 .mesa3d,
2191 .contiki,
2192 .hermit,
2193 .hurd,
2194 .opencl,
2195 .glsl450,
2196 .vulkan,
2197 .driverkit,
2198 .shadermodel,
2199 => @panic("TODO specify the C integer and float type sizes for this OS"),
2200 }
2201 }
2202
2203 pub fn c_type_alignment(target: Target, c_type: CType) u16 {
2204 // Overrides for unusual alignments
2205 switch (target.cpu.arch) {
2206 .avr => switch (c_type) {
2207 .short, .ushort => return 2,
2208 else => return 1,
2209 },
2210 .x86 => switch (target.os.tag) {
2211 .windows, .uefi => switch (c_type) {
2212 .longlong, .ulonglong, .double => return 8,
2213 .longdouble => switch (target.abi) {
2214 .gnu, .gnuilp32, .cygnus => return 4,
2215 else => return 8,
2216 },
2217 else => {},
2218 },
2219 else => {},
2220 },
2221 else => {},
2222 }
2223
2224 // Next-power-of-two-aligned, up to a maximum.
2225 return @min(
2226 std.math.ceilPowerOfTwoAssert(u16, (c_type_bit_size(target, c_type) + 7) / 8),
2227 switch (target.cpu.arch) {
2228 .arm, .armeb, .thumb, .thumbeb => switch (target.os.tag) {
2229 .netbsd => switch (target.abi) {
2230 .gnueabi,
2231 .gnueabihf,
2232 .eabi,
2233 .eabihf,
2234 .android,
2235 .musleabi,
2236 .musleabihf,
2237 => 8,
2238
2239 else => @as(u16, 4),
2240 },
2241 .ios, .tvos, .watchos => 4,
2242 else => 8,
2243 },
2244
2245 .msp430,
2246 .avr,
2247 => 2,
2248
2249 .arc,
2250 .csky,
2251 .x86,
2252 .xcore,
2253 .dxil,
2254 .loongarch32,
2255 .tce,
2256 .tcele,
2257 .le32,
2258 .amdil,
2259 .hsail,
2260 .spir,
2261 .spirv32,
2262 .kalimba,
2263 .shave,
2264 .renderscript32,
2265 .ve,
2266 .spu_2,
2267 => 4,
2268
2269 .aarch64_32,
2270 .amdgcn,
2271 .amdil64,
2272 .bpfel,
2273 .bpfeb,
2274 .hexagon,
2275 .hsail64,
2276 .loongarch64,
2277 .m68k,
2278 .mips,
2279 .mipsel,
2280 .sparc,
2281 .sparcel,
2282 .sparc64,
2283 .lanai,
2284 .le64,
2285 .nvptx,
2286 .nvptx64,
2287 .r600,
2288 .s390x,
2289 .spir64,
2290 .spirv64,
2291 .renderscript64,
2292 => 8,
2293
2294 .aarch64,
2295 .aarch64_be,
2296 .mips64,
2297 .mips64el,
2298 .powerpc,
2299 .powerpcle,
2300 .powerpc64,
2301 .powerpc64le,
2302 .riscv32,
2303 .riscv64,
2304 .x86_64,
2305 .wasm32,
2306 .wasm64,
2307 => 16,
2308 },
2309 );
2310 }
2311
2312 pub fn c_type_preferred_alignment(target: Target, c_type: CType) u16 {
2313 // Overrides for unusual alignments
2314 switch (target.cpu.arch) {
2315 .arm, .armeb, .thumb, .thumbeb => switch (target.os.tag) {
2316 .netbsd => switch (target.abi) {
2317 .gnueabi,
2318 .gnueabihf,
2319 .eabi,
2320 .eabihf,
2321 .android,
2322 .musleabi,
2323 .musleabihf,
2324 => {},
2325
2326 else => switch (c_type) {
2327 .longdouble => return 4,
2328 else => {},
2329 },
2330 },
2331 .ios, .tvos, .watchos => switch (c_type) {
2332 .longdouble => return 4,
2333 else => {},
2334 },
2335 else => {},
2336 },
2337 .arc => switch (c_type) {
2338 .longdouble => return 4,
2339 else => {},
2340 },
2341 .avr => switch (c_type) {
2342 .int, .uint, .long, .ulong, .float, .longdouble => return 1,
2343 .short, .ushort => return 2,
2344 .double => return 4,
2345 .longlong, .ulonglong => return 8,
2346 },
2347 .x86 => switch (target.os.tag) {
2348 .windows, .uefi => switch (c_type) {
2349 .longdouble => switch (target.abi) {
2350 .gnu, .gnuilp32, .cygnus => return 4,
2351 else => return 8,
2352 },
2353 else => {},
2354 },
2355 else => switch (c_type) {
2356 .longdouble => return 4,
2357 else => {},
2358 },
2359 },
2360 else => {},
2361 }
2362
2363 // Next-power-of-two-aligned, up to a maximum.
2364 return @min(
2365 std.math.ceilPowerOfTwoAssert(u16, (c_type_bit_size(target, c_type) + 7) / 8),
2366 switch (target.cpu.arch) {
2367 .msp430 => @as(u16, 2),
2368
2369 .csky,
2370 .xcore,
2371 .dxil,
2372 .loongarch32,
2373 .tce,
2374 .tcele,
2375 .le32,
2376 .amdil,
2377 .hsail,
2378 .spir,
2379 .spirv32,
2380 .kalimba,
2381 .shave,
2382 .renderscript32,
2383 .ve,
2384 .spu_2,
2385 => 4,
2386
2387 .arc,
2388 .arm,
2389 .armeb,
2390 .avr,
2391 .thumb,
2392 .thumbeb,
2393 .aarch64_32,
2394 .amdgcn,
2395 .amdil64,
2396 .bpfel,
2397 .bpfeb,
2398 .hexagon,
2399 .hsail64,
2400 .x86,
2401 .loongarch64,
2402 .m68k,
2403 .mips,
2404 .mipsel,
2405 .sparc,
2406 .sparcel,
2407 .sparc64,
2408 .lanai,
2409 .le64,
2410 .nvptx,
2411 .nvptx64,
2412 .r600,
2413 .s390x,
2414 .spir64,
2415 .spirv64,
2416 .renderscript64,
2417 => 8,
2418
2419 .aarch64,
2420 .aarch64_be,
2421 .mips64,
2422 .mips64el,
2423 .powerpc,
2424 .powerpcle,
2425 .powerpc64,
2426 .powerpc64le,
2427 .riscv32,
2428 .riscv64,
2429 .x86_64,
2430 .wasm32,
2431 .wasm64,
2432 => 16,
2433 },
2434 );
2435 }
1883};2436};
18842437
1885test {2438test {
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+2-2
...@@ -6073,7 +6073,7 @@ var fixed_buffer_mem: [100 * 1024]u8 = undefined;...@@ -6073,7 +6073,7 @@ var fixed_buffer_mem: [100 * 1024]u8 = undefined;
6073fn 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 {
6074 const stderr = io.getStdErr().writer();6074 const stderr = io.getStdErr().writer();
60756075
6076 var tree = try std.zig.parse(allocator, source);6076 var tree = try std.zig.Ast.parse(allocator, source, .zig);
6077 defer tree.deinit(allocator);6077 defer tree.deinit(allocator);
60786078
6079 for (tree.errors) |parse_error| {6079 for (tree.errors) |parse_error| {
...@@ -6124,7 +6124,7 @@ fn testCanonical(source: [:0]const u8) !void {...@@ -6124,7 +6124,7 @@ fn testCanonical(source: [:0]const u8) !void {
6124const Error = std.zig.Ast.Error.Tag;6124const Error = std.zig.Ast.Error.Tag;
61256125
6126fn testError(source: [:0]const u8, expected_errors: []const Error) !void {6126fn testError(source: [:0]const u8, expected_errors: []const Error) !void {
6127 var tree = try std.zig.parse(std.testing.allocator, source);6127 var tree = try std.zig.Ast.parse(std.testing.allocator, source, .zig);
6128 defer tree.deinit(std.testing.allocator);6128 defer tree.deinit(std.testing.allocator);
61296129
6130 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}
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+51-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,17 +531,6 @@ pub const Decl = struct {...@@ -533,17 +531,6 @@ 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.
537 /// This is populated regardless of semantic analysis and code generation.
538 link: link.File.LinkBlock,
539
540 /// Represents the function in the linked output file, if the `Decl` is a function.
541 /// This is stored here and not in `Fn` because `Decl` survives across updates but
542 /// `Fn` does not.
543 /// TODO Look into making `Fn` a longer lived structure and moving this field there
544 /// to save on memory usage.
545 fn_link: link.File.LinkFn,
546
547 /// The shallow set of other decls whose typed_value could possibly change if this Decl's534 /// The shallow set of other decls whose typed_value could possibly change if this Decl's
548 /// typed_value is modified.535 /// typed_value is modified.
549 dependants: DepsTable = .{},536 dependants: DepsTable = .{},
...@@ -2067,7 +2054,7 @@ pub const File = struct {...@@ -2067,7 +2054,7 @@ pub const File = struct {
2067 if (file.tree_loaded) return &file.tree;2054 if (file.tree_loaded) return &file.tree;
20682055
2069 const source = try file.getSource(gpa);2056 const source = try file.getSource(gpa);
2070 file.tree = try std.zig.parse(gpa, source.bytes);2057 file.tree = try Ast.parse(gpa, source.bytes, .zig);
2071 file.tree_loaded = true;2058 file.tree_loaded = true;
2072 return &file.tree;2059 return &file.tree;
2073 }2060 }
...@@ -3672,7 +3659,7 @@ pub fn astGenFile(mod: *Module, file: *File) !void {...@@ -3672,7 +3659,7 @@ pub fn astGenFile(mod: *Module, file: *File) !void {
3672 file.source = source;3659 file.source = source;
3673 file.source_loaded = true;3660 file.source_loaded = true;
36743661
3675 file.tree = try std.zig.parse(gpa, source);3662 file.tree = try Ast.parse(gpa, source, .zig);
3676 defer if (!file.tree_loaded) file.tree.deinit(gpa);3663 defer if (!file.tree_loaded) file.tree.deinit(gpa);
36773664
3678 if (file.tree.errors.len != 0) {3665 if (file.tree.errors.len != 0) {
...@@ -3987,7 +3974,7 @@ pub fn populateBuiltinFile(mod: *Module) !void {...@@ -3987,7 +3974,7 @@ pub fn populateBuiltinFile(mod: *Module) !void {
3987 else => |e| return e,3974 else => |e| return e,
3988 }3975 }
39893976
3990 file.tree = try std.zig.parse(gpa, file.source);3977 file.tree = try Ast.parse(gpa, file.source, .zig);
3991 file.tree_loaded = true;3978 file.tree_loaded = true;
3992 assert(file.tree.errors.len == 0); // builtin.zig must parse3979 assert(file.tree.errors.len == 0); // builtin.zig must parse
39933980
...@@ -4098,7 +4085,7 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl_index: Decl.Index) SemaError!void {...@@ -4098,7 +4085,7 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl_index: Decl.Index) SemaError!void {
40984085
4099 // The exports this Decl performs will be re-discovered, so we remove them here4086 // The exports this Decl performs will be re-discovered, so we remove them here
4100 // prior to re-analysis.4087 // prior to re-analysis.
4101 mod.deleteDeclExports(decl_index);4088 try mod.deleteDeclExports(decl_index);
41024089
4103 // Similarly, `@setAlignStack` invocations will be re-discovered.4090 // Similarly, `@setAlignStack` invocations will be re-discovered.
4104 if (decl.getFunction()) |func| {4091 if (decl.getFunction()) |func| {
...@@ -4878,14 +4865,31 @@ pub fn importFile(...@@ -4878,14 +4865,31 @@ pub fn importFile(
4878 };4865 };
4879}4866}
48804867
4881pub fn embedFile(mod: *Module, cur_file: *File, rel_file_path: []const u8) !*EmbedFile {4868pub fn embedFile(mod: *Module, cur_file: *File, import_string: []const u8) !*EmbedFile {
4882 const gpa = mod.gpa;4869 const gpa = mod.gpa;
48834870
4884 // The resolved path is used as the key in the table, to detect if4871 if (cur_file.pkg.table.get(import_string)) |pkg| {
4885 // a file refers to the same as another, despite different relative paths.4872 const resolved_path = try std.fs.path.resolve(gpa, &[_][]const u8{
4873 pkg.root_src_directory.path orelse ".", pkg.root_src_path,
4874 });
4875 var keep_resolved_path = false;
4876 defer if (!keep_resolved_path) gpa.free(resolved_path);
4877
4878 const gop = try mod.embed_table.getOrPut(gpa, resolved_path);
4879 errdefer assert(mod.embed_table.remove(resolved_path));
4880 if (gop.found_existing) return gop.value_ptr.*;
4881
4882 const sub_file_path = try gpa.dupe(u8, pkg.root_src_path);
4883 errdefer gpa.free(sub_file_path);
4884
4885 return newEmbedFile(mod, pkg, sub_file_path, resolved_path, &keep_resolved_path, gop);
4886 }
4887
4888 // The resolved path is used as the key in the table, to detect if a file
4889 // refers to the same as another, despite different relative paths.
4886 const cur_pkg_dir_path = cur_file.pkg.root_src_directory.path orelse ".";4890 const cur_pkg_dir_path = cur_file.pkg.root_src_directory.path orelse ".";
4887 const resolved_path = try std.fs.path.resolve(gpa, &[_][]const u8{4891 const resolved_path = try std.fs.path.resolve(gpa, &[_][]const u8{
4888 cur_pkg_dir_path, cur_file.sub_file_path, "..", rel_file_path,4892 cur_pkg_dir_path, cur_file.sub_file_path, "..", import_string,
4889 });4893 });
4890 var keep_resolved_path = false;4894 var keep_resolved_path = false;
4891 defer if (!keep_resolved_path) gpa.free(resolved_path);4895 defer if (!keep_resolved_path) gpa.free(resolved_path);
...@@ -4894,9 +4898,6 @@ pub fn embedFile(mod: *Module, cur_file: *File, rel_file_path: []const u8) !*Emb...@@ -4894,9 +4898,6 @@ pub fn embedFile(mod: *Module, cur_file: *File, rel_file_path: []const u8) !*Emb
4894 errdefer assert(mod.embed_table.remove(resolved_path));4898 errdefer assert(mod.embed_table.remove(resolved_path));
4895 if (gop.found_existing) return gop.value_ptr.*;4899 if (gop.found_existing) return gop.value_ptr.*;
48964900
4897 const new_file = try gpa.create(EmbedFile);
4898 errdefer gpa.destroy(new_file);
4899
4900 const resolved_root_path = try std.fs.path.resolve(gpa, &[_][]const u8{cur_pkg_dir_path});4901 const resolved_root_path = try std.fs.path.resolve(gpa, &[_][]const u8{cur_pkg_dir_path});
4901 defer gpa.free(resolved_root_path);4902 defer gpa.free(resolved_root_path);
49024903
...@@ -4915,7 +4916,23 @@ pub fn embedFile(mod: *Module, cur_file: *File, rel_file_path: []const u8) !*Emb...@@ -4915,7 +4916,23 @@ pub fn embedFile(mod: *Module, cur_file: *File, rel_file_path: []const u8) !*Emb
4915 };4916 };
4916 errdefer gpa.free(sub_file_path);4917 errdefer gpa.free(sub_file_path);
49174918
4918 var file = try cur_file.pkg.root_src_directory.handle.openFile(sub_file_path, .{});4919 return newEmbedFile(mod, cur_file.pkg, sub_file_path, resolved_path, &keep_resolved_path, gop);
4920}
4921
4922fn newEmbedFile(
4923 mod: *Module,
4924 pkg: *Package,
4925 sub_file_path: []const u8,
4926 resolved_path: []const u8,
4927 keep_resolved_path: *bool,
4928 gop: std.StringHashMapUnmanaged(*EmbedFile).GetOrPutResult,
4929) !*EmbedFile {
4930 const gpa = mod.gpa;
4931
4932 const new_file = try gpa.create(EmbedFile);
4933 errdefer gpa.destroy(new_file);
4934
4935 var file = try pkg.root_src_directory.handle.openFile(sub_file_path, .{});
4919 defer file.close();4936 defer file.close();
49204937
4921 const actual_stat = try file.stat();4938 const actual_stat = try file.stat();
...@@ -4928,10 +4945,6 @@ pub fn embedFile(mod: *Module, cur_file: *File, rel_file_path: []const u8) !*Emb...@@ -4928,10 +4945,6 @@ pub fn embedFile(mod: *Module, cur_file: *File, rel_file_path: []const u8) !*Emb
4928 const bytes = try file.readToEndAllocOptions(gpa, std.math.maxInt(u32), size_usize, 1, 0);4945 const bytes = try file.readToEndAllocOptions(gpa, std.math.maxInt(u32), size_usize, 1, 0);
4929 errdefer gpa.free(bytes);4946 errdefer gpa.free(bytes);
49304947
4931 log.debug("new embedFile. resolved_root_path={s}, resolved_path={s}, sub_file_path={s}, rel_file_path={s}", .{
4932 resolved_root_path, resolved_path, sub_file_path, rel_file_path,
4933 });
4934
4935 if (mod.comp.whole_cache_manifest) |whole_cache_manifest| {4948 if (mod.comp.whole_cache_manifest) |whole_cache_manifest| {
4936 const copied_resolved_path = try gpa.dupe(u8, resolved_path);4949 const copied_resolved_path = try gpa.dupe(u8, resolved_path);
4937 errdefer gpa.free(copied_resolved_path);4950 errdefer gpa.free(copied_resolved_path);
...@@ -4940,13 +4953,13 @@ pub fn embedFile(mod: *Module, cur_file: *File, rel_file_path: []const u8) !*Emb...@@ -4940,13 +4953,13 @@ pub fn embedFile(mod: *Module, cur_file: *File, rel_file_path: []const u8) !*Emb
4940 try whole_cache_manifest.addFilePostContents(copied_resolved_path, bytes, stat);4953 try whole_cache_manifest.addFilePostContents(copied_resolved_path, bytes, stat);
4941 }4954 }
49424955
4943 keep_resolved_path = true; // It's now owned by embed_table.4956 keep_resolved_path.* = true; // It's now owned by embed_table.
4944 gop.value_ptr.* = new_file;4957 gop.value_ptr.* = new_file;
4945 new_file.* = .{4958 new_file.* = .{
4946 .sub_file_path = sub_file_path,4959 .sub_file_path = sub_file_path,
4947 .bytes = bytes,4960 .bytes = bytes,
4948 .stat = stat,4961 .stat = stat,
4949 .pkg = cur_file.pkg,4962 .pkg = pkg,
4950 .owner_decl = undefined, // Set by Sema immediately after this function returns.4963 .owner_decl = undefined, // Set by Sema immediately after this function returns.
4951 };4964 };
4952 return new_file;4965 return new_file;
...@@ -5183,20 +5196,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) Allocator.Err...@@ -5183,20 +5196,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) Allocator.Err
5183 decl.zir_decl_index = @intCast(u32, decl_sub_index);5196 decl.zir_decl_index = @intCast(u32, decl_sub_index);
5184 if (decl.getFunction()) |_| {5197 if (decl.getFunction()) |_| {
5185 switch (comp.bin_file.tag) {5198 switch (comp.bin_file.tag) {
5186 .coff => {5199 .coff, .elf, .macho, .plan9 => {
5187 // TODO Implement for COFF
5188 },
5189 .elf => if (decl.fn_link.elf.len != 0) {
5190 // TODO Look into detecting when this would be unnecessary by storing enough state
5191 // in `Decl` to notice that the line number did not change.
5192 comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl_index });
5193 },
5194 .macho => if (decl.fn_link.macho.len != 0) {
5195 // TODO Look into detecting when this would be unnecessary by storing enough state
5196 // in `Decl` to notice that the line number did not change.
5197 comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl_index });
5198 },
5199 .plan9 => {
5200 // TODO Look into detecting when this would be unnecessary by storing enough state5200 // TODO Look into detecting when this would be unnecessary by storing enough state
5201 // in `Decl` to notice that the line number did not change.5201 // in `Decl` to notice that the line number did not change.
5202 comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl_index });5202 comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl_index });
...@@ -5265,34 +5265,11 @@ pub fn clearDecl(...@@ -5265,34 +5265,11 @@ pub fn clearDecl(
5265 assert(emit_h.decl_table.swapRemove(decl_index));5265 assert(emit_h.decl_table.swapRemove(decl_index));
5266 }5266 }
5267 _ = mod.compile_log_decls.swapRemove(decl_index);5267 _ = mod.compile_log_decls.swapRemove(decl_index);
5268 mod.deleteDeclExports(decl_index);5268 try mod.deleteDeclExports(decl_index);
52695269
5270 if (decl.has_tv) {5270 if (decl.has_tv) {
5271 if (decl.ty.isFnOrHasRuntimeBits()) {5271 if (decl.ty.isFnOrHasRuntimeBits()) {
5272 mod.comp.bin_file.freeDecl(decl_index);5272 mod.comp.bin_file.freeDecl(decl_index);
5273
5274 // TODO instead of a union, put this memory trailing Decl objects,
5275 // and allow it to be variably sized.
5276 decl.link = switch (mod.comp.bin_file.tag) {
5277 .coff => .{ .coff = link.File.Coff.Atom.empty },
5278 .elf => .{ .elf = link.File.Elf.TextBlock.empty },
5279 .macho => .{ .macho = link.File.MachO.Atom.empty },
5280 .plan9 => .{ .plan9 = link.File.Plan9.DeclBlock.empty },
5281 .c => .{ .c = {} },
5282 .wasm => .{ .wasm = link.File.Wasm.DeclBlock.empty },
5283 .spirv => .{ .spirv = {} },
5284 .nvptx => .{ .nvptx = {} },
5285 };
5286 decl.fn_link = switch (mod.comp.bin_file.tag) {
5287 .coff => .{ .coff = {} },
5288 .elf => .{ .elf = link.File.Dwarf.SrcFn.empty },
5289 .macho => .{ .macho = link.File.Dwarf.SrcFn.empty },
5290 .plan9 => .{ .plan9 = {} },
5291 .c => .{ .c = {} },
5292 .wasm => .{ .wasm = link.File.Wasm.FnData.empty },
5293 .spirv => .{ .spirv = .{} },
5294 .nvptx => .{ .nvptx = {} },
5295 };
5296 }5273 }
5297 if (decl.getInnerNamespace()) |namespace| {5274 if (decl.getInnerNamespace()) |namespace| {
5298 try namespace.deleteAllDecls(mod, outdated_decls);5275 try namespace.deleteAllDecls(mod, outdated_decls);
...@@ -5358,7 +5335,7 @@ pub fn abortAnonDecl(mod: *Module, decl_index: Decl.Index) void {...@@ -5358,7 +5335,7 @@ pub fn abortAnonDecl(mod: *Module, decl_index: Decl.Index) void {
53585335
5359/// Delete all the Export objects that are caused by this Decl. Re-analysis of5336/// Delete all the Export objects that are caused by this Decl. Re-analysis of
5360/// this Decl will cause them to be re-created (or not).5337/// this Decl will cause them to be re-created (or not).
5361fn deleteDeclExports(mod: *Module, decl_index: Decl.Index) void {5338fn deleteDeclExports(mod: *Module, decl_index: Decl.Index) Allocator.Error!void {
5362 var export_owners = (mod.export_owners.fetchSwapRemove(decl_index) orelse return).value;5339 var export_owners = (mod.export_owners.fetchSwapRemove(decl_index) orelse return).value;
53635340
5364 for (export_owners.items) |exp| {5341 for (export_owners.items) |exp| {
...@@ -5381,16 +5358,16 @@ fn deleteDeclExports(mod: *Module, decl_index: Decl.Index) void {...@@ -5381,16 +5358,16 @@ fn deleteDeclExports(mod: *Module, decl_index: Decl.Index) void {
5381 }5358 }
5382 }5359 }
5383 if (mod.comp.bin_file.cast(link.File.Elf)) |elf| {5360 if (mod.comp.bin_file.cast(link.File.Elf)) |elf| {
5384 elf.deleteExport(exp.link.elf);5361 elf.deleteDeclExport(decl_index, exp.options.name);
5385 }5362 }
5386 if (mod.comp.bin_file.cast(link.File.MachO)) |macho| {5363 if (mod.comp.bin_file.cast(link.File.MachO)) |macho| {
5387 macho.deleteExport(exp.link.macho);5364 try macho.deleteDeclExport(decl_index, exp.options.name);
5388 }5365 }
5389 if (mod.comp.bin_file.cast(link.File.Wasm)) |wasm| {5366 if (mod.comp.bin_file.cast(link.File.Wasm)) |wasm| {
5390 wasm.deleteExport(exp.link.wasm);5367 wasm.deleteDeclExport(decl_index);
5391 }5368 }
5392 if (mod.comp.bin_file.cast(link.File.Coff)) |coff| {5369 if (mod.comp.bin_file.cast(link.File.Coff)) |coff| {
5393 coff.deleteExport(exp.link.coff);5370 coff.deleteDeclExport(decl_index, exp.options.name);
5394 }5371 }
5395 if (mod.failed_exports.fetchSwapRemove(exp)) |failed_kv| {5372 if (mod.failed_exports.fetchSwapRemove(exp)) |failed_kv| {
5396 failed_kv.value.destroy(mod.gpa);5373 failed_kv.value.destroy(mod.gpa);
...@@ -5693,26 +5670,6 @@ pub fn allocateNewDecl(...@@ -5693,26 +5670,6 @@ pub fn allocateNewDecl(
5693 .deletion_flag = false,5670 .deletion_flag = false,
5694 .zir_decl_index = 0,5671 .zir_decl_index = 0,
5695 .src_scope = src_scope,5672 .src_scope = src_scope,
5696 .link = switch (mod.comp.bin_file.tag) {
5697 .coff => .{ .coff = link.File.Coff.Atom.empty },
5698 .elf => .{ .elf = link.File.Elf.TextBlock.empty },
5699 .macho => .{ .macho = link.File.MachO.Atom.empty },
5700 .plan9 => .{ .plan9 = link.File.Plan9.DeclBlock.empty },
5701 .c => .{ .c = {} },
5702 .wasm => .{ .wasm = link.File.Wasm.DeclBlock.empty },
5703 .spirv => .{ .spirv = {} },
5704 .nvptx => .{ .nvptx = {} },
5705 },
5706 .fn_link = switch (mod.comp.bin_file.tag) {
5707 .coff => .{ .coff = {} },
5708 .elf => .{ .elf = link.File.Dwarf.SrcFn.empty },
5709 .macho => .{ .macho = link.File.Dwarf.SrcFn.empty },
5710 .plan9 => .{ .plan9 = {} },
5711 .c => .{ .c = {} },
5712 .wasm => .{ .wasm = link.File.Wasm.FnData.empty },
5713 .spirv => .{ .spirv = .{} },
5714 .nvptx => .{ .nvptx = {} },
5715 },
5716 .generation = 0,5673 .generation = 0,
5717 .is_pub = false,5674 .is_pub = false,
5718 .is_exported = false,5675 .is_exported = false,
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+93-20
...@@ -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,
...@@ -6884,6 +6875,8 @@ fn analyzeInlineCallArg(...@@ -6884,6 +6875,8 @@ fn analyzeInlineCallArg(
6884 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);
6885 return err;6876 return err;
6886 };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");
6887 }6880 }
6888 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 = .{
6889 .func_inst = func_inst,6882 .func_inst = func_inst,
...@@ -6957,6 +6950,9 @@ fn analyzeInlineCallArg(...@@ -6957,6 +6950,9 @@ fn analyzeInlineCallArg(
6957 .val = arg_val,6950 .val = arg_val,
6958 };6951 };
6959 } 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 }
6960 sema.inst_map.putAssumeCapacityNoClobber(inst, uncasted_arg);6956 sema.inst_map.putAssumeCapacityNoClobber(inst, uncasted_arg);
6961 }6957 }
69626958
...@@ -8477,7 +8473,7 @@ fn handleExternLibName(...@@ -8477,7 +8473,7 @@ fn handleExternLibName(
8477 return sema.fail(8473 return sema.fail(
8478 block,8474 block,
8479 src_loc,8475 src_loc,
8480 "dependency on dynamic library '{s}' requires enabling Position Independent Code. Fixed by `-l{s}` or `-fPIC`.",8476 "dependency on dynamic library '{s}' requires enabling Position Independent Code. Fixed by '-l{s}' or '-fPIC'.",
8481 .{ lib_name, lib_name },8477 .{ lib_name, lib_name },
8482 );8478 );
8483 }8479 }
...@@ -9014,7 +9010,18 @@ fn zirParam(...@@ -9014,7 +9010,18 @@ fn zirParam(
9014 if (is_comptime and sema.preallocated_new_func != null) {9010 if (is_comptime and sema.preallocated_new_func != null) {
9015 // 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
9016 // function type of the function instruction in this block.9012 // function type of the function instruction in this block.
9017 const coerced_arg = try sema.coerce(block, param_ty, arg, src);9013 const coerced_arg = sema.coerce(block, param_ty, arg, .unneeded) catch |err| switch (err) {
9014 error.NeededSourceLocation => {
9015 // We are instantiating a generic function and a comptime arg
9016 // cannot be coerced to the param type, but since we don't
9017 // have the callee source location return `GenericPoison`
9018 // so that the instantiation is failed and the coercion
9019 // is handled by comptime call logic instead.
9020 assert(sema.is_generic_instantiation);
9021 return error.GenericPoison;
9022 },
9023 else => return err,
9024 };
9018 sema.inst_map.putAssumeCapacity(inst, coerced_arg);9025 sema.inst_map.putAssumeCapacity(inst, coerced_arg);
9019 return;9026 return;
9020 }9027 }
...@@ -19529,13 +19536,34 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air...@@ -19529,13 +19536,34 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
19529 const operand_info = operand_ty.ptrInfo().data;19536 const operand_info = operand_ty.ptrInfo().data;
19530 const dest_info = dest_ty.ptrInfo().data;19537 const dest_info = dest_ty.ptrInfo().data;
19531 if (!operand_info.mutable and dest_info.mutable) {19538 if (!operand_info.mutable and dest_info.mutable) {
19532 return sema.fail(block, src, "cast discards const qualifier", .{});19539 const msg = msg: {
19540 const msg = try sema.errMsg(block, src, "cast discards const qualifier", .{});
19541 errdefer msg.destroy(sema.gpa);
19542
19543 try sema.errNote(block, src, msg, "consider using '@qualCast'", .{});
19544 break :msg msg;
19545 };
19546 return sema.failWithOwnedErrorMsg(msg);
19533 }19547 }
19534 if (operand_info.@"volatile" and !dest_info.@"volatile") {19548 if (operand_info.@"volatile" and !dest_info.@"volatile") {
19535 return sema.fail(block, src, "cast discards volatile qualifier", .{});19549 const msg = msg: {
19550 const msg = try sema.errMsg(block, src, "cast discards volatile qualifier", .{});
19551 errdefer msg.destroy(sema.gpa);
19552
19553 try sema.errNote(block, src, msg, "consider using '@qualCast'", .{});
19554 break :msg msg;
19555 };
19556 return sema.failWithOwnedErrorMsg(msg);
19536 }19557 }
19537 if (operand_info.@"addrspace" != dest_info.@"addrspace") {19558 if (operand_info.@"addrspace" != dest_info.@"addrspace") {
19538 return sema.fail(block, src, "cast changes pointer address space", .{});19559 const msg = msg: {
19560 const msg = try sema.errMsg(block, src, "cast changes pointer address space", .{});
19561 errdefer msg.destroy(sema.gpa);
19562
19563 try sema.errNote(block, src, msg, "consider using '@addrSpaceCast'", .{});
19564 break :msg msg;
19565 };
19566 return sema.failWithOwnedErrorMsg(msg);
19539 }19567 }
1954019568
19541 const dest_is_slice = dest_ty.isSlice();19569 const dest_is_slice = dest_ty.isSlice();
...@@ -19590,6 +19618,8 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air...@@ -19590,6 +19618,8 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
19590 try sema.errNote(block, dest_ty_src, msg, "'{}' has alignment '{d}'", .{19618 try sema.errNote(block, dest_ty_src, msg, "'{}' has alignment '{d}'", .{
19591 dest_ty.fmt(sema.mod), dest_align,19619 dest_ty.fmt(sema.mod), dest_align,
19592 });19620 });
19621
19622 try sema.errNote(block, src, msg, "consider using '@alignCast'", .{});
19593 break :msg msg;19623 break :msg msg;
19594 };19624 };
19595 return sema.failWithOwnedErrorMsg(msg);19625 return sema.failWithOwnedErrorMsg(msg);
...@@ -19625,6 +19655,49 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air...@@ -19625,6 +19655,49 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
19625 return block.addBitCast(aligned_dest_ty, ptr);19655 return block.addBitCast(aligned_dest_ty, ptr);
19626}19656}
1962719657
19658fn zirQualCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
19659 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
19660 const src = inst_data.src();
19661 const dest_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
19662 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
19663 const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
19664 const dest_ty = try sema.resolveType(block, dest_ty_src, extra.lhs);
19665 const operand = try sema.resolveInst(extra.rhs);
19666 const operand_ty = sema.typeOf(operand);
19667
19668 try sema.checkPtrType(block, dest_ty_src, dest_ty);
19669 try sema.checkPtrOperand(block, operand_src, operand_ty);
19670
19671 var operand_payload = operand_ty.ptrInfo();
19672 var dest_info = dest_ty.ptrInfo();
19673
19674 operand_payload.data.mutable = dest_info.data.mutable;
19675 operand_payload.data.@"volatile" = dest_info.data.@"volatile";
19676
19677 const altered_operand_ty = Type.initPayload(&operand_payload.base);
19678 if (!altered_operand_ty.eql(dest_ty, sema.mod)) {
19679 const msg = msg: {
19680 const msg = try sema.errMsg(block, src, "'@qualCast' can only modify 'const' and 'volatile' qualifiers", .{});
19681 errdefer msg.destroy(sema.gpa);
19682
19683 dest_info.data.mutable = !operand_ty.isConstPtr();
19684 dest_info.data.@"volatile" = operand_ty.isVolatilePtr();
19685 const altered_dest_ty = Type.initPayload(&dest_info.base);
19686 try sema.errNote(block, src, msg, "expected type '{}'", .{altered_dest_ty.fmt(sema.mod)});
19687 try sema.errNote(block, src, msg, "got type '{}'", .{operand_ty.fmt(sema.mod)});
19688 break :msg msg;
19689 };
19690 return sema.failWithOwnedErrorMsg(msg);
19691 }
19692
19693 if (try sema.resolveMaybeUndefVal(operand)) |operand_val| {
19694 return sema.addConstant(dest_ty, operand_val);
19695 }
19696
19697 try sema.requireRuntimeBlock(block, src, null);
19698 return block.addBitCast(dest_ty, operand);
19699}
19700
19628fn 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 {
19629 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;19702 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
19630 const src = inst_data.src();19703 const src = inst_data.src();
...@@ -25141,7 +25214,7 @@ fn coerceExtra(...@@ -25141,7 +25214,7 @@ fn coerceExtra(
25141 (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)
25142 {25215 {
25143 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", .{});
25144 try sema.errNote(block, inst_src, msg, "consider using `try`, `catch`, or `if`", .{});25217 try sema.errNote(block, inst_src, msg, "consider using 'try', 'catch', or 'if'", .{});
25145 }25218 }
2514625219
25147 // ?T to T25220 // ?T to T
...@@ -25150,7 +25223,7 @@ fn coerceExtra(...@@ -25150,7 +25223,7 @@ fn coerceExtra(
25150 (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)
25151 {25224 {
25152 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", .{});
25153 try sema.errNote(block, inst_src, msg, "consider using `.?`, `orelse`, or `if`", .{});25226 try sema.errNote(block, inst_src, msg, "consider using '.?', 'orelse', or 'if'", .{});
25154 }25227 }
2515525228
25156 try in_memory_result.report(sema, block, inst_src, msg);25229 try in_memory_result.report(sema, block, inst_src, msg);
...@@ -26076,7 +26149,7 @@ fn coerceVarArgParam(...@@ -26076,7 +26149,7 @@ fn coerceVarArgParam(
26076 .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", .{}),
26077 .Float => float: {26150 .Float => float: {
26078 const target = sema.mod.getTarget();26151 const target = sema.mod.getTarget();
26079 const double_bits = @import("type.zig").CType.sizeInBits(.double, target);26152 const double_bits = target.c_type_bit_size(.double);
26080 const inst_bits = uncasted_ty.floatBits(sema.mod.getTarget());26153 const inst_bits = uncasted_ty.floatBits(sema.mod.getTarget());
26081 if (inst_bits >= double_bits) break :float inst;26154 if (inst_bits >= double_bits) break :float inst;
26082 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+70-51
...@@ -203,13 +203,7 @@ const DbgInfoReloc = struct {...@@ -203,13 +203,7 @@ const DbgInfoReloc = struct {
203 else => unreachable, // not a possible argument203 else => unreachable, // not a possible argument
204204
205 };205 };
206 try dw.genArgDbgInfo(206 try dw.genArgDbgInfo(reloc.name, reloc.ty, function.mod_fn.owner_decl, loc);
207 reloc.name,
208 reloc.ty,
209 function.bin_file.tag,
210 function.mod_fn.owner_decl,
211 loc,
212 );
213 },207 },
214 .plan9 => {},208 .plan9 => {},
215 .none => {},209 .none => {},
...@@ -255,14 +249,7 @@ const DbgInfoReloc = struct {...@@ -255,14 +249,7 @@ const DbgInfoReloc = struct {
255 break :blk .nop;249 break :blk .nop;
256 },250 },
257 };251 };
258 try dw.genVarDbgInfo(252 try dw.genVarDbgInfo(reloc.name, reloc.ty, function.mod_fn.owner_decl, is_ptr, loc);
259 reloc.name,
260 reloc.ty,
261 function.bin_file.tag,
262 function.mod_fn.owner_decl,
263 is_ptr,
264 loc,
265 );
266 },253 },
267 .plan9 => {},254 .plan9 => {},
268 .none => {},255 .none => {},
...@@ -4019,11 +4006,17 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type...@@ -4019,11 +4006,17 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
4019 .direct => .load_memory_ptr_direct,4006 .direct => .load_memory_ptr_direct,
4020 .import => unreachable,4007 .import => unreachable,
4021 };4008 };
4022 const mod = self.bin_file.options.module.?;
4023 const owner_decl = mod.declPtr(self.mod_fn.owner_decl);
4024 const atom_index = switch (self.bin_file.tag) {4009 const atom_index = switch (self.bin_file.tag) {
4025 .macho => owner_decl.link.macho.getSymbolIndex().?,4010 .macho => blk: {
4026 .coff => owner_decl.link.coff.getSymbolIndex().?,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 },
4027 else => unreachable, // unsupported target format4020 else => unreachable, // unsupported target format
4028 };4021 };
4029 _ = try self.addInst(.{4022 _ = try self.addInst(.{
...@@ -4301,34 +4294,37 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier...@@ -4301,34 +4294,37 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
4301 if (self.air.value(callee)) |func_value| {4294 if (self.air.value(callee)) |func_value| {
4302 if (func_value.castTag(.function)) |func_payload| {4295 if (func_value.castTag(.function)) |func_payload| {
4303 const func = func_payload.data;4296 const func = func_payload.data;
4304 const fn_owner_decl = mod.declPtr(func.owner_decl);
43054297
4306 if (self.bin_file.cast(link.File.Elf)) |elf_file| {4298 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
4307 try fn_owner_decl.link.elf.ensureInitialized(elf_file);4299 const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl);
4308 const got_addr = @intCast(u32, fn_owner_decl.link.elf.getOffsetTableAddress(elf_file));4300 const atom = elf_file.getAtom(atom_index);
4301 const got_addr = @intCast(u32, atom.getOffsetTableAddress(elf_file));
4309 try self.genSetReg(Type.initTag(.usize), .x30, .{ .memory = got_addr });4302 try self.genSetReg(Type.initTag(.usize), .x30, .{ .memory = got_addr });
4310 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {4303 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
4311 try fn_owner_decl.link.macho.ensureInitialized(macho_file);4304 const atom = try macho_file.getOrCreateAtomForDecl(func.owner_decl);
4305 const sym_index = macho_file.getAtom(atom).getSymbolIndex().?;
4312 try self.genSetReg(Type.initTag(.u64), .x30, .{4306 try self.genSetReg(Type.initTag(.u64), .x30, .{
4313 .linker_load = .{4307 .linker_load = .{
4314 .type = .got,4308 .type = .got,
4315 .sym_index = fn_owner_decl.link.macho.getSymbolIndex().?,4309 .sym_index = sym_index,
4316 },4310 },
4317 });4311 });
4318 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {4312 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
4319 try fn_owner_decl.link.coff.ensureInitialized(coff_file);4313 const atom = try coff_file.getOrCreateAtomForDecl(func.owner_decl);
4314 const sym_index = coff_file.getAtom(atom).getSymbolIndex().?;
4320 try self.genSetReg(Type.initTag(.u64), .x30, .{4315 try self.genSetReg(Type.initTag(.u64), .x30, .{
4321 .linker_load = .{4316 .linker_load = .{
4322 .type = .got,4317 .type = .got,
4323 .sym_index = fn_owner_decl.link.coff.getSymbolIndex().?,4318 .sym_index = sym_index,
4324 },4319 },
4325 });4320 });
4326 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {4321 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
4327 try p9.seeDecl(func.owner_decl);4322 const decl_block_index = try p9.seeDecl(func.owner_decl);
4323 const decl_block = p9.getDeclBlock(decl_block_index);
4328 const ptr_bits = self.target.cpu.arch.ptrBitWidth();4324 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
4329 const ptr_bytes: u64 = @divExact(ptr_bits, 8);4325 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
4330 const got_addr = p9.bases.data;4326 const got_addr = p9.bases.data;
4331 const got_index = fn_owner_decl.link.plan9.got_index.?;4327 const got_index = decl_block.got_index.?;
4332 const fn_got_addr = got_addr + got_index * ptr_bytes;4328 const fn_got_addr = got_addr + got_index * ptr_bytes;
4333 try self.genSetReg(Type.initTag(.usize), .x30, .{ .memory = fn_got_addr });4329 try self.genSetReg(Type.initTag(.usize), .x30, .{ .memory = fn_got_addr });
4334 } else unreachable;4330 } else unreachable;
...@@ -4349,11 +4345,13 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier...@@ -4349,11 +4345,13 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
43494345
4350 if (self.bin_file.cast(link.File.MachO)) |macho_file| {4346 if (self.bin_file.cast(link.File.MachO)) |macho_file| {
4351 const sym_index = try macho_file.getGlobalSymbol(mem.sliceTo(decl_name, 0));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().?;
4352 _ = try self.addInst(.{4350 _ = try self.addInst(.{
4353 .tag = .call_extern,4351 .tag = .call_extern,
4354 .data = .{4352 .data = .{
4355 .relocation = .{4353 .relocation = .{
4356 .atom_index = mod.declPtr(self.mod_fn.owner_decl).link.macho.getSymbolIndex().?,4354 .atom_index = atom_index,
4357 .sym_index = sym_index,4355 .sym_index = sym_index,
4358 },4356 },
4359 },4357 },
...@@ -5488,11 +5486,17 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro...@@ -5488,11 +5486,17 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
5488 .direct => .load_memory_ptr_direct,5486 .direct => .load_memory_ptr_direct,
5489 .import => unreachable,5487 .import => unreachable,
5490 };5488 };
5491 const mod = self.bin_file.options.module.?;
5492 const owner_decl = mod.declPtr(self.mod_fn.owner_decl);
5493 const atom_index = switch (self.bin_file.tag) {5489 const atom_index = switch (self.bin_file.tag) {
5494 .macho => owner_decl.link.macho.getSymbolIndex().?,5490 .macho => blk: {
5495 .coff => owner_decl.link.coff.getSymbolIndex().?,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 },
5496 else => unreachable, // unsupported target format5500 else => unreachable, // unsupported target format
5497 };5501 };
5498 _ = try self.addInst(.{5502 _ = try self.addInst(.{
...@@ -5602,11 +5606,17 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void...@@ -5602,11 +5606,17 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
5602 .direct => .load_memory_direct,5606 .direct => .load_memory_direct,
5603 .import => .load_memory_import,5607 .import => .load_memory_import,
5604 };5608 };
5605 const mod = self.bin_file.options.module.?;
5606 const owner_decl = mod.declPtr(self.mod_fn.owner_decl);
5607 const atom_index = switch (self.bin_file.tag) {5609 const atom_index = switch (self.bin_file.tag) {
5608 .macho => owner_decl.link.macho.getSymbolIndex().?,5610 .macho => blk: {
5609 .coff => owner_decl.link.coff.getSymbolIndex().?,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 },
5610 else => unreachable, // unsupported target format5620 else => unreachable, // unsupported target format
5611 };5621 };
5612 _ = try self.addInst(.{5622 _ = try self.addInst(.{
...@@ -5796,11 +5806,17 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I...@@ -5796,11 +5806,17 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I
5796 .direct => .load_memory_ptr_direct,5806 .direct => .load_memory_ptr_direct,
5797 .import => unreachable,5807 .import => unreachable,
5798 };5808 };
5799 const mod = self.bin_file.options.module.?;
5800 const owner_decl = mod.declPtr(self.mod_fn.owner_decl);
5801 const atom_index = switch (self.bin_file.tag) {5809 const atom_index = switch (self.bin_file.tag) {
5802 .macho => owner_decl.link.macho.getSymbolIndex().?,5810 .macho => blk: {
5803 .coff => owner_decl.link.coff.getSymbolIndex().?,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 },
5804 else => unreachable, // unsupported target format5820 else => unreachable, // unsupported target format
5805 };5821 };
5806 _ = try self.addInst(.{5822 _ = try self.addInst(.{
...@@ -6119,23 +6135,27 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne...@@ -6119,23 +6135,27 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne
6119 mod.markDeclAlive(decl);6135 mod.markDeclAlive(decl);
61206136
6121 if (self.bin_file.cast(link.File.Elf)) |elf_file| {6137 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
6122 try decl.link.elf.ensureInitialized(elf_file);6138 const atom_index = try elf_file.getOrCreateAtomForDecl(decl_index);
6123 return MCValue{ .memory = decl.link.elf.getOffsetTableAddress(elf_file) };6139 const atom = elf_file.getAtom(atom_index);
6140 return MCValue{ .memory = atom.getOffsetTableAddress(elf_file) };
6124 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {6141 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
6125 try decl.link.macho.ensureInitialized(macho_file);6142 const atom = try macho_file.getOrCreateAtomForDecl(decl_index);
6143 const sym_index = macho_file.getAtom(atom).getSymbolIndex().?;
6126 return MCValue{ .linker_load = .{6144 return MCValue{ .linker_load = .{
6127 .type = .got,6145 .type = .got,
6128 .sym_index = decl.link.macho.getSymbolIndex().?,6146 .sym_index = sym_index,
6129 } };6147 } };
6130 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {6148 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
6131 try decl.link.coff.ensureInitialized(coff_file);6149 const atom_index = try coff_file.getOrCreateAtomForDecl(decl_index);
6150 const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?;
6132 return MCValue{ .linker_load = .{6151 return MCValue{ .linker_load = .{
6133 .type = .got,6152 .type = .got,
6134 .sym_index = decl.link.coff.getSymbolIndex().?,6153 .sym_index = sym_index,
6135 } };6154 } };
6136 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {6155 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
6137 try p9.seeDecl(decl_index);6156 const decl_block_index = try p9.seeDecl(decl_index);
6138 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;
6139 return MCValue{ .memory = got_addr };6159 return MCValue{ .memory = got_addr };
6140 } else {6160 } else {
6141 return self.fail("TODO codegen non-ELF const Decl pointer", .{});6161 return self.fail("TODO codegen non-ELF const Decl pointer", .{});
...@@ -6148,8 +6168,7 @@ fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue {...@@ -6148,8 +6168,7 @@ fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue {
6148 return self.fail("lowering unnamed constant failed: {s}", .{@errorName(err)});6168 return self.fail("lowering unnamed constant failed: {s}", .{@errorName(err)});
6149 };6169 };
6150 if (self.bin_file.cast(link.File.Elf)) |elf_file| {6170 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
6151 const vaddr = elf_file.local_symbols.items[local_sym_index].st_value;6171 return MCValue{ .memory = elf_file.getSymbol(local_sym_index).st_value };
6152 return MCValue{ .memory = vaddr };
6153 } else if (self.bin_file.cast(link.File.MachO)) |_| {6172 } else if (self.bin_file.cast(link.File.MachO)) |_| {
6154 return MCValue{ .linker_load = .{6173 return MCValue{ .linker_load = .{
6155 .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+12-25
...@@ -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 => {},
...@@ -4256,12 +4243,11 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier...@@ -4256,12 +4243,11 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
4256 if (self.air.value(callee)) |func_value| {4243 if (self.air.value(callee)) |func_value| {
4257 if (func_value.castTag(.function)) |func_payload| {4244 if (func_value.castTag(.function)) |func_payload| {
4258 const func = func_payload.data;4245 const func = func_payload.data;
4259 const mod = self.bin_file.options.module.?;
4260 const fn_owner_decl = mod.declPtr(func.owner_decl);
42614246
4262 if (self.bin_file.cast(link.File.Elf)) |elf_file| {4247 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
4263 try fn_owner_decl.link.elf.ensureInitialized(elf_file);4248 const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl);
4264 const got_addr = @intCast(u32, fn_owner_decl.link.elf.getOffsetTableAddress(elf_file));4249 const atom = elf_file.getAtom(atom_index);
4250 const got_addr = @intCast(u32, atom.getOffsetTableAddress(elf_file));
4265 try self.genSetReg(Type.initTag(.usize), .lr, .{ .memory = got_addr });4251 try self.genSetReg(Type.initTag(.usize), .lr, .{ .memory = got_addr });
4266 } else if (self.bin_file.cast(link.File.MachO)) |_| {4252 } else if (self.bin_file.cast(link.File.MachO)) |_| {
4267 unreachable; // unsupported architecture for MachO4253 unreachable; // unsupported architecture for MachO
...@@ -6084,15 +6070,17 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne...@@ -6084,15 +6070,17 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne
6084 mod.markDeclAlive(decl);6070 mod.markDeclAlive(decl);
60856071
6086 if (self.bin_file.cast(link.File.Elf)) |elf_file| {6072 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
6087 try decl.link.elf.ensureInitialized(elf_file);6073 const atom_index = try elf_file.getOrCreateAtomForDecl(decl_index);
6088 return MCValue{ .memory = decl.link.elf.getOffsetTableAddress(elf_file) };6074 const atom = elf_file.getAtom(atom_index);
6075 return MCValue{ .memory = atom.getOffsetTableAddress(elf_file) };
6089 } else if (self.bin_file.cast(link.File.MachO)) |_| {6076 } else if (self.bin_file.cast(link.File.MachO)) |_| {
6090 unreachable; // unsupported architecture for MachO6077 unreachable; // unsupported architecture for MachO
6091 } else if (self.bin_file.cast(link.File.Coff)) |_| {6078 } else if (self.bin_file.cast(link.File.Coff)) |_| {
6092 return self.fail("TODO codegen COFF const Decl pointer", .{});6079 return self.fail("TODO codegen COFF const Decl pointer", .{});
6093 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {6080 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
6094 try p9.seeDecl(decl_index);6081 const decl_block_index = try p9.seeDecl(decl_index);
6095 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;
6096 return MCValue{ .memory = got_addr };6084 return MCValue{ .memory = got_addr };
6097 } else {6085 } else {
6098 return self.fail("TODO codegen non-ELF const Decl pointer", .{});6086 return self.fail("TODO codegen non-ELF const Decl pointer", .{});
...@@ -6106,8 +6094,7 @@ fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue {...@@ -6106,8 +6094,7 @@ fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue {
6106 return self.fail("lowering unnamed constant failed: {s}", .{@errorName(err)});6094 return self.fail("lowering unnamed constant failed: {s}", .{@errorName(err)});
6107 };6095 };
6108 if (self.bin_file.cast(link.File.Elf)) |elf_file| {6096 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
6109 const vaddr = elf_file.local_symbols.items[local_sym_index].st_value;6097 return MCValue{ .memory = elf_file.getSymbol(local_sym_index).st_value };
6110 return MCValue{ .memory = vaddr };
6111 } else if (self.bin_file.cast(link.File.MachO)) |_| {6098 } else if (self.bin_file.cast(link.File.MachO)) |_| {
6112 unreachable;6099 unreachable;
6113 } else if (self.bin_file.cast(link.File.Coff)) |_| {6100 } else if (self.bin_file.cast(link.File.Coff)) |_| {
src/arch/riscv64/CodeGen.zig+13-20
...@@ -1615,13 +1615,9 @@ fn genArgDbgInfo(self: Self, inst: Air.Inst.Index, mcv: MCValue) !void {...@@ -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,12 +1717,9 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier...@@ -1721,12 +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 mod = self.bin_file.options.module.?;1721 const atom = elf_file.getAtom(atom_index);
1726 const fn_owner_decl = mod.declPtr(func.owner_decl);1722 const got_addr = @intCast(u32, atom.getOffsetTableAddress(elf_file));
1727 try fn_owner_decl.link.elf.ensureInitialized(elf_file);
1728 const got_addr = @intCast(u32, fn_owner_decl.link.elf.getOffsetTableAddress(elf_file));
1729
1730 try self.genSetReg(Type.initTag(.usize), .ra, .{ .memory = got_addr });1723 try self.genSetReg(Type.initTag(.usize), .ra, .{ .memory = got_addr });
1731 _ = try self.addInst(.{1724 _ = try self.addInst(.{
1732 .tag = .jalr,1725 .tag = .jalr,
...@@ -2553,17 +2546,17 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne...@@ -2553,17 +2546,17 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne
2553 const decl = mod.declPtr(decl_index);2546 const decl = mod.declPtr(decl_index);
2554 mod.markDeclAlive(decl);2547 mod.markDeclAlive(decl);
2555 if (self.bin_file.cast(link.File.Elf)) |elf_file| {2548 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
2556 try decl.link.elf.ensureInitialized(elf_file);2549 const atom_index = try elf_file.getOrCreateAtomForDecl(decl_index);
2557 return MCValue{ .memory = decl.link.elf.getOffsetTableAddress(elf_file) };2550 const atom = elf_file.getAtom(atom_index);
2551 return MCValue{ .memory = atom.getOffsetTableAddress(elf_file) };
2558 } else if (self.bin_file.cast(link.File.MachO)) |_| {2552 } else if (self.bin_file.cast(link.File.MachO)) |_| {
2559 // TODO I'm hacking my way through here by repurposing .memory for storing2553 unreachable;
2560 // index to the GOT target symbol index.
2561 return MCValue{ .memory = decl.link.macho.sym_index };
2562 } else if (self.bin_file.cast(link.File.Coff)) |_| {2554 } else if (self.bin_file.cast(link.File.Coff)) |_| {
2563 return self.fail("TODO codegen COFF const Decl pointer", .{});2555 return self.fail("TODO codegen COFF const Decl pointer", .{});
2564 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {2556 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
2565 try p9.seeDecl(decl_index);2557 const decl_block_index = try p9.seeDecl(decl_index);
2566 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;
2567 return MCValue{ .memory = got_addr };2560 return MCValue{ .memory = got_addr };
2568 } else {2561 } else {
2569 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+9-13
...@@ -1216,11 +1216,10 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier...@@ -1216,11 +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 mod = self.bin_file.options.module.?;
1220 const fn_owner_decl = mod.declPtr(func.owner_decl);
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 try fn_owner_decl.link.elf.ensureInitialized(elf_file);1220 const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl);
1223 break :blk @intCast(u32, fn_owner_decl.link.elf.getOffsetTableAddress(elf_file));1221 const atom = elf_file.getAtom(atom_index);
1222 break :blk @intCast(u32, atom.getOffsetTableAddress(elf_file));
1224 } else unreachable;1223 } else unreachable;
12251224
1226 try self.genSetReg(Type.initTag(.usize), .o7, .{ .memory = got_addr });1225 try self.genSetReg(Type.initTag(.usize), .o7, .{ .memory = got_addr });
...@@ -3413,13 +3412,9 @@ fn genArgDbgInfo(self: Self, inst: Air.Inst.Index, mcv: MCValue) !void {...@@ -3413,13 +3412,9 @@ fn genArgDbgInfo(self: Self, inst: Air.Inst.Index, mcv: MCValue) !void {
34133412
3414 switch (self.debug_output) {3413 switch (self.debug_output) {
3415 .dwarf => |dw| switch (mcv) {3414 .dwarf => |dw| switch (mcv) {
3416 .register => |reg| try dw.genArgDbgInfo(3415 .register => |reg| try dw.genArgDbgInfo(name, ty, self.mod_fn.owner_decl, .{
3417 name,3416 .register = reg.dwarfLocOp(),
3418 ty,3417 }),
3419 self.bin_file.tag,
3420 self.mod_fn.owner_decl,
3421 .{ .register = reg.dwarfLocOp() },
3422 ),
3423 else => {},3418 else => {},
3424 },3419 },
3425 else => {},3420 else => {},
...@@ -4205,8 +4200,9 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne...@@ -4205,8 +4200,9 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne
42054200
4206 mod.markDeclAlive(decl);4201 mod.markDeclAlive(decl);
4207 if (self.bin_file.cast(link.File.Elf)) |elf_file| {4202 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
4208 try decl.link.elf.ensureInitialized(elf_file);4203 const atom_index = try elf_file.getOrCreateAtomForDecl(decl_index);
4209 return MCValue{ .memory = decl.link.elf.getOffsetTableAddress(elf_file) };4204 const atom = elf_file.getAtom(atom_index);
4205 return MCValue{ .memory = atom.getOffsetTableAddress(elf_file) };
4210 } else {4206 } else {
4211 return self.fail("TODO codegen non-ELF const Decl pointer", .{});4207 return self.fail("TODO codegen non-ELF const Decl pointer", .{});
4212 }4208 }
src/arch/wasm/CodeGen.zig+20-20
...@@ -1194,7 +1194,7 @@ fn genFunc(func: *CodeGen) InnerError!void {...@@ -1194,7 +1194,7 @@ fn genFunc(func: *CodeGen) InnerError!void {
1194 const fn_info = func.decl.ty.fnInfo();1194 const fn_info = func.decl.ty.fnInfo();
1195 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);
1196 defer func_type.deinit(func.gpa);1196 defer func_type.deinit(func.gpa);
1197 func.decl.fn_link.wasm.type_index = try func.bin_file.putOrGetFuncType(func_type);1197 _ = try func.bin_file.storeDeclType(func.decl_index, func_type);
11981198
1199 var cc_result = try func.resolveCallingConventionValues(func.decl.ty);1199 var cc_result = try func.resolveCallingConventionValues(func.decl.ty);
1200 defer cc_result.deinit(func.gpa);1200 defer cc_result.deinit(func.gpa);
...@@ -1269,10 +1269,10 @@ fn genFunc(func: *CodeGen) InnerError!void {...@@ -1269,10 +1269,10 @@ fn genFunc(func: *CodeGen) InnerError!void {
12691269
1270 var emit: Emit = .{1270 var emit: Emit = .{
1271 .mir = mir,1271 .mir = mir,
1272 .bin_file = &func.bin_file.base,1272 .bin_file = func.bin_file,
1273 .code = func.code,1273 .code = func.code,
1274 .locals = func.locals.items,1274 .locals = func.locals.items,
1275 .decl = func.decl,1275 .decl_index = func.decl_index,
1276 .dbg_output = func.debug_output,1276 .dbg_output = func.debug_output,
1277 .prev_di_line = 0,1277 .prev_di_line = 0,
1278 .prev_di_column = 0,1278 .prev_di_column = 0,
...@@ -2117,33 +2117,31 @@ fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModif...@@ -2117,33 +2117,31 @@ fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModif
2117 const fn_info = fn_ty.fnInfo();2117 const fn_info = fn_ty.fnInfo();
2118 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);
21192119
2120 const callee: ?*Decl = blk: {2120 const callee: ?Decl.Index = blk: {
2121 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;
2122 const module = func.bin_file.base.options.module.?;2122 const module = func.bin_file.base.options.module.?;
21232123
2124 if (func_val.castTag(.function)) |function| {2124 if (func_val.castTag(.function)) |function| {
2125 const decl = module.declPtr(function.data.owner_decl);2125 _ = try func.bin_file.getOrCreateAtomForDecl(function.data.owner_decl);
2126 try decl.link.wasm.ensureInitialized(func.bin_file);2126 break :blk function.data.owner_decl;
2127 break :blk decl;
2128 } else if (func_val.castTag(.extern_fn)) |extern_fn| {2127 } else if (func_val.castTag(.extern_fn)) |extern_fn| {
2129 const ext_decl = module.declPtr(extern_fn.data.owner_decl);2128 const ext_decl = module.declPtr(extern_fn.data.owner_decl);
2130 const ext_info = ext_decl.ty.fnInfo();2129 const ext_info = ext_decl.ty.fnInfo();
2131 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);
2132 defer func_type.deinit(func.gpa);2131 defer func_type.deinit(func.gpa);
2133 const atom = &ext_decl.link.wasm;2132 const atom_index = try func.bin_file.getOrCreateAtomForDecl(extern_fn.data.owner_decl);
2134 try atom.ensureInitialized(func.bin_file);2133 const atom = func.bin_file.getAtomPtr(atom_index);
2135 ext_decl.fn_link.wasm.type_index = try func.bin_file.putOrGetFuncType(func_type);2134 const type_index = try func.bin_file.storeDeclType(extern_fn.data.owner_decl, func_type);
2136 try func.bin_file.addOrUpdateImport(2135 try func.bin_file.addOrUpdateImport(
2137 mem.sliceTo(ext_decl.name, 0),2136 mem.sliceTo(ext_decl.name, 0),
2138 atom.getSymbolIndex().?,2137 atom.getSymbolIndex().?,
2139 ext_decl.getExternFn().?.lib_name,2138 ext_decl.getExternFn().?.lib_name,
2140 ext_decl.fn_link.wasm.type_index,2139 type_index,
2141 );2140 );
2142 break :blk ext_decl;2141 break :blk extern_fn.data.owner_decl;
2143 } else if (func_val.castTag(.decl_ref)) |decl_ref| {2142 } else if (func_val.castTag(.decl_ref)) |decl_ref| {
2144 const decl = module.declPtr(decl_ref.data);2143 _ = try func.bin_file.getOrCreateAtomForDecl(decl_ref.data);
2145 try decl.link.wasm.ensureInitialized(func.bin_file);2144 break :blk decl_ref.data;
2146 break :blk decl;
2147 }2145 }
2148 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()});
2149 };2147 };
...@@ -2164,7 +2162,8 @@ fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModif...@@ -2164,7 +2162,8 @@ fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModif
2164 }2162 }
21652163
2166 if (callee) |direct| {2164 if (callee) |direct| {
2167 try func.addLabel(.call, direct.link.wasm.sym_index);2165 const atom_index = func.bin_file.decls.get(direct).?;
2166 try func.addLabel(.call, func.bin_file.getAtom(atom_index).sym_index);
2168 } else {2167 } else {
2169 // in this case we call a function pointer2168 // in this case we call a function pointer
2170 // so load its value onto the stack2169 // so load its value onto the stack
...@@ -2477,7 +2476,7 @@ fn airArg(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {...@@ -2477,7 +2476,7 @@ fn airArg(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
2477 .dwarf => |dwarf| {2476 .dwarf => |dwarf| {
2478 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;
2479 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);
2480 try dwarf.genArgDbgInfo(name, arg_ty, .wasm, func.mod_fn.owner_decl, .{2479 try dwarf.genArgDbgInfo(name, arg_ty, func.mod_fn.owner_decl, .{
2481 .wasm_local = arg.local.value,2480 .wasm_local = arg.local.value,
2482 });2481 });
2483 },2482 },
...@@ -2760,9 +2759,10 @@ fn lowerDeclRefValue(func: *CodeGen, tv: TypedValue, decl_index: Module.Decl.Ind...@@ -2760,9 +2759,10 @@ fn lowerDeclRefValue(func: *CodeGen, tv: TypedValue, decl_index: Module.Decl.Ind
2760 }2759 }
27612760
2762 module.markDeclAlive(decl);2761 module.markDeclAlive(decl);
2763 try decl.link.wasm.ensureInitialized(func.bin_file);2762 const atom_index = try func.bin_file.getOrCreateAtomForDecl(decl_index);
2763 const atom = func.bin_file.getAtom(atom_index);
27642764
2765 const target_sym_index = decl.link.wasm.sym_index;2765 const target_sym_index = atom.sym_index;
2766 if (decl.ty.zigTypeTag() == .Fn) {2766 if (decl.ty.zigTypeTag() == .Fn) {
2767 try func.bin_file.addTableFunction(target_sym_index);2767 try func.bin_file.addTableFunction(target_sym_index);
2768 return WValue{ .function_index = target_sym_index };2768 return WValue{ .function_index = target_sym_index };
...@@ -5547,7 +5547,7 @@ fn airDbgVar(func: *CodeGen, inst: Air.Inst.Index, is_ptr: bool) !void {...@@ -5547,7 +5547,7 @@ fn airDbgVar(func: *CodeGen, inst: Air.Inst.Index, is_ptr: bool) !void {
5547 break :blk .nop;5547 break :blk .nop;
5548 },5548 },
5549 };5549 };
5550 try func.debug_output.dwarf.genVarDbgInfo(name, ty, .wasm, func.mod_fn.owner_decl, is_ptr, loc);5550 try func.debug_output.dwarf.genVarDbgInfo(name, ty, func.mod_fn.owner_decl, is_ptr, loc);
55515551
5552 func.finishAir(inst, .none, &.{});5552 func.finishAir(inst, .none, &.{});
5553}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+38-33
...@@ -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.getSymbolIndex().?2674 } 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.getSymbolIndex().?;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 => {},
...@@ -3995,19 +3996,19 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier...@@ -3995,19 +3996,19 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
3995 if (self.air.value(callee)) |func_value| {3996 if (self.air.value(callee)) |func_value| {
3996 if (func_value.castTag(.function)) |func_payload| {3997 if (func_value.castTag(.function)) |func_payload| {
3997 const func = func_payload.data;3998 const func = func_payload.data;
3998 const fn_owner_decl = mod.declPtr(func.owner_decl);
39993999
4000 if (self.bin_file.cast(link.File.Elf)) |elf_file| {4000 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
4001 try fn_owner_decl.link.elf.ensureInitialized(elf_file);4001 const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl);
4002 const got_addr = @intCast(u32, fn_owner_decl.link.elf.getOffsetTableAddress(elf_file));4002 const atom = elf_file.getAtom(atom_index);
4003 const got_addr = @intCast(u32, atom.getOffsetTableAddress(elf_file));
4003 _ = try self.addInst(.{4004 _ = try self.addInst(.{
4004 .tag = .call,4005 .tag = .call,
4005 .ops = Mir.Inst.Ops.encode(.{ .flags = 0b01 }),4006 .ops = Mir.Inst.Ops.encode(.{ .flags = 0b01 }),
4006 .data = .{ .imm = got_addr },4007 .data = .{ .imm = got_addr },
4007 });4008 });
4008 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {4009 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
4009 try fn_owner_decl.link.coff.ensureInitialized(coff_file);4010 const atom_index = try coff_file.getOrCreateAtomForDecl(func.owner_decl);
4010 const sym_index = fn_owner_decl.link.coff.getSymbolIndex().?;4011 const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?;
4011 try self.genSetReg(Type.initTag(.usize), .rax, .{4012 try self.genSetReg(Type.initTag(.usize), .rax, .{
4012 .linker_load = .{4013 .linker_load = .{
4013 .type = .got,4014 .type = .got,
...@@ -4023,8 +4024,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier...@@ -4023,8 +4024,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
4023 .data = undefined,4024 .data = undefined,
4024 });4025 });
4025 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {4026 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
4026 try fn_owner_decl.link.macho.ensureInitialized(macho_file);4027 const atom_index = try macho_file.getOrCreateAtomForDecl(func.owner_decl);
4027 const sym_index = fn_owner_decl.link.macho.getSymbolIndex().?;4028 const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?;
4028 try self.genSetReg(Type.initTag(.usize), .rax, .{4029 try self.genSetReg(Type.initTag(.usize), .rax, .{
4029 .linker_load = .{4030 .linker_load = .{
4030 .type = .got,4031 .type = .got,
...@@ -4040,11 +4041,12 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier...@@ -4040,11 +4041,12 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
4040 .data = undefined,4041 .data = undefined,
4041 });4042 });
4042 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {4043 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
4043 try p9.seeDecl(func.owner_decl);4044 const decl_block_index = try p9.seeDecl(func.owner_decl);
4045 const decl_block = p9.getDeclBlock(decl_block_index);
4044 const ptr_bits = self.target.cpu.arch.ptrBitWidth();4046 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
4045 const ptr_bytes: u64 = @divExact(ptr_bits, 8);4047 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
4046 const got_addr = p9.bases.data;4048 const got_addr = p9.bases.data;
4047 const got_index = fn_owner_decl.link.plan9.got_index.?;4049 const got_index = decl_block.got_index.?;
4048 const fn_got_addr = got_addr + got_index * ptr_bytes;4050 const fn_got_addr = got_addr + got_index * ptr_bytes;
4049 _ = try self.addInst(.{4051 _ = try self.addInst(.{
4050 .tag = .call,4052 .tag = .call,
...@@ -4080,15 +4082,15 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier...@@ -4080,15 +4082,15 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier
4080 });4082 });
4081 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {4083 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
4082 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().?;
4083 _ = try self.addInst(.{4087 _ = try self.addInst(.{
4084 .tag = .call_extern,4088 .tag = .call_extern,
4085 .ops = undefined,4089 .ops = undefined,
4086 .data = .{4090 .data = .{ .relocation = .{
4087 .relocation = .{4091 .atom_index = atom_index,
4088 .atom_index = mod.declPtr(self.mod_fn.owner_decl).link.macho.getSymbolIndex().?,4092 .sym_index = sym_index,
4089 .sym_index = sym_index,4093 } },
4090 },
4091 },
4092 });4094 });
4093 } else {4095 } else {
4094 return self.fail("TODO implement calling extern functions", .{});4096 return self.fail("TODO implement calling extern functions", .{});
...@@ -6719,23 +6721,27 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne...@@ -6719,23 +6721,27 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) Inne
6719 module.markDeclAlive(decl);6721 module.markDeclAlive(decl);
67206722
6721 if (self.bin_file.cast(link.File.Elf)) |elf_file| {6723 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
6722 try decl.link.elf.ensureInitialized(elf_file);6724 const atom_index = try elf_file.getOrCreateAtomForDecl(decl_index);
6723 return MCValue{ .memory = decl.link.elf.getOffsetTableAddress(elf_file) };6725 const atom = elf_file.getAtom(atom_index);
6726 return MCValue{ .memory = atom.getOffsetTableAddress(elf_file) };
6724 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {6727 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
6725 try decl.link.macho.ensureInitialized(macho_file);6728 const atom_index = try macho_file.getOrCreateAtomForDecl(decl_index);
6729 const sym_index = macho_file.getAtom(atom_index).getSymbolIndex().?;
6726 return MCValue{ .linker_load = .{6730 return MCValue{ .linker_load = .{
6727 .type = .got,6731 .type = .got,
6728 .sym_index = decl.link.macho.getSymbolIndex().?,6732 .sym_index = sym_index,
6729 } };6733 } };
6730 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {6734 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
6731 try decl.link.coff.ensureInitialized(coff_file);6735 const atom_index = try coff_file.getOrCreateAtomForDecl(decl_index);
6736 const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?;
6732 return MCValue{ .linker_load = .{6737 return MCValue{ .linker_load = .{
6733 .type = .got,6738 .type = .got,
6734 .sym_index = decl.link.coff.getSymbolIndex().?,6739 .sym_index = sym_index,
6735 } };6740 } };
6736 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {6741 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
6737 try p9.seeDecl(decl_index);6742 const decl_block_index = try p9.seeDecl(decl_index);
6738 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;
6739 return MCValue{ .memory = got_addr };6745 return MCValue{ .memory = got_addr };
6740 } else {6746 } else {
6741 return self.fail("TODO codegen non-ELF const Decl pointer", .{});6747 return self.fail("TODO codegen non-ELF const Decl pointer", .{});
...@@ -6748,8 +6754,7 @@ fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue {...@@ -6748,8 +6754,7 @@ fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue {
6748 return self.fail("lowering unnamed constant failed: {s}", .{@errorName(err)});6754 return self.fail("lowering unnamed constant failed: {s}", .{@errorName(err)});
6749 };6755 };
6750 if (self.bin_file.cast(link.File.Elf)) |elf_file| {6756 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
6751 const vaddr = elf_file.local_symbols.items[local_sym_index].st_value;6757 return MCValue{ .memory = elf_file.getSymbol(local_sym_index).st_value };
6752 return MCValue{ .memory = vaddr };
6753 } else if (self.bin_file.cast(link.File.MachO)) |_| {6758 } else if (self.bin_file.cast(link.File.MachO)) |_| {
6754 return MCValue{ .linker_load = .{6759 return MCValue{ .linker_load = .{
6755 .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/c.zig-1
...@@ -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;
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");
...@@ -11043,8 +11042,8 @@ fn backendSupportsF128(target: std.Target) bool {...@@ -11043,8 +11042,8 @@ fn backendSupportsF128(target: std.Target) bool {
11043fn intrinsicsAllowed(scalar_ty: Type, target: std.Target) bool {11042fn intrinsicsAllowed(scalar_ty: Type, target: std.Target) bool {
11044 return switch (scalar_ty.tag()) {11043 return switch (scalar_ty.tag()) {
11045 .f16 => backendSupportsF16(target),11044 .f16 => backendSupportsF16(target),
11046 .f80 => (CType.longdouble.sizeInBits(target) == 80) and backendSupportsF80(target),11045 .f80 => (target.c_type_bit_size(.longdouble) == 80) and backendSupportsF80(target),
11047 .f128 => (CType.longdouble.sizeInBits(target) == 128) and backendSupportsF128(target),11046 .f128 => (target.c_type_bit_size(.longdouble) == 128) and backendSupportsF128(target),
11048 else => true,11047 else => true,
11049 };11048 };
11050}11049}
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+9-41
...@@ -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
...@@ -580,22 +547,23 @@ pub const File = struct {...@@ -580,22 +547,23 @@ pub const File = struct {
580 }547 }
581 }548 }
582549
583 pub fn updateDeclLineNumber(base: *File, module: *Module, decl: *Module.Decl) UpdateDeclError!void {550 pub fn updateDeclLineNumber(base: *File, module: *Module, decl_index: Module.Decl.Index) UpdateDeclError!void {
551 const decl = module.declPtr(decl_index);
584 log.debug("updateDeclLineNumber {*} ({s}), line={}", .{552 log.debug("updateDeclLineNumber {*} ({s}), line={}", .{
585 decl, decl.name, decl.src_line + 1,553 decl, decl.name, decl.src_line + 1,
586 });554 });
587 assert(decl.has_tv);555 assert(decl.has_tv);
588 if (build_options.only_c) {556 if (build_options.only_c) {
589 assert(base.tag == .c);557 assert(base.tag == .c);
590 return @fieldParentPtr(C, "base", base).updateDeclLineNumber(module, decl);558 return @fieldParentPtr(C, "base", base).updateDeclLineNumber(module, decl_index);
591 }559 }
592 switch (base.tag) {560 switch (base.tag) {
593 .coff => return @fieldParentPtr(Coff, "base", base).updateDeclLineNumber(module, decl),561 .coff => return @fieldParentPtr(Coff, "base", base).updateDeclLineNumber(module, decl_index),
594 .elf => return @fieldParentPtr(Elf, "base", base).updateDeclLineNumber(module, decl),562 .elf => return @fieldParentPtr(Elf, "base", base).updateDeclLineNumber(module, decl_index),
595 .macho => return @fieldParentPtr(MachO, "base", base).updateDeclLineNumber(module, decl),563 .macho => return @fieldParentPtr(MachO, "base", base).updateDeclLineNumber(module, decl_index),
596 .c => return @fieldParentPtr(C, "base", base).updateDeclLineNumber(module, decl),564 .c => return @fieldParentPtr(C, "base", base).updateDeclLineNumber(module, decl_index),
597 .wasm => return @fieldParentPtr(Wasm, "base", base).updateDeclLineNumber(module, decl),565 .wasm => return @fieldParentPtr(Wasm, "base", base).updateDeclLineNumber(module, decl_index),
598 .plan9 => return @fieldParentPtr(Plan9, "base", base).updateDeclLineNumber(module, decl),566 .plan9 => return @fieldParentPtr(Plan9, "base", base).updateDeclLineNumber(module, decl_index),
599 .spirv, .nvptx => {},567 .spirv, .nvptx => {},
600 }568 }
601 }569 }
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+265-210
...@@ -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,14 +507,15 @@ fn growSectionVM(self: *Coff, sect_id: u32, needed_size: u32) !void {...@@ -480,14 +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
483fn allocateAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u32 {510fn allocateAtom(self: *Coff, atom_index: Atom.Index, new_atom_size: u32, alignment: u32) !u32 {
484 const tracy = trace(@src());511 const tracy = trace(@src());
485 defer tracy.end();512 defer tracy.end();
486513
514 const atom = self.getAtom(atom_index);
487 const sect_id = @enumToInt(atom.getSymbol(self).section_number) - 1;515 const sect_id = @enumToInt(atom.getSymbol(self).section_number) - 1;
488 const header = &self.sections.items(.header)[sect_id];516 const header = &self.sections.items(.header)[sect_id];
489 const free_list = &self.sections.items(.free_list)[sect_id];517 const free_list = &self.sections.items(.free_list)[sect_id];
490 const maybe_last_atom = &self.sections.items(.last_atom)[sect_id];518 const maybe_last_atom_index = &self.sections.items(.last_atom_index)[sect_id];
491 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;
492520
493 // 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,
...@@ -495,7 +523,7 @@ fn allocateAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u...@@ -495,7 +523,7 @@ fn allocateAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u
495 // 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
496 // 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
497 // 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.
498 var atom_placement: ?*Atom = null;526 var atom_placement: ?Atom.Index = null;
499 var free_list_removal: ?usize = null;527 var free_list_removal: ?usize = null;
500528
501 // First we look for an appropriately sized free list node.529 // First we look for an appropriately sized free list node.
...@@ -503,7 +531,8 @@ fn allocateAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u...@@ -503,7 +531,8 @@ fn allocateAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u
503 var vaddr = blk: {531 var vaddr = blk: {
504 var i: usize = 0;532 var i: usize = 0;
505 while (i < free_list.items.len) {533 while (i < free_list.items.len) {
506 const big_atom = free_list.items[i];534 const big_atom_index = free_list.items[i];
535 const big_atom = self.getAtom(big_atom_index);
507 // 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.
508 // Is it enough that we could fit this new atom?537 // Is it enough that we could fit this new atom?
509 const sym = big_atom.getSymbol(self);538 const sym = big_atom.getSymbol(self);
...@@ -531,34 +560,43 @@ fn allocateAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u...@@ -531,34 +560,43 @@ fn allocateAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u
531 const keep_free_list_node = remaining_capacity >= min_text_capacity;560 const keep_free_list_node = remaining_capacity >= min_text_capacity;
532561
533 // 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.
534 atom_placement = big_atom;563 atom_placement = big_atom_index;
535 if (!keep_free_list_node) {564 if (!keep_free_list_node) {
536 free_list_removal = i;565 free_list_removal = i;
537 }566 }
538 break :blk new_start_vaddr;567 break :blk new_start_vaddr;
539 } else if (maybe_last_atom.*) |last| {568 } else if (maybe_last_atom_index.*) |last_index| {
569 const last = self.getAtom(last_index);
540 const last_symbol = last.getSymbol(self);570 const last_symbol = last.getSymbol(self);
541 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;
542 const ideal_capacity_end_vaddr = last_symbol.value + ideal_capacity;572 const ideal_capacity_end_vaddr = last_symbol.value + ideal_capacity;
543 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);
544 atom_placement = last;574 atom_placement = last_index;
545 break :blk new_start_vaddr;575 break :blk new_start_vaddr;
546 } else {576 } else {
547 break :blk mem.alignForwardGeneric(u32, header.virtual_address, alignment);577 break :blk mem.alignForwardGeneric(u32, header.virtual_address, alignment);
548 }578 }
549 };579 };
550580
551 const expand_section = atom_placement == null or atom_placement.?.next == null;581 const expand_section = if (atom_placement) |placement_index|
582 self.getAtom(placement_index).next_index == null
583 else
584 true;
552 if (expand_section) {585 if (expand_section) {
553 const sect_capacity = self.allocatedSize(header.pointer_to_raw_data);586 const sect_capacity = self.allocatedSize(header.pointer_to_raw_data);
554 const needed_size: u32 = (vaddr + new_atom_size) - header.virtual_address;587 const needed_size: u32 = (vaddr + new_atom_size) - header.virtual_address;
555 if (needed_size > sect_capacity) {588 if (needed_size > sect_capacity) {
556 const new_offset = self.findFreeSpace(needed_size, default_file_alignment);589 const new_offset = self.findFreeSpace(needed_size, default_file_alignment);
557 const current_size = if (maybe_last_atom.*) |last_atom| blk: {590 const current_size = if (maybe_last_atom_index.*) |last_atom_index| blk: {
591 const last_atom = self.getAtom(last_atom_index);
558 const sym = last_atom.getSymbol(self);592 const sym = last_atom.getSymbol(self);
559 break :blk (sym.value + last_atom.size) - header.virtual_address;593 break :blk (sym.value + last_atom.size) - header.virtual_address;
560 } else 0;594 } else 0;
561 log.debug("moving {s} from 0x{x} to 0x{x}", .{ self.getSectionName(header), header.pointer_to_raw_data, new_offset });595 log.debug("moving {s} from 0x{x} to 0x{x}", .{
596 self.getSectionName(header),
597 header.pointer_to_raw_data,
598 new_offset,
599 });
562 const amt = try self.base.file.?.copyRangeAll(600 const amt = try self.base.file.?.copyRangeAll(
563 header.pointer_to_raw_data,601 header.pointer_to_raw_data,
564 self.base.file.?,602 self.base.file.?,
...@@ -577,26 +615,34 @@ fn allocateAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u...@@ -577,26 +615,34 @@ fn allocateAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u
577615
578 header.virtual_size = @max(header.virtual_size, needed_size);616 header.virtual_size = @max(header.virtual_size, needed_size);
579 header.size_of_raw_data = needed_size;617 header.size_of_raw_data = needed_size;
580 maybe_last_atom.* = atom;618 maybe_last_atom_index.* = atom_index;
581 }619 }
582620
583 atom.size = new_atom_size;621 {
584 atom.alignment = alignment;622 const atom_ptr = self.getAtomPtr(atom_index);
623 atom_ptr.size = new_atom_size;
624 atom_ptr.alignment = alignment;
625 }
585626
586 if (atom.prev) |prev| {627 if (atom.prev_index) |prev_index| {
587 prev.next = atom.next;628 const prev = self.getAtomPtr(prev_index);
629 prev.next_index = atom.next_index;
588 }630 }
589 if (atom.next) |next| {631 if (atom.next_index) |next_index| {
590 next.prev = atom.prev;632 const next = self.getAtomPtr(next_index);
633 next.prev_index = atom.prev_index;
591 }634 }
592635
593 if (atom_placement) |big_atom| {636 if (atom_placement) |big_atom_index| {
594 atom.prev = big_atom;637 const big_atom = self.getAtomPtr(big_atom_index);
595 atom.next = big_atom.next;638 const atom_ptr = self.getAtomPtr(atom_index);
596 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;
597 } else {642 } else {
598 atom.prev = null;643 const atom_ptr = self.getAtomPtr(atom_index);
599 atom.next = null;644 atom_ptr.prev_index = null;
645 atom_ptr.next_index = null;
600 }646 }
601 if (free_list_removal) |i| {647 if (free_list_removal) |i| {
602 _ = free_list.swapRemove(i);648 _ = free_list.swapRemove(i);
...@@ -701,24 +747,37 @@ pub fn allocateImportEntry(self: *Coff, target: SymbolWithLoc) !u32 {...@@ -701,24 +747,37 @@ pub fn allocateImportEntry(self: *Coff, target: SymbolWithLoc) !u32 {
701 return index;747 return index;
702}748}
703749
704fn createGotAtom(self: *Coff, target: SymbolWithLoc) !*Atom {750pub fn createAtom(self: *Coff) !Atom.Index {
705 const gpa = self.base.allocator;751 const gpa = self.base.allocator;
706 const atom = try gpa.create(Atom);752 const atom_index = @intCast(Atom.Index, self.atoms.items.len);
707 errdefer gpa.destroy(atom);753 const atom = try self.atoms.addOne(gpa);
708 atom.* = Atom.empty;754 const sym_index = try self.allocateSymbol();
709 try atom.ensureInitialized(self);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);
710 atom.size = @sizeOf(u64);771 atom.size = @sizeOf(u64);
711 atom.alignment = @alignOf(u64);772 atom.alignment = @alignOf(u64);
712773
713 try self.managed_atoms.append(gpa, atom);
714
715 const sym = atom.getSymbolPtr(self);774 const sym = atom.getSymbolPtr(self);
716 sym.section_number = @intToEnum(coff.SectionNumber, self.got_section_index.? + 1);775 sym.section_number = @intToEnum(coff.SectionNumber, self.got_section_index.? + 1);
717 sym.value = try self.allocateAtom(atom, atom.size, atom.alignment);776 sym.value = try self.allocateAtom(atom_index, atom.size, atom.alignment);
718777
719 log.debug("allocated GOT atom at 0x{x}", .{sym.value});778 log.debug("allocated GOT atom at 0x{x}", .{sym.value});
720779
721 try atom.addRelocation(self, .{780 try Atom.addRelocation(self, atom_index, .{
722 .type = .direct,781 .type = .direct,
723 .target = target,782 .target = target,
724 .offset = 0,783 .offset = 0,
...@@ -732,49 +791,46 @@ fn createGotAtom(self: *Coff, target: SymbolWithLoc) !*Atom {...@@ -732,49 +791,46 @@ fn createGotAtom(self: *Coff, target: SymbolWithLoc) !*Atom {
732 .UNDEFINED => @panic("TODO generate a binding for undefined GOT target"),791 .UNDEFINED => @panic("TODO generate a binding for undefined GOT target"),
733 .ABSOLUTE => {},792 .ABSOLUTE => {},
734 .DEBUG => unreachable, // not possible793 .DEBUG => unreachable, // not possible
735 else => try atom.addBaseRelocation(self, 0),794 else => try Atom.addBaseRelocation(self, atom_index, 0),
736 }795 }
737796
738 return atom;797 return atom_index;
739}798}
740799
741fn createImportAtom(self: *Coff) !*Atom {800fn createImportAtom(self: *Coff) !Atom.Index {
742 const gpa = self.base.allocator;801 const atom_index = try self.createAtom();
743 const atom = try gpa.create(Atom);802 const atom = self.getAtomPtr(atom_index);
744 errdefer gpa.destroy(atom);
745 atom.* = Atom.empty;
746 try atom.ensureInitialized(self);
747 atom.size = @sizeOf(u64);803 atom.size = @sizeOf(u64);
748 atom.alignment = @alignOf(u64);804 atom.alignment = @alignOf(u64);
749805
750 try self.managed_atoms.append(gpa, atom);
751
752 const sym = atom.getSymbolPtr(self);806 const sym = atom.getSymbolPtr(self);
753 sym.section_number = @intToEnum(coff.SectionNumber, self.idata_section_index.? + 1);807 sym.section_number = @intToEnum(coff.SectionNumber, self.idata_section_index.? + 1);
754 sym.value = try self.allocateAtom(atom, atom.size, atom.alignment);808 sym.value = try self.allocateAtom(atom_index, atom.size, atom.alignment);
755809
756 log.debug("allocated import atom at 0x{x}", .{sym.value});810 log.debug("allocated import atom at 0x{x}", .{sym.value});
757811
758 return atom;812 return atom_index;
759}813}
760814
761fn growAtom(self: *Coff, atom: *Atom, new_atom_size: u32, alignment: u32) !u32 {815fn growAtom(self: *Coff, atom_index: Atom.Index, new_atom_size: u32, alignment: u32) !u32 {
816 const atom = self.getAtom(atom_index);
762 const sym = atom.getSymbol(self);817 const sym = atom.getSymbol(self);
763 const align_ok = mem.alignBackwardGeneric(u32, sym.value, alignment) == sym.value;818 const align_ok = mem.alignBackwardGeneric(u32, sym.value, alignment) == sym.value;
764 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);
765 if (!need_realloc) return sym.value;820 if (!need_realloc) return sym.value;
766 return self.allocateAtom(atom, new_atom_size, alignment);821 return self.allocateAtom(atom_index, new_atom_size, alignment);
767}822}
768823
769fn shrinkAtom(self: *Coff, atom: *Atom, new_block_size: u32) void {824fn shrinkAtom(self: *Coff, atom_index: Atom.Index, new_block_size: u32) void {
770 _ = self;825 _ = self;
771 _ = atom;826 _ = atom_index;
772 _ = new_block_size;827 _ = new_block_size;
773 // 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
774 // capacity, insert a free list node for it.829 // capacity, insert a free list node for it.
775}830}
776831
777fn writeAtom(self: *Coff, atom: *Atom, code: []const u8) !void {832fn writeAtom(self: *Coff, atom_index: Atom.Index, code: []const u8) !void {
833 const atom = self.getAtom(atom_index);
778 const sym = atom.getSymbol(self);834 const sym = atom.getSymbol(self);
779 const section = self.sections.get(@enumToInt(sym.section_number) - 1);835 const section = self.sections.get(@enumToInt(sym.section_number) - 1);
780 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;
...@@ -784,18 +840,18 @@ fn writeAtom(self: *Coff, atom: *Atom, code: []const u8) !void {...@@ -784,18 +840,18 @@ fn writeAtom(self: *Coff, atom: *Atom, code: []const u8) !void {
784 file_offset + code.len,840 file_offset + code.len,
785 });841 });
786 try self.base.file.?.pwriteAll(code, file_offset);842 try self.base.file.?.pwriteAll(code, file_offset);
787 try self.resolveRelocs(atom);843 try self.resolveRelocs(atom_index);
788}844}
789845
790fn writePtrWidthAtom(self: *Coff, atom: *Atom) !void {846fn writePtrWidthAtom(self: *Coff, atom_index: Atom.Index) !void {
791 switch (self.ptr_width) {847 switch (self.ptr_width) {
792 .p32 => {848 .p32 => {
793 var buffer: [@sizeOf(u32)]u8 = [_]u8{0} ** @sizeOf(u32);849 var buffer: [@sizeOf(u32)]u8 = [_]u8{0} ** @sizeOf(u32);
794 try self.writeAtom(atom, &buffer);850 try self.writeAtom(atom_index, &buffer);
795 },851 },
796 .p64 => {852 .p64 => {
797 var buffer: [@sizeOf(u64)]u8 = [_]u8{0} ** @sizeOf(u64);853 var buffer: [@sizeOf(u64)]u8 = [_]u8{0} ** @sizeOf(u64);
798 try self.writeAtom(atom, &buffer);854 try self.writeAtom(atom_index, &buffer);
799 },855 },
800 }856 }
801}857}
...@@ -815,7 +871,8 @@ fn markRelocsDirtyByAddress(self: *Coff, addr: u32) void {...@@ -815,7 +871,8 @@ fn markRelocsDirtyByAddress(self: *Coff, addr: u32) void {
815 var it = self.relocs.valueIterator();871 var it = self.relocs.valueIterator();
816 while (it.next()) |relocs| {872 while (it.next()) |relocs| {
817 for (relocs.items) |*reloc| {873 for (relocs.items) |*reloc| {
818 const target_atom = reloc.getTargetAtom(self) orelse continue;874 const target_atom_index = reloc.getTargetAtomIndex(self) orelse continue;
875 const target_atom = self.getAtom(target_atom_index);
819 const target_sym = target_atom.getSymbol(self);876 const target_sym = target_atom.getSymbol(self);
820 if (target_sym.value < addr) continue;877 if (target_sym.value < addr) continue;
821 reloc.dirty = true;878 reloc.dirty = true;
...@@ -823,24 +880,26 @@ fn markRelocsDirtyByAddress(self: *Coff, addr: u32) void {...@@ -823,24 +880,26 @@ fn markRelocsDirtyByAddress(self: *Coff, addr: u32) void {
823 }880 }
824}881}
825882
826fn resolveRelocs(self: *Coff, atom: *Atom) !void {883fn resolveRelocs(self: *Coff, atom_index: Atom.Index) !void {
827 const relocs = self.relocs.get(atom) orelse return;884 const relocs = self.relocs.get(atom_index) orelse return;
828885
829 log.debug("relocating '{s}'", .{atom.getName(self)});886 log.debug("relocating '{s}'", .{self.getAtom(atom_index).getName(self)});
830887
831 for (relocs.items) |*reloc| {888 for (relocs.items) |*reloc| {
832 if (!reloc.dirty) continue;889 if (!reloc.dirty) continue;
833 try reloc.resolve(atom, self);890 try reloc.resolve(atom_index, self);
834 }891 }
835}892}
836893
837fn freeAtom(self: *Coff, atom: *Atom) void {894fn freeAtom(self: *Coff, atom_index: Atom.Index) void {
838 log.debug("freeAtom {*}", .{atom});895 log.debug("freeAtom {d}", .{atom_index});
896
897 const gpa = self.base.allocator;
839898
840 // Remove any relocs and base relocs associated with this Atom899 // Remove any relocs and base relocs associated with this Atom
841 self.freeRelocationsForAtom(atom);900 Atom.freeRelocations(self, atom_index);
842901
843 const gpa = self.base.allocator;902 const atom = self.getAtom(atom_index);
844 const sym = atom.getSymbol(self);903 const sym = atom.getSymbol(self);
845 const sect_id = @enumToInt(sym.section_number) - 1;904 const sect_id = @enumToInt(sym.section_number) - 1;
846 const free_list = &self.sections.items(.free_list)[sect_id];905 const free_list = &self.sections.items(.free_list)[sect_id];
...@@ -849,45 +908,46 @@ fn freeAtom(self: *Coff, atom: *Atom) void {...@@ -849,45 +908,46 @@ fn freeAtom(self: *Coff, atom: *Atom) void {
849 var i: usize = 0;908 var i: usize = 0;
850 // TODO turn free_list into a hash map909 // TODO turn free_list into a hash map
851 while (i < free_list.items.len) {910 while (i < free_list.items.len) {
852 if (free_list.items[i] == atom) {911 if (free_list.items[i] == atom_index) {
853 _ = free_list.swapRemove(i);912 _ = free_list.swapRemove(i);
854 continue;913 continue;
855 }914 }
856 if (free_list.items[i] == atom.prev) {915 if (free_list.items[i] == atom.prev_index) {
857 already_have_free_list_node = true;916 already_have_free_list_node = true;
858 }917 }
859 i += 1;918 i += 1;
860 }919 }
861 }920 }
862921
863 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];
864 if (maybe_last_atom.*) |last_atom| {923 if (maybe_last_atom_index.*) |last_atom_index| {
865 if (last_atom == atom) {924 if (last_atom_index == atom_index) {
866 if (atom.prev) |prev| {925 if (atom.prev_index) |prev_index| {
867 // TODO shrink the section size here926 // TODO shrink the section size here
868 maybe_last_atom.* = prev;927 maybe_last_atom_index.* = prev_index;
869 } else {928 } else {
870 maybe_last_atom.* = null;929 maybe_last_atom_index.* = null;
871 }930 }
872 }931 }
873 }932 }
874933
875 if (atom.prev) |prev| {934 if (atom.prev_index) |prev_index| {
876 prev.next = atom.next;935 const prev = self.getAtomPtr(prev_index);
936 prev.next_index = atom.next_index;
877937
878 if (!already_have_free_list_node and prev.freeListEligible(self)) {938 if (!already_have_free_list_node and prev.*.freeListEligible(self)) {
879 // 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
880 // ignore the OOM here.940 // ignore the OOM here.
881 free_list.append(gpa, prev) catch {};941 free_list.append(gpa, prev_index) catch {};
882 }942 }
883 } else {943 } else {
884 atom.prev = null;944 self.getAtomPtr(atom_index).prev_index = null;
885 }945 }
886946
887 if (atom.next) |next| {947 if (atom.next_index) |next_index| {
888 next.prev = atom.prev;948 self.getAtomPtr(next_index).prev_index = atom.prev_index;
889 } else {949 } else {
890 atom.next = null;950 self.getAtomPtr(atom_index).next_index = null;
891 }951 }
892952
893 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.953 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
...@@ -910,7 +970,7 @@ fn freeAtom(self: *Coff, atom: *Atom) void {...@@ -910,7 +970,7 @@ fn freeAtom(self: *Coff, atom: *Atom) void {
910 self.locals.items[sym_index].section_number = .UNDEFINED;970 self.locals.items[sym_index].section_number = .UNDEFINED;
911 _ = self.atom_by_index_table.remove(sym_index);971 _ = self.atom_by_index_table.remove(sym_index);
912 log.debug(" adding local symbol index {d} to free list", .{sym_index});972 log.debug(" adding local symbol index {d} to free list", .{sym_index});
913 atom.sym_index = 0;973 self.getAtomPtr(atom_index).sym_index = 0;
914}974}
915975
916pub 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 {
...@@ -927,15 +987,10 @@ pub fn updateFunc(self: *Coff, module: *Module, func: *Module.Fn, air: Air, live...@@ -927,15 +987,10 @@ pub fn updateFunc(self: *Coff, module: *Module, func: *Module.Fn, air: Air, live
927987
928 const decl_index = func.owner_decl;988 const decl_index = func.owner_decl;
929 const decl = module.declPtr(decl_index);989 const decl = module.declPtr(decl_index);
930 const atom = &decl.link.coff;990
931 try atom.ensureInitialized(self);991 const atom_index = try self.getOrCreateAtomForDecl(decl_index);
932 const gop = try self.decls.getOrPut(self.base.allocator, decl_index);992 self.freeUnnamedConsts(decl_index);
933 if (gop.found_existing) {993 Atom.freeRelocations(self, atom_index);
934 self.freeUnnamedConsts(decl_index);
935 self.freeRelocationsForAtom(&decl.link.coff);
936 } else {
937 gop.value_ptr.* = null;
938 }
939994
940 var code_buffer = std.ArrayList(u8).init(self.base.allocator);995 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
941 defer code_buffer.deinit();996 defer code_buffer.deinit();
...@@ -979,11 +1034,7 @@ pub fn lowerUnnamedConst(self: *Coff, tv: TypedValue, decl_index: Module.Decl.In...@@ -979,11 +1034,7 @@ pub fn lowerUnnamedConst(self: *Coff, tv: TypedValue, decl_index: Module.Decl.In
979 }1034 }
980 const unnamed_consts = gop.value_ptr;1035 const unnamed_consts = gop.value_ptr;
9811036
982 const atom = try gpa.create(Atom);1037 const atom_index = try self.createAtom();
983 errdefer gpa.destroy(atom);
984 atom.* = Atom.empty;
985 try atom.ensureInitialized(self);
986 try self.managed_atoms.append(gpa, atom);
9871038
988 const sym_name = blk: {1039 const sym_name = blk: {
989 const decl_name = try decl.getFullyQualifiedName(mod);1040 const decl_name = try decl.getFullyQualifiedName(mod);
...@@ -993,11 +1044,15 @@ pub fn lowerUnnamedConst(self: *Coff, tv: TypedValue, decl_index: Module.Decl.In...@@ -993,11 +1044,15 @@ pub fn lowerUnnamedConst(self: *Coff, tv: TypedValue, decl_index: Module.Decl.In
993 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 });
994 };1045 };
995 defer gpa.free(sym_name);1046 defer gpa.free(sym_name);
996 try self.setSymbolName(atom.getSymbolPtr(self), sym_name);1047 {
997 atom.getSymbolPtr(self).section_number = @intToEnum(coff.SectionNumber, self.rdata_section_index.? + 1);1048 const atom = self.getAtom(atom_index);
1049 const sym = atom.getSymbolPtr(self);
1050 try self.setSymbolName(sym, sym_name);
1051 sym.section_number = @intToEnum(coff.SectionNumber, self.rdata_section_index.? + 1);
1052 }
9981053
999 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, .{
1000 .parent_atom_index = atom.getSymbolIndex().?,1055 .parent_atom_index = self.getAtom(atom_index).getSymbolIndex().?,
1001 });1056 });
1002 const code = switch (res) {1057 const code = switch (res) {
1003 .ok => code_buffer.items,1058 .ok => code_buffer.items,
...@@ -1010,17 +1065,18 @@ pub fn lowerUnnamedConst(self: *Coff, tv: TypedValue, decl_index: Module.Decl.In...@@ -1010,17 +1065,18 @@ pub fn lowerUnnamedConst(self: *Coff, tv: TypedValue, decl_index: Module.Decl.In
1010 };1065 };
10111066
1012 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);
1013 atom.alignment = required_alignment;1069 atom.alignment = required_alignment;
1014 atom.size = @intCast(u32, code.len);1070 atom.size = @intCast(u32, code.len);
1015 atom.getSymbolPtr(self).value = try self.allocateAtom(atom, atom.size, atom.alignment);1071 atom.getSymbolPtr(self).value = try self.allocateAtom(atom_index, atom.size, atom.alignment);
1016 errdefer self.freeAtom(atom);1072 errdefer self.freeAtom(atom_index);
10171073
1018 try unnamed_consts.append(gpa, atom);1074 try unnamed_consts.append(gpa, atom_index);
10191075
1020 log.debug("allocated atom for {s} at 0x{x}", .{ sym_name, atom.getSymbol(self).value });1076 log.debug("allocated atom for {s} at 0x{x}", .{ sym_name, atom.getSymbol(self).value });
1021 log.debug(" (required alignment 0x{x})", .{required_alignment});1077 log.debug(" (required alignment 0x{x})", .{required_alignment});
10221078
1023 try self.writeAtom(atom, code);1079 try self.writeAtom(atom_index, code);
10241080
1025 return atom.getSymbolIndex().?;1081 return atom.getSymbolIndex().?;
1026}1082}
...@@ -1047,14 +1103,9 @@ pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) !...@@ -1047,14 +1103,9 @@ pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) !
1047 }1103 }
1048 }1104 }
10491105
1050 const atom = &decl.link.coff;1106 const atom_index = try self.getOrCreateAtomForDecl(decl_index);
1051 try atom.ensureInitialized(self);1107 Atom.freeRelocations(self, atom_index);
1052 const gop = try self.decls.getOrPut(self.base.allocator, decl_index);1108 const atom = self.getAtom(atom_index);
1053 if (gop.found_existing) {
1054 self.freeRelocationsForAtom(atom);
1055 } else {
1056 gop.value_ptr.* = null;
1057 }
10581109
1059 var code_buffer = std.ArrayList(u8).init(self.base.allocator);1110 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
1060 defer code_buffer.deinit();1111 defer code_buffer.deinit();
...@@ -1064,7 +1115,7 @@ pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) !...@@ -1064,7 +1115,7 @@ pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) !
1064 .ty = decl.ty,1115 .ty = decl.ty,
1065 .val = decl_val,1116 .val = decl_val,
1066 }, &code_buffer, .none, .{1117 }, &code_buffer, .none, .{
1067 .parent_atom_index = decl.link.coff.getSymbolIndex().?,1118 .parent_atom_index = atom.getSymbolIndex().?,
1068 });1119 });
1069 const code = switch (res) {1120 const code = switch (res) {
1070 .ok => code_buffer.items,1121 .ok => code_buffer.items,
...@@ -1082,7 +1133,20 @@ pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) !...@@ -1082,7 +1133,20 @@ pub fn updateDecl(self: *Coff, module: *Module, decl_index: Module.Decl.Index) !
1082 return self.updateDeclExports(module, decl_index, module.getDeclExports(decl_index));1133 return self.updateDeclExports(module, decl_index, module.getDeclExports(decl_index));
1083}1134}
10841135
1085fn getDeclOutputSection(self: *Coff, decl: *Module.Decl) u16 {1136pub fn getOrCreateAtomForDecl(self: *Coff, decl_index: Module.Decl.Index) !Atom.Index {
1137 const gop = try self.decls.getOrPut(self.base.allocator, decl_index);
1138 if (!gop.found_existing) {
1139 gop.value_ptr.* = .{
1140 .atom = try self.createAtom(),
1141 .section = self.getDeclOutputSection(decl_index),
1142 .exports = .{},
1143 };
1144 }
1145 return gop.value_ptr.atom;
1146}
1147
1148fn getDeclOutputSection(self: *Coff, decl_index: Module.Decl.Index) u16 {
1149 const decl = self.base.options.module.?.declPtr(decl_index);
1086 const ty = decl.ty;1150 const ty = decl.ty;
1087 const zig_ty = ty.zigTypeTag();1151 const zig_ty = ty.zigTypeTag();
1088 const val = decl.val;1152 const val = decl.val;
...@@ -1117,14 +1181,11 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []const u8,...@@ -1117,14 +1181,11 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []const u8,
1117 log.debug("updateDeclCode {s}{*}", .{ decl_name, decl });1181 log.debug("updateDeclCode {s}{*}", .{ decl_name, decl });
1118 const required_alignment = decl.getAlignment(self.base.options.target);1182 const required_alignment = decl.getAlignment(self.base.options.target);
11191183
1120 const decl_ptr = self.decls.getPtr(decl_index).?;1184 const decl_metadata = self.decls.get(decl_index).?;
1121 if (decl_ptr.* == null) {1185 const atom_index = decl_metadata.atom;
1122 decl_ptr.* = self.getDeclOutputSection(decl);1186 const atom = self.getAtom(atom_index);
1123 }1187 const sect_index = decl_metadata.section;
1124 const sect_index = decl_ptr.*.?;
1125
1126 const code_len = @intCast(u32, code.len);1188 const code_len = @intCast(u32, code.len);
1127 const atom = &decl.link.coff;
11281189
1129 if (atom.size != 0) {1190 if (atom.size != 0) {
1130 const sym = atom.getSymbolPtr(self);1191 const sym = atom.getSymbolPtr(self);
...@@ -1135,7 +1196,7 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []const u8,...@@ -1135,7 +1196,7 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []const u8,
1135 const capacity = atom.capacity(self);1196 const capacity = atom.capacity(self);
1136 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);
1137 if (need_realloc) {1198 if (need_realloc) {
1138 const vaddr = try self.growAtom(atom, code_len, required_alignment);1199 const vaddr = try self.growAtom(atom_index, code_len, required_alignment);
1139 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 });
1140 log.debug(" (required alignment 0x{x}", .{required_alignment});1201 log.debug(" (required alignment 0x{x}", .{required_alignment});
11411202
...@@ -1143,49 +1204,43 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []const u8,...@@ -1143,49 +1204,43 @@ fn updateDeclCode(self: *Coff, decl_index: Module.Decl.Index, code: []const u8,
1143 sym.value = vaddr;1204 sym.value = vaddr;
1144 log.debug(" (updating GOT entry)", .{});1205 log.debug(" (updating GOT entry)", .{});
1145 const got_target = SymbolWithLoc{ .sym_index = atom.getSymbolIndex().?, .file = null };1206 const got_target = SymbolWithLoc{ .sym_index = atom.getSymbolIndex().?, .file = null };
1146 const got_atom = self.getGotAtomForSymbol(got_target).?;1207 const got_atom_index = self.getGotAtomIndexForSymbol(got_target).?;
1147 self.markRelocsDirtyByTarget(got_target);1208 self.markRelocsDirtyByTarget(got_target);
1148 try self.writePtrWidthAtom(got_atom);1209 try self.writePtrWidthAtom(got_atom_index);
1149 }1210 }
1150 } else if (code_len < atom.size) {1211 } else if (code_len < atom.size) {
1151 self.shrinkAtom(atom, code_len);1212 self.shrinkAtom(atom_index, code_len);
1152 }1213 }
1153 atom.size = code_len;1214 self.getAtomPtr(atom_index).size = code_len;
1154 } else {1215 } else {
1155 const sym = atom.getSymbolPtr(self);1216 const sym = atom.getSymbolPtr(self);
1156 try self.setSymbolName(sym, decl_name);1217 try self.setSymbolName(sym, decl_name);
1157 sym.section_number = @intToEnum(coff.SectionNumber, sect_index + 1);1218 sym.section_number = @intToEnum(coff.SectionNumber, sect_index + 1);
1158 sym.type = .{ .complex_type = complex_type, .base_type = .NULL };1219 sym.type = .{ .complex_type = complex_type, .base_type = .NULL };
11591220
1160 const vaddr = try self.allocateAtom(atom, code_len, required_alignment);1221 const vaddr = try self.allocateAtom(atom_index, code_len, required_alignment);
1161 errdefer self.freeAtom(atom);1222 errdefer self.freeAtom(atom_index);
1162 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 });
1163 atom.size = code_len;1224 self.getAtomPtr(atom_index).size = code_len;
1164 sym.value = vaddr;1225 sym.value = vaddr;
11651226
1166 const got_target = SymbolWithLoc{ .sym_index = atom.getSymbolIndex().?, .file = null };1227 const got_target = SymbolWithLoc{ .sym_index = atom.getSymbolIndex().?, .file = null };
1167 const got_index = try self.allocateGotEntry(got_target);1228 const got_index = try self.allocateGotEntry(got_target);
1168 const got_atom = try self.createGotAtom(got_target);1229 const got_atom_index = try self.createGotAtom(got_target);
1230 const got_atom = self.getAtom(got_atom_index);
1169 self.got_entries.items[got_index].sym_index = got_atom.getSymbolIndex().?;1231 self.got_entries.items[got_index].sym_index = got_atom.getSymbolIndex().?;
1170 try self.writePtrWidthAtom(got_atom);1232 try self.writePtrWidthAtom(got_atom_index);
1171 }1233 }
11721234
1173 self.markRelocsDirtyByTarget(atom.getSymbolWithLoc());1235 self.markRelocsDirtyByTarget(atom.getSymbolWithLoc());
1174 try self.writeAtom(atom, code);1236 try self.writeAtom(atom_index, code);
1175}
1176
1177fn freeRelocationsForAtom(self: *Coff, atom: *Atom) void {
1178 var removed_relocs = self.relocs.fetchRemove(atom);
1179 if (removed_relocs) |*relocs| relocs.value.deinit(self.base.allocator);
1180 var removed_base_relocs = self.base_relocs.fetchRemove(atom);
1181 if (removed_base_relocs) |*base_relocs| base_relocs.value.deinit(self.base.allocator);
1182}1237}
11831238
1184fn freeUnnamedConsts(self: *Coff, decl_index: Module.Decl.Index) void {1239fn freeUnnamedConsts(self: *Coff, decl_index: Module.Decl.Index) void {
1185 const gpa = self.base.allocator;1240 const gpa = self.base.allocator;
1186 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;
1187 for (unnamed_consts.items) |atom| {1242 for (unnamed_consts.items) |atom_index| {
1188 self.freeAtom(atom);1243 self.freeAtom(atom_index);
1189 }1244 }
1190 unnamed_consts.clearAndFree(gpa);1245 unnamed_consts.clearAndFree(gpa);
1191}1246}
...@@ -1200,11 +1255,11 @@ pub fn freeDecl(self: *Coff, decl_index: Module.Decl.Index) void {...@@ -1200,11 +1255,11 @@ pub fn freeDecl(self: *Coff, decl_index: Module.Decl.Index) void {
12001255
1201 log.debug("freeDecl {*}", .{decl});1256 log.debug("freeDecl {*}", .{decl});
12021257
1203 if (self.decls.fetchRemove(decl_index)) |kv| {1258 if (self.decls.fetchRemove(decl_index)) |const_kv| {
1204 if (kv.value) |_| {1259 var kv = const_kv;
1205 self.freeAtom(&decl.link.coff);1260 self.freeAtom(kv.value.atom);
1206 self.freeUnnamedConsts(decl_index);1261 self.freeUnnamedConsts(decl_index);
1207 }1262 kv.value.exports.deinit(self.base.allocator);
1208 }1263 }
1209}1264}
12101265
...@@ -1257,16 +1312,10 @@ pub fn updateDeclExports(...@@ -1257,16 +1312,10 @@ pub fn updateDeclExports(
1257 const gpa = self.base.allocator;1312 const gpa = self.base.allocator;
12581313
1259 const decl = module.declPtr(decl_index);1314 const decl = module.declPtr(decl_index);
1260 const atom = &decl.link.coff;1315 const atom_index = try self.getOrCreateAtomForDecl(decl_index);
12611316 const atom = self.getAtom(atom_index);
1262 if (atom.getSymbolIndex() == null) return;
1263
1264 const gop = try self.decls.getOrPut(gpa, decl_index);
1265 if (!gop.found_existing) {
1266 gop.value_ptr.* = self.getDeclOutputSection(decl);
1267 }
1268
1269 const decl_sym = atom.getSymbol(self);1317 const decl_sym = atom.getSymbol(self);
1318 const decl_metadata = self.decls.getPtr(decl_index).?;
12701319
1271 for (exports) |exp| {1320 for (exports) |exp| {
1272 log.debug("adding new export '{s}'", .{exp.options.name});1321 log.debug("adding new export '{s}'", .{exp.options.name});
...@@ -1301,9 +1350,9 @@ pub fn updateDeclExports(...@@ -1301,9 +1350,9 @@ pub fn updateDeclExports(
1301 continue;1350 continue;
1302 }1351 }
13031352
1304 const sym_index = exp.link.coff.sym_index orelse blk: {1353 const sym_index = decl_metadata.getExport(self, exp.options.name) orelse blk: {
1305 const sym_index = try self.allocateSymbol();1354 const sym_index = try self.allocateSymbol();
1306 exp.link.coff.sym_index = sym_index;1355 try decl_metadata.exports.append(gpa, sym_index);
1307 break :blk sym_index;1356 break :blk sym_index;
1308 };1357 };
1309 const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = null };1358 const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = null };
...@@ -1326,16 +1375,15 @@ pub fn updateDeclExports(...@@ -1326,16 +1375,15 @@ pub fn updateDeclExports(
1326 }1375 }
1327}1376}
13281377
1329pub fn deleteExport(self: *Coff, exp: Export) void {1378pub fn deleteDeclExport(self: *Coff, decl_index: Module.Decl.Index, name: []const u8) void {
1330 if (self.llvm_object) |_| return;1379 if (self.llvm_object) |_| return;
1331 const sym_index = exp.sym_index orelse return;1380 const metadata = self.decls.getPtr(decl_index) orelse return;
1381 const sym_index = metadata.getExportPtr(self, name) orelse return;
13321382
1333 const gpa = self.base.allocator;1383 const gpa = self.base.allocator;
13341384 const sym_loc = SymbolWithLoc{ .sym_index = sym_index.*, .file = null };
1335 const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = null };
1336 const sym = self.getSymbolPtr(sym_loc);1385 const sym = self.getSymbolPtr(sym_loc);
1337 const sym_name = self.getSymbolName(sym_loc);1386 log.debug("deleting export '{s}'", .{name});
1338 log.debug("deleting export '{s}'", .{sym_name});
1339 assert(sym.storage_class == .EXTERNAL and sym.section_number != .UNDEFINED);1387 assert(sym.storage_class == .EXTERNAL and sym.section_number != .UNDEFINED);
1340 sym.* = .{1388 sym.* = .{
1341 .name = [_]u8{0} ** 8,1389 .name = [_]u8{0} ** 8,
...@@ -1345,9 +1393,9 @@ pub fn deleteExport(self: *Coff, exp: Export) void {...@@ -1345,9 +1393,9 @@ pub fn deleteExport(self: *Coff, exp: Export) void {
1345 .storage_class = .NULL,1393 .storage_class = .NULL,
1346 .number_of_aux_symbols = 0,1394 .number_of_aux_symbols = 0,
1347 };1395 };
1348 self.locals_free_list.append(gpa, sym_index) catch {};1396 self.locals_free_list.append(gpa, sym_index.*) catch {};
13491397
1350 if (self.resolver.fetchRemove(sym_name)) |entry| {1398 if (self.resolver.fetchRemove(name)) |entry| {
1351 defer gpa.free(entry.key);1399 defer gpa.free(entry.key);
1352 self.globals_free_list.append(gpa, entry.value) catch {};1400 self.globals_free_list.append(gpa, entry.value) catch {};
1353 self.globals.items[entry.value] = .{1401 self.globals.items[entry.value] = .{
...@@ -1355,6 +1403,8 @@ pub fn deleteExport(self: *Coff, exp: Export) void {...@@ -1355,6 +1403,8 @@ pub fn deleteExport(self: *Coff, exp: Export) void {
1355 .file = null,1403 .file = null,
1356 };1404 };
1357 }1405 }
1406
1407 sym_index.* = 0;
1358}1408}
13591409
1360fn resolveGlobalSymbol(self: *Coff, current: SymbolWithLoc) !void {1410fn resolveGlobalSymbol(self: *Coff, current: SymbolWithLoc) !void {
...@@ -1419,9 +1469,10 @@ pub fn flushModule(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod...@@ -1419,9 +1469,10 @@ pub fn flushModule(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
1419 if (self.imports_table.contains(global)) continue;1469 if (self.imports_table.contains(global)) continue;
14201470
1421 const import_index = try self.allocateImportEntry(global);1471 const import_index = try self.allocateImportEntry(global);
1422 const import_atom = try self.createImportAtom();1472 const import_atom_index = try self.createImportAtom();
1473 const import_atom = self.getAtom(import_atom_index);
1423 self.imports.items[import_index].sym_index = import_atom.getSymbolIndex().?;1474 self.imports.items[import_index].sym_index = import_atom.getSymbolIndex().?;
1424 try self.writePtrWidthAtom(import_atom);1475 try self.writePtrWidthAtom(import_atom_index);
1425 }1476 }
14261477
1427 if (build_options.enable_logging) {1478 if (build_options.enable_logging) {
...@@ -1455,22 +1506,14 @@ pub fn flushModule(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod...@@ -1455,22 +1506,14 @@ pub fn flushModule(self: *Coff, comp: *Compilation, prog_node: *std.Progress.Nod
1455 }1506 }
1456}1507}
14571508
1458pub fn getDeclVAddr(1509pub fn getDeclVAddr(self: *Coff, decl_index: Module.Decl.Index, reloc_info: link.File.RelocInfo) !u64 {
1459 self: *Coff,
1460 decl_index: Module.Decl.Index,
1461 reloc_info: link.File.RelocInfo,
1462) !u64 {
1463 const mod = self.base.options.module.?;
1464 const decl = mod.declPtr(decl_index);
1465
1466 assert(self.llvm_object == null);1510 assert(self.llvm_object == null);
14671511
1468 try decl.link.coff.ensureInitialized(self);1512 const this_atom_index = try self.getOrCreateAtomForDecl(decl_index);
1469 const sym_index = decl.link.coff.getSymbolIndex().?;1513 const sym_index = self.getAtom(this_atom_index).getSymbolIndex().?;
14701514 const atom_index = self.getAtomIndexForSymbol(.{ .sym_index = reloc_info.parent_atom_index, .file = null }).?;
1471 const atom = self.getAtomForSymbol(.{ .sym_index = reloc_info.parent_atom_index, .file = null }).?;
1472 const target = SymbolWithLoc{ .sym_index = sym_index, .file = null };1515 const target = SymbolWithLoc{ .sym_index = sym_index, .file = null };
1473 try atom.addRelocation(self, .{1516 try Atom.addRelocation(self, atom_index, .{
1474 .type = .direct,1517 .type = .direct,
1475 .target = target,1518 .target = target,
1476 .offset = @intCast(u32, reloc_info.offset),1519 .offset = @intCast(u32, reloc_info.offset),
...@@ -1478,7 +1521,7 @@ pub fn getDeclVAddr(...@@ -1478,7 +1521,7 @@ pub fn getDeclVAddr(
1478 .pcrel = false,1521 .pcrel = false,
1479 .length = 3,1522 .length = 3,
1480 });1523 });
1481 try atom.addBaseRelocation(self, @intCast(u32, reloc_info.offset));1524 try Atom.addBaseRelocation(self, atom_index, @intCast(u32, reloc_info.offset));
14821525
1483 return 0;1526 return 0;
1484}1527}
...@@ -1505,10 +1548,10 @@ pub fn getGlobalSymbol(self: *Coff, name: []const u8) !u32 {...@@ -1505,10 +1548,10 @@ pub fn getGlobalSymbol(self: *Coff, name: []const u8) !u32 {
1505 return global_index;1548 return global_index;
1506}1549}
15071550
1508pub fn updateDeclLineNumber(self: *Coff, module: *Module, decl: *Module.Decl) !void {1551pub fn updateDeclLineNumber(self: *Coff, module: *Module, decl_index: Module.Decl.Index) !void {
1509 _ = self;1552 _ = self;
1510 _ = module;1553 _ = module;
1511 _ = decl;1554 _ = decl_index;
1512 log.debug("TODO implement updateDeclLineNumber", .{});1555 log.debug("TODO implement updateDeclLineNumber", .{});
1513}1556}
15141557
...@@ -1529,7 +1572,8 @@ fn writeBaseRelocations(self: *Coff) !void {...@@ -1529,7 +1572,8 @@ fn writeBaseRelocations(self: *Coff) !void {
15291572
1530 var it = self.base_relocs.iterator();1573 var it = self.base_relocs.iterator();
1531 while (it.next()) |entry| {1574 while (it.next()) |entry| {
1532 const atom = entry.key_ptr.*;1575 const atom_index = entry.key_ptr.*;
1576 const atom = self.getAtom(atom_index);
1533 const offsets = entry.value_ptr.*;1577 const offsets = entry.value_ptr.*;
15341578
1535 for (offsets.items) |offset| {1579 for (offsets.items) |offset| {
...@@ -1613,7 +1657,8 @@ fn writeImportTable(self: *Coff) !void {...@@ -1613,7 +1657,8 @@ fn writeImportTable(self: *Coff) !void {
1613 const gpa = self.base.allocator;1657 const gpa = self.base.allocator;
16141658
1615 const section = self.sections.get(self.idata_section_index.?);1659 const section = self.sections.get(self.idata_section_index.?);
1616 const last_atom = section.last_atom orelse return;1660 const last_atom_index = section.last_atom_index orelse return;
1661 const last_atom = self.getAtom(last_atom_index);
16171662
1618 const iat_rva = section.header.virtual_address;1663 const iat_rva = section.header.virtual_address;
1619 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
...@@ -2051,27 +2096,37 @@ pub fn getOrPutGlobalPtr(self: *Coff, name: []const u8) !GetOrPutGlobalPtrResult...@@ -2051,27 +2096,37 @@ pub fn getOrPutGlobalPtr(self: *Coff, name: []const u8) !GetOrPutGlobalPtrResult
2051 return GetOrPutGlobalPtrResult{ .found_existing = false, .value_ptr = ptr };2096 return GetOrPutGlobalPtrResult{ .found_existing = false, .value_ptr = ptr };
2052}2097}
20532098
2099pub fn getAtom(self: *const Coff, atom_index: Atom.Index) Atom {
2100 assert(atom_index < self.atoms.items.len);
2101 return self.atoms.items[atom_index];
2102}
2103
2104pub fn getAtomPtr(self: *Coff, atom_index: Atom.Index) *Atom {
2105 assert(atom_index < self.atoms.items.len);
2106 return &self.atoms.items[atom_index];
2107}
2108
2054/// 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.
2055/// Returns null on failure.2110/// Returns null on failure.
2056pub fn getAtomForSymbol(self: *Coff, sym_loc: SymbolWithLoc) ?*Atom {2111pub fn getAtomIndexForSymbol(self: *const Coff, sym_loc: SymbolWithLoc) ?Atom.Index {
2057 assert(sym_loc.file == null); // TODO linking with object files2112 assert(sym_loc.file == null); // TODO linking with object files
2058 return self.atom_by_index_table.get(sym_loc.sym_index);2113 return self.atom_by_index_table.get(sym_loc.sym_index);
2059}2114}
20602115
2061/// Returns GOT atom that references `sym_loc` if one exists.2116/// Returns GOT atom that references `sym_loc` if one exists.
2062/// Returns null otherwise.2117/// Returns null otherwise.
2063pub fn getGotAtomForSymbol(self: *Coff, sym_loc: SymbolWithLoc) ?*Atom {2118pub fn getGotAtomIndexForSymbol(self: *const Coff, sym_loc: SymbolWithLoc) ?Atom.Index {
2064 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;
2065 const got_entry = self.got_entries.items[got_index];2120 const got_entry = self.got_entries.items[got_index];
2066 return self.getAtomForSymbol(.{ .sym_index = got_entry.sym_index, .file = null });2121 return self.getAtomIndexForSymbol(.{ .sym_index = got_entry.sym_index, .file = null });
2067}2122}
20682123
2069/// Returns import atom that references `sym_loc` if one exists.2124/// Returns import atom that references `sym_loc` if one exists.
2070/// Returns null otherwise.2125/// Returns null otherwise.
2071pub fn getImportAtomForSymbol(self: *Coff, sym_loc: SymbolWithLoc) ?*Atom {2126pub fn getImportAtomIndexForSymbol(self: *const Coff, sym_loc: SymbolWithLoc) ?Atom.Index {
2072 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;
2073 const imports_entry = self.imports.items[imports_index];2128 const imports_entry = self.imports.items[imports_index];
2074 return self.getAtomForSymbol(.{ .sym_index = imports_entry.sym_index, .file = null });2129 return self.getAtomIndexForSymbol(.{ .sym_index = imports_entry.sym_index, .file = null });
2075}2130}
20762131
2077fn 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+24-24
...@@ -27,23 +27,10 @@ alignment: u32,...@@ -27,23 +27,10 @@ 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,
35 .file = null,
36 .size = 0,
37 .alignment = 0,
38 .prev = null,
39 .next = null,
40};
41
42pub fn ensureInitialized(self: *Atom, coff_file: *Coff) !void {
43 if (self.getSymbolIndex() != null) return; // Already initialized
44 self.sym_index = try coff_file.allocateSymbol();
45 try coff_file.atom_by_index_table.putNoClobber(coff_file.base.allocator, self.sym_index, self);
46}
4734
48pub fn getSymbolIndex(self: Atom) ?u32 {35pub fn getSymbolIndex(self: Atom) ?u32 {
49 if (self.sym_index == 0) return null;36 if (self.sym_index == 0) return null;
...@@ -85,7 +72,8 @@ pub fn getName(self: Atom, coff_file: *const Coff) []const u8 {...@@ -85,7 +72,8 @@ pub fn getName(self: Atom, coff_file: *const Coff) []const u8 {
85/// 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.
86pub fn capacity(self: Atom, coff_file: *const Coff) u32 {73pub fn capacity(self: Atom, coff_file: *const Coff) u32 {
87 const self_sym = self.getSymbol(coff_file);74 const self_sym = self.getSymbol(coff_file);
88 if (self.next) |next| {75 if (self.next_index) |next_index| {
76 const next = coff_file.getAtom(next_index);
89 const next_sym = next.getSymbol(coff_file);77 const next_sym = next.getSymbol(coff_file);
90 return next_sym.value - self_sym.value;78 return next_sym.value - self_sym.value;
91 } else {79 } else {
...@@ -97,7 +85,8 @@ pub fn capacity(self: Atom, coff_file: *const Coff) u32 {...@@ -97,7 +85,8 @@ pub fn capacity(self: Atom, coff_file: *const Coff) u32 {
9785
98pub fn freeListEligible(self: Atom, coff_file: *const Coff) bool {86pub fn freeListEligible(self: Atom, coff_file: *const Coff) bool {
99 // 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.
100 const next = self.next orelse return false;88 const next_index = self.next_index orelse return false;
89 const next = coff_file.getAtom(next_index);
101 const self_sym = self.getSymbol(coff_file);90 const self_sym = self.getSymbol(coff_file);
102 const next_sym = next.getSymbol(coff_file);91 const next_sym = next.getSymbol(coff_file);
103 const cap = next_sym.value - self_sym.value;92 const cap = next_sym.value - self_sym.value;
...@@ -107,22 +96,33 @@ pub fn freeListEligible(self: Atom, coff_file: *const Coff) bool {...@@ -107,22 +96,33 @@ pub fn freeListEligible(self: Atom, coff_file: *const Coff) bool {
107 return surplus >= Coff.min_text_capacity;96 return surplus >= Coff.min_text_capacity;
108}97}
10998
110pub fn addRelocation(self: *Atom, coff_file: *Coff, reloc: Relocation) !void {99pub fn addRelocation(coff_file: *Coff, atom_index: Index, reloc: Relocation) !void {
111 const gpa = coff_file.base.allocator;100 const gpa = coff_file.base.allocator;
112 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 });
113 const gop = try coff_file.relocs.getOrPut(gpa, self);102 const gop = try coff_file.relocs.getOrPut(gpa, atom_index);
114 if (!gop.found_existing) {103 if (!gop.found_existing) {
115 gop.value_ptr.* = .{};104 gop.value_ptr.* = .{};
116 }105 }
117 try gop.value_ptr.append(gpa, reloc);106 try gop.value_ptr.append(gpa, reloc);
118}107}
119108
120pub fn addBaseRelocation(self: *Atom, coff_file: *Coff, offset: u32) !void {109pub fn addBaseRelocation(coff_file: *Coff, atom_index: Index, offset: u32) !void {
121 const gpa = coff_file.base.allocator;110 const gpa = coff_file.base.allocator;
122 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})", .{
123 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);
124 if (!gop.found_existing) {116 if (!gop.found_existing) {
125 gop.value_ptr.* = .{};117 gop.value_ptr.* = .{};
126 }118 }
127 try gop.value_ptr.append(gpa, offset);119 try gop.value_ptr.append(gpa, offset);
128}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+636-570
...@@ -1,43 +1,89 @@...@@ -1,43 +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 Atom = @import("Elf/Atom.zig");
14const Module = @import("../Module.zig");
15const Compilation = @import("../Compilation.zig");
16const Dwarf = @import("Dwarf.zig");
17const codegen = @import("../codegen.zig");13const codegen = @import("../codegen.zig");
18const lldMain = @import("../main.zig").lldMain;
19const trace = @import("../tracy.zig").trace;
20const Package = @import("../Package.zig");
21const Value = @import("../value.zig").Value;
22const Type = @import("../type.zig").Type;
23const TypedValue = @import("../TypedValue.zig");
24const link = @import("../link.zig");
25const File = link.File;
26const build_options = @import("build_options");
27const target_util = @import("../target.zig");
28const glibc = @import("../glibc.zig");14const glibc = @import("../glibc.zig");
15const link = @import("../link.zig");
16const lldMain = @import("../main.zig").lldMain;
29const musl = @import("../musl.zig");17const musl = @import("../musl.zig");
30const Cache = @import("../Cache.zig");18const target_util = @import("../target.zig");
19const trace = @import("../tracy.zig").trace;
20
31const 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;
32const Liveness = @import("../Liveness.zig");28const Liveness = @import("../Liveness.zig");
33const LlvmObject = @import("../codegen/llvm.zig").Object;29const LlvmObject = @import("../codegen/llvm.zig").Object;
3430const Module = @import("../Module.zig");
35pub const TextBlock = Atom;31const Package = @import("../Package.zig");
32const StringTable = @import("strtab.zig").StringTable;
33const Type = @import("../type.zig").Type;
34const TypedValue = @import("../TypedValue.zig");
35const Value = @import("../value.zig").Value;
3636
37const default_entry_addr = 0x8000000;37const default_entry_addr = 0x8000000;
3838
39pub const base_tag: File.Tag = .elf;39pub const base_tag: File.Tag = .elf;
4040
41const Section = struct {
42 shdr: elf.Elf64_Shdr,
43 phdr_index: u16,
44
45 /// Index of the last allocated atom in this section.
46 last_atom_index: ?Atom.Index = null,
47
48 /// A list of atoms that have surplus capacity. This list can have false
49 /// positives, as functions grow and shrink over time, only sometimes being added
50 /// or removed from the freelist.
51 ///
52 /// An atom has surplus capacity when its overcapacity value is greater than
53 /// padToIdeal(minimum_atom_size). That is, when it has so
54 /// much extra capacity, that we could fit a small new symbol in it, itself with
55 /// ideal_capacity or more.
56 ///
57 /// Ideal capacity is defined by size + (size / ideal_factor)
58 ///
59 /// Overcapacity is measured by actual_capacity - ideal_capacity. Note that
60 /// overcapacity can be negative. A simple way to have negative overcapacity is to
61 /// allocate a fresh text block, which will have ideal capacity, and then grow it
62 /// by 1 byte. It will then have -1 overcapacity.
63 free_list: std.ArrayListUnmanaged(Atom.Index) = .{},
64};
65
66const DeclMetadata = struct {
67 atom: Atom.Index,
68 shdr: u16,
69 /// A list of all exports aliases of this Decl.
70 exports: std.ArrayListUnmanaged(u32) = .{},
71
72 fn getExport(m: DeclMetadata, elf_file: *const Elf, name: []const u8) ?u32 {
73 for (m.exports.items) |exp| {
74 if (mem.eql(u8, name, elf_file.getGlobalName(exp))) return exp;
75 }
76 return null;
77 }
78
79 fn getExportPtr(m: *DeclMetadata, elf_file: *Elf, name: []const u8) ?*u32 {
80 for (m.exports.items) |*exp| {
81 if (mem.eql(u8, name, elf_file.getGlobalName(exp.*))) return exp;
82 }
83 return null;
84 }
85};
86
41base: File,87base: File,
42dwarf: ?Dwarf = null,88dwarf: ?Dwarf = null,
4389
...@@ -48,12 +94,12 @@ llvm_object: ?*LlvmObject = null,...@@ -48,12 +94,12 @@ llvm_object: ?*LlvmObject = null,
4894
49/// 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.
50/// Same order as in the file.96/// Same order as in the file.
51sections: std.ArrayListUnmanaged(elf.Elf64_Shdr) = std.ArrayListUnmanaged(elf.Elf64_Shdr){},97sections: std.MultiArrayList(Section) = .{},
52shdr_table_offset: ?u64 = null,98shdr_table_offset: ?u64 = null,
5399
54/// 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.
55/// Same order as in the file.101/// Same order as in the file.
56program_headers: std.ArrayListUnmanaged(elf.Elf64_Phdr) = std.ArrayListUnmanaged(elf.Elf64_Phdr){},102program_headers: std.ArrayListUnmanaged(elf.Elf64_Phdr) = .{},
57phdr_table_offset: ?u64 = null,103phdr_table_offset: ?u64 = null,
58/// 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
59phdr_load_re_index: ?u16 = null,105phdr_load_re_index: ?u16 = null,
...@@ -65,12 +111,10 @@ phdr_load_ro_index: ?u16 = null,...@@ -65,12 +111,10 @@ phdr_load_ro_index: ?u16 = null,
65/// 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
66phdr_load_rw_index: ?u16 = null,112phdr_load_rw_index: ?u16 = null,
67113
68phdr_shdr_table: std.AutoHashMapUnmanaged(u16, u16) = .{},
69
70entry_addr: ?u64 = null,114entry_addr: ?u64 = null,
71page_size: u32,115page_size: u32,
72116
73shstrtab: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8){},117shstrtab: StringTable(.strtab) = .{},
74shstrtab_index: ?u16 = null,118shstrtab_index: ?u16 = null,
75119
76symtab_section_index: ?u16 = null,120symtab_section_index: ?u16 = null,
...@@ -113,39 +157,14 @@ debug_line_header_dirty: bool = false,...@@ -113,39 +157,14 @@ debug_line_header_dirty: bool = false,
113157
114error_flags: File.ErrorFlags = File.ErrorFlags{},158error_flags: File.ErrorFlags = File.ErrorFlags{},
115159
116/// Pointer to the last allocated atom160/// Table of tracked Decls.
117atoms: std.AutoHashMapUnmanaged(u16, *TextBlock) = .{},161decls: std.AutoHashMapUnmanaged(Module.Decl.Index, DeclMetadata) = .{},
118
119/// A list of text blocks that have surplus capacity. This list can have false
120/// positives, as functions grow and shrink over time, only sometimes being added
121/// or removed from the freelist.
122///
123/// A text block has surplus capacity when its overcapacity value is greater than
124/// padToIdeal(minimum_text_block_size). That is, when it has so
125/// much extra capacity, that we could fit a small new symbol in it, itself with
126/// ideal_capacity or more.
127///
128/// Ideal capacity is defined by size + (size / ideal_factor)
129///
130/// Overcapacity is measured by actual_capacity - ideal_capacity. Note that
131/// overcapacity can be negative. A simple way to have negative overcapacity is to
132/// allocate a fresh text block, which will have ideal capacity, and then grow it
133/// by 1 byte. It will then have -1 overcapacity.
134atom_free_lists: std.AutoHashMapUnmanaged(u16, std.ArrayListUnmanaged(*TextBlock)) = .{},
135
136/// Table of Decls that are currently alive.
137/// We store them here so that we can properly dispose of any allocated
138/// memory within the atom in the incremental linker.
139/// TODO consolidate this.
140decls: std.AutoHashMapUnmanaged(Module.Decl.Index, ?u16) = .{},
141162
142/// List of atoms that are owned directly by the linker.163/// List of atoms that are owned directly by the linker.
143/// Currently these are only atoms that are the result of linking164atoms: std.ArrayListUnmanaged(Atom) = .{},
144/// object files. Atoms which take part in incremental linking are165
145/// at present owned by Module.Decl.166/// Table of atoms indexed by the symbol index.
146/// TODO consolidate this.167atom_by_index_table: std.AutoHashMapUnmanaged(u32, Atom.Index) = .{},
147managed_atoms: std.ArrayListUnmanaged(*TextBlock) = .{},
148atom_by_index_table: std.AutoHashMapUnmanaged(u32, *TextBlock) = .{},
149168
150/// Table of unnamed constants associated with a parent `Decl`.169/// Table of unnamed constants associated with a parent `Decl`.
151/// 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`
...@@ -173,15 +192,8 @@ unnamed_const_atoms: UnnamedConstTable = .{},...@@ -173,15 +192,8 @@ unnamed_const_atoms: UnnamedConstTable = .{},
173/// 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.
174relocs: RelocTable = .{},193relocs: RelocTable = .{},
175194
176const Reloc = struct {195const RelocTable = std.AutoHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(Atom.Reloc));
177 target: u32,196const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(Atom.Index));
178 offset: u64,
179 addend: u32,
180 prev_vaddr: u64,
181};
182
183const RelocTable = std.AutoHashMapUnmanaged(*TextBlock, std.ArrayListUnmanaged(Reloc));
184const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(*TextBlock));
185197
186/// When allocating, the ideal_capacity is calculated by198/// When allocating, the ideal_capacity is calculated by
187/// actual_capacity + (actual_capacity / ideal_factor)199/// actual_capacity + (actual_capacity / ideal_factor)
...@@ -190,15 +202,11 @@ const ideal_factor = 3;...@@ -190,15 +202,11 @@ const ideal_factor = 3;
190/// 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
191/// 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
192/// (plus extra for reserved capacity).204/// (plus extra for reserved capacity).
193const minimum_text_block_size = 64;205const minimum_atom_size = 64;
194pub const min_text_capacity = padToIdeal(minimum_text_block_size);206pub const min_text_capacity = padToIdeal(minimum_atom_size);
195207
196pub const PtrWidth = enum { p32, p64 };208pub const PtrWidth = enum { p32, p64 };
197209
198pub const Export = struct {
199 sym_index: ?u32 = null,
200};
201
202pub 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 {
203 assert(options.target.ofmt == .elf);211 assert(options.target.ofmt == .elf);
204212
...@@ -230,16 +238,19 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option...@@ -230,16 +238,19 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
230238
231 // There must always be a null section in index 0239 // There must always be a null section in index 0
232 try self.sections.append(allocator, .{240 try self.sections.append(allocator, .{
233 .sh_name = 0,241 .shdr = .{
234 .sh_type = elf.SHT_NULL,242 .sh_name = 0,
235 .sh_flags = 0,243 .sh_type = elf.SHT_NULL,
236 .sh_addr = 0,244 .sh_flags = 0,
237 .sh_offset = 0,245 .sh_addr = 0,
238 .sh_size = 0,246 .sh_offset = 0,
239 .sh_link = 0,247 .sh_size = 0,
240 .sh_info = 0,248 .sh_link = 0,
241 .sh_addralign = 0,249 .sh_info = 0,
242 .sh_entsize = 0,250 .sh_addralign = 0,
251 .sh_entsize = 0,
252 },
253 .phdr_index = undefined,
243 });254 });
244255
245 try self.populateMissingMetadata();256 try self.populateMissingMetadata();
...@@ -286,75 +297,67 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Elf {...@@ -286,75 +297,67 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Elf {
286}297}
287298
288pub fn deinit(self: *Elf) void {299pub fn deinit(self: *Elf) void {
300 const gpa = self.base.allocator;
301
289 if (build_options.have_llvm) {302 if (build_options.have_llvm) {
290 if (self.llvm_object) |llvm_object| llvm_object.destroy(self.base.allocator);303 if (self.llvm_object) |llvm_object| llvm_object.destroy(gpa);
291 }304 }
292305
293 self.sections.deinit(self.base.allocator);306 for (self.sections.items(.free_list)) |*free_list| {
294 self.program_headers.deinit(self.base.allocator);307 free_list.deinit(gpa);
295 self.shstrtab.deinit(self.base.allocator);308 }
296 self.local_symbols.deinit(self.base.allocator);309 self.sections.deinit(gpa);
297 self.global_symbols.deinit(self.base.allocator);310
298 self.global_symbol_free_list.deinit(self.base.allocator);311 self.program_headers.deinit(gpa);
299 self.local_symbol_free_list.deinit(self.base.allocator);312 self.shstrtab.deinit(gpa);
300 self.offset_table_free_list.deinit(self.base.allocator);313 self.local_symbols.deinit(gpa);
301 self.offset_table.deinit(self.base.allocator);314 self.global_symbols.deinit(gpa);
302 self.phdr_shdr_table.deinit(self.base.allocator);315 self.global_symbol_free_list.deinit(gpa);
303 self.decls.deinit(self.base.allocator);316 self.local_symbol_free_list.deinit(gpa);
304317 self.offset_table_free_list.deinit(gpa);
305 self.atoms.deinit(self.base.allocator);318 self.offset_table.deinit(gpa);
319
306 {320 {
307 var it = self.atom_free_lists.valueIterator();321 var it = self.decls.iterator();
308 while (it.next()) |free_list| {322 while (it.next()) |entry| {
309 free_list.deinit(self.base.allocator);323 entry.value_ptr.exports.deinit(gpa);
310 }324 }
311 self.atom_free_lists.deinit(self.base.allocator);325 self.decls.deinit(gpa);
312 }326 }
313327
314 for (self.managed_atoms.items) |atom| {328 self.atoms.deinit(gpa);
315 self.base.allocator.destroy(atom);329 self.atom_by_index_table.deinit(gpa);
316 }
317 self.managed_atoms.deinit(self.base.allocator);
318330
319 {331 {
320 var it = self.unnamed_const_atoms.valueIterator();332 var it = self.unnamed_const_atoms.valueIterator();
321 while (it.next()) |atoms| {333 while (it.next()) |atoms| {
322 atoms.deinit(self.base.allocator);334 atoms.deinit(gpa);
323 }335 }
324 self.unnamed_const_atoms.deinit(self.base.allocator);336 self.unnamed_const_atoms.deinit(gpa);
325 }337 }
326338
327 {339 {
328 var it = self.relocs.valueIterator();340 var it = self.relocs.valueIterator();
329 while (it.next()) |relocs| {341 while (it.next()) |relocs| {
330 relocs.deinit(self.base.allocator);342 relocs.deinit(gpa);
331 }343 }
332 self.relocs.deinit(self.base.allocator);344 self.relocs.deinit(gpa);
333 }345 }
334346
335 self.atom_by_index_table.deinit(self.base.allocator);
336
337 if (self.dwarf) |*dw| {347 if (self.dwarf) |*dw| {
338 dw.deinit();348 dw.deinit();
339 }349 }
340}350}
341351
342pub 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 {
343 const mod = self.base.options.module.?;
344 const decl = mod.declPtr(decl_index);
345
346 assert(self.llvm_object == null);353 assert(self.llvm_object == null);
347354
348 try decl.link.elf.ensureInitialized(self);355 const this_atom_index = try self.getOrCreateAtomForDecl(decl_index);
349 const target = decl.link.elf.getSymbolIndex().?;356 const this_atom = self.getAtom(this_atom_index);
350357 const target = this_atom.getSymbolIndex().?;
351 const vaddr = self.local_symbols.items[target].st_value;358 const vaddr = this_atom.getSymbol(self).st_value;
352 const atom = self.atom_by_index_table.get(reloc_info.parent_atom_index).?;359 const atom_index = self.getAtomIndexForSymbol(reloc_info.parent_atom_index).?;
353 const gop = try self.relocs.getOrPut(self.base.allocator, atom);360 try Atom.addRelocation(self, atom_index, .{
354 if (!gop.found_existing) {
355 gop.value_ptr.* = .{};
356 }
357 try gop.value_ptr.append(self.base.allocator, .{
358 .target = target,361 .target = target,
359 .offset = reloc_info.offset,362 .offset = reloc_info.offset,
360 .addend = reloc_info.addend,363 .addend = reloc_info.addend,
...@@ -375,7 +378,7 @@ fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 {...@@ -375,7 +378,7 @@ fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 {
375378
376 if (self.shdr_table_offset) |off| {379 if (self.shdr_table_offset) |off| {
377 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);
378 const tight_size = self.sections.items.len * shdr_size;381 const tight_size = self.sections.slice().len * shdr_size;
379 const increased_size = padToIdeal(tight_size);382 const increased_size = padToIdeal(tight_size);
380 const test_end = off + increased_size;383 const test_end = off + increased_size;
381 if (end > off and start < test_end) {384 if (end > off and start < test_end) {
...@@ -385,7 +388,7 @@ fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 {...@@ -385,7 +388,7 @@ fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 {
385388
386 if (self.phdr_table_offset) |off| {389 if (self.phdr_table_offset) |off| {
387 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);
388 const tight_size = self.sections.items.len * phdr_size;391 const tight_size = self.sections.slice().len * phdr_size;
389 const increased_size = padToIdeal(tight_size);392 const increased_size = padToIdeal(tight_size);
390 const test_end = off + increased_size;393 const test_end = off + increased_size;
391 if (end > off and start < test_end) {394 if (end > off and start < test_end) {
...@@ -393,7 +396,7 @@ fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 {...@@ -393,7 +396,7 @@ fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 {
393 }396 }
394 }397 }
395398
396 for (self.sections.items) |section| {399 for (self.sections.items(.shdr)) |section| {
397 const increased_size = padToIdeal(section.sh_size);400 const increased_size = padToIdeal(section.sh_size);
398 const test_end = section.sh_offset + increased_size;401 const test_end = section.sh_offset + increased_size;
399 if (end > section.sh_offset and start < test_end) {402 if (end > section.sh_offset and start < test_end) {
...@@ -420,7 +423,7 @@ pub fn allocatedSize(self: *Elf, start: u64) u64 {...@@ -420,7 +423,7 @@ pub fn allocatedSize(self: *Elf, start: u64) u64 {
420 if (self.phdr_table_offset) |off| {423 if (self.phdr_table_offset) |off| {
421 if (off > start and off < min_pos) min_pos = off;424 if (off > start and off < min_pos) min_pos = off;
422 }425 }
423 for (self.sections.items) |section| {426 for (self.sections.items(.shdr)) |section| {
424 if (section.sh_offset <= start) continue;427 if (section.sh_offset <= start) continue;
425 if (section.sh_offset < min_pos) min_pos = section.sh_offset;428 if (section.sh_offset < min_pos) min_pos = section.sh_offset;
426 }429 }
...@@ -439,31 +442,10 @@ pub fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u32) u64 {...@@ -439,31 +442,10 @@ pub fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u32) u64 {
439 return start;442 return start;
440}443}
441444
442/// TODO Improve this to use a table.
443fn makeString(self: *Elf, bytes: []const u8) !u32 {
444 try self.shstrtab.ensureUnusedCapacity(self.base.allocator, bytes.len + 1);
445 const result = self.shstrtab.items.len;
446 self.shstrtab.appendSliceAssumeCapacity(bytes);
447 self.shstrtab.appendAssumeCapacity(0);
448 return @intCast(u32, result);
449}
450
451pub fn getString(self: Elf, str_off: u32) []const u8 {
452 assert(str_off < self.shstrtab.items.len);
453 return mem.sliceTo(@ptrCast([*:0]const u8, self.shstrtab.items.ptr + str_off), 0);
454}
455
456fn updateString(self: *Elf, old_str_off: u32, new_name: []const u8) !u32 {
457 const existing_name = self.getString(old_str_off);
458 if (mem.eql(u8, existing_name, new_name)) {
459 return old_str_off;
460 }
461 return self.makeString(new_name);
462}
463
464pub fn populateMissingMetadata(self: *Elf) !void {445pub fn populateMissingMetadata(self: *Elf) !void {
465 assert(self.llvm_object == null);446 assert(self.llvm_object == null);
466447
448 const gpa = self.base.allocator;
467 const small_ptr = switch (self.ptr_width) {449 const small_ptr = switch (self.ptr_width) {
468 .p32 => true,450 .p32 => true,
469 .p64 => false,451 .p64 => false,
...@@ -477,7 +459,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {...@@ -477,7 +459,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {
477 const off = self.findFreeSpace(file_size, p_align);459 const off = self.findFreeSpace(file_size, p_align);
478 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 });
479 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;
480 try self.program_headers.append(self.base.allocator, .{462 try self.program_headers.append(gpa, .{
481 .p_type = elf.PT_LOAD,463 .p_type = elf.PT_LOAD,
482 .p_offset = off,464 .p_offset = off,
483 .p_filesz = file_size,465 .p_filesz = file_size,
...@@ -487,7 +469,6 @@ pub fn populateMissingMetadata(self: *Elf) !void {...@@ -487,7 +469,6 @@ pub fn populateMissingMetadata(self: *Elf) !void {
487 .p_align = p_align,469 .p_align = p_align,
488 .p_flags = elf.PF_X | elf.PF_R,470 .p_flags = elf.PF_X | elf.PF_R,
489 });471 });
490 try self.atom_free_lists.putNoClobber(self.base.allocator, self.phdr_load_re_index.?, .{});
491 self.entry_addr = null;472 self.entry_addr = null;
492 self.phdr_table_dirty = true;473 self.phdr_table_dirty = true;
493 }474 }
...@@ -504,7 +485,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {...@@ -504,7 +485,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {
504 // 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
505 // else in virtual memory.486 // else in virtual memory.
506 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;
507 try self.program_headers.append(self.base.allocator, .{488 try self.program_headers.append(gpa, .{
508 .p_type = elf.PT_LOAD,489 .p_type = elf.PT_LOAD,
509 .p_offset = off,490 .p_offset = off,
510 .p_filesz = file_size,491 .p_filesz = file_size,
...@@ -527,7 +508,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {...@@ -527,7 +508,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {
527 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 });
528 // TODO Same as for GOT509 // TODO Same as for GOT
529 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;
530 try self.program_headers.append(self.base.allocator, .{511 try self.program_headers.append(gpa, .{
531 .p_type = elf.PT_LOAD,512 .p_type = elf.PT_LOAD,
532 .p_offset = off,513 .p_offset = off,
533 .p_filesz = file_size,514 .p_filesz = file_size,
...@@ -537,7 +518,6 @@ pub fn populateMissingMetadata(self: *Elf) !void {...@@ -537,7 +518,6 @@ pub fn populateMissingMetadata(self: *Elf) !void {
537 .p_align = p_align,518 .p_align = p_align,
538 .p_flags = elf.PF_R,519 .p_flags = elf.PF_R,
539 });520 });
540 try self.atom_free_lists.putNoClobber(self.base.allocator, self.phdr_load_ro_index.?, .{});
541 self.phdr_table_dirty = true;521 self.phdr_table_dirty = true;
542 }522 }
543523
...@@ -551,7 +531,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {...@@ -551,7 +531,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {
551 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 });
552 // TODO Same as for GOT532 // TODO Same as for GOT
553 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;
554 try self.program_headers.append(self.base.allocator, .{534 try self.program_headers.append(gpa, .{
555 .p_type = elf.PT_LOAD,535 .p_type = elf.PT_LOAD,
556 .p_offset = off,536 .p_offset = off,
557 .p_filesz = file_size,537 .p_filesz = file_size,
...@@ -561,148 +541,145 @@ pub fn populateMissingMetadata(self: *Elf) !void {...@@ -561,148 +541,145 @@ pub fn populateMissingMetadata(self: *Elf) !void {
561 .p_align = p_align,541 .p_align = p_align,
562 .p_flags = elf.PF_R | elf.PF_W,542 .p_flags = elf.PF_R | elf.PF_W,
563 });543 });
564 try self.atom_free_lists.putNoClobber(self.base.allocator, self.phdr_load_rw_index.?, .{});
565 self.phdr_table_dirty = true;544 self.phdr_table_dirty = true;
566 }545 }
567546
568 if (self.shstrtab_index == null) {547 if (self.shstrtab_index == null) {
569 self.shstrtab_index = @intCast(u16, self.sections.items.len);548 self.shstrtab_index = @intCast(u16, self.sections.slice().len);
570 assert(self.shstrtab.items.len == 0);549 assert(self.shstrtab.buffer.items.len == 0);
571 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
572 const off = self.findFreeSpace(self.shstrtab.items.len, 1);551 const off = self.findFreeSpace(self.shstrtab.buffer.items.len, 1);
573 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 });
574 try self.sections.append(self.base.allocator, .{553 try self.sections.append(gpa, .{
575 .sh_name = try self.makeString(".shstrtab"),554 .shdr = .{
576 .sh_type = elf.SHT_STRTAB,555 .sh_name = try self.shstrtab.insert(gpa, ".shstrtab"),
577 .sh_flags = 0,556 .sh_type = elf.SHT_STRTAB,
578 .sh_addr = 0,557 .sh_flags = 0,
579 .sh_offset = off,558 .sh_addr = 0,
580 .sh_size = self.shstrtab.items.len,559 .sh_offset = off,
581 .sh_link = 0,560 .sh_size = self.shstrtab.buffer.items.len,
582 .sh_info = 0,561 .sh_link = 0,
583 .sh_addralign = 1,562 .sh_info = 0,
584 .sh_entsize = 0,563 .sh_addralign = 1,
564 .sh_entsize = 0,
565 },
566 .phdr_index = undefined,
585 });567 });
586 self.shstrtab_dirty = true;568 self.shstrtab_dirty = true;
587 self.shdr_table_dirty = true;569 self.shdr_table_dirty = true;
588 }570 }
589571
590 if (self.text_section_index == null) {572 if (self.text_section_index == null) {
591 self.text_section_index = @intCast(u16, self.sections.items.len);573 self.text_section_index = @intCast(u16, self.sections.slice().len);
592 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];574 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
593575
594 try self.sections.append(self.base.allocator, .{576 try self.sections.append(gpa, .{
595 .sh_name = try self.makeString(".text"),577 .shdr = .{
596 .sh_type = elf.SHT_PROGBITS,578 .sh_name = try self.shstrtab.insert(gpa, ".text"),
597 .sh_flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR,579 .sh_type = elf.SHT_PROGBITS,
598 .sh_addr = phdr.p_vaddr,580 .sh_flags = elf.SHF_ALLOC | elf.SHF_EXECINSTR,
599 .sh_offset = phdr.p_offset,581 .sh_addr = phdr.p_vaddr,
600 .sh_size = phdr.p_filesz,582 .sh_offset = phdr.p_offset,
601 .sh_link = 0,583 .sh_size = phdr.p_filesz,
602 .sh_info = 0,584 .sh_link = 0,
603 .sh_addralign = 1,585 .sh_info = 0,
604 .sh_entsize = 0,586 .sh_addralign = 1,
587 .sh_entsize = 0,
588 },
589 .phdr_index = self.phdr_load_re_index.?,
605 });590 });
606 try self.phdr_shdr_table.putNoClobber(
607 self.base.allocator,
608 self.phdr_load_re_index.?,
609 self.text_section_index.?,
610 );
611 self.shdr_table_dirty = true;591 self.shdr_table_dirty = true;
612 }592 }
613593
614 if (self.got_section_index == null) {594 if (self.got_section_index == null) {
615 self.got_section_index = @intCast(u16, self.sections.items.len);595 self.got_section_index = @intCast(u16, self.sections.slice().len);
616 const phdr = &self.program_headers.items[self.phdr_got_index.?];596 const phdr = &self.program_headers.items[self.phdr_got_index.?];
617597
618 try self.sections.append(self.base.allocator, .{598 try self.sections.append(gpa, .{
619 .sh_name = try self.makeString(".got"),599 .shdr = .{
620 .sh_type = elf.SHT_PROGBITS,600 .sh_name = try self.shstrtab.insert(gpa, ".got"),
621 .sh_flags = elf.SHF_ALLOC,601 .sh_type = elf.SHT_PROGBITS,
622 .sh_addr = phdr.p_vaddr,602 .sh_flags = elf.SHF_ALLOC,
623 .sh_offset = phdr.p_offset,603 .sh_addr = phdr.p_vaddr,
624 .sh_size = phdr.p_filesz,604 .sh_offset = phdr.p_offset,
625 .sh_link = 0,605 .sh_size = phdr.p_filesz,
626 .sh_info = 0,606 .sh_link = 0,
627 .sh_addralign = @as(u16, ptr_size),607 .sh_info = 0,
628 .sh_entsize = 0,608 .sh_addralign = @as(u16, ptr_size),
609 .sh_entsize = 0,
610 },
611 .phdr_index = self.phdr_got_index.?,
629 });612 });
630 try self.phdr_shdr_table.putNoClobber(
631 self.base.allocator,
632 self.phdr_got_index.?,
633 self.got_section_index.?,
634 );
635 self.shdr_table_dirty = true;613 self.shdr_table_dirty = true;
636 }614 }
637615
638 if (self.rodata_section_index == null) {616 if (self.rodata_section_index == null) {
639 self.rodata_section_index = @intCast(u16, self.sections.items.len);617 self.rodata_section_index = @intCast(u16, self.sections.slice().len);
640 const phdr = &self.program_headers.items[self.phdr_load_ro_index.?];618 const phdr = &self.program_headers.items[self.phdr_load_ro_index.?];
641619
642 try self.sections.append(self.base.allocator, .{620 try self.sections.append(gpa, .{
643 .sh_name = try self.makeString(".rodata"),621 .shdr = .{
644 .sh_type = elf.SHT_PROGBITS,622 .sh_name = try self.shstrtab.insert(gpa, ".rodata"),
645 .sh_flags = elf.SHF_ALLOC,623 .sh_type = elf.SHT_PROGBITS,
646 .sh_addr = phdr.p_vaddr,624 .sh_flags = elf.SHF_ALLOC,
647 .sh_offset = phdr.p_offset,625 .sh_addr = phdr.p_vaddr,
648 .sh_size = phdr.p_filesz,626 .sh_offset = phdr.p_offset,
649 .sh_link = 0,627 .sh_size = phdr.p_filesz,
650 .sh_info = 0,628 .sh_link = 0,
651 .sh_addralign = 1,629 .sh_info = 0,
652 .sh_entsize = 0,630 .sh_addralign = 1,
631 .sh_entsize = 0,
632 },
633 .phdr_index = self.phdr_load_ro_index.?,
653 });634 });
654 try self.phdr_shdr_table.putNoClobber(
655 self.base.allocator,
656 self.phdr_load_ro_index.?,
657 self.rodata_section_index.?,
658 );
659 self.shdr_table_dirty = true;635 self.shdr_table_dirty = true;
660 }636 }
661637
662 if (self.data_section_index == null) {638 if (self.data_section_index == null) {
663 self.data_section_index = @intCast(u16, self.sections.items.len);639 self.data_section_index = @intCast(u16, self.sections.slice().len);
664 const phdr = &self.program_headers.items[self.phdr_load_rw_index.?];640 const phdr = &self.program_headers.items[self.phdr_load_rw_index.?];
665641
666 try self.sections.append(self.base.allocator, .{642 try self.sections.append(gpa, .{
667 .sh_name = try self.makeString(".data"),643 .shdr = .{
668 .sh_type = elf.SHT_PROGBITS,644 .sh_name = try self.shstrtab.insert(gpa, ".data"),
669 .sh_flags = elf.SHF_WRITE | elf.SHF_ALLOC,645 .sh_type = elf.SHT_PROGBITS,
670 .sh_addr = phdr.p_vaddr,646 .sh_flags = elf.SHF_WRITE | elf.SHF_ALLOC,
671 .sh_offset = phdr.p_offset,647 .sh_addr = phdr.p_vaddr,
672 .sh_size = phdr.p_filesz,648 .sh_offset = phdr.p_offset,
673 .sh_link = 0,649 .sh_size = phdr.p_filesz,
674 .sh_info = 0,650 .sh_link = 0,
675 .sh_addralign = @as(u16, ptr_size),651 .sh_info = 0,
676 .sh_entsize = 0,652 .sh_addralign = @as(u16, ptr_size),
653 .sh_entsize = 0,
654 },
655 .phdr_index = self.phdr_load_rw_index.?,
677 });656 });
678 try self.phdr_shdr_table.putNoClobber(
679 self.base.allocator,
680 self.phdr_load_rw_index.?,
681 self.data_section_index.?,
682 );
683 self.shdr_table_dirty = true;657 self.shdr_table_dirty = true;
684 }658 }
685659
686 if (self.symtab_section_index == null) {660 if (self.symtab_section_index == null) {
687 self.symtab_section_index = @intCast(u16, self.sections.items.len);661 self.symtab_section_index = @intCast(u16, self.sections.slice().len);
688 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);
689 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);
690 const file_size = self.base.options.symbol_count_hint * each_size;664 const file_size = self.base.options.symbol_count_hint * each_size;
691 const off = self.findFreeSpace(file_size, min_align);665 const off = self.findFreeSpace(file_size, min_align);
692 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 });
693667
694 try self.sections.append(self.base.allocator, .{668 try self.sections.append(gpa, .{
695 .sh_name = try self.makeString(".symtab"),669 .shdr = .{
696 .sh_type = elf.SHT_SYMTAB,670 .sh_name = try self.shstrtab.insert(gpa, ".symtab"),
697 .sh_flags = 0,671 .sh_type = elf.SHT_SYMTAB,
698 .sh_addr = 0,672 .sh_flags = 0,
699 .sh_offset = off,673 .sh_addr = 0,
700 .sh_size = file_size,674 .sh_offset = off,
701 // The section header index of the associated string table.675 .sh_size = file_size,
702 .sh_link = self.shstrtab_index.?,676 // The section header index of the associated string table.
703 .sh_info = @intCast(u32, self.local_symbols.items.len),677 .sh_link = self.shstrtab_index.?,
704 .sh_addralign = min_align,678 .sh_info = @intCast(u32, self.local_symbols.items.len),
705 .sh_entsize = each_size,679 .sh_addralign = min_align,
680 .sh_entsize = each_size,
681 },
682 .phdr_index = undefined,
706 });683 });
707 self.shdr_table_dirty = true;684 self.shdr_table_dirty = true;
708 try self.writeSymbol(0);685 try self.writeSymbol(0);
...@@ -710,27 +687,30 @@ pub fn populateMissingMetadata(self: *Elf) !void {...@@ -710,27 +687,30 @@ pub fn populateMissingMetadata(self: *Elf) !void {
710687
711 if (self.dwarf) |*dw| {688 if (self.dwarf) |*dw| {
712 if (self.debug_str_section_index == null) {689 if (self.debug_str_section_index == null) {
713 self.debug_str_section_index = @intCast(u16, self.sections.items.len);690 self.debug_str_section_index = @intCast(u16, self.sections.slice().len);
714 assert(dw.strtab.items.len == 0);691 assert(dw.strtab.buffer.items.len == 0);
715 try dw.strtab.append(self.base.allocator, 0);692 try dw.strtab.buffer.append(gpa, 0);
716 try self.sections.append(self.base.allocator, .{693 try self.sections.append(gpa, .{
717 .sh_name = try self.makeString(".debug_str"),694 .shdr = .{
718 .sh_type = elf.SHT_PROGBITS,695 .sh_name = try self.shstrtab.insert(gpa, ".debug_str"),
719 .sh_flags = elf.SHF_MERGE | elf.SHF_STRINGS,696 .sh_type = elf.SHT_PROGBITS,
720 .sh_addr = 0,697 .sh_flags = elf.SHF_MERGE | elf.SHF_STRINGS,
721 .sh_offset = 0,698 .sh_addr = 0,
722 .sh_size = 0,699 .sh_offset = 0,
723 .sh_link = 0,700 .sh_size = 0,
724 .sh_info = 0,701 .sh_link = 0,
725 .sh_addralign = 1,702 .sh_info = 0,
726 .sh_entsize = 1,703 .sh_addralign = 1,
704 .sh_entsize = 1,
705 },
706 .phdr_index = undefined,
727 });707 });
728 self.debug_strtab_dirty = true;708 self.debug_strtab_dirty = true;
729 self.shdr_table_dirty = true;709 self.shdr_table_dirty = true;
730 }710 }
731711
732 if (self.debug_info_section_index == null) {712 if (self.debug_info_section_index == null) {
733 self.debug_info_section_index = @intCast(u16, self.sections.items.len);713 self.debug_info_section_index = @intCast(u16, self.sections.slice().len);
734714
735 const file_size_hint = 200;715 const file_size_hint = 200;
736 const p_align = 1;716 const p_align = 1;
...@@ -739,24 +719,27 @@ pub fn populateMissingMetadata(self: *Elf) !void {...@@ -739,24 +719,27 @@ pub fn populateMissingMetadata(self: *Elf) !void {
739 off,719 off,
740 off + file_size_hint,720 off + file_size_hint,
741 });721 });
742 try self.sections.append(self.base.allocator, .{722 try self.sections.append(gpa, .{
743 .sh_name = try self.makeString(".debug_info"),723 .shdr = .{
744 .sh_type = elf.SHT_PROGBITS,724 .sh_name = try self.shstrtab.insert(gpa, ".debug_info"),
745 .sh_flags = 0,725 .sh_type = elf.SHT_PROGBITS,
746 .sh_addr = 0,726 .sh_flags = 0,
747 .sh_offset = off,727 .sh_addr = 0,
748 .sh_size = file_size_hint,728 .sh_offset = off,
749 .sh_link = 0,729 .sh_size = file_size_hint,
750 .sh_info = 0,730 .sh_link = 0,
751 .sh_addralign = p_align,731 .sh_info = 0,
752 .sh_entsize = 0,732 .sh_addralign = p_align,
733 .sh_entsize = 0,
734 },
735 .phdr_index = undefined,
753 });736 });
754 self.shdr_table_dirty = true;737 self.shdr_table_dirty = true;
755 self.debug_info_header_dirty = true;738 self.debug_info_header_dirty = true;
756 }739 }
757740
758 if (self.debug_abbrev_section_index == null) {741 if (self.debug_abbrev_section_index == null) {
759 self.debug_abbrev_section_index = @intCast(u16, self.sections.items.len);742 self.debug_abbrev_section_index = @intCast(u16, self.sections.slice().len);
760743
761 const file_size_hint = 128;744 const file_size_hint = 128;
762 const p_align = 1;745 const p_align = 1;
...@@ -765,24 +748,27 @@ pub fn populateMissingMetadata(self: *Elf) !void {...@@ -765,24 +748,27 @@ pub fn populateMissingMetadata(self: *Elf) !void {
765 off,748 off,
766 off + file_size_hint,749 off + file_size_hint,
767 });750 });
768 try self.sections.append(self.base.allocator, .{751 try self.sections.append(gpa, .{
769 .sh_name = try self.makeString(".debug_abbrev"),752 .shdr = .{
770 .sh_type = elf.SHT_PROGBITS,753 .sh_name = try self.shstrtab.insert(gpa, ".debug_abbrev"),
771 .sh_flags = 0,754 .sh_type = elf.SHT_PROGBITS,
772 .sh_addr = 0,755 .sh_flags = 0,
773 .sh_offset = off,756 .sh_addr = 0,
774 .sh_size = file_size_hint,757 .sh_offset = off,
775 .sh_link = 0,758 .sh_size = file_size_hint,
776 .sh_info = 0,759 .sh_link = 0,
777 .sh_addralign = p_align,760 .sh_info = 0,
778 .sh_entsize = 0,761 .sh_addralign = p_align,
762 .sh_entsize = 0,
763 },
764 .phdr_index = undefined,
779 });765 });
780 self.shdr_table_dirty = true;766 self.shdr_table_dirty = true;
781 self.debug_abbrev_section_dirty = true;767 self.debug_abbrev_section_dirty = true;
782 }768 }
783769
784 if (self.debug_aranges_section_index == null) {770 if (self.debug_aranges_section_index == null) {
785 self.debug_aranges_section_index = @intCast(u16, self.sections.items.len);771 self.debug_aranges_section_index = @intCast(u16, self.sections.slice().len);
786772
787 const file_size_hint = 160;773 const file_size_hint = 160;
788 const p_align = 16;774 const p_align = 16;
...@@ -791,24 +777,27 @@ pub fn populateMissingMetadata(self: *Elf) !void {...@@ -791,24 +777,27 @@ pub fn populateMissingMetadata(self: *Elf) !void {
791 off,777 off,
792 off + file_size_hint,778 off + file_size_hint,
793 });779 });
794 try self.sections.append(self.base.allocator, .{780 try self.sections.append(gpa, .{
795 .sh_name = try self.makeString(".debug_aranges"),781 .shdr = .{
796 .sh_type = elf.SHT_PROGBITS,782 .sh_name = try self.shstrtab.insert(gpa, ".debug_aranges"),
797 .sh_flags = 0,783 .sh_type = elf.SHT_PROGBITS,
798 .sh_addr = 0,784 .sh_flags = 0,
799 .sh_offset = off,785 .sh_addr = 0,
800 .sh_size = file_size_hint,786 .sh_offset = off,
801 .sh_link = 0,787 .sh_size = file_size_hint,
802 .sh_info = 0,788 .sh_link = 0,
803 .sh_addralign = p_align,789 .sh_info = 0,
804 .sh_entsize = 0,790 .sh_addralign = p_align,
791 .sh_entsize = 0,
792 },
793 .phdr_index = undefined,
805 });794 });
806 self.shdr_table_dirty = true;795 self.shdr_table_dirty = true;
807 self.debug_aranges_section_dirty = true;796 self.debug_aranges_section_dirty = true;
808 }797 }
809798
810 if (self.debug_line_section_index == null) {799 if (self.debug_line_section_index == null) {
811 self.debug_line_section_index = @intCast(u16, self.sections.items.len);800 self.debug_line_section_index = @intCast(u16, self.sections.slice().len);
812801
813 const file_size_hint = 250;802 const file_size_hint = 250;
814 const p_align = 1;803 const p_align = 1;
...@@ -817,17 +806,20 @@ pub fn populateMissingMetadata(self: *Elf) !void {...@@ -817,17 +806,20 @@ pub fn populateMissingMetadata(self: *Elf) !void {
817 off,806 off,
818 off + file_size_hint,807 off + file_size_hint,
819 });808 });
820 try self.sections.append(self.base.allocator, .{809 try self.sections.append(gpa, .{
821 .sh_name = try self.makeString(".debug_line"),810 .shdr = .{
822 .sh_type = elf.SHT_PROGBITS,811 .sh_name = try self.shstrtab.insert(gpa, ".debug_line"),
823 .sh_flags = 0,812 .sh_type = elf.SHT_PROGBITS,
824 .sh_addr = 0,813 .sh_flags = 0,
825 .sh_offset = off,814 .sh_addr = 0,
826 .sh_size = file_size_hint,815 .sh_offset = off,
827 .sh_link = 0,816 .sh_size = file_size_hint,
828 .sh_info = 0,817 .sh_link = 0,
829 .sh_addralign = p_align,818 .sh_info = 0,
830 .sh_entsize = 0,819 .sh_addralign = p_align,
820 .sh_entsize = 0,
821 },
822 .phdr_index = undefined,
831 });823 });
832 self.shdr_table_dirty = true;824 self.shdr_table_dirty = true;
833 self.debug_line_header_dirty = true;825 self.debug_line_header_dirty = true;
...@@ -843,7 +835,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {...@@ -843,7 +835,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {
843 .p64 => @alignOf(elf.Elf64_Shdr),835 .p64 => @alignOf(elf.Elf64_Shdr),
844 };836 };
845 if (self.shdr_table_offset == null) {837 if (self.shdr_table_offset == null) {
846 self.shdr_table_offset = self.findFreeSpace(self.sections.items.len * shsize, shalign);838 self.shdr_table_offset = self.findFreeSpace(self.sections.slice().len * shsize, shalign);
847 self.shdr_table_dirty = true;839 self.shdr_table_dirty = true;
848 }840 }
849841
...@@ -874,7 +866,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {...@@ -874,7 +866,7 @@ pub fn populateMissingMetadata(self: *Elf) !void {
874 // offset + it's filesize.866 // offset + it's filesize.
875 var max_file_offset: u64 = 0;867 var max_file_offset: u64 = 0;
876868
877 for (self.sections.items) |shdr| {869 for (self.sections.items(.shdr)) |shdr| {
878 if (shdr.sh_offset + shdr.sh_size > max_file_offset) {870 if (shdr.sh_offset + shdr.sh_size > max_file_offset) {
879 max_file_offset = shdr.sh_offset + shdr.sh_size;871 max_file_offset = shdr.sh_offset + shdr.sh_size;
880 }872 }
...@@ -884,15 +876,18 @@ pub fn populateMissingMetadata(self: *Elf) !void {...@@ -884,15 +876,18 @@ pub fn populateMissingMetadata(self: *Elf) !void {
884 }876 }
885}877}
886878
887fn growAllocSection(self: *Elf, shdr_index: u16, phdr_index: u16, needed_size: u64) !void {879fn growAllocSection(self: *Elf, shdr_index: u16, needed_size: u64) !void {
888 // TODO Also detect virtual address collisions.880 // TODO Also detect virtual address collisions.
889 const shdr = &self.sections.items[shdr_index];881 const shdr = &self.sections.items(.shdr)[shdr_index];
882 const phdr_index = self.sections.items(.phdr_index)[shdr_index];
890 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];
891885
892 if (needed_size > self.allocatedSize(shdr.sh_offset)) {886 if (needed_size > self.allocatedSize(shdr.sh_offset)) {
893 // Must move the entire section.887 // Must move the entire section.
894 const new_offset = self.findFreeSpace(needed_size, self.page_size);888 const new_offset = self.findFreeSpace(needed_size, self.page_size);
895 const existing_size = if (self.atoms.get(phdr_index)) |last| blk: {889 const existing_size = if (maybe_last_atom_index) |last_atom_index| blk: {
890 const last = self.getAtom(last_atom_index);
896 const sym = last.getSymbol(self);891 const sym = last.getSymbol(self);
897 break :blk (sym.st_value + sym.st_size) - phdr.p_vaddr;892 break :blk (sym.st_value + sym.st_size) - phdr.p_vaddr;
898 } else if (shdr_index == self.got_section_index.?) blk: {893 } else if (shdr_index == self.got_section_index.?) blk: {
...@@ -900,8 +895,8 @@ fn growAllocSection(self: *Elf, shdr_index: u16, phdr_index: u16, needed_size: u...@@ -900,8 +895,8 @@ fn growAllocSection(self: *Elf, shdr_index: u16, phdr_index: u16, needed_size: u
900 } else 0;895 } else 0;
901 shdr.sh_size = 0;896 shdr.sh_size = 0;
902897
903 log.debug("new '{s}' file offset 0x{x} to 0x{x}", .{898 log.debug("new '{?s}' file offset 0x{x} to 0x{x}", .{
904 self.getString(shdr.sh_name),899 self.shstrtab.get(shdr.sh_name),
905 new_offset,900 new_offset,
906 new_offset + existing_size,901 new_offset + existing_size,
907 });902 });
...@@ -927,7 +922,7 @@ pub fn growNonAllocSection(...@@ -927,7 +922,7 @@ pub fn growNonAllocSection(
927 min_alignment: u32,922 min_alignment: u32,
928 requires_file_copy: bool,923 requires_file_copy: bool,
929) !void {924) !void {
930 const shdr = &self.sections.items[shdr_index];925 const shdr = &self.sections.items(.shdr)[shdr_index];
931926
932 if (needed_size > self.allocatedSize(shdr.sh_offset)) {927 if (needed_size > self.allocatedSize(shdr.sh_offset)) {
933 const existing_size = if (self.symtab_section_index.? == shdr_index) blk: {928 const existing_size = if (self.symtab_section_index.? == shdr_index) blk: {
...@@ -940,7 +935,7 @@ pub fn growNonAllocSection(...@@ -940,7 +935,7 @@ pub fn growNonAllocSection(
940 shdr.sh_size = 0;935 shdr.sh_size = 0;
941 // Move all the symbols to a new file location.936 // Move all the symbols to a new file location.
942 const new_offset = self.findFreeSpace(needed_size, min_alignment);937 const new_offset = self.findFreeSpace(needed_size, min_alignment);
943 log.debug("moving '{s}' from 0x{x} to 0x{x}", .{ self.getString(shdr.sh_name), shdr.sh_offset, new_offset });938 log.debug("moving '{?s}' from 0x{x} to 0x{x}", .{ self.shstrtab.get(shdr.sh_name), shdr.sh_offset, new_offset });
944939
945 if (requires_file_copy) {940 if (requires_file_copy) {
946 const amt = try self.base.file.?.copyRangeAll(941 const amt = try self.base.file.?.copyRangeAll(
...@@ -1011,6 +1006,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node...@@ -1011,6 +1006,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
1011 }1006 }
1012 }1007 }
10131008
1009 const gpa = self.base.allocator;
1014 var sub_prog_node = prog_node.start("ELF Flush", 0);1010 var sub_prog_node = prog_node.start("ELF Flush", 0);
1015 sub_prog_node.activate();1011 sub_prog_node.activate();
1016 defer sub_prog_node.end();1012 defer sub_prog_node.end();
...@@ -1029,12 +1025,13 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node...@@ -1029,12 +1025,13 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
1029 {1025 {
1030 var it = self.relocs.iterator();1026 var it = self.relocs.iterator();
1031 while (it.next()) |entry| {1027 while (it.next()) |entry| {
1032 const atom = entry.key_ptr.*;1028 const atom_index = entry.key_ptr.*;
1033 const relocs = entry.value_ptr.*;1029 const relocs = entry.value_ptr.*;
1030 const atom = self.getAtom(atom_index);
1034 const source_sym = atom.getSymbol(self);1031 const source_sym = atom.getSymbol(self);
1035 const source_shdr = self.sections.items[source_sym.st_shndx];1032 const source_shdr = self.sections.items(.shdr)[source_sym.st_shndx];
10361033
1037 log.debug("relocating '{s}'", .{self.getString(source_sym.st_name)});1034 log.debug("relocating '{?s}'", .{self.shstrtab.get(source_sym.st_name)});
10381035
1039 for (relocs.items) |*reloc| {1036 for (relocs.items) |*reloc| {
1040 const target_sym = self.local_symbols.items[reloc.target];1037 const target_sym = self.local_symbols.items[reloc.target];
...@@ -1045,10 +1042,10 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node...@@ -1045,10 +1042,10 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
1045 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;
1046 const file_offset = source_shdr.sh_offset + section_offset;1043 const file_offset = source_shdr.sh_offset + section_offset;
10471044
1048 log.debug(" ({x}: [() => 0x{x}] ({s}))", .{1045 log.debug(" ({x}: [() => 0x{x}] ({?s}))", .{
1049 reloc.offset,1046 reloc.offset,
1050 target_vaddr,1047 target_vaddr,
1051 self.getString(target_sym.st_name),1048 self.shstrtab.get(target_sym.st_name),
1052 });1049 });
10531050
1054 switch (self.ptr_width) {1051 switch (self.ptr_width) {
...@@ -1126,8 +1123,8 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node...@@ -1126,8 +1123,8 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
11261123
1127 switch (self.ptr_width) {1124 switch (self.ptr_width) {
1128 .p32 => {1125 .p32 => {
1129 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);
1130 defer self.base.allocator.free(buf);1127 defer gpa.free(buf);
11311128
1132 for (buf) |*phdr, i| {1129 for (buf) |*phdr, i| {
1133 phdr.* = progHeaderTo32(self.program_headers.items[i]);1130 phdr.* = progHeaderTo32(self.program_headers.items[i]);
...@@ -1138,8 +1135,8 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node...@@ -1138,8 +1135,8 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
1138 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.?);
1139 },1136 },
1140 .p64 => {1137 .p64 => {
1141 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);
1142 defer self.base.allocator.free(buf);1139 defer gpa.free(buf);
11431140
1144 for (buf) |*phdr, i| {1141 for (buf) |*phdr, i| {
1145 phdr.* = self.program_headers.items[i];1142 phdr.* = self.program_headers.items[i];
...@@ -1155,20 +1152,20 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node...@@ -1155,20 +1152,20 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
11551152
1156 {1153 {
1157 const shdr_index = self.shstrtab_index.?;1154 const shdr_index = self.shstrtab_index.?;
1158 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) {
1159 try self.growNonAllocSection(shdr_index, self.shstrtab.items.len, 1, false);1156 try self.growNonAllocSection(shdr_index, self.shstrtab.buffer.items.len, 1, false);
1160 const shstrtab_sect = self.sections.items[shdr_index];1157 const shstrtab_sect = self.sections.items(.shdr)[shdr_index];
1161 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);
1162 self.shstrtab_dirty = false;1159 self.shstrtab_dirty = false;
1163 }1160 }
1164 }1161 }
11651162
1166 if (self.dwarf) |dwarf| {1163 if (self.dwarf) |dwarf| {
1167 const shdr_index = self.debug_str_section_index.?;1164 const shdr_index = self.debug_str_section_index.?;
1168 if (self.debug_strtab_dirty or dwarf.strtab.items.len != self.sections.items[shdr_index].sh_size) {1165 if (self.debug_strtab_dirty or dwarf.strtab.buffer.items.len != self.sections.items(.shdr)[shdr_index].sh_size) {
1169 try self.growNonAllocSection(shdr_index, dwarf.strtab.items.len, 1, false);1166 try self.growNonAllocSection(shdr_index, dwarf.strtab.buffer.items.len, 1, false);
1170 const debug_strtab_sect = self.sections.items[shdr_index];1167 const debug_strtab_sect = self.sections.items(.shdr)[shdr_index];
1171 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);
1172 self.debug_strtab_dirty = false;1169 self.debug_strtab_dirty = false;
1173 }1170 }
1174 }1171 }
...@@ -1183,7 +1180,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node...@@ -1183,7 +1180,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
1183 .p64 => @alignOf(elf.Elf64_Shdr),1180 .p64 => @alignOf(elf.Elf64_Shdr),
1184 };1181 };
1185 const allocated_size = self.allocatedSize(self.shdr_table_offset.?);1182 const allocated_size = self.allocatedSize(self.shdr_table_offset.?);
1186 const needed_size = self.sections.items.len * shsize;1183 const needed_size = self.sections.slice().len * shsize;
11871184
1188 if (needed_size > allocated_size) {1185 if (needed_size > allocated_size) {
1189 self.shdr_table_offset = null; // free the space1186 self.shdr_table_offset = null; // free the space
...@@ -1192,12 +1189,13 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node...@@ -1192,12 +1189,13 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
11921189
1193 switch (self.ptr_width) {1190 switch (self.ptr_width) {
1194 .p32 => {1191 .p32 => {
1195 const buf = try self.base.allocator.alloc(elf.Elf32_Shdr, self.sections.items.len);1192 const slice = self.sections.slice();
1196 defer self.base.allocator.free(buf);1193 const buf = try gpa.alloc(elf.Elf32_Shdr, slice.len);
1194 defer gpa.free(buf);
11971195
1198 for (buf) |*shdr, i| {1196 for (buf) |*shdr, i| {
1199 shdr.* = sectHeaderTo32(self.sections.items[i]);1197 shdr.* = sectHeaderTo32(slice.items(.shdr)[i]);
1200 log.debug("writing section {s}: {}", .{ self.getString(shdr.sh_name), shdr.* });1198 log.debug("writing section {?s}: {}", .{ self.shstrtab.get(shdr.sh_name), shdr.* });
1201 if (foreign_endian) {1199 if (foreign_endian) {
1202 mem.byteSwapAllFields(elf.Elf32_Shdr, shdr);1200 mem.byteSwapAllFields(elf.Elf32_Shdr, shdr);
1203 }1201 }
...@@ -1205,12 +1203,13 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node...@@ -1205,12 +1203,13 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
1205 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.?);
1206 },1204 },
1207 .p64 => {1205 .p64 => {
1208 const buf = try self.base.allocator.alloc(elf.Elf64_Shdr, self.sections.items.len);1206 const slice = self.sections.slice();
1209 defer self.base.allocator.free(buf);1207 const buf = try gpa.alloc(elf.Elf64_Shdr, slice.len);
1208 defer gpa.free(buf);
12101209
1211 for (buf) |*shdr, i| {1210 for (buf) |*shdr, i| {
1212 shdr.* = self.sections.items[i];1211 shdr.* = slice.items(.shdr)[i];
1213 log.debug("writing section {s}: {}", .{ self.getString(shdr.sh_name), shdr.* });1212 log.debug("writing section {?s}: {}", .{ self.shstrtab.get(shdr.sh_name), shdr.* });
1214 if (foreign_endian) {1213 if (foreign_endian) {
1215 mem.byteSwapAllFields(elf.Elf64_Shdr, shdr);1214 mem.byteSwapAllFields(elf.Elf64_Shdr, shdr);
1216 }1215 }
...@@ -2021,7 +2020,7 @@ fn writeElfHeader(self: *Elf) !void {...@@ -2021,7 +2020,7 @@ fn writeElfHeader(self: *Elf) !void {
2021 mem.writeInt(u16, hdr_buf[index..][0..2], e_shentsize, endian);2020 mem.writeInt(u16, hdr_buf[index..][0..2], e_shentsize, endian);
2022 index += 2;2021 index += 2;
20232022
2024 const e_shnum = @intCast(u16, self.sections.items.len);2023 const e_shnum = @intCast(u16, self.sections.slice().len);
2025 mem.writeInt(u16, hdr_buf[index..][0..2], e_shnum, endian);2024 mem.writeInt(u16, hdr_buf[index..][0..2], e_shnum, endian);
2026 index += 2;2025 index += 2;
20272026
...@@ -2033,124 +2032,145 @@ fn writeElfHeader(self: *Elf) !void {...@@ -2033,124 +2032,145 @@ fn writeElfHeader(self: *Elf) !void {
2033 try self.base.file.?.pwriteAll(hdr_buf[0..index], 0);2032 try self.base.file.?.pwriteAll(hdr_buf[0..index], 0);
2034}2033}
20352034
2036fn freeTextBlock(self: *Elf, text_block: *TextBlock, phdr_index: u16) void {2035fn freeAtom(self: *Elf, atom_index: Atom.Index) void {
2037 const local_sym = text_block.getSymbol(self);2036 const atom = self.getAtom(atom_index);
2038 const name_str_index = local_sym.st_name;2037 log.debug("freeAtom {d} ({s})", .{ atom_index, atom.getName(self) });
2039 const name = self.getString(name_str_index);
2040 log.debug("freeTextBlock {*} ({s})", .{ text_block, name });
20412038
2042 self.freeRelocationsForTextBlock(text_block);2039 Atom.freeRelocations(self, atom_index);
20432040
2044 const free_list = self.atom_free_lists.getPtr(phdr_index).?;2041 const gpa = self.base.allocator;
2042 const shndx = atom.getSymbol(self).st_shndx;
2043 const free_list = &self.sections.items(.free_list)[shndx];
2045 var already_have_free_list_node = false;2044 var already_have_free_list_node = false;
2046 {2045 {
2047 var i: usize = 0;2046 var i: usize = 0;
2048 // TODO turn free_list into a hash map2047 // TODO turn free_list into a hash map
2049 while (i < free_list.items.len) {2048 while (i < free_list.items.len) {
2050 if (free_list.items[i] == text_block) {2049 if (free_list.items[i] == atom_index) {
2051 _ = free_list.swapRemove(i);2050 _ = free_list.swapRemove(i);
2052 continue;2051 continue;
2053 }2052 }
2054 if (free_list.items[i] == text_block.prev) {2053 if (free_list.items[i] == atom.prev_index) {
2055 already_have_free_list_node = true;2054 already_have_free_list_node = true;
2056 }2055 }
2057 i += 1;2056 i += 1;
2058 }2057 }
2059 }2058 }
20602059
2061 if (self.atoms.getPtr(phdr_index)) |last_block| {2060 const maybe_last_atom_index = &self.sections.items(.last_atom_index)[shndx];
2062 if (last_block.* == text_block) {2061 if (maybe_last_atom_index.*) |last_atom_index| {
2063 if (text_block.prev) |prev| {2062 if (last_atom_index == atom_index) {
2063 if (atom.prev_index) |prev_index| {
2064 // TODO shrink the section size here2064 // TODO shrink the section size here
2065 last_block.* = prev;2065 maybe_last_atom_index.* = prev_index;
2066 } else {2066 } else {
2067 _ = self.atoms.fetchRemove(phdr_index);2067 maybe_last_atom_index.* = null;
2068 }2068 }
2069 }2069 }
2070 }2070 }
20712071
2072 if (text_block.prev) |prev| {2072 if (atom.prev_index) |prev_index| {
2073 prev.next = text_block.next;2073 const prev = self.getAtomPtr(prev_index);
2074 prev.next_index = atom.next_index;
20742075
2075 if (!already_have_free_list_node and prev.freeListEligible(self)) {2076 if (!already_have_free_list_node and prev.*.freeListEligible(self)) {
2076 // 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
2077 // ignore the OOM here.2078 // ignore the OOM here.
2078 free_list.append(self.base.allocator, prev) catch {};2079 free_list.append(gpa, prev_index) catch {};
2079 }2080 }
2080 } else {2081 } else {
2081 text_block.prev = null;2082 self.getAtomPtr(atom_index).prev_index = null;
2082 }2083 }
20832084
2084 if (text_block.next) |next| {2085 if (atom.next_index) |next_index| {
2085 next.prev = text_block.prev;2086 self.getAtomPtr(next_index).prev_index = atom.prev_index;
2086 } else {2087 } else {
2087 text_block.next = null;2088 self.getAtomPtr(atom_index).next_index = null;
2088 }2089 }
20892090
2090 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.2091 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
2091 const local_sym_index = text_block.getSymbolIndex().?;2092 const local_sym_index = atom.getSymbolIndex().?;
2092 self.local_symbol_free_list.append(self.base.allocator, local_sym_index) catch {};2093
2094 self.local_symbol_free_list.append(gpa, local_sym_index) catch {};
2093 self.local_symbols.items[local_sym_index].st_info = 0;2095 self.local_symbols.items[local_sym_index].st_info = 0;
2096 self.local_symbols.items[local_sym_index].st_shndx = 0;
2094 _ = self.atom_by_index_table.remove(local_sym_index);2097 _ = self.atom_by_index_table.remove(local_sym_index);
2095 text_block.local_sym_index = 0;2098 self.getAtomPtr(atom_index).local_sym_index = 0;
20962099
2097 self.offset_table_free_list.append(self.base.allocator, text_block.offset_table_index) catch {};2100 self.offset_table_free_list.append(self.base.allocator, atom.offset_table_index) catch {};
2098
2099 if (self.dwarf) |*dw| {
2100 dw.freeAtom(&text_block.dbg_info_atom);
2101 }
2102}2101}
21032102
2104fn shrinkTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, phdr_index: u16) void {2103fn shrinkAtom(self: *Elf, atom_index: Atom.Index, new_block_size: u64) void {
2105 _ = self;2104 _ = self;
2106 _ = text_block;2105 _ = atom_index;
2107 _ = new_block_size;2106 _ = new_block_size;
2108 _ = phdr_index;
2109}2107}
21102108
2111fn 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 {
2112 const sym = text_block.getSymbol(self);2110 const atom = self.getAtom(atom_index);
2111 const sym = atom.getSymbol(self);
2113 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;
2114 const need_realloc = !align_ok or new_block_size > text_block.capacity(self);2113 const need_realloc = !align_ok or new_block_size > atom.capacity(self);
2115 if (!need_realloc) return sym.st_value;2114 if (!need_realloc) return sym.st_value;
2116 return self.allocateTextBlock(text_block, new_block_size, alignment, phdr_index);2115 return self.allocateAtom(atom_index, new_block_size, alignment);
2116}
2117
2118pub fn createAtom(self: *Elf) !Atom.Index {
2119 const gpa = self.base.allocator;
2120 const atom_index = @intCast(Atom.Index, self.atoms.items.len);
2121 const atom = try self.atoms.addOne(gpa);
2122 const local_sym_index = try self.allocateLocalSymbol();
2123 const offset_table_index = try self.allocateGotOffset();
2124 try self.atom_by_index_table.putNoClobber(gpa, local_sym_index, atom_index);
2125 atom.* = .{
2126 .local_sym_index = local_sym_index,
2127 .offset_table_index = offset_table_index,
2128 .prev_index = null,
2129 .next_index = null,
2130 };
2131 log.debug("creating ATOM(%{d}) at index {d}", .{ local_sym_index, atom_index });
2132 return atom_index;
2117}2133}
21182134
2119fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, alignment: u64, phdr_index: u16) !u64 {2135fn allocateAtom(self: *Elf, atom_index: Atom.Index, new_block_size: u64, alignment: u64) !u64 {
2120 const shdr_index = self.phdr_shdr_table.get(phdr_index).?;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];
2121 const phdr = &self.program_headers.items[phdr_index];2139 const phdr = &self.program_headers.items[phdr_index];
2122 const shdr = &self.sections.items[shdr_index];2140 const shdr = &self.sections.items(.shdr)[sym.st_shndx];
2123 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);
21242144
2125 // We use these to indicate our intention to update metadata, placing the new block,2145 // We use these to indicate our intention to update metadata, placing the new atom,
2126 // and possibly removing a free list node.2146 // and possibly removing a free list node.
2127 // 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
2128 // 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
2129 // 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.
2130 var block_placement: ?*TextBlock = null;2150 var atom_placement: ?Atom.Index = null;
2131 var free_list_removal: ?usize = null;2151 var free_list_removal: ?usize = null;
2132 var free_list = self.atom_free_lists.get(phdr_index).?;
21332152
2134 // First we look for an appropriately sized free list node.2153 // First we look for an appropriately sized free list node.
2135 // 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.
2136 const vaddr = blk: {2155 const vaddr = blk: {
2137 var i: usize = 0;2156 var i: usize = 0;
2138 while (i < free_list.items.len) {2157 while (i < free_list.items.len) {
2139 const big_block = free_list.items[i];2158 const big_atom_index = free_list.items[i];
2140 // We now have a pointer to a live text block that has too much capacity.2159 const big_atom = self.getAtom(big_atom_index);
2141 // 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.
2142 const sym = big_block.getSymbol(self);2161 // Is it enough that we could fit this new atom?
2143 const capacity = big_block.capacity(self);2162 const big_atom_sym = big_atom.getSymbol(self);
2163 const capacity = big_atom.capacity(self);
2144 const ideal_capacity = padToIdeal(capacity);2164 const ideal_capacity = padToIdeal(capacity);
2145 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;
2146 const capacity_end_vaddr = sym.st_value + capacity;2166 const capacity_end_vaddr = big_atom_sym.st_value + capacity;
2147 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;
2148 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);
2149 if (new_start_vaddr < ideal_capacity_end_vaddr) {2169 if (new_start_vaddr < ideal_capacity_end_vaddr) {
2150 // Additional bookkeeping here to notice if this free list node2170 // Additional bookkeeping here to notice if this free list node
2151 // 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
2152 // more of the extra capacity.2172 // more of the extra capacity.
2153 if (!big_block.freeListEligible(self)) {2173 if (!big_atom.freeListEligible(self)) {
2154 _ = free_list.swapRemove(i);2174 _ = free_list.swapRemove(i);
2155 } else {2175 } else {
2156 i += 1;2176 i += 1;
...@@ -2164,29 +2184,33 @@ fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, al...@@ -2164,29 +2184,33 @@ fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, al
2164 const keep_free_list_node = remaining_capacity >= min_text_capacity;2184 const keep_free_list_node = remaining_capacity >= min_text_capacity;
21652185
2166 // 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.
2167 block_placement = big_block;2187 atom_placement = big_atom_index;
2168 if (!keep_free_list_node) {2188 if (!keep_free_list_node) {
2169 free_list_removal = i;2189 free_list_removal = i;
2170 }2190 }
2171 break :blk new_start_vaddr;2191 break :blk new_start_vaddr;
2172 } else if (self.atoms.get(phdr_index)) |last| {2192 } else if (maybe_last_atom_index.*) |last_index| {
2173 const sym = last.getSymbol(self);2193 const last = self.getAtom(last_index);
2174 const ideal_capacity = padToIdeal(sym.st_size);2194 const last_sym = last.getSymbol(self);
2175 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;
2176 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);
2177 // 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.
2178 block_placement = last;2199 atom_placement = last_index;
2179 break :blk new_start_vaddr;2200 break :blk new_start_vaddr;
2180 } else {2201 } else {
2181 break :blk phdr.p_vaddr;2202 break :blk phdr.p_vaddr;
2182 }2203 }
2183 };2204 };
21842205
2185 const expand_text_section = block_placement == null or block_placement.?.next == null;2206 const expand_section = if (atom_placement) |placement_index|
2186 if (expand_text_section) {2207 self.getAtom(placement_index).next_index == null
2208 else
2209 true;
2210 if (expand_section) {
2187 const needed_size = (vaddr + new_block_size) - phdr.p_vaddr;2211 const needed_size = (vaddr + new_block_size) - phdr.p_vaddr;
2188 try self.growAllocSection(shdr_index, phdr_index, needed_size);2212 try self.growAllocSection(sym.st_shndx, needed_size);
2189 _ = try self.atoms.put(self.base.allocator, phdr_index, text_block);2213 maybe_last_atom_index.* = atom_index;
21902214
2191 if (self.dwarf) |_| {2215 if (self.dwarf) |_| {
2192 // 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
...@@ -2201,23 +2225,28 @@ fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, al...@@ -2201,23 +2225,28 @@ fn allocateTextBlock(self: *Elf, text_block: *TextBlock, new_block_size: u64, al
2201 }2225 }
2202 shdr.sh_addralign = math.max(shdr.sh_addralign, alignment);2226 shdr.sh_addralign = math.max(shdr.sh_addralign, alignment);
22032227
2204 // This function can also reallocate a text block.2228 // This function can also reallocate an atom.
2205 // 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
2206 // plugging it in to its new location.2230 // plugging it in to its new location.
2207 if (text_block.prev) |prev| {2231 if (atom.prev_index) |prev_index| {
2208 prev.next = text_block.next;2232 const prev = self.getAtomPtr(prev_index);
2233 prev.next_index = atom.next_index;
2209 }2234 }
2210 if (text_block.next) |next| {2235 if (atom.next_index) |next_index| {
2211 next.prev = text_block.prev;2236 const next = self.getAtomPtr(next_index);
2237 next.prev_index = atom.prev_index;
2212 }2238 }
22132239
2214 if (block_placement) |big_block| {2240 if (atom_placement) |big_atom_index| {
2215 text_block.prev = big_block;2241 const big_atom = self.getAtomPtr(big_atom_index);
2216 text_block.next = big_block.next;2242 const atom_ptr = self.getAtomPtr(atom_index);
2217 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;
2218 } else {2246 } else {
2219 text_block.prev = null;2247 const atom_ptr = self.getAtomPtr(atom_index);
2220 text_block.next = null;2248 atom_ptr.prev_index = null;
2249 atom_ptr.next_index = null;
2221 }2250 }
2222 if (free_list_removal) |i| {2251 if (free_list_removal) |i| {
2223 _ = free_list.swapRemove(i);2252 _ = free_list.swapRemove(i);
...@@ -2272,15 +2301,10 @@ pub fn allocateGotOffset(self: *Elf) !u32 {...@@ -2272,15 +2301,10 @@ pub fn allocateGotOffset(self: *Elf) !u32 {
2272 return index;2301 return index;
2273}2302}
22742303
2275fn freeRelocationsForTextBlock(self: *Elf, text_block: *TextBlock) void {
2276 var removed_relocs = self.relocs.fetchRemove(text_block);
2277 if (removed_relocs) |*relocs| relocs.value.deinit(self.base.allocator);
2278}
2279
2280fn freeUnnamedConsts(self: *Elf, decl_index: Module.Decl.Index) void {2304fn freeUnnamedConsts(self: *Elf, decl_index: Module.Decl.Index) void {
2281 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;
2282 for (unnamed_consts.items) |atom| {2306 for (unnamed_consts.items) |atom| {
2283 self.freeTextBlock(atom, self.phdr_load_ro_index.?);2307 self.freeAtom(atom);
2284 }2308 }
2285 unnamed_consts.clearAndFree(self.base.allocator);2309 unnamed_consts.clearAndFree(self.base.allocator);
2286}2310}
...@@ -2295,43 +2319,57 @@ pub fn freeDecl(self: *Elf, decl_index: Module.Decl.Index) void {...@@ -2295,43 +2319,57 @@ pub fn freeDecl(self: *Elf, decl_index: Module.Decl.Index) void {
22952319
2296 log.debug("freeDecl {*}", .{decl});2320 log.debug("freeDecl {*}", .{decl});
22972321
2298 if (self.decls.fetchRemove(decl_index)) |kv| {2322 if (self.decls.fetchRemove(decl_index)) |const_kv| {
2299 if (kv.value) |index| {2323 var kv = const_kv;
2300 self.freeTextBlock(&decl.link.elf, index);2324 self.freeAtom(kv.value.atom);
2301 self.freeUnnamedConsts(decl_index);2325 self.freeUnnamedConsts(decl_index);
2302 }2326 kv.value.exports.deinit(self.base.allocator);
2303 }2327 }
23042328
2305 if (self.dwarf) |*dw| {2329 if (self.dwarf) |*dw| {
2306 dw.freeDecl(decl);2330 dw.freeDecl(decl_index);
2331 }
2332}
2333
2334pub fn getOrCreateAtomForDecl(self: *Elf, decl_index: Module.Decl.Index) !Atom.Index {
2335 const gop = try self.decls.getOrPut(self.base.allocator, decl_index);
2336 if (!gop.found_existing) {
2337 gop.value_ptr.* = .{
2338 .atom = try self.createAtom(),
2339 .shdr = self.getDeclShdrIndex(decl_index),
2340 .exports = .{},
2341 };
2307 }2342 }
2343 return gop.value_ptr.atom;
2308}2344}
23092345
2310fn getDeclPhdrIndex(self: *Elf, decl: *Module.Decl) !u16 {2346fn getDeclShdrIndex(self: *Elf, decl_index: Module.Decl.Index) u16 {
2347 const decl = self.base.options.module.?.declPtr(decl_index);
2311 const ty = decl.ty;2348 const ty = decl.ty;
2312 const zig_ty = ty.zigTypeTag();2349 const zig_ty = ty.zigTypeTag();
2313 const val = decl.val;2350 const val = decl.val;
2314 const phdr_index: u16 = blk: {2351 const shdr_index: u16 = blk: {
2315 if (val.isUndefDeep()) {2352 if (val.isUndefDeep()) {
2316 // 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
2317 break :blk self.phdr_load_rw_index.?;2354 break :blk self.data_section_index.?;
2318 }2355 }
23192356
2320 switch (zig_ty) {2357 switch (zig_ty) {
2321 // TODO: what if this is a function pointer?2358 // TODO: what if this is a function pointer?
2322 .Fn => break :blk self.phdr_load_re_index.?,2359 .Fn => break :blk self.text_section_index.?,
2323 else => {2360 else => {
2324 if (val.castTag(.variable)) |_| {2361 if (val.castTag(.variable)) |_| {
2325 break :blk self.phdr_load_rw_index.?;2362 break :blk self.data_section_index.?;
2326 }2363 }
2327 break :blk self.phdr_load_ro_index.?;2364 break :blk self.rodata_section_index.?;
2328 },2365 },
2329 }2366 }
2330 };2367 };
2331 return phdr_index;2368 return shdr_index;
2332}2369}
23332370
2334fn 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;
2335 const mod = self.base.options.module.?;2373 const mod = self.base.options.module.?;
2336 const decl = mod.declPtr(decl_index);2374 const decl = mod.declPtr(decl_index);
23372375
...@@ -2341,60 +2379,65 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s...@@ -2341,60 +2379,65 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s
2341 log.debug("updateDeclCode {s}{*}", .{ decl_name, decl });2379 log.debug("updateDeclCode {s}{*}", .{ decl_name, decl });
2342 const required_alignment = decl.getAlignment(self.base.options.target);2380 const required_alignment = decl.getAlignment(self.base.options.target);
23432381
2344 const decl_ptr = self.decls.getPtr(decl_index).?;2382 const decl_metadata = self.decls.get(decl_index).?;
2345 if (decl_ptr.* == null) {2383 const atom_index = decl_metadata.atom;
2346 decl_ptr.* = try self.getDeclPhdrIndex(decl);2384 const atom = self.getAtom(atom_index);
2347 }2385
2348 const phdr_index = decl_ptr.*.?;2386 const shdr_index = decl_metadata.shdr;
2349 const shdr_index = self.phdr_shdr_table.get(phdr_index).?;2387 if (atom.getSymbol(self).st_size != 0) {
2388 const local_sym = atom.getSymbolPtr(self);
2389 local_sym.st_name = try self.shstrtab.insert(gpa, decl_name);
2390 local_sym.st_info = (elf.STB_LOCAL << 4) | stt_bits;
2391 local_sym.st_other = 0;
2392 local_sym.st_shndx = shdr_index;
23502393
2351 const local_sym = decl.link.elf.getSymbolPtr(self);2394 const capacity = atom.capacity(self);
2352 if (local_sym.st_size != 0) {
2353 const capacity = decl.link.elf.capacity(self);
2354 const need_realloc = code.len > capacity or2395 const need_realloc = code.len > capacity or
2355 !mem.isAlignedGeneric(u64, local_sym.st_value, required_alignment);2396 !mem.isAlignedGeneric(u64, local_sym.st_value, required_alignment);
2397
2356 if (need_realloc) {2398 if (need_realloc) {
2357 const vaddr = try self.growTextBlock(&decl.link.elf, code.len, required_alignment, phdr_index);2399 const vaddr = try self.growAtom(atom_index, code.len, required_alignment);
2358 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 });
2359 if (vaddr != local_sym.st_value) {2401 if (vaddr != local_sym.st_value) {
2360 local_sym.st_value = vaddr;2402 local_sym.st_value = vaddr;
23612403
2362 log.debug(" (writing new offset table entry)", .{});2404 log.debug(" (writing new offset table entry)", .{});
2363 self.offset_table.items[decl.link.elf.offset_table_index] = vaddr;2405 self.offset_table.items[atom.offset_table_index] = vaddr;
2364 try self.writeOffsetTableEntry(decl.link.elf.offset_table_index);2406 try self.writeOffsetTableEntry(atom.offset_table_index);
2365 }2407 }
2366 } else if (code.len < local_sym.st_size) {2408 } else if (code.len < local_sym.st_size) {
2367 self.shrinkTextBlock(&decl.link.elf, code.len, phdr_index);2409 self.shrinkAtom(atom_index, code.len);
2368 }2410 }
2369 local_sym.st_size = code.len;2411 local_sym.st_size = code.len;
2370 local_sym.st_name = try self.updateString(local_sym.st_name, decl_name);2412
2371 local_sym.st_info = (elf.STB_LOCAL << 4) | stt_bits;
2372 local_sym.st_other = 0;
2373 local_sym.st_shndx = shdr_index;
2374 // 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.
2375 try self.writeSymbol(decl.link.elf.getSymbolIndex().?);2414 try self.writeSymbol(atom.getSymbolIndex().?);
2376 } else {2415 } else {
2377 const name_str_index = try self.makeString(decl_name);2416 const local_sym = atom.getSymbolPtr(self);
2378 const vaddr = try self.allocateTextBlock(&decl.link.elf, code.len, required_alignment, phdr_index);
2379 errdefer self.freeTextBlock(&decl.link.elf, phdr_index);
2380 log.debug("allocated text block for {s} at 0x{x}", .{ decl_name, vaddr });
2381
2382 local_sym.* = .{2417 local_sym.* = .{
2383 .st_name = name_str_index,2418 .st_name = try self.shstrtab.insert(gpa, decl_name),
2384 .st_info = (elf.STB_LOCAL << 4) | stt_bits,2419 .st_info = (elf.STB_LOCAL << 4) | stt_bits,
2385 .st_other = 0,2420 .st_other = 0,
2386 .st_shndx = shdr_index,2421 .st_shndx = shdr_index,
2387 .st_value = vaddr,2422 .st_value = 0,
2388 .st_size = code.len,2423 .st_size = 0,
2389 };2424 };
2390 self.offset_table.items[decl.link.elf.offset_table_index] = vaddr;2425 const vaddr = try self.allocateAtom(atom_index, code.len, required_alignment);
2426 errdefer self.freeAtom(atom_index);
2427 log.debug("allocated text block for {s} at 0x{x}", .{ decl_name, vaddr });
23912428
2392 try self.writeSymbol(decl.link.elf.getSymbolIndex().?);2429 self.offset_table.items[atom.offset_table_index] = vaddr;
2393 try self.writeOffsetTableEntry(decl.link.elf.offset_table_index);2430 local_sym.st_value = vaddr;
2431 local_sym.st_size = code.len;
2432
2433 try self.writeSymbol(atom.getSymbolIndex().?);
2434 try self.writeOffsetTableEntry(atom.offset_table_index);
2394 }2435 }
23952436
2437 const local_sym = atom.getSymbolPtr(self);
2438 const phdr_index = self.sections.items(.phdr_index)[shdr_index];
2396 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;
2397 const file_offset = self.sections.items[shdr_index].sh_offset + section_offset;2440 const file_offset = self.sections.items(.shdr)[shdr_index].sh_offset + section_offset;
2398 try self.base.file.?.pwriteAll(code, file_offset);2441 try self.base.file.?.pwriteAll(code, file_offset);
23992442
2400 return local_sym;2443 return local_sym;
...@@ -2413,15 +2456,10 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven...@@ -2413,15 +2456,10 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven
24132456
2414 const decl_index = func.owner_decl;2457 const decl_index = func.owner_decl;
2415 const decl = module.declPtr(decl_index);2458 const decl = module.declPtr(decl_index);
2416 const atom = &decl.link.elf;2459
2417 try atom.ensureInitialized(self);2460 const atom_index = try self.getOrCreateAtomForDecl(decl_index);
2418 const gop = try self.decls.getOrPut(self.base.allocator, decl_index);2461 self.freeUnnamedConsts(decl_index);
2419 if (gop.found_existing) {2462 Atom.freeRelocations(self, atom_index);
2420 self.freeUnnamedConsts(decl_index);
2421 self.freeRelocationsForTextBlock(atom);
2422 } else {
2423 gop.value_ptr.* = null;
2424 }
24252463
2426 var code_buffer = std.ArrayList(u8).init(self.base.allocator);2464 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
2427 defer code_buffer.deinit();2465 defer code_buffer.deinit();
...@@ -2483,16 +2521,9 @@ pub fn updateDecl(self: *Elf, module: *Module, decl_index: Module.Decl.Index) !v...@@ -2483,16 +2521,9 @@ pub fn updateDecl(self: *Elf, module: *Module, decl_index: Module.Decl.Index) !v
2483 }2521 }
2484 }2522 }
24852523
2486 assert(!self.unnamed_const_atoms.contains(decl_index));2524 const atom_index = try self.getOrCreateAtomForDecl(decl_index);
24872525 Atom.freeRelocations(self, atom_index);
2488 const atom = &decl.link.elf;2526 const atom = self.getAtom(atom_index);
2489 try atom.ensureInitialized(self);
2490 const gop = try self.decls.getOrPut(self.base.allocator, decl_index);
2491 if (gop.found_existing) {
2492 self.freeRelocationsForTextBlock(atom);
2493 } else {
2494 gop.value_ptr.* = null;
2495 }
24962527
2497 var code_buffer = std.ArrayList(u8).init(self.base.allocator);2528 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
2498 defer code_buffer.deinit();2529 defer code_buffer.deinit();
...@@ -2509,14 +2540,14 @@ pub fn updateDecl(self: *Elf, module: *Module, decl_index: Module.Decl.Index) !v...@@ -2509,14 +2540,14 @@ pub fn updateDecl(self: *Elf, module: *Module, decl_index: Module.Decl.Index) !v
2509 }, &code_buffer, .{2540 }, &code_buffer, .{
2510 .dwarf = ds,2541 .dwarf = ds,
2511 }, .{2542 }, .{
2512 .parent_atom_index = decl.link.elf.getSymbolIndex().?,2543 .parent_atom_index = atom.getSymbolIndex().?,
2513 })2544 })
2514 else2545 else
2515 try codegen.generateSymbol(&self.base, decl.srcLoc(), .{2546 try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
2516 .ty = decl.ty,2547 .ty = decl.ty,
2517 .val = decl_val,2548 .val = decl_val,
2518 }, &code_buffer, .none, .{2549 }, &code_buffer, .none, .{
2519 .parent_atom_index = decl.link.elf.getSymbolIndex().?,2550 .parent_atom_index = atom.getSymbolIndex().?,
2520 });2551 });
25212552
2522 const code = switch (res) {2553 const code = switch (res) {
...@@ -2545,41 +2576,35 @@ pub fn updateDecl(self: *Elf, module: *Module, decl_index: Module.Decl.Index) !v...@@ -2545,41 +2576,35 @@ pub fn updateDecl(self: *Elf, module: *Module, decl_index: Module.Decl.Index) !v
2545}2576}
25462577
2547pub 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 {
2548 var code_buffer = std.ArrayList(u8).init(self.base.allocator);2579 const gpa = self.base.allocator;
2580
2581 var code_buffer = std.ArrayList(u8).init(gpa);
2549 defer code_buffer.deinit();2582 defer code_buffer.deinit();
25502583
2551 const mod = self.base.options.module.?;2584 const mod = self.base.options.module.?;
2552 const decl = mod.declPtr(decl_index);2585 const gop = try self.unnamed_const_atoms.getOrPut(gpa, decl_index);
2553
2554 const gop = try self.unnamed_const_atoms.getOrPut(self.base.allocator, decl_index);
2555 if (!gop.found_existing) {2586 if (!gop.found_existing) {
2556 gop.value_ptr.* = .{};2587 gop.value_ptr.* = .{};
2557 }2588 }
2558 const unnamed_consts = gop.value_ptr;2589 const unnamed_consts = gop.value_ptr;
25592590
2560 const atom = try self.base.allocator.create(TextBlock);2591 const decl = mod.declPtr(decl_index);
2561 errdefer self.base.allocator.destroy(atom);
2562 atom.* = TextBlock.empty;
2563 // TODO for unnamed consts we don't need GOT offset/entry allocated
2564 try atom.ensureInitialized(self);
2565 try self.managed_atoms.append(self.base.allocator, atom);
2566
2567 const name_str_index = blk: {2592 const name_str_index = blk: {
2568 const decl_name = try decl.getFullyQualifiedName(mod);2593 const decl_name = try decl.getFullyQualifiedName(mod);
2569 defer self.base.allocator.free(decl_name);2594 defer gpa.free(decl_name);
2570
2571 const index = unnamed_consts.items.len;2595 const index = unnamed_consts.items.len;
2572 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 });
2573 defer self.base.allocator.free(name);2597 defer gpa.free(name);
25742598 break :blk try self.shstrtab.insert(gpa, name);
2575 break :blk try self.makeString(name);
2576 };2599 };
2577 const name = self.getString(name_str_index);2600 const name = self.shstrtab.get(name_str_index).?;
2601
2602 const atom_index = try self.createAtom();
25782603
2579 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, .{
2580 .none = {},2605 .none = {},
2581 }, .{2606 }, .{
2582 .parent_atom_index = atom.getSymbolIndex().?,2607 .parent_atom_index = self.getAtom(atom_index).getSymbolIndex().?,
2583 });2608 });
2584 const code = switch (res) {2609 const code = switch (res) {
2585 .ok => code_buffer.items,2610 .ok => code_buffer.items,
...@@ -2592,31 +2617,27 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module...@@ -2592,31 +2617,27 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module
2592 };2617 };
25932618
2594 const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);2619 const required_alignment = typed_value.ty.abiAlignment(self.base.options.target);
2595 const phdr_index = self.phdr_load_ro_index.?;2620 const shdr_index = self.rodata_section_index.?;
2596 const shdr_index = self.phdr_shdr_table.get(phdr_index).?;2621 const phdr_index = self.sections.items(.phdr_index)[shdr_index];
2597 const vaddr = try self.allocateTextBlock(atom, code.len, required_alignment, phdr_index);2622 const local_sym = self.getAtom(atom_index).getSymbolPtr(self);
2598 errdefer self.freeTextBlock(atom, phdr_index);2623 local_sym.st_name = name_str_index;
25992624 local_sym.st_info = (elf.STB_LOCAL << 4) | elf.STT_OBJECT;
2600 log.debug("allocated text block for {s} at 0x{x}", .{ name, vaddr });2625 local_sym.st_other = 0;
26012626 local_sym.st_shndx = shdr_index;
2602 const local_sym = atom.getSymbolPtr(self);2627 local_sym.st_size = code.len;
2603 local_sym.* = .{2628 local_sym.st_value = try self.allocateAtom(atom_index, code.len, required_alignment);
2604 .st_name = name_str_index,2629 errdefer self.freeAtom(atom_index);
2605 .st_info = (elf.STB_LOCAL << 4) | elf.STT_OBJECT,2630
2606 .st_other = 0,2631 log.debug("allocated text block for {s} at 0x{x}", .{ name, local_sym.st_value });
2607 .st_shndx = shdr_index,2632
2608 .st_value = vaddr,2633 try self.writeSymbol(self.getAtom(atom_index).getSymbolIndex().?);
2609 .st_size = code.len,2634 try unnamed_consts.append(gpa, atom_index);
2610 };
2611
2612 try self.writeSymbol(atom.getSymbolIndex().?);
2613 try unnamed_consts.append(self.base.allocator, atom);
26142635
2615 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;
2616 const file_offset = self.sections.items[shdr_index].sh_offset + section_offset;2637 const file_offset = self.sections.items(.shdr)[shdr_index].sh_offset + section_offset;
2617 try self.base.file.?.pwriteAll(code, file_offset);2638 try self.base.file.?.pwriteAll(code, file_offset);
26182639
2619 return atom.getSymbolIndex().?;2640 return self.getAtom(atom_index).getSymbolIndex().?;
2620}2641}
26212642
2622pub fn updateDeclExports(2643pub fn updateDeclExports(
...@@ -2635,20 +2656,16 @@ pub fn updateDeclExports(...@@ -2635,20 +2656,16 @@ pub fn updateDeclExports(
2635 const tracy = trace(@src());2656 const tracy = trace(@src());
2636 defer tracy.end();2657 defer tracy.end();
26372658
2638 const decl = module.declPtr(decl_index);2659 const gpa = self.base.allocator;
2639 const atom = &decl.link.elf;
2640
2641 if (atom.getSymbolIndex() == null) return;
26422660
2661 const decl = module.declPtr(decl_index);
2662 const atom_index = try self.getOrCreateAtomForDecl(decl_index);
2663 const atom = self.getAtom(atom_index);
2643 const decl_sym = atom.getSymbol(self);2664 const decl_sym = atom.getSymbol(self);
2644 try self.global_symbols.ensureUnusedCapacity(self.base.allocator, exports.len);2665 const decl_metadata = self.decls.getPtr(decl_index).?;
2666 const shdr_index = decl_metadata.shdr;
26452667
2646 const gop = try self.decls.getOrPut(self.base.allocator, decl_index);2668 try self.global_symbols.ensureUnusedCapacity(gpa, exports.len);
2647 if (!gop.found_existing) {
2648 gop.value_ptr.* = try self.getDeclPhdrIndex(decl);
2649 }
2650 const phdr_index = gop.value_ptr.*.?;
2651 const shdr_index = self.phdr_shdr_table.get(phdr_index).?;
26522669
2653 for (exports) |exp| {2670 for (exports) |exp| {
2654 if (exp.options.section) |section_name| {2671 if (exp.options.section) |section_name| {
...@@ -2681,10 +2698,10 @@ pub fn updateDeclExports(...@@ -2681,10 +2698,10 @@ pub fn updateDeclExports(
2681 },2698 },
2682 };2699 };
2683 const stt_bits: u8 = @truncate(u4, decl_sym.st_info);2700 const stt_bits: u8 = @truncate(u4, decl_sym.st_info);
2684 if (exp.link.elf.sym_index) |i| {2701 if (decl_metadata.getExport(self, exp.options.name)) |i| {
2685 const sym = &self.global_symbols.items[i];2702 const sym = &self.global_symbols.items[i];
2686 sym.* = .{2703 sym.* = .{
2687 .st_name = try self.updateString(sym.st_name, exp.options.name),2704 .st_name = try self.shstrtab.insert(gpa, exp.options.name),
2688 .st_info = (stb_bits << 4) | stt_bits,2705 .st_info = (stb_bits << 4) | stt_bits,
2689 .st_other = 0,2706 .st_other = 0,
2690 .st_shndx = shdr_index,2707 .st_shndx = shdr_index,
...@@ -2692,30 +2709,29 @@ pub fn updateDeclExports(...@@ -2692,30 +2709,29 @@ pub fn updateDeclExports(
2692 .st_size = decl_sym.st_size,2709 .st_size = decl_sym.st_size,
2693 };2710 };
2694 } else {2711 } else {
2695 const name = try self.makeString(exp.options.name);
2696 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: {
2697 _ = self.global_symbols.addOneAssumeCapacity();2713 _ = self.global_symbols.addOneAssumeCapacity();
2698 break :blk self.global_symbols.items.len - 1;2714 break :blk self.global_symbols.items.len - 1;
2699 };2715 };
2716 try decl_metadata.exports.append(gpa, @intCast(u32, i));
2700 self.global_symbols.items[i] = .{2717 self.global_symbols.items[i] = .{
2701 .st_name = name,2718 .st_name = try self.shstrtab.insert(gpa, exp.options.name),
2702 .st_info = (stb_bits << 4) | stt_bits,2719 .st_info = (stb_bits << 4) | stt_bits,
2703 .st_other = 0,2720 .st_other = 0,
2704 .st_shndx = shdr_index,2721 .st_shndx = shdr_index,
2705 .st_value = decl_sym.st_value,2722 .st_value = decl_sym.st_value,
2706 .st_size = decl_sym.st_size,2723 .st_size = decl_sym.st_size,
2707 };2724 };
2708
2709 exp.link.elf.sym_index = @intCast(u32, i);
2710 }2725 }
2711 }2726 }
2712}2727}
27132728
2714/// Must be called only after a successful call to `updateDecl`.2729/// Must be called only after a successful call to `updateDecl`.
2715pub fn updateDeclLineNumber(self: *Elf, mod: *Module, decl: *const Module.Decl) !void {2730pub fn updateDeclLineNumber(self: *Elf, mod: *Module, decl_index: Module.Decl.Index) !void {
2716 const tracy = trace(@src());2731 const tracy = trace(@src());
2717 defer tracy.end();2732 defer tracy.end();
27182733
2734 const decl = mod.declPtr(decl_index);
2719 const decl_name = try decl.getFullyQualifiedName(mod);2735 const decl_name = try decl.getFullyQualifiedName(mod);
2720 defer self.base.allocator.free(decl_name);2736 defer self.base.allocator.free(decl_name);
27212737
...@@ -2723,16 +2739,18 @@ pub fn updateDeclLineNumber(self: *Elf, mod: *Module, decl: *const Module.Decl)...@@ -2723,16 +2739,18 @@ pub fn updateDeclLineNumber(self: *Elf, mod: *Module, decl: *const Module.Decl)
27232739
2724 if (self.llvm_object) |_| return;2740 if (self.llvm_object) |_| return;
2725 if (self.dwarf) |*dw| {2741 if (self.dwarf) |*dw| {
2726 try dw.updateDeclLineNumber(decl);2742 try dw.updateDeclLineNumber(mod, decl_index);
2727 }2743 }
2728}2744}
27292745
2730pub fn deleteExport(self: *Elf, exp: Export) void {2746pub fn deleteDeclExport(self: *Elf, decl_index: Module.Decl.Index, name: []const u8) void {
2731 if (self.llvm_object) |_| return;2747 if (self.llvm_object) |_| return;
27322748 const metadata = self.decls.getPtr(decl_index) orelse return;
2733 const sym_index = exp.sym_index orelse return;2749 const sym_index = metadata.getExportPtr(self, name) orelse return;
2734 self.global_symbol_free_list.append(self.base.allocator, sym_index) catch {};2750 log.debug("deleting export '{s}'", .{name});
2735 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;
2736}2754}
27372755
2738fn writeProgHeader(self: *Elf, index: usize) !void {2756fn writeProgHeader(self: *Elf, index: usize) !void {
...@@ -2761,7 +2779,7 @@ fn writeSectHeader(self: *Elf, index: usize) !void {...@@ -2761,7 +2779,7 @@ fn writeSectHeader(self: *Elf, index: usize) !void {
2761 switch (self.ptr_width) {2779 switch (self.ptr_width) {
2762 .p32 => {2780 .p32 => {
2763 var shdr: [1]elf.Elf32_Shdr = undefined;2781 var shdr: [1]elf.Elf32_Shdr = undefined;
2764 shdr[0] = sectHeaderTo32(self.sections.items[index]);2782 shdr[0] = sectHeaderTo32(self.sections.items(.shdr)[index]);
2765 if (foreign_endian) {2783 if (foreign_endian) {
2766 mem.byteSwapAllFields(elf.Elf32_Shdr, &shdr[0]);2784 mem.byteSwapAllFields(elf.Elf32_Shdr, &shdr[0]);
2767 }2785 }
...@@ -2769,7 +2787,7 @@ fn writeSectHeader(self: *Elf, index: usize) !void {...@@ -2769,7 +2787,7 @@ fn writeSectHeader(self: *Elf, index: usize) !void {
2769 return self.base.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);2787 return self.base.file.?.pwriteAll(mem.sliceAsBytes(&shdr), offset);
2770 },2788 },
2771 .p64 => {2789 .p64 => {
2772 var shdr = [1]elf.Elf64_Shdr{self.sections.items[index]};2790 var shdr = [1]elf.Elf64_Shdr{self.sections.items(.shdr)[index]};
2773 if (foreign_endian) {2791 if (foreign_endian) {
2774 mem.byteSwapAllFields(elf.Elf64_Shdr, &shdr[0]);2792 mem.byteSwapAllFields(elf.Elf64_Shdr, &shdr[0]);
2775 }2793 }
...@@ -2783,11 +2801,11 @@ fn writeOffsetTableEntry(self: *Elf, index: usize) !void {...@@ -2783,11 +2801,11 @@ fn writeOffsetTableEntry(self: *Elf, index: usize) !void {
2783 const entry_size: u16 = self.archPtrWidthBytes();2801 const entry_size: u16 = self.archPtrWidthBytes();
2784 if (self.offset_table_count_dirty) {2802 if (self.offset_table_count_dirty) {
2785 const needed_size = self.offset_table.items.len * entry_size;2803 const needed_size = self.offset_table.items.len * entry_size;
2786 try self.growAllocSection(self.got_section_index.?, self.phdr_got_index.?, needed_size);2804 try self.growAllocSection(self.got_section_index.?, needed_size);
2787 self.offset_table_count_dirty = false;2805 self.offset_table_count_dirty = false;
2788 }2806 }
2789 const endian = self.base.options.target.cpu.arch.endian();2807 const endian = self.base.options.target.cpu.arch.endian();
2790 const shdr = &self.sections.items[self.got_section_index.?];2808 const shdr = &self.sections.items(.shdr)[self.got_section_index.?];
2791 const off = shdr.sh_offset + @as(u64, entry_size) * index;2809 const off = shdr.sh_offset + @as(u64, entry_size) * index;
2792 switch (entry_size) {2810 switch (entry_size) {
2793 2 => {2811 2 => {
...@@ -2813,7 +2831,7 @@ fn writeSymbol(self: *Elf, index: usize) !void {...@@ -2813,7 +2831,7 @@ fn writeSymbol(self: *Elf, index: usize) !void {
2813 const tracy = trace(@src());2831 const tracy = trace(@src());
2814 defer tracy.end();2832 defer tracy.end();
28152833
2816 const syms_sect = &self.sections.items[self.symtab_section_index.?];2834 const syms_sect = &self.sections.items(.shdr)[self.symtab_section_index.?];
2817 // 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
2818 // due to running out of space.2836 // due to running out of space.
2819 if (self.local_symbols.items.len != syms_sect.sh_info) {2837 if (self.local_symbols.items.len != syms_sect.sh_info) {
...@@ -2835,7 +2853,7 @@ fn writeSymbol(self: *Elf, index: usize) !void {...@@ -2835,7 +2853,7 @@ fn writeSymbol(self: *Elf, index: usize) !void {
2835 .p64 => syms_sect.sh_offset + @sizeOf(elf.Elf64_Sym) * index,2853 .p64 => syms_sect.sh_offset + @sizeOf(elf.Elf64_Sym) * index,
2836 };2854 };
2837 const local = self.local_symbols.items[index];2855 const local = self.local_symbols.items[index];
2838 log.debug("writing symbol {d}, '{s}' at 0x{x}", .{ index, self.getString(local.st_name), off });2856 log.debug("writing symbol {d}, '{?s}' at 0x{x}", .{ index, self.shstrtab.get(local.st_name), off });
2839 log.debug(" ({})", .{local});2857 log.debug(" ({})", .{local});
2840 switch (self.ptr_width) {2858 switch (self.ptr_width) {
2841 .p32 => {2859 .p32 => {
...@@ -2865,7 +2883,7 @@ fn writeSymbol(self: *Elf, index: usize) !void {...@@ -2865,7 +2883,7 @@ fn writeSymbol(self: *Elf, index: usize) !void {
2865}2883}
28662884
2867fn writeAllGlobalSymbols(self: *Elf) !void {2885fn writeAllGlobalSymbols(self: *Elf) !void {
2868 const syms_sect = &self.sections.items[self.symtab_section_index.?];2886 const syms_sect = &self.sections.items(.shdr)[self.symtab_section_index.?];
2869 const sym_size: u64 = switch (self.ptr_width) {2887 const sym_size: u64 = switch (self.ptr_width) {
2870 .p32 => @sizeOf(elf.Elf32_Sym),2888 .p32 => @sizeOf(elf.Elf32_Sym),
2871 .p64 => @sizeOf(elf.Elf64_Sym),2889 .p64 => @sizeOf(elf.Elf64_Sym),
...@@ -3215,10 +3233,58 @@ const CsuObjects = struct {...@@ -3215,10 +3233,58 @@ const CsuObjects = struct {
3215fn logSymtab(self: Elf) void {3233fn logSymtab(self: Elf) void {
3216 log.debug("locals:", .{});3234 log.debug("locals:", .{});
3217 for (self.local_symbols.items) |sym, id| {3235 for (self.local_symbols.items) |sym, id| {
3218 log.debug(" {d}: {s}: @{x} in {d}", .{ id, self.getString(sym.st_name), sym.st_value, sym.st_shndx });3236 log.debug(" {d}: {?s}: @{x} in {d}", .{ id, self.shstrtab.get(sym.st_name), sym.st_value, sym.st_shndx });
3219 }3237 }
3220 log.debug("globals:", .{});3238 log.debug("globals:", .{});
3221 for (self.global_symbols.items) |sym, id| {3239 for (self.global_symbols.items) |sym, id| {
3222 log.debug(" {d}: {s}: @{x} in {d}", .{ id, self.getString(sym.st_name), sym.st_value, sym.st_shndx });3240 log.debug(" {d}: {?s}: @{x} in {d}", .{ id, self.shstrtab.get(sym.st_name), sym.st_value, sym.st_shndx });
3223 }3241 }
3224}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+33-29
...@@ -4,7 +4,6 @@ const std = @import("std");...@@ -4,7 +4,6 @@ const std = @import("std");
4const assert = std.debug.assert;4const assert = std.debug.assert;
5const elf = std.elf;5const elf = std.elf;
66
7const Dwarf = @import("../Dwarf.zig");
8const Elf = @import("../Elf.zig");7const Elf = @import("../Elf.zig");
98
10/// Each decl always gets a local symbol with the fully qualified name.9/// Each decl always gets a local symbol with the fully qualified name.
...@@ -20,44 +19,33 @@ offset_table_index: u32,...@@ -20,44 +19,33 @@ offset_table_index: u32,
2019
21/// Points to the previous and next neighbors, based on the `text_offset`.20/// Points to the previous and next neighbors, based on the `text_offset`.
22/// This can be used to find, for example, the capacity of this `TextBlock`.21/// This can be used to find, for example, the capacity of this `TextBlock`.
23prev: ?*Atom,22prev_index: ?Index,
24next: ?*Atom,23next_index: ?Index,
2524
26dbg_info_atom: Dwarf.Atom,25pub const Index = u32;
2726
28pub const empty = Atom{27pub const Reloc = struct {
29 .local_sym_index = 0,28 target: u32,
30 .offset_table_index = undefined,29 offset: u64,
31 .prev = null,30 addend: u32,
32 .next = null,31 prev_vaddr: u64,
33 .dbg_info_atom = undefined,
34};32};
3533
36pub fn ensureInitialized(self: *Atom, elf_file: *Elf) !void {
37 if (self.getSymbolIndex() != null) return; // Already initialized
38 self.local_sym_index = try elf_file.allocateLocalSymbol();
39 self.offset_table_index = try elf_file.allocateGotOffset();
40 try elf_file.atom_by_index_table.putNoClobber(elf_file.base.allocator, self.local_sym_index, self);
41}
42
43pub fn getSymbolIndex(self: Atom) ?u32 {34pub fn getSymbolIndex(self: Atom) ?u32 {
44 if (self.local_sym_index == 0) return null;35 if (self.local_sym_index == 0) return null;
45 return self.local_sym_index;36 return self.local_sym_index;
46}37}
4738
48pub fn getSymbol(self: Atom, elf_file: *Elf) elf.Elf64_Sym {39pub fn getSymbol(self: Atom, elf_file: *const Elf) elf.Elf64_Sym {
49 const sym_index = self.getSymbolIndex().?;40 return elf_file.getSymbol(self.getSymbolIndex().?);
50 return elf_file.local_symbols.items[sym_index];
51}41}
5242
53pub fn getSymbolPtr(self: Atom, elf_file: *Elf) *elf.Elf64_Sym {43pub fn getSymbolPtr(self: Atom, elf_file: *Elf) *elf.Elf64_Sym {
54 const sym_index = self.getSymbolIndex().?;44 return elf_file.getSymbolPtr(self.getSymbolIndex().?);
55 return &elf_file.local_symbols.items[sym_index];
56}45}
5746
58pub fn getName(self: Atom, elf_file: *Elf) []const u8 {47pub fn getName(self: Atom, elf_file: *const Elf) []const u8 {
59 const sym = self.getSymbol();48 return elf_file.getSymbolName(self.getSymbolIndex().?);
60 return elf_file.getString(sym.st_name);
61}49}
6250
63pub fn getOffsetTableAddress(self: Atom, elf_file: *Elf) u64 {51pub fn getOffsetTableAddress(self: Atom, elf_file: *Elf) u64 {
...@@ -72,9 +60,10 @@ pub fn getOffsetTableAddress(self: Atom, elf_file: *Elf) u64 {...@@ -72,9 +60,10 @@ pub fn getOffsetTableAddress(self: Atom, elf_file: *Elf) u64 {
72/// Returns how much room there is to grow in virtual address space.60/// Returns how much room there is to grow in virtual address space.
73/// File offset relocation happens transparently, so it is not included in61/// File offset relocation happens transparently, so it is not included in
74/// this calculation.62/// this calculation.
75pub fn capacity(self: Atom, elf_file: *Elf) u64 {63pub fn capacity(self: Atom, elf_file: *const Elf) u64 {
76 const self_sym = self.getSymbol(elf_file);64 const self_sym = self.getSymbol(elf_file);
77 if (self.next) |next| {65 if (self.next_index) |next_index| {
66 const next = elf_file.getAtom(next_index);
78 const next_sym = next.getSymbol(elf_file);67 const next_sym = next.getSymbol(elf_file);
79 return next_sym.st_value - self_sym.st_value;68 return next_sym.st_value - self_sym.st_value;
80 } else {69 } else {
...@@ -83,9 +72,10 @@ pub fn capacity(self: Atom, elf_file: *Elf) u64 {...@@ -83,9 +72,10 @@ pub fn capacity(self: Atom, elf_file: *Elf) u64 {
83 }72 }
84}73}
8574
86pub fn freeListEligible(self: Atom, elf_file: *Elf) bool {75pub fn freeListEligible(self: Atom, elf_file: *const Elf) bool {
87 // No need to keep a free list node for the last block.76 // No need to keep a free list node for the last block.
88 const next = self.next orelse return false;77 const next_index = self.next_index orelse return false;
78 const next = elf_file.getAtom(next_index);
89 const self_sym = self.getSymbol(elf_file);79 const self_sym = self.getSymbol(elf_file);
90 const next_sym = next.getSymbol(elf_file);80 const next_sym = next.getSymbol(elf_file);
91 const cap = next_sym.st_value - self_sym.st_value;81 const cap = next_sym.st_value - self_sym.st_value;
...@@ -94,3 +84,17 @@ pub fn freeListEligible(self: Atom, elf_file: *Elf) bool {...@@ -94,3 +84,17 @@ pub fn freeListEligible(self: Atom, elf_file: *Elf) bool {
94 const surplus = cap - ideal_cap;84 const surplus = cap - ideal_cap;
95 return surplus >= Elf.min_text_capacity;85 return surplus >= Elf.min_text_capacity;
96}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+327-297
...@@ -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,26 +1076,38 @@ pub fn allocateSpecialSymbols(self: *MachO) !void {...@@ -1053,26 +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;
1081 const atom_index = @intCast(Atom.Index, self.atoms.items.len);
1082 const atom = try self.atoms.addOne(gpa);
1083 const sym_index = try self.allocateSymbol();
1084 try self.atom_by_index_table.putNoClobber(gpa, sym_index, atom_index);
1085 atom.* = .{
1086 .sym_index = sym_index,
1087 .file = null,
1088 .size = 0,
1089 .alignment = 0,
1090 .prev_index = null,
1091 .next_index = null,
1092 };
1093 log.debug("creating ATOM(%{d}) at index {d}", .{ sym_index, atom_index });
1094 return atom_index;
1095}
10581096
1059 const atom = try gpa.create(Atom);1097pub fn createGotAtom(self: *MachO, target: SymbolWithLoc) !Atom.Index {
1060 atom.* = Atom.empty;1098 const atom_index = try self.createAtom();
1061 try atom.ensureInitialized(self);1099 const atom = self.getAtomPtr(atom_index);
1062 atom.size = @sizeOf(u64);1100 atom.size = @sizeOf(u64);
1063 atom.alignment = @alignOf(u64);1101 atom.alignment = @alignOf(u64);
1064 errdefer gpa.destroy(atom);
1065
1066 try self.managed_atoms.append(gpa, atom);
10671102
1068 const sym = atom.getSymbolPtr(self);1103 const sym = atom.getSymbolPtr(self);
1069 sym.n_type = macho.N_SECT;1104 sym.n_type = macho.N_SECT;
1070 sym.n_sect = self.got_section_index.? + 1;1105 sym.n_sect = self.got_section_index.? + 1;
1071 sym.n_value = try self.allocateAtom(atom, atom.size, @alignOf(u64));1106 sym.n_value = try self.allocateAtom(atom_index, atom.size, @alignOf(u64));
10721107
1073 log.debug("allocated GOT atom at 0x{x}", .{sym.n_value});1108 log.debug("allocated GOT atom at 0x{x}", .{sym.n_value});
10741109
1075 try atom.addRelocation(self, .{1110 try Atom.addRelocation(self, atom_index, .{
1076 .type = switch (self.base.options.target.cpu.arch) {1111 .type = switch (self.base.options.target.cpu.arch) {
1077 .aarch64 => @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),1112 .aarch64 => @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),
1078 .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),
...@@ -1087,45 +1122,39 @@ pub fn createGotAtom(self: *MachO, target: SymbolWithLoc) !*Atom {...@@ -1087,45 +1122,39 @@ pub fn createGotAtom(self: *MachO, target: SymbolWithLoc) !*Atom {
10871122
1088 const target_sym = self.getSymbol(target);1123 const target_sym = self.getSymbol(target);
1089 if (target_sym.undf()) {1124 if (target_sym.undf()) {
1090 try atom.addBinding(self, .{1125 try Atom.addBinding(self, atom_index, .{
1091 .target = self.getGlobal(self.getSymbolName(target)).?,1126 .target = self.getGlobal(self.getSymbolName(target)).?,
1092 .offset = 0,1127 .offset = 0,
1093 });1128 });
1094 } else {1129 } else {
1095 try atom.addRebase(self, 0);1130 try Atom.addRebase(self, atom_index, 0);
1096 }1131 }
10971132
1098 return atom;1133 return atom_index;
1099}1134}
11001135
1101pub fn createDyldPrivateAtom(self: *MachO) !void {1136pub fn createDyldPrivateAtom(self: *MachO) !void {
1102 if (self.dyld_stub_binder_index == null) return;1137 if (self.dyld_stub_binder_index == null) return;
1103 if (self.dyld_private_atom != null) return;1138 if (self.dyld_private_atom_index != null) return;
1104
1105 const gpa = self.base.allocator;
11061139
1107 const atom = try gpa.create(Atom);1140 const atom_index = try self.createAtom();
1108 atom.* = Atom.empty;1141 const atom = self.getAtomPtr(atom_index);
1109 try atom.ensureInitialized(self);
1110 atom.size = @sizeOf(u64);1142 atom.size = @sizeOf(u64);
1111 atom.alignment = @alignOf(u64);1143 atom.alignment = @alignOf(u64);
1112 errdefer gpa.destroy(atom);
11131144
1114 const sym = atom.getSymbolPtr(self);1145 const sym = atom.getSymbolPtr(self);
1115 sym.n_type = macho.N_SECT;1146 sym.n_type = macho.N_SECT;
1116 sym.n_sect = self.data_section_index.? + 1;1147 sym.n_sect = self.data_section_index.? + 1;
1117 self.dyld_private_atom = atom;1148 self.dyld_private_atom_index = atom_index;
11181149
1119 try self.managed_atoms.append(gpa, atom);1150 sym.n_value = try self.allocateAtom(atom_index, atom.size, @alignOf(u64));
1120
1121 sym.n_value = try self.allocateAtom(atom, atom.size, @alignOf(u64));
1122 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});
1123 try self.writePtrWidthAtom(atom);1152 try self.writePtrWidthAtom(atom_index);
1124}1153}
11251154
1126pub fn createStubHelperPreambleAtom(self: *MachO) !void {1155pub fn createStubHelperPreambleAtom(self: *MachO) !void {
1127 if (self.dyld_stub_binder_index == null) return;1156 if (self.dyld_stub_binder_index == null) return;
1128 if (self.stub_helper_preamble_atom != null) return;1157 if (self.stub_helper_preamble_atom_index != null) return;
11291158
1130 const gpa = self.base.allocator;1159 const gpa = self.base.allocator;
1131 const arch = self.base.options.target.cpu.arch;1160 const arch = self.base.options.target.cpu.arch;
...@@ -1134,22 +1163,23 @@ pub fn createStubHelperPreambleAtom(self: *MachO) !void {...@@ -1134,22 +1163,23 @@ pub fn createStubHelperPreambleAtom(self: *MachO) !void {
1134 .aarch64 => 6 * @sizeOf(u32),1163 .aarch64 => 6 * @sizeOf(u32),
1135 else => unreachable,1164 else => unreachable,
1136 };1165 };
1137 const atom = try gpa.create(Atom);1166 const atom_index = try self.createAtom();
1138 atom.* = Atom.empty;1167 const atom = self.getAtomPtr(atom_index);
1139 try atom.ensureInitialized(self);
1140 atom.size = size;1168 atom.size = size;
1141 atom.alignment = switch (arch) {1169 atom.alignment = switch (arch) {
1142 .x86_64 => 1,1170 .x86_64 => 1,
1143 .aarch64 => @alignOf(u32),1171 .aarch64 => @alignOf(u32),
1144 else => unreachable,1172 else => unreachable,
1145 };1173 };
1146 errdefer gpa.destroy(atom);
11471174
1148 const sym = atom.getSymbolPtr(self);1175 const sym = atom.getSymbolPtr(self);
1149 sym.n_type = macho.N_SECT;1176 sym.n_type = macho.N_SECT;
1150 sym.n_sect = self.stub_helper_section_index.? + 1;1177 sym.n_sect = self.stub_helper_section_index.? + 1;
11511178
1152 const dyld_private_sym_index = self.dyld_private_atom.?.getSymbolIndex().?;1179 const dyld_private_sym_index = if (self.dyld_private_atom_index) |dyld_index|
1180 self.getAtom(dyld_index).getSymbolIndex().?
1181 else
1182 unreachable;
11531183
1154 const code = try gpa.alloc(u8, size);1184 const code = try gpa.alloc(u8, size);
1155 defer gpa.free(code);1185 defer gpa.free(code);
...@@ -1168,7 +1198,7 @@ pub fn createStubHelperPreambleAtom(self: *MachO) !void {...@@ -1168,7 +1198,7 @@ pub fn createStubHelperPreambleAtom(self: *MachO) !void {
1168 code[9] = 0xff;1198 code[9] = 0xff;
1169 code[10] = 0x25;1199 code[10] = 0x25;
11701200
1171 try atom.addRelocations(self, 2, .{ .{1201 try Atom.addRelocations(self, atom_index, 2, .{ .{
1172 .type = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_SIGNED),1202 .type = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_SIGNED),
1173 .target = .{ .sym_index = dyld_private_sym_index, .file = null },1203 .target = .{ .sym_index = dyld_private_sym_index, .file = null },
1174 .offset = 3,1204 .offset = 3,
...@@ -1208,7 +1238,7 @@ pub fn createStubHelperPreambleAtom(self: *MachO) !void {...@@ -1208,7 +1238,7 @@ pub fn createStubHelperPreambleAtom(self: *MachO) !void {
1208 // br x161238 // br x16
1209 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());
12101240
1211 try atom.addRelocations(self, 4, .{ .{1241 try Atom.addRelocations(self, atom_index, 4, .{ .{
1212 .type = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGE21),1242 .type = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGE21),
1213 .target = .{ .sym_index = dyld_private_sym_index, .file = null },1243 .target = .{ .sym_index = dyld_private_sym_index, .file = null },
1214 .offset = 0,1244 .offset = 0,
...@@ -1241,16 +1271,14 @@ pub fn createStubHelperPreambleAtom(self: *MachO) !void {...@@ -1241,16 +1271,14 @@ pub fn createStubHelperPreambleAtom(self: *MachO) !void {
12411271
1242 else => unreachable,1272 else => unreachable,
1243 }1273 }
1244 self.stub_helper_preamble_atom = atom;1274 self.stub_helper_preamble_atom_index = atom_index;
12451275
1246 try self.managed_atoms.append(gpa, atom);1276 sym.n_value = try self.allocateAtom(atom_index, size, atom.alignment);
1247
1248 sym.n_value = try self.allocateAtom(atom, size, atom.alignment);
1249 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});
1250 try self.writeAtom(atom, code);1278 try self.writeAtom(atom_index, code);
1251}1279}
12521280
1253pub fn createStubHelperAtom(self: *MachO) !*Atom {1281pub fn createStubHelperAtom(self: *MachO) !Atom.Index {
1254 const gpa = self.base.allocator;1282 const gpa = self.base.allocator;
1255 const arch = self.base.options.target.cpu.arch;1283 const arch = self.base.options.target.cpu.arch;
1256 const size: u4 = switch (arch) {1284 const size: u4 = switch (arch) {
...@@ -1258,16 +1286,14 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {...@@ -1258,16 +1286,14 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
1258 .aarch64 => 3 * @sizeOf(u32),1286 .aarch64 => 3 * @sizeOf(u32),
1259 else => unreachable,1287 else => unreachable,
1260 };1288 };
1261 const atom = try gpa.create(Atom);1289 const atom_index = try self.createAtom();
1262 atom.* = Atom.empty;1290 const atom = self.getAtomPtr(atom_index);
1263 try atom.ensureInitialized(self);
1264 atom.size = size;1291 atom.size = size;
1265 atom.alignment = switch (arch) {1292 atom.alignment = switch (arch) {
1266 .x86_64 => 1,1293 .x86_64 => 1,
1267 .aarch64 => @alignOf(u32),1294 .aarch64 => @alignOf(u32),
1268 else => unreachable,1295 else => unreachable,
1269 };1296 };
1270 errdefer gpa.destroy(atom);
12711297
1272 const sym = atom.getSymbolPtr(self);1298 const sym = atom.getSymbolPtr(self);
1273 sym.n_type = macho.N_SECT;1299 sym.n_type = macho.N_SECT;
...@@ -1277,6 +1303,11 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {...@@ -1277,6 +1303,11 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
1277 defer gpa.free(code);1303 defer gpa.free(code);
1278 mem.set(u8, code, 0);1304 mem.set(u8, code, 0);
12791305
1306 const stub_helper_preamble_atom_sym_index = if (self.stub_helper_preamble_atom_index) |stub_index|
1307 self.getAtom(stub_index).getSymbolIndex().?
1308 else
1309 unreachable;
1310
1280 switch (arch) {1311 switch (arch) {
1281 .x86_64 => {1312 .x86_64 => {
1282 // pushq1313 // pushq
...@@ -1285,9 +1316,9 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {...@@ -1285,9 +1316,9 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
1285 // jmpq1316 // jmpq
1286 code[5] = 0xe9;1317 code[5] = 0xe9;
12871318
1288 try atom.addRelocation(self, .{1319 try Atom.addRelocation(self, atom_index, .{
1289 .type = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_BRANCH),1320 .type = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_BRANCH),
1290 .target = .{ .sym_index = self.stub_helper_preamble_atom.?.getSymbolIndex().?, .file = null },1321 .target = .{ .sym_index = stub_helper_preamble_atom_sym_index, .file = null },
1291 .offset = 6,1322 .offset = 6,
1292 .addend = 0,1323 .addend = 0,
1293 .pcrel = true,1324 .pcrel = true,
...@@ -1308,9 +1339,9 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {...@@ -1308,9 +1339,9 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
1308 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.b(0).toU32());1339 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.b(0).toU32());
1309 // 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`.
13101341
1311 try atom.addRelocation(self, .{1342 try Atom.addRelocation(self, atom_index, .{
1312 .type = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_BRANCH26),1343 .type = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_BRANCH26),
1313 .target = .{ .sym_index = self.stub_helper_preamble_atom.?.getSymbolIndex().?, .file = null },1344 .target = .{ .sym_index = stub_helper_preamble_atom_sym_index, .file = null },
1314 .offset = 4,1345 .offset = 4,
1315 .addend = 0,1346 .addend = 0,
1316 .pcrel = true,1347 .pcrel = true,
...@@ -1320,29 +1351,24 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {...@@ -1320,29 +1351,24 @@ pub fn createStubHelperAtom(self: *MachO) !*Atom {
1320 else => unreachable,1351 else => unreachable,
1321 }1352 }
13221353
1323 try self.managed_atoms.append(gpa, atom);1354 sym.n_value = try self.allocateAtom(atom_index, size, atom.alignment);
1324
1325 sym.n_value = try self.allocateAtom(atom, size, atom.alignment);
1326 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});
1327 try self.writeAtom(atom, code);1356 try self.writeAtom(atom_index, code);
13281357
1329 return atom;1358 return atom_index;
1330}1359}
13311360
1332pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, target: SymbolWithLoc) !*Atom {1361pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, target: SymbolWithLoc) !Atom.Index {
1333 const gpa = self.base.allocator;1362 const atom_index = try self.createAtom();
1334 const atom = try gpa.create(Atom);1363 const atom = self.getAtomPtr(atom_index);
1335 atom.* = Atom.empty;
1336 try atom.ensureInitialized(self);
1337 atom.size = @sizeOf(u64);1364 atom.size = @sizeOf(u64);
1338 atom.alignment = @alignOf(u64);1365 atom.alignment = @alignOf(u64);
1339 errdefer gpa.destroy(atom);
13401366
1341 const sym = atom.getSymbolPtr(self);1367 const sym = atom.getSymbolPtr(self);
1342 sym.n_type = macho.N_SECT;1368 sym.n_type = macho.N_SECT;
1343 sym.n_sect = self.la_symbol_ptr_section_index.? + 1;1369 sym.n_sect = self.la_symbol_ptr_section_index.? + 1;
13441370
1345 try atom.addRelocation(self, .{1371 try Atom.addRelocation(self, atom_index, .{
1346 .type = switch (self.base.options.target.cpu.arch) {1372 .type = switch (self.base.options.target.cpu.arch) {
1347 .aarch64 => @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),1373 .aarch64 => @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),
1348 .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),
...@@ -1354,22 +1380,20 @@ pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, target: SymbolWi...@@ -1354,22 +1380,20 @@ pub fn createLazyPointerAtom(self: *MachO, stub_sym_index: u32, target: SymbolWi
1354 .pcrel = false,1380 .pcrel = false,
1355 .length = 3,1381 .length = 3,
1356 });1382 });
1357 try atom.addRebase(self, 0);1383 try Atom.addRebase(self, atom_index, 0);
1358 try atom.addLazyBinding(self, .{1384 try Atom.addLazyBinding(self, atom_index, .{
1359 .target = self.getGlobal(self.getSymbolName(target)).?,1385 .target = self.getGlobal(self.getSymbolName(target)).?,
1360 .offset = 0,1386 .offset = 0,
1361 });1387 });
13621388
1363 try self.managed_atoms.append(gpa, atom);1389 sym.n_value = try self.allocateAtom(atom_index, atom.size, @alignOf(u64));
1364
1365 sym.n_value = try self.allocateAtom(atom, atom.size, @alignOf(u64));
1366 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) });
1367 try self.writePtrWidthAtom(atom);1391 try self.writePtrWidthAtom(atom_index);
13681392
1369 return atom;1393 return atom_index;
1370}1394}
13711395
1372pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {1396pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !Atom.Index {
1373 const gpa = self.base.allocator;1397 const gpa = self.base.allocator;
1374 const arch = self.base.options.target.cpu.arch;1398 const arch = self.base.options.target.cpu.arch;
1375 const size: u4 = switch (arch) {1399 const size: u4 = switch (arch) {
...@@ -1377,9 +1401,8 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {...@@ -1377,9 +1401,8 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
1377 .aarch64 => 3 * @sizeOf(u32),1401 .aarch64 => 3 * @sizeOf(u32),
1378 else => unreachable, // unhandled architecture type1402 else => unreachable, // unhandled architecture type
1379 };1403 };
1380 const atom = try gpa.create(Atom);1404 const atom_index = try self.createAtom();
1381 atom.* = Atom.empty;1405 const atom = self.getAtomPtr(atom_index);
1382 try atom.ensureInitialized(self);
1383 atom.size = size;1406 atom.size = size;
1384 atom.alignment = switch (arch) {1407 atom.alignment = switch (arch) {
1385 .x86_64 => 1,1408 .x86_64 => 1,
...@@ -1387,7 +1410,6 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {...@@ -1387,7 +1410,6 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
1387 else => unreachable, // unhandled architecture type1410 else => unreachable, // unhandled architecture type
13881411
1389 };1412 };
1390 errdefer gpa.destroy(atom);
13911413
1392 const sym = atom.getSymbolPtr(self);1414 const sym = atom.getSymbolPtr(self);
1393 sym.n_type = macho.N_SECT;1415 sym.n_type = macho.N_SECT;
...@@ -1403,7 +1425,7 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {...@@ -1403,7 +1425,7 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
1403 code[0] = 0xff;1425 code[0] = 0xff;
1404 code[1] = 0x25;1426 code[1] = 0x25;
14051427
1406 try atom.addRelocation(self, .{1428 try Atom.addRelocation(self, atom_index, .{
1407 .type = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_BRANCH),1429 .type = @enumToInt(macho.reloc_type_x86_64.X86_64_RELOC_BRANCH),
1408 .target = .{ .sym_index = laptr_sym_index, .file = null },1430 .target = .{ .sym_index = laptr_sym_index, .file = null },
1409 .offset = 2,1431 .offset = 2,
...@@ -1424,7 +1446,7 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {...@@ -1424,7 +1446,7 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
1424 // br x161446 // br x16
1425 mem.writeIntLittle(u32, code[8..12], aarch64.Instruction.br(.x16).toU32());1447 mem.writeIntLittle(u32, code[8..12], aarch64.Instruction.br(.x16).toU32());
14261448
1427 try atom.addRelocations(self, 2, .{1449 try Atom.addRelocations(self, atom_index, 2, .{
1428 .{1450 .{
1429 .type = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGE21),1451 .type = @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_PAGE21),
1430 .target = .{ .sym_index = laptr_sym_index, .file = null },1452 .target = .{ .sym_index = laptr_sym_index, .file = null },
...@@ -1446,13 +1468,11 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {...@@ -1446,13 +1468,11 @@ pub fn createStubAtom(self: *MachO, laptr_sym_index: u32) !*Atom {
1446 else => unreachable,1468 else => unreachable,
1447 }1469 }
14481470
1449 try self.managed_atoms.append(gpa, atom);1471 sym.n_value = try self.allocateAtom(atom_index, size, atom.alignment);
1450
1451 sym.n_value = try self.allocateAtom(atom, size, atom.alignment);
1452 log.debug("allocated stub atom at 0x{x}", .{sym.n_value});1472 log.debug("allocated stub atom at 0x{x}", .{sym.n_value});
1453 try self.writeAtom(atom, code);1473 try self.writeAtom(atom_index, code);
14541474
1455 return atom;1475 return atom_index;
1456}1476}
14571477
1458pub fn createMhExecuteHeaderSymbol(self: *MachO) !void {1478pub fn createMhExecuteHeaderSymbol(self: *MachO) !void {
...@@ -1586,9 +1606,12 @@ pub fn resolveSymbolsInDylibs(self: *MachO) !void {...@@ -1586,9 +1606,12 @@ pub fn resolveSymbolsInDylibs(self: *MachO) !void {
1586 if (self.stubs_table.contains(global)) break :blk;1606 if (self.stubs_table.contains(global)) break :blk;
15871607
1588 const stub_index = try self.allocateStubEntry(global);1608 const stub_index = try self.allocateStubEntry(global);
1589 const stub_helper_atom = try self.createStubHelperAtom();1609 const stub_helper_atom_index = try self.createStubHelperAtom();
1590 const laptr_atom = try self.createLazyPointerAtom(stub_helper_atom.getSymbolIndex().?, global);1610 const stub_helper_atom = self.getAtom(stub_helper_atom_index);
1591 const stub_atom = try self.createStubAtom(laptr_atom.getSymbolIndex().?);1611 const laptr_atom_index = try self.createLazyPointerAtom(stub_helper_atom.getSymbolIndex().?, global);
1612 const laptr_atom = self.getAtom(laptr_atom_index);
1613 const stub_atom_index = try self.createStubAtom(laptr_atom.getSymbolIndex().?);
1614 const stub_atom = self.getAtom(stub_atom_index);
1592 self.stubs.items[stub_index].sym_index = stub_atom.getSymbolIndex().?;1615 self.stubs.items[stub_index].sym_index = stub_atom.getSymbolIndex().?;
1593 self.markRelocsDirtyByTarget(global);1616 self.markRelocsDirtyByTarget(global);
1594 }1617 }
...@@ -1686,10 +1709,11 @@ pub fn resolveDyldStubBinder(self: *MachO) !void {...@@ -1686,10 +1709,11 @@ pub fn resolveDyldStubBinder(self: *MachO) !void {
16861709
1687 // Add dyld_stub_binder as the final GOT entry.1710 // Add dyld_stub_binder as the final GOT entry.
1688 const got_index = try self.allocateGotEntry(global);1711 const got_index = try self.allocateGotEntry(global);
1689 const got_atom = try self.createGotAtom(global);1712 const got_atom_index = try self.createGotAtom(global);
1713 const got_atom = self.getAtom(got_atom_index);
1690 self.got_entries.items[got_index].sym_index = got_atom.getSymbolIndex().?;1714 self.got_entries.items[got_index].sym_index = got_atom.getSymbolIndex().?;
16911715
1692 try self.writePtrWidthAtom(got_atom);1716 try self.writePtrWidthAtom(got_atom_index);
1693}1717}
16941718
1695pub fn deinit(self: *MachO) void {1719pub fn deinit(self: *MachO) void {
...@@ -1739,12 +1763,12 @@ pub fn deinit(self: *MachO) void {...@@ -1739,12 +1763,12 @@ pub fn deinit(self: *MachO) void {
1739 }1763 }
1740 self.sections.deinit(gpa);1764 self.sections.deinit(gpa);
17411765
1742 for (self.managed_atoms.items) |atom| {1766 self.atoms.deinit(gpa);
1743 gpa.destroy(atom);
1744 }
1745 self.managed_atoms.deinit(gpa);
17461767
1747 if (self.base.options.module) |_| {1768 if (self.base.options.module) |_| {
1769 for (self.decls.values()) |*m| {
1770 m.exports.deinit(gpa);
1771 }
1748 self.decls.deinit(gpa);1772 self.decls.deinit(gpa);
1749 } else {1773 } else {
1750 assert(self.decls.count() == 0);1774 assert(self.decls.count() == 0);
...@@ -1778,14 +1802,14 @@ pub fn deinit(self: *MachO) void {...@@ -1778,14 +1802,14 @@ pub fn deinit(self: *MachO) void {
1778 self.lazy_bindings.deinit(gpa);1802 self.lazy_bindings.deinit(gpa);
1779}1803}
17801804
1781fn freeAtom(self: *MachO, atom: *Atom) void {1805fn freeAtom(self: *MachO, atom_index: Atom.Index) void {
1782 log.debug("freeAtom {*}", .{atom});
1783
1784 const gpa = self.base.allocator;1806 const gpa = self.base.allocator;
1807 log.debug("freeAtom {d}", .{atom_index});
17851808
1786 // Remove any relocs and base relocs associated with this Atom1809 // Remove any relocs and base relocs associated with this Atom
1787 self.freeRelocationsForAtom(atom);1810 Atom.freeRelocations(self, atom_index);
17881811
1812 const atom = self.getAtom(atom_index);
1789 const sect_id = atom.getSymbol(self).n_sect - 1;1813 const sect_id = atom.getSymbol(self).n_sect - 1;
1790 const free_list = &self.sections.items(.free_list)[sect_id];1814 const free_list = &self.sections.items(.free_list)[sect_id];
1791 var already_have_free_list_node = false;1815 var already_have_free_list_node = false;
...@@ -1793,45 +1817,46 @@ fn freeAtom(self: *MachO, atom: *Atom) void {...@@ -1793,45 +1817,46 @@ fn freeAtom(self: *MachO, atom: *Atom) void {
1793 var i: usize = 0;1817 var i: usize = 0;
1794 // TODO turn free_list into a hash map1818 // TODO turn free_list into a hash map
1795 while (i < free_list.items.len) {1819 while (i < free_list.items.len) {
1796 if (free_list.items[i] == atom) {1820 if (free_list.items[i] == atom_index) {
1797 _ = free_list.swapRemove(i);1821 _ = free_list.swapRemove(i);
1798 continue;1822 continue;
1799 }1823 }
1800 if (free_list.items[i] == atom.prev) {1824 if (free_list.items[i] == atom.prev_index) {
1801 already_have_free_list_node = true;1825 already_have_free_list_node = true;
1802 }1826 }
1803 i += 1;1827 i += 1;
1804 }1828 }
1805 }1829 }
18061830
1807 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];
1808 if (maybe_last_atom.*) |last_atom| {1832 if (maybe_last_atom_index.*) |last_atom_index| {
1809 if (last_atom == atom) {1833 if (last_atom_index == atom_index) {
1810 if (atom.prev) |prev| {1834 if (atom.prev_index) |prev_index| {
1811 // TODO shrink the section size here1835 // TODO shrink the section size here
1812 maybe_last_atom.* = prev;1836 maybe_last_atom_index.* = prev_index;
1813 } else {1837 } else {
1814 maybe_last_atom.* = null;1838 maybe_last_atom_index.* = null;
1815 }1839 }
1816 }1840 }
1817 }1841 }
18181842
1819 if (atom.prev) |prev| {1843 if (atom.prev_index) |prev_index| {
1820 prev.next = atom.next;1844 const prev = self.getAtomPtr(prev_index);
1845 prev.next_index = atom.next_index;
18211846
1822 if (!already_have_free_list_node and prev.freeListEligible(self)) {1847 if (!already_have_free_list_node and prev.*.freeListEligible(self)) {
1823 // 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
1824 // the OOM here.1849 // the OOM here.
1825 free_list.append(gpa, prev) catch {};1850 free_list.append(gpa, prev_index) catch {};
1826 }1851 }
1827 } else {1852 } else {
1828 atom.prev = null;1853 self.getAtomPtr(atom_index).prev_index = null;
1829 }1854 }
18301855
1831 if (atom.next) |next| {1856 if (atom.next_index) |next_index| {
1832 next.prev = atom.prev;1857 self.getAtomPtr(next_index).prev_index = atom.prev_index;
1833 } else {1858 } else {
1834 atom.next = null;1859 self.getAtomPtr(atom_index).next_index = null;
1835 }1860 }
18361861
1837 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.1862 // Appending to free lists is allowed to fail because the free lists are heuristics based anyway.
...@@ -1859,27 +1884,24 @@ fn freeAtom(self: *MachO, atom: *Atom) void {...@@ -1859,27 +1884,24 @@ fn freeAtom(self: *MachO, atom: *Atom) void {
1859 self.locals.items[sym_index].n_type = 0;1884 self.locals.items[sym_index].n_type = 0;
1860 _ = self.atom_by_index_table.remove(sym_index);1885 _ = self.atom_by_index_table.remove(sym_index);
1861 log.debug(" adding local symbol index {d} to free list", .{sym_index});1886 log.debug(" adding local symbol index {d} to free list", .{sym_index});
1862 atom.sym_index = 0;1887 self.getAtomPtr(atom_index).sym_index = 0;
1863
1864 if (self.d_sym) |*d_sym| {
1865 d_sym.dwarf.freeAtom(&atom.dbg_info_atom);
1866 }
1867}1888}
18681889
1869fn shrinkAtom(self: *MachO, atom: *Atom, new_block_size: u64) void {1890fn shrinkAtom(self: *MachO, atom_index: Atom.Index, new_block_size: u64) void {
1870 _ = self;1891 _ = self;
1871 _ = atom;1892 _ = atom_index;
1872 _ = new_block_size;1893 _ = new_block_size;
1873 // 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
1874 // capacity, insert a free list node for it.1895 // capacity, insert a free list node for it.
1875}1896}
18761897
1877fn growAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) !u64 {1898fn growAtom(self: *MachO, atom_index: Atom.Index, new_atom_size: u64, alignment: u64) !u64 {
1899 const atom = self.getAtom(atom_index);
1878 const sym = atom.getSymbol(self);1900 const sym = atom.getSymbol(self);
1879 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;
1880 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);
1881 if (!need_realloc) return sym.n_value;1903 if (!need_realloc) return sym.n_value;
1882 return self.allocateAtom(atom, new_atom_size, alignment);1904 return self.allocateAtom(atom_index, new_atom_size, alignment);
1883}1905}
18841906
1885pub fn allocateSymbol(self: *MachO) !u32 {1907pub fn allocateSymbol(self: *MachO) !u32 {
...@@ -1986,15 +2008,12 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv...@@ -1986,15 +2008,12 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv
19862008
1987 const decl_index = func.owner_decl;2009 const decl_index = func.owner_decl;
1988 const decl = module.declPtr(decl_index);2010 const decl = module.declPtr(decl_index);
1989 const atom = &decl.link.macho;2011
1990 try atom.ensureInitialized(self);2012 const atom_index = try self.getOrCreateAtomForDecl(decl_index);
1991 const gop = try self.decls.getOrPut(self.base.allocator, decl_index);2013 self.freeUnnamedConsts(decl_index);
1992 if (gop.found_existing) {2014 Atom.freeRelocations(self, atom_index);
1993 self.freeUnnamedConsts(decl_index);2015
1994 self.freeRelocationsForAtom(atom);2016 const atom = self.getAtom(atom_index);
1995 } else {
1996 gop.value_ptr.* = null;
1997 }
19982017
1999 var code_buffer = std.ArrayList(u8).init(self.base.allocator);2018 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
2000 defer code_buffer.deinit();2019 defer code_buffer.deinit();
...@@ -2024,13 +2043,7 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv...@@ -2024,13 +2043,7 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv
2024 const addr = try self.updateDeclCode(decl_index, code);2043 const addr = try self.updateDeclCode(decl_index, code);
20252044
2026 if (decl_state) |*ds| {2045 if (decl_state) |*ds| {
2027 try self.d_sym.?.dwarf.commitDeclState(2046 try self.d_sym.?.dwarf.commitDeclState(module, decl_index, addr, atom.size, ds);
2028 module,
2029 decl_index,
2030 addr,
2031 decl.link.macho.size,
2032 ds,
2033 );
2034 }2047 }
20352048
2036 // 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
...@@ -2065,14 +2078,10 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu...@@ -2065,14 +2078,10 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu
20652078
2066 log.debug("allocating symbol indexes for {?s}", .{name});2079 log.debug("allocating symbol indexes for {?s}", .{name});
20672080
2068 const atom = try gpa.create(Atom);2081 const atom_index = try self.createAtom();
2069 errdefer gpa.destroy(atom);
2070 atom.* = Atom.empty;
2071 try atom.ensureInitialized(self);
2072 try self.managed_atoms.append(gpa, atom);
20732082
2074 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, .{
2075 .parent_atom_index = atom.getSymbolIndex().?,2084 .parent_atom_index = self.getAtom(atom_index).getSymbolIndex().?,
2076 });2085 });
2077 const code = switch (res) {2086 const code = switch (res) {
2078 .ok => code_buffer.items,2087 .ok => code_buffer.items,
...@@ -2085,24 +2094,25 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu...@@ -2085,24 +2094,25 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu
2085 };2094 };
20862095
2087 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);
2088 atom.size = code.len;2098 atom.size = code.len;
2089 atom.alignment = required_alignment;2099 atom.alignment = required_alignment;
2090 // TODO: work out logic for disambiguating functions from function pointers2100 // TODO: work out logic for disambiguating functions from function pointers
2091 // const sect_id = self.getDeclOutputSection(decl);2101 // const sect_id = self.getDeclOutputSection(decl_index);
2092 const sect_id = self.data_const_section_index.?;2102 const sect_id = self.data_const_section_index.?;
2093 const symbol = atom.getSymbolPtr(self);2103 const symbol = atom.getSymbolPtr(self);
2094 symbol.n_strx = name_str_index;2104 symbol.n_strx = name_str_index;
2095 symbol.n_type = macho.N_SECT;2105 symbol.n_type = macho.N_SECT;
2096 symbol.n_sect = sect_id + 1;2106 symbol.n_sect = sect_id + 1;
2097 symbol.n_value = try self.allocateAtom(atom, code.len, required_alignment);2107 symbol.n_value = try self.allocateAtom(atom_index, code.len, required_alignment);
2098 errdefer self.freeAtom(atom);2108 errdefer self.freeAtom(atom_index);
20992109
2100 try unnamed_consts.append(gpa, atom);2110 try unnamed_consts.append(gpa, atom_index);
21012111
2102 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 });
2103 log.debug(" (required alignment 0x{x})", .{required_alignment});2113 log.debug(" (required alignment 0x{x})", .{required_alignment});
21042114
2105 try self.writeAtom(atom, code);2115 try self.writeAtom(atom_index, code);
21062116
2107 return atom.getSymbolIndex().?;2117 return atom.getSymbolIndex().?;
2108}2118}
...@@ -2129,14 +2139,9 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index)...@@ -2129,14 +2139,9 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index)
2129 }2139 }
2130 }2140 }
21312141
2132 const atom = &decl.link.macho;2142 const atom_index = try self.getOrCreateAtomForDecl(decl_index);
2133 try atom.ensureInitialized(self);2143 Atom.freeRelocations(self, atom_index);
2134 const gop = try self.decls.getOrPut(self.base.allocator, decl_index);2144 const atom = self.getAtom(atom_index);
2135 if (gop.found_existing) {
2136 self.freeRelocationsForAtom(atom);
2137 } else {
2138 gop.value_ptr.* = null;
2139 }
21402145
2141 var code_buffer = std.ArrayList(u8).init(self.base.allocator);2146 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
2142 defer code_buffer.deinit();2147 defer code_buffer.deinit();
...@@ -2155,14 +2160,14 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index)...@@ -2155,14 +2160,14 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index)
2155 }, &code_buffer, .{2160 }, &code_buffer, .{
2156 .dwarf = ds,2161 .dwarf = ds,
2157 }, .{2162 }, .{
2158 .parent_atom_index = decl.link.macho.getSymbolIndex().?,2163 .parent_atom_index = atom.getSymbolIndex().?,
2159 })2164 })
2160 else2165 else
2161 try codegen.generateSymbol(&self.base, decl.srcLoc(), .{2166 try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
2162 .ty = decl.ty,2167 .ty = decl.ty,
2163 .val = decl_val,2168 .val = decl_val,
2164 }, &code_buffer, .none, .{2169 }, &code_buffer, .none, .{
2165 .parent_atom_index = decl.link.macho.getSymbolIndex().?,2170 .parent_atom_index = atom.getSymbolIndex().?,
2166 });2171 });
21672172
2168 const code = switch (res) {2173 const code = switch (res) {
...@@ -2176,13 +2181,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index)...@@ -2176,13 +2181,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index)
2176 const addr = try self.updateDeclCode(decl_index, code);2181 const addr = try self.updateDeclCode(decl_index, code);
21772182
2178 if (decl_state) |*ds| {2183 if (decl_state) |*ds| {
2179 try self.d_sym.?.dwarf.commitDeclState(2184 try self.d_sym.?.dwarf.commitDeclState(module, decl_index, addr, atom.size, ds);
2180 module,
2181 decl_index,
2182 addr,
2183 decl.link.macho.size,
2184 ds,
2185 );
2186 }2185 }
21872186
2188 // 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
...@@ -2190,7 +2189,20 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index)...@@ -2190,7 +2189,20 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index)
2190 try self.updateDeclExports(module, decl_index, module.getDeclExports(decl_index));2189 try self.updateDeclExports(module, decl_index, module.getDeclExports(decl_index));
2191}2190}
21922191
2193fn getDeclOutputSection(self: *MachO, decl: *Module.Decl) u8 {2192pub fn getOrCreateAtomForDecl(self: *MachO, decl_index: Module.Decl.Index) !Atom.Index {
2193 const gop = try self.decls.getOrPut(self.base.allocator, decl_index);
2194 if (!gop.found_existing) {
2195 gop.value_ptr.* = .{
2196 .atom = try self.createAtom(),
2197 .section = self.getDeclOutputSection(decl_index),
2198 .exports = .{},
2199 };
2200 }
2201 return gop.value_ptr.atom;
2202}
2203
2204fn getDeclOutputSection(self: *MachO, decl_index: Module.Decl.Index) u8 {
2205 const decl = self.base.options.module.?.declPtr(decl_index);
2194 const ty = decl.ty;2206 const ty = decl.ty;
2195 const val = decl.val;2207 const val = decl.val;
2196 const zig_ty = ty.zigTypeTag();2208 const zig_ty = ty.zigTypeTag();
...@@ -2341,13 +2353,11 @@ fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []const u8)...@@ -2341,13 +2353,11 @@ fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []const u8)
2341 const sym_name = try decl.getFullyQualifiedName(mod);2353 const sym_name = try decl.getFullyQualifiedName(mod);
2342 defer self.base.allocator.free(sym_name);2354 defer self.base.allocator.free(sym_name);
23432355
2344 const atom = &decl.link.macho;2356 const decl_metadata = self.decls.get(decl_index).?;
2345 const sym_index = atom.getSymbolIndex().?; // Atom was not initialized2357 const atom_index = decl_metadata.atom;
2346 const decl_ptr = self.decls.getPtr(decl_index).?;2358 const atom = self.getAtom(atom_index);
2347 if (decl_ptr.* == null) {2359 const sym_index = atom.getSymbolIndex().?;
2348 decl_ptr.* = self.getDeclOutputSection(decl);2360 const sect_id = decl_metadata.section;
2349 }
2350 const sect_id = decl_ptr.*.?;
2351 const code_len = code.len;2361 const code_len = code.len;
23522362
2353 if (atom.size != 0) {2363 if (atom.size != 0) {
...@@ -2357,11 +2367,11 @@ fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []const u8)...@@ -2357,11 +2367,11 @@ fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []const u8)
2357 sym.n_sect = sect_id + 1;2367 sym.n_sect = sect_id + 1;
2358 sym.n_desc = 0;2368 sym.n_desc = 0;
23592369
2360 const capacity = decl.link.macho.capacity(self);2370 const capacity = atom.capacity(self);
2361 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);
23622372
2363 if (need_realloc) {2373 if (need_realloc) {
2364 const vaddr = try self.growAtom(atom, code_len, required_alignment);2374 const vaddr = try self.growAtom(atom_index, code_len, required_alignment);
2365 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 });
2366 log.debug(" (required alignment 0x{x})", .{required_alignment});2376 log.debug(" (required alignment 0x{x})", .{required_alignment});
23672377
...@@ -2369,19 +2379,19 @@ fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []const u8)...@@ -2369,19 +2379,19 @@ fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []const u8)
2369 sym.n_value = vaddr;2379 sym.n_value = vaddr;
2370 log.debug(" (updating GOT entry)", .{});2380 log.debug(" (updating GOT entry)", .{});
2371 const got_target = SymbolWithLoc{ .sym_index = sym_index, .file = null };2381 const got_target = SymbolWithLoc{ .sym_index = sym_index, .file = null };
2372 const got_atom = self.getGotAtomForSymbol(got_target).?;2382 const got_atom_index = self.getGotAtomIndexForSymbol(got_target).?;
2373 self.markRelocsDirtyByTarget(got_target);2383 self.markRelocsDirtyByTarget(got_target);
2374 try self.writePtrWidthAtom(got_atom);2384 try self.writePtrWidthAtom(got_atom_index);
2375 }2385 }
2376 } else if (code_len < atom.size) {2386 } else if (code_len < atom.size) {
2377 self.shrinkAtom(atom, code_len);2387 self.shrinkAtom(atom_index, code_len);
2378 } else if (atom.next == null) {2388 } else if (atom.next_index == null) {
2379 const header = &self.sections.items(.header)[sect_id];2389 const header = &self.sections.items(.header)[sect_id];
2380 const segment = self.getSegment(sect_id);2390 const segment = self.getSegment(sect_id);
2381 const needed_size = (sym.n_value + code_len) - segment.vmaddr;2391 const needed_size = (sym.n_value + code_len) - segment.vmaddr;
2382 header.size = needed_size;2392 header.size = needed_size;
2383 }2393 }
2384 atom.size = code_len;2394 self.getAtomPtr(atom_index).size = code_len;
2385 } else {2395 } else {
2386 const name_str_index = try self.strtab.insert(gpa, sym_name);2396 const name_str_index = try self.strtab.insert(gpa, sym_name);
2387 const sym = atom.getSymbolPtr(self);2397 const sym = atom.getSymbolPtr(self);
...@@ -2390,32 +2400,32 @@ fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []const u8)...@@ -2390,32 +2400,32 @@ fn updateDeclCode(self: *MachO, decl_index: Module.Decl.Index, code: []const u8)
2390 sym.n_sect = sect_id + 1;2400 sym.n_sect = sect_id + 1;
2391 sym.n_desc = 0;2401 sym.n_desc = 0;
23922402
2393 const vaddr = try self.allocateAtom(atom, code_len, required_alignment);2403 const vaddr = try self.allocateAtom(atom_index, code_len, required_alignment);
2394 errdefer self.freeAtom(atom);2404 errdefer self.freeAtom(atom_index);
23952405
2396 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 });
2397 log.debug(" (required alignment 0x{x})", .{required_alignment});2407 log.debug(" (required alignment 0x{x})", .{required_alignment});
23982408
2399 atom.size = code_len;2409 self.getAtomPtr(atom_index).size = code_len;
2400 sym.n_value = vaddr;2410 sym.n_value = vaddr;
24012411
2402 const got_target = SymbolWithLoc{ .sym_index = sym_index, .file = null };2412 const got_target = SymbolWithLoc{ .sym_index = sym_index, .file = null };
2403 const got_index = try self.allocateGotEntry(got_target);2413 const got_index = try self.allocateGotEntry(got_target);
2404 const got_atom = try self.createGotAtom(got_target);2414 const got_atom_index = try self.createGotAtom(got_target);
2415 const got_atom = self.getAtom(got_atom_index);
2405 self.got_entries.items[got_index].sym_index = got_atom.getSymbolIndex().?;2416 self.got_entries.items[got_index].sym_index = got_atom.getSymbolIndex().?;
2406 try self.writePtrWidthAtom(got_atom);2417 try self.writePtrWidthAtom(got_atom_index);
2407 }2418 }
24082419
2409 self.markRelocsDirtyByTarget(atom.getSymbolWithLoc());2420 self.markRelocsDirtyByTarget(atom.getSymbolWithLoc());
2410 try self.writeAtom(atom, code);2421 try self.writeAtom(atom_index, code);
24112422
2412 return atom.getSymbol(self).n_value;2423 return atom.getSymbol(self).n_value;
2413}2424}
24142425
2415pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl: *const Module.Decl) !void {2426pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl_index: Module.Decl.Index) !void {
2416 _ = module;
2417 if (self.d_sym) |*d_sym| {2427 if (self.d_sym) |*d_sym| {
2418 try d_sym.dwarf.updateDeclLineNumber(decl);2428 try d_sym.dwarf.updateDeclLineNumber(module, decl_index);
2419 }2429 }
2420}2430}
24212431
...@@ -2432,22 +2442,17 @@ pub fn updateDeclExports(...@@ -2432,22 +2442,17 @@ pub fn updateDeclExports(
2432 if (self.llvm_object) |llvm_object|2442 if (self.llvm_object) |llvm_object|
2433 return llvm_object.updateDeclExports(module, decl_index, exports);2443 return llvm_object.updateDeclExports(module, decl_index, exports);
2434 }2444 }
2445
2435 const tracy = trace(@src());2446 const tracy = trace(@src());
2436 defer tracy.end();2447 defer tracy.end();
24372448
2438 const gpa = self.base.allocator;2449 const gpa = self.base.allocator;
24392450
2440 const decl = module.declPtr(decl_index);2451 const decl = module.declPtr(decl_index);
2441 const atom = &decl.link.macho;2452 const atom_index = try self.getOrCreateAtomForDecl(decl_index);
24422453 const atom = self.getAtom(atom_index);
2443 if (atom.getSymbolIndex() == null) return;
2444
2445 const gop = try self.decls.getOrPut(gpa, decl_index);
2446 if (!gop.found_existing) {
2447 gop.value_ptr.* = self.getDeclOutputSection(decl);
2448 }
2449
2450 const decl_sym = atom.getSymbol(self);2454 const decl_sym = atom.getSymbol(self);
2455 const decl_metadata = self.decls.getPtr(decl_index).?;
24512456
2452 for (exports) |exp| {2457 for (exports) |exp| {
2453 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});
...@@ -2485,9 +2490,9 @@ pub fn updateDeclExports(...@@ -2485,9 +2490,9 @@ pub fn updateDeclExports(
2485 continue;2490 continue;
2486 }2491 }
24872492
2488 const sym_index = exp.link.macho.sym_index orelse blk: {2493 const sym_index = decl_metadata.getExport(self, exp_name) orelse blk: {
2489 const sym_index = try self.allocateSymbol();2494 const sym_index = try self.allocateSymbol();
2490 exp.link.macho.sym_index = sym_index;2495 try decl_metadata.exports.append(gpa, sym_index);
2491 break :blk sym_index;2496 break :blk sym_index;
2492 };2497 };
2493 const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = null };2498 const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = null };
...@@ -2535,16 +2540,18 @@ pub fn updateDeclExports(...@@ -2535,16 +2540,18 @@ pub fn updateDeclExports(
2535 }2540 }
2536}2541}
25372542
2538pub fn deleteExport(self: *MachO, exp: Export) void {2543pub fn deleteDeclExport(self: *MachO, decl_index: Module.Decl.Index, name: []const u8) Allocator.Error!void {
2539 if (self.llvm_object) |_| return;2544 if (self.llvm_object) |_| return;
2540 const sym_index = exp.sym_index orelse return;2545 const metadata = self.decls.getPtr(decl_index) orelse return;
25412546
2542 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;
25432551
2544 const sym_loc = SymbolWithLoc{ .sym_index = sym_index, .file = null };2552 const sym_loc = SymbolWithLoc{ .sym_index = sym_index.*, .file = null };
2545 const sym = self.getSymbolPtr(sym_loc);2553 const sym = self.getSymbolPtr(sym_loc);
2546 const sym_name = self.getSymbolName(sym_loc);2554 log.debug("deleting export '{s}'", .{exp_name});
2547 log.debug("deleting export '{s}'", .{sym_name});
2548 assert(sym.sect() and sym.ext());2555 assert(sym.sect() and sym.ext());
2549 sym.* = .{2556 sym.* = .{
2550 .n_strx = 0,2557 .n_strx = 0,
...@@ -2553,9 +2560,9 @@ pub fn deleteExport(self: *MachO, exp: Export) void {...@@ -2553,9 +2560,9 @@ pub fn deleteExport(self: *MachO, exp: Export) void {
2553 .n_desc = 0,2560 .n_desc = 0,
2554 .n_value = 0,2561 .n_value = 0,
2555 };2562 };
2556 self.locals_free_list.append(gpa, sym_index) catch {};2563 self.locals_free_list.append(gpa, sym_index.*) catch {};
25572564
2558 if (self.resolver.fetchRemove(sym_name)) |entry| {2565 if (self.resolver.fetchRemove(exp_name)) |entry| {
2559 defer gpa.free(entry.key);2566 defer gpa.free(entry.key);
2560 self.globals_free_list.append(gpa, entry.value) catch {};2567 self.globals_free_list.append(gpa, entry.value) catch {};
2561 self.globals.items[entry.value] = .{2568 self.globals.items[entry.value] = .{
...@@ -2563,17 +2570,8 @@ pub fn deleteExport(self: *MachO, exp: Export) void {...@@ -2563,17 +2570,8 @@ pub fn deleteExport(self: *MachO, exp: Export) void {
2563 .file = null,2570 .file = null,
2564 };2571 };
2565 }2572 }
2566}
25672573
2568fn freeRelocationsForAtom(self: *MachO, atom: *Atom) void {2574 sym_index.* = 0;
2569 var removed_relocs = self.relocs.fetchOrderedRemove(atom);
2570 if (removed_relocs) |*relocs| relocs.value.deinit(self.base.allocator);
2571 var removed_rebases = self.rebases.fetchOrderedRemove(atom);
2572 if (removed_rebases) |*rebases| rebases.value.deinit(self.base.allocator);
2573 var removed_bindings = self.bindings.fetchOrderedRemove(atom);
2574 if (removed_bindings) |*bindings| bindings.value.deinit(self.base.allocator);
2575 var removed_lazy_bindings = self.lazy_bindings.fetchOrderedRemove(atom);
2576 if (removed_lazy_bindings) |*lazy_bindings| lazy_bindings.value.deinit(self.base.allocator);
2577}2575}
25782576
2579fn freeUnnamedConsts(self: *MachO, decl_index: Module.Decl.Index) void {2577fn freeUnnamedConsts(self: *MachO, decl_index: Module.Decl.Index) void {
...@@ -2594,29 +2592,25 @@ pub fn freeDecl(self: *MachO, decl_index: Module.Decl.Index) void {...@@ -2594,29 +2592,25 @@ pub fn freeDecl(self: *MachO, decl_index: Module.Decl.Index) void {
25942592
2595 log.debug("freeDecl {*}", .{decl});2593 log.debug("freeDecl {*}", .{decl});
25962594
2597 if (self.decls.fetchSwapRemove(decl_index)) |kv| {2595 if (self.decls.fetchSwapRemove(decl_index)) |const_kv| {
2598 if (kv.value) |_| {2596 var kv = const_kv;
2599 self.freeAtom(&decl.link.macho);2597 self.freeAtom(kv.value.atom);
2600 self.freeUnnamedConsts(decl_index);2598 self.freeUnnamedConsts(decl_index);
2601 }2599 kv.value.exports.deinit(self.base.allocator);
2602 }2600 }
26032601
2604 if (self.d_sym) |*d_sym| {2602 if (self.d_sym) |*d_sym| {
2605 d_sym.dwarf.freeDecl(decl);2603 d_sym.dwarf.freeDecl(decl_index);
2606 }2604 }
2607}2605}
26082606
2609pub 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 {
2610 const mod = self.base.options.module.?;
2611 const decl = mod.declPtr(decl_index);
2612
2613 assert(self.llvm_object == null);2608 assert(self.llvm_object == null);
26142609
2615 try decl.link.macho.ensureInitialized(self);2610 const this_atom_index = try self.getOrCreateAtomForDecl(decl_index);
2616 const sym_index = decl.link.macho.getSymbolIndex().?;2611 const sym_index = self.getAtom(this_atom_index).getSymbolIndex().?;
26172612 const atom_index = self.getAtomIndexForSymbol(.{ .sym_index = reloc_info.parent_atom_index, .file = null }).?;
2618 const atom = self.getAtomForSymbol(.{ .sym_index = reloc_info.parent_atom_index, .file = null }).?;2613 try Atom.addRelocation(self, atom_index, .{
2619 try atom.addRelocation(self, .{
2620 .type = switch (self.base.options.target.cpu.arch) {2614 .type = switch (self.base.options.target.cpu.arch) {
2621 .aarch64 => @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),2615 .aarch64 => @enumToInt(macho.reloc_type_arm64.ARM64_RELOC_UNSIGNED),
2622 .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),
...@@ -2628,7 +2622,7 @@ pub fn getDeclVAddr(self: *MachO, decl_index: Module.Decl.Index, reloc_info: Fil...@@ -2628,7 +2622,7 @@ pub fn getDeclVAddr(self: *MachO, decl_index: Module.Decl.Index, reloc_info: Fil
2628 .pcrel = false,2622 .pcrel = false,
2629 .length = 3,2623 .length = 3,
2630 });2624 });
2631 try atom.addRebase(self, @intCast(u32, reloc_info.offset));2625 try Atom.addRebase(self, atom_index, @intCast(u32, reloc_info.offset));
26322626
2633 return 0;2627 return 0;
2634}2628}
...@@ -2860,34 +2854,36 @@ fn moveSectionInVirtualMemory(self: *MachO, sect_id: u8, needed_size: u64) !void...@@ -2860,34 +2854,36 @@ fn moveSectionInVirtualMemory(self: *MachO, sect_id: u8, needed_size: u64) !void
2860 // TODO: enforce order by increasing VM addresses in self.sections container.2854 // TODO: enforce order by increasing VM addresses in self.sections container.
2861 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| {
2862 const index = @intCast(u8, sect_id + 1 + next_sect_id);2856 const index = @intCast(u8, sect_id + 1 + next_sect_id);
2863 const maybe_last_atom = &self.sections.items(.last_atom)[index];
2864 const next_segment = self.getSegmentPtr(index);2857 const next_segment = self.getSegmentPtr(index);
2865 next_header.addr += diff;2858 next_header.addr += diff;
2866 next_segment.vmaddr += diff;2859 next_segment.vmaddr += diff;
28672860
2868 if (maybe_last_atom.*) |last_atom| {2861 const maybe_last_atom_index = &self.sections.items(.last_atom_index)[index];
2869 var atom = last_atom;2862 if (maybe_last_atom_index.*) |last_atom_index| {
2863 var atom_index = last_atom_index;
2870 while (true) {2864 while (true) {
2865 const atom = self.getAtom(atom_index);
2871 const sym = atom.getSymbolPtr(self);2866 const sym = atom.getSymbolPtr(self);
2872 sym.n_value += diff;2867 sym.n_value += diff;
28732868
2874 if (atom.prev) |prev| {2869 if (atom.prev_index) |prev_index| {
2875 atom = prev;2870 atom_index = prev_index;
2876 } else break;2871 } else break;
2877 }2872 }
2878 }2873 }
2879 }2874 }
2880}2875}
28812876
2882fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) !u64 {2877fn allocateAtom(self: *MachO, atom_index: Atom.Index, new_atom_size: u64, alignment: u64) !u64 {
2883 const tracy = trace(@src());2878 const tracy = trace(@src());
2884 defer tracy.end();2879 defer tracy.end();
28852880
2881 const atom = self.getAtom(atom_index);
2886 const sect_id = atom.getSymbol(self).n_sect - 1;2882 const sect_id = atom.getSymbol(self).n_sect - 1;
2887 const segment = self.getSegmentPtr(sect_id);2883 const segment = self.getSegmentPtr(sect_id);
2888 const header = &self.sections.items(.header)[sect_id];2884 const header = &self.sections.items(.header)[sect_id];
2889 const free_list = &self.sections.items(.free_list)[sect_id];2885 const free_list = &self.sections.items(.free_list)[sect_id];
2890 const maybe_last_atom = &self.sections.items(.last_atom)[sect_id];2886 const maybe_last_atom_index = &self.sections.items(.last_atom_index)[sect_id];
2891 const requires_padding = blk: {2887 const requires_padding = blk: {
2892 if (!header.isCode()) break :blk false;2888 if (!header.isCode()) break :blk false;
2893 if (header.isSymbolStubs()) break :blk false;2889 if (header.isSymbolStubs()) break :blk false;
...@@ -2901,7 +2897,7 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) !...@@ -2901,7 +2897,7 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) !
2901 // 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
2902 // 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
2903 // 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.
2904 var atom_placement: ?*Atom = null;2900 var atom_placement: ?Atom.Index = null;
2905 var free_list_removal: ?usize = null;2901 var free_list_removal: ?usize = null;
29062902
2907 // First we look for an appropriately sized free list node.2903 // First we look for an appropriately sized free list node.
...@@ -2909,7 +2905,8 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) !...@@ -2909,7 +2905,8 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) !
2909 var vaddr = blk: {2905 var vaddr = blk: {
2910 var i: usize = 0;2906 var i: usize = 0;
2911 while (i < free_list.items.len) {2907 while (i < free_list.items.len) {
2912 const big_atom = free_list.items[i];2908 const big_atom_index = free_list.items[i];
2909 const big_atom = self.getAtom(big_atom_index);
2913 // 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.
2914 // Is it enough that we could fit this new atom?2911 // Is it enough that we could fit this new atom?
2915 const sym = big_atom.getSymbol(self);2912 const sym = big_atom.getSymbol(self);
...@@ -2937,30 +2934,35 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) !...@@ -2937,30 +2934,35 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) !
2937 const keep_free_list_node = remaining_capacity >= min_text_capacity;2934 const keep_free_list_node = remaining_capacity >= min_text_capacity;
29382935
2939 // 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.
2940 atom_placement = big_atom;2937 atom_placement = big_atom_index;
2941 if (!keep_free_list_node) {2938 if (!keep_free_list_node) {
2942 free_list_removal = i;2939 free_list_removal = i;
2943 }2940 }
2944 break :blk new_start_vaddr;2941 break :blk new_start_vaddr;
2945 } else if (maybe_last_atom.*) |last| {2942 } else if (maybe_last_atom_index.*) |last_index| {
2943 const last = self.getAtom(last_index);
2946 const last_symbol = last.getSymbol(self);2944 const last_symbol = last.getSymbol(self);
2947 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;
2948 const ideal_capacity_end_vaddr = last_symbol.n_value + ideal_capacity;2946 const ideal_capacity_end_vaddr = last_symbol.n_value + ideal_capacity;
2949 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);
2950 atom_placement = last;2948 atom_placement = last_index;
2951 break :blk new_start_vaddr;2949 break :blk new_start_vaddr;
2952 } else {2950 } else {
2953 break :blk mem.alignForwardGeneric(u64, segment.vmaddr, alignment);2951 break :blk mem.alignForwardGeneric(u64, segment.vmaddr, alignment);
2954 }2952 }
2955 };2953 };
29562954
2957 const expand_section = atom_placement == null or atom_placement.?.next == null;2955 const expand_section = if (atom_placement) |placement_index|
2956 self.getAtom(placement_index).next_index == null
2957 else
2958 true;
2958 if (expand_section) {2959 if (expand_section) {
2959 const sect_capacity = self.allocatedSize(header.offset);2960 const sect_capacity = self.allocatedSize(header.offset);
2960 const needed_size = (vaddr + new_atom_size) - segment.vmaddr;2961 const needed_size = (vaddr + new_atom_size) - segment.vmaddr;
2961 if (needed_size > sect_capacity) {2962 if (needed_size > sect_capacity) {
2962 const new_offset = self.findFreeSpace(needed_size, self.page_size);2963 const new_offset = self.findFreeSpace(needed_size, self.page_size);
2963 const current_size = if (maybe_last_atom.*) |last_atom| blk: {2964 const current_size = if (maybe_last_atom_index.*) |last_atom_index| blk: {
2965 const last_atom = self.getAtom(last_atom_index);
2964 const sym = last_atom.getSymbol(self);2966 const sym = last_atom.getSymbol(self);
2965 break :blk (sym.n_value + last_atom.size) - segment.vmaddr;2967 break :blk (sym.n_value + last_atom.size) - segment.vmaddr;
2966 } else 0;2968 } else 0;
...@@ -2992,7 +2994,7 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) !...@@ -2992,7 +2994,7 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) !
2992 header.size = needed_size;2994 header.size = needed_size;
2993 segment.filesize = mem.alignForwardGeneric(u64, needed_size, self.page_size);2995 segment.filesize = mem.alignForwardGeneric(u64, needed_size, self.page_size);
2994 segment.vmsize = mem.alignForwardGeneric(u64, needed_size, self.page_size);2996 segment.vmsize = mem.alignForwardGeneric(u64, needed_size, self.page_size);
2995 maybe_last_atom.* = atom;2997 maybe_last_atom_index.* = atom_index;
29962998
2997 self.segment_table_dirty = true;2999 self.segment_table_dirty = true;
2998 }3000 }
...@@ -3001,21 +3003,31 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) !...@@ -3001,21 +3003,31 @@ fn allocateAtom(self: *MachO, atom: *Atom, new_atom_size: u64, alignment: u64) !
3001 if (header.@"align" < align_pow) {3003 if (header.@"align" < align_pow) {
3002 header.@"align" = align_pow;3004 header.@"align" = align_pow;
3003 }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 }
30043011
3005 if (atom.prev) |prev| {3012 if (atom.prev_index) |prev_index| {
3006 prev.next = atom.next;3013 const prev = self.getAtomPtr(prev_index);
3014 prev.next_index = atom.next_index;
3007 }3015 }
3008 if (atom.next) |next| {3016 if (atom.next_index) |next_index| {
3009 next.prev = atom.prev;3017 const next = self.getAtomPtr(next_index);
3018 next.prev_index = atom.prev_index;
3010 }3019 }
30113020
3012 if (atom_placement) |big_atom| {3021 if (atom_placement) |big_atom_index| {
3013 atom.prev = big_atom;3022 const big_atom = self.getAtomPtr(big_atom_index);
3014 atom.next = big_atom.next;3023 const atom_ptr = self.getAtomPtr(atom_index);
3015 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;
3016 } else {3027 } else {
3017 atom.prev = null;3028 const atom_ptr = self.getAtomPtr(atom_index);
3018 atom.next = null;3029 atom_ptr.prev_index = null;
3030 atom_ptr.next_index = null;
3019 }3031 }
3020 if (free_list_removal) |i| {3032 if (free_list_removal) |i| {
3021 _ = free_list.swapRemove(i);3033 _ = free_list.swapRemove(i);
...@@ -3155,7 +3167,8 @@ fn collectRebaseData(self: *MachO, rebase: *Rebase) !void {...@@ -3155,7 +3167,8 @@ fn collectRebaseData(self: *MachO, rebase: *Rebase) !void {
3155 const gpa = self.base.allocator;3167 const gpa = self.base.allocator;
3156 const slice = self.sections.slice();3168 const slice = self.sections.slice();
31573169
3158 for (self.rebases.keys()) |atom, i| {3170 for (self.rebases.keys()) |atom_index, i| {
3171 const atom = self.getAtom(atom_index);
3159 log.debug(" ATOM(%{?d}, '{s}')", .{ atom.getSymbolIndex(), atom.getName(self) });3172 log.debug(" ATOM(%{?d}, '{s}')", .{ atom.getSymbolIndex(), atom.getName(self) });
31603173
3161 const sym = atom.getSymbol(self);3174 const sym = atom.getSymbol(self);
...@@ -3184,7 +3197,8 @@ fn collectBindData(self: *MachO, bind: anytype, raw_bindings: anytype) !void {...@@ -3184,7 +3197,8 @@ fn collectBindData(self: *MachO, bind: anytype, raw_bindings: anytype) !void {
3184 const gpa = self.base.allocator;3197 const gpa = self.base.allocator;
3185 const slice = self.sections.slice();3198 const slice = self.sections.slice();
31863199
3187 for (raw_bindings.keys()) |atom, i| {3200 for (raw_bindings.keys()) |atom_index, i| {
3201 const atom = self.getAtom(atom_index);
3188 log.debug(" ATOM(%{?d}, '{s}')", .{ atom.getSymbolIndex(), atom.getName(self) });3202 log.debug(" ATOM(%{?d}, '{s}')", .{ atom.getSymbolIndex(), atom.getName(self) });
31893203
3190 const sym = atom.getSymbol(self);3204 const sym = atom.getSymbol(self);
...@@ -3359,7 +3373,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, lazy_bind: LazyBind) !void...@@ -3359,7 +3373,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, lazy_bind: LazyBind) !void
3359 if (lazy_bind.size() == 0) return;3373 if (lazy_bind.size() == 0) return;
33603374
3361 const stub_helper_section_index = self.stub_helper_section_index.?;3375 const stub_helper_section_index = self.stub_helper_section_index.?;
3362 assert(self.stub_helper_preamble_atom != null);3376 assert(self.stub_helper_preamble_atom_index != null);
33633377
3364 const section = self.sections.get(stub_helper_section_index);3378 const section = self.sections.get(stub_helper_section_index);
33653379
...@@ -3369,10 +3383,11 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, lazy_bind: LazyBind) !void...@@ -3369,10 +3383,11 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, lazy_bind: LazyBind) !void
3369 else => unreachable,3383 else => unreachable,
3370 };3384 };
3371 const header = section.header;3385 const header = section.header;
3372 var atom = section.last_atom.?;3386 var atom_index = section.last_atom_index.?;
33733387
3374 var index: usize = lazy_bind.offsets.items.len;3388 var index: usize = lazy_bind.offsets.items.len;
3375 while (index > 0) : (index -= 1) {3389 while (index > 0) : (index -= 1) {
3390 const atom = self.getAtom(atom_index);
3376 const sym = atom.getSymbol(self);3391 const sym = atom.getSymbol(self);
3377 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;
3378 const bind_offset = lazy_bind.offsets.items[index - 1];3393 const bind_offset = lazy_bind.offsets.items[index - 1];
...@@ -3385,7 +3400,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, lazy_bind: LazyBind) !void...@@ -3385,7 +3400,7 @@ fn populateLazyBindOffsetsInStubHelper(self: *MachO, lazy_bind: LazyBind) !void
33853400
3386 try self.base.file.?.pwriteAll(mem.asBytes(&bind_offset), file_offset);3401 try self.base.file.?.pwriteAll(mem.asBytes(&bind_offset), file_offset);
33873402
3388 atom = atom.prev.?;3403 atom_index = atom.prev_index.?;
3389 }3404 }
3390}3405}
33913406
...@@ -3828,25 +3843,35 @@ pub fn getOrPutGlobalPtr(self: *MachO, name: []const u8) !GetOrPutGlobalPtrResul...@@ -3828,25 +3843,35 @@ pub fn getOrPutGlobalPtr(self: *MachO, name: []const u8) !GetOrPutGlobalPtrResul
3828 return GetOrPutGlobalPtrResult{ .found_existing = false, .value_ptr = ptr };3843 return GetOrPutGlobalPtrResult{ .found_existing = false, .value_ptr = ptr };
3829}3844}
38303845
3846pub fn getAtom(self: *MachO, atom_index: Atom.Index) Atom {
3847 assert(atom_index < self.atoms.items.len);
3848 return self.atoms.items[atom_index];
3849}
3850
3851pub fn getAtomPtr(self: *MachO, atom_index: Atom.Index) *Atom {
3852 assert(atom_index < self.atoms.items.len);
3853 return &self.atoms.items[atom_index];
3854}
3855
3831/// 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.
3832/// Returns null on failure.3857/// Returns null on failure.
3833pub fn getAtomForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?*Atom {3858pub fn getAtomIndexForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?Atom.Index {
3834 assert(sym_with_loc.file == null);3859 assert(sym_with_loc.file == null);
3835 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);
3836}3861}
38373862
3838/// Returns GOT atom that references `sym_with_loc` if one exists.3863/// Returns GOT atom that references `sym_with_loc` if one exists.
3839/// Returns null otherwise.3864/// Returns null otherwise.
3840pub fn getGotAtomForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?*Atom {3865pub fn getGotAtomIndexForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?Atom.Index {
3841 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;
3842 return self.got_entries.items[got_index].getAtom(self);3867 return self.got_entries.items[got_index].getAtomIndex(self);
3843}3868}
38443869
3845/// Returns stubs atom that references `sym_with_loc` if one exists.3870/// Returns stubs atom that references `sym_with_loc` if one exists.
3846/// Returns null otherwise.3871/// Returns null otherwise.
3847pub fn getStubsAtomForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?*Atom {3872pub fn getStubsAtomIndexForSymbol(self: *MachO, sym_with_loc: SymbolWithLoc) ?Atom.Index {
3848 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;
3849 return self.stubs.items[stubs_index].getAtom(self);3874 return self.stubs.items[stubs_index].getAtomIndex(self);
3850}3875}
38513876
3852/// Returns symbol location corresponding to the set entrypoint.3877/// Returns symbol location corresponding to the set entrypoint.
...@@ -4232,26 +4257,31 @@ pub fn logAtoms(self: *MachO) void {...@@ -4232,26 +4257,31 @@ pub fn logAtoms(self: *MachO) void {
4232 log.debug("atoms:", .{});4257 log.debug("atoms:", .{});
42334258
4234 const slice = self.sections.slice();4259 const slice = self.sections.slice();
4235 for (slice.items(.last_atom)) |last, i| {4260 for (slice.items(.last_atom_index)) |last_atom_index, i| {
4236 var atom = last orelse continue;4261 var atom_index = last_atom_index orelse continue;
4237 const header = slice.items(.header)[i];4262 const header = slice.items(.header)[i];
42384263
4239 while (atom.prev) |prev| {4264 while (true) {
4240 atom = prev;4265 const atom = self.getAtom(atom_index);
4266 if (atom.prev_index) |prev_index| {
4267 atom_index = prev_index;
4268 } else break;
4241 }4269 }
42424270
4243 log.debug("{s},{s}", .{ header.segName(), header.sectName() });4271 log.debug("{s},{s}", .{ header.segName(), header.sectName() });
42444272
4245 while (true) {4273 while (true) {
4246 self.logAtom(atom);4274 self.logAtom(atom_index);
4247 if (atom.next) |next| {4275 const atom = self.getAtom(atom_index);
4248 atom = next;4276 if (atom.next_index) |next_index| {
4277 atom_index = next_index;
4249 } else break;4278 } else break;
4250 }4279 }
4251 }4280 }
4252}4281}
42534282
4254pub fn logAtom(self: *MachO, atom: *const Atom) void {4283pub fn logAtom(self: *MachO, atom_index: Atom.Index) void {
4284 const atom = self.getAtom(atom_index);
4255 const sym = atom.getSymbol(self);4285 const sym = atom.getSymbol(self);
4256 const sym_name = atom.getName(self);4286 const sym_name = atom.getName(self);
4257 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})", .{
src/link/MachO/Atom.zig+42-40
...@@ -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,22 +54,6 @@ pub const SymbolAtOffset = struct {...@@ -54,22 +54,6 @@ pub const SymbolAtOffset = struct {
54 offset: u64,54 offset: u64,
55};55};
5656
57pub const empty = Atom{
58 .sym_index = 0,
59 .file = null,
60 .size = 0,
61 .alignment = 0,
62 .prev = null,
63 .next = null,
64 .dbg_info_atom = undefined,
65};
66
67pub fn ensureInitialized(self: *Atom, macho_file: *MachO) !void {
68 if (self.getSymbolIndex() != null) return; // Already initialized
69 self.sym_index = try macho_file.allocateSymbol();
70 try macho_file.atom_by_index_table.putNoClobber(macho_file.base.allocator, self.sym_index, self);
71}
72
73pub fn getSymbolIndex(self: Atom) ?u32 {57pub fn getSymbolIndex(self: Atom) ?u32 {
74 if (self.sym_index == 0) return null;58 if (self.sym_index == 0) return null;
75 return self.sym_index;59 return self.sym_index;
...@@ -108,7 +92,8 @@ pub fn getName(self: Atom, macho_file: *MachO) []const u8 {...@@ -108,7 +92,8 @@ pub fn getName(self: Atom, macho_file: *MachO) []const u8 {
108/// this calculation.92/// this calculation.
109pub fn capacity(self: Atom, macho_file: *MachO) u64 {93pub fn capacity(self: Atom, macho_file: *MachO) u64 {
110 const self_sym = self.getSymbol(macho_file);94 const self_sym = self.getSymbol(macho_file);
111 if (self.next) |next| {95 if (self.next_index) |next_index| {
96 const next = macho_file.getAtom(next_index);
112 const next_sym = next.getSymbol(macho_file);97 const next_sym = next.getSymbol(macho_file);
113 return next_sym.n_value - self_sym.n_value;98 return next_sym.n_value - self_sym.n_value;
114 } else {99 } else {
...@@ -120,7 +105,8 @@ pub fn capacity(self: Atom, macho_file: *MachO) u64 {...@@ -120,7 +105,8 @@ pub fn capacity(self: Atom, macho_file: *MachO) u64 {
120105
121pub fn freeListEligible(self: Atom, macho_file: *MachO) bool {106pub fn freeListEligible(self: Atom, macho_file: *MachO) bool {
122 // 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.
123 const next = self.next orelse return false;108 const next_index = self.next_index orelse return false;
109 const next = macho_file.getAtom(next_index);
124 const self_sym = self.getSymbol(macho_file);110 const self_sym = self.getSymbol(macho_file);
125 const next_sym = next.getSymbol(macho_file);111 const next_sym = next.getSymbol(macho_file);
126 const cap = next_sym.n_value - self_sym.n_value;112 const cap = next_sym.n_value - self_sym.n_value;
...@@ -130,19 +116,19 @@ pub fn freeListEligible(self: Atom, macho_file: *MachO) bool {...@@ -130,19 +116,19 @@ pub fn freeListEligible(self: Atom, macho_file: *MachO) bool {
130 return surplus >= MachO.min_text_capacity;116 return surplus >= MachO.min_text_capacity;
131}117}
132118
133pub fn addRelocation(self: *Atom, macho_file: *MachO, reloc: Relocation) !void {119pub fn addRelocation(macho_file: *MachO, atom_index: Index, reloc: Relocation) !void {
134 return self.addRelocations(macho_file, 1, .{reloc});120 return addRelocations(macho_file, atom_index, 1, .{reloc});
135}121}
136122
137pub fn addRelocations(123pub fn addRelocations(
138 self: *Atom,
139 macho_file: *MachO,124 macho_file: *MachO,
125 atom_index: Index,
140 comptime count: comptime_int,126 comptime count: comptime_int,
141 relocs: [count]Relocation,127 relocs: [count]Relocation,
142) !void {128) !void {
143 const gpa = macho_file.base.allocator;129 const gpa = macho_file.base.allocator;
144 const target = macho_file.base.options.target;130 const target = macho_file.base.options.target;
145 const gop = try macho_file.relocs.getOrPut(gpa, self);131 const gop = try macho_file.relocs.getOrPut(gpa, atom_index);
146 if (!gop.found_existing) {132 if (!gop.found_existing) {
147 gop.value_ptr.* = .{};133 gop.value_ptr.* = .{};
148 }134 }
...@@ -156,56 +142,72 @@ pub fn addRelocations(...@@ -156,56 +142,72 @@ pub fn addRelocations(
156 }142 }
157}143}
158144
159pub fn addRebase(self: *Atom, macho_file: *MachO, offset: u32) !void {145pub fn addRebase(macho_file: *MachO, atom_index: Index, offset: u32) !void {
160 const gpa = macho_file.base.allocator;146 const gpa = macho_file.base.allocator;
161 log.debug(" (adding rebase at offset 0x{x} in %{?d})", .{ offset, self.getSymbolIndex() });147 const atom = macho_file.getAtom(atom_index);
162 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);
163 if (!gop.found_existing) {150 if (!gop.found_existing) {
164 gop.value_ptr.* = .{};151 gop.value_ptr.* = .{};
165 }152 }
166 try gop.value_ptr.append(gpa, offset);153 try gop.value_ptr.append(gpa, offset);
167}154}
168155
169pub fn addBinding(self: *Atom, macho_file: *MachO, binding: Binding) !void {156pub fn addBinding(macho_file: *MachO, atom_index: Index, binding: Binding) !void {
170 const gpa = macho_file.base.allocator;157 const gpa = macho_file.base.allocator;
158 const atom = macho_file.getAtom(atom_index);
171 log.debug(" (adding binding to symbol {s} at offset 0x{x} in %{?d})", .{159 log.debug(" (adding binding to symbol {s} at offset 0x{x} in %{?d})", .{
172 macho_file.getSymbolName(binding.target),160 macho_file.getSymbolName(binding.target),
173 binding.offset,161 binding.offset,
174 self.getSymbolIndex(),162 atom.getSymbolIndex(),
175 });163 });
176 const gop = try macho_file.bindings.getOrPut(gpa, self);164 const gop = try macho_file.bindings.getOrPut(gpa, atom_index);
177 if (!gop.found_existing) {165 if (!gop.found_existing) {
178 gop.value_ptr.* = .{};166 gop.value_ptr.* = .{};
179 }167 }
180 try gop.value_ptr.append(gpa, binding);168 try gop.value_ptr.append(gpa, binding);
181}169}
182170
183pub fn addLazyBinding(self: *Atom, macho_file: *MachO, binding: Binding) !void {171pub fn addLazyBinding(macho_file: *MachO, atom_index: Index, binding: Binding) !void {
184 const gpa = macho_file.base.allocator;172 const gpa = macho_file.base.allocator;
173 const atom = macho_file.getAtom(atom_index);
185 log.debug(" (adding lazy binding to symbol {s} at offset 0x{x} in %{?d})", .{174 log.debug(" (adding lazy binding to symbol {s} at offset 0x{x} in %{?d})", .{
186 macho_file.getSymbolName(binding.target),175 macho_file.getSymbolName(binding.target),
187 binding.offset,176 binding.offset,
188 self.getSymbolIndex(),177 atom.getSymbolIndex(),
189 });178 });
190 const gop = try macho_file.lazy_bindings.getOrPut(gpa, self);179 const gop = try macho_file.lazy_bindings.getOrPut(gpa, atom_index);
191 if (!gop.found_existing) {180 if (!gop.found_existing) {
192 gop.value_ptr.* = .{};181 gop.value_ptr.* = .{};
193 }182 }
194 try gop.value_ptr.append(gpa, binding);183 try gop.value_ptr.append(gpa, binding);
195}184}
196185
197pub fn resolveRelocations(self: *Atom, macho_file: *MachO) !void {186pub fn resolveRelocations(macho_file: *MachO, atom_index: Index) !void {
198 const relocs = macho_file.relocs.get(self) orelse return;187 const atom = macho_file.getAtom(atom_index);
199 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);
200 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;
201 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;
202192
203 log.debug("relocating '{s}'", .{self.getName(macho_file)});193 log.debug("relocating '{s}'", .{atom.getName(macho_file)});
204194
205 for (relocs.items) |*reloc| {195 for (relocs.items) |*reloc| {
206 if (!reloc.dirty) continue;196 if (!reloc.dirty) continue;
207197
208 try reloc.resolve(self, macho_file, file_offset);198 try reloc.resolve(macho_file, atom_index, file_offset);
209 reloc.dirty = false;199 reloc.dirty = false;
210 }200 }
211}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/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+149-83
...@@ -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);
...@@ -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
...@@ -387,7 +401,7 @@ pub fn updateDecl(self: *Plan9, module: *Module, decl_index: Module.Decl.Index)...@@ -387,7 +401,7 @@ pub fn updateDecl(self: *Plan9, module: *Module, decl_index: Module.Decl.Index)
387 }401 }
388 }402 }
389403
390 try self.seeDecl(decl_index);404 _ = try self.seeDecl(decl_index);
391405
392 log.debug("codegen decl {*} ({s}) ({d})", .{ decl, decl.name, decl_index });406 log.debug("codegen decl {*} ({s}) ({d})", .{ decl, decl.name, decl_index });
393407
...@@ -414,28 +428,31 @@ pub fn updateDecl(self: *Plan9, module: *Module, decl_index: Module.Decl.Index)...@@ -414,28 +428,31 @@ pub fn updateDecl(self: *Plan9, module: *Module, decl_index: Module.Decl.Index)
414 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| {
415 self.base.allocator.free(old_entry.value);429 self.base.allocator.free(old_entry.value);
416 }430 }
417 return self.updateFinish(decl);431 return self.updateFinish(decl_index);
418}432}
419/// called at the end of update{Decl,Func}433/// called at the end of update{Decl,Func}
420fn updateFinish(self: *Plan9, decl: *Module.Decl) !void {434fn updateFinish(self: *Plan9, decl_index: Module.Decl.Index) !void {
435 const decl = self.base.options.module.?.declPtr(decl_index);
421 const is_fn = (decl.ty.zigTypeTag() == .Fn);436 const is_fn = (decl.ty.zigTypeTag() == .Fn);
422 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 });
423 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);
424 // write the internal linker metadata441 // write the internal linker metadata
425 decl.link.plan9.type = sym_t;442 decl_block.type = sym_t;
426 // write the symbol443 // write the symbol
427 // we already have the got index444 // we already have the got index
428 const sym: aout.Sym = .{445 const sym: aout.Sym = .{
429 .value = undefined, // the value of stuff gets filled in in flushModule446 .value = undefined, // the value of stuff gets filled in in flushModule
430 .type = decl.link.plan9.type,447 .type = decl_block.type,
431 .name = mem.span(decl.name),448 .name = mem.span(decl.name),
432 };449 };
433450
434 if (decl.link.plan9.sym_index) |s| {451 if (decl_block.sym_index) |s| {
435 self.syms.items[s] = sym;452 self.syms.items[s] = sym;
436 } else {453 } else {
437 const s = try self.allocateSymbolIndex();454 const s = try self.allocateSymbolIndex();
438 decl.link.plan9.sym_index = s;455 decl_block.sym_index = s;
439 self.syms.items[s] = sym;456 self.syms.items[s] = sym;
440 }457 }
441}458}
...@@ -550,6 +567,7 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No...@@ -550,6 +567,7 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No
550 while (it.next()) |entry| {567 while (it.next()) |entry| {
551 const decl_index = entry.key_ptr.*;568 const decl_index = entry.key_ptr.*;
552 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);
553 const out = entry.value_ptr.*;571 const out = entry.value_ptr.*;
554 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 });
555 {573 {
...@@ -568,16 +586,16 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No...@@ -568,16 +586,16 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No
568 iovecs_i += 1;586 iovecs_i += 1;
569 const off = self.getAddr(text_i, .t);587 const off = self.getAddr(text_i, .t);
570 text_i += out.code.len;588 text_i += out.code.len;
571 decl.link.plan9.offset = off;589 decl_block.offset = off;
572 if (!self.sixtyfour_bit) {590 if (!self.sixtyfour_bit) {
573 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));
574 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());
575 } else {593 } else {
576 mem.writeInt(u64, got_table[decl.link.plan9.got_index.? * 8 ..][0..8], off, self.base.options.target.cpu.arch.endian());594 mem.writeInt(u64, got_table[decl_block.got_index.? * 8 ..][0..8], off, self.base.options.target.cpu.arch.endian());
577 }595 }
578 self.syms.items[decl.link.plan9.sym_index.?].value = off;596 self.syms.items[decl_block.sym_index.?].value = off;
579 if (mod.decl_exports.get(decl_index)) |exports| {597 if (mod.decl_exports.get(decl_index)) |exports| {
580 try self.addDeclExports(mod, decl, exports.items);598 try self.addDeclExports(mod, decl_index, exports.items);
581 }599 }
582 }600 }
583 }601 }
...@@ -598,6 +616,7 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No...@@ -598,6 +616,7 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No
598 while (it.next()) |entry| {616 while (it.next()) |entry| {
599 const decl_index = entry.key_ptr.*;617 const decl_index = entry.key_ptr.*;
600 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);
601 const code = entry.value_ptr.*;620 const code = entry.value_ptr.*;
602 log.debug("write data decl {*} ({s})", .{ decl, decl.name });621 log.debug("write data decl {*} ({s})", .{ decl, decl.name });
603622
...@@ -606,15 +625,15 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No...@@ -606,15 +625,15 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No
606 iovecs_i += 1;625 iovecs_i += 1;
607 const off = self.getAddr(data_i, .d);626 const off = self.getAddr(data_i, .d);
608 data_i += code.len;627 data_i += code.len;
609 decl.link.plan9.offset = off;628 decl_block.offset = off;
610 if (!self.sixtyfour_bit) {629 if (!self.sixtyfour_bit) {
611 mem.writeInt(u32, got_table[decl.link.plan9.got_index.? * 4 ..][0..4], @intCast(u32, off), self.base.options.target.cpu.arch.endian());630 mem.writeInt(u32, got_table[decl_block.got_index.? * 4 ..][0..4], @intCast(u32, off), self.base.options.target.cpu.arch.endian());
612 } else {631 } else {
613 mem.writeInt(u64, got_table[decl.link.plan9.got_index.? * 8 ..][0..8], off, self.base.options.target.cpu.arch.endian());632 mem.writeInt(u64, got_table[decl_block.got_index.? * 8 ..][0..8], off, self.base.options.target.cpu.arch.endian());
614 }633 }
615 self.syms.items[decl.link.plan9.sym_index.?].value = off;634 self.syms.items[decl_block.sym_index.?].value = off;
616 if (mod.decl_exports.get(decl_index)) |exports| {635 if (mod.decl_exports.get(decl_index)) |exports| {
617 try self.addDeclExports(mod, decl, exports.items);636 try self.addDeclExports(mod, decl_index, exports.items);
618 }637 }
619 }638 }
620 // write the unnamed constants after the other data decls639 // write the unnamed constants after the other data decls
...@@ -676,7 +695,8 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No...@@ -676,7 +695,8 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No
676 for (kv.value_ptr.items) |reloc| {695 for (kv.value_ptr.items) |reloc| {
677 const target_decl_index = reloc.target;696 const target_decl_index = reloc.target;
678 const target_decl = mod.declPtr(target_decl_index);697 const target_decl = mod.declPtr(target_decl_index);
679 const target_decl_offset = target_decl.link.plan9.offset.?;698 const target_decl_block = self.getDeclBlock(self.decls.get(target_decl_index).?.index);
699 const target_decl_offset = target_decl_block.offset.?;
680700
681 const offset = reloc.offset;701 const offset = reloc.offset;
682 const addend = reloc.addend;702 const addend = reloc.addend;
...@@ -709,28 +729,36 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No...@@ -709,28 +729,36 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No
709fn addDeclExports(729fn addDeclExports(
710 self: *Plan9,730 self: *Plan9,
711 module: *Module,731 module: *Module,
712 decl: *Module.Decl,732 decl_index: Module.Decl.Index,
713 exports: []const *Module.Export,733 exports: []const *Module.Export,
714) !void {734) !void {
735 const metadata = self.decls.getPtr(decl_index).?;
736 const decl_block = self.getDeclBlock(metadata.index);
737
715 for (exports) |exp| {738 for (exports) |exp| {
716 // plan9 does not support custom sections739 // plan9 does not support custom sections
717 if (exp.options.section) |section_name| {740 if (exp.options.section) |section_name| {
718 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")) {
719 try module.failed_exports.put(module.gpa, exp, try Module.ErrorMsg.create(self.base.allocator, decl.srcLoc(), "plan9 does not support extra sections", .{}));742 try module.failed_exports.put(module.gpa, exp, try Module.ErrorMsg.create(
743 self.base.allocator,
744 module.declPtr(decl_index).srcLoc(),
745 "plan9 does not support extra sections",
746 .{},
747 ));
720 break;748 break;
721 }749 }
722 }750 }
723 const sym = .{751 const sym = .{
724 .value = decl.link.plan9.offset.?,752 .value = decl_block.offset.?,
725 .type = decl.link.plan9.type.toGlobal(),753 .type = decl_block.type.toGlobal(),
726 .name = exp.options.name,754 .name = exp.options.name,
727 };755 };
728756
729 if (exp.link.plan9) |i| {757 if (metadata.getExport(self, exp.options.name)) |i| {
730 self.syms.items[i] = sym;758 self.syms.items[i] = sym;
731 } else {759 } else {
732 try self.syms.append(self.base.allocator, sym);760 try self.syms.append(self.base.allocator, sym);
733 exp.link.plan9 = self.syms.items.len - 1;761 try metadata.exports.append(self.base.allocator, self.syms.items.len - 1);
734 }762 }
735 }763 }
736}764}
...@@ -760,13 +788,18 @@ pub fn freeDecl(self: *Plan9, decl_index: Module.Decl.Index) void {...@@ -760,13 +788,18 @@ pub fn freeDecl(self: *Plan9, decl_index: Module.Decl.Index) void {
760 self.base.allocator.free(removed_entry.value);788 self.base.allocator.free(removed_entry.value);
761 }789 }
762 }790 }
763 if (decl.link.plan9.got_index) |i| {791 if (self.decls.fetchRemove(decl_index)) |const_kv| {
764 // 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;
765 self.got_index_free_list.append(self.base.allocator, i) catch {};793 const decl_block = self.getDeclBlock(kv.value.index);
766 }794 if (decl_block.got_index) |i| {
767 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
768 self.syms_index_free_list.append(self.base.allocator, i) catch {};796 self.got_index_free_list.append(self.base.allocator, i) catch {};
769 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);
770 }803 }
771 self.freeUnnamedConsts(decl_index);804 self.freeUnnamedConsts(decl_index);
772 {805 {
...@@ -786,12 +819,30 @@ fn freeUnnamedConsts(self: *Plan9, decl_index: Module.Decl.Index) void {...@@ -786,12 +819,30 @@ fn freeUnnamedConsts(self: *Plan9, decl_index: Module.Decl.Index) void {
786 unnamed_consts.clearAndFree(self.base.allocator);819 unnamed_consts.clearAndFree(self.base.allocator);
787}820}
788821
789pub fn seeDecl(self: *Plan9, decl_index: Module.Decl.Index) !void {822fn createDeclBlock(self: *Plan9) !DeclBlock.Index {
790 const mod = self.base.options.module.?;823 const gpa = self.base.allocator;
791 const decl = mod.declPtr(decl_index);824 const index = @intCast(DeclBlock.Index, self.decl_blocks.items.len);
792 if (decl.link.plan9.got_index == null) {825 const decl_block = try self.decl_blocks.addOne(gpa);
793 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 };
794 }844 }
845 return gop.value_ptr.index;
795}846}
796847
797pub fn updateDeclExports(848pub fn updateDeclExports(
...@@ -800,7 +851,7 @@ pub fn updateDeclExports(...@@ -800,7 +851,7 @@ pub fn updateDeclExports(
800 decl_index: Module.Decl.Index,851 decl_index: Module.Decl.Index,
801 exports: []const *Module.Export,852 exports: []const *Module.Export,
802) !void {853) !void {
803 try self.seeDecl(decl_index);854 _ = try self.seeDecl(decl_index);
804 // we do all the things in flush855 // we do all the things in flush
805 _ = module;856 _ = module;
806 _ = exports;857 _ = exports;
...@@ -842,10 +893,17 @@ pub fn deinit(self: *Plan9) void {...@@ -842,10 +893,17 @@ pub fn deinit(self: *Plan9) void {
842 self.syms_index_free_list.deinit(gpa);893 self.syms_index_free_list.deinit(gpa);
843 self.file_segments.deinit(gpa);894 self.file_segments.deinit(gpa);
844 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 }
845}905}
846906
847pub const Export = ?usize;
848pub const base_tag = .plan9;
849pub 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 {
850 if (options.use_llvm)908 if (options.use_llvm)
851 return error.LLVMBackendDoesNotSupportPlan9;909 return error.LLVMBackendDoesNotSupportPlan9;
...@@ -911,20 +969,19 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void {...@@ -911,20 +969,19 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void {
911 }969 }
912 }970 }
913971
914 const mod = self.base.options.module.?;
915
916 // write the data symbols972 // write the data symbols
917 {973 {
918 var it = self.data_decl_table.iterator();974 var it = self.data_decl_table.iterator();
919 while (it.next()) |entry| {975 while (it.next()) |entry| {
920 const decl_index = entry.key_ptr.*;976 const decl_index = entry.key_ptr.*;
921 const decl = mod.declPtr(decl_index);977 const decl_metadata = self.decls.get(decl_index).?;
922 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.?];
923 try self.writeSym(writer, sym);980 try self.writeSym(writer, sym);
924 if (self.base.options.module.?.decl_exports.get(decl_index)) |exports| {981 if (self.base.options.module.?.decl_exports.get(decl_index)) |exports| {
925 for (exports.items) |e| {982 for (exports.items) |e| if (decl_metadata.getExport(self, e.options.name)) |exp_i| {
926 try self.writeSym(writer, self.syms.items[e.link.plan9.?]);983 try self.writeSym(writer, self.syms.items[exp_i]);
927 }984 };
928 }985 }
929 }986 }
930 }987 }
...@@ -943,16 +1000,17 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void {...@@ -943,16 +1000,17 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void {
943 var submap_it = symidx_and_submap.functions.iterator();1000 var submap_it = symidx_and_submap.functions.iterator();
944 while (submap_it.next()) |entry| {1001 while (submap_it.next()) |entry| {
945 const decl_index = entry.key_ptr.*;1002 const decl_index = entry.key_ptr.*;
946 const decl = mod.declPtr(decl_index);1003 const decl_metadata = self.decls.get(decl_index).?;
947 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.?];
948 try self.writeSym(writer, sym);1006 try self.writeSym(writer, sym);
949 if (self.base.options.module.?.decl_exports.get(decl_index)) |exports| {1007 if (self.base.options.module.?.decl_exports.get(decl_index)) |exports| {
950 for (exports.items) |e| {1008 for (exports.items) |e| if (decl_metadata.getExport(self, e.options.name)) |exp_i| {
951 const s = self.syms.items[e.link.plan9.?];1009 const s = self.syms.items[exp_i];
952 if (mem.eql(u8, s.name, "_start"))1010 if (mem.eql(u8, s.name, "_start"))
953 self.entry_val = s.value;1011 self.entry_val = s.value;
954 try self.writeSym(writer, s);1012 try self.writeSym(writer, s);
955 }1013 };
956 }1014 }
957 }1015 }
958 }1016 }
...@@ -960,10 +1018,10 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void {...@@ -960,10 +1018,10 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void {
960}1018}
9611019
962/// Must be called only after a successful call to `updateDecl`.1020/// Must be called only after a successful call to `updateDecl`.
963pub fn updateDeclLineNumber(self: *Plan9, mod: *Module, decl: *const Module.Decl) !void {1021pub fn updateDeclLineNumber(self: *Plan9, mod: *Module, decl_index: Module.Decl.Index) !void {
964 _ = self;1022 _ = self;
965 _ = mod;1023 _ = mod;
966 _ = decl;1024 _ = decl_index;
967}1025}
9681026
969pub fn getDeclVAddr(1027pub fn getDeclVAddr(
...@@ -1004,3 +1062,11 @@ pub fn getDeclVAddr(...@@ -1004,3 +1062,11 @@ pub fn getDeclVAddr(
1004 });1062 });
1005 return undefined;1063 return undefined;
1006}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+299-270
...@@ -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,19 @@ llvm_object: ?*LlvmObject = null,...@@ -47,18 +44,19 @@ 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) = .{},49/// Mapping between an `Atom` and its type index representing the Wasm
50/// type of the function signature.
51atom_types: std.AutoHashMapUnmanaged(Atom.Index, u32) = .{},
53/// List of all symbols generated by Zig code.52/// List of all symbols generated by Zig code.
54symbols: std.ArrayListUnmanaged(Symbol) = .{},53symbols: std.ArrayListUnmanaged(Symbol) = .{},
55/// List of symbol indexes which are free to be used.54/// List of symbol indexes which are free to be used.
56symbols_free_list: std.ArrayListUnmanaged(u32) = .{},55symbols_free_list: std.ArrayListUnmanaged(u32) = .{},
57/// Maps atoms to their segment index56/// Maps atoms to their segment index
58atoms: std.AutoHashMapUnmanaged(u32, *Atom) = .{},57atoms: std.AutoHashMapUnmanaged(u32, Atom.Index) = .{},
59/// Atoms managed and created by the linker. This contains atoms58/// List of all atoms.
60/// from object files, and not Atoms generated by a Decl.59managed_atoms: std.ArrayListUnmanaged(Atom) = .{},
61managed_atoms: std.ArrayListUnmanaged(*Atom) = .{},
62/// Represents the index into `segments` where the 'code' section60/// Represents the index into `segments` where the 'code' section
63/// lives.61/// lives.
64code_section_index: ?u32 = null,62code_section_index: ?u32 = null,
...@@ -148,7 +146,7 @@ undefs: std.StringArrayHashMapUnmanaged(SymbolLoc) = .{},...@@ -148,7 +146,7 @@ undefs: std.StringArrayHashMapUnmanaged(SymbolLoc) = .{},
148/// Maps a symbol's location to an atom. This can be used to find meta146/// 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.147/// 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.148/// Undefined (and synthetic) symbols do not have an Atom and therefore cannot be mapped.
151symbol_atom: std.AutoHashMapUnmanaged(SymbolLoc, *Atom) = .{},149symbol_atom: std.AutoHashMapUnmanaged(SymbolLoc, Atom.Index) = .{},
152/// Maps a symbol's location to its export name, which may differ from the decl's name150/// Maps a symbol's location to its export name, which may differ from the decl's name
153/// which does the exporting.151/// which does the exporting.
154/// Note: The value represents the offset into the string table, rather than the actual string.152/// Note: The value represents the offset into the string table, rather than the actual string.
...@@ -165,14 +163,14 @@ error_table_symbol: ?u32 = null,...@@ -165,14 +163,14 @@ error_table_symbol: ?u32 = null,
165// unit contains Zig code. The lifetime of these atoms are extended163// unit contains Zig code. The lifetime of these atoms are extended
166// until the end of the compiler's lifetime. Meaning they're not freed164// until the end of the compiler's lifetime. Meaning they're not freed
167// during `flush()` in incremental-mode.165// during `flush()` in incremental-mode.
168debug_info_atom: ?*Atom = null,166debug_info_atom: ?Atom.Index = null,
169debug_line_atom: ?*Atom = null,167debug_line_atom: ?Atom.Index = null,
170debug_loc_atom: ?*Atom = null,168debug_loc_atom: ?Atom.Index = null,
171debug_ranges_atom: ?*Atom = null,169debug_ranges_atom: ?Atom.Index = null,
172debug_abbrev_atom: ?*Atom = null,170debug_abbrev_atom: ?Atom.Index = null,
173debug_str_atom: ?*Atom = null,171debug_str_atom: ?Atom.Index = null,
174debug_pubnames_atom: ?*Atom = null,172debug_pubnames_atom: ?Atom.Index = null,
175debug_pubtypes_atom: ?*Atom = null,173debug_pubtypes_atom: ?Atom.Index = null,
176174
177pub const Segment = struct {175pub const Segment = struct {
178 alignment: u32,176 alignment: u32,
...@@ -180,19 +178,6 @@ pub const Segment = struct {...@@ -180,19 +178,6 @@ pub const Segment = struct {
180 offset: u32,178 offset: u32,
181};179};
182180
183pub const FnData = struct {
184 /// Reference to the wasm type that represents this function.
185 type_index: u32,
186 /// Contains debug information related to this function.
187 /// For Wasm, the offset is relative to the code-section.
188 src_fn: Dwarf.SrcFn,
189
190 pub const empty: FnData = .{
191 .type_index = undefined,
192 .src_fn = Dwarf.SrcFn.empty,
193 };
194};
195
196pub const Export = struct {181pub const Export = struct {
197 sym_index: ?u32 = null,182 sym_index: ?u32 = null,
198};183};
...@@ -434,10 +419,10 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option...@@ -434,10 +419,10 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
434 // at the end during `initializeCallCtorsFunction`.419 // at the end during `initializeCallCtorsFunction`.
435 }420 }
436421
437 if (!options.strip and options.module != null) {422 // if (!options.strip and options.module != null) {
438 wasm_bin.dwarf = Dwarf.init(allocator, &wasm_bin.base, options.target);423 // wasm_bin.dwarf = Dwarf.init(allocator, &wasm_bin.base, options.target);
439 try wasm_bin.initDebugSections();424 // try wasm_bin.initDebugSections();
440 }425 // }
441426
442 return wasm_bin;427 return wasm_bin;
443}428}
...@@ -478,6 +463,7 @@ fn createSyntheticSymbol(wasm: *Wasm, name: []const u8, tag: Symbol.Tag) !Symbol...@@ -478,6 +463,7 @@ fn createSyntheticSymbol(wasm: *Wasm, name: []const u8, tag: Symbol.Tag) !Symbol
478 try wasm.globals.put(wasm.base.allocator, name_offset, loc);463 try wasm.globals.put(wasm.base.allocator, name_offset, loc);
479 return loc;464 return loc;
480}465}
466
481/// Initializes symbols and atoms for the debug sections467/// Initializes symbols and atoms for the debug sections
482/// Initialization is only done when compiling Zig code.468/// Initialization is only done when compiling Zig code.
483/// When Zig is invoked as a linker instead, the atoms469/// When Zig is invoked as a linker instead, the atoms
...@@ -520,6 +506,36 @@ fn parseObjectFile(wasm: *Wasm, path: []const u8) !bool {...@@ -520,6 +506,36 @@ fn parseObjectFile(wasm: *Wasm, path: []const u8) !bool {
520 return true;506 return true;
521}507}
522508
509/// For a given `Module.Decl.Index` returns its corresponding `Atom.Index`.
510/// When the index was not found, a new `Atom` will be created, and its index will be returned.
511/// The newly created Atom is empty with default fields as specified by `Atom.empty`.
512pub fn getOrCreateAtomForDecl(wasm: *Wasm, decl_index: Module.Decl.Index) !Atom.Index {
513 const gop = try wasm.decls.getOrPut(wasm.base.allocator, decl_index);
514 if (!gop.found_existing) {
515 gop.value_ptr.* = try wasm.createAtom();
516 }
517 return gop.value_ptr.*;
518}
519
520/// Creates a new empty `Atom` and returns its `Atom.Index`
521fn createAtom(wasm: *Wasm) !Atom.Index {
522 const index = @intCast(Atom.Index, wasm.managed_atoms.items.len);
523 const atom = try wasm.managed_atoms.addOne(wasm.base.allocator);
524 atom.* = Atom.empty;
525 atom.sym_index = try wasm.allocateSymbol();
526 try wasm.symbol_atom.putNoClobber(wasm.base.allocator, .{ .file = null, .index = atom.sym_index }, index);
527
528 return index;
529}
530
531pub inline fn getAtom(wasm: *const Wasm, index: Atom.Index) Atom {
532 return wasm.managed_atoms.items[index];
533}
534
535pub inline fn getAtomPtr(wasm: *Wasm, index: Atom.Index) *Atom {
536 return &wasm.managed_atoms.items[index];
537}
538
523/// Parses an archive file and will then parse each object file539/// Parses an archive file and will then parse each object file
524/// that was found in the archive file.540/// that was found in the archive file.
525/// Returns false when the file is not an archive file.541/// Returns false when the file is not an archive file.
...@@ -861,15 +877,16 @@ fn resolveLazySymbols(wasm: *Wasm) !void {...@@ -861,15 +877,16 @@ fn resolveLazySymbols(wasm: *Wasm) !void {
861 try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc);877 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.878 _ = wasm.resolved_symbols.swapRemove(loc); // we don't want to emit this symbol, only use it for relocations.
863879
864 const atom = try wasm.base.allocator.create(Atom);880 // TODO: Can we use `createAtom` here while also re-using the symbol
865 errdefer wasm.base.allocator.destroy(atom);881 // from `createSyntheticSymbol`.
866 try wasm.managed_atoms.append(wasm.base.allocator, atom);882 const atom_index = @intCast(Atom.Index, wasm.managed_atoms.items.len);
883 const atom = try wasm.managed_atoms.addOne(wasm.base.allocator);
867 atom.* = Atom.empty;884 atom.* = Atom.empty;
868 atom.sym_index = loc.index;885 atom.sym_index = loc.index;
869 atom.alignment = 1;886 atom.alignment = 1;
870887
871 try wasm.parseAtom(atom, .{ .data = .synthetic });888 try wasm.parseAtom(atom_index, .{ .data = .synthetic });
872 try wasm.symbol_atom.putNoClobber(wasm.base.allocator, loc, atom);889 try wasm.symbol_atom.putNoClobber(wasm.base.allocator, loc, atom_index);
873 }890 }
874891
875 if (wasm.undefs.fetchSwapRemove("__heap_end")) |kv| {892 if (wasm.undefs.fetchSwapRemove("__heap_end")) |kv| {
...@@ -877,15 +894,14 @@ fn resolveLazySymbols(wasm: *Wasm) !void {...@@ -877,15 +894,14 @@ fn resolveLazySymbols(wasm: *Wasm) !void {
877 try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc);894 try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc);
878 _ = wasm.resolved_symbols.swapRemove(loc);895 _ = wasm.resolved_symbols.swapRemove(loc);
879896
880 const atom = try wasm.base.allocator.create(Atom);897 const atom_index = @intCast(Atom.Index, wasm.managed_atoms.items.len);
881 errdefer wasm.base.allocator.destroy(atom);898 const atom = try wasm.managed_atoms.addOne(wasm.base.allocator);
882 try wasm.managed_atoms.append(wasm.base.allocator, atom);
883 atom.* = Atom.empty;899 atom.* = Atom.empty;
884 atom.sym_index = loc.index;900 atom.sym_index = loc.index;
885 atom.alignment = 1;901 atom.alignment = 1;
886902
887 try wasm.parseAtom(atom, .{ .data = .synthetic });903 try wasm.parseAtom(atom_index, .{ .data = .synthetic });
888 try wasm.symbol_atom.putNoClobber(wasm.base.allocator, loc, atom);904 try wasm.symbol_atom.putNoClobber(wasm.base.allocator, loc, atom_index);
889 }905 }
890}906}
891907
...@@ -924,16 +940,6 @@ pub fn deinit(wasm: *Wasm) void {...@@ -924,16 +940,6 @@ pub fn deinit(wasm: *Wasm) void {
924 if (wasm.llvm_object) |llvm_object| llvm_object.destroy(gpa);940 if (wasm.llvm_object) |llvm_object| llvm_object.destroy(gpa);
925 }941 }
926942
927 if (wasm.base.options.module) |mod| {
928 var decl_it = wasm.decls.keyIterator();
929 while (decl_it.next()) |decl_index_ptr| {
930 const decl = mod.declPtr(decl_index_ptr.*);
931 decl.link.wasm.deinit(gpa);
932 }
933 } else {
934 assert(wasm.decls.count() == 0);
935 }
936
937 for (wasm.func_types.items) |*func_type| {943 for (wasm.func_types.items) |*func_type| {
938 func_type.deinit(gpa);944 func_type.deinit(gpa);
939 }945 }
...@@ -949,6 +955,7 @@ pub fn deinit(wasm: *Wasm) void {...@@ -949,6 +955,7 @@ pub fn deinit(wasm: *Wasm) void {
949 }955 }
950956
951 wasm.decls.deinit(gpa);957 wasm.decls.deinit(gpa);
958 wasm.atom_types.deinit(gpa);
952 wasm.symbols.deinit(gpa);959 wasm.symbols.deinit(gpa);
953 wasm.symbols_free_list.deinit(gpa);960 wasm.symbols_free_list.deinit(gpa);
954 wasm.globals.deinit(gpa);961 wasm.globals.deinit(gpa);
...@@ -958,9 +965,8 @@ pub fn deinit(wasm: *Wasm) void {...@@ -958,9 +965,8 @@ pub fn deinit(wasm: *Wasm) void {
958 wasm.symbol_atom.deinit(gpa);965 wasm.symbol_atom.deinit(gpa);
959 wasm.export_names.deinit(gpa);966 wasm.export_names.deinit(gpa);
960 wasm.atoms.deinit(gpa);967 wasm.atoms.deinit(gpa);
961 for (wasm.managed_atoms.items) |managed_atom| {968 for (wasm.managed_atoms.items) |*managed_atom| {
962 managed_atom.deinit(gpa);969 managed_atom.deinit(wasm);
963 gpa.destroy(managed_atom);
964 }970 }
965 wasm.managed_atoms.deinit(gpa);971 wasm.managed_atoms.deinit(gpa);
966 wasm.segments.deinit(gpa);972 wasm.segments.deinit(gpa);
...@@ -1018,18 +1024,24 @@ pub fn updateFunc(wasm: *Wasm, mod: *Module, func: *Module.Fn, air: Air, livenes...@@ -1018,18 +1024,24 @@ pub fn updateFunc(wasm: *Wasm, mod: *Module, func: *Module.Fn, air: Air, livenes
10181024
1019 const decl_index = func.owner_decl;1025 const decl_index = func.owner_decl;
1020 const decl = mod.declPtr(decl_index);1026 const decl = mod.declPtr(decl_index);
1021 const atom = &decl.link.wasm;1027 const atom_index = try wasm.getOrCreateAtomForDecl(decl_index);
1022 try atom.ensureInitialized(wasm);1028 const atom = wasm.getAtomPtr(atom_index);
1023 const gop = try wasm.decls.getOrPut(wasm.base.allocator, decl_index);1029 atom.clear();
1024 if (gop.found_existing) {
1025 atom.clear();
1026 } else gop.value_ptr.* = {};
10271030
1028 var decl_state: ?Dwarf.DeclState = if (wasm.dwarf) |*dwarf| try dwarf.initDeclState(mod, decl_index) else null;1031 // var decl_state: ?Dwarf.DeclState = if (wasm.dwarf) |*dwarf| try dwarf.initDeclState(mod, decl_index) else null;
1029 defer if (decl_state) |*ds| ds.deinit();1032 // defer if (decl_state) |*ds| ds.deinit();
10301033
1031 var code_writer = std.ArrayList(u8).init(wasm.base.allocator);1034 var code_writer = std.ArrayList(u8).init(wasm.base.allocator);
1032 defer code_writer.deinit();1035 defer code_writer.deinit();
1036 // const result = try codegen.generateFunction(
1037 // &wasm.base,
1038 // decl.srcLoc(),
1039 // func,
1040 // air,
1041 // liveness,
1042 // &code_writer,
1043 // if (decl_state) |*ds| .{ .dwarf = ds } else .none,
1044 // );
1033 const result = try codegen.generateFunction(1045 const result = try codegen.generateFunction(
1034 &wasm.base,1046 &wasm.base,
1035 decl.srcLoc(),1047 decl.srcLoc(),
...@@ -1037,7 +1049,7 @@ pub fn updateFunc(wasm: *Wasm, mod: *Module, func: *Module.Fn, air: Air, livenes...@@ -1037,7 +1049,7 @@ pub fn updateFunc(wasm: *Wasm, mod: *Module, func: *Module.Fn, air: Air, livenes
1037 air,1049 air,
1038 liveness,1050 liveness,
1039 &code_writer,1051 &code_writer,
1040 if (decl_state) |*ds| .{ .dwarf = ds } else .none,1052 .none,
1041 );1053 );
10421054
1043 const code = switch (result) {1055 const code = switch (result) {
...@@ -1049,19 +1061,19 @@ pub fn updateFunc(wasm: *Wasm, mod: *Module, func: *Module.Fn, air: Air, livenes...@@ -1049,19 +1061,19 @@ pub fn updateFunc(wasm: *Wasm, mod: *Module, func: *Module.Fn, air: Air, livenes
1049 },1061 },
1050 };1062 };
10511063
1052 if (wasm.dwarf) |*dwarf| {1064 // if (wasm.dwarf) |*dwarf| {
1053 try dwarf.commitDeclState(1065 // try dwarf.commitDeclState(
1054 mod,1066 // mod,
1055 decl_index,1067 // decl_index,
1056 // Actual value will be written after relocation.1068 // // Actual value will be written after relocation.
1057 // For Wasm, this is the offset relative to the code section1069 // // For Wasm, this is the offset relative to the code section
1058 // which isn't known until flush().1070 // // which isn't known until flush().
1059 0,1071 // 0,
1060 code.len,1072 // code.len,
1061 &decl_state.?,1073 // &decl_state.?,
1062 );1074 // );
1063 }1075 // }
1064 return wasm.finishUpdateDecl(decl, code);1076 return wasm.finishUpdateDecl(decl_index, code);
1065}1077}
10661078
1067// Generate code for the Decl, storing it in memory to be later written to1079// Generate code for the Decl, storing it in memory to be later written to
...@@ -1084,17 +1096,14 @@ pub fn updateDecl(wasm: *Wasm, mod: *Module, decl_index: Module.Decl.Index) !voi...@@ -1084,17 +1096,14 @@ pub fn updateDecl(wasm: *Wasm, mod: *Module, decl_index: Module.Decl.Index) !voi
1084 return;1096 return;
1085 }1097 }
10861098
1087 const atom = &decl.link.wasm;1099 const atom_index = try wasm.getOrCreateAtomForDecl(decl_index);
1088 try atom.ensureInitialized(wasm);1100 const atom = wasm.getAtomPtr(atom_index);
1089 const gop = try wasm.decls.getOrPut(wasm.base.allocator, decl_index);1101 atom.clear();
1090 if (gop.found_existing) {
1091 atom.clear();
1092 } else gop.value_ptr.* = {};
10931102
1094 if (decl.isExtern()) {1103 if (decl.isExtern()) {
1095 const variable = decl.getVariable().?;1104 const variable = decl.getVariable().?;
1096 const name = mem.sliceTo(decl.name, 0);1105 const name = mem.sliceTo(decl.name, 0);
1097 return wasm.addOrUpdateImport(name, decl.link.wasm.sym_index, variable.lib_name, null);1106 return wasm.addOrUpdateImport(name, atom.sym_index, variable.lib_name, null);
1098 }1107 }
1099 const val = if (decl.val.castTag(.variable)) |payload| payload.data.init else decl.val;1108 const val = if (decl.val.castTag(.variable)) |payload| payload.data.init else decl.val;
11001109
...@@ -1107,7 +1116,7 @@ pub fn updateDecl(wasm: *Wasm, mod: *Module, decl_index: Module.Decl.Index) !voi...@@ -1107,7 +1116,7 @@ pub fn updateDecl(wasm: *Wasm, mod: *Module, decl_index: Module.Decl.Index) !voi
1107 .{ .ty = decl.ty, .val = val },1116 .{ .ty = decl.ty, .val = val },
1108 &code_writer,1117 &code_writer,
1109 .none,1118 .none,
1110 .{ .parent_atom_index = decl.link.wasm.sym_index },1119 .{ .parent_atom_index = atom.sym_index },
1111 );1120 );
11121121
1113 const code = switch (res) {1122 const code = switch (res) {
...@@ -1119,26 +1128,29 @@ pub fn updateDecl(wasm: *Wasm, mod: *Module, decl_index: Module.Decl.Index) !voi...@@ -1119,26 +1128,29 @@ pub fn updateDecl(wasm: *Wasm, mod: *Module, decl_index: Module.Decl.Index) !voi
1119 },1128 },
1120 };1129 };
11211130
1122 return wasm.finishUpdateDecl(decl, code);1131 return wasm.finishUpdateDecl(decl_index, code);
1123}1132}
11241133
1125pub fn updateDeclLineNumber(wasm: *Wasm, mod: *Module, decl: *const Module.Decl) !void {1134pub fn updateDeclLineNumber(wasm: *Wasm, mod: *Module, decl_index: Module.Decl.Index) !void {
1126 if (wasm.llvm_object) |_| return;1135 if (wasm.llvm_object) |_| return;
1127 if (wasm.dwarf) |*dw| {1136 if (wasm.dwarf) |*dw| {
1128 const tracy = trace(@src());1137 const tracy = trace(@src());
1129 defer tracy.end();1138 defer tracy.end();
11301139
1140 const decl = mod.declPtr(decl_index);
1131 const decl_name = try decl.getFullyQualifiedName(mod);1141 const decl_name = try decl.getFullyQualifiedName(mod);
1132 defer wasm.base.allocator.free(decl_name);1142 defer wasm.base.allocator.free(decl_name);
11331143
1134 log.debug("updateDeclLineNumber {s}{*}", .{ decl_name, decl });1144 log.debug("updateDeclLineNumber {s}{*}", .{ decl_name, decl });
1135 try dw.updateDeclLineNumber(decl);1145 try dw.updateDeclLineNumber(mod, decl_index);
1136 }1146 }
1137}1147}
11381148
1139fn finishUpdateDecl(wasm: *Wasm, decl: *Module.Decl, code: []const u8) !void {1149fn finishUpdateDecl(wasm: *Wasm, decl_index: Module.Decl.Index, code: []const u8) !void {
1140 const mod = wasm.base.options.module.?;1150 const mod = wasm.base.options.module.?;
1141 const atom: *Atom = &decl.link.wasm;1151 const decl = mod.declPtr(decl_index);
1152 const atom_index = wasm.decls.get(decl_index).?;
1153 const atom = wasm.getAtomPtr(atom_index);
1142 const symbol = &wasm.symbols.items[atom.sym_index];1154 const symbol = &wasm.symbols.items[atom.sym_index];
1143 const full_name = try decl.getFullyQualifiedName(mod);1155 const full_name = try decl.getFullyQualifiedName(mod);
1144 defer wasm.base.allocator.free(full_name);1156 defer wasm.base.allocator.free(full_name);
...@@ -1204,48 +1216,51 @@ pub fn lowerUnnamedConst(wasm: *Wasm, tv: TypedValue, decl_index: Module.Decl.In...@@ -1204,48 +1216,51 @@ pub fn lowerUnnamedConst(wasm: *Wasm, tv: TypedValue, decl_index: Module.Decl.In
1204 const decl = mod.declPtr(decl_index);1216 const decl = mod.declPtr(decl_index);
12051217
1206 // Create and initialize a new local symbol and atom1218 // Create and initialize a new local symbol and atom
1207 const local_index = decl.link.wasm.locals.items.len;1219 const atom_index = try wasm.createAtom();
1220 const parent_atom_index = try wasm.getOrCreateAtomForDecl(decl_index);
1221 const parent_atom = wasm.getAtomPtr(parent_atom_index);
1222 const local_index = parent_atom.locals.items.len;
1223 try parent_atom.locals.append(wasm.base.allocator, atom_index);
1208 const fqdn = try decl.getFullyQualifiedName(mod);1224 const fqdn = try decl.getFullyQualifiedName(mod);
1209 defer wasm.base.allocator.free(fqdn);1225 defer wasm.base.allocator.free(fqdn);
1210 const name = try std.fmt.allocPrintZ(wasm.base.allocator, "__unnamed_{s}_{d}", .{ fqdn, local_index });1226 const name = try std.fmt.allocPrintZ(wasm.base.allocator, "__unnamed_{s}_{d}", .{ fqdn, local_index });
1211 defer wasm.base.allocator.free(name);1227 defer wasm.base.allocator.free(name);
1212
1213 const atom = try decl.link.wasm.locals.addOne(wasm.base.allocator);
1214 atom.* = Atom.empty;
1215 try atom.ensureInitialized(wasm);
1216 atom.alignment = tv.ty.abiAlignment(wasm.base.options.target);
1217 wasm.symbols.items[atom.sym_index] = .{
1218 .name = try wasm.string_table.put(wasm.base.allocator, name),
1219 .flags = @enumToInt(Symbol.Flag.WASM_SYM_BINDING_LOCAL),
1220 .tag = .data,
1221 .index = undefined,
1222 };
1223
1224 try wasm.resolved_symbols.putNoClobber(wasm.base.allocator, atom.symbolLoc(), {});
1225
1226 var value_bytes = std.ArrayList(u8).init(wasm.base.allocator);1228 var value_bytes = std.ArrayList(u8).init(wasm.base.allocator);
1227 defer value_bytes.deinit();1229 defer value_bytes.deinit();
12281230
1229 const result = try codegen.generateSymbol(1231 const code = code: {
1230 &wasm.base,1232 const atom = wasm.getAtomPtr(atom_index);
1231 decl.srcLoc(),1233 atom.alignment = tv.ty.abiAlignment(wasm.base.options.target);
1232 tv,1234 wasm.symbols.items[atom.sym_index] = .{
1233 &value_bytes,1235 .name = try wasm.string_table.put(wasm.base.allocator, name),
1234 .none,1236 .flags = @enumToInt(Symbol.Flag.WASM_SYM_BINDING_LOCAL),
1235 .{1237 .tag = .data,
1236 .parent_atom_index = atom.sym_index,1238 .index = undefined,
1237 .addend = null,1239 };
1238 },1240 try wasm.resolved_symbols.putNoClobber(wasm.base.allocator, atom.symbolLoc(), {});
1239 );1241
1240 const code = switch (result) {1242 const result = try codegen.generateSymbol(
1241 .ok => value_bytes.items,1243 &wasm.base,
1242 .fail => |em| {1244 decl.srcLoc(),
1243 decl.analysis = .codegen_failure;1245 tv,
1244 try mod.failed_decls.put(mod.gpa, decl_index, em);1246 &value_bytes,
1245 return error.AnalysisFail;1247 .none,
1246 },1248 .{
1249 .parent_atom_index = atom.sym_index,
1250 .addend = null,
1251 },
1252 );
1253 break :code switch (result) {
1254 .ok => value_bytes.items,
1255 .fail => |em| {
1256 decl.analysis = .codegen_failure;
1257 try mod.failed_decls.put(mod.gpa, decl_index, em);
1258 return error.AnalysisFail;
1259 },
1260 };
1247 };1261 };
12481262
1263 const atom = wasm.getAtomPtr(atom_index);
1249 atom.size = @intCast(u32, code.len);1264 atom.size = @intCast(u32, code.len);
1250 try atom.code.appendSlice(wasm.base.allocator, code);1265 try atom.code.appendSlice(wasm.base.allocator, code);
1251 return atom.sym_index;1266 return atom.sym_index;
...@@ -1293,10 +1308,13 @@ pub fn getDeclVAddr(...@@ -1293,10 +1308,13 @@ pub fn getDeclVAddr(
1293) !u64 {1308) !u64 {
1294 const mod = wasm.base.options.module.?;1309 const mod = wasm.base.options.module.?;
1295 const decl = mod.declPtr(decl_index);1310 const decl = mod.declPtr(decl_index);
1296 try decl.link.wasm.ensureInitialized(wasm);1311
1297 const target_symbol_index = decl.link.wasm.sym_index;1312 const target_atom_index = try wasm.getOrCreateAtomForDecl(decl_index);
1313 const target_symbol_index = wasm.getAtom(target_atom_index).sym_index;
1314
1298 assert(reloc_info.parent_atom_index != 0);1315 assert(reloc_info.parent_atom_index != 0);
1299 const atom = wasm.symbol_atom.get(.{ .file = null, .index = reloc_info.parent_atom_index }).?;1316 const atom_index = wasm.symbol_atom.get(.{ .file = null, .index = reloc_info.parent_atom_index }).?;
1317 const atom = wasm.getAtomPtr(atom_index);
1300 const is_wasm32 = wasm.base.options.target.cpu.arch == .wasm32;1318 const is_wasm32 = wasm.base.options.target.cpu.arch == .wasm32;
1301 if (decl.ty.zigTypeTag() == .Fn) {1319 if (decl.ty.zigTypeTag() == .Fn) {
1302 assert(reloc_info.addend == 0); // addend not allowed for function relocations1320 assert(reloc_info.addend == 0); // addend not allowed for function relocations
...@@ -1324,9 +1342,10 @@ pub fn getDeclVAddr(...@@ -1324,9 +1342,10 @@ pub fn getDeclVAddr(
1324 return target_symbol_index;1342 return target_symbol_index;
1325}1343}
13261344
1327pub fn deleteExport(wasm: *Wasm, exp: Export) void {1345pub fn deleteDeclExport(wasm: *Wasm, decl_index: Module.Decl.Index) void {
1328 if (wasm.llvm_object) |_| return;1346 if (wasm.llvm_object) |_| return;
1329 const sym_index = exp.sym_index orelse return;1347 const atom_index = wasm.decls.get(decl_index) orelse return;
1348 const sym_index = wasm.getAtom(atom_index).sym_index;
1330 const loc: SymbolLoc = .{ .file = null, .index = sym_index };1349 const loc: SymbolLoc = .{ .file = null, .index = sym_index };
1331 const symbol = loc.getSymbol(wasm);1350 const symbol = loc.getSymbol(wasm);
1332 const symbol_name = wasm.string_table.get(symbol.name);1351 const symbol_name = wasm.string_table.get(symbol.name);
...@@ -1352,7 +1371,8 @@ pub fn updateDeclExports(...@@ -1352,7 +1371,8 @@ pub fn updateDeclExports(
1352 }1371 }
13531372
1354 const decl = mod.declPtr(decl_index);1373 const decl = mod.declPtr(decl_index);
1355 if (decl.link.wasm.getSymbolIndex() == null) return; // unititialized1374 const atom_index = try wasm.getOrCreateAtomForDecl(decl_index);
1375 const atom = wasm.getAtom(atom_index);
13561376
1357 for (exports) |exp| {1377 for (exports) |exp| {
1358 if (exp.options.section) |section| {1378 if (exp.options.section) |section| {
...@@ -1367,7 +1387,7 @@ pub fn updateDeclExports(...@@ -1367,7 +1387,7 @@ pub fn updateDeclExports(
13671387
1368 const export_name = try wasm.string_table.put(wasm.base.allocator, exp.options.name);1388 const export_name = try wasm.string_table.put(wasm.base.allocator, exp.options.name);
1369 if (wasm.globals.getPtr(export_name)) |existing_loc| {1389 if (wasm.globals.getPtr(export_name)) |existing_loc| {
1370 if (existing_loc.index == decl.link.wasm.sym_index) continue;1390 if (existing_loc.index == atom.sym_index) continue;
1371 const existing_sym: Symbol = existing_loc.getSymbol(wasm).*;1391 const existing_sym: Symbol = existing_loc.getSymbol(wasm).*;
13721392
1373 const exp_is_weak = exp.options.linkage == .Internal or exp.options.linkage == .Weak;1393 const exp_is_weak = exp.options.linkage == .Internal or exp.options.linkage == .Weak;
...@@ -1388,15 +1408,16 @@ pub fn updateDeclExports(...@@ -1388,15 +1408,16 @@ pub fn updateDeclExports(
1388 } else if (exp_is_weak) {1408 } else if (exp_is_weak) {
1389 continue; // to-be-exported symbol is weak, so we keep the existing symbol1409 continue; // to-be-exported symbol is weak, so we keep the existing symbol
1390 } else {1410 } else {
1391 existing_loc.index = decl.link.wasm.sym_index;1411 // TODO: Revisit this, why was this needed?
1412 existing_loc.index = atom.sym_index;
1392 existing_loc.file = null;1413 existing_loc.file = null;
1393 exp.link.wasm.sym_index = existing_loc.index;1414 // exp.link.wasm.sym_index = existing_loc.index;
1394 }1415 }
1395 }1416 }
13961417
1397 const exported_decl = mod.declPtr(exp.exported_decl);1418 const exported_atom_index = try wasm.getOrCreateAtomForDecl(exp.exported_decl);
1398 const sym_index = exported_decl.link.wasm.sym_index;1419 const exported_atom = wasm.getAtom(exported_atom_index);
1399 const sym_loc = exported_decl.link.wasm.symbolLoc();1420 const sym_loc = exported_atom.symbolLoc();
1400 const symbol = sym_loc.getSymbol(wasm);1421 const symbol = sym_loc.getSymbol(wasm);
1401 switch (exp.options.linkage) {1422 switch (exp.options.linkage) {
1402 .Internal => {1423 .Internal => {
...@@ -1432,7 +1453,6 @@ pub fn updateDeclExports(...@@ -1432,7 +1453,6 @@ pub fn updateDeclExports(
1432 // if the symbol was previously undefined, remove it as an import1453 // if the symbol was previously undefined, remove it as an import
1433 _ = wasm.imports.remove(sym_loc);1454 _ = wasm.imports.remove(sym_loc);
1434 _ = wasm.undefs.swapRemove(exp.options.name);1455 _ = wasm.undefs.swapRemove(exp.options.name);
1435 exp.link.wasm.sym_index = sym_index;
1436 }1456 }
1437}1457}
14381458
...@@ -1442,11 +1462,13 @@ pub fn freeDecl(wasm: *Wasm, decl_index: Module.Decl.Index) void {...@@ -1442,11 +1462,13 @@ pub fn freeDecl(wasm: *Wasm, decl_index: Module.Decl.Index) void {
1442 }1462 }
1443 const mod = wasm.base.options.module.?;1463 const mod = wasm.base.options.module.?;
1444 const decl = mod.declPtr(decl_index);1464 const decl = mod.declPtr(decl_index);
1445 const atom = &decl.link.wasm;1465 const atom_index = wasm.decls.get(decl_index).?;
1466 const atom = wasm.getAtomPtr(atom_index);
1446 wasm.symbols_free_list.append(wasm.base.allocator, atom.sym_index) catch {};1467 wasm.symbols_free_list.append(wasm.base.allocator, atom.sym_index) catch {};
1447 _ = wasm.decls.remove(decl_index);1468 _ = wasm.decls.remove(decl_index);
1448 wasm.symbols.items[atom.sym_index].tag = .dead;1469 wasm.symbols.items[atom.sym_index].tag = .dead;
1449 for (atom.locals.items) |local_atom| {1470 for (atom.locals.items) |local_atom_index| {
1471 const local_atom = wasm.getAtom(local_atom_index);
1450 const local_symbol = &wasm.symbols.items[local_atom.sym_index];1472 const local_symbol = &wasm.symbols.items[local_atom.sym_index];
1451 local_symbol.tag = .dead; // also for any local symbol1473 local_symbol.tag = .dead; // also for any local symbol
1452 wasm.symbols_free_list.append(wasm.base.allocator, local_atom.sym_index) catch {};1474 wasm.symbols_free_list.append(wasm.base.allocator, local_atom.sym_index) catch {};
...@@ -1460,12 +1482,20 @@ pub fn freeDecl(wasm: *Wasm, decl_index: Module.Decl.Index) void {...@@ -1460,12 +1482,20 @@ pub fn freeDecl(wasm: *Wasm, decl_index: Module.Decl.Index) void {
1460 _ = wasm.resolved_symbols.swapRemove(atom.symbolLoc());1482 _ = wasm.resolved_symbols.swapRemove(atom.symbolLoc());
1461 _ = wasm.symbol_atom.remove(atom.symbolLoc());1483 _ = wasm.symbol_atom.remove(atom.symbolLoc());
14621484
1463 if (wasm.dwarf) |*dwarf| {1485 // if (wasm.dwarf) |*dwarf| {
1464 dwarf.freeDecl(decl);1486 // dwarf.freeDecl(decl_index);
1465 dwarf.freeAtom(&atom.dbg_info_atom);1487 // }
1466 }
14671488
1468 atom.deinit(wasm.base.allocator);1489 if (atom.next) |next_atom_index| {
1490 const next_atom = wasm.getAtomPtr(next_atom_index);
1491 next_atom.prev = atom.prev;
1492 atom.next = null;
1493 }
1494 if (atom.prev) |prev_index| {
1495 const prev_atom = wasm.getAtomPtr(prev_index);
1496 prev_atom.next = atom.next;
1497 atom.prev = null;
1498 }
1469}1499}
14701500
1471/// Appends a new entry to the indirect function table1501/// Appends a new entry to the indirect function table
...@@ -1572,7 +1602,7 @@ const Kind = union(enum) {...@@ -1572,7 +1602,7 @@ const Kind = union(enum) {
1572 initialized,1602 initialized,
1573 synthetic,1603 synthetic,
1574 },1604 },
1575 function: FnData,1605 function: void,
15761606
1577 /// Returns the segment name the data kind represents.1607 /// Returns the segment name the data kind represents.
1578 /// Asserts `kind` has its active tag set to `data`.1608 /// Asserts `kind` has its active tag set to `data`.
...@@ -1587,15 +1617,17 @@ const Kind = union(enum) {...@@ -1587,15 +1617,17 @@ const Kind = union(enum) {
1587};1617};
15881618
1589/// Parses an Atom and inserts its metadata into the corresponding sections.1619/// Parses an Atom and inserts its metadata into the corresponding sections.
1590fn parseAtom(wasm: *Wasm, atom: *Atom, kind: Kind) !void {1620fn parseAtom(wasm: *Wasm, atom_index: Atom.Index, kind: Kind) !void {
1621 const atom = wasm.getAtomPtr(atom_index);
1591 const symbol = (SymbolLoc{ .file = null, .index = atom.sym_index }).getSymbol(wasm);1622 const symbol = (SymbolLoc{ .file = null, .index = atom.sym_index }).getSymbol(wasm);
1592 const final_index: u32 = switch (kind) {1623 const final_index: u32 = switch (kind) {
1593 .function => |fn_data| result: {1624 .function => result: {
1594 const index = @intCast(u32, wasm.functions.count() + wasm.imported_functions_count);1625 const index = @intCast(u32, wasm.functions.count() + wasm.imported_functions_count);
1626 const type_index = wasm.atom_types.get(atom_index).?;
1595 try wasm.functions.putNoClobber(1627 try wasm.functions.putNoClobber(
1596 wasm.base.allocator,1628 wasm.base.allocator,
1597 .{ .file = null, .index = index },1629 .{ .file = null, .index = index },
1598 .{ .type_index = fn_data.type_index },1630 .{ .type_index = type_index },
1599 );1631 );
1600 symbol.tag = .function;1632 symbol.tag = .function;
1601 symbol.index = index;1633 symbol.index = index;
...@@ -1662,18 +1694,20 @@ fn parseAtom(wasm: *Wasm, atom: *Atom, kind: Kind) !void {...@@ -1662,18 +1694,20 @@ fn parseAtom(wasm: *Wasm, atom: *Atom, kind: Kind) !void {
1662 const segment: *Segment = &wasm.segments.items[final_index];1694 const segment: *Segment = &wasm.segments.items[final_index];
1663 segment.alignment = std.math.max(segment.alignment, atom.alignment);1695 segment.alignment = std.math.max(segment.alignment, atom.alignment);
16641696
1665 try wasm.appendAtomAtIndex(final_index, atom);1697 try wasm.appendAtomAtIndex(final_index, atom_index);
1666}1698}
16671699
1668/// From a given index, append the given `Atom` at the back of the linked list.1700/// From a given index, append the given `Atom` at the back of the linked list.
1669/// Simply inserts it into the map of atoms when it doesn't exist yet.1701/// Simply inserts it into the map of atoms when it doesn't exist yet.
1670pub fn appendAtomAtIndex(wasm: *Wasm, index: u32, atom: *Atom) !void {1702pub fn appendAtomAtIndex(wasm: *Wasm, index: u32, atom_index: Atom.Index) !void {
1671 if (wasm.atoms.getPtr(index)) |last| {1703 const atom = wasm.getAtomPtr(atom_index);
1672 last.*.next = atom;1704 if (wasm.atoms.getPtr(index)) |last_index_ptr| {
1673 atom.prev = last.*;1705 const last = wasm.getAtomPtr(last_index_ptr.*);
1674 last.* = atom;1706 last.*.next = atom_index;
1707 atom.prev = last_index_ptr.*;
1708 last_index_ptr.* = atom_index;
1675 } else {1709 } else {
1676 try wasm.atoms.putNoClobber(wasm.base.allocator, index, atom);1710 try wasm.atoms.putNoClobber(wasm.base.allocator, index, atom_index);
1677 }1711 }
1678}1712}
16791713
...@@ -1683,16 +1717,17 @@ fn allocateDebugAtoms(wasm: *Wasm) !void {...@@ -1683,16 +1717,17 @@ fn allocateDebugAtoms(wasm: *Wasm) !void {
1683 if (wasm.dwarf == null) return;1717 if (wasm.dwarf == null) return;
16841718
1685 const allocAtom = struct {1719 const allocAtom = struct {
1686 fn f(bin: *Wasm, maybe_index: *?u32, atom: *Atom) !void {1720 fn f(bin: *Wasm, maybe_index: *?u32, atom_index: Atom.Index) !void {
1687 const index = maybe_index.* orelse idx: {1721 const index = maybe_index.* orelse idx: {
1688 const index = @intCast(u32, bin.segments.items.len);1722 const index = @intCast(u32, bin.segments.items.len);
1689 try bin.appendDummySegment();1723 try bin.appendDummySegment();
1690 maybe_index.* = index;1724 maybe_index.* = index;
1691 break :idx index;1725 break :idx index;
1692 };1726 };
1727 const atom = bin.getAtomPtr(atom_index);
1693 atom.size = @intCast(u32, atom.code.items.len);1728 atom.size = @intCast(u32, atom.code.items.len);
1694 bin.symbols.items[atom.sym_index].index = index;1729 bin.symbols.items[atom.sym_index].index = index;
1695 try bin.appendAtomAtIndex(index, atom);1730 try bin.appendAtomAtIndex(index, atom_index);
1696 }1731 }
1697 }.f;1732 }.f;
16981733
...@@ -1714,15 +1749,16 @@ fn allocateAtoms(wasm: *Wasm) !void {...@@ -1714,15 +1749,16 @@ fn allocateAtoms(wasm: *Wasm) !void {
1714 var it = wasm.atoms.iterator();1749 var it = wasm.atoms.iterator();
1715 while (it.next()) |entry| {1750 while (it.next()) |entry| {
1716 const segment = &wasm.segments.items[entry.key_ptr.*];1751 const segment = &wasm.segments.items[entry.key_ptr.*];
1717 var atom: *Atom = entry.value_ptr.*.getFirst();1752 var atom_index = entry.value_ptr.*;
1718 var offset: u32 = 0;1753 var offset: u32 = 0;
1719 while (true) {1754 while (true) {
1755 const atom = wasm.getAtomPtr(atom_index);
1720 const symbol_loc = atom.symbolLoc();1756 const symbol_loc = atom.symbolLoc();
1721 if (wasm.code_section_index) |index| {1757 if (wasm.code_section_index) |index| {
1722 if (index == entry.key_ptr.*) {1758 if (index == entry.key_ptr.*) {
1723 if (!wasm.resolved_symbols.contains(symbol_loc)) {1759 if (!wasm.resolved_symbols.contains(symbol_loc)) {
1724 // only allocate resolved function body's.1760 // only allocate resolved function body's.
1725 atom = atom.next orelse break;1761 atom_index = atom.prev orelse break;
1726 continue;1762 continue;
1727 }1763 }
1728 }1764 }
...@@ -1736,8 +1772,7 @@ fn allocateAtoms(wasm: *Wasm) !void {...@@ -1736,8 +1772,7 @@ fn allocateAtoms(wasm: *Wasm) !void {
1736 atom.size,1772 atom.size,
1737 });1773 });
1738 offset += atom.size;1774 offset += atom.size;
1739 try wasm.symbol_atom.put(wasm.base.allocator, symbol_loc, atom); // Update atom pointers1775 atom_index = atom.prev orelse break;
1740 atom = atom.next orelse break;
1741 }1776 }
1742 segment.size = std.mem.alignForwardGeneric(u32, offset, segment.alignment);1777 segment.size = std.mem.alignForwardGeneric(u32, offset, segment.alignment);
1743 }1778 }
...@@ -1871,8 +1906,8 @@ fn initializeCallCtorsFunction(wasm: *Wasm) !void {...@@ -1871,8 +1906,8 @@ fn initializeCallCtorsFunction(wasm: *Wasm) !void {
1871 symbol.index = func_index;1906 symbol.index = func_index;
18721907
1873 // create the atom that will be output into the final binary1908 // create the atom that will be output into the final binary
1874 const atom = try wasm.base.allocator.create(Atom);1909 const atom_index = @intCast(Atom.Index, wasm.managed_atoms.items.len);
1875 errdefer wasm.base.allocator.destroy(atom);1910 const atom = try wasm.managed_atoms.addOne(wasm.base.allocator);
1876 atom.* = .{1911 atom.* = .{
1877 .size = @intCast(u32, function_body.items.len),1912 .size = @intCast(u32, function_body.items.len),
1878 .offset = 0,1913 .offset = 0,
...@@ -1882,15 +1917,14 @@ fn initializeCallCtorsFunction(wasm: *Wasm) !void {...@@ -1882,15 +1917,14 @@ fn initializeCallCtorsFunction(wasm: *Wasm) !void {
1882 .next = null,1917 .next = null,
1883 .prev = null,1918 .prev = null,
1884 .code = function_body.moveToUnmanaged(),1919 .code = function_body.moveToUnmanaged(),
1885 .dbg_info_atom = undefined,
1886 };1920 };
1887 try wasm.managed_atoms.append(wasm.base.allocator, atom);1921 try wasm.appendAtomAtIndex(wasm.code_section_index.?, atom_index);
1888 try wasm.appendAtomAtIndex(wasm.code_section_index.?, atom);1922 try wasm.symbol_atom.putNoClobber(wasm.base.allocator, loc, atom_index);
1889 try wasm.symbol_atom.putNoClobber(wasm.base.allocator, loc, atom);
18901923
1891 // `allocateAtoms` has already been called, set the atom's offset manually.1924 // `allocateAtoms` has already been called, set the atom's offset manually.
1892 // This is fine to do manually as we insert the atom at the very end.1925 // This is fine to do manually as we insert the atom at the very end.
1893 atom.offset = atom.prev.?.offset + atom.prev.?.size;1926 const prev_atom = wasm.getAtom(atom.prev.?);
1927 atom.offset = prev_atom.offset + prev_atom.size;
1894}1928}
18951929
1896fn setupImports(wasm: *Wasm) !void {1930fn setupImports(wasm: *Wasm) !void {
...@@ -2093,7 +2127,8 @@ fn setupExports(wasm: *Wasm) !void {...@@ -2093,7 +2127,8 @@ fn setupExports(wasm: *Wasm) !void {
2093 break :blk try wasm.string_table.put(wasm.base.allocator, sym_name);2127 break :blk try wasm.string_table.put(wasm.base.allocator, sym_name);
2094 };2128 };
2095 const exp: types.Export = if (symbol.tag == .data) exp: {2129 const exp: types.Export = if (symbol.tag == .data) exp: {
2096 const atom = wasm.symbol_atom.get(sym_loc).?;2130 const atom_index = wasm.symbol_atom.get(sym_loc).?;
2131 const atom = wasm.getAtom(atom_index);
2097 const va = atom.getVA(wasm, symbol);2132 const va = atom.getVA(wasm, symbol);
2098 const global_index = @intCast(u32, wasm.imported_globals_count + wasm.wasm_globals.items.len);2133 const global_index = @intCast(u32, wasm.imported_globals_count + wasm.wasm_globals.items.len);
2099 try wasm.wasm_globals.append(wasm.base.allocator, .{2134 try wasm.wasm_globals.append(wasm.base.allocator, .{
...@@ -2198,7 +2233,8 @@ fn setupMemory(wasm: *Wasm) !void {...@@ -2198,7 +2233,8 @@ fn setupMemory(wasm: *Wasm) !void {
2198 const segment_index = wasm.data_segments.get(".synthetic").?;2233 const segment_index = wasm.data_segments.get(".synthetic").?;
2199 const segment = &wasm.segments.items[segment_index];2234 const segment = &wasm.segments.items[segment_index];
2200 segment.offset = 0; // for simplicity we store the entire VA into atom's offset.2235 segment.offset = 0; // for simplicity we store the entire VA into atom's offset.
2201 const atom = wasm.symbol_atom.get(loc).?;2236 const atom_index = wasm.symbol_atom.get(loc).?;
2237 const atom = wasm.getAtomPtr(atom_index);
2202 atom.offset = @intCast(u32, mem.alignForwardGeneric(u64, memory_ptr, heap_alignment));2238 atom.offset = @intCast(u32, mem.alignForwardGeneric(u64, memory_ptr, heap_alignment));
2203 }2239 }
22042240
...@@ -2231,7 +2267,8 @@ fn setupMemory(wasm: *Wasm) !void {...@@ -2231,7 +2267,8 @@ fn setupMemory(wasm: *Wasm) !void {
2231 const segment_index = wasm.data_segments.get(".synthetic").?;2267 const segment_index = wasm.data_segments.get(".synthetic").?;
2232 const segment = &wasm.segments.items[segment_index];2268 const segment = &wasm.segments.items[segment_index];
2233 segment.offset = 0;2269 segment.offset = 0;
2234 const atom = wasm.symbol_atom.get(loc).?;2270 const atom_index = wasm.symbol_atom.get(loc).?;
2271 const atom = wasm.getAtomPtr(atom_index);
2235 atom.offset = @intCast(u32, memory_ptr);2272 atom.offset = @intCast(u32, memory_ptr);
2236 }2273 }
22372274
...@@ -2357,15 +2394,14 @@ pub fn getErrorTableSymbol(wasm: *Wasm) !u32 {...@@ -2357,15 +2394,14 @@ pub fn getErrorTableSymbol(wasm: *Wasm) !u32 {
2357 // and then return said symbol's index. The final table will be populated2394 // and then return said symbol's index. The final table will be populated
2358 // during `flush` when we know all possible error names.2395 // during `flush` when we know all possible error names.
23592396
2360 // As sym_index '0' is reserved, we use it for our stack pointer symbol2397 const atom_index = try wasm.createAtom();
2361 const symbol_index = wasm.symbols_free_list.popOrNull() orelse blk: {2398 const atom = wasm.getAtomPtr(atom_index);
2362 const index = @intCast(u32, wasm.symbols.items.len);2399 const slice_ty = Type.initTag(.const_slice_u8_sentinel_0);
2363 _ = try wasm.symbols.addOne(wasm.base.allocator);2400 atom.alignment = slice_ty.abiAlignment(wasm.base.options.target);
2364 break :blk index;2401 const sym_index = atom.sym_index;
2365 };
23662402
2367 const sym_name = try wasm.string_table.put(wasm.base.allocator, "__zig_err_name_table");2403 const sym_name = try wasm.string_table.put(wasm.base.allocator, "__zig_err_name_table");
2368 const symbol = &wasm.symbols.items[symbol_index];2404 const symbol = &wasm.symbols.items[sym_index];
2369 symbol.* = .{2405 symbol.* = .{
2370 .name = sym_name,2406 .name = sym_name,
2371 .tag = .data,2407 .tag = .data,
...@@ -2374,20 +2410,11 @@ pub fn getErrorTableSymbol(wasm: *Wasm) !u32 {...@@ -2374,20 +2410,11 @@ pub fn getErrorTableSymbol(wasm: *Wasm) !u32 {
2374 };2410 };
2375 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);2411 symbol.setFlag(.WASM_SYM_VISIBILITY_HIDDEN);
23762412
2377 const slice_ty = Type.initTag(.const_slice_u8_sentinel_0);2413 try wasm.resolved_symbols.put(wasm.base.allocator, atom.symbolLoc(), {});
23782414
2379 const atom = try wasm.base.allocator.create(Atom);2415 log.debug("Error name table was created with symbol index: ({d})", .{sym_index});
2380 atom.* = Atom.empty;2416 wasm.error_table_symbol = sym_index;
2381 atom.sym_index = symbol_index;2417 return sym_index;
2382 atom.alignment = slice_ty.abiAlignment(wasm.base.options.target);
2383 try wasm.managed_atoms.append(wasm.base.allocator, atom);
2384 const loc = atom.symbolLoc();
2385 try wasm.resolved_symbols.put(wasm.base.allocator, loc, {});
2386 try wasm.symbol_atom.put(wasm.base.allocator, loc, atom);
2387
2388 log.debug("Error name table was created with symbol index: ({d})", .{symbol_index});
2389 wasm.error_table_symbol = symbol_index;
2390 return symbol_index;
2391}2418}
23922419
2393/// Populates the error name table, when `error_table_symbol` is not null.2420/// Populates the error name table, when `error_table_symbol` is not null.
...@@ -2396,22 +2423,17 @@ pub fn getErrorTableSymbol(wasm: *Wasm) !u32 {...@@ -2396,22 +2423,17 @@ pub fn getErrorTableSymbol(wasm: *Wasm) !u32 {
2396/// The table is what is being pointed to within the runtime bodies that are generated.2423/// The table is what is being pointed to within the runtime bodies that are generated.
2397fn populateErrorNameTable(wasm: *Wasm) !void {2424fn populateErrorNameTable(wasm: *Wasm) !void {
2398 const symbol_index = wasm.error_table_symbol orelse return;2425 const symbol_index = wasm.error_table_symbol orelse return;
2399 const atom: *Atom = wasm.symbol_atom.get(.{ .file = null, .index = symbol_index }).?;2426 const atom_index = wasm.symbol_atom.get(.{ .file = null, .index = symbol_index }).?;
2427 const atom = wasm.getAtomPtr(atom_index);
2428
2400 // Rather than creating a symbol for each individual error name,2429 // Rather than creating a symbol for each individual error name,
2401 // we create a symbol for the entire region of error names. We then calculate2430 // we create a symbol for the entire region of error names. We then calculate
2402 // the pointers into the list using addends which are appended to the relocation.2431 // the pointers into the list using addends which are appended to the relocation.
2403 const names_atom = try wasm.base.allocator.create(Atom);2432 const names_atom_index = try wasm.createAtom();
2404 names_atom.* = Atom.empty;2433 const names_atom = wasm.getAtomPtr(names_atom_index);
2405 try wasm.managed_atoms.append(wasm.base.allocator, names_atom);
2406 const names_symbol_index = wasm.symbols_free_list.popOrNull() orelse blk: {
2407 const index = @intCast(u32, wasm.symbols.items.len);
2408 _ = try wasm.symbols.addOne(wasm.base.allocator);
2409 break :blk index;
2410 };
2411 names_atom.sym_index = names_symbol_index;
2412 names_atom.alignment = 1;2434 names_atom.alignment = 1;
2413 const sym_name = try wasm.string_table.put(wasm.base.allocator, "__zig_err_names");2435 const sym_name = try wasm.string_table.put(wasm.base.allocator, "__zig_err_names");
2414 const names_symbol = &wasm.symbols.items[names_symbol_index];2436 const names_symbol = &wasm.symbols.items[names_atom.sym_index];
2415 names_symbol.* = .{2437 names_symbol.* = .{
2416 .name = sym_name,2438 .name = sym_name,
2417 .tag = .data,2439 .tag = .data,
...@@ -2435,7 +2457,7 @@ fn populateErrorNameTable(wasm: *Wasm) !void {...@@ -2435,7 +2457,7 @@ fn populateErrorNameTable(wasm: *Wasm) !void {
2435 try atom.code.writer(wasm.base.allocator).writeIntLittle(u32, len - 1);2457 try atom.code.writer(wasm.base.allocator).writeIntLittle(u32, len - 1);
2436 // create relocation to the error name2458 // create relocation to the error name
2437 try atom.relocs.append(wasm.base.allocator, .{2459 try atom.relocs.append(wasm.base.allocator, .{
2438 .index = names_symbol_index,2460 .index = names_atom.sym_index,
2439 .relocation_type = .R_WASM_MEMORY_ADDR_I32,2461 .relocation_type = .R_WASM_MEMORY_ADDR_I32,
2440 .offset = offset,2462 .offset = offset,
2441 .addend = @intCast(i32, addend),2463 .addend = @intCast(i32, addend),
...@@ -2454,61 +2476,53 @@ fn populateErrorNameTable(wasm: *Wasm) !void {...@@ -2454,61 +2476,53 @@ fn populateErrorNameTable(wasm: *Wasm) !void {
24542476
2455 const name_loc = names_atom.symbolLoc();2477 const name_loc = names_atom.symbolLoc();
2456 try wasm.resolved_symbols.put(wasm.base.allocator, name_loc, {});2478 try wasm.resolved_symbols.put(wasm.base.allocator, name_loc, {});
2457 try wasm.symbol_atom.put(wasm.base.allocator, name_loc, names_atom);2479 try wasm.symbol_atom.put(wasm.base.allocator, name_loc, names_atom_index);
24582480
2459 // link the atoms with the rest of the binary so they can be allocated2481 // link the atoms with the rest of the binary so they can be allocated
2460 // and relocations will be performed.2482 // and relocations will be performed.
2461 try wasm.parseAtom(atom, .{ .data = .read_only });2483 try wasm.parseAtom(atom_index, .{ .data = .read_only });
2462 try wasm.parseAtom(names_atom, .{ .data = .read_only });2484 try wasm.parseAtom(names_atom_index, .{ .data = .read_only });
2463}2485}
24642486
2465/// From a given index variable, creates a new debug section.2487/// From a given index variable, creates a new debug section.
2466/// This initializes the index, appends a new segment,2488/// This initializes the index, appends a new segment,
2467/// and finally, creates a managed `Atom`.2489/// and finally, creates a managed `Atom`.
2468pub fn createDebugSectionForIndex(wasm: *Wasm, index: *?u32, name: []const u8) !*Atom {2490pub fn createDebugSectionForIndex(wasm: *Wasm, index: *?u32, name: []const u8) !Atom.Index {
2469 const new_index = @intCast(u32, wasm.segments.items.len);2491 const new_index = @intCast(u32, wasm.segments.items.len);
2470 index.* = new_index;2492 index.* = new_index;
2471 try wasm.appendDummySegment();2493 try wasm.appendDummySegment();
24722494
2473 const sym_index = wasm.symbols_free_list.popOrNull() orelse idx: {2495 const atom_index = try wasm.createAtom();
2474 const tmp_index = @intCast(u32, wasm.symbols.items.len);2496 const atom = wasm.getAtomPtr(atom_index);
2475 _ = try wasm.symbols.addOne(wasm.base.allocator);2497 wasm.symbols.items[atom.sym_index] = .{
2476 break :idx tmp_index;
2477 };
2478 wasm.symbols.items[sym_index] = .{
2479 .tag = .section,2498 .tag = .section,
2480 .name = try wasm.string_table.put(wasm.base.allocator, name),2499 .name = try wasm.string_table.put(wasm.base.allocator, name),
2481 .index = 0,2500 .index = 0,
2482 .flags = @enumToInt(Symbol.Flag.WASM_SYM_BINDING_LOCAL),2501 .flags = @enumToInt(Symbol.Flag.WASM_SYM_BINDING_LOCAL),
2483 };2502 };
24842503
2485 const atom = try wasm.base.allocator.create(Atom);
2486 atom.* = Atom.empty;
2487 atom.alignment = 1; // debug sections are always 1-byte-aligned2504 atom.alignment = 1; // debug sections are always 1-byte-aligned
2488 atom.sym_index = sym_index;2505 return atom_index;
2489 try wasm.managed_atoms.append(wasm.base.allocator, atom);
2490 try wasm.symbol_atom.put(wasm.base.allocator, atom.symbolLoc(), atom);
2491 return atom;
2492}2506}
24932507
2494fn resetState(wasm: *Wasm) void {2508fn resetState(wasm: *Wasm) void {
2495 for (wasm.segment_info.values()) |segment_info| {2509 for (wasm.segment_info.values()) |segment_info| {
2496 wasm.base.allocator.free(segment_info.name);2510 wasm.base.allocator.free(segment_info.name);
2497 }2511 }
2498 if (wasm.base.options.module) |mod| {2512
2499 var decl_it = wasm.decls.keyIterator();2513 var atom_it = wasm.decls.valueIterator();
2500 while (decl_it.next()) |decl_index_ptr| {2514 while (atom_it.next()) |atom_index| {
2501 const decl = mod.declPtr(decl_index_ptr.*);2515 const atom = wasm.getAtomPtr(atom_index.*);
2502 const atom = &decl.link.wasm;2516 atom.next = null;
2503 atom.next = null;2517 atom.prev = null;
2504 atom.prev = null;2518
25052519 for (atom.locals.items) |local_atom_index| {
2506 for (atom.locals.items) |*local_atom| {2520 const local_atom = wasm.getAtomPtr(local_atom_index);
2507 local_atom.next = null;2521 local_atom.next = null;
2508 local_atom.prev = null;2522 local_atom.prev = null;
2509 }
2510 }2523 }
2511 }2524 }
2525
2512 wasm.functions.clearRetainingCapacity();2526 wasm.functions.clearRetainingCapacity();
2513 wasm.exports.clearRetainingCapacity();2527 wasm.exports.clearRetainingCapacity();
2514 wasm.segments.clearRetainingCapacity();2528 wasm.segments.clearRetainingCapacity();
...@@ -2805,28 +2819,29 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod...@@ -2805,28 +2819,29 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod
2805 try wasm.setupStart();2819 try wasm.setupStart();
2806 try wasm.setupImports();2820 try wasm.setupImports();
2807 if (wasm.base.options.module) |mod| {2821 if (wasm.base.options.module) |mod| {
2808 var decl_it = wasm.decls.keyIterator();2822 var decl_it = wasm.decls.iterator();
2809 while (decl_it.next()) |decl_index_ptr| {2823 while (decl_it.next()) |entry| {
2810 const decl = mod.declPtr(decl_index_ptr.*);2824 const decl = mod.declPtr(entry.key_ptr.*);
2811 if (decl.isExtern()) continue;2825 if (decl.isExtern()) continue;
2812 const atom = &decl.*.link.wasm;2826 const atom_index = entry.value_ptr.*;
2813 if (decl.ty.zigTypeTag() == .Fn) {2827 if (decl.ty.zigTypeTag() == .Fn) {
2814 try wasm.parseAtom(atom, .{ .function = decl.fn_link.wasm });2828 try wasm.parseAtom(atom_index, .function);
2815 } else if (decl.getVariable()) |variable| {2829 } else if (decl.getVariable()) |variable| {
2816 if (!variable.is_mutable) {2830 if (!variable.is_mutable) {
2817 try wasm.parseAtom(atom, .{ .data = .read_only });2831 try wasm.parseAtom(atom_index, .{ .data = .read_only });
2818 } else if (variable.init.isUndefDeep()) {2832 } else if (variable.init.isUndefDeep()) {
2819 try wasm.parseAtom(atom, .{ .data = .uninitialized });2833 try wasm.parseAtom(atom_index, .{ .data = .uninitialized });
2820 } else {2834 } else {
2821 try wasm.parseAtom(atom, .{ .data = .initialized });2835 try wasm.parseAtom(atom_index, .{ .data = .initialized });
2822 }2836 }
2823 } else {2837 } else {
2824 try wasm.parseAtom(atom, .{ .data = .read_only });2838 try wasm.parseAtom(atom_index, .{ .data = .read_only });
2825 }2839 }
28262840
2827 // also parse atoms for a decl's locals2841 // also parse atoms for a decl's locals
2828 for (atom.locals.items) |*local_atom| {2842 const atom = wasm.getAtomPtr(atom_index);
2829 try wasm.parseAtom(local_atom, .{ .data = .read_only });2843 for (atom.locals.items) |local_atom_index| {
2844 try wasm.parseAtom(local_atom_index, .{ .data = .read_only });
2830 }2845 }
2831 }2846 }
28322847
...@@ -3071,20 +3086,22 @@ fn writeToFile(...@@ -3071,20 +3086,22 @@ fn writeToFile(
3071 var code_section_size: u32 = 0;3086 var code_section_size: u32 = 0;
3072 if (wasm.code_section_index) |code_index| {3087 if (wasm.code_section_index) |code_index| {
3073 const header_offset = try reserveVecSectionHeader(&binary_bytes);3088 const header_offset = try reserveVecSectionHeader(&binary_bytes);
3074 var atom: *Atom = wasm.atoms.get(code_index).?.getFirst();3089 var atom_index = wasm.atoms.get(code_index).?;
30753090
3076 // The code section must be sorted in line with the function order.3091 // The code section must be sorted in line with the function order.
3077 var sorted_atoms = try std.ArrayList(*Atom).initCapacity(wasm.base.allocator, wasm.functions.count());3092 var sorted_atoms = try std.ArrayList(*Atom).initCapacity(wasm.base.allocator, wasm.functions.count());
3078 defer sorted_atoms.deinit();3093 defer sorted_atoms.deinit();
30793094
3080 while (true) {3095 while (true) {
3096 var atom = wasm.getAtomPtr(atom_index);
3081 if (wasm.resolved_symbols.contains(atom.symbolLoc())) {3097 if (wasm.resolved_symbols.contains(atom.symbolLoc())) {
3082 if (!is_obj) {3098 if (!is_obj) {
3083 atom.resolveRelocs(wasm);3099 atom.resolveRelocs(wasm);
3084 }3100 }
3085 sorted_atoms.appendAssumeCapacity(atom);3101 sorted_atoms.appendAssumeCapacity(atom);
3086 }3102 }
3087 atom = atom.next orelse break;3103 // atom = if (atom.prev) |prev| wasm.getAtomPtr(prev) else break;
3104 atom_index = atom.prev orelse break;
3088 }3105 }
30893106
3090 const atom_sort_fn = struct {3107 const atom_sort_fn = struct {
...@@ -3124,11 +3141,11 @@ fn writeToFile(...@@ -3124,11 +3141,11 @@ fn writeToFile(
3124 // do not output 'bss' section unless we import memory and therefore3141 // do not output 'bss' section unless we import memory and therefore
3125 // want to guarantee the data is zero initialized3142 // want to guarantee the data is zero initialized
3126 if (!import_memory and std.mem.eql(u8, entry.key_ptr.*, ".bss")) continue;3143 if (!import_memory and std.mem.eql(u8, entry.key_ptr.*, ".bss")) continue;
3127 const atom_index = entry.value_ptr.*;3144 const segment_index = entry.value_ptr.*;
3128 const segment = wasm.segments.items[atom_index];3145 const segment = wasm.segments.items[segment_index];
3129 if (segment.size == 0) continue; // do not emit empty segments3146 if (segment.size == 0) continue; // do not emit empty segments
3130 segment_count += 1;3147 segment_count += 1;
3131 var atom: *Atom = wasm.atoms.getPtr(atom_index).?.*.getFirst();3148 var atom_index = wasm.atoms.get(segment_index).?;
31323149
3133 // flag and index to memory section (currently, there can only be 1 memory section in wasm)3150 // flag and index to memory section (currently, there can only be 1 memory section in wasm)
3134 try leb.writeULEB128(binary_writer, @as(u32, 0));3151 try leb.writeULEB128(binary_writer, @as(u32, 0));
...@@ -3139,6 +3156,7 @@ fn writeToFile(...@@ -3139,6 +3156,7 @@ fn writeToFile(
3139 // fill in the offset table and the data segments3156 // fill in the offset table and the data segments
3140 var current_offset: u32 = 0;3157 var current_offset: u32 = 0;
3141 while (true) {3158 while (true) {
3159 const atom = wasm.getAtomPtr(atom_index);
3142 if (!is_obj) {3160 if (!is_obj) {
3143 atom.resolveRelocs(wasm);3161 atom.resolveRelocs(wasm);
3144 }3162 }
...@@ -3154,8 +3172,8 @@ fn writeToFile(...@@ -3154,8 +3172,8 @@ fn writeToFile(
3154 try binary_writer.writeAll(atom.code.items);3172 try binary_writer.writeAll(atom.code.items);
31553173
3156 current_offset += atom.size;3174 current_offset += atom.size;
3157 if (atom.next) |next| {3175 if (atom.prev) |prev| {
3158 atom = next;3176 atom_index = prev;
3159 } else {3177 } else {
3160 // also pad with zeroes when last atom to ensure3178 // also pad with zeroes when last atom to ensure
3161 // segments are aligned.3179 // segments are aligned.
...@@ -3197,15 +3215,15 @@ fn writeToFile(...@@ -3197,15 +3215,15 @@ fn writeToFile(
3197 }3215 }
31983216
3199 if (!wasm.base.options.strip) {3217 if (!wasm.base.options.strip) {
3200 if (wasm.dwarf) |*dwarf| {3218 // if (wasm.dwarf) |*dwarf| {
3201 const mod = wasm.base.options.module.?;3219 // const mod = wasm.base.options.module.?;
3202 try dwarf.writeDbgAbbrev();3220 // try dwarf.writeDbgAbbrev();
3203 // for debug info and ranges, the address is always 0,3221 // // for debug info and ranges, the address is always 0,
3204 // as locations are always offsets relative to 'code' section.3222 // // as locations are always offsets relative to 'code' section.
3205 try dwarf.writeDbgInfoHeader(mod, 0, code_section_size);3223 // try dwarf.writeDbgInfoHeader(mod, 0, code_section_size);
3206 try dwarf.writeDbgAranges(0, code_section_size);3224 // try dwarf.writeDbgAranges(0, code_section_size);
3207 try dwarf.writeDbgLineHeader();3225 // try dwarf.writeDbgLineHeader();
3208 }3226 // }
32093227
3210 var debug_bytes = std.ArrayList(u8).init(wasm.base.allocator);3228 var debug_bytes = std.ArrayList(u8).init(wasm.base.allocator);
3211 defer debug_bytes.deinit();3229 defer debug_bytes.deinit();
...@@ -3228,11 +3246,11 @@ fn writeToFile(...@@ -3228,11 +3246,11 @@ fn writeToFile(
32283246
3229 for (debug_sections) |item| {3247 for (debug_sections) |item| {
3230 if (item.index) |index| {3248 if (item.index) |index| {
3231 var atom = wasm.atoms.get(index).?.getFirst();3249 var atom = wasm.getAtomPtr(wasm.atoms.get(index).?);
3232 while (true) {3250 while (true) {
3233 atom.resolveRelocs(wasm);3251 atom.resolveRelocs(wasm);
3234 try debug_bytes.appendSlice(atom.code.items);3252 try debug_bytes.appendSlice(atom.code.items);
3235 atom = atom.next orelse break;3253 atom = if (atom.prev) |prev| wasm.getAtomPtr(prev) else break;
3236 }3254 }
3237 try emitDebugSection(&binary_bytes, debug_bytes.items, item.name);3255 try emitDebugSection(&binary_bytes, debug_bytes.items, item.name);
3238 debug_bytes.clearRetainingCapacity();3256 debug_bytes.clearRetainingCapacity();
...@@ -3964,7 +3982,8 @@ fn emitSymbolTable(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), symbol_table:...@@ -3964,7 +3982,8 @@ fn emitSymbolTable(wasm: *Wasm, binary_bytes: *std.ArrayList(u8), symbol_table:
39643982
3965 if (symbol.isDefined()) {3983 if (symbol.isDefined()) {
3966 try leb.writeULEB128(writer, symbol.index);3984 try leb.writeULEB128(writer, symbol.index);
3967 const atom = wasm.symbol_atom.get(sym_loc).?;3985 const atom_index = wasm.symbol_atom.get(sym_loc).?;
3986 const atom = wasm.getAtom(atom_index);
3968 try leb.writeULEB128(writer, @as(u32, atom.offset));3987 try leb.writeULEB128(writer, @as(u32, atom.offset));
3969 try leb.writeULEB128(writer, @as(u32, atom.size));3988 try leb.writeULEB128(writer, @as(u32, atom.size));
3970 }3989 }
...@@ -4042,7 +4061,7 @@ fn emitCodeRelocations(...@@ -4042,7 +4061,7 @@ fn emitCodeRelocations(
4042 const reloc_start = binary_bytes.items.len;4061 const reloc_start = binary_bytes.items.len;
40434062
4044 var count: u32 = 0;4063 var count: u32 = 0;
4045 var atom: *Atom = wasm.atoms.get(code_index).?.getFirst();4064 var atom: *Atom = wasm.getAtomPtr(wasm.atoms.get(code_index).?);
4046 // for each atom, we calculate the uleb size and append that4065 // for each atom, we calculate the uleb size and append that
4047 var size_offset: u32 = 5; // account for code section size leb1284066 var size_offset: u32 = 5; // account for code section size leb128
4048 while (true) {4067 while (true) {
...@@ -4060,7 +4079,7 @@ fn emitCodeRelocations(...@@ -4060,7 +4079,7 @@ fn emitCodeRelocations(
4060 }4079 }
4061 log.debug("Emit relocation: {}", .{relocation});4080 log.debug("Emit relocation: {}", .{relocation});
4062 }4081 }
4063 atom = atom.next orelse break;4082 atom = if (atom.prev) |prev| wasm.getAtomPtr(prev) else break;
4064 }4083 }
4065 if (count == 0) return;4084 if (count == 0) return;
4066 var buf: [5]u8 = undefined;4085 var buf: [5]u8 = undefined;
...@@ -4091,7 +4110,7 @@ fn emitDataRelocations(...@@ -4091,7 +4110,7 @@ fn emitDataRelocations(
4091 // for each atom, we calculate the uleb size and append that4110 // for each atom, we calculate the uleb size and append that
4092 var size_offset: u32 = 5; // account for code section size leb1284111 var size_offset: u32 = 5; // account for code section size leb128
4093 for (wasm.data_segments.values()) |segment_index| {4112 for (wasm.data_segments.values()) |segment_index| {
4094 var atom: *Atom = wasm.atoms.get(segment_index).?.getFirst();4113 var atom: *Atom = wasm.getAtomPtr(wasm.atoms.get(segment_index).?);
4095 while (true) {4114 while (true) {
4096 size_offset += getULEB128Size(atom.size);4115 size_offset += getULEB128Size(atom.size);
4097 for (atom.relocs.items) |relocation| {4116 for (atom.relocs.items) |relocation| {
...@@ -4110,7 +4129,7 @@ fn emitDataRelocations(...@@ -4110,7 +4129,7 @@ fn emitDataRelocations(
4110 }4129 }
4111 log.debug("Emit relocation: {}", .{relocation});4130 log.debug("Emit relocation: {}", .{relocation});
4112 }4131 }
4113 atom = atom.next orelse break;4132 atom = if (atom.prev) |prev| wasm.getAtomPtr(prev) else break;
4114 }4133 }
4115 }4134 }
4116 if (count == 0) return;4135 if (count == 0) return;
...@@ -4149,3 +4168,13 @@ pub fn putOrGetFuncType(wasm: *Wasm, func_type: std.wasm.Type) !u32 {...@@ -4149,3 +4168,13 @@ pub fn putOrGetFuncType(wasm: *Wasm, func_type: std.wasm.Type) !u32 {
4149 });4168 });
4150 return index;4169 return index;
4151}4170}
4171
4172/// For the given `decl_index`, stores the corresponding type representing the function signature.
4173/// Asserts declaration has an associated `Atom`.
4174/// Returns the index into the list of types.
4175pub fn storeDeclType(wasm: *Wasm, decl_index: Module.Decl.Index, func_type: std.wasm.Type) !u32 {
4176 const atom_index = wasm.decls.get(decl_index).?;
4177 const index = try wasm.putOrGetFuncType(func_type);
4178 try wasm.atom_types.put(wasm.base.allocator, atom_index, index);
4179 return index;
4180}
src/link/Wasm/Atom.zig+19-28
...@@ -4,7 +4,6 @@ const std = @import("std");...@@ -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,24 +79,11 @@ pub fn format(atom: Atom, comptime fmt: []const u8, options: std.fmt.FormatOptio...@@ -83,24 +79,11 @@ 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
98pub fn ensureInitialized(atom: *Atom, wasm_bin: *Wasm) !void {
99 if (atom.getSymbolIndex() != null) return; // already initialized
100 atom.sym_index = try wasm_bin.allocateSymbol();
101 try wasm_bin.symbol_atom.putNoClobber(wasm_bin.base.allocator, atom.symbolLoc(), atom);
102}
103
104pub fn getSymbolIndex(atom: Atom) ?u32 {87pub fn getSymbolIndex(atom: Atom) ?u32 {
105 if (atom.sym_index == 0) return null;88 if (atom.sym_index == 0) return null;
106 return atom.sym_index;89 return atom.sym_index;
...@@ -203,20 +186,28 @@ fn relocationValue(atom: Atom, relocation: types.Relocation, wasm_bin: *const Wa...@@ -203,20 +186,28 @@ fn relocationValue(atom: Atom, relocation: types.Relocation, wasm_bin: *const Wa
203 if (symbol.isUndefined()) {186 if (symbol.isUndefined()) {
204 return 0;187 return 0;
205 }188 }
206 const target_atom = wasm_bin.symbol_atom.get(target_loc).?;189 const target_atom_index = wasm_bin.symbol_atom.get(target_loc) orelse {
190 // this can only occur during incremental-compilation when a relocation
191 // still points to a freed decl. It is fine to emit the value 0 here
192 // as no actual code will point towards it.
193 return 0;
194 };
195 const target_atom = wasm_bin.getAtom(target_atom_index);
207 const va = @intCast(i32, target_atom.getVA(wasm_bin, symbol));196 const va = @intCast(i32, target_atom.getVA(wasm_bin, symbol));
208 return @intCast(u32, va + relocation.addend);197 return @intCast(u32, va + relocation.addend);
209 },198 },
210 .R_WASM_EVENT_INDEX_LEB => return symbol.index,199 .R_WASM_EVENT_INDEX_LEB => return symbol.index,
211 .R_WASM_SECTION_OFFSET_I32 => {200 .R_WASM_SECTION_OFFSET_I32 => {
212 const target_atom = wasm_bin.symbol_atom.get(target_loc).?;201 const target_atom_index = wasm_bin.symbol_atom.get(target_loc).?;
202 const target_atom = wasm_bin.getAtom(target_atom_index);
213 const rel_value = @intCast(i32, target_atom.offset) + relocation.addend;203 const rel_value = @intCast(i32, target_atom.offset) + relocation.addend;
214 return @intCast(u32, rel_value);204 return @intCast(u32, rel_value);
215 },205 },
216 .R_WASM_FUNCTION_OFFSET_I32 => {206 .R_WASM_FUNCTION_OFFSET_I32 => {
217 const target_atom = wasm_bin.symbol_atom.get(target_loc) orelse {207 const target_atom_index = wasm_bin.symbol_atom.get(target_loc) orelse {
218 return @bitCast(u32, @as(i32, -1));208 return @bitCast(u32, @as(i32, -1));
219 };209 };
210 const target_atom = wasm_bin.getAtom(target_atom_index);
220 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)
221 const rel_value = @intCast(i32, target_atom.offset + offset) + relocation.addend;212 const rel_value = @intCast(i32, target_atom.offset + offset) + relocation.addend;
222 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+15-12
...@@ -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];
...@@ -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-585
...@@ -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,536 +6682,3 @@ pub const Type = extern union {...@@ -6689,536 +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 => 4,
7055
7056 .aarch64_32,
7057 .amdgcn,
7058 .amdil64,
7059 .bpfel,
7060 .bpfeb,
7061 .hexagon,
7062 .hsail64,
7063 .loongarch64,
7064 .m68k,
7065 .mips,
7066 .mipsel,
7067 .sparc,
7068 .sparcel,
7069 .sparc64,
7070 .lanai,
7071 .le64,
7072 .nvptx,
7073 .nvptx64,
7074 .r600,
7075 .s390x,
7076 .spir64,
7077 .spirv64,
7078 .renderscript64,
7079 => 8,
7080
7081 .aarch64,
7082 .aarch64_be,
7083 .mips64,
7084 .mips64el,
7085 .powerpc,
7086 .powerpcle,
7087 .powerpc64,
7088 .powerpc64le,
7089 .riscv32,
7090 .riscv64,
7091 .x86_64,
7092 .wasm32,
7093 .wasm64,
7094 => 16,
7095 },
7096 );
7097 }
7098
7099 pub fn preferredAlignment(self: CType, target: Target) u16 {
7100
7101 // Overrides for unusual alignments
7102 switch (target.cpu.arch) {
7103 .arm, .armeb, .thumb, .thumbeb => switch (target.os.tag) {
7104 .netbsd => switch (target.abi) {
7105 .gnueabi,
7106 .gnueabihf,
7107 .eabi,
7108 .eabihf,
7109 .android,
7110 .musleabi,
7111 .musleabihf,
7112 => {},
7113
7114 else => switch (self) {
7115 .longdouble => return 4,
7116 else => {},
7117 },
7118 },
7119 .ios, .tvos, .watchos => switch (self) {
7120 .longdouble => return 4,
7121 else => {},
7122 },
7123 else => {},
7124 },
7125 .arc => switch (self) {
7126 .longdouble => return 4,
7127 else => {},
7128 },
7129 .avr => switch (self) {
7130 .int, .uint, .long, .ulong, .float, .longdouble => return 1,
7131 .short, .ushort => return 2,
7132 .double => return 4,
7133 .longlong, .ulonglong => return 8,
7134 },
7135 .x86 => switch (target.os.tag) {
7136 .windows, .uefi => switch (self) {
7137 .longdouble => switch (target.abi) {
7138 .gnu, .gnuilp32, .cygnus => return 4,
7139 else => return 8,
7140 },
7141 else => {},
7142 },
7143 else => switch (self) {
7144 .longdouble => return 4,
7145 else => {},
7146 },
7147 },
7148 else => {},
7149 }
7150
7151 // Next-power-of-two-aligned, up to a maximum.
7152 return @min(
7153 std.math.ceilPowerOfTwoAssert(u16, (self.sizeInBits(target) + 7) / 8),
7154 switch (target.cpu.arch) {
7155 .msp430 => @as(u16, 2),
7156
7157 .csky,
7158 .xcore,
7159 .dxil,
7160 .loongarch32,
7161 .tce,
7162 .tcele,
7163 .le32,
7164 .amdil,
7165 .hsail,
7166 .spir,
7167 .spirv32,
7168 .kalimba,
7169 .shave,
7170 .renderscript32,
7171 .ve,
7172 .spu_2,
7173 => 4,
7174
7175 .arc,
7176 .arm,
7177 .armeb,
7178 .avr,
7179 .thumb,
7180 .thumbeb,
7181 .aarch64_32,
7182 .amdgcn,
7183 .amdil64,
7184 .bpfel,
7185 .bpfeb,
7186 .hexagon,
7187 .hsail64,
7188 .x86,
7189 .loongarch64,
7190 .m68k,
7191 .mips,
7192 .mipsel,
7193 .sparc,
7194 .sparcel,
7195 .sparc64,
7196 .lanai,
7197 .le64,
7198 .nvptx,
7199 .nvptx64,
7200 .r600,
7201 .s390x,
7202 .spir64,
7203 .spirv64,
7204 .renderscript64,
7205 => 8,
7206
7207 .aarch64,
7208 .aarch64_be,
7209 .mips64,
7210 .mips64el,
7211 .powerpc,
7212 .powerpcle,
7213 .powerpc64,
7214 .powerpc64le,
7215 .riscv32,
7216 .riscv64,
7217 .x86_64,
7218 .wasm32,
7219 .wasm64,
7220 => 16,
7221 },
7222 );
7223 }
7224};
stage1/zig1.wasm
Binary files a/stage1/zig1.wasm and b/stage1/zig1.wasm differ
test/behavior/basic.zig+18
...@@ -1125,3 +1125,21 @@ test "returning an opaque type from a function" {...@@ -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/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/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/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/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.zig+1
...@@ -102,4 +102,5 @@ pub fn addCases(cases: *tests.StandaloneContext) void {...@@ -102,4 +102,5 @@ pub fn addCases(cases: *tests.StandaloneContext) void {
102 cases.addBuildFile("test/standalone/issue_13030/build.zig", .{ .build_modes = true });102 cases.addBuildFile("test/standalone/issue_13030/build.zig", .{ .build_modes = true });
103 cases.addBuildFile("test/standalone/emit_asm_and_bin/build.zig", .{});103 cases.addBuildFile("test/standalone/emit_asm_and_bin/build.zig", .{});
104 cases.addBuildFile("test/standalone/issue_12588/build.zig", .{});104 cases.addBuildFile("test/standalone/issue_12588/build.zig", .{});
105 cases.addBuildFile("test/standalone/embed_generated_file/build.zig", .{});
105}106}
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/embed_generated_file/bootloader.zig created+1
...@@ -0,0 +1 @@
1pub export fn _start() void {}
test/standalone/embed_generated_file/build.zig created+28
...@@ -0,0 +1,28 @@
1const std = @import("std");
2
3pub fn build(b: *std.Build) void {
4 const target = b.standardTargetOptions(.{});
5 const optimize = b.standardOptimizeOption(.{});
6
7 const bootloader = b.addExecutable(.{
8 .name = "bootloader",
9 .root_source_file = .{ .path = "bootloader.zig" },
10 .target = .{
11 .cpu_arch = .x86,
12 .os_tag = .freestanding,
13 },
14 .optimize = .ReleaseSmall,
15 });
16
17 const exe = b.addTest(.{
18 .root_source_file = .{ .path = "main.zig" },
19 .target = target,
20 .optimize = optimize,
21 });
22 exe.addAnonymousModule("bootloader.elf", .{
23 .source_file = bootloader.getOutputSource(),
24 });
25
26 const test_step = b.step("test", "Test the program");
27 test_step.dependOn(&exe.step);
28}
test/standalone/embed_generated_file/main.zig created+8
...@@ -0,0 +1,8 @@
1const std = @import("std");
2const blah = @embedFile("bootloader.elf");
3
4test {
5 comptime {
6 std.debug.assert(std.mem.eql(u8, blah[1..][0..3], "ELF"));
7 }
8}
test/standalone/emit_asm_and_bin/build.zig+6-4
...@@ -1,8 +1,10 @@...@@ -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.addAnonymousModule("my_pkg", .{ .source_file = .{ .path = "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}
tools/generate_linux_syscalls.zig+25
...@@ -167,6 +167,31 @@ pub fn main() !void {...@@ -167,6 +167,31 @@ pub fn main() !void {
167167
168 try writer.writeAll("};\n\n");168 try writer.writeAll("};\n\n");
169 }169 }
170 {
171 try writer.writeAll(
172 \\pub const Mips64 = enum(usize) {
173 \\ pub const Linux = 5000;
174 \\
175 \\
176 );
177
178 const table = try linux_dir.readFile("arch/mips/kernel/syscalls/syscall_n64.tbl", buf);
179 var lines = mem.tokenize(u8, table, "\n");
180 while (lines.next()) |line| {
181 if (line[0] == '#') continue;
182
183 var fields = mem.tokenize(u8, line, " \t");
184 const number = fields.next() orelse return error.Incomplete;
185 // abi is always n64
186 _ = fields.next() orelse return error.Incomplete;
187 const name = fields.next() orelse return error.Incomplete;
188 const fixed_name = if (stdlib_renames.get(name)) |fixed| fixed else name;
189
190 try writer.print(" {s} = Linux + {s},\n", .{ zig.fmtId(fixed_name), number });
191 }
192
193 try writer.writeAll("};\n\n");
194 }
170 {195 {
171 try writer.writeAll("pub const PowerPC = enum(usize) {\n");196 try writer.writeAll("pub const PowerPC = enum(usize) {\n");
172197
tools/update_cpu_features.zig+1-1
...@@ -1306,7 +1306,7 @@ fn usageAndExit(file: fs.File, arg0: []const u8, code: u8) noreturn {...@@ -1306,7 +1306,7 @@ fn usageAndExit(file: fs.File, arg0: []const u8, code: u8) noreturn {
1306 \\1306 \\
1307 \\Updates lib/std/target/<target>.zig from llvm/lib/Target/<Target>/<Target>.td .1307 \\Updates lib/std/target/<target>.zig from llvm/lib/Target/<Target>/<Target>.td .
1308 \\1308 \\
1309 \\On a less beefy system, or when debugging, compile with --single-threaded.1309 \\On a less beefy system, or when debugging, compile with -fsingle-threaded.
1310 \\1310 \\
1311 , .{arg0}) catch std.process.exit(1);1311 , .{arg0}) catch std.process.exit(1);
1312 std.process.exit(code);1312 std.process.exit(code);