Index: include/llvm/IR/IntrinsicsWebAssembly.td =================================================================== --- include/llvm/IR/IntrinsicsWebAssembly.td +++ include/llvm/IR/IntrinsicsWebAssembly.td @@ -36,6 +36,20 @@ def int_wasm_current_memory : Intrinsic<[llvm_anyint_ty], [], [IntrReadMem]>; def int_wasm_grow_memory : Intrinsic<[llvm_anyint_ty], [LLVMMatchType<0>], []>; +// Returns min/max of two floats or lanewise min/max two of vectors of +// floats using WebAssembly's NaN propagation rules. Specifically, if +// either argument is NaN, return NaN. This is in contrast to the +// llvm.minnum and llvm.maxnum instrinsics, which return the non-NaN +// argument if it exists. +def int_wasm_min : Intrinsic<[llvm_anyfloat_ty], + [LLVMMatchType<0>, LLVMMatchType<0>], + [IntrNoMem, IntrSpeculatable, Commutative] +>; +def int_wasm_max : Intrinsic<[llvm_anyfloat_ty], + [LLVMMatchType<0>, LLVMMatchType<0>], + [IntrNoMem, IntrSpeculatable, Commutative] +>; + //===----------------------------------------------------------------------===// // Exception handling intrinsics //===----------------------------------------------------------------------===// Index: lib/Analysis/ConstantFolding.cpp =================================================================== --- lib/Analysis/ConstantFolding.cpp +++ lib/Analysis/ConstantFolding.cpp @@ -1400,6 +1400,8 @@ case Intrinsic::convert_from_fp16: case Intrinsic::convert_to_fp16: case Intrinsic::bitreverse: + case Intrinsic::wasm_max: + case Intrinsic::wasm_min: case Intrinsic::x86_sse_cvtss2si: case Intrinsic::x86_sse_cvtss2si64: case Intrinsic::x86_sse_cvttss2si: @@ -1912,6 +1914,28 @@ return ConstantFP::get(Ty->getContext(), maxnum(C1, C2)); } + if (IntrinsicID == Intrinsic::wasm_min) { + const APFloat &C1 = Op1->getValueAPF(); + const APFloat &C2 = Op2->getValueAPF(); + APFloat F = minnum(C1, C2); + if (C1.isNaN()) + F = C1; + else if (C2.isNaN()) + F = C2; + return ConstantFP::get(Ty->getContext(), F); + } + + if (IntrinsicID == Intrinsic::wasm_max) { + const APFloat &C1 = Op1->getValueAPF(); + const APFloat &C2 = Op2->getValueAPF(); + APFloat F = maxnum(C1, C2); + if (C1.isNaN()) + F = C1; + else if (C2.isNaN()) + F = C2; + return ConstantFP::get(Ty->getContext(), F); + } + if (!TLI) return nullptr; if ((Name == "pow" && TLI->has(LibFunc_pow)) || Index: lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp =================================================================== --- lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -5588,6 +5588,18 @@ getValue(I.getArgOperand(1)))); return nullptr; } + case Intrinsic::wasm_min: + setValue(&I, DAG.getNode(ISD::FMINNAN, sdl, + getValue(I.getArgOperand(0)).getValueType(), + getValue(I.getArgOperand(0)), + getValue(I.getArgOperand(1)))); + return nullptr; + case Intrinsic::wasm_max: + setValue(&I, DAG.getNode(ISD::FMAXNAN, sdl, + getValue(I.getArgOperand(0)).getValueType(), + getValue(I.getArgOperand(0)), + getValue(I.getArgOperand(1)))); + return nullptr; case Intrinsic::copysign: setValue(&I, DAG.getNode(ISD::FCOPYSIGN, sdl, getValue(I.getArgOperand(0)).getValueType(), Index: test/Analysis/ConstantFolding/min-max.ll =================================================================== --- /dev/null +++ test/Analysis/ConstantFolding/min-max.ll @@ -0,0 +1,43 @@ +; RUN: opt -instcombine -S -o - %s | FileCheck %s +; Tests that constant folding of min and max operations works as expected. + +declare float @llvm.wasm.min.f32(float, float) +declare float @llvm.wasm.max.f32(float, float) +declare <4 x float> @llvm.wasm.min.v4f32(<4 x float>, <4 x float>) +declare <4 x float> @llvm.wasm.max.v4f32(<4 x float>, <4 x float>) + +; CHECK: define float @wasm_min_float() { +define float @wasm_min_float() { + ; CHECK-NEXT: ret float 5.000000e+00 + %1 = call float @llvm.wasm.min.f32(float 5.0, float 42.0) + ret float %1 +} + +; CHECK: define <4 x float> @wasm_min_float_vec() { +define <4 x float> @wasm_min_float_vec() { + ; CHECK-NEXT: ret <4 x float> + %1 = call <4 x float> @llvm.wasm.min.v4f32( + <4 x float> , + <4 x float> + ) + ret <4 x float> %1 +} + +; CHECK: define float @wasm_max_float() { +define float @wasm_max_float() { + ; CHECK-NEXT: ret float 4.200000e+01 + %1 = call float @llvm.wasm.max.f32(float 5.0, float 42.0) + ret float %1 +} + +; CHECK: define <4 x float> @wasm_max_float_vec() { +define <4 x float> @wasm_max_float_vec() { + ; CHECK-NEXT: ret <4 x float> + %1 = call <4 x float> @llvm.wasm.max.v4f32( + <4 x float> , + <4 x float> + ) + ret <4 x float> %1 +} Index: test/CodeGen/WebAssembly/simd-arith.ll =================================================================== --- test/CodeGen/WebAssembly/simd-arith.ll +++ test/CodeGen/WebAssembly/simd-arith.ll @@ -707,6 +707,30 @@ ret <4 x float> %a } +; CHECK-LABEL: min_intrinsic_v4f32: +; NO-SIMD128-NOT: f32x4 +; SIMD128-NEXT: .param v128, v128{{$}} +; SIMD128-NEXT: .result v128{{$}} +; SIMD128-NEXT: f32x4.min $push[[R:[0-9]+]]=, $0, $1{{$}} +; SIMD128-NEXT: return $pop[[R]]{{$}} +declare <4 x float> @llvm.wasm.min.v4f32(<4 x float>, <4 x float>) +define <4 x float> @min_intrinsic_v4f32(<4 x float> %x, <4 x float> %y) { + %a = call <4 x float> @llvm.wasm.min.v4f32(<4 x float> %x, <4 x float> %y) + ret <4 x float> %a +} + +; CHECK-LABEL: max_intrinsic_v4f32: +; NO-SIMD128-NOT: f32x4 +; SIMD128-NEXT: .param v128, v128{{$}} +; SIMD128-NEXT: .result v128{{$}} +; SIMD128-NEXT: f32x4.max $push[[R:[0-9]+]]=, $0, $1{{$}} +; SIMD128-NEXT: return $pop[[R]]{{$}} +declare <4 x float> @llvm.wasm.max.v4f32(<4 x float>, <4 x float>) +define <4 x float> @max_intrinsic_v4f32(<4 x float> %x, <4 x float> %y) { + %a = call <4 x float> @llvm.wasm.max.v4f32(<4 x float> %x, <4 x float> %y) + ret <4 x float> %a +} + ; CHECK-LABEL: add_v4f32: ; NO-SIMD128-NOT: f32x4 ; SIMD128-NEXT: .param v128, v128{{$}} @@ -805,6 +829,30 @@ ret <2 x double> %a } +; CHECK-LABEL: min_intrinsic_v2f64: +; NO-SIMD128-NOT: f64x2 +; SIMD128-NEXT: .param v128, v128{{$}} +; SIMD128-NEXT: .result v128{{$}} +; SIMD128-NEXT: f64x2.min $push[[R:[0-9]+]]=, $0, $1{{$}} +; SIMD128-NEXT: return $pop[[R]]{{$}} +declare <2 x double> @llvm.wasm.min.v2f64(<2 x double>, <2 x double>) +define <2 x double> @min_intrinsic_v2f64(<2 x double> %x, <2 x double> %y) { + %a = call <2 x double> @llvm.wasm.min.v2f64(<2 x double> %x, <2 x double> %y) + ret <2 x double> %a +} + +; CHECK-LABEL: max_intrinsic_v2f64: +; NO-SIMD128-NOT: f64x2 +; SIMD128-NEXT: .param v128, v128{{$}} +; SIMD128-NEXT: .result v128{{$}} +; SIMD128-NEXT: f64x2.max $push[[R:[0-9]+]]=, $0, $1{{$}} +; SIMD128-NEXT: return $pop[[R]]{{$}} +declare <2 x double> @llvm.wasm.max.v2f64(<2 x double>, <2 x double>) +define <2 x double> @max_intrinsic_v2f64(<2 x double> %x, <2 x double> %y) { + %a = call <2 x double> @llvm.wasm.max.v2f64(<2 x double> %x, <2 x double> %y) + ret <2 x double> %a +} + ; CHECK-LABEL: add_v2f64: ; NO-SIMD128-NOT: f64x2 ; SIMD128-VM-NOT: f62x2