mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2024-12-28 04:33:05 +00:00
Change CaptureTracking to pass a Use* instead of a Value* when a value is
captured. This allows the tracker to look at the specific use, which may be especially interesting for function calls. Use this to fix 'nocapture' deduction in FunctionAttrs. The existing one does not iterate until a fixpoint and does not guarantee that it produces the same result regardless of iteration order. The new implementation builds up a graph of how arguments are passed from function to function, and uses a bottom-up walk on the argument-SCCs to assign nocapture. This gets us nocapture more often, and does so rather efficiently and independent of iteration order. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@147327 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
da813f4209
commit
b48a18903a
@ -50,10 +50,10 @@ namespace llvm {
|
||||
/// U->getUser() is always an Instruction.
|
||||
virtual bool shouldExplore(Use *U) = 0;
|
||||
|
||||
/// captured - The instruction I captured the pointer. Return true to
|
||||
/// stop the traversal or false to continue looking for more capturing
|
||||
/// instructions.
|
||||
virtual bool captured(Instruction *I) = 0;
|
||||
/// captured - Information about the pointer was captured by the user of
|
||||
/// use U. Return true to stop the traversal or false to continue looking
|
||||
/// for more capturing instructions.
|
||||
virtual bool captured(Use *U) = 0;
|
||||
};
|
||||
|
||||
/// PointerMayBeCaptured - Visit the value and the values derived from it and
|
||||
|
@ -30,8 +30,8 @@ namespace {
|
||||
|
||||
bool shouldExplore(Use *U) { return true; }
|
||||
|
||||
bool captured(Instruction *I) {
|
||||
if (isa<ReturnInst>(I) && !ReturnCaptures)
|
||||
bool captured(Use *U) {
|
||||
if (isa<ReturnInst>(U->getUser()) && !ReturnCaptures)
|
||||
return false;
|
||||
|
||||
Captured = true;
|
||||
@ -117,7 +117,7 @@ void llvm::PointerMayBeCaptured(const Value *V, CaptureTracker *Tracker) {
|
||||
for (CallSite::arg_iterator A = B; A != E; ++A)
|
||||
if (A->get() == V && !CS.doesNotCapture(A - B))
|
||||
// The parameter is not marked 'nocapture' - captured.
|
||||
if (Tracker->captured(I))
|
||||
if (Tracker->captured(U))
|
||||
return;
|
||||
break;
|
||||
}
|
||||
@ -130,7 +130,7 @@ void llvm::PointerMayBeCaptured(const Value *V, CaptureTracker *Tracker) {
|
||||
case Instruction::Store:
|
||||
if (V == I->getOperand(0))
|
||||
// Stored the pointer - conservatively assume it may be captured.
|
||||
if (Tracker->captured(I))
|
||||
if (Tracker->captured(U))
|
||||
return;
|
||||
// Storing to the pointee does not cause the pointer to be captured.
|
||||
break;
|
||||
@ -158,12 +158,12 @@ void llvm::PointerMayBeCaptured(const Value *V, CaptureTracker *Tracker) {
|
||||
break;
|
||||
// Otherwise, be conservative. There are crazy ways to capture pointers
|
||||
// using comparisons.
|
||||
if (Tracker->captured(I))
|
||||
if (Tracker->captured(U))
|
||||
return;
|
||||
break;
|
||||
default:
|
||||
// Something else - be conservative and say it is captured.
|
||||
if (Tracker->captured(I))
|
||||
if (Tracker->captured(U))
|
||||
return;
|
||||
break;
|
||||
}
|
||||
|
@ -349,7 +349,8 @@ namespace {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool captured(Instruction *I) {
|
||||
bool captured(Use *U) {
|
||||
Instruction *I = cast<Instruction>(U->getUser());
|
||||
if (BeforeHere != I && DT->dominates(BeforeHere, I))
|
||||
return false;
|
||||
Captured = true;
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include "llvm/Analysis/AliasAnalysis.h"
|
||||
#include "llvm/Analysis/CallGraph.h"
|
||||
#include "llvm/Analysis/CaptureTracking.h"
|
||||
#include "llvm/ADT/SCCIterator.h"
|
||||
#include "llvm/ADT/SmallSet.h"
|
||||
#include "llvm/ADT/Statistic.h"
|
||||
#include "llvm/ADT/UniqueVector.h"
|
||||
@ -225,31 +226,247 @@ bool FunctionAttrs::AddReadAttrs(const CallGraphSCC &SCC) {
|
||||
return MadeChange;
|
||||
}
|
||||
|
||||
namespace {
|
||||
// For a given pointer Argument, this retains a list of Arguments of functions
|
||||
// in the same SCC that the pointer data flows into. We use this to build an
|
||||
// SCC of the arguments.
|
||||
struct ArgumentGraphNode {
|
||||
Argument *Definition;
|
||||
SmallVector<ArgumentGraphNode*, 4> Uses;
|
||||
};
|
||||
|
||||
class ArgumentGraph {
|
||||
// We store pointers to ArgumentGraphNode objects, so it's important that
|
||||
// that they not move around upon insert.
|
||||
typedef std::map<Argument*, ArgumentGraphNode> ArgumentMapTy;
|
||||
|
||||
ArgumentMapTy ArgumentMap;
|
||||
|
||||
// There is no root node for the argument graph, in fact:
|
||||
// void f(int *x, int *y) { if (...) f(x, y); }
|
||||
// is an example where the graph is disconnected. The SCCIterator requires a
|
||||
// single entry point, so we maintain a fake ("synthetic") root node that
|
||||
// uses every node. Because the graph is directed and nothing points into
|
||||
// the root, it will not participate in any SCCs (except for its own).
|
||||
ArgumentGraphNode SyntheticRoot;
|
||||
|
||||
public:
|
||||
ArgumentGraph() { SyntheticRoot.Definition = 0; }
|
||||
|
||||
typedef SmallVectorImpl<ArgumentGraphNode*>::iterator iterator;
|
||||
|
||||
iterator begin() { return SyntheticRoot.Uses.begin(); }
|
||||
iterator end() { return SyntheticRoot.Uses.end(); }
|
||||
ArgumentGraphNode *getEntryNode() { return &SyntheticRoot; }
|
||||
|
||||
ArgumentGraphNode *operator[](Argument *A) {
|
||||
ArgumentGraphNode &Node = ArgumentMap[A];
|
||||
Node.Definition = A;
|
||||
SyntheticRoot.Uses.push_back(&Node);
|
||||
return &Node;
|
||||
}
|
||||
};
|
||||
|
||||
// This tracker checks whether callees are in the SCC, and if so it does not
|
||||
// consider that a capture, instead adding it to the "Uses" list and
|
||||
// continuing with the analysis.
|
||||
struct ArgumentUsesTracker : public CaptureTracker {
|
||||
ArgumentUsesTracker(const SmallPtrSet<Function*, 8> &SCCNodes)
|
||||
: Captured(false), SCCNodes(SCCNodes) {}
|
||||
|
||||
void tooManyUses() { Captured = true; }
|
||||
|
||||
bool shouldExplore(Use *U) { return true; }
|
||||
|
||||
bool captured(Use *U) {
|
||||
CallSite CS(U->getUser());
|
||||
if (!CS.getInstruction()) { Captured = true; return true; }
|
||||
|
||||
Function *F = CS.getCalledFunction();
|
||||
if (!F || !SCCNodes.count(F)) { Captured = true; return true; }
|
||||
|
||||
Function::arg_iterator AI = F->arg_begin(), AE = F->arg_end();
|
||||
for (CallSite::arg_iterator PI = CS.arg_begin(), PE = CS.arg_end();
|
||||
PI != PE; ++PI, ++AI) {
|
||||
if (AI == AE) {
|
||||
assert(F->isVarArg() && "More params than args in non-varargs call");
|
||||
Captured = true;
|
||||
return true;
|
||||
}
|
||||
if (PI == U) {
|
||||
Uses.push_back(AI);
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert(!Uses.empty() && "Capturing call-site captured nothing?");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Captured; // True only if certainly captured (used outside our SCC).
|
||||
SmallVector<Argument*, 4> Uses; // Uses within our SCC.
|
||||
|
||||
const SmallPtrSet<Function*, 8> &SCCNodes;
|
||||
};
|
||||
}
|
||||
|
||||
namespace llvm {
|
||||
template<> struct GraphTraits<ArgumentGraphNode*> {
|
||||
typedef ArgumentGraphNode NodeType;
|
||||
typedef SmallVectorImpl<ArgumentGraphNode*>::iterator ChildIteratorType;
|
||||
|
||||
static inline NodeType *getEntryNode(NodeType *A) { return A; }
|
||||
static inline ChildIteratorType child_begin(NodeType *N) {
|
||||
return N->Uses.begin();
|
||||
}
|
||||
static inline ChildIteratorType child_end(NodeType *N) {
|
||||
return N->Uses.end();
|
||||
}
|
||||
};
|
||||
template<> struct GraphTraits<ArgumentGraph*>
|
||||
: public GraphTraits<ArgumentGraphNode*> {
|
||||
static NodeType *getEntryNode(ArgumentGraph *AG) {
|
||||
return AG->getEntryNode();
|
||||
}
|
||||
static ChildIteratorType nodes_begin(ArgumentGraph *AG) {
|
||||
return AG->begin();
|
||||
}
|
||||
static ChildIteratorType nodes_end(ArgumentGraph *AG) {
|
||||
return AG->end();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// AddNoCaptureAttrs - Deduce nocapture attributes for the SCC.
|
||||
bool FunctionAttrs::AddNoCaptureAttrs(const CallGraphSCC &SCC) {
|
||||
bool Changed = false;
|
||||
|
||||
SmallPtrSet<Function*, 8> SCCNodes;
|
||||
|
||||
// Fill SCCNodes with the elements of the SCC. Used for quickly
|
||||
// looking up whether a given CallGraphNode is in this SCC.
|
||||
for (CallGraphSCC::iterator I = SCC.begin(), E = SCC.end(); I != E; ++I) {
|
||||
Function *F = (*I)->getFunction();
|
||||
if (F && !F->isDeclaration() && !F->mayBeOverridden())
|
||||
SCCNodes.insert(F);
|
||||
}
|
||||
|
||||
ArgumentGraph AG;
|
||||
|
||||
// Check each function in turn, determining which pointer arguments are not
|
||||
// captured.
|
||||
for (CallGraphSCC::iterator I = SCC.begin(), E = SCC.end(); I != E; ++I) {
|
||||
Function *F = (*I)->getFunction();
|
||||
|
||||
if (F == 0)
|
||||
// External node - skip it;
|
||||
// External node - only a problem for arguments that we pass to it.
|
||||
continue;
|
||||
|
||||
// Definitions with weak linkage may be overridden at linktime with
|
||||
// something that writes memory, so treat them like declarations.
|
||||
// something that captures pointers, so treat them like declarations.
|
||||
if (F->isDeclaration() || F->mayBeOverridden())
|
||||
continue;
|
||||
|
||||
// Functions that are readonly (or readnone) and nounwind and don't return
|
||||
// a value can't capture arguments. Don't analyze them.
|
||||
if (F->onlyReadsMemory() && F->doesNotThrow() &&
|
||||
F->getReturnType()->isVoidTy()) {
|
||||
for (Function::arg_iterator A = F->arg_begin(), E = F->arg_end();
|
||||
A != E; ++A) {
|
||||
if (A->getType()->isPointerTy() && !A->hasNoCaptureAttr()) {
|
||||
A->addAttr(Attribute::NoCapture);
|
||||
++NumNoCapture;
|
||||
Changed = true;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
for (Function::arg_iterator A = F->arg_begin(), E = F->arg_end(); A!=E; ++A)
|
||||
if (A->getType()->isPointerTy() && !A->hasNoCaptureAttr() &&
|
||||
!PointerMayBeCaptured(A, true, /*StoreCaptures=*/false)) {
|
||||
A->addAttr(Attribute::NoCapture);
|
||||
if (A->getType()->isPointerTy() && !A->hasNoCaptureAttr()) {
|
||||
ArgumentUsesTracker Tracker(SCCNodes);
|
||||
PointerMayBeCaptured(A, &Tracker);
|
||||
if (!Tracker.Captured) {
|
||||
if (Tracker.Uses.empty()) {
|
||||
// If it's trivially not captured, mark it nocapture now.
|
||||
A->addAttr(Attribute::NoCapture);
|
||||
++NumNoCapture;
|
||||
Changed = true;
|
||||
} else {
|
||||
// If it's not trivially captured and not trivially not captured,
|
||||
// then it must be calling into another function in our SCC. Save
|
||||
// its particulars for Argument-SCC analysis later.
|
||||
ArgumentGraphNode *Node = AG[A];
|
||||
for (SmallVectorImpl<Argument*>::iterator UI = Tracker.Uses.begin(),
|
||||
UE = Tracker.Uses.end(); UI != UE; ++UI)
|
||||
Node->Uses.push_back(AG[*UI]);
|
||||
}
|
||||
}
|
||||
// Otherwise, it's captured. Don't bother doing SCC analysis on it.
|
||||
}
|
||||
}
|
||||
|
||||
// The graph we've collected is partial because we stopped scanning for
|
||||
// argument uses once we solved the argument trivially. These partial nodes
|
||||
// show up as ArgumentGraphNode objects with an empty Uses list, and for
|
||||
// these nodes the final decision about whether they capture has already been
|
||||
// made. If the definition doesn't have a 'nocapture' attribute by now, it
|
||||
// captures.
|
||||
|
||||
for (scc_iterator<ArgumentGraph*> I = scc_begin(&AG), E = scc_end(&AG);
|
||||
I != E; ++I) {
|
||||
std::vector<ArgumentGraphNode*> &ArgumentSCC = *I;
|
||||
if (ArgumentSCC.size() == 1) {
|
||||
if (!ArgumentSCC[0]->Definition) continue; // synthetic root node
|
||||
|
||||
// eg. "void f(int* x) { if (...) f(x); }"
|
||||
if (ArgumentSCC[0]->Uses.size() == 1 &&
|
||||
ArgumentSCC[0]->Uses[0] == ArgumentSCC[0]) {
|
||||
ArgumentSCC[0]->Definition->addAttr(Attribute::NoCapture);
|
||||
++NumNoCapture;
|
||||
Changed = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
bool SCCCaptured = false;
|
||||
for (std::vector<ArgumentGraphNode*>::iterator I = ArgumentSCC.begin(),
|
||||
E = ArgumentSCC.end(); I != E && !SCCCaptured; ++I) {
|
||||
ArgumentGraphNode *Node = *I;
|
||||
if (Node->Uses.empty()) {
|
||||
if (!Node->Definition->hasNoCaptureAttr())
|
||||
SCCCaptured = true;
|
||||
}
|
||||
}
|
||||
if (SCCCaptured) continue;
|
||||
|
||||
SmallPtrSet<Argument*, 8> ArgumentSCCNodes;
|
||||
// Fill ArgumentSCCNodes with the elements of the ArgumentSCC. Used for
|
||||
// quickly looking up whether a given Argument is in this ArgumentSCC.
|
||||
for (std::vector<ArgumentGraphNode*>::iterator I = ArgumentSCC.begin(),
|
||||
E = ArgumentSCC.end(); I != E; ++I) {
|
||||
ArgumentSCCNodes.insert((*I)->Definition);
|
||||
}
|
||||
|
||||
for (std::vector<ArgumentGraphNode*>::iterator I = ArgumentSCC.begin(),
|
||||
E = ArgumentSCC.end(); I != E && !SCCCaptured; ++I) {
|
||||
ArgumentGraphNode *N = *I;
|
||||
for (SmallVectorImpl<ArgumentGraphNode*>::iterator UI = N->Uses.begin(),
|
||||
UE = N->Uses.end(); UI != UE; ++UI) {
|
||||
Argument *A = (*UI)->Definition;
|
||||
if (A->hasNoCaptureAttr() || ArgumentSCCNodes.count(A))
|
||||
continue;
|
||||
SCCCaptured = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (SCCCaptured) continue;
|
||||
|
||||
for (unsigned i = 0, e = ArgumentSCC.size(); i != e && !SCCCaptured; ++i) {
|
||||
Argument *A = ArgumentSCC[i]->Definition;
|
||||
A->addAttr(Attribute::NoCapture);
|
||||
++NumNoCapture;
|
||||
Changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
return Changed;
|
||||
|
@ -24,7 +24,7 @@ define void @test0_no(i32* %p) nounwind {
|
||||
; Add the readonly attribute, since there's just a call to a function which
|
||||
; TBAA says doesn't modify any memory.
|
||||
|
||||
; CHECK: define void @test1_yes(i32* %p) nounwind readonly {
|
||||
; CHECK: define void @test1_yes(i32* nocapture %p) nounwind readonly {
|
||||
define void @test1_yes(i32* %p) nounwind {
|
||||
call void @callee(i32* %p), !tbaa !1
|
||||
ret void
|
||||
|
@ -115,3 +115,64 @@ define void @nc5(void (i8*)* %f, i8* %p) {
|
||||
call void %f(i8* nocapture %p)
|
||||
ret void
|
||||
}
|
||||
|
||||
; CHECK: define void @test1_1(i8* nocapture %x1_1, i8* %y1_1)
|
||||
define void @test1_1(i8* %x1_1, i8* %y1_1) {
|
||||
call i8* @test1_2(i8* %x1_1, i8* %y1_1)
|
||||
store i32* null, i32** @g
|
||||
ret void
|
||||
}
|
||||
|
||||
; CHECK: define i8* @test1_2(i8* nocapture %x1_2, i8* %y1_2)
|
||||
define i8* @test1_2(i8* %x1_2, i8* %y1_2) {
|
||||
call void @test1_1(i8* %x1_2, i8* %y1_2)
|
||||
store i32* null, i32** @g
|
||||
ret i8* %y1_2
|
||||
}
|
||||
|
||||
; CHECK: define void @test2(i8* nocapture %x2)
|
||||
define void @test2(i8* %x2) {
|
||||
call void @test2(i8* %x2)
|
||||
store i32* null, i32** @g
|
||||
ret void
|
||||
}
|
||||
|
||||
; CHECK: define void @test3(i8* nocapture %x3, i8* nocapture %y3, i8* nocapture %z3)
|
||||
define void @test3(i8* %x3, i8* %y3, i8* %z3) {
|
||||
call void @test3(i8* %z3, i8* %y3, i8* %x3)
|
||||
store i32* null, i32** @g
|
||||
ret void
|
||||
}
|
||||
|
||||
; CHECK: define void @test4_1(i8* %x4_1)
|
||||
define void @test4_1(i8* %x4_1) {
|
||||
call i8* @test4_2(i8* %x4_1, i8* %x4_1, i8* %x4_1)
|
||||
store i32* null, i32** @g
|
||||
ret void
|
||||
}
|
||||
|
||||
; CHECK: define i8* @test4_2(i8* nocapture %x4_2, i8* %y4_2, i8* nocapture %z4_2)
|
||||
define i8* @test4_2(i8* %x4_2, i8* %y4_2, i8* %z4_2) {
|
||||
call void @test4_1(i8* null)
|
||||
store i32* null, i32** @g
|
||||
ret i8* %y4_2
|
||||
}
|
||||
|
||||
declare i8* @test5_1(i8* %x5_1)
|
||||
|
||||
; CHECK: define void @test5_2(i8* %x5_2)
|
||||
define void @test5_2(i8* %x5_2) {
|
||||
call i8* @test5_1(i8* %x5_2)
|
||||
store i32* null, i32** @g
|
||||
ret void
|
||||
}
|
||||
|
||||
declare void @test6_1(i8* %x6_1, i8* nocapture %y6_1, ...)
|
||||
|
||||
; CHECK: define void @test6_2(i8* %x6_2, i8* nocapture %y6_2, i8* %z6_2)
|
||||
define void @test6_2(i8* %x6_2, i8* %y6_2, i8* %z6_2) {
|
||||
call void (i8*, i8*, ...)* @test6_1(i8* %x6_2, i8* %y6_2, i8* %z6_2)
|
||||
store i32* null, i32** @g
|
||||
ret void
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user