aboutsummaryrefslogtreecommitdiff
path: root/src/arch/wasm/CodeGen.zig
diff options
context:
space:
mode:
authorLuuk de Gram <luuk@degram.dev>2022-06-15 20:26:53 +0200
committerLuuk de Gram <luuk@degram.dev>2022-06-19 14:30:13 +0200
commitfcd4280a8cabf7859fab450cc0d4b65f6adaabe5 (patch)
tree3cea87c6ea1fbb7bdf0d679b5ab0a1ca39c95b77 /src/arch/wasm/CodeGen.zig
parent33cf6ef621114daad63d14067b6ff374e664d410 (diff)
downloadzig-fcd4280a8cabf7859fab450cc0d4b65f6adaabe5.tar.gz
zig-fcd4280a8cabf7859fab450cc0d4b65f6adaabe5.zip
wasm: implement saturating add, sub for unsigned
Implements +| and -| for unsigned integers <= 64 bits.
Diffstat (limited to 'src/arch/wasm/CodeGen.zig')
-rw-r--r--src/arch/wasm/CodeGen.zig65
1 files changed, 63 insertions, 2 deletions
diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig
index c395c26437..2e1d88083c 100644
--- a/src/arch/wasm/CodeGen.zig
+++ b/src/arch/wasm/CodeGen.zig
@@ -1432,8 +1432,10 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
.const_ty => unreachable,
.add => self.airBinOp(inst, .add),
+ .add_sat => self.airSatBinOp(inst, .add),
.addwrap => self.airWrapBinOp(inst, .add),
.sub => self.airBinOp(inst, .sub),
+ .sub_sat => self.airSatBinOp(inst, .sub),
.subwrap => self.airWrapBinOp(inst, .sub),
.mul => self.airBinOp(inst, .mul),
.mulwrap => self.airWrapBinOp(inst, .mul),
@@ -1583,8 +1585,6 @@ fn genInst(self: *Self, inst: Air.Inst.Index) !WValue {
.memcpy => self.airMemcpy(inst),
- .add_sat,
- .sub_sat,
.mul_sat,
.mod,
.assembly,
@@ -4878,3 +4878,64 @@ fn airCeilFloorTrunc(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValu
try self.addLabel(.local_set, result.local);
return result;
}
+
+fn airSatBinOp(self: *Self, inst: Air.Inst.Index, op: Op) InnerError!WValue {
+ assert(op == .add or op == .sub);
+ if (self.liveness.isUnused(inst)) return WValue{ .none = {} };
+
+ const bin_op = self.air.instructions.items(.data)[inst].bin_op;
+ const ty = self.air.typeOfIndex(inst);
+ const lhs_operand = try self.resolveInst(bin_op.lhs);
+ const rhs_operand = try self.resolveInst(bin_op.rhs);
+
+ const int_info = ty.intInfo(self.target);
+ const is_signed = int_info.signedness == .signed;
+
+ if (int_info.bits > 64) {
+ return self.fail("TODO: saturating arithmetic for integers with bitsize '{d}'", .{int_info.bits});
+ }
+
+ const wasm_bits = toWasmBits(int_info.bits).?;
+
+ const lhs = if (is_signed) blk: {
+ break :blk try self.signAbsValue(lhs_operand, ty);
+ } else lhs_operand;
+ const rhs = if (is_signed) blk: {
+ break :blk try self.signAbsValue(rhs_operand, ty);
+ } else rhs_operand;
+
+ const opcode = buildOpcode(.{ .op = op, .valtype1 = typeToValtype(ty, self.target) });
+ try self.emitWValue(lhs);
+ try self.emitWValue(rhs);
+ try self.addTag(Mir.Inst.Tag.fromOpcode(opcode));
+ const bin_result = try self.allocLocal(ty);
+ try self.addLabel(.local_set, bin_result.local);
+
+ if (wasm_bits != int_info.bits and op == .add) {
+ const val: u64 = @intCast(u64, (@as(u65, 1) << @intCast(u7, int_info.bits)) - 1);
+ const imm_val = switch (wasm_bits) {
+ 32 => WValue{ .imm32 = @intCast(u32, val) },
+ 64 => WValue{ .imm64 = val },
+ else => unreachable,
+ };
+
+ const cmp_result = try self.cmp(bin_result, imm_val, ty, if (op == .add) .lt else .gt);
+ try self.emitWValue(bin_result);
+ try self.emitWValue(imm_val);
+ try self.emitWValue(cmp_result);
+ } else {
+ const cmp_result = try self.cmp(bin_result, lhs, ty, if (op == .add) .lt else .gt);
+ switch (wasm_bits) {
+ 32 => try self.addImm32(if (op == .add) @as(i32, -1) else 0),
+ 64 => try self.addImm64(if (op == .add) @bitCast(u64, @as(i64, -1)) else 0),
+ else => unreachable,
+ }
+ try self.emitWValue(bin_result);
+ try self.emitWValue(cmp_result);
+ }
+
+ try self.addTag(.select);
+ const result = try self.allocLocal(ty);
+ try self.addLabel(.local_set, result.local);
+ return result;
+}