diff --git a/README.md b/README.md index a23dc14d..4c3682eb 100644 --- a/README.md +++ b/README.md @@ -107,7 +107,7 @@ builder.positionAtEnd(of: entryBB) let local = builder.buildAlloca(type: FloatType.double, name: "local") // Compare to the condition -let test = builder.buildICmp(function.parameters[0], IntType.int1.zero(), .notEqual) +let test = builder.buildICmp(function.parameters[0], IntType.int1.zero(), .equal) // Create basic blocks for "then", "else", and "merge" let thenBB = function.appendBasicBlock(named: "then") diff --git a/Sources/LLVM/JIT.swift b/Sources/LLVM/JIT.swift index 497bdde2..55261d99 100644 --- a/Sources/LLVM/JIT.swift +++ b/Sources/LLVM/JIT.swift @@ -32,6 +32,10 @@ public final class JIT { /// The underlying LLVMExecutionEngineRef backing this JIT. internal let llvm: LLVMExecutionEngineRef + private static var linkOnce: () = { + return LLVMLinkInMCJIT() + }() + /// Creates a Just In Time compiler that will compile the code in the /// provided `Module` to the architecture of the provided `TargetMachine`, /// and execute it. @@ -41,6 +45,8 @@ public final class JIT { /// - machine: The target machine which you're compiling for /// - throws: JITError public init(module: Module, machine: TargetMachine) throws { + _ = JIT.linkOnce + var jit: LLVMExecutionEngineRef? var error: UnsafeMutablePointer? if LLVMCreateExecutionEngineForModule(&jit, module.llvm, &error) != 0 { @@ -54,22 +60,6 @@ public final class JIT { LLVMRunStaticConstructors(self.llvm) } - /// Runs the specified function with the provided arguments by compiling - /// it to machine code for the target architecture used to initialize this - /// JIT. - /// - /// - parameters: - /// - function: The function you wish to execute - /// - args: The arguments you wish to pass to the function - /// - returns: The LLVM value that the function returned - public func runFunction(_ function: Function, args: [IRValue]) -> IRValue { - var irArgs = args.map { $0.asLLVM() as Optional } - return irArgs.withUnsafeMutableBufferPointer { buf in - return LLVMRunFunction(llvm, function.asLLVM(), - UInt32(buf.count), buf.baseAddress) - } - } - /// Retrieves a pointer to the function compiled by this JIT. /// - parameter name: The name of the function you wish to look up. /// - returns: A pointer to the result of compiling the specified function. diff --git a/Tests/LLVMTests/JITSpec.swift b/Tests/LLVMTests/JITSpec.swift new file mode 100644 index 00000000..c664b3d1 --- /dev/null +++ b/Tests/LLVMTests/JITSpec.swift @@ -0,0 +1,79 @@ +import LLVM +import XCTest +import FileCheck +import Foundation + +class JITSpec : XCTestCase { + func testFibonacci() { + XCTAssert(fileCheckOutput(withPrefixes: ["JIT"]) { + let module = Module(name: "Fibonacci") + let builder = IRBuilder(module: module) + + let function = builder.addFunction( + "calculateFibs", + type: FunctionType(argTypes: [IntType.int1], + returnType: FloatType.double) + ) + let entryBB = function.appendBasicBlock(named: "entry") + builder.positionAtEnd(of: entryBB) + + // allocate space for a local value + let local = builder.buildAlloca(type: FloatType.double, name: "local") + + // Compare to the condition + let test = builder.buildICmp(function.parameters[0], IntType.int1.zero(), .equal) + + // Create basic blocks for "then", "else", and "merge" + let thenBB = function.appendBasicBlock(named: "then") + let elseBB = function.appendBasicBlock(named: "else") + let mergeBB = function.appendBasicBlock(named: "merge") + + builder.buildCondBr(condition: test, then: thenBB, else: elseBB) + + // MARK: Then Block + + builder.positionAtEnd(of: thenBB) + // local = 1/89, the fibonacci series (sort of) + let thenVal = FloatType.double.constant(1/89) + // Branch to the merge block + builder.buildBr(mergeBB) + + // MARK: Else Block + builder.positionAtEnd(of: elseBB) + // local = 1/109, the fibonacci series (sort of) backwards + let elseVal = FloatType.double.constant(1/109) + // Branch to the merge block + builder.buildBr(mergeBB) + + // MARK: Merge Block + + builder.positionAtEnd(of: mergeBB) + let phi = builder.buildPhi(FloatType.double, name: "phi_example") + phi.addIncoming([ + (thenVal, thenBB), + (elseVal, elseBB), + ]) + builder.buildStore(phi, to: local) + let ret = builder.buildLoad(local, name: "ret") + builder.buildRet(ret) + + // Setup the JIT + let jit = try! JIT(module: module, machine: TargetMachine()) + typealias FnPtr = @convention(c) (Bool) -> Double + // Retrieve a handle to the function we're going to invoke + let fnAddr = jit.addressOfFunction(name: "calculateFibs") + let fn = unsafeBitCast(fnAddr, to: FnPtr.self) + // JIT: 0.00917431192660551 + print(fn(true)) + // JIT-NEXT: 0.0112359550561798 + print(fn(false)) + }) + } + + #if !os(macOS) + static var allTests = testCase([ + ("testFibonacci", testFibonacci), + ]) + #endif +} + diff --git a/Tests/LinuxMain.swift b/Tests/LinuxMain.swift index f7bbade9..6d8a4267 100644 --- a/Tests/LinuxMain.swift +++ b/Tests/LinuxMain.swift @@ -9,5 +9,6 @@ XCTMain([ IRExceptionSpec.allTests, IROperationSpec.allTests, FileCheckSpec.allTests, + JITSpec.allTests, ]) #endif