Skip to content

Commit e88a86a

Browse files
committed
Simplify to only stash the throws effect
1 parent 485c3e7 commit e88a86a

File tree

1 file changed

+43
-55
lines changed

1 file changed

+43
-55
lines changed

src/ir/effects.h

Lines changed: 43 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)