diff --git a/spec/compiler/codegen/and_spec.cr b/spec/compiler/codegen/and_spec.cr index 337cceb138eb..7aa3cdfd6c7b 100644 --- a/spec/compiler/codegen/and_spec.cr +++ b/spec/compiler/codegen/and_spec.cr @@ -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 @@ -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 @@ -56,7 +56,7 @@ 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 @@ -64,7 +64,7 @@ describe "Code gen: and" do 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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -173,7 +173,7 @@ 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 @@ -181,6 +181,6 @@ describe "Code gen: and" do if 1 == 2 && (b = Reference.new) end b.nil? - ").to_b.should be_true + ", Bool).should be_true end end diff --git a/spec/spec_helper.cr b/spec/spec_helper.cr index ca5bc61ad3c4..31412035ff74 100644 --- a/spec/spec_helper.cr +++ b/spec/spec_helper.cr @@ -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(%( diff --git a/src/compiler/crystal/codegen/codegen.cr b/src/compiler/crystal/codegen/codegen.cr index a46d255901e5..67882e9d75dc 100644 --- a/src/compiler/crystal/codegen/codegen.cr +++ b/src/compiler/crystal/codegen/codegen.cr @@ -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, diff --git a/src/llvm/jit_compiler.cr b/src/llvm/jit_compiler.cr index 33d03e697107..4acae901f381 100644 --- a/src/llvm/jit_compiler.cr +++ b/src/llvm/jit_compiler.cr @@ -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 diff --git a/src/llvm/lib_llvm/execution_engine.cr b/src/llvm/lib_llvm/execution_engine.cr index f9de5c10ea39..bfc2e23154db 100644 --- a/src/llvm/lib_llvm/execution_engine.cr +++ b/src/llvm/lib_llvm/execution_engine.cr @@ -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