test/call_test.rb
# frozen_string_literal: true
require "test_helper"
class CallTestCase < Minitest::Test
def setup
LLVM.init_jit
end
def test_simple_call
test_module = define_module("test_module") do |host_module|
define_function(host_module, "test_function", [], LLVM::Int) do |builder, function, *arguments|
entry = function.basic_blocks.append
builder.position_at_end(entry)
builder.ret(LLVM::Int(1))
end
end
assert function = test_module.functions["test_function"]
assert_equal :function, function.type.kind
assert_equal 'i32 ()', function.type.to_s
assert_equal :function, function.function_type.kind
assert_equal 'i32 ()', function.function_type.to_s
assert_equal :integer, function.return_type.kind
assert_equal 'i32', function.return_type.to_s
assert_equal 1, run_function_on_module(test_module, "test_function").to_i
end
def test_nested_call
test_module = define_module("test_module") do |host_module|
function_1 = define_function(host_module, "test_function_1", [], LLVM::Int) do |builder, function, *arguments|
entry = function.basic_blocks.append
builder.position_at_end(entry)
builder.ret(LLVM::Int(1))
end
function_2 = define_function(host_module, "test_function_2", [], LLVM::Int) do |builder, function, *arguments|
entry = function.basic_blocks.append
builder.position_at_end(entry)
builder.ret(builder.call(function_1))
end
end
assert_equal 1, run_function_on_module(test_module, "test_function_2").to_i
end
def test_recursive_call
test_module = define_module("test_module") do |host_module|
define_function(host_module, "test_function", [LLVM::Int], LLVM::Int) do |builder, function, *arguments|
entry = function.basic_blocks.append
recurse = function.basic_blocks.append
exit = function.basic_blocks.append
builder.position_at_end(entry)
builder.cond(builder.icmp(:uge, arguments.first, LLVM::Int(5)), exit, recurse)
builder.position_at_end(recurse)
result = builder.call(function, builder.add(arguments.first, LLVM::Int(1)))
builder.br(exit)
builder.position_at_end(exit)
builder.ret(builder.phi(LLVM::Int, entry => arguments.first, recurse => result))
end
end
assert_equal 5, run_function_on_module(test_module, "test_function", 1).to_i
end
def test_external
test_module = define_module("test_module") do |host_module|
external = host_module.functions.add("abs", [LLVM::Int], LLVM::Int)
define_function(host_module, "test_function", [LLVM::Int], LLVM::Int) do |builder, function, *arguments|
entry = function.basic_blocks.append
builder.position_at_end(entry)
builder.ret(builder.call(external, arguments.first))
end
end
assert_equal(-10.abs, run_function_on_module(test_module, "test_function", -10).to_i)
end
def test_external_string
test_module = define_module("test_module") do |host_module|
global = host_module.globals.add(LLVM::Array(LLVM::Int8, 5), "path")
global.linkage = :internal
global.initializer = LLVM::ConstantArray.string("PATH")
refute_predicate global, :thread_local?
external = host_module.functions.add("getenv", [LLVM::Pointer(LLVM::Int8)], LLVM::Pointer(LLVM::Int8))
define_function(host_module, "test_function", [], LLVM::Pointer(LLVM::Int8)) do |builder, function, *arguments|
entry = function.basic_blocks.append
builder.position_at_end(entry)
parameter = builder.gep(global, [LLVM::Int(0), LLVM::Int(0)])
builder.ret(builder.call(external, parameter))
end
end
assert_equal ENV.fetch("PATH", nil), run_function_on_module(test_module, "test_function").to_ptr.read_pointer.read_string
end
def test_call_with_nonfunction
test_module = define_module("test_module") do |host_module|
define_function(host_module, "test_function", [], LLVM.Void) do |builder, function|
entry = function.basic_blocks.append
builder.position_at_end(entry)
assert_raises(ArgumentError) do
builder.call(nil)
end
assert_raises(ArgumentError) do
builder.call("test")
end
assert_raises(ArgumentError) do
builder.call(LLVM::Int64.from_i(0))
end
builder.ret nil
end
end
end
def test_call_default_call_conv
test_module = define_module("test_module") do |host_module|
callee_fun = define_function(host_module, "callee_fun", [LLVM::Int64], LLVM::Int64) do |builder, function, *arguments|
function.call_conv = :fast
entry = function.basic_blocks.append
builder.position_at_end(entry)
builder.ret(arguments[0])
end
define_function(host_module, "caller_fun", [], LLVM::Int64) do |builder, function, *arguments|
entry = function.basic_blocks.append
builder.position_at_end(entry)
builder.ret(builder.call(callee_fun, LLVM::Int64.from_i(42)))
end
end
assert function = test_module.functions["caller_fun"]
assert_match(/call fastcc i64 @callee_fun/, function.to_s)
assert_equal 42, run_function_on_module(test_module, "caller_fun").to_i
end
def test_call_by_function_name
test_module = define_module("test_module") do |host_module|
define_function(host_module, "callee_fun", [LLVM::Int64], LLVM::Int64) do |builder, function, *arguments|
function.call_conv = :fast
entry = function.basic_blocks.append
builder.position_at_end(entry)
builder.ret(arguments[0])
end
define_function(host_module, "caller_fun", [], LLVM::Int64) do |builder, function, *arguments|
entry = function.basic_blocks.append
builder.position_at_end(entry)
builder.ret(builder.call('callee_fun', LLVM::Int64.from_i(42)))
end
end
assert function = test_module.functions["caller_fun"]
assert_match(/call fastcc i64 @callee_fun/, function.to_s)
assert_equal 42, run_function_on_module(test_module, "caller_fun").to_i
end
def test_invoke_default_call_conv
skip "This seems to be broken"
test_module = define_module("test_module") do |host_module|
callee_fun = define_function(host_module, "callee_fun", [], LLVM::Int64) do |builder, function, *arguments|
function.call_conv = :fast
entry = function.basic_blocks.append
builder.position_at_end(entry)
builder.ret(LLVM::Int64.from_i(42))
end
caller_fun = define_function(host_module, "caller_fun", [], LLVM::Int64) do |builder, function, *arguments|
entry = function.basic_blocks.append
exit = function.basic_blocks.append
entry.build do |b|
retval = builder.invoke(callee_fun, [], entry, entry)
b.br exit
end
exit.build do |b|
b.ret retval
end
end
end
assert function = test_module.functions["caller_fun"]
assert_match(/call fastcc i64 @callee_fun/, function.to_s)
assert_equal 42, run_function_on_module(test_module, "caller_fun").to_i
end
end