authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-08-19 17:52:22-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-08-19 17:52:22-07:00
log9ec9c0f5e57820f4baa04b9674a6a4a88235b863
tree31f0da7dad51bcf707cba8555d7aa33f0ab0a1ba
parent5fdcb1a792e271c67f3aec36f8aca9e6c5503013

optimize the memory layout of Module.Fn and Module.Var

`is_pub` added to `Fn` would cost us an additional 8 bytes of memory per function, which is a real bummer since it's only 1 bit of information. If we wanted to really remove this, I suspect we could make this a function isPub() which looks at the AST of the corresponding Decl and finds if the FnProto AST node has the pub token. However I saw an easier approach - The data of whether something is pub or not is actually a property of a Decl anyway, not a function, so we can look at moving the field into Decl. Indeed, doing this, we see that Decl already has deletion_flag: bool which is hiding in the padding bytes between the enum (1 byte) and the following u32 field (generation). So if we put the is_pub bool there, it actually will take up no additional space, with 1 byte of padding remaining. This was an easy reworking of the code since any func.is_pub could be changed simply to func.owner_decl.is_pub. I also modified `Var` to make the init value non-optional and moved the optional bit to a has_init: bool field. This is worse from the perspective of control flow and safety, however it makes `@sizeOf(Var)` go from 32 bytes to 24 bytes. The more code we can fit into memory at once, the more justified we are in using the compiler as a long-running process that does incremental updates.

4 files changed, 16 insertions(+), 15 deletions(-)

