Skip to content

Document Some Values #3

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jan 9, 2017
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
61 changes: 36 additions & 25 deletions Sources/LLVMSwift/Global.swift
Original file line number Diff line number Diff line change
@@ -1,29 +1,40 @@
import cllvm

/// A `Global` represents a region of memory allocated at compile time instead
/// of at runtime. A global variable must either have an initializer, or make
/// reference to an external definition that has an initializer.
public struct Global: IRValue {
internal let llvm: LLVMValueRef

public var isExternallyInitialized: Bool {
get { return LLVMIsExternallyInitialized(llvm) != 0 }
set { LLVMSetExternallyInitialized(llvm, newValue.llvm) }
}

public var initializer: IRValue {
get { return LLVMGetInitializer(asLLVM()) }
set { LLVMSetInitializer(asLLVM(), newValue.asLLVM()) }
}

public var isGlobalConstant: Bool {
get { return LLVMIsGlobalConstant(asLLVM()) != 0 }
set { LLVMSetGlobalConstant(asLLVM(), newValue.llvm) }
}

public var isThreadLocal: Bool {
get { return LLVMIsThreadLocal(asLLVM()) != 0 }
set { LLVMSetThreadLocal(asLLVM(), newValue.llvm) }
}

public func asLLVM() -> LLVMValueRef {
return llvm
}
internal let llvm: LLVMValueRef

/// Returns whether this global variable has no initializer because it makes
/// reference to an initialized value in another translation unit.
public var isExternallyInitialized: Bool {
get { return LLVMIsExternallyInitialized(llvm) != 0 }
set { LLVMSetExternallyInitialized(llvm, newValue.llvm) }
}

/// Retrieves the initializer for this global variable, if it exists.
public var initializer: IRValue? {
get { return LLVMGetInitializer(asLLVM()) }
set { LLVMSetInitializer(asLLVM(), newValue!.asLLVM()) }
}

/// Returns whether this global variable is a constant, whether or not the
/// final definition of the global is not.
public var isGlobalConstant: Bool {
get { return LLVMIsGlobalConstant(asLLVM()) != 0 }
set { LLVMSetGlobalConstant(asLLVM(), newValue.llvm) }
}

/// Returns whether this global variable is thread-local. That is, returns
/// if this variable is not shared by multiple threads.
public var isThreadLocal: Bool {
get { return LLVMIsThreadLocal(asLLVM()) != 0 }
set { LLVMSetThreadLocal(asLLVM(), newValue.llvm) }
}

/// Retrieves the underlying LLVM value object.
public func asLLVM() -> LLVMValueRef {
return llvm
}
}
73 changes: 64 additions & 9 deletions Sources/LLVMSwift/Module.swift
Original file line number Diff line number Diff line change
@@ -1,18 +1,31 @@
import cllvm

/// A `Context` represents execution states for the core LLVM IR system.
public class Context {
internal let llvm: LLVMContextRef

/// Retrieves the global context instance.
public static let global = Context(llvm: LLVMGetGlobalContext()!)

/// Creates a `Context` object from an `LLVMContextRef` object.
public init(llvm: LLVMContextRef) {
self.llvm = llvm
}
}

