@@ -249,6 +249,7 @@ BackwardPass::CleanupBackwardPassInfoInFlowGraph()
249249 block->noImplicitCallNativeArrayUses = nullptr ;
250250 block->noImplicitCallJsArrayHeadSegmentSymUses = nullptr ;
251251 block->noImplicitCallArrayLengthSymUses = nullptr ;
252+ block->couldRemoveNegZeroBailoutForDef = nullptr ;
252253
253254 if (block->loop != nullptr )
254255 {
@@ -372,6 +373,7 @@ BackwardPass::MergeSuccBlocksInfo(BasicBlock * block)
372373 BVSparse<JitArenaAllocator> * fieldHoistCandidates = nullptr ;
373374 BVSparse<JitArenaAllocator> * slotDeadStoreCandidates = nullptr ;
374375 BVSparse<JitArenaAllocator> * byteCodeUpwardExposedUsed = nullptr ;
376+ BVSparse<JitArenaAllocator> * couldRemoveNegZeroBailoutForDef = nullptr ;
375377#if DBG
376378 uint byteCodeLocalsCount = func->GetJnFunction ()->GetLocalsCount ();
377379 StackSym ** byteCodeRestoreSyms = nullptr ;
@@ -435,6 +437,10 @@ BackwardPass::MergeSuccBlocksInfo(BasicBlock * block)
435437 {
436438 cloneStrCandidates = JitAnew (this ->globOpt ->alloc , BVSparse<JitArenaAllocator>, this ->globOpt ->alloc );
437439 }
440+ else
441+ {
442+ couldRemoveNegZeroBailoutForDef = JitAnew (this ->tempAlloc , BVSparse<JitArenaAllocator>, this ->tempAlloc );
443+ }
438444 }
439445
440446 bool firstSucc = true ;
@@ -802,6 +808,16 @@ BackwardPass::MergeSuccBlocksInfo(BasicBlock * block)
802808 }
803809#endif
804810 }
811+
812+ if (blockSucc->couldRemoveNegZeroBailoutForDef != nullptr )
813+ {
814+ couldRemoveNegZeroBailoutForDef->And (blockSucc->couldRemoveNegZeroBailoutForDef );
815+ if (deleteData)
816+ {
817+ JitAdelete (this ->tempAlloc , blockSucc->couldRemoveNegZeroBailoutForDef );
818+ blockSucc->couldRemoveNegZeroBailoutForDef = nullptr ;
819+ }
820+ }
805821 }
806822
807823 if (blockSucc->noImplicitCallUses != nullptr )
@@ -1002,6 +1018,7 @@ BackwardPass::MergeSuccBlocksInfo(BasicBlock * block)
10021018 Assert (block->noImplicitCallNativeArrayUses == nullptr );
10031019 Assert (block->noImplicitCallJsArrayHeadSegmentSymUses == nullptr );
10041020 Assert (block->noImplicitCallArrayLengthSymUses == nullptr );
1021+ Assert (block->couldRemoveNegZeroBailoutForDef == nullptr );
10051022 }
10061023 else
10071024 {
@@ -1053,6 +1070,7 @@ BackwardPass::MergeSuccBlocksInfo(BasicBlock * block)
10531070 block->noImplicitCallNativeArrayUses = noImplicitCallNativeArrayUses;
10541071 block->noImplicitCallJsArrayHeadSegmentSymUses = noImplicitCallJsArrayHeadSegmentSymUses;
10551072 block->noImplicitCallArrayLengthSymUses = noImplicitCallArrayLengthSymUses;
1073+ block->couldRemoveNegZeroBailoutForDef = couldRemoveNegZeroBailoutForDef;
10561074}
10571075
10581076ObjTypeGuardBucket
@@ -1220,6 +1238,11 @@ BackwardPass::DeleteBlockData(BasicBlock * block)
12201238 block->byteCodeRestoreSyms = nullptr ;
12211239#endif
12221240 }
1241+ if (block->couldRemoveNegZeroBailoutForDef != nullptr )
1242+ {
1243+ JitAdelete (this ->tempAlloc , block->couldRemoveNegZeroBailoutForDef );
1244+ block->couldRemoveNegZeroBailoutForDef = nullptr ;
1245+ }
12231246}
12241247
12251248void
@@ -4933,6 +4956,26 @@ BackwardPass::TrackBitWiseOrNumberOp(IR::Instr *const instr)
49334956 }
49344957}
49354958
4959+ void
4960+ BackwardPass::RemoveNegativeZeroBailout (IR::Instr* instr)
4961+ {
4962+ Assert (instr->HasBailOutInfo () && (instr->GetBailOutKind () & IR::BailOutOnNegativeZero));
4963+ IR::BailOutKind bailOutKind = instr->GetBailOutKind ();
4964+ bailOutKind = bailOutKind & ~IR::BailOutOnNegativeZero;
4965+ if (bailOutKind)
4966+ {
4967+ instr->SetBailOutKind (bailOutKind);
4968+ }
4969+ else
4970+ {
4971+ instr->ClearBailOutInfo ();
4972+ if (preOpBailOutInstrToProcess == instr)
4973+ {
4974+ preOpBailOutInstrToProcess = nullptr ;
4975+ }
4976+ }
4977+ }
4978+
49364979void
49374980BackwardPass::TrackIntUsage (IR::Instr *const instr)
49384981{
@@ -4967,29 +5010,48 @@ BackwardPass::TrackIntUsage(IR::Instr *const instr)
49675010 if (dstSym)
49685011 {
49695012 // For a dst where the def is in this block, transfer the current info into the instruction
4970- if (trackNegativeZero && negativeZeroDoesNotMatterBySymId-> TestAndClear (dstSym-> m_id ) )
5013+ if (trackNegativeZero)
49715014 {
4972- instr->ignoreNegativeZero = true ;
4973- if (tag == Js::DeadStorePhase && instr->HasBailOutInfo ())
5015+ if (negativeZeroDoesNotMatterBySymId->Test (dstSym->m_id ))
49745016 {
4975- IR::BailOutKind bailOutKind = instr->GetBailOutKind ();
4976- if (bailOutKind & IR::BailOutOnNegativeZero)
5017+ instr->ignoreNegativeZero = true ;
5018+ }
5019+
5020+ if (tag == Js::DeadStorePhase)
5021+ {
5022+ if (negativeZeroDoesNotMatterBySymId->TestAndClear (dstSym->m_id ))
49775023 {
4978- bailOutKind -= IR::BailOutOnNegativeZero;
4979- if (bailOutKind)
5024+ if (instr->HasBailOutInfo ())
49805025 {
4981- instr->SetBailOutKind (bailOutKind);
5026+ IR::BailOutKind bailOutKind = instr->GetBailOutKind ();
5027+ if (bailOutKind & IR::BailOutOnNegativeZero)
5028+ {
5029+ RemoveNegativeZeroBailout (instr);
5030+ }
49825031 }
4983- else
5032+ }
5033+ else
5034+ {
5035+ if (instr->HasBailOutInfo ())
49845036 {
4985- instr->ClearBailOutInfo ();
4986- if (preOpBailOutInstrToProcess == instr)
5037+ if (instr->GetBailOutKind () & IR::BailOutOnNegativeZero)
49875038 {
4988- preOpBailOutInstrToProcess = nullptr ;
5039+ if (this ->currentBlock ->couldRemoveNegZeroBailoutForDef ->TestAndClear (dstSym->m_id ))
5040+ {
5041+ RemoveNegativeZeroBailout (instr);
5042+ }
49895043 }
5044+ // This instruction could potentially bail out. Hence, we cannot reliably remove negative zero
5045+ // bailouts upstream. If we did, and the operation actually produced a -0, and this instruction
5046+ // bailed out, we'd use +0 instead of -0 in the interpreter.
5047+ this ->currentBlock ->couldRemoveNegZeroBailoutForDef ->ClearAll ();
49905048 }
49915049 }
49925050 }
5051+ else
5052+ {
5053+ this ->negativeZeroDoesNotMatterBySymId ->Clear (dstSym->m_id );
5054+ }
49935055 }
49945056 if (trackIntOverflow)
49955057 {
@@ -5096,38 +5158,41 @@ BackwardPass::TrackIntUsage(IR::Instr *const instr)
50965158 break ;
50975159
50985160 case Js::OpCode::Add_I4:
5161+ {
50995162 Assert (dstSym);
51005163 Assert (instr->GetSrc1 ());
51015164 Assert (instr->GetSrc1 ()->IsRegOpnd () || instr->GetSrc1 ()->IsIntConstOpnd ());
51025165 Assert (instr->GetSrc2 ());
51035166 Assert (instr->GetSrc2 ()->IsRegOpnd () || instr->GetSrc2 ()->IsIntConstOpnd ());
51045167
5105- if (instr->ignoreNegativeZero ||
5106- ! (instr->GetSrc1 ()->IsRegOpnd () && instr->GetSrc1 ()->AsRegOpnd ()->m_wasNegativeZeroPreventedByBailout ) ||
5107- !( instr->GetSrc2 ()->IsRegOpnd () && instr->GetSrc2 ()->AsRegOpnd ()->m_wasNegativeZeroPreventedByBailout ) )
5168+ if (instr->ignoreNegativeZero ||
5169+ (instr->GetSrc1 ()->IsIntConstOpnd () && instr->GetSrc1 ()->AsIntConstOpnd ()->GetValue () != 0 ) ||
5170+ instr->GetSrc2 ()->IsIntConstOpnd () && instr->GetSrc2 ()->AsIntConstOpnd ()->GetValue () != 0 )
51085171 {
5109- // -0 does not matter for dst, or this instruction does not generate -0 since one of the srcs is not -0
5110- // (regardless of -0 bailout checks)
51115172 SetNegativeZeroDoesNotMatterIfLastUse (instr->GetSrc1 ());
51125173 SetNegativeZeroDoesNotMatterIfLastUse (instr->GetSrc2 ());
51135174 break ;
51145175 }
5115-
5176+
51165177 // -0 + -0 == -0. As long as one src is guaranteed to not be -0, -0 does not matter for the other src. Pick a
51175178 // src for which to ignore negative zero, based on which sym is last-use. If both syms are last-use, src2 is
51185179 // picked arbitrarily.
5119- if (instr->GetSrc2 ()->IsRegOpnd () &&
5120- !currentBlock->upwardExposedUses ->Test (instr->GetSrc2 ()->AsRegOpnd ()->m_sym ->m_id ))
5121- {
5122- SetNegativeZeroDoesNotMatterIfLastUse (instr->GetSrc2 ());
5123- SetNegativeZeroMatters (instr->GetSrc1 ());
5124- }
5125- else
5180+ SetNegativeZeroMatters (instr->GetSrc1 ());
5181+ SetNegativeZeroMatters (instr->GetSrc2 ());
5182+ if (tag == Js::DeadStorePhase)
51265183 {
5127- SetNegativeZeroDoesNotMatterIfLastUse (instr->GetSrc1 ());
5128- SetNegativeZeroMatters (instr->GetSrc2 ());
5184+ if (instr->GetSrc2 ()->IsRegOpnd () &&
5185+ !currentBlock->upwardExposedUses ->Test (instr->GetSrc2 ()->AsRegOpnd ()->m_sym ->m_id ))
5186+ {
5187+ SetCouldRemoveNegZeroBailoutForDefIfLastUse (instr->GetSrc2 ());
5188+ }
5189+ else
5190+ {
5191+ SetCouldRemoveNegZeroBailoutForDefIfLastUse (instr->GetSrc1 ());
5192+ }
51295193 }
51305194 break ;
5195+ }
51315196
51325197 case Js::OpCode::Add_A:
51335198 Assert (dstSym);
@@ -5149,26 +5214,26 @@ BackwardPass::TrackIntUsage(IR::Instr *const instr)
51495214 break ;
51505215
51515216 case Js::OpCode::Sub_I4:
5217+ {
51525218 Assert (dstSym);
51535219 Assert (instr->GetSrc1 ());
51545220 Assert (instr->GetSrc1 ()->IsRegOpnd () || instr->GetSrc1 ()->IsIntConstOpnd ());
51555221 Assert (instr->GetSrc2 ());
51565222 Assert (instr->GetSrc2 ()->IsRegOpnd () || instr->GetSrc2 ()->IsIntConstOpnd ());
51575223
5158- if (instr->ignoreNegativeZero ||
5159- ! (instr->GetSrc1 ()->IsRegOpnd () && instr->GetSrc1 ()->AsRegOpnd ()->m_wasNegativeZeroPreventedByBailout ) ||
5224+ if (instr->ignoreNegativeZero ||
5225+ (instr->GetSrc1 ()->IsIntConstOpnd () && instr->GetSrc1 ()->AsIntConstOpnd ()->GetValue () != 0 ) ||
51605226 instr->GetSrc2 ()->IsIntConstOpnd () && instr->GetSrc2 ()->AsIntConstOpnd ()->GetValue () != 0 )
51615227 {
5162- // At least one of the following is true:
5163- // - -0 does not matter for dst
5164- // - Src1 is not -0 (regardless of -0 bailout checks), and so this instruction cannot generate -0
5165- // - Src2 is a nonzero int constant, and so this instruction cannot generate -0
51665228 SetNegativeZeroDoesNotMatterIfLastUse (instr->GetSrc1 ());
51675229 SetNegativeZeroDoesNotMatterIfLastUse (instr->GetSrc2 ());
5168- break ;
51695230 }
5170- goto NegativeZero_Sub_Default;
5171-
5231+ else
5232+ {
5233+ goto NegativeZero_Sub_Default;
5234+ }
5235+ break ;
5236+ }
51725237 case Js::OpCode::Sub_A:
51735238 Assert (dstSym);
51745239 Assert (instr->GetSrc1 ());
@@ -5197,7 +5262,11 @@ BackwardPass::TrackIntUsage(IR::Instr *const instr)
51975262 NegativeZero_Sub_Default:
51985263 // -0 - 0 == -0. As long as src1 is guaranteed to not be -0, -0 does not matter for src2.
51995264 SetNegativeZeroMatters (instr->GetSrc1 ());
5200- SetNegativeZeroDoesNotMatterIfLastUse (instr->GetSrc2 ());
5265+ SetNegativeZeroMatters (instr->GetSrc2 ());
5266+ if (this ->tag == Js::DeadStorePhase)
5267+ {
5268+ SetCouldRemoveNegZeroBailoutForDefIfLastUse (instr->GetSrc2 ());
5269+ }
52015270 break ;
52025271
52035272 case Js::OpCode::BrEq_I4:
@@ -5628,6 +5697,16 @@ BackwardPass::SetNegativeZeroMatters(IR::Opnd *const opnd)
56285697 }
56295698}
56305699
5700+ void
5701+ BackwardPass::SetCouldRemoveNegZeroBailoutForDefIfLastUse (IR::Opnd *const opnd)
5702+ {
5703+ StackSym * stackSym = IR::RegOpnd::TryGetStackSym (opnd);
5704+ if (stackSym && !this ->currentBlock ->upwardExposedUses ->Test (stackSym->m_id ))
5705+ {
5706+ this ->currentBlock ->couldRemoveNegZeroBailoutForDef ->Set (stackSym->m_id );
5707+ }
5708+ }
5709+
56315710void
56325711BackwardPass::SetIntOverflowDoesNotMatterIfLastUse (IR::Opnd *const opnd)
56335712{
0 commit comments