| ... | ... | @@ -1432,8 +1432,10 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { |
| 1432 | 1432 | .const_ty => unreachable, |
| 1433 | 1433 | |
| 1434 | 1434 | .add => self.airBinOp(inst, .add), |
| 1435 | .add_sat => self.airSatBinOp(inst, .add), |
| 1435 | 1436 | .addwrap => self.airWrapBinOp(inst, .add), |
| 1436 | 1437 | .sub => self.airBinOp(inst, .sub), |
| 1438 | .sub_sat => self.airSatBinOp(inst, .sub), |
| 1437 | 1439 | .subwrap => self.airWrapBinOp(inst, .sub), |
| 1438 | 1440 | .mul => self.airBinOp(inst, .mul), |
| 1439 | 1441 | .mulwrap => self.airWrapBinOp(inst, .mul), |
| ... | ... | @@ -1583,8 +1585,6 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue { |
| 1583 | 1585 | |
| 1584 | 1586 | .memcpy => self.airMemcpy(inst), |
| 1585 | 1587 | |
| 1586 | | .add_sat, |
| 1587 | | .sub_sat, |
| 1588 | 1588 | .mul_sat, |
| 1589 | 1589 | .mod, |
| 1590 | 1590 | .assembly, |
| ... | ... | @@ -4878,3 +4878,64 @@ fn airCeilFloorTrunc(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValu |
| 4878 | 4878 | try self.addLabel(.local_set, result.local); |
| 4879 | 4879 | return result; |
| 4880 | 4880 | } |
| 4881 | |
| 4882 | fn airSatBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue { |
| 4883 | assert(op == .add or op == .sub); |
| 4884 | if (self.liveness.isUnused(inst)) return WValue{ .none = {} }; |
| 4885 | |
| 4886 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 4887 | 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); |
| 4890 | |
| 4891 | const int_info = ty.intInfo(self.target); |
| 4892 | const is_signed = int_info.signedness == .signed; |
| 4893 | |
| 4894 | if (int_info.bits > 64) { |
| 4895 | return self.fail("TODO: saturating arithmetic for integers with bitsize '{d}'", .{int_info.bits}); |
| 4896 | } |
| 4897 | |
| 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); |
| 4913 | |
| 4914 | if (wasm_bits != int_info.bits and op == .add) { |
| 4915 | const val: u64 = @intCast(u64, (@as(u65, 1) << @intCast(u7, int_info.bits)) - 1); |
| 4916 | const imm_val = switch (wasm_bits) { |
| 4917 | 32 => WValue{ .imm32 = @intCast(u32, val) }, |
| 4918 | 64 => WValue{ .imm64 = val }, |
| 4919 | else => unreachable, |
| 4920 | }; |
| 4921 | |
| 4922 | const cmp_result = try self.cmp(bin_result, imm_val, ty, if (op == .add) .lt else .gt); |
| 4923 | try self.emitWValue(bin_result); |
| 4924 | try self.emitWValue(imm_val); |
| 4925 | try self.emitWValue(cmp_result); |
| 4926 | } else { |
| 4927 | const cmp_result = try self.cmp(bin_result, lhs, ty, if (op == .add) .lt else .gt); |
| 4928 | switch (wasm_bits) { |
| 4929 | 32 => try self.addImm32(if (op == .add) @as(i32, -1) else 0), |
| 4930 | 64 => try self.addImm64(if (op == .add) @bitCast(u64, @as(i64, -1)) else 0), |
| 4931 | else => unreachable, |
| 4932 | } |
| 4933 | try self.emitWValue(bin_result); |
| 4934 | try self.emitWValue(cmp_result); |
| 4935 | } |
| 4936 | |
| 4937 | try self.addTag(.select); |
| 4938 | const result = try self.allocLocal(ty); |
| 4939 | try self.addLabel(.local_set, result.local); |
| 4940 | return result; |
| 4941 | } |