authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-06-15 22:03:18+02:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-06-19 14:30:17+02:00
logce5d934f5f713a0dbc8787d9ffe58b4962042f8f
treecaeeec9a2dc9f34f137a803bfd5dea5f791abb4b
parentfcd4280a8cabf7859fab450cc0d4b65f6adaabe5
signaturelock-open Commit is signed but in an unrecognized format.

wasm: saturating add and sub for signed integers


1 files changed, 67 insertions(+), 18 deletions(-)

src/arch/wasm/CodeGen.zig+67-18
......@@ -4885,8 +4885,8 @@ fn airSatBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {
48854885
48864886 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
48874887 const ty = self.air.typeOfIndex(inst);
4888 const lhs_operand = try self.resolveInst(bin_op.lhs);
4889 const rhs_operand = try self.resolveInst(bin_op.rhs);
4888 const lhs = try self.resolveInst(bin_op.lhs);
4889 const rhs = try self.resolveInst(bin_op.rhs);
48904890
48914891 const int_info = ty.intInfo(self.target);
48924892 const is_signed = int_info.signedness == .signed;
......@@ -4895,22 +4895,12 @@ fn airSatBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {
48954895 return self.fail("TODO: saturating arithmetic for integers with bitsize '{d}'", .{int_info.bits});
48964896 }
48974897
4898 const wasm_bits = toWasmBits(int_info.bits).?;
4899
4900 const lhs = if (is_signed) blk: {
4901 break :blk try self.signAbsValue(lhs_operand, ty);
4902 } else lhs_operand;
4903 const rhs = if (is_signed) blk: {
4904 break :blk try self.signAbsValue(rhs_operand, ty);
4905 } else rhs_operand;
4906
4907 const opcode = buildOpcode(.{ .op = op, .valtype1 = typeToValtype(ty, self.target) });
4908 try self.emitWValue(lhs);
4909 try self.emitWValue(rhs);
4910 try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));
4911 const bin_result = try self.allocLocal(ty);
4912 try self.addLabel(.local_set, bin_result.local);
4898 if (is_signed) {
4899 return signedSat(self, lhs, rhs, ty, op);
4900 }
49134901
4902 const wasm_bits = toWasmBits(int_info.bits).?;
4903 const bin_result = try self.binOp(lhs, rhs, ty, op);
49144904 if (wasm_bits != int_info.bits and op == .add) {
49154905 const val: u64 = @intCast(u64, (@as(u65, 1) << @intCast(u7, int_info.bits)) - 1);
49164906 const imm_val = switch (wasm_bits) {
......@@ -4919,7 +4909,7 @@ fn airSatBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {
49194909 else => unreachable,
49204910 };
49214911
4922 const cmp_result = try self.cmp(bin_result, imm_val, ty, if (op == .add) .lt else .gt);
4912 const cmp_result = try self.cmp(bin_result, imm_val, ty, .lt);
49234913 try self.emitWValue(bin_result);
49244914 try self.emitWValue(imm_val);
49254915 try self.emitWValue(cmp_result);
......@@ -4939,3 +4929,62 @@ fn airSatBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {
49394929 try self.addLabel(.local_set, result.local);
49404930 return result;
49414931}
4932
4933fn signedSat(self: *Self, lhs_operand: WValue, rhs_operand: WValue, ty: Type, op: Op) InnerError!WValue {
4934 const int_info = ty.intInfo(self.target);
4935 const wasm_bits = toWasmBits(int_info.bits).?;
4936 const is_wasm_bits = wasm_bits == int_info.bits;
4937
4938 const lhs = if (!is_wasm_bits) try self.signAbsValue(lhs_operand, ty) else lhs_operand;
4939 const rhs = if (!is_wasm_bits) try self.signAbsValue(rhs_operand, ty) else rhs_operand;
4940
4941 const max_val: u64 = @intCast(u64, (@as(u65, 1) << @intCast(u7, int_info.bits - 1)) - 1);
4942 const min_val = @intCast(i64, ~@intCast(u63, max_val));
4943 const max_wvalue = switch (wasm_bits) {
4944 32 => WValue{ .imm32 = @intCast(u32, max_val) },
4945 64 => WValue{ .imm64 = max_val },
4946 else => unreachable,
4947 };
4948 const min_wvalue = switch (wasm_bits) {
4949 32 => WValue{ .imm32 = @bitCast(u32, @truncate(i32, min_val)) },
4950 64 => WValue{ .imm64 = @bitCast(u64, min_val) },
4951 else => unreachable,
4952 };
4953
4954 const bin_result = try self.binOp(lhs, rhs, ty, op);
4955 if (!is_wasm_bits) {
4956 const cmp_result_lt = try self.cmp(bin_result, max_wvalue, ty, .lt);
4957 try self.emitWValue(bin_result);
4958 try self.emitWValue(max_wvalue);
4959 try self.emitWValue(cmp_result_lt);
4960 try self.addTag(.select);
4961 try self.addLabel(.local_set, bin_result.local); // re-use local
4962
4963 const cmp_result_gt = try self.cmp(bin_result, min_wvalue, ty, .gt);
4964 try self.emitWValue(bin_result);
4965 try self.emitWValue(min_wvalue);
4966 try self.emitWValue(cmp_result_gt);
4967 try self.addTag(.select);
4968 try self.addLabel(.local_set, bin_result.local); // re-use local
4969 return self.wrapOperand(bin_result, ty);
4970 } else {
4971 const zero = switch (wasm_bits) {
4972 32 => WValue{ .imm32 = 0 },
4973 64 => WValue{ .imm64 = 0 },
4974 else => unreachable,
4975 };
4976 const cmp_bin_result = try self.cmp(bin_result, lhs, ty, .lt);
4977 const cmp_zero_result = try self.cmp(rhs, zero, ty, if (op == .add) .lt else .gt);
4978 const xor = try self.binOp(cmp_zero_result, cmp_bin_result, ty, .xor);
4979 const cmp_bin_zero_result = try self.cmp(bin_result, zero, ty, .lt);
4980 try self.emitWValue(max_wvalue);
4981 try self.emitWValue(min_wvalue);
4982 try self.emitWValue(cmp_bin_zero_result);
4983 try self.addTag(.select);
4984 try self.emitWValue(bin_result);
4985 try self.emitWValue(xor);
4986 try self.addTag(.select);
4987 try self.addLabel(.local_set, bin_result.local); // re-use local
4988 return bin_result;
4989 }
4990}