Skip to content

Commit 2ac6f3f

Browse files
author
Andres Madrid Ucha
committed
Partial Exectuer: Track progress per SCC to avoid redundant visits
Using a boolean (madeProgress) to track whether visiting a basic block's instructions yield new information (new edges, computing a phi value, or registering values). This boolean is pushed to a stack representing the progress of the parent's SCCS. After analyzing the last SCC of a parent, we check if any of its SCCs made progress. If so, recursion continues until the iteration limit is hit or the SCCs resolve. Otherwise, when no progress is made across all SCCS, we stop recursing, mark them as visited, and return to the parent to continue with its remaining children.
1 parent 37bccbb commit 2ac6f3f

File tree

1 file changed

+66
-25
lines changed

1 file changed

+66
-25
lines changed

llvm/lib/CheerpWriter/PartialExecuter.cpp

Lines changed: 66 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
#include <map>
5454
#include <unordered_map>
5555
#include <vector>
56+
#include <stack>
5657
#include "llvm/Cheerp/Utility.h"
5758
#include "llvm/Cheerp/BuiltinInstructions.h"
5859

@@ -301,7 +302,7 @@ class PartialInterpreter : public llvm::Interpreter {
301302
}
302303
return nullptr;
303304
}
304-
llvm::BasicBlock* visitBasicBlock(FunctionData& data, llvm::BasicBlock& BB);
305+
llvm::BasicBlock* visitBasicBlock(FunctionData& data, llvm::BasicBlock& BB, bool& madeProgress);
305306
explicit PartialInterpreter(std::unique_ptr<llvm::Module> M)
306307
: llvm::Interpreter(std::move(M), /*preExecute*/false)
307308
{
@@ -631,7 +632,7 @@ class PartialInterpreter : public llvm::Interpreter {
631632
{
632633
return (++functionCounters[F] > 0x1000);
633634
}
634-
void visitOuter(FunctionData& data, llvm::Instruction& I);
635+
void visitOuter(FunctionData& data, llvm::Instruction& I, bool& madeProgress);
635636
bool replaceKnownCEs()
636637
{
637638
if(fullyKnownCEs.empty())
@@ -786,7 +787,7 @@ static void removeEdgeBetweenBlocks(llvm::BasicBlock* from, llvm::BasicBlock* to
786787
}
787788
}
788789

789-
llvm::BasicBlock* PartialInterpreter::visitBasicBlock(FunctionData& data, llvm::BasicBlock& BB)
790+
llvm::BasicBlock* PartialInterpreter::visitBasicBlock(FunctionData& data, llvm::BasicBlock& BB, bool& madeProgress)
790791
{
791792
ExecutionContext& executionContext = getTopCallFrame();
792793
executionContext.CurBB = &BB;
@@ -797,7 +798,7 @@ llvm::BasicBlock* PartialInterpreter::visitBasicBlock(FunctionData& data, llvm::
797798
// Note that here we could also execute a Call, and that implies adding a CallFrame
798799
// executing there (possibly also in depth)
799800
// So getTopCallFrame() has to be called since it will possibly change
800-
visitOuter(data, *getTopCallFrame().CurInst++);
801+
visitOuter(data, *getTopCallFrame().CurInst++, madeProgress);
801802
}
802803

803804
// Find (if there are enough information) the next BB to be visited
@@ -885,6 +886,7 @@ class FunctionData
885886
};
886887
llvm::Function& F;
887888
std::vector<std::pair<llvm::BasicBlock*, llvm::BasicBlock*>> existingEdges;
889+
888890
typedef std::vector<Value*> VectorOfArgs;
889891

890892
llvm::DenseMap<const llvm::BasicBlock*, int> visitCounter;
@@ -990,9 +992,10 @@ class FunctionData
990992
assert(currentEE);
991993
return *currentEE;
992994
}
993-
void registerEdge(const llvm::BasicBlock* from, const llvm::BasicBlock* to)
995+
bool registerEdge(const llvm::BasicBlock* from, const llvm::BasicBlock* to)
994996
{
995-
visitedEdges.insert({from, to});
997+
auto [it, is_inserted] = visitedEdges.insert({from, to});
998+
return is_inserted;
996999
}
9971000
bool hasNoInfo(const VectorOfArgs& arguments) const
9981001
{
@@ -1178,29 +1181,36 @@ class FunctionData
11781181
{
11791182
knownValues[&I].everSkipped = true;
11801183
}
1181-
void registerValueForInst(llvm::Instruction& I, const GenericValue& GV, PartialInterpreter::BitMask strongBits)
1184+
bool registerValueForInst(llvm::Instruction& I, const GenericValue& GV, PartialInterpreter::BitMask strongBits)
11821185
{
11831186
// Only support integers for now, to simplify the handling of GVs
11841187
if(!I.getType()->isIntegerTy())
1185-
return;
1188+
return false;
11861189
// Do not attempt tracking of partially known values
11871190
if(strongBits != PartialInterpreter::BitMask::ALL)
1188-
return markInstSkipped(I);
1191+
{
1192+
markInstSkipped(I);
1193+
return false;
1194+
}
11891195
auto it = knownValues.find(&I);
11901196
if(it == knownValues.end())
11911197
{
11921198
// Never registered any value before, save the current one
11931199
knownValues[&I].value = GV;
1200+
return true;
11941201
}
11951202
else if(it->second.everSkipped)
11961203
{
11971204
// Nothing we can do anymore
1205+
return false;
11981206
}
11991207
else if(it->second.value.IntVal != GV.IntVal)
12001208
{
12011209
// Different value observed, bailout
12021210
it->second.everSkipped = true;
1211+
return false;
12031212
}
1213+
return false;
12041214
}
12051215
void replaceKnownValues() const
12061216
{
@@ -1363,6 +1373,7 @@ class BasicBlockGroupNode
13631373
// Implicit tree structure
13641374
BasicBlockGroupNode* parentNode;
13651375
std::list<BasicBlockGroupNode> childrenNodes;
1376+
std::stack<bool> SCCProgress;
13661377

13671378
// Other metadata
13681379
FunctionData& data;
@@ -1439,14 +1450,13 @@ class BasicBlockGroupNode
14391450
// Do the visit of the BB, with 'from' (possibly nullptr if unknown) as predecessor
14401451
// Loop backs will be directed to another BBgroup
14411452
// The visit will return the set of reachable BBs, to be added into visitNext
1442-
void runVisitBasicBlock(FunctionData& data, llvm::BasicBlock& BB, llvm::SmallVectorImpl<llvm::BasicBlock*>& visitNext)
1453+
void runVisitBasicBlock(FunctionData& data, llvm::BasicBlock& BB, llvm::SmallVectorImpl<llvm::BasicBlock*>& visitNext, bool& madeProgress)
14431454
{
14441455
assert(visitNext.empty());
14451456

14461457
PartialInterpreter& interpreter = data.getInterpreter();
14471458
interpreter.incomingBB = from;
1448-
BasicBlock* ret = interpreter.visitBasicBlock(data, BB);
1449-
1459+
BasicBlock* ret = interpreter.visitBasicBlock(data, BB, madeProgress);
14501460
if (ret)
14511461
{
14521462
visitNext.push_back(ret);
@@ -1506,10 +1516,11 @@ class BasicBlockGroupNode
15061516
registerEdge(bb, succ);
15071517
}
15081518
}
1509-
void registerEdge(llvm::BasicBlock* from, llvm::BasicBlock* to)
1519+
bool registerEdge(llvm::BasicBlock* from, llvm::BasicBlock* to)
15101520
{
1511-
data.registerEdge(from, to);
1521+
bool is_inserted = data.registerEdge(from, to);
15121522
notifySuccessor(from, currIter, to);
1523+
return is_inserted;
15131524
}
15141525
void cleanUp(llvm::BasicBlock* block)
15151526
{
@@ -1555,12 +1566,40 @@ class BasicBlockGroupNode
15551566
splitIntoSCCs(childrenNodes, reverseMappingBBToGroup); //These should be partially ordered with the last one possibly being the replica of the current one
15561567

15571568
llvm::SmallVector<llvm::BasicBlock*, 4> visitNext;
1569+
1570+
bool madeProgress = false;
1571+
runVisitBasicBlock(data, *start, visitNext, madeProgress);
1572+
for (llvm::BasicBlock* succ : visitNext){
1573+
if (registerEdge(start, succ))
1574+
madeProgress = true;
1575+
}
15581576

1559-
// Do the actual visit for start, while populating visitNext
1560-
runVisitBasicBlock(data, *start, visitNext);
1561-
for (llvm::BasicBlock* succ : visitNext)
1562-
registerEdge(start, succ);
1563-
1577+
if (parentNode)
1578+
{
1579+
// Track progress for this BB into the parent's stack
1580+
parentNode->SCCProgress.push(madeProgress);
1581+
if (parentNode->childrenNodes.size() == 1)
1582+
{
1583+
// If we are here, it means that we have analyzed all the SCCs of a parent
1584+
// and we can now check if for the current SCCs, there was any progress made
1585+
bool foundProgressInStack = false;
1586+
while (!parentNode->SCCProgress.empty())
1587+
{
1588+
if (parentNode->SCCProgress.top())
1589+
foundProgressInStack = true;
1590+
parentNode->SCCProgress.pop();
1591+
}
1592+
// If we there was progress, we allow recursion to continue on this group of SCCs
1593+
// until they resolve themselves or they hit the top iteration limit.
1594+
// Otherwise, when progress has stale, we give up on this group of SCC and allow their parent
1595+
// to continue with the rest of its children
1596+
if (!foundProgressInStack)
1597+
{
1598+
visitAll();
1599+
return;
1600+
}
1601+
}
1602+
}
15641603
// First has been already done
15651604
childrenNodes.pop_back();
15661605
while (!childrenNodes.empty())
@@ -1633,7 +1672,7 @@ void FunctionData::actualVisit()
16331672
groupData.recursiveVisit();
16341673
}
16351674

1636-
void PartialInterpreter::visitOuter(FunctionData& data, llvm::Instruction& I)
1675+
void PartialInterpreter::visitOuter(FunctionData& data, llvm::Instruction& I, bool& madeProgress)
16371676
{
16381677
if (PHINode* phi = dyn_cast<PHINode>(&I))
16391678
{
@@ -1647,6 +1686,7 @@ void PartialInterpreter::visitOuter(FunctionData& data, llvm::Instruction& I)
16471686
if (isValueComputed(incomingVal))
16481687
{
16491688
computedPhisValues.push_back({phi, incomingVal});
1689+
madeProgress = true;
16501690
return;
16511691
}
16521692
}
@@ -1715,18 +1755,17 @@ void PartialInterpreter::visitOuter(FunctionData& data, llvm::Instruction& I)
17151755
if (next && data.getVisitCounter(next) < MAX_NUMBER_OF_VISITS_PER_BB)
17161756
{
17171757
data.incrementVisitCounter(next);
1718-
17191758
// We know where execution should proceed
17201759
incomingBB = I.getParent();
17211760
getTopCallFrame().CurBB = next;
17221761
getTopCallFrame().CurInst = getTopCallFrame().CurBB->begin();
17231762

17241763
// Also here we have set the proper state for the execution so we can return
1764+
madeProgress = true;
17251765
return;
17261766
}
17271767
skip = true;
17281768
}
1729-
17301769
if (addToCounter(I.getFunction()))
17311770
skip = true;
17321771

@@ -1736,13 +1775,12 @@ void PartialInterpreter::visitOuter(FunctionData& data, llvm::Instruction& I)
17361775
//Pop current stack (will happen recursively over the call-stack)
17371776
popStackFrame();
17381777
removeFromMaps(getTopCallFrame().Caller);
1739-
17401778
return;
17411779
}
17421780
}
17431781
//If we are here it means we have to actually perform the execution via Interpreter
17441782

1745-
//Iff it's a call, set up the next stack frame
1783+
//If it's a call, set up the next stack frame
17461784
if (CallInst* CI = dyn_cast<CallInst>(&I))
17471785
forwardArgumentsToNextFrame(*CI);
17481786

@@ -1752,7 +1790,10 @@ void PartialInterpreter::visitOuter(FunctionData& data, llvm::Instruction& I)
17521790
BitMask strongBits = computeStronglyKnownBits(I.getOpcode(), I);
17531791

17541792
if(isInitialCallFrame())
1755-
data.registerValueForInst(I, getOperandValue(&I), strongBits);
1793+
{
1794+
if (data.registerValueForInst(I, getOperandValue(&I), strongBits))
1795+
madeProgress = true;
1796+
}
17561797

17571798
if (!isa<CallInst>(I))
17581799
{

0 commit comments

Comments
 (0)