authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-05-20 17:39:54-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-05-20 17:39:54-04:00
log7c2c0e36f8378d8efaf64d859bdffb91007db82a
treea8335099205f30d5bf9b4478ee9d73824b2f0abf
parent5db9f306ba88087d269dc5fe5672a5af5ab55333

stage2 parser: different memory layout of ParamDecl

Instead of being its own node, it's a struct inside FnProto. Instead of FnProto having a SinglyLinkedList of ParamDecl nodes, ParamDecls are appended directly in memory after the FnProto. throughput: 72.2 MiB/s => 72.9 MiB/s maxrss: 70 KB => 68 KB Importantly, the API is improved as well since the data is arranged linearly in memory.

3 files changed, 123 insertions(+), 105 deletions(-)

lib/std/zig/ast.zig+89-62
......@@ -477,7 +477,6 @@ pub const Node = struct {
477477 ErrorTag,
478478 AsmInput,
479479 AsmOutput,
480 ParamDecl,
481480 FieldInitializer,
482481 };
483482
......@@ -533,7 +532,6 @@ pub const Node = struct {
533532 switch (n.id) {
534533 .Root,
535534 .ContainerField,
536 .ParamDecl,
537535 .Block,
538536 .Payload,
539537 .PointerPayload,
......@@ -819,7 +817,7 @@ pub const Node = struct {
819817 return @ptrCast(*ContainerDecl, bytes.ptr);
820818 }
821819
822 pub fn free(self: *Decl, allocator: *mem.Allocator) void {
820 pub fn free(self: *ContainerDecl, allocator: *mem.Allocator) void {
823821 const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.fields_and_decls_len)];
824822 allocator.free(bytes);
825823 }
......@@ -979,13 +977,14 @@ pub const Node = struct {
979977 }
980978 };
981979
980 /// The params are directly after the FnProto in memory.
982981 pub const FnProto = struct {
983982 base: Node = Node{ .id = .FnProto },
984983 doc_comments: ?*DocComment,
985984 visib_token: ?TokenIndex,
986985 fn_token: TokenIndex,
987986 name_token: ?TokenIndex,
988 params: ParamList,
987 params_len: NodeIndex,
989988 return_type: ReturnType,
990989 var_args_token: ?TokenIndex,
991990 extern_export_inline_token: ?TokenIndex,
......@@ -997,16 +996,75 @@ pub const Node = struct {
997996 is_extern_prototype: bool = false, // TODO: Remove once extern fn rewriting is
998997 is_async: bool = false, // TODO: remove once async fn rewriting is
999998
1000 pub const ParamList = LinkedList(*Node);
1001
1002999 pub const ReturnType = union(enum) {
10031000 Explicit: *Node,
10041001 InferErrorSet: *Node,
10051002 Invalid: TokenIndex,
10061003 };
10071004
1005 pub const ParamDecl = struct {
1006 doc_comments: ?*DocComment,
1007 comptime_token: ?TokenIndex,
1008 noalias_token: ?TokenIndex,
1009 name_token: ?TokenIndex,
1010 param_type: ParamType,
1011
1012 pub const ParamType = union(enum) {
1013 var_type: *Node,
1014 var_args: TokenIndex,
1015 type_expr: *Node,
1016 };
1017
1018 pub fn iterate(self: *const ParamDecl) Node.Iterator {
1019 return .{ .parent_node = &self.base, .index = 0, .node = null };
1020 }
1021
1022 pub fn iterateNext(self: *const ParamDecl, it: *Node.Iterator) ?*Node {
1023 var i = it.index;
1024 it.index += 1;
1025
1026 if (i < 1) {
1027 switch (self.param_type) {
1028 .var_args => return null,
1029 .var_type, .type_expr => |node| return node,
1030 }
1031 }
1032 i -= 1;
1033
1034 return null;
1035 }
1036
1037 pub fn firstToken(self: *const ParamDecl) TokenIndex {
1038 if (self.comptime_token) |comptime_token| return comptime_token;
1039 if (self.noalias_token) |noalias_token| return noalias_token;
1040 if (self.name_token) |name_token| return name_token;
1041 switch (self.param_type) {
1042 .var_args => |tok| return tok,
1043 .var_type, .type_expr => |node| return node.firstToken(),
1044 }
1045 }
1046
1047 pub fn lastToken(self: *const ParamDecl) TokenIndex {
1048 switch (self.param_type) {
1049 .var_args => |tok| return tok,
1050 .var_type, .type_expr => |node| return node.lastToken(),
1051 }
1052 }
1053 };
1054
1055 /// After this the caller must initialize the params list.
1056 pub fn alloc(allocator: *mem.Allocator, params_len: NodeIndex) !*FnProto {
1057 const bytes = try allocator.alignedAlloc(u8, @alignOf(FnProto), sizeInBytes(params_len));
1058 return @ptrCast(*FnProto, bytes.ptr);
1059 }
1060
1061 pub fn free(self: *FnProto, allocator: *mem.Allocator) void {
1062 const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.params_len)];
1063 allocator.free(bytes);
1064 }
1065
10081066 pub fn iterate(self: *const FnProto) Node.Iterator {
1009 return .{ .parent_node = &self.base, .index = 0, .node = self.params.first };
1067 return .{ .parent_node = &self.base, .index = 0, .node = null };
10101068 }
10111069
10121070 pub fn iterateNext(self: *const FnProto, it: *Node.Iterator) ?*Node {
......@@ -1018,11 +1076,17 @@ pub const Node = struct {
10181076 i -= 1;
10191077 }
10201078
1021 if (it.node) |param| {
1022 it.index -= 1;
1023 it.node = param.next;
1024 return param.data;
1079 if (i < self.params_len) {
1080 switch (self.paramsConst()[i].param_type) {
1081 .var_type => |n| return n,
1082 .var_args => {
1083 i += 1;
1084 it.index += 1;
1085 },
1086 .type_expr => |n| return n,
1087 }
10251088 }
1089 i -= self.params_len;
10261090
10271091 if (self.align_expr) |align_expr| {
10281092 if (i < 1) return align_expr;
......@@ -1064,6 +1128,20 @@ pub const Node = struct {
10641128 .Invalid => |tok| return tok,
10651129 }
10661130 }
1131
1132 pub fn params(self: *FnProto) []ParamDecl {
1133 const decls_start = @ptrCast([*]u8, self) + @sizeOf(FnProto);
1134 return @ptrCast([*]ParamDecl, decls_start)[0..self.params_len];
1135 }
1136
1137 pub fn paramsConst(self: *const FnProto) []const ParamDecl {
1138 const decls_start = @ptrCast([*]const u8, self) + @sizeOf(FnProto);
1139 return @ptrCast([*]const ParamDecl, decls_start)[0..self.params_len];
1140 }
1141
1142 fn sizeInBytes(params_len: NodeIndex) usize {
1143 return @sizeOf(FnProto) + @sizeOf(ParamDecl) * @as(usize, params_len);
1144 }
10671145 };
10681146
10691147 pub const AnyFrameType = struct {
......@@ -1102,57 +1180,6 @@ pub const Node = struct {
11021180 }
11031181 };
11041182
1105 pub const ParamDecl = struct {
1106 base: Node = Node{ .id = .ParamDecl },
1107 doc_comments: ?*DocComment,
1108 comptime_token: ?TokenIndex,
1109 noalias_token: ?TokenIndex,
1110 name_token: ?TokenIndex,
1111 param_type: ParamType,
1112
1113 pub const ParamType = union(enum) {
1114 var_type: *Node,
1115 var_args: TokenIndex,
1116 type_expr: *Node,
1117 };
1118
1119 pub fn iterate(self: *const ParamDecl) Node.Iterator {
1120 return .{ .parent_node = &self.base, .index = 0, .node = null };
1121 }
1122
1123 pub fn iterateNext(self: *const ParamDecl, it: *Node.Iterator) ?*Node {
1124 var i = it.index;
1125 it.index += 1;
1126
1127 if (i < 1) {
1128 switch (self.param_type) {
1129 .var_args => return null,
1130 .var_type, .type_expr => |node| return node,
1131 }
1132 }
1133 i -= 1;
1134
1135 return null;
1136 }
1137
1138 pub fn firstToken(self: *const ParamDecl) TokenIndex {
1139 if (self.comptime_token) |comptime_token| return comptime_token;
1140 if (self.noalias_token) |noalias_token| return noalias_token;
1141 if (self.name_token) |name_token| return name_token;
1142 switch (self.param_type) {
1143 .var_args => |tok| return tok,
1144 .var_type, .type_expr => |node| return node.firstToken(),
1145 }
1146 }
1147
1148 pub fn lastToken(self: *const ParamDecl) TokenIndex {
1149 switch (self.param_type) {
1150 .var_args => |tok| return tok,
1151 .var_type, .type_expr => |node| return node.lastToken(),
1152 }
1153 }
1154 };
1155
11561183 pub const Block = struct {
11571184 base: Node = Node{ .id = .Block },
11581185 label: ?TokenIndex,
lib/std/zig/parse.zig+27-29
......@@ -71,7 +71,7 @@ const Parser = struct {
7171 const eof_token = p.eatToken(.Eof).?;
7272
7373 const node = try Node.Root.create(&p.arena.allocator, decls.len, eof_token);
74 std.mem.copy(*ast.Node, node.decls(), decls);
74 std.mem.copy(*Node, node.decls(), decls);
7575
7676 return node;
7777 }
......@@ -96,8 +96,8 @@ const Parser = struct {
9696 /// / ContainerField COMMA ContainerMembers
9797 /// / ContainerField
9898 /// /
99 fn parseContainerMembers(p: *Parser, top_level: bool) ![]*ast.Node {
100 var list = std.ArrayList(*ast.Node).init(p.gpa);
99 fn parseContainerMembers(p: *Parser, top_level: bool) ![]*Node {
100 var list = std.ArrayList(*Node).init(p.gpa);
101101 defer list.deinit();
102102
103103 var field_state: union(enum) {
......@@ -522,6 +522,7 @@ const Parser = struct {
522522 const name_token = p.eatToken(.Identifier);
523523 const lparen = try p.expectToken(.LParen);
524524 const params = try p.parseParamDeclList(&var_args_token);
525 defer p.gpa.free(params);
525526 const rparen = try p.expectToken(.RParen);
526527 const align_expr = try p.parseByteAlign();
527528 const section_expr = try p.parseLinkSection();
......@@ -544,13 +545,13 @@ const Parser = struct {
544545 else
545546 R{ .Explicit = return_type_expr.? };
546547
547 const fn_proto_node = try p.arena.allocator.create(Node.FnProto);
548 const fn_proto_node = try Node.FnProto.alloc(&p.arena.allocator, params.len);
548549 fn_proto_node.* = .{
549550 .doc_comments = null,
550551 .visib_token = null,
551552 .fn_token = fn_token,
552553 .name_token = name_token,
553 .params = params,
554 .params_len = params.len,
554555 .return_type = return_type,
555556 .var_args_token = var_args_token,
556557 .extern_export_inline_token = null,
......@@ -562,6 +563,7 @@ const Parser = struct {
562563 .is_extern_prototype = is_extern,
563564 .is_async = is_async,
564565 };
566 std.mem.copy(Node.FnProto.ParamDecl, fn_proto_node.params(), params);
565567
566568 return &fn_proto_node.base;
567569 }
......@@ -621,7 +623,7 @@ const Parser = struct {
621623 var type_expr: ?*Node = null;
622624 if (p.eatToken(.Colon)) |_| {
623625 if (p.eatToken(.Keyword_var)) |var_tok| {
624 const node = try p.arena.allocator.create(ast.Node.VarType);
626 const node = try p.arena.allocator.create(Node.VarType);
625627 node.* = .{ .token = var_tok };
626628 type_expr = &node.base;
627629 } else {
......@@ -1936,7 +1938,7 @@ const Parser = struct {
19361938 }
19371939
19381940 /// ParamDecl <- (KEYWORD_noalias / KEYWORD_comptime)? (IDENTIFIER COLON)? ParamType
1939 fn parseParamDecl(p: *Parser) !?*Node {
1941 fn parseParamDecl(p: *Parser, list: *std.ArrayList(Node.FnProto.ParamDecl)) !bool {
19401942 const doc_comments = try p.parseDocComment();
19411943 const noalias_token = p.eatToken(.Keyword_noalias);
19421944 const comptime_token = if (noalias_token == null) p.eatToken(.Keyword_comptime) else null;
......@@ -1951,31 +1953,30 @@ const Parser = struct {
19511953 if (noalias_token == null and
19521954 comptime_token == null and
19531955 name_token == null and
1954 doc_comments == null) return null;
1956 doc_comments == null) return false;
19551957 try p.errors.append(p.gpa, .{
19561958 .ExpectedParamType = .{ .token = p.tok_i },
19571959 });
19581960 return error.ParseError;
19591961 };
19601962
1961 const param_decl = try p.arena.allocator.create(Node.ParamDecl);
1962 param_decl.* = .{
1963 (try list.addOne()).* = .{
19631964 .doc_comments = doc_comments,
19641965 .comptime_token = comptime_token,
19651966 .noalias_token = noalias_token,
19661967 .name_token = name_token,
19671968 .param_type = param_type,
19681969 };
1969 return &param_decl.base;
1970 return true;
19701971 }
19711972
19721973 /// ParamType
19731974 /// <- KEYWORD_var
19741975 /// / DOT3
19751976 /// / TypeExpr
1976 fn parseParamType(p: *Parser) !?Node.ParamDecl.ParamType {
1977 fn parseParamType(p: *Parser) !?Node.FnProto.ParamDecl.ParamType {
19771978 // TODO cast from tuple to error union is broken
1978 const P = Node.ParamDecl.ParamType;
1979 const P = Node.FnProto.ParamDecl.ParamType;
19791980 if (try p.parseVarType()) |node| return P{ .var_type = node };
19801981 if (p.eatToken(.Ellipsis3)) |token| return P{ .var_args = token };
19811982 if (try p.parseTypeExpr()) |node| return P{ .type_expr = node };
......@@ -2587,7 +2588,7 @@ const Parser = struct {
25872588
25882589 if (p.eatToken(.Ellipsis2) != null) {
25892590 const end_expr = try p.parseExpr();
2590 const sentinel: ?*ast.Node = if (p.eatToken(.Colon) != null)
2591 const sentinel: ?*Node = if (p.eatToken(.Colon) != null)
25912592 try p.parseExpr()
25922593 else
25932594 null;
......@@ -2616,7 +2617,7 @@ const Parser = struct {
26162617 if (p.eatToken(.Period)) |period| {
26172618 if (try p.parseIdentifier()) |identifier| {
26182619 // TODO: It's a bit weird to return an InfixOp from the SuffixOp parser.
2619 // Should there be an ast.Node.SuffixOp.FieldAccess variant? Or should
2620 // Should there be an Node.SuffixOp.FieldAccess variant? Or should
26202621 // this grammar rule be altered?
26212622 const node = try p.arena.allocator.create(Node.InfixOp);
26222623 node.* = .{
......@@ -2652,13 +2653,13 @@ const Parser = struct {
26522653 /// ExprList <- (Expr COMMA)* Expr?
26532654 fn parseFnCallArguments(p: *Parser) !?AnnotatedParamList {
26542655 if (p.eatToken(.LParen) == null) return null;
2655 const list = try ListParseFn(Node.FnProto.ParamList, parseExpr)(p);
2656 const list = try ListParseFn(std.SinglyLinkedList(*Node), parseExpr)(p);
26562657 const rparen = try p.expectToken(.RParen);
26572658 return AnnotatedParamList{ .list = list, .rparen = rparen };
26582659 }
26592660
26602661 const AnnotatedParamList = struct {
2661 list: Node.FnProto.ParamList, // NOTE: may also be any other type SegmentedList(*Node, 2)
2662 list: std.SinglyLinkedList(*Node),
26622663 rparen: TokenIndex,
26632664 };
26642665
......@@ -2797,14 +2798,14 @@ const Parser = struct {
27972798 .lbrace_token = lbrace,
27982799 .rbrace_token = rbrace,
27992800 };
2800 std.mem.copy(*ast.Node, node.fieldsAndDecls(), members);
2801 std.mem.copy(*Node, node.fieldsAndDecls(), members);
28012802 return &node.base;
28022803 }
28032804
28042805 /// Holds temporary data until we are ready to construct the full ContainerDecl AST node.
28052806 const ContainerDeclType = struct {
28062807 kind_token: TokenIndex,
2807 init_arg_expr: ast.Node.ContainerDecl.InitArg,
2808 init_arg_expr: Node.ContainerDecl.InitArg,
28082809 };
28092810
28102811 /// ContainerDeclType
......@@ -2893,14 +2894,11 @@ const Parser = struct {
28932894 }
28942895
28952896 /// ParamDeclList <- (ParamDecl COMMA)* ParamDecl?
2896 fn parseParamDeclList(p: *Parser, var_args_token: *?TokenIndex) !Node.FnProto.ParamList {
2897 var list = Node.FnProto.ParamList{};
2898 var list_it = &list.first;
2899 var last: ?*Node = null;
2900 while (try p.parseParamDecl()) |node| {
2901 last = node;
2902 list_it = try p.llpush(*Node, list_it, node);
2897 fn parseParamDeclList(p: *Parser, var_args_token: *?TokenIndex) ![]Node.FnProto.ParamDecl {
2898 var list = std.ArrayList(Node.FnProto.ParamDecl).init(p.gpa);
2899 defer list.deinit();
29032900
2901 while (try p.parseParamDecl(&list)) {
29042902 switch (p.tokens[p.tok_i].id) {
29052903 .Comma => _ = p.nextToken(),
29062904 // all possible delimiters
......@@ -2914,13 +2912,13 @@ const Parser = struct {
29142912 },
29152913 }
29162914 }
2917 if (last) |node| {
2918 const param_type = node.cast(Node.ParamDecl).?.param_type;
2915 if (list.items.len != 0) {
2916 const param_type = list.items[list.items.len - 1].param_type;
29192917 if (param_type == .var_args) {
29202918 var_args_token.* = param_type.var_args;
29212919 }
29222920 }
2923 return list;
2921 return list.toOwnedSlice();
29242922 }
29252923
29262924 const NodeParseFn = fn (p: *Parser) Error!?*Node;
lib/std/zig/render.zig+7-14
......@@ -1479,13 +1479,11 @@ fn renderExpression(
14791479 try renderToken(tree, stream, lparen, indent, start_col, Space.None); // (
14801480
14811481 // render all on one line, no trailing comma
1482 var it = fn_proto.params.first;
1483 while (it) |param_decl_node_node| : (it = param_decl_node_node.next) {
1484 const param_decl_node = param_decl_node_node.data;
1485 try renderParamDecl(allocator, stream, tree, indent, start_col, param_decl_node, Space.None);
1482 for (fn_proto.params()) |param_decl, i| {
1483 try renderParamDecl(allocator, stream, tree, indent, start_col, param_decl, Space.None);
14861484
1487 if (param_decl_node_node.next != null) {
1488 const comma = tree.nextToken(param_decl_node.lastToken());
1485 if (i + 1 < fn_proto.params_len) {
1486 const comma = tree.nextToken(param_decl.lastToken());
14891487 try renderToken(tree, stream, comma, indent, start_col, Space.Space); // ,
14901488 }
14911489 }
......@@ -1494,11 +1492,9 @@ fn renderExpression(
14941492 const new_indent = indent + indent_delta;
14951493 try renderToken(tree, stream, lparen, new_indent, start_col, Space.Newline); // (
14961494
1497 var it = fn_proto.params.first;
1498 while (it) |param_decl_node_node| : (it = param_decl_node_node.next) {
1499 const param_decl_node = param_decl_node_node.data;
1495 for (fn_proto.params()) |param_decl| {
15001496 try stream.writeByteNTimes(' ', new_indent);
1501 try renderParamDecl(allocator, stream, tree, new_indent, start_col, param_decl_node, Space.Comma);
1497 try renderParamDecl(allocator, stream, tree, new_indent, start_col, param_decl, Space.Comma);
15021498 }
15031499 try stream.writeByteNTimes(' ', indent);
15041500 }
......@@ -2079,7 +2075,6 @@ fn renderExpression(
20792075 .VarDecl,
20802076 .Use,
20812077 .TestDecl,
2082 .ParamDecl,
20832078 => unreachable,
20842079 }
20852080}
......@@ -2162,11 +2157,9 @@ fn renderParamDecl(
21622157 tree: *ast.Tree,
21632158 indent: usize,
21642159 start_col: *usize,
2165 base: *ast.Node,
2160 param_decl: ast.Node.FnProto.ParamDecl,
21662161 space: Space,
21672162) (@TypeOf(stream).Error || Error)!void {
2168 const param_decl = @fieldParentPtr(ast.Node.ParamDecl, "base", base);
2169
21702163 try renderDocComments(tree, stream, param_decl, indent, start_col);
21712164
21722165 if (param_decl.comptime_token) |comptime_token| {