Skip to content

Commit

Permalink
Showing 4 changed files with 38 additions and 11 deletions.
11 changes: 11 additions & 0 deletions spec/compiler/codegen/primitives_spec.cr
Original file line number Diff line number Diff line change
@@ -176,4 +176,15 @@ describe "Code gen: primitives" do
$x
)).to_i.should eq(2)
end

it "uses built-in llvm function that returns a tuple" do
run(%(
lib Intrinsics
fun sadd_i32_with_overlow = "llvm.sadd.with.overflow.i32"(a : Int32, b : Int32) : {Int32, Bool}
end
x, o = Intrinsics.sadd_i32_with_overlow(1, 2)
x
)).to_i.should eq(3)
end
end
14 changes: 14 additions & 0 deletions src/compiler/crystal/codegen/ast.cr
Original file line number Diff line number Diff line change
@@ -90,6 +90,20 @@ module Crystal
def varargs?
false
end

# Returns `self` as an `External` if this Def must be considered
# an external in the codegen, meaning we need to respect the C ABI.
# The only case where this is not true if for LLVM instrinsics.
# For example overflow intrincis return a tuple, like {i32, i1}:
# in C ABI that is represented as i64, but we need to keep the original
# type here, respecting LLVM types, not the C ABI.
def considered_external?
if self.is_a?(External) && !self.real_name.starts_with?("llvm.")
self
else
nil
end
end
end

class External
18 changes: 10 additions & 8 deletions src/compiler/crystal/codegen/call.cr
Original file line number Diff line number Diff line change
@@ -46,8 +46,8 @@ class Crystal::CodeGenVisitor

def prepare_call_args(node, owner)
target_def = node.target_def
if target_def.is_a?(External)
prepare_call_args_external(node, target_def, owner)
if external = target_def.considered_external?
prepare_call_args_external(node, external, owner)
else
prepare_call_args_non_external(node, target_def, owner)
end
@@ -435,17 +435,19 @@ class Crystal::CodeGenVisitor

set_call_attributes node, target_def, self_type, is_closure, fun_type

if target_def.is_a?(External) && (target_def.type.proc? || target_def.type.is_a?(NilableProcType))
external = target_def.try &.considered_external?

if external && (external.type.proc? || external.type.is_a?(NilableProcType))
fun_ptr = bit_cast(@last, LLVM::VoidPointer)
ctx_ptr = LLVM::VoidPointer.null
return @last = make_fun(target_def.type, fun_ptr, ctx_ptr)
return @last = make_fun(external.type, fun_ptr, ctx_ptr)
end

if target_def.is_a?(External)
if external
if type.no_return?
unreachable
else
abi_return = abi_info(target_def).return_type
abi_return = abi_info(external).return_type
case abi_return.kind
when LLVM::ABI::ArgKind::Direct
if cast = abi_return.cast
@@ -484,8 +486,8 @@ class Crystal::CodeGenVisitor
end

def set_call_attributes(node : Call, target_def, self_type, is_closure, fun_type)
if target_def.is_a?(External)
set_call_attributes_external(node, target_def)
if external = target_def.considered_external?
set_call_attributes_external(node, external)
else
set_call_attributes_non_external(node, target_def, self_type, is_closure, fun_type)
end
6 changes: 3 additions & 3 deletions src/compiler/crystal/codegen/fun.cr
Original file line number Diff line number Diff line change
@@ -135,8 +135,8 @@ class Crystal::CodeGenVisitor
end

def codegen_fun_signature(mangled_name, target_def, self_type, is_fun_literal, is_closure)
if target_def.is_a?(External)
codegen_fun_signature_external(mangled_name, target_def)
if external = target_def.considered_external?
codegen_fun_signature_external(mangled_name, external)
else
codegen_fun_signature_non_external(mangled_name, target_def, self_type, is_fun_literal, is_closure)
end
@@ -184,7 +184,7 @@ class Crystal::CodeGenVisitor

setup_context_fun(mangled_name, target_def, llvm_args_types, llvm_return_type)

if @single_module && !target_def.no_inline?
if @single_module && !target_def.no_inline? && target_def.considered_external?
context.fun.linkage = LLVM::Linkage::Internal
end

0 comments on commit b464df6

Please sign in to comment.