| ... | ... | @@ -17,6 +17,8 @@ uav_link: std.AutoHashMapUnmanaged(struct { InternPool.Index, spec.StorageClass |
| 17 | 17 | entry_points: std.array_hash_map.Auto(Id, EntryPoint) = .empty, |
| 18 | 18 | error_buffer: ?Decl.Index = null, |
| 19 | 19 | struct_types: std.array_hash_map.Custom(StructType, Id, StructType.HashContext, true) = .empty, |
| 20 | /// SPIR-V ids of OpVariables whose pointee is a Block struct |
| 21 | block_var_ids: std.AutoHashMapUnmanaged(Id, void) = .empty, |
| 20 | 22 | builtins: std.AutoHashMapUnmanaged(struct { spec.BuiltIn, spec.StorageClass }, Decl.Index) = .empty, |
| 21 | 23 | sections: struct { |
| 22 | 24 | // Module layout, according to SPIR-V Spec section 2.4, "Logical Layout of a Module". |
| ... | ... | @@ -181,6 +183,7 @@ pub fn deinit(cg: *CodeGen) void { |
| 181 | 183 | cg.sections.functions.deinit(gpa); |
| 182 | 184 | |
| 183 | 185 | cg.struct_types.deinit(gpa); |
| 186 | cg.block_var_ids.deinit(gpa); |
| 184 | 187 | cg.builtins.deinit(gpa); |
| 185 | 188 | |
| 186 | 189 | cg.decls.deinit(gpa); |
| ... | ... | @@ -587,6 +590,71 @@ pub fn structType( |
| 587 | 590 | return result_id; |
| 588 | 591 | } |
| 589 | 592 | |
| 593 | /// Returns the layout-decorated variant of `ty` for use inside a Vulkan/OpenGL |
| 594 | /// interface block. Vulkan forbids nested Block decorations, so recursive calls |
| 595 | /// always pass `false`. |
| 596 | /// |
| 597 | /// This is distinct from `resolveType` because SPIR-V forbids such decorations |
| 598 | /// on the pointee of a Function-scope variable. |
| 599 | pub fn layoutType(cg: *CodeGen, ty: Type, is_block_root: bool) Error!Id { |
| 600 | const gpa = cg.gpa; |
| 601 | const zcu = cg.zcu; |
| 602 | const ip = &zcu.intern_pool; |
| 603 | |
| 604 | const result_id: Id = switch (ty.zigTypeTag(zcu)) { |
| 605 | .@"struct" => id: { |
| 606 | const struct_type = ip.loadStructType(ty.toIntern()); |
| 607 | if (struct_type.layout == .@"packed") return cg.resolveType(ty, .indirect); |
| 608 | |
| 609 | var member_types: std.ArrayList(Id) = .empty; |
| 610 | defer member_types.deinit(gpa); |
| 611 | const id = cg.allocId(); |
| 612 | if (is_block_root) try cg.decorate(id, .block); |
| 613 | var it = struct_type.iterateRuntimeOrder(ip); |
| 614 | while (it.next()) |field_index| { |
| 615 | const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[field_index]); |
| 616 | if (!field_ty.hasRuntimeBits(zcu)) continue; |
| 617 | try cg.decorateMember(id, @intCast(member_types.items.len), .{ .offset = .{ |
| 618 | .byte_offset = @intCast(ty.structFieldOffset(field_index, zcu)), |
| 619 | } }); |
| 620 | try member_types.append(gpa, try cg.layoutType(field_ty, false)); |
| 621 | } |
| 622 | try cg.sections.globals.emit(gpa, .OpTypeStruct, .{ |
| 623 | .id_result = id, |
| 624 | .id_ref = member_types.items, |
| 625 | }); |
| 626 | break :id id; |
| 627 | }, |
| 628 | .array => id: { |
| 629 | const elem_ty = ty.childType(zcu); |
| 630 | const elem_ty_id = try cg.layoutType(elem_ty, false); |
| 631 | const total_len = std.math.cast(u32, ty.arrayLenIncludingSentinel(zcu)) orelse |
| 632 | return cg.fail("array type of {} elements is too large", .{ty.arrayLenIncludingSentinel(zcu)}); |
| 633 | const id = try cg.arrayType(try cg.constInt(.u32, total_len), elem_ty_id); |
| 634 | if (elem_ty.hasRuntimeBits(zcu)) try cg.decorate(id, .{ |
| 635 | .array_stride = .{ .array_stride = @intCast(elem_ty.abiSize(zcu)) }, |
| 636 | }); |
| 637 | break :id id; |
| 638 | }, |
| 639 | .spirv => if (ty.isSpirvRuntimeArray(zcu)) id: { |
| 640 | const elem_ty = ty.childType(zcu); |
| 641 | const elem_ty_id = try cg.layoutType(elem_ty, false); |
| 642 | const id = cg.allocId(); |
| 643 | try cg.sections.globals.emit(gpa, .OpTypeRuntimeArray, .{ |
| 644 | .id_result = id, |
| 645 | .element_type = elem_ty_id, |
| 646 | }); |
| 647 | if (elem_ty.hasRuntimeBits(zcu)) try cg.decorate(id, .{ |
| 648 | .array_stride = .{ .array_stride = @intCast(elem_ty.abiSize(zcu)) }, |
| 649 | }); |
| 650 | break :id id; |
| 651 | } else return cg.resolveType(ty, .indirect), |
| 652 | else => return cg.resolveType(ty, .indirect), |
| 653 | }; |
| 654 | |
| 655 | return result_id; |
| 656 | } |
| 657 | |
| 590 | 658 | pub fn functionType(cg: *CodeGen, return_ty_id: Id, param_type_ids: []const Id) !Id { |
| 591 | 659 | const result_id = cg.allocId(); |
| 592 | 660 | try cg.sections.globals.emit(cg.gpa, .OpTypeFunction, .{ |
| ... | ... | @@ -806,7 +874,8 @@ pub fn genNav(cg: *CodeGen, do_codegen: bool) Error!void { |
| 806 | 874 | const storage_class = cg.storageClass(nav.resolved.?.@"addrspace"); |
| 807 | 875 | assert(storage_class != .generic); // These should be instance globals |
| 808 | 876 | |
| 809 | | const ty_id = try cg.resolveType(ty, .indirect); |
| 877 | const as = nav.resolved.?.@"addrspace"; |
| 878 | const ty_id = try cg.pointeeType(as, ty, true); |
| 810 | 879 | const ptr_ty_id = try cg.ptrType(ty_id, storage_class); |
| 811 | 880 | |
| 812 | 881 | try cg.sections.globals.emit(gpa, .OpVariable, .{ |
| ... | ... | @@ -819,15 +888,11 @@ pub fn genNav(cg: *CodeGen, do_codegen: bool) Error!void { |
| 819 | 888 | .vulkan, .opengl => { |
| 820 | 889 | switch (storage_class) { |
| 821 | 890 | .uniform, .push_constant, .storage_buffer, .physical_storage_buffer => { |
| 822 | | if (ty.zigTypeTag(zcu) == .@"struct" and storage_class != .physical_storage_buffer) { |
| 823 | | try cg.decorate(ty_id, .block); |
| 824 | | } |
| 825 | | |
| 826 | 891 | if (ty.hasRuntimeBits(zcu)) { |
| 827 | 892 | try cg.decorate(ptr_ty_id, .{ |
| 828 | 893 | .array_stride = .{ .array_stride = @intCast(ty.abiSize(zcu)) }, |
| 829 | 894 | }); |
| 830 | | try cg.decorateLayout(ty, ty_id); |
| 895 | if (!cg.needsLayout(as, ty)) try cg.decorateLayout(ty, ty_id); |
| 831 | 896 | } |
| 832 | 897 | }, |
| 833 | 898 | else => {}, |
| ... | ... | @@ -1815,6 +1880,9 @@ fn derivePtr(cg: *CodeGen, derivation: Value.PointerDeriveStep) !Id { |
| 1815 | 1880 | |
| 1816 | 1881 | const nav_ty_id = try cg.resolveType(nav_ty, .indirect); |
| 1817 | 1882 | const decl_ptr_ty_id = try cg.ptrType(nav_ty_id, storage_class); |
| 1883 | if (nav_ty.zigTypeTag(zcu) == .@"struct" and cg.needsLayout(nav.resolved.?.@"addrspace", nav_ty)) { |
| 1884 | try cg.block_var_ids.put(gpa, spv_decl.result_id, {}); |
| 1885 | } |
| 1818 | 1886 | if (decl_ptr_ty_id == ty_id) return spv_decl.result_id; |
| 1819 | 1887 | switch (target.os.tag) { |
| 1820 | 1888 | .vulkan, .opengl => return spv_decl.result_id, |
| ... | ... | @@ -1879,7 +1947,6 @@ fn derivePtr(cg: *CodeGen, derivation: Value.PointerDeriveStep) !Id { |
| 1879 | 1947 | .offset_and_cast => |oac| { |
| 1880 | 1948 | const parent_ptr_id = try cg.derivePtr(oac.parent.*); |
| 1881 | 1949 | const parent_ptr_ty = try oac.parent.ptrType(pt); |
| 1882 | | const result_ty_id = try cg.resolveType(oac.new_ptr_ty, .direct); |
| 1883 | 1950 | |
| 1884 | 1951 | if (oac.new_ptr_ty.ptrInfo(zcu).flags.vector_index != .none) { |
| 1885 | 1952 | return parent_ptr_id; |
| ... | ... | @@ -1890,13 +1957,25 @@ fn derivePtr(cg: *CodeGen, derivation: Value.PointerDeriveStep) !Id { |
| 1890 | 1957 | var cur = parent_ptr_ty.childType(zcu); |
| 1891 | 1958 | const dst_child = oac.new_ptr_ty.childType(zcu); |
| 1892 | 1959 | while (cur.toIntern() != dst_child.toIntern()) { |
| 1893 | | if (cur.zigTypeTag(zcu) == .array) { |
| 1894 | | cur = cur.childType(zcu); |
| 1895 | | depth += 1; |
| 1896 | | } else break; |
| 1960 | switch (cur.zigTypeTag(zcu)) { |
| 1961 | .array => { |
| 1962 | cur = cur.childType(zcu); |
| 1963 | depth += 1; |
| 1964 | }, |
| 1965 | .@"struct" => { |
| 1966 | if (cur.structFieldCount(zcu) == 0) break; |
| 1967 | if (cur.structFieldOffset(0, zcu) != 0) break; |
| 1968 | cur = cur.fieldType(0, zcu); |
| 1969 | depth += 1; |
| 1970 | }, |
| 1971 | else => break, |
| 1972 | } |
| 1897 | 1973 | } |
| 1898 | 1974 | if (cur.toIntern() == dst_child.toIntern()) { |
| 1899 | 1975 | if (depth != 0) { |
| 1976 | const as = oac.new_ptr_ty.ptrAddressSpace(zcu); |
| 1977 | const child_ty_id = try cg.pointeeType(as, dst_child, false); |
| 1978 | const result_ty_id = try cg.ptrType(child_ty_id, cg.storageClass(as)); |
| 1900 | 1979 | const scratch_top = cg.id_scratch.items.len; |
| 1901 | 1980 | defer cg.id_scratch.shrinkRetainingCapacity(scratch_top); |
| 1902 | 1981 | const zero = try cg.constInt(.u32, 0); |
| ... | ... | @@ -1907,6 +1986,7 @@ fn derivePtr(cg: *CodeGen, derivation: Value.PointerDeriveStep) !Id { |
| 1907 | 1986 | return parent_ptr_id; |
| 1908 | 1987 | } |
| 1909 | 1988 | } |
| 1989 | const result_ty_id = try cg.resolveType(oac.new_ptr_ty, .direct); |
| 1910 | 1990 | if (target.os.tag == .opencl) { |
| 1911 | 1991 | const result_ptr_id = cg.allocId(); |
| 1912 | 1992 | try cg.body.emit(gpa, .OpBitcast, .{ |
| ... | ... | @@ -4079,33 +4159,77 @@ fn extractVectorComponent(cg: *CodeGen, result_ty: Type, vector_id: Id, field: u |
| 4079 | 4159 | |
| 4080 | 4160 | const MemoryOptions = struct { |
| 4081 | 4161 | is_volatile: bool = false, |
| 4162 | ptr_address_space: std.lang.AddressSpace = .generic, |
| 4082 | 4163 | }; |
| 4083 | 4164 | |
| 4165 | /// Returns true if a pointee at address space must use the |
| 4166 | /// layout-decorated variant rather than the bare type. |
| 4167 | fn needsLayout(cg: *CodeGen, as: std.lang.AddressSpace, pointee_ty: Type) bool { |
| 4168 | const target = cg.zcu.getTarget(); |
| 4169 | if (target.os.tag != .vulkan and target.os.tag != .opengl) return false; |
| 4170 | switch (as) { |
| 4171 | .uniform, .push_constant, .storage_buffer => {}, |
| 4172 | else => return false, |
| 4173 | } |
| 4174 | return switch (pointee_ty.zigTypeTag(cg.zcu)) { |
| 4175 | .@"struct", .array => true, |
| 4176 | .spirv => pointee_ty.isSpirvRuntimeArray(cg.zcu), |
| 4177 | else => false, |
| 4178 | }; |
| 4179 | } |
| 4180 | |
| 4181 | fn pointeeType(cg: *CodeGen, as: std.lang.AddressSpace, ty: Type, is_block_root: bool) !Id { |
| 4182 | return if (cg.needsLayout(as, ty)) |
| 4183 | cg.layoutType(ty, is_block_root) |
| 4184 | else |
| 4185 | cg.resolveType(ty, .indirect); |
| 4186 | } |
| 4187 | |
| 4188 | fn convertLayout(cg: *CodeGen, dst_ty_id: Id, src_id: Id, src_ty_id: Id) !Id { |
| 4189 | if (dst_ty_id == src_ty_id) return src_id; |
| 4190 | const id = cg.allocId(); |
| 4191 | try cg.body.emit(cg.gpa, .OpCopyLogical, .{ |
| 4192 | .id_result_type = dst_ty_id, |
| 4193 | .id_result = id, |
| 4194 | .operand = src_id, |
| 4195 | }); |
| 4196 | return id; |
| 4197 | } |
| 4198 | |
| 4084 | 4199 | fn load(cg: *CodeGen, value_ty: Type, ptr_id: Id, options: MemoryOptions) !Id { |
| 4085 | 4200 | const zcu = cg.zcu; |
| 4086 | 4201 | const alignment: u32 = @intCast(value_ty.abiAlignment(zcu).toByteUnits().?); |
| 4087 | | const indirect_value_ty_id = try cg.resolveType(value_ty, .indirect); |
| 4088 | | const result_id = cg.allocId(); |
| 4089 | | const access: spec.MemoryAccess.Extended = .{ |
| 4090 | | .@"volatile" = options.is_volatile, |
| 4091 | | .aligned = .{ .literal_integer = alignment }, |
| 4092 | | }; |
| 4202 | const bare_ty_id = try cg.resolveType(value_ty, .indirect); |
| 4203 | const load_ty_id = if (cg.needsLayout(options.ptr_address_space, value_ty)) |
| 4204 | try cg.layoutType(value_ty, cg.block_var_ids.contains(ptr_id)) |
| 4205 | else |
| 4206 | bare_ty_id; |
| 4207 | const loaded_id = cg.allocId(); |
| 4093 | 4208 | try cg.body.emit(cg.gpa, .OpLoad, .{ |
| 4094 | | .id_result_type = indirect_value_ty_id, |
| 4095 | | .id_result = result_id, |
| 4209 | .id_result_type = load_ty_id, |
| 4210 | .id_result = loaded_id, |
| 4096 | 4211 | .pointer = ptr_id, |
| 4097 | | .memory_access = access, |
| 4212 | .memory_access = .{ |
| 4213 | .@"volatile" = options.is_volatile, |
| 4214 | .aligned = .{ .literal_integer = alignment }, |
| 4215 | }, |
| 4098 | 4216 | }); |
| 4217 | const result_id = try cg.convertLayout(bare_ty_id, loaded_id, load_ty_id); |
| 4099 | 4218 | return try cg.convertToDirect(value_ty, result_id); |
| 4100 | 4219 | } |
| 4101 | 4220 | |
| 4102 | 4221 | fn store(cg: *CodeGen, value_ty: Type, ptr_id: Id, value_id: Id, options: MemoryOptions) !void { |
| 4103 | | const indirect_value_id = try cg.convertToIndirect(value_ty, value_id); |
| 4104 | | const access: spec.MemoryAccess.Extended = .{ .@"volatile" = options.is_volatile }; |
| 4222 | const bare_value_id = try cg.convertToIndirect(value_ty, value_id); |
| 4223 | const bare_ty_id = try cg.resolveType(value_ty, .indirect); |
| 4224 | const store_ty_id = if (cg.needsLayout(options.ptr_address_space, value_ty)) |
| 4225 | try cg.layoutType(value_ty, cg.block_var_ids.contains(ptr_id)) |
| 4226 | else |
| 4227 | bare_ty_id; |
| 4228 | const object_id = try cg.convertLayout(store_ty_id, bare_value_id, bare_ty_id); |
| 4105 | 4229 | try cg.body.emit(cg.gpa, .OpStore, .{ |
| 4106 | 4230 | .pointer = ptr_id, |
| 4107 | | .object = indirect_value_id, |
| 4108 | | .memory_access = access, |
| 4231 | .object = object_id, |
| 4232 | .memory_access = .{ .@"volatile" = options.is_volatile }, |
| 4109 | 4233 | }); |
| 4110 | 4234 | } |
| 4111 | 4235 | |
| ... | ... | @@ -5520,23 +5644,18 @@ fn ptrAccessChain( |
| 5520 | 5644 | |
| 5521 | 5645 | fn ptrAdd(cg: *CodeGen, result_ty: Type, ptr_ty: Type, ptr_id: Id, offset_id: Id) !Id { |
| 5522 | 5646 | const zcu = cg.zcu; |
| 5523 | | const result_ty_id = try cg.resolveType(result_ty, .direct); |
| 5524 | | |
| 5525 | | switch (ptr_ty.ptrSize(zcu)) { |
| 5526 | | .one => { |
| 5527 | | // Pointer to array |
| 5528 | | // TODO: Is this correct? |
| 5529 | | return try cg.accessChainId(result_ty_id, ptr_id, &.{offset_id}); |
| 5530 | | }, |
| 5531 | | .c, .many => { |
| 5532 | | return try cg.ptrAccessChain(result_ty_id, ptr_id, offset_id, &.{}); |
| 5533 | | }, |
| 5534 | | .slice => { |
| 5647 | const as = result_ty.ptrAddressSpace(zcu); |
| 5648 | const child_ty_id = try cg.pointeeType(as, result_ty.childType(zcu), false); |
| 5649 | const result_ty_id = try cg.ptrType(child_ty_id, cg.storageClass(as)); |
| 5650 | return switch (ptr_ty.ptrSize(zcu)) { |
| 5651 | .one => cg.accessChainId(result_ty_id, ptr_id, &.{offset_id}), |
| 5652 | .c, .many => cg.ptrAccessChain(result_ty_id, ptr_id, offset_id, &.{}), |
| 5653 | .slice => blk: { |
| 5535 | 5654 | // TODO: This is probably incorrect. A slice should be returned here, though this is what llvm does. |
| 5536 | 5655 | const slice_ptr_id = try cg.extractField(result_ty, ptr_id, 0); |
| 5537 | | return try cg.ptrAccessChain(result_ty_id, slice_ptr_id, offset_id, &.{}); |
| 5656 | break :blk cg.ptrAccessChain(result_ty_id, slice_ptr_id, offset_id, &.{}); |
| 5538 | 5657 | }, |
| 5539 | | } |
| 5658 | }; |
| 5540 | 5659 | } |
| 5541 | 5660 | |
| 5542 | 5661 | fn airPtrAdd(cg: *CodeGen, inst: Air.Inst.Index) !?Id { |
| ... | ... | @@ -6482,16 +6601,16 @@ fn airSliceElemVal(cg: *CodeGen, inst: Air.Inst.Index) !?Id { |
| 6482 | 6601 | fn ptrElemPtr(cg: *CodeGen, ptr_ty: Type, ptr_id: Id, index_id: Id) !Id { |
| 6483 | 6602 | const zcu = cg.zcu; |
| 6484 | 6603 | // Construct new pointer type for the resulting pointer |
| 6485 | | const elem_ty = ptr_ty.indexableElem(zcu); |
| 6486 | | const elem_ty_id = try cg.resolveType(elem_ty, .indirect); |
| 6487 | | const elem_ptr_ty_id = try cg.ptrType(elem_ty_id, cg.storageClass(ptr_ty.ptrAddressSpace(zcu))); |
| 6604 | const as = ptr_ty.ptrAddressSpace(zcu); |
| 6605 | const elem_ty_id = try cg.pointeeType(as, ptr_ty.indexableElem(zcu), false); |
| 6606 | const elem_ptr_ty_id = try cg.ptrType(elem_ty_id, cg.storageClass(as)); |
| 6488 | 6607 | if (ptr_ty.isSinglePointer(zcu)) { |
| 6489 | 6608 | // Pointer-to-array. In this case, the resulting pointer is not of the same type |
| 6490 | 6609 | // as the ptr_ty (we want a *T, not a *[N]T), and hence we need to use accessChain. |
| 6491 | | return try cg.accessChainId(elem_ptr_ty_id, ptr_id, &.{index_id}); |
| 6610 | return cg.accessChainId(elem_ptr_ty_id, ptr_id, &.{index_id}); |
| 6492 | 6611 | } else { |
| 6493 | 6612 | // Resulting pointer type is the same as the ptr_ty, so use ptrAccessChain |
| 6494 | | return try cg.ptrAccessChain(elem_ptr_ty_id, ptr_id, index_id, &.{}); |
| 6613 | return cg.ptrAccessChain(elem_ptr_ty_id, ptr_id, index_id, &.{}); |
| 6495 | 6614 | } |
| 6496 | 6615 | } |
| 6497 | 6616 | |
| ... | ... | @@ -7375,7 +7494,10 @@ fn airLoad(cg: *CodeGen, inst: Air.Inst.Index) !?Id { |
| 7375 | 7494 | break :ptr_id try cg.accessChain(elem_ptr_ty_id, operand_ptr_id, &.{@intFromEnum(index)}); |
| 7376 | 7495 | }, |
| 7377 | 7496 | }; |
| 7378 | | return try cg.load(elem_ty, ptr_id, .{ .is_volatile = ptr_info.flags.is_volatile }); |
| 7497 | return try cg.load(elem_ty, ptr_id, .{ |
| 7498 | .is_volatile = ptr_info.flags.is_volatile, |
| 7499 | .ptr_address_space = ptr_info.flags.address_space, |
| 7500 | }); |
| 7379 | 7501 | } |
| 7380 | 7502 | |
| 7381 | 7503 | fn airStore(cg: *CodeGen, inst: Air.Inst.Index) !void { |
| ... | ... | @@ -7447,7 +7569,10 @@ fn airStore(cg: *CodeGen, inst: Air.Inst.Index) !void { |
| 7447 | 7569 | }, |
| 7448 | 7570 | }; |
| 7449 | 7571 | |
| 7450 | | try cg.store(elem_ty, ptr_id, value_id, .{ .is_volatile = ptr_info.flags.is_volatile }); |
| 7572 | try cg.store(elem_ty, ptr_id, value_id, .{ |
| 7573 | .is_volatile = ptr_info.flags.is_volatile, |
| 7574 | .ptr_address_space = ptr_info.flags.address_space, |
| 7575 | }); |
| 7451 | 7576 | } |
| 7452 | 7577 | |
| 7453 | 7578 | fn airRet(cg: *CodeGen, inst: Air.Inst.Index) !void { |