authorgravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2023-09-17 20:25:32+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-09-23 12:36:56-07:00
log749307dbb260886433f366b3fdf9baf8f5b2ae30
treeff2ec5fa6d7f69539bc9d0c7429d05bfae179f73
parent98046b4c3c09d3d115b38a943fce328a2dc20dc4

spirv: air union_init


2 files changed, 120 insertions(+), 71 deletions(-)

src/codegen/spirv.zig+120-70
......@@ -801,77 +801,14 @@ pub const DeclGen = struct {
801801 else => unreachable,
802802 },
803803 .un => |un| {
804 // To initialize a union, generate a temporary variable with the
805 // type that has the right field active, then pointer-cast and store
806 // the active field, and finally load and return the entire union.
807
808 const union_ty = mod.typeToUnion(ty).?;
809
810 if (union_ty.getLayout(ip) == .Packed) {
811 return self.todo("packed union types", .{});
812 }
813
814804 const active_field = ty.unionTagFieldIndex(un.tag.toValue(), mod).?;
815805 const layout = self.unionLayout(ty, active_field);
806 const payload = if (layout.active_field_size != 0)
807 try self.constant(layout.active_field_ty, un.val.toValue(), .indirect)
808 else
809 null;
816810
817 if (layout.payload_size == 0) {
818 // No payload, so represent this as just the tag type.
819 return try self.constant(ty.unionTagTypeSafety(mod).?, un.tag.toValue(), .indirect);
820 }
821
822 const un_active_ty_ref = try self.resolveUnionType(ty, active_field);
823 const un_active_ptr_ty_ref = try self.spv.ptrType(un_active_ty_ref, .Function);
824 const un_general_ptr_ty_ref = try self.spv.ptrType(result_ty_ref, .Function);
825
826 const var_id = self.spv.allocId();
827 try self.func.prologue.emit(self.spv.gpa, .OpVariable, .{
828 .id_result_type = self.typeId(un_active_ptr_ty_ref),
829 .id_result = var_id,
830 .storage_class = .Function,
831 });
832
833 if (layout.tag_size != 0) {
834 const tag_ty = ty.unionTagTypeSafety(mod).?;
835 const tag_ty_ref = try self.resolveType(tag_ty, .indirect);
836 const tag_ptr_ty_ref = try self.spv.ptrType(tag_ty_ref, .Function);
837 const ptr_id = try self.accessChain(tag_ptr_ty_ref, var_id, &.{@as(u32, @intCast(layout.tag_index))});
838 const tag_id = try self.constant(tag_ty, un.tag.toValue(), .indirect);
839 try self.func.body.emit(self.spv.gpa, .OpStore, .{
840 .pointer = ptr_id,
841 .object = tag_id,
842 });
843 }
844
845 if (layout.active_field_size != 0) {
846 const active_field_ty_ref = try self.resolveType(layout.active_field_ty, .indirect);
847 const active_field_ptr_ty_ref = try self.spv.ptrType(active_field_ty_ref, .Function);
848 const ptr_id = try self.accessChain(active_field_ptr_ty_ref, var_id, &.{@as(u32, @intCast(layout.active_field_index))});
849 const value_id = try self.constant(layout.active_field_ty, un.val.toValue(), .indirect);
850 try self.func.body.emit(self.spv.gpa, .OpStore, .{
851 .pointer = ptr_id,
852 .object = value_id,
853 });
854 }
855
856 // Just leave the padding fields uninitialized...
857 // TODO: Or should we initialize them with undef explicitly?
858
859 // Now cast the pointer and load it as the 'generic' union type.
860
861 const casted_var_id = self.spv.allocId();
862 try self.func.body.emit(self.spv.gpa, .OpBitcast, .{
863 .id_result_type = self.typeId(un_general_ptr_ty_ref),
864 .id_result = casted_var_id,
865 .operand = var_id,
866 });
867
868 const result_id = self.spv.allocId();
869 try self.func.body.emit(self.spv.gpa, .OpLoad, .{
870 .id_result_type = self.typeId(result_ty_ref),
871 .id_result = result_id,
872 .pointer = casted_var_id,
873 });
874 return result_id;
811 return try self.unionInit(ty, active_field, payload);
875812 },
876813 .memoized_call => unreachable,
877814 }
......@@ -1752,6 +1689,8 @@ pub const DeclGen = struct {
17521689
17531690 .set_union_tag => return try self.airSetUnionTag(inst),
17541691 .get_union_tag => try self.airGetUnionTag(inst),
1692 .union_init => try self.airUnionInit(inst),
1693
17551694 .struct_field_val => try self.airStructFieldVal(inst),
17561695
17571696 .struct_field_ptr_index_0 => try self.airStructFieldPtrIndex(inst, 0),
......@@ -2573,8 +2512,12 @@ pub const DeclGen = struct {
25732512 const union_ptr_id = try self.resolve(bin_op.lhs);
25742513 const new_tag_id = try self.resolve(bin_op.rhs);
25752514
2576 const ptr_id = try self.accessChain(tag_ptr_ty_ref, union_ptr_id, &.{layout.tag_index});
2577 try self.store(tag_ty, ptr_id, new_tag_id, un_ptr_ty.isVolatilePtr(mod));
2515 if (layout.payload_size == 0) {
2516 try self.store(tag_ty, union_ptr_id, new_tag_id, un_ptr_ty.isVolatilePtr(mod));
2517 } else {
2518 const ptr_id = try self.accessChain(tag_ptr_ty_ref, union_ptr_id, &.{layout.tag_index});
2519 try self.store(tag_ty, ptr_id, new_tag_id, un_ptr_ty.isVolatilePtr(mod));
2520 }
25782521 }
25792522
25802523 fn airGetUnionTag(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
......@@ -2594,6 +2537,113 @@ pub const DeclGen = struct {
25942537 return try self.extractField(tag_ty, union_handle, layout.tag_index);
25952538 }
25962539
2540 fn unionInit(
2541 self: *DeclGen,
2542 ty: Type,
2543 active_field: u32,
2544 payload: ?IdRef,
2545 ) !IdRef {
2546 // To initialize a union, generate a temporary variable with the
2547 // type that has the right field active, then pointer-cast and store
2548 // the active field, and finally load and return the entire union.
2549
2550 const mod = self.module;
2551 const ip = &mod.intern_pool;
2552 const union_ty = mod.typeToUnion(ty).?;
2553
2554 if (union_ty.getLayout(ip) == .Packed) {
2555 unreachable; // TODO
2556 }
2557
2558 const maybe_tag_ty = ty.unionTagTypeSafety(mod);
2559 const layout = self.unionLayout(ty, active_field);
2560
2561 const tag_int = if (layout.tag_size != 0) blk: {
2562 const tag_ty = maybe_tag_ty.?;
2563 const union_field_name = union_ty.field_names.get(ip)[active_field];
2564 const enum_field_index = tag_ty.enumFieldIndex(union_field_name, mod).?;
2565 const tag_val = try mod.enumValueFieldIndex(tag_ty, enum_field_index);
2566 const tag_int_val = try tag_val.intFromEnum(tag_ty, mod);
2567 break :blk tag_int_val.toUnsignedInt(mod);
2568 } else 0;
2569
2570 if (layout.payload_size == 0) {
2571 const tag_ty_ref = try self.resolveType(maybe_tag_ty.?, .direct);
2572 return try self.constInt(tag_ty_ref, tag_int);
2573 }
2574
2575 const un_active_ty_ref = try self.resolveUnionType(ty, active_field);
2576 const un_active_ptr_ty_ref = try self.spv.ptrType(un_active_ty_ref, .Function);
2577 const un_general_ty_ref = try self.resolveType(ty, .direct);
2578 const un_general_ptr_ty_ref = try self.spv.ptrType(un_general_ty_ref, .Function);
2579
2580 const tmp_id = self.spv.allocId();
2581 try self.func.prologue.emit(self.spv.gpa, .OpVariable, .{
2582 .id_result_type = self.typeId(un_active_ptr_ty_ref),
2583 .id_result = tmp_id,
2584 .storage_class = .Function,
2585 });
2586
2587 if (layout.tag_size != 0) {
2588 const tag_ty_ref = try self.resolveType(maybe_tag_ty.?, .direct);
2589 const tag_ptr_ty_ref = try self.spv.ptrType(tag_ty_ref, .Function);
2590 const ptr_id = try self.accessChain(tag_ptr_ty_ref, tmp_id, &.{@as(u32, @intCast(layout.tag_index))});
2591 const tag_id = try self.constInt(tag_ty_ref, tag_int);
2592 try self.func.body.emit(self.spv.gpa, .OpStore, .{
2593 .pointer = ptr_id,
2594 .object = tag_id,
2595 });
2596 }
2597
2598 if (layout.active_field_size != 0) {
2599 const active_field_ty_ref = try self.resolveType(layout.active_field_ty, .indirect);
2600 const active_field_ptr_ty_ref = try self.spv.ptrType(active_field_ty_ref, .Function);
2601 const ptr_id = try self.accessChain(active_field_ptr_ty_ref, tmp_id, &.{@as(u32, @intCast(layout.active_field_index))});
2602 try self.func.body.emit(self.spv.gpa, .OpStore, .{
2603 .pointer = ptr_id,
2604 .object = payload.?,
2605 });
2606 } else {
2607 assert(payload == null);
2608 }
2609
2610 // Just leave the padding fields uninitialized...
2611 // TODO: Or should we initialize them with undef explicitly?
2612
2613 // Now cast the pointer and load it as the 'generic' union type.
2614
2615 const casted_var_id = self.spv.allocId();
2616 try self.func.body.emit(self.spv.gpa, .OpBitcast, .{
2617 .id_result_type = self.typeId(un_general_ptr_ty_ref),
2618 .id_result = casted_var_id,
2619 .operand = tmp_id,
2620 });
2621
2622 const result_id = self.spv.allocId();
2623 try self.func.body.emit(self.spv.gpa, .OpLoad, .{
2624 .id_result_type = self.typeId(un_general_ty_ref),
2625 .id_result = result_id,
2626 .pointer = casted_var_id,
2627 });
2628
2629 return result_id;
2630 }
2631
2632 fn airUnionInit(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
2633 if (self.liveness.isUnused(inst)) return null;
2634
2635 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
2636 const extra = self.air.extraData(Air.UnionInit, ty_pl.payload).data;
2637 const ty = self.typeOfIndex(inst);
2638 const layout = self.unionLayout(ty, extra.field_index);
2639
2640 const payload = if (layout.active_field_size != 0)
2641 try self.resolve(extra.init)
2642 else
2643 null;
2644 return try self.unionInit(ty, extra.field_index, payload);
2645 }
2646
25972647 fn airStructFieldVal(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
25982648 if (self.liveness.isUnused(inst)) return null;
25992649
test/behavior/union.zig-1
......@@ -997,7 +997,6 @@ test "cast from pointer to anonymous struct to pointer to union" {
997997 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
998998 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
999999 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1000 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
10011000
10021001 const S = struct {
10031002 const U = union(enum) {