@@ -4944,7 +4944,10 @@ LinearScan::GeneratorBailIn::GeneratorBailIn(Func* func, LinearScan* linearScan)
49444944 // so no need to restore.
49454945 this ->initializedRegs .Set (this ->jitFnBody ->GetYieldReg ());
49464946
4947- // The environment is loaded before the resume jump table, no need to restore either.
4947+ // The environment is loaded before the resume jump table. At bail-in point, it can either
4948+ // still be in register or already spilled. If it's in register we're good. If it's been spilled,
4949+ // the register allocator should have inserted compensation code before the bail-in block, so we
4950+ // are still fine there.
49484951 this ->initializedRegs .Set (this ->jitFnBody ->GetEnvReg ());
49494952
49504953 this ->bailInSymbols = JitAnew (this ->func ->m_alloc , SListBase<BailInSymbol>);
@@ -5061,13 +5064,24 @@ void LinearScan::GeneratorBailIn::BuildBailInSymbolList(
50615064{
50625065 this ->bailInSymbols ->Clear (this ->func ->m_alloc );
50635066
5064- // Assume all symbols cannot be restored
5067+ // Make sure that all symbols in `upwardExposedUses` can be restored.
5068+ // The idea is to first assume that we cannot restore any of the symbols.
5069+ // Then we use the information in `byteCodeUpwardExposedUses` and `capturedValues`
5070+ // which contains information about symbols in the bytecode, copy-prop'd symbols, and
5071+ // symbols with constant values. As we go through these lists, we clear the
5072+ // bits in `unrestorableSymbols` to indicate that they can be restored. At the
5073+ // end, the bitvector has to be empty.
5074+
5075+ // Assume all symbols cannot be restored.
50655076 BVSparse<JitArenaAllocator> unrestorableSymbols (this ->func ->m_alloc );
50665077 unrestorableSymbols.Or (&upwardExposedUses);
50675078
50685079 unrestorableSymbols.Minus (&this ->initializedRegs );
50695080
5070- // Symbols in byteCodeUpwardExposedUses are restorable
5081+ // Symbols in byteCodeUpwardExposedUses are restorable.
5082+ // If a symbol is in byteCodeUpwardExposedUses, which means that it is not
5083+ // a constant nor a copy-prop candidate. In such cases, we can simply map
5084+ // the bytecode register directly to its backend id.
50715085 FOREACH_BITSET_IN_SPARSEBV (symId, &byteCodeUpwardExposedUses)
50725086 {
50735087 StackSym* stackSym = this ->func ->m_symTable ->FindStackSym (symId);
@@ -5081,14 +5095,32 @@ void LinearScan::GeneratorBailIn::BuildBailInSymbolList(
50815095 }
50825096 NEXT_BITSET_IN_SPARSEBV;
50835097
5084- // Symbols that were copy-prop'd
5098+ // Symbols that were copy-prop'd.
5099+ // Example:
5100+ // `copyPropSyms` having an entry { s_key : s_value } means that we can use `s_key`
5101+ // in place of `s_value`.
5102+ //
5103+ // 1) if we find `s_value` at this point (after clearing all symbols in
5104+ // `bytecodeUpwardExposedUses`), it means that `s_value` is a backend-only
5105+ // symbol, and that the only way to restore this symbol is through `s_key`.
5106+ // Since in `FillBailOutRecord`, we make sure to restore all symbols that are
5107+ // keys in in `usedCapturedValues`, we can simply use `s_key` to restore the value
5108+ // for `s_value`.
5109+ // 2) if we find `s_key`, then we can just map directly the value due to the above reason.
50855110 FOREACH_SLISTBASE_ENTRY (CopyPropSyms, copyPropSym, &capturedValues.copyPropSyms )
50865111 {
50875112 Sym* key = copyPropSym.Key ();
50885113 Sym* value = copyPropSym.Value ();
5089- if (unrestorableSymbols.TestAndClear (value->m_id ))
5114+
5115+ #if DBG
5116+ if (unrestorableSymbols.Test (value->m_id ) || unrestorableSymbols.Test (key->m_id ))
50905117 {
50915118 Assert (key->IsStackSym () && (key->AsStackSym ()->HasByteCodeRegSlot () || key->AsStackSym ()->IsFromByteCodeConstantTable ()));
5119+ }
5120+ #endif
5121+
5122+ if (unrestorableSymbols.TestAndClear (value->m_id ))
5123+ {
50925124 if (this ->NeedsReloadingSymWhenBailingIn (copyPropSym.Key ()))
50935125 {
50945126 BailInSymbol bailInSym (key->m_id /* fromByteCodeRegSlot */ , value->m_id /* toBackendId */ );
@@ -5097,7 +5129,6 @@ void LinearScan::GeneratorBailIn::BuildBailInSymbolList(
50975129 }
50985130 else if (unrestorableSymbols.TestAndClear (key->m_id ))
50995131 {
5100- Assert (key->IsStackSym () && (key->AsStackSym ()->HasByteCodeRegSlot () || key->AsStackSym ()->IsFromByteCodeConstantTable ()));
51015132 if (this ->NeedsReloadingSymWhenBailingIn (copyPropSym.Key ()))
51025133 {
51035134 BailInSymbol bailInSym (key->m_id /* fromByteCodeRegSlot */ , key->m_id /* toBackendId */ );
@@ -5107,7 +5138,8 @@ void LinearScan::GeneratorBailIn::BuildBailInSymbolList(
51075138 }
51085139 NEXT_SLISTBASE_ENTRY;
51095140
5110- // Used constant values
5141+ // Used constant values.
5142+ // These symbols are can be mapped directly.
51115143 FOREACH_SLISTBASE_ENTRY (ConstantStackSymValue, entry, &capturedValues.constantValues )
51125144 {
51135145 SymID symId = entry.Key ()->m_id ;
@@ -5132,6 +5164,7 @@ void LinearScan::GeneratorBailIn::BuildBailInSymbolList(
51325164 }
51335165 NEXT_SLISTBASE_ENTRY;
51345166
5167+ // Clear all symbols that don't need to be restored.
51355168 FOREACH_BITSET_IN_SPARSEBV_EDITING (symId, &unrestorableSymbols)
51365169 {
51375170 StackSym* stackSym = this ->func ->m_symTable ->FindStackSym (symId);
@@ -5253,8 +5286,8 @@ void LinearScan::GeneratorBailIn::InsertRestoreSymbols(
52535286
52545287bool LinearScan::GeneratorBailIn::NeedsReloadingBackendSymWhenBailingIn (StackSym* sym) const
52555288{
5256- // If we have for-in in the generator, don't need to reload the symbol again as it is done
5257- // during the resume jump table
5289+ // for-in enumerator in generator is loaded as part of the resume jump table.
5290+ // By the same reasoning as `initializedRegs`'s, we don't have to restore this whether or not it's been spilled.
52585291 if (this ->func ->GetForInEnumeratorSymForGeneratorSym () && this ->func ->GetForInEnumeratorSymForGeneratorSym ()->m_id == sym->m_id )
52595292 {
52605293 return false ;
0 commit comments