Skip to content

Commit 630f662

Browse files
committed
[MERGE #6232 @pleath] Change the way the JIT renumbers byte code temps
Merge pull request #6232 from pleath:no-unused The JIT currently does not renumber byte code temps in boolean-expression-like cases that have the form: Brm $L1 Rm = op1 ... Br $L2 $L1: Rm = op2 ... $L2: ... = op3 Rm This is hard to maintain, because it relies on conscientious use of the Unused op in byte code where a def of a register is not followed by a use, but we still want to renumber the register at the next use. Failure to emit Unused results in silent failure to renumber, which in turn results in suboptimal jitted code. This change deletes Unused, adds several byte codes of the form op_ReuseLoc, and causes the JIT to renumber lifetimes at each def unless the op_ReuseLoc is emitted. Failure to emit op_ReuseLoc will produce incorrect code that is detectable by the JIT (i.e., temps not defined on all paths). The biggest change is to the family of byte code patterns we emit to handle dynamic binding cases. These are restructured to limit the kinds of op_ReuseLoc ops we need to define.
2 parents 68edfad + fc907f2 commit 630f662

23 files changed

+23966
-23741
lines changed

lib/Backend/FlowGraph.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -835,6 +835,7 @@ FlowGraph::RunPeeps()
835835
case Js::OpCode::BrSrNeq_A:
836836
case Js::OpCode::BrOnHasProperty:
837837
case Js::OpCode::BrOnNoProperty:
838+
case Js::OpCode::BrOnHasLocalProperty:
838839
case Js::OpCode::BrOnNoLocalProperty:
839840
case Js::OpCode::BrHasSideEffects:
840841
case Js::OpCode::BrNotHasSideEffects:

lib/Backend/GlobOpt.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3418,6 +3418,7 @@ GlobOpt::OptSrc(IR::Opnd *opnd, IR::Instr * *pInstr, Value **indirIndexValRef, I
34183418
case Js::OpCode::BrOnNoProperty:
34193419
case Js::OpCode::BrOnNoLocalProperty:
34203420
case Js::OpCode::BrOnHasProperty:
3421+
case Js::OpCode::BrOnHasLocalProperty:
34213422
case Js::OpCode::LdMethodFldPolyInlineMiss:
34223423
case Js::OpCode::StSlotChkUndecl:
34233424
case Js::OpCode::ScopedLdInst:

lib/Backend/IR.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1906,6 +1906,14 @@ BranchInstr::Invert()
19061906
this->m_opcode = Js::OpCode::BrOnNoProperty;
19071907
break;
19081908

1909+
case Js::OpCode::BrOnHasLocalProperty:
1910+
this->m_opcode = Js::OpCode::BrOnNoLocalProperty;
1911+
break;
1912+
1913+
case Js::OpCode::BrOnNoLocalProperty:
1914+
this->m_opcode = Js::OpCode::BrOnHasLocalProperty;
1915+
break;
1916+
19091917
case Js::OpCode::BrOnNoProperty:
19101918
this->m_opcode = Js::OpCode::BrOnHasProperty;
19111919
break;

lib/Backend/IRBuilder.cpp

Lines changed: 75 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -353,12 +353,10 @@ IRBuilder::Build()
353353
if (tempCount > 0)
354354
{
355355
this->tempMap = AnewArrayZ(m_tempAlloc, SymID, tempCount);
356-
this->fbvTempUsed = BVFixed::New<JitArenaAllocator>(tempCount, m_tempAlloc);
357356
}
358357
else
359358
{
360359
this->tempMap = nullptr;
361-
this->fbvTempUsed = nullptr;
362360
}
363361

364362
m_func->m_headInstr = IR::EntryInstr::New(Js::OpCode::FunctionEntry, m_func);
@@ -1224,7 +1222,6 @@ IRBuilder::BuildSrcStackSymID(Js::RegSlot regSlot)
12241222
this->SetMappedTemp(regSlot, symID);
12251223
this->EnsureLoopBodyLoadSlot(symID);
12261224
}
1227-
this->SetTempUsed(regSlot, TRUE);
12281225
}
12291226
else
12301227
{
@@ -1315,7 +1312,7 @@ IRBuilder::BuildSrcOpnd(Js::RegSlot srcRegSlot, IRType type)
13151312
///----------------------------------------------------------------------------
13161313

13171314
IR::RegOpnd *
1318-
IRBuilder::BuildDstOpnd(Js::RegSlot dstRegSlot, IRType type, bool isCatchObjectSym)
1315+
IRBuilder::BuildDstOpnd(Js::RegSlot dstRegSlot, IRType type, bool isCatchObjectSym, bool reuseTemp)
13191316
{
13201317
StackSym * symDst;
13211318
SymID symID;
@@ -1336,24 +1333,20 @@ IRBuilder::BuildDstOpnd(Js::RegSlot dstRegSlot, IRType type, bool isCatchObjectS
13361333

13371334
// This is a def of a temp. Create a new sym ID for it if it's been used since its last def.
13381335
// !!!NOTE: always process an instruction's temp uses before its temp defs!!!
1339-
if (this->GetTempUsed(dstRegSlot))
1336+
1337+
symID = this->GetMappedTemp(dstRegSlot);
1338+
if (symID == 0)
13401339
{
1341-
symID = m_func->m_symTable->NewID();
1342-
this->SetTempUsed(dstRegSlot, FALSE);
1340+
// First time we've seen the temp. Just use the number that the front end gave it.
1341+
symID = static_cast<SymID>(dstRegSlot);
13431342
this->SetMappedTemp(dstRegSlot, symID);
13441343
}
1345-
else
1344+
else if (!reuseTemp)
13461345
{
1347-
symID = this->GetMappedTemp(dstRegSlot);
1348-
// The temp hasn't been used since its last def. There are 2 possibilities:
1349-
if (symID == 0)
1350-
{
1351-
// First time we've seen the temp. Just use the number that the front end gave it.
1352-
symID = static_cast<SymID>(dstRegSlot);
1353-
this->SetMappedTemp(dstRegSlot, symID);
1354-
}
1346+
// Byte code has not told us to reuse the mapped temp at this def, so don't. Make a new one.
1347+
symID = m_func->m_symTable->NewID();
1348+
this->SetMappedTemp(dstRegSlot, symID);
13551349
}
1356-
13571350
}
13581351
else
13591352
{
@@ -1506,6 +1499,7 @@ IRBuilder::BuildReg1(Js::OpCode newOpcode, uint32 offset, Js::RegSlot R0)
15061499
IR::Opnd * srcOpnd = nullptr;
15071500
bool isNotInt = false;
15081501
bool dstIsCatchObject = false;
1502+
bool reuseLoc = false;
15091503
ValueType dstValueType;
15101504
switch (newOpcode)
15111505
{
@@ -1560,6 +1554,9 @@ IRBuilder::BuildReg1(Js::OpCode newOpcode, uint32 offset, Js::RegSlot R0)
15601554
isNotInt = true;
15611555
break;
15621556

1557+
case Js::OpCode::LdLocalObj_ReuseLoc:
1558+
reuseLoc = true;
1559+
// fall through
15631560
case Js::OpCode::LdLocalObj:
15641561
if (!m_func->GetJITFunctionBody()->HasScopeObject())
15651562
{
@@ -1636,6 +1633,9 @@ IRBuilder::BuildReg1(Js::OpCode newOpcode, uint32 offset, Js::RegSlot R0)
16361633
break;
16371634
}
16381635

1636+
case Js::OpCode::LdFalse_ReuseLoc:
1637+
reuseLoc = true;
1638+
// fall through
16391639
case Js::OpCode::LdFalse:
16401640
{
16411641
const auto addrOpnd = IR::AddrOpnd::New(m_func->GetScriptContextInfo()->GetFalseAddr(), IR::AddrOpndKindDynamicVar, m_func, true);
@@ -1645,6 +1645,9 @@ IRBuilder::BuildReg1(Js::OpCode newOpcode, uint32 offset, Js::RegSlot R0)
16451645
break;
16461646
}
16471647

1648+
case Js::OpCode::LdTrue_ReuseLoc:
1649+
reuseLoc = true;
1650+
// fall through
16481651
case Js::OpCode::LdTrue:
16491652
{
16501653
const auto addrOpnd = IR::AddrOpnd::New(m_func->GetScriptContextInfo()->GetTrueAddr(), IR::AddrOpndKindDynamicVar, m_func, true);
@@ -1666,12 +1669,6 @@ IRBuilder::BuildReg1(Js::OpCode newOpcode, uint32 offset, Js::RegSlot R0)
16661669
isNotInt = TRUE;
16671670
break;
16681671

1669-
case Js::OpCode::Unused:
1670-
// Don't generate anything. Just indicate that the temp reg is used.
1671-
Assert(this->RegIsTemp(dstRegSlot));
1672-
this->SetTempUsed(dstRegSlot, TRUE);
1673-
return;
1674-
16751672
case Js::OpCode::InitUndecl:
16761673
srcOpnd = IR::AddrOpnd::New(m_func->GetScriptContextInfo()->GetUndeclBlockVarAddr(), IR::AddrOpndKindDynamicVar, m_func, true);
16771674
srcOpnd->SetValueType(ValueType::PrimitiveOrObject);
@@ -1705,7 +1702,7 @@ IRBuilder::BuildReg1(Js::OpCode newOpcode, uint32 offset, Js::RegSlot R0)
17051702
}
17061703
}
17071704

1708-
IR::RegOpnd * dstOpnd = this->BuildDstOpnd(dstRegSlot, TyVar, dstIsCatchObject);
1705+
IR::RegOpnd * dstOpnd = this->BuildDstOpnd(dstRegSlot, TyVar, dstIsCatchObject, reuseLoc);
17091706
dstOpnd->SetValueType(dstValueType);
17101707
StackSym * dstSym = dstOpnd->m_sym;
17111708
dstSym->m_isCatchObjectSym = dstIsCatchObject;
@@ -1773,9 +1770,25 @@ IRBuilder::BuildReg2(Js::OpCode newOpcode, uint32 offset, Js::RegSlot R0, Js::Re
17731770
{
17741771
IR::RegOpnd * src1Opnd = this->BuildSrcOpnd(R1);
17751772
StackSym * symSrc1 = src1Opnd->m_sym;
1773+
bool reuseLoc = false;
17761774

17771775
switch (newOpcode)
17781776
{
1777+
case Js::OpCode::Ld_A_ReuseLoc:
1778+
newOpcode = Js::OpCode::Ld_A;
1779+
reuseLoc = true;
1780+
break;
1781+
1782+
case Js::OpCode::Typeof_ReuseLoc:
1783+
newOpcode = Js::OpCode::Typeof;
1784+
reuseLoc = true;
1785+
break;
1786+
1787+
case Js::OpCode::UnwrapWithObj_ReuseLoc:
1788+
newOpcode = Js::OpCode::UnwrapWithObj;
1789+
reuseLoc = true;
1790+
break;
1791+
17791792
case Js::OpCode::SpreadObjectLiteral:
17801793
// fall through
17811794
case Js::OpCode::SetComputedNameVar:
@@ -1807,7 +1820,7 @@ IRBuilder::BuildReg2(Js::OpCode newOpcode, uint32 offset, Js::RegSlot R0, Js::Re
18071820
}
18081821
}
18091822

1810-
IR::RegOpnd * dstOpnd = this->BuildDstOpnd(R0);
1823+
IR::RegOpnd * dstOpnd = this->BuildDstOpnd(R0, TyVar, false, reuseLoc);
18111824
StackSym * dstSym = dstOpnd->m_sym;
18121825

18131826
IR::Instr * instr = nullptr;
@@ -2380,7 +2393,7 @@ IRBuilder::BuildReg2B1(Js::OpCode newOpcode, uint32 offset, Js::RegSlot dstRegSl
23802393

23812394
IR::Instr * instr;
23822395
IR::RegOpnd * srcOpnd = this->BuildSrcOpnd(srcRegSlot);
2383-
IR::RegOpnd * dstOpnd = this->BuildDstOpnd(dstRegSlot);
2396+
IR::RegOpnd * dstOpnd = this->BuildDstOpnd(dstRegSlot, TyVar, false, true);
23842397

23852398
IR::IndirOpnd * indir1Opnd = IR::IndirOpnd::New(dstOpnd, index, TyVar, m_func);
23862399

@@ -2417,22 +2430,23 @@ IRBuilder::BuildReg3B1(Js::OpCode newOpcode, uint32 offset, Js::RegSlot dstRegSl
24172430
IR::Instr * instr;
24182431
IR::RegOpnd * src1Opnd = this->BuildSrcOpnd(src1RegSlot);
24192432
IR::RegOpnd * src2Opnd = this->BuildSrcOpnd(src2RegSlot);
2420-
IR::RegOpnd * dstOpnd = this->BuildDstOpnd(dstRegSlot);
2421-
dstOpnd->SetValueType(ValueType::String);
2433+
IR::RegOpnd * dstOpnd = nullptr;
24222434

24232435
IR::Instr * newConcatStrMulti = nullptr;
24242436
switch (newOpcode)
24252437
{
24262438
case Js::OpCode::NewConcatStrMulti:
2427-
2439+
dstOpnd = this->BuildDstOpnd(dstRegSlot);
24282440
newConcatStrMulti = IR::Instr::New(Js::OpCode::NewConcatStrMulti, dstOpnd, IR::IntConstOpnd::New(index, TyUint32, m_func), m_func);
24292441
index = 0;
24302442
break;
24312443
case Js::OpCode::SetConcatStrMultiItem2:
2444+
dstOpnd = this->BuildDstOpnd(dstRegSlot, TyVar, false, true);
24322445
break;
24332446
default:
24342447
Assert(false);
24352448
};
2449+
dstOpnd->SetValueType(ValueType::String);
24362450
IR::IndirOpnd * indir1Opnd = IR::IndirOpnd::New(dstOpnd, index, TyVar, m_func);
24372451
IR::IndirOpnd * indir2Opnd = IR::IndirOpnd::New(dstOpnd, index + 1, TyVar, m_func);
24382452

@@ -3152,15 +3166,20 @@ IRBuilder::BuildElementC(Js::OpCode newOpcode, uint32 offset, Js::RegSlot fieldR
31523166
PropertyKind propertyKind = PropertyKindData;
31533167
IR::SymOpnd * fieldSymOpnd = this->BuildFieldOpnd(newOpcode, fieldRegSlot, propertyId, propertyIdIndex, propertyKind);
31543168
IR::RegOpnd * regOpnd;
3169+
bool reuseLoc = false;
31553170

31563171
switch (newOpcode)
31573172
{
3173+
case Js::OpCode::DeleteFld_ReuseLoc:
3174+
newOpcode = Js::OpCode::DeleteFld;
3175+
reuseLoc = true;
3176+
// fall through
31583177
case Js::OpCode::DeleteFld:
31593178
case Js::OpCode::DeleteRootFld:
31603179
case Js::OpCode::DeleteFldStrict:
31613180
case Js::OpCode::DeleteRootFldStrict:
31623181
// Load
3163-
regOpnd = this->BuildDstOpnd(regSlot);
3182+
regOpnd = this->BuildDstOpnd(regSlot, TyVar, false, reuseLoc);
31643183
instr = IR::Instr::New(newOpcode, regOpnd, fieldSymOpnd, m_func);
31653184
break;
31663185

@@ -3482,6 +3501,7 @@ IRBuilder::BuildElementSlotI1(Js::OpCode newOpcode, uint32 offset, Js::RegSlot r
34823501
StackSym * stackFuncPtrSym = nullptr;
34833502
SymID symID = m_func->GetJITFunctionBody()->GetLocalClosureReg();
34843503
bool isLdSlotThatWasNotProfiled = false;
3504+
bool reuseLoc = false;
34853505
StackSym* closureSym = m_func->GetLocalClosureSym();
34863506

34873507
uint scopeSlotSize = this->IsParamScopeDone() ? m_func->GetJITFunctionBody()->GetScopeSlotArraySize() : m_func->GetJITFunctionBody()->GetParamScopeSlotArraySize();
@@ -3679,9 +3699,12 @@ IRBuilder::BuildElementSlotI1(Js::OpCode newOpcode, uint32 offset, Js::RegSlot r
36793699
this->AddInstr(instr, offset);
36803700
break;
36813701

3702+
case Js::OpCode::LdEnvObj_ReuseLoc:
3703+
reuseLoc = true;
3704+
// fall through
36823705
case Js::OpCode::LdEnvObj:
36833706
fieldOpnd = this->BuildFieldOpnd(Js::OpCode::LdSlotArr, this->GetEnvReg(), slotId, (Js::PropertyIdIndexType)-1, PropertyKindSlotArray);
3684-
regOpnd = this->BuildDstOpnd(regSlot);
3707+
regOpnd = this->BuildDstOpnd(regSlot, TyVar, false, reuseLoc);
36853708
instr = IR::Instr::New(Js::OpCode::LdSlotArr, regOpnd, fieldOpnd, m_func);
36863709
this->AddInstr(instr, offset);
36873710

@@ -4255,9 +4278,14 @@ IRBuilder::BuildElementP(Js::OpCode newOpcode, uint32 offset, Js::RegSlot regSlo
42554278
propertyId = this->m_func->GetJITFunctionBody()->GetPropertyIdFromCacheId(inlineCacheIndex);
42564279

42574280
Js::RegSlot instance = this->GetEnvRegForEvalCode();
4281+
bool reuseLoc = false;
42584282

42594283
switch (newOpcode)
42604284
{
4285+
case Js::OpCode::LdLocalFld_ReuseLoc:
4286+
reuseLoc = true;
4287+
newOpcode = Js::OpCode::LdLocalFld;
4288+
// fall through
42614289
case Js::OpCode::LdLocalFld:
42624290
if (m_func->GetLocalClosureSym()->HasByteCodeRegSlot())
42634291
{
@@ -4272,7 +4300,7 @@ IRBuilder::BuildElementP(Js::OpCode newOpcode, uint32 offset, Js::RegSlot regSlo
42724300
{
42734301
fieldSymOpnd->AsPropertySymOpnd()->TryDisableRuntimePolymorphicCache();
42744302
}
4275-
regOpnd = this->BuildDstOpnd(regSlot);
4303+
regOpnd = this->BuildDstOpnd(regSlot, TyVar, false, reuseLoc);
42764304

42774305
instr = nullptr;
42784306
if (isProfiled)
@@ -4485,8 +4513,13 @@ IRBuilder::BuildElementCP(Js::OpCode newOpcode, uint32 offset, Js::RegSlot insta
44854513

44864514
IR::Instr * instr = nullptr;
44874515
bool isLdFldThatWasNotProfiled = false;
4516+
bool reuseLoc = false;
44884517
switch (newOpcode)
44894518
{
4519+
case Js::OpCode::LdFld_ReuseLoc:
4520+
reuseLoc = true;
4521+
newOpcode = Js::OpCode::LdFld;
4522+
// fall through
44904523
case Js::OpCode::LdFldForTypeOf:
44914524
case Js::OpCode::LdFld:
44924525
case Js::OpCode::LdLen_A:
@@ -4502,7 +4535,7 @@ IRBuilder::BuildElementCP(Js::OpCode newOpcode, uint32 offset, Js::RegSlot insta
45024535
case Js::OpCode::ScopedLdMethodFld:
45034536
// Load
45044537
// LdMethodFromFlags is backend only. Don't need to be added here.
4505-
regOpnd = this->BuildDstOpnd(regSlot);
4538+
regOpnd = this->BuildDstOpnd(regSlot, TyVar, false, reuseLoc);
45064539

45074540
if (isProfiled)
45084541
{
@@ -4879,6 +4912,7 @@ IRBuilder::BuildElementU(Js::OpCode newOpcode, uint32 offset, Js::RegSlot instan
48794912
IR::RegOpnd * regOpnd;
48804913
IR::SymOpnd * fieldSymOpnd;
48814914
Js::PropertyId propertyId = m_func->GetJITFunctionBody()->GetReferencedPropertyId(propertyIdIndex);
4915+
bool reuseLoc = false;
48824916

48834917
switch (newOpcode)
48844918
{
@@ -4926,10 +4960,14 @@ IRBuilder::BuildElementU(Js::OpCode newOpcode, uint32 offset, Js::RegSlot instan
49264960
instr = IR::Instr::New(newOpcode, fieldSymOpnd, regOpnd, m_func);
49274961
break;
49284962

4963+
case Js::OpCode::DeleteLocalFld_ReuseLoc:
4964+
newOpcode = Js::OpCode::DeleteLocalFld;
4965+
reuseLoc = true;
4966+
// fall through
49294967
case Js::OpCode::DeleteLocalFld:
49304968
newOpcode = Js::OpCode::DeleteFld;
49314969
fieldSymOpnd = BuildFieldOpnd(newOpcode, m_func->GetJITFunctionBody()->GetLocalClosureReg(), propertyId, propertyIdIndex, PropertyKindData);
4932-
regOpnd = BuildDstOpnd(instance);
4970+
regOpnd = BuildDstOpnd(instance, TyVar, false, reuseLoc);
49334971
instr = IR::Instr::New(newOpcode, regOpnd, fieldSymOpnd, m_func);
49344972
break;
49354973

@@ -7278,7 +7316,7 @@ void
72787316
IRBuilder::BuildBrLocalProperty(Js::OpCode newOpcode, uint32 offset)
72797317
{
72807318
Assert(!OpCodeAttr::HasMultiSizeLayout(newOpcode));
7281-
Assert(newOpcode == Js::OpCode::BrOnNoLocalProperty);
7319+
Assert(newOpcode == Js::OpCode::BrOnHasLocalProperty);
72827320

72837321
const unaligned Js::OpLayoutBrLocalProperty *branchInsn = m_jnReader.BrLocalProperty();
72847322

@@ -7322,7 +7360,8 @@ IRBuilder::BuildBrEnvProperty(Js::OpCode newOpcode, uint32 offset)
73227360
fieldSym = PropertySym::New(regOpnd->m_sym, propertyId, branchInsn->PropertyIdIndex, (uint)-1, PropertyKindData, m_func);
73237361
fieldOpnd = IR::SymOpnd::New(fieldSym, TyVar, m_func);
73247362

7325-
branchInstr = IR::BranchInstr::New(newOpcode == Js::OpCode::BrOnNoEnvProperty ? Js::OpCode::BrOnNoProperty : Js::OpCode::BrOnNoLocalProperty, nullptr, fieldOpnd, m_func);
7363+
Assert(newOpcode == Js::OpCode::BrOnHasEnvProperty || newOpcode == Js::OpCode::BrOnHasLocalEnvProperty);
7364+
branchInstr = IR::BranchInstr::New(newOpcode == Js::OpCode::BrOnHasEnvProperty ? Js::OpCode::BrOnHasProperty : Js::OpCode::BrOnHasLocalProperty, nullptr, fieldOpnd, m_func);
73267365
this->AddBranchInstr(branchInstr, offset, targetOffset);
73277366
}
73287367

0 commit comments

Comments
 (0)