Skip to content

Commit 17d30e5

Browse files
committed
Merge branch 'NewScObj_all_tests_pass' into build/wyrichte/NewScObj/BASE_squash_rebase_bcup_refactor_squash
2 parents 9ae76ce + 26abc99 commit 17d30e5

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

72 files changed

+1733
-680
lines changed

lib/Backend/BackendApi.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ Js::JavascriptMethod GetCheckAsmJsCodeGenThunk()
126126

127127
uint GetBailOutRegisterSaveSlotCount()
128128
{
129-
// REVIEW: not all registers are used, we are allocating more space then necessary.
129+
// REVIEW: not all registers are used, we are allocating more space than necessary.
130130
return LinearScanMD::GetRegisterSaveSlotCount();
131131
}
132132

lib/Backend/BackwardPass.cpp

+40-14
Original file line numberDiff line numberDiff line change
@@ -88,20 +88,26 @@ BackwardPass::DoMarkTempNumbers() const
8888
}
8989

9090
bool
91-
BackwardPass::DoMarkTempObjects() const
92-
{
93-
// only mark temp object on the backward store phase
94-
return (tag == Js::BackwardPhase) && !PHASE_OFF(Js::MarkTempPhase, this->func) &&
95-
!PHASE_OFF(Js::MarkTempObjectPhase, this->func) && func->DoGlobOpt() && func->GetHasTempObjectProducingInstr() &&
96-
!func->IsJitInDebugMode() &&
97-
func->DoGlobOptsForGeneratorFunc();
91+
BackwardPass::SatisfyMarkTempObjectsConditions() const {
92+
return !PHASE_OFF(Js::MarkTempPhase, this->func) &&
93+
!PHASE_OFF(Js::MarkTempObjectPhase, this->func) &&
94+
func->DoGlobOpt() && func->GetHasTempObjectProducingInstr() &&
95+
!func->IsJitInDebugMode() &&
96+
func->DoGlobOptsForGeneratorFunc();
9897

9998
// Why MarkTempObject is disabled under debugger:
10099
// We add 'identified so far dead non-temp locals' to byteCodeUpwardExposedUsed in ProcessBailOutInfo,
101100
// this may cause MarkTempObject to convert some temps back to non-temp when it sees a 'transferred exposed use'
102101
// from a temp to non-temp. That's in general not a supported conversion (while non-temp -> temp is fine).
103102
}
104103

104+
bool
105+
BackwardPass::DoMarkTempObjects() const
106+
{
107+
// only mark temp object on the backward store phase
108+
return (tag == Js::BackwardPhase) && SatisfyMarkTempObjectsConditions();
109+
}
110+
105111
bool
106112
BackwardPass::DoMarkTempNumbersOnTempObjects() const
107113
{
@@ -113,8 +119,7 @@ bool
113119
BackwardPass::DoMarkTempObjectVerify() const
114120
{
115121
// only mark temp object on the backward store phase
116-
return (tag == Js::DeadStorePhase) && !PHASE_OFF(Js::MarkTempPhase, this->func) &&
117-
!PHASE_OFF(Js::MarkTempObjectPhase, this->func) && func->DoGlobOpt() && func->GetHasTempObjectProducingInstr();
122+
return (tag == Js::DeadStorePhase) && SatisfyMarkTempObjectsConditions();
118123
}
119124
#endif
120125

@@ -7709,7 +7714,7 @@ BackwardPass::ProcessDef(IR::Opnd * opnd)
77097714
PropertySym *propertySym = sym->AsPropertySym();
77107715
ProcessStackSymUse(propertySym->m_stackSym, isJITOptimizedReg);
77117716

7712-
if(IsCollectionPass())
7717+
if (IsCollectionPass())
77137718
{
77147719
return false;
77157720
}
@@ -7796,7 +7801,7 @@ BackwardPass::ProcessDef(IR::Opnd * opnd)
77967801
}
77977802
}
77987803

7799-
if(IsCollectionPass())
7804+
if (IsCollectionPass())
78007805
{
78017806
return false;
78027807
}
@@ -8247,9 +8252,20 @@ BackwardPass::ProcessBailOnNoProfile(IR::Instr *instr, BasicBlock *block)
82478252
return false;
82488253
}
82498254

