@@ -67,19 +67,79 @@ static void printJS(Ref ast, T& output) {
6767 output << jser.buffer << std::endl;
6868}
6969
70+
71+ // Traversals
72+
73+ struct TraverseInfo {
74+ TraverseInfo () = default ;
75+ TraverseInfo (Ref node) : node(node) {
76+ assert (node.get ());
77+ if (node->isArray ()) {
78+ for (size_t i = 0 ; i < node->size (); i++) {
79+ maybeAdd (node[i]);
80+ }
81+ } else if (node->isAssign ()) {
82+ auto assign = node->asAssign ();
83+ maybeAdd (assign->target ());
84+ maybeAdd (assign->value ());
85+ } else if (node->isAssignName ()) {
86+ auto assign = node->asAssignName ();
87+ maybeAdd (assign->value ());
88+ } else {
89+ // no children
90+ }
91+ }
92+ Ref node;
93+ bool scanned = false ;
94+ std::vector<Ref> children;
95+
96+ private:
97+ void maybeAdd (Ref child) {
98+ if (child.get ()) {
99+ children.push_back (child);
100+ }
101+ }
102+ };
103+
104+ // Traverse, calling visit after the children
105+ static void traversePrePost (Ref node, std::function<void (Ref)> visitPre, std::function<void (Ref)> visitPost) {
106+ std::vector<TraverseInfo> stack;
107+ stack.push_back (TraverseInfo (node));
108+ while (!stack.empty ()) {
109+ TraverseInfo& back = stack.back ();
110+ if (!back.scanned ) {
111+ back.scanned = true ;
112+ // This is the first time we see this.
113+ visitPre (back.node );
114+ for (auto child : back.children ) {
115+ stack.emplace_back (child);
116+ }
117+ continue ;
118+ }
119+ // Time to post-visit the node itself
120+ auto node = back.node ;
121+ stack.pop_back ();
122+ visitPost (node);
123+ }
124+ }
125+
126+ static void traversePost (Ref node, std::function<void (Ref)> visit) {
127+ traversePrePost (node, [](Ref node) {}, visit);
128+ }
129+
70130static void optimizeJS (Ref ast) {
71131 // helpers
72132
73133 auto isOrZero = [](Ref node) {
74- return node->isArray () && node->size () > 0 && node[0 ] == BINARY && node[1 ] == OR && node[3 ]->isNumber () && node[3 ]->getNumber () == 0 ;
134+ return node->isArray () && ! node->empty () && node[0 ] == BINARY && node[1 ] == OR && node[3 ]->isNumber () && node[3 ]->getNumber () == 0 ;
75135 };
76136
77137 auto isPlus = [](Ref node) {
78- return node->isArray () && node->size () > 0 && node[0 ] == UNARY_PREFIX && node[1 ] == PLUS;
138+ return node->isArray () && ! node->empty () && node[0 ] == UNARY_PREFIX && node[1 ] == PLUS;
79139 };
80140
81141 auto isBitwise = [](Ref node) {
82- if (node->isArray () && node->size () > 0 && node[0 ] == BINARY) {
142+ if (node->isArray () && ! node->empty () && node[0 ] == BINARY) {
83143 auto op = node[1 ];
84144 return op == OR || op == AND || op == XOR || op == RSHIFT || op == TRSHIFT || op == LSHIFT;
85145 }
@@ -88,7 +148,7 @@ static void optimizeJS(Ref ast) {
88148
89149 // x >> 0 => x | 0
90150 traversePost (ast, [](Ref node) {
91- if (node->isArray () && node->size () > 0 && node[0 ] == BINARY && node[1 ] == RSHIFT && node[3 ]->isNumber ()) {
151+ if (node->isArray () && ! node->empty () && node[0 ] == BINARY && node[1 ] == RSHIFT && node[3 ]->isNumber ()) {
92152 if (node[3 ]->getNumber () == 0 ) {
93153 node[1 ]->setString (OR);
94154 }
@@ -124,6 +184,63 @@ static void optimizeJS(Ref ast) {
124184 }
125185 }
126186 });
187+
188+ // Remove unnecessary break/continue labels, when referring to the top level.
189+
190+ // XXX IString invalid("__wasm2js$INVALID_LABEL__");
191+ std::vector<Ref> breakCapturers;
192+ std::vector<Ref> continueCapturers;
193+ std::unordered_map<IString, Ref> labelToValue; // maps the label to the loop/etc.
194+ std::unordered_set<Value*> labelled; // all things with a label on them.
195+ Value INVALID;
196+ traversePrePost (ast, [&](Ref node) {
197+ if (node->isArray () && !node->empty ()) {
198+ if (node[0 ] == LABEL) {
199+ auto label = node[1 ]->getIString ();
200+ labelToValue[label] = node[2 ];
201+ labelled.insert (node[2 ].get ());
202+ } else if (node[0 ] == WHILE || node[0 ] == DO || node[0 ] == FOR) {
203+ breakCapturers.push_back (node);
204+ continueCapturers.push_back (node);
205+ } else if (node[0 ] == cashew::BLOCK) {
206+ if (labelled.count (node.get ())) {
207+ // Cannot break to a block without the label.
208+ breakCapturers.push_back (Ref (&INVALID));
209+ }
210+ } else if (node[0 ] == SWITCH) {
211+ breakCapturers.push_back (node);
212+ }
213+ }
214+ }, [&](Ref node) {
215+ if (node->isArray () && !node->empty ()) {
216+ if (node[0 ] == LABEL) {
217+ auto label = node[1 ]->getIString ();
218+ labelToValue.erase (label);
219+ labelled.erase (node[2 ].get ());
220+ } else if (node[0 ] == WHILE || node[0 ] == DO || node[0 ] == FOR) {
221+ breakCapturers.pop_back ();
222+ continueCapturers.pop_back ();
223+ } else if (node[0 ] == cashew::BLOCK) {
224+ if (labelled.count (node.get ())) {
225+ breakCapturers.pop_back ();
226+ }
227+ } else if (node[0 ] == SWITCH) {
228+ breakCapturers.pop_back ();
229+ } else if (node[0 ] == BREAK || node[0 ] == CONTINUE) {
230+ if (!node[1 ]->isNull ()) {
231+ auto label = node[1 ]->getIString ();
232+ assert (labelToValue.count (label));
233+ auto & capturers = node[0 ] == BREAK ? breakCapturers : continueCapturers;
234+ assert (!capturers.empty ());
235+ if (capturers.back () == labelToValue[label]) {
236+ // Success, the break/continue goes exactly where we would if we
237+ // didn't have the label!
238+ node[1 ]->setNull ();
239+ }
240+ }
241+ }
242+ }
243+ });
127244}
128245
129246static void emitWasm (Module& wasm, Output& output, Wasm2JSBuilder::Flags flags, PassOptions options, Name name) {
0 commit comments