Skip to content

Commit

Permalink
[Backport to 14] Add option to control builtin format for reverse tra…
Browse files Browse the repository at this point in the history
…nslation. (#3035)

Currently, we always convert SPIR-V bultins to globals for forward
translation and to functions for reverse translation.
I have a use case where I want to keep them as globals for reverse
translation, so I added this mode.
Implementations for both cases already existed, I just consolidated them
and added the option.

Backport of
#1986.
  • Loading branch information
haonanya1 authored Mar 5, 2025
1 parent fc08bfe commit f5bb824
Show file tree
Hide file tree
Showing 10 changed files with 182 additions and 70 deletions.
9 changes: 9 additions & 0 deletions include/LLVMSPIRVOpts.h
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -275,6 +282,8 @@ class TranslatorOpts {
bool EmitFunctionPtrAddrSpace = false;

bool PreserveAuxData = false;

BuiltinFormat SPIRVBuiltinFormat = BuiltinFormat::Function;
};

} // namespace SPIRV
Expand Down
7 changes: 7 additions & 0 deletions lib/SPIRV/SPIRVInternal.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion lib/SPIRV/SPIRVReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
76 changes: 76 additions & 0 deletions lib/SPIRV/SPIRVUtil.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<Value *, 16> 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<StringRef, 2> 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<CallInst>(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<Instruction>(V))
I->eraseFromParent();
else if (auto *F = dyn_cast<Function>(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();
Expand Down
69 changes: 1 addition & 68 deletions lib/SPIRV/SPIRVWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<Value *, 16> 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<StringRef, 2> 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<CallInst>(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<Instruction>(V))
I->eraseFromParent();
else if (auto *F = dyn_cast<Function>(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:
Expand Down Expand Up @@ -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())
Expand Down
1 change: 0 additions & 1 deletion lib/SPIRV/SPIRVWriter.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
4 changes: 4 additions & 0 deletions lib/SPIRV/libSPIRV/SPIRVModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
33 changes: 33 additions & 0 deletions test/builtin-functions.ll
Original file line number Diff line number Diff line change
@@ -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
}
32 changes: 32 additions & 0 deletions test/builtin-globals.ll
Original file line number Diff line number Diff line change
@@ -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
}
19 changes: 19 additions & 0 deletions tools/llvm-spirv/llvm-spirv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,16 @@ static cl::opt<bool> SPIRVReplaceLLVMFmulAddWithOpenCLMad(
"instruction from OpenCL extended instruction set"),
cl::init(true));

static cl::opt<SPIRV::BuiltinFormat> 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)
Expand Down Expand Up @@ -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)
Expand Down

0 comments on commit f5bb824

Please sign in to comment.