diff --git a/llvm/include/llvm/IR/FPEnv.h b/llvm/include/llvm/IR/FPEnv.h --- a/llvm/include/llvm/IR/FPEnv.h +++ b/llvm/include/llvm/IR/FPEnv.h @@ -22,6 +22,12 @@ namespace llvm { class StringRef; +namespace Intrinsic { +typedef unsigned ID; +} + +class Instruction; + namespace fp { /// Exception behavior used for floating point operations. @@ -60,6 +66,12 @@ return EB == fp::ebIgnore && RM == RoundingMode::NearestTiesToEven; } +/// Returns constrained intrinsic id to represent the given instruction in +/// strictfp function. If the instruction is already a constrained intrinsic or +/// does not have a constrained intrinsic counterpart, the function returns +/// zero. +Intrinsic::ID getConstrainedIntrinsicID(const Instruction &Instr); + /// Returns true if the rounding mode RM may be QRM at compile time or /// at run time. inline bool canRoundingModeBe(RoundingMode RM, RoundingMode QRM) { diff --git a/llvm/lib/IR/FPEnv.cpp b/llvm/lib/IR/FPEnv.cpp --- a/llvm/lib/IR/FPEnv.cpp +++ b/llvm/lib/IR/FPEnv.cpp @@ -14,6 +14,9 @@ #include "llvm/IR/FPEnv.h" #include "llvm/ADT/StringSwitch.h" +#include "llvm/IR/InstrTypes.h" +#include "llvm/IR/IntrinsicInst.h" +#include "llvm/IR/Intrinsics.h" namespace llvm { @@ -82,4 +85,46 @@ } return ExceptStr; } + +Intrinsic::ID getConstrainedIntrinsicID(const Instruction &Instr) { + Intrinsic::ID IID = Intrinsic::not_intrinsic; + switch (Instr.getOpcode()) { + case Instruction::FCmp: + // Unlike other instructions FCmp can be mapped to one of two intrinsic + // functions. We choose the non-signaling variant. + IID = Intrinsic::experimental_constrained_fcmp; + break; + + // Instructions +#define INSTRUCTION(NAME, NARG, ROUND_MODE, INTRINSIC) \ + case Instruction::NAME: \ + IID = Intrinsic::INTRINSIC; \ + break; +#define FUNCTION(NAME, NARG, ROUND_MODE, INTRINSIC) +#define CMP_INSTRUCTION(NAME, NARG, ROUND_MODE, INTRINSIC, DAGN) +#include "llvm/IR/ConstrainedOps.def" + + // Intrinsic calls. + case Instruction::Call: + if (auto *IntrinCall = dyn_cast(&Instr)) { + switch (IntrinCall->getIntrinsicID()) { +#define FUNCTION(NAME, NARG, ROUND_MODE, INTRINSIC) \ + case Intrinsic::NAME: \ + IID = Intrinsic::INTRINSIC; \ + break; +#define INSTRUCTION(NAME, NARG, ROUND_MODE, INTRINSIC) +#define CMP_INSTRUCTION(NAME, NARG, ROUND_MODE, INTRINSIC, DAGN) +#include "llvm/IR/ConstrainedOps.def" + default: + break; + } + } + break; + default: + break; + } + + return IID; +} + } // namespace llvm diff --git a/llvm/unittests/IR/InstructionsTest.cpp b/llvm/unittests/IR/InstructionsTest.cpp --- a/llvm/unittests/IR/InstructionsTest.cpp +++ b/llvm/unittests/IR/InstructionsTest.cpp @@ -17,6 +17,7 @@ #include "llvm/IR/DataLayout.h" #include "llvm/IR/DebugInfoMetadata.h" #include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/FPEnv.h" #include "llvm/IR/Function.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/LLVMContext.h" @@ -507,6 +508,59 @@ I->deleteValue(); } +TEST(InstructionTest, ConstrainedTrans) { + LLVMContext Context; + std::unique_ptr M(new Module("MyModule", Context)); + FunctionType *FTy = + FunctionType::get(Type::getVoidTy(Context), + {Type::getFloatTy(Context), Type::getFloatTy(Context), + Type::getInt32Ty(Context)}, + false); + auto *F = Function::Create(FTy, Function::ExternalLinkage, "", M.get()); + auto *BB = BasicBlock::Create(Context, "bb", F); + IRBuilder<> Builder(Context); + Builder.SetInsertPoint(BB); + auto *Arg0 = F->arg_begin(); + auto *Arg1 = F->arg_begin() + 1; + + { + auto *I = cast(Builder.CreateFAdd(Arg0, Arg1)); + EXPECT_EQ(Intrinsic::experimental_constrained_fadd, + getConstrainedIntrinsicID(*I)); + } + + { + auto *I = cast( + Builder.CreateFPToSI(Arg0, Type::getInt32Ty(Context))); + EXPECT_EQ(Intrinsic::experimental_constrained_fptosi, + getConstrainedIntrinsicID(*I)); + } + + { + auto *I = cast(Builder.CreateIntrinsic( + Intrinsic::ceil, {Type::getFloatTy(Context)}, {Arg0})); + EXPECT_EQ(Intrinsic::experimental_constrained_ceil, + getConstrainedIntrinsicID(*I)); + } + + { + auto *I = cast(Builder.CreateFCmpOEQ(Arg0, Arg1)); + EXPECT_EQ(Intrinsic::experimental_constrained_fcmp, + getConstrainedIntrinsicID(*I)); + } + + { + auto *Arg2 = F->arg_begin() + 2; + auto *I = cast(Builder.CreateAdd(Arg2, Arg2)); + EXPECT_EQ(Intrinsic::not_intrinsic, getConstrainedIntrinsicID(*I)); + } + + { + auto *I = cast(Builder.CreateConstrainedFPBinOp( + Intrinsic::experimental_constrained_fadd, Arg0, Arg0)); + EXPECT_EQ(Intrinsic::not_intrinsic, getConstrainedIntrinsicID(*I)); + } +} TEST(InstructionsTest, isEliminableCastPair) { LLVMContext C;