Skip to content

Commit 76226aa

Browse files
aemersonyuxuanchen1997
authored andcommitted
[AArch64] Don't tail call memset if it would convert to a bzero. (#98969)
Summary: Well, not quite that simple. We can tc memset since it returns the first argument but bzero doesn't do that and therefore we can end up miscompiling. This patch also refactors the logic out of isInTailCallPosition() into the callers. As a result memcpy and memmove are also modified to do the same thing for consistency. rdar://131419786 Test Plan: Reviewers: Subscribers: Tasks: Tags: Differential Revision: https://phabricator.intern.facebook.com/D60250902
1 parent 8f8b387 commit 76226aa

22 files changed

+172
-122
lines changed

llvm/include/llvm/CodeGen/Analysis.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,8 @@ ICmpInst::Predicate getICmpCondCode(ISD::CondCode Pred);
126126
/// between it and the return.
127127
///
128128
/// This function only tests target-independent requirements.
129-
bool isInTailCallPosition(const CallBase &Call, const TargetMachine &TM);
129+
bool isInTailCallPosition(const CallBase &Call, const TargetMachine &TM,
130+
bool ReturnsFirstArg = false);
130131

131132
/// Test if given that the input instruction is in the tail call position, if
132133
/// there is an attribute mismatch between the caller and the callee that will
@@ -144,7 +145,12 @@ bool attributesPermitTailCall(const Function *F, const Instruction *I,
144145
/// optimization.
145146
bool returnTypeIsEligibleForTailCall(const Function *F, const Instruction *I,
146147
const ReturnInst *Ret,
147-
const TargetLoweringBase &TLI);
148+
const TargetLoweringBase &TLI,
149+
bool ReturnsFirstArg = false);
150+
151+
/// Returns true if the parent of \p CI returns CI's first argument after
152+
/// calling \p CI.
153+
bool funcReturnsFirstArgOfCall(const CallInst &CI);
148154

149155
DenseMap<const MachineBasicBlock *, int>
150156
getEHScopeMembership(const MachineFunction &MF);

