Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SPIR-V] Reland entry point patches #14450

Open
wants to merge 10 commits into
base: sycl
Choose a base branch
from
1 change: 1 addition & 0 deletions llvm-spirv/lib/SPIRV/SPIRVInternal.h
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,7 @@ const static char ConvertHandleToImageINTEL[] = "ConvertHandleToImageINTEL";
const static char ConvertHandleToSamplerINTEL[] = "ConvertHandleToSamplerINTEL";
const static char ConvertHandleToSampledImageINTEL[] =
"ConvertHandleToSampledImageINTEL";
const static char EntrypointPrefix[] = "__spirv_entry_";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very minor Nit: Can you please add this three lines earlier to align with upstream translator?

Thanks

} // namespace kSPIRVName

namespace kSPIRVPostfix {
Expand Down
118 changes: 72 additions & 46 deletions llvm-spirv/lib/SPIRV/SPIRVReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2973,12 +2973,83 @@ bool SPIRVToLLVM::foreachFuncCtlMask(SourceTy Source, FuncTy Func) {
return true;
}

void SPIRVToLLVM::transFunctionAttrs(SPIRVFunction *BF, Function *F) {
if (BF->hasDecorate(DecorationReferencedIndirectlyINTEL))
F->addFnAttr("referenced-indirectly");
if (isFuncNoUnwind())
F->addFnAttr(Attribute::NoUnwind);
foreachFuncCtlMask(BF, [&](Attribute::AttrKind Attr) { F->addFnAttr(Attr); });

for (Function::arg_iterator I = F->arg_begin(), E = F->arg_end(); I != E;
++I) {
auto *BA = BF->getArgument(I->getArgNo());
mapValue(BA, &(*I));
setName(&(*I), BA);
AttributeMask IllegalAttrs = AttributeFuncs::typeIncompatible(I->getType());
BA->foreachAttr([&](SPIRVFuncParamAttrKind Kind) {
// Skip this function parameter attribute as it will translated among
// OpenCL metadata
if (Kind == FunctionParameterAttributeRuntimeAlignedINTEL)
return;
Attribute::AttrKind LLVMKind = SPIRSPIRVFuncParamAttrMap::rmap(Kind);
if (IllegalAttrs.contains(LLVMKind))
return;
Type *AttrTy = nullptr;
switch (LLVMKind) {
case Attribute::AttrKind::ByVal:
case Attribute::AttrKind::StructRet:
AttrTy = transType(BA->getType()->getPointerElementType());
break;
default:
break; // do nothing
}
// Make sure to use a correct constructor for a typed/typeless attribute
auto A = AttrTy ? Attribute::get(*Context, LLVMKind, AttrTy)
: Attribute::get(*Context, LLVMKind);
I->addAttr(A);
});

AttrBuilder Builder(*Context);
SPIRVWord MaxOffset = 0;
if (BA->hasDecorate(DecorationMaxByteOffset, 0, &MaxOffset))
Builder.addDereferenceableAttr(MaxOffset);
SPIRVWord AlignmentBytes = 0;
if (BA->hasDecorate(DecorationAlignment, 0, &AlignmentBytes))
Builder.addAlignmentAttr(AlignmentBytes);
I->addAttrs(Builder);
}
BF->foreachReturnValueAttr([&](SPIRVFuncParamAttrKind Kind) {
if (Kind == FunctionParameterAttributeNoWrite)
return;
F->addRetAttr(SPIRSPIRVFuncParamAttrMap::rmap(Kind));
});
}

Function *SPIRVToLLVM::transFunction(SPIRVFunction *BF) {
auto Loc = FuncMap.find(BF);
if (Loc != FuncMap.end())
return Loc->second;

auto IsKernel = isKernel(BF);

if (IsKernel) {
// search for a previous function with the same name
// upgrade it to a kernel and drop this if it's found
for (auto &I : FuncMap) {
auto BFName = I.getFirst()->getName();
if (BF->getName() == BFName) {
auto *F = I.getSecond();
F->setCallingConv(CallingConv::SPIR_KERNEL);
F->setLinkage(GlobalValue::ExternalLinkage);
F->setDSOLocal(false);
F = cast<Function>(mapValue(BF, F));
mapFunction(BF, F);
transFunctionAttrs(BF, F);
return F;
}
}
}

auto Linkage = IsKernel ? GlobalValue::ExternalLinkage : transLinkageType(BF);
FunctionType *FT = cast<FunctionType>(transType(BF->getFunctionType()));
std::string FuncName = BF->getName();
Expand Down Expand Up @@ -3022,52 +3093,7 @@ Function *SPIRVToLLVM::transFunction(SPIRVFunction *BF) {

F->setCallingConv(IsKernel ? CallingConv::SPIR_KERNEL
: CallingConv::SPIR_FUNC);
if (BF->hasDecorate(DecorationReferencedIndirectlyINTEL))
F->addFnAttr("referenced-indirectly");
if (isFuncNoUnwind())
F->addFnAttr(Attribute::NoUnwind);
foreachFuncCtlMask(BF, [&](Attribute::AttrKind Attr) { F->addFnAttr(Attr); });

for (Function::arg_iterator I = F->arg_begin(), E = F->arg_end(); I != E;
++I) {
auto BA = BF->getArgument(I->getArgNo());
mapValue(BA, &(*I));
setName(&(*I), BA);
BA->foreachAttr([&](SPIRVFuncParamAttrKind Kind) {
// Skip this function parameter attribute as it will translated among
// OpenCL metadata
if (Kind == FunctionParameterAttributeRuntimeAlignedINTEL)
return;
Attribute::AttrKind LLVMKind = SPIRSPIRVFuncParamAttrMap::rmap(Kind);
Type *AttrTy = nullptr;
switch (LLVMKind) {
case Attribute::AttrKind::ByVal:
case Attribute::AttrKind::StructRet:
AttrTy = transType(BA->getType()->getPointerElementType());
break;
default:
break; // do nothing
}
// Make sure to use a correct constructor for a typed/typeless attribute
auto A = AttrTy ? Attribute::get(*Context, LLVMKind, AttrTy)
: Attribute::get(*Context, LLVMKind);
I->addAttr(A);
});

AttrBuilder Builder(*Context);
SPIRVWord MaxOffset = 0;
if (BA->hasDecorate(DecorationMaxByteOffset, 0, &MaxOffset))
Builder.addDereferenceableAttr(MaxOffset);
SPIRVWord AlignmentBytes = 0;
if (BA->hasDecorate(DecorationAlignment, 0, &AlignmentBytes))
Builder.addAlignmentAttr(AlignmentBytes);
I->addAttrs(Builder);
}
BF->foreachReturnValueAttr([&](SPIRVFuncParamAttrKind Kind) {
if (Kind == FunctionParameterAttributeNoWrite)
return;
F->addRetAttr(SPIRSPIRVFuncParamAttrMap::rmap(Kind));
});
transFunctionAttrs(BF, F);

// Creating all basic blocks before creating instructions.
for (size_t I = 0, E = BF->getNumBasicBlock(); I != E; ++I) {
Expand Down
1 change: 1 addition & 0 deletions llvm-spirv/lib/SPIRV/SPIRVReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ class SPIRVToLLVM : private BuiltinCallHelper {
std::vector<Value *> transValue(const std::vector<SPIRVValue *> &,
Function *F, BasicBlock *);
Function *transFunction(SPIRVFunction *F);
void transFunctionAttrs(SPIRVFunction *BF, Function *F);
Value *transBlockInvoke(SPIRVValue *Invoke, BasicBlock *BB);
Instruction *transWGSizeQueryBI(SPIRVInstruction *BI, BasicBlock *BB);
Instruction *transSGSizeQueryBI(SPIRVInstruction *BI, BasicBlock *BB);
Expand Down
65 changes: 65 additions & 0 deletions llvm-spirv/lib/SPIRV/SPIRVRegularizeLLVM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
#include "SPIRVRegularizeLLVM.h"
#include "OCLUtil.h"
#include "SPIRVInternal.h"
#include "SPIRVMDWalker.h"
#include "libSPIRV/SPIRVDebug.h"

#include "llvm/ADT/StringExtras.h" // llvm::isDigit
Expand Down Expand Up @@ -638,6 +639,7 @@ void prepareCacheControlsTranslation(Metadata *MD, Instruction *Inst) {
/// Remove entities not representable by SPIR-V
bool SPIRVRegularizeLLVMBase::regularize() {
eraseUselessFunctions(M);
addKernelEntryPoint(M);
expandSYCLTypeUsing(M);
cleanupConversionToNonStdIntegers(M);

Expand Down Expand Up @@ -849,6 +851,69 @@ bool SPIRVRegularizeLLVMBase::regularize() {
return true;
}

void SPIRVRegularizeLLVMBase::addKernelEntryPoint(Module *M) {
std::vector<Function *> Work;

// Get a list of all functions that have SPIR kernel calling conv
for (auto &F : *M) {
if (F.getCallingConv() == CallingConv::SPIR_KERNEL)
Work.push_back(&F);
}
for (auto &F : Work) {
// for declarations just make them into SPIR functions.
F->setCallingConv(CallingConv::SPIR_FUNC);
if (F->isDeclaration())
continue;

// Otherwise add a wrapper around the function to act as an entry point.
FunctionType *FType = F->getFunctionType();
std::string WrapName =
kSPIRVName::EntrypointPrefix + static_cast<std::string>(F->getName());
Function *WrapFn =
getOrCreateFunction(M, F->getReturnType(), FType->params(), WrapName);

auto *CallBB = BasicBlock::Create(M->getContext(), "", WrapFn);
IRBuilder<> Builder(CallBB);

Function::arg_iterator DestI = WrapFn->arg_begin();
for (const Argument &I : F->args()) {
DestI->setName(I.getName());
DestI++;
}
SmallVector<Value *, 1> Args;
for (Argument &I : WrapFn->args()) {
Args.emplace_back(&I);
}
auto *CI = CallInst::Create(F, ArrayRef<Value *>(Args), "", CallBB);
CI->setCallingConv(F->getCallingConv());
CI->setAttributes(F->getAttributes());

// copy over all the metadata (should it be removed from F?)
SmallVector<std::pair<unsigned, MDNode *>> MDs;
F->getAllMetadata(MDs);
WrapFn->setAttributes(F->getAttributes());
for (auto MD = MDs.begin(), End = MDs.end(); MD != End; ++MD) {
WrapFn->addMetadata(MD->first, *MD->second);
}
WrapFn->setCallingConv(CallingConv::SPIR_KERNEL);
WrapFn->setLinkage(llvm::GlobalValue::InternalLinkage);

Builder.CreateRet(F->getReturnType()->isVoidTy() ? nullptr : CI);

// Have to find the spir-v metadata for execution mode and transfer it to
// the wrapper.
if (auto NMD = SPIRVMDWalker(*M).getNamedMD(kSPIRVMD::ExecutionMode)) {
while (!NMD.atEnd()) {
Function *MDF = nullptr;
auto N = NMD.nextOp(); /* execution mode MDNode */
N.get(MDF);
if (MDF == F)
N.M->replaceOperandWith(0, ValueAsMetadata::get(WrapFn));
}
}
}
}

} // namespace SPIRV

INITIALIZE_PASS(SPIRVRegularizeLLVMLegacy, "spvregular",
Expand Down
5 changes: 5 additions & 0 deletions llvm-spirv/lib/SPIRV/SPIRVRegularizeLLVM.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ class SPIRVRegularizeLLVMBase {
// Lower functions
bool regularize();

// SPIR-V disallows functions being entrypoints and called
// LLVM doesn't. This adds a wrapper around the entry point
// that later SPIR-V writer renames.
void addKernelEntryPoint(Module *M);

/// Some LLVM intrinsics that have no SPIR-V counterpart may be wrapped in
/// @spirv.llvm_intrinsic_* function. During reverse translation from SPIR-V
/// to LLVM IR we can detect this @spirv.llvm_intrinsic_* function and
Expand Down
27 changes: 17 additions & 10 deletions llvm-spirv/lib/SPIRV/SPIRVWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -875,13 +875,19 @@ SPIRVFunction *LLVMToSPIRVBase::transFunctionDecl(Function *F) {
static_cast<SPIRVFunction *>(mapValue(F, BM->addFunction(BFT)));
BF->setFunctionControlMask(transFunctionControlMask(F));
if (F->hasName()) {
if (isUniformGroupOperation(F))
BM->getErrorLog().checkError(
BM->isAllowedToUseExtension(
ExtensionID::SPV_KHR_uniform_group_instructions),
SPIRVEC_RequiresExtension, "SPV_KHR_uniform_group_instructions\n");

BM->setName(BF, F->getName().str());
if (isKernel(F)) {
/* strip the prefix as the runtime will be looking for this name */
std::string Prefix = kSPIRVName::EntrypointPrefix;
std::string Name = F->getName().str();
BM->setName(BF, Name.substr(Prefix.size()));
} else {
if (isUniformGroupOperation(F))
BM->getErrorLog().checkError(
BM->isAllowedToUseExtension(
ExtensionID::SPV_KHR_uniform_group_instructions),
SPIRVEC_RequiresExtension, "SPV_KHR_uniform_group_instructions\n");
BM->setName(BF, F->getName().str());
}
}
if (!isKernel(F) && F->getLinkage() != GlobalValue::InternalLinkage)
BF->setLinkageType(transLinkageType(F));
Expand Down Expand Up @@ -5728,7 +5734,7 @@ void LLVMToSPIRVBase::transFunction(Function *I) {

if (isKernel(I)) {
auto Interface = collectEntryPointInterfaces(BF, I);
BM->addEntryPoint(ExecutionModelKernel, BF->getId(), I->getName().str(),
BM->addEntryPoint(ExecutionModelKernel, BF->getId(), BF->getName(),
Interface);
}
}
Expand Down Expand Up @@ -6095,8 +6101,9 @@ bool LLVMToSPIRVBase::transMetadata() {
// Work around to translate kernel_arg_type and kernel_arg_type_qual metadata
static void transKernelArgTypeMD(SPIRVModule *BM, Function *F, MDNode *MD,
std::string MDName) {
std::string KernelArgTypesMDStr =
std::string(MDName) + "." + F->getName().str() + ".";
std::string Prefix = kSPIRVName::EntrypointPrefix;
std::string Name = F->getName().str().substr(Prefix.size());
std::string KernelArgTypesMDStr = std::string(MDName) + "." + Name + ".";
for (const auto &TyOp : MD->operands())
KernelArgTypesMDStr += cast<MDString>(TyOp)->getString().str() + ",";
BM->getString(KernelArgTypesMDStr);
Expand Down
21 changes: 21 additions & 0 deletions llvm-spirv/test/entry_point_func.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
;; Test to check that an LLVM spir_kernel gets translated into an
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test looks a bit different in the upstream Khronos Translator. Can you please check?
https://github.com/KhronosGroup/SPIRV-LLVM-Translator/blob/main/test/entry_point_func.ll

Thanks

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems that it was the initial version of the test. That led to the thing that we did not apply the changes from the following commit. Will update the test, thanks for the catch!

;; Entrypoint wrapper and Function with LinkageAttributes
; RUN: llvm-as %s -o %t.bc
; RUN: llvm-spirv %t.bc -o - -spirv-text | FileCheck %s --check-prefix=CHECK-SPIRV
; RUN: llvm-spirv %t.bc -o %t.spv
; RUN: spirv-val %t.spv

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"

define spir_kernel void @testfunction() {
ret void
}

; Check there is an entrypoint and a function produced.
; CHECK-SPIRV: EntryPoint 6 [[EP:[0-9]+]] "testfunction"
; CHECK-SPIRV: Name [[FUNC:[0-9]+]] "testfunction"
; CHECK-SPIRV: Decorate [[FUNC]] LinkageAttributes "testfunction" Export
; CHECK-SPIRV: Function 2 [[FUNC]] 0 3
; CHECK-SPIRV: Function 2 [[EP]] 0 3
; CHECK-SPIRV: FunctionCall 2 8 [[FUNC]]
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
; RUN: llvm-spirv --spirv-ext=+SPV_INTEL_cache_controls %t.bc -o %t.spv
; RUN: llvm-spirv -r %t.spv --spirv-target-env=SPV-IR -o - | llvm-dis -o - | FileCheck %s --check-prefix=CHECK-LLVM

; CHECK-SPIRV-DAG: EntryPoint [[#]] [[#Func:]] "test"
; CHECK-SPIRV-DAG: EntryPoint [[#]] [[#FuncGEP:]] "test_gep"
; CHECK-SPIRV-DAG: Name [[#Func:]] "test"
; CHECK-SPIRV-DAG: Name [[#FuncGEP:]] "test_gep"
; CHECK-SPIRV-DAG: TypeInt [[#Int32:]] 32 0
; CHECK-SPIRV-DAG: Constant [[#Int32]] [[#Zero:]] 0
; CHECK-SPIRV-DAG: Decorate [[#GEP1:]] CacheControlLoadINTEL 1 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ entry:
; CHECK-SPIRV: Capability FPGAArgumentInterfacesINTEL
; CHECK-SPIRV: Extension "SPV_INTEL_fpga_argument_interfaces"
; CHECK-SPIRV: Extension "SPV_INTEL_fpga_buffer_location"
; CHECK-SPIRV-DAG: Name [[IDS:[0-9]+]] "_arg_p"
; CHECK-SPIRV-DAG: Name [[ID:[0-9]+]] "_arg_p"
; CHECK-SPIRV: Decorate [[ID]] Alignment 4
; CHECK-SPIRV: Decorate [[ID]] MMHostInterfaceAddressWidthINTEL 32
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
; XFAIL: *

AlexeySachkov marked this conversation as resolved.
Show resolved Hide resolved
; RUN: llvm-as %s -o %t.bc
; RUN: llvm-spirv -spirv-ext=+SPV_INTEL_function_pointers -spirv-text %t.bc -o - | FileCheck %s --check-prefix=CHECK-SPIRV
; RUN: llvm-spirv -spirv-ext=+SPV_INTEL_function_pointers %t.bc -o %t.spv
Expand All @@ -12,7 +10,7 @@ target triple = "spir64-unknown-unknown"
; when used since they can't be translated directly.

; CHECK-SPIRV-DAG: Name [[#FOO:]] "foo"
; CHECK-SPIRV-DAG: EntryPoint [[#]] [[#BAR:]] "bar"
; CHECK-SPIRV-DAG: Name [[#BAR:]] "bar"
; CHECK-SPIRV-DAG: Name [[#Y:]] "y"
; CHECK-SPIRV-DAG: Name [[#FOOPTR:]] "foo.alias"
; CHECK-SPIRV-DAG: Decorate [[#FOO]] LinkageAttributes "foo" Export
Expand All @@ -34,7 +32,7 @@ target triple = "spir64-unknown-unknown"

; CHECK-LLVM: define spir_func i32 @foo(i32 %x)

; CHECK-LLVM: define spir_func void @bar(ptr %y)
; CHECK-LLVM: define spir_kernel void @bar(ptr %y)
; CHECK-LLVM: [[PTRTOINT:%.*]] = ptrtoint ptr @foo to i64
; CHECK-LLVM: store i64 [[PTRTOINT]], ptr %y, align 8

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
; CHECK-SPIRV: Capability FunctionPointersINTEL
; CHECK-SPIRV: Extension "SPV_INTEL_function_pointers"
;
; CHECK-SPIRV: EntryPoint [[#]] [[KERNEL_ID:[0-9]+]] "test"
; CHECK-SPIRV: Name [[KERNEL_ID:[0-9]+]] "test"
; CHECK-SPIRV: TypeInt [[INT32_TYPE_ID:[0-9]+]] 32
; CHECK-SPIRV: TypePointer [[INT_PTR:[0-9]+]] 5 [[INT32_TYPE_ID]]
; CHECK-SPIRV: TypeFunction [[FOO_TYPE_ID:[0-9]+]] [[INT32_TYPE_ID]] [[INT32_TYPE_ID]]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
; CHECK-SPIRV: Capability FunctionPointersINTEL
; CHECK-SPIRV: Extension "SPV_INTEL_function_pointers"
;
; CHECK-SPIRV: EntryPoint [[#]] [[KERNEL_ID:[0-9]+]] "test"
; CHECK-SPIRV: Name [[KERNEL_ID:[0-9]+]] "test"
; CHECK-SPIRV: TypeInt [[TYPE_INT32_ID:[0-9]+]] 32
; CHECK-SPIRV: TypeFunction [[FOO_TYPE_ID:[0-9]+]] [[TYPE_INT32_ID]] [[TYPE_INT32_ID]]
; CHECK-SPIRV: TypePointer [[FOO_PTR_TYPE_ID:[0-9]+]] {{[0-9]+}} [[FOO_TYPE_ID]]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
;
; CHECK-SPIRV: Capability FunctionPointersINTEL
; CHECK-SPIRV: Extension "SPV_INTEL_function_pointers"
; CHECK-SPIRV: EntryPoint [[#]] [[KERNEL_ID:[0-9]+]] "test"
; CHECK-SPIRV: Name [[KERNEL_ID:[0-9]+]] "test"
; CHECK-SPIRV: TypeInt [[TYPE_INT_ID:[0-9]+]]
; CHECK-SPIRV: TypeFunction [[FOO_TYPE_ID:[0-9]+]] [[TYPE_INT_ID]] [[TYPE_INT_ID]]
; CHECK-SPIRV: TypePointer [[FOO_PTR_ID:[0-9]+]] {{[0-9]+}} [[FOO_TYPE_ID]]
Expand Down
Loading
Loading