authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-08-28 23:20:21-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-08-28 23:20:21-07:00
loge9a00ba7f4ef2546cd0c98559002431c749374fe
tree18102a8fd19ea54f1049ba6a0be522391a2bb7c7
parent6a21875ddbe0f509122fbd220f1abb015cc7bac7
parent13b5cee4cce2be7b5d1423fcd59b00ff1807142e
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #21236 from ziglang/fuzz

exclude unreachable code paths from having coverage instrumentation

9 files changed, 333 insertions(+), 137 deletions(-)

lib/compiler/test_runner.zig+1-2
...@@ -166,6 +166,7 @@ fn mainServer() !void {...@@ -166,6 +166,7 @@ fn mainServer() !void {
166 if (log_err_count != 0) @panic("error logs detected");166 if (log_err_count != 0) @panic("error logs detected");
167 if (first) {167 if (first) {
168 first = false;168 first = false;
169 const entry_addr = @intFromPtr(test_fn.func);
169 try server.serveU64Message(.fuzz_start_addr, entry_addr);170 try server.serveU64Message(.fuzz_start_addr, entry_addr);
170 }171 }
171 }172 }
...@@ -347,7 +348,6 @@ const FuzzerSlice = extern struct {...@@ -347,7 +348,6 @@ const FuzzerSlice = extern struct {
347};348};
348349
349var is_fuzz_test: bool = undefined;350var is_fuzz_test: bool = undefined;
350var entry_addr: usize = 0;
351351
352extern fn fuzzer_next() FuzzerSlice;352extern fn fuzzer_next() FuzzerSlice;
353extern fn fuzzer_init(cache_dir: FuzzerSlice) void;353extern fn fuzzer_init(cache_dir: FuzzerSlice) void;
...@@ -358,7 +358,6 @@ pub fn fuzzInput(options: testing.FuzzInputOptions) []const u8 {...@@ -358,7 +358,6 @@ pub fn fuzzInput(options: testing.FuzzInputOptions) []const u8 {
358 if (crippled) return "";358 if (crippled) return "";
359 is_fuzz_test = true;359 is_fuzz_test = true;
360 if (builtin.fuzz) {360 if (builtin.fuzz) {
361 if (entry_addr == 0) entry_addr = @returnAddress();
362 return fuzzer_next().toSlice();361 return fuzzer_next().toSlice();
363 }362 }
364 if (options.corpus.len == 0) return "";363 if (options.corpus.len == 0) return "";
lib/fuzzer.zig+44-48
...@@ -30,19 +30,6 @@ fn logOverride(...@@ -30,19 +30,6 @@ fn logOverride(
3030
31export threadlocal var __sancov_lowest_stack: usize = std.math.maxInt(usize);31export threadlocal var __sancov_lowest_stack: usize = std.math.maxInt(usize);
3232
33var module_count_8bc: usize = 0;
34var module_count_pcs: usize = 0;
35
36export fn __sanitizer_cov_8bit_counters_init(start: [*]u8, end: [*]u8) void {
37 assert(@atomicRmw(usize, &module_count_8bc, .Add, 1, .monotonic) == 0);
38 fuzzer.pc_counters = start[0 .. end - start];
39}
40
41export fn __sanitizer_cov_pcs_init(start: [*]const Fuzzer.FlaggedPc, end: [*]const Fuzzer.FlaggedPc) void {
42 assert(@atomicRmw(usize, &module_count_pcs, .Add, 1, .monotonic) == 0);
43 fuzzer.flagged_pcs = start[0 .. end - start];
44}
45
46export fn __sanitizer_cov_trace_const_cmp1(arg1: u8, arg2: u8) void {33export fn __sanitizer_cov_trace_const_cmp1(arg1: u8, arg2: u8) void {
47 handleCmp(@returnAddress(), arg1, arg2);34 handleCmp(@returnAddress(), arg1, arg2);
48}35}
...@@ -105,7 +92,7 @@ const Fuzzer = struct {...@@ -105,7 +92,7 @@ const Fuzzer = struct {
105 gpa: Allocator,92 gpa: Allocator,
106 rng: std.Random.DefaultPrng,93 rng: std.Random.DefaultPrng,
107 input: std.ArrayListUnmanaged(u8),94 input: std.ArrayListUnmanaged(u8),
108 flagged_pcs: []const FlaggedPc,95 pcs: []const usize,
109 pc_counters: []u8,96 pc_counters: []u8,
110 n_runs: usize,97 n_runs: usize,
111 recent_cases: RunMap,98 recent_cases: RunMap,
...@@ -174,32 +161,18 @@ const Fuzzer = struct {...@@ -174,32 +161,18 @@ const Fuzzer = struct {
174 }161 }
175 };162 };
176163
177 const FlaggedPc = extern struct {
178 addr: usize,
179 flags: packed struct(usize) {
180 entry: bool,
181 _: @Type(.{ .int = .{ .signedness = .unsigned, .bits = @bitSizeOf(usize) - 1 } }),
182 },
183 };
184
185 const Analysis = struct {164 const Analysis = struct {
186 score: usize,165 score: usize,
187 id: Run.Id,166 id: Run.Id,
188 };167 };
189168
190 fn init(f: *Fuzzer, cache_dir: std.fs.Dir) !void {169 fn init(f: *Fuzzer, cache_dir: std.fs.Dir, pc_counters: []u8, pcs: []const usize) !void {
191 const flagged_pcs = f.flagged_pcs;
192
193 f.cache_dir = cache_dir;170 f.cache_dir = cache_dir;
171 f.pc_counters = pc_counters;
172 f.pcs = pcs;
194173
195 // Choose a file name for the coverage based on a hash of the PCs that will be stored within.174 // Choose a file name for the coverage based on a hash of the PCs that will be stored within.
196 const pc_digest = d: {175 const pc_digest = std.hash.Wyhash.hash(0, std.mem.sliceAsBytes(pcs));
197 var hasher = std.hash.Wyhash.init(0);
198 for (flagged_pcs) |flagged_pc| {
199 hasher.update(std.mem.asBytes(&flagged_pc.addr));
200 }
201 break :d f.coverage.run_id_hasher.final();
202 };
203 f.coverage_id = pc_digest;176 f.coverage_id = pc_digest;
204 const hex_digest = std.fmt.hex(pc_digest);177 const hex_digest = std.fmt.hex(pc_digest);
205 const coverage_file_path = "v/" ++ hex_digest;178 const coverage_file_path = "v/" ++ hex_digest;
...@@ -213,12 +186,12 @@ const Fuzzer = struct {...@@ -213,12 +186,12 @@ const Fuzzer = struct {
213 .truncate = false,186 .truncate = false,
214 });187 });
215 defer coverage_file.close();188 defer coverage_file.close();
216 const n_bitset_elems = (flagged_pcs.len + @bitSizeOf(usize) - 1) / @bitSizeOf(usize);189 const n_bitset_elems = (pcs.len + @bitSizeOf(usize) - 1) / @bitSizeOf(usize);
217 comptime assert(SeenPcsHeader.trailing[0] == .pc_bits_usize);190 comptime assert(SeenPcsHeader.trailing[0] == .pc_bits_usize);
218 comptime assert(SeenPcsHeader.trailing[1] == .pc_addr);191 comptime assert(SeenPcsHeader.trailing[1] == .pc_addr);
219 const bytes_len = @sizeOf(SeenPcsHeader) +192 const bytes_len = @sizeOf(SeenPcsHeader) +
220 n_bitset_elems * @sizeOf(usize) +193 n_bitset_elems * @sizeOf(usize) +
221 flagged_pcs.len * @sizeOf(usize);194 pcs.len * @sizeOf(usize);
222 const existing_len = coverage_file.getEndPos() catch |err| {195 const existing_len = coverage_file.getEndPos() catch |err| {
223 fatal("unable to check len of coverage file: {s}", .{@errorName(err)});196 fatal("unable to check len of coverage file: {s}", .{@errorName(err)});
224 };197 };
...@@ -233,12 +206,12 @@ const Fuzzer = struct {...@@ -233,12 +206,12 @@ const Fuzzer = struct {
233 fatal("unable to init coverage memory map: {s}", .{@errorName(err)});206 fatal("unable to init coverage memory map: {s}", .{@errorName(err)});
234 };207 };
235 if (existing_len != 0) {208 if (existing_len != 0) {
236 const existing_pcs_bytes = f.seen_pcs.items[@sizeOf(SeenPcsHeader) + @sizeOf(usize) * n_bitset_elems ..][0 .. flagged_pcs.len * @sizeOf(usize)];209 const existing_pcs_bytes = f.seen_pcs.items[@sizeOf(SeenPcsHeader) + @sizeOf(usize) * n_bitset_elems ..][0 .. pcs.len * @sizeOf(usize)];
237 const existing_pcs = std.mem.bytesAsSlice(usize, existing_pcs_bytes);210 const existing_pcs = std.mem.bytesAsSlice(usize, existing_pcs_bytes);
238 for (existing_pcs, flagged_pcs, 0..) |old, new, i| {211 for (existing_pcs, pcs, 0..) |old, new, i| {
239 if (old != new.addr) {212 if (old != new) {
240 fatal("incompatible existing coverage file (differing PC at index {d}: {x} != {x})", .{213 fatal("incompatible existing coverage file (differing PC at index {d}: {x} != {x})", .{
241 i, old, new.addr,214 i, old, new,
242 });215 });
243 }216 }
244 }217 }
...@@ -246,14 +219,12 @@ const Fuzzer = struct {...@@ -246,14 +219,12 @@ const Fuzzer = struct {
246 const header: SeenPcsHeader = .{219 const header: SeenPcsHeader = .{
247 .n_runs = 0,220 .n_runs = 0,
248 .unique_runs = 0,221 .unique_runs = 0,
249 .pcs_len = flagged_pcs.len,222 .pcs_len = pcs.len,
250 .lowest_stack = std.math.maxInt(usize),223 .lowest_stack = std.math.maxInt(usize),
251 };224 };
252 f.seen_pcs.appendSliceAssumeCapacity(std.mem.asBytes(&header));225 f.seen_pcs.appendSliceAssumeCapacity(std.mem.asBytes(&header));
253 f.seen_pcs.appendNTimesAssumeCapacity(0, n_bitset_elems * @sizeOf(usize));226 f.seen_pcs.appendNTimesAssumeCapacity(0, n_bitset_elems * @sizeOf(usize));
254 for (flagged_pcs) |flagged_pc| {227 f.seen_pcs.appendSliceAssumeCapacity(std.mem.sliceAsBytes(pcs));
255 f.seen_pcs.appendSliceAssumeCapacity(std.mem.asBytes(&flagged_pc.addr));
256 }
257 }228 }
258 }229 }
259230
...@@ -307,8 +278,8 @@ const Fuzzer = struct {...@@ -307,8 +278,8 @@ const Fuzzer = struct {
307 // Track code coverage from all runs.278 // Track code coverage from all runs.
308 comptime assert(SeenPcsHeader.trailing[0] == .pc_bits_usize);279 comptime assert(SeenPcsHeader.trailing[0] == .pc_bits_usize);
309 const header_end_ptr: [*]volatile usize = @ptrCast(f.seen_pcs.items[@sizeOf(SeenPcsHeader)..]);280 const header_end_ptr: [*]volatile usize = @ptrCast(f.seen_pcs.items[@sizeOf(SeenPcsHeader)..]);
310 const remainder = f.flagged_pcs.len % @bitSizeOf(usize);281 const remainder = f.pcs.len % @bitSizeOf(usize);
311 const aligned_len = f.flagged_pcs.len - remainder;282 const aligned_len = f.pcs.len - remainder;
312 const seen_pcs = header_end_ptr[0..aligned_len];283 const seen_pcs = header_end_ptr[0..aligned_len];
313 const pc_counters = std.mem.bytesAsSlice([@bitSizeOf(usize)]u8, f.pc_counters[0..aligned_len]);284 const pc_counters = std.mem.bytesAsSlice([@bitSizeOf(usize)]u8, f.pc_counters[0..aligned_len]);
314 const V = @Vector(@bitSizeOf(usize), u8);285 const V = @Vector(@bitSizeOf(usize), u8);
...@@ -433,7 +404,7 @@ var fuzzer: Fuzzer = .{...@@ -433,7 +404,7 @@ var fuzzer: Fuzzer = .{
433 .gpa = general_purpose_allocator.allocator(),404 .gpa = general_purpose_allocator.allocator(),
434 .rng = std.Random.DefaultPrng.init(0),405 .rng = std.Random.DefaultPrng.init(0),
435 .input = .{},406 .input = .{},
436 .flagged_pcs = undefined,407 .pcs = undefined,
437 .pc_counters = undefined,408 .pc_counters = undefined,
438 .n_runs = 0,409 .n_runs = 0,
439 .recent_cases = .{},410 .recent_cases = .{},
...@@ -455,8 +426,32 @@ export fn fuzzer_next() Fuzzer.Slice {...@@ -455,8 +426,32 @@ export fn fuzzer_next() Fuzzer.Slice {
455}426}
456427
457export fn fuzzer_init(cache_dir_struct: Fuzzer.Slice) void {428export fn fuzzer_init(cache_dir_struct: Fuzzer.Slice) void {
458 if (module_count_8bc == 0) fatal("__sanitizer_cov_8bit_counters_init was never called", .{});429 // Linkers are expected to automatically add `__start_<section>` and
459 if (module_count_pcs == 0) fatal("__sanitizer_cov_pcs_init was never called", .{});430 // `__stop_<section>` symbols when section names are valid C identifiers.
431
432 const pc_counters_start = @extern([*]u8, .{
433 .name = "__start___sancov_cntrs",
434 .linkage = .weak,
435 }) orelse fatal("missing __start___sancov_cntrs symbol");
436
437 const pc_counters_end = @extern([*]u8, .{
438 .name = "__stop___sancov_cntrs",
439 .linkage = .weak,
440 }) orelse fatal("missing __stop___sancov_cntrs symbol");
441
442 const pc_counters = pc_counters_start[0 .. pc_counters_end - pc_counters_start];
443
444 const pcs_start = @extern([*]usize, .{
445 .name = "__start___sancov_pcs1",
446 .linkage = .weak,
447 }) orelse fatal("missing __start___sancov_pcs1 symbol");
448
449 const pcs_end = @extern([*]usize, .{
450 .name = "__stop___sancov_pcs1",
451 .linkage = .weak,
452 }) orelse fatal("missing __stop___sancov_pcs1 symbol");
453
454 const pcs = pcs_start[0 .. pcs_end - pcs_start];
460455
461 const cache_dir_path = cache_dir_struct.toZig();456 const cache_dir_path = cache_dir_struct.toZig();
462 const cache_dir = if (cache_dir_path.len == 0)457 const cache_dir = if (cache_dir_path.len == 0)
...@@ -466,7 +461,8 @@ export fn fuzzer_init(cache_dir_struct: Fuzzer.Slice) void {...@@ -466,7 +461,8 @@ export fn fuzzer_init(cache_dir_struct: Fuzzer.Slice) void {
466 fatal("unable to open fuzz directory '{s}': {s}", .{ cache_dir_path, @errorName(err) });461 fatal("unable to open fuzz directory '{s}': {s}", .{ cache_dir_path, @errorName(err) });
467 };462 };
468463
469 fuzzer.init(cache_dir) catch |err| fatal("unable to init fuzzer: {s}", .{@errorName(err)});464 fuzzer.init(cache_dir, pc_counters, pcs) catch |err|
465 fatal("unable to init fuzzer: {s}", .{@errorName(err)});
470}466}
471467
472/// Like `std.ArrayListUnmanaged(u8)` but backed by memory mapping.468/// Like `std.ArrayListUnmanaged(u8)` but backed by memory mapping.
lib/std/Build/Fuzz/WebServer.zig+11-6
...@@ -664,11 +664,16 @@ fn addEntryPoint(ws: *WebServer, coverage_id: u64, addr: u64) error{ AlreadyRepo...@@ -664,11 +664,16 @@ fn addEntryPoint(ws: *WebServer, coverage_id: u64, addr: u64) error{ AlreadyRepo
664 const coverage_map = ws.coverage_files.getPtr(coverage_id).?;664 const coverage_map = ws.coverage_files.getPtr(coverage_id).?;
665 const header: *const abi.SeenPcsHeader = @ptrCast(coverage_map.mapped_memory[0..@sizeOf(abi.SeenPcsHeader)]);665 const header: *const abi.SeenPcsHeader = @ptrCast(coverage_map.mapped_memory[0..@sizeOf(abi.SeenPcsHeader)]);
666 const pcs = header.pcAddrs();666 const pcs = header.pcAddrs();
667 const index = std.sort.upperBound(usize, pcs, addr, struct {667 // Since this pcs list is unsorted, we must linear scan for the best index.
668 fn order(context: usize, item: usize) std.math.Order {668 const index = i: {
669 return std.math.order(item, context);669 var best: usize = 0;
670 for (pcs[1..], 1..) |elem_addr, i| {
671 if (elem_addr == addr) break :i i;
672 if (elem_addr > addr) continue;
673 if (elem_addr > pcs[best]) best = i;
670 }674 }
671 }.order);675 break :i best;
676 };
672 if (index >= pcs.len) {677 if (index >= pcs.len) {
673 log.err("unable to find unit test entry address 0x{x} in source locations (range: 0x{x} to 0x{x})", .{678 log.err("unable to find unit test entry address 0x{x} in source locations (range: 0x{x} to 0x{x})", .{
674 addr, pcs[0], pcs[pcs.len - 1],679 addr, pcs[0], pcs[pcs.len - 1],
...@@ -678,8 +683,8 @@ fn addEntryPoint(ws: *WebServer, coverage_id: u64, addr: u64) error{ AlreadyRepo...@@ -678,8 +683,8 @@ fn addEntryPoint(ws: *WebServer, coverage_id: u64, addr: u64) error{ AlreadyRepo
678 if (false) {683 if (false) {
679 const sl = coverage_map.source_locations[index];684 const sl = coverage_map.source_locations[index];
680 const file_name = coverage_map.coverage.stringAt(coverage_map.coverage.fileAt(sl.file).basename);685 const file_name = coverage_map.coverage.stringAt(coverage_map.coverage.fileAt(sl.file).basename);
681 log.debug("server found entry point for 0x{x} at {s}:{d}:{d}", .{686 log.debug("server found entry point for 0x{x} at {s}:{d}:{d} - index {d} between {x} and {x}", .{
682 addr, file_name, sl.line, sl.column,687 addr, file_name, sl.line, sl.column, index, pcs[index - 1], pcs[index + 1],
683 });688 });
684 }689 }
685 const gpa = ws.gpa;690 const gpa = ws.gpa;
lib/std/Build/Step/Compile.zig+11-5
...@@ -218,12 +218,18 @@ no_builtin: bool = false,...@@ -218,12 +218,18 @@ no_builtin: bool = false,
218/// Managed by the build runner, not user build script.218/// Managed by the build runner, not user build script.
219zig_process: ?*Step.ZigProcess,219zig_process: ?*Step.ZigProcess,
220220
221/// Enables deprecated coverage instrumentation that is only useful if you221/// Enables coverage instrumentation that is only useful if you are using third
222/// are using third party fuzzers that depend on it. Otherwise, slows down222/// party fuzzers that depend on it. Otherwise, slows down the instrumented
223/// the instrumented binary with unnecessary function calls.223/// binary with unnecessary function calls.
224///224///
225/// To enable fuzz testing instrumentation on a compilation, see the `fuzz`225/// This kind of coverage instrumentation is used by AFLplusplus v4.21c,
226/// flag in `Module`.226/// however, modern fuzzers - including Zig - have switched to using "inline
227/// 8-bit counters" or "inline bool flag" which incurs only a single
228/// instruction for coverage, along with "trace cmp" which instruments
229/// comparisons and reports the operands.
230///
231/// To instead enable fuzz testing instrumentation on a compilation using Zig's
232/// builtin fuzzer, see the `fuzz` flag in `Module`.
227sanitize_coverage_trace_pc_guard: ?bool = null,233sanitize_coverage_trace_pc_guard: ?bool = null,
228234
229pub const ExpectedCompileErrors = union(enum) {235pub const ExpectedCompileErrors = union(enum) {
src/Air.zig+12-1
...@@ -1126,7 +1126,9 @@ pub const CondBr = struct {...@@ -1126,7 +1126,9 @@ pub const CondBr = struct {
1126 pub const BranchHints = packed struct(u32) {1126 pub const BranchHints = packed struct(u32) {
1127 true: std.builtin.BranchHint,1127 true: std.builtin.BranchHint,
1128 false: std.builtin.BranchHint,1128 false: std.builtin.BranchHint,
1129 _: u26 = 0,1129 then_cov: CoveragePoint,
1130 else_cov: CoveragePoint,
1131 _: u24 = 0,
1130 };1132 };
1131};1133};
11321134
...@@ -1903,3 +1905,12 @@ pub fn unwrapSwitch(air: *const Air, switch_inst: Inst.Index) UnwrappedSwitch {...@@ -1903,3 +1905,12 @@ pub fn unwrapSwitch(air: *const Air, switch_inst: Inst.Index) UnwrappedSwitch {
1903pub const typesFullyResolved = types_resolved.typesFullyResolved;1905pub const typesFullyResolved = types_resolved.typesFullyResolved;
1904pub const typeFullyResolved = types_resolved.checkType;1906pub const typeFullyResolved = types_resolved.checkType;
1905pub const valFullyResolved = types_resolved.checkVal;1907pub const valFullyResolved = types_resolved.checkVal;
1908
1909pub const CoveragePoint = enum(u1) {
1910 /// Indicates the block is not a place of interest corresponding to
1911 /// a source location for coverage purposes.
1912 none,
1913 /// Point of interest. The next instruction emitted corresponds to
1914 /// a source location used for coverage instrumentation.
1915 poi,
1916};
src/Sema.zig+80-25
...@@ -6898,8 +6898,14 @@ fn popErrorReturnTrace(...@@ -6898,8 +6898,14 @@ fn popErrorReturnTrace(
6898 .payload = sema.addExtraAssumeCapacity(Air.CondBr{6898 .payload = sema.addExtraAssumeCapacity(Air.CondBr{
6899 .then_body_len = @intCast(then_block.instructions.items.len),6899 .then_body_len = @intCast(then_block.instructions.items.len),
6900 .else_body_len = @intCast(else_block.instructions.items.len),6900 .else_body_len = @intCast(else_block.instructions.items.len),
6901 // weight against error branch6901 .branch_hints = .{
6902 .branch_hints = .{ .true = .likely, .false = .unlikely },6902 // Weight against error branch.
6903 .true = .likely,
6904 .false = .unlikely,
6905 // Code coverage is not valuable on either branch.
6906 .then_cov = .none,
6907 .else_cov = .none,
6908 },
6903 }),6909 }),
6904 },6910 },
6905 },6911 },
...@@ -11796,14 +11802,22 @@ fn zirSwitchBlockErrUnion(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Comp...@@ -11796,14 +11802,22 @@ fn zirSwitchBlockErrUnion(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Comp
1179611802
11797 _ = try child_block.addInst(.{11803 _ = try child_block.addInst(.{
11798 .tag = .cond_br,11804 .tag = .cond_br,
11799 .data = .{ .pl_op = .{11805 .data = .{
11800 .operand = cond,11806 .pl_op = .{
11801 .payload = sema.addExtraAssumeCapacity(Air.CondBr{11807 .operand = cond,
11802 .then_body_len = @intCast(true_instructions.len),11808 .payload = sema.addExtraAssumeCapacity(Air.CondBr{
11803 .else_body_len = @intCast(sub_block.instructions.items.len),11809 .then_body_len = @intCast(true_instructions.len),
11804 .branch_hints = .{ .true = non_error_hint, .false = .none },11810 .else_body_len = @intCast(sub_block.instructions.items.len),
11805 }),11811 .branch_hints = .{
11806 } },11812 .true = non_error_hint,
11813 .false = .none,
11814 // Code coverage is desired for error handling.
11815 .then_cov = .poi,
11816 .else_cov = .poi,
11817 },
11818 }),
11819 },
11820 },
11807 });11821 });
11808 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(true_instructions));11822 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(true_instructions));
11809 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(sub_block.instructions.items));11823 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(sub_block.instructions.items));
...@@ -12853,7 +12867,13 @@ fn analyzeSwitchRuntimeBlock(...@@ -12853,7 +12867,13 @@ fn analyzeSwitchRuntimeBlock(
12853 sema.air_instructions.items(.data)[@intFromEnum(prev_cond_br)].pl_op.payload = sema.addExtraAssumeCapacity(Air.CondBr{12867 sema.air_instructions.items(.data)[@intFromEnum(prev_cond_br)].pl_op.payload = sema.addExtraAssumeCapacity(Air.CondBr{
12854 .then_body_len = @intCast(prev_then_body.len),12868 .then_body_len = @intCast(prev_then_body.len),
12855 .else_body_len = @intCast(cond_body.len),12869 .else_body_len = @intCast(cond_body.len),
12856 .branch_hints = .{ .true = prev_hint, .false = .none },12870 .branch_hints = .{
12871 .true = prev_hint,
12872 .false = .none,
12873 // Code coverage is desired for error handling.
12874 .then_cov = .poi,
12875 .else_cov = .poi,
12876 },
12857 });12877 });
12858 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(prev_then_body));12878 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(prev_then_body));
12859 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(cond_body));12879 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(cond_body));
...@@ -13133,7 +13153,12 @@ fn analyzeSwitchRuntimeBlock(...@@ -13133,7 +13153,12 @@ fn analyzeSwitchRuntimeBlock(
13133 sema.air_instructions.items(.data)[@intFromEnum(prev_cond_br)].pl_op.payload = sema.addExtraAssumeCapacity(Air.CondBr{13153 sema.air_instructions.items(.data)[@intFromEnum(prev_cond_br)].pl_op.payload = sema.addExtraAssumeCapacity(Air.CondBr{
13134 .then_body_len = @intCast(prev_then_body.len),13154 .then_body_len = @intCast(prev_then_body.len),
13135 .else_body_len = @intCast(case_block.instructions.items.len),13155 .else_body_len = @intCast(case_block.instructions.items.len),
13136 .branch_hints = .{ .true = prev_hint, .false = else_hint },13156 .branch_hints = .{
13157 .true = prev_hint,
13158 .false = else_hint,
13159 .then_cov = .poi,
13160 .else_cov = .poi,
13161 },
13137 });13162 });
13138 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(prev_then_body));13163 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(prev_then_body));
13139 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));13164 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items));
...@@ -19250,7 +19275,17 @@ fn zirBoolBr(...@@ -19250,7 +19275,17 @@ fn zirBoolBr(
19250 &else_block,19275 &else_block,
19251 lhs,19276 lhs,
19252 block_inst,19277 block_inst,
19253 if (is_bool_or) .{ .true = .none, .false = rhs_hint } else .{ .true = rhs_hint, .false = .none },19278 if (is_bool_or) .{
19279 .true = .none,
19280 .false = rhs_hint,
19281 .then_cov = .poi,
19282 .else_cov = .poi,
19283 } else .{
19284 .true = rhs_hint,
19285 .false = .none,
19286 .then_cov = .poi,
19287 .else_cov = .poi,
19288 },
19254 );19289 );
19255 if (!rhs_noret) {19290 if (!rhs_noret) {
19256 if (try sema.resolveDefinedValue(rhs_block, rhs_src, coerced_rhs_result)) |rhs_val| {19291 if (try sema.resolveDefinedValue(rhs_block, rhs_src, coerced_rhs_result)) |rhs_val| {
...@@ -19467,14 +19502,22 @@ fn zirCondbr(...@@ -19467,14 +19502,22 @@ fn zirCondbr(
19467 true_instructions.len + sub_block.instructions.items.len);19502 true_instructions.len + sub_block.instructions.items.len);
19468 _ = try parent_block.addInst(.{19503 _ = try parent_block.addInst(.{
19469 .tag = .cond_br,19504 .tag = .cond_br,
19470 .data = .{ .pl_op = .{19505 .data = .{
19471 .operand = cond,19506 .pl_op = .{
19472 .payload = sema.addExtraAssumeCapacity(Air.CondBr{19507 .operand = cond,
19473 .then_body_len = @intCast(true_instructions.len),19508 .payload = sema.addExtraAssumeCapacity(Air.CondBr{
19474 .else_body_len = @intCast(sub_block.instructions.items.len),19509 .then_body_len = @intCast(true_instructions.len),
19475 .branch_hints = .{ .true = true_hint, .false = false_hint },19510 .else_body_len = @intCast(sub_block.instructions.items.len),
19476 }),19511 .branch_hints = .{
19477 } },19512 .true = true_hint,
19513 .false = false_hint,
19514 // Code coverage is desired for error handling.
19515 .then_cov = .poi,
19516 .else_cov = .poi,
19517 },
19518 }),
19519 },
19520 },
19478 });19521 });
19479 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(true_instructions));19522 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(true_instructions));
19480 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(sub_block.instructions.items));19523 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(sub_block.instructions.items));
...@@ -19851,8 +19894,14 @@ fn retWithErrTracing(...@@ -19851,8 +19894,14 @@ fn retWithErrTracing(
19851 const cond_br_payload = sema.addExtraAssumeCapacity(Air.CondBr{19894 const cond_br_payload = sema.addExtraAssumeCapacity(Air.CondBr{
19852 .then_body_len = @intCast(then_block.instructions.items.len),19895 .then_body_len = @intCast(then_block.instructions.items.len),
19853 .else_body_len = @intCast(else_block.instructions.items.len),19896 .else_body_len = @intCast(else_block.instructions.items.len),
19854 // weight against error branch19897 .branch_hints = .{
19855 .branch_hints = .{ .true = .likely, .false = .unlikely },19898 // Weight against error branch.
19899 .true = .likely,
19900 .false = .unlikely,
19901 // Code coverage is not valuable on either branch.
19902 .then_cov = .none,
19903 .else_cov = .none,
19904 },
19856 });19905 });
19857 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(then_block.instructions.items));19906 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(then_block.instructions.items));
19858 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(else_block.instructions.items));19907 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(else_block.instructions.items));
...@@ -27473,8 +27522,14 @@ fn addSafetyCheckExtra(...@@ -27473,8 +27522,14 @@ fn addSafetyCheckExtra(
27473 .payload = sema.addExtraAssumeCapacity(Air.CondBr{27522 .payload = sema.addExtraAssumeCapacity(Air.CondBr{
27474 .then_body_len = 1,27523 .then_body_len = 1,
27475 .else_body_len = @intCast(fail_block.instructions.items.len),27524 .else_body_len = @intCast(fail_block.instructions.items.len),
27476 // safety check failure branch is cold27525 .branch_hints = .{
27477 .branch_hints = .{ .true = .likely, .false = .cold },27526 // Safety check failure branch is cold.
27527 .true = .likely,
27528 .false = .cold,
27529 // Code coverage not wanted for panic branches.
27530 .then_cov = .none,
27531 .else_cov = .none,
27532 },
27478 }),27533 }),
27479 },27534 },
27480 },27535 },
src/codegen/llvm.zig+155-46
...@@ -822,6 +822,9 @@ pub const Object = struct {...@@ -822,6 +822,9 @@ pub const Object = struct {
822 /// This is denormalized data.822 /// This is denormalized data.
823 struct_field_map: std.AutoHashMapUnmanaged(ZigStructField, c_uint),823 struct_field_map: std.AutoHashMapUnmanaged(ZigStructField, c_uint),
824824
825 /// Values for `@llvm.used`.
826 used: std.ArrayListUnmanaged(Builder.Constant),
827
825 const ZigStructField = struct {828 const ZigStructField = struct {
826 struct_ty: InternPool.Index,829 struct_ty: InternPool.Index,
827 field_index: u32,830 field_index: u32,
...@@ -975,6 +978,7 @@ pub const Object = struct {...@@ -975,6 +978,7 @@ pub const Object = struct {
975 .error_name_table = .none,978 .error_name_table = .none,
976 .null_opt_usize = .no_init,979 .null_opt_usize = .no_init,
977 .struct_field_map = .{},980 .struct_field_map = .{},
981 .used = .{},
978 };982 };
979 return obj;983 return obj;
980 }984 }
...@@ -1097,44 +1101,57 @@ pub const Object = struct {...@@ -1097,44 +1101,57 @@ pub const Object = struct {
1097 lto: bool,1101 lto: bool,
1098 };1102 };
10991103
1100 pub fn emit(self: *Object, options: EmitOptions) !void {1104 pub fn emit(o: *Object, options: EmitOptions) !void {
1101 const zcu = self.pt.zcu;1105 const zcu = o.pt.zcu;
1102 const comp = zcu.comp;1106 const comp = zcu.comp;
11031107
1104 {1108 {
1105 try self.genErrorNameTable();1109 try o.genErrorNameTable();
1106 try self.genCmpLtErrorsLenFunction();1110 try o.genCmpLtErrorsLenFunction();
1107 try self.genModuleLevelAssembly();1111 try o.genModuleLevelAssembly();
1112
1113 if (o.used.items.len > 0) {
1114 const array_llvm_ty = try o.builder.arrayType(o.used.items.len, .ptr);
1115 const init_val = try o.builder.arrayConst(array_llvm_ty, o.used.items);
1116 const compiler_used_variable = try o.builder.addVariable(
1117 try o.builder.strtabString("llvm.used"),
1118 array_llvm_ty,
1119 .default,
1120 );
1121 compiler_used_variable.setLinkage(.appending, &o.builder);
1122 compiler_used_variable.setSection(try o.builder.string("llvm.metadata"), &o.builder);
1123 try compiler_used_variable.setInitializer(init_val, &o.builder);
1124 }
11081125
1109 if (!self.builder.strip) {1126 if (!o.builder.strip) {
1110 {1127 {
1111 var i: usize = 0;1128 var i: usize = 0;
1112 while (i < self.debug_unresolved_namespace_scopes.count()) : (i += 1) {1129 while (i < o.debug_unresolved_namespace_scopes.count()) : (i += 1) {
1113 const namespace_index = self.debug_unresolved_namespace_scopes.keys()[i];1130 const namespace_index = o.debug_unresolved_namespace_scopes.keys()[i];
1114 const fwd_ref = self.debug_unresolved_namespace_scopes.values()[i];1131 const fwd_ref = o.debug_unresolved_namespace_scopes.values()[i];
11151132
1116 const namespace = zcu.namespacePtr(namespace_index);1133 const namespace = zcu.namespacePtr(namespace_index);
1117 const debug_type = try self.lowerDebugType(Type.fromInterned(namespace.owner_type));1134 const debug_type = try o.lowerDebugType(Type.fromInterned(namespace.owner_type));
11181135
1119 self.builder.debugForwardReferenceSetType(fwd_ref, debug_type);1136 o.builder.debugForwardReferenceSetType(fwd_ref, debug_type);
1120 }1137 }
1121 }1138 }
11221139
1123 self.builder.debugForwardReferenceSetType(1140 o.builder.debugForwardReferenceSetType(
1124 self.debug_enums_fwd_ref,1141 o.debug_enums_fwd_ref,
1125 try self.builder.metadataTuple(self.debug_enums.items),1142 try o.builder.metadataTuple(o.debug_enums.items),
1126 );1143 );
11271144
1128 self.builder.debugForwardReferenceSetType(1145 o.builder.debugForwardReferenceSetType(
1129 self.debug_globals_fwd_ref,1146 o.debug_globals_fwd_ref,
1130 try self.builder.metadataTuple(self.debug_globals.items),1147 try o.builder.metadataTuple(o.debug_globals.items),
1131 );1148 );
1132 }1149 }
1133 }1150 }
11341151
1135 const target_triple_sentinel =1152 const target_triple_sentinel =
1136 try self.gpa.dupeZ(u8, self.builder.target_triple.slice(&self.builder).?);1153 try o.gpa.dupeZ(u8, o.builder.target_triple.slice(&o.builder).?);
1137 defer self.gpa.free(target_triple_sentinel);1154 defer o.gpa.free(target_triple_sentinel);
11381155
1139 const emit_asm_msg = options.asm_path orelse "(none)";1156 const emit_asm_msg = options.asm_path orelse "(none)";
1140 const emit_bin_msg = options.bin_path orelse "(none)";1157 const emit_bin_msg = options.bin_path orelse "(none)";
...@@ -1147,15 +1164,15 @@ pub const Object = struct {...@@ -1147,15 +1164,15 @@ pub const Object = struct {
1147 const context, const module = emit: {1164 const context, const module = emit: {
1148 if (options.pre_ir_path) |path| {1165 if (options.pre_ir_path) |path| {
1149 if (std.mem.eql(u8, path, "-")) {1166 if (std.mem.eql(u8, path, "-")) {
1150 self.builder.dump();1167 o.builder.dump();
1151 } else {1168 } else {
1152 _ = try self.builder.printToFile(path);1169 _ = try o.builder.printToFile(path);
1153 }1170 }
1154 }1171 }
11551172
1156 const bitcode = try self.builder.toBitcode(self.gpa);1173 const bitcode = try o.builder.toBitcode(o.gpa);
1157 defer self.gpa.free(bitcode);1174 defer o.gpa.free(bitcode);
1158 self.builder.clearAndFree();1175 o.builder.clearAndFree();
11591176
1160 if (options.pre_bc_path) |path| {1177 if (options.pre_bc_path) |path| {
1161 var file = try std.fs.cwd().createFile(path, .{});1178 var file = try std.fs.cwd().createFile(path, .{});
...@@ -1283,7 +1300,10 @@ pub const Object = struct {...@@ -1283,7 +1300,10 @@ pub const Object = struct {
1283 .bitcode_filename = null,1300 .bitcode_filename = null,
1284 .coverage = .{1301 .coverage = .{
1285 .CoverageType = .Edge,1302 .CoverageType = .Edge,
1286 .IndirectCalls = true,1303 // Works in tandem with Inline8bitCounters or InlineBoolFlag.
1304 // Zig does not yet implement its own version of this but it
1305 // needs to for better fuzzing logic.
1306 .IndirectCalls = false,
1287 .TraceBB = false,1307 .TraceBB = false,
1288 .TraceCmp = true,1308 .TraceCmp = true,
1289 .TraceDiv = false,1309 .TraceDiv = false,
...@@ -1291,10 +1311,13 @@ pub const Object = struct {...@@ -1291,10 +1311,13 @@ pub const Object = struct {
1291 .Use8bitCounters = false,1311 .Use8bitCounters = false,
1292 .TracePC = false,1312 .TracePC = false,
1293 .TracePCGuard = comp.config.san_cov_trace_pc_guard,1313 .TracePCGuard = comp.config.san_cov_trace_pc_guard,
1294 .Inline8bitCounters = true,1314 // Zig emits its own inline 8-bit counters instrumentation.
1315 .Inline8bitCounters = false,
1295 .InlineBoolFlag = false,1316 .InlineBoolFlag = false,
1296 .PCTable = true,1317 // Zig emits its own PC table instrumentation.
1318 .PCTable = false,
1297 .NoPrune = false,1319 .NoPrune = false,
1320 // Workaround for https://github.com/llvm/llvm-project/pull/106464
1298 .StackDepth = true,1321 .StackDepth = true,
1299 .TraceLoads = false,1322 .TraceLoads = false,
1300 .TraceStores = false,1323 .TraceStores = false,
...@@ -1655,6 +1678,29 @@ pub const Object = struct {...@@ -1655,6 +1678,29 @@ pub const Object = struct {
1655 break :debug_info .{ file, subprogram };1678 break :debug_info .{ file, subprogram };
1656 } else .{.none} ** 2;1679 } else .{.none} ** 2;
16571680
1681 const fuzz: ?FuncGen.Fuzz = f: {
1682 if (!owner_mod.fuzz) break :f null;
1683 if (func_analysis.disable_instrumentation) break :f null;
1684 if (is_naked) break :f null;
1685 if (comp.config.san_cov_trace_pc_guard) break :f null;
1686
1687 // The void type used here is a placeholder to be replaced with an
1688 // array of the appropriate size after the POI count is known.
1689
1690 // Due to error "members of llvm.compiler.used must be named", this global needs a name.
1691 const anon_name = try o.builder.strtabStringFmt("__sancov_gen_.{d}", .{o.used.items.len});
1692 const counters_variable = try o.builder.addVariable(anon_name, .void, .default);
1693 try o.used.append(gpa, counters_variable.toConst(&o.builder));
1694 counters_variable.setLinkage(.private, &o.builder);
1695 counters_variable.setAlignment(comptime Builder.Alignment.fromByteUnits(1), &o.builder);
1696 counters_variable.setSection(try o.builder.string("__sancov_cntrs"), &o.builder);
1697
1698 break :f .{
1699 .counters_variable = counters_variable,
1700 .pcs = .{},
1701 };
1702 };
1703
1658 var fg: FuncGen = .{1704 var fg: FuncGen = .{
1659 .gpa = gpa,1705 .gpa = gpa,
1660 .air = air,1706 .air = air,
...@@ -1662,6 +1708,7 @@ pub const Object = struct {...@@ -1662,6 +1708,7 @@ pub const Object = struct {
1662 .ng = &ng,1708 .ng = &ng,
1663 .wip = wip,1709 .wip = wip,
1664 .is_naked = fn_info.cc == .Naked,1710 .is_naked = fn_info.cc == .Naked,
1711 .fuzz = fuzz,
1665 .ret_ptr = ret_ptr,1712 .ret_ptr = ret_ptr,
1666 .args = args.items,1713 .args = args.items,
1667 .arg_index = 0,1714 .arg_index = 0,
...@@ -1679,15 +1726,36 @@ pub const Object = struct {...@@ -1679,15 +1726,36 @@ pub const Object = struct {
1679 defer fg.deinit();1726 defer fg.deinit();
1680 deinit_wip = false;1727 deinit_wip = false;
16811728
1682 fg.genBody(air.getMainBody()) catch |err| switch (err) {1729 fg.genBody(air.getMainBody(), .poi) catch |err| switch (err) {
1683 error.CodegenFail => {1730 error.CodegenFail => {
1684 try zcu.failed_codegen.put(zcu.gpa, func.owner_nav, ng.err_msg.?);1731 try zcu.failed_codegen.put(gpa, func.owner_nav, ng.err_msg.?);
1685 ng.err_msg = null;1732 ng.err_msg = null;
1686 return;1733 return;
1687 },1734 },
1688 else => |e| return e,1735 else => |e| return e,
1689 };1736 };
16901737
1738 if (fg.fuzz) |*f| {
1739 {
1740 const array_llvm_ty = try o.builder.arrayType(f.pcs.items.len, .i8);
1741 f.counters_variable.ptrConst(&o.builder).global.ptr(&o.builder).type = array_llvm_ty;
1742 const zero_init = try o.builder.zeroInitConst(array_llvm_ty);
1743 try f.counters_variable.setInitializer(zero_init, &o.builder);
1744 }
1745
1746 const array_llvm_ty = try o.builder.arrayType(f.pcs.items.len, .ptr);
1747 const init_val = try o.builder.arrayConst(array_llvm_ty, f.pcs.items);
1748 // Due to error "members of llvm.compiler.used must be named", this global needs a name.
1749 const anon_name = try o.builder.strtabStringFmt("__sancov_gen_.{d}", .{o.used.items.len});
1750 const pcs_variable = try o.builder.addVariable(anon_name, array_llvm_ty, .default);
1751 try o.used.append(gpa, pcs_variable.toConst(&o.builder));
1752 pcs_variable.setLinkage(.private, &o.builder);
1753 pcs_variable.setMutability(.constant, &o.builder);
1754 pcs_variable.setAlignment(Type.usize.abiAlignment(zcu).toLlvm(), &o.builder);
1755 pcs_variable.setSection(try o.builder.string("__sancov_pcs1"), &o.builder);
1756 try pcs_variable.setInitializer(init_val, &o.builder);
1757 }
1758
1691 try fg.wip.finish();1759 try fg.wip.finish();
1692 }1760 }
16931761
...@@ -4729,6 +4797,7 @@ pub const FuncGen = struct {...@@ -4729,6 +4797,7 @@ pub const FuncGen = struct {
4729 liveness: Liveness,4797 liveness: Liveness,
4730 wip: Builder.WipFunction,4798 wip: Builder.WipFunction,
4731 is_naked: bool,4799 is_naked: bool,
4800 fuzz: ?Fuzz,
47324801
4733 file: Builder.Metadata,4802 file: Builder.Metadata,
4734 scope: Builder.Metadata,4803 scope: Builder.Metadata,
...@@ -4769,6 +4838,16 @@ pub const FuncGen = struct {...@@ -4769,6 +4838,16 @@ pub const FuncGen = struct {
47694838
4770 sync_scope: Builder.SyncScope,4839 sync_scope: Builder.SyncScope,
47714840
4841 const Fuzz = struct {
4842 counters_variable: Builder.Variable.Index,
4843 pcs: std.ArrayListUnmanaged(Builder.Constant),
4844
4845 fn deinit(f: *Fuzz, gpa: Allocator) void {
4846 f.pcs.deinit(gpa);
4847 f.* = undefined;
4848 }
4849 };
4850
4772 const BreakList = union {4851 const BreakList = union {
4773 list: std.MultiArrayList(struct {4852 list: std.MultiArrayList(struct {
4774 bb: Builder.Function.Block.Index,4853 bb: Builder.Function.Block.Index,
...@@ -4778,9 +4857,11 @@ pub const FuncGen = struct {...@@ -4778,9 +4857,11 @@ pub const FuncGen = struct {
4778 };4857 };
47794858
4780 fn deinit(self: *FuncGen) void {4859 fn deinit(self: *FuncGen) void {
4860 const gpa = self.gpa;
4861 if (self.fuzz) |*f| f.deinit(self.gpa);
4781 self.wip.deinit();4862 self.wip.deinit();
4782 self.func_inst_table.deinit(self.gpa);4863 self.func_inst_table.deinit(gpa);
4783 self.blocks.deinit(self.gpa);4864 self.blocks.deinit(gpa);
4784 }4865 }
47854866
4786 fn todo(self: *FuncGen, comptime format: []const u8, args: anytype) Error {4867 fn todo(self: *FuncGen, comptime format: []const u8, args: anytype) Error {
...@@ -4836,11 +4917,33 @@ pub const FuncGen = struct {...@@ -4836,11 +4917,33 @@ pub const FuncGen = struct {
4836 return o.null_opt_usize;4917 return o.null_opt_usize;
4837 }4918 }
48384919
4839 fn genBody(self: *FuncGen, body: []const Air.Inst.Index) Error!void {4920 fn genBody(self: *FuncGen, body: []const Air.Inst.Index, coverage_point: Air.CoveragePoint) Error!void {
4840 const o = self.ng.object;4921 const o = self.ng.object;
4841 const zcu = o.pt.zcu;4922 const zcu = o.pt.zcu;
4842 const ip = &zcu.intern_pool;4923 const ip = &zcu.intern_pool;
4843 const air_tags = self.air.instructions.items(.tag);4924 const air_tags = self.air.instructions.items(.tag);
4925 switch (coverage_point) {
4926 .none => {},
4927 .poi => if (self.fuzz) |*fuzz| {
4928 const poi_index = fuzz.pcs.items.len;
4929 const base_ptr = fuzz.counters_variable.toValue(&o.builder);
4930 const ptr = if (poi_index == 0) base_ptr else try self.wip.gep(.inbounds, .i8, base_ptr, &.{
4931 try o.builder.intValue(.i32, poi_index),
4932 }, "");
4933 const counter = try self.wip.load(.normal, .i8, ptr, .default, "");
4934 const one = try o.builder.intValue(.i8, 1);
4935 const counter_incremented = try self.wip.bin(.add, counter, one, "");
4936 _ = try self.wip.store(.normal, counter_incremented, ptr, .default);
4937
4938 // LLVM does not allow blockaddress on the entry block.
4939 const pc = if (self.wip.cursor.block == .entry)
4940 self.wip.function.toConst(&o.builder)
4941 else
4942 try o.builder.blockAddrConst(self.wip.function, self.wip.cursor.block);
4943 const gpa = self.gpa;
4944 try fuzz.pcs.append(gpa, pc);
4945 },
4946 }
4844 for (body, 0..) |inst, i| {4947 for (body, 0..) |inst, i| {
4845 if (self.liveness.isUnused(inst) and !self.air.mustLower(inst, ip)) continue;4948 if (self.liveness.isUnused(inst) and !self.air.mustLower(inst, ip)) continue;
48464949
...@@ -4949,7 +5052,7 @@ pub const FuncGen = struct {...@@ -4949,7 +5052,7 @@ pub const FuncGen = struct {
4949 .ret_ptr => try self.airRetPtr(inst),5052 .ret_ptr => try self.airRetPtr(inst),
4950 .arg => try self.airArg(inst),5053 .arg => try self.airArg(inst),
4951 .bitcast => try self.airBitCast(inst),5054 .bitcast => try self.airBitCast(inst),
4952 .int_from_bool => try self.airIntFromBool(inst),5055 .int_from_bool => try self.airIntFromBool(inst),
4953 .block => try self.airBlock(inst),5056 .block => try self.airBlock(inst),
4954 .br => try self.airBr(inst),5057 .br => try self.airBr(inst),
4955 .switch_br => try self.airSwitchBr(inst),5058 .switch_br => try self.airSwitchBr(inst),
...@@ -4966,7 +5069,7 @@ pub const FuncGen = struct {...@@ -4966,7 +5069,7 @@ pub const FuncGen = struct {
4966 .trunc => try self.airTrunc(inst),5069 .trunc => try self.airTrunc(inst),
4967 .fptrunc => try self.airFptrunc(inst),5070 .fptrunc => try self.airFptrunc(inst),
4968 .fpext => try self.airFpext(inst),5071 .fpext => try self.airFpext(inst),
4969 .int_from_ptr => try self.airIntFromPtr(inst),5072 .int_from_ptr => try self.airIntFromPtr(inst),
4970 .load => try self.airLoad(body[i..]),5073 .load => try self.airLoad(body[i..]),
4971 .loop => try self.airLoop(inst),5074 .loop => try self.airLoop(inst),
4972 .not => try self.airNot(inst),5075 .not => try self.airNot(inst),
...@@ -5089,8 +5192,13 @@ pub const FuncGen = struct {...@@ -5089,8 +5192,13 @@ pub const FuncGen = struct {
5089 }5192 }
5090 }5193 }
50915194
5092 fn genBodyDebugScope(self: *FuncGen, maybe_inline_func: ?InternPool.Index, body: []const Air.Inst.Index) Error!void {5195 fn genBodyDebugScope(
5093 if (self.wip.strip) return self.genBody(body);5196 self: *FuncGen,
5197 maybe_inline_func: ?InternPool.Index,
5198 body: []const Air.Inst.Index,
5199 coverage_point: Air.CoveragePoint,
5200 ) Error!void {
5201 if (self.wip.strip) return self.genBody(body, coverage_point);
50945202
5095 const old_file = self.file;5203 const old_file = self.file;
5096 const old_inlined = self.inlined;5204 const old_inlined = self.inlined;
...@@ -5137,7 +5245,8 @@ pub const FuncGen = struct {...@@ -5137,7 +5245,8 @@ pub const FuncGen = struct {
5137 .sp_flags = .{5245 .sp_flags = .{
5138 .Optimized = mod.optimize_mode != .Debug,5246 .Optimized = mod.optimize_mode != .Debug,
5139 .Definition = true,5247 .Definition = true,
5140 .LocalToUnit = true, // TODO: we can't know this at this point, since the function could be exported later!5248 // TODO: we can't know this at this point, since the function could be exported later!
5249 .LocalToUnit = true,
5141 },5250 },
5142 },5251 },
5143 o.debug_compile_unit,5252 o.debug_compile_unit,
...@@ -5171,7 +5280,7 @@ pub const FuncGen = struct {...@@ -5171,7 +5280,7 @@ pub const FuncGen = struct {
5171 .no_location => {},5280 .no_location => {},
5172 };5281 };
51735282
5174 try self.genBody(body);5283 try self.genBody(body, coverage_point);
5175 }5284 }
51765285
5177 pub const CallAttr = enum {5286 pub const CallAttr = enum {
...@@ -5881,7 +5990,7 @@ pub const FuncGen = struct {...@@ -5881,7 +5990,7 @@ pub const FuncGen = struct {
5881 const inst_ty = self.typeOfIndex(inst);5990 const inst_ty = self.typeOfIndex(inst);
58825991
5883 if (inst_ty.isNoReturn(zcu)) {5992 if (inst_ty.isNoReturn(zcu)) {
5884 try self.genBodyDebugScope(maybe_inline_func, body);5993 try self.genBodyDebugScope(maybe_inline_func, body, .none);
5885 return .none;5994 return .none;
5886 }5995 }
58875996
...@@ -5897,7 +6006,7 @@ pub const FuncGen = struct {...@@ -5897,7 +6006,7 @@ pub const FuncGen = struct {
5897 });6006 });
5898 defer assert(self.blocks.remove(inst));6007 defer assert(self.blocks.remove(inst));
58996008
5900 try self.genBodyDebugScope(maybe_inline_func, body);6009 try self.genBodyDebugScope(maybe_inline_func, body, .none);
59016010
5902 self.wip.cursor = .{ .block = parent_bb };6011 self.wip.cursor = .{ .block = parent_bb };
59036012
...@@ -5996,11 +6105,11 @@ pub const FuncGen = struct {...@@ -5996,11 +6105,11 @@ pub const FuncGen = struct {
59966105
5997 self.wip.cursor = .{ .block = then_block };6106 self.wip.cursor = .{ .block = then_block };
5998 if (hint == .then_cold) _ = try self.wip.callIntrinsicAssumeCold();6107 if (hint == .then_cold) _ = try self.wip.callIntrinsicAssumeCold();
5999 try self.genBodyDebugScope(null, then_body);6108 try self.genBodyDebugScope(null, then_body, extra.data.branch_hints.then_cov);
60006109
6001 self.wip.cursor = .{ .block = else_block };6110 self.wip.cursor = .{ .block = else_block };
6002 if (hint == .else_cold) _ = try self.wip.callIntrinsicAssumeCold();6111 if (hint == .else_cold) _ = try self.wip.callIntrinsicAssumeCold();
6003 try self.genBodyDebugScope(null, else_body);6112 try self.genBodyDebugScope(null, else_body, extra.data.branch_hints.else_cov);
60046113
6005 // No need to reset the insert cursor since this instruction is noreturn.6114 // No need to reset the insert cursor since this instruction is noreturn.
6006 return .none;6115 return .none;
...@@ -6085,7 +6194,7 @@ pub const FuncGen = struct {...@@ -6085,7 +6194,7 @@ pub const FuncGen = struct {
60856194
6086 fg.wip.cursor = .{ .block = return_block };6195 fg.wip.cursor = .{ .block = return_block };
6087 if (err_cold) _ = try fg.wip.callIntrinsicAssumeCold();6196 if (err_cold) _ = try fg.wip.callIntrinsicAssumeCold();
6088 try fg.genBodyDebugScope(null, body);6197 try fg.genBodyDebugScope(null, body, .poi);
60896198
6090 fg.wip.cursor = .{ .block = continue_block };6199 fg.wip.cursor = .{ .block = continue_block };
6091 }6200 }
...@@ -6196,14 +6305,14 @@ pub const FuncGen = struct {...@@ -6196,14 +6305,14 @@ pub const FuncGen = struct {
6196 }6305 }
6197 self.wip.cursor = .{ .block = case_block };6306 self.wip.cursor = .{ .block = case_block };
6198 if (switch_br.getHint(case.idx) == .cold) _ = try self.wip.callIntrinsicAssumeCold();6307 if (switch_br.getHint(case.idx) == .cold) _ = try self.wip.callIntrinsicAssumeCold();
6199 try self.genBodyDebugScope(null, case.body);6308 try self.genBodyDebugScope(null, case.body, .poi);
6200 }6309 }
62016310
6202 const else_body = it.elseBody();6311 const else_body = it.elseBody();
6203 self.wip.cursor = .{ .block = else_block };6312 self.wip.cursor = .{ .block = else_block };
6204 if (switch_br.getElseHint() == .cold) _ = try self.wip.callIntrinsicAssumeCold();6313 if (switch_br.getElseHint() == .cold) _ = try self.wip.callIntrinsicAssumeCold();
6205 if (else_body.len != 0) {6314 if (else_body.len != 0) {
6206 try self.genBodyDebugScope(null, else_body);6315 try self.genBodyDebugScope(null, else_body, .poi);
6207 } else {6316 } else {
6208 _ = try self.wip.@"unreachable"();6317 _ = try self.wip.@"unreachable"();
6209 }6318 }
...@@ -6222,7 +6331,7 @@ pub const FuncGen = struct {...@@ -6222,7 +6331,7 @@ pub const FuncGen = struct {
6222 _ = try self.wip.br(loop_block);6331 _ = try self.wip.br(loop_block);
62236332
6224 self.wip.cursor = .{ .block = loop_block };6333 self.wip.cursor = .{ .block = loop_block };
6225 try self.genBodyDebugScope(null, body);6334 try self.genBodyDebugScope(null, body, .none);
62266335
6227 // TODO instead of this logic, change AIR to have the property that6336 // TODO instead of this logic, change AIR to have the property that
6228 // every block is guaranteed to end with a noreturn instruction.6337 // every block is guaranteed to end with a noreturn instruction.
src/codegen/llvm/Builder.zig+3-2
...@@ -10046,8 +10046,9 @@ pub fn printUnbuffered(...@@ -10046,8 +10046,9 @@ pub fn printUnbuffered(
10046 }10046 }
1004710047
10048 if (maybe_dbg_index) |dbg_index| {10048 if (maybe_dbg_index) |dbg_index| {
10049 try writer.print(", !dbg !{}\n", .{dbg_index});10049 try writer.print(", !dbg !{}", .{dbg_index});
10050 } else try writer.writeByte('\n');10050 }
10051 try writer.writeByte('\n');
10051 }10052 }
10052 try writer.writeByte('}');10053 try writer.writeByte('}');
10053 }10054 }
src/print_air.zig+16-2
...@@ -791,7 +791,14 @@ const Writer = struct {...@@ -791,7 +791,14 @@ const Writer = struct {
791791
792 try w.writeOperand(s, inst, 0, pl_op.operand);792 try w.writeOperand(s, inst, 0, pl_op.operand);
793 if (w.skip_body) return s.writeAll(", ...");793 if (w.skip_body) return s.writeAll(", ...");
794 try s.writeAll(", {\n");794 try s.writeAll(",");
795 if (extra.data.branch_hints.true != .none) {
796 try s.print(" {s}", .{@tagName(extra.data.branch_hints.true)});
797 }
798 if (extra.data.branch_hints.then_cov != .none) {
799 try s.print(" {s}", .{@tagName(extra.data.branch_hints.then_cov)});
800 }
801 try s.writeAll(" {\n");
795 const old_indent = w.indent;802 const old_indent = w.indent;
796 w.indent += 2;803 w.indent += 2;
797804
...@@ -806,7 +813,14 @@ const Writer = struct {...@@ -806,7 +813,14 @@ const Writer = struct {
806813
807 try w.writeBody(s, then_body);814 try w.writeBody(s, then_body);
808 try s.writeByteNTimes(' ', old_indent);815 try s.writeByteNTimes(' ', old_indent);
809 try s.writeAll("}, {\n");816 try s.writeAll("},");
817 if (extra.data.branch_hints.false != .none) {
818 try s.print(" {s}", .{@tagName(extra.data.branch_hints.false)});
819 }
820 if (extra.data.branch_hints.else_cov != .none) {
821 try s.print(" {s}", .{@tagName(extra.data.branch_hints.else_cov)});
822 }
823 try s.writeAll(" {\n");
810824
811 if (liveness_condbr.else_deaths.len != 0) {825 if (liveness_condbr.else_deaths.len != 0) {
812 try s.writeByteNTimes(' ', w.indent);826 try s.writeByteNTimes(' ', w.indent);