[MemoryBuiltins] Add getFreedOperand() function (NFCI)

We currently assume in a number of places that free-like functions
free their first argument. This is true for all hardcoded free-like
functions, but with the new attribute-based design, the freed
argument is supposed to be indicated by the allocptr attribute.

To make sure we handle this correctly once allockind(free) is
respected, add a getFreedOperand() helper which returns the freed
argument, rather than just indicating whether the call frees *some*
argument.

This migrates most but not all users of isFreeCall() to the new
API. The remaining users are a bit more tricky.
This commit is contained in:
Nikita Popov 2022-07-21 12:10:36 +02:00
parent d2a4d6bf9c
commit c81dff3c30
10 changed files with 33 additions and 30 deletions

View File

@ -80,12 +80,11 @@ bool isReallocLikeFn(const Function *F, const TargetLibraryInfo *TLI);
/// isLibFreeFunction - Returns true if the function is a builtin free()
bool isLibFreeFunction(const Function *F, const LibFunc TLIFn);
/// isFreeCall - Returns non-null if the value is a call to the builtin free()
const CallInst *isFreeCall(const Value *I, const TargetLibraryInfo *TLI);
/// Returns true if the value is a call to a free function.
bool isFreeCall(const Value *I, const TargetLibraryInfo *TLI);
inline CallInst *isFreeCall(Value *I, const TargetLibraryInfo *TLI) {
return const_cast<CallInst*>(isFreeCall((const Value*)I, TLI));
}
/// If this if a call to a free function, return the freed operand.
Value *getFreedOperand(const CallBase *CB, const TargetLibraryInfo *TLI);
//===----------------------------------------------------------------------===//
// Properties of allocation functions

View File

