//===- 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. // //===----------------------------------------------------------------------===// #include "SPIRVLowerMemmove.h" #include "SPIRVInternal.h" #include "libSPIRV/SPIRVDebug.h" #include "llvm/Analysis/TargetTransformInfo.h" #include "llvm/IR/IRBuilder.h" #include "llvm/Transforms/Utils/LowerMemIntrinsics.h" #define DEBUG_TYPE "spvmemmove" using namespace llvm; using namespace SPIRV; namespace SPIRV { void SPIRVLowerMemmoveBase::LowerMemMoveInst(MemMoveInst &I) { // There is no direct equivalent of @llvm.memmove in SPIR-V and the closest // instructions are 'OpCopyMemory' and 'OpCopyMemorySized'. // // 'OpCopyMemory' does not accept amount of bytes to copy and infers that // from type which is being copied; also it only allows to copy value of a // particular type to pointer pointing to the same type. // // 'OpCopyMemorySized' is closer to @llvm.memmove, because it actually // copies bytes, but unlike memove it is not explicitly specified whether it // supports overlapping source and destination. Therefore, we replace // memmove with two 'OpCopyMemorySized' instructions: the first one copies // bytes from source to a temporary location, the second one copies bytes // from that temporary location to the destination. IRBuilder<> Builder(I.getParent()); Builder.SetInsertPoint(&I); auto *Length = cast(I.getLength()); auto *AllocaTy = ArrayType::get(IntegerType::getInt8Ty(*Context), Length->getZExtValue()); MaybeAlign SrcAlign = I.getSourceAlign(); AllocaInst *Alloca; { IRBuilderBase::InsertPointGuard IG(Builder); Builder.SetInsertPointPastAllocas(I.getParent()->getParent()); Alloca = Builder.CreateAlloca(AllocaTy); if (SrcAlign.has_value()) Alloca->setAlignment(SrcAlign.value()); } // FIXME: Do we need to pass the size of alloca here? From LangRef: // > The first argument is a constant integer representing the size of the // > object, or -1 if it is variable sized. // // https://llvm.org/docs/LangRef.html#llvm-lifetime-start-intrinsic Builder.CreateLifetimeStart(Alloca); Builder.CreateMemCpy(Alloca, SrcAlign, I.getRawSource(), SrcAlign, Length, I.isVolatile()); auto *SecondCpy = Builder.CreateMemCpy(I.getRawDest(), I.getDestAlign(), Alloca, SrcAlign, Length, I.isVolatile()); Builder.CreateLifetimeEnd(Alloca); SecondCpy->takeName(&I); I.replaceAllUsesWith(SecondCpy); I.dropAllReferences(); I.eraseFromParent(); } bool SPIRVLowerMemmoveBase::expandMemMoveIntrinsicUses(Function &F) { bool Changed = false; for (User *U : make_early_inc_range(F.users())) { MemMoveInst *Inst = cast(U); if (!isa(Inst->getLength())) { expandMemMoveAsLoop(Inst, TargetTransformInfo(F.getParent()->getDataLayout())); Inst->eraseFromParent(); } else { LowerMemMoveInst(*Inst); } Changed = true; } return Changed; } bool SPIRVLowerMemmoveBase::runLowerMemmove(Module &M) { Context = &M.getContext(); bool Changed = false; for (Function &F : M) { if (!F.isDeclaration()) continue; if (F.getIntrinsicID() == Intrinsic::memmove) Changed |= expandMemMoveIntrinsicUses(F); } verifyRegularizationPass(M, "SPIRVLowerMemmove"); return Changed; } llvm::PreservedAnalyses SPIRVLowerMemmovePass::run(llvm::Module &M, llvm::ModuleAnalysisManager &MAM) { return runLowerMemmove(M) ? llvm::PreservedAnalyses::none() : llvm::PreservedAnalyses::all(); } SPIRVLowerMemmoveLegacy::SPIRVLowerMemmoveLegacy() : ModulePass(ID) { initializeSPIRVLowerMemmoveLegacyPass(*PassRegistry::getPassRegistry()); } bool SPIRVLowerMemmoveLegacy::runOnModule(Module &M) { return runLowerMemmove(M); } char SPIRVLowerMemmoveLegacy::ID = 0; } // namespace SPIRV INITIALIZE_PASS(SPIRVLowerMemmoveLegacy, "spvmemmove", "Lower llvm.memmove into llvm.memcpy", false, false) ModulePass *llvm::createSPIRVLowerMemmoveLegacy() { return new SPIRVLowerMemmoveLegacy(); }