wasm: 128bit integer cmp support

This implements support for all compare operations on a 128bit integer,
for both signed and unsigned integers.

The new implementation is almost more efficient as it requires no control-flow,
unlike the old implementation which used a block with breaks.
This commit is contained in:
Luuk de Gram
2022-05-10 20:23:03 +02:00
parent c0ad0606df
commit 59d3714b8d

View File

@@ -2962,7 +2962,7 @@ fn intcast(self: *Self, operand: WValue, given: Type, wanted: Type) InnerError!W
try self.store(stack_ptr, .{ .imm64 = 0 }, Type.u64, 8);
}
return stack_ptr;
} else unreachable;
} else return self.fail("todo Wasm @intCast to 128bit integers", .{});
const result = try self.allocLocal(wanted);
try self.addLabel(.local_set, result.local);
@@ -3710,35 +3710,44 @@ fn cmpOptionals(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std
}
/// Compares big integers by checking both its high bits and low bits.
/// TODO: Lower this to compiler_rt call
/// TODO: Lower this to compiler_rt call when bitsize > 128
fn cmpBigInt(self: *Self, lhs: WValue, rhs: WValue, operand_ty: Type, op: std.math.CompareOperator) InnerError!WValue {
if (operand_ty.intInfo(self.target).bits > 128) {
return self.fail("TODO: Support cmpBigInt for integer bitsize: '{d}'", .{operand_ty.intInfo(self.target).bits});
}
const result = try self.allocLocal(Type.initTag(.i32));
{
try self.startBlock(.block, wasm.block_empty);
const lhs_high_bit = try self.load(lhs, Type.u64, 0);
const lhs_low_bit = try self.load(lhs, Type.u64, 8);
const rhs_high_bit = try self.load(rhs, Type.u64, 0);
const rhs_low_bit = try self.load(rhs, Type.u64, 8);
try self.emitWValue(lhs_high_bit);
try self.emitWValue(rhs_high_bit);
try self.addTag(.i64_ne);
try self.addLabel(.br_if, 0);
try self.emitWValue(lhs_low_bit);
try self.emitWValue(rhs_low_bit);
try self.addTag(.i64_ne);
try self.addLabel(.br_if, 0);
try self.addImm32(1);
try self.addLabel(.local_set, result.local);
try self.endBlock();
const lhs_high_bit = try self.load(lhs, Type.u64, 0);
const lhs_low_bit = try self.load(lhs, Type.u64, 8);
const rhs_high_bit = try self.load(rhs, Type.u64, 0);
const rhs_low_bit = try self.load(rhs, Type.u64, 8);
try self.emitWValue(or_result);
switch (op) {
.eq, .neq => {
const xor_high = try self.binOp(lhs_high_bit, rhs_high_bit, Type.u64, .xor);
const xor_low = try self.binOp(lhs_low_bit, rhs_low_bit, Type.u64, .xor);
const or_result = try self.binOp(xor_high, xor_low, Type.u64, .@"or");
switch (op) {
.eq => try self.addTag(.i64_eqz),
.neq => return self.cmp(or_result, .{ .imm32 = 0 }, Type.u32, .neq),
else => unreachable,
}
},
else => {
const ty = if (operand_ty.isSignedInt()) Type.i64 else Type.u64;
const low_bit_eql = try self.cmp(lhs_low_bit, rhs_low_bit, ty, .eq);
const high_bit_cmp = try self.cmp(lhs_high_bit, rhs_high_bit, ty, op);
const low_bit_cmp = try self.cmp(lhs_low_bit, rhs_low_bit, ty, op);
try self.emitWValue(low_bit_cmp);
try self.emitWValue(high_bit_cmp);
try self.emitWValue(low_bit_eql);
try self.addTag(.select);
},
}
try self.emitWValue(result);
try self.addImm32(0);
try self.addTag(if (op == .eq) .i32_ne else .i32_eq);
const result = try self.allocLocal(Type.initTag(.i32));
try self.addLabel(.local_set, result.local);
return result;
}