Skip to content

Commit

Permalink
Added CallConvention attribute for funs
Browse files Browse the repository at this point in the history
  • Loading branch information
Ary Borenszweig committed Feb 21, 2015
1 parent a905e59 commit 90c374d
Show file tree
Hide file tree
Showing 8 changed files with 106 additions and 9 deletions.
41 changes: 41 additions & 0 deletions spec/compiler/type_inference/fun_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -500,4 +500,45 @@ describe "Type inference: fun" do
}
)) { int32 }
end

it "specifies a call convention" do
result = infer_type(%(
lib LibFoo
@[CallConvention("X86_StdCall")]
fun foo : Int32
end
))
foo = result.program.types["LibFoo"].lookup_first_def("foo", nil) as External
foo.call_convention.should eq(LLVM::CallConvention::X86_StdCall)
end

it "errors if wrong number of arguments for CallConvention" do
assert_error %(
lib LibFoo
@[CallConvention("X86_StdCall", "bar")]
fun foo : Int32
end
),
"wrong number of arguments for attribute CallConvention (2 for 1)"
end

it "errors if CallConvention argument is not a string" do
assert_error %(
lib LibFoo
@[CallConvention(1)]
fun foo : Int32
end
),
"argument to CallConvention must be a string"
end

it "errors if CallConvention argument is not a valid string" do
assert_error %(
lib LibFoo
@[CallConvention("foo")]
fun foo : Int32
end
),
"invalid call convention. Valid values are #{LLVM::CallConvention.values.join ", "}"
end
end
4 changes: 4 additions & 0 deletions src/compiler/crystal/codegen/call.cr
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,10 @@ class Crystal::CodeGenVisitor < Crystal::Visitor
@last = call func, call_args
end

if target_def.is_a?(External) && (call_convention = target_def.call_convention)
@last.call_convention = call_convention
end

if @builder.end
return @last
end
Expand Down
4 changes: 4 additions & 0 deletions src/compiler/crystal/codegen/fun.cr
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,10 @@ class Crystal::CodeGenVisitor < Crystal::Visitor
)
context.fun.add_attribute LLVM::Attribute::NoReturn if target_def.no_returns?

if target_def.is_a?(External) && (call_convention = target_def.call_convention)
context.fun.call_convention = call_convention
end

no_inline = false
target_def.attributes.try &.each do |attribute|
case attribute.name
Expand Down
2 changes: 2 additions & 0 deletions src/compiler/crystal/semantic/ast.cr
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,8 @@ module Crystal

property :used
@used = false

property :call_convention
end

class EnumDef
Expand Down
40 changes: 38 additions & 2 deletions src/compiler/crystal/semantic/type_inference.cr
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ module Crystal
ValidClassVarAttributes = ThreadLocalAttributes
ValidStructDefAttributes = %w(Packed)
ValidDefAttributes = %w(AlwaysInline NoInline Raises ReturnsTwice)
ValidFunDefAttributes = %w(AlwaysInline NoInline Raises ReturnsTwice)
ValidFunDefAttributes = %w(AlwaysInline NoInline Raises ReturnsTwice CallConvention)
ValidEnumDefAttributes = %w(Flags)

getter mod
Expand Down Expand Up @@ -1673,7 +1673,8 @@ module Crystal
node.raise "can only declare fun at lib or global scope"
end

check_valid_attributes node, ValidDefAttributes, "fun"
call_convention = check_call_convention_attributes node
check_valid_attributes node, ValidFunDefAttributes, "fun"

args = node.args.map do |arg|
restriction = arg.restriction.not_nil!
Expand All @@ -1694,6 +1695,7 @@ module Crystal

external = External.for_fun(node.name, node.real_name, args, return_type, node.varargs, node.body, node)
external.doc = node.doc
external.call_convention = call_convention

if node_body = node.body
vars = MetaVars.new
Expand Down Expand Up @@ -2808,6 +2810,40 @@ module Crystal
end
end

def check_call_convention_attributes(node)
attributes = @attributes
return unless attributes

call_convention = nil

attributes.delete_if do |attr|
next false unless attr.name == "CallConvention"

if call_convention
attr.raise "call convention already specified"
end

if attr.args.length != 1
attr.raise "wrong number of arguments for attribute CallConvention (#{attr.args.length} for 1)"
end

call_convention_node = attr.args.first
unless call_convention_node.is_a?(StringLiteral)
call_convention_node.raise "argument to CallConvention must be a string"
end

value = call_convention_node.value
call_convention = LLVM::CallConvention.parse?(value)
unless call_convention
call_convention_node.raise "invalid call convention. Valid values are #{LLVM::CallConvention.values.join ", "}"
end

true
end

call_convention
end

def check_valid_attributes(node, valid_attributes, desc)
if attributes = @attributes
attributes.each do |attr|
Expand Down
14 changes: 7 additions & 7 deletions src/llvm/enums.cr
Original file line number Diff line number Diff line change
Expand Up @@ -139,12 +139,12 @@ module LLVM
end

enum CallConvention
C = 0
Fast = 8
Cold = 9
WebKitJS = 12
AnyReg = 13
X86Std = 64
X86Fastcall = 65
C = 0
Fast = 8
Cold = 9
WebKit_JS = 12
AnyReg = 13
X86_StdCall = 64
X86_FastCall = 65
end
end
2 changes: 2 additions & 0 deletions src/llvm/lib_llvm.cr
Original file line number Diff line number Diff line change
Expand Up @@ -222,4 +222,6 @@ lib LibLLVM
fun print_value_to_string = LLVMPrintValueToString(v : ValueRef) : UInt8*
fun get_function_call_convention = LLVMGetFunctionCallConv(fn : ValueRef) : LLVM::CallConvention
fun set_function_call_convention = LLVMSetFunctionCallConv(fn : ValueRef, cc : LLVM::CallConvention)
fun set_instruction_call_convention = LLVMSetInstructionCallConv(instr : ValueRef, cc : LLVM::CallConvention)
fun get_instruction_call_convention = LLVMGetInstructionCallConv(instr : ValueRef) : LLVM::CallConvention
end
8 changes: 8 additions & 0 deletions src/llvm/value_methods.cr
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,14 @@ module LLVM::ValueMethods
LibLLVM.get_linkage(self)
end

def call_convention=(call_convention)
LibLLVM.set_instruction_call_convention(self, call_convention)
end

def call_convention
LibLLVM.get_instruction_call_convention(self)
end

def global_constant=(global_constant)
LibLLVM.set_global_constant(self, global_constant ? 1 : 0)
end
Expand Down

0 comments on commit 90c374d

Please sign in to comment.