src-self-hosted/Module.zig+13-13
...@@ -170,6 +170,9 @@ pub const Decl = struct {...@@ -170,6 +170,9 @@ pub const Decl = struct {
170 /// This flag is set when this Decl is added to a check_for_deletion set, and cleared170 /// This flag is set when this Decl is added to a check_for_deletion set, and cleared
171 /// when removed.171 /// when removed.
172 deletion_flag: bool,172 deletion_flag: bool,
173 /// Whether the corresponding AST decl has a `pub` keyword.
174 is_pub: bool,
175
173 /// An integer that can be checked against the corresponding incrementing176 /// An integer that can be checked against the corresponding incrementing
174 /// generation field of Module. This is used to determine whether `complete` status177 /// generation field of Module. This is used to determine whether `complete` status
175 /// represents pre- or post- re-analysis.178 /// represents pre- or post- re-analysis.
...@@ -290,8 +293,6 @@ pub const Fn = struct {...@@ -290,8 +293,6 @@ pub const Fn = struct {
290 },293 },
291 owner_decl: *Decl,294 owner_decl: *Decl,
292295
293 is_pub: bool,
294
295 /// This memory is temporary and points to stack memory for the duration296 /// This memory is temporary and points to stack memory for the duration
296 /// of Fn analysis.297 /// of Fn analysis.
297 pub const Analysis = struct {298 pub const Analysis = struct {
...@@ -323,10 +324,10 @@ pub const Fn = struct {...@@ -323,10 +324,10 @@ pub const Fn = struct {
323};324};
324325
325pub const Var = struct {326pub const Var = struct {
326 value: ?Value,327 init: Value,
327 owner_decl: *Decl,328 owner_decl: *Decl,
328329
329 is_pub: bool,330 has_init: bool,
330 is_extern: bool,331 is_extern: bool,
331 is_mutable: bool,332 is_mutable: bool,
332 is_threadlocal: bool,333 is_threadlocal: bool,
...@@ -1247,7 +1248,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1247,7 +1248,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1247 };1248 };
1248 defer fn_type_scope.instructions.deinit(self.gpa);1249 defer fn_type_scope.instructions.deinit(self.gpa);
12491250
1250 const is_pub = fn_proto.getTrailer("visib_token") != null;1251 decl.is_pub = fn_proto.getTrailer("visib_token") != null;
1251 const body_node = fn_proto.getTrailer("body_node") orelse1252 const body_node = fn_proto.getTrailer("body_node") orelse
1252 return self.failTok(&fn_type_scope.base, fn_proto.fn_token, "TODO implement extern functions", .{});1253 return self.failTok(&fn_type_scope.base, fn_proto.fn_token, "TODO implement extern functions", .{});
12531254
...@@ -1385,7 +1386,6 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1385,7 +1386,6 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1385 new_func.* = .{1386 new_func.* = .{
1386 .analysis = .{ .queued = fn_zir },1387 .analysis = .{ .queued = fn_zir },
1387 .owner_decl = decl,1388 .owner_decl = decl,
1388 .is_pub = is_pub,
1389 };1389 };
1390 fn_payload.* = .{ .func = new_func };1390 fn_payload.* = .{ .func = new_func };
13911391
...@@ -1452,7 +1452,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1452,7 +1452,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1452 };1452 };
1453 defer block_scope.instructions.deinit(self.gpa);1453 defer block_scope.instructions.deinit(self.gpa);
14541454
1455 const is_pub = var_decl.getTrailer("visib_token") != null;1455 decl.is_pub = var_decl.getTrailer("visib_token") != null;
1456 const is_extern = blk: {1456 const is_extern = blk: {
1457 const maybe_extern_token = var_decl.getTrailer("extern_export_token") orelse1457 const maybe_extern_token = var_decl.getTrailer("extern_export_token") orelse
1458 break :blk false;1458 break :blk false;
...@@ -1477,7 +1477,6 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1477,7 +1477,6 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1477 return self.failNode(&block_scope.base, sect_expr, "TODO implement function section expression", .{});1477 return self.failNode(&block_scope.base, sect_expr, "TODO implement function section expression", .{});
1478 }1478 }
14791479
1480
1481 const explicit_type = blk: {1480 const explicit_type = blk: {
1482 const type_node = var_decl.getTrailer("type_node") orelse1481 const type_node = var_decl.getTrailer("type_node") orelse
1483 break :blk null;1482 break :blk null;
...@@ -1568,9 +1567,9 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {...@@ -1568,9 +1567,9 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
1568 const new_variable = try decl_arena.allocator.create(Var);1567 const new_variable = try decl_arena.allocator.create(Var);
1569 const var_payload = try decl_arena.allocator.create(Value.Payload.Variable);1568 const var_payload = try decl_arena.allocator.create(Value.Payload.Variable);
1570 new_variable.* = .{1569 new_variable.* = .{
1571 .value = value,
1572 .owner_decl = decl,1570 .owner_decl = decl,
1573 .is_pub = is_pub,1571 .init = value orelse undefined,
1572 .has_init = value != null,
1574 .is_extern = is_extern,1573 .is_extern = is_extern,
1575 .is_mutable = is_mutable,1574 .is_mutable = is_mutable,
1576 .is_threadlocal = is_threadlocal,1575 .is_threadlocal = is_threadlocal,
...@@ -2004,6 +2003,7 @@ fn allocateNewDecl(...@@ -2004,6 +2003,7 @@ fn allocateNewDecl(
2004 .wasm => .{ .wasm = null },2003 .wasm => .{ .wasm = null },
2005 },2004 },
2006 .generation = 0,2005 .generation = 0,
2006 .is_pub = false,
2007 };2007 };
2008 return new_decl;2008 return new_decl;
2009}2009}
...@@ -2440,15 +2440,15 @@ fn analyzeVarRef(self: *Module, scope: *Scope, src: usize, tv: TypedValue) Inner...@@ -2440,15 +2440,15 @@ fn analyzeVarRef(self: *Module, scope: *Scope, src: usize, tv: TypedValue) Inner
2440 const variable = tv.val.cast(Value.Payload.Variable).?.variable;2440 const variable = tv.val.cast(Value.Payload.Variable).?.variable;
24412441
2442 const ty = try self.singlePtrType(scope, src, variable.is_mutable, tv.ty);2442 const ty = try self.singlePtrType(scope, src, variable.is_mutable, tv.ty);
2443 if (!variable.is_mutable and !variable.is_extern and variable.value != null) {2443 if (!variable.is_mutable and !variable.is_extern and variable.has_init) {
2444 const val_payload = try scope.arena().create(Value.Payload.RefVal);2444 const val_payload = try scope.arena().create(Value.Payload.RefVal);
2445 val_payload.* = .{ .val = variable.value.? };2445 val_payload.* = .{ .val = variable.init };
2446 return self.constInst(scope, src, .{2446 return self.constInst(scope, src, .{
2447 .ty = ty,2447 .ty = ty,
2448 .val = Value.initPayload(&val_payload.base),2448 .val = Value.initPayload(&val_payload.base),
2449 });2449 });
2450 }2450 }
2451 2451
2452 const b = try self.requireRuntimeBlock(scope, src);2452 const b = try self.requireRuntimeBlock(scope, src);
2453 const inst = try b.arena.create(Inst.VarPtr);2453 const inst = try b.arena.create(Inst.VarPtr);
2454 inst.* = .{2454 inst.* = .{
src-self-hosted/zir.zig+1-1
...@@ -1881,7 +1881,7 @@ const EmitZIR = struct {...@@ -1881,7 +1881,7 @@ const EmitZIR = struct {
1881 } else if (typed_value.val.cast(Value.Payload.Variable)) |variable| {1881 } else if (typed_value.val.cast(Value.Payload.Variable)) |variable| {
1882 return self.emitTypedValue(src, .{1882 return self.emitTypedValue(src, .{
1883 .ty = typed_value.ty,1883 .ty = typed_value.ty,
1884 .val = variable.variable.value.?,1884 .val = variable.variable.init,
1885 });1885 });
1886 }1886 }
1887 if (typed_value.val.isUndef()) {1887 if (typed_value.val.isUndef()) {
src-self-hosted/zir_sema.zig-1
...@@ -666,7 +666,6 @@ fn analyzeInstFn(mod: *Module, scope: *Scope, fn_inst: *zir.Inst.Fn) InnerError!...@@ -666,7 +666,6 @@ fn analyzeInstFn(mod: *Module, scope: *Scope, fn_inst: *zir.Inst.Fn) InnerError!
666 new_func.* = .{666 new_func.* = .{
667 .analysis = .{ .queued = fn_zir },667 .analysis = .{ .queued = fn_zir },
668 .owner_decl = scope.decl().?,668 .owner_decl = scope.decl().?,
669 .is_pub = false,
670 };669 };
671 const fn_payload = try scope.arena().create(Value.Payload.Function);670 const fn_payload = try scope.arena().create(Value.Payload.Function);
672 fn_payload.* = .{ .func = new_func };671 fn_payload.* = .{ .func = new_func };
test/stage2/compare_output.zig+2
...@@ -586,6 +586,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -586,6 +586,7 @@ pub fn addCases(ctx: *TestContext) !void {
586 "",586 "",
587 );587 );
588588
589 // Character literals and multiline strings.
589 case.addCompareOutput(590 case.addCompareOutput(
590 \\export fn _start() noreturn {591 \\export fn _start() noreturn {
591 \\ const ignore = 592 \\ const ignore =
...@@ -618,6 +619,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -618,6 +619,7 @@ pub fn addCases(ctx: *TestContext) !void {
618 "",619 "",
619 );620 );
620621
622 // Global const.
621 case.addCompareOutput(623 case.addCompareOutput(
622 \\export fn _start() noreturn {624 \\export fn _start() noreturn {
623 \\ add(aa, bb);625 \\ add(aa, bb);