summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexey Sotkin <alexey.sotkin@intel.com>2017-05-25 17:21:59 +0300
committerYaxun (Sam) Liu <yaxun.liu@amd.com>2017-05-25 10:21:59 -0400
commitf8b02714c896d992541190b3ef0f08273e1ea92f (patch)
treeaaef729040044a7d96d21d12e6f303f0477a1aeb
parentcb7751bd34fa30691abfe9df7696da3dd88034c0 (diff)
Changing translation of llvm.memset intrinsic (#217)
In most cases llvm.memset is emitted by Clang to do zero-initialization in default constructors. With this commit this case is handled as followed: 1. Create global variable initialized by a null constant array. Type of array's element is i8, length of the array is `len` operand of the llvm.memset intrinsic. 2. Copy the array to the target by OpCopyMemorySized, keeping alignment and isvolatile arguments. Translation from SPIR-V to LLVM is updated as well. Fixes #203
-rw-r--r--lib/SPIRV/SPIRVReader.cpp32
-rw-r--r--lib/SPIRV/SPIRVWriter.cpp42
-rw-r--r--lib/SPIRV/libSPIRV/SPIRVEntry.h5
-rw-r--r--lib/SPIRV/libSPIRV/SPIRVInstruction.h1
-rw-r--r--test/llvm.memset.ll42
-rw-r--r--test/transcoding/llvm.memset.ll64
6 files changed, 125 insertions, 61 deletions
diff --git a/lib/SPIRV/SPIRVReader.cpp b/lib/SPIRV/SPIRVReader.cpp
index 651a500..a8d9f08 100644
--- a/lib/SPIRV/SPIRVReader.cpp
+++ b/lib/SPIRV/SPIRVReader.cpp
@@ -1661,11 +1661,34 @@ SPIRVToLLVM::transValueWithoutDecoration(SPIRVValue *BV, Function *F,
Type* SrcTy = transType(BS);
Type* TrgTy = transType(BT);
Type* SizeTy = transType(BC->getSize()->getType());
- Type* ArgTy[] = { TrgTy, SrcTy, SizeTy, Int32Ty, Int1Ty };
ostringstream TempName;
- TempName << ".p" << SPIRSPIRVAddrSpaceMap::rmap(BT->getPointerStorageClass()) << "i8";
- TempName << ".p" << SPIRSPIRVAddrSpaceMap::rmap(BS->getPointerStorageClass()) << "i8";
+ TempName << ".p"
+ << SPIRSPIRVAddrSpaceMap::rmap(BT->getPointerStorageClass())
+ << "i8";
+ Value *Src = nullptr;
+ // If we copy from zero-initialized array, we can optimize it to llvm.memset
+ if (BC->getSource()->isVariable()) {
+ auto *Init = static_cast<SPIRVVariable*>(BC->getSource())->getInitializer();
+ if (isa<OpConstantNull>(Init)) {
+ SPIRVType *Ty = static_cast<SPIRVConstantNull*>(Init)->getType();
+ if (isa<OpTypeArray>(Ty)) {
+ SPIRVTypeArray *AT = static_cast<SPIRVTypeArray*>(Ty);
+ SrcTy = transType(AT->getArrayElementType());
+ assert(SrcTy->isIntegerTy(8));
+ Src = ConstantInt::get(SrcTy, 0);
+ FuncName = "llvm.memset";
+ }
+ }
+ }
+ if (!Src) {
+ Src = transValue(BC->getSource(), F, BB);
+ TempName << ".p"
+ << SPIRSPIRVAddrSpaceMap::rmap(BS->getPointerStorageClass())
+ << "i8";
+ }
+ Type* ArgTy[] = { TrgTy, SrcTy, SizeTy, Int32Ty, Int1Ty };
+
FuncName += TempName.str();
if (BC->getSize()->getType()->getBitWidth() == 32)
FuncName += ".i32";
@@ -1680,8 +1703,7 @@ SPIRVToLLVM::transValueWithoutDecoration(SPIRVValue *BV, Function *F,
if (isFuncNoUnwind())
Func->addFnAttr(Attribute::NoUnwind);
- Value *Arg[] = { transValue(BC->getTarget(), Func, BB),
- transValue(BC->getSource(), Func, BB),
+ Value *Arg[] = { transValue(BC->getTarget(), Func, BB), Src,
dyn_cast<llvm::ConstantInt>(transValue(BC->getSize(),
Func, BB)),
ConstantInt::get(Int32Ty,
diff --git a/lib/SPIRV/SPIRVWriter.cpp b/lib/SPIRV/SPIRVWriter.cpp
index de7c1ca..44e92b3 100644
--- a/lib/SPIRV/SPIRVWriter.cpp
+++ b/lib/SPIRV/SPIRVWriter.cpp
@@ -1278,22 +1278,36 @@ LLVMToSPIRV::transIntrinsicInst(IntrinsicInst *II, SPIRVBasicBlock *BB) {
transValue(II->getArgOperand(2), BB), BB);
}
case Intrinsic::memset : {
- // If memset is used for zero-initialization, find a variable which is being
- // initialized and store null constant of the same type to this variable
+ // Generally memset can't be translated with current version of SPIRV spec.
+ // But in most cases it turns out that memset is emited by Clang to do
+ // zero-initializtion in default constructors.
+ // The code below handles only cases with val = 0 and constant len.
MemSetInst *MSI = cast<MemSetInst>(II);
- AllocaInst *AI = dyn_cast<AllocaInst>(MSI->getDest());
- ConstantInt *Val = dyn_cast<ConstantInt>(MSI->getValue());
- ConstantInt *Len = dyn_cast<ConstantInt>(MSI->getLength());
- if (AI && Val && Val->isZero() && Len &&
- AI->getAlignment() == MSI->getAlignment() && Len->getZExtValue()*8 ==
- M->getDataLayout()->getTypeSizeInBits(AI->getAllocatedType())) {
- SPIRVValue *Var = transValue(MSI->getDest(), BB);
- assert(Var && Var->isVariable());
- auto *VarTy = static_cast<SPIRVTypePointer *>(Var->getType());
- return BM->addStoreInst(Var, BM->addNullConstant(VarTy->getElementType()),
- getMemoryAccess(MSI), BB);
+ Value *Val = MSI->getValue();
+ if (!isa<Constant>(Val)) {
+ assert(!"Can't translate llvm.memset with non-const `value` argument");
+ return nullptr;
}
- assert(!"Can't translate llvm.memset with non-zero value argument");
+ if (!cast<Constant>(Val)->isZeroValue()) {
+ assert(!"Can't translate llvm.memset with non-zero `value` argument");
+ return nullptr;
+ }
+ Value *Len = MSI->getLength();
+ if (!isa<ConstantInt>(Len)) {
+ assert(!"Can't translate llvm.memset with non-const `length` argument");
+ return nullptr;
+ }
+ uint64_t NumElements = static_cast<ConstantInt*>(Len)->getZExtValue();
+ auto *AT = ArrayType::get(Val->getType(), NumElements);
+ SPIRVTypeArray *CompositeTy = static_cast<SPIRVTypeArray*>(transType(AT));
+ SPIRVValue *Init = BM->addNullConstant(CompositeTy);
+ SPIRVType *VarTy = transType(PointerType::get(AT, SPIRV::SPIRAS_Constant));
+ SPIRVValue *Source = BM->addVariable(VarTy,/*isConstant*/true,
+ spv::LinkageTypeInternal, Init, "",
+ StorageClassUniformConstant, nullptr);
+ SPIRVValue *Target = transValue(MSI->getRawDest(), BB);
+ return BM->addCopyMemorySizedInst(Target, Source, CompositeTy->getLength(),
+ getMemoryAccess(MSI), BB);
}
break;
case Intrinsic::memcpy :
diff --git a/lib/SPIRV/libSPIRV/SPIRVEntry.h b/lib/SPIRV/libSPIRV/SPIRVEntry.h
index 3355e97..004e3de 100644
--- a/lib/SPIRV/libSPIRV/SPIRVEntry.h
+++ b/lib/SPIRV/libSPIRV/SPIRVEntry.h
@@ -696,6 +696,11 @@ T* bcast(SPIRVEntry *E) {
return static_cast<T*>(E);
}
+template<spv::Op OC>
+bool isa(SPIRVEntry *E) {
+ return E->getOpCode() == OC;
+}
+
// ToDo: The following typedef's are place holders for SPIRV entity classes
// to be implemented.
// Each time a new class is implemented, remove the corresponding typedef.
diff --git a/lib/SPIRV/libSPIRV/SPIRVInstruction.h b/lib/SPIRV/libSPIRV/SPIRVInstruction.h
index bda2eff..d8cfa50 100644
--- a/lib/SPIRV/libSPIRV/SPIRVInstruction.h
+++ b/lib/SPIRV/libSPIRV/SPIRVInstruction.h
@@ -495,6 +495,7 @@ protected:
SPIRVValue::validate();
assert(isValid(StorageClass));
assert(Initializer.size() == 1 || Initializer.empty());
+ assert(getType()->isTypePointer());
}
void setWordCount(SPIRVWord TheWordCount) {
SPIRVEntry::setWordCount(TheWordCount);
diff --git a/test/llvm.memset.ll b/test/llvm.memset.ll
deleted file mode 100644
index 2f67c67..0000000
--- a/test/llvm.memset.ll
+++ /dev/null
@@ -1,42 +0,0 @@
-; RUN: llvm-as %s -o %t.bc
-; RUN: llvm-spirv %t.bc -spirv-text -o - | FileCheck %s
-
-; CHECK-NOT: llvm.memset
-
-; CHECK: TypeInt [[int32:[0-9]+]] 32
-; CHECK: Constant [[int32]] [[fiftieen:[0-9]+]] 15
-; CHECK: TypeArray [[int32x15:[0-9]+]] [[int32]] [[fiftieen]]
-; CHECK: TypePointer [[int32x15Ptr:[0-9]+]] 7 [[int32x15]]
-; CHECK: ConstantNull [[int32x15]] [[ConstantNull:[0-9]+]]
-
-; CHECK: Variable [[int32x15Ptr:[0-9]+]] [[mem:[0-9]+]] 7
-; CHECK: Store [[mem]] [[ConstantNull]]
-
-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-n8:16:32:64"
-target triple = "spir"
-
-; Function Attrs: nounwind
-define spir_func void @test() #0 {
-entry:
- %mem = alloca [15 x i32], align 4
- %0 = bitcast [15 x i32]* %mem to i8*
- call void @llvm.memset.p0i8.i32(i8* %0, i8 0, i32 60, i32 4, i1 false)
- ret void
-}
-
-; Function Attrs: nounwind
-declare void @llvm.memset.p0i8.i32(i8* nocapture, i8, i32, i32, i1) #1
-
-attributes #0 = { nounwind "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-realign-stack" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }
-attributes #1 = { nounwind }
-
-!opencl.enable.FP_CONTRACT = !{}
-!opencl.spir.version = !{!0}
-!opencl.ocl.version = !{!1}
-!opencl.used.extensions = !{!2}
-!opencl.used.optional.core.features = !{!2}
-!opencl.compiler.options = !{!2}
-
-!0 = !{i32 1, i32 2}
-!1 = !{i32 2, i32 0}
-!2 = !{}
diff --git a/test/transcoding/llvm.memset.ll b/test/transcoding/llvm.memset.ll
new file mode 100644
index 0000000..22eb041
--- /dev/null
+++ b/test/transcoding/llvm.memset.ll
@@ -0,0 +1,64 @@
+;; struct S1
+;; {
+;; int x;
+;; int y;
+;; int z;
+;; };
+;; S1 foo11()
+;; {
+;; return S1();
+;; }
+
+; RUN: llvm-as %s -o %t.bc
+; RUN: llvm-spirv %t.bc -spirv-text -o %t.spt
+; RUN: FileCheck < %t.spt %s --check-prefix=CHECK-SPIRV
+; RUN: llvm-spirv %t.bc -o %t.spv
+; RUN: llvm-spirv -r %t.spv -o %t.rev.bc
+; RUN: llvm-dis < %t.rev.bc | FileCheck %s --check-prefix=CHECK-LLVM
+
+; CHECK-SPIRV: TypeInt [[Int8:[0-9]+]] 8 0
+; CHECK-SPIRV: Constant {{[0-9]+}} [[Len:[0-9]+]] 12
+; CHECK-SPIRV: TypePointer [[Int8Ptr:[0-9]+]] 8 [[Int8]]
+; CHECK-SPIRV: TypeArray [[Int8x12:[0-9]+]] [[Int8]] [[Len]]
+; CHECK-SPIRV: ConstantNull [[Int8x12]] [[Init:[0-9]+]]
+; CHECK-SPIRV: Variable {{[0-9]+}} [[Source:[0-9]+]] 0 [[Init]]
+
+; CHECK-SPIRV: Bitcast [[Int8Ptr]] [[Target:[0-9]+]] {{[0-9]+}}
+; CHECK-SPIRV: CopyMemorySized [[Target]] [[Source]] [[Len]] 2 4
+
+
+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-n8:16:32:64"
+target triple = "spir"
+
+%struct.S1 = type { i32, i32, i32 }
+
+; Function Attrs: nounwind
+define spir_func void @_Z5foo11v(%struct.S1 addrspace(4)* noalias nocapture sret %agg.result) #0 {
+ %1 = bitcast %struct.S1 addrspace(4)* %agg.result to i8 addrspace(4)*
+ tail call void @llvm.memset.p4i8.i32(i8 addrspace(4)* %1, i8 0, i32 12, i32 4, i1 false)
+; CHECK-LLVM: call void @llvm.memset.p4i8.i32(i8 addrspace(4)* %1, i8 0, i32 12, i32 4, i1 false)
+ ret void
+}
+
+; Function Attrs: nounwind
+declare void @llvm.memset.p4i8.i32(i8 addrspace(4)* nocapture, i8, i32, i32, i1) #1
+
+attributes #0 = { nounwind "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-realign-stack" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #1 = { nounwind }
+
+!opencl.enable.FP_CONTRACT = !{}
+!opencl.spir.version = !{!0}
+!opencl.ocl.version = !{!1}
+!opencl.used.extensions = !{!2}
+!opencl.used.optional.core.features = !{!2}
+!opencl.compiler.options = !{!2}
+!llvm.ident = !{!3}
+!spirv.Source = !{!4}
+!spirv.String = !{!5}
+
+!0 = !{i32 1, i32 2}
+!1 = !{i32 2, i32 2}
+!2 = !{}
+!3 = !{!"clang version 3.6.1 "}
+!4 = !{i32 4, i32 202000, !5}
+!5 = !{!"llvm.memset.cl"}