diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp --- a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp @@ -125,8 +125,8 @@ setOperationAction(Op, T, Expand); // Note supported floating-point library function operators that otherwise // default to expand. - for (auto Op : - {ISD::FCEIL, ISD::FFLOOR, ISD::FTRUNC, ISD::FNEARBYINT, ISD::FRINT}) + for (auto Op : {ISD::FCEIL, ISD::FFLOOR, ISD::FTRUNC, ISD::FNEARBYINT, + ISD::FRINT, ISD::FROUNDEVEN}) setOperationAction(Op, T, Legal); // Support minimum and maximum, which otherwise default to expand. setOperationAction(ISD::FMINIMUM, T, Legal); @@ -247,7 +247,7 @@ // Expand float operations supported for scalars but not SIMD for (auto Op : {ISD::FCOPYSIGN, ISD::FLOG, ISD::FLOG2, ISD::FLOG10, - ISD::FEXP, ISD::FEXP2, ISD::FRINT}) + ISD::FEXP, ISD::FEXP2}) for (auto T : {MVT::v4f32, MVT::v2f64}) setOperationAction(Op, T, Expand); diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrFloat.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrFloat.td --- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrFloat.td +++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrFloat.td @@ -76,6 +76,10 @@ def : Pat<(frint f32:$src), (NEAREST_F32 f32:$src)>; def : Pat<(frint f64:$src), (NEAREST_F64 f64:$src)>; +// WebAssembly always rounds ties-to-even, so map froundeven to fnearbyint. +def : Pat<(froundeven f32:$src), (NEAREST_F32 f32:$src)>; +def : Pat<(froundeven f64:$src), (NEAREST_F64 f64:$src)>; + let isCommutable = 1 in { defm EQ : ComparisonFP; defm NE : ComparisonFP; diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyInstrSIMD.td b/llvm/lib/Target/WebAssembly/WebAssemblyInstrSIMD.td --- a/llvm/lib/Target/WebAssembly/WebAssemblyInstrSIMD.td +++ b/llvm/lib/Target/WebAssembly/WebAssemblyInstrSIMD.td @@ -1158,6 +1158,14 @@ defm TRUNC: SIMDUnary; defm NEAREST: SIMDUnary; +// WebAssembly doesn't expose inexact exceptions, so map frint to fnearbyint. +def : Pat<(v4f32 (frint (v4f32 V128:$src))), (NEAREST_F32x4 V128:$src)>; +def : Pat<(v2f64 (frint (v2f64 V128:$src))), (NEAREST_F64x2 V128:$src)>; + +// WebAssembly always rounds ties-to-even, so map froundeven to fnearbyint. +def : Pat<(v4f32 (froundeven (v4f32 V128:$src))), (NEAREST_F32x4 V128:$src)>; +def : Pat<(v2f64 (froundeven (v2f64 V128:$src))), (NEAREST_F64x2 V128:$src)>; + //===----------------------------------------------------------------------===// // Floating-point binary arithmetic //===----------------------------------------------------------------------===// diff --git a/llvm/test/CodeGen/WebAssembly/f32.ll b/llvm/test/CodeGen/WebAssembly/f32.ll --- a/llvm/test/CodeGen/WebAssembly/f32.ll +++ b/llvm/test/CodeGen/WebAssembly/f32.ll @@ -13,6 +13,7 @@ declare float @llvm.trunc.f32(float) declare float @llvm.nearbyint.f32(float) declare float @llvm.rint.f32(float) +declare float @llvm.roundeven.f32(float) declare float @llvm.fma.f32(float, float, float) define float @fadd32(float %x, float %y) { @@ -163,6 +164,17 @@ ret float %a } +define float @nearest32_via_roundeven(float %x) { +; CHECK-LABEL: nearest32_via_roundeven: +; CHECK: .functype nearest32_via_roundeven (f32) -> (f32) +; CHECK-NEXT: # %bb.0: +; CHECK-NEXT: local.get $push1=, 0 +; CHECK-NEXT: f32.nearest $push0=, $pop1 +; CHECK-NEXT: return $pop0 + %a = call float @llvm.roundeven.f32(float %x) + ret float %a +} + ; This is not "minimum" because a -0.0 input returns +0.0. define float @fmin32(float %x) { diff --git a/llvm/test/CodeGen/WebAssembly/f64.ll b/llvm/test/CodeGen/WebAssembly/f64.ll --- a/llvm/test/CodeGen/WebAssembly/f64.ll +++ b/llvm/test/CodeGen/WebAssembly/f64.ll @@ -13,6 +13,7 @@ declare double @llvm.trunc.f64(double) declare double @llvm.nearbyint.f64(double) declare double @llvm.rint.f64(double) +declare double @llvm.roundeven.f64(double) declare double @llvm.fma.f64(double, double, double) define double @fadd64(double %x, double %y) { @@ -163,6 +164,17 @@ ret double %a } +define double @nearest64_via_roundeven(double %x) { +; CHECK-LABEL: nearest64_via_roundeven: +; CHECK: .functype nearest64_via_roundeven (f64) -> (f64) +; CHECK-NEXT: # %bb.0: +; CHECK-NEXT: local.get $push1=, 0 +; CHECK-NEXT: f64.nearest $push0=, $pop1 +; CHECK-NEXT: return $pop0 + %a = call double @llvm.roundeven.f64(double %x) + ret double %a +} + ; This is not "minimum" because a -0.0 input returns +0.0. define double @fmin64(double %x) { diff --git a/llvm/test/CodeGen/WebAssembly/libcalls.ll b/llvm/test/CodeGen/WebAssembly/libcalls.ll --- a/llvm/test/CodeGen/WebAssembly/libcalls.ll +++ b/llvm/test/CodeGen/WebAssembly/libcalls.ll @@ -18,7 +18,6 @@ declare double @llvm.powi.f64.i32(double, i32) declare double @llvm.log.f64(double) declare double @llvm.exp.f64(double) -declare double @llvm.roundeven.f64(double) declare double @llvm.ldexp.f64.i32(double, i32) declare i32 @llvm.lround(double) @@ -236,21 +235,20 @@ ; CHECK-LABEL: f64libcalls: ; CHECK: .functype f64libcalls (f64, f64, i32) -> (f64) ; CHECK-NEXT: # %bb.0: -; CHECK-NEXT: local.get $push13=, 0 -; CHECK-NEXT: local.get $push10=, 0 -; CHECK-NEXT: call $push0=, cos, $pop10 +; CHECK-NEXT: local.get $push12=, 0 +; CHECK-NEXT: local.get $push9=, 0 +; CHECK-NEXT: call $push0=, cos, $pop9 ; CHECK-NEXT: call $push1=, log10, $pop0 -; CHECK-NEXT: local.get $push11=, 1 -; CHECK-NEXT: call $push2=, pow, $pop1, $pop11 -; CHECK-NEXT: local.get $push12=, 2 -; CHECK-NEXT: call $push3=, __powidf2, $pop2, $pop12 +; CHECK-NEXT: local.get $push10=, 1 +; CHECK-NEXT: call $push2=, pow, $pop1, $pop10 +; CHECK-NEXT: local.get $push11=, 2 +; CHECK-NEXT: call $push3=, __powidf2, $pop2, $pop11 ; CHECK-NEXT: call $push4=, log, $pop3 ; CHECK-NEXT: call $push5=, exp, $pop4 ; CHECK-NEXT: call $push6=, cbrt, $pop5 -; CHECK-NEXT: call $push7=, roundeven, $pop6 -; CHECK-NEXT: call $push8=, lround, $pop7 -; CHECK-NEXT: call $push9=, ldexp, $pop13, $pop8 -; CHECK-NEXT: return $pop9 +; CHECK-NEXT: call $push7=, lround, $pop6 +; CHECK-NEXT: call $push8=, ldexp, $pop12, $pop7 +; CHECK-NEXT: return $pop8 %a = call double @llvm.cos.f64(double %x) %b = call double @llvm.log10.f64(double %a) %c = call double @llvm.pow.f64(double %b, double %y) @@ -258,10 +256,9 @@ %e = call double @llvm.log.f64(double %d) %f = call double @llvm.exp.f64(double %e) %g = call fast double @llvm.pow.f64(double %f, double 0x3FD5555555555555) - %h = call double @llvm.roundeven.f64(double %g) - %i = call i32 @llvm.lround(double %h) - %j = call double @llvm.ldexp.f64.i32(double %x, i32 %i); - ret double %j + %h = call i32 @llvm.lround(double %g) + %i = call double @llvm.ldexp.f64.i32(double %x, i32 %h); + ret double %i } ; fcmp ord and unord (RTLIB::O_F32 / RTLIB::UO_F32 etc) are a special case (see diff --git a/llvm/test/CodeGen/WebAssembly/simd-intrinsics.ll b/llvm/test/CodeGen/WebAssembly/simd-intrinsics.ll --- a/llvm/test/CodeGen/WebAssembly/simd-intrinsics.ll +++ b/llvm/test/CodeGen/WebAssembly/simd-intrinsics.ll @@ -725,6 +725,26 @@ ret <4 x float> %v } +; CHECK-LABEL: nearest_v4f32_via_rint: +; CHECK-NEXT: .functype nearest_v4f32_via_rint (v128) -> (v128){{$}} +; CHECK-NEXT: f32x4.nearest $push[[R:[0-9]+]]=, $0{{$}} +; CHECK-NEXT: return $pop[[R]]{{$}} +declare <4 x float> @llvm.rint.v4f32(<4 x float>) +define <4 x float> @nearest_v4f32_via_rint(<4 x float> %a) { + %v = call <4 x float> @llvm.rint.v4f32(<4 x float> %a) + ret <4 x float> %v +} + +; CHECK-LABEL: nearest_v4f32_via_roundeven: +; CHECK-NEXT: .functype nearest_v4f32_via_roundeven (v128) -> (v128){{$}} +; CHECK-NEXT: f32x4.nearest $push[[R:[0-9]+]]=, $0{{$}} +; CHECK-NEXT: return $pop[[R]]{{$}} +declare <4 x float> @llvm.roundeven.v4f32(<4 x float>) +define <4 x float> @nearest_v4f32_via_roundeven(<4 x float> %a) { + %v = call <4 x float> @llvm.roundeven.v4f32(<4 x float> %a) + ret <4 x float> %v +} + ; CHECK-LABEL: madd_v4f32: ; CHECK-NEXT: .functype madd_v4f32 (v128, v128, v128) -> (v128){{$}} ; CHECK-NEXT: f32x4.relaxed_madd $push[[R:[0-9]+]]=, $0, $1, $2{{$}} @@ -862,6 +882,26 @@ ret <2 x double> %v } +; CHECK-LABEL: nearest_v2f64_via_rint: +; CHECK-NEXT: .functype nearest_v2f64_via_rint (v128) -> (v128){{$}} +; CHECK-NEXT: f64x2.nearest $push[[R:[0-9]+]]=, $0{{$}} +; CHECK-NEXT: return $pop[[R]]{{$}} +declare <2 x double> @llvm.rint.v2f64(<2 x double>) +define <2 x double> @nearest_v2f64_via_rint(<2 x double> %a) { + %v = call <2 x double> @llvm.rint.v2f64(<2 x double> %a) + ret <2 x double> %v +} + +; CHECK-LABEL: nearest_v2f64_via_roundeven: +; CHECK-NEXT: .functype nearest_v2f64_via_roundeven (v128) -> (v128){{$}} +; CHECK-NEXT: f64x2.nearest $push[[R:[0-9]+]]=, $0{{$}} +; CHECK-NEXT: return $pop[[R]]{{$}} +declare <2 x double> @llvm.roundeven.v2f64(<2 x double>) +define <2 x double> @nearest_v2f64_via_roundeven(<2 x double> %a) { + %v = call <2 x double> @llvm.roundeven.v2f64(<2 x double> %a) + ret <2 x double> %v +} + ; CHECK-LABEL: madd_v2f64: ; CHECK-NEXT: .functype madd_v2f64 (v128, v128, v128) -> (v128){{$}} ; CHECK-NEXT: f64x2.relaxed_madd $push[[R:[0-9]+]]=, $0, $1, $2{{$}} diff --git a/llvm/test/CodeGen/WebAssembly/simd-unsupported.ll b/llvm/test/CodeGen/WebAssembly/simd-unsupported.ll --- a/llvm/test/CodeGen/WebAssembly/simd-unsupported.ll +++ b/llvm/test/CodeGen/WebAssembly/simd-unsupported.ll @@ -433,14 +433,6 @@ ret <4 x float> %v } -; CHECK-LABEL: rint_v4f32: -; CHECK: f32.nearest -declare <4 x float> @llvm.rint.v4f32(<4 x float>) -define <4 x float> @rint_v4f32(<4 x float> %x) { - %v = call <4 x float> @llvm.rint.v4f32(<4 x float> %x) - ret <4 x float> %v -} - ; CHECK-LABEL: round_v4f32: ; CHECK: call $push[[L:[0-9]+]]=, roundf declare <4 x float> @llvm.round.v4f32(<4 x float>) @@ -533,14 +525,6 @@ ret <2 x double> %v } -; CHECK-LABEL: rint_v2f64: -; CHECK: f64.nearest -declare <2 x double> @llvm.rint.v2f64(<2 x double>) -define <2 x double> @rint_v2f64(<2 x double> %x) { - %v = call <2 x double> @llvm.rint.v2f64(<2 x double> %x) - ret <2 x double> %v -} - ; CHECK-LABEL: round_v2f64: ; CHECK: call $push[[L:[0-9]+]]=, round declare <2 x double> @llvm.round.v2f64(<2 x double>)