| author | |
| committer | |
| log | f87156e33c688effcf00b8fa9c2542391423ee78 |
| tree | 6b209218f51ca1a9e70bf246e364be7534cfd3fa |
| parent | 2ed9288246821c39ae75fa21998a53b34e713cd4 |
5 files changed, 893 insertions(+), 219 deletions(-)
lib/std/Thread/Mutex.zig+97-80| ... | ... | @@ -33,17 +33,29 @@ const testing = std.testing; |
| 33 | 33 | const StaticResetEvent = std.thread.StaticResetEvent; |
| 34 | 34 | |
| 35 | 35 | /// Try to acquire the mutex without blocking. Returns `null` if the mutex is |
| 36 | /// unavailable. Otherwise returns `Held`. Call `release` on `Held`. | |
| 37 | pub fn tryAcquire(m: *Mutex) ?Impl.Held { | |
| 36 | /// unavailable. Otherwise returns `Held`. Call `release` on `Held`, or use | |
| 37 | /// releaseDirect(). | |
| 38 | pub fn tryAcquire(m: *Mutex) ?Held { | |
| 38 | 39 | return m.impl.tryAcquire(); |
| 39 | 40 | } |
| 40 | 41 | |
| 41 | 42 | /// Acquire the mutex. Deadlocks if the mutex is already |
| 42 | 43 | /// held by the calling thread. |
| 43 | pub fn acquire(m: *Mutex) Impl.Held { | |
| 44 | pub fn acquire(m: *Mutex) Held { | |
| 44 | 45 | return m.impl.acquire(); |
| 45 | 46 | } |
| 46 | 47 | |
| 48 | /// Release the mutex. Prefer Held.release() if available. | |
| 49 | pub fn releaseDirect(m: *Mutex) void { | |
| 50 | return m.impl.releaseDirect(); | |
| 51 | } | |
| 52 | ||
| 53 | /// A held mutex handle. Call release to allow other threads to | |
| 54 | /// take the mutex. Do not call release() more than once. | |
| 55 | /// For more complex scenarios, this handle can be discarded | |
| 56 | /// and Mutex.releaseDirect can be called instead. | |
| 57 | pub const Held = Impl.Held; | |
| 58 | ||
| 47 | 59 | const Impl = if (builtin.single_threaded) |
| 48 | 60 | Dummy |
| 49 | 61 | else if (builtin.os.tag == .windows) |
| ... | ... | @@ -53,6 +65,32 @@ else if (std.Thread.use_pthreads) |
| 53 | 65 | else |
| 54 | 66 | AtomicMutex; |
| 55 | 67 | |
| 68 | fn HeldInterface(comptime MutexType: type) type { | |
| 69 | return struct { | |
| 70 | const Mixin = @This(); | |
| 71 | pub const Held = struct { | |
| 72 | mutex: *MutexType, | |
| 73 | ||
| 74 | pub fn release(held: Mixin.Held) void { | |
| 75 | held.mutex.releaseDirect(); | |
| 76 | } | |
| 77 | }; | |
| 78 | ||
| 79 | pub fn tryAcquire(m: *MutexType) ?Mixin.Held { | |
| 80 | if (m.tryAcquireDirect()) { | |
| 81 | return Mixin.Held{ .mutex = m }; | |
| 82 | } else { | |
| 83 | return null; | |
| 84 | } | |
| 85 | } | |
| 86 | ||
| 87 | pub fn acquire(m: *MutexType) Mixin.Held { | |
| 88 | m.acquireDirect(); | |
| 89 | return Mixin.Held{ .mutex = m }; | |
| 90 | } | |
| 91 | }; | |
| 92 | } | |
| 93 | ||
| 56 | 94 | pub const AtomicMutex = struct { |
| 57 | 95 | state: State = .unlocked, |
| 58 | 96 | |
| ... | ... | @@ -62,39 +100,32 @@ pub const AtomicMutex = struct { |
| 62 | 100 | waiting, |
| 63 | 101 | }; |
| 64 | 102 | |
| 65 | pub const Held = struct { | |
| 66 | mutex: *AtomicMutex, | |
| 103 | pub usingnamespace HeldInterface(@This()); | |
| 67 | 104 | |
| 68 | pub fn release(held: Held) void { | |
| 69 | switch (@atomicRmw(State, &held.mutex.state, .Xchg, .unlocked, .Release)) { | |
| 70 | .unlocked => unreachable, | |
| 71 | .locked => {}, | |
| 72 | .waiting => held.mutex.unlockSlow(), | |
| 73 | } | |
| 74 | } | |
| 75 | }; | |
| 76 | ||
| 77 | pub fn tryAcquire(m: *AtomicMutex) ?Held { | |
| 78 | if (@cmpxchgStrong( | |
| 105 | fn tryAcquireDirect(m: *AtomicMutex) bool { | |
| 106 | return @cmpxchgStrong( | |
| 79 | 107 | State, |
| 80 | 108 | &m.state, |
| 81 | 109 | .unlocked, |
| 82 | 110 | .locked, |
| 83 | 111 | .Acquire, |
| 84 | 112 | .Monotonic, |
| 85 | ) == null) { | |
| 86 | return Held{ .mutex = m }; | |
| 87 | } else { | |
| 88 | return null; | |
| 89 | } | |
| 113 | ) == null; | |
| 90 | 114 | } |
| 91 | 115 | |
| 92 | pub fn acquire(m: *AtomicMutex) Held { | |
| 116 | fn acquireDirect(m: *AtomicMutex) void { | |
| 93 | 117 | switch (@atomicRmw(State, &m.state, .Xchg, .locked, .Acquire)) { |
| 94 | 118 | .unlocked => {}, |
| 95 | 119 | else => |s| m.lockSlow(s), |
| 96 | 120 | } |
| 97 | return Held{ .mutex = m }; | |
| 121 | } | |
| 122 | ||
| 123 | fn releaseDirect(m: *AtomicMutex) void { | |
| 124 | switch (@atomicRmw(State, &m.state, .Xchg, .unlocked, .Release)) { | |
| 125 | .unlocked => unreachable, | |
| 126 | .locked => {}, | |
| 127 | .waiting => m.unlockSlow(), | |
| 128 | } | |
| 98 | 129 | } |
| 99 | 130 | |
| 100 | 131 | fn lockSlow(m: *AtomicMutex, current_state: State) void { |
| ... | ... | @@ -171,36 +202,20 @@ pub const AtomicMutex = struct { |
| 171 | 202 | pub const PthreadMutex = struct { |
| 172 | 203 | pthread_mutex: std.c.pthread_mutex_t = .{}, |
| 173 | 204 | |
| 174 | pub const Held = struct { | |
| 175 | mutex: *PthreadMutex, | |
| 176 | ||
| 177 | pub fn release(held: Held) void { | |
| 178 | switch (std.c.pthread_mutex_unlock(&held.mutex.pthread_mutex)) { | |
| 179 | .SUCCESS => return, | |
| 180 | .INVAL => unreachable, | |
| 181 | .AGAIN => unreachable, | |
| 182 | .PERM => unreachable, | |
| 183 | else => unreachable, | |
| 184 | } | |
| 185 | } | |
| 186 | }; | |
| 205 | pub usingnamespace HeldInterface(@This()); | |
| 187 | 206 | |
| 188 | /// Try to acquire the mutex without blocking. Returns null if | |
| 189 | /// the mutex is unavailable. Otherwise returns Held. Call | |
| 190 | /// release on Held. | |
| 191 | pub fn tryAcquire(m: *PthreadMutex) ?Held { | |
| 192 | if (std.c.pthread_mutex_trylock(&m.pthread_mutex) == .SUCCESS) { | |
| 193 | return Held{ .mutex = m }; | |
| 194 | } else { | |
| 195 | return null; | |
| 196 | } | |
| 207 | /// Try to acquire the mutex without blocking. Returns true if | |
| 208 | /// the mutex is unavailable. Otherwise returns false. Call | |
| 209 | /// release when done. | |
| 210 | fn tryAcquireDirect(m: *PthreadMutex) bool { | |
| 211 | return std.c.pthread_mutex_trylock(&m.pthread_mutex) == .SUCCESS; | |
| 197 | 212 | } |
| 198 | 213 | |
| 199 | 214 | /// Acquire the mutex. Will deadlock if the mutex is already |
| 200 | 215 | /// held by the calling thread. |
| 201 | pub fn acquire(m: *PthreadMutex) Held { | |
| 216 | fn acquireDirect(m: *PthreadMutex) void { | |
| 202 | 217 | switch (std.c.pthread_mutex_lock(&m.pthread_mutex)) { |
| 203 | .SUCCESS => return Held{ .mutex = m }, | |
| 218 | .SUCCESS => {}, | |
| 204 | 219 | .INVAL => unreachable, |
| 205 | 220 | .BUSY => unreachable, |
| 206 | 221 | .AGAIN => unreachable, |
| ... | ... | @@ -209,6 +224,16 @@ pub const PthreadMutex = struct { |
| 209 | 224 | else => unreachable, |
| 210 | 225 | } |
| 211 | 226 | } |
| 227 | ||
| 228 | fn releaseDirect(m: *PthreadMutex) void { | |
| 229 | switch (std.c.pthread_mutex_unlock(&m.pthread_mutex)) { | |
| 230 | .SUCCESS => return, | |
| 231 | .INVAL => unreachable, | |
| 232 | .AGAIN => unreachable, | |
| 233 | .PERM => unreachable, | |
| 234 | else => unreachable, | |
| 235 | } | |
| 236 | } | |
| 212 | 237 | }; |
| 213 | 238 | |
| 214 | 239 | /// This has the sematics as `Mutex`, however it does not actually do any |
| ... | ... | @@ -216,58 +241,50 @@ pub const PthreadMutex = struct { |
| 216 | 241 | pub const Dummy = struct { |
| 217 | 242 | lock: @TypeOf(lock_init) = lock_init, |
| 218 | 243 | |
| 219 | const lock_init = if (std.debug.runtime_safety) false else {}; | |
| 220 | ||
| 221 | pub const Held = struct { | |
| 222 | mutex: *Dummy, | |
| 244 | pub usingnamespace HeldInterface(@This()); | |
| 223 | 245 | |
| 224 | pub fn release(held: Held) void { | |
| 225 | if (std.debug.runtime_safety) { | |
| 226 | held.mutex.lock = false; | |
| 227 | } | |
| 228 | } | |
| 229 | }; | |
| 246 | const lock_init = if (std.debug.runtime_safety) false else {}; | |
| 230 | 247 | |
| 231 | /// Try to acquire the mutex without blocking. Returns null if | |
| 232 | /// the mutex is unavailable. Otherwise returns Held. Call | |
| 233 | /// release on Held. | |
| 234 | pub fn tryAcquire(m: *Dummy) ?Held { | |
| 248 | /// Try to acquire the mutex without blocking. Returns false if | |
| 249 | /// the mutex is unavailable. Otherwise returns true. | |
| 250 | fn tryAcquireDirect(m: *Dummy) bool { | |
| 235 | 251 | if (std.debug.runtime_safety) { |
| 236 | if (m.lock) return null; | |
| 252 | if (m.lock) return false; | |
| 237 | 253 | m.lock = true; |
| 238 | 254 | } |
| 239 | return Held{ .mutex = m }; | |
| 255 | return true; | |
| 240 | 256 | } |
| 241 | 257 | |
| 242 | 258 | /// Acquire the mutex. Will deadlock if the mutex is already |
| 243 | 259 | /// held by the calling thread. |
| 244 | pub fn acquire(m: *Dummy) Held { | |
| 245 | return m.tryAcquire() orelse @panic("deadlock detected"); | |
| 260 | fn acquireDirect(m: *Dummy) void { | |
| 261 | if (!m.tryAcquireDirect()) { | |
| 262 | @panic("deadlock detected"); | |
| 263 | } | |
| 264 | } | |
| 265 | ||
| 266 | fn releaseDirect(m: *Dummy) void { | |
| 267 | if (std.debug.runtime_safety) { | |
| 268 | m.lock = false; | |
| 269 | } | |
| 246 | 270 | } |
| 247 | 271 | }; |
| 248 | 272 | |
| 249 | 273 | const WindowsMutex = struct { |
| 250 | 274 | srwlock: windows.SRWLOCK = windows.SRWLOCK_INIT, |
| 251 | 275 | |
| 252 | pub const Held = struct { | |
| 253 | mutex: *WindowsMutex, | |
| 276 | pub usingnamespace HeldInterface(@This()); | |
| 254 | 277 | |
| 255 | pub fn release(held: Held) void { | |
| 256 | windows.kernel32.ReleaseSRWLockExclusive(&held.mutex.srwlock); | |
| 257 | } | |
| 258 | }; | |
| 259 | ||
| 260 | pub fn tryAcquire(m: *WindowsMutex) ?Held { | |
| 261 | if (windows.kernel32.TryAcquireSRWLockExclusive(&m.srwlock) != windows.FALSE) { | |
| 262 | return Held{ .mutex = m }; | |
| 263 | } else { | |
| 264 | return null; | |
| 265 | } | |
| 278 | fn tryAcquireDirect(m: *WindowsMutex) bool { | |
| 279 | return windows.kernel32.TryAcquireSRWLockExclusive(&m.srwlock) != windows.FALSE; | |
| 266 | 280 | } |
| 267 | 281 | |
| 268 | pub fn acquire(m: *WindowsMutex) Held { | |
| 282 | fn acquireDirect(m: *WindowsMutex) void { | |
| 269 | 283 | windows.kernel32.AcquireSRWLockExclusive(&m.srwlock); |
| 270 | return Held{ .mutex = m }; | |
| 284 | } | |
| 285 | ||
| 286 | fn releaseDirect(m: *WindowsMutex) void { | |
| 287 | windows.kernel32.ReleaseSRWLockExclusive(&m.srwlock); | |
| 271 | 288 | } |
| 272 | 289 | }; |
| 273 | 290 |
src/Sema.zig+6| ... | ... | @@ -90,6 +90,7 @@ const LazySrcLoc = Module.LazySrcLoc; |
| 90 | 90 | const RangeSet = @import("RangeSet.zig"); |
| 91 | 91 | const target_util = @import("target.zig"); |
| 92 | 92 | const Package = @import("Package.zig"); |
| 93 | const crash_report = @import("crash_report.zig"); | |
| 93 | 94 | |
| 94 | 95 | pub const InstMap = std.AutoHashMapUnmanaged(Zir.Inst.Index, Air.Inst.Ref); |
| 95 | 96 | |
| ... | ... | @@ -153,11 +154,16 @@ pub fn analyzeBody( |
| 153 | 154 | |
| 154 | 155 | var orig_captures: usize = parent_capture_scope.captures.count(); |
| 155 | 156 | |
| 157 | var crash_info = crash_report.prepAnalyzeBody(sema, block, body); | |
| 158 | crash_info.push(); | |
| 159 | defer crash_info.pop(); | |
| 160 | ||
| 156 | 161 | // We use a while(true) loop here to avoid a redundant way of breaking out of |
| 157 | 162 | // the loop. The only way to break out of the loop is with a `noreturn` |
| 158 | 163 | // instruction. |
| 159 | 164 | var i: usize = 0; |
| 160 | 165 | const result = while (true) { |
| 166 | crash_info.setBodyIndex(i); | |
| 161 | 167 | const inst = body[i]; |
| 162 | 168 | const air_inst: Air.Inst.Ref = switch (tags[inst]) { |
| 163 | 169 | // zig fmt: off |
src/crash_report.zig created+581| ... | ... | @@ -0,0 +1,581 @@ |
| 1 | const std = @import("std"); | |
| 2 | const builtin = @import("builtin"); | |
| 3 | const debug = std.debug; | |
| 4 | const os = std.os; | |
| 5 | const io = std.io; | |
| 6 | const print_zir = @import("print_zir.zig"); | |
| 7 | ||
| 8 | const Module = @import("Module.zig"); | |
| 9 | const Sema = @import("Sema.zig"); | |
| 10 | const Zir = @import("Zir.zig"); | |
| 11 | ||
| 12 | pub const is_enabled = builtin.mode == .Debug; | |
| 13 | ||
| 14 | /// To use these crash report diagnostics, publish these symbols in your main file. | |
| 15 | /// You will also need to call initialize() on startup, preferably as the very first operation in your program. | |
| 16 | pub const root_decls = struct { | |
| 17 | pub const panic = if (is_enabled) compilerPanic else std.builtin.default_panic; | |
| 18 | pub const enable_segfault_handler = if (is_enabled) false else debug.default_enable_segfault_handler; | |
| 19 | }; | |
| 20 | ||
| 21 | /// Install signal handlers to identify crashes and report diagnostics. | |
| 22 | pub fn initialize() void { | |
| 23 | if (is_enabled and debug.have_segfault_handling_support) { | |
| 24 | attachSegfaultHandler(); | |
| 25 | } | |
| 26 | } | |
| 27 | ||
| 28 | fn En(comptime T: type) type { | |
| 29 | return if (is_enabled) T else void; | |
| 30 | } | |
| 31 | ||
| 32 | fn en(val: anytype) En(@TypeOf(val)) { | |
| 33 | return if (is_enabled) val else {}; | |
| 34 | } | |
| 35 | ||
| 36 | pub const AnalyzeBody = struct { | |
| 37 | parent: if (is_enabled) ?*AnalyzeBody else void, | |
| 38 | sema: En(*Sema), | |
| 39 | block: En(*Module.Scope.Block), | |
| 40 | body: En([]const Zir.Inst.Index), | |
| 41 | body_index: En(usize), | |
| 42 | ||
| 43 | pub fn push(self: *@This()) void { | |
| 44 | if (!is_enabled) return; | |
| 45 | const head = &zir_state; | |
| 46 | debug.assert(self.parent == null); | |
| 47 | self.parent = head.*; | |
| 48 | head.* = self; | |
| 49 | } | |
| 50 | ||
| 51 | pub fn pop(self: *@This()) void { | |
| 52 | if (!is_enabled) return; | |
| 53 | const head = &zir_state; | |
| 54 | const old = head.*.?; | |
| 55 | debug.assert(old == self); | |
| 56 | head.* = old.parent; | |
| 57 | } | |
| 58 | ||
| 59 | pub fn setBodyIndex(self: *@This(), index: usize) void { | |
| 60 | if (!is_enabled) return; | |
| 61 | self.body_index = index; | |
| 62 | } | |
| 63 | }; | |
| 64 | ||
| 65 | threadlocal var zir_state: ?*AnalyzeBody = if (is_enabled) null else @compileError("Cannot use zir_state if crash_report is disabled."); | |
| 66 | ||
| 67 | pub fn prepAnalyzeBody(sema: *Sema, block: *Module.Scope.Block, body: []const Zir.Inst.Index) AnalyzeBody { | |
| 68 | if (is_enabled) { | |
| 69 | return .{ | |
| 70 | .parent = null, | |
| 71 | .sema = sema, | |
| 72 | .block = block, | |
| 73 | .body = body, | |
| 74 | .body_index = 0, | |
| 75 | }; | |
| 76 | } else { | |
| 77 | if (@sizeOf(AnalyzeBody) != 0) | |
| 78 | @compileError("AnalyzeBody must have zero size when crash reports are disabled"); | |
| 79 | return undefined; | |
| 80 | } | |
| 81 | } | |
| 82 | ||
| 83 | fn dumpStatusReport() !void { | |
| 84 | const anal = zir_state orelse return; | |
| 85 | // Note: We have the panic mutex here, so we can safely use the global crash heap. | |
| 86 | var fba = std.heap.FixedBufferAllocator.init(&crash_heap); | |
| 87 | const allocator = &fba.allocator; | |
| 88 | ||
| 89 | const stderr = io.getStdErr().writer(); | |
| 90 | const block: *Scope.Block = anal.block; | |
| 91 | ||
| 92 | try stderr.writeAll("Analyzing "); | |
| 93 | try writeFullyQualifiedDeclWithFile(block.src_decl, stderr); | |
| 94 | try stderr.writeAll("\n"); | |
| 95 | ||
| 96 | print_zir.renderInstructionContext( | |
| 97 | allocator, | |
| 98 | anal.body, | |
| 99 | anal.body_index, | |
| 100 | block.src_decl.getFileScope(), | |
| 101 | block.src_decl.src_node, | |
| 102 | 6, // indent | |
| 103 | stderr, | |
| 104 | ) catch |err| switch (err) { | |
| 105 | error.OutOfMemory => try stderr.writeAll(" <out of memory dumping zir>\n"), | |
| 106 | else => |e| return e, | |
| 107 | }; | |
| 108 | try stderr.writeAll(" For full context, use the command\n zig ast-check -t "); | |
| 109 | try writeFilePath(block.src_decl.getFileScope(), stderr); | |
| 110 | try stderr.writeAll("\n\n"); | |
| 111 | ||
| 112 | var parent = anal.parent; | |
| 113 | while (parent) |curr| { | |
| 114 | fba.reset(); | |
| 115 | try stderr.writeAll(" in "); | |
| 116 | try writeFullyQualifiedDeclWithFile(curr.block.src_decl, stderr); | |
| 117 | try stderr.writeAll("\n > "); | |
| 118 | print_zir.renderSingleInstruction( | |
| 119 | allocator, | |
| 120 | curr.body[curr.body_index], | |
| 121 | curr.block.src_decl.getFileScope(), | |
| 122 | curr.block.src_decl.src_node, | |
| 123 | 6, // indent | |
| 124 | stderr, | |
| 125 | ) catch |err| switch (err) { | |
| 126 | error.OutOfMemory => try stderr.writeAll(" <out of memory dumping zir>\n"), | |
| 127 | else => |e| return e, | |
| 128 | }; | |
| 129 | try stderr.writeAll("\n"); | |
| 130 | ||
| 131 | parent = curr.parent; | |
| 132 | } | |
| 133 | ||
| 134 | try stderr.writeAll("\n"); | |
| 135 | } | |
| 136 | ||
| 137 | const Scope = Module.Scope; | |
| 138 | const Decl = Module.Decl; | |
| 139 | ||
| 140 | var crash_heap: [16 * 4096]u8 = undefined; | |
| 141 | ||
| 142 | fn writeFilePath(file: *Scope.File, stream: anytype) !void { | |
| 143 | if (file.pkg.root_src_directory.path) |path| { | |
| 144 | try stream.writeAll(path); | |
| 145 | try stream.writeAll(std.fs.path.sep_str); | |
| 146 | } | |
| 147 | try stream.writeAll(file.sub_file_path); | |
| 148 | } | |
| 149 | ||
| 150 | fn writeFullyQualifiedDeclWithFile(decl: *Decl, stream: anytype) !void { | |
| 151 | try writeFilePath(decl.getFileScope(), stream); | |
| 152 | try stream.writeAll(": "); | |
| 153 | try decl.namespace.renderFullyQualifiedName(std.mem.sliceTo(decl.name, 0), stream); | |
| 154 | } | |
| 155 | ||
| 156 | fn compilerPanic(msg: []const u8, error_return_trace: ?*std.builtin.StackTrace) noreturn { | |
| 157 | PanicSwitch.preDispatch(); | |
| 158 | @setCold(true); | |
| 159 | const ret_addr = @returnAddress(); | |
| 160 | const stack_ctx: StackContext = .{ .current = .{ .ret_addr = ret_addr } }; | |
| 161 | PanicSwitch.dispatch(error_return_trace, stack_ctx, msg); | |
| 162 | } | |
| 163 | ||
| 164 | /// Attaches a global SIGSEGV handler | |
| 165 | pub fn attachSegfaultHandler() void { | |
| 166 | if (!debug.have_segfault_handling_support) { | |
| 167 | @compileError("segfault handler not supported for this target"); | |
| 168 | } | |
| 169 | if (builtin.os.tag == .windows) { | |
| 170 | _ = os.windows.kernel32.AddVectoredExceptionHandler(0, handleSegfaultWindows); | |
| 171 | return; | |
| 172 | } | |
| 173 | var act = os.Sigaction{ | |
| 174 | .handler = .{ .sigaction = handleSegfaultLinux }, | |
| 175 | .mask = os.empty_sigset, | |
| 176 | .flags = (os.SA.SIGINFO | os.SA.RESTART | os.SA.RESETHAND), | |
| 177 | }; | |
| 178 | ||
| 179 | os.sigaction(os.SIG.SEGV, &act, null); | |
| 180 | os.sigaction(os.SIG.ILL, &act, null); | |
| 181 | os.sigaction(os.SIG.BUS, &act, null); | |
| 182 | } | |
| 183 | ||
| 184 | fn handleSegfaultLinux(sig: i32, info: *const os.siginfo_t, ctx_ptr: ?*const c_void) callconv(.C) noreturn { | |
| 185 | // TODO: use alarm() here to prevent infinite loops | |
| 186 | PanicSwitch.preDispatch(); | |
| 187 | ||
| 188 | const addr = switch (builtin.os.tag) { | |
| 189 | .linux => @ptrToInt(info.fields.sigfault.addr), | |
| 190 | .freebsd => @ptrToInt(info.addr), | |
| 191 | .netbsd => @ptrToInt(info.info.reason.fault.addr), | |
| 192 | .openbsd => @ptrToInt(info.data.fault.addr), | |
| 193 | .solaris => @ptrToInt(info.reason.fault.addr), | |
| 194 | else => @compileError("TODO implement handleSegfaultLinux for new linux OS"), | |
| 195 | }; | |
| 196 | ||
| 197 | var err_buffer: [128]u8 = undefined; | |
| 198 | const error_msg = switch (sig) { | |
| 199 | os.SIG.SEGV => std.fmt.bufPrint(&err_buffer, "Segmentation fault at address 0x{x}", .{addr}) catch "Segmentation fault", | |
| 200 | os.SIG.ILL => std.fmt.bufPrint(&err_buffer, "Illegal instruction at address 0x{x}", .{addr}) catch "Illegal instruction", | |
| 201 | os.SIG.BUS => std.fmt.bufPrint(&err_buffer, "Bus error at address 0x{x}", .{addr}) catch "Bus error", | |
| 202 | else => std.fmt.bufPrint(&err_buffer, "Unknown error (signal {}) at address 0x{x}", .{ sig, addr }) catch "Unknown error", | |
| 203 | }; | |
| 204 | ||
| 205 | const stack_ctx: StackContext = switch (builtin.cpu.arch) { | |
| 206 | .i386 => ctx: { | |
| 207 | const ctx = @ptrCast(*const os.ucontext_t, @alignCast(@alignOf(os.ucontext_t), ctx_ptr)); | |
| 208 | const ip = @intCast(usize, ctx.mcontext.gregs[os.REG.EIP]); | |
| 209 | const bp = @intCast(usize, ctx.mcontext.gregs[os.REG.EBP]); | |
| 210 | break :ctx StackContext{ .exception = .{ .bp = bp, .ip = ip } }; | |
| 211 | }, | |
| 212 | .x86_64 => ctx: { | |
| 213 | const ctx = @ptrCast(*const os.ucontext_t, @alignCast(@alignOf(os.ucontext_t), ctx_ptr)); | |
| 214 | const ip = switch (builtin.os.tag) { | |
| 215 | .linux, .netbsd, .solaris => @intCast(usize, ctx.mcontext.gregs[os.REG.RIP]), | |
| 216 | .freebsd => @intCast(usize, ctx.mcontext.rip), | |
| 217 | .openbsd => @intCast(usize, ctx.sc_rip), | |
| 218 | else => unreachable, | |
| 219 | }; | |
| 220 | const bp = switch (builtin.os.tag) { | |
| 221 | .linux, .netbsd, .solaris => @intCast(usize, ctx.mcontext.gregs[os.REG.RBP]), | |
| 222 | .openbsd => @intCast(usize, ctx.sc_rbp), | |
| 223 | .freebsd => @intCast(usize, ctx.mcontext.rbp), | |
| 224 | else => unreachable, | |
| 225 | }; | |
| 226 | break :ctx StackContext{ .exception = .{ .bp = bp, .ip = ip } }; | |
| 227 | }, | |
| 228 | .arm => ctx: { | |
| 229 | const ctx = @ptrCast(*const os.ucontext_t, @alignCast(@alignOf(os.ucontext_t), ctx_ptr)); | |
| 230 | const ip = @intCast(usize, ctx.mcontext.arm_pc); | |
| 231 | const bp = @intCast(usize, ctx.mcontext.arm_fp); | |
| 232 | break :ctx StackContext{ .exception = .{ .bp = bp, .ip = ip } }; | |
| 233 | }, | |
| 234 | .aarch64 => ctx: { | |
| 235 | const ctx = @ptrCast(*const os.ucontext_t, @alignCast(@alignOf(os.ucontext_t), ctx_ptr)); | |
| 236 | const ip = @intCast(usize, ctx.mcontext.pc); | |
| 237 | // x29 is the ABI-designated frame pointer | |
| 238 | const bp = @intCast(usize, ctx.mcontext.regs[29]); | |
| 239 | break :ctx StackContext{ .exception = .{ .bp = bp, .ip = ip } }; | |
| 240 | }, | |
| 241 | else => .not_supported, | |
| 242 | }; | |
| 243 | ||
| 244 | PanicSwitch.dispatch(null, stack_ctx, error_msg); | |
| 245 | } | |
| 246 | ||
| 247 | const WindowsSegfaultMessage = union(enum) { | |
| 248 | literal: []const u8, | |
| 249 | segfault: void, | |
| 250 | illegal_instruction: void, | |
| 251 | }; | |
| 252 | ||
| 253 | fn handleSegfaultWindows(info: *os.windows.EXCEPTION_POINTERS) callconv(os.windows.WINAPI) c_long { | |
| 254 | switch (info.ExceptionRecord.ExceptionCode) { | |
| 255 | os.windows.EXCEPTION_DATATYPE_MISALIGNMENT => handleSegfaultWindowsExtra(info, .{ .literal = "Unaligned Memory Access" }), | |
| 256 | os.windows.EXCEPTION_ACCESS_VIOLATION => handleSegfaultWindowsExtra(info, .segfault), | |
| 257 | os.windows.EXCEPTION_ILLEGAL_INSTRUCTION => handleSegfaultWindowsExtra(info, .illegal_instruction), | |
| 258 | os.windows.EXCEPTION_STACK_OVERFLOW => handleSegfaultWindowsExtra(info, .{ .literal = "Stack Overflow" }), | |
| 259 | else => return os.windows.EXCEPTION_CONTINUE_SEARCH, | |
| 260 | } | |
| 261 | } | |
| 262 | ||
| 263 | fn handleSegfaultWindowsExtra(info: *os.windows.EXCEPTION_POINTERS, comptime msg: WindowsSegfaultMessage) noreturn { | |
| 264 | PanicSwitch.preDispatch(); | |
| 265 | ||
| 266 | const stack_ctx = if (@hasDecl(os.windows, "CONTEXT")) ctx: { | |
| 267 | const regs = info.ContextRecord.getRegs(); | |
| 268 | break :ctx StackContext{ .exception = .{ .bp = regs.bp, .ip = regs.ip } }; | |
| 269 | } else ctx: { | |
| 270 | const addr = @ptrToInt(info.ExceptionRecord.ExceptionAddress); | |
| 271 | break :ctx StackContext{ .current = .{ .ret_addr = addr } }; | |
| 272 | }; | |
| 273 | ||
| 274 | switch (msg) { | |
| 275 | .literal => |err| PanicSwitch.dispatch(null, stack_ctx, err), | |
| 276 | .segfault => { | |
| 277 | const format_item = "Segmentation fault at address 0x{x}"; | |
| 278 | var buf: [format_item.len + 32]u8 = undefined; // 32 is arbitrary, but sufficiently large | |
| 279 | const to_print = std.fmt.bufPrint(&buf, format_item, .{info.ExceptionRecord.ExceptionInformation[1]}) catch unreachable; | |
| 280 | PanicSwitch.dispatch(null, stack_ctx, to_print); | |
| 281 | }, | |
| 282 | .illegal_instruction => { | |
| 283 | const ip: ?usize = switch (stack_ctx) { | |
| 284 | .exception => |ex| ex.ip, | |
| 285 | .current => |cur| cur.ret_addr, | |
| 286 | .not_supported => null, | |
| 287 | }; | |
| 288 | ||
| 289 | if (ip) |addr| { | |
| 290 | const format_item = "Illegal instruction at address 0x{x}"; | |
| 291 | var buf: [format_item.len + 32]u8 = undefined; // 32 is arbitrary, but sufficiently large | |
| 292 | const to_print = std.fmt.bufPrint(&buf, format_item, .{addr}) catch unreachable; | |
| 293 | PanicSwitch.dispatch(null, stack_ctx, to_print); | |
| 294 | } else { | |
| 295 | PanicSwitch.dispatch(null, stack_ctx, "Illegal Instruction"); | |
| 296 | } | |
| 297 | }, | |
| 298 | } | |
| 299 | } | |
| 300 | ||
| 301 | const StackContext = union(enum) { | |
| 302 | current: struct { | |
| 303 | ret_addr: ?usize, | |
| 304 | }, | |
| 305 | exception: struct { | |
| 306 | bp: usize, | |
| 307 | ip: usize, | |
| 308 | }, | |
| 309 | not_supported: void, | |
| 310 | ||
| 311 | pub fn dumpStackTrace(ctx: @This()) void { | |
| 312 | switch (ctx) { | |
| 313 | .current => |ct| { | |
| 314 | debug.dumpCurrentStackTrace(ct.ret_addr); | |
| 315 | }, | |
| 316 | .exception => |ex| { | |
| 317 | debug.dumpStackTraceFromBase(ex.bp, ex.ip); | |
| 318 | }, | |
| 319 | .not_supported => { | |
| 320 | const stderr = io.getStdErr().writer(); | |
| 321 | stderr.writeAll("Stack trace not supported on this platform.\n") catch {}; | |
| 322 | }, | |
| 323 | } | |
| 324 | } | |
| 325 | }; | |
| 326 | ||
| 327 | const PanicSwitch = struct { | |
| 328 | const RecoverStage = enum { | |
| 329 | initialize, | |
| 330 | report_stack, | |
| 331 | release_mutex, | |
| 332 | release_ref_count, | |
| 333 | abort, | |
| 334 | silent_abort, | |
| 335 | }; | |
| 336 | ||
| 337 | const RecoverVerbosity = enum { | |
| 338 | message_and_stack, | |
| 339 | message_only, | |
| 340 | silent, | |
| 341 | }; | |
| 342 | ||
| 343 | const PanicState = struct { | |
| 344 | recover_stage: RecoverStage = .initialize, | |
| 345 | recover_verbosity: RecoverVerbosity = .message_and_stack, | |
| 346 | panic_ctx: StackContext = undefined, | |
| 347 | panic_trace: ?*const std.builtin.StackTrace = null, | |
| 348 | awaiting_dispatch: bool = false, | |
| 349 | }; | |
| 350 | ||
| 351 | /// Counter for the number of threads currently panicking. | |
| 352 | /// Updated atomically before taking the panic_mutex. | |
| 353 | /// In recoverable cases, the program will not abort | |
| 354 | /// until all panicking threads have dumped their traces. | |
| 355 | var panicking: u8 = 0; | |
| 356 | ||
| 357 | // Locked to avoid interleaving panic messages from multiple threads. | |
| 358 | var panic_mutex = std.Thread.Mutex{}; | |
| 359 | ||
| 360 | /// Tracks the state of the current panic. If the code within the | |
| 361 | /// panic triggers a secondary panic, this allows us to recover. | |
| 362 | threadlocal var panic_state_raw: PanicState = .{}; | |
| 363 | ||
| 364 | /// The segfault handlers above need to do some work before they can dispatch | |
| 365 | /// this switch. Calling preDispatch() first makes that work fault tolerant. | |
| 366 | pub fn preDispatch() void { | |
| 367 | // TODO: We want segfaults to trigger the panic recursively here, | |
| 368 | // but if there is a segfault accessing this TLS slot it will cause an | |
| 369 | // infinite loop. We should use `alarm()` to prevent the infinite | |
| 370 | // loop and maybe also use a non-thread-local global to detect if | |
| 371 | // it's happening and print a message. | |
| 372 | var panic_state: *volatile PanicState = &panic_state_raw; | |
| 373 | if (panic_state.awaiting_dispatch) { | |
| 374 | dispatch(null, .{ .current = .{ .ret_addr = null } }, "Panic while preparing callstack"); | |
| 375 | } | |
| 376 | panic_state.awaiting_dispatch = true; | |
| 377 | } | |
| 378 | ||
| 379 | /// This is the entry point to a panic-tolerant panic handler. | |
| 380 | /// preDispatch() *MUST* be called exactly once before calling this. | |
| 381 | /// A threadlocal "recover_stage" is updated throughout the process. | |
| 382 | /// If a panic happens during the panic, the recover_stage will be | |
| 383 | /// used to select a recover* function to call to resume the panic. | |
| 384 | /// The recover_verbosity field is used to handle panics while reporting | |
| 385 | /// panics within panics. If the panic handler triggers a panic, it will | |
| 386 | /// attempt to log an additional stack trace for the secondary panic. If | |
| 387 | /// that panics, it will fall back to just logging the panic message. If | |
| 388 | /// it can't even do that witout panicing, it will recover without logging | |
| 389 | /// anything about the internal panic. Depending on the state, "recover" | |
| 390 | /// here may just mean "call abort". | |
| 391 | pub fn dispatch( | |
| 392 | trace: ?*const std.builtin.StackTrace, | |
| 393 | stack_ctx: StackContext, | |
| 394 | msg: []const u8, | |
| 395 | ) noreturn { | |
| 396 | var panic_state: *volatile PanicState = &panic_state_raw; | |
| 397 | debug.assert(panic_state.awaiting_dispatch); | |
| 398 | panic_state.awaiting_dispatch = false; | |
| 399 | nosuspend switch (panic_state.recover_stage) { | |
| 400 | .initialize => goTo(initPanic, .{ panic_state, trace, stack_ctx, msg }), | |
| 401 | .report_stack => goTo(recoverReportStack, .{ panic_state, trace, stack_ctx, msg }), | |
| 402 | .release_mutex => goTo(recoverReleaseMutex, .{ panic_state, trace, stack_ctx, msg }), | |
| 403 | .release_ref_count => goTo(recoverReleaseRefCount, .{ panic_state, trace, stack_ctx, msg }), | |
| 404 | .abort => goTo(recoverAbort, .{ panic_state, trace, stack_ctx, msg }), | |
| 405 | .silent_abort => goTo(abort, .{}), | |
| 406 | }; | |
| 407 | } | |
| 408 | ||
| 409 | noinline fn initPanic( | |
| 410 | state: *volatile PanicState, | |
| 411 | trace: ?*const std.builtin.StackTrace, | |
| 412 | stack: StackContext, | |
| 413 | msg: []const u8, | |
| 414 | ) noreturn { | |
| 415 | // use a temporary so there's only one volatile store | |
| 416 | const new_state = PanicState{ | |
| 417 | .recover_stage = .abort, | |
| 418 | .panic_ctx = stack, | |
| 419 | .panic_trace = trace, | |
| 420 | }; | |
| 421 | state.* = new_state; | |
| 422 | ||
| 423 | _ = @atomicRmw(u8, &panicking, .Add, 1, .SeqCst); | |
| 424 | ||
| 425 | state.recover_stage = .release_ref_count; | |
| 426 | ||
| 427 | _ = panic_mutex.acquire(); | |
| 428 | ||
| 429 | state.recover_stage = .release_mutex; | |
| 430 | ||
| 431 | const stderr = io.getStdErr().writer(); | |
| 432 | if (builtin.single_threaded) { | |
| 433 | stderr.print("panic: ", .{}) catch goTo(releaseMutex, .{state}); | |
| 434 | } else { | |
| 435 | const current_thread_id = std.Thread.getCurrentId(); | |
| 436 | stderr.print("thread {} panic: ", .{current_thread_id}) catch goTo(releaseMutex, .{state}); | |
| 437 | } | |
| 438 | stderr.print("{s}\n", .{msg}) catch goTo(releaseMutex, .{state}); | |
| 439 | ||
| 440 | state.recover_stage = .report_stack; | |
| 441 | ||
| 442 | dumpStatusReport() catch |err| { | |
| 443 | stderr.print("\nIntercepted error.{} while dumping current state. Continuing...\n", .{err}) catch {}; | |
| 444 | }; | |
| 445 | ||
| 446 | goTo(reportStack, .{state}); | |
| 447 | } | |
| 448 | ||
| 449 | noinline fn recoverReportStack( | |
| 450 | state: *volatile PanicState, | |
| 451 | trace: ?*const std.builtin.StackTrace, | |
| 452 | stack: StackContext, | |
| 453 | msg: []const u8, | |
| 454 | ) noreturn { | |
| 455 | recover(state, trace, stack, msg); | |
| 456 | ||
| 457 | state.recover_stage = .release_mutex; | |
| 458 | const stderr = io.getStdErr().writer(); | |
| 459 | stderr.writeAll("\nOriginal Error:\n") catch {}; | |
| 460 | goTo(reportStack, .{state}); | |
| 461 | } | |
| 462 | ||
| 463 | noinline fn reportStack(state: *volatile PanicState) noreturn { | |
| 464 | state.recover_stage = .release_mutex; | |
| 465 | ||
| 466 | if (state.panic_trace) |t| { | |
| 467 | debug.dumpStackTrace(t.*); | |
| 468 | } | |
| 469 | state.panic_ctx.dumpStackTrace(); | |
| 470 | ||
| 471 | goTo(releaseMutex, .{state}); | |
| 472 | } | |
| 473 | ||
| 474 | noinline fn recoverReleaseMutex( | |
| 475 | state: *volatile PanicState, | |
| 476 | trace: ?*const std.builtin.StackTrace, | |
| 477 | stack: StackContext, | |
| 478 | msg: []const u8, | |
| 479 | ) noreturn { | |
| 480 | recover(state, trace, stack, msg); | |
| 481 | goTo(releaseMutex, .{state}); | |
| 482 | } | |
| 483 | ||
| 484 | noinline fn releaseMutex(state: *volatile PanicState) noreturn { | |
| 485 | state.recover_stage = .abort; | |
| 486 | ||
| 487 | panic_mutex.releaseDirect(); | |
| 488 | ||
| 489 | goTo(releaseRefCount, .{state}); | |
| 490 | } | |
| 491 | ||
| 492 | noinline fn recoverReleaseRefCount( | |
| 493 | state: *volatile PanicState, | |
| 494 | trace: ?*const std.builtin.StackTrace, | |
| 495 | stack: StackContext, | |
| 496 | msg: []const u8, | |
| 497 | ) noreturn { | |
| 498 | recover(state, trace, stack, msg); | |
| 499 | goTo(releaseRefCount, .{state}); | |
| 500 | } | |
| 501 | ||
| 502 | noinline fn releaseRefCount(state: *volatile PanicState) noreturn { | |
| 503 | state.recover_stage = .abort; | |
| 504 | ||
| 505 | if (@atomicRmw(u8, &panicking, .Sub, 1, .SeqCst) != 1) { | |
| 506 | // Another thread is panicking, wait for the last one to finish | |
| 507 | // and call abort() | |
| 508 | ||
| 509 | // Sleep forever without hammering the CPU | |
| 510 | var event: std.Thread.StaticResetEvent = .{}; | |
| 511 | event.wait(); | |
| 512 | // This should be unreachable, recurse into recoverAbort. | |
| 513 | @panic("event.wait() returned"); | |
| 514 | } | |
| 515 | ||
| 516 | goTo(abort, .{}); | |
| 517 | } | |
| 518 | ||
| 519 | noinline fn recoverAbort( | |
| 520 | state: *volatile PanicState, | |
| 521 | trace: ?*const std.builtin.StackTrace, | |
| 522 | stack: StackContext, | |
| 523 | msg: []const u8, | |
| 524 | ) noreturn { | |
| 525 | recover(state, trace, stack, msg); | |
| 526 | ||
| 527 | state.recover_stage = .silent_abort; | |
| 528 | const stderr = io.getStdErr().writer(); | |
| 529 | stderr.writeAll("Aborting...\n") catch {}; | |
| 530 | goTo(abort, .{}); | |
| 531 | } | |
| 532 | ||
| 533 | noinline fn abort() noreturn { | |
| 534 | os.abort(); | |
| 535 | } | |
| 536 | ||
| 537 | inline fn goTo(comptime func: anytype, args: anytype) noreturn { | |
| 538 | // TODO: Tailcall is broken right now, but eventually this should be used | |
| 539 | // to avoid blowing up the stack. It's ok for now though, there are no | |
| 540 | // cycles in the state machine so the max stack usage is bounded. | |
| 541 | //@call(.{.modifier = .always_tail}, func, args); | |
| 542 | @call(.{}, func, args); | |
| 543 | } | |
| 544 | ||
| 545 | fn recover( | |
| 546 | state: *volatile PanicState, | |
| 547 | trace: ?*const std.builtin.StackTrace, | |
| 548 | stack: StackContext, | |
| 549 | msg: []const u8, | |
| 550 | ) void { | |
| 551 | switch (state.recover_verbosity) { | |
| 552 | .message_and_stack => { | |
| 553 | // lower the verbosity, and restore it at the end if we don't panic. | |
| 554 | state.recover_verbosity = .message_only; | |
| 555 | ||
| 556 | const stderr = io.getStdErr().writer(); | |
| 557 | stderr.writeAll("\nPanicked during a panic: ") catch {}; | |
| 558 | stderr.writeAll(msg) catch {}; | |
| 559 | stderr.writeAll("\nInner panic stack:\n") catch {}; | |
| 560 | if (trace) |t| { | |
| 561 | debug.dumpStackTrace(t.*); | |
| 562 | } | |
| 563 | stack.dumpStackTrace(); | |
| 564 | ||
| 565 | state.recover_verbosity = .message_and_stack; | |
| 566 | }, | |
| 567 | .message_only => { | |
| 568 | state.recover_verbosity = .silent; | |
| 569 | ||
| 570 | const stderr = io.getStdErr().writer(); | |
| 571 | stderr.writeAll("\nPanicked while dumping inner panic stack: ") catch {}; | |
| 572 | stderr.writeAll(msg) catch {}; | |
| 573 | stderr.writeAll("\n") catch {}; | |
| 574 | ||
| 575 | // If we succeed, restore all the way to dumping the stack. | |
| 576 | state.recover_verbosity = .message_and_stack; | |
| 577 | }, | |
| 578 | .silent => {}, | |
| 579 | } | |
| 580 | } | |
| 581 | }; |
src/main.zig+6| ... | ... | @@ -20,6 +20,10 @@ const translate_c = @import("translate_c.zig"); |
| 20 | 20 | const Cache = @import("Cache.zig"); |
| 21 | 21 | const target_util = @import("target.zig"); |
| 22 | 22 | const ThreadPool = @import("ThreadPool.zig"); |
| 23 | const crash_report = @import("crash_report.zig"); | |
| 24 | ||
| 25 | // Crash report needs to override the panic handler and other root decls | |
| 26 | pub usingnamespace crash_report.root_decls; | |
| 23 | 27 | |
| 24 | 28 | pub fn fatal(comptime format: []const u8, args: anytype) noreturn { |
| 25 | 29 | std.log.emerg(format, args); |
| ... | ... | @@ -134,6 +138,8 @@ var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{ |
| 134 | 138 | }){}; |
| 135 | 139 | |
| 136 | 140 | pub fn main() anyerror!void { |
| 141 | crash_report.initialize(); | |
| 142 | ||
| 137 | 143 | var gpa_need_deinit = false; |
| 138 | 144 | const gpa = gpa: { |
| 139 | 145 | if (!std.builtin.link_libc) { |
src/print_zir.zig+203-139| ... | ... | @@ -24,15 +24,20 @@ pub fn renderAsTextToFile( |
| 24 | 24 | .code = scope_file.zir, |
| 25 | 25 | .indent = 0, |
| 26 | 26 | .parent_decl_node = 0, |
| 27 | .recurse_decls = true, | |
| 28 | .recurse_blocks = true, | |
| 27 | 29 | }; |
| 28 | 30 | |
| 31 | var raw_stream = std.io.bufferedWriter(fs_file.writer()); | |
| 32 | const stream = raw_stream.writer(); | |
| 33 | ||
| 29 | 34 | const main_struct_inst = Zir.main_struct_inst; |
| 30 | try fs_file.writer().print("%{d} ", .{main_struct_inst}); | |
| 31 | try writer.writeInstToStream(fs_file.writer(), main_struct_inst); | |
| 32 | try fs_file.writeAll("\n"); | |
| 35 | try stream.print("%{d} ", .{main_struct_inst}); | |
| 36 | try writer.writeInstToStream(stream, main_struct_inst); | |
| 37 | try stream.writeAll("\n"); | |
| 33 | 38 | const imports_index = scope_file.zir.extra[@enumToInt(Zir.ExtraIndex.imports)]; |
| 34 | 39 | if (imports_index != 0) { |
| 35 | try fs_file.writeAll("Imports:\n"); | |
| 40 | try stream.writeAll("Imports:\n"); | |
| 36 | 41 | |
| 37 | 42 | const extra = scope_file.zir.extraData(Zir.Inst.Imports, imports_index); |
| 38 | 43 | var import_i: u32 = 0; |
| ... | ... | @@ -44,13 +49,74 @@ pub fn renderAsTextToFile( |
| 44 | 49 | |
| 45 | 50 | const src: LazySrcLoc = .{ .token_abs = item.data.token }; |
| 46 | 51 | const import_path = scope_file.zir.nullTerminatedString(item.data.name); |
| 47 | try fs_file.writer().print(" @import(\"{}\") ", .{ | |
| 52 | try stream.print(" @import(\"{}\") ", .{ | |
| 48 | 53 | std.zig.fmtEscapes(import_path), |
| 49 | 54 | }); |
| 50 | try writer.writeSrc(fs_file.writer(), src); | |
| 51 | try fs_file.writer().writeAll("\n"); | |
| 55 | try writer.writeSrc(stream, src); | |
| 56 | try stream.writeAll("\n"); | |
| 52 | 57 | } |
| 53 | 58 | } |
| 59 | ||
| 60 | try raw_stream.flush(); | |
| 61 | } | |
| 62 | ||
| 63 | pub fn renderInstructionContext( | |
| 64 | gpa: *Allocator, | |
| 65 | block: []const Zir.Inst.Index, | |
| 66 | block_index: usize, | |
| 67 | scope_file: *Module.Scope.File, | |
| 68 | parent_decl_node: Ast.Node.Index, | |
| 69 | indent: u32, | |
| 70 | stream: anytype, | |
| 71 | ) !void { | |
| 72 | var arena = std.heap.ArenaAllocator.init(gpa); | |
| 73 | defer arena.deinit(); | |
| 74 | ||
| 75 | var writer: Writer = .{ | |
| 76 | .gpa = gpa, | |
| 77 | .arena = &arena.allocator, | |
| 78 | .file = scope_file, | |
| 79 | .code = scope_file.zir, | |
| 80 | .indent = if (indent < 2) 2 else indent, | |
| 81 | .parent_decl_node = parent_decl_node, | |
| 82 | .recurse_decls = false, | |
| 83 | .recurse_blocks = true, | |
| 84 | }; | |
| 85 | ||
| 86 | try writer.writeBody(stream, block[0..block_index]); | |
| 87 | try stream.writeByteNTimes(' ', writer.indent - 2); | |
| 88 | try stream.print("> %{d} ", .{block[block_index]}); | |
| 89 | try writer.writeInstToStream(stream, block[block_index]); | |
| 90 | try stream.writeByte('\n'); | |
| 91 | if (block_index + 1 < block.len) { | |
| 92 | try writer.writeBody(stream, block[block_index + 1 ..]); | |
| 93 | } | |
| 94 | } | |
| 95 | ||
| 96 | pub fn renderSingleInstruction( | |
| 97 | gpa: *Allocator, | |
| 98 | inst: Zir.Inst.Index, | |
| 99 | scope_file: *Module.Scope.File, | |
| 100 | parent_decl_node: Ast.Node.Index, | |
| 101 | indent: u32, | |
| 102 | stream: anytype, | |
| 103 | ) !void { | |
| 104 | var arena = std.heap.ArenaAllocator.init(gpa); | |
| 105 | defer arena.deinit(); | |
| 106 | ||
| 107 | var writer: Writer = .{ | |
| 108 | .gpa = gpa, | |
| 109 | .arena = &arena.allocator, | |
| 110 | .file = scope_file, | |
| 111 | .code = scope_file.zir, | |
| 112 | .indent = indent, | |
| 113 | .parent_decl_node = parent_decl_node, | |
| 114 | .recurse_decls = false, | |
| 115 | .recurse_blocks = false, | |
| 116 | }; | |
| 117 | ||
| 118 | try stream.print("%{d} ", .{inst}); | |
| 119 | try writer.writeInstToStream(stream, inst); | |
| 54 | 120 | } |
| 55 | 121 | |
| 56 | 122 | const Writer = struct { |
| ... | ... | @@ -59,7 +125,9 @@ const Writer = struct { |
| 59 | 125 | file: *Module.Scope.File, |
| 60 | 126 | code: Zir, |
| 61 | 127 | indent: u32, |
| 62 | parent_decl_node: u32, | |
| 128 | parent_decl_node: Ast.Node.Index, | |
| 129 | recurse_decls: bool, | |
| 130 | recurse_blocks: bool, | |
| 63 | 131 | |
| 64 | 132 | fn relativeToNodeIndex(self: *Writer, offset: i32) Ast.Node.Index { |
| 65 | 133 | return @bitCast(Ast.Node.Index, offset + @bitCast(i32, self.parent_decl_node)); |
| ... | ... | @@ -567,12 +635,8 @@ const Writer = struct { |
| 567 | 635 | try stream.print("\"{}\", ", .{ |
| 568 | 636 | std.zig.fmtEscapes(self.code.nullTerminatedString(extra.data.name)), |
| 569 | 637 | }); |
| 570 | try stream.writeAll("{\n"); | |
| 571 | self.indent += 2; | |
| 572 | try self.writeBody(stream, body); | |
| 573 | self.indent -= 2; | |
| 574 | try stream.writeByteNTimes(' ', self.indent); | |
| 575 | try stream.writeAll("}) "); | |
| 638 | try self.writeBracedBody(stream, body); | |
| 639 | try stream.writeAll(") "); | |
| 576 | 640 | try self.writeSrc(stream, inst_data.src()); |
| 577 | 641 | } |
| 578 | 642 | |
| ... | ... | @@ -881,12 +945,8 @@ const Writer = struct { |
| 881 | 945 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; |
| 882 | 946 | const extra = self.code.extraData(Zir.Inst.Block, inst_data.payload_index); |
| 883 | 947 | const body = self.code.extra[extra.end..][0..extra.data.body_len]; |
| 884 | try stream.writeAll("{\n"); | |
| 885 | self.indent += 2; | |
| 886 | try self.writeBody(stream, body); | |
| 887 | self.indent -= 2; | |
| 888 | try stream.writeByteNTimes(' ', self.indent); | |
| 889 | try stream.writeAll("}) "); | |
| 948 | try self.writeBracedBody(stream, body); | |
| 949 | try stream.writeAll(") "); | |
| 890 | 950 | } |
| 891 | 951 | |
| 892 | 952 | fn writePlNodeCondBr(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { |
| ... | ... | @@ -895,17 +955,11 @@ const Writer = struct { |
| 895 | 955 | const then_body = self.code.extra[extra.end..][0..extra.data.then_body_len]; |
| 896 | 956 | const else_body = self.code.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]; |
| 897 | 957 | try self.writeInstRef(stream, extra.data.condition); |
| 898 | try stream.writeAll(", {\n"); | |
| 899 | self.indent += 2; | |
| 900 | try self.writeBody(stream, then_body); | |
| 901 | self.indent -= 2; | |
| 902 | try stream.writeByteNTimes(' ', self.indent); | |
| 903 | try stream.writeAll("}, {\n"); | |
| 904 | self.indent += 2; | |
| 905 | try self.writeBody(stream, else_body); | |
| 906 | self.indent -= 2; | |
| 907 | try stream.writeByteNTimes(' ', self.indent); | |
| 908 | try stream.writeAll("}) "); | |
| 958 | try stream.writeAll(", "); | |
| 959 | try self.writeBracedBody(stream, then_body); | |
| 960 | try stream.writeAll(", "); | |
| 961 | try self.writeBracedBody(stream, else_body); | |
| 962 | try stream.writeAll(") "); | |
| 909 | 963 | try self.writeSrc(stream, inst_data.src()); |
| 910 | 964 | } |
| 911 | 965 | |
| ... | ... | @@ -963,17 +1017,10 @@ const Writer = struct { |
| 963 | 1017 | } else { |
| 964 | 1018 | const prev_parent_decl_node = self.parent_decl_node; |
| 965 | 1019 | if (src_node) |off| self.parent_decl_node = self.relativeToNodeIndex(off); |
| 966 | self.indent += 2; | |
| 967 | if (body.len == 0) { | |
| 968 | try stream.writeAll("{}, {\n"); | |
| 969 | } else { | |
| 970 | try stream.writeAll("{\n"); | |
| 971 | try self.writeBody(stream, body); | |
| 972 | ||
| 973 | try stream.writeByteNTimes(' ', self.indent - 2); | |
| 974 | try stream.writeAll("}, {\n"); | |
| 975 | } | |
| 1020 | try self.writeBracedDecl(stream, body); | |
| 1021 | try stream.writeAll(", {\n"); | |
| 976 | 1022 | |
| 1023 | self.indent += 2; | |
| 977 | 1024 | const bits_per_field = 4; |
| 978 | 1025 | const fields_per_u32 = 32 / bits_per_field; |
| 979 | 1026 | const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable; |
| ... | ... | @@ -1096,17 +1143,10 @@ const Writer = struct { |
| 1096 | 1143 | |
| 1097 | 1144 | const prev_parent_decl_node = self.parent_decl_node; |
| 1098 | 1145 | if (src_node) |off| self.parent_decl_node = self.relativeToNodeIndex(off); |
| 1099 | self.indent += 2; | |
| 1100 | if (body.len == 0) { | |
| 1101 | try stream.writeAll("{}, {\n"); | |
| 1102 | } else { | |
| 1103 | try stream.writeAll("{\n"); | |
| 1104 | try self.writeBody(stream, body); | |
| 1105 | ||
| 1106 | try stream.writeByteNTimes(' ', self.indent - 2); | |
| 1107 | try stream.writeAll("}, {\n"); | |
| 1108 | } | |
| 1146 | try self.writeBracedDecl(stream, body); | |
| 1147 | try stream.writeAll(", {\n"); | |
| 1109 | 1148 | |
| 1149 | self.indent += 2; | |
| 1110 | 1150 | const bits_per_field = 4; |
| 1111 | 1151 | const fields_per_u32 = 32 / bits_per_field; |
| 1112 | 1152 | const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable; |
| ... | ... | @@ -1251,18 +1291,25 @@ const Writer = struct { |
| 1251 | 1291 | try stream.writeAll(")"); |
| 1252 | 1292 | } |
| 1253 | 1293 | } |
| 1254 | const tag = self.code.instructions.items(.tag)[decl_index]; | |
| 1255 | try stream.print(" line({d}) hash({}): %{d} = {s}(", .{ | |
| 1256 | line, std.fmt.fmtSliceHexLower(&hash_bytes), decl_index, @tagName(tag), | |
| 1257 | }); | |
| 1258 | 1294 | |
| 1259 | const decl_block_inst_data = self.code.instructions.items(.data)[decl_index].pl_node; | |
| 1260 | const sub_decl_node_off = decl_block_inst_data.src_node; | |
| 1261 | self.parent_decl_node = self.relativeToNodeIndex(sub_decl_node_off); | |
| 1262 | try self.writePlNodeBlockWithoutSrc(stream, decl_index); | |
| 1263 | self.parent_decl_node = parent_decl_node; | |
| 1264 | try self.writeSrc(stream, decl_block_inst_data.src()); | |
| 1265 | try stream.writeAll("\n"); | |
| 1295 | if (self.recurse_decls) { | |
| 1296 | const tag = self.code.instructions.items(.tag)[decl_index]; | |
| 1297 | try stream.print(" line({d}) hash({}): %{d} = {s}(", .{ | |
| 1298 | line, std.fmt.fmtSliceHexLower(&hash_bytes), decl_index, @tagName(tag), | |
| 1299 | }); | |
| 1300 | ||
| 1301 | const decl_block_inst_data = self.code.instructions.items(.data)[decl_index].pl_node; | |
| 1302 | const sub_decl_node_off = decl_block_inst_data.src_node; | |
| 1303 | self.parent_decl_node = self.relativeToNodeIndex(sub_decl_node_off); | |
| 1304 | try self.writePlNodeBlockWithoutSrc(stream, decl_index); | |
| 1305 | self.parent_decl_node = parent_decl_node; | |
| 1306 | try self.writeSrc(stream, decl_block_inst_data.src()); | |
| 1307 | try stream.writeAll("\n"); | |
| 1308 | } else { | |
| 1309 | try stream.print(" line({d}) hash({}): %{d} = ...\n", .{ | |
| 1310 | line, std.fmt.fmtSliceHexLower(&hash_bytes), decl_index, | |
| 1311 | }); | |
| 1312 | } | |
| 1266 | 1313 | } |
| 1267 | 1314 | return extra_index; |
| 1268 | 1315 | } |
| ... | ... | @@ -1329,17 +1376,10 @@ const Writer = struct { |
| 1329 | 1376 | } else { |
| 1330 | 1377 | const prev_parent_decl_node = self.parent_decl_node; |
| 1331 | 1378 | if (src_node) |off| self.parent_decl_node = self.relativeToNodeIndex(off); |
| 1332 | self.indent += 2; | |
| 1333 | if (body.len == 0) { | |
| 1334 | try stream.writeAll("{}, {\n"); | |
| 1335 | } else { | |
| 1336 | try stream.writeAll("{\n"); | |
| 1337 | try self.writeBody(stream, body); | |
| 1338 | ||
| 1339 | try stream.writeByteNTimes(' ', self.indent - 2); | |
| 1340 | try stream.writeAll("}, {\n"); | |
| 1341 | } | |
| 1379 | try self.writeBracedDecl(stream, body); | |
| 1380 | try stream.writeAll(", {\n"); | |
| 1342 | 1381 | |
| 1382 | self.indent += 2; | |
| 1343 | 1383 | const bit_bags_count = std.math.divCeil(usize, fields_len, 32) catch unreachable; |
| 1344 | 1384 | const body_end = extra_index; |
| 1345 | 1385 | extra_index += bit_bags_count; |
| ... | ... | @@ -1463,18 +1503,18 @@ const Writer = struct { |
| 1463 | 1503 | |
| 1464 | 1504 | try self.writeInstRef(stream, extra.data.operand); |
| 1465 | 1505 | |
| 1506 | self.indent += 2; | |
| 1507 | ||
| 1466 | 1508 | if (special.body.len != 0) { |
| 1467 | 1509 | const prong_name = switch (special_prong) { |
| 1468 | 1510 | .@"else" => "else", |
| 1469 | 1511 | .under => "_", |
| 1470 | 1512 | else => unreachable, |
| 1471 | 1513 | }; |
| 1472 | try stream.print(", {s} => {{\n", .{prong_name}); | |
| 1473 | self.indent += 2; | |
| 1474 | try self.writeBody(stream, special.body); | |
| 1475 | self.indent -= 2; | |
| 1514 | try stream.writeAll(",\n"); | |
| 1476 | 1515 | try stream.writeByteNTimes(' ', self.indent); |
| 1477 | try stream.writeAll("}"); | |
| 1516 | try stream.print("{s} => ", .{prong_name}); | |
| 1517 | try self.writeBracedBody(stream, special.body); | |
| 1478 | 1518 | } |
| 1479 | 1519 | |
| 1480 | 1520 | var extra_index: usize = special.end; |
| ... | ... | @@ -1488,16 +1528,16 @@ const Writer = struct { |
| 1488 | 1528 | const body = self.code.extra[extra_index..][0..body_len]; |
| 1489 | 1529 | extra_index += body_len; |
| 1490 | 1530 | |
| 1491 | try stream.writeAll(", "); | |
| 1492 | try self.writeInstRef(stream, item_ref); | |
| 1493 | try stream.writeAll(" => {\n"); | |
| 1494 | self.indent += 2; | |
| 1495 | try self.writeBody(stream, body); | |
| 1496 | self.indent -= 2; | |
| 1531 | try stream.writeAll(",\n"); | |
| 1497 | 1532 | try stream.writeByteNTimes(' ', self.indent); |
| 1498 | try stream.writeAll("}"); | |
| 1533 | try self.writeInstRef(stream, item_ref); | |
| 1534 | try stream.writeAll(" => "); | |
| 1535 | try self.writeBracedBody(stream, body); | |
| 1499 | 1536 | } |
| 1500 | 1537 | } |
| 1538 | ||
| 1539 | self.indent -= 2; | |
| 1540 | ||
| 1501 | 1541 | try stream.writeAll(") "); |
| 1502 | 1542 | try self.writeSrc(stream, inst_data.src()); |
| 1503 | 1543 | } |
| ... | ... | @@ -1527,18 +1567,18 @@ const Writer = struct { |
| 1527 | 1567 | |
| 1528 | 1568 | try self.writeInstRef(stream, extra.data.operand); |
| 1529 | 1569 | |
| 1570 | self.indent += 2; | |
| 1571 | ||
| 1530 | 1572 | if (special.body.len != 0) { |
| 1531 | 1573 | const prong_name = switch (special_prong) { |
| 1532 | 1574 | .@"else" => "else", |
| 1533 | 1575 | .under => "_", |
| 1534 | 1576 | else => unreachable, |
| 1535 | 1577 | }; |
| 1536 | try stream.print(", {s} => {{\n", .{prong_name}); | |
| 1537 | self.indent += 2; | |
| 1538 | try self.writeBody(stream, special.body); | |
| 1539 | self.indent -= 2; | |
| 1578 | try stream.writeAll(",\n"); | |
| 1540 | 1579 | try stream.writeByteNTimes(' ', self.indent); |
| 1541 | try stream.writeAll("}"); | |
| 1580 | try stream.print("{s} => ", .{prong_name}); | |
| 1581 | try self.writeBracedBody(stream, special.body); | |
| 1542 | 1582 | } |
| 1543 | 1583 | |
| 1544 | 1584 | var extra_index: usize = special.end; |
| ... | ... | @@ -1552,14 +1592,11 @@ const Writer = struct { |
| 1552 | 1592 | const body = self.code.extra[extra_index..][0..body_len]; |
| 1553 | 1593 | extra_index += body_len; |
| 1554 | 1594 | |
| 1555 | try stream.writeAll(", "); | |
| 1556 | try self.writeInstRef(stream, item_ref); | |
| 1557 | try stream.writeAll(" => {\n"); | |
| 1558 | self.indent += 2; | |
| 1559 | try self.writeBody(stream, body); | |
| 1560 | self.indent -= 2; | |
| 1595 | try stream.writeAll(",\n"); | |
| 1561 | 1596 | try stream.writeByteNTimes(' ', self.indent); |
| 1562 | try stream.writeAll("}"); | |
| 1597 | try self.writeInstRef(stream, item_ref); | |
| 1598 | try stream.writeAll(" => "); | |
| 1599 | try self.writeBracedBody(stream, body); | |
| 1563 | 1600 | } |
| 1564 | 1601 | } |
| 1565 | 1602 | { |
| ... | ... | @@ -1574,8 +1611,11 @@ const Writer = struct { |
| 1574 | 1611 | const items = self.code.refSlice(extra_index, items_len); |
| 1575 | 1612 | extra_index += items_len; |
| 1576 | 1613 | |
| 1577 | for (items) |item_ref| { | |
| 1578 | try stream.writeAll(", "); | |
| 1614 | try stream.writeAll(",\n"); | |
| 1615 | try stream.writeByteNTimes(' ', self.indent); | |
| 1616 | ||
| 1617 | for (items) |item_ref, item_i| { | |
| 1618 | if (item_i != 0) try stream.writeAll(", "); | |
| 1579 | 1619 | try self.writeInstRef(stream, item_ref); |
| 1580 | 1620 | } |
| 1581 | 1621 | |
| ... | ... | @@ -1586,7 +1626,9 @@ const Writer = struct { |
| 1586 | 1626 | const item_last = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); |
| 1587 | 1627 | extra_index += 1; |
| 1588 | 1628 | |
| 1589 | try stream.writeAll(", "); | |
| 1629 | if (range_i != 0 or items.len != 0) { | |
| 1630 | try stream.writeAll(", "); | |
| 1631 | } | |
| 1590 | 1632 | try self.writeInstRef(stream, item_first); |
| 1591 | 1633 | try stream.writeAll("..."); |
| 1592 | 1634 | try self.writeInstRef(stream, item_last); |
| ... | ... | @@ -1594,14 +1636,13 @@ const Writer = struct { |
| 1594 | 1636 | |
| 1595 | 1637 | const body = self.code.extra[extra_index..][0..body_len]; |
| 1596 | 1638 | extra_index += body_len; |
| 1597 | try stream.writeAll(" => {\n"); | |
| 1598 | self.indent += 2; | |
| 1599 | try self.writeBody(stream, body); | |
| 1600 | self.indent -= 2; | |
| 1601 | try stream.writeByteNTimes(' ', self.indent); | |
| 1602 | try stream.writeAll("}"); | |
| 1639 | try stream.writeAll(" => "); | |
| 1640 | try self.writeBracedBody(stream, body); | |
| 1603 | 1641 | } |
| 1604 | 1642 | } |
| 1643 | ||
| 1644 | self.indent -= 2; | |
| 1645 | ||
| 1605 | 1646 | try stream.writeAll(") "); |
| 1606 | 1647 | try self.writeSrc(stream, inst_data.src()); |
| 1607 | 1648 | } |
| ... | ... | @@ -1796,12 +1837,8 @@ const Writer = struct { |
| 1796 | 1837 | const extra = self.code.extraData(Zir.Inst.Block, inst_data.payload_index); |
| 1797 | 1838 | const body = self.code.extra[extra.end..][0..extra.data.body_len]; |
| 1798 | 1839 | try self.writeInstRef(stream, inst_data.lhs); |
| 1799 | try stream.writeAll(", {\n"); | |
| 1800 | self.indent += 2; | |
| 1801 | try self.writeBody(stream, body); | |
| 1802 | self.indent -= 2; | |
| 1803 | try stream.writeByteNTimes(' ', self.indent); | |
| 1804 | try stream.writeAll("})"); | |
| 1840 | try stream.writeAll(", "); | |
| 1841 | try self.writeBracedBody(stream, body); | |
| 1805 | 1842 | } |
| 1806 | 1843 | |
| 1807 | 1844 | fn writeIntType(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { |
| ... | ... | @@ -1846,12 +1883,8 @@ const Writer = struct { |
| 1846 | 1883 | if (ret_ty_body.len == 0) { |
| 1847 | 1884 | try stream.writeAll("ret_ty=void"); |
| 1848 | 1885 | } else { |
| 1849 | try stream.writeAll("ret_ty={\n"); | |
| 1850 | self.indent += 2; | |
| 1851 | try self.writeBody(stream, ret_ty_body); | |
| 1852 | self.indent -= 2; | |
| 1853 | try stream.writeByteNTimes(' ', self.indent); | |
| 1854 | try stream.writeAll("}"); | |
| 1886 | try stream.writeAll("ret_ty="); | |
| 1887 | try self.writeBracedBody(stream, ret_ty_body); | |
| 1855 | 1888 | } |
| 1856 | 1889 | |
| 1857 | 1890 | try self.writeOptionalInstRef(stream, ", cc=", cc); |
| ... | ... | @@ -1860,16 +1893,9 @@ const Writer = struct { |
| 1860 | 1893 | try self.writeFlag(stream, ", extern", is_extern); |
| 1861 | 1894 | try self.writeFlag(stream, ", inferror", inferred_error_set); |
| 1862 | 1895 | |
| 1863 | if (body.len == 0) { | |
| 1864 | try stream.writeAll(", body={}) "); | |
| 1865 | } else { | |
| 1866 | try stream.writeAll(", body={\n"); | |
| 1867 | self.indent += 2; | |
| 1868 | try self.writeBody(stream, body); | |
| 1869 | self.indent -= 2; | |
| 1870 | try stream.writeByteNTimes(' ', self.indent); | |
| 1871 | try stream.writeAll("}) "); | |
| 1872 | } | |
| 1896 | try stream.writeAll(", body="); | |
| 1897 | try self.writeBracedBody(stream, body); | |
| 1898 | try stream.writeAll(") "); | |
| 1873 | 1899 | if (body.len != 0) { |
| 1874 | 1900 | try stream.print("(lbrace={d}:{d},rbrace={d}:{d}) ", .{ |
| 1875 | 1901 | src_locs.lbrace_line, @truncate(u16, src_locs.columns), |
| ... | ... | @@ -1929,18 +1955,19 @@ const Writer = struct { |
| 1929 | 1955 | } |
| 1930 | 1956 | |
| 1931 | 1957 | fn writeSrc(self: *Writer, stream: anytype, src: LazySrcLoc) !void { |
| 1932 | const tree = self.file.tree; | |
| 1933 | const src_loc: Module.SrcLoc = .{ | |
| 1934 | .file_scope = self.file, | |
| 1935 | .parent_decl_node = self.parent_decl_node, | |
| 1936 | .lazy = src, | |
| 1937 | }; | |
| 1938 | // Caller must ensure AST tree is loaded. | |
| 1939 | const abs_byte_off = src_loc.byteOffset(self.gpa) catch unreachable; | |
| 1940 | const delta_line = std.zig.findLineColumn(tree.source, abs_byte_off); | |
| 1941 | try stream.print("{s}:{d}:{d}", .{ | |
| 1942 | @tagName(src), delta_line.line + 1, delta_line.column + 1, | |
| 1943 | }); | |
| 1958 | if (self.file.tree_loaded) { | |
| 1959 | const tree = self.file.tree; | |
| 1960 | const src_loc: Module.SrcLoc = .{ | |
| 1961 | .file_scope = self.file, | |
| 1962 | .parent_decl_node = self.parent_decl_node, | |
| 1963 | .lazy = src, | |
| 1964 | }; | |
| 1965 | const abs_byte_off = src_loc.byteOffset(self.gpa) catch unreachable; | |
| 1966 | const delta_line = std.zig.findLineColumn(tree.source, abs_byte_off); | |
| 1967 | try stream.print("{s}:{d}:{d}", .{ | |
| 1968 | @tagName(src), delta_line.line + 1, delta_line.column + 1, | |
| 1969 | }); | |
| 1970 | } | |
| 1944 | 1971 | } |
| 1945 | 1972 | |
| 1946 | 1973 | fn writeSrcNode(self: *Writer, stream: anytype, src_node: ?i32) !void { |
| ... | ... | @@ -1950,6 +1977,43 @@ const Writer = struct { |
| 1950 | 1977 | return self.writeSrc(stream, src); |
| 1951 | 1978 | } |
| 1952 | 1979 | |
| 1980 | fn writeBracedDecl(self: *Writer, stream: anytype, body: []const Zir.Inst.Index) !void { | |
| 1981 | try self.writeBracedBodyConditional(stream, body, self.recurse_decls); | |
| 1982 | } | |
| 1983 | ||
| 1984 | fn writeBracedBody(self: *Writer, stream: anytype, body: []const Zir.Inst.Index) !void { | |
| 1985 | try self.writeBracedBodyConditional(stream, body, self.recurse_blocks); | |
| 1986 | } | |
| 1987 | ||
| 1988 | fn writeBracedBodyConditional(self: *Writer, stream: anytype, body: []const Zir.Inst.Index, enabled: bool) !void { | |
| 1989 | if (body.len == 0) { | |
| 1990 | try stream.writeAll("{}"); | |
| 1991 | } else if (enabled) { | |
| 1992 | try stream.writeAll("{\n"); | |
| 1993 | self.indent += 2; | |
| 1994 | try self.writeBody(stream, body); | |
| 1995 | self.indent -= 2; | |
| 1996 | try stream.writeByteNTimes(' ', self.indent); | |
| 1997 | try stream.writeAll("}"); | |
| 1998 | } else if (body.len == 1) { | |
| 1999 | try stream.writeByte('{'); | |
| 2000 | try self.writeInstIndex(stream, body[0]); | |
| 2001 | try stream.writeByte('}'); | |
| 2002 | } else if (body.len == 2) { | |
| 2003 | try stream.writeByte('{'); | |
| 2004 | try self.writeInstIndex(stream, body[0]); | |
| 2005 | try stream.writeAll(", "); | |
| 2006 | try self.writeInstIndex(stream, body[1]); | |
| 2007 | try stream.writeByte('}'); | |
| 2008 | } else { | |
| 2009 | try stream.writeByte('{'); | |
| 2010 | try self.writeInstIndex(stream, body[0]); | |
| 2011 | try stream.writeAll(".."); | |
| 2012 | try self.writeInstIndex(stream, body[body.len - 1]); | |
| 2013 | try stream.writeByte('}'); | |
| 2014 | } | |
| 2015 | } | |
| 2016 | ||
| 1953 | 2017 | fn writeBody(self: *Writer, stream: anytype, body: []const Zir.Inst.Index) !void { |
| 1954 | 2018 | for (body) |inst| { |
| 1955 | 2019 | try stream.writeByteNTimes(' ', self.indent); |