authorgravatar for paul.verigo@gmail.comPavel Verigo <paul.verigo@gmail.com> 2024-07-23 17:06:18+02:00
committergravatar for paul.verigo@gmail.comPavel Verigo <paul.verigo@gmail.com> 2024-07-23 17:06:18+02:00
logd71312d1048d93d5e806a600066426fd2db9b1fb
tree7cdbc18071685a8c14b04743a343e8eccbd3d9de
parent0c6aa44bc3c0670f3533c503ad9921c2439d8e35

stage2-wasm: mul_sat 32 bits <=, i64, i128


2 files changed, 208 insertions(+), 13 deletions(-)

src/arch/wasm/CodeGen.zig+101-1
......@@ -1837,6 +1837,7 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
18371837 .sub_sat => func.airSatBinOp(inst, .sub),
18381838 .sub_wrap => func.airWrapBinOp(inst, .sub),
18391839 .mul => func.airBinOp(inst, .mul),
1840 .mul_sat => func.airSatMul(inst),
18401841 .mul_wrap => func.airWrapBinOp(inst, .mul),
18411842 .div_float, .div_exact => func.airDiv(inst),
18421843 .div_trunc => func.airDivTrunc(inst),
......@@ -2002,7 +2003,6 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
20022003 .error_set_has_value => func.airErrorSetHasValue(inst),
20032004 .frame_addr => func.airFrameAddress(inst),
20042005
2005 .mul_sat,
20062006 .assembly,
20072007 .is_err_ptr,
20082008 .is_non_err_ptr,
......@@ -6783,6 +6783,106 @@ fn airMod(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
67836783 return func.finishAir(inst, .stack, &.{ bin_op.lhs, bin_op.rhs });
67846784}
67856785
6786fn airSatMul(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
6787 const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
6788
6789 const pt = func.pt;
6790 const mod = pt.zcu;
6791 const ty = func.typeOfIndex(inst);
6792 const int_info = ty.intInfo(mod);
6793 const is_signed = int_info.signedness == .signed;
6794
6795 const lhs = try func.resolveInst(bin_op.lhs);
6796 const rhs = try func.resolveInst(bin_op.rhs);
6797 const wasm_bits = toWasmBits(int_info.bits) orelse {
6798 return func.fail("TODO: mul_sat for {}", .{ty.fmt(pt)});
6799 };
6800
6801 switch (wasm_bits) {
6802 32 => {
6803 const upcast_ty: Type = if (is_signed) Type.i64 else Type.u64;
6804 const lhs_up = try func.intcast(lhs, ty, upcast_ty);
6805 const rhs_up = try func.intcast(rhs, ty, upcast_ty);
6806 var mul_res = try (try func.binOp(lhs_up, rhs_up, upcast_ty, .mul)).toLocal(func, upcast_ty);
6807 defer mul_res.free(func);
6808 if (is_signed) {
6809 const imm_max: WValue = .{ .imm64 = ~@as(u64, 0) >> @intCast(64 - (int_info.bits - 1)) };
6810 try func.emitWValue(mul_res);
6811 try func.emitWValue(imm_max);
6812 _ = try func.cmp(mul_res, imm_max, upcast_ty, .lt);
6813 try func.addTag(.select);
6814
6815 var tmp = try func.allocLocal(upcast_ty);
6816 defer tmp.free(func);
6817 try func.addLabel(.local_set, tmp.local.value);
6818
6819 const imm_min: WValue = .{ .imm64 = ~@as(u64, 0) << @intCast(int_info.bits - 1) };
6820 try func.emitWValue(tmp);
6821 try func.emitWValue(imm_min);
6822 _ = try func.cmp(tmp, imm_min, upcast_ty, .gt);
6823 try func.addTag(.select);
6824 } else {
6825 const imm_max: WValue = .{ .imm64 = ~@as(u64, 0) >> @intCast(64 - int_info.bits) };
6826 try func.emitWValue(mul_res);
6827 try func.emitWValue(imm_max);
6828 _ = try func.cmp(mul_res, imm_max, upcast_ty, .lt);
6829 try func.addTag(.select);
6830 }
6831 try func.addTag(.i32_wrap_i64);
6832 },
6833 64 => {
6834 if (!(int_info.bits == 64 and int_info.signedness == .signed)) {
6835 return func.fail("TODO: mul_sat for {}", .{ty.fmt(pt)});
6836 }
6837 const overflow_ret = try func.allocStack(Type.i32);
6838 _ = try func.callIntrinsic(
6839 "__mulodi4",
6840 &[_]InternPool.Index{ .i64_type, .i64_type, .usize_type },
6841 Type.i64,
6842 &.{ lhs, rhs, overflow_ret },
6843 );
6844 const xor = try func.binOp(lhs, rhs, Type.i64, .xor);
6845 const sign_v = try func.binOp(xor, .{ .imm64 = 63 }, Type.i64, .shr);
6846 _ = try func.binOp(sign_v, .{ .imm64 = ~@as(u63, 0) }, Type.i64, .xor);
6847 _ = try func.load(overflow_ret, Type.i32, 0);
6848 try func.addTag(.i32_eqz);
6849 try func.addTag(.select);
6850 },
6851 128 => {
6852 if (!(int_info.bits == 128 and int_info.signedness == .signed)) {
6853 return func.fail("TODO: mul_sat for {}", .{ty.fmt(pt)});
6854 }
6855 const overflow_ret = try func.allocStack(Type.i32);
6856 const ret = try func.callIntrinsic(
6857 "__muloti4",
6858 &[_]InternPool.Index{ .i128_type, .i128_type, .usize_type },
6859 Type.i128,
6860 &.{ lhs, rhs, overflow_ret },
6861 );
6862 try func.lowerToStack(ret);
6863 const xor = try func.binOp(lhs, rhs, Type.i128, .xor);
6864 const sign_v = try func.binOp(xor, .{ .imm32 = 127 }, Type.i128, .shr);
6865
6866 // xor ~@as(u127, 0)
6867 try func.emitWValue(sign_v);
6868 const lsb = try func.load(sign_v, Type.u64, 0);
6869 _ = try func.binOp(lsb, .{ .imm64 = ~@as(u64, 0) }, Type.u64, .xor);
6870 try func.store(.stack, .stack, Type.u64, sign_v.offset());
6871 try func.emitWValue(sign_v);
6872 const msb = try func.load(sign_v, Type.u64, 8);
6873 _ = try func.binOp(msb, .{ .imm64 = ~@as(u63, 0) }, Type.u64, .xor);
6874 try func.store(.stack, .stack, Type.u64, sign_v.offset() + 8);
6875
6876 try func.lowerToStack(sign_v);
6877 _ = try func.load(overflow_ret, Type.i32, 0);
6878 try func.addTag(.i32_eqz);
6879 try func.addTag(.select);
6880 },
6881 else => unreachable,
6882 }
6883 return func.finishAir(inst, .stack, &.{ bin_op.lhs, bin_op.rhs });
6884}
6885
67866886fn airSatBinOp(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {
67876887 assert(op == .add or op == .sub);
67886888 const bin_op = func.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
test/behavior/saturating_arithmetic.zig+107-12
......@@ -154,6 +154,109 @@ test "saturating subtraction 128bit" {
154154 try comptime S.doTheTest();
155155}
156156
157fn testSatMul(comptime T: type, a: T, b: T, expected: T) !void {
158 const res: T = a *| b;
159 try expect(res == expected);
160}
161
162test "saturating multiplication <= 32 bits" {
163 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
164 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
165 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
166 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
167 if (builtin.zig_backend == .stage2_c and comptime builtin.cpu.arch.isArmOrThumb()) return error.SkipZigTest;
168 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
169
170 if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .wasm32) {
171 // https://github.com/ziglang/zig/issues/9660
172 return error.SkipZigTest;
173 }
174
175 try testSatMul(u8, 0, maxInt(u8), 0);
176 try testSatMul(u8, 1 << 7, 1 << 7, maxInt(u8));
177 try testSatMul(u8, maxInt(u8) - 1, 2, maxInt(u8));
178 try testSatMul(u8, 1 << 4, 1 << 4, maxInt(u8));
179 try testSatMul(u8, 1 << 4, 1 << 3, 1 << 7);
180 try testSatMul(u8, 1 << 5, 1 << 3, maxInt(u8));
181 try testSatMul(u8, 10, 20, 200);
182
183 try testSatMul(u16, 0, maxInt(u16), 0);
184 try testSatMul(u16, 1 << 15, 1 << 15, maxInt(u16));
185 try testSatMul(u16, maxInt(u16) - 1, 2, maxInt(u16));
186 try testSatMul(u16, 1 << 8, 1 << 8, maxInt(u16));
187 try testSatMul(u16, 1 << 12, 1 << 3, 1 << 15);
188 try testSatMul(u16, 1 << 13, 1 << 3, maxInt(u16));
189 try testSatMul(u16, 10, 20, 200);
190
191 try testSatMul(u32, 0, maxInt(u32), 0);
192 try testSatMul(u32, 1 << 31, 1 << 31, maxInt(u32));
193 try testSatMul(u32, maxInt(u32) - 1, 2, maxInt(u32));
194 try testSatMul(u32, 1 << 16, 1 << 16, maxInt(u32));
195 try testSatMul(u32, 1 << 28, 1 << 3, 1 << 31);
196 try testSatMul(u32, 1 << 29, 1 << 3, maxInt(u32));
197 try testSatMul(u32, 10, 20, 200);
198
199 try testSatMul(i8, 0, maxInt(i8), 0);
200 try testSatMul(i8, 0, minInt(i8), 0);
201 try testSatMul(i8, 1 << 6, 1 << 6, maxInt(i8));
202 try testSatMul(i8, minInt(i8), minInt(i8), maxInt(i8));
203 try testSatMul(i8, maxInt(i8) - 1, 2, maxInt(i8));
204 try testSatMul(i8, minInt(i8) + 1, 2, minInt(i8));
205 try testSatMul(i8, 1 << 4, 1 << 4, maxInt(i8));
206 try testSatMul(i8, minInt(i4), 1 << 4, minInt(i8));
207 try testSatMul(i8, 10, 12, 120);
208 try testSatMul(i8, 10, -12, -120);
209
210 try testSatMul(i16, 0, maxInt(i16), 0);
211 try testSatMul(i16, 0, minInt(i16), 0);
212 try testSatMul(i16, 1 << 14, 1 << 14, maxInt(i16));
213 try testSatMul(i16, minInt(i16), minInt(i16), maxInt(i16));
214 try testSatMul(i16, maxInt(i16) - 1, 2, maxInt(i16));
215 try testSatMul(i16, minInt(i16) + 1, 2, minInt(i16));
216 try testSatMul(i16, 1 << 8, 1 << 8, maxInt(i16));
217 try testSatMul(i16, minInt(i8), 1 << 8, minInt(i16));
218 try testSatMul(i16, 10, 12, 120);
219 try testSatMul(i16, 10, -12, -120);
220
221 try testSatMul(i32, 0, maxInt(i32), 0);
222 try testSatMul(i32, 0, minInt(i32), 0);
223 try testSatMul(i32, 1 << 30, 1 << 30, maxInt(i32));
224 try testSatMul(i32, minInt(i32), minInt(i32), maxInt(i32));
225 try testSatMul(i32, maxInt(i32) - 1, 2, maxInt(i32));
226 try testSatMul(i32, minInt(i32) + 1, 2, minInt(i32));
227 try testSatMul(i32, 1 << 16, 1 << 16, maxInt(i32));
228 try testSatMul(i32, minInt(i16), 1 << 16, minInt(i32));
229 try testSatMul(i32, 10, 12, 120);
230 try testSatMul(i32, 10, -12, -120);
231}
232
233// TODO: remove this test, integrate into general test
234test "saturating mul i64, i128, wasm only" {
235 if (builtin.zig_backend != .stage2_wasm) return error.SkipZigTest;
236
237 try testSatMul(i64, 0, maxInt(i64), 0);
238 try testSatMul(i64, 0, minInt(i64), 0);
239 try testSatMul(i64, 1 << 62, 1 << 62, maxInt(i64));
240 try testSatMul(i64, minInt(i64), minInt(i64), maxInt(i64));
241 try testSatMul(i64, maxInt(i64) - 1, 2, maxInt(i64));
242 try testSatMul(i64, minInt(i64) + 1, 2, minInt(i64));
243 try testSatMul(i64, 1 << 32, 1 << 32, maxInt(i64));
244 try testSatMul(i64, minInt(i32), 1 << 32, minInt(i64));
245 try testSatMul(i64, 10, 12, 120);
246 try testSatMul(i64, 10, -12, -120);
247
248 try testSatMul(i128, 0, maxInt(i128), 0);
249 try testSatMul(i128, 0, minInt(i128), 0);
250 try testSatMul(i128, 1 << 126, 1 << 126, maxInt(i128));
251 try testSatMul(i128, minInt(i128), minInt(i128), maxInt(i128));
252 try testSatMul(i128, maxInt(i128) - 1, 2, maxInt(i128));
253 try testSatMul(i128, minInt(i128) + 1, 2, minInt(i128));
254 try testSatMul(i128, 1 << 64, 1 << 64, maxInt(i128));
255 try testSatMul(i128, minInt(i64), 1 << 64, minInt(i128));
256 try testSatMul(i128, 10, 12, 120);
257 try testSatMul(i128, 10, -12, -120);
258}
259
157260test "saturating multiplication" {
158261 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
159262 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
......@@ -183,23 +286,15 @@ test "saturating multiplication" {
183286 try testSatMul(u8, 2, 255, 255);
184287 try testSatMul(u128, maxInt(u128), maxInt(u128), maxInt(u128));
185288 }
186
187 fn testSatMul(comptime T: type, lhs: T, rhs: T, expected: T) !void {
188 try expect((lhs *| rhs) == expected);
189
190 var x = lhs;
191 x *|= rhs;
192 try expect(x == expected);
193 }
194289 };
195290
196291 try S.doTheTest();
197292 try comptime S.doTheTest();
198293
199 try comptime S.testSatMul(comptime_int, 0, 0, 0);
200 try comptime S.testSatMul(comptime_int, 3, 2, 6);
201 try comptime S.testSatMul(comptime_int, 651075816498665588400716961808225370057, 468229432685078038144554201546849378455, 304852860194144160265083087140337419215516305999637969803722975979232817921935);
202 try comptime S.testSatMul(comptime_int, 7, -593423721213448152027139550640105366508, -4153966048494137064189976854480737565556);
294 try comptime testSatMul(comptime_int, 0, 0, 0);
295 try comptime testSatMul(comptime_int, 3, 2, 6);
296 try comptime testSatMul(comptime_int, 651075816498665588400716961808225370057, 468229432685078038144554201546849378455, 304852860194144160265083087140337419215516305999637969803722975979232817921935);
297 try comptime testSatMul(comptime_int, 7, -593423721213448152027139550640105366508, -4153966048494137064189976854480737565556);
203298}
204299
205300test "saturating shift-left" {