This is an archive of the discontinued LLVM Phabricator instance.

[WebAssembly] Lower llvm.debugtrap properly
ClosedPublic

Authored by tlively on Jun 2 2020, 11:10 PM.

Details

Summary

Unlike normal traps, debug traps are allowed to return and can have
additional instructions in the same basic block. Without explicit
backend support for debug traps, they are lowered in ISel as normal
traps. Since normal traps are lowered in the WebAssembly backend to
the UNREACHABLE instruction, which is a terminator, using debug traps
could lead to invalid MBBs when there are additional instructions
after the trap. This patch fixes the issue by lowering debug traps to
a new version of the UNREACHABLE instruction, DEBUG_UNREACHABLE, that
is not a terminator.

An alternative approach would have been to make UNREACHABLE not a
terminator, but that breaks a large number of tests. In particular, it
would require removing the traps inserted after noreturn calls to
@llvm.wasm.throw because otherwise the terminator throw would be
followed by a non-terminator UNREACHABLE and we would be back to
having invalid MBBs. Overall the approach in this patch seems simpler.

Diff Detail

Event Timeline

tlively created this revision.Jun 2 2020, 11:10 PM
Herald added a project: Restricted Project. · View Herald TranscriptJun 2 2020, 11:10 PM

I'm not very familiar with how wasm debugger works, but do they use unreachable for breakpoints? Binaryen assumes unreachable is really unreachable and does all sorts of optimizations including DCE and everything, so if unreachable is actually used in debugger, that seems risky..?

I'm not very familiar with how wasm debugger works, but do they use unreachable for breakpoints? Binaryen assumes unreachable is really unreachable and does all sorts of optimizations including DCE and everything, so if unreachable is actually used in debugger, that seems risky..?

No, WebAssembly does not any sort of debugger instruction (although JS does). On other targets without dedicated debugger instructions, debugtrap is lowered to a normal trap, so that is what we are doing here. In fact, there is target-independent code to automatically change debug traps to normal traps before ISel when debugtrap is not supported. So this is the correct lowering to do, given that we don't have a debugger instruction. The reason the target-independent mechanism doesn't work for us is that it assumes the trap will lower to a non-terminator instruction.

Binaryen doesn't assume an unreachable is actually unreachable, the way C compilers do, does it? (and remove code before the unreachable)? I think it just knows that instructions that come after it are unreachable and removes those. I think it's ok once it's lowered to wasm unreachable; it really won't be able to return (and so Binaryen can do DCE), but that seems like a legit behavior for @llvm.debugtrap according to the langref.

Binaryen doesn't assume an unreachable is actually unreachable, the way C compilers do, does it? (and remove code before the unreachable)? I think it just knows that instructions that come after it are unreachable and removes those. I think it's ok once it's lowered to wasm unreachable; it really won't be able to return (and so Binaryen can do DCE), but that seems like a legit behavior for @llvm.debugtrap according to the langref.

Agreed.

Binaryen doesn't assume an unreachable is actually unreachable, the way C compilers do, does it? (and remove code before the unreachable)? I think it just knows that instructions that come after it are unreachable and removes those. I think it's ok once it's lowered to wasm unreachable; it really won't be able to return (and so Binaryen can do DCE), but that seems like a legit behavior for @llvm.debugtrap according to the langref.

Yeah you're right. I was confused; Binaryen's DCE only removes the code after unreachable is unreachable. But doesn't debugtrap instruction expect the control flow to return? That's why I was wondering if we can translate debugtrap to unreachable.

tlively added a comment.EditedJun 4 2020, 11:07 AM

Yeah you're right. I was confused; Binaryen's DCE only removes the code after unreachable is unreachable. But doesn't debugtrap instruction expect the control flow to return? That's why I was wondering if we can translate debugtrap to unreachable.

debugtrap allows control flow to return. It is ok if it doesn't return or if it is lowered to something that can't return.

aheejin accepted this revision.Jun 4 2020, 11:40 AM

I see. Thanks for the explanation.

This revision is now accepted and ready to land.Jun 4 2020, 11:40 AM
This revision was automatically updated to reflect the committed changes.