From 0f1c6d2f60a81c51bdbcf4b6211e23d76add0582 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Sat, 13 Jan 2018 02:46:31 -0500 Subject: [PATCH 1/5] Fix thinko in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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") From 853cbd9a0e0f2d5d946108c22145ca9cc8f6369e Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Sat, 13 Jan 2018 02:46:56 -0500 Subject: [PATCH 2/5] Link MCJIT when first JIT is created --- Sources/LLVM/JIT.swift | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Sources/LLVM/JIT.swift b/Sources/LLVM/JIT.swift index 497bdde2..386fe5f9 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 { From 664bddf02f1bc32cd27802f9cd4224f6d183f6b4 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Sat, 13 Jan 2018 02:47:13 -0500 Subject: [PATCH 3/5] Test the JIT --- Tests/LLVMTests/JITSpec.swift | 79 +++++++++++++++++++++++++++++++++++ Tests/LinuxMain.swift | 1 + 2 files changed, 80 insertions(+) create mode 100644 Tests/LLVMTests/JITSpec.swift 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 From 7806e634e20d43135b9830e654ed707fde060258 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Sat, 13 Jan 2018 02:47:40 -0500 Subject: [PATCH 4/5] Add GenericValue APIs to JIT And correct the type of runFunction --- Sources/LLVM/JIT.swift | 125 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 121 insertions(+), 4 deletions(-) diff --git a/Sources/LLVM/JIT.swift b/Sources/LLVM/JIT.swift index 386fe5f9..6cabf562 100644 --- a/Sources/LLVM/JIT.swift +++ b/Sources/LLVM/JIT.swift @@ -24,6 +24,123 @@ public enum JITError: Error, CustomStringConvertible { } } +public final class JITValue { + fileprivate let llvm: LLVMGenericValueRef + fileprivate init(llvm: LLVMGenericValueRef) { + self.llvm = llvm + } + + public convenience init?(int value: Int) { + guard + let llvm = LLVMCreateGenericValueOfInt(value.type.asLLVM(), + UInt64(bitPattern: Int64(value)), + /*signed:*/ true.llvm) + else { + return nil + } + self.init(llvm: llvm) + } + + public convenience init?(int value: UInt) { + guard + let llvm = LLVMCreateGenericValueOfInt(value.type.asLLVM(), + UInt64(value), + /*signed:*/ false.llvm) + else { + return nil + } + self.init(llvm: llvm) + } + + public convenience init?(int value: Float) { + guard + let llvm = LLVMCreateGenericValueOfFloat(FloatType.float.asLLVM(), Double(value)) + else { + return nil + } + self.init(llvm: llvm) + } + + public convenience init?(int value: Double) { + guard + let llvm = LLVMCreateGenericValueOfFloat(FloatType.double.asLLVM(), value) + else { + return nil + } + self.init(llvm: llvm) + } + + + public convenience init?(pointer value: UnsafeMutableRawPointer?) { + guard + let llvm = LLVMCreateGenericValueOfPointer(value) + else { + return nil + } + self.init(llvm: llvm) + } + + public convenience init?(int value: Constant) { + let intValue = LLVMConstIntGetSExtValue(value.asLLVM()) + guard + let llvm = LLVMCreateGenericValueOfInt(value.type.asLLVM(), + UInt64(bitPattern: intValue), + /*signed:*/ true.llvm) + else { + return nil + } + self.init(llvm: llvm) + } + + public convenience init?(int value: Constant) { + let intValue = LLVMConstIntGetZExtValue(value.asLLVM()) + guard + let llvm = LLVMCreateGenericValueOfInt(value.type.asLLVM(), + intValue, /*signed:*/ false.llvm) + else { + return nil + } + self.init(llvm: llvm) + } + + public convenience init?(float value: Constant) { + var infoLost: LLVMBool = 0 + let floatValue = LLVMConstRealGetDouble(value.asLLVM(), &infoLost) + guard infoLost == 0 else { + return nil + } + guard + let llvm = LLVMCreateGenericValueOfFloat(value.type.asLLVM(), floatValue) + else { + return nil + } + self.init(llvm: llvm) + } + + deinit { + LLVMDisposeGenericValue(self.llvm) + } + + public var bitwidth: UInt { + let width = LLVMGenericValueIntWidth(self.llvm) + return UInt(width) + } + + public var intValue: Int { + let int = LLVMGenericValueToInt(self.llvm, /*signed:*/ true.llvm) + return Int(bitPattern: UInt(int)) + } + + public var unsignedIntValue: UInt { + let int = LLVMGenericValueToInt(self.llvm, /*signed:*/ false.llvm) + return UInt(int) + } + + public var pointerValue: UnsafeMutableRawPointer? { + return LLVMGenericValueToPointer(self.llvm) + } +} + /// A `JIT` is a Just-In-Time compiler that will compile and execute LLVM IR /// that has been generated in a `Module`. It can execute arbitrary functions /// and return the value the function generated, allowing you to write @@ -68,11 +185,11 @@ public final class JIT { /// - 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 } + public func runFunction(_ function: Function, args: [JITValue]) -> JITValue { + var irArgs = args.map { $0.llvm as Optional } return irArgs.withUnsafeMutableBufferPointer { buf in - return LLVMRunFunction(llvm, function.asLLVM(), - UInt32(buf.count), buf.baseAddress) + return JITValue(llvm: LLVMRunFunction(llvm, function.asLLVM(), + UInt32(buf.count), buf.baseAddress)) } } From 4e53572a8cd2c0eb970b8c21dd29d1fe01667819 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Sat, 13 Jan 2018 10:59:53 -0500 Subject: [PATCH 5/5] Remove JIT.runFunction MCJIT::runFunction does not support full-featured argument passing. --- Sources/LLVM/JIT.swift | 133 ----------------------------------------- 1 file changed, 133 deletions(-) diff --git a/Sources/LLVM/JIT.swift b/Sources/LLVM/JIT.swift index 6cabf562..55261d99 100644 --- a/Sources/LLVM/JIT.swift +++ b/Sources/LLVM/JIT.swift @@ -24,123 +24,6 @@ public enum JITError: Error, CustomStringConvertible { } } -public final class JITValue { - fileprivate let llvm: LLVMGenericValueRef - fileprivate init(llvm: LLVMGenericValueRef) { - self.llvm = llvm - } - - public convenience init?(int value: Int) { - guard - let llvm = LLVMCreateGenericValueOfInt(value.type.asLLVM(), - UInt64(bitPattern: Int64(value)), - /*signed:*/ true.llvm) - else { - return nil - } - self.init(llvm: llvm) - } - - public convenience init?(int value: UInt) { - guard - let llvm = LLVMCreateGenericValueOfInt(value.type.asLLVM(), - UInt64(value), - /*signed:*/ false.llvm) - else { - return nil - } - self.init(llvm: llvm) - } - - public convenience init?(int value: Float) { - guard - let llvm = LLVMCreateGenericValueOfFloat(FloatType.float.asLLVM(), Double(value)) - else { - return nil - } - self.init(llvm: llvm) - } - - public convenience init?(int value: Double) { - guard - let llvm = LLVMCreateGenericValueOfFloat(FloatType.double.asLLVM(), value) - else { - return nil - } - self.init(llvm: llvm) - } - - - public convenience init?(pointer value: UnsafeMutableRawPointer?) { - guard - let llvm = LLVMCreateGenericValueOfPointer(value) - else { - return nil - } - self.init(llvm: llvm) - } - - public convenience init?(int value: Constant) { - let intValue = LLVMConstIntGetSExtValue(value.asLLVM()) - guard - let llvm = LLVMCreateGenericValueOfInt(value.type.asLLVM(), - UInt64(bitPattern: intValue), - /*signed:*/ true.llvm) - else { - return nil - } - self.init(llvm: llvm) - } - - public convenience init?(int value: Constant) { - let intValue = LLVMConstIntGetZExtValue(value.asLLVM()) - guard - let llvm = LLVMCreateGenericValueOfInt(value.type.asLLVM(), - intValue, /*signed:*/ false.llvm) - else { - return nil - } - self.init(llvm: llvm) - } - - public convenience init?(float value: Constant) { - var infoLost: LLVMBool = 0 - let floatValue = LLVMConstRealGetDouble(value.asLLVM(), &infoLost) - guard infoLost == 0 else { - return nil - } - guard - let llvm = LLVMCreateGenericValueOfFloat(value.type.asLLVM(), floatValue) - else { - return nil - } - self.init(llvm: llvm) - } - - deinit { - LLVMDisposeGenericValue(self.llvm) - } - - public var bitwidth: UInt { - let width = LLVMGenericValueIntWidth(self.llvm) - return UInt(width) - } - - public var intValue: Int { - let int = LLVMGenericValueToInt(self.llvm, /*signed:*/ true.llvm) - return Int(bitPattern: UInt(int)) - } - - public var unsignedIntValue: UInt { - let int = LLVMGenericValueToInt(self.llvm, /*signed:*/ false.llvm) - return UInt(int) - } - - public var pointerValue: UnsafeMutableRawPointer? { - return LLVMGenericValueToPointer(self.llvm) - } -} - /// A `JIT` is a Just-In-Time compiler that will compile and execute LLVM IR /// that has been generated in a `Module`. It can execute arbitrary functions /// and return the value the function generated, allowing you to write @@ -177,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: [JITValue]) -> JITValue { - var irArgs = args.map { $0.llvm as Optional } - return irArgs.withUnsafeMutableBufferPointer { buf in - return JITValue(llvm: 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.