xref: /freebsd/contrib/llvm-project/llvm/lib/Target/NVPTX/NVVMReflect.cpp (revision bdd1243df58e60e85101c09001d9812a789b6bc4)
10b57cec5SDimitry Andric //===- NVVMReflect.cpp - NVVM Emulate conditional compilation -------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // This pass replaces occurrences of __nvvm_reflect("foo") and llvm.nvvm.reflect
100b57cec5SDimitry Andric // with an integer.
110b57cec5SDimitry Andric //
120b57cec5SDimitry Andric // We choose the value we use by looking at metadata in the module itself.  Note
130b57cec5SDimitry Andric // that we intentionally only have one way to choose these values, because other
140b57cec5SDimitry Andric // parts of LLVM (particularly, InstCombineCall) rely on being able to predict
150b57cec5SDimitry Andric // the values chosen by this pass.
160b57cec5SDimitry Andric //
170b57cec5SDimitry Andric // If we see an unknown string, we replace its call with 0.
180b57cec5SDimitry Andric //
190b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
200b57cec5SDimitry Andric 
210b57cec5SDimitry Andric #include "NVPTX.h"
220b57cec5SDimitry Andric #include "llvm/ADT/SmallVector.h"
230b57cec5SDimitry Andric #include "llvm/ADT/StringMap.h"
240b57cec5SDimitry Andric #include "llvm/IR/Constants.h"
250b57cec5SDimitry Andric #include "llvm/IR/DerivedTypes.h"
260b57cec5SDimitry Andric #include "llvm/IR/Function.h"
270b57cec5SDimitry Andric #include "llvm/IR/InstIterator.h"
280b57cec5SDimitry Andric #include "llvm/IR/Instructions.h"
290b57cec5SDimitry Andric #include "llvm/IR/Intrinsics.h"
30480093f4SDimitry Andric #include "llvm/IR/IntrinsicsNVPTX.h"
310b57cec5SDimitry Andric #include "llvm/IR/Module.h"
32e8d8bef9SDimitry Andric #include "llvm/IR/PassManager.h"
330b57cec5SDimitry Andric #include "llvm/IR/Type.h"
340b57cec5SDimitry Andric #include "llvm/Pass.h"
350b57cec5SDimitry Andric #include "llvm/Support/CommandLine.h"
360b57cec5SDimitry Andric #include "llvm/Support/Debug.h"
370b57cec5SDimitry Andric #include "llvm/Support/raw_os_ostream.h"
380b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h"
390b57cec5SDimitry Andric #include "llvm/Transforms/Scalar.h"
400b57cec5SDimitry Andric #include <sstream>
410b57cec5SDimitry Andric #include <string>
420b57cec5SDimitry Andric #define NVVM_REFLECT_FUNCTION "__nvvm_reflect"
43*bdd1243dSDimitry Andric #define NVVM_REFLECT_OCL_FUNCTION "__nvvm_reflect_ocl"
440b57cec5SDimitry Andric 
450b57cec5SDimitry Andric using namespace llvm;
460b57cec5SDimitry Andric 
470b57cec5SDimitry Andric #define DEBUG_TYPE "nvptx-reflect"
480b57cec5SDimitry Andric 
490b57cec5SDimitry Andric namespace llvm { void initializeNVVMReflectPass(PassRegistry &); }
500b57cec5SDimitry Andric 
510b57cec5SDimitry Andric namespace {
520b57cec5SDimitry Andric class NVVMReflect : public FunctionPass {
530b57cec5SDimitry Andric public:
540b57cec5SDimitry Andric   static char ID;
550b57cec5SDimitry Andric   unsigned int SmVersion;
560b57cec5SDimitry Andric   NVVMReflect() : NVVMReflect(0) {}
570b57cec5SDimitry Andric   explicit NVVMReflect(unsigned int Sm) : FunctionPass(ID), SmVersion(Sm) {
580b57cec5SDimitry Andric     initializeNVVMReflectPass(*PassRegistry::getPassRegistry());
590b57cec5SDimitry Andric   }
600b57cec5SDimitry Andric 
610b57cec5SDimitry Andric   bool runOnFunction(Function &) override;
620b57cec5SDimitry Andric };
630b57cec5SDimitry Andric }
640b57cec5SDimitry Andric 
650b57cec5SDimitry Andric FunctionPass *llvm::createNVVMReflectPass(unsigned int SmVersion) {
660b57cec5SDimitry Andric   return new NVVMReflect(SmVersion);
670b57cec5SDimitry Andric }
680b57cec5SDimitry Andric 
690b57cec5SDimitry Andric static cl::opt<bool>
700b57cec5SDimitry Andric NVVMReflectEnabled("nvvm-reflect-enable", cl::init(true), cl::Hidden,
710b57cec5SDimitry Andric                    cl::desc("NVVM reflection, enabled by default"));
720b57cec5SDimitry Andric 
730b57cec5SDimitry Andric char NVVMReflect::ID = 0;
740b57cec5SDimitry Andric INITIALIZE_PASS(NVVMReflect, "nvvm-reflect",
750b57cec5SDimitry Andric                 "Replace occurrences of __nvvm_reflect() calls with 0/1", false,
760b57cec5SDimitry Andric                 false)
770b57cec5SDimitry Andric 
78e8d8bef9SDimitry Andric static bool runNVVMReflect(Function &F, unsigned SmVersion) {
790b57cec5SDimitry Andric   if (!NVVMReflectEnabled)
800b57cec5SDimitry Andric     return false;
810b57cec5SDimitry Andric 
82*bdd1243dSDimitry Andric   if (F.getName() == NVVM_REFLECT_FUNCTION ||
83*bdd1243dSDimitry Andric       F.getName() == NVVM_REFLECT_OCL_FUNCTION) {
840b57cec5SDimitry Andric     assert(F.isDeclaration() && "_reflect function should not have a body");
850b57cec5SDimitry Andric     assert(F.getReturnType()->isIntegerTy() &&
860b57cec5SDimitry Andric            "_reflect's return type should be integer");
870b57cec5SDimitry Andric     return false;
880b57cec5SDimitry Andric   }
890b57cec5SDimitry Andric 
900b57cec5SDimitry Andric   SmallVector<Instruction *, 4> ToRemove;
910b57cec5SDimitry Andric 
920b57cec5SDimitry Andric   // Go through the calls in this function.  Each call to __nvvm_reflect or
930b57cec5SDimitry Andric   // llvm.nvvm.reflect should be a CallInst with a ConstantArray argument.
940b57cec5SDimitry Andric   // First validate that. If the c-string corresponding to the ConstantArray can
950b57cec5SDimitry Andric   // be found successfully, see if it can be found in VarMap. If so, replace the
960b57cec5SDimitry Andric   // uses of CallInst with the value found in VarMap. If not, replace the use
970b57cec5SDimitry Andric   // with value 0.
980b57cec5SDimitry Andric 
990b57cec5SDimitry Andric   // The IR for __nvvm_reflect calls differs between CUDA versions.
1000b57cec5SDimitry Andric   //
1010b57cec5SDimitry Andric   // CUDA 6.5 and earlier uses this sequence:
1020b57cec5SDimitry Andric   //    %ptr = tail call i8* @llvm.nvvm.ptr.constant.to.gen.p0i8.p4i8
1030b57cec5SDimitry Andric   //        (i8 addrspace(4)* getelementptr inbounds
1040b57cec5SDimitry Andric   //           ([8 x i8], [8 x i8] addrspace(4)* @str, i32 0, i32 0))
1050b57cec5SDimitry Andric   //    %reflect = tail call i32 @__nvvm_reflect(i8* %ptr)
1060b57cec5SDimitry Andric   //
1070b57cec5SDimitry Andric   // The value returned by Sym->getOperand(0) is a Constant with a
1080b57cec5SDimitry Andric   // ConstantDataSequential operand which can be converted to string and used
1090b57cec5SDimitry Andric   // for lookup.
1100b57cec5SDimitry Andric   //
1110b57cec5SDimitry Andric   // CUDA 7.0 does it slightly differently:
1120b57cec5SDimitry Andric   //   %reflect = call i32 @__nvvm_reflect(i8* addrspacecast
1130b57cec5SDimitry Andric   //        (i8 addrspace(1)* getelementptr inbounds
1140b57cec5SDimitry Andric   //           ([8 x i8], [8 x i8] addrspace(1)* @str, i32 0, i32 0) to i8*))
1150b57cec5SDimitry Andric   //
1160b57cec5SDimitry Andric   // In this case, we get a Constant with a GlobalVariable operand and we need
1170b57cec5SDimitry Andric   // to dig deeper to find its initializer with the string we'll use for lookup.
1180b57cec5SDimitry Andric   for (Instruction &I : instructions(F)) {
1190b57cec5SDimitry Andric     CallInst *Call = dyn_cast<CallInst>(&I);
1200b57cec5SDimitry Andric     if (!Call)
1210b57cec5SDimitry Andric       continue;
1220b57cec5SDimitry Andric     Function *Callee = Call->getCalledFunction();
1230b57cec5SDimitry Andric     if (!Callee || (Callee->getName() != NVVM_REFLECT_FUNCTION &&
124*bdd1243dSDimitry Andric                     Callee->getName() != NVVM_REFLECT_OCL_FUNCTION &&
1250b57cec5SDimitry Andric                     Callee->getIntrinsicID() != Intrinsic::nvvm_reflect))
1260b57cec5SDimitry Andric       continue;
1270b57cec5SDimitry Andric 
1280b57cec5SDimitry Andric     // FIXME: Improve error handling here and elsewhere in this pass.
1290b57cec5SDimitry Andric     assert(Call->getNumOperands() == 2 &&
1300b57cec5SDimitry Andric            "Wrong number of operands to __nvvm_reflect function");
1310b57cec5SDimitry Andric 
1320b57cec5SDimitry Andric     // In cuda 6.5 and earlier, we will have an extra constant-to-generic
1330b57cec5SDimitry Andric     // conversion of the string.
1340b57cec5SDimitry Andric     const Value *Str = Call->getArgOperand(0);
1350b57cec5SDimitry Andric     if (const CallInst *ConvCall = dyn_cast<CallInst>(Str)) {
1360b57cec5SDimitry Andric       // FIXME: Add assertions about ConvCall.
1370b57cec5SDimitry Andric       Str = ConvCall->getArgOperand(0);
1380b57cec5SDimitry Andric     }
13981ad6265SDimitry Andric     // Pre opaque pointers we have a constant expression wrapping the constant
14081ad6265SDimitry Andric     // string.
14181ad6265SDimitry Andric     Str = Str->stripPointerCasts();
14281ad6265SDimitry Andric     assert(isa<Constant>(Str) &&
1430b57cec5SDimitry Andric            "Format of __nvvm_reflect function not recognized");
1440b57cec5SDimitry Andric 
14581ad6265SDimitry Andric     const Value *Operand = cast<Constant>(Str)->getOperand(0);
1460b57cec5SDimitry Andric     if (const GlobalVariable *GV = dyn_cast<GlobalVariable>(Operand)) {
1470b57cec5SDimitry Andric       // For CUDA-7.0 style __nvvm_reflect calls, we need to find the operand's
1480b57cec5SDimitry Andric       // initializer.
1490b57cec5SDimitry Andric       assert(GV->hasInitializer() &&
1500b57cec5SDimitry Andric              "Format of _reflect function not recognized");
1510b57cec5SDimitry Andric       const Constant *Initializer = GV->getInitializer();
1520b57cec5SDimitry Andric       Operand = Initializer;
1530b57cec5SDimitry Andric     }
1540b57cec5SDimitry Andric 
1550b57cec5SDimitry Andric     assert(isa<ConstantDataSequential>(Operand) &&
1560b57cec5SDimitry Andric            "Format of _reflect function not recognized");
1570b57cec5SDimitry Andric     assert(cast<ConstantDataSequential>(Operand)->isCString() &&
1580b57cec5SDimitry Andric            "Format of _reflect function not recognized");
1590b57cec5SDimitry Andric 
1600b57cec5SDimitry Andric     StringRef ReflectArg = cast<ConstantDataSequential>(Operand)->getAsString();
1610b57cec5SDimitry Andric     ReflectArg = ReflectArg.substr(0, ReflectArg.size() - 1);
1620b57cec5SDimitry Andric     LLVM_DEBUG(dbgs() << "Arg of _reflect : " << ReflectArg << "\n");
1630b57cec5SDimitry Andric 
1640b57cec5SDimitry Andric     int ReflectVal = 0; // The default value is 0
1650b57cec5SDimitry Andric     if (ReflectArg == "__CUDA_FTZ") {
1660b57cec5SDimitry Andric       // Try to pull __CUDA_FTZ from the nvvm-reflect-ftz module flag.  Our
1670b57cec5SDimitry Andric       // choice here must be kept in sync with AutoUpgrade, which uses the same
1680b57cec5SDimitry Andric       // technique to detect whether ftz is enabled.
1690b57cec5SDimitry Andric       if (auto *Flag = mdconst::extract_or_null<ConstantInt>(
1700b57cec5SDimitry Andric               F.getParent()->getModuleFlag("nvvm-reflect-ftz")))
1710b57cec5SDimitry Andric         ReflectVal = Flag->getSExtValue();
1720b57cec5SDimitry Andric     } else if (ReflectArg == "__CUDA_ARCH") {
1730b57cec5SDimitry Andric       ReflectVal = SmVersion * 10;
1740b57cec5SDimitry Andric     }
1750b57cec5SDimitry Andric     Call->replaceAllUsesWith(ConstantInt::get(Call->getType(), ReflectVal));
1760b57cec5SDimitry Andric     ToRemove.push_back(Call);
1770b57cec5SDimitry Andric   }
1780b57cec5SDimitry Andric 
1790b57cec5SDimitry Andric   for (Instruction *I : ToRemove)
1800b57cec5SDimitry Andric     I->eraseFromParent();
1810b57cec5SDimitry Andric 
1820b57cec5SDimitry Andric   return ToRemove.size() > 0;
1830b57cec5SDimitry Andric }
184e8d8bef9SDimitry Andric 
185e8d8bef9SDimitry Andric bool NVVMReflect::runOnFunction(Function &F) {
186e8d8bef9SDimitry Andric   return runNVVMReflect(F, SmVersion);
187e8d8bef9SDimitry Andric }
188e8d8bef9SDimitry Andric 
189e8d8bef9SDimitry Andric NVVMReflectPass::NVVMReflectPass() : NVVMReflectPass(0) {}
190e8d8bef9SDimitry Andric 
191e8d8bef9SDimitry Andric PreservedAnalyses NVVMReflectPass::run(Function &F,
192e8d8bef9SDimitry Andric                                        FunctionAnalysisManager &AM) {
193e8d8bef9SDimitry Andric   return runNVVMReflect(F, SmVersion) ? PreservedAnalyses::none()
194e8d8bef9SDimitry Andric                                       : PreservedAnalyses::all();
195e8d8bef9SDimitry Andric }
196