aboutsummaryrefslogtreecommitdiff
path: root/src/arch/wasm/CodeGen.zig
diff options
context:
space:
mode:
authorRobin Voetter <robin@voetter.nl>2023-10-14 12:52:31 +0200
committerRobin Voetter <robin@voetter.nl>2023-10-15 14:00:32 +0200
commit9c20449cc5be5da0458556e143980b40d51e8776 (patch)
treecfa8ddf891494b237ba3e9a5268f222c90821c0f /src/arch/wasm/CodeGen.zig
parent2fe16e072ac447de5826ee436f50f73f56876ff9 (diff)
downloadzig-9c20449cc5be5da0458556e143980b40d51e8776.tar.gz
zig-9c20449cc5be5da0458556e143980b40d51e8776.zip
wasm: lower min/max for floats to compiler_rt
The min and max builtins in Zig have some intricate behavior related to floats, that is not replicated with the min and max wasm instructions or using simple select operations. By lowering these instructions to compiler_rt, handling around NaNs is done correctly. See also https://github.com/WebAssembly/design/issues/214
Diffstat (limited to 'src/arch/wasm/CodeGen.zig')
-rw-r--r--src/arch/wasm/CodeGen.zig28
1 files changed, 21 insertions, 7 deletions
diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig
index ebcccd781d..3eac273164 100644
--- a/src/arch/wasm/CodeGen.zig
+++ b/src/arch/wasm/CodeGen.zig
@@ -6253,8 +6253,10 @@ fn airMulWithOverflow(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
func.finishAir(inst, result_ptr, &.{ extra.lhs, extra.rhs });
}
-fn airMaxMin(func: *CodeGen, inst: Air.Inst.Index, op: enum { max, min }) InnerError!void {
+fn airMaxMin(func: *CodeGen, inst: Air.Inst.Index, op: Op) InnerError!void {
+ assert(op == .max or op == .min);
const mod = func.bin_file.base.options.module.?;
+ const target = mod.getTarget();
const bin_op = func.air.instructions.items(.data)[inst].bin_op;
const ty = func.typeOfIndex(inst);
@@ -6269,13 +6271,25 @@ fn airMaxMin(func: *CodeGen, inst: Air.Inst.Index, op: enum { max, min }) InnerE
const lhs = try func.resolveInst(bin_op.lhs);
const rhs = try func.resolveInst(bin_op.rhs);
- // operands to select from
- try func.lowerToStack(lhs);
- try func.lowerToStack(rhs);
- _ = try func.cmp(lhs, rhs, ty, if (op == .max) .gt else .lt);
+ if (ty.zigTypeTag(mod) == .Float) {
+ var fn_name_buf: [64]u8 = undefined;
+ const float_bits = ty.floatBits(target);
+ const fn_name = std.fmt.bufPrint(&fn_name_buf, "{s}f{s}{s}", .{
+ target_util.libcFloatPrefix(float_bits),
+ @tagName(op),
+ target_util.libcFloatSuffix(float_bits),
+ }) catch unreachable;
+ const result = try func.callIntrinsic(fn_name, &.{ ty.ip_index, ty.ip_index }, ty, &.{ lhs, rhs });
+ try func.lowerToStack(result);
+ } else {
+ // operands to select from
+ try func.lowerToStack(lhs);
+ try func.lowerToStack(rhs);
+ _ = try func.cmp(lhs, rhs, ty, if (op == .max) .gt else .lt);
- // based on the result from comparison, return operand 0 or 1.
- try func.addTag(.select);
+ // based on the result from comparison, return operand 0 or 1.
+ try func.addTag(.select);
+ }
// store result in local
const result_ty = if (isByRef(ty, mod)) Type.u32 else ty;