8255+
// For generator functions, we don't want to move the BailOutOnNoProfile above
8256+
// certain instructions such as ResumeYield/ResumeYieldStar/CreateInterpreterStackFrameForGenerator
8257+
// This indicates the insertion point for the BailOutOnNoProfile in such cases.
8258+
IR::Instr *insertionPointForGenerator = nullptr;
8259+
82508260
// Don't hoist if we see calls with profile data (recursive calls)
82518261
while(!curInstr->StartsBasicBlock())
82528262
{
8263+
if (curInstr->DontHoistBailOnNoProfileAboveInGeneratorFunction())
8264+
{
8265+
Assert(insertionPointForGenerator == nullptr);
8266+
insertionPointForGenerator = curInstr;
8267+
}
8268+
82538269
// If a function was inlined, it must have had profile info.
82548270
if (curInstr->m_opcode == Js::OpCode::InlineeEnd || curInstr->m_opcode == Js::OpCode::InlineBuiltInEnd || curInstr->m_opcode == Js::OpCode::InlineNonTrackingBuiltInEnd
82558271
|| curInstr->m_opcode == Js::OpCode::InlineeStart || curInstr->m_opcode == Js::OpCode::EndCallForPolymorphicInlinee)
@@ -8320,7 +8336,8 @@ BackwardPass::ProcessBailOnNoProfile(IR::Instr *instr, BasicBlock *block)
83208336
// Now try to move this up the flowgraph to the predecessor blocks
83218337
FOREACH_PREDECESSOR_BLOCK(pred, block)
83228338
{
8323-
bool hoistBailToPred = true;
8339+
// Don't hoist BailOnNoProfile up past blocks containing ResumeYield/ResumeYieldStar
8340+
bool hoistBailToPred = (insertionPointForGenerator == nullptr);
83248341

83258342
if (block->isLoopHeader && pred->loop == block->loop)
83268343
{
@@ -8396,10 +8413,19 @@ BackwardPass::ProcessBailOnNoProfile(IR::Instr *instr, BasicBlock *block)
83968413
#if DBG
83978414
blockHeadInstr->m_noHelperAssert = true;
83988415
#endif
8399-
block->beginsBailOnNoProfile = true;
84008416

84018417
instr->m_func = curInstr->m_func;
8402-
curInstr->InsertAfter(instr);
8418+
8419+
if (insertionPointForGenerator != nullptr)
8420+
{
8421+
insertionPointForGenerator->InsertAfter(instr);
8422+
block->beginsBailOnNoProfile = false;
8423+
}
8424+
else
8425+
{
8426+
curInstr->InsertAfter(instr);
8427+
block->beginsBailOnNoProfile = true;
8428+
}
84038429

84048430
bool setLastInstr = (curInstr == block->GetLastInstr());
84058431
if (setLastInstr)

lib/Backend/BackwardPass.h

+2
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,8 @@ class BackwardPass
118118
bool DoByteCodeUpwardExposedUsed() const;
119119
bool DoCaptureByteCodeUpwardExposedUsed() const;
120120
void DoSetDead(IR::Opnd * opnd, bool isDead) const;
121+
122+
bool SatisfyMarkTempObjectsConditions() const;
121123
bool DoMarkTempObjects() const;
122124
bool DoMarkTempNumbers() const;
123125
bool DoMarkTempNumbersOnTempObjects() const;

lib/Backend/BailOut.cpp

+37-49
Original file line numberDiff line numberDiff line change
@@ -989,6 +989,25 @@ BailOutRecord::RestoreValue(IR::BailOutKind bailOutKind, Js::JavascriptCallStack
989989
value = Js::JavascriptNumber::ToVar(int32Value, scriptContext);
990990
BAILOUT_VERBOSE_TRACE(newInstance->function->GetFunctionBody(), bailOutKind, _u(", value: %10d (ToVar: 0x%p)"), int32Value, value);
991991
}
992+
else if (regSlot == newInstance->function->GetFunctionBody()->GetYieldRegister() && newInstance->function->GetFunctionBody()->IsCoroutine())
993+
{
994+
// This value can only either be:
995+
// 1) the ResumeYieldData. Even though this value is on the stack, it is only used to extract the data as part of Op_ResumeYield.
996+
// So there is no need to box the value.
997+
// 2) the object used as the return value for yield statement. This object is created on the heap, so no need to box either.
998+
Assert(value);
999+
1000+
#if ENABLE_DEBUG_CONFIG_OPTIONS
1001+
if (ThreadContext::IsOnStack(value))
1002+
{
1003+
BAILOUT_VERBOSE_TRACE(newInstance->function->GetFunctionBody(), bailOutKind, _u(", value: 0x%p (ResumeYieldData)"), value);
1004+
}
1005+
else
1006+
{
1007+
BAILOUT_VERBOSE_TRACE(newInstance->function->GetFunctionBody(), bailOutKind, _u(", value: 0x%p (Yield Return Value)"), value);
1008+
}
1009+
#endif
1010+
}
9921011
else
9931012
{
9941013
BAILOUT_VERBOSE_TRACE(newInstance->function->GetFunctionBody(), bailOutKind, _u(", value: 0x%p"), value);
@@ -1507,63 +1526,32 @@ BailOutRecord::BailOutHelper(Js::JavascriptCallStackLayout * layout, Js::ScriptF
15071526
if (executeFunction->IsCoroutine())
15081527
{
15091528
// If the FunctionBody is a generator then this call is being made by one of the three
1510-
// generator resuming methods: next(), throw(), or return(). They all pass the generator
1511-
// object as the first of two arguments. The real user arguments are obtained from the
1512-
// generator object. The second argument is the ResumeYieldData which is only needed
1513-
// when resuming a generator and not needed when yielding from a generator, as is occurring
1514-
// here.
1529+
// generator resuming methods: next(), throw(), or return(). They all pass the generator
1530+
// object as the first of two arguments. The real user arguments are obtained from the
1531+
// generator object. The second argument is the ResumeYieldData which is only needed when
1532+
// resuming a generator and not needed when yielding from a generator, as is occurring here.
15151533
AssertMsg(args.Info.Count == 2, "Generator ScriptFunctions should only be invoked by generator APIs with the pair of arguments they pass in -- the generator object and a ResumeYieldData pointer");
15161534
Js::JavascriptGenerator* generator = Js::VarTo<Js::JavascriptGenerator>(args[0]);
15171535
newInstance = generator->GetFrame();
15181536

1519-
if (newInstance != nullptr)
1520-
{
1521-
// BailOut will recompute OutArg pointers based on BailOutRecord. Reset them back
1522-
// to initial position before that happens so that OP_StartCall calls don't accumulate
1523-
// incorrectly over multiple yield bailouts.
1524-
newInstance->ResetOut();
1525-
1526-
// The debugger relies on comparing stack addresses of frames to decide when a step_out is complete so
1527-
// give the InterpreterStackFrame a legit enough stack address to make this comparison work.
1528-
newInstance->m_stackAddress = reinterpret_cast<DWORD_PTR>(&generator);
1529-
}
1530-
else
1531-
{
1532-
//
1533-
// Allocate a new InterpreterStackFrame instance on the recycler heap.
1534-
// It will live with the JavascriptGenerator object.
1535-
//
1536-
Js::Arguments generatorArgs = generator->GetArguments();
1537-
Js::InterpreterStackFrame::Setup setup(function, generatorArgs, true, isInlinee);
1538-
Assert(setup.GetStackAllocationVarCount() == 0);
1539-
size_t varAllocCount = setup.GetAllocationVarCount();
1540-
size_t varSizeInBytes = varAllocCount * sizeof(Js::Var);
1541-
DWORD_PTR stackAddr = reinterpret_cast<DWORD_PTR>(&generator); // as mentioned above, use any stack address from this frame to ensure correct debugging functionality
1542-
Js::LoopHeader* loopHeaderArray = executeFunction->GetHasAllocatedLoopHeaders() ? executeFunction->GetLoopHeaderArrayPtr() : nullptr;
1543-
1544-
allocation = RecyclerNewPlus(functionScriptContext->GetRecycler(), varSizeInBytes, Js::Var);
1545-
1546-
// Initialize the interpreter stack frame (constants) but not the param, the bailout record will restore the value
1547-
#if DBG
1548-
// Allocate invalidVar on GC instead of stack since this InterpreterStackFrame will out live the current real frame
1549-
Js::Var invalidVar = (Js::RecyclableObject*)RecyclerNewPlusLeaf(functionScriptContext->GetRecycler(), sizeof(Js::RecyclableObject), Js::Var);
1550-
memset(invalidVar, 0xFE, sizeof(Js::RecyclableObject));
1551-
#endif
1537+
// The jit relies on the interpreter stack frame to store various information such as
1538+
// for-in enumerators. Therefore, we always create an interpreter stack frame for generator
1539+
// as part of the resume jump table, at the beginning of the jit'd function, if it doesn't
1540+
// already exist.
1541+
Assert(newInstance != nullptr);
15521542

1553-
newInstance = setup.InitializeAllocation(allocation, nullptr, false, false, loopHeaderArray, stackAddr
1554-
#if DBG
1555-
, invalidVar
1556-
#endif
1557-
);
1543+
// BailOut will recompute OutArg pointers based on BailOutRecord. Reset them back
1544+
// to initial position before that happens so that OP_StartCall calls don't accumulate
1545+
// incorrectly over multiple yield bailouts.
1546+
newInstance->ResetOut();
15581547

1559-
newInstance->m_reader.Create(executeFunction);
1560-
1561-
generator->SetFrame(newInstance, varSizeInBytes);
1562-
}
1548+
// The debugger relies on comparing stack addresses of frames to decide when a step_out is complete so
1549+
// give the InterpreterStackFrame a legit enough stack address to make this comparison work.
1550+
newInstance->m_stackAddress = reinterpret_cast<DWORD_PTR>(&generator);
15631551
}
15641552
else
15651553
{
1566-
Js::InterpreterStackFrame::Setup setup(function, args, true, isInlinee);
1554+
Js::InterpreterStackFrame::Setup setup(function, args, true /* bailedOut */, isInlinee);
15671555
size_t varAllocCount = setup.GetAllocationVarCount();
15681556
size_t stackVarAllocCount = setup.GetStackAllocationVarCount();
15691557
size_t varSizeInBytes;
@@ -2826,7 +2814,7 @@ void BailOutRecord::CheckPreemptiveRejit(Js::FunctionBody* executeFunction, IR::
28262814

28272815
Js::Var BailOutRecord::BailOutForElidedYield(void * framePointer)
28282816
{
2829-
JIT_HELPER_REENTRANT_HEADER(NoSaveRegistersBailOutForElidedYield);
2817+
JIT_HELPER_NOT_REENTRANT_NOLOCK_HEADER(NoSaveRegistersBailOutForElidedYield);
28302818
Js::JavascriptCallStackLayout * const layout = Js::JavascriptCallStackLayout::FromFramePointer(framePointer);
28312819
Js::ScriptFunction ** functionRef = (Js::ScriptFunction **)&layout->functionObject;
28322820
Js::ScriptFunction * function = *functionRef;

lib/Backend/FlowGraph.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -3318,7 +3318,7 @@ FlowGraph::RemoveInstr(IR::Instr *instr, GlobOpt * globOpt)
33183318
if (opcode == Js::OpCode::Yield)
33193319
{
33203320
IR::Instr *instrLabel = newByteCodeUseInstr->m_next;
3321-
while (instrLabel->m_opcode != Js::OpCode::Label)
3321+
while (instrLabel->m_opcode != Js::OpCode::GeneratorBailInLabel)
33223322
{
33233323
instrLabel = instrLabel->m_next;
33243324
}

lib/Backend/Func.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ Func::Func(JitArenaAllocator *alloc, JITTimeWorkItem * workItem,
148148
, m_forInEnumeratorArrayOffset(-1)
149149
, argInsCount(0)
150150
, m_globalObjTypeSpecFldInfoArray(nullptr)
151+
, m_forInEnumeratorForGeneratorSym(nullptr)
151152
#if LOWER_SPLIT_INT64
152153
, m_int64SymPairMap(nullptr)
153154
#endif

lib/Backend/Func.h

+14-1
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,8 @@ class Func
205205
return
206206
!PHASE_OFF(Js::GlobOptPhase, this) && !IsSimpleJit() &&
207207
(!GetTopFunc()->HasTry() || GetTopFunc()->CanOptimizeTryCatch()) &&
208-
(!GetTopFunc()->HasFinally() || GetTopFunc()->CanOptimizeTryFinally());
208+
(!GetTopFunc()->HasFinally() || GetTopFunc()->CanOptimizeTryFinally()) &&
209+
!GetTopFunc()->GetJITFunctionBody()->IsCoroutine();
209210
}
210211

211212
bool DoInline() const
@@ -1061,11 +1062,23 @@ static const unsigned __int64 c_debugFillPattern8 = 0xcececececececece;
10611062
StackSym* m_loopParamSym;
10621063
StackSym* m_bailoutReturnValueSym;
10631064
StackSym* m_hasBailedOutSym;
1065+
StackSym* m_forInEnumeratorForGeneratorSym;
10641066

10651067
public:
10661068
StackSym* tempSymDouble;
10671069
StackSym* tempSymBool;
10681070

1071+
void SetForInEnumeratorSymForGeneratorSym(StackSym* sym)
1072+
{
1073+
Assert(this->m_forInEnumeratorForGeneratorSym == nullptr);
1074+
this->m_forInEnumeratorForGeneratorSym = sym;
1075+
}
1076+
1077+
StackSym* GetForInEnumeratorSymForGeneratorSym() const
1078+
{
1079+
return this->m_forInEnumeratorForGeneratorSym;
1080+
}
1081+
10691082
// StackSyms' corresponding getters/setters
10701083
void SetInlineeFrameStartSym(StackSym* sym)
10711084
{

lib/Backend/GlobOpt.cpp

+9
Original file line numberDiff line numberDiff line change
@@ -4707,6 +4707,14 @@ GlobOpt::ValueNumberDst(IR::Instr **pInstr, Value *src1Val, Value *src2Val)
47074707
case Js::OpCode::Coerce_Str:
47084708
AssertMsg(instr->GetDst()->GetValueType().IsString(),
47094709
"Creator of this instruction should have set the type");
4710+
4711+
// Due to fall through and the fact that Ld_A only takes one source,
4712+
// free the other source here.
4713+
if (instr->GetSrc2() && !(this->IsLoopPrePass() || src1ValueInfo == nullptr || !src1ValueInfo->IsString()))
4714+
{
4715+
instr->FreeSrc2();
4716+
}
4717+
47104718
// fall-through
47114719
case Js::OpCode::Coerce_StrOrRegex:
47124720
// We don't set the ValueType of src1 for Coerce_StrOrRegex, hence skip the ASSERT
@@ -13857,6 +13865,7 @@ GlobOpt::CheckJsArrayKills(IR::Instr *const instr)
1385713865

1385813866
case Js::OpCode::NewScObjectNoCtor:
1385913867
case Js::OpCode::NewScObjectNoCtorFull:
13868+
case Js::OpCode::GenCtorObj:
1386013869
if(doNativeArrayTypeSpec)
1386113870
{
1386213871
// Class/object construction can make something a prototype

lib/Backend/GlobOptBailOut.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -1501,6 +1501,7 @@ GlobOpt::MayNeedBailOnImplicitCall(IR::Instr const * instr, Value const * src1Va
15011501
);
15021502
}
15031503

1504+
case Js::OpCode::GenCtorObj:
15041505
case Js::OpCode::NewScObjectNoCtor:
15051506
if (instr->HasBailOutInfo() && (instr->GetBailOutKind() & ~IR::BailOutKindBits) == IR::BailOutFailedCtorGuardCheck)
15061507
{

lib/Backend/GlobOptFields.cpp

+4-1
Original file line numberDiff line numberDiff line change
@@ -546,6 +546,7 @@ GlobOpt::ProcessFieldKills(IR::Instr *instr, BVSparse<JitArenaAllocator> *bv, bo
546546
case Js::OpCode::InitProto:
547547
case Js::OpCode::NewScObjectNoCtor:
548548
case Js::OpCode::NewScObjectNoCtorFull:
549+
case Js::OpCode::GenCtorObj:
549550
if (inGlobOpt)
550551
{
551552
// Opcodes that make an object into a prototype may break object-header-inlining and final type opt.
@@ -1615,7 +1616,7 @@ GlobOpt::ValueNumberObjectType(IR::Opnd *dstOpnd, IR::Instr *instr)
16151616
return;
16161617
}
16171618

1618-
if (instr->IsNewScObjectInstr())
1619+
if (instr->IsNewScObjectInstr()/*|| instr->m_opcode == Js::OpCode::GenCtorObj*/)
16191620
{
16201621
// If we have a NewScObj* for which we have a valid constructor cache we know what type the created object will have.
16211622
// Let's produce the type value accordingly so we don't insert a type check and bailout in the constructor and
@@ -1627,6 +1628,8 @@ GlobOpt::ValueNumberObjectType(IR::Opnd *dstOpnd, IR::Instr *instr)
16271628
Assert(instr->IsProfiledInstr());
16281629
Assert(instr->GetBailOutKind() == IR::BailOutFailedCtorGuardCheck);
16291630

1631+
// TODO: possibly need to consider GenCtorObj for this path as well as
1632+
// determine if NewScObj was inlined only using GenCtorObj.
16301633
bool isCtorInlined = instr->m_opcode == Js::OpCode::NewScObjectNoCtor;
16311634
JITTimeConstructorCache * ctorCache = instr->m_func->GetConstructorCache(static_cast<Js::ProfileId>(instr->AsProfiledInstr()->u.profileId));
16321635
Assert(ctorCache != nullptr && (isCtorInlined || ctorCache->IsTypeFinal()));

0 commit comments

Comments
 (0)