diff options
| author | Luuk de Gram <luuk@degram.dev> | 2022-06-15 20:26:53 +0200 |
|---|---|---|
| committer | Luuk de Gram <luuk@degram.dev> | 2022-06-19 14:30:13 +0200 |
| commit | fcd4280a8cabf7859fab450cc0d4b65f6adaabe5 (patch) | |
| tree | 3cea87c6ea1fbb7bdf0d679b5ab0a1ca39c95b77 /src/arch/wasm/CodeGen.zig | |
| parent | 33cf6ef621114daad63d14067b6ff374e664d410 (diff) | |
| download | zig-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.zig | 65 |
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; +} |