llvm/include/llvm/CodeGen/SelectionDAG.h

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1182,24 +1182,30 @@ class SelectionDAG {
11821182
/// stack arguments from being clobbered.
11831183
SDValue getStackArgumentTokenFactor(SDValue Chain);
11841184

1185-
SDValue getMemcpy(SDValue Chain, const SDLoc &dl, SDValue Dst, SDValue Src,
1186-
SDValue Size, Align Alignment, bool isVol,
1187-
bool AlwaysInline, bool isTailCall,
1188-
MachinePointerInfo DstPtrInfo,
1189-
MachinePointerInfo SrcPtrInfo,
1190-
const AAMDNodes &AAInfo = AAMDNodes(),
1191-
AAResults *AA = nullptr);
1192-
1185+
/* \p CI if not null is the memset call being lowered.
1186+
* \p OverrideTailCall is an optional parameter that can be used to override
1187+
* the tail call optimization decision. */
1188+
SDValue
1189+
getMemcpy(SDValue Chain, const SDLoc &dl, SDValue Dst, SDValue Src,
1190+
SDValue Size, Align Alignment, bool isVol, bool AlwaysInline,
1191+
const CallInst *CI, std::optional<bool> OverrideTailCall,
1192+
MachinePointerInfo DstPtrInfo, MachinePointerInfo SrcPtrInfo,
1193+
const AAMDNodes &AAInfo = AAMDNodes(), AAResults *AA = nullptr);
1194+
1195+
/* \p CI if not null is the memset call being lowered.
1196+
* \p OverrideTailCall is an optional parameter that can be used to override
1197+
* the tail call optimization decision. */
11931198
SDValue getMemmove(SDValue Chain, const SDLoc &dl, SDValue Dst, SDValue Src,
1194-
SDValue Size, Align Alignment, bool isVol, bool isTailCall,
1199+
SDValue Size, Align Alignment, bool isVol,
1200+
const CallInst *CI, std::optional<bool> OverrideTailCall,
11951201
MachinePointerInfo DstPtrInfo,
11961202
MachinePointerInfo SrcPtrInfo,
11971203
const AAMDNodes &AAInfo = AAMDNodes(),
11981204
AAResults *AA = nullptr);
11991205

12001206
SDValue getMemset(SDValue Chain, const SDLoc &dl, SDValue Dst, SDValue Src,
12011207
SDValue Size, Align Alignment, bool isVol,
1202-
bool AlwaysInline, bool isTailCall,
1208+
bool AlwaysInline, const CallInst *CI,
12031209
MachinePointerInfo DstPtrInfo,
12041210
const AAMDNodes &AAInfo = AAMDNodes());
12051211

llvm/lib/CodeGen/Analysis.cpp

Lines changed: 19 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -532,7 +532,8 @@ static bool nextRealType(SmallVectorImpl<Type *> &SubTypes,
532532
/// between it and the return.
533533
///
534534
/// This function only tests target-independent requirements.
535-
bool llvm::isInTailCallPosition(const CallBase &Call, const TargetMachine &TM) {
535+
bool llvm::isInTailCallPosition(const CallBase &Call, const TargetMachine &TM,
536+
bool ReturnsFirstArg) {
536537
const BasicBlock *ExitBB = Call.getParent();
537538
const Instruction *Term = ExitBB->getTerminator();
538539
const ReturnInst *Ret = dyn_cast<ReturnInst>(Term);
@@ -575,7 +576,8 @@ bool llvm::isInTailCallPosition(const CallBase &Call, const TargetMachine &TM) {
575576

576577
const Function *F = ExitBB->getParent();
577578
return returnTypeIsEligibleForTailCall(
578-
F, &Call, Ret, *TM.getSubtargetImpl(*F)->getTargetLowering());
579+
F, &Call, Ret, *TM.getSubtargetImpl(*F)->getTargetLowering(),
580+
ReturnsFirstArg);
579581
}
580582

581583
bool llvm::attributesPermitTailCall(const Function *F, const Instruction *I,
@@ -638,26 +640,11 @@ bool llvm::attributesPermitTailCall(const Function *F, const Instruction *I,
638640
return CallerAttrs == CalleeAttrs;
639641
}
640642

641-
/// Check whether B is a bitcast of a pointer type to another pointer type,
642-
/// which is equal to A.
643-
static bool isPointerBitcastEqualTo(const Value *A, const Value *B) {
644-
assert(A && B && "Expected non-null inputs!");
645-
646-
auto *BitCastIn = dyn_cast<BitCastInst>(B);
647-
648-
if (!BitCastIn)
649-
return false;
650-
651-
if (!A->getType()->isPointerTy() || !B->getType()->isPointerTy())
652-
return false;
653-
654-
return A == BitCastIn->getOperand(0);
655-
}
656-
657643
bool llvm::returnTypeIsEligibleForTailCall(const Function *F,
658644
const Instruction *I,
659645
const ReturnInst *Ret,
660-
const TargetLoweringBase &TLI) {
646+
const TargetLoweringBase &TLI,
647+
bool ReturnsFirstArg) {
661648
// If the block ends with a void return or unreachable, it doesn't matter
662649
// what the call's return type is.
663650
if (!Ret || Ret->getNumOperands() == 0) return true;
@@ -671,26 +658,11 @@ bool llvm::returnTypeIsEligibleForTailCall(const Function *F,
671658
if (!attributesPermitTailCall(F, I, Ret, TLI, &AllowDifferingSizes))
672659
return false;
673660

674-
const Value *RetVal = Ret->getOperand(0), *CallVal = I;
675-
// Intrinsic like llvm.memcpy has no return value, but the expanded
676-
// libcall may or may not have return value. On most platforms, it
677-
// will be expanded as memcpy in libc, which returns the first
678-
// argument. On other platforms like arm-none-eabi, memcpy may be
679-
// expanded as library call without return value, like __aeabi_memcpy.
680-
const CallInst *Call = cast<CallInst>(I);
681-
if (Function *F = Call->getCalledFunction()) {
682-
Intrinsic::ID IID = F->getIntrinsicID();
683-
if (((IID == Intrinsic::memcpy &&
684-
TLI.getLibcallName(RTLIB::MEMCPY) == StringRef("memcpy")) ||
685-
(IID == Intrinsic::memmove &&
686-
TLI.getLibcallName(RTLIB::MEMMOVE) == StringRef("memmove")) ||
687-
(IID == Intrinsic::memset &&
688-
TLI.getLibcallName(RTLIB::MEMSET) == StringRef("memset"))) &&
689-
(RetVal == Call->getArgOperand(0) ||
690-
isPointerBitcastEqualTo(RetVal, Call->getArgOperand(0))))
691-
return true;
692-
}
661+
// If the return value is the first argument of the call.
662+
if (ReturnsFirstArg)
663+
return true;
693664

665+
const Value *RetVal = Ret->getOperand(0), *CallVal = I;
694666
SmallVector<unsigned, 4> RetPath, CallPath;
695667
SmallVector<Type *, 4> RetSubTypes, CallSubTypes;
696668

@@ -739,6 +711,15 @@ bool llvm::returnTypeIsEligibleForTailCall(const Function *F,
739711
return true;
740712
}
741713

714+
bool llvm::funcReturnsFirstArgOfCall(const CallInst &CI) {
715+
const ReturnInst *Ret = dyn_cast<ReturnInst>(CI.getParent()->getTerminator());
716+
Value *RetVal = Ret ? Ret->getReturnValue() : nullptr;
717+
bool ReturnsFirstArg = false;
718+
if (RetVal && ((RetVal == CI.getArgOperand(0))))
719+
ReturnsFirstArg = true;
720+
return ReturnsFirstArg;
721+
}
722+
742723
static void collectEHScopeMembers(
743724
DenseMap<const MachineBasicBlock *, int> &EHScopeMembership, int EHScope,
744725
const MachineBasicBlock *MBB) {

llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp

Lines changed: 49 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
#include <cstdint>
7676
#include <cstdlib>
7777
#include <limits>
78+
#include <optional>
7879
#include <set>
7980
#include <string>
8081
#include <utility>
@@ -8236,12 +8237,11 @@ static void checkAddrSpaceIsValidForLibcall(const TargetLowering *TLI,
82368237
}
82378238
}
82388239

8239-
SDValue SelectionDAG::getMemcpy(SDValue Chain, const SDLoc &dl, SDValue Dst,
8240-
SDValue Src, SDValue Size, Align Alignment,
8241-
bool isVol, bool AlwaysInline, bool isTailCall,
8242-
MachinePointerInfo DstPtrInfo,
8243-
MachinePointerInfo SrcPtrInfo,
8244-
const AAMDNodes &AAInfo, AAResults *AA) {
8240+
SDValue SelectionDAG::getMemcpy(
8241+
SDValue Chain, const SDLoc &dl, SDValue Dst, SDValue Src, SDValue Size,
8242+
Align Alignment, bool isVol, bool AlwaysInline, const CallInst *CI,
8243+
std::optional<bool> OverrideTailCall, MachinePointerInfo DstPtrInfo,
8244+
MachinePointerInfo SrcPtrInfo, const AAMDNodes &AAInfo, AAResults *AA) {
82458245
// Check to see if we should lower the memcpy to loads and stores first.
82468246
// For cases within the target-specified limits, this is the best choice.
82478247
ConstantSDNode *ConstantSize = dyn_cast<ConstantSDNode>(Size);
@@ -8296,6 +8296,18 @@ SDValue SelectionDAG::getMemcpy(SDValue Chain, const SDLoc &dl, SDValue Dst,
82968296
Entry.Node = Size; Args.push_back(Entry);
82978297
// FIXME: pass in SDLoc
82988298
TargetLowering::CallLoweringInfo CLI(*this);
8299+
bool IsTailCall = false;
8300+
if (OverrideTailCall.has_value()) {
8301+
IsTailCall = *OverrideTailCall;
8302+
} else {
8303+
bool LowersToMemcpy =
8304+
TLI->getLibcallName(RTLIB::MEMCPY) == StringRef("memcpy");
8305+
bool ReturnsFirstArg = CI && funcReturnsFirstArgOfCall(*CI);
8306+
IsTailCall = CI && CI->isTailCall() &&
8307+
isInTailCallPosition(*CI, getTarget(),
8308+
ReturnsFirstArg && LowersToMemcpy);
8309+
}
8310+
82998311
CLI.setDebugLoc(dl)
83008312
.setChain(Chain)
83018313
.setLibCallee(TLI->getLibcallCallingConv(RTLIB::MEMCPY),
@@ -8304,7 +8316,7 @@ SDValue SelectionDAG::getMemcpy(SDValue Chain, const SDLoc &dl, SDValue Dst,
83048316
TLI->getPointerTy(getDataLayout())),
83058317
std::move(Args))
83068318
.setDiscardResult()
8307-
.setTailCall(isTailCall);
8319+
.setTailCall(IsTailCall);
83088320

83098321
std::pair<SDValue,SDValue> CallResult = TLI->LowerCallTo(CLI);
83108322
return CallResult.second;
@@ -8352,7 +8364,8 @@ SDValue SelectionDAG::getAtomicMemcpy(SDValue Chain, const SDLoc &dl,
83528364

83538365
SDValue SelectionDAG::getMemmove(SDValue Chain, const SDLoc &dl, SDValue Dst,
83548366
SDValue Src, SDValue Size, Align Alignment,
8355-
bool isVol, bool isTailCall,
8367+
bool isVol, const CallInst *CI,
8368+
std::optional<bool> OverrideTailCall,
83568369
MachinePointerInfo DstPtrInfo,
83578370
MachinePointerInfo SrcPtrInfo,
83588371
const AAMDNodes &AAInfo, AAResults *AA) {
@@ -8398,6 +8411,19 @@ SDValue SelectionDAG::getMemmove(SDValue Chain, const SDLoc &dl, SDValue Dst,
83988411
Entry.Node = Size; Args.push_back(Entry);
83998412
// FIXME: pass in SDLoc
84008413
TargetLowering::CallLoweringInfo CLI(*this);
8414+
8415+
bool IsTailCall = false;
8416+
if (OverrideTailCall.has_value()) {
8417+
IsTailCall = *OverrideTailCall;
8418+
} else {
8419+
bool LowersToMemmove =
8420+
TLI->getLibcallName(RTLIB::MEMMOVE) == StringRef("memmove");
8421+
bool ReturnsFirstArg = CI && funcReturnsFirstArgOfCall(*CI);
8422+
IsTailCall = CI && CI->isTailCall() &&
8423+
isInTailCallPosition(*CI, getTarget(),
8424+
ReturnsFirstArg && LowersToMemmove);
8425+
}
8426+
84018427
CLI.setDebugLoc(dl)
84028428
.setChain(Chain)
84038429
.setLibCallee(TLI->getLibcallCallingConv(RTLIB::MEMMOVE),
@@ -8406,7 +8432,7 @@ SDValue SelectionDAG::getMemmove(SDValue Chain, const SDLoc &dl, SDValue Dst,
84068432
TLI->getPointerTy(getDataLayout())),
84078433
std::move(Args))
84088434
.setDiscardResult()
8409-
.setTailCall(isTailCall);
8435+
.setTailCall(IsTailCall);
84108436

84118437
std::pair<SDValue,SDValue> CallResult = TLI->LowerCallTo(CLI);
84128438
return CallResult.second;
@@ -8454,7 +8480,8 @@ SDValue SelectionDAG::getAtomicMemmove(SDValue Chain, const SDLoc &dl,
84548480

84558481
SDValue SelectionDAG::getMemset(SDValue Chain, const SDLoc &dl, SDValue Dst,
84568482
SDValue Src, SDValue Size, Align Alignment,
8457-
bool isVol, bool AlwaysInline, bool isTailCall,
8483+
bool isVol, bool AlwaysInline,
8484+
const CallInst *CI,
84588485
MachinePointerInfo DstPtrInfo,
84598486
const AAMDNodes &AAInfo) {
84608487
// Check to see if we should lower the memset to stores first.
@@ -8514,8 +8541,9 @@ SDValue SelectionDAG::getMemset(SDValue Chain, const SDLoc &dl, SDValue Dst,
85148541
return Entry;
85158542
};
85168543

8544+
bool UseBZero = isNullConstant(Src) && BzeroName;
85178545
// If zeroing out and bzero is present, use it.
8518-
if (isNullConstant(Src) && BzeroName) {
8546+
if (UseBZero) {
85198547
TargetLowering::ArgListTy Args;
85208548
Args.push_back(CreateEntry(Dst, PointerType::getUnqual(Ctx)));
85218549
Args.push_back(CreateEntry(Size, DL.getIntPtrType(Ctx)));
@@ -8533,8 +8561,16 @@ SDValue SelectionDAG::getMemset(SDValue Chain, const SDLoc &dl, SDValue Dst,
85338561
TLI->getPointerTy(DL)),
85348562
std::move(Args));
85358563
}
8536-
8537-
CLI.setDiscardResult().setTailCall(isTailCall);
8564+
bool LowersToMemset =
8565+
TLI->getLibcallName(RTLIB::MEMSET) == StringRef("memset");
8566+
// If we're going to use bzero, make sure not to tail call unless the
8567+
// subsequent return doesn't need a value, as bzero doesn't return the first
8568+
// arg unlike memset.
8569+
bool ReturnsFirstArg = CI && funcReturnsFirstArgOfCall(*CI) && !UseBZero;
8570+
bool IsTailCall =
8571+
CI && CI->isTailCall() &&
8572+
isInTailCallPosition(*CI, getTarget(), ReturnsFirstArg && LowersToMemset);
8573+
CLI.setDiscardResult().setTailCall(IsTailCall);
85388574

85398575
std::pair<SDValue, SDValue> CallResult = TLI->LowerCallTo(CLI);
85408576
return CallResult.second;

llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp

Lines changed: 19 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6460,14 +6460,14 @@ void SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I,
64606460
Align SrcAlign = MCI.getSourceAlign().valueOrOne();
64616461
Align Alignment = std::min(DstAlign, SrcAlign);
64626462
bool isVol = MCI.isVolatile();
6463-
bool isTC = I.isTailCall() && isInTailCallPosition(I, DAG.getTarget());
64646463
// FIXME: Support passing different dest/src alignments to the memcpy DAG
64656464
// node.
64666465
SDValue Root = isVol ? getRoot() : getMemoryRoot();
6467-
SDValue MC = DAG.getMemcpy(
6468-
Root, sdl, Op1, Op2, Op3, Alignment, isVol,
6469-
/* AlwaysInline */ false, isTC, MachinePointerInfo(I.getArgOperand(0)),
6470-
MachinePointerInfo(I.getArgOperand(1)), I.getAAMetadata(), AA);
6466+
SDValue MC = DAG.getMemcpy(Root, sdl, Op1, Op2, Op3, Alignment, isVol,
6467+
/* AlwaysInline */ false, &I, std::nullopt,
6468+
MachinePointerInfo(I.getArgOperand(0)),
6469+
MachinePointerInfo(I.getArgOperand(1)),
6470+
I.getAAMetadata(), AA);
64716471
updateDAGForMaybeTailCall(MC);
64726472
return;
64736473
}
@@ -6482,13 +6482,13 @@ void SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I,
64826482
Align SrcAlign = MCI.getSourceAlign().valueOrOne();
64836483
Align Alignment = std::min(DstAlign, SrcAlign);
64846484
bool isVol = MCI.isVolatile();
6485-
bool isTC = I.isTailCall() && isInTailCallPosition(I, DAG.getTarget());
64866485
// FIXME: Support passing different dest/src alignments to the memcpy DAG
64876486
// node.
6488-
SDValue MC = DAG.getMemcpy(
6489-
getRoot(), sdl, Dst, Src, Size, Alignment, isVol,
6490-
/* AlwaysInline */ true, isTC, MachinePointerInfo(I.getArgOperand(0)),
6491-
MachinePointerInfo(I.getArgOperand(1)), I.getAAMetadata(), AA);
6487+
SDValue MC = DAG.getMemcpy(getRoot(), sdl, Dst, Src, Size, Alignment, isVol,
6488+
/* AlwaysInline */ true, &I, std::nullopt,
6489+
MachinePointerInfo(I.getArgOperand(0)),
6490+
MachinePointerInfo(I.getArgOperand(1)),
6491+
I.getAAMetadata(), AA);
64926492
updateDAGForMaybeTailCall(MC);
64936493
return;
64946494
}
@@ -6500,11 +6500,10 @@ void SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I,
65006500
// @llvm.memset defines 0 and 1 to both mean no alignment.
65016501
Align Alignment = MSI.getDestAlign().valueOrOne();
65026502
bool isVol = MSI.isVolatile();
6503-
bool isTC = I.isTailCall() && isInTailCallPosition(I, DAG.getTarget());
65046503
SDValue Root = isVol ? getRoot() : getMemoryRoot();
65056504
SDValue MS = DAG.getMemset(
65066505
Root, sdl, Op1, Op2, Op3, Alignment, isVol, /* AlwaysInline */ false,
6507-
isTC, MachinePointerInfo(I.getArgOperand(0)), I.getAAMetadata());
6506+
&I, MachinePointerInfo(I.getArgOperand(0)), I.getAAMetadata());
65086507
updateDAGForMaybeTailCall(MS);
65096508
return;
65106509
}
@@ -6517,10 +6516,9 @@ void SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I,
65176516
// @llvm.memset defines 0 and 1 to both mean no alignment.
65186517
Align DstAlign = MSII.getDestAlign().valueOrOne();
65196518
bool isVol = MSII.isVolatile();
6520-
bool isTC = I.isTailCall() && isInTailCallPosition(I, DAG.getTarget());
65216519
SDValue Root = isVol ? getRoot() : getMemoryRoot();
65226520
SDValue MC = DAG.getMemset(Root, sdl, Dst, Value, Size, DstAlign, isVol,
6523-
/* AlwaysInline */ true, isTC,
6521+
/* AlwaysInline */ true, &I,
65246522
MachinePointerInfo(I.getArgOperand(0)),
65256523
I.getAAMetadata());
65266524
updateDAGForMaybeTailCall(MC);
@@ -6536,12 +6534,12 @@ void SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I,
65366534
Align SrcAlign = MMI.getSourceAlign().valueOrOne();
65376535
Align Alignment = std::min(DstAlign, SrcAlign);
65386536
bool isVol = MMI.isVolatile();
6539-
bool isTC = I.isTailCall() && isInTailCallPosition(I, DAG.getTarget());
65406537
// FIXME: Support passing different dest/src alignments to the memmove DAG
65416538
// node.
65426539
SDValue Root = isVol ? getRoot() : getMemoryRoot();
6543-
SDValue MM = DAG.getMemmove(Root, sdl, Op1, Op2, Op3, Alignment, isVol,
6544-
isTC, MachinePointerInfo(I.getArgOperand(0)),
6540+
SDValue MM = DAG.getMemmove(Root, sdl, Op1, Op2, Op3, Alignment, isVol, &I,
6541+
/* OverrideTailCall */ std::nullopt,
6542+
MachinePointerInfo(I.getArgOperand(0)),
65456543
MachinePointerInfo(I.getArgOperand(1)),
65466544
I.getAAMetadata(), AA);
65476545
updateDAGForMaybeTailCall(MM);
@@ -9039,11 +9037,10 @@ bool SelectionDAGBuilder::visitMemPCpyCall(const CallInst &I) {
90399037
// because the return pointer needs to be adjusted by the size of
90409038
// the copied memory.
90419039
SDValue Root = getMemoryRoot();
9042-
SDValue MC = DAG.getMemcpy(Root, sdl, Dst, Src, Size, Alignment, false, false,
9043-
/*isTailCall=*/false,
9044-
MachinePointerInfo(I.getArgOperand(0)),
9045-
MachinePointerInfo(I.getArgOperand(1)),
9046-
I.getAAMetadata());
9040+
SDValue MC = DAG.getMemcpy(
9041+
Root, sdl, Dst, Src, Size, Alignment, false, false, /*CI=*/nullptr,
9042+
std::nullopt, MachinePointerInfo(I.getArgOperand(0)),
9043+
MachinePointerInfo(I.getArgOperand(1)), I.getAAMetadata());
90479044
assert(MC.getNode() != nullptr &&
90489045
"** memcpy should not be lowered as TailCall in mempcpy context **");
90499046
DAG.setRoot(MC);

0 commit comments

Comments
 (0)