@@ -51,6 +51,30 @@ class EffectAnalyzer {
5151 Module& module ;
5252 FeatureSet features;
5353
54+ // Return calls appear no different than normal returns to local surrounding
55+ // code, but the effects of the return-called functions are visible to
56+ // callers. Collect effects of return-called functions here and merge them in
57+ // only when analyzing an entire function.
58+ //
59+ // Go through all this boilerplate to define copy constructors for this one
60+ // field so we don't have to explicitly define a copy constructor for the
61+ // entire EffectAnalyzer.
62+ struct ReturnCallEffectPtr : std::unique_ptr<EffectAnalyzer> {
63+ ReturnCallEffectPtr () = default ;
64+ ReturnCallEffectPtr (Module& module )
65+ : std::unique_ptr<EffectAnalyzer>(
66+ std::make_unique<EffectAnalyzer>(PassOptions{}, module )) {}
67+ ReturnCallEffectPtr (const ReturnCallEffectPtr& other)
68+ : ReturnCallEffectPtr() {}
69+ ReturnCallEffectPtr (ReturnCallEffectPtr&& other) = default ;
70+ ReturnCallEffectPtr& operator =(const ReturnCallEffectPtr& other) {
71+ *((std::unique_ptr<EffectAnalyzer>*)this ) = nullptr ;
72+ return *this ;
73+ }
74+ ReturnCallEffectPtr& operator =(ReturnCallEffectPtr&& other) = default ;
75+ };
76+ ReturnCallEffectPtr returnCallEffects;
77+
5478 // Walk an expression and all its children.
5579 void walk (Expression* ast) {
5680 InternalAnalyzer (*this ).walk (ast);
@@ -70,6 +94,11 @@ class EffectAnalyzer {
7094 void walk (Function* func) {
7195 walk (func->body );
7296
97+ // Effects of return-called functions will be visible to the caller.
98+ if (returnCallEffects) {
99+ mergeIn (*returnCallEffects);
100+ }
101+
73102 // We can ignore branching out of the function body - this can only be
74103 // a return, and that is only noticeable in the function, not outside.
75104 branchesOut = false ;
@@ -354,6 +383,13 @@ class EffectAnalyzer {
354383 }
355384 }
356385
386+ EffectAnalyzer& getReturnCallEffects () {
387+ if (!returnCallEffects) {
388+ returnCallEffects = ReturnCallEffectPtr (module );
389+ }
390+ return *returnCallEffects;
391+ }
392+
357393 // the checks above happen after the node's children were processed, in the
358394 // order of execution we must also check for control flow that happens before
359395 // the children, i.e., loops
@@ -466,44 +502,55 @@ class EffectAnalyzer {
466502 return ;
467503 }
468504
505+ const EffectAnalyzer* targetEffects = nullptr ;
506+ if (parent.funcEffectsMap ) {
507+ auto iter = parent.funcEffectsMap ->find (curr->target );
508+ if (iter != parent.funcEffectsMap ->end ()) {
509+ targetEffects = &iter->second ;
510+ }
511+ }
512+
469513 if (curr->isReturn ) {
470514 parent.branchesOut = true ;
471515 }
472516
473- if (parent.funcEffectsMap ) {
474- auto iter = parent.funcEffectsMap ->find (curr->target );
475- if (iter != parent.funcEffectsMap ->end ()) {
476- // We have effect information for this call target, and can just use
477- // that. The one change we may want to make is to remove throws_, if
478- // the target function throws and we know that will be caught anyhow,
479- // the same as the code below for the general path.
480- const auto & targetEffects = iter->second ;
481- if (targetEffects.throws_ && parent.tryDepth > 0 ) {
482- auto filteredEffects = targetEffects;
483- filteredEffects.throws_ = false ;
484- parent.mergeIn (filteredEffects);
485- } else {
486- // Just merge in all the effects.
487- parent.mergeIn (targetEffects);
488- }
489- return ;
517+ auto & effects = curr->isReturn ? parent.getReturnCallEffects () : parent;
518+
519+ if (targetEffects) {
520+ // We have effect information for this call target, and can just use
521+ // that. The one change we may want to make is to remove throws_, if
522+ // the target function throws and we know that will be caught anyhow,
523+ // the same as the code below for the general path. We can't filter
524+ // out throws on return calls because they will return out of the
525+ // handlers before making the call.
526+ if (targetEffects->throws_ && parent.tryDepth > 0 && !curr->isReturn ) {
527+ auto filteredEffects = *targetEffects;
528+ filteredEffects.throws_ = false ;
529+ effects.mergeIn (filteredEffects);
530+ } else {
531+ // Just merge in all the effects.
532+ effects.mergeIn (*targetEffects);
490533 }
534+ return ;
491535 }
492536
493- parent .calls = true ;
537+ effects .calls = true ;
494538 // When EH is enabled, any call can throw.
495- if (parent.features .hasExceptionHandling () && parent.tryDepth == 0 ) {
496- parent.throws_ = true ;
539+ if (parent.features .hasExceptionHandling () &&
540+ (parent.tryDepth == 0 || curr->isReturn )) {
541+ effects.throws_ = true ;
497542 }
498543 }
499544 void visitCallIndirect (CallIndirect* curr) {
500- parent.calls = true ;
501- if (parent.features .hasExceptionHandling () && parent.tryDepth == 0 ) {
502- parent.throws_ = true ;
503- }
504545 if (curr->isReturn ) {
505546 parent.branchesOut = true ;
506547 }
548+ auto & effects = curr->isReturn ? parent.getReturnCallEffects () : parent;
549+ effects.calls = true ;
550+ if (parent.features .hasExceptionHandling () &&
551+ (parent.tryDepth == 0 || curr->isReturn )) {
552+ effects.throws_ = true ;
553+ }
507554 }
508555 void visitLocalGet (LocalGet* curr) {
509556 parent.localsRead .insert (curr->index );
@@ -745,21 +792,24 @@ class EffectAnalyzer {
745792 }
746793 }
747794 void visitCallRef (CallRef* curr) {
795+ if (curr->isReturn ) {
796+ parent.branchesOut = true ;
797+ }
748798 if (curr->target ->type .isNull ()) {
749799 parent.trap = true ;
750800 return ;
751801 }
752- parent.calls = true ;
753- if (parent.features .hasExceptionHandling () && parent.tryDepth == 0 ) {
754- parent.throws_ = true ;
755- }
756- if (curr->isReturn ) {
757- parent.branchesOut = true ;
758- }
759802 // traps when the call target is null
760803 if (curr->target ->type .isNullable ()) {
761804 parent.implicitTrap = true ;
762805 }
806+
807+ auto & effects = curr->isReturn ? parent.getReturnCallEffects () : parent;
808+ effects.calls = true ;
809+ if (parent.features .hasExceptionHandling () &&
810+ (parent.tryDepth == 0 || curr->isReturn )) {
811+ effects.throws_ = true ;
812+ }
763813 }
764814 void visitRefTest (RefTest* curr) {}
765815 void visitRefCast (RefCast* curr) {
0 commit comments