/// Represents the possible errors that can be thrown while interacting with a
/// `Module` object.
public enum ModuleError: Error, CustomStringConvertible {
/// Thrown when a module does not pass the module verification process.
/// Includes the reason the module did not pass verification.
case didNotPassVerification(String)
/// Thrown when a module cannot be printed at a given path. Provides the
/// erroneous path and a deeper reason why printing to that path failed.
case couldNotPrint(path: String, error: String)
/// Thrown when a module cannot emit bitcode because it contains erroneous
/// declarations.
case couldNotEmitBitCode(path: String)

public var description: String {
switch self {
case .didNotPassVerification(let message):
Expand All @@ -25,8 +38,17 @@ public enum ModuleError: Error, CustomStringConvertible {
}
}

/// A `Module` represents the top-level structure of an LLVM program. An LLVM
/// module is effectively a translation unit or a collection of translation
/// units merged together.
public final class Module {
internal let llvm: LLVMModuleRef

/// Creates a `Module` with the given name.
///
/// - parameter name: The name of the module.
/// - parameter context: The context to associate this module with. If no
/// context is provided, one will be inferred.
public init(name: String, context: Context? = nil) {
if let context = context {
llvm = LLVMModuleCreateWithNameInContext(name, context.llvm)
Expand All @@ -36,13 +58,21 @@ public final class Module {
self.context = Context(llvm: LLVMGetModuleContext(llvm)!)
}
}


/// Returns the context associated with this module.
public let context: Context


/// Obtain the data layout for this module.
public var dataLayout: TargetData {
return TargetData(llvm: LLVMGetModuleDataLayout(llvm))
}


/// Print a representation of a module to a file at the given path.
///
/// If the provided path is not suitable for writing, this function will throw
/// `ModuleError.couldNotPrint`.
///
/// - parameter path: The path to write the module's representation to.
public func print(to path: String) throws {
var err: UnsafeMutablePointer<Int8>?
path.withCString { cString in
Expand All @@ -55,7 +85,13 @@ public final class Module {
throw ModuleError.couldNotPrint(path: path, error: String(cString: err))
}
}


/// Writes the bitcode of elements in this module to a file at the given path.
///
/// If the provided path is not suitable for writing, this function will throw
/// `ModuleError.couldNotEmitBitCode`.
///
/// - parameter path: The path to write the module's representation to.
public func emitBitCode(to path: String) throws {
let status = path.withCString { cString -> Int32 in
let mutable = strdup(cString)
Expand All @@ -67,17 +103,35 @@ public final class Module {
throw ModuleError.couldNotEmitBitCode(path: path)
}
}


/// Searches for and retrieves a type with the given name in this module if
/// that name references an existing type.
///
/// - parameter name: The name of the type to create.
///
/// - returns: A representation of the newly created type with the given name
/// or nil if such a representation could not be created.
public func type(named name: String) -> IRType? {
guard let type = LLVMGetTypeByName(llvm, name) else { return nil }
return convertType(type)
}


/// Searches for and retrieves a function with the given name in this module
/// if that name references an existing function.
///
/// - parameter name: The name of the function to create.
///
/// - returns: A representation of the newly created function with the given
/// name or nil if such a representation could not be created.
public func function(named name: String) -> Function? {
guard let fn = LLVMGetNamedFunction(llvm, name) else { return nil }
return Function(llvm: fn)
}


/// Verifies that this module is valid, taking the specified action if not.
/// If this module did not pass verification, a description of any invalid
/// constructs is provided with the thrown
/// `ModuleError.didNotPassVerification` error.
public func verify() throws {
var message: UnsafeMutablePointer<Int8>?
let status = Int(LLVMVerifyModule(llvm, LLVMReturnStatusAction, &message))
Expand All @@ -86,7 +140,8 @@ public final class Module {
throw ModuleError.didNotPassVerification(String(cString: message))
}
}


/// Dump a representation of this module to stderr.
public func dump() {
LLVMDumpModule(llvm)
}
Expand Down
66 changes: 59 additions & 7 deletions Sources/LLVMSwift/PhiNode.swift
Original file line number Diff line number Diff line change
@@ -1,12 +1,47 @@
import cllvm

/// A `PhiNode` object represents a PHI node.
///
/// Because all instructions in LLVM IR are in SSA (Single Static Assignment)
/// form, a PHI node is necessary when the value of a variable assignment
/// depends on the path the flow of control takes through the program. For
/// example:
///
/// ```swift
/// var a = 1
/// if c == 42 {
/// a = 2
/// }
/// let b = a
/// ```
///
/// The value of `b` in this program depends on the value of the condition
/// involving the variable `c`. Because `b` must be assigned to once, a PHI
/// node is created and the program effectively is transformed into the
/// following:
///
/// ```swift
/// let aNoCond = 1
/// if c == 42 {
/// let aCondTrue = 2
/// }
/// let b = PHI(aNoCond, aCondTrue)
/// ```
///
/// If the flow of control reaches `aCondTrue`, the value of `b` is `2`, else it
/// is `1` and SSA semantics are preserved.
public struct PhiNode: IRValue {
internal let llvm: LLVMValueRef


/// Adds a list of incoming value and their associated basic blocks to the end
/// of the list of incoming values for this PHI node.
///
/// - parameter valueMap: A list of incoming values and their associated basic
/// blocks.
public func addIncoming(_ valueMap: [(IRValue, BasicBlock)]) {
var values = valueMap.map { $0.0.asLLVM() as Optional }
var blocks = valueMap.map { $0.1.asLLVM() as Optional }

values.withUnsafeMutableBufferPointer { valueBuf in
blocks.withUnsafeMutableBufferPointer { blockBuf in
LLVMAddIncoming(llvm,
Expand All @@ -16,27 +51,44 @@ public struct PhiNode: IRValue {
}
}
}


/// Obtain the incoming values and their associated basic blocks for this PHI
/// node.
public var incoming: [(IRValue, BasicBlock)] {
let count = Int(LLVMCountIncoming(llvm))
var values = [(IRValue, BasicBlock)]()
for i in 0..<count {
guard let value = incomingValue(at: i),
let block = incomingBlock(at: i) else { continue }
let block = incomingBlock(at: i) else { continue }
values.append((value, block))
}
return values
}


/// Retrieves the incoming value for the given index for this PHI node, if it
/// exists.
///
/// - parameter index: The index of the incoming value to retrieve.
///
/// - returns: A value representing the incoming value to this PHI node at
/// the given index, if it exists.
public func incomingValue(at index: Int) -> IRValue? {
return LLVMGetIncomingValue(llvm, UInt32(index))
}


/// Retrieves the incoming basic block for the given index for this PHI node,
/// if it exists.
///
/// - parameter index: The index of the incoming basic block to retrieve.
///
/// - returns: A value representing the incoming basic block to this PHI node
/// at the given index, if it exists.
public func incomingBlock(at index: Int) -> BasicBlock? {
guard let blockRef = LLVMGetIncomingBlock(llvm, UInt32(index)) else { return nil }
return BasicBlock(llvm: blockRef)
}


/// Retrieves the underlying LLVM value object.
public func asLLVM() -> LLVMValueRef {
return llvm
}
Expand Down
29 changes: 20 additions & 9 deletions Sources/LLVMSwift/Switch.swift
Original file line number Diff line number Diff line change
@@ -1,13 +1,24 @@
import cllvm

/// A `Switch` represents a `switch` instruction. A `switch` instruction
/// defines a jump table of values and destination basic blocks to pass the flow
/// of control to if a condition value matches. If no match is made, control
/// flow passes to the default basic block.
public struct Switch: IRValue {
internal let llvm: LLVMValueRef

public func addCase(_ value: IRValue, _ block: BasicBlock) {
LLVMAddCase(llvm, value.asLLVM(), block.asLLVM())
}

public func asLLVM() -> LLVMValueRef {
return llvm
}
internal let llvm: LLVMValueRef

/// Inserts a case with the given value and destination basic block in the
/// jump table of this `switch` instruction.
///
/// - parameter value: The value that acts as the selector for this case.
/// - parameter block: The destination block for the flow of control if this
/// case is matched.
public func addCase(_ value: IRValue, _ block: BasicBlock) {
LLVMAddCase(llvm, value.asLLVM(), block.asLLVM())
}

/// Retrieves the underlying LLVM value object.
public func asLLVM() -> LLVMValueRef {
return llvm
}
}
33 changes: 19 additions & 14 deletions Sources/LLVMSwift/Use.swift
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
import cllvm

/// `Use` represents an iterator over the uses and users of a particular value
/// in an LLVM program.
public struct Use {
internal let llvm: LLVMUseRef

public func next() -> Use? {
guard let next = LLVMGetNextUse(llvm) else { return nil }
return Use(llvm: next)
}

public func user() -> IRValue? {
return LLVMGetUser(llvm)
}

public func usedValue() -> IRValue? {
return LLVMGetUsedValue(llvm)
}
internal let llvm: LLVMUseRef

/// Retrieves the next use of a value.
public func next() -> Use? {
guard let next = LLVMGetNextUse(llvm) else { return nil }
return Use(llvm: next)
}

/// Obtain the user value for this `User` object.
public func user() -> IRValue? {
return LLVMGetUser(llvm)
}

/// Obtain the value this `User` object corresponds to.
public func usedValue() -> IRValue? {
return LLVMGetUsedValue(llvm)
}
}