From 3944c72a148096e3933745a9fda0397c0dd67cbf Mon Sep 17 00:00:00 2001 From: Kendall Condon Date: Mon, 29 Jun 2026 21:11:46 -0400 Subject: [PATCH 1/2] default to LLVM and ReleaseSafe for fuzzing Currently, only the LLVM backend is capable of emitting fuzzing instrumentation, so `-ffuzz -fno-llvm` now results in an error. --- lib/init/src/main.zig | 2 +- src/Compilation/Config.zig | 10 +++++++++- src/main.zig | 1 + 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/init/src/main.zig b/lib/init/src/main.zig index d77848ab63f0afb0d963ace4577ac1ee701ed548..f4bc7d488da391a64071c17851dbc7bd43b7436d 100644 --- a/lib/init/src/main.zig +++ b/lib/init/src/main.zig @@ -45,7 +45,7 @@ test "fuzz example" { fn testOne(context: void, smith: *std.testing.Smith) !void { _ = context; - // Try command `zig build test --fuzz -Doptimize=ReleaseFast` to see if it manages to fail this test case! + // Try running `zig build test --fuzz` and see if it manages to fail this test case! const gpa = std.testing.allocator; var list: std.ArrayList(u8) = .empty; diff --git a/src/Compilation/Config.zig b/src/Compilation/Config.zig index 3691b1eb423b72130b0fd8e962137a08a1da1d61..fdfd8cfe741e1069aa886213940e8d50a88c7677 100644 --- a/src/Compilation/Config.zig +++ b/src/Compilation/Config.zig @@ -147,6 +147,7 @@ pub const ResolveError = error{ TargetRequiresPie, SanitizeThreadRequiresPie, SanitizeThreadRequiresLlvmBackend, + FuzzRequiresLlvmBackend, BackendLacksErrorTracing, LlvmLibraryUnavailable, LldUnavailable, @@ -196,7 +197,8 @@ pub fn resolve(options: Options) ResolveError!Config { break :b options.use_lib_llvm orelse true; }; - const root_optimize_mode = options.root_optimize_mode orelse .debug; + const root_optimize_mode = options.root_optimize_mode orelse + @as(std.lang.OptimizeMode, if (options.any_fuzz) .safe else .debug); // Make a decision on whether to use Clang or Aro for translate-c and compiling C files. const c_frontend: CFrontend = b: { @@ -349,6 +351,12 @@ pub fn resolve(options: Options) ResolveError!Config { break :b true; } + if (options.any_fuzz) { + // Fuzzing instrumentation requires the LLVM backend. + if (options.use_llvm == false) return error.FuzzRequiresLlvmBackend; + break :b true; + } + if (options.use_llvm) |x| break :b x; // If we cannot use LLVM libraries, then our own backends will be a diff --git a/src/main.zig b/src/main.zig index 68b38d043071c3ca48e2bd3aab68648165c13530..607cd7b643a5a94d06d710dc25feea16c55568c7 100644 --- a/src/main.zig +++ b/src/main.zig @@ -4328,6 +4328,7 @@ fn createModule( error.TargetRequiresPie => fatal("the specified target requires position independent executables", .{}), error.SanitizeThreadRequiresPie => fatal("thread sanitization requires position independent executables", .{}), error.SanitizeThreadRequiresLlvmBackend => fatal("thread sanitization requires the LLVM backend", .{}), + error.FuzzRequiresLlvmBackend => fatal("fuzzing requires the LLVM backend", .{}), error.BackendLacksErrorTracing => fatal("the selected backend has not yet implemented error return tracing", .{}), error.LlvmLibraryUnavailable => fatal("zig was compiled without LLVM libraries", .{}), error.LldUnavailable => fatal("zig was compiled without LLD libraries", .{}), -- 2.54.0 From aa8c774e0f15617137f56eb15c667e259b3be75a Mon Sep 17 00:00:00 2001 From: Kendall Condon Date: Mon, 29 Jun 2026 21:12:51 -0400 Subject: [PATCH 2/2] allow standardOptimizeOption to return null In order to allow the compiler to default to ReleaseSafe when compiling for fuzzing, Build.standardOptimizeOption has been changed to return an optional value. --- build.zig | 2 +- lib/std/Build.zig | 6 +++--- test/standalone/dependency_options/build.zig | 6 +++--- test/standalone/posix/build.zig | 2 +- test/tests.zig | 15 +++++++++------ 5 files changed, 17 insertions(+), 14 deletions(-) diff --git a/build.zig b/build.zig index ea520eec5e2ace2c81d0b614ffdfbe774ed88e49..cecba0923eb6c8fd63b0093264a2aa2482b672b4 100644 --- a/build.zig +++ b/build.zig @@ -883,7 +883,7 @@ fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void { } const AddCompilerModOptions = struct { - optimize: std.lang.Optimize, + optimize: ?std.lang.Optimize, target: std.Build.ResolvedTarget, strip: ?bool = null, valgrind: ?bool = null, diff --git a/lib/std/Build.zig b/lib/std/Build.zig index 36b48d62cf8075024dddc8dc21b339b929d34c5e..c1046962b509b152ce87271fa7403218b54819e1 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -1190,14 +1190,14 @@ pub const StandardOptimizeOptionOptions = struct { preferred_optimize_mode: ?std.builtin.Optimize = null, }; -pub fn standardOptimizeOption(b: *Build, options: StandardOptimizeOptionOptions) std.builtin.Optimize { +pub fn standardOptimizeOption(b: *Build, options: StandardOptimizeOptionOptions) ?std.builtin.Optimize { const graph = b.graph; if (options.preferred_optimize_mode) |mode| { if (b.option(bool, "release", "optimize for end users") orelse (graph.release_mode != .off)) { return mode; } else { - return .debug; + return null; } } @@ -1210,7 +1210,7 @@ pub fn standardOptimizeOption(b: *Build, options: StandardOptimizeOptionOptions) } return switch (graph.release_mode) { - .off => .debug, + .off => null, .any => { std.debug.print("the project does not declare a preferred optimization mode. choose: --release=fast, --release=safe, or --release=small\n", .{}); process.exit(1); diff --git a/test/standalone/dependency_options/build.zig b/test/standalone/dependency_options/build.zig index 5465cf7157a2d77378f2cd44b7369d70c2fe02c4..bf431dc9f4ed6bdf73bcabb566c628269e3934ea 100644 --- a/test/standalone/dependency_options/build.zig +++ b/test/standalone/dependency_options/build.zig @@ -10,14 +10,14 @@ pub fn build(b: *std.Build) !void { const none_specified_mod = none_specified.module("dummy"); if (!none_specified_mod.resolved_target.?.query.eql(b.graph.host.query)) return error.TestFailed; - const expected_optimize: std.builtin.Optimize = switch (b.graph.release_mode) { - .off => .debug, + const expected_optimize: ?std.builtin.OptimizeMode = switch (b.graph.release_mode) { + .off => null, .any => unreachable, .fast => .fast, .safe => .safe, .small => .small, }; - if (none_specified_mod.optimize.? != expected_optimize) return error.TestFailed; + if (none_specified_mod.optimize != expected_optimize) return error.TestFailed; // Passing null is the same as not specifying the option, // so this should resolve to the same cached dependency instance. diff --git a/test/standalone/posix/build.zig b/test/standalone/posix/build.zig index d2353e010c7c8681cc74829a4a5c9f052edb1e24..34575df0e54bd790f88d5921129a16b0cb35eff4 100644 --- a/test/standalone/posix/build.zig +++ b/test/standalone/posix/build.zig @@ -55,7 +55,7 @@ pub fn build(b: *std.Build) void { } } -fn run_exe(b: *std.Build, optimize: std.builtin.Optimize, case: *const Case, target: std.Build.ResolvedTarget, link_libc: bool) *std.Build.Step.Run { +fn run_exe(b: *std.Build, optimize: ?std.builtin.Optimize, case: *const Case, target: std.Build.ResolvedTarget, link_libc: bool) *std.Build.Step.Run { const exe_name = b.fmt("test-posix-{s}{s}{s}", .{ std.fs.path.stem(case.src_path), if (link_libc) "-libc" else "", diff --git a/test/tests.zig b/test/tests.zig index baebdd1381629db59e1abf8f371eb643d233a5a2..c3c501c99d71403862b8f3bcbea7be5367184ae7 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -20,7 +20,7 @@ pub const LinkContext = @import("src/Link.zig"); const ModuleTestTarget = struct { linkage: ?std.builtin.LinkMode = null, target: std.Target.Query = .{}, - optimize_mode: std.builtin.Optimize = .debug, + optimize_mode: ?std.builtin.Optimize = .debug, link_libc: ?bool = null, single_threaded: ?bool = null, use_llvm: ?bool = null, @@ -2795,7 +2795,7 @@ pub const ModuleTestOptions = struct { pub const TestOnly = union(enum) { default: void, - fuzz: OptimizeMode, + fuzz: ?OptimizeMode, }; }; @@ -2896,6 +2896,9 @@ fn addOneModuleTest( options: ModuleTestOptions, ) void { const target = &resolved_target.result; + const optimize_suffix = if (test_target.optimize_mode) |mode| switch (mode) { + inline else => |t| "-" ++ @tagName(t), + } else ""; const libc_suffix = if (test_target.link_libc == true) "-libc" else ""; const model_txt = target.cpu.model.name; @@ -2948,11 +2951,11 @@ fn addOneModuleTest( for (options.include_paths) |include_path| these_tests.root_module.addIncludePath(b.path(include_path)); - const qualified_name = b.fmt("{s}-{s}-{s}-{t}{s}{s}{s}{s}{s}{s}", .{ + const qualified_name = b.fmt("{s}-{s}-{s}{s}{s}{s}{s}{s}{s}{s}", .{ options.name, triple_txt, model_txt, - test_target.optimize_mode, + optimize_suffix, libc_suffix, single_threaded_suffix, backend_suffix, @@ -3087,11 +3090,11 @@ fn addOneModuleTest( } } -pub fn wouldUseLlvm(use_llvm: ?bool, query: std.Target.Query, optimize_mode: OptimizeMode) bool { +pub fn wouldUseLlvm(use_llvm: ?bool, query: std.Target.Query, optimize_mode: ?OptimizeMode) bool { if (comptime builtin.cpu.arch.endian() == .big) return true; // https://github.com/ziglang/zig/issues/25961 if (use_llvm) |x| return x; if (query.ofmt == .c) return false; - switch (optimize_mode) { + switch (optimize_mode orelse .debug) { .debug => {}, else => return true, } -- 2.54.0