From eee41b5d40935acecbfa3f9f907b48e276bb9d08 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Mon, 5 Nov 2018 15:19:48 -0500 Subject: [PATCH 1/5] Fixup COMDAT selection kind - fix the example the documentation - Rename ComdatSelectionKind to Comdat.SelectionKind --- Sources/LLVM/Comdat.swift | 110 +++++++++++++++++++------------------- 1 file changed, 56 insertions(+), 54 deletions(-) diff --git a/Sources/LLVM/Comdat.swift b/Sources/LLVM/Comdat.swift index 18edb074..2d542755 100644 --- a/Sources/LLVM/Comdat.swift +++ b/Sources/LLVM/Comdat.swift @@ -42,7 +42,7 @@ import cllvm /// /// var g1 = builder.addGlobal("g1", initializer: IntType.int8.constant(42)) /// g1.comdat = foo -/// var g2 = builder.addGlobal("g1", initializer: IntType.int8.constant(42)) +/// var g2 = builder.addGlobal("g2", initializer: IntType.int8.constant(42)) /// g2.comdat = bar /// /// From the object file perspective, this requires the creation of two sections @@ -57,66 +57,68 @@ public class Comdat { } /// The selection kind for this COMDAT section. - public var selectionKind: ComdatSelectionKind { - get { return ComdatSelectionKind(llvm: LLVMGetComdatSelectionKind(self.llvm)) } + public var selectionKind: Comdat.SelectionKind { + get { return Comdat.SelectionKind(llvm: LLVMGetComdatSelectionKind(self.llvm)) } set { LLVMSetComdatSelectionKind(self.llvm, newValue.llvm) } } } -/// A `ComdatSelectionKind` describes the behavior of the linker when -/// linking COMDAT sections. -public enum ComdatSelectionKind { - /// The linker may choose any COMDAT section with a matching key. - /// - /// This selection kind is the most relaxed - any section with the same key - /// but not necessarily identical size or contents can be chosen. Precisely - /// which section is chosen is implementation-defined. - /// - /// This selection kind is the default for all newly-inserted sections. - case any - /// The linker may choose any identically-keyed COMDAT section and requires - /// all other referenced data to match its selection's referenced data. - /// - /// This selection kind requires that the data in each COMDAT section be - /// identical in length and content. Inclusion of multiple non-identical - /// COMDAT sections with the same key is an error. - /// - /// For global objects in LLVM, identical contents is defined to mean that - /// their initializers point to the same global `IRValue`. - case exactMatch - /// The linker chooses the identically-keyed COMDAT section with the largest - /// size, ignoring content. - case largest - /// The COMDAT section with this key is unique. - /// - /// This selection requires that no other COMDAT section have the same key - /// as this section, making the choice of selection unambiguous. Inclusion - /// of any other COMDAT section with the same key is an error. - case noDuplicates - /// The linker may choose any identically-keyed COMDAT section and requires - /// all other sections to have the same size as its selection. - case sameSize +extension Comdat { + /// A `Comdat.SelectionKind` describes the behavior of the linker when + /// linking COMDAT sections. + public enum SelectionKind { + /// The linker may choose any COMDAT section with a matching key. + /// + /// This selection kind is the most relaxed - any section with the same key + /// but not necessarily identical size or contents can be chosen. Precisely + /// which section is chosen is implementation-defined. + /// + /// This selection kind is the default for all newly-inserted sections. + case any + /// The linker may choose any identically-keyed COMDAT section and requires + /// all other referenced data to match its selection's referenced data. + /// + /// This selection kind requires that the data in each COMDAT section be + /// identical in length and content. Inclusion of multiple non-identical + /// COMDAT sections with the same key is an error. + /// + /// For global objects in LLVM, identical contents is defined to mean that + /// their initializers point to the same global `IRValue`. + case exactMatch + /// The linker chooses the identically-keyed COMDAT section with the largest + /// size, ignoring content. + case largest + /// The COMDAT section with this key is unique. + /// + /// This selection requires that no other COMDAT section have the same key + /// as this section, making the choice of selection unambiguous. Inclusion + /// of any other COMDAT section with the same key is an error. + case noDuplicates + /// The linker may choose any identically-keyed COMDAT section and requires + /// all other sections to have the same size as its selection. + case sameSize - internal init(llvm: LLVMComdatSelectionKind) { - switch llvm { - case LLVMAnyComdatSelectionKind: self = .any - case LLVMExactMatchComdatSelectionKind: self = .exactMatch - case LLVMLargestComdatSelectionKind: self = .largest - case LLVMNoDuplicatesComdatSelectionKind: self = .noDuplicates - case LLVMSameSizeComdatSelectionKind: self = .sameSize - default: fatalError("unknown comdat selection kind \(llvm)") + internal init(llvm: LLVMComdatSelectionKind) { + switch llvm { + case LLVMAnyComdatSelectionKind: self = .any + case LLVMExactMatchComdatSelectionKind: self = .exactMatch + case LLVMLargestComdatSelectionKind: self = .largest + case LLVMNoDuplicatesComdatSelectionKind: self = .noDuplicates + case LLVMSameSizeComdatSelectionKind: self = .sameSize + default: fatalError("unknown comdat selection kind \(llvm)") + } } - } - private static let comdatMapping: [ComdatSelectionKind: LLVMComdatSelectionKind] = [ - .any: LLVMAnyComdatSelectionKind, - .exactMatch: LLVMExactMatchComdatSelectionKind, - .largest: LLVMLargestComdatSelectionKind, - .noDuplicates: LLVMNoDuplicatesComdatSelectionKind, - .sameSize: LLVMSameSizeComdatSelectionKind, - ] + private static let comdatMapping: [Comdat.SelectionKind: LLVMComdatSelectionKind] = [ + .any: LLVMAnyComdatSelectionKind, + .exactMatch: LLVMExactMatchComdatSelectionKind, + .largest: LLVMLargestComdatSelectionKind, + .noDuplicates: LLVMNoDuplicatesComdatSelectionKind, + .sameSize: LLVMSameSizeComdatSelectionKind, + ] - fileprivate var llvm: LLVMComdatSelectionKind { - return ComdatSelectionKind.comdatMapping[self]! + fileprivate var llvm: LLVMComdatSelectionKind { + return SelectionKind.comdatMapping[self]! + } } } From c263fc6d6d1e0f716b9883e2f70d8555b362fef4 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Mon, 5 Nov 2018 15:20:05 -0500 Subject: [PATCH 2/5] Update TLS documentation --- Sources/LLVM/Global.swift | 60 ++++++++++++++++++++++++++++++++------- 1 file changed, 49 insertions(+), 11 deletions(-) diff --git a/Sources/LLVM/Global.swift b/Sources/LLVM/Global.swift index c661c2d0..2829f0fe 100644 --- a/Sources/LLVM/Global.swift +++ b/Sources/LLVM/Global.swift @@ -5,27 +5,63 @@ import cllvm /// Enumerates the supported models of reference of thread-local variables. /// /// These models are listed from the most general, but least optimized, to the -/// fastest, but most restrictive. +/// fastest, but most restrictive in general, as architectural differences +/// play a role in determining the access patterns for thread-local storage. /// -/// Documentation of these models quotes the [Oracle Linker and Libraries -/// Guide](https://docs.oracle.com/cd/E23824_01/html/819-0690/chapter8-20.html). +/// In general, support for thread-local storage in statically-linked +/// applications is limited: some platforms may not even define the behavior of +/// TLS in such cases. This is usually not an issue as statically-linked code +/// only ever has one TLS block, the offset of the variables within that block +/// is known, and support for additional dynamic loading of code in +/// statically-linked code is limited. +/// +/// Computing the thread-specific address of a TLS variable is usually a dynamic +/// process that relies on an ABI-defined function call (usually +/// `__tls_get_addr`) to do the heavy lifting. +/// +/// TLS access models fall into two classes: static and dynamic. Regardless of +/// the actual model used, the dynamic linker must process all relocations +/// for thread-local variables whenever the module is loaded. Some models, +/// therefore, provide for a decrease in the overall number of relocations at +/// a cost of restrictions on which modules can access variables. public enum ThreadLocalModel { /// The variable is not thread local and hence has no associated model. case notThreadLocal /// Allows reference of all thread-local variables, from either a shared /// object or a dynamic executable. This model also supports the deferred /// allocation of a block of thread-local storage when the block is first - /// referenced from a specific thread. + /// referenced from a specific thread. Note that the linker is free to + /// optimize accesses using this model to one of the more specific models + /// below which may ultimately defeat lazy allocation of the TLS storagee + /// block. + /// + /// The code generated for this model does nto assume that any information + /// about the module or variable offsets is known at link-time. Instead, the + /// exact value of these variables is computed by the dynamic linker at + /// runtime and passeed to `__tls_get_addr` in an architecture-specific way. + /// + /// If possible, this model should be avoided if one of the more specific + /// models applies out of concern for code size and application startup + /// performance. case generalDynamic - /// This model is an optimization of the General Dynamic model. The compiler + /// This model is an optimization of the `generalDynamic` model. The compiler /// might determine that a variable is bound locally, or protected, within the - /// object being built. In this case, the compiler instructs the link-editor - /// to statically bind the dynamic `tlsoffset` and use this model. + /// object being built. In this case, the compiler instructs the linker + /// to statically bind the dynamic offset of the variable and use this model. /// /// This model provides a performance benefit over the General Dynamic model. - /// Only one call to `tls_get_addr()` is required per function, to determine - /// the address of `dtv0,m`. The dynamic thread-local storage offset, bound at - /// link-edit time, is added to the `dtv0,m` address for each reference. + /// Only one call to `__tls_get_addr` is required per function, to determine + /// the starting address of the variable within the TLS block for its + /// parent module. Additional accesses can add an offset to this address + /// value for repeated accesses. + /// + /// The optimization available over the `generalDynamic` model is defeated if + /// a variable is only ever accessed once, as its access would incur the + /// same `__tls_get_addr` call and the additional overhead of the offset + /// calculation. + /// + /// The linker cannot, in general, optimize from the general dynamic model + /// to the local dynamic model. case localDynamic /// This model can only reference thread-local variables which are available /// as part of the initial static thread-local template. This template is @@ -43,8 +79,10 @@ public enum ThreadLocalModel { /// objects should reference thread-local variables using a dynamic model of /// thread-local storage. case initialExec + /// This model is an optimization of the `localDynamic` model. + /// /// This model can only reference thread-local variables which are part of the - /// thread-local storage block of the dynamic executable. The link-editor + /// thread-local storage block of the dynamic executable. The linker /// calculates the thread pointer-relative offsets statically, without the /// need for dynamic relocations, or the extra reference to the GOT. This /// model can not be used to reference variables outside of the dynamic From 7dc0f303ed86d8cc11385a239683f40064880c69 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Mon, 5 Nov 2018 15:21:05 -0500 Subject: [PATCH 3/5] Reorder the IRBuilder - Move all the module-related operations to Module.swift - Move all the support enumerations to Operation.swift - Break IRBuilder around its marker pragmas into extensions --- Sources/LLVM/IRBuilder.swift | 495 ++++------------------------------- Sources/LLVM/Module.swift | 116 ++++++++ Sources/LLVM/Operation.swift | 306 ++++++++++++++++++++++ 3 files changed, 477 insertions(+), 440 deletions(-) create mode 100644 Sources/LLVM/Operation.swift diff --git a/Sources/LLVM/IRBuilder.swift b/Sources/LLVM/IRBuilder.swift index e1b17485..21962593 100644 --- a/Sources/LLVM/IRBuilder.swift +++ b/Sources/LLVM/IRBuilder.swift @@ -2,423 +2,6 @@ import cllvm #endif -/// Species the behavior that should occur on overflow during mathematical -/// operations. -public enum OverflowBehavior { - /// The result value of the operator is the mathematical result modulo `2^n`, - /// where `n` is the bit width of the result. - case `default` - /// The result value of the operator is a poison value if signed overflow - /// occurs. - case noSignedWrap - /// The result value of the operator is a poison value if unsigned overflow - /// occurs. - case noUnsignedWrap -} - -/// The condition codes available for integer comparison instructions. -public enum IntPredicate { - /// Yields `true` if the operands are equal, false otherwise without sign - /// interpretation. - case equal - /// Yields `true` if the operands are unequal, false otherwise without sign - /// interpretation. - case notEqual - - /// Interprets the operands as unsigned values and yields true if the first is - /// greater than the second. - case unsignedGreaterThan - /// Interprets the operands as unsigned values and yields true if the first is - /// greater than or equal to the second. - case unsignedGreaterThanOrEqual - /// Interprets the operands as unsigned values and yields true if the first is - /// less than the second. - case unsignedLessThan - /// Interprets the operands as unsigned values and yields true if the first is - /// less than or equal to the second. - case unsignedLessThanOrEqual - - /// Interprets the operands as signed values and yields true if the first is - /// greater than the second. - case signedGreaterThan - /// Interprets the operands as signed values and yields true if the first is - /// greater than or equal to the second. - case signedGreaterThanOrEqual - /// Interprets the operands as signed values and yields true if the first is - /// less than the second. - case signedLessThan - /// Interprets the operands as signed values and yields true if the first is - /// less than or equal to the second. - case signedLessThanOrEqual - - static let predicateMapping: [IntPredicate: LLVMIntPredicate] = [ - .equal: LLVMIntEQ, .notEqual: LLVMIntNE, .unsignedGreaterThan: LLVMIntUGT, - .unsignedGreaterThanOrEqual: LLVMIntUGE, .unsignedLessThan: LLVMIntULT, - .unsignedLessThanOrEqual: LLVMIntULE, .signedGreaterThan: LLVMIntSGT, - .signedGreaterThanOrEqual: LLVMIntSGE, .signedLessThan: LLVMIntSLT, - .signedLessThanOrEqual: LLVMIntSLE, - ] - - /// Retrieves the corresponding `LLVMIntPredicate`. - public var llvm: LLVMIntPredicate { - return IntPredicate.predicateMapping[self]! - } -} - -/// The condition codes available for floating comparison instructions. -public enum RealPredicate { - /// No comparison, always returns `false`. - case `false` - /// Ordered and equal. - case orderedEqual - /// Ordered greater than. - case orderedGreaterThan - /// Ordered greater than or equal. - case orderedGreaterThanOrEqual - /// Ordered less than. - case orderedLessThan - /// Ordered less than or equal. - case orderedLessThanOrEqual - /// Ordered and not equal. - case orderedNotEqual - /// Ordered (no nans). - case ordered - /// Unordered (either nans). - case unordered - /// Unordered or equal. - case unorderedEqual - /// Unordered or greater than. - case unorderedGreaterThan - /// Unordered or greater than or equal. - case unorderedGreaterThanOrEqual - /// Unordered or less than. - case unorderedLessThan - /// Unordered or less than or equal. - case unorderedLessThanOrEqual - /// Unordered or not equal. - case unorderedNotEqual - /// No comparison, always returns `true`. - case `true` - - static let predicateMapping: [RealPredicate: LLVMRealPredicate] = [ - .false: LLVMRealPredicateFalse, .orderedEqual: LLVMRealOEQ, - .orderedGreaterThan: LLVMRealOGT, .orderedGreaterThanOrEqual: LLVMRealOGE, - .orderedLessThan: LLVMRealOLT, .orderedLessThanOrEqual: LLVMRealOLE, - .orderedNotEqual: LLVMRealONE, .ordered: LLVMRealORD, .unordered: LLVMRealUNO, - .unorderedEqual: LLVMRealUEQ, .unorderedGreaterThan: LLVMRealUGT, - .unorderedGreaterThanOrEqual: LLVMRealUGE, .unorderedLessThan: LLVMRealULT, - .unorderedLessThanOrEqual: LLVMRealULE, .unorderedNotEqual: LLVMRealUNE, - .true: LLVMRealPredicateTrue, - ] - - /// Retrieves the corresponding `LLVMRealPredicate`. - public var llvm: LLVMRealPredicate { - return RealPredicate.predicateMapping[self]! - } -} - -/// `AtomicOrdering` enumerates available memory ordering semantics. -/// -/// Atomic instructions (`cmpxchg`, `atomicrmw`, `fence`, `atomic load`, and -/// `atomic store`) take ordering parameters that determine which other atomic -/// instructions on the same address they synchronize with. These semantics are -/// borrowed from Java and C++0x, but are somewhat more colloquial. If these -/// descriptions aren’t precise enough, check those specs (see spec references -/// in the atomics guide). fence instructions treat these orderings somewhat -/// differently since they don’t take an address. See that instruction’s -/// documentation for details. -public enum AtomicOrdering: Comparable { - /// A load or store which is not atomic - case notAtomic - /// Lowest level of atomicity, guarantees somewhat sane results, lock free. - /// - /// The set of values that can be read is governed by the happens-before - /// partial order. A value cannot be read unless some operation wrote it. This - /// is intended to provide a guarantee strong enough to model Java’s - /// non-volatile shared variables. This ordering cannot be specified for - /// read-modify-write operations; it is not strong enough to make them atomic - /// in any interesting way. - case unordered - /// Guarantees that if you take all the operations affecting a specific - /// address, a consistent ordering exists. - /// - /// In addition to the guarantees of unordered, there is a single total order - /// for modifications by monotonic operations on each address. All - /// modification orders must be compatible with the happens-before order. - /// There is no guarantee that the modification orders can be combined to a - /// global total order for the whole program (and this often will not be - /// possible). The read in an atomic read-modify-write operation (cmpxchg and - /// atomicrmw) reads the value in the modification order immediately before - /// the value it writes. If one atomic read happens before another atomic read - /// of the same address, the later read must see the same value or a later - /// value in the address’s modification order. This disallows reordering of - /// monotonic (or stronger) operations on the same address. If an address is - /// written monotonic-ally by one thread, and other threads monotonic-ally - /// read that address repeatedly, the other threads must eventually see the - /// write. This corresponds to the C++0x/C1x memory_order_relaxed. - case monotonic - /// Acquire provides a barrier of the sort necessary to acquire a lock to - /// access other memory with normal loads and stores. - /// - /// In addition to the guarantees of monotonic, a synchronizes-with edge may - /// be formed with a release operation. This is intended to model C++’s - /// `memory_order_acquire`. - case acquire - /// Release is similar to Acquire, but with a barrier of the sort necessary to - /// release a lock. - /// - /// In addition to the guarantees of monotonic, if this operation writes a - /// value which is subsequently read by an acquire operation, it - /// synchronizes-with that operation. (This isn’t a complete description; see - /// the C++0x definition of a release sequence.) This corresponds to the - /// C++0x/C1x memory_order_release. - case release - /// provides both an Acquire and a Release barrier (for fences and operations - /// which both read and write memory). - /// - /// This corresponds to the C++0x/C1x memory_order_acq_rel. - case acquireRelease - /// Provides Acquire semantics for loads and Release semantics for stores. - /// - /// In addition to the guarantees of acq_rel (acquire for an operation that - /// only reads, release for an operation that only writes), there is a global - /// total order on all sequentially-consistent operations on all addresses, - /// which is consistent with the happens-before partial order and with the - /// modification orders of all the affected addresses. Each - /// sequentially-consistent read sees the last preceding write to the same - /// address in this global order. This corresponds to the C++0x/C1x - /// `memory_order_seq_cst` and Java volatile. - case sequentiallyConsistent - - private static let orderingMapping: [AtomicOrdering: LLVMAtomicOrdering] = [ - .notAtomic: LLVMAtomicOrderingNotAtomic, - .unordered: LLVMAtomicOrderingUnordered, - .monotonic: LLVMAtomicOrderingMonotonic, - .acquire: LLVMAtomicOrderingAcquire, - .release: LLVMAtomicOrderingRelease, - .acquireRelease: LLVMAtomicOrderingAcquireRelease, - .sequentiallyConsistent: LLVMAtomicOrderingSequentiallyConsistent, - ] - - /// Returns whether the left atomic ordering is strictly weaker than the - /// right atomic order. - public static func <(lhs: AtomicOrdering, rhs: AtomicOrdering) -> Bool { - return lhs.llvm.rawValue < rhs.llvm.rawValue - } - - /// Retrieves the corresponding `LLVMAtomicOrdering`. - public var llvm: LLVMAtomicOrdering { - return AtomicOrdering.orderingMapping[self]! - } -} - -/// `AtomicReadModifyWriteOperation` enumerates the kinds of supported atomic -/// read-write-modify operations. -public enum AtomicReadModifyWriteOperation { - /// Set the new value and return the one old - /// - /// ``` - /// *ptr = val - /// ``` - case xchg - /// Add a value and return the old one - /// - /// ``` - /// *ptr = *ptr + val - /// ``` - case add - /// Subtract a value and return the old one - /// - /// ``` - /// *ptr = *ptr - val - /// ``` - case sub - /// And a value and return the old one - /// - /// ``` - /// *ptr = *ptr & val - /// ``` - case and - /// Not-And a value and return the old one - /// - /// ``` - /// *ptr = ~(*ptr & val) - /// ``` - case nand - /// OR a value and return the old one - /// - /// ``` - /// *ptr = *ptr | val - /// ``` - case or - /// Xor a value and return the old one - /// - /// ``` - /// *ptr = *ptr ^ val - /// ``` - case xor - /// Sets the value if it's greater than the original using a signed comparison - /// and return the old one. - /// - /// ``` - /// // Using a signed comparison - /// *ptr = *ptr > val ? *ptr : val - /// ``` - case max - /// Sets the value if it's Smaller than the original using a signed comparison - /// and return the old one. - /// - /// ``` - /// // Using a signed comparison - /// *ptr = *ptr < val ? *ptr : val - /// ``` - case min - /// Sets the value if it's greater than the original using an unsigned - /// comparison and return the old one. - /// - /// ``` - /// // Using an unsigned comparison - /// *ptr = *ptr > val ? *ptr : val - /// ``` - case umax - /// Sets the value if it's greater than the original using an unsigned - /// comparison and return the old one. - /// - /// ``` - /// // Using an unsigned comparison - /// *ptr = *ptr < val ? *ptr : val - /// ``` - case umin - - static let atomicRMWMapping: [AtomicReadModifyWriteOperation: LLVMAtomicRMWBinOp] = [ - .xchg: LLVMAtomicRMWBinOpXchg, .add: LLVMAtomicRMWBinOpAdd, - .sub: LLVMAtomicRMWBinOpSub, .and: LLVMAtomicRMWBinOpAnd, - .nand: LLVMAtomicRMWBinOpNand, .or: LLVMAtomicRMWBinOpOr, - .xor: LLVMAtomicRMWBinOpXor, .max: LLVMAtomicRMWBinOpMax, - .min: LLVMAtomicRMWBinOpMin, .umax: LLVMAtomicRMWBinOpUMax, - .umin: LLVMAtomicRMWBinOpUMin, - ] - - /// Retrieves the corresponding `LLVMAtomicRMWBinOp`. - public var llvm: LLVMAtomicRMWBinOp { - return AtomicReadModifyWriteOperation.atomicRMWMapping[self]! - } -} - -extension Module { - /// Searches for and retrieves a global variable with the given name in this - /// module if that name references an existing global variable. - /// - /// - parameter name: The name of the global to reference. - /// - /// - returns: A value representing the referenced global if it exists. - public func global(named name: String) -> Global? { - guard let ref = LLVMGetNamedGlobal(llvm, name) else { return nil } - return Global(llvm: ref) - } - - /// 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) - } - - /// Searches for and retrieves a comdat section with the given name in this - /// module. If none is found, one with that name is created and returned. - /// - /// - parameter name: The name of the comdat section to create. - /// - /// - returns: A representation of the newly created comdat section with the - /// given name. - public func comdat(named name: String) -> Comdat { - guard let comdat = LLVMGetOrInsertComdat(llvm, name) else { fatalError() } - return Comdat(llvm: comdat) - } - - /// Build a named global of the given type. - /// - /// - parameter name: The name of the newly inserted global value. - /// - parameter type: The type of the newly inserted global value. - /// - parameter addressSpace: The optional address space where the global - /// variable resides. - /// - /// - returns: A value representing the newly inserted global variable. - public func addGlobal(_ name: String, type: IRType, addressSpace: Int? = nil) -> Global { - let val: LLVMValueRef - if let addressSpace = addressSpace { - val = LLVMAddGlobalInAddressSpace(llvm, type.asLLVM(), name, UInt32(addressSpace)) - } else { - val = LLVMAddGlobal(llvm, type.asLLVM(), name) - } - return Global(llvm: val) - } - - /// Build a named global of the given type. - /// - /// - parameter name: The name of the newly inserted global value. - /// - parameter initializer: The initial value for the global variable. - /// - parameter addressSpace: The optional address space where the global - /// variable resides. - /// - /// - returns: A value representing the newly inserted global variable. - public func addGlobal(_ name: String, initializer: IRValue, addressSpace: Int? = nil) -> Global { - let global = addGlobal(name, type: initializer.type) - global.initializer = initializer - return global - } - - /// Build a named global string consisting of an array of `i8` type filled in - /// with the nul terminated string value. - /// - /// - parameter name: The name of the newly inserted global string value. - /// - parameter value: The character contents of the newly inserted global. - /// - /// - returns: A value representing the newly inserted global string variable. - public func addGlobalString(name: String, value: String) -> Global { - let length = value.utf8.count - - var global = addGlobal(name, type: - ArrayType(elementType: IntType.int8, count: length + 1)) - - global.alignment = Alignment(1) - global.initializer = value - - return global - } - - /// Build a named alias to a global value or a constant expression. - /// - /// Aliases, unlike function or variables, don’t create any new data. They are - /// just a new symbol and metadata for an existing position. - /// - /// - parameter name: The name of the newly inserted alias. - /// - parameter aliasee: The value or constant to alias. - /// - parameter type: The type of the aliased value or expression. - /// - /// - returns: A value representing the newly created alias. - public func addAlias(name: String, to aliasee: IRGlobal, type: IRType) -> Alias { - return Alias(llvm: LLVMAddAlias(llvm, type.asLLVM(), aliasee.asLLVM(), name)) - } -} - /// An `IRBuilder` is a helper object that generates LLVM instructions. IR /// Builders keep track of a position within a function or basic block and has /// methods to insert instructions at that position. @@ -436,8 +19,14 @@ public class IRBuilder { self.llvm = LLVMCreateBuilderInContext(module.context.llvm) } - // MARK: IR Navigation + deinit { + LLVMDisposeBuilder(llvm) + } +} + +// MARK: IR Navigation +extension IRBuilder { /// Repositions the IR Builder at the end of the given basic block. /// /// - parameter block: The basic block to reposition the IR Builder after. @@ -471,8 +60,6 @@ public class IRBuilder { LLVMClearInsertionPosition(llvm) } - // MARK: Instruction Insertion - /// Gets the basic block built instructions will be inserted into. public var insertBlock: BasicBlock? { guard let blockRef = LLVMGetInsertBlock(llvm) else { return nil } @@ -495,9 +82,11 @@ public class IRBuilder { LLVMInsertIntoBuilder(llvm, inst.asLLVM()) } } +} - // MARK: Convenience Instructions +// MARK: Convenience Instructions +extension IRBuilder { /// Builds the specified binary operation instruction with the given arguments. /// /// - parameter op: The operation to build. @@ -524,9 +113,11 @@ public class IRBuilder { public func buildCast(_ op: OpCode.Cast, value: IRValue, type: IRType, name: String = "") -> IRValue { return LLVMBuildCast(llvm, op.llvm, value.asLLVM(), type.asLLVM(), name) } +} - // MARK: Arithmetic Instructions +// MARK: Arithmetic Instructions +extension IRBuilder { /// Build a negation instruction with the given value as an operand. /// /// Whether an integer or floating point negate instruction is built is @@ -790,9 +381,11 @@ public class IRBuilder { } return LLVMBuildFCmp(llvm, predicate.llvm, lhsVal, rhsVal, name) } +} - // MARK: Logical Instructions +// MARK: Logical Instructions +extension IRBuilder { /// Build a bitwise logical not with the given value as an operand. /// /// - parameter val: The value to negate. @@ -875,9 +468,11 @@ public class IRBuilder { return LLVMBuildLShr(llvm, lhs.asLLVM(), rhs.asLLVM(), name) } } +} - // MARK: Conditional Instructions +// MARK: Conditional Instructions +extension IRBuilder { /// Build a phi node with the given type acting as the result of any incoming /// basic blocks. /// @@ -940,9 +535,11 @@ public class IRBuilder { `else`.asLLVM(), UInt32(caseCount))!) } +} - // MARK: Declaration Instructions +// MARK: Declaration Instructions +extension IRBuilder { /// Build a named function body with the given type. /// /// - parameter name: The name of the newly defined function. @@ -969,9 +566,11 @@ public class IRBuilder { } return type } +} - // MARK: Terminator Instructions +// MARK: Terminator Instructions +extension IRBuilder { /// Build an unconditional branch to the given basic block. /// /// The `br` instruction is used to cause control flow to transfer to a @@ -1110,9 +709,11 @@ public class IRBuilder { return Call(llvm: LLVMBuildCall(llvm, fn.asLLVM(), buf.baseAddress!, UInt32(buf.count), name)) } } +} - // MARK: Exception Handling Instructions +// MARK: Exception Handling Instructions +extension IRBuilder { /// Build a call to the given function with the given arguments with the /// possibility of control transfering to either the `next` basic block or /// the `catch` basic block if an exception occurs. @@ -1222,9 +823,11 @@ public class IRBuilder { public func buildVAArg(_ list: IRValue, type: IRType, name: String = "") -> IRValue { return LLVMBuildVAArg(llvm, list.asLLVM(), type.asLLVM(), name) } +} - // MARK: Memory Access Instructions +// MARK: Memory Access Instructions +extension IRBuilder { /// Build an `alloca` to allocate stack memory to hold a value of the given /// type. /// @@ -1370,9 +973,11 @@ public class IRBuilder { name: String = "") -> IRValue { return LLVMBuildExtractValue(llvm, value.asLLVM(), UInt32(index), name) } +} - // MARK: Null Test Instructions +// MARK: Null Test Instructions +extension IRBuilder { /// Build a comparision instruction that returns whether the given operand is /// `null`. /// @@ -1396,9 +1001,11 @@ public class IRBuilder { public func buildIsNotNull(_ val: IRValue, name: String = "") -> IRValue { return LLVMBuildIsNotNull(llvm, val.asLLVM(), name) } +} - // MARK: Conversion Instructions +// MARK: Conversion Instructions +extension IRBuilder { /// Build an instruction that either performs a truncation or a bitcast of /// the given value to a value of the given type. /// @@ -1632,9 +1239,11 @@ public class IRBuilder { ) return LLVMBuildPtrDiff(llvm, lhs.asLLVM(), rhs.asLLVM(), name) } +} - // MARK: Atomic Instructions +// MARK: Atomic Instructions +extension IRBuilder { /// Build a fence instruction that introduces "happens-before" edges between /// operations. /// @@ -1734,9 +1343,11 @@ public class IRBuilder { ) -> IRValue { return LLVMBuildAtomicRMW(llvm, atomicOp.llvm, ptr.asLLVM(), value.asLLVM(), ordering.llvm, singleThreaded.llvm) } +} - // MARK: C Standard Library Instructions +// MARK: C Standard Library Instructions +extension IRBuilder { /// Build a call to the C standard library `malloc` instruction. /// ``` /// (type *)malloc(sizeof(type)); @@ -1769,9 +1380,11 @@ public class IRBuilder { public func buildFree(_ ptr: IRValue) -> IRValue { return LLVMBuildFree(llvm, ptr.asLLVM()) } +} - // MARK: Aggregate Instructions +// MARK: Aggregate Instructions +extension IRBuilder { /// Build an instruction to insert a value into a member field in an /// aggregate value. /// @@ -1802,9 +1415,11 @@ public class IRBuilder { func buildExtractValue(aggregate: IRValue, index: Int, name: String = "") -> IRValue { return LLVMBuildExtractValue(llvm, aggregate.asLLVM(), UInt32(index), name) } +} - // MARK: Vector Instructions +// MARK: Vector Instructions +extension IRBuilder { /// Build a vector insert instruction to nondestructively insert the given /// value into the given vector. /// @@ -1856,9 +1471,11 @@ public class IRBuilder { } return LLVMBuildShuffleVector(llvm, vector1.asLLVM(), vector2.asLLVM(), mask.asLLVM(), name) } +} - // MARK: Global Variable Instructions +// MARK: Global Variable Instructions +extension IRBuilder { /// Build a named global of the given type. /// /// - parameter name: The name of the newly inserted global value. @@ -1931,9 +1548,11 @@ public class IRBuilder { public func addAlias(name: String, to aliasee: IRGlobal, type: IRType) -> Alias { return self.module.addAlias(name: name, to: aliasee, type: type) } +} - // MARK: Inline Assembly +// MARK: Inline Assembly +extension IRBuilder { /// Build a value representing an inline assembly expression (as opposed to /// module-level inline assembly). /// @@ -1973,10 +1592,6 @@ public class IRBuilder { public func buildInlineAssembly(_ asm: String, type: FunctionType, constraints: String = "", hasSideEffects: Bool = true, needsAlignedStack: Bool = true) -> IRValue { return LLVMConstInlineAsm(type.asLLVM(), asm, constraints, hasSideEffects.llvm, needsAlignedStack.llvm) } - - deinit { - LLVMDisposeBuilder(llvm) - } } private func lowerVector(_ type: IRType) -> IRType { diff --git a/Sources/LLVM/Module.swift b/Sources/LLVM/Module.swift index faebe392..eb9c65e9 100644 --- a/Sources/LLVM/Module.swift +++ b/Sources/LLVM/Module.swift @@ -269,6 +269,122 @@ public final class Module: CustomStringConvertible { } } +// MARK: Global Declarations + +extension Module { + /// Searches for and retrieves a global variable with the given name in this + /// module if that name references an existing global variable. + /// + /// - parameter name: The name of the global to reference. + /// + /// - returns: A value representing the referenced global if it exists. + public func global(named name: String) -> Global? { + guard let ref = LLVMGetNamedGlobal(llvm, name) else { return nil } + return Global(llvm: ref) + } + + /// 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) + } + + /// Searches for and retrieves a comdat section with the given name in this + /// module. If none is found, one with that name is created and returned. + /// + /// - parameter name: The name of the comdat section to create. + /// + /// - returns: A representation of the newly created comdat section with the + /// given name. + public func comdat(named name: String) -> Comdat { + guard let comdat = LLVMGetOrInsertComdat(llvm, name) else { fatalError() } + return Comdat(llvm: comdat) + } + + /// Build a named global of the given type. + /// + /// - parameter name: The name of the newly inserted global value. + /// - parameter type: The type of the newly inserted global value. + /// - parameter addressSpace: The optional address space where the global + /// variable resides. + /// + /// - returns: A value representing the newly inserted global variable. + public func addGlobal(_ name: String, type: IRType, addressSpace: Int? = nil) -> Global { + let val: LLVMValueRef + if let addressSpace = addressSpace { + val = LLVMAddGlobalInAddressSpace(llvm, type.asLLVM(), name, UInt32(addressSpace)) + } else { + val = LLVMAddGlobal(llvm, type.asLLVM(), name) + } + return Global(llvm: val) + } + + /// Build a named global of the given type. + /// + /// - parameter name: The name of the newly inserted global value. + /// - parameter initializer: The initial value for the global variable. + /// - parameter addressSpace: The optional address space where the global + /// variable resides. + /// + /// - returns: A value representing the newly inserted global variable. + public func addGlobal(_ name: String, initializer: IRValue, addressSpace: Int? = nil) -> Global { + let global = addGlobal(name, type: initializer.type) + global.initializer = initializer + return global + } + + /// Build a named global string consisting of an array of `i8` type filled in + /// with the nul terminated string value. + /// + /// - parameter name: The name of the newly inserted global string value. + /// - parameter value: The character contents of the newly inserted global. + /// + /// - returns: A value representing the newly inserted global string variable. + public func addGlobalString(name: String, value: String) -> Global { + let length = value.utf8.count + + var global = addGlobal(name, type: + ArrayType(elementType: IntType.int8, count: length + 1)) + + global.alignment = Alignment(1) + global.initializer = value + + return global + } + + /// Build a named alias to a global value or a constant expression. + /// + /// Aliases, unlike function or variables, don’t create any new data. They are + /// just a new symbol and metadata for an existing position. + /// + /// - parameter name: The name of the newly inserted alias. + /// - parameter aliasee: The value or constant to alias. + /// - parameter type: The type of the aliased value or expression. + /// + /// - returns: A value representing the newly created alias. + public func addAlias(name: String, to aliasee: IRGlobal, type: IRType) -> Alias { + return Alias(llvm: LLVMAddAlias(llvm, type.asLLVM(), aliasee.asLLVM(), name)) + } +} + extension Bool { internal var llvm: LLVMBool { return self ? 1 : 0 diff --git a/Sources/LLVM/Operation.swift b/Sources/LLVM/Operation.swift new file mode 100644 index 00000000..e78dc0f3 --- /dev/null +++ b/Sources/LLVM/Operation.swift @@ -0,0 +1,306 @@ +#if SWIFT_PACKAGE +import cllvm +#endif + +/// Species the behavior that should occur on overflow during mathematical +/// operations. +public enum OverflowBehavior { + /// The result value of the operator is the mathematical result modulo `2^n`, + /// where `n` is the bit width of the result. + case `default` + /// The result value of the operator is a poison value if signed overflow + /// occurs. + case noSignedWrap + /// The result value of the operator is a poison value if unsigned overflow + /// occurs. + case noUnsignedWrap +} + +/// The condition codes available for integer comparison instructions. +public enum IntPredicate { + /// Yields `true` if the operands are equal, false otherwise without sign + /// interpretation. + case equal + /// Yields `true` if the operands are unequal, false otherwise without sign + /// interpretation. + case notEqual + + /// Interprets the operands as unsigned values and yields true if the first is + /// greater than the second. + case unsignedGreaterThan + /// Interprets the operands as unsigned values and yields true if the first is + /// greater than or equal to the second. + case unsignedGreaterThanOrEqual + /// Interprets the operands as unsigned values and yields true if the first is + /// less than the second. + case unsignedLessThan + /// Interprets the operands as unsigned values and yields true if the first is + /// less than or equal to the second. + case unsignedLessThanOrEqual + + /// Interprets the operands as signed values and yields true if the first is + /// greater than the second. + case signedGreaterThan + /// Interprets the operands as signed values and yields true if the first is + /// greater than or equal to the second. + case signedGreaterThanOrEqual + /// Interprets the operands as signed values and yields true if the first is + /// less than the second. + case signedLessThan + /// Interprets the operands as signed values and yields true if the first is + /// less than or equal to the second. + case signedLessThanOrEqual + + static let predicateMapping: [IntPredicate: LLVMIntPredicate] = [ + .equal: LLVMIntEQ, .notEqual: LLVMIntNE, .unsignedGreaterThan: LLVMIntUGT, + .unsignedGreaterThanOrEqual: LLVMIntUGE, .unsignedLessThan: LLVMIntULT, + .unsignedLessThanOrEqual: LLVMIntULE, .signedGreaterThan: LLVMIntSGT, + .signedGreaterThanOrEqual: LLVMIntSGE, .signedLessThan: LLVMIntSLT, + .signedLessThanOrEqual: LLVMIntSLE, + ] + + /// Retrieves the corresponding `LLVMIntPredicate`. + public var llvm: LLVMIntPredicate { + return IntPredicate.predicateMapping[self]! + } +} + +/// The condition codes available for floating comparison instructions. +public enum RealPredicate { + /// No comparison, always returns `false`. + case `false` + /// Ordered and equal. + case orderedEqual + /// Ordered greater than. + case orderedGreaterThan + /// Ordered greater than or equal. + case orderedGreaterThanOrEqual + /// Ordered less than. + case orderedLessThan + /// Ordered less than or equal. + case orderedLessThanOrEqual + /// Ordered and not equal. + case orderedNotEqual + /// Ordered (no nans). + case ordered + /// Unordered (either nans). + case unordered + /// Unordered or equal. + case unorderedEqual + /// Unordered or greater than. + case unorderedGreaterThan + /// Unordered or greater than or equal. + case unorderedGreaterThanOrEqual + /// Unordered or less than. + case unorderedLessThan + /// Unordered or less than or equal. + case unorderedLessThanOrEqual + /// Unordered or not equal. + case unorderedNotEqual + /// No comparison, always returns `true`. + case `true` + + static let predicateMapping: [RealPredicate: LLVMRealPredicate] = [ + .false: LLVMRealPredicateFalse, .orderedEqual: LLVMRealOEQ, + .orderedGreaterThan: LLVMRealOGT, .orderedGreaterThanOrEqual: LLVMRealOGE, + .orderedLessThan: LLVMRealOLT, .orderedLessThanOrEqual: LLVMRealOLE, + .orderedNotEqual: LLVMRealONE, .ordered: LLVMRealORD, .unordered: LLVMRealUNO, + .unorderedEqual: LLVMRealUEQ, .unorderedGreaterThan: LLVMRealUGT, + .unorderedGreaterThanOrEqual: LLVMRealUGE, .unorderedLessThan: LLVMRealULT, + .unorderedLessThanOrEqual: LLVMRealULE, .unorderedNotEqual: LLVMRealUNE, + .true: LLVMRealPredicateTrue, + ] + + /// Retrieves the corresponding `LLVMRealPredicate`. + public var llvm: LLVMRealPredicate { + return RealPredicate.predicateMapping[self]! + } +} + +/// `AtomicOrdering` enumerates available memory ordering semantics. +/// +/// Atomic instructions (`cmpxchg`, `atomicrmw`, `fence`, `atomic load`, and +/// `atomic store`) take ordering parameters that determine which other atomic +/// instructions on the same address they synchronize with. These semantics are +/// borrowed from Java and C++0x, but are somewhat more colloquial. If these +/// descriptions aren’t precise enough, check those specs (see spec references +/// in the atomics guide). fence instructions treat these orderings somewhat +/// differently since they don’t take an address. See that instruction’s +/// documentation for details. +public enum AtomicOrdering: Comparable { + /// A load or store which is not atomic + case notAtomic + /// Lowest level of atomicity, guarantees somewhat sane results, lock free. + /// + /// The set of values that can be read is governed by the happens-before + /// partial order. A value cannot be read unless some operation wrote it. This + /// is intended to provide a guarantee strong enough to model Java’s + /// non-volatile shared variables. This ordering cannot be specified for + /// read-modify-write operations; it is not strong enough to make them atomic + /// in any interesting way. + case unordered + /// Guarantees that if you take all the operations affecting a specific + /// address, a consistent ordering exists. + /// + /// In addition to the guarantees of unordered, there is a single total order + /// for modifications by monotonic operations on each address. All + /// modification orders must be compatible with the happens-before order. + /// There is no guarantee that the modification orders can be combined to a + /// global total order for the whole program (and this often will not be + /// possible). The read in an atomic read-modify-write operation (cmpxchg and + /// atomicrmw) reads the value in the modification order immediately before + /// the value it writes. If one atomic read happens before another atomic read + /// of the same address, the later read must see the same value or a later + /// value in the address’s modification order. This disallows reordering of + /// monotonic (or stronger) operations on the same address. If an address is + /// written monotonic-ally by one thread, and other threads monotonic-ally + /// read that address repeatedly, the other threads must eventually see the + /// write. This corresponds to the C++0x/C1x memory_order_relaxed. + case monotonic + /// Acquire provides a barrier of the sort necessary to acquire a lock to + /// access other memory with normal loads and stores. + /// + /// In addition to the guarantees of monotonic, a synchronizes-with edge may + /// be formed with a release operation. This is intended to model C++’s + /// `memory_order_acquire`. + case acquire + /// Release is similar to Acquire, but with a barrier of the sort necessary to + /// release a lock. + /// + /// In addition to the guarantees of monotonic, if this operation writes a + /// value which is subsequently read by an acquire operation, it + /// synchronizes-with that operation. (This isn’t a complete description; see + /// the C++0x definition of a release sequence.) This corresponds to the + /// C++0x/C1x memory_order_release. + case release + /// provides both an Acquire and a Release barrier (for fences and operations + /// which both read and write memory). + /// + /// This corresponds to the C++0x/C1x memory_order_acq_rel. + case acquireRelease + /// Provides Acquire semantics for loads and Release semantics for stores. + /// + /// In addition to the guarantees of acq_rel (acquire for an operation that + /// only reads, release for an operation that only writes), there is a global + /// total order on all sequentially-consistent operations on all addresses, + /// which is consistent with the happens-before partial order and with the + /// modification orders of all the affected addresses. Each + /// sequentially-consistent read sees the last preceding write to the same + /// address in this global order. This corresponds to the C++0x/C1x + /// `memory_order_seq_cst` and Java volatile. + case sequentiallyConsistent + + private static let orderingMapping: [AtomicOrdering: LLVMAtomicOrdering] = [ + .notAtomic: LLVMAtomicOrderingNotAtomic, + .unordered: LLVMAtomicOrderingUnordered, + .monotonic: LLVMAtomicOrderingMonotonic, + .acquire: LLVMAtomicOrderingAcquire, + .release: LLVMAtomicOrderingRelease, + .acquireRelease: LLVMAtomicOrderingAcquireRelease, + .sequentiallyConsistent: LLVMAtomicOrderingSequentiallyConsistent, + ] + + /// Returns whether the left atomic ordering is strictly weaker than the + /// right atomic order. + public static func <(lhs: AtomicOrdering, rhs: AtomicOrdering) -> Bool { + return lhs.llvm.rawValue < rhs.llvm.rawValue + } + + /// Retrieves the corresponding `LLVMAtomicOrdering`. + public var llvm: LLVMAtomicOrdering { + return AtomicOrdering.orderingMapping[self]! + } +} + +/// `AtomicReadModifyWriteOperation` enumerates the kinds of supported atomic +/// read-write-modify operations. +public enum AtomicReadModifyWriteOperation { + /// Set the new value and return the one old + /// + /// ``` + /// *ptr = val + /// ``` + case xchg + /// Add a value and return the old one + /// + /// ``` + /// *ptr = *ptr + val + /// ``` + case add + /// Subtract a value and return the old one + /// + /// ``` + /// *ptr = *ptr - val + /// ``` + case sub + /// And a value and return the old one + /// + /// ``` + /// *ptr = *ptr & val + /// ``` + case and + /// Not-And a value and return the old one + /// + /// ``` + /// *ptr = ~(*ptr & val) + /// ``` + case nand + /// OR a value and return the old one + /// + /// ``` + /// *ptr = *ptr | val + /// ``` + case or + /// Xor a value and return the old one + /// + /// ``` + /// *ptr = *ptr ^ val + /// ``` + case xor + /// Sets the value if it's greater than the original using a signed comparison + /// and return the old one. + /// + /// ``` + /// // Using a signed comparison + /// *ptr = *ptr > val ? *ptr : val + /// ``` + case max + /// Sets the value if it's Smaller than the original using a signed comparison + /// and return the old one. + /// + /// ``` + /// // Using a signed comparison + /// *ptr = *ptr < val ? *ptr : val + /// ``` + case min + /// Sets the value if it's greater than the original using an unsigned + /// comparison and return the old one. + /// + /// ``` + /// // Using an unsigned comparison + /// *ptr = *ptr > val ? *ptr : val + /// ``` + case umax + /// Sets the value if it's greater than the original using an unsigned + /// comparison and return the old one. + /// + /// ``` + /// // Using an unsigned comparison + /// *ptr = *ptr < val ? *ptr : val + /// ``` + case umin + + static let atomicRMWMapping: [AtomicReadModifyWriteOperation: LLVMAtomicRMWBinOp] = [ + .xchg: LLVMAtomicRMWBinOpXchg, .add: LLVMAtomicRMWBinOpAdd, + .sub: LLVMAtomicRMWBinOpSub, .and: LLVMAtomicRMWBinOpAnd, + .nand: LLVMAtomicRMWBinOpNand, .or: LLVMAtomicRMWBinOpOr, + .xor: LLVMAtomicRMWBinOpXor, .max: LLVMAtomicRMWBinOpMax, + .min: LLVMAtomicRMWBinOpMin, .umax: LLVMAtomicRMWBinOpUMax, + .umin: LLVMAtomicRMWBinOpUMin, + ] + + /// Retrieves the corresponding `LLVMAtomicRMWBinOp`. + public var llvm: LLVMAtomicRMWBinOp { + return AtomicReadModifyWriteOperation.atomicRMWMapping[self]! + } +} From 6f27742443a0fb86a91f79ddfd47068495406e28 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Mon, 5 Nov 2018 15:21:19 -0500 Subject: [PATCH 4/5] Add local unnamed address kinds --- Sources/LLVM/IRGlobal.swift | 4 ++-- Sources/LLVM/Linkage.swift | 21 +++++++++++---------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/Sources/LLVM/IRGlobal.swift b/Sources/LLVM/IRGlobal.swift index d3187a73..e64338a5 100644 --- a/Sources/LLVM/IRGlobal.swift +++ b/Sources/LLVM/IRGlobal.swift @@ -34,8 +34,8 @@ extension IRGlobal { /// Retrieves an indicator for the significance of a global value's address. public var unnamedAddressKind: UnnamedAddressKind { - get { return UnnamedAddressKind(llvm: LLVMHasUnnamedAddr(asLLVM())) } - set { LLVMSetUnnamedAddr(asLLVM(), newValue.llvm) } + get { return UnnamedAddressKind(llvm: LLVMGetUnnamedAddress(asLLVM())) } + set { LLVMSetUnnamedAddress(asLLVM(), newValue.llvm) } } /// Retrieves the COMDAT section for this global, if it exists. diff --git a/Sources/LLVM/Linkage.swift b/Sources/LLVM/Linkage.swift index 9cd87361..6c2f13aa 100644 --- a/Sources/LLVM/Linkage.swift +++ b/Sources/LLVM/Linkage.swift @@ -237,28 +237,29 @@ public enum UnnamedAddressKind { /// current module but it may or may not be significant to another module; /// only the content of the value is known to be significant within the /// current module. -// case local + case local /// Indicates that the address of this global value is not significant to the /// current module or any other module; only the content of the value /// is significant globally. case global - private static let unnamedAddressMapping: [UnnamedAddressKind: LLVMBool] = [ - .none: LLVMBool(0), -// .local: LLVMBool(1), - .global: LLVMBool(1), - ] + private static let unnamedAddressMapping: [UnnamedAddressKind: LLVMUnnamedAddr] = [ + .none: LLVMNoUnnamedAddr, + .local: LLVMLocalUnnamedAddr, + .global: LLVMGlobalUnnamedAddr, + ] - internal init(llvm: LLVMBool) { + internal init(llvm: LLVMUnnamedAddr) { switch llvm { - case 0: self = .none - case 1: self = .global + case LLVMNoUnnamedAddr: self = .none + case LLVMLocalUnnamedAddr: self = .local + case LLVMGlobalUnnamedAddr: self = .global default: fatalError("unknown unnamed address kind \(llvm)") } } - internal var llvm: LLVMBool { + internal var llvm: LLVMUnnamedAddr { return UnnamedAddressKind.unnamedAddressMapping[self]! } } From ce90642f8cba94e8fbe221ec00072fa96392e257 Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Mon, 5 Nov 2018 15:21:35 -0500 Subject: [PATCH 5/5] Silence warnings in the tests --- Sources/LLVM/Global.swift | 2 +- Tests/LLVMTests/IRBuilderSpec.swift | 12 ++++++------ Tests/LLVMTests/IRGlobalSpec.swift | 4 ++-- Tests/LLVMTests/IROperationSpec.swift | 12 ++++++------ 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Sources/LLVM/Global.swift b/Sources/LLVM/Global.swift index 2829f0fe..8040482f 100644 --- a/Sources/LLVM/Global.swift +++ b/Sources/LLVM/Global.swift @@ -35,7 +35,7 @@ public enum ThreadLocalModel { /// below which may ultimately defeat lazy allocation of the TLS storagee /// block. /// - /// The code generated for this model does nto assume that any information + /// The code generated for this model does not assume that any information /// about the module or variable offsets is known at link-time. Instead, the /// exact value of these variables is computed by the dynamic linker at /// runtime and passeed to `__tls_get_addr` in an architecture-specific way. diff --git a/Tests/LLVMTests/IRBuilderSpec.swift b/Tests/LLVMTests/IRBuilderSpec.swift index 9496e6a7..d3997933 100644 --- a/Tests/LLVMTests/IRBuilderSpec.swift +++ b/Tests/LLVMTests/IRBuilderSpec.swift @@ -33,9 +33,9 @@ class IRBuilderSpec : XCTestCase { // IRBUILDERARITH: @a = global i32 1 // IRBUILDERARITH-NEXT: @b = global i32 1 - var g1 = builder.addGlobal("a", type: IntType.int32) + let g1 = builder.addGlobal("a", type: IntType.int32) g1.initializer = Int32(1) - var g2 = builder.addGlobal("b", type: IntType.int32) + let g2 = builder.addGlobal("b", type: IntType.int32) g2.initializer = Int32(1) // IRBUILDERARITH-NEXT: @vec1 = global <8 x i64> @@ -121,9 +121,9 @@ class IRBuilderSpec : XCTestCase { // IRBUILDERCMP: @a = global i32 1 // IRBUILDERCMP-NEXT: @b = global i32 1 - var g1 = builder.addGlobal("a", type: IntType.int32) + let g1 = builder.addGlobal("a", type: IntType.int32) g1.initializer = Int32(1) - var g2 = builder.addGlobal("b", type: IntType.int32) + let g2 = builder.addGlobal("b", type: IntType.int32) g2.initializer = Int32(1) // IRBUILDERCMP: define void @main() { @@ -175,9 +175,9 @@ class IRBuilderSpec : XCTestCase { // IRBUILDERFCMP: @a = global double 1 // IRBUILDERFCMP-NEXT: @b = global double 1 - var g1 = builder.addGlobal("a", type: FloatType.double) + let g1 = builder.addGlobal("a", type: FloatType.double) g1.initializer = FloatType.double.constant(1) - var g2 = builder.addGlobal("b", type: FloatType.double) + let g2 = builder.addGlobal("b", type: FloatType.double) g2.initializer = FloatType.double.constant(1) // IRBUILDERFCMP: define void @main() { diff --git a/Tests/LLVMTests/IRGlobalSpec.swift b/Tests/LLVMTests/IRGlobalSpec.swift index 2757685b..cd8ac974 100644 --- a/Tests/LLVMTests/IRGlobalSpec.swift +++ b/Tests/LLVMTests/IRGlobalSpec.swift @@ -11,7 +11,7 @@ class IRGlobalSpec : XCTestCase { let module = Module(name: "IRInertGlobalTest") let builder = IRBuilder(module: module) // IRINERTGLOBAL: @external_global = external constant i32 - var extGlobal = builder.addGlobal("external_global", type: IntType.int32) + let extGlobal = builder.addGlobal("external_global", type: IntType.int32) extGlobal.isGlobalConstant = true // IRINERTGLOBAL: @got.external_global = private unnamed_addr constant i32* @external_global var gotGlobal = builder.addGlobal("got.external_global", @@ -21,7 +21,7 @@ class IRGlobalSpec : XCTestCase { gotGlobal.isGlobalConstant = true // IRINERTGLOBAL: @external_relative_reference = global i32 trunc (i64 sub (i64 ptrtoint (i32** @got.external_global to i64), i64 ptrtoint (i32* @external_relative_reference to i64)) to i32) - var ext_relative_reference = builder.addGlobal("external_relative_reference", type: IntType.int32) + let ext_relative_reference = builder.addGlobal("external_relative_reference", type: IntType.int32) ext_relative_reference.initializer = Constant.pointerToInt(gotGlobal, .int64) .subtracting(Constant.pointerToInt(ext_relative_reference, .int64)).truncate(to: .int32) module.dump() diff --git a/Tests/LLVMTests/IROperationSpec.swift b/Tests/LLVMTests/IROperationSpec.swift index ea1b4e02..eace9475 100644 --- a/Tests/LLVMTests/IROperationSpec.swift +++ b/Tests/LLVMTests/IROperationSpec.swift @@ -12,17 +12,17 @@ class IROperationSpec : XCTestCase { let builder = IRBuilder(module: module) // BINARYOP: @a = global i32 1 - var gi1 = builder.addGlobal("a", type: IntType.int32) + let gi1 = builder.addGlobal("a", type: IntType.int32) gi1.initializer = Int32(1) // BINARYOP-NEXT: @b = global i32 1 - var gi2 = builder.addGlobal("b", type: IntType.int32) + let gi2 = builder.addGlobal("b", type: IntType.int32) gi2.initializer = Int32(1) // BINARYOP-NEXT: @c = global float 0.000000e+00 - var gf1 = builder.addGlobal("c", type: FloatType.float) + let gf1 = builder.addGlobal("c", type: FloatType.float) gf1.initializer = FloatType.float.constant(0.0) // BINARYOP-NEXT: @d = global float 0.000000e+00 - var gf2 = builder.addGlobal("d", type: FloatType.float) + let gf2 = builder.addGlobal("d", type: FloatType.float) gf2.initializer = FloatType.float.constant(0.0) // BINARYOP: define void @main() { @@ -96,11 +96,11 @@ class IROperationSpec : XCTestCase { let builder = IRBuilder(module: module) // CASTOP: @a = global i32 1 - var gi = builder.addGlobal("a", type: IntType.int32) + let gi = builder.addGlobal("a", type: IntType.int32) gi.initializer = Int32(1) // CASTOP-NEXT: @f = global float 0.000000e+00 - var gf = builder.addGlobal("f", type: FloatType.float) + let gf = builder.addGlobal("f", type: FloatType.float) gf.initializer = FloatType.float.constant(0.0) // CASTOP: define void @main() {