Skip to content

Commit 580f3a4

Browse files
amanasifkhalidsirntar
authored andcommitted
JIT: Visit blocks in RPO during LSRA (dotnet#107927)
1 parent 127a20f commit 580f3a4

File tree

2 files changed

+40
-308
lines changed

2 files changed

+40
-308
lines changed

src/coreclr/jit/lsra.cpp

Lines changed: 38 additions & 272 deletions
Original file line numberDiff line numberDiff line change
@@ -913,11 +913,10 @@ LinearScan::LinearScan(Compiler* theCompiler)
913913
// Note that we don't initialize the bbVisitedSet until we do the first traversal
914914
// This is so that any blocks that are added during the first traversal
915915
// are accounted for (and we don't have BasicBlockEpoch issues).
916-
blockSequencingDone = false;
917-
blockSequence = nullptr;
918-
blockSequenceWorkList = nullptr;
919-
curBBSeqNum = 0;
920-
bbSeqCount = 0;
916+
blockSequencingDone = false;
917+
blockSequence = nullptr;
918+
curBBSeqNum = 0;
919+
bbSeqCount = 0;
921920

922921
// Information about each block, including predecessor blocks used for variable locations at block entry.
923922
blockInfo = nullptr;
@@ -926,39 +925,6 @@ LinearScan::LinearScan(Compiler* theCompiler)
926925
tgtPrefUse = nullptr;
927926
}
928927

929-
//------------------------------------------------------------------------
930-
// getNextCandidateFromWorkList: Get the next candidate for block sequencing
931-
//
932-
// Arguments:
933-
// None.
934-
//
935-
// Return Value:
936-
// The next block to be placed in the sequence.
937-
//
938-
// Notes:
939-
// This method currently always returns the next block in the list, and relies on having
940-
// blocks added to the list only when they are "ready", and on the
941-
// addToBlockSequenceWorkList() method to insert them in the proper order.
942-
// However, a block may be in the list and already selected, if it was subsequently
943-
// encountered as both a flow and layout successor of the most recently selected
944-
// block.
945-
//
946-
BasicBlock* LinearScan::getNextCandidateFromWorkList()
947-
{
948-
BasicBlockList* nextWorkList = nullptr;
949-
for (BasicBlockList* workList = blockSequenceWorkList; workList != nullptr; workList = nextWorkList)
950-
{
951-
nextWorkList = workList->next;
952-
BasicBlock* candBlock = workList->block;
953-
removeFromBlockSequenceWorkList(workList, nullptr);
954-
if (!isBlockVisited(candBlock))
955-
{
956-
return candBlock;
957-
}
958-
}
959-
return nullptr;
960-
}
961-
962928
//------------------------------------------------------------------------
963929
// setBlockSequence: Determine the block order for register allocation.
964930
//
@@ -969,8 +935,7 @@ BasicBlock* LinearScan::getNextCandidateFromWorkList()
969935
// None
970936
//
971937
// Notes:
972-
// On return, the blockSequence array contains the blocks, in the order in which they
973-
// will be allocated.
938+
// On return, the blockSequence array contains the blocks in reverse post-order.
974939
// This method clears the bbVisitedSet on LinearScan, and when it returns the set
975940
// contains all the bbNums for the block.
976941
//
@@ -986,19 +951,20 @@ void LinearScan::setBlockSequence()
986951
// Initialize the "visited" blocks set.
987952
bbVisitedSet = BlockSetOps::MakeEmpty(compiler);
988953

989-
BlockSet readySet(BlockSetOps::MakeEmpty(compiler));
990-
BlockSet predSet(BlockSetOps::MakeEmpty(compiler));
991-
992-
assert(blockSequence == nullptr && bbSeqCount == 0);
993-
blockSequence = new (compiler, CMK_LSRA) BasicBlock*[compiler->fgBBcount];
994-
bbNumMaxBeforeResolution = compiler->fgBBNumMax;
995-
blockInfo = new (compiler, CMK_LSRA) LsraBlockInfo[bbNumMaxBeforeResolution + 1];
954+
assert((blockSequence == nullptr) && (bbSeqCount == 0));
955+
FlowGraphDfsTree* const dfsTree = compiler->fgComputeDfs</* useProfile */ true>();
956+
blockSequence = dfsTree->GetPostOrder();
957+
bbNumMaxBeforeResolution = compiler->fgBBNumMax;
958+
blockInfo = new (compiler, CMK_LSRA) LsraBlockInfo[bbNumMaxBeforeResolution + 1];
996959

