| author | |
| committer | |
| log | e9a00ba7f4ef2546cd0c98559002431c749374fe |
| tree | 18102a8fd19ea54f1049ba6a0be522391a2bb7c7 |
| parent | 6a21875ddbe0f509122fbd220f1abb015cc7bac7 |
| parent | 13b5cee4cce2be7b5d1423fcd59b00ff1807142e |
| signature |
exclude unreachable code paths from having coverage instrumentation9 files changed, 333 insertions(+), 137 deletions(-)
lib/compiler/test_runner.zig+1-2| ... | ... | @@ -166,6 +166,7 @@ fn mainServer() !void { |
| 166 | 166 | if (log_err_count != 0) @panic("error logs detected"); |
| 167 | 167 | if (first) { |
| 168 | 168 | first = false; |
| 169 | const entry_addr = @intFromPtr(test_fn.func); | |
| 169 | 170 | try server.serveU64Message(.fuzz_start_addr, entry_addr); |
| 170 | 171 | } |
| 171 | 172 | } |
| ... | ... | @@ -347,7 +348,6 @@ const FuzzerSlice = extern struct { |
| 347 | 348 | }; |
| 348 | 349 | |
| 349 | 350 | var is_fuzz_test: bool = undefined; |
| 350 | var entry_addr: usize = 0; | |
| 351 | 351 | |
| 352 | 352 | extern fn fuzzer_next() FuzzerSlice; |
| 353 | 353 | extern fn fuzzer_init(cache_dir: FuzzerSlice) void; |
| ... | ... | @@ -358,7 +358,6 @@ pub fn fuzzInput(options: testing.FuzzInputOptions) []const u8 { |
| 358 | 358 | if (crippled) return ""; |
| 359 | 359 | is_fuzz_test = true; |
| 360 | 360 | if (builtin.fuzz) { |
| 361 | if (entry_addr == 0) entry_addr = @returnAddress(); | |
| 362 | 361 | return fuzzer_next().toSlice(); |
| 363 | 362 | } |
| 364 | 363 | if (options.corpus.len == 0) return ""; |
lib/fuzzer.zig+44-48| ... | ... | @@ -30,19 +30,6 @@ fn logOverride( |
| 30 | 30 | |
| 31 | 31 | export threadlocal var __sancov_lowest_stack: usize = std.math.maxInt(usize); |
| 32 | 32 | |
| 33 | var module_count_8bc: usize = 0; | |
| 34 | var module_count_pcs: usize = 0; | |
| 35 | ||
| 36 | export 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 | ||
| 41 | export 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 | ||
| 46 | 33 | export fn __sanitizer_cov_trace_const_cmp1(arg1: u8, arg2: u8) void { |
| 47 | 34 | handleCmp(@returnAddress(), arg1, arg2); |
| 48 | 35 | } |
| ... | ... | @@ -105,7 +92,7 @@ const Fuzzer = struct { |
| 105 | 92 | gpa: Allocator, |
| 106 | 93 | rng: std.Random.DefaultPrng, |
| 107 | 94 | input: std.ArrayListUnmanaged(u8), |
| 108 | flagged_pcs: []const FlaggedPc, | |
| 95 | pcs: []const usize, | |
| 109 | 96 | pc_counters: []u8, |
| 110 | 97 | n_runs: usize, |
| 111 | 98 | recent_cases: RunMap, |
| ... | ... | @@ -174,32 +161,18 @@ const Fuzzer = struct { |
| 174 | 161 | } |
| 175 | 162 | }; |
| 176 | 163 | |
| 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 | 164 | const Analysis = struct { |
| 186 | 165 | score: usize, |
| 187 | 166 | id: Run.Id, |
| 188 | 167 | }; |
| 189 | 168 | |
| 190 | fn init(f: *Fuzzer, cache_dir: std.fs.Dir) !void { | |
| 191 | const flagged_pcs = f.flagged_pcs; | |
| 192 | ||
| 169 | fn init(f: *Fuzzer, cache_dir: std.fs.Dir, pc_counters: []u8, pcs: []const usize) !void { | |
| 193 | 170 | f.cache_dir = cache_dir; |
| 171 | f.pc_counters = pc_counters; | |
| 172 | f.pcs = pcs; | |
| 194 | 173 | |
| 195 | 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: { | |
| 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 | }; | |
| 175 | const pc_digest = std.hash.Wyhash.hash(0, std.mem.sliceAsBytes(pcs)); | |
| 203 | 176 | f.coverage_id = pc_digest; |
| 204 | 177 | const hex_digest = std.fmt.hex(pc_digest); |
| 205 | 178 | const coverage_file_path = "v/" ++ hex_digest; |
| ... | ... | @@ -213,12 +186,12 @@ const Fuzzer = struct { |
| 213 | 186 | .truncate = false, |
| 214 | 187 | }); |
| 215 | 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 | 190 | comptime assert(SeenPcsHeader.trailing[0] == .pc_bits_usize); |
| 218 | 191 | comptime assert(SeenPcsHeader.trailing[1] == .pc_addr); |
| 219 | 192 | const bytes_len = @sizeOf(SeenPcsHeader) + |
| 220 | 193 | n_bitset_elems * @sizeOf(usize) + |
| 221 | flagged_pcs.len * @sizeOf(usize); | |
| 194 | pcs.len * @sizeOf(usize); | |
| 222 | 195 | const existing_len = coverage_file.getEndPos() catch |err| { |
| 223 | 196 | fatal("unable to check len of coverage file: {s}", .{@errorName(err)}); |
| 224 | 197 | }; |
| ... | ... | @@ -233,12 +206,12 @@ const Fuzzer = struct { |
| 233 | 206 | fatal("unable to init coverage memory map: {s}", .{@errorName(err)}); |
| 234 | 207 | }; |
| 235 | 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 | 210 | const existing_pcs = std.mem.bytesAsSlice(usize, existing_pcs_bytes); |
| 238 | for (existing_pcs, flagged_pcs, 0..) |old, new, i| { | |
| 239 | if (old != new.addr) { | |
| 211 | for (existing_pcs, pcs, 0..) |old, new, i| { | |
| 212 | if (old != new) { | |
| 240 | 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 | 219 | const header: SeenPcsHeader = .{ |
| 247 | 220 | .n_runs = 0, |
| 248 | 221 | .unique_runs = 0, |
| 249 | .pcs_len = flagged_pcs.len, | |
| 222 | .pcs_len = pcs.len, | |
| 250 | 223 | .lowest_stack = std.math.maxInt(usize), |
| 251 | 224 | }; |
| 252 | 225 | f.seen_pcs.appendSliceAssumeCapacity(std.mem.asBytes(&header)); |
| 253 | 226 | f.seen_pcs.appendNTimesAssumeCapacity(0, n_bitset_elems * @sizeOf(usize)); |
| 254 | for (flagged_pcs) |flagged_pc| { | |
| 255 | f.seen_pcs.appendSliceAssumeCapacity(std.mem.asBytes(&flagged_pc.addr)); | |
| 256 | } | |
| 227 | f.seen_pcs.appendSliceAssumeCapacity(std.mem.sliceAsBytes(pcs)); | |
| 257 | 228 | } |
| 258 | 229 | } |
| 259 | 230 | |
| ... | ... | @@ -307,8 +278,8 @@ const Fuzzer = struct { |
| 307 | 278 | // Track code coverage from all runs. |
| 308 | 279 | comptime assert(SeenPcsHeader.trailing[0] == .pc_bits_usize); |
| 309 | 280 | const header_end_ptr: [*]volatile usize = @ptrCast(f.seen_pcs.items[@sizeOf(SeenPcsHeader)..]); |
| 310 | const remainder = f.flagged_pcs.len % @bitSizeOf(usize); | |
| 311 | const aligned_len = f.flagged_pcs.len - remainder; | |
| 281 | const remainder = f.pcs.len % @bitSizeOf(usize); | |
| 282 | const aligned_len = f.pcs.len - remainder; | |
| 312 | 283 | const seen_pcs = header_end_ptr[0..aligned_len]; |
| 313 | 284 | const pc_counters = std.mem.bytesAsSlice([@bitSizeOf(usize)]u8, f.pc_counters[0..aligned_len]); |
| 314 | 285 | const V = @Vector(@bitSizeOf(usize), u8); |
| ... | ... | @@ -433,7 +404,7 @@ var fuzzer: Fuzzer = .{ |
| 433 | 404 | .gpa = general_purpose_allocator.allocator(), |
| 434 | 405 | .rng = std.Random.DefaultPrng.init(0), |
| 435 | 406 | .input = .{}, |
| 436 | .flagged_pcs = undefined, | |
| 407 | .pcs = undefined, | |
| 437 | 408 | .pc_counters = undefined, |
| 438 | 409 | .n_runs = 0, |
| 439 | 410 | .recent_cases = .{}, |
| ... | ... | @@ -455,8 +426,32 @@ export fn fuzzer_next() Fuzzer.Slice { |
| 455 | 426 | } |
| 456 | 427 | |
| 457 | 428 | export fn fuzzer_init(cache_dir_struct: Fuzzer.Slice) void { |
| 458 | if (module_count_8bc == 0) fatal("__sanitizer_cov_8bit_counters_init was never called", .{}); | |
| 459 | if (module_count_pcs == 0) fatal("__sanitizer_cov_pcs_init was never called", .{}); | |
| 429 | // Linkers are expected to automatically add `__start_<section>` and | |
| 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]; | |
| 460 | 455 | |
| 461 | 456 | const cache_dir_path = cache_dir_struct.toZig(); |
| 462 | 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 | 461 | fatal("unable to open fuzz directory '{s}': {s}", .{ cache_dir_path, @errorName(err) }); |
| 467 | 462 | }; |
| 468 | 463 | |
| 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 | } |
| 471 | 467 | |
| 472 | 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 | 664 | const coverage_map = ws.coverage_files.getPtr(coverage_id).?; |
| 665 | 665 | const header: *const abi.SeenPcsHeader = @ptrCast(coverage_map.mapped_memory[0..@sizeOf(abi.SeenPcsHeader)]); |
| 666 | 666 | const pcs = header.pcAddrs(); |
| 667 | const index = std.sort.upperBound(usize, pcs, addr, struct { | |
| 668 | fn order(context: usize, item: usize) std.math.Order { | |
| 669 | return std.math.order(item, context); | |
| 667 | // Since this pcs list is unsorted, we must linear scan for the best index. | |
| 668 | const index = i: { | |
| 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 | 677 | if (index >= pcs.len) { |
| 673 | 678 | log.err("unable to find unit test entry address 0x{x} in source locations (range: 0x{x} to 0x{x})", .{ |
| 674 | 679 | addr, pcs[0], pcs[pcs.len - 1], |
| ... | ... | @@ -678,8 +683,8 @@ fn addEntryPoint(ws: *WebServer, coverage_id: u64, addr: u64) error{ AlreadyRepo |
| 678 | 683 | if (false) { |
| 679 | 684 | const sl = coverage_map.source_locations[index]; |
| 680 | 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}", .{ | |
| 682 | addr, file_name, sl.line, sl.column, | |
| 686 | log.debug("server found entry point for 0x{x} at {s}:{d}:{d} - index {d} between {x} and {x}", .{ | |
| 687 | addr, file_name, sl.line, sl.column, index, pcs[index - 1], pcs[index + 1], | |
| 683 | 688 | }); |
| 684 | 689 | } |
| 685 | 690 | const gpa = ws.gpa; |
lib/std/Build/Step/Compile.zig+11-5| ... | ... | @@ -218,12 +218,18 @@ no_builtin: bool = false, |
| 218 | 218 | /// Managed by the build runner, not user build script. |
| 219 | 219 | zig_process: ?*Step.ZigProcess, |
| 220 | 220 | |
| 221 | /// Enables deprecated coverage instrumentation that is only useful if you | |
| 222 | /// are using third party fuzzers that depend on it. Otherwise, slows down | |
| 223 | /// the instrumented binary with unnecessary function calls. | |
| 221 | /// Enables coverage instrumentation that is only useful if you are using third | |
| 222 | /// party fuzzers that depend on it. Otherwise, slows down the instrumented | |
| 223 | /// binary with unnecessary function calls. | |
| 224 | 224 | /// |
| 225 | /// To enable fuzz testing instrumentation on a compilation, see the `fuzz` | |
| 226 | /// flag in `Module`. | |
| 225 | /// This kind of coverage instrumentation is used by AFLplusplus v4.21c, | |
| 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`. | |
| 227 | 233 | sanitize_coverage_trace_pc_guard: ?bool = null, |
| 228 | 234 | |
| 229 | 235 | pub const ExpectedCompileErrors = union(enum) { |
src/Air.zig+12-1| ... | ... | @@ -1126,7 +1126,9 @@ pub const CondBr = struct { |
| 1126 | 1126 | pub const BranchHints = packed struct(u32) { |
| 1127 | 1127 | true: std.builtin.BranchHint, |
| 1128 | 1128 | false: std.builtin.BranchHint, |
| 1129 | _: u26 = 0, | |
| 1129 | then_cov: CoveragePoint, | |
| 1130 | else_cov: CoveragePoint, | |
| 1131 | _: u24 = 0, | |
| 1130 | 1132 | }; |
| 1131 | 1133 | }; |
| 1132 | 1134 | |
| ... | ... | @@ -1903,3 +1905,12 @@ pub fn unwrapSwitch(air: *const Air, switch_inst: Inst.Index) UnwrappedSwitch { |
| 1903 | 1905 | pub const typesFullyResolved = types_resolved.typesFullyResolved; |
| 1904 | 1906 | pub const typeFullyResolved = types_resolved.checkType; |
| 1905 | 1907 | pub const valFullyResolved = types_resolved.checkVal; |
| 1908 | ||
| 1909 | pub 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 | 6898 | .payload = sema.addExtraAssumeCapacity(Air.CondBr{ |
| 6899 | 6899 | .then_body_len = @intCast(then_block.instructions.items.len), |
| 6900 | 6900 | .else_body_len = @intCast(else_block.instructions.items.len), |
| 6901 | // weight against error branch | |
| 6902 | .branch_hints = .{ .true = .likely, .false = .unlikely }, | |
| 6901 | .branch_hints = .{ | |
| 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 | 11802 | |
| 11797 | 11803 | _ = try child_block.addInst(.{ |
| 11798 | 11804 | .tag = .cond_br, |
| 11799 | .data = .{ .pl_op = .{ | |
| 11800 | .operand = cond, | |
| 11801 | .payload = sema.addExtraAssumeCapacity(Air.CondBr{ | |
| 11802 | .then_body_len = @intCast(true_instructions.len), | |
| 11803 | .else_body_len = @intCast(sub_block.instructions.items.len), | |
| 11804 | .branch_hints = .{ .true = non_error_hint, .false = .none }, | |
| 11805 | }), | |
| 11806 | } }, | |
| 11805 | .data = .{ | |
| 11806 | .pl_op = .{ | |
| 11807 | .operand = cond, | |
| 11808 | .payload = sema.addExtraAssumeCapacity(Air.CondBr{ | |
| 11809 | .then_body_len = @intCast(true_instructions.len), | |
| 11810 | .else_body_len = @intCast(sub_block.instructions.items.len), | |
| 11811 | .branch_hints = .{ | |
| 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 | 11822 | sema.air_extra.appendSliceAssumeCapacity(@ptrCast(true_instructions)); |
| 11809 | 11823 | sema.air_extra.appendSliceAssumeCapacity(@ptrCast(sub_block.instructions.items)); |
| ... | ... | @@ -12853,7 +12867,13 @@ fn analyzeSwitchRuntimeBlock( |
| 12853 | 12867 | sema.air_instructions.items(.data)[@intFromEnum(prev_cond_br)].pl_op.payload = sema.addExtraAssumeCapacity(Air.CondBr{ |
| 12854 | 12868 | .then_body_len = @intCast(prev_then_body.len), |
| 12855 | 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 | 12878 | sema.air_extra.appendSliceAssumeCapacity(@ptrCast(prev_then_body)); |
| 12859 | 12879 | sema.air_extra.appendSliceAssumeCapacity(@ptrCast(cond_body)); |
| ... | ... | @@ -13133,7 +13153,12 @@ fn analyzeSwitchRuntimeBlock( |
| 13133 | 13153 | sema.air_instructions.items(.data)[@intFromEnum(prev_cond_br)].pl_op.payload = sema.addExtraAssumeCapacity(Air.CondBr{ |
| 13134 | 13154 | .then_body_len = @intCast(prev_then_body.len), |
| 13135 | 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 | 13163 | sema.air_extra.appendSliceAssumeCapacity(@ptrCast(prev_then_body)); |
| 13139 | 13164 | sema.air_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items)); |
| ... | ... | @@ -19250,7 +19275,17 @@ fn zirBoolBr( |
| 19250 | 19275 | &else_block, |
| 19251 | 19276 | lhs, |
| 19252 | 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 | 19290 | if (!rhs_noret) { |
| 19256 | 19291 | if (try sema.resolveDefinedValue(rhs_block, rhs_src, coerced_rhs_result)) |rhs_val| { |
| ... | ... | @@ -19467,14 +19502,22 @@ fn zirCondbr( |
| 19467 | 19502 | true_instructions.len + sub_block.instructions.items.len); |
| 19468 | 19503 | _ = try parent_block.addInst(.{ |
| 19469 | 19504 | .tag = .cond_br, |
| 19470 | .data = .{ .pl_op = .{ | |
| 19471 | .operand = cond, | |
| 19472 | .payload = sema.addExtraAssumeCapacity(Air.CondBr{ | |
| 19473 | .then_body_len = @intCast(true_instructions.len), | |
| 19474 | .else_body_len = @intCast(sub_block.instructions.items.len), | |
| 19475 | .branch_hints = .{ .true = true_hint, .false = false_hint }, | |
| 19476 | }), | |
| 19477 | } }, | |
| 19505 | .data = .{ | |
| 19506 | .pl_op = .{ | |
| 19507 | .operand = cond, | |
| 19508 | .payload = sema.addExtraAssumeCapacity(Air.CondBr{ | |
| 19509 | .then_body_len = @intCast(true_instructions.len), | |
| 19510 | .else_body_len = @intCast(sub_block.instructions.items.len), | |
| 19511 | .branch_hints = .{ | |
| 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 | 19522 | sema.air_extra.appendSliceAssumeCapacity(@ptrCast(true_instructions)); |
| 19480 | 19523 | sema.air_extra.appendSliceAssumeCapacity(@ptrCast(sub_block.instructions.items)); |
| ... | ... | @@ -19851,8 +19894,14 @@ fn retWithErrTracing( |
| 19851 | 19894 | const cond_br_payload = sema.addExtraAssumeCapacity(Air.CondBr{ |
| 19852 | 19895 | .then_body_len = @intCast(then_block.instructions.items.len), |
| 19853 | 19896 | .else_body_len = @intCast(else_block.instructions.items.len), |
| 19854 | // weight against error branch | |
| 19855 | .branch_hints = .{ .true = .likely, .false = .unlikely }, | |
| 19897 | .branch_hints = .{ | |
| 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 | 19906 | sema.air_extra.appendSliceAssumeCapacity(@ptrCast(then_block.instructions.items)); |
| 19858 | 19907 | sema.air_extra.appendSliceAssumeCapacity(@ptrCast(else_block.instructions.items)); |
| ... | ... | @@ -27473,8 +27522,14 @@ fn addSafetyCheckExtra( |
| 27473 | 27522 | .payload = sema.addExtraAssumeCapacity(Air.CondBr{ |
| 27474 | 27523 | .then_body_len = 1, |
| 27475 | 27524 | .else_body_len = @intCast(fail_block.instructions.items.len), |
| 27476 | // safety check failure branch is cold | |
| 27477 | .branch_hints = .{ .true = .likely, .false = .cold }, | |
| 27525 | .branch_hints = .{ | |
| 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 | 822 | /// This is denormalized data. |
| 823 | 823 | struct_field_map: std.AutoHashMapUnmanaged(ZigStructField, c_uint), |
| 824 | 824 | |
| 825 | /// Values for `@llvm.used`. | |
| 826 | used: std.ArrayListUnmanaged(Builder.Constant), | |
| 827 | ||
| 825 | 828 | const ZigStructField = struct { |
| 826 | 829 | struct_ty: InternPool.Index, |
| 827 | 830 | field_index: u32, |
| ... | ... | @@ -975,6 +978,7 @@ pub const Object = struct { |
| 975 | 978 | .error_name_table = .none, |
| 976 | 979 | .null_opt_usize = .no_init, |
| 977 | 980 | .struct_field_map = .{}, |
| 981 | .used = .{}, | |
| 978 | 982 | }; |
| 979 | 983 | return obj; |
| 980 | 984 | } |
| ... | ... | @@ -1097,44 +1101,57 @@ pub const Object = struct { |
| 1097 | 1101 | lto: bool, |
| 1098 | 1102 | }; |
| 1099 | 1103 | |
| 1100 | pub fn emit(self: *Object, options: EmitOptions) !void { | |
| 1101 | const zcu = self.pt.zcu; | |
| 1104 | pub fn emit(o: *Object, options: EmitOptions) !void { | |
| 1105 | const zcu = o.pt.zcu; | |
| 1102 | 1106 | const comp = zcu.comp; |
| 1103 | 1107 | |
| 1104 | 1108 | { |
| 1105 | try self.genErrorNameTable(); | |
| 1106 | try self.genCmpLtErrorsLenFunction(); | |
| 1107 | try self.genModuleLevelAssembly(); | |
| 1109 | try o.genErrorNameTable(); | |
| 1110 | try o.genCmpLtErrorsLenFunction(); | |
| 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 | } | |
| 1108 | 1125 | |
| 1109 | if (!self.builder.strip) { | |
| 1126 | if (!o.builder.strip) { | |
| 1110 | 1127 | { |
| 1111 | 1128 | var i: usize = 0; |
| 1112 | while (i < self.debug_unresolved_namespace_scopes.count()) : (i += 1) { | |
| 1113 | const namespace_index = self.debug_unresolved_namespace_scopes.keys()[i]; | |
| 1114 | const fwd_ref = self.debug_unresolved_namespace_scopes.values()[i]; | |
| 1129 | while (i < o.debug_unresolved_namespace_scopes.count()) : (i += 1) { | |
| 1130 | const namespace_index = o.debug_unresolved_namespace_scopes.keys()[i]; | |
| 1131 | const fwd_ref = o.debug_unresolved_namespace_scopes.values()[i]; | |
| 1115 | 1132 | |
| 1116 | 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)); | |
| 1118 | 1135 | |
| 1119 | self.builder.debugForwardReferenceSetType(fwd_ref, debug_type); | |
| 1136 | o.builder.debugForwardReferenceSetType(fwd_ref, debug_type); | |
| 1120 | 1137 | } |
| 1121 | 1138 | } |
| 1122 | 1139 | |
| 1123 | self.builder.debugForwardReferenceSetType( | |
| 1124 | self.debug_enums_fwd_ref, | |
| 1125 | try self.builder.metadataTuple(self.debug_enums.items), | |
| 1140 | o.builder.debugForwardReferenceSetType( | |
| 1141 | o.debug_enums_fwd_ref, | |
| 1142 | try o.builder.metadataTuple(o.debug_enums.items), | |
| 1126 | 1143 | ); |
| 1127 | 1144 | |
| 1128 | self.builder.debugForwardReferenceSetType( | |
| 1129 | self.debug_globals_fwd_ref, | |
| 1130 | try self.builder.metadataTuple(self.debug_globals.items), | |
| 1145 | o.builder.debugForwardReferenceSetType( | |
| 1146 | o.debug_globals_fwd_ref, | |
| 1147 | try o.builder.metadataTuple(o.debug_globals.items), | |
| 1131 | 1148 | ); |
| 1132 | 1149 | } |
| 1133 | 1150 | } |
| 1134 | 1151 | |
| 1135 | 1152 | const target_triple_sentinel = |
| 1136 | try self.gpa.dupeZ(u8, self.builder.target_triple.slice(&self.builder).?); | |
| 1137 | defer self.gpa.free(target_triple_sentinel); | |
| 1153 | try o.gpa.dupeZ(u8, o.builder.target_triple.slice(&o.builder).?); | |
| 1154 | defer o.gpa.free(target_triple_sentinel); | |
| 1138 | 1155 | |
| 1139 | 1156 | const emit_asm_msg = options.asm_path orelse "(none)"; |
| 1140 | 1157 | const emit_bin_msg = options.bin_path orelse "(none)"; |
| ... | ... | @@ -1147,15 +1164,15 @@ pub const Object = struct { |
| 1147 | 1164 | const context, const module = emit: { |
| 1148 | 1165 | if (options.pre_ir_path) |path| { |
| 1149 | 1166 | if (std.mem.eql(u8, path, "-")) { |
| 1150 | self.builder.dump(); | |
| 1167 | o.builder.dump(); | |
| 1151 | 1168 | } else { |
| 1152 | _ = try self.builder.printToFile(path); | |
| 1169 | _ = try o.builder.printToFile(path); | |
| 1153 | 1170 | } |
| 1154 | 1171 | } |
| 1155 | 1172 | |
| 1156 | const bitcode = try self.builder.toBitcode(self.gpa); | |
| 1157 | defer self.gpa.free(bitcode); | |
| 1158 | self.builder.clearAndFree(); | |
| 1173 | const bitcode = try o.builder.toBitcode(o.gpa); | |
| 1174 | defer o.gpa.free(bitcode); | |
| 1175 | o.builder.clearAndFree(); | |
| 1159 | 1176 | |
| 1160 | 1177 | if (options.pre_bc_path) |path| { |
| 1161 | 1178 | var file = try std.fs.cwd().createFile(path, .{}); |
| ... | ... | @@ -1283,7 +1300,10 @@ pub const Object = struct { |
| 1283 | 1300 | .bitcode_filename = null, |
| 1284 | 1301 | .coverage = .{ |
| 1285 | 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 | 1307 | .TraceBB = false, |
| 1288 | 1308 | .TraceCmp = true, |
| 1289 | 1309 | .TraceDiv = false, |
| ... | ... | @@ -1291,10 +1311,13 @@ pub const Object = struct { |
| 1291 | 1311 | .Use8bitCounters = false, |
| 1292 | 1312 | .TracePC = false, |
| 1293 | 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 | 1316 | .InlineBoolFlag = false, |
| 1296 | .PCTable = true, | |
| 1317 | // Zig emits its own PC table instrumentation. | |
| 1318 | .PCTable = false, | |
| 1297 | 1319 | .NoPrune = false, |
| 1320 | // Workaround for https://github.com/llvm/llvm-project/pull/106464 | |
| 1298 | 1321 | .StackDepth = true, |
| 1299 | 1322 | .TraceLoads = false, |
| 1300 | 1323 | .TraceStores = false, |
| ... | ... | @@ -1655,6 +1678,29 @@ pub const Object = struct { |
| 1655 | 1678 | break :debug_info .{ file, subprogram }; |
| 1656 | 1679 | } else .{.none} ** 2; |
| 1657 | 1680 | |
| 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 | 1704 | var fg: FuncGen = .{ |
| 1659 | 1705 | .gpa = gpa, |
| 1660 | 1706 | .air = air, |
| ... | ... | @@ -1662,6 +1708,7 @@ pub const Object = struct { |
| 1662 | 1708 | .ng = &ng, |
| 1663 | 1709 | .wip = wip, |
| 1664 | 1710 | .is_naked = fn_info.cc == .Naked, |
| 1711 | .fuzz = fuzz, | |
| 1665 | 1712 | .ret_ptr = ret_ptr, |
| 1666 | 1713 | .args = args.items, |
| 1667 | 1714 | .arg_index = 0, |
| ... | ... | @@ -1679,15 +1726,36 @@ pub const Object = struct { |
| 1679 | 1726 | defer fg.deinit(); |
| 1680 | 1727 | deinit_wip = false; |
| 1681 | 1728 | |
| 1682 | fg.genBody(air.getMainBody()) catch |err| switch (err) { | |
| 1729 | fg.genBody(air.getMainBody(), .poi) catch |err| switch (err) { | |
| 1683 | 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 | 1732 | ng.err_msg = null; |
| 1686 | 1733 | return; |
| 1687 | 1734 | }, |
| 1688 | 1735 | else => |e| return e, |
| 1689 | 1736 | }; |
| 1690 | 1737 | |
| 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 | 1759 | try fg.wip.finish(); |
| 1692 | 1760 | } |
| 1693 | 1761 | |
| ... | ... | @@ -4729,6 +4797,7 @@ pub const FuncGen = struct { |
| 4729 | 4797 | liveness: Liveness, |
| 4730 | 4798 | wip: Builder.WipFunction, |
| 4731 | 4799 | is_naked: bool, |
| 4800 | fuzz: ?Fuzz, | |
| 4732 | 4801 | |
| 4733 | 4802 | file: Builder.Metadata, |
| 4734 | 4803 | scope: Builder.Metadata, |
| ... | ... | @@ -4769,6 +4838,16 @@ pub const FuncGen = struct { |
| 4769 | 4838 | |
| 4770 | 4839 | sync_scope: Builder.SyncScope, |
| 4771 | 4840 | |
| 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 | 4851 | const BreakList = union { |
| 4773 | 4852 | list: std.MultiArrayList(struct { |
| 4774 | 4853 | bb: Builder.Function.Block.Index, |
| ... | ... | @@ -4778,9 +4857,11 @@ pub const FuncGen = struct { |
| 4778 | 4857 | }; |
| 4779 | 4858 | |
| 4780 | 4859 | fn deinit(self: *FuncGen) void { |
| 4860 | const gpa = self.gpa; | |
| 4861 | if (self.fuzz) |*f| f.deinit(self.gpa); | |
| 4781 | 4862 | self.wip.deinit(); |
| 4782 | self.func_inst_table.deinit(self.gpa); | |
| 4783 | self.blocks.deinit(self.gpa); | |
| 4863 | self.func_inst_table.deinit(gpa); | |
| 4864 | self.blocks.deinit(gpa); | |
| 4784 | 4865 | } |
| 4785 | 4866 | |
| 4786 | 4867 | fn todo(self: *FuncGen, comptime format: []const u8, args: anytype) Error { |
| ... | ... | @@ -4836,11 +4917,33 @@ pub const FuncGen = struct { |
| 4836 | 4917 | return o.null_opt_usize; |
| 4837 | 4918 | } |
| 4838 | 4919 | |
| 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 | 4921 | const o = self.ng.object; |
| 4841 | 4922 | const zcu = o.pt.zcu; |
| 4842 | 4923 | const ip = &zcu.intern_pool; |
| 4843 | 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 | 4947 | for (body, 0..) |inst, i| { |
| 4845 | 4948 | if (self.liveness.isUnused(inst) and !self.air.mustLower(inst, ip)) continue; |
| 4846 | 4949 | |
| ... | ... | @@ -4949,7 +5052,7 @@ pub const FuncGen = struct { |
| 4949 | 5052 | .ret_ptr => try self.airRetPtr(inst), |
| 4950 | 5053 | .arg => try self.airArg(inst), |
| 4951 | 5054 | .bitcast => try self.airBitCast(inst), |
| 4952 | .int_from_bool => try self.airIntFromBool(inst), | |
| 5055 | .int_from_bool => try self.airIntFromBool(inst), | |
| 4953 | 5056 | .block => try self.airBlock(inst), |
| 4954 | 5057 | .br => try self.airBr(inst), |
| 4955 | 5058 | .switch_br => try self.airSwitchBr(inst), |
| ... | ... | @@ -4966,7 +5069,7 @@ pub const FuncGen = struct { |
| 4966 | 5069 | .trunc => try self.airTrunc(inst), |
| 4967 | 5070 | .fptrunc => try self.airFptrunc(inst), |
| 4968 | 5071 | .fpext => try self.airFpext(inst), |
| 4969 | .int_from_ptr => try self.airIntFromPtr(inst), | |
| 5072 | .int_from_ptr => try self.airIntFromPtr(inst), | |
| 4970 | 5073 | .load => try self.airLoad(body[i..]), |
| 4971 | 5074 | .loop => try self.airLoop(inst), |
| 4972 | 5075 | .not => try self.airNot(inst), |
| ... | ... | @@ -5089,8 +5192,13 @@ pub const FuncGen = struct { |
| 5089 | 5192 | } |
| 5090 | 5193 | } |
| 5091 | 5194 | |
| 5092 | fn genBodyDebugScope(self: *FuncGen, maybe_inline_func: ?InternPool.Index, body: []const Air.Inst.Index) Error!void { | |
| 5093 | if (self.wip.strip) return self.genBody(body); | |
| 5195 | fn genBodyDebugScope( | |
| 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); | |
| 5094 | 5202 | |
| 5095 | 5203 | const old_file = self.file; |
| 5096 | 5204 | const old_inlined = self.inlined; |
| ... | ... | @@ -5137,7 +5245,8 @@ pub const FuncGen = struct { |
| 5137 | 5245 | .sp_flags = .{ |
| 5138 | 5246 | .Optimized = mod.optimize_mode != .Debug, |
| 5139 | 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 | 5252 | o.debug_compile_unit, |
| ... | ... | @@ -5171,7 +5280,7 @@ pub const FuncGen = struct { |
| 5171 | 5280 | .no_location => {}, |
| 5172 | 5281 | }; |
| 5173 | 5282 | |
| 5174 | try self.genBody(body); | |
| 5283 | try self.genBody(body, coverage_point); | |
| 5175 | 5284 | } |
| 5176 | 5285 | |
| 5177 | 5286 | pub const CallAttr = enum { |
| ... | ... | @@ -5881,7 +5990,7 @@ pub const FuncGen = struct { |
| 5881 | 5990 | const inst_ty = self.typeOfIndex(inst); |
| 5882 | 5991 | |
| 5883 | 5992 | if (inst_ty.isNoReturn(zcu)) { |
| 5884 | try self.genBodyDebugScope(maybe_inline_func, body); | |
| 5993 | try self.genBodyDebugScope(maybe_inline_func, body, .none); | |
| 5885 | 5994 | return .none; |
| 5886 | 5995 | } |
| 5887 | 5996 | |
| ... | ... | @@ -5897,7 +6006,7 @@ pub const FuncGen = struct { |
| 5897 | 6006 | }); |
| 5898 | 6007 | defer assert(self.blocks.remove(inst)); |
| 5899 | 6008 | |
| 5900 | try self.genBodyDebugScope(maybe_inline_func, body); | |
| 6009 | try self.genBodyDebugScope(maybe_inline_func, body, .none); | |
| 5901 | 6010 | |
| 5902 | 6011 | self.wip.cursor = .{ .block = parent_bb }; |
| 5903 | 6012 | |
| ... | ... | @@ -5996,11 +6105,11 @@ pub const FuncGen = struct { |
| 5996 | 6105 | |
| 5997 | 6106 | self.wip.cursor = .{ .block = then_block }; |
| 5998 | 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); | |
| 6000 | 6109 | |
| 6001 | 6110 | self.wip.cursor = .{ .block = else_block }; |
| 6002 | 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); | |
| 6004 | 6113 | |
| 6005 | 6114 | // No need to reset the insert cursor since this instruction is noreturn. |
| 6006 | 6115 | return .none; |
| ... | ... | @@ -6085,7 +6194,7 @@ pub const FuncGen = struct { |
| 6085 | 6194 | |
| 6086 | 6195 | fg.wip.cursor = .{ .block = return_block }; |
| 6087 | 6196 | if (err_cold) _ = try fg.wip.callIntrinsicAssumeCold(); |
| 6088 | try fg.genBodyDebugScope(null, body); | |
| 6197 | try fg.genBodyDebugScope(null, body, .poi); | |
| 6089 | 6198 | |
| 6090 | 6199 | fg.wip.cursor = .{ .block = continue_block }; |
| 6091 | 6200 | } |
| ... | ... | @@ -6196,14 +6305,14 @@ pub const FuncGen = struct { |
| 6196 | 6305 | } |
| 6197 | 6306 | self.wip.cursor = .{ .block = case_block }; |
| 6198 | 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 | } |
| 6201 | 6310 | |
| 6202 | 6311 | const else_body = it.elseBody(); |
| 6203 | 6312 | self.wip.cursor = .{ .block = else_block }; |
| 6204 | 6313 | if (switch_br.getElseHint() == .cold) _ = try self.wip.callIntrinsicAssumeCold(); |
| 6205 | 6314 | if (else_body.len != 0) { |
| 6206 | try self.genBodyDebugScope(null, else_body); | |
| 6315 | try self.genBodyDebugScope(null, else_body, .poi); | |
| 6207 | 6316 | } else { |
| 6208 | 6317 | _ = try self.wip.@"unreachable"(); |
| 6209 | 6318 | } |
| ... | ... | @@ -6222,7 +6331,7 @@ pub const FuncGen = struct { |
| 6222 | 6331 | _ = try self.wip.br(loop_block); |
| 6223 | 6332 | |
| 6224 | 6333 | self.wip.cursor = .{ .block = loop_block }; |
| 6225 | try self.genBodyDebugScope(null, body); | |
| 6334 | try self.genBodyDebugScope(null, body, .none); | |
| 6226 | 6335 | |
| 6227 | 6336 | // TODO instead of this logic, change AIR to have the property that |
| 6228 | 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 | 10046 | } |
| 10047 | 10047 | |
| 10048 | 10048 | if (maybe_dbg_index) |dbg_index| { |
| 10049 | try writer.print(", !dbg !{}\n", .{dbg_index}); | |
| 10050 | } else try writer.writeByte('\n'); | |
| 10049 | try writer.print(", !dbg !{}", .{dbg_index}); | |
| 10050 | } | |
| 10051 | try writer.writeByte('\n'); | |
| 10051 | 10052 | } |
| 10052 | 10053 | try writer.writeByte('}'); |
| 10053 | 10054 | } |
src/print_air.zig+16-2| ... | ... | @@ -791,7 +791,14 @@ const Writer = struct { |
| 791 | 791 | |
| 792 | 792 | try w.writeOperand(s, inst, 0, pl_op.operand); |
| 793 | 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 | 802 | const old_indent = w.indent; |
| 796 | 803 | w.indent += 2; |
| 797 | 804 | |
| ... | ... | @@ -806,7 +813,14 @@ const Writer = struct { |
| 806 | 813 | |
| 807 | 814 | try w.writeBody(s, then_body); |
| 808 | 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"); | |
| 810 | 824 | |
| 811 | 825 | if (liveness_condbr.else_deaths.len != 0) { |
| 812 | 826 | try s.writeByteNTimes(' ', w.indent); |