summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordoe300 <doe300@users.noreply.github.com>2017-06-29 20:38:40 +0200
committerYaxun (Sam) Liu <yaxun.liu@amd.com>2017-06-29 14:38:40 -0400
commitec2026af3091333bef812957109e6217af4ba94a (patch)
treeaf9d7d10c80dc7d929a9010635df52a45b9f8eaa
parentf8b02714c896d992541190b3ef0f08273e1ea92f (diff)
Squashed the lower-memmove pass (#209)
-rw-r--r--include/SPIRV.h12
-rw-r--r--lib/SPIRV/CMakeLists.txt1
-rw-r--r--lib/SPIRV/SPIRVLowerMemmove.cpp139
-rw-r--r--lib/SPIRV/SPIRVWriter.cpp1
-rw-r--r--lib/SPIRV/libSPIRV/SPIRVInstruction.h3
-rw-r--r--test/transcoding/llvm.memmove.ll70
6 files changed, 220 insertions, 6 deletions
diff --git a/include/SPIRV.h b/include/SPIRV.h
index 4029eed..605e042 100644
--- a/include/SPIRV.h
+++ b/include/SPIRV.h
@@ -56,6 +56,7 @@ void initializeOCLTypeToSPIRVPass(PassRegistry&);
void initializeSPIRVLowerBoolPass(PassRegistry&);
void initializeSPIRVLowerConstExprPass(PassRegistry&);
void initializeSPIRVLowerOCLBlocksPass(PassRegistry&);
+void initializeSPIRVLowerMemmovePass(PassRegistry&);
void initializeSPIRVRegularizeLLVMPass(PassRegistry&);
void initializeSPIRVToOCL20Pass(PassRegistry&);
void initializeTransOCLMDPass(PassRegistry&);
@@ -135,6 +136,9 @@ ModulePass *createSPIRVLowerConstExpr();
/// Create a pass for lowering OCL 2.0 blocks to functions calls.
ModulePass *createSPIRVLowerOCLBlocks();
+/// Create a pass for lowering llvm.memmove to llvm.memcpys with a temporary variable.
+ModulePass *createSPIRVLowerMemmove();
+
/// Create a pass for regularize LLVM module to be translated to SPIR-V.
ModulePass *createSPIRVRegularizeLLVM();
@@ -146,10 +150,10 @@ ModulePass *createSPIRVToOCL20();
/// metadata.
ModulePass *createTransOCLMD();
-/// Create and return a pass that writes the module to the specified
-/// ostream.
-ModulePass *createSPIRVWriterPass(llvm::raw_ostream &Str);
-
+/// Create and return a pass that writes the module to the specified
+/// ostream.
+ModulePass *createSPIRVWriterPass(llvm::raw_ostream &Str);
+
} // namespace llvm
diff --git a/lib/SPIRV/CMakeLists.txt b/lib/SPIRV/CMakeLists.txt
index 9d28865..d313439 100644
--- a/lib/SPIRV/CMakeLists.txt
+++ b/lib/SPIRV/CMakeLists.txt
@@ -30,6 +30,7 @@ add_llvm_library(LLVMSPIRVLib
SPIRVLowerBool.cpp
SPIRVLowerConstExpr.cpp
SPIRVLowerOCLBlocks.cpp
+ SPIRVLowerMemmove.cpp
SPIRVReader.cpp
SPIRVRegularizeLLVM.cpp
SPIRVToOCL20.cpp
diff --git a/lib/SPIRV/SPIRVLowerMemmove.cpp b/lib/SPIRV/SPIRVLowerMemmove.cpp
new file mode 100644
index 0000000..cd9056c
--- /dev/null
+++ b/lib/SPIRV/SPIRVLowerMemmove.cpp
@@ -0,0 +1,139 @@
+//===- SPIRVLowerMemmove.cpp - Lower llvm.memmove to llvm.memcpys ----------===//
+//
+// The LLVM/SPIRV Translator
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+// Copyright (c) 2014 Advanced Micro Devices, Inc. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the "Software"),
+// to deal with the Software without restriction, including without limitation
+// the rights to use, copy, modify, merge, publish, distribute, sublicense,
+// and/or sell copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following conditions:
+//
+// Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimers.
+// Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimers in the documentation
+// and/or other materials provided with the distribution.
+// Neither the names of Advanced Micro Devices, Inc., nor the names of its
+// contributors may be used to endorse or promote products derived from this
+// Software without specific prior written permission.
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH
+// THE SOFTWARE.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements lowering llvm.memmove into several llvm.memcpys.
+//
+//===----------------------------------------------------------------------===//
+#define DEBUG_TYPE "spvmemmove"
+
+#include "SPIRVInternal.h"
+#include "llvm/IR/InstVisitor.h"
+#include "llvm/IR/IntrinsicInst.h"
+#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/Verifier.h"
+#include "llvm/Pass.h"
+#include "llvm/PassSupport.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace llvm;
+using namespace SPIRV;
+
+namespace SPIRV {
+cl::opt<bool> SPIRVLowerMemmoveValidate("spvmemmove-validate",
+ cl::desc("Validate module after lowering llvm.memmove instructions into "
+ "llvm.memcpy"));
+
+class SPIRVLowerMemmove: public ModulePass,
+ public InstVisitor<SPIRVLowerMemmove> {
+public:
+ SPIRVLowerMemmove():ModulePass(ID), Context(nullptr) {
+ initializeSPIRVLowerMemmovePass(*PassRegistry::getPassRegistry());
+ }
+ virtual void visitMemMoveInst(MemMoveInst &I) {
+ IRBuilder<> Builder(I.getParent());
+ Builder.SetInsertPoint(&I);
+ auto *Dest = I.getRawDest();
+ auto *Src = I.getRawSource();
+ auto *SrcTy = Src->getType();
+ if (!isa<ConstantInt>(I.getLength()))
+ // ToDo: for non-constant length, could use a loop to copy a
+ // fixed length chunk at a time. For now simply fail
+ report_fatal_error("llvm.memmove of non-constant length not supported",
+ false);
+ auto *Length = cast<ConstantInt>(I.getLength());
+ if (isa<BitCastInst>(Src))
+ // The source could be bit-cast from another type,
+ // need the original type for the allocation of the temporary variable
+ SrcTy = cast<BitCastInst>(Src)->getOperand(0)->getType();
+ auto Align = I.getAlignment();
+ auto Volatile = I.isVolatile();
+ Value *NumElements = nullptr;
+ uint64_t ElementsCount = 1;
+ if (SrcTy->isArrayTy()) {
+ NumElements = Builder.getInt32(SrcTy->getArrayNumElements());
+ ElementsCount = SrcTy->getArrayNumElements();
+ }
+ if (Mod->getDataLayout()->getTypeSizeInBits(SrcTy->getPointerElementType())
+ * ElementsCount != Length->getZExtValue() * 8)
+ report_fatal_error("Size of the memcpy should match the allocated memory",
+ false);
+
+ auto *Alloca = Builder.CreateAlloca(SrcTy->getPointerElementType(),
+ NumElements);
+ auto *LifetimeStart = Builder.CreateLifetimeStart(Alloca);
+ auto *FirstCpy = Builder.CreateMemCpy(Alloca, Src, Length, Align, Volatile);
+ auto *SecondCpy = Builder.CreateMemCpy(Dest, Alloca, Length, Align,
+ Volatile);
+ auto *LifetimeEnd = Builder.CreateLifetimeEnd(Alloca);
+
+ SecondCpy->takeName(&I);
+ I.replaceAllUsesWith(SecondCpy);
+ I.dropAllReferences();
+ I.eraseFromParent();
+ }
+ virtual bool runOnModule(Module &M) {
+ Context = &M.getContext();
+ Mod = &M;
+ visit(M);
+
+ if (SPIRVLowerMemmoveValidate) {
+ DEBUG(dbgs() << "After SPIRVLowerMemmove:\n" << M);
+ std::string Err;
+ raw_string_ostream ErrorOS(Err);
+ if (verifyModule(M, &ErrorOS)){
+ Err = std::string("Fails to verify module: ") + Err;
+ report_fatal_error(Err.c_str(), false);
+ }
+ }
+ return true;
+ }
+
+ static char ID;
+private:
+ LLVMContext *Context;
+ Module *Mod;
+};
+
+char SPIRVLowerMemmove::ID = 0;
+}
+
+INITIALIZE_PASS(SPIRVLowerMemmove, "spvmemmove",
+ "Lower llvm.memmove into llvm.memcpy", false, false)
+
+ModulePass *llvm::createSPIRVLowerMemmove() {
+ return new SPIRVLowerMemmove();
+}
diff --git a/lib/SPIRV/SPIRVWriter.cpp b/lib/SPIRV/SPIRVWriter.cpp
index 44e92b3..9bc2937 100644
--- a/lib/SPIRV/SPIRVWriter.cpp
+++ b/lib/SPIRV/SPIRVWriter.cpp
@@ -1838,6 +1838,7 @@ addPassesForSPIRV(PassManager &PassMgr) {
PassMgr.add(createSPIRVRegularizeLLVM());
PassMgr.add(createSPIRVLowerConstExpr());
PassMgr.add(createSPIRVLowerBool());
+ PassMgr.add(createSPIRVLowerMemmove());
}
bool
diff --git a/lib/SPIRV/libSPIRV/SPIRVInstruction.h b/lib/SPIRV/libSPIRV/SPIRVInstruction.h
index d8cfa50..a3e0169 100644
--- a/lib/SPIRV/libSPIRV/SPIRVInstruction.h
+++ b/lib/SPIRV/libSPIRV/SPIRVInstruction.h
@@ -1878,8 +1878,7 @@ protected:
assert(Obj->getStorageClass() == StorageClassFunction &&
"Invalid storage class");
assert(Obj->getType()->isTypePointer() &&
- Obj->getType()->getPointerElementType()->isTypeInt() &&
- "Object type must be an integer type scalar");
+ "Objects type must be a pointer");
if (!Obj->getType()->getPointerElementType()->isTypeVoid() ||
!Module->hasCapability(CapabilityAddresses))
assert(Size == 0 && "Size must be 0");
diff --git a/test/transcoding/llvm.memmove.ll b/test/transcoding/llvm.memmove.ll
new file mode 100644
index 0000000..62283bf
--- /dev/null
+++ b/test/transcoding/llvm.memmove.ll
@@ -0,0 +1,70 @@
+; RUN: llvm-as %s -o %t.bc
+; RUN: llvm-spirv %t.bc -spirv-text -o %t.txt
+; RUN: FileCheck < %t.txt %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-NOT: llvm.memmove
+
+; CHECK-SPIRV: Variable {{[0-9]+}} [[mem:[0-9]+]] 7
+
+; CHECK-SPIRV: LifetimeStart [[mem]] [[size:[0-9]+]]
+; CHECK-SPIRV: Bitcast [[i8Ty:[0-9]+]] [[tmp1:[0-9]+]] [[mem]]
+; CHECK-SPIRV: CopyMemorySized [[tmp1]] {{[0-9]+}} {{[0-9]+}}
+; CHECK-SPIRV: Bitcast [[i8Ty]] [[tmp2:[0-9]+]] [[mem]]
+; CHECK-SPIRV: CopyMemorySized {{[0-9]+}} [[tmp2]] {{[0-9]+}}
+; CHECK-SPIRV: LifetimeStop [[mem]] [[size]]
+
+; CHECK-LLVM-NOT: llvm.memmove
+
+; CHECK-LLVM: [[local:%[0-9]+]] = alloca %struct.SomeStruct
+; CHECK-LLVM: [[tmp1:%[0-9]+]] = bitcast %struct.SomeStruct* [[local]] to [[type:i[0-9]+\*]]
+; CHECK-LLVM: call void @llvm.lifetime.start({{i[0-9]+}} {{-?[0-9]+}}, [[type]] [[tmp1]])
+; CHECK-LLVM: [[tmp2:%[0-9]+]] = bitcast %struct.SomeStruct* [[local]] to [[type]]
+; CHECK-LLVM: call void @llvm.memcpy
+; CHECK-LLVM: ([[type]] [[tmp2]],
+; CHECK-LLVM: {{i[0-9]+}} [[size:[0-9]+]]
+; CHECK-LLVM: [[tmp3:%[0-9]+]] = bitcast %struct.SomeStruct* [[local]] to [[type]]
+; CHECK-LLVM: call void @llvm.memcpy
+; CHECK-LLVM: , [[type]] [[tmp3]], {{i[0-9]+}} [[size]]
+; CHECK-LLVM: call void @llvm.lifetime.end({{i[0-9]+}} {{-?[0-9]+}}, [[type]] [[tmp1]])
+
+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-unknown-unknown"
+
+%struct.SomeStruct = type { <16 x float>, i32, [60 x i8] }
+
+; Function Attrs: nounwind
+define spir_kernel void @test_struct(%struct.SomeStruct addrspace(1)* nocapture readonly %in, %struct.SomeStruct addrspace(1)* nocapture %out) #0 {
+ %1 = bitcast %struct.SomeStruct addrspace(1)* %in to i8 addrspace(1)*
+ %2 = bitcast %struct.SomeStruct addrspace(1)* %out to i8 addrspace(1)*
+ call void @llvm.memmove.p1i8.p1i8.i32(i8 addrspace(1)* %2, i8 addrspace(1)* %1, i32 128, i32 64, i1 false)
+ ret void
+}
+
+; Function Attrs: nounwind
+declare void @llvm.memmove.p1i8.p1i8.i32(i8 addrspace(1)* nocapture, i8 addrspace(1)* nocapture readonly, 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.kernels = !{!0}
+!opencl.enable.FP_CONTRACT = !{}
+!opencl.spir.version = !{!7}
+!opencl.ocl.version = !{!7}
+!opencl.used.extensions = !{!8}
+!opencl.used.optional.core.features = !{!8}
+!opencl.compiler.options = !{!8}
+!llvm.ident = !{!9}
+
+!0 = !{void (%struct.SomeStruct addrspace(1)*, %struct.SomeStruct addrspace(1)*)* @test_struct, !1, !2, !3, !4, !5, !6}
+!1 = !{!"kernel_arg_addr_space", i32 1, i32 1}
+!2 = !{!"kernel_arg_access_qual", !"none", !"none"}
+!3 = !{!"kernel_arg_type", !"struct SomeStruct*", !"struct SomeStruct*"}
+!4 = !{!"kernel_arg_base_type", !"struct SomeStruct*", !"struct SomeStruct*"}
+!5 = !{!"kernel_arg_type_qual", !"const", !""}
+!6 = !{!"kernel_arg_name", !"in", !"out"}
+!7 = !{i32 1, i32 2}
+!8 = !{}
+!9 = !{!"clang version 3.6.1 (https://github.com/KhronosGroup/SPIR d7e44c3b27581e54ca0e522987d1ade2bd29b70d) (https://github.com/KhronosGroup/SPIRV-LLVM.git d42743684ea8338358504e44ef8363b9dc675c66)"}