@@ -4427,6 +4427,309 @@ BasicBlock::CleanUpValueMaps()
4427
4427
}
4428
4428
}
4429
4429
4430
+ bool IsLegalOpcodeForPathDepBrFold (IR::Instr *instr)
4431
+ {
4432
+ if (!instr->IsRealInstr ())
4433
+ {
4434
+ return true ;
4435
+ }
4436
+ switch (instr->m_opcode )
4437
+ {
4438
+ case Js::OpCode::Ld_A:
4439
+ case Js::OpCode::Ld_I4:
4440
+ case Js::OpCode::ByteCodeUses:
4441
+ return true ;
4442
+ }
4443
+ #if DBG
4444
+ if (PHASE_TRACE (Js::PathDepBranchFoldingPhase, instr->m_func ) && Js::Configuration::Global.flags .Verbose )
4445
+ {
4446
+ Output::Print (_u (" Skipping PathDependentBranchFolding due to: " ));
4447
+ instr->Dump ();
4448
+ }
4449
+ #endif
4450
+ return false ;
4451
+ }
4452
+
4453
+ IR::LabelInstr* BasicBlock::CanProveConditionalBranch (IR::BranchInstr *branch, GlobOpt* globOpt, GlobHashTable * localSymToValueMap)
4454
+ {
4455
+ if (!branch->GetSrc1 () || !branch->GetSrc1 ()->GetStackSym ())
4456
+ {
4457
+ return nullptr ;
4458
+ }
4459
+
4460
+ Value *src1Val = nullptr , *src2Val = nullptr ;
4461
+ Js::Var src1Var = nullptr , src2Var = nullptr ;
4462
+
4463
+ auto FindValueInLocalThenGlobalValueTable = [&](StackSym *sym) -> Value *
4464
+ {
4465
+ Value ** srcValPtr = localSymToValueMap->Get (sym);
4466
+ Value * srcVal = nullptr ;
4467
+ if (!srcValPtr)
4468
+ {
4469
+ srcVal = this ->globOptData .FindValue (sym);
4470
+ }
4471
+ else
4472
+ {
4473
+ srcVal = *srcValPtr;
4474
+ }
4475
+ if (!srcVal)
4476
+ {
4477
+ return nullptr ;
4478
+ }
4479
+ return srcVal;
4480
+ };
4481
+
4482
+ src1Val = FindValueInLocalThenGlobalValueTable (branch->GetSrc1 ()->GetStackSym ());
4483
+ if (!src1Val)
4484
+ {
4485
+ return nullptr ;
4486
+ }
4487
+ src1Var = globOpt->GetConstantVar (branch->GetSrc1 (), src1Val);
4488
+
4489
+ if (branch->GetSrc2 () != nullptr )
4490
+ {
4491
+ if (branch->GetSrc2 ()->GetStackSym ())
4492
+ {
4493
+ src2Val = FindValueInLocalThenGlobalValueTable (branch->GetSrc2 ()->GetStackSym ());
4494
+ }
4495
+ if (!src2Val)
4496
+ {
4497
+ return nullptr ;
4498
+ }
4499
+ src2Var = globOpt->GetConstantVar (branch->GetSrc2 (), src2Val);
4500
+ }
4501
+
4502
+ bool provenTrue;
4503
+ if (!globOpt->CanProveConditionalBranch (branch, src1Val, src2Val, src1Var, src2Var, &provenTrue))
4504
+ {
4505
+ return nullptr ;
4506
+ }
4507
+
4508
+ IR::LabelInstr * newTarget = provenTrue ? branch->GetTarget () : branch->GetNextRealInstrOrLabel ()->AsLabelInstr ();
4509
+
4510
+ if (PHASE_TRACE (Js::PathDepBranchFoldingPhase, this ->func ))
4511
+ {
4512
+ Output::Print (_u (" TRACE PathDependentBranchFolding: " ));
4513
+ Output::Print (_u (" Can prove retarget of branch in Block %d from Block %d to Block %d in func %s\n " ),
4514
+ this ->GetBlockNum (),
4515
+ this ->GetLastInstr ()->IsBranchInstr () ? this ->GetLastInstr ()->AsBranchInstr ()->GetTarget ()->GetBasicBlock ()->GetBlockNum () : this ->GetNext ()->GetBlockNum (),
4516
+ newTarget->GetBasicBlock ()->GetBlockNum (),
4517
+ this ->func ->GetJITFunctionBody ()->GetDisplayName ());
4518
+ Output::Flush ();
4519
+ }
4520
+ return newTarget;
4521
+ }
4522
+
4523
+ bool
4524
+ BasicBlock::CheckLegalityAndFoldPathDepBranches (GlobOpt* globOpt)
4525
+ {
4526
+ IR::LabelInstr * lastBranchTarget = nullptr ;
4527
+
4528
+ // For the block we start processing from, we maintain a valueTable, we go over the instrs in the block
4529
+ // If we find a Ld_A, we find if the src1 already has a value in the local valueTable, if not we use the value from the pred's valueTable merge
4530
+ // For other instrs, we assign null value
4531
+ GlobHashTable * localSymToValueMap = GlobHashTable::New (globOpt->alloc , 8 );
4532
+
4533
+ FOREACH_INSTR_IN_BLOCK (instr, this )
4534
+ {
4535
+ if (OpCodeAttr::HasDeadFallThrough (instr->m_opcode ))
4536
+ {
4537
+ return false ;
4538
+ }
4539
+ if (instr->GetDst ())
4540
+ {
4541
+ if (instr->GetDst ()->GetStackSym ())
4542
+ {
4543
+ if (instr->m_opcode == Js::OpCode::Ld_A)
4544
+ {
4545
+ Value **localValue = localSymToValueMap->FindOrInsertNew (instr->GetDst ()->GetSym ());
4546
+ if (instr->GetSrc1 ()->GetSym ())
4547
+ {
4548
+ Value **localSrc1Value = localSymToValueMap->Get (instr->GetSrc1 ()->GetSym ());
4549
+ *localValue = localSrc1Value == nullptr ? this ->globOptData .FindValue (instr->GetSrc1 ()->GetSym ()) : *localSrc1Value;
4550
+ }
4551
+ }
4552
+ else
4553
+ {
4554
+ // complex instr, can't track value, insert nullptr
4555
+ Value **localValue = localSymToValueMap->FindOrInsertNew (instr->GetDst ()->GetSym ());
4556
+ *localValue = nullptr ;
4557
+ }
4558
+ }
4559
+ }
4560
+ } NEXT_INSTR_IN_BLOCK;
4561
+
4562
+ IR::Instr * instr = this ->GetLastInstr ();
4563
+ IR::LabelInstr * currentLabel = nullptr ;
4564
+ /* We start from the current instruction and go on scanning for legality, as long as it is legal to skip an instruction, skip.
4565
+ * When we see an unconditional branch, start scanning from the branchTarget
4566
+ * When we see a conditional branch, check if we can prove the branch target, if we can, adjust the flowgraph, and continue in the direction of the proven target
4567
+ * We stop, when we no longer can skip instructions, either due to legality check or a non provable conditional branch
4568
+ */
4569
+ while (instr)
4570
+ {
4571
+ if (!instr->IsBranchInstr () && !instr->IsLabelInstr () && !IsLegalOpcodeForPathDepBrFold (instr))
4572
+ {
4573
+ return lastBranchTarget != nullptr ;
4574
+ }
4575
+ if (OpCodeAttr::HasDeadFallThrough (instr->m_opcode )) // BailOnNoProfile etc
4576
+ {
4577
+ return lastBranchTarget != nullptr ;
4578
+ }
4579
+ if (instr->GetDst ())
4580
+ {
4581
+ if (!instr->GetDst ()->IsRegOpnd ()) // complex dstOpnd, stop.
4582
+ {
4583
+ return lastBranchTarget != nullptr ;
4584
+ }
4585
+ IR::RegOpnd *dst = instr->GetDst ()->AsRegOpnd ();
4586
+ if (currentLabel)
4587
+ {
4588
+ BasicBlock * currentBlock = currentLabel->GetBasicBlock ();
4589
+ // If the dstOpnd is defined and used in the same block, it is legal, but if the successor blocks use it, stop.
4590
+ if (currentBlock->successorBlockUses && currentBlock->successorBlockUses ->Test (dst->GetStackSym ()->m_id ))
4591
+ {
4592
+ return lastBranchTarget != nullptr ;
4593
+ }
4594
+ }
4595
+ if (instr->GetSrc1 ())
4596
+ {
4597
+ // If the src of the load instruction is produced in the currentBlock, its value will not be available in the valuetable, check in local valuetable
4598
+ Value **localValue = localSymToValueMap->FindOrInsertNew (instr->GetDst ()->GetSym ());
4599
+ if (instr->GetSrc1 ()->GetSym ())
4600
+ {
4601
+ Value **localSrc1Value = localSymToValueMap->Get (instr->GetSrc1 ()->GetSym ());
4602
+ *localValue = localSrc1Value == nullptr ? this ->globOptData .FindValue (instr->GetSrc1 ()->GetSym ()) : *localSrc1Value;
4603
+ }
4604
+ else
4605
+ {
4606
+ ValueType src1Value = instr->GetSrc1 ()->GetValueType ();
4607
+ if (src1Value.IsUndefined ())
4608
+ {
4609
+ *localValue = globOpt->NewGenericValue (ValueType::Undefined, instr->GetDst ());
4610
+ }
4611
+ // MGTODO : handle other constants
4612
+ else
4613
+ {
4614
+ return lastBranchTarget != nullptr ;
4615
+ }
4616
+ }
4617
+ }
4618
+ }
4619
+ if (instr->IsLabelInstr ())
4620
+ {
4621
+ if (instr->AsLabelInstr ()->m_isLoopTop )
4622
+ {
4623
+ // don't cross over to loops
4624
+ return lastBranchTarget != nullptr ;
4625
+ }
4626
+ currentLabel = instr->AsLabelInstr ();
4627
+ }
4628
+ if (instr->IsBranchInstr ())
4629
+ {
4630
+ IR::BranchInstr* branch = instr->AsBranchInstr ();
4631
+ IR::LabelInstr* branchTarget = nullptr ;
4632
+
4633
+ if (branch->IsUnconditional ())
4634
+ {
4635
+ branchTarget = branch->GetTarget ();
4636
+ if (!branchTarget)
4637
+ {
4638
+ return lastBranchTarget != nullptr ;
4639
+ }
4640
+ if (branchTarget->m_isLoopTop )
4641
+ {
4642
+ return lastBranchTarget != nullptr ;
4643
+ }
4644
+ }
4645
+ else
4646
+ {
4647
+ if (branch->GetTarget ()->m_isLoopTop )
4648
+ {
4649
+ return lastBranchTarget != nullptr ;
4650
+ }
4651
+ branchTarget = CanProveConditionalBranch (branch, globOpt, localSymToValueMap);
4652
+ if (!branchTarget)
4653
+ {
4654
+ return lastBranchTarget != nullptr ;
4655
+ }
4656
+ }
4657
+
4658
+ if (this ->GetLastInstr ()->IsBranchInstr () && (this ->GetLastInstr ()->AsBranchInstr ()->GetTarget () == branchTarget))
4659
+ {
4660
+ // happens on the first block we start from only
4661
+ lastBranchTarget = branchTarget;
4662
+ instr = lastBranchTarget;
4663
+ continue ;
4664
+ }
4665
+ if (branchTarget != this ->GetLastInstr ()->GetNextRealInstrOrLabel ())
4666
+ {
4667
+ IR::Instr* lastInstr = this ->GetLastInstr ();
4668
+ IR::BranchInstr * newBranch = IR::BranchInstr::New (Js::OpCode::Br, branchTarget, branchTarget->m_func );
4669
+ newBranch->SetByteCodeOffset (lastInstr);
4670
+ if (lastInstr->IsBranchInstr ())
4671
+ {
4672
+ globOpt->ConvertToByteCodeUses (lastInstr);
4673
+ }
4674
+ this ->GetLastInstr ()->InsertAfter (newBranch);
4675
+ globOpt->func ->m_fg ->AddEdge (this , branchTarget->GetBasicBlock ());
4676
+ this ->IncrementDataUseCount ();
4677
+ }
4678
+ else
4679
+ {
4680
+ // If the new target is a fall through label, delete the branch
4681
+ globOpt->ConvertToByteCodeUses (this ->GetLastInstr ());
4682
+ }
4683
+ // We are adding an unconditional branch, go over all the current successors and remove the ones that are dead now
4684
+ FOREACH_SUCCESSOR_BLOCK_EDITING (blockSucc, this , iter)
4685
+ {
4686
+ if (branchTarget != blockSucc->GetFirstInstr ()->AsLabelInstr ())
4687
+ {
4688
+ // Change the old succ edge to dead
4689
+ this ->RemoveDeadSucc (blockSucc, globOpt->func ->m_fg );
4690
+ if (this ->GetDataUseCount () > 0 )
4691
+ {
4692
+ this ->DecrementDataUseCount ();
4693
+ }
4694
+ if (blockSucc->GetPredList ()->Count () == 0 )
4695
+ {
4696
+ this ->func ->m_fg ->RemoveBlock (blockSucc, globOpt);
4697
+ }
4698
+ }
4699
+ }NEXT_SUCCESSOR_BLOCK_EDITING;
4700
+ lastBranchTarget = branchTarget;
4701
+ instr = lastBranchTarget;
4702
+ }
4703
+ else
4704
+ {
4705
+ instr = instr->m_next ;
4706
+ }
4707
+ }
4708
+
4709
+ return lastBranchTarget != nullptr ;
4710
+ }
4711
+
4712
+ bool
4713
+ BasicBlock::PathDepBranchFolding (GlobOpt* globOpt)
4714
+ {
4715
+ if (PHASE_OFF (Js::PathDepBranchFoldingPhase, this ->func ))
4716
+ {
4717
+ return false ;
4718
+ }
4719
+ if (globOpt->IsLoopPrePass ())
4720
+ {
4721
+ return false ;
4722
+ }
4723
+ if (func->IsOOPJIT () || !CONFIG_FLAG (OOPJITMissingOpts))
4724
+ {
4725
+ return false ;
4726
+ }
4727
+
4728
+ CheckLegalityAndFoldPathDepBranches (globOpt);
4729
+
4730
+ return true ;
4731
+ }
4732
+
4430
4733
void
4431
4734
BasicBlock::MergePredBlocksValueMaps (GlobOpt* globOpt)
4432
4735
{
0 commit comments