997-
assert(blockSequenceWorkList == nullptr);
960+
// Flip the DFS traversal to get the reverse post-order traversal
961+
// (this is the order in which blocks will be allocated)
962+
for (unsigned left = 0, right = dfsTree->GetPostOrderCount() - 1; left < right; left++, right--)
963+
{
964+
std::swap(blockSequence[left], blockSequence[right]);
965+
}
998966

999-
verifiedAllBBs = false;
1000967
hasCriticalEdges = false;
1001-
BasicBlock* nextBlock;
1002968
// We use a bbNum of 0 for entry RefPositions.
1003969
// The other information in blockInfo[0] will never be used.
1004970
blockInfo[0].weight = BB_UNITY_WEIGHT;
@@ -1009,14 +975,9 @@ void LinearScan::setBlockSequence()
1009975
}
1010976
#endif // TRACK_LSRA_STATS
1011977

1012-
JITDUMP("Start LSRA Block Sequence: \n");
1013-
for (BasicBlock* block = compiler->fgFirstBB; block != nullptr; block = nextBlock)
1014-
{
978+
auto visitBlock = [this](BasicBlock* block) {
1015979
JITDUMP("Current block: " FMT_BB "\n", block->bbNum);
1016-
blockSequence[bbSeqCount] = block;
1017980
markBlockVisited(block);
1018-
bbSeqCount++;
1019-
nextBlock = nullptr;
1020981

1021982
// Initialize the blockInfo.
1022983
// predBBNum will be set later.
@@ -1087,73 +1048,41 @@ void LinearScan::setBlockSequence()
10871048
assert(!"Switch with single successor");
10881049
}
10891050

1090-
for (unsigned succIndex = 0; succIndex < numSuccs; succIndex++)
1051+
for (BasicBlock* const succ : block->Succs(compiler))
10911052
{
1092-
BasicBlock* succ = block->GetSucc(succIndex, compiler);
1093-
if (checkForCriticalOutEdge && succ->GetUniquePred(compiler) == nullptr)
1053+
if (checkForCriticalOutEdge && (succ->GetUniquePred(compiler) == nullptr))
10941054
{
10951055
blockInfo[block->bbNum].hasCriticalOutEdge = true;
10961056
hasCriticalEdges = true;
10971057
// We can stop checking now.
1098-
checkForCriticalOutEdge = false;
1099-
}
1100-
1101-
if (isTraversalLayoutOrder() || isBlockVisited(succ))
1102-
{
1103-
continue;
1104-
}
1105-
1106-
// We've now seen a predecessor, so add it to the work list and the "readySet".
1107-
// It will be inserted in the worklist according to the specified traversal order
1108-
// (i.e. pred-first or random, since layout order is handled above).
1109-
if (!BlockSetOps::IsMember(compiler, readySet, succ->bbNum))
1110-
{
1111-
JITDUMP("\tSucc block: " FMT_BB, succ->bbNum);
1112-
addToBlockSequenceWorkList(readySet, succ, predSet);
1113-
BlockSetOps::AddElemD(compiler, readySet, succ->bbNum);
1058+
break;
11141059
}
11151060
}
1061+
};
11161062

1117-
// For layout order, simply use bbNext
1118-
if (isTraversalLayoutOrder())
1119-
{
1120-
nextBlock = block->Next();
1121-
continue;
1122-
}
1063+
JITDUMP("Start LSRA Block Sequence: \n");
1064+
for (unsigned i = 0; i < dfsTree->GetPostOrderCount(); i++)
1065+
{
1066+
visitBlock(blockSequence[i]);
1067+
}
11231068

