Skip to content

Conversation

@sunfishcode
Copy link
Member

This is the idea described in WebAssembly/spec#163 , making tableswitch trap if the index is out of bounds rather than having an explicit default. This makes tableswitch simpler and conceptually closer to being "just a jump table", and is consistent with other constructs in WebAssembly (trap on out of bounds, instead of having default behavior).

The main downside is that in cases when an application emits a range check branch for itself, and the JIT uses a branch to implement the bounds check, we'd get two branches checking the range rather than one. Clever JITs could optimize this away, but simple JITs might not.

Despite my general interest in simple JITs, the factor that I think tips the balance in this case is that tableswitch is very infrequent relative to most other instructions, so a simple JIT aiming to be as fast as possible should be able to do extra work to recognize the common patterns where it can elide its own branch without a significant overall slowdown.

@titzer
Copy link

titzer commented Nov 26, 2015

Would it be OK to wait for some usage data before implementing this change?

I find the disadvantage of the redundant branch to be pretty significant. In the case of the application introducing its own bounds check before the switch (to avoid the trap), the compiler analysis required to eliminate the redundant second branch amounts to an ad-hoc range analysis. It's far easier for an application to put in an unreachable statement in the default case than the other way around.

@ghost
Copy link

ghost commented Nov 26, 2015

Even a consumer compiler that can derive the range might be better off if the default is kept rather than needing to trap because if the derived range is small but larger than the table length then it could extent the jump table size filling elements with the default and avoiding an extra branch to the default.

@jfbastien
Copy link
Member

@titzer's default+unreachable suggestion seems to achieve @sunfishcode's goals and be simpler: It's still a dumb table, but users don't emit an extra branch before. It's an ad-hoc convention, but IMO a nicer one than having a preceding branch.

@sunfishcode
Copy link
Member Author

@titzer Yes, I'm ok taking some time to properly explore this.

@jfbastien That wouldn't satisfy my stated goals of making it "simpler and conceptually closer to being "just a jump table"", because when the default isn't unreachable it's still an auxiliary branch tacked on the side, or "consistent with other constructs in WebAssembly (trap on out of bounds, instead of having default behavior)", because it's a default behavior.

@AndrewScheidecker
Copy link

Either way, tableswitch codegen needs to support branching to a default target or trap if the range of the index can't be inferred. In all cases where the code generator could infer the range of the index to eliminate a branch-to-trap, it could do the same to eliminate a branch-to-default; I don't see how trapping semantics would allow better code to be generated.

A default target directly exposes this implicit branch-on-range of the tableswitch, and is practically useful, so I think we should keep it.

You could apply the same argument to traps on memory access out-of-bounds, and indirect call index out-of-bounds, but for memory access at least, it would make implementations that use hardware memory protection to catch out-of-bounds memory accesses harder.

@sunfishcode
Copy link
Member Author

Memory access out-of-bounds and indirect call index out-of-bounds both do trap, so making tableswitch trap instead of having a default would be consistent with them. And, I am contemplating a future where we use hardware mechanisms to bounds-check tableswitch.

I'll grant that with reasonably clever optimizers, there's very little effective difference between tableswitch trapping on out-of-bounds, and a tableswitch with a default+unreachable. We can optimize both into the same code.

When there is a default and it isn't unreachable though, tableswitch becomes conceptually more than "just a jump table"; it's a jump table with an auxiliary branch tacked on. From my perspective, the question is whether language purity in this area is worth the inconvenience of optimizing away the redundant branch in today's implementations. My present intuition favors it, but I'm open to exploring the space more before committing to it.

@sunfishcode
Copy link
Member Author

There's currently no consensus for this change.

@jfbastien jfbastien deleted the no_default branch February 27, 2016 00:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants