11
2+ #include " ir/properties.h"
23#include " pass.h"
34#include " wasm-traversal.h"
45#include " wasm.h"
6+ #include < stack>
57#include < vector>
68
79namespace wasm {
810
911namespace {
1012
1113struct Finder : TryDepthWalker<Finder> {
14+ explicit Finder (const PassOptions& passOptions)
15+ : TryDepthWalker<Finder>(), passOptions(passOptions) {}
16+ const PassOptions& passOptions;
1217 std::vector<Call*> tailCalls;
1318 std::vector<CallIndirect*> tailCallIndirects;
14- void visitFunction (Function* curr) { checkTailCall (curr->body ); }
15- void visitReturn (Return* curr) { checkTailCall (curr->value ); }
16-
17- private:
18- void checkTailCall (Expression* expr) {
19- if (expr == nullptr ) {
19+ void visitFunction (Function* curr) {
20+ if (passOptions.shrinkLevel > 0 && passOptions.optimizeLevel == 0 ) {
21+ // When we more force on the binary size, add return_call will increase
22+ // the code size.
2023 return ;
2124 }
25+ checkTailCall (curr->body );
26+ }
27+ void visitReturn (Return* curr) {
2228 if (tryDepth > 0 ) {
23- // We are in a try block, so we cannot optimize tail calls.
24- return ;
25- }
26- if (auto * call = expr->dynCast <Call>()) {
27- if (!call->isReturn && call->type == getFunction ()->getResults ()) {
28- tailCalls.push_back (call);
29- }
30- return ;
31- }
32- if (auto * call = expr->dynCast <CallIndirect>()) {
33- if (!call->isReturn && call->type == getFunction ()->getResults ()) {
34- tailCallIndirects.push_back (call);
35- }
36- return ;
37- }
38- if (auto * block = expr->dynCast <Block>()) {
39- return checkTailCall (block->list );
40- }
41- if (auto * ifElse = expr->dynCast <If>()) {
42- checkTailCall (ifElse->ifTrue );
43- checkTailCall (ifElse->ifFalse );
29+ // (return (call ...)) is not equal to (return_call ...) in try block
4430 return ;
4531 }
32+ checkTailCall (curr->value );
4633 }
47- void checkTailCall (ExpressionList const & exprs) {
48- if (exprs.empty ()) {
49- return ;
34+
35+ private:
36+ void checkTailCall (Expression* const curr) {
37+ std::stack<Expression*> workList{};
38+ workList.push (curr);
39+ while (!workList.empty ()) {
40+ Expression* const target = workList.top ();
41+ workList.pop ();
42+ if (auto * call = target->dynCast <Call>()) {
43+ if (!call->isReturn && call->type == getFunction ()->getResults ()) {
44+ tailCalls.push_back (call);
45+ }
46+ } else if (auto * call = target->dynCast <CallIndirect>()) {
47+ if (!call->isReturn && call->type == getFunction ()->getResults ()) {
48+ tailCallIndirects.push_back (call);
49+ }
50+ } else if (auto * ifElse = target->dynCast <If>()) {
51+ workList.push (ifElse->ifTrue );
52+ workList.push (ifElse->ifFalse );
53+ } else {
54+ Expression* const next = Properties::getImmediateFallthrough (
55+ target, passOptions, *getModule ());
56+ if (next != target) {
57+ workList.push (next);
58+ }
59+ }
5060 }
51- checkTailCall (exprs.back ());
52- return ;
5361 }
5462};
5563
@@ -64,7 +72,7 @@ struct TailCallOptimizer : public Pass {
6472 if (!module ->features .hasTailCall ()) {
6573 return ;
6674 }
67- Finder finder{};
75+ Finder finder{getPassOptions () };
6876 finder.walkFunctionInModule (function, module );
6977 for (Call* call : finder.tailCalls ) {
7078 if (!call->isReturn ) {
0 commit comments