summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorkkyzylov <ksenia.kyzylova@intel.com>2016-10-05 17:53:29 +0300
committerYaxun (Sam) Liu <yaxun.liu@amd.com>2016-10-05 10:53:29 -0400
commit6a1bbcba8e9e0810fdf6f4d1546099a075176001 (patch)
tree765460fedccb65aaaefc05940aa24f428c284607
parent8fc9c34d8f6e3dfdb6c566b339479f4c3270c7bf (diff)
Support translation of OpLifetimeStart and OpLifetimeStop instructions (#191)
-rw-r--r--lib/SPIRV/SPIRVReader.cpp45
-rw-r--r--lib/SPIRV/SPIRVWriter.cpp37
-rw-r--r--lib/SPIRV/libSPIRV/SPIRVBasicBlock.h9
-rw-r--r--lib/SPIRV/libSPIRV/SPIRVEntry.h2
-rw-r--r--lib/SPIRV/libSPIRV/SPIRVInstruction.h41
-rw-r--r--lib/SPIRV/libSPIRV/SPIRVModule.cpp24
-rw-r--r--lib/SPIRV/libSPIRV/SPIRVModule.h3
-rw-r--r--test/lifetime.ll73
8 files changed, 232 insertions, 2 deletions
diff --git a/lib/SPIRV/SPIRVReader.cpp b/lib/SPIRV/SPIRVReader.cpp
index d39d147..a6b4163 100644
--- a/lib/SPIRV/SPIRVReader.cpp
+++ b/lib/SPIRV/SPIRVReader.cpp
@@ -55,6 +55,7 @@
#include "llvm/IR/DIBuilder.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/Instructions.h"
+#include "llvm/IR/IntrinsicInst.h"
#include "llvm/IR/Metadata.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Operator.h"
@@ -462,6 +463,7 @@ private:
Value *getTranslatedValue(SPIRVValue *BV);
Type *getTranslatedType(SPIRVType *BT);
+ IntrinsicInst *getLifetimeStartIntrinsic(Instruction *I);
SPIRVErrorLog &getErrorLog() {
return BM->getErrorLog();
@@ -524,6 +526,23 @@ SPIRVToLLVM::getTranslatedValue(SPIRVValue *BV){
return nullptr;
}
+IntrinsicInst *
+SPIRVToLLVM::getLifetimeStartIntrinsic(Instruction *I) {
+ auto II = dyn_cast<IntrinsicInst>(I);
+ if (II && II->getIntrinsicID() == Intrinsic::lifetime_start)
+ return II;
+ // Bitcast might be inserted during translation of OpLifetimeStart
+ auto BC = dyn_cast<BitCastInst>(I);
+ if (BC) {
+ for (const auto &U : BC->users()) {
+ II = dyn_cast<IntrinsicInst>(U);
+ if (II && II->getIntrinsicID() == Intrinsic::lifetime_start)
+ return II;;
+ }
+ }
+ return nullptr;
+}
+
void
SPIRVToLLVM::setAttrByCalledFunc(CallInst *Call) {
Function *F = Call->getCalledFunction();
@@ -1545,6 +1564,32 @@ SPIRVToLLVM::transValueWithoutDecoration(SPIRVValue *BV, Function *F,
transValue(RV->getReturnValue(), F, BB), BB));
}
+ case OpLifetimeStart: {
+ SPIRVLifetimeStart *LTStart = static_cast<SPIRVLifetimeStart*>(BV);
+ IRBuilder<> Builder(BB);
+ SPIRVWord Size = LTStart->getSize();
+ ConstantInt *S = nullptr;
+ if (Size)
+ S = Builder.getInt64(Size);
+ Value* Var = transValue(LTStart->getObject(), F, BB);
+ CallInst *Start = Builder.CreateLifetimeStart(Var, S);
+ return mapValue(BV, Start->getOperand(1));
+ }
+
+ case OpLifetimeStop: {
+ SPIRVLifetimeStop *LTStop = static_cast<SPIRVLifetimeStop*>(BV);
+ IRBuilder<> Builder(BB);
+ SPIRVWord Size = LTStop->getSize();
+ ConstantInt *S = nullptr;
+ if (Size)
+ S = Builder.getInt64(Size);
+ auto Var = transValue(LTStop->getObject(), F, BB);
+ for (const auto &I : Var->users())
+ if (auto II = getLifetimeStartIntrinsic(dyn_cast<Instruction>(I)))
+ return mapValue(BV, Builder.CreateLifetimeEnd(II->getOperand(1), S));
+ return mapValue(BV, Builder.CreateLifetimeEnd(Var, S));
+ }
+
case OpStore: {
SPIRVStore *BS = static_cast<SPIRVStore*>(BV);
StoreInst *SI = new StoreInst(transValue(BS->getSrc(), F, BB),
diff --git a/lib/SPIRV/SPIRVWriter.cpp b/lib/SPIRV/SPIRVWriter.cpp
index be784e7..6b3350a 100644
--- a/lib/SPIRV/SPIRVWriter.cpp
+++ b/lib/SPIRV/SPIRVWriter.cpp
@@ -276,6 +276,7 @@ private:
SPIRVInstruction* transBinaryInst(BinaryOperator* B, SPIRVBasicBlock* BB);
SPIRVInstruction* transCmpInst(CmpInst* Cmp, SPIRVBasicBlock* BB);
+ SPIRVInstruction* transLifetimeIntrinsicInst(Op OC, IntrinsicInst *Intrinsic, SPIRVBasicBlock *BB);
void dumpUsers(Value *V);
@@ -837,6 +838,38 @@ LLVMToSPIRV::transCmpInst(CmpInst* Cmp, SPIRVBasicBlock* BB) {
return BI;
}
+SPIRVInstruction *
+LLVMToSPIRV::transLifetimeIntrinsicInst(Op OC, IntrinsicInst *II, SPIRVBasicBlock *BB) {
+ int64_t Size = dyn_cast<ConstantInt>(II->getOperand(0))->getSExtValue();
+ if (Size == -1)
+ Size = 0;
+ auto Op1 = II->getOperand(1);
+
+ if (auto AI = dyn_cast<AllocaInst>(Op1)) {
+ assert(!Size ||
+ M->getDataLayout()->getTypeSizeInBits(AI->getAllocatedType()) == Size * 8 &&
+ "Size of the argument should match the allocated memory");
+ return BM->addLifetimeInst(OC, transValue(Op1, BB), Size, BB);
+ }
+ // Bitcast might be inserted during translation of OpLifetimeStart
+ assert(isa<BitCastInst>(Op1));
+ for (const auto &U : Op1->users()) {
+ auto BCU = dyn_cast<IntrinsicInst>(U);
+ assert(BCU && (BCU->getIntrinsicID() == Intrinsic::lifetime_start ||
+ BCU->getIntrinsicID() == Intrinsic::lifetime_end) &&
+ "The only users of this bitcast instruction are lifetime intrinsics");
+ }
+ auto AI = dyn_cast<AllocaInst>(dyn_cast<BitCastInst>(Op1)->getOperand(0));
+ assert(AI && (!Size ||
+ M->getDataLayout()->getTypeSizeInBits(AI->getAllocatedType()) == Size * 8) &&
+ "Size of the argument should match the allocated memory");
+ auto LT = BM->addLifetimeInst(OC, transValue(AI, BB), Size, BB);
+ auto BC = LT->getPrevious();
+ if (BC && BC->getOpCode() == OpBitcast)
+ BM->eraseInstruction(BC, BB);
+ return LT;
+}
+
SPIRV::SPIRVInstruction *LLVMToSPIRV::transUnaryInst(UnaryInstruction *U,
SPIRVBasicBlock *BB) {
Op BOC = OpNop;
@@ -1270,6 +1303,10 @@ LLVMToSPIRV::transIntrinsicInst(IntrinsicInst *II, SPIRVBasicBlock *BB) {
transValue(II->getOperand(2), BB),
getMemoryAccess(cast<MemIntrinsic>(II)),
BB);
+ case Intrinsic::lifetime_start:
+ return transLifetimeIntrinsicInst(OpLifetimeStart, II, BB);
+ case Intrinsic::lifetime_end:
+ return transLifetimeIntrinsicInst(OpLifetimeStop, II, BB);
default:
// LLVM intrinsic functions shouldn't get to SPIRV, because they
// would have no definition there.
diff --git a/lib/SPIRV/libSPIRV/SPIRVBasicBlock.h b/lib/SPIRV/libSPIRV/SPIRVBasicBlock.h
index ccbd292..5ed2f9e 100644
--- a/lib/SPIRV/libSPIRV/SPIRVBasicBlock.h
+++ b/lib/SPIRV/libSPIRV/SPIRVBasicBlock.h
@@ -79,6 +79,11 @@ public:
void setScope(SPIRVEntry *Scope);
void setParent(SPIRVFunction *F) { ParentF = F;}
SPIRVInstruction *addInstruction(SPIRVInstruction *I);
+ void eraseInstruction(const SPIRVInstruction *I) {
+ auto Loc = find(I);
+ assert(Loc != InstVec.end());
+ InstVec.erase(Loc);
+ }
void setAttr() { setHasNoType();}
_SPIRV_DCL_ENCDEC
@@ -97,6 +102,10 @@ private:
const {
return std::find(InstVec.begin(), InstVec.end(), Inst);
}
+
+ SPIRVInstructionVector::iterator find(const SPIRVInstruction *Inst) {
+ return std::find(InstVec.begin(), InstVec.end(), Inst);
+ }
};
typedef SPIRVBasicBlock SPIRVLabel;
diff --git a/lib/SPIRV/libSPIRV/SPIRVEntry.h b/lib/SPIRV/libSPIRV/SPIRVEntry.h
index b66b12f..4092b16 100644
--- a/lib/SPIRV/libSPIRV/SPIRVEntry.h
+++ b/lib/SPIRV/libSPIRV/SPIRVEntry.h
@@ -753,8 +753,6 @@ _SPIRV_OP(EmitStreamVertex)
_SPIRV_OP(EndStreamPrimitive)
_SPIRV_OP(LoopMerge)
_SPIRV_OP(Kill)
-_SPIRV_OP(LifetimeStart)
-_SPIRV_OP(LifetimeStop)
_SPIRV_OP(ImageSparseSampleImplicitLod, 305)
_SPIRV_OP(ImageSparseSampleExplicitLod, 306)
_SPIRV_OP(ImageSparseSampleDrefImplicitLod, 307)
diff --git a/lib/SPIRV/libSPIRV/SPIRVInstruction.h b/lib/SPIRV/libSPIRV/SPIRVInstruction.h
index 23e2e24..2a5223d 100644
--- a/lib/SPIRV/libSPIRV/SPIRVInstruction.h
+++ b/lib/SPIRV/libSPIRV/SPIRVInstruction.h
@@ -1820,6 +1820,47 @@ protected:
SPIRVId MemSema;
};
+template<Op OC>
+class SPIRVLifetime : public SPIRVInstruction {
+public:
+ // Complete constructor
+ SPIRVLifetime(SPIRVId TheObject, SPIRVWord TheSize,
+ SPIRVBasicBlock *TheBB) :
+ SPIRVInstruction(3, OC, TheBB), Object(TheObject), Size(TheSize) {
+ validate();
+ assert(TheBB && "Invalid BB");
+ };
+ // Incomplete constructor
+ SPIRVLifetime() :SPIRVInstruction(OC), Object(SPIRVID_INVALID),
+ Size(SPIRVWORD_MAX) {
+ setHasNoId();
+ setHasNoType();
+ }
+ SPIRVCapVec getRequiredCapability() const {
+ return getVec(CapabilityKernel);
+ }
+ SPIRVValue *getObject() { return getValue(Object); };
+ SPIRVWord getSize() { return Size; };
+protected:
+ void validate() const {
+ auto Obj = static_cast<SPIRVVariable*>(getValue(Object));
+ assert(Obj->getStorageClass() == StorageClassFunction &&
+ "Invalid storage class");
+ assert(Obj->getType()->isTypePointer() &&
+ Obj->getType()->getPointerElementType()->isTypeInt() &&
+ "Object type must be an integer type scalar");
+ if (!Obj->getType()->getPointerElementType()->isTypeVoid() ||
+ !Module->hasCapability(CapabilityAddresses))
+ assert(Size == 0 && "Size must be 0");
+ }
+ _SPIRV_DEF_ENCDEC2(Object, Size)
+ SPIRVId Object;
+ SPIRVWord Size;
+};
+
+typedef SPIRVLifetime<OpLifetimeStart> SPIRVLifetimeStart;
+typedef SPIRVLifetime<OpLifetimeStop> SPIRVLifetimeStop;
+
class SPIRVGroupAsyncCopy:public SPIRVInstruction {
public:
static const Op OC = OpGroupAsyncCopy;
diff --git a/lib/SPIRV/libSPIRV/SPIRVModule.cpp b/lib/SPIRV/libSPIRV/SPIRVModule.cpp
index 0a910f8..f3058d5 100644
--- a/lib/SPIRV/libSPIRV/SPIRVModule.cpp
+++ b/lib/SPIRV/libSPIRV/SPIRVModule.cpp
@@ -184,6 +184,7 @@ public:
virtual SPIRVFunction *addFunction(SPIRVFunction *);
virtual SPIRVFunction *addFunction(SPIRVTypeFunction *, SPIRVId);
virtual SPIRVEntry *replaceForward(SPIRVForward *, SPIRVEntry *);
+ virtual void eraseInstruction(SPIRVInstruction *, SPIRVBasicBlock *);
// Type creation functions
template<class T> T * addType(T *Ty);
@@ -277,6 +278,8 @@ public:
SPIRVBasicBlock* BB, SPIRVType *Ty);
virtual SPIRVInstTemplateBase *addInstTemplate(Op OC,
const std::vector<SPIRVWord>& Ops, SPIRVBasicBlock* BB, SPIRVType *Ty);
+ virtual SPIRVInstruction *addLifetimeInst(Op OC, SPIRVValue *Object,
+ SPIRVWord Size, SPIRVBasicBlock *BB);
virtual SPIRVInstruction *addMemoryBarrierInst(
Scope ScopeKind, SPIRVWord MemFlag, SPIRVBasicBlock *BB);
virtual SPIRVInstruction *addUnreachableInst(SPIRVBasicBlock *);
@@ -888,6 +891,16 @@ SPIRVModuleImpl::replaceForward(SPIRVForward *Forward, SPIRVEntry *Entry) {
return Entry;
}
+void
+SPIRVModuleImpl::eraseInstruction(SPIRVInstruction *I, SPIRVBasicBlock *BB) {
+ SPIRVId Id = I->getId();
+ BB->eraseInstruction(I);
+ auto Loc = IdEntryMap.find(Id);
+ assert(Loc != IdEntryMap.end());
+ IdEntryMap.erase(Loc);
+ delete I;
+}
+
SPIRVValue *
SPIRVModuleImpl::addConstant(SPIRVValue *C) {
return add(C);
@@ -1104,6 +1117,17 @@ SPIRVModuleImpl::addControlBarrierInst(SPIRVValue *ExecKind,
}
SPIRVInstruction *
+SPIRVModuleImpl::addLifetimeInst(Op OC, SPIRVValue *Object, SPIRVWord Size,
+ SPIRVBasicBlock *BB) {
+ if(OC == OpLifetimeStart)
+ return BB->addInstruction(new SPIRVLifetimeStart(Object->getId(),
+ Size, BB));
+ else
+ return BB->addInstruction(new SPIRVLifetimeStop(Object->getId(),
+ Size, BB));
+}
+
+SPIRVInstruction *
SPIRVModuleImpl::addMemoryBarrierInst(Scope ScopeKind,
SPIRVWord MemFlag, SPIRVBasicBlock *BB) {
return addInstruction(SPIRVInstTemplateBase::create(OpMemoryBarrier,
diff --git a/lib/SPIRV/libSPIRV/SPIRVModule.h b/lib/SPIRV/libSPIRV/SPIRVModule.h
index e5ee717..21b8a66 100644
--- a/lib/SPIRV/libSPIRV/SPIRVModule.h
+++ b/lib/SPIRV/libSPIRV/SPIRVModule.h
@@ -188,6 +188,7 @@ public:
virtual SPIRVFunction *addFunction(SPIRVTypeFunction *,
SPIRVId Id = SPIRVID_INVALID) = 0;
virtual SPIRVEntry *replaceForward(SPIRVForward *, SPIRVEntry *) = 0;
+ virtual void eraseInstruction(SPIRVInstruction *, SPIRVBasicBlock *) = 0;
// Type creation functions
virtual SPIRVTypeArray *addArrayType(SPIRVType *, SPIRVConstant *) = 0;
@@ -282,6 +283,8 @@ public:
const std::vector<SPIRVWord>& Ops, SPIRVBasicBlock* BB, SPIRVType *Ty) = 0;
virtual SPIRVInstruction *addLoadInst(SPIRVValue *,
const std::vector<SPIRVWord>&, SPIRVBasicBlock *) = 0;
+ virtual SPIRVInstruction *addLifetimeInst(Op OC, SPIRVValue *Object,
+ SPIRVWord Size, SPIRVBasicBlock *BB) = 0;
virtual SPIRVInstruction *addMemoryBarrierInst(
Scope ScopeKind, SPIRVWord MemFlag, SPIRVBasicBlock *BB)
= 0;
diff --git a/test/lifetime.ll b/test/lifetime.ll
new file mode 100644
index 0000000..3f396f0
--- /dev/null
+++ b/test/lifetime.ll
@@ -0,0 +1,73 @@
+; RUN: llvm-as %s -o %t.bc
+; RUN: llvm-spirv %t.bc -spirv-mem2reg=0 -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 %t.spv -o %t.spv.bc
+; RUN: llvm-dis < %t.spv.bc | FileCheck %s --check-prefix=CHECK-LLVM
+
+; CHECK-SPIRV-NOT: Bitcast
+; CHECK-SPIRV: 3 LifetimeStart [[tmp:[0-9]+]] 0
+; CHECK-SPIRV: 3 LifetimeStop [[tmp]] 0
+
+; CHECK-LLVM: %[[tmp1:[0-9]+]] = bitcast i32* %{{[0-9]+}} to i8*
+; CHECK-LLVM: call void @llvm.lifetime.start(i64 -1, i8* %[[tmp1]])
+; CHECK-LLVM: call void @llvm.lifetime.end(i64 -1, i8* %[[tmp1]])
+; CHECK-LLVM: declare void @llvm.lifetime.start(i64, i8* nocapture)
+; CHECK-LLVM: declare void @llvm.lifetime.end(i64, i8* nocapture)
+
+; ModuleID = 'main'
+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"
+
+; Function Attrs: nounwind
+define spir_kernel void @lifetime_simple(i32 addrspace(1)* nocapture %res, i32 addrspace(1)* nocapture %lhs, i32 addrspace(1)* nocapture %rhs) #0 {
+ %1 = alloca i32
+ %2 = call spir_func i64 @_Z13get_global_idj(i32 0) #1
+ %3 = shl i64 %2, 32
+ %4 = ashr exact i64 %3, 32
+ %5 = getelementptr inbounds i32 addrspace(1)* %lhs, i64 %4
+ %6 = load i32 addrspace(1)* %5, align 4
+ %7 = getelementptr inbounds i32 addrspace(1)* %rhs, i64 %4
+ %8 = load i32 addrspace(1)* %7, align 4
+ %9 = sub i32 %6, %8
+ %10 = bitcast i32* %1 to i8*
+ call void @llvm.lifetime.start(i64 -1, i8* %10)
+ store i32 %9, i32* %1
+ %11 = load i32* %1
+ call void @llvm.lifetime.end(i64 -1, i8* %10)
+ %12 = getelementptr inbounds i32 addrspace(1)* %res, i64 %4
+ store i32 %11, i32 addrspace(1)* %12, align 4
+ ret void
+}
+
+; Function Attrs: nounwind
+declare void @llvm.lifetime.start(i64, i8* nocapture) #0
+
+; Function Attrs: nounwind
+declare void @llvm.lifetime.end(i64, i8* nocapture) #0
+
+; Function Attrs: nounwind readnone
+declare spir_func i64 @_Z13get_global_idj(i32) #1
+
+attributes #0 = { nounwind }
+attributes #1 = { nounwind readnone }
+
+!opencl.kernels = !{!0}
+!opencl.enable.FP_CONTRACT = !{}
+!spirv.Source = !{!6}
+!opencl.spir.version = !{!7}
+!opencl.ocl.version = !{!7}
+!opencl.used.extensions = !{!8}
+!opencl.used.optional.core.features = !{!8}
+!spirv.Generator = !{!9}
+
+!0 = !{void (i32 addrspace(1)*, i32 addrspace(1)*, i32 addrspace(1)*)* @lifetime_simple, !1, !2, !3, !4, !5}
+!1 = !{!"kernel_arg_addr_space", i32 1, i32 1, i32 1}
+!2 = !{!"kernel_arg_access_qual", !"none", !"none", !"none"}
+!3 = !{!"kernel_arg_type", !"int*", !"int*", !"int*"}
+!4 = !{!"kernel_arg_type_qual", !"", !"", !""}
+!5 = !{!"kernel_arg_base_type", !"int*", !"int*", !"int*"}
+!6 = !{i32 3, i32 102000}
+!7 = !{i32 1, i32 2}
+!8 = !{}
+!9 = !{i16 7, i16 0}