[CFE, SystemZ] New target hook testFPKind() for checks of FP values.

The recent commit 00a6254 "Stop traping on sNaN in builtin_isnan" changed the
lowering in constrained FP mode of builtin_isnan from an FP comparison to
integer operations to avoid trapping.

SystemZ has a special instruction "Test Data Class" which is the preferred
way to do this check. This patch adds a new target hook "testFPKind()" that
lets SystemZ emit the s390_tdc intrinsic instead.

testFPKind() takes the BuiltinID as an argument and is expected to soon
handle more opcodes than just 'builtin_isnan'.

Review: Thomas Preud'homme, Ulrich Weigand
Differential Revision: https://reviews.llvm.org/D96568
This commit is contained in:
Jonas Paulsson 2021-02-12 02:57:26 +01:00
parent bb260b1ca7
commit e57bd1ff4f
4 changed files with 89 additions and 1 deletions

View File

@ -2997,6 +2997,9 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
return RValue::get(Builder.CreateZExt(V, ConvertType(E->getType())));
}
if (Value *Result = getTargetHooks().testFPKind(V, BuiltinID, Builder, CGM))
return RValue::get(Result);
// NaN has all exp bits set and a non zero significand. Therefore:
// isnan(V) == ((exp mask - (abs(V) & exp mask)) < 0)
unsigned bitsize = Ty->getScalarSizeInBits();

View File

@ -21,6 +21,7 @@
#include "clang/AST/RecordLayout.h"
#include "clang/Basic/CodeGenOptions.h"
#include "clang/Basic/DiagnosticFrontend.h"
#include "clang/Basic/Builtins.h"
#include "clang/CodeGen/CGFunctionInfo.h"
#include "clang/CodeGen/SwiftCallingConv.h"
#include "llvm/ADT/SmallBitVector.h"
@ -30,6 +31,7 @@
#include "llvm/ADT/Twine.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/IntrinsicsNVPTX.h"
#include "llvm/IR/IntrinsicsS390.h"
#include "llvm/IR/Type.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm> // std::sort
@ -7200,8 +7202,37 @@ public:
SystemZTargetCodeGenInfo(CodeGenTypes &CGT, bool HasVector, bool SoftFloatABI)
: TargetCodeGenInfo(
std::make_unique<SystemZABIInfo>(CGT, HasVector, SoftFloatABI)) {}
};
llvm::Value *testFPKind(llvm::Value *V, unsigned BuiltinID,
CGBuilderTy &Builder,
CodeGenModule &CGM) const override {
assert(V->getType()->isFloatingPointTy() && "V should have an FP type.");
// Only use TDC in constrained FP mode.
if (!Builder.getIsFPConstrained())
return nullptr;
llvm::Type *Ty = V->getType();
if (Ty->isFloatTy() || Ty->isDoubleTy() || Ty->isFP128Ty()) {
llvm::Module &M = CGM.getModule();
auto &Ctx = M.getContext();
llvm::Function *TDCFunc =
llvm::Intrinsic::getDeclaration(&M, llvm::Intrinsic::s390_tdc, Ty);
unsigned TDCBits = 0;
switch (BuiltinID) {
case Builtin::BI__builtin_isnan:
TDCBits = 0xf;
break;
default:
break;
}
if (TDCBits)
return Builder.CreateCall(
TDCFunc,
{V, llvm::ConstantInt::get(llvm::Type::getInt64Ty(Ctx), TDCBits)});
}
return nullptr;
}
};
}
bool SystemZABIInfo::isPromotableIntegerTypeForABI(QualType Ty) const {

View File

@ -14,6 +14,7 @@
#ifndef LLVM_CLANG_LIB_CODEGEN_TARGETINFO_H
#define LLVM_CLANG_LIB_CODEGEN_TARGETINFO_H
#include "CGBuilder.h"
#include "CodeGenModule.h"
#include "CGValue.h"
#include "clang/AST/Type.h"
@ -126,6 +127,16 @@ public:
return Address;
}
/// Performs a target specific test of a floating point value for things
/// like IsNaN, Infinity, ... Nullptr is returned if no implementation
/// exists.
virtual llvm::Value *
testFPKind(llvm::Value *V, unsigned BuiltinID, CGBuilderTy &Builder,
CodeGenModule &CGM) const {
assert(V->getType()->isFloatingPointTy() && "V should have an FP type.");
return nullptr;
}
/// Corrects the low-level LLVM type for a given constraint and "usual"
/// type.
///

View File

@ -0,0 +1,43 @@
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py
// REQUIRES: systemz-registered-target
// RUN: %clang_cc1 %s -emit-llvm -ffp-exception-behavior=maytrap -o - -triple s390x-linux-gnu | FileCheck %s
#pragma float_control(except, on)
// CHECK-LABEL: @test_isnan_float(
// CHECK-NEXT: entry:
// CHECK-NEXT: [[F_ADDR:%.*]] = alloca float, align 4
// CHECK-NEXT: store float [[F:%.*]], float* [[F_ADDR]], align 4
// CHECK-NEXT: [[TMP0:%.*]] = load float, float* [[F_ADDR]], align 4
// CHECK-NEXT: [[TMP1:%.*]] = call i32 @llvm.s390.tdc.f32(float [[TMP0]], i64 15) [[ATTR2:#.*]]
// CHECK-NEXT: ret i32 [[TMP1]]
//
int test_isnan_float(float f) {
return __builtin_isnan(f);
}
// CHECK-LABEL: @test_isnan_double(
// CHECK-NEXT: entry:
// CHECK-NEXT: [[D_ADDR:%.*]] = alloca double, align 8
// CHECK-NEXT: store double [[D:%.*]], double* [[D_ADDR]], align 8
// CHECK-NEXT: [[TMP0:%.*]] = load double, double* [[D_ADDR]], align 8
// CHECK-NEXT: [[TMP1:%.*]] = call i32 @llvm.s390.tdc.f64(double [[TMP0]], i64 15) [[ATTR2]]
// CHECK-NEXT: ret i32 [[TMP1]]
//
int test_isnan_double(double d) {
return __builtin_isnan(d);
}
// CHECK-LABEL: @test_isnan_long_double(
// CHECK-NEXT: entry:
// CHECK-NEXT: [[LD_ADDR:%.*]] = alloca fp128, align 8
// CHECK-NEXT: [[LD:%.*]] = load fp128, fp128* [[TMP0:%.*]], align 8
// CHECK-NEXT: store fp128 [[LD]], fp128* [[LD_ADDR]], align 8
// CHECK-NEXT: [[TMP1:%.*]] = load fp128, fp128* [[LD_ADDR]], align 8
// CHECK-NEXT: [[TMP2:%.*]] = call i32 @llvm.s390.tdc.f128(fp128 [[TMP1]], i64 15) [[ATTR2]]
// CHECK-NEXT: ret i32 [[TMP2]]
//
int test_isnan_long_double(long double ld) {
return __builtin_isnan(ld);
}