Commit 58b9200106
Changed files (2)
src
arch
src/arch/spirv/CodeGen.zig
@@ -431,15 +431,12 @@ fn resolveUav(cg: *CodeGen, val: InternPool.Index) !Id {
const zcu = cg.module.zcu;
const ty: Type = .fromInterned(zcu.intern_pool.typeOf(val));
const ty_id = try cg.resolveType(ty, .indirect);
- const decl_ptr_ty_id = try cg.module.ptrType(ty_id, cg.module.storageClass(.generic));
const spv_decl_index = blk: {
const entry = try cg.module.uav_link.getOrPut(cg.module.gpa, .{ val, .function });
if (entry.found_existing) {
try cg.addFunctionDep(entry.value_ptr.*, .function);
-
- const result_id = cg.module.declPtr(entry.value_ptr.*).result_id;
- return try cg.castToGeneric(decl_ptr_ty_id, result_id);
+ return cg.module.declPtr(entry.value_ptr.*).result_id;
}
const spv_decl_index = try cg.module.allocDecl(.invocation_global);
@@ -520,7 +517,7 @@ fn resolveUav(cg: *CodeGen, val: InternPool.Index) !Id {
});
}
- return try cg.castToGeneric(decl_ptr_ty_id, result_id);
+ return result_id;
}
fn addFunctionDep(cg: *CodeGen, decl_index: Module.Decl.Index, storage_class: StorageClass) !void {
@@ -535,21 +532,6 @@ fn addFunctionDep(cg: *CodeGen, decl_index: Module.Decl.Index, storage_class: St
}
}
-fn castToGeneric(cg: *CodeGen, type_id: Id, ptr_id: Id) !Id {
- const target = cg.module.zcu.getTarget();
- if (target.cpu.has(.spirv, .generic_pointer)) {
- const result_id = cg.module.allocId();
- try cg.body.emit(cg.module.gpa, .OpPtrCastToGeneric, .{
- .id_result_type = type_id,
- .id_result = result_id,
- .pointer = ptr_id,
- });
- return result_id;
- }
-
- return ptr_id;
-}
-
/// Start a new SPIR-V block, Emits the label of the new block, and stores which
/// block we are currently generating.
/// Note that there is no such thing as nested blocks like in ZIR or AIR, so we don't need to
@@ -1209,11 +1191,7 @@ fn constantNavRef(cg: *CodeGen, ty: Type, nav_index: InternPool.Nav.Index) !Id {
const spv_decl_index = try cg.module.resolveNav(ip, nav_index);
const spv_decl = cg.module.declPtr(spv_decl_index);
-
- const decl_id = switch (spv_decl.kind) {
- .func => unreachable, // TODO: Is this possible?
- .global, .invocation_global => spv_decl.result_id,
- };
+ assert(spv_decl.kind != .func);
const storage_class = cg.module.storageClass(nav.getAddrspace());
try cg.addFunctionDep(spv_decl_index, storage_class);
@@ -1221,23 +1199,18 @@ fn constantNavRef(cg: *CodeGen, ty: Type, nav_index: InternPool.Nav.Index) !Id {
const nav_ty_id = try cg.resolveType(nav_ty, .indirect);
const decl_ptr_ty_id = try cg.module.ptrType(nav_ty_id, storage_class);
- const ptr_id = switch (storage_class) {
- .generic => try cg.castToGeneric(decl_ptr_ty_id, decl_id),
- else => decl_id,
- };
-
if (decl_ptr_ty_id != ty_id) {
// Differing pointer types, insert a cast.
const casted_ptr_id = cg.module.allocId();
try cg.body.emit(cg.module.gpa, .OpBitcast, .{
.id_result_type = ty_id,
.id_result = casted_ptr_id,
- .operand = ptr_id,
+ .operand = spv_decl.result_id,
});
return casted_ptr_id;
- } else {
- return ptr_id;
}
+
+ return spv_decl.result_id;
}
// Turn a Zig type's name into a cache reference.
@@ -2120,28 +2093,7 @@ fn buildSelect(cg: *CodeGen, condition: Temporary, lhs: Temporary, rhs: Temporar
return v.finalize(result_ty, results);
}
-const CmpPredicate = enum {
- l_eq,
- l_ne,
- i_ne,
- i_eq,
- s_lt,
- s_gt,
- s_le,
- s_ge,
- u_lt,
- u_gt,
- u_le,
- u_ge,
- f_oeq,
- f_une,
- f_olt,
- f_ole,
- f_ogt,
- f_oge,
-};
-
-fn buildCmp(cg: *CodeGen, pred: CmpPredicate, lhs: Temporary, rhs: Temporary) !Temporary {
+fn buildCmp(cg: *CodeGen, opcode: Opcode, lhs: Temporary, rhs: Temporary) !Temporary {
const v = cg.vectorization(.{ lhs, rhs });
const ops = v.components();
const results = cg.module.allocIds(ops);
@@ -2153,27 +2105,6 @@ fn buildCmp(cg: *CodeGen, pred: CmpPredicate, lhs: Temporary, rhs: Temporary) !T
const op_lhs = try v.prepare(cg, lhs);
const op_rhs = try v.prepare(cg, rhs);
- const opcode: Opcode = switch (pred) {
- .l_eq => .OpLogicalEqual,
- .l_ne => .OpLogicalNotEqual,
- .i_eq => .OpIEqual,
- .i_ne => .OpINotEqual,
- .s_lt => .OpSLessThan,
- .s_gt => .OpSGreaterThan,
- .s_le => .OpSLessThanEqual,
- .s_ge => .OpSGreaterThanEqual,
- .u_lt => .OpULessThan,
- .u_gt => .OpUGreaterThan,
- .u_le => .OpULessThanEqual,
- .u_ge => .OpUGreaterThanEqual,
- .f_oeq => .OpFOrdEqual,
- .f_une => .OpFUnordNotEqual,
- .f_olt => .OpFOrdLessThan,
- .f_ole => .OpFOrdLessThanEqual,
- .f_ogt => .OpFOrdGreaterThan,
- .f_oge => .OpFOrdGreaterThanEqual,
- };
-
for (0..ops) |i| {
try cg.body.emitRaw(cg.module.gpa, opcode, 4);
cg.body.writeOperand(Id, op_result_ty_id);
@@ -2278,7 +2209,10 @@ fn buildUnary(cg: *CodeGen, op: UnaryOp, operand: Temporary) !Temporary {
.log,
.log2,
.log10,
- => return cg.todo("implement unary operation '{s}' for {s} os", .{ @tagName(op), @tagName(target.os.tag) }),
+ => return cg.todo(
+ "implement unary operation '{s}' for {s} os",
+ .{ @tagName(op), @tagName(target.os.tag) },
+ ),
else => unreachable,
},
else => unreachable,
@@ -2298,40 +2232,8 @@ fn buildUnary(cg: *CodeGen, op: UnaryOp, operand: Temporary) !Temporary {
return v.finalize(result_ty, results);
}
-const BinaryOp = enum {
- i_add,
- f_add,
- i_sub,
- f_sub,
- i_mul,
- f_mul,
- s_div,
- u_div,
- f_div,
- s_rem,
- f_rem,
- s_mod,
- u_mod,
- f_mod,
- srl,
- sra,
- sll,
- bit_and,
- bit_or,
- bit_xor,
- f_max,
- s_max,
- u_max,
- f_min,
- s_min,
- u_min,
- l_and,
- l_or,
-};
-
-fn buildBinary(cg: *CodeGen, op: BinaryOp, lhs: Temporary, rhs: Temporary) !Temporary {
+fn buildBinary(cg: *CodeGen, opcode: Opcode, lhs: Temporary, rhs: Temporary) !Temporary {
const zcu = cg.module.zcu;
- const target = cg.module.zcu.getTarget();
const v = cg.vectorization(.{ lhs, rhs });
const ops = v.components();
@@ -2344,73 +2246,12 @@ fn buildBinary(cg: *CodeGen, op: BinaryOp, lhs: Temporary, rhs: Temporary) !Temp
const op_lhs = try v.prepare(cg, lhs);
const op_rhs = try v.prepare(cg, rhs);
- if (switch (op) {
- .i_add => .OpIAdd,
- .f_add => .OpFAdd,
- .i_sub => .OpISub,
- .f_sub => .OpFSub,
- .i_mul => .OpIMul,
- .f_mul => .OpFMul,
- .s_div => .OpSDiv,
- .u_div => .OpUDiv,
- .f_div => .OpFDiv,
- .s_rem => .OpSRem,
- .f_rem => .OpFRem,
- .s_mod => .OpSMod,
- .u_mod => .OpUMod,
- .f_mod => .OpFMod,
- .srl => .OpShiftRightLogical,
- .sra => .OpShiftRightArithmetic,
- .sll => .OpShiftLeftLogical,
- .bit_and => .OpBitwiseAnd,
- .bit_or => .OpBitwiseOr,
- .bit_xor => .OpBitwiseXor,
- .l_and => .OpLogicalAnd,
- .l_or => .OpLogicalOr,
- else => @as(?Opcode, null),
- }) |opcode| {
- for (0..ops) |i| {
- try cg.body.emitRaw(cg.module.gpa, opcode, 4);
- cg.body.writeOperand(Id, op_result_ty_id);
- cg.body.writeOperand(Id, results.at(i));
- cg.body.writeOperand(Id, op_lhs.at(i));
- cg.body.writeOperand(Id, op_rhs.at(i));
- }
- } else {
- const set = try cg.importExtendedSet();
-
- // TODO: Put these numbers in some definition
- const extinst: u32 = switch (target.os.tag) {
- .opencl => switch (op) {
- .f_max => 27, // fmax
- .s_max => 156, // s_max
- .u_max => 157, // u_max
- .f_min => 28, // fmin
- .s_min => 158, // s_min
- .u_min => 159, // u_min
- else => unreachable,
- },
- .vulkan, .opengl => switch (op) {
- .f_max => 40, // FMax
- .s_max => 42, // SMax
- .u_max => 41, // UMax
- .f_min => 37, // FMin
- .s_min => 39, // SMin
- .u_min => 38, // UMin
- else => unreachable,
- },
- else => unreachable,
- };
-
- for (0..ops) |i| {
- try cg.body.emit(cg.module.gpa, .OpExtInst, .{
- .id_result_type = op_result_ty_id,
- .id_result = results.at(i),
- .set = set,
- .instruction = .{ .inst = extinst },
- .id_ref_4 = &.{ op_lhs.at(i), op_rhs.at(i) },
- });
- }
+ for (0..ops) |i| {
+ try cg.body.emitRaw(cg.module.gpa, opcode, 4);
+ cg.body.writeOperand(Id, op_result_ty_id);
+ cg.body.writeOperand(Id, results.at(i));
+ cg.body.writeOperand(Id, op_lhs.at(i));
+ cg.body.writeOperand(Id, op_rhs.at(i));
}
return v.finalize(result_ty, results);
@@ -2420,10 +2261,7 @@ fn buildBinary(cg: *CodeGen, op: BinaryOp, lhs: Temporary, rhs: Temporary) !Temp
/// or OpIMul and s_mul_hi or u_mul_hi on OpenCL.
fn buildWideMul(
cg: *CodeGen,
- op: enum {
- s_mul_extended,
- u_mul_extended,
- },
+ signedness: std.builtin.Signedness,
lhs: Temporary,
rhs: Temporary,
) !struct { Temporary, Temporary } {
@@ -2450,9 +2288,9 @@ fn buildWideMul(
// OpUMulExtended. For these we will use the OpenCL s_mul_hi to compute the high-order bits
// instead.
const set = try cg.importExtendedSet();
- const overflow_inst: u32 = switch (op) {
- .s_mul_extended => 160, // s_mul_hi
- .u_mul_extended => 203, // u_mul_hi
+ const overflow_inst: u32 = switch (signedness) {
+ .signed => 160, // s_mul_hi
+ .unsigned => 203, // u_mul_hi
};
for (0..ops) |i| {
@@ -2481,9 +2319,9 @@ fn buildWideMul(
}));
const op_result_ty_id = try cg.resolveType(op_result_ty, .direct);
- const opcode: Opcode = switch (op) {
- .s_mul_extended => .OpSMulExtended,
- .u_mul_extended => .OpUMulExtended,
+ const opcode: Opcode = switch (signedness) {
+ .signed => .OpSMulExtended,
+ .unsigned => .OpUMulExtended,
};
for (0..ops) |i| {
@@ -2718,7 +2556,7 @@ fn convertToDirect(cg: *CodeGen, ty: Type, operand_id: Id) !Id {
};
const result = try cg.buildCmp(
- .i_ne,
+ .OpINotEqual,
Temporary.init(operand_ty, operand_id),
Temporary.init(.u1, false_id),
);
@@ -2817,9 +2655,9 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) Error!void {
const air_tags = cg.air.instructions.items(.tag);
const maybe_result_id: ?Id = switch (air_tags[@intFromEnum(inst)]) {
// zig fmt: off
- .add, .add_wrap, .add_optimized => try cg.airArithOp(inst, .f_add, .i_add, .i_add),
- .sub, .sub_wrap, .sub_optimized => try cg.airArithOp(inst, .f_sub, .i_sub, .i_sub),
- .mul, .mul_wrap, .mul_optimized => try cg.airArithOp(inst, .f_mul, .i_mul, .i_mul),
+ .add, .add_wrap, .add_optimized => try cg.airArithOp(inst, .OpFAdd, .OpIAdd, .OpIAdd),
+ .sub, .sub_wrap, .sub_optimized => try cg.airArithOp(inst, .OpFSub, .OpISub, .OpISub),
+ .mul, .mul_wrap, .mul_optimized => try cg.airArithOp(inst, .OpFMul, .OpIMul, .OpIMul),
.sqrt => try cg.airUnOpSimple(inst, .sqrt),
.sin => try cg.airUnOpSimple(inst, .sin),
@@ -2837,15 +2675,15 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) Error!void {
.trunc_float => try cg.airUnOpSimple(inst, .trunc),
.neg, .neg_optimized => try cg.airUnOpSimple(inst, .f_neg),
- .div_float, .div_float_optimized => try cg.airArithOp(inst, .f_div, .s_div, .u_div),
+ .div_float, .div_float_optimized => try cg.airArithOp(inst, .OpFDiv, .OpSDiv, .OpUDiv),
.div_floor, .div_floor_optimized => try cg.airDivFloor(inst),
.div_trunc, .div_trunc_optimized => try cg.airDivTrunc(inst),
- .rem, .rem_optimized => try cg.airArithOp(inst, .f_rem, .s_rem, .u_mod),
- .mod, .mod_optimized => try cg.airArithOp(inst, .f_mod, .s_mod, .u_mod),
+ .rem, .rem_optimized => try cg.airArithOp(inst, .OpFRem, .OpSRem, .OpUMod),
+ .mod, .mod_optimized => try cg.airArithOp(inst, .OpFMod, .OpSMod, .OpUMod),
- .add_with_overflow => try cg.airAddSubOverflow(inst, .i_add, .u_lt, .s_lt),
- .sub_with_overflow => try cg.airAddSubOverflow(inst, .i_sub, .u_gt, .s_gt),
+ .add_with_overflow => try cg.airAddSubOverflow(inst, .OpIAdd, .OpULessThan, .OpSLessThan),
+ .sub_with_overflow => try cg.airAddSubOverflow(inst, .OpISub, .OpUGreaterThan, .OpSGreaterThan),
.mul_with_overflow => try cg.airMulOverflow(inst),
.shl_with_overflow => try cg.airShlOverflow(inst),
@@ -2864,14 +2702,14 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) Error!void {
.ptr_add => try cg.airPtrAdd(inst),
.ptr_sub => try cg.airPtrSub(inst),
- .bit_and => try cg.airBinOpSimple(inst, .bit_and),
- .bit_or => try cg.airBinOpSimple(inst, .bit_or),
- .xor => try cg.airBinOpSimple(inst, .bit_xor),
- .bool_and => try cg.airBinOpSimple(inst, .l_and),
- .bool_or => try cg.airBinOpSimple(inst, .l_or),
+ .bit_and => try cg.airBinOpSimple(inst, .OpBitwiseAnd),
+ .bit_or => try cg.airBinOpSimple(inst, .OpBitwiseOr),
+ .xor => try cg.airBinOpSimple(inst, .OpBitwiseXor),
+ .bool_and => try cg.airBinOpSimple(inst, .OpLogicalAnd),
+ .bool_or => try cg.airBinOpSimple(inst, .OpLogicalOr),
- .shl, .shl_exact => try cg.airShift(inst, .sll, .sll),
- .shr, .shr_exact => try cg.airShift(inst, .srl, .sra),
+ .shl, .shl_exact => try cg.airShift(inst, .OpShiftLeftLogical, .OpShiftLeftLogical),
+ .shr, .shr_exact => try cg.airShift(inst, .OpShiftRightLogical, .OpShiftRightArithmetic),
.min => try cg.airMinMax(inst, .min),
.max => try cg.airMinMax(inst, .max),
@@ -2983,7 +2821,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) Error!void {
try cg.inst_results.putNoClobber(gpa, inst, result_id);
}
-fn airBinOpSimple(cg: *CodeGen, inst: Air.Inst.Index, op: BinaryOp) !?Id {
+fn airBinOpSimple(cg: *CodeGen, inst: Air.Inst.Index, op: Opcode) !?Id {
const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
const lhs = try cg.temporary(bin_op.lhs);
const rhs = try cg.temporary(bin_op.rhs);
@@ -2992,7 +2830,7 @@ fn airBinOpSimple(cg: *CodeGen, inst: Air.Inst.Index, op: BinaryOp) !?Id {
return try result.materialize(cg);
}
-fn airShift(cg: *CodeGen, inst: Air.Inst.Index, unsigned: BinaryOp, signed: BinaryOp) !?Id {
+fn airShift(cg: *CodeGen, inst: Air.Inst.Index, unsigned: Opcode, signed: Opcode) !?Id {
const zcu = cg.module.zcu;
const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
@@ -3042,28 +2880,77 @@ fn airMinMax(cg: *CodeGen, inst: Air.Inst.Index, op: MinMax) !?Id {
}
fn minMax(cg: *CodeGen, lhs: Temporary, rhs: Temporary, op: MinMax) !Temporary {
+ const zcu = cg.module.zcu;
+ const target = zcu.getTarget();
const info = cg.arithmeticTypeInfo(lhs.ty);
- const binop: BinaryOp = switch (info.class) {
- .float => switch (op) {
- .min => .f_min,
- .max => .f_max,
+ const v = cg.vectorization(.{ lhs, rhs });
+ const ops = v.components();
+ const results = cg.module.allocIds(ops);
+
+ const op_result_ty = lhs.ty.scalarType(zcu);
+ const op_result_ty_id = try cg.resolveType(op_result_ty, .direct);
+ const result_ty = try v.resultType(cg, lhs.ty);
+
+ const op_lhs = try v.prepare(cg, lhs);
+ const op_rhs = try v.prepare(cg, rhs);
+
+ const ext_inst: u32 = switch (target.os.tag) {
+ .opencl => switch (info.class) {
+ .float => switch (op) {
+ .min => 28, // fmin
+ .max => 27, // fmax
+ },
+ .integer,
+ .strange_integer,
+ .composite_integer,
+ => switch (info.signedness) {
+ .signed => switch (op) {
+ .min => 158, // s_min
+ .max => 156, // s_max
+ },
+ .unsigned => switch (op) {
+ .min => 159, // u_min
+ .max => 157, // u_max
+ },
+ },
+ .bool => unreachable,
},
- .integer, .strange_integer => switch (info.signedness) {
- .signed => switch (op) {
- .min => .s_min,
- .max => .s_max,
+ .vulkan, .opengl => switch (info.class) {
+ .float => switch (op) {
+ .min => 37, // FMin
+ .max => 40, // FMax
},
- .unsigned => switch (op) {
- .min => .u_min,
- .max => .u_max,
+ .integer,
+ .strange_integer,
+ .composite_integer,
+ => switch (info.signedness) {
+ .signed => switch (op) {
+ .min => 39, // SMin
+ .max => 42, // SMax
+ },
+ .unsigned => switch (op) {
+ .min => 38, // UMin
+ .max => 41, // UMax
+ },
},
+ .bool => unreachable,
},
- .composite_integer => unreachable, // TODO
- .bool => unreachable,
+ else => unreachable,
};
- return try cg.buildBinary(binop, lhs, rhs);
+ const set = try cg.importExtendedSet();
+ for (0..ops) |i| {
+ try cg.body.emit(cg.module.gpa, .OpExtInst, .{
+ .id_result_type = op_result_ty_id,
+ .id_result = results.at(i),
+ .set = set,
+ .instruction = .{ .inst = ext_inst },
+ .id_ref_4 = &.{ op_lhs.at(i), op_rhs.at(i) },
+ });
+ }
+
+ return v.finalize(result_ty, results);
}
/// This function normalizes values to a canonical representation
@@ -3083,14 +2970,14 @@ fn normalize(cg: *CodeGen, value: Temporary, info: ArithmeticTypeInfo) !Temporar
.unsigned => {
const mask_value = if (info.bits == 64) 0xFFFF_FFFF_FFFF_FFFF else (@as(u64, 1) << @as(u6, @intCast(info.bits))) - 1;
const mask_id = try cg.constInt(ty.scalarType(zcu), mask_value);
- return try cg.buildBinary(.bit_and, value, Temporary.init(ty.scalarType(zcu), mask_id));
+ return try cg.buildBinary(.OpBitwiseAnd, value, Temporary.init(ty.scalarType(zcu), mask_id));
},
.signed => {
// Shift left and right so that we can copy the sight bit that way.
const shift_amt_id = try cg.constInt(ty.scalarType(zcu), info.backing_bits - info.bits);
const shift_amt: Temporary = .init(ty.scalarType(zcu), shift_amt_id);
- const left = try cg.buildBinary(.sll, value, shift_amt);
- return try cg.buildBinary(.sra, left, shift_amt);
+ const left = try cg.buildBinary(.OpShiftLeftLogical, value, shift_amt);
+ return try cg.buildBinary(.OpShiftRightArithmetic, left, shift_amt);
},
},
}
@@ -3108,7 +2995,7 @@ fn airDivFloor(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
.integer, .strange_integer => {
switch (info.signedness) {
.unsigned => {
- const result = try cg.buildBinary(.u_div, lhs, rhs);
+ const result = try cg.buildBinary(.OpUDiv, lhs, rhs);
return try result.materialize(cg);
},
.signed => {},
@@ -3118,26 +3005,26 @@ fn airDivFloor(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
// (a / b) - (a % b != 0 && a < 0 != b < 0);
// There shouldn't be any overflow issues.
- const div = try cg.buildBinary(.s_div, lhs, rhs);
- const rem = try cg.buildBinary(.s_rem, lhs, rhs);
+ const div = try cg.buildBinary(.OpSDiv, lhs, rhs);
+ const rem = try cg.buildBinary(.OpSRem, lhs, rhs);
const zero: Temporary = .init(lhs.ty, try cg.constInt(lhs.ty, 0));
- const rem_is_not_zero = try cg.buildCmp(.i_ne, rem, zero);
+ const rem_is_not_zero = try cg.buildCmp(.OpINotEqual, rem, zero);
const result_negative = try cg.buildCmp(
- .l_ne,
- try cg.buildCmp(.s_lt, lhs, zero),
- try cg.buildCmp(.s_lt, rhs, zero),
+ .OpLogicalNotEqual,
+ try cg.buildCmp(.OpSLessThan, lhs, zero),
+ try cg.buildCmp(.OpSLessThan, rhs, zero),
);
const rem_is_not_zero_and_result_is_negative = try cg.buildBinary(
- .l_and,
+ .OpLogicalAnd,
rem_is_not_zero,
result_negative,
);
const result = try cg.buildBinary(
- .i_sub,
+ .OpISub,
div,
try cg.intFromBool2(rem_is_not_zero_and_result_is_negative, div.ty),
);
@@ -3145,7 +3032,7 @@ fn airDivFloor(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
return try result.materialize(cg);
},
.float => {
- const div = try cg.buildBinary(.f_div, lhs, rhs);
+ const div = try cg.buildBinary(.OpFDiv, lhs, rhs);
const result = try cg.buildUnary(.floor, div);
return try result.materialize(cg);
},
@@ -3164,16 +3051,16 @@ fn airDivTrunc(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
.composite_integer => unreachable, // TODO
.integer, .strange_integer => switch (info.signedness) {
.unsigned => {
- const result = try cg.buildBinary(.u_div, lhs, rhs);
+ const result = try cg.buildBinary(.OpUDiv, lhs, rhs);
return try result.materialize(cg);
},
.signed => {
- const result = try cg.buildBinary(.s_div, lhs, rhs);
+ const result = try cg.buildBinary(.OpSDiv, lhs, rhs);
return try result.materialize(cg);
},
},
.float => {
- const div = try cg.buildBinary(.f_div, lhs, rhs);
+ const div = try cg.buildBinary(.OpFDiv, lhs, rhs);
const result = try cg.buildUnary(.trunc, div);
return try result.materialize(cg);
},
@@ -3191,9 +3078,9 @@ fn airUnOpSimple(cg: *CodeGen, inst: Air.Inst.Index, op: UnaryOp) !?Id {
fn airArithOp(
cg: *CodeGen,
inst: Air.Inst.Index,
- comptime fop: BinaryOp,
- comptime sop: BinaryOp,
- comptime uop: BinaryOp,
+ comptime fop: Opcode,
+ comptime sop: Opcode,
+ comptime uop: Opcode,
) !?Id {
const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
@@ -3253,11 +3140,11 @@ fn abs(cg: *CodeGen, result_ty: Type, value: Temporary) !Temporary {
fn airAddSubOverflow(
cg: *CodeGen,
inst: Air.Inst.Index,
- comptime add: BinaryOp,
- comptime ucmp: CmpPredicate,
- comptime scmp: CmpPredicate,
+ comptime add: Opcode,
+ u_opcode: Opcode,
+ s_opcode: Opcode,
) !?Id {
- _ = scmp;
+ _ = s_opcode;
// Note: OpIAddCarry and OpISubBorrow are not really useful here: For unsigned numbers,
// there is in both cases only one extra operation required. For signed operations,
// the overflow bit is set then going from 0x80.. to 0x00.., but this doesn't actually
@@ -3285,7 +3172,7 @@ fn airAddSubOverflow(
const overflowed = switch (info.signedness) {
// Overflow happened if the result is smaller than either of the operands. It doesn't matter which.
// For subtraction the conditions need to be swapped.
- .unsigned => try cg.buildCmp(ucmp, result, lhs),
+ .unsigned => try cg.buildCmp(u_opcode, result, lhs),
// For signed operations, we check the signs of the operands and the result.
.signed => blk: {
// Signed overflow detection using the sign bits of the operands and the result.
@@ -3297,19 +3184,19 @@ fn airAddSubOverflow(
// (sign(a) != sign(b)) && (sign(a) != sign(result))
const zero: Temporary = .init(rhs.ty, try cg.constInt(rhs.ty, 0));
- const lhs_is_neg = try cg.buildCmp(.s_lt, lhs, zero);
- const rhs_is_neg = try cg.buildCmp(.s_lt, rhs, zero);
- const result_is_neg = try cg.buildCmp(.s_lt, result, zero);
+ const lhs_is_neg = try cg.buildCmp(.OpSLessThan, lhs, zero);
+ const rhs_is_neg = try cg.buildCmp(.OpSLessThan, rhs, zero);
+ const result_is_neg = try cg.buildCmp(.OpSLessThan, result, zero);
- const signs_match = try cg.buildCmp(.l_eq, lhs_is_neg, rhs_is_neg);
- const result_sign_differs = try cg.buildCmp(.l_ne, lhs_is_neg, result_is_neg);
+ const signs_match = try cg.buildCmp(.OpLogicalEqual, lhs_is_neg, rhs_is_neg);
+ const result_sign_differs = try cg.buildCmp(.OpLogicalNotEqual, lhs_is_neg, result_is_neg);
- const overflow_condition = if (add == .i_add)
+ const overflow_condition = if (add == .OpIAdd)
signs_match
- else // .i_sub
+ else // .OpISub
try cg.buildUnary(.l_not, signs_match);
- break :blk try cg.buildBinary(.l_and, overflow_condition, result_sign_differs);
+ break :blk try cg.buildCmp(.OpLogicalAnd, overflow_condition, result_sign_differs);
},
};
@@ -3361,23 +3248,23 @@ fn airMulOverflow(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
const casted_lhs = try cg.buildConvert(op_ty, lhs);
const casted_rhs = try cg.buildConvert(op_ty, rhs);
- const full_result = try cg.buildBinary(.i_mul, casted_lhs, casted_rhs);
+ const full_result = try cg.buildBinary(.OpIMul, casted_lhs, casted_rhs);
const low_bits = try cg.buildConvert(lhs.ty, full_result);
const result = try cg.normalize(low_bits, info);
// Shift the result bits away to get the overflow bits.
const shift: Temporary = .init(full_result.ty, try cg.constInt(full_result.ty, info.bits));
- const overflow = try cg.buildBinary(.srl, full_result, shift);
+ const overflow = try cg.buildBinary(.OpShiftRightLogical, full_result, shift);
// Directly check if its zero in the op_ty without converting first.
const zero: Temporary = .init(full_result.ty, try cg.constInt(full_result.ty, 0));
- const overflowed = try cg.buildCmp(.i_ne, zero, overflow);
+ const overflowed = try cg.buildCmp(.OpINotEqual, zero, overflow);
break :blk .{ result, overflowed };
}
- const low_bits, const high_bits = try cg.buildWideMul(.u_mul_extended, lhs, rhs);
+ const low_bits, const high_bits = try cg.buildWideMul(.unsigned, lhs, rhs);
// Truncate the result, if required.
const result = try cg.normalize(low_bits, info);
@@ -3386,17 +3273,17 @@ fn airMulOverflow(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
// high bits of the low word of the result (those outside the range of the
// int) are nonzero.
const zero: Temporary = .init(lhs.ty, try cg.constInt(lhs.ty, 0));
- const high_overflowed = try cg.buildCmp(.i_ne, zero, high_bits);
+ const high_overflowed = try cg.buildCmp(.OpINotEqual, zero, high_bits);
// If no overflow bits in low_bits, no extra work needs to be done.
if (info.backing_bits == info.bits) break :blk .{ result, high_overflowed };
// Shift the result bits away to get the overflow bits.
const shift: Temporary = .init(lhs.ty, try cg.constInt(lhs.ty, info.bits));
- const low_overflow = try cg.buildBinary(.srl, low_bits, shift);
- const low_overflowed = try cg.buildCmp(.i_ne, zero, low_overflow);
+ const low_overflow = try cg.buildBinary(.OpShiftRightLogical, low_bits, shift);
+ const low_overflowed = try cg.buildCmp(.OpINotEqual, zero, low_overflow);
- const overflowed = try cg.buildBinary(.l_or, low_overflowed, high_overflowed);
+ const overflowed = try cg.buildCmp(.OpLogicalOr, low_overflowed, high_overflowed);
break :blk .{ result, overflowed };
},
@@ -3412,16 +3299,16 @@ fn airMulOverflow(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
// (lhs > 0 && rhs < 0) || (lhs < 0 && rhs > 0)
const zero: Temporary = .init(lhs.ty, try cg.constInt(lhs.ty, 0));
- const lhs_negative = try cg.buildCmp(.s_lt, lhs, zero);
- const rhs_negative = try cg.buildCmp(.s_lt, rhs, zero);
- const lhs_positive = try cg.buildCmp(.s_gt, lhs, zero);
- const rhs_positive = try cg.buildCmp(.s_gt, rhs, zero);
+ const lhs_negative = try cg.buildCmp(.OpSLessThan, lhs, zero);
+ const rhs_negative = try cg.buildCmp(.OpSLessThan, rhs, zero);
+ const lhs_positive = try cg.buildCmp(.OpSGreaterThan, lhs, zero);
+ const rhs_positive = try cg.buildCmp(.OpSGreaterThan, rhs, zero);
// Set to `true` if we expect -1.
const expected_overflow_bit = try cg.buildBinary(
- .l_or,
- try cg.buildBinary(.l_and, lhs_positive, rhs_negative),
- try cg.buildBinary(.l_and, lhs_negative, rhs_positive),
+ .OpLogicalOr,
+ try cg.buildCmp(.OpLogicalAnd, lhs_positive, rhs_negative),
+ try cg.buildCmp(.OpLogicalAnd, lhs_negative, rhs_positive),
);
if (maybe_op_ty_bits) |op_ty_bits| {
@@ -3430,7 +3317,7 @@ fn airMulOverflow(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
const casted_lhs = try cg.buildConvert(op_ty, lhs);
const casted_rhs = try cg.buildConvert(op_ty, rhs);
- const full_result = try cg.buildBinary(.i_mul, casted_lhs, casted_rhs);
+ const full_result = try cg.buildBinary(.OpIMul, casted_lhs, casted_rhs);
// Truncate to the result type.
const low_bits = try cg.buildConvert(lhs.ty, full_result);
@@ -3443,18 +3330,18 @@ fn airMulOverflow(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
const shift: Temporary = .init(full_result.ty, try cg.constInt(full_result.ty, info.bits - 1));
// Use SRA so that any sign bits are duplicated. Now we can just check if ALL bits are set
// for negative cases.
- const overflow = try cg.buildBinary(.sra, full_result, shift);
+ const overflow = try cg.buildBinary(.OpShiftRightArithmetic, full_result, shift);
const long_all_set: Temporary = .init(full_result.ty, try cg.constInt(full_result.ty, -1));
const long_zero: Temporary = .init(full_result.ty, try cg.constInt(full_result.ty, 0));
const mask = try cg.buildSelect(expected_overflow_bit, long_all_set, long_zero);
- const overflowed = try cg.buildCmp(.i_ne, mask, overflow);
+ const overflowed = try cg.buildCmp(.OpINotEqual, mask, overflow);
break :blk .{ result, overflowed };
}
- const low_bits, const high_bits = try cg.buildWideMul(.s_mul_extended, lhs, rhs);
+ const low_bits, const high_bits = try cg.buildWideMul(.signed, lhs, rhs);
// Truncate result if required.
const result = try cg.normalize(low_bits, info);
@@ -3465,7 +3352,7 @@ fn airMulOverflow(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
// Like with unsigned, overflow happened if high_bits are not the ones we expect,
// and we also need to check some ones from the low bits.
- const high_overflowed = try cg.buildCmp(.i_ne, mask, high_bits);
+ const high_overflowed = try cg.buildCmp(.OpINotEqual, mask, high_bits);
// If no overflow bits in low_bits, no extra work needs to be done.
// Careful, we still have to check the sign bit, so this branch
@@ -3476,10 +3363,10 @@ fn airMulOverflow(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
const shift: Temporary = .init(lhs.ty, try cg.constInt(lhs.ty, info.bits - 1));
// Use SRA so that any sign bits are duplicated. Now we can just check if ALL bits are set
// for negative cases.
- const low_overflow = try cg.buildBinary(.sra, low_bits, shift);
- const low_overflowed = try cg.buildCmp(.i_ne, mask, low_overflow);
+ const low_overflow = try cg.buildBinary(.OpShiftRightArithmetic, low_bits, shift);
+ const low_overflowed = try cg.buildCmp(.OpINotEqual, mask, low_overflow);
- const overflowed = try cg.buildBinary(.l_or, low_overflowed, high_overflowed);
+ const overflowed = try cg.buildCmp(.OpLogicalOr, low_overflowed, high_overflowed);
break :blk .{ result, overflowed };
},
@@ -3517,15 +3404,15 @@ fn airShlOverflow(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
// so just manually upcast it if required.
const casted_shift = try cg.buildConvert(base.ty.scalarType(zcu), shift);
- const left = try cg.buildBinary(.sll, base, casted_shift);
+ const left = try cg.buildBinary(.OpShiftLeftLogical, base, casted_shift);
const result = try cg.normalize(left, info);
const right = switch (info.signedness) {
- .unsigned => try cg.buildBinary(.srl, result, casted_shift),
- .signed => try cg.buildBinary(.sra, result, casted_shift),
+ .unsigned => try cg.buildBinary(.OpShiftRightLogical, result, casted_shift),
+ .signed => try cg.buildBinary(.OpShiftRightArithmetic, result, casted_shift),
};
- const overflowed = try cg.buildCmp(.i_ne, base, right);
+ const overflowed = try cg.buildCmp(.OpINotEqual, base, right);
const ov = try cg.intFromBool(overflowed);
const result_ty_id = try cg.resolveType(result_ty, .direct);
@@ -3957,19 +3844,19 @@ fn cmp(
return switch (op) {
.eq => try cg.buildBinary(
- .l_and,
+ .OpLogicalAnd,
try cg.cmp(.eq, lhs_valid, rhs_valid),
try cg.buildBinary(
- .l_or,
+ .OpLogicalOr,
try cg.buildUnary(.l_not, lhs_valid),
try cg.cmp(.eq, lhs_pl, rhs_pl),
),
),
.neq => try cg.buildBinary(
- .l_or,
+ .OpLogicalOr,
try cg.cmp(.neq, lhs_valid, rhs_valid),
try cg.buildBinary(
- .l_and,
+ .OpLogicalAnd,
lhs_valid,
try cg.cmp(.neq, lhs_pl, rhs_pl),
),
@@ -3981,37 +3868,37 @@ fn cmp(
}
const info = cg.arithmeticTypeInfo(scalar_ty);
- const pred: CmpPredicate = switch (info.class) {
+ const pred: Opcode = switch (info.class) {
.composite_integer => unreachable, // TODO
.float => switch (op) {
- .eq => .f_oeq,
- .neq => .f_une,
- .lt => .f_olt,
- .lte => .f_ole,
- .gt => .f_ogt,
- .gte => .f_oge,
+ .eq => .OpFOrdEqual,
+ .neq => .OpFUnordNotEqual,
+ .lt => .OpFOrdLessThan,
+ .lte => .OpFOrdLessThanEqual,
+ .gt => .OpFOrdGreaterThan,
+ .gte => .OpFOrdGreaterThanEqual,
},
.bool => switch (op) {
- .eq => .l_eq,
- .neq => .l_ne,
+ .eq => .OpLogicalEqual,
+ .neq => .OpLogicalNotEqual,
else => unreachable,
},
.integer, .strange_integer => switch (info.signedness) {
.signed => switch (op) {
- .eq => .i_eq,
- .neq => .i_ne,
- .lt => .s_lt,
- .lte => .s_le,
- .gt => .s_gt,
- .gte => .s_ge,
+ .eq => .OpIEqual,
+ .neq => .OpINotEqual,
+ .lt => .OpSLessThan,
+ .lte => .OpSLessThanEqual,
+ .gt => .OpSGreaterThan,
+ .gte => .OpSGreaterThanEqual,
},
.unsigned => switch (op) {
- .eq => .i_eq,
- .neq => .i_ne,
- .lt => .u_lt,
- .lte => .u_le,
- .gt => .u_gt,
- .gte => .u_ge,
+ .eq => .OpIEqual,
+ .neq => .OpINotEqual,
+ .lt => .OpULessThan,
+ .lte => .OpULessThanEqual,
+ .gt => .OpUGreaterThan,
+ .gte => .OpUGreaterThanEqual,
},
},
};
@@ -4312,12 +4199,12 @@ fn airAggregateInit(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
.ty = field_int_ty,
.value = .{ .singleton = field_int_id },
});
- const shifted = try cg.buildBinary(.sll, extended_int_conv, .{
+ const shifted = try cg.buildBinary(.OpShiftLeftLogical, extended_int_conv, .{
.ty = backing_int_ty,
.value = .{ .singleton = shift_rhs },
});
const running_int_tmp = try cg.buildBinary(
- .bit_or,
+ .OpBitwiseOr,
.{ .ty = backing_int_ty, .value = .{ .singleton = running_int_id } },
shifted,
);
@@ -4770,17 +4657,20 @@ fn airStructFieldVal(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
.@"struct" => switch (object_ty.containerLayout(zcu)) {
.@"packed" => {
const struct_ty = zcu.typeToPackedStruct(object_ty).?;
+ const struct_backing_int_bits = cg.module.backingIntBits(@intCast(object_ty.bitSize(zcu))).@"0";
const bit_offset = zcu.structPackedFieldBitOffset(struct_ty, field_index);
- const bit_offset_id = try cg.constInt(.u16, bit_offset);
+ // We use the same int type the packed struct is backed by, because even though it would
+ // be valid SPIR-V to use an smaller type like u16, some implementations like PoCL will complain.
+ const bit_offset_id = try cg.constInt(object_ty, bit_offset);
const signedness = if (field_ty.isInt(zcu)) field_ty.intInfo(zcu).signedness else .unsigned;
const field_bit_size: u16 = @intCast(field_ty.bitSize(zcu));
const field_int_ty = try pt.intType(signedness, field_bit_size);
const shift_lhs: Temporary = .{ .ty = object_ty, .value = .{ .singleton = object_id } };
- const shift = try cg.buildBinary(.srl, shift_lhs, .{ .ty = .u16, .value = .{ .singleton = bit_offset_id } });
+ const shift = try cg.buildBinary(.OpShiftRightLogical, shift_lhs, .{ .ty = object_ty, .value = .{ .singleton = bit_offset_id } });
const mask_id = try cg.constInt(object_ty, (@as(u64, 1) << @as(u6, @intCast(field_bit_size))) - 1);
- const masked = try cg.buildBinary(.bit_and, shift, .{ .ty = object_ty, .value = .{ .singleton = mask_id } });
+ const masked = try cg.buildBinary(.OpBitwiseAnd, shift, .{ .ty = object_ty, .value = .{ .singleton = mask_id } });
const result_id = blk: {
- if (cg.module.backingIntBits(field_bit_size).@"0" == cg.module.backingIntBits(@intCast(object_ty.bitSize(zcu))).@"0")
+ if (cg.module.backingIntBits(field_bit_size).@"0" == struct_backing_int_bits)
break :blk try cg.bitCast(field_int_ty, object_ty, try masked.materialize(cg));
const trunc = try cg.buildConvert(field_int_ty, masked);
break :blk try trunc.materialize(cg);
@@ -4799,7 +4689,7 @@ fn airStructFieldVal(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
const int_ty = try pt.intType(signedness, field_bit_size);
const mask_id = try cg.constInt(backing_int_ty, (@as(u64, 1) << @as(u6, @intCast(field_bit_size))) - 1);
const masked = try cg.buildBinary(
- .bit_and,
+ .OpBitwiseAnd,
.{ .ty = backing_int_ty, .value = .{ .singleton = object_id } },
.{ .ty = backing_int_ty, .value = .{ .singleton = mask_id } },
);
@@ -4858,7 +4748,7 @@ fn airFieldParentPtr(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
const field_offset_id = try cg.constInt(.usize, field_offset);
const field_ptr_tmp: Temporary = .init(.usize, field_ptr_int);
const field_offset_tmp: Temporary = .init(.usize, field_offset_id);
- const result = try cg.buildBinary(.i_sub, field_ptr_tmp, field_offset_tmp);
+ const result = try cg.buildBinary(.OpISub, field_ptr_tmp, field_offset_tmp);
break :base_ptr_int try result.materialize(cg);
};
@@ -4947,7 +4837,6 @@ fn alloc(
ty: Type,
options: AllocOptions,
) !Id {
- const target = cg.module.zcu.getTarget();
const ty_id = try cg.resolveType(ty, .indirect);
const ptr_fn_ty_id = try cg.module.ptrType(ty_id, .function);
@@ -4961,20 +4850,7 @@ fn alloc(
.initializer = options.initializer,
});
- switch (target.os.tag) {
- .vulkan, .opengl => return var_id,
- else => {},
- }
-
- switch (options.storage_class) {
- .generic => {
- const ptr_gn_ty_id = try cg.module.ptrType(ty_id, .generic);
- // Convert to a generic pointer
- return cg.castToGeneric(ptr_gn_ty_id, var_id);
- },
- .function => return var_id,
- else => unreachable,
- }
+ return var_id;
}
fn airAlloc(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
src/arch/spirv/Module.zig
@@ -368,7 +368,10 @@ pub fn finalize(module: *Module, gpa: Allocator) ![]Word {
}
if (target.cpu.arch == .spirv64) try module.addCapability(.int64);
if (target.cpu.has(.spirv, .int64)) try module.addCapability(.int64);
- if (target.cpu.has(.spirv, .float16)) try module.addCapability(.float16);
+ if (target.cpu.has(.spirv, .float16)) {
+ if (target.os.tag == .opencl) try module.addExtension("cl_khr_fp16");
+ try module.addCapability(.float16);
+ }
if (target.cpu.has(.spirv, .float64)) try module.addCapability(.float64);
if (target.cpu.has(.spirv, .generic_pointer)) try module.addCapability(.generic_pointer);
if (target.cpu.has(.spirv, .vector16)) try module.addCapability(.vector16);
@@ -920,7 +923,7 @@ pub fn debugString(module: *Module, string: []const u8) !Id {
pub fn storageClass(module: *Module, as: std.builtin.AddressSpace) spec.StorageClass {
const target = module.zcu.getTarget();
return switch (as) {
- .generic => if (target.cpu.has(.spirv, .generic_pointer)) .generic else .function,
+ .generic => .function,
.global => switch (target.os.tag) {
.opencl, .amdhsa => .cross_workgroup,
else => .storage_buffer,