authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-05-20 23:47:04-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-05-20 23:47:04-04:00
logd57d9448aa322c1818de110adeb3cc69ac5dbcd9
tree9bef690df27fc49b002aef664725fb01a892351d
parent3c5d581ce35b137a7b80ac1431c1d9132e281fef

stage2 parsing: rework block statements AST memory layout

block statements are now directly following the Block AST node rather than a singly linked list. This had negligible impact on performance: throughput: 72.3 MiB/s => 72.7 MiB/s however it greatly improves the API since the statements are laid out in a flat array in memory.

3 files changed, 48 insertions(+), 18 deletions(-)

lib/std/zig/ast.zig+36-7
...@@ -804,6 +804,7 @@ pub const Node = struct {...@@ -804,6 +804,7 @@ pub const Node = struct {
804 }804 }
805 };805 };
806806
807 /// The fields and decls Node pointers directly follow this struct in memory.
807 pub const ContainerDecl = struct {808 pub const ContainerDecl = struct {
808 base: Node = Node{ .id = .ContainerDecl },809 base: Node = Node{ .id = .ContainerDecl },
809 kind_token: TokenIndex,810 kind_token: TokenIndex,
...@@ -1188,23 +1189,37 @@ pub const Node = struct {...@@ -1188,23 +1189,37 @@ pub const Node = struct {
1188 }1189 }
1189 };1190 };
11901191
1192 /// The statements of the block follow Block directly in memory.
1191 pub const Block = struct {1193 pub const Block = struct {
1192 base: Node = Node{ .id = .Block },1194 base: Node = Node{ .id = .Block },
1193 label: ?TokenIndex,1195 statements_len: NodeIndex,
1194 lbrace: TokenIndex,1196 lbrace: TokenIndex,
1195 statements: StatementList,
1196 rbrace: TokenIndex,1197 rbrace: TokenIndex,
1198 label: ?TokenIndex,
11971199
1198 pub const StatementList = LinkedList(*Node);1200 /// After this the caller must initialize the statements list.
1201 pub fn alloc(allocator: *mem.Allocator, statements_len: NodeIndex) !*Block {
1202 const bytes = try allocator.alignedAlloc(u8, @alignOf(Block), sizeInBytes(statements_len));
1203 return @ptrCast(*Block, bytes.ptr);
1204 }
1205
1206 pub fn free(self: *Block, allocator: *mem.Allocator) void {
1207 const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.statements_len)];
1208 allocator.free(bytes);
1209 }
11991210
1200 pub fn iterate(self: *const Block) Node.Iterator {1211 pub fn iterate(self: *const Block) Node.Iterator {
1201 return .{ .parent_node = &self.base, .index = 0, .node = self.statements.first };1212 return .{ .parent_node = &self.base, .index = 0, .node = null };
1202 }1213 }
12031214
1204 pub fn iterateNext(self: *const Block, it: *Node.Iterator) ?*Node {1215 pub fn iterateNext(self: *const Block, it: *Node.Iterator) ?*Node {
1205 const child = it.node orelse return null;1216 var i = it.index;
1206 it.node = child.next;1217 it.index += 1;
1207 return child.data;1218
1219 if (i < self.statements_len) return self.statementsConst()[i];
1220 i -= self.statements_len;
1221
1222 return null;
1208 }1223 }
12091224
1210 pub fn firstToken(self: *const Block) TokenIndex {1225 pub fn firstToken(self: *const Block) TokenIndex {
...@@ -1218,6 +1233,20 @@ pub const Node = struct {...@@ -1218,6 +1233,20 @@ pub const Node = struct {
1218 pub fn lastToken(self: *const Block) TokenIndex {1233 pub fn lastToken(self: *const Block) TokenIndex {
1219 return self.rbrace;1234 return self.rbrace;
1220 }1235 }
1236
1237 pub fn statements(self: *Block) []*Node {
1238 const decls_start = @ptrCast([*]u8, self) + @sizeOf(Block);
1239 return @ptrCast([*]*Node, decls_start)[0..self.statements_len];
1240 }
1241
1242 pub fn statementsConst(self: *const Block) []const *Node {
1243 const decls_start = @ptrCast([*]const u8, self) + @sizeOf(Block);
1244 return @ptrCast([*]const *Node, decls_start)[0..self.statements_len];
1245 }
1246
1247 fn sizeInBytes(statements_len: NodeIndex) usize {
1248 return @sizeOf(Block) + @sizeOf(*Node) * @as(usize, statements_len);
1249 }
1221 };1250 };
12221251
1223 pub const Defer = struct {1252 pub const Defer = struct {
lib/std/zig/parse.zig+7-5
...@@ -1178,8 +1178,9 @@ const Parser = struct {...@@ -1178,8 +1178,9 @@ const Parser = struct {
1178 fn parseBlock(p: *Parser) !?*Node {1178 fn parseBlock(p: *Parser) !?*Node {
1179 const lbrace = p.eatToken(.LBrace) orelse return null;1179 const lbrace = p.eatToken(.LBrace) orelse return null;
11801180
1181 var statements = Node.Block.StatementList{};1181 var statements = std.ArrayList(*Node).init(p.gpa);
1182 var statements_it = &statements.first;1182 defer statements.deinit();
1183
1183 while (true) {1184 while (true) {
1184 const statement = (p.parseStatement() catch |err| switch (err) {1185 const statement = (p.parseStatement() catch |err| switch (err) {
1185 error.OutOfMemory => return error.OutOfMemory,1186 error.OutOfMemory => return error.OutOfMemory,
...@@ -1189,18 +1190,19 @@ const Parser = struct {...@@ -1189,18 +1190,19 @@ const Parser = struct {
1189 continue;1190 continue;
1190 },1191 },
1191 }) orelse break;1192 }) orelse break;
1192 statements_it = try p.llpush(*Node, statements_it, statement);1193 try statements.append(statement);
1193 }1194 }
11941195
1195 const rbrace = try p.expectToken(.RBrace);1196 const rbrace = try p.expectToken(.RBrace);
11961197
1197 const block_node = try p.arena.allocator.create(Node.Block);1198 const block_node = try Node.Block.alloc(&p.arena.allocator, statements.items.len);
1198 block_node.* = .{1199 block_node.* = .{
1199 .label = null,1200 .label = null,
1200 .lbrace = lbrace,1201 .lbrace = lbrace,
1201 .statements = statements,1202 .statements_len = statements.items.len,
1202 .rbrace = rbrace,1203 .rbrace = rbrace,
1203 };1204 };
1205 std.mem.copy(*Node, block_node.statements(), statements.items);
12041206
1205 return &block_node.base;1207 return &block_node.base;
1206 }1208 }
lib/std/zig/render.zig+5-6
...@@ -358,21 +358,20 @@ fn renderExpression(...@@ -358,21 +358,20 @@ fn renderExpression(
358 try renderToken(tree, stream, tree.nextToken(label), indent, start_col, Space.Space);358 try renderToken(tree, stream, tree.nextToken(label), indent, start_col, Space.Space);
359 }359 }
360360
361 if (block.statements.first == null) {361 if (block.statements_len == 0) {
362 try renderToken(tree, stream, block.lbrace, indent + indent_delta, start_col, Space.None);362 try renderToken(tree, stream, block.lbrace, indent + indent_delta, start_col, Space.None);
363 return renderToken(tree, stream, block.rbrace, indent, start_col, space);363 return renderToken(tree, stream, block.rbrace, indent, start_col, space);
364 } else {364 } else {
365 const block_indent = indent + indent_delta;365 const block_indent = indent + indent_delta;
366 try renderToken(tree, stream, block.lbrace, block_indent, start_col, Space.Newline);366 try renderToken(tree, stream, block.lbrace, block_indent, start_col, Space.Newline);
367367
368 var it = block.statements.first;368 const block_statements = block.statements();
369 while (it) |statement_node| : (it = statement_node.next) {369 for (block_statements) |statement, i| {
370 const statement = statement_node.data;
371 try stream.writeByteNTimes(' ', block_indent);370 try stream.writeByteNTimes(' ', block_indent);
372 try renderStatement(allocator, stream, tree, block_indent, start_col, statement);371 try renderStatement(allocator, stream, tree, block_indent, start_col, statement);
373372
374 if (statement_node.next) |next_statement| {373 if (i + 1 < block_statements.len) {
375 try renderExtraNewline(tree, stream, start_col, next_statement.data);374 try renderExtraNewline(tree, stream, start_col, block_statements[i + 1]);
376 }375 }
377 }376 }
378377