Support exception handling on windows#6419
Conversation
|
Wat? You are glad you accomplished it?? You are ****ing amazing, Chris! |
|
My hero! |
|
@RX14 💯 💯 💯 💯 💯 💯 💯 |
|
I used a compiler with this patch to compile my local branch with Windows library bindings. It didn't break anything, but the specs don't contain any expectations about exceptions, because that didn't work until now. But I'll work on that. I also compiled crinja's spec suite. It's one of the largest Crystal projects I know and there are quite a number of exception raised and rescued. And it worked fine 👍 Not all specs passed (which was to be expected), but none fails because of an error in exception handling. |
|
@straight-shoota thats fantastic. I wouldn't be surprised if there's still quite a few edge cases in here though. The key hint is usually an "invalid instruction" segfault (llvm adds a |
bcardiff
left a comment
There was a problem hiding this comment.
This is great @RX14. I am still OOF one more week, but it was too much temptation to go through this PR 😅 .
There are some questions, doubts and ideas on how to avoid loosing track of the decisions and knowledge needed to understand this change.
| end | ||
|
|
||
| delegate ptr2int, int2ptr, and, or, not, call, bit_cast, | ||
| def call(func, name : String = "") |
There was a problem hiding this comment.
Is there a reason why the handling of the funclet operand bundle can't be done in codegen_call_or_invoke? That would leave the plain delegation in place and avoid duplication.
There was a problem hiding this comment.
Because there's too many places where codegen generates a call outside of codegen_call_or_invoke. I originally had it in codegen_call_or_invoke but I didnt feel like duplicating funclet code in the 50 other places that generate calls (think codegen for constants, there's way more though)
|
|
||
| if return_phi = context.return_phi | ||
| return_phi.add @last, node_type | ||
| elsif catch_pad = @catch_pad |
There was a problem hiding this comment.
So, returns inside a catch_pad section needs to be handled different. I fail to see that the current specs handle return with values. I neither find specs added on this topic, so maybe is something to add in the suite. WDYT?
There was a problem hiding this comment.
Yeah, I didn't really look at the spec suite since it's impossible to run the compiler spec suite on windows yet because the compiler depends on a bunch of stuff thats not ported.
|
|
||
| def printf(format, args = [] of LLVM::Value) | ||
| call @printf, [global_string_pointer(format)] + args | ||
| def printf(format, args = [] of LLVM::Value, catch_pad = nil) |
There was a problem hiding this comment.
call to printf do really need to be handled with the funclet? Up to know the call was never replaced by an invoke, so it was assumed to never raise AFAIK.
There was a problem hiding this comment.
every call inside a catch pad needs a funclet. This is not about call/invoke and @rescue_block, it's about @catch_pad which is a totally different concern and semantic. This is about telling LLVM which method calls are inside the "catch block", and it applies to every single method call. @rescue_block is about codegenning invoke instructions, and it only matters for calls that raise.
There was a problem hiding this comment.
Ok. I also missed this was inside the builder, outside the codegen where the handling of the operand bundle is already handled.
| false | ||
| end | ||
|
|
||
| private def codegen_windows_seh(node : ExceptionHandler) |
There was a problem hiding this comment.
-
I think there should be some comments here explaining how the exception data is handled. I know it comes from documentation of llvm seh and trial an error. But there are some decisions w.r.t. how exception is encoded as
void*that would help future readers. Also some basic examples of the result inllvm-irwould be great for documentation IMO (l tried to do that at for overflow here in the past). -
And I am tempted to suggest some comment or extraction of common parts with the codegen_landing_pad. The handling of multiple ensures is shared. Maybe a strategy struct can be used to encapsulate custom logic for pads vs seh, leaving the existing comments
visit(node : ExceptionHandler)/codegen_landing_padthat goes into the details of the handling of the AST. The only real value of this would be to help the reader to understand separate the concepts that are together in this code.
There was a problem hiding this comment.
I'll definitely look at how to possibly abstract this, if not I'll definitely add some comments.
| exception.inspect_with_backtrace(STDERR) | ||
| LibC.exit(1) | ||
| end | ||
| {% if flag?(:debug_raise) %} |
There was a problem hiding this comment.
I would add a TODO note to remote the whole puts here, or just leave it commented for a while.
There was a problem hiding this comment.
I don't see why we can't leave the flag in indefinitely.
There was a problem hiding this comment.
If it's going to stay, then it needs to be supported in the other platforms.
| node.value | ||
| end | ||
|
|
||
| def void_ptr_type_descriptor |
There was a problem hiding this comment.
I'm glad code snippets end up been useful :-). Thanks for tidying them up a bit.
|
Should this also be on 0.26.0 @bcardiff? |
0bab1bc to
0e0e28a
Compare
|
Pushed an update which merges the windows and linux exception handling code and improves the comments. |
| node_else = node.else | ||
| rescue_ensure_block = nil | ||
|
|
||
| # Here we transform this: |
There was a problem hiding this comment.
I would say that this whole comment section should stay. Your additions inlines are great, but keeping this sections with numbers and later references would be ideal. They facilitate understanding the later code a lot.
Other than that LGTM.
There was a problem hiding this comment.
I don't like that block comment because it's very incomplete information, and once you distill it into it's core it's just what I've written below.
Once I start thinking how to improve the comment, I just end up with repeating what i've written below
There was a problem hiding this comment.
The once you distill time is decreased a lot with a nice introduction like the one that is removed. I think that the intricate nature of the code transformation deserves that kind of introduction. Just restoring those line are enough.
|
Added a comment @bcardiff |
| # type restrictions on the exception caught. The basic strategy is to codegen this | ||
| # | ||
| # ``` | ||
| # ```cr |
There was a problem hiding this comment.
cr here is invalid (that's gfm), it better be empty (like it was before)
ditto all below
There was a problem hiding this comment.
It doesn't hurt, though.
There was a problem hiding this comment.
It doesn't help either (especially since it was good before that change).
There was a problem hiding this comment.
who cares, the comment isn't for anyone but humans
Yeah, and humans know what cr stands for...
There was a problem hiding this comment.
I agree with @Sija. It's better without the cr. But we can clean up that later (together with one more ```crystal that is somewhere in the code...)
This was a tough one, but I learned a lot along the way and I'm glad I accomplished it.