From 0259accb609b2f9bb0442775e4659ce9e7e39f45 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig <aborenszweig@manas.com.ar> Date: Fri, 23 May 2014 22:22:20 -0300 Subject: [PATCH] Try to implement closures without trampolines --- libs/llvm/llvm.cr | 2 + spec/compiler/codegen/fun_spec.cr | 18 +++++ src/compiler/crystal/codegen.cr | 70 ++++++++++++++----- .../crystal/codegen/llvm_builder_helper.cr | 8 +++ src/compiler/crystal/codegen/llvm_typer.cr | 25 ++++++- 5 files changed, 101 insertions(+), 22 deletions(-) diff --git a/libs/llvm/llvm.cr b/libs/llvm/llvm.cr index 95555cc7ec..e6007c41f4 100644 --- a/libs/llvm/llvm.cr +++ b/libs/llvm/llvm.cr @@ -158,6 +158,8 @@ module LLVM Float = LibLLVM.float_type Double = LibLLVM.double_type + VoidPointer = LLVM.pointer_type(LLVM::Int8) + ifdef x86_64 SizeT = Int64 else diff --git a/spec/compiler/codegen/fun_spec.cr b/spec/compiler/codegen/fun_spec.cr index 211bb86d21..92f29fdc30 100644 --- a/spec/compiler/codegen/fun_spec.cr +++ b/spec/compiler/codegen/fun_spec.cr @@ -166,4 +166,22 @@ describe "Code gen: fun" do f.call(1 || 1.5).to_i ").to_i.should eq(1) end + + it "allows passing fun type to C automatically" do + run(%( + require "prelude" + + lib C + fun qsort(base : Void*, nel : C::SizeT, width : C::SizeT, (Void*, Void* -> Int32)) + end + + ary = [3, 1, 4, 2] + C.qsort(ary.buffer as Void*, ary.length.to_sizet, sizeof(Int32).to_sizet, ->(a : Void*, b : Void*) { + a = a as Int32* + b = b as Int32* + a.value <=> b.value + }) + ary[0] + )).to_i.should eq(1) + end end diff --git a/src/compiler/crystal/codegen.cr b/src/compiler/crystal/codegen.cr index 8af1b2b135..fd2619db59 100644 --- a/src/compiler/crystal/codegen.cr +++ b/src/compiler/crystal/codegen.cr @@ -640,7 +640,17 @@ module Crystal end def codegen_primitive_fun_call(node, target_def, call_args) - codegen_call_or_invoke(node, target_def, nil, call_args[0], call_args[1 .. -1], true, target_def.type) + closure_ptr = call_args[0] + args = call_args[1 .. -1] + + fun_ptr = @builder.extract_value closure_ptr, 0 + fun_ptr = bit_cast fun_ptr, llvm_fun_type(context.type) + + ctx_ptr = @builder.extract_value closure_ptr, 1 + + args.push ctx_ptr + + codegen_call_or_invoke(node, target_def, nil, fun_ptr, args, true, target_def.type) end def codegen_primitive_pointer_diff(node, target_def, call_args) @@ -887,11 +897,14 @@ module Crystal fun_literal_name = "~fun_literal_#{@fun_literal_count}" is_closure = context.closure_vars || context.closure_self the_fun = codegen_fun(fun_literal_name, node.def, context.type, false, @main_mod, true, is_closure) - @last = (check_main_fun fun_literal_name, the_fun).fun + fun_ptr = (check_main_fun fun_literal_name, the_fun).fun + closure_ptr = alloca llvm_type(node.type) + store bit_cast(fun_ptr, LLVM::VoidPointer), gep(closure_ptr, 0, 0) if is_closure - trampoline_init node.type, @last, context.closure_ptr.not_nil! + store bit_cast(context.closure_ptr.not_nil!, LLVM::VoidPointer), gep(closure_ptr, 0, 1) end + @last = load(closure_ptr) false end @@ -905,12 +918,13 @@ module Crystal call_self = llvm_self end last_fun = target_def_fun(node.call.target_def, owner) - @last = last_fun.fun + closure_ptr = alloca llvm_type(node.type) + store bit_cast(last_fun.fun, LLVM::VoidPointer), gep(closure_ptr, 0, 0) if call_self - wrapper = trampoline_wrapper(node.call.target_def, last_fun) - trampoline_init node.type, wrapper.fun, call_self + store bit_cast(call_self, LLVM::VoidPointer), gep(closure_ptr, 0, 1) end + @last = load(closure_ptr) false end @@ -1298,8 +1312,10 @@ module Crystal obj_type = node.obj.type to_type = node.to.type.instance_type - if obj_type.pointer? || obj_type.fun? + if obj_type.pointer? @last = cast_to last_value, to_type + elsif obj_type.fun? + # Nothing to do else resulting_type = obj_type.filter_by(to_type).not_nil! @@ -1674,6 +1690,7 @@ module Crystal def prepare_call_args(node, owner) has_out = false target_def = node.target_def + is_external = target_def.is_a?(External) call_args = Array(LibLLVM::ValueRef).new(node.args.length + 1) old_needs_value = @needs_value @@ -1698,9 +1715,9 @@ module Crystal when Var # For out arguments we reserve the space. After the call # we move the value to the variable. - call_args << alloca(llvm_type(arg.type)) + call_arg = alloca(llvm_type(arg.type)) when InstanceVar - call_args << instance_var_ptr(type, arg.name, llvm_self_ptr) + call_arg = instance_var_ptr(type, arg.name, llvm_self_ptr) else arg.raise "Bug: out argument was #{arg}" end @@ -1713,9 +1730,14 @@ module Crystal # Def argument might be missing if it's a variadic call call_arg = downcast(call_arg, def_arg.type, arg.type, true) if def_arg + end - call_args << call_arg + if is_external && arg.type.fun? + fun_ptr = @builder.extract_value call_arg, 0 + call_arg = bit_cast fun_ptr, llvm_fun_type(arg.type) end + + call_args << call_arg end @needs_value = old_needs_value @@ -2023,6 +2045,8 @@ module Crystal end def codegen_fun_signature(mangled_name, target_def, self_type, is_fun_literal, is_closure) + is_external = target_def.is_a?(External) + args = Array(Arg).new(target_def.args.length + 1) if !is_fun_literal && self_type.passed_as_self? @@ -2031,15 +2055,22 @@ module Crystal args.concat target_def.args - llvm_args_types = args.map { |arg| llvm_arg_type(arg.type) } - if is_closure - llvm_args_types << LLVM.pointer_type(context.closure_type.not_nil!) + if is_external + llvm_args_types = args.map { |arg| llvm_c_type(arg.type) } + llvm_return_type = llvm_c_type(target_def.type) + else + llvm_args_types = args.map { |arg| llvm_arg_type(arg.type) } + llvm_return_type = llvm_type(target_def.type) + end + + if is_fun_literal + llvm_args_types << LLVM::VoidPointer end context.fun = @llvm_mod.functions.add( mangled_name, llvm_args_types, - llvm_type(target_def.type), + llvm_return_type, target_def.varargs, ) context.fun.add_attribute LibLLVM::Attribute::NoReturn if target_def.no_returns? @@ -2059,14 +2090,10 @@ module Crystal end end - if is_closure - LLVM.add_attribute context.fun.get_param(llvm_args_types.length - 1), LibLLVM::Attribute::Nest - end - args end - def setup_closure_vars(closure_vars, context = self.context, closure_ptr = context.fun.get_param(context.fun.param_count - 1)) + def setup_closure_vars(closure_vars, context = self.context, closure_ptr = context_fun_closure_ptr) if context.closure_skip_parent parent_context = context.closure_parent_context.not_nil! setup_closure_vars(parent_context.closure_vars.not_nil!, parent_context, closure_ptr) @@ -2086,6 +2113,11 @@ module Crystal end end + def context_fun_closure_ptr + void_ptr = context.fun.get_param(context.fun.param_count - 1) + bit_cast void_ptr, LLVM.pointer_type(context.closure_type.not_nil!) + end + def create_local_copy_of_fun_args(target_def, self_type, args, is_fun_literal) target_def_vars = target_def.vars args.each_with_index do |arg, i| diff --git a/src/compiler/crystal/codegen/llvm_builder_helper.cr b/src/compiler/crystal/codegen/llvm_builder_helper.cr index c6fa1f786b..d06a3744dc 100644 --- a/src/compiler/crystal/codegen/llvm_builder_helper.cr +++ b/src/compiler/crystal/codegen/llvm_builder_helper.cr @@ -216,6 +216,14 @@ module Crystal llvm_typer.llvm_embedded_type(type) end + def llvm_fun_type(type) + llvm_typer.fun_type(type as FunType) + end + + def llvm_c_type(type) + llvm_typer.llvm_c_type(type) + end + def llvm_size(type) size_of llvm_type(type) end diff --git a/src/compiler/crystal/codegen/llvm_typer.cr b/src/compiler/crystal/codegen/llvm_typer.cr index 76240c7018..a0b31cb40a 100644 --- a/src/compiler/crystal/codegen/llvm_typer.cr +++ b/src/compiler/crystal/codegen/llvm_typer.cr @@ -4,6 +4,7 @@ require "llvm" module Crystal class LLVMTyper TYPE_ID_POINTER = LLVM.pointer_type(LLVM::Int32) + FUN_TYPE = LLVM.struct_type [LLVM::VoidPointer, LLVM::VoidPointer], "->" getter landing_pad_type @@ -11,12 +12,13 @@ module Crystal @cache = {} of Type => LibLLVM::TypeRef @struct_cache = {} of Type => LibLLVM::TypeRef @arg_cache = {} of Type => LibLLVM::TypeRef + @c_cache = {} of Type => LibLLVM::TypeRef @embedded_cache = {} of Type => LibLLVM::TypeRef target = LLVM::Target.first machine = target.create_target_machine("i686-unknown-linux").not_nil! @layout = machine.data_layout.not_nil! - @landing_pad_type = LLVM.struct_type([LLVM.pointer_type(LLVM::Int8), LLVM::Int32], "landing_pad") + @landing_pad_type = LLVM.struct_type([LLVM::VoidPointer, LLVM::Int32], "landing_pad") end def llvm_type(type) @@ -154,8 +156,7 @@ module Crystal end def create_llvm_type(type : FunType) - arg_types = type.arg_types.map { |arg_type| llvm_arg_type(arg_type) } - LLVM.pointer_type(LLVM.function_type(arg_types, llvm_type(type.return_type))) + FUN_TYPE end def create_llvm_type(type : AliasType) @@ -288,6 +289,24 @@ module Crystal llvm_type type end + def llvm_c_type(type : Type) + @c_cache[type] ||= create_llvm_c_type(type) + end + + def create_llvm_c_type(type : FunType) + fun_type(type) + end + + def create_llvm_c_type(type) + llvm_arg_type(type) + end + + def fun_type(type : FunType) + arg_types = type.arg_types.map { |arg_type| llvm_arg_type(arg_type) } + arg_types << LLVM::VoidPointer + LLVM.pointer_type(LLVM.function_type(arg_types, llvm_type(type.return_type))) + end + def closure_context_type(vars, parent_llvm_type, self_type) LLVM.struct_type("closure") do |a_struct| elems = Array(LibLLVM::TypeRef).new(vars.length + (parent_llvm_type ? 1 : 0)) -- GitLab