Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions include/LLVMSPIRVOpts.h
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,14 @@ class TranslatorOpts {
PreserveOCLKernelArgTypeMetadataThroughString = Value;
}

bool shouldEmitFunctionPtrAddrSpace() const noexcept {
return EmitFunctionPtrAddrSpace;
}

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

private:
// Common translation options
VersionNumber MaxVersion = VersionNumber::MaximumVersion;
Expand Down Expand Up @@ -262,6 +270,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;
};

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

Expand All @@ -203,6 +204,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 @@ -219,6 +222,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
57 changes: 46 additions & 11 deletions lib/SPIRV/SPIRVReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -381,11 +381,17 @@ Type *SPIRVToLLVM::transType(SPIRVType *T, bool IsClassMember) {
}
case internal::OpTypeTokenINTEL:
return mapType(T, Type::getTokenTy(*Context));
case OpTypePointer:
case OpTypePointer: {
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;
return mapType(
T, PointerType::get(
transType(T->getPointerElementType(), IsClassMember),
SPIRSPIRVAddrSpaceMap::rmap(T->getPointerStorageClass())));
transType(T->getPointerElementType(), IsClassMember), AS));
}
case OpTypeVector:
return mapType(T,
FixedVectorType::get(transType(T->getVectorComponentType()),
Expand Down Expand Up @@ -1564,10 +1570,20 @@ Value *SPIRVToLLVM::transValueWithoutDecoration(SPIRVValue *BV, Function *F,
case OpTypeVector:
return mapValue(BV, ConstantVector::get(CV));
case OpTypeMatrix:
case OpTypeArray:
return mapValue(
BV, ConstantArray::get(dyn_cast<ArrayType>(transType(BCC->getType())),
CV));
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: {
auto BCCTy = dyn_cast<StructType>(transType(BCC->getType()));
auto Members = BCCTy->getNumElements();
Expand All @@ -1582,7 +1598,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 @@ -1620,7 +1641,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 @@ -3038,7 +3062,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 @@ -3087,7 +3111,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 @@ -3486,6 +3510,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 @@ -98,7 +98,7 @@ class SPIRVToLLVM {
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
9 changes: 9 additions & 0 deletions lib/SPIRV/SPIRVWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,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 @@ -615,6 +620,10 @@ SPIRVType *LLVMToSPIRVBase::transPointerType(SPIRVType *ET, unsigned AddrSpc) {
if (Loc != PointeeTypeMap.end())
return Loc->second;

if (AddrSpc == SPIRAS_CodeSectionINTEL &&
!BM->shouldEmitFunctionPtrAddrSpace())
return transPointerType(ET, SPIRAS_Private);

SPIRVType *TranslatedTy = BM->addPointerType(
SPIRSPIRVAddrSpaceMap::map(static_cast<SPIRAddressSpace>(AddrSpc)), ET);
PointeeTypeMap[TypeKey] = TranslatedTy;
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 @@ -286,6 +286,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 @@ -168,6 +168,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 @@ -140,6 +140,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 @@ -539,6 +540,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,51 @@
; OpenCL C source:
; char foo(char a) {
; return a;
; }
; void bar() {
; int (*fun_ptr)(int) = &foo;
; }

; 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: TypeFunction [[#FOO_TY:]] [[#]] [[#]]
; CHECK-SPIRV: TypeFunction [[#DEST_TY:]] [[#]] [[#]]
; CHECK-SPIRV: TypePointer [[#DEST_TY_PTR:]] [[#]] [[#DEST_TY]]
; CHECK-SPIRV: 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 i8 (i8) addrspace(9)* @foo to i32 (i32) 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 = alloca i32 (i32)*, align 4
store i32 (i32)* bitcast (i8 (i8)* @foo to i32 (i32)*), i32 (i32)** %1, align 4
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)"}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
; 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 - | FileCheck %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: Capability FunctionPointersINTEL
; CHECK-SPIRV: Extension "SPV_INTEL_function_pointers"
; CHECK-SPIRV: Name [[F1Name:[0-9]+]] "f1"
; CHECK-SPIRV: Name [[F2Name:[0-9]+]] "f2"
; CHECK-SPIRV: TypeInt [[Int32:[0-9]+]] 32
; CHECK-SPIRV: TypeInt [[Int64:[0-9]+]] 64
; CHECK-SPIRV-DAG: Constant [[Int32]] [[XArg:[0-9]+]] 32
; CHECK-SPIRV-DAG: Constant [[Int32]] [[YArg:[0-9]+]] 2

; CHECK-SPIRV: ConstantFunctionPointerINTEL {{[0-9]+}} [[F1:[0-9]+]] [[F1Name]]
; CHECK-SPIRV: ConstantFunctionPointerINTEL {{[0-9]+}} [[F2:[0-9]+]] [[F2Name]]
; CHECK-SPIRV: ConstantComposite {{[0-9]+}} [[ConstComp:[0-9]+]] [[F1]] [[F2]]
; CHECK-SPIRV: Variable {{[0-9]+}} [[Var:[0-9]+]] {{[0-9]+}} [[ConstComp]]

; CHECK-SPIRV: InBoundsPtrAccessChain {{[0-9]+}} [[GEP:[0-9]+]] [[Var]] {{[0-9]+}} {{[0-9]+}}
; CHECK-SPIRV: Load {{[0-9]+}} [[FuncPtr:[0-9]+]] [[GEP]]
; CHECK-SPIRV: FunctionPointerCallINTEL [[Int32]] {{[0-9]+}} [[FuncPtr]] [[XArg]] [[YArg]]

; CHECK-LLVM: @__const.main.funcs = internal constant [2 x i32 (i32, i32) addrspace(9)*] [i32 (i32, i32) addrspace(9)* @f1, i32 (i32, i32) addrspace(9)* @f2], align 16
; CHECK-LLVM: %[[Idx:[a-z0-9]+]] = getelementptr inbounds [2 x i32 (i32, i32) addrspace(9)*], [2 x i32 (i32, i32) addrspace(9)*]* @__const.main.funcs, i64 0, i64 %{{[a-z0-9]+}}
; CHECK-LLVM: %[[FuncPtr:[a-z0-9]+]] = load i32 (i32, i32) addrspace(9)*, i32 (i32, i32) addrspace(9)** %[[Idx]], align 8
; CHECK-LLVM: %{{[a-z0-9]+}} = call spir_func addrspace(9) i32 %[[FuncPtr]](i32 32, i32 2)

target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "spir-unknown-unknown"

@__const.main.funcs = private unnamed_addr constant [2 x i32 (i32, i32)*] [i32 (i32, i32)* @f1, i32 (i32, i32)* @f2], align 16

; Function Attrs: norecurse nounwind readnone uwtable
define dso_local i32 @f1(i32 %a, i32 %b) #0 {
entry:
%add = add nsw i32 %b, %a
ret i32 %add
}

; Function Attrs: norecurse nounwind readnone uwtable
define dso_local i32 @f2(i32 %a, i32 %b) #0 {
entry:
%sub = sub nsw i32 %a, %b
ret i32 %sub
}

; Function Attrs: nounwind uwtable
define dso_local i32 @main() local_unnamed_addr #1 {
entry:
%call = tail call i32 @rand() #3
%rem = srem i32 %call, 2
%idxprom = sext i32 %rem to i64
%arrayidx = getelementptr inbounds [2 x i32 (i32, i32)*], [2 x i32 (i32, i32)*]* @__const.main.funcs, i64 0, i64 %idxprom
%0 = load i32 (i32, i32)*, i32 (i32, i32)** %arrayidx, align 8
%call1 = tail call i32 %0(i32 32, i32 2) #3
ret i32 %call1
}

; Function Attrs: nounwind
declare dso_local i32 @rand() local_unnamed_addr #2

attributes #0 = { norecurse nounwind readnone uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="none" "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" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { nounwind uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="none" "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" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #2 = { nounwind "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
Loading
Loading