From 4f742c4cfc3c3134a0d6ebfdfc354286ae97b2c1 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Fri, 19 Jul 2024 01:04:59 -0400 Subject: [PATCH] dev: introduce dev environments that enable compiler feature sets --- CMakeLists.txt | 1 + bootstrap.c | 3 +- build.zig | 10 +- src/Compilation.zig | 144 ++++++++++++------------- src/Zcu.zig | 10 +- src/Zcu/PerThread.zig | 14 ++- src/codegen.zig | 50 ++++++--- src/codegen/llvm.zig | 1 - src/dev.zig | 238 ++++++++++++++++++++++++++++++++++++++++++ src/link.zig | 77 +++++++------- src/link/Coff/lld.zig | 15 ++- src/link/Elf.zig | 3 + src/link/Wasm.zig | 3 + src/main.zig | 181 +++++++++++++++++--------------- src/mingw.zig | 4 +- stage1/config.zig.in | 3 +- 16 files changed, 520 insertions(+), 237 deletions(-) create mode 100644 src/dev.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index ac3a7e5fbe..f9436d7c2a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -578,6 +578,7 @@ set(ZIG_STAGE2_SOURCES src/codegen/spirv/Section.zig src/codegen/spirv/spec.zig src/crash_report.zig + src/dev.zig src/glibc.zig src/introspect.zig src/libcxx.zig diff --git a/bootstrap.c b/bootstrap.c index 529d3f737a..f62fc858eb 100644 --- a/bootstrap.c +++ b/bootstrap.c @@ -140,8 +140,7 @@ int main(int argc, char **argv) { "pub const value_tracing = false;\n" "pub const skip_non_native = false;\n" "pub const force_gpa = false;\n" - "pub const only_c = false;\n" - "pub const only_core_functionality = true;\n" + "pub const dev = .core;\n" , zig_version); if (written < 100) panic("unable to write to config.zig file"); diff --git a/build.zig b/build.zig index 148e374a5f..d6ba1e9330 100644 --- a/build.zig +++ b/build.zig @@ -8,6 +8,7 @@ const io = std.io; const fs = std.fs; const InstallDirectoryOptions = std.Build.InstallDirectoryOptions; const assert = std.debug.assert; +const DevEnv = @import("src/dev.zig").Env; const zig_version: std.SemanticVersion = .{ .major = 0, .minor = 14, .patch = 0 }; const stack_size = 32 * 1024 * 1024; @@ -232,8 +233,7 @@ pub fn build(b: *std.Build) !void { exe_options.addOption(bool, "llvm_has_arc", llvm_has_arc); exe_options.addOption(bool, "llvm_has_xtensa", llvm_has_xtensa); exe_options.addOption(bool, "force_gpa", force_gpa); - exe_options.addOption(bool, "only_c", only_c); - exe_options.addOption(bool, "only_core_functionality", only_c); + exe_options.addOption(DevEnv, "dev", b.option(DevEnv, "dev", "Build a compiler with a reduced feature set for development of specific features") orelse if (only_c) .bootstrap else .full); if (link_libc) { exe.linkLibC(); @@ -393,8 +393,6 @@ pub fn build(b: *std.Build) !void { test_cases_options.addOption(bool, "llvm_has_arc", llvm_has_arc); test_cases_options.addOption(bool, "llvm_has_xtensa", llvm_has_xtensa); test_cases_options.addOption(bool, "force_gpa", force_gpa); - test_cases_options.addOption(bool, "only_c", only_c); - test_cases_options.addOption(bool, "only_core_functionality", true); test_cases_options.addOption(bool, "enable_qemu", b.enable_qemu); test_cases_options.addOption(bool, "enable_wine", b.enable_wine); test_cases_options.addOption(bool, "enable_wasmtime", b.enable_wasmtime); @@ -406,6 +404,7 @@ pub fn build(b: *std.Build) !void { test_cases_options.addOption([:0]const u8, "version", version); test_cases_options.addOption(std.SemanticVersion, "semver", semver); test_cases_options.addOption([]const []const u8, "test_filters", test_filters); + test_cases_options.addOption(DevEnv, "dev", if (only_c) .bootstrap else .core); var chosen_opt_modes_buf: [4]builtin.OptimizeMode = undefined; var chosen_mode_index: usize = 0; @@ -575,7 +574,6 @@ fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void { exe_options.addOption(u32, "mem_leak_frames", 0); exe_options.addOption(bool, "have_llvm", false); exe_options.addOption(bool, "force_gpa", false); - exe_options.addOption(bool, "only_c", true); exe_options.addOption([:0]const u8, "version", version); exe_options.addOption(std.SemanticVersion, "semver", semver); exe_options.addOption(bool, "enable_debug_extensions", false); @@ -585,7 +583,7 @@ fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void { exe_options.addOption(bool, "enable_tracy_callstack", false); exe_options.addOption(bool, "enable_tracy_allocation", false); exe_options.addOption(bool, "value_tracing", false); - exe_options.addOption(bool, "only_core_functionality", true); + exe_options.addOption(DevEnv, "dev", .bootstrap); const run_opt = b.addSystemCommand(&.{ "wasm-opt", diff --git a/src/Compilation.zig b/src/Compilation.zig index 8243bc3620..b1c1851a6c 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -38,6 +38,7 @@ const Zir = std.zig.Zir; const Air = @import("Air.zig"); const Builtin = @import("Builtin.zig"); const LlvmObject = @import("codegen/llvm.zig").Object; +const dev = @import("dev.zig"); pub const Config = @import("Compilation/Config.zig"); @@ -94,8 +95,15 @@ native_system_include_paths: []const []const u8, force_undefined_symbols: std.StringArrayHashMapUnmanaged(void), c_object_table: std.AutoArrayHashMapUnmanaged(*CObject, void) = .{}, -win32_resource_table: if (build_options.only_core_functionality) void else std.AutoArrayHashMapUnmanaged(*Win32Resource, void) = - if (build_options.only_core_functionality) {} else .{}, +win32_resource_table: if (dev.env.supports(.win32_resource)) std.AutoArrayHashMapUnmanaged(*Win32Resource, void) else struct { + pub fn keys(_: @This()) [0]void { + return .{}; + } + pub fn count(_: @This()) u0 { + return 0; + } + pub fn deinit(_: @This(), _: Allocator) void {} +} = .{}, link_error_flags: link.File.ErrorFlags = .{}, link_errors: std.ArrayListUnmanaged(link.File.ErrorMsg) = .{}, @@ -125,7 +133,13 @@ c_object_work_queue: std.fifo.LinearFifo(*CObject, .Dynamic), /// These jobs are to invoke the RC compiler to create a compiled resource file (.res), which /// gets linked with the Compilation. -win32_resource_work_queue: if (build_options.only_core_functionality) void else std.fifo.LinearFifo(*Win32Resource, .Dynamic), +win32_resource_work_queue: if (dev.env.supports(.win32_resource)) std.fifo.LinearFifo(*Win32Resource, .Dynamic) else struct { + pub fn ensureUnusedCapacity(_: @This(), _: u0) error{}!void {} + pub fn readItem(_: @This()) ?noreturn { + return null; + } + pub fn deinit(_: @This()) void {} +}, /// These jobs are to tokenize, parse, and astgen files, which may be outdated /// since the last compilation, as well as scan for `@import` and queue up @@ -142,8 +156,12 @@ failed_c_objects: std.AutoArrayHashMapUnmanaged(*CObject, *CObject.Diag.Bundle) /// The ErrorBundle memory is owned by the `Win32Resource`, using Compilation's general purpose allocator. /// This data is accessed by multiple threads and is protected by `mutex`. -failed_win32_resources: if (build_options.only_core_functionality) void else std.AutoArrayHashMapUnmanaged(*Win32Resource, ErrorBundle) = - if (build_options.only_core_functionality) {} else .{}, +failed_win32_resources: if (dev.env.supports(.win32_resource)) std.AutoArrayHashMapUnmanaged(*Win32Resource, ErrorBundle) else struct { + pub fn values(_: @This()) [0]void { + return .{}; + } + pub fn deinit(_: @This(), _: Allocator) void {} +} = .{}, /// Miscellaneous things that can fail. misc_failures: std.AutoArrayHashMapUnmanaged(MiscTask, MiscError) = .{}, @@ -1484,7 +1502,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil .done = false, }, .c_object_work_queue = std.fifo.LinearFifo(*CObject, .Dynamic).init(gpa), - .win32_resource_work_queue = if (build_options.only_core_functionality) {} else std.fifo.LinearFifo(*Win32Resource, .Dynamic).init(gpa), + .win32_resource_work_queue = if (dev.env.supports(.win32_resource)) std.fifo.LinearFifo(*Win32Resource, .Dynamic).init(gpa) else .{}, .astgen_work_queue = std.fifo.LinearFifo(Zcu.File.Index, .Dynamic).init(gpa), .embed_file_work_queue = std.fifo.LinearFifo(*Zcu.EmbedFile, .Dynamic).init(gpa), .c_source_files = options.c_source_files, @@ -1711,7 +1729,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil comp.emit_llvm_ir != null or comp.emit_llvm_bc != null)) { - if (build_options.only_c) unreachable; + dev.check(.llvm_backend); if (opt_zcu) |zcu| zcu.llvm_object = try LlvmObject.create(arena, comp); } @@ -1738,8 +1756,11 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil } // Add a `Win32Resource` for each `rc_source_files` and one for `manifest_file`. - if (!build_options.only_core_functionality) { - try comp.win32_resource_table.ensureTotalCapacity(gpa, options.rc_source_files.len + @intFromBool(options.manifest_file != null)); + const win32_resource_count = + options.rc_source_files.len + @intFromBool(options.manifest_file != null); + if (win32_resource_count > 0) { + dev.check(.win32_resource); + try comp.win32_resource_table.ensureTotalCapacity(gpa, win32_resource_count); for (options.rc_source_files) |rc_source_file| { const win32_resource = try gpa.create(Win32Resource); errdefer gpa.destroy(win32_resource); @@ -1905,9 +1926,7 @@ pub fn destroy(comp: *Compilation) void { for (comp.work_queues) |work_queue| work_queue.deinit(); if (!InternPool.single_threaded) comp.codegen_work.queue.deinit(); comp.c_object_work_queue.deinit(); - if (!build_options.only_core_functionality) { - comp.win32_resource_work_queue.deinit(); - } + comp.win32_resource_work_queue.deinit(); comp.astgen_work_queue.deinit(); comp.embed_file_work_queue.deinit(); @@ -1956,17 +1975,15 @@ pub fn destroy(comp: *Compilation) void { } comp.failed_c_objects.deinit(gpa); - if (!build_options.only_core_functionality) { - for (comp.win32_resource_table.keys()) |key| { - key.destroy(gpa); - } - comp.win32_resource_table.deinit(gpa); - - for (comp.failed_win32_resources.values()) |*value| { - value.deinit(gpa); - } - comp.failed_win32_resources.deinit(gpa); + for (comp.win32_resource_table.keys()) |key| { + key.destroy(gpa); } + comp.win32_resource_table.deinit(gpa); + + for (comp.failed_win32_resources.values()) |*value| { + value.deinit(gpa); + } + comp.failed_win32_resources.deinit(gpa); for (comp.link_errors.items) |*item| item.deinit(gpa); comp.link_errors.deinit(gpa); @@ -2153,17 +2170,15 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void { // For compiling Win32 resources, we rely on the cache hash system to avoid duplicating work. // Add a Job for each Win32 resource file. - if (!build_options.only_core_functionality) { - try comp.win32_resource_work_queue.ensureUnusedCapacity(comp.win32_resource_table.count()); - for (comp.win32_resource_table.keys()) |key| { - comp.win32_resource_work_queue.writeItemAssumeCapacity(key); - } - if (comp.file_system_inputs) |fsi| { - for (comp.win32_resource_table.keys()) |win32_resource| switch (win32_resource.src) { - .rc => |f| try comp.appendFileSystemInput(fsi, Cache.Path.cwd(), f.src_path), - .manifest => continue, - }; - } + try comp.win32_resource_work_queue.ensureUnusedCapacity(comp.win32_resource_table.count()); + for (comp.win32_resource_table.keys()) |key| { + comp.win32_resource_work_queue.writeItemAssumeCapacity(key); + } + if (comp.file_system_inputs) |fsi| { + for (comp.win32_resource_table.keys()) |win32_resource| switch (win32_resource.src) { + .rc => |f| try comp.appendFileSystemInput(fsi, Cache.Path.cwd(), f.src_path), + .manifest => continue, + }; } if (comp.module) |zcu| { @@ -2397,7 +2412,6 @@ fn flush(comp: *Compilation, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: try link.File.C.flushEmitH(zcu); if (zcu.llvm_object) |llvm_object| { - if (build_options.only_c) unreachable; const default_emit = switch (comp.cache_use) { .whole => |whole| .{ .directory = whole.tmp_artifact_directory.?, @@ -2541,17 +2555,15 @@ fn addNonIncrementalStuffToCacheManifest( man.hash.addListOfBytes(key.src.extra_flags); } - if (!build_options.only_core_functionality) { - for (comp.win32_resource_table.keys()) |key| { - switch (key.src) { - .rc => |rc_src| { - _ = try man.addFile(rc_src.src_path, null); - man.hash.addListOfBytes(rc_src.extra_flags); - }, - .manifest => |manifest_path| { - _ = try man.addFile(manifest_path, null); - }, - } + for (comp.win32_resource_table.keys()) |key| { + switch (key.src) { + .rc => |rc_src| { + _ = try man.addFile(rc_src.src_path, null); + man.hash.addListOfBytes(rc_src.extra_flags); + }, + .manifest => |manifest_path| { + _ = try man.addFile(manifest_path, null); + }, } } @@ -2695,8 +2707,6 @@ pub fn emitLlvmObject( llvm_object: *LlvmObject, prog_node: std.Progress.Node, ) !void { - if (build_options.only_c) @compileError("unreachable"); - const sub_prog_node = prog_node.start("LLVM Emit Object", 0); defer sub_prog_node.end(); @@ -2860,6 +2870,7 @@ fn reportMultiModuleErrors(pt: Zcu.PerThread) !void { /// or whatever is needed so that it can be executed. /// After this, one must call` makeFileWritable` before calling `update`. pub fn makeBinFileExecutable(comp: *Compilation) !void { + if (!dev.env.supports(.make_executable)) return; const lf = comp.bin_file orelse return; return lf.makeExecutable(); } @@ -2897,6 +2908,8 @@ const Header = extern struct { /// saved, such as the target and most CLI flags. A cache hit will only occur /// when subsequent compiler invocations use the same set of flags. pub fn saveState(comp: *Compilation) !void { + dev.check(.incremental); + const lf = comp.bin_file orelse return; const gpa = comp.gpa; @@ -3001,10 +3014,8 @@ pub fn totalErrorCount(comp: *Compilation) u32 { total += bundle.diags.len; } - if (!build_options.only_core_functionality) { - for (comp.failed_win32_resources.values()) |errs| { - total += errs.errorMessageCount(); - } + for (comp.failed_win32_resources.values()) |errs| { + total += errs.errorMessageCount(); } if (comp.module) |zcu| { @@ -3082,10 +3093,8 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle { try diag_bundle.addToErrorBundle(&bundle); } - if (!build_options.only_core_functionality) { - for (comp.failed_win32_resources.values()) |error_bundle| { - try bundle.addBundleAsRoots(error_bundle); - } + for (comp.failed_win32_resources.values()) |error_bundle| { + try bundle.addBundleAsRoots(error_bundle); } for (comp.lld_errors.items) |lld_error| { @@ -3509,11 +3518,10 @@ fn performAllTheWorkInner( comp.work_queue_wait_group.reset(); defer comp.work_queue_wait_group.wait(); - if (!build_options.only_c and !build_options.only_core_functionality) { - if (comp.docs_emit != null) { - comp.thread_pool.spawnWg(&comp.work_queue_wait_group, workerDocsCopy, .{comp}); - comp.work_queue_wait_group.spawnManager(workerDocsWasm, .{ comp, main_progress_node }); - } + if (comp.docs_emit != null) { + dev.check(.docs_emit); + comp.thread_pool.spawnWg(&comp.work_queue_wait_group, workerDocsCopy, .{comp}); + comp.work_queue_wait_group.spawnManager(workerDocsWasm, .{ comp, main_progress_node }); } { @@ -3585,12 +3593,10 @@ fn performAllTheWorkInner( }); } - if (!build_options.only_core_functionality) { - while (comp.win32_resource_work_queue.readItem()) |win32_resource| { - comp.thread_pool.spawnWg(&comp.work_queue_wait_group, workerUpdateWin32Resource, .{ - comp, win32_resource, main_progress_node, - }); - } + while (comp.win32_resource_work_queue.readItem()) |win32_resource| { + comp.thread_pool.spawnWg(&comp.work_queue_wait_group, workerUpdateWin32Resource, .{ + comp, win32_resource, main_progress_node, + }); } } @@ -3867,9 +3873,6 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job, prog_node: std.Progre }; }, .windows_import_lib => |index| { - if (build_options.only_c) - @panic("building import libs not included in core functionality"); - const named_frame = tracy.namedFrame("windows_import_lib"); defer named_frame.end(); @@ -4466,7 +4469,8 @@ pub const CImportResult = struct { /// This API is currently coupled pretty tightly to stage1's needs; it will need to be reworked /// a bit when we want to start using it from self-hosted. pub fn cImport(comp: *Compilation, c_src: []const u8, owner_mod: *Package.Module) !CImportResult { - if (build_options.only_core_functionality) @panic("@cImport is not available in a zig2.c build"); + dev.check(.translate_c_command); + const tracy_trace = trace(@src()); defer tracy_trace.end(); diff --git a/src/Zcu.zig b/src/Zcu.zig index dbc2e5a2c4..41311234d2 100644 --- a/src/Zcu.zig +++ b/src/Zcu.zig @@ -38,6 +38,7 @@ const Alignment = InternPool.Alignment; const AnalUnit = InternPool.AnalUnit; const BuiltinFn = std.zig.BuiltinFn; const LlvmObject = @import("codegen/llvm.zig").Object; +const dev = @import("dev.zig"); comptime { @setEvalBranchQuota(4000); @@ -57,7 +58,7 @@ comp: *Compilation, /// Usually, the LlvmObject is managed by linker code, however, in the case /// that -fno-emit-bin is specified, the linker code never executes, so we /// store the LlvmObject here. -llvm_object: ?*LlvmObject, +llvm_object: if (dev.env.supports(.llvm_backend)) ?*LlvmObject else ?noreturn, /// Pointer to externally managed resource. root_mod: *Package.Module, @@ -2403,10 +2404,7 @@ pub fn deinit(zcu: *Zcu) void { const pt: Zcu.PerThread = .{ .tid = .main, .zcu = zcu }; const gpa = zcu.gpa; - if (zcu.llvm_object) |llvm_object| { - if (build_options.only_c) unreachable; - llvm_object.deinit(); - } + if (zcu.llvm_object) |llvm_object| llvm_object.deinit(); for (zcu.import_table.keys()) |key| { gpa.free(key); @@ -3041,7 +3039,7 @@ pub fn deleteUnitExports(zcu: *Zcu, anal_unit: AnalUnit) void { // `updateExports` on flush). // This case is needed because in some rare edge cases, `Sema` wants to add and delete exports // within a single update. - if (!build_options.only_c) { + if (dev.env.supports(.incremental)) { for (exports, exports_base..) |exp, export_idx| { if (zcu.comp.bin_file) |lf| { lf.deleteExport(exp.exported, exp.opts.name); diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig index 5781a46b65..b4c8c834f9 100644 --- a/src/Zcu/PerThread.zig +++ b/src/Zcu/PerThread.zig @@ -64,6 +64,7 @@ pub fn astGenFile( path_digest: Cache.BinDigest, opt_root_decl: Zcu.Decl.OptionalIndex, ) !void { + dev.check(.ast_gen); assert(!file.mod.isBuiltin()); const tracy = trace(@src()); @@ -504,6 +505,8 @@ pub fn ensureFileAnalyzed(pt: Zcu.PerThread, file_index: Zcu.File.Index) Zcu.Sem /// For example an inferred error set is not resolved until after `analyzeFnBody`. /// is called. pub fn ensureDeclAnalyzed(pt: Zcu.PerThread, decl_index: Zcu.Decl.Index) Zcu.SemaError!void { + dev.check(.sema); + const tracy = trace(@src()); defer tracy.end(); @@ -552,9 +555,9 @@ pub fn ensureDeclAnalyzed(pt: Zcu.PerThread, decl_index: Zcu.Decl.Index) Zcu.Sem } if (was_outdated) { + dev.check(.incremental); // The exports this Decl performs will be re-discovered, so we remove them here // prior to re-analysis. - if (build_options.only_c) unreachable; mod.deleteUnitExports(decl_as_depender); mod.deleteUnitReferences(decl_as_depender); } @@ -623,6 +626,8 @@ pub fn ensureDeclAnalyzed(pt: Zcu.PerThread, decl_index: Zcu.Decl.Index) Zcu.Sem } pub fn ensureFuncBodyAnalyzed(pt: Zcu.PerThread, maybe_coerced_func_index: InternPool.Index) Zcu.SemaError!void { + dev.check(.sema); + const tracy = trace(@src()); defer tracy.end(); @@ -684,7 +689,7 @@ pub fn ensureFuncBodyAnalyzed(pt: Zcu.PerThread, maybe_coerced_func_index: Inter zcu.potentially_outdated.swapRemove(func_as_depender); if (was_outdated) { - if (build_options.only_c) unreachable; + dev.check(.incremental); _ = zcu.outdated_ready.swapRemove(func_as_depender); zcu.deleteUnitExports(func_as_depender); zcu.deleteUnitReferences(func_as_depender); @@ -836,7 +841,6 @@ pub fn linkerUpdateFunc(pt: Zcu.PerThread, func_index: InternPool.Index, air: Ai }, }; } else if (zcu.llvm_object) |llvm_object| { - if (build_options.only_c) unreachable; llvm_object.updateFunc(pt, func_index, air, liveness) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, }; @@ -845,6 +849,7 @@ pub fn linkerUpdateFunc(pt: Zcu.PerThread, func_index: InternPool.Index, air: Ai /// https://github.com/ziglang/zig/issues/14307 pub fn semaPkg(pt: Zcu.PerThread, pkg: *Module) !void { + dev.check(.sema); const import_file_result = try pt.importPkg(pkg); const root_decl_index = pt.zcu.fileRootDecl(import_file_result.file_index); if (root_decl_index == .none) { @@ -2481,7 +2486,6 @@ fn processExportsInner( if (zcu.comp.bin_file) |lf| { try zcu.handleUpdateExports(export_indices, lf.updateExports(pt, exported, export_indices)); } else if (zcu.llvm_object) |llvm_object| { - if (build_options.only_c) unreachable; try zcu.handleUpdateExports(export_indices, llvm_object.updateExports(pt, exported, export_indices)); } } @@ -2654,7 +2658,6 @@ pub fn linkerUpdateDecl(pt: Zcu.PerThread, decl_index: Zcu.Decl.Index) !void { }, }; } else if (zcu.llvm_object) |llvm_object| { - if (build_options.only_c) unreachable; llvm_object.updateDecl(pt, decl_index) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, }; @@ -3271,6 +3274,7 @@ const BigIntMutable = std.math.big.int.Mutable; const build_options = @import("build_options"); const builtin = @import("builtin"); const Cache = std.Build.Cache; +const dev = @import("../dev.zig"); const InternPool = @import("../InternPool.zig"); const isUpDir = @import("../introspect.zig").isUpDir; const Liveness = @import("../Liveness.zig"); diff --git a/src/codegen.zig b/src/codegen.zig index 3dc3d415be..2967f41dc2 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -22,6 +22,7 @@ const Type = @import("Type.zig"); const Value = @import("Value.zig"); const Zir = std.zig.Zir; const Alignment = InternPool.Alignment; +const dev = @import("dev.zig"); pub const Result = union(enum) { /// The `code` parameter passed to `generateSymbol` has the value ok. @@ -43,6 +44,23 @@ pub const DebugInfoOutput = union(enum) { none, }; +fn devFeatureForBackend(comptime backend: std.builtin.CompilerBackend) dev.Feature { + comptime assert(mem.startsWith(u8, @tagName(backend), "stage2_")); + return @field(dev.Feature, @tagName(backend)["stage2_".len..] ++ "_backend"); +} + +fn importBackend(comptime backend: std.builtin.CompilerBackend) type { + return switch (backend) { + .stage2_aarch64 => @import("arch/aarch64/CodeGen.zig"), + .stage2_arm => @import("arch/arm/CodeGen.zig"), + .stage2_riscv64 => @import("arch/riscv64/CodeGen.zig"), + .stage2_sparc64 => @import("arch/sparc64/CodeGen.zig"), + .stage2_wasm => @import("arch/wasm/CodeGen.zig"), + .stage2_x86_64 => @import("arch/x86_64/CodeGen.zig"), + else => unreachable, + }; +} + pub fn generateFunction( lf: *link.File, pt: Zcu.PerThread, @@ -58,21 +76,18 @@ pub fn generateFunction( const decl = zcu.declPtr(func.owner_decl); const namespace = zcu.namespacePtr(decl.src_namespace); const target = namespace.fileScope(zcu).mod.resolved_target.result; - switch (target.cpu.arch) { - .arm, - .armeb, - => return @import("arch/arm/CodeGen.zig").generate(lf, pt, src_loc, func_index, air, liveness, code, debug_output), - .aarch64, - .aarch64_be, - .aarch64_32, - => return @import("arch/aarch64/CodeGen.zig").generate(lf, pt, src_loc, func_index, air, liveness, code, debug_output), - .riscv64 => return @import("arch/riscv64/CodeGen.zig").generate(lf, pt, src_loc, func_index, air, liveness, code, debug_output), - .sparc64 => return @import("arch/sparc64/CodeGen.zig").generate(lf, pt, src_loc, func_index, air, liveness, code, debug_output), - .x86_64 => return @import("arch/x86_64/CodeGen.zig").generate(lf, pt, src_loc, func_index, air, liveness, code, debug_output), - .wasm32, - .wasm64, - => return @import("arch/wasm/CodeGen.zig").generate(lf, pt, src_loc, func_index, air, liveness, code, debug_output), + switch (target_util.zigBackend(target, false)) { else => unreachable, + inline .stage2_aarch64, + .stage2_arm, + .stage2_riscv64, + .stage2_sparc64, + .stage2_wasm, + .stage2_x86_64, + => |backend| { + dev.check(devFeatureForBackend(backend)); + return importBackend(backend).generate(lf, pt, src_loc, func_index, air, liveness, code, debug_output); + }, } } @@ -89,9 +104,12 @@ pub fn generateLazyFunction( const decl = zcu.declPtr(decl_index); const namespace = zcu.namespacePtr(decl.src_namespace); const target = namespace.fileScope(zcu).mod.resolved_target.result; - switch (target.cpu.arch) { - .x86_64 => return @import("arch/x86_64/CodeGen.zig").generateLazy(lf, pt, src_loc, lazy_sym, code, debug_output), + switch (target_util.zigBackend(target, false)) { else => unreachable, + inline .stage2_x86_64 => |backend| { + dev.check(devFeatureForBackend(backend)); + return importBackend(backend).generateLazy(lf, pt, src_loc, lazy_sym, code, debug_output); + }, } } diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index b2d32fb539..4ed652a36d 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -868,7 +868,6 @@ pub const Object = struct { pub const TypeMap = std.AutoHashMapUnmanaged(InternPool.Index, Builder.Type); pub fn create(arena: Allocator, comp: *Compilation) !*Object { - if (build_options.only_c) unreachable; const gpa = comp.gpa; const target = comp.root_mod.resolved_target.result; const llvm_target_triple = try targetTriple(arena, target); diff --git a/src/dev.zig b/src/dev.zig new file mode 100644 index 0000000000..6d99df4aa5 --- /dev/null +++ b/src/dev.zig @@ -0,0 +1,238 @@ +pub const Env = enum { + /// zig1 features + bootstrap, + + /// zig2 features + core, + + /// stage3 features + full, + + /// - `zig cc` + /// - `zig c++` + /// - `zig translate-c` + c_source, + + /// - `zig ast-check` + /// - `zig changelist` + /// - `zig dump-zir` + ast_gen, + + /// - ast_gen + /// - `zig build-* -fno-emit-bin` + sema, + + /// - sema + /// - jit command on x86_64-linux host + /// - `zig build-* -fno-llvm -fno-lld -target x86_64-linux` + @"x86_64-linux", + + pub inline fn supports(comptime dev_env: Env, comptime feature: Feature) bool { + return switch (dev_env) { + .full => true, + .bootstrap => switch (feature) { + .build_exe_command, + .build_obj_command, + .ast_gen, + .sema, + .c_backend, + .c_linker, + => true, + else => false, + }, + .core => switch (feature) { + .build_exe_command, + .build_lib_command, + .build_obj_command, + .test_command, + .run_command, + .ar_command, + .build_command, + .clang_command, + .stdio_listen, + .build_import_lib, + .make_executable, + .make_writable, + .incremental, + .ast_gen, + .sema, + .llvm_backend, + .c_backend, + .wasm_backend, + .arm_backend, + .x86_64_backend, + .aarch64_backend, + .x86_backend, + .riscv64_backend, + .sparc64_backend, + .spirv64_backend, + .lld_linker, + .coff_linker, + .elf_linker, + .macho_linker, + .c_linker, + .wasm_linker, + .spirv_linker, + .plan9_linker, + .nvptx_linker, + => true, + .cc_command, + .translate_c_command, + .jit_command, + .fetch_command, + .init_command, + .targets_command, + .version_command, + .env_command, + .zen_command, + .help_command, + .ast_check_command, + .detect_cpu_command, + .changelist_command, + .dump_zir_command, + .llvm_ints_command, + .docs_emit, + // Avoid dragging networking into zig2.c because it adds dependencies on some + // linker symbols that are annoying to satisfy while bootstrapping. + .network_listen, + .win32_resource, + => false, + }, + .c_source => switch (feature) { + .clang_command, + .cc_command, + .translate_c_command, + => true, + else => false, + }, + .ast_gen => switch (feature) { + .ast_check_command, + .changelist_command, + .dump_zir_command, + .make_executable, + .make_writable, + .incremental, + .ast_gen, + => true, + else => false, + }, + .sema => switch (feature) { + .build_exe_command, + .build_lib_command, + .build_obj_command, + .test_command, + .run_command, + .sema, + => true, + else => Env.ast_gen.supports(feature), + }, + .@"x86_64-linux" => switch (feature) { + .x86_64_backend, + .elf_linker, + => true, + else => Env.sema.supports(feature), + }, + }; + } + + pub inline fn supportsAny(comptime dev_env: Env, comptime features: []const Feature) bool { + inline for (features) |feature| if (dev_env.supports(feature)) return true; + return false; + } + + pub inline fn supportsAll(comptime dev_env: Env, comptime features: []const Feature) bool { + inline for (features) |feature| if (!dev_env.supports(feature)) return false; + return true; + } +}; + +pub const Feature = enum { + build_exe_command, + build_lib_command, + build_obj_command, + test_command, + run_command, + ar_command, + build_command, + clang_command, + cc_command, + translate_c_command, + jit_command, + fetch_command, + init_command, + targets_command, + version_command, + env_command, + zen_command, + help_command, + ast_check_command, + detect_cpu_command, + changelist_command, + dump_zir_command, + llvm_ints_command, + + docs_emit, + stdio_listen, + network_listen, + build_import_lib, + win32_resource, + make_executable, + make_writable, + incremental, + ast_gen, + sema, + + llvm_backend, + c_backend, + wasm_backend, + arm_backend, + x86_64_backend, + aarch64_backend, + x86_backend, + riscv64_backend, + sparc64_backend, + spirv64_backend, + + lld_linker, + coff_linker, + elf_linker, + macho_linker, + c_linker, + wasm_linker, + spirv_linker, + plan9_linker, + nvptx_linker, +}; + +/// Makes the code following the call to this function unreachable if `feature` is disabled. +pub fn check(comptime feature: Feature) if (env.supports(feature)) void else noreturn { + if (env.supports(feature)) return; + @panic("development environment " ++ @tagName(env) ++ " does not support feature " ++ @tagName(feature)); +} + +/// Makes the code following the call to this function unreachable if all of `features` are disabled. +pub fn checkAny(comptime features: []const Feature) if (env.supportsAny(features)) void else noreturn { + if (env.supportsAny(features)) return; + comptime var feature_tags: []const u8 = ""; + inline for (features[0 .. features.len - 1]) |feature| feature_tags = feature_tags ++ @tagName(feature) ++ ", "; + feature_tags = feature_tags ++ "or " ++ @tagName(features[features.len - 1]); + @panic("development environment " ++ @tagName(env) ++ " does not support feature " ++ feature_tags); +} + +/// Makes the code following the call to this function unreachable if any of `features` are disabled. +pub fn checkAll(comptime features: []const Feature) if (env.supportsAll(features)) void else noreturn { + if (env.supportsAll(features)) return; + inline for (features) |feature| if (!env.supports(feature)) + @panic("development environment " ++ @tagName(env) ++ " does not support feature " ++ @tagName(feature)); +} + +const build_options = @import("build_options"); + +pub const env: Env = if (@hasDecl(build_options, "dev")) + @field(Env, @tagName(build_options.dev)) +else if (@hasDecl(build_options, "only_c") and build_options.only_c) + .bootstrap +else if (@hasDecl(build_options, "only_core_functionality") and build_options.only_core_functionality) + .core +else + .full; diff --git a/src/link.zig b/src/link.zig index 03ee3185ab..cf780c76fa 100644 --- a/src/link.zig +++ b/src/link.zig @@ -21,6 +21,7 @@ const Value = @import("Value.zig"); const LlvmObject = @import("codegen/llvm.zig").Object; const lldMain = @import("main.zig").lldMain; const Package = @import("Package.zig"); +const dev = @import("dev.zig"); /// When adding a new field, remember to update `hashAddSystemLibs`. /// These are *always* dynamically linked. Static libraries will be @@ -192,7 +193,7 @@ pub const File = struct { ) !*File { switch (Tag.fromObjectFormat(comp.root_mod.resolved_target.result.ofmt)) { inline else => |tag| { - if (tag != .c and build_options.only_c) unreachable; + dev.check(tag.devFeature()); const ptr = try tag.Type().open(arena, comp, emit, options); return &ptr.base; }, @@ -207,7 +208,7 @@ pub const File = struct { ) !*File { switch (Tag.fromObjectFormat(comp.root_mod.resolved_target.result.ofmt)) { inline else => |tag| { - if (tag != .c and build_options.only_c) unreachable; + dev.check(tag.devFeature()); const ptr = try tag.Type().createEmpty(arena, comp, emit, options); return &ptr.base; }, @@ -219,12 +220,13 @@ pub const File = struct { } pub fn makeWritable(base: *File) !void { + dev.check(.make_writable); const comp = base.comp; const gpa = comp.gpa; switch (base.tag) { .coff, .elf, .macho, .plan9, .wasm => { - if (build_options.only_c) unreachable; if (base.file != null) return; + dev.checkAny(&.{ .coff_linker, .elf_linker, .macho_linker, .plan9_linker, .wasm_linker }); const emit = base.emit; if (base.child_pid) |pid| { if (builtin.os.tag == .windows) { @@ -263,11 +265,12 @@ pub const File = struct { .mode = determineMode(use_lld, output_mode, link_mode), }); }, - .c, .spirv, .nvptx => {}, + .c, .spirv, .nvptx => dev.checkAny(&.{ .c_linker, .spirv_linker, .nvptx_linker }), } } pub fn makeExecutable(base: *File) !void { + dev.check(.make_executable); const comp = base.comp; const output_mode = comp.config.output_mode; const link_mode = comp.config.link_mode; @@ -283,7 +286,7 @@ pub const File = struct { } switch (base.tag) { .elf => if (base.file) |f| { - if (build_options.only_c) unreachable; + dev.check(.elf_linker); if (base.zcu_object_sub_path != null and use_lld) { // The file we have open is not the final file that we want to // make executable, so we don't have to close it. @@ -302,7 +305,7 @@ pub const File = struct { } }, .coff, .macho, .plan9, .wasm => if (base.file) |f| { - if (build_options.only_c) unreachable; + dev.checkAny(&.{ .coff_linker, .macho_linker, .plan9_linker, .wasm_linker }); if (base.zcu_object_sub_path != null) { // The file we have open is not the final file that we want to // make executable, so we don't have to close it. @@ -321,7 +324,7 @@ pub const File = struct { } } }, - .c, .spirv, .nvptx => {}, + .c, .spirv, .nvptx => dev.checkAny(&.{ .c_linker, .spirv_linker, .nvptx_linker }), } } @@ -366,13 +369,13 @@ pub const File = struct { /// constant. Returns the symbol index of the lowered constant in the read-only section /// of the final binary. pub fn lowerUnnamedConst(base: *File, pt: Zcu.PerThread, val: Value, decl_index: InternPool.DeclIndex) UpdateDeclError!u32 { - if (build_options.only_c) @compileError("unreachable"); switch (base.tag) { .spirv => unreachable, .c => unreachable, .nvptx => unreachable, - inline else => |t| { - return @as(*t.Type(), @fieldParentPtr("base", base)).lowerUnnamedConst(pt, val, decl_index); + inline else => |tag| { + dev.check(tag.devFeature()); + return @as(*tag.Type(), @fieldParentPtr("base", base)).lowerUnnamedConst(pt, val, decl_index); }, } } @@ -383,15 +386,15 @@ pub const File = struct { /// Optionally, it is possible to specify where to expect the symbol defined if it /// is an import. pub fn getGlobalSymbol(base: *File, name: []const u8, lib_name: ?[]const u8) UpdateDeclError!u32 { - if (build_options.only_c) @compileError("unreachable"); log.debug("getGlobalSymbol '{s}' (expected in '{?s}')", .{ name, lib_name }); switch (base.tag) { .plan9 => unreachable, .spirv => unreachable, .c => unreachable, .nvptx => unreachable, - inline else => |t| { - return @as(*t.Type(), @fieldParentPtr("base", base)).getGlobalSymbol(name, lib_name); + inline else => |tag| { + dev.check(tag.devFeature()); + return @as(*tag.Type(), @fieldParentPtr("base", base)).getGlobalSymbol(name, lib_name); }, } } @@ -402,7 +405,7 @@ pub const File = struct { assert(decl.has_tv); switch (base.tag) { inline else => |tag| { - if (tag != .c and build_options.only_c) unreachable; + dev.check(tag.devFeature()); return @as(*tag.Type(), @fieldParentPtr("base", base)).updateDecl(pt, decl_index); }, } @@ -418,7 +421,7 @@ pub const File = struct { ) UpdateDeclError!void { switch (base.tag) { inline else => |tag| { - if (tag != .c and build_options.only_c) unreachable; + dev.check(tag.devFeature()); return @as(*tag.Type(), @fieldParentPtr("base", base)).updateFunc(pt, func_index, air, liveness); }, } @@ -430,7 +433,7 @@ pub const File = struct { switch (base.tag) { .spirv, .nvptx => {}, inline else => |tag| { - if (tag != .c and build_options.only_c) unreachable; + dev.check(tag.devFeature()); return @as(*tag.Type(), @fieldParentPtr("base", base)).updateDeclLineNumber(pt, decl_index); }, } @@ -454,7 +457,7 @@ pub const File = struct { if (base.file) |f| f.close(); switch (base.tag) { inline else => |tag| { - if (tag != .c and build_options.only_c) unreachable; + dev.check(tag.devFeature()); @as(*tag.Type(), @fieldParentPtr("base", base)).deinit(); }, } @@ -536,12 +539,9 @@ pub const File = struct { /// and `use_lld`, not only `effectiveOutputMode`. /// `arena` has the lifetime of the call to `Compilation.update`. pub fn flush(base: *File, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) FlushError!void { - if (build_options.only_c) { - assert(base.tag == .c); - return @as(*C, @fieldParentPtr("base", base)).flush(arena, tid, prog_node); - } const comp = base.comp; if (comp.clang_preprocessor_mode == .yes or comp.clang_preprocessor_mode == .pch) { + dev.check(.clang_command); const gpa = comp.gpa; const emit = base.emit; // TODO: avoid extra link step when it's just 1 object file (the `zig cc -c` case) @@ -565,6 +565,7 @@ pub const File = struct { } switch (base.tag) { inline else => |tag| { + dev.check(tag.devFeature()); return @as(*tag.Type(), @fieldParentPtr("base", base)).flush(arena, tid, prog_node); }, } @@ -575,7 +576,7 @@ pub const File = struct { pub fn flushModule(base: *File, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) FlushError!void { switch (base.tag) { inline else => |tag| { - if (tag != .c and build_options.only_c) unreachable; + dev.check(tag.devFeature()); return @as(*tag.Type(), @fieldParentPtr("base", base)).flushModule(arena, tid, prog_node); }, } @@ -585,7 +586,7 @@ pub const File = struct { pub fn freeDecl(base: *File, decl_index: InternPool.DeclIndex) void { switch (base.tag) { inline else => |tag| { - if (tag != .c and build_options.only_c) unreachable; + dev.check(tag.devFeature()); @as(*tag.Type(), @fieldParentPtr("base", base)).freeDecl(decl_index); }, } @@ -608,7 +609,7 @@ pub const File = struct { ) UpdateExportsError!void { switch (base.tag) { inline else => |tag| { - if (tag != .c and build_options.only_c) unreachable; + dev.check(tag.devFeature()); return @as(*tag.Type(), @fieldParentPtr("base", base)).updateExports(pt, exported, export_indices); }, } @@ -627,12 +628,12 @@ pub const File = struct { /// May be called before or after updateFunc/updateDecl therefore it is up to the linker to allocate /// the block/atom. pub fn getDeclVAddr(base: *File, pt: Zcu.PerThread, decl_index: InternPool.DeclIndex, reloc_info: RelocInfo) !u64 { - if (build_options.only_c) @compileError("unreachable"); switch (base.tag) { .c => unreachable, .spirv => unreachable, .nvptx => unreachable, inline else => |tag| { + dev.check(tag.devFeature()); return @as(*tag.Type(), @fieldParentPtr("base", base)).getDeclVAddr(pt, decl_index, reloc_info); }, } @@ -647,24 +648,24 @@ pub const File = struct { decl_align: InternPool.Alignment, src_loc: Zcu.LazySrcLoc, ) !LowerResult { - if (build_options.only_c) @compileError("unreachable"); switch (base.tag) { .c => unreachable, .spirv => unreachable, .nvptx => unreachable, inline else => |tag| { + dev.check(tag.devFeature()); return @as(*tag.Type(), @fieldParentPtr("base", base)).lowerAnonDecl(pt, decl_val, decl_align, src_loc); }, } } pub fn getAnonDeclVAddr(base: *File, decl_val: InternPool.Index, reloc_info: RelocInfo) !u64 { - if (build_options.only_c) @compileError("unreachable"); switch (base.tag) { .c => unreachable, .spirv => unreachable, .nvptx => unreachable, inline else => |tag| { + dev.check(tag.devFeature()); return @as(*tag.Type(), @fieldParentPtr("base", base)).getAnonDeclVAddr(decl_val, reloc_info); }, } @@ -675,7 +676,6 @@ pub const File = struct { exported: Zcu.Exported, name: InternPool.NullTerminatedString, ) void { - if (build_options.only_c) @compileError("unreachable"); switch (base.tag) { .plan9, .spirv, @@ -683,12 +683,15 @@ pub const File = struct { => {}, inline else => |tag| { + dev.check(tag.devFeature()); return @as(*tag.Type(), @fieldParentPtr("base", base)).deleteExport(exported, name); }, } } pub fn linkAsArchive(base: *File, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) FlushError!void { + dev.check(.lld_linker); + const tracy = trace(@src()); defer tracy.end(); @@ -743,10 +746,8 @@ pub const File = struct { for (comp.c_object_table.keys()) |key| { _ = try man.addFile(key.status.success.object_path, null); } - if (!build_options.only_core_functionality) { - for (comp.win32_resource_table.keys()) |key| { - _ = try man.addFile(key.status.success.res_path, null); - } + for (comp.win32_resource_table.keys()) |key| { + _ = try man.addFile(key.status.success.res_path, null); } try man.addOptionalFile(zcu_obj_path); try man.addOptionalFile(compiler_rt_path); @@ -777,7 +778,7 @@ pub const File = struct { }; } - const win32_resource_table_len = if (build_options.only_core_functionality) 0 else comp.win32_resource_table.count(); + const win32_resource_table_len = comp.win32_resource_table.count(); const num_object_files = objects.len + comp.c_object_table.count() + win32_resource_table_len + 2; var object_files = try std.ArrayList([*:0]const u8).initCapacity(gpa, num_object_files); defer object_files.deinit(); @@ -788,10 +789,8 @@ pub const File = struct { for (comp.c_object_table.keys()) |key| { object_files.appendAssumeCapacity(try arena.dupeZ(u8, key.status.success.object_path)); } - if (!build_options.only_core_functionality) { - for (comp.win32_resource_table.keys()) |key| { - object_files.appendAssumeCapacity(try arena.dupeZ(u8, key.status.success.res_path)); - } + for (comp.win32_resource_table.keys()) |key| { + object_files.appendAssumeCapacity(try arena.dupeZ(u8, key.status.success.res_path)); } if (zcu_obj_path) |p| { object_files.appendAssumeCapacity(try arena.dupeZ(u8, p)); @@ -869,6 +868,10 @@ pub const File = struct { .dxcontainer => @panic("TODO implement dxcontainer object format"), }; } + + pub fn devFeature(tag: Tag) dev.Feature { + return @field(dev.Feature, @tagName(tag) ++ "_linker"); + } }; pub const ErrorFlags = struct { diff --git a/src/link/Coff/lld.zig b/src/link/Coff/lld.zig index 4ec8458367..17bcefc1b4 100644 --- a/src/link/Coff/lld.zig +++ b/src/link/Coff/lld.zig @@ -2,6 +2,7 @@ const std = @import("std"); const build_options = @import("build_options"); const allocPrint = std.fmt.allocPrint; const assert = std.debug.assert; +const dev = @import("../../dev.zig"); const fs = std.fs; const log = std.log.scoped(.link); const mem = std.mem; @@ -18,6 +19,8 @@ const Compilation = @import("../../Compilation.zig"); const Zcu = @import("../../Zcu.zig"); pub fn linkWithLLD(self: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) !void { + dev.check(.lld_linker); + const tracy = trace(@src()); defer tracy.end(); @@ -77,10 +80,8 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no for (comp.c_object_table.keys()) |key| { _ = try man.addFile(key.status.success.object_path, null); } - if (!build_options.only_core_functionality) { - for (comp.win32_resource_table.keys()) |key| { - _ = try man.addFile(key.status.success.res_path, null); - } + for (comp.win32_resource_table.keys()) |key| { + _ = try man.addFile(key.status.success.res_path, null); } try man.addOptionalFile(module_obj_path); man.hash.addOptionalBytes(entry_name); @@ -274,10 +275,8 @@ pub fn linkWithLLD(self: *Coff, arena: Allocator, tid: Zcu.PerThread.Id, prog_no try argv.append(key.status.success.object_path); } - if (!build_options.only_core_functionality) { - for (comp.win32_resource_table.keys()) |key| { - try argv.append(key.status.success.res_path); - } + for (comp.win32_resource_table.keys()) |key| { + try argv.append(key.status.success.res_path); } if (module_obj_path) |p| { diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 579df0760a..d09fea144c 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -2148,6 +2148,8 @@ fn scanRelocs(self: *Elf) !void { } fn linkWithLLD(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) !void { + dev.check(.lld_linker); + const tracy = trace(@src()); defer tracy.end(); @@ -6430,6 +6432,7 @@ const math = std.math; const mem = std.mem; const codegen = @import("../codegen.zig"); +const dev = @import("../dev.zig"); const eh_frame = @import("Elf/eh_frame.zig"); const gc = @import("Elf/gc.zig"); const glibc = @import("../glibc.zig"); diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig index 32af004132..6239425614 100644 --- a/src/link/Wasm.zig +++ b/src/link/Wasm.zig @@ -6,6 +6,7 @@ const assert = std.debug.assert; const build_options = @import("build_options"); const builtin = @import("builtin"); const codegen = @import("../codegen.zig"); +const dev = @import("../dev.zig"); const fs = std.fs; const leb = std.leb; const link = @import("../link.zig"); @@ -3325,6 +3326,8 @@ fn emitImport(wasm: *Wasm, writer: anytype, import: types.Import) !void { } fn linkWithLLD(wasm: *Wasm, arena: Allocator, tid: Zcu.PerThread.Id, prog_node: std.Progress.Node) !void { + dev.check(.lld_linker); + const tracy = trace(@src()); defer tracy.end(); diff --git a/src/main.zig b/src/main.zig index 9b14170d78..64cbbdffe4 100644 --- a/src/main.zig +++ b/src/main.zig @@ -30,6 +30,7 @@ const Zcu = @import("Zcu.zig"); const AstGen = std.zig.AstGen; const mingw = @import("mingw.zig"); const Server = std.zig.Server; +const dev = @import("dev.zig"); pub const std_options = .{ .wasiCwd = wasi_cwd, @@ -195,17 +196,6 @@ pub fn main() anyerror!void { wasi_preopens = try fs.wasi.preopensAlloc(arena); } - // Short circuit some of the other logic for bootstrapping. - if (build_options.only_c) { - if (mem.eql(u8, args[1], "build-exe")) { - return buildOutputType(gpa, arena, args, .{ .build = .Exe }); - } else if (mem.eql(u8, args[1], "build-obj")) { - return buildOutputType(gpa, arena, args, .{ .build = .Obj }); - } else { - @panic("only build-exe or build-obj is supported in a -Donly-c build"); - } - } - return mainArgs(gpa, arena, args); } @@ -227,6 +217,7 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { } if (process.can_execv and std.posix.getenvZ("ZIG_IS_DETECTING_LIBC_PATHS") != null) { + dev.check(.cc_command); // In this case we have accidentally invoked ourselves as "the system C compiler" // to figure out where libc is installed. This is essentially infinite recursion // via child process execution due to the CC environment variable pointing to Zig. @@ -260,39 +251,49 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { const cmd = args[1]; const cmd_args = args[2..]; if (mem.eql(u8, cmd, "build-exe")) { + dev.check(.build_exe_command); return buildOutputType(gpa, arena, args, .{ .build = .Exe }); } else if (mem.eql(u8, cmd, "build-lib")) { + dev.check(.build_lib_command); return buildOutputType(gpa, arena, args, .{ .build = .Lib }); } else if (mem.eql(u8, cmd, "build-obj")) { + dev.check(.build_obj_command); return buildOutputType(gpa, arena, args, .{ .build = .Obj }); } else if (mem.eql(u8, cmd, "test")) { + dev.check(.test_command); return buildOutputType(gpa, arena, args, .zig_test); } else if (mem.eql(u8, cmd, "run")) { + dev.check(.run_command); return buildOutputType(gpa, arena, args, .run); } else if (mem.eql(u8, cmd, "dlltool") or mem.eql(u8, cmd, "ranlib") or mem.eql(u8, cmd, "lib") or mem.eql(u8, cmd, "ar")) { + dev.check(.ar_command); return process.exit(try llvmArMain(arena, args)); } else if (mem.eql(u8, cmd, "build")) { + dev.check(.build_command); return cmdBuild(gpa, arena, cmd_args); } else if (mem.eql(u8, cmd, "clang") or mem.eql(u8, cmd, "-cc1") or mem.eql(u8, cmd, "-cc1as")) { + dev.check(.clang_command); return process.exit(try clangMain(arena, args)); } else if (mem.eql(u8, cmd, "ld.lld") or mem.eql(u8, cmd, "lld-link") or mem.eql(u8, cmd, "wasm-ld")) { + dev.check(.lld_linker); return process.exit(try lldMain(arena, args, true)); - } else if (build_options.only_core_functionality) { - @panic("only a few subcommands are supported in a zig2.c build"); } else if (mem.eql(u8, cmd, "cc")) { + dev.check(.cc_command); return buildOutputType(gpa, arena, args, .cc); } else if (mem.eql(u8, cmd, "c++")) { + dev.check(.cc_command); return buildOutputType(gpa, arena, args, .cpp); } else if (mem.eql(u8, cmd, "translate-c")) { + dev.check(.translate_c_command); return buildOutputType(gpa, arena, args, .translate_c); } else if (mem.eql(u8, cmd, "rc")) { const use_server = cmd_args.len > 0 and std.mem.eql(u8, cmd_args[0], "--zig-integration"); @@ -332,16 +333,19 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { } else if (mem.eql(u8, cmd, "init")) { return cmdInit(gpa, arena, cmd_args); } else if (mem.eql(u8, cmd, "targets")) { + dev.check(.targets_command); const host = std.zig.resolveTargetQueryOrFatal(.{}); const stdout = io.getStdOut().writer(); return @import("print_targets.zig").cmdTargets(arena, cmd_args, stdout, host); } else if (mem.eql(u8, cmd, "version")) { + dev.check(.version_command); try std.io.getStdOut().writeAll(build_options.version ++ "\n"); // Check libc++ linkage to make sure Zig was built correctly, but only // for "env" and "version" to avoid affecting the startup time for // build-critical commands (check takes about ~10 μs) return verifyLibcxxCorrectlyLinked(); } else if (mem.eql(u8, cmd, "env")) { + dev.check(.env_command); verifyLibcxxCorrectlyLinked(); return @import("print_env.zig").cmdEnv(arena, cmd_args, io.getStdOut().writer()); } else if (mem.eql(u8, cmd, "reduce")) { @@ -350,8 +354,10 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { .root_src_path = "reduce.zig", }); } else if (mem.eql(u8, cmd, "zen")) { + dev.check(.zen_command); return io.getStdOut().writeAll(info_zen); } else if (mem.eql(u8, cmd, "help") or mem.eql(u8, cmd, "-h") or mem.eql(u8, cmd, "--help")) { + dev.check(.help_command); return io.getStdOut().writeAll(usage); } else if (mem.eql(u8, cmd, "ast-check")) { return cmdAstCheck(gpa, arena, cmd_args); @@ -726,14 +732,10 @@ const ArgMode = union(enum) { run, }; -/// Avoid dragging networking into zig2.c because it adds dependencies on some -/// linker symbols that are annoying to satisfy while bootstrapping. -const Ip4Address = if (build_options.only_core_functionality) void else std.net.Ip4Address; - const Listen = union(enum) { none, - ip4: Ip4Address, - stdio, + stdio: if (dev.env.supports(.stdio_listen)) void else noreturn, + ip4: if (dev.env.supports(.network_listen)) std.net.Ip4Address else noreturn, }; const ArgsIterator = struct { @@ -1338,9 +1340,10 @@ fn buildOutputType( } else if (mem.eql(u8, arg, "--listen")) { const next_arg = args_iter.nextOrFatal(); if (mem.eql(u8, next_arg, "-")) { + dev.check(.stdio_listen); listen = .stdio; } else { - if (build_options.only_core_functionality) unreachable; + dev.check(.network_listen); // example: --listen 127.0.0.1:9000 var it = std.mem.splitScalar(u8, next_arg, ':'); const host = it.next().?; @@ -1351,6 +1354,7 @@ fn buildOutputType( fatal("invalid host: '{s}': {s}", .{ host, @errorName(err) }) }; } } else if (mem.eql(u8, arg, "--listen=-")) { + dev.check(.stdio_listen); listen = .stdio; } else if (mem.eql(u8, arg, "--debug-link-snapshot")) { if (!build_options.enable_link_snapshots) { @@ -1359,6 +1363,7 @@ fn buildOutputType( enable_link_snapshots = true; } } else if (mem.eql(u8, arg, "-fincremental")) { + dev.check(.incremental); opt_incremental = true; } else if (mem.eql(u8, arg, "-fno-incremental")) { opt_incremental = false; @@ -1762,7 +1767,7 @@ fn buildOutputType( } }, .cc, .cpp => { - if (build_options.only_c) unreachable; + dev.check(.cc_command); emit_h = .no; soname = .no; @@ -3395,7 +3400,6 @@ fn buildOutputType( switch (listen) { .none => {}, .stdio => { - if (build_options.only_c) unreachable; try serve( comp, std.io.getStdIn(), @@ -3409,8 +3413,6 @@ fn buildOutputType( return cleanExit(); }, .ip4 => |ip4_addr| { - if (build_options.only_core_functionality) unreachable; - const addr: std.net.Address = .{ .in = ip4_addr }; var server = try addr.listen(.{ @@ -3454,50 +3456,50 @@ fn buildOutputType( else => |e| return e, }; } - if (build_options.only_c) return cleanExit(); try comp.makeBinFileExecutable(); saveState(comp, incremental); - if (test_exec_args.items.len == 0 and target.ofmt == .c) default_exec_args: { - // Default to using `zig run` to execute the produced .c code from `zig test`. - const c_code_loc = emit_bin_loc orelse break :default_exec_args; - const c_code_directory = c_code_loc.directory orelse comp.bin_file.?.emit.directory; - const c_code_path = try fs.path.join(arena, &[_][]const u8{ - c_code_directory.path orelse ".", c_code_loc.basename, - }); - try test_exec_args.appendSlice(arena, &.{ self_exe_path, "run" }); - if (zig_lib_directory.path) |p| { - try test_exec_args.appendSlice(arena, &.{ "-I", p }); - } - - if (create_module.resolved_options.link_libc) { - try test_exec_args.append(arena, "-lc"); - } else if (target.os.tag == .windows) { - try test_exec_args.appendSlice(arena, &.{ - "--subsystem", "console", - "-lkernel32", "-lntdll", - }); - } - - const first_cli_mod = create_module.modules.values()[0]; - if (first_cli_mod.target_arch_os_abi) |triple| { - try test_exec_args.appendSlice(arena, &.{ "-target", triple }); - } - if (first_cli_mod.target_mcpu) |mcpu| { - try test_exec_args.append(arena, try std.fmt.allocPrint(arena, "-mcpu={s}", .{mcpu})); - } - if (create_module.dynamic_linker) |dl| { - try test_exec_args.appendSlice(arena, &.{ "--dynamic-linker", dl }); - } - try test_exec_args.append(arena, c_code_path); - } - - const run_or_test = switch (arg_mode) { + if (switch (arg_mode) { .run => true, .zig_test => !test_no_exec, else => false, - }; - if (run_or_test) { + }) { + dev.checkAny(&.{ .run_command, .test_command }); + + if (test_exec_args.items.len == 0 and target.ofmt == .c) default_exec_args: { + // Default to using `zig run` to execute the produced .c code from `zig test`. + const c_code_loc = emit_bin_loc orelse break :default_exec_args; + const c_code_directory = c_code_loc.directory orelse comp.bin_file.?.emit.directory; + const c_code_path = try fs.path.join(arena, &[_][]const u8{ + c_code_directory.path orelse ".", c_code_loc.basename, + }); + try test_exec_args.appendSlice(arena, &.{ self_exe_path, "run" }); + if (zig_lib_directory.path) |p| { + try test_exec_args.appendSlice(arena, &.{ "-I", p }); + } + + if (create_module.resolved_options.link_libc) { + try test_exec_args.append(arena, "-lc"); + } else if (target.os.tag == .windows) { + try test_exec_args.appendSlice(arena, &.{ + "--subsystem", "console", + "-lkernel32", "-lntdll", + }); + } + + const first_cli_mod = create_module.modules.values()[0]; + if (first_cli_mod.target_arch_os_abi) |triple| { + try test_exec_args.appendSlice(arena, &.{ "-target", triple }); + } + if (first_cli_mod.target_mcpu) |mcpu| { + try test_exec_args.append(arena, try std.fmt.allocPrint(arena, "-mcpu={s}", .{mcpu})); + } + if (create_module.dynamic_linker) |dl| { + try test_exec_args.appendSlice(arena, &.{ "--dynamic-linker", dl }); + } + try test_exec_args.append(arena, c_code_path); + } + try runOrTest( comp, gpa, @@ -4459,7 +4461,8 @@ fn cmdTranslateC( file_system_inputs: ?*std.ArrayListUnmanaged(u8), prog_node: std.Progress.Node, ) !void { - if (build_options.only_core_functionality) @panic("@translate-c is not available in a zig2.c build"); + dev.check(.translate_c_command); + const color: Color = .auto; assert(comp.c_source_files.len == 1); const c_source_file = comp.c_source_files[0]; @@ -4627,6 +4630,8 @@ const usage_init = ; fn cmdInit(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { + dev.check(.init_command); + { var i: usize = 0; while (i < args.len) : (i += 1) { @@ -4678,6 +4683,8 @@ fn cmdInit(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { } fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { + dev.check(.build_command); + var build_file: ?[]const u8 = null; var override_lib_dir: ?[]const u8 = try EnvVar.ZIG_LIB_DIR.get(arena); var override_global_cache_dir: ?[]const u8 = try EnvVar.ZIG_GLOBAL_CACHE_DIR.get(arena); @@ -4969,16 +4976,12 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { }); defer thread_pool.deinit(); - // Dummy http client that is not actually used when only_core_functionality is enabled. + // Dummy http client that is not actually used when fetch_command is unsupported. // Prevents bootstrap from depending on a bunch of unnecessary stuff. - const HttpClient = if (build_options.only_core_functionality) struct { + var http_client: if (dev.env.supports(.fetch_command)) std.http.Client else struct { allocator: Allocator, - fn deinit(self: *@This()) void { - _ = self; - } - } else std.http.Client; - - var http_client: HttpClient = .{ .allocator = gpa }; + fn deinit(_: @This()) void {} + } = .{ .allocator = gpa }; defer http_client.deinit(); var unlazy_set: Package.Fetch.JobQueue.UnlazySet = .{}; @@ -5045,16 +5048,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { var cleanup_build_dir: ?fs.Dir = null; defer if (cleanup_build_dir) |*dir| dir.close(); - if (build_options.only_core_functionality) { - try createEmptyDependenciesModule( - arena, - root_mod, - global_cache_directory, - local_cache_directory, - builtin_mod, - config, - ); - } else { + if (dev.env.supports(.fetch_command)) { const fetch_prog_node = root_prog_node.start("Fetch Packages", 0); defer fetch_prog_node.end(); @@ -5203,7 +5197,14 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { } } } - } + } else try createEmptyDependenciesModule( + arena, + root_mod, + global_cache_directory, + local_cache_directory, + builtin_mod, + config, + ); try root_mod.deps.put(arena, "@build", build_mod); @@ -5269,7 +5270,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void { if (code == 2) process.exit(2); if (code == 3) { - if (build_options.only_core_functionality) process.exit(3); + if (!dev.env.supports(.fetch_command)) process.exit(3); // Indicates the configure phase failed due to missing lazy // dependencies and stdout contains the hashes of the ones // that are missing. @@ -5346,6 +5347,8 @@ fn jitCmd( args: []const []const u8, options: JitCmdOptions, ) !void { + dev.check(.jit_command); + const color: Color = .auto; const root_prog_node = if (options.progress_node) |node| node else std.Progress.start(.{ .disable_printing = (color == .off), @@ -5995,6 +5998,8 @@ fn cmdAstCheck( arena: Allocator, args: []const []const u8, ) !void { + dev.check(.ast_check_command); + const Zir = std.zig.Zir; var color: Color = .auto; @@ -6154,6 +6159,8 @@ fn cmdDetectCpu( arena: Allocator, args: []const []const u8, ) !void { + dev.check(.detect_cpu_command); + _ = gpa; _ = arena; @@ -6293,6 +6300,8 @@ fn cmdDumpLlvmInts( arena: Allocator, args: []const []const u8, ) !void { + dev.check(.llvm_ints_command); + _ = gpa; if (!build_options.have_llvm) @@ -6336,6 +6345,8 @@ fn cmdDumpZir( arena: Allocator, args: []const []const u8, ) !void { + dev.check(.dump_zir_command); + _ = arena; const Zir = std.zig.Zir; @@ -6395,6 +6406,8 @@ fn cmdChangelist( arena: Allocator, args: []const []const u8, ) !void { + dev.check(.changelist_command); + const color: Color = .auto; const Zir = std.zig.Zir; @@ -6895,6 +6908,8 @@ fn cmdFetch( arena: Allocator, args: []const []const u8, ) !void { + dev.check(.fetch_command); + const color: Color = .auto; const work_around_btrfs_bug = native_os == .linux and EnvVar.ZIG_BTRFS_WORKAROUND.isSet(); diff --git a/src/mingw.zig b/src/mingw.zig index 5aa79064ee..7359be564c 100644 --- a/src/mingw.zig +++ b/src/mingw.zig @@ -9,6 +9,7 @@ const builtin = @import("builtin"); const Compilation = @import("Compilation.zig"); const build_options = @import("build_options"); const Cache = std.Build.Cache; +const dev = @import("dev.zig"); pub const CRTFile = enum { crt2_o, @@ -157,7 +158,8 @@ fn add_cc_args( } pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void { - if (build_options.only_c) @compileError("building import libs not included in core functionality"); + dev.check(.build_import_lib); + var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa); defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); diff --git a/stage1/config.zig.in b/stage1/config.zig.in index b9e3064f60..500e089fa8 100644 --- a/stage1/config.zig.in +++ b/stage1/config.zig.in @@ -12,5 +12,4 @@ pub const enable_tracy = false; pub const value_tracing = false; pub const skip_non_native = false; pub const force_gpa = false; -pub const only_c = false; -pub const only_core_functionality = true; +pub const dev = .core;