[llvm] [GlobalISel] Add a shared matcher for memcpy-family instructions (NFC) (PR #201766)
Cullen Rhodes via llvm-commits
llvm-commits at lists.llvm.org
Mon Jun 8 00:01:56 PDT 2026
================
@@ -2078,3 +2081,177 @@ llvm::GFConstant::getConstant(Register Const, const MachineRegisterInfo &MRI) {
return GFConstant(MayBeConstant->Value, GFConstantKind::Scalar);
}
+
+// Returns a list of types to use for memory op lowering in MemOps. A partial
+// port of findOptimalMemOpLowering in TargetLowering.
+static bool findGISelOptimalMemOpLowering(std::vector<LLT> &MemOps,
+ unsigned Limit, const MemOp &Op,
+ unsigned DstAS, unsigned SrcAS,
+ const AttributeList &FuncAttributes,
+ const TargetLowering &TLI) {
+ if (Op.isMemcpyWithFixedDstAlign() && Op.getSrcAlign() < Op.getDstAlign())
+ return false;
+
+ LLT Ty = TLI.getOptimalMemOpLLT(Op, FuncAttributes);
+
+ if (Ty == LLT()) {
+ // Use the largest scalar type whose alignment constraints are satisfied.
+ // We only need to check DstAlign here as SrcAlign is always greater or
+ // equal to DstAlign (or zero).
+ Ty = LLT::integer(64);
+ if (Op.isFixedDstAlign())
+ while (Op.getDstAlign() < Ty.getSizeInBytes() &&
+ !TLI.allowsMisalignedMemoryAccesses(Ty, DstAS, Op.getDstAlign()))
+ Ty = LLT::integer(Ty.getSizeInBytes());
+ assert(Ty.getSizeInBits() > 0 && "Could not find valid type");
+ // FIXME: check for the largest legal type we can load/store to.
+ }
+
+ unsigned NumMemOps = 0;
+ uint64_t Size = Op.size();
+ while (Size) {
+ unsigned TySize = Ty.getSizeInBytes();
+ while (TySize > Size) {
+ // For now, only use non-vector load / store's for the left-over pieces.
+ LLT NewTy = Ty;
+ // FIXME: check for mem op safety and legality of the types. Not all of
+ // SDAGisms map cleanly to GISel concepts.
+ if (NewTy.isVector())
+ NewTy =
+ NewTy.getSizeInBits() > 64 ? LLT::integer(64) : LLT::integer(32);
+ NewTy = LLT::integer(llvm::bit_floor(NewTy.getSizeInBits() - 1));
+ unsigned NewTySize = NewTy.getSizeInBytes();
+ assert(NewTySize > 0 && "Could not find appropriate type");
+
+ // If the new LLT cannot cover all of the remaining bits, then consider
+ // issuing a (or a pair of) unaligned and overlapping load / store.
+ unsigned Fast;
+ // Need to get a VT equivalent for allowMisalignedMemoryAccesses().
+ MVT VT = getMVTForLLT(Ty);
+ if (NumMemOps && Op.allowOverlap() && NewTySize < Size &&
+ TLI.allowsMisalignedMemoryAccesses(
+ VT, DstAS, Op.isFixedDstAlign() ? Op.getDstAlign() : Align(1),
+ MachineMemOperand::MONone, &Fast) &&
+ Fast)
+ TySize = Size;
+ else {
+ Ty = NewTy;
+ TySize = NewTySize;
+ }
+ }
+
+ if (++NumMemOps > Limit)
+ return false;
+
+ MemOps.push_back(Ty);
+ Size -= TySize;
+ }
+
+ return true;
+}
+
+bool llvm::canLowerMemCpyFamily(const MachineInstr &MI,
+ const MachineRegisterInfo &MRI, unsigned MaxLen,
+ Register &Dst, Register &Src,
+ uint64_t &KnownLen, Align &DstAlign,
+ Align &SrcAlign, bool &IsVolatile,
+ bool &DstAlignCanChange,
+ std::vector<LLT> &MemOps) {
+ const unsigned Opc = MI.getOpcode();
+ assert((Opc == TargetOpcode::G_MEMCPY ||
+ Opc == TargetOpcode::G_MEMCPY_INLINE ||
+ Opc == TargetOpcode::G_MEMMOVE || Opc == TargetOpcode::G_MEMSET) &&
+ "Expected memcpy like instruction");
+
+ auto MMOIt = MI.memoperands_begin();
+ const MachineMemOperand *MemOp = *MMOIt;
+
+ DstAlign = MemOp->getBaseAlign();
+ Register Len;
+ std::tie(Dst, Src, Len) = MI.getFirst3Regs();
+
+ if (Opc != TargetOpcode::G_MEMSET) {
+ assert(MMOIt != MI.memoperands_end() && "Expected a second MMO on MI");
+ MemOp = *(++MMOIt);
+ SrcAlign = MemOp->getBaseAlign();
+ }
+
+ // See if this is a constant length copy.
+ auto LenVRegAndVal = getIConstantVRegValWithLookThrough(Len, MRI);
+ if (!LenVRegAndVal) {
+ // FIXME: support dynamically sized G_MEMCPY_INLINE
+ assert(Opc != TargetOpcode::G_MEMCPY_INLINE &&
+ "inline memcpy with dynamic size is not yet supported");
+ return false;
+ }
+
+ KnownLen = LenVRegAndVal->Value.getZExtValue();
+ DstAlignCanChange = false;
+
+ if (KnownLen == 0)
+ return true;
+
+ if (Opc != TargetOpcode::G_MEMCPY_INLINE && MaxLen && KnownLen > MaxLen)
+ return false;
+
+ IsVolatile = MemOp->isVolatile();
----------------
c-rhodes wrote:
this broke sanitizer-aarch64-linux-bootstrap-ubsan: https://lab.llvm.org/buildbot/#/builders/85/builds/22356
it returns true above for zero-length ops before initializing this, but it's only used below in the findGISelOptimalMemOpLowering calls and the lowering helpers don't actually need IsVolatile so it should never have been forward, will post fix shortly
https://github.com/llvm/llvm-project/pull/201766
More information about the llvm-commits
mailing list