authorgravatar for alichraghi@proton.meAli Chraghi <alichraghi@proton.me> 2024-02-19 17:08:39+03:30
committergravatar for alichraghi@proton.meAli Chraghi <alichraghi@proton.me> 2024-04-06 09:01:46+03:30
log436f53f55d3191bfa56418d98130d763fa5a6b22
treef9434f4e9bd566660997405d085a61ab505fc94b
parent97850149386a8ec54ffce285973d3e7010cefa00

spirv: implement `@mulWithOverflow`


6 files changed, 63 insertions(+), 12 deletions(-)

src/codegen/spirv.zig+59-2
...@@ -2315,6 +2315,7 @@ const DeclGen = struct {...@@ -2315,6 +2315,7 @@ const DeclGen = struct {
2315 .sub, .sub_wrap, .sub_optimized => try self.airArithOp(inst, .OpFSub, .OpISub, .OpISub),2315 .sub, .sub_wrap, .sub_optimized => try self.airArithOp(inst, .OpFSub, .OpISub, .OpISub),
2316 .mul, .mul_wrap, .mul_optimized => try self.airArithOp(inst, .OpFMul, .OpIMul, .OpIMul),2316 .mul, .mul_wrap, .mul_optimized => try self.airArithOp(inst, .OpFMul, .OpIMul, .OpIMul),
23172317
2318
2318 .abs => try self.airAbs(inst),2319 .abs => try self.airAbs(inst),
2319 .floor => try self.airFloor(inst),2320 .floor => try self.airFloor(inst),
23202321
...@@ -2330,6 +2331,7 @@ const DeclGen = struct {...@@ -2330,6 +2331,7 @@ const DeclGen = struct {
23302331
2331 .add_with_overflow => try self.airAddSubOverflow(inst, .OpIAdd, .OpULessThan, .OpSLessThan),2332 .add_with_overflow => try self.airAddSubOverflow(inst, .OpIAdd, .OpULessThan, .OpSLessThan),
2332 .sub_with_overflow => try self.airAddSubOverflow(inst, .OpISub, .OpUGreaterThan, .OpSGreaterThan),2333 .sub_with_overflow => try self.airAddSubOverflow(inst, .OpISub, .OpUGreaterThan, .OpSGreaterThan),
2334 .mul_with_overflow => try self.airMulOverflow(inst),
2333 .shl_with_overflow => try self.airShlOverflow(inst),2335 .shl_with_overflow => try self.airShlOverflow(inst),
23342336
2335 .mul_add => try self.airMulAdd(inst),2337 .mul_add => try self.airMulAdd(inst),
...@@ -2733,8 +2735,8 @@ const DeclGen = struct {...@@ -2733,8 +2735,8 @@ const DeclGen = struct {
2733 else => unreachable,2735 else => unreachable,
2734 };2736 };
2735 const set_id = switch (target.os.tag) {2737 const set_id = switch (target.os.tag) {
2736 .opencl => try self.spv.importInstructionSet("OpenCL.std"),2738 .opencl => try self.spv.importInstructionSet(.@"OpenCL.std"),
2737 .vulkan => try self.spv.importInstructionSet("GLSL.std.450"),2739 .vulkan => try self.spv.importInstructionSet(.@"GLSL.std.450"),
2738 else => unreachable,2740 else => unreachable,
2739 };2741 };
27402742
...@@ -2998,6 +3000,61 @@ const DeclGen = struct {...@@ -2998,6 +3000,61 @@ const DeclGen = struct {
2998 );3000 );
2999 }3001 }
30003002
3003 fn airMulOverflow(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
3004 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
3005 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
3006 const lhs = try self.resolve(extra.lhs);
3007 const rhs = try self.resolve(extra.rhs);
3008
3009 const result_ty = self.typeOfIndex(inst);
3010 const operand_ty = self.typeOf(extra.lhs);
3011 const ov_ty = result_ty.structFieldType(1, self.module);
3012
3013 const info = self.arithmeticTypeInfo(operand_ty);
3014 switch (info.class) {
3015 .composite_integer => return self.todo("overflow ops for composite integers", .{}),
3016 .strange_integer, .integer => {},
3017 .float, .bool => unreachable,
3018 }
3019
3020 var wip_result = try self.elementWise(operand_ty, true);
3021 defer wip_result.deinit();
3022 var wip_ov = try self.elementWise(ov_ty, true);
3023 defer wip_ov.deinit();
3024
3025 const zero_id = try self.constInt(wip_result.ty_ref, 0);
3026 const zero_ov_id = try self.constInt(wip_ov.ty_ref, 0);
3027 const one_ov_id = try self.constInt(wip_ov.ty_ref, 1);
3028
3029 for (wip_result.results, wip_ov.results, 0..) |*result_id, *ov_id, i| {
3030 const lhs_elem_id = try wip_result.elementAt(operand_ty, lhs, i);
3031 const rhs_elem_id = try wip_result.elementAt(operand_ty, rhs, i);
3032
3033 result_id.* = try self.arithOp(wip_result.ty, lhs_elem_id, rhs_elem_id, .OpFMul, .OpIMul, .OpIMul);
3034
3035 // (a != 0) and (x / a != b)
3036 const not_zero_id = try self.cmp(.neq, Type.bool, wip_result.ty, lhs_elem_id, zero_id);
3037 const res_rhs_id = try self.arithOp(wip_result.ty, result_id.*, lhs_elem_id, .OpFDiv, .OpSDiv, .OpUDiv);
3038 const res_rhs_not_rhs_id = try self.cmp(.neq, Type.bool, wip_result.ty, res_rhs_id, rhs_elem_id);
3039 const cond_id = try self.binOpSimple(Type.bool, not_zero_id, res_rhs_not_rhs_id, .OpLogicalAnd);
3040
3041 ov_id.* = self.spv.allocId();
3042 try self.func.body.emit(self.spv.gpa, .OpSelect, .{
3043 .id_result_type = wip_ov.ty_id,
3044 .id_result = ov_id.*,
3045 .condition = cond_id,
3046 .object_1 = one_ov_id,
3047 .object_2 = zero_ov_id,
3048 });
3049 }
3050
3051 return try self.constructStruct(
3052 result_ty,
3053 &.{ operand_ty, ov_ty },
3054 &.{ try wip_result.finalize(), try wip_ov.finalize() },
3055 );
3056 }
3057
3001 fn airShlOverflow(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {3058 fn airShlOverflow(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
3002 const mod = self.module;3059 const mod = self.module;
3003 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;3060 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
src/codegen/spirv/Assembler.zig+4-1
...@@ -263,7 +263,10 @@ fn processInstruction(self: *Assembler) !void {...@@ -263,7 +263,10 @@ fn processInstruction(self: *Assembler) !void {
263 .OpExtInstImport => blk: {263 .OpExtInstImport => blk: {
264 const set_name_offset = self.inst.operands.items[1].string;264 const set_name_offset = self.inst.operands.items[1].string;
265 const set_name = std.mem.sliceTo(self.inst.string_bytes.items[set_name_offset..], 0);265 const set_name = std.mem.sliceTo(self.inst.string_bytes.items[set_name_offset..], 0);
266 break :blk .{ .value = try self.spv.importInstructionSet(set_name) };266 const set_tag = std.meta.stringToEnum(spec.InstructionSet, set_name) orelse {
267 return self.fail(set_name_offset, "unknown instruction set: {s}", .{set_name});
268 };
269 break :blk .{ .value = try self.spv.importInstructionSet(set_tag) };
267 },270 },
268 else => switch (self.inst.opcode.class()) {271 else => switch (self.inst.opcode.class()) {
269 .TypeDeclaration => try self.processTypeInstruction(),272 .TypeDeclaration => try self.processTypeInstruction(),
test/behavior/for.zig-1
...@@ -226,7 +226,6 @@ test "else continue outer for" {...@@ -226,7 +226,6 @@ test "else continue outer for" {
226226
227test "for loop with else branch" {227test "for loop with else branch" {
228 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO228 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
229 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
230229
231 {230 {
232 var x = [_]u32{ 1, 2 };231 var x = [_]u32{ 1, 2 };
test/behavior/hasdecl.zig-4
...@@ -12,8 +12,6 @@ const Bar = struct {...@@ -12,8 +12,6 @@ const Bar = struct {
12};12};
1313
14test "@hasDecl" {14test "@hasDecl" {
15 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
16
17 try expect(@hasDecl(Foo, "public_thing"));15 try expect(@hasDecl(Foo, "public_thing"));
18 try expect(!@hasDecl(Foo, "private_thing"));16 try expect(!@hasDecl(Foo, "private_thing"));
19 try expect(!@hasDecl(Foo, "no_thing"));17 try expect(!@hasDecl(Foo, "no_thing"));
...@@ -24,8 +22,6 @@ test "@hasDecl" {...@@ -24,8 +22,6 @@ test "@hasDecl" {
24}22}
2523
26test "@hasDecl using a sliced string literal" {24test "@hasDecl using a sliced string literal" {
27 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
28
29 try expect(@hasDecl(@This(), "std") == true);25 try expect(@hasDecl(@This(), "std") == true);
30 try expect(@hasDecl(@This(), "std"[0..0]) == false);26 try expect(@hasDecl(@This(), "std"[0..0]) == false);
31 try expect(@hasDecl(@This(), "std"[0..1]) == false);27 try expect(@hasDecl(@This(), "std"[0..1]) == false);
test/behavior/math.zig-3
...@@ -788,7 +788,6 @@ test "small int addition" {...@@ -788,7 +788,6 @@ test "small int addition" {
788test "basic @mulWithOverflow" {788test "basic @mulWithOverflow" {
789 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO789 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
790 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO790 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
791 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
792791
793 {792 {
794 var a: u8 = 86;793 var a: u8 = 86;
...@@ -821,7 +820,6 @@ test "basic @mulWithOverflow" {...@@ -821,7 +820,6 @@ test "basic @mulWithOverflow" {
821test "extensive @mulWithOverflow" {820test "extensive @mulWithOverflow" {
822 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO821 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
823 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO822 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
824 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
825823
826 {824 {
827 var a: u5 = 3;825 var a: u5 = 3;
...@@ -998,7 +996,6 @@ test "@mulWithOverflow bitsize > 32" {...@@ -998,7 +996,6 @@ test "@mulWithOverflow bitsize > 32" {
998 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO996 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
999 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO997 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1000 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO998 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1001 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
1002999
1003 {1000 {
1004 var a: u62 = 3;1001 var a: u62 = 3;
test/behavior/vector.zig-1
...@@ -1136,7 +1136,6 @@ test "@mulWithOverflow" {...@@ -1136,7 +1136,6 @@ test "@mulWithOverflow" {
1136 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO1136 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1137 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO1137 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1138 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO1138 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1139 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
11401139
1141 const S = struct {1140 const S = struct {
1142 fn doTheTest() !void {1141 fn doTheTest() !void {