Skip to content

Commit 46c9eb9

Browse files
authored
Introduce CodeSectionINTEL storage class (#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: #1392
1 parent 630a90a commit 46c9eb9

24 files changed

+1311
-9
lines changed

include/LLVMSPIRVOpts.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,14 @@ class TranslatorOpts {
236236
PreserveOCLKernelArgTypeMetadataThroughString = Value;
237237
}
238238

239+
bool shouldEmitFunctionPtrAddrSpace() const noexcept {
240+
return EmitFunctionPtrAddrSpace;
241+
}
242+
243+
void setEmitFunctionPtrAddrSpace(bool Value) noexcept {
244+
EmitFunctionPtrAddrSpace = Value;
245+
}
246+
239247
void setBuiltinFormat(BuiltinFormat Value) noexcept {
240248
SPIRVBuiltinFormat = Value;
241249
}
@@ -287,6 +295,10 @@ class TranslatorOpts {
287295
// kernel_arg_type_qual metadata through OpString
288296
bool PreserveOCLKernelArgTypeMetadataThroughString = false;
289297

298+
// Controls if CodeSectionINTEL can be emitted and consumed with a dedicated
299+
// address space
300+
bool EmitFunctionPtrAddrSpace = false;
301+
290302
bool PreserveAuxData = false;
291303

292304
BuiltinFormat SPIRVBuiltinFormat = BuiltinFormat::Function;

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: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -348,16 +348,21 @@ Type *SPIRVToLLVM::transType(SPIRVType *T, bool UseTPT) {
348348
case internal::OpTypeTokenINTEL:
349349
return mapType(T, Type::getTokenTy(*Context));
350350
case OpTypePointer: {
351-
const unsigned AS =
352-
SPIRSPIRVAddrSpaceMap::rmap(T->getPointerStorageClass());
351+
unsigned AS = SPIRSPIRVAddrSpaceMap::rmap(T->getPointerStorageClass());
352+
if (AS == SPIRAS_CodeSectionINTEL && !BM->shouldEmitFunctionPtrAddrSpace())
353+
AS = SPIRAS_Private;
354+
if (BM->shouldEmitFunctionPtrAddrSpace() &&
355+
T->getPointerElementType()->getOpCode() == OpTypeFunction)
356+
AS = SPIRAS_CodeSectionINTEL;
353357
Type *ElementTy = transType(T->getPointerElementType(), UseTPT);
354358
if (UseTPT)
355359
return TypedPointerType::get(ElementTy, AS);
356360
return mapType(T, PointerType::get(ElementTy, AS));
357361
}
358362
case OpTypeUntypedPointerKHR: {
359-
const unsigned AS =
360-
SPIRSPIRVAddrSpaceMap::rmap(T->getPointerStorageClass());
363+
unsigned AS = SPIRSPIRVAddrSpaceMap::rmap(T->getPointerStorageClass());
364+
if (AS == SPIRAS_CodeSectionINTEL && !BM->shouldEmitFunctionPtrAddrSpace())
365+
AS = SPIRAS_Private;
361366
return mapType(T, PointerType::get(*Context, AS));
362367
}
363368
case OpTypeVector:
@@ -1469,6 +1474,17 @@ Value *SPIRVToLLVM::transValueWithoutDecoration(SPIRVValue *BV, Function *F,
14691474
case OpTypeMatrix:
14701475
case OpTypeArray: {
14711476
auto *AT = cast<ArrayType>(transType(BCC->getType()));
1477+
for (size_t I = 0; I != AT->getNumElements(); ++I) {
1478+
auto *ElemTy = AT->getElementType();
1479+
if (auto *ElemPtrTy = dyn_cast<PointerType>(ElemTy)) {
1480+
assert(isa<PointerType>(CV[I]->getType()) &&
1481+
"Constant type doesn't match constexpr array element type");
1482+
if (ElemPtrTy->getAddressSpace() !=
1483+
cast<PointerType>(CV[I]->getType())->getAddressSpace())
1484+
CV[I] = ConstantExpr::getAddrSpaceCast(CV[I], AT->getElementType());
1485+
}
1486+
}
1487+
14721488
return mapValue(BV, ConstantArray::get(AT, CV));
14731489
}
14741490
case OpTypeStruct: {
@@ -1485,7 +1501,12 @@ Value *SPIRVToLLVM::transValueWithoutDecoration(SPIRVValue *BV, Function *F,
14851501
!BCCTy->getElementType(I)->isPointerTy())
14861502
continue;
14871503

1488-
CV[I] = ConstantExpr::getBitCast(CV[I], BCCTy->getElementType(I));
1504+
if (cast<PointerType>(CV[I]->getType())->getAddressSpace() !=
1505+
cast<PointerType>(BCCTy->getElementType(I))->getAddressSpace())
1506+
CV[I] =
1507+
ConstantExpr::getAddrSpaceCast(CV[I], BCCTy->getElementType(I));
1508+
else
1509+
CV[I] = ConstantExpr::getBitCast(CV[I], BCCTy->getElementType(I));
14891510
}
14901511
}
14911512

@@ -1521,7 +1542,10 @@ Value *SPIRVToLLVM::transValueWithoutDecoration(SPIRVValue *BV, Function *F,
15211542
static_cast<SPIRVConstantFunctionPointerINTEL *>(BV);
15221543
SPIRVFunction *F = BC->getFunction();
15231544
BV->setName(F->getName());
1524-
return mapValue(BV, transFunction(F));
1545+
const unsigned AS = BM->shouldEmitFunctionPtrAddrSpace()
1546+
? SPIRAS_CodeSectionINTEL
1547+
: SPIRAS_Private;
1548+
return mapValue(BV, transFunction(F, AS));
15251549
}
15261550

15271551
case OpUndef:
@@ -3046,7 +3070,7 @@ void SPIRVToLLVM::transFunctionAttrs(SPIRVFunction *BF, Function *F) {
30463070
});
30473071
}
30483072

3049-
Function *SPIRVToLLVM::transFunction(SPIRVFunction *BF) {
3073+
Function *SPIRVToLLVM::transFunction(SPIRVFunction *BF, unsigned AS) {
30503074
auto Loc = FuncMap.find(BF);
30513075
if (Loc != FuncMap.end())
30523076
return Loc->second;
@@ -3094,7 +3118,7 @@ Function *SPIRVToLLVM::transFunction(SPIRVFunction *BF) {
30943118
}
30953119
Function *F = M->getFunction(FuncName);
30963120
if (!F)
3097-
F = Function::Create(FT, Linkage, FuncName, M);
3121+
F = Function::Create(FT, Linkage, AS, FuncName, M);
30983122
F = cast<Function>(mapValue(BF, F));
30993123
mapFunction(BF, F);
31003124

@@ -3501,6 +3525,17 @@ bool SPIRVToLLVM::translate() {
35013525
DbgTran->transDebugInst(EI);
35023526
}
35033527

3528+
for (auto *FP : BM->getFunctionPointers()) {
3529+
SPIRVConstantFunctionPointerINTEL *BC =
3530+
static_cast<SPIRVConstantFunctionPointerINTEL *>(FP);
3531+
SPIRVFunction *F = BC->getFunction();
3532+
FP->setName(F->getName());
3533+
const unsigned AS = BM->shouldEmitFunctionPtrAddrSpace()
3534+
? SPIRAS_CodeSectionINTEL
3535+
: SPIRAS_Private;
3536+
mapValue(FP, transFunction(F, AS));
3537+
}
3538+
35043539
for (unsigned I = 0, E = BM->getNumFunctions(); I != E; ++I) {
35053540
transFunction(BM->getFunction(I));
35063541
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: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -655,6 +655,11 @@ SPIRVType *LLVMToSPIRVBase::transPointerType(Type *ET, unsigned AddrSpc) {
655655
((AddrSpc == SPIRAS_GlobalDevice) || (AddrSpc == SPIRAS_GlobalHost))) {
656656
return transPointerType(ET, SPIRAS_Global);
657657
}
658+
// Lower function pointer address space to private if
659+
// spirv-emit-function-ptr-addr-space is not passed
660+
if (AddrSpc == SPIRAS_CodeSectionINTEL &&
661+
!BM->shouldEmitFunctionPtrAddrSpace())
662+
return transPointerType(ET, SPIRAS_Private);
658663
if (ST && !ST->isSized()) {
659664
Op OpCode;
660665
StringRef STName = ST->getName();
@@ -750,6 +755,9 @@ SPIRVType *LLVMToSPIRVBase::transPointerType(SPIRVType *ET, unsigned AddrSpc) {
750755
return Loc->second;
751756

752757
SPIRVType *TranslatedTy = nullptr;
758+
if (AddrSpc == SPIRAS_CodeSectionINTEL &&
759+
!BM->shouldEmitFunctionPtrAddrSpace())
760+
return transPointerType(ET, SPIRAS_Private);
753761
if (BM->isAllowedToUseExtension(ExtensionID::SPV_KHR_untyped_pointers) &&
754762
!(ET->isTypeArray() || ET->isTypeVector() || ET->isTypeStruct() ||
755763
ET->isTypeImage() || ET->isTypeSampler() || ET->isTypePipe())) {

lib/SPIRV/libSPIRV/SPIRVInstruction.cpp

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

lib/SPIRV/libSPIRV/SPIRVModule.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,13 @@ class SPIRVModuleImpl : public SPIRVModule {
142142
SPIRVConstant *getLiteralAsConstant(unsigned Literal) override;
143143
unsigned getNumFunctions() const override { return FuncVec.size(); }
144144
unsigned getNumVariables() const override { return VariableVec.size(); }
145+
std::vector<SPIRVValue *> getFunctionPointers() const override {
146+
std::vector<SPIRVValue *> Res;
147+
for (auto *C : ConstVec)
148+
if (C->getOpCode() == OpConstantFunctionPointerINTEL)
149+
Res.emplace_back(C);
150+
return Res;
151+
}
145152
SourceLanguage getSourceLanguage(SPIRVWord *Ver = nullptr) const override {
146153
if (Ver)
147154
*Ver = SrcLangVer;

lib/SPIRV/libSPIRV/SPIRVModule.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ class SPIRVModule {
142142
virtual SPIRVMemoryModelKind getMemoryModel() const = 0;
143143
virtual unsigned getNumFunctions() const = 0;
144144
virtual unsigned getNumVariables() const = 0;
145+
virtual std::vector<SPIRVValue *> getFunctionPointers() const = 0;
145146
virtual SourceLanguage getSourceLanguage(SPIRVWord *) const = 0;
146147
virtual std::set<std::string> &getSourceExtension() = 0;
147148
virtual SPIRVValue *getValue(SPIRVId TheId) const = 0;
@@ -554,6 +555,10 @@ class SPIRVModule {
554555
.shouldPreserveOCLKernelArgTypeMetadataThroughString();
555556
}
556557

558+
bool shouldEmitFunctionPtrAddrSpace() const noexcept {
559+
return TranslationOpts.shouldEmitFunctionPtrAddrSpace();
560+
}
561+
557562
bool preserveAuxData() const noexcept {
558563
return TranslationOpts.preserveAuxData();
559564
}
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 %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), ptr @foo
45+
46+
define spir_kernel void @bar(ptr %y) {
47+
store i64 ptrtoint (ptr @foo.alias to i64), ptr %y
48+
ret void
49+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
; OpenCL C source:
2+
; char foo(char a) {
3+
; return a;
4+
; }
5+
; void bar() {
6+
; int (*fun_ptr)(int) = &foo;
7+
; fun_ptr(0);
8+
; }
9+
10+
; RUN: llvm-as %s -o %t.bc
11+
; RUN: llvm-spirv %t.bc -spirv-ext=+SPV_INTEL_function_pointers -o %t.spv
12+
; RUN: llvm-spirv %t.spv -to-text -o %t.spt
13+
; RUN: FileCheck < %t.spt %s --check-prefix=CHECK-SPIRV
14+
; RUN: llvm-spirv -r -spirv-emit-function-ptr-addr-space %t.spv -o %t.r.bc
15+
; RUN: llvm-dis %t.r.bc -o %t.r.ll
16+
; RUN: FileCheck < %t.r.ll %s --check-prefix=CHECK-LLVM
17+
18+
; CHECK-SPIRV-DAG: TypeInt [[#I8:]] 8
19+
; CHECK-SPIRV-DAG: TypeInt [[#I32:]] 32
20+
; CHECK-SPIRV-DAG: TypeFunction [[#FOO_TY:]] [[#I8]] [[#I8]]
21+
; CHECK-SPIRV-DAG: TypeFunction [[#DEST_TY:]] [[#I32]] [[#I32]]
22+
; CHECK-SPIRV-DAG: TypePointer [[#DEST_TY_PTR:]] [[#]] [[#DEST_TY]]
23+
; CHECK-SPIRV-DAG: TypePointer [[#FOO_TY_PTR:]] [[#]] [[#FOO_TY]]
24+
; CHECK-SPIRV: ConstantFunctionPointerINTEL [[#FOO_TY_PTR]] [[#FOO_PTR:]] [[#FOO:]]
25+
; CHECK-SPIRV: Function [[#]] [[#FOO]] [[#]] [[#FOO_TY]]
26+
27+
; CHECK-SPIRV: Bitcast [[#DEST_TY_PTR]] [[#]] [[#FOO_PTR]]
28+
29+
; CHECK-LLVM: bitcast ptr addrspace(9) @foo to ptr addrspace(9)
30+
31+
; ModuleID = './example.c'
32+
source_filename = "./example.c"
33+
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"
34+
target triple = "spir"
35+
36+
; Function Attrs: noinline nounwind optnone
37+
define dso_local spir_func signext i8 @foo(i8 signext %0) #0 {
38+
ret i8 %0
39+
}
40+
41+
; Function Attrs: noinline nounwind optnone
42+
define dso_local spir_func void @bar() #0 {
43+
%1 = call i32 @foo(i32 0)
44+
ret void
45+
}
46+
47+
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" }
48+
49+
!llvm.module.flags = !{!0}
50+
!llvm.ident = !{!1}
51+
52+
!0 = !{i32 1, !"wchar_size", i32 4}
53+
!1 = !{!"clang version 11.0.0 (https://github.com/llvm/llvm-project.git 0e1accd0f726eef2c47be9f37dd0a06cb50d207e)"}

0 commit comments

Comments
 (0)