As pointed out in #187518 , currently, `__builtin_isnormal` returns `true` for subnormal half precision floating point numbers on `s390x. This is because there is a custom lowering defined which lowers an `f16` `IS_FPCLASS` ISD node by extending the `f16` value to `f32`, and then using SystemZ's "test data class" instruction to determine whether the number is subnormal. However, a number that is subnormal in 16 bits of precision will no longer be subnormal in 32 bits of precision, and so the test always returns true, i.e. all subnormal numbers are classified as normal. This PR addresses this by removing the custom lowering and instead relying on the generic expansion of `IS_FPCLASS`, which does not have this error. Fixes #187518 .
238 lines
6.3 KiB
LLVM
238 lines
6.3 KiB
LLVM
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
|
|
; Test intrinsic 'is_fpclass'.
|
|
;
|
|
; RUN: llc < %s -mtriple=s390x-linux-gnu | FileCheck %s
|
|
|
|
declare i1 @llvm.is.fpclass.f16(half, i32)
|
|
declare i1 @llvm.is.fpclass.f32(float, i32)
|
|
declare i1 @llvm.is.fpclass.f64(double, i32)
|
|
declare i1 @llvm.is.fpclass.f128(fp128, i32)
|
|
|
|
|
|
define i1 @isnan_h(half %x) {
|
|
; CHECK-LABEL: isnan_h:
|
|
; CHECK: # %bb.0:
|
|
; CHECK-NEXT: # kill: def $f0h killed $f0h def $f0d
|
|
; CHECK-NEXT: lgdr %r0, %f0
|
|
; CHECK-NEXT: risbg %r0, %r0, 49, 191, 16
|
|
; CHECK-NEXT: chi %r0, 31744
|
|
; CHECK-NEXT: ipm %r0
|
|
; CHECK-NEXT: risbg %r2, %r0, 63, 191, 35
|
|
; CHECK-NEXT: # kill: def $r2l killed $r2l killed $r2d
|
|
; CHECK-NEXT: br %r14
|
|
%1 = call i1 @llvm.is.fpclass.f16(half %x, i32 3) ; nan
|
|
ret i1 %1
|
|
}
|
|
|
|
define i1 @isnan_f(float %x) {
|
|
; CHECK-LABEL: isnan_f:
|
|
; CHECK: # %bb.0:
|
|
; CHECK-NEXT: tceb %f0, 15
|
|
; CHECK-NEXT: ipm %r2
|
|
; CHECK-NEXT: srl %r2, 28
|
|
; CHECK-NEXT: br %r14
|
|
%1 = call i1 @llvm.is.fpclass.f32(float %x, i32 3) ; nan
|
|
ret i1 %1
|
|
}
|
|
|
|
define i1 @isnan_d(double %x) {
|
|
; CHECK-LABEL: isnan_d:
|
|
; CHECK: # %bb.0:
|
|
; CHECK-NEXT: tcdb %f0, 15
|
|
; CHECK-NEXT: ipm %r2
|
|
; CHECK-NEXT: srl %r2, 28
|
|
; CHECK-NEXT: br %r14
|
|
%1 = call i1 @llvm.is.fpclass.f64(double %x, i32 3) ; nan
|
|
ret i1 %1
|
|
}
|
|
|
|
define i1 @isnan_x(fp128 %x) {
|
|
; CHECK-LABEL: isnan_x:
|
|
; CHECK: # %bb.0:
|
|
; CHECK-NEXT: ld %f0, 0(%r2)
|
|
; CHECK-NEXT: ld %f2, 8(%r2)
|
|
; CHECK-NEXT: tcxb %f0, 15
|
|
; CHECK-NEXT: ipm %r2
|
|
; CHECK-NEXT: srl %r2, 28
|
|
; CHECK-NEXT: br %r14
|
|
%1 = call i1 @llvm.is.fpclass.f128(fp128 %x, i32 3) ; nan
|
|
ret i1 %1
|
|
}
|
|
|
|
define i1 @isqnan_f(float %x) {
|
|
; CHECK-LABEL: isqnan_f:
|
|
; CHECK: # %bb.0:
|
|
; CHECK-NEXT: tceb %f0, 12
|
|
; CHECK-NEXT: ipm %r2
|
|
; CHECK-NEXT: srl %r2, 28
|
|
; CHECK-NEXT: br %r14
|
|
%1 = call i1 @llvm.is.fpclass.f32(float %x, i32 2) ; qnan
|
|
ret i1 %1
|
|
}
|
|
|
|
define i1 @issnan_f(float %x) {
|
|
; CHECK-LABEL: issnan_f:
|
|
; CHECK: # %bb.0:
|
|
; CHECK-NEXT: tceb %f0, 3
|
|
; CHECK-NEXT: ipm %r2
|
|
; CHECK-NEXT: srl %r2, 28
|
|
; CHECK-NEXT: br %r14
|
|
%1 = call i1 @llvm.is.fpclass.f32(float %x, i32 1) ; snan
|
|
ret i1 %1
|
|
}
|
|
|
|
define i1 @isinf_f(float %x) {
|
|
; CHECK-LABEL: isinf_f:
|
|
; CHECK: # %bb.0:
|
|
; CHECK-NEXT: tceb %f0, 48
|
|
; CHECK-NEXT: ipm %r2
|
|
; CHECK-NEXT: srl %r2, 28
|
|
; CHECK-NEXT: br %r14
|
|
%1 = call i1 @llvm.is.fpclass.f32(float %x, i32 516) ; 0x204 = "inf"
|
|
ret i1 %1
|
|
}
|
|
|
|
define i1 @isposinf_f(float %x) {
|
|
; CHECK-LABEL: isposinf_f:
|
|
; CHECK: # %bb.0:
|
|
; CHECK-NEXT: tceb %f0, 32
|
|
; CHECK-NEXT: ipm %r2
|
|
; CHECK-NEXT: srl %r2, 28
|
|
; CHECK-NEXT: br %r14
|
|
%1 = call i1 @llvm.is.fpclass.f32(float %x, i32 512) ; 0x200 = "+inf"
|
|
ret i1 %1
|
|
}
|
|
|
|
define i1 @isneginf_f(float %x) {
|
|
; CHECK-LABEL: isneginf_f:
|
|
; CHECK: # %bb.0:
|
|
; CHECK-NEXT: tceb %f0, 16
|
|
; CHECK-NEXT: ipm %r2
|
|
; CHECK-NEXT: srl %r2, 28
|
|
; CHECK-NEXT: br %r14
|
|
%1 = call i1 @llvm.is.fpclass.f32(float %x, i32 4) ; "-inf"
|
|
ret i1 %1
|
|
}
|
|
|
|
define i1 @isfinite_f(float %x) {
|
|
; CHECK-LABEL: isfinite_f:
|
|
; CHECK: # %bb.0:
|
|
; CHECK-NEXT: tceb %f0, 4032
|
|
; CHECK-NEXT: ipm %r2
|
|
; CHECK-NEXT: srl %r2, 28
|
|
; CHECK-NEXT: br %r14
|
|
%1 = call i1 @llvm.is.fpclass.f32(float %x, i32 504) ; 0x1f8 = "finite"
|
|
ret i1 %1
|
|
}
|
|
|
|
define i1 @isposfinite_f(float %x) {
|
|
; CHECK-LABEL: isposfinite_f:
|
|
; CHECK: # %bb.0:
|
|
; CHECK-NEXT: tceb %f0, 2688
|
|
; CHECK-NEXT: ipm %r2
|
|
; CHECK-NEXT: srl %r2, 28
|
|
; CHECK-NEXT: br %r14
|
|
%1 = call i1 @llvm.is.fpclass.f32(float %x, i32 448) ; 0x1c0 = "+finite"
|
|
ret i1 %1
|
|
}
|
|
|
|
define i1 @isnegfinite_f(float %x) {
|
|
; CHECK-LABEL: isnegfinite_f:
|
|
; CHECK: # %bb.0:
|
|
; CHECK-NEXT: tceb %f0, 1344
|
|
; CHECK-NEXT: ipm %r2
|
|
; CHECK-NEXT: srl %r2, 28
|
|
; CHECK-NEXT: br %r14
|
|
%1 = call i1 @llvm.is.fpclass.f32(float %x, i32 56) ; 0x38 = "-finite"
|
|
ret i1 %1
|
|
}
|
|
|
|
define i1 @isnotfinite_f(float %x) {
|
|
; CHECK-LABEL: isnotfinite_f:
|
|
; CHECK: # %bb.0:
|
|
; CHECK-NEXT: tceb %f0, 63
|
|
; CHECK-NEXT: ipm %r2
|
|
; CHECK-NEXT: srl %r2, 28
|
|
; CHECK-NEXT: br %r14
|
|
%1 = call i1 @llvm.is.fpclass.f32(float %x, i32 519) ; ox207 = "inf|nan"
|
|
ret i1 %1
|
|
}
|
|
|
|
|
|
define i1 @isnormal_h(half %x) {
|
|
; CHECK-LABEL: isnormal_h:
|
|
; CHECK: # %bb.0:
|
|
; CHECK-NEXT: # kill: def $f0h killed $f0h def $f0d
|
|
; CHECK-NEXT: lgdr %r0, %f0
|
|
; CHECK-NEXT: risbg %r0, %r0, 49, 191, 16
|
|
; CHECK-NEXT: ahi %r0, -1024
|
|
; CHECK-NEXT: llhr %r0, %r0
|
|
; CHECK-NEXT: chi %r0, 30720
|
|
; CHECK-NEXT: ipm %r0
|
|
; CHECK-NEXT: risbg %r2, %r0, 63, 191, 36
|
|
; CHECK-NEXT: # kill: def $r2l killed $r2l killed $r2d
|
|
; CHECK-NEXT: br %r14
|
|
%1 = call i1 @llvm.is.fpclass.f16(half %x, i32 264) ; 0x108 = normal
|
|
ret i1 %1
|
|
}
|
|
|
|
define i1 @isnormal_f(float %x) {
|
|
; CHECK-LABEL: isnormal_f:
|
|
; CHECK: # %bb.0:
|
|
; CHECK-NEXT: tceb %f0, 768
|
|
; CHECK-NEXT: ipm %r2
|
|
; CHECK-NEXT: srl %r2, 28
|
|
; CHECK-NEXT: br %r14
|
|
%1 = call i1 @llvm.is.fpclass.f32(float %x, i32 264) ; 0x108 = normal
|
|
ret i1 %1
|
|
}
|
|
|
|
define i1 @isnormal_d(double %x) {
|
|
; CHECK-LABEL: isnormal_d:
|
|
; CHECK: # %bb.0:
|
|
; CHECK-NEXT: tcdb %f0, 768
|
|
; CHECK-NEXT: ipm %r2
|
|
; CHECK-NEXT: srl %r2, 28
|
|
; CHECK-NEXT: br %r14
|
|
%1 = call i1 @llvm.is.fpclass.f64(double %x, i32 264) ; 0x108 = normal
|
|
ret i1 %1
|
|
}
|
|
|
|
define i1 @issubnormal_h(half %x) {
|
|
; CHECK-LABEL: issubnormal_h:
|
|
; CHECK: # %bb.0:
|
|
; CHECK-NEXT: # kill: def $f0h killed $f0h def $f0d
|
|
; CHECK-NEXT: lgdr %r0, %f0
|
|
; CHECK-NEXT: risbg %r0, %r0, 49, 191, 16
|
|
; CHECK-NEXT: ahi %r0, -1
|
|
; CHECK-NEXT: clfi %r0, 1023
|
|
; CHECK-NEXT: ipm %r0
|
|
; CHECK-NEXT: risbg %r2, %r0, 63, 191, 36
|
|
; CHECK-NEXT: # kill: def $r2l killed $r2l killed $r2d
|
|
; CHECK-NEXT: br %r14
|
|
%1 = call i1 @llvm.is.fpclass.f16(half %x, i32 144) ; 0x90 = subnormal
|
|
ret i1 %1
|
|
}
|
|
|
|
define i1 @issubnormal_f(float %x) {
|
|
; CHECK-LABEL: issubnormal_f:
|
|
; CHECK: # %bb.0:
|
|
; CHECK-NEXT: tceb %f0, 192
|
|
; CHECK-NEXT: ipm %r2
|
|
; CHECK-NEXT: srl %r2, 28
|
|
; CHECK-NEXT: br %r14
|
|
%1 = call i1 @llvm.is.fpclass.f32(float %x, i32 144) ; 0x90 = subnormal
|
|
ret i1 %1
|
|
}
|
|
|
|
define i1 @issubnormal_d(double %x) {
|
|
; CHECK-LABEL: issubnormal_d:
|
|
; CHECK: # %bb.0:
|
|
; CHECK-NEXT: tcdb %f0, 192
|
|
; CHECK-NEXT: ipm %r2
|
|
; CHECK-NEXT: srl %r2, 28
|
|
; CHECK-NEXT: br %r14
|
|
%1 = call i1 @llvm.is.fpclass.f64(double %x, i32 144) ; 0x90 = subnormal
|
|
ret i1 %1
|
|
}
|