Skip to content

Commit

Permalink
Introduce CodeSectionINTEL storage class (#2728)
Browse files Browse the repository at this point in the history
This storage class is used for function pointers. It's added as based on cl_intel_function_pointers specification, it is not guaranteed that sizeof(void(*)(void) == sizeof(void *) - to allow consumers use this fact, we cannot say that function pointer belongs to the same storage class as data pointers.

It wasn't added during initial implementation, now it's time to fill this gap. As it would be a breaking change its generation is added only under -spirv-emit-function-ptr-addr-space option. Also SPIR-V consumer may pass this option during reverse translation to get new address space even in a case, when OpConstantFunctionPointerINTEL doesn't reside in CodeSectionINTEL storage class.

Expected behavior:

No option is passed to the forward translation stage and function pointers are in addrspace(9):
no CodeSectionINTEL storage class in SPIR-V
The option is passed to the forward translation stage and function pointers are in addrepace(9):
CodeSectionINTEL storage class is generated
No option is passed to the reverse translation stage: function pointers are in private address space
The option is passed to the reverse translation stage: function pointers are in addrspace(9)
Spec:
https://github.com/intel/llvm/blob/sycl/sycl/doc/design/spirv-extensions/SPV_INTEL_function_pointers.asciidoc

The previous approach: #1392
  • Loading branch information
MrSidims authored Sep 27, 2024
1 parent 630a90a commit 46c9eb9
Show file tree
Hide file tree
Showing 24 changed files with 1,311 additions and 9 deletions.
12 changes: 12 additions & 0 deletions include/LLVMSPIRVOpts.h
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,14 @@ class TranslatorOpts {
PreserveOCLKernelArgTypeMetadataThroughString = Value;
}

bool shouldEmitFunctionPtrAddrSpace() const noexcept {
return EmitFunctionPtrAddrSpace;
}

void setEmitFunctionPtrAddrSpace(bool Value) noexcept {
EmitFunctionPtrAddrSpace = Value;
}

void setBuiltinFormat(BuiltinFormat Value) noexcept {
SPIRVBuiltinFormat = Value;
}
Expand Down Expand Up @@ -287,6 +295,10 @@ class TranslatorOpts {
// kernel_arg_type_qual metadata through OpString
bool PreserveOCLKernelArgTypeMetadataThroughString = false;

// Controls if CodeSectionINTEL can be emitted and consumed with a dedicated
// address space
bool EmitFunctionPtrAddrSpace = false;

bool PreserveAuxData = false;

BuiltinFormat SPIRVBuiltinFormat = BuiltinFormat::Function;
Expand Down
4 changes: 4 additions & 0 deletions lib/SPIRV/SPIRVInternal.h
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ enum SPIRAddressSpace {
SPIRAS_GlobalHost,
SPIRAS_Input,
SPIRAS_Output,
SPIRAS_CodeSectionINTEL,
SPIRAS_Count,
};

Expand All @@ -199,6 +200,8 @@ template <> inline void SPIRVMap<SPIRAddressSpace, std::string>::init() {
add(SPIRAS_Local, "Local");
add(SPIRAS_Generic, "Generic");
add(SPIRAS_Input, "Input");
add(SPIRAS_CodeSectionINTEL, "CodeSectionINTEL");

add(SPIRAS_GlobalDevice, "GlobalDevice");
add(SPIRAS_GlobalHost, "GlobalHost");
}
Expand All @@ -215,6 +218,7 @@ inline void SPIRVMap<SPIRAddressSpace, SPIRVStorageClassKind>::init() {
add(SPIRAS_Input, StorageClassInput);
add(SPIRAS_GlobalDevice, StorageClassDeviceOnlyINTEL);
add(SPIRAS_GlobalHost, StorageClassHostOnlyINTEL);
add(SPIRAS_CodeSectionINTEL, StorageClassCodeSectionINTEL);
}
typedef SPIRVMap<SPIRAddressSpace, SPIRVStorageClassKind> SPIRSPIRVAddrSpaceMap;

Expand Down
51 changes: 43 additions & 8 deletions lib/SPIRV/SPIRVReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -348,16 +348,21 @@ Type *SPIRVToLLVM::transType(SPIRVType *T, bool UseTPT) {
case internal::OpTypeTokenINTEL:
return mapType(T, Type::getTokenTy(*Context));
case OpTypePointer: {
const unsigned AS =
SPIRSPIRVAddrSpaceMap::rmap(T->getPointerStorageClass());
unsigned AS = SPIRSPIRVAddrSpaceMap::rmap(T->getPointerStorageClass());
if (AS == SPIRAS_CodeSectionINTEL && !BM->shouldEmitFunctionPtrAddrSpace())
AS = SPIRAS_Private;
if (BM->shouldEmitFunctionPtrAddrSpace() &&
T->getPointerElementType()->getOpCode() == OpTypeFunction)
AS = SPIRAS_CodeSectionINTEL;
Type *ElementTy = transType(T->getPointerElementType(), UseTPT);
if (UseTPT)
return TypedPointerType::get(ElementTy, AS);
return mapType(T, PointerType::get(ElementTy, AS));
}
case OpTypeUntypedPointerKHR: {
const unsigned AS =
SPIRSPIRVAddrSpaceMap::rmap(T->getPointerStorageClass());
unsigned AS = SPIRSPIRVAddrSpaceMap::rmap(T->getPointerStorageClass());
if (AS == SPIRAS_CodeSectionINTEL && !BM->shouldEmitFunctionPtrAddrSpace())
AS = SPIRAS_Private;
return mapType(T, PointerType::get(*Context, AS));
}
case OpTypeVector:
Expand Down Expand Up @@ -1469,6 +1474,17 @@ Value *SPIRVToLLVM::transValueWithoutDecoration(SPIRVValue *BV, Function *F,
case OpTypeMatrix:
case OpTypeArray: {
auto *AT = cast<ArrayType>(transType(BCC->getType()));
for (size_t I = 0; I != AT->getNumElements(); ++I) {
auto *ElemTy = AT->getElementType();
if (auto *ElemPtrTy = dyn_cast<PointerType>(ElemTy)) {
assert(isa<PointerType>(CV[I]->getType()) &&
"Constant type doesn't match constexpr array element type");
if (ElemPtrTy->getAddressSpace() !=
cast<PointerType>(CV[I]->getType())->getAddressSpace())
CV[I] = ConstantExpr::getAddrSpaceCast(CV[I], AT->getElementType());
}
}

return mapValue(BV, ConstantArray::get(AT, CV));
}
case OpTypeStruct: {
Expand All @@ -1485,7 +1501,12 @@ Value *SPIRVToLLVM::transValueWithoutDecoration(SPIRVValue *BV, Function *F,
!BCCTy->getElementType(I)->isPointerTy())
continue;

CV[I] = ConstantExpr::getBitCast(CV[I], BCCTy->getElementType(I));
if (cast<PointerType>(CV[I]->getType())->getAddressSpace() !=
cast<PointerType>(BCCTy->getElementType(I))->getAddressSpace())
CV[I] =
ConstantExpr::getAddrSpaceCast(CV[I], BCCTy->getElementType(I));
else
CV[I] = ConstantExpr::getBitCast(CV[I], BCCTy->getElementType(I));
}
}

Expand Down Expand Up @@ -1521,7 +1542,10 @@ Value *SPIRVToLLVM::transValueWithoutDecoration(SPIRVValue *BV, Function *F,
static_cast<SPIRVConstantFunctionPointerINTEL *>(BV);
SPIRVFunction *F = BC->getFunction();
BV->setName(F->getName());
return mapValue(BV, transFunction(F));
const unsigned AS = BM->shouldEmitFunctionPtrAddrSpace()
? SPIRAS_CodeSectionINTEL
: SPIRAS_Private;
return mapValue(BV, transFunction(F, AS));
}

case OpUndef:
Expand Down Expand Up @@ -3046,7 +3070,7 @@ void SPIRVToLLVM::transFunctionAttrs(SPIRVFunction *BF, Function *F) {
});
}

Function *SPIRVToLLVM::transFunction(SPIRVFunction *BF) {
Function *SPIRVToLLVM::transFunction(SPIRVFunction *BF, unsigned AS) {
auto Loc = FuncMap.find(BF);
if (Loc != FuncMap.end())
return Loc->second;
Expand Down Expand Up @@ -3094,7 +3118,7 @@ Function *SPIRVToLLVM::transFunction(SPIRVFunction *BF) {
}
Function *F = M->getFunction(FuncName);
if (!F)
F = Function::Create(FT, Linkage, FuncName, M);
F = Function::Create(FT, Linkage, AS, FuncName, M);
F = cast<Function>(mapValue(BF, F));
mapFunction(BF, F);

Expand Down Expand Up @@ -3501,6 +3525,17 @@ bool SPIRVToLLVM::translate() {
DbgTran->transDebugInst(EI);
}

for (auto *FP : BM->getFunctionPointers()) {
SPIRVConstantFunctionPointerINTEL *BC =
static_cast<SPIRVConstantFunctionPointerINTEL *>(FP);
SPIRVFunction *F = BC->getFunction();
FP->setName(F->getName());
const unsigned AS = BM->shouldEmitFunctionPtrAddrSpace()
? SPIRAS_CodeSectionINTEL
: SPIRAS_Private;
mapValue(FP, transFunction(F, AS));
}

for (unsigned I = 0, E = BM->getNumFunctions(); I != E; ++I) {
transFunction(BM->getFunction(I));
transUserSemantic(BM->getFunction(I));
Expand Down
2 changes: 1 addition & 1 deletion lib/SPIRV/SPIRVReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ class SPIRVToLLVM : private BuiltinCallHelper {
void transAuxDataInst(SPIRVExtInst *BC);
std::vector<Value *> transValue(const std::vector<SPIRVValue *> &,
Function *F, BasicBlock *);
Function *transFunction(SPIRVFunction *F);
Function *transFunction(SPIRVFunction *F, unsigned AS = SPIRAS_Private);
void transFunctionAttrs(SPIRVFunction *BF, Function *F);
Value *transBlockInvoke(SPIRVValue *Invoke, BasicBlock *BB);
Instruction *transWGSizeQueryBI(SPIRVInstruction *BI, BasicBlock *BB);
Expand Down
8 changes: 8 additions & 0 deletions lib/SPIRV/SPIRVWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -655,6 +655,11 @@ SPIRVType *LLVMToSPIRVBase::transPointerType(Type *ET, unsigned AddrSpc) {
((AddrSpc == SPIRAS_GlobalDevice) || (AddrSpc == SPIRAS_GlobalHost))) {
return transPointerType(ET, SPIRAS_Global);
}
// Lower function pointer address space to private if
// spirv-emit-function-ptr-addr-space is not passed
if (AddrSpc == SPIRAS_CodeSectionINTEL &&
!BM->shouldEmitFunctionPtrAddrSpace())
return transPointerType(ET, SPIRAS_Private);
if (ST && !ST->isSized()) {
Op OpCode;
StringRef STName = ST->getName();
Expand Down Expand Up @@ -750,6 +755,9 @@ SPIRVType *LLVMToSPIRVBase::transPointerType(SPIRVType *ET, unsigned AddrSpc) {
return Loc->second;

SPIRVType *TranslatedTy = nullptr;
if (AddrSpc == SPIRAS_CodeSectionINTEL &&
!BM->shouldEmitFunctionPtrAddrSpace())
return transPointerType(ET, SPIRAS_Private);
if (BM->isAllowedToUseExtension(ExtensionID::SPV_KHR_untyped_pointers) &&
!(ET->isTypeArray() || ET->isTypeVector() || ET->isTypeStruct() ||
ET->isTypeImage() || ET->isTypeSampler() || ET->isTypePipe())) {
Expand Down
5 changes: 5 additions & 0 deletions lib/SPIRV/libSPIRV/SPIRVInstruction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,11 @@ SPIRVInstruction *createInstFromSpecConstantOp(SPIRVSpecConstantOp *Inst) {
auto OC = static_cast<Op>(Ops[0]);
assert(isSpecConstantOpAllowedOp(OC) &&
"Op code not allowed for OpSpecConstantOp");
auto *Const = Inst->getOperand(1);
// LLVM would eliminate a bitcast from a function pointer in a constexpr
// context. Cut this short here to avoid necessity to align address spaces
if (OC == OpBitcast && Const->getOpCode() == OpConstantFunctionPointerINTEL)
return static_cast<SPIRVInstruction *>(Const);
Ops.erase(Ops.begin(), Ops.begin() + 1);
auto *BM = Inst->getModule();
auto *RetInst = SPIRVInstTemplateBase::create(
Expand Down
7 changes: 7 additions & 0 deletions lib/SPIRV/libSPIRV/SPIRVModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,13 @@ class SPIRVModuleImpl : public SPIRVModule {
SPIRVConstant *getLiteralAsConstant(unsigned Literal) override;
unsigned getNumFunctions() const override { return FuncVec.size(); }
unsigned getNumVariables() const override { return VariableVec.size(); }
std::vector<SPIRVValue *> getFunctionPointers() const override {
std::vector<SPIRVValue *> Res;
for (auto *C : ConstVec)
if (C->getOpCode() == OpConstantFunctionPointerINTEL)
Res.emplace_back(C);
return Res;
}
SourceLanguage getSourceLanguage(SPIRVWord *Ver = nullptr) const override {
if (Ver)
*Ver = SrcLangVer;
Expand Down
5 changes: 5 additions & 0 deletions lib/SPIRV/libSPIRV/SPIRVModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ class SPIRVModule {
virtual SPIRVMemoryModelKind getMemoryModel() const = 0;
virtual unsigned getNumFunctions() const = 0;
virtual unsigned getNumVariables() const = 0;
virtual std::vector<SPIRVValue *> getFunctionPointers() const = 0;
virtual SourceLanguage getSourceLanguage(SPIRVWord *) const = 0;
virtual std::set<std::string> &getSourceExtension() = 0;
virtual SPIRVValue *getValue(SPIRVId TheId) const = 0;
Expand Down Expand Up @@ -554,6 +555,10 @@ class SPIRVModule {
.shouldPreserveOCLKernelArgTypeMetadataThroughString();
}

bool shouldEmitFunctionPtrAddrSpace() const noexcept {
return TranslationOpts.shouldEmitFunctionPtrAddrSpace();
}

bool preserveAuxData() const noexcept {
return TranslationOpts.preserveAuxData();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
; 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
; RUN: llvm-spirv -r -spirv-emit-function-ptr-addr-space %t.spv -o - | llvm-dis -o - | FileCheck %s --check-prefix=CHECK-LLVM

target datalayout = "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024"
target triple = "spir64-unknown-unknown"

; Check that aliases are dereferenced and translated to their aliasee values
; when used since they can't be translated directly.

; CHECK-SPIRV-DAG: Name [[#FOO:]] "foo"
; 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
; CHECK-SPIRV-DAG: Decorate [[#BAR]] LinkageAttributes "bar" Export
; CHECK-SPIRV-DAG: TypeInt [[#I32:]] 32 0
; CHECK-SPIRV-DAG: TypeInt [[#I64:]] 64 0
; CHECK-SPIRV-DAG: TypeFunction [[#FOO_TYPE:]] [[#I32]] [[#I32]]
; CHECK-SPIRV-DAG: TypeVoid [[#VOID:]]
; CHECK-SPIRV-DAG: TypePointer [[#I64PTR:]] 7 [[#I64]]
; CHECK-SPIRV-DAG: TypeFunction [[#BAR_TYPE:]] [[#VOID]] [[#I64PTR]]
; CHECK-SPIRV-DAG: TypePointer [[#FOOPTR_TYPE:]] 7 [[#FOO_TYPE]]
; CHECK-SPIRV-DAG: ConstantFunctionPointerINTEL [[#FOOPTR_TYPE]] [[#FOOPTR]] [[#FOO]]

; CHECK-SPIRV: Function [[#I32]] [[#FOO]] 0 [[#FOO_TYPE]]

; CHECK-SPIRV: Function [[#VOID]] [[#BAR]] 0 [[#BAR_TYPE]]
; CHECK-SPIRV: FunctionParameter [[#I64PTR]] [[#Y]]
; CHECK-SPIRV: ConvertPtrToU [[#I64]] [[#PTRTOINT:]] [[#FOOPTR]]
; CHECK-SPIRV: Store [[#Y]] [[#PTRTOINT]] 2 8

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

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

define spir_func i32 @foo(i32 %x) {
ret i32 %x
}

@foo.alias = internal alias i32 (i32), ptr @foo

define spir_kernel void @bar(ptr %y) {
store i64 ptrtoint (ptr @foo.alias to i64), ptr %y
ret void
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
; OpenCL C source:
; char foo(char a) {
; return a;
; }
; void bar() {
; int (*fun_ptr)(int) = &foo;
; fun_ptr(0);
; }

; RUN: llvm-as %s -o %t.bc
; RUN: llvm-spirv %t.bc -spirv-ext=+SPV_INTEL_function_pointers -o %t.spv
; RUN: llvm-spirv %t.spv -to-text -o %t.spt
; RUN: FileCheck < %t.spt %s --check-prefix=CHECK-SPIRV
; RUN: llvm-spirv -r -spirv-emit-function-ptr-addr-space %t.spv -o %t.r.bc
; RUN: llvm-dis %t.r.bc -o %t.r.ll
; RUN: FileCheck < %t.r.ll %s --check-prefix=CHECK-LLVM

; CHECK-SPIRV-DAG: TypeInt [[#I8:]] 8
; CHECK-SPIRV-DAG: TypeInt [[#I32:]] 32
; CHECK-SPIRV-DAG: TypeFunction [[#FOO_TY:]] [[#I8]] [[#I8]]
; CHECK-SPIRV-DAG: TypeFunction [[#DEST_TY:]] [[#I32]] [[#I32]]
; CHECK-SPIRV-DAG: TypePointer [[#DEST_TY_PTR:]] [[#]] [[#DEST_TY]]
; CHECK-SPIRV-DAG: TypePointer [[#FOO_TY_PTR:]] [[#]] [[#FOO_TY]]
; CHECK-SPIRV: ConstantFunctionPointerINTEL [[#FOO_TY_PTR]] [[#FOO_PTR:]] [[#FOO:]]
; CHECK-SPIRV: Function [[#]] [[#FOO]] [[#]] [[#FOO_TY]]

; CHECK-SPIRV: Bitcast [[#DEST_TY_PTR]] [[#]] [[#FOO_PTR]]

; CHECK-LLVM: bitcast ptr addrspace(9) @foo to ptr addrspace(9)

; ModuleID = './example.c'
source_filename = "./example.c"
target datalayout = "e-p:32:32-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024"
target triple = "spir"

; Function Attrs: noinline nounwind optnone
define dso_local spir_func signext i8 @foo(i8 signext %0) #0 {
ret i8 %0
}

; Function Attrs: noinline nounwind optnone
define dso_local spir_func void @bar() #0 {
%1 = call i32 @foo(i32 0)
ret void
}

attributes #0 = { noinline nounwind optnone "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }

!llvm.module.flags = !{!0}
!llvm.ident = !{!1}

!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{!"clang version 11.0.0 (https://github.com/llvm/llvm-project.git 0e1accd0f726eef2c47be9f37dd0a06cb50d207e)"}
Loading

0 comments on commit 46c9eb9

Please sign in to comment.