authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2023-09-20 18:47:47+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2023-09-23 22:01:08+01:00
log01906a3ad8b792627e55551686c25d02fef64e98
treeaf2bb5724e33fd6c761f025aee60041482aa25ef
parentfbe9fcd243c646e62dc19f87d3e03b7e3f458a84
signaturelock-open Commit is signed but in an unrecognized format.

print_zir: speed up ZIR printing

Source location resolution previously made ZIR printing incredibly slow, since it was O(N^2). Since we usually resolve source locations approximately in order, it is much more efficient to resolve them using a "cursor" which navigates the file. This takes the time for `zig ast-check -t Sema.zig` down from many minutes (enough that I got bored and killed the process; well over 10) to a few seconds.

1 files changed, 59 insertions(+), 2 deletions(-)

src/print_zir.zig+59-2
...@@ -130,6 +130,63 @@ const Writer = struct {...@@ -130,6 +130,63 @@ const Writer = struct {
130 recurse_decls: bool,130 recurse_decls: bool,
131 recurse_blocks: bool,131 recurse_blocks: bool,
132132
133 /// Using `std.zig.findLineColumn` whenever we need to resolve a source location makes ZIR
134 /// printing O(N^2), which can have drastic effects - taking a ZIR dump from a few seconds to
135 /// many minutes. Since we're usually resolving source locations close to one another,
136 /// preserving state across source location resolutions speeds things up a lot.
137 line_col_cursor: struct {
138 line: usize = 0,
139 column: usize = 0,
140 line_start: usize = 0,
141 off: usize = 0,
142
143 fn find(cur: *@This(), source: []const u8, want_offset: usize) std.zig.Loc {
144 if (want_offset < cur.off) {
145 // Go back to the start of this line
146 cur.off = cur.line_start;
147 cur.column = 0;
148
149 while (want_offset < cur.off) {
150 // Go back to the newline
151 cur.off -= 1;
152
153 // Seek to the start of the previous line
154 while (cur.off > 0 and source[cur.off - 1] != '\n') {
155 cur.off -= 1;
156 }
157 cur.line_start = cur.off;
158 cur.line -= 1;
159 }
160 }
161
162 // The cursor is now positioned before `want_offset`.
163 // Seek forward as in `std.zig.findLineColumn`.
164
165 while (cur.off < want_offset) : (cur.off += 1) {
166 switch (source[cur.off]) {
167 '\n' => {
168 cur.line += 1;
169 cur.column = 0;
170 cur.line_start = cur.off + 1;
171 },
172 else => {
173 cur.column += 1;
174 },
175 }
176 }
177
178 while (cur.off < source.len and source[cur.off] != '\n') {
179 cur.off += 1;
180 }
181
182 return .{
183 .line = cur.line,
184 .column = cur.column,
185 .source_line = source[cur.line_start..cur.off],
186 };
187 }
188 } = .{},
189
133 fn relativeToNodeIndex(self: *Writer, offset: i32) Ast.Node.Index {190 fn relativeToNodeIndex(self: *Writer, offset: i32) Ast.Node.Index {
134 return @as(Ast.Node.Index, @bitCast(offset + @as(i32, @bitCast(self.parent_decl_node))));191 return @as(Ast.Node.Index, @bitCast(offset + @as(i32, @bitCast(self.parent_decl_node))));
135 }192 }
...@@ -2590,8 +2647,8 @@ const Writer = struct {...@@ -2590,8 +2647,8 @@ const Writer = struct {
2590 .lazy = src,2647 .lazy = src,
2591 };2648 };
2592 const src_span = src_loc.span(self.gpa) catch unreachable;2649 const src_span = src_loc.span(self.gpa) catch unreachable;
2593 const start = std.zig.findLineColumn(tree.source, src_span.start);2650 const start = self.line_col_cursor.find(tree.source, src_span.start);
2594 const end = std.zig.findLineColumn(tree.source, src_span.end);2651 const end = self.line_col_cursor.find(tree.source, src_span.end);
2595 try stream.print("{s}:{d}:{d} to :{d}:{d}", .{2652 try stream.print("{s}:{d}:{d} to :{d}:{d}", .{
2596 @tagName(src), start.line + 1, start.column + 1,2653 @tagName(src), start.line + 1, start.column + 1,
2597 end.line + 1, end.column + 1,2654 end.line + 1, end.column + 1,