Skip to content
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
48 changes: 24 additions & 24 deletions spec/compiler/codegen/and_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -2,42 +2,42 @@ require "../../spec_helper"

describe "Code gen: and" do
it "codegens and with bool false and false" do
run("false && false").to_b.should be_false
run("false && false", Bool).should be_false
end

it "codegens and with bool false and true" do
run("false && true").to_b.should be_false
run("false && true", Bool).should be_false
end

it "codegens and with bool true and true" do
run("true && true").to_b.should be_true
run("true && true", Bool).should be_true
end

it "codegens and with bool true and false" do
run("true && false").to_b.should be_false
run("true && false", Bool).should be_false
end

it "codegens and with bool and int 1" do
run("struct Bool; def to_i!; 0; end; end; (false && 2).to_i!").to_i.should eq(0)
run("struct Bool; def to_i!; 0; end; end; (false && 2).to_i!", Int32).should eq(0)
end

it "codegens and with bool and int 2" do
run("struct Bool; def to_i!; 0; end; end; (true && 2).to_i!").to_i.should eq(2)
run("struct Bool; def to_i!; 0; end; end; (true && 2).to_i!", Int32).should eq(2)
end

it "codegens and with primitive type other than bool" do
run("1 && 2").to_i.should eq(2)
run("1 && 2", Int32).should eq(2)
end

it "codegens and with primitive type other than bool with union" do
run("(1 && 1.5).to_f").to_f64.should eq(1.5)
run("(1 && 1.5).to_f", Float64).should eq(1.5)
end

it "codegens and with primitive type other than bool" do
run(%(
struct Nil; def to_i!; 0; end; end
(nil && 2).to_i!
)).to_i.should eq(0)
), Int32).should eq(0)
end

it "codegens and with nilable as left node 1" do
Expand All @@ -47,7 +47,7 @@ describe "Code gen: and" do
a = Reference.new
a = nil
(a && 2).to_i!
").to_i.should eq(0)
", Int32).should eq(0)
end

it "codegens and with nilable as left node 2" do
Expand All @@ -56,15 +56,15 @@ describe "Code gen: and" do
a = nil
a = Reference.new
(a && 2).to_i!
").to_i.should eq(2)
", Int32).should eq(2)
end

