@@ -80,6 +80,25 @@ std::atomic<int> gcUnderstoodUsedStores(0);
8080std::atomic<int > gcHaltedWithNoLoads (0 );
8181std::atomic<int > gcHaltedWithLoads (0 );
8282
83+ enum class HaltReason {
84+ NoHalt,
85+ Call,
86+ Throw,
87+ Trap,
88+ BranchOut,
89+ Interaction,
90+ Exit,
91+ Unknown
92+ };
93+
94+ std::atomic<int > gcHaltCall (0 );
95+ std::atomic<int > gcHaltThrow (0 );
96+ std::atomic<int > gcHaltTrap (0 );
97+ std::atomic<int > gcHaltBranchOut (0 );
98+ std::atomic<int > gcHaltInteraction (0 );
99+ std::atomic<int > gcHaltExit (0 );
100+ std::atomic<int > gcHaltUnknown (0 );
101+
83102// A variation of LocalGraph that can also compare expressions to check for
84103// their equivalence. Basic LocalGraph just looks at locals, while this class
85104// goes further and looks at the structure of the expression, taking into
@@ -146,15 +165,27 @@ struct Logic {
146165 //
147166 // The default behavior here considers all calls to be barriers. Subclasses
148167 // can use whole-program information to do better.
149- bool isBarrier (Expression* curr, const ShallowEffectAnalyzer& currEffects) {
168+ HaltReason isBarrier (Expression* curr,
169+ const ShallowEffectAnalyzer& currEffects) {
150170 // TODO: ignore throws of an exception that is definitely caught in this
151171 // function
152172 // TODO: if we add an "ignore after trap mode" (to assume nothing happens
153173 // after a trap) then we could stop assuming any trap can lead to
154174 // access of global data, likely greatly reducing the number of
155175 // barriers.
156- return currEffects.calls || currEffects.throws () || currEffects.trap ||
157- currEffects.branchesOut ;
176+ if (currEffects.calls )
177+ return HaltReason::Call;
178+ if (currEffects.throws ())
179+ return HaltReason::Throw;
180+ if (currEffects.trap && !currEffects.trapsNeverHappen )
181+ return HaltReason::Trap;
182+ if (currEffects.branchesOut )
183+ return HaltReason::BranchOut;
184+
185+ return HaltReason::NoHalt;
186+ // return currEffects.calls || currEffects.throws() || (currEffects.trap &&
187+ // !currEffects.trapsNeverHappen) ||
188+ // currEffects.branchesOut;
158189 };
159190
160191 // Returns whether an expression may interact with loads and stores in
@@ -431,6 +462,10 @@ struct GCLogic : public ComparingLogic {
431462 return mayAlias (load, store);
432463 }
433464
465+ std::cerr << " mayInteractWith\n " ;
466+ curr->dump ();
467+ return false ;
468+
434469 // This is not a load or a store that we recognize; check for generic heap
435470 // interactions.
436471 return currEffects.readsMutableStruct || currEffects.writesStruct ;
@@ -474,6 +509,14 @@ struct DeadStoreCFG
474509 int localHaltedWithNoLoads = 0 ;
475510 int localHaltedWithLoads = 0 ;
476511
512+ int localHaltCall = 0 ;
513+ int localHaltThrow = 0 ;
514+ int localHaltTrap = 0 ;
515+ int localHaltBranchOut = 0 ;
516+ int localHaltInteraction = 0 ;
517+ int localHaltExit = 0 ;
518+ int localHaltUnknown = 0 ;
519+
477520 DeadStoreCFG (Module& wasm, Function* func, PassOptions& passOptions)
478521 : func(func), passOptions(passOptions), logic(func, passOptions, wasm) {
479522 this ->setModule (&wasm);
@@ -491,13 +534,8 @@ struct DeadStoreCFG
491534 // Add all relevant things to the list of exprs for the current basic block.
492535 if (isStore (curr) || isLoad (curr)) {
493536 exprs.push_back (curr);
494- } else if (logic.isBarrier (curr, currEffects)) {
495- // Barriers can be very common, so as a minor optimization avoid having
496- // consecutive ones; a single barrier will stop us.
497- if (exprs.empty () || exprs.back () != barrier) {
498- exprs.push_back (barrier);
499- }
500- } else if (logic.mayInteract (curr, currEffects)) {
537+ } else if (logic.isBarrier (curr, currEffects) != HaltReason::NoHalt ||
538+ logic.mayInteract (curr, currEffects)) {
501539 exprs.push_back (curr);
502540 }
503541 }
@@ -558,11 +596,37 @@ struct DeadStoreCFG
558596
559597 // When we find something we cannot optimize, stop flowing and mark the
560598 // store as unoptimizable.
561- auto halt = [&]() {
599+ auto halt = [&](HaltReason reason ) {
562600 work.clear ();
563601 understoodStores.erase (store);
564602 if (definiteLoads == 0 ) {
565603 localHaltedWithNoLoads++;
604+ switch (reason) {
605+ case HaltReason::NoHalt:
606+ assert (false && " impossible" );
607+ break ;
608+ case HaltReason::Call:
609+ localHaltCall++;
610+ break ;
611+ case HaltReason::Throw:
612+ localHaltThrow++;
613+ break ;
614+ case HaltReason::Trap:
615+ localHaltTrap++;
616+ break ;
617+ case HaltReason::BranchOut:
618+ localHaltBranchOut++;
619+ break ;
620+ case HaltReason::Interaction:
621+ localHaltInteraction++;
622+ break ;
623+ case HaltReason::Exit:
624+ localHaltExit++;
625+ break ;
626+ case HaltReason::Unknown:
627+ localHaltUnknown++;
628+ break ;
629+ }
566630 } else {
567631 localHaltedWithLoads++;
568632 }
@@ -574,14 +638,21 @@ struct DeadStoreCFG
574638 for (size_t i = from; i < block->contents .exprs .size (); i++) {
575639 auto * curr = block->contents .exprs [i];
576640
577- if (curr == barrier) {
578- halt ();
579- return ;
580- }
581-
582641 ShallowEffectAnalyzer currEffects (
583642 passOptions, *this ->getModule (), curr);
584643
644+ if (auto reason = logic.isBarrier (curr, currEffects);
645+ reason != HaltReason::NoHalt) {
646+ halt (reason);
647+ return ;
648+ // if (currEffects.calls) halt(HaltReason::Call);
649+ // else if (currEffects.throws()) halt(HaltReason::Throw);
650+ // else if (currEffects.trap && !currEffects.trapsNeverHappen)
651+ // halt(HaltReason::Trap); else if (currEffects.branchesOut)
652+ // halt(HaltReason::BranchOut); else halt(HaltReason::Unknown);
653+ // return;
654+ }
655+
585656 if (logic.isLoadFrom (curr, currEffects, store)) {
586657 // We found a definite load of this store, note it.
587658 definiteLoads++;
@@ -592,7 +663,7 @@ struct DeadStoreCFG
592663 return ;
593664 } else if (logic.mayInteractWith (curr, currEffects, store)) {
594665 // Stop: we cannot fully analyze the uses of this store.
595- halt ();
666+ halt (HaltReason::Interaction );
596667 return ;
597668 }
598669 }
@@ -605,7 +676,7 @@ struct DeadStoreCFG
605676 if (block == this ->exit ) {
606677 // Any value flowing out can be reached by global code outside the
607678 // function after we leave.
608- halt ();
679+ halt (HaltReason::Exit );
609680 }
610681 };
611682
@@ -665,6 +736,14 @@ struct DeadStoreCFG
665736 gcUnderstoodUsedStores += localUnderstoodUsedStores;
666737 gcHaltedWithNoLoads += localHaltedWithNoLoads;
667738 gcHaltedWithLoads += localHaltedWithLoads;
739+
740+ gcHaltCall += localHaltCall;
741+ gcHaltThrow += localHaltThrow;
742+ gcHaltTrap += localHaltTrap;
743+ gcHaltBranchOut += localHaltBranchOut;
744+ gcHaltInteraction += localHaltInteraction;
745+ gcHaltExit += localHaltExit;
746+ gcHaltUnknown += localHaltUnknown;
668747 }
669748
670749 if (replacer.replacements .empty ()) {
@@ -718,6 +797,14 @@ struct LocalDeadStoreEliminationRunner : public Pass {
718797 gcHaltedWithNoLoads = 0 ;
719798 gcHaltedWithLoads = 0 ;
720799
800+ gcHaltCall = 0 ;
801+ gcHaltThrow = 0 ;
802+ gcHaltTrap = 0 ;
803+ gcHaltBranchOut = 0 ;
804+ gcHaltInteraction = 0 ;
805+ gcHaltExit = 0 ;
806+ gcHaltUnknown = 0 ;
807+
721808 // Run the actual DSE in parallel. We use a nested PassRunner to take
722809 // advantage of its built-in function parallelism logic.
723810 PassRunner passRunner (module , getPassOptions ());
@@ -733,6 +820,20 @@ struct LocalDeadStoreEliminationRunner : public Pass {
733820 << " \n " ;
734821 std::cerr << " Halted w/ NO loads (Likely Dead): "
735822 << gcHaltedWithNoLoads.load () << " \n " ;
823+ if (gcHaltedWithNoLoads > 0 ) {
824+ std::cerr << " Reason: Call: " << gcHaltCall.load () << " \n " ;
825+ std::cerr << " Reason: Throw: " << gcHaltThrow.load () << " \n " ;
826+ std::cerr << " Reason: Trap: " << gcHaltTrap.load () << " \n " ;
827+ std::cerr << " Reason: BranchOut: " << gcHaltBranchOut.load ()
828+ << " \n " ;
829+ std::cerr << " Reason: Interaction: " << gcHaltInteraction.load ()
830+ << " \n " ;
831+ std::cerr << " Reason: Exit: " << gcHaltExit.load () << " \n " ;
832+ if (gcHaltUnknown > 0 ) {
833+ std::cerr << " Reason: Unknown: " << gcHaltUnknown.load ()
834+ << " \n " ;
835+ }
836+ }
736837 std::cerr << " Halted w/ loads (Partially Used): "
737838 << gcHaltedWithLoads.load () << " \n " ;
738839 std::cerr << " =========================\n\n " ;
0 commit comments