diff --git a/src/codegen/spirv.zig b/src/codegen/spirv.zig index 79f8171b4e..00d7fa94f9 100644 --- a/src/codegen/spirv.zig +++ b/src/codegen/spirv.zig @@ -629,6 +629,16 @@ const DeclGen = struct { return self.backingIntBits(ty) == null; } + /// Checks whether the type can be directly translated to SPIR-V vectors + fn isVector(self: *DeclGen, ty: Type) bool { + const mod = self.module; + if (ty.zigTypeTag(mod) != .Vector) return false; + const elem_ty = ty.childType(mod); + const len = ty.vectorLen(mod); + const is_scalar = elem_ty.isNumeric(mod) or elem_ty.toIntern() == .bool_type; + return is_scalar and len > 1 and len <= 4; + } + fn arithmeticTypeInfo(self: *DeclGen, ty: Type) ArithmeticTypeInfo { const mod = self.module; const target = self.getTarget(); @@ -694,6 +704,24 @@ const DeclGen = struct { /// This function, unlike SpvModule.constInt, takes care to bitcast /// the value to an unsigned int first for Kernels. fn constInt(self: *DeclGen, ty_ref: CacheRef, value: anytype) !IdRef { + switch (self.spv.cache.lookup(ty_ref)) { + .vector_type => |vec_type| { + const elem_ids = try self.gpa.alloc(IdRef, vec_type.component_count); + defer self.gpa.free(elem_ids); + const int_value = try self.constInt(vec_type.component_type, value); + @memset(elem_ids, int_value); + + const constituents_id = self.spv.allocId(); + try self.func.body.emit(self.spv.gpa, .OpCompositeConstruct, .{ + .id_result_type = self.typeId(ty_ref), + .id_result = constituents_id, + .constituents = elem_ids, + }); + return constituents_id; + }, + else => {}, + } + if (value < 0) { const ty = self.spv.cache.lookup(ty_ref).int_type; // Manually truncate the value so that the resulting value @@ -711,6 +739,24 @@ const DeclGen = struct { /// Emits a float constant fn constFloat(self: *DeclGen, ty_ref: CacheRef, value: f128) !IdRef { + switch (self.spv.cache.lookup(ty_ref)) { + .vector_type => |vec_type| { + const elem_ids = try self.gpa.alloc(IdRef, vec_type.component_count); + defer self.gpa.free(elem_ids); + const int_value = try self.constFloat(vec_type.component_type, value); + @memset(elem_ids, int_value); + + const constituents_id = self.spv.allocId(); + try self.func.body.emit(self.spv.gpa, .OpCompositeConstruct, .{ + .id_result_type = self.typeId(ty_ref), + .id_result = constituents_id, + .constituents = elem_ids, + }); + return constituents_id; + }, + else => {}, + } + const ty = self.spv.cache.lookup(ty_ref).float_type; return switch (ty.bits) { 16 => try self.spv.resolveId(.{ .float = .{ .ty = ty_ref, .value = .{ .float16 = @floatCast(value) } } }), @@ -726,9 +772,9 @@ const DeclGen = struct { /// if the parameters are in indirect representation, then the result is too. fn constructComposite(self: *DeclGen, ty: Type, constituents: []const IdRef) !IdRef { const constituents_id = self.spv.allocId(); - const type_id = try self.resolveTypeId(ty); + const type_id = try self.resolveType(ty, .direct); try self.func.body.emit(self.spv.gpa, .OpCompositeConstruct, .{ - .id_result_type = type_id, + .id_result_type = self.typeId(type_id), .id_result = constituents_id, .constituents = constituents, }); @@ -1448,12 +1494,11 @@ const DeclGen = struct { const elem_ty = ty.childType(mod); const elem_ty_ref = try self.resolveType(elem_ty, .indirect); const len = ty.vectorLen(mod); - const is_scalar = elem_ty.isNumeric(mod) or elem_ty.toIntern() == .bool_type; - const ty_ref = if (is_scalar and len > 1 and len <= 4) - try self.spv.vectorType(ty.vectorLen(mod), elem_ty_ref) + const ty_ref = if (self.isVector(ty)) + try self.spv.vectorType(len, elem_ty_ref) else - try self.spv.arrayType(ty.vectorLen(mod), elem_ty_ref); + try self.spv.arrayType(len, elem_ty_ref); try self.type_map.put(self.gpa, ty.toIntern(), .{ .ty_ref = ty_ref }); return ty_ref; @@ -1752,18 +1797,16 @@ const DeclGen = struct { } /// This structure is used as helper for element-wise operations. It is intended - /// to be used with both vectors and single elements. + /// to be used with vectors, fake vectors (arrays) and single elements. const WipElementWise = struct { dg: *DeclGen, result_ty: Type, + ty: Type, /// Always in direct representation. - result_ty_ref: CacheRef, - scalar_ty: Type, - /// Always in direct representation. - scalar_ty_ref: CacheRef, - scalar_ty_id: IdRef, - /// True if the input is actually a vector type. - is_vector: bool, + ty_ref: CacheRef, + ty_id: IdRef, + /// True if the input is an array type. + is_array: bool, /// The element-wise operation should fill these results before calling finalize(). /// These should all be in **direct** representation! `finalize()` will convert /// them to indirect if required. @@ -1774,29 +1817,28 @@ const DeclGen = struct { } /// Utility function to extract the element at a particular index in an - /// input vector. This type is expected to be a vector if `wip.is_vector`, and - /// a scalar otherwise. + /// input array. This type is expected to be a fake vector (array) if `wip.is_array`, and + /// a vector or scalar otherwise. fn elementAt(wip: WipElementWise, ty: Type, value: IdRef, index: usize) !IdRef { const mod = wip.dg.module; - if (wip.is_vector) { + if (wip.is_array) { assert(ty.isVector(mod)); return try wip.dg.extractField(ty.childType(mod), value, @intCast(index)); } else { - assert(!ty.isVector(mod)); assert(index == 0); return value; } } - /// Turns the results of this WipElementWise into a result. This can either - /// be a vector or single element, depending on `result_ty`. + /// Turns the results of this WipElementWise into a result. This can be + /// vectors, fake vectors (arrays) and single elements, depending on `result_ty`. /// After calling this function, this WIP is no longer usable. /// Results is in `direct` representation. fn finalize(wip: *WipElementWise) !IdRef { - if (wip.is_vector) { + if (wip.is_array) { // Convert all the constituents to indirect, as required for the array. for (wip.results) |*result| { - result.* = try wip.dg.convertToIndirect(wip.scalar_ty, result.*); + result.* = try wip.dg.convertToIndirect(wip.ty, result.*); } return try wip.dg.constructComposite(wip.result_ty, wip.results); } else { @@ -1806,33 +1848,30 @@ const DeclGen = struct { /// Allocate a result id at a particular index, and return it. fn allocId(wip: *WipElementWise, index: usize) IdRef { - assert(wip.is_vector or index == 0); + assert(wip.is_array or index == 0); wip.results[index] = wip.dg.spv.allocId(); return wip.results[index]; } }; /// Create a new element-wise operation. - fn elementWise(self: *DeclGen, result_ty: Type) !WipElementWise { + fn elementWise(self: *DeclGen, result_ty: Type, force_element_wise: bool) !WipElementWise { const mod = self.module; - // For now, this operation also reasons in terms of `.direct` representation. - const result_ty_ref = try self.resolveType(result_ty, .direct); - const is_vector = result_ty.isVector(mod); - const num_results = if (is_vector) result_ty.vectorLen(mod) else 1; + const is_array = result_ty.isVector(mod) and (!self.isVector(result_ty) or force_element_wise); + const num_results = if (is_array) result_ty.vectorLen(mod) else 1; const results = try self.gpa.alloc(IdRef, num_results); - for (results) |*result| result.* = undefined; + @memset(results, undefined); - const scalar_ty = result_ty.scalarType(mod); - const scalar_ty_ref = try self.resolveType(scalar_ty, .direct); + const ty = if (is_array) result_ty.scalarType(mod) else result_ty; + const ty_ref = try self.resolveType(ty, .direct); return .{ .dg = self, .result_ty = result_ty, - .result_ty_ref = result_ty_ref, - .scalar_ty = scalar_ty, - .scalar_ty_ref = scalar_ty_ref, - .scalar_ty_id = self.typeId(scalar_ty_ref), - .is_vector = is_vector, + .ty = ty, + .ty_ref = ty_ref, + .ty_id = self.typeId(ty_ref), + .is_array = is_array, .results = results, }; } @@ -2160,7 +2199,6 @@ const DeclGen = struct { fn genInst(self: *DeclGen, inst: Air.Inst.Index) !void { const mod = self.module; const ip = &mod.intern_pool; - // TODO: remove now-redundant isUnused calls from AIR handler functions if (self.liveness.isUnused(inst) and !self.air.mustLower(inst, ip)) return; @@ -2312,11 +2350,11 @@ const DeclGen = struct { } fn binOpSimple(self: *DeclGen, ty: Type, lhs_id: IdRef, rhs_id: IdRef, comptime opcode: Opcode) !IdRef { - var wip = try self.elementWise(ty); + var wip = try self.elementWise(ty, false); defer wip.deinit(); for (0..wip.results.len) |i| { try self.func.body.emit(self.spv.gpa, opcode, .{ - .id_result_type = wip.scalar_ty_id, + .id_result_type = wip.ty_id, .id_result = wip.allocId(i), .operand_1 = try wip.elementAt(ty, lhs_id, i), .operand_2 = try wip.elementAt(ty, rhs_id, i), @@ -2326,8 +2364,6 @@ const DeclGen = struct { } fn airBinOpSimple(self: *DeclGen, inst: Air.Inst.Index, comptime opcode: Opcode) !?IdRef { - if (self.liveness.isUnused(inst)) return null; - const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; const lhs_id = try self.resolve(bin_op.lhs); const rhs_id = try self.resolve(bin_op.rhs); @@ -2337,7 +2373,6 @@ const DeclGen = struct { } fn airShift(self: *DeclGen, inst: Air.Inst.Index, comptime unsigned: Opcode, comptime signed: Opcode) !?IdRef { - if (self.liveness.isUnused(inst)) return null; const mod = self.module; const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; const lhs_id = try self.resolve(bin_op.lhs); @@ -2345,7 +2380,7 @@ const DeclGen = struct { const result_ty = self.typeOfIndex(inst); const shift_ty = self.typeOf(bin_op.rhs); - const scalar_shift_ty_ref = try self.resolveType(shift_ty.scalarType(mod), .direct); + const shift_ty_ref = try self.resolveType(shift_ty, .direct); const info = self.arithmeticTypeInfo(result_ty); switch (info.class) { @@ -2354,7 +2389,7 @@ const DeclGen = struct { .float, .bool => unreachable, } - var wip = try self.elementWise(result_ty); + var wip = try self.elementWise(result_ty, false); defer wip.deinit(); for (wip.results, 0..) |*result_id, i| { const lhs_elem_id = try wip.elementAt(result_ty, lhs_id, i); @@ -2362,10 +2397,10 @@ const DeclGen = struct { // Sometimes Zig doesn't make both of the arguments the same types here. SPIR-V expects that, // so just manually upcast it if required. - const shift_id = if (scalar_shift_ty_ref != wip.scalar_ty_ref) blk: { + const shift_id = if (shift_ty_ref != wip.ty_ref) blk: { const shift_id = self.spv.allocId(); try self.func.body.emit(self.spv.gpa, .OpUConvert, .{ - .id_result_type = wip.scalar_ty_id, + .id_result_type = wip.ty_id, .id_result = shift_id, .unsigned_value = rhs_elem_id, }); @@ -2374,7 +2409,7 @@ const DeclGen = struct { const value_id = self.spv.allocId(); const args = .{ - .id_result_type = wip.scalar_ty_id, + .id_result_type = wip.ty_id, .id_result = value_id, .base = lhs_elem_id, .shift = shift_id, @@ -2386,14 +2421,12 @@ const DeclGen = struct { try self.func.body.emit(self.spv.gpa, unsigned, args); } - result_id.* = try self.normalize(wip.scalar_ty_ref, value_id, info); + result_id.* = try self.normalize(wip.ty_ref, value_id, info); } return try wip.finalize(); } fn airMinMax(self: *DeclGen, inst: Air.Inst.Index, op: std.math.CompareOperator) !?IdRef { - if (self.liveness.isUnused(inst)) return null; - const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; const lhs_id = try self.resolve(bin_op.lhs); const rhs_id = try self.resolve(bin_op.rhs); @@ -2405,14 +2438,14 @@ const DeclGen = struct { fn minMax(self: *DeclGen, result_ty: Type, op: std.math.CompareOperator, lhs_id: IdRef, rhs_id: IdRef) !IdRef { const info = self.arithmeticTypeInfo(result_ty); - var wip = try self.elementWise(result_ty); + var wip = try self.elementWise(result_ty, true); defer wip.deinit(); for (wip.results, 0..) |*result_id, i| { const lhs_elem_id = try wip.elementAt(result_ty, lhs_id, i); const rhs_elem_id = try wip.elementAt(result_ty, rhs_id, i); // TODO: Use fmin for OpenCL - const cmp_id = try self.cmp(op, Type.bool, wip.scalar_ty, lhs_elem_id, rhs_elem_id); + const cmp_id = try self.cmp(op, Type.bool, wip.ty, lhs_elem_id, rhs_elem_id); const selection_id = switch (info.class) { .float => blk: { // cmp uses OpFOrd. When we have 0 [<>] nan this returns false, @@ -2440,7 +2473,7 @@ const DeclGen = struct { result_id.* = self.spv.allocId(); try self.func.body.emit(self.spv.gpa, .OpSelect, .{ - .id_result_type = wip.scalar_ty_id, + .id_result_type = wip.ty_id, .id_result = result_id.*, .condition = selection_id, .object_1 = lhs_elem_id, @@ -2505,7 +2538,6 @@ const DeclGen = struct { comptime sop: Opcode, comptime uop: Opcode, ) !?IdRef { - if (self.liveness.isUnused(inst)) return null; // LHS and RHS are guaranteed to have the same type, and AIR guarantees // the result to be the same as the LHS and RHS, which matches SPIR-V. @@ -2545,7 +2577,7 @@ const DeclGen = struct { .bool => unreachable, }; - var wip = try self.elementWise(ty); + var wip = try self.elementWise(ty, false); defer wip.deinit(); for (wip.results, 0..) |*result_id, i| { const lhs_elem_id = try wip.elementAt(ty, lhs_id, i); @@ -2553,7 +2585,7 @@ const DeclGen = struct { const value_id = self.spv.allocId(); const operands = .{ - .id_result_type = wip.scalar_ty_id, + .id_result_type = wip.ty_id, .id_result = value_id, .operand_1 = lhs_elem_id, .operand_2 = rhs_elem_id, @@ -2568,26 +2600,24 @@ const DeclGen = struct { // TODO: Trap on overflow? Probably going to be annoying. // TODO: Look into SPV_KHR_no_integer_wrap_decoration which provides NoSignedWrap/NoUnsignedWrap. - result_id.* = try self.normalize(wip.scalar_ty_ref, value_id, info); + result_id.* = try self.normalize(wip.ty_ref, value_id, info); } return try wip.finalize(); } fn airAbs(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { - if (self.liveness.isUnused(inst)) return null; - const mod = self.module; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const operand_id = try self.resolve(ty_op.operand); // Note: operand_ty may be signed, while ty is always unsigned! const operand_ty = self.typeOf(ty_op.operand); - const ty = self.typeOfIndex(inst); - const info = self.arithmeticTypeInfo(ty); + const result_ty = self.typeOfIndex(inst); + const info = self.arithmeticTypeInfo(result_ty); const operand_scalar_ty = operand_ty.scalarType(mod); const operand_scalar_ty_ref = try self.resolveType(operand_scalar_ty, .direct); - var wip = try self.elementWise(ty); + var wip = try self.elementWise(result_ty, true); defer wip.deinit(); const zero_id = switch (info.class) { @@ -2615,7 +2645,7 @@ const DeclGen = struct { .composite_integer => unreachable, // TODO .bool => unreachable, } - const neg_norm_id = try self.normalize(wip.scalar_ty_ref, neg_id, info); + const neg_norm_id = try self.normalize(wip.ty_ref, neg_id, info); const gt_zero_id = try self.cmp(.gt, Type.bool, operand_scalar_ty, elem_id, zero_id); const abs_id = self.spv.allocId(); @@ -2627,7 +2657,7 @@ const DeclGen = struct { .object_2 = neg_norm_id, }); // For Shader, we may need to cast from signed to unsigned here. - result_id.* = try self.bitCast(wip.scalar_ty, operand_scalar_ty, abs_id); + result_id.* = try self.bitCast(wip.ty, operand_scalar_ty, abs_id); } return try wip.finalize(); } @@ -2639,8 +2669,7 @@ const DeclGen = struct { comptime ucmp: Opcode, comptime scmp: Opcode, ) !?IdRef { - if (self.liveness.isUnused(inst)) return null; - + const mod = self.module; const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; const lhs = try self.resolve(extra.lhs); @@ -2651,6 +2680,10 @@ const DeclGen = struct { const ov_ty = result_ty.structFieldType(1, self.module); const bool_ty_ref = try self.resolveType(Type.bool, .direct); + const cmp_ty_ref = if (self.isVector(operand_ty)) + try self.spv.vectorType(operand_ty.vectorLen(mod), bool_ty_ref) + else + bool_ty_ref; const info = self.arithmeticTypeInfo(operand_ty); switch (info.class) { @@ -2659,9 +2692,9 @@ const DeclGen = struct { .float, .bool => unreachable, } - var wip_result = try self.elementWise(operand_ty); + var wip_result = try self.elementWise(operand_ty, false); defer wip_result.deinit(); - var wip_ov = try self.elementWise(ov_ty); + var wip_ov = try self.elementWise(ov_ty, false); defer wip_ov.deinit(); for (wip_result.results, wip_ov.results, 0..) |*result_id, *ov_id, i| { const lhs_elem_id = try wip_result.elementAt(operand_ty, lhs, i); @@ -2671,14 +2704,14 @@ const DeclGen = struct { const value_id = self.spv.allocId(); try self.func.body.emit(self.spv.gpa, add, .{ - .id_result_type = wip_result.scalar_ty_id, + .id_result_type = wip_result.ty_id, .id_result = value_id, .operand_1 = lhs_elem_id, .operand_2 = rhs_elem_id, }); // Normalize the result so that the comparisons go well - result_id.* = try self.normalize(wip_result.scalar_ty_ref, value_id, info); + result_id.* = try self.normalize(wip_result.ty_ref, value_id, info); const overflowed_id = switch (info.signedness) { .unsigned => blk: { @@ -2686,7 +2719,7 @@ const DeclGen = struct { // For subtraction the conditions need to be swapped. const overflowed_id = self.spv.allocId(); try self.func.body.emit(self.spv.gpa, ucmp, .{ - .id_result_type = self.typeId(bool_ty_ref), + .id_result_type = self.typeId(cmp_ty_ref), .id_result = overflowed_id, .operand_1 = result_id.*, .operand_2 = lhs_elem_id, @@ -2712,9 +2745,9 @@ const DeclGen = struct { // = (rhs < 0) == (lhs > value) const rhs_lt_zero_id = self.spv.allocId(); - const zero_id = try self.constInt(wip_result.scalar_ty_ref, 0); + const zero_id = try self.constInt(wip_result.ty_ref, 0); try self.func.body.emit(self.spv.gpa, .OpSLessThan, .{ - .id_result_type = self.typeId(bool_ty_ref), + .id_result_type = self.typeId(cmp_ty_ref), .id_result = rhs_lt_zero_id, .operand_1 = rhs_elem_id, .operand_2 = zero_id, @@ -2722,7 +2755,7 @@ const DeclGen = struct { const value_gt_lhs_id = self.spv.allocId(); try self.func.body.emit(self.spv.gpa, scmp, .{ - .id_result_type = self.typeId(bool_ty_ref), + .id_result_type = self.typeId(cmp_ty_ref), .id_result = value_gt_lhs_id, .operand_1 = lhs_elem_id, .operand_2 = result_id.*, @@ -2730,7 +2763,7 @@ const DeclGen = struct { const overflowed_id = self.spv.allocId(); try self.func.body.emit(self.spv.gpa, .OpLogicalEqual, .{ - .id_result_type = self.typeId(bool_ty_ref), + .id_result_type = self.typeId(cmp_ty_ref), .id_result = overflowed_id, .operand_1 = rhs_lt_zero_id, .operand_2 = value_gt_lhs_id, @@ -2739,7 +2772,7 @@ const DeclGen = struct { }, }; - ov_id.* = try self.intFromBool(wip_ov.scalar_ty_ref, overflowed_id); + ov_id.* = try self.intFromBool(wip_ov.ty_ref, overflowed_id); } return try self.constructComposite( @@ -2749,7 +2782,6 @@ const DeclGen = struct { } fn airShlOverflow(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { - if (self.liveness.isUnused(inst)) return null; const mod = self.module; const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; @@ -2759,11 +2791,15 @@ const DeclGen = struct { const result_ty = self.typeOfIndex(inst); const operand_ty = self.typeOf(extra.lhs); const shift_ty = self.typeOf(extra.rhs); - const scalar_shift_ty_ref = try self.resolveType(shift_ty.scalarType(mod), .direct); + const shift_ty_ref = try self.resolveType(shift_ty, .direct); const ov_ty = result_ty.structFieldType(1, self.module); const bool_ty_ref = try self.resolveType(Type.bool, .direct); + const cmp_ty_ref = if (self.isVector(operand_ty)) + try self.spv.vectorType(operand_ty.vectorLen(mod), bool_ty_ref) + else + bool_ty_ref; const info = self.arithmeticTypeInfo(operand_ty); switch (info.class) { @@ -2772,9 +2808,9 @@ const DeclGen = struct { .float, .bool => unreachable, } - var wip_result = try self.elementWise(operand_ty); + var wip_result = try self.elementWise(operand_ty, false); defer wip_result.deinit(); - var wip_ov = try self.elementWise(ov_ty); + var wip_ov = try self.elementWise(ov_ty, false); defer wip_ov.deinit(); for (wip_result.results, wip_ov.results, 0..) |*result_id, *ov_id, i| { const lhs_elem_id = try wip_result.elementAt(operand_ty, lhs, i); @@ -2782,10 +2818,10 @@ const DeclGen = struct { // Sometimes Zig doesn't make both of the arguments the same types here. SPIR-V expects that, // so just manually upcast it if required. - const shift_id = if (scalar_shift_ty_ref != wip_result.scalar_ty_ref) blk: { + const shift_id = if (shift_ty_ref != wip_result.ty_ref) blk: { const shift_id = self.spv.allocId(); try self.func.body.emit(self.spv.gpa, .OpUConvert, .{ - .id_result_type = wip_result.scalar_ty_id, + .id_result_type = wip_result.ty_id, .id_result = shift_id, .unsigned_value = rhs_elem_id, }); @@ -2794,18 +2830,18 @@ const DeclGen = struct { const value_id = self.spv.allocId(); try self.func.body.emit(self.spv.gpa, .OpShiftLeftLogical, .{ - .id_result_type = wip_result.scalar_ty_id, + .id_result_type = wip_result.ty_id, .id_result = value_id, .base = lhs_elem_id, .shift = shift_id, }); - result_id.* = try self.normalize(wip_result.scalar_ty_ref, value_id, info); + result_id.* = try self.normalize(wip_result.ty_ref, value_id, info); const right_shift_id = self.spv.allocId(); switch (info.signedness) { .signed => { try self.func.body.emit(self.spv.gpa, .OpShiftRightArithmetic, .{ - .id_result_type = wip_result.scalar_ty_id, + .id_result_type = wip_result.ty_id, .id_result = right_shift_id, .base = result_id.*, .shift = shift_id, @@ -2813,7 +2849,7 @@ const DeclGen = struct { }, .unsigned => { try self.func.body.emit(self.spv.gpa, .OpShiftRightLogical, .{ - .id_result_type = wip_result.scalar_ty_id, + .id_result_type = wip_result.ty_id, .id_result = right_shift_id, .base = result_id.*, .shift = shift_id, @@ -2823,13 +2859,13 @@ const DeclGen = struct { const overflowed_id = self.spv.allocId(); try self.func.body.emit(self.spv.gpa, .OpINotEqual, .{ - .id_result_type = self.typeId(bool_ty_ref), + .id_result_type = self.typeId(cmp_ty_ref), .id_result = overflowed_id, .operand_1 = lhs_elem_id, .operand_2 = right_shift_id, }); - ov_id.* = try self.intFromBool(wip_ov.scalar_ty_ref, overflowed_id); + ov_id.* = try self.intFromBool(wip_ov.ty_ref, overflowed_id); } return try self.constructComposite( @@ -2839,8 +2875,6 @@ const DeclGen = struct { } fn airMulAdd(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { - if (self.liveness.isUnused(inst)) return null; - const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; const extra = self.air.extraData(Air.Bin, pl_op.payload).data; @@ -2853,19 +2887,19 @@ const DeclGen = struct { const info = self.arithmeticTypeInfo(ty); assert(info.class == .float); // .mul_add is only emitted for floats - var wip = try self.elementWise(ty); + var wip = try self.elementWise(ty, false); defer wip.deinit(); for (0..wip.results.len) |i| { const mul_result = self.spv.allocId(); try self.func.body.emit(self.spv.gpa, .OpFMul, .{ - .id_result_type = wip.scalar_ty_id, + .id_result_type = wip.ty_id, .id_result = mul_result, .operand_1 = try wip.elementAt(ty, mulend1, i), .operand_2 = try wip.elementAt(ty, mulend2, i), }); try self.func.body.emit(self.spv.gpa, .OpFAdd, .{ - .id_result_type = wip.scalar_ty_id, + .id_result_type = wip.ty_id, .id_result = wip.allocId(i), .operand_1 = mul_result, .operand_2 = try wip.elementAt(ty, addend, i), @@ -2875,20 +2909,16 @@ const DeclGen = struct { } fn airSplat(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { - if (self.liveness.isUnused(inst)) return null; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const operand_id = try self.resolve(ty_op.operand); const result_ty = self.typeOfIndex(inst); - var wip = try self.elementWise(result_ty); + var wip = try self.elementWise(result_ty, true); defer wip.deinit(); - for (wip.results) |*result_id| { - result_id.* = operand_id; - } + @memset(wip.results, operand_id); return try wip.finalize(); } fn airReduce(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { - if (self.liveness.isUnused(inst)) return null; const mod = self.module; const reduce = self.air.instructions.items(.data)[@intFromEnum(inst)].reduce; const operand = try self.resolve(reduce.operand); @@ -2956,7 +2986,6 @@ const DeclGen = struct { fn airShuffle(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { const mod = self.module; - if (self.liveness.isUnused(inst)) return null; const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const extra = self.air.extraData(Air.Shuffle, ty_pl.payload).data; const a = try self.resolve(extra.a); @@ -2965,20 +2994,20 @@ const DeclGen = struct { const ty = self.typeOfIndex(inst); - var wip = try self.elementWise(ty); + var wip = try self.elementWise(ty, true); defer wip.deinit(); for (wip.results, 0..) |*result_id, i| { const elem = try mask.elemValue(mod, i); if (elem.isUndef(mod)) { - result_id.* = try self.spv.constUndef(wip.scalar_ty_ref); + result_id.* = try self.spv.constUndef(wip.ty_ref); continue; } const index = elem.toSignedInt(mod); if (index >= 0) { - result_id.* = try self.extractField(wip.scalar_ty, a, @intCast(index)); + result_id.* = try self.extractField(wip.ty, a, @intCast(index)); } else { - result_id.* = try self.extractField(wip.scalar_ty, b, @intCast(~index)); + result_id.* = try self.extractField(wip.ty, b, @intCast(~index)); } } return try wip.finalize(); @@ -3069,7 +3098,6 @@ const DeclGen = struct { } fn airPtrAdd(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { - if (self.liveness.isUnused(inst)) return null; const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; const ptr_id = try self.resolve(bin_op.lhs); @@ -3081,7 +3109,6 @@ const DeclGen = struct { } fn airPtrSub(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { - if (self.liveness.isUnused(inst)) return null; const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; const ptr_id = try self.resolve(bin_op.lhs); @@ -3188,7 +3215,7 @@ const DeclGen = struct { return result_id; }, .Vector => { - var wip = try self.elementWise(result_ty); + var wip = try self.elementWise(result_ty, true); defer wip.deinit(); const scalar_ty = ty.scalarType(mod); for (wip.results, 0..) |*result_id, i| { @@ -3257,7 +3284,6 @@ const DeclGen = struct { inst: Air.Inst.Index, comptime op: std.math.CompareOperator, ) !?IdRef { - if (self.liveness.isUnused(inst)) return null; const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; const lhs_id = try self.resolve(bin_op.lhs); const rhs_id = try self.resolve(bin_op.rhs); @@ -3268,8 +3294,6 @@ const DeclGen = struct { } fn airVectorCmp(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { - if (self.liveness.isUnused(inst)) return null; - const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const vec_cmp = self.air.extraData(Air.VectorCmp, ty_pl.payload).data; const lhs_id = try self.resolve(vec_cmp.lhs); @@ -3351,7 +3375,6 @@ const DeclGen = struct { } fn airBitCast(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { - if (self.liveness.isUnused(inst)) return null; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const operand_id = try self.resolve(ty_op.operand); const operand_ty = self.typeOf(ty_op.operand); @@ -3360,8 +3383,6 @@ const DeclGen = struct { } fn airIntCast(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { - if (self.liveness.isUnused(inst)) return null; - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const operand_id = try self.resolve(ty_op.operand); const src_ty = self.typeOf(ty_op.operand); @@ -3374,19 +3395,19 @@ const DeclGen = struct { return operand_id; } - var wip = try self.elementWise(dst_ty); + var wip = try self.elementWise(dst_ty, false); defer wip.deinit(); for (wip.results, 0..) |*result_id, i| { const elem_id = try wip.elementAt(src_ty, operand_id, i); const value_id = self.spv.allocId(); switch (dst_info.signedness) { .signed => try self.func.body.emit(self.spv.gpa, .OpSConvert, .{ - .id_result_type = wip.scalar_ty_id, + .id_result_type = wip.ty_id, .id_result = value_id, .signed_value = elem_id, }), .unsigned => try self.func.body.emit(self.spv.gpa, .OpUConvert, .{ - .id_result_type = wip.scalar_ty_id, + .id_result_type = wip.ty_id, .id_result = value_id, .unsigned_value = elem_id, }), @@ -3397,7 +3418,7 @@ const DeclGen = struct { // type, we don't need to normalize when growing the type. The // representation is already the same. if (dst_info.bits < src_info.bits) { - result_id.* = try self.normalize(wip.scalar_ty_ref, value_id, dst_info); + result_id.* = try self.normalize(wip.ty_ref, value_id, dst_info); } else { result_id.* = value_id; } @@ -3417,16 +3438,12 @@ const DeclGen = struct { } fn airIntFromPtr(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { - if (self.liveness.isUnused(inst)) return null; - const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; const operand_id = try self.resolve(un_op); return try self.intFromPtr(operand_id); } fn airFloatFromInt(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { - if (self.liveness.isUnused(inst)) return null; - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const operand_ty = self.typeOf(ty_op.operand); const operand_id = try self.resolve(ty_op.operand); @@ -3451,8 +3468,6 @@ const DeclGen = struct { } fn airIntFromFloat(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { - if (self.liveness.isUnused(inst)) return null; - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const operand_id = try self.resolve(ty_op.operand); const dest_ty = self.typeOfIndex(inst); @@ -3476,24 +3491,20 @@ const DeclGen = struct { } fn airIntFromBool(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { - if (self.liveness.isUnused(inst)) return null; - const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; const operand_id = try self.resolve(un_op); const result_ty = self.typeOfIndex(inst); - var wip = try self.elementWise(result_ty); + var wip = try self.elementWise(result_ty, false); defer wip.deinit(); for (wip.results, 0..) |*result_id, i| { const elem_id = try wip.elementAt(Type.bool, operand_id, i); - result_id.* = try self.intFromBool(wip.scalar_ty_ref, elem_id); + result_id.* = try self.intFromBool(wip.ty_ref, elem_id); } return try wip.finalize(); } fn airFloatCast(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { - if (self.liveness.isUnused(inst)) return null; - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const operand_id = try self.resolve(ty_op.operand); const dest_ty = self.typeOfIndex(inst); @@ -3509,18 +3520,17 @@ const DeclGen = struct { } fn airNot(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { - if (self.liveness.isUnused(inst)) return null; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const operand_id = try self.resolve(ty_op.operand); const result_ty = self.typeOfIndex(inst); const info = self.arithmeticTypeInfo(result_ty); - var wip = try self.elementWise(result_ty); + var wip = try self.elementWise(result_ty, false); defer wip.deinit(); for (0..wip.results.len) |i| { const args = .{ - .id_result_type = wip.scalar_ty_id, + .id_result_type = wip.ty_id, .id_result = wip.allocId(i), .operand = try wip.elementAt(result_ty, operand_id, i), }; @@ -3541,8 +3551,6 @@ const DeclGen = struct { } fn airArrayToSlice(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { - if (self.liveness.isUnused(inst)) return null; - const mod = self.module; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const array_ptr_ty = self.typeOf(ty_op.operand); @@ -3563,15 +3571,10 @@ const DeclGen = struct { // Convert the pointer-to-array to a pointer to the first element. try self.accessChain(elem_ptr_ty_ref, array_ptr_id, &.{0}); - return try self.constructComposite( - slice_ty, - &.{ elem_ptr_id, len_id }, - ); + return try self.constructComposite(slice_ty, &.{ elem_ptr_id, len_id }); } fn airSlice(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { - if (self.liveness.isUnused(inst)) return null; - const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; const ptr_id = try self.resolve(bin_op.lhs); @@ -3580,15 +3583,10 @@ const DeclGen = struct { // Note: Types should not need to be converted to direct, these types // dont need to be converted. - return try self.constructComposite( - slice_ty, - &.{ ptr_id, len_id }, - ); + return try self.constructComposite(slice_ty, &.{ ptr_id, len_id }); } fn airAggregateInit(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { - if (self.liveness.isUnused(inst)) return null; - const mod = self.module; const ip = &mod.intern_pool; const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; @@ -3710,7 +3708,6 @@ const DeclGen = struct { } fn airSliceField(self: *DeclGen, inst: Air.Inst.Index, field: u32) !?IdRef { - if (self.liveness.isUnused(inst)) return null; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const field_ty = self.typeOfIndex(inst); const operand_id = try self.resolve(ty_op.operand); @@ -3767,8 +3764,6 @@ const DeclGen = struct { } fn airPtrElemPtr(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { - if (self.liveness.isUnused(inst)) return null; - const mod = self.module; const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; @@ -3786,8 +3781,6 @@ const DeclGen = struct { } fn airArrayElemVal(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { - if (self.liveness.isUnused(inst)) return null; - const mod = self.module; const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; const array_ty = self.typeOf(bin_op.lhs); @@ -3808,8 +3801,6 @@ const DeclGen = struct { } fn airPtrElemVal(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { - if (self.liveness.isUnused(inst)) return null; - const mod = self.module; const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; const ptr_ty = self.typeOf(bin_op.lhs); @@ -3866,8 +3857,6 @@ const DeclGen = struct { } fn airGetUnionTag(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { - if (self.liveness.isUnused(inst)) return null; - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const un_ty = self.typeOf(ty_op.operand); @@ -3951,8 +3940,6 @@ const DeclGen = struct { } fn airUnionInit(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { - if (self.liveness.isUnused(inst)) return null; - const mod = self.module; const ip = &mod.intern_pool; const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; @@ -3969,8 +3956,6 @@ const DeclGen = struct { } fn airStructFieldVal(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { - if (self.liveness.isUnused(inst)) return null; - const mod = self.module; const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const struct_field = self.air.extraData(Air.StructField, ty_pl.payload).data; @@ -4091,7 +4076,6 @@ const DeclGen = struct { } fn airStructFieldPtrIndex(self: *DeclGen, inst: Air.Inst.Index, field_index: u32) !?IdRef { - if (self.liveness.isUnused(inst)) return null; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const struct_ptr = try self.resolve(ty_op.operand); const struct_ptr_ty = self.typeOf(ty_op.operand); @@ -4145,7 +4129,6 @@ const DeclGen = struct { } fn airAlloc(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { - if (self.liveness.isUnused(inst)) return null; const mod = self.module; const ptr_ty = self.typeOfIndex(inst); assert(ptr_ty.ptrAddressSpace(mod) == .generic); @@ -4729,9 +4712,7 @@ const DeclGen = struct { try self.beginSpvBlock(ok_block); } - if (self.liveness.isUnused(inst)) { - return null; - } + if (!eu_layout.payload_has_bits) { return null; } @@ -4741,8 +4722,6 @@ const DeclGen = struct { } fn airErrUnionErr(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { - if (self.liveness.isUnused(inst)) return null; - const mod = self.module; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const operand_id = try self.resolve(ty_op.operand); @@ -4766,8 +4745,6 @@ const DeclGen = struct { } fn airErrUnionPayload(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { - if (self.liveness.isUnused(inst)) return null; - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const operand_id = try self.resolve(ty_op.operand); const payload_ty = self.typeOfIndex(inst); @@ -4781,8 +4758,6 @@ const DeclGen = struct { } fn airWrapErrUnionErr(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { - if (self.liveness.isUnused(inst)) return null; - const mod = self.module; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const err_union_ty = self.typeOfIndex(inst); @@ -4804,8 +4779,6 @@ const DeclGen = struct { } fn airWrapErrUnionPayload(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { - if (self.liveness.isUnused(inst)) return null; - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const err_union_ty = self.typeOfIndex(inst); const operand_id = try self.resolve(ty_op.operand); @@ -4825,8 +4798,6 @@ const DeclGen = struct { } fn airIsNull(self: *DeclGen, inst: Air.Inst.Index, is_pointer: bool, pred: enum { is_null, is_non_null }) !?IdRef { - if (self.liveness.isUnused(inst)) return null; - const mod = self.module; const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; const operand_id = try self.resolve(un_op); @@ -4899,8 +4870,6 @@ const DeclGen = struct { } fn airIsErr(self: *DeclGen, inst: Air.Inst.Index, pred: enum { is_err, is_non_err }) !?IdRef { - if (self.liveness.isUnused(inst)) return null; - const mod = self.module; const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; const operand_id = try self.resolve(un_op); @@ -4935,8 +4904,6 @@ const DeclGen = struct { } fn airUnwrapOptional(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { - if (self.liveness.isUnused(inst)) return null; - const mod = self.module; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const operand_id = try self.resolve(ty_op.operand); @@ -4953,8 +4920,6 @@ const DeclGen = struct { } fn airUnwrapOptionalPtr(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { - if (self.liveness.isUnused(inst)) return null; - const mod = self.module; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const operand_id = try self.resolve(ty_op.operand); @@ -4979,8 +4944,6 @@ const DeclGen = struct { } fn airWrapOptional(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { - if (self.liveness.isUnused(inst)) return null; - const mod = self.module; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const payload_ty = self.typeOf(ty_op.operand);