authorgravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2023-09-17 17:17:46+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-09-23 12:36:56-07:00
log5dffbf32bfb0c88163d2a116b6b0f9d802dce8cc
treefb64112e57ec921bfd3fc68c2309a24e37cc217b
parentdecdedf97d493b665866cfa8207a7fcb6a94c017

spirv: air struct_field_val for unions


2 files changed, 115 insertions(+), 47 deletions(-)

src/codegen/spirv.zig+115-43
...@@ -452,16 +452,12 @@ pub const DeclGen = struct {...@@ -452,16 +452,12 @@ pub const DeclGen = struct {
452 .storage_class = .Function,452 .storage_class = .Function,
453 });453 });
454454
455 // Note: using 32-bit ints here because usize crashes the translator as well
456 const index_ty_ref = try self.intType(.unsigned, 32);
457
458 const spv_composite_ty = self.spv.cache.lookup(result_ty_ref).struct_type;455 const spv_composite_ty = self.spv.cache.lookup(result_ty_ref).struct_type;
459 const member_types = spv_composite_ty.member_types;456 const member_types = spv_composite_ty.member_types;
460457
461 for (constituents, member_types, 0..) |constitent_id, member_ty_ref, index| {458 for (constituents, member_types, 0..) |constitent_id, member_ty_ref, index| {
462 const index_id = try self.constInt(index_ty_ref, index);
463 const ptr_member_ty_ref = try self.spv.ptrType(member_ty_ref, .Function);459 const ptr_member_ty_ref = try self.spv.ptrType(member_ty_ref, .Function);
464 const ptr_id = try self.accessChain(ptr_member_ty_ref, ptr_composite_id, &.{index_id});460 const ptr_id = try self.accessChain(ptr_member_ty_ref, ptr_composite_id, &.{@as(u32, @intCast(index))});
465 try self.func.body.emit(self.spv.gpa, .OpStore, .{461 try self.func.body.emit(self.spv.gpa, .OpStore, .{
466 .pointer = ptr_id,462 .pointer = ptr_id,
467 .object = constitent_id,463 .object = constitent_id,
...@@ -493,16 +489,12 @@ pub const DeclGen = struct {...@@ -493,16 +489,12 @@ pub const DeclGen = struct {
493 .storage_class = .Function,489 .storage_class = .Function,
494 });490 });
495491
496 // Note: using 32-bit ints here because usize crashes the translator as well
497 const index_ty_ref = try self.intType(.unsigned, 32);
498
499 const spv_composite_ty = self.spv.cache.lookup(result_ty_ref).array_type;492 const spv_composite_ty = self.spv.cache.lookup(result_ty_ref).array_type;
500 const elem_ty_ref = spv_composite_ty.element_type;493 const elem_ty_ref = spv_composite_ty.element_type;
501 const ptr_elem_ty_ref = try self.spv.ptrType(elem_ty_ref, .Function);494 const ptr_elem_ty_ref = try self.spv.ptrType(elem_ty_ref, .Function);
502495
503 for (constituents, 0..) |constitent_id, index| {496 for (constituents, 0..) |constitent_id, index| {
504 const index_id = try self.constInt(index_ty_ref, index);497 const ptr_id = try self.accessChain(ptr_elem_ty_ref, ptr_composite_id, &.{@as(u32, @intCast(index))});
505 const ptr_id = try self.accessChain(ptr_elem_ty_ref, ptr_composite_id, &.{index_id});
506 try self.func.body.emit(self.spv.gpa, .OpStore, .{498 try self.func.body.emit(self.spv.gpa, .OpStore, .{
507 .pointer = ptr_id,499 .pointer = ptr_id,
508 .object = constitent_id,500 .object = constitent_id,
...@@ -535,18 +527,36 @@ pub const DeclGen = struct {...@@ -535,18 +527,36 @@ pub const DeclGen = struct {
535 const decl_id = self.spv.declPtr(spv_decl_index).result_id;527 const decl_id = self.spv.declPtr(spv_decl_index).result_id;
536 try self.func.decl_deps.put(self.spv.gpa, spv_decl_index, {});528 try self.func.decl_deps.put(self.spv.gpa, spv_decl_index, {});
537529
538 switch (decl.@"addrspace") {530 const final_storage_class = spvStorageClass(decl.@"addrspace");
539 .generic => {531
540 // Pointer should be generic, but is actually placed in CrossWorkgroup.532 const decl_ty_ref = try self.resolveType(decl.ty, .indirect);
533 const decl_ptr_ty_ref = try self.spv.ptrType(decl_ty_ref, final_storage_class);
534
535 const ptr_id = switch (final_storage_class) {
536 .Generic => blk: {
537 // Pointer should be Generic, but is actually placed in CrossWorkgroup.
541 const result_id = self.spv.allocId();538 const result_id = self.spv.allocId();
542 try self.func.body.emit(self.spv.gpa, .OpPtrCastToGeneric, .{539 try self.func.body.emit(self.spv.gpa, .OpPtrCastToGeneric, .{
543 .id_result_type = ty_id,540 .id_result_type = self.typeId(decl_ptr_ty_ref),
544 .id_result = result_id,541 .id_result = result_id,
545 .pointer = decl_id,542 .pointer = decl_id,
546 });543 });
547 return result_id;544 break :blk result_id;
548 },545 },
549 else => return decl_id, // Variable is already correct, probably. Maybe needs a bitcast?546 else => decl_id,
547 };
548
549 if (decl_ptr_ty_ref != ty_ref) {
550 // Differing pointer types, insert a cast.
551 const casted_ptr_id = self.spv.allocId();
552 try self.func.body.emit(self.spv.gpa, .OpBitcast, .{
553 .id_result_type = ty_id,
554 .id_result = casted_ptr_id,
555 .operand = ptr_id,
556 });
557 return casted_ptr_id;
558 } else {
559 return ptr_id;
550 }560 }
551 },561 },
552 }562 }
...@@ -820,14 +830,11 @@ pub const DeclGen = struct {...@@ -820,14 +830,11 @@ pub const DeclGen = struct {
820 .storage_class = .Function,830 .storage_class = .Function,
821 });831 });
822832
823 const index_ty_ref = try self.intType(.unsigned, 32);
824
825 if (layout.tag_size != 0) {833 if (layout.tag_size != 0) {
826 const index_id = try self.constInt(index_ty_ref, @as(u32, @intCast(layout.tag_index)));
827 const tag_ty = ty.unionTagTypeSafety(mod).?;834 const tag_ty = ty.unionTagTypeSafety(mod).?;
828 const tag_ty_ref = try self.resolveType(tag_ty, .indirect);835 const tag_ty_ref = try self.resolveType(tag_ty, .indirect);
829 const tag_ptr_ty_ref = try self.spv.ptrType(tag_ty_ref, .Function);836 const tag_ptr_ty_ref = try self.spv.ptrType(tag_ty_ref, .Function);
830 const ptr_id = try self.accessChain(tag_ptr_ty_ref, var_id, &.{index_id});837 const ptr_id = try self.accessChain(tag_ptr_ty_ref, var_id, &.{@as(u32, @intCast(layout.tag_index))});
831 const tag_id = try self.constant(tag_ty, un.tag.toValue(), .indirect);838 const tag_id = try self.constant(tag_ty, un.tag.toValue(), .indirect);
832 try self.func.body.emit(self.spv.gpa, .OpStore, .{839 try self.func.body.emit(self.spv.gpa, .OpStore, .{
833 .pointer = ptr_id,840 .pointer = ptr_id,
...@@ -836,10 +843,9 @@ pub const DeclGen = struct {...@@ -836,10 +843,9 @@ pub const DeclGen = struct {
836 }843 }
837844
838 if (layout.active_field_size != 0) {845 if (layout.active_field_size != 0) {
839 const index_id = try self.constInt(index_ty_ref, @as(u32, @intCast(layout.active_field_index)));
840 const active_field_ty_ref = try self.resolveType(layout.active_field_ty, .indirect);846 const active_field_ty_ref = try self.resolveType(layout.active_field_ty, .indirect);
841 const active_field_ptr_ty_ref = try self.spv.ptrType(active_field_ty_ref, .Function);847 const active_field_ptr_ty_ref = try self.spv.ptrType(active_field_ty_ref, .Function);
842 const ptr_id = try self.accessChain(active_field_ptr_ty_ref, var_id, &.{index_id});848 const ptr_id = try self.accessChain(active_field_ptr_ty_ref, var_id, &.{@as(u32, @intCast(layout.active_field_index))});
843 const value_id = try self.constant(layout.active_field_ty, un.val.toValue(), .indirect);849 const value_id = try self.constant(layout.active_field_ty, un.val.toValue(), .indirect);
844 try self.func.body.emit(self.spv.gpa, .OpStore, .{850 try self.func.body.emit(self.spv.gpa, .OpStore, .{
845 .pointer = ptr_id,851 .pointer = ptr_id,
...@@ -2070,40 +2076,65 @@ pub const DeclGen = struct {...@@ -2070,40 +2076,65 @@ pub const DeclGen = struct {
2070 return result_id;2076 return result_id;
2071 }2077 }
20722078
2073 /// AccessChain is essentially PtrAccessChain with 0 as initial argument. The effective2079 fn indicesToIds(self: *DeclGen, indices: []const u32) ![]IdRef {
2074 /// difference lies in whether the resulting type of the first dereference will be the2080 const index_ty_ref = try self.intType(.unsigned, 32);
2075 /// same as that of the base pointer, or that of a dereferenced base pointer. AccessChain2081 const ids = try self.gpa.alloc(IdRef, indices.len);
2076 /// is the latter and PtrAccessChain is the former.2082 errdefer self.gpa.free(ids);
2077 fn accessChain(2083 for (indices, ids) |index, *id| {
2084 id.* = try self.constInt(index_ty_ref, index);
2085 }
2086
2087 return ids;
2088 }
2089
2090 fn accessChainId(
2078 self: *DeclGen,2091 self: *DeclGen,
2079 result_ty_ref: CacheRef,2092 result_ty_ref: CacheRef,
2080 base: IdRef,2093 base: IdRef,
2081 indexes: []const IdRef,2094 indices: []const IdRef,
2082 ) !IdRef {2095 ) !IdRef {
2083 const result_id = self.spv.allocId();2096 const result_id = self.spv.allocId();
2084 try self.func.body.emit(self.spv.gpa, .OpInBoundsAccessChain, .{2097 try self.func.body.emit(self.spv.gpa, .OpInBoundsAccessChain, .{
2085 .id_result_type = self.typeId(result_ty_ref),2098 .id_result_type = self.typeId(result_ty_ref),
2086 .id_result = result_id,2099 .id_result = result_id,
2087 .base = base,2100 .base = base,
2088 .indexes = indexes,2101 .indexes = indices,
2089 });2102 });
2090 return result_id;2103 return result_id;
2091 }2104 }
20922105
2106 /// AccessChain is essentially PtrAccessChain with 0 as initial argument. The effective
2107 /// difference lies in whether the resulting type of the first dereference will be the
2108 /// same as that of the base pointer, or that of a dereferenced base pointer. AccessChain
2109 /// is the latter and PtrAccessChain is the former.
2110 fn accessChain(
2111 self: *DeclGen,
2112 result_ty_ref: CacheRef,
2113 base: IdRef,
2114 indices: []const u32,
2115 ) !IdRef {
2116 const ids = try self.indicesToIds(indices);
2117 defer self.gpa.free(ids);
2118 return try self.accessChainId(result_ty_ref, base, ids);
2119 }
2120
2093 fn ptrAccessChain(2121 fn ptrAccessChain(
2094 self: *DeclGen,2122 self: *DeclGen,
2095 result_ty_ref: CacheRef,2123 result_ty_ref: CacheRef,
2096 base: IdRef,2124 base: IdRef,
2097 element: IdRef,2125 element: IdRef,
2098 indexes: []const IdRef,2126 indices: []const u32,
2099 ) !IdRef {2127 ) !IdRef {
2128 const ids = try self.indicesToIds(indices);
2129 defer self.gpa.free(ids);
2130
2100 const result_id = self.spv.allocId();2131 const result_id = self.spv.allocId();
2101 try self.func.body.emit(self.spv.gpa, .OpInBoundsPtrAccessChain, .{2132 try self.func.body.emit(self.spv.gpa, .OpInBoundsPtrAccessChain, .{
2102 .id_result_type = self.typeId(result_ty_ref),2133 .id_result_type = self.typeId(result_ty_ref),
2103 .id_result = result_id,2134 .id_result = result_id,
2104 .base = base,2135 .base = base,
2105 .element = element,2136 .element = element,
2106 .indexes = indexes,2137 .indexes = ids,
2107 });2138 });
2108 return result_id;2139 return result_id;
2109 }2140 }
...@@ -2116,7 +2147,7 @@ pub const DeclGen = struct {...@@ -2116,7 +2147,7 @@ pub const DeclGen = struct {
2116 .One => {2147 .One => {
2117 // Pointer to array2148 // Pointer to array
2118 // TODO: Is this correct?2149 // TODO: Is this correct?
2119 return try self.accessChain(result_ty_ref, ptr_id, &.{offset_id});2150 return try self.accessChainId(result_ty_ref, ptr_id, &.{offset_id});
2120 },2151 },
2121 .C, .Many => {2152 .C, .Many => {
2122 return try self.ptrAccessChain(result_ty_ref, ptr_id, offset_id, &.{});2153 return try self.ptrAccessChain(result_ty_ref, ptr_id, offset_id, &.{});
...@@ -2493,7 +2524,7 @@ pub const DeclGen = struct {...@@ -2493,7 +2524,7 @@ pub const DeclGen = struct {
2493 if (ptr_ty.isSinglePointer(mod)) {2524 if (ptr_ty.isSinglePointer(mod)) {
2494 // Pointer-to-array. In this case, the resulting pointer is not of the same type2525 // Pointer-to-array. In this case, the resulting pointer is not of the same type
2495 // as the ptr_ty (we want a *T, not a *[N]T), and hence we need to use accessChain.2526 // as the ptr_ty (we want a *T, not a *[N]T), and hence we need to use accessChain.
2496 return try self.accessChain(elem_ptr_ty_ref, ptr_id, &.{index_id});2527 return try self.accessChainId(elem_ptr_ty_ref, ptr_id, &.{index_id});
2497 } else {2528 } else {
2498 // Resulting pointer type is the same as the ptr_ty, so use ptrAccessChain2529 // Resulting pointer type is the same as the ptr_ty, so use ptrAccessChain
2499 return try self.ptrAccessChain(elem_ptr_ty_ref, ptr_id, index_id, &.{});2530 return try self.ptrAccessChain(elem_ptr_ty_ref, ptr_id, index_id, &.{});
...@@ -2540,15 +2571,14 @@ pub const DeclGen = struct {...@@ -2540,15 +2571,14 @@ pub const DeclGen = struct {
2540 const un_ty = self.typeOf(ty_op.operand);2571 const un_ty = self.typeOf(ty_op.operand);
25412572
2542 const mod = self.module;2573 const mod = self.module;
2543 const layout = un_ty.unionGetLayout(mod);2574 const layout = self.unionLayout(un_ty, null);
2544 if (layout.tag_size == 0) return null;2575 if (layout.tag_size == 0) return null;
25452576
2546 const union_handle = try self.resolve(ty_op.operand);2577 const union_handle = try self.resolve(ty_op.operand);
2547 if (layout.payload_size == 0) return union_handle;2578 if (layout.payload_size == 0) return union_handle;
25482579
2549 const tag_ty = un_ty.unionTagTypeSafety(mod).?;2580 const tag_ty = un_ty.unionTagTypeSafety(mod).?;
2550 const tag_index = @intFromBool(layout.tag_align.compare(.lt, layout.payload_align));2581 return try self.extractField(tag_ty, union_handle, layout.tag_index);
2551 return try self.extractField(tag_ty, union_handle, tag_index);
2552 }2582 }
25532583
2554 fn airStructFieldVal(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {2584 fn airStructFieldVal(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
...@@ -2558,16 +2588,60 @@ pub const DeclGen = struct {...@@ -2558,16 +2588,60 @@ pub const DeclGen = struct {
2558 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;2588 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
2559 const struct_field = self.air.extraData(Air.StructField, ty_pl.payload).data;2589 const struct_field = self.air.extraData(Air.StructField, ty_pl.payload).data;
25602590
2561 const struct_ty = self.typeOf(struct_field.struct_operand);2591 const container_ty = self.typeOf(struct_field.struct_operand);
2562 const object_id = try self.resolve(struct_field.struct_operand);2592 const object_id = try self.resolve(struct_field.struct_operand);
2563 const field_index = struct_field.field_index;2593 const field_index = struct_field.field_index;
2564 const field_ty = struct_ty.structFieldType(field_index, mod);2594 const field_ty = container_ty.structFieldType(field_index, mod);
25652595
2566 if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) return null;2596 if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) return null;
25672597
2568 assert(struct_ty.zigTypeTag(mod) == .Struct); // Cannot do unions yet.2598 switch (container_ty.zigTypeTag(mod)) {
2599 .Struct => switch (container_ty.containerLayout(mod)) {
2600 .Packed => unreachable, // TODO
2601 else => return try self.extractField(field_ty, object_id, field_index),
2602 },
2603 .Union => switch (container_ty.containerLayout(mod)) {
2604 .Packed => unreachable, // TODO
2605 else => {
2606 // Store, pointer-cast, load
2607 const un_general_ty_ref = try self.resolveType(container_ty, .indirect);
2608 const un_general_ptr_ty_ref = try self.spv.ptrType(un_general_ty_ref, .Function);
2609 const un_active_ty_ref = try self.resolveUnionType(container_ty, field_index);
2610 const un_active_ptr_ty_ref = try self.spv.ptrType(un_active_ty_ref, .Function);
2611 const field_ty_ref = try self.resolveType(field_ty, .indirect);
2612 const field_ptr_ty_ref = try self.spv.ptrType(field_ty_ref, .Function);
2613
2614 const tmp_id = self.spv.allocId();
2615 try self.func.prologue.emit(self.spv.gpa, .OpVariable, .{
2616 .id_result_type = self.typeId(un_general_ptr_ty_ref),
2617 .id_result = tmp_id,
2618 .storage_class = .Function,
2619 });
2620 try self.func.body.emit(self.spv.gpa, .OpStore, .{
2621 .pointer = tmp_id,
2622 .object = object_id,
2623 });
2624 const casted_tmp_id = self.spv.allocId();
2625 try self.func.body.emit(self.spv.gpa, .OpBitcast, .{
2626 .id_result_type = self.typeId(un_active_ptr_ty_ref),
2627 .id_result = casted_tmp_id,
2628 .operand = tmp_id,
2629 });
2630 const layout = self.unionLayout(container_ty, field_index);
2631 const field_ptr_id = try self.accessChain(field_ptr_ty_ref, casted_tmp_id, &.{layout.active_field_index});
2632 const result_id = self.spv.allocId();
2633 try self.func.body.emit(self.spv.gpa, .OpLoad, .{
2634 .id_result_type = self.typeId(field_ty_ref),
2635 .id_result = result_id,
2636 .pointer = field_ptr_id,
2637 });
2638 return try self.convertToDirect(field_ty, result_id);
2639 },
2640 },
2641 else => unreachable,
2642 }
25692643
2570 return try self.extractField(field_ty, object_id, field_index);2644 // return try self.extractField(field_ty, object_id, field_index);
2571 }2645 }
25722646
2573 fn structFieldPtr(2647 fn structFieldPtr(
...@@ -2583,10 +2657,8 @@ pub const DeclGen = struct {...@@ -2583,10 +2657,8 @@ pub const DeclGen = struct {
2583 .Struct => switch (object_ty.containerLayout(mod)) {2657 .Struct => switch (object_ty.containerLayout(mod)) {
2584 .Packed => unreachable, // TODO2658 .Packed => unreachable, // TODO
2585 else => {2659 else => {
2586 const field_index_ty_ref = try self.intType(.unsigned, 32);
2587 const field_index_id = try self.constInt(field_index_ty_ref, field_index);
2588 const result_ty_ref = try self.resolveType(result_ptr_ty, .direct);2660 const result_ty_ref = try self.resolveType(result_ptr_ty, .direct);
2589 return try self.accessChain(result_ty_ref, object_ptr, &.{field_index_id});2661 return try self.accessChain(result_ty_ref, object_ptr, &.{field_index});
2590 },2662 },
2591 },2663 },
2592 else => unreachable, // TODO2664 else => unreachable, // TODO
test/behavior/union.zig-4
...@@ -14,7 +14,6 @@ test "basic unions with floats" {...@@ -14,7 +14,6 @@ test "basic unions with floats" {
14 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;14 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
15 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;15 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
16 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO16 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
17 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
1817
19 var foo = FooWithFloats{ .int = 1 };18 var foo = FooWithFloats{ .int = 1 };
20 try expect(foo.int == 1);19 try expect(foo.int == 1);
...@@ -42,7 +41,6 @@ test "basic unions" {...@@ -42,7 +41,6 @@ test "basic unions" {
42 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;41 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
43 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO42 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
44 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO43 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
45 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
4644
47 var foo = Foo{ .int = 1 };45 var foo = Foo{ .int = 1 };
48 try expect(foo.int == 1);46 try expect(foo.int == 1);
...@@ -342,7 +340,6 @@ test "constant packed union" {...@@ -342,7 +340,6 @@ test "constant packed union" {
342 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;340 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
343 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;341 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
344 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO342 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
345 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
346343
347 try testConstPackedUnion(&[_]PackThis{PackThis{ .StringLiteral = 1 }});344 try testConstPackedUnion(&[_]PackThis{PackThis{ .StringLiteral = 1 }});
348}345}
...@@ -453,7 +450,6 @@ test "global union with single field is correctly initialized" {...@@ -453,7 +450,6 @@ test "global union with single field is correctly initialized" {
453 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;450 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
454 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;451 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
455 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO452 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
456 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
457453
458 glbl = Foo1{454 glbl = Foo1{
459 .f = @typeInfo(Foo1).Union.fields[0].type{ .x = 123 },455 .f = @typeInfo(Foo1).Union.fields[0].type{ .x = 123 },