authorgravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2023-10-21 15:30:20+02:00
committergravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2023-10-21 17:46:53+02:00
log1deec09f03d07aefcff1f886085c929c93669b5d
treeb8ceeaa4aef003c3c701252f5b5d4238be8a748e
parent5090d75e48ba0a044997b93b0c5cf1f7dcec60f1
signaturebadge-check Signed by SSH key SHA256:CQ99aPxq+RueiL9u7z0FEki5Fm7V6T8q4PrEGmINrA4

spirv: improve union operations

This removes the strategy where union with different active fields would be generated, and instead simply pointer casts the active field type where required. This also allows removing spv.ptrType and using self.ptrType instead, and allows caching all union types (because there is only the canonical one).

3 files changed, 104 insertions(+), 137 deletions(-)

src/codegen/spirv.zig+104-126
...@@ -874,12 +874,12 @@ const DeclGen = struct {...@@ -874,12 +874,12 @@ const DeclGen = struct {
874 },874 },
875 .un => |un| {875 .un => |un| {
876 const active_field = ty.unionTagFieldIndex(un.tag.toValue(), mod).?;876 const active_field = ty.unionTagFieldIndex(un.tag.toValue(), mod).?;
877 const layout = self.unionLayout(ty, active_field);877 const union_obj = mod.typeToUnion(ty).?;
878 const payload = if (layout.active_field_size != 0)878 const field_ty = union_obj.field_types.get(ip)[active_field].toType();
879 try self.constant(layout.active_field_ty, un.val.toValue(), .direct)879 const payload = if (field_ty.hasRuntimeBitsIgnoreComptime(mod))
880 try self.constant(field_ty, un.val.toValue(), .direct)
880 else881 else
881 null;882 null;
882
883 return try self.unionInit(ty, active_field, payload);883 return try self.unionInit(ty, active_field, payload);
884 },884 },
885 .memoized_call => unreachable,885 .memoized_call => unreachable,
...@@ -1105,29 +1105,25 @@ const DeclGen = struct {...@@ -1105,29 +1105,25 @@ const DeclGen = struct {
1105 return try self.spv.ptrType(child_ty_ref, storage_class);1105 return try self.spv.ptrType(child_ty_ref, storage_class);
1106 }1106 }
11071107
1108 /// Generate a union type, optionally with a known field. If the tag alignment is greater1108 /// Generate a union type. Union types are always generated with the
1109 /// than that of the payload, a regular union (non-packed, with both tag and payload), will1109 /// most aligned field active. If the tag alignment is greater
1110 /// be generated as follows:1110 /// than that of the payload, a regular union (non-packed, with both tag and
1111 /// If the active field is known:1111 /// payload), will be generated as follows:
1112 /// struct {1112 /// struct {
1113 /// tag: TagType,1113 /// tag: TagType,
1114 /// payload: ActivePayloadType,1114 /// payload: MostAlignedFieldType,
1115 /// payload_padding: [payload_size - @sizeOf(ActivePayloadType)]u8,1115 /// payload_padding: [payload_size - @sizeOf(MostAlignedFieldType)]u8,
1116 /// padding: [padding_size]u8,1116 /// padding: [padding_size]u8,
1117 /// }1117 /// }
1118 /// If the payload alignment is greater than that of the tag:1118 /// If the payload alignment is greater than that of the tag:
1119 /// struct {1119 /// struct {
1120 /// payload: ActivePayloadType,1120 /// payload: MostAlignedFieldType,
1121 /// payload_padding: [payload_size - @sizeOf(ActivePayloadType)]u8,1121 /// payload_padding: [payload_size - @sizeOf(MostAlignedFieldType)]u8,
1122 /// tag: TagType,1122 /// tag: TagType,
1123 /// padding: [padding_size]u8,1123 /// padding: [padding_size]u8,
1124 /// }1124 /// }
1125 /// If the active payload is unknown, it will default back to the most aligned field. This is
1126 /// to make sure that the overal struct has the correct alignment in spir-v.
1127 /// If any of the fields' size is 0, it will be omitted.1125 /// If any of the fields' size is 0, it will be omitted.
1128 /// NOTE: When the active field is set to something other than the most aligned field, the1126 fn resolveUnionType(self: *DeclGen, ty: Type) !CacheRef {
1129 /// resulting struct will be *underaligned*.
1130 fn resolveUnionType(self: *DeclGen, ty: Type, maybe_active_field: ?usize) !CacheRef {
1131 const mod = self.module;1127 const mod = self.module;
1132 const ip = &mod.intern_pool;1128 const ip = &mod.intern_pool;
1133 const union_obj = mod.typeToUnion(ty).?;1129 const union_obj = mod.typeToUnion(ty).?;
...@@ -1136,17 +1132,13 @@ const DeclGen = struct {...@@ -1136,17 +1132,13 @@ const DeclGen = struct {
1136 return self.todo("packed union types", .{});1132 return self.todo("packed union types", .{});
1137 }1133 }
11381134
1139 const layout = self.unionLayout(ty, maybe_active_field);1135 const layout = self.unionLayout(ty);
11401136 if (!layout.has_payload) {
1141 if (layout.payload_size == 0) {
1142 // No payload, so represent this as just the tag type.1137 // No payload, so represent this as just the tag type.
1143 return try self.resolveType(union_obj.enum_tag_ty.toType(), .indirect);1138 return try self.resolveType(union_obj.enum_tag_ty.toType(), .indirect);
1144 }1139 }
11451140
1146 // TODO: We need to add the active field to the key, somehow.1141 if (self.type_map.get(ty.toIntern())) |info| return info.ty_ref;
1147 if (maybe_active_field == null) {
1148 if (self.type_map.get(ty.toIntern())) |info| return info.ty_ref;
1149 }
11501142
1151 var member_types: [4]CacheRef = undefined;1143 var member_types: [4]CacheRef = undefined;
1152 var member_names: [4]CacheString = undefined;1144 var member_names: [4]CacheString = undefined;
...@@ -1159,10 +1151,10 @@ const DeclGen = struct {...@@ -1159,10 +1151,10 @@ const DeclGen = struct {
1159 member_names[layout.tag_index] = try self.spv.resolveString("(tag)");1151 member_names[layout.tag_index] = try self.spv.resolveString("(tag)");
1160 }1152 }
11611153
1162 if (layout.active_field_size != 0) {1154 if (layout.payload_size != 0) {
1163 const active_payload_ty_ref = try self.resolveType(layout.active_field_ty, .indirect);1155 const payload_ty_ref = try self.resolveType(layout.payload_ty, .indirect);
1164 member_types[layout.active_field_index] = active_payload_ty_ref;1156 member_types[layout.payload_index] = payload_ty_ref;
1165 member_names[layout.active_field_index] = try self.spv.resolveString("(payload)");1157 member_names[layout.payload_index] = try self.spv.resolveString("(payload)");
1166 }1158 }
11671159
1168 if (layout.payload_padding_size != 0) {1160 if (layout.payload_padding_size != 0) {
...@@ -1183,9 +1175,7 @@ const DeclGen = struct {...@@ -1183,9 +1175,7 @@ const DeclGen = struct {
1183 .member_names = member_names[0..layout.total_fields],1175 .member_names = member_names[0..layout.total_fields],
1184 } });1176 } });
11851177
1186 if (maybe_active_field == null) {1178 try self.type_map.put(self.gpa, ty.toIntern(), .{ .ty_ref = ty_ref });
1187 try self.type_map.put(self.gpa, ty.toIntern(), .{ .ty_ref = ty_ref });
1188 }
1189 return ty_ref;1179 return ty_ref;
1190 }1180 }
11911181
...@@ -1453,7 +1443,7 @@ const DeclGen = struct {...@@ -1453,7 +1443,7 @@ const DeclGen = struct {
1453 try self.type_map.put(self.gpa, ty.toIntern(), .{ .ty_ref = ty_ref });1443 try self.type_map.put(self.gpa, ty.toIntern(), .{ .ty_ref = ty_ref });
1454 return ty_ref;1444 return ty_ref;
1455 },1445 },
1456 .Union => return try self.resolveUnionType(ty, null),1446 .Union => return try self.resolveUnionType(ty),
1457 .ErrorSet => return try self.intType(.unsigned, 16),1447 .ErrorSet => return try self.intType(.unsigned, 16),
1458 .ErrorUnion => {1448 .ErrorUnion => {
1459 const payload_ty = ty.errorUnionPayload(mod);1449 const payload_ty = ty.errorUnionPayload(mod);
...@@ -1567,14 +1557,16 @@ const DeclGen = struct {...@@ -1567,14 +1557,16 @@ const DeclGen = struct {
1567 }1557 }
15681558
1569 const UnionLayout = struct {1559 const UnionLayout = struct {
1570 active_field: u32,1560 /// If false, this union is represented
1571 active_field_ty: Type,1561 /// by only an integer of the tag type.
1572 payload_size: u32,1562 has_payload: bool,
1573
1574 tag_size: u32,1563 tag_size: u32,
1575 tag_index: u32,1564 tag_index: u32,
1576 active_field_size: u32,1565 /// Note: This is the size of the payload type itself, NOT the size of the ENTIRE payload.
1577 active_field_index: u32,1566 /// Use `has_payload` instead!!
1567 payload_ty: Type,
1568 payload_size: u32,
1569 payload_index: u32,
1578 payload_padding_size: u32,1570 payload_padding_size: u32,
1579 payload_padding_index: u32,1571 payload_padding_index: u32,
1580 padding_size: u32,1572 padding_size: u32,
...@@ -1582,23 +1574,19 @@ const DeclGen = struct {...@@ -1582,23 +1574,19 @@ const DeclGen = struct {
1582 total_fields: u32,1574 total_fields: u32,
1583 };1575 };
15841576
1585 fn unionLayout(self: *DeclGen, ty: Type, maybe_active_field: ?usize) UnionLayout {1577 fn unionLayout(self: *DeclGen, ty: Type) UnionLayout {
1586 const mod = self.module;1578 const mod = self.module;
1587 const ip = &mod.intern_pool;1579 const ip = &mod.intern_pool;
1588 const layout = ty.unionGetLayout(self.module);1580 const layout = ty.unionGetLayout(self.module);
1589 const union_obj = mod.typeToUnion(ty).?;1581 const union_obj = mod.typeToUnion(ty).?;
15901582
1591 const active_field = maybe_active_field orelse layout.most_aligned_field;
1592 const active_field_ty = union_obj.field_types.get(ip)[active_field].toType();
1593
1594 var union_layout = UnionLayout{1583 var union_layout = UnionLayout{
1595 .active_field = @intCast(active_field),1584 .has_payload = layout.payload_size != 0,
1596 .active_field_ty = active_field_ty,
1597 .payload_size = @intCast(layout.payload_size),
1598 .tag_size = @intCast(layout.tag_size),1585 .tag_size = @intCast(layout.tag_size),
1599 .tag_index = undefined,1586 .tag_index = undefined,
1600 .active_field_size = undefined,1587 .payload_ty = undefined,
1601 .active_field_index = undefined,1588 .payload_size = undefined,
1589 .payload_index = undefined,
1602 .payload_padding_size = undefined,1590 .payload_padding_size = undefined,
1603 .payload_padding_index = undefined,1591 .payload_padding_index = undefined,
1604 .padding_size = @intCast(layout.padding),1592 .padding_size = @intCast(layout.padding),
...@@ -1606,11 +1594,16 @@ const DeclGen = struct {...@@ -1606,11 +1594,16 @@ const DeclGen = struct {
1606 .total_fields = undefined,1594 .total_fields = undefined,
1607 };1595 };
16081596
1609 union_layout.active_field_size = if (active_field_ty.hasRuntimeBitsIgnoreComptime(mod))1597 if (union_layout.has_payload) {
1610 @intCast(active_field_ty.abiSize(mod))1598 const most_aligned_field = layout.most_aligned_field;
1611 else1599 const most_aligned_field_ty = union_obj.field_types.get(ip)[most_aligned_field].toType();
1612 0;1600 union_layout.payload_ty = most_aligned_field_ty;
1613 union_layout.payload_padding_size = @intCast(layout.payload_size - union_layout.active_field_size);1601 union_layout.payload_size = @intCast(most_aligned_field_ty.abiSize(mod));
1602 } else {
1603 union_layout.payload_size = 0;
1604 }
1605
1606 union_layout.payload_padding_size = @intCast(layout.payload_size - union_layout.payload_size);
16141607
1615 const tag_first = layout.tag_align.compare(.gte, layout.payload_align);1608 const tag_first = layout.tag_align.compare(.gte, layout.payload_align);
1616 var field_index: u32 = 0;1609 var field_index: u32 = 0;
...@@ -1620,8 +1613,8 @@ const DeclGen = struct {...@@ -1620,8 +1613,8 @@ const DeclGen = struct {
1620 field_index += 1;1613 field_index += 1;
1621 }1614 }
16221615
1623 if (union_layout.active_field_size != 0) {1616 if (union_layout.payload_size != 0) {
1624 union_layout.active_field_index = field_index;1617 union_layout.payload_index = field_index;
1625 field_index += 1;1618 field_index += 1;
1626 }1619 }
16271620
...@@ -3300,7 +3293,7 @@ const DeclGen = struct {...@@ -3300,7 +3293,7 @@ const DeclGen = struct {
3300 const bin_op = self.air.instructions.items(.data)[inst].bin_op;3293 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
3301 const un_ptr_ty = self.typeOf(bin_op.lhs);3294 const un_ptr_ty = self.typeOf(bin_op.lhs);
3302 const un_ty = un_ptr_ty.childType(mod);3295 const un_ty = un_ptr_ty.childType(mod);
3303 const layout = self.unionLayout(un_ty, null);3296 const layout = self.unionLayout(un_ty);
33043297
3305 if (layout.tag_size == 0) return;3298 if (layout.tag_size == 0) return;
33063299
...@@ -3310,7 +3303,7 @@ const DeclGen = struct {...@@ -3310,7 +3303,7 @@ const DeclGen = struct {
3310 const union_ptr_id = try self.resolve(bin_op.lhs);3303 const union_ptr_id = try self.resolve(bin_op.lhs);
3311 const new_tag_id = try self.resolve(bin_op.rhs);3304 const new_tag_id = try self.resolve(bin_op.rhs);
33123305
3313 if (layout.payload_size == 0) {3306 if (!layout.has_payload) {
3314 try self.store(tag_ty, union_ptr_id, new_tag_id, .{ .is_volatile = un_ptr_ty.isVolatilePtr(mod) });3307 try self.store(tag_ty, union_ptr_id, new_tag_id, .{ .is_volatile = un_ptr_ty.isVolatilePtr(mod) });
3315 } else {3308 } else {
3316 const ptr_id = try self.accessChain(tag_ptr_ty_ref, union_ptr_id, &.{layout.tag_index});3309 const ptr_id = try self.accessChain(tag_ptr_ty_ref, union_ptr_id, &.{layout.tag_index});
...@@ -3325,11 +3318,11 @@ const DeclGen = struct {...@@ -3325,11 +3318,11 @@ const DeclGen = struct {
3325 const un_ty = self.typeOf(ty_op.operand);3318 const un_ty = self.typeOf(ty_op.operand);
33263319
3327 const mod = self.module;3320 const mod = self.module;
3328 const layout = self.unionLayout(un_ty, null);3321 const layout = self.unionLayout(un_ty);
3329 if (layout.tag_size == 0) return null;3322 if (layout.tag_size == 0) return null;
33303323
3331 const union_handle = try self.resolve(ty_op.operand);3324 const union_handle = try self.resolve(ty_op.operand);
3332 if (layout.payload_size == 0) return union_handle;3325 if (!layout.has_payload) return union_handle;
33333326
3334 const tag_ty = un_ty.unionTagTypeSafety(mod).?;3327 const tag_ty = un_ty.unionTagTypeSafety(mod).?;
3335 return try self.extractField(tag_ty, union_handle, layout.tag_index);3328 return try self.extractField(tag_ty, union_handle, layout.tag_index);
...@@ -3342,8 +3335,8 @@ const DeclGen = struct {...@@ -3342,8 +3335,8 @@ const DeclGen = struct {
3342 payload: ?IdRef,3335 payload: ?IdRef,
3343 ) !IdRef {3336 ) !IdRef {
3344 // To initialize a union, generate a temporary variable with the3337 // To initialize a union, generate a temporary variable with the
3345 // type that has the right field active, then pointer-cast and store3338 // union type, then get the field pointer and pointer-cast it to the
3346 // the active field, and finally load and return the entire union.3339 // right type to store it. Finally load the entire union.
33473340
3348 const mod = self.module;3341 const mod = self.module;
3349 const ip = &mod.intern_pool;3342 const ip = &mod.intern_pool;
...@@ -3354,7 +3347,7 @@ const DeclGen = struct {...@@ -3354,7 +3347,7 @@ const DeclGen = struct {
3354 }3347 }
33553348
3356 const maybe_tag_ty = ty.unionTagTypeSafety(mod);3349 const maybe_tag_ty = ty.unionTagTypeSafety(mod);
3357 const layout = self.unionLayout(ty, active_field);3350 const layout = self.unionLayout(ty);
33583351
3359 const tag_int = if (layout.tag_size != 0) blk: {3352 const tag_int = if (layout.tag_size != 0) blk: {
3360 const tag_ty = maybe_tag_ty.?;3353 const tag_ty = maybe_tag_ty.?;
...@@ -3365,23 +3358,12 @@ const DeclGen = struct {...@@ -3365,23 +3358,12 @@ const DeclGen = struct {
3365 break :blk tag_int_val.toUnsignedInt(mod);3358 break :blk tag_int_val.toUnsignedInt(mod);
3366 } else 0;3359 } else 0;
33673360
3368 if (layout.payload_size == 0) {3361 if (!layout.has_payload) {
3369 const tag_ty_ref = try self.resolveType(maybe_tag_ty.?, .direct);3362 const tag_ty_ref = try self.resolveType(maybe_tag_ty.?, .direct);
3370 return try self.constInt(tag_ty_ref, tag_int);3363 return try self.constInt(tag_ty_ref, tag_int);
3371 }3364 }
33723365
3373 // TODO: Make this use self.ptrType3366 const tmp_id = try self.alloc(ty, .{ .storage_class = .Function });
3374 const un_active_ty_ref = try self.resolveUnionType(ty, active_field);
3375 const un_active_ptr_ty_ref = try self.spv.ptrType(un_active_ty_ref, .Function);
3376 const un_general_ty_ref = try self.resolveType(ty, .direct);
3377 const un_general_ptr_ty_ref = try self.spv.ptrType(un_general_ty_ref, .Function);
3378
3379 const tmp_id = self.spv.allocId();
3380 try self.func.prologue.emit(self.spv.gpa, .OpVariable, .{
3381 .id_result_type = self.typeId(un_active_ptr_ty_ref),
3382 .id_result = tmp_id,
3383 .storage_class = .Function,
3384 });
33853367
3386 if (layout.tag_size != 0) {3368 if (layout.tag_size != 0) {
3387 const tag_ty_ref = try self.resolveType(maybe_tag_ty.?, .direct);3369 const tag_ty_ref = try self.resolveType(maybe_tag_ty.?, .direct);
...@@ -3391,10 +3373,19 @@ const DeclGen = struct {...@@ -3391,10 +3373,19 @@ const DeclGen = struct {
3391 try self.store(maybe_tag_ty.?, ptr_id, tag_id, .{});3373 try self.store(maybe_tag_ty.?, ptr_id, tag_id, .{});
3392 }3374 }
33933375
3394 if (layout.active_field_size != 0) {3376 const payload_ty = union_ty.field_types.get(ip)[active_field].toType();
3395 const active_field_ptr_ty_ref = try self.ptrType(layout.active_field_ty, .Function);3377 if (payload_ty.hasRuntimeBitsIgnoreComptime(mod)) {
3396 const ptr_id = try self.accessChain(active_field_ptr_ty_ref, tmp_id, &.{@as(u32, @intCast(layout.active_field_index))});3378 const pl_ptr_ty_ref = try self.ptrType(layout.payload_ty, .Function);
3397 try self.store(layout.active_field_ty, ptr_id, payload.?, .{});3379 const pl_ptr_id = try self.accessChain(pl_ptr_ty_ref, tmp_id, &.{layout.payload_index});
3380 const active_pl_ptr_ty_ref = try self.ptrType(payload_ty, .Function);
3381 const active_pl_ptr_id = self.spv.allocId();
3382 try self.func.body.emit(self.spv.gpa, .OpBitcast, .{
3383 .id_result_type = self.typeId(active_pl_ptr_ty_ref),
3384 .id_result = active_pl_ptr_id,
3385 .operand = pl_ptr_id,
3386 });
3387
3388 try self.store(payload_ty, active_pl_ptr_id, payload.?, .{});
3398 } else {3389 } else {
3399 assert(payload == null);3390 assert(payload == null);
3400 }3391 }
...@@ -3402,34 +3393,21 @@ const DeclGen = struct {...@@ -3402,34 +3393,21 @@ const DeclGen = struct {
3402 // Just leave the padding fields uninitialized...3393 // Just leave the padding fields uninitialized...
3403 // TODO: Or should we initialize them with undef explicitly?3394 // TODO: Or should we initialize them with undef explicitly?
34043395
3405 // Now cast the pointer and load it as the 'generic' union type.3396 return try self.load(ty, tmp_id, .{});
3406
3407 const casted_var_id = self.spv.allocId();
3408 try self.func.body.emit(self.spv.gpa, .OpBitcast, .{
3409 .id_result_type = self.typeId(un_general_ptr_ty_ref),
3410 .id_result = casted_var_id,
3411 .operand = tmp_id,
3412 });
3413
3414 const result_id = self.spv.allocId();
3415 try self.func.body.emit(self.spv.gpa, .OpLoad, .{
3416 .id_result_type = self.typeId(un_general_ty_ref),
3417 .id_result = result_id,
3418 .pointer = casted_var_id,
3419 });
3420
3421 return result_id;
3422 }3397 }
34233398
3424 fn airUnionInit(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {3399 fn airUnionInit(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
3425 if (self.liveness.isUnused(inst)) return null;3400 if (self.liveness.isUnused(inst)) return null;
34263401
3402 const mod = self.module;
3403 const ip = &mod.intern_pool;
3427 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;3404 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
3428 const extra = self.air.extraData(Air.UnionInit, ty_pl.payload).data;3405 const extra = self.air.extraData(Air.UnionInit, ty_pl.payload).data;
3429 const ty = self.typeOfIndex(inst);3406 const ty = self.typeOfIndex(inst);
3430 const layout = self.unionLayout(ty, extra.field_index);
34313407
3432 const payload = if (layout.active_field_size != 0)3408 const union_obj = mod.typeToUnion(ty).?;
3409 const field_ty = union_obj.field_types.get(ip)[extra.field_index].toType();
3410 const payload = if (field_ty.hasRuntimeBitsIgnoreComptime(mod))
3433 try self.resolve(extra.init)3411 try self.resolve(extra.init)
3434 else3412 else
3435 null;3413 null;
...@@ -3458,30 +3436,24 @@ const DeclGen = struct {...@@ -3458,30 +3436,24 @@ const DeclGen = struct {
3458 .Union => switch (object_ty.containerLayout(mod)) {3436 .Union => switch (object_ty.containerLayout(mod)) {
3459 .Packed => unreachable, // TODO3437 .Packed => unreachable, // TODO
3460 else => {3438 else => {
3461 // Store, pointer-cast, load3439 // Store, ptr-elem-ptr, pointer-cast, load
3462 const un_general_ty_ref = try self.resolveType(object_ty, .indirect);3440 const layout = self.unionLayout(object_ty);
3463 const un_general_ptr_ty_ref = try self.spv.ptrType(un_general_ty_ref, .Function);3441 assert(layout.has_payload);
3464 const un_active_ty_ref = try self.resolveUnionType(object_ty, field_index);3442
3465 const un_active_ptr_ty_ref = try self.spv.ptrType(un_active_ty_ref, .Function);3443 const tmp_id = try self.alloc(object_ty, .{ .storage_class = .Function });
3466 const field_ty_ref = try self.resolveType(field_ty, .indirect);
3467 const field_ptr_ty_ref = try self.spv.ptrType(field_ty_ref, .Function);
3468
3469 const tmp_id = self.spv.allocId();
3470 try self.func.prologue.emit(self.spv.gpa, .OpVariable, .{
3471 .id_result_type = self.typeId(un_general_ptr_ty_ref),
3472 .id_result = tmp_id,
3473 .storage_class = .Function,
3474 });
3475 try self.store(object_ty, tmp_id, object_id, .{});3444 try self.store(object_ty, tmp_id, object_id, .{});
3476 const casted_tmp_id = self.spv.allocId();3445
3446 const pl_ptr_ty_ref = try self.ptrType(layout.payload_ty, .Function);
3447 const pl_ptr_id = try self.accessChain(pl_ptr_ty_ref, tmp_id, &.{layout.payload_index});
3448
3449 const active_pl_ptr_ty_ref = try self.ptrType(field_ty, .Function);
3450 const active_pl_ptr_id = self.spv.allocId();
3477 try self.func.body.emit(self.spv.gpa, .OpBitcast, .{3451 try self.func.body.emit(self.spv.gpa, .OpBitcast, .{
3478 .id_result_type = self.typeId(un_active_ptr_ty_ref),3452 .id_result_type = self.typeId(active_pl_ptr_ty_ref),
3479 .id_result = casted_tmp_id,3453 .id_result = active_pl_ptr_id,
3480 .operand = tmp_id,3454 .operand = pl_ptr_id,
3481 });3455 });
3482 const layout = self.unionLayout(object_ty, field_index);3456 return try self.load(field_ty, active_pl_ptr_id, .{});
3483 const field_ptr_id = try self.accessChain(field_ptr_ty_ref, casted_tmp_id, &.{layout.active_field_index});
3484 return try self.load(field_ty, field_ptr_id, .{});
3485 },3457 },
3486 },3458 },
3487 else => unreachable,3459 else => unreachable,
...@@ -3540,18 +3512,24 @@ const DeclGen = struct {...@@ -3540,18 +3512,24 @@ const DeclGen = struct {
3540 .Union => switch (object_ty.containerLayout(mod)) {3512 .Union => switch (object_ty.containerLayout(mod)) {
3541 .Packed => unreachable, // TODO3513 .Packed => unreachable, // TODO
3542 else => {3514 else => {
3515 const layout = self.unionLayout(object_ty);
3516 if (!layout.has_payload) {
3517 // Asked to get a pointer to a zero-sized field. Just lower this
3518 // to undefined, there is no reason to make it be a valid pointer.
3519 return try self.spv.constUndef(result_ty_ref);
3520 }
3521
3543 const storage_class = spvStorageClass(object_ptr_ty.ptrAddressSpace(mod));3522 const storage_class = spvStorageClass(object_ptr_ty.ptrAddressSpace(mod));
3544 const un_active_ty_ref = try self.resolveUnionType(object_ty, field_index);3523 const pl_ptr_ty_ref = try self.ptrType(layout.payload_ty, storage_class);
3545 const un_active_ptr_ty_ref = try self.spv.ptrType(un_active_ty_ref, storage_class);3524 const pl_ptr_id = try self.accessChain(pl_ptr_ty_ref, object_ptr, &.{layout.payload_index});
35463525
3547 const casted_id = self.spv.allocId();3526 const active_pl_ptr_id = self.spv.allocId();
3548 try self.func.body.emit(self.spv.gpa, .OpBitcast, .{3527 try self.func.body.emit(self.spv.gpa, .OpBitcast, .{
3549 .id_result_type = self.typeId(un_active_ptr_ty_ref),3528 .id_result_type = self.typeId(result_ty_ref),
3550 .id_result = casted_id,3529 .id_result = active_pl_ptr_id,
3551 .operand = object_ptr,3530 .operand = pl_ptr_id,
3552 });3531 });
3553 const layout = self.unionLayout(object_ty, field_index);3532 return active_pl_ptr_id;
3554 return try self.accessChain(result_ty_ref, casted_id, &.{layout.active_field_index});
3555 },3533 },
3556 },3534 },
3557 else => unreachable,3535 else => unreachable,
test/behavior/struct.zig-1
...@@ -736,7 +736,6 @@ test "packed struct with u0 field access" {...@@ -736,7 +736,6 @@ test "packed struct with u0 field access" {
736test "access to global struct fields" {736test "access to global struct fields" {
737 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO737 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
738 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO738 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
739 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
740739
741 g_foo.bar.value = 42;740 g_foo.bar.value = 42;
742 try expect(g_foo.bar.value == 42);741 try expect(g_foo.bar.value == 42);
test/behavior/union.zig-10
...@@ -399,7 +399,6 @@ test "tagged union with no payloads" {...@@ -399,7 +399,6 @@ test "tagged union with no payloads" {
399 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;399 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
400 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;400 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
401 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO401 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
402 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
403402
404 const a = UnionEnumNoPayloads{ .B = {} };403 const a = UnionEnumNoPayloads{ .B = {} };
405 switch (a) {404 switch (a) {
...@@ -474,7 +473,6 @@ test "update the tag value for zero-sized unions" {...@@ -474,7 +473,6 @@ test "update the tag value for zero-sized unions" {
474 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;473 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
475 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;474 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
476 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO475 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
477 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
478476
479 const S = union(enum) {477 const S = union(enum) {
480 U0: void,478 U0: void,
...@@ -515,7 +513,6 @@ test "method call on an empty union" {...@@ -515,7 +513,6 @@ test "method call on an empty union" {
515 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;513 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
516 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;514 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
517 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO515 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
518 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
519516
520 const S = struct {517 const S = struct {
521 const MyUnion = union(MyUnionTag) {518 const MyUnion = union(MyUnionTag) {
...@@ -593,7 +590,6 @@ test "tagged union with all void fields but a meaningful tag" {...@@ -593,7 +590,6 @@ test "tagged union with all void fields but a meaningful tag" {
593 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;590 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
594 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;591 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
595 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO592 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
596 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
597593
598 const S = struct {594 const S = struct {
599 const B = union(enum) {595 const B = union(enum) {
...@@ -795,7 +791,6 @@ test "@unionInit stored to a const" {...@@ -795,7 +791,6 @@ test "@unionInit stored to a const" {
795 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO791 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
796 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO792 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
797 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO793 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
798 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
799794
800 const S = struct {795 const S = struct {
801 const U = union(enum) {796 const U = union(enum) {
...@@ -867,7 +862,6 @@ test "union no tag with struct member" {...@@ -867,7 +862,6 @@ test "union no tag with struct member" {
867 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO862 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
868 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO863 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
869 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO864 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
870 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
871865
872 const Struct = struct {};866 const Struct = struct {};
873 const Union = union {867 const Union = union {
...@@ -1079,7 +1073,6 @@ test "@unionInit on union with tag but no fields" {...@@ -1079,7 +1073,6 @@ test "@unionInit on union with tag but no fields" {
1079 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO1073 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1080 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO1074 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1081 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO1075 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1082 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
10831076
1084 const S = struct {1077 const S = struct {
1085 const Type = enum(u8) { no_op = 105 };1078 const Type = enum(u8) { no_op = 105 };
...@@ -1128,7 +1121,6 @@ test "global variable struct contains union initialized to non-most-aligned fiel...@@ -1128,7 +1121,6 @@ test "global variable struct contains union initialized to non-most-aligned fiel
1128 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO1121 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1129 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO1122 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1130 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO1123 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1131 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
11321124
1133 const T = struct {1125 const T = struct {
1134 const U = union(enum) {1126 const U = union(enum) {
...@@ -1348,7 +1340,6 @@ test "union field ptr - zero sized payload" {...@@ -1348,7 +1340,6 @@ test "union field ptr - zero sized payload" {
1348 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO1340 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1349 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO1341 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1350 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO1342 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1351 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
13521343
1353 const U = union {1344 const U = union {
1354 foo: void,1345 foo: void,
...@@ -1363,7 +1354,6 @@ test "union field ptr - zero sized field" {...@@ -1363,7 +1354,6 @@ test "union field ptr - zero sized field" {
1363 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO1354 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1364 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO1355 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1365 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO1356 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1366 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
13671357
1368 const U = union {1358 const U = union {
1369 foo: void,1359 foo: void,