Conversation
When an instruction has a type immediate that we cannot print because the expression it is supposed to come from has unreachable or null type, we instead print an unreachable block with a comment saying what instruction we failed to print. We previously handled this via different code paths for type immediates that come from child expressions that type immediates that come from the printed expression's own return type. Unify these code paths in the printer by improving `Properties::hasUnwritableTypeImmediate` to handle both cases. Also fix the printing of `ShallowExpression` to check for unwritable type immediates first to avoid assertion failures.
The recent change (#8608) that reduced the number of scratch locals introduced by IRBuilder also inadvertently introduced a bug. That commit changed the conditions under which we package multiple expressions into a block to be popped together. We previously did this whenever there was a get of a scratch local to hoist a value to the top of the stack. After #8608, we started using blocks whenever there were multiple expressions hoisted. These conditions are usually identical, but we missed the case where there are multiple expressions and also no `get` because the value is unreachable. The introduction of a block where there was none before invalidated our logic for determining whether or not to pop back past an unreachable expression based on whether the expressions underneath it would satisfy the type constraints of the parent. Simplify that logic by calculating the stack size at which we must avoid popping past an unreachable rather than calculating the index of the unreachable itself. This makes the logic work correctly whether or not the unreachable will be popped as part of a block. That in turn lets us remove an extra unreachable we were conservatively pushing onto the stack when "hoisting" unreachable values. Add tests for the cases where we can and cannot pop past an unreachable that is followed by a none-typed expression. The former case previously parsed incorrectly and the latter case is now parsed without introducing extra unreachable instructions.
| if (!cast->desc->type.isRef()) { | ||
| return true; | ||
| } | ||
| if (!cast->desc->type.getHeapType().getDescribedType()) { |
There was a problem hiding this comment.
Ah, this is from an outdated version of #8616. Will update this branch.
But to answer the question, yes, this would fail validation, but this code is used from the Printer, so needs to work correctly for invalid IR as well.
| i32.const 0 | ||
| unreachable | ||
| nop | ||
| struct.set $pair 0 |
There was a problem hiding this comment.
Can we not stop parsing entirely at the unreachable? We can skip unreachable code since we don't try to precisely roundtrip anyhow. In fact I believe we used to do that in the past?
There was a problem hiding this comment.
We can't skip parsing instructions after unreachables if we want to be able to write tests containing expressions with unreachable children (which we definitely do). We might have skipped parsing such instructions in the binary parser in the past, but now both text and binary parsers use IRBuilder.
|
🤦 this didn't include the commit the remove the stale code from #8616... |
The recent change (#8608) that reduced the number of scratch locals introduced by IRBuilder also inadvertently introduced a bug. That commit changed the conditions under which we package multiple expressions into a block to be popped together. We previously did this whenever there was a get of a scratch local to hoist a value to the top of the stack. After #8608, we started using blocks whenever there were multiple expressions hoisted. These conditions are usually identical, but we missed the case where there are multiple expressions and also no
getbecause the value is unreachable. The introduction of a block where there was none before invalidated our logic for determining whether or not to pop back past an unreachable expression based on whether the expressions underneath it would satisfy the type constraints of the parent.Simplify that logic by calculating the stack size at which we must avoid popping past an unreachable rather than calculating the index of the unreachable itself. This makes the logic work correctly whether or not the unreachable will be popped as part of a block. That in turn lets us remove an extra unreachable we were conservatively pushing onto the stack when "hoisting" unreachable values.
Add tests for the cases where we can and cannot pop past an unreachable that is followed by a none-typed expression. The former case previously parsed incorrectly and the latter case is now parsed without introducing extra unreachable instructions.