diff --git a/include/LLVMSPIRVOpts.h b/include/LLVMSPIRVOpts.h index bd1c73f318..0de0e4ca6f 100644 --- a/include/LLVMSPIRVOpts.h +++ b/include/LLVMSPIRVOpts.h @@ -109,6 +109,8 @@ enum class DebugInfoEIS : uint32_t { NonSemantic_Shader_DebugInfo_200 }; +enum class BuiltinFormat : uint32_t { Function, Global }; + /// \brief Helper class to manage SPIR-V translation class TranslatorOpts { public: @@ -229,6 +231,11 @@ class TranslatorOpts { EmitFunctionPtrAddrSpace = Value; } + void setBuiltinFormat(BuiltinFormat Value) noexcept { + SPIRVBuiltinFormat = Value; + } + BuiltinFormat getBuiltinFormat() const noexcept { return SPIRVBuiltinFormat; } + private: // Common translation options VersionNumber MaxVersion = VersionNumber::MaximumVersion; @@ -275,6 +282,8 @@ class TranslatorOpts { bool EmitFunctionPtrAddrSpace = false; bool PreserveAuxData = false; + + BuiltinFormat SPIRVBuiltinFormat = BuiltinFormat::Function; }; } // namespace SPIRV diff --git a/lib/SPIRV/SPIRVInternal.h b/lib/SPIRV/SPIRVInternal.h index d4cfd23e0c..ef0afb82c9 100644 --- a/lib/SPIRV/SPIRVInternal.h +++ b/lib/SPIRV/SPIRVInternal.h @@ -1113,6 +1113,13 @@ bool lowerBuiltinVariableToCall(GlobalVariable *GV, // Transform all builtin variables into calls bool lowerBuiltinVariablesToCalls(Module *M); +// Transform all builtin calls into variables +bool lowerBuiltinCallsToVariables(Module *M); + +// Transform all builtins into variables or calls +// depending on user specification +bool lowerBuiltins(SPIRVModule *BM, Module *M); + /// \brief Post-process OpenCL or SPIRV builtin function returning struct type. /// /// Some builtin functions are translated to SPIR-V instructions with diff --git a/lib/SPIRV/SPIRVReader.cpp b/lib/SPIRV/SPIRVReader.cpp index 95b8ace215..f6e9d9f9a7 100644 --- a/lib/SPIRV/SPIRVReader.cpp +++ b/lib/SPIRV/SPIRVReader.cpp @@ -3677,7 +3677,7 @@ bool SPIRVToLLVM::translate() { // e.g. load i32, i32* @__spirv_BuiltInGlobalLinearId, align 4 // If the desired format is global variables, we don't have to lower them // as calls. - if (!lowerBuiltinVariablesToCalls(M)) + if (!lowerBuiltins(BM, M)) return false; if (BM->getDesiredBIsRepresentation() == BIsRepresentation::SPIRVFriendlyIR) { SPIRVWord SrcLangVer = 0; diff --git a/lib/SPIRV/SPIRVUtil.cpp b/lib/SPIRV/SPIRVUtil.cpp index 615caa115c..f193a53c0e 100644 --- a/lib/SPIRV/SPIRVUtil.cpp +++ b/lib/SPIRV/SPIRVUtil.cpp @@ -2062,6 +2062,82 @@ bool lowerBuiltinVariablesToCalls(Module *M) { return true; } +/// Transforms SPV-IR work-item builtin calls to SPIRV builtin variables. +/// e.g. +/// SPV-IR: @_Z33__spirv_BuiltInGlobalInvocationIdi(i) +/// is transformed as: +/// x = load GlobalInvocationId; extract x, i +/// e.g. +/// SPV-IR: @_Z22__spirv_BuiltInWorkDim() +/// is transformed as: +/// load WorkDim +bool lowerBuiltinCallsToVariables(Module *M) { + LLVM_DEBUG(dbgs() << "Enter lowerBuiltinCallsToVariables\n"); + // Store instructions and functions that need to be removed. + SmallVector ToRemove; + for (auto &F : *M) { + // Builtins should be declaration only. + if (!F.isDeclaration()) + continue; + StringRef DemangledName; + if (!oclIsBuiltin(F.getName(), DemangledName)) + continue; + LLVM_DEBUG(dbgs() << "Function demangled name: " << DemangledName << '\n'); + SmallVector Postfix; + // Deprefix "__spirv_" + StringRef Name = dePrefixSPIRVName(DemangledName, Postfix); + // Lookup SPIRV Builtin map. + if (!SPIRVBuiltInNameMap::rfind(Name.str(), nullptr)) + continue; + std::string BuiltinVarName = DemangledName.str(); + LLVM_DEBUG(dbgs() << "builtin variable name: " << BuiltinVarName << '\n'); + bool IsVec = F.getFunctionType()->getNumParams() > 0; + Type *GVType = + IsVec ? FixedVectorType::get(F.getReturnType(), 3) : F.getReturnType(); + auto *BV = new GlobalVariable( + *M, GVType, /*isConstant=*/true, GlobalValue::ExternalLinkage, nullptr, + BuiltinVarName, 0, GlobalVariable::NotThreadLocal, SPIRAS_Input); + for (auto *U : F.users()) { + auto *CI = dyn_cast(U); + assert(CI && "invalid instruction"); + const DebugLoc &DLoc = CI->getDebugLoc(); + Instruction *NewValue = new LoadInst(GVType, BV, "", CI); + if (DLoc) + NewValue->setDebugLoc(DLoc); + LLVM_DEBUG(dbgs() << "Transform: " << *CI << " => " << *NewValue << '\n'); + if (IsVec) { + NewValue = + ExtractElementInst::Create(NewValue, CI->getArgOperand(0), "", CI); + if (DLoc) + NewValue->setDebugLoc(DLoc); + LLVM_DEBUG(dbgs() << *NewValue << '\n'); + } + NewValue->takeName(CI); + CI->replaceAllUsesWith(NewValue); + ToRemove.push_back(CI); + } + ToRemove.push_back(&F); + } + for (auto *V : ToRemove) { + if (auto *I = dyn_cast(V)) + I->eraseFromParent(); + else if (auto *F = dyn_cast(V)) + F->eraseFromParent(); + else + llvm_unreachable("Unexpected value to remove!"); + } + return true; +} + +bool lowerBuiltins(SPIRVModule *BM, Module *M) { + auto Format = BM->getBuiltinFormat(); + if (Format == BuiltinFormat::Function && !lowerBuiltinVariablesToCalls(M)) + return false; + if (Format == BuiltinFormat::Global && !lowerBuiltinCallsToVariables(M)) + return false; + return true; +} + bool postProcessBuiltinReturningStruct(Function *F) { Module *M = F->getParent(); LLVMContext *Context = &M->getContext(); diff --git a/lib/SPIRV/SPIRVWriter.cpp b/lib/SPIRV/SPIRVWriter.cpp index 4b22b300c1..b576333728 100644 --- a/lib/SPIRV/SPIRVWriter.cpp +++ b/lib/SPIRV/SPIRVWriter.cpp @@ -2830,73 +2830,6 @@ bool LLVMToSPIRVBase::transBuiltinSet() { return true; } -/// Transforms SPV-IR work-item builtin calls to SPIRV builtin variables. -/// e.g. -/// SPV-IR: @_Z33__spirv_BuiltInGlobalInvocationIdi(i) -/// is transformed as: -/// x = load GlobalInvocationId; extract x, i -/// e.g. -/// SPV-IR: @_Z22__spirv_BuiltInWorkDim() -/// is transformed as: -/// load WorkDim -bool LLVMToSPIRVBase::transWorkItemBuiltinCallsToVariables() { - LLVM_DEBUG(dbgs() << "Enter transWorkItemBuiltinCallsToVariables\n"); - // Store instructions and functions that need to be removed. - SmallVector ToRemove; - for (auto &F : *M) { - // Builtins should be declaration only. - if (!F.isDeclaration()) - continue; - StringRef DemangledName; - if (!oclIsBuiltin(F.getName(), DemangledName)) - continue; - LLVM_DEBUG(dbgs() << "Function demangled name: " << DemangledName << '\n'); - SmallVector Postfix; - // Deprefix "__spirv_" - StringRef Name = dePrefixSPIRVName(DemangledName, Postfix); - // Lookup SPIRV Builtin map. - if (!SPIRVBuiltInNameMap::rfind(Name.str(), nullptr)) - continue; - std::string BuiltinVarName = DemangledName.str(); - LLVM_DEBUG(dbgs() << "builtin variable name: " << BuiltinVarName << '\n'); - bool IsVec = F.getFunctionType()->getNumParams() > 0; - Type *GVType = - IsVec ? FixedVectorType::get(F.getReturnType(), 3) : F.getReturnType(); - auto *BV = new GlobalVariable( - *M, GVType, /*isConstant=*/true, GlobalValue::ExternalLinkage, nullptr, - BuiltinVarName, 0, GlobalVariable::NotThreadLocal, SPIRAS_Input); - for (auto *U : F.users()) { - auto *CI = dyn_cast(U); - assert(CI && "invalid instruction"); - const DebugLoc &DLoc = CI->getDebugLoc(); - Instruction *NewValue = new LoadInst(GVType, BV, "", CI); - if (DLoc) - NewValue->setDebugLoc(DLoc); - LLVM_DEBUG(dbgs() << "Transform: " << *CI << " => " << *NewValue << '\n'); - if (IsVec) { - NewValue = - ExtractElementInst::Create(NewValue, CI->getArgOperand(0), "", CI); - if (DLoc) - NewValue->setDebugLoc(DLoc); - LLVM_DEBUG(dbgs() << *NewValue << '\n'); - } - NewValue->takeName(CI); - CI->replaceAllUsesWith(NewValue); - ToRemove.push_back(CI); - } - ToRemove.push_back(&F); - } - for (auto *V : ToRemove) { - if (auto *I = dyn_cast(V)) - I->eraseFromParent(); - else if (auto *F = dyn_cast(V)) - F->eraseFromParent(); - else - llvm_unreachable("Unexpected value to remove!"); - } - return true; -} - /// Translate sampler* spcv.cast(i32 arg) or /// sampler* __translate_sampler_initializer(i32 arg) /// Three cases are possible: @@ -4894,7 +4827,7 @@ bool LLVMToSPIRVBase::translate() { BM->addCapability(CapabilityLinkage); // Transform SPV-IR builtin calls to builtin variables. - if (!transWorkItemBuiltinCallsToVariables()) + if (!lowerBuiltinCallsToVariables(M)) return false; if (!transSourceLanguage()) diff --git a/lib/SPIRV/SPIRVWriter.h b/lib/SPIRV/SPIRVWriter.h index 2b0529e596..763bc5ee14 100644 --- a/lib/SPIRV/SPIRVWriter.h +++ b/lib/SPIRV/SPIRVWriter.h @@ -105,7 +105,6 @@ class LLVMToSPIRVBase { bool transSourceLanguage(); bool transExtension(); bool transBuiltinSet(); - bool transWorkItemBuiltinCallsToVariables(); bool isKnownIntrinsic(Intrinsic::ID Id); SPIRVValue *transIntrinsicInst(IntrinsicInst *Intrinsic, SPIRVBasicBlock *BB); enum class FPBuiltinType { diff --git a/lib/SPIRV/libSPIRV/SPIRVModule.h b/lib/SPIRV/libSPIRV/SPIRVModule.h index 8e4e0bac47..4ce191473d 100644 --- a/lib/SPIRV/libSPIRV/SPIRVModule.h +++ b/lib/SPIRV/libSPIRV/SPIRVModule.h @@ -551,6 +551,10 @@ class SPIRVModule { return TranslationOpts.preserveAuxData(); } + BuiltinFormat getBuiltinFormat() const noexcept { + return TranslationOpts.getBuiltinFormat(); + } + SPIRVExtInstSetKind getDebugInfoEIS() const { switch (TranslationOpts.getDebugInfoEIS()) { case DebugInfoEIS::SPIRV_Debug: diff --git a/test/builtin-functions.ll b/test/builtin-functions.ll new file mode 100644 index 0000000000..e588c87690 --- /dev/null +++ b/test/builtin-functions.ll @@ -0,0 +1,33 @@ +; RUN: llvm-as %s -o %t.bc +; RUN: llvm-spirv %t.bc -spirv-text -o %t +; RUN: llvm-spirv -r %t -spirv-text --spirv-target-env=SPV-IR --spirv-builtin-format=function -o %t2_rev.bc +; RUN: llvm-spirv -r %t -spirv-text --spirv-target-env=SPV-IR --spirv-builtin-format=global -o %t3_rev.bc +; RUN: llvm-spirv -r %t -spirv-text --spirv-builtin-format=function -o %t2_rev_ocl.bc +; RUN: llvm-dis < %t2_rev.bc | FileCheck --check-prefix=CHECK-FUNCTION-FORMAT-REV %s +; RUN: llvm-dis < %t3_rev.bc | FileCheck --check-prefix=CHECK-GLOBAL-FORMAT-REV %s +; RUN: llvm-dis < %t2_rev_ocl.bc | FileCheck --check-prefix=CHECK-FUNCTION-FORMAT-OCL-REV %s +; RUN: llvm-spirv %t.bc -o %t.spv +; RUN: spirv-val %t.spv +; RUN: llvm-spirv %t.bc --spirv-builtin-format=function -o %t2.spv +; RUN: spirv-val %t2.spv +; RUN: llvm-spirv %t.bc --spirv-builtin-format=global -o %t3.spv +; RUN: spirv-val %t3.spv + +; CHECK-FUNCTION-FORMAT-REV: declare spir_func i64 @_Z26__spirv_BuiltInWorkgroupIdi(i32) +; CHECK-FUNCTION-FORMAT-OCL-REV: declare spir_func i64 @_Z12get_group_idj(i32) + +; CHECK-GLOBAL-FORMAT-REV: @__spirv_BuiltInWorkgroupId = external addrspace(7) constant <3 x i64> + +; ModuleID = 'test.bc' +target datalayout = "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-n8:16:32:64" +target triple = "spir64-unknown-unknown" + +; Function Attrs: nounwind +declare spir_func i64 @_Z26__spirv_BuiltInWorkgroupIdi(i32) #0 + +; Function Attrs: convergent norecurse nounwind +define weak_odr dso_local spir_kernel void @foo() { +entry: + %0 = call spir_func i64 @_Z26__spirv_BuiltInWorkgroupIdi(i32 0) #0 + ret void +} diff --git a/test/builtin-globals.ll b/test/builtin-globals.ll new file mode 100644 index 0000000000..d36e657674 --- /dev/null +++ b/test/builtin-globals.ll @@ -0,0 +1,32 @@ +; RUN: llvm-as %s -o %t.bc +; RUN: llvm-spirv %t.bc -spirv-text -o %t +; RUN: llvm-spirv -r %t -spirv-text --spirv-target-env=SPV-IR --spirv-builtin-format=function -o %t2_rev.bc +; RUN: llvm-spirv -r %t -spirv-text --spirv-target-env=SPV-IR --spirv-builtin-format=global -o %t3_rev.bc +; RUN: llvm-spirv -r %t -spirv-text --spirv-builtin-format=function -o %t2_rev_ocl.bc +; RUN: llvm-dis < %t2_rev.bc | FileCheck --check-prefix=CHECK-FUNCTION-FORMAT-REV %s +; RUN: llvm-dis < %t3_rev.bc | FileCheck --check-prefix=CHECK-GLOBAL-FORMAT-REV %s +; RUN: llvm-dis < %t2_rev_ocl.bc | FileCheck --check-prefix=CHECK-FUNCTION-FORMAT-OCL-REV %s +; RUN: llvm-spirv %t.bc -o %t.spv +; RUN: spirv-val %t.spv +; RUN: llvm-spirv %t.bc --spirv-builtin-format=function -o %t2.spv +; RUN: spirv-val %t2.spv +; RUN: llvm-spirv %t.bc --spirv-builtin-format=global -o %t3.spv +; RUN: spirv-val %t3.spv + +; CHECK-FUNCTION-FORMAT-REV: declare spir_func i64 @_Z26__spirv_BuiltInWorkgroupIdi(i32) +; CHECK-FUNCTION-FORMAT-OCL-REV: declare spir_func i64 @_Z12get_group_idj(i32) + +; CHECK-GLOBAL-FORMAT-REV: @__spirv_BuiltInWorkgroupId = external addrspace(1) constant <3 x i64>, align 32 + +; ModuleID = 'test.bc' +target datalayout = "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-n8:16:32:64" +target triple = "spir64-unknown-unknown" + +@__spirv_BuiltInWorkgroupId = external dso_local local_unnamed_addr addrspace(1) constant <3 x i64>, align 32 + +; Function Attrs: convergent norecurse nounwind +define weak_odr dso_local spir_kernel void @foo() { +entry: + %0 = load i64, i64 addrspace(1)* getelementptr inbounds (<3 x i64>, <3 x i64> addrspace(1)* @__spirv_BuiltInWorkgroupId, i64 0, i64 0), align 32 + ret void +} diff --git a/tools/llvm-spirv/llvm-spirv.cpp b/tools/llvm-spirv/llvm-spirv.cpp index 070fbbb8b3..421ddf8146 100644 --- a/tools/llvm-spirv/llvm-spirv.cpp +++ b/tools/llvm-spirv/llvm-spirv.cpp @@ -256,6 +256,16 @@ static cl::opt SPIRVReplaceLLVMFmulAddWithOpenCLMad( "instruction from OpenCL extended instruction set"), cl::init(true)); +static cl::opt SPIRVBuiltinFormat( + "spirv-builtin-format", + cl::desc("Set LLVM-IR representation of SPIR-V builtin variables:"), + cl::init(SPIRV::BuiltinFormat::Function), + cl::values( + clEnumValN(SPIRV::BuiltinFormat::Function, "function", + "Use functions to represent SPIR-V builtin variables"), + clEnumValN(SPIRV::BuiltinFormat::Global, "global", + "Use globals to represent SPIR-V builtin variables"))); + static std::string removeExt(const std::string &FileName) { size_t Pos = FileName.find_last_of("."); if (Pos != std::string::npos) @@ -700,6 +710,15 @@ int main(int Ac, char **Av) { Opts.setFPContractMode(FPCMode); + if (SPIRVBuiltinFormat.getNumOccurrences() != 0) { + if (!IsReverse) { + errs() << "Note: --spirv-builtin-format option ignored as it only " + "affects translation from SPIR-V to LLVM IR"; + } else { + Opts.setBuiltinFormat(SPIRVBuiltinFormat); + } + } + if (SPIRVMemToReg) Opts.setMemToRegEnabled(SPIRVMemToReg); if (SPIRVGenKernelArgNameMD)