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 {
452452 .storage_class = .Function,
453453 });
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
458455 const spv_composite_ty = self.spv.cache.lookup(result_ty_ref).struct_type;
459456 const member_types = spv_composite_ty.member_types;
460457
461458 for (constituents, member_types, 0..) |constitent_id, member_ty_ref, index| {
462 const index_id = try self.constInt(index_ty_ref, index);
463459 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))});
465461 try self.func.body.emit(self.spv.gpa, .OpStore, .{
466462 .pointer = ptr_id,
467463 .object = constitent_id,
......@@ -493,16 +489,12 @@ pub const DeclGen = struct {
493489 .storage_class = .Function,
494490 });
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
499492 const spv_composite_ty = self.spv.cache.lookup(result_ty_ref).array_type;
500493 const elem_ty_ref = spv_composite_ty.element_type;
501494 const ptr_elem_ty_ref = try self.spv.ptrType(elem_ty_ref, .Function);
502495
503496 for (constituents, 0..) |constitent_id, index| {
504 const index_id = try self.constInt(index_ty_ref, index);
505 const ptr_id = try self.accessChain(ptr_elem_ty_ref, ptr_composite_id, &.{index_id});
497 const ptr_id = try self.accessChain(ptr_elem_ty_ref, ptr_composite_id, &.{@as(u32, @intCast(index))});
506498 try self.func.body.emit(self.spv.gpa, .OpStore, .{
507499 .pointer = ptr_id,
508500 .object = constitent_id,
......@@ -535,18 +527,36 @@ pub const DeclGen = struct {
535527 const decl_id = self.spv.declPtr(spv_decl_index).result_id;
536528 try self.func.decl_deps.put(self.spv.gpa, spv_decl_index, {});
537529
538 switch (decl.@"addrspace") {
539 .generic => {
540 // Pointer should be generic, but is actually placed in CrossWorkgroup.
530 const final_storage_class = spvStorageClass(decl.@"addrspace");
531
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.
541538 const result_id = self.spv.allocId();
542539 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),
544541 .id_result = result_id,
545542 .pointer = decl_id,
546543 });
547 return result_id;
544 break :blk result_id;
548545 },
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;
550560 }
551561 },
552562 }
......@@ -820,14 +830,11 @@ pub const DeclGen = struct {
820830 .storage_class = .Function,
821831 });
822832
823 const index_ty_ref = try self.intType(.unsigned, 32);
824
825833 if (layout.tag_size != 0) {
826 const index_id = try self.constInt(index_ty_ref, @as(u32, @intCast(layout.tag_index)));
827834 const tag_ty = ty.unionTagTypeSafety(mod).?;
828835 const tag_ty_ref = try self.resolveType(tag_ty, .indirect);
829836 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))});
831838 const tag_id = try self.constant(tag_ty, un.tag.toValue(), .indirect);
832839 try self.func.body.emit(self.spv.gpa, .OpStore, .{
833840 .pointer = ptr_id,
......@@ -836,10 +843,9 @@ pub const DeclGen = struct {
836843 }
837844
838845 if (layout.active_field_size != 0) {
839 const index_id = try self.constInt(index_ty_ref, @as(u32, @intCast(layout.active_field_index)));
840846 const active_field_ty_ref = try self.resolveType(layout.active_field_ty, .indirect);
841847 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))});
843849 const value_id = try self.constant(layout.active_field_ty, un.val.toValue(), .indirect);
844850 try self.func.body.emit(self.spv.gpa, .OpStore, .{
845851 .pointer = ptr_id,
......@@ -2070,40 +2076,65 @@ pub const DeclGen = struct {
20702076 return result_id;
20712077 }
20722078
2073 /// AccessChain is essentially PtrAccessChain with 0 as initial argument. The effective
2074 /// difference lies in whether the resulting type of the first dereference will be the
2075 /// same as that of the base pointer, or that of a dereferenced base pointer. AccessChain
2076 /// is the latter and PtrAccessChain is the former.
2077 fn accessChain(
2079 fn indicesToIds(self: *DeclGen, indices: []const u32) ![]IdRef {
2080 const index_ty_ref = try self.intType(.unsigned, 32);
2081 const ids = try self.gpa.alloc(IdRef, indices.len);
2082 errdefer self.gpa.free(ids);
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(
20782091 self: *DeclGen,
20792092 result_ty_ref: CacheRef,
20802093 base: IdRef,
2081 indexes: []const IdRef,
2094 indices: []const IdRef,
20822095 ) !IdRef {
20832096 const result_id = self.spv.allocId();
20842097 try self.func.body.emit(self.spv.gpa, .OpInBoundsAccessChain, .{
20852098 .id_result_type = self.typeId(result_ty_ref),
20862099 .id_result = result_id,
20872100 .base = base,
2088 .indexes = indexes,
2101 .indexes = indices,
20892102 });
20902103 return result_id;
20912104 }
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
20932121 fn ptrAccessChain(
20942122 self: *DeclGen,
20952123 result_ty_ref: CacheRef,
20962124 base: IdRef,
20972125 element: IdRef,
2098 indexes: []const IdRef,
2126 indices: []const u32,
20992127 ) !IdRef {
2128 const ids = try self.indicesToIds(indices);
2129 defer self.gpa.free(ids);
2130
21002131 const result_id = self.spv.allocId();
21012132 try self.func.body.emit(self.spv.gpa, .OpInBoundsPtrAccessChain, .{
21022133 .id_result_type = self.typeId(result_ty_ref),
21032134 .id_result = result_id,
21042135 .base = base,
21052136 .element = element,
2106 .indexes = indexes,
2137 .indexes = ids,
21072138 });
21082139 return result_id;
21092140 }
......@@ -2116,7 +2147,7 @@ pub const DeclGen = struct {
21162147 .One => {
21172148 // Pointer to array
21182149 // 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});
21202151 },
21212152 .C, .Many => {
21222153 return try self.ptrAccessChain(result_ty_ref, ptr_id, offset_id, &.{});
......@@ -2493,7 +2524,7 @@ pub const DeclGen = struct {
24932524 if (ptr_ty.isSinglePointer(mod)) {
24942525 // Pointer-to-array. In this case, the resulting pointer is not of the same type
24952526 // 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});
24972528 } else {
24982529 // Resulting pointer type is the same as the ptr_ty, so use ptrAccessChain
24992530 return try self.ptrAccessChain(elem_ptr_ty_ref, ptr_id, index_id, &.{});
......@@ -2540,15 +2571,14 @@ pub const DeclGen = struct {
25402571 const un_ty = self.typeOf(ty_op.operand);
25412572
25422573 const mod = self.module;
2543 const layout = un_ty.unionGetLayout(mod);
2574 const layout = self.unionLayout(un_ty, null);
25442575 if (layout.tag_size == 0) return null;
25452576
25462577 const union_handle = try self.resolve(ty_op.operand);
25472578 if (layout.payload_size == 0) return union_handle;
25482579
25492580 const tag_ty = un_ty.unionTagTypeSafety(mod).?;
2550 const tag_index = @intFromBool(layout.tag_align.compare(.lt, layout.payload_align));
2551 return try self.extractField(tag_ty, union_handle, tag_index);
2581 return try self.extractField(tag_ty, union_handle, layout.tag_index);
25522582 }
25532583
25542584 fn airStructFieldVal(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
......@@ -2558,16 +2588,60 @@ pub const DeclGen = struct {
25582588 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
25592589 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);
25622592 const object_id = try self.resolve(struct_field.struct_operand);
25632593 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
25662596 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);
25712645 }
25722646
25732647 fn structFieldPtr(
......@@ -2583,10 +2657,8 @@ pub const DeclGen = struct {
25832657 .Struct => switch (object_ty.containerLayout(mod)) {
25842658 .Packed => unreachable, // TODO
25852659 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);
25882660 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});
25902662 },
25912663 },
25922664 else => unreachable, // TODO
test/behavior/union.zig-4
......@@ -14,7 +14,6 @@ test "basic unions with floats" {
1414 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
1515 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
1616 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
17 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
1817
1918 var foo = FooWithFloats{ .int = 1 };
2019 try expect(foo.int == 1);
......@@ -42,7 +41,6 @@ test "basic unions" {
4241 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
4342 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
4443 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
45 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
4644
4745 var foo = Foo{ .int = 1 };
4846 try expect(foo.int == 1);
......@@ -342,7 +340,6 @@ test "constant packed union" {
342340 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
343341 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
344342 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
345 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
346343
347344 try testConstPackedUnion(&[_]PackThis{PackThis{ .StringLiteral = 1 }});
348345}
......@@ -453,7 +450,6 @@ test "global union with single field is correctly initialized" {
453450 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
454451 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
455452 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
456 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
457453
458454 glbl = Foo1{
459455 .f = @typeInfo(Foo1).Union.fields[0].type{ .x = 123 },