1124-
while (nextBlock == nullptr)
1069+
// If the DFS didn't visit any blocks, add them to the end of blockSequence
1070+
if (dfsTree->GetPostOrderCount() < compiler->fgBBcount)
1071+
{
1072+
// Unvisited blocks are more likely to be at the back of the list, so iterate backwards
1073+
unsigned i = dfsTree->GetPostOrderCount();
1074+
for (BasicBlock* block = compiler->fgLastBB; i < compiler->fgBBcount; block = block->Prev())
11251075
{
1126-
nextBlock = getNextCandidateFromWorkList();
1127-
1128-
// TODO-Throughput: We would like to bypass this traversal if we know we've handled all
1129-
// the blocks - but fgBBcount does not appear to be updated when blocks are removed.
1130-
if (nextBlock == nullptr /* && bbSeqCount != compiler->fgBBcount*/ && !verifiedAllBBs)
1131-
{
1132-
// If we don't encounter all blocks by traversing the regular successor links, do a full
1133-
// traversal of all the blocks, and add them in layout order.
1134-
// This may include:
1135-
// - internal-only blocks which may not be in the flow graph
1136-
// - blocks that have become unreachable due to optimizations, but that are strongly
1137-
// connected (these are not removed)
1138-
// - EH blocks
1139-
1140-
for (BasicBlock* const seqBlock : compiler->Blocks())
1141-
{
1142-
if (!isBlockVisited(seqBlock))
1143-
{
1144-
JITDUMP("\tUnvisited block: " FMT_BB, seqBlock->bbNum);
1145-
addToBlockSequenceWorkList(readySet, seqBlock, predSet);
1146-
BlockSetOps::AddElemD(compiler, readySet, seqBlock->bbNum);
1147-
}
1148-
}
1149-
verifiedAllBBs = true;
1150-
}
1151-
else
1076+
assert(block != nullptr);
1077+
if (!isBlockVisited(block))
11521078
{
1153-
break;
1079+
visitBlock(block);
1080+
blockSequence[i++] = block;
11541081
}
11551082
}
11561083
}
1084+
1085+
bbSeqCount = compiler->fgBBcount;
11571086
blockSequencingDone = true;
11581087

11591088
#ifdef DEBUG
@@ -1199,169 +1128,6 @@ void LinearScan::setBlockSequence()
11991128
#endif
12001129
}
12011130

