From a024efb29995ee5c9b60ca662ea30de17dc02bd0 Mon Sep 17 00:00:00 2001 From: Harlan Haskins Date: Wed, 11 Jan 2017 00:15:46 -0500 Subject: [PATCH 1/2] Added JIT compiler abstraction --- Sources/LLVM/JIT.swift | 112 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 Sources/LLVM/JIT.swift diff --git a/Sources/LLVM/JIT.swift b/Sources/LLVM/JIT.swift new file mode 100644 index 00000000..5afc0945 --- /dev/null +++ b/Sources/LLVM/JIT.swift @@ -0,0 +1,112 @@ +import cllvm + +/// JITError represents the different kinds of errors the JIT compiler can throw +public enum JITError: Error, CustomStringConvertible { + /// The JIT was unable to be initialized. A message is provided explaining + /// the failure. + case couldNotInitialize(String) + + /// A human-readable description of the error. + public var description: String { + switch self { + case .couldNotInitialize(let message): + return "could not initialize JIT: \(message)" + } + } +} + +/// 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 interactive +/// programs that will run as soon as they are compiled. +class JIT { + /// The underlying LLVMExecutionEngineRef backing this JIT + internal let llvm: LLVMExecutionEngineRef + + /// 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. + /// + /// - parameters: + /// - module: The module containing code you wish to execute + /// - machine: The target machine which you're compiling for + /// - throws: JITError + init(module: Module, machine: TargetMachine) throws { + var jit: LLVMExecutionEngineRef? + var error: UnsafeMutablePointer? + if LLVMCreateExecutionEngineForModule(&jit, module.llvm, &error) != 0 { + let str = String(cString: error!) + throw JITError.couldNotInitialize(str) + } + guard let _jit = jit else { + throw JITError.couldNotInitialize("JIT was NULL") + } + self.llvm = _jit + } + + + /// Runs the specified function with the provided arguments by compiling + /// it to machine code for your specific architecture. + /// + /// - 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 + 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) + } + } + + + /// Runs the specified function as if it were the `main` function in an + /// executable. It takes an array of argument strings and passes them + /// into the function as `argc` and `argv`. + /// + /// - parameters: + /// - function: The `main` function you wish to execute + /// - args: The string arguments you wish to pass to the function + /// - returns: The numerical exit code returned by the function + func runFunctionAsMain(_ function: Function, args: [String]) -> Int { + // FIXME: Also add in envp. + return withCArrayOfCStrings(args) { buf in + return Int(LLVMRunFunctionAsMain(llvm, function.asLLVM(), + UInt32(buf.count), + buf.baseAddress, nil)) + } + } +} + + +/// Runs the provided block with the equivalent C strings copied from the +/// passed-in array. The C strings will only be alive for the duration +/// of the block, and they will be freed when the block exits. +/// +/// - parameters: +/// - strings: The strings you intend to convert to C strings +/// - block: A block that uses the C strings +/// - returns: The result of the passed-in block. +/// - throws: Will only throw if the passed-in block throws. +internal func withCArrayOfCStrings(_ strings: [String], _ block: + (UnsafeBufferPointer?>) throws -> T) rethrows -> T { + var cStrings = [UnsafeMutablePointer?]() + for string in strings { + string.withCString { + cStrings.append(strdup($0)) + } + } + defer { + for cStr in cStrings { + free(cStr) + } + } + return try cStrings.withUnsafeBufferPointer { buf in + // We need to make this "immutable" but that doesn't change + // their size or contents. + let constPtr = unsafeBitCast(buf.baseAddress, + to: UnsafePointer?>.self) + return try block(UnsafeBufferPointer(start: constPtr, count: buf.count)) + } +} From 8471f8a046a7950c6437594468d12ecc65bf62e2 Mon Sep 17 00:00:00 2001 From: Harlan Haskins Date: Wed, 11 Jan 2017 11:09:05 -0500 Subject: [PATCH 2/2] Added missing access control and fixed comment nits. --- Sources/LLVM/JIT.swift | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/Sources/LLVM/JIT.swift b/Sources/LLVM/JIT.swift index 5afc0945..e4aa49c3 100644 --- a/Sources/LLVM/JIT.swift +++ b/Sources/LLVM/JIT.swift @@ -1,6 +1,7 @@ import cllvm -/// JITError represents the different kinds of errors the JIT compiler can throw +/// JITError represents the different kinds of errors the JIT compiler can +/// throw. public enum JITError: Error, CustomStringConvertible { /// The JIT was unable to be initialized. A message is provided explaining /// the failure. @@ -15,12 +16,12 @@ public enum JITError: Error, CustomStringConvertible { } } -/// 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 interactive -/// programs that will run as soon as they are compiled. -class JIT { - /// The underlying LLVMExecutionEngineRef backing this JIT +/// 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 +/// interactive programs that will run as soon as they are compiled. +public final class JIT { + /// The underlying LLVMExecutionEngineRef backing this JIT. internal let llvm: LLVMExecutionEngineRef /// Creates a Just In Time compiler that will compile the code in the @@ -31,7 +32,7 @@ class JIT { /// - module: The module containing code you wish to execute /// - machine: The target machine which you're compiling for /// - throws: JITError - init(module: Module, machine: TargetMachine) throws { + public init(module: Module, machine: TargetMachine) throws { var jit: LLVMExecutionEngineRef? var error: UnsafeMutablePointer? if LLVMCreateExecutionEngineForModule(&jit, module.llvm, &error) != 0 { @@ -46,13 +47,14 @@ class JIT { /// Runs the specified function with the provided arguments by compiling - /// it to machine code for your specific architecture. + /// 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 - func runFunction(_ function: Function, args: [IRValue]) -> IRValue { + 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(), @@ -69,7 +71,7 @@ class JIT { /// - function: The `main` function you wish to execute /// - args: The string arguments you wish to pass to the function /// - returns: The numerical exit code returned by the function - func runFunctionAsMain(_ function: Function, args: [String]) -> Int { + public func runFunctionAsMain(_ function: Function, args: [String]) -> Int { // FIXME: Also add in envp. return withCArrayOfCStrings(args) { buf in return Int(LLVMRunFunctionAsMain(llvm, function.asLLVM(),