| ... | @@ -52,12 +52,6 @@ const Block = struct { | ... | @@ -52,12 +52,6 @@ const Block = struct { |
| 52 | | 52 | |
| 53 | const BlockMap = std.AutoHashMapUnmanaged(Air.Inst.Index, *Block); | 53 | const BlockMap = std.AutoHashMapUnmanaged(Air.Inst.Index, *Block); |
| 54 | | 54 | |
| 55 | /// Maps Zig decl indices to SPIR-V linking information. | | |
| 56 | pub const DeclLinkMap = std.AutoHashMapUnmanaged(Decl.Index, SpvModule.Decl.Index); | | |
| 57 | | | |
| 58 | /// Maps anon decl indices to SPIR-V linking information. | | |
| 59 | pub const AnonDeclLinkMap = std.AutoHashMapUnmanaged(struct { InternPool.Index, StorageClass }, SpvModule.Decl.Index); | | |
| 60 | | | |
| 61 | /// This structure holds information that is relevant to the entire compilation, | 55 | /// This structure holds information that is relevant to the entire compilation, |
| 62 | /// in contrast to `DeclGen`, which only holds relevant information about a | 56 | /// in contrast to `DeclGen`, which only holds relevant information about a |
| 63 | /// single decl. | 57 | /// single decl. |
| ... | @@ -70,10 +64,10 @@ pub const Object = struct { | ... | @@ -70,10 +64,10 @@ pub const Object = struct { |
| 70 | | 64 | |
| 71 | /// The Zig module that this object file is generated for. | 65 | /// The Zig module that this object file is generated for. |
| 72 | /// A map of Zig decl indices to SPIR-V decl indices. | 66 | /// A map of Zig decl indices to SPIR-V decl indices. |
| 73 | decl_link: DeclLinkMap = .{}, | 67 | decl_link: std.AutoHashMapUnmanaged(Decl.Index, SpvModule.Decl.Index) = .{}, |
| 74 | | 68 | |
| 75 | /// A map of Zig InternPool indices for anonymous decls to SPIR-V decl indices. | 69 | /// A map of Zig InternPool indices for anonymous decls to SPIR-V decl indices. |
| 76 | anon_decl_link: AnonDeclLinkMap = .{}, | 70 | anon_decl_link: std.AutoHashMapUnmanaged(struct { InternPool.Index, StorageClass }, SpvModule.Decl.Index) = .{}, |
| 77 | | 71 | |
| 78 | /// A map that maps AIR intern pool indices to SPIR-V cache references (which | 72 | /// A map that maps AIR intern pool indices to SPIR-V cache references (which |
| 79 | /// is basically the same thing except for SPIR-V). | 73 | /// is basically the same thing except for SPIR-V). |
| ... | @@ -1266,22 +1260,32 @@ const DeclGen = struct { | ... | @@ -1266,22 +1260,32 @@ const DeclGen = struct { |
| 1266 | | 1260 | |
| 1267 | const elem_ty = ty.childType(mod); | 1261 | const elem_ty = ty.childType(mod); |
| 1268 | const elem_ty_ref = try self.resolveType(elem_ty, .indirect); | 1262 | const elem_ty_ref = try self.resolveType(elem_ty, .indirect); |
| 1269 | const total_len = std.math.cast(u32, ty.arrayLenIncludingSentinel(mod)) orelse { | 1263 | var total_len = std.math.cast(u32, ty.arrayLenIncludingSentinel(mod)) orelse { |
| 1270 | return self.fail("array type of {} elements is too large", .{ty.arrayLenIncludingSentinel(mod)}); | 1264 | return self.fail("array type of {} elements is too large", .{ty.arrayLenIncludingSentinel(mod)}); |
| 1271 | }; | 1265 | }; |
| 1272 | if (!ty.hasRuntimeBitsIgnoreComptime(mod)) { | 1266 | const ty_ref = if (!elem_ty.hasRuntimeBitsIgnoreComptime(mod)) blk: { |
| 1273 | // The size of the array would be 0, but that is not allowed in SPIR-V. | 1267 | // The size of the array would be 0, but that is not allowed in SPIR-V. |
| 1274 | // This path can be reached for example when there is a slicing of a pointer | 1268 | // This path can be reached when the backend is asked to generate a pointer to |
| 1275 | // that produces a zero-length array. In all cases where this type can be generated, | 1269 | // an array of some zero-bit type. This should always be an indirect path. |
| 1276 | // we should be in an indirect path (direct uses of this type should be filtered out in Sema). | | |
| 1277 | assert(repr == .indirect); | 1270 | assert(repr == .indirect); |
| 1278 | | 1271 | |
| 1279 | return try self.spv.resolve(.{ .opaque_type = .{ | 1272 | // We cannot use the child type here, so just use an opaque type. |
| | 1273 | break :blk try self.spv.resolve(.{ .opaque_type = .{ |
| 1280 | .name = try self.spv.resolveString("zero-sized array"), | 1274 | .name = try self.spv.resolveString("zero-sized array"), |
| 1281 | } }); | 1275 | } }); |
| 1282 | } | 1276 | } else if (total_len == 0) blk: { |
| | 1277 | // The size of the array would be 0, but that is not allowed in SPIR-V. |
| | 1278 | // This path can be reached for example when there is a slicing of a pointer |
| | 1279 | // that produces a zero-length array. In all cases where this type can be generated, |
| | 1280 | // this should be an indirect path. |
| | 1281 | assert(repr == .indirect); |
| | 1282 | |
| | 1283 | // In this case, we have an array of a non-zero sized type. In this case, |
| | 1284 | // generate an array of 1 element instead, so that ptr_elem_ptr instructions |
| | 1285 | // can be lowered to ptrAccessChain instead of manually performing the math. |
| | 1286 | break :blk try self.spv.arrayType(1, elem_ty_ref); |
| | 1287 | } else try self.spv.arrayType(total_len, elem_ty_ref); |
| 1283 | | 1288 | |
| 1284 | const ty_ref = try self.spv.arrayType(total_len, elem_ty_ref); | | |
| 1285 | try self.type_map.put(self.gpa, ty.toIntern(), .{ .ty_ref = ty_ref }); | 1289 | try self.type_map.put(self.gpa, ty.toIntern(), .{ .ty_ref = ty_ref }); |
| 1286 | return ty_ref; | 1290 | return ty_ref; |
| 1287 | }, | 1291 | }, |
| ... | @@ -2554,38 +2558,85 @@ const DeclGen = struct { | ... | @@ -2554,38 +2558,85 @@ const DeclGen = struct { |
| 2554 | var cmp_lhs_id = lhs_id; | 2558 | var cmp_lhs_id = lhs_id; |
| 2555 | var cmp_rhs_id = rhs_id; | 2559 | var cmp_rhs_id = rhs_id; |
| 2556 | const bool_ty_ref = try self.resolveType(Type.bool, .direct); | 2560 | const bool_ty_ref = try self.resolveType(Type.bool, .direct); |
| 2557 | const opcode: Opcode = opcode: { | 2561 | const op_ty = switch (ty.zigTypeTag(mod)) { |
| 2558 | const op_ty = switch (ty.zigTypeTag(mod)) { | 2562 | .Int, .Bool, .Float => ty, |
| 2559 | .Int, .Bool, .Float => ty, | 2563 | .Enum => ty.intTagType(mod), |
| 2560 | .Enum => ty.intTagType(mod), | 2564 | .ErrorSet => Type.u16, |
| 2561 | .ErrorSet => Type.u16, | 2565 | .Pointer => blk: { |
| 2562 | .Pointer => blk: { | 2566 | // Note that while SPIR-V offers OpPtrEqual and OpPtrNotEqual, they are |
| 2563 | // Note that while SPIR-V offers OpPtrEqual and OpPtrNotEqual, they are | 2567 | // currently not implemented in the SPIR-V LLVM translator. Thus, we emit these using |
| 2564 | // currently not implemented in the SPIR-V LLVM translator. Thus, we emit these using | 2568 | // OpConvertPtrToU... |
| 2565 | // OpConvertPtrToU... | 2569 | cmp_lhs_id = self.spv.allocId(); |
| 2566 | cmp_lhs_id = self.spv.allocId(); | 2570 | cmp_rhs_id = self.spv.allocId(); |
| 2567 | cmp_rhs_id = self.spv.allocId(); | 2571 | |
| 2568 | | 2572 | const usize_ty_id = self.typeId(try self.sizeType()); |
| 2569 | const usize_ty_id = self.typeId(try self.sizeType()); | 2573 | |
| 2570 | | 2574 | try self.func.body.emit(self.spv.gpa, .OpConvertPtrToU, .{ |
| 2571 | try self.func.body.emit(self.spv.gpa, .OpConvertPtrToU, .{ | 2575 | .id_result_type = usize_ty_id, |
| 2572 | .id_result_type = usize_ty_id, | 2576 | .id_result = cmp_lhs_id, |
| 2573 | .id_result = cmp_lhs_id, | 2577 | .pointer = lhs_id, |
| 2574 | .pointer = lhs_id, | 2578 | }); |
| 2575 | }); | | |
| 2576 | | 2579 | |
| 2577 | try self.func.body.emit(self.spv.gpa, .OpConvertPtrToU, .{ | 2580 | try self.func.body.emit(self.spv.gpa, .OpConvertPtrToU, .{ |
| 2578 | .id_result_type = usize_ty_id, | 2581 | .id_result_type = usize_ty_id, |
| 2579 | .id_result = cmp_rhs_id, | 2582 | .id_result = cmp_rhs_id, |
| 2580 | .pointer = rhs_id, | 2583 | .pointer = rhs_id, |
| 2581 | }); | 2584 | }); |
| 2582 | | 2585 | |
| 2583 | break :blk Type.usize; | 2586 | break :blk Type.usize; |
| 2584 | }, | 2587 | }, |
| 2585 | .Optional => unreachable, // TODO | 2588 | .Optional => { |
| 2586 | else => unreachable, | 2589 | const payload_ty = ty.optionalChild(mod); |
| 2587 | }; | 2590 | if (ty.optionalReprIsPayload(mod)) { |
| | 2591 | assert(payload_ty.hasRuntimeBitsIgnoreComptime(mod)); |
| | 2592 | assert(!payload_ty.isSlice(mod)); |
| | 2593 | return self.cmp(op, payload_ty, lhs_id, rhs_id); |
| | 2594 | } |
| | 2595 | |
| | 2596 | const lhs_valid_id = if (payload_ty.hasRuntimeBitsIgnoreComptime(mod)) |
| | 2597 | try self.extractField(Type.bool, lhs_id, 1) |
| | 2598 | else |
| | 2599 | try self.convertToDirect(Type.bool, lhs_id); |
| 2588 | | 2600 | |
| | 2601 | const rhs_valid_id = if (payload_ty.hasRuntimeBitsIgnoreComptime(mod)) |
| | 2602 | try self.extractField(Type.bool, rhs_id, 1) |
| | 2603 | else |
| | 2604 | try self.convertToDirect(Type.bool, rhs_id); |
| | 2605 | |
| | 2606 | const valid_cmp_id = try self.cmp(op, Type.bool, lhs_valid_id, rhs_valid_id); |
| | 2607 | if (!payload_ty.hasRuntimeBitsIgnoreComptime(mod)) { |
| | 2608 | return valid_cmp_id; |
| | 2609 | } |
| | 2610 | |
| | 2611 | // TODO: Should we short circuit here? It shouldn't affect correctness, but |
| | 2612 | // perhaps it will generate more efficient code. |
| | 2613 | |
| | 2614 | const lhs_pl_id = try self.extractField(payload_ty, lhs_id, 0); |
| | 2615 | const rhs_pl_id = try self.extractField(payload_ty, rhs_id, 0); |
| | 2616 | |
| | 2617 | const pl_cmp_id = try self.cmp(op, payload_ty, lhs_pl_id, rhs_pl_id); |
| | 2618 | |
| | 2619 | // op == .eq => lhs_valid == rhs_valid && lhs_pl == rhs_pl |
| | 2620 | // op == .neq => lhs_valid != rhs_valid || lhs_pl != rhs_pl |
| | 2621 | |
| | 2622 | const result_id = self.spv.allocId(); |
| | 2623 | const args = .{ |
| | 2624 | .id_result_type = self.typeId(bool_ty_ref), |
| | 2625 | .id_result = result_id, |
| | 2626 | .operand_1 = valid_cmp_id, |
| | 2627 | .operand_2 = pl_cmp_id, |
| | 2628 | }; |
| | 2629 | switch (op) { |
| | 2630 | .eq => try self.func.body.emit(self.spv.gpa, .OpLogicalAnd, args), |
| | 2631 | .neq => try self.func.body.emit(self.spv.gpa, .OpLogicalOr, args), |
| | 2632 | else => unreachable, |
| | 2633 | } |
| | 2634 | return result_id; |
| | 2635 | }, |
| | 2636 | else => unreachable, |
| | 2637 | }; |
| | 2638 | |
| | 2639 | const opcode: Opcode = opcode: { |
| 2589 | const info = try self.arithmeticTypeInfo(op_ty); | 2640 | const info = try self.arithmeticTypeInfo(op_ty); |
| 2590 | const signedness = switch (info.class) { | 2641 | const signedness = switch (info.class) { |
| 2591 | .composite_integer => { | 2642 | .composite_integer => { |
| ... | @@ -2653,7 +2704,6 @@ const DeclGen = struct { | ... | @@ -2653,7 +2704,6 @@ const DeclGen = struct { |
| 2653 | const lhs_id = try self.resolve(bin_op.lhs); | 2704 | const lhs_id = try self.resolve(bin_op.lhs); |
| 2654 | const rhs_id = try self.resolve(bin_op.rhs); | 2705 | const rhs_id = try self.resolve(bin_op.rhs); |
| 2655 | const ty = self.typeOf(bin_op.lhs); | 2706 | const ty = self.typeOf(bin_op.lhs); |
| 2656 | assert(ty.eql(self.typeOf(bin_op.rhs), self.module)); | | |
| 2657 | | 2707 | |
| 2658 | return try self.cmp(op, ty, lhs_id, rhs_id); | 2708 | return try self.cmp(op, ty, lhs_id, rhs_id); |
| 2659 | } | 2709 | } |
| ... | @@ -3061,16 +3111,17 @@ const DeclGen = struct { | ... | @@ -3061,16 +3111,17 @@ const DeclGen = struct { |
| 3061 | const mod = self.module; | 3111 | const mod = self.module; |
| 3062 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; | 3112 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 3063 | const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; | 3113 | const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| 3064 | const ptr_ty = self.typeOf(bin_op.lhs); | 3114 | const src_ptr_ty = self.typeOf(bin_op.lhs); |
| 3065 | const elem_ty = ptr_ty.childType(mod); | 3115 | const elem_ty = src_ptr_ty.childType(mod); |
| | 3116 | const ptr_id = try self.resolve(bin_op.lhs); |
| | 3117 | |
| 3066 | if (!elem_ty.hasRuntimeBitsIgnoreComptime(mod)) { | 3118 | if (!elem_ty.hasRuntimeBitsIgnoreComptime(mod)) { |
| 3067 | const ptr_ty_ref = try self.resolveType(ptr_ty, .direct); | 3119 | const dst_ptr_ty = self.typeOfIndex(inst); |
| 3068 | return try self.spv.constUndef(ptr_ty_ref); | 3120 | return try self.bitCast(dst_ptr_ty, src_ptr_ty, ptr_id); |
| 3069 | } | 3121 | } |
| 3070 | | 3122 | |
| 3071 | const ptr_id = try self.resolve(bin_op.lhs); | | |
| 3072 | const index_id = try self.resolve(bin_op.rhs); | 3123 | const index_id = try self.resolve(bin_op.rhs); |
| 3073 | return try self.ptrElemPtr(ptr_ty, ptr_id, index_id); | 3124 | return try self.ptrElemPtr(src_ptr_ty, ptr_id, index_id); |
| 3074 | } | 3125 | } |
| 3075 | | 3126 | |
| 3076 | fn airArrayElemVal(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { | 3127 | fn airArrayElemVal(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { |