Skip to content

Commit 2818bde

Browse files
MrSidimsmshelego
authored andcommitted
[Backport to 16] Introduce CodeSectionINTEL storage class (KhronosGroup#2728)
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: KhronosGroup#1392
1 parent 3dc0eee commit 2818bde

22 files changed

+1204
-7
lines changed

include/LLVMSPIRVOpts.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,14 @@ class TranslatorOpts {
221221
PreserveOCLKernelArgTypeMetadataThroughString = Value;
222222
}
223223

224+
bool shouldEmitFunctionPtrAddrSpace() const noexcept {
225+
return EmitFunctionPtrAddrSpace;
226+
}
227+
228+
void setEmitFunctionPtrAddrSpace(bool Value) noexcept {
229+
EmitFunctionPtrAddrSpace = Value;
230+
}
231+
224232
private:
225233
// Common translation options
226234
VersionNumber MaxVersion = VersionNumber::MaximumVersion;
@@ -262,6 +270,10 @@ class TranslatorOpts {
262270
// kernel_arg_type_qual metadata through OpString
263271
bool PreserveOCLKernelArgTypeMetadataThroughString = false;
264272

273+
// Controls if CodeSectionINTEL can be emitted and consumed with a dedicated
274+
// address space
275+
bool EmitFunctionPtrAddrSpace = false;
276+
265277
bool PreserveAuxData = false;
266278
};
267279

lib/SPIRV/SPIRVInternal.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ enum SPIRAddressSpace {
189189
SPIRAS_GlobalHost,
190190
SPIRAS_Input,
191191
SPIRAS_Output,
192+
SPIRAS_CodeSectionINTEL,
192193
SPIRAS_Count,
193194
};
194195

@@ -199,6 +200,8 @@ template <> inline void SPIRVMap<SPIRAddressSpace, std::string>::init() {
199200
add(SPIRAS_Local, "Local");
200201
add(SPIRAS_Generic, "Generic");
201202
add(SPIRAS_Input, "Input");
203+
add(SPIRAS_CodeSectionINTEL, "CodeSectionINTEL");
204+
202205
add(SPIRAS_GlobalDevice, "GlobalDevice");
203206
add(SPIRAS_GlobalHost, "GlobalHost");
204207
}
@@ -215,6 +218,7 @@ inline void SPIRVMap<SPIRAddressSpace, SPIRVStorageClassKind>::init() {
215218
add(SPIRAS_Input, StorageClassInput);
216219
add(SPIRAS_GlobalDevice, StorageClassDeviceOnlyINTEL);
217220
add(SPIRAS_GlobalHost, StorageClassHostOnlyINTEL);
221+
add(SPIRAS_CodeSectionINTEL, StorageClassCodeSectionINTEL);
218222
}
219223
typedef SPIRVMap<SPIRAddressSpace, SPIRVStorageClassKind> SPIRSPIRVAddrSpaceMap;
220224

lib/SPIRV/SPIRVReader.cpp

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -344,8 +344,12 @@ Type *SPIRVToLLVM::transType(SPIRVType *T, bool UseTPT) {
344344
case internal::OpTypeTokenINTEL:
345345
return mapType(T, Type::getTokenTy(*Context));
346346
case OpTypePointer: {
347-
const unsigned AS =
348-
SPIRSPIRVAddrSpaceMap::rmap(T->getPointerStorageClass());
347+
unsigned AS = SPIRSPIRVAddrSpaceMap::rmap(T->getPointerStorageClass());
348+
if (AS == SPIRAS_CodeSectionINTEL && !BM->shouldEmitFunctionPtrAddrSpace())
349+
AS = SPIRAS_Private;
350+
if (BM->shouldEmitFunctionPtrAddrSpace() &&
351+
T->getPointerElementType()->getOpCode() == OpTypeFunction)
352+
AS = SPIRAS_CodeSectionINTEL;
349353
Type *ElementTy = transType(T->getPointerElementType(), UseTPT);
350354
if (UseTPT)
351355
return TypedPointerType::get(ElementTy, AS);
@@ -1443,6 +1447,17 @@ Value *SPIRVToLLVM::transValueWithoutDecoration(SPIRVValue *BV, Function *F,
14431447
case OpTypeMatrix:
14441448
case OpTypeArray: {
14451449
auto *AT = cast<ArrayType>(transType(BCC->getType()));
1450+
for (size_t I = 0; I != AT->getNumElements(); ++I) {
1451+
auto *ElemTy = AT->getElementType();
1452+
if (auto *ElemPtrTy = dyn_cast<PointerType>(ElemTy)) {
1453+
assert(isa<PointerType>(CV[I]->getType()) &&
1454+
"Constant type doesn't match constexpr array element type");
1455+
if (ElemPtrTy->getAddressSpace() !=
1456+
cast<PointerType>(CV[I]->getType())->getAddressSpace())
1457+
CV[I] = ConstantExpr::getAddrSpaceCast(CV[I], AT->getElementType());
1458+
}
1459+
}
1460+
14461461
return mapValue(BV, ConstantArray::get(AT, CV));
14471462
}
14481463
case OpTypeStruct: {
@@ -1459,7 +1474,12 @@ Value *SPIRVToLLVM::transValueWithoutDecoration(SPIRVValue *BV, Function *F,
14591474
!BCCTy->getElementType(I)->isPointerTy())
14601475
continue;
14611476

1462-
CV[I] = ConstantExpr::getBitCast(CV[I], BCCTy->getElementType(I));
1477+
if (cast<PointerType>(CV[I]->getType())->getAddressSpace() !=
1478+
cast<PointerType>(BCCTy->getElementType(I))->getAddressSpace())
1479+
CV[I] =
1480+
ConstantExpr::getAddrSpaceCast(CV[I], BCCTy->getElementType(I));
1481+
else
1482+
CV[I] = ConstantExpr::getBitCast(CV[I], BCCTy->getElementType(I));
14631483
}
14641484
}
14651485

@@ -1495,7 +1515,10 @@ Value *SPIRVToLLVM::transValueWithoutDecoration(SPIRVValue *BV, Function *F,
14951515
static_cast<SPIRVConstantFunctionPointerINTEL *>(BV);
14961516
SPIRVFunction *F = BC->getFunction();
14971517
BV->setName(F->getName());
1498-
return mapValue(BV, transFunction(F));
1518+
const unsigned AS = BM->shouldEmitFunctionPtrAddrSpace()
1519+
? SPIRAS_CodeSectionINTEL
1520+
: SPIRAS_Private;
1521+
return mapValue(BV, transFunction(F, AS));
14991522
}
15001523

15011524
case OpUndef:
@@ -2936,7 +2959,7 @@ void SPIRVToLLVM::transFunctionAttrs(SPIRVFunction *BF, Function *F) {
29362959
});
29372960
}
29382961

2939-
Function *SPIRVToLLVM::transFunction(SPIRVFunction *BF) {
2962+
Function *SPIRVToLLVM::transFunction(SPIRVFunction *BF, unsigned AS) {
29402963
auto Loc = FuncMap.find(BF);
29412964
if (Loc != FuncMap.end())
29422965
return Loc->second;
@@ -2984,7 +3007,7 @@ Function *SPIRVToLLVM::transFunction(SPIRVFunction *BF) {
29843007
}
29853008
Function *F = M->getFunction(FuncName);
29863009
if (!F)
2987-
F = Function::Create(FT, Linkage, FuncName, M);
3010+
F = Function::Create(FT, Linkage, AS, FuncName, M);
29883011
F = cast<Function>(mapValue(BF, F));
29893012
mapFunction(BF, F);
29903013

@@ -3377,6 +3400,17 @@ bool SPIRVToLLVM::translate() {
33773400
DbgTran->transDebugInst(EI);
33783401
}
33793402

3403+
for (auto *FP : BM->getFunctionPointers()) {
3404+
SPIRVConstantFunctionPointerINTEL *BC =
3405+
static_cast<SPIRVConstantFunctionPointerINTEL *>(FP);
3406+
SPIRVFunction *F = BC->getFunction();
3407+
FP->setName(F->getName());
3408+
const unsigned AS = BM->shouldEmitFunctionPtrAddrSpace()
3409+
? SPIRAS_CodeSectionINTEL
3410+
: SPIRAS_Private;
3411+
mapValue(FP, transFunction(F, AS));
3412+
}
3413+
33803414
for (unsigned I = 0, E = BM->getNumFunctions(); I != E; ++I) {
33813415
transFunction(BM->getFunction(I));
33823416
transUserSemantic(BM->getFunction(I));

lib/SPIRV/SPIRVReader.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ class SPIRVToLLVM : private BuiltinCallHelper {
103103
void transAuxDataInst(SPIRVExtInst *BC);
104104
std::vector<Value *> transValue(const std::vector<SPIRVValue *> &,
105105
Function *F, BasicBlock *);
106-
Function *transFunction(SPIRVFunction *F);
106+
Function *transFunction(SPIRVFunction *F, unsigned AS = SPIRAS_Private);
107107
void transFunctionAttrs(SPIRVFunction *BF, Function *F);
108108
Value *transBlockInvoke(SPIRVValue *Invoke, BasicBlock *BB);
109109
Instruction *transWGSizeQueryBI(SPIRVInstruction *BI, BasicBlock *BB);

lib/SPIRV/SPIRVWriter.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,11 @@ SPIRVType *LLVMToSPIRVBase::transPointerType(Type *ET, unsigned AddrSpc) {
585585
((AddrSpc == SPIRAS_GlobalDevice) || (AddrSpc == SPIRAS_GlobalHost))) {
586586
return transPointerType(ET, SPIRAS_Global);
587587
}
588+
// Lower function pointer address space to private if
589+
// spirv-emit-function-ptr-addr-space is not passed
590+
if (AddrSpc == SPIRAS_CodeSectionINTEL &&
591+
!BM->shouldEmitFunctionPtrAddrSpace())
592+
return transPointerType(ET, SPIRAS_Private);
588593
if (ST && !ST->isSized()) {
589594
Op OpCode;
590595
StringRef STName = ST->getName();
@@ -685,6 +690,10 @@ SPIRVType *LLVMToSPIRVBase::transPointerType(SPIRVType *ET, unsigned AddrSpc) {
685690
if (Loc != PointeeTypeMap.end())
686691
return Loc->second;
687692

693+
if (AddrSpc == SPIRAS_CodeSectionINTEL &&
694+
!BM->shouldEmitFunctionPtrAddrSpace())
695+
return transPointerType(ET, SPIRAS_Private);
696+
688697
SPIRVType *TranslatedTy = BM->addPointerType(
689698
SPIRSPIRVAddrSpaceMap::map(static_cast<SPIRAddressSpace>(AddrSpc)), ET);
690699
PointeeTypeMap[TypeKey] = TranslatedTy;

lib/SPIRV/libSPIRV/SPIRVInstruction.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,11 @@ SPIRVInstruction *createInstFromSpecConstantOp(SPIRVSpecConstantOp *Inst) {
286286
auto OC = static_cast<Op>(Ops[0]);
287287
assert(isSpecConstantOpAllowedOp(OC) &&
288288
"Op code not allowed for OpSpecConstantOp");
289+
auto *Const = Inst->getOperand(1);
290+
// LLVM would eliminate a bitcast from a function pointer in a constexpr
291+
// context. Cut this short here to avoid necessity to align address spaces
292+
if (OC == OpBitcast && Const->getOpCode() == OpConstantFunctionPointerINTEL)
293+
return static_cast<SPIRVInstruction *>(Const);
289294
Ops.erase(Ops.begin(), Ops.begin() + 1);
290295
auto *BM = Inst->getModule();
291296
auto *RetInst = SPIRVInstTemplateBase::create(

lib/SPIRV/libSPIRV/SPIRVModule.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,13 @@ class SPIRVModuleImpl : public SPIRVModule {
168168
SPIRVConstant *getLiteralAsConstant(unsigned Literal) override;
169169
unsigned getNumFunctions() const override { return FuncVec.size(); }
170170
unsigned getNumVariables() const override { return VariableVec.size(); }
171+
std::vector<SPIRVValue *> getFunctionPointers() const override {
172+
std::vector<SPIRVValue *> Res;
173+
for (auto *C : ConstVec)
174+
if (C->getOpCode() == OpConstantFunctionPointerINTEL)
175+
Res.emplace_back(C);
176+
return Res;
177+
}
171178
SourceLanguage getSourceLanguage(SPIRVWord *Ver = nullptr) const override {
172179
if (Ver)
173180
*Ver = SrcLangVer;

lib/SPIRV/libSPIRV/SPIRVModule.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ class SPIRVModule {
140140
virtual SPIRVMemoryModelKind getMemoryModel() const = 0;
141141
virtual unsigned getNumFunctions() const = 0;
142142
virtual unsigned getNumVariables() const = 0;
143+
virtual std::vector<SPIRVValue *> getFunctionPointers() const = 0;
143144
virtual SourceLanguage getSourceLanguage(SPIRVWord *) const = 0;
144145
virtual std::set<std::string> &getSourceExtension() = 0;
145146
virtual SPIRVValue *getValue(SPIRVId TheId) const = 0;
@@ -529,6 +530,10 @@ class SPIRVModule {
529530
.shouldPreserveOCLKernelArgTypeMetadataThroughString();
530531
}
531532

533+
bool shouldEmitFunctionPtrAddrSpace() const noexcept {
534+
return TranslationOpts.shouldEmitFunctionPtrAddrSpace();
535+
}
536+
532537
bool preserveAuxData() const noexcept {
533538
return TranslationOpts.preserveAuxData();
534539
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
; RUN: llvm-as %s -o %t.bc
2+
; RUN: llvm-spirv -spirv-ext=+SPV_INTEL_function_pointers -spirv-text %t.bc -o - | FileCheck %s --check-prefix=CHECK-SPIRV
3+
; RUN: llvm-spirv -spirv-ext=+SPV_INTEL_function_pointers %t.bc -o %t.spv
4+
; RUN: llvm-spirv -r -spirv-emit-function-ptr-addr-space -emit-opaque-pointers %t.spv -o - | llvm-dis -o - | FileCheck %s --check-prefix=CHECK-LLVM
5+
6+
target datalayout = "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024"
7+
target triple = "spir64-unknown-unknown"
8+
9+
; Check that aliases are dereferenced and translated to their aliasee values
10+
; when used since they can't be translated directly.
11+
12+
; CHECK-SPIRV-DAG: Name [[#FOO:]] "foo"
13+
; CHECK-SPIRV-DAG: Name [[#BAR:]] "bar"
14+
; CHECK-SPIRV-DAG: Name [[#Y:]] "y"
15+
; CHECK-SPIRV-DAG: Name [[#FOOPTR:]] "foo.alias"
16+
; CHECK-SPIRV-DAG: Decorate [[#FOO]] LinkageAttributes "foo" Export
17+
; CHECK-SPIRV-DAG: Decorate [[#BAR]] LinkageAttributes "bar" Export
18+
; CHECK-SPIRV-DAG: TypeInt [[#I32:]] 32 0
19+
; CHECK-SPIRV-DAG: TypeInt [[#I64:]] 64 0
20+
; CHECK-SPIRV-DAG: TypeFunction [[#FOO_TYPE:]] [[#I32]] [[#I32]]
21+
; CHECK-SPIRV-DAG: TypeVoid [[#VOID:]]
22+
; CHECK-SPIRV-DAG: TypePointer [[#I64PTR:]] 7 [[#I64]]
23+
; CHECK-SPIRV-DAG: TypeFunction [[#BAR_TYPE:]] [[#VOID]] [[#I64PTR]]
24+
; CHECK-SPIRV-DAG: TypePointer [[#FOOPTR_TYPE:]] 7 [[#FOO_TYPE]]
25+
; CHECK-SPIRV-DAG: ConstantFunctionPointerINTEL [[#FOOPTR_TYPE]] [[#FOOPTR]] [[#FOO]]
26+
27+
; CHECK-SPIRV: Function [[#I32]] [[#FOO]] 0 [[#FOO_TYPE]]
28+
29+
; CHECK-SPIRV: Function [[#VOID]] [[#BAR]] 0 [[#BAR_TYPE]]
30+
; CHECK-SPIRV: FunctionParameter [[#I64PTR]] [[#Y]]
31+
; CHECK-SPIRV: ConvertPtrToU [[#I64]] [[#PTRTOINT:]] [[#FOOPTR]]
32+
; CHECK-SPIRV: Store [[#Y]] [[#PTRTOINT]] 2 8
33+
34+
; CHECK-LLVM: define spir_func i32 @foo(i32 %x) addrspace(9)
35+
36+
; CHECK-LLVM: define spir_kernel void @bar(ptr %y)
37+
; CHECK-LLVM: [[PTRTOINT:%.*]] = ptrtoint ptr addrspace(9) @foo to i64
38+
; CHECK-LLVM: store i64 [[PTRTOINT]], ptr %y, align 8
39+
40+
define spir_func i32 @foo(i32 %x) {
41+
ret i32 %x
42+
}
43+
44+
@foo.alias = internal alias i32 (i32), i32 (i32)* @foo
45+
46+
define spir_kernel void @bar(i64* %y) {
47+
store i64 ptrtoint (i32 (i32)* @foo.alias to i64), i64* %y
48+
ret void
49+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
; OpenCL C source:
2+
; char foo(char a) {
3+
; return a;
4+
; }
5+
; void bar() {
6+
; int (*fun_ptr)(int) = &foo;
7+
; }
8+
9+
; RUN: llvm-as -opaque-pointers=0 %s -o %t.bc
10+
; RUN: llvm-spirv %t.bc -opaque-pointers=0 -spirv-ext=+SPV_INTEL_function_pointers -o %t.spv
11+
; RUN: llvm-spirv %t.spv -to-text -o %t.spt
12+
; RUN: FileCheck < %t.spt %s --check-prefix=CHECK-SPIRV
13+
; RUN: llvm-spirv -r -spirv-emit-function-ptr-addr-space -emit-opaque-pointers %t.spv -o %t.r.bc
14+
; RUN: llvm-dis %t.r.bc -o %t.r.ll
15+
; RUN: FileCheck < %t.r.ll %s --check-prefix=CHECK-LLVM
16+
17+
; CHECK-SPIRV: TypeFunction [[#FOO_TY:]] [[#]] [[#]]
18+
; CHECK-SPIRV: TypeFunction [[#DEST_TY:]] [[#]] [[#]]
19+
; CHECK-SPIRV: TypePointer [[#DEST_TY_PTR:]] [[#]] [[#DEST_TY]]
20+
; CHECK-SPIRV: TypePointer [[#FOO_TY_PTR:]] [[#]] [[#FOO_TY]]
21+
; CHECK-SPIRV: ConstantFunctionPointerINTEL [[#FOO_TY_PTR]] [[#FOO_PTR:]] [[#FOO:]]
22+
; CHECK-SPIRV: Function [[#]] [[#FOO]] [[#]] [[#FOO_TY]]
23+
24+
; CHECK-SPIRV: Bitcast [[#DEST_TY_PTR]] [[#]] [[#FOO_PTR]]
25+
26+
; CHECK-LLVM: bitcast ptr addrspace(9) @foo to ptr addrspace(9)
27+
28+
; ModuleID = './example.c'
29+
source_filename = "./example.c"
30+
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"
31+
target triple = "spir"
32+
33+
; Function Attrs: noinline nounwind optnone
34+
define dso_local spir_func signext i8 @foo(i8 signext %0) #0 {
35+
ret i8 %0
36+
}
37+
38+
; Function Attrs: noinline nounwind optnone
39+
define dso_local spir_func void @bar() #0 {
40+
%1 = alloca i32 (i32)*, align 4
41+
store i32 (i32)* bitcast (i8 (i8)* @foo to i32 (i32)*), i32 (i32)** %1, align 4
42+
ret void
43+
}
44+
45+
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" }
46+
47+
!llvm.module.flags = !{!0}
48+
!llvm.ident = !{!1}
49+
50+
!0 = !{i32 1, !"wchar_size", i32 4}
51+
!1 = !{!"clang version 11.0.0 (https://github.com/llvm/llvm-project.git 0e1accd0f726eef2c47be9f37dd0a06cb50d207e)"}

0 commit comments

Comments
 (0)