Skip to content

Support exception handling on windows #6419

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Aug 6, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions spec/compiler/codegen/exception_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -1285,4 +1285,20 @@ describe "Code gen: exception" do
end
)).to_string.should eq("foo")
end

it "codegens return from rescue with value" do
run(%(
require "prelude"

def foo
begin
raise "foo"
rescue
return 5
end
end

foo
)).to_i.should eq(5)
end
end
3 changes: 2 additions & 1 deletion spec/std/dir_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ describe "Dir" do
end
end

it "tests empty? on a directory path to a file" do
# TODO: do we even want this?
pending_win32 "tests empty? on a directory path to a file" do
ex = expect_raises(Errno, /Error determining size of/) do
Dir.empty?(datapath("dir", "f1.txt", "/"))
end
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/crystal/codegen/call.cr
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,7 @@ class Crystal::CodeGenVisitor

if raises && (rescue_block = @rescue_block)
invoke_out_block = new_block "invoke_out"
@last = builder.invoke func, call_args, invoke_out_block, rescue_block
@last = invoke func, call_args, invoke_out_block, rescue_block
position_at_end invoke_out_block
else
@last = call func, call_args
Expand Down
23 changes: 21 additions & 2 deletions src/compiler/crystal/codegen/codegen.cr
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ module Crystal
MALLOC_NAME = "__crystal_malloc64"
MALLOC_ATOMIC_NAME = "__crystal_malloc_atomic64"
REALLOC_NAME = "__crystal_realloc64"
PERSONALITY_NAME = "__crystal_personality"
GET_EXCEPTION_NAME = "__crystal_get_exception"

class Program
Expand Down Expand Up @@ -97,6 +96,7 @@ module Crystal
getter llvm_typer : LLVMTyper
getter alloca_block : LLVM::BasicBlock
getter entry_block : LLVM::BasicBlock
getter personality_name : String
property last : LLVM::Value

class LLVMVar
Expand Down Expand Up @@ -129,6 +129,7 @@ module Crystal
@argv : LLVM::Value
@empty_md_list : LLVM::Value
@rescue_block : LLVM::BasicBlock?
@catch_pad : LLVM::Value?
@malloc_fun : LLVM::Function?
@malloc_atomic_fun : LLVM::Function?
@c_malloc_fun : LLVM::Function?
Expand Down Expand Up @@ -156,6 +157,15 @@ module Crystal
ret_type = @llvm_typer.llvm_return_type(@main_ret_type)
@main = @llvm_mod.functions.add(MAIN_NAME, [llvm_context.int32, llvm_context.void_pointer.pointer], ret_type)

if @program.has_flag? "windows"
@personality_name = "__CxxFrameHandler3"

personality_function = @llvm_mod.functions.add(@personality_name, [] of LLVM::Type, llvm_context.int32, true)
@main.personality_function = personality_function
else
@personality_name = "__crystal_personality"
end

emit_main_def_debug_metadata(@main, "??") unless @debug.none?

@context = Context.new @main, @program
Expand Down Expand Up @@ -282,7 +292,7 @@ module Crystal

def visit(node : FunDef)
case node.name
when MALLOC_NAME, MALLOC_ATOMIC_NAME, REALLOC_NAME, RAISE_NAME, PERSONALITY_NAME, GET_EXCEPTION_NAME
when MALLOC_NAME, MALLOC_ATOMIC_NAME, REALLOC_NAME, RAISE_NAME, @codegen.personality_name, GET_EXCEPTION_NAME
@codegen.accept node
end
false
Expand Down Expand Up @@ -613,6 +623,12 @@ module Crystal

if return_phi = context.return_phi
return_phi.add @last, node_type
elsif catch_pad = @catch_pad
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Member Author

@RX14 RX14 Jul 24, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

ret_block = new_block "catchret_ret"
builder.build_catch_ret catch_pad, ret_block

position_at_end ret_block
codegen_return node_type
else
codegen_return node_type
end
Expand Down Expand Up @@ -1518,6 +1534,7 @@ module Crystal
old_fun = context.fun
old_ensure_exception_handlers = @ensure_exception_handlers
old_rescue_block = @rescue_block
old_catch_pad = @catch_pad
old_entry_block = @entry_block
old_alloca_block = @alloca_block
old_needs_value = @needs_value
Expand All @@ -1528,6 +1545,7 @@ module Crystal

@ensure_exception_handlers = nil
@rescue_block = nil
@catch_pad = nil

block_value = yield

Expand All @@ -1539,6 +1557,7 @@ module Crystal
@llvm_typer = old_llvm_typer
@ensure_exception_handlers = old_ensure_exception_handlers
@rescue_block = old_rescue_block
@catch_pad = old_catch_pad
@entry_block = old_entry_block
@alloca_block = old_alloca_block
@needs_value = old_needs_value
Expand Down
14 changes: 12 additions & 2 deletions src/compiler/crystal/codegen/crystal_llvm_builder.cr
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,14 @@ module Crystal
value
end

def printf(format, args = [] of LLVM::Value)
call @printf, [global_string_pointer(format)] + args
def printf(format, args = [] of LLVM::Value, catch_pad = nil)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok. I also missed this was inside the builder, outside the codegen where the handling of the operand bundle is already handled.

if catch_pad
funclet = build_operand_bundle_def("funclet", [catch_pad])
else
funclet = LLVM::OperandBundleDef.null
end

call @printf, [global_string_pointer(format)] + args, bundle: funclet
end

def position_at_end(block)
Expand All @@ -54,6 +60,10 @@ module Crystal
@builder.insert_block
end

def build_operand_bundle_def(name, values : Array(LLVM::Value))
@builder.build_operand_bundle_def(name, values)
end

def to_unsafe
@builder.to_unsafe
end
Expand Down
Loading