1202-
//------------------------------------------------------------------------
1203-
// compareBlocksForSequencing: Compare two basic blocks for sequencing order.
1204-
//
1205-
// Arguments:
1206-
// block1 - the first block for comparison
1207-
// block2 - the second block for comparison
1208-
// useBlockWeights - whether to use block weights for comparison
1209-
//
1210-
// Return Value:
1211-
// -1 if block1 is preferred.
1212-
// 0 if the blocks are equivalent.
1213-
// 1 if block2 is preferred.
1214-
//
1215-
// Notes:
1216-
// See addToBlockSequenceWorkList.
1217-
//
1218-
int LinearScan::compareBlocksForSequencing(BasicBlock* block1, BasicBlock* block2, bool useBlockWeights)
1219-
{
1220-
if (useBlockWeights)
1221-
{
1222-
weight_t weight1 = block1->getBBWeight(compiler);
1223-
weight_t weight2 = block2->getBBWeight(compiler);
1224-
1225-
if (weight1 > weight2)
1226-
{
1227-
return -1;
1228-
}
1229-
else if (weight1 < weight2)
1230-
{
1231-
return 1;
1232-
}
1233-
}
1234-
1235-
// If weights are the same prefer LOWER bbnum
1236-
if (block1->bbNum < block2->bbNum)
1237-
{
1238-
return -1;
1239-
}
1240-
else if (block1->bbNum == block2->bbNum)
1241-
{
1242-
return 0;
1243-
}
1244-
else
1245-
{
1246-
return 1;
1247-
}
1248-
}
1249-
1250-
//------------------------------------------------------------------------
1251-
// addToBlockSequenceWorkList: Add a BasicBlock to the work list for sequencing.
1252-
//
1253-
// Arguments:
1254-
// sequencedBlockSet - the set of blocks that are already sequenced
1255-
// block - the new block to be added
1256-
// predSet - the buffer to save predecessors set. A block set allocated by the caller used here as a
1257-
// temporary block set for constructing a predecessor set. Allocated by the caller to avoid reallocating a new block
1258-
// set with every call to this function
1259-
//
1260-
// Return Value:
1261-
// None.
1262-
//
1263-
// Notes:
1264-
// The first block in the list will be the next one to be sequenced, as soon
1265-
// as we encounter a block whose successors have all been sequenced, in pred-first
1266-
// order, or the very next block if we are traversing in random order (once implemented).
1267-
// This method uses a comparison method to determine the order in which to place
1268-
// the blocks in the list. This method queries whether all predecessors of the
1269-
// block are sequenced at the time it is added to the list and if so uses block weights
1270-
// for inserting the block. A block is never inserted ahead of its predecessors.
1271-
// A block at the time of insertion may not have all its predecessors sequenced, in
1272-
// which case it will be sequenced based on its block number. Once a block is inserted,
1273-
// its priority\order will not be changed later once its remaining predecessors are
1274-
// sequenced. This would mean that work list may not be sorted entirely based on
1275-
// block weights alone.
1276-
//
1277-
// Note also that, when random traversal order is implemented, this method
1278-
// should insert the blocks into the list in random order, so that we can always
1279-
// simply select the first block in the list.
1280-
//
1281-
void LinearScan::addToBlockSequenceWorkList(BlockSet sequencedBlockSet, BasicBlock* block, BlockSet& predSet)
1282-
{
1283-
// The block that is being added is not already sequenced
1284-
assert(!BlockSetOps::IsMember(compiler, sequencedBlockSet, block->bbNum));
1285-
1286-
// Get predSet of block
1287-
BlockSetOps::ClearD(compiler, predSet);
1288-
for (BasicBlock* const predBlock : block->PredBlocks())
1289-
{
1290-
BlockSetOps::AddElemD(compiler, predSet, predBlock->bbNum);
1291-
}
1292-
1293-
// If either a rarely run block or all its preds are already sequenced, use block's weight to sequence
1294-
bool useBlockWeight = block->isRunRarely() || BlockSetOps::IsSubset(compiler, sequencedBlockSet, predSet);
1295-
JITDUMP(", Criteria: %s", useBlockWeight ? "weight" : "bbNum");
1296-
1297-
BasicBlockList* prevNode = nullptr;
1298-
BasicBlockList* nextNode = blockSequenceWorkList;
1299-
while (nextNode != nullptr)
1300-
{
1301-
int seqResult;
1302-
1303-
if (nextNode->block->isRunRarely())
1304-
{
1305-
// If the block that is yet to be sequenced is a rarely run block, always use block weights for sequencing
1306-
seqResult = compareBlocksForSequencing(nextNode->block, block, true);
1307-
}
1308-
else if (BlockSetOps::IsMember(compiler, predSet, nextNode->block->bbNum))
1309-
{
1310-
// always prefer unsequenced pred blocks
1311-
seqResult = -1;
1312-
}
1313-
else
1314-
{
1315-
seqResult = compareBlocksForSequencing(nextNode->block, block, useBlockWeight);
1316-
}
1317-
1318-
if (seqResult > 0)
1319-
{
1320-
break;
1321-
}
1322-
1323-
prevNode = nextNode;
1324-
nextNode = nextNode->next;
1325-
}
1326-
1327-
BasicBlockList* newListNode = new (compiler, CMK_LSRA) BasicBlockList(block, nextNode);
1328-
if (prevNode == nullptr)
1329-
{
1330-
blockSequenceWorkList = newListNode;
1331-
}
1332-
else
1333-
{
1334-
prevNode->next = newListNode;
1335-
}
1336-
1337-
#ifdef DEBUG
1338-
nextNode = blockSequenceWorkList;
1339-
JITDUMP(", Worklist: [");
1340-
while (nextNode != nullptr)
1341-
{
1342-
JITDUMP(FMT_BB " ", nextNode->block->bbNum);
1343-
nextNode = nextNode->next;
1344-
}
1345-
JITDUMP("]\n");
1346-
#endif
1347-
}
1348-
1349-
void LinearScan::removeFromBlockSequenceWorkList(BasicBlockList* listNode, BasicBlockList* prevNode)
1350-
{
1351-
if (listNode == blockSequenceWorkList)
1352-
{
1353-
assert(prevNode == nullptr);
1354-
blockSequenceWorkList = listNode->next;
1355-
}
1356-
else
1357-
{
1358-
assert(prevNode != nullptr && prevNode->next == listNode);
1359-
prevNode->next = listNode->next;
1360-
}
1361-
// TODO-Cleanup: consider merging Compiler::BlockListNode and BasicBlockList
1362-
// compiler->FreeBlockListNode(listNode);
1363-
}
1364-
13651131
// Initialize the block order for allocation (called each time a new traversal begins).
13661132
BasicBlock* LinearScan::startBlockSequence()
13671133
{

0 commit comments

Comments
 (0)