diff options
author | doe300 <doe300@users.noreply.github.com> | 2017-06-29 20:38:40 +0200 |
---|---|---|
committer | Yaxun (Sam) Liu <yaxun.liu@amd.com> | 2017-06-29 14:38:40 -0400 |
commit | ec2026af3091333bef812957109e6217af4ba94a (patch) | |
tree | af9d7d10c80dc7d929a9010635df52a45b9f8eaa | |
parent | f8b02714c896d992541190b3ef0f08273e1ea92f (diff) |
Squashed the lower-memmove pass (#209)
-rw-r--r-- | include/SPIRV.h | 12 | ||||
-rw-r--r-- | lib/SPIRV/CMakeLists.txt | 1 | ||||
-rw-r--r-- | lib/SPIRV/SPIRVLowerMemmove.cpp | 139 | ||||
-rw-r--r-- | lib/SPIRV/SPIRVWriter.cpp | 1 | ||||
-rw-r--r-- | lib/SPIRV/libSPIRV/SPIRVInstruction.h | 3 | ||||
-rw-r--r-- | test/transcoding/llvm.memmove.ll | 70 |
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)"} |