@ -361,7 +361,7 @@ bool GlobalsAAResult::AnalyzeUsesOfPointer(Value *V,
if (Call->isDataOperand(&U)) {
// Detect calls to free.
if (Call->isArgOperand(&U) &&
isFreeCall(I, &GetTLI(*Call->getFunction()))) {
getFreedOperand(Call, &GetTLI(*Call->getFunction())) == U) {
if (Writers)
Writers->insert(Call->getParent()->getParent());
} else {

View File

@ -531,20 +531,25 @@ bool llvm::isLibFreeFunction(const Function *F, const LibFunc TLIFn) {
return true;
}
/// isFreeCall - Returns non-null if the value is a call to the builtin free()
const CallInst *llvm::isFreeCall(const Value *I, const TargetLibraryInfo *TLI) {
bool llvm::isFreeCall(const Value *I, const TargetLibraryInfo *TLI) {
bool IsNoBuiltinCall;
const Function *Callee = getCalledFunction(I, IsNoBuiltinCall);
if (Callee == nullptr || IsNoBuiltinCall)
return nullptr;
return false;
LibFunc TLIFn;
if (!TLI || !TLI->getLibFunc(*Callee, TLIFn) || !TLI->has(TLIFn))
return nullptr;
return false;
return isLibFreeFunction(Callee, TLIFn) ? dyn_cast<CallInst>(I) : nullptr;
return isLibFreeFunction(Callee, TLIFn);
}
Value *llvm::getFreedOperand(const CallBase *CB, const TargetLibraryInfo *TLI) {
// All currently supported free functions free the first argument.
if (isFreeCall(CB, TLI))
return CB->getArgOperand(0);
return nullptr;
}
//===----------------------------------------------------------------------===//
// Utility functions to compute size of objects.

View File

@ -139,10 +139,12 @@ static ModRefInfo GetLocation(const Instruction *Inst, MemoryLocation &Loc,
return ModRefInfo::ModRef;
}
if (const CallInst *CI = isFreeCall(Inst, &TLI)) {
// calls to free() deallocate the entire structure
Loc = MemoryLocation::getAfter(CI->getArgOperand(0));
return ModRefInfo::Mod;
if (const CallBase *CB = dyn_cast<CallBase>(Inst)) {
if (Value *FreedOp = getFreedOperand(CB, &TLI)) {
// calls to free() deallocate the entire structure
Loc = MemoryLocation::getAfter(FreedOp);
return ModRefInfo::Mod;
}
}
if (const IntrinsicInst *II = dyn_cast<IntrinsicInst>(Inst)) {

View File

@ -1372,7 +1372,7 @@ struct AAPointerInfoFloating : public AAPointerInfoImpl {
if (auto *CB = dyn_cast<CallBase>(Usr)) {
if (CB->isLifetimeStartOrEnd())
return true;
if (TLI && isFreeCall(CB, TLI))
if (getFreedOperand(CB, TLI) == U)
return true;
if (CB->isArgOperand(&U)) {
unsigned ArgNo = CB->getArgOperandNo(&U);

View File

@ -1140,8 +1140,8 @@ Instruction *InstCombinerImpl::visitCallInst(CallInst &CI) {
if (Value *V = simplifyCall(&CI, SQ.getWithInstruction(&CI)))
return replaceInstUsesWith(CI, V);
if (isFreeCall(&CI, &TLI))
return visitFree(CI);
if (Value *FreedOp = getFreedOperand(&CI, &TLI))
return visitFree(CI, FreedOp);
// If the caller function (i.e. us, the function that contains this CallInst)
// is nounwind, mark the call as nounwind, even if the callee isn't.

View File

@ -152,7 +152,7 @@ public:
Instruction *visitGEPOfBitcast(BitCastInst *BCI, GetElementPtrInst &GEP);
Instruction *visitAllocaInst(AllocaInst &AI);
Instruction *visitAllocSite(Instruction &FI);
Instruction *visitFree(CallInst &FI);
Instruction *visitFree(CallInst &FI, Value *FreedOp);
Instruction *visitLoadInst(LoadInst &LI);
Instruction *visitStoreInst(StoreInst &SI);
Instruction *visitAtomicRMWInst(AtomicRMWInst &SI);

View File

@ -3034,9 +3034,7 @@ static Instruction *tryToMoveFreeBeforeNullTest(CallInst &FI,
return &FI;
}
Instruction *InstCombinerImpl::visitFree(CallInst &FI) {
Value *Op = FI.getArgOperand(0);
Instruction *InstCombinerImpl::visitFree(CallInst &FI, Value *Op) {
// free undef -> unreachable.
if (isa<UndefValue>(Op)) {
// Leave a marker since we can't modify the CFG here.

View File

@ -1108,9 +1108,8 @@ struct DSEState {
return {std::make_pair(MemoryLocation(Ptr, Len), false)};
if (auto *CB = dyn_cast<CallBase>(I)) {
if (isFreeCall(I, &TLI))
return {std::make_pair(MemoryLocation::getAfter(CB->getArgOperand(0)),
true)};
if (Value *FreedOp = getFreedOperand(CB, &TLI))
return {std::make_pair(MemoryLocation::getAfter(FreedOp), true)};
}
return None;

View File

@ -493,13 +493,13 @@ bool llvm::wouldInstructionBeTriviallyDead(Instruction *I,
}
}
if (CallInst *CI = isFreeCall(I, TLI))
if (Constant *C = dyn_cast<Constant>(CI->getArgOperand(0)))
return C->isNullValue() || isa<UndefValue>(C);
if (auto *Call = dyn_cast<CallBase>(I))
if (auto *Call = dyn_cast<CallBase>(I)) {
if (Value *FreedOp = getFreedOperand(Call, TLI))
if (Constant *C = dyn_cast<Constant>(FreedOp))
return C->isNullValue() || isa<UndefValue>(C);
if (isMathLibCallNoop(Call, TLI))
return true;
}
// Non-volatile atomic loads from constants can be removed.
if (auto *LI = dyn_cast<LoadInst>(I))