Skip to content

Commit 579267d

Browse files
[MERGE #5153 @Penguinwizzard] Hoist speculation masking when possible
Merge pull request #5153 from Penguinwizzard:ponch_opt_clean This change attempts to move speculation-related masking from the inside of loops to the out-edges. The bulk of the work happens in the dead store pass; I took advantage of the multiple passes on loops to gather data about speculated uses, and then the final dead store pass handles marking instructions which we think are safe and storing the data for the Lowerer. There's a new set of data structures introduced in ClusterList.h; the purpose of these is to try to get relatively efficient set joins when dealing with the sets of symbols here. There's a couple of parts that may be a little tailored for this use case (segmentclusterlist is set up for symbol clustering patterns and for two-pass runs), but I think we might find another use for them sometime. The Lowering code is pretty limited to handling the new opcode, which has a list of symbols to mask, and expands to instructions that block speculation on those symbols.
2 parents 512bdf6 + 26b53dc commit 579267d

22 files changed

+1452
-212
lines changed

lib/Backend/BackwardPass.cpp

Lines changed: 719 additions & 4 deletions
Large diffs are not rendered by default.

lib/Backend/BackwardPass.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,9 +178,16 @@ class BackwardPass
178178
BVSparse<JitArenaAllocator> * intOverflowDoesNotMatterInRangeBySymId;
179179
BVSparse<JitArenaAllocator> * candidateSymsRequiredToBeInt;
180180
BVSparse<JitArenaAllocator> * candidateSymsRequiredToBeLossyInt;
181-
StackSym *considerSymAsRealUseInNoImplicitCallUses;
181+
StackSym * considerSymAsRealUseInNoImplicitCallUses;
182182
bool intOverflowCurrentlyMattersInRange;
183183
bool isCollectionPass;
184+
enum class CollectionPassSubPhase
185+
{
186+
None,
187+
FirstPass,
188+
SecondPass
189+
} collectionPassSubPhase;
190+
bool isLoopPrepass;
184191

185192
class FloatSymEquivalenceClass
186193
{

lib/Backend/FlowGraph.cpp

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -561,11 +561,11 @@ FlowGraph::Build(void)
561561

562562
// Add edge to finally block, leave block
563563
this->AddEdge(currentBlock, this->finallyLabelStack->Top()->GetBasicBlock());
564-
this->AddEdge(currentBlock, leaveBlock);
564+
this->AddEdge(currentBlock, leaveBlock);
565565
}
566566
}
567567
}
568-
}
568+
}
569569
else if (instr->m_opcode == Js::OpCode::Finally)
570570
{
571571
AssertOrFailFast(!this->finallyLabelStack->Empty());
@@ -581,7 +581,7 @@ FlowGraph::Build(void)
581581
block->SetBlockNum(blockNum++);
582582
} NEXT_BLOCK_ALL;
583583
}
584-
584+
585585
this->FindLoops();
586586

