Skip to content

Commit

Permalink
wasm: lower min/max for floats to compiler_rt
Browse files Browse the repository at this point in the history
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 WebAssembly/design#214
  • Loading branch information
Snektron committed Oct 14, 2023
1 parent ea8efa0 commit fafa75b
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 7 deletions.
28 changes: 21 additions & 7 deletions src/arch/wasm/CodeGen.zig
Original file line number Diff line number Diff line change
Expand Up @@ -6234,8 +6234,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);
Expand All @@ -6250,13 +6252,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;
Expand Down
6 changes: 6 additions & 0 deletions test/behavior/maximum_minimum.zig
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,12 @@ test "@min/max for floats" {
try expectEqual(x, @min(y, x));
try expectEqual(y, @max(x, y));
try expectEqual(y, @max(y, x));

if (T != comptime_float) {
var nan: T = std.math.nan(T);
try expectEqual(y, @max(nan, y));
try expectEqual(y, @max(y, nan));
}
}
};

Expand Down

0 comments on commit fafa75b

Please sign in to comment.