@@ -51,30 +51,6 @@ 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-
7854 // Walk an expression and all its children.
7955 void walk (Expression* ast) {
8056 InternalAnalyzer (*this ).walk (ast);
@@ -95,8 +71,8 @@ class EffectAnalyzer {
9571 walk (func->body );
9672
9773 // Effects of return-called functions will be visible to the caller.
98- if (returnCallEffects ) {
99- mergeIn (*returnCallEffects) ;
74+ if (hasReturnCallThrow ) {
75+ throws_ = true ;
10076 }
10177
10278 // We can ignore branching out of the function body - this can only be
@@ -172,6 +148,16 @@ class EffectAnalyzer {
172148 // or a continuation that is never continued, are examples of that.
173149 bool mayNotReturn = false ;
174150
151+ // Return calls are indistinguishable from normal returns from the perspective
152+ // of their surrounding code, and the return-callee's effects only become
153+ // visible when considering the effects of the whole function containing the
154+ // return call. To model this correctly, stash the callee's effects on the
155+ // side and only merge them in after walking a full function body.
156+ //
157+ // We currently do this stashing only for the throw effect, but in principle
158+ // we could do it for all effects if it made a difference.
159+ bool hasReturnCallThrow = false ;
160+
175161 // Helper functions to check for various effect types
176162
177163 bool accessesLocal () const {
@@ -383,13 +369,6 @@ class EffectAnalyzer {
383369 }
384370 }
385371
386- EffectAnalyzer& getReturnCallEffects () {
387- if (!returnCallEffects) {
388- returnCallEffects = ReturnCallEffectPtr (module );
389- }
390- return *returnCallEffects;
391- }
392-
393372 // the checks above happen after the node's children were processed, in the
394373 // order of execution we must also check for control flow that happens before
395374 // the children, i.e., loops
@@ -512,44 +491,51 @@ class EffectAnalyzer {
512491
513492 if (curr->isReturn ) {
514493 parent.branchesOut = true ;
494+ // When EH is enabled, any call can throw.
495+ if (parent.features .hasExceptionHandling () &&
496+ (!targetEffects || targetEffects->throws ())) {
497+ parent.hasReturnCallThrow = true ;
498+ }
515499 }
516500
517- auto & effects = curr->isReturn ? parent.getReturnCallEffects () : parent;
518-
519501 if (targetEffects) {
520502 // 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 ) {
503+ // that. The one change we may want to make is to remove throws_, if the
504+ // target function throws and we know that will be caught anyhow, the
505+ // same as the code below for the general path. We can always filter out
506+ // throws for return calls because they are already more precisely
507+ // captured by `hasReturnCallThrow` .
508+ if (targetEffects->throws_ && ( parent.tryDepth > 0 || curr->isReturn ) ) {
527509 auto filteredEffects = *targetEffects;
528510 filteredEffects.throws_ = false ;
529- effects .mergeIn (filteredEffects);
511+ parent .mergeIn (filteredEffects);
530512 } else {
531513 // Just merge in all the effects.
532- effects .mergeIn (*targetEffects);
514+ parent .mergeIn (*targetEffects);
533515 }
534516 return ;
535517 }
536518
537- effects.calls = true ;
538- // When EH is enabled, any call can throw.
539- if (parent.features .hasExceptionHandling () &&
540- (parent.tryDepth == 0 || curr->isReturn )) {
541- effects.throws_ = true ;
519+ parent.calls = true ;
520+ // When EH is enabled, any call can throw. Skip this for return calls
521+ // because the throw is already more precisely captured by
522+ // `hasReturnCallThrow`.
523+ if (parent.features .hasExceptionHandling () && parent.tryDepth == 0 &&
524+ !curr->isReturn ) {
525+ parent.throws_ = true ;
542526 }
543527 }
544528 void visitCallIndirect (CallIndirect* curr) {
529+ parent.calls = true ;
545530 if (curr->isReturn ) {
546531 parent.branchesOut = true ;
532+ if (parent.features .hasExceptionHandling ()) {
533+ parent.hasReturnCallThrow = true ;
534+ }
547535 }
548- auto & effects = curr->isReturn ? parent.getReturnCallEffects () : parent;
549- effects.calls = true ;
550536 if (parent.features .hasExceptionHandling () &&
551- (parent.tryDepth == 0 || curr->isReturn )) {
552- effects .throws_ = true ;
537+ (parent.tryDepth == 0 && ! curr->isReturn )) {
538+ parent .throws_ = true ;
553539 }
554540 }
555541 void visitLocalGet (LocalGet* curr) {
@@ -794,6 +780,9 @@ class EffectAnalyzer {
794780 void visitCallRef (CallRef* curr) {
795781 if (curr->isReturn ) {
796782 parent.branchesOut = true ;
783+ if (parent.features .hasExceptionHandling ()) {
784+ parent.hasReturnCallThrow = true ;
785+ }
797786 }
798787 if (curr->target ->type .isNull ()) {
799788 parent.trap = true ;
@@ -804,11 +793,10 @@ class EffectAnalyzer {
804793 parent.implicitTrap = true ;
805794 }
806795
807- auto & effects = curr->isReturn ? parent.getReturnCallEffects () : parent;
808- effects.calls = true ;
796+ parent.calls = true ;
809797 if (parent.features .hasExceptionHandling () &&
810798 (parent.tryDepth == 0 || curr->isReturn )) {
811- effects .throws_ = true ;
799+ parent .throws_ = true ;
812800 }
813801 }
814802 void visitRefTest (RefTest* curr) {}
0 commit comments