[FunctionAttrs] Extract a helper function for the core logic used to

evaluate whether 'readonly' or 'readnone' apply to a given function.
This both reduces indentation and will make it easy to share the logic
with a new pass manager implementation.

llvm-svn: 248181
This commit is contained in:
Chandler Carruth 2015-09-21 17:39:41 +00:00
parent 126caeb043
commit 7542d37688
1 changed files with 117 additions and 90 deletions

View File

@ -89,6 +89,115 @@ INITIALIZE_PASS_END(FunctionAttrs, "functionattrs",
Pass *llvm::createFunctionAttrsPass() { return new FunctionAttrs(); }
namespace {
/// The three kinds of memory access relevant to 'readonly' and
/// 'readnone' attributes.
enum MemoryAccessKind {
MAK_ReadNone = 0,
MAK_ReadOnly = 1,
MAK_MayWrite = 2
};
}
static MemoryAccessKind
checkFunctionMemoryAccess(Function &F, AAResults &AAR,
const SmallPtrSetImpl<Function *> &SCCNodes) {
FunctionModRefBehavior MRB = AAR.getModRefBehavior(&F);
if (MRB == FMRB_DoesNotAccessMemory)
// Already perfect!
return MAK_ReadNone;
// Definitions with weak linkage may be overridden at linktime with
// something that writes memory, so treat them like declarations.
if (F.isDeclaration() || F.mayBeOverridden()) {
if (AliasAnalysis::onlyReadsMemory(MRB))
return MAK_ReadOnly;
// Conservatively assume it writes to memory.
return MAK_MayWrite;
}
// Scan the function body for instructions that may read or write memory.
bool ReadsMemory = false;
for (inst_iterator II = inst_begin(F), E = inst_end(F); II != E; ++II) {
Instruction *I = &*II;
// Some instructions can be ignored even if they read or write memory.
// Detect these now, skipping to the next instruction if one is found.
CallSite CS(cast<Value>(I));
if (CS) {
// Ignore calls to functions in the same SCC.
if (CS.getCalledFunction() && SCCNodes.count(CS.getCalledFunction()))
continue;
FunctionModRefBehavior MRB = AAR.getModRefBehavior(CS);
// If the call doesn't access arbitrary memory, we may be able to
// figure out something.
if (AliasAnalysis::onlyAccessesArgPointees(MRB)) {
// If the call does access argument pointees, check each argument.
if (AliasAnalysis::doesAccessArgPointees(MRB))
// Check whether all pointer arguments point to local memory, and
// ignore calls that only access local memory.
for (CallSite::arg_iterator CI = CS.arg_begin(), CE = CS.arg_end();
CI != CE; ++CI) {
Value *Arg = *CI;
if (Arg->getType()->isPointerTy()) {
AAMDNodes AAInfo;
I->getAAMetadata(AAInfo);
MemoryLocation Loc(Arg, MemoryLocation::UnknownSize, AAInfo);
if (!AAR.pointsToConstantMemory(Loc, /*OrLocal=*/true)) {
if (MRB & MRI_Mod)
// Writes non-local memory. Give up.
return MAK_MayWrite;
if (MRB & MRI_Ref)
// Ok, it reads non-local memory.
ReadsMemory = true;
}
}
}
continue;
}
// The call could access any memory. If that includes writes, give up.
if (MRB & MRI_Mod)
return MAK_MayWrite;
// If it reads, note it.
if (MRB & MRI_Ref)
ReadsMemory = true;
continue;
} else if (LoadInst *LI = dyn_cast<LoadInst>(I)) {
// Ignore non-volatile loads from local memory. (Atomic is okay here.)
if (!LI->isVolatile()) {
MemoryLocation Loc = MemoryLocation::get(LI);
if (AAR.pointsToConstantMemory(Loc, /*OrLocal=*/true))
continue;
}
} else if (StoreInst *SI = dyn_cast<StoreInst>(I)) {
// Ignore non-volatile stores to local memory. (Atomic is okay here.)
if (!SI->isVolatile()) {
MemoryLocation Loc = MemoryLocation::get(SI);
if (AAR.pointsToConstantMemory(Loc, /*OrLocal=*/true))
continue;
}
} else if (VAArgInst *VI = dyn_cast<VAArgInst>(I)) {
// Ignore vaargs on local memory.
MemoryLocation Loc = MemoryLocation::get(VI);
if (AAR.pointsToConstantMemory(Loc, /*OrLocal=*/true))
continue;
}
// Any remaining instructions need to be taken seriously! Check if they
// read or write memory.
if (I->mayWriteToMemory())
// Writes memory. Just give up.
return MAK_MayWrite;
// If this instruction may read memory, remember that.
ReadsMemory |= I->mayReadFromMemory();
}
return ReadsMemory ? MAK_ReadOnly : MAK_ReadNone;
}
/// Deduce readonly/readnone attributes for the SCC.
bool FunctionAttrs::AddReadAttrs(const CallGraphSCC &SCC) {
SmallPtrSet<Function *, 8> SCCNodes;
@ -117,97 +226,15 @@ bool FunctionAttrs::AddReadAttrs(const CallGraphSCC &SCC) {
// work around the limitations of the legacy pass manager.
AAResults AAR(createLegacyPMAAResults(*this, *F, BAR));
FunctionModRefBehavior MRB = AAR.getModRefBehavior(F);
if (MRB == FMRB_DoesNotAccessMemory)
// Already perfect!
continue;
// Definitions with weak linkage may be overridden at linktime with
// something that writes memory, so treat them like declarations.
if (F->isDeclaration() || F->mayBeOverridden()) {
if (!AliasAnalysis::onlyReadsMemory(MRB))
// May write memory. Just give up.
return false;
switch (checkFunctionMemoryAccess(*F, AAR, SCCNodes)) {
case MAK_MayWrite:
return false;
case MAK_ReadOnly:
ReadsMemory = true;
continue;
}
// Scan the function body for instructions that may read or write memory.
for (inst_iterator II = inst_begin(F), E = inst_end(F); II != E; ++II) {
Instruction *I = &*II;
// Some instructions can be ignored even if they read or write memory.
// Detect these now, skipping to the next instruction if one is found.
CallSite CS(cast<Value>(I));
if (CS) {
// Ignore calls to functions in the same SCC.
if (CS.getCalledFunction() && SCCNodes.count(CS.getCalledFunction()))
continue;
FunctionModRefBehavior MRB = AAR.getModRefBehavior(CS);
// If the call doesn't access arbitrary memory, we may be able to
// figure out something.
if (AliasAnalysis::onlyAccessesArgPointees(MRB)) {
// If the call does access argument pointees, check each argument.
if (AliasAnalysis::doesAccessArgPointees(MRB))
// Check whether all pointer arguments point to local memory, and
// ignore calls that only access local memory.
for (CallSite::arg_iterator CI = CS.arg_begin(), CE = CS.arg_end();
CI != CE; ++CI) {
Value *Arg = *CI;
if (Arg->getType()->isPointerTy()) {
AAMDNodes AAInfo;
I->getAAMetadata(AAInfo);
MemoryLocation Loc(Arg, MemoryLocation::UnknownSize, AAInfo);
if (!AAR.pointsToConstantMemory(Loc, /*OrLocal=*/true)) {
if (MRB & MRI_Mod)
// Writes non-local memory. Give up.
return false;
if (MRB & MRI_Ref)
// Ok, it reads non-local memory.
ReadsMemory = true;
}
}
}
continue;
}
// The call could access any memory. If that includes writes, give up.
if (MRB & MRI_Mod)
return false;
// If it reads, note it.
if (MRB & MRI_Ref)
ReadsMemory = true;
continue;
} else if (LoadInst *LI = dyn_cast<LoadInst>(I)) {
// Ignore non-volatile loads from local memory. (Atomic is okay here.)
if (!LI->isVolatile()) {
MemoryLocation Loc = MemoryLocation::get(LI);
if (AAR.pointsToConstantMemory(Loc, /*OrLocal=*/true))
continue;
}
} else if (StoreInst *SI = dyn_cast<StoreInst>(I)) {
// Ignore non-volatile stores to local memory. (Atomic is okay here.)
if (!SI->isVolatile()) {
MemoryLocation Loc = MemoryLocation::get(SI);
if (AAR.pointsToConstantMemory(Loc, /*OrLocal=*/true))
continue;
}
} else if (VAArgInst *VI = dyn_cast<VAArgInst>(I)) {
// Ignore vaargs on local memory.
MemoryLocation Loc = MemoryLocation::get(VI);
if (AAR.pointsToConstantMemory(Loc, /*OrLocal=*/true))
continue;
}
// Any remaining instructions need to be taken seriously! Check if they
// read or write memory.
if (I->mayWriteToMemory())
// Writes memory. Just give up.
return false;
// If this instruction may read memory, remember that.
ReadsMemory |= I->mayReadFromMemory();
break;
case MAK_ReadNone:
// Nothing to do!
break;
}
}