//===- LowerMemIntrinsics.cpp ----------------------------------*- C++ -*--===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/Transforms/Utils/LowerMemIntrinsics.h" #include "llvm/Analysis/ScalarEvolution.h" #include "llvm/Analysis/TargetTransformInfo.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/MDBuilder.h" #include "llvm/IR/ProfDataUtils.h" #include "llvm/ProfileData/InstrProf.h" #include "llvm/Support/Debug.h" #include "llvm/Support/MathExtras.h" #include "llvm/Transforms/Utils/BasicBlockUtils.h" #include "llvm/Transforms/Utils/LoopUtils.h" #include #include #define DEBUG_TYPE "lower-mem-intrinsics" using namespace llvm; namespace llvm { extern cl::opt ProfcheckDisableMetadataFixes; } /// \returns \p Len urem \p OpSize, checking for optimization opportunities. /// \p OpSizeVal must be the integer value of the \c ConstantInt \p OpSize. static Value *getRuntimeLoopRemainder(IRBuilderBase &B, Value *Len, Value *OpSize, unsigned OpSizeVal) { // For powers of 2, we can and by (OpSizeVal - 1) instead of using urem. if (isPowerOf2_32(OpSizeVal)) return B.CreateAnd(Len, OpSizeVal - 1); return B.CreateURem(Len, OpSize); } /// \returns (\p Len udiv \p OpSize) mul \p OpSize, checking for optimization /// opportunities. /// If \p RTLoopRemainder is provided, it must be the result of /// \c getRuntimeLoopRemainder() with the same arguments. static Value *getRuntimeLoopUnits(IRBuilderBase &B, Value *Len, Value *OpSize, unsigned OpSizeVal, Value *RTLoopRemainder = nullptr) { if (!RTLoopRemainder) RTLoopRemainder = getRuntimeLoopRemainder(B, Len, OpSize, OpSizeVal); return B.CreateSub(Len, RTLoopRemainder); } namespace { /// Container for the return values of insertLoopExpansion. struct LoopExpansionInfo { /// The instruction at the end of the main loop body. Instruction *MainLoopIP = nullptr; /// The unit index in the main loop body. Value *MainLoopIndex = nullptr; /// The instruction at the end of the residual loop body. Can be nullptr if no /// residual is required. Instruction *ResidualLoopIP = nullptr; /// The unit index in the residual loop body. Can be nullptr if no residual is /// required. Value *ResidualLoopIndex = nullptr; }; std::optional getAverageMemOpLoopTripCount(const MemIntrinsic &I) { if (ProfcheckDisableMetadataFixes) return std::nullopt; if (std::optional EC = I.getFunction()->getEntryCount(); !EC || !EC->getCount()) return std::nullopt; if (const auto Len = I.getLengthInBytes()) return Len->getZExtValue(); uint64_t Total = 0; SmallVector ProfData = getValueProfDataFromInst(I, InstrProfValueKind::IPVK_MemOPSize, std::numeric_limits::max(), Total); if (!Total) return std::nullopt; uint64_t TripCount = 0; for (const auto &P : ProfData) TripCount += P.Count * P.Value; return std::round(1.0 * TripCount / Total); } } // namespace /// Insert the control flow and loop counters for a memcpy/memset loop /// expansion. /// /// This function inserts IR corresponding to the following C code before /// \p InsertBefore: /// \code /// LoopUnits = (Len / MainLoopStep) * MainLoopStep; /// ResidualUnits = Len - LoopUnits; /// MainLoopIndex = 0; /// if (LoopUnits > 0) { /// do { /// // MainLoopIP /// MainLoopIndex += MainLoopStep; /// } while (MainLoopIndex < LoopUnits); /// } /// for (size_t i = 0; i < ResidualUnits; i += ResidualLoopStep) { /// ResidualLoopIndex = LoopUnits + i; /// // ResidualLoopIP /// } /// \endcode /// /// \p MainLoopStep and \p ResidualLoopStep determine by how many "units" the /// loop index is increased in each iteration of the main and residual loops, /// respectively. In most cases, the "unit" will be bytes, but larger units are /// useful for lowering memset.pattern. /// /// The computation of \c LoopUnits and \c ResidualUnits is performed at compile /// time if \p Len is a \c ConstantInt. /// The second (residual) loop is omitted if \p ResidualLoopStep is 0 or equal /// to \p MainLoopStep. /// The generated \c MainLoopIP, \c MainLoopIndex, \c ResidualLoopIP, and /// \c ResidualLoopIndex are returned in a \c LoopExpansionInfo object. /// /// If provided, \p ExpectedUnits is used as the expected number of units /// handled by the loop expansion when computing branch weights. static LoopExpansionInfo insertLoopExpansion(Instruction *InsertBefore, Value *Len, unsigned MainLoopStep, unsigned ResidualLoopStep, StringRef BBNamePrefix, std::optional ExpectedUnits) { assert((ResidualLoopStep == 0 || MainLoopStep % ResidualLoopStep == 0) && "ResidualLoopStep must divide MainLoopStep if specified"); assert(ResidualLoopStep <= MainLoopStep && "ResidualLoopStep cannot be larger than MainLoopStep"); assert(MainLoopStep > 0 && "MainLoopStep must be non-zero"); LoopExpansionInfo LEI; // If the length is known to be zero, there is nothing to do. if (auto *CLen = dyn_cast(Len)) if (CLen->isZero()) return LEI; BasicBlock *PreLoopBB = InsertBefore->getParent(); BasicBlock *PostLoopBB = PreLoopBB->splitBasicBlock( InsertBefore, BBNamePrefix + "-post-expansion"); Function *ParentFunc = PreLoopBB->getParent(); LLVMContext &Ctx = PreLoopBB->getContext(); const DebugLoc &DbgLoc = InsertBefore->getStableDebugLoc(); IRBuilder<> PreLoopBuilder(PreLoopBB->getTerminator()); PreLoopBuilder.SetCurrentDebugLocation(DbgLoc); // Calculate the main loop trip count and remaining units to cover after the // loop. Type *LenType = Len->getType(); IntegerType *ILenType = cast(LenType); ConstantInt *CIMainLoopStep = ConstantInt::get(ILenType, MainLoopStep); ConstantInt *Zero = ConstantInt::get(ILenType, 0U); // We can avoid conditional branches and/or entire loops if we know any of the // following: // - that the main loop must be executed at least once // - that the main loop will not be executed at all // - that the residual loop must be executed at least once // - that the residual loop will not be executed at all bool MustTakeMainLoop = false; bool MayTakeMainLoop = true; bool MustTakeResidualLoop = false; bool MayTakeResidualLoop = true; Value *LoopUnits = Len; Value *ResidualUnits = nullptr; if (MainLoopStep != 1) { if (auto *CLen = dyn_cast(Len)) { uint64_t TotalUnits = CLen->getZExtValue(); uint64_t LoopEndCount = alignDown(TotalUnits, MainLoopStep); uint64_t ResidualCount = TotalUnits - LoopEndCount; LoopUnits = ConstantInt::get(LenType, LoopEndCount); ResidualUnits = ConstantInt::get(LenType, ResidualCount); MustTakeMainLoop = LoopEndCount > 0; MayTakeMainLoop = MustTakeMainLoop; MustTakeResidualLoop = ResidualCount > 0; MayTakeResidualLoop = MustTakeResidualLoop; // TODO: This could also use known bits to check if a non-constant loop // count is guaranteed to be a multiple of MainLoopStep, in which case we // could omit the residual loop. It's unclear if that is worthwhile. } else { ResidualUnits = getRuntimeLoopRemainder(PreLoopBuilder, Len, CIMainLoopStep, MainLoopStep); LoopUnits = getRuntimeLoopUnits(PreLoopBuilder, Len, CIMainLoopStep, MainLoopStep, ResidualUnits); } } else if (auto *CLen = dyn_cast(Len)) { MustTakeMainLoop = CLen->getZExtValue() > 0; MayTakeMainLoop = MustTakeMainLoop; } // The case where both loops are omitted (i.e., the length is known zero) is // already handled at the beginning of this function. assert((MayTakeMainLoop || MayTakeResidualLoop) && "At least one of the loops must be generated"); BasicBlock *MainLoopBB = nullptr; CondBrInst *MainLoopBr = nullptr; // Construct the main loop unless we statically known that it is not taken. if (MayTakeMainLoop) { MainLoopBB = BasicBlock::Create(Ctx, BBNamePrefix + "-expansion-main-body", ParentFunc, PostLoopBB); IRBuilder<> LoopBuilder(MainLoopBB); LoopBuilder.SetCurrentDebugLocation(DbgLoc); PHINode *LoopIndex = LoopBuilder.CreatePHI(LenType, 2, "loop-index"); LEI.MainLoopIndex = LoopIndex; LoopIndex->addIncoming(ConstantInt::get(LenType, 0U), PreLoopBB); Value *NewIndex = LoopBuilder.CreateAdd( LoopIndex, ConstantInt::get(LenType, MainLoopStep)); LoopIndex->addIncoming(NewIndex, MainLoopBB); // One argument of the addition is a loop-variant PHI, so it must be an // Instruction (i.e., it cannot be a Constant). LEI.MainLoopIP = cast(NewIndex); // Stay in the MainLoop until we have handled all the LoopUnits. The False // target is adjusted below if a residual is generated. MainLoopBr = LoopBuilder.CreateCondBr( LoopBuilder.CreateICmpULT(NewIndex, LoopUnits), MainLoopBB, PostLoopBB); if (ExpectedUnits.has_value()) { uint64_t BackedgeTakenCount = ExpectedUnits.value() / MainLoopStep; if (BackedgeTakenCount > 0) BackedgeTakenCount -= 1; // The last iteration goes to the False target. MDBuilder MDB(ParentFunc->getContext()); setFittedBranchWeights(*MainLoopBr, {BackedgeTakenCount, 1}, /*IsExpected=*/false); } else { setExplicitlyUnknownBranchWeightsIfProfiled(*MainLoopBr, DEBUG_TYPE); } } // Construct the residual loop if it is requested from the caller unless we // statically know that it won't be taken. bool ResidualLoopRequested = ResidualLoopStep > 0 && ResidualLoopStep < MainLoopStep; BasicBlock *ResidualLoopBB = nullptr; BasicBlock *ResidualCondBB = nullptr; if (ResidualLoopRequested && MayTakeResidualLoop) { ResidualLoopBB = BasicBlock::Create(Ctx, BBNamePrefix + "-expansion-residual-body", PreLoopBB->getParent(), PostLoopBB); // The residual loop body is either reached from the ResidualCondBB (which // checks if the residual loop needs to be executed), from the main loop // body if we know statically that the residual must be executed, or from // the pre-loop BB (conditionally or unconditionally) if the main loop is // omitted. BasicBlock *PredOfResLoopBody = PreLoopBB; if (MainLoopBB) { // If it's statically known that the residual must be executed, we don't // need to create a preheader BB. if (MustTakeResidualLoop) { MainLoopBr->setSuccessor(1, ResidualLoopBB); PredOfResLoopBody = MainLoopBB; } else { // Construct a preheader BB to check if the residual loop is executed. ResidualCondBB = BasicBlock::Create(Ctx, BBNamePrefix + "-expansion-residual-cond", PreLoopBB->getParent(), ResidualLoopBB); // Determine if we need to branch to the residual loop or bypass it. IRBuilder<> RCBuilder(ResidualCondBB); RCBuilder.SetCurrentDebugLocation(DbgLoc); auto *BR = RCBuilder.CreateCondBr(RCBuilder.CreateICmpNE(ResidualUnits, Zero), ResidualLoopBB, PostLoopBB); if (ExpectedUnits.has_value()) { MDBuilder MDB(ParentFunc->getContext()); BR->setMetadata(LLVMContext::MD_prof, MDB.createLikelyBranchWeights()); } else { setExplicitlyUnknownBranchWeightsIfProfiled(*BR, DEBUG_TYPE); } MainLoopBr->setSuccessor(1, ResidualCondBB); PredOfResLoopBody = ResidualCondBB; } } IRBuilder<> ResBuilder(ResidualLoopBB); ResBuilder.SetCurrentDebugLocation(DbgLoc); PHINode *ResidualIndex = ResBuilder.CreatePHI(LenType, 2, "residual-loop-index"); ResidualIndex->addIncoming(Zero, PredOfResLoopBody); // Add the offset at the end of the main loop to the loop counter of the // residual loop to get the proper index. If the main loop was omitted, we // can also omit the addition. if (MainLoopBB) LEI.ResidualLoopIndex = ResBuilder.CreateAdd(LoopUnits, ResidualIndex); else LEI.ResidualLoopIndex = ResidualIndex; Value *ResNewIndex = ResBuilder.CreateAdd( ResidualIndex, ConstantInt::get(LenType, ResidualLoopStep)); ResidualIndex->addIncoming(ResNewIndex, ResidualLoopBB); // One argument of the addition is a loop-variant PHI, so it must be an // Instruction (i.e., it cannot be a Constant). LEI.ResidualLoopIP = cast(ResNewIndex); // Stay in the residual loop until all ResidualUnits are handled. CondBrInst *BR = ResBuilder.CreateCondBr( ResBuilder.CreateICmpULT(ResNewIndex, ResidualUnits), ResidualLoopBB, PostLoopBB); if (ExpectedUnits.has_value()) { uint64_t BackedgeTakenCount = (ExpectedUnits.value() % MainLoopStep) / ResidualLoopStep; if (BackedgeTakenCount > 0) BackedgeTakenCount -= 1; // The last iteration goes to the False target. MDBuilder MDB(ParentFunc->getContext()); setFittedBranchWeights(*BR, {BackedgeTakenCount, 1}, /*IsExpected=*/false); } else { setExplicitlyUnknownBranchWeightsIfProfiled(*BR, DEBUG_TYPE); } } // Create the branch in the pre-loop block. if (MustTakeMainLoop) { // Go unconditionally to the main loop if it's statically known that it must // be executed. assert(MainLoopBB); PreLoopBuilder.CreateBr(MainLoopBB); } else if (!MainLoopBB && ResidualLoopBB) { if (MustTakeResidualLoop) { // If the main loop is omitted and the residual loop is statically known // to be executed, go there unconditionally. PreLoopBuilder.CreateBr(ResidualLoopBB); } else { // If the main loop is omitted and we don't know if the residual loop is // executed, go there if necessary. The PreLoopBB takes the role of the // preheader for the residual loop in this case. auto *BR = PreLoopBuilder.CreateCondBr( PreLoopBuilder.CreateICmpNE(ResidualUnits, Zero), ResidualLoopBB, PostLoopBB); if (ExpectedUnits.has_value()) { MDBuilder MDB(ParentFunc->getContext()); BR->setMetadata(LLVMContext::MD_prof, MDB.createLikelyBranchWeights()); } else { setExplicitlyUnknownBranchWeightsIfProfiled(*BR, DEBUG_TYPE); } } } else { // Otherwise, go conditionally to the main loop or its successor. // If there is no residual loop, the successor is the post-loop BB. BasicBlock *FalseBB = PostLoopBB; if (ResidualCondBB) { // If we constructed a pre-header for the residual loop, that is the // successor. FalseBB = ResidualCondBB; } else if (ResidualLoopBB) { // If there is a residual loop but the preheader is omitted (because the // residual loop is statically known to be executed), the successor // is the residual loop body. assert(MustTakeResidualLoop); FalseBB = ResidualLoopBB; } auto *BR = PreLoopBuilder.CreateCondBr( PreLoopBuilder.CreateICmpNE(LoopUnits, Zero), MainLoopBB, FalseBB); if (ExpectedUnits.has_value()) { MDBuilder MDB(ParentFunc->getContext()); BR->setMetadata(LLVMContext::MD_prof, MDB.createLikelyBranchWeights()); } else { setExplicitlyUnknownBranchWeightsIfProfiled(*BR, DEBUG_TYPE); } } // Delete the unconditional branch inserted by splitBasicBlock. PreLoopBB->getTerminator()->eraseFromParent(); return LEI; } void llvm::createMemCpyLoopKnownSize(Instruction *InsertBefore, Value *SrcAddr, Value *DstAddr, ConstantInt *CopyLen, Align SrcAlign, Align DstAlign, bool SrcIsVolatile, bool DstIsVolatile, bool CanOverlap, const TargetTransformInfo &TTI, std::optional AtomicElementSize, std::optional AverageTripCount) { // No need to expand zero length copies. if (CopyLen->isZero()) return; BasicBlock *PreLoopBB = InsertBefore->getParent(); Function *ParentFunc = PreLoopBB->getParent(); LLVMContext &Ctx = PreLoopBB->getContext(); const DataLayout &DL = ParentFunc->getDataLayout(); MDBuilder MDB(Ctx); MDNode *NewDomain = MDB.createAnonymousAliasScopeDomain("MemCopyDomain"); StringRef Name = "MemCopyAliasScope"; MDNode *NewScope = MDB.createAnonymousAliasScope(NewDomain, Name); unsigned SrcAS = cast(SrcAddr->getType())->getAddressSpace(); unsigned DstAS = cast(DstAddr->getType())->getAddressSpace(); Type *TypeOfCopyLen = CopyLen->getType(); Type *LoopOpType = TTI.getMemcpyLoopLoweringType( Ctx, CopyLen, SrcAS, DstAS, SrcAlign, DstAlign, AtomicElementSize); assert((!AtomicElementSize || !LoopOpType->isVectorTy()) && "Atomic memcpy lowering is not supported for vector operand type"); Type *Int8Type = Type::getInt8Ty(Ctx); TypeSize LoopOpSize = DL.getTypeStoreSize(LoopOpType); assert(LoopOpSize.isFixed() && "LoopOpType cannot be a scalable vector type"); assert((!AtomicElementSize || LoopOpSize % *AtomicElementSize == 0) && "Atomic memcpy lowering is not supported for selected operand size"); uint64_t LoopEndCount = alignDown(CopyLen->getZExtValue(), LoopOpSize.getFixedValue()); // Skip the loop expansion entirely if the loop would never be taken. if (LoopEndCount != 0) { LoopExpansionInfo LEI = insertLoopExpansion(InsertBefore, CopyLen, LoopOpSize, 0, "static-memcpy", AverageTripCount); assert(LEI.MainLoopIP && LEI.MainLoopIndex && "Main loop should be generated for non-zero loop count"); // Fill MainLoopBB IRBuilder<> MainLoopBuilder(LEI.MainLoopIP); Align PartDstAlign(commonAlignment(DstAlign, LoopOpSize)); Align PartSrcAlign(commonAlignment(SrcAlign, LoopOpSize)); // If we used LoopOpType as GEP element type, we would iterate over the // buffers in TypeStoreSize strides while copying TypeAllocSize bytes, i.e., // we would miss bytes if TypeStoreSize != TypeAllocSize. Therefore, use // byte offsets computed from the TypeStoreSize. Value *SrcGEP = MainLoopBuilder.CreateInBoundsGEP(Int8Type, SrcAddr, LEI.MainLoopIndex); LoadInst *Load = MainLoopBuilder.CreateAlignedLoad( LoopOpType, SrcGEP, PartSrcAlign, SrcIsVolatile); if (!CanOverlap) { // Set alias scope for loads. Load->setMetadata(LLVMContext::MD_alias_scope, MDNode::get(Ctx, NewScope)); } Value *DstGEP = MainLoopBuilder.CreateInBoundsGEP(Int8Type, DstAddr, LEI.MainLoopIndex); StoreInst *Store = MainLoopBuilder.CreateAlignedStore( Load, DstGEP, PartDstAlign, DstIsVolatile); if (!CanOverlap) { // Indicate that stores don't overlap loads. Store->setMetadata(LLVMContext::MD_noalias, MDNode::get(Ctx, NewScope)); } if (AtomicElementSize) { Load->setAtomic(AtomicOrdering::Unordered); Store->setAtomic(AtomicOrdering::Unordered); } assert(!LEI.ResidualLoopIP && !LEI.ResidualLoopIndex && "No residual loop was requested"); } // Copy the remaining bytes with straight-line code. uint64_t BytesCopied = LoopEndCount; uint64_t RemainingBytes = CopyLen->getZExtValue() - BytesCopied; if (RemainingBytes == 0) return; IRBuilder<> RBuilder(InsertBefore); SmallVector RemainingOps; TTI.getMemcpyLoopResidualLoweringType(RemainingOps, Ctx, RemainingBytes, SrcAS, DstAS, SrcAlign, DstAlign, AtomicElementSize); for (auto *OpTy : RemainingOps) { Align PartSrcAlign(commonAlignment(SrcAlign, BytesCopied)); Align PartDstAlign(commonAlignment(DstAlign, BytesCopied)); TypeSize OperandSize = DL.getTypeStoreSize(OpTy); assert((!AtomicElementSize || OperandSize % *AtomicElementSize == 0) && "Atomic memcpy lowering is not supported for selected operand size"); Value *SrcGEP = RBuilder.CreateInBoundsGEP( Int8Type, SrcAddr, ConstantInt::get(TypeOfCopyLen, BytesCopied)); LoadInst *Load = RBuilder.CreateAlignedLoad(OpTy, SrcGEP, PartSrcAlign, SrcIsVolatile); if (!CanOverlap) { // Set alias scope for loads. Load->setMetadata(LLVMContext::MD_alias_scope, MDNode::get(Ctx, NewScope)); } Value *DstGEP = RBuilder.CreateInBoundsGEP( Int8Type, DstAddr, ConstantInt::get(TypeOfCopyLen, BytesCopied)); StoreInst *Store = RBuilder.CreateAlignedStore(Load, DstGEP, PartDstAlign, DstIsVolatile); if (!CanOverlap) { // Indicate that stores don't overlap loads. Store->setMetadata(LLVMContext::MD_noalias, MDNode::get(Ctx, NewScope)); } if (AtomicElementSize) { Load->setAtomic(AtomicOrdering::Unordered); Store->setAtomic(AtomicOrdering::Unordered); } BytesCopied += OperandSize; } assert(BytesCopied == CopyLen->getZExtValue() && "Bytes copied should match size in the call!"); } void llvm::createMemCpyLoopUnknownSize( Instruction *InsertBefore, Value *SrcAddr, Value *DstAddr, Value *CopyLen, Align SrcAlign, Align DstAlign, bool SrcIsVolatile, bool DstIsVolatile, bool CanOverlap, const TargetTransformInfo &TTI, std::optional AtomicElementSize, std::optional AverageTripCount) { BasicBlock *PreLoopBB = InsertBefore->getParent(); Function *ParentFunc = PreLoopBB->getParent(); const DataLayout &DL = ParentFunc->getDataLayout(); LLVMContext &Ctx = PreLoopBB->getContext(); MDBuilder MDB(Ctx); MDNode *NewDomain = MDB.createAnonymousAliasScopeDomain("MemCopyDomain"); StringRef Name = "MemCopyAliasScope"; MDNode *NewScope = MDB.createAnonymousAliasScope(NewDomain, Name); unsigned SrcAS = cast(SrcAddr->getType())->getAddressSpace(); unsigned DstAS = cast(DstAddr->getType())->getAddressSpace(); Type *LoopOpType = TTI.getMemcpyLoopLoweringType( Ctx, CopyLen, SrcAS, DstAS, SrcAlign, DstAlign, AtomicElementSize); assert((!AtomicElementSize || !LoopOpType->isVectorTy()) && "Atomic memcpy lowering is not supported for vector operand type"); TypeSize LoopOpSize = DL.getTypeStoreSize(LoopOpType); assert((!AtomicElementSize || LoopOpSize % *AtomicElementSize == 0) && "Atomic memcpy lowering is not supported for selected operand size"); Type *Int8Type = Type::getInt8Ty(Ctx); Type *ResidualLoopOpType = AtomicElementSize ? Type::getIntNTy(Ctx, *AtomicElementSize * 8) : Int8Type; TypeSize ResidualLoopOpSize = DL.getTypeStoreSize(ResidualLoopOpType); assert(ResidualLoopOpSize == (AtomicElementSize ? *AtomicElementSize : 1) && "Store size is expected to match type size"); LoopExpansionInfo LEI = insertLoopExpansion(InsertBefore, CopyLen, LoopOpSize, ResidualLoopOpSize, "dynamic-memcpy", AverageTripCount); assert(LEI.MainLoopIP && LEI.MainLoopIndex && "Main loop should be generated for unknown size copy"); // Fill MainLoopBB IRBuilder<> MainLoopBuilder(LEI.MainLoopIP); Align PartSrcAlign(commonAlignment(SrcAlign, LoopOpSize)); Align PartDstAlign(commonAlignment(DstAlign, LoopOpSize)); // If we used LoopOpType as GEP element type, we would iterate over the // buffers in TypeStoreSize strides while copying TypeAllocSize bytes, i.e., // we would miss bytes if TypeStoreSize != TypeAllocSize. Therefore, use byte // offsets computed from the TypeStoreSize. Value *SrcGEP = MainLoopBuilder.CreateInBoundsGEP(Int8Type, SrcAddr, LEI.MainLoopIndex); LoadInst *Load = MainLoopBuilder.CreateAlignedLoad( LoopOpType, SrcGEP, PartSrcAlign, SrcIsVolatile); if (!CanOverlap) { // Set alias scope for loads. Load->setMetadata(LLVMContext::MD_alias_scope, MDNode::get(Ctx, NewScope)); } Value *DstGEP = MainLoopBuilder.CreateInBoundsGEP(Int8Type, DstAddr, LEI.MainLoopIndex); StoreInst *Store = MainLoopBuilder.CreateAlignedStore( Load, DstGEP, PartDstAlign, DstIsVolatile); if (!CanOverlap) { // Indicate that stores don't overlap loads. Store->setMetadata(LLVMContext::MD_noalias, MDNode::get(Ctx, NewScope)); } if (AtomicElementSize) { Load->setAtomic(AtomicOrdering::Unordered); Store->setAtomic(AtomicOrdering::Unordered); } // Fill ResidualLoopBB. if (!LEI.ResidualLoopIP) return; Align ResSrcAlign(commonAlignment(PartSrcAlign, ResidualLoopOpSize)); Align ResDstAlign(commonAlignment(PartDstAlign, ResidualLoopOpSize)); IRBuilder<> ResLoopBuilder(LEI.ResidualLoopIP); Value *ResSrcGEP = ResLoopBuilder.CreateInBoundsGEP(Int8Type, SrcAddr, LEI.ResidualLoopIndex); LoadInst *ResLoad = ResLoopBuilder.CreateAlignedLoad( ResidualLoopOpType, ResSrcGEP, ResSrcAlign, SrcIsVolatile); if (!CanOverlap) { // Set alias scope for loads. ResLoad->setMetadata(LLVMContext::MD_alias_scope, MDNode::get(Ctx, NewScope)); } Value *ResDstGEP = ResLoopBuilder.CreateInBoundsGEP(Int8Type, DstAddr, LEI.ResidualLoopIndex); StoreInst *ResStore = ResLoopBuilder.CreateAlignedStore( ResLoad, ResDstGEP, ResDstAlign, DstIsVolatile); if (!CanOverlap) { // Indicate that stores don't overlap loads. ResStore->setMetadata(LLVMContext::MD_noalias, MDNode::get(Ctx, NewScope)); } if (AtomicElementSize) { ResLoad->setAtomic(AtomicOrdering::Unordered); ResStore->setAtomic(AtomicOrdering::Unordered); } } // If \p Addr1 and \p Addr2 are pointers to different address spaces, create an // addresspacecast to obtain a pair of pointers in the same addressspace. The // caller needs to ensure that addrspacecasting is possible. // No-op if the pointers are in the same address space. static std::pair tryInsertCastToCommonAddrSpace(IRBuilderBase &B, Value *Addr1, Value *Addr2, const TargetTransformInfo &TTI) { Value *ResAddr1 = Addr1; Value *ResAddr2 = Addr2; unsigned AS1 = cast(Addr1->getType())->getAddressSpace(); unsigned AS2 = cast(Addr2->getType())->getAddressSpace(); if (AS1 != AS2) { if (TTI.isValidAddrSpaceCast(AS2, AS1)) ResAddr2 = B.CreateAddrSpaceCast(Addr2, Addr1->getType()); else if (TTI.isValidAddrSpaceCast(AS1, AS2)) ResAddr1 = B.CreateAddrSpaceCast(Addr1, Addr2->getType()); else llvm_unreachable("Can only lower memmove between address spaces if they " "support addrspacecast"); } return {ResAddr1, ResAddr2}; } // Lower memmove to IR. memmove is required to correctly copy overlapping memory // regions; therefore, it has to check the relative positions of the source and // destination pointers and choose the copy direction accordingly. // // The code below is an IR rendition of this C function: // // void* memmove(void* dst, const void* src, size_t n) { // unsigned char* d = dst; // const unsigned char* s = src; // if (s < d) { // // copy backwards // while (n--) { // d[n] = s[n]; // } // } else { // // copy forward // for (size_t i = 0; i < n; ++i) { // d[i] = s[i]; // } // } // return dst; // } // // If the TargetTransformInfo specifies a wider MemcpyLoopLoweringType, it is // used for the memory accesses in the loops. Then, additional loops with // byte-wise accesses are added for the remaining bytes. static void createMemMoveLoopUnknownSize(Instruction *InsertBefore, Value *SrcAddr, Value *DstAddr, Value *CopyLen, Align SrcAlign, Align DstAlign, bool SrcIsVolatile, bool DstIsVolatile, const TargetTransformInfo &TTI) { Type *TypeOfCopyLen = CopyLen->getType(); BasicBlock *OrigBB = InsertBefore->getParent(); Function *F = OrigBB->getParent(); const DataLayout &DL = F->getDataLayout(); LLVMContext &Ctx = OrigBB->getContext(); unsigned SrcAS = cast(SrcAddr->getType())->getAddressSpace(); unsigned DstAS = cast(DstAddr->getType())->getAddressSpace(); Type *LoopOpType = TTI.getMemcpyLoopLoweringType(Ctx, CopyLen, SrcAS, DstAS, SrcAlign, DstAlign); TypeSize LoopOpSize = DL.getTypeStoreSize(LoopOpType); Type *Int8Type = Type::getInt8Ty(Ctx); bool LoopOpIsInt8 = LoopOpType == Int8Type; // If the memory accesses are wider than one byte, residual loops with // i8-accesses are required to move remaining bytes. bool RequiresResidual = !LoopOpIsInt8; Type *ResidualLoopOpType = Int8Type; TypeSize ResidualLoopOpSize = DL.getTypeStoreSize(ResidualLoopOpType); // Calculate the loop trip count and remaining bytes to copy after the loop. IntegerType *ILengthType = cast(TypeOfCopyLen); ConstantInt *CILoopOpSize = ConstantInt::get(ILengthType, LoopOpSize); ConstantInt *CIResidualLoopOpSize = ConstantInt::get(ILengthType, ResidualLoopOpSize); ConstantInt *Zero = ConstantInt::get(ILengthType, 0); const DebugLoc &DbgLoc = InsertBefore->getStableDebugLoc(); IRBuilder<> PLBuilder(InsertBefore); PLBuilder.SetCurrentDebugLocation(DbgLoc); Value *RuntimeLoopBytes = CopyLen; Value *RuntimeLoopRemainder = nullptr; Value *SkipResidualCondition = nullptr; if (RequiresResidual) { RuntimeLoopRemainder = getRuntimeLoopRemainder(PLBuilder, CopyLen, CILoopOpSize, LoopOpSize); RuntimeLoopBytes = getRuntimeLoopUnits(PLBuilder, CopyLen, CILoopOpSize, LoopOpSize, RuntimeLoopRemainder); SkipResidualCondition = PLBuilder.CreateICmpEQ(RuntimeLoopRemainder, Zero, "skip_residual"); } Value *SkipMainCondition = PLBuilder.CreateICmpEQ(RuntimeLoopBytes, Zero, "skip_main"); // Create the a comparison of src and dst, based on which we jump to either // the forward-copy part of the function (if src >= dst) or the backwards-copy // part (if src < dst). // SplitBlockAndInsertIfThenElse conveniently creates the basic if-then-else // structure. Its block terminators (unconditional branches) are replaced by // the appropriate conditional branches when the loop is built. // If the pointers are in different address spaces, they need to be converted // to a compatible one. Cases where memory ranges in the different address // spaces cannot overlap are lowered as memcpy and not handled here. auto [CmpSrcAddr, CmpDstAddr] = tryInsertCastToCommonAddrSpace(PLBuilder, SrcAddr, DstAddr, TTI); Value *PtrCompare = PLBuilder.CreateICmpULT(CmpSrcAddr, CmpDstAddr, "compare_src_dst"); Instruction *ThenTerm, *ElseTerm; SplitBlockAndInsertIfThenElse(PtrCompare, InsertBefore->getIterator(), &ThenTerm, &ElseTerm); // If the LoopOpSize is greater than 1, each part of the function consists of // four blocks: // memmove_copy_backwards: // skip the residual loop when 0 iterations are required // memmove_bwd_residual_loop: // copy the last few bytes individually so that the remaining length is // a multiple of the LoopOpSize // memmove_bwd_middle: skip the main loop when 0 iterations are required // memmove_bwd_main_loop: the actual backwards loop BB with wide accesses // memmove_copy_forward: skip the main loop when 0 iterations are required // memmove_fwd_main_loop: the actual forward loop BB with wide accesses // memmove_fwd_middle: skip the residual loop when 0 iterations are required // memmove_fwd_residual_loop: copy the last few bytes individually // // The main and residual loop are switched between copying forward and // backward so that the residual loop always operates on the end of the moved // range. This is based on the assumption that buffers whose start is aligned // with the LoopOpSize are more common than buffers whose end is. // // If the LoopOpSize is 1, each part of the function consists of two blocks: // memmove_copy_backwards: skip the loop when 0 iterations are required // memmove_bwd_main_loop: the actual backwards loop BB // memmove_copy_forward: skip the loop when 0 iterations are required // memmove_fwd_main_loop: the actual forward loop BB BasicBlock *CopyBackwardsBB = ThenTerm->getParent(); CopyBackwardsBB->setName("memmove_copy_backwards"); BasicBlock *CopyForwardBB = ElseTerm->getParent(); CopyForwardBB->setName("memmove_copy_forward"); BasicBlock *ExitBB = InsertBefore->getParent(); ExitBB->setName("memmove_done"); Align PartSrcAlign(commonAlignment(SrcAlign, LoopOpSize)); Align PartDstAlign(commonAlignment(DstAlign, LoopOpSize)); // Accesses in the residual loops do not share the same alignment as those in // the main loops. Align ResidualSrcAlign(commonAlignment(PartSrcAlign, ResidualLoopOpSize)); Align ResidualDstAlign(commonAlignment(PartDstAlign, ResidualLoopOpSize)); // Copying backwards. { BasicBlock *MainLoopBB = BasicBlock::Create( F->getContext(), "memmove_bwd_main_loop", F, CopyForwardBB); // The predecessor of the memmove_bwd_main_loop. Updated in the // following if a residual loop is emitted first. BasicBlock *PredBB = CopyBackwardsBB; if (RequiresResidual) { // backwards residual loop BasicBlock *ResidualLoopBB = BasicBlock::Create( F->getContext(), "memmove_bwd_residual_loop", F, MainLoopBB); IRBuilder<> ResidualLoopBuilder(ResidualLoopBB); ResidualLoopBuilder.SetCurrentDebugLocation(DbgLoc); PHINode *ResidualLoopPhi = ResidualLoopBuilder.CreatePHI(ILengthType, 0); Value *ResidualIndex = ResidualLoopBuilder.CreateSub( ResidualLoopPhi, CIResidualLoopOpSize, "bwd_residual_index"); // If we used LoopOpType as GEP element type, we would iterate over the // buffers in TypeStoreSize strides while copying TypeAllocSize bytes, // i.e., we would miss bytes if TypeStoreSize != TypeAllocSize. Therefore, // use byte offsets computed from the TypeStoreSize. Value *LoadGEP = ResidualLoopBuilder.CreateInBoundsGEP(Int8Type, SrcAddr, ResidualIndex); Value *Element = ResidualLoopBuilder.CreateAlignedLoad( ResidualLoopOpType, LoadGEP, ResidualSrcAlign, SrcIsVolatile, "element"); Value *StoreGEP = ResidualLoopBuilder.CreateInBoundsGEP(Int8Type, DstAddr, ResidualIndex); ResidualLoopBuilder.CreateAlignedStore(Element, StoreGEP, ResidualDstAlign, DstIsVolatile); // After the residual loop, go to an intermediate block. BasicBlock *IntermediateBB = BasicBlock::Create( F->getContext(), "memmove_bwd_middle", F, MainLoopBB); // Later code expects a terminator in the PredBB. IRBuilder<> IntermediateBuilder(IntermediateBB); IntermediateBuilder.SetCurrentDebugLocation(DbgLoc); IntermediateBuilder.CreateUnreachable(); ResidualLoopBuilder.CreateCondBr( ResidualLoopBuilder.CreateICmpEQ(ResidualIndex, RuntimeLoopBytes), IntermediateBB, ResidualLoopBB); ResidualLoopPhi->addIncoming(ResidualIndex, ResidualLoopBB); ResidualLoopPhi->addIncoming(CopyLen, CopyBackwardsBB); // How to get to the residual: CondBrInst *BrInst = CondBrInst::Create(SkipResidualCondition, IntermediateBB, ResidualLoopBB, ThenTerm->getIterator()); BrInst->setDebugLoc(DbgLoc); ThenTerm->eraseFromParent(); PredBB = IntermediateBB; } // main loop IRBuilder<> MainLoopBuilder(MainLoopBB); MainLoopBuilder.SetCurrentDebugLocation(DbgLoc); PHINode *MainLoopPhi = MainLoopBuilder.CreatePHI(ILengthType, 0); Value *MainIndex = MainLoopBuilder.CreateSub(MainLoopPhi, CILoopOpSize, "bwd_main_index"); Value *LoadGEP = MainLoopBuilder.CreateInBoundsGEP(Int8Type, SrcAddr, MainIndex); Value *Element = MainLoopBuilder.CreateAlignedLoad( LoopOpType, LoadGEP, PartSrcAlign, SrcIsVolatile, "element"); Value *StoreGEP = MainLoopBuilder.CreateInBoundsGEP(Int8Type, DstAddr, MainIndex); MainLoopBuilder.CreateAlignedStore(Element, StoreGEP, PartDstAlign, DstIsVolatile); MainLoopBuilder.CreateCondBr(MainLoopBuilder.CreateICmpEQ(MainIndex, Zero), ExitBB, MainLoopBB); MainLoopPhi->addIncoming(MainIndex, MainLoopBB); MainLoopPhi->addIncoming(RuntimeLoopBytes, PredBB); // How to get to the main loop: Instruction *PredBBTerm = PredBB->getTerminator(); CondBrInst *BrInst = CondBrInst::Create( SkipMainCondition, ExitBB, MainLoopBB, PredBBTerm->getIterator()); BrInst->setDebugLoc(DbgLoc); PredBBTerm->eraseFromParent(); } // Copying forward. // main loop { BasicBlock *MainLoopBB = BasicBlock::Create(F->getContext(), "memmove_fwd_main_loop", F, ExitBB); IRBuilder<> MainLoopBuilder(MainLoopBB); MainLoopBuilder.SetCurrentDebugLocation(DbgLoc); PHINode *MainLoopPhi = MainLoopBuilder.CreatePHI(ILengthType, 0, "fwd_main_index"); Value *LoadGEP = MainLoopBuilder.CreateInBoundsGEP(Int8Type, SrcAddr, MainLoopPhi); Value *Element = MainLoopBuilder.CreateAlignedLoad( LoopOpType, LoadGEP, PartSrcAlign, SrcIsVolatile, "element"); Value *StoreGEP = MainLoopBuilder.CreateInBoundsGEP(Int8Type, DstAddr, MainLoopPhi); MainLoopBuilder.CreateAlignedStore(Element, StoreGEP, PartDstAlign, DstIsVolatile); Value *MainIndex = MainLoopBuilder.CreateAdd(MainLoopPhi, CILoopOpSize); MainLoopPhi->addIncoming(MainIndex, MainLoopBB); MainLoopPhi->addIncoming(Zero, CopyForwardBB); Instruction *CopyFwdBBTerm = CopyForwardBB->getTerminator(); BasicBlock *SuccessorBB = ExitBB; if (RequiresResidual) SuccessorBB = BasicBlock::Create(F->getContext(), "memmove_fwd_middle", F, ExitBB); // leaving or staying in the main loop MainLoopBuilder.CreateCondBr( MainLoopBuilder.CreateICmpEQ(MainIndex, RuntimeLoopBytes), SuccessorBB, MainLoopBB); // getting in or skipping the main loop CondBrInst *BrInst = CondBrInst::Create(SkipMainCondition, SuccessorBB, MainLoopBB, CopyFwdBBTerm->getIterator()); BrInst->setDebugLoc(DbgLoc); CopyFwdBBTerm->eraseFromParent(); if (RequiresResidual) { BasicBlock *IntermediateBB = SuccessorBB; IRBuilder<> IntermediateBuilder(IntermediateBB); IntermediateBuilder.SetCurrentDebugLocation(DbgLoc); BasicBlock *ResidualLoopBB = BasicBlock::Create( F->getContext(), "memmove_fwd_residual_loop", F, ExitBB); IntermediateBuilder.CreateCondBr(SkipResidualCondition, ExitBB, ResidualLoopBB); // Residual loop IRBuilder<> ResidualLoopBuilder(ResidualLoopBB); ResidualLoopBuilder.SetCurrentDebugLocation(DbgLoc); PHINode *ResidualLoopPhi = ResidualLoopBuilder.CreatePHI(ILengthType, 0, "fwd_residual_index"); Value *LoadGEP = ResidualLoopBuilder.CreateInBoundsGEP(Int8Type, SrcAddr, ResidualLoopPhi); Value *Element = ResidualLoopBuilder.CreateAlignedLoad( ResidualLoopOpType, LoadGEP, ResidualSrcAlign, SrcIsVolatile, "element"); Value *StoreGEP = ResidualLoopBuilder.CreateInBoundsGEP(Int8Type, DstAddr, ResidualLoopPhi); ResidualLoopBuilder.CreateAlignedStore(Element, StoreGEP, ResidualDstAlign, DstIsVolatile); Value *ResidualIndex = ResidualLoopBuilder.CreateAdd(ResidualLoopPhi, CIResidualLoopOpSize); ResidualLoopBuilder.CreateCondBr( ResidualLoopBuilder.CreateICmpEQ(ResidualIndex, CopyLen), ExitBB, ResidualLoopBB); ResidualLoopPhi->addIncoming(ResidualIndex, ResidualLoopBB); ResidualLoopPhi->addIncoming(RuntimeLoopBytes, IntermediateBB); } } } // Similar to createMemMoveLoopUnknownSize, only the trip counts are computed at // compile time, obsolete loops and branches are omitted, and the residual code // is straight-line code instead of a loop. static void createMemMoveLoopKnownSize(Instruction *InsertBefore, Value *SrcAddr, Value *DstAddr, ConstantInt *CopyLen, Align SrcAlign, Align DstAlign, bool SrcIsVolatile, bool DstIsVolatile, const TargetTransformInfo &TTI) { // No need to expand zero length moves. if (CopyLen->isZero()) return; Type *TypeOfCopyLen = CopyLen->getType(); BasicBlock *OrigBB = InsertBefore->getParent(); Function *F = OrigBB->getParent(); const DataLayout &DL = F->getDataLayout(); LLVMContext &Ctx = OrigBB->getContext(); unsigned SrcAS = cast(SrcAddr->getType())->getAddressSpace(); unsigned DstAS = cast(DstAddr->getType())->getAddressSpace(); Type *LoopOpType = TTI.getMemcpyLoopLoweringType(Ctx, CopyLen, SrcAS, DstAS, SrcAlign, DstAlign); TypeSize LoopOpSize = DL.getTypeStoreSize(LoopOpType); assert(LoopOpSize.isFixed() && "LoopOpType cannot be a scalable vector type"); Type *Int8Type = Type::getInt8Ty(Ctx); // Calculate the loop trip count and remaining bytes to copy after the loop. uint64_t BytesCopiedInLoop = alignDown(CopyLen->getZExtValue(), LoopOpSize.getFixedValue()); uint64_t RemainingBytes = CopyLen->getZExtValue() - BytesCopiedInLoop; IntegerType *ILengthType = cast(TypeOfCopyLen); ConstantInt *Zero = ConstantInt::get(ILengthType, 0); ConstantInt *LoopBound = ConstantInt::get(ILengthType, BytesCopiedInLoop); ConstantInt *CILoopOpSize = ConstantInt::get(ILengthType, LoopOpSize); const DebugLoc &DbgLoc = InsertBefore->getStableDebugLoc(); IRBuilder<> PLBuilder(InsertBefore); PLBuilder.SetCurrentDebugLocation(DbgLoc); auto [CmpSrcAddr, CmpDstAddr] = tryInsertCastToCommonAddrSpace(PLBuilder, SrcAddr, DstAddr, TTI); Value *PtrCompare = PLBuilder.CreateICmpULT(CmpSrcAddr, CmpDstAddr, "compare_src_dst"); Instruction *ThenTerm, *ElseTerm; SplitBlockAndInsertIfThenElse(PtrCompare, InsertBefore->getIterator(), &ThenTerm, &ElseTerm); BasicBlock *CopyBackwardsBB = ThenTerm->getParent(); BasicBlock *CopyForwardBB = ElseTerm->getParent(); BasicBlock *ExitBB = InsertBefore->getParent(); ExitBB->setName("memmove_done"); Align PartSrcAlign(commonAlignment(SrcAlign, LoopOpSize)); Align PartDstAlign(commonAlignment(DstAlign, LoopOpSize)); // Helper function to generate a load/store pair of a given type in the // residual. Used in the forward and backward branches. auto GenerateResidualLdStPair = [&](Type *OpTy, IRBuilderBase &Builder, uint64_t &BytesCopied) { Align ResSrcAlign(commonAlignment(SrcAlign, BytesCopied)); Align ResDstAlign(commonAlignment(DstAlign, BytesCopied)); TypeSize OperandSize = DL.getTypeStoreSize(OpTy); // If we used LoopOpType as GEP element type, we would iterate over the // buffers in TypeStoreSize strides while copying TypeAllocSize bytes, i.e., // we would miss bytes if TypeStoreSize != TypeAllocSize. Therefore, use // byte offsets computed from the TypeStoreSize. Value *SrcGEP = Builder.CreateInBoundsGEP( Int8Type, SrcAddr, ConstantInt::get(TypeOfCopyLen, BytesCopied)); LoadInst *Load = Builder.CreateAlignedLoad(OpTy, SrcGEP, ResSrcAlign, SrcIsVolatile); Value *DstGEP = Builder.CreateInBoundsGEP( Int8Type, DstAddr, ConstantInt::get(TypeOfCopyLen, BytesCopied)); Builder.CreateAlignedStore(Load, DstGEP, ResDstAlign, DstIsVolatile); BytesCopied += OperandSize; }; // Copying backwards. if (RemainingBytes != 0) { CopyBackwardsBB->setName("memmove_bwd_residual"); uint64_t BytesCopied = BytesCopiedInLoop; // Residual code is required to move the remaining bytes. We need the same // instructions as in the forward case, only in reverse. So we generate code // the same way, except that we change the IRBuilder insert point for each // load/store pair so that each one is inserted before the previous one // instead of after it. IRBuilder<> BwdResBuilder(CopyBackwardsBB, CopyBackwardsBB->getFirstNonPHIIt()); BwdResBuilder.SetCurrentDebugLocation(DbgLoc); SmallVector RemainingOps; TTI.getMemcpyLoopResidualLoweringType(RemainingOps, Ctx, RemainingBytes, SrcAS, DstAS, PartSrcAlign, PartDstAlign); for (auto *OpTy : RemainingOps) { // reverse the order of the emitted operations BwdResBuilder.SetInsertPoint(CopyBackwardsBB, CopyBackwardsBB->getFirstNonPHIIt()); GenerateResidualLdStPair(OpTy, BwdResBuilder, BytesCopied); } } if (BytesCopiedInLoop != 0) { BasicBlock *LoopBB = CopyBackwardsBB; BasicBlock *PredBB = OrigBB; if (RemainingBytes != 0) { // if we introduce residual code, it needs its separate BB LoopBB = CopyBackwardsBB->splitBasicBlock( CopyBackwardsBB->getTerminator(), "memmove_bwd_loop"); PredBB = CopyBackwardsBB; } else { CopyBackwardsBB->setName("memmove_bwd_loop"); } IRBuilder<> LoopBuilder(LoopBB->getTerminator()); LoopBuilder.SetCurrentDebugLocation(DbgLoc); PHINode *LoopPhi = LoopBuilder.CreatePHI(ILengthType, 0); Value *Index = LoopBuilder.CreateSub(LoopPhi, CILoopOpSize, "bwd_index"); Value *LoadGEP = LoopBuilder.CreateInBoundsGEP(Int8Type, SrcAddr, Index); Value *Element = LoopBuilder.CreateAlignedLoad( LoopOpType, LoadGEP, PartSrcAlign, SrcIsVolatile, "element"); Value *StoreGEP = LoopBuilder.CreateInBoundsGEP(Int8Type, DstAddr, Index); LoopBuilder.CreateAlignedStore(Element, StoreGEP, PartDstAlign, DstIsVolatile); // Replace the unconditional branch introduced by // SplitBlockAndInsertIfThenElse to turn LoopBB into a loop. Instruction *UncondTerm = LoopBB->getTerminator(); LoopBuilder.CreateCondBr(LoopBuilder.CreateICmpEQ(Index, Zero), ExitBB, LoopBB); UncondTerm->eraseFromParent(); LoopPhi->addIncoming(Index, LoopBB); LoopPhi->addIncoming(LoopBound, PredBB); } // Copying forward. BasicBlock *FwdResidualBB = CopyForwardBB; if (BytesCopiedInLoop != 0) { CopyForwardBB->setName("memmove_fwd_loop"); BasicBlock *LoopBB = CopyForwardBB; BasicBlock *SuccBB = ExitBB; if (RemainingBytes != 0) { // if we introduce residual code, it needs its separate BB SuccBB = CopyForwardBB->splitBasicBlock(CopyForwardBB->getTerminator(), "memmove_fwd_residual"); FwdResidualBB = SuccBB; } IRBuilder<> LoopBuilder(LoopBB->getTerminator()); LoopBuilder.SetCurrentDebugLocation(DbgLoc); PHINode *LoopPhi = LoopBuilder.CreatePHI(ILengthType, 0, "fwd_index"); Value *LoadGEP = LoopBuilder.CreateInBoundsGEP(Int8Type, SrcAddr, LoopPhi); Value *Element = LoopBuilder.CreateAlignedLoad( LoopOpType, LoadGEP, PartSrcAlign, SrcIsVolatile, "element"); Value *StoreGEP = LoopBuilder.CreateInBoundsGEP(Int8Type, DstAddr, LoopPhi); LoopBuilder.CreateAlignedStore(Element, StoreGEP, PartDstAlign, DstIsVolatile); Value *Index = LoopBuilder.CreateAdd(LoopPhi, CILoopOpSize); LoopPhi->addIncoming(Index, LoopBB); LoopPhi->addIncoming(Zero, OrigBB); // Replace the unconditional branch to turn LoopBB into a loop. Instruction *UncondTerm = LoopBB->getTerminator(); LoopBuilder.CreateCondBr(LoopBuilder.CreateICmpEQ(Index, LoopBound), SuccBB, LoopBB); UncondTerm->eraseFromParent(); } if (RemainingBytes != 0) { uint64_t BytesCopied = BytesCopiedInLoop; // Residual code is required to move the remaining bytes. In the forward // case, we emit it in the normal order. IRBuilder<> FwdResBuilder(FwdResidualBB->getTerminator()); FwdResBuilder.SetCurrentDebugLocation(DbgLoc); SmallVector RemainingOps; TTI.getMemcpyLoopResidualLoweringType(RemainingOps, Ctx, RemainingBytes, SrcAS, DstAS, PartSrcAlign, PartDstAlign); for (auto *OpTy : RemainingOps) GenerateResidualLdStPair(OpTy, FwdResBuilder, BytesCopied); } } /// Create a Value of \p DstType that consists of a sequence of copies of /// \p SetValue, using bitcasts and a vector splat. static Value *createMemSetSplat(const DataLayout &DL, IRBuilderBase &B, Value *SetValue, Type *DstType) { TypeSize DstSize = DL.getTypeStoreSize(DstType); Type *SetValueType = SetValue->getType(); TypeSize SetValueSize = DL.getTypeStoreSize(SetValueType); assert(SetValueSize == DL.getTypeAllocSize(SetValueType) && "Store size and alloc size of SetValue's type must match"); assert(SetValueSize != 0 && DstSize % SetValueSize == 0 && "DstType size must be a multiple of SetValue size"); Value *Result = SetValue; if (DstSize != SetValueSize) { if (!SetValueType->isIntegerTy() && !SetValueType->isFloatingPointTy()) { // If the type cannot be put into a vector, bitcast to iN first. LLVMContext &Ctx = SetValue->getContext(); Result = B.CreateBitCast(Result, Type::getIntNTy(Ctx, SetValueSize * 8), "setvalue.toint"); } // Form a sufficiently large vector consisting of SetValue, repeated. Result = B.CreateVectorSplat(DstSize / SetValueSize, Result, "setvalue.splat"); } // The value has the right size, but we might have to bitcast it to the right // type. Result = B.CreateBitCast(Result, DstType, "setvalue.splat.cast"); return Result; } static void createMemSetLoopKnownSize(Instruction *InsertBefore, Value *DstAddr, ConstantInt *Len, Value *SetValue, Align DstAlign, bool IsVolatile, const TargetTransformInfo *TTI, std::optional AverageTripCount) { // No need to expand zero length memsets. if (Len->isZero()) return; BasicBlock *PreLoopBB = InsertBefore->getParent(); Function *ParentFunc = PreLoopBB->getParent(); const DataLayout &DL = ParentFunc->getDataLayout(); LLVMContext &Ctx = PreLoopBB->getContext(); unsigned DstAS = cast(DstAddr->getType())->getAddressSpace(); Type *TypeOfLen = Len->getType(); Type *Int8Type = Type::getInt8Ty(Ctx); assert(SetValue->getType() == Int8Type && "Can only set bytes"); Type *LoopOpType = Int8Type; if (TTI) { // Use the same memory access type as for a memcpy with the same Dst and Src // alignment and address space. LoopOpType = TTI->getMemcpyLoopLoweringType( Ctx, Len, DstAS, DstAS, DstAlign, DstAlign, std::nullopt); } TypeSize LoopOpSize = DL.getTypeStoreSize(LoopOpType); assert(LoopOpSize.isFixed() && "LoopOpType cannot be a scalable vector type"); uint64_t LoopEndCount = alignDown(Len->getZExtValue(), LoopOpSize.getFixedValue()); if (LoopEndCount != 0) { Value *SplatSetValue = nullptr; { IRBuilder<> PreLoopBuilder(InsertBefore); SplatSetValue = createMemSetSplat(DL, PreLoopBuilder, SetValue, LoopOpType); } // Don't generate a residual loop, the remaining bytes are set with // straight-line code. LoopExpansionInfo LEI = insertLoopExpansion( InsertBefore, Len, LoopOpSize, 0, "static-memset", AverageTripCount); assert(LEI.MainLoopIP && LEI.MainLoopIndex && "Main loop should be generated for non-zero loop count"); // Fill MainLoopBB IRBuilder<> MainLoopBuilder(LEI.MainLoopIP); Align PartDstAlign(commonAlignment(DstAlign, LoopOpSize)); Value *DstGEP = MainLoopBuilder.CreateInBoundsGEP(Int8Type, DstAddr, LEI.MainLoopIndex); MainLoopBuilder.CreateAlignedStore(SplatSetValue, DstGEP, PartDstAlign, IsVolatile); assert(!LEI.ResidualLoopIP && !LEI.ResidualLoopIndex && "No residual loop was requested"); } uint64_t BytesSet = LoopEndCount; uint64_t RemainingBytes = Len->getZExtValue() - BytesSet; if (RemainingBytes == 0) return; IRBuilder<> RBuilder(InsertBefore); assert(TTI && "there cannot be a residual loop without TTI"); SmallVector RemainingOps; TTI->getMemcpyLoopResidualLoweringType(RemainingOps, Ctx, RemainingBytes, DstAS, DstAS, DstAlign, DstAlign, std::nullopt); Type *PreviousOpTy = nullptr; Value *SplatSetValue = nullptr; for (auto *OpTy : RemainingOps) { TypeSize OperandSize = DL.getTypeStoreSize(OpTy); assert(OperandSize.isFixed() && "Operand types cannot be scalable vector types"); Align PartDstAlign(commonAlignment(DstAlign, BytesSet)); // Avoid recomputing the splat SetValue if it's the same as for the last // iteration. if (OpTy != PreviousOpTy) SplatSetValue = createMemSetSplat(DL, RBuilder, SetValue, OpTy); Value *DstGEP = RBuilder.CreateInBoundsGEP( Int8Type, DstAddr, ConstantInt::get(TypeOfLen, BytesSet)); RBuilder.CreateAlignedStore(SplatSetValue, DstGEP, PartDstAlign, IsVolatile); BytesSet += OperandSize; PreviousOpTy = OpTy; } assert(BytesSet == Len->getZExtValue() && "Bytes set should match size in the call!"); } static void createMemSetLoopUnknownSize(Instruction *InsertBefore, Value *DstAddr, Value *Len, Value *SetValue, Align DstAlign, bool IsVolatile, const TargetTransformInfo *TTI, std::optional AverageTripCount) { BasicBlock *PreLoopBB = InsertBefore->getParent(); Function *ParentFunc = PreLoopBB->getParent(); const DataLayout &DL = ParentFunc->getDataLayout(); LLVMContext &Ctx = PreLoopBB->getContext(); unsigned DstAS = cast(DstAddr->getType())->getAddressSpace(); Type *Int8Type = Type::getInt8Ty(Ctx); assert(SetValue->getType() == Int8Type && "Can only set bytes"); Type *LoopOpType = Int8Type; if (TTI) { LoopOpType = TTI->getMemcpyLoopLoweringType( Ctx, Len, DstAS, DstAS, DstAlign, DstAlign, std::nullopt); } TypeSize LoopOpSize = DL.getTypeStoreSize(LoopOpType); assert(LoopOpSize.isFixed() && "LoopOpType cannot be a scalable vector type"); Type *ResidualLoopOpType = Int8Type; TypeSize ResidualLoopOpSize = DL.getTypeStoreSize(ResidualLoopOpType); Value *SplatSetValue = SetValue; { IRBuilder<> PreLoopBuilder(InsertBefore); SplatSetValue = createMemSetSplat(DL, PreLoopBuilder, SetValue, LoopOpType); } LoopExpansionInfo LEI = insertLoopExpansion(InsertBefore, Len, LoopOpSize, ResidualLoopOpSize, "dynamic-memset", AverageTripCount); assert(LEI.MainLoopIP && LEI.MainLoopIndex && "Main loop should be generated for unknown size memset"); // Fill MainLoopBB IRBuilder<> MainLoopBuilder(LEI.MainLoopIP); Align PartDstAlign(commonAlignment(DstAlign, LoopOpSize)); Value *DstGEP = MainLoopBuilder.CreateInBoundsGEP(Int8Type, DstAddr, LEI.MainLoopIndex); MainLoopBuilder.CreateAlignedStore(SplatSetValue, DstGEP, PartDstAlign, IsVolatile); // Fill ResidualLoopBB if (!LEI.ResidualLoopIP) return; Align ResDstAlign(commonAlignment(PartDstAlign, ResidualLoopOpSize)); IRBuilder<> ResLoopBuilder(LEI.ResidualLoopIP); Value *ResDstGEP = ResLoopBuilder.CreateInBoundsGEP(Int8Type, DstAddr, LEI.ResidualLoopIndex); ResLoopBuilder.CreateAlignedStore(SetValue, ResDstGEP, ResDstAlign, IsVolatile); } static void createMemSetPatternLoop(Instruction *InsertBefore, Value *DstAddr, Value *Len, Value *SetValue, Align DstAlign, bool IsVolatile, const TargetTransformInfo *TTI, std::optional AverageTripCount) { // No need to expand zero length memset.pattern. if (auto *CLen = dyn_cast(Len)) if (CLen->isZero()) return; BasicBlock *PreLoopBB = InsertBefore->getParent(); Function *ParentFunc = PreLoopBB->getParent(); const DataLayout &DL = ParentFunc->getDataLayout(); LLVMContext &Ctx = PreLoopBB->getContext(); unsigned DstAS = cast(DstAddr->getType())->getAddressSpace(); Type *PreferredLoopOpType = SetValue->getType(); if (TTI) { PreferredLoopOpType = TTI->getMemcpyLoopLoweringType( Ctx, Len, DstAS, DstAS, DstAlign, DstAlign, std::nullopt); } TypeSize PreferredLoopOpStoreSize = DL.getTypeStoreSize(PreferredLoopOpType); assert(PreferredLoopOpStoreSize.isFixed() && "PreferredLoopOpType cannot be a scalable vector type"); TypeSize PreferredLoopOpAllocSize = DL.getTypeAllocSize(PreferredLoopOpType); Type *OriginalType = SetValue->getType(); TypeSize OriginalTypeStoreSize = DL.getTypeStoreSize(OriginalType); TypeSize OriginalTypeAllocSize = DL.getTypeAllocSize(OriginalType); // The semantics of memset.pattern restrict what vectorization we can do: It // has to behave like a series of stores of the SetValue type at offsets that // are spaced by the alloc size of the SetValue type. If store and alloc size // of the SetValue type don't match, the bytes that aren't covered by these // stores must not be overwritten. We therefore only vectorize memset.pattern // if the store and alloc sizes of the SetValue are equal and properly divide // the size of the preferred lowering type (and only if store and alloc size // for the preferred lowering type are also equal). unsigned MainLoopStep = 1; Type *MainLoopType = OriginalType; TypeSize MainLoopAllocSize = OriginalTypeAllocSize; unsigned ResidualLoopStep = 0; Type *ResidualLoopType = nullptr; if (PreferredLoopOpStoreSize == PreferredLoopOpAllocSize && OriginalTypeStoreSize == OriginalTypeAllocSize && OriginalTypeStoreSize < PreferredLoopOpStoreSize && PreferredLoopOpStoreSize % OriginalTypeStoreSize == 0) { // Multiple instances of SetValue can be combined to reach the preferred // loop op size. MainLoopStep = PreferredLoopOpStoreSize / OriginalTypeStoreSize; MainLoopType = PreferredLoopOpType; MainLoopAllocSize = PreferredLoopOpStoreSize; ResidualLoopStep = 1; ResidualLoopType = OriginalType; } // The step arguments here are in terms of the alloc size of the SetValue, not // in terms of bytes. LoopExpansionInfo LEI = insertLoopExpansion(InsertBefore, Len, MainLoopStep, ResidualLoopStep, "memset.pattern", AverageTripCount); Align PartDstAlign(commonAlignment(DstAlign, MainLoopAllocSize)); if (LEI.MainLoopIP) { // Create the loop-invariant splat value before the loop. IRBuilder<> PreLoopBuilder(PreLoopBB->getTerminator()); Value *MainLoopSetValue = SetValue; if (MainLoopType != OriginalType) MainLoopSetValue = createMemSetSplat(DL, PreLoopBuilder, SetValue, MainLoopType); // Fill MainLoopBB IRBuilder<> MainLoopBuilder(LEI.MainLoopIP); Value *DstGEP = MainLoopBuilder.CreateInBoundsGEP(MainLoopType, DstAddr, LEI.MainLoopIndex); MainLoopBuilder.CreateAlignedStore(MainLoopSetValue, DstGEP, PartDstAlign, IsVolatile); } if (!LEI.ResidualLoopIP) return; // Fill ResidualLoopBB Align ResDstAlign( commonAlignment(PartDstAlign, DL.getTypeAllocSize(ResidualLoopType))); IRBuilder<> ResLoopBuilder(LEI.ResidualLoopIP); Value *ResDstGEP = ResLoopBuilder.CreateInBoundsGEP(ResidualLoopType, DstAddr, LEI.ResidualLoopIndex); ResLoopBuilder.CreateAlignedStore(SetValue, ResDstGEP, ResDstAlign, IsVolatile); } template static bool canOverlap(MemTransferBase *Memcpy, ScalarEvolution *SE) { if (SE) { const SCEV *SrcSCEV = SE->getSCEV(Memcpy->getRawSource()); const SCEV *DestSCEV = SE->getSCEV(Memcpy->getRawDest()); if (SE->isKnownPredicateAt(CmpInst::ICMP_NE, SrcSCEV, DestSCEV, Memcpy)) return false; } return true; } void llvm::expandMemCpyAsLoop(MemCpyInst *Memcpy, const TargetTransformInfo &TTI, ScalarEvolution *SE) { bool CanOverlap = canOverlap(Memcpy, SE); auto TripCount = getAverageMemOpLoopTripCount(*Memcpy); if (ConstantInt *CI = dyn_cast(Memcpy->getLength())) { createMemCpyLoopKnownSize( /*InsertBefore=*/Memcpy, /*SrcAddr=*/Memcpy->getRawSource(), /*DstAddr=*/Memcpy->getRawDest(), /*CopyLen=*/CI, /*SrcAlign=*/Memcpy->getSourceAlign().valueOrOne(), /*DstAlign=*/Memcpy->getDestAlign().valueOrOne(), /*SrcIsVolatile=*/Memcpy->isVolatile(), /*DstIsVolatile=*/Memcpy->isVolatile(), /*CanOverlap=*/CanOverlap, /*TTI=*/TTI, /*AtomicElementSize=*/std::nullopt, /*AverageTripCount=*/TripCount); } else { createMemCpyLoopUnknownSize( /*InsertBefore=*/Memcpy, /*SrcAddr=*/Memcpy->getRawSource(), /*DstAddr=*/Memcpy->getRawDest(), /*CopyLen=*/Memcpy->getLength(), /*SrcAlign=*/Memcpy->getSourceAlign().valueOrOne(), /*DstAlign=*/Memcpy->getDestAlign().valueOrOne(), /*SrcIsVolatile=*/Memcpy->isVolatile(), /*DstIsVolatile=*/Memcpy->isVolatile(), /*CanOverlap=*/CanOverlap, /*TTI=*/TTI, /*AtomicElementSize=*/std::nullopt, /*AverageTripCount=*/TripCount); } } bool llvm::expandMemMoveAsLoop(MemMoveInst *Memmove, const TargetTransformInfo &TTI) { Value *CopyLen = Memmove->getLength(); Value *SrcAddr = Memmove->getRawSource(); Value *DstAddr = Memmove->getRawDest(); Align SrcAlign = Memmove->getSourceAlign().valueOrOne(); Align DstAlign = Memmove->getDestAlign().valueOrOne(); bool SrcIsVolatile = Memmove->isVolatile(); bool DstIsVolatile = SrcIsVolatile; IRBuilder<> CastBuilder(Memmove); CastBuilder.SetCurrentDebugLocation(Memmove->getStableDebugLoc()); unsigned SrcAS = SrcAddr->getType()->getPointerAddressSpace(); unsigned DstAS = DstAddr->getType()->getPointerAddressSpace(); if (SrcAS != DstAS) { if (!TTI.addrspacesMayAlias(SrcAS, DstAS)) { // We may not be able to emit a pointer comparison, but we don't have // to. Expand as memcpy. auto AverageTripCount = getAverageMemOpLoopTripCount(*Memmove); if (ConstantInt *CI = dyn_cast(CopyLen)) { createMemCpyLoopKnownSize( /*InsertBefore=*/Memmove, SrcAddr, DstAddr, CI, SrcAlign, DstAlign, SrcIsVolatile, DstIsVolatile, /*CanOverlap=*/false, TTI, std::nullopt, AverageTripCount); } else { createMemCpyLoopUnknownSize( /*InsertBefore=*/Memmove, SrcAddr, DstAddr, CopyLen, SrcAlign, DstAlign, SrcIsVolatile, DstIsVolatile, /*CanOverlap=*/false, TTI, std::nullopt, AverageTripCount); } return true; } if (!(TTI.isValidAddrSpaceCast(DstAS, SrcAS) || TTI.isValidAddrSpaceCast(SrcAS, DstAS))) { // We don't know generically if it's legal to introduce an // addrspacecast. We need to know either if it's legal to insert an // addrspacecast, or if the address spaces cannot alias. LLVM_DEBUG( dbgs() << "Do not know how to expand memmove between different " "address spaces\n"); return false; } } if (ConstantInt *CI = dyn_cast(CopyLen)) { createMemMoveLoopKnownSize( /*InsertBefore=*/Memmove, SrcAddr, DstAddr, CI, SrcAlign, DstAlign, SrcIsVolatile, DstIsVolatile, TTI); } else { createMemMoveLoopUnknownSize( /*InsertBefore=*/Memmove, SrcAddr, DstAddr, CopyLen, SrcAlign, DstAlign, SrcIsVolatile, DstIsVolatile, TTI); } return true; } void llvm::expandMemSetAsLoop(MemSetInst *Memset, const TargetTransformInfo *TTI) { auto AverageTripCount = getAverageMemOpLoopTripCount(*Memset); if (ConstantInt *CI = dyn_cast(Memset->getLength())) { createMemSetLoopKnownSize( /*InsertBefore=*/Memset, /*DstAddr=*/Memset->getRawDest(), /*Len=*/CI, /*SetValue=*/Memset->getValue(), /*DstAlign=*/Memset->getDestAlign().valueOrOne(), /*IsVolatile=*/Memset->isVolatile(), /*TTI=*/TTI, /*AverageTripCount=*/AverageTripCount); } else { createMemSetLoopUnknownSize( /*InsertBefore=*/Memset, /*DstAddr=*/Memset->getRawDest(), /*Len=*/Memset->getLength(), /*SetValue=*/Memset->getValue(), /*DstAlign=*/Memset->getDestAlign().valueOrOne(), /*IsVolatile=*/Memset->isVolatile(), /*TTI=*/TTI, /*AverageTripCount=*/AverageTripCount); } } void llvm::expandMemSetAsLoop(MemSetInst *MemSet, const TargetTransformInfo &TTI) { expandMemSetAsLoop(MemSet, &TTI); } void llvm::expandMemSetPatternAsLoop(MemSetPatternInst *Memset, const TargetTransformInfo *TTI) { createMemSetPatternLoop( /*InsertBefore=*/Memset, /*DstAddr=*/Memset->getRawDest(), /*Len=*/Memset->getLength(), /*SetValue=*/Memset->getValue(), /*DstAlign=*/Memset->getDestAlign().valueOrOne(), /*IsVolatile=*/Memset->isVolatile(), /*TTI=*/TTI, /*AverageTripCount=*/getAverageMemOpLoopTripCount(*Memset)); } void llvm::expandMemSetPatternAsLoop(MemSetPatternInst *MemSet, const TargetTransformInfo &TTI) { expandMemSetPatternAsLoop(MemSet, &TTI); } void llvm::expandAtomicMemCpyAsLoop(AnyMemCpyInst *AtomicMemcpy, const TargetTransformInfo &TTI, ScalarEvolution *SE) { assert(AtomicMemcpy->isAtomic()); if (ConstantInt *CI = dyn_cast(AtomicMemcpy->getLength())) { createMemCpyLoopKnownSize( /*InsertBefore=*/AtomicMemcpy, /*SrcAddr=*/AtomicMemcpy->getRawSource(), /*DstAddr=*/AtomicMemcpy->getRawDest(), /*CopyLen=*/CI, /*SrcAlign=*/AtomicMemcpy->getSourceAlign().valueOrOne(), /*DstAlign=*/AtomicMemcpy->getDestAlign().valueOrOne(), /*SrcIsVolatile=*/AtomicMemcpy->isVolatile(), /*DstIsVolatile=*/AtomicMemcpy->isVolatile(), /*CanOverlap=*/false, // SrcAddr & DstAddr may not overlap by spec. /*TTI=*/TTI, /*AtomicElementSize=*/AtomicMemcpy->getElementSizeInBytes()); } else { createMemCpyLoopUnknownSize( /*InsertBefore=*/AtomicMemcpy, /*SrcAddr=*/AtomicMemcpy->getRawSource(), /*DstAddr=*/AtomicMemcpy->getRawDest(), /*CopyLen=*/AtomicMemcpy->getLength(), /*SrcAlign=*/AtomicMemcpy->getSourceAlign().valueOrOne(), /*DstAlign=*/AtomicMemcpy->getDestAlign().valueOrOne(), /*SrcIsVolatile=*/AtomicMemcpy->isVolatile(), /*DstIsVolatile=*/AtomicMemcpy->isVolatile(), /*CanOverlap=*/false, // SrcAddr & DstAddr may not overlap by spec. /*TargetTransformInfo=*/TTI, /*AtomicElementSize=*/AtomicMemcpy->getElementSizeInBytes()); } }