@@ -154,6 +154,15 @@ struct BreakValueDropper : public ControlFlowWalker<BreakValueDropper> {
154154 }
155155};
156156
157+ static bool hasUnreachableChild (Block* block) {
158+ for (auto * test : block->list ) {
159+ if (test->type == unreachable) {
160+ return true ;
161+ }
162+ }
163+ return false ;
164+ }
165+
157166// core block optimizer routine
158167static void optimizeBlock (Block* curr, Module* module , PassOptions& passOptions) {
159168 bool more = true ;
@@ -168,6 +177,11 @@ static void optimizeBlock(Block* curr, Module* module, PassOptions& passOptions)
168177 if (drop) {
169178 child = drop->value ->dynCast <Block>();
170179 if (child) {
180+ if (hasUnreachableChild (child)) {
181+ // don't move around unreachable code, as it can change types
182+ // dce should have been run anyhow
183+ continue ;
184+ }
171185 if (child->name .is ()) {
172186 Expression* expression = child;
173187 // check if it's ok to remove the value from all breaks to us
@@ -200,15 +214,6 @@ static void optimizeBlock(Block* curr, Module* module, PassOptions& passOptions)
200214 }
201215 if (!child) continue ;
202216 if (child->name .is ()) continue ; // named blocks can have breaks to them (and certainly do, if we ran RemoveUnusedNames and RemoveUnusedBrs)
203- if (child->type == unreachable) {
204- // an unreachable block can have a concrete final element (which is never reached)
205- if (!child->list .empty ()) {
206- if (isConcreteWasmType (child->list .back ()->type )) {
207- // just remove it
208- child->list .pop_back ();
209- }
210- }
211- }
212217 ExpressionList merged (module ->allocator );
213218 for (size_t j = 0 ; j < i; j++) {
214219 merged.push_back (curr->list [j]);
@@ -219,6 +224,16 @@ static void optimizeBlock(Block* curr, Module* module, PassOptions& passOptions)
219224 for (size_t j = i + 1 ; j < curr->list .size (); j++) {
220225 merged.push_back (curr->list [j]);
221226 }
227+ // if we merged a concrete element in the middle, drop it
228+ if (!merged.empty ()) {
229+ auto * last = merged.back ();
230+ for (auto *& item : merged) {
231+ if (item != last && isConcreteWasmType (item->type )) {
232+ Builder builder (*module );
233+ item = builder.makeDrop (item);
234+ }
235+ }
236+ }
222237 curr->list .swap (merged);
223238 more = true ;
224239 changed = true ;
@@ -241,6 +256,23 @@ struct MergeBlocks : public WalkerPass<PostWalker<MergeBlocks>> {
241256 optimizeBlock (curr, getModule (), getPassOptions ());
242257 }
243258
259+ // given
260+ // (curr
261+ // (block=child
262+ // (..more..)
263+ // (back)
264+ // )
265+ // (..other..children..)
266+ // )
267+ // if child is a block, we can move this around to
268+ // (block
269+ // (..more..)
270+ // (curr
271+ // (back)
272+ // (..other..children..)
273+ // )
274+ // )
275+ // at which point the block is on the outside and potentially mergeable with an outer block
244276 Block* optimize (Expression* curr, Expression*& child, Block* outer = nullptr , Expression** dependency1 = nullptr , Expression** dependency2 = nullptr ) {
245277 if (!child) return outer;
246278 if ((dependency1 && *dependency1) || (dependency2 && *dependency2)) {
@@ -251,18 +283,29 @@ struct MergeBlocks : public WalkerPass<PostWalker<MergeBlocks>> {
251283 }
252284 if (auto * block = child->dynCast <Block>()) {
253285 if (!block->name .is () && block->list .size () >= 2 ) {
254- child = block->list .back ();
255- // we modified child (which is a reference to a pointer), which modifies curr, which might change its type
256- // (e.g. (drop (block (result i32) .. (unreachable)))
257- // the child was a block of i32, and is being replaced with an unreachable, so the
258- // parent will likely need to be unreachable too
259- auto oldType = curr->type ;
260- ReFinalize ().walk (curr);
286+ // if we move around unreachable code, type changes could occur. avoid that, as
287+ // anyhow it means we should have run dce before getting here
288+ if (curr->type == none && hasUnreachableChild (block)) {
289+ // moving the block to the outside would replace a none with an unreachable
290+ return outer;
291+ }
292+ auto * back = block->list .back ();
293+ if (back->type == unreachable) {
294+ // curr is not reachable, dce could remove it; don't try anything fancy
295+ // here
296+ return outer;
297+ }
298+ // we are going to replace the block with the final element, so they should
299+ // be identically typed
300+ if (block->type != back->type ) {
301+ return outer;
302+ }
303+ child = back;
261304 if (outer == nullptr ) {
262305 // reuse the block, move it out
263306 block->list .back () = curr;
264307 // we want the block outside to have the same type as curr had
265- block->finalize (oldType );
308+ block->finalize (curr-> type );
266309 replaceCurrent (block);
267310 return block;
268311 } else {
0 commit comments