This restriction was originally added in https://reviews.llvm.org/D143256, with the given justification: > Currently, in TargetLowering, if the target does not support fminnum, we lower to fminimum if neither operand could be a NaN. But this isn't quite correct because fminnum and fminimum treat +/-0 differently; so, we need to prove that one of the operands isn't a zero. As far as I can tell, this was never correct. Before https://github.com/llvm/llvm-project/pull/172012, `minnum` and `maxnum` were nondeterministic with regards to signed zero, so it's always been perfectly legal to lower them to operations that order signed zeroes.
350 lines
11 KiB
LLVM
350 lines
11 KiB
LLVM
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
|
|
; RUN: llc < %s -disable-wasm-fallthrough-return-opt -wasm-keep-registers | FileCheck %s
|
|
|
|
; Test that basic 64-bit floating-point operations assemble as expected.
|
|
|
|
target triple = "wasm32-unknown-unknown"
|
|
|
|
declare double @llvm.fabs.f64(double)
|
|
declare double @llvm.copysign.f64(double, double)
|
|
declare double @llvm.sqrt.f64(double)
|
|
declare double @llvm.ceil.f64(double)
|
|
declare double @llvm.floor.f64(double)
|
|
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) {
|
|
; CHECK-LABEL: fadd64:
|
|
; CHECK: .functype fadd64 (f64, f64) -> (f64)
|
|
; CHECK-NEXT: # %bb.0:
|
|
; CHECK-NEXT: local.get $push2=, 0
|
|
; CHECK-NEXT: local.get $push1=, 1
|
|
; CHECK-NEXT: f64.add $push0=, $pop2, $pop1
|
|
; CHECK-NEXT: return $pop0
|
|
%a = fadd double %x, %y
|
|
ret double %a
|
|
}
|
|
|
|
define double @fsub64(double %x, double %y) {
|
|
; CHECK-LABEL: fsub64:
|
|
; CHECK: .functype fsub64 (f64, f64) -> (f64)
|
|
; CHECK-NEXT: # %bb.0:
|
|
; CHECK-NEXT: local.get $push2=, 0
|
|
; CHECK-NEXT: local.get $push1=, 1
|
|
; CHECK-NEXT: f64.sub $push0=, $pop2, $pop1
|
|
; CHECK-NEXT: return $pop0
|
|
%a = fsub double %x, %y
|
|
ret double %a
|
|
}
|
|
|
|
define double @fmul64(double %x, double %y) {
|
|
; CHECK-LABEL: fmul64:
|
|
; CHECK: .functype fmul64 (f64, f64) -> (f64)
|
|
; CHECK-NEXT: # %bb.0:
|
|
; CHECK-NEXT: local.get $push2=, 0
|
|
; CHECK-NEXT: local.get $push1=, 1
|
|
; CHECK-NEXT: f64.mul $push0=, $pop2, $pop1
|
|
; CHECK-NEXT: return $pop0
|
|
%a = fmul double %x, %y
|
|
ret double %a
|
|
}
|
|
|
|
define double @fdiv64(double %x, double %y) {
|
|
; CHECK-LABEL: fdiv64:
|
|
; CHECK: .functype fdiv64 (f64, f64) -> (f64)
|
|
; CHECK-NEXT: # %bb.0:
|
|
; CHECK-NEXT: local.get $push2=, 0
|
|
; CHECK-NEXT: local.get $push1=, 1
|
|
; CHECK-NEXT: f64.div $push0=, $pop2, $pop1
|
|
; CHECK-NEXT: return $pop0
|
|
%a = fdiv double %x, %y
|
|
ret double %a
|
|
}
|
|
|
|
define double @fabs64(double %x) {
|
|
; CHECK-LABEL: fabs64:
|
|
; CHECK: .functype fabs64 (f64) -> (f64)
|
|
; CHECK-NEXT: # %bb.0:
|
|
; CHECK-NEXT: local.get $push1=, 0
|
|
; CHECK-NEXT: f64.abs $push0=, $pop1
|
|
; CHECK-NEXT: return $pop0
|
|
%a = call double @llvm.fabs.f64(double %x)
|
|
ret double %a
|
|
}
|
|
|
|
define double @fneg64(double %x) {
|
|
; CHECK-LABEL: fneg64:
|
|
; CHECK: .functype fneg64 (f64) -> (f64)
|
|
; CHECK-NEXT: # %bb.0:
|
|
; CHECK-NEXT: local.get $push1=, 0
|
|
; CHECK-NEXT: f64.neg $push0=, $pop1
|
|
; CHECK-NEXT: return $pop0
|
|
%a = fsub double -0., %x
|
|
ret double %a
|
|
}
|
|
|
|
define double @copysign64(double %x, double %y) {
|
|
; CHECK-LABEL: copysign64:
|
|
; CHECK: .functype copysign64 (f64, f64) -> (f64)
|
|
; CHECK-NEXT: # %bb.0:
|
|
; CHECK-NEXT: local.get $push2=, 0
|
|
; CHECK-NEXT: local.get $push1=, 1
|
|
; CHECK-NEXT: f64.copysign $push0=, $pop2, $pop1
|
|
; CHECK-NEXT: return $pop0
|
|
%a = call double @llvm.copysign.f64(double %x, double %y)
|
|
ret double %a
|
|
}
|
|
|
|
define double @sqrt64(double %x) {
|
|
; CHECK-LABEL: sqrt64:
|
|
; CHECK: .functype sqrt64 (f64) -> (f64)
|
|
; CHECK-NEXT: # %bb.0:
|
|
; CHECK-NEXT: local.get $push1=, 0
|
|
; CHECK-NEXT: f64.sqrt $push0=, $pop1
|
|
; CHECK-NEXT: return $pop0
|
|
%a = call double @llvm.sqrt.f64(double %x)
|
|
ret double %a
|
|
}
|
|
|
|
define double @ceil64(double %x) {
|
|
; CHECK-LABEL: ceil64:
|
|
; CHECK: .functype ceil64 (f64) -> (f64)
|
|
; CHECK-NEXT: # %bb.0:
|
|
; CHECK-NEXT: local.get $push1=, 0
|
|
; CHECK-NEXT: f64.ceil $push0=, $pop1
|
|
; CHECK-NEXT: return $pop0
|
|
%a = call double @llvm.ceil.f64(double %x)
|
|
ret double %a
|
|
}
|
|
|
|
define double @floor64(double %x) {
|
|
; CHECK-LABEL: floor64:
|
|
; CHECK: .functype floor64 (f64) -> (f64)
|
|
; CHECK-NEXT: # %bb.0:
|
|
; CHECK-NEXT: local.get $push1=, 0
|
|
; CHECK-NEXT: f64.floor $push0=, $pop1
|
|
; CHECK-NEXT: return $pop0
|
|
%a = call double @llvm.floor.f64(double %x)
|
|
ret double %a
|
|
}
|
|
|
|
define double @trunc64(double %x) {
|
|
; CHECK-LABEL: trunc64:
|
|
; CHECK: .functype trunc64 (f64) -> (f64)
|
|
; CHECK-NEXT: # %bb.0:
|
|
; CHECK-NEXT: local.get $push1=, 0
|
|
; CHECK-NEXT: f64.trunc $push0=, $pop1
|
|
; CHECK-NEXT: return $pop0
|
|
%a = call double @llvm.trunc.f64(double %x)
|
|
ret double %a
|
|
}
|
|
|
|
define double @nearest64(double %x) {
|
|
; CHECK-LABEL: nearest64:
|
|
; CHECK: .functype nearest64 (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.nearbyint.f64(double %x)
|
|
ret double %a
|
|
}
|
|
|
|
define double @nearest64_via_rint(double %x) {
|
|
; CHECK-LABEL: nearest64_via_rint:
|
|
; CHECK: .functype nearest64_via_rint (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.rint.f64(double %x)
|
|
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) {
|
|
; CHECK-LABEL: fmin64:
|
|
; CHECK: .functype fmin64 (f64) -> (f64)
|
|
; CHECK-NEXT: # %bb.0:
|
|
; CHECK-NEXT: f64.const $push0=, 0x0p0
|
|
; CHECK-NEXT: local.get $push5=, 0
|
|
; CHECK-NEXT: local.get $push4=, 0
|
|
; CHECK-NEXT: f64.const $push3=, 0x0p0
|
|
; CHECK-NEXT: f64.ge $push1=, $pop4, $pop3
|
|
; CHECK-NEXT: f64.select $push2=, $pop0, $pop5, $pop1
|
|
; CHECK-NEXT: return $pop2
|
|
%a = fcmp ult double %x, 0.0
|
|
%b = select i1 %a, double %x, double 0.0
|
|
ret double %b
|
|
}
|
|
|
|
; This is not "maximum" because a -0.0 input returns +0.0.
|
|
|
|
define double @fmax64(double %x) {
|
|
; CHECK-LABEL: fmax64:
|
|
; CHECK: .functype fmax64 (f64) -> (f64)
|
|
; CHECK-NEXT: # %bb.0:
|
|
; CHECK-NEXT: f64.const $push0=, 0x0p0
|
|
; CHECK-NEXT: local.get $push5=, 0
|
|
; CHECK-NEXT: local.get $push4=, 0
|
|
; CHECK-NEXT: f64.const $push3=, 0x0p0
|
|
; CHECK-NEXT: f64.le $push1=, $pop4, $pop3
|
|
; CHECK-NEXT: f64.select $push2=, $pop0, $pop5, $pop1
|
|
; CHECK-NEXT: return $pop2
|
|
%a = fcmp ugt double %x, 0.0
|
|
%b = select i1 %a, double %x, double 0.0
|
|
ret double %b
|
|
}
|
|
|
|
declare double @llvm.minimum.f64(double, double)
|
|
define double @fmin64_intrinsic(double %x, double %y) {
|
|
; CHECK-LABEL: fmin64_intrinsic:
|
|
; CHECK: .functype fmin64_intrinsic (f64, f64) -> (f64)
|
|
; CHECK-NEXT: # %bb.0:
|
|
; CHECK-NEXT: local.get $push2=, 0
|
|
; CHECK-NEXT: local.get $push1=, 1
|
|
; CHECK-NEXT: f64.min $push0=, $pop2, $pop1
|
|
; CHECK-NEXT: return $pop0
|
|
%a = call double @llvm.minimum.f64(double %x, double %y)
|
|
ret double %a
|
|
}
|
|
|
|
declare double @llvm.minnum.f64(double, double)
|
|
define double @fminnum64_intrinsic(double %x, double %y) {
|
|
; CHECK-LABEL: fminnum64_intrinsic:
|
|
; CHECK: .functype fminnum64_intrinsic (f64, f64) -> (f64)
|
|
; CHECK-NEXT: # %bb.0:
|
|
; CHECK-NEXT: local.get $push2=, 0
|
|
; CHECK-NEXT: local.get $push1=, 1
|
|
; CHECK-NEXT: f64.min $push0=, $pop2, $pop1
|
|
; CHECK-NEXT: return $pop0
|
|
%a = call nnan double @llvm.minnum.f64(double %x, double %y)
|
|
ret double %a
|
|
}
|
|
|
|
define double @fminnum64_nsz_intrinsic(double %x, double %y) {
|
|
; CHECK-LABEL: fminnum64_nsz_intrinsic:
|
|
; CHECK: .functype fminnum64_nsz_intrinsic (f64, f64) -> (f64)
|
|
; CHECK-NEXT: # %bb.0:
|
|
; CHECK-NEXT: local.get $push2=, 0
|
|
; CHECK-NEXT: local.get $push1=, 1
|
|
; CHECK-NEXT: f64.min $push0=, $pop2, $pop1
|
|
; CHECK-NEXT: return $pop0
|
|
%a = call nnan nsz double @llvm.minnum.f64(double %x, double %y)
|
|
ret double %a
|
|
}
|
|
|
|
define double @fminnum64_zero_intrinsic(double %x) {
|
|
; CHECK-LABEL: fminnum64_zero_intrinsic:
|
|
; CHECK: .functype fminnum64_zero_intrinsic (f64) -> (f64)
|
|
; CHECK-NEXT: # %bb.0:
|
|
; CHECK-NEXT: local.get $push2=, 0
|
|
; CHECK-NEXT: f64.const $push0=, -0x0p0
|
|
; CHECK-NEXT: f64.min $push1=, $pop2, $pop0
|
|
; CHECK-NEXT: return $pop1
|
|
%a = call nnan double @llvm.minnum.f64(double %x, double -0.0)
|
|
ret double %a
|
|
}
|
|
|
|
define double @fminnum64_non_zero_intrinsic(double %x) {
|
|
; CHECK-LABEL: fminnum64_non_zero_intrinsic:
|
|
; CHECK: .functype fminnum64_non_zero_intrinsic (f64) -> (f64)
|
|
; CHECK-NEXT: # %bb.0:
|
|
; CHECK-NEXT: local.get $push2=, 0
|
|
; CHECK-NEXT: f64.const $push0=, -0x1p0
|
|
; CHECK-NEXT: f64.min $push1=, $pop2, $pop0
|
|
; CHECK-NEXT: return $pop1
|
|
%a = call nnan double @llvm.minnum.f64(double %x, double -1.0)
|
|
ret double %a
|
|
}
|
|
|
|
declare double @llvm.maximum.f64(double, double)
|
|
define double @fmax64_intrinsic(double %x, double %y) {
|
|
; CHECK-LABEL: fmax64_intrinsic:
|
|
; CHECK: .functype fmax64_intrinsic (f64, f64) -> (f64)
|
|
; CHECK-NEXT: # %bb.0:
|
|
; CHECK-NEXT: local.get $push2=, 0
|
|
; CHECK-NEXT: local.get $push1=, 1
|
|
; CHECK-NEXT: f64.max $push0=, $pop2, $pop1
|
|
; CHECK-NEXT: return $pop0
|
|
%a = call double @llvm.maximum.f64(double %x, double %y)
|
|
ret double %a
|
|
}
|
|
|
|
declare double @llvm.maxnum.f64(double, double)
|
|
define double@fmaxnum64_intrinsic(double %x, double %y) {
|
|
; CHECK-LABEL: fmaxnum64_intrinsic:
|
|
; CHECK: .functype fmaxnum64_intrinsic (f64, f64) -> (f64)
|
|
; CHECK-NEXT: # %bb.0:
|
|
; CHECK-NEXT: local.get $push2=, 0
|
|
; CHECK-NEXT: local.get $push1=, 1
|
|
; CHECK-NEXT: f64.max $push0=, $pop2, $pop1
|
|
; CHECK-NEXT: return $pop0
|
|
%a = call nnan double @llvm.maxnum.f64(double %x, double %y)
|
|
ret double %a
|
|
}
|
|
|
|
define double@fmaxnum64_nsz_intrinsic(double %x, double %y) {
|
|
; CHECK-LABEL: fmaxnum64_nsz_intrinsic:
|
|
; CHECK: .functype fmaxnum64_nsz_intrinsic (f64, f64) -> (f64)
|
|
; CHECK-NEXT: # %bb.0:
|
|
; CHECK-NEXT: local.get $push2=, 0
|
|
; CHECK-NEXT: local.get $push1=, 1
|
|
; CHECK-NEXT: f64.max $push0=, $pop2, $pop1
|
|
; CHECK-NEXT: return $pop0
|
|
%a = call nnan nsz double @llvm.maxnum.f64(double %x, double %y)
|
|
ret double %a
|
|
}
|
|
|
|
define double @fmaxnum64_zero_intrinsic(double %x) {
|
|
; CHECK-LABEL: fmaxnum64_zero_intrinsic:
|
|
; CHECK: .functype fmaxnum64_zero_intrinsic (f64) -> (f64)
|
|
; CHECK-NEXT: # %bb.0:
|
|
; CHECK-NEXT: local.get $push2=, 0
|
|
; CHECK-NEXT: f64.const $push0=, 0x0p0
|
|
; CHECK-NEXT: f64.max $push1=, $pop2, $pop0
|
|
; CHECK-NEXT: return $pop1
|
|
%a = call nnan double @llvm.maxnum.f64(double %x, double 0.0)
|
|
ret double %a
|
|
}
|
|
|
|
define double @fmaxnum64_non_zero_intrinsic(double %x) {
|
|
; CHECK-LABEL: fmaxnum64_non_zero_intrinsic:
|
|
; CHECK: .functype fmaxnum64_non_zero_intrinsic (f64) -> (f64)
|
|
; CHECK-NEXT: # %bb.0:
|
|
; CHECK-NEXT: local.get $push2=, 0
|
|
; CHECK-NEXT: f64.const $push0=, 0x1p0
|
|
; CHECK-NEXT: f64.max $push1=, $pop2, $pop0
|
|
; CHECK-NEXT: return $pop1
|
|
%a = call nnan double @llvm.maxnum.f64(double %x, double 1.0)
|
|
ret double %a
|
|
}
|
|
|
|
define double @fma64(double %a, double %b, double %c) {
|
|
; CHECK-LABEL: fma64:
|
|
; CHECK: .functype fma64 (f64, f64, f64) -> (f64)
|
|
; CHECK-NEXT: # %bb.0:
|
|
; CHECK-NEXT: local.get $push3=, 0
|
|
; CHECK-NEXT: local.get $push2=, 1
|
|
; CHECK-NEXT: local.get $push1=, 2
|
|
; CHECK-NEXT: call $push0=, fma, $pop3, $pop2, $pop1
|
|
; CHECK-NEXT: return $pop0
|
|
%d = call double @llvm.fma.f64(double %a, double %b, double %c)
|
|
ret double %d
|
|
}
|