aboutsummaryrefslogtreecommitdiff
path: root/src/Sema.zig
diff options
context:
space:
mode:
authorkcbanner <kcbanner@gmail.com>2023-10-21 12:03:36 -0400
committerMatthew Lugg <mlugg@mlugg.co.uk>2023-10-31 01:35:58 +0000
commit4d044ee7e0b1ca61b8f2205f318449780ae23bd2 (patch)
tree682b42b724238e7dc0231685eecaf0f408a1620e /src/Sema.zig
parentffaeb4533395fafcf020d0aca2f2efd58e48a384 (diff)
downloadzig-4d044ee7e0b1ca61b8f2205f318449780ae23bd2.tar.gz
zig-4d044ee7e0b1ca61b8f2205f318449780ae23bd2.zip
sema: Add union alignment resolution
- Add resolveUnionAlignment, to resolve a union's alignment only, without triggering layout resolution. - Update resolveUnionLayout to cache size, alignment, and padding. abiSizeAdvanced and abiAlignmentAdvanced now use this information instead of computing it each time.
Diffstat (limited to 'src/Sema.zig')
-rw-r--r--src/Sema.zig113
1 files changed, 104 insertions, 9 deletions
diff --git a/src/Sema.zig b/src/Sema.zig
index 51895dedb1..c83ef222c0 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -3200,6 +3200,7 @@ fn zirUnionDecl(
.any_aligned_fields = small.any_aligned_fields,
.requires_comptime = .unknown,
.assumed_runtime_bits = false,
+ .alignment = .none,
},
.decl = new_decl_index,
.namespace = new_namespace_index,
@@ -20988,6 +20989,7 @@ fn zirReify(
.any_aligned_fields = any_aligned_fields,
.requires_comptime = .unknown,
.assumed_runtime_bits = false,
+ .alignment = .none,
},
.field_types = union_fields.items(.type),
.field_aligns = if (any_aligned_fields) union_fields.items(.alignment) else &.{},
@@ -34921,11 +34923,56 @@ fn checkMemOperand(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void
return sema.failWithOwnedErrorMsg(block, msg);
}
+/// Resolve a unions's alignment only without triggering resolution of its layout.
+/// Asserts that the alignment is not yet resolved.
+pub fn resolveUnionAlignment(
+ sema: *Sema,
+ ty: Type,
+ union_type: InternPool.Key.UnionType,
+) CompileError!Alignment {
+ const mod = sema.mod;
+ const ip = &mod.intern_pool;
+ const target = mod.getTarget();
+
+ assert(!union_type.haveLayout(ip));
+
+ if (union_type.flagsPtr(ip).status == .field_types_wip) {
+ // We'll guess "pointer-aligned", if the union has an
+ // underaligned pointer field then some allocations
+ // might require explicit alignment.
+ return Alignment.fromByteUnits(@divExact(target.ptrBitWidth(), 8));
+ }
+
+ try sema.resolveTypeFieldsUnion(ty, union_type);
+
+ const union_obj = ip.loadUnionType(union_type);
+ var max_align: Alignment = .@"1";
+ for (0..union_obj.field_names.len) |field_index| {
+ const field_ty = union_obj.field_types.get(ip)[field_index].toType();
+ if (!(try sema.typeHasRuntimeBits(field_ty))) continue;
+
+ const explicit_align = union_obj.fieldAlign(ip, @intCast(field_index));
+ const field_align = if (explicit_align != .none)
+ explicit_align
+ else
+ try sema.typeAbiAlignment(field_ty);
+
+ max_align = max_align.max(field_align);
+ }
+
+ union_type.flagsPtr(ip).alignment = max_align;
+ return max_align;
+}
+
+/// This logic must be kept in sync with `Module.getUnionLayout`.
fn resolveUnionLayout(sema: *Sema, ty: Type) CompileError!void {
const mod = sema.mod;
const ip = &mod.intern_pool;
- try sema.resolveTypeFields(ty);
- const union_obj = mod.typeToUnion(ty).?;
+
+ const union_type = ip.indexToKey(ty.ip_index).union_type;
+ try sema.resolveTypeFieldsUnion(ty, union_type);
+
+ const union_obj = ip.loadUnionType(union_type);
switch (union_obj.flagsPtr(ip).status) {
.none, .have_field_types => {},
.field_types_wip, .layout_wip => {
@@ -34939,25 +34986,74 @@ fn resolveUnionLayout(sema: *Sema, ty: Type) CompileError!void {
},
.have_layout, .fully_resolved_wip, .fully_resolved => return,
}
+
const prev_status = union_obj.flagsPtr(ip).status;
errdefer if (union_obj.flagsPtr(ip).status == .layout_wip) {
union_obj.flagsPtr(ip).status = prev_status;
};
union_obj.flagsPtr(ip).status = .layout_wip;
- for (0..union_obj.field_types.len) |field_index| {
+
+ var max_size: u64 = 0;
+ var max_align: Alignment = .@"1";
+ for (0..union_obj.field_names.len) |field_index| {
const field_ty = union_obj.field_types.get(ip)[field_index].toType();
- sema.resolveTypeLayout(field_ty) catch |err| switch (err) {
+ if (!(try sema.typeHasRuntimeBits(field_ty))) continue;
+
+ max_size = @max(max_size, sema.typeAbiSize(field_ty) catch |err| switch (err) {
error.AnalysisFail => {
const msg = sema.err orelse return err;
try sema.addFieldErrNote(ty, field_index, msg, "while checking this field", .{});
return err;
},
else => return err,
- };
- }
- union_obj.flagsPtr(ip).status = .have_layout;
- _ = try sema.typeRequiresComptime(ty);
+ });
+
+ const explicit_align = union_obj.fieldAlign(ip, @intCast(field_index));
+ const field_align = if (explicit_align != .none)
+ explicit_align
+ else
+ try sema.typeAbiAlignment(field_ty);
+
+ max_align = max_align.max(field_align);
+ }
+
+ const flags = union_obj.flagsPtr(ip);
+ const has_runtime_tag = flags.runtime_tag.hasTag() and try sema.typeHasRuntimeBits(union_obj.enum_tag_ty.toType());
+ const size, const alignment, const padding = if (has_runtime_tag) layout: {
+ const enum_tag_type = union_obj.enum_tag_ty.toType();
+ const tag_align = try sema.typeAbiAlignment(enum_tag_type);
+ const tag_size = try sema.typeAbiSize(enum_tag_type);
+
+ // Put the tag before or after the payload depending on which one's
+ // alignment is greater.
+ var size: u64 = 0;
+ var padding: u32 = 0;
+ if (tag_align.compare(.gte, max_align)) {
+ // {Tag, Payload}
+ size += tag_size;
+ size = max_align.forward(size);
+ size += max_size;
+ const prev_size = size;
+ size = tag_align.forward(size);
+ padding = @intCast(size - prev_size);
+ } else {
+ // {Payload, Tag}
+ size += max_size;
+ size = tag_align.forward(size);
+ size += tag_size;
+ const prev_size = size;
+ size = max_align.forward(size);
+ padding = @intCast(size - prev_size);
+ }
+
+ break :layout .{ size, max_align.max(tag_align), padding };
+ } else .{ max_align.forward(max_size), max_align, 0 };
+
+ union_type.size(ip).* = @intCast(size);
+ union_type.padding(ip).* = padding;
+ flags.alignment = alignment;
+ flags.status = .have_layout;
if (union_obj.flagsPtr(ip).assumed_runtime_bits and !(try sema.typeHasRuntimeBits(ty))) {
const msg = try Module.ErrorMsg.create(
@@ -35034,7 +35130,6 @@ fn resolveStructFully(sema: *Sema, ty: Type) CompileError!void {
fn resolveUnionFully(sema: *Sema, ty: Type) CompileError!void {
try sema.resolveUnionLayout(ty);
- try sema.resolveTypeFields(ty);
const mod = sema.mod;
const ip = &mod.intern_pool;