it "codegens and with non-false union as left node" do
run("
a = 1.5
a = 1
(a && 2).to_i!
").to_i.should eq(2)
", Int32).should eq(2)
end

it "codegens and with nil union as left node 1" do
Expand All @@ -73,7 +73,7 @@ describe "Code gen: and" do
a = nil
a = 1
(a && 2).to_i!
").to_i.should eq(2)
", Int32).should eq(2)
end

it "codegens and with nil union as left node 2" do
Expand All @@ -82,7 +82,7 @@ describe "Code gen: and" do
a = 1
a = nil
(a && 2).to_i!
").to_i.should eq(0)
", Int32).should eq(0)
end

it "codegens and with bool union as left node 1" do
Expand All @@ -91,7 +91,7 @@ describe "Code gen: and" do
a = false
a = 1
(a && 2).to_i!
").to_i.should eq(2)
", Int32).should eq(2)
end

it "codegens and with bool union as left node 2" do
Expand All @@ -100,7 +100,7 @@ describe "Code gen: and" do
a = 1
a = false
(a && 2).to_i!
").to_i.should eq(0)
", Int32).should eq(0)
end

it "codegens and with bool union as left node 3" do
Expand All @@ -109,7 +109,7 @@ describe "Code gen: and" do
a = 1
a = true
(a && 2).to_i!
").to_i.should eq(2)
", Int32).should eq(2)
end

it "codegens and with bool union as left node 1" do
Expand All @@ -120,7 +120,7 @@ describe "Code gen: and" do
a = nil
a = 2
(a && 3).to_i!
").to_i.should eq(3)
", Int32).should eq(3)
end

it "codegens and with bool union as left node 2" do
Expand All @@ -131,7 +131,7 @@ describe "Code gen: and" do
a = 2
a = false
(a && 3).to_i!
").to_i.should eq(1)
", Int32).should eq(1)
end

it "codegens and with bool union as left node 3" do
Expand All @@ -142,7 +142,7 @@ describe "Code gen: and" do
a = 2
a = true
(a && 3).to_i!
").to_i.should eq(3)
", Int32).should eq(3)
end

it "codegens and with bool union as left node 4" do
Expand All @@ -153,14 +153,14 @@ describe "Code gen: and" do
a = true
a = nil
(a && 3).to_i!
").to_i.should eq(0)
", Int32).should eq(0)
end

it "codegens assign in right node, after must be nilable" do
run("
a = 1 == 2 && (b = Reference.new)
b.nil?
").to_b.should be_true
", Bool).should be_true
end

it "codegens assign in right node, inside if must not be nil" do
Expand All @@ -173,14 +173,14 @@ describe "Code gen: and" do
else
0
end
").to_i.should eq(1)
", Int32).should eq(1)
end

it "codegens assign in right node, after if must be nilable" do
run("
if 1 == 2 && (b = Reference.new)
end
b.nil?
").to_b.should be_true
", Bool).should be_true
end
end
12 changes: 12 additions & 0 deletions spec/spec_helper.cr
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,18 @@ def run(code, filename = nil, inject_primitives = true, debug = Crystal::Debug::
end
end

def run(code, return_type : T.class, filename : String? = nil, inject_primitives = true, debug = Crystal::Debug::None, flags = nil, *, file = __FILE__) forall T
if inject_primitives
code = %(require "primitives"\n#{code})
end

if code.includes?(%(require "prelude")) || flags
fail "TODO: support the prelude in typed codegen specs", file: file
else
new_program.run(code, return_type: T, filename: filename, debug: debug)
end
end

def test_c(c_code, crystal_code, *, file = __FILE__, &)
with_temp_c_object_file(c_code, file: file) do |o_filename|
yield run(%(
Expand Down
50 changes: 50 additions & 0 deletions src/compiler/crystal/codegen/codegen.cr
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,56 @@ module Crystal
end
end

def run(code, return_type : T.class, filename : String? = nil, debug = Debug::Default) forall T
parser = new_parser(code)
parser.filename = filename
node = parser.parse
node = normalize node
node = semantic node
evaluate node, T, debug: debug
end

def evaluate(node, return_type : T.class, debug = Debug::Default) : T forall T
visitor = CodeGenVisitor.new self, node, single_module: true, debug: debug
visitor.accept node
visitor.process_finished_hooks
visitor.finish

llvm_mod = visitor.modules[""].mod
llvm_mod.target = target_machine.triple

main = visitor.typed_fun?(llvm_mod, MAIN_NAME).not_nil!
llvm_context = llvm_mod.context

# void (*__evaluate_wrapper)(void*)
wrapper_type = LLVM::Type.function([llvm_context.void_pointer], llvm_context.void)
wrapper = llvm_mod.functions.add("__evaluate_wrapper", wrapper_type) do |func|
func.basic_blocks.append "entry" do |builder|
argc = llvm_context.int32.const_int(0)
argv = llvm_context.void_pointer.pointer.null
ret = builder.call(main.type, main.func, [argc, argv])
unless node.type.void? || node.type.nil_type?
out_ptr = func.params[0]
{% if LibLLVM::IS_LT_150 %}
out_ptr = builder.bit_cast out_ptr, main.type.return_type.pointer
{% end %}
builder.store(ret, out_ptr)
end
builder.ret
end
end

llvm_mod.verify

result = uninitialized T
LLVM::JITCompiler.new(llvm_mod) do |jit|
func_ptr = jit.function_address("__evaluate_wrapper")
func = Proc(T*, Nil).new(func_ptr, Pointer(Void).null)
func.call(pointerof(result))
end
result
end

def codegen(node, single_module = false, debug = Debug::Default,
frame_pointers = FramePointers::Auto)
visitor = CodeGenVisitor.new self, node, single_module: single_module,
Expand Down
4 changes: 4 additions & 0 deletions src/llvm/jit_compiler.cr
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ class LLVM::JITCompiler
LibLLVM.get_pointer_to_global(self, value)
end

def function_address(name : String) : Void*
Pointer(Void).new(LibLLVM.get_function_address(self, name.check_no_null_byte))
end

def to_unsafe
@unwrap
end
Expand Down
1 change: 1 addition & 0 deletions src/llvm/lib_llvm/execution_engine.cr
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,5 @@ lib LibLLVM
fun run_function = LLVMRunFunction(ee : ExecutionEngineRef, f : ValueRef, num_args : UInt, args : GenericValueRef*) : GenericValueRef
fun get_execution_engine_target_machine = LLVMGetExecutionEngineTargetMachine(ee : ExecutionEngineRef) : TargetMachineRef
fun get_pointer_to_global = LLVMGetPointerToGlobal(ee : ExecutionEngineRef, global : ValueRef) : Void*
fun get_function_address = LLVMGetFunctionAddress(ee : ExecutionEngineRef, name : Char*) : UInt64
end