587587
#if DBG_DUMP
@@ -1739,6 +1739,14 @@ FlowGraph::Destroy(void)
17391739
Region * predRegion = nullptr;
17401740
FOREACH_PREDECESSOR_BLOCK(predBlock, block)
17411741
{
1742+
BasicBlock* intermediateBlock = block;
1743+
// Skip blocks inserted for airlock/masking purposes
1744+
while ((predBlock->isAirLockBlock || predBlock->isAirLockCompensationBlock) && predBlock->GetFirstInstr()->AsLabelInstr()->GetRegion() == region)
1745+
{
1746+
Assert(predBlock->GetPredList()->HasOne());
1747+
intermediateBlock = predBlock;
1748+
predBlock = predBlock->GetPredList()->Head()->GetPred();
1749+
}
17421750
predRegion = predBlock->GetFirstInstr()->AsLabelInstr()->GetRegion();
17431751
if (predBlock->GetLastInstr() == nullptr)
17441752
{
@@ -1751,7 +1759,7 @@ FlowGraph::Destroy(void)
17511759
case Js::OpCode::TryCatch:
17521760
case Js::OpCode::TryFinally:
17531761
AssertMsg(region->GetParent() == predRegion, "Bad region prop on entry to try-catch/finally");
1754-
if (block->GetFirstInstr() == predBlock->GetLastInstr()->AsBranchInstr()->GetTarget())
1762+
if (intermediateBlock->GetFirstInstr() == predBlock->GetLastInstr()->AsBranchInstr()->GetTarget())
17551763
{
17561764
if (predBlock->GetLastInstr()->m_opcode == Js::OpCode::TryCatch)
17571765
{
@@ -2069,7 +2077,7 @@ FlowGraph::UpdateRegionForBlockFromEHPred(BasicBlock * block, bool reassign)
20692077
Assert(region || block->GetPredList()->Count() == 0 || block->firstInstr->AsLabelInstr()->GetRegion());
20702078

20712079
if (region)
2072-
{
2080+
{
20732081
if (!region->ehBailoutData)
20742082
{
20752083
region->AllocateEHBailoutData(this->func, tryInstr);
@@ -2253,15 +2261,36 @@ FlowGraph::InsertAirlockBlock(FlowEdge * edge)
22532261
BasicBlock * sourceBlock = edge->GetPred();
22542262
BasicBlock * sinkBlock = edge->GetSucc();
22552263

2264+
IR::Instr * sourceLastInstr = sourceBlock->GetLastInstr();
2265+
2266+
//
2267+
// Normalize block
2268+
//
2269+
if(!sourceLastInstr->IsBranchInstr())
2270+
{
2271+
// There are some cases where the last instruction of a block can be not a branch;
2272+
// for example, if it was previously a conditional branch that was impossible to take.
2273+
// In these situations, we can insert an unconditional branch to fallthrough for that
2274+
// block, to renormalize it.
2275+
SListBaseCounted<FlowEdge*>* successors = sourceBlock->GetSuccList();
2276+
// Only handling the case for one arc left at the moment; other cases are likely bugs.
2277+
AssertOrFailFastMsg(successors->HasOne(), "Failed to normalize weird block before airlock");
2278+
FlowEdge* onlyLink = successors->Head();
2279+
AssertOrFailFastMsg(onlyLink == edge, "Found duplicate of edge?");
2280+
AssertOrFailFastMsg(onlyLink->GetSucc() == sinkBlock, "State inconsistent");
2281+
sourceLastInstr->InsertAfter(IR::BranchInstr::New(Js::OpCode::Br, onlyLink->GetSucc()->GetFirstInstr()->AsLabelInstr(), sourceLastInstr->m_func));
2282+
sourceLastInstr = sourceLastInstr->m_next;
2283+
}
2284+
22562285
BasicBlock * sinkPrevBlock = sinkBlock->prev;
22572286
IR::Instr * sinkPrevBlockLastInstr = sinkPrevBlock->GetLastInstr();
2258-
IR::Instr * sourceLastInstr = sourceBlock->GetLastInstr();
22592287

22602288
airlockBlock->loop = sinkBlock->loop;
22612289
airlockBlock->SetBlockNum(this->blockCount++);
22622290
#ifdef DBG
22632291
airlockBlock->isAirLockBlock = true;
22642292
#endif
2293+
22652294
//
22662295
// Fixup block linkage
22672296
//
@@ -2312,6 +2341,7 @@ FlowGraph::InsertAirlockBlock(FlowEdge * edge)
23122341
airlockBlock->SetLastInstr(airlockBr);
23132342

23142343
airlockLabel->SetByteCodeOffset(sinkLabel);
2344+
airlockLabel->SetRegion(sinkLabel->GetRegion());
23152345

23162346
// Fixup flow out of sourceBlock
23172347
IR::BranchInstr *sourceBr = sourceLastInstr->AsBranchInstr();
@@ -2433,6 +2463,7 @@ FlowGraph::InsertCompensationCodeForBlockMove(FlowEdge * edge, bool insertToLoo
24332463
compBlock->SetLastInstr(compBr);
24342464

24352465
compLabel->SetByteCodeOffset(sinkLabel);
2466+
compLabel->SetRegion(sinkLabel->GetRegion());
24362467

24372468
// Fixup flow out of sourceBlock
24382469
if (sourceLastInstr->IsBranchInstr())

lib/Backend/FlowGraph.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -603,6 +603,10 @@ class Loop
603603
BasicBlock *dominatingLoopCountableBlock;
604604
LoopCount *loopCount;
605605
SymIdToStackSymMap *loopCountBasedBoundBaseSyms;
606+
typedef SegmentClusterList<SymID, JitArenaAllocator> LoopSymClusterList;
607+
LoopSymClusterList *symClusterList;
608+
BVSparse<JitArenaAllocator> * internallyDereferencedSyms;
609+
SList<IR::ByteCodeUsesInstr*> *outwardSpeculationMaskInstrs;
606610

607611
bool isDead : 1;
608612
bool hasDeadStoreCollectionPass : 1;
@@ -729,6 +733,9 @@ class Loop
729733
dominatingLoopCountableBlock(nullptr),
730734
loopCount(nullptr),
731735
loopCountBasedBoundBaseSyms(nullptr),
736+
symClusterList(nullptr),
737+
internallyDereferencedSyms(nullptr),
738+
outwardSpeculationMaskInstrs(nullptr),
732739
isDead(false),
733740
allFieldsKilled(false),
734741
isLeaf(true),

lib/Backend/GlobOpt.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -932,7 +932,7 @@ class GlobOpt
932932
bool CheckIfInstrInTypeCheckSeqEmitsTypeCheck(IR::Instr* instr, IR::PropertySymOpnd *opnd);
933933
template<bool makeChanges>
934934
bool ProcessPropOpInTypeCheckSeq(IR::Instr* instr, IR::PropertySymOpnd *opnd, BasicBlock* block, bool updateExistingValue, bool* emitsTypeCheckOut = nullptr, bool* changesTypeValueOut = nullptr, bool *isObjTypeChecked = nullptr);
935-
void KillObjectHeaderInlinedTypeSyms(BasicBlock *block, bool isObjTypeSpecialized, SymID symId = (SymID)-1);
935+
void KillObjectHeaderInlinedTypeSyms(BasicBlock *block, bool isObjTypeSpecialized, SymID symId = SymID_Invalid);
936936
void ValueNumberObjectType(IR::Opnd *dstOpnd, IR::Instr *instr);
937937
void SetSingleTypeOnObjectTypeValue(Value* value, const JITTypeHolder type);
938938
void SetTypeSetOnObjectTypeValue(Value* value, Js::EquivalentTypeSet* typeSet);

lib/Backend/IR.cpp

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4532,8 +4532,16 @@ Instr::Dump(IRDumpFlags flags)
45324532
}
45334533
}
45344534

4535-
Output::SkipToColumn(20);
4536-
Output::Print(_u("="));
4535+
if (this->isSafeToSpeculate)
4536+
{
4537+
Output::SkipToColumn(19);
4538+
Output::Print(_u("<=="));
4539+
}
4540+
else
4541+
{
4542+
Output::SkipToColumn(20);
4543+
Output::Print(_u("="));
4544+
}
45374545
}
45384546

45394547
PrintOpCodeName();
@@ -4610,21 +4618,22 @@ Instr::Dump(IRDumpFlags flags)
46104618
}
46114619
}
46124620

4613-
if (this->IsByteCodeUsesInstr())
4621+
if (this->IsByteCodeUsesInstr() || this->m_opcode == Js::OpCode::SpeculatedLoadFence)
46144622
{
4615-
if (this->AsByteCodeUsesInstr()->GetByteCodeUpwardExposedUsed())
4623+
ByteCodeUsesInstr* tempbcu = static_cast<ByteCodeUsesInstr*>(this);
4624+
if (tempbcu->GetByteCodeUpwardExposedUsed())
46164625
{
46174626
bool first = true;
4618-
FOREACH_BITSET_IN_SPARSEBV(id, this->AsByteCodeUsesInstr()->GetByteCodeUpwardExposedUsed())
4627+
FOREACH_BITSET_IN_SPARSEBV(id, tempbcu->GetByteCodeUpwardExposedUsed())
46194628
{
46204629
Output::Print(first? _u("s%d") : _u(", s%d"), id);
46214630
first = false;
46224631
}
46234632
NEXT_BITSET_IN_SPARSEBV;
46244633
}
4625-
if (this->AsByteCodeUsesInstr()->propertySymUse)
4634+
if (tempbcu->propertySymUse)
46264635
{
4627-
Output::Print(_u(" PropSym: %d"), this->AsByteCodeUsesInstr()->propertySymUse->m_id);
4636+
Output::Print(_u(" PropSym: %d"), tempbcu->propertySymUse->m_id);
46284637
}
46294638
}
46304639

lib/Backend/IR.h

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,8 @@ class Instr
168168
isCtorCall(false),
169169
isCallInstrProtectedByNoProfileBailout(false),
170170
hasSideEffects(false),
171-
isNonFastPathFrameDisplay(false)
171+
isNonFastPathFrameDisplay(false),
172+
isSafeToSpeculate(false)
172173
#if DBG
173174
, highlight(0)
174175
#endif
@@ -219,6 +220,10 @@ class Instr
219220

220221
bool IsCloned() const { return isCloned; }
221222
void SetIsCloned(bool isCloned) { this->isCloned = isCloned; }
223+
224+
bool IsSafeToSpeculate() const { return isSafeToSpeculate; }
225+
void SetIsSafeToSpeculate(bool isSafe) { this->isSafeToSpeculate = isSafe; }
226+
222227
bool HasBailOutInfo() const { return hasBailOutInfo; }
223228
bool HasAuxBailOut() const { return hasAuxBailOut; }
224229
bool HasTypeCheckBailOut() const;
@@ -503,6 +508,9 @@ class Instr
503508
// used only for SIMD Ld/St from typed arrays.
504509
// we keep these here to avoid increase in number of opcodes and to not use ExtendedArgs
505510
uint8 dataWidth;
511+
#if DBG
512+
WORD highlight;
513+
#endif
506514

507515

508516
bool isFsBased : 1; // TEMP : just for BS testing
@@ -526,8 +534,9 @@ class Instr
526534
bool hasSideEffects : 1; // The instruction cannot be dead stored
527535
bool isNonFastPathFrameDisplay : 1;
528536
protected:
529-
bool isCloned:1;
530-
bool hasBailOutInfo:1;
537+
bool isCloned : 1;
538+
bool hasBailOutInfo : 1;
539+
bool isSafeToSpeculate : 1;
531540

532541
// Used for aux bail out. We are using same bailOutInfo, just different boolean to hide regular bail out.
533542
// Refer to ConvertToBailOutInstr implementation for details.
@@ -538,11 +547,6 @@ class Instr
538547
Opnd * m_dst;
539548
Opnd * m_src1;
540549
Opnd * m_src2;
541-
#if DBG
542-
WORD highlight;
543-
#endif
544-
545-
546550

547551
void Init(Js::OpCode opcode, IRKind kind, Func * func);
548552
IR::Instr * CloneInstr() const;

lib/Backend/IR.inl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ Instr::AsProfiledLabelInstr()
204204
inline bool
205205
Instr::IsByteCodeUsesInstr() const
206206
{
207-
return GetKind() == IR::InstrKindByteCodeUses;
207+
return GetKind() == IR::InstrKindByteCodeUses && m_opcode == Js::OpCode::ByteCodeUses;
208208
}
209209

210210
inline ByteCodeUsesInstr *

lib/Backend/LinearScan.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2114,7 +2114,7 @@ void LinearScan::RecordLoopUse(Lifetime *lifetime, RegNum reg)
21142114
// We are trying to avoid the need for compensation at the bottom of the loop if
21152115
// the reg ends up being spilled before it is actually used.
21162116
Loop *curLoop = this->curLoop;
2117-
SymID symId = (SymID)-1;
2117+
SymID symId = SymID_Invalid;
21182118

21192119
if (lifetime)
21202120
{

0 commit comments

Comments
 (0)