authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-04-29 16:57:13-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-04-29 16:57:13-07:00
log55e86b724a2ce60b777bd94d1203f243435727b7
treecf799707230163746c9642c04a6545fd96a57657
parentfb4cb430e096d3142eeedb129425b01e80442516

AstGen: implement comptime struct fields


5 files changed, 50 insertions(+), 19 deletions(-)

BRANCH_TODO+4
...@@ -52,6 +52,10 @@...@@ -52,6 +52,10 @@
52 - not sure why this happened, it's stage1 code??52 - not sure why this happened, it's stage1 code??
53 - search the behavior test diff for "TODO"53 - search the behavior test diff for "TODO"
5454
55 * memory efficiency: add another representation for structs which use
56 natural alignment for fields and do not have any comptime fields. this
57 will save 16 bytes per struct field in the compilation.
58
55fn getAnonTypeName(mod: *Module, scope: *Scope, base_token: std.zig.ast.TokenIndex) ![]u8 {59fn getAnonTypeName(mod: *Module, scope: *Scope, base_token: std.zig.ast.TokenIndex) ![]u8 {
56 // TODO add namespaces, generic function signatrues60 // TODO add namespaces, generic function signatrues
57 const tree = scope.tree();61 const tree = scope.tree();
src/AstGen.zig+16-11
...@@ -3220,7 +3220,9 @@ fn structDeclInner(...@@ -3220,7 +3220,9 @@ fn structDeclInner(
3220 var fields_data = ArrayListUnmanaged(u32){};3220 var fields_data = ArrayListUnmanaged(u32){};
3221 defer fields_data.deinit(gpa);3221 defer fields_data.deinit(gpa);
32223222
3223 // We only need this if there are greater than 16 fields.3223 const bits_per_field = 4;
3224 const fields_per_u32 = 32 / bits_per_field;
3225 // We only need this if there are greater than fields_per_u32 fields.
3224 var bit_bag = ArrayListUnmanaged(u32){};3226 var bit_bag = ArrayListUnmanaged(u32){};
3225 defer bit_bag.deinit(gpa);3227 defer bit_bag.deinit(gpa);
32263228
...@@ -3307,13 +3309,10 @@ fn structDeclInner(...@@ -3307,13 +3309,10 @@ fn structDeclInner(
3307 },3309 },
3308 else => unreachable,3310 else => unreachable,
3309 };3311 };
3310 if (field_index % 16 == 0 and field_index != 0) {3312 if (field_index % fields_per_u32 == 0 and field_index != 0) {
3311 try bit_bag.append(gpa, cur_bit_bag);3313 try bit_bag.append(gpa, cur_bit_bag);
3312 cur_bit_bag = 0;3314 cur_bit_bag = 0;
3313 }3315 }
3314 if (member.comptime_token) |comptime_token| {
3315 return astgen.failTok(comptime_token, "TODO implement comptime struct fields", .{});
3316 }
3317 try fields_data.ensureUnusedCapacity(gpa, 4);3316 try fields_data.ensureUnusedCapacity(gpa, 4);
33183317
3319 const field_name = try gz.identAsString(member.ast.name_token);3318 const field_name = try gz.identAsString(member.ast.name_token);
...@@ -3324,9 +3323,13 @@ fn structDeclInner(...@@ -3324,9 +3323,13 @@ fn structDeclInner(
33243323
3325 const have_align = member.ast.align_expr != 0;3324 const have_align = member.ast.align_expr != 0;
3326 const have_value = member.ast.value_expr != 0;3325 const have_value = member.ast.value_expr != 0;
3327 cur_bit_bag = (cur_bit_bag >> 2) |3326 const is_comptime = member.comptime_token != null;
3328 (@as(u32, @boolToInt(have_align)) << 30) |3327 const unused = false;
3329 (@as(u32, @boolToInt(have_value)) << 31);3328 cur_bit_bag = (cur_bit_bag >> bits_per_field) |
3329 (@as(u32, @boolToInt(have_align)) << 28) |
3330 (@as(u32, @boolToInt(have_value)) << 29) |
3331 (@as(u32, @boolToInt(is_comptime)) << 30) |
3332 (@as(u32, @boolToInt(unused)) << 31);
33303333
3331 if (have_align) {3334 if (have_align) {
3332 const align_inst = try expr(&block_scope, &block_scope.base, align_rl, member.ast.align_expr);3335 const align_inst = try expr(&block_scope, &block_scope.base, align_rl, member.ast.align_expr);
...@@ -3335,14 +3338,16 @@ fn structDeclInner(...@@ -3335,14 +3338,16 @@ fn structDeclInner(
3335 if (have_value) {3338 if (have_value) {
3336 const default_inst = try expr(&block_scope, &block_scope.base, .{ .ty = field_type }, member.ast.value_expr);3339 const default_inst = try expr(&block_scope, &block_scope.base, .{ .ty = field_type }, member.ast.value_expr);
3337 fields_data.appendAssumeCapacity(@enumToInt(default_inst));3340 fields_data.appendAssumeCapacity(@enumToInt(default_inst));
3341 } else if (member.comptime_token) |comptime_token| {
3342 return astgen.failTok(comptime_token, "comptime field without default initialization value", .{});
3338 }3343 }
33393344
3340 field_index += 1;3345 field_index += 1;
3341 }3346 }
3342 {3347 {
3343 const empty_slot_count = 16 - (field_index % 16);3348 const empty_slot_count = fields_per_u32 - (field_index % fields_per_u32);
3344 if (empty_slot_count < 16) {3349 if (empty_slot_count < fields_per_u32) {
3345 cur_bit_bag >>= @intCast(u5, empty_slot_count * 2);3350 cur_bit_bag >>= @intCast(u5, empty_slot_count * bits_per_field);
3346 }3351 }
3347 }3352 }
3348 {3353 {
src/Module.zig+1
...@@ -471,6 +471,7 @@ pub const Struct = struct {...@@ -471,6 +471,7 @@ pub const Struct = struct {
471 abi_align: Value,471 abi_align: Value,
472 /// Uses `unreachable_value` to indicate no default.472 /// Uses `unreachable_value` to indicate no default.
473 default_val: Value,473 default_val: Value,
474 is_comptime: bool,
474 };475 };
475476
476 pub fn getFullyQualifiedName(s: *Struct, gpa: *Allocator) ![]u8 {477 pub fn getFullyQualifiedName(s: *Struct, gpa: *Allocator) ![]u8 {
src/Sema.zig+11-2
...@@ -719,14 +719,16 @@ pub fn zirStructDecl(...@@ -719,14 +719,16 @@ pub fn zirStructDecl(
719 sema.branch_count = struct_sema.branch_count;719 sema.branch_count = struct_sema.branch_count;
720 sema.branch_quota = struct_sema.branch_quota;720 sema.branch_quota = struct_sema.branch_quota;
721 }721 }
722 const bit_bags_count = std.math.divCeil(usize, fields_len, 16) catch unreachable;722 const bits_per_field = 4;
723 const fields_per_u32 = 32 / bits_per_field;
724 const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable;
723 const body_end = extra_index + body.len;725 const body_end = extra_index + body.len;
724 extra_index += bit_bags_count;726 extra_index += bit_bags_count;
725 var bit_bag_index: usize = body_end;727 var bit_bag_index: usize = body_end;
726 var cur_bit_bag: u32 = undefined;728 var cur_bit_bag: u32 = undefined;
727 var field_i: u32 = 0;729 var field_i: u32 = 0;
728 while (field_i < fields_len) : (field_i += 1) {730 while (field_i < fields_len) : (field_i += 1) {
729 if (field_i % 16 == 0) {731 if (field_i % fields_per_u32 == 0) {
730 cur_bit_bag = sema.code.extra[bit_bag_index];732 cur_bit_bag = sema.code.extra[bit_bag_index];
731 bit_bag_index += 1;733 bit_bag_index += 1;
732 }734 }
...@@ -734,6 +736,12 @@ pub fn zirStructDecl(...@@ -734,6 +736,12 @@ pub fn zirStructDecl(
734 cur_bit_bag >>= 1;736 cur_bit_bag >>= 1;
735 const has_default = @truncate(u1, cur_bit_bag) != 0;737 const has_default = @truncate(u1, cur_bit_bag) != 0;
736 cur_bit_bag >>= 1;738 cur_bit_bag >>= 1;
739 const is_comptime = @truncate(u1, cur_bit_bag) != 0;
740 cur_bit_bag >>= 1;
741 const unused = @truncate(u1, cur_bit_bag) != 0;
742 cur_bit_bag >>= 1;
743
744 _ = unused;
737745
738 const field_name_zir = sema.code.nullTerminatedString(sema.code.extra[extra_index]);746 const field_name_zir = sema.code.nullTerminatedString(sema.code.extra[extra_index]);
739 extra_index += 1;747 extra_index += 1;
...@@ -753,6 +761,7 @@ pub fn zirStructDecl(...@@ -753,6 +761,7 @@ pub fn zirStructDecl(
753 .ty = field_ty,761 .ty = field_ty,
754 .abi_align = Value.initTag(.abi_align_default),762 .abi_align = Value.initTag(.abi_align_default),
755 .default_val = Value.initTag(.unreachable_value),763 .default_val = Value.initTag(.unreachable_value),
764 .is_comptime = is_comptime,
756 };765 };
757766
758 if (has_align) {767 if (has_align) {
src/Zir.zig+18-6
...@@ -2387,13 +2387,16 @@ pub const Inst = struct {...@@ -2387,13 +2387,16 @@ pub const Inst = struct {
2387 /// link_section: Ref, // if corresponding bit is set2387 /// link_section: Ref, // if corresponding bit is set
2388 /// }2388 /// }
2389 /// 2. inst: Index // for every body_len2389 /// 2. inst: Index // for every body_len
2390 /// 3. has_bits: u32 // for every 16 fields2390 /// 3. flags: u32 // for every 8 fields
2391 /// - sets of 2 bits:2391 /// - sets of 4 bits:
2392 /// 0b0X: whether corresponding field has an align expression2392 /// 0b000X: whether corresponding field has an align expression
2393 /// 0bX0: whether corresponding field has a default expression2393 /// 0b00X0: whether corresponding field has a default expression
2394 /// 0b0X00: whether corresponding field is comptime
2395 /// 0bX000: unused
2394 /// 4. fields: { // for every fields_len2396 /// 4. fields: { // for every fields_len
2395 /// field_name: u32,2397 /// field_name: u32,
2396 /// field_type: Ref,2398 /// field_type: Ref,
2399 /// - if none, means `anytype`.
2397 /// align: Ref, // if corresponding bit is set2400 /// align: Ref, // if corresponding bit is set
2398 /// default_value: Ref, // if corresponding bit is set2401 /// default_value: Ref, // if corresponding bit is set
2399 /// }2402 /// }
...@@ -3394,14 +3397,16 @@ const Writer = struct {...@@ -3394,14 +3397,16 @@ const Writer = struct {
3394 try stream.writeAll("}, {\n");3397 try stream.writeAll("}, {\n");
3395 }3398 }
33963399
3397 const bit_bags_count = std.math.divCeil(usize, fields_len, 16) catch unreachable;3400 const bits_per_field = 4;
3401 const fields_per_u32 = 32 / bits_per_field;
3402 const bit_bags_count = std.math.divCeil(usize, fields_len, fields_per_u32) catch unreachable;
3398 const body_end = extra_index;3403 const body_end = extra_index;
3399 extra_index += bit_bags_count;3404 extra_index += bit_bags_count;
3400 var bit_bag_index: usize = body_end;3405 var bit_bag_index: usize = body_end;
3401 var cur_bit_bag: u32 = undefined;3406 var cur_bit_bag: u32 = undefined;
3402 var field_i: u32 = 0;3407 var field_i: u32 = 0;
3403 while (field_i < fields_len) : (field_i += 1) {3408 while (field_i < fields_len) : (field_i += 1) {
3404 if (field_i % 16 == 0) {3409 if (field_i % fields_per_u32 == 0) {
3405 cur_bit_bag = self.code.extra[bit_bag_index];3410 cur_bit_bag = self.code.extra[bit_bag_index];
3406 bit_bag_index += 1;3411 bit_bag_index += 1;
3407 }3412 }
...@@ -3409,6 +3414,12 @@ const Writer = struct {...@@ -3409,6 +3414,12 @@ const Writer = struct {
3409 cur_bit_bag >>= 1;3414 cur_bit_bag >>= 1;
3410 const has_default = @truncate(u1, cur_bit_bag) != 0;3415 const has_default = @truncate(u1, cur_bit_bag) != 0;
3411 cur_bit_bag >>= 1;3416 cur_bit_bag >>= 1;
3417 const is_comptime = @truncate(u1, cur_bit_bag) != 0;
3418 cur_bit_bag >>= 1;
3419 const unused = @truncate(u1, cur_bit_bag) != 0;
3420 cur_bit_bag >>= 1;
3421
3422 _ = unused;
34123423
3413 const field_name = self.code.nullTerminatedString(self.code.extra[extra_index]);3424 const field_name = self.code.nullTerminatedString(self.code.extra[extra_index]);
3414 extra_index += 1;3425 extra_index += 1;
...@@ -3416,6 +3427,7 @@ const Writer = struct {...@@ -3416,6 +3427,7 @@ const Writer = struct {
3416 extra_index += 1;3427 extra_index += 1;
34173428
3418 try stream.writeByteNTimes(' ', self.indent);3429 try stream.writeByteNTimes(' ', self.indent);
3430 try self.writeFlag(stream, "comptime ", is_comptime);
3419 try stream.print("{}: ", .{std.zig.fmtId(field_name)});3431 try stream.print("{}: ", .{std.zig.fmtId(field_name)});
3420 try self.writeInstRef(stream, field_type);3432 try self.writeInstRef(stream, field_type);
34213433